From abaa8c8b7fc812710fef3064cde4623fc716eee3 Mon Sep 17 00:00:00 2001 From: "liyi.ly" Date: Tue, 27 Oct 2026 18:00:27 +0800 Subject: [PATCH] feat(codex): reuse session processes Reuse Codex session processes with private runtime directories, crash recovery coverage, and process-pool lifecycle tests. Remove repo-local prd-spec/specs records and update repository guidance so public-change evidence is checked before committing. Co-authored-by: TRAE CLI --- .gitignore | 5 +- AGENTS.md | 24 +- .../docs/framework/agent/runtime.en.mdx | 37 +- docs/content/docs/framework/agent/runtime.mdx | 37 +- frontend/README.md | 46 +- prd-spec/README.md | 130 --- prd-spec/README.zh.md | 130 --- .../2026-09-12-a2a-first-event-heartbeat.md | 51 -- ...2026-09-12-a2a-first-event-heartbeat.zh.md | 51 -- .../2026-09-23-aidap-workspace-readiness.md | 33 - ...2026-09-23-aidap-workspace-readiness.zh.md | 33 - .../2026-09-23-mpa-a2a-discovery-default.md | 48 - ...2026-09-23-mpa-a2a-discovery-default.zh.md | 48 - .../2026-09-23-mpa-registry-tls-url.md | 35 - .../2026-09-23-mpa-registry-tls-url.zh.md | 35 - .../a2a-long-running/2026-09-15-wait.md | 17 - .../a2a-long-running/2026-09-15-wait.zh.md | 17 - .../apmplus-appkey/2026-10-22-fallback.md | 11 - .../apmplus-appkey/2026-10-22-fallback.zh.md | 11 - .../2026-09-15-channel-group-actions.md | 17 - .../2026-09-15-channel-group-actions.zh.md | 17 - .../2026-10-12-server-default-scope.md | 40 - .../2026-10-12-server-default-scope.zh.md | 40 - .../2026-09-15-channel-qr-lifetime.md | 21 - .../2026-09-15-channel-qr-lifetime.zh.md | 21 - .../2026-09-14-chat-model-dropdown.md | 56 -- .../2026-09-14-chat-model-dropdown.zh.md | 56 -- ...26-10-19-code-package-upload-validation.md | 93 -- ...10-19-code-package-upload-validation.zh.md | 93 -- .../2026-10-13-dependency-e2e-release-gate.md | 95 -- ...26-10-13-dependency-e2e-release-gate.zh.md | 95 -- ...2026-10-09-restore-esbuild-lock-entries.md | 85 -- ...6-10-09-restore-esbuild-lock-entries.zh.md | 85 -- .../2026-09-17-replay.md | 13 - .../2026-09-17-replay.zh.md | 13 - .../2026-10-22-single-runtime-picker.md | 53 -- .../2026-10-22-single-runtime-picker.zh.md | 53 -- .../2026-10-22-runtime-auth.md | 58 -- .../2026-10-22-runtime-auth.zh.md | 58 -- .../2026-10-22-agent-alias-business-store.md | 76 -- ...026-10-22-agent-alias-business-store.zh.md | 76 -- .../2026-09-14-mpa-agent-filtering.md | 61 -- .../2026-09-14-mpa-agent-filtering.zh.md | 61 -- .../2026-10-14-disable-application-apm.md | 46 - .../2026-10-14-disable-application-apm.zh.md | 46 - .../2026-10-14-restore-application-apm.md | 75 -- .../2026-10-14-restore-application-apm.zh.md | 62 -- .../2026-10-14-server-app-key.md | 48 - .../2026-10-14-server-app-key.zh.md | 48 - .../2026-10-15-otlp-endpoint.md | 38 - .../2026-10-15-otlp-endpoint.zh.md | 38 - .../2026-10-14-byteplus-default-images.md | 69 -- .../2026-10-14-byteplus-default-images.zh.md | 69 -- .../2026-10-14-byteplus-model-identities.md | 104 --- ...2026-10-14-byteplus-model-identities.zh.md | 96 -- .../2026-10-15-default-maximum.md | 28 - .../2026-10-15-default-maximum.zh.md | 28 - .../2026-10-14-skip-new-requests.md | 43 - .../2026-10-14-skip-new-requests.zh.md | 43 - .../2026-09-15-mpa-channel-visibility.md | 28 - .../2026-09-15-mpa-channel-visibility.zh.md | 28 - .../2026-10-09-restore-main-images.md | 56 -- .../2026-10-09-restore-main-images.zh.md | 56 -- .../2026-09-30-explicit-new-request.md | 56 -- .../2026-09-30-explicit-new-request.zh.md | 47 - .../2026-09-30-recover-step-navigation.md | 45 - .../2026-09-30-recover-step-navigation.zh.md | 43 - .../2026-10-20-diagnostic-completeness.md | 39 - .../2026-10-20-diagnostic-completeness.zh.md | 39 - .../2026-10-20-failure-details.md | 42 - .../2026-10-20-failure-details.zh.md | 42 - .../2026-10-15-local-task-recovery.md | 103 --- .../2026-10-15-local-task-recovery.zh.md | 103 --- .../mpa-default-jwt/2026-09-15-default-jwt.md | 9 - .../2026-09-15-default-jwt.zh.md | 9 - .../2026-09-30-studio-key-auth-default.md | 98 -- .../2026-09-30-studio-key-auth-default.zh.md | 87 -- .../2026-10-13-confirm-delete-outcome.md | 61 -- .../2026-10-13-confirm-delete-outcome.zh.md | 61 -- .../2026-10-14-transient-errors.md | 45 - .../2026-10-14-transient-errors.zh.md | 45 - .../2026-10-09-python310-timeout.md | 82 -- .../2026-10-09-python310-timeout.zh.md | 73 -- ...-09-29-ensure-managed-workload-identity.md | 80 -- ...-29-ensure-managed-workload-identity.zh.md | 73 -- .../2026-10-14-owned-resource-metadata.md | 40 - .../2026-10-14-owned-resource-metadata.zh.md | 40 - .../2026-10-09-valid-network-marker.md | 56 -- .../2026-10-09-valid-network-marker.zh.md | 56 -- .../2026-10-09-empty-network-results.md | 55 -- .../2026-10-09-empty-network-results.zh.md | 55 -- .../2026-10-14-provider-errors.md | 39 - .../2026-10-14-provider-errors.zh.md | 39 - .../2026-10-13-model-source.md | 39 - .../2026-10-13-model-source.zh.md | 39 - .../2026-10-10-terminal-create-failure.md | 73 -- .../2026-10-10-terminal-create-failure.zh.md | 73 -- .../2026-10-14-sdk-error-codes.md | 32 - .../2026-10-14-sdk-error-codes.zh.md | 32 - .../2026-10-14-sdk-json-and-restore-ov.md | 42 - .../2026-10-14-sdk-json-and-restore-ov.zh.md | 42 - .../2026-09-29-set-fresh-runtime-command.md | 50 - ...2026-09-29-set-fresh-runtime-command.zh.md | 45 - ...6-09-29-runtime-initial-tool-attachment.md | 90 -- ...9-29-runtime-initial-tool-attachment.zh.md | 75 -- ...09-12-mpa-runtime-integration-hardening.md | 160 ---- ...12-mpa-runtime-integration-hardening.zh.md | 160 ---- .../2026-09-13-dev-loop-execution.md | 40 - .../2026-09-13-dev-loop-execution.zh.md | 40 - .../2026-09-13-dev-loop-review.md | 31 - .../2026-09-13-dev-loop-review.zh.md | 31 - .../2026-09-13-functional-validation-cases.md | 23 - ...26-09-13-functional-validation-cases.zh.md | 23 - .../2026-10-07-persistent-mcp-secret.md | 78 -- .../2026-10-07-persistent-mcp-secret.zh.md | 78 -- .../2026-09-29-preserve-runtime-command.md | 60 -- .../2026-09-29-preserve-runtime-command.zh.md | 51 -- .../2026-09-29-typed-event-dedup.md | 46 - .../2026-09-29-typed-event-dedup.zh.md | 46 - .../2026-09-28-registry-owned-network.md | 115 --- .../2026-09-28-registry-owned-network.zh.md | 98 -- .../2026-10-09-entered-name-description.md | 54 -- .../2026-10-09-entered-name-description.zh.md | 54 -- .../2026-10-09-standard-shared-gateway.md | 57 -- .../2026-10-09-standard-shared-gateway.zh.md | 57 -- .../2026-10-09-sts-account.md | 53 -- .../2026-10-09-sts-account.zh.md | 53 -- .../2026-10-09-subnet-order.md | 50 - .../2026-10-09-subnet-order.zh.md | 50 - .../2026-09-21-mpa-test-types.md | 50 - .../2026-09-21-mpa-test-types.zh.md | 50 - .../2026-10-10-complete-fork-increments.md | 66 -- .../2026-10-10-complete-fork-increments.zh.md | 66 -- .../2026-10-13-startup-and-lockfile.md | 52 -- .../2026-10-13-startup-and-lockfile.zh.md | 52 -- .../2026-10-15-worker-provider-metadata.md | 100 -- .../2026-10-15-worker-provider-metadata.zh.md | 100 -- .../2026-09-28-agent-derived-worker-name.md | 52 -- ...2026-09-28-agent-derived-worker-name.zh.md | 52 -- .../2026-10-09-runtime-named-worker.md | 54 -- .../2026-10-09-runtime-named-worker.zh.md | 54 -- .../2026-09-20-worker-metadata-visibility.md | 53 -- ...026-09-20-worker-metadata-visibility.zh.md | 53 -- .../2026-09-20-worker-retry.md | 56 -- .../2026-09-20-worker-retry.zh.md | 56 -- .../2026-09-30-worker-metadata-deadline.md | 54 -- .../2026-09-30-worker-metadata-deadline.zh.md | 54 -- .../2026-10-09-independent-worker.md | 62 -- .../2026-10-09-independent-worker.zh.md | 62 -- .../2026-10-09-worker-image-defaults.md | 42 - .../2026-10-09-worker-image-defaults.zh.md | 42 - .../pr1180-ci/2026-10-21-repair-ci-gates.md | 67 -- .../2026-10-21-repair-ci-gates.zh.md | 67 -- .../2026-10-23-a2a-uri-test-fixtures.md | 60 -- .../2026-10-23-a2a-uri-test-fixtures.zh.md | 60 -- ...10-12-google-adk-opentelemetry-baseline.md | 120 --- ...12-google-adk-opentelemetry-baseline.zh.md | 120 --- .../2026-09-17-default-db-spans.md | 9 - .../2026-09-17-default-db-spans.zh.md | 9 - .../2026-10-20-skill-shell-tool-naming.md | 37 - .../2026-10-20-skill-shell-tool-naming.zh.md | 37 - ...2026-10-12-general-deployment-isolation.md | 45 - ...6-10-12-general-deployment-isolation.zh.md | 45 - .../2026-10-15-byteplus-tag-endpoint.md | 137 --- .../2026-10-15-byteplus-tag-endpoint.zh.md | 122 --- .../2026-10-15-lock-and-tos-tests.md | 53 -- .../2026-10-15-lock-and-tos-tests.zh.md | 53 -- .../2026-09-22-mpa-a2a-final.md | 56 -- .../2026-09-22-mpa-a2a-final.zh.md | 56 -- .../2026-09-22-mpa-a2a-reasoning.md | 34 - .../2026-09-22-mpa-a2a-reasoning.zh.md | 34 - .../2026-09-22-mpa-direct-answer-reasoning.md | 50 - ...26-09-22-mpa-direct-answer-reasoning.zh.md | 50 - .../2026-09-22-mpa-reasoning-snapshots.md | 36 - .../2026-09-22-mpa-reasoning-snapshots.zh.md | 36 - .../2026-09-22-mpa-wrapper-preview.md | 46 - .../2026-09-22-mpa-wrapper-preview.zh.md | 46 - .../2026-09-22-preserve-followup-content.md | 35 - ...2026-09-22-preserve-followup-content.zh.md | 35 - .../2026-09-22-preserve-sandbox-reasoning.md | 36 - ...026-09-22-preserve-sandbox-reasoning.zh.md | 36 - ...026-09-22-studio-codex-commentary-dedup.md | 113 --- ...-09-22-studio-codex-commentary-dedup.zh.md | 113 --- .../studio-cron-e2e/2026-09-15-integration.md | 17 - .../2026-09-15-integration.zh.md | 17 - .../2026-10-20-latest-image-snapshots.md | 103 --- .../2026-10-20-latest-image-snapshots.zh.md | 103 --- .../2026-10-21-readonly-tag-resolution.md | 77 -- .../2026-10-21-readonly-tag-resolution.zh.md | 77 -- .../2026-10-22-fork-ci-validation.md | 100 -- .../2026-10-22-fork-ci-validation.zh.md | 84 -- .../2026-09-30-prefer-a2a.md | 97 -- .../2026-09-30-prefer-a2a.zh.md | 83 -- ...026-09-23-complete-aidap-runtime-policy.md | 86 -- ...-09-23-complete-aidap-runtime-policy.zh.md | 86 -- ...09-23-complete-mpa-cloud-runtime-policy.md | 94 -- ...23-complete-mpa-cloud-runtime-policy.zh.md | 94 -- .../2026-09-30-hide-artifact-entry.md | 75 -- .../2026-09-30-hide-artifact-entry.zh.md | 62 -- .../2026-10-15-cloud-iam.md | 52 -- .../2026-10-15-cloud-iam.zh.md | 52 -- ...09-24-start-another-agent-after-failure.md | 55 -- ...24-start-another-agent-after-failure.zh.md | 55 -- .../2026-09-20-dialog-theme.md | 50 - .../2026-09-20-dialog-theme.zh.md | 50 - .../2026-09-20-studio-mpa-profile-errors.md | 97 -- ...2026-09-20-studio-mpa-profile-errors.zh.md | 97 -- .../2026-09-23-use-temporary-state.md | 82 -- .../2026-09-23-use-temporary-state.zh.md | 82 -- .../2026-10-07-align-sdk-wheel.md | 99 -- .../2026-10-07-align-sdk-wheel.zh.md | 87 -- .../2026-09-22-a2a-session-routing.md | 51 -- .../2026-09-22-a2a-session-routing.zh.md | 51 -- .../2026-09-22-studio-startup-assets.md | 194 ---- .../2026-09-22-studio-startup-assets.zh.md | 194 ---- .../2026-10-15-tos-credentials.md | 79 -- .../2026-10-15-tos-credentials.zh.md | 70 -- .../2026-09-17-session-trace.md | 13 - .../2026-09-17-session-trace.zh.md | 13 - .../2026-09-23-rebuild-corrupted-assets.md | 37 - .../2026-09-23-rebuild-corrupted-assets.zh.md | 37 - .../2026-10-13-bounded-cache.md | 73 -- .../2026-10-13-bounded-cache.zh.md | 73 -- .../2026-09-23-yaml-secret-guard.md | 72 -- .../2026-09-23-yaml-secret-guard.zh.md | 72 -- .../2026-10-16-bot-binding-types.md | 49 - .../2026-10-16-bot-binding-types.zh.md | 49 - .../2026-09-15-channel-auth-methods.md | 34 - .../2026-09-15-channel-auth-methods.zh.md | 34 - .../2026-09-15-channel-configuration-modes.md | 73 -- ...26-09-15-channel-configuration-modes.zh.md | 73 -- .../2026-09-15-channel-qr-regeneration.md | 36 - .../2026-09-15-channel-qr-regeneration.zh.md | 36 - .../2026-10-15-deepseek-harness-runtime.md | 176 ---- .../2026-10-15-deepseek-harness-runtime.zh.md | 176 ---- .../2026-10-12-harness-tos-skills.md | 90 -- .../2026-10-12-harness-tos-skills.zh.md | 90 -- .../lark-binding/2026-10-15-lark-binding.md | 60 -- .../2026-10-15-lark-binding.zh.md | 60 -- .../2026-09-15-message-channel-binding.md | 61 -- .../2026-09-15-message-channel-binding.zh.md | 61 -- .../2026-09-15-message-channel-workspace.md | 50 - ...2026-09-15-message-channel-workspace.zh.md | 50 - .../2026-09-18-mpa-agent-info-rail.md | 94 -- .../2026-09-18-mpa-agent-info-rail.zh.md | 94 -- ...-10-mpa-agent-oneclick-provision-design.md | 221 ----- ...-mpa-agent-oneclick-provision-design.zh.md | 221 ----- ...t-oneclick-provision-verification-cases.md | 182 ---- ...neclick-provision-verification-cases.zh.md | 159 ---- ...rovision-orchestration-alignment-design.md | 228 ----- ...ision-orchestration-alignment-design.zh.md | 226 ----- ...2-mpa-agent-studio-a2a-streaming-design.md | 230 ----- ...pa-agent-studio-a2a-streaming-design.zh.md | 230 ----- ...studio-a2a-streaming-verification-cases.md | 48 - ...dio-a2a-streaming-verification-cases.zh.md | 48 - .../2026-09-20-studio-mpa-creation.md | 216 ----- .../2026-09-20-studio-mpa-creation.zh.md | 216 ----- .../2026-09-23-mpa-tos-output-mount.md | 77 -- .../2026-09-23-mpa-tos-output-mount.zh.md | 66 -- .../2026-09-23-studio-builtin-mpa-profile.md | 52 -- ...026-09-23-studio-builtin-mpa-profile.zh.md | 52 -- .../2026-09-23-studio-mpa-openviking-key.md | 42 - ...2026-09-23-studio-mpa-openviking-key.zh.md | 42 - ...26-09-23-studio-mpa-three-step-creation.md | 55 -- ...09-23-studio-mpa-three-step-creation.zh.md | 55 -- .../2026-10-09-named-studio-creation.md | 83 -- .../2026-10-09-named-studio-creation.zh.md | 83 -- .../evidence/2026-09-11-code-review.zh.md | 57 -- .../2026-09-11-verification-execution.zh.md | 73 -- .../mpa-create.config.example.yaml | 146 --- ...26-10-13-independent-business-workspace.md | 73 -- ...10-13-independent-business-workspace.zh.md | 73 -- .../2026-10-14-automatic-creation.md | 118 --- .../2026-10-14-automatic-creation.zh.md | 118 --- .../2026-10-14-inference-endpoints.md | 57 -- .../2026-10-14-inference-endpoints.zh.md | 57 -- .../2026-10-10-byteplus-creation.md | 94 -- .../2026-10-10-byteplus-creation.zh.md | 95 -- .../2026-09-16-mpa-channels-automation.md | 103 --- .../2026-09-16-mpa-channels-automation.zh.md | 103 --- ...6-10-20-collaborating-agents-detail-tab.md | 47 - ...0-20-collaborating-agents-detail-tab.zh.md | 47 - .../2026-10-20-collaborating-agents.md | 80 -- .../2026-10-20-collaborating-agents.zh.md | 80 -- .../2026-10-20-direct-credentials.md | 32 - .../2026-10-20-direct-credentials.zh.md | 32 - .../2026-10-21-a2a-space.md | 14 - .../2026-10-21-a2a-space.zh.md | 18 - .../2026-10-22-center-registration.md | 111 --- .../2026-10-22-center-registration.zh.md | 111 --- .../2026-10-22-endpoints-and-skills.md | 58 -- .../2026-10-22-endpoints-and-skills.zh.md | 58 -- .../2026-10-22-member-actions.md | 61 -- .../2026-10-22-member-actions.zh.md | 61 -- .../2026-10-22-space-aliases.md | 33 - .../2026-10-22-space-aliases.zh.md | 33 - .../2026-10-22-space-management.md | 77 -- .../2026-10-22-space-management.zh.md | 77 -- .../2026-10-13-max-instance-input.md | 70 -- .../2026-10-13-max-instance-input.zh.md | 70 -- .../2026-09-15-mpa-feishu-channels.md | 31 - .../2026-09-15-mpa-feishu-channels.zh.md | 31 - .../2026-10-09-studio-multi-bot.md | 81 -- .../2026-10-09-studio-multi-bot.zh.md | 81 -- .../2026-10-10-port-multi-bot.md | 72 -- .../2026-10-10-port-multi-bot.zh.md | 72 -- .../2026-10-22-independent-images.md | 68 -- .../2026-10-22-independent-images.zh.md | 68 -- .../2026-10-22-latest-update-snapshots.md | 86 -- .../2026-10-22-latest-update-snapshots.zh.md | 86 -- .../2026-10-22-manual-image-source.md | 64 -- .../2026-10-22-manual-image-source.zh.md | 64 -- .../2026-10-10-account-ark-key.md | 69 -- .../2026-10-10-account-ark-key.zh.md | 69 -- .../2026-10-10-latest-created-key.md | 64 -- .../2026-10-10-latest-created-key.zh.md | 64 -- ...10-13-runtime-name-and-workspace-labels.md | 74 -- ...13-runtime-name-and-workspace-labels.zh.md | 74 -- .../2026-10-13-default-model-creation.md | 62 -- .../2026-10-13-default-model-creation.zh.md | 62 -- .../2026-10-12-owned-resource-deletion.md | 56 -- .../2026-10-12-owned-resource-deletion.zh.md | 56 -- .../2026-09-15-dev-loop-spec-review.md | 56 -- .../2026-09-15-dev-loop-spec-review.zh.md | 56 -- .../2026-09-15-functional-validation-cases.md | 286 ------ ...26-09-15-functional-validation-cases.zh.md | 286 ------ .../2026-09-15-m0-evidence.md | 26 - .../2026-09-15-m0-evidence.zh.md | 26 - ...6-09-15-mpa-p0-productionization-design.md | 438 --------- ...9-15-mpa-p0-productionization-design.zh.md | 438 --------- .../mpa-p0-productionization/browser-cases.md | 101 --- .../browser-cases.zh.md | 101 --- .../2026-09-22-mpa-response-grouping.md | 38 - .../2026-09-22-mpa-response-grouping.zh.md | 38 - .../2026-10-09-managed-runtime-role.md | 74 -- .../2026-10-09-managed-runtime-role.zh.md | 74 -- .../2026-09-21-deployment-account-pg.md | 89 -- .../2026-09-21-deployment-account-pg.zh.md | 89 -- .../2026-09-23-auto-pg-workspaces.md | 64 -- .../2026-09-23-auto-pg-workspaces.zh.md | 64 -- .../2026-09-23-space-scoped-manual-pg.md | 49 - .../2026-09-23-space-scoped-manual-pg.zh.md | 49 - .../2026-09-23-two-pg-workspaces.md | 57 -- .../2026-09-23-two-pg-workspaces.zh.md | 57 -- ...6-09-13-mpa-studio-control-plane-design.md | 170 ---- ...9-13-mpa-studio-control-plane-design.zh.md | 170 ---- ...tudio-control-plane-implementation-plan.md | 139 --- ...io-control-plane-implementation-plan.zh.md | 130 --- ...2026-09-20-mpa-studio-workload-identity.md | 124 --- ...6-09-20-mpa-studio-workload-identity.zh.md | 124 --- .../2026-10-16-skill-observability.md | 95 -- .../2026-10-16-skill-observability.zh.md | 95 -- .../2026-10-21-studio-agents-md-edit.md | 52 -- .../2026-10-21-studio-agents-md-edit.zh.md | 53 -- .../2026-10-09-basic-only.md | 33 - .../2026-10-09-basic-only.zh.md | 33 - .../2026-09-15-mono-task-management.md | 54 -- .../2026-09-15-mono-task-management.zh.md | 54 -- .../2026-09-15-remove-agent-input.md | 5 - .../2026-09-15-remove-agent-input.zh.md | 5 - .../features/studio-mpa-cron-tasks/README.md | 46 - .../studio-mpa-cron-tasks/README.zh.md | 46 - .../2026-09-23-deploy-identity-reuse.md | 35 - .../2026-09-23-deploy-identity-reuse.zh.md | 35 - .../2026-09-23-studio-mpa-prewarm.md | 31 - .../2026-09-23-studio-mpa-prewarm.zh.md | 31 - .../2026-09-20-main-integration.md | 9 - .../2026-09-20-main-integration.zh.md | 9 - .../2026-09-28-studio-mpa-source-filter.md | 128 --- .../2026-09-28-studio-mpa-source-filter.zh.md | 128 --- .../2026-10-12-bucket-only.md | 313 ------- .../2026-10-12-bucket-only.zh.md | 246 ----- .../2026-09-23-retain-id-token.md | 39 - .../2026-09-23-retain-id-token.zh.md | 39 - .../2026-09-17-history-download.md | 36 - .../2026-09-17-history-download.zh.md | 36 - ...udio-tool-activity-visualization-design.md | 276 ------ ...o-tool-activity-visualization-design.zh.md | 276 ------ .../2026-10-14-studio-owned-resources.md | 61 -- .../2026-10-14-studio-owned-resources.zh.md | 61 -- .../2026-10-15-byteplus-rebase.md | 37 - .../2026-10-15-byteplus-rebase.zh.md | 37 - scripts/benchmark_codex_tools.py | 405 +++++++++ scripts/codex_sandbox_probe.py | 214 +++++ specs/README.md | 114 --- specs/README.zh.md | 114 --- specs/apmplus-configuration/README.md | 3 - specs/apmplus-configuration/README.zh.md | 3 - specs/mpa-channels/README.md | 67 -- specs/mpa-channels/README.zh.md | 67 -- specs/mpa-collaborating-agents/README.md | 56 -- specs/mpa-collaborating-agents/README.zh.md | 56 -- specs/mpa-image-update/README.md | 47 - specs/mpa-image-update/README.zh.md | 47 - specs/mpa-runtime-control/README.md | 193 ---- specs/mpa-runtime-control/README.zh.md | 193 ---- specs/mpa-runtime-provisioning/README.md | 148 --- specs/mpa-runtime-provisioning/README.zh.md | 138 --- specs/runtime-deepseek-harness/README.md | 116 --- specs/runtime-deepseek-harness/README.zh.md | 116 --- .../README.md | 59 -- .../README.zh.md | 59 -- specs/skill-observability/README.md | 37 - specs/skill-observability/README.zh.md | 37 - specs/studio-agents-md/README.md | 47 - specs/studio-agents-md/README.zh.md | 42 - specs/studio-mpa-control-plane/README.md | 157 ---- specs/studio-mpa-control-plane/README.zh.md | 157 ---- specs/studio-mpa-creation/README.md | 300 ------ specs/studio-mpa-creation/README.zh.md | 289 ------ specs/studio-mpa-cron-tasks/README.md | 41 - specs/studio-mpa-cron-tasks/README.zh.md | 41 - specs/studio-mpa-prewarm/README.md | 18 - specs/studio-mpa-prewarm/README.zh.md | 18 - specs/studio-oauth-session/README.md | 26 - specs/studio-oauth-session/README.zh.md | 26 - specs/studio-runtime-diagnostics/README.md | 63 -- specs/studio-runtime-diagnostics/README.zh.md | 63 -- specs/studio-sandbox-download/README.md | 21 - specs/studio-sandbox-download/README.zh.md | 21 - specs/studio-skill-document/README.md | 23 - specs/studio-skill-document/README.zh.md | 23 - specs/studio-tool-activity/README.md | 124 --- specs/studio-tool-activity/README.zh.md | 124 --- specs/tracing-cache/README.md | 29 - specs/tracing-cache/README.zh.md | 29 - tests/integrations/agentkit/test_app.py | 28 + .../codex/test_codex_crash_recovery_smoke.py | 324 +++++++ tests/runtime/codex/test_codex_mcp_bridge.py | 266 ++++++ .../runtime/codex/test_codex_private_dirs.py | 132 +++ .../runtime/codex/test_codex_process_pool.py | 361 ++++++++ tests/runtime/codex/test_codex_runtime_sdk.py | 132 ++- .../runtime/codex/test_codex_runtime_smoke.py | 62 +- .../test_codex_sandbox_isolation_smoke.py | 294 ++++++ tests/runtime/codex/test_codex_shim_rounds.py | 2 + .../runtime/codex/test_codex_thread_store.py | 19 + .../codex/test_codex_tool_workloads.py | 134 +++ .../runtime/codex/test_codex_turn_contract.py | 443 ++++++++- .../runtime/codex/test_codex_turn_control.py | 142 +++ .../conformance/conformance_adapters.py | 10 +- tests/runtime/differential/fake_codex_sdk.py | 15 +- veadk/cli/cli_mpa.py | 2 +- veadk/integrations/agentkit/app.py | 33 +- veadk/integrations/mpa/managed/README.md | 29 +- veadk/integrations/mpa/managed/README.zh.md | 26 +- veadk/runtime/codex/__init__.py | 32 +- veadk/runtime/codex/config.py | 40 +- veadk/runtime/codex/mcp_bridge.py | 195 +++- veadk/runtime/codex/metrics.py | 3 +- veadk/runtime/codex/private_dirs.py | 157 ++++ veadk/runtime/codex/process_pool.py | 446 +++++++++ veadk/runtime/codex/proxy.py | 33 +- veadk/runtime/codex/runtime.py | 851 ++++++++++++++---- veadk/runtime/codex/thread_store.py | 71 ++ veadk/runtime/codex/turn_control.py | 132 ++- 455 files changed, 4841 insertions(+), 30723 deletions(-) delete mode 100644 prd-spec/README.md delete mode 100644 prd-spec/README.zh.md delete mode 100644 prd-spec/bugfixes/2026-09-12-a2a-first-event-heartbeat.md delete mode 100644 prd-spec/bugfixes/2026-09-12-a2a-first-event-heartbeat.zh.md delete mode 100644 prd-spec/bugfixes/2026-09-23-aidap-workspace-readiness.md delete mode 100644 prd-spec/bugfixes/2026-09-23-aidap-workspace-readiness.zh.md delete mode 100644 prd-spec/bugfixes/2026-09-23-mpa-a2a-discovery-default.md delete mode 100644 prd-spec/bugfixes/2026-09-23-mpa-a2a-discovery-default.zh.md delete mode 100644 prd-spec/bugfixes/2026-09-23-mpa-registry-tls-url.md delete mode 100644 prd-spec/bugfixes/2026-09-23-mpa-registry-tls-url.zh.md delete mode 100644 prd-spec/bugfixes/a2a-long-running/2026-09-15-wait.md delete mode 100644 prd-spec/bugfixes/a2a-long-running/2026-09-15-wait.zh.md delete mode 100644 prd-spec/bugfixes/apmplus-appkey/2026-10-22-fallback.md delete mode 100644 prd-spec/bugfixes/apmplus-appkey/2026-10-22-fallback.zh.md delete mode 100644 prd-spec/bugfixes/channel-group-actions/2026-09-15-channel-group-actions.md delete mode 100644 prd-spec/bugfixes/channel-group-actions/2026-09-15-channel-group-actions.zh.md delete mode 100644 prd-spec/bugfixes/channel-group-actions/2026-10-12-server-default-scope.md delete mode 100644 prd-spec/bugfixes/channel-group-actions/2026-10-12-server-default-scope.zh.md delete mode 100644 prd-spec/bugfixes/channel-qr-lifetime/2026-09-15-channel-qr-lifetime.md delete mode 100644 prd-spec/bugfixes/channel-qr-lifetime/2026-09-15-channel-qr-lifetime.zh.md delete mode 100644 prd-spec/bugfixes/chat-model-dropdown/2026-09-14-chat-model-dropdown.md delete mode 100644 prd-spec/bugfixes/chat-model-dropdown/2026-09-14-chat-model-dropdown.zh.md delete mode 100644 prd-spec/bugfixes/code-package-upload-validation/2026-10-19-code-package-upload-validation.md delete mode 100644 prd-spec/bugfixes/code-package-upload-validation/2026-10-19-code-package-upload-validation.zh.md delete mode 100644 prd-spec/bugfixes/dependency-e2e-release-gate/2026-10-13-dependency-e2e-release-gate.md delete mode 100644 prd-spec/bugfixes/dependency-e2e-release-gate/2026-10-13-dependency-e2e-release-gate.zh.md delete mode 100644 prd-spec/bugfixes/frontend-lockfile-sync/2026-10-09-restore-esbuild-lock-entries.md delete mode 100644 prd-spec/bugfixes/frontend-lockfile-sync/2026-10-09-restore-esbuild-lock-entries.zh.md delete mode 100644 prd-spec/bugfixes/history-turn-identity/2026-09-17-replay.md delete mode 100644 prd-spec/bugfixes/history-turn-identity/2026-09-17-replay.zh.md delete mode 100644 prd-spec/bugfixes/mpa-a2a-runtime-picker/2026-10-22-single-runtime-picker.md delete mode 100644 prd-spec/bugfixes/mpa-a2a-runtime-picker/2026-10-22-single-runtime-picker.zh.md delete mode 100644 prd-spec/bugfixes/mpa-a2a-space-deletion/2026-10-22-runtime-auth.md delete mode 100644 prd-spec/bugfixes/mpa-a2a-space-deletion/2026-10-22-runtime-auth.zh.md delete mode 100644 prd-spec/bugfixes/mpa-agent-alias/2026-10-22-agent-alias-business-store.md delete mode 100644 prd-spec/bugfixes/mpa-agent-alias/2026-10-22-agent-alias-business-store.zh.md delete mode 100644 prd-spec/bugfixes/mpa-agent-filtering/2026-09-14-mpa-agent-filtering.md delete mode 100644 prd-spec/bugfixes/mpa-agent-filtering/2026-09-14-mpa-agent-filtering.zh.md delete mode 100644 prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-disable-application-apm.md delete mode 100644 prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-disable-application-apm.zh.md delete mode 100644 prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-restore-application-apm.md delete mode 100644 prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-restore-application-apm.zh.md delete mode 100644 prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-server-app-key.md delete mode 100644 prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-server-app-key.zh.md delete mode 100644 prd-spec/bugfixes/mpa-byteplus-apm/2026-10-15-otlp-endpoint.md delete mode 100644 prd-spec/bugfixes/mpa-byteplus-apm/2026-10-15-otlp-endpoint.zh.md delete mode 100644 prd-spec/bugfixes/mpa-byteplus-images/2026-10-14-byteplus-default-images.md delete mode 100644 prd-spec/bugfixes/mpa-byteplus-images/2026-10-14-byteplus-default-images.zh.md delete mode 100644 prd-spec/bugfixes/mpa-byteplus-model-identities/2026-10-14-byteplus-model-identities.md delete mode 100644 prd-spec/bugfixes/mpa-byteplus-model-identities/2026-10-14-byteplus-model-identities.zh.md delete mode 100644 prd-spec/bugfixes/mpa-byteplus-scaling/2026-10-15-default-maximum.md delete mode 100644 prd-spec/bugfixes/mpa-byteplus-scaling/2026-10-15-default-maximum.zh.md delete mode 100644 prd-spec/bugfixes/mpa-byteplus-without-openviking/2026-10-14-skip-new-requests.md delete mode 100644 prd-spec/bugfixes/mpa-byteplus-without-openviking/2026-10-14-skip-new-requests.zh.md delete mode 100644 prd-spec/bugfixes/mpa-channel-visibility/2026-09-15-mpa-channel-visibility.md delete mode 100644 prd-spec/bugfixes/mpa-channel-visibility/2026-09-15-mpa-channel-visibility.zh.md delete mode 100644 prd-spec/bugfixes/mpa-create-images/2026-10-09-restore-main-images.md delete mode 100644 prd-spec/bugfixes/mpa-create-images/2026-10-09-restore-main-images.zh.md delete mode 100644 prd-spec/bugfixes/mpa-create-navigation/2026-09-30-explicit-new-request.md delete mode 100644 prd-spec/bugfixes/mpa-create-navigation/2026-09-30-explicit-new-request.zh.md delete mode 100644 prd-spec/bugfixes/mpa-create-navigation/2026-09-30-recover-step-navigation.md delete mode 100644 prd-spec/bugfixes/mpa-create-navigation/2026-09-30-recover-step-navigation.zh.md delete mode 100644 prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-diagnostic-completeness.md delete mode 100644 prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-diagnostic-completeness.zh.md delete mode 100644 prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-failure-details.md delete mode 100644 prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-failure-details.zh.md delete mode 100644 prd-spec/bugfixes/mpa-creation-local-state/2026-10-15-local-task-recovery.md delete mode 100644 prd-spec/bugfixes/mpa-creation-local-state/2026-10-15-local-task-recovery.zh.md delete mode 100644 prd-spec/bugfixes/mpa-default-jwt/2026-09-15-default-jwt.md delete mode 100644 prd-spec/bugfixes/mpa-default-jwt/2026-09-15-default-jwt.zh.md delete mode 100644 prd-spec/bugfixes/mpa-default-jwt/2026-09-30-studio-key-auth-default.md delete mode 100644 prd-spec/bugfixes/mpa-default-jwt/2026-09-30-studio-key-auth-default.zh.md delete mode 100644 prd-spec/bugfixes/mpa-delete-timeout/2026-10-13-confirm-delete-outcome.md delete mode 100644 prd-spec/bugfixes/mpa-delete-timeout/2026-10-13-confirm-delete-outcome.zh.md delete mode 100644 prd-spec/bugfixes/mpa-iam-retry/2026-10-14-transient-errors.md delete mode 100644 prd-spec/bugfixes/mpa-iam-retry/2026-10-14-transient-errors.zh.md delete mode 100644 prd-spec/bugfixes/mpa-iam-timeout-compat/2026-10-09-python310-timeout.md delete mode 100644 prd-spec/bugfixes/mpa-iam-timeout-compat/2026-10-09-python310-timeout.zh.md delete mode 100644 prd-spec/bugfixes/mpa-managed-workload-identity/2026-09-29-ensure-managed-workload-identity.md delete mode 100644 prd-spec/bugfixes/mpa-managed-workload-identity/2026-09-29-ensure-managed-workload-identity.zh.md delete mode 100644 prd-spec/bugfixes/mpa-name-retry/2026-10-14-owned-resource-metadata.md delete mode 100644 prd-spec/bugfixes/mpa-name-retry/2026-10-14-owned-resource-metadata.zh.md delete mode 100644 prd-spec/bugfixes/mpa-network-description/2026-10-09-valid-network-marker.md delete mode 100644 prd-spec/bugfixes/mpa-network-description/2026-10-09-valid-network-marker.zh.md delete mode 100644 prd-spec/bugfixes/mpa-network-discovery/2026-10-09-empty-network-results.md delete mode 100644 prd-spec/bugfixes/mpa-network-discovery/2026-10-09-empty-network-results.zh.md delete mode 100644 prd-spec/bugfixes/mpa-openviking-diagnostics/2026-10-14-provider-errors.md delete mode 100644 prd-spec/bugfixes/mpa-openviking-diagnostics/2026-10-14-provider-errors.zh.md delete mode 100644 prd-spec/bugfixes/mpa-openviking-source/2026-10-13-model-source.md delete mode 100644 prd-spec/bugfixes/mpa-openviking-source/2026-10-13-model-source.zh.md delete mode 100644 prd-spec/bugfixes/mpa-pg-recovery/2026-10-10-terminal-create-failure.md delete mode 100644 prd-spec/bugfixes/mpa-pg-recovery/2026-10-10-terminal-create-failure.zh.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-error-codes.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-error-codes.zh.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-json-and-restore-ov.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-json-and-restore-ov.zh.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-fresh-command/2026-09-29-set-fresh-runtime-command.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-fresh-command/2026-09-29-set-fresh-runtime-command.zh.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-initial-tool/2026-09-29-runtime-initial-tool-attachment.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-initial-tool/2026-09-29-runtime-initial-tool-attachment.zh.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-integration/2026-09-12-mpa-runtime-integration-hardening.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-integration/2026-09-12-mpa-runtime-integration-hardening.zh.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-dev-loop-execution.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-dev-loop-execution.zh.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-dev-loop-review.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-dev-loop-review.zh.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-functional-validation-cases.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-functional-validation-cases.zh.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-mcp-secret/2026-10-07-persistent-mcp-secret.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-mcp-secret/2026-10-07-persistent-mcp-secret.zh.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-reference-command/2026-09-29-preserve-runtime-command.md delete mode 100644 prd-spec/bugfixes/mpa-runtime-reference-command/2026-09-29-preserve-runtime-command.zh.md delete mode 100644 prd-spec/bugfixes/mpa-sandbox-event-identity/2026-09-29-typed-event-dedup.md delete mode 100644 prd-spec/bugfixes/mpa-sandbox-event-identity/2026-09-29-typed-event-dedup.zh.md delete mode 100644 prd-spec/bugfixes/mpa-shared-network-bootstrap/2026-09-28-registry-owned-network.md delete mode 100644 prd-spec/bugfixes/mpa-shared-network-bootstrap/2026-09-28-registry-owned-network.zh.md delete mode 100644 prd-spec/bugfixes/mpa-skill-space-description/2026-10-09-entered-name-description.md delete mode 100644 prd-spec/bugfixes/mpa-skill-space-description/2026-10-09-entered-name-description.zh.md delete mode 100644 prd-spec/bugfixes/mpa-standard-gateway/2026-10-09-standard-shared-gateway.md delete mode 100644 prd-spec/bugfixes/mpa-standard-gateway/2026-10-09-standard-shared-gateway.zh.md delete mode 100644 prd-spec/bugfixes/mpa-studio-account/2026-10-09-sts-account.md delete mode 100644 prd-spec/bugfixes/mpa-studio-account/2026-10-09-sts-account.zh.md delete mode 100644 prd-spec/bugfixes/mpa-subnet-order/2026-10-09-subnet-order.md delete mode 100644 prd-spec/bugfixes/mpa-subnet-order/2026-10-09-subnet-order.zh.md delete mode 100644 prd-spec/bugfixes/mpa-test-types/2026-09-21-mpa-test-types.md delete mode 100644 prd-spec/bugfixes/mpa-test-types/2026-09-21-mpa-test-types.zh.md delete mode 100644 prd-spec/bugfixes/mpa-upstream-incremental-alignment/2026-10-10-complete-fork-increments.md delete mode 100644 prd-spec/bugfixes/mpa-upstream-incremental-alignment/2026-10-10-complete-fork-increments.zh.md delete mode 100644 prd-spec/bugfixes/mpa-upstream-readiness/2026-10-13-startup-and-lockfile.md delete mode 100644 prd-spec/bugfixes/mpa-upstream-readiness/2026-10-13-startup-and-lockfile.zh.md delete mode 100644 prd-spec/bugfixes/mpa-worker-diagnostics/2026-10-15-worker-provider-metadata.md delete mode 100644 prd-spec/bugfixes/mpa-worker-diagnostics/2026-10-15-worker-provider-metadata.zh.md delete mode 100644 prd-spec/bugfixes/mpa-worker-naming/2026-09-28-agent-derived-worker-name.md delete mode 100644 prd-spec/bugfixes/mpa-worker-naming/2026-09-28-agent-derived-worker-name.zh.md delete mode 100644 prd-spec/bugfixes/mpa-worker-naming/2026-10-09-runtime-named-worker.md delete mode 100644 prd-spec/bugfixes/mpa-worker-naming/2026-10-09-runtime-named-worker.zh.md delete mode 100644 prd-spec/bugfixes/mpa-worker-retry/2026-09-20-worker-metadata-visibility.md delete mode 100644 prd-spec/bugfixes/mpa-worker-retry/2026-09-20-worker-metadata-visibility.zh.md delete mode 100644 prd-spec/bugfixes/mpa-worker-retry/2026-09-20-worker-retry.md delete mode 100644 prd-spec/bugfixes/mpa-worker-retry/2026-09-20-worker-retry.zh.md delete mode 100644 prd-spec/bugfixes/mpa-worker-retry/2026-09-30-worker-metadata-deadline.md delete mode 100644 prd-spec/bugfixes/mpa-worker-retry/2026-09-30-worker-metadata-deadline.zh.md delete mode 100644 prd-spec/bugfixes/mpa-worker-template/2026-10-09-independent-worker.md delete mode 100644 prd-spec/bugfixes/mpa-worker-template/2026-10-09-independent-worker.zh.md delete mode 100644 prd-spec/bugfixes/mpa-worker-template/2026-10-09-worker-image-defaults.md delete mode 100644 prd-spec/bugfixes/mpa-worker-template/2026-10-09-worker-image-defaults.zh.md delete mode 100644 prd-spec/bugfixes/pr1180-ci/2026-10-21-repair-ci-gates.md delete mode 100644 prd-spec/bugfixes/pr1180-ci/2026-10-21-repair-ci-gates.zh.md delete mode 100644 prd-spec/bugfixes/pr1182-ci/2026-10-23-a2a-uri-test-fixtures.md delete mode 100644 prd-spec/bugfixes/pr1182-ci/2026-10-23-a2a-uri-test-fixtures.zh.md delete mode 100644 prd-spec/bugfixes/runtime-dependency-compatibility/2026-10-12-google-adk-opentelemetry-baseline.md delete mode 100644 prd-spec/bugfixes/runtime-dependency-compatibility/2026-10-12-google-adk-opentelemetry-baseline.zh.md delete mode 100644 prd-spec/bugfixes/runtime-disable-db-spans/2026-09-17-default-db-spans.md delete mode 100644 prd-spec/bugfixes/runtime-disable-db-spans/2026-09-17-default-db-spans.zh.md delete mode 100644 prd-spec/bugfixes/skill-shell-tool-naming/2026-10-20-skill-shell-tool-naming.md delete mode 100644 prd-spec/bugfixes/skill-shell-tool-naming/2026-10-20-skill-shell-tool-naming.zh.md delete mode 100644 prd-spec/bugfixes/studio-agent-category-isolation/2026-10-12-general-deployment-isolation.md delete mode 100644 prd-spec/bugfixes/studio-agent-category-isolation/2026-10-12-general-deployment-isolation.zh.md delete mode 100644 prd-spec/bugfixes/studio-byteplus-tag-endpoint/2026-10-15-byteplus-tag-endpoint.md delete mode 100644 prd-spec/bugfixes/studio-byteplus-tag-endpoint/2026-10-15-byteplus-tag-endpoint.zh.md delete mode 100644 prd-spec/bugfixes/studio-ci-lock-tests/2026-10-15-lock-and-tos-tests.md delete mode 100644 prd-spec/bugfixes/studio-ci-lock-tests/2026-10-15-lock-and-tos-tests.zh.md delete mode 100644 prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-final.md delete mode 100644 prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-final.zh.md delete mode 100644 prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-reasoning.md delete mode 100644 prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-reasoning.zh.md delete mode 100644 prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-direct-answer-reasoning.md delete mode 100644 prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-direct-answer-reasoning.zh.md delete mode 100644 prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-reasoning-snapshots.md delete mode 100644 prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-reasoning-snapshots.zh.md delete mode 100644 prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-wrapper-preview.md delete mode 100644 prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-wrapper-preview.zh.md delete mode 100644 prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-followup-content.md delete mode 100644 prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-followup-content.zh.md delete mode 100644 prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-sandbox-reasoning.md delete mode 100644 prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-sandbox-reasoning.zh.md delete mode 100644 prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-studio-codex-commentary-dedup.md delete mode 100644 prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-studio-codex-commentary-dedup.zh.md delete mode 100644 prd-spec/bugfixes/studio-cron-e2e/2026-09-15-integration.md delete mode 100644 prd-spec/bugfixes/studio-cron-e2e/2026-09-15-integration.zh.md delete mode 100644 prd-spec/bugfixes/studio-latest-image-refresh/2026-10-20-latest-image-snapshots.md delete mode 100644 prd-spec/bugfixes/studio-latest-image-refresh/2026-10-20-latest-image-snapshots.zh.md delete mode 100644 prd-spec/bugfixes/studio-latest-image-refresh/2026-10-21-readonly-tag-resolution.md delete mode 100644 prd-spec/bugfixes/studio-latest-image-refresh/2026-10-21-readonly-tag-resolution.zh.md delete mode 100644 prd-spec/bugfixes/studio-latest-image-refresh/2026-10-22-fork-ci-validation.md delete mode 100644 prd-spec/bugfixes/studio-latest-image-refresh/2026-10-22-fork-ci-validation.zh.md delete mode 100644 prd-spec/bugfixes/studio-mpa-a2a-preference/2026-09-30-prefer-a2a.md delete mode 100644 prd-spec/bugfixes/studio-mpa-a2a-preference/2026-09-30-prefer-a2a.zh.md delete mode 100644 prd-spec/bugfixes/studio-mpa-aidap-permissions/2026-09-23-complete-aidap-runtime-policy.md delete mode 100644 prd-spec/bugfixes/studio-mpa-aidap-permissions/2026-09-23-complete-aidap-runtime-policy.zh.md delete mode 100644 prd-spec/bugfixes/studio-mpa-aidap-permissions/2026-09-23-complete-mpa-cloud-runtime-policy.md delete mode 100644 prd-spec/bugfixes/studio-mpa-aidap-permissions/2026-09-23-complete-mpa-cloud-runtime-policy.zh.md delete mode 100644 prd-spec/bugfixes/studio-mpa-artifact-entry/2026-09-30-hide-artifact-entry.md delete mode 100644 prd-spec/bugfixes/studio-mpa-artifact-entry/2026-09-30-hide-artifact-entry.zh.md delete mode 100644 prd-spec/bugfixes/studio-mpa-cloud-iam/2026-10-15-cloud-iam.md delete mode 100644 prd-spec/bugfixes/studio-mpa-cloud-iam/2026-10-15-cloud-iam.zh.md delete mode 100644 prd-spec/bugfixes/studio-mpa-creation/2026-09-24-start-another-agent-after-failure.md delete mode 100644 prd-spec/bugfixes/studio-mpa-creation/2026-09-24-start-another-agent-after-failure.zh.md delete mode 100644 prd-spec/bugfixes/studio-mpa-dialog-theme/2026-09-20-dialog-theme.md delete mode 100644 prd-spec/bugfixes/studio-mpa-dialog-theme/2026-09-20-dialog-theme.zh.md delete mode 100644 prd-spec/bugfixes/studio-mpa-profile-errors/2026-09-20-studio-mpa-profile-errors.md delete mode 100644 prd-spec/bugfixes/studio-mpa-profile-errors/2026-09-20-studio-mpa-profile-errors.zh.md delete mode 100644 prd-spec/bugfixes/studio-mpa-writable-state/2026-09-23-use-temporary-state.md delete mode 100644 prd-spec/bugfixes/studio-mpa-writable-state/2026-09-23-use-temporary-state.zh.md delete mode 100644 prd-spec/bugfixes/studio-openviking-wheel/2026-10-07-align-sdk-wheel.md delete mode 100644 prd-spec/bugfixes/studio-openviking-wheel/2026-10-07-align-sdk-wheel.zh.md delete mode 100644 prd-spec/bugfixes/studio-session-protocol/2026-09-22-a2a-session-routing.md delete mode 100644 prd-spec/bugfixes/studio-session-protocol/2026-09-22-a2a-session-routing.zh.md delete mode 100644 prd-spec/bugfixes/studio-startup-assets/2026-09-22-studio-startup-assets.md delete mode 100644 prd-spec/bugfixes/studio-startup-assets/2026-09-22-studio-startup-assets.zh.md delete mode 100644 prd-spec/bugfixes/studio-tos-credentials/2026-10-15-tos-credentials.md delete mode 100644 prd-spec/bugfixes/studio-tos-credentials/2026-10-15-tos-credentials.zh.md delete mode 100644 prd-spec/bugfixes/studio-trace-pagination/2026-09-17-session-trace.md delete mode 100644 prd-spec/bugfixes/studio-trace-pagination/2026-09-17-session-trace.zh.md delete mode 100644 prd-spec/bugfixes/studio-webui-build-integrity/2026-09-23-rebuild-corrupted-assets.md delete mode 100644 prd-spec/bugfixes/studio-webui-build-integrity/2026-09-23-rebuild-corrupted-assets.zh.md delete mode 100644 prd-spec/bugfixes/tracing-memory/2026-10-13-bounded-cache.md delete mode 100644 prd-spec/bugfixes/tracing-memory/2026-10-13-bounded-cache.zh.md delete mode 100644 prd-spec/bugfixes/yaml-secret-guard/2026-09-23-yaml-secret-guard.md delete mode 100644 prd-spec/bugfixes/yaml-secret-guard/2026-09-23-yaml-secret-guard.zh.md delete mode 100644 prd-spec/features/bot-binding-types/2026-10-16-bot-binding-types.md delete mode 100644 prd-spec/features/bot-binding-types/2026-10-16-bot-binding-types.zh.md delete mode 100644 prd-spec/features/channel-auth-methods/2026-09-15-channel-auth-methods.md delete mode 100644 prd-spec/features/channel-auth-methods/2026-09-15-channel-auth-methods.zh.md delete mode 100644 prd-spec/features/channel-configuration-modes/2026-09-15-channel-configuration-modes.md delete mode 100644 prd-spec/features/channel-configuration-modes/2026-09-15-channel-configuration-modes.zh.md delete mode 100644 prd-spec/features/channel-qr-regeneration/2026-09-15-channel-qr-regeneration.md delete mode 100644 prd-spec/features/channel-qr-regeneration/2026-09-15-channel-qr-regeneration.zh.md delete mode 100644 prd-spec/features/deepseek-harness-runtime/2026-10-15-deepseek-harness-runtime.md delete mode 100644 prd-spec/features/deepseek-harness-runtime/2026-10-15-deepseek-harness-runtime.zh.md delete mode 100644 prd-spec/features/harness-tos-skills/2026-10-12-harness-tos-skills.md delete mode 100644 prd-spec/features/harness-tos-skills/2026-10-12-harness-tos-skills.zh.md delete mode 100644 prd-spec/features/lark-binding/2026-10-15-lark-binding.md delete mode 100644 prd-spec/features/lark-binding/2026-10-15-lark-binding.zh.md delete mode 100644 prd-spec/features/message-channel-binding/2026-09-15-message-channel-binding.md delete mode 100644 prd-spec/features/message-channel-binding/2026-09-15-message-channel-binding.zh.md delete mode 100644 prd-spec/features/message-channel-workspace/2026-09-15-message-channel-workspace.md delete mode 100644 prd-spec/features/message-channel-workspace/2026-09-15-message-channel-workspace.zh.md delete mode 100644 prd-spec/features/mpa-agent-info-rail/2026-09-18-mpa-agent-info-rail.md delete mode 100644 prd-spec/features/mpa-agent-info-rail/2026-09-18-mpa-agent-info-rail.zh.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-10-mpa-agent-oneclick-provision-design.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-10-mpa-agent-oneclick-provision-design.zh.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-11-mpa-agent-oneclick-provision-verification-cases.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-11-mpa-agent-oneclick-provision-verification-cases.zh.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-11-mpa-agent-provision-orchestration-alignment-design.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-11-mpa-agent-provision-orchestration-alignment-design.zh.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-12-mpa-agent-studio-a2a-streaming-design.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-12-mpa-agent-studio-a2a-streaming-design.zh.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-12-mpa-agent-studio-a2a-streaming-verification-cases.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-12-mpa-agent-studio-a2a-streaming-verification-cases.zh.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-20-studio-mpa-creation.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-20-studio-mpa-creation.zh.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-mpa-tos-output-mount.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-mpa-tos-output-mount.zh.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-builtin-mpa-profile.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-builtin-mpa-profile.zh.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-openviking-key.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-openviking-key.zh.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-three-step-creation.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-three-step-creation.zh.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-10-09-named-studio-creation.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/2026-10-09-named-studio-creation.zh.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/evidence/2026-09-11-code-review.zh.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/evidence/2026-09-11-verification-execution.zh.md delete mode 100644 prd-spec/features/mpa-agent-oneclick-provision/mpa-create.config.example.yaml delete mode 100644 prd-spec/features/mpa-agent-pg-workspace/2026-10-13-independent-business-workspace.md delete mode 100644 prd-spec/features/mpa-agent-pg-workspace/2026-10-13-independent-business-workspace.zh.md delete mode 100644 prd-spec/features/mpa-byteplus-openviking/2026-10-14-automatic-creation.md delete mode 100644 prd-spec/features/mpa-byteplus-openviking/2026-10-14-automatic-creation.zh.md delete mode 100644 prd-spec/features/mpa-byteplus-openviking/2026-10-14-inference-endpoints.md delete mode 100644 prd-spec/features/mpa-byteplus-openviking/2026-10-14-inference-endpoints.zh.md delete mode 100644 prd-spec/features/mpa-byteplus/2026-10-10-byteplus-creation.md delete mode 100644 prd-spec/features/mpa-byteplus/2026-10-10-byteplus-creation.zh.md delete mode 100644 prd-spec/features/mpa-channels-automation/2026-09-16-mpa-channels-automation.md delete mode 100644 prd-spec/features/mpa-channels-automation/2026-09-16-mpa-channels-automation.zh.md delete mode 100644 prd-spec/features/mpa-collaborating-agents/2026-10-20-collaborating-agents-detail-tab.md delete mode 100644 prd-spec/features/mpa-collaborating-agents/2026-10-20-collaborating-agents-detail-tab.zh.md delete mode 100644 prd-spec/features/mpa-collaborating-agents/2026-10-20-collaborating-agents.md delete mode 100644 prd-spec/features/mpa-collaborating-agents/2026-10-20-collaborating-agents.zh.md delete mode 100644 prd-spec/features/mpa-collaborating-agents/2026-10-20-direct-credentials.md delete mode 100644 prd-spec/features/mpa-collaborating-agents/2026-10-20-direct-credentials.zh.md delete mode 100644 prd-spec/features/mpa-collaborating-agents/2026-10-21-a2a-space.md delete mode 100644 prd-spec/features/mpa-collaborating-agents/2026-10-21-a2a-space.zh.md delete mode 100644 prd-spec/features/mpa-collaborating-agents/2026-10-22-center-registration.md delete mode 100644 prd-spec/features/mpa-collaborating-agents/2026-10-22-center-registration.zh.md delete mode 100644 prd-spec/features/mpa-collaborating-agents/2026-10-22-endpoints-and-skills.md delete mode 100644 prd-spec/features/mpa-collaborating-agents/2026-10-22-endpoints-and-skills.zh.md delete mode 100644 prd-spec/features/mpa-collaborating-agents/2026-10-22-member-actions.md delete mode 100644 prd-spec/features/mpa-collaborating-agents/2026-10-22-member-actions.zh.md delete mode 100644 prd-spec/features/mpa-collaborating-agents/2026-10-22-space-aliases.md delete mode 100644 prd-spec/features/mpa-collaborating-agents/2026-10-22-space-aliases.zh.md delete mode 100644 prd-spec/features/mpa-collaborating-agents/2026-10-22-space-management.md delete mode 100644 prd-spec/features/mpa-collaborating-agents/2026-10-22-space-management.zh.md delete mode 100644 prd-spec/features/mpa-create-scaling/2026-10-13-max-instance-input.md delete mode 100644 prd-spec/features/mpa-create-scaling/2026-10-13-max-instance-input.zh.md delete mode 100644 prd-spec/features/mpa-feishu-channels/2026-09-15-mpa-feishu-channels.md delete mode 100644 prd-spec/features/mpa-feishu-channels/2026-09-15-mpa-feishu-channels.zh.md delete mode 100644 prd-spec/features/mpa-feishu-multi-bot/2026-10-09-studio-multi-bot.md delete mode 100644 prd-spec/features/mpa-feishu-multi-bot/2026-10-09-studio-multi-bot.zh.md delete mode 100644 prd-spec/features/mpa-feishu-multi-bot/2026-10-10-port-multi-bot.md delete mode 100644 prd-spec/features/mpa-feishu-multi-bot/2026-10-10-port-multi-bot.zh.md delete mode 100644 prd-spec/features/mpa-image-update/2026-10-22-independent-images.md delete mode 100644 prd-spec/features/mpa-image-update/2026-10-22-independent-images.zh.md delete mode 100644 prd-spec/features/mpa-image-update/2026-10-22-latest-update-snapshots.md delete mode 100644 prd-spec/features/mpa-image-update/2026-10-22-latest-update-snapshots.zh.md delete mode 100644 prd-spec/features/mpa-image-update/2026-10-22-manual-image-source.md delete mode 100644 prd-spec/features/mpa-image-update/2026-10-22-manual-image-source.zh.md delete mode 100644 prd-spec/features/mpa-model-key-discovery/2026-10-10-account-ark-key.md delete mode 100644 prd-spec/features/mpa-model-key-discovery/2026-10-10-account-ark-key.zh.md delete mode 100644 prd-spec/features/mpa-model-key-discovery/2026-10-10-latest-created-key.md delete mode 100644 prd-spec/features/mpa-model-key-discovery/2026-10-10-latest-created-key.zh.md delete mode 100644 prd-spec/features/mpa-name-preflight/2026-10-13-runtime-name-and-workspace-labels.md delete mode 100644 prd-spec/features/mpa-name-preflight/2026-10-13-runtime-name-and-workspace-labels.zh.md delete mode 100644 prd-spec/features/mpa-openviking-auto/2026-10-13-default-model-creation.md delete mode 100644 prd-spec/features/mpa-openviking-auto/2026-10-13-default-model-creation.zh.md delete mode 100644 prd-spec/features/mpa-owned-resource-deletion/2026-10-12-owned-resource-deletion.md delete mode 100644 prd-spec/features/mpa-owned-resource-deletion/2026-10-12-owned-resource-deletion.zh.md delete mode 100644 prd-spec/features/mpa-p0-productionization/2026-09-15-dev-loop-spec-review.md delete mode 100644 prd-spec/features/mpa-p0-productionization/2026-09-15-dev-loop-spec-review.zh.md delete mode 100644 prd-spec/features/mpa-p0-productionization/2026-09-15-functional-validation-cases.md delete mode 100644 prd-spec/features/mpa-p0-productionization/2026-09-15-functional-validation-cases.zh.md delete mode 100644 prd-spec/features/mpa-p0-productionization/2026-09-15-m0-evidence.md delete mode 100644 prd-spec/features/mpa-p0-productionization/2026-09-15-m0-evidence.zh.md delete mode 100644 prd-spec/features/mpa-p0-productionization/2026-09-15-mpa-p0-productionization-design.md delete mode 100644 prd-spec/features/mpa-p0-productionization/2026-09-15-mpa-p0-productionization-design.zh.md delete mode 100644 prd-spec/features/mpa-p0-productionization/browser-cases.md delete mode 100644 prd-spec/features/mpa-p0-productionization/browser-cases.zh.md delete mode 100644 prd-spec/features/mpa-response-grouping/2026-09-22-mpa-response-grouping.md delete mode 100644 prd-spec/features/mpa-response-grouping/2026-09-22-mpa-response-grouping.zh.md delete mode 100644 prd-spec/features/mpa-runtime-iam/2026-10-09-managed-runtime-role.md delete mode 100644 prd-spec/features/mpa-runtime-iam/2026-10-09-managed-runtime-role.zh.md delete mode 100644 prd-spec/features/mpa-serverless-pg/2026-09-21-deployment-account-pg.md delete mode 100644 prd-spec/features/mpa-serverless-pg/2026-09-21-deployment-account-pg.zh.md delete mode 100644 prd-spec/features/mpa-space-scoped-resources/2026-09-23-auto-pg-workspaces.md delete mode 100644 prd-spec/features/mpa-space-scoped-resources/2026-09-23-auto-pg-workspaces.zh.md delete mode 100644 prd-spec/features/mpa-space-scoped-resources/2026-09-23-space-scoped-manual-pg.md delete mode 100644 prd-spec/features/mpa-space-scoped-resources/2026-09-23-space-scoped-manual-pg.zh.md delete mode 100644 prd-spec/features/mpa-space-scoped-resources/2026-09-23-two-pg-workspaces.md delete mode 100644 prd-spec/features/mpa-space-scoped-resources/2026-09-23-two-pg-workspaces.zh.md delete mode 100644 prd-spec/features/mpa-studio-control-plane/2026-09-13-mpa-studio-control-plane-design.md delete mode 100644 prd-spec/features/mpa-studio-control-plane/2026-09-13-mpa-studio-control-plane-design.zh.md delete mode 100644 prd-spec/features/mpa-studio-control-plane/2026-09-13-mpa-studio-control-plane-implementation-plan.md delete mode 100644 prd-spec/features/mpa-studio-control-plane/2026-09-13-mpa-studio-control-plane-implementation-plan.zh.md delete mode 100644 prd-spec/features/mpa-studio-workload-identity/2026-09-20-mpa-studio-workload-identity.md delete mode 100644 prd-spec/features/mpa-studio-workload-identity/2026-09-20-mpa-studio-workload-identity.zh.md delete mode 100644 prd-spec/features/skill-observability/2026-10-16-skill-observability.md delete mode 100644 prd-spec/features/skill-observability/2026-10-16-skill-observability.zh.md delete mode 100644 prd-spec/features/studio-agents-md-edit/2026-10-21-studio-agents-md-edit.md delete mode 100644 prd-spec/features/studio-agents-md-edit/2026-10-21-studio-agents-md-edit.zh.md delete mode 100644 prd-spec/features/studio-mpa-basic-only/2026-10-09-basic-only.md delete mode 100644 prd-spec/features/studio-mpa-basic-only/2026-10-09-basic-only.zh.md delete mode 100644 prd-spec/features/studio-mpa-cron-tasks/2026-09-15-mono-task-management.md delete mode 100644 prd-spec/features/studio-mpa-cron-tasks/2026-09-15-mono-task-management.zh.md delete mode 100644 prd-spec/features/studio-mpa-cron-tasks/2026-09-15-remove-agent-input.md delete mode 100644 prd-spec/features/studio-mpa-cron-tasks/2026-09-15-remove-agent-input.zh.md delete mode 100644 prd-spec/features/studio-mpa-cron-tasks/README.md delete mode 100644 prd-spec/features/studio-mpa-cron-tasks/README.zh.md delete mode 100644 prd-spec/features/studio-mpa-identity-reuse/2026-09-23-deploy-identity-reuse.md delete mode 100644 prd-spec/features/studio-mpa-identity-reuse/2026-09-23-deploy-identity-reuse.zh.md delete mode 100644 prd-spec/features/studio-mpa-login/2026-09-23-studio-mpa-prewarm.md delete mode 100644 prd-spec/features/studio-mpa-login/2026-09-23-studio-mpa-prewarm.zh.md delete mode 100644 prd-spec/features/studio-mpa-main-integration/2026-09-20-main-integration.md delete mode 100644 prd-spec/features/studio-mpa-main-integration/2026-09-20-main-integration.zh.md delete mode 100644 prd-spec/features/studio-mpa-source-filter/2026-09-28-studio-mpa-source-filter.md delete mode 100644 prd-spec/features/studio-mpa-source-filter/2026-09-28-studio-mpa-source-filter.zh.md delete mode 100644 prd-spec/features/studio-mpa-tos-iam/2026-10-12-bucket-only.md delete mode 100644 prd-spec/features/studio-mpa-tos-iam/2026-10-12-bucket-only.zh.md delete mode 100644 prd-spec/features/studio-oidc-id-token/2026-09-23-retain-id-token.md delete mode 100644 prd-spec/features/studio-oidc-id-token/2026-09-23-retain-id-token.zh.md delete mode 100644 prd-spec/features/studio-sandbox-download/2026-09-17-history-download.md delete mode 100644 prd-spec/features/studio-sandbox-download/2026-09-17-history-download.zh.md delete mode 100644 prd-spec/features/studio-tool-activity-visualization/2026-09-12-studio-tool-activity-visualization-design.md delete mode 100644 prd-spec/features/studio-tool-activity-visualization/2026-09-12-studio-tool-activity-visualization-design.zh.md delete mode 100644 prd-spec/refactors/mpa-contract-location/2026-10-14-studio-owned-resources.md delete mode 100644 prd-spec/refactors/mpa-contract-location/2026-10-14-studio-owned-resources.zh.md delete mode 100644 prd-spec/refactors/mpa-upstream-reconciliation/2026-10-15-byteplus-rebase.md delete mode 100644 prd-spec/refactors/mpa-upstream-reconciliation/2026-10-15-byteplus-rebase.zh.md create mode 100644 scripts/benchmark_codex_tools.py create mode 100644 scripts/codex_sandbox_probe.py delete mode 100644 specs/README.md delete mode 100644 specs/README.zh.md delete mode 100644 specs/apmplus-configuration/README.md delete mode 100644 specs/apmplus-configuration/README.zh.md delete mode 100644 specs/mpa-channels/README.md delete mode 100644 specs/mpa-channels/README.zh.md delete mode 100644 specs/mpa-collaborating-agents/README.md delete mode 100644 specs/mpa-collaborating-agents/README.zh.md delete mode 100644 specs/mpa-image-update/README.md delete mode 100644 specs/mpa-image-update/README.zh.md delete mode 100644 specs/mpa-runtime-control/README.md delete mode 100644 specs/mpa-runtime-control/README.zh.md delete mode 100644 specs/mpa-runtime-provisioning/README.md delete mode 100644 specs/mpa-runtime-provisioning/README.zh.md delete mode 100644 specs/runtime-deepseek-harness/README.md delete mode 100644 specs/runtime-deepseek-harness/README.zh.md delete mode 100644 specs/runtime-dependency-compatibility/README.md delete mode 100644 specs/runtime-dependency-compatibility/README.zh.md delete mode 100644 specs/skill-observability/README.md delete mode 100644 specs/skill-observability/README.zh.md delete mode 100644 specs/studio-agents-md/README.md delete mode 100644 specs/studio-agents-md/README.zh.md delete mode 100644 specs/studio-mpa-control-plane/README.md delete mode 100644 specs/studio-mpa-control-plane/README.zh.md delete mode 100644 specs/studio-mpa-creation/README.md delete mode 100644 specs/studio-mpa-creation/README.zh.md delete mode 100644 specs/studio-mpa-cron-tasks/README.md delete mode 100644 specs/studio-mpa-cron-tasks/README.zh.md delete mode 100644 specs/studio-mpa-prewarm/README.md delete mode 100644 specs/studio-mpa-prewarm/README.zh.md delete mode 100644 specs/studio-oauth-session/README.md delete mode 100644 specs/studio-oauth-session/README.zh.md delete mode 100644 specs/studio-runtime-diagnostics/README.md delete mode 100644 specs/studio-runtime-diagnostics/README.zh.md delete mode 100644 specs/studio-sandbox-download/README.md delete mode 100644 specs/studio-sandbox-download/README.zh.md delete mode 100644 specs/studio-skill-document/README.md delete mode 100644 specs/studio-skill-document/README.zh.md delete mode 100644 specs/studio-tool-activity/README.md delete mode 100644 specs/studio-tool-activity/README.zh.md delete mode 100644 specs/tracing-cache/README.md delete mode 100644 specs/tracing-cache/README.zh.md create mode 100644 tests/runtime/codex/test_codex_crash_recovery_smoke.py create mode 100644 tests/runtime/codex/test_codex_private_dirs.py create mode 100644 tests/runtime/codex/test_codex_process_pool.py create mode 100644 tests/runtime/codex/test_codex_sandbox_isolation_smoke.py create mode 100644 tests/runtime/codex/test_codex_tool_workloads.py create mode 100644 veadk/runtime/codex/private_dirs.py create mode 100644 veadk/runtime/codex/process_pool.py diff --git a/.gitignore b/.gitignore index d13eb1d30..78c8ca8b1 100644 --- a/.gitignore +++ b/.gitignore @@ -192,6 +192,8 @@ cython_debug/ # docs **/node_modules/ +prd-spec/ +specs/ # 忽略所有 .temp 目录 **/.temp/ @@ -212,7 +214,8 @@ cython_debug/ **/.adk/ # Local AI/design and verification artifacts. Keep evidence summaries in -# PRD/spec documents, not raw screenshots, traces, manifests, or run logs. +# change records or spec documents, not raw screenshots, traces, manifests, +# or run logs. /.artifacts/ /.superpowers/ /evidence/ diff --git a/AGENTS.md b/AGENTS.md index c6cf3afe8..d03a4684e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,24 +5,21 @@ VeADK includes a Python SDK, CLI/runtime integrations, and AgentKit Studio with ## Language and document ownership - Keep this repository-wide development standard in English. Use English for new code identifiers, comments, and docstrings; user-facing text follows the owning area's localization rules. -- Read [prd-spec/README.md](prd-spec/README.md) before creating or updating a change design. Every PRD/design must have complete English `.md` and Chinese `.zh.md` counterparts, sharing the same directory, date, and basename. -- Read [specs/README.md](specs/README.md) before defining or changing a component contract. Maintain each component under `specs//README.md` and `README.zh.md`. -- Keep both languages semantically equivalent and update them in the same change. Preserve identifiers, commands, examples, limits, and acceptance criteria across translations. Missing or conflicting counterparts block approval and delivery. -- PRDs own change-specific intent, scope, tasks, and acceptance; component specs own maintained responsibilities, interfaces, state, data, and failure semantics. Link them rather than duplicate contracts. +- Keep durable change intent, scope, tasks, evidence, and acceptance in the PR/MR description, linked issue, external design document, or affected user documentation when the change needs long-lived context. Do not create repository-local design or component-spec files by default. - Before modifying frontend code, read `frontend/SPEC.md`; also apply it to its supporting backend interfaces as specified there. It remains the Studio development standard and is not replaced or translated by this document. - Preserve existing user-documentation conventions: the documentation site uses Chinese `.mdx` and English `.en.mdx`; bilingual module/example READMEs use English `README.md` and Chinese `README.zh.md`. ## Required development workflow 1. **Inspect and agree on scope.** Read the affected implementation, tests, and contracts. When the user describes a requirement, propose an implementation plan and wait for the user to be satisfied before making edits. Distinguish verified facts from assumptions. -2. **Design before implementation.** Features, refactors, and bugfixes require a bilingual PRD under `prd-spec/features/`, `prd-spec/refactors/`, or `prd-spec/bugfixes/`. Include background/evidence, goals, non-goals, scenarios, requirements, design, affected files, tasks, tests, risks, and acceptance criteria. -3. **Assess component contracts.** Create or update affected bilingual component specs for changes to ownership, APIs, configuration, state/data, events, permissions, security, compatibility, runtime behavior, or observability. Record a justified no-impact conclusion when no component contract changes. Do not backfill unrelated components. -4. **Clear design review.** Review PRD/spec consistency, feasibility, boundaries, errors, security, compatibility, testability, and bilingual equivalence. Use the available `review-spec` skill; if unavailable, perform and record the same review directly. Resolve blockers and record user approval before modifying production code or tests. A generic skill's Chinese-only default does not override this repository's bilingual rule. +2. **Plan before implementation.** For features, refactors, and bugfixes, size the change against the affected contracts and risk. Record background/evidence, goals, non-goals, affected files, tasks, tests, risks, and acceptance criteria in the PR/MR description, linked issue, external design document, or user documentation when the change needs durable context. +3. **Assess contracts.** Identify changes to ownership, APIs, configuration, state/data, events, permissions, security, compatibility, runtime behavior, or observability. Record the contract impact or no-impact rationale in the PR/MR description, linked issue, external design document, or user documentation as appropriate. Do not backfill unrelated contracts. +4. **Clear scope review.** Review plan consistency, feasibility, boundaries, errors, security, compatibility, and testability. Resolve blockers and record user approval before modifying production code or tests. 5. **Implement against the approved design.** Map changes to requirements/tasks, write a failing regression or contract test first, then implement and refactor within scope. Contract or scope changes return to design review before implementation proceeds. 6. **Verify and review.** Run affected tests first, then required repository and component gates. Review both implementation correctness and edge cases, resource handling, security, test quality, and contract alignment. Fix blocking findings and rerun affected checks. -7. **Reconcile and deliver.** Synchronize both document languages, component contracts, affected user docs/examples, and required generated artifacts. Record actual verification commands, results, unverified areas, and remaining risks. All agreed tasks and acceptance criteria must be satisfied before declaring completion; deferred scope requires user agreement. +7. **Reconcile and deliver.** Synchronize affected user docs/examples and required generated artifacts. Record actual verification commands, results, unverified areas, and remaining risks. All agreed tasks and acceptance criteria must be satisfied before declaring completion; deferred scope requires user agreement. -Pure documentation, translation, or formatting changes that preserve runtime behavior and public contracts do not need a recursive PRD. They still require an agreed approach, applicable bilingual updates, and proportionate checks. This exception includes maintaining these development guidelines; it does not exempt code or behavioral changes. +Pure documentation, translation, or formatting changes that preserve runtime behavior and public contracts do not need a separate change design. They still require an agreed approach, applicable bilingual updates where the owning document convention requires them, and proportionate checks. This exception includes maintaining these development guidelines; it does not exempt code or behavioral changes. ## Implementation boundaries @@ -37,10 +34,10 @@ Pure documentation, translation, or formatting changes that preserve runtime beh - Do not treat `uv.lock` success as proof that published package metadata is installable. PyPI users resolve from `pyproject.toml` metadata, not this repository's lock file. - When changing runtime dependencies in `pyproject.toml`, consider both direct use and shared transitive constraints from key upstream packages such as `google-adk`, `agentkit-sdk-python`, OpenTelemetry, `mcp`, `fastmcp`, `litellm`, and storage/cloud SDKs. - High-risk upstream dependencies must declare an explicit tested upper bound. Do not leave them open-ended when an unadapted future version could break installation or runtime behavior. -- Avoid exact pins for shared base dependencies unless there is a concrete compatibility reason. If an exact pin is necessary, document the reason in the dependency comment or the change PRD. +- Avoid exact pins for shared base dependencies unless there is a concrete compatibility reason. If an exact pin is necessary, document the reason in the dependency comment, PR/MR description, linked issue, or external design document. - Before widening a high-risk dependency range, check whether the highest allowed version still resolves with the current runtime pins. Use `scripts/check_dependency_e2e.py declared-range` or the release workflow's manual dispatch when the check is too heavy for ordinary PR iteration. - If a dependency upgrade only works by relying on another package's current transitive dependency, declare the required direct dependency explicitly in `pyproject.toml` rather than depending on accidental resolver behavior. -- If a heavy dependency E2E check is intentionally not run before merge, record the risk and the expected tag/manual workflow coverage in the PRD or PR description. +- If a heavy dependency E2E check is intentionally not run before merge, record the risk and the expected tag/manual workflow coverage in the PR/MR description, linked issue, or external design document. ## Verification gates @@ -54,19 +51,20 @@ Use the repository-local Python environment and current `pyproject.toml`, `pytes | Studio frontend | `npm --prefix frontend test` and `npm --prefix frontend run build`, plus real browser checks of the normal flow and affected loading, empty, error, cancellation, retry, keyboard/IME, and narrow-window cases. Follow `frontend/SPEC.md` for additional checks and release asset requirements. | | Studio backend or generated Python | Targeted Python tests plus Ruff and Pyright for changed Python files, as required by `frontend/SPEC.md`; verify generated code, dependencies, configuration, and deployment payloads together. | | Harness/sidecar contracts | The affected checks and existing coverage thresholds in `.github/workflows/harness-sidecar-release-gate.yaml`, including `npm --prefix frontend run test:harness-sidecar-coverage` when frontend sidecar contracts change. | -| PRD/spec and guideline-only changes | Check paired-language completeness, contract/identifier consistency, relative links, referenced paths/commands, and diff whitespace. No runtime test result may be claimed from documentation checks. | +| Guideline-only changes | Check document consistency, relative links, referenced paths/commands, and diff whitespace. No runtime test result may be claimed from documentation checks. | | User documentation site | The affected Markdown/MDX checks and build/type checks defined in `docs/package.json`; do not apply site-specific tooling to unrelated plain Markdown without checking its scope. | - Before committing, run `uv run --extra dev pre-commit run --all-files` and unit tests after synchronizing the branch as described below. Pre-commit includes Ruff and secret scanning; it does not replace browser, integration, or runtime verification. - Prefer targeted checks during iteration. Expand to affected integration/E2E and regression gates for shared or high-risk changes and final delivery; explain any omitted required check. - Record check outcomes as `pass`, `fail`, `blocked`, `not_run`, or `not_applicable`, with reasons for the last three. Include the tested revision/diff scope and execution date. Skipped tests do not prove the relevant behavior. -- Keep change-specific review and verification summaries in the bilingual PRD by default. Separate evidence artifacts are optional when useful; OpenSpec and a multi-report hierarchy are not prerequisites. +- Keep change-specific review and verification summaries in the PR/MR description, linked issue, external design document, or affected user documentation as appropriate. Separate evidence artifacts are optional when useful; OpenSpec and a multi-report hierarchy are not prerequisites. ## Git and execution safety - Do not use `codex/` as a branch name prefix. Use semantic prefixes such as `feat/`, `fix/`, `chore/`, or `docs/`, with a name reflecting the requirement. - After creating or switching to a new branch, run `git pull` before development. If no upstream exists or synchronization fails, resolve the target explicitly rather than guessing or bypassing the requirement. - Before committing, fetch the latest remote code, rebase the branch onto the intended remote base, then run pre-commit and unit tests. Preserve unrelated user changes and stop if safe synchronization requires a user decision. +- Before committing, check whether the change affects public Python imports/signatures, CLI/configuration, generated projects, Studio API types, runtime/event contracts, user-visible behavior, documentation, dependency policy, or compatibility. If it does, make sure the GitHub PR, linked issue, release notes, or user documentation records the impact, compatibility notes, verification, and remaining risks; if it does not, state that no public GitHub-facing record is required in the PR/MR or handoff. - Commit only changes related to the current feature or fix. Commit, push, PR creation, publication, and deployment require user authorization; approval of a design does not authorize them. - Never commit real security-sensitive information or private data to GitHub. This includes API keys, access keys, secret keys, bearer/session/refresh tokens, cookies, signed URLs, private certificates, production logs with credentials, personal data dumps, and cloud account secrets. Use placeholders, environment variables, secret references, or documented local-only setup instead. - The repository pre-commit configuration must reject hardcoded API key and token material before a commit is created. If secret scanning reports a finding, stop and remove or redact the value rather than bypassing the hook. diff --git a/docs/content/docs/framework/agent/runtime.en.mdx b/docs/content/docs/framework/agent/runtime.en.mdx index ead8459c0..ca7d37665 100644 --- a/docs/content/docs/framework/agent/runtime.en.mdx +++ b/docs/content/docs/framework/agent/runtime.en.mdx @@ -152,7 +152,9 @@ agent = Agent( The Codex SDK's built-in approval handler answers every `requestApproval` notification with `accept`, and `AsyncCodex` exposes no hook to replace it. So `auto_review` auto-approves every sandbox escalation and file change without consulting a human or ADK. Only the default `deny_all` actually keeps Codex inside the sandbox. Do not use `auto_review` for multi-tenant deployments or untrusted input. -One sandbox/network combination is easy to misread: `network_access` is written to the `[sandbox_workspace_write]` table of Codex's `config.toml`, and **only the `workspace_write` sandbox reads it**. +**Session isolation.** For `workspace_write` and `read_only`, VeADK runs Codex's sandboxed commands under its own permissions profile (`default_permissions = "veadk"` in the generated `config.toml`) instead of Codex's `workspace-write`/`read-only` presets, which let commands read the whole disk and write `$TMPDIR` and `/tmp`, where every session's workspace and `CODEX_HOME` live. The profile starts from read-only access, denies the temp dirs (and a non-shared `workspace_root`), and grants back only this session's workspace (writable under `workspace_write`) and its own skills (readable). Commands get the workspace as `TMPDIR`. On Linux, Codex's sandbox (bubblewrap) cannot reach a read-only grant beneath a denied directory, so there the session's own workspace and skills are granted write access instead, which means `read_only` on Linux still lets commands write the session's own workspace; other sessions stay unreachable. So one session's shell can neither read nor modify another session's workspace, rollout or configuration. Files elsewhere on the host (for example the application's own config or a local SQLite session database) remain readable; isolate tenants at the container or OS-user level when that matters. The Linux sandbox differs from macOS Seatbelt: verify on your deployment image with `CODEX_RUN_SMOKE=1 uv run --extra dev pytest -m codex_smoke -p no:xdist tests/runtime/codex/test_codex_sandbox_isolation_smoke.py`. + +One sandbox/network combination is easy to misread: `network_access` is written to that permissions profile's `network` table, and **only the `workspace_write` sandbox honours it**. | `sandbox` | Is `network_access` honoured? | | :--- | :--- | @@ -298,6 +300,7 @@ Either way, a path that came from the *model* is untrusted input: resolve it aga | :--- | :--- | :--- | | `reasoning_effort` | `"medium"` | Reasoning budget: `minimal`/`low`/`medium`/`high`/`xhigh`. Higher is slower and uses more tokens. | | `personality` | `"pragmatic"` | Codex's own reply style: `none`/`friendly`/`pragmatic`. | +| `mcp_process_reuse` | `True` | Same-instance reuse for stable ADK/MCP tools on `direct` + `resume`, with fresh per-invocation executors and SDK thread/turn identity validation. Set `False` to disable MCP process reuse. | | `model_transport` | `"auto"` | How Codex reaches the model. `direct`: Codex calls a Responses-capable backend itself and uses the agent's tools through a local MCP server the runtime runs for the turn, so Codex drives the tool loop. `shim`: an in-process Responses→Chat shim sits in between and executes the tools, for chat-only backends. `auto` picks `direct` for Volcengine Ark (`*.volces.com`), BytePlus (`*.bytepluses.com`) and `api.openai.com`, `shim` otherwise; an agent whose `model_extra_config` has a non-empty `extra_body` stays on `shim` under `auto`, because `direct` cannot forward a request body. With an explicit `direct`, `extra_body` is not forwarded and a warning is logged. | | `turn_timeout_seconds` | `1800.0` | Upper bound for one Codex turn, in seconds; past it the turn is interrupted and a `TimeoutError` is raised. `None` means no bound. On the `direct` transport with `thread_mode="resume"`, invocations of one session queue on a per-session lock, so an unbounded turn would also block every later invocation of that session; the default is therefore 30 minutes. In the other modes invocations of one session may run concurrently. | | `auto_compact_token_limit` | `None` | Token count at which Codex compacts the history itself (Codex's `model_auto_compact_token_limit`). `direct` transport only; `None` keeps Codex's default. | @@ -311,6 +314,20 @@ Either way, a path that came from the *model* is untrusted input: resolve it aga It now bounds the **whole Codex turn**, not a single backend request. Codex issues a fresh backend request after every native tool round, so the old per-request counter actually allowed *rounds × budget* tool executions. The default was raised at the same time so that turns which reach for an ADK tool only after several native tool rounds are not cut short. If you were relying on `8` as a cost backstop, re-evaluate — `RunConfig(max_llm_calls=...)` (below) is now the better ceiling. + +### Parallel and batch tool workloads + +Independent async function tools can already execute concurrently when Codex emits them in the same model response. Mark independence in application instructions and concise tool descriptions; dependent reads and writes must retain their required ordering. An application-owned batch tool can bound independent reads and return a result or explicit error for each item. Authorize each item: ADK tools execute on the host, so the Codex sandbox does not replace business permissions. One batch is one SDK tool invocation, and the application must cap the number of business reads separately. + +The checkout provides a local example and benchmark, `scripts/benchmark_codex_tools.py`, with `single`, `parallel`, `serial` and `batch` scenarios. Its `bounded_batch_lookup` example limits admission, applies per-item timeouts, preserves cancellation and reports partial failures. It never combines arbitrary registered tools automatically. + +```bash +uv run --extra dev python scripts/benchmark_codex_tools.py --scenario parallel --turns 5 --pool-size 20 +uv run --extra dev python scripts/benchmark_codex_tools.py --scenario parallel --turns 5 --pool-size 0 +``` + +These runs use the real Codex binary and a scripted loopback model without provider tokens. They validate warm hits, model request counts, context growth and tool results; their latency is framework/simulation evidence, not a real-model performance claim. Run the real-binary regression separately with `CODEX_RUN_SMOKE=1 uv run --extra dev pytest tests/runtime/codex/test_codex_tool_workloads.py -m codex_smoke -p no:xdist -rs`. + ### How instructions reach Codex Codex ships its own ~20KB system prompt, tuned for its own toolchain (`apply_patch`, `update_plan`, shell, AGENTS.md). VeADK deliberately does **not** override it via `base_instructions`: Codex *replaces* the built-in template outright when that field is set, which would delete all of that guidance (Codex's own docs call the equivalent config key strongly discouraged). @@ -329,7 +346,7 @@ The `direct` transport removes `request_user_input` (and `web_search`, multi-age ### Codex environment variables -The six variables below **override** the Python `CodexRuntimeConfig` rather than acting as defaults for it. A hard-coded `sandbox="workspace_write"` in your code is still overridden by `VEADK_CODEX_SANDBOX=full_access` in the deployment environment. This precedence is the opposite of most settings and matters most where the platform injects environment variables (containers, FaaS). +The variables below **override** the Python `CodexRuntimeConfig` rather than acting as defaults for it. A hard-coded `sandbox="workspace_write"` in your code is still overridden by `VEADK_CODEX_SANDBOX=full_access` in the deployment environment. This precedence is the opposite of most settings and matters most where the platform injects environment variables (containers, FaaS). | Variable | Overrides | Notes | @@ -340,6 +357,22 @@ The six variables below **override** the Python `CodexRuntimeConfig` rather than | `VEADK_CODEX_NETWORK_ACCESS` | `network_access` | `1`/`true`/`yes`/`on` enable it; anything else disables it. | | `VEADK_CODEX_MODEL_TRANSPORT` | `model_transport` | `auto`/`direct`/`shim`. | | `VEADK_CODEX_THREAD_MODE` | `thread_mode` | `resume`/`ephemeral`. | +| `VEADK_CODEX_MCP_PROCESS_REUSE` | `mcp_process_reuse` | Guarded MCP process reuse is enabled by default. `0`/`false`/`no`/`off` disable it; `1`/`true`/`yes`/`on` enable it. `VEADK_CODEX_PROCESS_POOL_SIZE=0` disables all reuse. | + +On the `direct` transport with `thread_mode="resume"`, both tool-free turns and turns exposing stable ADK/MCP tools may keep their process by default. Set `mcp_process_reuse=False` or `VEADK_CODEX_MCP_PROCESS_REUSE=0` to disable MCP process reuse while retaining tool-free reuse. Each invocation binds fresh executors, callbacks, workspace and tool budgets. Before execution the bridge requires the SDK-owned thread/turn IDs; stale, missing or inconsistent identities and repeated process-wide request IDs are rejected. Idle processes admit no tool calls. The process is reused only in the same store/session/thread with unchanged model, permissions, skills, instructions and tool manifest and matching stored thread identity/version. Errors, interruptions, failed persistence and ambiguous request lifecycles close it. `VEADK_CODEX_PROCESS_POOL_SIZE=0` retains the cold path for all invocations. + +Keeping processes alive trades resident memory for lower setup and context costs; model latency still affects whole-turn performance. Repeated request IDs after an MCP reconnect are rejected before execution, and the process is closed rather than replaying an already started turn. Use the disable switch when the application needs per-invocation process lifetime. + +Reuse avoids repeated skills/environment injection on the warm path; actual cold recovery still keeps the required context. An existing empty workspace is not reported as reset; a newly recreated workspace is. + +| Variable | Default | Notes | +| :--- | :--- | :--- | +| `VEADK_CODEX_PROCESS_POOL_SIZE` | `20` | Idle processes kept per VeADK process; the least recently used is closed beyond it. `0` disables reuse (one process per invocation). | +| `VEADK_CODEX_PROCESS_IDLE_TTL_SECONDS` | `600` | An idle process is closed after this many seconds. | +| `VEADK_CODEX_MAX_CONCURRENT_TURNS` | unset (no cap) | Maximum Codex turns (each its own `codex app-server`, roughly 100MB) running at once per VeADK process; further invocations wait for a slot before allocating a workspace, private home, tools, session lock, or child process. Admission, setup, and execution share `turn_timeout_seconds`; pre-turn expiry raises `CodexTurnAdmissionTimeout`. A recursively transferred Codex agent in the same task does not take a second slot. | +| `VEADK_CODEX_DENY_READ_PATHS` | unset | Extra host paths (separated by `os.pathsep`, `:` on Linux/macOS) that sandboxed commands may not read, e.g. the application's config and `.env`, or a local SQLite session database. | + +Warm processes are released on their own after the idle TTL. To release them earlier, `await veadk.runtime.codex.release_codex_session(app_name, user_id, session_id)` closes one session's warm processes (call it when deleting a session), and `await veadk.runtime.codex.shutdown_codex_runtime()` closes all of them together with the MCP bridge and shims; the AgentKit app calls the latter on shutdown. If a VeADK process is killed, its Codex processes exit with it, and a later VeADK process on the host removes locked workspace and `CODEX_HOME` roots only when it can prove their owner is gone. Ownerless directories from older VeADK versions are retained because age alone cannot prove that no live process uses them. Four more variables tune the Responses→Chat shim. These are not part of the override rule above — they only tune the shim itself: diff --git a/docs/content/docs/framework/agent/runtime.mdx b/docs/content/docs/framework/agent/runtime.mdx index b18c21d70..f1021f515 100644 --- a/docs/content/docs/framework/agent/runtime.mdx +++ b/docs/content/docs/framework/agent/runtime.mdx @@ -146,7 +146,9 @@ agent = Agent( Codex SDK 内置的审批处理器对每一个 `requestApproval` 通知都回答 `accept`,而 `AsyncCodex` 没有提供替换该处理器的接口。因此 `auto_review` 会自动批准每一次沙箱提权和文件修改,既不询问人工,也不经过 ADK。只有默认的 `deny_all` 才真正把 Codex 约束在沙箱内。在多租户或不可信输入场景中,请勿使用 `auto_review`。 -关于沙箱与网络的组合,有一个容易误判的点:`network_access` 只会写入 Codex `config.toml` 的 `[sandbox_workspace_write]` 段,**只有 `workspace_write` 沙箱会读取它**。 +**Session 隔离。** `workspace_write` 和 `read_only` 下,VeADK 不再使用 Codex 的 `workspace-write`/`read-only` 预设(它们允许命令读取整个磁盘、写入 `$TMPDIR` 和 `/tmp`,而所有 Session 的工作目录和 `CODEX_HOME` 都在这里),而是让沙箱命令运行在 VeADK 生成的权限配置下(`config.toml` 中的 `default_permissions = "veadk"`)。该配置以只读访问为基础,拒绝访问临时目录(以及非共享的 `workspace_root`),只放行当前 Session 的工作目录(`workspace_write` 下可写)和它自己的 skills(只读),并把工作目录设为命令的 `TMPDIR`。Linux 上 Codex 的沙箱(bubblewrap)无法访问位于被拒绝目录之下的只读放行路径,因此在 Linux 上当前 Session 的工作目录和 skills 改为授予写权限,也就是说 Linux 上的 `read_only` 仍允许命令写入当前 Session 自己的工作目录;其他 Session 依然无法访问。因此一个 Session 的 shell 既读不到、也改不了其他 Session 的工作目录、rollout 和配置。主机上的其他文件(例如应用自身的配置、本地 SQLite Session 数据库)仍然可读;需要隔离时请在容器或操作系统用户层面隔离租户。Linux 沙箱与 macOS Seatbelt 实现不同,请在部署镜像上运行 `CODEX_RUN_SMOKE=1 uv run --extra dev pytest -m codex_smoke -p no:xdist tests/runtime/codex/test_codex_sandbox_isolation_smoke.py` 验证。 + +关于沙箱与网络的组合,有一个容易误判的点:`network_access` 会写入上述权限配置的 `network` 段,**只有 `workspace_write` 沙箱会遵循它**。 | `sandbox` | `network_access` 是否生效 | | :--- | :--- | @@ -266,6 +268,7 @@ def load_orders(day: str) -> dict: | :--- | :--- | :--- | | `reasoning_effort` | `"medium"` | 推理强度,可选 `minimal`/`low`/`medium`/`high`/`xhigh`。越高越慢、消耗 token 越多。 | | `personality` | `"pragmatic"` | Codex 自带的回复风格,可选 `none`/`friendly`/`pragmatic`。 | +| `mcp_process_reuse` | `True` | 默认开启 `direct` + `resume` 下稳定 ADK/MCP 工具的本实例进程复用;每轮重新绑定执行器并校验 SDK 的 thread/turn 身份。设置 `False` 关闭 MCP 进程复用。 | | `model_transport` | `"auto"` | Codex 如何访问模型:`direct` 让 Codex 直接调用支持 Responses API 的后端,并通过 runtime 为本轮启动的本地 MCP server 使用 Agent 的工具,工具循环由 Codex 驱动;`shim` 经由进程内 Responses→Chat 转换层,由转换层执行工具,适用于只支持 chat 的后端;`auto` 对火山方舟(`*.volces.com`)、BytePlus(`*.bytepluses.com`)与 `api.openai.com` 选 `direct`,其余选 `shim`;但 Agent 的 `model_extra_config` 含非空 `extra_body` 时,`auto` 仍选 `shim`,因为 `direct` 无法转发请求体。显式指定 `direct` 时 `extra_body` 不会被转发,并记录一条警告。 | | `turn_timeout_seconds` | `1800.0` | 单个 Codex 回合的时间上限(秒),到期后中断回合并抛出 `TimeoutError`;`None` 表示不限。`direct` 传输且 `thread_mode="resume"` 时,同一 Session 的调用在一把 Session 级锁上排队,不设上限的回合会同时阻塞该 Session 之后的所有调用,因此默认给出 30 分钟上限;其余模式同一 Session 的调用可以并发执行。 | | `auto_compact_token_limit` | `None` | 历史 token 超过该值时由 Codex 自行压缩(Codex 的 `model_auto_compact_token_limit`),仅 `direct` 传输生效;`None` 沿用 Codex 默认值。 | @@ -279,6 +282,20 @@ def load_orders(day: str) -> dict: 它现在约束的是**整个 Codex turn**,而不再是单次后端请求。Codex 每完成一次原生工具调用就会发起一次新的后端请求,所以旧的「每请求」计数实际允许的执行次数是「请求轮数 × budget」。默认值同时调高,是为了让「先跑若干轮原生工具、之后才调用 ADK 工具」的回合不被提前截断。如果你此前靠 `8` 这个值来兜底控制成本,请重新评估——现在更合适的成本上限是 `RunConfig(max_llm_calls=...)`(见下)。 + +### 并行与批量工具场景 + +当 Codex 在同一次模型响应里生成多个独立异步函数调用时,现有链路即可并行执行。应用 instruction 和简洁的工具描述应明确哪些查询独立;有依赖的读取和写入仍按业务顺序执行。应用可以提供批量工具,对每个查询项返回结果或明确错误,并逐项鉴权:ADK 工具在宿主进程执行,Codex 沙箱不能替代业务权限。一个批量请求计作一次 SDK 工具调用,应用须另外限制业务查询数量。 + +仓库的 `scripts/benchmark_codex_tools.py` 提供 `single`、`parallel`、`serial`、`batch` 示例与基准。其 `bounded_batch_lookup` 限制批量准入、设置逐项超时、保留取消行为并报告部分失败,不会自动合并任意已注册工具。 + +```bash +uv run --extra dev python scripts/benchmark_codex_tools.py --scenario parallel --turns 5 --pool-size 20 +uv run --extra dev python scripts/benchmark_codex_tools.py --scenario parallel --turns 5 --pool-size 0 +``` + +这些运行使用真实 Codex 二进制和本地模拟模型,不消耗 provider tokens;验证复用命中、模型请求次数、上下文增长和工具结果,时延属于框架与模拟证据,不能替代真实模型性能测量。真实二进制回归单独运行:`CODEX_RUN_SMOKE=1 uv run --extra dev pytest tests/runtime/codex/test_codex_tool_workloads.py -m codex_smoke -p no:xdist -rs`。 + ### 指令是怎么下发给 Codex 的 Codex 自带一份约 20KB、针对自身工具链(`apply_patch`、`update_plan`、shell、AGENTS.md)调优过的系统提示词。VeADK **不会**通过 `base_instructions` 覆盖它——Codex 在该字段被设置时会**整体替换**内置模板,这些指导会被全部删掉(Codex 官方文档也强烈不建议这么做)。 @@ -297,7 +314,7 @@ Codex 自带一份约 20KB、针对自身工具链(`apply_patch`、`update_pla ### Codex 环境变量 -以下六个环境变量**覆盖** Python 中的 `CodexRuntimeConfig`,而不是作为它的默认值。也就是说,即使代码里写死了 `sandbox="workspace_write"`,部署环境里的 `VEADK_CODEX_SANDBOX=full_access` 依然会生效。这一优先级与大多数配置项相反,在容器/函数计算等由平台注入环境变量的场景中尤其需要注意。 +以下环境变量**覆盖** Python 中的 `CodexRuntimeConfig`,而不是作为它的默认值。也就是说,即使代码里写死了 `sandbox="workspace_write"`,部署环境里的 `VEADK_CODEX_SANDBOX=full_access` 依然会生效。这一优先级与大多数配置项相反,在容器/函数计算等由平台注入环境变量的场景中尤其需要注意。 | 环境变量 | 覆盖的配置项 | 说明 | @@ -308,6 +325,22 @@ Codex 自带一份约 20KB、针对自身工具链(`apply_patch`、`update_pla | `VEADK_CODEX_NETWORK_ACCESS` | `network_access` | `1`/`true`/`yes`/`on` 视为开启,其余视为关闭。 | | `VEADK_CODEX_MODEL_TRANSPORT` | `model_transport` | `auto`/`direct`/`shim`。 | | `VEADK_CODEX_THREAD_MODE` | `thread_mode` | `resume`/`ephemeral`。 | +| `VEADK_CODEX_MCP_PROCESS_REUSE` | `mcp_process_reuse` | 默认开启带身份校验的 MCP 进程复用。`0`/`false`/`no`/`off` 关闭,`1`/`true`/`yes`/`on` 开启。`VEADK_CODEX_PROCESS_POOL_SIZE=0` 关闭全部复用。 | + +`direct` 传输且 `thread_mode="resume"` 时,无工具回合与暴露稳定 ADK/MCP 工具的回合默认都可以保留进程。设置 `mcp_process_reuse=False` 或 `VEADK_CODEX_MCP_PROCESS_REUSE=0` 关闭 MCP 进程复用,无工具回合仍可复用。每轮重新绑定执行器、回调、工作目录和工具预算;执行前必须匹配 SDK 确认的 thread/turn ID,旧轮次、缺失或不一致的身份及进程内重复的请求 ID 均被拒绝。空闲进程不接受工具执行。仅在同一存储、Session、thread,且模型、权限、skills、instruction、工具声明未变,存储中的 thread 身份和版本仍匹配时复用。错误、中断、保存失败或请求生命周期无法确定时关闭进程。`VEADK_CODEX_PROCESS_POOL_SIZE=0` 保持所有回合每轮新起进程的路径。 + +保留进程会用常驻内存换取更少的启动与上下文开销,整轮性能仍受模型时延影响。MCP 重连后出现重复请求 ID 时,会在工具执行前拒绝并关闭进程,不会自动重放已开始的回合。需要每轮独立进程生命周期的应用可使用关闭开关。 + +复用避免在正常 warm 路径反复注入 skills 和环境上下文;真正冷恢复时仍保留必要上下文。已有的空工作目录不再被提示为重置,新建或重建的目录仍会提示。 + +| 环境变量 | 默认值 | 说明 | +| :--- | :--- | :--- | +| `VEADK_CODEX_PROCESS_POOL_SIZE` | `20` | 每个 VeADK 进程最多保留的空闲进程数,超出时关闭最久未用的。`0` 表示关闭复用(每次调用一个进程)。 | +| `VEADK_CODEX_PROCESS_IDLE_TTL_SECONDS` | `600` | 空闲进程超过该秒数后关闭。 | +| `VEADK_CODEX_MAX_CONCURRENT_TURNS` | 未设置(不限) | 每个 VeADK 进程同时运行的 Codex 回合上限(每个回合一个 `codex app-server`,约 100MB)。超出的调用会在分配 workspace、private home、tools、session lock 或子进程前排队;准入、setup 和执行共享 `turn_timeout_seconds`,回合开始前过期会抛出 `CodexTurnAdmissionTimeout`。同一 Task 内递归转交给的 Codex Agent 不重复占用名额。 | +| `VEADK_CODEX_DENY_READ_PATHS` | 未设置 | 沙箱命令禁止读取的额外主机路径(用 `os.pathsep` 分隔,Linux/macOS 上为 `:`),例如应用自身的配置和 `.env`、本地 SQLite Session 数据库。 | + +常驻进程会在空闲超时后自动释放。如需提前释放:`await veadk.runtime.codex.release_codex_session(app_name, user_id, session_id)` 关闭某个 Session 的常驻进程(删除 Session 时调用);`await veadk.runtime.codex.shutdown_codex_runtime()` 关闭全部常驻进程以及 MCP bridge 和 shim,AgentKit 应用在关闭时会自动调用它。VeADK 进程被强杀时,它的 Codex 子进程会随之退出;同一主机上后续的 VeADK 进程只会在能证明 owner 已退出时清理带锁的 workspace 和 `CODEX_HOME` root。旧版本留下的无 owner 标记目录会保留,因为仅凭目录年龄无法证明没有活跃进程正在使用它。 另有四个用于 Responses→Chat 转换层(shim)的环境变量。它们不参与上面的覆盖规则,只是 shim 自身的调参: diff --git a/frontend/README.md b/frontend/README.md index f2c4aa284..6b006fd88 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -8,11 +8,10 @@ server that `veadk frontend` launches — no separate backend. Studio administrators can open **Collaborating Agents** under **Agents → MPA → selected agent**, in the tab below **Basic information**. Add, edit, enable/disable, check and remove external Agent registrations. Connection checks validate Agent Cards only; task dispatch is not implemented here. Credentials are Runtime environment references, not browser -secret input. See the [configuration and handoff contract](../specs/mpa-collaborating-agents/README.md). +secret input. Studio 管理员可进入「智能体 → MPA 智能体 → 选中智能体」,在「基本信息」下的 Tab 打开「协作 Agent」,增删改查、启停并验证 Agent Card。凭据直接输入,默认隐藏且可切换显示;保存后不回显,编辑留空保留,后端自动加密持久化。启用控制派发资格,停用保留配置;本次不包含任务派发。需同步升级 Runtime。 -参见[配置及交接说明](../specs/mpa-collaborating-agents/README.zh.md)。 ## Release notifications @@ -22,12 +21,10 @@ This is the Runtime autoscaling ceiling (`MaxInstance`), not the initial number of running instances. Submitted requests retain their selected value on retry; legacy requests without this field keep their original configuration. Duplicate names show the existing error message in a red alert. General-agent creation is unchanged. -See [the creation contract](../specs/studio-mpa-creation/README.md). MPA 基本信息提供可编辑的“最大实例数”,火山默认 20、BytePlus 默认 10,对应 Runtime 的扩容上限 `MaxInstance`,并非初始启动实例数。已提交请求重试沿用原值;没有此字段的旧请求 保留原配置。名称重复时以红色错误框突出原提示,通用智能体创建不变。 -参见[创建契约](../specs/studio-mpa-creation/README.zh.md)。 MPA session output storage accepts only optional buckets beginning with `agentkit-platform-`. The Worker Tool uses IAM Role without TOS keys; creation additively grants that role @@ -1619,7 +1616,7 @@ DatePicker 在基础组件中展示日期与日期时间选择;IndexLayout 在 全屏入口为 `/components-preview/?fullscreen=app-layout#app-layout`,详见 [组件预览说明](src/components-preview/README.md) ### MPA 消息渠道 -在“自动化 → 消息渠道”打开“MPA智能体消息渠道”,与飞书机器人创建、网站集成并列。选择已有 MPA 智能体后配置飞书、企业微信、钉钉;原智能体详情不再显示消息渠道栏目。列表按当前账号授权范围加载,选项展示名称、描述、“创建者 | 相对创建时间”,以及地域和 Runtime ID,支持按名称、Runtime ID、地域搜索已加载结果,并可加载更多;切换智能体会清理临时二维码和凭据,已有绑定继续保留。三个渠道均提供“极速配置”和“手动配置”,默认极速配置,切换方式后仅显示对应内容。极速配置沿用飞书、钉钉扫码绑定和企业微信官方 SDK 弹窗授权;手动配置分别填写飞书 App ID / App Secret、钉钉 Client ID / Client Secret、企业微信 Bot ID / Secret;仅飞书提供允许群列表,并独立展示消息网关、入站路由和回复投递状态。钉钉与企业微信无需配置本地群白名单。所有渠道均可解绑,旧 Runtime 必须升级后才能使用新增绑定接口。需要 Studio 管理员身份、Runtime 的 `CHANNEL_ADMIN_AUTH_MODE=runtime_key`,以及通过部署配置注入并在更新时保留的 `CHANNEL_STATE_ENCRYPTION_KEY`(Fernet key)。渠道数据库必须独立于其他 MPA 实例。飞书和钉钉的扫码凭据由服务端获取;企微 SDK 授权结果仅在浏览器内存中短暂存在,立即提交 Runtime 保存并完成注册,取消、切页和五分钟超时会清理弹窗。配置完成不等于收发验证通过;部署后请单独验收私聊和授权群 @ 对话。详见 [接口契约](../specs/mpa-channels/README.zh.md)。 +在“自动化 → 消息渠道”打开“MPA智能体消息渠道”,与飞书机器人创建、网站集成并列。选择已有 MPA 智能体后配置飞书、企业微信、钉钉;原智能体详情不再显示消息渠道栏目。列表按当前账号授权范围加载,选项展示名称、描述、“创建者 | 相对创建时间”,以及地域和 Runtime ID,支持按名称、Runtime ID、地域搜索已加载结果,并可加载更多;切换智能体会清理临时二维码和凭据,已有绑定继续保留。三个渠道均提供“极速配置”和“手动配置”,默认极速配置,切换方式后仅显示对应内容。极速配置沿用飞书、钉钉扫码绑定和企业微信官方 SDK 弹窗授权;手动配置分别填写飞书 App ID / App Secret、钉钉 Client ID / Client Secret、企业微信 Bot ID / Secret;仅飞书提供允许群列表,并独立展示消息网关、入站路由和回复投递状态。钉钉与企业微信无需配置本地群白名单。所有渠道均可解绑,旧 Runtime 必须升级后才能使用新增绑定接口。需要 Studio 管理员身份、Runtime 的 `CHANNEL_ADMIN_AUTH_MODE=runtime_key`,以及通过部署配置注入并在更新时保留的 `CHANNEL_STATE_ENCRYPTION_KEY`(Fernet key)。渠道数据库必须独立于其他 MPA 实例。飞书和钉钉的扫码凭据由服务端获取;企微 SDK 授权结果仅在浏览器内存中短暂存在,立即提交 Runtime 保存并完成注册,取消、切页和五分钟超时会清理弹窗。配置完成不等于收发验证通过;部署后请单独验收私聊和授权群 @ 对话。 已绑定飞书、钉钉或企业微信时,可点击“重新生成二维码”展开“消息渠道扫码配对”。生成二维码不会先解绑当前机器人;飞书扫码并绑定成功后替换原机器人,可在授权流程选择绑定存量机器人或新建机器人。 @@ -1682,9 +1679,9 @@ Studio 新请求为每个 MPA 准备独立 `mpa_agent_` Workspac The MPA creation dialog asks for a Runtime name (4–64 ASCII letters, digits, underscores or hyphens) and description. The server generates the stable Agent ID; neither ID nor image inputs are shown. Fresh requests use the built-in `mpa/mpa_agent:latest` and `mpa/mpa_codex_worker:latest` images. Submitted legacy drafts retain their original request and images for retry. Existing Runtimes are unchanged. See [managed creation](../veadk/integrations/mpa/managed/README.md). -The dialog now uses three steps: basics (required Runtime name and description), automatic PostgreSQL preparation, and OpenViking preparation. It links to the relevant Volcengine console pages. The server obtains PostgreSQL Workspace connections through deployment credentials. New Volcengine Beijing requests automatically prepare an independent paid enterprise OpenViking library and inject its four Runtime variables; retries reuse the registered library. New BytePlus Johor requests automatically prepare a personal library using the resolved MPA ModelArk API Key and two inference endpoints (server overrides: `OV_VLM_ENDPOINT_ID` / `OV_EMBEDDING_ENDPOINT_ID`). Its default-user and overseas data-plane behavior require a separately adapted Runtime image before memory works. Already-submitted manual requests still enable OpenViking only when all three connection values are entered; leaving them blank omits the four variables, including inherited values. The key is never saved in browser drafts or task SQLite. Only the final step submits; nonsecret draft choices and request identity survive reopening in the same browser session. See the [creation contract](../specs/studio-mpa-creation/README.md). +The dialog now uses three steps: basics (required Runtime name and description), automatic PostgreSQL preparation, and OpenViking preparation. It links to the relevant Volcengine console pages. The server obtains PostgreSQL Workspace connections through deployment credentials. New Volcengine Beijing requests automatically prepare an independent paid enterprise OpenViking library and inject its four Runtime variables; retries reuse the registered library. New BytePlus Johor requests automatically prepare a personal library using the resolved MPA ModelArk API Key and two inference endpoints (server overrides: `OV_VLM_ENDPOINT_ID` / `OV_EMBEDDING_ENDPOINT_ID`). Its default-user and overseas data-plane behavior require a separately adapted Runtime image before memory works. Already-submitted manual requests still enable OpenViking only when all three connection values are entered; leaving them blank omits the four variables, including inherited values. The key is never saved in browser drafts or task SQLite. Only the final step submits; nonsecret draft choices and request identity survive reopening in the same browser session. -创建弹窗现分为基础信息(必填 Runtime 名称和描述)、PostgreSQL 自动准备、OpenViking 准备说明三步,并提供对应的火山引擎控制台入口。服务端通过部署凭据获取 PostgreSQL Workspace 连接。火山北京新请求自动准备独立的付费企业版 OpenViking 库并注入四个 Runtime 变量,重试复用已登记库。BytePlus 柔佛新请求使用已解析的 MPA ModelArk API Key 和两个推理接入点(服务端可通过 `OV_VLM_ENDPOINT_ID` / `OV_EMBEDDING_ENDPOINT_ID` 覆盖)自动准备个人版库;其 default 用户及海外数据面行为需要单独适配 Runtime 镜像后才能使用记忆。已提交手动请求仍在三项连接值全部填写时启用 OpenViking;全空时不注入四个变量,也不继承旧值。密钥不保存到浏览器草稿或任务 SQLite。仅最后一步提交,非密钥草稿与请求身份在同一浏览器会话中可恢复。参见[创建契约](../specs/studio-mpa-creation/README.zh.md)。 +创建弹窗现分为基础信息(必填 Runtime 名称和描述)、PostgreSQL 自动准备、OpenViking 准备说明三步,并提供对应的火山引擎控制台入口。服务端通过部署凭据获取 PostgreSQL Workspace 连接。火山北京新请求自动准备独立的付费企业版 OpenViking 库并注入四个 Runtime 变量,重试复用已登记库。BytePlus 柔佛新请求使用已解析的 MPA ModelArk API Key 和两个推理接入点(服务端可通过 `OV_VLM_ENDPOINT_ID` / `OV_EMBEDDING_ENDPOINT_ID` 覆盖)自动准备个人版库;其 default 用户及海外数据面行为需要单独适配 Runtime 镜像后才能使用记忆。已提交手动请求仍在三项连接值全部填写时启用 OpenViking;全空时不注入四个变量,也不继承旧值。密钥不保存到浏览器草稿或任务 SQLite。仅最后一步提交,非密钥草稿与请求身份在同一浏览器会话中可恢复。 After a confirmed failure or cancellation, the dialog retains the old task for same-ID retry. “Create another agent” generates a new request UUID and blank name; the server derives the new agent ID on submission and returns to the first step. It replaces only the browser draft, not any cloud resources from the previous attempt. Running or uncertain submissions cannot switch identities. @@ -1786,7 +1783,7 @@ with bounded parallel reads. This is intended for the initial dataset sizes; large collections will need a separate rebuildable query index. ### MPA 消息渠道 -在“自动化 → 消息渠道”打开“MPA智能体消息渠道”,与飞书机器人创建、网站集成并列。选择已有 MPA 智能体后配置飞书、企业微信、钉钉;原智能体详情不再显示消息渠道栏目。列表按当前账号授权范围加载,选项展示名称、描述、“创建者 | 相对创建时间”,以及地域和 Runtime ID,支持按名称、Runtime ID、地域搜索已加载结果,并可加载更多;切换智能体会清理临时二维码和凭据,已有绑定继续保留。三个渠道均提供“极速配置”和“手动配置”,默认极速配置,切换方式后仅显示对应内容。极速配置沿用飞书、钉钉扫码绑定和企业微信官方 SDK 弹窗授权;手动配置分别填写飞书 App ID / App Secret、钉钉 Client ID / Client Secret、企业微信 Bot ID / Secret;仅飞书提供允许群列表,并独立展示消息网关、入站路由和回复投递状态。钉钉与企业微信无需配置本地群白名单。所有渠道均可解绑,旧 Runtime 必须升级后才能使用新增绑定接口。需要 Studio 管理员身份、Runtime 的 `CHANNEL_ADMIN_AUTH_MODE=runtime_key`,以及通过部署配置注入并在更新时保留的 `CHANNEL_STATE_ENCRYPTION_KEY`(Fernet key)。渠道数据库必须独立于其他 MPA 实例。飞书和钉钉的扫码凭据由服务端获取;企微 SDK 授权结果仅在浏览器内存中短暂存在,立即提交 Runtime 保存并完成注册,取消、切页和五分钟超时会清理弹窗。配置完成不等于收发验证通过;部署后请单独验收私聊和授权群 @ 对话。详见 [接口契约](../specs/mpa-channels/README.zh.md)。 +在“自动化 → 消息渠道”打开“MPA智能体消息渠道”,与飞书机器人创建、网站集成并列。选择已有 MPA 智能体后配置飞书、企业微信、钉钉;原智能体详情不再显示消息渠道栏目。列表按当前账号授权范围加载,选项展示名称、描述、“创建者 | 相对创建时间”,以及地域和 Runtime ID,支持按名称、Runtime ID、地域搜索已加载结果,并可加载更多;切换智能体会清理临时二维码和凭据,已有绑定继续保留。三个渠道均提供“极速配置”和“手动配置”,默认极速配置,切换方式后仅显示对应内容。极速配置沿用飞书、钉钉扫码绑定和企业微信官方 SDK 弹窗授权;手动配置分别填写飞书 App ID / App Secret、钉钉 Client ID / Client Secret、企业微信 Bot ID / Secret;仅飞书提供允许群列表,并独立展示消息网关、入站路由和回复投递状态。钉钉与企业微信无需配置本地群白名单。所有渠道均可解绑,旧 Runtime 必须升级后才能使用新增绑定接口。需要 Studio 管理员身份、Runtime 的 `CHANNEL_ADMIN_AUTH_MODE=runtime_key`,以及通过部署配置注入并在更新时保留的 `CHANNEL_STATE_ENCRYPTION_KEY`(Fernet key)。渠道数据库必须独立于其他 MPA 实例。飞书和钉钉的扫码凭据由服务端获取;企微 SDK 授权结果仅在浏览器内存中短暂存在,立即提交 Runtime 保存并完成注册,取消、切页和五分钟超时会清理弹窗。配置完成不等于收发验证通过;部署后请单独验收私聊和授权群 @ 对话。 已绑定飞书、钉钉或企业微信时,可点击“重新生成二维码”展开“消息渠道扫码配对”。生成二维码不会先解绑当前机器人;飞书扫码并绑定成功后替换原机器人,可在授权流程选择绑定存量机器人或新建机器人。 @@ -1839,9 +1836,9 @@ Studio 新请求为每个 MPA 准备独立 `mpa_agent_` Workspac The MPA creation dialog asks for a Runtime name (4–64 ASCII letters, digits, underscores or hyphens) and description. The server generates the stable Agent ID; neither ID nor image inputs are shown. Fresh requests use the built-in `mpa/mpa_agent:latest` and `mpa/mpa_codex_worker:latest` images. Submitted legacy drafts retain their original request and images for retry. Existing Runtimes are unchanged. See [managed creation](../veadk/integrations/mpa/managed/README.md). -The dialog now uses three steps: basics (required Runtime name and description), automatic PostgreSQL preparation, and OpenViking preparation. It links to the relevant Volcengine console pages. The server obtains PostgreSQL Workspace connections through deployment credentials. New Volcengine Beijing requests automatically prepare an independent paid enterprise OpenViking library and inject its four Runtime variables; retries reuse the registered library. New BytePlus Johor requests automatically prepare a personal library using the resolved MPA ModelArk API Key and two inference endpoints (server overrides: `OV_VLM_ENDPOINT_ID` / `OV_EMBEDDING_ENDPOINT_ID`). Its default-user and overseas data-plane behavior require a separately adapted Runtime image before memory works. Already-submitted manual requests still enable OpenViking only when all three connection values are entered; leaving them blank omits the four variables, including inherited values. The key is never saved in browser drafts or task SQLite. Only the final step submits; nonsecret draft choices and request identity survive reopening in the same browser session. See the [creation contract](../specs/studio-mpa-creation/README.md). +The dialog now uses three steps: basics (required Runtime name and description), automatic PostgreSQL preparation, and OpenViking preparation. It links to the relevant Volcengine console pages. The server obtains PostgreSQL Workspace connections through deployment credentials. New Volcengine Beijing requests automatically prepare an independent paid enterprise OpenViking library and inject its four Runtime variables; retries reuse the registered library. New BytePlus Johor requests automatically prepare a personal library using the resolved MPA ModelArk API Key and two inference endpoints (server overrides: `OV_VLM_ENDPOINT_ID` / `OV_EMBEDDING_ENDPOINT_ID`). Its default-user and overseas data-plane behavior require a separately adapted Runtime image before memory works. Already-submitted manual requests still enable OpenViking only when all three connection values are entered; leaving them blank omits the four variables, including inherited values. The key is never saved in browser drafts or task SQLite. Only the final step submits; nonsecret draft choices and request identity survive reopening in the same browser session. -创建弹窗现分为基础信息(必填 Runtime 名称和描述)、PostgreSQL 自动准备、OpenViking 准备说明三步,并提供对应的火山引擎控制台入口。服务端通过部署凭据获取 PostgreSQL Workspace 连接。火山北京新请求自动准备独立的付费企业版 OpenViking 库并注入四个 Runtime 变量,重试复用已登记库。BytePlus 柔佛新请求使用已解析的 MPA ModelArk API Key 和两个推理接入点(服务端可通过 `OV_VLM_ENDPOINT_ID` / `OV_EMBEDDING_ENDPOINT_ID` 覆盖)自动准备个人版库;其 default 用户及海外数据面行为需要单独适配 Runtime 镜像后才能使用记忆。已提交手动请求仍在三项连接值全部填写时启用 OpenViking;全空时不注入四个变量,也不继承旧值。密钥不保存到浏览器草稿或任务 SQLite。仅最后一步提交,非密钥草稿与请求身份在同一浏览器会话中可恢复。参见[创建契约](../specs/studio-mpa-creation/README.zh.md)。 +创建弹窗现分为基础信息(必填 Runtime 名称和描述)、PostgreSQL 自动准备、OpenViking 准备说明三步,并提供对应的火山引擎控制台入口。服务端通过部署凭据获取 PostgreSQL Workspace 连接。火山北京新请求自动准备独立的付费企业版 OpenViking 库并注入四个 Runtime 变量,重试复用已登记库。BytePlus 柔佛新请求使用已解析的 MPA ModelArk API Key 和两个推理接入点(服务端可通过 `OV_VLM_ENDPOINT_ID` / `OV_EMBEDDING_ENDPOINT_ID` 覆盖)自动准备个人版库;其 default 用户及海外数据面行为需要单独适配 Runtime 镜像后才能使用记忆。已提交手动请求仍在三项连接值全部填写时启用 OpenViking;全空时不注入四个变量,也不继承旧值。密钥不保存到浏览器草稿或任务 SQLite。仅最后一步提交,非密钥草稿与请求身份在同一浏览器会话中可恢复。 After a confirmed failure or cancellation, the dialog retains the old task for same-ID retry. “Create another agent” generates a new request UUID and blank name; the server derives the new agent ID on submission and returns to the first step. It replaces only the browser draft, not any cloud resources from the previous attempt. Running or uncertain submissions cannot switch identities. @@ -1890,10 +1887,7 @@ MPA 智能体消息渠道”中选择机器人。扫码或手动添加后,启 权限;账号读取失败或无权限时阻止飞书任务提交,不任意选择机器人。旧 Runtime 保持单机器人界面,兼容的 MPA 镜像需另行部署。 -Contracts and verification / 契约与验证: -[channels](../specs/mpa-channels/README.md), -[tasks](../specs/studio-mpa-cron-tasks/README.md), -[design](../prd-spec/features/mpa-feishu-multi-bot/2026-10-09-studio-multi-bot.md). +Public contract impact and verification should be recorded in the GitHub PR, linked issue, release notes, or user documentation for the change. ### General and MPA deployment isolation @@ -1914,20 +1908,20 @@ MPA deletion waits up to 180 seconds. If the browser times out, Studio makes one MPA 删除最多等待 180 秒。浏览器超时后,Studio 会只读检查一次:仅 Runtime 明确返回 NotFound 且完整列表也不存在时确认完成。否则显示结果尚未确认,需稍后刷新检查,不能直接认定失败。通用智能体仍使用原来的 30 秒超时。结果确认要求相同的 Studio 已认证用户和部署凭据;后端重启或凭据轮换可能导致结果无法确认。此修改不改变资源清理和保留规则。更新后请重启 Studio 并刷新页面。 -MPA runtime-role preparation now distinguishes missing deployment IAM permissions from temporary IAM/network failures. Temporary failures retry within the role deadline; exhausted attempts display an actionable same-agent retry message. Safe operation/category/Request ID diagnostics are retained by the server. See the [IAM recovery design](../prd-spec/bugfixes/mpa-iam-retry/2026-10-14-transient-errors.md). +MPA runtime-role preparation now distinguishes missing deployment IAM permissions from temporary IAM/network failures. Temporary failures retry within the role deadline; exhausted attempts display an actionable same-agent retry message. Safe operation/category/Request ID diagnostics are retained by the server. -MPA 运行角色准备现区分部署 IAM 权限不足与临时 IAM/网络失败。临时失败在角色期限内重试,耗尽后提示用同一智能体重试。服务端保留安全的接口/类别/Request ID 诊断。见[IAM 恢复设计](../prd-spec/bugfixes/mpa-iam-retry/2026-10-14-transient-errors.zh.md)。 +MPA 运行角色准备现区分部署 IAM 权限不足与临时 IAM/网络失败。临时失败在角色期限内重试,耗尽后提示用同一智能体重试。服务端保留安全的接口/类别/Request ID 诊断。 BytePlus managed MPA creation in ap-southeast-1 again automatically prepares personal OpenViking. New/editable requests use automatic configuration without manual credentials. Submitted no-OV/automatic/manual requests retain their original recovery mode; use New Agent to enable OV after a no-OV submission. Domestic automatic OV and uncertain-create protection are unchanged. MPA same-request retries validate the original stored payload and deployment-account snapshot before checking owned Runtime/Worker names. A fresh same-name request still fails. Empty Runtime owner tags can only be supplemented by the authenticated original intent; foreign metadata remains rejected. This restores recovery after a failed readiness check without changing generic-agent creation. -BytePlus Studio MPA creation now retrieves the deployment-account APM AppKey through the overseas service host and injects `OBSERVABILITY_OPENTELEMETRY_APMPLUS_API_KEY` into Runtime Envs. The key stays out of browser/task/deployment records; lookup failures stop resource preparation with safe diagnostics. Restart Studio and use a new request. Domestic/general-agent creation and CLI profiles are unchanged. See the [APM key design](../prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-server-app-key.md). +BytePlus Studio MPA creation now retrieves the deployment-account APM AppKey through the overseas service host and injects `OBSERVABILITY_OPENTELEMETRY_APMPLUS_API_KEY` into Runtime Envs. The key stays out of browser/task/deployment records; lookup failures stop resource preparation with safe diagnostics. Restart Studio and use a new request. Domestic/general-agent creation and CLI profiles are unchanged. -BytePlus Studio MPA 创建现通过海外服务 Host 获取部署账号的 APM AppKey,注入 Runtime Envs 的 `OBSERVABILITY_OPENTELEMETRY_APMPLUS_API_KEY`。Key 不进入浏览器/任务/部署记录;获取失败时以安全诊断停止资源准备。重启 Studio 后使用新请求。国内/通用智能体创建和 CLI 配置不变。见 [APM Key 设计](../prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-server-app-key.zh.md)。 +BytePlus Studio MPA 创建现通过海外服务 Host 获取部署账号的 APM AppKey,注入 Runtime Envs 的 `OBSERVABILITY_OPENTELEMETRY_APMPLUS_API_KEY`。Key 不进入浏览器/任务/部署记录;获取失败时以安全诊断停止资源准备。重启 Studio 后使用新请求。国内/通用智能体创建和 CLI 配置不变。 -MPA 创建的镜像输入继续使用服务端 latest 默认值;部署层匿名只读匹配同公共仓库已有、digest 完全相同的非 latest tag,并持久化选择;重试只验证原选择。无需仓库写权限、手填版本或新增流水线;发布方须保留匹配版本标签且不得覆盖,无匹配项明确失败。详见 [Studio MPA 镜像快照契约](../specs/studio-mpa-creation/README.zh.md#studio-latest-内容快照2026-10-21)。 +MPA 创建的镜像输入继续使用服务端 latest 默认值;部署层匿名只读匹配同公共仓库已有、digest 完全相同的非 latest tag,并持久化选择;重试只验证原选择。无需仓库写权限、手填版本或新增流水线;发布方须保留匹配版本标签且不得覆盖,无匹配项明确失败。 ### MPA creation failure details / MPA 创建失败详情 @@ -1961,8 +1955,6 @@ debug/publish environments. Bot Profile overrides and MA remote-host are unchang 这里直接更新选中的 Runtime,不新增 ArkClaw 的调试/发布双环境;Bot Profile 覆盖规则和 MA remote-host 保持原有行为。 -Contracts: [English](../specs/studio-agents-md/README.md) / [中文](../specs/studio-agents-md/README.zh.md). - ### A2A Center registration MPA administrators open **Collaborating Agents → A2A Center**, select a space, and choose **Register Agent**. Select an authorized existing Runtime and its public/private endpoint, or import an Agent Card as JSON/HTTPS URL. Registrations live in AgentKit; **Connect selected space** controls which space this MPA can use. No per-Agent MPA binding is required. Creation uses the Studio server cloud identity; space listing/dispatch uses Runtime identity, so both must access the same account/region/space. On an unknown create result, refresh and reconcile before registering again. The previous local manual-registration form is retired; existing local configuration is cleaned only when its upgraded Runtime starts. @@ -1970,16 +1962,16 @@ MPA administrators open **Collaborating Agents → A2A Center**, select a space, ### A2A space management -Collaborating Agents separates space binding from space members. Use **New space** to create an AgentKit A2A space in the target Runtime region with a name, project (default `default`) and optional semantic search (off by default). Studio requires management authorization, target Runtime access and cloud creation permission. Creation selects the space; click **Connect selected space** separately to bind it. **Register Agent** registers in the space shown in the members section. After an unconfirmed create or cancellation during submission, refresh and check the space list before another attempt. Local tests use simulated cloud responses; live provider creation requires separate verification. See the [bilingual design](../prd-spec/features/mpa-collaborating-agents/2026-10-22-space-management.md). +Collaborating Agents separates space binding from space members. Use **New space** to create an AgentKit A2A space in the target Runtime region with a name, project (default `default`) and optional semantic search (off by default). Studio requires management authorization, target Runtime access and cloud creation permission. Creation selects the space; click **Connect selected space** separately to bind it. **Register Agent** registers in the space shown in the members section. After an unconfirmed create or cancellation during submission, refresh and check the space list before another attempt. Local tests use simulated cloud responses; live provider creation requires separate verification. -A2A Center member rows provide **View** and **Deregister**. View opens a bounded details dialog; deregistration removes the shared registry record after confirmation and preserves its source Runtime. **Delete space** requires an empty space and disconnection from the current MPA; the server rechecks both before deletion and never cascades member removal. Other applications may still depend on the space, so deletion requires explicit confirmation. Runtime registrations rejected as duplicates now show a specific message instead of an unconfirmed-write warning. Network/unknown write outcomes still require read reconciliation before another attempt. See the [member action design](../prd-spec/features/mpa-collaborating-agents/2026-10-22-member-actions.md). +A2A Center member rows provide **View** and **Deregister**. View opens a bounded details dialog; deregistration removes the shared registry record after confirmation and preserves its source Runtime. **Delete space** requires an empty space and disconnection from the current MPA; the server rechecks both before deletion and never cascades member removal. Other applications may still depend on the space, so deletion requires explicit confirmation. Runtime registrations rejected as duplicates now show a specific message instead of an unconfirmed-write warning. Network/unknown write outcomes still require read reconciliation before another attempt. -A2A registration now reads the chosen Runtime's configured public/private endpoints and displays their addresses; endpoint loading, read errors and unavailable addresses block Runtime registration until resolved. View includes the sanitized effective service address and AgentCard skills (name, description, tags and examples), with distinct empty/unavailable states. URL query/fragment data and credentials are excluded. Space deletion preflight uses the same server-owned Runtime Studio credential as normal collaborator reads; Runtime authentication failure is reported separately from cloud permission refusal. Restart the Studio backend and refresh the page after updating. See the [endpoint/skill design](../prd-spec/features/mpa-collaborating-agents/2026-10-22-endpoints-and-skills.md) and [authentication fix](../prd-spec/bugfixes/mpa-a2a-space-deletion/2026-10-22-runtime-auth.md). +A2A registration now reads the chosen Runtime's configured public/private endpoints and displays their addresses; endpoint loading, read errors and unavailable addresses block Runtime registration until resolved. View includes the sanitized effective service address and AgentCard skills (name, description, tags and examples), with distinct empty/unavailable states. URL query/fragment data and credentials are excluded. Space deletion preflight uses the same server-owned Runtime Studio credential as normal collaborator reads; Runtime authentication failure is reported separately from cloud permission refusal. Restart the Studio backend and refresh the page after updating. -A2A Runtime registration now has one required Runtime picker. The selected Runtime ID is carried automatically; there is no separate manual-ID input. Runtime-list loading, empty and error states remain distinct, with retry for empty/error and pagination for more choices. A selection removed by a list refresh invalidates its old endpoint and cannot submit. The existing registration API and imported Agent Card workflows remain unchanged. See the [single-picker fix](../prd-spec/bugfixes/mpa-a2a-runtime-picker/2026-10-22-single-runtime-picker.md). +A2A Runtime registration now has one required Runtime picker. The selected Runtime ID is carried automatically; there is no separate manual-ID input. Runtime-list loading, empty and error states remain distinct, with retry for empty/error and pagination for more choices. A selection removed by a list refresh invalidates its old endpoint and cannot submit. The existing registration API and imported Agent Card workflows remain unchanged. ### A2A Agent aliases / A2A Agent 别名 @@ -1989,9 +1981,9 @@ In Collaborating Agents → A2A Center, use **Set alias** on a registered Agent. ### MPA image updates / MPA 镜像更新 -MPA management navigation places **Image updates** second, between Basic and Collaborating Agents. Runtime and Worker each accept a manually entered full tagged image address or a version from their corresponding fixed domestic/overseas public repository. Entering an address clears the public selection; selecting a public version clears the manual address. Both paths show the complete target and require independent confirmation. Use **Load more versions** to see additional public tags. Manual addresses may use other registries and remain usable when catalog loading fails; Studio validates format without contacting those registries, while AgentKit checks image pull permissions and compatibility. Runtime updates request immediate release; Worker updates change only the bound, exclusively managed template. Existing sandbox sessions may keep their old image: create a new session to use the updated template. A submitted request is not proof of application health. Refresh status to reconcile; an unknown outcome blocks duplicate submission. Restart Studio with its original cloud credential environment and refresh the browser to activate the new backend and assets. See the [image management contract](../specs/mpa-image-update/README.md). +MPA management navigation places **Image updates** second, between Basic and Collaborating Agents. Runtime and Worker each accept a manually entered full tagged image address or a version from their corresponding fixed domestic/overseas public repository. Entering an address clears the public selection; selecting a public version clears the manual address. Both paths show the complete target and require independent confirmation. Use **Load more versions** to see additional public tags. Manual addresses may use other registries and remain usable when catalog loading fails; Studio validates format without contacting those registries, while AgentKit checks image pull permissions and compatibility. Runtime updates request immediate release; Worker updates change only the bound, exclusively managed template. Existing sandbox sessions may keep their old image: create a new session to use the updated template. A submitted request is not proof of application health. Refresh status to reconcile; an unknown outcome blocks duplicate submission. Restart Studio with its original cloud credential environment and refresh the browser to activate the new backend and assets. -MPA 管理页将 **镜像更新** 放在基本信息和协作 Agent 之间。Runtime 与 Worker 均支持手填完整带标签镜像地址,或从对应国内/海外固定公共仓库选择版本。输入地址清空公共版本,选择公共版本清空手填内容;两种方式均展示完整目标并独立确认更新。可通过 **加载更多版本** 查看其他公共标签。手填可使用其他仓库,不受目录加载失败影响;Studio 仅校验格式而不请求该仓库,拉取权限和兼容性由 AgentKit 检查。Runtime 请求立即发布;Worker 只更新绑定且可证明独占归属的托管模板。已有沙箱会话可能保留原镜像,请新建会话使用新模板。提交成功不代表应用健康,需刷新状态核对;结果未确认时禁止重复提交。更新后须使用原云凭据环境重启 Studio 并刷新浏览器,启用新后端和资源。参见[镜像管理契约](../specs/mpa-image-update/README.zh.md)。 +MPA 管理页将 **镜像更新** 放在基本信息和协作 Agent 之间。Runtime 与 Worker 均支持手填完整带标签镜像地址,或从对应国内/海外固定公共仓库选择版本。输入地址清空公共版本,选择公共版本清空手填内容;两种方式均展示完整目标并独立确认更新。可通过 **加载更多版本** 查看其他公共标签。手填可使用其他仓库,不受目录加载失败影响;Studio 仅校验格式而不请求该仓库,拉取权限和兼容性由 AgentKit 检查。Runtime 请求立即发布;Worker 只更新绑定且可证明独占归属的托管模板。已有沙箱会话可能保留原镜像,请新建会话使用新模板。提交成功不代表应用健康,需刷新状态核对;结果未确认时禁止重复提交。更新后须使用原云凭据环境重启 Studio 并刷新浏览器,启用新后端和资源。 Selecting **latest** from a public repository resolves it to an existing version before confirmation. The dialog shows the actual image and content digest; submission verifies the selected version again and does not follow a later change to latest. A missing/overwritten version or failed verification prevents an update. Read-only resolution can be retried. This applies only to MPA Runtime/Worker in Volcengine and BytePlus; manual addresses retain their existing behavior, including manually entered latest. diff --git a/prd-spec/README.md b/prd-spec/README.md deleted file mode 100644 index 9c092ae6d..000000000 --- a/prd-spec/README.md +++ /dev/null @@ -1,130 +0,0 @@ -# PRD and Design Specification Guidelines - -[中文版](README.zh.md) - -This directory holds change-specific requirements and designs for VeADK. It adopts the feature/refactor/bugfix organization used by `mpa-codex-worker`, while using VeADK's own component boundaries, tools, and bilingual documentation conventions. - -## 1. Document ownership - -| Location | Responsibility | -| --- | --- | -| [AGENTS.md](../AGENTS.md) | Repository-wide development workflow and required gates, written in English. | -| `prd-spec/` | Why a particular change is needed, its scope, proposed solution, implementation tasks, and acceptance criteria. | -| [specs/](../specs/README.md) | Maintained component contracts: responsibilities, interfaces, state, data, and failure semantics. | -| [frontend/SPEC.md](../frontend/SPEC.md) | Existing Studio frontend and supporting backend development rules. Do not duplicate or replace them here. | -| `docs/content/docs/`, module READMEs, and `examples/` | User-facing documentation and runnable usage examples. Preserve each area's existing language conventions. | - -A PRD describes a change; a component spec describes the maintained contract. Link the two, but do not create competing copies of the same contract. - -## 2. Directory structure and naming - -```text -prd-spec/ -├── README.md -├── README.zh.md -├── features/ -│ └── / -│ ├── YYYY-MM-DD-.md -│ └── YYYY-MM-DD-.zh.md -├── refactors/ -│ └── / -│ ├── YYYY-MM-DD-.md -│ └── YYYY-MM-DD-.zh.md -└── bugfixes/ - └── / - ├── YYYY-MM-DD-.md - └── YYYY-MM-DD-.zh.md -``` - -- Create category and topic directories when the first actual design needs them; empty scaffolding and placeholder designs are not required. -- Use stable English kebab-case topic names and descriptions. The date is the design's creation date. -- English uses `.md`; Chinese uses `.zh.md`. Both files share the same directory, date, and basename. -- For example, `features/runtime-session-lifecycle/2026-09-10-runtime-session-lifecycle-design.md` and its `.zh.md` counterpart illustrate naming only; they do not declare an implemented feature. -- Use the topic as the stable change ID. If a later independent change reuses the topic, assign it a distinct change ID and link the preceding design. -- A new substantive design iteration gets a new dated pair. For multiple iterations on the same day, add a distinguishing English suffix. Preserve historical designs and link their successors. -- Corrections, review fixes, translations, and implementation-status updates may update the current pair in place. Do not rewrite an old design to conceal a changed decision. - -## 3. Classification - -| Category | Use for | -| --- | --- | -| `features/` | New user-visible or public SDK capabilities, including extensions to existing behavior. | -| `refactors/` | Internal restructuring that preserves external behavior and contracts. | -| `bugfixes/` | Correcting an existing defect, regression, crash, data error, or interaction failure. | - -Choose the category by the intended outcome, not the size of the diff. A public behavior change must not be hidden under a refactor. Clarify mixed or ambiguous scope before implementation. - -## 4. Bilingual contract - -1. Every design is one complete English document plus one complete Chinese document. Do not split either language into separate PRD, spec, and plan files; keep its requirements, design, tasks, and acceptance criteria together. -2. Add a relative link to the other language near the top of each file. -3. Keep the change ID, revision date, lifecycle status, requirement IDs, task IDs, acceptance IDs, API identifiers, paths, commands, examples, and numeric limits aligned. Translate prose, not program identifiers. -4. Maintain both versions in the same change. The Chinese version is not an abbreviated summary, and the English version is not an optional translation backlog. -5. Review both versions for semantic equivalence. Missing counterparts, stale translations, or conflicting requirements block design approval and delivery; do not silently choose whichever version is convenient. -6. This bilingual rule overrides the Chinese-only default of a generic PRD skill for this repository. It does not change the documentation site's `.mdx` / `.en.mdx` convention. - -## 5. Required design content - -Each document must be understandable without chat history. Link supporting code and contracts, but restate the essential decisions and acceptance conditions locally. - -| Section | Required content | -| --- | --- | -| Metadata | Change ID, creation/revision dates, lifecycle status, counterpart link, related component specs, and predecessor/successor when applicable. | -| Background and evidence | Current behavior, affected users/callers, concrete source or reproduction evidence, and assumptions that remain unverified. | -| Goals and non-goals | What this change must accomplish and what is explicitly outside its scope. | -| Scenarios and requirements | Observable Given/When/Then scenarios and stable `FR-*` requirement IDs, including failure and boundary cases. | -| Design and contract impact | Responsibilities, alternatives and choice, interfaces, state/data, concurrency, permissions, security, compatibility, and affected consumers. Mark irrelevant areas as not applicable with a reason. | -| Implementation tasks | Ordered `T-*` tasks, affected files, dependencies, and links to requirements and tests. | -| Verification and acceptance | `AC-*` criteria, test layers, actual repository commands, required environment, negative cases, and evidence needed to declare completion. | -| Risks and open questions | Unresolved decisions, operational risks, rollout/recovery considerations, and explicitly agreed follow-up work. | -| Review and delivery record | Review findings and resolution, user approval, executed checks and results, skipped/blocked checks with reasons, and remaining scope. | - -Use lifecycle states `draft`, `in-review`, `approved`, `implemented`, and `superseded`. Approval requires resolved blockers and user agreement; `implemented` additionally requires completed in-scope tasks and acceptance evidence. Never equate an approved design with a shipped feature. - -### Feature emphasis - -Describe the user or SDK caller's workflow, new behavior, interface examples, dependencies, errors, and how the capability is reached through its actual entry point. Include integration and end-to-end verification when the change crosses a runtime or UI boundary. - -### Bugfix emphasis - -Record expected versus actual behavior, reproduction conditions, root-cause evidence, the affected path, and a regression test that fails before the fix. Distinguish a hypothesis from a confirmed cause. Explain why the proposed fix addresses the cause rather than masking the symptom. - -### Refactor emphasis - -Describe existing coupling or complexity, the new responsibility boundaries, unchanged external contracts, the implementation sequence, and tests that establish behavioral equivalence. Explicitly identify any required data or caller transition; if public behavior changes, reconsider the classification. - -## 6. VeADK impact checklist - -Only cover affected areas, and explain exclusions where a related contract might reasonably be impacted. - -- **Python SDK:** public imports and signatures, Agent/Runner behavior, configuration and defaults, optional dependencies, supported Python versions, examples, and downstream callers. -- **Studio and code generation:** frontend types, server routes, generated Python/configuration/dependencies, deployment payloads, built web assets, and the rules in `frontend/SPEC.md`. -- **Runtime and integrations:** session identity/persistence, stream termination, cancellation, retries, late events, MCP/tools, harness/sidecar behavior, and resource cleanup. -- **Cloud and security:** provider/region boundaries, authorization, credentials, user-controlled URLs/files, redaction, external side effects, and recovery after partial failures. -- **User documentation:** affected Chinese/English pages, example READMEs, and whether users need upgrade or migration instructions. - -Do not import `mpa-codex-worker`-specific commands, credentials, deployment authorization, coverage thresholds, or a blanket no-compatibility policy. VeADK is a published SDK as well as Studio; breaking changes require explicit impact analysis and approval. - -## 7. Design-to-delivery workflow - -1. Inspect the current implementation and relevant contracts, clarify scope, and obtain agreement on the approach before edits. -2. Write the bilingual PRD pair and assess component-spec impact. Create or update the affected bilingual component specs; explain a no-impact conclusion in the PRD. -3. Review requirements, contract consistency, implementation feasibility, tests, and bilingual equivalence. Resolve blockers and record approval before changing production code or tests. -4. Work through the approved tasks using spec-driven development and test-first implementation. A changed requirement returns to design review before its implementation proceeds. -5. Run targeted checks first, then the gates required by `AGENTS.md`, `frontend/SPEC.md`, and affected CI workflows. UI changes require real browser verification; mocked or static checks do not prove live integration behavior. -6. Review the implementation and tests, resolve blocking findings, and reconcile both document languages with the delivered behavior. Record actual commands, outcomes, and any missing verification. -7. Mark the design implemented only when all agreed tasks and acceptance criteria are satisfied. Deferred scope requires explicit user agreement and a traceable follow-up, not a silent status change. - -Pure documentation, translation, or formatting work that does not change runtime behavior or public contracts does not need a recursive PRD about the documentation itself. It still requires agreed scope, synchronized language pairs where applicable, and relevant checks. - -## 8. Acceptance traceability - -Use a compact table in each design, extending it as implementation produces evidence: - -| Requirement | Task | Acceptance criterion | Test or verification command | Result/evidence | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1` | `AC-1`: observable outcome | Exact command or manual procedure | `not_run` until executed | - -For checks, use `pass`, `fail`, `blocked`, `not_run`, or `not_applicable`; explain the latter three. Include execution date, tested revision/diff scope, and a concise result. Keep evidence in the design unless separate artifacts are necessary; link those artifacts rather than adding a mandatory report hierarchy. - -Do not place real API keys, AK/SK, tokens, cookies, signed URLs, personal data, or unredacted production logs in either language version or its evidence. diff --git a/prd-spec/README.zh.md b/prd-spec/README.zh.md deleted file mode 100644 index 0f3558838..000000000 --- a/prd-spec/README.zh.md +++ /dev/null @@ -1,130 +0,0 @@ -# PRD 与设计规格规范 - -[English](README.md) - -本目录存放 VeADK 针对具体变更的需求和方案设计。目录组织参考 `mpa-codex-worker` 的功能、重构、Bug 修复分类,同时采用 VeADK 自身的组件边界、工具链和双语文档约定。 - -## 1. 文档职责 - -| 位置 | 职责 | -| --- | --- | -| [AGENTS.md](../AGENTS.md) | 仓库级开发流程和强制门禁,使用英文编写。 | -| `prd-spec/` | 某次变更的原因、范围、方案、实施任务和验收标准。 | -| [specs/](../specs/README.zh.md) | 持续维护的组件契约:职责、接口、状态、数据和失败语义。 | -| [frontend/SPEC.md](../frontend/SPEC.md) | 现有 Studio 前端及配套后端开发规范,不在此重复或替换。 | -| `docs/content/docs/`、模块 README 和 `examples/` | 用户文档和可运行的使用示例,保持各区域已有的语言约定。 | - -PRD 描述一次变更,组件 spec 描述持续维护的契约。两者相互关联,但不建立相互竞争的契约副本。 - -## 2. 目录结构与命名 - -```text -prd-spec/ -├── README.md -├── README.zh.md -├── features/ -│ └── / -│ ├── YYYY-MM-DD-.md -│ └── YYYY-MM-DD-.zh.md -├── refactors/ -│ └── / -│ ├── YYYY-MM-DD-.md -│ └── YYYY-MM-DD-.zh.md -└── bugfixes/ - └── / - ├── YYYY-MM-DD-.md - └── YYYY-MM-DD-.zh.md -``` - -- 首份实际设计需要时再创建分类和主题目录,不要求空目录骨架或占位设计。 -- 主题名和描述采用稳定的英文 kebab-case;日期为设计创建日期。 -- 英文使用 `.md`,中文使用 `.zh.md`。两份文件的目录、日期和基础文件名一致。 -- 例如 `features/runtime-session-lifecycle/2026-09-10-runtime-session-lifecycle-design.md` 及其 `.zh.md` 对应文件仅演示命名,不代表已实现该功能。 -- 使用主题作为稳定的 change ID。后续独立变更复用同一主题时,应分配不同的 change ID 并关联前一版设计。 -- 实质性设计迭代新增带日期的双语文件;同一天多次迭代时增加可区分的英文后缀。保留历史设计并链接后继版本。 -- 勘误、评审整改、翻译和实施状态更新可直接修改当前双语文件。不得通过改写旧设计掩盖决策变化。 - -## 3. 分类规则 - -| 类别 | 适用范围 | -| --- | --- | -| `features/` | 新增用户可感知或公开 SDK 能力,包括对已有行为的扩展。 | -| `refactors/` | 保持外部行为和契约不变的内部结构调整。 | -| `bugfixes/` | 修复已有缺陷、回归、崩溃、数据错误或交互故障。 | - -根据预期结果而不是 diff 大小选择分类。不得以重构名义隐藏公开行为变化。混合或不明确的范围应在实现前澄清。 - -## 4. 双语契约 - -1. 每份设计包含一份完整英文文档和一份完整中文文档。每种语言内部不拆分为独立 PRD、spec、plan 文件;需求、设计、任务和验收标准保持在同一文件中。 -2. 每份文件顶部附近提供对应语言版本的相对链接。 -3. 两版的 change ID、修订日期、生命周期状态、需求 ID、任务 ID、验收 ID、API 标识符、路径、命令、示例和数值限制必须一致。翻译正文,不翻译程序标识符。 -4. 同一次变更同步维护两版。中文版不是缩略摘要,英文版也不是可延后的翻译任务。 -5. 评审必须检查语义等价。缺少对应版本、翻译过期或需求冲突会阻塞设计批准和交付;不得静默选用对当前实现更方便的版本。 -6. 本仓库双语规则覆盖通用 PRD skill 的“仅中文”默认要求,但不改变文档站 `.mdx` / `.en.mdx` 的约定。 - -## 5. 设计必需内容 - -每份文档必须能脱离聊天记录独立理解。可以引用代码和契约,但关键决策及验收条件必须在文档中明确说明。 - -| 章节 | 必需内容 | -| --- | --- | -| 元信息 | Change ID、创建及修订日期、生命周期状态、对应语言链接、相关组件 spec,以及适用时的前序和后继版本。 | -| 背景与证据 | 当前行为、受影响用户或调用方、具体源码或复现证据,以及尚未验证的假设。 | -| 目标与非目标 | 本次变更必须实现的结果,以及明确不在范围内的内容。 | -| 场景与需求 | 可观察的 Given/When/Then 场景及稳定的 `FR-*` 需求 ID,包括失败和边界情况。 | -| 设计与契约影响 | 职责、备选方案及选择、接口、状态和数据、并发、权限、安全、兼容性及受影响调用方。不适用的部分注明原因。 | -| 实施任务 | 有序的 `T-*` 任务、涉及文件、依赖关系,以及关联的需求和测试。 | -| 验证与验收 | `AC-*` 验收标准、测试层次、仓库实际命令、所需环境、反向用例及完成判定所需证据。 | -| 风险与待决事项 | 未解决决策、运行风险、发布和恢复注意事项,以及明确约定的后续工作。 | -| 评审与交付记录 | 评审问题和处理结果、用户批准、已执行检查及结果、跳过或阻塞检查及原因、剩余范围。 | - -生命周期状态使用 `draft`、`in-review`、`approved`、`implemented` 和 `superseded`。批准要求阻塞项清零并获得用户同意;`implemented` 还要求范围内任务完成且具备验收证据。不得将设计获批等同于功能已交付。 - -### 功能文档重点 - -说明用户或 SDK 调用方的流程、新行为、接口示例、依赖、错误,以及如何通过实际入口使用该能力。跨越运行时或 UI 边界时,包含集成和端到端验证。 - -### Bug 修复文档重点 - -记录预期与实际行为、复现条件、根因证据、受影响路径,以及修复前会失败的回归测试。区分假设与已确认原因。说明方案为何解决根因,而不是掩盖现象。 - -### 重构文档重点 - -说明现有耦合或复杂度、新职责边界、保持不变的外部契约、实施顺序及证明行为等价的测试。明确必要的数据或调用方切换;公开行为发生变化时,应重新考虑分类。 - -## 6. VeADK 影响检查表 - -只展开受影响的领域;当某个相关契约可能受到影响但实际被排除时,应说明理由。 - -- **Python SDK:** 公开导入和签名、Agent/Runner 行为、配置及默认值、可选依赖、支持的 Python 版本、示例和下游调用方。 -- **Studio 与代码生成:** 前端类型、服务端路由、生成的 Python/配置/依赖、部署 Payload、构建后的 Web 资源,以及 `frontend/SPEC.md` 的规范。 -- **运行时与集成:** Session 身份和持久化、流终止、取消、重试、迟到事件、MCP/工具、Harness/Sidecar 行为及资源清理。 -- **云与安全:** Provider/区域边界、授权、凭据、用户控制的 URL/文件、脱敏、外部副作用及部分失败后的恢复。 -- **用户文档:** 受影响的中英文页面、示例 README,以及是否需要升级或迁移说明。 - -不得照搬 `mpa-codex-worker` 专用命令、凭据、部署授权、覆盖率阈值,或一刀切的“不保留兼容”策略。VeADK 既是已发布 SDK,也包含 Studio;破坏性变更必须明确评估影响并获得批准。 - -## 7. 从设计到交付的流程 - -1. 检查当前实现及相关契约,澄清范围,在修改前就方案取得一致。 -2. 编写双语 PRD 并评估组件 spec 影响。创建或更新受影响组件的双语 spec;无影响时在 PRD 中说明理由。 -3. 评审需求、契约一致性、实现可行性、测试及双语等价性。在修改生产代码或测试前清零阻塞项并记录批准。 -4. 按已批准任务进行规格驱动开发和测试优先实现。需求变化时,先返回设计评审,再继续实现该变化。 -5. 先运行针对性检查,再执行 `AGENTS.md`、`frontend/SPEC.md` 和受影响 CI 工作流要求的门禁。UI 变更必须进行真实浏览器验证;Mock 或静态检查不能证明真实集成行为。 -6. 审查实现和测试,解决阻塞问题,并将两种语言的文档与交付行为对齐。记录实际命令、结果和缺失的验证。 -7. 只有约定任务和验收标准全部满足后才能标记为已实现。延后范围必须获得用户明确同意并建立可追踪的后续任务,不得静默变更状态。 - -不改变运行时行为或公开契约的纯文档、翻译或格式调整,无需递归编写“关于文档的 PRD”。仍须确认范围、同步适用的双语文件,并执行相关检查。 - -## 8. 验收追踪 - -每份设计使用紧凑表格,并随实现补充证据: - -| 需求 | 任务 | 验收标准 | 测试或验证命令 | 结果/证据 | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1` | `AC-1`:可观察结果 | 精确命令或人工操作步骤 | 执行前为 `not_run` | - -检查状态使用 `pass`、`fail`、`blocked`、`not_run` 或 `not_applicable`,后三种必须说明原因。记录执行日期、被测版本/diff 范围和简要结果。默认将证据保留在设计内;确需独立产物时链接到产物,不强制新增多层报告目录。 - -两种语言的文档和证据均不得包含真实 API Key、AK/SK、Token、Cookie、签名 URL、个人数据或未脱敏生产日志。 diff --git a/prd-spec/bugfixes/2026-09-12-a2a-first-event-heartbeat.md b/prd-spec/bugfixes/2026-09-12-a2a-first-event-heartbeat.md deleted file mode 100644 index 414916129..000000000 --- a/prd-spec/bugfixes/2026-09-12-a2a-first-event-heartbeat.md +++ /dev/null @@ -1,51 +0,0 @@ -# A2A First Event Heartbeat and Reasoning Projection - -- **Status:** Implemented and verified -- **Date:** 2026-09-12 -- **Change type:** Bugfix -- **Chinese version:** [2026-09-12-a2a-first-event-heartbeat.zh.md](2026-09-12-a2a-first-event-heartbeat.zh.md) -- **Affected component:** [Studio Tool Activity](../../../specs/studio-tool-activity/README.md) - -## 1. Evidence and Problem - -Studio aborts `/run_sse` when no SSE frame is received within 30 seconds. In the verified Runtime request, the A2A task started at 22:15:41 and the model produced its first displayable content at 22:16:02, but agent-card negotiation made the total request age exceed 30 seconds. The Runtime returned HTTP 200 and logged the active invocation, proving that the public APIG route was reachable. - -The bridge currently drops A2A `submitted` and message-less `working` status updates. A healthy long-running invocation can therefore produce no downstream Studio frame before the browser deadline and be cancelled as a false network timeout. - -## 2. Scope and Design - -- Convert non-final A2A `submitted` and message-less `working` status updates into a Studio metadata-only heartbeat event. -- Preserve the existing projection for `working` messages containing visible text. -- Preserve `metadata.adk_thought=true` message and artifact parts as Studio `thought=true` events instead of dropping them. Keep cumulative-delta tracking independent for reasoning and answer text. -- If the upstream stream ends after answer deltas but without an explicit final text artifact, finalize the exact accumulated answer as one authoritative Studio event. Never promote reasoning into an answer. -- Do not render heartbeat text, create a transcript block, complete the turn, or resubmit the request. -- Do not alter Runtime provisioning, mpa-agent, task semantics, history persistence, or the existing 30-second client deadline. - -`a2a_event_to_studio_events()` emits one partial ADK-compatible event containing an empty `parts` list and `customMetadata.a2aStatus`. `runSSE()` treats the parsed frame as first-event evidence, while `createAssistantEventProjector()` ignores events that do not affect the assistant turn. Terminal status updates remain excluded. - -## 3. Tests and Acceptance - -- Unit-test `submitted` and message-less `working` heartbeat projection. -- Unit-test that the heartbeat is ignored by transcript projection. -- Unit-test live and artifact reasoning projection plus independent cumulative-delta suppression. -- Unit-test synthesized completion from received answer deltas, including no duplicate after an explicit final event and no promotion of reasoning-only streams. -- Preserve text-bearing working and terminal status behavior. -- Run targeted A2A/Studio tests, frontend tests, build, asset verification, and pre-commit. -- Live Runtime acceptance: the first downstream SSE frame arrives before 30 seconds and the original request is submitted once. - -## 4. Risks - -- A heartbeat must not create an empty assistant turn. Mitigation: empty parts and an explicit projector test. -- A terminal state must not masquerade as liveness. Mitigation: emit only non-final `submitted`/`working`. -- A retry could duplicate work. Mitigation: no retry or fallback behavior changes. - -## 5. Verification Record - -- `uv run --extra dev pytest tests/cli/test_runtime_a2a_stream.py tests/cli/test_frontend_runtime_proxy.py -q`: 93 passed after adding reasoning, terminal-finalization, and repeated-artifact-ID coverage. -- Live Runtime `r-yeuujrrcowb21078p9jh`: first metadata-only heartbeat arrived in 0.737 seconds; the final answer arrived in 5.999 seconds; one request produced nine downstream frames. -- Repeated message-less `working` updates are suppressed per `(taskId, state)` by the request-local decoder. -- Live reasoning verification: first frame arrived in 0.702 seconds and the bridge emitted 216 `thought=true` deltas, 17 answer-text deltas, and 5 tool events for one request. -- `npm --prefix frontend test`: 1,079 tests passed, including the heartbeat no-transcript regression. -- Live completion verification after the finalization fix: first frame arrived in 1.001 seconds; the bridge emitted 122 reasoning deltas, 20 answer deltas, 4 tool events, and a non-partial final answer containing `reasoning-check`, so Studio no longer classifies the HTTP 200 stream as empty. -- Live verification after unique projected event IDs: first frame arrived in 0.587 seconds and the final non-partial answer was preserved after 130 reasoning deltas and 23 answer deltas. -- Full-path replay verification: a fresh 176-event real Runtime stream was passed through Studio's actual `createAssistantEventProjector()`; it produced `completed=true`, visible final text, and no incomplete-response condition. diff --git a/prd-spec/bugfixes/2026-09-12-a2a-first-event-heartbeat.zh.md b/prd-spec/bugfixes/2026-09-12-a2a-first-event-heartbeat.zh.md deleted file mode 100644 index 0273e32ef..000000000 --- a/prd-spec/bugfixes/2026-09-12-a2a-first-event-heartbeat.zh.md +++ /dev/null @@ -1,51 +0,0 @@ -# A2A 首事件心跳与 Reasoning 投影修复 - -- **状态:** 已实施并完成验证 -- **日期:** 2026-09-12 -- **变更类型:** Bugfix -- **英文版本:** [2026-09-12-a2a-first-event-heartbeat.md](2026-09-12-a2a-first-event-heartbeat.md) -- **影响组件:** [Studio 工具活动](../../../specs/studio-tool-activity/README.zh.md) - -## 1. 证据与问题 - -Studio 在 30 秒内未收到 SSE 帧时会中止 `/run_sse`。本次真实 Runtime 请求中,A2A task 于 22:15:41 开始,模型于 22:16:02 产生首个可展示内容;由于此前还有 agent-card 协商,总请求耗时超过 30 秒。Runtime 返回 HTTP 200 且记录了执行中的 invocation,证明公网 APIG 链路可达。 - -当前桥接会丢弃 A2A 的 `submitted` 状态以及不含消息的 `working` 状态。因此,健康但耗时较长的调用可能在浏览器截止时间前没有任何下游 Studio 帧,并被误判成网络超时而取消。 - -## 2. 范围与设计 - -- 将非终态且不含 Agent 消息的 A2A `submitted`、`working` 状态转换为 Studio metadata-only heartbeat 事件。 -- 保留带可见文本的 `working` 消息现有投影行为。 -- 将 `metadata.adk_thought=true` 的 message 和 artifact part 保留为 Studio `thought=true` 事件,不再丢弃;reasoning 与答案文本分别维护累计 delta 状态。 -- 若上游在发送答案增量后结束但未提供显式最终文本 artifact,则将已累计的原始答案收敛为一个权威 Studio 终态事件;禁止把 reasoning 提升为答案。 -- heartbeat 不显示文本、不创建 transcript block、不结束 turn,也不重新提交请求。 -- 不修改 Runtime 创建、mpa-agent、task 语义、历史持久化或现有 30 秒客户端截止时间。 - -`a2a_event_to_studio_events()` 输出一个包含空 `parts` 和 `customMetadata.a2aStatus` 的 partial ADK 兼容事件。`runSSE()` 将解析成功的帧视为首事件,而 `createAssistantEventProjector()` 会忽略不影响 assistant turn 的事件。终态 status update 继续不输出。 - -## 3. 测试与验收 - -- 单测覆盖 `submitted` 和无消息 `working` heartbeat 投影。 -- 单测覆盖 heartbeat 被 transcript 投影忽略。 -- 单测覆盖实时和 artifact reasoning 投影,以及 reasoning/答案独立累计去重。 -- 单测覆盖从已接收答案增量生成终态、显式终态后不重复,以及纯 reasoning 流不得转成答案。 -- 保持带文本的 working 和终态 status 行为不变。 -- 执行定向 A2A/Studio 测试、前端测试、构建、资产校验和 pre-commit。 -- 真实 Runtime 验收:首个下游 SSE 帧在 30 秒内到达,原始请求只提交一次。 - -## 4. 风险 - -- heartbeat 不得创建空 assistant turn。缓解:使用空 parts,并增加显式 projector 测试。 -- 终态不得伪装成存活信号。缓解:仅输出非 final 的 `submitted`/`working`。 -- retry 可能造成重复执行。缓解:不修改 retry 或 fallback 行为。 - -## 5. 验证记录 - -- `uv run --extra dev pytest tests/cli/test_runtime_a2a_stream.py tests/cli/test_frontend_runtime_proxy.py -q`:增加 reasoning、终态收敛和重复 artifact ID 覆盖后 93 项通过。 -- 真实 Runtime `r-yeuujrrcowb21078p9jh`:仅含 metadata 的首个 heartbeat 在 0.737 秒到达,最终回答在 5.999 秒到达,一次请求共产生 9 个下游帧。 -- 重复的无消息 `working` 更新由请求内 decoder 按 `(taskId, state)` 去重。 -- 真实 reasoning 验证:首帧在 0.702 秒到达;单次请求经桥接输出 216 个 `thought=true` 增量、17 个答案文本增量和 5 个工具事件。 -- `npm --prefix frontend test`:1,079 项测试通过,包含 heartbeat 不创建 transcript 的回归用例。 -- 最终化修复后的真实验证:首帧在 1.001 秒到达;桥接输出 122 个 reasoning 增量、20 个答案增量、4 个工具事件,以及包含 `reasoning-check` 的非 partial 最终答案,因此 Studio 不再把 HTTP 200 流判定为空。 -- projected event ID 唯一化后的真实验证:首帧在 0.587 秒到达,经过 130 个 reasoning 增量和 23 个答案增量后,非 partial 最终答案仍被完整保留。 -- 全链路重放验证:将一次新的 176-event 真实 Runtime 流逐条送入 Studio 实际使用的 `createAssistantEventProjector()`,结果为 `completed=true`、最终文本可见且不会触发空回复条件。 diff --git a/prd-spec/bugfixes/2026-09-23-aidap-workspace-readiness.md b/prd-spec/bugfixes/2026-09-23-aidap-workspace-readiness.md deleted file mode 100644 index fccec655d..000000000 --- a/prd-spec/bugfixes/2026-09-23-aidap-workspace-readiness.md +++ /dev/null @@ -1,33 +0,0 @@ -# Wait for newly created AIDAP Workspace metadata - -[中文版](2026-09-23-aidap-workspace-readiness.zh.md) - -Date: 2026-09-23. Status: implemented in the working tree. Owner: [Studio MPA creation](../../specs/studio-mpa-creation/README.md). This corrects the automatic PostgreSQL flow in [the feature design](../features/mpa-space-scoped-resources/2026-09-23-auto-pg-workspaces.md). - -## Background and evidence - -A new MPA creation failed immediately after each Workspace was allocated: the first attempt stopped at `admin_workspace`, and the next attempt reached `business_workspace` before failing. Both recorded Workspace IDs now resolve in the verified account as running PostgreSQL 17 resources with valid ownership tags and connection parameters. Read-only SQL connections to both succeed and the returned account has `CREATEDB`. The exact original exception was redacted by the task runner, so eventual metadata visibility is an inference, not a proven historical response. The implementation confirms a vulnerable path: it validates detail fields and tags immediately after `CreateWorkspace`, outside the `PGNotReady` polling loop. - -## Goal, scope and scenarios - -- A newly created or recorded VeADK-owned Workspace may temporarily lack detail fields/tags or return provider `ResourceNotFound`. Wait for it to become visible within the existing creation timeout, then continue using the recorded ID. -- A present but wrong account, region, project, name, engine or ownership tag remains a hard failure. Explicit adoption keeps strict validation. No second `CreateWorkspace` call, Workspace deletion, migration, or database write is part of this fix. -- A user retry with the same agent ID must reuse both recorded Workspaces. Other MPA creation steps and generic Studio agents are outside scope. - -## Design and affected files - -In `veadk/integrations/mpa/managed/pg_bootstrap.py`, treat missing identity fields or expected tags as `PGNotReady` only for Workspaces marked as created by this bootstrap state. Move detail validation into the existing readiness loop; treat provider not-found for such recorded owned resources as transient. Keep nonempty mismatches fatal. Add offline delayed-metadata and wrong-owner tests in `tests/integrations/mpa_managed/test_auto_pg.py`. Update this contract and both language documents; no frontend API or UI change is required. - -## Tasks, tests and acceptance - -1. Reproduce immediate tag/detail failure with a fake AIDAP provider before the fix. -2. Poll only transient absence and retain strict mismatch checks; keep the original Workspace IDs. -3. Run managed creation tests, affected CLI tests, Python static/type checks when available, and `git diff --check`. Restart local Studio only after confirming no active creation tasks. - -Acceptance: delayed provider metadata succeeds without another create; wrong ownership fails; both real recorded Workspaces remain intact; the local Studio process loads the fix. No live creation is needed to establish the offline behavior. - -## Risks, review and verification - -Review: the fix changes only the timing of validation for owned Workspaces. Permanent absence waits until the existing bootstrap timeout and does not trigger duplicate allocation. A genuine conflicting value still fails immediately. A later SQL, IAM or Runtime issue is not covered by this fix. - -Verification record (2026-09-23, branch `feat/from-main-20260922`, uncommitted diff): delayed-metadata test failed before implementation and passes after it; wrong ownership still fails. Read-only AIDAP/SQL checks pass for both recorded resources. `UV_CACHE_DIR=/tmp/veadk-uv-cache uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py -q`: 346 passed. CLI regression: 41 passed with local mock-server binding allowed. Python compilation and `git diff --check` pass. Ruff/Pyright are unavailable locally and Ruff is absent from the offline cache, so those checks are blocked. The local Studio process was restarted after confirming zero active creation tasks; its config endpoint reports automatic mode enabled and ready. No live retry or new cloud allocation was performed for verification. diff --git a/prd-spec/bugfixes/2026-09-23-aidap-workspace-readiness.zh.md b/prd-spec/bugfixes/2026-09-23-aidap-workspace-readiness.zh.md deleted file mode 100644 index aaf885080..000000000 --- a/prd-spec/bugfixes/2026-09-23-aidap-workspace-readiness.zh.md +++ /dev/null @@ -1,33 +0,0 @@ -# 等待新建 AIDAP Workspace 元数据就绪 - -[English](2026-09-23-aidap-workspace-readiness.md) - -日期:2026-09-23。状态:已在工作区实施。归属:[Studio MPA 创建](../../specs/studio-mpa-creation/README.zh.md)。修正[自动 PostgreSQL 功能设计](../features/mpa-space-scoped-resources/2026-09-23-auto-pg-workspaces.zh.md)。 - -## 背景和证据 - -新建 MPA 在每个 Workspace 分配后立即失败:第一次停在 `admin_workspace`,下一次推进到 `business_workspace` 后失败。现在两个已记录的 Workspace ID 均能在已核验的账号中查到,状态为运行中、引擎为 PostgreSQL 17,归属标签和连接参数正确。两个库的只读 SQL 连接均成功,返回的账号具备 `CREATEDB` 权限。任务运行器脱敏了当时的原始异常,因此“元数据稍后才可见”是推断,不是已证实的历史响应。代码中存在明确的易错路径:`CreateWorkspace` 后立即校验详情字段和标签,且校验不在 `PGNotReady` 等待循环内。 - -## 目标、范围和场景 - -- 新建或已记录且由 VeADK 拥有的 Workspace 若暂时缺少详情字段/标签,或服务商返回 `ResourceNotFound`,应在现有创建超时范围内等待可见,然后继续使用已记录 ID。 -- 已出现但错误的账号、地域、项目、名称、引擎或归属标签仍立即失败。显式接管保持严格校验。本修正不再次调用 `CreateWorkspace`,不删除 Workspace,不迁移或写入数据库。 -- 用户以相同智能体 ID 重试时须复用已记录的两个 Workspace。其他 MPA 创建阶段及通用 Studio 智能体不在范围内。 - -## 设计和涉及文件 - -在 `veadk/integrations/mpa/managed/pg_bootstrap.py` 中,仅对引导状态标记为自行创建的 Workspace,把缺少身份字段或预期标签判为 `PGNotReady`。将详情校验放入已有就绪等待循环;此类已记录自有资源的服务商 not-found 视为暂时不可见。非空但错误的值仍立即失败。在 `tests/integrations/mpa_managed/test_auto_pg.py` 中增加离线延迟元数据和错误归属测试。更新本契约和双语文档;不改变前端 API 或界面。 - -## 任务、测试和验收 - -1. 修正前用模拟 AIDAP 服务复现立即校验标签/详情失败。 -2. 仅等待暂时缺失,保留严格冲突校验和原 Workspace ID。 -3. 运行托管创建测试、受影响 CLI 测试、可用时的 Python 静态/类型检查及 `git diff --check`。确认没有进行中创建任务后才重启本机 Studio。 - -验收:延迟出现的服务商元数据可成功且不重复创建;错误归属仍失败;两个真实已记录 Workspace 保持原状;本机 Studio 加载修正。验证离线行为不需要真实新建资源。 - -## 风险、审查和验证 - -审查:修正只改变自有 Workspace 的校验时机。永久缺失会等到现有引导超时,但不会重复分配。真实冲突值仍立即失败。后续 SQL、IAM 或 Runtime 错误不在本修正范围。 - -验证记录(2026-09-23,分支 `feat/from-main-20260922`,未提交改动):延迟元数据测试在实施前失败、实施后通过;错误归属仍失败。两个已记录资源的只读 AIDAP/SQL 检查通过。`UV_CACHE_DIR=/tmp/veadk-uv-cache uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py -q`:346 通过。允许本地模拟服务绑定后,CLI 回归 41 通过。Python 编译及 `git diff --check` 通过。本机无 Ruff/Pyright 可执行文件且离线缓存无 Ruff,相关检查受阻。确认创建中任务为零后已重启本机 Studio;配置接口返回自动模式且可创建。验证期间未执行线上重试或分配新云资源。 diff --git a/prd-spec/bugfixes/2026-09-23-mpa-a2a-discovery-default.md b/prd-spec/bugfixes/2026-09-23-mpa-a2a-discovery-default.md deleted file mode 100644 index 060ac73fe..000000000 --- a/prd-spec/bugfixes/2026-09-23-mpa-a2a-discovery-default.md +++ /dev/null @@ -1,48 +0,0 @@ -# MPA A2A discovery defaults - -[中文版](2026-09-23-mpa-a2a-discovery-default.zh.md) - -- Change: mpa-a2a-discovery-default; date: 2026-09-23; status: implemented. -- Contract: [MPA provisioning](../../specs/mpa-runtime-provisioning/README.md). - -## Evidence and scope - -Live read-only comparison confirmed that the old Runtime leaves DISABLE_JWT_AUTH unset, returns 404 from list-apps and 200 from the A2A agent card, and Studio uses a2a-default. The new Runtime receives DISABLE_JWT_AUTH=true from build_runtime_env, advertises default, and fails REST profile-status with 500 because no JWT public key is configured. Both support A2A discovery. Images differ, but this fix does not change the image. - -## Requirements, design and acceptance - -- FR-1 / T-1 / AC-1: Default creation to DISABLE_JWT_AUTH=false and ENABLE_A2A=true, retaining A2A_TIP_VERIFY_ENABLED=false and outer Runtime key-auth. First reproduce with tests, then verify CLI environment and managed flat creation payloads. -- FR-2 / T-2 / AC-2: Preserve explicit extra_env and managed.runtime.env precedence; referenced Runtime/template environments retain existing behavior. General-agent behavior is unchanged; the Studio MPA proxy path compatibility follows the additional design below. Non-goals: enabling MPA_AGENTKIT_MODE, JWT provisioning, identity architecture migration, database changes. -- FR-3 / T-3 / AC-3: Update only the affected new Runtime's discovery configuration, release a new version on that Runtime, wait for Ready and application readiness, then verify list-apps 404, agent card 200, Studio a2a-default and an isolated test conversation. Never print credentials. Report failures without reverting unrelated resources. - -## Files, risks and recovery - -Affected: mpa_provision.py, test_mpa_provision_env.py, mpa_managed/test_service.py, bilingual provisioning specs and module READMEs. The user explicitly authorized restoring old A2A behavior and fixing the template. Code does not automatically migrate existing Runtimes; only the identified new Runtime is repaired here. Release can briefly interrupt that Runtime; preserve all other environment and image settings. Pending creation payload hashes remain strictly checked, with no configuration-drift bypass; old tasks reporting drift need their original configuration or a new creation. Operators remain responsible for explicit JWT bypass overrides. - -## Design review - -review-spec is unavailable; requirements, interfaces, security, compatibility, errors, testability and bilingual equivalence were reviewed directly. No blockers; the current user instruction approves this approach. Session state, streaming and cancellation logic remain unchanged; this selects the existing A2A protocol. Outer key-auth and REST JWT validation remain intact. - -## Verification record - -2026-09-23, current uncommitted diff. Detailed results are recorded below. Frontend build: not_applicable (no frontend changes). Full repository tests and pre-commit: not_run (no commit requested; run affected regressions first). - -## Additional design review: shared public path - -The live V4 send exposed a second cause: the shared public endpoint has a /runtime/ prefix, but its agent card drops that prefix from /a2a/jsonrpc. FR-4 / T-4 / AC-4: only for a verified MPA Runtime, the Studio backend restores a /runtime/[a-z0-9-]+ prefix from the control-plane endpoint when the card is same-origin and its path is exactly /a2a/jsonrpc. Already-prefixed, cross-origin, custom RPC paths and URLs with query, fragment or userinfo remain unchanged; general agents are unchanged. Apply consistently to sending, stream fallback and history restoration. Add frontend/server/mpa_a2a.py, wire cli_frontend.py and add unit tests plus proxy integration coverage. Preserve failure/cancellation behavior without extra retries. Direct design re-review passed; this is necessary compatibility work within the user's approved A2A chat repair. Restart local Studio to load backend code; no frontend build required. - -## Implementation and final verification - -2026-09-23, current uncommitted branch diff: FR-1 through FR-4, T-1 through T-4 and AC-1 through AC-4 completed. - -- `pass`: test-first reproduction produced 5 default-related failures; before path repair the expanded integration matrix produced 6 expected failures and 12 passes. -- `pass`: `UV_CACHE_DIR=/private/tmp/veadk-uv-cache uv run --extra dev pytest -q tests/integrations/test_mpa_provision_env.py tests/integrations/mpa_managed tests/integrations/test_mpa_runtime.py`, 366 passed. After correcting the test-helper typing, reran the environment and service targets: 37 passed. -- `pass`: `uv run --extra dev pytest -q tests/cli/test_cli_mpa.py tests/cli/test_frontend_runtime_proxy.py -k 'a2a or mpa'`, 32 passed; after path repair, `uv run --extra dev pytest -q tests/cli/test_mpa_a2a_url.py tests/cli/test_frontend_runtime_proxy.py --tb=short`, 106 passed, covering unchanged general-agent URLs, streaming/non-streaming/fallback and history restoration. -- `pass`: Ruff check/format for the 7 affected Python files, gitleaks and diff whitespace. -- `pass`: Pyright reports no errors for the template, environment/service tests and new URL helper/test. `fail`: whole-file Pyright for cli_frontend.py and the existing proxy tests reports 56 pre-existing errors; comparing diagnostic messages and filenames against HEAD copies yields identical results and no new diagnostics. Unrelated typing errors were not changed. -- `pass`: updated only 2 discovery variables on the new live Runtime, released V4, verified platform Ready and application readiness; the old Runtime remains V7 with unchanged configuration. New list-apps returns 404 and agent card returns 200. -- `pass`: after restarting local Studio, the real browser selected the new Runtime as a2a-default; an isolated test conversation returned final answer 2 to simple arithmetic, with reasoning and footer and no JWT/404 error. The test conversation is retained; no additional cloud resources were created. REST profile-status still requires JWT and is not an A2A acceptance condition. -- `not_run`: full repository regression and all-files pre-commit (large scope and unrelated uncommitted work; no commit requested); frontend build/layout scenarios `not_applicable` (no TS/CSS/generated asset changes). Live sandbox skills, cancellation and cross-process history restoration were not exercised; their protocols are unchanged. -- Restricted network prevented online uvx pre-commit resolution; the identical gitleaks hook passed from the existing offline cache. No global configuration change or gate bypass. No commit or push. - -Final review added an empty-userinfo URL boundary regression: failed before the guard correction, then all 15 URL unit tests passed. diff --git a/prd-spec/bugfixes/2026-09-23-mpa-a2a-discovery-default.zh.md b/prd-spec/bugfixes/2026-09-23-mpa-a2a-discovery-default.zh.md deleted file mode 100644 index 7ba05fc22..000000000 --- a/prd-spec/bugfixes/2026-09-23-mpa-a2a-discovery-default.zh.md +++ /dev/null @@ -1,48 +0,0 @@ -# MPA A2A 发现默认配置修复 - -[English](2026-09-23-mpa-a2a-discovery-default.md) - -- 变更:mpa-a2a-discovery-default;日期:2026-09-23;状态:implemented。 -- 契约:[MPA 部署](../../specs/mpa-runtime-provisioning/README.zh.md)。 - -## 证据与范围 - -只读线上对比确认:旧 Runtime 未设置 DISABLE_JWT_AUTH,list-apps 返回 404,A2A agent card 返回 200,Studio 使用 a2a-default。新 Runtime 由 build_runtime_env 注入 DISABLE_JWT_AUTH=true,list-apps 返回 default,进入 REST profile-status 后因无 JWT 公钥返回 500。两者 A2A 发现均正常。镜像不同但本次不切换镜像。 - -## 需求、设计与验收 - -- FR-1 / T-1 / AC-1:创建默认值改为 DISABLE_JWT_AUTH=false、ENABLE_A2A=true,保留 A2A_TIP_VERIFY_ENABLED=false 和外层 Runtime key-auth。测试先复现,再验证 CLI 环境与 managed flat 创建 payload。 -- FR-2 / T-2 / AC-2:保留显式 extra_env 和 managed.runtime.env 覆盖优先级;引用 Runtime / 模板的环境沿用原有逻辑。通用智能体逻辑不改动;Studio MPA 代理的路径兼容按下方追加设计实施。非目标:启用 MPA_AGENTKIT_MODE、补 JWT、公钥/身份架构迁移、修改数据库。 -- FR-3 / T-3 / AC-3:对本次失败的新 Runtime 单独更新上述发现配置;同一个 Runtime 发布新版本,等待 Ready 和应用 readiness,再验证 list-apps 404、agent card 200、Studio a2a-default 与隔离测试会话聊天。不得输出密钥。失败如实报告,不自动回退其他资源。 - -## 文件、风险与恢复 - -涉及 mpa_provision.py、test_mpa_provision_env.py、mpa_managed/test_service.py、双语部署规范和模块 README。用户已明确授权沿用旧 A2A 链路并修正模板。现有 Runtime 不会被代码自动迁移;本次仅修复已定位的新 Runtime。发布可能短暂中断该 Runtime;保留其他环境和镜像。未完成创建任务的 payload hash 仍保持严格校验,不绕过配置漂移保护;旧任务若报漂移需用原配置完成或重新创建。显式禁用 JWT 的自定义配置仍由操作者负责。 - -## 设计审查 - -review-spec 不可用,已直接审查需求、接口、安全、兼容性、错误处理、测试性和双语一致性。没有阻塞项;用户本轮指令批准上述方案。不改变会话状态、流式事件或取消逻辑,仅选择现有 A2A 协议。外层 key-auth 不变,REST JWT 校验保持有效。 - -## 验证记录 - -2026-09-23,当前未提交差异。详细结果见下方实施记录。前端构建:not_applicable(无前端变更)。全仓测试及 pre-commit:not_run(本轮不提交,先执行受影响回归)。 - -## 追加设计审查:共享公网路径 - -线上 V4 会话发送暴露第二个原因:共享公网 endpoint 带 /runtime/ 前缀,但 agent card 的 /a2a/jsonrpc 丢失该前缀。FR-4 / T-4 / AC-4:Studio 后端仅对已验证类型为 MPA 的 Runtime,在 agent card 与控制面 endpoint 同源且卡片路径恰为 /a2a/jsonrpc 时,补齐符合 /runtime/[a-z0-9-]+ 的前缀。已带前缀、跨源、非默认 RPC 路径、含查询/片段/用户信息的 URL 不做改写;通用智能体不改。适用于发送、流式回退和历史读取。新增 frontend/server/mpa_a2a.py、修改 cli_frontend.py 并增加单测和现有代理集成矩阵。保留失败及取消行为,无额外重试。直接复审已通过,属于用户批准恢复 A2A 聊天的必要兼容修复。需要重启本地 Studio 加载后端代码;无需前端构建。 - -## 实施与最终验证记录 - -2026-09-23,当前分支未提交差异:FR-1 至 FR-4、T-1 至 T-4、AC-1 至 AC-4 已完成。 - -- `pass`:测试先行,原默认值导致 5 个回归失败;路径修复前新集成矩阵 6 例失败、12 例通过,均为预期复现。 -- `pass`:`UV_CACHE_DIR=/private/tmp/veadk-uv-cache uv run --extra dev pytest -q tests/integrations/test_mpa_provision_env.py tests/integrations/mpa_managed tests/integrations/test_mpa_runtime.py`,366 例通过。测试辅助函数类型修正后,环境及 service 两个目标文件重跑 37 例通过。 -- `pass`:`uv run --extra dev pytest -q tests/cli/test_cli_mpa.py tests/cli/test_frontend_runtime_proxy.py -k 'a2a or mpa'`,32 例通过;追加路径修复后 `uv run --extra dev pytest -q tests/cli/test_mpa_a2a_url.py tests/cli/test_frontend_runtime_proxy.py --tb=short`,106 例通过,覆盖通用智能体地址不变、流式/非流式/回退和历史恢复。 -- `pass`:Ruff check/format 对本次 7 个 Python 文件通过;gitleaks 扫描通过;diff whitespace 通过。 -- `pass`:模板、环境测试、service 测试及新增 URL helper/test 的 Pyright 无错误。`fail`:cli_frontend.py 和旧代理测试文件全文件 Pyright 有 56 个存量错误;与 HEAD 副本逐条比较诊断文本及文件后完全相同,没有新增诊断。未为本修复修改无关类型错误。 -- `pass`:线上新 Runtime 仅更新 2 个发现变量,发布 V4,平台 Ready、应用 readiness 成功;旧 Runtime 仍 V7 且配置不变。新 Runtime 的 list-apps 返回 404、agent card 返回 200。 -- `pass`:重启本地 Studio 后,真实浏览器选中新 Runtime 显示 a2a-default;独立测试会话发送简单算术,收到最终答案 2,推理和底部操作栏正常,无 JWT/404 错误。保留该测试会话;没有创建额外云资源。REST profile-status 仍要求 JWT,不作为 A2A 的验收条件。 -- `not_run`:全仓回归、全文件 pre-commit(范围大且包含无关未提交修改,本轮未要求提交);前端构建/布局场景 `not_applicable`(无 TS/CSS/生成资源变更)。未真实运行沙箱技能、取消及跨进程历史恢复;本次不改变这些协议。 -- 工具网络受限导致在线 uvx 拉取 pre-commit 失败,使用既有离线缓存运行相同 gitleaks hook 并通过,未修改全局配置或绕过检查。没有提交或推送。 - -最终审查补充空用户信息 URL 边界回归:修正保护条件前失败,修正后 15 个 URL 单测全部通过。 diff --git a/prd-spec/bugfixes/2026-09-23-mpa-registry-tls-url.md b/prd-spec/bugfixes/2026-09-23-mpa-registry-tls-url.md deleted file mode 100644 index 4b81acf8d..000000000 --- a/prd-spec/bugfixes/2026-09-23-mpa-registry-tls-url.md +++ /dev/null @@ -1,35 +0,0 @@ -# Normalize the managed MPA registry TLS URL - -[中文版](2026-09-23-mpa-registry-tls-url.zh.md) - -Date: 2026-09-23. Status: implemented in the working tree. Component: [Studio MPA creation](../../specs/studio-mpa-creation/README.md). - -## Evidence, scope and requirements - -A newly deployed Runtime reached platform Ready in V2 but `/readiness` returned 503. Its MPA metadata bootstrap reported TypeError. Replaying the image's SQLAlchemy asyncpg connection construction with the injected registry URL reproduced `connect() got an unexpected keyword argument 'sslmode'`. VeADK's own database adapter converts this parameter, whereas the independently versioned MPA image does not. - -FR-1: Normalize `sslmode` to `ssl` when injecting `SHARED_APIG_DATABASE_URL`, preserving its value, credentials, host, database and unrelated parameters. Already-compatible URLs stay byte-for-byte unchanged. Reject contradictory or repeated TLS parameters without exposing the URL. -FR-2: Both initial creation and finalization receive the compatible URL. A pending deployment whose recorded hash differs only by this normalization may resume. A lost create response must replay its original payload with its original client token. Unrelated changes remain rejected. -FR-3: Recover the authorized current Runtime by changing only this environment value, then verify platform readiness, application readiness and Studio task completion. No new PG, APIG, worker or Runtime is allocated by recovery. - -## Design, tasks and acceptance - -T-1 / AC-1: Add offline tests for driver connection arguments, TLS modes, conflicting parameters, initial/final payloads and retries. First demonstrate failure. -T-2 / AC-2: Add a pure URL normalizer in managed `runtime.py`; use it in the injected environment. Extend the pending-hash compatibility check for this specific legacy representation, retaining exact replay when the Runtime ID is unknown. -T-3 / AC-3: Run managed deployment regressions, changed-file Ruff/Pyright, whitespace checks and secret scanning. Apply the single-variable Runtime correction and observe the existing creation task. - -Only managed MPA provisioning is affected; generic Studio conversations, UI, images, database ownership and APIG creation are outside scope. Update the owning component contract in both languages. The raw deployment profile remains available to the control plane; credentials never enter reports or tests. The URL conversion preserves the requested TLS mode; a database proxy's internal connection encryption is outside this adapter's control. - -## Review, authorization and verification - -Direct design review (review-spec is unavailable): reviewed compatibility, idempotency, secret handling and failure boundaries. User authorized fixing and recovering this failed creation with “帮忙解决下”. No additional design blocker remains. Live recovery may publish one additional Runtime version. Existing registry records and cloud resources are retained. - -Verification (2026-09-23, `feat/from-main-20260922`, this uncommitted fix): - -- **pass:** 12 new regression cases failed before implementation and passed afterward. The affected deployment selection passed 52 tests. Broader managed-creation and provisioning-environment regression passed 359 tests; the CLI selection passed 19 tests. Six initially failing service tests used a non-URL stub; their fixture now uses a valid fake PostgreSQL URL. -- **pass:** `UV_CACHE_DIR=/private/tmp/veadk-uv-cache uv run --extra dev pytest -q tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py` and the separate `tests/cli/test_cli_mpa.py` selection. CLI tests ran with local mock-server port binding allowed. -- **pass:** Ruff 0.11.12 and Pyright on `runtime.py`, `test_registry_url.py`, and `test_service.py`; Gitleaks on affected files; `git diff --check`; paired-document links/identifiers reviewed. Tools used temporary caches after the default cache path was unavailable. -- **pass (live):** Only the existing Runtime's registry environment value was corrected. V3 reached Ready, the application readiness probe returned success, the existing Studio task became `succeeded`, and the deployment registry is `ready` with `pending=false`. No additional PG, APIG, worker or Runtime was allocated. No secret values were included in evidence. -- **not_applicable:** frontend build/browser visual gates and harness smoke because no frontend, transcript or harness code changed. **not_run:** repository-wide regression and all-files pre-commit because this delivery is a scoped uncommitted fix; affected regression and secret/static checks ran. Old Runtime images are supported through producer-side normalization, without a rebuild. - -T-1–T-3 and AC-1–AC-3 are satisfied. No commit or push was performed. diff --git a/prd-spec/bugfixes/2026-09-23-mpa-registry-tls-url.zh.md b/prd-spec/bugfixes/2026-09-23-mpa-registry-tls-url.zh.md deleted file mode 100644 index 935380772..000000000 --- a/prd-spec/bugfixes/2026-09-23-mpa-registry-tls-url.zh.md +++ /dev/null @@ -1,35 +0,0 @@ -# 规范化托管 MPA 注册库 TLS 连接地址 - -[English](2026-09-23-mpa-registry-tls-url.md) - -日期:2026-09-23。状态:已在工作区实现。组件:[Studio MPA 创建](../../specs/studio-mpa-creation/README.zh.md)。 - -## 证据、范围和需求 - -新 Runtime 的 V2 已达到平台 Ready,但 `/readiness` 返回 503,MPA 元数据引导报 TypeError。使用下发的注册库地址复现镜像中的 SQLAlchemy asyncpg 连接构造,得到 `connect() got an unexpected keyword argument 'sslmode'`。VeADK 自身的数据库适配器会转换此参数,独立版本的 MPA 镜像没有转换。 - -FR-1:下发 `SHARED_APIG_DATABASE_URL` 时将 `sslmode` 转为 `ssl`,保留参数值、凭据、主机、数据库和其他参数。已兼容的 URL 保持字节级不变。拒绝冲突或重复 TLS 参数,错误不得暴露 URL。 -FR-2:初次创建和最终配置均收到兼容地址。进行中任务若记录的哈希仅因本次规范化而不同,可以恢复。丢失创建响应时必须使用原客户端令牌重放原始请求。其他配置变化仍被拒绝。 -FR-3:仅修改已授权当前 Runtime 的这一环境变量,然后验证平台就绪、应用就绪和 Studio 任务完成。恢复不分配新的 PG、APIG、Worker 或 Runtime。 - -## 设计、任务与验收 - -T-1 / AC-1:添加离线测试,覆盖驱动连接参数、TLS 模式、参数冲突、初次/最终请求及重试;先验证测试失败。 -T-2 / AC-2:在 managed `runtime.py` 添加纯 URL 规范化函数,并用于下发环境变量。将进行中哈希兼容检查扩展到这一特定旧表示;Runtime ID 未知时保留精确重放。 -T-3 / AC-3:执行托管部署回归、变更文件 Ruff/Pyright、空白检查和密钥扫描。对当前 Runtime 修正单个变量,并观察已有创建任务。 - -仅影响托管 MPA 部署;通用 Studio 对话、UI、镜像、数据库归属和 APIG 创建不在范围内。同步更新所属组件的双语契约。原部署配置继续供控制面使用;凭据不得进入报告或测试。URL 转换保留所要求的 TLS 模式;数据库代理的内部连接加密不属于此适配器的控制范围。 - -## 审查、授权与验证 - -直接设计审查(review-spec 不可用):已审查兼容性、幂等性、秘密处理和失败边界。用户以“帮忙解决下”授权修复并恢复本次失败创建,无额外设计阻塞。真实恢复可能增加一个 Runtime 发布版本。保留现有注册记录和云资源。 - -验证记录(2026-09-23,`feat/from-main-20260922`,本次未提交修复): - -- **pass:**新增 12 个回归用例在实现前失败、实现后通过;受影响部署用例共 52 项通过。扩大后的托管创建和部署环境回归共 359 项通过,CLI 用例共 19 项通过。最初 6 项服务测试使用了非 URL 占位值,现已将测试夹具改为有效的虚构 PostgreSQL URL。 -- **pass:**`UV_CACHE_DIR=/private/tmp/veadk-uv-cache uv run --extra dev pytest -q tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py`,以及单独的 `tests/cli/test_cli_mpa.py`。CLI 测试允许绑定本地模拟服务端口。 -- **pass:**对 `runtime.py`、`test_registry_url.py` 和 `test_service.py` 执行 Ruff 0.11.12 和 Pyright;对相关文件执行 Gitleaks;`git diff --check`;核对双语文档链接及标识符。默认缓存路径不可用后,检查工具使用临时缓存。 -- **pass(真实环境):**仅修正已有 Runtime 的注册库环境变量值。V3 达到 Ready,应用就绪探针成功,已有 Studio 任务变为 `succeeded`,部署注册记录为 `ready` 且 `pending=false`。未分配额外 PG、APIG、Worker 或 Runtime,验证记录未包含秘密值。 -- **not_applicable:**前端构建/浏览器视觉检查及 harness 冒烟,因为没有修改前端、对话或 harness 代码。**not_run:**全仓库回归及全文件 pre-commit,本次交付为限定范围的未提交修复;已执行受影响回归及密钥/静态检查。通过生产方规范化兼容旧 Runtime 镜像,无需重建镜像。 - -T-1–T-3 和 AC-1–AC-3 均已满足。没有提交或推送。 diff --git a/prd-spec/bugfixes/a2a-long-running/2026-09-15-wait.md b/prd-spec/bugfixes/a2a-long-running/2026-09-15-wait.md deleted file mode 100644 index 558f95595..000000000 --- a/prd-spec/bugfixes/a2a-long-running/2026-09-15-wait.md +++ /dev/null @@ -1,17 +0,0 @@ -# A2A long-running request status - -[中文](2026-09-15-wait.zh.md) - -Change: a2a-long-running; 2026-09-15; approved. User requested the fix after a real 93-second request completed after Studio aborted at 30 seconds. Scope is the Studio bridge and existing transcript progress UI. Backend business logic, authentication, task creation and automatic retries are excluded. - -FR-1: The authenticated A2A bridge emits a nonterminal `customMetadata.a2aStatus=connecting` event before waiting for upstream HTTP. It means waiting for Runtime, not accepted or successful execution. Existing 30-second first-event guard remains for unavailable Studio/probe paths; after a valid event, requests continue until completion, failure or cancellation. -FR-2: Existing submitted/working events render localized queue/execution status using the existing progress component. Status is scoped to the active assistant turn and is not an answer; real content replaces the placeholder. Unknown statuses use the normal thinking placeholder. -FR-3: HTTP/protocol failures still surface, incomplete replies remain failures, and closing the stream cleans up the upstream client. No auto-retry of side effects. - -T-1/AC-1: Add failing bridge and projector regressions, including non-streaming/fallback/streaming, delayed final response, cancellation, replay and normal reply. T-2/AC-2: Implement minimal metadata projection and progress rendering without new dependencies. T-3/AC-3: Run affected Python/frontend tests, frontend suite/build, changed-line coverage above 95%, Ruff/Pyright/pre-commit and browser checks; update bundled assets. Related contract: specs/studio-runtime-diagnostics. - -Review: approved user request; boundaries and translations reviewed, no blockers. Missing ui-ux-pro-max skill is not available at the referenced or installed locations; use existing accessible status component and frontend/SPEC.md. Risk: waiting for a silent upstream can persist until user cancellation; status deliberately does not claim server acceptance. No new storage, permissions or cloud configuration. Verification pending. - -Verification on 2026-09-15: frontend suite 1099 passed; Python Runtime proxy and A2A decoder suite 101 passed. Four Vitest progress/metadata rendering checks passed. Changed executable bridge/projector/placeholder lines measured with pytest-cov and Vitest V8: 7/7 covered (100%); the App wiring was verified in the real browser. A deterministic stream test advances 120 seconds after connecting and receives the final answer without abort. Existing cancellation/error/incomplete/replay regressions pass. Build and pre-commit pass; source diff whitespace passes, generated vendor template strings retain whitespace warnings. Pyright on cli_frontend reports 34 existing diagnostics outside this change. UI skill ui-ux-pro-max unavailable; existing accessible progress component used. Real browser on localhost:8765 showed waiting followed by a complete reply from the selected Runtime; no tools or scheduled tasks were requested. Bundled assets and local launch path updated. No cloud configuration changes. Narrow-screen/reduced-motion behavior reuses the unchanged progress component; no separate narrow viewport run was performed. - -Additional gate result: test_frontend_runtime_proxy.py Pyright reports 20 pre-existing diagnostics, reproduced from e523cc7. The commit hook secret check timed out after 2 seconds; the separate full pre-commit secret scan passed. Push is blocked by GitHub 403 for PolarisCzh on superops-team/veadk-python. diff --git a/prd-spec/bugfixes/a2a-long-running/2026-09-15-wait.zh.md b/prd-spec/bugfixes/a2a-long-running/2026-09-15-wait.zh.md deleted file mode 100644 index 50e431d43..000000000 --- a/prd-spec/bugfixes/a2a-long-running/2026-09-15-wait.zh.md +++ /dev/null @@ -1,17 +0,0 @@ -# A2A 长耗时请求状态 - -[English](2026-09-15-wait.md) - -变更:a2a-long-running;2026-09-15;approved。真实请求耗时 93 秒完成,但 Studio 在 30 秒提前中断,用户要求修复。范围为 Studio 桥接与现有对话进度界面,不涉及后端业务、鉴权、任务创建或自动重试。 - -FR-1:已鉴权的 A2A 桥接在等待上游 HTTP 前输出非终止事件 `customMetadata.a2aStatus=connecting`,只表示等待 Runtime,不代表接受任务或执行成功。Studio/探测不可用时仍保留 30 秒首事件保护;收到有效事件后等待完成、失败或取消。 -FR-2:已有 submitted/working 状态通过现有进度组件展示本地化排队/执行状态,归属于当前助手轮次且不算答案,实际内容替换占位。未知状态使用普通思考占位。 -FR-3:HTTP/协议错误继续上报,回复不完整仍报错,关闭流释放上游连接,不自动重试有副作用的请求。 - -T-1/AC-1:先补失败回归,覆盖桥接非流式/降级/流式、延迟结果、取消、重放和正常回复。T-2/AC-2:最小实现元数据投影和进度展示,不增加依赖。T-3/AC-3:执行相关 Python/前端测试、前端全量测试与构建、增量覆盖率高于 95%、Ruff/Pyright/pre-commit 与浏览器检查,同步打包资源。相关契约:specs/studio-runtime-diagnostics。 - -评审:用户已明确要求修复;边界与双语内容检查无阻塞。ui-ux-pro-max 在引用位置与安装位置均缺失,复用现有可访问状态组件并遵循 frontend/SPEC.md。风险:静默上游可能持续等待直至用户取消,状态不声称服务端已接受请求。不增加存储、权限或云配置。验证待执行。 - -2026-09-15 验证:前端全量 1099 项通过;Python Runtime 代理和 A2A 解码测试 101 项通过;4 项 Vitest 状态/元数据渲染测试通过。pytest-cov 与 Vitest V8 统计新增桥接/投影/占位可执行行 7/7 覆盖(100%),App 接线已在真实浏览器验证。确定性流测试在 connecting 后推进 120 秒仍能收到最终答案而未中断。已有取消/错误/不完整/重放回归通过。构建与 pre-commit 通过,源文件 diff 空白检查通过,生成资源中第三方模板字符串仍有空白告警。cli_frontend 的 Pyright 有 34 处改动外已有诊断。ui-ux-pro-max 不可用,复用现有可访问进度组件。localhost:8765 真实浏览器已看到等待提示随后被选定 Runtime 的完整答案替换,未请求工具调用或定时任务。已更新打包资源与本地启动路径,没有修改云端配置。窄屏与减少动效沿用未变化的进度组件,未单独进行窄视口检查。 - -补充检查结果:test_frontend_runtime_proxy.py 的 Pyright 有 20 处已有诊断,已从 e523cc7 基线复现。提交钩子密钥检查在 2 秒后超时,独立运行的全量 pre-commit 密钥扫描已通过。推送因 PolarisCzh 对 superops-team/veadk-python 无写权限返回 GitHub 403 而受阻。 diff --git a/prd-spec/bugfixes/apmplus-appkey/2026-10-22-fallback.md b/prd-spec/bugfixes/apmplus-appkey/2026-10-22-fallback.md deleted file mode 100644 index 9279b4cd5..000000000 --- a/prd-spec/bugfixes/apmplus-appkey/2026-10-22-fallback.md +++ /dev/null @@ -1,11 +0,0 @@ -# APMPlus AppKey fallback - -User-approved scope: reuse automatic GetAppKey, support tenant keys and safe failure; commit and push, no deployment. - -Evidence: APMPlusConfig already lazily retrieves and caches a successful key. MPA prevented that path before config construction. Goals: explicit key > tenant key > existing credential discovery; blank keys count as absent; malformed API responses must not leak their payload. Preserve IAM/STS, provider routing, public signatures and cached-property lifetime. Failure remains an exception in VeADK; the optional MPA registration boundary logs a sanitized failure and continues startup. No empty-success fallback, global credential cache, content-capture changes or trace-context changes. - -Plan: add failing configuration/auth tests, implement small resolver changes, run targeted and related tracing tests, measure added-line coverage above 95%, review and commit/push. Test explicit/tenant/automatic selection, lazy successful cache, failed lookup retry, environment AK/SK and IAM session token, malformed/empty responses. All API calls mocked; no cloud validation claimed. - -Design review: scope and ownership consistent; cached-property is per settings instance, not an account-global cache. Failure details must not contain provider response. Existing bounded HTTP timeout remains unchanged. No blocking findings. User authorization is the current request to implement the previously discussed fallback. - -Verification: 78 related configuration/auth/tracing tests passed; added executable lines 6/6 (100%), plus all key-precedence/cache branches exercised. Full-repository pre-commit passed after formatting the changed tests, including secret scanning. No dependency changes, package publication or cloud calls. diff --git a/prd-spec/bugfixes/apmplus-appkey/2026-10-22-fallback.zh.md b/prd-spec/bugfixes/apmplus-appkey/2026-10-22-fallback.zh.md deleted file mode 100644 index 255b8e7a7..000000000 --- a/prd-spec/bugfixes/apmplus-appkey/2026-10-22-fallback.zh.md +++ /dev/null @@ -1,11 +0,0 @@ -# APMPlus AppKey 兜底 - -用户已批准:复用自动 GetAppKey,支持租户 key 和安全失败;提交推送,不部署。 - -证据:APMPlusConfig 已延迟获取并缓存成功的 key,MPA 在构造配置之前阻断了这条路径。目标:显式 key 优先,其次租户 key,最后现有凭据发现;空白 key 视为未配置;异常 API 响应不能泄露正文。保留 IAM/STS、区域路由、公开签名和 cached-property 生命周期。VeADK 获取失败仍抛错,MPA 可选注册边界记录脱敏失败并继续启动。不返回空成功,不增加跨账号全局缓存,不改内容采集和 trace 上下文。 - -计划:先补失败测试,再实现,运行定向及关联 tracing 测试,新增代码行覆盖率超过95%,评审后提交推送。覆盖显式/租户/自动选择、延迟成功缓存、失败重试、环境AK/SK、IAM临时token、异常及空响应。API全部模拟,不宣称线上验证。 - -方案评审:范围和职责一致;缓存限于 settings 实例,不跨账号共享。失败信息不得包含服务响应。保留现有HTTP超时。无阻塞项。授权来自本轮用户明确要求实施已讨论方案。 - -验证:配置/鉴权/Tracing关联78项通过;新增可执行行6/6(100%),key优先级及缓存分支均覆盖。格式化本次测试后,全仓pre-commit(含密钥扫描)通过。未变更依赖、未发布包、未调用云API。 diff --git a/prd-spec/bugfixes/channel-group-actions/2026-09-15-channel-group-actions.md b/prd-spec/bugfixes/channel-group-actions/2026-09-15-channel-group-actions.md deleted file mode 100644 index 635867878..000000000 --- a/prd-spec/bugfixes/channel-group-actions/2026-09-15-channel-group-actions.md +++ /dev/null @@ -1,17 +0,0 @@ -# Compact group permission actions - -[中文](2026-09-15-channel-group-actions.zh.md) - -Date: 2026-09-15. Status: implemented. The user requests removal of Edit and replacement of Remove with a cross icon in Feishu group permission rows. - -## Evidence, scope and design -RuntimeChannels renders Edit and Remove as full buttons; its descendant span rule also stretches button internals. Remove Edit, reuse the repository SVG cross for the remaining accessible icon button, and scope text sizing to the row's direct label. Keep a 28px target on the right and wrap long group names within available space. Preserve the existing DELETE/refresh, busy/error behavior, and form. No new confirmation is introduced; the current handler has none. No API, permission, state/data or backend contract changes; the component contract assessment is no impact. - -## Tasks and acceptance -T-1/AC-1: no Edit entry, one cross action per group with a group-specific accessible label. T-2/AC-2: existing removal works and long names do not displace the icon at narrow widths. T-3/AC-3: run existing channel/frontend tests, build/assets, and mocked real-browser desktop/narrow checks. No new tests for the reversible presentation change; existing removal handlers remain unchanged. - -## Review and risks -Direct review (review-spec unavailable): user instruction approves scope, existing SVG/button/semantic tokens reused, no behavior or authorization expansion. No blockers. Design skills referenced by frontend/SPEC.md remain unavailable. No cloud/provider operations or deployment. Verification complete. - -## Verification results -2026-09-15 current uncommitted group-action diff: pass. `cd frontend && ./node_modules/.bin/vitest run tests/runtimeChannels.test.tsx --maxWorkers 1`: 31 tests; `npm --prefix frontend test`: 1208 tests; `npm --prefix frontend run build`: pass (existing bundle warnings); `npm --prefix frontend run test:webui-assets`: 104 files/248 references. Browser with isolated mocked APIs: no Edit, 28px cross, long names fit desktop and 480×1000 narrow layout; keyboard Enter removes the row and shows zero groups. The initial mock missed Runtime DELETE method override; correcting the mock passed without production handler changes. Scoped whitespace/paired links passed. Python/IME/new asynchronous-state checks not_applicable; live operations, deployment and pre-commit gates not_run (no deployment or commit requested). T-1 through T-3/AC-1 through AC-3 complete; no review blockers. diff --git a/prd-spec/bugfixes/channel-group-actions/2026-09-15-channel-group-actions.zh.md b/prd-spec/bugfixes/channel-group-actions/2026-09-15-channel-group-actions.zh.md deleted file mode 100644 index ccd2c871e..000000000 --- a/prd-spec/bugfixes/channel-group-actions/2026-09-15-channel-group-actions.zh.md +++ /dev/null @@ -1,17 +0,0 @@ -# 精简群权限操作 - -[English](2026-09-15-channel-group-actions.md) - -日期:2026-09-15。状态:implemented。用户要求移除飞书群权限行的“编辑”,将“移除”替换为叉号图标。 - -## 证据、范围与设计 -RuntimeChannels 将编辑和移除渲染为完整按钮,后代 span 规则还会拉伸按钮内部。去掉编辑,剩余操作复用仓库 SVG 叉号和可访问图标按钮,文本尺寸规则仅作用于行的直接标签。右侧保留 28px 点击区域,长群名在剩余空间换行。保留既有 DELETE/刷新、忙碌/错误行为及表单。不新增确认;当前处理没有确认。不改变 API、权限、状态/数据或后端契约,组件契约评估为无影响。 - -## 任务与验收 -T-1/AC-1:无编辑入口,每群仅有一个叉号操作并带具体群名的可访问标签。T-2/AC-2:既有移除正常,长名称在窄窗口不挤走图标。T-3/AC-3:运行既有渠道/前端测试、构建/资源检查、模拟 API 的真实浏览器宽/窄检查。可逆展示调整不新增测试,移除处理保持不变。 - -## 评审与风险 -直接评审(review-spec 不可用):用户指令授权此范围,复用既有 SVG/按钮/语义变量,无行为或鉴权扩展。无阻塞。frontend/SPEC.md 引用的设计技能仍不可用。不执行云端/渠道操作或部署。验证完成。 - -## 验证结果 -2026-09-15 当前未提交的群权限操作差异:pass。`cd frontend && ./node_modules/.bin/vitest run tests/runtimeChannels.test.tsx --maxWorkers 1`:31 项;`npm --prefix frontend test`:1208 项;`npm --prefix frontend run build`:通过(既有包体积警告);`npm --prefix frontend run test:webui-assets`:104 文件/248 引用。浏览器配隔离模拟 API:无编辑、28px 叉号、长群名在桌面及 480×1000 窄窗口布局正常;键盘 Enter 移除后显示 0 个群。初次模拟遗漏 Runtime DELETE 方法覆盖,修正模拟后通过,无需更改生产处理。范围内空白/双语链接检查通过。Python/IME/新增异步状态检查 not_applicable;真实云操作、部署和提交前门禁 not_run(未要求部署或提交)。T-1 至 T-3/AC-1 至 AC-3 完成,评审无阻塞。 diff --git a/prd-spec/bugfixes/channel-group-actions/2026-10-12-server-default-scope.md b/prd-spec/bugfixes/channel-group-actions/2026-10-12-server-default-scope.md deleted file mode 100644 index d5f172a80..000000000 --- a/prd-spec/bugfixes/channel-group-actions/2026-10-12-server-default-scope.md +++ /dev/null @@ -1,40 +0,0 @@ -# Server-owned defaults for new Feishu group permissions - -[中文](2026-10-12-server-default-scope.zh.md) - -Change: studio-channel-server-default-scope. Date: 2026-10-12. Status: implemented. -Contract: [mpa-channels](../../../specs/mpa-channels/README.md), CON-17. - -## Evidence and scope -RuntimeChannels initializes group_sender and sends it with every group-permission POST. This overrides Runtime defaults even when the administrator only intends to allow a group. The user explicitly approved removing the isolation selector and omitting this field, while preserving existing groups. Only the Studio consumer changes; Runtime API types, optional scope support and backend precedence remain intact. - -## Requirements and design -FR-1: A new group form contains no isolation selector or hidden scope state. FR-2: Its POST contains channel, trimmed chatId and appId when an account is selected, but never groupSessionScope, including null or an empty string. FR-3: Existing groups retain their returned scope display; rendering, refreshing or adding another group must not issue automatic updates to them. FR-4: Existing loading, disabled, IME, cancellation, failure/retry and multi-bot targeting behavior remains. - -Delete only the selector, its local state/options/imports, and the POST property. Do not force group, clear stored scopes, change data structures, migrate data, add dependencies or touch execution/streaming. API-level callers may still use the optional field. The Runtime owns resulting defaults; Studio does not claim every deployment defaults to group. Adding an already existing ID follows the unchanged Runtime upsert behavior; no migration guarantee is invented. - -## Tasks and acceptance -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| FR-1/FR-2 | T-1: regression first, then minimal RuntimeChannels change | AC-1: selector absent and exact POST body omits scope in single/multi-bot modes | runtimeChannels.test.tsx | -| FR-3 | T-2: retain returned scope rendering | AC-2: existing group_sender/topic modes display without writes | runtimeChannels.test.tsx | -| FR-4 | T-3: run existing interaction tests and local browser checks | AC-3: normal/failure/retry/keyboard/narrow flow and account identity preserved | npm test, targeted Vitest, npm run build, browser | - -## Review and authorization -Direct design review: feasible existing optional API, no backend or migration change; selected-account auth and abort handling unchanged. The existing-list fallback stays unchanged. No blockers. Authorization is the user's explicit request in this chat. No additional approval needed for the specified minimal change. The referenced frontend-design/ui-ux-pro-max files were absent after searching the repo and local skill roots; this deletion uses frontend/SPEC.md and the component foundation without a new visual design. A global/local VeADK review-spec skill is absent; this review records the equivalent checks directly, without applying another repository's report workflow. - -## Verification and delivery -Verified on 2026-10-12 against HEAD 812faadf plus this worktree diff. Production change removes 31 lines from RuntimeChannels; English/Chinese group description now only describes admission. Existing Button/input/form styles and returned scope labels are reused unchanged; no new styles or dependencies. frontend/README.md and CON-17 are reconciled in both languages. - -- pass: before-fix targeted regression 2 failed / 3 passed / 61 skipped, reproducing the selector and unintended multi-bot scope property. -- pass: targeted `./node_modules/.bin/vitest run tests/runtimeChannels.test.tsx tests/feishuAccounts.test.tsx` from frontend: runtimeChannels.test.tsx 66 passed. No separate feishuAccounts test file matched; multi-bot tests run in RuntimeChannels. -- pass: `npm --prefix frontend test`: Node 1377 passed, Vitest 67 passed. Existing i18next/localStorage warnings remain; no failed tests. -- pass: `npm --prefix frontend run check:i18n`: 2 locales / 21 namespaces consistent. -- pass: final `npm --prefix frontend run build`: TypeScript and both Vite builds succeeded; generated veadk/webui assets updated. Asset grouping changes are generated output, not edits to unrelated source. -- pass: `npm --prefix frontend run test:webui-assets`: 113 files / 350 internal references verified. -- pass: real browser mounted the actual component with isolated HTTP fixtures: disabled empty submit, no isolation control, busy controls disabled, failed save preserves chat ID, keyboard Enter retries, success clears input, original group_sender label stays visible; 390px viewport flow usable. Recorded three actual POST bodies contain only channel/chatId; no write to the existing group. Multi-bot appId omission regression is covered by Vitest, not this single-bot browser fixture. Temporary viewport reset and fixture files/processes removed after verification. -- pass: gitleaks via repository pre-commit hook on changed source/test/docs files; `git diff --check`. -- not_applicable: Python tests/Ruff/Pyright, because no Python changes. -- not_run: real cloud/Feishu E2E, deployment, remote CI, full pre-commit/all-files; no such publication authorized and preserve unrelated worktree edits. - -Local logs and browser evidence are `/tmp/mpa-studio-default-scope-*`, including the form screenshot. No credentials or real production data used. Existing package-lock.json change (512 removed lines) was preserved without modification. No commit/push/deploy performed. Remaining risk: Runtime bot-level inheritance may differ by deployment; omission deliberately delegates that choice. Re-adding an existing ID uses existing Runtime upsert semantics rather than a new preservation guarantee. diff --git a/prd-spec/bugfixes/channel-group-actions/2026-10-12-server-default-scope.zh.md b/prd-spec/bugfixes/channel-group-actions/2026-10-12-server-default-scope.zh.md deleted file mode 100644 index c75d88a1d..000000000 --- a/prd-spec/bugfixes/channel-group-actions/2026-10-12-server-default-scope.zh.md +++ /dev/null @@ -1,40 +0,0 @@ -# 新增飞书群权限沿用服务端默认策略 - -[English](2026-10-12-server-default-scope.md) - -变更:studio-channel-server-default-scope。日期:2026-10-12。状态:implemented。 -契约:[mpa-channels](../../../specs/mpa-channels/README.zh.md),CON-17。 - -## 依据和范围 -RuntimeChannels 默认选中 group_sender,并在每次群权限 POST 中提交,导致管理员仅想允许一个群时也覆盖 Runtime 默认值。用户已明确授权删除隔离选择器、不提交该字段、保留已有群。本次只改 Studio 调用方;Runtime API 类型、可选策略能力和后端优先级保持不变。 - -## 需求和设计 -FR-1:新增群表单不含隔离选择器或隐藏策略状态。FR-2:POST 仅包含 channel、去空白 chatId,以及选择账号时的 appId;不包含 groupSessionScope,包括 null 或空字符串。FR-3:已有群继续显示服务端返回的策略;渲染、刷新或新增其他群不会自动更新已有群。FR-4:保留加载、禁用、IME、取消、失败重试及多机器人目标选择行为。 - -仅删除选择器及其状态、选项、导入和提交属性。不强制 group、不清空持久策略、不改数据结构或迁移、不加依赖、不触碰执行和流式。API 调用方仍可显式使用可选字段。最终默认值由 Runtime 决定,Studio 不承诺所有部署都默认 group。再次添加已有 ID 仍按 Runtime 原 upsert 行为处理,不虚构迁移保证。 - -## 任务和验收 -| 需求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| FR-1/FR-2 | T-1:先回归再最小修改 RuntimeChannels | AC-1:没有选择器,单/多机器人 POST 精确请求体均无策略字段 | runtimeChannels.test.tsx | -| FR-3 | T-2:保留返回策略展示 | AC-2:已有 group_sender/话题策略正常显示且无写入 | runtimeChannels.test.tsx | -| FR-4 | T-3:执行既有交互测试及本地浏览器检查 | AC-3:正常/失败/重试/键盘/窄窗口及账号身份保留 | npm test、定向 Vitest、npm run build、浏览器 | - -## 评审和授权 -直接设计评审:现有可选接口可实现,无后端或迁移变化;所选账号鉴权和取消逻辑不变;既有列表回退显示不变。无阻塞项。授权依据为本聊天用户明确要求,无需对相同最小改动重复确认。仓库引用的 frontend-design/ui-ux-pro-max 在仓库和本地技能目录搜索后仍不存在;本次删除控件遵循 frontend/SPEC.md 及组件基础规范,不新增视觉设计。VeADK 本地/全局 review-spec 缺失,在此直接记录等价检查,不套用其他仓库的报告工作流。 - -## 验证和交付 -2026-10-12 在 HEAD 812faadf 加本次工作区差异上验证。生产代码仅从 RuntimeChannels 删除 31 行,中英文群说明改为只描述准入。沿用原 Button、输入框、表单样式和返回策略标签,不新增样式或依赖;frontend/README.md 和 CON-17 已同步两种语言。 - -- pass:改前定向回归 2 failed / 3 passed / 61 skipped,复现选择器及多机器人请求中多出的策略属性。 -- pass:在 frontend 运行 `./node_modules/.bin/vitest run tests/runtimeChannels.test.tsx tests/feishuAccounts.test.tsx` 对应定向命令,runtimeChannels.test.tsx 66 passed;未匹配独立 feishuAccounts 测试文件,多机器人测试位于 RuntimeChannels。 -- pass:`npm --prefix frontend test`:Node 1377 passed,Vitest 67 passed。保留既有 i18next/localStorage 告警,无失败测试。 -- pass:`npm --prefix frontend run check:i18n`:2 种语言、21 个命名空间一致。 -- pass:最终 `npm --prefix frontend run build`:TypeScript 及两次 Vite 构建成功,veadk/webui 生成物已更新;资源分组变化属于构建输出,并未编辑其他业务源码。 -- pass:`npm --prefix frontend run test:webui-assets`:113 个文件、350 个内部引用校验通过。 -- pass:真实浏览器挂载实际组件、使用隔离 HTTP fixture,检查空输入禁用、无隔离控件、执行中控件禁用、失败保留群 ID、Enter 重试、成功清空输入、原 group_sender 标签保留;390px 视口流程可用。记录三次实际 POST 均仅含 channel/chatId,未写入已有群。多机器人 appId 请求回归由 Vitest 覆盖,此浏览器 fixture 为单机器人。验证后恢复视口、移除临时文件并停止 fixture 进程。 -- pass:仓库 pre-commit 的 gitleaks 扫描变更源码/测试/文档;`git diff --check`。 -- not_applicable:Python 测试/Ruff/Pyright,无 Python 改动。 -- not_run:真实云/飞书 E2E、部署、远端 CI、完整 pre-commit/all-files;未授权发布,保留无关工作区改动。 - -本地日志和浏览器证据为 `/tmp/mpa-studio-default-scope-*`,包含表单截图;未使用凭据或真实生产数据。已有 package-lock.json 的 512 行删除保持原样,没有修改。未提交/推送/部署。剩余风险:不同部署的 Runtime Bot 级继承可能不同,省略字段就是将默认选择交给它;再次添加已有 ID 仍按原 Runtime upsert 语义,不增加新保留保证。 diff --git a/prd-spec/bugfixes/channel-qr-lifetime/2026-09-15-channel-qr-lifetime.md b/prd-spec/bugfixes/channel-qr-lifetime/2026-09-15-channel-qr-lifetime.md deleted file mode 100644 index cd7902409..000000000 --- a/prd-spec/bugfixes/channel-qr-lifetime/2026-09-15-channel-qr-lifetime.md +++ /dev/null @@ -1,21 +0,0 @@ -# Transient channel pairing QR - -[中文](2026-09-15-channel-qr-lifetime.zh.md) - -Date: 2026-09-15. Status: implemented. Contract: [MPA channels](../../../specs/mpa-channels/README.md). User approved removing waiting/link text and discarding QR display after navigation or refresh. - -## Evidence, scope and requirements -RuntimeChannels stores binding IDs in sessionStorage and resumes pairing on mount. This restores a QR after returning to a channel. FR-1: remove the PENDING waiting text and Open authorization page link while keeping the QR and meaningful error/terminal states. FR-2: binding UI and polling live only in the mounted provider panel. Switching channel/page, browser reload or the panel Refresh clears pairing display. Returning requires an explicit new generation. Ignore and clean legacy stored binding IDs. FR-3: preserve persisted bot configuration, cancel requests/timers and reject late responses; no DELETE, provider revocation or backend changes. - -## Design and review -Keep binding state in React only, remove sessionStorage persistence/resumption, clean the legacy key on mount, and reset transient pairing on panel refresh. Keep existing keyed provider mounts and abort controllers. Direct review (review-spec unavailable): API/credential/server binding lifecycle unchanged; only client resume contract changes. User request explicitly approves this change. QR hiding is not server revocation. No unresolved blocker. - -## Tasks and acceptance -T-1/AC-1 (FR-1): regression verifies QR remains while waiting text/link disappear. -T-2/AC-2 (FR-2/3): test channel switch/return, legacy stored ID, refresh and cancellation; implement in RuntimeChannels.tsx. -T-3/AC-3: frontend Vitest/regression/build/assets and browser checks; update docs. Affected files: component, component tests, bilingual design/spec, frontend README and generated webui. No Python change or live cloud operation. - -## Verification -2026-09-15, current uncommitted frontend diff: pass. Test-first regression: 8 failures before implementation, 31 channel component tests passed after (`cd frontend && ./node_modules/.bin/vitest run tests/runtimeChannels.test.tsx --maxWorkers 1`). Frontend suite: 1206 passed (`npm --prefix frontend test`). Production build: pass (`npm --prefix frontend run build`), existing chunk-size warnings only. Packaged assets: 104 files and 248 references verified (`npm --prefix frontend run test:webui-assets`). - -Browser checks with the real component and simulated API: pairing hides waiting/link text, keyboard switch/return does not restore pairing, browser reload does not restore it, and DingTalk panel Refresh removes pairing while preserving the bot summary; 480×850 layout checked. Component tests assert the QR image itself remains initially and disappears on reset; preview responses omitted actual image bytes. Temporary files/server/tab cleaned and viewport reset. Paired documentation, relative links and scoped whitespace checks passed. Python checks: not_applicable (no Python change). Live provider operations: not_run (UI-only change, no real accounts used). diff --git a/prd-spec/bugfixes/channel-qr-lifetime/2026-09-15-channel-qr-lifetime.zh.md b/prd-spec/bugfixes/channel-qr-lifetime/2026-09-15-channel-qr-lifetime.zh.md deleted file mode 100644 index 6c59b024d..000000000 --- a/prd-spec/bugfixes/channel-qr-lifetime/2026-09-15-channel-qr-lifetime.zh.md +++ /dev/null @@ -1,21 +0,0 @@ -# 渠道配对二维码仅临时展示 - -[English](2026-09-15-channel-qr-lifetime.md) - -日期:2026-09-15。状态:implemented。契约:[MPA channels](../../../specs/mpa-channels/README.zh.md)。用户批准移除等待/链接文案,导航或刷新后不恢复二维码。 - -## 证据、范围与需求 -RuntimeChannels 在 sessionStorage 保存绑定 ID,并在挂载时恢复配对,导致回到渠道后恢复二维码。FR-1:移除 PENDING 等待文案和“打开授权页面”链接,保留二维码及有意义的错误/终态。FR-2:绑定界面和轮询仅存在于当前挂载的渠道面板。切换渠道/页面、浏览器刷新或面板“刷新”均清除配对展示;返回后需主动重新生成。忽略并清理旧版已保存绑定 ID。FR-3:保留持久化机器人配置,取消请求/定时器并忽略迟到响应;不调用 DELETE、不撤销平台授权、不修改后端。 - -## 设计与评审 -绑定状态仅存 React,移除 sessionStorage 保存/恢复,挂载时清理旧键,面板刷新时重置临时配对状态。复用渠道 key 挂载和 AbortController。直接评审(review-spec 不可用):API/凭据/服务端绑定生命周期不变,仅改变客户端恢复契约。用户明确批准此变化。隐藏二维码不代表服务端撤销。无未解决阻塞。 - -## 任务与验收 -T-1/AC-1(FR-1):回归验证二维码保留,等待文案/链接消失。 -T-2/AC-2(FR-2/3):测试切换/返回渠道、旧存储 ID、刷新及取消;在 RuntimeChannels.tsx 实施。 -T-3/AC-3:前端 Vitest/回归/构建/资源及浏览器检查,更新文档。影响组件、组件测试、双语设计/契约、前端 README 和 webui 产物。不改 Python 或执行真实云操作。 - -## 验证 -2026-09-15,当前未提交前端 diff:pass。测试先行:实施前 8 项失败,实施后 31 项渠道组件测试通过(`cd frontend && ./node_modules/.bin/vitest run tests/runtimeChannels.test.tsx --maxWorkers 1`)。前端回归:1206 项通过(`npm --prefix frontend test`)。生产构建:pass(`npm --prefix frontend run build`),仅既有大包体警告。资源校验:104 文件、248 引用通过(`npm --prefix frontend run test:webui-assets`)。 - -真实组件与模拟 API 的浏览器验证:配对区不显示等待/链接文案,键盘切换并返回不恢复配对,浏览器刷新不恢复,钉钉面板刷新移除配对且保留机器人摘要;检查了 480×850 布局。组件测试确认二维码图片初始保留、重置后消失;预览响应未提供真实图片字节。临时文件/服务/标签页已清理并恢复视口。双语文档、相对链接及定向空白检查通过。Python 检查:not_applicable(未改 Python)。真实平台操作:not_run(纯界面变更,未用真实账号)。 diff --git a/prd-spec/bugfixes/chat-model-dropdown/2026-09-14-chat-model-dropdown.md b/prd-spec/bugfixes/chat-model-dropdown/2026-09-14-chat-model-dropdown.md deleted file mode 100644 index 77119e94d..000000000 --- a/prd-spec/bugfixes/chat-model-dropdown/2026-09-14-chat-model-dropdown.md +++ /dev/null @@ -1,56 +0,0 @@ -# Chat Model Dropdown Visibility Bugfix - -- **Change ID:** `chat-model-dropdown` -- **Created:** 2026-09-14 -- **Status:** implemented -- **Chinese:** [2026-09-14-chat-model-dropdown.zh.md](2026-09-14-chat-model-dropdown.zh.md) -- **Related specs:** [Studio MPA Control Plane](../../../specs/studio-mpa-control-plane/README.md) - -## Background And Evidence - -In an active Studio chat, the Turn model selector in the composer uses `NewChatCompactSelect`. The shared compact select menu opens below its trigger by default. The composer model selector overrides the menu to open upward with `bottom: calc(100% + 6px)`, but it did not reset the inherited `top` position. That left the menu over-constrained, so users could see the search field without a usable full option list. - -The issue affects switching models during chat. It does not affect model list retrieval or the immutable Turn model snapshot contract. - -## Goals - -- Make the chat composer model dropdown show the search field and the option list together. -- Keep the option list scrollable within the viewport. -- Preserve the existing compact select behavior for Agent, Skill, and video controls. - -## Non-Goals - -- Do not change how models are loaded from Sandbox sessions. -- Do not change selected model persistence or Turn snapshot semantics. -- Do not redesign `NewChatCompactSelect`. - -## Requirements - -- `FR-1`: The composer model menu must open upward from the model trigger without conflicting `top` and `bottom` positioning. -- `FR-2`: The composer model option list must have a bounded height so long model catalogs remain scrollable. -- `FR-3`: Shared compact select menus outside the composer must keep their existing default downward placement. - -## Design - -Add composer-scoped CSS overrides for `.composer-model-select .new-chat-compact-select__menu` and `.composer-model-select .new-chat-compact-select__list`. The menu explicitly sets `top: auto` while preserving the existing right-aligned upward placement. The list gets a viewport-aware `max-height` so the full model catalog can be inspected by scrolling instead of being clipped. - -No component contract changes are required. The fix is presentation-only and uses existing markup, keyboard behavior, search placeholder text, and listbox semantics. - -## Tasks - -- `T-1`: Update composer-scoped compact select CSS. -- `T-2`: Add source-level regression assertions for the upward menu placement and bounded list height. -- `T-3`: Run targeted frontend tests, build Studio assets, and verify packaged asset references. - -## Verification And Acceptance - -| Requirement | Task | Acceptance criterion | Verification | Result | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1`, `T-2` | Composer model menu resets inherited `top` and opens upward from the trigger. | `node --test frontend/tests/newChatComposerLayout.test.mjs frontend/tests/newChatAgentPicker.test.mjs frontend/tests/myAgents.test.mjs` | pass | -| `FR-2` | `T-1`, `T-2` | Composer model options are constrained by a viewport-aware max height and scroll inside the menu. | `node --test frontend/tests/newChatComposerLayout.test.mjs frontend/tests/newChatAgentPicker.test.mjs frontend/tests/myAgents.test.mjs` | pass | -| `FR-3` | `T-1` | The override is scoped to `.composer-model-select`; shared compact select defaults remain unchanged. | `node --test frontend/tests/newChatComposerLayout.test.mjs frontend/tests/newChatAgentPicker.test.mjs frontend/tests/myAgents.test.mjs` | pass | -| All | `T-3` | Built Studio assets include the UI fix and pass asset reference validation. | `npm --prefix frontend run build -- --mode development`; `npm --prefix frontend run test:webui-assets`; `git diff --check` | pass | - -## Risks - -Real browser validation is still needed to confirm the exact viewport in the user's active chat. The CSS change is scoped and reversible if the visual check finds an additional stacking or clipping parent. diff --git a/prd-spec/bugfixes/chat-model-dropdown/2026-09-14-chat-model-dropdown.zh.md b/prd-spec/bugfixes/chat-model-dropdown/2026-09-14-chat-model-dropdown.zh.md deleted file mode 100644 index 87e1a1170..000000000 --- a/prd-spec/bugfixes/chat-model-dropdown/2026-09-14-chat-model-dropdown.zh.md +++ /dev/null @@ -1,56 +0,0 @@ -# 聊天模型下拉菜单可见性修复 - -- **变更 ID:** `chat-model-dropdown` -- **创建日期:** 2026-09-14 -- **状态:** 已实施 -- **英文版本:** [2026-09-14-chat-model-dropdown.md](2026-09-14-chat-model-dropdown.md) -- **相关规格:** [Studio MPA 控制面](../../../specs/studio-mpa-control-plane/README.zh.md) - -## 背景与证据 - -Studio 活跃聊天中的 Turn 模型选择器使用 `NewChatCompactSelect`。共享 compact select 菜单默认向触发器下方展开。Composer 模型选择器通过 `bottom: calc(100% + 6px)` 覆盖为向上展开,但没有重置继承的 `top` 定位,导致菜单同时受上下定位约束;用户只能看到搜索框,完整选项列表不可用或被裁切。 - -该问题影响聊天过程中切换模型;不影响模型列表获取,也不改变 Turn 模型快照不可变契约。 - -## 目标 - -- 让聊天 Composer 模型下拉菜单同时显示搜索输入和选项列表。 -- 长模型列表必须限制在视口内,并在菜单内部滚动。 -- 保持 Agent、Skill 和视频等其他 compact select 的现有行为不变。 - -## 非目标 - -- 不修改 Sandbox 会话的模型加载方式。 -- 不修改已选模型持久化或 Turn 快照语义。 -- 不重设计 `NewChatCompactSelect`。 - -## 需求 - -- `FR-1`:Composer 模型菜单必须从模型触发器向上展开,且不再同时受到冲突的 `top` 和 `bottom` 定位约束。 -- `FR-2`:Composer 模型选项列表必须有明确的高度上限,长模型目录可以在菜单内滚动查看。 -- `FR-3`:Composer 之外的共享 compact select 菜单必须保持默认向下展开行为。 - -## 设计 - -为 `.composer-model-select .new-chat-compact-select__menu` 和 `.composer-model-select .new-chat-compact-select__list` 增加局部 CSS 覆盖。菜单显式设置 `top: auto`,保留既有右对齐、向上展开定位。列表增加基于视口的 `max-height`,使完整模型目录通过菜单内部滚动查看,而不是被裁切。 - -无需修改组件契约。该修复仅影响展示层,复用现有 DOM、键盘交互、搜索 placeholder 文案和 listbox 语义。 - -## 任务 - -- `T-1`:更新 Composer 范围内的 compact select CSS。 -- `T-2`:为向上菜单定位和列表高度上限增加源码级回归断言。 -- `T-3`:执行定向前端测试、构建 Studio 资源,并验证打包资源引用。 - -## 验证与验收 - -| 需求 | 任务 | 验收标准 | 验证 | 结果 | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1`, `T-2` | Composer 模型菜单重置继承的 `top`,从触发器向上展开。 | `node --test frontend/tests/newChatComposerLayout.test.mjs frontend/tests/newChatAgentPicker.test.mjs frontend/tests/myAgents.test.mjs` | pass | -| `FR-2` | `T-1`, `T-2` | Composer 模型选项使用视口感知的最大高度,并在菜单内滚动。 | `node --test frontend/tests/newChatComposerLayout.test.mjs frontend/tests/newChatAgentPicker.test.mjs frontend/tests/myAgents.test.mjs` | pass | -| `FR-3` | `T-1` | 覆盖仅限 `.composer-model-select`;共享 compact select 默认行为不变。 | `node --test frontend/tests/newChatComposerLayout.test.mjs frontend/tests/newChatAgentPicker.test.mjs frontend/tests/myAgents.test.mjs` | pass | -| 全部 | `T-3` | 构建后的 Studio 资源包含 UI 修复,并通过资源引用校验。 | `npm --prefix frontend run build -- --mode development`; `npm --prefix frontend run test:webui-assets`; `git diff --check` | pass | - -## 风险 - -仍需真实浏览器确认用户当前聊天视口下的实际展示效果。CSS 修改已限定在 Composer 模型选择器内;如果视觉验证发现额外的层级或裁切父容器,可继续小范围调整。 diff --git a/prd-spec/bugfixes/code-package-upload-validation/2026-10-19-code-package-upload-validation.md b/prd-spec/bugfixes/code-package-upload-validation/2026-10-19-code-package-upload-validation.md deleted file mode 100644 index 2521c0126..000000000 --- a/prd-spec/bugfixes/code-package-upload-validation/2026-10-19-code-package-upload-validation.md +++ /dev/null @@ -1,93 +0,0 @@ -# Code Package Upload Validation - -[中文版](2026-10-19-code-package-upload-validation.zh.md) - -## Metadata - -- Change ID: `code-package-upload-validation` -- Created: 2026-10-19 -- Revised: 2026-10-19 -- Status: implemented -- Related component specs: none changed. This bugfix preserves the existing Studio code package deployment contract and only moves known validation failures earlier in the upload flow. -- User approval: confirmed in chat on 2026-10-19 for three checks: require `app.py` and `requirements.txt` when no Dockerfile is provided, warn about missing `0.0.0.0:8000` listening evidence, and reject Python cache/binary files. - -## Background and Evidence - -Studio's code package upload page currently validates ZIP structure, safe paths, duplicate paths, and whether the declared `agentkit.yaml common.entry_point` file exists. A package with `agentkit.yaml` pointing at `main.py` can pass upload validation. - -For ordinary uploaded packages without a custom Dockerfile, Studio later adds its default Dockerfile. That Dockerfile runs `CMD ["python", "-m", "app"]` and installs `requirements.txt`. Therefore a package that has `main.py` but no root `app.py`, or no root `requirements.txt`, may fail only during cloud build or Runtime startup. This is later than users expect and gives weaker remediation guidance. - -The upload ZIP reader decodes files as text. Python caches such as `__pycache__/` and `*.pyc`, as well as other binary files, are not deployable source files and can corrupt or pollute the browser-provided project. - -## Goals - -- `G-1`: Reject packages that cannot satisfy Studio's default Dockerfile assumptions when no custom `Dockerfile` is present. -- `G-2`: Warn users when the package does not show clear evidence that it listens on `0.0.0.0:8000`. -- `G-3`: Reject Python cache and binary-looking package entries before deployment. -- `G-4`: Provide concise bilingual messages that explain the problem and the recommended fix. - -## Non-Goals - -- Do not change cloud deployment, AgentKit SDK launch behavior, or generated Dockerfile behavior. -- Do not perform full Python, shell, or Dockerfile static analysis. -- Do not add a new UI component or redesign the upload panel. -- Do not update component specs because no public API, route, runtime contract, or ownership boundary changes. - -## Scenarios and Requirements - -- `FR-1`: Given an uploaded package without a root `Dockerfile`, when it lacks root `app.py`, Studio must reject it with a message explaining that the default startup uses `python -m app` and suggesting root `app.py` or a custom Dockerfile. -- `FR-2`: Given an uploaded package without a root `Dockerfile`, when it lacks root `requirements.txt`, Studio must reject it with a message explaining that the default build installs `requirements.txt` and suggesting root `requirements.txt` or a custom Dockerfile. -- `FR-3`: Given an uploaded package containing `__pycache__/`, `*.pyc`, or binary-looking entries, Studio must reject it and tell the user to remove caches and binary build artifacts. -- `FR-4`: Given an uploaded package that otherwise passes validation, when Studio cannot find evidence of listening on `0.0.0.0:8000`, Studio must show a non-blocking warning that explains the Runtime reachability risk and gives a `uvicorn.run` example. -- `FR-5`: Existing ZIP size, file-count, path, duplicate, and `agentkit.yaml` entry point checks must continue to behave as before. - -## Design and Contract Impact - -The change adds browser-side validation in `frontend/src/create/CodePackageCreate.tsx` after ZIP entries are normalized and before the project is accepted for deployment. The checks reuse the normalized root-relative file list that the deployment API already receives. - -Validation policy: - -- Root `Dockerfile` disables the strict `app.py` and `requirements.txt` defaults because the package owns its build and startup commands. -- Without root `Dockerfile`, root `app.py` and root `requirements.txt` are required. -- `__pycache__/`, `*.pyc`, and entries containing replacement characters or NUL bytes after UTF-8 decoding are rejected as cache or binary-like files. -- `0.0.0.0:8000` listening evidence is a warning, not a blocker. Static detection checks package text for `0.0.0.0` plus `8000` or `PORT`, and Dockerfile text for port/listening hints. False negatives are acceptable because the message is advisory. - -Security and privacy: The checks reduce accidental binary/cache uploads and do not inspect external systems or transmit new data. Messages must not echo large file content. - -Compatibility: Packages that previously deployed successfully and include `app.py` and `requirements.txt` are unchanged. Packages relying on a custom Dockerfile remain allowed. Packages that passed upload but would fail under Studio defaults now fail earlier with remediation. - -## Implementation Tasks - -- `T-1`: Add package validation helpers and warning state in `frontend/src/create/CodePackageCreate.tsx`. Covers `FR-1` through `FR-4`. -- `T-2`: Add synchronized English and Chinese strings in `frontend/src/i18n/resources/en-US/create.json` and `frontend/src/i18n/resources/zh-CN/create.json`. Covers `FR-1` through `FR-4`. -- `T-3`: Extend focused frontend tests in `frontend/tests/codePackageDeploy.test.mjs`. Covers `FR-1` through `FR-5`. -- `T-4`: Run targeted frontend tests and record results. Covers all acceptance criteria. - -## Verification and Acceptance - -- `AC-1`: Upload validation rejects missing `app.py` without a Dockerfile and shows a message with the problem and recommendation. -- `AC-2`: Upload validation rejects missing `requirements.txt` without a Dockerfile and shows a message with the problem and recommendation. -- `AC-3`: Upload validation rejects Python cache and binary-like entries. -- `AC-4`: Upload validation can show a non-blocking `0.0.0.0:8000` warning for packages without listening evidence. -- `AC-5`: Existing package upload validations remain covered by tests. - -| Requirement | Task | Acceptance criterion | Test or verification command | Result/evidence | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1`, `T-2`, `T-3` | `AC-1` | `node --test frontend/tests/codePackageDeploy.test.mjs` | pass, 2026-10-19, 12 tests passed | -| `FR-2` | `T-1`, `T-2`, `T-3` | `AC-2` | `node --test frontend/tests/codePackageDeploy.test.mjs` | pass, 2026-10-19, 12 tests passed | -| `FR-3` | `T-1`, `T-2`, `T-3` | `AC-3` | `node --test frontend/tests/codePackageDeploy.test.mjs` | pass, 2026-10-19, 12 tests passed | -| `FR-4` | `T-1`, `T-2`, `T-3` | `AC-4` | `node --test frontend/tests/codePackageDeploy.test.mjs` | pass, 2026-10-19, 12 tests passed | -| `FR-5` | `T-3`, `T-4` | `AC-5` | `npm --prefix frontend test`; `npm --prefix frontend run check:i18n`; `npm --prefix frontend run build` | pass, 2026-10-19. Frontend test: 1389 node tests and 118 Vitest tests passed. i18n: 2 locales and 21 namespaces consistent. Build completed with existing Vite chunk/dynamic-import warnings. | - -Browser checks were not run because this change only adds upload-time validation text and does not introduce a new interaction surface beyond the existing upload panel. Repository pre-commit was not run because no commit was requested. - -## Risks and Open Questions - -- Static listening detection can produce false warnings. This is accepted because the warning is non-blocking and explains the deployment risk. -- A custom Dockerfile can still be invalid in ways the upload page cannot fully detect. This change only adds lightweight preflight hints. - -## Review and Delivery Record - -- Design review: performed directly because no `review-spec` skill is available in this session. The scoped fix is feasible, security impact is positive, compatibility impact is limited to earlier failure of packages that do not satisfy existing default build assumptions, and bilingual requirements are aligned. -- Implementation status: implemented. -- Verification status: pass for targeted code package test, full frontend tests, i18n consistency, and frontend build. `npm --prefix frontend run build` refreshed generated `veadk/webui` assets as expected. diff --git a/prd-spec/bugfixes/code-package-upload-validation/2026-10-19-code-package-upload-validation.zh.md b/prd-spec/bugfixes/code-package-upload-validation/2026-10-19-code-package-upload-validation.zh.md deleted file mode 100644 index 08360e6ac..000000000 --- a/prd-spec/bugfixes/code-package-upload-validation/2026-10-19-code-package-upload-validation.zh.md +++ /dev/null @@ -1,93 +0,0 @@ -# 代码包上传校验 - -[English](2026-10-19-code-package-upload-validation.md) - -## 元数据 - -- Change ID:`code-package-upload-validation` -- 创建日期:2026-10-19 -- 修订日期:2026-10-19 -- 状态:implemented -- 相关组件规格:无变更。本 bugfix 保持现有 Studio 代码包部署契约不变,只把已知部署失败更早暴露在上传流程中。 -- 用户批准:2026-10-19 已在对话中确认三项检查:无 Dockerfile 时要求 `app.py` 和 `requirements.txt`,缺少 `0.0.0.0:8000` 监听证据时提示警告,拒绝 Python 缓存/二进制文件。 - -## 背景和证据 - -Studio 代码包上传页目前会校验 ZIP 结构、安全路径、重复路径,以及 `agentkit.yaml common.entry_point` 声明的文件是否存在。一个在 `agentkit.yaml` 中指向 `main.py` 的代码包可以通过上传校验。 - -对没有自定义 Dockerfile 的普通上传代码包,Studio 后续会补充默认 Dockerfile。该 Dockerfile 执行 `CMD ["python", "-m", "app"]`,并安装 `requirements.txt`。因此只有 `main.py` 但没有根目录 `app.py`,或没有根目录 `requirements.txt` 的代码包,可能要到云构建或 Runtime 启动阶段才失败。这个反馈过晚,修复建议也不够明确。 - -上传 ZIP 读取器会按文本解码文件。`__pycache__/`、`*.pyc` 等 Python 缓存以及其他二进制文件不是可部署源码,可能破坏或污染浏览器提供的项目。 - -## 目标 - -- `G-1`:没有自定义 `Dockerfile` 时,拒绝不满足 Studio 默认 Dockerfile 假设的代码包。 -- `G-2`:代码包没有明确显示监听 `0.0.0.0:8000` 时提示用户。 -- `G-3`:部署前拒绝 Python 缓存和疑似二进制文件。 -- `G-4`:提供简洁的中英文文案,说明问题和建议修复方式。 - -## 非目标 - -- 不改变云端部署、AgentKit SDK launch 行为或生成 Dockerfile 行为。 -- 不做完整 Python、shell 或 Dockerfile 静态分析。 -- 不新增 UI 组件或重新设计上传面板。 -- 不更新组件规格,因为本变更不改变公开 API、路由、Runtime 契约或所有权边界。 - -## 场景和需求 - -- `FR-1`:给定一个没有根目录 `Dockerfile` 的上传包,当它缺少根目录 `app.py` 时,Studio 必须拒绝,并说明默认启动使用 `python -m app`,建议提供根目录 `app.py` 或自定义 Dockerfile。 -- `FR-2`:给定一个没有根目录 `Dockerfile` 的上传包,当它缺少根目录 `requirements.txt` 时,Studio 必须拒绝,并说明默认构建会安装 `requirements.txt`,建议提供根目录 `requirements.txt` 或自定义 Dockerfile。 -- `FR-3`:给定一个包含 `__pycache__/`、`*.pyc` 或疑似二进制条目的上传包,Studio 必须拒绝,并提示移除缓存和二进制构建产物。 -- `FR-4`:给定一个通过其他校验的上传包,当 Studio 未发现监听 `0.0.0.0:8000` 的证据时,Studio 必须展示非阻断警告,说明 Runtime 可访问性风险,并给出 `uvicorn.run` 示例。 -- `FR-5`:既有 ZIP 大小、文件数、路径、重复项和 `agentkit.yaml` 入口校验必须保持原有行为。 - -## 设计和契约影响 - -本变更在 `frontend/src/create/CodePackageCreate.tsx` 中增加浏览器侧校验,位置在 ZIP 条目归一化之后、项目被接受部署之前。校验复用部署 API 已经接收的根目录相对文件列表。 - -校验策略: - -- 根目录 `Dockerfile` 存在时,不强制默认的 `app.py` 和 `requirements.txt`,因为代码包自行负责构建和启动命令。 -- 没有根目录 `Dockerfile` 时,必须存在根目录 `app.py` 和根目录 `requirements.txt`。 -- `__pycache__/`、`*.pyc`,以及 UTF-8 解码后含替换字符或 NUL 字符的条目会作为缓存或疑似二进制文件被拒绝。 -- `0.0.0.0:8000` 监听证据只作为 warning,不作为阻断。静态检测会查找项目文本中的 `0.0.0.0` 以及 `8000` 或 `PORT`,并在 Dockerfile 文本中查找端口/监听线索。因为该提示是建议性质,可以接受误报。 - -安全和隐私:该检查减少意外上传二进制/缓存文件,不访问外部系统,也不传输新数据。文案不得回显大段文件内容。 - -兼容性:已包含 `app.py` 和 `requirements.txt` 且能部署成功的包不受影响。依赖自定义 Dockerfile 的包仍然允许。过去能通过上传但会在 Studio 默认构建下失败的包,现在会更早失败并给出修复建议。 - -## 实现任务 - -- `T-1`:在 `frontend/src/create/CodePackageCreate.tsx` 中增加包校验 helper 和 warning 状态。覆盖 `FR-1` 至 `FR-4`。 -- `T-2`:在 `frontend/src/i18n/resources/en-US/create.json` 和 `frontend/src/i18n/resources/zh-CN/create.json` 中增加同步文案。覆盖 `FR-1` 至 `FR-4`。 -- `T-3`:扩展 `frontend/tests/codePackageDeploy.test.mjs` 的聚焦前端测试。覆盖 `FR-1` 至 `FR-5`。 -- `T-4`:运行目标前端测试并记录结果。覆盖所有验收标准。 - -## 验证和验收 - -- `AC-1`:上传校验拒绝没有 Dockerfile 且缺少 `app.py` 的包,并展示包含问题和建议的文案。 -- `AC-2`:上传校验拒绝没有 Dockerfile 且缺少 `requirements.txt` 的包,并展示包含问题和建议的文案。 -- `AC-3`:上传校验拒绝 Python 缓存和疑似二进制条目。 -- `AC-4`:对缺少监听证据但其他校验通过的包,上传校验可以展示非阻断 `0.0.0.0:8000` 警告。 -- `AC-5`:既有代码包上传校验仍有测试覆盖。 - -| Requirement | Task | Acceptance criterion | Test or verification command | Result/evidence | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1`, `T-2`, `T-3` | `AC-1` | `node --test frontend/tests/codePackageDeploy.test.mjs` | pass,2026-10-19,12 个测试通过 | -| `FR-2` | `T-1`, `T-2`, `T-3` | `AC-2` | `node --test frontend/tests/codePackageDeploy.test.mjs` | pass,2026-10-19,12 个测试通过 | -| `FR-3` | `T-1`, `T-2`, `T-3` | `AC-3` | `node --test frontend/tests/codePackageDeploy.test.mjs` | pass,2026-10-19,12 个测试通过 | -| `FR-4` | `T-1`, `T-2`, `T-3` | `AC-4` | `node --test frontend/tests/codePackageDeploy.test.mjs` | pass,2026-10-19,12 个测试通过 | -| `FR-5` | `T-3`, `T-4` | `AC-5` | `npm --prefix frontend test`; `npm --prefix frontend run check:i18n`; `npm --prefix frontend run build` | pass,2026-10-19。前端测试:1389 个 node 测试和 118 个 Vitest 测试通过。i18n:2 个 locale、21 个 namespace 一致。Build 完成,保留既有 Vite chunk/dynamic-import warning。 | - -未运行浏览器检查,因为本变更只增加上传阶段校验文案,没有引入超出现有上传面板的新交互界面。未运行仓库 pre-commit,因为用户未请求提交。 - -## 风险和开放问题 - -- 静态监听检测可能产生误报。该风险已接受,因为警告不阻断,并会说明部署风险。 -- 自定义 Dockerfile 仍可能存在上传页无法完整检测的问题。本变更只增加轻量预检提示。 - -## 评审和交付记录 - -- 设计评审:本会话没有可用的 `review-spec` skill,因此直接执行评审。该修复范围可行,安全影响为正,兼容性影响仅限于更早拒绝不满足既有默认构建假设的包,中英文需求保持一致。 -- 实现状态:implemented。 -- 验证状态:目标代码包测试、完整前端测试、i18n 一致性检查和前端 build 均通过。`npm --prefix frontend run build` 已按预期刷新生成的 `veadk/webui` 资产。 diff --git a/prd-spec/bugfixes/dependency-e2e-release-gate/2026-10-13-dependency-e2e-release-gate.md b/prd-spec/bugfixes/dependency-e2e-release-gate/2026-10-13-dependency-e2e-release-gate.md deleted file mode 100644 index 8f1d3e3ca..000000000 --- a/prd-spec/bugfixes/dependency-e2e-release-gate/2026-10-13-dependency-e2e-release-gate.md +++ /dev/null @@ -1,95 +0,0 @@ -# Dependency E2E Release Gate - -[中文版](2026-10-13-dependency-e2e-release-gate.zh.md) - -| Field | Value | -| --- | --- | -| Change ID | `dependency-e2e-release-gate` | -| Created | 2026-10-13 | -| Status | approved | -| Component specs | No component contract impact; this change only adds release-time validation for package metadata and dependency resolution. | - -## Background - -The dependency incident showed that local `uv.lock` success is not enough for PyPI users. VeADK can publish metadata that lets pip resolve an unsupported upstream version, or pin a shared base dependency so tightly that a compatible upstream stack cannot be installed. - -## Goals and non-goals - -| Type | Scope | -| --- | --- | -| Goal | Block PyPI publication when the built wheel cannot be installed in a clean environment. | -| Goal | Expose high-risk direct dependency upgrades that conflict with current runtime pins or other package metadata. | -| Goal | Reject high-risk unbounded runtime dependencies and exact pins for shared OpenTelemetry packages. | -| Non-goal | Exhaustively test every optional extra, Studio/docker/sandbox runtime, or real cloud service. | - -## Requirements - -| ID | Requirement | -| --- | --- | -| FR-1 | The tag publish workflow must install the built wheel in a clean venv, run `pip check`, and run a minimal import smoke before uploading to PyPI. | -| FR-2 | The tag publish workflow must test selected high-risk base runtime direct dependencies at their highest currently available versions inside VeADK's declared ranges, together with the built wheel's current runtime pins. | -| FR-3 | The policy check must fail for unbounded `google-adk` and exact `opentelemetry-*` runtime pins. | -| FR-4 | The checks must run on the release artifact, not the developer `uv.lock` environment. | - -## Design - -| Part | Decision | -| --- | --- | -| Script | Add `scripts/check_dependency_e2e.py` with `policy`, `clean-install`, and `declared-range` commands. | -| Workflow | Expose a standalone `dependency-policy-gate` on PRs and pushes; run clean install and declared-range checks only for tag publication or manual workflow dispatch. | -| Dependency scope | Check selected high-risk base dependencies: `google-adk`, `agentkit-sdk-python`, OpenTelemetry packages, `litellm`, `mcp`, `fastmcp`, `tos`, and `jsonschema`; each candidate upgrade is resolved against the full wheel metadata, including exact runtime pins. Optional extras are deferred. | -| Developer guidance | Add repository-level dependency metadata guidelines so developers and coding agents avoid unbounded high-risk dependencies, unjustified shared base pins, and `uv.lock`-only validation before the heavier release E2E runs. | -| Runtime smoke | Import `veadk`, `google.adk`, and `opentelemetry` only; no credentials or external service calls. | - -## Tasks - -| ID | Work | Requirement | -| --- | --- | --- | -| T-1 | Add the dependency E2E script. | FR-1, FR-2, FR-3, FR-4 | -| T-2 | Add unit tests for dependency policy and highest-version selection. | FR-2, FR-3 | -| T-3 | Wire the script into the tag publish workflow and PR-visible dependency policy gate. | FR-1, FR-2, FR-4 | -| T-5 | Run the policy check from pre-commit for dependency metadata edits. | FR-3 | -| T-6 | Document repository-level dependency metadata guidelines for developers and coding agents. | FR-2, FR-3 | -| T-4 | Adjust runtime dependency metadata if the new checks expose an existing conflict. | FR-2, FR-3 | - -## Acceptance - -| Requirement | Task | Criterion | Verification | Result | -| --- | --- | --- | --- | --- | -| FR-1 | T-1, T-3 | Clean wheel install gate exists before PyPI upload. | `uv run --extra dev pytest tests/test_dependency_e2e.py` | pass | -| FR-2 | T-1, T-2, T-3 | Selected high-risk direct dependency highest-version checks are implemented and unit-tested against current runtime pins. | `uv run --extra dev pytest tests/test_dependency_e2e.py`; `uv run python scripts/check_dependency_e2e.py --dist-dir declared-range` | pass | -| FR-3 | T-1, T-2, T-4 | `google-adk` requires an upper bound and `opentelemetry-*` exact pins are rejected. | `uv run python scripts/check_dependency_e2e.py policy` | pass | -| FR-4 | T-3 | Workflow checks `dist/*.whl` instead of `uv.lock`. | workflow diff review | pass | -| FR-3 | T-5 | Local pre-commit runs the same runtime dependency policy check when `pyproject.toml` changes. | `uv run --extra dev pre-commit run --all-files` | pass | -| FR-2, FR-3 | T-6 | `AGENTS.md` documents dependency metadata rules for high-risk upper bounds, shared base pins, direct dependency declarations, and heavyweight E2E deferral. | guideline diff review | pass | - -## Risks - -| Risk | Mitigation | -| --- | --- | -| PyPI state can change after release. | Keep the gate on tag publish and optionally run it manually before tagging. | -| Highest-version installs may be slower than normal unit tests. | Check only selected high-risk dependencies and run the heavy checks only on tag publication or manual workflow dispatch. | - -## Review record - -User approved implementation after reviewing the E2E plan in chat. Direct review found no component-spec change because public SDK behavior and runtime contracts are unchanged. - -## Verification record - -| Date | Command | Result | Notes | -| --- | --- | --- | --- | -| 2026-10-13 | `uv lock` | pass | Refreshed lock metadata after dependency bound updates. | -| 2026-10-13 | `uv run --extra dev pytest tests/test_dependency_e2e.py` | pass | 11 tests passed. | -| 2026-10-13 | `uv run python scripts/check_dependency_e2e.py policy` | pass | Runtime dependency policy passed. | -| 2026-10-13 | `uv run python scripts/check_dependency_e2e.py --dist-dir declared-range` | pass | Validated selected high-risk dependency pins including `google-adk==2.7.1`, `opentelemetry-exporter-otlp==1.42.1`, `opentelemetry-instrumentation-logging==0.63b1`, `fastmcp==3.4.8`, `tos==2.8.7`, and `jsonschema==4.23.0`. | -| 2026-10-13 | `uv run python scripts/check_dependency_e2e.py --dist-dir clean-install` | blocked | Local PyPI download for `google-adk` was too slow and was interrupted; the workflow gate remains wired to run this in GitHub Actions before upload. | - -## Dependency metadata adjustments found by the E2E check - -| Dependency | Before | After | Reason | -| --- | --- | --- | --- | -| `opentelemetry-exporter-otlp` | `>=1.37.0` | `>=1.37.0,<1.43` | `google-adk 2.7.x` caps OpenTelemetry SDK/API at the 1.42 line. | -| `opentelemetry-instrumentation-logging` | `>=0.56b0` | `>=0.56b0,<0.64` | The 0.64+ instrumentation line resolves with newer OpenTelemetry packages than `google-adk 2.7.x` allows. | -| `fastmcp` | `>=2.12.3` | `>=2.12.3,<4` | `fastmcp 4.x` requires `mcp>=2`, while VeADK currently declares `mcp==1.26.0`. | -| `tos` | `>=2.8.4` | `>=2.8.4,<2.9` | `tos 2.9.x` pins `wrapt==1.16.0`, conflicting with VeADK's `wrapt==1.17.2`. | -| `jsonschema` | `>=4.23,<5` | `>=4.23,<4.24` | `litellm 1.83.x` pins `jsonschema==4.23.0`; the previous range overstated installable versions. | diff --git a/prd-spec/bugfixes/dependency-e2e-release-gate/2026-10-13-dependency-e2e-release-gate.zh.md b/prd-spec/bugfixes/dependency-e2e-release-gate/2026-10-13-dependency-e2e-release-gate.zh.md deleted file mode 100644 index df6f9b95f..000000000 --- a/prd-spec/bugfixes/dependency-e2e-release-gate/2026-10-13-dependency-e2e-release-gate.zh.md +++ /dev/null @@ -1,95 +0,0 @@ -# 依赖 E2E 发布门禁 - -[English](2026-10-13-dependency-e2e-release-gate.md) - -| 字段 | 值 | -| --- | --- | -| 变更 ID | `dependency-e2e-release-gate` | -| 创建日期 | 2026-10-13 | -| 状态 | approved | -| 组件规格 | 无组件契约影响;本变更只增加发布期的包元数据和依赖解析校验。 | - -## 背景 - -本次依赖问题说明:本地 `uv.lock` 可用不代表 PyPI 用户可用。VeADK 可能发布出允许 pip 解析到未适配上游版本的元数据,也可能把共享基础依赖 pin 得过死,导致原本兼容的上游依赖栈无法安装。 - -## 目标和非目标 - -| 类型 | 范围 | -| --- | --- | -| 目标 | 发布到 PyPI 前,如果构建出的 wheel 无法在干净环境安装,则阻塞发布。 | -| 目标 | 暴露高风险 direct dependency 升级后与当前 runtime pin 或其它包元数据冲突的问题。 | -| 目标 | 拒绝高风险无上限 runtime 依赖,以及 OpenTelemetry 共享依赖 exact pin。 | -| 非目标 | 不穷举所有 optional extra、Studio/docker/sandbox runtime,且不调用真实云服务。 | - -## 需求 - -| ID | 需求 | -| --- | --- | -| FR-1 | tag 发布 workflow 必须在上传 PyPI 前,将构建出的 wheel 安装到干净 venv,执行 `pip check` 和最小 import smoke。 | -| FR-2 | tag 发布 workflow 必须验证选定高风险 base runtime direct dependency 在 VeADK 声明范围内当前可用最高版本,能否和构建出的 wheel 当前 runtime pin 一起解析。 | -| FR-3 | 策略检查必须拦截无上限 `google-adk` 和 exact pin 的 `opentelemetry-*` runtime 依赖。 | -| FR-4 | 检查必须基于发布产物,而不是开发者的 `uv.lock` 环境。 | - -## 设计 - -| 部分 | 决策 | -| --- | --- | -| 脚本 | 新增 `scripts/check_dependency_e2e.py`,提供 `policy`、`clean-install`、`declared-range` 命令。 | -| Workflow | 在 PR 和 push 上暴露独立的 `dependency-policy-gate`;clean install 和 declared-range 重检查只在 tag 发布或手动 workflow dispatch 时运行。 | -| 依赖范围 | 只检查选定高风险 base 依赖:`google-adk`、`agentkit-sdk-python`、OpenTelemetry packages、`litellm`、`mcp`、`fastmcp`、`tos` 和 `jsonschema`;每个候选升级都会和完整 wheel metadata 一起解析,包括精确 runtime pin。optional extras 后续再扩展。 | -| 开发规范 | 增加仓库级 dependency metadata 规范,要求开发者和 coding agent 在重 E2E 发布门禁前先规避高风险依赖无上限、无理由 shared base pin、只依赖 `uv.lock` 验证等问题。 | -| Runtime smoke | 只 import `veadk`、`google.adk`、`opentelemetry`;不需要凭据,也不访问外部服务。 | - -## 任务 - -| ID | 工作 | 需求 | -| --- | --- | --- | -| T-1 | 新增依赖 E2E 脚本。 | FR-1, FR-2, FR-3, FR-4 | -| T-2 | 增加依赖策略和最高版本选择的单测。 | FR-2, FR-3 | -| T-3 | 将脚本接入 tag 发布 workflow 和 PR 可见的 dependency policy gate。 | FR-1, FR-2, FR-4 | -| T-5 | 在 pre-commit 中对依赖元数据改动执行 policy 检查。 | FR-3 | -| T-6 | 为开发者和 coding agent 记录仓库级 dependency metadata 规范。 | FR-2, FR-3 | -| T-4 | 如果新检查暴露已有依赖冲突,则修正 runtime dependency metadata。 | FR-2, FR-3 | - -## 验收 - -| 需求 | 任务 | 标准 | 验证 | 结果 | -| --- | --- | --- | --- | --- | -| FR-1 | T-1, T-3 | PyPI 上传前存在 clean wheel install 门禁。 | `uv run --extra dev pytest tests/test_dependency_e2e.py` | pass | -| FR-2 | T-1, T-2, T-3 | 已实现并单测覆盖选定高风险 direct dependency 最高声明版本与当前 runtime pin 的兼容检查。 | `uv run --extra dev pytest tests/test_dependency_e2e.py`;`uv run python scripts/check_dependency_e2e.py --dist-dir declared-range` | pass | -| FR-3 | T-1, T-2, T-4 | `google-adk` 必须有上限,`opentelemetry-*` 不允许 exact pin。 | `uv run python scripts/check_dependency_e2e.py policy` | pass | -| FR-4 | T-3 | Workflow 检查 `dist/*.whl`,不依赖 `uv.lock`。 | workflow diff review | pass | -| FR-3 | T-5 | 修改 `pyproject.toml` 时,本地 pre-commit 会执行同一 runtime dependency policy 检查。 | `uv run --extra dev pre-commit run --all-files` | pass | -| FR-2, FR-3 | T-6 | `AGENTS.md` 已记录高风险依赖上限、shared base pin、直接依赖声明和重 E2E 延后执行等 dependency metadata 规则。 | guideline diff review | pass | - -## 风险 - -| 风险 | 缓解 | -| --- | --- | -| PyPI 状态在发布后仍可能变化。 | tag 发布时运行门禁,必要时也可手动预检。 | -| 最高版本安装检查比普通单测慢。 | 只检查选定高风险依赖,且重检查只在 tag 发布或手动 workflow dispatch 时运行。 | - -## 评审记录 - -用户在会话中审阅 E2E 方案后批准开始实现。直接评审结论:本变更不改变公开 SDK 行为和运行时契约,因此无需更新组件 spec。 - -## 验证记录 - -| 日期 | 命令 | 结果 | 说明 | -| --- | --- | --- | --- | -| 2026-10-13 | `uv lock` | pass | 依赖边界调整后刷新 lock metadata。 | -| 2026-10-13 | `uv run --extra dev pytest tests/test_dependency_e2e.py` | pass | 11 个测试通过。 | -| 2026-10-13 | `uv run python scripts/check_dependency_e2e.py policy` | pass | Runtime dependency policy 通过。 | -| 2026-10-13 | `uv run python scripts/check_dependency_e2e.py --dist-dir declared-range` | pass | 已验证选定高风险依赖的声明范围最高版本,包括 `google-adk==2.7.1`、`opentelemetry-exporter-otlp==1.42.1`、`opentelemetry-instrumentation-logging==0.63b1`、`fastmcp==3.4.8`、`tos==2.8.7` 和 `jsonschema==4.23.0`。 | -| 2026-10-13 | `uv run python scripts/check_dependency_e2e.py --dist-dir clean-install` | blocked | 本地下载 `google-adk` 过慢后中断;workflow 已接入该门禁,会在 GitHub Actions 上传前执行。 | - -## E2E 暴露出的依赖元数据调整 - -| 依赖 | 调整前 | 调整后 | 原因 | -| --- | --- | --- | --- | -| `opentelemetry-exporter-otlp` | `>=1.37.0` | `>=1.37.0,<1.43` | `google-adk 2.7.x` 将 OpenTelemetry SDK/API 限制在 1.42 线。 | -| `opentelemetry-instrumentation-logging` | `>=0.56b0` | `>=0.56b0,<0.64` | 0.64+ instrumentation 会解析到 `google-adk 2.7.x` 不允许的新版 OpenTelemetry 包。 | -| `fastmcp` | `>=2.12.3` | `>=2.12.3,<4` | `fastmcp 4.x` 要求 `mcp>=2`,但 VeADK 当前声明 `mcp==1.26.0`。 | -| `tos` | `>=2.8.4` | `>=2.8.4,<2.9` | `tos 2.9.x` pin 了 `wrapt==1.16.0`,与 VeADK 的 `wrapt==1.17.2` 冲突。 | -| `jsonschema` | `>=4.23,<5` | `>=4.23,<4.24` | `litellm 1.83.x` pin 了 `jsonschema==4.23.0`;原范围夸大了可安装版本。 | diff --git a/prd-spec/bugfixes/frontend-lockfile-sync/2026-10-09-restore-esbuild-lock-entries.md b/prd-spec/bugfixes/frontend-lockfile-sync/2026-10-09-restore-esbuild-lock-entries.md deleted file mode 100644 index 199ba6506..000000000 --- a/prd-spec/bugfixes/frontend-lockfile-sync/2026-10-09-restore-esbuild-lock-entries.md +++ /dev/null @@ -1,85 +0,0 @@ -# Restore Frontend esbuild Lock Entries - -[中文版](2026-10-09-restore-esbuild-lock-entries.zh.md) - -## Metadata - -- Change ID: `frontend-lockfile-sync` -- Created: 2026-10-09 -- Revised: 2026-10-10 -- Status: implemented -- Related component specs: none; this change restores generated dependency metadata without changing component contracts - -## Background and Evidence - -Pull request #23 fails the Harness Sidecar Release Gate during `npm ci --ignore-scripts`. The CI log reports that `esbuild@0.28.2` and its platform packages are missing from `frontend/package-lock.json`. - -`frontend/package.json` is identical to `origin/main`. Commit `3f134207` removed 512 lock-file lines, including Vitest's nested esbuild package and platform-specific optional packages, without changing dependency declarations. The confirmed root cause is an incomplete generated lock file. - -## Goals and Non-goals - -### Goals - -- Restore a lock file that is complete for supported CI platforms. -- Make `npm ci --ignore-scripts` and the affected frontend release gate pass. -- Preserve all declared dependency versions and product behavior. - -### Non-goals - -- Change frontend dependencies or version ranges. -- Change Studio UI, runtime behavior, or harness-sidecar contracts. -- Modify unrelated PR #23 implementation files. - -## Scenarios and Requirements - -### Scenario - -Given the unchanged `frontend/package.json`, when CI runs `npm ci --ignore-scripts` on Linux, then npm must resolve the exact lock-file graph without reporting missing esbuild packages. - -- `FR-1`: `frontend/package-lock.json` must contain the dependency graph generated for the current `frontend/package.json`. -- `FR-2`: The fix must not change `frontend/package.json` or component behavior. -- `FR-3`: The affected frontend gate, normal frontend tests, and production build must pass locally. - -## Design and Contract Impact - -Restore `frontend/package-lock.json` to the complete `origin/main` version because `frontend/package.json` is unchanged and the branch-only lock diff consists solely of accidental deletions. - -- Interfaces and state: not applicable; no source contract changes. -- Concurrency and permissions: not applicable. -- Security: no credentials or external inputs are added. -- Compatibility: restores Linux CI compatibility while preserving dependency declarations. -- Component specs: no impact; the change corrects generated package metadata only. -- User documentation: no impact. - -Alternative rejected: running a broad dependency upgrade. It would create unrelated version churn and does not address the confirmed accidental deletion narrowly. - -## Implementation Tasks - -- `T-1` (`FR-1`, `FR-2`): restore `frontend/package-lock.json` from `origin/main`. -- `T-2` (`FR-3`): run the affected installation, coverage, test, and build commands. -- `T-3`: review the final diff for unrelated changes and record verification evidence. - -## Verification and Acceptance - -| Requirement | Task | Acceptance criterion | Verification | Result | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1` | `AC-1`: npm accepts the lock file | `npm --prefix frontend ci --ignore-scripts` | pass: 695 packages installed | -| `FR-2` | `T-1`, `T-3` | `AC-2`: dependency declarations and component contracts are unchanged | `git diff origin/main -- frontend/package.json` and diff review | pass: no package declaration or contract diff | -| `FR-3` | `T-2` | `AC-3`: affected coverage gate passes | `npm --prefix frontend run test:harness-sidecar-coverage` | pass: 22 tests | -| `FR-3` | `T-2` | `AC-4`: frontend tests pass | `npm --prefix frontend test` | pass: 1,377 Node tests and 65 Vitest tests | -| `FR-3` | `T-2` | `AC-5`: production build passes | `npm --prefix frontend run build` | pass | - -## Risks and Open Questions - -- `npm run build` may regenerate tracked web assets. Any generated diff must be reviewed and retained only if it is required by the current source. -- No open design questions remain. - -## Review and Delivery Record - -- 2026-10-09: Root cause confirmed from PR #23 CI logs and the branch diff. -- 2026-10-09: Design review found no contract, security, compatibility, or testability blocker. -- 2026-10-09: The user approved restoring the complete lock file and running the frontend gates. -- 2026-10-09: Restored the 512 deleted lock-file lines from `origin/main`; installation, affected coverage, complete frontend tests, and production build passed on diff scope `d3370fd1` plus this working-tree fix. -- 2026-10-09: `npm ci` reported existing dependency audit findings (6 low, 3 moderate, 6 high, 1 critical); dependency remediation is outside this lock restoration. -- 2026-10-10: Commit `5644e146` reintroduced the same 512-line deletion. Restored the lock file from current `upstream/main`; `npm ci --ignore-scripts`, 1,377 Node tests, 67 Vitest tests, 22 Sidecar coverage tests, and both production builds passed. -- Commit, push, and PR updates remain separately authorized operations. diff --git a/prd-spec/bugfixes/frontend-lockfile-sync/2026-10-09-restore-esbuild-lock-entries.zh.md b/prd-spec/bugfixes/frontend-lockfile-sync/2026-10-09-restore-esbuild-lock-entries.zh.md deleted file mode 100644 index d5f42cec0..000000000 --- a/prd-spec/bugfixes/frontend-lockfile-sync/2026-10-09-restore-esbuild-lock-entries.zh.md +++ /dev/null @@ -1,85 +0,0 @@ -# 恢复前端 esbuild 锁文件条目 - -[English](2026-10-09-restore-esbuild-lock-entries.md) - -## 元数据 - -- 变更 ID:`frontend-lockfile-sync` -- 创建日期:2026-10-09 -- 修订日期:2026-10-10 -- 状态:implemented -- 相关组件规格:无;本变更仅恢复生成的依赖元数据,不改变组件契约 - -## 背景与证据 - -PR #23 在 Harness Sidecar Release Gate 的 `npm ci --ignore-scripts` 阶段失败。CI 日志报告 `frontend/package-lock.json` 缺少 `esbuild@0.28.2` 及其平台包。 - -`frontend/package.json` 与 `origin/main` 完全一致。提交 `3f134207` 在未修改依赖声明的情况下删除了 512 行锁文件内容,其中包括 Vitest 嵌套的 esbuild 包和平台专属可选包。已确认的根因是生成的锁文件不完整。 - -## 目标与非目标 - -### 目标 - -- 恢复对受支持 CI 平台完整的锁文件。 -- 使 `npm ci --ignore-scripts` 和受影响的前端发布 gate 通过。 -- 保持所有声明的依赖版本和产品行为不变。 - -### 非目标 - -- 修改前端依赖或版本范围。 -- 修改 Studio UI、运行时行为或 harness-sidecar 契约。 -- 修改 PR #23 中无关的实现文件。 - -## 场景与需求 - -### 场景 - -给定未变化的 `frontend/package.json`,当 CI 在 Linux 上运行 `npm ci --ignore-scripts` 时,npm 必须能够解析精确的锁文件依赖图,并且不报告缺少 esbuild 包。 - -- `FR-1`:`frontend/package-lock.json` 必须包含当前 `frontend/package.json` 对应的完整依赖图。 -- `FR-2`:修复不得改变 `frontend/package.json` 或组件行为。 -- `FR-3`:受影响的前端 gate、常规前端测试和生产构建必须在本地通过。 - -## 设计与契约影响 - -将 `frontend/package-lock.json` 恢复为完整的 `origin/main` 版本,因为 `frontend/package.json` 未变化,且分支独有的锁文件差异仅包含意外删除。 - -- 接口与状态:不适用;没有源码契约变更。 -- 并发与权限:不适用。 -- 安全:不新增凭据或外部输入。 -- 兼容性:恢复 Linux CI 兼容性,同时保持依赖声明不变。 -- 组件规格:无影响;本变更只修正生成的包元数据。 -- 用户文档:无影响。 - -已拒绝的替代方案:执行广泛的依赖升级。该方案会产生无关的版本变动,且不能精确解决已确认的意外删除。 - -## 实施任务 - -- `T-1`(`FR-1`、`FR-2`):从 `origin/main` 恢复 `frontend/package-lock.json`。 -- `T-2`(`FR-3`):运行受影响的安装、覆盖率、测试和构建命令。 -- `T-3`:检查最终差异是否存在无关变更,并记录验证证据。 - -## 验证与验收 - -| 需求 | 任务 | 验收标准 | 验证命令 | 结果 | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1` | `AC-1`:npm 接受锁文件 | `npm --prefix frontend ci --ignore-scripts` | pass:安装 695 个包 | -| `FR-2` | `T-1`、`T-3` | `AC-2`:依赖声明和组件契约未变化 | `git diff origin/main -- frontend/package.json` 和差异审查 | pass:无依赖声明或契约差异 | -| `FR-3` | `T-2` | `AC-3`:受影响的覆盖率 gate 通过 | `npm --prefix frontend run test:harness-sidecar-coverage` | pass:22 项测试 | -| `FR-3` | `T-2` | `AC-4`:前端测试通过 | `npm --prefix frontend test` | pass:1,377 项 Node 测试和 65 项 Vitest 测试 | -| `FR-3` | `T-2` | `AC-5`:生产构建通过 | `npm --prefix frontend run build` | pass | - -## 风险与待决问题 - -- `npm run build` 可能重新生成已跟踪的 Web 资产。必须审查所有生成差异,只保留当前源码确实需要的内容。 -- 没有待决设计问题。 - -## 审查与交付记录 - -- 2026-10-09:根据 PR #23 CI 日志和分支差异确认根因。 -- 2026-10-09:设计审查未发现契约、安全、兼容性或可测试性阻塞项。 -- 2026-10-09:用户批准恢复完整锁文件并运行前端 gate。 -- 2026-10-09:从 `origin/main` 恢复被删除的 512 行锁文件内容;在 `d3370fd1` 加本工作区修复的差异范围内,依赖安装、受影响的覆盖率、完整前端测试和生产构建均通过。 -- 2026-10-09:`npm ci` 报告既有依赖审计问题(6 个 low、3 个 moderate、6 个 high、1 个 critical);依赖修复不属于本次锁文件恢复范围。 -- 2026-10-10:提交 `5644e146` 再次引入相同的 512 行删除。从当前 `upstream/main` 恢复锁文件;`npm ci --ignore-scripts`、1,377 项 Node 测试、67 项 Vitest 测试、22 项 Sidecar coverage 测试和两项生产构建均通过。 -- 提交、推送和更新 PR 仍需单独授权。 diff --git a/prd-spec/bugfixes/history-turn-identity/2026-09-17-replay.md b/prd-spec/bugfixes/history-turn-identity/2026-09-17-replay.md deleted file mode 100644 index f3b6a182b..000000000 --- a/prd-spec/bugfixes/history-turn-identity/2026-09-17-replay.md +++ /dev/null @@ -1,13 +0,0 @@ -# Preserve historical conversation order - -[中文](2026-09-17-replay.zh.md) - -Approved by the user on 2026-09-17. Evidence: replaying question 1, answer 1, question 2, answer 2 produces question 1, answer 2, question 2. Every user boundary resets the projector counter under the same `adk-history` prefix; global turn upsert replaces the earlier answer. - -Requirement: preserve all historical replies and their order, including multiple authors and final replies per user turn. Scope: `eventsToTurns` and regression tests. Give each newly created history projector a prefix based on the accumulated turn count, retaining its per-projector sequence. Do not change stored events, backend APIs, stream behavior, or rendering style. No component contract changes: this restores existing history fidelity. - -Plan: reproduce with a failing multi-turn regression, change only the history projector prefix, test missing invocation IDs and multiple replies, measure changed-line coverage above 95%, run frontend regression/build/assets and pre-commit, then reload the real historical session locally. Review performed directly: uniqueness holds because each reset follows insertion of a user turn; tool responses continue using author-based classification. No new dependencies or data migration. - -Verification: 1102 frontend tests and 6 coverage tests passed; modified production statement coverage 100%. Build, 102 bundled files / 246 references, pre-commit and diff whitespace checks passed. No backend or Runtime image changes. - -Live browser pass: reopened the reported Runtime session `231046f6-789c-4e40-bda6-d687224857ea`; both answers remain after their respective questions, and the conversation ends with the second answer. Local Studio rebuilt and restarted. diff --git a/prd-spec/bugfixes/history-turn-identity/2026-09-17-replay.zh.md b/prd-spec/bugfixes/history-turn-identity/2026-09-17-replay.zh.md deleted file mode 100644 index 73f76e96f..000000000 --- a/prd-spec/bugfixes/history-turn-identity/2026-09-17-replay.zh.md +++ /dev/null @@ -1,13 +0,0 @@ -# 保持历史对话顺序 - -[English](2026-09-17-replay.md) - -用户于 2026-09-17 批准。复现:问题一、回答一、问题二、回答二被渲染成问题一、回答二、问题二。每条用户消息都重建 projector,在相同 `adk-history` 前缀下重置计数器,全局更新因此覆盖前面的回答。 - -要求:保留历史回复及顺序,包括同轮多个作者和多个最终回复。范围:`eventsToTurns` 与回归测试。新建历史 projector 时使用当前已累计的 turn 数作为前缀,保留 projector 内部序号。不修改存储事件、后端接口、流式处理或视觉样式。无组件契约变更:恢复已有历史忠实展示语义。 - -计划:先复现多轮用例失败,仅修改历史 projector 前缀,验证缺少 invocation ID 和多回复场景,新增行覆盖率超过 95%,执行前端回归、构建、资源检查和 pre-commit,最后在本地重新打开真实历史会话。已直接完成设计审查:每次重建前都会插入用户 turn,故计数保证唯一;工具响应仍按 author 分类。不增加依赖或迁移数据。 - -验证:前端 1102 个测试、覆盖率测试 6 个通过;修改的生产代码语句覆盖率 100%。构建、102 个打包文件 / 246 个引用、pre-commit 和 diff 空白检查均通过。不涉及后端或 Runtime 镜像变更。 - -浏览器实测通过:重新打开问题对应的 Runtime 会话 `231046f6-789c-4e40-bda6-d687224857ea`,两个回答分别保留在各自问题之后,会话以第二个回答结束。本地 Studio 已重建并重启。 diff --git a/prd-spec/bugfixes/mpa-a2a-runtime-picker/2026-10-22-single-runtime-picker.md b/prd-spec/bugfixes/mpa-a2a-runtime-picker/2026-10-22-single-runtime-picker.md deleted file mode 100644 index 5508b47cd..000000000 --- a/prd-spec/bugfixes/mpa-a2a-runtime-picker/2026-10-22-single-runtime-picker.md +++ /dev/null @@ -1,53 +0,0 @@ -# One required Runtime picker for A2A registration - -[中文版](2026-10-22-single-runtime-picker.zh.md) - -ID: mpa-a2a-single-runtime-picker. Date/revision: 2026-10-22. Status: approved. -Related: [component](../../../specs/mpa-collaborating-agents/README.md), [endpoint design](../../features/mpa-collaborating-agents/2026-10-22-endpoints-and-skills.md). - -## Evidence, goals and scope - -A2ARegistration renders both a Runtime Select and a required Runtime ID input bound to the same form.runtimeId. The user identified the duplicate field and approved: “按此调整”. The manual field originally bypassed list pagination/read failures; the form already supports pagination and retry. Remove duplicate input while preserving selection, endpoint loading and registration payload. - -FR-1: Always show one required existing-Runtime Select, using authorized paginated Runtime list values. Do not render a separate Runtime ID textbox. Keep IDs internally and in existing option labels/detail views. Source Runtime registration is blocked until an option still present in the current list and its matching endpoint are available. Switching selections cancels/discards stale endpoint reads. A selection removed by a list replacement must not remain submittable. -FR-2: Distinguish initial loading, successful empty list, and read failure. Empty/error states offer list retry; pagination appends/deduplicates choices and preserves selection. Remove manual-ID instructions in both locales. Keep keyboard navigation, Escape/cancel/focus return and Standard JSON/URL input/IME behavior. -FR-3: Preserve POST body RuntimeId/NetworkType, backend authorization, public API compatibility and uncertainty handling. No backend, Runtime, IAM, dependency or database change; no real cloud writes, commit or push. - -## Design, tasks and acceptance - -Reuse Select/FormLabel/Button/TextShimmer and existing form layout. Move required semantics to the Runtime picker; focus the first visible source control on form mount. Derive sourceRuntimeId only from a listed selection, so list replacement invalidates old endpoints and submit state. Keep source-specific API union unchanged. CON-RUNTIME-PICKER is a UI contract refinement; endpoint and backend contracts are unchanged. - -| Requirement | Task | Acceptance | Tests | -| --- | --- | --- | --- | -| FR-1 | T-1: failing regression then remove input and gate listed selection | AC-1: no ID textbox; picker required; selected ID sent automatically; stale/missing selection cannot submit | frontend/tests/collaboratingAgents.test.tsx | -| FR-2 | T-2: loading/empty/error/pagination and keyboard regression; locales | AC-2: states distinct and retryable, pagination preserves choice, no manual-ID hint, Standard IME preserved | UI tests; real browser with mocked API, including narrow layout | -| FR-3 | T-3: docs, types, build/assets and review | AC-3: frontend tests/build/i18n/assets/tsc and scoped secret/doc checks pass; no backend diff added | repository frontend gates; git diff --check | - -## Review, approval and risks - -No review-spec skill is available. Direct review covers feasibility, component ownership, state/cancellation, errors, security, compatibility, testability and English/Chinese equivalence; no blockers. User approved the stated single-picker plan before code/test edits. Existing frontend-design/ui-ux-pro-max guidance applies to feedback and component reuse; no visual redesign. Removing manual entry means unavailable Runtime lists must be retried rather than bypassed. This is intentional approved scope. Provider eligibility and last-moment authorization still apply server-side. Whole Python regression and Ruff/Pyright are not applicable to this frontend-only delta; existing unrelated dirty changes are preserved. - -## Verification - -Date: 2026-10-22. Scope: incremental frontend-only change on existing uncommitted A2A Center work above 9ab0ef2d. All checks not_run until executed. Live registration/deletion not_run because verification must be read-only or isolated mocks. Results will be recorded below. - - -## Implementation and verification record (2026-10-22) - -Production/UI implementation is complete. T-1/T-2/T-3 code and verification changes are present locally, with no backend/API changes in this incremental scope. Four new regression cases failed before implementation; all 40 collaborating-Agent tests now pass. Standard import IME coverage was moved to its actual text input after removing the Runtime text field. Review verified required semantics, selection-to-ID mapping, empty/error/loading states, append pagination, stale endpoint invalidation, focus and cancellation. No in-scope code findings remain. - -| Check | Result | Evidence | -| --- | --- | --- | -| `npm --prefix frontend test` | pass | 1389 Node tests; 187 Vitest cases in 11 files, including 40 collaborating-Agent cases. | -| `npm --prefix frontend run build` | pass | Matching packaged assets generated; existing large-chunk advisory remains. | -| `npm --prefix frontend run check:i18n` | pass | 2 locales / 22 namespaces. | -| `npm --prefix frontend run test:webui-assets` | pass | 114 packaged files / 351 references. | -| `frontend/node_modules/.bin/tsc --noEmit -p frontend/tsconfig.json` | pass | No diagnostics. | -| Browser with isolated mock fetch | pass | One required picker, no manual ID input; automatic endpoint loading; retry after list failure; confirmed empty state; keyboard selection and cancel/focus return. At 390px the form clientWidth and scrollWidth both measured 342px. Fixture files, Vite and tab removed. | -| Scoped gitleaks using repository config; paired-document links/IDs; `git diff --check` | pass | No secrets found. Preserve configured scanner exclusions. | -| Python tests/Ruff/Pyright | not_applicable | This incremental fix changes no Python implementation or tests. Prior auth/endpoint verification remains in its own design. | -| Commit gates and live cloud writes | not_run | No commit, push or cloud registration/deletion authorized. | -| Local startup / current built entry | pass | Studio serves the current index and JavaScript asset, HTTP 200. | -| Cloud-account environment after local restart | blocked | The agent gracefully restarted Studio to clear its cached old index, but the replacement process did not inherit the original cloud AK/SK environment. Local .env has no matching credentials. Read-only space lookup now returns 409. This is an agent-caused operational regression, not a cloud IAM refusal. No credentials were printed, persisted or replaced, and no alternate CLI profile was selected. User has been asked for the original startup/configuration path or to restore the original terminal environment; recovery is pending. | - -Lifecycle remains approved until the startup environment regression is recovered and verified. Do not claim full task completion. The UI implementation and tests are reviewable, but cloud access is currently unavailable. All prior unrelated changes are retained. No commit/push/Runtime deployment was performed. diff --git a/prd-spec/bugfixes/mpa-a2a-runtime-picker/2026-10-22-single-runtime-picker.zh.md b/prd-spec/bugfixes/mpa-a2a-runtime-picker/2026-10-22-single-runtime-picker.zh.md deleted file mode 100644 index d15cbd471..000000000 --- a/prd-spec/bugfixes/mpa-a2a-runtime-picker/2026-10-22-single-runtime-picker.zh.md +++ /dev/null @@ -1,53 +0,0 @@ -# A2A 注册仅保留一个必选 Runtime 控件 - -[English](2026-10-22-single-runtime-picker.md) - -ID:mpa-a2a-single-runtime-picker。日期/修订:2026-10-22。状态:approved。 -关联:[组件](../../../specs/mpa-collaborating-agents/README.zh.md)、[端点设计](../../features/mpa-collaborating-agents/2026-10-22-endpoints-and-skills.zh.md)。 - -## 证据、目标与范围 - -A2ARegistration 同时渲染 Runtime Select 和必填 Runtime ID 输入框,共用 form.runtimeId。用户指出字段重复并批准“按此调整”。手动字段原用于绕过列表分页/读取失败,但表单已有分页和重试。删除重复输入,同时保留选择、端点加载和注册 Payload。 - -FR-1:始终显示一个必选的已有 Runtime Select,选项来自已授权的分页 Runtime 列表,不再渲染独立 Runtime ID 文本框。ID 仍用于内部值以及已有选项标签/详情。Runtime 来源必须选择仍存在于当前列表的选项,并获得匹配端点后才允许注册。切换选择取消/丢弃旧端点读取。替换列表后已不存在的选择不得继续提交。 -FR-2:区分首次加载、成功空列表和读取失败。空/错误状态提供列表重试;分页追加/去重选项并保留选择。两种语言移除手动 ID 提示。保留键盘导航、Escape/取消/焦点返回,以及 Standard JSON/URL 输入与 IME 行为。 -FR-3:保留 POST 的 RuntimeId/NetworkType、后端授权、公共 API 兼容与结果不明处理。不改后端、Runtime、IAM、依赖或数据库;不执行真实云写入、提交或推送。 - -## 设计、任务与验收 - -复用 Select/FormLabel/Button/TextShimmer 和已有表单布局。将必填语义移到 Runtime 选择控件;表单挂载时聚焦首个可见来源控件。sourceRuntimeId 仅从已列出的选择推导,使列表替换能失效旧端点和提交状态。来源 API 联合类型不变。CON-RUNTIME-PICKER 是 UI 合同细化,端点及后端合同不变。 - -| 需求 | 任务 | 验收 | 测试 | -| --- | --- | --- | --- | -| FR-1 | T-1:先补失败回归,再删输入并限制已列出选择 | AC-1:无 ID 文本框,选择控件必选,自动提交所选 ID,迟到/缺失选择不能提交 | frontend/tests/collaboratingAgents.test.tsx | -| FR-2 | T-2:加载/空/错误/分页及键盘回归,更新语言 | AC-2:状态明确且可重试,分页保留选择,无手动 ID 提示,保留 Standard IME | UI 测试;模拟 API 的真实浏览器,含窄屏 | -| FR-3 | T-3:文档、类型、构建/产物与评审 | AC-3:前端测试/构建/i18n/assets/tsc、范围内密钥/文档检查通过,无新增后端差异 | 仓库前端门禁;git diff --check | - -## 评审、批准和风险 - -没有 review-spec 技能,已直接评审可行性、组件归属、状态/取消、错误、安全、兼容、可测试性和双语等价,无阻塞。用户在代码/测试修改前批准上述单一选择方案。沿用 frontend-design/ui-ux-pro-max 的反馈和组件复用指导,不重新设计视觉。移除手动入口后,Runtime 列表不可用时必须重试,不能绕过,这是已批准范围。提供商资格和最终授权仍由服务端执行。全量 Python 回归及 Ruff/Pyright 对此次纯前端增量为 not_applicable;保留已有无关未提交修改。 - -## 验证 - -日期:2026-10-22。范围:9ab0ef2d 之上已有未提交 A2A 中心工作的纯前端增量。执行前检查均 not_run。真实注册/删除为 not_run,验证仅只读或隔离模拟。结果另记于下方。 - - -## 实现与验证记录(2026-10-22) - -生产/UI 实现已完成,T-1/T-2/T-3 代码与验证变更在本地,此次增量未改后端/API。实现前 4 项新增回归失败;当前协作 Agent 40 项均通过。移除 Runtime 文本字段后,将 Standard 导入 IME 覆盖移至其真实文本输入。评审核对必填语义、选择到 ID 的映射、空/错误/加载状态、追加分页、旧端点失效、焦点和取消,无范围内代码发现。 - -| 检查 | 结果 | 证据 | -| --- | --- | --- | -| `npm --prefix frontend test` | pass | 1389 项 Node;11 文件 187 项 Vitest,其中协作 Agent 40 项。 | -| `npm --prefix frontend run build` | pass | 已生成匹配产物;保留既有大分块提示。 | -| `npm --prefix frontend run check:i18n` | pass | 2 语言 / 22 命名空间。 | -| `npm --prefix frontend run test:webui-assets` | pass | 114 产物文件 / 351 引用。 | -| `frontend/node_modules/.bin/tsc --noEmit -p frontend/tsconfig.json` | pass | 无诊断。 | -| 隔离模拟 fetch 的真实浏览器 | pass | 单一必选控件,无手动 ID 输入;自动加载端点;列表失败后重试;确认空状态;键盘选择和取消/焦点返回。390px 下表单 clientWidth 与 scrollWidth 均为 342px。夹具、Vite 和标签已移除。 | -| 仓库配置的范围内 gitleaks;双语文档链接/编号;`git diff --check` | pass | 未发现秘密;保留既有扫描排除项。 | -| Python 测试/Ruff/Pyright | not_applicable | 此增量未改 Python 实现或测试;先前认证/端点验证留在所属设计。 | -| 提交门禁和真实云写入 | not_run | 未授权提交、推送或云注册/删除。 | -| 本机启动/当前构建入口 | pass | Studio 页面与当前 JavaScript 资源均 HTTP 200。 | -| 本机重启后的云账号环境 | blocked | Agent 为清除缓存旧入口优雅重启了 Studio,但新进程未继承原云 AK/SK 环境;本地 .env 无对应凭据,空间只读查询现返回 409。这是 Agent 引入的运行环境回归,不是云 IAM 拒绝。未输出、落盘或替换凭据,未代选其他 CLI profile。已询问用户原启动/配置路径,或请其用原终端环境恢复,目前待恢复。 | - -运行环境回归恢复并验证前,生命周期保持 approved,不宣称全部完成。UI 实现和测试可评审,但当前云访问不可用。保留全部原有无关修改。未提交、推送或部署 Runtime。 diff --git a/prd-spec/bugfixes/mpa-a2a-space-deletion/2026-10-22-runtime-auth.md b/prd-spec/bugfixes/mpa-a2a-space-deletion/2026-10-22-runtime-auth.md deleted file mode 100644 index c6fe8e33e..000000000 --- a/prd-spec/bugfixes/mpa-a2a-space-deletion/2026-10-22-runtime-auth.md +++ /dev/null @@ -1,58 +0,0 @@ -# A2A space deletion Runtime authentication - -[中文版](2026-10-22-runtime-auth.zh.md) - -Change ID: mpa-a2a-space-deletion-runtime-auth. Created/revised: 2026-10-22. Status: implemented. -Related: [component](../../../specs/mpa-collaborating-agents/README.md), [preceding feature](../../features/mpa-collaborating-agents/2026-10-22-member-actions.md). - -## Evidence, scope and requirements - -The new space DELETE reads the target binding through `_runtime_json_request`, which omits `X-MPA-Studio-Key`. The existing dedicated collaborator proxy supplies this server-owned credential; the MPA binding endpoint requires it. An isolated reproduction returned 401 before any cloud call. A read through the existing proxy succeeded. This establishes an implementation defect; the original failed deletion's raw response was not captured. - -FR-1: Apply existing `collaborator_management_headers` policy to Runtime JSON requests for collaborator paths. Use the authorized server Runtime key, strip browser-supplied credentials, enforce administrator access and reject missing keys before upstream I/O. Unrelated JSON paths remain unchanged. -FR-2: A 401/403 from the binding preflight returns a bounded `runtime_auth_failed` reason with the same status. The UI states Runtime authentication failed and deletion was not performed. Cloud refusals retain the existing permission message. No raw upstream error is returned for this preflight auth failure, no automatic retry and no cloud operation after any failed preflight. -FR-3: Retain bound/nonempty/identity checks, timeout/uncertainty and cancellation behavior. Do not delete a real space, change cloud IAM, deploy a Runtime, commit or push as part of verification. - -## Design and affected files - -Reuse `veadk/integrations/mpa/collaborator_proxy.py` from `_runtime_json_request` in `veadk/cli/cli_frontend.py`. No new key parameter, user-supplied header trust, dependency, database state or API route. Scope the new reason to binding reads in `frontend/server/a2a_center.py`. Add the reason to `frontend/src/adk/a2aSpace.ts`, map it in `A2AResourceActions.tsx` and provide equivalent en-US/zh-CN messages. Preserve ModalButton, alert/focus/retry controls and current layout. Update the component spec, frontend README and generated assets. SDK public contracts, generated projects, Runtime/Harness code and dispatch are unaffected. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| FR-1 | T-1: failing real Studio route/MockTransport regression then header fix | AC-1: binding receives server key instead of browser forgery and an empty unbound fixture can reach simulated cloud deletion | tests/cli/test_frontend_runtime_proxy.py | -| FR-2 | T-2: preflight error and UI regressions then reason mapping | AC-2: binding auth refusal makes zero cloud calls; UI shows Runtime failure, remains retryable; cloud 403 retains its message | tests/cli/test_a2a_center_actions.py; frontend/tests/collaboratingAgents.test.tsx | -| FR-3 | T-3: regression, security and delivery review | AC-3: existing checks, authorization, redaction, cancellation and unrelated helper callers remain intact | targeted Python; npm test/build; i18n/assets/tsc; changed Python Ruff/Pyright; browser with mocked APIs; secret scan; git diff --check | - -## Risks, review and approval - -Shared helper changes must be route-scoped by the existing policy. Broadly adding credentials to unrelated paths is prohibited. A separate cloud IAM refusal may remain after authentication is fixed; it must be reported as such. Unit/mock checks do not prove a live cloud DELETE succeeds. Backend activation requires restarting the current local Studio; this must not interrupt active user work without checking. - -The available skill catalog has no review-spec skill. Direct review covered contract ownership, error boundaries, server-owned credentials, compatibility, feasibility, negative cases and bilingual equivalence; no blockers remain. User approved the proposed authentication/error distinction fix with “能修复吗” before production/test edits. Additional endpoint/skill requirements are a separate scope confirmation and are not silently approved by this record. - -## Verification record - -Tested scope: current uncommitted A2A Center diff on branch feat/from-upstream-main-20261020; baseline HEAD 9ab0ef2d. Date: 2026-10-22. Final checks are recorded in the delivery evidence. No live cloud writes. - - -## Delivery evidence (2026-10-22) - -Implemented the approved scope against the existing uncommitted A2A changes above 9ab0ef2d. Regression-first evidence: 22 selected Python and 8 UI cases failed before implementation. Review resolved stale endpoint selection, safe optional Card projection, browser credential override, and separate read-auth versus cloud-permission failures. No unresolved in-scope findings. - -| Check | Result | Evidence / limits | -| --- | --- | --- | -| `uv run --extra dev pytest tests/cli/test_a2a_center_actions.py tests/cli/test_a2a_center_registration.py tests/cli/test_frontend_runtime_proxy.py tests/test_mpa_collaborator_proxy_policy.py -q` | pass | 227 passed (6 warnings); MockTransport exercises actual Studio mounting and the shared helper, including successful simulated deletion and zero cloud calls on binding-auth refusal. | -| `npm --prefix frontend test` | pass | 1389 Node tests and 183 Vitest tests across 11 files, including 36 collaborating-Agent cases. | -| `npm --prefix frontend run build` | pass | Current packaged WebUI rebuilt; existing large-chunk advisory remains. | -| `npm --prefix frontend run check:i18n`; `npm --prefix frontend run test:webui-assets` | pass | 2 locales / 22 namespaces; 114 packaged files / 351 internal references. | -| `frontend/node_modules/.bin/tsc --noEmit -p frontend/tsconfig.json` | pass | No diagnostics. | -| `uv run --extra dev pre-commit run ruff-check --files ...`; `ruff-format` with the same files | pass | a2a_center.py, test_a2a_center_actions.py, test_frontend_runtime_proxy.py, cli_frontend.py. Formatter changes were reconciled before final tests. | -| Pyright using existing adjacent-repository executable, `--pythonpath .venv/bin/python frontend/server/a2a_center.py tests/cli/test_a2a_center_actions.py` | pass | 0 errors/warnings; no dependency installation or global configuration changes. | -| Whole-file CLI/proxy-test Pyright | fail (baseline) | 37 CLI and 20 existing proxy-test diagnostics. Re-running with only this change's helper block and proxy tests removed produced identical diagnostic-message multisets (57 each), establishing no added diagnostics. Temporary comparison files removed. | -| Browser | pass | Real browser with isolated mocked fetch: endpoint descriptions and full selected URL, mouse and keyboard switching, cancel/focus return, details/skills, 390px iframe and internal scrolling, and actionable preflight-auth alert. Fixtures and Vite process removed. | -| Live local read integration | pass | Current localhost Studio exposes the new endpoint route, serves the matching built app, and returns HTTP 200 for both real source Runtime endpoint reads and both registry detail reads. Observed one Runtime with public/private and another with public only; effective addresses and skills matched the console records. No cloud write performed. | -| Scoped gitleaks with repository config; document links/pairs/IDs; `git diff --check` | pass | No leaked secrets found; existing scanner exclusions retained, no bypass. | -| Full Python regression, commit-time all-files pre-commit, live cloud writes | not_run | Changes are scoped to A2A routes and a path-filtered existing auth policy; affected proxy/API consumers tested. No commit/push authorized, so branch synchronization and commit gates were not invoked. Real cloud deletion/registration is not proven by mocks or read-only checks. | - -T-1/T-2/T-3 and AC-1/AC-2/AC-3 completed. Current Studio was restarted externally during the task; this agent did not interrupt it. New backend and built app are active; refreshing the browser loads the new UI. No commit, push or Runtime deployment. Retained all unrelated user changes. Subsequent name investigation used read-only live values and did not alter AgentCard or registration names. diff --git a/prd-spec/bugfixes/mpa-a2a-space-deletion/2026-10-22-runtime-auth.zh.md b/prd-spec/bugfixes/mpa-a2a-space-deletion/2026-10-22-runtime-auth.zh.md deleted file mode 100644 index 8141a7dec..000000000 --- a/prd-spec/bugfixes/mpa-a2a-space-deletion/2026-10-22-runtime-auth.zh.md +++ /dev/null @@ -1,58 +0,0 @@ -# A2A 空间删除的 Runtime 认证 - -[English](2026-10-22-runtime-auth.md) - -变更 ID:mpa-a2a-space-deletion-runtime-auth。创建/修订:2026-10-22。状态:implemented。 -关联:[组件](../../../specs/mpa-collaborating-agents/README.zh.md)、[前置功能](../../features/mpa-collaborating-agents/2026-10-22-member-actions.zh.md)。 - -## 证据、范围与需求 - -新空间 DELETE 通过 `_runtime_json_request` 读取目标绑定,但未携带 `X-MPA-Studio-Key`。已有协作代理提供这一服务端持有的凭据;MPA 绑定端点要求该凭据。隔离复现返回 401,尚未调用任何云接口。经已有代理读取成功。这证明实现存在缺陷;未捕获用户原失败删除的原始响应。 - -FR-1:Runtime JSON 请求访问协作路径时应用已有 `collaborator_management_headers` 策略。使用获授权的服务端 Runtime Key,过滤浏览器提供的凭据,要求管理员权限,缺少 Key 时在上游 I/O 前拒绝。无关 JSON 路径不变。 -FR-2:绑定前置检查返回 401/403 时,保持状态码并返回有界原因 `runtime_auth_failed`。UI 说明 Runtime 认证失败、尚未执行删除。云端拒绝保留现有权限提示。此前置认证失败不返回原始上游错误,不自动重试,任何前置失败后均不执行云操作。 -FR-3:保留绑定/非空/身份检查、超时/不确定结果及取消行为。验证期间不删除真实空间、不改云 IAM、不部署 Runtime、不提交或推送。 - -## 设计与影响文件 - -在 `veadk/cli/cli_frontend.py` 的 `_runtime_json_request` 中复用 `veadk/integrations/mpa/collaborator_proxy.py`。不新增 Key 参数、不信任用户请求头、不新增依赖、数据库状态或 API 路由。在 `frontend/server/a2a_center.py` 中仅对绑定读取增加新原因。在 `frontend/src/adk/a2aSpace.ts` 接受该原因,`A2AResourceActions.tsx` 映射并增加等价 en-US/zh-CN 文案。沿用 ModalButton、alert/焦点/重试控件及布局。更新组件合同、frontend README 和构建产物。SDK 公共合同、生成项目、Runtime/Harness 代码与派发不受影响。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| FR-1 | T-1:先补真实 Studio 路由/MockTransport 失败回归,再补认证头 | AC-1:绑定收到服务端 Key 而非浏览器伪造值;空且未绑定夹具可进入模拟云删除 | tests/cli/test_frontend_runtime_proxy.py | -| FR-2 | T-2:先补前置错误和 UI 回归,再映射原因 | AC-2:绑定认证拒绝时云调用为零;UI 显示 Runtime 失败并可重试;云 403 保留原提示 | tests/cli/test_a2a_center_actions.py;frontend/tests/collaboratingAgents.test.tsx | -| FR-3 | T-3:回归、安全和交付评审 | AC-3:保留已有检查、授权、脱敏、取消及无关 helper 行为 | 定向 Python;npm test/build;i18n/assets/tsc;变更 Python Ruff/Pyright;模拟 API 的真实浏览器;密钥扫描;git diff --check | - -## 风险、评审与批准 - -共享 helper 必须由已有策略限制路径,禁止向无关路径广泛添加凭据。修复认证后仍可能遇到独立云 IAM 拒绝,应准确报告。单元/模拟测试不能证明真实云 DELETE 成功。后端生效需要重启当前本机 Studio;必须先检查,避免中断用户正在进行的工作。 - -可用技能目录没有 review-spec,已直接评审合同归属、错误边界、服务端凭据、兼容性、可行性、负面用例及双语等价,无阻塞。用户在生产/测试编辑前以“能修复吗”批准此前提出的认证/错误区分方案。新增端点/技能需求另行确认,不通过此记录隐式批准。 - -## 验证记录 - -测试范围:分支 feat/from-upstream-main-20261020 当前未提交 A2A 中心差异;基线 HEAD 9ab0ef2d。日期:2026-10-22。最终检查记录于交付证据。无真实云写入。 - - -## 交付证据(2026-10-22) - -在 9ab0ef2d 之上的已有未提交 A2A 变更中实现已批准范围。测试先行证据:实现前 22 项定向 Python 和 8 项 UI 用例失败。评审解决迟到端点选择、可选 Card 安全投影、浏览器凭据覆盖以及读取认证/云权限错误区分,无范围内未解决发现。 - -| 检查 | 结果 | 证据 / 限制 | -| --- | --- | --- | -| `uv run --extra dev pytest tests/cli/test_a2a_center_actions.py tests/cli/test_a2a_center_registration.py tests/cli/test_frontend_runtime_proxy.py tests/test_mpa_collaborator_proxy_policy.py -q` | pass | 227 passed(6 warnings);MockTransport 覆盖真实 Studio 挂载和共享 helper,包括成功模拟删除以及绑定认证拒绝后零云调用。 | -| `npm --prefix frontend test` | pass | 1389 项 Node 和 11 文件 183 项 Vitest,其中协作 Agent 36 项。 | -| `npm --prefix frontend run build` | pass | 已重建当前 WebUI 产物;保留既有大分块提示。 | -| `npm --prefix frontend run check:i18n`;`npm --prefix frontend run test:webui-assets` | pass | 2 语言 / 22 命名空间;114 产物文件 / 351 内部引用。 | -| `frontend/node_modules/.bin/tsc --noEmit -p frontend/tsconfig.json` | pass | 无诊断。 | -| `uv run --extra dev pre-commit run ruff-check --files ...`;相同文件的 `ruff-format` | pass | a2a_center.py、test_a2a_center_actions.py、test_frontend_runtime_proxy.py、cli_frontend.py;格式化变更已在最终测试前对齐。 | -| 使用相邻仓库已有 Pyright,`--pythonpath .venv/bin/python frontend/server/a2a_center.py tests/cli/test_a2a_center_actions.py` | pass | 0 错误/警告;未安装依赖或改变全局配置。 | -| CLI / 代理测试整文件 Pyright | fail(基线) | CLI 37 项和代理测试既有 20 项诊断。仅移除本次 helper 块和代理测试后,对照诊断消息多重集完全一致(各 57 项),无新增诊断。临时对照文件已删除。 | -| 浏览器 | pass | 隔离模拟 fetch 的真实浏览器:端点描述和选中完整 URL、鼠标/键盘切换、取消/焦点返回、详情/技能、390px iframe 与内部滚动、可操作的前置认证提示。夹具和 Vite 进程已删除。 | -| 本机实时只读集成 | pass | 当前 localhost Studio 已提供新端点路由并服务匹配构建,两个真实来源 Runtime 的端点读取及两个注册详情读取均 HTTP 200。一条同时有公私网,另一条仅公网;生效地址和技能与控制台记录一致。未执行云写入。 | -| 仓库配置的范围内 gitleaks;文档链接/双语/编号;`git diff --check` | pass | 未发现秘密泄露;保留既有扫描排除项,未绕过。 | -| 全量 Python 回归、提交前全文件 pre-commit、真实云写入 | not_run | 变更限 A2A 路由和已有按路径过滤认证策略,已验证受影响代理/API 调用者。未授权提交/推送,因此未执行分支同步及提交门禁。模拟或只读检查不证明真实云删除/注册成功。 | - -T-1/T-2/T-3、AC-1/AC-2/AC-3 完成。任务期间 Studio 由外部重启,本 Agent 未中断进程。新后端和构建已生效,浏览器刷新加载新界面。未提交、推送或部署 Runtime,保留全部无关用户修改。后续名称调查仅读取实时值,未改变 AgentCard 或注册名称。 diff --git a/prd-spec/bugfixes/mpa-agent-alias/2026-10-22-agent-alias-business-store.md b/prd-spec/bugfixes/mpa-agent-alias/2026-10-22-agent-alias-business-store.md deleted file mode 100644 index 270d8d648..000000000 --- a/prd-spec/bugfixes/mpa-agent-alias/2026-10-22-agent-alias-business-store.md +++ /dev/null @@ -1,76 +0,0 @@ -# Registered Agent aliases in the MPA business database - -[中文版](2026-10-22-agent-alias-business-store.zh.md) - -- Change ID: agent-alias-business-store -- Date: 2026-10-22 -- Status: implemented -- Approval: user explicitly approved business-database aliases, removal of the alias dependency on AGENTKIT_ACCOUNT_ID, and synchronized changes in VeADK and agentkit-mpa-agent. -- Supersedes the account-shared storage decision in the 2026-10-22 space-aliases design. - -## Background and evidence - -The current Runtime alias provider opens SHARED_APIG_DATABASE_URL and requires AGENTKIT_ACCOUNT_ID, although the alias belongs to a registered Agent under a space in the current MPA. Studio consequently disables editing when account identity is absent. Its proxy also rejects alias PUT requests before forwarding them. Space bindings already use the durable MPA business engine; reuse that engine. - -## Goals and non-goals - -Persist registered Agent aliases in the business database; use the same data for management, discovery and exact @name resolution. Correct Studio labels and allow the existing alias update route through its authenticated proxy. Ordinary Agent behavior, center registration names, Agent Cards, cloud configuration and dispatch/Harness authority are outside this change. No new dependency, cloud write, deployment, automatic shared-data import or destructive schema change. - -## Scenarios and requirements - -- FR-1: With no account ID or shared database configured, saving and clearing an Agent alias succeeds using the durable business engine. A restart retains its value and revision. -- FR-2: Scope is (mpa_id, region, space_id, agent_id). Normalized alias uniqueness is within (mpa_id, region, space_id). Different MPAs/spaces/regions do not share aliases, even on one engine. -- FR-3: List, discovery and @name resolution use that same scope. Existing NFKC normalization, 80-character limit, casefold uniqueness, revision CAS, tombstones and live membership/dispatch checks remain. Storage errors fail closed without fallback to the account database. -- FR-4: Studio says Agent alias and explains the current MPA/space scope. Legacy Runtime capability absence remains disabled with an upgrade message. PUT is allowed only for the existing binding route and the exact alias route with a valid a-* ID; existing ownership/admin authentication remains enforced. - -## Design and contract impact - -Runtime A2AAliasStore reuses A2ASpaceStore.engine, never creates or disposes a separate engine. Add business table mpa_a2a_registration_aliases with mpa_id VARCHAR(256), region VARCHAR(64), space_id/agent_id VARCHAR(128) as primary key; alias VARCHAR(80), alias_key VARCHAR(240), revision INTEGER NOT NULL; unique (mpa_id, region, space_id, alias_key). Add migration 0016; preserve published migrations. Lazy initialization uses the existing advisory lock on PostgreSQL. Never alter or read the old mpa_a2a_agent_aliases table; users must re-enter previously shared aliases to avoid copying data into the wrong MPA. Rollback retains additive tables and restores the old binary; no deletion is needed. - -The service owns one alias store over its business engine. Management list/save receive the current MPA identity from the server dependency, never from the request body. Region is derived from Runtime settings. External API paths, bodies and response fields remain compatible. A failed business-store operation remains an explicit error; no empty success or fallback. Management may edit accessible spaces while dispatch still requires the selected binding. Existing cancellation, finite resolution timeout, binding recheck and live registry verification remain intact. - -VeADK only updates localized guidance, documents, proxy allowlist and matching built assets. No ordinary Agent deployment/configuration change. Reuse the existing alias editor and primitive controls without visual redesign. Update the maintained collaborating-Agent contracts in both repositories and both VeADK languages. - -## Tasks and affected files - -- T-1 / FR-1–3: failing Runtime store/service/route regressions, isolation/restart/conflict/error and PostgreSQL migration tests. -- T-2 / FR-1–3: app/stores/a2a_alias.py, app/services/a2a_space.py, app/api/v1/collaborating_agents.py and additive app/migrations/0016_a2a_registration_aliases.sql. -- T-3 / FR-4: proxy regression, veadk/cli/cli_frontend.py, frontend locale guidance, existing editor tests and generated web assets. -- T-4: reconcile contracts, user documentation and verification evidence; review security, compatibility, resource ownership and bilingual equivalence. - -## Verification and acceptance - -AC-1 (FR-1/T-1–2): no shared configuration required; business-store persistence survives a new service/connection. AC-2 (FR-2/T-1–2): MPA/region/space isolation, uniqueness, CAS and clearing work on SQLite and disposable PostgreSQL. AC-3 (FR-3/T-1–2): management save/list, discovery and exact mention lookup agree; inaccessible membership and database errors cannot dispatch. AC-4 (FR-4/T-3): actual Studio proxy forwards alias PUT with server credentials, preserves upstream conflicts and rejects unrelated PUT/unauthorized writes; UI no longer requests shared-account setup. - -Commands and actual results are recorded below after execution. Runtime: targeted pytest, make lint, full make test, governance/project-skills/project-tooling checks, isolated PostgreSQL migration/store contracts. VeADK: targeted uv run --extra dev pytest, Ruff/Pyright on changed Python, npm --prefix frontend test, npm --prefix frontend run build, i18n/assets checks and real browser fixture normal/error/keyboard/IME/cancel/narrow states. No real cloud calls; local fixtures are not deployment evidence. - -## Risks and review - -Aliases formerly saved in the shared database are deliberately not imported. Upgrade both Studio and Runtime to activate the complete route/storage change. Business-store failure blocks alias-dependent operations. Existing database engine lifecycle belongs to the channel store and must not be disposed by alias code. No external side-effect authority changes. Direct design review performed because review-spec is unavailable: ownership, scope, failure semantics, API compatibility, additive rollback, tests and paired-language consistency reviewed; no design blockers. User approval above covers this correction. - -## Delivery record - -2026-10-22, uncommitted implementation over Runtime 0d3dd35 and VeADK 46f60e12: - -- pass: RED before implementation reproduced 3 Runtime failures, 6 alias proxy rejections and 2 locale failures; corresponding regressions are now green. -- pass: final isolated PostgreSQL/Runtime targeted command covering tests/test_a2a_aliases.py, tests/test_a2a_business_aliases.py, tests/test_a2a_business_alias_migration.py, tests/test_a2a_rebase_migration.py, tests/test_a2a_registry_collaborators.py, tests/test_a2a_management_routes.py and tests/test_schema_migration_contracts.py — 54 passed, no skips. Disposable PostgreSQL 16 verified fresh/v15 and all v14 migration lineages, repeated initialization, cross-connection conflicts and preservation of the old shared table. Container removed afterwards. -- pass: PATH=/tmp/a2a-codex-0155/node_modules/.bin:$PATH make test — 10823 passed, 386 skipped in 634.22 seconds. Before/after source fingerprint 5a56a035892b8a93396e4d82a69df257965e39da64f9d02ec8c13c5748db7694 matched. Skips are not proof of live integrations. An earlier interrupted run was invalidated by source drift and is excluded. -- pass: make lint; Ruff on changed Runtime files; Pyright on the three changed Runtime implementation files — 0 errors. A previously implicit pagination total is initialized explicitly without changing lookup behavior. -- pass: make governance-check BASE_SHA=HEAD HEAD_SHA=HEAD (current worktree correction; contract check plus 14 tests), make project-skills-check. Default origin/master comparison includes multiple historical change IDs and cannot select one; no gate code or validation was bypassed. -- fail: make project-tooling-check — pre-existing missing OpenViking CLI/skills. User declined separate environment documentation; global configuration was not changed. Full release qualification is not claimed. -- pass: uv run --extra dev pytest -q tests/cli/test_frontend_runtime_proxy.py — 142 passed. Broader uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" tests/cli — 1844 passed, 6 skipped (2 workers). -- pass: npm --prefix frontend test — 1389 Node tests and 236 Vitest tests; npm --prefix frontend run build; npm --prefix frontend run check:i18n (2 locales/23 namespaces); npm --prefix frontend run test:webui-assets (114 files/351 references). Build regenerated matching hashed assets and removed obsolete hashes. -- pass: real browser fixture using the actual alias editor, Studio tokens/CSS and actual Runtime HTTP routes with a fake cloud registry and disposable SQLite business database: Chinese save, page reload/read-back, clear, CAS conflict and refresh, Escape cancel, unsupported Runtime message, and a 360-pixel container. Synthetic IME, request cancellation/unmount and duplicate-submit tests passed; a native IME and resized browser window were not separately exercised. Temporary fixture files, servers and database were removed. -- pass: VeADK Ruff hooks, pre-commit --all-files (including secret scan); Pyright compared current files against HEAD with the same interpreter: 57 existing diagnostics, 0 added. Raw Pyright is therefore fail on the baseline; no unrelated type fixes were included. Repo dependencies do not install a standalone Ruff command, so the pinned repository pre-commit hooks supplied it. -- pass: paired-language requirement/identifier/relative-link and diff-whitespace checks. Final edits after the full Runtime run change documentation and its ignore exception only; implementation/tests remain as tested. -- not_run: real cloud/model smoke, deployment, image publication, commit and push — outside this authorized correction. No Tool/MCP declaration/adapter changes, so the path-triggered MCP tool gate is not applicable. Full coverage/release gates were not run for this local code delivery. - -AC-1–4 and T-1–4 are satisfied for local behavior. Code/security/test/architecture review found no remaining blocker in this correction: business engine lifecycle stays with its original owner, scope is server-derived, store failures fail closed, migration is additive, and proxy authorization remains intact. Old shared aliases require re-entry. Upgrade Studio and Runtime together; no cloud account-ID patch is needed. Complete release qualification still has the tooling/baseline static limitations listed above. No commit, push, image publication or deployment performed. - -Additional secret-scan evidence: default Gitleaks over all changed files (17.70 MB) flagged one minified vendor expression a minified member-access expression; manual source inspection confirmed an identifier expression, not a credential. VeADK scan using its existing .gitleaks.toml (1.02 MB of included source) and the Runtime default scan (456.14 KB) both found zero leaks. No scanner rule or allowlist was changed. - -2026-10-22 follow-up authorization: user authorized committing/pushing VeADK only and tracking PR CI. Fetched official main and fork branch, then rebased official main (already current at cb9975df). The lockfile only lost 27 optional build-tool records; backed up and restored without changing dependencies. Staged scanning flagged the quoted minified vendor expression in these documents; removed that example without changing scanner rules. The Runtime repository is not committed in this follow-up. - -CI preparation: static inspection found missing Apache headers in five existing PR files. Added license comments only to frontend/server/a2a_center.py, frontend/server/mpa_images.py, tests/cli/test_a2a_center_actions.py, tests/cli/test_a2a_center_registration.py and tests/cli/test_mpa_images.py. No runtime or contract change; this comment-only correction requires no recursive PRD. - -Final pre-commit validation (2026-10-22): after synchronization with cb9975df, all pre-commit hooks passed; frontend 1389 Node + 236 Vitest tests, build, i18n and 114-file/351-reference checks passed; dependency policy and uv lock --check passed. Running the CI Sidecar script produced 143 passed/1 skipped (96% coverage, threshold 91%), 9 passed/50 deselected, 97 passed and 39 passed across its four backend groups; frontend Sidecar 22 passed, 100% line/94.44% branch coverage. Pyright on the five license-only files reported zero errors; all 30 PR Python files have required headers. Default Python regression (2 workers): 7861 passed, 53 skipped, 4 xfailed, 1 failed in 474.00 seconds. The sole failure remains tests/frontend/server/runtime_artifacts/test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes; an independent archive of unchanged HEAD 46f60e12 fails identically in this environment. Local Python 3.10 returns None for the .md MIME lookup; unrelated artifact behavior was not modified. Commit/push are authorized in this follow-up. GitHub CI must be assessed on the new remote revision; older runs are action_required and are not passes. diff --git a/prd-spec/bugfixes/mpa-agent-alias/2026-10-22-agent-alias-business-store.zh.md b/prd-spec/bugfixes/mpa-agent-alias/2026-10-22-agent-alias-business-store.zh.md deleted file mode 100644 index d080b42f1..000000000 --- a/prd-spec/bugfixes/mpa-agent-alias/2026-10-22-agent-alias-business-store.zh.md +++ /dev/null @@ -1,76 +0,0 @@ -# 已注册 Agent 别名写入 MPA 业务库 - -[English](2026-10-22-agent-alias-business-store.md) - -- Change ID:agent-alias-business-store -- 日期:2026-10-22 -- 状态:implemented -- 用户授权:明确同意别名写入业务库、移除别名对 AGENTKIT_ACCOUNT_ID 的依赖,并同步修改 VeADK 与 agentkit-mpa-agent。 -- 取代 2026-10-22 space-aliases 设计中的账号共享存储决定。 - -## 背景与证据 - -当前 Runtime 别名 provider 单独连接 SHARED_APIG_DATABASE_URL 并要求 AGENTKIT_ACCOUNT_ID,但别名属于当前 MPA 中某个空间下的已注册 Agent。账号标识缺失时 Studio 因此禁用编辑,其代理还会在转发前拒绝别名 PUT。空间绑定已使用持久化 MPA 业务库引擎,应复用该引擎。 - -## 目标与非目标 - -已注册 Agent 别名持久化到业务库;管理、发现和精确 @名称解析使用同一份数据。修正 Studio 文案并让现有别名更新路由通过鉴权代理。本次不修改普通 Agent 行为、中心注册名称、Agent Card、云配置或派发/Harness 授权;不新增依赖、不写云、不部署、不自动导入共享数据、不破坏已有表结构。 - -## 场景与需求 - -- FR-1:没有账号 ID 和共享库配置时,使用持久化业务引擎保存、清除 Agent 别名;重启保留值与 revision。 -- FR-2:作用域为 (mpa_id, region, space_id, agent_id),规范化别名在 (mpa_id, region, space_id) 内唯一;同一引擎中的不同 MPA、空间、地域互不共享。 -- FR-3:列表、发现和 @名称解析使用相同作用域。保留 NFKC、80 字符上限、casefold 唯一性、revision CAS、清空墓碑以及实时成员与派发校验。业务库存储错误明确失败,不回退账号库。 -- FR-4:Studio 显示“Agent 别名”并解释当前 MPA/空间作用域;旧 Runtime 未声明能力时继续禁用并提示升级。PUT 仅允许现有绑定路由和 Agent ID 合法的精确别名路由;保留所有者/管理员鉴权。 - -## 设计与契约影响 - -Runtime A2AAliasStore 复用 A2ASpaceStore.engine,不新建或释放独立引擎。新增业务表 mpa_a2a_registration_aliases:主键 mpa_id VARCHAR(256)、region VARCHAR(64)、space_id/agent_id VARCHAR(128);字段 alias VARCHAR(80)、alias_key VARCHAR(240)、revision INTEGER NOT NULL;唯一约束 (mpa_id, region, space_id, alias_key)。追加 0016 迁移,不修改已发布迁移。PostgreSQL 懒初始化继续使用现有 advisory lock。不修改或读取旧 mpa_a2a_agent_aliases 表;已有共享别名需重新填写,避免错误复制到其他 MPA。回滚保留新增表并回退二进制,无须删数据。 - -服务持有复用业务引擎的别名 store。管理列表/保存通过服务端 dependency 获取当前 MPA,不接收请求体身份;地域来自 Runtime 配置。外部 API 路径、请求体和响应字段兼容。业务库失败明确报错,不返回空成功、不回退。管理可编辑有权限的空间,但派发仍需已绑定空间。保留取消、有限名称查询超时、绑定重查与实时中心校验。 - -VeADK 仅修改双语提示、文档、代理白名单与对应构建产物,不改普通 Agent 部署/配置。复用现有别名编辑器和基础控件,不做视觉重设计。同步两个仓库协作 Agent 长期契约与 VeADK 双语文档。 - -## 任务与受影响文件 - -- T-1 / FR-1–3:先补 Runtime store/service/路由回归,覆盖隔离、重启、冲突、错误和 PostgreSQL 迁移。 -- T-2 / FR-1–3:app/stores/a2a_alias.py、app/services/a2a_space.py、app/api/v1/collaborating_agents.py,追加 app/migrations/0016_a2a_registration_aliases.sql。 -- T-3 / FR-4:代理回归、veadk/cli/cli_frontend.py、frontend 双语提示、现有编辑器测试与 web 构建产物。 -- T-4:同步契约、用户文档、验证证据;审查安全、兼容性、资源归属和双语等价性。 - -## 验证与验收 - -AC-1(FR-1/T-1–2):无需共享配置;新服务/连接可读取业务库持久化别名。AC-2(FR-2/T-1–2):SQLite 与隔离 PostgreSQL 验证 MPA/地域/空间隔离、唯一性、CAS 与清空。AC-3(FR-3/T-1–2):管理保存/列表、发现与精确名称解析一致;无成员权限或数据库失败不能派发。AC-4(FR-4/T-3):真实 Studio 代理使用服务端凭据转发别名 PUT、保留上游冲突、拒绝无关 PUT 和未授权写;UI 不再要求配置共享账号。 - -执行后在下方记录实际命令/结果。Runtime:定向 pytest、make lint、全量 make test、governance/project-skills/project-tooling 检查、隔离 PostgreSQL 迁移/store 契约。VeADK:定向 uv run --extra dev pytest、变更 Python 的 Ruff/Pyright、npm --prefix frontend test、npm --prefix frontend run build、i18n/assets 检查,真实浏览器模拟接口的正常/错误/键盘/IME/取消/窄屏流程。不调用真实云;本地模拟不构成部署证据。 - -## 风险与评审 - -旧共享别名不自动导入;完整启用需同时升级 Studio 和 Runtime。业务库故障阻断依赖别名的操作;业务引擎生命周期归 channel store,别名代码不得释放。未增加外部副作用权限。review-spec 不可用,已直接评审归属、作用域、错误、API 兼容性、新增迁移回滚、测试和双语等价,无设计阻塞;以上用户授权覆盖此次修正。 - -## 交付记录 - -2026-10-22,Runtime 0d3dd35 与 VeADK 46f60e12 上的未提交修正: - -- pass:实现前 RED 复现 3 项 Runtime 失败、6 项代理拒绝、2 项文案失败;对应回归现已通过。 -- pass:最终隔离 PostgreSQL/Runtime 定向命令覆盖 tests/test_a2a_aliases.py、tests/test_a2a_business_aliases.py、tests/test_a2a_business_alias_migration.py、tests/test_a2a_rebase_migration.py、tests/test_a2a_registry_collaborators.py、tests/test_a2a_management_routes.py、tests/test_schema_migration_contracts.py,54 passed、无跳过。临时 PostgreSQL 16 验证全新/v15、全部 v14 迁移来源、重复初始化、跨连接冲突及旧共享表保留,容器已删除。 -- pass:PATH=/tmp/a2a-codex-0155/node_modules/.bin:$PATH make test,10823 passed、386 skipped,634.22 秒;前后源码指纹均为 5a56a035892b8a93396e4d82a69df257965e39da64f9d02ec8c13c5748db7694。跳过不能证明真实集成。早期运行因修改了候选树而中断/失效,不计入通过。 -- pass:make lint、变更 Runtime 文件 Ruff、三个变更实现文件 Pyright(0 错误);显式初始化分页 total,不改变查询行为。 -- pass:make governance-check BASE_SHA=HEAD HEAD_SHA=HEAD(当前工作区修正,契约检查和 14 项测试)、make project-skills-check。默认 origin/master 比较包含多个历史 change-id,无法唯一选择;未修改或绕过门禁。 -- fail:make project-tooling-check,既有 OpenViking CLI/skill 缺失;用户选择不另写环境说明,未修改全局配置。不声称完整发布准出。 -- pass:uv run --extra dev pytest -q tests/cli/test_frontend_runtime_proxy.py,142 passed;扩大到 uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" tests/cli,1844 passed、6 skipped,使用 2 个 worker。 -- pass:npm --prefix frontend test,1389 项 Node、236 项 Vitest;npm --prefix frontend run build、npm --prefix frontend run check:i18n(2 语言/23 命名空间)、npm --prefix frontend run test:webui-assets(114 文件/351 引用);重新生成匹配哈希资源并移除旧哈希。 -- pass:真实浏览器使用实际别名编辑器、Studio tokens/CSS、真实 Runtime HTTP 路由,中心为 fake registry,业务库为临时 SQLite:中文保存、刷新页面读回、清空、CAS 冲突/刷新、Escape 取消、旧 Runtime 提示和 360 像素容器均已验证。合成 IME、请求取消/卸载和重复提交测试通过;未单独验证原生输入法和缩放浏览器窗口。临时文件、服务与数据库已清理。 -- pass:VeADK Ruff hooks、pre-commit --all-files(含密钥扫描);Pyright 使用同一解释器对照 HEAD:57 个既有诊断、0 新增。因此原始 Pyright 对基线仍为 fail,不顺手修复无关类型问题。仓库依赖未安装独立 Ruff 命令,改用仓库锁定的 pre-commit hooks。 -- pass:双语需求/标识符/相对链接及 diff 空白检查。Runtime 全量运行后的最终修改仅为文档及其 ignore 例外,实现/测试与已验证版本相同。 -- not_run:真实云/模型烟测、部署、镜像发布、提交和推送,均不属于本次授权。未改 Tool/MCP 声明或 adapter,路径触发的 MCP tool gate 为 not_applicable;本地代码交付未执行完整 coverage/发布门禁。 - -AC-1–4、T-1–4 的本地行为验收已满足。实现/安全/测试/架构审查无本次阻塞:业务引擎生命周期保留原所有者、作用域由服务端确定、存储失败拒绝、新增迁移、代理鉴权保持。旧共享别名需重填;Studio 和 Runtime 应一起升级,无需补云端账号 ID。完整发布准出仍有上述工具与基线静态检查限制。未提交、推送、发布镜像或部署。 - -附加密钥扫描:默认 Gitleaks 扫描全部变更文件(17.70 MB)时将压缩依赖表达式 一段压缩后的成员访问表达式 报为一项疑似密钥;人工核对为标识符表达式,不是凭据。随后 VeADK 使用既有 .gitleaks.toml 扫描纳入范围的源码(1.02 MB)、Runtime 使用默认规则(456.14 KB),均为零泄漏;未修改扫描规则或白名单。 - -2026-10-22 提交授权补充:用户授权仅提交、推送 VeADK 并跟踪 PR CI。已 fetch 官方 main 和 fork 分支,并 rebase 官方 main(无新增提交,基线仍为 cb9975df)。锁文件仅丢失 27 个可选构建工具记录,已保留备份并恢复,未修改依赖。暂存扫描发现文档中引用的压缩依赖表达式误报,已删除样例,不修改扫描规则。Runtime 仓库本轮不提交。 - -CI 准备补充:静态检查发现本 PR 已有 5 个 Python 文件缺失 Apache 头,现仅补齐许可证注释:frontend/server/a2a_center.py、frontend/server/mpa_images.py、tests/cli/test_a2a_center_actions.py、tests/cli/test_a2a_center_registration.py、tests/cli/test_mpa_images.py。无业务行为或契约变化;此纯注释修正无需递归 PRD。 - -提交前最终验证(2026-10-22):同步 cb9975df 后,pre-commit 全部通过;前端 1389 Node + 236 Vitest、build、i18n、114 文件/351 引用检查通过;依赖 policy 和 uv lock --check 通过。按 CI 脚本执行 Sidecar 后端四组:143 passed/1 skipped(覆盖率 96%,门槛 91%)、9 passed/50 deselected、97 passed、39 passed;前端 Sidecar 22 passed,行覆盖 100%、分支 94.44%。5 个仅补许可证头文件 Pyright 0 错误;PR 全部 30 个 Python 文件有规定头。默认 Python 全量(2 workers)为 7861 passed、53 skipped、4 xfailed、1 failed(474.00 秒);唯一失败仍为 tests/frontend/server/runtime_artifacts/test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes,原 HEAD 46f60e12 的独立归档在同一环境也失败;本机 Python 3.10 的 .md MIME 查询为 None,未改动无关产物行为。提交推送已获本轮用户授权;GitHub CI 需以新提交的远端结果为准,目前旧运行均为 action_required,不能记为通过。 diff --git a/prd-spec/bugfixes/mpa-agent-filtering/2026-09-14-mpa-agent-filtering.md b/prd-spec/bugfixes/mpa-agent-filtering/2026-09-14-mpa-agent-filtering.md deleted file mode 100644 index 8ae230c6d..000000000 --- a/prd-spec/bugfixes/mpa-agent-filtering/2026-09-14-mpa-agent-filtering.md +++ /dev/null @@ -1,61 +0,0 @@ -# MPA Agent Filtering Bugfix - -- Change ID: `mpa-agent-filtering` -- Created: 2026-09-14 -- Status: approved -- Chinese: [2026-09-14-mpa-agent-filtering.zh.md](2026-09-14-mpa-agent-filtering.zh.md) -- Related specs: [MPA Runtime Provisioning](../../../specs/mpa-runtime-provisioning/README.md), [Studio MPA Control Plane](../../../specs/studio-mpa-control-plane/README.md) - -## Background And Evidence - -Studio added `CloudRuntime.agentCategory` and the new-chat picker can show an `MPA Agent` type, but MPA Runtimes are not reliably returned there. The server classification must use an explicit Runtime tag; artifact URL matching lets old test images pollute the MPA filter and is not reliable enough for product grouping. The MPA Runtime provisioning path does not write that tag, and frontend filtering currently happens after one `/web/runtimes` page is loaded, so MPA Runtimes can be missed when a page mostly contains general agents. The My Agents page also lacks an `mpa` type option. - -## Goals - -- Persist a stable MPA Runtime classification tag during `veadk mpa create`. -- Let `/web/runtimes` filter by agent category on the server side. -- Make the new-chat picker and My Agents page use the same MPA category. -- Preserve compatibility with existing callers that do not pass a category. - -## Non-Goals - -- Do not change the mpa-agent image or its runtime protocol. -- Do not push images or modify `~/workspace/bytedance/mpa/agentkit-mpa-agent` in this bugfix. -- Do not automatically mutate cloud Runtime tags during read-only listing. - -## Requirements - -- `FR-1`: MPA Runtime create and convergent update must include `veadk:agent-type=mpa`. -- `FR-2`: `/web/runtimes` must accept optional `agentCategory=general|mpa` and return pages that are filtered before pagination is exposed to the frontend. -- `FR-5`: `agentCategory=mpa` must use the Volcano Tag service's positive `veadk:agent-type=mpa` lookup before Runtime detail hydration so sparse MPA results do not require scanning unrelated Runtime pages. -- `FR-3`: Existing untagged MPA Runtimes must not be classified as MPA by image-name heuristics. They enter the MPA filter only after an explicit tag repair writes `veadk:agent-type=mpa`. -- `FR-4`: New-chat and My Agents type filters must both expose MPA and must not mix MPA Runtimes into General. - -## Design - -`veadk.integrations.mpa.mpa_runtime.provision_runtime()` adds a `tags` mapping parameter. The MPA CLI passes `{"veadk:agent-type": "mpa"}`. The function writes tags on both `CreateRuntimeRequest` and `UpdateRuntimeRequest` so retries converge existing named Runtimes to the same category. - -`GET /web/runtimes` keeps its response shape and adds an optional query parameter `agentCategory`. Invalid values return `400`. Runtime category is derived only from explicit Runtime tags. When `agentCategory=mpa` is present, the server queries the Volcano Tag service with a positive `veadk:agent-type=mpa` filter to obtain candidate Runtime IDs, then hydrates those Runtime records by ID. The existing per-page Tag service lookup is retained for ordinary Runtime listing because the Runtime SDK response may omit custom tags. `agentCategory=general` remains a local exclusion filter because the control plane does not expose a negative tag filter. - -The frontend adds `agentCategory` to `getRuntimes()` options. NewChatAgentPicker asks the server for `mpa` or `general` instead of filtering a single page locally. MyAgents adds `mpa` to its type enum and uses the same runtime listing path for `general` and `mpa`; personal sandbox agent types remain unchanged. - -## Tasks - -- `T-1`: Update MPA Runtime provisioning tags and tests. -- `T-2`: Update `/web/runtimes` filtering and tests. -- `T-3`: Update frontend client, NewChatAgentPicker, MyAgents, i18n, and source-based tests. -- `T-4`: Run targeted Python/frontend checks and rebuild Studio assets if frontend output changes. - -## Verification And Acceptance - -| Requirement | Task | Acceptance criterion | Verification | Result | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1` | MPA create/update Runtime requests include `veadk:agent-type=mpa`. | `uv run --extra dev pytest tests/integrations/test_mpa_runtime.py tests/cli/test_cli_mpa.py` | pass | -| `FR-2` | `T-2` | `/web/runtimes?agentCategory=mpa` returns only MPA Runtime pages and rejects invalid categories. | `uv run --extra dev pytest tests/cli/test_frontend_runtime_proxy.py::test_runtime_list_filters_agent_category_before_pagination -q` | pass | -| `FR-5` | `T-2` | MPA Runtime listing uses the Tag service positive filter to obtain candidate Runtime IDs and avoids scanning later unrelated Runtime list pages. | `uv run --extra dev pytest tests/cli/test_frontend_runtime_proxy.py::test_runtime_list_filters_agent_category_before_pagination -q` | pass | -| `FR-4` | `T-3` | New-chat and My Agents expose MPA and request category-filtered Runtime pages. | `node --test frontend/tests/newChatAgentPicker.test.mjs frontend/tests/myAgents.test.mjs`; `npm --prefix frontend test` | pass | -| All | `T-4` | Built Studio assets match source changes. | `npm --prefix frontend run build -- --mode development`; `npm --prefix frontend run test:webui-assets`; `git diff --check` | pass | - -## Risks - -Historical MPA Runtimes without `veadk:agent-type=mpa` remain classified as General until a deliberate tag repair is run. This bugfix does not add that repair command; it makes new and updated MPA Runtime classifications reliable and fixes server-side filtering semantics without admitting old test images by name. diff --git a/prd-spec/bugfixes/mpa-agent-filtering/2026-09-14-mpa-agent-filtering.zh.md b/prd-spec/bugfixes/mpa-agent-filtering/2026-09-14-mpa-agent-filtering.zh.md deleted file mode 100644 index e824cf4a3..000000000 --- a/prd-spec/bugfixes/mpa-agent-filtering/2026-09-14-mpa-agent-filtering.zh.md +++ /dev/null @@ -1,61 +0,0 @@ -# MPA 智能体筛选修复 - -- Change ID: `mpa-agent-filtering` -- Created: 2026-09-14 -- Status: approved -- English: [2026-09-14-mpa-agent-filtering.md](2026-09-14-mpa-agent-filtering.md) -- Related specs: [MPA Runtime Provisioning](../../../specs/mpa-runtime-provisioning/README.md), [Studio MPA Control Plane](../../../specs/studio-mpa-control-plane/README.md) - -## 背景与证据 - -Studio 已新增 `CloudRuntime.agentCategory`,新对话选择器也可以展示 `MPA 智能体` 类型,但 MPA Runtime 不能稳定出现在该分类下。服务端分类必须使用显式 Runtime 标签;artifact URL 匹配会让历史测试镜像污染 MPA 筛选,不能作为产品分组依据。MPA Runtime 创建链路目前不会写入该标签;前端又是在加载一页 `/web/runtimes` 后再过滤,因此当某一页主要是通用智能体时,MPA Runtime 会被漏掉。“我的智能体”页面也还没有 `mpa` 类型选项。 - -## 目标 - -- 在 `veadk mpa create` 创建 MPA Runtime 时持久化稳定分类标签。 -- 让 `/web/runtimes` 支持服务端按智能体分类过滤。 -- 让新对话选择器和“我的智能体”页面使用同一套 MPA 分类。 -- 保持未传分类参数的既有调用兼容。 - -## 非目标 - -- 不修改 mpa-agent 镜像或运行时协议。 -- 本修复不推送镜像,也不修改 `~/workspace/bytedance/mpa/agentkit-mpa-agent`。 -- 不在只读列表接口里自动修改云端 Runtime 标签。 - -## 需求 - -- `FR-1`: MPA Runtime 创建和按名称收敛更新必须包含 `veadk:agent-type=mpa`。 -- `FR-2`: `/web/runtimes` 必须接受可选 `agentCategory=general|mpa`,并在暴露分页结果前完成服务端过滤。 -- `FR-5`: `agentCategory=mpa` 必须先通过 Volcano Tag 服务的正向 `veadk:agent-type=mpa` 查询获得 Runtime ID,再补齐 Runtime 详情,避免 MPA 结果稀疏时扫描大量无关 Runtime 页。 -- `FR-3`: 已有未打标签的 MPA Runtime 不得通过镜像名启发式归类为 MPA。只有显式补写 `veadk:agent-type=mpa` 后才进入 MPA 筛选。 -- `FR-4`: 新对话和“我的智能体”类型筛选都必须展示 MPA,并且通用智能体中不得混入 MPA Runtime。 - -## 设计 - -`veadk.integrations.mpa.mpa_runtime.provision_runtime()` 新增 `tags` 映射参数。MPA CLI 传入 `{"veadk:agent-type": "mpa"}`。该函数在 `CreateRuntimeRequest` 和 `UpdateRuntimeRequest` 中都写入标签,确保重试复用同名 Runtime 时也会收敛到同一分类。 - -`GET /web/runtimes` 保持响应结构不变,新增可选查询参数 `agentCategory`。无效值返回 `400`。Runtime 分类只来自显式 Runtime 标签。当传入 `agentCategory=mpa` 时,服务端先使用 Volcano Tag 服务按正向 `veadk:agent-type=mpa` 过滤获得候选 Runtime ID,再按 ID 补齐 Runtime 详情。普通 Runtime 列表仍保留逐页 Tag 服务补查,因为 Runtime SDK 响应可能缺失自定义标签。`agentCategory=general` 继续使用本地排除过滤,因为控制面不提供“非某标签”的负向过滤。 - -前端为 `getRuntimes()` 增加 `agentCategory` 参数。NewChatAgentPicker 不再对单页结果做本地二次过滤,而是向服务端请求 `mpa` 或 `general`。MyAgents 将 `mpa` 加入类型枚举,并对 `general` 与 `mpa` 共用 Runtime 列表路径;个人沙箱智能体类型保持不变。 - -## 任务 - -- `T-1`: 更新 MPA Runtime 标签写入和测试。 -- `T-2`: 更新 `/web/runtimes` 分类过滤和测试。 -- `T-3`: 更新前端 client、NewChatAgentPicker、MyAgents、i18n 和源码断言测试。 -- `T-4`: 运行目标 Python/前端检查,并在前端输出变化时重建 Studio 资产。 - -## 验证与验收 - -| Requirement | Task | Acceptance criterion | Verification | Result | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1` | MPA create/update Runtime 请求包含 `veadk:agent-type=mpa`。 | `uv run --extra dev pytest tests/integrations/test_mpa_runtime.py tests/cli/test_cli_mpa.py` | pass | -| `FR-2` | `T-2` | `/web/runtimes?agentCategory=mpa` 只返回 MPA Runtime 分页,并拒绝非法分类。 | `uv run --extra dev pytest tests/cli/test_frontend_runtime_proxy.py::test_runtime_list_filters_agent_category_before_pagination -q` | pass | -| `FR-5` | `T-2` | MPA Runtime 列表通过 Tag 服务正向过滤获得候选 Runtime ID,不再扫描后续无关 Runtime 列表页。 | `uv run --extra dev pytest tests/cli/test_frontend_runtime_proxy.py::test_runtime_list_filters_agent_category_before_pagination -q` | pass | -| `FR-4` | `T-3` | 新对话和“我的智能体”都展示 MPA,并请求服务端分类过滤后的 Runtime 页。 | `node --test frontend/tests/newChatAgentPicker.test.mjs frontend/tests/myAgents.test.mjs`; `npm --prefix frontend test` | pass | -| All | `T-4` | Studio 构建产物与源码变化一致。 | `npm --prefix frontend run build -- --mode development`; `npm --prefix frontend run test:webui-assets`; `git diff --check` | pass | - -## 风险 - -历史 MPA Runtime 如果没有 `veadk:agent-type=mpa`,仍会被归为通用智能体,直到执行显式标签修复。本修复不新增修复命令;它保证新建和更新后的 MPA Runtime 分类可靠,修复服务端分类分页语义,并避免按名称误纳入旧测试镜像。 diff --git a/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-disable-application-apm.md b/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-disable-application-apm.md deleted file mode 100644 index 3e0669d38..000000000 --- a/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-disable-application-apm.md +++ /dev/null @@ -1,46 +0,0 @@ -# Disable application APM in the BytePlus Studio profile - -[中文版](2026-10-14-disable-application-apm.zh.md) - -Change ID: mpa-byteplus-application-apm. Date: 2026-10-14. Status: superseded. -Contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), CON-17. -Successor: [Restore application APM](2026-10-14-restore-application-apm.md). - -## Background, goals and scope - -A BytePlus Runtime failed during Agent initialization after APM GetAppKey returned InternalServiceTimeout. The built-in overseas profile inherits ENABLE_APMPLUS=true and FORCE_APMPLUS_EXPORTER_REGISTRATION=true from the domestic profile. The user explicitly approved setting these two BytePlus defaults to false to test application startup. No production log, credential or signed URL is retained here. - -Goal: stop requesting application APM initialization by default for BytePlus managed Studio creation. Live verification showed that `ApmplusEnable=true` rewrites the Runtime `ENABLE_APMPLUS` value to `true`, so the BytePlus control-plane setting must also be false. Non-goals: changing domestic defaults, general agents, Worker telemetry, agentkit-mpa-agent code, retry/network recovery, or existing cloud resources. - -## Requirements and scenarios - -- FR-1: Given the built-in BytePlus profile, both ENABLE_APMPLUS and FORCE_APMPLUS_EXPORTER_REGISTRATION are string false in the resolved Runtime environment and deployment payload. -- FR-2: Given the built-in Volcengine profile, both application flags and ApmplusEnable remain true. Given BytePlus, both application flags and ApmplusEnable are false. Provider calls return fresh profiles and cannot contaminate each other. -- FR-3: Retain trusted explicit configuration overrides, existing request hashing, cancellation and partial-failure handling. No API, credential, schema or task-state changes. Do not automatically update an existing Runtime or bypass a conflicting pending deployment. - -## Design, impact and tasks - -T-1: Add a failing provider-parametrized regression to tests/integrations/mpa_managed/test_byteplus.py, checking raw defaults, resolved profile and apply_runtime_settings output without cloud calls (FR-1/FR-2). -T-2: Override the two application keys and `apmplus-enable` in the existing BytePlus branch of studio_profile_values (FR-1/FR-3). The maintained CON-17 and bilingual managed README document the provider distinction. -T-3: Run targeted tests, managed/startup regression with two workers, changed-file Ruff/Pyright, scoped secret scan and bilingual/link/whitespace checks. No frontend assets change; browser/build checks are not applicable because no frontend or HTTP interface changes. - -## Review, risks and acceptance - -User approval: explicit requests on 2026-10-14 to disable the two BytePlus flags and, after live evidence showed the platform override, to disable BytePlus `ApmplusEnable`. Direct design review (review-spec unavailable): scope, configuration precedence, provider isolation, security, compatibility, tests and bilingual equivalence reviewed; no blockers. No new dependencies or sensitive values. BytePlus application and platform APM initialization are disabled; Volcengine remains unchanged. This does not prove startup, OV or chat succeeds, and does not resolve separately diagnosed failures. Changed Runtime settings may conflict with an already pending deployment hash; use a new request for the trial. - -- AC-1: BytePlus resolved environment and payload contain both false values. -- AC-2: Domestic environment and platform ApmplusEnable remain unchanged; BytePlus ApmplusEnable is false; repeated provider loads remain isolated. -- AC-3: Local checks pass; live verification is reported separately, without cloud writes. - -## Verification and delivery - -2026-10-14, local diff based on 2ffe6e14: not_run, implementation checks pending. not_run, live creation/restart/commit/push and all-files pre-commit, none requested. not_applicable, frontend/browser/build and harness smoke, no changes to these boundaries. Preserve unrelated local changes. - -2026-10-14 results, scoped local change on 2ffe6e14: -- pass: pre-fix regression reproduced one BytePlus failure while the domestic case passed. -- pass: `uv run --extra dev pytest -q tests/integrations/mpa_managed/test_byteplus.py tests/integrations/mpa_managed/test_config.py`, 105 tests. -- pass: `uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/cli/test_studio_startup_imports.py`, 1,093 tests, four dependency deprecation warnings; two workers chosen for the local environment. -- pass: Ruff 0.11.12 check/format and Pyright `--pythonpath .venv/bin/python` on studio_profile.py and test_byteplus.py, zero errors/warnings. -- pass: repository-configured Gitleaks hook on an isolated snapshot of scoped source/tests/docs; paired requirement/task/acceptance identifiers, relative links and diff whitespace. -- not_run: live cloud startup and chat; user will restart Studio and submit a new request. No cloud resource, task record or service was changed. No commit/push requested, so synchronization and all-files pre-commit remain not_run. -Final review: only two BytePlus application flags changed in production code. Domestic and platform APM flags retain their values; no retry bypass, frontend artifact, dependency or general-agent changes. AC-1/AC-2/AC-3 satisfied by local evidence; live deployment remains unverified. diff --git a/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-disable-application-apm.zh.md b/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-disable-application-apm.zh.md deleted file mode 100644 index 46c769594..000000000 --- a/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-disable-application-apm.zh.md +++ /dev/null @@ -1,46 +0,0 @@ -# 关闭 BytePlus Studio 配置中的应用 APM - -[English](2026-10-14-disable-application-apm.md) - -变更 ID:mpa-byteplus-application-apm。日期:2026-10-14。状态:superseded。 -契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-17。 -后续变更:[恢复应用 APM](2026-10-14-restore-application-apm.zh.md)。 - -## 背景、目标和范围 - -一个 BytePlus Runtime 在 APM GetAppKey 返回 InternalServiceTimeout 后,因 Agent 初始化失败而退出。内置海外配置从国内继承了 ENABLE_APMPLUS=true 和 FORCE_APMPLUS_EXPORTER_REGISTRATION=true。用户明确同意将这两个 BytePlus 默认值改为 false,测试应用启动。本文不保留生产日志、凭据或签名 URL。 - -目标:BytePlus 托管 Studio 创建默认不再请求初始化应用 APM。真实验证表明 `ApmplusEnable=true` 会将 Runtime 的 `ENABLE_APMPLUS` 重写为 `true`,因此 BytePlus 控制面设置也必须为 false。非目标:修改国内默认值、通用智能体、Worker 观测、agentkit-mpa-agent 代码、重试/网络恢复或已有云资源。 - -## 需求和场景 - -- FR-1:使用内置 BytePlus 配置时,解析后的 Runtime 环境和部署请求中 ENABLE_APMPLUS 与 FORCE_APMPLUS_EXPORTER_REGISTRATION 都为字符串 false。 -- FR-2:使用内置火山配置时,两个应用开关及 ApmplusEnable 均保持 true。使用 BytePlus 时,两个应用开关及 ApmplusEnable 均为 false。每次调用返回独立配置,平台之间不能相互污染。 -- FR-3:保留可信服务端显式配置覆盖、现有请求哈希、取消和部分失败处理。不修改 API、凭据、数据库结构或任务状态。不自动更新已有 Runtime,也不绕过未完成部署的配置冲突。 - -## 设计、影响和任务 - -T-1:在 tests/integrations/mpa_managed/test_byteplus.py 添加先失败的平台参数化回归,检查原始默认值、解析配置及 apply_runtime_settings 输出,不调用云接口(FR-1/FR-2)。 -T-2:在 studio_profile_values 现有 BytePlus 分支覆盖两个应用键及 `apmplus-enable`(FR-1/FR-3)。维护 CON-17 和双语托管 README,说明平台差异。 -T-3:执行目标测试、两进程托管/启动回归、变更文件 Ruff/Pyright、范围密钥扫描和双语/链接/空白检查。不修改前端产物;没有前端或 HTTP 接口变化,浏览器与构建检查不适用。 - -## 评审、风险和验收 - -用户批准:2026-10-14 明确要求关闭两个 BytePlus 开关,并在真实证据表明平台会覆盖后要求关闭 BytePlus `ApmplusEnable`。直接设计评审(review-spec 不可用):已检查范围、配置优先级、平台隔离、安全、兼容性、测试和双语一致性,无阻塞项。不新增依赖或敏感值。BytePlus 应用及平台 APM 初始化均关闭,火山引擎保持不变。这不证明启动、OV 或聊天成功,也不解决其他已诊断故障。Runtime 设置变化可能与未完成部署哈希冲突;本次试验应使用新请求。 - -- AC-1:BytePlus 解析环境和请求包含两个 false 值。 -- AC-2:国内环境与平台 ApmplusEnable 不变;BytePlus ApmplusEnable 为 false;多次加载平台配置保持隔离。 -- AC-3:本地检查通过;真实验证单独报告,不执行云写入。 - -## 验证与交付 - -2026-10-14,基于 2ffe6e14 的本地差异:not_run,实施检查待执行。not_run,真实创建/重启/提交/推送及全文件 pre-commit,均未要求。not_applicable,前端/浏览器/构建和 harness smoke,未修改这些边界。保留无关本地改动。 - -2026-10-14 结果,基于 2ffe6e14 的本次局部变更: -- pass:修复前回归复现一项 BytePlus 失败,国内用例通过。 -- pass:`uv run --extra dev pytest -q tests/integrations/mpa_managed/test_byteplus.py tests/integrations/mpa_managed/test_config.py`,105 项测试。 -- pass:`uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/cli/test_studio_startup_imports.py`,1,093 项测试、四项依赖弃用警告;依据本地环境选用两个进程。 -- pass:对 studio_profile.py 与 test_byteplus.py 执行 Ruff 0.11.12 check/format 和 Pyright `--pythonpath .venv/bin/python`,零错误/警告。 -- pass:在隔离快照中按仓库配置执行本次源码/测试/文档的 Gitleaks hook;双语需求/任务/验收标识、相对链接与差异空白检查通过。 -- not_run:真实云启动与聊天;用户将重启 Studio 并提交新请求。没有修改云资源、任务记录或服务。未要求提交/推送,因此同步和全文件 pre-commit 仍为 not_run。 -最终评审:生产代码仅修改两个 BytePlus 应用开关。国内与平台 APM 保持原值;没有绕过重试,也不修改前端产物、依赖或通用智能体。本地证据满足 AC-1/AC-2/AC-3;真实部署仍未验证。 diff --git a/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-restore-application-apm.md b/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-restore-application-apm.md deleted file mode 100644 index 90f087ca6..000000000 --- a/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-restore-application-apm.md +++ /dev/null @@ -1,75 +0,0 @@ -# Restore application APM in the BytePlus Studio profile - -[中文版](2026-10-14-restore-application-apm.zh.md) - -Change ID: mpa-byteplus-application-apm-restore. Date: 2026-10-14. Status: implemented. -Contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), CON-17. -Predecessor: [Disable application APM](2026-10-14-disable-application-apm.md). - -## Background, goals and scope - -BytePlus application APM was temporarily disabled to bypass an overseas -`GetAppKey` timeout. Live creation then reached Runtime readiness, but the -platform removed `ENABLE_APMPLUS=false` from the effective environment and the -creation verifier waited for an exact configuration match. The user has now -requested restoration of all APM bypass settings. - -Goal: restore the inherited Runtime defaults for BytePlus: -`ApmplusEnable=true`, `ENABLE_APMPLUS=true`, and -`FORCE_APMPLUS_EXPORTER_REGISTRATION=true`. Non-goals: fixing the upstream -APMPlus timeout, changing readiness reconciliation, mutating existing Runtime -resources, or changing Volcengine defaults. - -## Requirements and scenarios - -- FR-1: New BytePlus profiles inherit all three enabled APM defaults. -- FR-2: Volcengine defaults remain enabled and provider profile calls remain - isolated. -- FR-3: Existing Runtime resources and submitted task snapshots are unchanged; - a restarted Studio and a new request are required to consume the restored - defaults. - -## Design, impact and tasks - -- T-1: Change the provider regression to require enabled APM defaults for both - providers and confirm it fails before implementation (FR-1/FR-2). -- T-2: Remove only the BytePlus APM overrides from - `studio_profile_values`; preserve image, model, OV and provider settings - (FR-1/FR-3). -- T-3: Reconcile this PRD, its predecessor and the bilingual component contract; - run targeted tests, Ruff, Pyright, bilingual/link and whitespace checks. - -No API, schema, credential, frontend or generated-asset contract changes. -The known BytePlus APMPlus service timeout can again make Runtime startup fail; -this rollback is intentional and does not claim that upstream APMPlus works. - -## Review, risks and acceptance - -User approval: on 2026-10-14 the user explicitly requested restoring -`ENABLE_APMPLUS` and the related APM bypass settings. Direct design review: -scope, provider isolation, compatibility, security and testability were checked; -no blocker remains. The primary risk is recurrence of the already observed -`GetAppKey InternalServiceTimeout`. - -- AC-1: Both providers resolve and deploy all three APM settings as enabled. -- AC-2: Targeted tests and static checks pass. -- AC-3: Documentation is bilingual and records that no existing Runtime is - mutated. - -## Verification and delivery - -2026-10-14, local diff based on the current worktree: - -- pass: the changed regression failed for BytePlus before implementation and - passed after removing the three overrides. -- pass: `uv run --extra dev pytest -q - tests/integrations/mpa_managed/test_byteplus.py - tests/integrations/mpa_managed/test_config.py`, 105 tests. -- pass: `uv run --extra dev pytest -n 2 -q - tests/integrations/mpa_managed tests/cli/test_studio_startup_imports.py`, - 1,093 tests with four dependency deprecation warnings. -- pass: Ruff 0.11.12 check/format and Pyright on the changed Python files. -- pass: paired-document presence, relative links and scoped diff whitespace. -- not_run: live cloud creation, commit, push and all-files pre-commit were not - requested. -- not_applicable: frontend build/browser checks; no frontend behavior changes. diff --git a/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-restore-application-apm.zh.md b/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-restore-application-apm.zh.md deleted file mode 100644 index 3f230b88b..000000000 --- a/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-restore-application-apm.zh.md +++ /dev/null @@ -1,62 +0,0 @@ -# 恢复 BytePlus Studio 配置中的应用 APM - -[English](2026-10-14-restore-application-apm.md) - -变更 ID:mpa-byteplus-application-apm-restore。日期:2026-10-14。状态:implemented。 -契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-17。 -前序变更:[关闭应用 APM](2026-10-14-disable-application-apm.zh.md)。 - -## 背景、目标和范围 - -此前为绕过海外 `GetAppKey` 超时,临时关闭了 BytePlus 应用 APM。真实创建随后到达 -Runtime 就绪,但平台从实际环境中移除了 `ENABLE_APMPLUS=false`,创建验证器因要求 -配置完全一致而持续等待。用户现已要求恢复全部 APM 绕过设置。 - -目标:恢复 BytePlus 继承的 Runtime 默认值:`ApmplusEnable=true`、 -`ENABLE_APMPLUS=true` 和 `FORCE_APMPLUS_EXPORTER_REGISTRATION=true`。 -非目标:修复上游 APMPlus 超时、修改就绪协调、变更已有 Runtime 资源或修改火山引擎 -默认值。 - -## 需求和场景 - -- FR-1:新的 BytePlus 配置继承三个已启用的 APM 默认值。 -- FR-2:火山引擎默认值继续启用,各平台配置调用保持隔离。 -- FR-3:已有 Runtime 资源和已提交任务快照不变;须重启 Studio 并创建新请求后才使用 - 恢复后的默认值。 - -## 设计、影响和任务 - -- T-1:将平台回归测试改为要求两个平台均启用 APM,并确认实现前失败(FR-1/FR-2)。 -- T-2:仅从 `studio_profile_values` 删除 BytePlus APM 覆盖项;保留镜像、模型、OV 和 - 平台设置(FR-1/FR-3)。 -- T-3:同步本文、前序 PRD 和双语组件契约;运行定向测试、Ruff、Pyright、双语/链接和 - 空白检查。 - -不修改 API、数据库结构、凭据、前端或生成产物契约。已知的 BytePlus APMPlus 服务超时 -可能再次导致 Runtime 启动失败;这是有意回滚,不代表上游 APMPlus 已恢复。 - -## 评审、风险和验收 - -用户批准:2026-10-14 用户明确要求恢复 `ENABLE_APMPLUS` 及相关 APM 绕过设置。 -直接设计评审:已检查范围、平台隔离、兼容性、安全和可测试性,无阻塞项。主要风险是 -已观察到的 `GetAppKey InternalServiceTimeout` 再次出现。 - -- AC-1:两个平台解析和部署的三个 APM 设置均为启用。 -- AC-2:定向测试和静态检查通过。 -- AC-3:文档双语一致,并记录不修改已有 Runtime。 - -## 验证与交付 - -2026-10-14,基于当前工作区差异: - -- pass:修改后的回归测试在实施前对 BytePlus 失败,删除三个覆盖项后通过。 -- pass:`uv run --extra dev pytest -q - tests/integrations/mpa_managed/test_byteplus.py - tests/integrations/mpa_managed/test_config.py`,105 项测试。 -- pass:`uv run --extra dev pytest -n 2 -q - tests/integrations/mpa_managed tests/cli/test_studio_startup_imports.py`, - 1,093 项测试,四项依赖弃用警告。 -- pass:对变更 Python 文件执行 Ruff 0.11.12 check/format 和 Pyright。 -- pass:双语文档存在、相对链接和局部差异空白检查。 -- not_run:未要求真实云创建、提交、推送和全文件 pre-commit。 -- not_applicable:前端构建/浏览器检查;没有前端行为变化。 diff --git a/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-server-app-key.md b/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-server-app-key.md deleted file mode 100644 index ef2b688c6..000000000 --- a/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-server-app-key.md +++ /dev/null @@ -1,48 +0,0 @@ -# Inject a deployment-account APM AppKey into BytePlus Studio Runtimes - -[中文版](2026-10-14-server-app-key.zh.md) - -Change ID: mpa-byteplus-server-app-key. Date: 2026-10-14. Status: implemented. -Contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), CON-17. -Predecessor: [APM restoration](2026-10-14-restore-application-apm.md). - -## Evidence, goals and non-goals - -Runtime Agent initialization fails when its bundled VeADK GetAppKey call times out. A read-only probe of apmplus-server.ap-southeast-1.byteplusapi.com using the deployment account succeeded. Bundled VeADK 1.1.5 supports OBSERVABILITY_OPENTELEMETRY_APMPLUS_API_KEY and skips automatic lookup when present; this was verified against installed source in isolation. No real key or production log is recorded here. - -Goal: Studio obtains the BytePlus AppKey before resource preparation and injects it into Runtime Envs. Restore the inherited enabled APM defaults as previously requested, replacing the temporary bypass. Non-goals: changing the image, global VeADK host selection, domestic/generic-agent/CLI behavior, OTLP endpoint selection, sandbox credential compatibility, existing cloud resources or network retry recovery. - -## Requirements and scenarios - -- FR-1: After verifying the deployment account and resolving the model key, Studio BytePlus creation calls GetAppKey with service apmplus_server, version 2024-07-30, signing region ap-southeast-1 and host apmplus-server.ap-southeast-1.byteplusapi.com. Refresh and verify credentials for each attempt; include STS token and X-Apmplus-Region. Resolve before IAM/PG/network/resource mutations. Domestic and non-Studio callers do not call this helper. -- FR-2: Accept only a usable data.app_key string; inject OBSERVABILITY_OPENTELEMETRY_APMPLUS_API_KEY into Runtime creation/update Envs. Do not mutate the caller profile or persist the raw key in task/bootstrap/deployment records, browser responses, diagnostics or reports. Valid trusted explicit Runtime keys are preserved; blank values use discovery; malformed/masked keys fail safely. -- FR-3: At most three transient read attempts with 10/30-second HTTP connect/read limits and a 45-second per-attempt async deadline; retry delays 0.2/0.5 seconds. Cancellation prevents later attempts or mutations. Permission, protocol and permanent provider failures stop without fallback. Diagnostics retain only allowlisted operation/category/attempt/outcome/errorCode/requestId, including HTTP error metadata. Fail before resource preparation with a safe exception. -- FR-4: Restore BytePlus ApmplusEnable=true, ENABLE_APMPLUS=true and FORCE_APMPLUS_EXPORTER_REGISTRATION=true; preserve domestic values, explicit configuration and provider isolation. No submitted snapshot upgrade or existing Runtime update. Reads on retries obtain the account key again; unchanged pending request hashes remain authoritative and changed keys/configurations fail rather than silently redeploying. - -## Design and tasks - -T-1: Add failing isolated helper and service regressions: signed request fields, Runtime environment delivery, trusted configuration reuse, exclusion of domestic and CLI paths, malformed responses, errors/retries/cancellation and pre-mutation failure. Update enabled-default regression (FR-1 through FR-4). -T-2: Add a small managed apm_key module, reuse signed transport and existing diagnostic metadata validators, allowlist get_apm_key and wire service.provision. Hold the fetched key only in a local variable until Runtime environment construction. Existing request/hash and secret storage rules remain unchanged. -T-3: Update bilingual CON-17, managed README and frontend README; record review and checks. Run targeted and managed/startup regression with two workers, changed-Python Ruff/Pyright, configured scoped Gitleaks, paired identifiers/links and whitespace checks. - -## Review, security, risks and acceptance - -User approval: explicit 2026-10-14 request to implement the described server-side key retrieval/injection; earlier request withdrew the APM bypass. Direct review (review-spec unavailable): provider scope, credential refresh, safe failure diagnostics, secret persistence, bounded cancellation, compatibility and bilingual equivalence reviewed; no blockers. Runtime environment access remains privileged. Deployment credentials additionally need apmplus_server:GetAppKey; no Runtime role policy expansion or long-lived deployment AK/SK injection. Platform rewriting of disabled APM flags motivated restoring the enabled defaults. - -AC-1: Only Studio BytePlus looks up the key before mutations and the actual Runtime payload contains it. AC-2: Safe failure/cancellation and secret exclusions pass regression. AC-3: Domestic/generic/CLI paths and pending-input guards remain unchanged; checks and docs reconciled. Real APM export and Runtime startup/chat are separate live tests. The known BytePlus sandbox IAM defect is not fixed here. Old incomplete deployments may need a new request because their original payload lacks this environment variable. - -## Verification - -2026-10-14, local diff based on 2ffe6e14: not_run, implementation checks pending. not_run, live creation/update/restart/commit/push and all-files pre-commit, none authorized for this implementation. not_applicable, frontend/browser/build and harness smoke, no affected UI/API/event boundaries or artifacts. Preserve unrelated local changes. - -2026-10-14 final verification (local diff on 2ffe6e14): -- pass: pre-implementation helper regression failed collection because the module was absent; the first implementation run also exposed the missing diagnostic operation/service wiring, which was completed before final tests. -- pass: `uv run --extra dev pytest -q tests/integrations/mpa_managed/test_apm_key.py tests/integrations/mpa_managed/test_byteplus.py tests/integrations/mpa_managed/test_service.py`, 213 tests. Four additional isolation/invalid-explicit-key cases subsequently passed with the final 23-test helper suite. -- pass: `uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/cli/test_studio_startup_imports.py`, 1,112 tests, two dependency deprecation warnings; two workers selected for local resources. Subsequent edits only added tests/docs, with targeted rerun. -- pass: Ruff 0.11.12 check/format and Pyright `--pythonpath .venv/bin/python` on apm_key.py, service.py, diagnostics.py, test_apm_key.py and test_service.py, zero errors/warnings. An initial test double's undeclared account_id assignment was corrected with monkeypatch; test_service formatting was corrected. -- pass: `uv build --wheel --out-dir /tmp/mpa-app-key-wheel`; archive contains the new helper and wired service, excludes .env. No new frontend build or dependency changes. -- blocked: live read-only new-helper smoke could not start because Studio no longer listened on port 8000 and its process credentials were unavailable. The earlier dedicated-host GetAppKey probe succeeded, but was not this new helper. No Runtime/resource update occurred. Live injection, startup, tracing and chat remain not_run. -- pass: final scoped repository-configured Gitleaks, bilingual IDs/links and diff whitespace (recorded after execution below). -Final review: Runtime handoff is verified in the existing full fake provisioner, and its result/registry contain no raw AppKey. Global SDK GetAppKey host selection and Runtime/sandbox IAM clients are unchanged. All in-scope tasks and local acceptance checks complete; existing cloud deployments remain unchanged. No commit/push/service restart. - -Final documentation checks: pass, scoped relative links and paired identifiers. An unrelated pre-existing frontend README link to an optional local skill example is absent and was not changed. An initial Gitleaks scan flagged a documentation phrase; prose was reworded and the unchanged repository-configured scan passed. No exclusions were added. diff --git a/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-server-app-key.zh.md b/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-server-app-key.zh.md deleted file mode 100644 index 9e6e4fae6..000000000 --- a/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-server-app-key.zh.md +++ /dev/null @@ -1,48 +0,0 @@ -# 向 BytePlus Studio Runtime 注入部署账号的 APM AppKey - -[English](2026-10-14-server-app-key.md) - -变更 ID:mpa-byteplus-server-app-key。日期:2026-10-14。状态:implemented。 -契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-17。 -前置设计:[APM 恢复](2026-10-14-restore-application-apm.zh.md)。 - -## 证据、目标和非目标 - -Runtime 内置 VeADK 的 GetAppKey 超时时,Agent 初始化失败。使用部署账号对 apmplus-server.ap-southeast-1.byteplusapi.com 的只读探测成功。内置 VeADK 1.1.5 支持 OBSERVABILITY_OPENTELEMETRY_APMPLUS_API_KEY,有值时跳过自动获取;已对安装源码执行隔离验证。本文不记录真实 Key 或生产日志。 - -目标:Studio 在准备资源前获取 BytePlus AppKey,注入 Runtime Envs。按此前用户要求恢复继承的启用 APM 默认值,替代临时绕过。非目标:修改镜像、全局 VeADK Host 选择、国内/通用智能体/CLI 行为、OTLP 上报地址、沙箱凭据兼容、已有云资源或网络重试恢复。 - -## 需求和场景 - -- FR-1:核验部署账号并解析模型 Key 后,Studio BytePlus 创建调用 GetAppKey,service 为 apmplus_server、version 为 2024-07-30、签名地域为 ap-southeast-1、host 为 apmplus-server.ap-southeast-1.byteplusapi.com。每次尝试刷新并核验凭据,传递 STS Token 与 X-Apmplus-Region。在 IAM/PG/网络/资源写入前解析。国内与非 Studio 调用者不调用此辅助函数。 -- FR-2:仅接受可用的 data.app_key 字符串;将 OBSERVABILITY_OPENTELEMETRY_APMPLUS_API_KEY 注入 Runtime 创建/更新 Envs。不修改调用者配置,不把原始 Key 持久化到任务/启动/部署记录、浏览器响应、诊断或报告。保留可信配置中的有效显式 Runtime Key;空值走发现,格式错误或遮罩 Key 安全失败。 -- FR-3:临时错误最多三次读取,每次 HTTP 连接/读取限制 10/30 秒,异步期限 45 秒;重试间隔 0.2/0.5 秒。取消后不再尝试或写资源。权限、协议和永久服务错误停止,不回退。诊断仅保留白名单 operation/category/attempt/outcome/errorCode/requestId,包含 HTTP 错误元信息。准备资源前抛出安全异常。 -- FR-4:恢复 BytePlus ApmplusEnable=true、ENABLE_APMPLUS=true 与 FORCE_APMPLUS_EXPORTER_REGISTRATION=true;国内值、显式配置和平台隔离不变。不升级已提交快照,不更新已有 Runtime。重试重新读取账号 Key;保留未完成请求哈希检查,Key/配置变化时失败,不静默重部署。 - -## 设计和任务 - -T-1:先添加失败的隔离辅助函数与服务回归:签名 Host/账号/STS、原始 Key 传递、显式 Key、国内/CLI 排除、错误响应、错误/重试/取消和写资源前失败。更新启用默认值回归(FR-1 至 FR-4)。 -T-2:增加小型 managed apm_key 模块,复用签名传输及现有诊断元信息校验,白名单新增 get_apm_key,接入 service.provision。取得的 Key 仅保存在局部变量中,直到构造 Runtime 环境。现有请求/哈希与秘密存储规则不变。 -T-3:更新双语 CON-17、托管 README 和 frontend README,记录评审与检查。执行目标测试及两进程托管/启动回归、变更 Python 文件 Ruff/Pyright、仓库配置的范围 Gitleaks、双语标识/链接与空白检查。 - -## 评审、安全、风险和验收 - -用户批准:2026-10-14 明确要求实现上述服务端获取/注入 Key;此前已要求撤销 APM 绕过。直接评审(review-spec 不可用):已检查平台范围、凭据刷新、安全失败诊断、秘密持久化、取消期限、兼容和双语一致性,无阻塞项。Runtime 环境访问仍需特权。部署凭据额外需要 apmplus_server:GetAppKey;不扩展 Runtime 角色策略,不注入长期部署 AK/SK。平台重写禁用 APM 的环境变量,是恢复启用默认值的原因。 - -AC-1:仅 Studio BytePlus 在写资源前获取 Key,实际 Runtime 请求含该 Key。AC-2:安全失败/取消及秘密排除回归通过。AC-3:国内/通用智能体/CLI 路径与未完成输入保护不变,检查和文档已同步。真实 APM 上报及 Runtime 启动/聊天另行验证。本次不修复已知 BytePlus 沙箱 IAM 缺陷。旧未完成部署的原请求不含此变量,可能需要新请求。 - -## 验证 - -2026-10-14,基于 2ffe6e14 的本地差异:not_run,实施检查待执行。not_run,真实创建/更新/重启/提交/推送及全文件 pre-commit,本次实施未授权这些操作。not_applicable,前端/浏览器/构建与 harness smoke,未影响 UI/API/事件边界或产物。保留无关本地改动。 - -2026-10-14 最终验证(基于 2ffe6e14 的本地差异): -- pass:实施前回归因模块不存在而收集失败;第一轮实施运行还暴露诊断操作白名单/服务接线缺失,最终测试前已补齐。 -- pass:`uv run --extra dev pytest -q tests/integrations/mpa_managed/test_apm_key.py tests/integrations/mpa_managed/test_byteplus.py tests/integrations/mpa_managed/test_service.py`,213 项测试。随后新增四项隔离/无效显式 Key 用例,最终辅助函数套件 23 项全部通过。 -- pass:`uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/cli/test_studio_startup_imports.py`,1,112 项测试、两项依赖弃用警告;依据本地资源选用两个进程。此后仅增加测试/文档,执行了目标重跑。 -- pass:apm_key.py、service.py、diagnostics.py、test_apm_key.py 和 test_service.py 的 Ruff 0.11.12 check/format 与 Pyright `--pythonpath .venv/bin/python`,零错误/警告。初始替身未声明 account_id 的赋值已改用 monkeypatch,test_service 格式已修正。 -- pass:`uv build --wheel --out-dir /tmp/mpa-app-key-wheel`;归档含新辅助模块和接线服务,不含 .env。不新增前端构建或依赖变化。 -- blocked:Studio 已不再监听 8000,无法取得进程凭据,新辅助函数真实只读探测未能启动。此前专用 Host 的 GetAppKey 探测成功,但不是本次新辅助函数。没有更新 Runtime/资源。真实注入、启动、观测和聊天仍为 not_run。 -- pass:最终按仓库配置执行范围 Gitleaks、双语标识/链接与差异空白检查(执行后记录见下)。 -最终评审:现有完整模拟创建流程已验证 Runtime 传递,结果/注册库不含原始 AppKey。全局 SDK GetAppKey Host 选择与 Runtime/沙箱 IAM 客户端不变。本次任务和本地验收检查完成,已有云部署保持原样。没有提交/推送/服务重启。 - -最终文档检查:pass,本次范围相对链接和双语标识通过。frontend README 中一个无关的历史可选本地技能示例链接不存在,未改动。初始 Gitleaks 扫描标记了一处文档表述,调整文字后,沿用仓库原配置的扫描通过;未新增排除规则。 diff --git a/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-15-otlp-endpoint.md b/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-15-otlp-endpoint.md deleted file mode 100644 index 3582ef8c9..000000000 --- a/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-15-otlp-endpoint.md +++ /dev/null @@ -1,38 +0,0 @@ -# BytePlus Studio MPA OTLP endpoint - -[中文版](2026-10-15-otlp-endpoint.zh.md) - -## Metadata and evidence -Change ID: mpa-byteplus-otlp-endpoint. Date: 2026-10-15. Status: implemented. -The SDK default uses the domestic volces.com suffix even for overseas regions. The operator supplied the BytePlus Singapore collector address. AppKey retrieval alone does not select this collector. - -## Scope and requirements -FR-1: New BytePlus Studio profiles inject `OBSERVABILITY_OPENTELEMETRY_APMPLUS_ENDPOINT=http://apmplus-ap-southeast-1.bytepluses.com:4317` into Runtime Envs. -FR-2: Volcengine profiles, generic agents, global SDK defaults, AppKey retrieval and APM enablement remain unchanged. Existing Runtime resources and submitted task snapshots are not migrated. -FR-3: Existing trusted Runtime environment override precedence remains unchanged. -Given a new BytePlus request, its generated Runtime payload carries the endpoint. Given a domestic request, no overseas endpoint is added. - -## Design and tasks -T-1: Extend provider isolation regression to fail on the missing endpoint, checking raw defaults, loaded profiles and Runtime payloads, including explicit override behavior. -T-2: Add the constant value only to the BytePlus branch of `veadk/integrations/mpa/managed/studio_profile.py`. -T-3: Reconcile Studio creation CON-17 and managed README in both languages; run targeted tests, Ruff, Pyright and whitespace/link checks. -Related contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), CON-17. Affected test: `tests/integrations/mpa_managed/test_byteplus.py`. -No new API, schema, credentials, network calls, timeout, cancellation or concurrency behavior. Existing environment-to-payload handling is reused. Only the default collector configuration changes. - -## Review and acceptance -User approval: explicit request on 2026-10-15 to inject the supplied endpoint for BytePlus creation without changing domestic behavior. Direct design review performed because review-spec is unavailable: provider boundary, override precedence, retry snapshots, security, compatibility and bilingual equivalence checked; no blockers. -AC-1: BytePlus default and resulting payload contain the endpoint. AC-2: Domestic default/payload omit it and explicit overrides survive. AC-3: scoped checks pass with live verification separately stated. -Risk: actual collector reachability and accepted traces require cloud validation. Existing deployments need a separately authorized update; restart Studio for new requests. This change does not itself prove successful APM export. - -## Verification -2026-10-15, local change on 4997196d, only Studio profile, provider regression and paired documentation: -- pass: regression before implementation reproduced BytePlus KeyError while domestic passed. -- pass: `uv run --extra dev pytest -q tests/integrations/mpa_managed/test_byteplus.py tests/integrations/mpa_managed/test_apm_key.py tests/integrations/mpa_managed/test_service.py` — 219 tests, two dependency deprecation warnings. -- pass: `uv tool run --from ruff==0.11.12 ruff check` and `ruff format --check` on the two changed Python files; `uv tool run pyright --pythonpath .venv/bin/python` on those files — zero errors/warnings. Temporary tools used because local Ruff/Pyright executables are absent, following the existing user preference to record verification only. -- pass: `git diff --check`. Paired requirement IDs and relative links checked. -- not_applicable: frontend build/browser and harness smoke; no UI, API or harness code changed. -- not_run: live cloud deployment/export verification and all-files pre-commit; no deployment or commit requested. Scoped hooks checked separately below. -Review: the domestic branch returns before the new environment entry; no shared SDK change or secret added. Existing unrelated frontend lockfile changes preserved. AC-1/AC-2 covered by generated-payload tests; live Trace delivery remains unverified. - - -- pass: `uv run --extra dev pre-commit run --files ` — Ruff check/format and hardcoded-secret scan passed; dependency/YAML hooks skipped because their file types are unaffected. diff --git a/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-15-otlp-endpoint.zh.md b/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-15-otlp-endpoint.zh.md deleted file mode 100644 index f067bfe58..000000000 --- a/prd-spec/bugfixes/mpa-byteplus-apm/2026-10-15-otlp-endpoint.zh.md +++ /dev/null @@ -1,38 +0,0 @@ -# BytePlus Studio MPA OTLP 上报地址 - -[English](2026-10-15-otlp-endpoint.md) - -## 元数据与证据 -变更 ID:mpa-byteplus-otlp-endpoint。日期:2026-10-15。状态:implemented。 -SDK 的默认地址即便在海外地域也使用国内 volces.com 后缀。用户提供了 BytePlus 新加坡采集地址。仅获取 AppKey 不会选择该采集地址。 - -## 范围与需求 -FR-1:新的 BytePlus Studio 配置向 Runtime Envs 注入 `OBSERVABILITY_OPENTELEMETRY_APMPLUS_ENDPOINT=http://apmplus-ap-southeast-1.bytepluses.com:4317`。 -FR-2:火山配置、通用智能体、全局 SDK 默认值、AppKey 获取及 APM 启用状态不变。不迁移已有 Runtime 资源及已提交任务快照。 -FR-3:保留现有可信 Runtime 环境变量覆盖优先级。 -新建 BytePlus 请求生成的 Runtime 请求体携带上报地址;国内请求不增加海外上报地址。 - -## 设计与任务 -T-1:扩展供应商隔离回归,先验证缺失地址时失败,覆盖原始默认值、加载后的配置、Runtime 请求体及显式覆盖。 -T-2:仅在 `veadk/integrations/mpa/managed/studio_profile.py` 的 BytePlus 分支加入地址常量值。 -T-3:同步中英文 Studio 创建 CON-17 及托管模块 README;运行目标测试、Ruff、Pyright、空白和链接检查。 -相关契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-17。受影响测试:`tests/integrations/mpa_managed/test_byteplus.py`。 -不新增 API、数据库结构、凭据、网络调用、超时、取消或并发行为。复用既有环境变量到请求体的处理,仅修改默认采集地址配置。 - -## 评审与验收 -用户批准:2026-10-15 明确要求 BytePlus 创建注入所给地址且不影响国内行为。因 review-spec 不可用,直接完成设计评审:已检查供应商边界、覆盖优先级、重试快照、安全、兼容及双语一致性,无阻塞项。 -AC-1:BytePlus 默认值及最终请求体包含地址。AC-2:国内默认值及请求体不包含此项,显式覆盖仍生效。AC-3:范围内检查通过,单独说明云端验证情况。 -风险:采集地址实际连通性及 Trace 接收需要云端验证。已有部署需另行授权更新;新请求需重启 Studio。本变更不证明 APM 已成功上报。 - -## 验证 -2026-10-15,基于 4997196d 的本地修改,仅涉及 Studio 配置、供应商回归及双语文档: -- pass:实现前回归重现 BytePlus KeyError,国内用例通过。 -- pass:`uv run --extra dev pytest -q tests/integrations/mpa_managed/test_byteplus.py tests/integrations/mpa_managed/test_apm_key.py tests/integrations/mpa_managed/test_service.py` — 219 项通过,两个依赖弃用警告。 -- pass:对两个修改的 Python 文件运行 `uv tool run --from ruff==0.11.12 ruff check` 和 `ruff format --check`;运行 `uv tool run pyright --pythonpath .venv/bin/python` — 零错误、零警告。本地无 Ruff/Pyright 可执行文件,使用临时工具,遵循用户此前仅记录验证结果的偏好。 -- pass:`git diff --check`,已检查双语需求 ID 和相对链接。 -- not_applicable:前端构建/浏览器及 harness smoke;未修改 UI、API 或 harness 代码。 -- not_run:真实云端部署/上报验证及全文件 pre-commit;用户未要求部署或提交。范围内 hook 另行记录于下方。 -评审:国内分支在新增环境变量前返回;未修改共享 SDK 或加入密钥。保留原有无关前端锁文件修改。生成请求体测试覆盖 AC-1/AC-2;真实 Trace 投递仍未验证。 - - -- pass:`uv run --extra dev pre-commit run --files <八个本次修改文件>` — Ruff 检查/格式化及硬编码密钥扫描通过;依赖/YAML hook 因无对应文件改动而跳过。 diff --git a/prd-spec/bugfixes/mpa-byteplus-images/2026-10-14-byteplus-default-images.md b/prd-spec/bugfixes/mpa-byteplus-images/2026-10-14-byteplus-default-images.md deleted file mode 100644 index 6d62aeaa5..000000000 --- a/prd-spec/bugfixes/mpa-byteplus-images/2026-10-14-byteplus-default-images.md +++ /dev/null @@ -1,69 +0,0 @@ -# Use BytePlus-hosted defaults for managed MPA images - -[中文版](2026-10-14-byteplus-default-images.zh.md) - -- ID: `mpa-byteplus-default-images` -- Created/revised: 2026-10-14 -- Status: implemented -- Contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md) - -## Background and evidence - -BytePlus managed creation currently inherits the Volcengine Beijing Runtime and Worker image references unless trusted environment overrides are set. Live BytePlus creation reached `CreateTool` and failed with `provider_error`; the persisted task snapshot confirmed that it still selected the Beijing `cr.volces.com` Worker image. Public BytePlus repositories have now been prepared and anonymous manifest reads verified for: - -- `agentkit-platform-3001037806-ap-southeast-1.cr.bytepluses.com/mpa/mpa_agent:latest` -- `agentkit-platform-3001037806-ap-southeast-1.cr.bytepluses.com/mpa/mpa_codex_worker:latest` - -The verified manifests had digests `sha256:1db0caa3...` and `sha256:b9ea5f49...` respectively. The Worker image was built from `agentkit-mpa-codex-worker` `master@7aa11f0`. - -## Goals and non-goals - -Make new built-in BytePlus Studio requests select the BytePlus-hosted Runtime and Worker images by default. Preserve trusted server overrides and all domestic defaults. - -Do not mutate or retry existing tasks, change image validation, pin defaults to digests, rebuild images, alter cloud resources, change browser inputs, or change CLI/YAML profiles. - -## Scenarios and requirements - -- FR-1: `load_studio_profile(provider="byteplus")` defaults Runtime and Worker images to the two `ap-southeast-1.cr.bytepluses.com` latest references above. -- FR-2: Nonempty `VEADK_MPA_BYTEPLUS_RUNTIME_IMAGE` and `VEADK_MPA_BYTEPLUS_WORKER_IMAGE` remain higher-priority trusted overrides. -- FR-3: `load_studio_profile()` for Volcengine retains the Beijing Runtime and Worker defaults exactly. -- FR-4: Existing submitted tasks retain their persisted image snapshots. A new request after Studio restart is required to receive changed defaults. - -## Design and affected files - -Define the BytePlus registry once in the BytePlus branch of `studio_profile_values` and use its two image references as the fallback values behind the existing environment overrides. No frontend or HTTP shape changes are required because config/task APIs already expose and snapshot the resolved nonsecret defaults. - -Affected files: - -- `veadk/integrations/mpa/managed/studio_profile.py` -- `tests/integrations/mpa_managed/test_byteplus.py` -- paired Studio MPA component contract -- this paired bugfix design - -## Tasks, tests and acceptance - -| Requirement | Task | Acceptance | -| --- | --- | --- | -| FR-1/FR-3 | Add a failing provider-isolation assertion, then change the defaults | BytePlus selects the overseas references and Volcengine remains unchanged | -| FR-2 | Retain and run override coverage | Both trusted overrides still win | -| FR-4 | Update the paired contract and verify task semantics | Documentation states that only new task snapshots receive the defaults | - -Verification: targeted BytePlus pytest, changed-file Ruff and Pyright, paired-language/link review, and `git diff --check`. Frontend build and browser checks are not applicable because no frontend or HTTP contract changes. - -## Risks and review - -The `latest` tags are mutable by design and match the requested Studio convention; the verified digests are evidence, not pinned runtime values. Public manifest readability does not prove AgentKit `CreateTool` or Runtime application readiness, so a new isolated BytePlus creation remains required. Existing failed tasks cannot adopt the new images because changing their snapshot would violate retry identity. - -Direct design review (`review-spec` unavailable): scope, provider isolation, override precedence, task immutability, security, compatibility, testability and bilingual equivalence checked; no blockers. The user approved the preceding plan with “帮我改下”. No commit, push or cloud deployment is authorized by this approval. - -## Verification record - -2026-10-14 working diff: - -- pass: the new default assertion failed against the old implementation with both images still resolving to the Beijing registry. -- pass: `uv run --extra dev pytest tests/integrations/mpa_managed/test_byteplus.py tests/integrations/mpa_managed/test_tasks.py tests/integrations/mpa_managed/test_studio_shared_resources.py -q` — 47 passed, with two upstream deprecation warnings. -- pass: Ruff 0.11.12 check/format and Pyright for the changed Python files; `git diff --check`; paired links and semantics reviewed. -- pass: direct profile evaluation selected both BytePlus-hosted references while retaining both Beijing domestic references. -- pass: public BytePlus image manifests were readable before implementation. -- pass: after restarting Studio, live task `209b63d5-04ec-490f-95d8-41b7931891f4` persisted both BytePlus-hosted defaults and completed Worker creation/readiness. The task later failed independently at automatic OpenViking creation with `openvikingCreateUnknown`; Runtime creation was not reached. -- not_applicable: frontend build/browser checks; no frontend or HTTP contract changed. diff --git a/prd-spec/bugfixes/mpa-byteplus-images/2026-10-14-byteplus-default-images.zh.md b/prd-spec/bugfixes/mpa-byteplus-images/2026-10-14-byteplus-default-images.zh.md deleted file mode 100644 index a1b3a567d..000000000 --- a/prd-spec/bugfixes/mpa-byteplus-images/2026-10-14-byteplus-default-images.zh.md +++ /dev/null @@ -1,69 +0,0 @@ -# 托管 MPA 使用 BytePlus 托管的默认镜像 - -[English](2026-10-14-byteplus-default-images.md) - -- ID:`mpa-byteplus-default-images` -- 创建/修订日期:2026-10-14 -- 状态:implemented -- 契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md) - -## 背景与证据 - -BytePlus 托管创建当前在未设置可信环境变量覆盖时,会继承火山引擎北京地域的 Runtime 和 Worker 镜像地址。真实 BytePlus 创建已推进到 `CreateTool`,随后以 `provider_error` 失败;持久化任务快照确认仍选择北京 `cr.volces.com` Worker 镜像。现已准备以下 BytePlus 公共仓库,并验证匿名读取 manifest 成功: - -- `agentkit-platform-3001037806-ap-southeast-1.cr.bytepluses.com/mpa/mpa_agent:latest` -- `agentkit-platform-3001037806-ap-southeast-1.cr.bytepluses.com/mpa/mpa_codex_worker:latest` - -两份已验证 manifest 的 digest 分别为 `sha256:1db0caa3...` 和 `sha256:b9ea5f49...`。Worker 镜像基于 `agentkit-mpa-codex-worker` 的 `master@7aa11f0` 构建。 - -## 目标与非目标 - -使新的 BytePlus 内置 Studio 请求默认选择 BytePlus 托管的 Runtime 和 Worker 镜像。保留可信服务端覆盖及全部国内默认值。 - -不修改或重试已有任务,不改变镜像校验,不将默认值固定为 digest,不重新构建镜像,不修改云资源,不改变浏览器输入,也不改变 CLI/YAML 配置。 - -## 场景与需求 - -- FR-1:`load_studio_profile(provider="byteplus")` 默认使用上文两个 `ap-southeast-1.cr.bytepluses.com` latest Runtime/Worker 地址。 -- FR-2:非空 `VEADK_MPA_BYTEPLUS_RUNTIME_IMAGE` 和 `VEADK_MPA_BYTEPLUS_WORKER_IMAGE` 继续作为优先级更高的可信覆盖。 -- FR-3:火山引擎 `load_studio_profile()` 的北京 Runtime/Worker 默认值完全不变。 -- FR-4:已提交任务保留其持久化镜像快照。Studio 重启后必须创建新请求才能取得新默认值。 - -## 设计与影响文件 - -在 `studio_profile_values` 的 BytePlus 分支中定义一次 BytePlus 仓库地址,并将其两个镜像地址作为现有环境变量覆盖项之后的默认值。配置/任务接口已返回并快照解析后的非敏感默认值,因此不需要前端或 HTTP 结构变更。 - -影响文件: - -- `veadk/integrations/mpa/managed/studio_profile.py` -- `tests/integrations/mpa_managed/test_byteplus.py` -- 成对 Studio MPA 组件契约 -- 本成对 bugfix 设计 - -## 任务、测试与验收 - -| 需求 | 任务 | 验收 | -| --- | --- | --- | -| FR-1/FR-3 | 先增加失败的平台隔离断言,再修改默认值 | BytePlus 选择海外地址,火山引擎保持不变 | -| FR-2 | 保留并运行覆盖项测试 | 两个可信覆盖项继续优先 | -| FR-4 | 更新成对契约并核验任务语义 | 文档明确只有新任务快照取得新默认值 | - -验证:BytePlus 定向 pytest、改动文件 Ruff 和 Pyright、双语/链接复核及 `git diff --check`。没有前端或 HTTP 契约变化,因此前端构建和浏览器检查不适用。 - -## 风险与评审 - -按请求沿用 Studio 约定的可变 `latest` 标签;已验证 digest 只作为证据,不作为运行时固定值。公共 manifest 可读不能证明 AgentKit `CreateTool` 或 Runtime 应用就绪,因此仍需用新的隔离 BytePlus 创建做真实验收。已有失败任务不能接管新镜像,因为修改其快照会破坏重试身份。 - -直接设计评审(`review-spec` 不可用):已检查范围、平台隔离、覆盖优先级、任务不可变性、安全性、兼容性、可测试性和双语等价性;无阻塞项。用户以“帮我改下”批准了前述方案。本批准不授权提交、推送或云端部署。 - -## 验证记录 - -2026-10-14 当前工作差异: - -- pass:新增默认值断言在旧实现上失败,两个镜像仍解析为北京仓库。 -- pass:`uv run --extra dev pytest tests/integrations/mpa_managed/test_byteplus.py tests/integrations/mpa_managed/test_tasks.py tests/integrations/mpa_managed/test_studio_shared_resources.py -q` — 47 项通过,另有两个上游弃用警告。 -- pass:对改动 Python 文件执行 Ruff 0.11.12 检查/格式化和 Pyright;`git diff --check`;双语链接及语义已复核。 -- pass:直接解析配置时选中两个 BytePlus 托管地址,并保留两个北京国内地址。 -- pass:实现前已验证 BytePlus 公共镜像 manifest 可读。 -- pass:Studio 重启后,真实任务 `209b63d5-04ec-490f-95d8-41b7931891f4` 已持久化两个 BytePlus 托管默认值,并完成 Worker 创建及就绪。任务随后在独立的 OpenViking 自动创建阶段以 `openvikingCreateUnknown` 失败,尚未进入 Runtime 创建。 -- not_applicable:前端构建/浏览器检查;没有前端或 HTTP 契约变化。 diff --git a/prd-spec/bugfixes/mpa-byteplus-model-identities/2026-10-14-byteplus-model-identities.md b/prd-spec/bugfixes/mpa-byteplus-model-identities/2026-10-14-byteplus-model-identities.md deleted file mode 100644 index 36d135cd8..000000000 --- a/prd-spec/bugfixes/mpa-byteplus-model-identities/2026-10-14-byteplus-model-identities.md +++ /dev/null @@ -1,104 +0,0 @@ -# Use BytePlus Model Identities for Managed OpenViking - -[中文版](2026-10-14-byteplus-model-identities.zh.md) - -- Change ID: `mpa-byteplus-model-identities` -- Created/Revised: 2026-10-14 -- Status: implemented -- Related spec: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md) - -## Background and evidence - -BytePlus managed OpenViking currently defaults `OV_VLM_ENDPOINT_ID` and -`OV_EMBEDDING_ENDPOINT_ID` to domestic `doubao-*` identifiers. Read-only Ark -lookups in account `3001037806`, region `ap-southeast-1`, returned -`NotFound.EndpointId` for those identifiers. A live OpenViking resource accepted -the configuration but semantic ingestion timed out with embedding work repeatedly -requeued and no vectors produced. - -BytePlus Ark `CreateEndpoint` dry runs rejected the domestic model names and -accepted `seed-2-0-lite` version `260428` and -`skylark-embedding-vision` version `251215`. The user approved retaining the -current API Key plus model-identity binding instead of adopting Coding Plan or -creating account-specific `ep-*` resources. - -## Goals and non-goals - -Goals: - -- Default BytePlus VLM identity to `seed-2-0-lite-260428`. -- Default BytePlus embedding identity to - `skylark-embedding-vision-251215`. -- Preserve API Key injection, environment overrides, validation, redaction, - retry, and persisted nonsecret intent. - -Non-goals: - -- Do not change domestic OpenViking defaults. -- Do not create, update, or delete Ark endpoints or OpenViking resources. -- Do not adopt Coding Plan or change data-plane base URLs. -- Do not migrate existing tasks or OpenViking resources. - -## Scenarios and requirements - -- `FR-1`: Given a new BytePlus automatic OpenViking request without overrides, - its VLM and embedding credentials use the two BytePlus model identities above. -- `FR-2`: `OV_VLM_ENDPOINT_ID` and `OV_EMBEDDING_ENDPOINT_ID` continue to - override defaults and use the existing validation. -- `FR-3`: The resolved model API Key is attached only to the copied wire request - and is never persisted. -- `FR-4`: Volcengine requests and existing persisted requests remain unchanged. - -## Design and contract impact - -Change only the fallback values returned by `byteplus_model_endpoints()`. The -existing request builder, API Key injection, registry comparison, and cloud -adapter remain unchanged. Existing requests retain their persisted identities; -changing their inputs continues to produce a configuration conflict. New tasks -created after Studio restart receive the new defaults. - -Security, permissions, concurrency, state, and public Python APIs are unchanged. -The maintained Studio MPA creation contract must record the new defaults. - -## Affected files and tasks - -- `T-1` / `FR-1..4`: update BytePlus regression fixtures and assertions first. -- `T-2` / `FR-1..2`: update `byteplus_model_endpoints()`. -- `T-3` / `FR-1..4`: synchronize the bilingual Studio MPA creation spec. -- `T-4`: run targeted tests, Ruff, Pyright, bilingual checks, and diff checks. - -## Verification and acceptance - -| Requirement | Acceptance criterion | Verification | Result | -| --- | --- | --- | --- | -| `FR-1` | Default request uses both BytePlus identities | OpenViking and service pytest suite | `pass`: 240 tests | -| `FR-2` | Explicit overrides and invalid-value checks remain covered | Same pytest command | `pass` | -| `FR-3` | API Key remains wire-only | Same pytest command | `pass` | -| `FR-4` | Domestic code path is unchanged | Diff review and existing service tests | `pass` | -| Quality | Changed Python passes static checks | Ruff `0.11.12`, Pyright, and `git diff --check` | `pass`: 0 errors | - -Acceptance criteria: - -- `AC-1`: new BytePlus automatic requests persist the approved model identities. -- `AC-2`: the wire request contains the API Key while registry state does not. -- `AC-3`: environment overrides retain precedence. -- `AC-4`: no cloud resource is mutated by implementation verification. - -## Risks and recovery - -Provider acceptance at collection creation does not prove successful inference; -a new isolated live resource is required for end-to-end semantic verification. -Existing misconfigured resources are not mutated. Operators can restore the old -behavior with both environment overrides if rollback is required. - -## Review and delivery record - -Direct review found no API, credential, state-machine, or domestic compatibility -change beyond the BytePlus defaults. The model identities were verified by -BytePlus Ark dry run. The user approved the exact values and retained API Key -binding on 2026-10-14. Implementation completed on 2026-10-14. Verification: - -- `uv run --extra dev pytest tests/integrations/mpa_managed/test_openviking.py tests/integrations/mpa_managed/test_openviking_byteplus.py tests/integrations/mpa_managed/test_openviking_diagnostics.py tests/integrations/mpa_managed/test_service.py -q`: `pass`, 240 tests. -- Ruff `0.11.12` check/format and Pyright for the changed Python files: `pass`. -- `git diff --check` for the scoped files: `pass`. -- Live creation with the corrected identities: `not_run`; it requires a new paid OpenViking resource and explicit execution after Studio restart. diff --git a/prd-spec/bugfixes/mpa-byteplus-model-identities/2026-10-14-byteplus-model-identities.zh.md b/prd-spec/bugfixes/mpa-byteplus-model-identities/2026-10-14-byteplus-model-identities.zh.md deleted file mode 100644 index cbf88420b..000000000 --- a/prd-spec/bugfixes/mpa-byteplus-model-identities/2026-10-14-byteplus-model-identities.zh.md +++ /dev/null @@ -1,96 +0,0 @@ -# 托管 OpenViking 使用 BytePlus 模型标识 - -[English](2026-10-14-byteplus-model-identities.md) - -- Change ID: `mpa-byteplus-model-identities` -- Created/Revised: 2026-10-14 -- Status: implemented -- Related spec: [Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md) - -## 背景与证据 - -BytePlus 托管 OpenViking 当前将 `OV_VLM_ENDPOINT_ID` 和 -`OV_EMBEDDING_ENDPOINT_ID` 默认设为国内 `doubao-*` 标识。对账号 -`3001037806`、地域 `ap-southeast-1` 的只读 Ark 查询表明这些标识返回 -`NotFound.EndpointId`。真实 OpenViking 资源虽然接受了该配置,但语义入库 -超时,Embedding 工作持续重新入队且未生成向量。 - -BytePlus Ark `CreateEndpoint` DryRun 拒绝国内模型名,并接受 -`seed-2-0-lite` 版本 `260428` 和 `skylark-embedding-vision` 版本 -`251215`。用户已批准保留当前 API Key 加模型标识绑定,不采用 Coding Plan, -也不创建账号专属 `ep-*` 资源。 - -## 目标与非目标 - -目标: - -- BytePlus VLM 默认标识改为 `seed-2-0-lite-260428`。 -- BytePlus Embedding 默认标识改为 - `skylark-embedding-vision-251215`。 -- 保留 API Key 注入、环境变量覆盖、校验、脱敏、重试和非密钥意图持久化。 - -非目标: - -- 不改变国内 OpenViking 默认值。 -- 不创建、更新或删除 Ark Endpoint 或 OpenViking 资源。 -- 不采用 Coding Plan,也不改变数据面 Base URL。 -- 不迁移已有任务或 OpenViking 资源。 - -## 场景与需求 - -- `FR-1`:新的 BytePlus 自动 OpenViking 请求未设置覆盖时,VLM 和 - Embedding 凭据使用上述两个 BytePlus 模型标识。 -- `FR-2`:`OV_VLM_ENDPOINT_ID` 和 `OV_EMBEDDING_ENDPOINT_ID` 继续覆盖 - 默认值并沿用现有校验。 -- `FR-3`:解析后的模型 API Key 仅附加到复制的线上请求,绝不持久化。 -- `FR-4`:Volcengine 请求和已持久化请求保持不变。 - -## 设计与契约影响 - -仅修改 `byteplus_model_endpoints()` 返回的 fallback 值。现有请求构造器、 -API Key 注入、注册表比对和云适配器保持不变。已有请求继续保留已持久化标识; -改变其输入仍会产生配置冲突。Studio 重启后创建的新任务取得新默认值。 - -安全、权限、并发、状态和公开 Python API 均不改变。维护中的 Studio MPA -创建契约需记录新默认值。 - -## 影响文件与任务 - -- `T-1` / `FR-1..4`:先更新 BytePlus 回归 fixture 和断言。 -- `T-2` / `FR-1..2`:更新 `byteplus_model_endpoints()`。 -- `T-3` / `FR-1..4`:同步 Studio MPA 创建双语组件契约。 -- `T-4`:运行定向测试、Ruff、Pyright、双语检查和 diff 检查。 - -## 验证与验收 - -| 需求 | 验收条件 | 验证 | 结果 | -| --- | --- | --- | --- | -| `FR-1` | 默认请求使用两个 BytePlus 标识 | OpenViking 与 service pytest 测试集 | `pass`:240 项 | -| `FR-2` | 显式覆盖与非法值检查继续受覆盖 | 同一 pytest 命令 | `pass` | -| `FR-3` | API Key 仍仅在线上传递 | 同一 pytest 命令 | `pass` | -| `FR-4` | 国内代码路径不变 | diff 审查及现有 service 测试 | `pass` | -| 质量 | 变更 Python 通过静态检查 | Ruff `0.11.12`、Pyright 和 `git diff --check` | `pass`:0 个错误 | - -验收标准: - -- `AC-1`:新的 BytePlus 自动请求持久化已批准的模型标识。 -- `AC-2`:线上请求包含 API Key,注册表状态不包含 API Key。 -- `AC-3`:环境变量覆盖继续具有优先级。 -- `AC-4`:实现验证不变更云资源。 - -## 风险与恢复 - -云端在建库时接受配置不代表推理一定成功;端到端语义验证需要创建新的隔离 -真实资源。已有错误配置资源不做变更。需要回滚时,运维可同时设置两个环境变量 -恢复旧行为。 - -## 评审与交付记录 - -直接评审确认除 BytePlus 默认值外,不改变 API、凭据、状态机或国内兼容性。 -模型标识已通过 BytePlus Ark DryRun 验证。用户于 2026-10-14 批准精确值并 -保留 API Key 绑定。实现于 2026-10-14 完成。验证结果: - -- `uv run --extra dev pytest tests/integrations/mpa_managed/test_openviking.py tests/integrations/mpa_managed/test_openviking_byteplus.py tests/integrations/mpa_managed/test_openviking_diagnostics.py tests/integrations/mpa_managed/test_service.py -q`:`pass`,240 项。 -- 对变更 Python 文件运行 Ruff `0.11.12` check/format 和 Pyright:`pass`。 -- 对范围内文件运行 `git diff --check`:`pass`。 -- 使用修正标识进行真实创建:`not_run`;需要 Studio 重启后显式创建新的付费 OpenViking 资源。 diff --git a/prd-spec/bugfixes/mpa-byteplus-scaling/2026-10-15-default-maximum.md b/prd-spec/bugfixes/mpa-byteplus-scaling/2026-10-15-default-maximum.md deleted file mode 100644 index cdaf086aa..000000000 --- a/prd-spec/bugfixes/mpa-byteplus-scaling/2026-10-15-default-maximum.md +++ /dev/null @@ -1,28 +0,0 @@ -# Default BytePlus MPA maximum instances to 10 - -[中文版](2026-10-15-default-maximum.zh.md) - -Date: 2026-10-15. Scope: Studio MPA creation only. Authorized by the user request to change the overseas BytePlus maximum instances to 10. Status: implemented and locally verified. - -## Evidence, requirements and design - -The creation dialog hardcodes 20 for fresh input, missing editable-draft values, the numeric buffer, new-request reset and localized help. FR-1: choose 10 for BytePlus and retain 20 for Volcengine using the trusted existing cloudProvider prop. FR-2: preserve explicit draft edits and submitted request values, including omission in legacy submitted requests; send the numeric value through the existing maxInstance API field. FR-3: interpolate provider-specific help text and regenerate WebUI assets. - -Reuse the existing numeric input and validation. This changes a default scaling limit, not a hard cap or immediate instance allocation. Do not change backend/YAML/CLI profile defaults, minimum instances, concurrency, generic-agent creation or existing deployed Runtime configuration. No cloud operations, commit or push are authorized. The existing CON-21 scaling contract needs only a provider-specific default clarification in both languages. - -## Review, tasks and acceptance - -Direct design review (review-spec unavailable): provider selection, preserved request identity, legacy omission, localization and unchanged deployment mapping reviewed; no blockers. The user explicitly requested this scoped default change. T-1: failing React tests for fresh BytePlus submission, draft restoration, new-request reset and immutable retry. T-2: implement provider-aware defaults and localized help. T-3: run frontend tests, production build, i18n/assets checks, targeted existing backend scaling tests and proportionate browser verification. Acceptance: BytePlus new requests submit 10; Volcengine remains 20; explicit/legacy retry inputs are unchanged; generated assets match source. No component ownership or API/schema change. - -## Verification - -- pass: regression first, two new cases failed with expected 10 / actual 20; three explicit/legacy cases already passed. -- pass: `npm --prefix frontend test`, 1,389 Node tests and 116 component tests, including all five new cases. -- pass: `uv run --extra dev pytest -q tests/integrations/mpa_managed/test_creation_scaling.py`, 20 tests; no backend source changes. -- pass: `npm --prefix frontend run build`, TypeScript plus app/widget production bundles. -- pass: `npm --prefix frontend run check:i18n` and `npm --prefix frontend run test:webui-assets`, 113 packaged files / 350 internal references. -- pass: real browser using the actual React dialog and isolated mocked APIs on a temporary local server: BytePlus input/help 10, submitted maxInstance 10, new-request reset 10, zero rejected; Volcengine input/help 20. Temporary fixture/server/tab were removed; no real cloud creation. -- pass: `git diff --check`. Frontend-only change; Python Ruff/Pyright and harness/sidecar smoke are not_applicable. -- not_run: full Python regression (frontend defaults only; existing scaling tests cover the unchanged backend mapping), live cloud scaling, commit and push (not requested). - -- pass: `uv run --extra dev pre-commit run --all-files`, formatting, secret scanning and dependency policy. diff --git a/prd-spec/bugfixes/mpa-byteplus-scaling/2026-10-15-default-maximum.zh.md b/prd-spec/bugfixes/mpa-byteplus-scaling/2026-10-15-default-maximum.zh.md deleted file mode 100644 index 571a848e4..000000000 --- a/prd-spec/bugfixes/mpa-byteplus-scaling/2026-10-15-default-maximum.zh.md +++ /dev/null @@ -1,28 +0,0 @@ -# BytePlus MPA 最大实例数默认设为 10 - -[English](2026-10-15-default-maximum.md) - -日期:2026-10-15。范围:仅 Studio MPA 创建。授权:用户要求将海外 BytePlus 最大实例数改为 10。状态:已实现并完成本地验证。 - -## 证据、需求与设计 - -创建弹窗在新输入、缺少该值的可编辑草稿、数字编辑缓冲、新请求重置及本地化帮助中写死 20。FR-1:使用已有可信 cloudProvider 属性,为 BytePlus 选择 10,为火山保留 20。FR-2:保留显式草稿修改和已提交请求的数值,包括旧已提交请求省略字段的情况;通过已有 maxInstance API 字段传递数字。FR-3:帮助文字插入对应提供商的默认值,重新生成 WebUI 产物。 - -复用现有数字输入和校验。此次改变默认扩容上限,不是强制上限或立即分配的实例数。不修改后端/YAML/CLI 配置默认值、最小实例数、并发、通用智能体创建或已部署 Runtime 设置。未授权云端操作、提交或推送。现有 CON-21 扩容契约仅需在双语文档中说明分提供商默认值。 - -## 审查、任务与验收 - -直接设计审查(review-spec 不可用):已检查提供商选择、请求身份保留、旧字段省略、本地化及不变的部署映射,无阻塞项。用户已明确要求此范围的默认值修改。T-1:先写 BytePlus 新请求提交、草稿恢复、新请求重置和不可变重试的失败 React 测试。T-2:实现分提供商默认值和本地化帮助。T-3:执行前端测试、生产构建、国际化/产物检查、已有后端扩容定向测试及适度浏览器验证。验收:BytePlus 新请求提交 10;火山仍为 20;显式/旧重试输入保持原样;构建产物与源码一致。不改变组件归属或 API/数据结构。 - -## 验证 - -- pass:先执行失败回归,两个新增场景因预期 10、实际 20 而失败;三个显式值/旧请求场景原本已通过。 -- pass:`npm --prefix frontend test`,1,389 项 Node 测试、116 项组件测试通过,包含全部五项新增场景。 -- pass:`uv run --extra dev pytest -q tests/integrations/mpa_managed/test_creation_scaling.py`,20 项通过;没有后端源码改动。 -- pass:`npm --prefix frontend run build`,TypeScript、应用及 widget 生产构建通过。 -- pass:`npm --prefix frontend run check:i18n`、`npm --prefix frontend run test:webui-assets`,113 个产物文件、350 个内部引用通过。 -- pass:临时本地服务使用真实 React 弹窗与隔离模拟 API 完成浏览器验证:BytePlus 输入/帮助为 10,提交 maxInstance 为 10,新请求重置为 10,拒绝 0;火山输入/帮助仍为 20。临时 fixture、服务和标签页均已清理;没有真实云端创建。 -- pass:`git diff --check`。仅前端改动;Python Ruff/Pyright 和 harness/sidecar smoke 为 not_applicable。 -- not_run:完整 Python 回归(仅前端默认值,已有扩容测试覆盖未改动的后端映射)、真实云端扩缩容、提交及推送(未请求)。 - -- pass:`uv run --extra dev pre-commit run --all-files`,格式、密钥扫描及依赖策略检查通过。 diff --git a/prd-spec/bugfixes/mpa-byteplus-without-openviking/2026-10-14-skip-new-requests.md b/prd-spec/bugfixes/mpa-byteplus-without-openviking/2026-10-14-skip-new-requests.md deleted file mode 100644 index 0090d9cea..000000000 --- a/prd-spec/bugfixes/mpa-byteplus-without-openviking/2026-10-14-skip-new-requests.md +++ /dev/null @@ -1,43 +0,0 @@ -# Temporarily skip OpenViking in new BytePlus Studio requests - -[中文版](2026-10-14-skip-new-requests.zh.md) - -Change ID: mpa-byteplus-without-openviking. Date: 2026-10-14. Status: superseded; temporary behavior restored on 2026-10-14. -Contracts: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), CON-17/CON-18. - -## Evidence, goals and scope - -A BytePlus task reached OV creation after PG/network/IM Gateway/Worker succeeded, then failed with ProductUnordered. The user explicitly requests skipping OV to test the rest of the creation/chat chain. Do not purchase/activate services or change billing. Preserve domestic automatic OV and already-submitted request identity; do not reset the uncertain-create guard or change existing resource bindings. - -## Requirements and scenarios - -- FR-1: Built-in BytePlus configuration stops advertising automatic OV for new requests; domestic defaults continue advertising it. Explicit prior automatic requests and manual profiles retain supported behavior. -- FR-2: New BytePlus forms and editable restored drafts clear OV auto selection and manual fields when the server no longer advertises auto. Show a localized skipped-memory explanation, hide OV inputs, and submit without auto or credentials. Submitted automatic/manual requests preserve their original fields and retry intent. -- FR-3: Blank request fields use the existing manual-disabled server path, remove inherited OPENVIKING_* variables and never call OV provisioning. PG, IAM, network, APIG/IM Gateway, Worker, Skill Space, Runtime and general agents keep their existing contracts. -- FR-4: Keep three-step navigation, console link, optional TOS, cancellation, new-request action and retry behavior. Old failed automatic requests require the existing New Agent action; they are not silently converted. - -## Design, tasks and review - -T-1: config.py disables only built-in BytePlus auto availability. The existing with_creation_resources empty-input behavior owns Runtime omission; no new API enum or dependency. -T-2: MpaCreateDialog.tsx clears OV choices only for editable BytePlus requests with no server auto availability and clears memory-only key state. Render the existing paragraph/layout for the skipped state; no CSS/icon/component extension. Add paired locale copy. Archived automatic and nonempty manual requests retain their existing UI. -T-3: Test profile defaults, domestic compatibility, old explicit auto, restored editable draft, submitted retry and no-OV Runtime payload (including inherited settings) before implementation. Run managed/startup Python, Ruff/Pyright, frontend test/build/i18n/assets, isolated browser normal/retry/narrow/Escape checks and secret scanning. -T-4: Synchronize this bilingual design, component contract and module/frontend READMEs; preserve unrelated local changes and generated outputs from other tasks. - -Approval: user's explicit 2026-10-14 request to temporarily skip BytePlus OV; the implementation scope was stated before editing. Direct review (review-spec unavailable) checked boundaries, absent-field compatibility, immutable submitted requests, secret omission and bilingual equivalence; no blocker. Repository-referenced frontend-design/ui-ux-pro-max files are unavailable at their paths or installed skill roots; use the existing dialog and Foundation/SPEC rules without introducing visual design. - -## Acceptance and risks - -AC-1 (FR-1): BytePlus new config omits auto; domestic still advertises auto; explicit old auto resolves unchanged. -AC-2 (FR-2/FR-4): New/restored editable BytePlus requests contain no OV credentials/auto and display the skipped explanation; retries preserve submitted identity and values. Existing manual recovery still works. -AC-3 (FR-3): Mocked BytePlus provisioning reaches Runtime with no OPENVIKING_* values, including inherited template/reference values; no OV API is called. -AC-4 (all): Required targeted/regression/static/frontend/browser/secret gates pass with actual evidence. Live chat validation is separate and user-driven. No server restart, cloud mutation, commit or push in this implementation. - -The resulting agent has no OV-backed long-term memory. This change cannot make an old automatic task retry as a different request. Other cloud/model/startup errors may still prevent the full chain from succeeding. - -## Verification - -2026-10-14, diff based on 2ffe6e14. not_run: implementation checks pending. Live cloud creation/chat not_run; no automatic additional resource writes. Harness/Codex smoke not_applicable because execution protocols are unchanged. - -Verification completed 2026-10-14: pass, failing regressions reproduced BytePlus auto advertisement and retained editable auto draft; an additional failure reproduced submitted intent being cleared by configuration refresh. All fixed. pass: `uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/cli/test_studio_startup_imports.py` (1,045 tests, four dependency warnings, two workers). pass: `npm --prefix frontend test` (Node suite and 100 React cases), production build, i18n (2 locales/21 namespaces), packaged assets (113 files/350 references). pass: Ruff 0.11.12 and Pyright with explicit repository Python interpreter on all three changed Python files (zero errors/warnings). pass: actual dialog in Chromium with mocked cloud APIs, normal no-OV submission, same-request retry, editable stale draft, submitted auto recovery, domestic auto, narrow viewport and Escape. pass: repository Gitleaks hook over 128 isolated in-scope source/doc/generated files. Browser fixture initially lacked application theme tokens; it was corrected to import the real theme and set the same light default before final visual verification. Temporary browser files/server removed. pass: paired identifiers/links and diff whitespace. not_run: full-repository/all-files pre-commit because no commit requested; focused static/secret and affected regression gates were used. No new dependencies, billing changes or cloud writes. Existing package-lock changes are preserved. - -Superseded by [restore OV and complete Runtime diagnostics](../mpa-runtime-diagnostics/2026-10-14-sdk-json-and-restore-ov.md), explicitly requested after OV allowlisting. Historical evidence below describes the temporary skip only. diff --git a/prd-spec/bugfixes/mpa-byteplus-without-openviking/2026-10-14-skip-new-requests.zh.md b/prd-spec/bugfixes/mpa-byteplus-without-openviking/2026-10-14-skip-new-requests.zh.md deleted file mode 100644 index 619592048..000000000 --- a/prd-spec/bugfixes/mpa-byteplus-without-openviking/2026-10-14-skip-new-requests.zh.md +++ /dev/null @@ -1,43 +0,0 @@ -# BytePlus Studio 新请求暂时跳过 OpenViking - -[English](2026-10-14-skip-new-requests.md) - -变更 ID:mpa-byteplus-without-openviking。日期:2026-10-14。状态:superseded;未进行真实创建/聊天。 -组件契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-17/CON-18。 - -## 证据、目标与范围 - -一次 BytePlus 任务通过 PG/网络/IM Gateway/Worker 后,在 OV 创建返回 ProductUnordered。用户明确要求跳过 OV,验证其余创建/聊天链路。不购买或开通服务,不修改结算。保留国内自动 OV 及已提交请求身份;不重置不确定建库保护,不修改已有资源绑定。 - -## 需求与场景 - -- FR-1:内置 BytePlus 配置不再为新请求声明自动 OV;国内默认仍声明。明确提交过的自动请求和手动配置保留现有支持行为。 -- FR-2:服务端不再声明自动 OV 时,新的 BytePlus 表单及恢复的可编辑草稿清除自动选择和手动字段,显示本地化跳过记忆说明,隐藏 OV 输入,不提交自动模式或凭证。已提交自动/手动请求保留原字段和重试意图。 -- FR-3:空请求字段使用现有手动禁用路径,清除继承的 OPENVIKING_* 变量,不调用 OV 准备。PG、IAM、网络、APIG/IM Gateway、Worker、Skill Space、Runtime 和通用智能体保持现有契约。 -- FR-4:保留三步导航、控制台链接、可选 TOS、取消、新建另一个请求及重试行为。旧失败自动请求需使用现有“新建另一个智能体”,不静默转换。 - -## 设计、任务与评审 - -T-1:config.py 只关闭内置 BytePlus 自动能力声明;现有 with_creation_resources 空输入行为负责 Runtime 不注入,不新增 API 枚举或依赖。 -T-2:MpaCreateDialog.tsx 仅为可编辑、服务端未声明自动能力的 BytePlus 请求清除 OV 选择,并清除内存中的 Key。跳过状态使用现有段落/布局,不扩展 CSS/图标/组件,增加双语文案。归档自动及非空手动请求保留现有 UI。 -T-3:先测试配置默认、国内兼容、旧明确自动请求、恢复可编辑草稿、已提交重试及无 OV Runtime 载荷(含继承配置),再实现。运行 MPA/启动 Python、Ruff/Pyright、前端测试/构建/i18n/产物、隔离浏览器正常/重试/窄窗口/Escape 及密钥扫描。 -T-4:同步双语设计、组件契约和模块/前端 README;保留其他任务的本地改动及生成产物。 - -批准记录:用户在 2026-10-14 明确要求暂时跳过 BytePlus OV,实现前已说明范围。review-spec 不可用,已直接评审边界、缺省字段兼容、已提交请求不可变、密钥排除及双语一致性,无阻塞项。仓库引用的 frontend-design/ui-ux-pro-max 文件在指定路径和已安装技能根中均不可用,使用现有弹窗及 Foundation/SPEC 规则,不引入视觉设计。 - -## 验收与风险 - -AC-1(FR-1):BytePlus 新配置省略自动能力,国内仍声明,旧明确自动请求解析不变。 -AC-2(FR-2/FR-4):新建/恢复可编辑 BytePlus 请求不含 OV 凭证或自动选择,显示跳过说明;重试保留提交身份和值,已有手动恢复仍可用。 -AC-3(FR-3):模拟 BytePlus 准备流程到达 Runtime 时不含 OPENVIKING_*,包括继承模板/参考值,不调用 OV API。 -AC-4(全部):目标/回归/静态/前端/浏览器/密钥检查通过并记录真实证据;真实聊天验证独立,由用户执行。本次实现不重启服务、不修改云资源、不提交或推送。 - -新智能体没有 OV 长期记忆。本修改不会让旧自动任务以不同请求重试。其他云端/模型/启动错误仍可能影响全链路。 - -## 验证 - -2026-10-14,基于 2ffe6e14 的差异。not_run:等待实现检查。真实云端创建/聊天 not_run,未自动追加资源写入。Harness/Codex smoke not_applicable,因为执行协议未改。 - -2026-10-14 验证完成:pass,失败回归复现 BytePlus 声明自动能力及可编辑旧草稿仍保留自动模式;补充回归复现配置刷新清除已提交意图,均已修复。pass:`uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/cli/test_studio_startup_imports.py`(1,045 项,4 条依赖警告,两个 worker)。pass:`npm --prefix frontend test`(Node 套件及 100 个 React 场景)、生产构建、i18n(2 语言/21 命名空间)、打包产物(113 文件/350 引用)。pass:三个修改 Python 文件通过 Ruff 0.11.12 和显式仓库解释器的 Pyright(零错误/警告)。pass:Chromium 加载真实弹窗,模拟云端 API,验证正常无 OV 提交、同请求重试、可编辑旧草稿、已提交自动恢复、国内自动、窄窗口和 Escape。pass:128 个范围内源码/文档/产物隔离副本通过仓库 Gitleaks hook。浏览器测试入口最初缺少应用主题 token,已导入真实主题并使用相同的浅色默认后完成最终视觉检查。临时浏览器文件/服务已清理。pass:双语标识/链接及差异空白。not_run:全仓/全文件 pre-commit,因为未要求提交,使用目标静态/密钥及受影响回归检查。未新增依赖、修改结算或写云资源,保留已有 package-lock 改动。 - -已由[恢复 OV 并补齐 Runtime 诊断](../mpa-runtime-diagnostics/2026-10-14-sdk-json-and-restore-ov.zh.md)替代:用户在 OV 开白后明确要求恢复。下方历史证据仅描述临时跳过行为。 diff --git a/prd-spec/bugfixes/mpa-channel-visibility/2026-09-15-mpa-channel-visibility.md b/prd-spec/bugfixes/mpa-channel-visibility/2026-09-15-mpa-channel-visibility.md deleted file mode 100644 index ba362901a..000000000 --- a/prd-spec/bugfixes/mpa-channel-visibility/2026-09-15-mpa-channel-visibility.md +++ /dev/null @@ -1,28 +0,0 @@ -# MPA-only message channel visibility - -[中文](2026-09-15-mpa-channel-visibility.zh.md) - -Date: 2026-09-15. Status: implemented. User approval: only the MPA agent category should expose Message channels after Runtime selection. Contract: [MPA channels](../../../specs/mpa-channels/README.md). - -## Evidence and requirements -CloudRuntime carries agentCategory, but MyAgents runtime cards and App detail AgentEntry drop it; AgentWorkspace currently shows channels for every type. FR-1: preserve general/mpa category through Runtime list → card → detail entry. Prefer explicit Runtime category, falling back to the category of the filtered list for older responses. FR-2: expose/render channels only when selectedAgent has a Runtime ID and category mpa; general/unknown/local agents hide it. FR-3: if channels is selected when the category changes or an unsupported focus is requested, fall back to basic without mounting channel requests. - -## Design and scope -Add optional category metadata to MyAgentCardData.runtime and AgentEntry, propagate it in MyAgents and App, filter sections and guard content in AgentWorkspace. Preserve usage permissions, other sections, provider behavior and QR lifecycle. Unknown category fails closed for visibility. No backend/API authorization changes and no live cloud operations. - -## Review, tasks and acceptance -Direct review (review-spec unavailable): additive metadata, backward-compatible optional fields, filtered-list fallback tied to request category, explicit guard prevents stale panel mounting. No blockers; user request approves scope. -T-1/AC-1 (FR-1): source contract tests for metadata propagation, including cached list mapping. -T-2/AC-2 (FR-2/3): executable predicate cases for MPA/general/unknown/no Runtime; section filter/render/fallback tests. Implement four source files listed above. -T-3/AC-3: targeted/frontend regression, build/assets, real browser with mocked Runtime APIs for category switching and unsupported focused section. Update paired docs and generated assets. - -## Verification and risks -Execution date: 2026-09-15; scope: the current uncommitted category propagation and visibility diff, its tests/docs, and rebuilt WebUI assets. -- pass: test-first regression failed before implementation; `node --test frontend/tests/agentWorkspace.test.mjs` then passed 34 tests. -- pass: `npm --prefix frontend test` passed 1208 tests after updating the existing Runtime mapping assertion for its category argument. -- pass: `npm --prefix frontend run build`; `npm --prefix frontend run test:webui-assets` verified 104 files and 248 internal references. Existing bundle-size warnings remain. -- pass: real browser running AgentWorkspace with mocked APIs showed MPA channels, hid them for general/unknown categories, returned to basic after switching, and restored the entry on returning to MPA. The MPA/general switch also passed at 480×850. Basic metadata error UI was expected from the isolated mock. -- pass: direct implementation review found no blockers; paired documents and scoped diff whitespace checked. T-1 through T-3 and AC-1 through AC-3 complete. -- not_applicable: Python checks, IME/input and channel binding/error/retry behavior are unchanged by this diff. No commit was requested, so pre-commit synchronization was not run. -- not_run: live cloud/provider verification and deployment; no live operations were requested. -Category-less non-list entry points intentionally hide the channel; no name/image heuristics. diff --git a/prd-spec/bugfixes/mpa-channel-visibility/2026-09-15-mpa-channel-visibility.zh.md b/prd-spec/bugfixes/mpa-channel-visibility/2026-09-15-mpa-channel-visibility.zh.md deleted file mode 100644 index b8d6b9320..000000000 --- a/prd-spec/bugfixes/mpa-channel-visibility/2026-09-15-mpa-channel-visibility.zh.md +++ /dev/null @@ -1,28 +0,0 @@ -# 消息渠道仅对 MPA 显示 - -[English](2026-09-15-mpa-channel-visibility.md) - -日期:2026-09-15。状态:implemented。用户批准:选择 Runtime 后,只有 MPA 智能体大类显示消息渠道。契约:[MPA channels](../../../specs/mpa-channels/README.zh.md)。 - -## 证据与需求 -CloudRuntime 已有 agentCategory,但 MyAgents Runtime 卡片与 App 详情 AgentEntry 丢失该字段;AgentWorkspace 当前对所有类型显示渠道。FR-1:沿 Runtime 列表 → 卡片 → 详情传递 general/mpa 分类。优先使用 Runtime 明确分类,旧响应缺失时使用请求时筛选的类目。FR-2:仅 selectedAgent 具有 Runtime ID 且分类为 mpa 时显示入口/内容;通用/未知/本地智能体隐藏。FR-3:分类变化或不支持的定位请求使 channels 不再适用时回到 basic,不挂载渠道请求。 - -## 设计与范围 -MyAgentCardData.runtime 和 AgentEntry 增加可选分类元数据,MyAgents 和 App 传递,AgentWorkspace 过滤栏目并保护内容渲染。保留用量权限、其他栏目、各渠道行为和二维码生命周期。未知分类默认隐藏。不改变后端/API 鉴权,不执行真实云操作。 - -## 评审、任务与验收 -直接评审(review-spec 不可用):新增可选元数据保持兼容,列表回退分类绑定请求类目,显式渲染保护避免旧面板挂载。无阻塞;用户请求授权此范围。 -T-1/AC-1(FR-1):元数据传递源码契约测试,覆盖缓存列表映射。 -T-2/AC-2(FR-2/3):执行谓词测试覆盖 MPA/通用/未知/无 Runtime,检查栏目过滤/内容保护/回退;修改上述四个源文件。 -T-3/AC-3:定向/前端回归、构建/资源、真实浏览器配模拟 Runtime API 验证分类切换与不支持的栏目定位;同步双语文档和产物。 - -## 验证与风险 -执行日期:2026-09-15;范围:当前未提交的分类传递及栏目可见性差异、对应测试/文档和重建的 WebUI 产物。 -- pass:测试先行,实施前回归失败;实施后 `node --test frontend/tests/agentWorkspace.test.mjs` 34 项通过。 -- pass:为现有 Runtime 映射断言增加分类参数后,`npm --prefix frontend test` 1208 项通过。 -- pass:`npm --prefix frontend run build`;`npm --prefix frontend run test:webui-assets` 校验 104 个文件和 248 个内部引用。仍有既有构建包体积警告。 -- pass:真实浏览器运行 AgentWorkspace 并模拟 API,验证 MPA 显示渠道、通用/未知分类隐藏、切换后回到基本信息、切回 MPA 恢复入口。480×850 下 MPA/通用切换也通过。隔离模拟返回的基本信息错误界面符合预期。 -- pass:直接实施评审无阻塞;已检查双语文档和范围内差异空白。T-1 至 T-3、AC-1 至 AC-3 完成。 -- not_applicable:未修改 Python、IME/输入及渠道绑定/错误/重试行为。不涉及提交,因此未运行提交前同步。 -- not_run:真实云端/渠道验证和部署;用户未要求真实操作。 -非列表入口缺失分类时有意隐藏渠道,不根据名称/镜像猜测。 diff --git a/prd-spec/bugfixes/mpa-create-images/2026-10-09-restore-main-images.md b/prd-spec/bugfixes/mpa-create-images/2026-10-09-restore-main-images.md deleted file mode 100644 index df6ee167e..000000000 --- a/prd-spec/bugfixes/mpa-create-images/2026-10-09-restore-main-images.md +++ /dev/null @@ -1,56 +0,0 @@ -# Restore main MPA creation image defaults - -[中文版](2026-10-09-restore-main-images.zh.md) - -- Change ID: `restore-main-images` -- Date: 2026-10-09 -- Status: approved by the user's explicit request to withdraw public image discovery; implemented and locally verified; not committed or pushed. - -## Background and evidence - -A Worker CreateTool request failed with `InvalidParameter.ImageUrl` after Studio selected a public image using a digest reference. The user requires withdrawing the public registry discovery change and following main's two `/mpa/` `:latest` defaults. The earlier creation-page correction already follows main. - -## Scope, scenarios and requirements - -- FR-1: Restore built-in MPA image `agentkit-platform-2112682748-cn-beijing.cr.volces.com/mpa/mpa_agent:latest` and Worker image `agentkit-platform-2112682748-cn-beijing.cr.volces.com/mpa/mpa_codex_worker:latest`. Config inspection and POST use local profile defaults, without registry queries or digest conversion. -- FR-2: Restore main's creation dialog/client wiring and regressions. Remove discovery-only resolver, fixtures, tests, request-image helper and timeout/query extensions. Keep legacy explicit API images and pre-existing first-submission image snapshots. -- FR-3: Preserve STS-derived deployment account, automatic IAM preparation, network/APIG fixes, independent Worker creation, safe IAM diagnostics and main's named creation/basic details. -- FR-4: Reconcile bilingual specs/docs and regenerate packaged WebUI assets. Remove the withdrawn feature's documentation and accidental Vite cache artifact. - -Non-goals: database/task/registry edits, clearing pending intents, live deployment, migration, committing or pushing. An old failed request may still contain the rejected digest in its immutable snapshot; it must not silently change on retry. A fresh request uses the restored defaults. `latest` is mutable and does not guarantee immutable image content across time. - -## Design, affected files and contract assessment - -Restore main versions of Studio routes, API client, dialog and image/name tests; retain IAM regression coverage. Change only image settings in `managed/studio_profile.py`, preserving its new-account/IAM/reference-free settings. Remove `frontend/server/mpa_creation_images.py`, discovery-only tests/conftest, and `CreationTasks.request_images`. Keep `CreationTasks.start` snapshot semantics unchanged. Current ownership/API defaults are maintained by [Studio creation CON-1/CON-9](../../../specs/studio-mpa-creation/README.md); restore their local-default contract in both languages. Other resource contracts are unchanged. Remove the superseded public-image PRD pair and point current specs to this correction. - -## Tasks, tests and acceptance - -1. T-1 / FR-1: Restore main's route/name regression expectations and observe failure against the discovery implementation. -2. T-2 / FR-1–FR-3: Withdraw discovery wiring and restore defaults; assert other branch behavior remains in the diff. -3. T-3 / FR-4: Synchronize documents and build assets; run creation/CLI Python tests, changed-file Ruff/Pyright, frontend tests/build/i18n/assets, pre-commit and an isolated browser flow. - -Acceptance: exact default URLs in config and new task snapshots, no discovery code references or network dependency, unchanged legacy snapshots/owner isolation, main form without image fields, intact new-account changes, passing affected gates. No cloud writes are needed to prove the rollback; live cloud pull/readiness remains unverified. - -## Review and risks - -`review-spec` is unavailable; direct review covers scope, bilingual equivalence, compatibility, ownership, secret exclusion, retries, cancellation and tests. The user explicitly approved withdrawal; no further approval is required for this bounded rollback. Existing failed digest snapshots and mutable latest tags are the known limits. No blockers. - -## Verification - -Scope: uncommitted rollback against `df797f94`, using `origin/main` at `f6ed8ffa` as the reference. Execution date: 2026-10-09. T-1–T-3 and FR-1–FR-4 are complete within local rollback scope. - -| Check | Outcome | Evidence | -| --- | --- | --- | -| Regression first | pass | Restored `test_new_creation_generates_owner_scoped_id_and_latest_images` and `test_authorized_config_returns_defaults_and_task_freezes_them`; both failed before withdrawal and pass after it. | -| `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -n 2 -q` | pass | 614 passed, 10 warnings; 2 workers selected for the local machine. | -| `npm --prefix frontend test` | pass | 1,377 Node tests and 65 Vitest tests passed; no skips. Covers recovery, retry, errors, cancellation, loading, keyboard/IME and owner isolation. | -| `npm --prefix frontend run build` | pass | App and widget built, packaged assets regenerated. Existing chunk-size/dependency warnings remain non-blocking. | -| `npm --prefix frontend run check:i18n` / `run test:webui-assets` | pass | 2 locales / 21 namespaces; 113 files and 350 internal references verified. | -| Changed-file Ruff / Pyright | pass | `uvx --from ruff==0.11.12 ruff check` and `uvx pyright --pythonpath .venv/bin/python` for the route, profile, tasks and two restored test modules: no findings. | -| `uv run --extra dev pre-commit run --all-files` | pass | Ruff check/format, hardcoded secret detection and YAML secret scanning passed. | -| Isolated real browser | pass | Mock configuration only: name required, description preserved, all three steps, no image inputs; final create entry enabled. 390×844 viewport has no horizontal overflow. No real submission; temporary files, tab and Vite process removed. | -| Contract/diff review | pass | Route/client/dialog match main; profile retains STS/IAM/reference-free differences, tasks retain IAM diagnostics. No runtime discovery references or dangling withdrawn-PRD links. Bilingual identifiers and links checked; `git diff --check` passed. | -| Real cloud creation / pull readiness | not_run | No cloud mutation authorized for this rollback. Existing failed task snapshots were not edited; start a fresh request after reloading Studio to use new defaults. | -| Full SDK regression / harness smoke | not_applicable | This rollback affects only managed Studio image selection and restores main contracts; shared SDK and sidecar behavior are unchanged. | - -Local tests prove default selection and compatibility, not that cloud services accept or can pull either mutable tag. The user's existing Studio process was not restarted. diff --git a/prd-spec/bugfixes/mpa-create-images/2026-10-09-restore-main-images.zh.md b/prd-spec/bugfixes/mpa-create-images/2026-10-09-restore-main-images.zh.md deleted file mode 100644 index 7c102fe39..000000000 --- a/prd-spec/bugfixes/mpa-create-images/2026-10-09-restore-main-images.zh.md +++ /dev/null @@ -1,56 +0,0 @@ -# 恢复 main 的 MPA 创建镜像默认值 - -[English](2026-10-09-restore-main-images.md) - -- Change ID:`restore-main-images` -- 日期:2026-10-09 -- 状态:用户明确要求撤回公开镜像查询,已批准;已实施并完成本地验证;未提交或推送。 - -## 背景与证据 - -Studio 通过 digest 引用选择公开镜像后,Worker CreateTool 请求返回 `InvalidParameter.ImageUrl`。用户要求撤回公开仓库查询改动,并遵循 main 的两个 `/mpa/` `:latest` 默认镜像。此前创建页面修正已遵循 main。 - -## 范围、场景与需求 - -- FR-1:恢复内置 MPA 镜像 `agentkit-platform-2112682748-cn-beijing.cr.volces.com/mpa/mpa_agent:latest` 和 Worker 镜像 `agentkit-platform-2112682748-cn-beijing.cr.volces.com/mpa/mpa_codex_worker:latest`。配置检查和 POST 使用本地 profile 默认值,不查询仓库或转换 digest。 -- FR-2:恢复 main 的创建弹窗/客户端接线和回归。删除仅供查询使用的解析器、fixture、测试、请求镜像查询方法及超时/查询参数扩展。保留旧 API 显式镜像和此前已有的首次提交镜像快照。 -- FR-3:保留 STS 推导部署账号、自动 IAM 准备、网络/APIG 修复、独立 Worker 创建、安全 IAM 诊断,以及 main 的名称创建/基本详情。 -- FR-4:同步双语契约/说明并重新生成 WebUI 产物。删除已撤回功能的文档及误提交的 Vite 缓存产物。 - -非目标:修改数据库/任务/注册库、清空未完成意图、真实部署、迁移、提交或推送。旧失败请求的不可变快照可能仍包含被拒绝的 digest;重试不得静默更换。新请求使用恢复的默认值。`latest` 可变,不能保证不同时刻镜像内容不变。 - -## 设计、影响文件与契约评估 - -恢复 main 版本的 Studio 路由、API 客户端、弹窗及镜像/名称测试;保留 IAM 回归覆盖。仅调整 `managed/studio_profile.py` 的镜像设置,保留新账号/IAM/无参考模板配置。删除 `frontend/server/mpa_creation_images.py`、仅用于仓库查询的测试/conftest,以及 `CreationTasks.request_images`。`CreationTasks.start` 快照语义不变。当前归属/API 默认值由 [Studio 创建 CON-1/CON-9](../../../specs/studio-mpa-creation/README.zh.md) 维护;双语均恢复本地默认值合同。其他资源合同不变。删除已取代的公开镜像 PRD,当前契约改为链接此次修正。 - -## 任务、测试与验收 - -1. T-1 / FR-1:恢复 main 路由/名称回归期望,先在查询实现上观察失败。 -2. T-2 / FR-1–FR-3:撤回查询接线并恢复默认值;通过 diff 确认本分支其他行为保留。 -3. T-3 / FR-4:同步文档并构建产物;运行创建/CLI Python 测试、改动文件 Ruff/Pyright、前端测试/构建/i18n/产物、pre-commit 及隔离浏览器流程。 - -验收:配置和新任务快照中包含准确的默认 URL,无查询代码引用或仓库网络依赖,旧快照/owner 隔离不变,main 表单不提供镜像字段,新账号改动完整保留,受影响检查通过。无需云写入证明撤回;真实云端拉取/就绪仍未验证。 - -## 审查与风险 - -`review-spec` 不可用;直接审查覆盖范围、双语等价、兼容性、归属、密钥排除、重试、取消和测试。用户明确批准撤回,此范围内无需再次批准。旧失败 digest 快照和可变 latest 标签是已知限制,无阻塞项。 - -## 验证 - -范围:相对 `df797f94` 的未提交撤回,参考 `origin/main` 的 `f6ed8ffa`。执行日期:2026-10-09。T-1–T-3、FR-1–FR-4 在本地撤回范围内完成。 - -| 检查 | 结果 | 证据 | -| --- | --- | --- | -| 回归先行 | pass | 恢复 `test_new_creation_generates_owner_scoped_id_and_latest_images` 与 `test_authorized_config_returns_defaults_and_task_freezes_them`;撤回前两项失败,撤回后通过。 | -| `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -n 2 -q` | pass | 614 项通过,10 条警告;本机采用 2 个 worker。 | -| `npm --prefix frontend test` | pass | 1,377 项 Node 测试与 65 项 Vitest 测试通过,无跳过。覆盖恢复、重试、错误、取消、加载、键盘/IME 和 owner 隔离。 | -| `npm --prefix frontend run build` | pass | App 与 widget 构建通过,包内产物已重新生成。已有包体积/依赖警告不阻塞。 | -| `npm --prefix frontend run check:i18n` / `run test:webui-assets` | pass | 2 种语言 / 21 个 namespace;验证 113 个文件及 350 个内部引用。 | -| 改动文件 Ruff / Pyright | pass | 对路由、profile、tasks 和两个恢复的测试模块运行 `uvx --from ruff==0.11.12 ruff check` 与 `uvx pyright --pythonpath .venv/bin/python`:无问题。 | -| `uv run --extra dev pre-commit run --all-files` | pass | Ruff check/format、硬编码密钥检测和 YAML 密钥扫描通过。 | -| 隔离真实浏览器 | pass | 仅模拟配置:名称必填、描述保留、三步流程、无镜像输入;最终创建入口可用。390×844 窗口无横向溢出。未实际提交;临时文件、标签和 Vite 进程已清理。 | -| 契约/diff 审查 | pass | 路由/客户端/弹窗与 main 一致;profile 保留 STS/IAM/无参考模板差异,tasks 保留 IAM 诊断。无运行时查询引用或已撤回 PRD 的悬空链接。双语标识符、链接已核对;`git diff --check` 通过。 | -| 真实云创建 / 拉取就绪 | not_run | 此次撤回未授权云修改。未编辑旧失败任务快照;重载 Studio 后新建请求以使用新默认值。 | -| 全量 SDK 回归 / harness smoke | not_applicable | 此次仅撤回托管 Studio 镜像选择并恢复 main 合同;共享 SDK 与 sidecar 行为不变。 | - -本地测试证明默认值选择和兼容性,不证明云服务接受或可拉取这两个可变标签。未重启用户现有 Studio 进程。 diff --git a/prd-spec/bugfixes/mpa-create-navigation/2026-09-30-explicit-new-request.md b/prd-spec/bugfixes/mpa-create-navigation/2026-09-30-explicit-new-request.md deleted file mode 100644 index e6690798e..000000000 --- a/prd-spec/bugfixes/mpa-create-navigation/2026-09-30-explicit-new-request.md +++ /dev/null @@ -1,56 +0,0 @@ -# Explicit new MPA request - -[中文](2026-09-30-explicit-new-request.zh.md) - -Change ID: mpa-create-new-request. Date: 2026-09-30. Status: implemented. -Predecessor: [navigation fix](2026-09-30-recover-step-navigation.md). -Component: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), CON-9. - -## Evidence, goals and scope - -The directory opens `MpaCreateDialog`, which restores the regional browser draft. -Submitted drafts lock their settings. `startAnotherAgent` already resets identity, -defaults, secrets and state, but its guard/button only allow failed/cancelled tasks. -Users with uncertain POST outcomes or failed task lookup cannot open an editable -new request. The user explicitly approved generating a new request for New Agent. - -FR-1: Any submitted draft offers the explicit New Agent action, including unknown, -running and cancelling tasks. It stays disabled during a pending UI action or -configuration load. Merely reopening or navigating never changes identity. -FR-2: Reuse the existing reset: fresh request/agent IDs, step 1, unlocked inputs, -configured image/PG defaults, empty description/OpenViking/TOS secrets and task -state. Same-ID retry remains unchanged until New Agent is clicked. -FR-3: Explain that New Agent does not cancel the old task. Only browser recovery -state is replaced; no POST/cancel/cloud mutation occurs until explicit submission. -Existing per-owner active-task admission may reject submission while old work runs. -Abort old polling and ignore late responses through the existing task-ID effect. - -No API, Runtime/Worker, provisioning, task persistence or credential contract -changes. No new task-history UI or automatic cloud cleanup. Existing generated -agent IDs remain read-only. Reuse Button/ModalLayout, no CSS or dependencies. - -## Tasks, acceptance and review - -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| FR-1 | T-1: failing regression, then button/guard change | AC-1: uncertain/running/cancelling/unavailable tasks can explicitly open a fresh form; busy/loading cannot | `vitest run tests/mpaCreation.test.tsx` | -| FR-2 | T-2: reuse reset, bilingual help/user docs | AC-2: identities differ, defaults restored, all editable settings unlocked, secrets cleared, retry unchanged | dialog regression and added-statement coverage >95% | -| FR-3 | T-3: review, build, push/deploy | AC-3: no implicit start/cancel, late poll ignored; real browser edit/keyboard checks, cloud release | `npm --prefix frontend test`, `npm --prefix frontend run build`, pre-commit and browser | - -Risks: the old server task/resources remain; its recovery record is replaced on -explicit New Agent. Reopening without that action still restores it. Admission -limits remain authoritative. Rollback is the previous Studio bundle. -Review: CON-9 exception and bilingual semantics reviewed; no automatic identity -change, cancellation or secret persistence. `review-spec` is unavailable; reviewed -directly. Approval: user's explicit New Agent/new-request instruction in this chat. -Verification (2026-09-30): regression failed before the fix (7 failures), then -targeted dialog tests passed 30/30. Added executable statements covered 1/1 -(100%). Full frontend test: 1367 Node + 47 Vitest passed; production build and -pre-commit all-files (including secrets) passed. Code `4f1650de` deployed to the -existing cloud Studio (`f5d61f368888` / `xuime6ro`) at 11:02 Asia/Shanghai. -Cloud/browser: pass. The user's restored locked tab offered New Agent; Return -opened step 1 with a different ID and editable description/images. Description -and TOS bucket edits worked, OpenViking/TOS fields were enabled, and 720px-window -layout passed. Test values were cleared; no new MPA was submitted or old task -cancelled. The viewport was reset and the fresh form left open. Python checks: -not_applicable, no Python or API payload changes. diff --git a/prd-spec/bugfixes/mpa-create-navigation/2026-09-30-explicit-new-request.zh.md b/prd-spec/bugfixes/mpa-create-navigation/2026-09-30-explicit-new-request.zh.md deleted file mode 100644 index c74c37228..000000000 --- a/prd-spec/bugfixes/mpa-create-navigation/2026-09-30-explicit-new-request.zh.md +++ /dev/null @@ -1,47 +0,0 @@ -# 显式新建 MPA 请求 - -[English](2026-09-30-explicit-new-request.md) - -变更 ID:mpa-create-new-request。日期:2026-09-30。状态:implemented。 -前序:[导航修复](2026-09-30-recover-step-navigation.zh.md)。 -组件:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-9。 - -## 证据、目标与范围 - -目录打开 `MpaCreateDialog` 后恢复该地域的浏览器草稿,已提交草稿锁定配置。 -`startAnotherAgent` 已能重置身份、默认值、密钥和状态,但守卫及按钮仅允许 -失败/取消任务。POST 结果未知或任务查询失败时,用户无法开启可编辑的新请求。 -用户已明确批准“新建智能体”生成新请求。 - -FR-1:任何已提交草稿都提供显式“新建智能体”,包括未知、运行中和取消中的任务。 -UI 操作未结束或配置加载时禁用。仅重开窗口或切换步骤不改变身份。 -FR-2:复用现有重置:新请求/智能体 ID、第一步、开放输入、配置的镜像/PG 默认值, -清空描述、OpenViking/TOS 密钥及任务状态。点击新建前,同 ID 重试保持不变。 -FR-3:说明新建不会取消旧任务。仅替换浏览器恢复记录;明确提交前不发起 -POST、取消或云端变更。旧任务未结束时,现有单用户任务限制可能拒绝新提交。 -通过已有 task-ID effect 中止旧轮询并忽略迟到响应。 - -不改 API、Runtime/Worker、创建服务、任务持久化和凭据契约。不增加任务历史 UI -或自动云资源清理。生成的智能体 ID 仍只读。复用 Button/ModalLayout,不改 CSS -或增加依赖。 - -## 任务、验收与评审 - -| 需求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| FR-1 | T-1:先失败回归,再改按钮/守卫 | AC-1:未知/运行中/取消中/查询不可用任务可显式新建;忙碌/加载时不可 | `vitest run tests/mpaCreation.test.tsx` | -| FR-2 | T-2:复用重置,双语提示及用户文档 | AC-2:身份不同、默认值恢复、配置开放编辑、密钥清空、重试不变 | 弹窗回归及新增语句覆盖率 >95% | -| FR-3 | T-3:评审、构建、推送/部署 | AC-3:无隐式提交/取消,忽略旧轮询;浏览器编辑/键盘验证及云端发布 | `npm --prefix frontend test`、`npm --prefix frontend run build`、pre-commit 及浏览器 | - -风险:旧服务端任务/资源保留;明确新建后替换其浏览器恢复记录。不点击新建时, -重开仍恢复旧任务。提交限制仍由服务端执行。回滚使用上一版 Studio 包。 -评审:CON-9 例外及双语语义已核对;不自动改身份、取消或持久化密钥。 -`review-spec` 不可用,已直接评审。批准:用户在本对话明确要求新建生成新请求。 -验证(2026-09-30):修复前回归失败(7 项),修复后定向弹窗测试 30/30 pass。 -新增可执行语句覆盖 1/1(100%)。全量前端 1367 个 Node + 47 个 Vitest pass。 -生产构建及 pre-commit 全量检查(含密钥扫描)pass。代码 `4f1650de` 已于北京时间 -11:02 发布至现有云端 Studio(`f5d61f368888` / `xuime6ro`)。云端/浏览器:pass。 -用户恢复的锁定页签显示新建入口;Return 键触发后进入第一步,ID 不同,描述/镜像 -可编辑。描述及 TOS 桶名实际输入成功,OpenViking/TOS 字段可编辑,720px 窗口 -布局正常。测试值已清空,未提交新 MPA 或取消旧任务。已恢复窗口尺寸并保留新表单。 -Python 检查:not_applicable,不改 Python/API 载荷。 diff --git a/prd-spec/bugfixes/mpa-create-navigation/2026-09-30-recover-step-navigation.md b/prd-spec/bugfixes/mpa-create-navigation/2026-09-30-recover-step-navigation.md deleted file mode 100644 index 267e1a316..000000000 --- a/prd-spec/bugfixes/mpa-create-navigation/2026-09-30-recover-step-navigation.md +++ /dev/null @@ -1,45 +0,0 @@ -# Recover MPA creation step navigation - -[中文](2026-09-30-recover-step-navigation.zh.md) - -Change ID: mpa-create-navigation. Date: 2026-09-30. Status: implemented. - -## Evidence and scope - -`MpaCreateDialog` restores submitted drafts at step 3, renders inert step labels, -and hides Previous for submitted requests. A lost POST response therefore leaves -users unable to inspect earlier settings. The user requested a fix and cloud update. - -## Requirements and design - -- FR-1: Step buttons and Previous allow inspecting earlier settings after restore. - Forward navigation retains image/PG validation; busy requests disable navigation. -- FR-2: Submitted settings and retry identity stay locked. Preserve the existing - New Agent action for failed/cancelled tasks. Unknown and running requests must - retain their identity; navigation never submits or cancels a cloud request. -- FR-3: Preserve styling, keyboard focus, secret exclusion, and the API payload. - -Reuse the existing dialog and validators; no new dependency. -Update Studio creation's navigation contract. Backend provisioning, task storage, -resource cleanup and automatic recovery are outside scope. An unknown original -request may have created resources, so navigation must preserve its identity. - -## Tasks, acceptance and review - -T-1/AC-1: Add regression tests for restored drafts, locked settings, navigation, -forward validation, running/busy states and explicit fresh draft creation. -T-2/AC-2: Implement step buttons and Previous, preserving identity and locks. -T-3/AC-3: Run frontend tests/build, measure added-line coverage, push and deploy; -verify the restored-draft flow in a browser without creating cloud resources. - -Review: identity and bilingual scope reviewed; no automatic POST/reset on restore. -Approval: user's explicit fix/deploy instruction in this conversation. -Verification (2026-09-30): regression tests failed before the fix (pass as a -reproducer). Frontend suite: 1367 Node + 42 Vitest tests pass; build pass; -added executable statements: 3/3 covered (100%); pre-commit all-files pass, -including secret scans. Cloud/browser verification: pass. Code `1bed366a` -deployed to the existing Studio application `f5d61f368888`, function `xuime6ro`; -release completed at 10:31 Asia/Shanghai. The remote browser verified step clicks, -keyboard activation, Previous, and close/reopen restoration at step 3 with the -same agent ID. Submitted-draft locks/identity are covered by regression tests; -no new MPA was submitted during this navigation check. diff --git a/prd-spec/bugfixes/mpa-create-navigation/2026-09-30-recover-step-navigation.zh.md b/prd-spec/bugfixes/mpa-create-navigation/2026-09-30-recover-step-navigation.zh.md deleted file mode 100644 index e4ad2eb5c..000000000 --- a/prd-spec/bugfixes/mpa-create-navigation/2026-09-30-recover-step-navigation.zh.md +++ /dev/null @@ -1,43 +0,0 @@ -# 恢复 MPA 创建步骤导航 - -[English](2026-09-30-recover-step-navigation.md) - -变更 ID:mpa-create-navigation。日期:2026-09-30。状态:implemented。 - -## 证据与范围 - -`MpaCreateDialog` 恢复已提交草稿时进入第 3 步,步骤标题无法点击, -且隐藏已提交请求的“上一步”。POST 响应丢失后,用户无法查看之前的配置。 -用户已要求修复并更新云端。 - -## 需求与设计 - -- FR-1:步骤按钮和“上一步”支持恢复后查看前面的配置。 - 向前跳转保留镜像/PG 校验;请求忙碌时禁用导航。 -- FR-2:已提交配置与重试身份保持锁定。保留已有的失败/取消任务 - “创建另一个智能体”入口。状态未知或运行中的请求保留原身份; - 导航操作不得提交或取消任何云端请求。 -- FR-3:保留样式、键盘焦点、密钥不落盘约束和 API 载荷。 - -复用已有弹窗与校验器,不增加依赖。 -更新 Studio 创建导航契约。后端创建、任务存储、资源清理与自动恢复不在范围内。 -状态未知的原请求可能已创建资源,因此导航必须保留原请求身份。 - -## 任务、验收与评审 - -T-1/AC-1:回归测试覆盖恢复草稿、配置锁定、导航、向前校验、 -运行中/忙碌状态和明确创建新草稿。 -T-2/AC-2:实现步骤按钮与“上一步”,保留请求身份与配置锁定。 -T-3/AC-3:运行前端测试/构建、统计新增行覆盖率、推送并部署; -在浏览器验证恢复草稿流程,不创建云资源。 - -评审:身份边界与双语范围已核对;恢复时不自动 POST 或重置。 -批准:本对话中用户明确要求修复和部署。 -验证(2026-09-30):修复前回归测试失败,复现结果为 pass。 -前端 1367 个 Node + 42 个 Vitest 测试 pass;构建 pass; -新增可执行语句覆盖 3/3(100%);pre-commit 全量检查及密钥扫描 pass。 -云端/浏览器验证:pass。代码 `1bed366a` 已部署至现有 Studio 应用 -`f5d61f368888`、函数 `xuime6ro`,北京时间 10:31 发布完成。 -远程浏览器验证了步骤点击、键盘触发、“上一步”和关闭后恢复第 3 步草稿, -智能体 ID 保持不变。已提交草稿的锁定与身份保持由回归测试覆盖; -本次导航验证未提交新 MPA。 diff --git a/prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-diagnostic-completeness.md b/prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-diagnostic-completeness.md deleted file mode 100644 index a268cf3a9..000000000 --- a/prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-diagnostic-completeness.md +++ /dev/null @@ -1,39 +0,0 @@ -# Complete MPA exception diagnostics - -[中文](2026-10-20-diagnostic-completeness.zh.md) - -- Change: mpa-diagnostic-completeness; date: 2026-10-20; status: implemented. -- Predecessor: [failure display](2026-10-20-failure-details.md). -- Contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md). - -## Evidence, scope and approval -Live BytePlus retries stopped in IAM and Runtime preparation with only provider_error. IAM replaces the original exception and suppresses its chain; Runtime credential/client setup can escape operation capture. The failure summary and definition list also inherit a larger font than the 13px reason paragraph. User explicitly approved supplementing diagnostics and matching the font on 2026-10-20. No additional approval is needed for these agreed changes. - -## Requirements and scenarios -FR-1: Given a wrapped SDK/HTTP failure, retain bounded exception types, HTTP status and normalized response type before a wrapper discards context, alongside existing code/Request ID. Extract structured identifiers from bounded SDK JSON arguments and response headers where available. Unknown classes normalize to Exception; arbitrary messages, response bodies, headers, credentials, SQL and stack traces are never persisted or displayed. -FR-2: Distinguish credential loading, STS identity verification and Runtime client initialization from actual Runtime operations. Preserve original exceptions, IAM error keys/categories and existing retry/control flow, with no new cloud calls or retries. Repeated outer reports preserve the original details. -FR-3: Persist optional diagnostics in an additive SQLite column, validate on write and read, return through the existing task API and include in expanded/copied failure details. Old rows/events remain valid; per-attempt cursor, owner checks and history limit are unchanged. -FR-4: Summary, labels, values and copy feedback use 13px with 1.6 line height, matching the existing reason paragraph. Reuse the existing failure component/Button; no global/shared component overrides. Success, cancellation, ordinary agents and action buttons retain behavior. No suggestions section. - -## Design and tasks -T1: Add failing regression tests for wrapped HTTP errors, unknown types/secrets, STS failures, persisted/reopened diagnostics and frontend copy/visibility. T2: Extend managed diagnostics, IAM wrapper and Runtime setup boundaries, task persistence/types and MPA failure rendering/locales. T3: Run targeted then managed Python regressions, Ruff/Pyright, frontend tests/build/i18n/assets and isolated browser typography/keyboard/copy/narrow checks. T4: Update both contract languages and verification record. -Use allowlisted exception-chain tokens (maximum eight), integer HTTP status 100–599 and normalized response-type enum. Do not re-enable raw stderr capture. Existing failure_identifiers/classify_error used by retries remain behaviorally unchanged; diagnostic enrichment is separate. New fields are optional and require no cloud schema or credential changes. Cancellation remains uncaught by Exception handlers. - -## Review, risks and acceptance -Direct review (review-spec unavailable) checked security, compatibility, terminal propagation, cancellation, migrations and bilingual equivalence; no blockers. Frontend design skill files referenced by SPEC are unavailable locally; use the existing component and SPEC/Foundation typography rules without introducing a new design system. -AC-1: A fake SDK error wrapping HTTP/JSON failure reaches task/UI with safe cause data and no secret text. AC-2: STS failure keeps its original exception and is identified separately, without changing call counts. AC-3: Old SQLite rows and retries remain readable; no stale diagnostics. AC-4: Real browser confirms matching font, wrapping, expand/copy and absence on success. Historical lost exceptions cannot be recovered and provider errors may lack HTTP metadata. This change improves diagnosis, not underlying provider reliability. - -## Verification -See final verification below; no live resource changes authorized or required. Preserve unrelated local changes. No commit/push requested. - -## Final verification and review (2026-10-20) -Scope: working-tree diagnostic-completeness change on feat/from-upstream-main-20261020; unrelated changes preserved. -- pass: initial failing regressions reproduced missing cause fields and STS origin. Final targeted `uv run --no-sync --extra dev pytest tests/integrations/mpa_managed/test_creation_failure_details.py tests/integrations/mpa_managed/test_iam_retry.py tests/integrations/mpa_managed/test_runtime_diagnostics.py -q`: 73 passed. -- pass: `uv run --no-sync --extra dev pytest -n 2 tests/integrations/mpa_managed -q`: 1,176 passed, two upstream OpenTelemetry deprecation warnings. Existing exact diagnostic expectations updated for additive fields; retry call counts/delays remain asserted. -- pass: `npm --prefix frontend test`: 1,389 Node tests plus 146 Vitest tests. After adding the final negative display case, `cd frontend && npx vitest run tests/mpaCreationFailure.test.tsx`: 18 passed. -- pass: changed-file Ruff 0.11.12 and Pyright using `.venv/bin/python`; zero errors/warnings. `npm --prefix frontend run build`, `check:i18n`, `test:webui-assets`; packaged WebUI regenerated. Build retains existing large-chunk warnings. -- pass: isolated real-browser check of actual component/localization/CSS: all reason/summary/definition labels and values measure 13px / 20.8px line-height; 320px container has no horizontal overflow; keyboard expand, copy, successful/running/cancelled invisibility and return to failure verified. Screenshot `/tmp/mpa-diagnostic-font-preview.png` contains synthetic data. Temporary fixture/server/tab removed. Input IME not_applicable. -- pass: paired-language identifiers/links and `git diff --check`. -- fail (reviewed false positive): temporary Gitleaks v8.24.2 scan of changed source/tests/design and generated WebUI found one generic-api-key candidate in minified MarkdownPromptEditor. Read-only comparison confirmed the identical unquoted JavaScript expression already exists in HEAD; it contains no string literal or credential. No scanner rule was bypassed and unrelated generated code was not rewritten to silence it. -- not_run: real cloud create/retry (requires user restarting Studio and retrying the original task); repository-wide unrelated Python suites and all-files pre-commit (no commit requested). No resource or retry-policy changes were made. -Direct review confirms safe evidence survives IAM replacement and terminal propagation, SQLite migration preserves old rows, setup labels do not alter exceptions/call counts, and no raw exception text/body/headers or secrets reach the UI. All scoped implementation tasks complete; historical root causes remain unavailable. diff --git a/prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-diagnostic-completeness.zh.md b/prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-diagnostic-completeness.zh.md deleted file mode 100644 index 3ca314a41..000000000 --- a/prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-diagnostic-completeness.zh.md +++ /dev/null @@ -1,39 +0,0 @@ -# 补齐 MPA 异常诊断 - -[English](2026-10-20-diagnostic-completeness.md) - -- 变更:mpa-diagnostic-completeness;日期:2026-10-20;状态:implemented。 -- 前序:[失败展示](2026-10-20-failure-details.zh.md)。 -- 契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md)。 - -## 证据、范围和批准 -BytePlus 实际重试在 IAM 和 Runtime 准备阶段停止,只记录 provider_error。IAM 替换原始异常并抑制异常链;Runtime 凭据及客户端准备可能遗漏操作诊断。失败详情标题和定义列表继承了比 13px 原因段落更大的字体。用户于 2026-10-20 明确要求补齐诊断并统一字号,已批准此范围,无需重复确认。 - -## 需求和场景 -FR-1:SDK/HTTP 包装异常在上下文被丢弃前保留有界的异常类型、HTTP 状态、归一化响应类型以及已有错误码/Request ID。从有界 SDK JSON 参数及可用响应头提取结构化标识。未知异常类归一为 Exception;不持久化或展示任意消息、响应体、请求头、凭据、SQL 和堆栈。 -FR-2:区分加载凭据、STS 身份校验、Runtime 客户端初始化与实际 Runtime 操作。保留原异常、IAM 错误键及类别、既有重试和控制流,不增加云调用或重试。外层重复上报保留原始诊断。 -FR-3:以 SQLite 增量字段保存可选诊断,写入和读取均校验,通过既有任务 API 返回并加入展开及复制内容。兼容旧行和事件;尝试游标、属主校验、历史条数限制不变。 -FR-4:详情标题、标签、值和复制反馈使用 13px、1.6 行高,与已有原因段落一致。复用既有失败组件及 Button,不覆盖全局或共享组件。成功、取消、通用智能体及操作按钮行为保持不变。不添加建议区。 - -## 设计和任务 -T1:先补失败回归测试,覆盖 HTTP 包装异常、未知类及敏感值、STS 异常、诊断持久化重新读取、前端复制和展示边界。T2:扩展 managed 诊断、IAM 包装、Runtime 准备边界、任务持久化和类型、MPA 展示及翻译。T3:运行定向和 managed Python 回归、Ruff/Pyright、前端测试/构建/国际化/产物检查,并用隔离浏览器检查字号、键盘、复制、窄窗口。T4:同步双语契约及验证记录。 -使用固定异常类型链(最多八项)、100–599 整数 HTTP 状态和归一化响应类型枚举。不重新开启原始 stderr 保存。既有影响重试的 failure_identifiers/classify_error 行为不变;诊断补充独立处理。新增字段均可选,不修改云端表结构或凭据。Exception 捕获不拦截取消。 - -## 审查、风险和验收 -直接审查(review-spec 不可用)已核对安全、兼容、终态传播、取消、迁移、双语一致性,无阻塞。SPEC 引用的前端设计技能文件在本地不可用,采用已有组件及 SPEC/Foundation 字体规则,不引入新设计体系。 -AC-1:模拟 SDK 包装 HTTP/JSON 异常能通过任务传到 UI,保留安全诊断且不含敏感文本。AC-2:STS 异常保持原异常对象并独立标识,调用次数不变。AC-3:旧 SQLite 行及重试兼容,不展示旧诊断。AC-4:真实浏览器确认字号一致、换行、展开/复制及成功时隐藏。历史丢失异常无法恢复,提供方也可能不返回 HTTP 信息。本次改善可诊断性,不直接修复云接口可靠性。 - -## 验证 -见下方最终验证;不需要且未授权变更真实云资源。保留无关本地改动。未要求提交或推送。 - -## 最终验证及审查(2026-10-20) -范围:feat/from-upstream-main-20261020 工作区的诊断补齐改动;保留无关改动。 -- pass:先失败回归复现丢失异常字段及 STS 来源。最终定向命令 `uv run --no-sync --extra dev pytest tests/integrations/mpa_managed/test_creation_failure_details.py tests/integrations/mpa_managed/test_iam_retry.py tests/integrations/mpa_managed/test_runtime_diagnostics.py -q`:73 项通过。 -- pass:`uv run --no-sync --extra dev pytest -n 2 tests/integrations/mpa_managed -q`:1,176 项通过,两条上游 OpenTelemetry 弃用警告。原诊断精确预期补入新增字段,仍断言重试调用次数及间隔。 -- pass:`npm --prefix frontend test`:1,389 项 Node 测试和 146 项 Vitest 测试通过。最后新增非法展示值用例后,`cd frontend && npx vitest run tests/mpaCreationFailure.test.tsx`:18 项通过。 -- pass:修改文件的 Ruff 0.11.12 和使用 `.venv/bin/python` 的 Pyright 零错误/警告。`npm --prefix frontend run build`、`check:i18n`、`test:webui-assets` 通过,已重新生成 WebUI。构建仍有既有的大体积 chunk 警告。 -- pass:隔离真实浏览器加载实际组件、翻译和 CSS:原因/标题/详情标签和值均为 13px、20.8px 行高;320px 容器无横向溢出;键盘展开、复制、成功/运行/取消时隐藏及返回失败状态通过。截图 `/tmp/mpa-diagnostic-font-preview.png` 仅含模拟数据。临时页面、服务和浏览器标签已清理。输入法不适用。 -- pass:双语标识、相对链接及 `git diff --check`。 -- fail(已核对为误报):临时 Gitleaks v8.24.2 扫描变更源文件/测试/设计及生成 WebUI,在压缩 MarkdownPromptEditor 中命中一处 generic-api-key。只读比对确认 HEAD 已存在完全相同的不带引号的 JavaScript 表达式,不含字符串字面量或凭据。未绕过扫描规则,也未为了消除告警改写无关生成代码。 -- not_run:真实云创建/重试(需用户重启 Studio 并重试原任务);无关 Python 全仓测试及 all-files pre-commit(本次未要求提交)。未改变资源或重试策略。 -直接审查确认安全证据穿过 IAM 异常替换及终态上报、SQLite 迁移保留旧行、准备阶段标识不改变异常及调用次数,原始异常文本/响应体/请求头和密钥不进入页面。范围内实现任务完成;历史根因仍无法还原。 diff --git a/prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-failure-details.md b/prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-failure-details.md deleted file mode 100644 index 211b29443..000000000 --- a/prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-failure-details.md +++ /dev/null @@ -1,42 +0,0 @@ -# MPA creation failure details - -## Evidence and approval -The user approved failure-only diagnostics for all Volcengine and BytePlus MPA creation resources, explicitly excluding suggestions and changes to success, provisioning, retry or general-agent behavior. RuntimeCloud currently reports only Runtime writes, network/APIG wrappers discard identifiers, runner emits a generic final diagnostic, and the dialog only consumes configuration_changed. Approval: user request on 2026-10-20. - -## Requirements -R1: Failed tasks show the failing operation, a localized safe reason, provider error code and Request ID when supplied. No suggestions. All stages (account/configuration, IAM, PG/database, VPC/subnet, APIG/IM, worker, OV, skills, Runtime/readiness) have a safe fallback. -R2: Extend diagnostic capture at existing call boundaries without adding calls, retries, sleeps or changing exceptions, ordering, cloud payloads, ownership checks or recovery decisions. Preserve the originating diagnostic when exceptions are wrapped. -R3: Hide previous-attempt diagnostics from the current result only when the existing start/resume transition accepts a new attempt. Never display diagnostics on success or cancellation; ignore mismatched stages and retrying events in failed-task UI. -R4: Never expose arbitrary exception text, SDK bodies, URLs, SQL, model/PG secrets or request payloads. Show allowlisted localized explanations from structured identifiers; unknown failures explicitly state that no detailed safe reason is available. Code/Request ID retain existing bounded validation. Full raw response dumps are not part of this change. -R5: Expand/copy error details with accessible text, handle clipboard failure, preserve success DOM and all existing action/retry behavior. General agents never use this component. - -## Design and affected files -Use a terminal diagnostic annotation on exceptions for lossless safe propagation; existing report calls and retry categories remain unchanged. Capture new operations in managed/runtime.py, network_cloud.py and gateway_cloud.py; preserve already-captured PG/IAM/OV/worker/model/APM metadata. SQL failures get safe classification only, never SQL text. runner emits the originating diagnostic or a safe generic fallback. tasks.py advances an attempt cursor in its existing accepted-start transaction; old diagnostic history remains retained under the existing 100-row limit. The MPA dialog renders a separate failure component with bilingual translations, without modifying shared deployment UI. Extend diagnostics/task/resource tests and MPA frontend tests; regenerate WebUI. - -## Contract assessment and design review -Update [Studio MPA creation](../../../specs/studio-mpa-creation/README.md) with failure display/propagation and per-attempt diagnostics. Existing HTTP fields remain optional and compatible. Additive SQLite tasks.diagnostic_start_id (default 0) migration preserves old records; no cloud provisioning contract change. Direct review refined R3 from deleting diagnostic rows to an attempt cursor to preserve the existing history contract. Direct design review (review-spec unavailable): security, stale attempts, fallback, exception preservation, cancellations, bilingual equivalence and testability reviewed; no blockers. Crucially, display quota classification does not change retry classification. - -## Non-goals and risks -No new cloud troubleshooting requests, automatic cleanup or altered retry policy. Historical generic errors cannot be reconstructed. Unknown provider text is withheld rather than attempting unreliable free-text redaction. Preserve all unrelated local changes. - -## Tasks and acceptance -1. Write failing tests for resource diagnostics, safe propagation, stale-attempt isolation, successful-task invisibility and both providers. -2. Implement capture and failure-only rendering. -3. Targeted Python tests, managed regression suite, changed-file Ruff/Pyright; frontend tests/build/i18n/assets; isolated browser error/success/copy/narrow checks. -4. Reconcile bilingual docs and record actual results. Accept when quota/permission/timeout are visible with available identifiers and all existing flow tests pass. - -## Verification and final review -Scope: this working-tree failure-details diff on `feat/from-upstream-main-20261020`, 2026-10-20; unrelated collaboration-agent changes preserved. - -- pass: initial red resource/propagation and frontend tests, then implementation and regressions. Final focused Python run: `uv run --no-sync --extra dev pytest tests/integrations/mpa_managed/test_creation_failure_details.py tests/integrations/mpa_managed/test_worker_recovery.py tests/integrations/mpa_managed/test_runtime_diagnostics.py -q` (74 passed). -- pass: sequential managed suite before final hardening (1,161 passed). Final `uv run --no-sync --extra dev pytest -n 2 tests/integrations/mpa_managed -q`: 1,168 passed, two upstream OpenTelemetry deprecation warnings. Two workers limit resource use. -- pass: `npm --prefix frontend test` (1,389 Node tests and 145 Vitest tests), including general/MPA separation and existing creation/retry tests. -- pass: changed Python files checked with `uvx --from ruff==0.11.12 ruff check ...` and `uvx pyright --pythonpath .venv/bin/python ...` (zero errors/warnings). Temporary tools use the repository Python environment; no global configuration changes. Initial tool-version/interpreter-path issues were corrected and checks rerun. -- pass: real browser against an isolated fixture using the actual failure component and localization/theme: quota details, copy, keyboard expand/collapse, 360px container wrapping, missing details, success/running/cancelled suppression. Clipboard failure and retry recovery also covered by tests. Screenshot: `/tmp/mpa-creation-failure-preview.png` (synthetic data). Temporary fixtures/server removed. IME not_applicable: no input controls changed. -- not_run: live cloud create/retry/deploy; no cloud mutation required or authorized. Provider tests use fakes for both Volcengine and BytePlus. Repository-wide unrelated suites and pre-commit not_run: no commit requested, targeted managed regression plus frontend gates cover changed boundaries. - -Direct final review: no added cloud calls/retry loops or payload/order changes; existing exceptions and retry classifiers remain unchanged. New metadata only affects diagnostics. Existing known IAM/OV error keys retain specific explanations. Secrets remain excluded from UI/task records. Previous-attempt diagnostics remain in bounded history and are hidden only after an accepted restart. Public success output and ordinary-agent code are untouched. PRD/spec languages and references checked together. - - -- pass: `npm --prefix frontend run build`, `npm --prefix frontend run check:i18n` (2 locales / 22 namespaces), `npm --prefix frontend run test:webui-assets` (114 files / 351 references); packaged WebUI regenerated. -- pass: bilingual pair/identifier/relative-link checks and `git diff --check`. No commit, push or deployment performed. diff --git a/prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-failure-details.zh.md b/prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-failure-details.zh.md deleted file mode 100644 index bee44f673..000000000 --- a/prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-failure-details.zh.md +++ /dev/null @@ -1,42 +0,0 @@ -# MPA 创建失败详情 - -## 证据与批准 -用户已批准覆盖国内火山和 BytePlus 所有 MPA 创建资源的失败诊断,明确不要建议部分,不改变成功展示、创建、重试或普通智能体行为。当前 RuntimeCloud 仅记录 Runtime 写操作,网络/APIG 包装丢弃标识,runner 最终记录泛化诊断,弹窗只使用 configuration_changed。批准来源:2026-10-20 用户请求。 - -## 要求 -R1:失败任务显示具体操作、本地化安全原因、平台返回的错误码及 Request ID。无建议部分。账号/配置、IAM、PG/数据库、VPC/子网、APIG/IM、沙箱、OV、技能空间、Runtime/就绪等阶段都有安全兜底。 -R2:在已有调用边界补诊断,不新增调用、重试、等待,不改变异常、顺序、云端参数、归属检查或恢复决策。异常包装后保留源头诊断。 -R3:只有现有启动/恢复流程接受新一轮执行时,才在当前结果中隐藏上一轮诊断。成功及取消不展示诊断;失败页面忽略阶段不匹配和 retrying 事件。 -R4:不暴露任意异常正文、SDK 响应体、URL、SQL、模型/PG 密钥或请求体。根据结构化标识显示白名单本地化解释;未知失败明确说明没有可展示的详细安全原因。错误码/Request ID 沿用有界校验。不展示完整原始响应。 -R5:支持展开/复制错误详情,提供可访问文本和复制失败反馈,保留成功 DOM 及现有操作/重试行为。普通智能体不使用该组件。 - -## 设计与影响文件 -通过异常上的终态诊断标记无损传递安全信息;已有 report 及重试分类不变。在 managed/runtime.py、network_cloud.py、gateway_cloud.py 补操作记录,保留 PG/IAM/OV/worker/model/APM 已采集的元数据。SQL 错误只做安全分类,不展示 SQL。runner 输出源头诊断或安全兜底;tasks.py 在已有的接受启动事务中推进诊断轮次游标,保留原有最多 100 条的历史记录。MPA 弹窗渲染独立失败组件及双语文案,不修改共用部署 UI。扩展诊断/任务/资源及 MPA 前端测试,重新构建 WebUI。 - -## 契约评估与设计审查 -更新 [Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),补充失败展示/传递及轮次隔离。已有 HTTP 字段继续可选且兼容。SQLite tasks 新增 diagnostic_start_id 列(默认 0),兼容旧记录,不改云端创建契约。直接审查将 R3 从删除诊断记录细化为轮次游标,以保留已有历史记录契约。直接设计审查(review-spec 不可用):已核对安全、过期轮次、兜底、异常保留、取消、双语一致性及可测试性,无阻塞。特别注意:展示时的配额分类不改变重试分类。 - -## 非目标与风险 -不新增云端排查请求、自动清理或修改重试策略。历史泛化错误无法恢复。未知平台正文不直接展示,避免依赖不可靠的自由文本脱敏。保留所有无关本地改动。 - -## 任务与验收 -1. 先写资源诊断、安全传递、旧轮次隔离、成功态不显示及两种云平台的失败测试。 -2. 实现诊断采集及仅失败时渲染。 -3. 定向 Python 测试、managed 回归、修改文件 Ruff/Pyright;前端测试/构建/国际化/资源;隔离浏览器错误/成功/复制/窄屏检查。 -4. 同步双语文档并记录实测结果。验收:配额/权限/超时及可用标识可见,现有流程测试通过。 - -## 验证与最终审查 -范围:2026-10-20 `feat/from-upstream-main-20261020` 工作区本次失败详情修改;保留无关的协作智能体改动。 - -- pass:先执行失败的资源/传递和前端测试,再实现并回归。最终定向 Python:`uv run --no-sync --extra dev pytest tests/integrations/mpa_managed/test_creation_failure_details.py tests/integrations/mpa_managed/test_worker_recovery.py tests/integrations/mpa_managed/test_runtime_diagnostics.py -q`(74 项通过)。 -- pass:最终加固前串行 managed 回归(1,161 项通过);最终 `uv run --no-sync --extra dev pytest -n 2 tests/integrations/mpa_managed -q`:1,168 项通过,两个上游 OpenTelemetry 弃用警告;使用两个 worker 控制资源占用。 -- pass:`npm --prefix frontend test`(1,389 项 Node 测试、145 项 Vitest 测试),包含通用/MPA 隔离及现有创建/重试测试。 -- pass:修改的 Python 文件使用 `uvx --from ruff==0.11.12 ruff check ...` 和 `uvx pyright --pythonpath .venv/bin/python ...`(零错误/警告)。临时工具读取仓库 Python 环境,未修改全局配置;初次工具版本/解释器路径问题已纠正并重跑。 -- pass:真实浏览器使用实际失败组件、双语文案和主题的隔离页面,验证配额详情、复制、键盘展开/收起、360px 容器换行、缺少详情、成功/创建中/取消不显示。复制失败及重试恢复另有自动测试。截图 `/tmp/mpa-creation-failure-preview.png` 为模拟数据。临时页面与服务已移除。IME 为 not_applicable:未改输入控件。 -- not_run:真实云端创建/重试/部署,无需且未授权云端写操作;平台测试均模拟火山及 BytePlus。全仓无关测试与 pre-commit 为 not_run:未要求提交;定向 managed 回归及前端检查覆盖所改边界。 - -直接最终审查:未新增云端调用/重试循环或改变参数/顺序,现有异常和重试分类器保持不变;新增元数据只影响诊断。已有 IAM/OV 错误键仍提供具体解释。密钥不进入页面/任务记录。旧轮次诊断保留在有界历史中,仅接受重启后隐藏。成功输出及通用智能体代码未改动。PRD/spec 双语与引用一起核对。 - - -- pass:`npm --prefix frontend run build`、`npm --prefix frontend run check:i18n`(2 种语言 / 22 个命名空间)、`npm --prefix frontend run test:webui-assets`(114 个文件 / 351 处引用);已重新生成发布 WebUI。 -- pass:双语配对/标识/相对链接检查和 `git diff --check`。未提交、推送或部署。 diff --git a/prd-spec/bugfixes/mpa-creation-local-state/2026-10-15-local-task-recovery.md b/prd-spec/bugfixes/mpa-creation-local-state/2026-10-15-local-task-recovery.md deleted file mode 100644 index 90b1ae263..000000000 --- a/prd-spec/bugfixes/mpa-creation-local-state/2026-10-15-local-task-recovery.md +++ /dev/null @@ -1,103 +0,0 @@ -# MPA Creation Local Task Recovery - -[中文版](2026-10-15-local-task-recovery.zh.md) - -## Metadata - -- Change ID: `mpa-creation-local-state` -- Created: 2026-10-15 -- Revised: 2026-10-15 -- Status: approved -- Related component spec: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md) - -## Background and Evidence - -Studio MPA creation already persists per-owner task state in a local SQLite database. However, the frontend mainly restores progress from browser `sessionStorage`. After refresh or storage loss, the dialog cannot discover the latest local task by owner and region. Local MPA operation recovery may also be unavailable when Studio TOS storage is not configured. - -The `wlc-ap-test` BytePlus run also showed an intent mismatch after the operator changed TOS configuration between retries. The server removed `tosBucket` from the stored task payload but passed it to the child process, while Worker hashing included the resulting TOS mount configuration. That allowed the task layer to treat the request as unchanged even though the Worker intent changed, causing a late `configuration_changed` failure. - -After the AIDAP retry fix, the same task reached the Worker stage repeatedly and BytePlus rejected `create_worker` with `InvalidParameter.TosEndpoint`. Retries with the legacy `https://tos-ap-southeast-1.bytepluses.com`, host-only `tos-ap-southeast-1.bytepluses.com`, omitted endpoint, and AgentKit-style `http://tos-ap-southeast-1.bytepluses.com` shapes failed, including request IDs `2026101511483686035D67AE3B4A1C41A8`, `20261015115339C02052F0ED43FE23806D`, `2026101512015520E30384A95D0B0B510C`, and `202610151214245AE8256781CAFD0F5F45`. This shows BytePlus Tool creation currently does not accept the Tool-level TOS mount configuration. Agent creation must therefore degrade by creating the Worker without a Tool TOS mount while preserving the rest of the MPA resources. - -## Goals and Non-Goals - -Goals: - -- Add an owner-scoped local task listing endpoint for refresh recovery. -- Let the frontend recover the latest submitted task from the server when `sessionStorage` is absent or stale. -- Persist nonsecret `tosBucket` as part of task identity so changed TOS configuration is rejected early. -- Include the latest safe diagnostic summary in recovered task payloads. -- Retry transient AIDAP read/query failures with bounded backoff while preserving create idempotency safety. -- Try the AgentKit Tool mount endpoint shape `http://tos-ap-southeast-1.bytepluses.com` for BytePlus IAM role mounts, but automatically degrade to Worker creation without Tool-level TOS mount when BytePlus rejects `TosEndpoint`. -- Keep degraded retries idempotent and clear Runtime TOS mount environment flags so the Runtime does not assume a mount that was not created. - -Non-goals: - -- Change domestic Volcengine Worker TOS mount serialization. -- Persist TOS access keys or secrets. -- Replace the durable production MPA operation service. -- Add a full recovery console in this change. -- Retry `CreateWorkspace`, which lacks an idempotency token. - -## Requirements - -- `FR-1`: Given a submitted task exists in the local SQLite database, when the dialog opens after refresh without browser recovery state, then it can recover and poll the latest task for the current owner and region. -- `FR-2`: Given the same `requestId` is submitted with a different `tosBucket`, then the server rejects it at task admission instead of reaching Worker hash validation. -- `FR-3`: Given a task has diagnostics, then the latest diagnostic is returned as safe structured metadata and contains no raw provider text or credentials. -- `FR-4`: Given the task is passed to the child process, then nonsecret `tosBucket` still reaches the TOS configuration path and no TOS secret is persisted. -- `FR-5`: Given a read-only AIDAP call such as branch, compute, endpoint, account, database, connection, list or detail fails with a transient provider error, then the create flow retries with bounded backoff and records the exact safe AIDAP operation. -- `FR-6`: Given `CreateWorkspace` fails, then it is not automatically retried because the cloud API lacks a client idempotency token. -- `FR-7`: Given BytePlus Worker creation uses an IAM role TOS mount, then the first create payload uses `MountPoints[].Endpoint=http://tos-ap-southeast-1.bytepluses.com`, and unfinished legacy intents hashed with omitted endpoint, `tos-ap-southeast-1.bytepluses.com`, or `https://tos-ap-southeast-1.bytepluses.com` are upgraded instead of being rejected as changed inputs. -- `FR-8`: Given BytePlus rejects Worker creation with `InvalidParameter.TosEndpoint`, then the same creation flow records a safe diagnostic and retries Worker creation without `TosMountConfig`, with a new client token and a persisted degradation marker. -- `FR-9`: Given Worker TOS mount is degraded, then Runtime deployment does not set `MPA_CODEX_WORKER_TOS_MOUNT_ENABLED` or `MPA_CODEX_WORKER_TOS_BUCKET`. - -## Design and Contract Impact - -`CreationTasks` will expose an owner-scoped `list()` method returning recent tasks. `GET /web/mpa-creation/tasks?region=...` will call it and return `{tasks:[...]}`. Each task keeps the existing task shape and may add `latestDiagnostic`. - -The task payload will keep `tosBucket` when present. The child input will still receive a separate `tos` object, and `_run()` will not forward `tosBucket` through unrelated resource fields. This makes the existing duplicate request comparison catch TOS changes before child startup. - -Frontend `MpaCreateDialog` will call the list endpoint when no submitted task is already restored from session storage, then attach to the most recent submitted task for the region. Browser storage remains an optimization, not the only source of truth. - -Component spec impact: this is an additive HTTP endpoint and a stricter request identity check. Existing task query endpoints remain unchanged. - -AIDAP read calls use up to eight total attempts with bounded exponential backoff. Retryable categories are `connection`, `timeout`, `throttled`, and `unavailable`. Permission, authentication, invalid parameter, ownership/configuration, missing resource and protocol-shape failures fail closed. `create_workspace` remains single-attempt to avoid duplicate resources. - -BytePlus Worker TOS mount serialization rewrites `MountPoints[].Endpoint` from the domestic AgentKit default to `http://tos-ap-southeast-1.bytepluses.com` before hashing and cloud submission. The Worker retry path recognizes unfinished legacy hashes that omitted the endpoint, included the original HTTPS BytePlus endpoint, or included the later host-only endpoint, then upgrades the stored hash to the HTTP endpoint request. - -If the provider still returns `InvalidParameter.TosEndpoint`, the Worker provisioner records `worker_tos_mount_disabled=true` with a safe reason, switches to a request without `TosMountConfig`, rotates the client token to avoid idempotency conflicts across changed payloads, and retries `create_worker`. `provision()` rereads the registry record after Worker creation and reapplies Runtime TOS env from the actual Worker state so degraded workers do not advertise an unavailable mount. - -## Tasks - -- `T-1`: Add `CreationTasks.list()` with latest diagnostic summary. -- `T-2`: Add `GET /web/mpa-creation/tasks`. -- `T-3`: Keep `tosBucket` in task payload while still passing `tos` to the child. -- `T-4`: Add frontend client and dialog recovery logic. -- `T-5`: Add backend and focused frontend tests. -- `T-6`: Add AIDAP read retry diagnostics and tests while keeping Workspace creation single-attempt. -- `T-7`: Adjust BytePlus Worker TOS mount serialization and legacy hash recovery. -- `T-8`: Add BytePlus Worker TOS mount degradation and Runtime env reconciliation. - -## Verification and Acceptance - -| Requirement | Task | Acceptance criterion | Test or verification command | Result/evidence | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1`, `T-2`, `T-4`, `T-5` | Refresh recovery can discover a submitted task without session storage | `uv run --extra dev pytest tests/integrations/mpa_managed/test_tasks.py tests/integrations/mpa_managed/test_routes.py -q`; `npm --prefix frontend test -- mpaCreation.test.tsx --runInBand` | `pass`, backend 34 passed and frontend 1389 Node tests plus 117 Vitest tests passed on 2026-10-15 | -| `FR-2` | `T-3`, `T-5` | Changed `tosBucket` for the same request is rejected at admission | Same backend pytest command | `pass`, backend 34 passed on 2026-10-15 | -| `FR-3` | `T-1`, `T-5` | Latest diagnostic is safe and structured | Same backend pytest command | `pass`, backend 34 passed on 2026-10-15 | -| `FR-4` | `T-3`, `T-5` | Child receives TOS selection and secrets stay out of persistence | Same backend pytest command | `pass`, backend 34 passed on 2026-10-15 | -| `FR-5` | `T-6` | Transient AIDAP read failures retry and report the concrete AIDAP operation | `uv run --extra dev pytest tests/integrations/mpa_managed/test_auto_pg.py tests/integrations/mpa_managed/test_byteplus.py tests/integrations/mpa_managed/test_tasks.py tests/integrations/mpa_managed/test_routes.py -q`; `uv run --python 3.13 --extra dev pytest tests/integrations/mpa_managed/test_auto_pg.py::test_sdk_read_calls_tolerate_repeated_cross_region_failures -q` | `pass`, 129 passed plus focused repeated-failure coverage on 2026-10-15 | -| `FR-6` | `T-6` | `CreateWorkspace` failures do not retry automatically | Same AIDAP/PG pytest command | `pass`, 129 passed on 2026-10-15 | -| `FR-7` | `T-7` | BytePlus Worker create uses the HTTP TOS Endpoint and legacy endpoint hashes resume | `uv run --python 3.13 --extra dev pytest tests/integrations/mpa_managed/test_byteplus.py::test_byteplus_worker_mount_and_environment_are_overseas tests/integrations/mpa_managed/test_byteplus.py::test_byteplus_worker_recovers_legacy_tos_endpoint -q` | `pass`, covered by focused 6-test run on 2026-10-15 | -| `FR-8` | `T-8` | BytePlus `InvalidParameter.TosEndpoint` falls back to Worker creation without Tool TOS mount | `uv run --python 3.13 --extra dev pytest tests/integrations/mpa_managed/test_byteplus.py::test_byteplus_worker_tos_endpoint_failure_falls_back_without_mount -q` | `pass`, covered by focused 6-test run on 2026-10-15 | -| `FR-9` | `T-8` | Runtime TOS env is cleared when Worker mount is degraded | `uv run --python 3.13 --extra dev pytest tests/integrations/mpa_managed/test_service.py::test_tos_runtime_env_is_owned_and_contains_no_credentials -q` | `pass`, covered by focused 6-test run on 2026-10-15 | - -## Risks - -- Local SQLite remains instance-local; it is suitable for local Studio and single-instance deployments, not cross-instance production recovery. -- Listing recent tasks must stay owner-scoped to avoid exposing another user's drafts or cloud resource names. - -## Review and Delivery Record - -Design review conclusion: the change is a narrow reliability fix over existing local task storage. It avoids new cloud side effects and makes TOS changes fail earlier. - -Implementation completed on 2026-10-15. Targeted backend tests, focused frontend tests, AIDAP/PG retry tests, BytePlus Worker TOS endpoint/degradation regression tests, Ruff, i18n check, frontend build and `git diff --check` passed. The frontend build emitted existing chunk-size and mixed dynamic/static import warnings. diff --git a/prd-spec/bugfixes/mpa-creation-local-state/2026-10-15-local-task-recovery.zh.md b/prd-spec/bugfixes/mpa-creation-local-state/2026-10-15-local-task-recovery.zh.md deleted file mode 100644 index e4654e748..000000000 --- a/prd-spec/bugfixes/mpa-creation-local-state/2026-10-15-local-task-recovery.zh.md +++ /dev/null @@ -1,103 +0,0 @@ -# MPA 创建本地任务恢复 - -[English](2026-10-15-local-task-recovery.md) - -## 元数据 - -- 变更 ID:`mpa-creation-local-state` -- 创建日期:2026-10-15 -- 修订日期:2026-10-15 -- 状态:approved -- 相关组件规格:[Studio MPA creation](../../../specs/studio-mpa-creation/README.zh.md) - -## 背景与证据 - -Studio MPA 创建已经将 owner 维度的任务状态持久化到本地 SQLite 数据库。但前端主要依赖浏览器 `sessionStorage` 恢复进度。刷新、缓存丢失或草稿被替换后,弹窗无法按当前 owner 和地域从服务端发现最近任务。本地未配置 Studio TOS 存储时,MPA operation service 也可能不可用。 - -`wlc-ap-test` 的 BytePlus 创建暴露了另一个问题:操作者在重试之间修改了 TOS 配置。服务端把 `tosBucket` 从存储的任务 payload 中移除,只单独传给子进程;而 Worker 哈希包含最终 TOS mount 配置。结果是任务层认为请求未变化,但 Worker intent 已变化,最后在 Worker 阶段才报 `configuration_changed`。 - -AIDAP 重试修复后,同一任务已能多次推进到 Worker 阶段,但 BytePlus `create_worker` 持续返回 `InvalidParameter.TosEndpoint`。使用旧的 `https://tos-ap-southeast-1.bytepluses.com`、后续改成的 `tos-ap-southeast-1.bytepluses.com`、省略 endpoint,以及 AgentKit 风格的 `http://tos-ap-southeast-1.bytepluses.com` 都被拒绝,对应 request ID 包括 `2026101511483686035D67AE3B4A1C41A8`、`20261015115339C02052F0ED43FE23806D`、`2026101512015520E30384A95D0B0B510C` 和 `202610151214245AE8256781CAFD0F5F45`。这说明 BytePlus Tool 创建当前不接受 Tool 级 TOS mount 配置。Agent 创建应降级为不带 Tool TOS mount 创建 Worker,同时保留其余 MPA 资源。 - -## 目标与非目标 - -目标: - -- 增加按 owner 隔离的本地任务列表接口,用于刷新恢复。 -- 当前端没有或丢失 `sessionStorage` 时,可从服务端恢复当前 owner 和地域下最近的已提交任务。 -- 将非密的 `tosBucket` 纳入任务身份,TOS 配置变化在提交阶段尽早拒绝。 -- 在恢复的任务载荷中包含最新安全诊断摘要。 -- 对暂时性的 AIDAP 读/查询失败做有界退避重试,同时保持创建 Workspace 的幂等安全边界。 -- BytePlus IAM role 模式的 Worker TOS mount 先尝试 AgentKit Tool mount endpoint 形态 `http://tos-ap-southeast-1.bytepluses.com`;当 BytePlus 拒绝 `TosEndpoint` 时,自动降级为不带 Tool 级 TOS mount 创建 Worker。 -- 降级后的重试保持幂等,并清理 Runtime TOS mount 环境变量,避免 Runtime 误认为 Worker 已挂载。 - -非目标: - -- 不改变国内火山 Worker TOS mount 序列化。 -- 不持久化 TOS access key、secret 等密钥。 -- 不替代生产环境需要的持久化 MPA operation service。 -- 本次不建设完整恢复控制台。 -- 不自动重试缺少幂等 token 的 `CreateWorkspace`。 - -## 需求 - -- `FR-1`:给定本地 SQLite 中已有提交任务,当弹窗刷新后没有浏览器恢复状态时,应能按当前 owner 和地域恢复并继续轮询最近任务。 -- `FR-2`:给定同一 `requestId` 使用不同 `tosBucket` 再次提交,服务端应在任务接收阶段拒绝,而不是进入 Worker 哈希校验后失败。 -- `FR-3`:给定任务已有诊断,恢复任务应返回最新的安全结构化诊断,不包含原始服务商文案或凭据。 -- `FR-4`:给定任务进入子进程,非密 `tosBucket` 仍应进入 TOS 配置路径,且 TOS 密钥不得持久化。 -- `FR-5`:给定 branch、compute、endpoint、account、database、connection、list 或 detail 等只读 AIDAP 调用遇到暂时性服务商错误,创建流程应有界退避重试,并记录具体安全 AIDAP 操作名。 -- `FR-6`:给定 `CreateWorkspace` 失败,不得自动重试,因为云 API 没有客户端幂等 token。 -- `FR-7`:给定 BytePlus Worker 创建使用 IAM role TOS mount,首次创建 payload 应使用 `MountPoints[].Endpoint=http://tos-ap-southeast-1.bytepluses.com`;已用省略 endpoint、`tos-ap-southeast-1.bytepluses.com` 或 `https://tos-ap-southeast-1.bytepluses.com` 计算 hash 的未完成旧 intent 应被升级,而不是被拒绝为输入变化。 -- `FR-8`:给定 BytePlus 以 `InvalidParameter.TosEndpoint` 拒绝 Worker 创建,同一创建流程应记录安全诊断,并使用新的客户端 token 以不带 `TosMountConfig` 的请求重试 Worker 创建,同时持久化降级标记。 -- `FR-9`:给定 Worker TOS mount 已降级,Runtime 部署不应设置 `MPA_CODEX_WORKER_TOS_MOUNT_ENABLED` 或 `MPA_CODEX_WORKER_TOS_BUCKET`。 - -## 设计与契约影响 - -`CreationTasks` 增加按 owner 查询最近任务的 `list()` 方法。`GET /web/mpa-creation/tasks?region=...` 调用它并返回 `{tasks:[...]}`。每个任务保持现有任务形状,并可新增 `latestDiagnostic`。 - -任务 payload 会在存在 `tosBucket` 时保留该字段。子进程输入仍单独包含 `tos` 对象,`_run()` 不会将 `tosBucket` 作为无关资源字段传递。这样现有重复请求比较会在子进程启动前捕获 TOS 变化。 - -前端 `MpaCreateDialog` 在没有从 session storage 恢复到提交任务时调用列表接口,并挂接当前地域最近的已提交任务。浏览器存储继续作为快速恢复缓存,而不是唯一状态源。 - -组件规格影响:新增一个 HTTP 查询端点,并收紧请求身份一致性。现有单任务查询端点不变。 - -AIDAP 读调用最多执行八次尝试,使用有界指数退避。可重试类别为 `connection`、`timeout`、`throttled` 和 `unavailable`。权限、认证、参数非法、归属/配置冲突、资源缺失和协议形态错误均快速失败。`create_workspace` 保持单次调用,避免重复资源。 - -BytePlus Worker TOS mount 序列化会先将 AgentKit 生成的国内默认 `MountPoints[].Endpoint` 改写为 `http://tos-ap-southeast-1.bytepluses.com`,再计算 hash 并提交云端。Worker 重试路径识别省略 endpoint、原始 HTTPS BytePlus endpoint 或后续 host-only endpoint 的未完成旧 hash,并将存储 hash 升级为 HTTP endpoint 请求。 - -如果服务商仍返回 `InvalidParameter.TosEndpoint`,Worker provisioner 会记录 `worker_tos_mount_disabled=true` 和安全原因,切换到不带 `TosMountConfig` 的请求,轮换客户端 token 以避免变更 payload 的幂等冲突,并重试 `create_worker`。`provision()` 在 Worker 创建后重新读取 registry record,并按实际 Worker 状态重新应用 Runtime TOS env,避免降级后的 Worker 暴露不可用挂载。 - -## 任务 - -- `T-1`:新增带最新诊断摘要的 `CreationTasks.list()`。 -- `T-2`:新增 `GET /web/mpa-creation/tasks`。 -- `T-3`:任务 payload 保留 `tosBucket`,同时继续将 `tos` 传给子进程。 -- `T-4`:新增前端客户端与弹窗恢复逻辑。 -- `T-5`:补充后端与聚焦前端测试。 -- `T-6`:增加 AIDAP 读重试诊断与测试,同时保持 Workspace 创建单次执行。 -- `T-7`:调整 BytePlus Worker TOS mount 序列化和旧 hash 恢复。 -- `T-8`:增加 BytePlus Worker TOS mount 降级与 Runtime env 对齐。 - -## 验证与验收 - -| 需求 | 任务 | 验收标准 | 测试或验证命令 | 结果/证据 | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1`, `T-2`, `T-4`, `T-5` | 无 session storage 时可从本地服务端恢复已提交任务 | `uv run --extra dev pytest tests/integrations/mpa_managed/test_tasks.py tests/integrations/mpa_managed/test_routes.py -q`;`npm --prefix frontend test -- mpaCreation.test.tsx --runInBand` | `pass`,2026-10-15 后端 34 个测试通过,前端 1389 个 Node 测试和 117 个 Vitest 测试通过 | -| `FR-2` | `T-3`, `T-5` | 同一请求修改 `tosBucket` 会在任务接收阶段拒绝 | 同一后端 pytest 命令 | `pass`,2026-10-15 后端 34 个测试通过 | -| `FR-3` | `T-1`, `T-5` | 最新诊断为安全结构化数据 | 同一后端 pytest 命令 | `pass`,2026-10-15 后端 34 个测试通过 | -| `FR-4` | `T-3`, `T-5` | 子进程收到 TOS 选择且密钥不持久化 | 同一后端 pytest 命令 | `pass`,2026-10-15 后端 34 个测试通过 | -| `FR-5` | `T-6` | 暂时性 AIDAP 读失败会重试并记录具体 AIDAP 操作 | `uv run --extra dev pytest tests/integrations/mpa_managed/test_auto_pg.py tests/integrations/mpa_managed/test_byteplus.py tests/integrations/mpa_managed/test_tasks.py tests/integrations/mpa_managed/test_routes.py -q`;`uv run --python 3.13 --extra dev pytest tests/integrations/mpa_managed/test_auto_pg.py::test_sdk_read_calls_tolerate_repeated_cross_region_failures -q` | `pass`,2026-10-15 共 129 个测试通过,并补充重复失败聚焦覆盖 | -| `FR-6` | `T-6` | `CreateWorkspace` 失败不会自动重试 | 同一 AIDAP/PG pytest 命令 | `pass`,2026-10-15 共 129 个测试通过 | -| `FR-7` | `T-7` | BytePlus Worker 创建使用 HTTP TOS Endpoint,且旧 endpoint hash 可恢复 | `uv run --python 3.13 --extra dev pytest tests/integrations/mpa_managed/test_byteplus.py::test_byteplus_worker_mount_and_environment_are_overseas tests/integrations/mpa_managed/test_byteplus.py::test_byteplus_worker_recovers_legacy_tos_endpoint -q` | `pass`,2026-10-15 聚焦 6 个测试覆盖 | -| `FR-8` | `T-8` | BytePlus `InvalidParameter.TosEndpoint` 会降级为不带 Tool TOS mount 创建 Worker | `uv run --python 3.13 --extra dev pytest tests/integrations/mpa_managed/test_byteplus.py::test_byteplus_worker_tos_endpoint_failure_falls_back_without_mount -q` | `pass`,2026-10-15 聚焦 6 个测试覆盖 | -| `FR-9` | `T-8` | Worker mount 降级时清理 Runtime TOS env | `uv run --python 3.13 --extra dev pytest tests/integrations/mpa_managed/test_service.py::test_tos_runtime_env_is_owned_and_contains_no_credentials -q` | `pass`,2026-10-15 聚焦 6 个测试覆盖 | - -## 风险 - -- 本地 SQLite 仍是实例本地状态,适合本地 Studio 和单实例部署,不等价于跨实例生产恢复。 -- 最近任务列表必须按 owner 隔离,避免暴露其他用户的草稿或云资源名称。 - -## 评审与交付记录 - -设计评审结论:这是基于已有本地任务存储的窄范围可靠性修复,不增加新的云侧副作用,并让 TOS 变化更早失败。 - -实现已于 2026-10-15 完成。定向后端测试、聚焦前端测试、AIDAP/PG 重试测试、BytePlus Worker TOS endpoint/降级回归测试、Ruff、i18n 检查、前端构建和 `git diff --check` 均通过。前端构建仍输出既有的大 chunk 与动态/静态混合导入警告。 diff --git a/prd-spec/bugfixes/mpa-default-jwt/2026-09-15-default-jwt.md b/prd-spec/bugfixes/mpa-default-jwt/2026-09-15-default-jwt.md deleted file mode 100644 index 1d2bd40b7..000000000 --- a/prd-spec/bugfixes/mpa-default-jwt/2026-09-15-default-jwt.md +++ /dev/null @@ -1,9 +0,0 @@ -# Default JWT setting for VeADK MPA provisioning - -The user approved adding this deployment default on 2026-09-15. MPA deployments have no ArkClaw identity pools, but REST endpoints currently require JWT unless explicitly configured. Add DISABLE_JWT_AUTH=true to build_runtime_env defaults. Existing extra_env processing remains last so explicit false wins. Ordinary Agent deployments, MPA backend defaults, gateway authentication, and existing TIP defaults are outside scope. - -Change only the provisioning helper, focused tests, and this bilingual specification update. Acceptance: omitted value becomes true; explicit false remains false; input overrides are not mutated. Test the helper and CLI callers, measure changed-line coverage above 95%, and run Ruff, Pyright and pre-commit. No cloud mutation is required. - -Design review: existing override precedence provides the required behavior without new configuration or branches. No unresolved design issues. User approval is the explicit request to implement on a new branch. - -Verification (2026-09-15): regression failed with missing DISABLE_JWT_AUTH before implementation; helper and CLI tests pass (26). pytest-cov reports 98.65% statement coverage for the provisioning module. The added constant dictionary entry has no independent executable line; its containing dictionary construction is covered, and default/explicit-false behavior is asserted. Changed behavior coverage is complete; a separate added executable production-line percentage is not applicable. Ruff, formatting, secret scanning and diff whitespace pass. Production-file Pyright passes; test-file Pyright reports two unchanged diagnostics at line 47, reproduced from HEAD. Remote provisioning base was fetched and rebased before final checks (already current). No cloud deployment or live Runtime mutation was performed. Review found no new correctness or contract issues. diff --git a/prd-spec/bugfixes/mpa-default-jwt/2026-09-15-default-jwt.zh.md b/prd-spec/bugfixes/mpa-default-jwt/2026-09-15-default-jwt.zh.md deleted file mode 100644 index 22b2ddc59..000000000 --- a/prd-spec/bugfixes/mpa-default-jwt/2026-09-15-default-jwt.zh.md +++ /dev/null @@ -1,9 +0,0 @@ -# VeADK MPA 部署默认 JWT 配置 - -用户于 2026-09-15 明确批准添加此部署默认值。MPA 部署没有 ArkClaw 身份池,但 REST 接口目前默认要求 JWT。在 build_runtime_env 默认值中添加 DISABLE_JWT_AUTH=true,保留 extra_env 最后覆盖,因此用户显式 false 优先。普通 Agent 部署、MPA 后端默认值、网关鉴权和现有 TIP 默认值不在修改范围内。 - -仅修改部署辅助函数、相关测试及本次双语规范。验收:未配置时为 true;显式 false 保持 false;不修改传入的覆盖字典。运行辅助函数和 CLI 调用测试,验证增量行覆盖率超过 95%,执行 Ruff、Pyright 和 pre-commit。不需要修改云资源。 - -设计复核:复用现有覆盖顺序,无需新配置项或分支。无未解决设计问题。用户明确要求在新分支实现,构成本次批准。 - -验证(2026-09-15):实现前回归因缺少 DISABLE_JWT_AUTH 失败;环境组装和 CLI 测试共 26 项通过。pytest-cov 统计部署模块语句覆盖率为 98.65%。新增字典常量项不对应独立可执行行,其所属字典构造语句已覆盖,默认值与显式 false 的行为均有断言。新增行为全部覆盖,独立新增生产代码可执行行覆盖率不适用。Ruff、格式、密钥扫描和 diff 空白检查通过。生产文件 Pyright 通过;测试文件第 47 行存在两处未变化的诊断,已使用 HEAD 版本复现。最终检查前已 fetch 并 rebase 远端部署基线(已是最新)。没有执行云端部署或修改在线 Runtime。复核未发现新的正确性或契约问题。 diff --git a/prd-spec/bugfixes/mpa-default-jwt/2026-09-30-studio-key-auth-default.md b/prd-spec/bugfixes/mpa-default-jwt/2026-09-30-studio-key-auth-default.md deleted file mode 100644 index 22a2294dc..000000000 --- a/prd-spec/bugfixes/mpa-default-jwt/2026-09-30-studio-key-auth-default.md +++ /dev/null @@ -1,98 +0,0 @@ -# Restore the Studio MPA JWT bypass default - -[中文版](2026-09-30-studio-key-auth-default.zh.md) - -- Change ID: `mpa-studio-key-auth-default` -- Created/revised: 2026-09-30 -- Status: approved -- Contract: [MPA Runtime provisioning](../../../specs/mpa-runtime-provisioning/README.md) -- Predecessor: [A2A discovery defaults](../2026-09-23-mpa-a2a-discovery-default.md); supersedes only its JWT default decision. - -## Background and scope - -`build_runtime_env` currently injects `DISABLE_JWT_AUTH=false`. Studio uses the -Runtime API key, not a legacy `X-Jwt-Token`, so JWT-gated REST calls such as file -download can fail. The user explicitly requested changing this existing PR's -default to `true`. Both managed flat creation and `veadk mpa create` consume the -shared helper; referenced Runtime/templates use their own environments. - -Goals: restore the shared default and retain explicit overrides. Non-goals: -cloud deployment, existing Runtime/session migration, image builds, frontend -changes, JWT issuance, or enabling `MPA_AGENTKIT_MODE`. - -## Requirements and design - -- `FR-1`: Given fresh flat inputs without an override, the generated environment - contains `DISABLE_JWT_AUTH=true`, `ENABLE_A2A=true`, and - `A2A_TIP_VERIFY_ENABLED=false`; outer gateway key-auth is unchanged. -- `FR-2`: Given explicit `extra_env` or `managed.runtime.env` with - `DISABLE_JWT_AUTH=false`, it wins without mutating the input mapping. -- `FR-3`: Existing Runtime/template environments and deployed resources are not - automatically changed. `MPA_AGENTKIT_MODE` remains unset by this helper. - -Change one default in `build_runtime_env`, not its callers. Reuse the existing -override mechanism and regression tests; add no configuration or dependency. -There are no schema, persistence, concurrency, or retry changes. - -Security: bypassing the inner legacy REST JWT gate also affects admin checks -and header-based identity, not just download. APIG key-auth remains required; -trusted Studio/gateway callers must control user headers. Endpoints that obtain -a Runtime principal directly are not fixed by this flag. With a compatible -MPA image, `/list-apps` may advertise ADK instead of forcing A2A discovery; -this change does not guarantee transport compatibility or migrate old sessions. -Operators needing legacy JWT must explicitly set `false`. Recovery is an -explicit configuration override and release, not an automatic rollback. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | Verification | Result | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1`: update default and paired contracts/docs | `AC-1`: default and managed create payload match | `test_mpa_provision_env.py`, `mpa_managed/test_service.py`, CLI regressions | pass: 460 focused tests | -| `FR-2` | `T-2`: regress explicit override | `AC-2`: explicit false wins, other settings unchanged | Existing helper/managed override tests | pass: included in focused tests | -| `FR-3` | `T-3`: verify scope and gates | `AC-3`: no cloud writes, mode unset, source retained | Diff review, focused tests, full regression, Ruff/Pyright/pre-commit | Scope pass; gate limitations below | - -Use `uv run --extra dev pytest` for affected tests, plus coverage for -`veadk.integrations.mpa.mpa_provision` and changed executable lines (required -incremental coverage above 95%). Full regression: -`uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`. -Run Ruff and Pyright on changed Python files, and -`uv run --extra dev pre-commit run --all-files`. No provider credentials are -required. Live cloud/download E2E is `not_run`: outside this code-only request. - -## Review and delivery - -2026-09-30: direct design review (`review-spec` unavailable) checked shared -callers, override precedence, security/discovery changes, testability, scope, -and bilingual equivalence. No design blockers. Approval is the user's explicit -request to restore `true` in the existing PR. Implementation is complete; -repository-wide gate limitations keep the design status `approved`. - -Verification on 2026-09-30, changes on `42120304`, synchronized with -`superops/main` (`6fba185d`): - -- Regression-first: default assertion failed before the fix (`false != true`). -- Focused command: `uv run --extra dev python -c 'import pydantic_settings; import pytest; raise SystemExit(pytest.main(["tests/integrations/test_mpa_provision_env.py", "tests/integrations/mpa_managed", "tests/cli/test_cli_mpa.py", "--cov=veadk.integrations.mpa.mpa_provision", "--cov-branch", "--cov-report=term-missing", "-q"]))'`: - **460 passed**. The preload avoids the existing focused collection - import-order problem without changing dependencies. -- Coverage: the constant has no independent executable coverage line. Its - enclosing dictionary assignment (line 162) is covered **1/1 (100%)**, with - no new branch. Module combined statement/branch coverage: **96.77%**; - statement coverage: **98.98%**. -- Full parallel regression using the command above: **6152 passed, 7 failed, - 2 collection errors, 22 skipped, 2 xfailed**. Six Harness failures lack - optional `llama_index`; two self-hosted collection errors lack `anthropic`; - the unchanged wheel test expects SDK `>=0.8.0` instead of declared `>=0.8.5`. - These failures are outside this change; full regression is not a pass. -- Ruff: pass via `uv tool run --from ruff==0.11.12 ruff check` on all three - changed Python files. Direct `uv run ruff` / `uv run pyright` cannot find - those tools in the project environment, so isolated runners were used. -- `uv tool run --from pyright pyright --pythonpath .venv/bin/python` on all - three changed Python files reports two diagnostics in unchanged - `test_service.py` lines 78/137 (optional template, dynamic `_credentials`). - The production helper and environment test alone pass with zero diagnostics. -- `uv run --extra dev pre-commit run --all-files`: pass, including Ruff format - and both secret scans. Diff whitespace, bilingual requirement IDs, and - relative Markdown links: pass. -- Frontend build/browser: not_applicable; no frontend code/assets changed. - Contracts, paired managed READMEs, and `frontend/README.md` are synchronized. - Cloud deployment/download E2E: not_run, outside this code-only request. diff --git a/prd-spec/bugfixes/mpa-default-jwt/2026-09-30-studio-key-auth-default.zh.md b/prd-spec/bugfixes/mpa-default-jwt/2026-09-30-studio-key-auth-default.zh.md deleted file mode 100644 index 837dba2d5..000000000 --- a/prd-spec/bugfixes/mpa-default-jwt/2026-09-30-studio-key-auth-default.zh.md +++ /dev/null @@ -1,87 +0,0 @@ -# 恢复 Studio MPA 的 JWT 绕过默认值 - -[English](2026-09-30-studio-key-auth-default.md) - -- Change ID:`mpa-studio-key-auth-default` -- 创建/修订:2026-09-30 -- 状态:approved -- 契约:[MPA Runtime 部署](../../../specs/mpa-runtime-provisioning/README.zh.md) -- 前序:[A2A 发现默认配置](../2026-09-23-mpa-a2a-discovery-default.zh.md);仅替代其中 JWT 默认值的决策。 - -## 背景与范围 - -`build_runtime_env` 当前注入 `DISABLE_JWT_AUTH=false`。Studio 使用 Runtime -API key,而不是旧版 `X-Jwt-Token`,因此下载等受 JWT 保护的 REST 调用可能失败。 -用户明确要求在现有 PR 中将默认值改为 `true`。Managed flat 创建和 -`veadk mpa create` 均使用共享 helper;引用 Runtime/模板使用各自的环境。 - -目标:恢复共享默认值并保留显式覆盖。非目标:云端部署、已有 Runtime/会话迁移、 -镜像构建、前端修改、JWT 签发,或开启 `MPA_AGENTKIT_MODE`。 - -## 需求与设计 - -- `FR-1`:给定没有显式覆盖的全新 flat 输入,生成环境包含 - `DISABLE_JWT_AUTH=true`、`ENABLE_A2A=true`、`A2A_TIP_VERIFY_ENABLED=false`; - 外层网关 key-auth 不变。 -- `FR-2`:给定 `extra_env` 或 `managed.runtime.env` 显式指定 - `DISABLE_JWT_AUTH=false`,覆盖优先,且不修改输入映射。 -- `FR-3`:不自动修改已有 Runtime/模板环境和已部署资源;helper 仍不注入 - `MPA_AGENTKIT_MODE`。 - -只改 `build_runtime_env` 的一个默认值,不在调用方分别修改。复用既有覆盖机制和 -回归测试,不新增配置或依赖。不改变 schema、持久化、并发或重试。 - -安全:绕过旧版 REST 内层 JWT 同时影响管理权限检查和基于 header 的身份,不只 -影响下载。APIG key-auth 仍必需;可信 Studio/网关调用方必须控制用户 header。 -直接获取 Runtime principal 的端点不因该开关而修复。使用兼容的 MPA 镜像时, -`/list-apps` 可能发现 ADK,而不是强制走 A2A;本次不保证传输兼容性,也不迁移旧 -会话。需要旧版 JWT 的操作方应显式设置 `false`。恢复方式为显式覆盖配置并发布, -不是自动回滚。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | 验证 | 结果 | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1`:修改默认值及双语契约/文档 | `AC-1`:默认值与托管创建载荷一致 | `test_mpa_provision_env.py`、`mpa_managed/test_service.py`、CLI 回归 | pass:460 个目标测试 | -| `FR-2` | `T-2`:回归显式覆盖 | `AC-2`:显式 false 优先,其他配置不变 | 既有 helper/managed 覆盖测试 | pass:包含在目标测试中 | -| `FR-3` | `T-3`:核验范围及门禁 | `AC-3`:无云端写操作、mode 未注入、来源保留 | Diff 审查、目标测试、全量回归、Ruff/Pyright/pre-commit | 范围 pass;门禁限制见下文 | - -使用 `uv run --extra dev pytest` 运行受影响测试,并测量 -`veadk.integrations.mpa.mpa_provision` 及修改的可执行行覆盖率(增量要求超过 -95%)。全量回归: -`uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`。 -对修改的 Python 文件运行 Ruff、Pyright,以及 -`uv run --extra dev pre-commit run --all-files`。不需要云端凭据。 -真实云端/下载 E2E 为 `not_run`:不属于本次只改代码的请求。 - -## 审查与交付 - -2026-09-30:直接设计审查(`review-spec` 不可用)检查了共享调用方、覆盖优先级、 -安全/发现变化、可测试性、范围和双语一致性。无设计阻塞项。用户明确要求在现有 -PR 恢复 `true`,作为本次批准。实现已完成;因仓库级门禁限制,设计状态保持 -`approved`。 - -2026-09-30 验证,基于 `42120304` 的修改,已同步 `superops/main`(`6fba185d`): - -- 测试先行:默认值断言在修复前失败(`false != true`)。 -- 目标命令:`uv run --extra dev python -c 'import pydantic_settings; import pytest; raise SystemExit(pytest.main(["tests/integrations/test_mpa_provision_env.py", "tests/integrations/mpa_managed", "tests/cli/test_cli_mpa.py", "--cov=veadk.integrations.mpa.mpa_provision", "--cov-branch", "--cov-report=term-missing", "-q"]))'`: - **460 passed**。预先导入避开既有目标测试收集的导入顺序问题,不改变依赖。 -- 覆盖率:常量没有独立可执行覆盖行。所属字典构造语句(第 162 行)已覆盖 - **1/1(100%)**,无新增分支。模块语句/分支综合覆盖率 **96.77%**, - 语句覆盖率 **98.98%**。 -- 使用前述命令执行全量并行回归:**6152 passed、7 failed、2 个收集错误、 - 22 skipped、2 xfailed**。6 个 Harness 失败缺可选 `llama_index`;两个自托管 - 收集错误缺 `anthropic`;未修改的 wheel 测试预期 SDK `>=0.8.0`,而声明为 - `>=0.8.5`。这些问题不在本次范围内;全量回归不记为通过。 -- Ruff:对三个修改的 Python 文件使用 - `uv tool run --from ruff==0.11.12 ruff check`,pass。直接 `uv run ruff` / - `uv run pyright` 在项目环境中找不到对应工具,改用隔离运行器。 -- 对三个修改的 Python 文件运行 - `uv tool run --from pyright pyright --pythonpath .venv/bin/python`,报告未修改的 - `test_service.py` 第 78/137 行两处诊断(可选模板、动态 `_credentials`)。 - 单独检查生产 helper 与环境测试,零诊断通过。 -- `uv run --extra dev pre-commit run --all-files`:pass,包括 Ruff 格式化和两种 - 密钥扫描。Diff 空白、双语需求 ID 和 Markdown 相对链接:pass。 -- 前端构建/浏览器:not_applicable;未修改前端代码/产物。已同步契约、双语 - managed README 和 `frontend/README.md`。云端部署/下载 E2E:not_run, - 不属于本次只改代码的请求。 diff --git a/prd-spec/bugfixes/mpa-delete-timeout/2026-10-13-confirm-delete-outcome.md b/prd-spec/bugfixes/mpa-delete-timeout/2026-10-13-confirm-delete-outcome.md deleted file mode 100644 index 091642558..000000000 --- a/prd-spec/bugfixes/mpa-delete-timeout/2026-10-13-confirm-delete-outcome.md +++ /dev/null @@ -1,61 +0,0 @@ -# Confirm MPA deletion after a client timeout - -[中文版](2026-10-13-confirm-delete-outcome.zh.md) - -- Change ID: `mpa-delete-timeout` -- Created/revised: 2026-10-13 -- Status: implemented -- Contract: [Studio MPA control plane](../../../specs/studio-mpa-control-plane/README.md), CON-30a - -## Evidence, goals and scope - -MPA deletion uses `deleteRuntime` with the ordinary 30-second `apiFetch` deadline. The server rechecks sessions and resource references and deletes exclusive templates/Skill Spaces before Runtime. A browser timeout does not cancel these backend side effects. A reported timeout was followed by confirmed Runtime NotFound and absence of its Skill Space; the template was not independently queried. The exact stage/duration was not recovered from logs. - -FR-1: Give only explicitly selected MPA deletion a 180-second request deadline. General-agent deletion retains its 30-second deadline and behavior. -FR-2: After a TimeoutError, issue one read-only, uncached 30-second confirmation of the same Runtime ID/region through an Agent-management-authorized BFF. Report success only for explicit cloud Runtime NotFound plus complete ListRuntimes absence. Do not equate an authorization 404, access denial, transport error, malformed response, repeated token or pagination budget exhaustion with deletion. A Runtime visible only through ListRuntimes is still present. -FR-3: If confirmation returns present or cannot confirm, keep a localized uncertain-result message and the existing error/retry UI. Never automatically repeat DELETE. Non-timeout deletion failures preserve their current error. - -No creation changes, shared timeout changes, new async-job system, deletion-order changes, Profile query, resource purchase, migration, image changes or general-agent behavior changes are included. Runtime absence confirms the selected Runtime outcome, not independently inspected resource cleanup by an external actor. - -## Design and affected files - -Add a small read-only backend helper and endpoint `/web/mpa-runtime-deletion-status?runtimeId=...®ion=...` returning `{deleted: boolean}` with `Cache-Control: no-store`. Require current Agent management rights. Existing authorization verifies a present Runtime; authorization-generated HTTP errors never reach the missing-resource branch. If GetRuntime reports a structured Runtime-specific NotFound, scan all pages under the same deployment credentials, provider and region, with bounded pagination. Do not return resource/environment data or raw exceptions. Endpoint timeouts and unconfirmed errors return a safe explicit error. Cloud GetRuntime hiding a still-listed Runtime must not produce success. - -The client uses the existing requestSignal/apiFetch and existing confirmation UI. MPA-only catch handles TimeoutError, verifies the boolean result and resolves the original delete promise only on true. False/failed queries produce localized guidance to refresh/check later. Tests use fake deadlines and cloud responses, not live deletion. - -Files: `frontend/src/adk/client.ts`, paired ADK locale resources, `frontend/server/mpa_deletion_status.py`, route mount in `veadk/cli/cli_frontend.py`, frontend executable client tests, backend route/ownership/pagination tests; paired PRD/spec, frontend README and generated WebUI assets. No layout/CSS/icon changes; existing confirmation loading/error/retry/keyboard behavior is the visual baseline. - -## Tasks and acceptance - -- T-1 / FR-1 / AC-1: regression first for MPA 180 seconds versus general 30 seconds; general requests never call confirmation. -- T-2 / FR-2 / AC-2: implement authorized bounded status, cover structured/wrapped NotFound, paginated absence/presence, query failure, invalid pagination, cancellation and no mutation. -- T-3 / FR-3 / AC-3: verify confirmed completion, uncertain/present outcomes, malformed payload, permission and non-timeout failures; complete existing deletion/UI regression, build and isolated browser checks. - -## Review, authorization, risks and verification - -The user approved the proposed MPA-only timeout/confirmation fix with “改下” on 2026-10-13. `review-spec` is unavailable; direct review confirmed bilingual equivalence, no general-agent or cleanup-order change, bounded reads and fail-closed errors. No review blocker remains. Security review adds a bounded private confirmation context: remember only server-authorized MPA delete attempts, keyed by Studio owner/Runtime/region, with a hash of the current deployment credentials. At most 1024 contexts live for one hour; neither credentials nor their hash reach clients. Missing context, server restart or credential rotation produces an unconfirmed result, never success. Recheck credentials after cloud reads to detect mid-query account changes. This is not a persistent deletion job or new mutation protocol. No real cloud deletion, commit, push, deployment or Studio restart is authorized. - -Cloud listing is eventually consistent and cannot guarantee transaction-like cross-service cleanup. The read-only confirmation cannot infer details of resources deleted by external operators. Account changes/authorization failure cannot silently produce success. Known old-file static errors must be baseline-compared rather than hidden. Actual results are recorded below. Required gates: affected Python tests, Ruff/Pyright changed files, `npm --prefix frontend test`, build/i18n/assets, isolated browser and configured secret scan. No harness/process contract is affected. - -## Delivery review and verification (2026-10-13) - -T-1–T-3 / AC-1–AC-3 are implemented. Review confirmed MPA-only client gating, unchanged generic POST/deadline, no delete replay, structured-only absence checks, complete bounded pagination, credential/owner scoping and retained partial-cleanup behavior. Standard-library timeout is used on Python >=3.11 and the existing async-timeout dependency on older supported Python; no dependency was added. No creation, Profile, Runtime image, schema or deletion-order change was made. The new server module is included by the existing `frontend*` package discovery. - -Tested scope: this deletion fix on `71175c68636dc5b951e9804eb292d85402511e68`, alongside pre-existing local PG/name/OV changes which were preserved. Initial executable frontend regressions failed before the fix; backend tests initially failed collection because the new status module did not exist. Fake cloud operations and isolated browser responses are separate from live verification. - -| Check | Result | Evidence / limitations | -| --- | --- | --- | -| Python deletion/role regression | pass | `uv run --extra dev pytest tests/frontend/server/test_mpa_deletion_status.py tests/integrations/mpa_managed/test_deletion.py tests/cli/test_studio_rbac.py -q --no-cov`: 210 passed before final additional negative cases. | -| Final confirmation unit/route tests | pass | `uv run --extra dev pytest tests/frontend/server/test_mpa_deletion_status.py -q --no-cov`: 25 passed, including real Studio route mounting with simulated cloud deletion, actual-category general isolation, timeout, expiry/count bounds and wrapped provider errors. | -| Frontend client regressions | pass | `node --test frontend/tests/mpaDeletionTimeout.test.mjs`: 10 passed, including confirmation transport timeout and preserving non-timeout server diagnostics. | -| Frontend full regression | pass | `npm --prefix frontend test`: 1389 Node +84 React passed, including all 10 new client regression cases. | -| Build/i18n/assets | pass | `npm --prefix frontend run build`, `npm --prefix frontend run check:i18n`, `npm --prefix frontend run test:webui-assets`: synchronized release assets, 2 locales /21 namespaces, 113 files /350 references. Existing chunk-size warnings remain. | -| Browser | pass | Bundled Playwright with isolated headless Chrome and actual AgentWorkspace/client: normal success, timeout confirmed, still present, query failure, busy-disabled state, keyboard Enter/Escape, cancel/reopen, general deletion and 390×844 view. Mock fetch blocked real cloud calls; temporary server/fixtures removed. | -| Ruff / new-module Pyright | pass | Temporary `uvx` tools; Ruff on all 3 changed Python files and Pyright with `.venv/bin/python` on new module/test. No machine-global installation. | -| Legacy CLI Pyright | fail | 37 existing errors; comparison against HEAD has exactly the same diagnostic messages/rules, with no new diagnostics from the fix. Unrelated historical errors were not changed. | -| Documentation / whitespace / secrets | pass | Paired-language/identifier/link review, `git diff --check`, repository-configured redacted Gitleaks on changed sources/assets. | -| Live destructive E2E | not_run | No new deletion authorized or executed; earlier read-only diagnosis is not a live test of this fix. | -| Broad SDK/process/harness tests | not_applicable | No SDK, process or harness contracts changed; focused deletion, RBAC and frontend regression cover the changed path. | -| Commit/pre-commit | not_run | No commit authorized; branch synchronization and full pre-commit remain required before any later commit. | - -Restart Studio in the original credential-bearing terminal and refresh the page to load the backend and matching assets. No server restart, commit, push or deployment was performed. Remaining limits: contexts are process-local and expire; restart, a different worker process or credential rotation leaves the outcome unconfirmed. Runtime absence does not independently prove externally triggered related-resource cleanup. No new review blockers remain in the scoped fix. diff --git a/prd-spec/bugfixes/mpa-delete-timeout/2026-10-13-confirm-delete-outcome.zh.md b/prd-spec/bugfixes/mpa-delete-timeout/2026-10-13-confirm-delete-outcome.zh.md deleted file mode 100644 index 212240f22..000000000 --- a/prd-spec/bugfixes/mpa-delete-timeout/2026-10-13-confirm-delete-outcome.zh.md +++ /dev/null @@ -1,61 +0,0 @@ -# MPA 客户端超时后确认删除结果 - -[English](2026-10-13-confirm-delete-outcome.md) - -- Change ID:`mpa-delete-timeout` -- 创建/修订:2026-10-13 -- 状态:implemented -- 契约:[Studio MPA 控制面](../../../specs/studio-mpa-control-plane/README.zh.md),CON-30a - -## 证据、目标与范围 - -MPA 删除使用 `deleteRuntime`,沿用普通 `apiFetch` 的 30 秒超时。服务端复核会话和资源引用,先删除独占模板/Skill Space,再删 Runtime。浏览器超时不会取消这些后台副作用。一次报超时后,已确认 Runtime 返回 NotFound、Skill Space 不再存在;未独立查询模板。没有从日志恢复准确耗时/阶段。 - -FR-1:仅明确选择的 MPA 删除使用 180 秒请求超时。通用智能体删除保留 30 秒及既有行为。 -FR-2:TimeoutError 后,对同一 Runtime ID/地域执行一次只读、不缓存、限时 30 秒的确认,经过智能体管理授权 BFF。仅云端明确 Runtime NotFound 且完整 ListRuntimes 不存在时报告成功。授权产生的 404、权限拒绝、网络错误、格式错误、重复 token 或超出分页预算都不能视作删除成功。仅在 ListRuntimes 可见的 Runtime 仍然存在。 -FR-3:若确认仍存在或无法确认,保留本地化结果待确认提示及既有错误/重试 UI。不自动重复 DELETE。非超时删除失败保留原错误。 - -不修改创建、公共超时、异步任务体系、删除顺序、Profile 查询或通用智能体行为;不购买资源、迁移或修改镜像。Runtime 不存在只能确认目标 Runtime 结果,不等于独立核验外部操作导致的其他资源清理。 - -## 设计与受影响文件 - -增加小型只读后端模块和 `/web/mpa-runtime-deletion-status?runtimeId=...®ion=...`,返回 `{deleted: boolean}`,设置 `Cache-Control: no-store`。要求当前智能体管理权限。存在的 Runtime 使用既有授权核验;授权产生的 HTTP 错误不会进入资源缺失分支。若 GetRuntime 返回结构化 Runtime 特定 NotFound,则用同一部署凭据、提供商及地域完整扫描分页,设置分页上限。不返回资源/环境数据或原始异常。接口超时和无法确认均返回安全明确错误。云端 GetRuntime 隐藏但仍在列表的 Runtime 不能误判成功。 - -客户端复用 requestSignal/apiFetch 与既有确认 UI。仅 MPA 的 catch 处理 TimeoutError,核验布尔结果,仅 true 时完成原删除 promise。false/查询失败用本地化文案提示稍后刷新检查。测试使用模拟超时和云响应,不实际删除。 - -文件:`frontend/src/adk/client.ts`、双语 ADK 文案、`frontend/server/mpa_deletion_status.py`、`veadk/cli/cli_frontend.py` 路由挂载、前端可执行客户端测试、后端路由/归属/分页测试;双语 PRD/spec、frontend README 与 WebUI 构建资源。不改布局/CSS/图标;视觉基准为现有确认弹窗的加载/错误/重试/键盘行为。 - -## 任务与验收 - -- T-1 / FR-1 / AC-1:先写 MPA 180 秒、通用 30 秒回归;通用请求不调用结果确认。 -- T-2 / FR-2 / AC-2:实现授权、有界状态查询,覆盖结构化/包装 NotFound、分页缺失/存在、查询失败、错误分页、取消和无写操作。 -- T-3 / FR-3 / AC-3:核验确认完成、待确认/仍存在、错误 payload、权限和非超时失败;完成已有删除/UI 回归、构建与隔离浏览器检查。 - -## 审查、授权、风险与验证 - -用户于 2026-10-13 通过“改下”批准 MPA 专属超时/结果确认方案。`review-spec` 不可用,直接审查确认双语等价、通用智能体与清理顺序不变、有界读取及失败关闭行为,无审查阻塞。安全审查补充有界私有确认上下文:仅登记服务端已授权的 MPA 删除尝试,按 Studio 用户/Runtime/地域隔离,保存当前部署凭据的哈希。最多 1024 条,保留一小时;凭据及哈希均不返回客户端。上下文缺失、服务重启或凭据轮换只能返回待确认,不能成功。云查询后复核凭据,发现查询中途账号变化。这不是持久删除任务或新的写协议。未授权真实云删除、提交、推送、部署或重启 Studio。 - -云列表具有最终一致性,无法保证跨服务事务式清理。只读确认不能推断外部操作者删除资源的细节。账号改变/授权失败不能静默报告成功。旧文件静态错误需对照基线,不能隐藏。实际验证结果记录如下。必需检查:受影响 Python 测试、改动文件 Ruff/Pyright、`npm --prefix frontend test`、build/国际化/资源引用、隔离浏览器及仓库配置密钥扫描。不影响 harness/进程契约。 - -## 交付审查与验证(2026-10-13) - -T-1–T-3 / AC-1–AC-3 已实现。审查确认客户端仅 MPA 启用,通用 POST/超时不变、不重复删除、仅结构化缺失确认、完整有界分页、凭据/用户隔离及部分清理语义保留。Python >=3.11 使用标准库超时,较早支持版本使用现有 async-timeout 依赖,没有新增依赖。未修改创建、Profile、Runtime 镜像、schema 或删除顺序。现有 `frontend*` 包发现规则会包含新服务端模块。 - -验证范围:基于 `71175c68636dc5b951e9804eb292d85402511e68` 的本次删除修复,保留工作区已有 PG/名称/OV 改动。修复前前端可执行回归失败;后端测试最初因状态模块尚不存在而收集失败。模拟云操作及隔离浏览器响应与真实验证明确分开。 - -| 检查 | 结果 | 证据 / 限制 | -| --- | --- | --- | -| Python 删除/角色回归 | pass | `uv run --extra dev pytest tests/frontend/server/test_mpa_deletion_status.py tests/integrations/mpa_managed/test_deletion.py tests/cli/test_studio_rbac.py -q --no-cov`:最终追加负例前 210 项通过。 | -| 最终确认单元/路由测试 | pass | `uv run --extra dev pytest tests/frontend/server/test_mpa_deletion_status.py -q --no-cov`:25 项通过,含真实 Studio 路由挂载下模拟云删除、服务端真实类别的通用隔离、超时、过期/数量上限与包装提供商错误。 | -| 前端客户端回归 | pass | `node --test frontend/tests/mpaDeletionTimeout.test.mjs`:10 项通过,含确认查询网络超时及保留非超时服务端诊断。 | -| 前端完整回归 | pass | `npm --prefix frontend test`:1389 项 Node +84 项 React 通过,包含全部 10 项新增客户端回归。 | -| 构建/国际化/资源 | pass | `npm --prefix frontend run build`、`npm --prefix frontend run check:i18n`、`npm --prefix frontend run test:webui-assets`:同步发布资源,2 个语言 /21 个命名空间,113 个文件 /350 个引用。仍有既有大 chunk 警告。 | -| 浏览器 | pass | 内置 Playwright、隔离无头 Chrome 及真实 AgentWorkspace/client:正常成功、超时确认、仍存在、查询失败、忙碌禁用、Enter/Escape、取消重开、通用删除及 390×844 视图。模拟 fetch 阻止真实云调用;已清理临时服务/夹具。 | -| Ruff / 新模块 Pyright | pass | 临时 `uvx` 工具;Ruff 覆盖 3 个改动 Python 文件,新模块/测试以 `.venv/bin/python` 跑 Pyright。无全局安装。 | -| 旧 CLI Pyright | fail | 37 条已有错误;与 HEAD 对比诊断消息/规则完全一致,本修复无新增诊断。未修改无关历史错误。 | -| 文档 / 空白 / 密钥 | pass | 双语/标识符/链接核对、`git diff --check`,对改动源码/资源使用仓库配置及脱敏输出 Gitleaks。 | -| 真实破坏性 E2E | not_run | 未授权或执行新删除;此前只读诊断不是本修复的真实验证。 | -| 广泛 SDK/进程/harness 测试 | not_applicable | 未修改 SDK、进程或 harness 契约;定向删除、RBAC 与前端回归覆盖改动路径。 | -| 提交/pre-commit | not_run | 未授权提交;后续提交前仍需同步分支并执行全量 pre-commit。 | - -请在保有原凭据的终端重启 Studio 并刷新页面,加载后端及配套资源。本次未重启服务、提交、推送或部署。剩余限制:确认上下文按进程保存并会到期;重启、切换服务进程或轮换凭据会导致结果无法确认。Runtime 缺失不能独立证明由外部操作触发的相关资源清理。本次范围内无新增审查阻塞。 diff --git a/prd-spec/bugfixes/mpa-iam-retry/2026-10-14-transient-errors.md b/prd-spec/bugfixes/mpa-iam-retry/2026-10-14-transient-errors.md deleted file mode 100644 index f12d54da4..000000000 --- a/prd-spec/bugfixes/mpa-iam-retry/2026-10-14-transient-errors.md +++ /dev/null @@ -1,45 +0,0 @@ -# IAM transient failure recovery and diagnostics - -[中文版](2026-10-14-transient-errors.zh.md) - -Change ID: mpa-iam-retry. Date: 2026-10-14. Status: implemented; live mutation not performed. -Contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), CON-10 and CON-11. - -## Evidence, goals and scope - -IAM SDK errors are converted into IamError, preserving recognized codes only. Transport timeout/connection errors lose their category and immediately fail role preparation. A real overseas task failed with iamVerificationFailed while later read-only verification of the same role and credentials passed; its original error was not recorded, so the historical cause is unconfirmed. The user explicitly approved retaining safe operation/category/Request ID, bounded transient retry and distinct UI messages. The subsequent Worker CreateTool failure is a separate read-only investigation, not a reason to expand this fix. - -## Requirements and scenarios - -- FR-1: Preserve allowlisted IAM operation/category/code and a validated Request ID from structured SDK responses/errors. Never persist exception text, credentials, role/policy documents or arbitrary response data. -- FR-2: Reconcile timeout, connection, throttling and unavailable failures with at most four transient attempts and 1/2/4-second waits, inside the existing 120-second role deadline and task deadline. Permission, configuration/conflict, validation and unknown errors fail immediately. Permission is not retried. -- FR-3: Each retry restarts read-before-write reconciliation using the same fixed role/policy names. A lost create/attach response requires readback on the next cycle, preserving existing additive and collision handling. No parallel operation retries, rollback or permission expansion. Cancellation remains terminal; a SDK request already dispatched may finish, but no later reconciliation call is dispatched after cancellation. -- FR-4: Emit iamTemporaryFailure for exhausted transient requests/deadline; existing permanent error keys remain. Store operation/category/attempt/outcome and optional Request ID in bounded task diagnostics (100/task). Migrate the existing SQLite table additively; legacy four-field diagnostics remain valid. -- FR-5: Localized UI distinguishes temporary IAM/network failures from deployment permission failures. General agents, role permission sets, provider endpoints, OV and Worker behavior remain unchanged. - -## Design and tasks - -T-1: Add structured metadata to IamError; IamCloud extracts only safe fields using existing error classification. Capture HTTP status category when available and never parse unstructured text into user-visible messages. -T-2: Add the bounded recovery counter to ensure_runtime_role. Preserve get-or-create and Global policy readback; report safe diagnostics before retry/failure. -T-3: Extend diagnostics validator/report with optional requestId, allowlist IAM operations; tasks.py adds a request_id column with empty default, persists it and logs only validated metadata. No HTTP task schema change except the additional error enum key. -T-4: Add regression tests before code for transient recovery/exhaustion, permanent failure, unknown errors, cancellation/late response, lost create response, Request ID redaction and SQLite migration; update locale copy/docs/assets, run affected Python, Ruff/Pyright, frontend/build/browser and secret checks. - -## Review, risks and acceptance - -User approval: explicit '改下' accepting the three changes on 2026-10-14. Direct design review (review-spec unavailable): safe fields, finite retry/deadline, idempotent resource readback, cancellation and legacy diagnostic compatibility reviewed; bilingual pair matches. No blocker. - -- AC-1: Timeout/connection/429/5xx recover; four failures terminate with iamTemporaryFailure and recorded operation/category. Missing IAM permission remains iamPermissionDenied without retry. -- AC-2: Read-before-write prevents extra role/policy creation after a lost response; existing trust/policy documents and unrelated bindings are preserved; cancellation prevents subsequent SDK calls. -- AC-3: Structured Request ID survives persisted diagnostics, malicious/non-ID strings are omitted, old records migrate without losing data; raw secrets never appear in errors/log records. -- AC-4: UI strings, tests/build/assets and bilingual contracts match; record actual checks below. - -The original real failure cannot be proven retroactively. Real IAM writes and paid cloud creation are not authorized for this implementation; all tests use isolated fakes. Full verification pending. Commit/push are outside scope. Existing user changes stay intact. - - -## Verification (2026-10-14) - -Scope: uncommitted IAM changes on base `2ffe6e14`, preserving other local work. The first new regression run reproduced 13 failures. After implementation, IAM/tasks/Worker recovery targeted checks passed (68). The complete affected suite `uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/cli/test_studio_startup_imports.py` passed 1,017 tests with four warnings. Subsequent HTTP-status extraction and thread-cancellation hardening passed all 18 IAM retry tests. Both requests with transport failures and structured 429/5xx/403 errors are covered. - -pass: Ruff 0.11.12 check/format and Pyright on iam.py, diagnostics.py, tasks.py and the two changed IAM tests (0 errors/warnings). pass: frontend test (1,389 Node + 97 React), production build, i18n (2 locales/21 namespaces), assets (113 files/350 references), paired design identifiers/links and whitespace. Browser Chromium loaded the actual dialog with isolated mocked APIs, checked explicit IAM temporary failure, same-request retry, personal/default UI, existing manual recovery, domestic display, Escape and narrow viewport. Temporary fixture/server removed after verification; no real resource creation. User lockfile hash is unchanged. - -not_run: full repository Python/all-files pre-commit because no commit requested and the affected MPA suite plus frontend gates cover this scope. not_applicable: Harness/Codex smoke and model/OV live integration because these contracts are unchanged. not_run: real IAM mutations, Worker CreateTool retry or cloud creation. The existing Worker failure remains provider_error with no original code/Request ID; read-only search confirms no same-name template, which does not identify the rejection reason. diff --git a/prd-spec/bugfixes/mpa-iam-retry/2026-10-14-transient-errors.zh.md b/prd-spec/bugfixes/mpa-iam-retry/2026-10-14-transient-errors.zh.md deleted file mode 100644 index 0c9ce9129..000000000 --- a/prd-spec/bugfixes/mpa-iam-retry/2026-10-14-transient-errors.zh.md +++ /dev/null @@ -1,45 +0,0 @@ -# IAM 临时失败恢复与诊断 - -[English](2026-10-14-transient-errors.md) - -变更 ID:mpa-iam-retry。日期:2026-10-14。状态:implemented;未执行真实云端变更。 -契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-10 和 CON-11。 - -## 证据、目标与范围 - -IAM SDK 错误转换为 IamError 时仅保留已知错误码。传输超时/连接错误丢失类别,角色准备立即失败。真实海外任务返回 iamVerificationFailed,而相同角色和凭据后续只读核对通过;原始错误未记录,历史原因不能确定。用户明确批准保留安全的接口/类别/Request ID、有限临时错误重试及区分界面提示。之后的 Worker CreateTool 失败是独立只读调查,不扩大本修复范围。 - -## 要求与场景 - -- FR-1:保留 IAM 白名单接口/类别/错误码及从结构化 SDK 响应/错误中取得、经校验的 Request ID。不保存异常文字、凭据、角色/策略文档或任意响应内容。 -- FR-2:对超时、连接失败、限流和服务不可用执行最多四次临时错误尝试,等待 1/2/4 秒,受已有 120 秒角色期限及任务期限限制。权限、配置/冲突、参数及未知错误立即失败;缺权限不重试。 -- FR-3:每次重试使用相同固定角色/策略名称,从先读后写的协调开始。创建/绑定响应丢失后,下一轮必须先回读,保留已有增量绑定及冲突处理。不并行重试操作,不回滚或扩大权限。取消保持终态;已发送的 SDK 请求可能完成,但取消后不再发送后续协调调用。 -- FR-4:临时请求耗尽或截止期限时返回 iamTemporaryFailure,保留原永久错误 key。有限任务诊断(每任务 100 条)保存接口/类别/次数/结果及可选 Request ID。增量迁移 SQLite 表,兼容旧四字段诊断。 -- FR-5:界面区分临时 IAM/网络失败与部署权限不足。通用智能体、角色权限集合、服务端点、OV 和 Worker 行为不变。 - -## 设计与任务 - -T-1:为 IamError 增加结构化元数据;IamCloud 使用已有分类,仅提取安全字段。有 HTTP 状态时保留类别,不从非结构化文字产生可见消息。 -T-2:ensure_runtime_role 增加有限恢复计数,保留 get-or-create 和 Global 策略回读;重试/失败前记录安全诊断。 -T-3:diagnostics 校验器/report 增加可选 requestId、IAM 接口白名单;tasks.py 增加默认空值的 request_id 列,仅持久化/输出经过校验的元数据。HTTP 任务结构仅增加错误枚举 key。 -T-4:先写回归覆盖临时恢复/耗尽、永久/未知错误、取消/晚返回、创建响应丢失、Request ID 脱敏和 SQLite 迁移;更新双语界面/文档/产物,执行受影响 Python、Ruff/Pyright、前端/构建/浏览器及密钥检查。 - -## 评审、风险与验收 - -用户批准:2026-10-14 明确回复“改下”,接受这三项修改。直接设计评审(review-spec 不可用):已审查安全字段、有限重试/期限、资源回读幂等、取消及旧诊断兼容;双语对应一致,无阻塞。 - -- AC-1:超时/连接/429/5xx 可恢复;四次失败以 iamTemporaryFailure 结束并记录接口/类别。缺少 IAM 权限仍立即返回 iamPermissionDenied,不重试。 -- AC-2:创建响应丢失后先读后写,避免重复创建角色/策略;保留已有信任/策略文档及其他绑定;取消后不调用后续 SDK 接口。 -- AC-3:结构化 Request ID 保留在诊断中,恶意/非 ID 文字忽略;旧记录迁移不丢失,原始密钥不进入错误/日志。 -- AC-4:界面文案、测试/构建/产物及双语契约一致;下方记录实际检查。 - -无法追溯证明原真实失败原因。本实现未授权真实 IAM 写入或付费云创建;测试均使用隔离模拟。完整验证待执行。提交/推送不在范围内。保留已有用户修改。 - - -## 验证(2026-10-14) - -范围:基于 `2ffe6e14` 的未提交 IAM 改动,保留其他本地工作。新增回归首次运行复现 13 项失败。实施后 IAM/tasks/Worker 恢复定向检查 68 项通过。完整受影响套件 `uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/cli/test_studio_startup_imports.py` 通过 1,017 项、4 项警告。之后 HTTP 状态提取及线程取消补强,全部 18 项 IAM 重试测试通过。覆盖传输异常及结构化 429/5xx/403 错误。 - -pass:iam.py、diagnostics.py、tasks.py 及两个修改的 IAM 测试的 Ruff 0.11.12 检查/格式与 Pyright(0 错误/警告)。pass:前端测试(1,389 项 Node + 97 项 React)、生产构建、国际化(2 个语言/21 个命名空间)、产物(113 文件/350 引用)、双语设计标识/链接及空白检查。真实 Chromium 加载实际弹窗,API 隔离模拟,检查明确 IAM 临时失败、同请求重试、个人版/default 界面、旧手动恢复、国内展示、Escape 和窄视口。验证后移除临时夹具及服务,未创建真实资源。用户 lockfile 哈希未变化。 - -not_run:全仓库 Python/all-files pre-commit,因为未要求提交,受影响 MPA 套件及前端门禁覆盖本范围。not_applicable:Harness/Codex smoke 和模型/OV 真实联调,因为契约未改变。not_run:真实 IAM 变更、Worker CreateTool 重试或云端创建。既有 Worker 失败仍为 provider_error,缺少原始错误码/Request ID;只读搜索确认没有同名模板,这不能确定被拒绝的具体原因。 diff --git a/prd-spec/bugfixes/mpa-iam-timeout-compat/2026-10-09-python310-timeout.md b/prd-spec/bugfixes/mpa-iam-timeout-compat/2026-10-09-python310-timeout.md deleted file mode 100644 index 8837a6b31..000000000 --- a/prd-spec/bugfixes/mpa-iam-timeout-compat/2026-10-09-python310-timeout.md +++ /dev/null @@ -1,82 +0,0 @@ -# Preserve IAM Timeout Semantics on Python 3.10 - -[中文版](2026-10-09-python310-timeout.zh.md) - -## Metadata - -- Change ID: `mpa-iam-timeout-compat` -- Created: 2026-10-09 -- Revised: 2026-10-09 -- Status: implemented -- Related component specs: none; the existing IAM failure contract is preserved - -## Background and Evidence - -PR #23 fails the Python 3.10 unit-test job in -`test_readback_timeout_and_cancellation_keep_resources`. The managed MPA IAM -reconciler wraps `asyncio.wait_for()` with `except TimeoutError`. On Python -3.10, `asyncio.wait_for()` raises `asyncio.TimeoutError`, which is not caught by -the built-in `TimeoutError` handler. Python 3.11 and newer alias these exception -types, which hid the compatibility defect in newer local environments. - -The CI result was 6,497 passed and one failed. The uncaught timeout escapes -instead of being translated to the existing `IamError` with -`iamVerificationFailed`. - -## Goals and Non-goals - -### Goals - -- Preserve the existing IAM verification failure behavior on Python 3.10. -- Keep cancellation propagation and resource retention unchanged. -- Make the existing regression test pass on Python 3.10 and current Python. - -### Non-goals - -- Change IAM resources, permissions, retry timing, or reconciliation order. -- Change the public error contract or add a new fallback. -- Modify unrelated MPA behavior. - -## Requirements and Design - -- `FR-1`: A timeout from `asyncio.wait_for()` must be translated to the existing - `IamError`. -- `FR-2`: Caller cancellation must continue to propagate as - `asyncio.CancelledError`. -- `FR-3`: The fix must work on Python 3.10 and Python 3.12 without changing - functional IAM logic. - -Use `except asyncio.TimeoutError` at the existing translation boundary. This is -the exception raised by `asyncio.wait_for()` on Python 3.10 and remains -compatible with newer Python versions. - -Component contract impact: none. The implementation is corrected to satisfy the -already-tested timeout and cancellation contract. No interfaces, configuration, -permissions, persistence, security boundaries, or user documentation change. - -## Tasks and Acceptance - -| Requirement | Task | Acceptance | Verification | Result | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1`: reproduce before the fix | Python 3.10 exposes the uncaught timeout | Python 3.10 targeted pytest | pass: reproduced the uncaught `asyncio.TimeoutError` | -| `FR-1`, `FR-3` | `T-2`: use `asyncio.TimeoutError` | Existing timeout translation passes | Python 3.10 and 3.12 targeted pytest | pass: 13 tests on each version | -| `FR-2` | `T-2` | Existing cancellation assertion passes | Same targeted pytest | pass | -| `FR-3` | `T-3`: static verification | Changed Python file is clean | Ruff and Pyright | pass | - -## Risks and Open Questions - -- Risk is limited to the exception selector at the existing timeout boundary. -- No open questions remain. - -## Review and Delivery Record - -- 2026-10-09: CI traceback confirmed the Python 3.10 exception mismatch. -- 2026-10-09: Design review found no contract, security, or compatibility - blocker. -- 2026-10-09: The user approved a compatibility-only fix and explicitly - requested no functional behavior change. -- 2026-10-09: Python 3.10 reproduced the failure before the fix. After the - one-line exception qualification, the IAM tests passed on Python 3.10.20 and - Python 3.12.13 (13 tests each), the Python 3.12 managed MPA suite passed (664 - tests), and Ruff 0.11.12 and Pyright passed. -- Commit and push require separate authorization. diff --git a/prd-spec/bugfixes/mpa-iam-timeout-compat/2026-10-09-python310-timeout.zh.md b/prd-spec/bugfixes/mpa-iam-timeout-compat/2026-10-09-python310-timeout.zh.md deleted file mode 100644 index ea72e17fe..000000000 --- a/prd-spec/bugfixes/mpa-iam-timeout-compat/2026-10-09-python310-timeout.zh.md +++ /dev/null @@ -1,73 +0,0 @@ -# 在 Python 3.10 保持 IAM 超时语义 - -[English](2026-10-09-python310-timeout.md) - -## 元数据 - -- 变更 ID:`mpa-iam-timeout-compat` -- 创建日期:2026-10-09 -- 修订日期:2026-10-09 -- 状态:implemented -- 相关组件规格:无;保持现有 IAM 失败契约 - -## 背景与证据 - -PR #23 的 Python 3.10 单元测试任务在 -`test_readback_timeout_and_cancellation_keep_resources` 失败。托管 MPA IAM -协调器使用 `except TimeoutError` 包裹 `asyncio.wait_for()`。在 Python 3.10 -中,`asyncio.wait_for()` 抛出 `asyncio.TimeoutError`,不会被内置 -`TimeoutError` 处理器捕获。Python 3.11 及更高版本将这两个异常类型设为别名, -因此较新的本地环境掩盖了这个兼容性缺陷。 - -CI 结果为 6,497 项通过、1 项失败。未捕获的超时直接逸出,而没有转换为现有的 -`IamError` 和 `iamVerificationFailed`。 - -## 目标与非目标 - -### 目标 - -- 在 Python 3.10 保持现有 IAM 校验失败行为。 -- 保持取消传播和资源保留行为不变。 -- 使现有回归测试在 Python 3.10 和当前 Python 上通过。 - -### 非目标 - -- 修改 IAM 资源、权限、重试时间或协调顺序。 -- 修改公开错误契约或增加新回退行为。 -- 修改无关 MPA 行为。 - -## 需求与设计 - -- `FR-1`:`asyncio.wait_for()` 的超时必须转换为现有 `IamError`。 -- `FR-2`:调用方取消必须继续以 `asyncio.CancelledError` 传播。 -- `FR-3`:修复必须同时适用于 Python 3.10 和 Python 3.12,且不改变 IAM 功能逻辑。 - -在现有转换边界使用 `except asyncio.TimeoutError`。这是 Python 3.10 中 -`asyncio.wait_for()` 抛出的异常,并且与较新 Python 版本兼容。 - -组件契约影响:无。实现被修正为满足已有测试覆盖的超时和取消契约。接口、配置、 -权限、持久化、安全边界和用户文档均不变化。 - -## 任务与验收 - -| 需求 | 任务 | 验收标准 | 验证 | 结果 | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1`:修复前复现 | Python 3.10 暴露未捕获超时 | Python 3.10 目标 pytest | pass:复现未捕获的 `asyncio.TimeoutError` | -| `FR-1`、`FR-3` | `T-2`:使用 `asyncio.TimeoutError` | 现有超时转换测试通过 | Python 3.10 和 3.12 目标 pytest | pass:两个版本各 13 项 | -| `FR-2` | `T-2` | 现有取消断言通过 | 同一目标 pytest | pass | -| `FR-3` | `T-3`:静态验证 | 修改的 Python 文件检查通过 | Ruff 和 Pyright | pass | - -## 风险与待决问题 - -- 风险仅限于现有超时边界的异常选择器。 -- 没有待决问题。 - -## 审查与交付记录 - -- 2026-10-09:CI traceback 确认 Python 3.10 异常类型不匹配。 -- 2026-10-09:设计审查未发现契约、安全或兼容性阻塞项。 -- 2026-10-09:用户批准仅做兼容性修复,并明确要求不改变功能行为。 -- 2026-10-09:修复前在 Python 3.10 复现失败。完成一行异常类型限定后, - IAM 测试在 Python 3.10.20 和 Python 3.12.13 各通过 13 项,Python 3.12 - 托管 MPA 测试通过 664 项,Ruff 0.11.12 和 Pyright 均通过。 -- 提交和推送需要单独授权。 diff --git a/prd-spec/bugfixes/mpa-managed-workload-identity/2026-09-29-ensure-managed-workload-identity.md b/prd-spec/bugfixes/mpa-managed-workload-identity/2026-09-29-ensure-managed-workload-identity.md deleted file mode 100644 index 5ed075feb..000000000 --- a/prd-spec/bugfixes/mpa-managed-workload-identity/2026-09-29-ensure-managed-workload-identity.md +++ /dev/null @@ -1,80 +0,0 @@ -# Ensure Workload Identity for Managed MPA Provisioning - -- **Change ID:** `mpa-managed-workload-identity` -- **Created / revised:** 2026-09-29 -- **Status:** implemented -- **Chinese:** [2026-09-29-ensure-managed-workload-identity.zh.md](2026-09-29-ensure-managed-workload-identity.zh.md) -- **Component:** [MPA Runtime Provisioning](../../../specs/mpa-runtime-provisioning/README.md) -- **Predecessor:** [MPA Studio Workload Identity Provisioning](../../features/mpa-studio-workload-identity/2026-09-20-mpa-studio-workload-identity.md) - -## 1. Background and evidence - -`veadk mpa create` ensures the shared Studio WorkloadPool and the per-agent -WorkloadIdentity before provisioning. Studio uses the managed `provision()` -path, which does not call that ensure operation. With `managed.from-runtime`, -the reference pool can be inherited while an empty YAML override removes the -identity, and mpa-agent then rejects the partial pair during startup. - -## 2. Goals and non-goals - -### Goals - -1. Apply the existing idempotent Workload Identity ensure operation to managed provisioning. -2. Inject the ensured pool and identity as an inseparable pair for every managed source mode. -3. Prevent a reference Runtime from donating its agent-specific identity. -4. Fail before PostgreSQL, gateway, Worker, or Runtime mutations when identity preparation fails. - -### Non-goals - -- Changing naming, Identity permissions, UserPool injection, or mpa-agent consumption. -- Deleting identities after a later provisioning failure. -- Migrating existing Runtimes. - -## 3. Scenarios and requirements - -- **FR-1:** Managed provisioning ensures `agentkit-studio-workload` and - `{MPA_AGENT_ID}-studio` after account validation and before other provisioning mutations. -- **FR-2:** The ensured pair overrides reference/template values and empty YAML values. -- **FR-3:** A non-empty explicit managed Runtime value that differs from the canonical name is rejected before cloud mutation. -- **FR-4:** Identity API failures preserve the existing redacted, actionable error and prevent downstream mutations. -- **FR-5:** Reference Runtime template extraction excludes both workload identity variables. - -## 4. Design and contract impact - -The managed service reuses `ensure_studio_workload_identity()` and constructs -`IdentityClient` from the same rotating deployment credentials already used by -the Runtime control plane. The synchronous SDK call runs in a worker thread. -No new dependency or public API is introduced. Empty explicit values mean -"unspecified"; conflicting non-empty values fail closed. Created identity -resources remain for idempotent retry. - -## 5. Implementation tasks - -- **T-1:** Add a failing managed-service regression for ensure ordering, injection, and downstream blocking. -- **T-2:** Wire the existing ensure helper into managed provisioning and exclude inherited workload identity fields. -- **T-3:** Update the bilingual component contract and run targeted, coverage, pre-commit, and regression checks. -- **T-4:** Create one live MPA from local Studio and verify Runtime, Worker, and TOS mount state. - -## 6. Verification and acceptance - -| Requirement | Task | Acceptance criterion | Command | Result | -| --- | --- | --- | --- | --- | -| FR-1–FR-5 | T-1, T-2 | AC-1: all managed source modes receive the ensured pair and failures stop downstream work | `uv run --extra dev pytest tests/integrations/mpa_managed` | pass; 390 tests | -| FR-1–FR-5 | T-3 | AC-2: changed Python lines meet at least 95% coverage and repository checks pass | targeted coverage, pre-commit, and affected regression | pass; changed executable lines 100%, full regression 5259 passed, 8 skipped, 2 xfailed | -| FR-1, FR-2 | T-4 | AC-3: a new live MPA has a Ready Runtime and Worker with `/data/output` TOS configuration | local Studio creation and cloud inspection | pass; `mi-6fc92ba155cc4bb28a733fb2`, Runtime `r-yew4qlw5c017agjttpcw`, Worker `t-yew4qkzu9smwrumgn11q` | - -## 7. Risks and recovery - -The Studio management identity needs the existing Identity get/create actions. -An ensure failure now blocks creation earlier, which is intentional. An identity -created before a later failure is retained and reused on retry. Rollback is the -code revert; no data migration is required. - -## 8. Review and delivery record - -- User approval: 2026-09-29, approved adding ensure to managed provisioning and requested a live retry. -- Design review: existing naming, helper, credentials, and error contracts are reused; no unresolved interface, security, or compatibility blocker remains. -- Implementation and verification: completed 2026-09-29. Managed provisioning - ensures and injects the canonical pair before downstream mutation; reference - identities are excluded. The live Studio task succeeded with a Ready Runtime - and Worker. The Tool has `agentkit-demo` mounted read-write at `/data/output`. diff --git a/prd-spec/bugfixes/mpa-managed-workload-identity/2026-09-29-ensure-managed-workload-identity.zh.md b/prd-spec/bugfixes/mpa-managed-workload-identity/2026-09-29-ensure-managed-workload-identity.zh.md deleted file mode 100644 index 9d686cdbb..000000000 --- a/prd-spec/bugfixes/mpa-managed-workload-identity/2026-09-29-ensure-managed-workload-identity.zh.md +++ /dev/null @@ -1,73 +0,0 @@ -# 为托管 MPA 创建确保 Workload Identity - -- **变更 ID:** `mpa-managed-workload-identity` -- **创建 / 修订日期:** 2026-09-29 -- **状态:** implemented -- **英文版:** [2026-09-29-ensure-managed-workload-identity.md](2026-09-29-ensure-managed-workload-identity.md) -- **组件:** [MPA Runtime 创建](../../../specs/mpa-runtime-provisioning/README.zh.md) -- **前序设计:** [MPA Studio Workload Identity 创建](../../features/mpa-studio-workload-identity/2026-09-20-mpa-studio-workload-identity.zh.md) - -## 1. 背景与证据 - -`veadk mpa create` 会在创建前确保 Studio 共享 WorkloadPool 和每个 Agent 的 -WorkloadIdentity。Studio 使用托管 `provision()` 链路,但该链路没有调用 ensure。 -使用 `managed.from-runtime` 时可能继承参考 Runtime 的 Pool,同时 YAML 空值覆盖掉 -Identity,mpa-agent 随后会因变量对不完整而拒绝启动。 - -## 2. 目标与非目标 - -### 目标 - -1. 在托管创建中复用现有幂等 Workload Identity ensure。 -2. 对所有托管来源模式成对注入 ensure 后的 Pool 和 Identity。 -3. 防止参考 Runtime 带入其 Agent 专属 Identity。 -4. Identity 准备失败时,在 PostgreSQL、网关、Worker 或 Runtime 修改前停止。 - -### 非目标 - -- 修改命名、Identity 权限、UserPool 注入或 mpa-agent 消费逻辑。 -- 后续创建失败时删除 Identity。 -- 迁移已有 Runtime。 - -## 3. 场景与需求 - -- **FR-1:** 托管创建在账号校验后、其他创建副作用前确保 - `agentkit-studio-workload` 和 `{MPA_AGENT_ID}-studio`。 -- **FR-2:** ensure 得到的变量对覆盖参考 Runtime、模板和 YAML 空值。 -- **FR-3:** 托管 Runtime 显式提供的非空值若与规范名称不一致,必须在云资源修改前拒绝。 -- **FR-4:** Identity API 失败保留现有脱敏、可执行错误,并阻止后续修改。 -- **FR-5:** 从参考 Runtime 提取模板时排除两个 Workload Identity 变量。 - -## 4. 设计与契约影响 - -托管服务复用 `ensure_studio_workload_identity()`,并使用 Runtime 控制面已有的同一组 -轮换部署凭据创建 `IdentityClient`。同步 SDK 调用在线程中执行。不新增依赖或公共 API。 -显式空值表示“未配置”;冲突的非空值关闭式失败。已创建的 Identity 资源保留供幂等重试。 - -## 5. 实现任务 - -- **T-1:** 先增加托管服务失败回归,覆盖 ensure 顺序、注入和下游阻断。 -- **T-2:** 在托管创建中接入现有 ensure helper,并排除继承的 Workload Identity 字段。 -- **T-3:** 更新双语组件契约,执行针对性测试、覆盖率、pre-commit 和受影响回归。 -- **T-4:** 从本地 Studio 创建一个真实 MPA,检查 Runtime、Worker 和 TOS 挂载状态。 - -## 6. 验证与验收 - -| 需求 | 任务 | 验收标准 | 命令 | 结果 | -| --- | --- | --- | --- | --- | -| FR-1–FR-5 | T-1、T-2 | AC-1:所有托管来源模式收到 ensure 后的变量对,失败会阻止下游工作 | `uv run --extra dev pytest tests/integrations/mpa_managed` | pass;390 条测试 | -| FR-1–FR-5 | T-3 | AC-2:Python 增量行覆盖率至少 95%,仓库检查通过 | 针对性覆盖率、pre-commit 和受影响回归 | pass;增量可执行行 100%,全量回归 5259 passed、8 skipped、2 xfailed | -| FR-1、FR-2 | T-4 | AC-3:新 MPA 的 Runtime 与 Worker Ready,且具有 `/data/output` TOS 配置 | 本地 Studio 创建与云资源检查 | pass;`mi-6fc92ba155cc4bb28a733fb2`、Runtime `r-yew4qlw5c017agjttpcw`、Worker `t-yew4qkzu9smwrumgn11q` | - -## 7. 风险与恢复 - -Studio 管理身份需要既有 Identity get/create action。ensure 失败会更早阻止创建,这是预期行为。 -后续失败前已创建的 Identity 会保留并在重试时复用。回退只需撤销代码,不需要数据迁移。 - -## 8. 评审与交付记录 - -- 用户批准:2026-09-29,批准为托管创建增加 ensure,并要求真实重试。 -- 设计评审:复用现有命名、helper、凭据和错误契约;接口、安全与兼容性不存在未解决阻塞项。 -- 实现与验证:2026-09-29 完成。托管创建在下游修改前确保并注入规范变量对,且排除参考 - Runtime 的身份变量。真实 Studio 任务创建出 Ready Runtime 与 Worker;Tool 把 - `agentkit-demo` 以读写方式挂载到 `/data/output`。 diff --git a/prd-spec/bugfixes/mpa-name-retry/2026-10-14-owned-resource-metadata.md b/prd-spec/bugfixes/mpa-name-retry/2026-10-14-owned-resource-metadata.md deleted file mode 100644 index 16b5d793f..000000000 --- a/prd-spec/bugfixes/mpa-name-retry/2026-10-14-owned-resource-metadata.md +++ /dev/null @@ -1,40 +0,0 @@ -# Preserve owned-resource name preflight on MPA retry - -[中文版](2026-10-14-owned-resource-metadata.zh.md) - -Change ID: mpa-name-retry. Date: 2026-10-14. Status: implemented. -Contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), CON-20; predecessor [name preflight](../../features/mpa-name-preflight/2026-10-13-runtime-name-and-workspace-labels.md), FR-3. - -## Evidence and scope - -Same-request retry after Runtime deployment receives runtimeNameTaken. Installed GetRuntimeResponse omits Tags, while ListRuntimes exposes them. Name checks require owner tags only from GetRuntime. Worker creation writes managed_by=mpa-deployment and mpa_agent_key=agent_suffix(account,region,agent_id), plus MPA_AGENT_ID in Envs; name preflight instead demands an MPA_AGENT_ID tag that creation never writes. Existing tests incorrectly emulate that tag and detailed Runtime tags. The observed Runtime Error/startup failure is a separate issue, not repaired here. - -Goal: restore the approved own-resource retry guarantee while rejecting foreign/ambiguous matches. No change to frontend, request identity, scope hashing, cloud payload, resource mutation, Runtime startup, account credentials or generic agents. User's current retry-defect report and previously approved same-request recovery requirement authorize this correction; the concrete metadata-based plan was stated before edits. - -## Requirements and design - -- FR-1: Combine ownership evidence from the same matching ListRuntimes item and GetRuntime detail. Detail MPA_AGENT_ID must match; owner and optional instance tag must match. Conflicting owner/instance evidence in either response fails closed. Missing all owner evidence never authorizes a fresh request; original intents require the private proof in the amendment. Preserve existing CLI behavior without a Studio owner. -- FR-2: Worker identity requires matching environment MPA_AGENT_ID and canonical managed_by/mpa_agent_key tags, using the same account/region/agent suffix as creation. An arbitrary legacy MPA_AGENT_ID tag alone is insufficient. Missing or foreign canonical ownership still blocks. -- FR-3: Keep query timeout/cancellation, failure keys, Workspace checks, pagination, local reservation, private task owner/account snapshot and immutable retry inputs unchanged. No name-check bypass solely because the request ID exists; fresh same-name requests still fail. - -T-1: Update unrealistic Worker fixture and write failing SDK-shaped Runtime/list-tag tests and foreign/conflicting/missing-tag cases in test_name_preflight.py. T-2: Fix names.py only using existing suffix/tags helpers. T-3: Run targeted name/worker/task/route tests, managed/startup regression, Ruff/Pyright and scoped repository secret/doc checks; perform separately identified read-only live preflight against the failed task if metadata can be verified. - -## Contract assessment, review and acceptance - -The maintained CON-20 already promises verified own-resource retry, so this repairs its implementation and test mapping without changing public contracts. Add only a bilingual verification/change link. review-spec unavailable; direct review confirms no weaker ownership requirement, no secret output, unchanged mutation boundary and equivalent languages. Existing default false positives are explained, not treated as permission to reuse unknown resources. No design blocker. - -AC-1: Actual SDK-shaped detail without Tags and a matching list item allows original-request ownership; contradictions or missing/foreign owner fail. -AC-2: Creation's real Worker tag shape permits reuse; foreign/missing tags and copied legacy tags reject. Domestic and BytePlus use the same invariant. -AC-3: Required gates pass; no frontend/build required (not_applicable), no real task replay, service restart, commit or push. Read-only preflight evidence does not prove Runtime startup recovery. - -## Verification and risks - -2026-10-14, base 2ffe6e14 with unrelated local work retained. not_run: implementation checks pending. not_applicable: frontend/browser/harness/Codex gates, no affected UI or protocol. not_run: all-files pre-commit because no commit requested; scoped gates required. Provider list metadata can be eventually consistent; ambiguous ownership continues to block safely. Runtime Error may still cause readiness failure after the name check is fixed. - -## Approved design amendment after live readback - -Raw GetRuntime and ListRuntimes for the failed BytePlus Runtime both returned empty Tags, so SDK shape correction alone cannot authorize recovery. FR-1 is refined: absent owner tags may be accepted only for a server-authenticated original creation intent with identical normalized stored payload, matching private provider/account/region snapshot and matching server-derived MPA_AGENT_ID environment. Explicit foreign/conflicting tags still reject. CreationTasks.name_scope_for_request reads owner/request-scoped SQLite and rejects changed fingerprints. POST runs it after normalizing secret-free payload, before cloud name checks; public request/response fields do not change. Name preflight verifies the stored account against STS. The child already receives expectedAccount from the validated task snapshot; pass that existing proof into its name check, retaining all account and resource checks. Initial requests without such a saved intent remain strict. Never trust a browser-supplied retry flag. Additional affected files: tasks.py, service.py, frontend/server/mpa_creation.py, and corresponding task/route tests. No runner protocol expansion, resource writes or frontend rebuild is needed. This is a necessary completion of previously approved same-request recovery, not permission to reuse foreign or ambiguous resources. Direct review confirms the stronger fingerprint/account guards and resolves the original missing-owner assumption. Real read-only verification remains required. - -2026-10-14 verification on local diff based on 2ffe6e14: pass, initial tests reproduced nine failures; private-proof amendment reproduced five failures before wiring. pass, name/routes targeted 37 tests; managed/startup regression 1,091 tests with two workers (four dependency warnings). pass, read-only actual BytePlus preflight: original stored inputs and derived agent identity/account matched, canonical Worker tags matched, original name accepted. No real task start, cloud mutation or restart was executed. This preflight does not repair the observed Runtime Error. Initial fixtures used an unsupported Profile.provider replacement and typed SDK constructor; corrected to values.cloud_provider and model_validate. Ruff/Pyright fixture imports/nullable assertions were corrected before final gates. Final static/secret/doc results recorded below. Frontend/build/browser/harness/Codex not_applicable: no UI/protocol change; supporting API regression is covered. All-files pre-commit/commit/push not_run: not requested. Unrelated local changes retained. - -Final gates: pass, Ruff check/format and Pyright on all six affected Python files (zero errors/warnings); final name/route regression 37 tests; repository-configured Gitleaks on isolated scope; paired IDs/relative links and diff whitespace. Existing secret-scan exclusions for tests/build are unchanged. Final review confirms no public retry-proof input and private matching happens before cloud checks and task acceptance. No outstanding in-scope blocker. diff --git a/prd-spec/bugfixes/mpa-name-retry/2026-10-14-owned-resource-metadata.zh.md b/prd-spec/bugfixes/mpa-name-retry/2026-10-14-owned-resource-metadata.zh.md deleted file mode 100644 index 483bad50d..000000000 --- a/prd-spec/bugfixes/mpa-name-retry/2026-10-14-owned-resource-metadata.zh.md +++ /dev/null @@ -1,40 +0,0 @@ -# 保留 MPA 重试时自有资源的名称预检 - -[English](2026-10-14-owned-resource-metadata.md) - -变更 ID:mpa-name-retry。日期:2026-10-14。状态:implemented。 -契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-20;前置:[名称预检](../../features/mpa-name-preflight/2026-10-13-runtime-name-and-workspace-labels.zh.md),FR-3。 - -## 证据与范围 - -Runtime 部署后,同一请求重试被 runtimeNameTaken 拦截。已安装 GetRuntimeResponse 不含 Tags,ListRuntimes 则包含;名称检查只从 GetRuntime 要求创建者标签。沙箱创建写入 managed_by=mpa-deployment、mpa_agent_key=agent_suffix(account,region,agent_id),并在 Envs 写 MPA_AGENT_ID;名称预检却要求创建过程未写入的 MPA_AGENT_ID 标签。现有测试错误模拟了此标签和 Runtime 详情标签。观测到的 Runtime Error/启动失败是另一问题,本次不修复。 - -目标:恢复已批准的自有资源重试保证,并继续拒绝外部/归属不明的同名资源。不改前端、请求身份、作用域哈希、云端载荷、资源变更、Runtime 启动、账号凭据或通用智能体。用户当前的重试缺陷报告以及此前批准的同请求恢复要求授权此次纠正;修改前已说明按真实元信息修复的具体方案。 - -## 需求与设计 - -- FR-1:组合对应 ListRuntimes 条目和 GetRuntime 详情的归属证据。详情 MPA_AGENT_ID 必须匹配;创建者与可选内部实例标签必须匹配。任一响应的创建者/内部实例证据冲突均安全拒绝。全部缺失创建者证据时新请求不允许复用;原意图须满足补充设计中的私有证明。不传 Studio owner 的已有 CLI 行为保持不变。 -- FR-2:Worker 身份要求环境变量 MPA_AGENT_ID 和真实 managed_by/mpa_agent_key 标签匹配,使用与创建过程相同的账号/地域/agent suffix。仅有任意旧 MPA_AGENT_ID 标签不足以证明归属。真实标签缺失或外部归属仍拦截。 -- FR-3:查询超时/取消、错误键、Workspace 检查、分页、本地名称保留、任务创建者/账号快照及不可变重试输入均不变。不因存在请求 ID 而直接跳过检查,新的同名请求仍失败。 - -T-1:修正错误 Worker 夹具,在 test_name_preflight.py 先写 SDK 形状的 Runtime/列表标签失败测试,以及外部/冲突/缺失标签场景。T-2:仅修复 names.py,复用已有 suffix/标签方法。T-3:运行目标名称/Worker/任务/路由测试、managed/startup 回归、Ruff/Pyright 及范围内仓库密钥/文档检查;能验证元信息时,对失败任务另行做只读真实预检。 - -## 契约评估、评审与验收 - -现有 CON-20 已要求验证自有资源后允许重试,本次修复实现及测试映射,不改变公开契约。仅补双语验证/变更链接。review-spec 不可用,直接评审确认归属要求不减弱、不输出密钥、写操作边界不变及双语等价。解释已有误报,不把误报当作允许接管未知资源的理由。无设计阻塞。 - -AC-1:真实 SDK 形状的详情缺少 Tags,但对应列表条目证明原请求归属时允许复用;冲突或缺失/外部创建者仍失败。 -AC-2:创建过程真实 Worker 标签形状允许复用;外部/缺失标签和仅复制旧标签仍拒绝。国内与 BytePlus 使用相同不变量。 -AC-3:必需检查通过;前端/构建为 not_applicable,不重放真实任务、不重启服务、不提交/推送。只读预检证据不证明 Runtime 启动已恢复。 - -## 验证与风险 - -2026-10-14,基于 2ffe6e14,保留无关本地工作。not_run:实现检查待执行。not_applicable:前端/浏览器/harness/Codex,UI 和协议不受影响。not_run:全部文件 pre-commit,未要求提交;须执行范围内检查。服务商列表元信息可能最终一致,归属不明仍安全拒绝。名称检查修复后,Runtime Error 仍可能导致就绪失败。 - -## 云端只读核对后的已批准设计补充 - -失败的 BytePlus Runtime 的原始 GetRuntime 和 ListRuntimes 均返回空 Tags,仅修正 SDK 形状无法允许恢复。FR-1 细化:仅在服务端认证的原创建意图中,标准化载荷与已存参数完全一致,私有 provider/account/region 快照匹配,且服务端派生的 MPA_AGENT_ID 环境变量匹配时,才允许缺失创建者标签。明确外部/冲突标签仍拒绝。CreationTasks.name_scope_for_request 按创建者/请求读取 SQLite 并拒绝改变指纹。POST 在标准化无密钥载荷后、云端名称检查前调用;公开请求/响应字段不变。名称预检将存储账号与 STS 核对。子进程已从验证的任务快照接收 expectedAccount,将此已有证明传入名称检查,保留全部账号及资源校验。无保存意图的新请求继续严格检查,绝不信任浏览器提供的重试标志。补充影响文件:tasks.py、service.py、frontend/server/mpa_creation.py 和对应任务/路由测试。无需扩展 runner 协议,不执行资源变更、不重建前端。这是此前批准的同请求恢复所必需的补齐,不授权复用外部或归属不明资源。直接评审确认更严格的指纹/账号保护,修正最初对缺失创建者标签的假设,仍须只读实测。 - -2026-10-14 验证,基于 2ffe6e14 的本地差异:pass,最初测试复现九项失败;补充私有证明连线前复现五项失败。pass,名称/路由目标 37 项;两个 worker 的 managed/startup 回归 1,091 项(四条依赖警告)。pass,实际 BytePlus 只读预检:原存储参数、派生 agent 身份/账号一致,真实 Worker 标签匹配,原名称允许恢复。未启动真实任务、未执行云端变更或服务重启,此预检不修复已观测到的 Runtime Error。最初夹具误用不支持的 Profile.provider 替换和有类型 SDK 构造参数,已改为 values.cloud_provider 和 model_validate。Ruff/Pyright 夹具导入/可空调用断言已在最终检查前修正。最终静态/密钥/文档结果见下方。前端/构建/浏览器/harness/Codex 为 not_applicable:UI 和协议未变,支持 API 有回归覆盖。全部文件 pre-commit/提交/推送为 not_run:未要求执行。保留无关本地工作。 - -最终检查:pass,六个受影响 Python 文件的 Ruff 检查/格式及 Pyright,零错误/警告;最终名称/路由回归 37 项;隔离范围按仓库配置的 Gitleaks;双语 ID/相对链接及 diff 空白。现有测试/构建密钥扫描排除规则未改变。最终评审确认不增加公开重试证明输入,私有匹配发生在云端检查及任务接受前。范围内无剩余阻塞。 diff --git a/prd-spec/bugfixes/mpa-network-description/2026-10-09-valid-network-marker.md b/prd-spec/bugfixes/mpa-network-description/2026-10-09-valid-network-marker.md deleted file mode 100644 index 123313f62..000000000 --- a/prd-spec/bugfixes/mpa-network-description/2026-10-09-valid-network-marker.md +++ /dev/null @@ -1,56 +0,0 @@ -# Managed network description and rejected-intent recovery - -[中文版](2026-10-09-valid-network-marker.zh.md) - -Change ID: mpa-network-description. Created/revised: 2026-10-09. Status: implemented. - -## Evidence, goals and scope - -A fresh-account creation fails at `network`: CloudTrail confirms `CreateVpc` returned `InvalidDescription.Malformed`. The shared registry retains a VPC intent with `vpc_dispatched=false`, no VPC ID, and a colon-containing ownership description. VPC/subnet description rules do not allow colons: [CreateVpc](https://docs.volcengine.com/docs/VirtualPrivateCloud/CreateVpc-CreatingaVPC?lang=en), [CreateSubnet](https://docs.volcengine.com/docs/VirtualPrivateCloud/CreateSubnet-Creatingasubnet?lang=en). Preserve the verified account/region ownership hash and names. No credentials or raw audit payloads are recorded here. - -The user requested correction after the diagnosis, approving this bounded fix. No frontend, IAM, PG, APIG, Runtime/chat or image changes; no live cloud creation or database maintenance performed by the coding agent. Deployment itself still requires an authorized create/retry request. - -## Requirements and scenarios - -- FR-1: New VPC/subnet requests use `mpa-account-network-v1-` as Description, preserving names, account/region hash and ownership validation. -- FR-2: A persisted old description-only intent can change to the new description only when the dispatch flag is explicitly false, no resource ID is recorded and name discovery has no matching resource. Other request fields must match exactly. Generate a new ClientToken because the body changes; persist before dispatch under the existing lock. -- FR-3: Uncertain/in-flight old intents cannot issue another Create. Matching discovered old resources may be recovered. Both exact old and new scope markers are accepted for ownership; unrelated descriptions, wrong accounts, CIDRs, VPCs or zones remain errors. Existing resources are not rewritten, deleted or recreated. -- FR-4: Same-body definite permission rejection retains its ClientToken as before. Cancellation/timeout retain intent and avoid duplicate creation. - -Scenarios: fresh account passes provider description validation; a rejected old VPC/subnet intent resumes with corrected description and new token; changed CIDR/project/zone remains rejected; unknown outcome without a discovered resource fails closed; old resources are reused from registry or by exact scoped name/marker. - -## Design and affected files - -`managed/network.py` owns legal/legacy scope markers and exact-description-only compatibility in `_create`. Existing resource wait and ownership checks accept only the two scoped markers. The existing `NetworkCloudError` definite-rejection flag is the evidence for recovery; absence of the flag is not evidence of rejection. No new tables or APIs. No errors are converted into success. - -Component contracts: [Studio creation](../../../specs/studio-mpa-creation/README.md), [Runtime provisioning](../../../specs/mpa-runtime-provisioning/README.md). Update both languages and managed READMEs. Tests live in `tests/integrations/mpa_managed/test_deployment_network.py` with isolated cloud/registry fakes. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | -| --- | --- | --- | -| FR-1 | T-1: failing provider-format regression then legal marker | AC-1: both create payloads satisfy allowed description characters | -| FR-2 | T-2: exact rejected-intent recovery | AC-2: same agent retries successfully; only description/token change, saved before dispatch | -| FR-3, FR-4 | T-3: compatibility and safety tests | AC-3: old recovery/reuse, no duplicate unknown creates, changed-input rejection, same-body token preservation | -| FR-1–4 | T-4: bilingual reconciliation and checks | AC-4: managed regressions, Ruff/Pyright and secret/diff checks pass | - -Commands: `uv run --extra dev pytest tests/integrations/mpa_managed`; changed-file Ruff/Pyright and pre-commit. Frontend/build/browser/sidecar checks are not_applicable: no frontend or sidecar changes. Full-repository checks and live smoke are reported separately; no commit or deployment requested. - -## Review, approval and risks - -Direct review (review-spec unavailable): exact scoped compatibility, false-versus-missing dispatch distinction, new token for changed body, locked durable save before cloud call, resource ownership and partial failure all reviewed. Bilingual equivalence reviewed; no blockers. Approval: user requested this fix after the proposed description/rejected-intent solution. Existing resources are retained. A legacy intent with unknown outcome may still need operator reconciliation; never reset it to claim success. Verification recorded below. - -## Verification and delivery record - -2026-10-09, working tree based on `ef0ad519`, scoped to network descriptions/recovery. Earlier IAM/account work and unrelated user changes are preserved. - -- pass: test-first provider-format regressions produced 3 expected failures (`InvalidDescription.Malformed`) before the code change. -- pass: `uv run --extra dev pytest tests/integrations/mpa_managed/test_deployment_network.py tests/integrations/mpa_managed/test_deployment_network_cloud.py -q`: 62 passed, including VPC and subnet rejected-intent correction, unknown outcome discovery/blocking, legacy reuse, payload conflict and same-body token preservation. -- pass: final `uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py -q`: 508 passed; existing dependency deprecation warnings only. -- pass: `uvx --from ruff==0.11.12 ruff check` and `ruff format --check` for the two changed Python files. Final `uvx pyright --pythonpath .venv/bin/python veadk/integrations/mpa/managed/network.py tests/integrations/mpa_managed/test_deployment_network.py`: 0 errors, 0 warnings. An initial test-fixture dictionary inference error was fixed with an in-place update; no production typing bypass. -- pass: changed-file `uv run --extra dev pre-commit run --files` for Python and bilingual documents: Ruff/check/format and hardcoded-secret scan passed. YAML scan not_applicable (no YAML changes). -- pass: paired identifiers, relative design links and `git diff --check`. Reconciled the Studio configuration sentence with the preceding STS-account fix. -- not_applicable: frontend tests/build/browser, generated web assets and sidecar gates; no UI or sidecar change. -- not_run: full-repository regression/all-files pre-commit because no commit is requested and the change is limited to provisioning; live retry/cloud mutation because the user will submit the create/retry request. The diagnostic audit was read-only, not a successful creation smoke. - -Review complete: other inputs remain immutable, exact old/new scope markers only, no reset of unknown dispatch, changed-body tokens are regenerated and persisted before the cloud call. AC-1–4 met by isolated checks; full live creation is not claimed. Existing resources and records are retained until an authorized retry performs the bounded intent correction. diff --git a/prd-spec/bugfixes/mpa-network-description/2026-10-09-valid-network-marker.zh.md b/prd-spec/bugfixes/mpa-network-description/2026-10-09-valid-network-marker.zh.md deleted file mode 100644 index da486a521..000000000 --- a/prd-spec/bugfixes/mpa-network-description/2026-10-09-valid-network-marker.zh.md +++ /dev/null @@ -1,56 +0,0 @@ -# 托管网络合法描述与拒绝意图恢复 - -[English](2026-10-09-valid-network-marker.md) - -变更 ID:mpa-network-description。创建/修订日期:2026-10-09。状态:implemented。 - -## 证据、目标与范围 - -新账号创建在 `network` 失败:CloudTrail 确认 `CreateVpc` 返回 `InvalidDescription.Malformed`。共享注册表保留 `vpc_dispatched=false`、无 VPC ID、描述含冒号的 VPC 意图。VPC/子网描述规则不允许冒号:[CreateVpc](https://docs.volcengine.com/docs/VirtualPrivateCloud/CreateVpc-CreatingaVPC?lang=en)、[CreateSubnet](https://docs.volcengine.com/docs/VirtualPrivateCloud/CreateSubnet-Creatingasubnet?lang=en)。保留核验账号/地域的归属哈希及名称。此处不记录凭据或原始审计载荷。 - -用户在诊断后要求修正,批准本次限定修复。不改变前端、IAM、PG、APIG、Runtime/聊天或镜像;编码代理不执行真实云创建或数据库维护。真实部署仍通过获授权的创建/重试请求执行。 - -## 需求与场景 - -- FR-1:新 VPC/子网请求以 `mpa-account-network-v1-` 为 Description,保留名称、账号/地域哈希及归属校验。 -- FR-2:持久化旧意图仅描述不同,且派发标记明确为 false、无已登记资源 ID、按名称查询无匹配资源时,才能改为新描述。其他请求字段必须完全一致。因请求体改变,生成新的 ClientToken;在现有锁内先保存后派发。 -- FR-3:结果不确定/在途旧意图不得再次 Create。可恢复查询到的旧资源。归属仅接受当前范围精确的旧/新标记;无关描述、错误账号、CIDR、VPC 或可用区仍失败。已有资源不改写、不删除、不重建。 -- FR-4:相同请求体被明确拒绝权限时仍保留原 ClientToken。取消/超时保留意图,避免重复创建。 - -场景:新账号通过服务商描述校验;旧 VPC/子网拒绝意图以修正描述和新 token 恢复;CIDR/项目/可用区改变仍拒绝;结果不确定且查询无资源时安全失败;旧资源从注册表或精确范围名称/标记恢复复用。 - -## 设计与受影响文件 - -`managed/network.py` 管理合法/旧范围标记及 `_create` 内仅描述不同的精确兼容。现有资源等待和归属检查只接受这两个范围标记。现有 `NetworkCloudError` 明确拒绝派发标记作为恢复证据;标记缺失不代表明确拒绝。不新增表或 API,不将错误转换成成功。 - -组件契约:[Studio 创建](../../../specs/studio-mpa-creation/README.zh.md)、[Runtime 部署](../../../specs/mpa-runtime-provisioning/README.zh.md)。同步双语版本及 managed README。测试位于 `tests/integrations/mpa_managed/test_deployment_network.py`,使用隔离云端/注册表夹具。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | -| --- | --- | --- | -| FR-1 | T-1:服务商格式失败回归及合法标记 | AC-1:两种创建载荷满足描述允许字符 | -| FR-2 | T-2:精确拒绝意图恢复 | AC-2:同一智能体可重试成功;仅描述/token 改变,并先保存后派发 | -| FR-3、FR-4 | T-3:兼容及安全测试 | AC-3:旧资源恢复/复用、结果不确定不重复创建、输入变化拒绝、相同请求 token 保留 | -| FR-1–4 | T-4:双语同步及检查 | AC-4:managed 回归、Ruff/Pyright、密钥/差异检查通过 | - -命令:`uv run --extra dev pytest tests/integrations/mpa_managed`,变更文件 Ruff/Pyright 和 pre-commit。前端/构建/浏览器/sidecar 检查为 not_applicable:没有前端或 sidecar 改动。全仓检查及真实 smoke 单独记录;未请求提交或部署。 - -## 审查、批准与风险 - -直接审查(review-spec 不可用):已检查精确范围兼容、false 与标记缺失的区别、变更请求体使用新 token、云调用前锁内持久化、资源归属及部分失败。双语语义等价,无阻塞项。批准来源:用户在获知描述/拒绝意图修复方案后要求修复。保留已有资源。结果不确定的旧意图仍可能需要运维核对,不重置它来宣称成功。验证记录见下。 - -## 验证与交付记录 - -2026-10-09,基于 `ef0ad519` 工作区,仅涉及网络描述/恢复。保留此前 IAM/账号工作及用户无关改动。 - -- pass:测试先行的服务商格式回归在修改代码前出现 3 个预期失败(`InvalidDescription.Malformed`)。 -- pass:`uv run --extra dev pytest tests/integrations/mpa_managed/test_deployment_network.py tests/integrations/mpa_managed/test_deployment_network_cloud.py -q`:62 项通过,覆盖 VPC/子网拒绝意图修正、未知结果查询恢复/阻止、旧资源复用、载荷冲突和相同请求 token 保留。 -- pass:最终 `uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py -q`:508 项通过;仅有既有依赖弃用警告。 -- pass:两个变更 Python 文件的 `uvx --from ruff==0.11.12 ruff check` 和 `ruff format --check`。最终 `uvx pyright --pythonpath .venv/bin/python veadk/integrations/mpa/managed/network.py tests/integrations/mpa_managed/test_deployment_network.py`:0 错误、0 警告。初次测试夹具字典推断问题通过原地 update 修正,未绕过生产类型检查。 -- pass:Python 和双语文档变更文件的 `uv run --extra dev pre-commit run --files`:Ruff 检查/格式及硬编码密钥扫描通过。YAML 扫描 not_applicable(无 YAML 改动)。 -- pass:双语标识、设计相对链接和 `git diff --check`。Studio 配置描述与此前 STS 账号修复同步。 -- not_applicable:前端测试/构建/浏览器、生成 web 产物及 sidecar 门禁;没有 UI 或 sidecar 改动。 -- not_run:全仓回归/all-files pre-commit,因未请求提交且变更限于部署;真实重试/云资源变更由用户提交创建/重试请求后执行。诊断审计是只读查询,不代表创建 smoke 成功。 - -审查完成:其他输入保持不可变,仅接受精确范围的新/旧标记,不重置结果不确定的派发,请求体变化时重新生成 token 并在云调用前保存。AC-1–4 通过隔离检查满足,不宣称完整真实创建成功。保留已有资源和记录,直到获授权的重试执行限定意图修正。 diff --git a/prd-spec/bugfixes/mpa-network-discovery/2026-10-09-empty-network-results.md b/prd-spec/bugfixes/mpa-network-discovery/2026-10-09-empty-network-results.md deleted file mode 100644 index f77cca38b..000000000 --- a/prd-spec/bugfixes/mpa-network-discovery/2026-10-09-empty-network-results.md +++ /dev/null @@ -1,55 +0,0 @@ -# Empty managed network discovery results - -[中文版](2026-10-09-empty-network-results.zh.md) - -Change ID: mpa-network-discovery. Created/revised: 2026-10-09. Status: implemented. - -## Evidence and scope - -Read-only diagnosis confirmed an Available managed VPC, no subnet creation intent, and a successful DescribeSubnets response serialized by the installed SDK as `subnets=None, total_count=0`. `NetworkCloud._list` rejects this as invalid before the provisioner can create its first subnet. This follows the [ownership-description fix](../mpa-network-description/2026-10-09-valid-network-marker.md). - -Goal: normalize a confirmed empty provider result and resume preparation in the recorded VPC. Non-goals: changes to UI, IAM, PG, APIG, Runtime/chat, resource naming or automatic deletion. No cloud writes, task retries, database edits, commits or deployment performed by this change's verification. - -## Requirements and scenarios - -- FR-1: On the first discovery page, a null or absent `vpcs`/`subnets` collection with an integer `total_count=0` is an empty list. Existing list responses retain their behavior. -- FR-2: Reject null/missing collections without an explicit integer zero, scalar collections even with zero, and a null collection on subsequent pages. Boolean/string/float counts are not integer evidence. Do not convert provider errors to empty results. -- FR-3: A recorded managed VPC with this empty subnet result proceeds to create one subnet. A second ensure reuses the same VPC and subnet; durable intents, ownership validation and uncertain-outcome duplicate prevention remain unchanged. - -Given a fresh VPC and zero subnets, discovery returns empty and creation continues. Given incomplete/malformed discovery or a provider permission error, preparation fails. Given a paginated nonempty result, a later null/zero response cannot discard earlier rows. - -## Design and contract impact - -`managed/network_cloud.py` owns normalization in `_list`; both discovery methods use it. Normalize only `items is None`, `type(total_count) is int`, zero count, and first page. All existing pagination limits, errors, credentials and request parameters remain unchanged. No schema, public signature, permissions or configuration changes; no new dependencies. The [Studio creation contract](../../../specs/studio-mpa-creation/README.md) owns empty/error distinctions; [Runtime provisioning](../../../specs/mpa-runtime-provisioning/README.md) references it. Caller retry retains the existing VPC; no migration or cleanup is needed. - -Concurrency, cancellation and timeout handling are unchanged because only a successfully returned discovery payload is normalized. Never reinterpret a failed request or unknown Create outcome as an empty discovery. - -## Tasks, tests and acceptance - -| Requirement | Task | Acceptance | -| --- | --- | --- | -| FR-1 | T-1: failing installed-SDK and absent-collection tests, then normalization | AC-1: DescribeVpcs/DescribeSubnets zero-result responses return empty | -| FR-2 | T-2: negative and pagination regressions | AC-2: malformed responses/errors still fail; populated pages are retained | -| FR-3 | T-3: isolated provisioner/adapter integration | AC-3: resume recorded VPC, create one subnet, reuse on repeat | -| FR-1–3 | T-4: bilingual review and verification | AC-4: affected tests, Ruff/Pyright, secret/diff/document checks pass | - -Commands: `uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py -q`; changed-file Ruff/Pyright and pre-commit. Real cloud retry is a separate user-submitted smoke; no live success is claimed from fakes. - -## Review, approval and risks - -Direct design review performed because review-spec is unavailable. Reviewed provider serialization, null-versus-invalid distinctions, pagination consistency, ownership, recovery, credential redaction and bilingual equivalence. No blocking findings. User approval: “帮我改正” after the diagnosed null/zero response and proposed empty-result correction. Risk: null responses lacking integer zero still fail deliberately; this is not a general suppression of malformed discovery. Unrelated working-tree changes remain preserved. - -## Verification and delivery - -2026-10-09, working tree based on `ef0ad519`. Test-first, implementation and verification results are recorded below. Frontend/build/browser/generated assets and sidecar checks are not_applicable: those contracts are unchanged. Full-repository/all-files commit checks and live cloud smoke are not_run: no commit or live retry is requested in this change. - - -Results for the scoped correction: - -- pass: before implementation, the four installed-SDK/absent-collection regressions failed with `Network discovery returned invalid results`; after completing the durable VPC test fixture, the orchestration regression also failed at the same discovery error. -- pass: network adapter/provisioner suite: 88 passed. Final managed/Runtime command above: 534 passed, 5 existing dependency deprecation warnings, 23.67 seconds. -- pass: final isolated resume regression: 1 passed after a test-only parameter-name correction; production behavior was unchanged. -- pass: `uvx --from ruff==0.11.12 ruff check` and `ruff format --check` for `network_cloud.py`, `test_deployment_network_cloud.py`, `test_deployment_network.py`; `uvx pyright --pythonpath .venv/bin/python` on the same files: 0 errors, 0 warnings. The initial fake/adapter method parameter-name mismatch was fixed without a type suppression. -- pass: `uv run --extra dev pre-commit run --files` for the three Python files and six bilingual documents: Ruff/check/format and hardcoded-secret scan passed. YAML scan not_applicable (no YAML changes). Paired FR/T/AC identifiers, all relative document link targets and `git diff --check` passed. - -Implementation review: only a confirmed first-page empty result is normalized. Rejected null/nonzero and later-page responses do not reach resource creation. Existing list responses, provider-error propagation, record reuse and persist-before-create recovery are covered. AC-1–4 are met by isolated tests and final document/secret checks. No successful live retry is claimed. diff --git a/prd-spec/bugfixes/mpa-network-discovery/2026-10-09-empty-network-results.zh.md b/prd-spec/bugfixes/mpa-network-discovery/2026-10-09-empty-network-results.zh.md deleted file mode 100644 index 317ec0ed4..000000000 --- a/prd-spec/bugfixes/mpa-network-discovery/2026-10-09-empty-network-results.zh.md +++ /dev/null @@ -1,55 +0,0 @@ -# 托管网络查询的空结果 - -[English](2026-10-09-empty-network-results.md) - -变更 ID:mpa-network-discovery。创建/修订:2026-10-09。状态:implemented。 - -## 证据与范围 - -只读诊断确认托管 VPC 为 Available,尚无子网创建意图,成功的 DescribeSubnets 响应经当前 SDK 序列化为 `subnets=None, total_count=0`。`NetworkCloud._list` 将其视为无效结果,导致编排器无法创建第一个子网。本修复接续[归属描述修复](../mpa-network-description/2026-10-09-valid-network-marker.zh.md)。 - -目标:规范化明确的提供方空结果,在已记录的 VPC 中继续准备子网。非目标:修改 UI、IAM、PG、APIG、Runtime/聊天、资源名称或自动删除。本次验证不执行云写入、任务重试、数据库修改、提交或部署。 - -## 需求与场景 - -- FR-1:查询第一页时,`vpcs`/`subnets` 为 null 或缺失且整数 `total_count=0`,应解释为空列表。已有列表响应保持原有行为。 -- FR-2:null/缺失集合但没有明确整数零、即使数量为零的标量集合、后续页面的 null 集合均拒绝。布尔/字符串/浮点数量不算整数证据。提供方错误不得转换为空结果。 -- FR-3:已记录的托管 VPC 返回上述空子网结果时,继续创建一个子网。第二次 ensure 复用原 VPC 和子网;持久化意图、归属校验及未知结果防重复机制保持不变。 - -新 VPC 没有子网时,查询返回空列表并继续创建。查询不完整/畸形或提供方权限错误时,准备失败。分页非空结果之后出现 null/零响应时,不得丢弃之前的记录。 - -## 设计与契约影响 - -`managed/network_cloud.py` 的 `_list` 负责规范化,两个查询方法共用。仅在 `items is None`、`type(total_count) is int`、数量为零且为第一页时规范化。现有分页上限、错误、凭据及请求参数保持不变。不修改数据结构、公开签名、权限或配置,不新增依赖。[Studio 创建契约](../../../specs/studio-mpa-creation/README.zh.md) 负责区分空结果与错误;[Runtime 部署](../../../specs/mpa-runtime-provisioning/README.zh.md) 引用该契约。用户重试保留现有 VPC,无需迁移或清理。 - -并发、取消及超时处理不变,因为只规范化已经成功返回的查询载荷。不得将失败请求或未知 Create 结果解释为空查询。 - -## 任务、测试与验收 - -| 需求 | 任务 | 验收 | -| --- | --- | --- | -| FR-1 | T-1:先添加当前 SDK 及缺失集合的失败测试,再规范化 | AC-1:DescribeVpcs/DescribeSubnets 零记录响应返回空列表 | -| FR-2 | T-2:负向与分页回归 | AC-2:畸形响应/错误仍失败;非空分页记录保留 | -| FR-3 | T-3:隔离的编排器/适配器集成 | AC-3:恢复已记录 VPC,创建一个子网,重复调用复用 | -| FR-1–3 | T-4:双语审查及验证 | AC-4:相关测试、Ruff/Pyright、密钥/差异/文档检查通过 | - -命令:`uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py -q`;修改文件的 Ruff/Pyright 和 pre-commit。真实云重试为用户另行提交的冒烟,不以模拟测试宣称真实创建成功。 - -## 审查、批准与风险 - -review-spec 不可用,直接执行设计审查。已审查提供方序列化、null 与畸形结果区别、分页一致性、归属、恢复、凭据脱敏与双语等价,无阻塞项。用户批准:诊断 null/零响应并提出空结果修复后,用户要求“帮我改正”。风险:没有明确整数零的 null 响应仍故意报错,不普遍吞掉畸形查询。保留工作区无关修改。 - -## 验证与交付 - -2026-10-09,基于 `ef0ad519` 的工作区。下文已记录先失败测试、实现及验证结果。前端/构建/浏览器/生成资源及 sidecar 检查为 not_applicable:未改变其契约。全仓库/全文件提交检查及真实云冒烟为 not_run:本次未请求提交或真实重试。 - - -本次修复验证结果: - -- pass:实现前,四个当前 SDK/缺失集合回归均因 `Network discovery returned invalid results` 失败;补全持久化 VPC 测试夹具后,编排回归也在同一查询错误处失败。 -- pass:网络适配器/编排器专项:88 项通过。上述最终托管/Runtime 命令:534 项通过,5 条既有依赖弃用警告,用时 23.67 秒。 -- pass:测试方法参数名称修正后的最终恢复专项:1 项通过;未修改生产行为。 -- pass:对 `network_cloud.py`、`test_deployment_network_cloud.py`、`test_deployment_network.py` 运行 `uvx --from ruff==0.11.12 ruff check` 和 `ruff format --check`;同文件 `uvx pyright --pythonpath .venv/bin/python`:0 错误、0 警告。初始模拟对象/适配器方法参数名称不一致已修正,未使用类型抑制。 -- pass:对三个 Python 文件及六份双语文档执行 `uv run --extra dev pre-commit run --files`:Ruff/静态/格式及硬编码密钥扫描通过。YAML 扫描为 not_applicable(未修改 YAML)。双语 FR/T/AC 标识、全部相对文档链接目标及 `git diff --check` 通过。 - -实现审查:只规范化明确的第一页空结果。拒绝的 null/非零及后续页面响应不会进入资源创建。已有列表响应、提供方错误透传、记录复用及创建前持久化恢复均有覆盖。隔离测试及最终文档/密钥检查满足 AC-1–4。不宣称真实重试成功。 diff --git a/prd-spec/bugfixes/mpa-openviking-diagnostics/2026-10-14-provider-errors.md b/prd-spec/bugfixes/mpa-openviking-diagnostics/2026-10-14-provider-errors.md deleted file mode 100644 index c917eccce..000000000 --- a/prd-spec/bugfixes/mpa-openviking-diagnostics/2026-10-14-provider-errors.md +++ /dev/null @@ -1,39 +0,0 @@ -# Preserve OpenViking provider error diagnostics - -[中文版](2026-10-14-provider-errors.zh.md) - -Change ID: mpa-openviking-diagnostics. Date: 2026-10-14. Status: implemented; live reproduction not performed. -Contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), CON-10 and CON-18. - -## Evidence and scope - -A BytePlus creation failed at CreateOpenVikingCollection with provider_error. The task retained neither the structured provider code nor Request ID, and the uncertain-create guard subsequently reported openvikingCreateUnknown. A read-only account/region listing returned zero collections, but this cannot recover the original rejection. The user requested error logging after this diagnosis. This change preserves safe error metadata for both OV adapters; it does not change create parameters, model endpoints, error categories, retry eligibility, resource binding or uncertain-create recovery. Historical errors cannot be reconstructed. - -## Requirements and scenarios - -- FR-1: Structured OV response errors retain the interface operation, safe provider errorCode and Request ID in the existing diagnostic event. Structured HTTP/SDK exception bodies use the same extraction; malformed bodies and transport exceptions emit the existing category without raw text. -- FR-2: Permit only nonsecret bounded codes (1–128 characters, capitalized ASCII identifiers with dot/underscore separators) and existing hex/UUID Request IDs. Reject arbitrary text, credential-shaped values, URLs, multiline strings and oversized inputs. Never log provider Message, request/response bodies, model credentials, authorization headers or exception text. -- FR-3: Add optional errorCode to validated diagnostic events and an additive error_code SQLite column. Existing four-field and requestId events remain compatible, old records remain intact, retention remains 100 events per task, and the parent logs validated metadata with task/stage context. Public task responses remain unchanged. -- FR-4: Preserve domestic and BytePlus creation behavior, read retry limits, single create dispatch, cancellation and duplicate-create protection. OV errors remain enum-only user-visible messages. - -## Design and affected files - -T-1 (FR-1/FR-4): Add safe metadata extraction to managed/openviking.py using structured JSON bodies only; capture Request ID on malformed responses with a missing or invalid Result where available. Keep provider transports and create payloads unchanged. -T-2 (FR-2/FR-3): Extend managed/diagnostics.py validation/report and managed/tasks.py persistence/logging. Validate before data crosses the child/parent boundary. Existing IAM and Worker diagnostics continue to work. -T-3 (all requirements): Add tests/integrations/mpa_managed/test_openviking_diagnostics.py with mocked response errors for both providers, exceptions/transport errors, hostile metadata, interrupted create retry protection, persisted metadata and old-schema migration. Run these regressions before implementation, then affected MPA tests and static/secret checks. -T-4: Synchronize this bilingual design and CON-10/CON-18. No frontend assets or Runtime adaptation are needed because task HTTP fields and UI behavior do not change. - -## Review, risks and acceptance - -Approval: the user's 2026-10-14 request to add OV creation error logs accepts the previously proposed diagnostic capture. Direct review performed because review-spec is unavailable: structured-only extraction, secret exclusion, additive schema, legacy compatibility, bounded diagnostics and unchanged cloud side effects reviewed; paired documents are equivalent. No blocker. - -- AC-1: A simulated BytePlus or domestic provider rejection produces operation/category/errorCode/requestId sufficient for lookup, while provider text and credentials never reach logs/events/errors. -- AC-2: Invalid metadata is omitted, legacy events are accepted, new metadata persists after reopening an isolated task database, and migration preserves existing rows. -- AC-3: Transport failures, read retries and uncertain-create duplicate protection retain existing behavior. -- AC-4: Affected Python tests, Ruff/Pyright, whitespace, paired contracts and secret checks pass. Real cloud writes, service restart, resetting failed tasks, commit and push are outside this request. - -Live reproduction requires a later newly dispatched request after loading the change; the existing uncertain task does not automatically repeat its create. Code validation cannot prove overseas model endpoint availability. - -## Verification - -2026-10-14, local diff based on 2ffe6e14, preserving unrelated work. pass: the first regression reproduced 10 failures; after implementation the affected managed MPA/startup suite passed 1,040 tests with four dependency warnings (`uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/cli/test_studio_startup_imports.py`, two workers). Two subsequent structured-SDK-body cases brought the targeted diagnostic suite to 20 passing tests (`uv run --extra dev pytest -q tests/integrations/mpa_managed/test_openviking_diagnostics.py`). pass: Ruff 0.11.12 check/format and `uvx pyright --pythonpath .venv/bin/python` on all four changed Python files (zero errors/warnings). Initial Pyright without the repo interpreter could not resolve dependencies; the explicit interpreter resolved this without machine-global changes. pass: the repository Gitleaks hook on an isolated staged copy of the 11 in-scope files; no secrets. pass: paired document identifiers/links and diff whitespace. not_run: full-repository/all-files pre-commit because no commit was requested and the focused Python/static/secret gates cover this scope. not_applicable: frontend/browser/Codex/Harness checks because no UI, generated assets or execution contracts change. not_run: cloud creation and live error reproduction; no additional writes authorized. diff --git a/prd-spec/bugfixes/mpa-openviking-diagnostics/2026-10-14-provider-errors.zh.md b/prd-spec/bugfixes/mpa-openviking-diagnostics/2026-10-14-provider-errors.zh.md deleted file mode 100644 index 8561a0ff4..000000000 --- a/prd-spec/bugfixes/mpa-openviking-diagnostics/2026-10-14-provider-errors.zh.md +++ /dev/null @@ -1,39 +0,0 @@ -# 保留 OpenViking 云端错误诊断 - -[English](2026-10-14-provider-errors.md) - -变更 ID:mpa-openviking-diagnostics。日期:2026-10-14。状态:implemented;未进行真实复现。 -组件契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-10 和 CON-18。 - -## 证据与范围 - -一次 BytePlus 创建在 CreateOpenVikingCollection 返回 provider_error。任务未保留结构化云端错误码或 Request ID,随后不确定建库保护显示 openvikingCreateUnknown。只读账号/地域列表返回零个库,但无法据此恢复原始拒绝原因。用户在此诊断后要求补错误日志。本次为两个 OV 适配器保留安全错误元信息,不改变建库参数、模型接入点、错误分类、重试条件、资源绑定或不确定建库恢复。历史错误无法还原。 - -## 需求与场景 - -- FR-1:结构化 OV 响应错误在现有诊断事件中保留接口操作、安全云端 errorCode 和 Request ID。结构化 HTTP/SDK 异常响应体采用相同提取方式;格式错误的响应体及传输异常仅发送现有分类,不发送原始文本。 -- FR-2:仅允许非敏感、有界错误码(1–128 字符,以大写字母开头的 ASCII 标识符,可用点或下划线分隔)和现有十六进制/UUID Request ID。拒绝任意文本、凭证形式的值、URL、多行和超长输入。绝不记录云端 Message、请求/响应体、模型凭证、鉴权头或异常文本。 -- FR-3:诊断事件增加可选 errorCode,SQLite 追加 error_code 列。兼容旧四字段及 requestId 事件,保留旧记录,每个任务仍最多保留 100 条诊断;父进程结合任务/阶段记录已验证元信息。公开任务响应不变。 -- FR-4:保持国内和 BytePlus 建库行为、读取重试上限、单次建库提交、取消与防重复建库保护。OV 面向用户的错误仍为枚举文案。 - -## 设计与影响文件 - -T-1(FR-1/FR-4):在 managed/openviking.py 中仅从结构化 JSON 响应体提取安全元信息;成功响应缺少 Result 或 Result 格式不正确时,若存在 Request ID 则保留。云端传输与建库载荷不变。 -T-2(FR-2/FR-3):扩展 managed/diagnostics.py 的验证/报告及 managed/tasks.py 的持久化/日志;在子进程/父进程边界验证数据。现有 IAM、Worker 诊断继续兼容。 -T-3(全部需求):新增 tests/integrations/mpa_managed/test_openviking_diagnostics.py,使用模拟响应覆盖两个提供商的错误、异常/传输故障、恶意元信息、不确定建库防重复、诊断持久化及旧表迁移。先运行失败回归,再运行受影响 MPA 测试和静态/密钥检查。 -T-4:同步双语设计及 CON-10/CON-18。任务 HTTP 字段和 UI 行为不变,因此无需前端产物或 Runtime 改造。 - -## 评审、风险与验收 - -批准记录:用户在 2026-10-14 要求补 OV 创建错误日志,接受此前提出的诊断记录方案。review-spec 不可用,已直接评审:仅结构化提取、密钥排除、追加表结构、旧版本兼容、诊断上限及不变的云端副作用;双语文档语义一致,无阻塞项。 - -- AC-1:模拟 BytePlus 或国内云端拒绝时输出可用于定位的 operation/category/errorCode/requestId,云端文本和凭证不会进入日志、事件或错误文案。 -- AC-2:无效元信息被忽略,旧事件被接受,新字段在隔离任务库重开后仍存在,迁移保留旧记录。 -- AC-3:传输故障、读取重试及不确定建库防重复行为不变。 -- AC-4:受影响 Python 测试、Ruff/Pyright、空白、双语契约及密钥检查通过。真实云端写入、服务重启、重置失败任务、提交和推送均不在本次范围内。 - -真实复现需加载修改后再次提交新的请求;现有不确定任务不会自动重复建库。代码验证不能证明海外模型接入点可用。 - -## 验证 - -2026-10-14,基于 2ffe6e14 的本地差异,保留无关改动。pass:首次回归复现 10 个失败;实现后受影响 MPA/启动套件通过 1,040 项测试,含 4 条依赖警告(`uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/cli/test_studio_startup_imports.py`,两个 worker)。随后补充两项结构化 SDK 响应体场景,日志专项套件共通过 20 项(`uv run --extra dev pytest -q tests/integrations/mpa_managed/test_openviking_diagnostics.py`)。pass:全部四个修改的 Python 文件通过 Ruff 0.11.12 检查/格式及 `uvx pyright --pythonpath .venv/bin/python`(零错误/警告)。首次 Pyright 未指定仓库解释器而无法解析依赖,显式指定后解决,未改变机器全局配置。pass:11 个范围内文件在隔离暂存副本中通过仓库 Gitleaks hook,无密钥。pass:双语文档标识/链接及差异空白检查。not_run:全仓/全文件 pre-commit,因为未要求提交,本次使用受影响 Python/静态/密钥检查覆盖范围。not_applicable:前端/浏览器/Codex/Harness 检查,因为未改 UI、产物或执行契约。not_run:云端建库及真实错误复现,未获额外写入授权。 diff --git a/prd-spec/bugfixes/mpa-openviking-source/2026-10-13-model-source.md b/prd-spec/bugfixes/mpa-openviking-source/2026-10-13-model-source.md deleted file mode 100644 index 43da97b85..000000000 --- a/prd-spec/bugfixes/mpa-openviking-source/2026-10-13-model-source.md +++ /dev/null @@ -1,39 +0,0 @@ -# OpenViking model source and rejected-intent recovery - -[中文版](2026-10-13-model-source.zh.md) - -Date: 2026-10-13. Status: implemented; live acceptance pending. Base: `71175c68636dc5b951e9804eb292d85402511e68` plus the uncommitted automatic OV feature. - -## Background and scope -A user-created Beijing MPA reached OV after Worker preparation and was rejected by CreateOpenVikingCollection with InvalidParameter: unsupported model source empty. Cloud audit confirms an explicit rejection, not an uncertain create. Omitting Credentials is therefore insufficient. The reference library reports Source volcengine and versioned model identifiers even though its console custom-credential list is empty. Official create documentation requires Source and EndpointID for volcengine and conditionally ApiKey. -Goal: keep automatic Beijing OV, with explicit model source/identifiers and the already resolved deployment-account Ark key. Do not add UI inputs, create model endpoints, alter BytePlus/general agents, update existing OV credentials, or execute a paid cloud retry without separate user action/authorization. - -## Requirements and design -- FR-1: VLM Credentials uses Source `volcengine`, EndpointID `doubao-seed-2-0-lite-260428`; Embedding uses Source `volcengine`, EndpointID `doubao-embedding-vision-251215`. Keep the existing model names/versions. These identifiers come from the reference library; acceptance for a new API library remains subject to live verification. -- FR-2: The adapter receives the resolved Ark model key separately, validates it locally, and attaches ApiKey to a copied wire payload only. Nonsecret intents contain Source/EndpointID but no model key. Registry, task results, diagnostics and repr must exclude both Ark and OV keys. Fresh Studio uses its account-discovered key; explicitly opted-in CLI can use its configured model key, including template MODEL_AGENT_API_KEY. Missing/masked/invalid keys fail before dispatch with the existing configuration error. Registered-library retry reads OV key again without resending model credentials. -- FR-3: Upgrade the complete matching previous no-Credentials intent only when there is no OV ID, no pending Runtime operation, and openviking_create_requested is explicitly false. This is the durable state after a proven rejection. Unknown/in-flight creates, changed project/name/model/description and registered IDs remain fail-closed; do not erase bindings or reset flags to work around failures. A recovered request retains agent/library name and records the corrected nonsecret intent before dispatch. -- FR-4: Validate Source/EndpointID in Get results before reuse, excluding returned key values from storage. Keep lock, timeout, cancellation, one-shot create and readiness behavior. Create requests get at most one dispatch; no extra paid fallback. - -## Affected files and tasks -OpenViking adapter/provisioner, MPA service wiring, focused OV/service tests, bilingual creation contract and operator/design docs. No frontend or Runtime protocol change; the same four OV env vars are already supported. T-1 regression tests for wire credentials/no secret persistence; T-2 source validation and safe rejected-intent recovery; T-3 wire existing model key; T-4 run affected creation regression, Ruff/Pyright, secret scan and paired-document checks. - -## Acceptance, risks and review -AC-1 wire credentials are explicit and secret-free intent remains stable; AC-2 same failed agent can recover only after proven rejection; AC-3 changed/unknown/registered requests never create a duplicate; AC-4 BytePlus/manual/general paths unchanged and checks recorded. Endpoint aliases and model/key authorization still require live acceptance; default model access may require account enablement. No claim of successful cloud creation until confirmed. -Direct design review (review-spec unavailable) covers compatibility, secret lifetimes, copied payload, ownership, rejection vs uncertainty, cancellation/deadlines and bilingual equivalence. User requested the missing-source fix on 2026-10-13; the concrete plan reuses the existing Ark key and preserves the failed task identity. No commit, push or cloud retry authorized. - -## Verification -Test-first regression: 27 failed / 2 passed before implementation (new keyword and explicit Credentials were absent). After implementation, OV/service tests passed 192 cases. Final broad creation/RBAC regression passed 1,087 cases with two workers. Read-only Get of the reference library confirms the exact Source and both EndpointID values; this is not new-library creation acceptance. - -| Gate (2026-10-13, uncommitted correction) | Result | -| --- | --- | -| `uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed/test_openviking.py tests/integrations/mpa_managed/test_service.py` | pass: 192 | -| `uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/frontend/server/mpa/test_runtime_profile_routes.py tests/cli/test_studio_rbac.py` | pass: 1,087 | -| `uvx ruff==0.11.12 check ` and `format --check` | pass | -| `uvx pyright --pythonpath .venv/bin/python ` | pass: 0 errors/warnings | -| `uv run --extra dev pre-commit run gitleaks --files ` | pass: source/tests/docs/built assets | -| Paired docs, links/identifiers and `git diff --check` | pass | -| Frontend rebuild/browser | not_applicable: correction has no frontend changes; initial auto-OV frontend gates remain recorded in the feature PRD | -| Corrected live create / key retrieval / Runtime readiness | not_run: user can retry the same failed task; no automatic cloud write performed | -| Full-repository pre-commit / commit / push | not_run: no commit requested | - -Review: wire payload is copied; credentials never enter the registry intent; source/endpoint readback excludes secret fields; registered/ambiguous/changed requests cannot be upgraded. AC-1–AC-4 implementation/offline checks complete; corrected cloud acceptance remains pending. Each retry starts a new runner process, so this child-process correction is loaded on retry without changing the existing browser request. diff --git a/prd-spec/bugfixes/mpa-openviking-source/2026-10-13-model-source.zh.md b/prd-spec/bugfixes/mpa-openviking-source/2026-10-13-model-source.zh.md deleted file mode 100644 index 80f284442..000000000 --- a/prd-spec/bugfixes/mpa-openviking-source/2026-10-13-model-source.zh.md +++ /dev/null @@ -1,39 +0,0 @@ -# OpenViking 模型来源及已拒绝意图恢复 - -[English](2026-10-13-model-source.md) - -日期:2026-10-13。状态:已实现;待真实验收。基础:`71175c68636dc5b951e9804eb292d85402511e68` 加未提交的自动 OV 功能。 - -## 背景与范围 -用户创建的北京 MPA 完成 Worker 准备后,CreateOpenVikingCollection 返回 InvalidParameter:模型来源为空。云审计确认明确拒绝,不是未知创建结果。因此不能省略 Credentials。参考库即便控制台显示自配凭证为空,接口仍返回 Source volcengine 与带版本模型标识。官方建库文档要求 volcengine 的 Source、EndpointID,并要求条件性的 ApiKey。 -目标:保留北京自动 OV,显式传入模型来源/标识及已经解析的部署账号方舟 Key。不新增页面输入、不创建模型接入点、不改 BytePlus/通用智能体、不更新现有 OV 凭证,也不在没有单独用户操作/授权时执行付费云重试。 - -## 要求与设计 -- FR-1:VLM Credentials 使用 Source `volcengine`、EndpointID `doubao-seed-2-0-lite-260428`;Embedding 使用 Source `volcengine`、EndpointID `doubao-embedding-vision-251215`。保留原模型名/版本。标识来自参考库,新 API 创建是否接受仍需真实验证。 -- FR-2:适配器单独接收已解析方舟模型 Key,本地校验后仅将 ApiKey 加入复制的网络请求。非秘密意图含 Source/EndpointID,但没有模型 Key。注册库、任务结果、诊断及 repr 不得出现方舟或 OV Key。新 Studio 使用账号自动发现的 Key;显式启用的 CLI 可使用配置模型 Key,包括模板 MODEL_AGENT_API_KEY。缺失/掩码/非法 Key 在发送前以现有配置错误拒绝。注册库重试仅重新取 OV Key,不重发模型凭证。 -- FR-3:仅当没有 OV ID、没有待完成 Runtime 操作、openviking_create_requested 明确为 false,才升级完全匹配的旧无 Credentials 意图。这是明确拒绝后的持久状态。未知/进行中创建、项目/名称/模型/描述改变及已注册 ID 继续安全拒绝,不删除绑定或重置标志来绕过失败。恢复保留智能体/库名,发送前登记修正的非秘密意图。 -- FR-4:复用前核验 Get 的 Source/EndpointID,不持久化返回的 Key。保留锁、超时、取消、单次创建与就绪语义。每次创建最多发送一次,不增加付费回退。 - -## 影响文件与任务 -OpenViking 适配器/资源准备、MPA 服务透传、目标 OV/service 测试、双语创建契约及使用/设计说明。没有前端或 Runtime 协议变化,仍使用已支持的四个 OV 环境变量。T-1 请求凭证/不持久化秘密回归;T-2 来源核验与明确拒绝意图恢复;T-3 透传现有模型 Key;T-4 受影响创建回归、Ruff/Pyright、秘密扫描及双语文档检查。 - -## 验收、风险与评审 -AC-1 网络凭证明确,非秘密意图稳定;AC-2 同一失败智能体仅在明确拒绝后可恢复;AC-3 改变/未知/已登记请求不重复建库;AC-4 BytePlus/手动/通用流程保持行为并记录检查。接入点别名、模型/Key 权限仍需真实验收,默认模型可能需要账号开通。确认前不声称云建库成功。 -review-spec 不可用,直接设计评审核验兼容、秘密生命周期、请求复制、归属、拒绝与未知结果、取消/期限及双语等价。用户于 2026-10-13 要求修复缺失来源,具体方案复用已有方舟 Key 并保留失败任务身份。未授权提交、推送或云重试。 - -## 验证 -测试先行回归:实现前 27 失败 / 2 通过(缺少新增关键字及显式 Credentials)。实现后 OV/service 192 项通过。最终广泛创建/RBAC 回归以两个 worker 执行,1,087 项通过。参考库只读 Get 核实了 Source 和两个 EndpointID 的准确值,这不等于新库创建验收。 - -| 检查(2026-10-13,未提交修正) | 结果 | -| --- | --- | -| `uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed/test_openviking.py tests/integrations/mpa_managed/test_service.py` | pass:192 | -| `uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/frontend/server/mpa/test_runtime_profile_routes.py tests/cli/test_studio_rbac.py` | pass:1,087 | -| `uvx ruff==0.11.12 check ` 及 `format --check` | pass | -| `uvx pyright --pythonpath .venv/bin/python ` | pass:0 错误/警告 | -| `uv run --extra dev pre-commit run gitleaks --files ` | pass:源码/测试/文档/构建产物 | -| 双语文档、链接/标识及 `git diff --check` | pass | -| 前端重建/浏览器 | not_applicable:本修正未修改前端,初版 auto-OV 前端检查保留在功能 PRD 中 | -| 修正后真实创建 / 取 Key / Runtime 就绪 | not_run:用户可重试原失败任务,未自动执行云写操作 | -| 全仓库 pre-commit / commit / push | not_run:未要求提交 | - -评审:网络请求使用副本;凭证不进入注册意图;来源/接入点回读排除秘密字段;已登记/未知/改变请求不能升级。AC-1–AC-4 实现/离线检查完成,修正后真实云验收待完成。每次重试启动新的 runner 进程,所以该子进程修正在重试时加载,无需改变已有浏览器请求。 diff --git a/prd-spec/bugfixes/mpa-pg-recovery/2026-10-10-terminal-create-failure.md b/prd-spec/bugfixes/mpa-pg-recovery/2026-10-10-terminal-create-failure.md deleted file mode 100644 index 6053d715a..000000000 --- a/prd-spec/bugfixes/mpa-pg-recovery/2026-10-10-terminal-create-failure.md +++ /dev/null @@ -1,73 +0,0 @@ -# Recover terminal PG Workspace creation failures - -[中文版](2026-10-10-terminal-create-failure.zh.md) - -- Change ID: `mpa-pg-terminal-create-failure` -- Date: 2026-10-10 -- Status: implemented -- Component: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), CON-13 - -## Background and evidence - -Automatic PG bootstrap is shared by account, region and project. CreateWorkspace can fail after leaving a cloud Workspace in `CreateFailed`, while SQLite retains an intent without a resource ID. Discovery validates ownership tags before checking status, outside the readiness loop. Missing tags therefore immediately fail every later agent request. An outer provider InternalError can contain a purchasing rejection; its outer code alone cannot establish whether a resource exists. - -## Goals, non-goals and scenarios - -Restore creation after a confirmed terminal creation failure, without deleting cloud resources or changing healthy reuse. Apply the same recovery contract to Volcengine and BytePlus. Do not alter manual/explicit adoption, databases, VPC, APIG, Runtime, images, frontend, or chat. No live cloud mutations are needed for implementation verification. - -Scenarios: initial failure with or without an ID; retry after permissions are corrected; failed old resource alongside a healthy replacement; replacement request timeout; concurrent agents; partial metadata; conflicting ownership; explicit IDs and deleted/unknown/operationally failed resources. - -## Requirements and design - -- **FR-1:** Only exact `CreateFailed` is a recoverable terminal creation status. `Failed`, deleted/deleting, unknown statuses and service errors retain fail-closed semantics. A newly dispatched create that reaches `CreateFailed` fails that task; do not repeatedly purchase in the same attempt. -- **FR-2:** Before retiring a failed candidate, require complete matching account, region, project, name, engine and nonempty ID; reject present conflicting ownership tags. Missing ownership tags are allowed only when classifying `CreateFailed`, never when adopting a ready resource. Explicit configured IDs never change automatically. -- **FR-3:** Add a nonsecret `failed_workspaces` SQLite table keyed by scope, purpose and ID. Retire a confirmed failed managed binding and its active intent atomically. Preserve old cloud resources. Ignore retired IDs only while they remain `CreateFailed`; reject a changed status. Existing tables are retained and upgraded additively. -- **FR-4:** With an ID-less uncertain intent, one newly observed confirmed failed candidate and no live candidate permits retirement. Multiple unretired failed candidates remain ambiguous. Previously retired failures must never resolve a newer uncertain create. No candidates still means uncertain; no blind CreateWorkspace retry. A healthy/creating candidate remains subject to existing identity/tag checks and duplicate protection. -- **FR-5:** Discovery of incomplete owned metadata enters the existing bounded readiness loop instead of failing outside it. Present conflicts still fail immediately. Use the existing per-scope OS lock and PG/task deadline; cancellation retains durable identity/intent. -- **FR-6:** Initial healthy creation, shared reuse, successful response-loss recovery and downstream failure remain compatible. Generic API/UI failure categories and sanitized diagnostics remain unchanged. An already recorded Workspace that is missing or has an operational failure is never replaced. - -Affected files: `managed/pg_bootstrap.py`, `tests/integrations/mpa_managed/test_auto_pg.py`, paired component specs, managed module READMEs and this design. No new dependency, browser field, provider request format or cloud delete API. - -## Tasks, tests and acceptance - -- **T-1 / AC-1:** Regression tests first: both providers, admin/business purposes, missing tags, stored IDs, explicit adoption, concurrent recovery and healthy reuse. -- **T-2 / AC-2:** Implement additive durable retirement and state-first recovery; at most one new create per purpose per request. Failed cloud resources remain untouched. -- **T-3 / AC-3:** Verify unknown outcomes, retired-resource history, ambiguity, changed retired status, foreign scope/tags, API errors, missing details, timeout and cancellation prevent duplicate or destructive operations. -- **T-4 / AC-4:** Run targeted and managed integration regressions, Python default regression with two workers, changed-file Ruff/Pyright, paired-document and whitespace checks. No frontend change, so frontend build/browser gates are not applicable. Do not commit/push or perform live cloud creation without authorization. - -## Risks and review - -CreateFailed classification relies on a cloud terminal creation status and verified scope. Missing tags on failed resources do not authorize adoption or deletion. Preserve failed IDs durably so an old failure cannot authorize repetition of a later timed-out request. Independent hosts still require shared bootstrap state; this fix does not introduce distributed locking. Retained failed cloud resources may need operator cleanup. Old stores with unknown outcomes and multiple candidates deliberately require manual recovery. - -User approved the recovery rule with “改下” on 2026-10-10 after the normal/failure/unknown behavior was explained. `review-spec` is unavailable; direct review covered feasibility, additive schema, state transitions, cancellation, locking, explicit-ID boundaries, secrets, domestic compatibility and bilingual equivalence. No blockers; production/test edits may proceed. - -## Verification record - -Verification date: 2026-10-10. Tested revision: working tree based on `5644e146c9dfff6d2e290b1663cb2ea41dbb6b91`, including the previously approved BytePlus adaptation. This recovery change modifies one production module and one test module, plus paired design/spec/module documentation. Existing adaptation edits are preserved. The normal/failure/concurrency tests use disposable SQLite files and fake cloud adapters, not user state or live cloud resources. - -| Check | Outcome | Evidence / limitation | -| --- | --- | --- | -| Failing regression before implementation | pass | 14 of 16 initial recovery cases failed for missing-tag recovery, retired-history absence and failure-state handling; two ownership guards already passed. Additional present-conflict cases failed before their corrections. | -| `uv run --extra dev pytest tests/integrations/mpa_managed -q --tb=short` | pass | 782 tests, 5 existing warnings; 30 new recovery/compatibility cases. Both providers, both Workspace purposes, interleaved concurrent requests, unchanged healthy reuse, unknown/cancelled outcomes, migration and explicit-ID guards covered. | -| `uv run --with ruff==0.11.12 ruff check ` and `ruff format --check` | pass | `pg_bootstrap.py` and `test_auto_pg.py`; temporary tooling, no global installation. | -| `uv run --with pyright pyright ` | pass | Same two files; 0 errors, warnings or information diagnostics. | -| `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" --tb=short` | fail | 6,985 passed, 1 failed, 53 skipped, 4 xfailed in 411.61 seconds. Existing `test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes` expects `text/markdown`; local MIME detection returns `application/octet-stream`. The same failure was reproduced on unmodified HEAD during the prior BytePlus verification. Production recovery code was unchanged during this run; the two final compatibility tests were additionally verified in the final 782-test managed run. | -| Paired documents, identifiers, relative links and `git diff --check` | pass | Reviewed both language versions and maintained CON-13; no credentials or production logs in the change. | -| Frontend/browser/build, harness coverage, Codex smoke | not_applicable | No UI/generated release asset, chat, sidecar or Codex runtime change. | -| Pre-commit, branch synchronization, commit/push | not_run | No commit or push authorized; these gates apply before a subsequent commit. | -| Live creation/replacement | not_run | This code fix was verified offline; no new cloud mutation authorized for its verification. The earlier explicitly authorized cleanup is a separate operation. | - -Final direct review: creation count is bounded to one new purchase per purpose per request, failed identities survive restarts, state release is atomic under the existing lock, explicit IDs and unrelated scope/purpose records are preserved, terminal operational failures are not replaced, and missing metadata cannot hide a present conflict. Failed resource cleanup and distributed/multi-host coordination remain operator responsibilities. - - -## Commit preparation verification (2026-10-10) - -The user authorized committing and pushing the local changes. Fetched `origin` and `upstream`, then ran `git rebase --autostash upstream/main` on `feat/mpa-account-fixes-upstream-clean-20261010`. The branch was already current; all 159 pre-existing changed file states matched after synchronization. The baseline remains `5644e146c9dfff6d2e290b1663cb2ea41dbb6b91`. The only subsequent production adjustment is Ruff formatting of the creation-route registration in the CLI. - -- **pass:** `uv run --extra dev pre-commit run --all-files`: Ruff check/format and both secret scanners. The first run reformatted the CLI call; the repeat passed. -- **pass:** post-synchronization `uv run --extra dev pytest tests/integrations/mpa_managed -q --tb=short`: 782 passed, 5 existing warnings. -- **pass:** post-synchronization `npm --prefix frontend test`: 1,377 Node tests and 67 Vitest tests. -- **pass:** post-synchronization `npm --prefix frontend run check:i18n` and `npm --prefix frontend run test:webui-assets`: 2 locales / 21 namespaces and 113 files / 350 references. -- **not_run:** repeated full Python regression, frontend build/browser and Pyright. Synchronization preserved their previously tested source; the CLI adjustment changes formatting only. Existing full-regression MIME and CLI Pyright baseline failures remain documented above. No new cloud creation, publication or deployment is part of this commit request; overseas end-to-end readiness remains unverified. - -The commit includes BytePlus adaptation, terminal PG creation-failure recovery, paired documents, tests and matching generated frontend assets. No credentials, local state databases or production logs are included. diff --git a/prd-spec/bugfixes/mpa-pg-recovery/2026-10-10-terminal-create-failure.zh.md b/prd-spec/bugfixes/mpa-pg-recovery/2026-10-10-terminal-create-failure.zh.md deleted file mode 100644 index 1a3158565..000000000 --- a/prd-spec/bugfixes/mpa-pg-recovery/2026-10-10-terminal-create-failure.zh.md +++ /dev/null @@ -1,73 +0,0 @@ -# 恢复 PG Workspace 的终态创建失败 - -[English](2026-10-10-terminal-create-failure.md) - -- 变更 ID:`mpa-pg-terminal-create-failure` -- 日期:2026-10-10 -- 状态:已实现 -- 组件:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-13 - -## 背景与证据 - -自动 PG 初始化按账号、地域和项目共享。CreateWorkspace 可能报错,同时在云端留下 `CreateFailed` 的 Workspace,SQLite 则保留没有资源 ID 的意图。发现逻辑在检查状态前校验归属标签,且位于就绪等待循环外,因此标签缺失会让所有后续智能体请求立即失败。服务商外层 InternalError 可能包含购买权限拒绝;仅凭外层错误码无法确定资源是否存在。 - -## 目标、非目标与场景 - -明确终态创建失败后恢复创建,不删除云资源,不改变正常复用。国内和 BytePlus 使用相同恢复规则。不修改手动模式或显式接管、数据库、VPC、APIG、Runtime、镜像、前端或聊天。实现验证无需真实云端写操作。 - -场景:首次失败有或没有 ID;修正权限后重试;旧失败资源与正常替代资源同时存在;替代请求超时;多个智能体并发;元数据不完整;归属冲突;显式 ID,以及删除、未知状态或运行故障的资源。 - -## 需求与设计 - -- **FR-1:** 仅精确的 `CreateFailed` 属于可恢复的终态创建失败。`Failed`、删除或删除中、未知状态和服务错误继续快速失败。本次刚发出的创建若进入 `CreateFailed`,当前任务失败,不在同一次尝试内重复购买。 -- **FR-2:** 将失败候选记录退出前,必须完整匹配账号、地域、项目、名称、引擎及非空 ID;已存在但冲突的归属标签必须拒绝。仅在分类 `CreateFailed` 时允许标签缺失,接管正常资源不放宽标签检查。显式配置的 ID 不自动变更。 -- **FR-3:** SQLite 增加非敏感 `failed_workspaces` 表,以范围、用途和 ID 为键。原子地将已确认失败的托管绑定及活动意图退出,保留原云资源。仅当退出的 ID 仍为 `CreateFailed` 时忽略它,状态变化则拒绝。原有表保留,采用增量升级。 -- **FR-4:** 对无 ID 的未知意图,只有一个新发现的已确认失败候选且没有正常候选时,允许退出;多个未退出失败候选仍视为歧义。之前已退出的失败资源不能用于解释更新请求的未知结果。无候选仍表示结果未知,不盲目重发 CreateWorkspace。正常或创建中候选继续执行现有身份、标签与防重复校验。 -- **FR-5:** 发现托管资源元数据暂不完整时,进入现有有界就绪循环,而非在循环外失败。已存在的冲突仍立即失败。沿用范围级 OS 锁和 PG/任务截止时间;取消保留持久化身份或意图。 -- **FR-6:** 正常首次创建、共享复用、创建响应丢失后的成功恢复和后续步骤失败保持兼容。通用 API/UI 错误分类与脱敏诊断不变。已记录的 Workspace 缺失或发生运行故障时绝不替换。 - -影响文件:`managed/pg_bootstrap.py`、`tests/integrations/mpa_managed/test_auto_pg.py`、双语组件规范、托管模块 README 和本设计。不增加依赖、浏览器字段、服务商请求格式或云端删除接口。 - -## 任务、测试与验收 - -- **T-1 / AC-1:** 先编写回归测试:两种服务商、管理/业务用途、标签缺失、已存 ID、显式接管、并发恢复和正常复用。 -- **T-2 / AC-2:** 实现增量持久化退出记录和先判断状态的恢复,每次请求每种用途最多创建一个新资源,不改动失败云资源。 -- **T-3 / AC-3:** 验证未知结果、退出资源历史、歧义、退出资源状态变化、外部范围/标签、API 错误、详情缺失、超时与取消不会引起重复或破坏操作。 -- **T-4 / AC-4:** 执行针对性及托管流程集成回归、两进程默认 Python 回归、改动文件 Ruff/Pyright、双语文档和空白检查。无前端变更,前端构建/浏览器门禁不适用。未经授权不提交、推送或进行真实云创建。 - -## 风险与评审 - -CreateFailed 分类依赖云端终态创建状态和已验证范围。失败资源标签缺失不代表允许接管或删除。持久化保留失败 ID,防止旧失败授权重发更新的超时请求。独立主机仍需共享初始化状态,本修复不引入分布式锁。保留的失败云资源可能需管理员清理。旧状态结果未知且存在多个候选时,有意保留人工恢复要求。 - -2026-10-10 用户在了解正常、失败及未知状态行为后,以“改下”批准恢复规则。`review-spec` 不可用,直接评审覆盖可行性、增量表结构、状态转换、取消、锁、显式 ID 边界、敏感信息、国内兼容和双语一致性。没有阻塞项,可以修改生产代码和测试。 - -## 验证记录 - -验证日期:2026-10-10。测试版本:基于 `5644e146c9dfff6d2e290b1663cb2ea41dbb6b91` 的工作区,包含此前已批准的 BytePlus 适配。本次恢复修复修改一个生产模块、一个测试模块及双语设计/规范/模块说明,保留已有适配改动。正常、失败及并发测试使用临时 SQLite 文件和模拟云适配器,不使用用户状态或真实云资源。 - -| 检查 | 结果 | 证据 / 限制 | -| --- | --- | --- | -| 实现前失败回归 | pass | 首批 16 个恢复用例中 14 个因缺失标签恢复、退出历史不存在及失败状态处理而失败,两个归属防护原本通过;另补的现有冲突用例也在修正前失败。 | -| `uv run --extra dev pytest tests/integrations/mpa_managed -q --tb=short` | pass | 782 个测试,5 条已有警告;新增 30 个恢复/兼容用例。覆盖两种服务商、两种 Workspace 用途、交错并发请求、正常复用不变、未知/取消结果、迁移和显式 ID 防护。 | -| `uv run --with ruff==0.11.12 ruff check ` 及 `ruff format --check` | pass | `pg_bootstrap.py` 和 `test_auto_pg.py`;临时工具环境,没有全局安装。 | -| `uv run --with pyright pyright ` | pass | 同上两个文件,0 个错误、警告或信息诊断。 | -| `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" --tb=short` | fail | 6,985 项通过,1 项失败,53 项跳过,4 项 xfailed,耗时 411.61 秒。已有 `test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes` 预期 `text/markdown`,本地 MIME 检测返回 `application/octet-stream`。此前 BytePlus 验证已在未修改 HEAD 上复现同一失败。本次全量运行期间恢复生产代码未变化,最后两个兼容用例另在最终 782 项托管流程测试中验证。 | -| 双语文档、标识符、相对链接与 `git diff --check` | pass | 评审两种语言及维护的 CON-13,改动不包含凭据或生产日志。 | -| 前端/浏览器/构建、harness 覆盖率、Codex smoke | not_applicable | 没有 UI/生成发布产物、聊天、sidecar 或 Codex 运行时变更。 | -| Pre-commit、分支同步、提交/推送 | not_run | 未授权提交或推送;后续提交前执行对应门禁。 | -| 真实创建/替换 | not_run | 本代码修复采用离线验证,未授权为验证执行新的云端写操作;此前明确授权的清理是独立操作。 | - -最终直接评审:每次请求每种用途最多购买一个新资源,失败身份重启后仍保留,解除状态在现有锁下原子执行,显式 ID 与其他范围/用途记录保留,运行故障终态不会被替换,缺失元数据无法遮盖已存在冲突。失败资源清理及分布式/多主机协调仍由管理员负责。 - - -## 提交前验证(2026-10-10) - -用户已授权提交并推送本地修改。获取 `origin` 和 `upstream` 后,在 `feat/mpa-account-fixes-upstream-clean-20261010` 执行 `git rebase --autostash upstream/main`。分支已经同步;同步后原有 159 个改动文件状态全部一致。基础版本仍为 `5644e146c9dfff6d2e290b1663cb2ea41dbb6b91`。随后唯一的生产代码调整是 Ruff 格式化 CLI 中的创建路由注册调用。 - -- **pass:** `uv run --extra dev pre-commit run --all-files`:Ruff 检查/格式化及两项敏感信息扫描。首次运行格式化了 CLI 调用,再次运行全部通过。 -- **pass:** 同步后 `uv run --extra dev pytest tests/integrations/mpa_managed -q --tb=short`:782 项通过,5 条已有警告。 -- **pass:** 同步后 `npm --prefix frontend test`:1,377 项 Node 测试和 67 项 Vitest 测试。 -- **pass:** 同步后 `npm --prefix frontend run check:i18n` 及 `npm --prefix frontend run test:webui-assets`:2 种语言 / 21 个命名空间,113 个文件 / 350 个引用。 -- **not_run:** 重复执行完整 Python 回归、前端构建/浏览器及 Pyright。同步保留了此前已验证的源码,CLI 调整仅改变格式。完整回归的 MIME 及 CLI Pyright 基线失败仍按上文记录。本次提交请求不涉及新的云端创建、发布或部署;海外全流程就绪仍未验证。 - -提交包含 BytePlus 适配、PG 创建失败终态恢复、双语文档、测试及配套前端构建产物,不包含凭据、本地状态数据库或生产日志。 diff --git a/prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-error-codes.md b/prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-error-codes.md deleted file mode 100644 index 7697cb08b..000000000 --- a/prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-error-codes.md +++ /dev/null @@ -1,32 +0,0 @@ -# Preserve MPA Runtime SDK failure codes - -[中文版](2026-10-14-sdk-error-codes.zh.md) - -Change ID: mpa-runtime-diagnostics. Date: 2026-10-14. Status: implemented; live retry pending. -Contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), CON-10. - -## Evidence, scope and approval - -A BytePlus no-OV request initialized its business database and Skill Space, saved pending create intent, then failed without a Runtime ID. Its SDK error was reduced to provision/provider_error. AgentKit ApiError retains error_code, but the Runtime adapter does not record it. The user explicitly approves adding logs before retrying the same task. Direct review (review-spec unavailable) confirms bounded safe metadata, legacy diagnostic compatibility and unchanged side effects; no blocker. - -## Requirements, design and tasks - -- FR-1 / T-1: RuntimeCloud.call emits create_runtime/update_runtime failures using the existing diagnostic channel. These are also the requests that enable publishing; no separate release API is used in the current provisioning path. Preserve category, validated SDK errorCode and requestId when the SDK exposes one. Traverse at most eight exception/cause objects, reject cycles and use existing validators. Never parse exception text or emit request bodies, credentials, headers, provider Message or ClientToken. -- FR-2 / T-2: Reraise the same original exception; success, cancellation, request serialization, provider selection, timeout, call count and SDK retry behavior are unchanged. Neither saved inputs nor pending binding/token are modified. Skills/other operations keep their existing diagnostic behavior. Add only the two operation names to the allowlist; existing diagnostic persistence already supports these fields. -- FR-3 / T-3: Write failing isolated tests first for both providers, create/update, direct/wrapped/unsafe codes, missing Request ID, transport/cancellation, unchanged payload and successful results. Verify managed/startup tests, Ruff/Pyright, secret scan and paired docs. Update CON-10 and module READMEs. Frontend/general agents and cloud contracts do not change. - -## Acceptance and risk - -AC-1: Simulated Runtime SDK rejection reaches the persisted diagnostic channel with operation/category/errorCode and optional Request ID, without provider text or secrets. -AC-2: The same exception/payload and single dispatch are preserved, with bounded cycle-safe traversal, no logging on success/cancellation and no new retry/cleanup side effect. -AC-3: Affected checks pass with actual evidence. No commit/push, real API replay, server restart or cloud writes during implementation. - -Historical exceptions cannot be reconstructed. Current AgentKit SDK does not expose structured Request ID on every ApiError; do not invent one. The user must load the updated backend before retrying. Original ClientToken semantics and binding checks remain in force. Unknown real provider causes are not claimed as fixed by logging. - -## Verification - -2026-10-14, local diff on base 2ffe6e14; preserve unrelated work. not_run: implementation checks pending. Frontend/browser/Harness/Codex gates not_applicable because their behavior is unchanged. Live Runtime create/retry not_run; user-driven reproduction follows implementation. - -2026-10-14 verification: pass, the first isolated regression reproduced 11 failures before implementation. pass, Runtime/OV diagnostic targeted tests (32). pass, `uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/cli/test_studio_startup_imports.py` (1,057 tests, four dependency warnings, two workers). pass, Ruff 0.11.12 check/format and Pyright with `--pythonpath .venv/bin/python` on the two changed modules and new test (zero errors/warnings). pass, repository Gitleaks hook on 10 isolated in-scope files; no secrets. pass, paired identifiers/links and diff whitespace. Full-repository/all-files pre-commit not_run because no commit was requested; affected regression/static/secret gates cover this scope. Real cloud create/replay and service restart not_run; the user will restart and retry the original task. - -Follow-up: non-200 SDK Exception(bytes) did not expose structured attributes. The original attribute-only limitation is addressed in [bounded JSON decoding](2026-10-14-sdk-json-and-restore-ov.md); this document retains the initial design and verification history. diff --git a/prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-error-codes.zh.md b/prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-error-codes.zh.md deleted file mode 100644 index b478e9e33..000000000 --- a/prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-error-codes.zh.md +++ /dev/null @@ -1,32 +0,0 @@ -# 保留 MPA Runtime SDK 失败错误码 - -[English](2026-10-14-sdk-error-codes.md) - -变更 ID:mpa-runtime-diagnostics。日期:2026-10-14。状态:implemented;等待真实重试。 -组件契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-10。 - -## 证据、范围与批准 - -一次 BytePlus 无 OV 请求初始化业务库和 Skill Space,保存 pending 创建意图后失败,没有 Runtime ID。SDK 错误被简化为 provision/provider_error。AgentKit ApiError 保留 error_code,但 Runtime 适配器未记录。用户明确批准先补日志,再重试同一任务。review-spec 不可用,已直接评审有界安全元信息、旧诊断兼容和不变副作用,无阻塞项。 - -## 需求、设计与任务 - -- FR-1 / T-1:RuntimeCloud.call 使用现有诊断通道记录 create_runtime/update_runtime 失败。这也是启用发布的请求,当前创建链路未调用单独 release API。保留分类、经过验证的 SDK errorCode,以及 SDK 暴露时的 requestId。最多遍历八个异常/cause,拒绝循环,复用现有验证器。不解析异常文本,不发送请求体、凭证、鉴权头、云端 Message 或 ClientToken。 -- FR-2 / T-2:重新抛出原异常;成功、取消、请求序列化、提供商选择、超时、调用次数和 SDK 重试行为不变。不修改已保存输入或 pending 绑定/token。Skills/其他操作保留现有诊断行为。操作白名单仅增加两个名称,现有诊断持久化已支持这些字段。 -- FR-3 / T-3:先编写隔离失败测试,覆盖两个提供商、创建/更新、直接/包装/不安全错误码、缺少 Request ID、传输/取消、载荷不变和成功结果。运行 MPA/启动测试、Ruff/Pyright、密钥扫描及双语检查,同步 CON-10 和模块 README。前端/通用智能体和云端契约不变。 - -## 验收与风险 - -AC-1:模拟 Runtime SDK 拒绝通过持久化诊断通道保留 operation/category/errorCode 和可选 Request ID,不含云端文本或密钥。 -AC-2:保留原异常/载荷及单次调用,遍历有界且防循环,成功/取消不记录失败,不增加重试/清理副作用。 -AC-3:受影响检查通过并记录真实证据。实现期间不提交/推送、不重放真实接口、不重启服务、不写云资源。 - -历史异常无法还原。当前 AgentKit SDK 的部分 ApiError 不暴露结构化 Request ID,不能伪造。用户需加载更新后的后端后重试。原 ClientToken 语义和绑定校验继续生效。补日志不代表已修复尚未确认的真实云端原因。 - -## 验证 - -2026-10-14,基于 2ffe6e14 的本地差异,保留无关工作。not_run:等待实现检查。前端/浏览器/Harness/Codex 检查 not_applicable,因为行为不变。真实 Runtime 创建/重试 not_run,实现后由用户复现。 - -2026-10-14 验证:pass,首次隔离回归在实现前复现 11 个失败。pass,Runtime/OV 诊断专项 32 项。pass,`uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/cli/test_studio_startup_imports.py`(1,057 项,4 条依赖警告,两个 worker)。pass,两个修改模块及新测试通过 Ruff 0.11.12 检查/格式和指定 `--pythonpath .venv/bin/python` 的 Pyright(零错误/警告)。pass,10 个范围内文件的隔离副本通过仓库 Gitleaks hook,无密钥。pass,双语标识/链接及差异空白。全仓/全文件 pre-commit not_run,因为未要求提交,使用受影响回归/静态/密钥检查覆盖范围。真实云端创建/重放及服务重启 not_run,由用户重启后重试原任务。 - -后续:SDK 对非 200 响应抛出的 Exception(bytes) 没有结构化属性。最初仅读取属性的限制由[有界 JSON 解码](2026-10-14-sdk-json-and-restore-ov.zh.md)补齐;本文保留最初设计与验证历史。 diff --git a/prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-json-and-restore-ov.md b/prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-json-and-restore-ov.md deleted file mode 100644 index 2bacc4009..000000000 --- a/prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-json-and-restore-ov.md +++ /dev/null @@ -1,42 +0,0 @@ -# Decode Runtime SDK rejection metadata and restore BytePlus OV - -[中文版](2026-10-14-sdk-json-and-restore-ov.zh.md) - -Change ID: mpa-runtime-json-and-restore-ov. Date: 2026-10-14. Status: implemented; live cloud retry pending. -Predecessors: [Runtime diagnostics](2026-10-14-sdk-error-codes.md) and [temporary OV skip](../mpa-byteplus-without-openviking/2026-10-14-skip-new-requests.md). -Contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), CON-10/CON-17/CON-18. - -## Evidence, goals and non-goals - -Live CreateRuntime failure retained only provider_error. Installed SDK Service.json raises Exception(bytes) for non-200 responses; its wrapper retains the cause without structured attributes. Existing Runtime diagnostics inspect only attributes. The user now reports BytePlus OV allowlisting and requests restoring automatic OV. Authorization covers these two fixes, not real cloud creates, restarting Studio, commits or pushes. General agents, domestic payloads, IAM, billing, model defaults, resource bindings and uncertain-create protection are outside scope. - -## Requirements and scenarios - -- FR-1: Given a direct or wrapped SDK rejection, extract validated errorCode/requestId from attributes, HTTP/SDK structured bodies, or a single bounded exception argument containing JSON or its SDK bytes representation. Traverse at most eight causes with cycle detection. Limit argument/body parsing to 65,536 bytes/characters; reject malformed, oversized, deeply nested and non-JSON text. Never evaluate code or log provider Message, body, credentials or ClientToken. Reuse existing OV metadata validators/decoder. -- FR-2: Reraise the same exception and preserve dispatch count, cancellation, payload, token, classification and retry behavior. No cloud replay is added. -- FR-3: Built-in BytePlus ap-southeast-1 new requests again advertise automatic OV. Remove temporary skip-only UI/locales and normalization. New/editable drafts select auto without manual fields; submitted no-OV/auto/manual tasks retain original intent across configuration refresh and retry. Domestic auto and manual fallback for unsupported profiles remain unchanged. -- FR-4: Keep existing BytePlus personal developer library creation, endpoint/key injection and durable binding reuse. No existing tasks or cloud resources are rewritten. Old uncertain creates remain guarded; operator allowlisting does not prove model credentials or service readiness. - -## Design and tasks - -T-1 (FR-1/FR-2): Add failing tests in test_runtime_diagnostics.py for wrapped bytes/JSON/string representations, HTTP/SDK bodies, malformed/unsafe/oversized/cyclic cases, exception identity and one dispatch; extend Runtime helper only. -T-2 (FR-3/FR-4): Change profile regression to require auto and add submitted no-OV recovery tests. Restore config.py supported-provider defaults and remove only temporary skip portions in MpaCreateDialog.tsx/locales. Retain submittedIntent race protection and all unrelated BytePlus features. -T-3: Synchronize bilingual component/module documentation and supersede historical skip design. Run targeted and managed/startup Python regression, Ruff/Pyright, frontend tests/build/i18n/assets, real browser using isolated mocked APIs, scoped secret scan and paired-link/whitespace checks. Preserve unrelated work including package-lock changes. - -## Review, risks and acceptance - -User approval: explicit 2026-10-14 request to complete logs and restore OV. Direct design review (review-spec unavailable) found no blocker after bounding parsing, retaining original exception and immutable submitted intents. Existing dialog/Foundation styles are reused without new visual components. Referenced frontend-design/ui-ux-pro-max skills are unavailable; apply repository SPEC directly. - -AC-1: Safe Runtime identifiers survive real SDK-shaped simulated exceptions; forbidden values/text never enter diagnostics; original exception/token and single dispatch survive. -AC-2: New BytePlus requests contain auto; previously submitted no-OV and auto requests preserve their identity/mode on retry and config reload; domestic behavior is unchanged. -AC-3: Required checks pass with actual evidence. No real cloud mutation or service restart. Live user-driven retry remains separate. - -Parser fallback cannot reconstruct historical errors or identify transport failures with no provider response. Already submitted no-OV tasks cannot gain OV on retry; create a new request for OV testing. Actual BytePlus OV allowlisting/model usability and Runtime rejection cause remain unverified. - -## Verification - -2026-10-14, local diff based on 2ffe6e14. not_run: implementation gates pending. not_run: real cloud creation/retry, no cloud writes authorized for implementation. not_applicable: harness/Codex smoke, protocols unchanged. All-files pre-commit not_run: no commit requested; scoped static and secret checks required. - -2026-10-14 results (local diff on 2ffe6e14): pass, pre-fix regressions reproduced 11 failures; Runtime/BytePlus/OV diagnostic targeted suite passed 73 tests, final Runtime-only suite 30. pass, managed/startup regression with two workers 1,075 tests (four dependency warnings). pass, npm --prefix frontend test: Node suite and 101 React tests; production build, i18n (2 locales/21 namespaces), asset validation (113 files/350 references). pass, Ruff 0.11.12 check/format and Pyright with repository interpreter on four Python files (zero errors/warnings). pass, actual in-app Chromium dialog with mocked APIs: new overseas auto submission, original no-OV retry identity/mode, domestic auto presentation, 420x760 viewport and Escape. Browser fixture initially used the wrong i18n import and was corrected before validation; it and the isolated Vite server were removed. One initial frontend regression incorrectly expected an unavailable reload button; corrected to verify recovery on configuration arrival. Initial Pyright fixture attribute errors were corrected with a typed SDK-shaped exception. Scope secret and paired-document checks pending below. not_run, real cloud creates, restart, all-files pre-commit/commit/push; no mutations performed. No new dependency or package-lock change in this fix. - -Final review: pass, paired requirement/contract IDs, relative links and git diff whitespace. pass, repository-configured Gitleaks hook on an isolated 106-file snapshot. Initial scan omitted .gitleaks.toml and flagged a generated MarkdownPromptEditor vendor expression using anchor.key as generic-api-key; verified as code, then reran with the unchanged repository configuration. Existing repository allowlist excludes tests and built WebUI; source changes were scanned under that policy, not a newly added bypass. No source credential was found. Browser artifacts/server were removed; existing package-lock remains its pre-existing 512-line deletion. All in-scope acceptance checks complete; live provider behavior is not claimed as verified. diff --git a/prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-json-and-restore-ov.zh.md b/prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-json-and-restore-ov.zh.md deleted file mode 100644 index 72e383c3a..000000000 --- a/prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-json-and-restore-ov.zh.md +++ /dev/null @@ -1,42 +0,0 @@ -# 解码 Runtime SDK 拒绝元信息并恢复 BytePlus OV - -[English](2026-10-14-sdk-json-and-restore-ov.md) - -变更 ID:mpa-runtime-json-and-restore-ov。日期:2026-10-14。状态:implemented;真实云端重试待验证。 -前置:[Runtime 诊断](2026-10-14-sdk-error-codes.zh.md)和[临时跳过 OV](../mpa-byteplus-without-openviking/2026-10-14-skip-new-requests.zh.md)。 -契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-10/CON-17/CON-18。 - -## 证据、目标与非目标 - -实际 CreateRuntime 失败仍只留下 provider_error。已安装 SDK 的 Service.json 对非 200 响应抛出 Exception(bytes),外层包装保留 cause,但没有结构化属性;现有 Runtime 诊断只读取属性。用户现在说明 BytePlus OV 已开白,要求恢复自动 OV。本次授权包含这两项修复,不包含实际云端创建、重启 Studio、提交或推送。通用智能体、国内载荷、IAM、计费、模型默认值、资源绑定和不确定建库保护不在修改范围。 - -## 需求与场景 - -- FR-1:直接或包装后的 SDK 拒绝,从属性、HTTP/SDK 结构化响应体,或单个包含 JSON/SDK 字节串表示的有界异常参数提取已验证 errorCode/requestId。最多遍历八个 cause 并防循环;参数/响应体解析上限 65,536 字节/字符。拒绝格式错误、超长、过深及非 JSON 文本;不执行代码,不记录云端 Message、响应体、凭证或 ClientToken。复用 OV 元信息校验/解码方法。 -- FR-2:重新抛出同一异常,保留调用次数、取消、载荷、token、分类与重试行为,不增加云端重放。 -- FR-3:内置 BytePlus ap-southeast-1 新请求重新声明自动 OV。移除临时跳过专用 UI/文案和清空逻辑。新请求/可编辑草稿选择 auto 且无需手填;已提交的无 OV/自动/手动任务在配置刷新及重试时保留原意图。国内自动流程和不支持自动模式的配置所用手动路径不变。 -- FR-4:保留已有 BytePlus 个人版 developer 建库、端点/Key 注入及持久绑定复用。不改写已有任务或云资源。不确定建库继续受保护;账号开白不代表模型凭证及服务一定可用。 - -## 设计与任务 - -T-1(FR-1/FR-2):先在 test_runtime_diagnostics.py 增加失败测试,覆盖包装的 bytes/JSON/字符串表示、HTTP/SDK 响应体、格式错误/危险值/超长/循环、异常身份和单次调用,仅扩展 Runtime 提取方法。 -T-2(FR-3/FR-4):配置回归改为要求 auto,增加已提交无 OV 恢复测试。恢复 config.py 支持提供方的自动默认,仅删除 MpaCreateDialog.tsx/文案中的临时跳过部分,保留 submittedIntent 竞态保护和无关 BytePlus 能力。 -T-3:同步双语组件/模块文档,将历史跳过设计标记为被替代。执行目标及 managed/startup Python 回归、Ruff/Pyright、前端测试/构建/国际化/产物、隔离模拟接口的真实浏览器检查、范围内密钥扫描及双语链接/空白检查。保留包括 package-lock 在内的无关改动。 - -## 评审、风险与验收 - -用户批准:2026-10-14 明确要求补完整日志及恢复 OV。review-spec 不可用,直接设计评审在限制解析边界、保留原异常和已提交意图后无阻塞。复用现有弹窗/Foundation 样式,不新增视觉组件。引用的 frontend-design/ui-ux-pro-max 技能不可用,直接遵循仓库 SPEC。 - -AC-1:模拟真实 SDK 形状的异常保留安全 Runtime 标识;禁止的值/文本不进入诊断;原异常/token 和单次调用不变。 -AC-2:BytePlus 新请求包含 auto;已提交无 OV 和自动请求在重试/配置刷新时保留身份和模式;国内行为不变。 -AC-3:必需检查有实际通过证据,不执行云端变更或服务重启。真实重试由用户另行验证。 - -解析补充不能恢复历史错误,也不能解释无云端响应的传输错误。已提交无 OV 任务无法通过重试获得 OV,测试 OV 应新建请求。BytePlus 实际开白/模型可用性及 Runtime 拒绝原因仍未验证。 - -## 验证 - -2026-10-14,基于 2ffe6e14 的本地差异。not_run:实现检查待执行。not_run:真实云端创建/重试,本次实现不执行云端写操作。not_applicable:harness/Codex smoke,协议未变。全部文件 pre-commit 为 not_run:未要求提交,须执行范围内静态及密钥检查。 - -2026-10-14 结果(基于 2ffe6e14 的本地差异):pass,修复前回归复现 11 项失败;Runtime/BytePlus/OV 诊断目标套件 73 项通过,最终 Runtime 单独套件 30 项通过。pass,两个 worker 的 managed/startup 回归 1,075 项(四条依赖警告)。pass,npm --prefix frontend test:Node 套件和 101 项 React 测试;生产构建、国际化(2 种语言/21 个 namespace)、产物(113 个文件/350 个引用)。pass,四个 Python 文件的 Ruff 0.11.12 检查/格式及使用仓库解释器的 Pyright,零错误/警告。pass,内置 Chromium 中用模拟接口验证海外新请求自动提交、原无 OV 重试身份/模式、国内自动展示、420x760 窄窗口及 Escape。浏览器夹具起初使用错误 i18n 导入,验证前已修正;夹具和隔离 Vite 服务已移除。最初一项前端回归误用不存在的刷新按钮,已修正为验证配置加载后的恢复。最初 Pyright 夹具属性错误已通过有类型的 SDK 异常类修正。范围内密钥及双语文档检查待下方补充。not_run:真实云端创建、重启、全部文件 pre-commit/提交/推送,未执行变更。本次修复不增加依赖,也未改动 package-lock。 - -最终评审:pass,双语需求/契约 ID、相对链接及 git diff 空白检查。pass,在隔离的 106 文件快照上按仓库配置执行 Gitleaks hook。最初扫描遗漏 .gitleaks.toml,将生成的 MarkdownPromptEditor 第三方 anchor.key 代码表达式误报为 generic-api-key;核实为代码后,使用未修改的仓库配置重新检查。现有仓库白名单排除 tests 和编译 WebUI,本次按既有策略扫描源码,未新增绕过规则。未发现源码凭证。浏览器夹具/服务已移除;package-lock 仍保留原有 512 行删除。范围内验收完成,不宣称真实服务商行为已验证。 diff --git a/prd-spec/bugfixes/mpa-runtime-fresh-command/2026-09-29-set-fresh-runtime-command.md b/prd-spec/bugfixes/mpa-runtime-fresh-command/2026-09-29-set-fresh-runtime-command.md deleted file mode 100644 index d8d971b5c..000000000 --- a/prd-spec/bugfixes/mpa-runtime-fresh-command/2026-09-29-set-fresh-runtime-command.md +++ /dev/null @@ -1,50 +0,0 @@ -# Set the Fresh Managed Runtime Command - -[中文版](2026-09-29-set-fresh-runtime-command.zh.md) - -- **Change ID:** `mpa-runtime-fresh-command` -- **Date:** 2026-09-29 -- **Status:** implemented -- **Contract:** [MPA Runtime provisioning](../../../specs/mpa-runtime-provisioning/README.md) -- **Predecessor:** [Preserve the reference Runtime command](../../mpa-runtime-reference-command/2026-09-29-preserve-runtime-command.md) - -## Background and evidence - -After workload identity provisioning was fixed, a live Studio creation using -the built-in profile reached Runtime creation with the requested MPA image, -Worker Tool, and TOS environment, but the Runtime entered version 0 `Error`. -Its `Command` was empty. The earlier fix preserves a reference Runtime command, -but the built-in Studio profile uses `fresh_template()` and has no reference. -The selected MPA Runtime image is started by `bash run.sh`. - -## Goals and non-goals - -- Set `Command="bash run.sh"` on fresh managed MPA Runtime templates. -- Preserve explicit JSON-template and reference-Runtime command behavior. -- Do not make the command user-configurable or infer it from arbitrary images. - -## Requirements and design - -- **FR-1:** `fresh_template()` MUST emit the standard MPA command `bash run.sh`. -- **FR-2:** `managed.from-runtime` MUST continue preserving the reference command. -- **FR-3:** An explicit JSON template remains authoritative and is not modified unless the existing Runtime settings do so. - -The command is one fixed field in the existing MPA-only fresh template. No API, -schema, permission, persistence, or dependency change is required. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| FR-1–FR-3 | T-1 add a failing flat-source assertion and set the fresh-template field | AC-1 all managed source regressions pass | `uv run --extra dev pytest tests/integrations/mpa_managed/test_service.py tests/integrations/mpa_managed/test_agent_deployment.py` | -| FR-1 | T-2 retry live Studio creation | AC-2 the new Runtime progresses beyond version 0 Error with the command present | local Studio creation and Runtime inspection | - -## Risks, review, and delivery - -This is scoped to the MPA-only fresh template and the same command already used -by the known-good reference Runtime. Rollback removes the field. The design was -reviewed directly for scope, compatibility, security, and testability; the user -requested fixing the creation blockers and retrying a new Runtime. The targeted -60 tests and full 5259-test regression passed. Live Studio creation produced -Ready Runtime `r-yew4qlw5c017agjttpcw` at version 2 with -`Command="bash run.sh"`. diff --git a/prd-spec/bugfixes/mpa-runtime-fresh-command/2026-09-29-set-fresh-runtime-command.zh.md b/prd-spec/bugfixes/mpa-runtime-fresh-command/2026-09-29-set-fresh-runtime-command.zh.md deleted file mode 100644 index 80d43361e..000000000 --- a/prd-spec/bugfixes/mpa-runtime-fresh-command/2026-09-29-set-fresh-runtime-command.zh.md +++ /dev/null @@ -1,45 +0,0 @@ -# 设置全新托管 Runtime 启动命令 - -[English](2026-09-29-set-fresh-runtime-command.md) - -- **变更 ID:** `mpa-runtime-fresh-command` -- **日期:** 2026-09-29 -- **状态:** implemented -- **契约:** [MPA Runtime 部署](../../../specs/mpa-runtime-provisioning/README.zh.md) -- **前序设计:** [保留参考 Runtime 启动命令](../../mpa-runtime-reference-command/2026-09-29-preserve-runtime-command.zh.md) - -## 背景与证据 - -修复 Workload Identity 创建后,使用内置 profile 的真实 Studio 创建已经带上指定 MPA -镜像、Worker Tool 和 TOS 环境并进入 Runtime 创建,但 Runtime 在版本 0 进入 `Error`, -其 `Command` 为空。此前修复只保留参考 Runtime 的命令,而内置 Studio profile 使用 -`fresh_template()`,没有参考 Runtime。当前 MPA Runtime 镜像通过 `bash run.sh` 启动。 - -## 目标与非目标 - -- 为全新托管 MPA Runtime 模板设置 `Command="bash run.sh"`。 -- 保持显式 JSON 模板和参考 Runtime 的命令行为。 -- 不开放命令配置,也不从任意镜像推断命令。 - -## 需求与设计 - -- **FR-1:** `fresh_template()` 必须输出标准 MPA 命令 `bash run.sh`。 -- **FR-2:** `managed.from-runtime` 必须继续保留参考命令。 -- **FR-3:** 显式 JSON 模板保持权威,除现有 Runtime settings 外不做修改。 - -仅在现有 MPA 专用 fresh template 中增加一个固定字段。不修改 API、schema、权限、 -持久化或依赖。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| FR-1–FR-3 | T-1 增加先失败的 flat-source 断言并设置 fresh-template 字段 | AC-1 所有 managed 来源回归通过 | `uv run --extra dev pytest tests/integrations/mpa_managed/test_service.py tests/integrations/mpa_managed/test_agent_deployment.py` | -| FR-1 | T-2 重试真实 Studio 创建 | AC-2 新 Runtime 携带命令并越过版本 0 Error | 本地 Studio 创建与 Runtime 检查 | - -## 风险、审查与交付 - -改动只影响 MPA 专用 fresh template,命令与已知可用参考 Runtime 相同;回退时删除该字段。 -已直接审查范围、兼容性、安全与可测试性;用户要求修复创建阻塞并重建 Runtime。针对性 -60 条测试及 5259 条全量回归通过。真实 Studio 创建得到 Ready 的 Runtime -`r-yew4qlw5c017agjttpcw`,版本为 2,且 `Command="bash run.sh"`。 diff --git a/prd-spec/bugfixes/mpa-runtime-initial-tool/2026-09-29-runtime-initial-tool-attachment.md b/prd-spec/bugfixes/mpa-runtime-initial-tool/2026-09-29-runtime-initial-tool-attachment.md deleted file mode 100644 index 69dc0340f..000000000 --- a/prd-spec/bugfixes/mpa-runtime-initial-tool/2026-09-29-runtime-initial-tool-attachment.md +++ /dev/null @@ -1,90 +0,0 @@ -# Attach the worker Tool during initial Runtime creation - -[中文版](2026-09-29-runtime-initial-tool-attachment.zh.md) - -- **Change ID:** `mpa-runtime-initial-tool` -- **Date:** 2026-09-29 -- **Status:** implemented -- **Contract:** [MPA Runtime provisioning](../../../specs/mpa-runtime-provisioning/README.md) - -## Background and evidence - -Managed MPA deployment currently removes `ToolId` from the first `CreateRuntime` -request and attaches it in a later `UpdateRuntime`. A live comparison showed that -the control plane can move such a Runtime directly to version 0 `Error` before -network and worker resources are prepared, while a creation request containing -the ready worker Tool reaches `Ready`. The delayed attachment was introduced to -work around an `InvalidParameter.ToolId` observed while replacing the Tool on an -already failed Runtime; it does not establish that initial creation rejects the -Tool. - -## Goals and non-goals - -- Restore `ToolId` in the initial Runtime create payload when the desired - configuration contains it. -- Preserve the existing final update that publishes authoritative endpoint/key - environment values and converges all desired fields. -- Preserve pending-request hashing, client-token retry, worker creation, TOS - configuration, and existing Runtime update behavior. -- Do not change Tool provisioning, TOS credentials, session mounts, Runtime - images, or migrate existing failed Runtimes. - -## Scenarios and requirements - -- **FR-1:** Given a newly provisioned worker Tool, when a new managed Runtime is - created, `CreateRuntime` MUST include that Tool's `ToolId`. -- **FR-2:** Given the Runtime becomes platform-ready, finalization MUST continue - to publish endpoint/key environment values and converge the desired Tool. -- **FR-3:** Lost-response retries MUST retain the same desired payload and - `ClientToken` behavior. - -## Design and contract impact - -Remove the special copy-and-pop operation before `cloud.create`; pass the -already normalized `desired` payload directly. Keep `ToolId` in the convergent -update field set so existing Runtimes and finalization remain idempotent. This -changes the managed Runtime lifecycle contract, so the bilingual provisioning -spec is updated. Public Python signatures, persistence schemas, permissions, -credential handling, and compatibility inputs are unchanged. - -## Tasks, verification, and acceptance - -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| `FR-1` | `T-1` update regression assertions, then Runtime create payload | `AC-1` first create contains the worker `ToolId` | `uv run --extra dev pytest tests/integrations/mpa_managed/test_agent_deployment.py tests/integrations/mpa_managed/test_service.py` | -| `FR-2` | `T-2` preserve convergent update | `AC-2` final Runtime retains the same `ToolId` and finalized environment | same command | -| `FR-3` | `T-3` run managed deployment regressions | `AC-3` retry/idempotency tests remain green | `uv run --extra dev pytest tests/integrations/mpa_managed` | - -## Risks, recovery, and review - -The remaining risk is a control-plane variant that rejects a newly created Tool -at Runtime creation. The observed live path and the successful reference Runtime -support initial attachment; a failure remains visible rather than being reported -as partial success. Recovery is to revert this focused change. No secrets or -external mutations are part of automated verification. - -`review-spec` is unavailable. Direct review covered scope, lifecycle ordering, -retry/idempotency, security, compatibility, testability, and bilingual -equivalence; no blockers remain. The user's instruction to fix the diagnosed -regression approves this design. - -## Delivery record - -Implemented on 2026-09-29. `T-1` through `T-3` and `AC-1` through `AC-3` are -complete for the affected path. - -- `pass`: the two affected test files pass, 58 tests total. The regression - failed before the implementation with missing `ToolId` and passes after it. -- `pass`: a standard-library line trace over the regression test executed both - changed production lines, giving 100% incremental executable-line coverage. -- `pass`: changed-file pre-commit checks passed Ruff check/format and hardcoded - secret scanning; `git diff --check` passed. -- `fail` (unrelated environment issue): the whole `mpa_managed` suite under - coverage reports AgentKit SDK/Pydantic `AliasChoices` class-identity errors. - The same coverage instrumentation also fails the isolated regression before - reaching the changed path; ordinary targeted pytest passes. -- `fail` (unrelated existing formatting): all-files pre-commit reformats - `veadk/integrations/mpa/managed/tasks.py`, which is outside this change. That - generated edit was reverted and the affected-file hooks were rerun cleanly. -- `not_run`: live cloud creation; automated tests use isolated fake control - planes and perform no external mutations. diff --git a/prd-spec/bugfixes/mpa-runtime-initial-tool/2026-09-29-runtime-initial-tool-attachment.zh.md b/prd-spec/bugfixes/mpa-runtime-initial-tool/2026-09-29-runtime-initial-tool-attachment.zh.md deleted file mode 100644 index 336f5b90f..000000000 --- a/prd-spec/bugfixes/mpa-runtime-initial-tool/2026-09-29-runtime-initial-tool-attachment.zh.md +++ /dev/null @@ -1,75 +0,0 @@ -# Runtime 首次创建时挂载 Worker Tool - -[English](2026-09-29-runtime-initial-tool-attachment.md) - -- **变更 ID:** `mpa-runtime-initial-tool` -- **日期:** 2026-09-29 -- **状态:** implemented -- **契约:** [MPA Runtime 部署](../../../specs/mpa-runtime-provisioning/README.zh.md) - -## 背景与证据 - -当前托管 MPA 部署会从首次 `CreateRuntime` 请求中删除 `ToolId`,再通过后续 -`UpdateRuntime` 挂载。线上对比表明:这种 Runtime 可能在网络和 Worker 资源准备前 -直接进入版本 0 `Error`;首次创建请求携带已 Ready Worker Tool 的 Runtime 则能进入 -`Ready`。延迟挂载原本用于规避在一个已经失败的 Runtime 上替换 Tool 时出现的 -`InvalidParameter.ToolId`,该现象不能证明首次创建会拒绝 Tool。 - -## 目标与非目标 - -- 当期望配置包含 `ToolId` 时,在 Runtime 首次创建请求中恢复该字段。 -- 保留现有最终更新,用权威 endpoint/key 环境完成发布并收敛所有期望字段。 -- 保留未完成请求哈希、ClientToken 重试、Worker 创建、TOS 配置以及已有 Runtime - 更新行为。 -- 不修改 Tool 创建、TOS 凭据、会话挂载、Runtime 镜像,也不迁移已有失败 Runtime。 - -## 场景与需求 - -- **FR-1:** 已创建新的 Worker Tool 时,新建托管 Runtime 的 `CreateRuntime` 必须携带 - 该 Tool 的 `ToolId`。 -- **FR-2:** Runtime 达到平台 Ready 后,最终化仍必须发布 endpoint/key 环境并收敛 - 期望 Tool。 -- **FR-3:** 创建响应丢失后的重试必须保留相同期望请求和 `ClientToken` 行为。 - -## 设计与契约影响 - -删除调用 `cloud.create` 前复制并移除字段的特殊逻辑,直接传入已规范化的 `desired` -请求。收敛更新字段继续包含 `ToolId`,保证已有 Runtime 更新和最终化保持幂等。本次 -修改托管 Runtime 生命周期契约,因此同步更新双语部署规范。Python 公共签名、持久化 -schema、权限、凭据处理及兼容输入均不变。 - -## 任务、验证与验收 - -| 需求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| `FR-1` | `T-1` 先修改回归断言,再修改 Runtime 创建请求 | `AC-1` 首次创建携带 Worker `ToolId` | `uv run --extra dev pytest tests/integrations/mpa_managed/test_agent_deployment.py tests/integrations/mpa_managed/test_service.py` | -| `FR-2` | `T-2` 保留收敛更新 | `AC-2` 最终 Runtime 保留相同 `ToolId` 与最终环境 | 同上 | -| `FR-3` | `T-3` 运行托管部署回归 | `AC-3` 重试和幂等测试继续通过 | `uv run --extra dev pytest tests/integrations/mpa_managed` | - -## 风险、恢复与审查 - -剩余风险是部分控制面变体可能拒绝在 Runtime 创建时携带刚创建的 Tool。已观测线上 -路径和成功参考 Runtime 均支持首次挂载;失败会显式暴露,不会被报告为部分成功。 -恢复方式是回退本次聚焦修改。自动验证不包含密钥或外部写操作。 - -`review-spec` 不可用。已直接审查范围、生命周期顺序、重试/幂等、安全、兼容性、 -可测试性和双语一致性,没有阻塞项。用户要求修复已诊断回归,视为批准本设计。 - -## 交付记录 - -已于 2026-09-29 实施。受影响路径的 `T-1` 至 `T-3`、`AC-1` 至 `AC-3` -已完成。 - -- `pass`:两个受影响测试文件共 58 个测试通过。实现前回归因缺少 `ToolId` 失败, - 实现后通过。 -- `pass`:使用标准库行跟踪运行回归测试,本次两个生产代码变更行均被执行,增量 - 可执行行覆盖率为 100%。 -- `pass`:变更文件的 pre-commit Ruff 检查/格式化及硬编码密钥扫描通过; - `git diff --check` 通过。 -- `fail`(无关环境问题):在 coverage 下运行完整 `mpa_managed` 套件时,AgentKit - SDK/Pydantic 出现 `AliasChoices` 类身份不匹配。相同 coverage 插桩对单个回归也会在 - 到达变更路径前失败;普通目标 pytest 通过。 -- `fail`(无关存量格式):全文件 pre-commit 会格式化本次范围外的 - `veadk/integrations/mpa/managed/tasks.py`。已还原该自动改动,并对受影响文件重新运行 - hooks,结果通过。 -- `not_run`:真实云端创建;自动测试使用隔离的假控制面,不产生外部变更。 diff --git a/prd-spec/bugfixes/mpa-runtime-integration/2026-09-12-mpa-runtime-integration-hardening.md b/prd-spec/bugfixes/mpa-runtime-integration/2026-09-12-mpa-runtime-integration-hardening.md deleted file mode 100644 index 201348f69..000000000 --- a/prd-spec/bugfixes/mpa-runtime-integration/2026-09-12-mpa-runtime-integration-hardening.md +++ /dev/null @@ -1,160 +0,0 @@ -# MPA Runtime Integration Hardening and Studio Diagnostics - -- **Change ID:** `mpa-runtime-integration-hardening` -- **Status:** Approved for implementation -- **Created / revised:** 2026-09-12 -- **Change type:** Bugfix with user-visible diagnostics extensions -- **Chinese version:** [2026-09-12-mpa-runtime-integration-hardening.zh.md](2026-09-12-mpa-runtime-integration-hardening.zh.md) -- **Affected components:** [MPA Runtime Provisioning](../../../specs/mpa-runtime-provisioning/README.md), [Studio Runtime Diagnostics](../../../specs/studio-runtime-diagnostics/README.md) - -## 1. Background and evidence - -An mpa-agent created by `veadk mpa create` runs without ArkClaw control-plane resources. It deliberately uses the compatibility value `CLAW_SPACE_ID=csi-`, external PostgreSQL, and an external OpenViking service. Runtime logs nevertheless show `arkclaw:ListResources` returning `403 AccessDenied` every 120 seconds and once on normal A2A session startup. The concrete call sites are `McpToolsCache._load_candidate_resources()` and `SessionService._query_appcenter_mcp_ids()`. The failures are caught, so chat continues, but ArkClaw MCP discovery is unavailable and the logs are polluted by repeated tracebacks. - -The same live run proves four adjacent integration defects: - -1. The phase-two Runtime update writes `A2A_PUBLIC_URL` but not the real key-auth credential as `CODEX_MCP_RUNTIME_API_KEY`; the built-in MCP source therefore warns that worker calls may be rejected. -2. `IDENTITY_STARTUP_ENABLED=false` skips startup metadata loading, but lazy sandbox login remains enabled. A sandbox turn tries to build a login URL and logs `IdentityConfig.UserPoolConfig missing fields: user_pool_id`. -3. Sandbox progress uses `append=true` for the first artifact update. The A2A SDK rejects every chunk because the artifact does not yet exist: `Received append=True for nonexistent artifact index ... Ignoring chunk.` The worker completes, but progressive tool output is lost. -4. Runtime `r-yeuujrrcowb21078p9jh` reports `apmplus_enable=false`; Studio's `/web/runtime-trace` correctly returns 404 and cannot display spans. - -Two Studio gaps are also verified. The Runtime log service requests only 500 lines while the UI promises 1,000, and the dialog has no download action. A2A sandbox `usage.updated` events reach the bridge, but `A2AStreamDecoder` does not map them to Studio `usageMetadata`, so the existing token indicator cannot account for them. - -Per-conversation model selection is not currently supported. The primary agent resolves its model from the persisted agent record or Runtime environment, while Codex delegation accepts an independent `modelOverride`. Adding a model selector without a single request-scoped contract would therefore produce split-model turns. - -## 2. Goals - -- `FR-1`: VeADK-created runtimes must not call ArkClaw `ListResources` when ArkClaw resource discovery is not part of their provisioning model. Native ArkClaw deployments keep their current default behavior. -- `FR-2`: Phase-two provisioning must atomically publish the resolved public URL and Runtime key to the Runtime environment, without printing or persisting the key in source artifacts. -- `FR-3`: VeADK mode must not attempt user-pool login when no user pool was provisioned. -- `FR-4`: The first sandbox artifact update must create the artifact; subsequent updates may append to it, preserving event ordering and deduplication. -- `FR-5`: Runtime logs must remain live, display the latest 1,000 sanitized lines, and let the user download exactly the current sanitized snapshot. -- `FR-6`: Studio must reuse its existing token accounting UI for mpa-agent primary-model and delegated-worker usage, without counting cumulative snapshots repeatedly. -- `FR-7`: Studio may select an allowed model for one conversation turn. The override must apply consistently to the primary agent and delegated worker, must not expose credentials, and must fall back to the configured default when absent. -- `FR-8`: `veadk mpa create` must enable APMPlus tracing by default for AgentKit Runtime deployments, and Studio must retain its existing disabled, collecting, forbidden, ready, and error states. -- `FR-9`: Expected absence of optional control-plane configuration must be handled without full warning tracebacks; unexpected infrastructure failures must remain visible. - -## 3. Non-goals - -- Granting `arkclaw:ListResources` to synthetic compatibility spaces or creating ArkClaw user pools. -- Removing `CLAW_SPACE_ID`, changing PostgreSQL/OpenViking ownership, or changing APIG key-auth. -- Downloading an unbounded historical log archive; Runtime returns a bounded snapshot only. -- Sending model API keys to the browser or accepting arbitrary endpoint/provider credentials per request. -- Changing the behavior of native ArkClaw deployments. - -## 4. Scenarios - -1. **External-resource runtime:** Given a Runtime created by VeADK, when its cache refreshes or a session resolves MCP sources, then no AppCenter request is made and no `ListResources` warning is emitted. -2. **Built-in MCP:** Given a key-auth Runtime, when Codex calls the built-in MCP endpoint, then the phase-two Runtime key is used in the internal request header. -3. **No identity pool:** Given `IDENTITY_STARTUP_ENABLED=false`, when a turn delegates to sandbox, then no login URL is attempted and the sandbox proceeds with the configured shared credentials. -4. **Sandbox progress:** Given the first normalized worker event, when it is relayed through A2A, then the first artifact update uses `append=false`; later events use `append=true` and are visible to Studio. -5. **Logs:** Given a resolved Runtime instance, when logs refresh, then Studio replaces its bounded snapshot, retains at most 1,000 lines, and downloads the same text with a deterministic `.log` filename. -6. **Usage:** Given primary-model ADK usage or worker `usage.updated`, when Studio receives the event, then the existing indicator shows current and cumulative usage once per authoritative update. -7. **Model override:** Given an allowed model selected in Studio, when a message is sent, then the BFF places the model ID in A2A metadata and mpa-agent applies it request-locally to both the primary endpoint resolver and delegated worker. Invalid or unavailable IDs fail before model execution. -8. **Trace:** Given a newly provisioned Runtime and sufficient APMPlus read permission, when a completed session trace is opened, then Studio transitions from collecting to a span tree. Missing permission remains an explicit 403 rather than empty data. - -## 5. Design - -### 5.1 Provisioning mode and credentials - -Use existing narrow switches instead of introducing a broad deployment-mode enum: - -- Set `APPCENTER_RESOURCE_DISCOVERY_ENABLED=false` in the VeADK environment. Add this mpa-agent setting with a native-default value of `true`; it controls both the session fallback query and the AppCenter dependency supplied to `McpToolsCache`. `MCP_TOOLS_CACHE_ENABLED` remains enabled so explicitly cached/non-AppCenter sources can still benefit from it. -- Set `MPA_LAZY_LOGIN=false` together with `IDENTITY_STARTUP_ENABLED=false`. The identity-independent runtime must never enter the lazy user-pool login path. -- Set `APMPLUS_TRACE_CONTENT=false` by default; this retains span timing and structured details without exporting raw prompts or responses. -- Extend Runtime phase two to merge `A2A_PUBLIC_URL` and `CODEX_MCP_RUNTIME_API_KEY=` before `UpdateRuntime(ReleaseEnable=True)`. VeFaaS phase two receives the same pair. Add the Runtime key to the secret-redaction set. -- Pass `ApmplusEnable=true` on Runtime create and reuse/update. This is a Runtime control-plane field, not an environment variable. - -This is preferred over granting ArkClaw permissions (wrong resource model) or merely reducing log level (would retain futile calls and broken functionality). - -### 5.2 Expected optional-configuration failures - -mpa-agent dependency construction passes `appcenter_client=None` when resource discovery is disabled. `SessionService` and `McpToolsCache` already treat `None` as no discovery. The sandbox desensitization lookup treats `AgentNotFound` as the expected absence of an agent configuration and falls back at debug/info level; transport/database errors continue to log warnings with tracebacks. - -The compatibility 404s for `/list-apps` and `/web/agent-info/a2a-default` are Studio's legacy probe before its successful A2A agent-card fallback. They are not Runtime failures. This change does not add fake routes; follow-up logging may classify these known probe 404s below warning level without altering response semantics. - -### 5.3 A2A artifact ordering - -Maintain request-local state in `relay_codex_event`: the first emitted `sandbox-` artifact uses `append=false`; every later event uses `append=true`. Event IDs remain the deduplication key. The state changes only after enqueue succeeds, so a failed first enqueue cannot cause later events to append to a nonexistent artifact. - -### 5.4 Logs and download - -`RuntimeLogService.read_logs()` requests `Limit=1000`, sanitizes the response, and bounds it to its final 1,000 logical lines. SSE continues to replace, not concatenate, the snapshot every second. The dialog download button creates a UTF-8 text Blob from the current rendered snapshot, downloads it locally, and immediately revokes the object URL. It is disabled when no log text exists. No new cloud endpoint or broader authorization scope is introduced. - -### 5.5 Token usage projection - -The A2A bridge maps two sources into the existing ADK event fields: - -- A2A `metadata.adk_usage_metadata` becomes `usageMetadata` on the projected Studio event. -- Sandbox `usage.updated` maps worker fields (`inputTokens`, `outputTokens`, `totalTokens`, `cachedTokens`, `reasoningTokens`, `modelId`) into the existing camel-case ADK usage/model fields. - -The decoder tracks the last authoritative usage snapshot per `(source, requestId/model)` and emits only a positive delta to cumulative accounting. This prevents repeated cumulative A2A status snapshots from inflating totals. Existing `addTokenUsageFor()` and `TokenUsageIndicator` remain the single owner of display. - -### 5.6 Model selection - -The first implementation is an allowlisted request override, not Runtime mutation: - -- The mpa-agent agent card advertises model IDs from a new non-secret `MPA_SELECTABLE_MODELS` JSON/CSV environment value plus the configured default. -- Studio shows the selector only for the A2A virtual app and only when more than one model is advertised. Selection is session-local and disabled while a turn is running. -- `runSSE` sends `modelId` in `custom_metadata`; the A2A BFF forwards it as request metadata. -- mpa-agent validates the model ID, stores it in `InvocationContext`, and `ModelEndpointResolver` prefers it for that invocation while retaining configured provider/base URL/API key. Codex delegation receives the same `modelOverride.modelId`. -- Sessions and other channels keep the configured default unless their request explicitly carries an allowed override. No global environment update occurs. - -If the deployed model endpoint does not support multiple model IDs with the same credential/base URL, only the default is advertised and the selector is hidden. - -### 5.7 Trace observability - -Provisioning sets `ApmplusEnable=true` and waits for the released version. Existing `/web/runtime-trace` authorization and normalization remain unchanged. Verification distinguishes: - -- 404: Runtime tracing disabled (configuration defect); -- 425: trace still collecting (retryable); -- 403: Studio credentials lack `APMPlusServerReadOnlyAccess` (permission blocker); -- 200: normalized spans rendered by `TraceDrawer`. - -## 6. Security, compatibility, and failure semantics - -- All new switches default to current native mpa-agent behavior. Only `veadk mpa create` opts out of ArkClaw discovery/login. -- Runtime API keys remain server-side, are redacted in dry-run/output, and are never placed in agent-card metadata or browser payloads. -- Log downloads contain the same already-sanitized bounded snapshot visible in the UI. -- Model overrides are IDs only and must match the server-advertised allowlist. They cannot change provider, URL, or secret. -- Phase-two update failure is fatal because a partially configured Runtime cannot safely expose built-in MCP. The created resource ID remains in the error for recovery, without its key. -- APMPlus content tracing remains disabled unless explicitly overridden; enabling tracing does not authorize Studio to read it. - -## 7. Implementation tasks - -| ID | Work | Primary files | Depends on | -| --- | --- | --- | --- | -| `T-1` | Add discovery boundary and expected-absence handling in mpa-agent | `app/core/config.py`, `app/api/deps.py`, `app/integrations/agentkit_sandbox.py` | none | -| `T-2` | Correct first A2A artifact append semantics | `app/a2a/executor.py`, `tests/test_a2a_executor.py` | none | -| `T-3` | Inject VeADK mode env/key and enable Runtime APMPlus | `mpa_provision.py`, `mpa_runtime.py`, `cli_mpa.py` | `T-1` | -| `T-4` | Align 1,000-line snapshot and add download | `runtime_logs.py`, `RuntimeLogsDialog.tsx`, i18n/tests | none | -| `T-5` | Project authoritative token usage | `runtime_a2a_stream.py`, token projection tests | `T-2` | -| `T-6` | Add allowlisted request-scoped model selection | mpa-agent invocation/model/A2A files; Studio BFF/client/composer | `T-3` | -| `T-7` | Validate live trace and end-to-end behavior | Runtime/Studio live checks | `T-1`–`T-6` | - -## 8. Tests and acceptance - -| Requirement | Acceptance criterion | Verification | Initial result | -| --- | --- | --- | --- | -| `FR-1`, `FR-3` | No AppCenter/user-pool call in VeADK mode; native defaults preserved | mpa-agent config/dependency/session tests and live log observation over two refresh intervals | Unit tests `pass`; live new-image verification `not_run` | -| `FR-2` | Phase-two request contains URL and key; no output leaks key | `tests/integrations/test_mpa_runtime.py`, `test_mpa_provision_env.py`, CLI tests | `pass` | -| `FR-4` | First artifact creates, later artifacts append, all arrive | `tests/test_a2a_executor.py` plus real sandbox turn | Unit tests `pass`; live new-image verification `not_run` | -| `FR-5` | 1,000-line bounded live view and matching download | Runtime log backend/frontend tests plus browser check | Automated tests/build `pass`; browser check `blocked` by missing Playwright browser | -| `FR-6` | Primary and worker usage display once, reload remains consistent | decoder/token tests plus real Runtime turn | Automated tests `pass`; live new-image verification `not_run` | -| `FR-7` | Allowed override reaches both paths; invalid ID rejected; default unchanged | mpa-agent unit/integration tests and Studio browser test | Automated tests `pass`; live new-image verification `not_run` | -| `FR-8` | New/update Runtime has APMPlus enabled and TraceDrawer reaches 200 or reports an external 403 permission blocker | Runtime tests and live `/web/runtime-trace` | Runtime request tests `pass`; current version 31 remains `fail` (verified 404 disabled); no deployment performed | - -Required repository checks include targeted Python tests, `npm --prefix frontend test`, `npm --prefix frontend run build`, changed-file Ruff/Pyright, the applicable broader Python regression, browser verification, and pre-commit. Real cloud checks are reported separately from simulated tests. - -## 9. Review record - -Design review found and resolved these blockers before implementation: - -- **Over-broad mode flag:** replaced by the narrow `APPCENTER_RESOURCE_DISCOVERY_ENABLED` switch, preserving independent caches and native defaults. -- **Secret exposure risk:** model selection carries only IDs; phase-two keys remain server-side and join the redaction set. -- **Token double counting:** defined authoritative snapshot/delta normalization instead of forwarding cumulative values blindly. -- **Trace/privacy ambiguity:** separated `ApmplusEnable=true` from `APMPLUS_TRACE_CONTENT=false`. -- **Artifact race:** first-artifact state advances only after successful enqueue. -- **Unbounded log download:** download is exactly the sanitized 1,000-line current snapshot. - -No unresolved design blocker remains. The user's explicit request to analyze and fix the reported exceptions and add the listed diagnostics is recorded as implementation approval for this scoped design. Any broader auth, control-plane, or model-credential change requires a new review. diff --git a/prd-spec/bugfixes/mpa-runtime-integration/2026-09-12-mpa-runtime-integration-hardening.zh.md b/prd-spec/bugfixes/mpa-runtime-integration/2026-09-12-mpa-runtime-integration-hardening.zh.md deleted file mode 100644 index df039e8ca..000000000 --- a/prd-spec/bugfixes/mpa-runtime-integration/2026-09-12-mpa-runtime-integration-hardening.zh.md +++ /dev/null @@ -1,160 +0,0 @@ -# MPA Runtime 集成加固与 Studio 诊断能力 - -- **Change ID:** `mpa-runtime-integration-hardening` -- **状态:** 已批准实施 -- **创建 / 修订日期:** 2026-09-12 -- **变更类型:** Bug 修复及用户可见诊断扩展 -- **English version:** [2026-09-12-mpa-runtime-integration-hardening.md](2026-09-12-mpa-runtime-integration-hardening.md) -- **受影响组件:** [MPA Runtime Provisioning](../../../specs/mpa-runtime-provisioning/README.zh.md)、[Studio Runtime Diagnostics](../../../specs/studio-runtime-diagnostics/README.zh.md) - -## 1. 背景与证据 - -`veadk mpa create` 创建的 mpa-agent 不依赖 ArkClaw 控制面资源,而是明确使用兼容值 `CLAW_SPACE_ID=csi-`、外部 PostgreSQL 和外部 OpenViking。Runtime 日志仍显示 `arkclaw:ListResources` 每 120 秒返回一次 `403 AccessDenied`,普通 A2A Session 启动时也会调用一次。已确认调用点为 `McpToolsCache._load_candidate_resources()` 和 `SessionService._query_appcenter_mcp_ids()`。异常被捕获后聊天仍能继续,但 ArkClaw MCP 发现不可用,且完整堆栈持续污染日志。 - -同一次真实运行还证明了四个相邻的集成缺陷: - -1. Runtime 第二阶段更新只写入 `A2A_PUBLIC_URL`,没有把真实 key-auth 凭据写为 `CODEX_MCP_RUNTIME_API_KEY`,因此内置 MCP 源警告 worker 调用可能被拒绝。 -2. `IDENTITY_STARTUP_ENABLED=false` 跳过了启动期身份元数据加载,但 lazy sandbox login 仍然开启。Sandbox 回合尝试生成登录链接并记录 `IdentityConfig.UserPoolConfig missing fields: user_pool_id`。 -3. Sandbox 进度的首个 artifact update 使用 `append=true`。A2A SDK 因 artifact 尚不存在而拒绝每个 chunk:`Received append=True for nonexistent artifact index ... Ignoring chunk.` Worker 最终完成,但渐进工具输出丢失。 -4. Runtime `r-yeuujrrcowb21078p9jh` 返回 `apmplus_enable=false`;Studio 的 `/web/runtime-trace` 正确返回 404,无法展示 Span。 - -另有两个 Studio 缺口已经验证。Runtime 日志服务只请求 500 行,而 UI 承诺 1,000 行,且弹窗没有下载入口。A2A sandbox 的 `usage.updated` 已到达桥接层,但 `A2AStreamDecoder` 未将其映射为 Studio `usageMetadata`,现有 Token 指示器因此无法统计。 - -当前也不支持按会话选择模型。主 Agent 从持久化 Agent 记录或 Runtime 环境解析模型,而 Codex 委托使用独立的 `modelOverride`。若没有统一的请求级契约就增加选择器,会导致同一回合使用两个不同模型。 - -## 2. 目标 - -- `FR-1`:VeADK 创建的 Runtime 未采用 ArkClaw 资源发现模型时,不得调用 ArkClaw `ListResources`;原生 ArkClaw 部署保持当前默认行为。 -- `FR-2`:第二阶段部署必须把已解析的公网 URL 和 Runtime key 原子发布到 Runtime 环境,且不得在输出或源码产物中泄露 key。 -- `FR-3`:VeADK 模式没有用户池时,不得尝试用户池登录。 -- `FR-4`:首个 sandbox artifact update 必须创建 artifact;后续更新才可 append,并保持事件顺序和去重。 -- `FR-5`:Runtime 日志保持实时刷新,展示最近 1,000 行脱敏日志,并允许用户下载当前同一份脱敏快照。 -- `FR-6`:Studio 必须复用现有 Token 统计 UI 展示 mpa-agent 主模型和委托 worker 用量,不得重复累计快照。 -- `FR-7`:Studio 可为单次对话回合选择允许的模型。覆盖值必须一致作用于主 Agent 与委托 worker,不暴露凭据;未设置时回退到配置默认值。 -- `FR-8`:`veadk mpa create` 默认开启 AgentKit Runtime 的 APMPlus 链路观测;Studio 保留现有未开启、采集中、无权限、就绪和失败状态。 -- `FR-9`:可选控制面配置缺失应避免输出完整 warning 堆栈;非预期基础设施故障仍须可见。 - -## 3. 非目标 - -- 给合成兼容 Space 授予 `arkclaw:ListResources`,或创建 ArkClaw 用户池。 -- 删除 `CLAW_SPACE_ID`、改变 PostgreSQL/OpenViking 归属或改变 APIG key-auth。 -- 下载无限历史日志;Runtime 只提供有界快照。 -- 把模型 API Key 发送给浏览器,或允许请求传入任意 Endpoint/Provider 凭据。 -- 改变原生 ArkClaw 部署行为。 - -## 4. 场景 - -1. **外部资源 Runtime:** 给定 VeADK 创建的 Runtime,当缓存刷新或 Session 解析 MCP 源时,不发送 AppCenter 请求,也不记录 `ListResources` 警告。 -2. **内置 MCP:** 给定 key-auth Runtime,当 Codex 调用内置 MCP Endpoint 时,内部请求头使用第二阶段写入的 Runtime key。 -3. **无身份池:** 给定 `IDENTITY_STARTUP_ENABLED=false`,当回合委托给 sandbox 时,不尝试登录 URL,并使用配置的共享凭据继续执行。 -4. **Sandbox 进度:** 给定首个规范化 worker 事件,当通过 A2A 中继时,首个 artifact update 使用 `append=false`;后续使用 `append=true` 且均可被 Studio 展示。 -5. **日志:** 给定已解析的 Runtime 实例,当日志刷新时,Studio 替换有界快照、最多保留 1,000 行,并以确定的 `.log` 文件名下载相同文本。 -6. **用量:** 给定主模型 ADK 用量或 worker `usage.updated`,当 Studio 收到事件时,现有指示器只对每次权威更新统计一次当前值和累计值。 -7. **模型覆盖:** 给定 Studio 选择了允许模型,当发送消息时,BFF 将模型 ID 放入 A2A metadata,mpa-agent 按请求同时应用于主模型解析和委托 worker;非法或不可用 ID 在模型执行前失败。 -8. **链路:** 给定新部署 Runtime 且具备 APMPlus 读取权限,当打开已完成 Session 的链路时,Studio 从采集中进入 Span 树;缺少权限仍明确返回 403,而不是伪装为空数据。 - -## 5. 设计 - -### 5.1 部署模式与凭据 - -复用窄粒度开关,不引入宽泛的部署模式枚举: - -- VeADK 环境设置 `APPCENTER_RESOURCE_DISCOVERY_ENABLED=false`。mpa-agent 新增该配置,原生默认值为 `true`;它同时控制 Session fallback 查询以及传给 `McpToolsCache` 的 AppCenter 依赖。`MCP_TOOLS_CACHE_ENABLED` 继续开启,使显式缓存或非 AppCenter 来源仍可受益。 -- 在 `IDENTITY_STARTUP_ENABLED=false` 的同时设置 `MPA_LAZY_LOGIN=false`,避免不带用户池的 Runtime 进入 lazy login 路径。 -- 默认设置 `APMPLUS_TRACE_CONTENT=false`,保留 Span 时序和结构化详情,但不导出原始 Prompt/Response。 -- Runtime 第二阶段合并 `A2A_PUBLIC_URL` 和 `CODEX_MCP_RUNTIME_API_KEY=` 后调用 `UpdateRuntime(ReleaseEnable=True)`;VeFaaS 第二阶段写入相同键值。Runtime key 加入 secret redaction 集合。 -- Runtime 创建及复用更新时传入 `ApmplusEnable=true`。这是 Runtime 控制面字段,不是环境变量。 - -相比授予 ArkClaw 权限(资源模型错误)或仅降低日志级别(仍会进行无效调用并保留功能缺口),该方案解决根因。 - -### 5.2 可选配置缺失 - -资源发现关闭时,mpa-agent 依赖构造向下传递 `appcenter_client=None`。`SessionService` 与 `McpToolsCache` 已将 `None` 视为不发现。Sandbox 脱敏配置查询将 `AgentNotFound` 视为 Agent 配置缺失的预期情况,以 debug/info 级回退;传输或数据库错误仍保留 warning 堆栈。 - -`/list-apps` 与 `/web/agent-info/a2a-default` 的兼容性 404 是 Studio 在成功回退 A2A agent-card 前进行的旧协议探测,不是 Runtime 故障。本次不增加伪造路由;后续日志分类可降低这些已知探测 404 的告警等级,但不改变响应语义。 - -### 5.3 A2A artifact 顺序 - -`relay_codex_event` 保存请求级状态:首个 `sandbox-` artifact 使用 `append=false`,后续事件使用 `append=true`。事件 ID 继续作为去重键。只有 enqueue 成功后才推进状态,避免首个 enqueue 失败后后续事件追加到不存在的 artifact。 - -### 5.4 日志与下载 - -`RuntimeLogService.read_logs()` 请求 `Limit=1000`,脱敏后限制为最后 1,000 个逻辑行。SSE 继续每秒替换快照而非拼接。弹窗下载按钮从当前渲染快照创建 UTF-8 Blob,本地下载后立即回收 Object URL;无日志时禁用。不新增云端接口,也不扩大授权范围。 - -### 5.5 Token 用量投影 - -A2A 桥接层把两类来源映射到现有 ADK 事件字段: - -- A2A `metadata.adk_usage_metadata` 映射为投影事件的 `usageMetadata`。 -- Sandbox `usage.updated` 将 worker 字段(`inputTokens`、`outputTokens`、`totalTokens`、`cachedTokens`、`reasoningTokens`、`modelId`)映射为现有 camel-case ADK usage/model 字段。 - -Decoder 按 `(source, requestId/model)` 记录最后一次权威用量快照,只向累计统计发出正向增量,避免重复的 A2A 累计状态快照放大总量。现有 `addTokenUsageFor()` 和 `TokenUsageIndicator` 继续作为唯一展示所有者。 - -### 5.6 模型选择 - -首版采用 allowlist 请求级覆盖,而非修改 Runtime: - -- mpa-agent agent card 通过新的非敏感 `MPA_SELECTABLE_MODELS` JSON/CSV 环境值,加上配置默认模型,发布模型 ID。 -- Studio 仅在 A2A 虚拟 App 且广告模型多于一个时展示选择器。选择保存在 Session 本地,回合运行时禁用。 -- `runSSE` 在 `custom_metadata` 中发送 `modelId`;A2A BFF 作为请求 metadata 转发。 -- mpa-agent 校验模型 ID、写入 `InvocationContext`;`ModelEndpointResolver` 在当前请求优先使用它,同时保留配置的 provider/base URL/API key。Codex 委托接收同一个 `modelOverride.modelId`。 -- Session 及其他渠道没有显式允许覆盖时继续使用配置默认值,不进行全局环境更新。 - -如果已部署模型 Endpoint 不能用相同凭据和 Base URL 支持多个模型 ID,则只广告默认模型并隐藏选择器。 - -### 5.7 链路观测 - -部署传入 `ApmplusEnable=true` 并等待新版本 Ready。现有 `/web/runtime-trace` 授权与规范化逻辑保持不变。验证区分: - -- 404:Runtime 未开启链路(配置缺陷); -- 425:链路仍在采集(可重试); -- 403:Studio 凭据缺少 `APMPlusServerReadOnlyAccess`(权限阻塞); -- 200:`TraceDrawer` 展示规范化 Span。 - -## 6. 安全、兼容与失败语义 - -- 所有新开关默认保持原生 mpa-agent 行为,仅 `veadk mpa create` 退出 ArkClaw 发现和登录。 -- Runtime API Key 仅位于服务端,在 dry-run/输出中脱敏,不写入 agent-card metadata 或浏览器 Payload。 -- 日志下载内容与 UI 当前可见的已脱敏 1,000 行快照完全相同。 -- 模型覆盖只包含 ID 且必须匹配服务端广告 allowlist,不能改变 Provider、URL 或 Secret。 -- 第二阶段更新失败为致命错误,因为部分配置的 Runtime 无法安全暴露内置 MCP。错误保留已创建资源 ID 供恢复,但不包含 Key。 -- 除非显式覆盖,APMPlus 内容追踪保持关闭;开启链路不等同于授权 Studio 读取。 - -## 7. 实施任务 - -| ID | 工作 | 主要文件 | 依赖 | -| --- | --- | --- | --- | -| `T-1` | 在 mpa-agent 增加发现边界和预期缺失处理 | `app/core/config.py`、`app/api/deps.py`、`app/integrations/agentkit_sandbox.py` | 无 | -| `T-2` | 修正首个 A2A artifact append 语义 | `app/a2a/executor.py`、`tests/test_a2a_executor.py` | 无 | -| `T-3` | 注入 VeADK 模式环境/key 并开启 Runtime APMPlus | `mpa_provision.py`、`mpa_runtime.py`、`cli_mpa.py` | `T-1` | -| `T-4` | 对齐 1,000 行快照并增加下载 | `runtime_logs.py`、`RuntimeLogsDialog.tsx`、i18n/测试 | 无 | -| `T-5` | 投影权威 Token 用量 | `runtime_a2a_stream.py`、Token 投影测试 | `T-2` | -| `T-6` | 增加 allowlist 请求级模型选择 | mpa-agent invocation/model/A2A 文件;Studio BFF/client/composer | `T-3` | -| `T-7` | 验证真实链路和端到端行为 | Runtime/Studio 真实检查 | `T-1`–`T-6` | - -## 8. 测试与验收 - -| 需求 | 验收标准 | 验证方式 | 初始结果 | -| --- | --- | --- | --- | -| `FR-1`、`FR-3` | VeADK 模式不调用 AppCenter/用户池;原生默认保持 | mpa-agent 配置/依赖/Session 测试及跨两个刷新周期的真实日志 | 单元测试 `pass`;新镜像真实验证 `not_run` | -| `FR-2` | 第二阶段请求包含 URL 与 key;输出不泄漏 key | `tests/integrations/test_mpa_runtime.py`、`test_mpa_provision_env.py`、CLI 测试 | `pass` | -| `FR-4` | 首个 artifact 创建,后续 append,全部到达 | `tests/test_a2a_executor.py` 及真实 sandbox 回合 | 单元测试 `pass`;新镜像真实验证 `not_run` | -| `FR-5` | 1,000 行有界实时视图与一致下载 | Runtime 日志后端/前端测试及浏览器检查 | 自动测试/构建 `pass`;浏览器检查因缺少 Playwright 浏览器而 `blocked` | -| `FR-6` | 主模型和 worker 用量只显示一次,刷新后保持一致 | Decoder/Token 测试及真实 Runtime 回合 | 自动测试 `pass`;新镜像真实验证 `not_run` | -| `FR-7` | 允许覆盖到达两条路径;非法 ID 被拒绝;默认不变 | mpa-agent 单元/集成测试及 Studio 浏览器测试 | 自动测试 `pass`;新镜像真实验证 `not_run` | -| `FR-8` | 新建/更新 Runtime 开启 APMPlus,TraceDrawer 获得 200 或报告外部 403 权限阻塞 | Runtime 测试及真实 `/web/runtime-trace` | Runtime 请求测试 `pass`;当前版本 31 仍为 `fail`(已验证 404 未开启);未执行部署 | - -仓库必需检查包括目标 Python 测试、`npm --prefix frontend test`、`npm --prefix frontend run build`、变更 Python 文件 Ruff/Pyright、适用的更广 Python 回归、浏览器验证及 pre-commit。真实云验证与模拟测试分开记录。 - -## 9. 评审记录 - -方案评审在实现前发现并解决以下阻塞项: - -- **模式开关过宽:** 改为窄粒度 `APPCENTER_RESOURCE_DISCOVERY_ENABLED`,保留独立缓存及原生默认值。 -- **Secret 泄露风险:** 模型选择只携带 ID;第二阶段 Key 保持服务端且加入脱敏集合。 -- **Token 重复统计:** 定义权威快照/增量规范化,而不是直接转发累计值。 -- **链路与隐私歧义:** 将 `ApmplusEnable=true` 与 `APMPLUS_TRACE_CONTENT=false` 分离。 -- **Artifact 竞态:** 仅在首次 enqueue 成功后推进 artifact 状态。 -- **日志下载无界:** 下载内容严格等于当前已脱敏 1,000 行快照。 - -当前无未解决的设计阻塞项。用户明确要求分析并修复已报告异常,同时增加列出的诊断能力,记录为本范围的实施批准。任何更广的鉴权、控制面或模型凭据变更均需重新评审。 diff --git a/prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-dev-loop-execution.md b/prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-dev-loop-execution.md deleted file mode 100644 index c493975ab..000000000 --- a/prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-dev-loop-execution.md +++ /dev/null @@ -1,40 +0,0 @@ -# Dev Loop Execution Record - -- **Change ID:** `mpa-runtime-integration-hardening` -- **Status:** Live E2E passed; awaiting compliant commits -- **Date:** 2026-09-13 -- **Chinese version:** [2026-09-13-dev-loop-execution.zh.md](2026-09-13-dev-loop-execution.zh.md) - -## Functional validation execution - -| Case | Actual execution | Result | -| --- | --- | --- | -| `VC-1` | mpa-agent dependency/config tests | Automated and live Runtime v45 verification pass. | -| `VC-2` | VeADK provisioning/Runtime/CLI tests and secret scan | Pass. | -| `VC-3` | mpa-agent A2A executor tests | Pass; live Runtime verification pass. | -| `VC-4` | Runtime log backend tests, frontend tests, build | Pass; browser blocked because Playwright Chromium is not installed. | -| `VC-5` | A2A decoder usage replay/delta tests and frontend Token tests | Pass; live Runtime verification pass. | -| `VC-6` | mpa-agent model/card/delegation tests, BFF tests, frontend capability checks | Pass; live Runtime verification pass. | -| `VC-7` | Runtime request tests plus live metadata/trace endpoint inspection | Request and live checks pass. Runtime v45 has `apmplus_enable=true`; Studio trace returns HTTP 200 with 88 spans. | -| `VC-8` | Targeted suites, frontend full tests/build, pre-commit, default Python regression | Targeted and frontend pass. Default regression: 4,025 pass, 6 fail, 2 error because optional `anthropic`/`llama_index` dependencies are absent. | - -## Review rounds - -### Round 1 - -- Fixed AppCenter cache construction still being possible when discovery was disabled. -- Fixed model-generated `sandbox_task.modelOverride` being able to override the validated request model. -- Fixed stale new-session model-selection state leaking into a later new session. -- Added explicit APMPlus configurability to the Runtime helper while keeping `veadk mpa create` default enabled. - -### Round 2 - -- Rechecked empty/default model catalogs, invalid model IDs, replayed usage, first-artifact enqueue failure, Blob URL cleanup, key redaction, old-runtime compatibility, and Runtime trace status semantics. -- No remaining P0/P1 code finding. - -## Gates - -- Local implementation and review: pass. -- Live E2E: pass; Runtime `r-yeuujrrcowb21078p9jh` v45 is Ready and live Session/model/MCP/trace paths were verified. -- Commit/push: not executed yet; final synchronization and repository gates remain. -- Tag: not applicable; no package release was authorized. diff --git a/prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-dev-loop-execution.zh.md b/prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-dev-loop-execution.zh.md deleted file mode 100644 index d6f361efd..000000000 --- a/prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-dev-loop-execution.zh.md +++ /dev/null @@ -1,40 +0,0 @@ -# Dev Loop 执行记录 - -- **Change ID:** `mpa-runtime-integration-hardening` -- **状态:** 真实 E2E 已通过;等待合规提交 -- **日期:** 2026-09-13 -- **English version:** [2026-09-13-dev-loop-execution.md](2026-09-13-dev-loop-execution.md) - -## 功能验证执行 - -| Case | 实际执行 | 结果 | -| --- | --- | --- | -| `VC-1` | mpa-agent 依赖/配置测试 | 自动与真实 Runtime v45 验证通过。 | -| `VC-2` | VeADK 部署/Runtime/CLI 测试及 Secret scan | 通过。 | -| `VC-3` | mpa-agent A2A executor 测试 | 通过;真实 Runtime 验证通过。 | -| `VC-4` | Runtime 日志后端测试、前端测试、构建 | 通过;浏览器因未安装 Playwright Chromium 而阻塞。 | -| `VC-5` | A2A Decoder 用量重放/增量测试及前端 Token 测试 | 通过;真实 Runtime 验证通过。 | -| `VC-6` | mpa-agent 模型/card/委托测试、BFF 测试、前端能力检查 | 通过;真实 Runtime 验证通过。 | -| `VC-7` | Runtime 请求测试及真实 metadata/trace Endpoint 检查 | 请求和真实验证通过。Runtime v45 的 `apmplus_enable=true`,Studio Trace 返回 HTTP 200 和 88 个 Span。 | -| `VC-8` | 目标测试、前端全量测试/构建、pre-commit、默认 Python 回归 | 目标和前端通过。默认回归:4,025 通过,6 失败,2 error,原因为缺少可选 `anthropic`/`llama_index` 依赖。 | - -## 两轮评审 - -### 第一轮 - -- 修复关闭资源发现后仍可能构造 AppCenter cache。 -- 修复模型生成的 `sandbox_task.modelOverride` 可以覆盖已校验请求模型。 -- 修复新 Session 的模型选择状态可能泄漏到下一个新 Session。 -- 为 Runtime helper 增加显式 APMPlus 可配置能力,同时保持 `veadk mpa create` 默认开启。 - -### 第二轮 - -- 复查空/默认模型目录、非法模型 ID、用量重放、首次 artifact enqueue 失败、Blob URL 清理、Key 脱敏、旧 Runtime 兼容及 Trace 状态语义。 -- 当前无剩余 P0/P1 代码问题。 - -## 门禁 - -- 本地实现与评审:通过。 -- 真实 E2E:通过;Runtime `r-yeuujrrcowb21078p9jh` v45 Ready,真实 Session、模型切换、MCP、Trace 均已验证。 -- Commit/push:尚未执行,等待最终同步和仓库门禁。 -- Tag:不适用;未授权包发布。 diff --git a/prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-dev-loop-review.md b/prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-dev-loop-review.md deleted file mode 100644 index 7bb144e1c..000000000 --- a/prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-dev-loop-review.md +++ /dev/null @@ -1,31 +0,0 @@ -# Dev Loop Spec Review: MPA Runtime Integration Hardening - -- **Change ID:** `mpa-runtime-integration-hardening` -- **Status:** Approved -- **Date:** 2026-09-13 -- **Chinese version:** [2026-09-13-dev-loop-review.zh.md](2026-09-13-dev-loop-review.zh.md) -- **Reviewed design:** [2026-09-12-mpa-runtime-integration-hardening.md](2026-09-12-mpa-runtime-integration-hardening.md) - -## Review conclusion - -The design is implementable and aligned with the current VeADK/mpa-agent boundaries. No P0/P1 blocker remains. The review covered context consistency, ambiguity, SDD/TDD fit, minimal implementation, compatibility, existing deployments, failure recovery, security, observability, and extensibility. - -## Findings and resolutions - -| Priority | Finding | Resolution | -| --- | --- | --- | -| P1 | A broad VeADK mode flag could disable unrelated caches. | Use the narrow `APPCENTER_RESOURCE_DISCOVERY_ENABLED` switch and keep native default `true`. | -| P1 | A model-generated `sandbox_task.modelOverride` could bypass the Studio allowlist. | A validated request-scoped model always overrides the tool argument. | -| P1 | The first A2A progress update used `append=true` before the artifact existed. | First successful enqueue creates the artifact; only subsequent events append. | -| P1 | Runtime key injection could leak through dry-run output. | Treat `CODEX_MCP_RUNTIME_API_KEY` as a secret and inject it only server-side during phase two. | -| P2 | Replayed usage snapshots could inflate session totals. | Track source/request snapshots and emit only positive deltas. | -| P2 | Enabling APMPlus could unintentionally expose prompt content. | Enable Runtime tracing while retaining `APMPLUS_TRACE_CONTENT=false`. | -| P2 | Log download could bypass the display bound or sanitization. | Download exactly the server-sanitized current 1,000-line snapshot. | -| P3 | Existing Runtime version 31 cannot prove the new behavior. | Record live verification as blocked until a new image is published and the Runtime is updated. | - -## Approval gate - -- P0: none -- P1: all resolved -- P2/P3: treatment recorded above -- Result: approved for the existing SDD/TDD implementation and verification workflow. diff --git a/prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-dev-loop-review.zh.md b/prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-dev-loop-review.zh.md deleted file mode 100644 index e2ae6b259..000000000 --- a/prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-dev-loop-review.zh.md +++ /dev/null @@ -1,31 +0,0 @@ -# Dev Loop Spec 评审:MPA Runtime 集成加固 - -- **Change ID:** `mpa-runtime-integration-hardening` -- **状态:** 已批准 -- **日期:** 2026-09-13 -- **English version:** [2026-09-13-dev-loop-review.md](2026-09-13-dev-loop-review.md) -- **评审方案:** [2026-09-12-mpa-runtime-integration-hardening.zh.md](2026-09-12-mpa-runtime-integration-hardening.zh.md) - -## 评审结论 - -方案具备可落地性,且与当前 VeADK/mpa-agent 边界一致。当前没有 P0/P1 阻塞项。评审覆盖上下文一致性、歧义、SDD/TDD 适配、最小实现、兼容性、存量部署、失败恢复、安全、可观测性和扩展性。 - -## 问题与处理 - -| 优先级 | 问题 | 处理 | -| --- | --- | --- | -| P1 | 宽泛的 VeADK 模式开关可能关闭无关缓存。 | 使用窄粒度 `APPCENTER_RESOURCE_DISCOVERY_ENABLED`,原生默认保持 `true`。 | -| P1 | 模型生成的 `sandbox_task.modelOverride` 可能绕过 Studio allowlist。 | 经校验的请求级模型始终覆盖工具参数。 | -| P1 | 首个 A2A 进度更新在 artifact 不存在时使用 `append=true`。 | 第一次成功 enqueue 创建 artifact,后续事件才 append。 | -| P1 | Runtime key 注入可能通过 dry-run 输出泄露。 | 将 `CODEX_MCP_RUNTIME_API_KEY` 作为 Secret,仅在第二阶段服务端注入。 | -| P2 | 重放用量快照可能放大 Session 总量。 | 按来源/请求记录快照,只发出正向增量。 | -| P2 | 开启 APMPlus 可能意外暴露 Prompt 内容。 | 开启 Runtime 链路,同时保持 `APMPLUS_TRACE_CONTENT=false`。 | -| P2 | 日志下载可能绕过展示上限或脱敏。 | 只下载服务端已脱敏的当前 1,000 行快照。 | -| P3 | 现有 Runtime version 31 无法证明新行为。 | 在发布新镜像并更新 Runtime 前,将真实验证记录为 blocked。 | - -## 批准门禁 - -- P0:无 -- P1:全部解决 -- P2/P3:处理方案已记录 -- 结论:批准进入现有 SDD/TDD 实现及验证流程。 diff --git a/prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-functional-validation-cases.md b/prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-functional-validation-cases.md deleted file mode 100644 index adcd5e313..000000000 --- a/prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-functional-validation-cases.md +++ /dev/null @@ -1,23 +0,0 @@ -# Functional Validation Cases: MPA Runtime Integration Hardening - -- **Change ID:** `mpa-runtime-integration-hardening` -- **Status:** Active -- **Date:** 2026-09-13 -- **Chinese version:** [2026-09-13-functional-validation-cases.zh.md](2026-09-13-functional-validation-cases.zh.md) - -## Cases - -| Case | Requirements | Preconditions and execution | Expected result | Evidence | Status | -| --- | --- | --- | --- | --- | --- | -| `VC-1` | `FR-1`, `FR-3`, `FR-9` | Run mpa-agent dependency/config tests; after publishing, observe a VeADK Runtime for at least 240 seconds and execute one sandbox turn. | No `ListResources` call, no missing `user_pool_id`, and no expected-config traceback. | Test output; sanitized Runtime logs. | Automated `pass`; live `blocked` by unpublished image. | -| `VC-2` | `FR-2` | Run provisioning env/runtime/CLI tests. | Phase two contains URL and Runtime key; output does not expose the key. | Pytest output and secret scan. | `pass` | -| `VC-3` | `FR-4` | Execute A2A relay tests and a real sandbox command. | First artifact uses `append=false`; subsequent events append and remain visible. | Pytest output; live A2A stream. | Automated `pass`; live `blocked`. | -| `VC-4` | `FR-5` | Run Runtime log backend/frontend tests; open logs, wait for refresh, download. | At most 1,000 sanitized lines; downloaded bytes equal the displayed snapshot. | Pytest/frontend test output; browser screenshot and downloaded file hash. | Automated `pass`; browser `blocked` by missing Playwright Chromium. | -| `VC-5` | `FR-6` | Replay primary A2A usage and repeated worker `usage.updated`; then perform a real primary and sandbox turn. | Existing Token UI shows model/current/cumulative use once; replay does not inflate totals. | Decoder/frontend tests; screenshot and captured SSE. | Automated `pass`; live `blocked`. | -| `VC-6` | `FR-7` | Configure two selectable models; send default and alternate turns; submit an invalid model ID; delegate one turn. | Selector is capability-gated and disabled while busy; valid override reaches both paths; invalid ID is rejected before execution. | mpa-agent/bridge/frontend tests; Runtime logs and screenshots. | Automated `pass`; live `blocked`. | -| `VC-7` | `FR-8` | Inspect Runtime metadata; query `/web/runtime-trace` after a completed turn. | `apmplus_enable=true`; endpoint returns 425 while collecting, 200 with spans, or explicit 403 permission error. | Runtime metadata and normalized trace JSON. | Request tests `pass`; current v31 `fail` because tracing is disabled. | -| `VC-8` | Regression | Run targeted suites, frontend full tests/build, pre-commit, and default Python regression. | No related regression; environment-only failures are separately classified. | Command logs. | `pass` except optional-dependency regression failures. | - -## Coverage gate - -Every P0/P1 requirement is covered by at least one executable case. Live cases that require publishing are not treated as passed; they remain blocked until explicit deployment authorization is provided. diff --git a/prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-functional-validation-cases.zh.md b/prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-functional-validation-cases.zh.md deleted file mode 100644 index 9e80413fa..000000000 --- a/prd-spec/bugfixes/mpa-runtime-integration/2026-09-13-functional-validation-cases.zh.md +++ /dev/null @@ -1,23 +0,0 @@ -# 功能验证 Case:MPA Runtime 集成加固 - -- **Change ID:** `mpa-runtime-integration-hardening` -- **状态:** 生效 -- **日期:** 2026-09-13 -- **English version:** [2026-09-13-functional-validation-cases.md](2026-09-13-functional-validation-cases.md) - -## Case 清单 - -| Case | 需求 | 前置条件与执行 | 预期结果 | 证据 | 状态 | -| --- | --- | --- | --- | --- | --- | -| `VC-1` | `FR-1`、`FR-3`、`FR-9` | 运行 mpa-agent 依赖/配置测试;发布后观察 VeADK Runtime 至少 240 秒并执行一次 sandbox 回合。 | 无 `ListResources` 调用、无缺失 `user_pool_id`,预期配置缺失不打印 traceback。 | 测试输出、脱敏 Runtime 日志。 | 自动测试 `pass`;真实验证因镜像未发布而 `blocked`。 | -| `VC-2` | `FR-2` | 运行部署环境、Runtime、CLI 测试。 | 第二阶段同时包含 URL 与 Runtime key;输出不泄露 Key。 | Pytest 输出及 Secret scan。 | `pass` | -| `VC-3` | `FR-4` | 执行 A2A relay 测试及真实 sandbox 命令。 | 首个 artifact 使用 `append=false`,后续事件 append 且可见。 | Pytest 输出、真实 A2A 流。 | 自动测试 `pass`;真实验证 `blocked`。 | -| `VC-4` | `FR-5` | 运行 Runtime 日志后端/前端测试;打开日志、等待刷新并下载。 | 最多 1,000 行脱敏日志;下载字节与当前显示快照一致。 | Pytest/前端测试输出、浏览器截图、下载文件 Hash。 | 自动测试 `pass`;浏览器因缺少 Playwright Chromium 而 `blocked`。 | -| `VC-5` | `FR-6` | 重放主 A2A 用量和重复 worker `usage.updated`;再执行真实主模型和 sandbox 回合。 | 现有 Token UI 一次性展示模型、当前值与累计值;重放不放大总量。 | Decoder/前端测试、截图及 SSE。 | 自动测试 `pass`;真实验证 `blocked`。 | -| `VC-6` | `FR-7` | 配置两个可选模型;发送默认/备选回合;提交非法模型 ID;执行一次委托。 | 选择器按能力显示且忙碌时禁用;合法覆盖到达两条路径;非法 ID 在执行前拒绝。 | mpa-agent/桥接/前端测试、Runtime 日志及截图。 | 自动测试 `pass`;真实验证 `blocked`。 | -| `VC-7` | `FR-8` | 检查 Runtime metadata;完成回合后查询 `/web/runtime-trace`。 | `apmplus_enable=true`;采集中返回 425,成功返回 200 Span,或明确返回 403 权限错误。 | Runtime metadata 及规范化 Trace JSON。 | 请求测试 `pass`;当前 v31 因链路关闭为 `fail`。 | -| `VC-8` | 回归 | 运行目标测试、前端全量测试/构建、pre-commit 及默认 Python 回归。 | 无相关回归;环境依赖失败单独分类。 | 命令日志。 | 除可选依赖回归失败外为 `pass`。 | - -## 覆盖门禁 - -每个 P0/P1 需求至少由一个可执行 Case 覆盖。需要发布的真实 Case 不得记为通过;在获得明确部署授权前保持 blocked。 diff --git a/prd-spec/bugfixes/mpa-runtime-mcp-secret/2026-10-07-persistent-mcp-secret.md b/prd-spec/bugfixes/mpa-runtime-mcp-secret/2026-10-07-persistent-mcp-secret.md deleted file mode 100644 index 1b1f5a458..000000000 --- a/prd-spec/bugfixes/mpa-runtime-mcp-secret/2026-10-07-persistent-mcp-secret.md +++ /dev/null @@ -1,78 +0,0 @@ -# Persistent MPA Runtime MCP secret - -[中文版](2026-10-07-persistent-mcp-secret.zh.md) - -- Change ID: `mpa-runtime-mcp-secret`; date: 2026-10-07; status: implemented. -- Contracts: [Runtime provisioning](../../../specs/mpa-runtime-provisioning/README.md), [Studio creation](../../../specs/studio-mpa-creation/README.md). - -## Background and scope - -Both legacy provisioning and managed Studio/CLI creation omit `MCP_TOKEN_SECRET` by default. MPA consequently uses a process-local secret that cannot verify tokens after replacement or across replicas. The user explicitly requested implementation from the main branch after agreeing to generate once and retain the configured value. - -The scope is provisioning configuration, persistence, reference-template isolation, and secret masking. MPA JWT format, Meegle OAuth, frontend flows, automatic modification of existing deployments, and live cloud releases are outside this change. - -## Requirements and design - -- `FR-1`: Fresh creation injects a cryptographically random 32-byte secret, encoded as 64 hexadecimal characters, into Runtime environment variables. Blank values count as missing. -- `FR-2`: An explicit nonblank value wins. Otherwise reuse the target Runtime's nonblank value before generating a replacement. Do not mutate caller-owned environment dictionaries. -- `FR-3`: Creation, endpoint/key finalization, and retries use the same secret. Managed creation stores `mcp_token_secret` in the agent's PostgreSQL `mpa_deployment_settings` table before CreateRuntime. Atomic insert-or-retain handles retries and concurrent generation; explicit/current values reconcile the stored value only after validating any pending request hash, so rejected retries cannot replace its saved secret. Dispose connections on success and failure, and propagate database errors before cloud creation. -- `FR-4`: Reference Runtime templates exclude `MCP_TOKEN_SECRET`, so a new Agent gets its own secret. Explicit JSON-template values remain supported. Dry-run output masks explicit values; normal results and nonsecret deployment registry records exclude the secret. -- `FR-5`: Legacy provisioning persists through Runtime environment values. Lost-response name lookup reuses the existing resource and its secret. Existing managed unfinished requests with incompatible hashes remain blocked under the existing recovery contract. - -No signatures, OAuth permissions, or credential access scopes change. Use existing standard-library `secrets`, SQLAlchemy, and Runtime SDK objects. PostgreSQL storage has the same access boundary as the agent database; Runtime env is still the verification source of truth. Random generation during dry-run is unnecessary. No new UI or dependencies are needed. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| FR-1, FR-2 | T-1: legacy Runtime injection | AC-1: generation, explicit/blank values, reuse, and unchanged input | `tests/integrations/test_mpa_runtime.py` | -| FR-3 | T-2: durable managed secret and injection | AC-2: retries/finalization retain secret; failures dispose connections | managed database and deployment tests | -| FR-4 | T-3: reference isolation and masking | AC-3: reference secret excluded and output masked | managed deployment and environment tests | -| FR-1–FR-5 | T-4: regression, incremental coverage, lint and review | AC-4: incremental executable-line coverage >95%; required checks recorded | pytest-cov, diff coverage, pre-commit, Pyright | - -Affected files: `mpa_runtime.py`, `mpa_provision.py`, `managed/runtime.py`, `managed/database.py`, their existing tests, and the two linked bilingual contracts. - -## Review and verification record - -Direct design review performed because `review-spec` is unavailable: ownership, retries, secret isolation, database cleanup, error propagation, compatibility and bilingual equivalence checked; no design blockers. User approval: explicit implementation request in this chat on 2026-10-07, building on the previously agreed generate-once/persist behavior. User instructions authorize commit/push after checks. - -Verification on 2026-10-07, branch `fix/mpa-runtime-mcp-secret`, against `superops/main` at `056e8acd0096b6b7b0ee8203ef0f4f11f1efbfcc`: - -| Check | Outcome | Evidence | -| --- | --- | --- | -| Regression before implementation | pass | Missing secret, reuse and managed persistence tests failed before the corresponding fix; rejected changed-secret retry also reproduced before moving persistence after hash validation. | -| Changed test files | pass | 93 tests, including 21 added parameterized cases. | -| Coverage and deployment edge contracts | pass | 113 tests; added executable lines covered: 23/23 (100%), computed by intersecting `git diff --unified=0 superops/main` with coverage XML line hits. | -| Affected managed creation regression | pass | 478 tests on repeat and in the final post-sync coverage run; main baseline has 457 tests. Initial run had one transient failure in the unchanged subprocess diagnostic test; repeat passed. | -| Pre-commit and whitespace | pass | `uv run --extra dev pre-commit run --all-files` and `git diff --check`; Ruff and secret scans passed. | -| Pyright on eight changed Python files | fail (baseline) | 34 diagnostics on both this branch and main, with identical file/message/rule sets; no added diagnostics. Existing SDK alias constructor declarations and fake-client optional attributes cause the failures. | -| Full unrelated repository regression | not_run | Scope is MPA provisioning only, with no shared dependency or runtime execution changes; affected managed creation regression was used. | -| Live cloud/PostgreSQL/Feishu | not_run | Offline SDK/database fakes only; this task authorizes source changes, not a cloud deployment. | - -Coverage collection encounters an SDK/Pydantic lazy-import error on both main and this branch. Preloading the unmodified SDK before enabling pytest-cov avoids that interaction without replacing the SDK or changing application behavior: - -```bash -uv run --extra dev python - <<'PYTEST' -import agentkit.sdk.skills.types -import pytest -raise SystemExit(pytest.main([ - "tests/integrations/test_mpa_runtime.py", - "tests/integrations/test_mpa_provision_env.py", - "tests/integrations/mpa_managed/test_agent_deployment.py", - "tests/integrations/mpa_managed/test_deployment_database_unit.py", - "tests/integrations/mpa_managed/test_runtime_deployment_edges.py", - "-q", "--tb=short", - "--cov=veadk.integrations.mpa.mpa_runtime", - "--cov=veadk.integrations.mpa.mpa_provision", - "--cov=veadk.integrations.mpa.managed.runtime", - "--cov=veadk.integrations.mpa.managed.database", - "--cov-report=xml:/tmp/mpa-mcp-secret-coverage.xml", -])) -PYTEST -``` - -For the broader affected run, also preload `from pydantic import RootModel`, then use the managed test directory plus the two legacy test files without coverage. Pyright uses the project `.venv/bin/python` via `--pythonpath`; compare baseline diagnostics instead of claiming a clean type check. - -Implementation review: explicit configuration wins; blank values generate/reuse correctly; active Runtime values reconcile the durable value; retries preserve request identity; reference templates exclude the secret; caller dictionaries and public results remain safe; database engines dispose on success and failure. T-1 through T-4 and AC-1 through AC-4 are complete within the offline scope. - -Risks: database preparation now requires writing the agent-owned settings table; callers explicitly overriding a key perform an intentional rotation. Existing unfinished pre-change requests with incompatible hashes still require their existing recovery procedure. Secrets must never appear in reports or fixture snapshots. diff --git a/prd-spec/bugfixes/mpa-runtime-mcp-secret/2026-10-07-persistent-mcp-secret.zh.md b/prd-spec/bugfixes/mpa-runtime-mcp-secret/2026-10-07-persistent-mcp-secret.zh.md deleted file mode 100644 index 31548efa5..000000000 --- a/prd-spec/bugfixes/mpa-runtime-mcp-secret/2026-10-07-persistent-mcp-secret.zh.md +++ /dev/null @@ -1,78 +0,0 @@ -# MPA Runtime 固定 MCP 密钥 - -[English](2026-10-07-persistent-mcp-secret.md) - -- 变更 ID:`mpa-runtime-mcp-secret`;日期:2026-10-07;状态:implemented。 -- 契约:[Runtime 创建](../../../specs/mpa-runtime-provisioning/README.zh.md)、[Studio 创建](../../../specs/studio-mpa-creation/README.zh.md)。 - -## 背景和范围 - -旧创建入口及托管 Studio/CLI 创建默认均未注入 `MCP_TOKEN_SECRET`。MPA 因而使用进程内临时密钥,无法在实例替换或跨副本时校验已有 token。用户认可生成一次并保存配置的方案后,明确要求从主分支实现。 - -范围包括创建配置、持久化、参考模板隔离和密钥脱敏。MPA JWT 格式、Meegle OAuth、前端流程、自动修改存量部署及真实云发布不在本次范围内。 - -## 需求和设计 - -- `FR-1`:首次创建向 Runtime 环境变量注入密码学随机的 32 字节密钥,编码为 64 位十六进制字符串。空白值视为未配置。 -- `FR-2`:显式非空值优先;否则先沿用目标 Runtime 已有非空值,再考虑生成。不修改调用方传入的环境变量字典。 -- `FR-3`:创建、地址/API Key 回填及重试使用同一密钥。托管创建在 CreateRuntime 前,将 `mcp_token_secret` 保存至 Agent 专属 PostgreSQL 的 `mpa_deployment_settings` 表。原子插入或保留已有值处理重试及并发生成;显式值和当前 Runtime 值仅在检查未完成请求摘要之后同步至数据库,避免被拒绝的重试覆盖原密钥。成功及失败均释放连接,数据库错误在云创建前向上传播。 -- `FR-4`:参考 Runtime 模板排除 `MCP_TOKEN_SECRET`,新 Agent 获得独立密钥;保留显式 JSON 模板值的配置能力。dry-run 脱敏显式值,正常返回值及非敏感部署登记记录不包含密钥。 -- `FR-5`:旧创建入口通过 Runtime 环境变量持久化。创建响应丢失后,按名称发现已有资源并沿用其密钥。已有托管未完成请求如摘要不兼容,继续按原恢复契约阻断。 - -不变更函数签名、OAuth 权限及凭证访问范围。复用标准库 `secrets`、SQLAlchemy 和 Runtime SDK 对象。PostgreSQL 存储沿用 Agent 数据库的访问边界,Runtime 环境变量仍是验签配置来源。dry-run 无需生成随机值。不新增 UI 或依赖。 - -## 任务和验收 - -| 需求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| FR-1、FR-2 | T-1:旧入口注入 | AC-1:生成、显式/空值、复用及输入不变 | `tests/integrations/test_mpa_runtime.py` | -| FR-3 | T-2:托管密钥持久化及注入 | AC-2:重试/回填保持密钥,失败释放连接 | 托管数据库及部署测试 | -| FR-4 | T-3:参考隔离和脱敏 | AC-3:排除参考密钥,输出脱敏 | 托管部署及环境变量测试 | -| FR-1–FR-5 | T-4:回归、增量覆盖、检查和审查 | AC-4:增量可执行行覆盖率超过 95%,记录所需检查 | pytest-cov、diff coverage、pre-commit、Pyright | - -涉及文件:`mpa_runtime.py`、`mpa_provision.py`、`managed/runtime.py`、`managed/database.py`、对应现有测试,以及上方关联的两组双语契约。 - -## 审查和验证记录 - -`review-spec` 不可用,已直接审查职责归属、重试、密钥隔离、数据库清理、错误传播、兼容性及双语一致性,无设计阻断。用户批准:2026-10-07 在本会话明确要求实现,基于此前已认可的生成一次并持久化方案。用户指令授权检查后 commit/push。 - -2026-10-07 验证记录:分支 `fix/mpa-runtime-mcp-secret`,基线为 `superops/main` 的 `056e8acd0096b6b7b0ee8203ef0f4f11f1efbfcc`。 - -| 检查 | 结果 | 证据 | -| --- | --- | --- | -| 实现前回归 | pass | 缺少密钥、复用及托管持久化用例在对应修复前失败;修改密钥的重试被拒绝却覆盖存值的问题,在把写入移到摘要检查之后前也已复现。 | -| 修改的测试文件 | pass | 93 个用例,其中新增 21 个参数化用例。 | -| 覆盖率及部署边界契约 | pass | 113 个用例;将 `git diff --unified=0 superops/main` 与覆盖率 XML 的行命中数据求交,新增可执行行覆盖 23/23(100%)。 | -| 受影响的托管创建回归 | pass | 复跑及同步后的最终覆盖率运行均为 478 个用例通过,主分支基线为 457 个。首次运行中一个未修改的子进程诊断用例暂时失败,复跑通过。 | -| Pre-commit 及空白检查 | pass | `uv run --extra dev pre-commit run --all-files`、`git diff --check`;Ruff 和凭证扫描通过。 | -| 八个修改 Python 文件的 Pyright | fail(基线问题) | 本分支及主分支均为 34 条诊断,文件/消息/规则集合完全一致,无新增诊断。现有 SDK 别名构造参数声明及假客户端可空属性导致失败。 | -| 全仓无关回归 | not_run | 范围为 MPA 创建,不改变共享依赖及运行执行逻辑;已运行受影响的托管创建回归。 | -| 真实云/PostgreSQL/飞书 | not_run | 仅使用离线 SDK/数据库假实现;本任务授权源码修改,未要求云发布。 | - -覆盖率采集在主分支和本分支均遇到 SDK/Pydantic 延迟导入错误。在 pytest-cov 启动前预加载未修改的 SDK,可以避免该交互,不替换 SDK,也不改变应用行为。可执行命令与英文版一致: - -```bash -uv run --extra dev python - <<'PYTEST' -import agentkit.sdk.skills.types -import pytest -raise SystemExit(pytest.main([ - "tests/integrations/test_mpa_runtime.py", - "tests/integrations/test_mpa_provision_env.py", - "tests/integrations/mpa_managed/test_agent_deployment.py", - "tests/integrations/mpa_managed/test_deployment_database_unit.py", - "tests/integrations/mpa_managed/test_runtime_deployment_edges.py", - "-q", "--tb=short", - "--cov=veadk.integrations.mpa.mpa_runtime", - "--cov=veadk.integrations.mpa.mpa_provision", - "--cov=veadk.integrations.mpa.managed.runtime", - "--cov=veadk.integrations.mpa.managed.database", - "--cov-report=xml:/tmp/mpa-mcp-secret-coverage.xml", -])) -PYTEST -``` - -扩大受影响回归时,再预加载 `from pydantic import RootModel`,使用托管测试目录及两个旧入口测试文件,不开启覆盖率。Pyright 通过 `--pythonpath` 指定项目 `.venv/bin/python`;对照基线诊断,不把类型检查宣称为完全通过。 - -实现审查:显式配置优先;空值正确生成/复用;当前 Runtime 值同步到持久化值;重试保持请求身份;参考模板排除密钥;调用方字典及公开结果安全;数据库引擎在成功及失败时均释放。离线范围内 T-1 至 T-4、AC-1 至 AC-4 已完成。 - -风险:数据库准备新增写入 Agent 专属配置表的权限需求;调用方显式覆盖密钥属于主动轮换。旧版遗留未完成请求如摘要不兼容,仍须按原恢复流程处理。报告及测试快照不得包含真实凭证。 diff --git a/prd-spec/bugfixes/mpa-runtime-reference-command/2026-09-29-preserve-runtime-command.md b/prd-spec/bugfixes/mpa-runtime-reference-command/2026-09-29-preserve-runtime-command.md deleted file mode 100644 index aa701236a..000000000 --- a/prd-spec/bugfixes/mpa-runtime-reference-command/2026-09-29-preserve-runtime-command.md +++ /dev/null @@ -1,60 +0,0 @@ -# Preserve the reference Runtime command - -[中文版](2026-09-29-preserve-runtime-command.zh.md) - -- **Change ID:** `mpa-runtime-reference-command` -- **Date:** 2026-09-29 -- **Status:** implemented -- **Contract:** [MPA Runtime provisioning](../../../specs/mpa-runtime-provisioning/README.md) - -## Background and evidence - -Live managed creation from a Ready reference Runtime produced a new Runtime in -version 0 `Error`. The reference used `Command="bash run.sh"`; the new Runtime -had an empty command. Source inspection confirms `template_from_runtime()` -copies image, resources, role, and project but omits `Command`. - -## Goals and non-goals - -- Preserve a reference Runtime's `Command` in the new Runtime create payload. -- Keep all existing identity, credential, endpoint, Tool, network, and - environment sanitization unchanged. -- Do not infer a command when the reference omits it and do not change flat or - explicit-template creation. - -## Requirements and design - -- **FR-1:** `managed.from-runtime` MUST copy `Command` when present. -- **FR-2:** Reference-owned identities and secrets MUST continue to be removed. -- **FR-3:** A missing reference command MUST remain absent rather than receiving - a guessed default. - -Add `Command` to the existing allowlist in `template_from_runtime()`. This is -the smallest root-cause fix and preserves the allowlist's security boundary. -No API, schema, permission, concurrency, or persistence changes are required. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| `FR-1` | `T-1` add a failing reference-template assertion, then update the allowlist | `AC-1` copied template contains the exact command | `uv run --extra dev pytest tests/integrations/mpa_managed/test_agent_deployment.py` | -| `FR-2`, `FR-3` | `T-2` run affected managed regressions | `AC-2` sanitization and command-absence behavior remain unchanged | same command plus `tests/integrations/mpa_managed/test_service.py` | - -## Risks, review, and delivery - -The command is already trusted Runtime configuration from the same account and -is passed to the same control-plane field. The risk is limited to preserving a -previously dropped value; rollback removes the allowlist entry. `review-spec` -is unavailable, so scope, security, compatibility, failure behavior, -testability, and bilingual equivalence were reviewed directly. No blockers -remain, and the user's instruction approves this fix. - -Implemented on 2026-09-29. The regression failed before the implementation with -missing `Command`; after the one-field allowlist change, the two affected test -files pass all 58 tests. The present/absent reference-command tests execute all -12 executable lines in `template_from_runtime()` (100%). Changed-file -pre-commit Ruff and secret checks pass, as does `git diff --check`. The required -all-files pre-commit reaches and passes Ruff check and both secret scanners, but -Ruff format rewrites the unrelated pre-existing layout in -`veadk/integrations/mpa/managed/tasks.py`; that out-of-scope edit was reverted. -Live cloud recreation is `not_run` in this code-change step. diff --git a/prd-spec/bugfixes/mpa-runtime-reference-command/2026-09-29-preserve-runtime-command.zh.md b/prd-spec/bugfixes/mpa-runtime-reference-command/2026-09-29-preserve-runtime-command.zh.md deleted file mode 100644 index 6c4bc3362..000000000 --- a/prd-spec/bugfixes/mpa-runtime-reference-command/2026-09-29-preserve-runtime-command.zh.md +++ /dev/null @@ -1,51 +0,0 @@ -# 保留参考 Runtime 的启动命令 - -[English](2026-09-29-preserve-runtime-command.md) - -- **变更 ID:** `mpa-runtime-reference-command` -- **日期:** 2026-09-29 -- **状态:** implemented -- **契约:** [MPA Runtime 部署](../../../specs/mpa-runtime-provisioning/README.zh.md) - -## 背景与证据 - -使用一个 Ready 参考 Runtime 进行真实托管创建时,新 Runtime 在版本 0 进入 -`Error`。参考 Runtime 使用 `Command="bash run.sh"`,新 Runtime 的命令为空。 -源码确认 `template_from_runtime()` 会复制镜像、资源、角色和项目,但遗漏 `Command`。 - -## 目标与非目标 - -- 在新 Runtime 创建请求中保留参考 Runtime 的 `Command`。 -- 保持现有身份、凭据、endpoint、Tool、网络和环境清理逻辑不变。 -- 参考 Runtime 未提供命令时不推断默认值,也不修改平铺或显式模板创建路径。 - -## 需求与设计 - -- **FR-1:** `managed.from-runtime` 必须在 `Command` 存在时复制该字段。 -- **FR-2:** 必须继续移除参考 Runtime 所属的身份和密钥。 -- **FR-3:** 参考 Runtime 未提供命令时必须保持缺失,不得猜测默认命令。 - -在 `template_from_runtime()` 现有白名单中加入 `Command`。这是针对根因的最小修复, -并保留白名单安全边界。不修改 API、schema、权限、并发或持久化。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| `FR-1` | `T-1` 先增加失败断言,再更新白名单 | `AC-1` 复制模板包含完全相同的命令 | `uv run --extra dev pytest tests/integrations/mpa_managed/test_agent_deployment.py` | -| `FR-2`、`FR-3` | `T-2` 运行受影响托管回归 | `AC-2` 清理及命令缺失行为保持不变 | 同上,并运行 `tests/integrations/mpa_managed/test_service.py` | - -## 风险、审查与交付 - -该命令是同一账号下参考 Runtime 的既有可信配置,并传入相同控制面字段。风险仅限于 -保留此前被丢弃的值;回滚时删除该白名单项即可。`review-spec` 不可用,因此已直接 -审查范围、安全、兼容性、失败行为、可测试性和双语一致性。没有阻塞项,用户本轮 -指令批准该修复。 - -已于 2026-09-29 实施。实现前回归因缺少 `Command` 失败;加入一个白名单字段后, -两个受影响测试文件共 58 个测试全部通过。参考命令存在/缺失测试执行了 -`template_from_runtime()` 的全部 12 个可执行行,覆盖率 100%。变更文件的 -pre-commit Ruff、密钥检查及 `git diff --check` 均通过。必需的全文件 pre-commit -中 Ruff check 和两个密钥扫描通过,但 Ruff format 会重写范围外 -`veadk/integrations/mpa/managed/tasks.py` 的存量排版;该无关改动已还原。本次代码 -修复步骤未运行真实云端重建。 diff --git a/prd-spec/bugfixes/mpa-sandbox-event-identity/2026-09-29-typed-event-dedup.md b/prd-spec/bugfixes/mpa-sandbox-event-identity/2026-09-29-typed-event-dedup.md deleted file mode 100644 index 67ed953bb..000000000 --- a/prd-spec/bugfixes/mpa-sandbox-event-identity/2026-09-29-typed-event-dedup.md +++ /dev/null @@ -1,46 +0,0 @@ -# Preserve distinct MPA sandbox events sharing a source ID - -[中文版](2026-09-29-typed-event-dedup.zh.md) - -ID: `mpa-sandbox-event-identity`; date: 2026-09-29; status: implemented. -Contract: [Studio Runtime Diagnostics CON-8](../../../specs/studio-runtime-diagnostics/README.md). - -## Evidence, scope and requirements -Read-only session inspection and anonymous replay confirmed that `usage.updated` and `invocation.completed` can share a source ID. The agentkit-mpa-agent relay and both Studio decoder dedup stages discard the completion. Without the authoritative final marker, the UI can retain streamed preview and wrapper result as two answers. - -FR-1: Preserve different normalized event types with the same source ID, including usage, completion, failure and cancellation. Deduplicate actual replays by task/invocation/type/source ID; preserve events without source IDs. -FR-2: Preserve multipart snapshots containing both already-seen and new events. Keep wire IDs and the `mpa.sandbox-event.v1` schema unchanged. -FR-3: Keep reasoning, tools and token accounting; one sandbox final must suppress only an exact complete outer answer mirror. Generic A2A/ADK decoding remains unchanged. -Non-goals: UI redesign, fuzzy text filtering, history mutation, authentication changes, image builds, deployment and cloud resource changes. - -## Design and affected files -In agentkit-mpa-agent `app/a2a/executor.py`, key the request-local relay set by `(source_event_id, event_type)`; invocation isolation already comes from the handler closure. Original persistence callback and payload allowlist stay unchanged. -In `veadk/cli/runtime_a2a_stream.py`, defer MPA-schema artifact frame deduplication to per-event projection. A first-part ID cannot identify a multipart frame. Projection uses `(task_id, invocation_id, event_type, source_event_id)`. Generic frame identity stays unchanged. No persistent schema, API, permission or dependency changes. Sets remain decoder/request-local and follow its existing lifetime; disconnect/cancel releases them. Completion, failure and cancellation are separate semantic identities, not mutually deduplicated by source ID. -Tests: `tests/cli/test_runtime_a2a_stream.py`, agentkit-mpa-agent `tests/test_a2a_executor.py`. Existing frontend projection is verified with anonymous Python-to-Node replay without changing frontend source/assets. - -## Tasks and acceptance -- T-1 / AC-1 (FR-1/2): Add failing offline tests for same-ID usage/completion in both orders, replay suppression, invocation/task isolation, missing IDs, mixed multipart replay and failure/cancellation. -- T-2 / AC-2 (FR-1/2/3): Apply the scoped relay and decoder changes; preserve exact wire payloads and generic deduplication. -- T-3 / AC-3 (FR-3): Run affected Python suites, Ruff/Pyright, anonymous frontend live/history replay, scoped secret scan and whitespace checks. Verify one answer, retained reasoning and correct token count. Record actual results and unverified deployment separately. - -## Review, approval and risks -Direct review substitutes for unavailable review-spec. Checked ownership, compatibility, errors/terminal states, callback order, privacy, bounded request-local lifetime, testability and bilingual equivalence; no blockers. User approved implementation with “帮我改下” after the two-repository diagnosis. No production session data is used in committed fixtures. -Risk: Studio alone cannot recover an event the upstream relay never sends. Both fixes must be deployed to activate the complete correction; image deployment is outside this request. Late exact replays remain suppressed; distinct events remain visible. No migration is needed and old persisted history is unchanged. - -## Verification -Date: 2026-09-29. Diff bases: VeADK `79acf209`; agentkit-mpa-agent `62cf960`. Existing unrelated edits are preserved. Results follow below. - -### Results -T-1/T-2/T-3 completed for the code change, without deployment. Checks were run against the working diff on 2026-09-29. -- `pass`: before the fix, seven new Studio cases and two relay cases failed; the missing-ID compatibility case passed. Afterward, `uv run --extra dev pytest tests/cli/test_runtime_a2a_stream.py tests/cli/test_frontend_runtime_proxy.py -q` passed 156 tests (six existing deprecation warnings). -- `pass`: in agentkit-mpa-agent, `.venv/bin/python -m pytest tests/test_a2a_executor.py tests/test_a2a_codex_terminal.py tests/test_codex_delegation_tool.py -q` passed 113 tests. Existing ADK experimental/deprecation warnings remain. -- `pass`: `node --test frontend/tests/mpaResponseGrouping.test.mjs frontend/tests/mpaContentPreservation.test.mjs` passed 22 tests. Anonymous Python decoder → actual frontend projector/grouping replay produced one answer, one retained thinking block and exactly 100 tokens in both live and history modes. -- `pass`: Ruff 0.11.12 check on the two changed Python files in each repository; Studio formatter check; Studio `uv run --with pyright pyright veadk/cli/runtime_a2a_stream.py tests/cli/test_runtime_a2a_stream.py` reported zero errors/warnings. Runtime Ruff used `--no-cache` after the sandbox prevented cache writes. -- `fail` (pre-existing baseline): Runtime Pyright on `app/a2a/executor.py` and `tests/test_a2a_executor.py` reported 45 errors. An isolated HEAD copy with the same interpreter/import path reported the same 45 diagnostic messages; this change adds none. Unrelated type cleanup is excluded. -- `pass`: direct code/design review confirms original callbacks, allowlist and generic dedup remain; paired-language identifiers/links, scoped gitleaks scan and `git diff --check` checked. -- `not_applicable`: frontend build/generated assets, sidecar gates and new layout/keyboard/IME checks; no frontend or sidecar files changed. -- `not_run`: real browser/cloud end-to-end, image build, deployment, service restart, commit and push. Verification is offline replay; existing Runtime images still require replacement to activate upstream forwarding, and Studio must reload the decoder. Full repository regression was not run because the correction is limited to explicit MPA events; affected decoder/proxy/relay/terminal and frontend preservation suites were used. - -## Rebase validation — 2026-10-07 - -Reconciled with main `e0448a4d` for PR #17. The original functional patch is retained; upstream discovery/authentication and Worker recovery changes are preserved. See the [combined reconciliation and verification record](../mpa-shared-network-bootstrap/2026-09-28-registry-owned-network.md#pr-17-rebase-reconciliation--2026-10-07). diff --git a/prd-spec/bugfixes/mpa-sandbox-event-identity/2026-09-29-typed-event-dedup.zh.md b/prd-spec/bugfixes/mpa-sandbox-event-identity/2026-09-29-typed-event-dedup.zh.md deleted file mode 100644 index d9556c5e5..000000000 --- a/prd-spec/bugfixes/mpa-sandbox-event-identity/2026-09-29-typed-event-dedup.zh.md +++ /dev/null @@ -1,46 +0,0 @@ -# 保留来源 ID 相同的不同 MPA 沙箱事件 - -[English](2026-09-29-typed-event-dedup.md) - -ID:`mpa-sandbox-event-identity`;日期:2026-09-29;状态:implemented。 -契约:[Studio Runtime Diagnostics CON-8](../../../specs/studio-runtime-diagnostics/README.zh.md)。 - -## 证据、范围与需求 -只读会话检查及匿名回放确认,`usage.updated` 和 `invocation.completed` 可以共用来源 ID。agentkit-mpa-agent 转发器及 Studio 解码器的两层去重会丢弃完成事件。缺少权威最终标记时,界面可能把流式预览和包装结果保留成两份答案。 - -FR-1:保留来源 ID 相同而标准化类型不同的事件,包括用量、完成、失败及取消。真正的重放按任务/调用/类型/来源 ID 去重;没有来源 ID 的事件仍然保留。 -FR-2:保留同时包含已见和新事件的多部分快照。不改变传输 ID 和 `mpa.sandbox-event.v1` 协议。 -FR-3:保留推理、工具及 token 统计;沙箱最终答案只消除完全一致的外层完整答案副本。通用 A2A/ADK 解码保持不变。 -非目标:界面重设计、模糊文本过滤、修改历史记录、鉴权调整、镜像构建、部署及云资源变更。 - -## 设计与影响文件 -agentkit-mpa-agent 的 `app/a2a/executor.py` 使用 `(source_event_id, event_type)` 作为请求内转发去重键;处理器闭包已提供调用隔离。原持久化回调及载荷白名单保持不变。 -`veadk/cli/runtime_a2a_stream.py` 将 MPA 协议 artifact 帧的去重交给逐事件投影。首个部分的 ID 无法标识整个多部分帧。投影使用 `(task_id, invocation_id, event_type, source_event_id)`。通用帧标识保持不变。不涉及持久化结构、API、权限或依赖变更。集合仍局限于解码器/请求现有生命周期,断开或取消后释放。完成、失败、取消是独立语义,不因来源 ID 相同而互相去重。 -测试:`tests/cli/test_runtime_a2a_stream.py`、agentkit-mpa-agent 的 `tests/test_a2a_executor.py`。使用匿名 Python 到 Node 回放验证现有前端投影,不修改前端源码或资源。 - -## 任务与验收 -- T-1 / AC-1(FR-1/2):先补失败的离线测试,覆盖相同 ID 的用量/完成事件的两种顺序、重放去重、调用/任务隔离、缺失 ID、混合多部分重放、失败/取消。 -- T-2 / AC-2(FR-1/2/3):实施限定范围的转发器和解码器修正,保留原始传输载荷及通用去重。 -- T-3 / AC-3(FR-3):运行受影响 Python 测试、Ruff/Pyright、匿名前端实时/历史回放、范围内敏感信息扫描及空白检查。验证只有一个答案、推理保留及 token 正确,分别记录实际结果及未验证的部署。 - -## 评审、批准及风险 -review-spec 不可用,采用直接评审。已检查职责、兼容性、错误/终态、回调顺序、隐私、请求内生命周期、可测试性及双语一致性,无阻断项。用户在双仓库诊断后以“帮我改下”批准实施。提交的测试夹具不使用生产会话数据。 -风险:Studio 无法恢复上游未发送的事件,需部署两端修正才能完整生效;镜像部署不在本次请求范围。迟到的完全重放仍去重,不同事件继续展示。无需迁移,不修改历史持久化记录。 - -## 验证 -日期:2026-09-29。差异基线:VeADK `79acf209`;agentkit-mpa-agent `62cf960`。保留现有无关修改。结果如下。 - -### 结果 -代码修改的 T-1/T-2/T-3 已完成,未部署。以下检查在 2026-09-29 的工作区差异上运行。 -- `pass`:修复前,新增 Studio 测试中七项失败,转发器两项失败;缺失 ID 的兼容性测试通过。修复后,`uv run --extra dev pytest tests/cli/test_runtime_a2a_stream.py tests/cli/test_frontend_runtime_proxy.py -q` 通过 156 项(六条已有弃用警告)。 -- `pass`:agentkit-mpa-agent 中,`.venv/bin/python -m pytest tests/test_a2a_executor.py tests/test_a2a_codex_terminal.py tests/test_codex_delegation_tool.py -q` 通过 113 项。保留已有 ADK 实验性/弃用警告。 -- `pass`:`node --test frontend/tests/mpaResponseGrouping.test.mjs frontend/tests/mpaContentPreservation.test.mjs` 通过 22 项。匿名 Python 解码器 → 实际前端投影/分组回放,在实时及历史模式中均得到一个答案、一个保留的推理块,token 恰为 100。 -- `pass`:两个仓库各两个修改 Python 文件通过 Ruff 0.11.12 检查;Studio 格式检查通过;Studio `uv run --with pyright pyright veadk/cli/runtime_a2a_stream.py tests/cli/test_runtime_a2a_stream.py` 零错误/警告。Runtime Ruff 缓存写入被沙箱限制后,改用 `--no-cache` 检查。 -- `fail`(已有基线):Runtime 的 `app/a2a/executor.py` 和 `tests/test_a2a_executor.py` 经 Pyright 检查有 45 个错误。隔离的 HEAD 副本使用相同解释器及导入路径,得到同样的 45 条诊断,本次没有新增。无关类型清理不纳入本次范围。 -- `pass`:直接代码/设计评审确认原回调、白名单及通用去重保留;检查双语标识/链接、范围内 gitleaks 扫描及 `git diff --check`。 -- `not_applicable`:前端构建/生成资源、sidecar 门禁及新增布局/键盘/输入法检查;未修改前端或 sidecar 文件。 -- `not_run`:真实浏览器/云端端到端、镜像构建、部署、服务重启、提交和推送。验证为离线回放;现有 Runtime 镜像仍须替换才能启用上游转发修正,Studio 须重新加载解码器。未运行全仓回归,因为修正仅限显式 MPA 事件;已运行受影响的解码/代理/转发/终态及前端内容保留测试。 - -## Rebase 验证 — 2026-10-07 - -为 PR #17 与 main `e0448a4d` 协调。保留原功能补丁及上游发现/鉴权和 Worker 恢复改动。参见[组合协调及验证记录](../mpa-shared-network-bootstrap/2026-09-28-registry-owned-network.zh.md#pr-17-rebase-协调--2026-10-07)。 diff --git a/prd-spec/bugfixes/mpa-shared-network-bootstrap/2026-09-28-registry-owned-network.md b/prd-spec/bugfixes/mpa-shared-network-bootstrap/2026-09-28-registry-owned-network.md deleted file mode 100644 index 0be026101..000000000 --- a/prd-spec/bugfixes/mpa-shared-network-bootstrap/2026-09-28-registry-owned-network.md +++ /dev/null @@ -1,115 +0,0 @@ -# Registry-owned MPA network bootstrap - -[中文版](2026-09-28-registry-owned-network.zh.md) - -- Change ID: `mpa-shared-network-bootstrap` -- Created/revised: 2026-09-28 -- Status: implemented -- Contract: [Studio creation CON-1/2/8/13](../../../specs/studio-mpa-creation/README.md) - -## Background and scope - -`studio_profile.py` pins an existing VPC, subnet and APIG. Although -`AccountNetworkProvisioner` and `SharedAPIGService` already persist shared -resources, Studio adopts these fixed resources even when the management registry -is empty. The user requests that creation reuse registered resources or create -and persist missing resources in `mpa_admin_workspace/mpa_admin_db`. - -Goals: make the built-in Studio entry point use that existing bootstrap path. -Non-goals: network migration, deleting/resetting existing records or resources, -quota increases, changing sharing scope, Runtime retry recovery, database -permissions redesign, or changes to generic-agent chat/UI. - -## Requirements and design - -- FR-1: Remove the built-in VPC/subnet/APIG IDs. Keep the existing account, - region, images, model and credential requirements. Explicit CLI adoption is - unchanged. -- FR-2: After automatic PG preparation, use its resolved management URL for - `mpa_account_network`, `mpa_account_apig`, and deployment records. Share by - verified account and region. Empty registry: create VPC/subnet, then APIG and - IM Gateway, persist identities, then create the agent's dependent resources. -- FR-3: Existing registry: validate and reuse the registered network and gateway. - A partial preparation resumes using durable intent and known resource IDs. - Permission errors, unavailable resources, quota failures and uncertain creates - are not treated as missing records or successful creation. -- FR-4: Retain account locks and pre-dispatch persistence. Concurrent callers - must not duplicate resources; cancellation/timeout retains recorded resources. - No cloud resource deletion or registry rewrite occurs during this change. - -Use the existing provisioners, schemas and locks rather than adding another -resource registry or new selector. No HTTP/schema/frontend asset change is -required. Existing records (including an exhausted VPC) continue to be reused; -changing them requires a separately scoped migration. Automatic APIG creation -retains the existing Serverless request; APIG quotas and private database -connectivity remain deployment prerequisites. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| FR-1 | T-1 remove fixed resource defaults | AC-1 built-in profile has no adopted IDs | configuration regression fails before fix | -| FR-2/3 | T-2 exercise Studio profile through service with simulated PG/cloud | AC-2 shared records use admin DB; first call creates, next reuses | service regression | -| FR-3/4 | T-3 run recovery/concurrency tests | AC-3 uncertain creates and failures do not cause duplicate resources | network/gateway/PG suites | -| FR-1–4 | T-4 reconcile docs and review | AC-4 bilingual contract and operator guidance match | links, whitespace, Ruff/Pyright, managed regression | - -Affected files: `studio_profile.py`, managed config/service tests, Studio creation -spec pair, managed README pair, and `frontend/README.md` operator notes. - -## Review and approval - -The user's 2026-09-28 instruction explicitly approves registry-first reuse and -creation of missing shared VPC/APIG resources. It supersedes the earlier proposal -to move all new agents to a separate network. No additional approval is inferred -for editing live records or deleting resources. `review-spec` is unavailable; -direct design review checked scope, source-of-truth, compatibility, errors, -concurrency, security, testability and bilingual equivalence. No design blocker: -existing provisioners implement FR-2–4; the defect is the built-in selection. - -## Verification record - -Scope: working diff based on `79acf209`, 2026-09-28. - -- `pass`: test-first reproduction: the new configuration assertion and four - service scenarios failed with the old pinned resource IDs before the fix. -- `pass`: `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py tests/cli/test_frontend_deploy_iam.py -q --tb=short` - — 414 passed, five dependency deprecation warnings. -- `pass`: after test lint cleanup, - `uv run --extra dev pytest tests/integrations/mpa_managed/test_studio_shared_resources.py -q` - — four passed. -- `pass`: changed-Python-file Ruff and Pyright. Bare `uv run ruff`/`uv run pyright` - were unavailable; `uv run --with ruff --with pyright ` - used an isolated tool environment. Pyright: zero errors/warnings. Initial - uv-cache sandbox access required an approved escalation. No global settings or - project dependencies were changed; the user requested recording this here only. -- `pass`: `uv run --with pre-commit pre-commit run --files `: - repository-pinned Ruff check/format passed. The gitleaks staged hook passed but - does not cover unstaged files; a separate redacted directory scan covers all - changed/new files. YAML hook: `not_applicable`, no YAML changes. -- `pass`: `git diff --check`, new relative links and paired requirement/task/acceptance - IDs. Direct final review found no blocking code or bilingual inconsistency. -- `not_run`: live cloud smoke and real PostgreSQL integration. All new scenarios - simulate cloud/PG boundaries; they do not prove live permissions, quotas or - connectivity. No live registry/resource was changed. -- `not_applicable`: browser/frontend build, no UI or HTTP shape changes. Full - repository regression omitted in favor of the affected 414-test suite: the - production change only removes built-in resource selection defaults. - -T-1–4 and AC-1–4 are complete for the local code change. Existing registry entries -remain authoritative; restart local Studio or redeploy cloud Studio to load the -new defaults. No commit, push or deployment was performed. - -## PR #17 rebase reconciliation — 2026-10-07 - -User authorized conflict resolution against `origin/main` (`e0448a4d`). Direct review preserves main's Workload Identity initialization, Worker metadata waiting, MPA-first A2A discovery and `DISABLE_JWT_AUTH=true`, alongside this branch's registry selection, Worker naming and typed-event deduplication. No new production contract is introduced. Documentation retains both contributions; Worker test imports retain `_missing_worker_metadata`. The unrelated 512-line lockfile deletion is removed to preserve main's dependency records. - -The first offline combined regression produced 686 passes and four failures: the shared-resource fake cloud returns no credentials, while main now invokes `ensure_workload_identity`. Adapt this test boundary using an `AsyncMock` that returns distinct agent-derived workload identities, and assert initialization plus Runtime environment propagation. Keep production Identity initialization intact and retain all network/PG assertions. This is test-fixture compatibility within the approved rebase scope; real cloud operations remain excluded. Verification results follow after rerun. - -### Reconciliation results - -Scope: the rebased PR #17 diff on `e0448a4d`, 2026-10-07. -- `pass`: `uv run --extra dev pytest -n 2 tests/integrations/mpa_managed tests/cli/test_runtime_a2a_stream.py tests/cli/test_frontend_runtime_proxy.py tests/cli/test_cli_mpa.py tests/cli/test_cli_mpa_control.py tests/cli/test_mpa_a2a_url.py tests/integrations/test_mpa_provision_env.py -q` — 690 tests, 14 dependency deprecation warnings; two workers used. The four previously failing fixture cases pass after adapting the Identity boundary. -- `pass`: `node --test frontend/tests/mpaResponseGrouping.test.mjs frontend/tests/mpaContentPreservation.test.mjs frontend/tests/mpaSessionProtocol.test.mjs frontend/tests/runSseAbort.test.mjs` — 71 tests. -- `pass`: `uv run --extra dev pre-commit run --all-files`; changed Python files pass Pyright with zero errors/warnings; the adjusted fixture additionally passes Ruff/Pyright. -- `pass`: all Worker test functions from both sides are retained (13 combined); the three original production patches are unchanged relative to their respective bases; main's discovery/authentication and lockfile match exactly. Paired documents/relative links and diff whitespace checked. -- `not_run`: full-repository regression, real cloud/browser smoke and deployment. This is conflict reconciliation with a test-only dependency adapter; targeted provisioning/proxy/protocol suites establish the affected compatibility. No frontend source/generated assets or cloud resources changed. diff --git a/prd-spec/bugfixes/mpa-shared-network-bootstrap/2026-09-28-registry-owned-network.zh.md b/prd-spec/bugfixes/mpa-shared-network-bootstrap/2026-09-28-registry-owned-network.zh.md deleted file mode 100644 index 482ad7a4d..000000000 --- a/prd-spec/bugfixes/mpa-shared-network-bootstrap/2026-09-28-registry-owned-network.zh.md +++ /dev/null @@ -1,98 +0,0 @@ -# MPA 网络由共享注册库驱动创建 - -[English](2026-09-28-registry-owned-network.md) - -- 变更 ID:`mpa-shared-network-bootstrap` -- 创建/修订日期:2026-09-28 -- 状态:implemented -- 契约:[Studio 创建 CON-1/2/8/13](../../../specs/studio-mpa-creation/README.zh.md) - -## 背景与范围 - -`studio_profile.py` 固定了已有 VPC、子网和 APIG。虽然 -`AccountNetworkProvisioner` 与 `SharedAPIGService` 已支持共享资源持久化, -Studio 在管理注册库为空时仍会接管这些固定资源。用户要求创建流程从 -`mpa_admin_workspace/mpa_admin_db` 复用已登记资源,缺失时创建并保存。 - -目标:让 Studio 内置入口使用已有的自动准备流程。 -非目标:网络迁移、删除/重置已有记录或资源、提高配额、改变共享范围、修复 -Runtime 重试、重新设计数据库权限,以及修改通用智能体聊天或 UI。 - -## 需求与设计 - -- FR-1:移除内置 VPC/子网/APIG ID。保留现有账号、地域、镜像、模型及凭据要求。 - CLI 显式接管保持不变。 -- FR-2:自动准备 PG 后,以解析出的管理库连接保存 `mpa_account_network`、 - `mpa_account_apig` 和部署记录。按核验后的账号和地域共享。空注册库时先创建 - VPC/子网,再创建 APIG 和 IM Gateway、持久化资源 ID,再创建智能体相关资源。 -- FR-3:有记录时校验并复用已登记的网络和网关。部分完成时依据持久化意图和资源 - ID 恢复。权限错误、资源不可用、配额不足及创建结果未知不能当作记录缺失或成功。 -- FR-4:保留账号锁和调用前持久化。并发不能重复创建;取消/超时保留已记录资源。 - 本次不删除云资源、不改写现网注册记录。 - -复用已有 provisioner、表结构和锁,不增加另一套资源注册库或选择器。 -不改变 HTTP/表结构/前端产物。已有记录(包括配额耗尽的 VPC)仍复用;修改这些 -记录需要另行明确迁移范围。自动创建 APIG 保持已有 Serverless 请求;APIG 配额和 -私网数据库连通性仍是部署前提。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| FR-1 | T-1 移除固定资源默认值 | AC-1 内置配置不含接管 ID | 配置回归在修复前失败 | -| FR-2/3 | T-2 使用模拟 PG/云端执行 Studio 配置的 service 流程 | AC-2 共享记录使用管理库;首次创建、后续复用 | service 回归 | -| FR-3/4 | T-3 执行恢复/并发测试 | AC-3 不确定创建和失败不会重复创建资源 | 网络/网关/PG 测试集 | -| FR-1–4 | T-4 同步文档并审查 | AC-4 双语契约与操作说明一致 | 链接、空白、Ruff/Pyright、托管创建回归 | - -影响文件:`studio_profile.py`、托管配置/service 测试、Studio 创建规范双语文件、 -托管 README 双语文件,以及 `frontend/README.md` 的操作说明。 - -## 审查与批准 - -用户在 2026-09-28 明确要求按注册库复用、创建缺失的共享 VPC/APIG,已批准该范围。 -这取代此前将所有新智能体迁往另一套网络的建议,不意味着批准修改现网记录或 -删除资源。`review-spec` 不可用;已直接审查范围、数据来源、兼容性、错误、并发、 -安全、可测试性和双语一致性。无设计阻塞:已有 provisioner 实现 FR-2–4,缺陷在 -内置配置的资源选择。 - -## 验证记录 - -范围:基于 `79acf209` 的工作区 diff,2026-09-28。 - -- `pass`:先写测试复现;修复前,新的配置断言和四个 service 场景均因固定资源 ID 失败。 -- `pass`:`uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py tests/cli/test_frontend_deploy_iam.py -q --tb=short` - — 414 项通过,五条依赖弃用警告。 -- `pass`:测试 lint 收尾后,执行 - `uv run --extra dev pytest tests/integrations/mpa_managed/test_studio_shared_resources.py -q` - — 四项通过。 -- `pass`:改动 Python 文件的 Ruff 和 Pyright。直接 `uv run ruff`/`uv run pyright` - 不可用,使用 `uv run --with ruff --with pyright ` - 的隔离工具环境。Pyright 零错误/警告。首次 uv 缓存访问受沙箱限制,获准提权后运行。 - 未修改全局设置或项目依赖;按用户要求仅在此记录。 -- `pass`:`uv run --with pre-commit pre-commit run --files `: - 仓库固定版本 Ruff 检查/格式通过。gitleaks 暂存区钩子通过,但不覆盖未暂存文件; - 另以脱敏目录扫描覆盖全部改动/新增文件。YAML 钩子:`not_applicable`,无 YAML 变更。 -- `pass`:`git diff --check`、新增相对链接及双语需求/任务/验收 ID 核对。 - 最终直接审查未发现代码或双语一致性阻塞问题。 -- `not_run`:真实云端冒烟及真实 PostgreSQL 集成。新增场景模拟云/PG 边界,不能证明 - 现网权限、配额或连通性。本次未修改现网注册记录或资源。 -- `not_applicable`:浏览器/前端构建,无 UI 或 HTTP 结构变化。采用受影响的 414 项测试, - 未执行全仓回归:生产代码仅移除内置资源选择默认值。 - -本地代码变更的 T-1–4 与 AC-1–4 已完成。已有注册记录仍为准;重启本地 Studio -或重新部署云端 Studio 后加载新默认配置。本次未提交、推送或部署。 - -## PR #17 rebase 协调 — 2026-10-07 - -用户批准解决与 `origin/main`(`e0448a4d`)的冲突。直接评审保留 main 的 Workload Identity 初始化、Worker 元数据等待、MPA 优先 A2A 发现及 `DISABLE_JWT_AUTH=true`,同时保留本分支的注册库资源选择、Worker 命名及带类型的事件去重。不引入新的生产契约。文档保留双方内容,Worker 测试导入保留 `_missing_worker_metadata`。移除无关的 512 行锁文件删除,保留 main 的依赖记录。 - -首次离线组合回归得到 686 项通过、四项失败:共享资源测试的模拟云对象不返回凭据,而 main 现在会调用 `ensure_workload_identity`。在该测试边界使用 `AsyncMock` 返回按智能体区分的 workload identity,并断言初始化调用及 Runtime 环境传递。保持生产身份初始化逻辑和所有网络/PG 断言。这属于已批准 rebase 范围内的测试夹具适配,不进行真实云操作。重跑后补充验证结果。 - -### 协调结果 - -范围:2026-10-07,PR #17 在 `e0448a4d` 上的 rebase 差异。 -- `pass`:`uv run --extra dev pytest -n 2 tests/integrations/mpa_managed tests/cli/test_runtime_a2a_stream.py tests/cli/test_frontend_runtime_proxy.py tests/cli/test_cli_mpa.py tests/cli/test_cli_mpa_control.py tests/cli/test_mpa_a2a_url.py tests/integrations/test_mpa_provision_env.py -q` — 690 项,14 条依赖弃用警告,使用两个 worker。适配 Identity 边界后,先前失败的四项夹具场景通过。 -- `pass`:`node --test frontend/tests/mpaResponseGrouping.test.mjs frontend/tests/mpaContentPreservation.test.mjs frontend/tests/mpaSessionProtocol.test.mjs frontend/tests/runSseAbort.test.mjs` — 71 项。 -- `pass`:`uv run --extra dev pre-commit run --all-files`;修改的 Python 文件经 Pyright 检查零错误/警告;适配后的夹具额外通过 Ruff/Pyright。 -- `pass`:双方所有 Worker 测试函数均保留(合并后 13 个);三个原生产补丁相对各自基线不变;main 的发现/鉴权和锁文件完全保留。已检查双语文档/相对链接及 diff 空白。 -- `not_run`:全仓回归、真实云端/浏览器冒烟及部署。本次为冲突协调及仅测试依赖适配,目标创建/代理/协议测试验证受影响兼容性。未修改前端源码/生成资源或云资源。 diff --git a/prd-spec/bugfixes/mpa-skill-space-description/2026-10-09-entered-name-description.md b/prd-spec/bugfixes/mpa-skill-space-description/2026-10-09-entered-name-description.md deleted file mode 100644 index 35221f8f4..000000000 --- a/prd-spec/bugfixes/mpa-skill-space-description/2026-10-09-entered-name-description.md +++ /dev/null @@ -1,54 +0,0 @@ -# Use the entered MPA name in Skill Space descriptions - -[中文版](2026-10-09-entered-name-description.zh.md) - -- Change ID: `mpa-skill-space-description`; date: 2026-10-09; status: implemented. -- Contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), CON-3/CON-4. - -## Background and scope - -`managed/skills.py` currently creates `Description: Skills for MPA agent `. Studio creation now separates the entered Runtime name from the generated internal ID. A user identifying an agent by its entered name therefore cannot recognize the description. The user requested that this description use the entered name. - -Change only descriptions of newly created named Skill Spaces. Keep generated `mpa_skills_` names, `display_name` tags, IDs, ownership tags, database/channel/Worker bindings and internal ID generation unchanged. Existing/configured spaces are reused without updates. No cloud rename, migration, deployment, commit or push is in scope. Generic agents, UI, dependencies and permissions are unchanged. - -## Requirements, scenarios and acceptance - -- FR-1 / AC-1: A fresh named deployment creates `Description: Skills for MPA agent `. Use the existing Runtime-name validation. Without a name, preserve the original internal-ID description. -- FR-2 / AC-2: Forward `runtime_name` from Runtime deployment to Skill Space preparation. Space names, tags and bindings remain based on internal identity. Existing spaces require no create/update, regardless of the supplied display name. -- FR-3 / AC-3: Preserve unknown-outcome recovery: no blind second CreateSkillSpace. When a pre-upgrade persisted request matches the complete current request with only its description restored to the original internal-ID value, replay that persisted request for discovery/ownership verification. Reject all other pending-input changes, including changed new descriptions or projects. - -Scenarios: fresh named request has a recognizable description; unnamed CLI retains its behavior; an existing space remains untouched; a lost response from an old or new request is discovered exactly once; an undiscovered lost response or changed pending request remains an explicit error. - -## Design and affected contracts - -Add an optional keyword-only `runtime_name: str = ""` to `ensure_skill_space`, validate nonempty names with `validate_runtime_name`, and pass it from `RuntimeDeployer.deploy`. Description uses `runtime_name or agent_id`. The existing persisted `skill_space_request` already contains all recovery inputs; no field/schema migration is needed. Compare the full old-description candidate before permitting legacy recovery. Keep name discovery, ownership validation, locking, cancellation, timeout and resource cleanup rules unchanged. Description is presentation metadata and must not become an ownership key. - -Production: `managed/skills.py`, `managed/runtime.py`. Tests: `test_agent_deployment.py`, `test_service.py`. Update the bilingual creation spec and managed README. No frontend, generated assets, external API request-body or runtime image change. Optional Python keyword preserves callers. No credentials or live user data in fixtures/evidence. - -## Tasks and verification - -1. T-1 / FR-1–FR-3: Add failing regressions for named/unnamed descriptions, internal binding stability, name validation, existing reuse, old/new lost-response recovery and pending-input refusal. -2. T-2 / FR-1–FR-3: Implement name forwarding and exact legacy-description recovery. -3. T-3: Run targeted deployment/service tests, managed/CLI regression with two workers, changed-file Ruff/Pyright, pre-commit, bilingual/link and whitespace checks; review consistency and record results. - -Commands: `uv run --extra dev pytest tests/integrations/mpa_managed/test_agent_deployment.py tests/integrations/mpa_managed/test_runtime_deployment_edges.py tests/integrations/mpa_managed/test_service.py -n 2 -q`; `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -n 2 -q`; changed-file Ruff/Pyright; `uv run --extra dev pre-commit run --all-files`; `git diff --check`. - -## Review, approval and risks - -The user's request to replace the internal ID in the description with the entered name approves this narrow extension of the preceding naming fix. `review-spec` is unavailable; direct review checked request compatibility, full-payload recovery, ownership boundaries, errors and bilingual equivalence. No blockers. Old/existing spaces keep their descriptions; the improvement applies after restarting Studio to fresh creation. Display metadata does not imply a cloud ID or uniqueness guarantee. Live cloud operations are not authorized. Frontend/build/browser/sidecar gates are not applicable to this backend-only metadata change. - -## Delivery record - -T-1–T-3 and AC-1–AC-3 are complete. Direct implementation review verified that descriptions never authorize adoption, full legacy payload comparison preserves repeated unknown-outcome recovery, existing spaces are untouched, and changed pending inputs still fail. All pre-existing uncommitted changes, including the image-discovery rollback and Worker-name fix, are preserved separately. - -Tested on 2026-10-09: the four changed Python files and affected managed/CLI suites in the uncommitted diff based on `df797f94`. - -- `pass`: Regression-first checks reproduced six failures before implementation (two compatibility/default cases already passed). -- `pass`: Targeted deployment/edge/service command above: 182 tests. Final managed/CLI command with two workers: 686 tests, including named/unnamed creation, unchanged internal bindings, existing reuse, invalid names and old/new recovery. -- `pass`: Ruff 0.11.12 check/format on the four changed Python files; Pyright: zero errors/warnings. -- `pass`: `uv run --extra dev pre-commit run --all-files`, including both secret checks; bilingual pairs, contract identifiers and local design links; `git diff --check`. -- `not_run`: Full-repository Python regression; affected managed/CLI coverage was selected for this bounded metadata change. -- `not_run`: Real cloud creation/update; no live operations authorized or performed. Restart Studio before verifying a fresh creation in the console. Previously created spaces retain their original descriptions. -- `not_applicable`: Frontend/browser/build, sidecar and runtime-process smoke gates; no changes to those contracts or artifacts. - -No commit, push or deployment was performed. diff --git a/prd-spec/bugfixes/mpa-skill-space-description/2026-10-09-entered-name-description.zh.md b/prd-spec/bugfixes/mpa-skill-space-description/2026-10-09-entered-name-description.zh.md deleted file mode 100644 index 0de75c493..000000000 --- a/prd-spec/bugfixes/mpa-skill-space-description/2026-10-09-entered-name-description.zh.md +++ /dev/null @@ -1,54 +0,0 @@ -# 技能空间描述使用手填 MPA 名称 - -[English](2026-10-09-entered-name-description.md) - -- Change ID:`mpa-skill-space-description`;日期:2026-10-09;状态:implemented。 -- 契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-3/CON-4。 - -## 背景与范围 - -`managed/skills.py` 当前创建 `Description: Skills for MPA agent `。Studio 创建已将手填 Runtime 名称与自动生成内部 ID 分开。用户通过手填名称识别智能体时,难以辨认该描述。用户要求描述使用手填名称。 - -仅修改新建名称请求的技能空间描述。保持生成的 `mpa_skills_` 名称、`display_name` 标签、ID、归属标签、数据库/渠道/Worker 绑定及内部 ID 生成不变。已有/显式配置空间复用,不更新。不涉及云资源改名、迁移、部署、提交或推送。通用智能体、UI、依赖与权限不变。 - -## 需求、场景与验收 - -- FR-1 / AC-1:新名称部署创建 `Description: Skills for MPA agent `。沿用现有 Runtime 名称校验。不提供名称时保留原内部 ID 描述。 -- FR-2 / AC-2:Runtime 部署将 `runtime_name` 传给技能空间准备。空间名称、标签及绑定继续由内部标识派生。已有空间不创建/更新,与传入的展示名称无关。 -- FR-3 / AC-3:保留结果未知恢复,不盲目重复 CreateSkillSpace。升级前持久化请求仅在完整当前请求恢复为原内部 ID 描述后完全匹配时,才重放该请求用于发现和归属校验。其他未完成输入变化仍拒绝,包括新描述或项目变化。 - -场景:新名称请求描述可辨认;不提供名称的 CLI 保持原行为;已有空间不修改;旧/新请求丢失响应后准确发现且只创建一次;无法发现的丢失响应或改变未完成请求仍明确报错。 - -## 设计与受影响契约 - -为 `ensure_skill_space` 增加可选仅关键字参数 `runtime_name: str = ""`,非空时使用 `validate_runtime_name` 校验,由 `RuntimeDeployer.deploy` 传入。描述使用 `runtime_name or agent_id`。已有持久化 `skill_space_request` 包含完整恢复输入,不增加字段或数据库迁移。仅在完整旧描述候选匹配时允许旧版恢复。名称发现、归属验证、锁、取消、超时和资源清理保持不变。描述属于展示信息,不得作为归属键。 - -生产文件:`managed/skills.py`、`managed/runtime.py`。测试:`test_agent_deployment.py`、`test_service.py`。更新双语创建契约和 managed README。不修改前端、生成产物、外部 API 请求体或 Runtime 镜像。可选 Python 关键字兼容调用方。测试/证据不含凭证或真实用户数据。 - -## 任务与验证 - -1. T-1 / FR-1–FR-3:补充先失败回归,覆盖有/无名称描述、内部绑定稳定、名称校验、已有复用、旧/新丢失响应恢复及未完成输入变化拒绝。 -2. T-2 / FR-1–FR-3:实现名称传入及完整旧描述恢复。 -3. T-3:运行部署/服务目标测试、使用两个进程的 managed/CLI 回归、改动文件 Ruff/Pyright、pre-commit、双语/链接及空白检查;审查一致性并记录结果。 - -命令:`uv run --extra dev pytest tests/integrations/mpa_managed/test_agent_deployment.py tests/integrations/mpa_managed/test_runtime_deployment_edges.py tests/integrations/mpa_managed/test_service.py -n 2 -q`;`uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -n 2 -q`;改动文件 Ruff/Pyright;`uv run --extra dev pre-commit run --all-files`;`git diff --check`。 - -## 审查、批准与风险 - -用户要求描述中的内部 ID 改为手填名称,批准对前序命名修复的这一小范围扩展。`review-spec` 不可用;直接审查核对请求兼容、完整请求恢复、归属边界、错误及双语等价,无阻塞。旧/已有空间保留原描述;重启 Studio 后对新建请求生效。展示信息不代表云 ID 或唯一性保证。未授权真实云操作。本次仅后端展示元数据变化,前端/构建/浏览器/sidecar 检查不适用。 - -## 交付记录 - -T-1–T-3 和 AC-1–AC-3 已完成。直接实施审查确认描述不授权接管、完整旧请求比较保留连续结果未知恢复、已有空间不修改、改变未完成输入仍失败。此前所有未提交修改,包括镜像查询撤回和 Worker 名称修复,独立保留。 - -2026-10-09 测试范围:基于 `df797f94` 的未提交差异中四个 Python 文件及受影响的 managed/CLI 测试。 - -- `pass`:先失败回归在实施前复现 6 项失败(两项兼容/默认场景已通过)。 -- `pass`:上述部署/边界/服务目标命令 182 项通过;最终 managed/CLI 命令使用两个进程,686 项通过,包含有/无名称创建、内部绑定不变、已有复用、非法名称及旧/新恢复。 -- `pass`:四个改动 Python 文件的 Ruff 0.11.12 check/format;Pyright 零错误/警告。 -- `pass`:`uv run --extra dev pre-commit run --all-files`,包含两项密钥检查;双语配对、契约标识及本地设计链接;`git diff --check`。 -- `not_run`:全仓 Python 回归;针对本次有限元数据变更,选择受影响的 managed/CLI 覆盖。 -- `not_run`:真实云创建/更新,未授权也未执行。控制台验证新的创建前应重启 Studio;此前创建的空间保留原描述。 -- `not_applicable`:前端/浏览器/构建、sidecar 及运行时进程 smoke;没有修改这些契约或产物。 - -未执行提交、推送或部署。 diff --git a/prd-spec/bugfixes/mpa-standard-gateway/2026-10-09-standard-shared-gateway.md b/prd-spec/bugfixes/mpa-standard-gateway/2026-10-09-standard-shared-gateway.md deleted file mode 100644 index 97eacd5e3..000000000 --- a/prd-spec/bugfixes/mpa-standard-gateway/2026-10-09-standard-shared-gateway.md +++ /dev/null @@ -1,57 +0,0 @@ -# Standard shared APIG creation - -[中文版](2026-10-09-standard-shared-gateway.zh.md) - -Change ID: mpa-standard-gateway. Created/revised: 2026-10-09. Status: implemented. - -## Evidence, goals and boundaries - -Read-only investigation confirmed a successful historical shared MPA gateway is standard, with two 1c2g nodes, small_1 CLB, public/private networking and traffic billing. The new account has exhausted its Serverless gateway count while standard count has headroom. Current managed creation hardcodes serverless and prepares only one subnet. The provider [CreateGateway contract](https://docs.volcengine.com/docs/apig/CreateGateway?lang=en) requires at least two subnets in different zones. [GetGatewayAvailableZones](https://docs.volcengine.com/docs/apig/GetGatewayAvailableZones-QuerythecurrentRegionAvailabilityZonelist?lang=zh) supplies APIG-supported zones, which can differ from ECS zones. - -Goal: new managed gateways use the working standard configuration, prepare an appropriate cross-zone network and recover definite quota rejection. Non-goals: frontend changes, general-agent chat, moving existing Runtimes, deleting resources, switching cloud accounts, copying old-account resource IDs, upgrading images or automatically increasing quota. Real creation remains a separate user submission. Existing standard/serverless registrations and explicit adoption retain compatibility. - -## Requirements and scenarios - -- FR-1: CreateGateway uses standard, Replicas=2, InstanceSpecCode=1c2g, CLBSpecCode=small_1, both public/private enabled and traffic billing. Reject fewer than two or duplicate subnet IDs before dispatch. -- FR-2: For a new gateway without registered/adopted ID, query APIG zones and prepare two available same-VPC subnets in distinct supported zones under the existing account lock. Reuse eligible subnets first, create only missing companions with nonoverlapping CIDRs. Include ready GetSubnet results in CIDR reservations when DescribeSubnets lags. Preserve VPC and all existing resources. -- FR-3: Each companion subnet has its own durable intent/token/dispatch flag and stable scope/zone name. Timeouts/cancellation/unknown results require discovery before another Create; retries retain payload and token. Conflicts, malformed zones, ownership drift, CIDR exhaustion and fewer than two APIG zones fail closed. A recorded pending companion remains authoritative even if another suitable subnet appears. -- FR-4: Existing gateway reuse/adoption does not require new zone discovery or network changes. Existing Runtime network payloads stay unchanged; new Runtime payloads use the prepared gateway subnet selection. Management registry records are the source of truth. -- FR-5: ExceededQuota is a definite provider rejection for gateway and IM creation; save create_requested/im_create_requested=false and allow same-agent retry after correction. Timeout/internal/unknown errors retain duplicate protection. A historical unknown flag is not automatically cleared. A one-off recovery may clear only the audited rejected intent under the shared lock after checking the exact scope/VPC/name and absence of the gateway; it must not delete resources or reset arbitrary intents. -- FR-6: Deployment IAM policy includes the read-only apig:GetGatewayAvailableZones action. Runtime role policy and other permissions remain unchanged. No credentials or provider payloads enter user output, source or reports. - -Scenarios: fresh shared setup creates two cross-zone subnets and one standard gateway/IM service; a recorded single-subnet VPC adds only its missing companion; separate agents reuse the same admin registry; a lost companion Create response resumes by exact name; quota rejection is recoverable; an uncertain gateway outcome still blocks duplication; existing registered gateways/Runtimes retain their network. - -## Design and contract impact - -`gateway_cloud.py` owns the standard request and APIG zone adapter. `network.py` adds a gateway-specific preparation method using the same entry lock and persists companion intents inside gateway_subnet_intents keyed by zone. Existing single-subnet preparation remains unchanged for independent Runtime callers. `service.py` invokes companion preparation only when no gateway is registered/adopted, passes gateway subnets independently, and only changes a fresh Runtime's template network. `gateway.py` classifies the definite quota rejection. `frontend_deploy_policy.py` grants the one new read action. No public HTTP/API/CLI fields, configuration or tables change; the JSON registry gains optional companion intents. All provider calls refresh and verify credentials. - -Owner contracts: [Studio creation](../../../specs/studio-mpa-creation/README.md) and [Runtime provisioning](../../../specs/mpa-runtime-provisioning/README.md). Update both languages and managed module READMEs. Frontend/browser/assets/sidecar gates are not_applicable because their interfaces/behavior are unchanged. Isolated tests cover actual request shape, orchestration and recovery; live provisioning is reported separately. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | -| --- | --- | --- | -| FR-1, FR-6 | T-1: failing adapter/policy regressions then standard request/zones | AC-1: standard payload and minimum IDs, strict zones, deployment permission | -| FR-2–4 | T-2: failing companion/reuse/cancellation tests then durable preparation and entry-point wiring | AC-2: new setup/one-subnet recovery/repeated agents, no existing Runtime changes or duplicate subnet creation | -| FR-5 | T-3: failing quota recovery tests then definite-error handling | AC-3: gateway/IM quota retries allowed, unknown outcomes remain blocked | -| FR-1–6 | T-4: docs, targeted/regression tests and review | AC-4: affected tests, Ruff/Pyright, secret/diff/bilingual/link checks pass | - -Commands: `uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py tests/cli/test_frontend_deploy_iam.py -q`; changed-file Ruff/Pyright/pre-commit. No commit, push, deployment, destructive cleanup or live creation is part of verification. - -## Review, approval and risks - -Direct review because review-spec is unavailable: scoped ownership, cross-zone requirements, independent intents, locks, partial failures, unchanged Runtime compatibility, permissions and bilingual equivalence reviewed. User approved the standard gateway/two-zone/IM/registry/retry solution with “帮我改”. The new read action needs deployment permission; empty/failed zone lookup is not silently substituted with ECS results. New standard resources have their provider billing; quota/availability errors can still occur and remain explicit failures. Pending historical flags remain conservative until audited operator recovery. No design blockers remain. - -## Verification and delivery - -2026-10-09, tested working tree based on ef0ad519 plus this uncommitted gateway correction and preceding approved account/IAM/network fixes; unrelated user changes preserved. T-1–T-4 and AC-1–AC-4 are satisfied for isolated implementation verification. - -- pass: test-first adapter/quota baseline (13 expected failures, 14 passes), companion/policy baseline (12 expected failures, 64 passes), and delayed-list CIDR regression (one expected failure), then implementation. -- pass: `uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py tests/cli/test_frontend_deploy_iam.py -q --tb=short`: 578 passed, five existing dependency deprecation warnings. Covers standard payload, distinct APIG zones, reuse across agents, cancellation/lost-response recovery, authoritative pending intent, ownership/zone/account drift, CIDR exhaustion and delayed lists, quota recovery and unchanged existing Runtime network. -- pass: `uvx --from ruff==0.11.12 ruff check` and `ruff format --check` for the 12 affected Python source/test files; `uvx pyright --pythonpath .venv/bin/python` for gateway_cloud.py, gateway.py, network.py, service.py and frontend_deploy_policy.py (zero errors). -- pass: `uv run --extra dev pre-commit run --files `: Ruff check/format and hardcoded secret scan. Concrete-YAML secret gate not_applicable (no YAML changes). -- pass: paired-language, relative-path/reference and diff whitespace checks; direct implementation review found no remaining blockers. Preserve scope, registry locks and Runtime network compatibility; cancellation does not roll back resources. -- pass (live data repair only): under the shared account/region lock, reverified deployment STS identity, failed local task/no active creation, exact provider ExceededQuota audit and absence of the scoped gateway; cleared only that rejected create_requested flag and verified readback. No cloud resource was created/deleted and no other record was changed. Unknown historical intents have no automatic bypass. -- not_run: real standard gateway/IM/Runtime creation; the user must submit after restarting local Studio (or redeploying cloud Studio). Provider quota, availability and permissions remain live checks. -- not_run: full repository parallel regression and all-files commit gate; verification covered affected creation/provisioning/CLI regressions. The earlier broad run has unrelated SDK dependency/collection failures recorded in the IAM design. No commit was requested. -- not_applicable: frontend/browser/build/assets/sidecar gates because no frontend or event/chat interface changed. diff --git a/prd-spec/bugfixes/mpa-standard-gateway/2026-10-09-standard-shared-gateway.zh.md b/prd-spec/bugfixes/mpa-standard-gateway/2026-10-09-standard-shared-gateway.zh.md deleted file mode 100644 index 194b174be..000000000 --- a/prd-spec/bugfixes/mpa-standard-gateway/2026-10-09-standard-shared-gateway.zh.md +++ /dev/null @@ -1,57 +0,0 @@ -# 标准型共享 APIG 创建 - -[English](2026-10-09-standard-shared-gateway.md) - -变更 ID:mpa-standard-gateway。创建/修订:2026-10-09。状态:implemented。 - -## 证据、目标与边界 - -只读排查确认,历史成功的 MPA 共享网关为标准型,两个 1c2g 节点、small_1 CLB、公私网启用、按流量计费。新账号 Serverless 网关数量已达限额,标准型数量仍有余量。当前托管创建硬编码 serverless,且只准备一个子网。提供方 [CreateGateway 契约](https://docs.volcengine.com/docs/apig/CreateGateway?lang=en)要求至少两个不同可用区的子网。[GetGatewayAvailableZones](https://docs.volcengine.com/docs/apig/GetGatewayAvailableZones-QuerythecurrentRegionAvailabilityZonelist?lang=zh) 提供 APIG 支持的可用区,可能与 ECS 可用区不同。 - -目标:新建托管网关采用已验证的标准型配置,准备适合的跨可用区网络,并恢复明确的配额拒绝。非目标:修改前端、通用智能体聊天、迁移现有 Runtime、删除资源、切换云账号、复制旧账号资源 ID、升级镜像或自动申请提额。真实创建由用户另行提交。已有标准型/Serverless 登记和显式接管保持兼容。 - -## 需求与场景 - -- FR-1:CreateGateway 使用 standard、Replicas=2、InstanceSpecCode=1c2g、CLBSpecCode=small_1、公私网均启用、按 traffic 计费。少于两个或重复子网 ID 在发送前拒绝。 -- FR-2:没有登记/接管 ID 的新网关,在现有账号锁内查询 APIG 可用区,准备同一 VPC 内两个不同受支持可用区的可用子网。优先复用合适子网,只创建缺失的伴随子网,CIDR 不重叠。DescribeSubnets 延迟时,计入 GetSubnet 已确认就绪的子网网段。保留 VPC 及所有现有资源。 -- FR-3:每个伴随子网有独立的持久化意图/token/发送标记和稳定的范围/可用区名称。超时/取消/未知结果在再次 Create 前必须查询恢复;重试保留载荷和 token。冲突、畸形可用区、归属变化、CIDR 耗尽及 APIG 可用区不足两个均明确失败。已记录的未完成伴随意图保持权威,即使出现其他合适子网也不能跳过。 -- FR-4:已有网关复用/接管不要求新可用区查询或网络变更。已有 Runtime 网络载荷保持不变;新 Runtime 载荷使用准备好的网关子网选择。管理注册库记录为事实来源。 -- FR-5:网关和 IM 创建的 ExceededQuota 属于提供方明确拒绝,保存 create_requested/im_create_requested=false,修正后允许同智能体重试。超时/内部/未知错误保留防重复保护。历史未知标记不自动清除。一次性恢复只有在共享锁内核实明确拒绝的审计、精确范围/VPC/名称及网关不存在后才可清除该意图;不得删除资源或重置任意意图。 -- FR-6:部署 IAM 策略增加只读 apig:GetGatewayAvailableZones。Runtime 角色策略和其他权限不变。凭据及提供方载荷不得进入用户输出、源码或报告。 - -场景:新共享环境创建两个跨可用区子网和一个标准网关/IM 服务;已记录单子网 VPC 只补齐缺失子网;不同智能体复用同一管理注册库;伴随子网 Create 响应丢失后按精确名称恢复;配额拒绝可恢复;网关未知结果仍阻止重复;已登记网关/Runtime 保留网络。 - -## 设计与契约影响 - -`gateway_cloud.py` 负责标准型请求和 APIG 可用区适配。`network.py` 添加网关专用准备方法,复用 entry 锁,并在 gateway_subnet_intents 中按可用区持久化伴随意图。独立 Runtime 调用方的单子网准备保持不变。`service.py` 仅在没有已登记/接管网关时调用伴随准备,独立传递网关子网,仅修改新 Runtime 模板网络。`gateway.py` 识别明确配额拒绝。`frontend_deploy_policy.py` 增加一个只读动作。不修改公开 HTTP/API/CLI 字段、配置或表;JSON 注册记录增加可选伴随意图。所有提供方调用刷新并核验凭据。 - -归属契约:[Studio 创建](../../../specs/studio-mpa-creation/README.zh.md)及 [Runtime 部署](../../../specs/mpa-runtime-provisioning/README.zh.md)。同步双语及托管模块 README。前端/浏览器/资源/sidecar 检查为 not_applicable,因其接口/行为未改。隔离测试覆盖真实请求形态、编排及恢复;真实部署单独报告。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | -| --- | --- | --- | -| FR-1、FR-6 | T-1:先失败的适配器/策略回归,再标准型请求/可用区 | AC-1:标准型载荷和最少 ID、严格可用区、部署权限 | -| FR-2–4 | T-2:先失败的伴随/复用/取消测试,再持久准备及入口接线 | AC-2:新环境/单子网恢复/多智能体,无现有 Runtime 变更或重复创建 | -| FR-5 | T-3:先失败的配额恢复测试,再明确错误处理 | AC-3:网关/IM 配额允许重试,未知结果仍阻止 | -| FR-1–6 | T-4:文档、专项/回归及审查 | AC-4:相关测试、Ruff/Pyright、密钥/差异/双语/链接通过 | - -命令:`uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py tests/cli/test_frontend_deploy_iam.py -q`;修改文件的 Ruff/Pyright/pre-commit。验证不包含提交、推送、部署、破坏性清理或真实创建。 - -## 审查、批准与风险 - -review-spec 不可用,直接审查:范围归属、跨可用区要求、独立意图、锁、部分失败、Runtime 兼容不变、权限及双语等价均已审查。用户以“帮我改”批准标准型网关/双可用区/IM/注册库/重试方案。新只读动作需要部署权限;可用区为空/查询失败时不静默替换为 ECS 结果。新标准型资源按提供方计费;配额/资源不足仍可能发生,并保留明确失败。历史未完成标记在审计确认的操作恢复前保持保守。无设计阻塞项。 - -## 验证与交付 - -2026-10-09,测试范围为 ef0ad519 工作区加本次未提交网关修复及此前已批准的账号/IAM/网络修复;保留无关用户修改。隔离实现验证满足 T-1–T-4、AC-1–AC-4。 - -- pass:先运行适配器/配额基线(13 个预期失败、14 个通过)、伴随子网/策略基线(12 个预期失败、64 个通过)及列表延迟 CIDR 回归(一个预期失败),然后实现。 -- pass:`uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py tests/cli/test_frontend_deploy_iam.py -q --tb=short`:578 个通过,五条已有依赖弃用警告。覆盖标准型载荷、APIG 不同可用区、多智能体复用、取消/响应丢失恢复、未完成意图权威性、归属/可用区/账号变化、CIDR 耗尽及列表延迟、配额恢复和已有 Runtime 网络不变。 -- pass:12 个相关 Python 源码/测试文件执行 `uvx --from ruff==0.11.12 ruff check`、`ruff format --check`;gateway_cloud.py、gateway.py、network.py、service.py、frontend_deploy_policy.py 执行 `uvx pyright --pythonpath .venv/bin/python`,零错误。 -- pass:`uv run --extra dev pre-commit run --files ` 的 Ruff 检查/格式及硬编码密钥扫描。具体 YAML 密钥检查 not_applicable(没有 YAML 修改)。 -- pass:双语配对、相对路径/引用、差异空白检查;直接实现审查无剩余阻塞项。保留范围、注册库锁和 Runtime 网络兼容;取消不回滚资源。 -- pass(仅真实数据修复):在共享账号/地域锁内,重新核验部署 STS 身份、本地失败任务/无运行中创建、精确的 ExceededQuota 提供方审计及范围内网关不存在;仅清除这次明确拒绝的 create_requested 标记并回读核验。未创建/删除云资源,未修改其他记录。历史未知意图没有自动绕过。 -- not_run:真实标准网关/IM/Runtime 创建;用户需重启本地 Studio(云端重新部署)后提交。提供方配额、资源及权限仍需实际检查。 -- not_run:全仓库并行回归及全文件提交检查;本次覆盖相关创建/部署/CLI 回归。此前全量运行的无关 SDK 依赖/收集失败已记录在 IAM 设计中。未收到提交请求。 -- not_applicable:前端/浏览器/构建/资源/sidecar 检查,没有前端或事件/聊天接口变化。 diff --git a/prd-spec/bugfixes/mpa-studio-account/2026-10-09-sts-account.md b/prd-spec/bugfixes/mpa-studio-account/2026-10-09-sts-account.md deleted file mode 100644 index 5461c4c51..000000000 --- a/prd-spec/bugfixes/mpa-studio-account/2026-10-09-sts-account.md +++ /dev/null @@ -1,53 +0,0 @@ -# Studio deployment account from STS - -[中文版](2026-10-09-sts-account.zh.md) - -Change ID: mpa-studio-account. Created/revised: 2026-10-09. Status: implemented. - -## Evidence and scope - -Built-in Studio configures a fixed expected account. A new-account credential passes STS but fails the `checking` stage in `managed/service.py` before IAM or resource preparation. The user explicitly requested removal of this built-in account restriction on 2026-10-09. No real credentials or task payloads belong in this document. - -## Requirements and scenarios - -- FR-1: Studio has no expected account; a nonempty STS identity determines the deployment account for IAM, PG, network, APIG and Runtime ownership. -- FR-2: Fresh Runtime `CLAW_SPACE_ID` and `RUNTIME_IAM_ROLE_TRN` derive from the verified account and selected role. No old-account environment value overrides them in built-in defaults. -- FR-3: Explicit YAML account restrictions, caller environment overrides, referenced/template Runtime behavior and the existing check against credential account changes remain compatible. -- FR-4: Image registry addresses and Worker reference IDs are source resources, not deployment identities. Preserve their defaults; do not assume source resources exist in a new account or claim cross-account access is verified. - -Given a different valid STS account, built-in provisioning reaches IAM preparation and uses that account in Runtime environment values. Given an explicit YAML expected-account mismatch, creation still stops before mutation. Given blank STS identity or account changes during creation, fail closed as before. - -## Design and affected contracts - -Remove built-in `account-id` and fixed account-derived environment fields. Keep the image registry as an explicitly named source registry. Fresh templates generate role name/TRN using the selected Runtime role and verified account; existing `apply_runtime_settings` overrides remain authoritative. `build_runtime_env` already derives `CLAW_SPACE_ID` from STS account, so reuse it. - -Contracts: [Studio creation](../../../specs/studio-mpa-creation/README.md), [Runtime provisioning](../../../specs/mpa-runtime-provisioning/README.md). No API, database schema, frontend, task state or permission changes. No cloud mutations, deployment, process restart, commit or push are included. Cancellation and resource cleanup remain unchanged. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| FR-1, FR-2 | T-1: regression tests, profile/template fix | AC-1: simulated built-in creation without an account override uses fake STS account through IAM and Runtime | managed config/shared-resource tests | -| FR-3 | T-2: boundary regressions | AC-2: explicit mismatch fails before IAM; selected role derives correct TRN | service tests | -| FR-4 | T-3: bilingual contracts and usage | AC-3: preserved image/reference defaults, documented access limitation | document and diff checks | - -Affect `managed/studio_profile.py`, `managed/service.py`, managed tests, both component specs and managed READMEs. Run `uv run --extra dev pytest tests/integrations/mpa_managed`, changed-file Ruff/Pyright and secret scanning. Frontend/browser/build are not applicable because UI and web assets are unchanged by this fix. Live cloud creation is not_run without an isolated authorized test. - -## Review, approval and risks - -Direct design review (review-spec skill unavailable): requirements and bilingual contracts agree; preserve explicit CLI safety checks, credential-change protection and overrides; no secret output or implicit source-resource cloning. No blockers. User instruction to remove the built-in restriction approves this bounded fix. New-account source-image/reference access can still fail later; this fix does not certify the full new-account deployment chain. Verification recorded below. - -## Verification record - -2026-10-09, working-tree account fix on base `ef0ad519`; previous IAM work and unrelated lockfile changes retained. - -- pass: pre-fix profile/shared-resource regression, 5 expected failures reproduced the fixed-account guard; after implementation the same flow passes with simulated STS identity, IAM preparation and account-derived Runtime metadata. -- pass: `uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py -q`: 496 passed. A new role test initially used an invalid fixture MPA ID; corrected to the supported format, then the full target passed. -- pass: `uvx --from ruff==0.11.12 ruff check` and repository pre-commit Ruff format/check for the five changed Python files; production Pyright initially passed. Test Pyright found two pre-existing fixture typing issues and one new missing AsyncMock assertion; fixed minimally, final check recorded below. -- pass: changed-file `uv run --extra dev pre-commit run --files` including Python, bilingual specs, managed README and this design: Ruff and hardcoded-secret scan passed; YAML scan not_applicable (no YAML changes). -- not_applicable: frontend tests/build/browser, general-agent chat and sidecar gates; this fix changes no frontend/chat/sidecar contracts or generated assets. -- not_run: full repository regression and all-files pre-commit; no commit requested, narrow provisioning regressions cover this fix. Live new-account creation, image/reference access and server restart are not_run; no cloud mutation was performed. - -Review: existing CLI account assertion is retained for explicitly configured profiles; built-in profile omits the assertion input. Verified account remains the registry scope and credential-change guard. Additional fixture assertions/monkeypatch only repair typing in the affected tests. Both document languages and relative links are reconciled. - -- pass: final `uvx pyright --pythonpath .venv/bin/python` for both production files and all three affected test files: 0 errors, 0 warnings. Post-fixture `uv run --extra dev pytest tests/integrations/mpa_managed/test_service.py tests/integrations/mpa_managed/test_studio_shared_resources.py -q`: 70 passed. Final changed-file pre-commit passed. diff --git a/prd-spec/bugfixes/mpa-studio-account/2026-10-09-sts-account.zh.md b/prd-spec/bugfixes/mpa-studio-account/2026-10-09-sts-account.zh.md deleted file mode 100644 index 44b6e556a..000000000 --- a/prd-spec/bugfixes/mpa-studio-account/2026-10-09-sts-account.zh.md +++ /dev/null @@ -1,53 +0,0 @@ -# Studio 使用 STS 核验的部署账号 - -[English](2026-10-09-sts-account.md) - -变更 ID:mpa-studio-account。创建/修订日期:2026-10-09。状态:implemented。 - -## 证据与范围 - -内置 Studio 配置固定的预期账号。新账号凭据通过 STS 后,在 `managed/service.py` 的 `checking` 阶段失败,尚未准备 IAM 或其他资源。用户在 2026-10-09 明确要求去掉该内置账号限制。本文件不包含真实凭据或任务载荷。 - -## 需求与场景 - -- FR-1:Studio 不预设账号;非空 STS 身份决定 IAM、PG、网络、APIG 和 Runtime 归属的部署账号。 -- FR-2:新建 Runtime 的 `CLAW_SPACE_ID` 和 `RUNTIME_IAM_ROLE_TRN` 由核验账号和所选角色生成。内置默认值不以旧账号环境变量覆盖它们。 -- FR-3:保留显式 YAML 账号限制、调用方环境变量覆盖、引用/模板 Runtime 行为以及创建期间凭据切换账号的现有检查。 -- FR-4:镜像仓库地址和 Worker 参考 ID 是源资源,不是部署身份。保留默认值,不假定新账号存在这些源资源,不宣称已验证跨账号访问。 - -当 STS 返回另一个有效账号,内置创建应进入 IAM 准备,并在 Runtime 环境变量中使用该账号。当显式 YAML 预期账号不匹配时,仍在资源变更前停止。STS 身份为空或创建中账号变化时,继续按原规则拒绝执行。 - -## 设计与受影响契约 - -移除内置 `account-id` 和固定账号派生的环境字段。镜像仓库保留为名称明确的源仓库。新建模板使用所选 Runtime 角色和核验账号生成角色名/TRN;现有 `apply_runtime_settings` 的显式覆盖保持优先。`build_runtime_env` 已根据 STS 账号派生 `CLAW_SPACE_ID`,直接复用。 - -契约:[Studio 创建](../../../specs/studio-mpa-creation/README.zh.md)、[Runtime 部署](../../../specs/mpa-runtime-provisioning/README.zh.md)。不改变 API、数据库结构、前端、任务状态或权限。不包含云资源变更、部署、进程重启、提交或推送。取消及资源清理行为保持不变。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| FR-1、FR-2 | T-1:回归测试及配置/模板修复 | AC-1:模拟内置创建无需覆盖账号,IAM 和 Runtime 使用模拟 STS 账号 | managed 配置/共享资源测试 | -| FR-3 | T-2:边界回归 | AC-2:显式账号不匹配在 IAM 前失败;所选角色派生正确 TRN | service 测试 | -| FR-4 | T-3:双语契约及使用说明 | AC-3:保留镜像/参考默认值并记录访问限制 | 文档及差异检查 | - -涉及 `managed/studio_profile.py`、`managed/service.py`、managed 测试、两组组件契约和 managed README。运行 `uv run --extra dev pytest tests/integrations/mpa_managed`、变更文件 Ruff/Pyright 和密钥扫描。本次修复不修改 UI 或 web 产物,前端/浏览器/构建检查不适用。未获得隔离真实测试授权时,云端创建为 not_run。 - -## 审查、批准与风险 - -直接设计审查(review-spec 技能不可用):需求与双语契约一致;保留显式 CLI 安全检查、凭据账号变化保护和覆盖规则;不输出密钥,不隐式复制源资源。无阻塞项。用户要求移除内置限制,批准本次限定修复。新账号仍可能在后续遇到源镜像/参考模板访问失败,本次修复不证明完整新账号部署链路。验证记录见下。 - -## 验证记录 - -2026-10-09,基于 `ef0ad519` 工作区的账号修复;保留此前 IAM 工作及无关 lockfile 改动。 - -- pass:修复前配置/共享资源回归出现 5 个预期失败,复现固定账号拦截;实现后相同流程使用模拟 STS 身份,通过 IAM 准备和 Runtime 账号元数据检查。 -- pass:`uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py -q`:496 项通过。新增角色测试最初使用了无效 MPA ID 夹具,改为支持的格式后完整目标通过。 -- pass:五个变更 Python 文件的 `uvx --from ruff==0.11.12 ruff check` 和仓库 pre-commit Ruff 格式/检查;生产代码 Pyright 首轮通过。测试 Pyright 发现两处既有夹具类型问题及一处新增 AsyncMock 缺失断言,均做最小修复,最终结果见下。 -- pass:包含 Python、双语契约、managed README 和本设计的变更文件 `uv run --extra dev pre-commit run --files`:Ruff 和硬编码密钥扫描通过;YAML 扫描 not_applicable(未修改 YAML)。 -- not_applicable:前端测试/构建/浏览器、通用智能体聊天及 sidecar 门禁;本修复不改动前端/聊天/sidecar 契约或生成产物。 -- not_run:全仓回归和 all-files pre-commit;没有提交请求,限定部署回归覆盖本修复。真实新账号创建、镜像/参考资源访问及服务重启为 not_run;未执行云资源变更。 - -审查:显式配置仍保留 CLI 账号断言;内置配置不提供断言输入。核验账号继续决定注册表范围及凭据账号变化保护。额外夹具断言/monkeypatch 仅修复受影响测试的类型问题。双语文档及相对链接已同步。 - -- pass:最终对两个生产文件及三个受影响测试文件执行 `uvx pyright --pythonpath .venv/bin/python`:0 错误、0 警告。夹具修正后执行 `uv run --extra dev pytest tests/integrations/mpa_managed/test_service.py tests/integrations/mpa_managed/test_studio_shared_resources.py -q`:70 项通过。最终变更文件 pre-commit 通过。 diff --git a/prd-spec/bugfixes/mpa-subnet-order/2026-10-09-subnet-order.md b/prd-spec/bugfixes/mpa-subnet-order/2026-10-09-subnet-order.md deleted file mode 100644 index 9720a92df..000000000 --- a/prd-spec/bugfixes/mpa-subnet-order/2026-10-09-subnet-order.md +++ /dev/null @@ -1,50 +0,0 @@ -# Preserve managed Runtime recovery across subnet ordering changes - -[中文版](2026-10-09-subnet-order.zh.md) - -- Change ID: `mpa-subnet-order` -- Created/revised: 2026-10-09 -- Status: implemented -- Contracts: [Studio creation](../../../specs/studio-mpa-creation/README.md), [Runtime provisioning](../../../specs/mpa-runtime-provisioning/README.md). - -## Background and scope - -Read-only diagnosis found a Runtime with platform Ready and HTTP 200 readiness, but its creation task failed immediately after V1 became Ready. The shared registry and Runtime contained the same two subnets in opposite order. validate_runtime compares SubnetIds as an ordered list and raises a migration error. An isolated synthetic reproduction confirmed this cause. AccountNetworkProvisioner.ensure repeats that ordered comparison on resume. Adopting the platform order also changes the existing request hash on retry. - -Goals: ignore provider-only subnet permutations at both guards and resume the original deployment without replacing resources. Non-goals: cloud resource writes during verification, registry repairs, broad diagnostics changes, UI/chat changes, permissions, image changes or bypassing actual network drift. - -## Scenarios and requirements - -- FR-1 / AC-1: Given identical unique nonempty subnet lists in different orders, initial Runtime validation and existing Runtime network preparation accept them. VpcId and EnableSharedInternetAccess remain exact comparisons; added, removed, substituted, duplicated or malformed subnet selections remain errors. -- FR-2 / AC-2: Retry retains the caller's requested subnet order, or the registered order when its membership equals the current Runtime selection. Otherwise retain the current selection, never adopt a different registered membership. This preserves already persisted request hashes and ClientTokens for unchanged inputs. Do not sort or rewrite old deployment records or relax changed-input conflict checks. -- FR-3 / AC-3: Simulated full creation and recovery of a pending V1 deployment with reordered provider metadata finish environment/key finalization and readiness, retain the Runtime/Skill Space/database identifiers and create no duplicate resources. Caller inputs and provider objects remain unchanged. - -## Design and contract impact - -Share a small subnet-membership comparator in managed/network.py, validating list shape, nonempty string IDs and uniqueness before set comparison. Use it only for SubnetIds in network.py and runtime.py; other immutable fields remain strict. For resume, retain explicit requested ordering. With no explicit selection, prefer the shared registry ordering only if it has identical members to the active Runtime; otherwise preserve the active per-agent selection. This addresses both migration guards and retry hash stability without changing hashing format or persistent schema. - -Studio creation owns this network/recovery contract; Runtime provisioning references it. No API/configuration/schema/dependency/permission changes; general agents, CLI legacy provisioning, async deadlines, locks, cancellation, terminal handling and credential boundaries remain unchanged. Malformed lists are rejected rather than converted to sets blindly. Reuse existing fake cloud/registry lifecycle tests; no frontend or generated assets are affected. - -## Tasks and affected files - -- T-1 (FR-1/AC-1): failing tests in test_deployment_network.py and test_runtime_deployment_edges.py; update managed/network.py and managed/runtime.py. -- T-2 (FR-2, FR-3/AC-2, AC-3): regression for a persisted pending V1 with original hash, reversed metadata, same-ID retry and no duplicates; preserve explicit order and registered order in network preparation. -- T-3: reconcile paired Studio/Runtime specs and managed README guides, run targeted/broader affected provisioning tests, Ruff/Pyright, scoped pre-commit/secret scan and document link/whitespace checks. - -## Risks and acceptance verification - -Blind set conversion could hide duplicate or invalid IDs: validate before comparison and cover negatives. Returning provider order could strand old pending hashes: exercise an already persisted original request through the production retry path. Registry membership can differ for explicit per-agent overrides: do not substitute the shared default when members differ. Simulated tests do not prove cloud publication; live retry remains an operator action after the fix. No unreviewed cloud mutation is needed to deliver this code repair. - -## Review and approval - -User approved the preceding proposed repair with “修复下” on 2026-10-09. The implementation detail preserving hash order is necessary to deliver that same-ID recovery requirement. review-spec is unavailable in the skill catalog; direct review before production/test edits checked bilingual equivalence, contract ownership, error boundaries, compatibility, security, testability and retained concurrency/deadline behavior. No blockers. Tested scope will be ef0ad519 plus this fix and the pre-existing uncommitted MPA changes. Results will be recorded below; full commit synchronization/gates await commit authorization. - -## Results (2026-10-09) - -- pass: T-1/T-2 and AC-1/AC-2/AC-3. The corrected fail-first fixture produced six failures from ordered comparison/request-hash drift, one existing-success boundary passed (103 deselected), before implementation. -- pass: `uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py tests/cli/test_frontend_deploy_iam.py -q --tb=short` — 625 passed, five existing deprecation warnings. Includes fresh/pending lifecycle, finalization, unchanged hash/token, negative drift and malformed-list coverage. -- pass: `uvx --from ruff==0.11.12 ruff check` and `uvx pyright --pythonpath .venv/bin/python` on network.py, runtime.py, test_deployment_network.py and test_runtime_deployment_edges.py. Initial Pyright findings were repaired (list narrowing and conditional test variables); final result zero errors. -- pass: direct implementation review verifies unchanged request hashing, no mutation of caller/provider selections, same-membership-only registry ordering preference, no relaxed resource ownership or real-drift checks, and unchanged async lifecycle/cleanup. -- blocked: fresh live read-only Runtime/registry verification returned ApiError; no provider payload or credentials were printed. The earlier diagnostic snapshot and synthetic regression establish the ordering defect, but no successful post-fix cloud read or retry is claimed. -- pass: scoped pre-commit (Ruff, formatting and hardcoded-secret detection), paired-language relative links/identifiers and diff whitespace. Formatting initially changed two test files; checks were rerun after formatting. YAML scan not_applicable, no YAML changed. -- not_run: cloud deployment retry/full live publication; this verification performs no cloud writes. Full SDK-wide regression is not_run because the affected managed/legacy deployment suite covers the scoped integration change. UI/build/browser/generated artifacts and Codex/harness process smoke are not_applicable: no changes to those paths. Full commit synchronization/all-files gates await an authorized commit. T-3 complete; no deferred implementation scope. diff --git a/prd-spec/bugfixes/mpa-subnet-order/2026-10-09-subnet-order.zh.md b/prd-spec/bugfixes/mpa-subnet-order/2026-10-09-subnet-order.zh.md deleted file mode 100644 index 9c43159c3..000000000 --- a/prd-spec/bugfixes/mpa-subnet-order/2026-10-09-subnet-order.zh.md +++ /dev/null @@ -1,50 +0,0 @@ -# 在子网顺序变化时保留托管 Runtime 的恢复能力 - -[English](2026-10-09-subnet-order.md) - -- 变更 ID:`mpa-subnet-order` -- 创建/修订:2026-10-09 -- 状态:implemented -- 契约:[Studio 创建](../../../specs/studio-mpa-creation/README.zh.md)、[Runtime 部署](../../../specs/mpa-runtime-provisioning/README.zh.md)。 - -## 背景和范围 - -只读诊断发现 Runtime 的平台状态为 Ready,就绪接口为 HTTP 200,但创建任务在 V1 就绪后立即失败。共享注册记录与 Runtime 包含同样两个子网,顺序相反。validate_runtime 按有序列表比较 SubnetIds,抛出迁移错误。独立的合成样例已确认原因。AccountNetworkProvisioner.ensure 在恢复时重复该有序比较,采用平台顺序还会改变重试时已有的请求哈希。 - -目标:两处检查均忽略提供方仅改变子网顺序的情况,恢复原部署且不替换资源。非目标:验证时写云资源、修复注册记录、扩大诊断改造、修改 UI/聊天、权限、镜像或绕过真实网络变更。 - -## 场景和需求 - -- FR-1 / AC-1:两份子网列表成员相同、非空且无重复,仅顺序不同,首次 Runtime 校验和已有 Runtime 网络准备均接受。VpcId、EnableSharedInternetAccess 仍精确比较;增加、删除、替换、重复或格式错误的子网选择仍报错。 -- FR-2 / AC-2:重试保留调用方请求中的子网顺序;没有显式选择且共享记录与当前 Runtime 成员一致时保留注册顺序,否则保留当前选择,不采用成员不同的共享默认值。这保留未改变输入的已持久化请求哈希和 ClientToken。不排序或改写旧部署记录,不放宽输入变更冲突检查。 -- FR-3 / AC-3:模拟完整创建及已有待完成 V1 的恢复,平台元数据顺序相反时仍完成环境/密钥补齐和就绪检查,保留 Runtime/Skill Space/数据库标识且无重复资源。调用方输入和平台对象保持不变。 - -## 设计和契约影响 - -在 managed/network.py 共用一个小型子网成员比较函数,先验证列表格式、非空字符串 ID 和唯一性,再比较集合。network.py 和 runtime.py 仅对 SubnetIds 使用该函数,其他不可变字段仍严格比较。恢复时保留显式请求顺序;没有显式选择时,仅在共享注册记录与当前 Runtime 成员相同时使用注册顺序,否则保留当前智能体的选择。这同时解决迁移误判与重试哈希稳定性,不改变哈希格式或持久化结构。 - -Studio 创建拥有网络/恢复契约,Runtime 部署引用该契约。无 API/配置/结构/依赖/权限变化;通用智能体、旧 CLI 部署、异步截止时间、锁、取消、终态处理及凭据边界均不变。不盲目转换集合,格式错误列表仍拒绝。复用现有模拟云/注册记录生命周期测试,不涉及前端或生成资源。 - -## 任务和影响文件 - -- T-1(FR-1/AC-1):在 test_deployment_network.py、test_runtime_deployment_edges.py 添加先失败测试,修改 managed/network.py、managed/runtime.py。 -- T-2(FR-2、FR-3/AC-2、AC-3):覆盖已有待完成 V1、原始哈希、反序元数据、同 ID 重试及无重复资源;网络准备保留显式或注册顺序。 -- T-3:同步 Studio/Runtime 双语契约及 managed README 指南,运行目标和受影响部署回归、Ruff/Pyright、范围内 pre-commit/密钥扫描及文档链接/空白检查。 - -## 风险和验收验证 - -盲目转换集合可能隐藏重复或非法 ID:比较前验证并覆盖反例。采用平台顺序可能让旧待完成哈希无法恢复:通过生产重试路径验证已持久化原始请求。显式智能体覆盖可能与注册成员不同:不得以共享默认值替换。模拟测试不能证明云端发布,修复后的真实重试仍由操作者执行。交付代码修复不需要未经审查的云写操作。 - -## 审查和批准 - -用户于 2026-10-09 用“修复下”批准上轮提出的修复。保留哈希顺序是实现同 ID 恢复要求的必要细节。技能目录无 review-spec,修改生产代码/测试前已直接审查双语等价性、契约归属、错误边界、兼容性、安全、可测试性以及保留的并发/截止时间行为,无阻塞项。验证范围为 ef0ad519 加本次修复及已有未提交 MPA 修改。结果记录于下方;完整提交同步和门禁等待提交授权。 - -## 结果(2026-10-09) - -- pass:T-1/T-2、AC-1/AC-2/AC-3。修正测试样例后,实施前先失败测试出现六处有序比较/请求哈希漂移失败,一项原本成功的边界通过(103 项未选中)。 -- pass:`uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py tests/cli/test_frontend_deploy_iam.py -q --tb=short` — 625 项通过,五条已有弃用警告。包含首次/待完成生命周期、补齐流程、原哈希/token 保留、真实变更及格式错误列表反例。 -- pass:对 network.py、runtime.py、test_deployment_network.py、test_runtime_deployment_edges.py 执行 `uvx --from ruff==0.11.12 ruff check`、`uvx pyright --pythonpath .venv/bin/python`。初次 Pyright 问题已修复(列表类型收窄和条件测试变量),最终零错误。 -- pass:直接实现审查确认请求哈希算法未变,不修改调用方/平台的子网选择,仅成员一致时优先采用注册顺序,不放宽资源归属或真实变更检查,异步生命周期/清理不变。 -- blocked:本次真实 Runtime/注册记录只读核对返回 ApiError,未输出提供方请求内容或凭据。此前诊断快照及合成回归已确认顺序缺陷,但不声称修复后云查询或重试已成功。 -- pass:范围内 pre-commit(Ruff、格式及硬编码密钥检测)、双语相对链接/标识和 diff 空白检查。格式检查首次修改两个测试文件,格式化后重跑检查。YAML 扫描 not_applicable,未修改 YAML。 -- not_run:云端部署重试/完整实际发布,验证不执行云写操作。全 SDK 回归 not_run,受影响的托管/旧部署测试已覆盖本次集成修复。UI/构建/浏览器/生成资源及 Codex/harness 进程 smoke 为 not_applicable,未修改这些路径。完整提交同步/全文件门禁等待提交授权。T-3 完成,无延期实现范围。 diff --git a/prd-spec/bugfixes/mpa-test-types/2026-09-21-mpa-test-types.md b/prd-spec/bugfixes/mpa-test-types/2026-09-21-mpa-test-types.md deleted file mode 100644 index de16653a3..000000000 --- a/prd-spec/bugfixes/mpa-test-types/2026-09-21-mpa-test-types.md +++ /dev/null @@ -1,50 +0,0 @@ -# MPA editor type diagnostics - -[中文版](2026-09-21-mpa-test-types.zh.md) - -- ID: mpa-test-types; created/revised: 2026-09-21; status: implemented. -- Component: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md). - -## Background and scope - -The user requested fixing editor errors. Pyright over managed MPA implementation, Studio route and tests reports 20 errors in test doubles and inferred numeric timeout types. Frontend `tsc --noEmit -p tsconfig.json` passes. Tests use narrower inferred literals, add undeclared attributes, pass an untyped namespace as Profile, fake SDK clients as tuples, and pass fake engines to an AsyncEngine contract. No runtime creation failure is implicated. - -## Requirements and design - -- FR-1: Eliminate the 20 reproducible Pyright errors without ignore comments, disabling checks, casts that hide incorrect fixtures, or relaxing production object contracts. -- FR-2: Use real Profile/Managed models in service tests, explicitly declare fake capabilities and method signatures, and use spec-constrained mocks for SDK/engine boundaries. Preserve assertions, fault injection and external-service isolation. -- FR-3: Annotate runtime/network timeout seconds as float to describe already-supported fractional durations; retain defaults and runtime logic. No dependency, config, HTTP/UI, persistence or resource change. - -Component impact: no maintained contract change; annotations document existing numeric behavior and tests conform to existing interfaces. No component-spec edits required. Affected files: `runtime.py`, `network.py`, test_agent_deployment, test_deployment_database_unit, test_deployment_network_cloud, test_gateway, test_runtime_deployment_edges, test_service under managed MPA. No unrelated lint cleanup. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | -| --- | --- | --- | -| FR-1/2 | T-1: reproduce and correct typed fixtures | AC-1: complete managed MPA implementation/route/test Pyright has zero errors; existing assertions retained | -| FR-3 | T-2: annotate durations | AC-2: defaults and timeout/cancellation tests unchanged | -| All | T-3: verification and paired documentation | AC-3: MPA/CLI regression, changed-file Ruff/format, TypeScript check and whitespace pass | - -The failing Pyright run is the regression baseline (20 errors). Existing behavioral tests validate the fixture corrections; no redundant new behavior test is needed for type-only changes. Run `uv run --extra dev --with pyright pyright veadk/integrations/mpa/managed frontend/server/mpa_creation.py tests/integrations/mpa_managed`, `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q`, and pinned Ruff checks. - -## Review and approval - -The user explicitly requested “有爆红啊,修一下” and clarified “代码编辑器报红”. The stated plan fixes reproducible type mismatches without suppressions. Direct review (review-spec unavailable) found no contract/security/bilingual blocker. Risk: editor interpreter-specific errors outside this command may still need the exact file/message; do not claim unobserved editor state. No commit/push/cloud operations or service restart needed. - -## Verification - -2026-09-21 working diff: baseline Pyright fail (20 errors); TypeScript pass. Final checks not_run (pending). Browser/build not_applicable (no frontend changes); live cloud not_run (not relevant); pre-commit not_applicable (no commit requested). - - -## Final review and verification (2026-09-21) - -Status: implemented. T-1/T-2/T-3 and AC-1/AC-2/AC-3 complete. Production diff contains only float annotations for existing durations, retaining default values; fixture edits retain behavioral/fault-injection assertions and use real profile contracts. No suppression or production contract weakening introduced. - -- pass: full scoped Pyright command above — 0 errors, 0 warnings (baseline 20 errors). -- pass: MPA/CLI regression command above — 272 passed, one upstream Starlette deprecation warning. -- pass: `frontend/node_modules/.bin/tsc --noEmit -p frontend/tsconfig.json` (executed from frontend with equivalent relative paths), no frontend edits. -- pass: changed-file Ruff check/format, relative links, bilingual review and diff whitespace. -- During iteration, signature alignment exposed one fake lock call without identity and four mock-engine dialect failures; corrected the test call and explicit dialect binding before the final 272-pass run. No failing regression deferred. -- not_run: repository-wide regression, because only isolated MPA tests and duration annotations changed; full affected component/CLI suite passed. Browser/build/live cloud: not_applicable. Pre-commit/commit: not_applicable, no commit requested. - -- pass: repository-rule Gitleaks scan of all 10 changed code/test/design files; no secrets detected. diff --git a/prd-spec/bugfixes/mpa-test-types/2026-09-21-mpa-test-types.zh.md b/prd-spec/bugfixes/mpa-test-types/2026-09-21-mpa-test-types.zh.md deleted file mode 100644 index 4681beda3..000000000 --- a/prd-spec/bugfixes/mpa-test-types/2026-09-21-mpa-test-types.zh.md +++ /dev/null @@ -1,50 +0,0 @@ -# MPA 编辑器类型诊断 - -[English](2026-09-21-mpa-test-types.md) - -- ID:mpa-test-types;创建/修订:2026-09-21;状态:implemented。 -- 组件:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md)。 - -## 背景与范围 - -用户要求修复编辑器报红。对托管 MPA 实现、Studio 路由和测试执行 Pyright,发现测试替身和数字超时类型推断共 20 处错误。前端 `tsc --noEmit -p tsconfig.json` 通过。测试存在字面量推断过窄、动态添加未声明属性、用未声明类型的 namespace 代替 Profile、用元组代替 SDK 客户端、向 AsyncEngine 契约传入自定义替身等问题。与运行中的创建失败无关。 - -## 需求与设计 - -- FR-1:消除 20 处可复现的 Pyright 报错,不加忽略注释、不关闭检查、不用强制类型转换掩盖错误替身、不放宽生产对象契约。 -- FR-2:服务测试使用真实 Profile/Managed 模型,显式声明替身能力和方法签名,SDK/engine 边界使用带 spec 约束的 mock。保留断言、故障注入和外部服务隔离。 -- FR-3:runtime/network 超时秒数标注 float,准确描述本来支持的小数时长;保留默认值和运行逻辑。不修改依赖、配置、HTTP/UI、持久化或资源。 - -组件影响:维护契约无变化;类型标注明确现有数字行为,测试符合现有接口,无需修改组件规范。影响文件:`runtime.py`、`network.py` 和托管 MPA 下 test_agent_deployment、test_deployment_database_unit、test_deployment_network_cloud、test_gateway、test_runtime_deployment_edges、test_service。不顺手清理无关 lint。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | -| --- | --- | --- | -| FR-1/2 | T-1:复现并修正类型化替身 | AC-1:完整托管 MPA 实现/路由/测试 Pyright 零错误,保留既有断言 | -| FR-3 | T-2:时长标注 | AC-2:默认值与超时/取消测试不变 | -| 全部 | T-3:验证和双语文档 | AC-3:MPA/CLI 回归、变更文件 Ruff/格式、TypeScript 检查、空白检查通过 | - -已失败的 Pyright 是回归基线(20 处错误)。现有行为测试验证替身修正,类型改动无需重复增加行为测试。执行 `uv run --extra dev --with pyright pyright veadk/integrations/mpa/managed frontend/server/mpa_creation.py tests/integrations/mpa_managed`、`uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q` 及固定版本 Ruff 检查。 - -## 审查与批准 - -用户明确要求“有爆红啊,修一下”,并澄清“代码编辑器报红”。已说明方案:修复可复现的类型不匹配,不屏蔽检查。直接审查(review-spec 不可用)无契约/安全/双语阻塞项。风险:此命令以外的编辑器解释器专属错误仍可能需要具体文件/消息,不声称观察了编辑器状态。无需提交/推送/云操作或服务重启。 - -## 验证 - -2026-09-21 工作区差异:Pyright 基线 fail(20 处),TypeScript pass。最终检查 not_run(待执行)。浏览器/构建 not_applicable(无前端改动);真实云 not_run(不相关);pre-commit not_applicable(未请求提交)。 - - -## 最终审查与验证(2026-09-21) - -状态:implemented。T-1/T-2/T-3 和 AC-1/AC-2/AC-3 完成。生产差异仅为现有时长的 float 标注,默认值不变;替身修改保留行为/故障注入断言,使用真实 Profile 契约。未屏蔽检查,未放宽生产契约。 - -- pass:上述完整范围 Pyright — 0 errors、0 warnings(基线 20 errors)。 -- pass:上述 MPA/CLI 回归命令 — 272 passed,一条上游 Starlette 弃用警告。 -- pass:`frontend/node_modules/.bin/tsc --noEmit -p frontend/tsconfig.json`(实际在 frontend 目录用等价相对路径执行),无前端修改。 -- pass:变更文件 Ruff 检查/格式、相对链接、双语审查、差异空白检查。 -- 迭代中签名对齐暴露一个无身份参数的替身锁调用,以及四个 mock engine 方言失败;已修正测试调用和显式方言绑定,最终 272 项通过,未遗留失败回归。 -- not_run:全仓回归,改动仅涉及隔离 MPA 测试和时长标注,完整受影响组件/CLI 测试已通过。浏览器/构建/真实云:not_applicable。pre-commit/提交:not_applicable,未请求提交。 - -- pass:按仓库 Gitleaks 规则扫描全部 10 个变更代码/测试/设计文件,未发现密钥。 diff --git a/prd-spec/bugfixes/mpa-upstream-incremental-alignment/2026-10-10-complete-fork-increments.md b/prd-spec/bugfixes/mpa-upstream-incremental-alignment/2026-10-10-complete-fork-increments.md deleted file mode 100644 index 70ff0e993..000000000 --- a/prd-spec/bugfixes/mpa-upstream-incremental-alignment/2026-10-10-complete-fork-increments.md +++ /dev/null @@ -1,66 +0,0 @@ -# Complete fork MPA increments on official main - -- Change ID: `mpa-upstream-incremental-alignment`; date: 2026-10-10; status: implemented. -- [Chinese](2026-10-10-complete-fork-increments.zh.md). -- Contract: [Studio MPA control plane](../../../specs/studio-mpa-control-plane/README.md). - -## Background and evidence -The prior official contribution branch `feat/mpa-studio-upstream-alignment-20261009` ends at `033b2711`; official PR #1156 landed as `171d8d86`. Fork main `035387d1` has a different history. Official PR #1160 starts directly from `171d8d86` and already includes fork PR #23, but omits PR #22's Basic-only MPA details. Copying the entire fork tree would remove newer official Codex and other changes. - -The incremental audit uses the fork alignment merge `ef0ad519` through `035387d1`, compared with official base and contribution HEAD `80fd1e19`. This establishes feature coverage, rather than treating every historical commit as an unapplied change. - -| Fork change | Contribution treatment | -| --- | --- | -| PR #21: entered Runtime name, description, three-step creation, image defaults, name validation, TOS guidance | Already in official PR #1156; retain unchanged. | -| PR #22: Basic-only MPA details, synchronous focus normalization, no hidden Profile requests | Port production code, regression tests, documentation, and rebuild release assets. | -| PR #23: STS account resolution, IAM preparation and Python 3.10 timeout, network discovery/description/subnet order, standard APIG, independent worker creation/defaults/naming, skill-space description, deployment diagnostics | Already in #1160; retain unchanged and run affected regressions. | -| Lockfile restoration | Effective lockfile content already present on official main; retain. | -| Official secret-scan exclusions and cancellation PID race fix | Retain official implementation; fork lacks these contribution fixes. | -| Official BytePlus sidecar behavior and SDK features | Retain; differences from fork are not new fork increments. | - -## Goals, scenarios and requirements -FR-1 / AC-1: all effective fork increments since the previous contribution remain available in the official contribution; no blanket fork/main merge or reversed official changes. -FR-2 / AC-2: selecting an MPA, restoring any hidden section, or switching from a General agent renders Basic only before effects run, with no Agent View/Profile/session configuration requests or Profile operations. General navigation and update behavior remain. -FR-3 / AC-3: package tests, bilingual contracts and historical designs accompany source changes; generated web assets match the combined official source. -FR-4 / AC-4: preserve official secret scanning and cancellation fixes, and run the current repository gates with honest results. -Non-goals: backend API redesign, chat changes, cloud resource creation, deployment, merging the official PR, rewriting fork main, or closing another PR. - -## Design and affected files -Port the reviewed PR #22 patch to `frontend/src/ui/AgentWorkspace.tsx`; derive the effective section synchronously from category and requested section. Keep hidden control-plane implementations and their cleanup, gated by their owning sections. No new dependencies, data migration, authentication or concurrency contract changes. Update `frontend/tests/mpaBasicDetails.test.tsx`, source-contract tests and the package test command; append the Basic-only contract to both component spec languages and README while retaining official documentation. Import the historical Basic-only PRD pair; its dated cloud evidence is historical, not evidence of this integration. Rebuild `veadk/webui` from the combined tree. - -## Tasks and verification -T-1 (FR-1): audit all paths changed by `ef0ad519..035387d1`, excluding generated hashes and formatting-only historical document differences; account for each remaining difference. -T-2 (FR-2): run the imported component regression before porting production code, then port the patch and source-contract expectations. -T-3 (FR-3): frontend tests, build, i18n, asset references, component coverage and loopback browser checks using the actual component with mocked network boundaries. -T-4 (FR-4): targeted managed-creation/CLI tests, default two-worker Python regression, fetch/rebase official base, all-files pre-commit, and diff/document review before authorized commit/push. -Browser cases: MPA and General navigation, hidden focus, switching, normal/loading/empty/error Runtime information, read-only and narrow viewport, keyboard focus. New input/IME, mutation cancellation and retry are not applicable because this patch introduces no inputs or mutations; existing creation/sidecar regressions remain required. Live cloud writes are not run. - -## Risks and review -Risk: replacing whole files from fork could overwrite official enhancements. Resolution: only port the audited PR #22 behavior and use content comparisons for retained creation paths. Asset churn is required hashed build output, not additional hand-written functionality. -Direct design review performed because `review-spec` is unavailable: boundaries, effect ordering, cleanup, compatibility, security, tests and bilingual equivalence have no blockers. Referenced frontend design skills are absent locally; existing SPEC/Foundation rules and direct review apply. No visual redesign or new components are proposed. -Approval: the user's explicit 2026-10-10 request to compare the previous contribution and include all new main functionality approves this bounded integration; prior authorization to prepare/push the official PR remains applicable. - -## Delivery record -Checks below cover the integration diff above `80fd1e19`, with matching evidence in the Chinese counterpart. No current cloud verification is claimed. - -Verification record on 2026-10-10, integration diff over `80fd1e19`: -- **pass**: path audit: 83 non-generated paths in `ef0ad519..035387d1`; all effective new source/tests/contracts/designs match fork main. Exceptions: README preserves official documentation and adds bilingual guidance; `test_tasks.py` preserves the official PID race fix; four historical PRDs differ only in trailing whitespace. Official secret-scan exclusions, BytePlus behavior and Codex SDK files are retained. -- **pass**: regression first: `npm --prefix frontend exec -- vitest run --root frontend tests/mpaBasicDetails.test.tsx` failed all 10 cases before production port; after port, all 10 pass. -- **pass**: `npm --prefix frontend test`: 1,377 Node and 65 Vitest tests. -- **pass**: `npm --prefix frontend run build` (TypeScript plus app/widget builds); `npm --prefix frontend run check:i18n`; `npm --prefix frontend run test:webui-assets` (113 files, 350 references). -- **pass**: `npm --prefix frontend run test:harness-sidecar-coverage`: statements 98.55%, branches 94.44%, functions/lines 100%, above the existing thresholds. -- **pass**: `npm --prefix frontend exec -- vitest run --root frontend tests/mpaBasicDetails.test.tsx --coverage --coverage.include=src/ui/AgentWorkspace.tsx --coverage.reporter=json`: changed executable statement-start lines 7/7 covered (100%); not whole-component coverage. -- **pass**: loopback in-app browser, actual `AgentWorkspace` with mocked fetch: MPA Basic-only including restored Profile/session focus, chat callback, General navigation/Integration, switching back, Runtime normal/loading/failure, empty envs and read-only, keyboard Tab focus, 390px viewport (body width 390px), no hidden control-plane requests. Integration discovery failure was simulated, not a live provider result. Temporary fixture, tab, viewport override and server removed. -- **pass**: `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_frontend_deploy_iam.py -q`: 678 passed. An initial invocation referenced a nonexistent extra test path and ran no tests; corrected above. -- **not_applicable**: focused Ruff/Pyright for new Python code (no Python files changed in this integration); all-files pre-commit still required. Real cloud creation/deployment and new IME/mutation flows were not run as explained above. -- **fail** (unchanged baseline): `uv run --extra dev --extra codex --extra extensions --extra sandbox pytest -n 2 -m "not codex_smoke and not piagent_smoke"`: 6,869 passed, 53 skipped, 4 xfailed, 1 failed in 480.71s. Two workers were used per the repository default. Optional dependencies were prepared through declared extras in the repository-local environment; no dependency file changed. The only failure is `tests/frontend/server/runtime_artifacts/test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes`: local Python 3.10/macOS `mimetypes.guess_type("reports/summary.md")` returns no type, producing `application/octet-stream` instead of `text/markdown`. The test and complete `frontend/server/runtime_artifacts/` implementation are identical to official main. This unrelated baseline remains a limitation, not a passing test. -- **pass**: refreshed `origin/main` and `upstream/main` remain `035387d1` and `171d8d86`; `git -c rebase.autoStash=true rebase upstream/main` reported up to date and restored all integration changes. -- **pass**: bilingual links/requirement IDs/quoted identifiers and scoped diff whitespace review. Direct final review found no lost increments, new dependencies, backend/chat changes, or overwritten official fixes. -- **pass**: after synchronization, `uv run --no-sync --extra dev pre-commit run --all-files`, `uv run --no-sync --extra dev pytest tests/integrations/mpa_managed tests/cli/test_frontend_deploy_iam.py -q` (678 passed), and `npm --prefix frontend test` (1,377 Node / 65 Vitest) passed again. `--no-sync` preserves the prepared repository-local optional extras. Commit/push metadata belongs to Git and the official PR. - -| Requirement | Task | Acceptance | Evidence | -| --- | --- | --- | --- | -| FR-1 | T-1 | AC-1: all effective increments retained | 83-path audit and documented official exceptions: pass. | -| FR-2 | T-2 | AC-2: Basic-only MPA, unchanged General | 10 regression cases plus loopback browser: pass. | -| FR-3 | T-3 | AC-3: tests, documents and matching assets | Frontend/build/i18n/assets/coverage and bilingual review: pass. | -| FR-4 | T-4 | AC-4: preserve official fixes and execute gates honestly | Rebase, pre-commit, targeted tests: pass; full regression: one unchanged MIME baseline failure recorded. | diff --git a/prd-spec/bugfixes/mpa-upstream-incremental-alignment/2026-10-10-complete-fork-increments.zh.md b/prd-spec/bugfixes/mpa-upstream-incremental-alignment/2026-10-10-complete-fork-increments.zh.md deleted file mode 100644 index 19273bd9c..000000000 --- a/prd-spec/bugfixes/mpa-upstream-incremental-alignment/2026-10-10-complete-fork-increments.zh.md +++ /dev/null @@ -1,66 +0,0 @@ -# 在官方 main 补齐 fork 的 MPA 增量 - -- 变更 ID:`mpa-upstream-incremental-alignment`;日期:2026-10-10;状态:implemented。 -- [English](2026-10-10-complete-fork-increments.md)。 -- 契约:[Studio MPA 控制面](../../../specs/studio-mpa-control-plane/README.zh.md)。 - -## 背景与证据 -上次官方贡献分支 `feat/mpa-studio-upstream-alignment-20261009` 的末端是 `033b2711`;官方 PR #1156 以 `171d8d86` 合入。fork main `035387d1` 的历史不同。官方 PR #1160 直接基于 `171d8d86`,已包含 fork PR #23,但漏了 PR #22 的 MPA 详情仅基本信息。整体复制 fork 文件树会删掉官方较新的 Codex 等改动。 - -增量核对范围为 fork 对齐合并 `ef0ad519` 至 `035387d1`,与官方基线及贡献 HEAD `80fd1e19` 对比,以功能覆盖为准,不把每个历史提交都当作未应用的改动。 - -| fork 改动 | 本次处理 | -| --- | --- | -| PR #21:手填 Runtime 名称、描述、三步创建、镜像默认值、名称校验、TOS 提示 | 已在官方 PR #1156 中,保持。 | -| PR #22:MPA 详情仅基本信息、同步归一化焦点、停止隐藏 Profile 请求 | 迁入生产代码、回归测试、文档并重建发布资源。 | -| PR #23:STS 账号解析、IAM 准备及 Python 3.10 超时、网络发现/描述/子网顺序、标准 APIG、独立 Worker 创建/默认值/名称、技能空间描述、部署诊断 | 已在 #1160 中,保持并运行相关回归。 | -| lockfile 修复 | 有效内容已在官方 main 中,保持。 | -| 官方密钥扫描排除与取消时 PID 竞争修复 | 保留官方实现;fork 缺少这些贡献时修复。 | -| 官方 BytePlus sidecar 行为和 SDK 功能 | 保留;与 fork 的差异不属于 fork 新增功能。 | - -## 目标、场景与需求 -FR-1 / AC-1:上次贡献以来 fork 的全部有效增量在官方贡献中保留;不整体合并 fork/main,不反向撤销官方功能。 -FR-2 / AC-2:选择 MPA、恢复任意隐藏页面或从普通智能体切换时,在副作用前仅渲染基本信息;不请求 Agent View/Profile/会话配置,不执行 Profile 操作。普通智能体的导航和更新行为保持。 -FR-3 / AC-3:源码附带包测试、双语契约及历史设计;生成资源与合并后的官方源码一致。 -FR-4 / AC-4:保留官方密钥扫描和取消修复,运行当前仓库检查并如实记录。 -非目标:重设计后端 API、聊天改动、创建云资源、部署、合并官方 PR、改写 fork main、关闭其他 PR。 - -## 设计与影响文件 -将已审查的 PR #22 迁入 `frontend/src/ui/AgentWorkspace.tsx`,按类别和请求页面同步计算有效页面。保留隐藏控制面实现及清理逻辑,仅由所属页面触发。无新增依赖、数据迁移、鉴权或并发契约变化。更新 `frontend/tests/mpaBasicDetails.test.tsx`、源码契约测试及包测试命令;在双语组件规范和 README 追加仅基本信息约束,保留官方文档。迁入历史 PRD 双语文件,其有日期的云端证据仅为历史记录,不代表此次整合验证。从合并后的源码重建 `veadk/webui`。 - -## 任务与验证 -T-1(FR-1):逐个核对 `ef0ad519..035387d1` 的路径,排除生成哈希及历史文档纯格式差异,对剩余差异逐项说明。 -T-2(FR-2):迁入组件回归,在修改生产代码前验证失败,再迁入实现和源码契约断言。 -T-3(FR-3):前端测试、构建、i18n、资源引用、组件覆盖率及真实组件配合模拟网络的本地浏览器检查。 -T-4(FR-4):定向创建/CLI 测试、默认双 worker Python 回归、获取并 rebase 官方基线、全文件 pre-commit 及差异/文档审查后,按授权提交推送。 -浏览器场景:MPA 和普通导航、隐藏焦点、切换、Runtime 正常/加载/空/失败、只读、窄窗口、键盘焦点。新增输入/IME、写入取消及重试不适用,因为本次不增加输入或写入;既有创建/sidecar 回归仍须运行。不执行真实云端写入。 - -## 风险与审查 -风险:从 fork 整体替换文件可能覆盖官方增强。处理:仅迁入核对过的 PR #22 行为,用内容比较确认保留的创建路径。资源差异来自必须提交的哈希构建产物,不是新增手写功能。 -因 `review-spec` 不可用已直接审查:边界、副作用顺序、清理、兼容、安全、测试和双语一致性无阻塞。引用的前端设计 skill 本地缺失,采用既有 SPEC/Foundation 规范及直接审查。本次不重设计外观或新增组件。 -批准:用户于 2026-10-10 明确要求对比上次贡献并带上 main 全部新增功能,批准本次有限整合;此前准备并推送官方 PR 的授权继续有效。 - -## 交付记录 -以下检查针对 `80fd1e19` 之上的整合差异,英文版同步记录证据。不宣称本次云端验证。 - -2026-10-10 验证记录,针对 `80fd1e19` 之上的整合差异: -- **pass**:逐路径核对 `ef0ad519..035387d1` 的 83 个非生成文件,全部有效新增源码/测试/契约/设计与 fork main 一致。例外:README 保留官方文档并追加双语说明;`test_tasks.py` 保留官方 PID 竞争修复;四份历史 PRD 仅末尾空白不同。保留官方密钥扫描排除、BytePlus 行为和 Codex SDK 文件。 -- **pass**:先运行回归:`npm --prefix frontend exec -- vitest run --root frontend tests/mpaBasicDetails.test.tsx` 在迁入生产实现前 10 个用例全部失败;迁入后 10 个全部通过。 -- **pass**:`npm --prefix frontend test`:1,377 个 Node 和 65 个 Vitest 测试。 -- **pass**:`npm --prefix frontend run build`(TypeScript 及 app/widget 构建);`npm --prefix frontend run check:i18n`;`npm --prefix frontend run test:webui-assets`(113 文件、350 引用)。 -- **pass**:`npm --prefix frontend run test:harness-sidecar-coverage`:语句 98.55%、分支 94.44%、函数/行 100%,达到既有阈值。 -- **pass**:`npm --prefix frontend exec -- vitest run --root frontend tests/mpaBasicDetails.test.tsx --coverage --coverage.include=src/ui/AgentWorkspace.tsx --coverage.reporter=json`:变更可执行语句起始行覆盖 7/7(100%),不是整个组件覆盖率。 -- **pass**:本地内置浏览器使用真实 `AgentWorkspace` 与模拟 fetch:MPA 仅基本信息及恢复 Profile/会话焦点、聊天回调、普通导航/集成页、切回 MPA、Runtime 正常/加载/失败、空环境变量及只读、Tab 键盘焦点、390px 窗口(body 宽 390px),无隐藏控制面请求。集成发现失败为模拟结果,不代表实际云端。临时夹具、标签页、窗口覆盖及服务已清理。 -- **pass**:`uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_frontend_deploy_iam.py -q`:678 passed。首次附加了不存在的测试路径而未执行测试,已按上述命令修正。 -- **not_applicable**:新增 Python 代码的定向 Ruff/Pyright(此次整合没有修改 Python);仍须执行全文件 pre-commit。未运行真实云端创建/部署及新增 IME/写入流程,原因见上文。 -- **fail**(未修改的基线):`uv run --extra dev --extra codex --extra extensions --extra sandbox pytest -n 2 -m "not codex_smoke and not piagent_smoke"`:6,869 passed、53 skipped、4 xfailed、1 failed,耗时 480.71s。按仓库默认使用两个 worker,通过项目声明的 extras 在仓库本地环境准备可选依赖,无依赖文件变更。唯一失败为 `tests/frontend/server/runtime_artifacts/test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes`:本地 Python 3.10/macOS 的 `mimetypes.guess_type("reports/summary.md")` 未返回类型,得到 `application/octet-stream`,而测试期待 `text/markdown`。该测试及整个 `frontend/server/runtime_artifacts/` 实现与官方 main 相同。此无关基线问题仍是验证限制,不算通过。 -- **pass**:重新获取 `origin/main` 和 `upstream/main`,仍为 `035387d1` 和 `171d8d86`;`git -c rebase.autoStash=true rebase upstream/main` 显示已最新,并恢复全部整合改动。 -- **pass**:双语链接/需求 ID/引用标识和 scoped diff 空白检查。直接最终审查没有发现漏掉增量、新增依赖、后端/聊天变更或覆盖官方修复。 -- **pass**:同步后再次通过 `uv run --no-sync --extra dev pre-commit run --all-files`、`uv run --no-sync --extra dev pytest tests/integrations/mpa_managed tests/cli/test_frontend_deploy_iam.py -q`(678 passed)、`npm --prefix frontend test`(1,377 Node / 65 Vitest)。`--no-sync` 保留仓库本地已准备的可选依赖。提交/推送元数据以 Git 和官方 PR 为准。 - -| 需求 | 任务 | 验收 | 证据 | -| --- | --- | --- | --- | -| FR-1 | T-1 | AC-1:全部有效增量保留 | 83 路径核对及记录的官方例外:pass。 | -| FR-2 | T-2 | AC-2:MPA 仅基本信息,普通行为不变 | 10 回归用例及本地浏览器:pass。 | -| FR-3 | T-3 | AC-3:测试、文档及匹配资源 | 前端/构建/i18n/资源/覆盖率及双语审查:pass。 | -| FR-4 | T-4 | AC-4:保留官方修复并如实执行检查 | rebase、pre-commit、定向测试:pass;全量回归记录一个未修改的 MIME 基线失败。 | diff --git a/prd-spec/bugfixes/mpa-upstream-readiness/2026-10-13-startup-and-lockfile.md b/prd-spec/bugfixes/mpa-upstream-readiness/2026-10-13-startup-and-lockfile.md deleted file mode 100644 index bb6a67452..000000000 --- a/prd-spec/bugfixes/mpa-upstream-readiness/2026-10-13-startup-and-lockfile.md +++ /dev/null @@ -1,52 +0,0 @@ -# MPA upstream PR startup and lock-file readiness - -[中文版](2026-10-13-startup-and-lockfile.zh.md) - -- Change ID: `mpa-upstream-readiness`; date: 2026-10-13; status: implemented. -- Scope: the four MPA lifecycle commits rebased onto official `b8038fb1`. -- Contracts: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md); [previous lock-file restoration](../frontend-lockfile-sync/2026-10-09-restore-esbuild-lock-entries.md). - -## Evidence, requirements and scope - -Full offline regression on `ea949fee` returned 7,184 passed, 2 failed, 53 skipped and 4 xfailed. Official `b8038fb1` in an isolated source snapshot passes the Studio startup test but reproduces the artifact-writer MIME test failure under the same local Python 3.10/macOS environment. The branch's new OV adapter eagerly imports `veadk.utils.volcengine_sign`, violating the existing lazy Studio startup contract. The deleted 512 lock-file lines also cause npm 10.9.2 (the frontend CI runtime family) to reject esbuild entries, despite npm 11 accepting them. - -FR-1: Registering MPA routes/configuration must preserve the existing lazy startup boundary. Load the signing helper only when an OV API request runs. Preserve wire parameters, retry/error/cancellation behavior and provider boundaries. -FR-2: Restore the complete official frontend lock file. Package dependency declarations match official main; only test-script selection differs. Do not change dependency ranges or unrelated artifact-writer logic. - -## Design, tasks and acceptance - -T-1 / AC-1: Use the already failing isolated Studio startup test as the regression. Move the signing import into the existing request closure; point two adapter mock patches at the helper's owning module. Update the paired maintained OV startup contract. -T-2 / AC-2: Restore generated `frontend/package-lock.json` from `upstream/main` and repeat an isolated npm 10.9.2 `ci --ignore-scripts --dry-run` using Linux/x64 selection. -T-3 / AC-3: Rerun startup, managed/deployment/RBAC tests, affected frontend tests/coverage, lint/type checks and all-file pre-commit; record actual outcomes. Existing cloud operations, credentials, session identity and cleanup policies remain unchanged. - -## Review, authorization and risks - -Direct review (review-spec unavailable) confirms this is a correction within the already approved OV feature and previously approved lock restoration, needed to prepare the upstream PR the user explicitly requested. No new feature or cloud action is introduced. No speculative wrapper, dependency upgrade or unrelated MIME fix is needed. Keep the original published development branch; commit the corrections on the separate upstream PR branch. Local baseline failures must be disclosed, not hidden by skipping them or altering unrelated production behavior. - -## Verification - -Initial failing evidence above is recorded before correction. Final outcomes will be added after execution. Live cloud/OV smoke is not_run because no additional paid resource operation is authorized. - -### Final preparation results (2026-10-13) - -Scope: `ea949fee` plus this readiness correction, based on `b8038fb1`. AC-1–AC-3 complete. - -- pass: 1,185 affected startup/managed/profile-route/RBAC tests after correction; no external services used. The global suite was not repeated because the correction only delays one signing import and restores generated metadata; the failing startup case and all affected deployment boundaries were rerun. -- pass: 1,389 Node tests, 95 React tests, production build, 22 sidecar coverage tests (lines 100%, branches 94.44%), i18n and packaged asset references. Build output remains byte-identical to the rebased tracked assets. -- pass: npm 10.9.2 isolated Linux/x64 `ci --ignore-scripts --dry-run` with the restored official lock; dependency declarations/ranges unchanged. This validates the lock graph, not a real Linux installation. -- pass: Ruff 0.11.12, Pyright (0 errors/warnings) for the corrected adapter/tests; all-file pre-commit including secret/YAML scans; paired-link/identifier/whitespace review. -- fail (baseline): the full local suite before correction had 7,184 passed, 2 failed, 53 skipped and 4 xfailed. Startup is fixed; `tests/frontend/server/runtime_artifacts/test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes` also fails on unchanged official main under local Python 3.10/macOS (Markdown MIME resolves to `application/octet-stream`). That unrelated writer/test was not changed. -- not_run: additional paid cloud/OV smoke and real Codex/Pi smoke; no harness protocol changes or live-resource authorization. - -The user explicitly authorized creating the official PR on 2026-10-13; this includes the necessary preparation commit and branch push, not merging or deployment. The original fork development branch remains unchanged. Actual commands: - -```bash -uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" -uv run --extra dev pytest -n 2 -q tests/cli/test_studio_startup_imports.py tests/integrations/mpa_managed tests/frontend/server/mpa/test_runtime_profile_routes.py tests/cli/test_studio_rbac.py -uv run --extra dev pre-commit run --all-files -npm --prefix frontend test -npm --prefix frontend run build -npm --prefix frontend run test:harness-sidecar-coverage -npm --prefix frontend run check:i18n -npm --prefix frontend run test:webui-assets -``` diff --git a/prd-spec/bugfixes/mpa-upstream-readiness/2026-10-13-startup-and-lockfile.zh.md b/prd-spec/bugfixes/mpa-upstream-readiness/2026-10-13-startup-and-lockfile.zh.md deleted file mode 100644 index 4e2accef9..000000000 --- a/prd-spec/bugfixes/mpa-upstream-readiness/2026-10-13-startup-and-lockfile.zh.md +++ /dev/null @@ -1,52 +0,0 @@ -# MPA 官方 PR 的启动与锁文件检查修正 - -[English](2026-10-13-startup-and-lockfile.md) - -- Change ID:`mpa-upstream-readiness`;日期:2026-10-13;状态:implemented。 -- 范围:rebase 到官方 `b8038fb1` 的四个 MPA 生命周期提交。 -- 契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md);[原锁文件恢复设计](../frontend-lockfile-sync/2026-10-09-restore-esbuild-lock-entries.zh.md)。 - -## 证据、需求与范围 - -`ea949fee` 全量离线回归得到 7,184 passed、2 failed、53 skipped、4 xfailed。隔离源码快照中的官方 `b8038fb1` 启动测试通过,但同一 Python 3.10/macOS 环境下复现了产物写入 MIME 测试失败。分支新增 OV 适配器提前导入 `veadk.utils.volcengine_sign`,违反已有 Studio 延迟加载启动契约。被删除的 512 行锁文件也使 npm 10.9.2(前端 CI 所用运行时系列)拒绝 esbuild 条目,虽然 npm 11 接受。 - -FR-1:注册 MPA 路由/配置时保留原延迟加载启动边界。只在执行 OV API 请求时加载签名函数。保留请求参数、重试/错误/取消和平台边界。 -FR-2:恢复官方完整前端锁文件。依赖声明与官方 main 相同,仅测试脚本选择不同。不更改依赖范围或无关产物写入逻辑。 - -## 设计、任务与验收 - -T-1 / AC-1:以已失败的隔离 Studio 启动测试作回归。将签名 import 移入原请求闭包,两处适配器模拟 patch 指向函数所属模块;同步双语 OV 启动契约。 -T-2 / AC-2:从 `upstream/main` 恢复生成的 `frontend/package-lock.json`,在隔离目录按 Linux/x64 选择重跑 npm 10.9.2 `ci --ignore-scripts --dry-run`。 -T-3 / AC-3:重跑启动、managed/部署/RBAC 测试、相关前端测试/覆盖率、lint/type 和全文件 pre-commit,记录实际结果。既有云操作、凭据、会话身份和清理策略不变。 - -## 审查、授权与风险 - -直接审查(review-spec 不可用)确认这是已批准 OV 功能和此前已批准锁文件恢复范围内的修正,为用户明确要求的官方 PR 做准备。不增加功能或云操作;无需推测性 wrapper、依赖升级或无关 MIME 修复。保留已发布原开发分支,在独立官方 PR 分支提交修正。须披露本地基线失败,不以跳过或修改无关生产行为来隐藏。 - -## 验证 - -以上失败证据记录于修正前,执行后补充最终结果。真实云/OV smoke 为 not_run,未授权额外付费资源操作。 - -### 最终准备结果(2026-10-13) - -范围:`ea949fee` 加本次检查修正,基于 `b8038fb1`。AC-1–AC-3 完成。 - -- pass:修正后 1,185 项相关启动/managed/Profile 路由/RBAC 测试;未使用外部服务。因修正仅延迟一个签名 import、恢复生成元数据,没有重复全仓测试;已重跑失败启动用例及所有受影响部署边界。 -- pass:1,389 项 Node、95 项 React、生产构建、22 项 sidecar 覆盖测试(lines 100%、branches 94.44%)、国际化与发布资源引用。构建产物与 rebase 后已跟踪资源逐字节一致。 -- pass:恢复官方锁文件后,隔离目录 npm 10.9.2 按 Linux/x64 执行 `ci --ignore-scripts --dry-run`;依赖声明/范围不变。验证的是锁文件图,并非真实 Linux 安装。 -- pass:修正适配器/测试的 Ruff 0.11.12、Pyright(0 错误/警告),全文件 pre-commit(含密钥/YAML 扫描),双语链接/标识符/空白审查。 -- fail(基线):修正前全量本地测试 7,184 passed、2 failed、53 skipped、4 xfailed。启动问题已修正;`tests/frontend/server/runtime_artifacts/test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes` 在原样官方 main、同一 Python 3.10/macOS 环境也失败(Markdown MIME 解析为 `application/octet-stream`)。未修改无关 writer/测试。 -- not_run:额外付费云/OV smoke 及真实 Codex/Pi smoke;未改 harness 协议,也未授权真实资源操作。 - -用户于 2026-10-13 明确授权创建官方 PR,包含必要准备提交及分支推送,不包含合并/部署。原 fork 开发分支不变。实际命令: - -```bash -uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" -uv run --extra dev pytest -n 2 -q tests/cli/test_studio_startup_imports.py tests/integrations/mpa_managed tests/frontend/server/mpa/test_runtime_profile_routes.py tests/cli/test_studio_rbac.py -uv run --extra dev pre-commit run --all-files -npm --prefix frontend test -npm --prefix frontend run build -npm --prefix frontend run test:harness-sidecar-coverage -npm --prefix frontend run check:i18n -npm --prefix frontend run test:webui-assets -``` diff --git a/prd-spec/bugfixes/mpa-worker-diagnostics/2026-10-15-worker-provider-metadata.md b/prd-spec/bugfixes/mpa-worker-diagnostics/2026-10-15-worker-provider-metadata.md deleted file mode 100644 index aeef49aec..000000000 --- a/prd-spec/bugfixes/mpa-worker-diagnostics/2026-10-15-worker-provider-metadata.md +++ /dev/null @@ -1,100 +0,0 @@ -# MPA Worker Provider Metadata Diagnostics - -[中文版](2026-10-15-worker-provider-metadata.zh.md) - -## Metadata - -- Change ID: `mpa-worker-diagnostics` -- Created: 2026-10-15 -- Revised: 2026-10-15 -- Status: approved -- Related component spec: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md) - -## Background and Evidence - -BytePlus Studio MPA creation failed for task `84f8dfe6-e7fa-4063-9803-912ff12fbc3a` at stage `worker` with diagnostics `create_worker/provider_error`, then surfaced as `creationFailed` at stage `network`. The private task database recorded no `error_code` or `request_id` for the Worker failure. Read-only checks showed: - -- `/web/ui-config` is `provider:"byteplus"` and `/web/runtime-config` reports `credentials:true`. -- The BytePlus PG bootstrap database already has admin and per-agent business Workspace records for `mpa_agent_wlc-ap-test`. -- BytePlus AgentKit Tools list has no `wlc-ap-test` Tool and no Tool tagged with the target `mpa_agent_key`. -- The current child runner drops raw stderr and only persists allowlisted `MPA_EVENT` diagnostics, so the historical provider response cannot be recovered. -- Runtime and OpenViking paths already extract safe provider `Error.Code` and `RequestId` from wrapped SDK bodies, but Worker retry diagnostics only classify the exception and discard those identifiers. - -Assumption: the provider response may contain a structured error code in the SDK wrapper body or response object, as seen in Runtime/OpenViking tests. This change does not assume which cloud-side error is present. - -## Goals and Non-Goals - -Goals: - -- Preserve safe provider `errorCode` and `requestId` for Worker API failures when available. -- Reuse the existing `task_diagnostics.error_code` and `request_id` fields. -- Keep raw provider messages, request bodies, credentials, image URLs with tokens, and exception text out of task state and logs. -- Avoid changing Worker create payloads, resource lifecycle, retry counts, UI behavior, or cloud permissions. - -Non-goals: - -- Fix the underlying BytePlus `CreateTool` rejection. -- Add new UI fields or expose raw provider messages to the browser. -- Retry failed historical tasks automatically. -- Change Runtime/OpenViking behavior beyond moving shared safe-metadata extraction to a common helper. - -## Scenarios and Requirements - -- `FR-1`: Given `WorkerCloud.create` raises an SDK error whose wrapped body contains `ResponseMetadata.Error.Code` and `RequestId`, when `retry_worker` records the failed attempt, then `task_diagnostics` stores the allowlisted `error_code` and `request_id`. -- `FR-2`: Given the SDK error body is malformed, oversized, free text, or token-shaped, when diagnostics run, then no raw text or unsafe identifier is stored. -- `FR-3`: Given a safe provider code is extracted, when the existing category is generic `provider_error` or `unknown`, then diagnostics may refine the category through the existing safe code-category map. -- `FR-4`: Given Runtime create/update failures already record metadata, when the helper moves to diagnostics, then existing Runtime behavior and tests remain equivalent. - -## Design and Contract Impact - -The implementation adds a shared safe helper in `veadk.integrations.mpa.managed.diagnostics` to inspect a bounded exception chain for: - -- direct `error_code` / `request_id` attributes, -- structured `response.json()` or `body` metadata handled by the existing OpenViking helper, -- single string/bytes SDK arguments containing bounded JSON or bytes literals. - -`retry_worker` uses the helper before recording diagnostics. It passes the safe identifiers to the existing `report()` path, which already validates allowlisted fields. The helper rejects malformed, oversized, free-text and token-shaped values. - -Component spec impact: no contract change. `task_diagnostics` already supports optional `error_code` and `request_id`, and the redaction boundary remains unchanged. This bugfix fills missing Worker coverage for the existing observability contract. - -Security impact: positive. The change increases structured diagnosis without widening persisted data. Raw provider messages and payloads remain excluded. - -Compatibility impact: additive diagnostic metadata only. Existing rows remain valid and callers that ignore the fields are unchanged. - -## Implementation Tasks - -- `T-1`: Move safe SDK metadata extraction into `diagnostics.py`. -- `T-2`: Update Runtime create/update diagnostics to use the shared helper without behavior drift. -- `T-3`: Update Worker retry diagnostics to report safe `errorCode` and `requestId`. -- `T-4`: Add regression coverage for Worker SDK wrapper metadata and unsafe body rejection. - -## Verification and Acceptance - -| Requirement | Task | Acceptance criterion | Test or verification command | Result/evidence | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1`, `T-3`, `T-4` | `AC-1`: Worker diagnostics preserve safe provider code/request ID | `uv run --extra dev pytest tests/integrations/mpa_managed/test_runtime_diagnostics.py tests/integrations/mpa_managed/test_worker_recovery.py -q` | `pass`, 57 passed on 2026-10-15 | -| `FR-2` | `T-1`, `T-4` | `AC-2`: malformed/unsafe bodies do not persist text or tokens | Same targeted pytest command | `pass`, 57 passed on 2026-10-15 | -| `FR-3` | `T-3`, `T-4` | `AC-3`: safe codes can refine generic Worker categories | Same targeted pytest command | `pass`, 57 passed on 2026-10-15 | -| `FR-4` | `T-2` | `AC-4`: existing Runtime diagnostics tests still pass | Same targeted pytest command | `pass`, 57 passed on 2026-10-15 | - -Required targeted command: - -```bash -uv run --extra dev pytest tests/integrations/mpa_managed/test_runtime_diagnostics.py tests/integrations/mpa_managed/test_worker_recovery.py -q -``` - -Live BytePlus confirmation requires restarting Studio with the updated backend and retrying the same or a new MPA creation request. That cloud retry is separate from automated unit verification. - -## Risks and Open Questions - -- The provider may still omit structured metadata; in that case diagnostics will remain generic and no raw fallback will be stored. -- The likely BytePlus cause remains unconfirmed until a new retry records a code or cloud control-plane logs are checked. -- `.zshrc` currently starts Studio as a side effect after exporting BytePlus credentials; interactive shell launches can accidentally attempt a second Studio process on port 8000. - -## Review and Delivery Record - -Design review conclusion: this is a narrow observability bugfix using an already-established redaction pattern. It does not change cloud payloads or user-visible API shape. - -User approval: active debugging request requested continuous monitoring and root-cause diagnosis; this diagnostic enhancement is required to avoid guessing from generic `provider_error`. - -Implementation completed on 2026-10-15. Targeted pytest passed with 57 tests; `uv run ruff check veadk/integrations/mpa/managed/diagnostics.py veadk/integrations/mpa/managed/runtime.py tests/integrations/mpa_managed/test_worker_recovery.py` and `git diff --check` also passed. diff --git a/prd-spec/bugfixes/mpa-worker-diagnostics/2026-10-15-worker-provider-metadata.zh.md b/prd-spec/bugfixes/mpa-worker-diagnostics/2026-10-15-worker-provider-metadata.zh.md deleted file mode 100644 index 4c79a41f3..000000000 --- a/prd-spec/bugfixes/mpa-worker-diagnostics/2026-10-15-worker-provider-metadata.zh.md +++ /dev/null @@ -1,100 +0,0 @@ -# MPA Worker 服务商元数据诊断 - -[English](2026-10-15-worker-provider-metadata.md) - -## 元数据 - -- 变更 ID:`mpa-worker-diagnostics` -- 创建日期:2026-10-15 -- 修订日期:2026-10-15 -- 状态:approved -- 相关组件规格:[Studio MPA creation](../../../specs/studio-mpa-creation/README.zh.md) - -## 背景与证据 - -BytePlus Studio MPA 创建任务 `84f8dfe6-e7fa-4063-9803-912ff12fbc3a` 在 `worker` 阶段失败,诊断为 `create_worker/provider_error`,随后在 `network` 阶段展示为 `creationFailed`。私有任务数据库没有记录 Worker 失败的 `error_code` 或 `request_id`。只读检查显示: - -- `/web/ui-config` 为 `provider:"byteplus"`,`/web/runtime-config` 返回 `credentials:true`。 -- BytePlus PG 引导库已有 `mpa_agent_wlc-ap-test` 的管理 Workspace 和独立业务 Workspace 记录。 -- BytePlus AgentKit Tools 列表中没有名为 `wlc-ap-test` 的 Tool,也没有带目标 `mpa_agent_key` 的 Tool。 -- 当前子进程丢弃原始 stderr,只持久化白名单 `MPA_EVENT` 诊断,因此历史服务商响应无法恢复。 -- Runtime 和 OpenViking 路径已经会从 SDK 包装 body 中安全提取服务商 `Error.Code` 与 `RequestId`,但 Worker 重试诊断只分类异常并丢弃这些标识。 - -假设:与 Runtime/OpenViking 测试中的形态一致,服务商响应可能在 SDK 包装 body 或 response 对象中包含结构化错误码。本变更不假设具体的云侧错误类型。 - -## 目标与非目标 - -目标: - -- Worker API 失败时,如服务商提供安全的 `errorCode` 和 `requestId`,则保留到诊断中。 -- 复用现有 `task_diagnostics.error_code` 和 `request_id` 字段。 -- 原始服务商文案、请求体、凭据、带 token 的镜像 URL 以及异常文本不得进入任务状态或日志。 -- 不改变 Worker 创建载荷、资源生命周期、重试次数、UI 行为或云权限。 - -非目标: - -- 直接修复 BytePlus `CreateTool` 的底层拒绝原因。 -- 新增 UI 字段或向浏览器暴露原始服务商文案。 -- 自动重试历史失败任务。 -- 除抽取逻辑共用化外,不改变 Runtime/OpenViking 行为。 - -## 场景与需求 - -- `FR-1`:给定 `WorkerCloud.create` 抛出的 SDK 错误包装 body 含 `ResponseMetadata.Error.Code` 和 `RequestId`,当 `retry_worker` 记录失败尝试时,`task_diagnostics` 应保存白名单内的 `error_code` 和 `request_id`。 -- `FR-2`:给定 SDK 错误 body 格式错误、过大、为自由文本或疑似 token,当诊断运行时,不得保存原始文本或不安全标识。 -- `FR-3`:给定已抽取到安全服务商错误码,当现有分类是通用 `provider_error` 或 `unknown` 时,诊断可通过现有安全错误码分类表细化分类。 -- `FR-4`:给定 Runtime 创建/更新失败已记录元数据,当 helper 移入 diagnostics 后,现有 Runtime 行为和测试应保持等价。 - -## 设计与契约影响 - -实现会在 `veadk.integrations.mpa.managed.diagnostics` 中新增共享安全 helper,检查有界异常链中的: - -- 直接 `error_code` / `request_id` 属性; -- 现有 OpenViking helper 可处理的结构化 `response.json()` 或 `body` 元数据; -- 单个字符串/字节 SDK 参数中的有界 JSON 或 bytes 字面量。 - -`retry_worker` 在记录诊断前使用该 helper,并将安全标识传入现有 `report()` 路径;`report()` 已经对白名单字段做校验。helper 会拒绝格式错误、过大、自由文本和疑似 token 的值。 - -组件规格影响:无契约变更。`task_diagnostics` 已支持可选 `error_code` 和 `request_id`,脱敏边界保持不变。本修复只是补齐现有可观测性契约在 Worker 路径上的覆盖。 - -安全影响:正向。变更增加结构化诊断,但不扩大持久化数据范围。原始服务商文案和载荷仍被排除。 - -兼容影响:只增加诊断元数据。已有记录仍有效,忽略这些字段的调用方不受影响。 - -## 实现任务 - -- `T-1`:将安全 SDK 元数据抽取移动到 `diagnostics.py`。 -- `T-2`:更新 Runtime 创建/更新诊断以使用共享 helper,并保持行为不漂移。 -- `T-3`:更新 Worker 重试诊断以记录安全 `errorCode` 和 `requestId`。 -- `T-4`:补充 Worker SDK 包装元数据与不安全 body 拒绝的回归测试。 - -## 验证与验收 - -| 需求 | 任务 | 验收标准 | 测试或验证命令 | 结果/证据 | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1`, `T-3`, `T-4` | `AC-1`:Worker 诊断保留安全服务商错误码/request ID | `uv run --extra dev pytest tests/integrations/mpa_managed/test_runtime_diagnostics.py tests/integrations/mpa_managed/test_worker_recovery.py -q` | `pass`,2026-10-15 共 57 个测试通过 | -| `FR-2` | `T-1`, `T-4` | `AC-2`:格式错误或不安全 body 不持久化文本或 token | 同一定向 pytest 命令 | `pass`,2026-10-15 共 57 个测试通过 | -| `FR-3` | `T-3`, `T-4` | `AC-3`:安全错误码可细化通用 Worker 分类 | 同一定向 pytest 命令 | `pass`,2026-10-15 共 57 个测试通过 | -| `FR-4` | `T-2` | `AC-4`:现有 Runtime 诊断测试仍通过 | 同一定向 pytest 命令 | `pass`,2026-10-15 共 57 个测试通过 | - -必需的定向命令: - -```bash -uv run --extra dev pytest tests/integrations/mpa_managed/test_runtime_diagnostics.py tests/integrations/mpa_managed/test_worker_recovery.py -q -``` - -真实 BytePlus 确认需要使用更新后的后端重启 Studio,并对同一或新的 MPA 创建请求执行重试。该云端重试与自动化单测验证分开记录。 - -## 风险与待确认问题 - -- 服务商仍可能不提供结构化元数据;这种情况下诊断会保持通用分类,且不会使用原始文本兜底。 -- 在新的重试记录错误码或查询云侧控制面日志前,BytePlus 的具体原因仍未最终确认。 -- 当前 `.zshrc` 在导出 BytePlus 凭据后会直接启动 Studio;交互式 shell 启动可能意外尝试在 8000 端口启动第二个 Studio 进程。 - -## 评审与交付记录 - -设计评审结论:这是一个很窄的可观测性 bugfix,复用既有脱敏模式,不改变云端载荷或用户可见 API 形态。 - -用户批准:当前调试请求要求持续关注创建流程并定位根因;该诊断增强是避免基于通用 `provider_error` 猜测的必要步骤。 - -实现已于 2026-10-15 完成。定向 pytest 通过 57 个测试;`uv run ruff check veadk/integrations/mpa/managed/diagnostics.py veadk/integrations/mpa/managed/runtime.py tests/integrations/mpa_managed/test_worker_recovery.py` 和 `git diff --check` 也已通过。 diff --git a/prd-spec/bugfixes/mpa-worker-naming/2026-09-28-agent-derived-worker-name.md b/prd-spec/bugfixes/mpa-worker-naming/2026-09-28-agent-derived-worker-name.md deleted file mode 100644 index 92def39ec..000000000 --- a/prd-spec/bugfixes/mpa-worker-naming/2026-09-28-agent-derived-worker-name.md +++ /dev/null @@ -1,52 +0,0 @@ -# Agent-derived MPA sandbox template names - -- Change ID: `mpa-worker-naming`; created/revised: 2026-09-28; status: implemented. -- [中文](2026-09-28-agent-derived-worker-name.zh.md) -- Contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), CON-3. - -## Evidence, goals and scope - -`managed/worker.py` currently names tools `mpa_worker_` plus a scoped hash. The inspected ArkClaw implementation instead derives its formal tool name from the trimmed agent ID, replacing hyphens with underscores. Operators need to identify templates by agent ID. Change only managed MPA tool creation; do not rename existing cloud tools, change Runtime names/IDs, images, networks, tags, database names, general agents, or create debug tools. - -## Requirements and scenarios - -- FR-1 / AC-1: A fresh `mi-example` agent creates/discovers the template `mi_example`; trim surrounding whitespace and replace every `-` with `_`. -- FR-2 / AC-2: Registered worker IDs and explicitly supplied existing IDs retain their bindings without discovery, creation or renaming. -- FR-3 / AC-3: A pre-change unfinished intent retains its hashed name, full payload hash and ClientToken for retry, whether the provider already created the tool or not. A changed payload still fails before discovery/creation. -- FR-4 / AC-4: Name collisions cannot authorize adoption. Preserve ownership verification, scoped tags, timeout, cancellation and terminal-state handling. - -## Design and compatibility - -Build the fresh request with the agent-derived Name. When a persisted worker_hash differs, calculate the request hash with the former hashed Name. Only an exact match permits that legacy request to continue; otherwise retain the existing configuration-change error. No schema or API additions, migrations, dependencies, new permissions, or secret persistence. Registered IDs bypass request-name construction as before. The existing account lock remains responsible for serialization. Name normalization can collide, so the existing collision refusal and ownership checks remain mandatory. Rollback after a new unfinished intent requires completing that intent with the new code; existing bound IDs remain usable. - -## Tasks and affected files - -- T-1 (FR-1–FR-4): Add isolated regression coverage in `tests/integrations/mpa_managed/test_worker.py`; demonstrate fresh naming fails before implementation. -- T-2 (FR-1, FR-3): Update `veadk/integrations/mpa/managed/worker.py` minimally. -- T-3: Synchronize this pair, Studio creation CON-3, and the managed integration README pair; run worker/managed regression, Ruff, Pyright and whitespace checks. - -## Review and approval - -2026-09-28: The user approved the proposed agent-derived naming with existing-resource/retry compatibility by saying “改为旧规则”. `review-spec` is unavailable; direct design review checked boundaries, failure semantics, ownership, security, rollback, testability and language equivalence. No blockers. Production edits follow this review. - -## Verification - -Scope: working-tree worker naming diff atop the current branch; unrelated shared-network changes are preserved. T-1–T-3 results are recorded below. Live cloud creation: `not_run` (not required for this name/payload-only change; no cloud mutations authorized here). Browser/build: `not_applicable` (no frontend or browser API changes). Commit gates: `not_run` (no commit requested). - -### Verification record, 2026-09-28 - -T-1–T-3 / AC-1–AC-4 complete for the working-tree naming change. Pre-implementation naming regression: expected `fail` (2 failed, 14 passed). After implementation: - -- `uv run --extra dev pytest tests/integrations/mpa_managed/test_worker.py tests/integrations/mpa_managed/test_worker_recovery.py tests/integrations/mpa_managed/test_worker_metadata.py -q --tb=short`: `pass`, 67 passed. -- `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py tests/cli/test_frontend_deploy_iam.py -q --tb=short`: `pass`, 422 passed, 5 existing dependency deprecation warnings. -- `uv run --with ruff --with pyright ruff check veadk/integrations/mpa/managed/worker.py tests/integrations/mpa_managed/test_worker.py`: `pass`; initial import-order failure corrected. -- `uv run --with ruff --with pyright pyright veadk/integrations/mpa/managed/worker.py tests/integrations/mpa_managed/test_worker.py`: `pass`, 0 errors. -- `git diff --check`: `pass`. - -Final code review: only an exact legacy payload hash permits the legacy name; idempotency tokens and ownership markers are preserved; existing IDs bypass creation. Requirements/contracts are synchronized across languages. Initial uv cache access was sandbox-blocked; checks completed after approved access. Per the user's existing preference, record this verification only, without changing environment guidance. No commit, push or cloud mutation. - -Additional checks: repository-pinned Ruff lint/format, PRD relative links, bilingual counterparts, and changed-file `gitleaks dir --redact` scanning all `pass`. Frontend documentation updated; no generated assets changed. - -## Rebase validation — 2026-10-07 - -Reconciled with main `e0448a4d` for PR #17. The original functional patch is retained; upstream discovery/authentication and Worker recovery changes are preserved. See the [combined reconciliation and verification record](../mpa-shared-network-bootstrap/2026-09-28-registry-owned-network.md#pr-17-rebase-reconciliation--2026-10-07). diff --git a/prd-spec/bugfixes/mpa-worker-naming/2026-09-28-agent-derived-worker-name.zh.md b/prd-spec/bugfixes/mpa-worker-naming/2026-09-28-agent-derived-worker-name.zh.md deleted file mode 100644 index 09d71330a..000000000 --- a/prd-spec/bugfixes/mpa-worker-naming/2026-09-28-agent-derived-worker-name.zh.md +++ /dev/null @@ -1,52 +0,0 @@ -# 从智能体 ID 派生 MPA 沙箱模板名称 - -- Change ID:`mpa-worker-naming`;创建/修订:2026-09-28;状态:implemented。 -- [English](2026-09-28-agent-derived-worker-name.md) -- 契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-3。 - -## 证据、目标与范围 - -`managed/worker.py` 当前使用 `mpa_worker_` 加作用域哈希命名工具。已核对的 ArkClaw 实现则去掉智能体 ID 两端空白,将连字符换成下划线,作为正式工具名称。目标是让用户按智能体 ID 识别模板。仅修改托管 MPA 工具创建;不重命名已有云工具,不改变 Runtime 名称/ID、镜像、网络、标签、数据库名称或通用智能体,不创建调试工具。 - -## 需求与场景 - -- FR-1 / AC-1:新 `mi-example` 智能体创建/发现模板 `mi_example`;去掉两端空白,将所有 `-` 替换为 `_`。 -- FR-2 / AC-2:已登记 worker ID 和显式指定的已有 ID 保留绑定,不进行发现、创建或重命名。 -- FR-3 / AC-3:变更前未完成的创建意图,重试时保留哈希名称、完整请求哈希和 ClientToken,无论云端是否已经创建工具。若请求内容改变,仍在发现/创建前失败。 -- FR-4 / AC-4:同名不构成接管授权。保留所有权校验、作用域标签、超时、取消和终态处理。 - -## 设计与兼容 - -新请求使用智能体派生的 Name。已有 worker_hash 不匹配时,使用原哈希 Name 重新计算请求哈希。仅完全匹配才继续使用旧请求,否则保留配置变更错误。无数据库结构/API 扩展、迁移、新依赖、新权限或密钥持久化。已有绑定 ID 仍直接跳过请求名称构造。现有账号锁继续负责串行化。名称规范化可能产生碰撞,因此必须保留同名冲突拒绝和所有权检查。新意图尚未完成时回退旧代码,需使用新代码完成该意图;已有绑定 ID 仍可使用。 - -## 任务与影响文件 - -- T-1(FR-1–FR-4):在 `tests/integrations/mpa_managed/test_worker.py` 增加隔离回归测试;实现前证明新命名测试失败。 -- T-2(FR-1、FR-3):最小修改 `veadk/integrations/mpa/managed/worker.py`。 -- T-3:同步双语设计、Studio 创建 CON-3 和托管集成 README 双语文档;运行 worker/managed 回归、Ruff、Pyright 和空白检查。 - -## 评审与批准 - -2026-09-28:用户回复“改为旧规则”,批准已提出的按智能体命名并兼容已有资源/重试方案。`review-spec` 不可用;直接评审已核对边界、失败语义、所有权、安全、回退、可测性及双语等价性,无阻塞项。生产代码修改在本评审之后。 - -## 验证 - -范围:当前分支工作区的 worker 命名改动;保留无关的共享网络修改。T-1–T-3 结果见下文。真实云创建:`not_run`(仅名称/请求变更无需执行;本次不进行云修改)。浏览器/构建:`not_applicable`(无前端或浏览器 API 变更)。提交门禁:`not_run`(用户未要求提交)。 - -### 2026-09-28 验证记录 - -T-1–T-3 / AC-1–AC-4 已完成,范围为当前工作区的命名改动。实现前命名测试 `fail`(2 failed、14 passed),符合预期;实现后: - -- `uv run --extra dev pytest tests/integrations/mpa_managed/test_worker.py tests/integrations/mpa_managed/test_worker_recovery.py tests/integrations/mpa_managed/test_worker_metadata.py -q --tb=short`:`pass`,67 passed。 -- `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py tests/cli/test_frontend_deploy_iam.py -q --tb=short`:`pass`,422 passed,5 条已有依赖弃用警告。 -- `uv run --with ruff --with pyright ruff check veadk/integrations/mpa/managed/worker.py tests/integrations/mpa_managed/test_worker.py`:`pass`;首次 import 排序失败已修正。 -- `uv run --with ruff --with pyright pyright veadk/integrations/mpa/managed/worker.py tests/integrations/mpa_managed/test_worker.py`:`pass`,0 errors。 -- `git diff --check`:`pass`。 - -最终代码评审:完整旧 payload 哈希匹配才保留旧名称;不改变幂等凭据或所有权标识;已有 ID 不进入创建分支。双语需求/契约已同步。uv 缓存首次受沙箱限制,批准访问后检查完成;按用户已有偏好仅记录本次验证,不修改环境说明。无提交、推送或云端修改。 - -附加检查:仓库固定 Ruff 检查及格式检查、PRD 相对链接、双语对应和修改文件的 `gitleaks dir --redact` 扫描均为 `pass`。前端说明已更新,无生成产物变化。 - -## Rebase 验证 — 2026-10-07 - -为 PR #17 与 main `e0448a4d` 协调。保留原功能补丁及上游发现/鉴权和 Worker 恢复改动。参见[组合协调及验证记录](../mpa-shared-network-bootstrap/2026-09-28-registry-owned-network.zh.md#pr-17-rebase-协调--2026-10-07)。 diff --git a/prd-spec/bugfixes/mpa-worker-naming/2026-10-09-runtime-named-worker.md b/prd-spec/bugfixes/mpa-worker-naming/2026-10-09-runtime-named-worker.md deleted file mode 100644 index 52028f2b3..000000000 --- a/prd-spec/bugfixes/mpa-worker-naming/2026-10-09-runtime-named-worker.md +++ /dev/null @@ -1,54 +0,0 @@ -# Use the entered Runtime name for new MPA sandbox templates - -[中文版](2026-10-09-runtime-named-worker.zh.md) - -- Change ID: `runtime-named-worker`; created/revised: 2026-10-09; status: implemented. -- Contract: [Studio MPA creation CON-3](../../../specs/studio-mpa-creation/README.md). -- Predecessor: [agent-derived worker names](2026-09-28-agent-derived-worker-name.md). - -## Background, goals and non-goals - -Named Studio creation keeps an automatically generated agent ID separate from the entered Runtime name. `service.provision` forwards the name to Runtime deployment but not to `ensure_worker`, so sandbox templates still display the generated ID with hyphens replaced by underscores. The user withdrew the proposed internal-ID change and requested only matching new template names to the entered name. - -Keep internal ID generation, ownership, database/Skill Space/channel bindings, Runtime names, image defaults, directory search and the three-step form unchanged. No cloud renames, task/database edits, credential changes, deployment, commit or push are authorized. Generic agents and existing CLI callers without a name remain unchanged. - -## Requirements and scenarios - -- FR-1 / AC-1: A fresh named request creates/discovers a Worker with exactly the validated, trimmed Runtime name, including case, hyphens and underscores. The existing 4–64 ASCII-name contract applies; invalid names fail before Worker cloud calls. The [official CreateTool contract](https://docs.volcengine.com/docs/agentkit/CreateTool_-_Creates_tool?lang=zh) supports the same characters and length. -- FR-2 / AC-2: The service passes the name to Worker preparation; `MPA_AGENT_ID`, scoped ownership tags, registry keys and Runtime `ToolId` retain their original identifiers. Legacy calls without a name still use the normalized agent ID. -- FR-3 / AC-3: Existing bound/configured Worker IDs are authoritative and are not renamed. A pending pre-upgrade Worker intent can replay the normalized-ID name or older hashed name only if the entire candidate payload matches its persisted hash, retaining the original ClientToken. All other payload changes fail before discovery/creation. -- FR-4 / AC-4: New pending intents store their selected `worker_name` in the existing deployment JSON. A later different name must not be treated as a legacy fallback. A matching name owned by another agent is still an error; no silent adoption or success. - -Given a named fresh request, the template name matches the Runtime name and internal bindings remain stable. Given a lost create response or upgrade, the exact pending payload/token is retried. Given a registered Worker, no discovery/create/rename occurs. Given an unrelated duplicate name or changed image, fail without weakening ownership checks. - -## Design, contracts and affected files - -Add an optional keyword-only `runtime_name` to `ensure_worker` and forward it from `service.provision`. Validate it with the existing Runtime-name validator and use it directly as new CreateTool `Name`. Existing IDs bypass naming reconciliation. Persist nonsecret `worker_name` with newly saved intents. For old records without that field, compare the complete current payload against the two historical Name variants before permitting fallback; retain the field's absence across retries so repeated lost responses still replay the original name. Names alone never authorize reuse. Existing account locking, retry bounds, timeout/cancellation and metadata verification remain unchanged. - -Production files: `managed/worker.py`, `managed/service.py`. Tests: `test_worker.py`, `test_service.py`. Maintain bilingual creation specs and managed README naming sections. No SQL migration, external API/request-body change, new dependency, permission or frontend/build artifact is needed. Public optional-keyword addition preserves existing callers. Search already uses Runtime `name`, so its behavior is unchanged. Human names can collide; existing collision refusal remains required rather than adding unrequested suffixes. - -## Tasks, acceptance and verification - -1. T-1 / FR-1–FR-4: Add failing regressions for exact names, boundaries, service forwarding, pending replay, changed-name/image refusal, collision refusal and existing-ID reuse. -2. T-2 / FR-1–FR-4: Implement the optional name and full-hash legacy compatibility. -3. T-3: Reconcile both documentation languages; run targeted Worker/service tests, managed/CLI regression, changed-file Ruff/Pyright, pre-commit and whitespace/link checks. - -Commands: `uv run --extra dev pytest tests/integrations/mpa_managed/test_worker.py tests/integrations/mpa_managed/test_worker_recovery.py tests/integrations/mpa_managed/test_service.py -n 2 -q`; `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -n 2 -q`; Ruff/Pyright on changed Python files; `uv run --extra dev pre-commit run --all-files`; `git diff --check`. Real cloud creation is not authorized and is reported separately. Frontend/browser/sidecar gates are not applicable because this change touches only managed provisioning, with no UI or sidecar changes. - -## Review, approval and risks - -The user's final instruction “保持原样吧,但是把沙箱模板的名称也换成手填的就行” approves this bounded scope and supersedes the internal-ID proposal, which made no code changes. `review-spec` is unavailable; direct review checked ownership, hash/token recovery, existing IDs, exact-name constraints, cancellation/timeouts, bilingual equivalence and testability. No design blockers. An existing same-name Tool can reject a new request. Existing tools will still have their previous names; correcting them requires a separately authorized operation. A pending new-name intent requires this code to replay it after rollback. - -## Delivery evidence - -T-1–T-3 and AC-1–AC-4 are complete. Direct implementation review found and fixed the repeated legacy-retry case before delivery. Existing uncommitted image-discovery rollback is preserved separately; its tests/assets are not attributed to this naming fix. Tested scope: the four changed Python files and affected managed/CLI suites on the uncommitted diff based on `df797f94`, 2026-10-09. - -- `pass`: Regression-first new-name tests initially failed in 16 cases; a subsequent repeated legacy-retry regression failed in two cases before its fix. -- `pass`: Targeted Worker/recovery/service checks: 175 tests. Final managed/CLI regression with two workers: 678 tests, including continuous legacy retry and existing-ID reuse. -- `pass`: Ruff 0.11.12 check/format on the four changed Python files; Pyright: zero errors/warnings. -- `pass`: `uv run --extra dev pre-commit run --all-files`, including both secret checks; `git diff --check`; paired-language and local-link checks. -- `not_run`: Full repository Python regression; the affected managed/CLI integration coverage was selected for this bounded provisioning change. -- `not_run`: Real cloud creation/rename; not authorized. Restart the running Studio service to load the change before creating a fresh named request. -- `not_applicable`: Frontend/browser/build, sidecar and runtime-process smoke gates; no UI, generated assets, sidecar or runtime-process change in this fix. - -No commit, push, deployment or existing-resource rename was performed. diff --git a/prd-spec/bugfixes/mpa-worker-naming/2026-10-09-runtime-named-worker.zh.md b/prd-spec/bugfixes/mpa-worker-naming/2026-10-09-runtime-named-worker.zh.md deleted file mode 100644 index 6aae3d8e1..000000000 --- a/prd-spec/bugfixes/mpa-worker-naming/2026-10-09-runtime-named-worker.zh.md +++ /dev/null @@ -1,54 +0,0 @@ -# 新 MPA 沙箱模板使用手填 Runtime 名称 - -[English](2026-10-09-runtime-named-worker.md) - -- Change ID:`runtime-named-worker`;创建/修订:2026-10-09;状态:implemented。 -- 契约:[Studio MPA 创建 CON-3](../../../specs/studio-mpa-creation/README.zh.md)。 -- 前序:[从智能体 ID 派生 Worker 名称](2026-09-28-agent-derived-worker-name.zh.md)。 - -## 背景、目标与非目标 - -Studio 名称创建将自动生成的智能体 ID 与手填 Runtime 名称分开。`service.provision` 将名称传给 Runtime 部署,却没有传给 `ensure_worker`,所以沙箱模板仍显示将连字符替换为下划线的内部 ID。用户取消了内部 ID 改造,只要求新模板名称与手填名称一致。 - -保持内部 ID 生成、归属、数据库/技能空间/渠道绑定、Runtime 名称、镜像默认值、列表搜索及三步表单不变。未授权云资源改名、任务/数据库修改、凭证变更、部署、提交或推送。通用智能体及不提供名称的旧 CLI 调用不变。 - -## 需求与场景 - -- FR-1 / AC-1:新的名称创建请求创建/发现 Worker 时,使用经过校验、去除首尾空白的 Runtime 名称,保留大小写、连字符和下划线。沿用 4–64 位 ASCII 名称合同;非法名称在 Worker 云调用前失败。[官方 CreateTool 合同](https://docs.volcengine.com/docs/agentkit/CreateTool_-_Creates_tool?lang=zh) 支持相同字符和长度。 -- FR-2 / AC-2:服务将名称传给 Worker 准备;`MPA_AGENT_ID`、作用域归属标签、注册库键及 Runtime `ToolId` 仍使用原标识。不提供名称的旧调用仍使用标准化的智能体 ID。 -- FR-3 / AC-3:已绑定/显式配置的 Worker ID 优先,不进行改名。升级前未完成的 Worker 意图仅在完整候选请求与已存 hash 一致时,才可重放标准化 ID 名称或更早的哈希名称,保留原 ClientToken。其他请求变化在发现/创建前失败。 -- FR-4 / AC-4:新的未完成意图在已有部署 JSON 中保存选中的 `worker_name`。后续不同名称不得被视为旧版兼容回退。同名但属于其他智能体的资源仍报错,不静默接管或视为成功。 - -给定新的名称请求,模板名称与 Runtime 名称一致,内部绑定稳定。给定创建响应丢失或升级,重试完整原请求/token。给定已登记 Worker,不发现/创建/改名。给定无关同名资源或镜像变化,不降低归属检查并直接失败。 - -## 设计、契约与影响文件 - -为 `ensure_worker` 增加可选仅关键字参数 `runtime_name`,由 `service.provision` 传入。使用现有 Runtime 名称校验器校验,并直接作为新 CreateTool `Name`。已有 ID 跳过命名协调。新意图保存时持久化非密钥字段 `worker_name`。旧记录没有该字段时,对两种历史 Name 分别计算完整当前请求的 hash,仅完全匹配才允许回退;重试时仍不补写该字段,确保连续丢失响应时继续重放原名称。名称本身不授权复用。账号锁、重试次数、超时/取消及元数据验证保持不变。 - -生产文件:`managed/worker.py`、`managed/service.py`。测试:`test_worker.py`、`test_service.py`。维护双语创建契约及 managed README 命名段落。不需要 SQL 迁移、外部 API/请求体变更、新依赖、权限或前端/构建产物修改。公开可选关键字参数兼容现有调用。搜索已使用 Runtime `name`,行为不变。用户名称可能冲突;保留现有冲突拒绝,不添加未要求的后缀。 - -## 任务、验收与验证 - -1. T-1 / FR-1–FR-4:补充先失败回归,覆盖准确名称、边界、服务透传、未完成重放、名称/镜像变化拒绝、同名冲突拒绝和已有 ID 复用。 -2. T-2 / FR-1–FR-4:实现可选名称和完整 hash 的旧版兼容。 -3. T-3:同步双语文档;运行 Worker/服务目标测试、managed/CLI 回归、改动文件 Ruff/Pyright、pre-commit 及空白/链接检查。 - -命令:`uv run --extra dev pytest tests/integrations/mpa_managed/test_worker.py tests/integrations/mpa_managed/test_worker_recovery.py tests/integrations/mpa_managed/test_service.py -n 2 -q`;`uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -n 2 -q`;改动 Python 文件 Ruff/Pyright;`uv run --extra dev pre-commit run --all-files`;`git diff --check`。真实云创建未授权,单独报告。前端/浏览器/sidecar 检查不适用,本次只修改托管创建,无 UI 或 sidecar 变化。 - -## 审查、批准与风险 - -用户最终要求“保持原样吧,但是把沙箱模板的名称也换成手填的就行”,批准此范围,并取代尚未修改代码的内部 ID 方案。`review-spec` 不可用;直接审查核对归属、hash/token 恢复、已有 ID、准确名称约束、取消/超时、双语等价和可测试性,无设计阻塞。已有同名 Tool 可能使新请求失败。已有模板继续保留旧名称,修正须另行授权。若回滚后要重放新的名称意图,需要此版本代码。 - -## 交付证据 - -T-1–T-3 与 AC-1–AC-4 已完成。交付前直接实施审查发现并修复了旧任务连续重试的问题。之前未提交的镜像查询撤回独立保留,其测试/构建产物不归属于本次命名修复。测试范围:基于 `df797f94` 的未提交差异中四个 Python 文件及受影响的 managed/CLI 测试,执行日期 2026-10-09。 - -- `pass`:先失败回归中,新名称测试最初 16 项失败;随后旧任务连续重试回归在修复前 2 项失败。 -- `pass`:Worker/恢复/服务目标测试 175 项;最终 managed/CLI 回归使用两个进程,678 项通过,包含旧任务连续重试和已有 ID 复用。 -- `pass`:四个改动 Python 文件的 Ruff 0.11.12 check/format;Pyright 零错误/警告。 -- `pass`:`uv run --extra dev pre-commit run --all-files`,包含两项密钥检查;`git diff --check`;双语配对及本地链接检查。 -- `not_run`:全仓 Python 回归;针对本次有限的创建流程变更,选择受影响的 managed/CLI 集成覆盖。 -- `not_run`:真实云创建/改名,未授权。创建新的名称请求前,应重启正在运行的 Studio 服务加载改动。 -- `not_applicable`:前端/浏览器/构建、sidecar 和运行时进程 smoke;本次修复没有 UI、生成产物、sidecar 或运行时进程变更。 - -未执行提交、推送、部署或已有资源改名。 diff --git a/prd-spec/bugfixes/mpa-worker-retry/2026-09-20-worker-metadata-visibility.md b/prd-spec/bugfixes/mpa-worker-retry/2026-09-20-worker-metadata-visibility.md deleted file mode 100644 index 9b08e9700..000000000 --- a/prd-spec/bugfixes/mpa-worker-retry/2026-09-20-worker-metadata-visibility.md +++ /dev/null @@ -1,53 +0,0 @@ -# Worker initialization metadata visibility - -[中文版](2026-09-20-worker-metadata-visibility.zh.md) - -- ID: mpa-worker-metadata-visibility; date: 2026-09-20; status: implemented. -- Predecessor: [Worker API recovery](2026-09-20-worker-retry.md). -- Contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md). - -## Background and evidence - -A monitored creation failed with safe category `ownership` in the same second its Worker was created. Eleven seconds later the Worker was Ready; read-only checks confirmed matching ID, project, ownership tags and agent environment. Manual retry reused that Worker and passed. The exact initial missing/conflicting field was not captured, so delayed metadata is a strongly supported hypothesis, not a complete historical response reconstruction. Code currently treats absent tags/ID or an empty project as a permanent conflict before considering initialization status. SDK GetTool fields are optional. - -## Scope and requirements - -- FR-1: Separate missing ownership metadata from present conflicting values. During recognized initialization states (Creating, Pending, Starting, Initializing, Provisioning, or omitted/empty status), a managed Worker with persisted worker_id, worker_token and worker_hash may wait for missing ToolId, ProjectName, managed_by or mpa_agent_key. At most 4 incomplete observations, waiting 5/10/20 seconds; budget is not reset by intervening complete observations. Existing 600-second stage/task deadlines and cancellation remain authoritative. -- FR-2: Present conflicting ID/project/tag/MPA_AGENT_ID fails immediately, even if other fields are missing. Failed/Error/Deleted/Deleting and unknown nonempty statuses do not receive metadata grace. Ready with missing required metadata fails. Unmanaged/existing workers or incomplete creation intent receive no grace. Retain existing compatibility for omitted default project on unmanaged workers and absent optional agent environment; never accept a managed Worker without ID/project/tags verified. -- FR-3: Safe diagnostics distinguish fixed fields `worker_id`, `worker_project`, `worker_managed_by`, `worker_agent_key`, `worker_agent_binding`, `worker_state`; categories `metadata_pending`, `metadata_missing`, `ownership`, `resource_failed`, with existing bounded attempt/outcome fields. Do not log actual values. Report all missing fields on each bounded observation, but check all conflicts first. Successful readiness is required after metadata becomes complete. -- FR-4: Query retry/idempotency and resource bindings remain unchanged. No new Worker is created during metadata waiting. No UI/API/schema change or live cloud test creation. - -## Design and affected files - -Add one local validation helper in `worker.py` that reports fixed field operations, returns missing fields, and preserves fail-fast conflicts. The readiness loop owns the bounded missing-metadata counter and sleeps; terminal validation precedes waiting. Extend diagnostics allowlists and fixed internal exception category mappings. Keep raw SDK values private. Update paired component/operator documents. Tests in `test_worker_metadata.py` cover metadata transitions, every conflicting field, ready/terminal/unknown states, persisted-intent boundaries, exhaustion, cancellation/deadline, stable create count and redaction. Existing worker/task/CLI tests remain gates. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | -| --- | --- | --- | -| FR-1/2 | T-1: regression tests and worker state handling | AC-1: partial initial response reaches Ready without manual retry; conflicts and unsupported cases fail immediately; exhaustion/cancel/deadline bounded | -| FR-3 | T-2: safe field diagnostics and tests | AC-2: pending/missing/conflict field identifiable without values, no secrets in logs/storage | -| FR-4 | T-3: full affected suite and paired docs | AC-3: original idempotency/owner/task contracts pass, no cloud mutations or running-task interruption | - -## Review, risks and approval - -User approval: “帮我修复这个问题把” approves the preceding proposal to wait only for incomplete initialization metadata while rejecting explicit conflicts. Direct review (review-spec unavailable) checks safety, bounded waits, cancellation, schema compatibility, tests and bilingual equivalence; no blocker. Slow metadata beyond the 35-second accumulated grace still fails safely and can be retried. A conflicting initial value will now be identified but not silently accepted. No global deployment replay or UI changes. Restart local Studio only after all active creations finish. - -## Verification - -2026-09-20 working diff: not_run (implementation pending). Frontend/browser/build: not_applicable (no UI/HTTP change). Real cloud creation: not_run (no authorization to create test resources); existing user's task is monitored read-only. Commit/pre-commit: not_applicable (no commit requested). - - -## Delivery review and verification (2026-09-21) - -Scope: current worker/diagnostics implementation and worker metadata/recovery tests, plus paired docs. T-1/T-2/T-3 and AC-1/AC-2/AC-3 are complete. Direct implementation review confirms that explicit conflicts are checked before any grace, no missing required managed identity can succeed, the counter never resets, cancellation and stage deadlines bound all waits, and no new create call occurs during metadata waiting. Fixed diagnostics pass the existing parent allowlist validation and retain no provider field values. No schema or HTTP/UI change. - -- pass: test-first regression produced 21 failures and 4 passes before implementation, including incomplete initial metadata and field-level diagnostic coverage. -- pass: `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q` — 272 passed, one upstream Starlette deprecation warning. Includes 28 metadata cases plus existing recovery, ownership, durable task and CLI coverage; no external cloud writes. -- pass: `uv run --extra dev --with ruff==0.11.12 ruff check` and `uv run --extra dev --with pyright pyright` on worker.py, diagnostics.py, test_worker_metadata.py and test_worker_recovery.py; Ruff format applied. Pyright initially found an overly narrow inferred test dictionary type; corrected to `dict[str, object]`, rerun returned zero errors. -- pass: repository-rule Gitleaks scan of 10 affected code/test/doc files, relative PRD links, paired-language/identifier review and `git diff --check`. -- pass: read-only monitoring confirmed the user's existing retry reached succeeded; no active creation remained before planned local service reload. -- not_run: live reproduction of fresh cloud creation (not authorized), full-repository regression (complete affected component/CLI suite passed), pre-commit (no commit requested). UI/build/browser: not_applicable, no UI/HTTP contract change. -- Remaining limitation: the original first-response field was not retained. The new missing-metadata regression is fixed and field diagnostics now distinguish a future explicit conflict. This is not a claim of live first-create validation. - -- pass: local Studio reload completed after confirming zero active creations; configuration endpoint returned HTTP 200 with configured=true. No cloud creation was triggered. diff --git a/prd-spec/bugfixes/mpa-worker-retry/2026-09-20-worker-metadata-visibility.zh.md b/prd-spec/bugfixes/mpa-worker-retry/2026-09-20-worker-metadata-visibility.zh.md deleted file mode 100644 index 4a4c0d0af..000000000 --- a/prd-spec/bugfixes/mpa-worker-retry/2026-09-20-worker-metadata-visibility.zh.md +++ /dev/null @@ -1,53 +0,0 @@ -# Worker 初始化元数据可见性 - -[English](2026-09-20-worker-metadata-visibility.md) - -- ID:mpa-worker-metadata-visibility;日期:2026-09-20;状态:implemented。 -- 前序:[Worker API 恢复](2026-09-20-worker-retry.zh.md)。 -- 契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md)。 - -## 背景与证据 - -跟踪到一次创建在 Worker 刚创建的同一秒以安全类别 `ownership` 失败。11 秒后 Worker 为 Ready,只读检查确认 ID、项目、归属标签和智能体环境均匹配;手动重试复用了同一 Worker 并通过。首次具体缺失/冲突字段未记录,因此元数据延迟是有较强证据的推断,不是完整历史响应还原。现有代码在考虑初始化状态前将缺失标签/ID 或空项目视为永久冲突。SDK GetTool 字段均可选。 - -## 范围与需求 - -- FR-1:区分归属元数据缺失和已有值冲突。对于持久化 worker_id、worker_token、worker_hash 的托管 Worker,在已识别的初始化状态(Creating、Pending、Starting、Initializing、Provisioning,或未返回/空状态)下,可等待缺失的 ToolId、ProjectName、managed_by、mpa_agent_key。最多 4 次不完整观测,等待 5/10/20 秒;中途完整响应不重置预算。现有 600 秒阶段/任务期限和取消仍优先。 -- FR-2:已有且冲突的 ID/项目/标签/MPA_AGENT_ID 立即失败,即使其他字段缺失。Failed/Error/Deleted/Deleting 和非空未知状态不享受元数据宽限。Ready 缺少必需字段立即失败。非托管/既有 Worker 或创建意图不完整时不享受宽限。保留非托管 Worker 省略默认项目以及可选智能体环境缺失的兼容性;托管 Worker 的 ID/项目/标签未验证绝不接受。 -- FR-3:安全诊断区分固定字段 `worker_id`、`worker_project`、`worker_managed_by`、`worker_agent_key`、`worker_agent_binding`、`worker_state`;类别为 `metadata_pending`、`metadata_missing`、`ownership`、`resource_failed`,复用有界尝试次数/结果字段。不记录真实值。每次有界观测报告所有缺失字段,但先检查所有冲突。元数据完整后仍必须等待 Ready。 -- FR-4:查询重试/幂等和资源绑定保持不变。等待元数据期间不创建新 Worker。不修改 UI/API/数据库结构,不为测试创建真实云资源。 - -## 设计与影响文件 - -在 `worker.py` 增加局部校验函数,以固定字段操作记录、返回缺失字段并保持冲突快速失败。就绪循环管理有界缺失计数和等待;终态校验优先于等待。扩展诊断白名单及固定内部异常类别映射。原始 SDK 值保持私有。更新双语组件/运维文档。`test_worker_metadata.py` 覆盖元数据变化、每个冲突字段、Ready/终态/未知状态、持久化意图边界、耗尽、取消/期限、稳定创建次数和脱敏。保留 Worker/任务/CLI 测试门禁。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | -| --- | --- | --- | -| FR-1/2 | T-1:回归测试与 Worker 状态处理 | AC-1:初始化不完整响应无需手动重试即可到 Ready;冲突/不支持场景立即失败;耗尽/取消/期限有界 | -| FR-3 | T-2:安全字段诊断及测试 | AC-2:可识别待同步/缺失/冲突字段,不泄露值,日志/存储无密钥 | -| FR-4 | T-3:完整受影响测试及双语文档 | AC-3:原幂等/归属/任务契约通过,无云写入、不打断运行任务 | - -## 审查、风险与批准 - -用户批准:“帮我修复这个问题把”批准上文提出的仅等待初始化元数据缺失、拒绝显式冲突的方案。直接审查(review-spec 不可用)覆盖安全、有界等待、取消、结构兼容、测试及双语等价,无阻塞项。元数据超过累计 35 秒宽限仍安全失败,可重试。若初始值冲突,将明确识别,不静默接受。不重跑整个部署,不改 UI。仅在所有活动创建完成后重启本地 Studio。 - -## 验证 - -2026-09-20 工作区差异:not_run(待实现)。前端/浏览器/构建:not_applicable(无 UI/HTTP 变化)。真实云创建:not_run(未授权创建测试资源);只读跟踪用户已有任务。提交/pre-commit:not_applicable(未请求提交)。 - - -## 交付审查与验证(2026-09-21) - -范围:当前 worker/diagnostics 实现、Worker 元数据/恢复测试以及双语文档。T-1/T-2/T-3 和 AC-1/AC-2/AC-3 完成。直接实现审查确认:任何宽限之前先检查显式冲突,托管身份必需字段缺失绝不成功,计数不重置,取消及阶段期限限制所有等待,元数据等待期间不新增创建调用。固定诊断通过已有父进程白名单校验,不保留服务端字段值。无数据库结构或 HTTP/UI 改动。 - -- pass:测试先行,实现前出现 21 failed、4 passed,覆盖初始化元数据不完整和字段诊断。 -- pass:`uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q` — 272 passed,一条上游 Starlette 弃用警告。包含 28 项元数据用例及原恢复、归属、持久化任务和 CLI 覆盖,无外部云写入。 -- pass:对 worker.py、diagnostics.py、test_worker_metadata.py、test_worker_recovery.py 执行 `uv run --extra dev --with ruff==0.11.12 ruff check` 和 `uv run --extra dev --with pyright pyright`,已执行 Ruff format。Pyright 初次发现测试字典推断过窄,已标注 `dict[str, object]`,复查零错误。 -- pass:按仓库规则对 10 个受影响代码/测试/文档文件执行 Gitleaks,检查 PRD 相对链接、双语/标识符一致性,`git diff --check` 通过。 -- pass:只读跟踪确认用户已有重试任务到达 succeeded,计划重载本地服务前没有活动创建。 -- not_run:真实首次云创建复现(未授权)、全仓回归(完整受影响组件/CLI 测试已通过)、pre-commit(未请求提交)。UI/构建/浏览器:not_applicable,无 UI/HTTP 契约改动。 -- 剩余限制:原始首次响应字段未保留。已修复新增的元数据缺失回归,字段诊断可区分未来的显式冲突;不声称完成真实首次创建验证。 - -- pass:确认零活动创建后,本地 Studio 已重载;配置接口返回 HTTP 200 且 configured=true。未触发云创建。 diff --git a/prd-spec/bugfixes/mpa-worker-retry/2026-09-20-worker-retry.md b/prd-spec/bugfixes/mpa-worker-retry/2026-09-20-worker-retry.md deleted file mode 100644 index e55142bad..000000000 --- a/prd-spec/bugfixes/mpa-worker-retry/2026-09-20-worker-retry.md +++ /dev/null @@ -1,56 +0,0 @@ -# MPA worker recovery and safe diagnostics - -[中文版](2026-09-20-worker-retry.zh.md) - -- ID: mpa-worker-retry; created/revised: 2026-09-20; status: implemented. -- Contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md). - -## Evidence, goals and boundaries - -`runner.py` discards exceptions; `tasks.py` discards stderr and resets the latest failure on manual retry. `worker.py` propagates transient API failures immediately. The user observed a first failure at worker preparation followed by progress on manual retry. Its historical root cause is unknown because the exception was discarded. This fix addresses those verified recovery/observability gaps, not a claimed reconstruction of that failure. - -Automatically recover bounded transient worker failures and retain safe per-task diagnostics across manual retries. Do not replay the entire deployment, change images/configuration or ownership rules, introduce cloud resources for testing, expose raw errors, or redesign the dialog. Other cloud stages gain final diagnostics but no automatic retries. - -## Requirements and scenarios - -- FR-1: Worker reference lookup, discovery, idempotent create and readiness reads attempt at most 4 times, sleeping 1, 2, 4 seconds between recoverable failures. Retry timeout/connection, known throttling/service codes and HTTP 408/429/500/502/503/504. Unknown, authentication, validation, ownership and terminal resource failures stop immediately. Never classify by substring matching arbitrary error text. -- FR-2: Retry recognized not-found only while reading a worker with a persisted managed creation intent and ID. Missing reference/existing workers fail. The stage has a 600-second default deadline including discovery/retries; the overall task deadline and cancellation still win. Retries use the same persisted ClientToken and payload; failed discovery never means absence. -- FR-3: Store allowlisted diagnostic events (stage, operation, category, attempt, retrying/failed, timestamp, task ID) in the private task SQLite database; retain the newest 100 per task across manual retries. Do not persist exception messages, tracebacks, URLs, headers, payloads, credentials, arbitrary provider codes or environment values. Log the same safe fields server-side. API response shape/error codes remain compatible. -- FR-4: Runner reports classified final failures from all stages. Parent records abnormal child exits, timeouts and cancellation even when no child diagnostic arrives. Validate child diagnostic fields before logging/storage. Diagnostics must not turn a failure into success or hide cleanup. - -## Design and impact - -Add a small diagnostic classifier and Worker retry helper. Interpret the installed AgentKit typed errors/error_code and chained requests transport errors, including HTTP status; map only fixed known codes to fixed categories. Transient create retries retain the original idempotency token. Owned readiness 404 tolerance is bounded and does not relax ownership validation. Append a task diagnostic table without modifying old task rows or HTTP contracts; bounded retention is transactional. Runner uses a scoped diagnostic callback; supervisor revalidates events. CLI can log safe events without Studio persistence. UI and generated assets require no changes. - -## Tasks, tests and acceptance - -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| FR-1/2 | T-1: tests, diagnostics module and worker integration | AC-1: transient recovery, exhausted retries, stable token/payload, reference 404/permission fail fast, ownership checks, cancellation/deadline | Targeted worker and diagnostic tests | -| FR-3/4 | T-2: runner/task integration and migration tests | AC-2: safe history survives retry/reopen, cap 100, reject malformed/injected events, preserve terminal states | Real child-process task tests and runner tests | -| All | T-3: paired contract/operator docs and review | AC-3: docs match implementation, checks recorded | Python regression, Ruff, Pyright, whitespace/secret checks | - -Affected paths: `veadk/integrations/mpa/managed/{diagnostics,worker,runner,tasks}.py`, corresponding tests, paired component/operator docs. No frontend code or new dependencies. - -## Risks and review - -Unknown provider codes intentionally fail closed; a transient error outside the allowlist may still require manual retry. Cloud requests may complete after cancellation; persisted intent remains the recovery authority. SDK-internal retries may add underlying network attempts beyond the four orchestration calls. Diagnostic categories trade raw detail for credential safety. Readiness polling keeps its existing cadence. - -Direct design review (review-spec unavailable): consistency, bounded retry, mutation idempotency, cancellation, input validation, confidentiality, migration and bilingual equivalence checked; no blockers. User approval: “帮我补” explicitly approves the preceding proposal for safe error records and transient automatic retries. No commit/push/deployment authorized by this design. Live historical failure reproduction is not possible without retained evidence; isolated simulation is required. - -## Verification record - -2026-09-20 working diff: not_run (implementation pending). Frontend/browser: not_applicable (no UI/HTTP contract change). Live cloud writes: not_run (no new cloud creation requested). Commit/pre-commit: not_applicable (no commit requested). - - -### Implemented verification (2026-09-20, current working diff) - -- pass: initial regression run failed in 19 new cases before implementation (missing recovery/diagnostics); no live cloud calls. -- pass: `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q` — 244 passed; one upstream Starlette deprecation warning. -- pass: final affected tests after timeout annotation correction: `uv run --extra dev pytest tests/integrations/mpa_managed/test_worker_recovery.py tests/integrations/mpa_managed/test_worker.py tests/integrations/mpa_managed/test_tasks.py -q` — 39 passed. -- pass: `uv run --extra dev --with ruff==0.11.12 ruff check` and `uv run --extra dev --with pyright pyright` for diagnostics/worker/runner/tasks plus test_worker_recovery/test_worker; no errors. Ruff format applied. Initial bare `uv run ruff` was unavailable; temporary tool dependencies resolved it without project/global changes. Pyright found an inferred int-only timeout in a fractional-deadline test; corrected the timeout annotation to float and reran checks. -- pass: repository-rule Gitleaks scan of 25 implementation/test/doc files, paired PRD links/requirement IDs, manual bilingual semantic review, `git diff --check`. -- pass: review confirms fixed safe enums, bounded operation retries, same persisted create token/payload, ownership fail-fast, unchanged API, task history cap and real subprocess cleanup coverage. T-1/T-2/T-3 and AC-1/AC-2/AC-3 complete. -- not_run: repository-wide Python regression (change isolated to managed MPA; full affected component and CLI suite passed). UI build/browser and generated assets: not_applicable, no UI/HTTP shape change. Live cloud creation: not_run; simulated recovery does not establish the discarded historical cause. Commit/pre-commit: not_applicable, no commit requested. - -- pass: local Studio reloaded only after active creation count reached zero; configuration GET returned HTTP 200/configured=true. First task access initialized `task_diagnostics` while retaining existing tasks. The local unauthenticated task lookup returned 404; no authenticated UI/cloud creation smoke is claimed. The user's preceding task was already succeeded before restart. diff --git a/prd-spec/bugfixes/mpa-worker-retry/2026-09-20-worker-retry.zh.md b/prd-spec/bugfixes/mpa-worker-retry/2026-09-20-worker-retry.zh.md deleted file mode 100644 index 887f44b7b..000000000 --- a/prd-spec/bugfixes/mpa-worker-retry/2026-09-20-worker-retry.zh.md +++ /dev/null @@ -1,56 +0,0 @@ -# MPA Worker 自动恢复与安全诊断 - -[English](2026-09-20-worker-retry.md) - -- ID:mpa-worker-retry;创建/修订:2026-09-20;状态:implemented。 -- 契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md)。 - -## 证据、目标与边界 - -`runner.py` 丢弃异常;`tasks.py` 丢弃 stderr,并在手动重试时重置最近的失败;`worker.py` 立即传播暂时性 API 失败。用户遇到首次在准备 Worker 时失败、手动重试后继续的情况。由于异常已被丢弃,历史故障的根因未知。本次修复已确认的恢复和诊断缺口,不声称还原历史原因。 - -有限自动恢复 Worker 暂时性故障,跨手动重试保留安全的任务诊断。不重跑整个部署、不修改镜像/配置或归属规则、不创建云资源用于测试、不暴露原始错误、不重做弹窗。其他云阶段补最终诊断但不加自动重试。 - -## 需求与场景 - -- FR-1:Worker 参考查询、发现、幂等创建和就绪查询最多尝试 4 次,在可恢复失败之间分别等待 1、2、4 秒。重试超时/连接故障、已知限流/服务错误码和 HTTP 408/429/500/502/503/504。未知、鉴权、校验、归属和资源终态错误立即停止。不通过任意错误文本的子串匹配分类。 -- FR-2:仅在读取具有已持久化托管创建意图和 ID 的 Worker 时重试已识别的不存在错误。参考/既有 Worker 缺失立即失败。阶段默认 600 秒,包括发现和重试;总任务期限和取消仍优先。重试保持同一持久化 ClientToken 和载荷,发现失败绝不能当成不存在。 -- FR-3:在私有任务 SQLite 中保存白名单诊断事件(阶段、操作、类别、尝试次数、retrying/failed、时间戳、任务 ID);跨手动重试保留每任务最新 100 条。不保存异常消息、堆栈、URL、请求头、载荷、凭据、任意服务端错误码或环境值。服务端日志记录相同安全字段。API 响应结构/错误码保持兼容。 -- FR-4:Runner 为所有阶段报告已分类的最终失败。即使子进程没有诊断,父进程也记录异常退出、超时和取消。在日志/存储之前校验子进程诊断字段。诊断不得把失败变成成功或掩盖清理。 - -## 设计与影响 - -新增小型诊断分类器和 Worker 重试函数。识别已安装 AgentKit 的类型化异常/error_code 与链式 requests 传输异常(含 HTTP 状态);仅将固定已知码映射为固定类别。创建重试保持原幂等令牌。托管就绪查询的 404 容忍有界,不放宽归属校验。增加任务诊断表,不修改旧任务记录或 HTTP 契约;事务内限制保留数量。Runner 使用有作用域的诊断回调,父进程再次验证事件。CLI 可记录安全事件,但不写 Studio 任务库。UI 和构建产物无需修改。 - -## 任务、测试与验收 - -| 需求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| FR-1/2 | T-1:测试、诊断模块及 Worker 集成 | AC-1:暂时性恢复、耗尽重试、稳定令牌/载荷、参考 404/权限快速失败、归属校验、取消/期限 | Worker 与诊断目标测试 | -| FR-3/4 | T-2:runner/任务集成与迁移测试 | AC-2:历史跨重试/重开保留、上限 100、拒绝畸形/注入事件、保持终态 | 真实子进程任务测试及 runner 测试 | -| 全部 | T-3:双语契约/运维文档及审查 | AC-3:文档与实现一致、记录检查 | Python 回归、Ruff、Pyright、空白/密钥检查 | - -影响路径:`veadk/integrations/mpa/managed/{diagnostics,worker,runner,tasks}.py`、对应测试、双语组件/运维文档。不改前端代码,不加依赖。 - -## 风险与审查 - -未知服务端错误码故意快速失败,白名单外的暂时性故障仍可能需要手动重试。云请求可在取消后完成,持久化意图仍是恢复依据。SDK 内部重试可能使实际网络请求次数超过四次编排调用。诊断使用类别,在细节与凭据安全之间取舍。就绪轮询保持原频率。 - -直接设计审查(review-spec 不可用):已检查一致性、有界重试、写操作幂等、取消、输入校验、保密、迁移和双语等价,无阻塞项。用户批准:“帮我补”明确批准上文提出的安全错误记录与暂时性自动重试方案。本设计不授权提交/推送/部署。历史错误缺少证据,无法真实复现;必须使用隔离模拟。 - -## 验证记录 - -2026-09-20 工作区差异:not_run(待实现)。前端/浏览器:not_applicable(无 UI/HTTP 契约变化)。真实云写入:not_run(未请求创建新云资源)。提交/pre-commit:not_applicable(未请求提交)。 - - -### 实现验证(2026-09-20,当前工作区差异) - -- pass:实现前新增 19 个回归用例失败(缺少恢复/诊断),未调用真实云服务。 -- pass:`uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q` — 244 passed;一条上游 Starlette 弃用警告。 -- pass:修正超时类型标注后最终受影响测试:`uv run --extra dev pytest tests/integrations/mpa_managed/test_worker_recovery.py tests/integrations/mpa_managed/test_worker.py tests/integrations/mpa_managed/test_tasks.py -q` — 39 passed。 -- pass:对 diagnostics/worker/runner/tasks 和 test_worker_recovery/test_worker 执行 `uv run --extra dev --with ruff==0.11.12 ruff check` 与 `uv run --extra dev --with pyright pyright`,无错误;已执行 Ruff format。初次裸命令 `uv run ruff` 不可用,使用临时工具依赖解决,未修改项目/全局配置。Pyright 在小数期限测试中发现 timeout 被推断为仅 int;已标注为 float 并复查。 -- pass:按仓库规则对 25 个实现/测试/文档文件执行 Gitleaks,检查双语 PRD 链接/需求 ID,人工复核双语语义,`git diff --check` 通过。 -- pass:审查确认固定安全枚举、有界操作重试、保持持久化创建令牌/载荷、归属快速失败、API 不变、历史数量上限以及真实子进程清理测试覆盖。T-1/T-2/T-3 与 AC-1/AC-2/AC-3 完成。 -- not_run:全仓 Python 回归(改动限定于托管 MPA,完整受影响组件和 CLI 测试已通过)。UI 构建/浏览器和产物:not_applicable,无 UI/HTTP 结构变化。真实云创建:not_run;模拟恢复不能证明已丢弃的历史原因。提交/pre-commit:not_applicable,未请求提交。 - -- pass:仅在活动创建数为零后重载本地 Studio;配置 GET 返回 HTTP 200/configured=true。首次任务访问初始化 `task_diagnostics`,保留已有任务。本地未登录身份查询任务返回 404,不声称完成已登录 UI/真实云创建冒烟。用户此前任务在重启前已经 succeeded。 diff --git a/prd-spec/bugfixes/mpa-worker-retry/2026-09-30-worker-metadata-deadline.md b/prd-spec/bugfixes/mpa-worker-retry/2026-09-30-worker-metadata-deadline.md deleted file mode 100644 index 7af719e99..000000000 --- a/prd-spec/bugfixes/mpa-worker-retry/2026-09-30-worker-metadata-deadline.md +++ /dev/null @@ -1,54 +0,0 @@ -# Worker metadata stage deadline - -[中文版](2026-09-30-worker-metadata-deadline.zh.md) - -- ID: mpa-worker-metadata-deadline; created/revised: 2026-09-30; status: approved. -- Predecessor: [metadata visibility](2026-09-20-worker-metadata-visibility.md). -- Contract: [Studio creation CON-10](../../../specs/studio-mpa-creation/README.md). - -## Evidence, goals and scope - -Cloud diagnostics showed four incomplete ownership-tag observations during Worker initialization, followed by failure after 35 seconds. Read-only metadata inspection later showed the same Tool Ready with matching ownership, project and agent binding, approximately 151 seconds after creation. The source imposes a four-observation limit independently of the 600-second Worker deadline. The shared `ensure_worker` path serves managed Studio and CLI provisioning. - -Wait for delayed ownership metadata within the existing deadline instead of failing early. Non-goals: TOS validation changes, image changes, HTTP/UI/schema changes, timeout extension, deployment, resource creation or live retries. Template readiness does not prove Sandbox/session/TOS execution. - -## Requirements and design - -- FR-1: Given complete persisted managed creation intent and recognized initialization status, missing ownership metadata is polled every 5 seconds until it becomes visible or the existing deadline/cancellation ends the operation. Remove the independent four-observation budget; do not use unbounded exponential sleep. -- FR-2: Ready with missing required metadata, conflicting present values, terminal/unknown states and incomplete creation intent retain fail-fast behavior. Intermediate complete initialization responses do not reset or extend the deadline. Preserve Worker binding and never create a second Tool during polling. -- FR-3: Preserve the diagnostic protocol's attempt range 1–4 by saturating the metadata observation counter at 4. Later events remain `metadata_pending`/`retrying`; bounded task history and redaction remain unchanged. Worker API error retries still have four attempts and 1/2/4-second waits. - -Reuse `asyncio.wait_for` in `ensure_worker`; change only the missing-metadata branch of `worker.py`. No new configuration, dependency, persistence or public interface. Update CON-10 and the paired operator guide; legacy create paths and TOS credential boundaries are unchanged. - -## Tasks and acceptance - -| Requirement | Task | Acceptance / verification | -| --- | --- | --- | -| FR-1/3 | T-1: failing metadata regression, minimal loop fix | AC-1: more than four incomplete observations reach Ready, including interleaved complete initialization; fixed sleeps and valid saturated diagnostics | -| FR-2 | T-2: timeout/cancellation and existing security regressions | AC-2: continued polling ends at deadline/cancellation, keeps binding, creates nothing extra; strict negative cases pass | -| FR-1–3 | T-3: coverage, gates and paired documentation | AC-3: changed executable-line coverage >95%; affected/full Python tests, changed-file Ruff/Pyright, pre-commit and doc checks pass | - -## Review and risks - -User approval: “那你改下代码” approves the preceding proposal to wait within the stage timeout and retain strict Ready ownership checks. Direct design review (`review-spec` unavailable) covers callers, deadline/cancellation, conflicts, credential isolation, diagnostic bounds and bilingual equivalence; no blockers. Fixed polling increases reads versus exponential backoff but matches the existing readiness polling cadence and remains deadline-bounded. Missing TOS mount metadata remains independently strict and outside this confirmed ownership-tag defect. - -## Verification record - -2026-09-30 working diff: - -- pass: test-first `uv run --extra dev pytest tests/integrations/mpa_managed/test_worker_metadata.py -q` reproduced the defect before implementation (13 failed, 17 passed). -- pass: `uv run --extra dev python -c 'import pydantic_settings; import pytest; raise SystemExit(pytest.main(["tests/integrations/mpa_managed", "tests/cli/test_cli_mpa.py", "-q", "--tb=short", "--cov=veadk.integrations.mpa.managed.worker", "--cov-branch", "--cov-report=json:/tmp/veadk-worker-metadata-coverage-20260930.json", "--cov-report=term-missing"]))'` — 443 passed. Direct targeted collection initially encountered an existing lazy-import `pydantic.RootModel` ordering failure; initializing `pydantic_settings` before collection avoids it without a source/dependency change. Module line/branch coverage is 89%; changed executable-line coverage is 100% (3/3), satisfying >95%. Simulated metadata tests cover 4/30/200 incomplete observations, interleaved initialization, valid saturated diagnostics, cancellation/deadline, conflicts and unchanged creation counts. -- pass: changed-file Ruff 0.11.12 and Pyright, pre-commit all-files (including Gitleaks/YAML secret scan), new relative links and `git diff --check`. The first pre-commit run formatted the two changed Python files; its second run passed all hooks. -- fail: `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" -q --tb=short` — 6151 passed, 8 failed, 2 collection errors, 22 skipped, 2 xfailed, 6 subtests passed. Six Harness failures require absent optional `llama_index`; two self-host collection errors require absent `anthropic`; one GitHub delivery test makes an unmocked external request and timed out; one wheel metadata assertion expects `>=0.8.0` while unchanged `pyproject.toml` requires `>=0.8.5`. Those files/dependency declarations are unchanged by this fix; no Worker metadata test failed. AC-3's full-repository gate remains blocked, so the design remains approved rather than claiming all acceptance complete. No unrelated dependency/test changes are included. -- not_run: cloud/browser/deployment are outside this code-only change. Frontend test/build and Codex/provider smoke: not_applicable, no UI/generated assets or process/provider execution changes. - -Direct implementation review confirms unchanged caller interfaces, strict ownership/TOS checks, bounded fixed polling under the existing wrapper deadline, cancellation propagation and retained bindings. Both document languages preserve FR/AC/T IDs and numeric limits. The branch was fetched and rebased against the existing PR's `superops/main` base before verification; already up to date. No running Studio process or cloud resource was modified. - -### Subsequently authorized release - -On 2026-09-30, the user separately authorized updating the existing Studio with “那你先部署云端吧”, without retrying MPA creation. Reused `VeFaaS.update_application_code_bundle` to replace only the offline code bundle. - -- pass: source `c9b2c285`; wheel `0.0.1.dev1248+gc9b2c285f.d20260930`, SHA256 `d68b5308995a81698bc2f4c8871ef62ea831f6bf82c1a282cbf9ebb1cf7738d7`. All 186 offline file hashes passed; 113 frontend assets and dependency declarations match the previous release. -- pass: existing application `deploy_success`; all 10 instances are revision 36 and Ready, replacing revision 35; sampled two instances completed service startup. All 44 environment values match revision 35 (response ordering changed); the entrypoint remains `bash ./run.sh`, with unchanged network, role, runtime, memory and request timeout. -- pass: refreshed the original cloud address in a separate browser tab; login state and homepage work. Preserved the user's original failed creation form without submitting a retry. -- not_run: live MPA creation and session/TOS read/write. Recovery of the existing Error Runtime's empty network is outside this fix; successful publication does not prove same-agent-ID retry success. AC-3's existing full-repository gate failures remain unresolved. diff --git a/prd-spec/bugfixes/mpa-worker-retry/2026-09-30-worker-metadata-deadline.zh.md b/prd-spec/bugfixes/mpa-worker-retry/2026-09-30-worker-metadata-deadline.zh.md deleted file mode 100644 index ca81ccdba..000000000 --- a/prd-spec/bugfixes/mpa-worker-retry/2026-09-30-worker-metadata-deadline.zh.md +++ /dev/null @@ -1,54 +0,0 @@ -# Worker 元数据阶段期限 - -[English](2026-09-30-worker-metadata-deadline.md) - -- ID:mpa-worker-metadata-deadline;创建/修订:2026-09-30;状态:approved。 -- 前序:[元数据可见性](2026-09-20-worker-metadata-visibility.zh.md)。 -- 契约:[Studio 创建 CON-10](../../../specs/studio-mpa-creation/README.zh.md)。 - -## 证据、目标与范围 - -云端诊断显示 Worker 初始化期间四次归属标签不完整,35 秒后创建失败。随后只读检查显示同一个 Tool 在创建约 151 秒后 Ready,归属、项目和智能体绑定均匹配。源码在 600 秒 Worker 期限之外设置了独立的四次观察限制。共享 `ensure_worker` 同时服务托管 Studio 和 CLI 创建。 - -让归属元数据延迟在现有期限内等待,不提前失败。非目标:修改 TOS 校验、镜像、HTTP/UI/数据库结构、延长期限、部署、创建资源或真实重试。模板就绪不证明 Sandbox/会话/TOS 执行成功。 - -## 需求与设计 - -- FR-1:已有完整托管创建意图且处于已识别初始化状态时,归属元数据缺失每 5 秒检查,直到可见或现有期限/取消结束操作。删除独立四次观察预算,不使用无界指数等待。 -- FR-2:Ready 缺必需元数据、已有值冲突、终态/未知状态及创建意图不完整仍快速失败。中途完整初始化响应不重置或延长期限。保留 Worker 绑定,轮询不创建第二个 Tool。 -- FR-3:元数据观察计数最大保持 4,保留诊断协议 attempt 的 1–4 范围。后续事件仍为 `metadata_pending`/`retrying`,任务历史上限及脱敏不变。Worker API 错误重试仍最多四次,间隔 1/2/4 秒。 - -复用 `ensure_worker` 的 `asyncio.wait_for`,仅修改 `worker.py` 的元数据缺失分支。不新增配置、依赖、持久化或公共接口。更新 CON-10 及双语操作指南;旧创建路径和 TOS 凭证边界不变。 - -## 任务与验收 - -| 需求 | 任务 | 验收 / 验证 | -| --- | --- | --- | -| FR-1/3 | T-1:失败回归及最小循环修复 | AC-1:超过四次缺失后到达 Ready,包括穿插完整初始化响应;固定间隔及饱和诊断合法 | -| FR-2 | T-2:超时/取消及原安全回归 | AC-2:持续轮询受期限/取消限制,保留绑定,不额外创建;严格负例通过 | -| FR-1–3 | T-3:覆盖率、门禁和双语文档 | AC-3:增量可执行行覆盖率 >95%;受影响/全量 Python 测试、改动文件 Ruff/Pyright、pre-commit 及文档检查通过 | - -## 审查与风险 - -用户批准:“那你改下代码”批准此前在阶段超时内等待、保留 Ready 严格归属校验的方案。直接设计审查(`review-spec` 不可用)覆盖调用者、期限/取消、冲突、凭证隔离、诊断范围及双语等价,无阻塞。固定间隔相比指数退避会增加查询,但与原就绪轮询一致且受期限限制。TOS 挂载元数据缺失仍独立严格校验,不属于此次已确认的归属标签缺陷。 - -## 验证记录 - -2026-09-30 工作区改动: - -- pass:测试先行执行 `uv run --extra dev pytest tests/integrations/mpa_managed/test_worker_metadata.py -q`,修复前复现缺陷(13 failed、17 passed)。 -- pass:`uv run --extra dev python -c 'import pydantic_settings; import pytest; raise SystemExit(pytest.main(["tests/integrations/mpa_managed", "tests/cli/test_cli_mpa.py", "-q", "--tb=short", "--cov=veadk.integrations.mpa.managed.worker", "--cov-branch", "--cov-report=json:/tmp/veadk-worker-metadata-coverage-20260930.json", "--cov-report=term-missing"]))'` — 443 passed。直接目标测试收集最初遇到已有的 `pydantic.RootModel` 延迟导入顺序问题;收集前初始化 `pydantic_settings` 即可避免,未修改源码/依赖。模块行/分支覆盖率为 89%,增量可执行行覆盖率 100%(3/3),满足 >95%。模拟元数据测试覆盖 4/30/200 次缺失、穿插初始化响应、合法饱和诊断、取消/期限、冲突及创建次数不变。 -- pass:改动文件 Ruff 0.11.12、Pyright、全文件 pre-commit(含 Gitleaks/YAML 密钥扫描)、新相对链接与 `git diff --check`。第一次 pre-commit 格式化了两个改动 Python 文件,第二次全部通过。 -- fail:`uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" -q --tb=short` — 6151 passed、8 failed、2 个收集错误、22 skipped、2 xfailed、6 个子测试通过。六个 Harness 失败依赖未安装的可选 `llama_index`;两个自托管收集错误缺 `anthropic`;一个 GitHub 交付测试未模拟外部请求而超时;一个 wheel 元数据断言要求 `>=0.8.0`,未修改的 `pyproject.toml` 已要求 `>=0.8.5`。本修复未修改这些文件/依赖声明,Worker 元数据测试无失败。AC-3 全仓门禁仍受阻,因此设计保持 approved,不声称全部验收完成。不纳入无关依赖/测试修改。 -- not_run:云端/浏览器/部署不属于本次代码改动。前端测试/构建及 Codex/provider 冒烟:not_applicable,无 UI/生成产物或进程/服务商执行改动。 - -直接实现审查确认:调用接口、严格归属/TOS 校验不变,固定间隔轮询受已有包装期限限制,取消继续传播,绑定保留。双语 FR/AC/T 标识及数字限制一致。验证前已 fetch 并 rebase 至现有 PR 的 `superops/main` 基线,分支已是最新。未修改运行中的 Studio 或云资源。 - -### 后续授权发布 - -2026-09-30 用户“那你先部署云端吧”另行授权更新现有 Studio,不授权重试创建 MPA。复用 `VeFaaS.update_application_code_bundle`,仅替换离线代码包。 - -- pass:发布源码 `c9b2c285`;wheel `0.0.1.dev1248+gc9b2c285f.d20260930`,SHA256 `d68b5308995a81698bc2f4c8871ef62ea831f6bf82c1a282cbf9ebb1cf7738d7`。186 个离线文件哈希通过;113 个前端资源及依赖声明与上一版一致。 -- pass:原应用发布状态 `deploy_success`;10 个实例全部修订 36、Ready,替换修订 35;抽查两个实例服务启动完成。原 44 项环境变量值与修订 35 一致(返回顺序变化);入口仍为 `bash ./run.sh`,网络、角色、运行时、内存和请求期限一致。 -- pass:在独立浏览器标签刷新原云端地址,登录态及首页正常;保留用户原失败创建表单,不提交重试。 -- not_run:真实 MPA 创建及会话/TOS 读写。已有 Error Runtime 的空网络恢复问题未在本修复中处理;发布成功不证明同一智能体 ID 重试成功。AC-3 的既有全仓门禁失败仍未消除。 diff --git a/prd-spec/bugfixes/mpa-worker-template/2026-10-09-independent-worker.md b/prd-spec/bugfixes/mpa-worker-template/2026-10-09-independent-worker.md deleted file mode 100644 index ecac3ad2d..000000000 --- a/prd-spec/bugfixes/mpa-worker-template/2026-10-09-independent-worker.md +++ /dev/null @@ -1,62 +0,0 @@ -# Worker creation without an old-account template - -[中文版](2026-10-09-independent-worker.zh.md) - -- Change ID: `mpa-worker-template` -- Created/revised: 2026-10-09 -- Status: implemented -- Contracts: [Studio creation](../../../specs/studio-mpa-creation/README.md), [Runtime provisioning](../../../specs/mpa-runtime-provisioning/README.md). - -## Background and evidence - -Studio's built-in Worker profile references a Tool in a previous account. A fresh deployment account cannot read that Tool: creation fails at `get_reference_worker` before `CreateTool`. The Worker image is already explicit. `worker.py` only copies environment settings from the reference; command, resources, role and networking come from the provisioner. The pinned Worker source revision `81f3496`, `scripts/run.sh`, supplies startup defaults without a reference Tool. - -## Goals, non-goals and scenarios - -Allow a fresh account to create a Worker from its image and explicit startup settings. Keep optional references/existing Workers compatible. Do not change images, IAM/networking, Runtime chat, frontend, or automatically modify existing Tools. Registry image access remains an independent prerequisite. - -- Fresh Studio creation reaches `CreateTool` without reading an old reference. -- A CLI profile may explicitly inherit an accessible Tool's environment, with explicit settings taking precedence. -- Missing explicit references remain failures; retrying an uncertain creation preserves its payload/token. - -## Requirements and design - -- **FR-1:** Remove built-in `reference-id`. Pin nonsecret `PORT`, Worker port, headless/minimal mode, workspace/data/runtime/sidecar roots and Skill Space base directory to the pinned image's defaults. -- **FR-2:** Add optional `managed.worker.env: dict[str, str]`, default empty. Resolve whole `${ENV_NAME}` references on the server. Reject invalid uppercase environment keys, agent/Runtime/Tool/Skill Space bindings, inherited channel/Runtime credentials and control database URLs. Nonempty env with `existing-id` fails rather than being silently ignored. -- **FR-3:** Merge filtered reference environment, explicit env, then generated `MPA_AGENT_ID`. Do not mutate the profile or reference response. Optional explicit reference failure never falls back to an empty environment. -- **FR-4:** Keep sorted request hashing, ClientToken, ownership validation, cancellation and bounded retry unchanged. Changed explicit env on an unfinished request is a configuration conflict. Do not persist env values in deployment records or expose them in config summaries, repr or error responses. - -The existing image contract handles model credentials per session; Studio does not copy old model keys. Additional operator settings use Worker env with server secret references. No new dependency, cloud API, permission, task schema or frontend asset is required. The Studio component owns the new configuration contract; Runtime provisioning references it. Legacy non-managed creation is unchanged. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | -| --- | --- | --- | -| FR-1 | T-1: update built-in profile and regression | AC-1: image-only fresh-account request skips reference lookup and carries explicit startup settings | -| FR-2 | T-2: config schema/resolution and safe validation tests | AC-2: secret refs resolve; invalid/owned keys and existing-worker env fail without exposing values | -| FR-3 | T-3: request merge and compatibility tests | AC-3: explicit values win, agent binding remains generated, explicit missing reference still fails | -| FR-4 | T-4: retry/immutability tests and review | AC-4: unchanged payload retries use the same token; changed env fails; registry/summary/repr exclude env values | - -## Risks and recovery - -Defaults cover the pinned image; custom images may need explicit env. No cloud creation smoke is performed automatically. Existing Tool IDs remain authoritative; no rename/update. Tasks that failed before Worker intent creation can retry with the same agent ID after Studio restarts. Already-dispatched tasks with changed hashes require their original configuration; never reset intent to bypass duplicate protection. - -## Review and verification record - -Direct design review (review-spec unavailable) cleared configuration precedence, secret handling, account boundaries, partial failure, compatibility and bilingual equivalence. User approved the proposed removal of the fixed old template and explicit configuration with “帮我改” on 2026-10-09 before implementation. - -Verification scope: this Worker change on `ef0ad519` plus prior uncommitted account/IAM/network/APIG fixes. Required commands: targeted config/Worker tests, managed provisioning regression, changed-file Ruff/Pyright, scoped pre-commit, bilingual/link/whitespace checks. Live cloud creation: `not_run`; code and simulated requests cannot prove image pull permissions or platform readiness. Browser/frontend gates: `not_applicable`, no UI/API changes. Results will be recorded after execution. - -### Executed checks (2026-10-09) - -- `fail` before implementation: config/Worker regression reproduced the fixed old reference and missing env support (5 failed, 94 passed). -- `pass`: `uv run --extra dev pytest tests/integrations/mpa_managed/test_config.py tests/integrations/mpa_managed/test_worker.py tests/integrations/mpa_managed/test_worker_recovery.py tests/integrations/mpa_managed/test_worker_metadata.py -q --tb=short` — 152 passed. -- `pass`: `uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py tests/cli/test_frontend_deploy_iam.py -q --tb=short` — 594 passed, 5 existing deprecation warnings; includes final reference filtering and pending-env recovery. -- `pass`: `uvx --from ruff==0.11.12 ruff check` and `uvx pyright --pythonpath .venv/bin/python` on `config.py`, `worker.py`, `studio_profile.py`, `test_config.py`, `test_worker.py` in their managed source/test directories — zero errors. -- `pass`: `uv run --extra dev pre-commit run --files` on the five Python files and all eight changed documentation files — Ruff and hardcoded-secret detection passed. YAML hook `not_applicable` (no YAML changed). -- `pass`: paired language identifiers/relative links and `git diff --check`. Direct implementation review found no blockers: no reference lookup in the simulated new-account request, explicit override/immutability, filtered bindings/control URLs, safe secret refs and unchanged pending-token protection. AC-1 through AC-4 passed with simulated requests. -- `not_run`: live image pull/container startup and broad unrelated SDK regression. The isolated managed suite covers the affected contracts; no cloud resource was created for verification. `not_applicable`: frontend build/browser, generated assets and runtime process smoke (no chat/frontend/image code changes). Full all-files commit gates await a separately authorized commit. - -T-1 through T-4 are complete. No agreed implementation scope is deferred; live platform readiness remains an operational validation limit. Restart local Studio and retry the same agent that stopped before Worker intent creation. - -Startup-default follow-up: [2026-10-09-worker-image-defaults](2026-10-09-worker-image-defaults.md) supersedes the explicit built-in env choice in FR-1/AC-1. Initial verification above is historical evidence; all other contracts remain. diff --git a/prd-spec/bugfixes/mpa-worker-template/2026-10-09-independent-worker.zh.md b/prd-spec/bugfixes/mpa-worker-template/2026-10-09-independent-worker.zh.md deleted file mode 100644 index eeb7a1382..000000000 --- a/prd-spec/bugfixes/mpa-worker-template/2026-10-09-independent-worker.zh.md +++ /dev/null @@ -1,62 +0,0 @@ -# 不依赖旧账号模板的 Worker 创建 - -[English](2026-10-09-independent-worker.md) - -- 变更 ID:`mpa-worker-template` -- 创建/修订:2026-10-09 -- 状态:implemented -- 契约:[Studio 创建](../../../specs/studio-mpa-creation/README.zh.md)、[Runtime 部署](../../../specs/mpa-runtime-provisioning/README.zh.md)。 - -## 背景与证据 - -Studio 内置 Worker 配置引用旧账号中的 Tool。新部署账号无法读取该 Tool:创建在 `get_reference_worker` 失败,尚未调用 `CreateTool`。Worker 镜像已显式配置。`worker.py` 只从引用模板复制环境设置;命令、资源、角色与网络由编排器生成。固定 Worker 源码版本 `81f3496` 的 `scripts/run.sh` 提供启动默认值,无需引用 Tool。 - -## 目标、非目标与场景 - -让新账号使用镜像和显式启动配置创建 Worker,同时兼容可选模板引用和已有 Worker。不修改镜像、IAM/网络、Runtime 聊天、前端,也不自动修改已有 Tool。镜像仓库访问仍是独立前置条件。 - -- 新 Studio 创建不读取旧模板,直接到达 `CreateTool`。 -- CLI 配置可以显式继承有权限访问的 Tool 环境,显式设置优先。 -- 显式引用不存在仍失败;结果不确定的创建重试保留原请求和 token。 - -## 需求与设计 - -- **FR-1:** 删除内置 `reference-id`。将不含密钥的 `PORT`、Worker 端口、headless/minimal 模式、workspace/data/runtime/sidecar 根目录及技能空间基础目录显式固定为该镜像的默认值。 -- **FR-2:** 新增可选 `managed.worker.env: dict[str, str]`,默认空。服务端解析完整 `${ENV_NAME}` 引用。拒绝非法大写环境变量名、智能体/Runtime/Tool/技能空间绑定、继承的渠道/Runtime 凭据以及控制库连接地址。`existing-id` 携带非空 env 时失败,避免静默忽略。 -- **FR-3:** 按已过滤模板环境、显式 env、生成的 `MPA_AGENT_ID` 顺序合并。不修改配置对象和模板响应。显式引用失败不回退为空环境。 -- **FR-4:** 保留排序请求哈希、ClientToken、归属校验、取消和有界重试。未完成请求的显式 env 改变时报告配置冲突。环境值不写入部署记录,也不出现在配置摘要、repr 或错误响应中。 - -已有镜像契约支持按会话传入模型凭据;Studio 不复制旧模型密钥。额外运维配置通过 Worker env 和服务端密钥引用提供。不新增依赖、云 API、权限、任务表结构或前端产物。新配置契约由 Studio 组件维护,Runtime 部署引用该契约。非托管旧创建逻辑不变。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | -| --- | --- | --- | -| FR-1 | T-1:修改内置配置并补回归 | AC-1:新账号使用镜像创建时跳过模板读取,提交显式启动配置 | -| FR-2 | T-2:配置结构/解析及安全校验测试 | AC-2:密钥引用可解析;非法/保留字段及已有 Worker env 失败且不暴露值 | -| FR-3 | T-3:请求合并与兼容测试 | AC-3:显式值优先,智能体绑定由编排器生成,缺失显式模板仍失败 | -| FR-4 | T-4:重试/不可变测试及审查 | AC-4:相同请求重试 token 不变;修改 env 失败;注册记录/摘要/repr 不含环境值 | - -## 风险与恢复 - -默认值覆盖固定镜像;自定义镜像可能需要显式 env。不自动执行云创建冒烟。已有 Tool ID 仍是绑定依据,不重命名/更新。尚未写入 Worker 创建意图就失败的任务,可重启 Studio 后以同一智能体 ID 重试。已发出创建且哈希改变的任务必须保留原配置;禁止重置意图绕过防重复保护。 - -## 审查与验证记录 - -未提供 review-spec,直接审查通过了配置优先级、密钥处理、账号边界、部分失败、兼容性及双语一致性。2026-10-09 用户以“帮我改”批准移除固定旧模板并显式配置,在实现前记录。 - -验证范围:`ef0ad519` 加本次 Worker 修改及此前未提交的账号/IAM/网络/APIG 修复。必需命令:配置/Worker 定向测试、托管部署回归、修改文件 Ruff/Pyright、定向 pre-commit、双语/链接/空白检查。真实云创建:`not_run`;代码和模拟请求不能证明镜像拉取权限或平台就绪。浏览器/前端检查:`not_applicable`,无 UI/API 改动。执行后补充结果。 - -### 已执行检查(2026-10-09) - -- 实现前 `fail`:配置/Worker 回归复现固定旧模板和缺少 env 支持(5 failed、94 passed)。 -- `pass`:`uv run --extra dev pytest tests/integrations/mpa_managed/test_config.py tests/integrations/mpa_managed/test_worker.py tests/integrations/mpa_managed/test_worker_recovery.py tests/integrations/mpa_managed/test_worker_metadata.py -q --tb=short` — 152 passed。 -- `pass`:`uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py tests/cli/test_frontend_deploy_iam.py -q --tb=short` — 594 passed、5 条已有弃用警告;覆盖最终模板过滤及未完成 env 恢复。 -- `pass`:对 managed 源码/测试目录的 `config.py`、`worker.py`、`studio_profile.py`、`test_config.py`、`test_worker.py` 执行 `uvx --from ruff==0.11.12 ruff check` 和 `uvx pyright --pythonpath .venv/bin/python` — 零错误。 -- `pass`:对上述五个 Python 文件及八个修改文档执行 `uv run --extra dev pre-commit run --files` — Ruff 和硬编码密钥检测通过。YAML 钩子 `not_applicable`(未修改 YAML)。 -- `pass`:双语标识/相对链接及 `git diff --check`。直接实现审查无阻塞项:模拟新账号请求不读取模板、显式覆盖/不可变性、绑定/控制库地址过滤、安全密钥引用及未完成 token 保护。AC-1 至 AC-4 通过模拟请求验收。 -- `not_run`:真实镜像拉取/容器启动及全量无关 SDK 回归。隔离托管测试集覆盖受影响契约;验证没有创建云资源。`not_applicable`:前端构建/浏览器、生成产物及运行进程冒烟(未修改聊天/前端/镜像代码)。完整 all-files 提交检查等待另行授权提交。 - -T-1 至 T-4 完成,无已约定实现范围延期;真实平台就绪仍属于运维验证限制。重启本地 Studio,以同一智能体 ID 重试尚未生成 Worker 创建意图的任务。 - -启动默认值后续修订:[2026-10-09-worker-image-defaults](2026-10-09-worker-image-defaults.zh.md) 替代 FR-1/AC-1 中内置 env 显式注入的选择。上述初次验证为历史证据,其余契约保留。 diff --git a/prd-spec/bugfixes/mpa-worker-template/2026-10-09-worker-image-defaults.md b/prd-spec/bugfixes/mpa-worker-template/2026-10-09-worker-image-defaults.md deleted file mode 100644 index c44cb2372..000000000 --- a/prd-spec/bugfixes/mpa-worker-template/2026-10-09-worker-image-defaults.md +++ /dev/null @@ -1,42 +0,0 @@ -# Use Worker image defaults without duplicate Tool environment - -[中文版](2026-10-09-worker-image-defaults.zh.md) - -- Change ID: `mpa-worker-image-defaults` -- Created/revised: 2026-10-09 -- Status: implemented -- Predecessor: [independent Worker creation](2026-10-09-independent-worker.md), amends its FR-1/AC-1 startup-setting choice only. -- Contracts: [Studio creation](../../../specs/studio-mpa-creation/README.md); Runtime provisioning consumes that contract unchanged. - -## Evidence, goals and scope - -The pinned Worker source `81f3496` Dockerfile defines PORT=8000, MPA_CODEX_WORKER_PORT=8092, headless/minimal mode and all five configured directory roots. scripts/run.sh also supplies these defaults and synchronizes PUBLIC_PORT from PORT. Duplicating the nine defaults in Tool Envs is unnecessary and overrides defaults when an operator selects another image. The control-plane Tool environment list is not the full container environment. - -Remove only the nine built-in Worker env entries. Keep optional managed.worker.env, reference/existing Worker support, fixed CreateTool.Port=8000 and generated MPA_AGENT_ID. Do not mutate existing Tools, chat, frontend, image versions, permissions or resource identity. Custom images must support the existing /opt/gem/run.sh entrypoint and public port 8000, or use explicit compatible configuration; removing duplication does not guarantee arbitrary image compatibility. - -## Requirements, scenarios and design - -- FR-1: Built-in Studio has empty Worker env and no reference ID. Its new CreateTool Envs contains only generated MPA_AGENT_ID; Port remains 8000. Container startup obtains its port/mode/path settings from the image. -- FR-2: Preserve explicit Worker env merging, validation, redaction and sorted hashing/ClientToken behavior. Existing bindings are not updated. A previously dispatched pending request with changed env still fails hash validation rather than creating a duplicate. -- AC-1: Built-in profile and simulated new-account creation tests prove no duplicate env keys/reference lookup, retain image/role/Port and bind MPA_AGENT_ID. -- AC-2: Existing config/Worker recovery regressions pass, including explicit env and changed-pending-payload rejection. - -No state/schema/API/permission/dependency changes. The Studio spec and bilingual guide describe image-owned defaults; no frontend assets are needed. - -## Tasks, review and verification - -- T-1 (FR-1/AC-1): failing built-in profile/request regressions, remove env block in studio_profile.py, update test_config.py/test_worker.py. -- T-2 (FR-2/AC-2): existing affected provisioning regression and Python gates; synchronize Studio spec/guide and predecessor note. - -User approved removal of all unnecessary defaults on 2026-10-09 with “没必要的就都去了”. Direct design review (review-spec unavailable) cleared bilingual equivalence, entrypoint/port boundary, explicit overrides and pending-intent safety before edits. No blocker or unanswered implementation decision. - -Verification scope: this follow-up on ef0ad519 plus prior uncommitted MPA fixes. Required: fail-first two targeted tests; managed/legacy provisioning and IAM suite; Ruff/Pyright on three changed Python files; scoped pre-commit and paired-language links/identifiers/whitespace. Live cloud startup is not_run (no new cloud writes authorized for verification); frontend/browser/generated assets and runtime process smoke are not_applicable (no changes to those paths). Full commit gates await an authorized commit. Results follow below. - -## Results (2026-10-09) - -- fail before implementation: two targeted tests failed on the duplicate defaults (97 deselected). -- pass: `uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py tests/cli/test_frontend_deploy_iam.py -q --tb=short` — 594 passed, five existing deprecation warnings. AC-1/AC-2 and T-1/T-2 completed with simulated requests. -- pass: `uvx --from ruff==0.11.12 ruff check` and `uvx pyright --pythonpath .venv/bin/python` on studio_profile.py, test_config.py and test_worker.py in their managed directories; zero errors. -- pass: `uv run --extra dev pre-commit run --files` on these three Python files and eight changed documentation files; Ruff/format/hardcoded-secret detection passed. YAML hook not_applicable, no YAML changes. -- pass: paired identifiers/relative links and `git diff --check`; direct implementation review confirms image defaults, generated agent binding and optional explicit env coexist without changing recovery protection. -- not_run: live cloud creation/container startup and unrelated full SDK regression; affected deployment suite passed. not_applicable: UI/build/browser/generated artifacts and runtime smoke. No cloud resources or existing Tools changed. Full commit gates await authorized commit. diff --git a/prd-spec/bugfixes/mpa-worker-template/2026-10-09-worker-image-defaults.zh.md b/prd-spec/bugfixes/mpa-worker-template/2026-10-09-worker-image-defaults.zh.md deleted file mode 100644 index 1db779beb..000000000 --- a/prd-spec/bugfixes/mpa-worker-template/2026-10-09-worker-image-defaults.zh.md +++ /dev/null @@ -1,42 +0,0 @@ -# 使用 Worker 镜像默认值,移除重复 Tool 环境配置 - -[English](2026-10-09-worker-image-defaults.md) - -- 变更 ID:`mpa-worker-image-defaults` -- 创建/修订:2026-10-09 -- 状态:implemented -- 前序:[独立 Worker 创建](2026-10-09-independent-worker.zh.md),仅修订其 FR-1/AC-1 的启动设置选择。 -- 契约:[Studio 创建](../../../specs/studio-mpa-creation/README.zh.md);Runtime 部署继续引用该契约。 - -## 证据、目标与范围 - -固定 Worker 源码 `81f3496` 的 Dockerfile 定义 PORT=8000、MPA_CODEX_WORKER_PORT=8092、headless/minimal 模式及全部五个目录根路径。scripts/run.sh 同样提供这些默认值,并从 PORT 同步 PUBLIC_PORT。在 Tool Envs 中重复九项默认值没有必要,还会覆盖运维选择其他镜像时的默认设置。控制面的 Tool 环境列表不等于完整容器环境。 - -只移除内置 Worker env 中这九项。保留可选 managed.worker.env、模板引用/已有 Worker 支持、固定 CreateTool.Port=8000 及生成的 MPA_AGENT_ID。不修改已有 Tool、聊天、前端、镜像版本、权限或资源身份。自定义镜像必须支持已有 /opt/gem/run.sh 入口及对外端口 8000,或使用兼容的显式配置;去掉重复设置不代表任意镜像都兼容。 - -## 需求、场景与设计 - -- FR-1:内置 Studio Worker env 为空,reference ID 为空。新 CreateTool Envs 仅包含生成的 MPA_AGENT_ID;Port 仍为 8000。容器启动从镜像获得端口/模式/目录设置。 -- FR-2:保留显式 Worker env 合并、校验、脱敏及排序哈希/ClientToken 行为。不更新已有绑定。已发送而未完成的请求如 env 改变,仍拒绝哈希不一致,不重复创建。 -- AC-1:内置配置和模拟新账号创建测试证明不重复注入、不读取模板,保留镜像/角色/Port 并绑定 MPA_AGENT_ID。 -- AC-2:已有配置/Worker 恢复回归通过,包括显式 env 及未完成载荷改变时的拒绝。 - -不改变状态/表结构/API/权限/依赖。Studio 契约和双语指南说明默认值归镜像所有;无需前端产物。 - -## 任务、审查与验证 - -- T-1(FR-1/AC-1):先写失败的内置配置/请求回归,删除 studio_profile.py 的 env 块,更新 test_config.py/test_worker.py。 -- T-2(FR-2/AC-2):运行受影响部署回归及 Python 检查,同步 Studio 契约/指南及前序说明。 - -用户于 2026-10-09 以“没必要的就都去了”批准移除全部不必要默认配置。未提供 review-spec,编辑前直接审查通过双语一致性、入口/端口边界、显式覆盖及未完成意图保护。没有阻塞或未决实现选择。 - -验证范围:ef0ad519 加本次后续修改及此前未提交的 MPA 修复。必需:两项先失败的定向测试;托管/旧部署和 IAM 测试集;三个修改 Python 文件的 Ruff/Pyright;定向 pre-commit 与双语链接/标识/空白检查。真实云启动为 not_run(未授权为验证新增云写入);前端/浏览器/生成产物及运行进程冒烟为 not_applicable(未修改这些路径)。完整提交检查等待授权提交。结果见下文。 - -## 结果(2026-10-09) - -- 实现前 fail:两项定向测试因重复默认配置失败(97 deselected)。 -- pass:`uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py tests/cli/test_frontend_deploy_iam.py -q --tb=short` — 594 passed,五条已有弃用警告。AC-1/AC-2 及 T-1/T-2 已通过模拟请求完成。 -- pass:对 managed 目录内 studio_profile.py、test_config.py、test_worker.py 执行 `uvx --from ruff==0.11.12 ruff check` 和 `uvx pyright --pythonpath .venv/bin/python`,零错误。 -- pass:对三个 Python 文件及八个修改文档执行 `uv run --extra dev pre-commit run --files`,Ruff/格式/硬编码密钥检测通过。YAML 钩子为 not_applicable,未修改 YAML。 -- pass:双语标识/相对链接及 `git diff --check`;直接实现审查确认镜像默认值、生成的智能体绑定和可选显式 env 共存,恢复保护不变。 -- not_run:真实云创建/容器启动及无关全量 SDK 回归;受影响部署测试集通过。not_applicable:UI/构建/浏览器/生成产物及运行冒烟。未修改云资源或已有 Tool。完整提交检查等待授权提交。 diff --git a/prd-spec/bugfixes/pr1180-ci/2026-10-21-repair-ci-gates.md b/prd-spec/bugfixes/pr1180-ci/2026-10-21-repair-ci-gates.md deleted file mode 100644 index 27a5cbad9..000000000 --- a/prd-spec/bugfixes/pr1180-ci/2026-10-21-repair-ci-gates.md +++ /dev/null @@ -1,67 +0,0 @@ -# Repair PR #1180 CI Gates - -[中文版](2026-10-21-repair-ci-gates.zh.md) - -## Metadata - -- Change ID: `pr1180-ci` -- Created/Revised: 2026-10-21 -- Status: implemented (publication authorized; verification in progress) -- Related component specs: none; generated metadata, license comments, and test fixture text preserve all runtime contracts. -- Predecessor: [frontend lock restoration](../frontend-lockfile-sync/2026-10-09-restore-esbuild-lock-entries.md). - -## Background and Evidence - -At PR head `8f9911b8`, run `39579012758` fails `npm ci --ignore-scripts`: esbuild 0.28.2 and its platform packages are missing. Commit `d2dc3da2` removed 512 lock lines; the only package.json change is a test command, so dependency declarations still match its parent. - -Run `39579012740` reports missing Apache headers in the two MPA proxy modules and their policy tests. Run `39579012654` reports one unverified PostgreSQL finding in a fake connection string in `test_creation_failure_details.py`. No verified credential is reported. A follow-up commit alone cannot clear this finding because the workflow scans PR history. - -## Goals and Non-goals - -Restore reproducible installation, required license headers, and scanner-safe test data while preserving redaction assertions. Dependency upgrades, runtime changes, scanner exclusions, deployment, commits, pushes, and history rewrites are outside the local repair authorization. - -## Scenarios and Requirements - -- `FR-1`: Given unchanged dependency declarations, CI installation must accept the complete lock graph. -- `FR-2`: Given the four Python files, each must carry the standard `.licenserc.yaml` header. -- `FR-3`: Given private exception text, diagnostic events must retain origin metadata and omit private text. The fixture must not contain a credential-shaped connection string. -- `FR-4`: Local verification must distinguish working-tree remediation from the remaining historical scan failure. - -## Design and Contract Impact - -Restore `frontend/package-lock.json` from `d2dc3da2^` without regenerating or upgrading dependencies. Add comment-only headers to `veadk/integrations/mpa/{agents_md_proxy,collaborator_proxy}.py` and `tests/test_mpa_{agents_md,collaborator}_proxy_policy.py`. Replace only the exception fixture text in `tests/integrations/mpa_managed/test_creation_failure_details.py`; preserve all assertions. - -Public APIs, permissions, state, concurrency, configuration, generated code, and component contracts are unchanged. UI/browser and live cloud checks are not applicable because no UI, network behavior, or cloud call changes. Existing CI failures provide the red baseline; use the existing diagnostic regression test rather than adding a test of comments or lock text. - -Reject scanner allowlisting and dependency upgrades: neither addresses the narrow cause. A separately authorized rewrite must remove the fixture from the introducing commit and preserve authorship, with remote synchronization and lease protection before publishing. - -## Tasks and Verification - -| Requirement | Task | Acceptance | Command/evidence | Result | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1`: restore lock | `AC-1`: exact parent graph; clean install | `git diff d2dc3da2^ -- frontend/package-lock.json`; `npm --prefix frontend ci --ignore-scripts` | pass: no diff; 695 packages installed | -| `FR-2` | `T-2`: add headers | `AC-2`: standard header in all four files | Compare with `.licenserc.yaml` and the existing diagnostic test header; Ruff | pass: all four match | -| `FR-3` | `T-3`: repair fixture | `AC-3`: existing assertions pass | `uv run --extra dev pytest tests/integrations/mpa_managed/test_creation_failure_details.py tests/test_mpa_agents_md_proxy_policy.py tests/test_mpa_collaborator_proxy_policy.py -q` | pass: 34 tests | -| `FR-1` | `T-4`: frontend gates | `AC-4`: coverage, tests, build pass | `npm --prefix frontend run test:harness-sidecar-coverage`; `npm --prefix frontend test`; `npm --prefix frontend run build -- --outDir /tmp/veadk-pr1180-build --emptyOutDir false` | pass: 22 coverage tests; 1,389 Node and 154 Vitest tests; both builds | -| `FR-4` | `T-5`: review and report | `AC-5`: honest checks and history limitation | `uv run --extra dev pre-commit run --all-files`; diff review; `gh pr checks 1180 --repo volcengine/veadk-python` | pass locally; remote still has the original three failures and pending jobs | - -Execute T-1 through T-3 first, T-4 next, T-5 last. No rollout is included. Restore the affected files from HEAD to recover the local repair if necessary, after preserving any later user changes. - -## Risks and Review Record - -- Local macOS/Node 26 verification differs from Ubuntu/Node 22.17.0 CI. -- Build output must be isolated so verification does not introduce unrelated packaged asset changes. -- Historical secret scanning remains unresolved until the user authorizes rewriting and publishing the PR commits. -- 2026-10-21: User approved the previously explained three repairs with “帮我修一下”. -- 2026-10-21: `review-spec` review confirms narrow scope, no contract impact, preserved assertions and bilingual equivalence. The historical scan limitation is explicitly separated from local acceptance. No local design blocker remains. -- 2026-10-21: All local acceptance checks above ran against HEAD `8f9911b8` plus this repair. No dependency declarations, scanner rules, production statements, test assertions, or tracked web assets changed. Header equality was checked using `head -14` and `diff` for all four files. -- Ruff: `uv run --extra dev ruff check` with the five changed Python paths passed. Pyright was initially unavailable via `uv run`; isolated `uvx pyright --pythonpath .venv/bin/python` with those paths passed with 0 errors and 0 warnings. Running uvx without the repository interpreter first produced missing-import errors; selecting the correct interpreter resolved them without changing configuration. -- Coverage: statements 98.55%, branches 94.44%, functions and lines 100%. `git diff --check` passed. Manual final review confirms fixture redaction coverage is unchanged and the restored lock exactly matches the complete parent blob. -- Test workflow: Step1–Step5 completed with one fixture repair/verification round (`all-pass`); no new test cases or production defect findings. Step6 Python coverage is not_applicable to this fixture/comment repair; no applicable coverage target changed. Step7 `utree flush` passed. -- Broad Python regression and backend Sidecar gates: not_run locally because no executable production logic or shared contract changed; PR Python 3.12 regression passed. Real cloud, runtime smoke, and browser checks: not_applicable to this repair. Local Apache Eyes and TruffleHog binaries: not_run, unavailable; standard header comparison and pre-commit secret gates passed, but they do not prove a clean historical TruffleHog scan. -- Installation reports 16 existing dependency audit findings; dependency remediation remains outside this restoration. Build reports large-chunk warnings and completes successfully. -- Repository helper plan/handoff runs returned `onboarding_required` because no Manifest exists; actual acceptance is recorded here using repository-native checks. No repository onboarding changes were introduced. -- 2026-10-21: The user explicitly authorized rewriting and publishing with “允许”. Both remotes were fetched: `upstream/main` remains `5e5deb6a`, and the expected remote PR head remains `8f9911b8`. Interactive rebase onto `upstream/main` places each repair in its introducing commit while preserving author identity and author date. -- First rewritten commit `03814668`: all-files pre-commit passed and 30 affected tests passed before amendment. Commit hooks passed without bypasses. -- Final second-commit working tree: all-files pre-commit passed and all 34 affected tests passed in 2.28s after remote synchronization. The six repaired files exactly match the previously verified local repair; no unrelated implementation or asset diff was introduced. Whitespace checks passed. -- Publication plan: finish the second amendment with remaining headers and this bilingual evidence, rerun all-files pre-commit and the 34 affected Python tests, verify the final diff and rewritten history, then push with an explicit lease bound to remote head `8f9911b8188c5bba20085c0ff68c6b7e5fe02050`. Cloud deployment and merging remain outside authorization. diff --git a/prd-spec/bugfixes/pr1180-ci/2026-10-21-repair-ci-gates.zh.md b/prd-spec/bugfixes/pr1180-ci/2026-10-21-repair-ci-gates.zh.md deleted file mode 100644 index d9810b40c..000000000 --- a/prd-spec/bugfixes/pr1180-ci/2026-10-21-repair-ci-gates.zh.md +++ /dev/null @@ -1,67 +0,0 @@ -# 修复 PR #1180 CI 门禁 - -[English](2026-10-21-repair-ci-gates.md) - -## 元数据 - -- Change ID:`pr1180-ci` -- 创建/修订:2026-10-21 -- 状态:implemented(已授权发布;验证进行中) -- 关联组件规范:无;生成元数据、许可证注释和测试夹具文本保持全部运行时契约。 -- 前序方案:[前端锁文件恢复](../frontend-lockfile-sync/2026-10-09-restore-esbuild-lock-entries.zh.md)。 - -## 背景与证据 - -PR 提交 `8f9911b8` 的运行 `39579012758` 在 `npm ci --ignore-scripts` 失败:缺少 esbuild 0.28.2 及其平台包。提交 `d2dc3da2` 删除了 512 行锁文件;package.json 只改了测试命令,因此依赖声明仍与其父提交一致。 - -运行 `39579012740` 报告两个 MPA 代理模块及其策略测试缺少 Apache 许可证头。运行 `39579012654` 在 `test_creation_failure_details.py` 的假连接串中报告一个未经验证的 PostgreSQL 扫描发现,没有已验证凭据。工作流扫描 PR 历史,因此仅追加修复提交不能清除该发现。 - -## 目标与非目标 - -恢复可复现安装、必需的许可证头和不会触发扫描器的测试数据,保留脱敏断言。依赖升级、运行时变化、扫描排除、部署、提交、推送和历史改写不在本地修复授权内。 - -## 场景与需求 - -- `FR-1`:依赖声明不变时,CI 安装必须接受完整锁定依赖图。 -- `FR-2`:四个 Python 文件必须包含 `.licenserc.yaml` 的标准许可证头。 -- `FR-3`:异常文本包含私密内容时,诊断事件必须保留来源元数据并排除私密文本。夹具不能包含凭据形态的连接串。 -- `FR-4`:本地验证必须区分工作区修复与仍存在的历史扫描失败。 - -## 设计与契约影响 - -从 `d2dc3da2^` 恢复 `frontend/package-lock.json`,不重新生成或升级依赖。为 `veadk/integrations/mpa/{agents_md_proxy,collaborator_proxy}.py` 和 `tests/test_mpa_{agents_md,collaborator}_proxy_policy.py` 添加仅注释的许可证头。仅替换 `tests/integrations/mpa_managed/test_creation_failure_details.py` 中的异常夹具文本,保留全部断言。 - -公开 API、权限、状态、并发、配置、生成代码和组件契约不变。没有 UI、网络行为或云调用变化,因此 UI/浏览器和真实云端检查不适用。现有 CI 失败提供修复前的失败基线;复用既有诊断回归测试,不添加仅检查注释或锁文件文本的测试。 - -不采用扫描白名单或依赖升级,因为它们不能针对性解决根因。后续需单独授权改写:从引入问题的提交中删除该夹具,保留作者信息,发布前同步远端并使用 lease 保护。 - -## 任务与验证 - -| 需求 | 任务 | 验收 | 命令/证据 | 结果 | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1`:恢复锁文件 | `AC-1`:与父提交依赖图一致,干净安装成功 | `git diff d2dc3da2^ -- frontend/package-lock.json`;`npm --prefix frontend ci --ignore-scripts` | pass:无差异;安装 695 个包 | -| `FR-2` | `T-2`:添加许可证头 | `AC-2`:四个文件均有标准头 | 对照 `.licenserc.yaml` 和既有诊断测试的许可证头;Ruff | pass:四个文件均一致 | -| `FR-3` | `T-3`:修复夹具 | `AC-3`:原有断言通过 | `uv run --extra dev pytest tests/integrations/mpa_managed/test_creation_failure_details.py tests/test_mpa_agents_md_proxy_policy.py tests/test_mpa_collaborator_proxy_policy.py -q` | pass:34 个测试 | -| `FR-1` | `T-4`:前端门禁 | `AC-4`:覆盖率、测试、构建通过 | `npm --prefix frontend run test:harness-sidecar-coverage`;`npm --prefix frontend test`;`npm --prefix frontend run build -- --outDir /tmp/veadk-pr1180-build --emptyOutDir false` | pass:22 个覆盖率测试;1,389 个 Node 和 154 个 Vitest 测试;两项构建 | -| `FR-4` | `T-5`:审查与报告 | `AC-5`:如实报告检查及历史限制 | `uv run --extra dev pre-commit run --all-files`;差异审查;`gh pr checks 1180 --repo volcengine/veadk-python` | 本地 pass;远端仍有原三项失败和排队任务 | - -先执行 T-1 至 T-3,再执行 T-4,最后执行 T-5。不包含发布。需要撤销本地修复时,在保留后续用户改动的前提下,从 HEAD 恢复相关文件。 - -## 风险与评审记录 - -- 本地 macOS/Node 26 验证与 Ubuntu/Node 22.17.0 CI 环境不同。 -- 构建输出必须隔离,避免验证引入无关的打包资源变化。 -- 用户授权改写并发布 PR 提交前,历史密钥扫描仍未解决。 -- 2026-10-21:用户以“帮我修一下”批准了此前已说明的三处修复。 -- 2026-10-21:`review-spec` 评审确认范围收敛、无契约影响、保留断言且双语等价。历史扫描限制与本地验收明确区分,没有本地设计阻塞项。 -- 2026-10-21:上表全部本地验收检查针对 HEAD `8f9911b8` 加本次修复执行。依赖声明、扫描规则、生产语句、测试断言和已跟踪 Web 资源均未变化。使用 `head -14` 和 `diff` 对照四个文件的许可证头,全部一致。 -- Ruff:`uv run --extra dev ruff check` 加五个修改的 Python 路径通过。Pyright 最初无法通过 `uv run` 启动;使用隔离的 `uvx pyright --pythonpath .venv/bin/python` 加这些路径通过,0 错误、0 警告。最初未指定仓库解释器的 uvx 检查报告缺少导入;选择正确解释器后解决,未修改配置。 -- 覆盖率:语句 98.55%,分支 94.44%,函数和行 100%。`git diff --check` 通过。最终人工审查确认夹具的脱敏覆盖不变,恢复的锁文件与完整父提交 blob 完全一致。 -- 测试流程:Step1–Step5 完成,一轮夹具修复/验证(`all-pass`);没有新增测试用例或生产缺陷发现。Step6 Python 覆盖率对此夹具/注释修复为 not_applicable,没有适用覆盖率目标变化。Step7 `utree flush` 通过。 -- 全量 Python 回归和后端 Sidecar 门禁:本地 not_run,因为没有可执行生产逻辑或共享契约变化;PR Python 3.12 回归已通过。真实云端、运行时 smoke 和浏览器检查:对此修复为 not_applicable。本地 Apache Eyes 和 TruffleHog 二进制:not_run,工具不可用;标准许可证头对照和 pre-commit 密钥门禁通过,但不能证明历史 TruffleHog 扫描干净。 -- 安装报告 16 个既有依赖审计发现;依赖整改不在本次恢复范围。构建报告大分块警告,仍成功完成。 -- 仓库辅助工具 plan/handoff 因缺少 Manifest 返回 `onboarding_required`;实际验收使用仓库原生命令记录于本文,未引入仓库接入改动。 -- 2026-10-21:用户以“允许”明确授权改写并发布。已拉取两个远端:`upstream/main` 仍为 `5e5deb6a`,预期远端 PR 提交仍为 `8f9911b8`。对 `upstream/main` 执行交互式 rebase,将各项修复放入引入问题的提交,保留作者身份和作者日期。 -- 第一条改写提交 `03814668`:修改提交前全量 pre-commit 和 30 个受影响测试通过。提交钩子通过,没有绕过检查。 -- 第二条提交的最终工作区:远端同步后,全量 pre-commit 通过,34 个受影响测试在 2.28s 内全部通过。六个修复文件与此前验证过的本地修复完全一致,没有引入无关实现或资源差异。空白检查通过。 -- 发布计划:将其余许可证头和本双语证据纳入第二条提交,重跑全量 pre-commit 和 34 个受影响 Python 测试,验证最终差异和改写历史,然后使用绑定远端提交 `8f9911b8188c5bba20085c0ff68c6b7e5fe02050` 的显式 lease 推送。云端部署和合并仍不在授权内。 diff --git a/prd-spec/bugfixes/pr1182-ci/2026-10-23-a2a-uri-test-fixtures.md b/prd-spec/bugfixes/pr1182-ci/2026-10-23-a2a-uri-test-fixtures.md deleted file mode 100644 index dee1b7c3c..000000000 --- a/prd-spec/bugfixes/pr1182-ci/2026-10-23-a2a-uri-test-fixtures.md +++ /dev/null @@ -1,60 +0,0 @@ -# Remove Password-shaped A2A URI Test Fixtures - -[中文版](2026-10-23-a2a-uri-test-fixtures.zh.md) - -## Scope and Evidence - -- Change ID: `pr1182-ci` -- Created/Revised: 2026-10-23 -- Status: implemented and history repaired; publication authorized, CI confirmation pending. -- PR #1182 head: `0e71e4aa`; base: `cb9975df`. - -Secret Scan run `39893501693` failed with exit 183. TruffleHog 3.99.4 reported 0 verified and 3 unverified URI results. GitHub annotations locate two fixtures in `tests/cli/test_a2a_center_actions.py` and one in `tests/cli/test_a2a_center_registration.py`; their current line numbers are 403, 484, and 109. These are deliberately invalid test inputs containing fake passwords, not verified credentials. - -## Requirements and Design - -- `FR-1`: Preserve API rejection and display filtering of URLs with userinfo. -- `FR-2`: Remove password-shaped test literals while retaining existing assertions. -- `FR-3`: Keep scanner enforcement and all production behavior unchanged. - -Replace the three password-bearing inputs with username-only URLs. Keep `private-value` or `secret` as the username so existing non-disclosure assertions remain meaningful. `_card_url` and `_display_url` already reject nonempty usernames. Endpoint filtering must still return only the expected safe endpoint. Tests use FastAPI TestClient and mocked cloud calls; no real provider operation is needed. - -No component contract changes: registration validation, `CON-CARD-VIEW`, `CON-ENDPOINTS`, authentication, permissions, state, concurrency and runtime behavior remain unchanged. See [A2A Center specification](../../../specs/mpa-collaborating-agents/README.md). UI, generated assets, dependencies, scanner rules and user documentation are unaffected. - -Reject exclusions, encoded passwords and string splitting: fix unnecessary test data rather than hide credential-shaped material. The CI workflow scans commit history; a follow-up commit alone will still include the old fixtures. Remote remediation must separately remove these literals from the introducing commit while preserving later changes, merge structure and authorship. - -## Tasks and Acceptance - -| Requirement | Task | Acceptance | Verification | Result | -| --- | --- | --- | --- | --- | -| `FR-1`, `FR-2` | `T-1`: change three fixtures | `AC-1`: same response/filter assertions pass | `uv run --extra dev pytest tests/cli/test_a2a_center_actions.py tests/cli/test_a2a_center_registration.py -q` | pass: 88 tests before and after | -| `FR-2` | `T-2`: compare scanner results | `AC-2`: original fixture scan reports URI results; repaired files do not | TruffleHog 3.99.4 filesystem scan with verification disabled, URI detector only | pass: 3 findings/exit 183 before; 0 findings/exit 0 after | -| `FR-3` | `T-3`: verify/review | `AC-3`: only fixtures and bilingual evidence change | Ruff, Pyright, `uv run --extra dev pre-commit run --all-files`, `git diff --check` and manual review | pass locally | - -Execute baseline verification, T-1, T-2 and T-3 in order, then report remaining history remediation. Recovery is restoring the two test files from HEAD after preserving subsequent user changes. - -## Review, Risks and Authorization - -- User requested diagnosis and repair of PR #1182 CI, then approved the proposed history repair and PR update with "帮我提交" on 2026-10-23. This authorizes commit, preserving authors and merge topology during history rewrite, and publication with an explicit force-with-lease. Merge and deployment remain outside scope. -- `review-spec`: feasible, minimal, no API or contract impact; username-only inputs still exercise the stated userinfo boundary and preserve sensitive-text assertions. Password-specific input coverage is intentionally replaced by userinfo coverage; production checks for passwords remain unchanged. -- The existing failed CI is the scanner red baseline. Existing API tests establish behavior before/after; additional tests of literal formatting would not establish product correctness. -- These are API route tests, outside the unit-test generation skill's stated scope. No new test cases are required. -- Local macOS scanning without verification establishes detector behavior, not a clean CI history scan. Final CI confirmation requires the authorized history repair and rerun. -- Other checks currently pending must not be reported as passing. Record actual verification below. - -## Verification Record - -2026-10-23, tested scope `0e71e4aa` plus this local repair on `fix/pr1182-ci` (created from the PR branch and synchronized using `git pull --ff-only`). - -- Target tests: pass before (88 in 2.62s) and after (88 in 1.15s), using repository Python 3.10. The two dependency deprecation warnings are unchanged. -- Scanner: downloaded TruffleHog 3.99.4 for macOS ARM64 into `/tmp/veadk-pr1182-tools`, with SHA-256 verified against the release checksum; no global installation. Baseline copies are outside the repository. -- Baseline command: `/tmp/veadk-pr1182-tools/trufflehog filesystem /tmp/veadk-pr1182-fixtures-before --include-detectors=URI --no-verification --no-update --json --fail`; fail/183 with exactly 3 URI findings. This expected failure is the negative control. -- Repaired command: `/tmp/veadk-pr1182-tools/trufflehog filesystem tests/cli/test_a2a_center_actions.py tests/cli/test_a2a_center_registration.py --include-detectors=URI --no-verification --no-update --json --fail`; pass/0 with 0 URI findings. -- All-files pre-commit: pass, including Ruff check/format and both secret gates. `uvx pyright --pythonpath .venv/bin/python tests/cli/test_a2a_center_actions.py tests/cli/test_a2a_center_registration.py`: pass, 0 errors and warnings. -- Diff/paired-language review: pass; only three fixture lines change, assertions and production files unchanged. No product defect is inferred from scanner detection. -- Broader local Python/frontend/browser/cloud tests: not_applicable to this fixture-only change. Existing remote frontend/backend gates, release verification, smoke, license and Python 3.12 checks passed; remote Python 3.10 remains pending at the last check. -- Repository helper plan returned `onboarding_required` because there is no Manifest; use the native contracts and checks above rather than introduce onboarding changes. -- History remediation: pass locally. Rebased onto fetched `upstream/main` (`c662cc8d`) using `--rebase-merges`, amended introducing commit `46f60e12` to `497f4b65`, and preserved subsequent changes, merge topology, author identities and author dates. Rewritten pre-documentation head is `f44063d4`. A local backup ref retains the old history for recovery and is not published. -- Synchronization: the only additional tree differences from old PR head are upstream PR #1181's six-file skill telemetry correction; there were no conflicts or manual changes to those files. Target tests plus `tests/skills/test_skill_telemetry.py`: pass, 101 tests in 6.92s after synchronization. -- History scanner: pass, 0 findings, using `/tmp/veadk-pr1182-tools/trufflehog git file:///Users/bytedance/Desktop/github-code/veadk-python --since-commit=c662cc8d --branch=fix/pr1182-ci --skip-additional-refs --include-detectors=URI --no-verification --no-update --json --fail`. Only the rewritten PR history was scanned; this does not replace CI's full detector configuration. -- Publication: authorized; expected old remote head is `0e71e4aa4888d8b7a79b4a59919d7b7a4289af62`. New remote CI results remain pending until publication. diff --git a/prd-spec/bugfixes/pr1182-ci/2026-10-23-a2a-uri-test-fixtures.zh.md b/prd-spec/bugfixes/pr1182-ci/2026-10-23-a2a-uri-test-fixtures.zh.md deleted file mode 100644 index c12fce5f9..000000000 --- a/prd-spec/bugfixes/pr1182-ci/2026-10-23-a2a-uri-test-fixtures.zh.md +++ /dev/null @@ -1,60 +0,0 @@ -# 去除 A2A URI 测试中的密码形态夹具 - -[English](2026-10-23-a2a-uri-test-fixtures.md) - -## 范围与证据 - -- Change ID:`pr1182-ci` -- 创建/修订:2026-10-23 -- 状态:实现和历史修复完成;发布已授权,CI 确认待完成。 -- PR #1182 提交:`0e71e4aa`;基线:`cb9975df`。 - -Secret Scan 运行 `39893501693` 以退出码 183 失败。TruffleHog 3.99.4 报告 0 个已验证、3 个未经验证的 URI 发现。GitHub 注解定位到 `tests/cli/test_a2a_center_actions.py` 的两处夹具及 `tests/cli/test_a2a_center_registration.py` 的一处夹具;当前行号分别为 403、484 和 109。这些是刻意无效且含假密码的测试输入,并非已验证凭据。 - -## 需求与设计 - -- `FR-1`:保留 API 拒绝及展示过滤带用户信息 URL 的行为。 -- `FR-2`:去除密码形态测试字面量,保留既有断言。 -- `FR-3`:保持扫描门禁和全部生产行为不变。 - -将三处含密码输入替换为只有用户名的 URL。保留 `private-value` 或 `secret` 作为用户名,使既有不泄露断言仍然有意义。`_card_url` 和 `_display_url` 已拒绝非空用户名。端点过滤仍须只返回预期安全端点。测试使用 FastAPI TestClient 和模拟云调用,无需真实云端操作。 - -没有组件契约变化:注册校验、`CON-CARD-VIEW`、`CON-ENDPOINTS`、身份验证、权限、状态、并发和运行时行为不变。参见 [A2A 中心规范](../../../specs/mpa-collaborating-agents/README.zh.md)。UI、生成资源、依赖、扫描规则和用户文档均不受影响。 - -不采用排除项、编码密码或字符串拆分:去除不必要测试数据,而不是隐藏凭据形态内容。CI 工作流扫描提交历史,因此只追加修复提交仍会包含旧夹具。远端整改需另行从引入问题的提交中去除这些字面量,并保留后续改动、合并结构和作者信息。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | 验证 | 结果 | -| --- | --- | --- | --- | --- | -| `FR-1`、`FR-2` | `T-1`:修改三处夹具 | `AC-1`:相同响应/过滤断言通过 | `uv run --extra dev pytest tests/cli/test_a2a_center_actions.py tests/cli/test_a2a_center_registration.py -q` | pass:修复前后均为 88 个测试 | -| `FR-2` | `T-2`:对照扫描结果 | `AC-2`:原夹具扫描产生 URI 发现,修复文件不产生 | TruffleHog 3.99.4 filesystem 扫描,禁用验证,仅启用 URI 检测器 | pass:修复前 3 个发现/退出码 183;修复后 0 个发现/退出码 0 | -| `FR-3` | `T-3`:验证/审查 | `AC-3`:仅夹具和双语证据变化 | Ruff、Pyright、`uv run --extra dev pre-commit run --all-files`、`git diff --check` 和人工审查 | 本地 pass | - -依次执行基线验证、T-1、T-2、T-3,然后报告剩余历史整改。需要撤销时,在保留后续用户改动的前提下,从 HEAD 恢复两个测试文件。 - -## 评审、风险与授权 - -- 用户要求诊断并修复 PR #1182 CI,并于 2026-10-23 以“帮我提交”批准所提历史修复和 PR 更新方案。授权提交、保留作者和合并拓扑的历史改写,以及使用显式 force-with-lease 发布。合并与部署仍不在范围内。 -- `review-spec`:方案可行、改动最小、无 API 或契约影响;只有用户名的输入仍覆盖约定的用户信息边界,保留敏感文本断言。密码专用输入覆盖明确替换为用户信息覆盖;生产密码检查不变。 -- 既有失败 CI 提供扫描失败基线。现有 API 测试用于确认修复前后行为;新增仅校验字面量格式的测试不能证明产品正确性。 -- 这些是 API 路由测试,不在单元测试生成技能声明的适用范围内。无需新增测试用例。 -- 本地 macOS 禁用验证的扫描只证明检测器行为,不证明 CI 历史扫描干净。最终 CI 确认需授权历史修复并重新运行。 -- 其他排队检查不能报告为通过。实际验证结果记录于下文。 - -## 验证记录 - -2026-10-23,验证范围为 `0e71e4aa` 加本次本地修复,分支为 `fix/pr1182-ci`(从 PR 分支创建,并以 `git pull --ff-only` 同步)。 - -- 目标测试:修复前 pass(88 个,2.62s)、修复后 pass(88 个,1.15s),使用仓库 Python 3.10。两条依赖弃用警告不变。 -- 扫描工具:下载 macOS ARM64 的 TruffleHog 3.99.4 至 `/tmp/veadk-pr1182-tools`,根据发布校验和验证 SHA-256;没有全局安装。基线副本存放于仓库外。 -- 基线命令:`/tmp/veadk-pr1182-tools/trufflehog filesystem /tmp/veadk-pr1182-fixtures-before --include-detectors=URI --no-verification --no-update --json --fail`;fail/183,恰好 3 个 URI 发现。这一预期失败是负控证据。 -- 修复后命令:`/tmp/veadk-pr1182-tools/trufflehog filesystem tests/cli/test_a2a_center_actions.py tests/cli/test_a2a_center_registration.py --include-detectors=URI --no-verification --no-update --json --fail`;pass/0,0 个 URI 发现。 -- 全量 pre-commit:pass,包括 Ruff check/format 和两项密钥门禁。`uvx pyright --pythonpath .venv/bin/python tests/cli/test_a2a_center_actions.py tests/cli/test_a2a_center_registration.py`:pass,0 错误、0 警告。 -- 差异/双语评审:pass;仅三行夹具变化,断言和生产文件不变。不会因扫描发现推断存在产品缺陷。 -- 更广泛的本地 Python/前端/浏览器/云端测试:对此仅夹具变化为 not_applicable。远端既有前后端门禁、发布验证、smoke、许可证和 Python 3.12 检查通过;最后查询时远端 Python 3.10 测试仍在排队。 -- 仓库辅助工具 plan 因缺少 Manifest 返回 `onboarding_required`;采用上述原生契约和检查,不引入仓库接入改动。 -- 历史整改:本地 pass。以 `--rebase-merges` 变基至已获取的 `upstream/main`(`c662cc8d`),将引入提交 `46f60e12` 修正为 `497f4b65`,保留后续改动、合并拓扑、作者身份和作者日期。文档提交前的新头为 `f44063d4`。本地备份引用保留旧历史以便恢复,不发布该引用。 -- 同步:相较旧 PR 头,额外树差异仅为上游 PR #1181 的六文件技能指标修正;没有冲突或对这些文件的手动修改。同步后目标测试加 `tests/skills/test_skill_telemetry.py`:pass,101 个测试,6.92s。 -- 历史扫描:pass,0 个发现,命令为 `/tmp/veadk-pr1182-tools/trufflehog git file:///Users/bytedance/Desktop/github-code/veadk-python --since-commit=c662cc8d --branch=fix/pr1182-ci --skip-additional-refs --include-detectors=URI --no-verification --no-update --json --fail`。仅扫描改写后的 PR 历史,不能替代 CI 的完整检测器配置。 -- 发布:已授权;预期旧远端头为 `0e71e4aa4888d8b7a79b4a59919d7b7a4289af62`。新远端 CI 结果需发布后确认。 diff --git a/prd-spec/bugfixes/runtime-dependency-compatibility/2026-10-12-google-adk-opentelemetry-baseline.md b/prd-spec/bugfixes/runtime-dependency-compatibility/2026-10-12-google-adk-opentelemetry-baseline.md deleted file mode 100644 index fe2e3aef9..000000000 --- a/prd-spec/bugfixes/runtime-dependency-compatibility/2026-10-12-google-adk-opentelemetry-baseline.md +++ /dev/null @@ -1,120 +0,0 @@ -# Align package dependency metadata for Google ADK and OpenTelemetry - -[中文版](2026-10-12-google-adk-opentelemetry-baseline.zh.md) - -- Change ID: `runtime-dependency-compatibility` -- Created/revised: 2026-10-13 -- Status: implemented -- Related component spec: [Runtime dependency compatibility](../../../specs/runtime-dependency-compatibility/README.md) -- Scope: main package dependency metadata, the main development lock, metadata regression tests, and minimal ADK 2.7 build-graph/Studio compatibility coverage. Studio, sandbox, generated project, managed sidecar, and Docker dependency declarations are intentionally out of scope. - -## Background and evidence - -Two upstream Google ADK commits fix session event timestamp determinism and UTC storage/filtering: - -- `4ca975eb9acd30a64b2c8615a93d31dada63052c`: `fix(sessions): make event reads deterministic and timezone-correct` -- `1b8ed3d9c0b125852df1902dd32b65bcab63ce1d`: `fix(sessions): store and filter event timestamps in UTC` - -Those fixes are available in Google ADK `2.7.0` and later. The local VeADK lock previously selected Google ADK `2.2.0`. - -The confirmed resolver blockers were: - -- AgentKit SDK's old OpenTelemetry upper bounds. They were fixed by publishing `agentkit-sdk-python==0.8.9`. -- VeADK's direct `opentelemetry-exporter-otlp==1.37.0` exact pin. - -Fresh resolver evidence on 2026-10-13 showed that an unconstrained `google-adk>=2.7.0` selects `google-adk==2.11.0`. The targeted compatibility suite failed on that line because ADK 2.11 removed or changed internals VeADK still patches, including `google.adk.flows.llm_flows.functions.__call_tool_async`, and piagent tool execution then saw a missing `run_config` on the test invocation context. This PR therefore supports the current 2.7 compatibility window as `google-adk>=2.7.0,<2.8.0`. - -## Goals - -- Raise VeADK's Google ADK requirement to `google-adk>=2.7.0,<2.8.0` so the UTC session timestamp fixes are available while avoiding known ADK 2.8+ compatibility risk. -- Keep VeADK's AgentKit requirement flexible at `agentkit-sdk-python>=0.8.5` so new installs can resolve to `0.8.9` without forcing an exact pin. -- Keep VeADK's direct OpenTelemetry exporter flexible at `opentelemetry-exporter-otlp>=1.37.0`. -- Update the main development `uv.lock` to the current compatible resolution. -- Preserve ADK 2.7 build-graph and Studio lazy GenAI behavior covered by regression tests. - -## Non-goals - -- Do not change Studio, sandbox, generated project, managed sidecar, or Docker dependency declarations in this change. -- Do not pin VeADK to `agentkit-sdk-python==0.8.9`. -- Do not support ADK `2.8.0` or later in this PR; that requires a separate compatibility pass for changed ADK internals. -- Do not publish packages, Docker images, sandbox images, or Studio assets. - -## Requirements - -- `FR-1`: Package metadata must use `google-adk>=2.7.0,<2.8.0` for the base dependency and `google-adk[eval]>=2.7.0,<2.8.0` for the eval extra. -- `FR-2`: Package metadata must keep `agentkit-sdk-python>=0.8.5`. -- `FR-3`: Package metadata must use `opentelemetry-exporter-otlp>=1.37.0`, not an exact pin. -- `FR-4`: The main `uv.lock` metadata must reflect the updated package metadata and resolve Google ADK to a compatible 2.7.x release. -- `FR-5`: Regression tests must cover package metadata and lock metadata. -- `FR-6`: ADK build-graph serialization must continue to expose VeADK nested `sub_agents` under ADK 2.7.x. -- `FR-7`: Studio startup tests must continue to verify lazy GenAI model loading under ADK 2.7.x telemetry APIs. - -## Design and contract impact - -Update the published package dependency metadata and the main development lock: - -- `google-adk>=2.7.0,<2.8.0` -- `agentkit-sdk-python>=0.8.5` -- `opentelemetry-exporter-otlp>=1.37.0` - -`google-adk[eval]` uses the same `>=2.7.0,<2.8.0` window. The `harness-sidecar` optional dependency keeps the same AgentKit lower bound. - -The `google-adk<2.8.0` upper bound is deliberate. It prevents fresh installs from selecting currently unverified ADK 2.8+ releases after the targeted suite reproduced ADK 2.11 incompatibility. - -ADK 2.7's graph serializer skips Pydantic fields marked `exclude=True`; VeADK's `Agent.sub_agents` uses that flag. The build-graph compatibility patch therefore adds `sub_agents` from the live VeADK agent only when ADK's serialized result omits them. - -ADK 2.7 no longer exposes `_experimental_semconv.transformers` in the Studio test path. The test now falls back to `google.genai.models.t` while preserving the assertion that Studio defers the real GenAI models module until first use. - -No persistent state, security boundary, or public Python API changes are introduced. - -## Implementation tasks - -| Task | Requirements | Affected files | -| --- | --- | --- | -| `T-1` | `FR-1`, `FR-2`, `FR-3` | `pyproject.toml` | -| `T-2` | `FR-4` | `uv.lock` | -| `T-3` | `FR-5` | `tests/test_runtime_dependency_compatibility.py` | -| `T-4` | `FR-6` | `veadk/utils/patches.py`, `tests/cli/test_frontend_trace.py` | -| `T-5` | `FR-7` | `tests/cli/test_studio_startup_imports.py` | -| `T-6` | all | PRD/spec reconciliation and verification record | - -## Verification and acceptance - -| Requirement | Acceptance criterion | Result/evidence | -| --- | --- | --- | -| `FR-1` | Package metadata uses `google-adk>=2.7.0,<2.8.0` and `google-adk[eval]>=2.7.0,<2.8.0` | pass | -| `FR-2` | `agentkit-sdk-python>=0.8.5` remains in package metadata | pass | -| `FR-3` | `opentelemetry-exporter-otlp>=1.37.0` remains flexible | pass | -| `FR-4` | `uv.lock` metadata reflects the updated package metadata and resolves ADK 2.7.x | pass: lock resolves `google-adk==2.7.1` | -| `FR-5` | targeted dependency metadata tests pass | pass | -| `FR-6` | nested VeADK agents appear in ADK build-graph JSON | pass | -| `FR-7` | Studio lazy GenAI startup tests pass under ADK 2.7.x | pass | - -## Risks and open questions - -- Users with existing lock files or constraints can still remain on older dependency selections until they update those files. -- ADK `2.8.0` and later are intentionally excluded until VeADK adapts to upstream internal API changes. -- Studio, sandbox, generated project, managed sidecar, and Docker paths still have their existing dependency declarations and should be handled separately if product owners decide to update those surfaces. - -## Review and approval - -- User approved upgrading ADK directly in this PR on 2026-10-13 and asked to add an upper bound if higher ADK versions are incompatible. -- Direct design review (`review-spec` unavailable): scope, dependency bounds, lock behavior, ADK 2.7 compatibility, ADK 2.11 failure evidence, and bilingual equivalence reviewed. - -## Delivery record - -### Verification (2026-10-13, working diff) - -| Check | Result | -| --- | --- | -| PyPI version check for `google-adk` | pass: latest stable observed as `2.11.0`; unrestricted `>=2.7.0` would select a newer line | -| `uv lock --upgrade-package google-adk` with unrestricted `>=2.7.0` | fail for compatibility scope: resolved `google-adk==2.11.0`; targeted suite failed on ADK internal changes | -| `uv lock --upgrade-package google-adk` with `>=2.7.0,<2.8.0` | pass: resolved `google-adk==2.7.1` | -| `uv run --extra dev pytest -q tests/test_runtime_dependency_compatibility.py tests/extensions/harness/test_sidecar_wheel.py::test_wheel_metadata_keeps_sidecar_optional_and_sdk_constraints_aligned tests/cli/test_frontend_sandbox.py::test_snapshot_metadata_survives_older_sdk_and_scopes_cards tests/runtime/piagent/test_piagent_runtime.py::test_piagent_runtime_emits_canonical_bridge_tool_events tests/runtime/piagent/test_piagent_runtime.py::test_piagent_runtime_exposes_before_model_added_tools tests/test_adk_sync_tool_thread_pool_patch.py::test_run_code_uses_agent_thread_pool_when_configured tests/tools/builtin_tools/create_agent/test_create_agent_toolset.py::test_runner_transfers_from_create_agents_to_dynamic_sub_agent tests/cli/test_frontend_trace.py::test_build_graph_serializes_nested_veadk_agent_models tests/cli/test_studio_startup_imports.py::test_studio_fast_api_defers_genai_models_until_first_request tests/cli/test_studio_startup_imports.py::test_studio_deferred_genai_models_support_openapi_first_use` | pass: 11 passed | -| `uv run --extra dev pytest -q -n 4 tests/runtime/piagent/test_piagent_runtime.py` | pass: 55 passed; verifies PiAgent fake invocation contexts now provide the ADK 2.7 `run_config` expected by tool execution | -| `uv run --extra dev pre-commit run --all-files` | pass | -| `git diff --check` | pass | -| `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` | fail: 6913 passed, 55 skipped, 4 xfailed, 16 failed. The failed run included stale assertions fixed by this change and Codex runtime tests missing the optional `openai_codex` package under `--extra dev`. The Codex failure subset passed with `--extra codex`. | -| `uv run --extra dev --extra codex pytest -q` for the 10 failed Codex runtime tests | pass: 10 passed | - -This change is complete for the main package dependency and ADK 2.7 compatibility scope. Studio, sandbox, generated project, managed sidecar, and Docker dependency declarations are deferred. diff --git a/prd-spec/bugfixes/runtime-dependency-compatibility/2026-10-12-google-adk-opentelemetry-baseline.zh.md b/prd-spec/bugfixes/runtime-dependency-compatibility/2026-10-12-google-adk-opentelemetry-baseline.zh.md deleted file mode 100644 index e51dcab0c..000000000 --- a/prd-spec/bugfixes/runtime-dependency-compatibility/2026-10-12-google-adk-opentelemetry-baseline.zh.md +++ /dev/null @@ -1,120 +0,0 @@ -# 对齐 Google ADK 与 OpenTelemetry 的包依赖元数据 - -[English](2026-10-12-google-adk-opentelemetry-baseline.md) - -- 变更 ID:`runtime-dependency-compatibility` -- 创建/修订日期:2026-10-13 -- 状态:implemented -- 相关组件规格:[Runtime dependency compatibility](../../../specs/runtime-dependency-compatibility/README.zh.md) -- 范围:主包依赖元数据、主开发 lock、元数据回归测试,以及最小 ADK 2.7 build-graph/Studio 兼容覆盖。Studio、sandbox、生成项目、托管 sidecar 和 Docker 依赖声明有意不在本次范围内。 - -## 背景与证据 - -两个上游 Google ADK commit 修复了 session event 时间戳确定性以及 UTC 存储/过滤问题: - -- `4ca975eb9acd30a64b2c8615a93d31dada63052c`:`fix(sessions): make event reads deterministic and timezone-correct` -- `1b8ed3d9c0b125852df1902dd32b65bcab63ce1d`:`fix(sessions): store and filter event timestamps in UTC` - -这些修复从 Google ADK `2.7.0` 开始可用。VeADK 本地 lock 此前解析到 Google ADK `2.2.0`。 - -已确认的 resolver 阻塞点是: - -- AgentKit SDK 旧的 OpenTelemetry 上限;该问题已通过发布 `agentkit-sdk-python==0.8.9` 修复。 -- VeADK 直接声明的 `opentelemetry-exporter-otlp==1.37.0` 精确 pin。 - -2026-10-13 的全新 resolver 证据显示,不加上限的 `google-adk>=2.7.0` 会选择 `google-adk==2.11.0`。目标兼容测试在该版本线上失败,因为 ADK 2.11 移除或改变了 VeADK 仍在 patch 的内部入口,包括 `google.adk.flows.llm_flows.functions.__call_tool_async`;piagent 工具执行还会在测试 invocation context 上遇到缺失 `run_config` 的问题。因此本 PR 将当前支持窗口定义为 `google-adk>=2.7.0,<2.8.0`。 - -## 目标 - -- 将 VeADK 的 Google ADK 需求提升到 `google-adk>=2.7.0,<2.8.0`,确保包含 UTC session 时间戳修复,同时避开已知 ADK 2.8+ 兼容风险。 -- 保持 VeADK 的 AgentKit 需求为灵活的 `agentkit-sdk-python>=0.8.5`,使全新安装可以解析到 `0.8.9`,但不做精确 pin。 -- 保持 VeADK 直接的 OpenTelemetry exporter 依赖为灵活的 `opentelemetry-exporter-otlp>=1.37.0`。 -- 将主开发 `uv.lock` 更新到当前兼容解析。 -- 保持回归测试覆盖的 ADK 2.7 build-graph 与 Studio lazy GenAI 行为。 - -## 非目标 - -- 不在本次变更中修改 Studio、sandbox、生成项目、托管 sidecar 或 Docker 依赖声明。 -- 不把 VeADK pin 到 `agentkit-sdk-python==0.8.9`。 -- 不在本 PR 支持 ADK `2.8.0` 或更高版本;这需要单独适配已变化的 ADK 内部 API。 -- 不发布包、Docker 镜像、sandbox 镜像或 Studio 资产。 - -## 需求 - -- `FR-1`:基础依赖必须使用 `google-adk>=2.7.0,<2.8.0`,eval extra 必须使用 `google-adk[eval]>=2.7.0,<2.8.0`。 -- `FR-2`:包元数据必须保持 `agentkit-sdk-python>=0.8.5`。 -- `FR-3`:包元数据必须使用 `opentelemetry-exporter-otlp>=1.37.0`,而不是精确 pin。 -- `FR-4`:主 `uv.lock` 元数据必须反映更新后的包元数据,并将 Google ADK 解析到兼容的 2.7.x 版本。 -- `FR-5`:回归测试必须覆盖包元数据和 lock 元数据。 -- `FR-6`:ADK build-graph 序列化在 ADK 2.7.x 下必须继续暴露 VeADK 嵌套 `sub_agents`。 -- `FR-7`:Studio 启动测试必须继续验证 ADK 2.7.x telemetry API 下的 lazy GenAI model 加载。 - -## 设计与契约影响 - -更新发布包依赖元数据和主开发 lock: - -- `google-adk>=2.7.0,<2.8.0` -- `agentkit-sdk-python>=0.8.5` -- `opentelemetry-exporter-otlp>=1.37.0` - -`google-adk[eval]` 使用同样的 `>=2.7.0,<2.8.0` 窗口。`harness-sidecar` optional dependency 保持相同的 AgentKit 下限。 - -`google-adk<2.8.0` 上限是有意设置的。目标测试已复现 ADK 2.11 不兼容,因此该上限避免全新安装选择当前未验证的 ADK 2.8+ 版本。 - -ADK 2.7 的 graph serializer 会跳过 Pydantic 中标记 `exclude=True` 的字段;VeADK 的 `Agent.sub_agents` 使用了该标记。因此 build-graph 兼容 patch 只在 ADK 原生序列化结果缺失 `sub_agents` 时,从运行中的 VeADK agent 补回子 Agent。 - -ADK 2.7 在 Studio 测试路径中不再暴露 `_experimental_semconv.transformers`。测试现在 fallback 到 `google.genai.models.t`,同时保留“Studio 在首次真正使用前延迟加载真实 GenAI models 模块”的断言。 - -本次变更不引入持久化状态、安全边界或公开 Python API 变化。 - -## 实施任务 - -| 任务 | 需求 | 影响文件 | -| --- | --- | --- | -| `T-1` | `FR-1`, `FR-2`, `FR-3` | `pyproject.toml` | -| `T-2` | `FR-4` | `uv.lock` | -| `T-3` | `FR-5` | `tests/test_runtime_dependency_compatibility.py` | -| `T-4` | `FR-6` | `veadk/utils/patches.py`, `tests/cli/test_frontend_trace.py` | -| `T-5` | `FR-7` | `tests/cli/test_studio_startup_imports.py` | -| `T-6` | all | PRD/spec 对齐和验证记录 | - -## 验证与验收 - -| 需求 | 验收标准 | 结果/证据 | -| --- | --- | --- | -| `FR-1` | 包元数据使用 `google-adk>=2.7.0,<2.8.0` 和 `google-adk[eval]>=2.7.0,<2.8.0` | pass | -| `FR-2` | 包元数据保留 `agentkit-sdk-python>=0.8.5` | pass | -| `FR-3` | `opentelemetry-exporter-otlp>=1.37.0` 保持灵活 | pass | -| `FR-4` | `uv.lock` 元数据反映更新后的包元数据并解析 ADK 2.7.x | pass:lock 解析为 `google-adk==2.7.1` | -| `FR-5` | 目标依赖元数据测试通过 | pass | -| `FR-6` | 嵌套 VeADK agents 出现在 ADK build-graph JSON 中 | pass | -| `FR-7` | Studio lazy GenAI 启动测试在 ADK 2.7.x 下通过 | pass | - -## 风险与开放问题 - -- 已有 lock 文件或 constraints 的用户仍可能停留在旧依赖选择,直到他们更新这些文件。 -- ADK `2.8.0` 及更高版本被有意排除,直到 VeADK 适配上游内部 API 变化。 -- Studio、sandbox、生成项目、托管 sidecar 和 Docker 路径仍保留现有依赖声明;如果产品负责人决定更新这些表面,应另行处理。 - -## 评审与批准 - -- 用户在 2026-10-13 明确批准在此 PR 中直接升级 ADK,并要求如果更高 ADK 版本存在不兼容就设置上限。 -- 直接设计评审(`review-spec` 不可用):已评审范围、依赖边界、lock 行为、ADK 2.7 兼容性、ADK 2.11 失败证据和中英文等价性。 - -## 交付记录 - -### 验证(2026-10-13,working diff) - -| 检查 | 结果 | -| --- | --- | -| `google-adk` PyPI 版本检查 | pass:观察到最新稳定版为 `2.11.0`;不设上限的 `>=2.7.0` 会选择更新版本线 | -| 在无限制 `>=2.7.0` 下运行 `uv lock --upgrade-package google-adk` | fail(针对兼容范围):解析为 `google-adk==2.11.0`;目标测试因 ADK 内部变化失败 | -| 在 `>=2.7.0,<2.8.0` 下运行 `uv lock --upgrade-package google-adk` | pass:解析为 `google-adk==2.7.1` | -| `uv run --extra dev pytest -q tests/test_runtime_dependency_compatibility.py tests/extensions/harness/test_sidecar_wheel.py::test_wheel_metadata_keeps_sidecar_optional_and_sdk_constraints_aligned tests/cli/test_frontend_sandbox.py::test_snapshot_metadata_survives_older_sdk_and_scopes_cards tests/runtime/piagent/test_piagent_runtime.py::test_piagent_runtime_emits_canonical_bridge_tool_events tests/runtime/piagent/test_piagent_runtime.py::test_piagent_runtime_exposes_before_model_added_tools tests/test_adk_sync_tool_thread_pool_patch.py::test_run_code_uses_agent_thread_pool_when_configured tests/tools/builtin_tools/create_agent/test_create_agent_toolset.py::test_runner_transfers_from_create_agents_to_dynamic_sub_agent tests/cli/test_frontend_trace.py::test_build_graph_serializes_nested_veadk_agent_models tests/cli/test_studio_startup_imports.py::test_studio_fast_api_defers_genai_models_until_first_request tests/cli/test_studio_startup_imports.py::test_studio_deferred_genai_models_support_openapi_first_use` | pass:11 passed | -| `uv run --extra dev pytest -q -n 4 tests/runtime/piagent/test_piagent_runtime.py` | pass:55 passed;验证 PiAgent fake invocation context 已提供 ADK 2.7 工具执行期望的 `run_config` | -| `uv run --extra dev pre-commit run --all-files` | pass | -| `git diff --check` | pass | -| `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` | fail:6913 passed,55 skipped,4 xfailed,16 failed。该失败运行包含已在本变更中修复的旧断言,以及在 `--extra dev` 下缺少 optional `openai_codex` 包的 Codex runtime 测试。Codex 失败子集在 `--extra codex` 下通过。 | -| 对 10 个失败的 Codex runtime 测试运行 `uv run --extra dev --extra codex pytest -q` | pass:10 passed | - -本次变更已完成主包依赖和 ADK 2.7 兼容范围。Studio、sandbox、生成项目、托管 sidecar 和 Docker 依赖声明延后处理。 diff --git a/prd-spec/bugfixes/runtime-disable-db-spans/2026-09-17-default-db-spans.md b/prd-spec/bugfixes/runtime-disable-db-spans/2026-09-17-default-db-spans.md deleted file mode 100644 index 2bce0c916..000000000 --- a/prd-spec/bugfixes/runtime-disable-db-spans/2026-09-17-default-db-spans.md +++ /dev/null @@ -1,9 +0,0 @@ -# Default database instrumentation exclusion - -User-approved scope: branch from upstream feat/mpa-agent-oneclick-provision and inject the database instrumentation setting when creating MPA Runtime. Native MPA code and existing deployments are unchanged. - -The shared build_runtime_env builder adds OTEL_PYTHON_DISABLED_INSTRUMENTATIONS=sqlalchemy,asyncpg,psycopg,psycopg2,dbapi. Existing extra_env merge remains last-wins, including empty strings. Preview and deployment use the same builder; phase-two environment updates retain the value. No dependencies or authentication changes. Business tracing remains enabled. - -Self-review: additive default, explicit override preserved, no secrets or live deployment. Update the MPA provisioning contract in both languages. Regression: verify the default and custom/empty override, run environment, CLI and Runtime tests, measure incremental coverage, then commit/push to the user's fork. - -Verification: 34 environment/Runtime/CLI tests passed; modified environment builder coverage 17/17 executable lines (100%). The additive dictionary entry is verified explicitly. Pre-commit (Ruff check/format and secret scan) and Pyright passed. No cloud resource was changed. diff --git a/prd-spec/bugfixes/runtime-disable-db-spans/2026-09-17-default-db-spans.zh.md b/prd-spec/bugfixes/runtime-disable-db-spans/2026-09-17-default-db-spans.zh.md deleted file mode 100644 index 7d34873eb..000000000 --- a/prd-spec/bugfixes/runtime-disable-db-spans/2026-09-17-default-db-spans.zh.md +++ /dev/null @@ -1,9 +0,0 @@ -# 默认关闭数据库自动埋点 - -用户已授权:从上游 feat/mpa-agent-oneclick-provision 新建分支,在创建 MPA Runtime 时注入数据库埋点配置。不改原生 MPA 代码和已有部署。 - -共享 build_runtime_env 增加 OTEL_PYTHON_DISABLED_INSTRUMENTATIONS=sqlalchemy,asyncpg,psycopg,psycopg2,dbapi。保留 extra_env 最后覆盖语义,包括空字符串。预览和部署使用同一构建函数,第二阶段环境更新保留该值。不增加依赖,不修改鉴权,业务 tracing 保持启用。 - -已自审:新增默认值,保留显式覆盖,不包含凭证或线上部署。同步中英文 MPA 部署契约。回归验证默认值及自定义/空值覆盖,运行环境、CLI 和 Runtime 测试,统计增量覆盖率后提交并推送用户仓库。 - -验证:34 项环境、Runtime、CLI 测试通过;修改的环境构建函数覆盖 17/17 可执行行(100%),新增字典默认值已显式断言。pre-commit(Ruff 检查、格式、密钥扫描)和 Pyright 通过。未修改云资源。 diff --git a/prd-spec/bugfixes/skill-shell-tool-naming/2026-10-20-skill-shell-tool-naming.md b/prd-spec/bugfixes/skill-shell-tool-naming/2026-10-20-skill-shell-tool-naming.md deleted file mode 100644 index f75393376..000000000 --- a/prd-spec/bugfixes/skill-shell-tool-naming/2026-10-20-skill-shell-tool-naming.md +++ /dev/null @@ -1,37 +0,0 @@ -# Skill shell tool naming - -[中文](2026-10-20-skill-shell-tool-naming.zh.md) - -- Change: skill-shell-tool-naming; date: 2026-10-20; status: approved. -- Scope approval: the user requested this fix after reviewing canonical naming, optional aliases, bounded Sandbox recovery, and error detail preservation. Local commits are authorized by the user; push, SDK publication and deployment remain outside scope. - -## Evidence and goals - -`SkillsToolset` publishes `FunctionTool(bash_tool)`, whose declared name is `bash_tool`. `SkillsTool` describes execution as "use the bash tool" both in its declaration and its loaded-skill footer. A remote run emitted several valid `bash_tool` calls, then an unknown `bash` call and failed. Wording is a verified ambiguity, not proof of a unique model-level cause or a regression from SDK 1.0.10. - -FR-1: SDK-generated execution instructions must name the actual callable, `bash_tool`, in both the tool description and loaded-skill footer. Preserve user-authored Skill content verbatim. -FR-2: Keep tool names, schemas, execution, imports and signatures compatible. Do not introduce a `bash` alias or rename an existing tool. - -## Design and contracts - -Read the canonical function name from the existing shell callable rather than maintaining another spelling. Update only SDK-owned instruction text. Existing SkillsToolset discovery, registry, installation, working directory, environment, file handling, authorization and execution contracts remain unchanged; no component-spec change is required because this corrects descriptive wording to match the existing interface. - -Final scope: the user excluded `actb-mono` because releasing that repository is costly. All Sandbox callback code, tests and changelog changes have been withdrawn. Keep only SDK naming corrections and bounded, redacted error details in the samples bridge. Unknown tool calls can still fail; no automatic recovery is provided. This SDK change does not automatically update a deployed Sandbox image. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | Verification | Result | -| --- | --- | --- | --- | --- | -| FR-1 | T-1: add regression tests | AC-1: instruction names equal the real FunctionTool declaration | `uv run --extra dev pytest tests/skills/test_skill_tool_names.py` | passed | -| FR-1 | T-2: fix generated strings | AC-2: loaded Skill body remains intact, footer uses canonical name | same tests | passed | -| FR-2 | T-3: regression and review | AC-3: existing Skill installation/loading tests pass; tool declarations unchanged | targeted skills tests; Ruff; diff whitespace | passed | - -## Review, risks and delivery - -Direct design review completed: naming and evidence agree with current code; scope preserves interfaces and privileges; tests compare instructions to a real declaration. No review-spec skill is available, so the equivalent feasibility, compatibility, failure-boundary, testability and bilingual checks were performed directly. No unresolved design blockers. User approval is recorded above. - -Skill-authored references to generic "bash" are outside SDK-generated wording. Probabilistic model output may still use an unknown tool name; automatic recovery is outside the approved scope. Image rebuild/release is a separate deployment action. Cloud provisioning timeouts are outside this change. - -## Verification results - -New regression tests failed before the fix and passed afterward. Naming, installation, callback and ADK toolset suites: 30 passed. Ruff E/F/I checks and diff whitespace checks passed. No SDK release; deployed Sandboxes require a later image build using the updated SDK. diff --git a/prd-spec/bugfixes/skill-shell-tool-naming/2026-10-20-skill-shell-tool-naming.zh.md b/prd-spec/bugfixes/skill-shell-tool-naming/2026-10-20-skill-shell-tool-naming.zh.md deleted file mode 100644 index d155b1c4a..000000000 --- a/prd-spec/bugfixes/skill-shell-tool-naming/2026-10-20-skill-shell-tool-naming.zh.md +++ /dev/null @@ -1,37 +0,0 @@ -# Skill Shell 工具命名 - -[English](2026-10-20-skill-shell-tool-naming.md) - -- 变更:skill-shell-tool-naming;日期:2026-10-20;状态:approved。 -- 范围批准:用户在讨论规范命名、可选别名、Sandbox 有限纠错和错误详情保留后要求修复。用户已授权本地提交;不包含推送、SDK 发布或部署。 - -## 证据与目标 - -`SkillsToolset` 发布 `FunctionTool(bash_tool)`,声明名称为 `bash_tool`。`SkillsTool` 的工具说明和加载技能后的 footer 都写着 "use the bash tool"。一次远端运行先正确调用多次 `bash_tool`,随后调用不存在的 `bash` 并失败。说明文案的歧义已经确认,但不能证明它是模型误调用的唯一原因,也不能证明这是相对 SDK 1.0.10 的回归。 - -FR-1:SDK 生成的执行指引必须在工具说明与加载技能后的 footer 中使用真实可调用名称 `bash_tool`。原样保留技能作者的正文。 -FR-2:保留工具名称、schema、执行、导入路径和函数签名的兼容性。不新增 `bash` 别名,不重命名现有工具。 - -## 设计与契约 - -从已有 Shell 函数取得规范名称,不再额外维护一个拼写。仅修改 SDK 自己生成的说明。SkillsToolset 的发现、注册表、安装、工作目录、环境、文件处理、授权与执行契约不变;本次只是让描述与既有接口一致,不需要修改组件契约。 - -最终范围:用户因发布成本明确排除 `actb-mono`,已撤回该库的 Sandbox 回调代码、测试及变更记录。仅保留 SDK 命名修复和 samples 桥接层有界、脱敏的错误详情。模型误用不存在的工具名仍可能失败,不提供自动纠错。SDK 源码变更不会自动更新已部署的 Sandbox 镜像。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | 验证 | 结果 | -| --- | --- | --- | --- | --- | -| FR-1 | T-1:增加回归测试 | AC-1:说明中的工具名称等于实际 FunctionTool 声明 | `uv run --extra dev pytest tests/skills/test_skill_tool_names.py` | passed | -| FR-1 | T-2:修正文案 | AC-2:加载的 Skill 正文不变,footer 使用规范名称 | 同上测试 | passed | -| FR-2 | T-3:回归与评审 | AC-3:既有 Skill 安装与加载测试通过,工具声明不变 | 相关 skills 测试;Ruff;diff 空白检查 | passed | - -## 评审、风险与交付 - -已直接完成设计评审:命名与证据符合当前代码;范围保留接口与权限;测试对照真实工具声明。当前没有 review-spec 技能,因此直接检查了可行性、兼容性、失败边界、可测试性与双语一致性。没有未解决的设计阻碍。用户批准记录见上文。 - -技能作者正文中的泛称 "bash" 不属于 SDK 生成文案。模型输出具有概率性,仍可能误用工具名;自动纠错不在最终批准范围内。镜像构建、发布属于独立部署动作。云端创建会话超时不在本次范围内。 - -## 验证结果 - -新增测试先失败后修复通过;工具命名、安装、回调及 ADK toolset 相关测试共 30 项通过。Ruff 的 E/F/I 检查和 diff 空白检查通过。未发布 SDK;已部署 Sandbox 需后续使用新版 SDK 构建镜像。 diff --git a/prd-spec/bugfixes/studio-agent-category-isolation/2026-10-12-general-deployment-isolation.md b/prd-spec/bugfixes/studio-agent-category-isolation/2026-10-12-general-deployment-isolation.md deleted file mode 100644 index 49a12fb1f..000000000 --- a/prd-spec/bugfixes/studio-agent-category-isolation/2026-10-12-general-deployment-isolation.md +++ /dev/null @@ -1,45 +0,0 @@ -# General deployment and MPA isolation - -## Background and scope - -At `812faadf`, ProjectPreview treats every AgentDraft as MPA, CustomCreate sends `agentCategory: mpa` and applies Profile after every structured deployment, and MyAgents includes every draft in both category lists. This can mislabel generic Runtime resources and invoke unsupported MPA APIs. Restore the pre-MPA generic build/deploy/publish flow while retaining existing scaling, evaluation and GitHub options. Preserve dedicated MPA creation and explicit MPA editing. - -## Requirements, design and acceptance - -- R1: New structured/YAML creation defaults to general. Draft presence, names, model, skills, delegation and quick/traditional surface never imply MPA. -- R2: Existing targets persist `agentCategory` from the selected Runtime or explicit MPA entry. Explicit category wins; legacy `mpaProfileOnly: true` remains MPA, otherwise default to general. An instance ID alone is insufficient. -- R3: Pass category into ProjectPreview and gate both CustomCreate deployment paths. Only MPA executes Profile/smoke and reports those phases in initial, success, error and retry task state. Preserve ordinary deployment, cancellation and errors. -- R4: Filter local drafts using the same rules before merging server-filtered Runtime results. Preserve ownership, region and search filtering, including after draft restoration. -- R5: MpaCreateDialog and managed creation APIs remain unchanged. Do not change chat or migrate existing cloud labels automatically. - -Generic deployment must send general, complete without Profile/smoke calls, and remain general after failure/retry. Explicit MPA must retain its lifecycle. Untyped old drafts belong to general; legacy Profile-only drafts belong to MPA. No unrelated cleanup or cloud resource mutation. - -## Files and tasks - -1. Add a category resolver and target persistence in agentDraftStorage/App. -2. Gate CustomCreate and ProjectPreview lifecycle, payload and task flags. -3. Filter MyAgents drafts. -4. Write failing category/storage and deployment regressions before implementation; correct the old test requiring every draft to be MPA. -5. Run frontend tests, build, i18n, asset and isolated browser checks; synchronize release assets and record results. - -## Review and risks - -See the [component contract](../../../specs/studio-mpa-control-plane/README.md). Direct design review on 2026-10-12 covers boundaries, errors/retry, security, compatibility, testability and bilingual equivalence; review-spec is unavailable. User approved implementation on 2026-10-12, requiring the original generic flow and additive MPA. Reuse existing UI with no visual redesign. No backend API changes. - -Previously mislabeled Runtime resources remain mislabeled until separately verified and corrected. Old identifiers cannot reliably recover intent; untyped drafts default to general. Both preview and quick-deploy paths must use identical category rules. Preserve unrelated group permission edits and package-lock changes. - -## Verification - -Scope: this fix on `812faadf`, 2026-10-12. Cloud deployment: not_run (no mutation authorized). Python checks: not_applicable (frontend scope). Commit gates: not_run (no commit requested). - -### Results and implementation review - -- pass: regression-first checks reproduced generic tasks being marked MPA, including failed deployments. The focused category/storage/source suite passed 24 tests after the fix. -- pass: `npm --prefix frontend test`: 1,379 Node tests and 74 component tests; includes real React handlers for ordinary preview deployment, quick creation, update with a legacy instance ID, retry, abort, ambiguous status, explicit MPA Profile failure, list switching and dedicated MPA creation regressions. -- pass: `npm --prefix frontend run build` (TypeScript and both bundles), `npm --prefix frontend run check:i18n`, `npm --prefix frontend run test:webui-assets`, and `git diff --check`. Release assets include the pre-existing group permission changes. Existing Vite warnings are not new failures. -- pass: isolated browser fixture using actual ProjectPreview/MyAgents components and simulated APIs: generic successful deployment has no MPA steps; failure/retry remains general; Escape cancels confirmation; category switching separates drafts; Chinese search and empty results work at 390×844. No cloud credentials or resource mutations used. Temporary fixture/server removed. -- not_run: live cloud deployment, browser slow-network timing and native IME composition; no cloud operation authorized, and input/loading implementation was not changed. Abort/ambiguous-status behavior is covered by component tests. -- not_applicable: Python, harness-sidecar coverage and documentation-site gates; their implementations/contracts did not change. -- not_run: pre-commit/rebase/commit/push; no commit requested. - -Final review: category is carried from matching Runtime ID/region, preserved with deployment targets and used in preview, quick creation, task recovery and list filtering. Existing MPA creation APIs remain untouched. No blocking findings in the reviewed diff; existing server mislabels require separate remediation. All implementation tasks completed within the agreed frontend scope. diff --git a/prd-spec/bugfixes/studio-agent-category-isolation/2026-10-12-general-deployment-isolation.zh.md b/prd-spec/bugfixes/studio-agent-category-isolation/2026-10-12-general-deployment-isolation.zh.md deleted file mode 100644 index 320859691..000000000 --- a/prd-spec/bugfixes/studio-agent-category-isolation/2026-10-12-general-deployment-isolation.zh.md +++ /dev/null @@ -1,45 +0,0 @@ -# 通用部署与 MPA 隔离 - -## 背景和范围 - -在 `812faadf` 中,ProjectPreview 把所有 AgentDraft 当成 MPA,CustomCreate 固定提交 `agentCategory: mpa` 并在所有结构化部署后应用 Profile,MyAgents 在两个类型列表都加入全部草稿。这会误标通用 Runtime,并调用不支持的 MPA 接口。恢复引入 MPA 前的通用构建、部署、发布流程,保留现有实例配置、评测和 GitHub 选项。保留独立 MPA 创建与明确的 MPA 编辑。 - -## 需求、设计和验收 - -- R1:新的结构化/YAML 创建默认 general。存在草稿、名称、模型、技能、委派和快捷/传统界面都不能推断 MPA。 -- R2:已有目标从选中 Runtime 或明确 MPA 入口保存 `agentCategory`。明确类型优先;旧 `mpaProfileOnly: true` 仍为 MPA,否则默认 general。只有实例 ID 不足以判断。 -- R3:向 ProjectPreview 传递类型,并限制 CustomCreate 两条部署路径。仅 MPA 执行 Profile/smoke,并在初始、成功、失败和重试任务状态报告这些阶段。保留通用部署、取消和错误行为。 -- R4:合并服务端已筛选 Runtime 前,按同样规则筛选本地草稿。保留归属、地域和搜索筛选,包括恢复草稿后。 -- R5:MpaCreateDialog 与托管创建 API 不变。不修改聊天,不自动迁移已有云端标签。 - -通用部署须发送 general,不调用 Profile/smoke 即可完成,失败和重试后仍为 general。明确 MPA 保留对应流程。无类型旧草稿归通用;旧 Profile-only 草稿归 MPA。不做无关清理或云资源变更。 - -## 文件和任务 - -1. 增加类型解析函数及 agentDraftStorage/App 目标持久化。 -2. 限制 CustomCreate 和 ProjectPreview 流程、请求及任务标记。 -3. 筛选 MyAgents 草稿。 -4. 实现前先编写失败的类型/存储和部署回归测试;纠正要求所有草稿为 MPA 的旧测试。 -5. 执行前端测试、构建、国际化、产物和隔离浏览器检查;同步发布产物并记录结果。 - -## 评审和风险 - -参见[组件契约](../../../specs/studio-mpa-control-plane/README.zh.md)。2026-10-12 直接设计评审覆盖边界、错误/重试、安全、兼容性、可测试性和双语等价性;review-spec 不可用。用户于 2026-10-12 同意实施,要求保持原通用流程,MPA 作为额外流程。复用现有 UI,不做视觉重设计。后端 API 不变。 - -已有误标 Runtime 在单独核实修正前仍误标。旧标识无法可靠还原意图,无类型草稿默认通用。预览与快捷部署两条路径必须使用相同类型规则。保留无关群权限修改及 package-lock 修改。 - -## 验证 - -范围:`812faadf` 上本次修复,2026-10-12。云部署:not_run(未授权变更)。Python 检查:not_applicable(前端范围)。提交检查:not_run(未要求提交)。 - -### 结果和实现评审 - -- pass:先写回归测试复现通用任务被标为 MPA,包括失败部署。修复后类型、存储和源代码契约的定向用例通过 24 项。 -- pass:`npm --prefix frontend test`:1,379 项 Node 测试和 74 项组件测试;覆盖真实 React 处理函数的普通预览部署、快捷创建、带旧实例 ID 的更新、重试、取消、状态不确定、明确 MPA Profile 失败、列表切换及独立 MPA 创建回归。 -- pass:`npm --prefix frontend run build`(TypeScript 和两套产物)、`npm --prefix frontend run check:i18n`、`npm --prefix frontend run test:webui-assets` 和 `git diff --check`。发布产物包含既有群权限修改。现有 Vite 警告不是新增失败。 -- pass:使用真实 ProjectPreview/MyAgents 组件、模拟 API 的隔离浏览器验证:通用成功部署不含 MPA 阶段,失败/重试仍为通用,Escape 取消确认,类型切换隔离草稿,390×844 下中文搜索和空结果正常。未使用云凭据或变更云资源。临时测试页和服务已移除。 -- not_run:真实云部署、浏览器慢网络时序及原生输入法组合输入;未授权云操作,且未修改输入/加载实现。取消及状态不确定行为由组件测试覆盖。 -- not_applicable:Python、harness-sidecar 覆盖率和文档站门禁;其实现/契约没有变化。 -- not_run:pre-commit、rebase、提交、推送;未要求提交。 - -最终评审:按 Runtime ID/地域匹配类型,随部署目标保存,并用于预览、快捷创建、任务恢复和列表筛选。独立 MPA 创建 API 未改动。已评审差异无阻塞项;已有云端误标需单独修复。约定前端范围内的实现任务均已完成。 diff --git a/prd-spec/bugfixes/studio-byteplus-tag-endpoint/2026-10-15-byteplus-tag-endpoint.md b/prd-spec/bugfixes/studio-byteplus-tag-endpoint/2026-10-15-byteplus-tag-endpoint.md deleted file mode 100644 index d33b7bac0..000000000 --- a/prd-spec/bugfixes/studio-byteplus-tag-endpoint/2026-10-15-byteplus-tag-endpoint.md +++ /dev/null @@ -1,137 +0,0 @@ -# BytePlus Runtime Tag Endpoint Fix - -- Change ID: `studio-byteplus-tag-endpoint` -- Status: `implemented` -- Created: 2026-10-15 -- Revised: 2026-10-15 -- Chinese: [2026-10-15-byteplus-tag-endpoint.zh.md](2026-10-15-byteplus-tag-endpoint.zh.md) -- Component: [Studio MPA control plane](../../../specs/studio-mpa-control-plane/README.md) - -## Background and evidence - -A source deployment of Studio to BytePlus `ap-southeast-1` returns HTTP 502 from -`GET /web/runtimes`. The nested Tag `GetResources` response is HTTP 400 -`InvalidEndpoint`. Runtime SDK clients already select a BytePlus host, but the -generated Tag SDK clients leave `Configuration.host` empty. The SDK then falls -back to the Volcengine endpoint for this region. - -The same read-only `GetResources` request succeeds when sent to -`open.byteplusapi.com`, confirming that the request shape, credentials, and IAM -permissions are valid. - -## Goals and non-goals - -### Goals - -- Send BytePlus Runtime Tag reads to `open.byteplusapi.com`. -- Preserve the existing Volcengine endpoint behavior. -- Add a regression test that inspects the generated SDK configuration used by - the Runtime listing route. -- Preserve existing fail-closed Tag error handling. - -### Non-goals - -- Changing Runtime list schemas, filters, authorization, or pagination. -- Adding endpoint input controlled by the browser. -- Changing or recreating cloud Runtime resources. -- Modifying frontend UI behavior. - -## Scenarios and requirements - -- `FR-1`: Given a BytePlus Studio deployment, when `/web/runtimes` performs a - Tag-filtered lookup, the Tag SDK host must be - `https://open.byteplusapi.com`. -- `FR-2`: Given a Volcengine Studio deployment, existing generated-SDK endpoint - resolution must remain unchanged. -- `FR-3`: Tag failures must remain explicit errors and must not become empty - Runtime lists. - -## Design and contract impact - -Introduce a provider-specific Tag OpenAPI host helper. Configure each Runtime -Tag read client with the BytePlus host before constructing `TAGApi`; leave the -Volcengine configuration host unset so its current SDK resolution remains -unchanged. - -The public HTTP contract is unchanged. The maintained `CON-15`/`CON-31` -provider-boundary contract is clarified to require provider-correct Tag -endpoints. No state, persistence, concurrency, credential, permission, or -frontend type changes are introduced. Credentials remain server-side and the -browser cannot choose the endpoint. - -## Affected files and tasks - -- `T-1` (`FR-1`, `FR-2`): Add a failing BytePlus Runtime-list Tag host test. -- `T-2` (`FR-1`, `FR-2`): Add provider-specific Tag host selection and apply it - to Runtime Tag reads. -- `T-3` (`FR-3`): Update the bilingual component contract. -- `T-4`: Run targeted Python tests, Ruff, Pyright, frontend tests/build, and an - isolated live BytePlus read after redeployment. - -## Verification and acceptance - -| Requirement | Task | Acceptance criterion | Verification | Result | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1`, `T-2` | `AC-1`: BytePlus Tag SDK receives `https://open.byteplusapi.com` | Targeted pytest | `pass`: BytePlus host regression passed | -| `FR-2` | `T-1`, `T-2` | `AC-2`: Volcengine behavior is unchanged | Targeted pytest | `pass`: existing Runtime Tag suite passed | -| `FR-3` | `T-3` | `AC-3`: Existing fail-closed behavior remains covered | Targeted pytest | `pass`: Runtime proxy suite passed | -| `FR-1` | `T-4` | `AC-4`: Redeployed BytePlus `/web/runtimes` no longer fails with `InvalidEndpoint` | Live browser/API check | `blocked`: VeFaaS new-revision pod readiness timed out before traffic switched | - -Required commands: - -```bash -uv run --extra dev pytest tests/cli/test_frontend_runtime_proxy.py -uv run --extra dev ruff check veadk/utils/cloud_provider.py veadk/cli/cli_frontend.py tests/cli/test_frontend_runtime_proxy.py -uv run --extra dev pyright veadk/utils/cloud_provider.py veadk/cli/cli_frontend.py -npm --prefix frontend test -npm --prefix frontend run build -git diff --check -``` - -## Risks and recovery - -An incorrect host would keep Runtime discovery unavailable. The focused -configuration assertion and live read cover that risk. Rollback removes the -BytePlus host assignment; no data migration or cloud cleanup is required. - -## Review and delivery record - -Review on 2026-10-15 confirmed the failure is endpoint selection rather than -credentials, IAM, Runtime state, or request shape. The design preserves -Volcengine behavior and fail-closed errors. The user approved implementation in -the conversation. - -Implementation verification on 2026-10-15: - -- `pass`: `tests/cli/test_frontend_runtime_proxy.py`, 120 tests. -- `pass`: focused Ruff over the three changed Python files. -- `pass`: `npm --prefix frontend test`, 1389 Node tests and 116 Vitest tests. -- `pass`: `npm --prefix frontend run build`. -- `pass`: paired-document and `git diff --check` checks. -- `blocked`: Pyright reports 37 existing errors in `cli_frontend.py`; no error - references the new Tag host lines. -- `blocked`: the live BytePlus update created Application revision 2 and - Function revision 3, but VeFaaS failed the release with - `internal_system_error` / `check new revision's pod ready timeout`. Stable - traffic remained on the preceding revision, so `/web/runtimes` acceptance - could not exercise this fix. - - -### Post-rebase verification (2026-10-15) - -Scope: the Tag endpoint diff on remote branch revision `65cb4f50`. - -- `pass`: rebased onto `origin/feat/mpa-lifecycle-upstream-20261013` without conflicts. -- `pass`: `uv run --extra dev pre-commit run --all-files`, including Ruff, - secret scanning, dependency policy, and YAML scanning. -- `fail`: `uv run --extra dev pytest -q tests/cli/test_frontend_runtime_proxy.py tests/integrations/mpa_managed -n 2`: - 1268 passed, 3 failed. Two workers were used for this scoped regression run. - All three failures are existing `test_creation_images.py` assertions that - `tosBucket` is absent; the remote recovery change now preserves that field. - Running the three cases on clean `65cb4f50` reproduces the same failures. - This Tag fix does not modify those creation contracts or tests. -- `blocked`: Pyright over all three changed Python files reports 57 errors. - Baseline files from `65cb4f50` produce identical diagnostic messages and counts; - this diff introduces no additional diagnostic. -- `not_run`: frontend build/browser and cloud deployment were not repeated for - this backend-only submission; the earlier live deployment limitation remains. diff --git a/prd-spec/bugfixes/studio-byteplus-tag-endpoint/2026-10-15-byteplus-tag-endpoint.zh.md b/prd-spec/bugfixes/studio-byteplus-tag-endpoint/2026-10-15-byteplus-tag-endpoint.zh.md deleted file mode 100644 index 3a1f2b783..000000000 --- a/prd-spec/bugfixes/studio-byteplus-tag-endpoint/2026-10-15-byteplus-tag-endpoint.zh.md +++ /dev/null @@ -1,122 +0,0 @@ -# BytePlus Runtime 标签 Endpoint 修复 - -- Change ID:`studio-byteplus-tag-endpoint` -- 状态:`implemented` -- 创建日期:2026-10-15 -- 修订日期:2026-10-15 -- English:[2026-10-15-byteplus-tag-endpoint.md](2026-10-15-byteplus-tag-endpoint.md) -- 组件:[Studio MPA 控制面](../../../specs/studio-mpa-control-plane/README.zh.md) - -## 背景与证据 - -通过源码部署到 BytePlus `ap-southeast-1` 的 Studio 在调用 -`GET /web/runtimes` 时返回 HTTP 502。内层 Tag `GetResources` 响应为 -HTTP 400 `InvalidEndpoint`。Runtime SDK 客户端已经选择 BytePlus host, -但生成的 Tag SDK 客户端将 `Configuration.host` 留空,导致 SDK 在该地域 -回退到火山引擎 endpoint。 - -同一个只读 `GetResources` 请求显式发送到 `open.byteplusapi.com` 时成功, -证明请求结构、凭据和 IAM 权限均有效。 - -## 目标与非目标 - -### 目标 - -- 将 BytePlus Runtime 标签读取发送到 `open.byteplusapi.com`。 -- 保持现有火山引擎 endpoint 行为。 -- 增加回归测试,检查 Runtime 列表路由实际使用的生成 SDK 配置。 -- 保持既有 Tag 错误失败关闭行为。 - -### 非目标 - -- 修改 Runtime 列表 schema、过滤、授权或分页。 -- 增加由浏览器控制的 endpoint 输入。 -- 修改或重建云端 Runtime 资源。 -- 修改前端 UI 行为。 - -## 场景与需求 - -- `FR-1`:对于 BytePlus Studio 部署,当 `/web/runtimes` 执行标签过滤查询时, - Tag SDK host 必须为 `https://open.byteplusapi.com`。 -- `FR-2`:对于火山引擎 Studio 部署,既有生成 SDK endpoint 解析行为必须保持不变。 -- `FR-3`:Tag 失败必须继续表现为明确错误,不能转换为空 Runtime 列表。 - -## 设计与契约影响 - -新增 provider 专属 Tag OpenAPI host helper。在构造 `TAGApi` 前,为每个 Runtime -标签读取客户端配置 BytePlus host;火山引擎配置继续保持 host 为空,沿用当前 SDK -解析行为。 - -公开 HTTP 契约不变。维护契约 `CON-15`/`CON-31` 增补 provider 正确的 Tag -endpoint 要求。不引入状态、持久化、并发、凭据、权限或前端类型变化。凭据继续仅在 -服务端使用,浏览器不能选择 endpoint。 - -## 影响文件与任务 - -- `T-1`(`FR-1`、`FR-2`):增加先失败的 BytePlus Runtime 列表 Tag host 测试。 -- `T-2`(`FR-1`、`FR-2`):增加 provider 专属 Tag host 选择并应用到 Runtime - 标签读取。 -- `T-3`(`FR-3`):更新双语组件契约。 -- `T-4`:运行定向 Python 测试、Ruff、Pyright、前端测试/构建,并在重新部署后 - 执行隔离的 BytePlus 真实读取。 - -## 验证与验收 - -| 需求 | 任务 | 验收标准 | 验证 | 结果 | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-1`、`T-2` | `AC-1`:BytePlus Tag SDK 收到 `https://open.byteplusapi.com` | 定向 pytest | `pass`:BytePlus host 回归通过 | -| `FR-2` | `T-1`、`T-2` | `AC-2`:火山引擎行为不变 | 定向 pytest | `pass`:既有 Runtime Tag 测试通过 | -| `FR-3` | `T-3` | `AC-3`:既有失败关闭行为继续被覆盖 | 定向 pytest | `pass`:Runtime proxy 测试通过 | -| `FR-1` | `T-4` | `AC-4`:重新部署后的 BytePlus `/web/runtimes` 不再因 `InvalidEndpoint` 失败 | 真实浏览器/API 检查 | `blocked`:VeFaaS 新 revision Pod 就绪超时,流量切换前发布失败 | - -要求执行: - -```bash -uv run --extra dev pytest tests/cli/test_frontend_runtime_proxy.py -uv run --extra dev ruff check veadk/utils/cloud_provider.py veadk/cli/cli_frontend.py tests/cli/test_frontend_runtime_proxy.py -uv run --extra dev pyright veadk/utils/cloud_provider.py veadk/cli/cli_frontend.py -npm --prefix frontend test -npm --prefix frontend run build -git diff --check -``` - -## 风险与恢复 - -错误 host 会导致 Runtime 发现继续不可用。定向配置断言和真实读取覆盖该风险。回滚时 -移除 BytePlus host 赋值即可,不需要数据迁移或云资源清理。 - -## 评审与交付记录 - -2026-10-15 评审确认故障来自 endpoint 选择,而非凭据、IAM、Runtime 状态或请求 -结构。设计保持火山引擎行为和失败关闭错误。用户已在对话中批准实现。交付与验证证据 -如下: - -- `pass`:`tests/cli/test_frontend_runtime_proxy.py`,120 项。 -- `pass`:三个已改 Python 文件的定向 Ruff。 -- `pass`:`npm --prefix frontend test`,1389 项 Node 测试和 116 项 Vitest。 -- `pass`:`npm --prefix frontend run build`。 -- `pass`:双语文档配对和 `git diff --check`。 -- `blocked`:Pyright 在 `cli_frontend.py` 中报告 37 个既有错误;没有错误指向新增 - Tag host 行。 -- `blocked`:BytePlus 真实更新创建了 Application revision 2 和 Function - revision 3,但 VeFaaS 以 `internal_system_error` / - `check new revision's pod ready timeout` 终止发布。稳定流量仍停留在上一 revision, - 因此 `/web/runtimes` 验收未能覆盖本次修复。 - - -### Rebase 后验证(2026-10-15) - -范围:远端分支版本 `65cb4f50` 上的 Tag endpoint 改动。 - -- `pass`:无冲突 rebase 到 `origin/feat/mpa-lifecycle-upstream-20261013`。 -- `pass`:`uv run --extra dev pre-commit run --all-files`,包括 Ruff、 - 密钥扫描、依赖策略和 YAML 扫描。 -- `fail`:`uv run --extra dev pytest -q tests/cli/test_frontend_runtime_proxy.py tests/integrations/mpa_managed -n 2`: - 1268 项通过,3 项失败。本次范围内回归使用两个 worker。 - 三项均为既有 `test_creation_images.py` 中断言 `tosBucket` 不存在的测试; - 远端恢复逻辑现已保留该字段。在干净的 `65cb4f50` 上运行这三项可复现同样失败。 - 本次 Tag 修复不修改这些创建契约或测试。 -- `blocked`:三个改动 Python 文件的 Pyright 检查报告 57 条错误。 - `65cb4f50` 基线文件的诊断消息与数量完全相同,本次改动没有新增诊断。 -- `not_run`:本次仅后端提交,没有重复前端构建、浏览器检查或云端部署; - 之前真实部署验证的限制仍然存在。 diff --git a/prd-spec/bugfixes/studio-ci-lock-tests/2026-10-15-lock-and-tos-tests.md b/prd-spec/bugfixes/studio-ci-lock-tests/2026-10-15-lock-and-tos-tests.md deleted file mode 100644 index 4d1da3519..000000000 --- a/prd-spec/bugfixes/studio-ci-lock-tests/2026-10-15-lock-and-tos-tests.md +++ /dev/null @@ -1,53 +0,0 @@ -# Restore PR 1167 CI without changing product behavior - -[中文版](2026-10-15-lock-and-tos-tests.zh.md) - -Date: 2026-10-15. Base: `8ae26a6d`. Status: approved for implementation. - -## Background and scope - -PR 1167 frontend CI and release verification fail at npm ci because the lockfile lacks esbuild 0.28.2 and its platform packages for the nested Vite dependency. Python 3.12 reports three failures in test_creation_images.py: old assertions forbid persisting tosBucket, conflicting with the approved [local task recovery design](../mpa-creation-local-state/2026-10-15-local-task-recovery.md). Bucket names are nonsecret request identity; TOS credentials must remain excluded. - -## Requirements and design - -- R1: Repair only missing lockfile resolution entries. Keep package.json, all existing package versions and product source unchanged. Generate in a clean directory using CI-compatible Node/npm; verify a clean npm ci. -- R2: Update the three stale test cases to require the selected bucket in the task response/persistent payload and retain secret-exclusion checks. Read the stored JSON structurally, rather than searching raw SQLite bytes. Preserve existing request-change rejection and child TOS forwarding tests. -- R3: Run targeted failures first, then managed Python regression, npm test, build, sidecar coverage, and changed-file lint/secret checks. Do not weaken CI gates or remove tests. - -Affected files: frontend/package-lock.json; tests/integrations/mpa_managed/test_creation_images.py; this bilingual design. Component contract impact: none; this restores verification of the existing task recovery contract and completes development dependency metadata. No production runtime, public API, cloud resources or security policy changes. No deployment, publication, commit or push in scope. - -## Tasks and acceptance - -1. Reproduce existing test failures and lockfile installation failure in isolation. -2. Repair lock metadata and align assertions; preserve bucket-change conflict and secret rejection behavior. -3. Verify that existing locked packages retain versions, clean install succeeds, all affected tests and build pass, then inspect the final diff. - -Risks: npm can rewrite unrelated metadata, so compare all existing lock entries; builds may generate assets, so run in an isolated workspace and avoid unrelated asset churn. Linux CI must still rerun after publication; macOS validation is not Linux execution. UI/browser checks are not applicable to this metadata/test-only change. - -## Review and approval - -User approved the previously proposed scope with “帮我修一下,不要影响功能”. Direct design review (review-spec unavailable) checked contract alignment, security, minimal scope, dependency compatibility, tests and bilingual equivalence; no blockers. Existing failure evidence is the regression-first baseline; production behavior remains unchanged. - -## Verification - -Completed; detailed evidence follows. - - -## Verification record - -2026-10-15, base `8ae26a6d`, lockfile/test/documentation diff only: - -- pass: reproduced the exact npm ci missing-esbuild error under Node 22.17.0/npm 10.9.2 in a clean temporary directory and the three pytest failures (15 other cases passed). -- pass: `npm install --package-lock-only --ignore-scripts --no-audit --no-fund` in that directory adds exactly 27 lock entries. Structural comparison confirms no existing entries or versions changed. package.json and all production sources remain unchanged. -- pass: clean `npm ci --ignore-scripts --no-audit --no-fund` with Node 22.17.0/npm 10.9.2 on macOS installs 697 packages. -- pass: `uv run --extra dev pytest tests/integrations/mpa_managed -n 2 -q --tb=short` — 1151 passed on Python 3.10; two workers bound local resource use. -- pass: isolated Python 3.12 environment, `uv run --python 3.12 --extra dev pytest tests/integrations/mpa_managed/test_creation_images.py tests/integrations/mpa_managed/test_routes.py tests/integrations/mpa_managed/test_tasks.py -n 2 -q --tb=short` — 55 passed, including bucket-change rejection and persistence coverage. The repository environment was not replaced. -- pass: `npm test` in the isolated frontend workspace — 1389 Node tests and 118 Vitest tests passed. The first isolated attempt lacked supporting repository files and failed four test modules with ENOENT; copying the required tracked support files resolved the harness setup error without code changes. -- pass: `npm run test:harness-sidecar-coverage` — 22 tests; statements 98.55%, branches 94.44%, functions/lines 100%. -- pass: `npm run build` in the isolated workspace — TypeScript and both Vite builds succeeded; existing size/deprecation warnings remain. No generated repository assets are included in this change. -- pass: changed-file pre-commit Ruff check/format and secret scan; dependency/YAML hooks not_applicable; `git diff --check` and bilingual identifier/link review. -- fail (pre-existing): changed-file Pyright reports two diagnostics. The same command against the HEAD file reports identical rules/messages; no newly introduced diagnostics. No unrelated type cleanup. -- not_run: full-repository Python suite and GitHub rerun (no commit or push requested). Affected regression completed above. -- not_applicable: real browser/cloud smoke; no product UI/runtime behavior changed. - -Linux container validation: pass. Fresh `node:22.17.0-bookworm-slim` on Linux ARM64 (Node 22.17.0/npm 10.9.2), `npm ci --ignore-scripts --no-audit --no-fund`, `npm run test:harness-sidecar-coverage`, and `npm test` all exited successfully: 22 coverage tests, 1389 Node tests and 118 Vitest tests. GitHub Linux x64 rerun remains separate. No commit, push, deployment or cloud resource mutation performed. diff --git a/prd-spec/bugfixes/studio-ci-lock-tests/2026-10-15-lock-and-tos-tests.zh.md b/prd-spec/bugfixes/studio-ci-lock-tests/2026-10-15-lock-and-tos-tests.zh.md deleted file mode 100644 index 81b8a4b95..000000000 --- a/prd-spec/bugfixes/studio-ci-lock-tests/2026-10-15-lock-and-tos-tests.zh.md +++ /dev/null @@ -1,53 +0,0 @@ -# 修复 PR 1167 CI,保持产品行为不变 - -[English](2026-10-15-lock-and-tos-tests.md) - -日期:2026-10-15。基线:`8ae26a6d`。状态:已批准实施。 - -## 背景与范围 - -PR 1167 的前端 CI 和发布验证在 npm ci 阶段失败:锁文件缺少嵌套 Vite 依赖所需的 esbuild 0.28.2 及其平台包。Python 3.12 的 test_creation_images.py 有三个失败:旧断言禁止持久化 tosBucket,与已批准的[本地任务恢复设计](../mpa-creation-local-state/2026-10-15-local-task-recovery.zh.md)冲突。桶名是非密钥请求身份,TOS 凭据仍须排除。 - -## 需求与设计 - -- R1:仅修复锁文件缺失的解析条目。保持 package.json、已有包版本及产品源码不变。在干净目录使用与 CI 兼容的 Node/npm 生成,验证干净环境 npm ci。 -- R2:更新三个旧测试用例,要求任务响应/持久载荷保存所选桶,同时保留密钥排除检查。结构化读取持久 JSON,不再搜索 SQLite 原始字节。保留已有请求变更拒绝和子进程 TOS 传递测试。 -- R3:先复现定向失败,再运行 managed Python 回归、npm test、构建、sidecar 覆盖率及改动文件 lint/密钥检查。不降低 CI 门槛或删除测试。 - -涉及文件:frontend/package-lock.json;tests/integrations/mpa_managed/test_creation_images.py;本双语设计。组件契约影响:无;恢复现有任务恢复契约的验证并补全开发依赖元数据。不修改生产运行时、公共 API、云资源或安全策略。不包含部署、发布、提交或推送。 - -## 任务与验收 - -1. 在隔离环境复现现有测试失败和锁文件安装失败。 -2. 修复锁元数据并同步断言;保留修改桶名冲突和密钥拒绝行为。 -3. 验证已有锁定包版本不变、干净安装成功、受影响测试和构建通过,再审查最终差异。 - -风险:npm 可能改写无关元数据,需比较全部已有锁条目;构建可能生成资源,因此在隔离工作目录运行,避免无关产物变化。发布后仍需重新运行 Linux CI,macOS 验证不等于 Linux 实测。本次仅改元数据/测试,不适用 UI/浏览器检查。 - -## 评审与批准 - -用户回复“帮我修一下,不要影响功能”,批准前述方案。直接设计评审(review-spec 不可用)检查了契约一致性、安全、最小范围、依赖兼容性、测试与双语等价性,无阻断项。现有失败证据作为先行回归基线,生产行为不变。 - -## 验证 - -已完成,详细证据如下。 - - -## 验证记录 - -2026-10-15,基线 `8ae26a6d`,仅锁文件/测试/文档差异: - -- pass:在干净临时目录用 Node 22.17.0/npm 10.9.2 复现完全一致的 npm ci 缺少 esbuild 错误,并复现三个 pytest 失败(其余 15 项通过)。 -- pass:该目录执行 `npm install --package-lock-only --ignore-scripts --no-audit --no-fund`,恰好增加 27 个锁条目。结构化比较确认已有条目及版本均未改变,package.json 和全部生产源码保持不变。 -- pass:macOS 上使用 Node 22.17.0/npm 10.9.2 干净执行 `npm ci --ignore-scripts --no-audit --no-fund`,安装 697 个包。 -- pass:`uv run --extra dev pytest tests/integrations/mpa_managed -n 2 -q --tb=short` — Python 3.10 上 1151 项通过;使用两个 worker 限制本机资源占用。 -- pass:隔离 Python 3.12 环境执行 `uv run --python 3.12 --extra dev pytest tests/integrations/mpa_managed/test_creation_images.py tests/integrations/mpa_managed/test_routes.py tests/integrations/mpa_managed/test_tasks.py -n 2 -q --tb=short` — 55 项通过,覆盖修改桶名拒绝和持久化。没有替换仓库 Python 环境。 -- pass:隔离前端工作区执行 `npm test` — 1389 个 Node 测试及 118 个 Vitest 测试通过。首次隔离执行缺少仓库配套文件,四个测试模块因 ENOENT 失败;复制所需已跟踪配套文件后解决测试环境问题,没有改代码。 -- pass:`npm run test:harness-sidecar-coverage` — 22 项测试;语句覆盖率 98.55%、分支 94.44%、函数/行 100%。 -- pass:隔离工作区执行 `npm run build` — TypeScript 和两个 Vite 构建成功,保留既有体积/弃用警告。本次不包含仓库生成资源变更。 -- pass:改动文件 pre-commit 的 Ruff 检查/格式化和密钥扫描;依赖/YAML hook 为 not_applicable;`git diff --check` 及双语标识符/链接审查。 -- fail(既有问题):改动文件 Pyright 报告两个诊断,同命令检查 HEAD 文件得到完全相同的规则/消息,没有新增诊断,未清理无关类型问题。 -- not_run:全仓库 Python 套件及 GitHub 重跑(未要求提交或推送);已完成上述受影响回归。 -- not_applicable:真实浏览器/云端 smoke,没有产品 UI/运行行为变化。 - -Linux 容器验证:pass。在 Linux ARM64 上使用全新 `node:22.17.0-bookworm-slim`(Node 22.17.0/npm 10.9.2),`npm ci --ignore-scripts --no-audit --no-fund`、`npm run test:harness-sidecar-coverage`、`npm test` 均成功退出:22 个覆盖率测试、1389 个 Node 测试和 118 个 Vitest 测试。GitHub Linux x64 重跑仍是独立验证。未执行提交、推送、部署或云资源修改。 diff --git a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-final.md b/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-final.md deleted file mode 100644 index b1ced40de..000000000 --- a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-final.md +++ /dev/null @@ -1,56 +0,0 @@ -# MPA A2A final-answer ownership - -[中文](2026-09-22-mpa-a2a-final.zh.md) - -- Date: 2026-09-22 -- ID: `mpa-a2a-final` -- Status: implemented; local server activation pending -- Components: [Runtime diagnostics CON-8](../../../specs/studio-runtime-diagnostics/README.md), [Studio tool activity](../../../specs/studio-tool-activity/README.md) - -## Evidence and background - -Read-only inspection of the user's completed v7 A2A task found a sandbox artifact with 57 thought deltas, 14 answer deltas, one usage update, and one `invocation.completed` whose payload contains `finalMessage`. Concatenated answer deltas and finalMessage have identical hashes. A separate artifact holds the outer function response. The restored session exposes one final answer. No credentials, original task identifiers, or private event bodies are retained in fixtures. - -`runtime_a2a_stream._sandbox_event` ignores `finalMessage`; sandbox deltas carry invocationId while the outer result does not. The frontend therefore places these answer copies in different accumulators. Previous fixes for delegate-tool activity and native MPA session wrappers do not cover this A2A bridge. - -## Goals, non-goals, and requirements - -- **FR-1:** Project a nonempty sandbox `invocation.completed.payload.finalMessage` as the authoritative consolidated, completed answer, retaining the sandbox invocation identity. -- **FR-2:** Once that successful answer is projected, suppress later outer text/thought/result mirrors for the same known A2A task. Preserve sandbox tool events, errors and usage accounting. Do not compare arbitrary answer text or affect ordinary ADK/native session paths. -- **FR-3:** Project each identified sandbox source event at most once per task across streaming updates and task snapshots. Other tasks and legacy non-MPA A2A streams retain existing behavior. -- **FR-4:** A failed/cancelled invocation or an empty final must not claim answer ownership. A stream without a sandbox final keeps its existing fallback behavior. Finalizing an already consolidated sandbox answer must not synthesize another answer. - -Non-goals: authentication/deployment changes, fixing the separate transient discovery error, changing worker generation, hiding arbitrary reasoning, or modifying stored task data. - -## Design and affected files - -In `veadk/cli/runtime_a2a_stream.py`, add successful sandbox-final projection and decoder-local ownership keyed by A2A task ID. Track projected sandbox event IDs by task to handle replay without treating mutable outer artifact IDs as immutable events. Filter only outer projected text after ownership; metadata-only usage/status continues. State lives only for the request's decoder, so cancellation or disconnect discards it normally. The existing stream boundary and network timeouts do not change. - -Tests in `tests/cli/test_runtime_a2a_stream.py` model the observed sandbox delta/final plus outer result shape. A sanitized Python-to-TypeScript projection replay checks the actual frontend accumulators. Update the paired component spec; frontend release artifacts are required only if frontend production code changes. - -## Tasks, tests, and acceptance - -- **T-1 / AC-1:** Failing test proves sandbox final was missing and duplicate outer result survives; final result must have one authoritative answer. -- **T-2 / AC-2:** Cover late outer reasoning, same/different text mirrors, replayed sandbox final/task snapshot, task isolation, tool/usage preservation, empty finals, failure/cancellation, and legacy A2A fallback. -- **T-3 / AC-3:** Run targeted Python bridge/proxy tests, Ruff/Pyright on changed Python files, cross-layer/browser replay, and relevant frontend regression. Record actual gates and remaining live limitations. - -## Review and authorization - -The user explicitly asked to continue fixing the original duplicate after discovery recovered. This implements that existing scope using verified task evidence. Direct design review (review-spec unavailable) confirms no public request schema or cloud state change; the intended change is the A2A projection contract. Successful nonempty finals alone own the answer. Risk: an outer model's later reformulation is omitted intentionally after delegated completion. A task must emit its sandbox final before the outer mirror, as the inspected executor does. Missing IDs cannot safely establish cross-event ownership. - -## Verification - -- **pass:** Read-only task structure inspection; no new agent execution. -- **pass:** Before implementation, three new regression cases failed (32 existing/negative cases passed). -- **pass:** `uv run --extra dev pytest tests/cli/test_runtime_a2a_stream.py tests/cli/test_frontend_runtime_proxy.py -q`: 121 passed, five existing SDK deprecation warnings. -- **pass:** Ruff 0.11.12 check and format check plus Pyright on both changed Python files (zero errors/warnings). The repository environment lacks standalone Ruff/Pyright executables; `uv run --with ruff==0.11.12 ...` and `uv run --with pyright ...` supplied temporary tools without modifying project dependencies or global configuration. -- **pass:** Python-to-TypeScript replay using the observed protocol shape and placeholder text: HEAD bridge produces two answer blocks in two turns; fixed bridge produces one answer block in one turn. Real browser replay with production `eventsToTurns` and `Blocks` confirms that difference; no browser console errors. This reconstructed fixture is not a complete captured SSE trace. -- **pass:** Related frontend projector/activity regressions: 70 tests. `git diff --check`; paired documents and links reviewed. Temporary browser fixture and Vite server removed. -- **pass:** Repository pre-commit `gitleaks` hook for this follow-up's changed code/document files; no hardcoded secrets detected. -- **blocked:** A further full raw-task replay query returned intermittent HTTP 429/500. Earlier successful read-only task structure/hash evidence is recorded above; no live generation was triggered. -- **not_run:** Activation on the user's local Studio process requires restart. A2A task/session mappings are currently process-local; restart clears that local history index without deleting remote tasks. Await user agreement before this disruptive step. No cloud deployment, commit or push. -- **not_applicable:** Frontend build/IME/layout/network lifecycle changes: this follow-up changes the Python projection only; previous frontend assets remain intact. Broader unrelated Python suites are omitted because the affected bridge and Runtime proxy suites cover this change. - -Implementation review on 2026-09-22 confirms task-scoped successful-final ownership, replay identity isolation, legacy payload fallback and preservation of usage/tools. T-1–T-3 and AC-1–AC-3 pass for the recorded regression and replay scope; production activation remains pending. - -> The broad suppression rules are superseded by [preserving follow-up content](2026-09-22-preserve-followup-content.md). Historical verification below describes the earlier behavior. diff --git a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-final.zh.md b/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-final.zh.md deleted file mode 100644 index 4ad9ac339..000000000 --- a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-final.zh.md +++ /dev/null @@ -1,56 +0,0 @@ -# MPA A2A 最终回答所有权 - -[English](2026-09-22-mpa-a2a-final.md) - -- 日期:2026-09-22 -- ID:`mpa-a2a-final` -- 状态:已实现;本地服务生效待完成 -- 组件:[Runtime 诊断 CON-8](../../../specs/studio-runtime-diagnostics/README.zh.md)、[Studio 工具活动](../../../specs/studio-tool-activity/README.zh.md) - -## 证据与背景 - -只读检查用户已完成的 v7 A2A 任务,发现 sandbox artifact 包含 57 个推理增量、14 个答案增量、1 个用量更新,以及 1 个 payload 包含 `finalMessage` 的 `invocation.completed`。答案增量拼接后与 finalMessage 哈希相同,另一个 artifact 保存外层 function response。恢复的会话只有一份最终答案。测试数据不保留凭据、原任务标识或私有事件原文。 - -`runtime_a2a_stream._sandbox_event` 忽略了 `finalMessage`;沙箱增量携带 invocationId,外层结果没有,所以前端把两份答案放入不同累加器。前几次针对 delegate 工具活动、原生 MPA 会话包装响应的修复没有覆盖这条 A2A 桥接链路。 - -## 目标、非目标与需求 - -- **FR-1:** 把非空的 sandbox `invocation.completed.payload.finalMessage` 投影为权威合并完成答案,保留 sandbox invocation 标识。 -- **FR-2:** 成功答案投影后,抑制同一个已知 A2A 任务后续的外层文本/推理/结果副本,保留沙箱工具事件、错误和用量统计。不比较任意答案文本,不影响普通 ADK/原生会话路径。 -- **FR-3:** 同一任务中已标识的 sandbox 源事件,在流式更新和任务快照间最多投影一次。其他任务及非 MPA 的旧 A2A 流保持原有行为。 -- **FR-4:** 失败/取消的调用以及空 final 不得取得答案所有权。没有 sandbox final 的流保持原有回退行为。已合并的 sandbox 答案不得在流结束时再次合成答案。 - -非目标:鉴权/部署变更、修复独立的临时发现错误、改变 worker 生成、隐藏任意推理、修改已存任务数据。 - -## 设计与涉及文件 - -在 `veadk/cli/runtime_a2a_stream.py` 增加成功 sandbox final 投影,并按 A2A 任务 ID 在 decoder 内跟踪所有权。按任务跟踪已投影的 sandbox 事件 ID,处理重放,同时不把可追加的外层 artifact ID 视作不可变事件。仅在取得所有权后过滤外层投影文本;仅含 metadata 的用量/状态继续通过。状态仅存在于本次请求 decoder,取消或断连后正常销毁,不改变既有流边界和网络超时。 - -`tests/cli/test_runtime_a2a_stream.py` 按已观察到的沙箱增量/final 加外层结果结构建模。脱敏的 Python 到 TypeScript 投影回放检验真实前端累加器。更新双语组件契约;只有前端生产代码变更时才需要前端发布资产。 - -## 任务、测试与验收 - -- **T-1 / AC-1:** 先写失败用例,证明沙箱 final 缺失且外层重复结果仍存在;最终必须只有一份权威答案。 -- **T-2 / AC-2:** 覆盖迟到外层推理、相同/不同文本副本、sandbox final/任务快照重放、任务隔离、工具/用量保留、空 final、失败/取消和旧 A2A 回退。 -- **T-3 / AC-3:** 执行定向 Python 桥接/代理测试、修改 Python 文件的 Ruff/Pyright、跨层/浏览器回放及相关前端回归,记录真实门禁与剩余线上验证限制。 - -## 评审与授权 - -用户在发现接口恢复后明确要求继续修复原重复问题。本次基于已核验任务证据实现已有范围。直接设计评审(review-spec 不可用)确认不改变公共请求 schema 或云端状态,预期变更为 A2A 投影契约。仅成功非空 final 取得答案所有权。风险:委派结束后外层模型的改写将被有意省略。任务需要先发 sandbox final 再发外层副本,符合已检查 executor 实现;缺失标识时不能安全地建立跨事件所有权。 - -## 验证 - -- **pass:** 只读任务结构检查,没有新执行智能体。 -- **pass:** 实现前 3 个新增回归用例失败,32 个已有/负向用例通过。 -- **pass:** `uv run --extra dev pytest tests/cli/test_runtime_a2a_stream.py tests/cli/test_frontend_runtime_proxy.py -q`:121 项通过,有 5 条已有 SDK 弃用警告。 -- **pass:** 两个修改 Python 文件的 Ruff 0.11.12 检查、格式检查及 Pyright(零错误/警告)。仓库环境缺少独立 Ruff/Pyright 可执行文件,通过 `uv run --with ruff==0.11.12 ...` 和 `uv run --with pyright ...` 临时提供工具,没有修改项目依赖或全局配置。 -- **pass:** 使用已观察协议结构和占位文本的 Python 到 TypeScript 回放:HEAD 桥接产生两个 Turn、两个答案块,修复后一个 Turn、一个答案块。真实浏览器使用生产 `eventsToTurns` 和 `Blocks` 验证了该差异,控制台无错误。该构造数据并非完整 SSE 抓包。 -- **pass:** 相关前端投影/活动回归 70 项通过;`git diff --check`、双语文档与链接检查通过。临时浏览器页面及 Vite 服务已移除。 -- **pass:** 仓库 pre-commit `gitleaks` hook 检查本次跟进修改的代码/文档,未检测到硬编码密钥。 -- **blocked:** 进一步完整原任务回放查询遭遇间歇 HTTP 429/500,之前成功的只读任务结构/哈希证据见上文,没有触发新生成。 -- **not_run:** 用户本地 Studio 需要重启才能加载修复。A2A 任务/会话映射当前保存在进程内,重启会清空本地历史索引,但不会删除远端任务。在这一中断性操作前等待用户同意。未云部署、提交或推送。 -- **not_applicable:** 前端构建/IME/布局/网络生命周期变更:本次跟进仅修改 Python 投影,保留之前的前端构建资产。未跑无关 Python 全量套件,相关桥接和 Runtime 代理套件覆盖本次改动。 - -2026-09-22 实现评审确认成功 final 按任务取得所有权、重放标识隔离、旧 payload 回退以及用量/工具保留。T-1–T-3 与 AC-1–AC-3 在已记录回归和回放范围内通过,生产生效仍待完成。 - -> 宽泛过滤规则由[保留补充内容](2026-09-22-preserve-followup-content.zh.md)替代。下文历史验证描述的是此前行为。 diff --git a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-reasoning.md b/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-reasoning.md deleted file mode 100644 index fbca77616..000000000 --- a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-reasoning.md +++ /dev/null @@ -1,34 +0,0 @@ -# MPA A2A reasoning snapshots and empty-response notices - -[中文版](2026-09-22-mpa-a2a-reasoning.zh.md) - -## Metadata and evidence -- ID: `mpa-a2a-reasoning`; date: 2026-09-22; status: implemented. -- Contract: [Studio runtime diagnostics](../../../specs/studio-runtime-diagnostics/README.md), CON-10. - -The user reports one final answer, repeated outer reasoning and an empty-response notice preceding sandbox tools. Read-only task inspection confirms successful completion and a nonempty sandbox final. App.tsx displays the empty notice for each finalized reasoning-only turn independently. The A2A bridge marks consolidated outer reasoning with `projectionSource=a2a-artifact`; repeating such snapshots after the preview boundary is committed appends the same full thought again. Exact live SSE chronology is not captured; tests use the observed event contract, not raw production text. - -## Goals, scope and requirements -- FR-1: Enable the change only for a registered Runtime app `a2a-default` with MPA category/instance metadata. General ADK, general A2A, local agents and native MPA use existing behavior. -- FR-2: In the opted-in projector, a nonpartial, reasoning-only `a2a-artifact` snapshot matching or extending the adjacent prior reasoning block replaces that block. Preserve distinct thoughts, token deltas, sandbox reasoning, tools, attachments and errors. Never deduplicate arbitrary model text. -- FR-3: Only for MPA A2A, decide empty response across assistant fragments between two user messages. No notice while that request runs/presents, or when any fragment has visible content. A genuinely empty completed request retains one notice at its last nonempty fragment. Previous requests remain independent. General agents retain the old per-turn rule. -- FR-4: Live streams, history replay and auth continuation use the same explicit projector option, with state scoped to one request. No auth, API routing, cloud deployment or backend modification. - -## Design, tasks and acceptance -T-1 / AC-1: add failing projector tests for repeated/extended snapshots, live/history, two requests, tools and default/general behavior. T-2 / AC-2: add empty-notice tests for running, success, empty, cancelled, multiple user messages and general behavior. T-3 / AC-3: wire `frontend/src/adk/client.ts`, `blocks.ts`, `App.tsx` and a small pure transcript helper, run targeted/full frontend tests, build, assets and browser checks; update README and paired contract. Existing cancellation/errors remain unchanged and are not converted to successful empty output. - -## Review, authorization and risks -The user explicitly approved fixing these symptoms while preserving general-agent behavior. Direct review substitutes for unavailable review-spec: opt-in protocol/category gates, no default projection changes, no cross-request state, no credential changes, bilingual parity reviewed. No blockers. Risk: incomplete captured chronology; bounded snapshot reconciliation must not remove distinct reasoning or partial deltas. Existing persisted task restoration may contain final text only; missing history is not reconstructed. No visual design changes. - -## Verification -Verified on 2026-09-22 against the working diff based on `3bbd260a`, including existing uncommitted changes. T-1 through T-3 are complete. Direct implementation review confirmed opt-in gating at all five projection entry points, independent user-request boundaries, and preserved default behavior; bilingual requirements and contract are aligned. - -- `pass`: test-first baseline reproduced the duplicate snapshot (six failures before implementation, five from the missing empty-notice helper); focused regressions now pass as part of the full suite. -- `pass`: `npm --prefix frontend test` — 1,287 Node tests and 25 Vitest tests. -- `pass`: `npm --prefix frontend run build` — TypeScript and app/widget builds. Initial incorrect argument placement and ES2020-incompatible array access were corrected before the successful run; existing chunk-size warnings remain. -- `pass`: `npm --prefix frontend run test:webui-assets` — 104 packaged files and 248 references. -- `pass`: isolated browser fixture using the actual projector, notice helper and Blocks renderer — one MPA reasoning snapshot/final, no intermediate or running empty notice, one genuine empty notice, unchanged general-agent control; no console errors. Temporary fixture/server removed. -- `pass`: browser at `http://127.0.0.1:8000/index.html` loaded the rebuilt `index-BoFYrKMR.js`; no backend restart. The running process caches `/` HTML, so use `/index.html` to load new assets. -- `pass`: scoped `uv run --with pre-commit pre-commit run gitleaks --files ...` and `git diff --check`. -- `not_run`: new live cloud model generation/full captured SSE replay; browser cases use contract-derived simulated events. Verify the user's next real message separately. -- `not_applicable`: Python gates, keyboard/IME and responsive-layout changes; this increment changes neither backend code nor controls/layout. Cancellation/request-boundary behavior is covered by regression tests. No commit/push requested or performed. diff --git a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-reasoning.zh.md b/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-reasoning.zh.md deleted file mode 100644 index 498f87e00..000000000 --- a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-reasoning.zh.md +++ /dev/null @@ -1,34 +0,0 @@ -# MPA A2A 推理快照与空回复提示 - -[English](2026-09-22-mpa-a2a-reasoning.md) - -## 元信息与证据 -- ID:`mpa-a2a-reasoning`;日期:2026-09-22;状态:implemented。 -- 契约:[Studio runtime diagnostics](../../../specs/studio-runtime-diagnostics/README.zh.md),CON-10。 - -用户反馈最终答案一份,但外层推理重复,sandbox 工具之前出现空回复提示。只读任务检查确认成功完成且 sandbox final 非空。App.tsx 对每个已结束、只有推理的 turn 独立显示空提示。A2A 桥接用 `projectionSource=a2a-artifact` 标识外层完整推理,预览边界已提交后重复快照会再次追加整段推理。未捕获完整实时 SSE 顺序;测试使用观察到的事件契约,不保存生产原文。 - -## 目标、范围与需求 -- FR-1:仅注册为 `a2a-default`、且含 MPA 类型/实例信息的 Runtime 应用启用。通用 ADK、通用 A2A、本地智能体、原生 MPA 沿用既有行为。 -- FR-2:启用后的 projector 对非 partial、仅含推理的 `a2a-artifact` 快照,若匹配或扩展紧邻的前一个推理块,则替换该块。保留不同推理、token 增量、sandbox 推理、工具、附件和错误。不对任意模型文本去重。 -- FR-3:仅 MPA A2A 按两条用户消息之间的所有助手片段判断空回复。本轮运行/展示期间,或任何片段含可见内容时不提示。真正空的已结束请求仍在最后一个非空片段保留一次提示。历史请求独立判断,通用智能体沿用原逐 turn 规则。 -- FR-4:实时、历史回放、鉴权续接使用同一个显式 projector 选项,状态限定在本次请求。不改鉴权、接口路由、云部署或后端。 - -## 设计、任务与验收 -T-1 / AC-1:先增加失败测试,覆盖重复/扩展快照、实时/历史、两次请求、工具、默认/通用行为。T-2 / AC-2:增加空提示测试,覆盖运行、成功、空、取消、多条用户消息、通用行为。T-3 / AC-3:连接 `frontend/src/adk/client.ts`、`blocks.ts`、`App.tsx` 和小型纯转录辅助模块,运行定向/完整前端测试、构建、产物和浏览器检查,更新 README 与双语契约。既有取消/错误保持不变,不伪装成空的成功输出。 - -## 评审、授权与风险 -用户明确批准修复这些表现并保留通用智能体行为。review-spec 不可用,已直接评审:显式协议/类型门控、默认投影不变、无跨请求状态、不改凭据、双语等价,无阻塞项。风险:未捕获完整实时顺序;限定快照合并不得移除不同推理或 partial 增量。既有任务历史恢复可能只有最终文本,不重构缺失历史。不改变视觉设计。 - -## 验证 -2026-09-22 在基于 `3bbd260a` 的工作区差异上验证,包含既有未提交修改。T-1 至 T-3 已完成。直接实现评审确认五个投影入口显式门控、用户请求边界独立、默认行为保留;双语需求与契约一致。 - -- `pass`:测试先行基线复现重复快照(实现前六项失败,其中五项由于空提示辅助函数尚不存在);定向回归现已随完整套件通过。 -- `pass`:`npm --prefix frontend test` — 1,287 项 Node 测试和 25 项 Vitest 测试。 -- `pass`:`npm --prefix frontend run build` — TypeScript 与 app/widget 构建。首次发现的参数位置错误和不兼容 ES2020 的数组访问已修正后重跑通过;保留既有 chunk 大小警告。 -- `pass`:`npm --prefix frontend run test:webui-assets` — 104 个打包文件、248 条引用。 -- `pass`:隔离浏览器页面使用真实 projector、提示辅助函数和 Blocks 渲染器 — MPA 推理快照/最终答案各一份,中间片段和运行中无空提示,真正空回复保留一条,通用智能体对照行为不变;无控制台错误。临时页面与服务已清理。 -- `pass`:浏览器访问 `http://127.0.0.1:8000/index.html` 加载新构建 `index-BoFYrKMR.js`;未重启后端。运行进程缓存 `/` 的 HTML,使用 `/index.html` 加载新资源。 -- `pass`:限定文件的 `uv run --with pre-commit pre-commit run gitleaks --files ...` 与 `git diff --check`。 -- `not_run`:新增真实云模型请求/完整捕获 SSE 回放;浏览器场景使用契约推导的模拟事件,用户下一条真实消息另行复核。 -- `not_applicable`:Python 门禁、键盘/输入法和响应式布局变更;本次增量不改后端、控件或布局。取消/请求边界由回归测试覆盖。未请求或执行提交/推送。 diff --git a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-direct-answer-reasoning.md b/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-direct-answer-reasoning.md deleted file mode 100644 index 62217f8c0..000000000 --- a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-direct-answer-reasoning.md +++ /dev/null @@ -1,50 +0,0 @@ -# Preserve MPA direct-answer reasoning -[中文版](2026-09-22-mpa-direct-answer-reasoning.zh.md) - -ID: `mpa-direct-answer-reasoning`; date: 2026-09-22; status: implemented. - -## Evidence and requirements -Following [snapshot normalization](2026-09-22-mpa-reasoning-snapshots.md), a replay with outer thought deltas and an answer-only final loses the thinking block. Normalization suppresses the complete thought mirror; `applyEvent` only preserves sandbox thinking when clearing the preview. The user approved the proposed correction with “改下”. - -FR-1: Explicit MPA A2A mode preserves both outer and sandbox thinking before consolidated events without authoritative thought content. A direct answer retains completed, expandable reasoning and exactly one final answer. -FR-2: Authoritative thought snapshots still replace their previews. Invocation isolation, distinct phases, tools, final deduplication, usage, errors and interrupted output remain intact. General ADK/A2A and native MPA default modes remain unchanged. -FR-3: No authentication, cloud, persistence, layout or dependency changes. Backend correction scope is specified in FR-4. No recovery of events absent from server history. - -## Design, tasks and acceptance -T-1/AC-1: Add failing live/full-event replay tests for outer thought → answer delta → corrected final, and preservation across a tool boundary. Test default-mode compatibility, authoritative snapshot replacement and interrupted output. -T-2/AC-2: Remove only the sandbox-source restriction from MPA preview-thinking retention in `frontend/src/blocks.ts`. Keep the explicit `mpaA2a` gate and authoritative-thought guard. Update runtime diagnostics CON-10 in both languages. No new state; existing finalization closes thinking. -T-3/AC-3: Run targeted tests, full frontend test/build/assets checks, actual Blocks browser replay, scoped secret scan, bilingual review and diff whitespace checks. Ship matching generated web assets. - -## Review and risks -Direct review substitutes for unavailable review-spec. Scope, compatibility, event ownership, failure/cancellation and bilingual equivalence checked; no blockers. The risk is duplicated thinking when an authoritative thought snapshot exists; the existing guard and regression test retain replacement behavior. New controls/IME/narrow-layout and Python/sidecar verification are not applicable. User approval covers implementation, not commit/push or server restart. - -## Verification -2026-09-22; working diff based on `962ac8e0`. Verification results below. - -### Results -Executed 2026-09-22 on the working diff based on `962ac8e0`. T-1 through T-3 complete. -- `pass`: two new regression cases failed before the fix; 51 targeted projection/grouping tests pass after it. -- `pass`: `npm --prefix frontend test` — 1,305 Node tests and 25 Vitest tests. -- `pass`: `npm --prefix frontend run build` and `npm --prefix frontend run test:webui-assets` — TypeScript, app/widget builds; 104 packaged files, 248 references. Existing chunk-size warnings remain. -- `pass`: isolated browser using actual Python bridge fixture output, live projector and Blocks: reasoning survives completion and can expand above the single answer. Temporary fixture/server removed. No new cloud generation. -- `pass`: local `/index.html` serves `index-ek6fATgL.js`, byte-identical to the current build. Root `/` still caches `index-Y2wjLniI.js`; use `/index.html` to verify without restarting. -- `pass`: scoped source/test/doc/generated-asset secret scanning, bilingual review and `git diff --check`. -- `not_applicable`: Python, sidecar, new layout/input/keyboard/IME/loading/retry gates; none changed. Interruption covered by partial-output regression. -- `not_run`: live cloud generation; verification uses synthetic event replay. No commit, push or backend restart performed. - -## Correction after real multipart artifact regression -2026-09-22: The user reported word-sized reasoning blocks after the direct-answer fix. Read-only inspection confirmed 35 thought parts and one answer part in the final artifact. The original fixture incorrectly represented the artifact thought as a single part. Previous browser/test passes did not validate this shape; acceptance is reopened. - -FR-4: In explicit MPA mode, coalesce adjacent thought text parts in artifact-update and task artifacts before normalization, just as for status messages. Preserve exact whitespace, non-thought parts and boundaries. Completed/replacement artifacts are snapshots; append chunks are deltas. Do not reinterpret generic A2A or merge across tools/answers. Full snapshots match the complete streamed text, not individual words. -T-4/AC-4: Failing regressions must include tokenized final artifact-update and task snapshots, with/without prior deltas, extended snapshots, whitespace, append chunks and generic compatibility. Verify the real task read-only replay and a sanitized end-to-end Python bridge → frontend Blocks browser replay. Backend change is limited to `runtime_a2a_stream.py`; its tests, Ruff/Pyright and proxy regressions are required. Activation requires backend restart and must be reported explicitly. - -Direct design review: additive correction to user-approved direct-answer preservation; requirements, boundary ownership, no cross-source deletion, privacy and bilingual equivalence reviewed. No authentication, cloud deployment, persisted state or general-agent behavior changes. Approval is the user's ongoing regression correction request. - -### Multipart correction verification -2026-09-22, same working diff based on `962ac8e0`. T-4 complete; initial verification above is retained as evidence of the coverage gap. -- `pass`: seven new backend scenarios failed before correction and pass afterward; 54 decoder tests pass. -- `pass`: read-only real task history + tokenized final artifact replay; projected thought exactly equals the 128-character authoritative thought, zero extra thought events from the final artifact. Actual frontend projection/grouping yields one completed thinking block and one answer block. Private temporary payloads removed. -- `pass`: browser replay of synthetic 35-part artifact through actual Python bridge/projector/Blocks; one expandable complete reasoning block and one answer. No real cloud generation. -- `pass`: `uv run --extra dev pytest tests/cli/test_runtime_a2a_stream.py tests/cli/test_frontend_runtime_proxy.py -q` — 137 tests, five existing deprecation warnings. Ruff 0.11.12 check/format and Pyright on decoder/tests — zero errors/warnings. -- `pass`: previous 1,305 Node/25 Vitest tests and app/widget build remain applicable because frontend production code did not change during this correction; scoped gitleaks and diff whitespace checks pass. -- `not_run`: backend activation; existing running service must restart to load the parser correction. No commit/push or restart performed. diff --git a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-direct-answer-reasoning.zh.md b/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-direct-answer-reasoning.zh.md deleted file mode 100644 index efc43db64..000000000 --- a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-direct-answer-reasoning.zh.md +++ /dev/null @@ -1,50 +0,0 @@ -# 保留 MPA 直接回答时的推理 -[English](2026-09-22-mpa-direct-answer-reasoning.md) - -ID:`mpa-direct-answer-reasoning`;日期:2026-09-22;状态:implemented。 - -## 证据与需求 -[快照归一化](2026-09-22-mpa-reasoning-snapshots.zh.md)后,重放外层推理增量和仅含答案的最终事件,会丢失 thinking 块。归一化省略完整推理副本,但 `applyEvent` 清理预览时只保留沙箱推理。用户以“改下”批准了提出的修正。 - -FR-1:显式 MPA A2A 模式下,不含权威推理内容的完整事件到达前,保留外层及沙箱的 thinking。直接回答保留已完成、可展开的推理,以及一次最终答案。 -FR-2:权威推理快照仍替换预览。调用隔离、不同阶段、工具、答案去重、用量、错误及中断输出保持不变。通用 ADK/A2A 和原生 MPA 默认模式不变。 -FR-3:不修改鉴权、云端、持久化、布局或依赖。后端修正范围见 FR-4。不恢复服务端历史中缺失的事件。 - -## 设计、任务与验收 -T-1/AC-1:先增加失败的实时/完整事件重放回归,覆盖外层推理 → 答案增量 → 修正最终答案,以及跨工具边界保留。验证默认模式兼容、权威快照替换和中断输出。 -T-2/AC-2:仅移除 `frontend/src/blocks.ts` 中 MPA 预览推理保留逻辑的沙箱来源限制。保留显式 `mpaA2a` 开关和权威推理保护条件。同步更新运行时诊断 CON-10 双语契约。无新增状态;现有完成逻辑结束推理。 -T-3/AC-3:运行定向测试、完整前端测试/构建/资源检查、真实 Blocks 浏览器回放、限定范围密钥扫描、双语审查及差异空白检查。同步生成匹配的 Web 资源。 - -## 审查与风险 -review-spec 不可用,以直接审查替代。范围、兼容性、事件归属、失败/取消及双语等价性已检查,无阻塞。风险是存在权威推理快照时造成推理重复;保留原保护条件并用回归验证替换行为。新增控件/IME/窄窗口布局及 Python/sidecar 检查不适用。用户授权实施,不包含提交/推送或重启服务。 - -## 验证 -2026-09-22;基于 `962ac8e0` 的工作区差异。验证结果如下。 - -### 结果 -执行日期 2026-09-22,基于 `962ac8e0` 的工作区差异。T-1 至 T-3 完成。 -- `pass`:新增两项回归修复前失败;修复后 51 项定向投影/分组测试通过。 -- `pass`:`npm --prefix frontend test` — 1,305 项 Node 测试及 25 项 Vitest 测试。 -- `pass`:`npm --prefix frontend run build` 和 `npm --prefix frontend run test:webui-assets` — TypeScript、主页面/组件包构建;104 个打包文件、248 个引用。仍有现有分包大小警告。 -- `pass`:隔离浏览器使用真实 Python 桥接生成的模拟事件、实时投影器及 Blocks:推理在结束后保留,可在一次最终答案上方展开。临时数据/服务已清理。未发起新云端生成。 -- `pass`:本地 `/index.html` 提供 `index-ek6fATgL.js`,字节与当前构建一致。根路径 `/` 仍缓存 `index-Y2wjLniI.js`,使用 `/index.html` 可无需重启验证。 -- `pass`:限定范围源码/测试/文档/生成资源密钥扫描、双语审查及 `git diff --check`。 -- `not_applicable`:Python、sidecar、新布局/输入/键盘/IME/加载/重试检查,本次均未修改。中断由部分输出回归覆盖。 -- `not_run`:真实云端生成;验证使用模拟事件重放。未提交、推送或重启后端。 - -## 真实多 part artifact 回归后的修正 -2026-09-22:用户反馈直接回答修复后,每个词都成为推理块。只读检查确认最终 artifact 含 35 个推理 part 和一个答案 part。原模拟数据错误地把 artifact 推理表示为单个 part。此前浏览器/测试通过并未覆盖该结构,重新打开验收。 - -FR-4:仅显式 MPA 模式,在归一化前合并 artifact-update 和 task artifact 中相邻的推理文本 part,与状态消息一致。保留精确空白、非推理 part 及边界。完整/替换 artifact 是快照,追加分片是增量。不改变通用 A2A,不跨工具/答案合并。完整快照与全部流式文本匹配,而非逐词匹配。 -T-4/AC-4:先增加失败回归,覆盖分词后的最终 artifact-update 和 task 快照、有/无此前增量、扩展快照、空白、追加分片及通用兼容。验证真实任务只读回放,并以脱敏模拟数据执行 Python 桥接 → 前端 Blocks 的端到端浏览器回放。后端仅修改 `runtime_a2a_stream.py`;需执行其测试、Ruff/Pyright 及代理回归。生效需重启后端,必须明确报告。 - -直接设计审查:对用户已批准的直接回答保留进行修正;检查需求、边界归属、禁止跨来源删除、隐私及双语等价。不修改鉴权、云部署、持久化状态或通用智能体行为。授权来自用户持续的回归修正反馈。 - -### 多片段修正验证 -2026-09-22,同一基于 `962ac8e0` 的工作区差异。T-4 完成;保留上方初次验证,记录测试覆盖遗漏。 -- `pass`:新增七项后端场景修复前失败、修复后通过;54 项解码器测试通过。 -- `pass`:只读重放真实任务历史及分词后的完整 artifact;投影推理与权威推理的 128 个字符完全一致,最终 artifact 未新增推理事件。真实前端投影/分组得到一个完成推理块及一个答案块。临时私有回包已清理。 -- `pass`:以模拟 35-part artifact 通过真实 Python 桥接/投影器/Blocks 进行浏览器回放;只显示一个可展开的完整推理和一个答案。未发起真实云端生成。 -- `pass`:`uv run --extra dev pytest tests/cli/test_runtime_a2a_stream.py tests/cli/test_frontend_runtime_proxy.py -q` — 137 项测试,五项现有弃用警告。解码器/测试的 Ruff 0.11.12 检查/格式及 Pyright — 零错误/警告。 -- `pass`:此前 1,305 项 Node/25 项 Vitest 测试及主页面/组件包构建继续有效,本轮修正未再次修改前端生产代码;限定范围 gitleaks 和差异空白检查通过。 -- `not_run`:后端激活;当前服务仍需重启才能加载解析修正。未提交/推送或重启。 diff --git a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-reasoning-snapshots.md b/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-reasoning-snapshots.md deleted file mode 100644 index 89d761028..000000000 --- a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-reasoning-snapshots.md +++ /dev/null @@ -1,36 +0,0 @@ -# Normalize MPA reasoning snapshots by source -[中文版](2026-09-22-mpa-reasoning-snapshots.zh.md) - -ID: `mpa-reasoning-snapshots`; date: 2026-09-22; status: implemented. - -## Evidence and scope -Read-only inspection of the reported task confirms outer status deltas followed by a multi-part full message, and sandbox deltas followed by an exact full replay. A sandbox tool.call arrives before the last thought tokens. Replaying outer history before sandbox artifacts reproduces the duplicate: existing cross-source suffix comparison drops punctuation and corrupts the accumulated sandbox text. This continues the user-approved missing/duplicate reasoning correction; command presentation is not redesigned. - -## Requirements and design -FR-1: Normalize only explicit MPA A2A streams. Add optional `mpa_a2a=False` to the decoder; server passes the existing trusted runtime category. Default/general behavior is preserved. -FR-2: Join consecutive thought parts in MPA status messages before comparing their complete snapshot to accumulated outer text. Usage-marked snapshots remain supported. Never compare sandbox reasoning with outer reasoning or another invocation. Preserve real repeated deltas and novel text; only a complete matching/extended snapshot replaces previously emitted content. -FR-3: Track sandbox reasoning segments per task/invocation. Tool results and answer deltas mark a new reasoning phase, but tool.call alone does not: already-issued thought tails can arrive after a call. Emit `reasoningSegmentId` on thoughts. Scoped frontend projection appends a late tail to the matching thinking block across the tool row. Never move/delete tool rows or distinct reasoning phases. Snapshot equality suppresses only the accumulated full replay after multiple deltas; a new phase with identical text remains visible. -FR-4: Preserve failure/cancellation output, event replay dedup, final-answer dedup, usage and invocation isolation. No credentials, cloud deployment, user-state mutation or new dependencies. Server history missing events cannot be reconstructed. - -## Tasks and acceptance -T-1/AC-1: failing synthetic regressions for outer multi-part snapshots, mixed-source punctuation, sandbox full replay, late thought tails, separate invocations, repeated deltas and later distinct phases. -T-2/AC-2: implement the explicit backend mode, pass runtime category, and add scoped frontend segment projection. Update CON-10 in bilingual runtime diagnostics. -T-3/AC-3: targeted Python/frontend and proxy tests, Ruff/Pyright, full frontend tests/build/assets, isolated browser replay, secret scan and diff checks. Record actual activation separately; no automatic restart of a server retaining in-memory session indexes. - -## Review and risks -Direct review substitutes for unavailable review-spec. Requirements, security, state ownership, cancellation, compatibility and bilingual equivalence reviewed; no design blockers. User “看看怎么修” continues previous explicit fix authorization. Unlabelled full replay detection is conservative and restricted to a phase with multiple deltas; identical text alone across phases is never enough to deduplicate. Async ordering is covered by a tool.call between thought deltas. No API request-schema change; output metadata is additive. - -## Verification -2026-09-22; working diff based on `3bbd260a`. Verification results below. No commit or push requested. - -## Delivery verification -Executed 2026-09-22 on the working diff based on `3bbd260a`; T-1 through T-3 implemented and reviewed. -- `pass`: failing baseline reproduced three absent decoder-mode cases, late-tail block splitting, and mixed null metadata. Added regressions cover snapshots, repeated deltas, phase/invocation isolation, interruption, live/history projection and trusted runtime mode selection. -- `pass`: read-only real-task event replay produces two reasoning segments whose full contents exactly match the authoritative outer and worker messages; no raw content/identifiers copied into fixtures or docs. This is replay, not a new live generation. -- `pass`: `uv run --extra dev pytest tests/cli/test_runtime_a2a_stream.py tests/cli/test_frontend_runtime_proxy.py -q` — 130 tests, five existing SDK deprecation warnings. -- `pass`: `npm --prefix frontend test` — 1,303 Node and 25 Vitest tests; `npm --prefix frontend run build`; `npm --prefix frontend run test:webui-assets` — 104 files, 248 references. Existing chunk-size warnings remain. -- `pass`: isolated real-browser replay with sanitized Python bridge output and actual Blocks; exactly two expandable reasoning blocks, one retained tool, one final answer. Temporary browser/server/fixture removed. -- `pass`: Ruff 0.11.12 check/format on four changed Python files, scoped repository/default gitleaks including built assets, bilingual contract review and `git diff --check`. -- `fail` (existing baseline): Pyright reports 36 errors in `cli_frontend.py` and 20 in `test_frontend_runtime_proxy.py`. Compared copies from HEAD: identical diagnostic messages/rules before and after; zero new errors. Decoder and its tests have zero errors. Unrelated baseline issues were not modified. -- `not_run`: activation/new live cloud generation. Port 8000 process started 17:44:27, before backend edits at 18:01; restart remains necessary. A restart loses process-local A2A session indexes, so activation awaits the user's decision. -- `not_applicable`: new input, keyboard/IME, loading/retry or narrow-layout controls, SDK/sidecar changes. No commit or push performed. diff --git a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-reasoning-snapshots.zh.md b/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-reasoning-snapshots.zh.md deleted file mode 100644 index 54cb0864a..000000000 --- a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-reasoning-snapshots.zh.md +++ /dev/null @@ -1,36 +0,0 @@ -# 按来源归一化 MPA 推理快照 -[English](2026-09-22-mpa-reasoning-snapshots.md) - -ID:`mpa-reasoning-snapshots`;日期:2026-09-22;状态:implemented。 - -## 证据与范围 -只读检查本次任务,确认外层状态增量后带有多个 part 的完整消息,沙箱增量后带有完全一致的完整副本。沙箱 tool.call 比最后几个推理字符更早到达。先重放外层历史,再重放沙箱 artifact,可复现重复:现有跨来源后缀比较丢弃了标点,破坏沙箱累积文本。延续用户已批准的推理丢失/重复修正,不重新设计命令展示。 - -## 需求与设计 -FR-1:只归一化显式 MPA A2A 流。解码器新增可选 `mpa_a2a=False`,服务端传入已有的可信 runtime 分类。默认/通用行为保持不变。 -FR-2:先合并 MPA 状态消息中相邻的推理 part,再把完整快照与累积外层文本比较;继续支持用量标记的快照。不得跨沙箱/外层或不同调用比较推理。保留真实重复增量和新内容;仅匹配/扩展的完整快照替换已经输出的内容。 -FR-3:按任务/调用跟踪沙箱推理阶段。工具结果和答案增量标记下一个推理阶段,但 tool.call 本身不标记:已发出的推理尾部可能比调用晚到。推理附带 `reasoningSegmentId`;限定模式的前端投影将迟到尾部追加到工具行前匹配的 thinking 块。不移动/删除工具行或不同推理阶段。仅多次增量之后与完整累积文本匹配的快照被去重;新阶段即使文本相同也保留。 -FR-4:保留失败/取消输出、事件重放去重、最终答案去重、用量及调用隔离。不涉及凭据、云部署、用户状态修改或新依赖。无法重建服务端历史中缺失的事件。 - -## 任务与验收 -T-1/AC-1:先增加失败的模拟回归,覆盖外层多 part 快照、混合来源标点、沙箱完整副本、迟到推理尾部、调用隔离、重复增量和后续不同阶段。 -T-2/AC-2:实施后端显式模式、传入 runtime 分类,并增加前端限定范围的阶段投影。同步更新运行时诊断 CON-10 双语契约。 -T-3/AC-3:运行定向 Python/前端及代理测试、Ruff/Pyright、完整前端测试/构建/资源检查、隔离浏览器回放、密钥扫描及差异检查。单独记录实际激活;不自动重启仍持有内存会话索引的服务。 - -## 审查与风险 -review-spec 不可用,以直接审查替代。检查需求、安全、状态归属、取消、兼容性及双语等价,无设计阻塞。用户“看看怎么修”延续之前明确修复授权。无显式标记的完整副本识别采取保守方式,仅限包含多个增量的阶段;不同阶段文本相同不能单独作为去重依据。以推理增量之间穿插 tool.call 覆盖异步顺序。不修改 API 请求结构;输出元数据为追加字段。 - -## 验证 -2026-09-22;基于 `3bbd260a` 的工作区差异。验证结果如下。用户未要求提交或推送。 - -## 交付验证 -执行日期 2026-09-22,基于 `3bbd260a` 的工作区差异;T-1 至 T-3 已实施并审查。 -- `pass`:修复前复现三项缺少解码模式的失败、迟到尾部拆段以及混合空元数据失败。新增回归覆盖快照、重复增量、阶段/调用隔离、中断、实时/历史投影和可信 runtime 模式选择。 -- `pass`:只读重放真实任务事件,得到两段推理,全文分别与权威外层/沙箱消息完全一致;未将原始内容/标识写入测试或文档。这是重放,不是新发起的实时生成。 -- `pass`:`uv run --extra dev pytest tests/cli/test_runtime_a2a_stream.py tests/cli/test_frontend_runtime_proxy.py -q` — 130 项测试,五项现有 SDK 弃用警告。 -- `pass`:`npm --prefix frontend test` — 1,303 项 Node 和 25 项 Vitest 测试;`npm --prefix frontend run build`;`npm --prefix frontend run test:webui-assets` — 104 个文件、248 个引用。仍有现有分包大小警告。 -- `pass`:使用脱敏模拟 Python 桥接输出和真实 Blocks 的隔离浏览器回放;恰好两个可展开推理块、一个保留的工具、一个最终答案。临时浏览器/服务/数据已清理。 -- `pass`:四个修改的 Python 文件通过 Ruff 0.11.12 检查/格式检查;限定范围仓库/默认规则 gitleaks(含构建产物)、双语契约审查及 `git diff --check`。 -- `fail`(现有基线):Pyright 在 `cli_frontend.py` 报 36 项错误,在 `test_frontend_runtime_proxy.py` 报 20 项错误。与 HEAD 副本比较,前后诊断信息/规则完全一致,无新增错误。解码器及其测试零错误。未修改无关基线问题。 -- `not_run`:激活/新发起真实云端生成。8000 端口进程启动于 17:44:27,早于 18:01 的后端修改,仍需重启。重启会丢失进程内 A2A 会话索引,因此激活等待用户决定。 -- `not_applicable`:新增输入、键盘/IME、加载/重试或窄窗口控件、SDK/sidecar 变更。未提交或推送。 diff --git a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-wrapper-preview.md b/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-wrapper-preview.md deleted file mode 100644 index c921196c2..000000000 --- a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-wrapper-preview.md +++ /dev/null @@ -1,46 +0,0 @@ -# Preserve MPA preview replacement after wrapper completion - -[中文](2026-09-22-mpa-wrapper-preview.zh.md) - -- ID: `mpa-wrapper-preview` -- Date: 2026-09-22 -- Status: implemented; original-session verification pending -- Component: [Studio tool activity](../../../specs/studio-tool-activity/README.md) -- Earlier attempt: [Codex tool final ownership](2026-09-22-studio-codex-commentary-dedup.md) - -## Evidence and scope - -The user reports an unchanged duplicate answer separated by a reasoning block. The earlier attempt assumed `delegate_to_codex_sandbox`; inspection of `client.ts` confirms native MPA endpoints use `/api/v1/sessions/{id}/run` and `/sse`. The local agentkit-mpa-agent implementation projects `message.delta`, `thought.completed`, and `invocation.completed` with sandbox metadata. The user's raw session is still unavailable; this is a contract-derived reproduction, not a captured production trace. - -Studio reproduces two answers with this sequence: `sandbox_task` call, sandbox answer delta, wrapper `finalAlreadyEmitted: true`, completed thought, sandbox final. The wrapper advances `liveStart` to the block count, incorrectly committing the preview. The later authoritative answer is appended to the committed preview. - -## Requirements and design - -- **FR-1:** A wrapper completion closes the parent tool and thinking but preserves the existing preview boundary. It must not commit or discard streamed answer text. -- **FR-2:** A later consolidated event replaces the preview using existing ADK projection rules. The authoritative answer may differ from the preview; no text-equality heuristic is needed. -- **FR-3:** If the transport ends without a durable final, the preview remains visible and completed. Tool failures/results, history without partials, other invocations, and ordinary agents keep their existing behavior. - -Change only the `isFinalAlreadyEmittedSandboxResponse` branch in `frontend/src/blocks.ts`. Add regressions to `frontend/tests/parallelStreamAggregation.test.mjs`; update paired component contracts and regenerate `veadk/webui`. No backend, transport, persisted data, permissions, inputs, styles, or deployment changes. Do not redesign the separate delegate-tool implementation in this follow-up. - -## Tasks and acceptance - -1. **T-1 / AC-1:** Add a failing regression for wrapper-before-final ordering, including an intervening completed thought and changed final text. Exactly one authoritative answer remains. -2. **T-2 / AC-2:** Preserve the preview boundary; verify wrapper-only finish retains the answer and completes the tool. Verify history parity and ordinary chat regressions. -3. **T-3 / AC-3:** Run targeted and full frontend tests, typecheck, build/assets checks, and browser replay. Record outcomes and limitations in both languages. - -## Review, authorization, and risks - -The user has repeatedly authorized fixing this same duplicate-display issue. Direct design review (review-spec unavailable) finds the change local to the MPA wrapper marker and consistent with existing preview semantics. No new scope or external operation needs approval. Risk: later events can replace live preview text; this is intentional consolidation behavior, and transport-only finish must retain it. The production screenshot alone cannot establish that this particular ordering caused it; raw-session verification remains outstanding. - -## Verification - -- **pass:** Before editing, a standalone replay of the contract-derived sequence produced text, thinking, duplicate text in one turn. -- **pass:** Test-first regression: all 4 new cases failed before the one-line boundary fix, then passed. `node --test frontend/tests/parallelStreamAggregation.test.mjs frontend/tests/toolActivityModel.test.mjs frontend/tests/codexSandboxProgress.test.mjs`: 70 passed. -- **pass:** `npm --prefix frontend test`: 1,261 Node tests and 25 Vitest tests; no skipped Node tests. Typecheck: `npx tsc --noEmit -p tsconfig.json` from frontend. -- **pass:** `npm --prefix frontend run build`, `npm --prefix frontend run test:webui-assets` (104 files, 248 references), and `git diff --check`. Existing build chunk-size/import warnings remain. -- **pass:** Browser replay using actual `eventsToTurns` and `Blocks` with the contract-derived fixture: running preview, wrapper-only finish, completed answer, durable history, ordinary-agent answer, and 360px content width. One answer is visible and console errors are empty. Temporary fixture and Vite server removed. -- **pass:** Local Studio was found serving a cached old entry because `_index_html` is read at process startup. Restarted using the original `uv run veadk studio` command (omitting automatic browser opening); rebuilt entry is `index-i19v0vDQ.js`. -- **not_applicable:** Backend, input/IME, network retries/cancellation UI, and cloud execution gates: this follow-up only changes preview consolidation. No commit/push requested; commit-time gates not run. Existing related and unrelated working-tree changes were preserved. -- **blocked:** Raw-session verification pending the session URL/identity requested from the user. - -Review on 2026-09-22: no text matching, cross-agent state, or stored data changes were introduced. T-1–T-3 and AC-1–AC-3 pass for the reproduced native-event sequence; the original session remains explicitly unverified. diff --git a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-wrapper-preview.zh.md b/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-wrapper-preview.zh.md deleted file mode 100644 index 542894627..000000000 --- a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-wrapper-preview.zh.md +++ /dev/null @@ -1,46 +0,0 @@ -# MPA 包装结束后保留预览替换边界 - -[English](2026-09-22-mpa-wrapper-preview.md) - -- ID:`mpa-wrapper-preview` -- 日期:2026-09-22 -- 状态:已实现;原始会话验证待完成 -- 组件:[Studio 工具活动](../../../specs/studio-tool-activity/README.zh.md) -- 前次尝试:[Codex 工具最终回答所有权](2026-09-22-studio-codex-commentary-dedup.zh.md) - -## 证据和范围 - -用户反馈重复回答没有变化,两份答案之间有推理块。前次尝试假设使用 `delegate_to_codex_sandbox`;检查 `client.ts` 确认原生 MPA 端点使用 `/api/v1/sessions/{id}/run` 和 `/sse`。本地 agentkit-mpa-agent 实现会投影携带 sandbox metadata 的 `message.delta`、`thought.completed` 和 `invocation.completed`。尚未拿到用户原始会话;本次为根据契约构造的复现,并非生产抓包。 - -Studio 在以下顺序下复现两份答案:`sandbox_task` 调用、sandbox 答案增量、带 `finalAlreadyEmitted: true` 的包装响应、已完成推理、sandbox 最终回答。包装响应把 `liveStart` 推进到块数量,错误地将预览定稿,后到的权威回答就追加在已定稿的预览后面。 - -## 需求和设计 - -- **FR-1:** 包装结束关闭父工具和推理,但保留原有预览边界,不得将流式答案定稿或丢弃。 -- **FR-2:** 后到的合并事件按已有 ADK 投影规则替换预览。权威答案可以与预览不同,不需要文本相等的启发式判断。 -- **FR-3:** 如果传输结束时没有持久化最终事件,仍保留预览并完成展示。工具失败/结果、没有增量的历史、其他 invocation 和通用智能体保持原有行为。 - -仅修改 `frontend/src/blocks.ts` 的 `isFinalAlreadyEmittedSandboxResponse` 分支。在 `frontend/tests/parallelStreamAggregation.test.mjs` 添加回归测试,更新双语组件契约,重新生成 `veadk/webui`。不涉及后端、传输、持久化数据、权限、输入、样式或部署。本次跟进不重新设计独立的 delegate 工具实现。 - -## 任务和验收 - -1. **T-1 / AC-1:** 为包装先于最终事件的顺序添加失败回归,包括中间已完成推理以及最终文本变化,只保留一份权威答案。 -2. **T-2 / AC-2:** 保留预览边界,验证仅包装结束时仍保留答案且完成工具;验证历史一致性和通用聊天回归。 -3. **T-3 / AC-3:** 执行定向及全部前端测试、类型检查、构建/资产检查和浏览器回放,在双语文档记录结果与限制。 - -## 评审、授权和风险 - -用户已多次授权修复同一重复展示问题。直接设计评审(review-spec 不可用)确认改动仅限 MPA 包装标记,符合已有预览语义,没有需要额外批准的新范围或外部操作。风险:后续事件可能替换实时预览,这是预期的合并行为;仅传输结束必须保留预览。仅凭生产截图无法确定就是该顺序导致,原始会话验证仍待完成。 - -## 验证 - -- **pass:** 修改前独立回放根据契约构造的顺序,同一个 Turn 出现文本、推理、重复文本。 -- **pass:** 先写回归:4 个新增用例在一行边界修复前全部失败,修复后通过。`node --test frontend/tests/parallelStreamAggregation.test.mjs frontend/tests/toolActivityModel.test.mjs frontend/tests/codexSandboxProgress.test.mjs`:70 项通过。 -- **pass:** `npm --prefix frontend test`:1,261 项 Node 测试和 25 项 Vitest 测试通过,Node 无跳过。类型检查:frontend 下 `npx tsc --noEmit -p tsconfig.json`。 -- **pass:** `npm --prefix frontend run build`、`npm --prefix frontend run test:webui-assets`(104 文件、248 引用)和 `git diff --check`。保留已有构建分块大小/导入警告。 -- **pass:** 浏览器使用真实 `eventsToTurns` 和 `Blocks` 回放根据契约构造的数据:运行预览、仅包装结束、完整回答、持久化历史、通用智能体回答及 360px 内容宽度。只显示一份答案,控制台无错误。临时测试页面与 Vite 服务已移除。 -- **pass:** 发现本地 Studio 仍返回缓存的旧入口,因为 `_index_html` 在进程启动时读取。已按原 `uv run veadk studio` 命令重启(省略自动打开浏览器),新构建入口为 `index-i19v0vDQ.js`。 -- **not_applicable:** 后端、输入/IME、网络重试/取消 UI 和云端执行门禁:本次跟进仅修改预览合并。用户未要求提交/推送,未运行提交时门禁。已保留工作区中已有相关及无关改动。 -- **blocked:** 原始会话验证等待已向用户请求的会话地址/标识。 - -2026-09-22 评审:没有引入文本匹配、跨智能体状态或存储数据变更。T-1–T-3 与 AC-1–AC-3 已在复现的原生事件顺序下通过,原始会话仍明确标记未验证。 diff --git a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-followup-content.md b/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-followup-content.md deleted file mode 100644 index ca28f0834..000000000 --- a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-followup-content.md +++ /dev/null @@ -1,35 +0,0 @@ -# Preserve distinct follow-up content during MPA deduplication - -[中文版](2026-09-22-preserve-followup-content.zh.md) - -## Metadata and evidence -ID: `mpa-preserve-followup`; date: 2026-09-22; status: implemented. -Supersedes the broad ownership/filtering rules in [Codex ownership](2026-09-22-studio-codex-commentary-dedup.md) and [A2A final ownership](2026-09-22-mpa-a2a-final.md); retains final projection, wrapper handling and MPA grouping. -Isolated review reproduced three defects: a general agent's distinct text after delegate completion disappears; commentary containing an answer plus a warning is removed in full; the A2A bridge drops all outer text after a sandbox final regardless of equality. The user explicitly authorized correcting these findings. - -## Requirements and design -- FR-1: Restore general Codex delegate projection/activity to the remote-base behavior. Remove generic answer ownership and substring filtering, including associated obsolete tests; replace them with preservation regressions. Do not change ordinary ADK/native MPA protocols. -- FR-2: The A2A bridge records successful nonempty sandbox final strings per task and drops only boundary-trimmed exact duplicate complete answer text parts. Preserve reasoning, distinct/extended text, partial deltas, tools/errors and metadata; unknown/failed/empty finals cannot establish a reference. Keep sandbox event-ID replay deduplication. No buffering or new stream lifetime state beyond task-local reference strings. -- FR-3: Tag the final-owner turn only in explicitly enabled MPA A2A projection. The grouped presentation suppresses other fragments' answer text only when their complete concatenated answer equals a known sandbox final. Never mutate raw turns or remove reasoning/tools/attachments. If later deltas extend that fragment, the entire original answer becomes visible again. This prevents permanent prefix loss and hides exact stream mirrors without buffering. Feedback points to a visible answer, not a suppressed mirror. User/system boundaries isolate grouping. -- FR-4: Keep the single MPA footer and usage aggregation. Content-bearing usage/progress events must still reach the renderer; empty control events must not erase an existing preview. No auth, API routing, cloud deployment, persistence schema or general-agent grouping change. - -## Tasks and acceptance -T-1/AC-1: first add failing general-follow-up and mixed-commentary tests, backend distinct/thought/exact/partial/task-isolation tests, and MPA view exact/extended/live-history/feedback tests. -T-2/AC-2: remove obsolete generic filtering, implement narrow backend equality and non-destructive MPA view filtering; update runtime diagnostics CON-8/CON-11 and tool activity contracts in both languages. -T-3/AC-3: affected frontend/Python tests, full frontend suite/build/assets, Ruff/Pyright, secret scan, browser replay with actual projector/grouping/Blocks; record live activation separately. No server restart without a concrete decision because it loses the local A2A session index. - -## Review and risks -Direct review substitutes for unavailable review-spec; source and contracts inspected, bilingual parity reviewed, no blockers. User “修正吧” authorizes this correction. Streaming text equal to the final is hidden only in the derived view and reappears intact if extended. Missing runtime history cannot be reconstructed. General agents deliberately retain their original tool commentary behavior. Failure/cancellation does not erase distinct partial output. Existing supplementary trace/copy/share handlers remain unchanged. - -## Verification -Verified 2026-09-22 on the working diff based on `3bbd260a`. T-1 through T-3 implemented and reviewed. Existing generic tool implementation and tests are restored to `origin/main`; the new preservation suite replaces the removed broad-suppression assertions. Exact view filtering is reversible and preserves non-text blocks and the visible feedback target. - -- `pass`: red baseline reproduced seven backend failures; frontend counterexamples reproduced generic follow-up/commentary loss and absent scoped final metadata. A fixture assertion was corrected to inspect concatenated text rather than require a separate block. -- `pass`: `uv run --extra dev pytest tests/cli/test_runtime_a2a_stream.py tests/cli/test_frontend_runtime_proxy.py -q` — 126 tests, five existing SDK deprecation warnings. -- `pass`: `npm --prefix frontend test` — 1,298 Node and 25 Vitest tests, including six new preservation cases and unchanged general Codex regressions. -- `pass`: `npm --prefix frontend run build` and `npm --prefix frontend run test:webui-assets`; TypeScript and app/widget build, 104 files and 248 references. Existing chunk-size warnings remain. -- `pass`: Ruff 0.11.12 check/format and Pyright on `veadk/cli/runtime_a2a_stream.py` and `tests/cli/test_runtime_a2a_stream.py`, zero type errors/warnings. Temporary `uv run --with` tools used without changing dependencies/global settings. -- `pass`: isolated real-browser replay using fixture events projected by the actual Python bridge, actual TypeScript projection/grouping and Blocks. Exact final/prefix mirror shows one answer; extended text shows its entire prefix and unique warning. No browser errors; temporary fixture/server removed. This is simulated data, not a live cloud generation. -- `pass`: scoped gitleaks, paired-language/contract review and `git diff --check`. -- `not_run`: backend activation and live cloud generation; awaiting the user's restart decision because existing process-local A2A session indexes would be cleared. Code tests do not imply activation on port 8000. -- `not_applicable`: new layout/IME/keyboard behavior, broader SDK/sidecar changes; rendering structure and input controls unchanged. No commit or push performed. diff --git a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-followup-content.zh.md b/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-followup-content.zh.md deleted file mode 100644 index c24f22aac..000000000 --- a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-followup-content.zh.md +++ /dev/null @@ -1,35 +0,0 @@ -# MPA 去重时保留不同的补充内容 - -[English](2026-09-22-preserve-followup-content.md) - -## 元信息与证据 -ID:`mpa-preserve-followup`;日期:2026-09-22;状态:implemented。 -替代 [Codex 所有权](2026-09-22-studio-codex-commentary-dedup.zh.md) 和 [A2A 最终答案所有权](2026-09-22-mpa-a2a-final.zh.md) 中宽泛的过滤规则;保留最终事件投影、wrapper 处理和 MPA 合并。 -隔离审查复现三项问题:通用智能体 delegate 完成后的不同文本消失;包含答案及警告的 commentary 整段被删;A2A 桥接不比较内容就丢弃 sandbox final 后的所有外层文本。用户明确授权修正这些问题。 - -## 需求与设计 -- FR-1:将通用 Codex delegate 投影/活动恢复到远端基线行为。删除通用答案所有权、子串过滤及相应过时测试,补充内容保留回归。不改变普通 ADK/原生 MPA 协议。 -- FR-2:A2A 桥接按任务记录成功且非空的 sandbox final 文本,只删除去除首尾空白后完全相同的完整答案文本 part。保留推理、不同/扩展文本、partial 增量、工具/错误和元数据;未知/失败/空 final 不建立参考。保留 sandbox event-ID 回放去重。不缓冲流,新增状态仅为任务局部参考文本。 -- FR-3:仅明确启用的 MPA A2A 投影标记最终答案所属 turn。合并展示仅在其他片段拼接后的完整答案与已知 sandbox final 相等时隐藏该片段的答案文本。不修改原 turn,不删除推理/工具/附件。后续增量扩展该片段时,完整原文重新显示。这避免永久丢失前缀,无需缓冲即可隐藏完全相同的流式副本。反馈指向可见答案,而非隐藏副本。用户/系统边界隔离分组。 -- FR-4:保留 MPA 单一底栏和用量统计。包含正文的用量/进度事件仍需进入渲染,空控制事件不得擦除既有预览。不改变鉴权、接口路由、云部署、持久化 schema 或通用智能体分组。 - -## 任务与验收 -T-1/AC-1:先增加失败测试,覆盖通用补充文本、混合 commentary、后端不同文本/推理/完全重复/partial/任务隔离,以及 MPA 展示的完全重复/扩展/实时历史/反馈。 -T-2/AC-2:删除过时的通用过滤,实现后端严格相等比较与不破坏原文的 MPA 展示去重;同步双语 runtime diagnostics CON-8/CON-11 及工具活动契约。 -T-3/AC-3:相关前端/Python 测试、完整前端套件/构建/产物、Ruff/Pyright、密钥扫描、使用真实 projector/分组/Blocks 的浏览器回放;实际服务生效情况单独记录。后端重启会丢失本地 A2A 会话索引,需先明确安排。 - -## 评审与风险 -review-spec 不可用,已直接评审源码/契约、检查双语一致,无阻塞。用户“修正吧”授权本次修正。流式文本与最终答案相等时仅在派生视图隐藏;扩展后完整重现。不重建 Runtime 缺失历史。通用智能体有意保持原有工具 commentary 行为。失败/取消不擦除不同的部分输出。既有轨迹/复制/分享处理函数不变。 - -## 验证 -2026-09-22 在基于 `3bbd260a` 的工作区差异上验证。T-1 至 T-3 已实现并评审。通用工具实现及测试恢复到 `origin/main`,新增内容保留套件替代此前宽泛过滤断言。严格相等的视图过滤可逆,保留非文本 block 及可见答案反馈目标。 - -- `pass`:红灯基线复现七项后端失败;前端反例复现通用补充文本/commentary 丢失和缺少限定范围的 final 元数据。测试修正了一处断言:检查拼接文本,而非强制正文位于独立 block。 -- `pass`:`uv run --extra dev pytest tests/cli/test_runtime_a2a_stream.py tests/cli/test_frontend_runtime_proxy.py -q` — 126 项通过,五项既有 SDK 弃用警告。 -- `pass`:`npm --prefix frontend test` — 1,298 项 Node 和 25 项 Vitest 测试,含六项新增内容保留场景与原有通用 Codex 回归。 -- `pass`:`npm --prefix frontend run build` 与 `npm --prefix frontend run test:webui-assets`;TypeScript 和 app/widget 构建通过,104 个文件、248 条引用。保留既有 chunk 大小警告。 -- `pass`:对 `veadk/cli/runtime_a2a_stream.py` 与 `tests/cli/test_runtime_a2a_stream.py` 执行 Ruff 0.11.12 check/format 和 Pyright,类型错误/警告均为零。通过临时 `uv run --with` 工具执行,未改依赖或全局设置。 -- `pass`:隔离真实浏览器回放,使用实际 Python 桥接投影的模拟事件、真实 TypeScript 投影/分组与 Blocks。完全相同的 final/前缀副本只显示一次;扩展后完整显示原前缀与独有警告。无浏览器错误,临时页面/服务已清理。这是模拟数据,不是真实云端生成。 -- `pass`:限定文件 gitleaks、双语/契约评审及 `git diff --check`。 -- `not_run`:后端生效及真实云生成;等待用户确认重启,因为重启会清空当前进程内的 A2A 会话索引。代码测试不代表 8000 端口服务已生效。 -- `not_applicable`:新增布局/输入法/键盘行为、无关 SDK/sidecar 变更;渲染结构与输入控件不变。未提交或推送。 diff --git a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-sandbox-reasoning.md b/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-sandbox-reasoning.md deleted file mode 100644 index a5b0c1ba0..000000000 --- a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-sandbox-reasoning.md +++ /dev/null @@ -1,36 +0,0 @@ -# Preserve sandbox reasoning when the final answer arrives - -[中文版](2026-09-22-preserve-sandbox-reasoning.zh.md) - -## Scope and evidence -ID: `mpa-preserve-sandbox-reasoning`; date: 2026-09-22; status: implemented. -Continuation of [content preservation](2026-09-22-preserve-followup-content.md), authorized by the user's request to fix missing messages and subsequent screenshot correction. Source inspection shows `applyEvent` truncates the shared live-preview region on consolidated events, removing sandbox thought deltas when the final contains only answer text. The screenshot shows distinct outer and sandbox reasoning; the actual failing sequence will be verified before implementation. - -## Requirements and scenarios -- FR-1: In explicit MPA A2A mode, sandbox reasoning streamed before an answer must survive answer completion and non-reasoning consolidated tool/control events. Given outer reasoning, sandbox thought deltas, answer deltas and a final answer, retain both reasoning stages and one answer. -- FR-2: A consolidated event containing authoritative thought text still replaces its thought preview. Preserve invocation isolation, replay deduplication, tool/error content and cancellation behavior. Live and full-event history projection agree; missing server history is outside scope. -- FR-3: Default/general-agent and native MPA projection remain unchanged. No API, backend, authentication, cloud resources, session persistence or input/UI layout changes. - -## Design and contract impact -Keep thinking blocks from the live-preview region before an MPA sandbox consolidated event replaces answer preview, unless that event supplies its own thought text. Preserve their order; existing completion logic closes them. Do not guess whether thoughts are duplicates by text or merge distinct invocation identities. This uses existing blocks and adds no state, timers or dependencies. Update runtime diagnostics CON-10 in both languages. Permissions/security and cloud side effects: not applicable; pure local projection. - -## Tasks and acceptance -- T-1 / AC-1 / FR-1: Add a failing full-sequence regression with two reasoning stages and one answer, for live projection and full-event replay/grouping. -- T-2 / AC-2 / FR-2: Implement the scoped preservation; test consolidated tool events, authoritative thought snapshots, repeated event IDs, interrupted previews and separate invocations. -- T-3 / AC-3 / FR-3: Test unchanged default mode; run frontend tests/build/assets and an isolated real-browser replay with actual Blocks rendering. Check paired documentation and whitespace. - -## Review, risks and verification -Direct design review (review-spec unavailable): scope, event ownership, concurrency, failure/cancellation, compatibility and bilingual equivalence checked; no design blockers. Prior “修正吧” approval and continued report authorize this correction. The risk is retaining a preview when an authoritative thought snapshot exists; explicit thought detection and regression coverage address it. Existing server history may contain only the final answer and cannot recover absent thought events. - -Verification date: 2026-09-22; scope: working diff based on `3bbd260a`. Tests/build/browser: pass; results below. No commit, push, restart or live cloud generation authorized by this change. - -## Verification results -2026-09-22; working diff based on `3bbd260a`; T-1 through T-3 complete. -- `pass`: regression reproduced three failures before the fix (lost worker reasoning); all 48 targeted projection/grouping tests passed after it. -- `pass`: `npm --prefix frontend test` — 1,302 Node tests and 25 Vitest tests. -- `pass`: `npm --prefix frontend run build` (TypeScript, app and widget) and `npm --prefix frontend run test:webui-assets` (104 files, 248 references). Existing chunk-size warnings remain. -- `pass`: isolated browser replay using actual projector, grouping and Blocks; two completed reasoning headers remain, worker reasoning expands, one final answer. Fixture files and server removed. -- `pass`: scoped gitleaks with 20 MB target limit, bilingual contract/link review and `git diff --check`. -- `pass`: `/index.html` on port 8000 serves current entry `index-BpWPgu-v.js`, whose bytes match the new build. `/` still caches the earlier entry `index-D5nISdvG.js`; use `/index.html` without restarting the server. -- `not_run`: live cloud generation and incomplete server-history reconstruction. Browser check uses synthetic events. -- `not_applicable`: Python/sidecar gates (no new changes), input/IME/loading/retry/narrow-layout changes (no controls/layout changed); cancellation covered by retained partial output and stream-finish tests. No commit, push or restart performed. diff --git a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-sandbox-reasoning.zh.md b/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-sandbox-reasoning.zh.md deleted file mode 100644 index 4b819f5af..000000000 --- a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-sandbox-reasoning.zh.md +++ /dev/null @@ -1,36 +0,0 @@ -# 保留最终答案到达前的沙箱推理 - -[English](2026-09-22-preserve-sandbox-reasoning.md) - -## 范围与证据 -ID:`mpa-preserve-sandbox-reasoning`;日期:2026-09-22;状态:implemented。 -延续[内容保留修正](2026-09-22-preserve-followup-content.zh.md),用户已要求修复消息遗漏,并继续用截图指出问题。源码显示 `applyEvent` 在完整事件到达时截断共用的临时预览区,最终事件只有答案时也会移除沙箱推理增量。截图中的外层与沙箱推理内容不同;实施前通过完整事件序列确认失败。 - -## 需求与场景 -- FR-1:显式 MPA A2A 模式中,答案完成或不含推理的完整工具/控制事件到达后,保留此前流式沙箱推理。给定外层推理、沙箱推理增量、答案增量和最终答案,保留两阶段推理及一个答案。 -- FR-2:含权威推理文本的完整事件仍替换推理预览。保留调用隔离、事件重放去重、工具/错误内容和取消语义。实时与完整事件历史投影一致;服务端缺失的历史不在本次范围。 -- FR-3:默认/通用智能体及原生 MPA 投影保持不变。不修改 API、后端、鉴权、云资源、会话持久化或输入/界面布局。 - -## 设计与契约影响 -MPA 沙箱完整事件替换答案预览前,保留预览区内的 thinking 块,除非该事件自身提供推理文本。保留顺序,由现有完成逻辑结束推理状态。不按文本猜测推理是否重复,不合并不同调用身份。复用现有块,无新增状态、定时器或依赖。同步更新运行时诊断 CON-10 双语契约。权限/安全与云副作用不适用:仅本地投影逻辑。 - -## 任务与验收 -- T-1 / AC-1 / FR-1:先增加失败的完整序列回归,验证实时投影及完整事件重放/分组均保留两段推理和一个答案。 -- T-2 / AC-2 / FR-2:实施限定范围的保留逻辑;测试完整工具事件、权威推理快照、重复事件 ID、中断预览和调用隔离。 -- T-3 / AC-3 / FR-3:验证默认模式不变;运行前端测试/构建/静态资源校验,使用真实 Blocks 在隔离浏览器中重放。检查双语文档及空白。 - -## 审查、风险与验证 -直接设计审查(review-spec 不可用):检查范围、事件归属、并发、失败/取消、兼容性及双语等价性,无设计阻塞。之前“修正吧”的授权与后续问题反馈覆盖此修正。风险是权威推理快照存在时仍保留预览;通过显式推理检测及回归覆盖避免。现有服务端历史可能只有最终答案,无法恢复不存在的推理事件。 - -验证日期:2026-09-22;范围:基于 `3bbd260a` 的工作区差异。测试/构建/浏览器:pass,结果如下。本次不授权提交、推送、重启或真实云端生成。 - -## 验证结果 -2026-09-22;基于 `3bbd260a` 的工作区差异;T-1 至 T-3 已完成。 -- `pass`:修复前复现三项失败(沙箱推理丢失);修复后 48 项定向投影/分组测试全部通过。 -- `pass`:`npm --prefix frontend test` — 1,302 项 Node 测试及 25 项 Vitest 测试。 -- `pass`:`npm --prefix frontend run build`(TypeScript、主页面及组件包)和 `npm --prefix frontend run test:webui-assets`(104 个文件、248 个引用)。仍有现有分包大小警告。 -- `pass`:隔离浏览器使用真实投影器、分组及 Blocks 重放;保留两个完成推理入口,沙箱推理可以展开,最终答案只有一个。临时文件及服务器已清理。 -- `pass`:20 MB 目标上限的限定范围 gitleaks、双语契约/链接审查及 `git diff --check`。 -- `pass`:8000 端口 `/index.html` 提供最新入口 `index-BpWPgu-v.js`,其字节与新构建一致。`/` 仍缓存旧入口 `index-D5nISdvG.js`;无需重启,使用 `/index.html`。 -- `not_run`:真实云端生成及不完整服务端历史重建。浏览器验证使用模拟事件。 -- `not_applicable`:Python/sidecar 检查(本次无新增修改)、输入/IME/加载/重试/窄窗口布局变更(未修改控件或布局);取消通过保留部分输出及流结束测试覆盖。未提交、推送或重启。 diff --git a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-studio-codex-commentary-dedup.md b/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-studio-codex-commentary-dedup.md deleted file mode 100644 index c98b1e8a7..000000000 --- a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-studio-codex-commentary-dedup.md +++ /dev/null @@ -1,113 +0,0 @@ -# Studio Codex final-answer ownership - -[Chinese](2026-09-22-studio-codex-commentary-dedup.zh.md) - -- ID: `studio-codex-commentary-dedup` -- Created/revised: 2026-09-22 -- Status: superseded -- Component: [Studio tool activity](../../../specs/studio-tool-activity/README.md) - -## Background and evidence - -**Evidence correction (2026-09-22):** The following diagnosis assumed the delegate-tool path without the user's raw events. Native MPA uses its own session SSE endpoint; this attempt does not establish a fix for the reported MPA session. See the [native MPA follow-up](2026-09-22-mpa-wrapper-preview.md). Earlier synthetic checks remain valid only for their tested event format. - -The legacy `arkclaw-team -> mpa-agent` path gives the delegated Codex result one final-answer owner. `mpa-agent` marks the completed sandbox invocation, stops later top-level model text, and filters duplicate model text from SSE/history. The current `Studio -> agentkit-mpa-agent` path uses the dynamically injected `delegate_to_codex_sandbox` tool instead. VeADK already sets `skip_summarization` for a successful tool result, but Studio preserves a visible Codex commentary item and separately appends the function response `message` as the final answer. - -A reproduced turn contains the same complete answer before tool activities and after them. The first copy is a commentary activity and the second is the authoritative direct answer. Existing final-event filtering removes nested `assistant_final` events but intentionally preserves commentary, so it does not cover this case. - -The first exact-match fix was insufficient against the live sequence. Codex may also emit completed reasoning that contains the full final answer, and the outer model may emit more reasoning and answer text after the successful function response. The legacy path treated the delegated response as the turn's final-answer owner; Studio needs the same ownership boundary rather than only comparing one commentary block. - -## Goals and non-goals - -### Goals - -- Show one authoritative final answer after a successful Codex delegation, including when activity reasoning contains that answer or the outer model emits later text. -- Apply the same rule to live progress and persisted `codex_activity` replay. -- Preserve commands, searches, file changes, failures, distinct activity that does not repeat the final answer, and the final answer. - -### Non-goals - -- Changing Codex generation, app-server event schemas, Studio Channel transport, or `agentkit-mpa-agent`. -- Fuzzy, semantic, or cross-turn deduplication. -- Hiding command output that independently contains the same words. -- Changing ordinary chat turns or calls to tools other than `delegate_to_codex_sandbox`. - -## Scenarios - -1. A commentary item contains `Result`, tool activities follow, and the successful response message is `Result`: Studio removes the commentary item and renders one final `Result`. -2. Completed Codex reasoning contains the full `Result`: Studio omits that redundant reasoning item while keeping adjacent commands and searches. -3. The outer model emits reasoning and answer text after the successful Codex function response: Studio retains the delegated response and ignores those later model text parts in the same turn. -4. Commentary says `Checking the skill` and the final response says `Result`: both remain visible. -5. A general agent uses ordinary chat or another tool: its reasoning and answer projection remain unchanged. A general agent that explicitly invokes `delegate_to_codex_sandbox` receives the same tool-scoped final-owner behavior. -6. A failed or busy Codex response contains a message: existing behavior remains and no direct answer is synthesized. - -## Requirements and design - -- **FR-1 — Delegated final-answer ownership.** A successful `delegate_to_codex_sandbox` response with a non-empty `message` owns the final answer for that assistant turn. Once projected, later outer-model text and thought parts in the same turn are ignored. -- **FR-2 — Call-scoped activity reconciliation.** After hydrating the matched tool's activity snapshot, omit commentary or reasoning blocks whose boundary-trimmed text contains the complete boundary-trimmed final answer verbatim. Do not use fuzzy or semantic matching. -- **FR-3 — Preserve structured and unrelated activity.** Keep commands, searches, file changes, plans, authorization, failures, and activity text that does not contain the complete final answer. Never inspect command output for this rule and never alter another tool block. -- **FR-4 — Tool scope.** Apply final ownership only when a successful `delegate_to_codex_sandbox` result is present. Ordinary model chat and every other tool keep their existing projection behavior, regardless of agent category. -- **FR-5 — Live/history parity and idempotence.** Apply the same reconciliation to live progress plus response hydration and persisted history. Replaying the response or receiving a later wrapper-model answer must still produce one final answer. - -Implementation uses a pure activity transformation in `frontend/src/ui/builtin-tools/codexSandboxProgress.ts` and invokes it from the matched Codex function-response branch in `frontend/src/blocks.ts` before the authoritative answer is appended. The turn projector derives final ownership only from a completed successful Codex tool response and suppresses subsequent unstructured model text/thought parts while continuing to process structured events. The transformation returns the original activity object when no item changes. - -This changes the maintained `studio-tool-activity` transcript contract and therefore updates both component-spec languages. It does not change transport, backend, persistence, authorization, or public API contracts. - -## Affected files and tasks - -- `frontend/src/ui/builtin-tools/codexSandboxProgress.ts`: add the pure call-local deduplication helper. -- `frontend/src/blocks.ts`: apply it after response hydration and before final-answer projection. -- `frontend/tests/codexSandboxProgress.test.mjs`: add regression coverage for equal and distinct commentary. -- `specs/studio-tool-activity/README.md` and `README.zh.md`: record the final-answer ownership rule. -- `veadk/webui`: regenerate the packaged frontend. `.gitattributes` applies the existing generated-JavaScript whitespace exception to `website-integration.js` too, preserving semantic whitespace inside bundled template literals. - -| Task | Requirement | Acceptance | -| --- | --- | --- | -| T-1: add failing projection tests | FR-1–FR-5 | The reported commentary/reasoning/late-answer sequence reproduces repeated output; unrelated-tool fixtures retain normal text | -| T-2: implement tool-scoped final ownership | FR-1–FR-5 | The regression passes without changing structured activity or ordinary chat | -| T-3: verify and reconcile bilingual documents | All | Targeted test, full frontend tests, TypeScript/build, assets, i18n, and diff checks are recorded | - -## Tests - -- Targeted: `node --test frontend/tests/codexSandboxProgress.test.mjs`. -- Frontend regression: `npm --prefix frontend test`. -- Build and generated assets: `npm --prefix frontend run build` and `npm --prefix frontend run test:webui-assets`. -- Localization: `npm --prefix frontend run check:i18n`. -- Real browser: reproduce a Codex turn containing equal commentary/final text; verify one answer, preserved tool activities, replay after reload, and distinct-commentary behavior. - -## Risks - -- A short final answer may occur inside a longer reasoning item. Omitting that completed reasoning is acceptable because the same successful delegation has already supplied the user-visible answer; structured execution evidence remains visible. -- Boundary trimming plus verbatim containment covers the reported self-talk without semantic comparison or Markdown normalization. -- Suppressing all later model text could affect unrelated turns if scoped broadly. Ownership is therefore derived only from a successful Codex tool response in the current accumulator and does not cross turns, sessions, or other tools. - -## Acceptance criteria - -- AC-1: The reproduced `commentary -> reasoning containing answer -> tools -> successful response -> outer reasoning/final` turn displays the answer once. -- AC-2: Structured activity and distinct commentary/reasoning remain ordered and visible. -- AC-3: Ordinary chat, non-Codex tools, and failed/busy Codex calls retain their existing projection behavior. -- AC-4: Live and persisted response paths produce the same blocks, including response replay. -- AC-5: Required automated checks pass; any unavailable browser check is reported as blocked or not run. - -## Review and approval - -The user approved the original exact-match implementation and then approved the expanded tool-scoped final-ownership plan on 2026-09-22 after the live counterexample and general-agent boundary were explained. Direct design review was used because no `review-spec` skill is available. The review found the solution deterministic, backward compatible at the transport boundary, isolated to successful Codex delegation, free of credential/data changes, and testable in the existing projector suite. No blocker remains. - -## Verification - -Execution date: 2026-09-22. Scope: the working-tree fix based on `3bbd260a` on `codex/from-main-20260921`. - -- **pass** — Test-first regression: the expanded duplicate-activity test and late-event/history test failed before implementation and passed afterward. -- **pass** — From `frontend`, `node --test tests/codexSandboxProgress.test.mjs tests/parallelStreamAggregation.test.mjs`: 39/39 tests. Coverage includes live activity, persisted snapshots, repeated responses, late model text/thought, ordinary tools, separate authors/invocations, next user turn, unfinished success, failed/busy/cancelled/timeout responses, and existing parallel-turn behavior. -- **pass** — `npm --prefix frontend test`: 1,258 Node tests and 25 Vitest tests. The later cancellation/timeout fixture extension passed in the targeted rerun above. -- **pass** — From `frontend`, `npx tsc --noEmit -p tsconfig.json`; `npm run check:i18n` (2 locales, 21 namespaces). -- **pass** — `npm --prefix frontend run build` and `npm --prefix frontend run test:webui-assets`: 104 files and 248 references validated. Build warnings concern existing mixed imports and large bundles. -- **pass** — Isolated real-browser rendering with the production `eventsToTurns` and `Blocks` modules and synthetic events: running-to-completed transition, late duplicate output, history replay, distinct commentary and command preservation, ordinary chat, failed response, keyboard selection, and a 360-pixel content column. The completed result contained one final answer; browser error logs were empty. The temporary fixture and development server were removed afterward. -- **pass** — Local Studio on port 8000 serves the rebuilt `index-WJxKEjkM.js` entry. This confirms the served version, not an already-open tab's cached version. -- **pass** — `git diff --check`; bilingual requirements/identifiers and relative links reviewed. -- **not_run** — Live cloud MPA reproduction: the actual failing session's raw event envelope was not available. The browser and unit checks model the reported sequence and do not establish that the online session uses this delegate tool. -- **not_applicable** — Python, IME/input, and request cancellation UI gates: no backend, input, or request lifecycle code changed. Cancellation result projection is tested. Commit-time pre-commit gates have not run because no commit is requested. - -Direct implementation review found final ownership confined to the current assistant author/invocation state until transport finish or a new user round. Structured tool activity and separate authors/invocations remain intact. T-1–T-3 and AC-1–AC-5 are satisfied by the recorded checks, with live-cloud verification explicitly outstanding. - -> The broad suppression rules are superseded by [preserving follow-up content](2026-09-22-preserve-followup-content.md). Historical verification below describes the earlier behavior. diff --git a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-studio-codex-commentary-dedup.zh.md b/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-studio-codex-commentary-dedup.zh.md deleted file mode 100644 index f48ebca62..000000000 --- a/prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-studio-codex-commentary-dedup.zh.md +++ /dev/null @@ -1,113 +0,0 @@ -# Studio Codex 最终回答所有权 - -[English](2026-09-22-studio-codex-commentary-dedup.md) - -- ID:`studio-codex-commentary-dedup` -- 创建/修订:2026-09-22 -- 状态:superseded -- 组件:[Studio 工具活动](../../../specs/studio-tool-activity/README.zh.md) - -## 背景与证据 - -**证据更正(2026-09-22):** 下述诊断在缺少用户原始事件时假设了 delegate 工具链路。原生 MPA 使用自身的会话 SSE 端点,因此本次尝试不能证明已修复用户报告的 MPA 会话。参见[原生 MPA 跟进修复](2026-09-22-mpa-wrapper-preview.zh.md)。此前构造事件的检查结果仅对所测事件格式有效。 - -旧 `arkclaw-team -> mpa-agent` 链路为委派的 Codex 结果指定唯一的最终回答所有者。`mpa-agent` 会标记已完成的 sandbox invocation、阻止顶层模型继续输出文本,并从 SSE/历史中过滤重复模型文本。当前 `Studio -> agentkit-mpa-agent` 链路改用动态注入的 `delegate_to_codex_sandbox` 工具。VeADK 已为成功工具结果设置 `skip_summarization`,但 Studio 会保留一条可见的 Codex commentary,同时又把 function response 的 `message` 追加为最终回答。 - -已复现的 Turn 在工具活动前后各出现一次完全相同的完整回答。第一份来自 commentary activity,第二份是权威直接回答。现有 final event 过滤会移除嵌套的 `assistant_final`,但会刻意保留 commentary,因此未覆盖该场景。 - -首版精确匹配修复仍不足以覆盖真实链路。Codex 还可能输出包含完整最终回答的已完成 reasoning,外层模型也可能在成功 function response 后继续输出 reasoning 和答案文本。旧链路把委派响应作为该 Turn 的最终回答所有者;Studio 需要补齐相同的所有权边界,而不是只比较一条 commentary。 - -## 目标与非目标 - -### 目标 - -- Codex 委派成功后只展示一个权威最终回答,包括 activity reasoning 含有该回答或外层模型继续输出文本的情况。 -- 对实时 progress 和持久化 `codex_activity` 重放使用相同规则。 -- 保留命令、搜索、文件修改、失败、不重复最终回答的不同 activity,以及最终回答。 - -### 非目标 - -- 修改 Codex 生成、app-server 事件 schema、Studio Channel 传输或 `agentkit-mpa-agent`。 -- 进行模糊、语义或跨 Turn 去重。 -- 隐藏独立包含相同文字的命令输出。 -- 修改普通聊天 Turn 或 `delegate_to_codex_sandbox` 以外的工具调用。 - -## 场景 - -1. commentary 内容为 `结果`,随后有工具活动,成功响应 message 也是 `结果`:Studio 删除该 commentary,只渲染一次最终 `结果`。 -2. 已完成的 Codex reasoning 包含完整 `结果`:Studio 省略这条冗余 reasoning,同时保留相邻命令和搜索。 -3. 成功 Codex function response 后外层模型继续输出 reasoning 和答案:Studio 保留委派响应,忽略同一 Turn 后续的模型文本 part。 -4. commentary 为 `正在检查技能`,最终响应为 `结果`:两者都保持可见。 -5. 通用智能体执行普通聊天或其他工具:reasoning 和答案投影保持不变;如果通用智能体明确调用 `delegate_to_codex_sandbox`,则使用相同的工具范围最终所有权规则。 -6. failed 或 busy Codex 响应携带 message:保持既有行为,不合成直接回答。 - -## 需求与设计 - -- **FR-1 — 委派最终回答所有权。** 成功的 `delegate_to_codex_sandbox` 响应提供非空 `message` 后,该响应拥有当前 assistant Turn 的最终回答。完成投影后,忽略同一 Turn 后续的外层模型文本和 thought part。 -- **FR-2 — 调用范围内 activity 对账。** 匹配工具完成 activity snapshot hydration 后,如果 commentary 或 reasoning 去除首尾空白后的文本逐字包含去除首尾空白后的完整最终回答,则省略该 block。不得使用模糊或语义匹配。 -- **FR-3 — 保留结构化和无关活动。** 保留命令、搜索、文件修改、计划、授权、失败,以及不包含完整最终回答的 activity 文本。不得对命令输出使用该规则,也不得修改另一个工具 block。 -- **FR-4 — 工具范围。** 只有存在成功 `delegate_to_codex_sandbox` 结果时才应用最终所有权。无论智能体类别如何,普通模型聊天和其他工具均保持既有投影行为。 -- **FR-5 — 实时/历史一致和幂等。** 对实时 progress 加 response hydration 与持久化历史使用相同对账规则。重放响应或收到外层模型后续答案时仍只产生一个最终回答。 - -实现在 `frontend/src/ui/builtin-tools/codexSandboxProgress.ts` 使用纯 activity 转换,并在 `frontend/src/blocks.ts` 匹配到 Codex function response 后、追加权威回答前调用。Turn projector 仅从已完成且成功的 Codex 工具响应推导最终所有权,并在继续处理结构化事件的同时抑制后续非结构化模型文本/thought part。没有活动变化时返回原 activity 对象。 - -本次修改维护中的 `studio-tool-activity` transcript 契约,因此同步更新组件规范双语版本。不修改传输、后端、持久化、鉴权或公开 API 契约。 - -## 影响文件与任务 - -- `frontend/src/ui/builtin-tools/codexSandboxProgress.ts`:增加调用局部的纯去重 helper。 -- `frontend/src/blocks.ts`:在响应 hydration 后、最终回答投影前执行去重。 -- `frontend/tests/codexSandboxProgress.test.mjs`:增加 commentary 相同和不同的回归覆盖。 -- `specs/studio-tool-activity/README.md` 和 `README.zh.md`:记录最终回答所有权规则。 -- `veadk/webui`:重新生成前端打包资产。`.gitattributes` 将已有的生成 JavaScript 空白例外同步应用于 `website-integration.js`,保留打包模板字符串中有语义的空白。 - -| 任务 | 需求 | 验收 | -| --- | --- | --- | -| T-1:增加失败的投影测试 | FR-1–FR-5 | 用户报告的 commentary/reasoning/后续答案顺序可复现重复;无关工具 fixture 保持普通文本 | -| T-2:实现工具范围最终所有权 | FR-1–FR-5 | 回归通过,且不改变结构化活动或普通聊天 | -| T-3:验证并核对双语文档 | 全部 | 记录定向测试、完整前端测试、TypeScript/build、资产、i18n 和 diff 检查 | - -## 测试 - -- 定向:`node --test frontend/tests/codexSandboxProgress.test.mjs`。 -- 前端回归:`npm --prefix frontend test`。 -- 构建和生成资产:`npm --prefix frontend run build` 和 `npm --prefix frontend run test:webui-assets`。 -- 国际化:`npm --prefix frontend run check:i18n`。 -- 真实浏览器:复现包含相同 commentary/final 文本的 Codex Turn,确认回答仅一次、工具活动保留、刷新后重放一致,并验证不同 commentary。 - -## 风险 - -- 较短的最终回答可能出现在更长 reasoning 内。由于同一次成功委派已提供用户可见答案,省略该已完成 reasoning 是可接受行为;结构化执行证据仍然保留。 -- 去除边界空白后逐字包含可以覆盖报告中的自我对话,同时避免语义比较或 Markdown 归一化。 -- 若广泛抑制后续模型文本,可能影响无关 Turn。因此所有权只从当前 accumulator 内成功的 Codex 工具响应推导,不跨 Turn、Session 或其他工具。 - -## 验收标准 - -- AC-1:已复现的 `commentary -> 包含答案的 reasoning -> 工具 -> 成功响应 -> 外层 reasoning/final` Turn 只展示一次回答。 -- AC-2:结构化 activity 和不同内容的 commentary/reasoning 保持顺序且可见。 -- AC-3:普通聊天、非 Codex 工具以及 failed/busy Codex 调用保持既有投影行为。 -- AC-4:实时和持久化响应路径产生相同 blocks,包括响应重放。 -- AC-5:所需自动化检查通过;无法执行的浏览器检查明确记录为 blocked 或 not run。 - -## 评审与批准 - -2026-09-22,用户先批准首版精确匹配实现,随后在说明真实反例和通用智能体边界后,批准扩展后的工具范围最终所有权方案。当前没有可用的 `review-spec` skill,因此执行直接设计评审。评审确认方案结果确定、传输边界向后兼容、仅限成功 Codex 委派、不涉及凭据或数据变更,并可由现有 projector 测试覆盖,无阻塞项。 - -## 验证 - -执行日期:2026-09-22。范围:`codex/from-main-20260921` 分支上基于 `3bbd260a` 的工作区修复。 - -- **pass** — 测试先行:扩展的重复 activity 测试和后续事件/历史测试在实现前失败,实现后通过。 -- **pass** — 在 `frontend` 执行 `node --test tests/codexSandboxProgress.test.mjs tests/parallelStreamAggregation.test.mjs`:39/39。覆盖实时 activity、持久化 snapshot、重复响应、后续模型文本/thought、普通工具、不同 author/invocation、下一轮用户消息、未完成的成功响应、failed/busy/cancelled/timeout 响应以及既有并行 Turn 行为。 -- **pass** — `npm --prefix frontend test`:1,258 个 Node 测试和 25 个 Vitest 测试通过。之后扩展的取消/超时 fixture 已通过上述定向重跑。 -- **pass** — 在 `frontend` 执行 `npx tsc --noEmit -p tsconfig.json`;`npm run check:i18n`(2 个 locale,21 个 namespace)。 -- **pass** — `npm --prefix frontend run build` 和 `npm --prefix frontend run test:webui-assets`:已验证 104 个文件和 248 个引用。构建告警来自既有的混合 import 和较大 bundle。 -- **pass** — 使用生产 `eventsToTurns`、`Blocks` 模块和合成事件进行隔离真实浏览器渲染:执行中转完成、后续重复输出、历史重放、保留不同 commentary 和命令、普通聊天、失败响应、键盘选择以及 360 像素内容栏。完成后只显示一次最终答案,浏览器错误日志为空。验证后已移除临时 fixture 并停止开发服务器。 -- **pass** — 本地 8000 端口 Studio 提供重新构建的 `index-WJxKEjkM.js` 入口。这确认的是服务端提供的版本,不代表已有标签页缓存的版本。 -- **pass** — `git diff --check`;双语需求/标识符和相对链接已核对。 -- **not_run** — 云端 MPA 实际复现:尚未取得发生问题的真实 Session 原始事件。浏览器和单元验证模拟用户报告的事件顺序,不能证明在线会话使用了该委派工具。 -- **not_applicable** — Python、IME/输入和请求取消 UI 检查:未修改后端、输入或请求生命周期代码;已测试取消结果的投影。当前未请求提交,因此未执行提交前 pre-commit 门禁。 - -直接实现评审确认最终所有权仅保存在当前 assistant author/invocation 状态中,直到传输结束或进入新的用户轮次;结构化工具活动及不同 author/invocation 均保留。T-1–T-3 和 AC-1–AC-5 已由所记录的检查满足,云端实际验证明确保留为待确认项。 - -> 宽泛过滤规则由[保留补充内容](2026-09-22-preserve-followup-content.zh.md)替代。下文历史验证描述的是此前行为。 diff --git a/prd-spec/bugfixes/studio-cron-e2e/2026-09-15-integration.md b/prd-spec/bugfixes/studio-cron-e2e/2026-09-15-integration.md deleted file mode 100644 index 2794f3ca7..000000000 --- a/prd-spec/bugfixes/studio-cron-e2e/2026-09-15-integration.md +++ /dev/null @@ -1,17 +0,0 @@ -# Studio scheduled-task end-to-end integration - -[中文](2026-09-15-integration.zh.md) - -2026-09-15; approved by user. Target: local Studio and the explicitly selected Beijing Runtime. FR-1: create/update/run/list scheduled tasks through both conversation and page controls, with common user ownership. FR-2: inject x-user-id from the authorized Studio principal, never from arbitrary browser headers. MPA A2A may adopt this header only with JWT disabled; verified TIP identity has precedence. JWT-enabled behavior and existing user isolation remain unchanged. FR-3: upgrade old image to current additive ADK adapter; preserve database/network/credentials and existing task records. Long-running streams keep status and eventual results. No all-user reads. - -T-1: failing identity regression and minimal fixes. T-2: unit tests, changed-line coverage above 95%, frontend build/tests as applicable and secret scan. T-3: publish amd64 image and update only authorized Runtime, preserving configuration, then verify readiness. T-4: use uniquely named Web-delivery test tasks for chat/page CRUD and manual execution, verify cross-channel listing and successful runs. Disable test tasks after checks; retain run evidence. Failures must be corrected and retested. Review: scoped identity resolution reuses existing service paths, no new authentication bypass or dependency. Acceptance is live functional evidence, not only image readiness. Record checks and limitations below. - -## Verification, 2026-09-15 - -Pass: Runtime `r-yeuugf44qob21078l38i` is Ready on image `20260915-f617c67-studio-e2e` (linux/amd64, source `f617c67`). Health, readiness, list-apps, sessions and scheduled-task list return 200. Preserved database/network/auth settings; disabled optional metadata startup and AppCenter resource discovery for the standalone deployment. - -Pass: real Chrome/local Studio conversation called create/update/run/list tools. Chat task `5819ad31-a2f7-4077-8f22-084a46a27cd0` reached version 2 and run `b990fc1d-d8f2-4c0b-9b13-d7c565e63e4b` succeeded. Page controls created/edited/triggered task `51ecb600-d3b1-41c1-b810-cfec427de9e2`, version 2; run result session `5047e17e-f41e-4742-be43-b515cbf438ca` succeeded. Both tasks appear together in the Runtime list, show 100% success (2 runs), and remain disabled with Web delivery. Scheduled execution sessions appear in history. - -Live follow-up fix: assistant stream projection ignores `author=user` echoes while preserving agent-authored tool responses with `content.role=user`. Regression failed before the fix and passes after it; no event storage changes. - -Pass: 151 MPA tests, 85 Studio Python tests, 1100 frontend tests, 5 waiting/echo coverage tests, frontend build and bundled asset verification. Changed Python identity statements and frontend echo guard have 100% coverage. Pre-existing Pyright diagnostics remain outside this change (34 source, 20 test diagnostics); not a clean whole-file typecheck. Standard pinned pre-commit is the lint/security gate. diff --git a/prd-spec/bugfixes/studio-cron-e2e/2026-09-15-integration.zh.md b/prd-spec/bugfixes/studio-cron-e2e/2026-09-15-integration.zh.md deleted file mode 100644 index 4df40afe8..000000000 --- a/prd-spec/bugfixes/studio-cron-e2e/2026-09-15-integration.zh.md +++ /dev/null @@ -1,17 +0,0 @@ -# Studio 定时任务端到端接入 - -[English](2026-09-15-integration.md) - -2026-09-15;用户已批准。目标为本地 Studio 与明确指定的北京 Runtime。FR-1:通过对话与页面完成定时任务创建/修改/触发/列表,两者使用相同用户归属。FR-2:由已鉴权的 Studio 主体注入 x-user-id,不信任浏览器任意身份头。MPA A2A 仅在 JWT 关闭时采用该头,已验证 TIP 身份优先;JWT 开启行为与用户隔离不变。FR-3:把旧镜像更新为现有增量 ADK 兼容接口版本,保留数据库/网络/凭证及历史任务。长流持续展示状态并收到结果,不开放全用户查询。 - -T-1:先写身份失败回归,再做最小修复。T-2:单测、增量覆盖率高于 95%、适用的前端测试构建及密钥扫描。T-3:发布 amd64 镜像,仅更新授权 Runtime 并保持配置,验证就绪。T-4:使用唯一名称、Web 投递的测试任务,完成对话/页面操作、交叉列表及执行成功验证,结束后禁用测试任务并保留运行证据。失败须修复重测。评审:身份解析复用现有服务链路,不增加鉴权绕过或依赖;验收以实际功能证据为准,不仅看镜像就绪。下方记录验证及限制。 - -## 验证记录,2026-09-15 - -通过:Runtime `r-yeuugf44qob21078l38i` 已 Ready,镜像 `20260915-f617c67-studio-e2e`(linux/amd64,源码 `f617c67`)。健康、就绪、list-apps、sessions 和任务列表均返回 200。保留数据库、网络、鉴权配置;独立部署关闭可选元数据启动和 AppCenter 资源发现。 - -通过:真实 Chrome 本地 Studio 对话完成创建、修改、触发、列表工具调用。对话任务 `5819ad31-a2f7-4077-8f22-084a46a27cd0` 达到版本 2,执行 `b990fc1d-d8f2-4c0b-9b13-d7c565e63e4b` 成功。页面按钮创建、编辑、触发任务 `51ecb600-d3b1-41c1-b810-cfec427de9e2`,版本 2,结果会话 `5047e17e-f41e-4742-be43-b515cbf438ca` 执行成功。两条任务在 Runtime 列表共同显示,2 次执行、成功率 100%,均保持禁用并使用 Web 投递。定时执行会话出现在历史列表。 - -实测补充修复:模型流投影忽略 `author=user` 回显,保留 `content.role=user` 的 Agent 工具响应;回归用例先失败再通过,不修改事件存储。 - -通过:MPA 151 个测试、Studio Python 85 个测试、前端 1100 个测试、等待/回显覆盖测试 5 个、前端构建与打包资源验证。新增 Python 身份语句及前端回显判断覆盖率 100%。Pyright 原有诊断仍在本次范围外(源码 34、测试 20),不声明整文件类型检查干净;使用仓库固定版本 pre-commit 作为 lint/安全检查。 diff --git a/prd-spec/bugfixes/studio-latest-image-refresh/2026-10-20-latest-image-snapshots.md b/prd-spec/bugfixes/studio-latest-image-refresh/2026-10-20-latest-image-snapshots.md deleted file mode 100644 index 146468a77..000000000 --- a/prd-spec/bugfixes/studio-latest-image-refresh/2026-10-20-latest-image-snapshots.md +++ /dev/null @@ -1,103 +0,0 @@ -# Studio latest image snapshots - -> Superseded for current implementation by the approved [read-only resolution design](2026-10-21-readonly-tag-resolution.md). The following records the original registry-writing design and its historical deployment evidence. - -[中文版](2026-10-20-latest-image-snapshots.zh.md) - -- Change ID: `studio-latest-image-refresh` -- Created: 2026-10-20; revised: 2026-10-21 -- Status: approved; user approved digest snapshots, Studio implementation/deployment and new-MPA validation on 2026-10-21. -- Component: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md). -- Baseline: `86c93529969e6dde86890b30f7e407bfc32ff5ea`, matching the deployed Studio source archive. - -## Evidence and cause - -The registry's current Worker `latest` manifest matches the recently published release. A local pull and three source-file hashes confirm the new code. AgentKit CreateTool receives the correct latest URL. CloudTrail confirms its downstream CreateFunction request also contains that URL, without an old image ID. Nevertheless, the resulting veFaaS revision and a new session use an earlier internal image ID. A successful image precache does not update that published binding. Updating the function source and releasing another revision fixed individual test functions, but did not fix subsequent CreateTool calls. - -The proven failure boundary is veFaaS source synchronization/resolution during creation; the service's cache key, TTL, and internal selection algorithm remain unverified. Studio currently accepts platform Ready without establishing image freshness. The current API rejects digest-form precache URLs and rejects using a prewarmed internal registry URL as a new source. Thus neither digest URLs nor internal image-ID URLs are established supported deployment mechanisms. - -A separate Studio task failed although its Runtime later became Ready V1. The exact exception remains unverified. Do not claim an image fix resolves this completion failure; investigate it separately before changing retry behavior. - -## Goals and non-goals - -Keep `latest` as the operator's configured input and publication workflow. Each new Studio MPA must bind the contents selected for that creation, while a retry must retain its original selection. Verify an entirely new MPA through Studio and inspect its actual Worker contents. Do not change old MPA sessions, calendar authorization, Agent/Worker business code, cloud cache settings, or delete failed resources. Standalone CLI provisioning remains compatible. - -## Scenarios and requirements - -- **FR-1:** Given Studio latest inputs, when a new deployment identity is prepared, resolve each supported CR image's current manifest and create a deterministic content snapshot tag in the same repository. Preserve the original latest tag. -- **FR-2:** Given a recorded image selection, when the same creation is retried, reuse that selection even if latest has moved. Reject changed requested inputs. Do not silently rewrite pre-upgrade unfinished Worker/Runtime request hashes. -- **FR-3:** Given denied registry access, missing manifests, invalid digests, conflicting snapshot tags, or unsupported latest registries, fail with bounded redacted diagnostics before Worker/Runtime creation. Never fall back to an unverified latest deployment. -- **FR-4:** Given a new MPA, completion requires the normal Runtime deployment finalization and application readiness. E2E additionally inspects a newly allocated Worker session's actual release markers/hashes; platform Ready is insufficient. -- **FR-5:** Given a partial deployment failure, retain resources and original intent. Diagnose the failure without forwarding child stderr, secrets, or arbitrary exception text. - -## Design and contracts - -For Studio-owned fresh deployments only, add a focused internal image-resolution helper beside managed provisioning. Use existing provider-aware AgentKit CR authentication and HTTP dependencies. Restrict trusted registry hosts to the configured provider's supported CR domains; validate repository/tag/digest syntax, use HTTPS with redirects disabled and bounded timeouts, and never accept an authentication challenge pointing at an arbitrary host. - -Read the latest manifest with explicit supported OCI/Docker media types, verify its SHA-256 against its declared digest, and publish exactly those bytes as `mpa-sha256-<64 lowercase hex digits>` in the same repository through the registry distribution API. If the snapshot tag exists, verify its digest rather than overwriting conflicting content. Re-read the snapshot tag before use. The source latest tag is never deleted, replaced, or moved by this operation. Snapshot tags are durable release references; automatic garbage collection is outside scope. - -Persist requested reference, selected digest, and deployment reference in the existing native per-agent registry record under its deployment lock before CreateTool/CreateRuntime. No table/schema migration is needed. Both Runtime and Worker use their selected snapshot references. Subsequent retries validate requested references and reuse persisted deployment references. Fail closed for ambiguous legacy pending state; do not repair old creations implicitly. Registry record state is the selection authority; Studio task/browser inputs continue to show latest. A snapshot selects latest at creation time, not continuously for an already running MPA. - -This is a deliberate distinction: the user-facing input remains latest, while the underlying Tool/Runtime image URL will be the content snapshot tag. It does not satisfy a requirement that the underlying platform URL itself must literally end in `:latest`; that alternative requires a platform-supported cache invalidation/fresh-source contract that has not been established. - -Cloud writes are scoped to the selected repository and newly created deployment identity. Registry pull and push permissions are required; do not modify IAM automatically. There are no new dependencies or public HTTP request fields. No frontend behavior change is planned. Cancellation, existing request tokens, network/DB ownership validation, and unknown-outcome recovery remain intact. API/transport errors remain explicit; only safe operation/category/code/request ID may reach Studio. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | Verification | Current result | -| --- | --- | --- | --- | --- | -| FR-1/FR-3 | T-1: implement trusted CR manifest snapshot helper | AC-1: same bytes/digest, latest untouched; negative access/format/conflict cases fail | Regression tests first; helper tests with mocked CR/HTTP | pass; local evidence below | -| FR-2 | T-2: wire selection into Studio-owned provisioning and native record | AC-2: retry after latest moves uses original snapshot; independent MPA resolves new snapshot; legacy pending state preserved | Managed service/Worker/Runtime integration regressions | pass; local evidence below | -| FR-4/FR-5 | T-3: isolate actual Studio completion exception and fix only a proven cause | AC-3: failing regression reproduces confirmed cause; normal final update and readiness complete | Targeted creation/deployment tests and safe live diagnostics | not_run; cause not established | -| All | T-4: validate and publish corrected Studio | AC-4: changed-line coverage above 95%, required gates honestly reported, original Studio auth/env retained | pytest coverage, Ruff, changed-file Pyright, pre-commit all-files, broader regression as affected; supported Studio update | pass; deployed function revision 11; evidence below | -| FR-4 | T-5: create a new MPA in the existing authorized Chrome session | AC-5: Studio task succeeds; Runtime readiness succeeds; selected manifest maps to actual new Worker code; safe functional execution reaches terminal result | Browser evidence plus official cloud API/source probes; no human IM message | pass; new MPA and bounded Worker execution verified; evidence below | - -## Risks and recovery - -Snapshot publication requires registry write permission and consumes tag metadata. Multi-platform media types must remain intact; do not rewrite a manifest or silently select a different architecture. Verify that AgentKit accepts the snapshot tag in a bounded new resource experiment before making the feature available. Registry tag immutability is not presumed: detect conflicting existing content and validate on retry before further deployment writes. Keep existing failed resources, and roll Studio back through its supported release mechanism if acceptance fails. Do not claim a change to the platform's internal cache implementation. - -## Review and delivery record - -Direct design review performed because no review-spec skill is available: checked provider boundaries, secret handling, manifest integrity, cancellation/timeouts, retry identity, legacy pending compatibility, latest input semantics, and bilingual equivalence. Resolved: the user approved latest-as-input with content snapshots as deployment URLs on 2026-10-21. The exact separate deployment-finalization exception remains a diagnosis task, not a presumed fix. Implementation and local verification are recorded below; deployment and live MPA validation remain separate. The user authorized implementation, deployment and new-MPA validation on 2026-10-21. - -## 2026-10-21 approval and implementation scope - -User instruction: “修改一下studio 部署一下试试”. Both a unique tag and an automatically computed `mpa-sha256-` tag were live-tested against published Worker source, establishing feasibility. The source baseline is synchronized to `5e5deb6a`. Scope covers snapshot resolution, durable image selection in the native deployment record, paired component contracts and tests, BP Studio update and new creation validation. The separate creation-finalization failure remains unexplained; no speculative fix is included. Preserve resources and report any live failure. Security and bilingual equivalence were reviewed directly because review-spec is unavailable. - -## 2026-10-21 local implementation and verification - -Implemented `managed/image_snapshot.py`, invoked under the existing deployment lock in `service.provision`. Native JSON persists requested/resolved images; initial preparation pins both images, while retries only verify the saved snapshots. Non-Studio CLI and fixed tags are unchanged. Parse the CR instance name from the region-qualified host, retain STS authentication, and verify exact manifest bytes, digest and media type. Tests cover the registry protocol, provider/redirect boundaries, authentication, conflicts/corrupt state, failure/cancellation, retry and Worker/Runtime payload integration. - -Environment: Python 3.12.12; `uv sync --frozen --extra dev --python 3.12`; dependencies and lock file unchanged. - -- pass: `pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -n 2 -q`: 1220 passed. -- pass: 47 snapshot/integration cases; module line/branch coverage 100%; incremental production lines 130/130 (100%). Coverage preloads `pydantic_settings` and SDK Runtime types before pytest to avoid dependency import-identity errors. The same errors reproduce on unmodified main and parallel coverage runs; no business-code workaround was added. -- pass: `npm --prefix frontend test`: 8 files, 118 cases; `npm --prefix frontend run build`. -- pass: changed-file Ruff/Pyright; `pre-commit run --all-files` including secret scanning; `git diff --check`. -- pass: the same implementation resolved and reread Agent/Worker snapshots in the authorized real BP account, preserving latest. -- pass: Studio update, browser MPA creation, application readiness and bounded Worker execution; live evidence below. Independent Codex/Pi/Harness, the full A2A regression suite, domestic live deployment, calendar and human IM are outside this validation. - -Direct review covered dependencies/public fields, credential host boundaries, native locking/durable selection, fixed-tag side effects after cancellation, legacy pending rejection, CLI compatibility and bilingual equivalence. Snapshot tags are retained without automatic garbage collection. - - -## 2026-10-21 live deployment and new MPA verification - -Implementation commit `4f527251a181ac5b3902fad7c97eb3315c9215e0` was pushed to `polaris/czh/studio-latest-image-refresh`, based on `origin/main` `5e5deb6a`. The supported `veadk studio update --provider byteplus --vefaas-app-name czh-agentkit-studio --region ap-southeast-1 --project default --path --skip-cronjob-scheduler` completed at 16:46:41 Asia/Shanghai. Studio application `3463b27bd210` / function `cst7puxr` is at stable function revision **11**, release status `done` (application revision **8** is a separate counter). All existing environment values and the cronjob scheduler were retained. The deployed source bundle's nested VeADK wheel was compared with local production-module hashes; both snapshot resolver and service matched. The existing Chrome login remained valid. - -The actual Studio UI created **`studio-snapshot-1021-a`** and displayed creation success. Its native deployment record is `ready`, and preserves both original `:latest` inputs plus their selected references: - -| Resource | Observed result | -| --- | --- | -| Agent | `mi-9dde1915ce7a577984364e87` | -| Runtime | `r-yexqhuek8whyrrzwphfg`, Ready, version 2; authenticated `/health` and `/readiness` both HTTP 200 | -| Worker Tool | `t-yexqhssyyohyrrzwi40r`, Ready | -| Runtime image | `mpa/mpa_agent:mpa-sha256-63d32384da817cb38b55b6dfeda7d2cd8a851e9d119d18abb30ee8632aec599f` | -| Worker image | `mpa/mpa_codex_worker:mpa-sha256-1e40dd91e8c10b067a31ef4113eb9829575baf9101cf9d5e6103144b9491f862` | -| Worker function / revision / actual image ID | `3a0e93hw` / 1 / **`ayk6eadc2t`**, distinct from old `vollyz6n0i` | -| Actual Worker source | 215 Python files; fingerprint `30c2b491f1fe460e731627bda9f40f160a6d26f4d7d8398ff91a3056a2943490`, matching the published new Worker; `private_user.py` present | -| Business PG / SkillSpace / OpenViking | `snug-lime-8e01240u`, database ready / `ss-yexqhud5ogdlwzgmriry` / `ov-4dcc6e25514a5979`, READY | -| Functional smoke | Studio session `ef433e28-4172-448d-bef8-6533efdca55f`; Worker ran `/bin/bash -lc 'printf STUDIO_SNAPSHOT_1021_OK'`, exit 0, and the completed UI answer returned `STUDIO_SNAPSHOT_1021_OK` | - -Both image references use prefix `agentkit-platform-3001037806-ap-southeast-1.cr.bytepluses.com/`. The dedicated source-probe session `s-yexqhwxv5shyrrzwfm4d` was deleted. The new MPA and successful Studio conversation were retained for review; its task Worker session is `s-yexqifihhcuexbvqlils` (created at 16:58:57). Safe deployment, native-record, actual-source and screenshot evidence is retained locally outside Git; credentials and raw deployment records were not committed. - -Limits: this verifies current Studio completion and a bounded Worker task, not the unknown cause of the earlier unrelated completion exception (T-3), calendar authorization or the full A2A suite. The Studio role resolved/reused snapshots created by the authorized registry preflight; creation of a previously absent snapshot tag under that role was not independently exercised. Domestic cloud deployment and the running Agent container's source fingerprint were not inspected. Existing MPAs keep their previous image selections; only new Studio deployment identities select the latest content. diff --git a/prd-spec/bugfixes/studio-latest-image-refresh/2026-10-20-latest-image-snapshots.zh.md b/prd-spec/bugfixes/studio-latest-image-refresh/2026-10-20-latest-image-snapshots.zh.md deleted file mode 100644 index d1eadfd1b..000000000 --- a/prd-spec/bugfixes/studio-latest-image-refresh/2026-10-20-latest-image-snapshots.zh.md +++ /dev/null @@ -1,103 +0,0 @@ -# Studio latest 镜像内容快照 - -> 当前实现已由批准的[只读解析设计](2026-10-21-readonly-tag-resolution.zh.md)替代。下文保留原仓库写入设计及历史部署证据。 - -[English](2026-10-20-latest-image-snapshots.md) - -- 变更 ID:`studio-latest-image-refresh` -- 创建:2026-10-20;修订:2026-10-21 -- 状态:approved;2026-10-21 用户确认 digest 快照方案,并授权修改、部署 Studio 和创建验证 MPA。 -- 组件:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md)。 -- 基线:`86c93529969e6dde86890b30f7e407bfc32ff5ea`,与已部署 Studio 的源码归档一致。 - -## 证据与原因 - -仓库当前 Worker `latest` manifest 与最近发布版本一致。本地拉取和三个源码文件哈希确认代码是新版。AgentKit CreateTool 收到正确 latest 地址;CloudTrail 证明下游 CreateFunction 请求也携带该地址,未携带旧 image ID。然而生成的 veFaaS revision 及新 Session 仍使用旧内部 image ID。预热成功不会更新已经发布的绑定。刷新函数 source 并发布新 revision 能修好个别测试函数,但不能修好后续 CreateTool。 - -已经证明的故障边界是 veFaaS 创建时的 source 同步/解析;服务内部缓存键、TTL 和选择算法尚未确认。Studio 当前仅接受平台 Ready,没有确认镜像内容新旧。目前 API 拒绝 digest 格式的预热地址,也拒绝将预热后的内部镜像地址作为新 source,因此这两种地址都不能视为已建立的受支持部署机制。 - -另一个 Studio 任务失败后,其 Runtime 后来达到 Ready V1。确切异常尚未确认。不能宣称镜像修复同时解决了部署收尾失败;必须单独调查,再决定是否修改重试行为。 - -## 目标与非目标 - -保留用户配置 `latest`、持续向 latest 推送的工作方式。每个全新 Studio MPA 必须绑定本次创建选定的内容,同一次创建重试必须保留原选择。通过 Studio 创建全新 MPA 并检查实际 Worker 内容。不修改旧 MPA Session、日历授权、Agent/Worker 业务代码和云缓存配置,不删除失败资源。保持独立 CLI 部署兼容。 - -## 场景与需求 - -- **FR-1:** Studio 输入 latest,新部署身份准备时,读取受支持 CR 镜像的当前 manifest,在同仓库生成确定性的内容快照 tag,保留原 latest。 -- **FR-2:** 已记录镜像选择时,同一次创建重试即使 latest 移动也复用原选择;拒绝更换原输入;不静默改写升级前未完成 Worker/Runtime 的请求哈希。 -- **FR-3:** 仓库拒绝访问、manifest 缺失、digest 非法、快照 tag 冲突或 latest 仓库不受支持时,在创建 Worker/Runtime 前用有界、脱敏诊断明确失败,不回退到未核验的 latest 部署。 -- **FR-4:** 新 MPA 完成要求正常部署收尾和应用 readiness;E2E 还必须检查新 Worker Session 的实际版本标记/源码哈希,不能仅依据平台 Ready。 -- **FR-5:** 部分部署失败时保留资源和原创建意图;诊断不得转发子进程 stderr、密钥或任意异常文本。 - -## 设计与契约 - -仅对 Studio 所有的新部署,在 managed 部署逻辑旁新增一个职责明确的内部镜像解析模块。复用已有、区分 provider 的 AgentKit CR 认证及 HTTP 依赖。可信仓库主机限制为当前 provider 支持的 CR 域名;校验仓库、tag、digest 格式;HTTPS 请求禁止重定向并设置有界超时;不能根据认证挑战向任意主机发送凭据。 - -显式接受受支持的 OCI/Docker media type,读取 latest manifest,计算 SHA-256 并核对声明的 digest。通过 registry distribution API,将原始字节发布为同仓库的 `mpa-sha256-<64 位小写十六进制>` tag。若快照已存在,应核验 digest,不覆盖内容冲突的 tag;使用前重新读取确认。不会删除、覆盖或移动原 latest。快照 tag 是持久发布引用,自动清理不在本次范围。 - -在现有原生逐 Agent 注册记录中,持部署锁、于 CreateTool/CreateRuntime 前持久化原输入、选中 digest、部署引用;无需表结构迁移。Runtime 和 Worker 都使用各自快照引用。重试验证原输入并复用记录的部署引用。对升级前含糊的 pending 状态明确失败,不隐式修复旧创建。注册记录是镜像选择的权威来源;Studio 任务/浏览器输入继续显示 latest。快照选择的是创建时的 latest,不会持续改变已运行 MPA。 - -这里有明确语义区别:用户输入继续是 latest,底层 Tool/Runtime 的镜像地址会是内容快照 tag。如果要求底层平台地址本身必须始终以 `:latest` 结尾,则本方案不满足;那需要平台提供已验证的缓存失效/强制拉新契约,目前尚未建立。 - -云写操作限制在选定镜像仓库和新部署身份。需要仓库拉取和推送权限,不自动修改 IAM。不新增依赖和公开 HTTP 请求字段,不计划修改前端行为。保留取消处理、既有请求 token、网络/数据库归属验证和未知结果恢复。API/传输错误明确暴露,仅允许安全的操作名、类别、错误码和 request ID 进入 Studio。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | 验证 | 当前结果 | -| --- | --- | --- | --- | --- | -| FR-1/FR-3 | T-1:可信 CR manifest 快照模块 | AC-1:字节/digest 一致、latest 未变;访问/格式/冲突负例明确失败 | 先写失败回归;模拟 CR/HTTP 模块测试 | pass;本地证据见下文 | -| FR-2 | T-2:接入 Studio 部署与原生记录 | AC-2:latest 移动后同身份重试保留原快照;独立 MPA 解析新快照;保留旧 pending 状态 | managed service/Worker/Runtime 集成回归 | pass;本地证据见下文 | -| FR-4/FR-5 | T-3:定位实际 Studio 收尾异常,只修已证实原因 | AC-3:失败回归重现根因;正常最终更新及 readiness 完成 | 定向创建/部署测试和安全在线诊断 | not_run;根因尚未建立 | -| 全部 | T-4:验证并发布 Studio | AC-4:增量覆盖率高于 95%;如实报告门禁;保留 Studio 原认证/环境 | pytest coverage、Ruff、改动文件 Pyright、全文件 pre-commit、按影响扩大回归;支持的 Studio update | pass;已部署函数 revision 11,证据见下文 | -| FR-4 | T-5:复用已授权 Chrome 会话创建全新 MPA | AC-5:Studio 任务成功、Runtime readiness 成功、选中 manifest 对应实际新 Worker 代码、安全功能执行终态 | 浏览器证据及官方云 API/源码探针;不发送真人 IM 消息 | pass;新 MPA 与有界 Worker 执行已验证,证据见下文 | - -## 风险与恢复 - -生成快照需要仓库写权限并增加 tag 元数据。多架构 media type 必须完整保留,不改写 manifest,不静默切换架构。开放该能力前,先用有界新资源实验确认 AgentKit 接受快照 tag。不假定 registry tag 不可变:已有内容冲突必须检测,重试进一步写部署资源前重新校验。保留失败资源;验收失败时通过支持的发布机制回滚 Studio。不宣称修改了平台内部缓存实现。 - -## 审查与交付记录 - -没有可用 review-spec 技能,已直接审查 provider 边界、凭据保护、manifest 完整性、取消/超时、重试身份、旧 pending 兼容、latest 输入语义及双语一致性。已解除阻塞:用户明确同意 latest 作为输入、内容快照作为底层部署地址。独立的部署收尾异常仍是待定位任务,不预设修法。2026-10-21 用户已批准方案、代码修改、部署及新建验证;见下方交付记录。 - -## 2026-10-21 批准与实施范围 - -用户指令:“修改一下studio 部署一下试试”。已实测唯一 tag 与按 latest digest 自动生成的 `mpa-sha256-` 都可让新 Worker 采用已发布源码;因此接受此设计作为实现基线。源码基线同步至 `5e5deb6a`。实施范围为镜像快照解析、原生部署记录中的持久版本选择、受影响双语契约和测试、BP Studio 更新与新建验证。既有部署收尾失败的根因仍未知,不预设或扩大修复;若新建仍失败,保留资源并如实报告。安全及双语一致性已直接复核;review-spec 技能不可用。 - -## 2026-10-21 本地实现与验证 - -实现 `managed/image_snapshot.py`,由 `service.provision` 的既有部署锁调用;原生 JSON 保存请求/解析结果,首次准备固定两个镜像,重试仅验证原快照。非 Studio CLI 和固定 tag 不变。CR 实例名从带地域的仓库域名解析,认证携带 STS;原始 manifest 字节、digest、media type 均核验。测试覆盖仓库协议、跨平台/重定向拒绝、认证、冲突/损坏状态、失败/取消、重试及 Worker/Runtime payload 接入。 - -验证环境:Python 3.12.12,`uv sync --frozen --extra dev --python 3.12`;未修改依赖/锁文件。 - -- pass:`pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -n 2 -q`,1220 passed。 -- pass:47 个快照/接入用例;模块行/分支覆盖率 100%;增量生产代码行覆盖 130/130(100%)。覆盖执行在启动 pytest 前预加载 `pydantic_settings` 与 SDK Runtime 类型,规避依赖导入身份问题;相同问题在未修改主分支和带 coverage 的并行执行中均可重现,未通过改业务代码掩盖。 -- pass:`npm --prefix frontend test`,8 文件、118 用例;`npm --prefix frontend run build`。 -- pass:改动 Python 的 Ruff/Pyright;`pre-commit run --all-files`(含密钥扫描);`git diff --check`。 -- pass:已授权 BP 真实账号上调用同一实现,解析并回读 Agent/Worker digest 快照,latest 保持不变。 -- pass:Studio 更新、浏览器新建 MPA、应用就绪和有界 Worker 执行;在线证据见下文。独立 Codex/Pi/Harness、完整 A2A 回归、国内真实部署、日历/真人 IM 不属于本次验证。 - -直接复核:无新增依赖/公开字段;HTTP 凭据主机边界、原生锁/持久选择、取消后固定 tag 副作用、旧 pending 拒绝、CLI 兼容及双语一致性均已检查。快照 tag 保留,不自动清理。 - - -## 2026-10-21 在线部署与新 MPA 验证 - -实现提交 `4f527251a181ac5b3902fad7c97eb3315c9215e0` 已推送至 `polaris/czh/studio-latest-image-refresh`,基于 `origin/main` `5e5deb6a`。支持的 `veadk studio update --provider byteplus --vefaas-app-name czh-agentkit-studio --region ap-southeast-1 --project default --path --skip-cronjob-scheduler` 于 Asia/Shanghai 16:46:41 完成。Studio 应用 `3463b27bd210` / 函数 `cst7puxr` 的稳定函数 revision 为 **11**,发布状态 `done`(应用 revision **8** 是独立计数)。所有原环境变量值和 cronjob scheduler 均保留。下载部署源码包,在内嵌 VeADK wheel 中比较生产模块哈希,快照解析与 service 均匹配本地。原 Chrome 登录仍有效。 - -实际 Studio 页面创建 **`studio-snapshot-1021-a`** 并显示创建成功。原生部署记录为 `ready`,同时保留两个原始 `:latest` 输入及选中引用: - -| 资源 | 实测结果 | -| --- | --- | -| Agent | `mi-9dde1915ce7a577984364e87` | -| Runtime | `r-yexqhuek8whyrrzwphfg`,Ready,version 2;带鉴权 `/health`、`/readiness` 均 HTTP 200 | -| Worker Tool | `t-yexqhssyyohyrrzwi40r`,Ready | -| Runtime 镜像 | `mpa/mpa_agent:mpa-sha256-63d32384da817cb38b55b6dfeda7d2cd8a851e9d119d18abb30ee8632aec599f` | -| Worker 镜像 | `mpa/mpa_codex_worker:mpa-sha256-1e40dd91e8c10b067a31ef4113eb9829575baf9101cf9d5e6103144b9491f862` | -| Worker 函数 / revision / 实际 image ID | `3a0e93hw` / 1 / **`ayk6eadc2t`**,与旧 `vollyz6n0i` 不同 | -| 实际 Worker 源码 | 215 个 Python 文件;指纹 `30c2b491f1fe460e731627bda9f40f160a6d26f4d7d8398ff91a3056a2943490`,匹配已发布新 Worker;存在 `private_user.py` | -| 业务 PG / SkillSpace / OpenViking | `snug-lime-8e01240u`,数据库 ready / `ss-yexqhud5ogdlwzgmriry` / `ov-4dcc6e25514a5979`,READY | -| 功能冒烟 | Studio 会话 `ef433e28-4172-448d-bef8-6533efdca55f`;Worker 执行 `/bin/bash -lc 'printf STUDIO_SNAPSHOT_1021_OK'`,退出码 0,已完成的页面回答返回 `STUDIO_SNAPSHOT_1021_OK` | - -两个镜像地址前缀均为 `agentkit-platform-3001037806-ap-southeast-1.cr.bytepluses.com/`。专用源码探针 Session `s-yexqhwxv5shyrrzwfm4d` 已删除。新 MPA 和成功的 Studio 对话保留供核查,其任务 Worker Session 为 `s-yexqifihhcuexbvqlils`(16:58:57 创建)。脱敏部署、原生记录、实际源码和截图证据保留在 Git 外的本地目录;未提交凭据或原始部署记录。 - -边界:此次证明当前 Studio 创建收尾和有界 Worker 任务成功,未定位先前独立收尾异常的未知根因(T-3),未验证日历授权或完整 A2A 套件。Studio 角色解析并复用了已授权仓库预检生成的快照;尚未单独验证该角色创建一个原本不存在的快照 tag。未检查国内云真实部署和运行中 Agent 容器源码指纹。既有 MPA 保留旧镜像选择;只有新的 Studio 部署身份会选取当时最新内容。 diff --git a/prd-spec/bugfixes/studio-latest-image-refresh/2026-10-21-readonly-tag-resolution.md b/prd-spec/bugfixes/studio-latest-image-refresh/2026-10-21-readonly-tag-resolution.md deleted file mode 100644 index e43df9124..000000000 --- a/prd-spec/bugfixes/studio-latest-image-refresh/2026-10-21-readonly-tag-resolution.md +++ /dev/null @@ -1,77 +0,0 @@ -# Studio read-only latest tag resolution - -[中文版](2026-10-21-readonly-tag-resolution.zh.md) - -- Change ID: `studio-latest-image-refresh`; revised 2026-10-21. -- Status: approved for implementation/deployment/live verification by the user's instruction to change and verify the read-only matching scheme. -- Supersedes the registry-writing part of [the original snapshot design](2026-10-20-latest-image-snapshots.md). Existing deployment selections and historical evidence remain valid. -- Component: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), CON-1/3/6/8/9. - -## Evidence and scope - -An anonymous, credential-free probe of both built-in public registries confirmed that Agent and Worker repositories support repository-scoped anonymous pull tokens, complete tag listing, and exact manifest-digest matching. BP matches include `master-a72afa274fe5-20261020` and `master-31fc42b4c521-20261021-1205`; domestic matches include `master-d8665d03578a-20261016` and `master-a63685214d07-20261016`. This proves read access and matching at probe time, not Runtime adoption or a registry-enforced immutability policy. - -The original implementation requires source-registry write permission and therefore is unsuitable for ordinary users of platform-owned public repositories. Replace publication with anonymous, read-only selection of an existing tag in the same repository. Do not introduce CI, image copies, dependencies, frontend fields, IAM changes, or changes to existing MPAs/standalone CLI. Authenticated private `latest` repositories are outside this public-discovery contract; explicit fixed tags retain their behavior. - -## Requirements and design - -- **RO-1:** Read latest, verify raw Docker/OCI manifest or index bytes against `Docker-Content-Digest`, and list existing tags using only HTTPS GET and anonymous `repository::pull` tokens. Never read cloud credentials, netrc, or Docker configuration; disable ambient requests authentication. Authentication challenges must use the same registry host/HTTPS/default port without userinfo or redirects. -- **RO-2:** Read all bounded tag pages before selection. Accept only valid tag names. Prefer an already-published `mpa-sha256-` tag, then consider other non-latest tags in descending lexical order. Verify candidate manifest bytes; a removed candidate (404) may be skipped, but invalid/authentication/transport responses fail. Use the first exact digest match. A tag's non-overwrite policy is the publisher's responsibility; a name alone does not prove immutability. -- **RO-3:** Fail explicitly if no existing version tag matches; never publish a tag or silently fall back to latest. A publisher must retain a corresponding non-overwritten version tag whenever latest changes. Selection means the latest observed at initial resolution; movement afterward does not change that deployment intent. -- **RO-4:** Keep native `image_snapshots.requested/selected` and both-image persistence under the existing lock. New selection accepts any validated same-repository non-latest tag. Existing digest-named selections remain compatible. Retry reads only the saved tag and verifies its digest; changed/missing saved content fails, never reselects. Keep old unfinished-intent rejection and standalone CLI/fixed-tag compatibility. -- **RO-5:** Bound each response to 1 MiB, enumeration to 20 pages / 1000 tag entries, and resolution to a 120-second budget checked before HTTP calls and while consuming bodies (an in-flight read can run until its bounded read timeout). Per-call connect/read limits remain 10/30 seconds or the remaining budget. Pagination must remain on the exact same repository tags-list endpoint, have only `n`/`last` parameters, no credentials/fragments, and no cycles; malformed or cross-origin links fail. Cancellation retains no partial persisted selection; the existing subprocess supervisor handles termination. Errors remain sanitized. - -The owning production module remains `managed/image_snapshot.py` for compatibility; service integration and native schema do not change. Update its protocol tests, bilingual component/operator docs, the frontend operator note, and PR description. The initial snapshot design is retained as historical evidence, explicitly superseded. - -## Review and approval - -No review-spec skill is available; reviewed directly before code changes: public/ordinary-user permission boundary, anonymous token scope, host/path/pagination validation, raw digest integrity, malformed/large responses, deadlines, cancellation, mutable/deleted tags, legacy saved selections, retry idempotency, CLI isolation, test mapping, and English/Chinese equivalence. User approval covers implementation, update of PR #1178, BP Studio deployment, and a new isolated MPA test. No additional permission expansion or repository writes are part of discovery. - -## Tasks, acceptance and verification - -| Requirement | Task / acceptance | Verification | Result | -| --- | --- | --- | --- | -| RO-1/2/3/5 | Anonymous exact-match selection, strict bounded GET-only protocol, explicit failures | First write failing regression; fake HTTP protocol/security/deadline cases; live anonymous resolution | pass; evidence below | -| RO-4 | Persist both selections under lock; retry old/new saved tags; preserve CLI and fixed tags | Managed service/integration and CLI regressions | pass; evidence below | -| All | Incremental coverage above 95%; quality/security gates and synchronized docs | Focused branch coverage, managed/CLI regression, changed-file Ruff/Pyright, all-files pre-commit, frontend test/build | pass; evidence below | -| All | Deploy exact tested source and create a fresh Studio MPA | Supported Studio update, deployed module hashes, UI success, native ready state and selected tags, application readiness, actual Worker source/image ID, bounded completed Worker task | pass for deployment/creation/image adoption and fresh-session smoke; initial functional failure retained below | - -## Risks and recovery - -Read access and tag policies may change. Missing matching tags or registry access must produce a safe diagnostic rather than create a stale deployment. The finite enumeration/time budgets may reject a very large or slow repository. Tags are not cryptographically immutable; publisher discipline remains necessary and retry revalidation detects changes. Public network egress is required; discovery uses direct HTTPS and deliberately ignores environment proxies and netrc. Preserve failed cloud resources and the original persisted intent; rollback Studio through its supported release path if necessary. Do not claim cache invalidation or a fix for the earlier unrelated completion exception. Leave live outcomes unverified until observed. - -## 2026-10-21 local and anonymous live verification - -The first three GET-only regressions failed against the previous credential-dependent implementation before production changes. The updated suite has **93 passed**; resolver line and branch coverage is **100%**. Relative to origin/main, all **173/173 added executable production lines** (resolver plus service integration) are covered. Coverage execution preloads `pydantic_settings` and SDK Runtime types before pytest to avoid the already-established upstream import-identity issue; no dependency or business-code workaround was introduced. - -- pass: `.venv/bin/python -m pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -n 2 -q`, **1266 passed**. -- pass: frontend tests **118 passed**, production build, changed-file Pyright (0 errors), all-files pre-commit including Ruff/format/secret scanning, and `git diff --check`. -- pass: the production resolver performed real anonymous selection and saved-tag revalidation for Agent and Worker on both providers, with cloud credential access replaced by a function that raises. BP selected the existing digest-named tags; domestic selected the existing master tags described above. No tag was created or modified. Safe evidence is stored locally outside Git. -- pass: updated BP Studio deployment, new MPA creation, actual Worker code and a fresh-session command smoke, with the initial functional failure preserved below. Full A2A, Codex/Pi/Harness suites, domestic deployment, calendar and human IM remain outside this bounded validation. - -## 2026-10-21 deployed source, new MPA and functional limits - -Commit `09e124e8be59ae379c6f1a7e87aba2d568cc1282` was pushed to the existing PR #1178 and deployed using the supported BP Studio update path with `--skip-cronjob-scheduler`. Application `3463b27bd210` / function `cst7puxr` reached stable function revision **12**, release status `done`, at 17:46:56 Asia/Shanghai. All existing environment values and scheduler settings were preserved. The deployed wheel resolver hash is `5028f8feb77cadd5b615f0248a23783c5f7798fdb31ccda3d3f8196a6448cc06`; service hash is `a1e21d2dab3e34bcd5d05860eb4d98b3e1e22ef4f8b3e38a4ce5546c423688d1`; both match the tested checkout. - -The existing authorized Chrome session created **`studio-readonly-1021-a`** and displayed creation success. Native state is `ready` and retains both requested latest references plus their selected existing tags: - -| Resource | Observed result | -| --- | --- | -| Agent | `mi-5c74df2575dc5e89ad445d76` | -| Runtime | `r-yexqmg6z9cy9bkt9hm42`, Ready, version 2; authenticated health/readiness HTTP 200 | -| Worker Tool | `t-yexqlmcetcy9bkt9ihdf`, Ready; created at 17:48 and reused on the same-request retries | -| Agent selected tag | `mpa_agent:mpa-sha256-63d32384da817cb38b55b6dfeda7d2cd8a851e9d119d18abb30ee8632aec599f` | -| Worker selected tag | `mpa_codex_worker:mpa-sha256-1e40dd91e8c10b067a31ef4113eb9829575baf9101cf9d5e6103144b9491f862` | -| Worker function / revision / actual image ID | `ptlrrzjl` / 1 / **`ayk6eadc2t`**, not old `vollyz6n0i` | -| Actual Worker source | 215 Python files; fingerprint `30c2b491f1fe460e731627bda9f40f160a6d26f4d7d8398ff91a3056a2943490`, matching the published Worker; private-user module present | -| Business PG / SkillSpace / OV | `sunny-bluet-3db026cb`, aidb ready / `ss-yexqmg5kowdlwzgmqn9d` / `ov-f54074c915387e15`, READY | -| Successful bounded smoke | Fresh Studio session `8606260b-eeb7-4752-9782-0a860fc6fd07` ran `/bin/bash -lc 'printf STUDIO_READONLY_1021_FRESH_OK'`; actual stdout matched, exit 0, and the completed answer displayed both | - -The two image references use `agentkit-platform-3001037806-ap-southeast-1.cr.bytepluses.com/mpa/`. These tags already existed; discovery published nothing. The dedicated source-probe session `s-yexqlugkxsuexbvqjn6i` was deleted. Safe native/deployment/API evidence and creation/functional screenshots are retained locally outside Git; no secrets or raw cloud records were committed. - -Two independent conditions were observed and must not be hidden by the successful fresh-session smoke: - -1. Creation task `49556dec-276c-4053-9507-ed08cdb98bf3` initially hit OV `InvalidParameter`. Overseas CloudTrail records for requests `2026102109482052668D9A8D2903007881` and `20261021095118D4C86EA35A3890F6A34D` establish that collection count exceeded the account maximum **20**; the existing UI generalized this as a model/configuration rejection. After explicit user approval, only the previous test `studio-snapshot-1021-a` library `ov-4dcc6e25514a5979` was ownership-checked and deleted; deletion succeeded and a subsequent Get confirmed collection-not-found. That old test MPA now has no usable OV memory library. The same creation request then succeeded, retaining its Worker and both image selections. No OV code, model setting, quota or Runtime configuration workaround was introduced. -2. Initial Studio session `de1ce841-0f42-4087-be22-8845b1014798` did **not** execute the requested printf. Its UI reported `inactive_worker_dispatch` and `harness invocation plan update was not persisted`; it ended with a failure explanation. The durable harness/control state reported `completed`, which does not prove command execution. Source inspection locates the former in the Agent MainRuntime active-grant check and the latter in guarded plan persistence; the root cause remains unproven and neither path was changed here. No pause mutation was issued because inspection found the task already completed. A separate fresh session with a bounded stop-on-error prompt subsequently executed the command successfully. This is a creation/image/fresh-session smoke pass, **not** proof that all initial-session or explicit Worker-dispatch paths are reliable. - -Remaining limits: full A2A/calendar/IM and domestic Runtime deployment are unverified; publisher tag retention/non-overwrite remains required. The successful new MPA and both functional conversations are retained for review. Existing MPAs are not upgraded. diff --git a/prd-spec/bugfixes/studio-latest-image-refresh/2026-10-21-readonly-tag-resolution.zh.md b/prd-spec/bugfixes/studio-latest-image-refresh/2026-10-21-readonly-tag-resolution.zh.md deleted file mode 100644 index 879e1c3df..000000000 --- a/prd-spec/bugfixes/studio-latest-image-refresh/2026-10-21-readonly-tag-resolution.zh.md +++ /dev/null @@ -1,77 +0,0 @@ -# Studio latest 只读标签解析 - -[English](2026-10-21-readonly-tag-resolution.md) - -- 变更 ID:`studio-latest-image-refresh`;修订:2026-10-21。 -- 状态:approved;用户已明确要求修改并验证只读匹配方案,授权实现、部署和在线验证。 -- 替代[原快照设计](2026-10-20-latest-image-snapshots.zh.md)中的仓库写入部分;既有部署选择和历史验证记录仍有效。 -- 组件:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-1/3/6/8/9。 - -## 证据与范围 - -不携带凭据的匿名探针已确认,两个内置公共仓库的 Agent、Worker 均支持仓库范围匿名 pull token、完整标签列表和 manifest digest 精确匹配。BP 匹配项包括 `master-a72afa274fe5-20261020`、`master-31fc42b4c521-20261021-1205`;国内包括 `master-d8665d03578a-20261016`、`master-a63685214d07-20261016`。这证明探测时可读和内容一致,不证明 Runtime 采用情况或仓库强制标签不可变。 - -原实现要求源仓库写权限,不适用于使用平台公共仓库的普通用户。改为匿名只读选择同仓库已有标签,不新增 CI、镜像搬运、依赖、前端字段、IAM 变更,不修改既有 MPA 或独立 CLI。需要鉴权的私有 latest 仓库不属于公共发现契约;显式固定 tag 保留原行为。 - -## 需求与设计 - -- **RO-1:** 读取 latest,校验原始 Docker/OCI manifest 或 index 字节与 `Docker-Content-Digest` 一致;只用 HTTPS GET 和匿名 `repository::pull` token 列举标签。绝不读取云凭据、netrc、Docker 配置,并禁用 requests 环境鉴权。认证挑战须为同仓库主机、HTTPS、默认端口,无用户信息及重定向。 -- **RO-2:** 先完整读取有界分页再选择。仅接受合法标签名;优先已有 `mpa-sha256-`,其次按标签字典倒序检查其他非 latest 标签。核验候选 manifest 字节;候选删除(404)可跳过,非法内容、鉴权及传输错误明确失败。采用第一个 digest 精确匹配项。标签不覆盖由发布方保证,不能凭名称宣称不可变。 -- **RO-3:** 无匹配版本标签时明确失败;绝不发布标签或静默回退 latest。每次移动 latest,发布方须保留对应的不覆盖版本标签。选择固定的是初次解析时观察到的 latest,其后变化不改变本次部署意图。 -- **RO-4:** 保留原生 `image_snapshots.requested/selected` 和既有锁内双镜像持久化。新选择可为校验通过的同仓库任意非 latest 标签,兼容原有 digest 命名快照。重试只读取保存的 tag 并核验 digest;已保存内容变更或缺失时失败,绝不重新选择。保留旧未完成意图拒绝规则以及独立 CLI、固定 tag 兼容性。 -- **RO-5:** 单响应限制 1 MiB,枚举限制 20 页 / 1000 个标签项,解析预算 120 秒,在 HTTP 调用前及读取响应块时检查(已在途读取可持续到其有界读取超时)。单请求连接/读取上限为 10/30 秒,或更小的剩余预算。分页必须保持同主机、同仓库 tags-list 路径,只允许 `n`/`last` 参数,无用户信息/片段和循环;非法或跨源链接失败。取消不持久化部分选择,由现有子进程 supervisor 处理终止。错误保持脱敏。 - -生产模块继续使用 `managed/image_snapshot.py` 以兼容现有接入,service 和原生表结构不变。更新协议测试、双语组件/操作文档、前端操作说明和 PR 描述。保留原快照设计作为历史证据,并明确标为已替代。 - -## 审查与批准 - -没有可用 review-spec 技能;改代码前直接审查了公共仓库/普通用户权限边界、匿名 token scope、主机/路径/分页校验、原始 digest 完整性、非法/大响应、超时预算、取消、可变/删除标签、旧持久选择、重试幂等、CLI 隔离、测试映射和双语一致性。用户批准涵盖实现、更新 PR #1178、BP Studio 部署以及新建隔离 MPA 测试。镜像发现不扩大权限、不写仓库。 - -## 任务、验收与验证 - -| 需求 | 任务 / 验收 | 验证 | 结果 | -| --- | --- | --- | --- | -| RO-1/2/3/5 | 匿名精确匹配,严格有界 GET 协议,明确失败 | 先写失败回归;模拟 HTTP 协议/安全/预算;真实匿名解析 | pass;证据见下文 | -| RO-4 | 锁内持久化两个选择,兼容新旧保存标签,保留 CLI/固定 tag | managed service/集成和 CLI 回归 | pass;证据见下文 | -| 全部 | 增量覆盖率高于 95%,质量/安全门禁和文档同步 | 定向分支覆盖、managed/CLI 回归、改动文件 Ruff/Pyright、全文件 pre-commit、前端测试/构建 | pass;证据见下文 | -| 全部 | 部署已测源码并创建全新 Studio MPA | 支持的 Studio update、部署模块哈希、页面成功、原生 ready/选中标签、应用就绪、实际 Worker 源码/image ID、有界任务完成 | 部署/创建/镜像采用及新会话冒烟 pass;首次功能失败保留如下 | - -## 风险与恢复 - -读取权限和标签策略可能变化;无匹配项或访问失败必须给出安全诊断,不能继续创建旧版本。有限枚举和时间预算可能拒绝过大或过慢仓库。标签不是密码学不可变,仍需发布方保证不覆盖,重试校验能发现内容变化。需要公共网络出站访问;发现采用直连 HTTPS,明确不读取环境代理和 netrc。失败保留云资源和原持久意图,必要时通过支持的发布路径回滚 Studio。不宣称缓存失效,也不宣称修复先前独立收尾异常。未观察到的在线结果保持未验证。 - -## 2026-10-21 本地及真实匿名验证 - -改生产代码前,前三个仅 GET 回归在原依赖凭据的实现上失败。更新后 **93 passed**,解析模块行/分支覆盖率 **100%**。相对 origin/main,解析模块及 service 接入新增的 **173/173 个可执行生产代码行**全部覆盖。覆盖执行在 pytest 前预加载 `pydantic_settings` 和 SDK Runtime 类型,规避已有上游导入身份问题,未引入依赖或业务代码绕过。 - -- pass:`.venv/bin/python -m pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -n 2 -q`,**1266 passed**。 -- pass:前端 **118 passed**、生产构建、改动文件 Pyright(0 errors)、全文件 pre-commit(含 Ruff/format/密钥扫描)和 `git diff --check`。 -- pass:使用生产解析模块,真实匿名选择及重试校验两个平台的 Agent/Worker;把云凭据入口替换为调用即抛异常的函数。BP 选择已有 digest 命名标签,国内选择上述已有 master 标签。没有创建或修改任何标签;脱敏证据在 Git 外本地保存。 -- pass:更新后的 BP Studio 部署、新 MPA 创建、实际 Worker 源码和新会话命令冒烟;首次功能失败记录保留如下。完整 A2A、Codex/Pi/Harness、国内真实部署、日历和真人 IM 仍不属于此次有界验证。 - -## 2026-10-21 部署源码、新 MPA 及功能边界 - -提交 `09e124e8be59ae379c6f1a7e87aba2d568cc1282` 已推送到原 PR #1178,通过支持的 BP Studio update 路径部署,并使用 `--skip-cronjob-scheduler`。应用 `3463b27bd210` / 函数 `cst7puxr` 于 Asia/Shanghai 17:46:56 达到稳定函数 revision **12**,发布状态 `done`。原环境变量值和 scheduler 设置均保留。部署 wheel 中解析模块哈希为 `5028f8feb77cadd5b615f0248a23783c5f7798fdb31ccda3d3f8196a6448cc06`,service 哈希为 `a1e21d2dab3e34bcd5d05860eb4d98b3e1e22ef4f8b3e38a4ce5546c423688d1`,均与已测源码一致。 - -复用已授权 Chrome 会话创建 **`studio-readonly-1021-a`**,页面显示创建成功。原生状态为 `ready`,保留两个请求的 latest 地址以及已选中的现有标签: - -| 资源 | 实测结果 | -| --- | --- | -| Agent | `mi-5c74df2575dc5e89ad445d76` | -| Runtime | `r-yexqmg6z9cy9bkt9hm42`,Ready,version 2;带鉴权 health/readiness HTTP 200 | -| Worker Tool | `t-yexqlmcetcy9bkt9ihdf`,Ready;17:48 创建,同一请求重试复用 | -| Agent 选中标签 | `mpa_agent:mpa-sha256-63d32384da817cb38b55b6dfeda7d2cd8a851e9d119d18abb30ee8632aec599f` | -| Worker 选中标签 | `mpa_codex_worker:mpa-sha256-1e40dd91e8c10b067a31ef4113eb9829575baf9101cf9d5e6103144b9491f862` | -| Worker 函数 / revision / 实际 image ID | `ptlrrzjl` / 1 / **`ayk6eadc2t`**,不是旧 `vollyz6n0i` | -| 实际 Worker 源码 | 215 个 Python 文件;指纹 `30c2b491f1fe460e731627bda9f40f160a6d26f4d7d8398ff91a3056a2943490`,匹配已发布 Worker,存在私有用户模块 | -| 业务 PG / SkillSpace / OV | `sunny-bluet-3db026cb`,aidb ready / `ss-yexqmg5kowdlwzgmqn9d` / `ov-f54074c915387e15`,READY | -| 成功的有界冒烟 | 新 Studio 会话 `8606260b-eeb7-4752-9782-0a860fc6fd07` 执行 `/bin/bash -lc 'printf STUDIO_READONLY_1021_FRESH_OK'`;真实 stdout 一致,退出码 0,完成的页面回答显示了两者 | - -镜像地址前缀为 `agentkit-platform-3001037806-ap-southeast-1.cr.bytepluses.com/mpa/`。两个标签原本已经存在,发现阶段没有发布任何标签。专用源码探针 Session `s-yexqlugkxsuexbvqjn6i` 已删除。脱敏原生/部署/API 证据及创建/功能截图在 Git 外本地保存,未提交凭据或原始云记录。 - -观察到两个独立条件,不能被后续新会话成功掩盖: - -1. 创建任务 `49556dec-276c-4053-9507-ed08cdb98bf3` 最初遇到 OV `InvalidParameter`。海外 CloudTrail 中请求 `2026102109482052668D9A8D2903007881`、`20261021095118D4C86EA35A3890F6A34D` 证明库数量超过账号上限 **20**;既有 UI 泛化显示为模型/配置拒绝。用户明确授权后,仅对上次测试 `studio-snapshot-1021-a` 的库 `ov-4dcc6e25514a5979` 核验归属并删除;删除成功,随后 Get 确认 collection-not-found。该旧验证 MPA 现在没有可用的 OV 记忆库。原创建请求随后成功,Worker 和两个镜像选择均保留。没有修改 OV 代码、模型设置、配额,也没有用 Runtime 配置变通。 -2. 首次 Studio 会话 `de1ce841-0f42-4087-be22-8845b1014798` **没有执行**所请求的 printf。页面报告 `inactive_worker_dispatch` 和 `harness invocation plan update was not persisted`,最后回复失败解释。持久 harness/control 状态为 `completed`,不能据此证明命令执行。源码将前者定位到 Agent MainRuntime active-grant 检查,后者定位到有条件的计划持久化;确切根因未建立,本次未修改这两条路径。检查时任务已完成,因此没有发起暂停变更。另一个新会话使用遇错即止的有界提示词,随后成功执行命令。这证明创建/镜像/新会话冒烟通过,**不证明**全部初始会话或显式 Worker 派发路径可靠。 - -剩余边界:完整 A2A/日历/IM 和国内 Runtime 部署未验证,仍要求发布方保留版本标签且不覆盖。新 MPA 及两个功能对话保留供核查,不升级既有 MPA。 diff --git a/prd-spec/bugfixes/studio-latest-image-refresh/2026-10-22-fork-ci-validation.md b/prd-spec/bugfixes/studio-latest-image-refresh/2026-10-22-fork-ci-validation.md deleted file mode 100644 index bde57fb47..000000000 --- a/prd-spec/bugfixes/studio-latest-image-refresh/2026-10-22-fork-ci-validation.md +++ /dev/null @@ -1,100 +0,0 @@ -# Fork CI validation and frontend gate timeout - -[中文版](2026-10-22-fork-ci-validation.zh.md) - -Change ID: studio-fork-ci-validation. Date: 2026-10-22. Status: implemented. - -## Evidence and scope - -PR #1178 at `86d56c47` passed unit tests, license, secrets, dependency policy, -and backend checks. Both callers of the shared frontend gate timed out during -`npm ci --ignore-scripts`. Runs `39593230026` and `39593230579` explicitly report -that the job exceeded its 3-minute limit; their aggregate gates failed because -the frontend result was cancelled. The lockfile includes npmmirror URLs. Whether -that mirror caused the delay is unproven. The existing release verifier already -uses the public npm registry and `replace-registry-host=always`. - -The user requested fixing CI and first completing it in their own fork PR to -avoid upstream approval waits. Scope is CI configuration and contract tests; -runtime, UI, image selection, dependencies, and lockfile contents are unchanged. - -## Requirements and design - -- FR-1: Given either caller, the shared frontend job uses the public npm registry, - replaces lockfile registry hosts, and has a bounded 10-minute budget. Keep - coverage, frontend tests, parallel execution, and fail-closed aggregation. -- FR-2: Given a PR in a fork, run the same shared gate and Studio bundle/smoke - verifier. Remove the upstream-repository restriction only from PR validation. -- FR-3: Given a fork PR or push, publication and notification remain disabled. - Release allocation, publication, and notification still require manual dispatch - from upstream `main`; workflow permissions remain `contents: read`. - -Affected consumers are the standalone shared workflow and the release workflow -that calls it. No component-spec change: application APIs, persisted state, -permissions, deployment, or runtime contracts do not change. The increased CI -budget and wider validation trigger are owned by these workflows. No new secrets, -dependencies, caches, retries, or publishing capabilities are introduced. - -## Tasks and acceptance - -1. T-1 / AC-1: Extend existing YAML contract tests; demonstrate failure before - changing workflows. Assert registry settings, 10-minute timeout, PR validation, - read-only permissions, and unchanged release guards (FR-1/2/3). -2. T-2 / AC-2: Update `.github/workflows/harness-sidecar-release-gate.yaml` and - `.github/workflows/publish-studio-release.yaml`; run contract tests, changed - Python Ruff/Pyright, all-files pre-commit, actual frontend coverage/tests, and - a production build in a temporary output directory (FR-1/2). -3. T-3 / AC-3: Create a fork PR against a branch pinned to upstream `main`, run - all applicable PR CI, and verify publication/notification are skipped. Report - any outstanding failures; do not equate local passes with hosted CI (FR-2/3). -4. T-4: After fork verification succeeds, bring the verified fix to upstream - PR #1178 and report the fork evidence. Hosted upstream approval may remain. - -## Review, risks, and delivery - -User approval: “当前ci又有几个没过 先去修一下”, followed by requesting complete CI -in their own fork PR. Direct design review (no review-spec skill available): -confirmed both callers share the job, full validation remains credential-free, -release guards stay intact, tests cover all changed YAML values, and both language -versions match. No blockers. Slow public network access can still exceed the -10-minute cap; a timeout remains a failure. Fork branch protection is not proof -of upstream policy. There is no executable production-code delta, so incremental -production coverage is not applicable; YAML contract checks cover the config. - -Local verification on 2026-10-22: pass, 272 Python tests across both workflow -contracts and the image snapshot/service suites; both regressions failed before -the workflow edits. Pass, repository-pinned Ruff 0.11.12, Pyright using the project -Python, and all-files pre-commit. Pass, frontend Sidecar coverage (22 tests; -100% lines, 94.44% branches), frontend tests (1389 Node tests and 118 Vitest tests), -and production build to `/tmp/studio-fork-ci-build-20261022`. Local Node 25.2.1 / -npm 11.6.2 differ from CI's pinned Node 22.17.0 / npm 10; hosted CI remains required. -The formatter modified one test file and all-files pre-commit passed on rerun. -An unpinned Ruff invocation and Pyright without the project Python were unsuitable; -the pinned/project-aware commands above passed without rule suppressions. - -Hosted fork CI: pass (AC-3), [fork PR #1](https://github.com/PolarisCzh/veadk-python/pull/1), -head `7ee50a79a4bbd5d59d2116dd76dc369eb737b9c6`, base -`5e5deb6a8a663fed4918f1ab6b2822c93a6c989b`. All seven workflows and 14 validation -jobs succeeded automatically without a maintainer approval: - -- [Unit Tests](https://github.com/PolarisCzh/veadk-python/actions/runs/39716494115): - Python 3.10: 7633 passed, 46 skipped, 4 xfailed; Python 3.12: 7636 passed, - 43 skipped, 4 xfailed; Codex smoke succeeded. Skipped/xfail cases are not passes. -- [Shared gate](https://github.com/PolarisCzh/veadk-python/actions/runs/39716494063): - backend, frontend, and aggregate succeeded; frontend completed in 66 seconds. -- [Release verification](https://github.com/PolarisCzh/veadk-python/actions/runs/39716494045): - shared backend/frontend/aggregate plus full Studio bundle, simulated customer - update, startup and installed-package smoke validation succeeded; its frontend - completed in 58 seconds. Release allocation, publication, and notification - were all skipped as required by FR-3. -- License `39716494036`, secrets `39716494022`, dependency policy `39716494030`, - and pre-commit `39716494050` all succeeded. - -GitHub had suspended workflows on this fork despite its API reporting Actions -as enabled. Re-enabled them through the Actions page. The default branch was -briefly changed for diagnosis and restored to `main`; its commits were preserved. -The fork PR base is a dedicated branch pinned to upstream main because fork main -contains separate work. The verified CI fix is carried to upstream PR #1178 with -this documentation-only evidence update; its upstream execution/approval is a -separate status, not implied by fork success. No cloud MPA creation or application -redeployment was performed. diff --git a/prd-spec/bugfixes/studio-latest-image-refresh/2026-10-22-fork-ci-validation.zh.md b/prd-spec/bugfixes/studio-latest-image-refresh/2026-10-22-fork-ci-validation.zh.md deleted file mode 100644 index 533a69c11..000000000 --- a/prd-spec/bugfixes/studio-latest-image-refresh/2026-10-22-fork-ci-validation.zh.md +++ /dev/null @@ -1,84 +0,0 @@ -# Fork CI 验证与前端门禁超时修复 - -[English](2026-10-22-fork-ci-validation.md) - -变更 ID:studio-fork-ci-validation。日期:2026-10-22。状态:implemented。 - -## 证据与范围 - -PR #1178 的 `86d56c47` 已通过单测、许可证、密钥扫描、依赖策略和后端检查。 -共享前端门禁的两个调用方均在 `npm ci --ignore-scripts` 阶段超时。 -运行 `39593230026` 和 `39593230579` 明确报告超过 3 分钟上限;汇总门禁因 -前端结果为 cancelled 而失败。锁文件含 npmmirror 地址,但该镜像站是否导致 -延迟尚未证实。现有发布验证任务已使用 npm 官方源和 -`replace-registry-host=always`。 - -用户要求修复 CI,并先在自己的 fork PR 中完整运行,避免等待上游批准。 -范围仅为 CI 配置和契约测试;运行时、UI、镜像选择、依赖和锁文件内容均不变。 - -## 需求与设计 - -- FR-1:任一调用方使用共享前端任务时,采用 npm 官方源、替换锁文件仓库域名, - 并设置有界的 10 分钟预算。保留覆盖率、前端测试、并行执行及失败关闭的汇总检查。 -- FR-2:fork 中的 PR 执行同一共享门禁与 Studio 打包及冒烟验证。 - 仅对 PR 验证移除上游仓库限制。 -- FR-3:fork PR 或 push 不启用发布和通知。版本分配、发布及通知仍要求 - 上游 `main` 上的手动触发;工作流权限保持 `contents: read`。 - -受影响的调用方是独立共享工作流和调用它的发布工作流。无需修改组件规范: -应用 API、持久状态、权限、部署和运行时契约不变。增加 CI 预算和扩展验证触发 -由工作流自身负责。不引入新密钥、依赖、缓存、重试或发布能力。 - -## 任务与验收 - -1. T-1 / AC-1:扩展既有 YAML 契约测试,在修改工作流前证明失败。 - 断言仓库配置、10 分钟超时、PR 验证、只读权限及不变的发布守卫(FR-1/2/3)。 -2. T-2 / AC-2:修改 `.github/workflows/harness-sidecar-release-gate.yaml` 和 - `.github/workflows/publish-studio-release.yaml`;执行契约测试、变更 Python - 的 Ruff/Pyright、全部 pre-commit、实际前端覆盖率及测试,并将生产构建输出 - 到临时目录(FR-1/2)。 -3. T-3 / AC-3:创建 fork PR,基线分支固定为上游 `main`,执行全部适用的 PR CI, - 验证发布和通知跳过。如有失败须报告,不能将本地通过等同于云端 CI(FR-2/3)。 -4. T-4:fork 验证通过后,将已验证修复带回上游 PR #1178 并提供 fork 证据。 - 上游云端批准仍可能需要等待。 - -## 评审、风险与交付 - -用户授权:“当前ci又有几个没过 先去修一下”,随后要求先在自己的 fork PR -中完整验证。直接设计评审(无可用 review-spec 技能):确认两个调用方共享任务, -完整验证不依赖凭据,发布守卫不变,测试覆盖全部变更的 YAML 值,中英文等价。 -无阻断项。公共网络缓慢仍可能超过 10 分钟上限,超时仍判失败。 -fork 分支保护不能证明上游策略。无可执行生产代码增量,因此增量生产覆盖率 -不适用;配置由 YAML 契约检查覆盖。 - -2026-10-22 本地验证:通过,两个工作流契约及镜像解析/服务共 272 项 Python -测试;两个回归均在修改工作流前失败。仓库固定的 Ruff 0.11.12、使用项目 Python -的 Pyright 及全部 pre-commit 通过。前端 Sidecar 覆盖率通过(22 项测试,行覆盖率 -100%、分支覆盖率 94.44%),前端测试通过(1389 项 Node 测试和 118 项 Vitest -测试),生产构建输出到 `/tmp/studio-fork-ci-build-20261022` 并通过。本地 Node -25.2.1 / npm 11.6.2 不同于 CI 固定的 Node 22.17.0 / npm 10,仍需云端验证。 -格式化器调整了一个测试文件,重跑全部 pre-commit 后通过。未固定版本的 Ruff -及未指定项目 Python 的 Pyright 调用不适用;上述固定版本及项目环境命令通过, -未添加规则豁免。 - -fork 云端 CI:通过(AC-3),[fork PR #1](https://github.com/PolarisCzh/veadk-python/pull/1), -head 为 `7ee50a79a4bbd5d59d2116dd76dc369eb737b9c6`,base 为 -`5e5deb6a8a663fed4918f1ab6b2822c93a6c989b`。全部 7 个工作流、14 个验证任务 -自动运行成功,无需维护者批准: - -- [Unit Tests](https://github.com/PolarisCzh/veadk-python/actions/runs/39716494115): - Python 3.10:7633 passed、46 skipped、4 xfailed;Python 3.12:7636 passed、 - 43 skipped、4 xfailed;Codex 冒烟成功。跳过和 xfail 用例不计为通过。 -- [共享门禁](https://github.com/PolarisCzh/veadk-python/actions/runs/39716494063): - 后端、前端和汇总全部成功;前端耗时 66 秒。 -- [发布验证](https://github.com/PolarisCzh/veadk-python/actions/runs/39716494045): - 共享前后端及汇总、完整 Studio 打包、模拟用户更新、启动与安装包冒烟验证均成功; - 其前端耗时 58 秒。版本分配、发布和通知均按 FR-3 要求跳过。 -- 许可证 `39716494036`、密钥扫描 `39716494022`、依赖策略 `39716494030` 和 - pre-commit `39716494050` 全部成功。 - -GitHub 曾暂停该 fork 的工作流,尽管 API 显示 Actions 启用;已通过 Actions 页面 -重新启用。诊断时短暂调整默认分支,随后恢复为 `main`,原提交保留。 -fork main 包含其他工作,因此本次 PR 使用固定为上游 main 的专用基线分支。 -已验证的 CI 修复连同本次仅文档证据更新同步到上游 PR #1178;上游运行及批准 -属于独立状态,不能由 fork 成功推定。本次未创建云端 MPA 或重新部署应用。 diff --git a/prd-spec/bugfixes/studio-mpa-a2a-preference/2026-09-30-prefer-a2a.md b/prd-spec/bugfixes/studio-mpa-a2a-preference/2026-09-30-prefer-a2a.md deleted file mode 100644 index 38e279704..000000000 --- a/prd-spec/bugfixes/studio-mpa-a2a-preference/2026-09-30-prefer-a2a.md +++ /dev/null @@ -1,97 +0,0 @@ -# Prefer the existing A2A bridge for MPA discovery - -[中文版](2026-09-30-prefer-a2a.zh.md) - -- Change ID: `studio-mpa-a2a-preference`; created/revised: 2026-09-30 -- Status: implemented; contract: [Runtime diagnostics](../../../specs/studio-runtime-diagnostics/README.md), `CON-13` - -## Evidence, scope and requirements - -Studio currently probes ADK `/list-apps` first and only discovers A2A on 404. -With `DISABLE_JWT_AUTH=true`, MPA advertises ADK, and native session creation -first requests `profile-status`, whose independent RuntimePrincipal gate still -requires a public key unless AgentKit mode is configured. This source chain -explains the reported missing-key error; the failing cloud URL is not captured. -The user approved preferring A2A for MPA while preserving this auth setting. - -- `FR-1`: Given an authorized MPA Runtime with a valid Agent Card, GET proxy - `list-apps` returns `["a2a-default"]` before probing ADK, even if ADK is available. -- `FR-2`: General Runtimes keep ADK-first discovery and existing 404 A2A fallback. - MPA without a usable card continues the existing discovery/error path. -- `FR-3`: Selected A2A sessions use existing create/run/history mapping and skip - native Profile/execution-config preflight; auth, TOS, downloads and API keys - are unchanged. Existing native sessions are not migrated. - -## Design, review and boundaries - -Add a scoped guard after Runtime authorization, credential resolution and -trusted-user header construction in `_runtime_proxy`. Reuse the bounded -`_runtime_a2a_agent_card` helper and `a2a-default` adapter. No new helper, -configuration, dependency, UI or Runtime/Worker change. GET `list-apps` is the -only changed request; other methods/paths retain their existing behavior. -Existing helper failures return no card and its upstream/client cleanup remains -in `finally`; cancellation is not caught. Category is from trusted control-plane -metadata, not browser input. Agent Card URL normalization is unchanged. - -Direct review (`review-spec` unavailable) confirms scope, trusted authorization, -fallback/resource behavior, testability and bilingual equivalence. User's -“改吧” approves the preceding plan. Ponytail reuses the existing shared entrypoint. -Visual skills are not applicable: no UI, layout, style or interaction code changes. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | Evidence | -| --- | --- | --- | --- | -| `FR-1`, `FR-2` | `T-1`: failing discovery regression, then shared guard | `AC-1`: MPA wins over working ADK; general/no-card/error boundaries preserved | Before: 8 fail/2 pass; after: 40 discovery/bridge cases pass | -| `FR-3` | `T-2`: replay existing bridge and session protocol tests | `AC-2`: create/run/history remain A2A without native preflight | 188 targeted Python and 49 frontend protocol checks pass | -| all | `T-3`: gates, docs and PR | `AC-3`: >95% incremental coverage, paired docs, commit/push | Incremental lines/branches 100%; pre-commit passes; baseline failures below | - -Use `uv run --extra dev pytest tests/cli/test_frontend_runtime_proxy.py -tests/cli/test_runtime_a2a_stream.py tests/cli/test_mpa_a2a_url.py`. Run broader -Python regression with two workers and opt-in smoke markers excluded. Run -frontend protocol checks as cross-layer evidence; no changed frontend bundle -requires regeneration. Validate changed Python with Ruff/Pyright. Record actual -results and known baseline failures here. Local isolated HTTP replay must not -be reported as real model/cloud/browser E2E; no cloud deployment is requested. - -## Verification and final review (2026-09-30) - -- Scope: four added executable lines and one corrected docstring on `7c365f00`. - Focused pytest selects `prefers_a2a_only_for_mpa_discovery` and - `bridges_a2a_only_runtime_for_studio_chat` with `--cov=veadk.cli.cli_frontend - --cov-branch --cov-report=json:/tmp/studio-mpa-a2a-preference-coverage.json`. - JSON matched against added executable lines: 4/4 lines, 4/4 branch arcs (100%), - not whole-module coverage. Matrix includes ADK/A2A coexistence, general ADK/404, - absent/invalid/non-object/malformed/timed-out cards and preserved 401/500 errors. -- Targeted command above: **pass**, 188 tests. Bridge replay includes creation, - streaming/send fallback, model metadata and history with shared-gateway URLs. - `node --test frontend/tests/mpaSessionProtocol.test.mjs frontend/tests/runSseAbort.test.mjs`: - **pass**, 49, including native-preflight exclusion and protocol stability on errors/cancellation. -- Full `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" - --continue-on-collection-errors --tb=short -q`: **fail**, 6174 pass, 7 fail, - 2 collection errors, 22 skipped, 2 xfailed, 6 subtests pass. Isolated `7c365f00` - with the same interpreter reproduces all seven failing nodes and both errors - (62 other checks pass). Six harness failures and collection errors require - missing optional `llama_index`/extensions; one wheel assertion conflicts with - the existing SDK constraint. No dependency or unrelated test change. -- `uv run --extra dev` lacks Ruff/Pyright binaries; existing local binaries were - used without installation, Pyright with this checkout's `.venv/bin/python`. - Direct file checks: **fail**, Ruff 0.16.7 has 105 diagnostics, Pyright 56; - baseline message/code multisets are identical, with no new diagnostics. - Pinned `uv run --extra dev pre-commit run --all-files`: **pass**, including - Ruff and secret scans. Paired identifiers/links and whitespace: **pass**. -- Direct implementation review confirms authorization precedes the scoped probe, - trusted headers are retained and bounded helper cleanup/cancellation is unchanged. - No frontend executable changes: frontend build/assets and visual/IME checks - are **not_applicable**. Automated HTTP replay is not browser/cloud/model E2E; - no deployment, real Runtime creation, model run or live download was performed. - -## Risks and rollout - -Reconnect the MPA in Studio to refresh its cached discovered apps. Existing -saved native sessions do not change automatically, and A2A bridge history has -its existing restoration/persistence limits. Native-only Profile/configuration -features remain outside A2A. No guarantee is made that missing Agent Cards or -unrelated authenticated management endpoints are fixed. Restore ADK-first -discovery by reverting this guard. No cloud resources, credentials or real -agent execution are needed for implementation tests. diff --git a/prd-spec/bugfixes/studio-mpa-a2a-preference/2026-09-30-prefer-a2a.zh.md b/prd-spec/bugfixes/studio-mpa-a2a-preference/2026-09-30-prefer-a2a.zh.md deleted file mode 100644 index 9fce5867a..000000000 --- a/prd-spec/bugfixes/studio-mpa-a2a-preference/2026-09-30-prefer-a2a.zh.md +++ /dev/null @@ -1,83 +0,0 @@ -# MPA 发现优先使用既有 A2A 桥接 - -[English](2026-09-30-prefer-a2a.md) - -- Change ID:`studio-mpa-a2a-preference`;创建/修订:2026-09-30 -- 状态:implemented;契约:[Runtime 诊断](../../../specs/studio-runtime-diagnostics/README.zh.md),`CON-13` - -## 证据、范围与需求 - -Studio 当前先探测 ADK `/list-apps`,仅在 404 后发现 A2A。 -设置 `DISABLE_JWT_AUTH=true` 后 MPA 广告 ADK,原生会话创建先查询 `profile-status`, -其独立 RuntimePrincipal 鉴权在未配置 AgentKit 模式时仍要求公钥。该源码链解释 -用户报告的缺公钥错误;尚未捕获具体失败的云端 URL。用户批准 MPA 优先 A2A, -同时保留当前鉴权开关。 - -- `FR-1`:已授权 MPA Runtime 提供有效 Agent Card 时,GET 代理 `list-apps` - 在探测 ADK 前返回 `["a2a-default"]`,即使 ADK 同时可用。 -- `FR-2`:普通 Runtime 保持 ADK 优先及既有 404 A2A 回退。MPA 没有可用 - 卡片时继续既有发现/错误流程。 -- `FR-3`:选中 A2A 后会话使用原有创建/运行/历史映射,跳过原生 Profile/ - 执行配置前置查询;鉴权、TOS、下载和 API Key 不变。不迁移已有原生会话。 - -## 设计、审查与边界 - -在 `_runtime_proxy` 完成 Runtime 授权、凭据解析和可信身份 header 构造后, -增加限定条件。复用有超时的 `_runtime_a2a_agent_card` 及 `a2a-default` 适配。 -不增加 helper、配置、依赖或 UI,不修改 Runtime/Worker。只改变 GET `list-apps`, -其他方法/路径不变。既有 helper 失败返回无卡片,上游/客户端继续在 `finally` -清理,不捕获取消。类别来自可信控制面元数据而非浏览器;卡片 URL 归一化不变。 - -直接审查(`review-spec` 不可用)确认范围、可信授权、回退/资源处理、可测试性及 -双语一致性。用户“改吧”批准前述方案。Ponytail 复用既有共享入口。视觉技能不适用: -不修改 UI、布局、样式或交互代码。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | 证据 | -| --- | --- | --- | --- | -| `FR-1`、`FR-2` | `T-1`:失败发现回归,再改共享条件 | `AC-1`:MPA 优先于可用 ADK,保留普通/无卡片/错误边界 | 修复前 8 失败/2 通过;修复后 40 项发现/桥接通过 | -| `FR-3` | `T-2`:回放既有桥接/会话协议测试 | `AC-2`:创建/运行/历史保持 A2A,无原生前置查询 | 188 项目标 Python 及 49 项前端协议通过 | -| 全部 | `T-3`:门禁、文档和 PR | `AC-3`:增量覆盖率 >95%、双语文档、提交/推送 | 增量行/分支 100%;pre-commit 通过;基线失败见下文 | - -执行 `uv run --extra dev pytest tests/cli/test_frontend_runtime_proxy.py -tests/cli/test_runtime_a2a_stream.py tests/cli/test_mpa_a2a_url.py`。使用两 worker -执行排除可选 smoke 的更广 Python 回归。前端协议测试作为跨层证据;无前端 bundle -变化,不需重新生成。修改的 Python 运行 Ruff/Pyright。在此记录实际结果和既有 -基线失败。本地隔离 HTTP 回放不作为真实模型/云端/浏览器 E2E;本次不请求云端部署。 - -## 验证及最终审查(2026-09-30) - -- 范围:基于 `7c365f00` 增加四行可执行语句及修正一处 docstring。目标 pytest - 选择 `prefers_a2a_only_for_mpa_discovery` 和 - `bridges_a2a_only_runtime_for_studio_chat`,附加 `--cov=veadk.cli.cli_frontend - --cov-branch --cov-report=json:/tmp/studio-mpa-a2a-preference-coverage.json`。 - JSON 对照可执行新增行:4/4 行、4/4 分支弧(100%),不是整个模块覆盖率。 - 矩阵包含 ADK/A2A 共存、普通 ADK/404、缺失/无效/非对象/非法 JSON/超时卡片 - 及保留 401/500 错误。 -- 前述目标命令:**pass**,188 项。桥接回放包括共享网关 URL 下的创建、 - 流式/发送回退、模型元数据和历史恢复。 - `node --test frontend/tests/mpaSessionProtocol.test.mjs frontend/tests/runSseAbort.test.mjs`: - **pass**,49 项,含排除原生前置查询和错误/取消时协议不变。 -- 全量 `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" - --continue-on-collection-errors --tb=short -q`:**fail**,6174 通过、7 失败、 - 2 收集错误、22 跳过、2 xfailed、6 subtests 通过。相同解释器在隔离的 - `7c365f00` 复现全部七个失败节点和两个错误(另 62 项通过)。六项 harness - 失败及收集错误需要缺失的可选 `llama_index`/extensions;一项 wheel 断言 - 与已有 SDK 约束不一致。不修改依赖或无关测试。 -- `uv run --extra dev` 没有 Ruff/Pyright 可执行文件;使用已有本地工具,不安装 - 依赖,Pyright 指向当前 checkout 的 `.venv/bin/python`。直接文件检查:**fail**, - Ruff 0.16.7 为 105 项、Pyright 为 56 项;与基线的消息/code 多重集合完全一致, - 无新增诊断。固定版本 `uv run --extra dev pre-commit run --all-files`:**pass**, - 包含 Ruff 和密钥扫描。双语标识/链接及空白:**pass**。 -- 直接实现审查确认新探测前完成授权,保留可信 header,有界 helper 的清理/取消 - 不变。无前端可执行修改:前端构建/产物及视觉/IME 检查为 **not_applicable**。 - 自动 HTTP 回放不作为浏览器/云端/模型 E2E;未部署、未创建真实 Runtime、 - 未运行模型或验证真实下载。 - -## 风险与上线 - -在 Studio 重新连接 MPA,刷新已缓存的发现应用。保存的原生会话不会自动改变, -A2A 历史沿用原有恢复/持久化限制。原生专属 Profile/配置功能不属于 A2A。 -不保证修复卡片缺失或无关管理接口鉴权。回退时撤销新增条件即可恢复 ADK 优先。 -实现测试不需要云资源、真实凭据或真实 Agent 执行。 diff --git a/prd-spec/bugfixes/studio-mpa-aidap-permissions/2026-09-23-complete-aidap-runtime-policy.md b/prd-spec/bugfixes/studio-mpa-aidap-permissions/2026-09-23-complete-aidap-runtime-policy.md deleted file mode 100644 index b66fb6363..000000000 --- a/prd-spec/bugfixes/studio-mpa-aidap-permissions/2026-09-23-complete-aidap-runtime-policy.md +++ /dev/null @@ -1,86 +0,0 @@ -# Complete Studio AIDAP runtime permissions - -[中文版](2026-09-23-complete-aidap-runtime-policy.zh.md) - -- Change ID: `studio-mpa-aidap-permissions` -- Created/Revised: 2026-09-23 -- Status: superseded -- Related component: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md) -- Successor: [Complete MPA cloud runtime policy](2026-09-23-complete-mpa-cloud-runtime-policy.md) -- User approval: 2026-09-23, add every permission required by the automatic PostgreSQL Workspace flow. - -## Background and evidence - -Cloud Studio MPA creation now passes writable-state initialization but fails at the `admin_workspace` stage. Local deployments can succeed because they use operator AK/SK credentials, while a VeFaaS deployment normally uses STS credentials for `VeADKFrontendServiceRole`. - -`PGCloud` invokes nine AIDAP operations: `DescribeWorkspaces`, `CreateWorkspace`, `DescribeWorkspaceDetail`, `DescribeBranches`, `DescribeComputes`, `DescribeWorkspaceEndpoint`, `DescribeDBAccounts`, `DescribeDatabases`, and `DescribeDBAccountConnection`. The default Studio policy currently grants only the five collection queries. It omits the create, detail, endpoint, and account-connection actions required by the same flow. This code-to-policy mismatch is confirmed; the exact provider error remains unavailable because this shell has no cloud credentials or log access. - -Expected behavior is that a default cloud Studio deployment can run its supported automatic PostgreSQL preparation with its execution role. Actual behavior is that the role lacks four actions exercised at and after the failing stage. - -## Goals and non-goals - -Goals: - -- Grant all nine AIDAP actions currently exercised by `PGCloud`. -- Refresh an existing `VeADKFrontendPolicy` during the normal `studio update` role-policy refresh. -- Add a regression test that ties the default policy to the automatic PG call surface. -- Preserve credential redaction, HTTP behavior, idempotency, and custom-role ownership. - -Non-goals: - -- Granting `aidap:*` or unrelated AIDAP administrative operations. -- Automatically rewriting customer-owned Studio roles. -- Changing AIDAP orchestration, retry behavior, Workspace lifecycle, or deploying the fix. - -## Scenarios and requirements - -- **FR-1:** Given the default Studio execution role, its custom policy contains all nine AIDAP actions required by `PGCloud`. -- **FR-2:** Given an existing default Studio role, `studio update` replaces the custom policy document and verifies the refreshed action set. -- **FR-3:** Given a customer-owned Studio role, the update flow does not replace its policy automatically; its operator must grant the same required actions. -- **FR-4:** The policy grants explicit actions only and does not add `aidap:*`. - -## Design and contract impact - -Add these missing actions to `FRONTEND_DEPLOY_POLICY`: - -- `aidap:CreateWorkspace` -- `aidap:DescribeWorkspaceDetail` -- `aidap:DescribeWorkspaceEndpoint` -- `aidap:DescribeDBAccountConnection` - -Together with the five existing actions, this exactly covers the methods called by `PGCloud`. `ensure_default_frontend_role_policy` already updates and verifies `VeADKFrontendPolicy`, so no IAM update algorithm change is required. Customer-owned roles remain untouched to preserve their ownership boundary. - -The cloud credential source remains unchanged: local execution may use AK/SK, while VeFaaS reads role STS credentials. No credentials, provider responses, or connection strings are persisted or returned. The additional create permission authorizes the existing user-approved automatic Workspace operation; it does not introduce a new cloud side effect. API, configuration, state, concurrency, and compatibility contracts otherwise remain unchanged. - -## Implementation tasks - -- **T-1 (FR-1, FR-4):** Add a failing policy-completeness test for the exact nine-action set. -- **T-2 (FR-1):** Add the four missing actions to `veadk/cli/frontend_deploy_policy.py`. -- **T-3 (FR-2, FR-3):** Run existing default/custom-role refresh tests. -- **T-4:** Run affected MPA/CLI tests and changed-file static checks. -- **T-5:** After separate deployment authorization, update Studio and run a live cloud creation smoke test. - -Affected files: `veadk/cli/frontend_deploy_policy.py`, `tests/cli/test_frontend_deploy_iam.py`, this bilingual PRD, and the bilingual Studio MPA creation component spec. - -## Verification and acceptance - -| Requirement | Task | Acceptance criterion | Verification | Result | -| --- | --- | --- | --- | --- | -| `FR-1`, `FR-4` | `T-1`, `T-2` | `AC-1`: the policy contains the exact required AIDAP subset and excludes `aidap:*` | `uv run --extra dev pytest tests/cli/test_frontend_deploy_iam.py tests/cli/test_studio_update_permissions.py -q` | `pass`, 21 tests, 2026-09-23 | -| `FR-2`, `FR-3` | `T-3` | `AC-2`: default policy refresh and custom-role preservation tests pass | Same targeted test command | `pass`, 2026-09-23 | -| `FR-1` | `T-4` | `AC-3`: affected MPA and CLI regressions plus Ruff/Pyright pass | `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py tests/cli/test_frontend_deploy_iam.py tests/cli/test_studio_update_permissions.py -q`; `uv run --with ruff ruff check veadk/cli/frontend_deploy_policy.py tests/cli/test_frontend_deploy_iam.py`; `uv run --with pyright pyright veadk/cli/frontend_deploy_policy.py tests/cli/test_frontend_deploy_iam.py` | `pass`, 417 tests, Ruff and Pyright, 2026-09-23 | -| `FR-1`, `FR-2` | `T-5` | `AC-4`: deployed Studio passes `admin_workspace` and completes or reaches the next legitimate stage | Authorized `studio update` plus live creation | `not_run`; deployment not authorized | - -Frontend tests/build are not applicable because no UI, browser behavior, or HTTP schema changes. A local policy test proves document completeness but not that IAM propagation or live AIDAP access succeeds. - -## Risks and recovery - -- IAM policy propagation may be delayed after update; retry only after the policy is visible. -- A service-side permission name mismatch or an unrelated account/project conflict may still fail the cloud smoke test; preserve sanitized diagnostics. -- Rollback removes the four actions and restores the known policy mismatch. - -## Review and delivery record - -Direct design review on 2026-09-23 checked policy scope, least privilege, existing-role refresh, customer-role ownership, credential handling, compatibility, testability, and bilingual equivalence. No blocking issue remains. The user explicitly approved adding all permissions used by this flow. - -The regression test failed before the policy edit and named the four missing actions. After the edit, 21 focused permission tests and 417 affected MPA/CLI tests passed. Ruff, Pyright, bilingual identifier checks, and `git diff --check` passed. The repository-prescribed bare `uv run --extra dev ruff ...` command was `blocked` because Ruff is not declared in the current dev environment; the isolated `uv run --with ruff ...` equivalent passed. Deployment and live AIDAP verification remain `not_run` pending separate authorization, so lifecycle status remains `approved`. diff --git a/prd-spec/bugfixes/studio-mpa-aidap-permissions/2026-09-23-complete-aidap-runtime-policy.zh.md b/prd-spec/bugfixes/studio-mpa-aidap-permissions/2026-09-23-complete-aidap-runtime-policy.zh.md deleted file mode 100644 index 6354fb946..000000000 --- a/prd-spec/bugfixes/studio-mpa-aidap-permissions/2026-09-23-complete-aidap-runtime-policy.zh.md +++ /dev/null @@ -1,86 +0,0 @@ -# 补齐 Studio AIDAP 运行权限 - -[English](2026-09-23-complete-aidap-runtime-policy.md) - -- 变更 ID:`studio-mpa-aidap-permissions` -- 创建/修订日期:2026-09-23 -- 状态:superseded -- 相关组件:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md) -- 后继设计:[补齐 MPA 云端运行策略](2026-09-23-complete-mpa-cloud-runtime-policy.zh.md) -- 用户批准:2026-09-23,加入自动 PostgreSQL Workspace 流程需要的全部权限。 - -## 背景与证据 - -云端 Studio MPA 创建现已通过可写状态初始化,但在 `admin_workspace` 阶段失败。本地部署可成功,是因为本地使用运维 AK/SK;VeFaaS 部署通常使用 `VeADKFrontendServiceRole` 的 STS 凭据。 - -`PGCloud` 调用 9 项 AIDAP 操作:`DescribeWorkspaces`、`CreateWorkspace`、`DescribeWorkspaceDetail`、`DescribeBranches`、`DescribeComputes`、`DescribeWorkspaceEndpoint`、`DescribeDBAccounts`、`DescribeDatabases` 和 `DescribeDBAccountConnection`。默认 Studio 策略目前只授予 5 项集合查询权限,缺少同一流程所需的创建、详情、端点和账号连接操作。代码与策略不一致已确认;由于当前 shell 没有云凭据或日志访问能力,尚未取得服务商的确切错误码。 - -预期行为是默认云端 Studio 部署可使用其执行角色完成受支持的自动 PostgreSQL 准备。实际行为是该角色缺少失败阶段及后续流程实际调用的 4 项权限。 - -## 目标与非目标 - -目标: - -- 授予 `PGCloud` 当前实际调用的全部 9 项 AIDAP Action。 -- 在正常 `studio update` 角色策略刷新中更新已有 `VeADKFrontendPolicy`。 -- 增加回归测试,将默认策略与自动 PG 调用面绑定。 -- 保持凭据脱敏、HTTP 行为、幂等性和自定义角色所有权不变。 - -非目标: - -- 授予 `aidap:*` 或无关的 AIDAP 管理操作。 -- 自动改写客户自有 Studio 角色。 -- 修改 AIDAP 编排、重试行为、Workspace 生命周期或执行部署。 - -## 场景与需求 - -- **FR-1:**使用默认 Studio 执行角色时,其自定义策略包含 `PGCloud` 所需的全部 9 项 AIDAP Action。 -- **FR-2:**已有默认 Studio 角色执行 `studio update` 时,应替换自定义策略文档并核验刷新后的 Action 集合。 -- **FR-3:**使用客户自有 Studio 角色时,更新流程不得自动替换其策略;运维须自行授予同一组必需权限。 -- **FR-4:**策略只授予明确 Action,不增加 `aidap:*`。 - -## 设计与契约影响 - -在 `FRONTEND_DEPLOY_POLICY` 中增加以下缺失 Action: - -- `aidap:CreateWorkspace` -- `aidap:DescribeWorkspaceDetail` -- `aidap:DescribeWorkspaceEndpoint` -- `aidap:DescribeDBAccountConnection` - -它们与现有 5 项 Action 共同精确覆盖 `PGCloud` 调用的方法。`ensure_default_frontend_role_policy` 已负责更新并核验 `VeADKFrontendPolicy`,无需修改 IAM 更新算法。为保持所有权边界,客户自有角色仍不自动修改。 - -云端凭据来源保持不变:本地执行可使用 AK/SK,VeFaaS 读取角色 STS 凭据。不持久化或返回任何凭据、服务商响应或连接串。新增创建权限用于已有且经用户批准的自动 Workspace 操作,不引入新的云端副作用。API、配置、状态、并发及兼容性契约均不作其他修改。 - -## 实现任务 - -- **T-1(FR-1、FR-4):**为精确的 9 项权限集合增加先失败的策略完整性测试。 -- **T-2(FR-1):**在 `veadk/cli/frontend_deploy_policy.py` 中增加 4 项缺失 Action。 -- **T-3(FR-2、FR-3):**运行已有默认角色刷新及自定义角色保留测试。 -- **T-4:**运行受影响的 MPA/CLI 测试及改动文件静态检查。 -- **T-5:**另行获得部署授权后更新 Studio,并执行真实云端创建冒烟测试。 - -涉及文件:`veadk/cli/frontend_deploy_policy.py`、`tests/cli/test_frontend_deploy_iam.py`、本双语 PRD 及 Studio MPA 创建双语组件规范。 - -## 验证与验收 - -| 需求 | 任务 | 验收标准 | 验证 | 结果 | -| --- | --- | --- | --- | --- | -| `FR-1`、`FR-4` | `T-1`、`T-2` | `AC-1`:策略包含精确的必需 AIDAP 子集且不含 `aidap:*` | `uv run --extra dev pytest tests/cli/test_frontend_deploy_iam.py tests/cli/test_studio_update_permissions.py -q` | `pass`,21 项测试,2026-09-23 | -| `FR-2`、`FR-3` | `T-3` | `AC-2`:默认策略刷新及自定义角色保留测试通过 | 同一目标测试命令 | `pass`,2026-09-23 | -| `FR-1` | `T-4` | `AC-3`:受影响的 MPA/CLI 回归及 Ruff/Pyright 通过 | `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py tests/cli/test_frontend_deploy_iam.py tests/cli/test_studio_update_permissions.py -q`;`uv run --with ruff ruff check veadk/cli/frontend_deploy_policy.py tests/cli/test_frontend_deploy_iam.py`;`uv run --with pyright pyright veadk/cli/frontend_deploy_policy.py tests/cli/test_frontend_deploy_iam.py` | `pass`,417 项测试、Ruff 及 Pyright,2026-09-23 | -| `FR-1`、`FR-2` | `T-5` | `AC-4`:已部署 Studio 通过 `admin_workspace`,并完成创建或进入下一个合理阶段 | 经授权的 `studio update` 及真实创建 | `not_run`;未获部署授权 | - -由于不修改 UI、浏览器行为或 HTTP 结构,前端测试/构建不适用。本地策略测试只能证明文档完整,不能证明 IAM 传播或真实 AIDAP 访问成功。 - -## 风险与恢复 - -- 更新后 IAM 策略传播可能延迟;仅在策略可见后重试。 -- 服务端权限名称不匹配或无关的账号/项目冲突仍可能导致云端冒烟失败;须保留脱敏诊断。 -- 回滚会移除 4 项 Action,并恢复已知的策略缺口。 - -## 审查与交付记录 - -2026-09-23 直接设计审查已检查策略范围、最小权限、已有角色刷新、客户角色所有权、凭据处理、兼容性、可测试性及双语一致性,无阻塞问题。用户已明确批准加入该流程使用的全部权限。 - -策略修改前,回归测试按预期失败并列出 4 项缺失 Action。修改后,21 项权限目标测试及 417 项受影响 MPA/CLI 测试通过;Ruff、Pyright、双语标识检查和 `git diff --check` 均通过。仓库规定的裸 `uv run --extra dev ruff ...` 命令因当前 dev 环境未声明 Ruff 而为 `blocked`,等效的隔离命令 `uv run --with ruff ...` 已通过。部署和真实 AIDAP 验证仍等待单独授权,为 `not_run`,因此生命周期状态保持 `approved`。 diff --git a/prd-spec/bugfixes/studio-mpa-aidap-permissions/2026-09-23-complete-mpa-cloud-runtime-policy.md b/prd-spec/bugfixes/studio-mpa-aidap-permissions/2026-09-23-complete-mpa-cloud-runtime-policy.md deleted file mode 100644 index 7c05b6196..000000000 --- a/prd-spec/bugfixes/studio-mpa-aidap-permissions/2026-09-23-complete-mpa-cloud-runtime-policy.md +++ /dev/null @@ -1,94 +0,0 @@ -# Complete the Studio MPA cloud runtime policy - -[中文版](2026-09-23-complete-mpa-cloud-runtime-policy.zh.md) - -- Change ID: `studio-mpa-cloud-runtime-permissions` -- Created/Revised: 2026-09-23 -- Status: approved -- Related component: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md) -- Predecessor: [AIDAP-only permission fix](2026-09-23-complete-aidap-runtime-policy.md) -- User approval: 2026-09-23, grant every permission used by all MPA creation stages, including VPC and APIG. - -## Background and evidence - -Cloud Studio uses `VeADKFrontendServiceRole` STS credentials, while a local deployment commonly uses an operator's broader AK/SK permissions. The initial audit found four missing AIDAP actions. The expanded code audit covers every direct cloud adapter reachable from `provision()`: - -| Stage | Direct operations | Existing coverage before this revision | -| --- | --- | --- | -| Account check | `sts:GetCallerIdentity` | Missing | -| Automatic PG | Nine explicit `aidap:` actions | Complete after the predecessor fix | -| Network | `ecs:DescribeZones`; VPC describe/list/create operations | Missing `ecs:DescribeZones`, `vpc:DescribeSubnetAttributes`, `vpc:CreateVpc`, `vpc:CreateSubnet` | -| Gateway | APIG list/create/get and IM Gateway create/status operations | All five missing | -| Worker, Skill Space, Runtime | AgentKit get/list/create/update operations | Covered by existing `agentkit:*` | -| Database/OpenViking | PostgreSQL protocol and Runtime environment injection | No additional cloud IAM Action | - -The policy mismatch can cause cloud-only failures at successive stages even when local creation succeeds. - -## Goals and non-goals - -Goals: - -- Cover every cloud IAM Action directly exercised by the current MPA creation path. -- Keep the default role refresh idempotent for existing deployments. -- Add one regression test that documents the complete cross-service permission set. -- Preserve customer-owned role ownership and credential handling. - -Non-goals: - -- Granting `aidap:*`, `vpc:*`, `ecs:*`, or `apig:*`. -- Adding speculative actions not reached by the current implementation. -- Replacing the existing `agentkit:*` policy in this fix. -- Changing orchestration, API payloads, retry/state behavior, or deploying the change. - -## Scenarios and requirements - -- **FR-1:** The default Studio policy contains `sts:GetCallerIdentity`. -- **FR-2:** The policy contains all seven network actions used by `NetworkCloud`: `ecs:DescribeZones`, `vpc:DescribeVpcs`, `vpc:DescribeVpcAttributes`, `vpc:DescribeSubnets`, `vpc:DescribeSubnetAttributes`, `vpc:CreateVpc`, and `vpc:CreateSubnet`. -- **FR-3:** The policy contains all five APIG actions used by `GatewayCloud`: `apig:ListGateways`, `apig:CreateGateway`, `apig:GetGateway`, `apig:CreateIMChannelGateway`, and `apig:GetIMChannelGatewayStatus`. -- **FR-4:** The policy retains `aidap:DescribeWorkspaces`, `aidap:CreateWorkspace`, `aidap:DescribeWorkspaceDetail`, `aidap:DescribeBranches`, `aidap:DescribeComputes`, `aidap:DescribeWorkspaceEndpoint`, `aidap:DescribeDBAccounts`, `aidap:DescribeDatabases`, `aidap:DescribeDBAccountConnection`, and `agentkit:*` coverage required by automatic PG, Worker, Skill Space, and Runtime operations. -- **FR-5:** `studio update` refreshes the default role policy; customer-owned roles remain unchanged. -- **FR-6:** No new service wildcard is introduced for STS, ECS, VPC, APIG, or AIDAP. - -## Design and contract impact - -Add ten missing explicit actions to `FRONTEND_DEPLOY_POLICY`: one STS action, one ECS action, three VPC actions, and five APIG actions. Existing actions remain unchanged. The policy already contains the four VPC read actions and all nine AIDAP actions required by the flow, as well as `agentkit:*`. - -`ensure_default_frontend_role_policy` already updates and reads back `VeADKFrontendPolicy`, so existing default deployments receive the expanded document on `studio update`. Customer roles are not modified automatically and must grant the same required actions themselves. - -No secret, state, API, concurrency, network topology, or cloud resource lifecycle changes. Permissions authorize only operations the approved flow already attempts. A Runtime's bound role is a separate trust boundary; this change affects the Studio execution role, not Runtime workload permissions. - -## Implementation tasks - -- **T-1 (FR-1–FR-6):** Replace the AIDAP-only regression with a complete MPA cloud-policy test and confirm it fails before the policy edit. -- **T-2 (FR-1–FR-3, FR-6):** Add the ten missing explicit actions. -- **T-3 (FR-4, FR-5):** Run policy refresh and MPA integration regressions. -- **T-4:** Run Ruff, Pyright, bilingual consistency, whitespace, and default parallel Python regression. -- **T-5:** After separate authorization, deploy and retry one live MPA creation through all stages. - -Affected files: `veadk/cli/frontend_deploy_policy.py`, `tests/cli/test_frontend_deploy_iam.py`, this bilingual PRD, the predecessor PRD, the bilingual component spec, and the active debug record. - -## Verification and acceptance - -| Requirement | Task | Acceptance criterion | Verification | Result | -| --- | --- | --- | --- | --- | -| `FR-1`–`FR-4`, `FR-6` | `T-1`, `T-2` | `AC-1`: the default policy contains the complete explicit MPA set without new service wildcards | `uv run --extra dev pytest tests/cli/test_frontend_deploy_iam.py tests/cli/test_studio_update_permissions.py -q` | `pass`, 22 tests, 2026-09-23 | -| `FR-5` | `T-3` | `AC-2`: default-role refresh and custom-role preservation pass | Same targeted command | `pass`, 2026-09-23 | -| `FR-1`–`FR-6` | `T-3`, `T-4` | `AC-3`: affected and default regression gates pass | Affected tests, Ruff, Pyright, bilingual/whitespace checks, and default parallel regression | affected/static `pass`; default regression `fail` on unrelated baseline/environment issues, 2026-09-23 | -| `FR-1`–`FR-5` | `T-5` | `AC-4`: deployed creation passes PG, network, gateway, worker, skills, Runtime, and readiness stages | Authorized deployment and live smoke | `not_run`; deployment not authorized | - -Frontend tests/build are not applicable because no UI or HTTP schema changes. Static policy checks do not prove IAM propagation, service enablement, quotas, resource compatibility, or live network access. - -## Risks and recovery - -- Another service-side dependent permission may be enforced but not visible as a direct client call; live smoke remains required. -- IAM propagation may be delayed after update. -- Existing explicit denies, unavailable services, quota limits, or resource conflicts still take precedence. -- Rollback restores the known incomplete policy. - -## Review and delivery record - -Direct review on 2026-09-23 traced `provision()` through `RuntimeCloud`, `PGCloud`, `NetworkCloud`, `GatewayCloud`, `WorkerCloud`, and Skill Space/Runtime operations. It checked least privilege, service/action spelling, default-role refresh, customer-role ownership, security boundaries, compatibility, testability, and bilingual equivalence. The user approved the expanded all-stage scope. - -The complete-policy test failed before the edit with exactly the ten audited missing actions. After the edit, 22 IAM/update tests and 418 affected MPA/CLI tests passed. `uv run --with ruff ruff check veadk/cli/frontend_deploy_policy.py tests/cli/test_frontend_deploy_iam.py`, the equivalent Pyright command, policy uniqueness, bilingual identifier, and `git diff --check` checks passed. - -The required default regression ran as `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`: 5,216 passed, 11 skipped, 2 xfailed, 8 failed, and 2 collection errors. Six Harness failures and two Sandbox collection errors require undeclared optional `llama_index`/`anthropic` dependencies. Two optimized-Skill tests failed from parallel region-state interference and passed when rerun serially (2 passed). None touches the changed policy or test. Deployment and live MPA creation remain `not_run` pending authorization, so lifecycle status remains `approved`. diff --git a/prd-spec/bugfixes/studio-mpa-aidap-permissions/2026-09-23-complete-mpa-cloud-runtime-policy.zh.md b/prd-spec/bugfixes/studio-mpa-aidap-permissions/2026-09-23-complete-mpa-cloud-runtime-policy.zh.md deleted file mode 100644 index 1ed84b5f4..000000000 --- a/prd-spec/bugfixes/studio-mpa-aidap-permissions/2026-09-23-complete-mpa-cloud-runtime-policy.zh.md +++ /dev/null @@ -1,94 +0,0 @@ -# 补齐 Studio MPA 云端运行策略 - -[English](2026-09-23-complete-mpa-cloud-runtime-policy.md) - -- 变更 ID:`studio-mpa-cloud-runtime-permissions` -- 创建/修订日期:2026-09-23 -- 状态:approved -- 相关组件:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md) -- 前序设计:[仅 AIDAP 权限修复](2026-09-23-complete-aidap-runtime-policy.zh.md) -- 用户批准:2026-09-23,授予 MPA 创建所有阶段使用的全部权限,包括 VPC 和 APIG。 - -## 背景与证据 - -云端 Studio 使用 `VeADKFrontendServiceRole` 的 STS 凭据,本地部署通常使用权限更广的运维 AK/SK。初次审计发现 4 项 AIDAP 权限缺失。本次扩展代码审计覆盖 `provision()` 可达的每个直接云端适配器: - -| 阶段 | 直接操作 | 本次修订前的覆盖情况 | -| --- | --- | --- | -| 账号检查 | `sts:GetCallerIdentity` | 缺失 | -| 自动 PG | 9 项明确的 `aidap:` Action | 前序修复后完整 | -| 网络 | `ecs:DescribeZones`;VPC 查询/列表/创建操作 | 缺少 `ecs:DescribeZones`、`vpc:DescribeSubnetAttributes`、`vpc:CreateVpc`、`vpc:CreateSubnet` | -| 网关 | APIG 列表/创建/详情及 IM Gateway 创建/状态操作 | 5 项全部缺失 | -| Worker、Skill Space、Runtime | AgentKit 查询/列表/创建/更新操作 | 已由现有 `agentkit:*` 覆盖 | -| 数据库/OpenViking | PostgreSQL 协议及 Runtime 环境变量注入 | 无额外云 IAM Action | - -该策略缺口会使云端流程在连续阶段失败,即使本地创建能够成功。 - -## 目标与非目标 - -目标: - -- 覆盖当前 MPA 创建路径直接执行的每项云 IAM Action。 -- 保持默认角色对已有部署的幂等刷新。 -- 用一个回归测试记录完整的跨服务权限集合。 -- 保持客户自有角色所有权和凭据处理不变。 - -非目标: - -- 授予 `aidap:*`、`vpc:*`、`ecs:*` 或 `apig:*`。 -- 增加当前实现不可达的猜测性 Action。 -- 在本修复中替换现有 `agentkit:*` 策略。 -- 修改编排、API 请求、重试/状态行为或执行部署。 - -## 场景与需求 - -- **FR-1:**默认 Studio 策略包含 `sts:GetCallerIdentity`。 -- **FR-2:**策略包含 `NetworkCloud` 使用的全部 7 项网络权限:`ecs:DescribeZones`、`vpc:DescribeVpcs`、`vpc:DescribeVpcAttributes`、`vpc:DescribeSubnets`、`vpc:DescribeSubnetAttributes`、`vpc:CreateVpc` 和 `vpc:CreateSubnet`。 -- **FR-3:**策略包含 `GatewayCloud` 使用的全部 5 项 APIG 权限:`apig:ListGateways`、`apig:CreateGateway`、`apig:GetGateway`、`apig:CreateIMChannelGateway` 和 `apig:GetIMChannelGatewayStatus`。 -- **FR-4:**策略保留自动 PG、Worker、Skill Space 和 Runtime 操作所需的 `aidap:DescribeWorkspaces`、`aidap:CreateWorkspace`、`aidap:DescribeWorkspaceDetail`、`aidap:DescribeBranches`、`aidap:DescribeComputes`、`aidap:DescribeWorkspaceEndpoint`、`aidap:DescribeDBAccounts`、`aidap:DescribeDatabases`、`aidap:DescribeDBAccountConnection` 及 `agentkit:*` 覆盖。 -- **FR-5:**`studio update` 刷新默认角色策略;客户自有角色保持不变。 -- **FR-6:**不为 STS、ECS、VPC、APIG 或 AIDAP 引入新的服务通配符。 - -## 设计与契约影响 - -在 `FRONTEND_DEPLOY_POLICY` 中增加 10 项缺失的明确 Action:STS 1 项、ECS 1 项、VPC 3 项、APIG 5 项。现有 Action 不变。策略已经包含流程需要的 4 项 VPC 读取权限、全部 9 项 AIDAP 权限及 `agentkit:*`。 - -`ensure_default_frontend_role_policy` 已负责更新并回读核验 `VeADKFrontendPolicy`,因此已有默认部署会在 `studio update` 时取得扩展后的策略文档。客户角色不被自动修改,须由运维自行授予同一组必需权限。 - -不修改密钥、状态、API、并发、网络拓扑或云资源生命周期。新增权限仅授权已批准流程当前正在尝试的操作。Runtime 绑定的角色属于独立信任边界;本次修改的是 Studio 执行角色,不是 Runtime 工作负载权限。 - -## 实现任务 - -- **T-1(FR-1–FR-6):**将仅 AIDAP 回归扩展为完整 MPA 云端策略测试,并确认修改策略前测试失败。 -- **T-2(FR-1–FR-3、FR-6):**增加 10 项缺失的明确 Action。 -- **T-3(FR-4、FR-5):**运行策略刷新和 MPA 集成回归。 -- **T-4:**运行 Ruff、Pyright、双语一致性、空白检查及默认并行 Python 回归。 -- **T-5:**另行授权后部署,并执行一次覆盖全部阶段的真实 MPA 创建重试。 - -涉及文件:`veadk/cli/frontend_deploy_policy.py`、`tests/cli/test_frontend_deploy_iam.py`、本双语 PRD、前序 PRD、双语组件规范及当前调试记录。 - -## 验证与验收 - -| 需求 | 任务 | 验收标准 | 验证 | 结果 | -| --- | --- | --- | --- | --- | -| `FR-1`–`FR-4`、`FR-6` | `T-1`、`T-2` | `AC-1`:默认策略包含完整明确的 MPA 权限集合且不引入新服务通配符 | `uv run --extra dev pytest tests/cli/test_frontend_deploy_iam.py tests/cli/test_studio_update_permissions.py -q` | `pass`,22 项测试,2026-09-23 | -| `FR-5` | `T-3` | `AC-2`:默认角色刷新及客户角色保留测试通过 | 同一目标命令 | `pass`,2026-09-23 | -| `FR-1`–`FR-6` | `T-3`、`T-4` | `AC-3`:受影响及默认回归门禁通过 | 受影响测试、Ruff、Pyright、双语/空白检查及默认并行回归 | 受影响/静态检查 `pass`;默认回归因无关基线/环境问题 `fail`,2026-09-23 | -| `FR-1`–`FR-5` | `T-5` | `AC-4`:已部署创建通过 PG、网络、网关、Worker、Skill Space、Runtime 和就绪阶段 | 经授权部署及真实冒烟 | `not_run`;未获部署授权 | - -由于不修改 UI 或 HTTP 结构,前端测试/构建不适用。静态策略检查不能证明 IAM 传播、服务开通、配额、资源兼容性或真实网络访问。 - -## 风险与恢复 - -- 服务端可能强制要求无法从直接客户端调用观察到的依赖权限;仍须真实冒烟验证。 -- 更新后 IAM 策略传播可能延迟。 -- 已有显式拒绝、服务未开通、配额限制或资源冲突仍优先生效。 -- 回滚会恢复已知不完整策略。 - -## 审查与交付记录 - -2026-09-23 直接审查沿 `provision()` 跟踪了 `RuntimeCloud`、`PGCloud`、`NetworkCloud`、`GatewayCloud`、`WorkerCloud` 以及 Skill Space/Runtime 操作,并检查最小权限、服务/Action 拼写、默认角色刷新、客户角色所有权、安全边界、兼容性、可测试性和双语一致性。用户已批准扩展后的全阶段范围。 - -完整策略测试在修改前按预期失败,并恰好列出审计所得的 10 项缺失 Action。修改后,22 项 IAM/更新测试及 418 项受影响 MPA/CLI 测试通过。`uv run --with ruff ruff check veadk/cli/frontend_deploy_policy.py tests/cli/test_frontend_deploy_iam.py`、等效 Pyright 命令、策略唯一性、双语标识及 `git diff --check` 均通过。 - -规定的默认回归命令 `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` 执行结果为 5,216 通过、11 跳过、2 xfailed、8 失败、2 个收集错误。6 个 Harness 失败和 2 个 Sandbox 收集错误需要当前未声明的可选依赖 `llama_index`/`anthropic`;2 个优化 Skill 测试因并行地域状态串扰失败,串行复跑为 2 项通过。以上均不涉及本次策略或测试文件。部署和真实 MPA 创建仍等待授权,为 `not_run`,因此生命周期状态保持 `approved`。 diff --git a/prd-spec/bugfixes/studio-mpa-artifact-entry/2026-09-30-hide-artifact-entry.md b/prd-spec/bugfixes/studio-mpa-artifact-entry/2026-09-30-hide-artifact-entry.md deleted file mode 100644 index d8a759e87..000000000 --- a/prd-spec/bugfixes/studio-mpa-artifact-entry/2026-09-30-hide-artifact-entry.md +++ /dev/null @@ -1,75 +0,0 @@ -# Hide the incompatible session artifact entry for MPA - -[中文版](2026-09-30-hide-artifact-entry.zh.md) - -- Change ID: `studio-mpa-artifact-entry`; created/revised: 2026-09-30 -- Status: approved; contract: [Studio Sandbox downloads](../../../specs/studio-sandbox-download/README.md), `CON-5` - -## Background, scope and requirements - -The composer mounts `RuntimeArtifacts` for every connected Runtime session. -Its hook fetches even while closed. That service reads Studio `artifacts/` -or a Runtime `/mnt/artifacts` mount, not an MPA Worker `/data/output` mount. -The user explicitly requested hiding this entry for MPA until supported. - -- `FR-1`: Given native or A2A MPA connections identified by existing Runtime - metadata, no artifact entry/component is mounted and its fetch hook does not run. -- `FR-2`: Given a general Runtime with a session, retain the existing entry; - local apps, no-session states and dedicated Sandbox views retain their guards. -- `FR-3`: Switching between general and MPA connections reevaluates visibility. - Existing Markdown file downloads, backend authorization, storage paths, - credentials and MPA identity/transport classification are unchanged. - -## Design and review - -Add only the two existing product predicates `isMpaRuntimeApp` and -`isMpaA2aRuntimeApp` to the composer mount condition. Do not change those -transport-sensitive predicates, hide with CSS, add a configuration switch, -infer category from image/name, or wire another storage adapter. Removing the -component also retains its existing abort/unmount behavior. No schemas, -persistence or permissions change; UI hiding is not an authorization control. -No style, typography, icon, layout or replacement notice changes. - -Direct review (`review-spec` unavailable) checked native/A2A coverage, scope, -security, testability and bilingual equivalence. User request approves this -scope. Ponytail selects the existing predicates; frontend-design preserves the -current visual system. Referenced `ui-ux-pro-max` files are unavailable; apply -the existing Studio/Foundation rules instead. No design blockers. - -## Tasks, verification and risks - -| Requirement | Task | Acceptance | Evidence | -| --- | --- | --- | --- | -| `FR-1`, `FR-2` | `T-1`: regress and change the actual composer guard | `AC-1`: native/A2A MPA hidden; general visible; existing guards retained | Source-extracted actual guard: 5 failures before the fix, 25 passes after | -| `FR-3` | `T-2`: check switching and ordinary artifact interactions | `AC-2`: current selection controls mounting without changing download APIs | Selection regression passes; 19 artifact interaction/cache tests pass; browser session check blocked as below | -| all | `T-3`: build, review and deliver | `AC-3`: synchronized docs/assets, >95% incremental coverage | 1376 Node + 47 Vitest tests pass; build and packaged assets pass; pre-commit passes | - -Run a failing regression first. Reuse the existing source-extraction test -pattern to execute the actual App guard with the real classification helpers, -including both metadata forms and absent Runtime/session/Sandbox boundaries. -Run artifact interaction tests explicitly because the default npm test does -not include them. Build matching `veadk/webui` assets, inspect the browser at -normal/narrow widths and verify general-to-MPA switching. Record all results -here. No cloud deployment or real agent execution is requested; no Python -behavior changes require rerunning the unrelated full Python suite. - -### Results and remaining browser check - -The one-line production guard reuses both existing predicates. V8 coverage of -the source-extracted actual guard with real classifiers is 100% (5/5 ranges; -counts 23/19/15/11/9), not whole-App coverage. TypeScript, Vite and widget builds -pass; packaged asset validation verifies 113 files and 350 internal references. -Generated hash-dependent bundles are updated together. Build chunk-size warnings -are non-blocking. Diff whitespace and pre-commit checks pass. - -The current source was served on local Vite port 5188 using the existing local -backend on 8765, without changing that backend or cloud resources. Browser login, -home and agent picker render; the MPA list remained in loading state after -selection, so an existing session could not be opened. Actual session visibility, -browser switching and narrow-window checks remain unverified; the source-level -matrix is not presented as browser E2E. No cloud deployment or model invocation -was performed. The remaining browser check does not justify unrelated fixes. - -Risk: incomplete connection metadata cannot identify MPA; retain the existing -classification contract rather than guessing. Future Sandbox artifact support -needs a separate adapter; this change does not implement or promise it. diff --git a/prd-spec/bugfixes/studio-mpa-artifact-entry/2026-09-30-hide-artifact-entry.zh.md b/prd-spec/bugfixes/studio-mpa-artifact-entry/2026-09-30-hide-artifact-entry.zh.md deleted file mode 100644 index 247cae43e..000000000 --- a/prd-spec/bugfixes/studio-mpa-artifact-entry/2026-09-30-hide-artifact-entry.zh.md +++ /dev/null @@ -1,62 +0,0 @@ -# 为 MPA 隐藏不兼容的会话产物入口 - -[English](2026-09-30-hide-artifact-entry.md) - -- Change ID:`studio-mpa-artifact-entry`;创建/修订:2026-09-30 -- 状态:approved;契约:[Studio Sandbox 下载](../../../specs/studio-sandbox-download/README.zh.md),`CON-5` - -## 背景、范围与需求 - -Composer 对所有已连接 Runtime 会话挂载 `RuntimeArtifacts`,其 hook 在抽屉关闭 -时也请求数据。该服务读取 Studio 的 `artifacts/` 或 Runtime 的 `/mnt/artifacts` -挂载,不读取 MPA Worker 的 `/data/output`。用户明确要求支持前先隐藏 MPA 入口。 - -- `FR-1`:给定现有 Runtime 元数据识别的原生或 A2A MPA 连接,不挂载产物入口/ - 组件,也不运行其请求 hook。 -- `FR-2`:给定有会话的普通 Runtime,保留既有入口;本地 app、无会话及独立 - Sandbox 页面保留原有条件。 -- `FR-3`:普通与 MPA 连接之间切换时重新判断显示。既有 Markdown 文件下载、 - 后端鉴权、存储路径、凭据及 MPA 身份/传输分类不变。 - -## 设计与审查 - -只在 Composer 挂载条件中增加现有产品判断 `isMpaRuntimeApp` 和 -`isMpaA2aRuntimeApp`。不改变具有传输语义的判断函数,不通过 CSS 隐藏,不增加 -配置开关,不从镜像/名称推断类别,不接入新存储适配。移除组件沿用其既有取消/ -卸载行为。无 schema、持久化或权限变化;隐藏 UI 不作为鉴权控制。 -不修改样式、字体、图标、布局,也不添加替代提示。 - -直接审查(`review-spec` 不可用)检查原生/A2A 覆盖、范围、安全、可测试性及 -双语一致性。用户请求批准该范围。Ponytail 选择复用判断;frontend-design 保留 -现有视觉体系。引用的 `ui-ux-pro-max` 文件不存在,改按 Studio/Foundation 规范 -执行。无设计阻塞项。 - -## 任务、验证与风险 - -| 需求 | 任务 | 验收 | 证据 | -| --- | --- | --- | --- | -| `FR-1`、`FR-2` | `T-1`:回归并修改实际 Composer 条件 | `AC-1`:原生/A2A MPA 隐藏、普通显示、原条件保留 | 提取实际条件执行:修复前 5 项失败,修复后 25 项通过 | -| `FR-3` | `T-2`:检查切换及普通产物交互 | `AC-2`:当前选择控制挂载,不改变下载 API | 切换回归通过;19 项产物交互/缓存测试通过;浏览器会话验证受阻,见下文 | -| 全部 | `T-3`:构建、审查及交付 | `AC-3`:文档/产物同步、增量覆盖率 >95% | 1376 项 Node + 47 项 Vitest 通过;构建/打包资源验证通过;pre-commit 通过 | - -先运行失败回归。复用现有源码提取测试模式,以实际分类 helper 执行 App 中的 -真实条件,包括两种元数据及缺 Runtime/会话/Sandbox 边界。默认 npm test 不包含 -产物交互测试,需显式执行。构建匹配的 `veadk/webui` 产物,在普通/窄屏检查浏览器, -验证普通到 MPA 的切换。结果记录在本文。没有请求云端部署或真实 Agent 执行; -无 Python 行为变化,无需重跑无关的全量 Python 测试。 - -### 结果与剩余浏览器检查 - -生产代码仅修改一行条件,复用两个既有判断。使用实际分类函数执行源码提取的 -真实条件,V8 覆盖率 100%(5/5 范围,次数 23/19/15/11/9),不是整个 App 的覆盖率。 -TypeScript、Vite 及 widget 构建通过;打包资源验证通过,覆盖 113 个文件和 -350 个内部引用。同步更新依赖 hash 的生成 bundle。构建体积告警不阻塞。 -diff 空白检查及 pre-commit 通过。 - -当前源码通过本地 Vite 5188 端口复用已有 8765 后端,未修改该后端或云资源。 -浏览器登录、首页及智能体选择器正常呈现;选中 MPA 类型后列表停留加载状态, -无法打开已有会话。真实会话显示、浏览器切换及窄屏检查尚未验证,不将源码矩阵 -测试当作浏览器 E2E。未部署云端、未调用模型;不会为剩余检查扩展修复无关问题。 - -风险:连接元数据缺失时不能识别 MPA,遵循既有分类契约,不猜测。未来 Sandbox -产物支持需要独立适配,本次不实现或承诺该能力。 diff --git a/prd-spec/bugfixes/studio-mpa-cloud-iam/2026-10-15-cloud-iam.md b/prd-spec/bugfixes/studio-mpa-cloud-iam/2026-10-15-cloud-iam.md deleted file mode 100644 index 71ea802d5..000000000 --- a/prd-spec/bugfixes/studio-mpa-cloud-iam/2026-10-15-cloud-iam.md +++ /dev/null @@ -1,52 +0,0 @@ -# Cloud Studio MPA credentials and OpenViking permissions - -[中文版](2026-10-15-cloud-iam.zh.md) - -- Date: 2026-10-15 -- Status: approved for implementation -- Scope: BytePlus cloud Studio credentials and shared Studio OpenViking policy. - -## Background and evidence - -`studio deploy` clears deployment secrets before constructing the VeFaaS function environment. Managed BytePlus creation requires either BytePlus AK/SK or an explicit credential file. The deployment currently supplies neither. The shared frontend role grants VikingDB read-only access and selected operations, but does not explicitly grant the three OpenViking provisioning operations. Local AK/SK success does not exercise this role. - -## Goals, non-goals and scenarios - -New BytePlus cloud deployments must use the mounted rotating IAM credential file for MPA creation. Both cloud providers must grant the default Studio role the operations needed to create an OV library, read readiness and retrieve its user key. Local AK/SK and explicit CLI credential selection remain unchanged. No UI, service activation, quota, model configuration, runtime image, deletion or cloud resource changes are included. Existing installations require redeployment; this change does not migrate live functions or custom roles automatically. - -## Requirements and design - -- R1: In the BytePlus branch of the allowlisted deployed environment, set `VEADK_MPA_BYTEPLUS_CREDENTIAL_FILE=/var/run/secrets/iam/credential`. Do not copy a developer machine's path or AK/SK. Domestic deployment remains unchanged. -- R2: Reuse existing profile and credential loading: explicit file takes priority and is reread per call; missing, malformed or incomplete files fail without leaking credentials. Keep standalone BytePlus's explicit-file requirement to avoid accidental cross-provider credential fallback. -- R3: Add only `vikingdb:CreateOpenVikingCollection`, `vikingdb:GetOpenVikingCollection`, and `vikingdb:AccessOpenVikingApiKey` to the existing shared custom policy. Reuse existing policy creation/update/readback verification; no deletion permission or broad write wildcard. -- R4: Document redeployment and the distinction between deployer identity and function role. Service activation, quotas and model access remain prerequisites. - -Affected implementation: `veadk/cli/cli_frontend.py` and `veadk/cli/frontend_deploy_policy.py`. Tests: `tests/cli/test_studio_deploy_target.py`, `tests/cli/test_frontend_deploy_iam.py`, `tests/integrations/mpa_managed/test_byteplus.py`. Update the paired [component contract](../../../specs/studio-mpa-creation/README.md) and managed module READMEs. Credential selection and permission contracts are affected; HTTP, task state and data contracts are unchanged. - -## Tasks and acceptance - -1. Add regression assertions to the mocked deployment flow and IAM create/update payloads; confirm failure before implementation (R1/R3). -2. Test profile-to-file loading, credential rotation, missing/invalid files and continued local/provider isolation (R2). -3. Implement the two small changes, then run targeted tests and the affected managed/deployment regression suites (R1–R3). -4. Run changed-file Ruff/Pyright, paired documentation/link checks and whitespace review; record actual results (R4). - -Acceptance: a mocked BytePlus deployment emits the mounted path and no AK/SK; domestic deployment emits no BytePlus credential path; both new and existing default role policy payloads include the three actions; per-call reads pick up rotation and reject invalid credentials. - -## Risks and design review - -`review-spec` is unavailable; direct review checked scope, security, compatibility, failure behavior, tests and bilingual equivalence. No blockers. User approval: “帮我改下”, following the proposed implementation plan and clarification that OV policy applies to both providers. The path relies on the existing VeFaaS IAM mount. Cloud acceptance of actions and real end-to-end creation require a separately authorized live deployment. Unit tests cannot prove service activation or runtime readiness. No commit, push or cloud deployment is authorized by this implementation request. - -## Verification - -2026-10-15; base `ac4b4ba6`, current change limited to two production files, three test files and paired documentation. - -- pass: regression-first run produced 5 expected failures (one BytePlus deployment payload and four provider/new-existing role combinations), with 6 passes. -- pass: `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_studio_deploy_target.py tests/cli/test_frontend_deploy_iam.py tests/cli/test_frontend_deploy_iam_vestack.py tests/cli/test_studio_deploy_permissions.py -n 2 -q --tb=short` — 1222 passed, two existing upstream deprecation warnings; two workers selected for bounded local resource use. -- pass: after strengthening deployer-secret/local-path isolation and fixing the new test's provider annotation, the three focused test functions ran again: 11 passed, one upstream warning. -- pass: changed-file Ruff check/format and secret scanning through `uv run --extra dev pre-commit run --files `; dependency/YAML hooks not_applicable. The local dev environment lacks the Ruff executable, so direct checks used the repository hook version through `uvx --from ruff==0.11.12 ruff` without changing global configuration. -- fail (pre-existing): `uvx pyright --pythonpath .venv/bin/python ` reports 47 existing errors. A temporary copy of the same files from HEAD, checked with the same project/interpreter, reports exactly the same diagnostics by file, rule and message; no added errors. The initial new test annotation error was fixed before this comparison. No unrelated type errors were changed. -- pass: paired design identifiers, relative links, manual bilingual/contract review and `git diff --check`. Final implementation review confirms per-call credential loading remains unchanged, no deployment AK/SK is copied, existing policy verification is reused and no broad OV write/delete permission is added. -- not_applicable: frontend build/browser and smoke gates (no UI, HTTP, harness or sidecar changes). -- not_run: live deployment/E2E (no deployment authorized); full-repository tests/pre-commit-all (no commit requested; affected regression and changed-file hooks completed). - -R1–R4 are implemented and locally covered. Existing installations still require redeployment; actual cloud readiness remains unverified. Existing unrelated APM design files were preserved. diff --git a/prd-spec/bugfixes/studio-mpa-cloud-iam/2026-10-15-cloud-iam.zh.md b/prd-spec/bugfixes/studio-mpa-cloud-iam/2026-10-15-cloud-iam.zh.md deleted file mode 100644 index 553e94619..000000000 --- a/prd-spec/bugfixes/studio-mpa-cloud-iam/2026-10-15-cloud-iam.zh.md +++ /dev/null @@ -1,52 +0,0 @@ -# 云端 Studio MPA 凭据与 OpenViking 权限 - -[English](2026-10-15-cloud-iam.md) - -- 日期:2026-10-15 -- 状态:已批准实施 -- 范围:BytePlus 云端 Studio 凭据及共享 Studio OpenViking 策略。 - -## 背景与证据 - -`studio deploy` 在构造 VeFaaS 函数环境前清除部署密钥。托管 BytePlus 创建要求 BytePlus AK/SK 或显式凭据文件,但部署目前没有提供这两种来源。共享前端角色具有 VikingDB 只读权限及部分操作,没有明确授权三个 OpenViking 准备操作。本地 AK/SK 创建成功并未验证该角色。 - -## 目标、非目标与场景 - -新部署的 BytePlus 云端 Studio 应使用挂载的轮转 IAM 凭据文件创建 MPA。两个云平台的默认 Studio 角色均应具备创建 OV 库、查询就绪状态及获取用户 Key 的权限。本地 AK/SK 和显式 CLI 凭据选择不变。不涉及 UI、服务开通、配额、模型配置、Runtime 镜像、删除或云资源变更。已有部署需要重新部署;本改动不会自动迁移线上函数或自定义角色。 - -## 需求与设计 - -- R1:在部署环境白名单的 BytePlus 分支写入 `VEADK_MPA_BYTEPLUS_CREDENTIAL_FILE=/var/run/secrets/iam/credential`。不复制开发机路径或 AK/SK。国内部署不变。 -- R2:复用现有 profile 和凭据读取:显式文件优先,每次调用重新读取;文件缺失、格式错误或字段不完整时明确失败且不泄露凭据。保留独立 BytePlus 调用要求显式文件的规则,避免意外跨平台回退。 -- R3:在现有共享自定义策略中仅增加 `vikingdb:CreateOpenVikingCollection`、`vikingdb:GetOpenVikingCollection`、`vikingdb:AccessOpenVikingApiKey`。复用策略创建、更新及读回校验;不新增删除权限或宽泛写权限通配符。 -- R4:说明重新部署要求及部署者身份与函数角色的区别。服务开通、配额及模型访问仍为前置条件。 - -涉及实现:`veadk/cli/cli_frontend.py`、`veadk/cli/frontend_deploy_policy.py`。测试:`tests/cli/test_studio_deploy_target.py`、`tests/cli/test_frontend_deploy_iam.py`、`tests/integrations/mpa_managed/test_byteplus.py`。同步双语[组件契约](../../../specs/studio-mpa-creation/README.zh.md)及 managed 模块 README。影响凭据选择与权限契约;HTTP、任务状态及数据契约不变。 - -## 任务与验收 - -1. 在模拟部署流程及 IAM 创建/更新载荷中增加回归断言,先确认旧实现失败(R1/R3)。 -2. 验证 profile 到文件读取、凭据轮换、文件缺失/非法及本地/平台隔离(R2)。 -3. 实施两处小改动,运行定向测试及受影响的 managed/部署回归(R1–R3)。 -4. 运行改动文件 Ruff/Pyright、双语文档/链接检查及空白审查,记录实际结果(R4)。 - -验收:模拟 BytePlus 部署输出挂载路径且不输出 AK/SK;国内部署不输出 BytePlus 凭据路径;新建和已有默认角色策略载荷包含三个动作;每次读取可识别轮换并拒绝非法凭据。 - -## 风险与设计评审 - -`review-spec` 不可用;直接评审了范围、安全、兼容性、失败行为、测试及双语等价性,无阻断项。用户在实现方案及 OV 策略同时适用于国内的说明后回复“帮我改下”,批准实施。路径依赖现有 VeFaaS IAM 挂载。云端是否接受动作及真实端到端创建需要另行授权的实际部署。单元测试不能证明服务开通或 Runtime 就绪。本次实施请求不授权提交、推送或云端部署。 - -## 验证 - -2026-10-15;基线 `ac4b4ba6`,当前改动仅涉及两个生产文件、三个测试文件及双语文档。 - -- pass:先行回归产生 5 个预期失败(一个 BytePlus 部署载荷及四个平台/新旧角色组合),另有 6 项通过。 -- pass:`uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_studio_deploy_target.py tests/cli/test_frontend_deploy_iam.py tests/cli/test_frontend_deploy_iam_vestack.py tests/cli/test_studio_deploy_permissions.py -n 2 -q --tb=short` — 1222 项通过,两个已有上游弃用警告;使用两个 worker 控制本机资源占用。 -- pass:加强部署者密钥/本地路径隔离并修复新增测试的平台类型注解后,重新执行三个定向测试函数:11 项通过,一个上游警告。 -- pass:通过 `uv run --extra dev pre-commit run --files ` 完成改动文件 Ruff 检查/格式化和密钥扫描;依赖/YAML hook 为 not_applicable。本地开发环境没有 Ruff 可执行文件,因此直接检查使用仓库 hook 版本 `uvx --from ruff==0.11.12 ruff`,未修改全局配置。 -- fail(既有问题):`uvx pyright --pythonpath .venv/bin/python ` 报告 47 个已有错误。将 HEAD 中相同文件复制到临时目录,以相同项目/解释器检查,按文件、规则和消息对比诊断完全一致,没有新增错误。首次发现的新增测试注解错误已在比较前修复;未修改无关类型错误。 -- pass:双语设计标识符、相对链接、人工双语/契约评审和 `git diff --check`。最终实现审查确认逐次凭据读取不变、不复制部署 AK/SK、复用已有策略校验、不增加宽泛 OV 写/删除权限。 -- not_applicable:前端构建/浏览器及 smoke 门禁(无 UI、HTTP、harness 或 sidecar 改动)。 -- not_run:真实部署/E2E(未授权部署);全仓库测试/pre-commit-all(未要求提交,已完成受影响回归和改动文件 hook)。 - -R1–R4 已实现并有本地覆盖。已有部署仍需重新部署,真实云端就绪尚未验证。保留了已有的无关 APM 设计文件。 diff --git a/prd-spec/bugfixes/studio-mpa-creation/2026-09-24-start-another-agent-after-failure.md b/prd-spec/bugfixes/studio-mpa-creation/2026-09-24-start-another-agent-after-failure.md deleted file mode 100644 index 5d333d4e3..000000000 --- a/prd-spec/bugfixes/studio-mpa-creation/2026-09-24-start-another-agent-after-failure.md +++ /dev/null @@ -1,55 +0,0 @@ -# Start another MPA agent after a failed creation - -[中文版](2026-09-24-start-another-agent-after-failure.zh.md) - -- Change ID: `studio-mpa-new-after-failure` -- Date: 2026-09-24 -- Status: approved by the user's request to start a different agent after failure -- Component: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md) - -## Background and evidence - -The creation dialog persists a submitted `requestId`, `agentId`, and `taskId` in `sessionStorage` and restores them on reopen. It clears that entry only after success. After a failed or cancelled task, closing and opening the create card therefore returns to the old task with only a retry action. The generated agent ID is read-only, so the user cannot begin a different agent in the UI. - -## Goals, non-goals and scenarios - -Offer an explicit way to start a different MPA agent after a **known terminal failure or cancellation**. Preserve same-ID retry and server-side task/resource records. Do not reset running tasks or an uncertain POST result, delete cloud resources, alter the task API, or persist OpenViking credentials. - -- A failed or cancelled task shows both retry and a separate new-agent action. -- Choosing the new-agent action clears only the browser recovery entry, generates a fresh request ID and agent ID, resets the three-step form to basics, and restores server defaults for image and PG fields. -- Closing and reopening the new draft keeps the new identity; the old task is not submitted again. -- Running, cancelling and uncertain-submission states do not offer this action. - -## Requirements and design - -- **FR-1:** Show a localized new-agent action only for confirmed `failed` or `cancelled` task snapshots, alongside the existing retry path. -- **FR-2:** On selection, replace the old session entry and clear the in-memory task identity; generate a new UUID and `mi-` ID with the existing length and reset description, OpenViking inputs and wizard step. Prefill images and manual PG target from the current safe config. -- **FR-3:** Leave server tasks and registered cloud resources untouched. Keep pending/unknown responses on their original identity to preserve idempotent recovery. Never store the OpenViking API Key. - -Affected files: creation dialog, localized UI text, dialog tests, Studio component contract, and frontend user documentation. No backend or cloud API change is required. - -## Tasks, tests and risks - -1. Add a failing dialog regression test for reopening a failed task and starting a different agent, including fresh IDs, cleared secret and persisted new draft. Cover cancellation and absence while running. -2. Implement the terminal-state action with existing buttons and form state; retain retry semantics. -3. Run focused frontend tests, the full frontend test/build/asset gates and a browser interaction check where available. - -An accidental reset could abandon a recoverable task. Restrict the action to confirmed terminal states and keep the old server task and resources unchanged. Acceptance requires a user-visible new path, distinct request and agent IDs, no stale task status after reset, and unchanged same-ID retry. - -## Review and verification - -Design review: resetting only after a confirmed terminal task keeps the server's idempotency boundary intact. The explicit action makes abandoning the old recovery draft deliberate; no cleanup or deletion is implied. The existing session key and component buttons are reused. The English and Chinese contracts and instructions were checked for equivalent behavior. - -Verification on 2026-09-24, working-tree diff on `feat/from-main-20260923`: - -| Check | Result | Evidence | -| --- | --- | --- | -| Focused dialog regression | pass | `./node_modules/.bin/vitest run tests/mpaCreation.test.tsx`: 22 passed, including failed/cancelled task, fresh submission, secret reset, and running/uncertain guards. | -| Frontend tests | pass | `npm test`: 1305 Node tests and 39 Vitest tests passed. | -| Localization | pass | `npm run check:i18n`: two locales and 21 namespaces consistent. | -| Frontend build | pass | `npm run build`: both Vite bundles built; existing chunk-size and dynamic-import warnings remain. | -| Packaged assets | pass | `npm run test:webui-assets`: 104 files and 248 internal references verified. | -| Diff whitespace | pass | `git diff --check`. | -| Browser creation-dialog interaction | blocked | A local static server loaded the generated HTML/JS, but `/web/ui-config` and the login endpoints returned 404 without the Studio backend. No live cloud creation was attempted. | - -The build refreshed hashed `veadk/webui` assets, including dependent Mermaid chunks. The asset check verifies that the updated references resolve. No backend, cloud resource or existing task was changed by the new action. diff --git a/prd-spec/bugfixes/studio-mpa-creation/2026-09-24-start-another-agent-after-failure.zh.md b/prd-spec/bugfixes/studio-mpa-creation/2026-09-24-start-another-agent-after-failure.zh.md deleted file mode 100644 index fe68f2826..000000000 --- a/prd-spec/bugfixes/studio-mpa-creation/2026-09-24-start-another-agent-after-failure.zh.md +++ /dev/null @@ -1,55 +0,0 @@ -# 创建失败后新建另一个 MPA 智能体 - -[English](2026-09-24-start-another-agent-after-failure.md) - -- Change ID: `studio-mpa-new-after-failure` -- Date: 2026-09-24 -- Status: 用户要求在失败后创建不同的智能体,已批准该范围 -- Component: [Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md) - -## 背景与证据 - -创建弹窗将已提交的 `requestId`、`agentId` 和 `taskId` 保存在 `sessionStorage`,重新打开时恢复;只有成功后才清除。任务失败或取消后,关闭再点击创建卡片仍会回到旧任务,界面仅提供重试。生成的智能体 ID 只读,用户无法在界面中开始创建另一个智能体。 - -## 目标、非目标与场景 - -在**已确认失败或取消**后,提供明确的新建其他 MPA 智能体方式。保留原 ID 重试以及服务端任务和资源记录。不重置运行中任务或结果不明的 POST,不删除云资源,不修改任务 API,也不持久化 OpenViking 凭据。 - -- 失败或取消的任务同时显示重试和独立的新建操作。 -- 选择新建后,仅清除浏览器恢复记录,生成新的请求 ID 和智能体 ID,三步表单回到基础信息,并恢复服务端的镜像及 PG 字段默认值。 -- 关闭并重新打开新草稿时仍使用新的身份,不再次提交旧任务。 -- 运行中、取消中及提交结果不明时不提供该操作。 - -## 需求与设计 - -- **FR-1:**仅在任务快照明确为 `failed` 或 `cancelled` 时展示本地化的新建操作,同时保留现有重试路径。 -- **FR-2:**点击后替换旧会话记录并清除内存中的任务身份;生成新的 UUID 和符合现有长度的 `mi-` ID,重置描述、OpenViking 输入及向导步骤,并使用当前安全配置预填镜像和手动 PG 目标。 -- **FR-3:**不改动服务端任务及已登记云资源。等待中或结果未知的请求仍保持原身份,以便幂等恢复。OpenViking API Key 不进入浏览器存储。 - -涉及文件:创建弹窗、双语界面文本、弹窗测试、Studio 组件契约和前端用户文档。无需修改后端或云接口。 - -## 任务、测试与风险 - -1. 先加失败回归测试,覆盖重新打开失败任务后新建、ID 更新、密钥清空及新草稿持久化;覆盖取消态和运行态不展示新建。 -2. 使用现有按钮与表单状态实现终态操作,保持重试语义。 -3. 运行定向前端测试、全部前端测试/构建/产物检查;环境允许时做真实浏览器交互检查。 - -误触重置可能放弃可恢复任务,因此操作仅在确认终态时提供,且旧任务与资源仍保留在服务端。验收要求存在清晰的新建入口、不同的请求及智能体 ID、重置后不显示旧任务状态,并保持同 ID 重试。 - -## 审查与验证 - -设计审查:仅在确认任务终态后重置,不破坏服务端幂等边界。显式操作让放弃旧恢复草稿成为用户主动选择;不暗示清理或删除。复用现有会话键和组件按钮。已核对中英文契约与说明的行为一致。 - -2026-09-24 对 `feat/from-main-20260923` 工作区差异的验证: - -| 检查 | 结果 | 证据 | -| --- | --- | --- | -| 创建弹窗定向回归 | pass | `./node_modules/.bin/vitest run tests/mpaCreation.test.tsx`:22 项通过,覆盖失败/取消、新身份提交、密钥清空及运行中/结果不明保护。 | -| 前端测试 | pass | `npm test`:1305 项 Node 测试和 39 项 Vitest 测试通过。 | -| 本地化 | pass | `npm run check:i18n`:两种语言、21 个命名空间一致。 | -| 前端构建 | pass | `npm run build`:两份 Vite 产物构建成功;仍有已有的包大小及动态导入警告。 | -| 打包资源 | pass | `npm run test:webui-assets`:验证 104 个文件及 248 处内部引用。 | -| 差异空白检查 | pass | `git diff --check`。 | -| 浏览器创建弹窗交互 | blocked | 本地静态服务已加载生成的 HTML/JS,但缺少 Studio 后端,`/web/ui-config` 和登录接口返回 404,因此无法进入创建弹窗。未执行真实云端创建。 | - -构建刷新了 `veadk/webui` 中带哈希的资源文件,包括依赖的 Mermaid 分块;资源检查确认更新后的引用都能解析。新建操作本身不修改后端、云资源或旧任务。 diff --git a/prd-spec/bugfixes/studio-mpa-dialog-theme/2026-09-20-dialog-theme.md b/prd-spec/bugfixes/studio-mpa-dialog-theme/2026-09-20-dialog-theme.md deleted file mode 100644 index d36ae3df4..000000000 --- a/prd-spec/bugfixes/studio-mpa-dialog-theme/2026-09-20-dialog-theme.md +++ /dev/null @@ -1,50 +0,0 @@ -# Studio MPA dialog theme repair - -[中文版](2026-09-20-dialog-theme.zh.md) - -- Change ID: `studio-mpa-dialog-theme`; created/revised: 2026-09-20. -- Status: implemented. User approved the proposed repair with “改一下”. -- Related contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md); [frontend standard](../../../frontend/SPEC.md). - -## Evidence and goals - -The production entry imports legacy page styles but omits the component library theme styles. The built CSS has no definitions of `--studio-text-primary` or `--studio-bg-elevated`. MpaCreateDialog uses these variables without fallbacks while ModalLayout falls back to a dark background, leaving inherited near-black body text unreadable. The supplied screenshot reproduces this on the light agents page. - -Restore readable dialog labels, values, plan and error messages using existing component themes. Do not change provisioning APIs, configuration, task state, deployment or resource allocation. - -## Requirements and design - -- FR-1: Load the existing theme and component-theme CSS through the production Studio entry before rendering; do not create another palette or override primitive styles. -- FR-2: With no explicit root theme, initialize `data-theme=light` to match the existing page. Preserve an explicit light or dark root theme. Do not change saved preferences or component-preview defaults. -- FR-3: The dialog, inputs, textarea and action buttons must use coherent colors; labels and body text must have at least 4.5:1 contrast. Preserve missing-config submission blocking, retry, close and keyboard behavior at desktop and 390px width. - -Changes are limited to `frontend/src/main.tsx`, this bilingual record, a short frontend README note and matching generated `veadk/webui` assets. Loading shared styles may correct other library components too; check their scoped selectors and keep the legacy page surface unchanged. No public API, persistence, permission or component contract changes: this restores the already documented themed-component behavior. No backend tests or cloud operations apply. - -## Tasks and acceptance - -- T-1 / AC-1: Record the missing variables and failed browser contrast before editing production code. -- T-2 / AC-2: Load both existing stylesheets and initialize the absent root theme; verify light/dark styles and unchanged explicit-theme selection. -- T-3 / AC-3: Run `npm --prefix frontend test`, `npm --prefix frontend run build`, and `npm --prefix frontend run test:webui-assets`; verify the actual dialog in a browser with isolated configuration responses, readable errors, retry, keyboard focus and a narrow viewport. -- T-4 / AC-4: Review the diff, preserve unrelated working changes, and record real outcomes in both languages. No commit, image publication or deployment is part of this repair. - -## Review, risks and verification - -Direct design review: pass. The existing component styles own button/textarea light variants, so loading only the base token stylesheet would be incomplete. Root theme initialization must precede rendering and must not overwrite an explicit theme. Bilingual scope and identifiers match. The referenced frontend-design and ui-ux-pro-max skills are absent from the repository and installed skill roots; follow the checked-in frontend standard. A sibling repository's review-spec skill owns a different workflow and is not applied here. - -Verification pending. Use isolated browser fixtures, not real provisioning calls. Native OS IME interaction and real cloud configuration remain outside this visual repair. - - -### Verification record (2026-09-20) - -Scope: six added production-entry lines, README, this design pair and regenerated package assets; unrelated existing working-tree changes preserved. T-1–T-4 / AC-1–AC-4 are complete. - -- **pass — baseline browser reproduction:** actual main.tsx and MpaCreateDialog with isolated API responses rendered `rgb(9, 9, 11)` text on `rgb(28, 28, 30)`; theme token was empty. -- **pass — browser after repair:** default light background `rgb(255, 255, 255)`, secondary text contrast 6.23:1, primary labels/values 14.53:1. Explicit dark theme retained with light text. At 390×844 the 358px-wide modal fits the viewport; missing-configuration and loading messages remain visible. Retry enables creation, multiline Chinese text and Tab work, Escape closes the dialog. No live creation request was made. Native OS IME was not automated. -- **pass — frontend tests:** `npm --prefix frontend test`: 1208 Node tests and 22 Vitest tests. Existing dependency/deprecation warnings only. -- **pass — production build:** `npm --prefix frontend run build`, including TypeScript and both application/widget builds. Existing large-bundle warning retained. -- **pass — package assets:** `npm --prefix frontend run test:webui-assets`: 104 files, 248 references. No temporary fixture code included in built assets. -- **pass — review:** shared theme selectors and fallback behavior inspected; no duplicated colors, primitive overrides, backend/API changes or new dependencies. Paired-language scope and identifiers checked; `git diff --check` passes. Temporary fixture/server/tabs removed and viewport reset. -- **not_run — Gitleaks:** executable unavailable on PATH and checked local locations; manually reviewed the six added runtime lines and documentation, which contain no credential material. No commit or publication performed; repository pre-commit remains required before an authorized commit. -- **not_applicable — Python/Ruff/Pyright/cloud verification:** no Python or cloud behavior changed. The server prerequisite error in the screenshot is preserved and is not fixed by a theme repair. - -Preview recovery: changing the temporary Vite configuration caused blank hot reloads; an isolated cache fixed the verification environment. Production config was not changed. The package build succeeded with the normal config. diff --git a/prd-spec/bugfixes/studio-mpa-dialog-theme/2026-09-20-dialog-theme.zh.md b/prd-spec/bugfixes/studio-mpa-dialog-theme/2026-09-20-dialog-theme.zh.md deleted file mode 100644 index 3438bfd1b..000000000 --- a/prd-spec/bugfixes/studio-mpa-dialog-theme/2026-09-20-dialog-theme.zh.md +++ /dev/null @@ -1,50 +0,0 @@ -# Studio MPA 弹窗主题修复 - -[English](2026-09-20-dialog-theme.md) - -- 变更 ID:`studio-mpa-dialog-theme`;创建/更新:2026-09-20。 -- 状态:implemented。用户以“改一下”批准了已提出的修复方案。 -- 相关契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md);[前端规范](../../../frontend/SPEC.md)。 - -## 证据与目标 - -生产入口加载了旧页面样式,却未加载组件库主题样式。构建 CSS 中没有 `--studio-text-primary` 和 `--studio-bg-elevated` 的定义。MpaCreateDialog 使用这些变量且没有回退值,而 ModalLayout 回退到深色背景,正文继承的近黑文字因此无法阅读。用户截图在浅色智能体页面复现了问题。 - -使用现有组件主题恢复标签、输入值、创建计划和错误信息的可读性。不改变资源创建 API、配置、任务状态、部署或资源分配。 - -## 需求与设计 - -- FR-1:在生产 Studio 入口渲染前加载现有主题与组件主题 CSS;不新建配色体系,不覆盖基础组件样式。 -- FR-2:根节点未指定主题时初始化 `data-theme=light`,与已有页面一致;保留显式指定的浅色或深色主题。不修改持久化偏好或组件预览的默认主题。 -- FR-3:弹窗、输入框、文本域和按钮使用一致配色;标签与正文对比度至少为 4.5:1。桌面及 390px 窗口下保留缺配置禁止提交、重试、关闭及键盘行为。 - -修改限定于 `frontend/src/main.tsx`、本双语记录、前端 README 简短说明和对应的 `veadk/webui` 构建产物。加载共享样式也可能修正其他组件库控件;检查其作用域选择器并保持旧页面背景不变。无公共 API、持久化、权限或组件契约变更:本次恢复已有文档约定的组件主题行为。不涉及后端测试或云操作。 - -## 任务与验收 - -- T-1 / AC-1:修改生产代码前记录变量缺失和浏览器对比度失败。 -- T-2 / AC-2:加载两个现有样式表并初始化缺失的根主题;验证浅色、深色配色以及显式主题不被覆盖。 -- T-3 / AC-3:运行 `npm --prefix frontend test`、`npm --prefix frontend run build` 和 `npm --prefix frontend run test:webui-assets`;以隔离的配置响应在真实浏览器中验证实际弹窗、错误信息、重试、键盘焦点和窄窗口。 -- T-4 / AC-4:检查差异、保留无关工作区修改,并在双语记录中写明实际结果。本次不包含提交、镜像发布或部署。 - -## 评审、风险与验证 - -直接设计评审:pass。现有组件样式维护按钮和文本域的浅色变体,因此仅加载基础主题变量仍不完整。根主题初始化必须先于渲染,不能覆盖显式主题。双语范围与标识一致。仓库引用的 frontend-design、ui-ux-pro-max 技能在仓库和已安装技能目录中均缺失,按仓库前端规范执行。兄弟仓库的 review-spec 技能属于另一套工作流,本次不套用。 - -验证待执行。使用隔离浏览器用例,不调用真实资源创建。原生操作系统输入法交互及真实云配置不属于本次视觉修复范围。 - - -### 验证记录(2026-09-20) - -范围:生产入口新增六行、README、本双语方案和重新生成的包内静态资源;保留已有无关工作区修改。T-1–T-4 / AC-1–AC-4 已完成。 - -- **pass — 修复前浏览器复现:** 使用实际 main.tsx、MpaCreateDialog 和隔离 API 响应,观察到 `rgb(9, 9, 11)` 文字配 `rgb(28, 28, 30)` 背景,主题变量为空。 -- **pass — 修复后浏览器:** 默认浅色背景为 `rgb(255, 255, 255)`,次要文字对比度 6.23:1,主要标签和输入值为 14.53:1。显式深色主题保持深底浅字。390×844 窗口中弹窗宽 358px,完整位于视口内;缺配置及加载提示清晰。重新检查后允许创建,多行中文输入和 Tab 正常,Escape 可关闭弹窗。未发起真实创建请求,未自动化操作原生输入法候选窗口。 -- **pass — 前端测试:** `npm --prefix frontend test`:1208 项 Node 测试及 22 项 Vitest 测试通过;只有已有依赖/弃用警告。 -- **pass — 生产构建:** `npm --prefix frontend run build`,包含 TypeScript 和应用/嵌入组件两次构建;保留已有大包警告。 -- **pass — 包内资源:** `npm --prefix frontend run test:webui-assets`:104 个文件、248 处引用通过;构建资源不含临时测试页面代码。 -- **pass — 复核:** 检查共享主题选择器及回退行为;没有重复配色、基础组件覆盖、后端/API 改动或新依赖。双语范围及标识一致,`git diff --check` 通过。临时测试页面、服务器和浏览器标签已清理,视口已恢复。 -- **not_run — Gitleaks:** PATH 和已检查的本地位置没有可执行文件;已人工检查新增六行运行时代码和文档,不包含凭据。没有提交或发布;今后授权提交前仍需执行仓库 pre-commit。 -- **not_applicable — Python/Ruff/Pyright/云验证:** 没有 Python 或云行为变化。截图中的服务端前置配置错误保留,主题修复不解决该错误。 - -预览恢复:修改临时 Vite 配置后热重载出现空白,使用独立缓存恢复验证环境。没有修改生产 Vite 配置,正常配置下的包构建成功。 diff --git a/prd-spec/bugfixes/studio-mpa-profile-errors/2026-09-20-studio-mpa-profile-errors.md b/prd-spec/bugfixes/studio-mpa-profile-errors/2026-09-20-studio-mpa-profile-errors.md deleted file mode 100644 index a310d2d6a..000000000 --- a/prd-spec/bugfixes/studio-mpa-profile-errors/2026-09-20-studio-mpa-profile-errors.md +++ /dev/null @@ -1,97 +0,0 @@ -# Studio MPA Profile Error Handling Bugfix - -[中文版](2026-09-20-studio-mpa-profile-errors.zh.md) - -## Metadata - -- Change ID: `studio-mpa-profile-errors` -- Created: 2026-09-20 -- Revised: 2026-09-20 -- Status: `implemented` -- Related component specs: [Studio MPA Control Plane](../../../specs/studio-mpa-control-plane/README.md), [MPA Runtime Control](../../../specs/mpa-runtime-control/README.md) - -## Background and Evidence - -Local Studio Profile validation exposed three distinct failures: - -- `GET /web/mpa/agent-operations?status=active` returned `503 mpa_operation_service_unavailable` when Studio was launched without `--dev` and no Studio TOS storage was configured. -- The selected Runtime `r-yev5cmzp4wzn6n5iodgd` in `cn-beijing` is `Ready` but still runs an older MPA image, version 14. Its native session API returns `401 {"detail":"X-Jwt-Token header is required"}` through `/web/runtime-proxy/.../api/v1/sessions`. -- The same Runtime's Profile status endpoint returns a plain `404 {"detail":"Not Found"}`, which previously surfaced as a raw safe error instead of the existing `profile_not_found`/orphan Runtime state. -- Current MPA Runtime images may expose Agent metadata only through the A2A virtual app. For example, Runtime `r-yeuujrrcowb21078p9jh` returns `200` for `/web/agent-info/a2a-default`, while `/web/agent-info/default` returns `404`, which previously surfaced as "Unable to load Agent details". - -The first issue is a local launch-mode problem. The latter two are compatibility and presentation problems for old Runtime images that predate the AgentKit `Authorization` adapter and Profile status contract. - -## Goals - -- Start the local Studio in a configuration where the MPA operation service is available without production TOS. -- Preserve fail-closed Runtime authentication behavior; do not synthesize or forward `X-Jwt-Token`. -- Show actionable, localized UI errors for old Runtime images and keep the Profile page usable enough to show Runtime identity and capability state. -- Keep Profile writes, Session configuration, and Debug disabled when the Runtime contract is not compatible. - -## Non-Goals - -- No cloud Runtime mutation, image rollout, or automatic Runtime upgrade. -- No change to the Runtime authentication contract: current AgentKit-mode Runtime images must keep using `Authorization`. -- No submission of local browser evidence or other test artifacts. - -## Requirements - -| ID | Requirement | -| --- | --- | -| `FR-1` | Local `veadk studio --dev --vite` without Studio TOS must expose the process-local MPA operation repository so active operation recovery returns `200`. | -| `FR-2` | An upstream `401` whose detail requires `X-Jwt-Token` must be classified as `runtime_legacy_auth_unsupported`, not a generic session-list or runtime request failure. | -| `FR-3` | `GET /web/mpa/agents/{mpaInstanceId}/view` must return a normal `MpaAgentView` with Runtime metadata and a safe error for old or missing Profile endpoints instead of failing the whole page. | -| `FR-4` | The Profile page must display localized guidance and disable Profile write, Session config, and Debug controls when the Runtime image is not compatible. | -| `FR-5` | Runtime-backed Agent details must fall back to the A2A virtual app metadata when `default` agent metadata is absent. | - -## Design - -`frontend/server/mpa/runtime_client.py` now classifies upstream string error details: - -- `X-Jwt-Token header is required` becomes `runtime_legacy_auth_unsupported`. -- plain 404 text from `GET .../profile-status` becomes `profile_not_found`. - -`frontend/server/mpa/routes.py` treats `runtime_legacy_auth_unsupported` as a safe Profile-view error. The response keeps `bindingStatus=bound`, includes the Runtime metadata, omits Profile status, and returns `canWrite=false` plus `canDebug=false`. - -`frontend/src/adk/client.ts` uses structured error parsing for Session list failures. For MPA Runtime sessions, the old `X-Jwt-Token` 401 becomes a localized upgrade message that preserves the raw detail for diagnosis. - -`frontend/src/ui/AgentWorkspace.tsx` maps `safeError.code` to localized MPA control-plane messages and uses `capabilities.canWrite` to decide whether the Profile apply button is enabled. - -`frontend/src/adk/client.ts` now falls back from `/web/agent-info/{app}` to `/web/agent-info/a2a-default` when the original agent-info request returns 404. It still propagates non-404, authentication, and server failures. - -Local validation uses: - -```bash -APP_ENV=development VEADK_MPA_TEST_SCENARIOS=0 uv run veadk studio --dev --host 127.0.0.1 --port 18179 --vite --no-open -VEADK_API_TARGET=http://127.0.0.1:18179 npm --prefix frontend run dev -- --host 127.0.0.1 --port 5179 -``` - -## Affected Files - -- `frontend/server/mpa/runtime_client.py` -- `frontend/server/mpa/routes.py` -- `frontend/src/adk/client.ts` -- `frontend/src/ui/AgentWorkspace.tsx` -- `frontend/src/i18n/resources/en-US/adk.json` -- `frontend/src/i18n/resources/zh-CN/adk.json` -- `frontend/src/i18n/resources/en-US/ui.json` -- `frontend/src/i18n/resources/zh-CN/ui.json` -- `tests/frontend/server/mpa/test_runtime_profile_routes.py` -- `frontend/tests/runSseAbort.test.mjs` -- `frontend/tests/agentWorkspace.test.mjs` - -## Verification - -| Check | Result | Evidence | -| --- | --- | --- | -| Backend Profile routes | pass | `uv run --extra dev pytest tests/frontend/server/mpa/test_runtime_profile_routes.py -q` returned 28 passed. | -| Frontend source-contract tests | pass | `npm --prefix frontend test -- agentWorkspace.test.mjs runSseAbort.test.mjs` returned 1244 passed because the script still runs the repository frontend test set. | -| i18n parity | pass | `npm --prefix frontend run check:i18n` returned pass for 2 locales and 21 namespaces. | -| Local operation recovery endpoint | pass | With `--dev`, `GET /web/mpa/agent-operations?status=active` through Vite returned `200 {"operations":[]}`. | -| Old Runtime profile view | pass | `GET /web/mpa/agents/r-yev5cmzp4wzn6n5iodgd/view?...` returned `200`, Runtime metadata, `bindingStatus=orphan_runtime`, `canDebug=false`, and `safeError.code=profile_not_found`. | -| Old Runtime session list | expected limited | `GET /web/runtime-proxy/r-yev5cmzp4wzn6n5iodgd/api/v1/sessions?...` still returns upstream `401 X-Jwt-Token header is required`; the client now presents this as an old Runtime image compatibility message. | -| A2A agent metadata fallback | pass | `GET /web/runtime-proxy/r-yeuujrrcowb21078p9jh/web/agent-info/default?...` returned `404`, while `/web/agent-info/a2a-default?...` returned `200`; `getAgentInfo` now retries the A2A path and the frontend regression is covered by `runSseAbort.test.mjs`. | - -## Risks and Follow-Up - -The selected Runtime version 14 cannot be made fully compatible from Studio without updating the deployed Runtime image. The user should validate against a current Runtime image such as the already verified version 62 path for full Session list and chat behavior. diff --git a/prd-spec/bugfixes/studio-mpa-profile-errors/2026-09-20-studio-mpa-profile-errors.zh.md b/prd-spec/bugfixes/studio-mpa-profile-errors/2026-09-20-studio-mpa-profile-errors.zh.md deleted file mode 100644 index adeaba9de..000000000 --- a/prd-spec/bugfixes/studio-mpa-profile-errors/2026-09-20-studio-mpa-profile-errors.zh.md +++ /dev/null @@ -1,97 +0,0 @@ -# Studio MPA Profile 错误处理修复 - -[English](2026-09-20-studio-mpa-profile-errors.md) - -## 元数据 - -- Change ID:`studio-mpa-profile-errors` -- 创建日期:2026-09-20 -- 修订日期:2026-09-20 -- 状态:`implemented` -- 相关组件规范:[Studio MPA Control Plane](../../../specs/studio-mpa-control-plane/README.zh.md)、[MPA Runtime Control](../../../specs/mpa-runtime-control/README.zh.md) - -## 背景与证据 - -本地 Studio Profile 验证暴露了三个独立问题: - -- Studio 未带 `--dev` 启动且没有配置 Studio TOS 存储时,`GET /web/mpa/agent-operations?status=active` 返回 `503 mpa_operation_service_unavailable`。 -- 当前选中的 Runtime `r-yev5cmzp4wzn6n5iodgd` 位于 `cn-beijing`,状态为 `Ready`,但仍运行较旧的 MPA 镜像 version 14。它通过 `/web/runtime-proxy/.../api/v1/sessions` 调用原生 Session API 时返回 `401 {"detail":"X-Jwt-Token header is required"}`。 -- 同一 Runtime 的 Profile status endpoint 返回普通 `404 {"detail":"Not Found"}`,此前会显示为原始 safe error,而不是现有的 `profile_not_found`/orphan Runtime 状态。 -- 当前 MPA Runtime 镜像可能只通过 A2A virtual app 暴露 Agent 元数据。例如 Runtime `r-yeuujrrcowb21078p9jh` 的 `/web/agent-info/a2a-default` 返回 `200`,但 `/web/agent-info/default` 返回 `404`,此前会表现为“无法加载 Agent 详情”。 - -第一个问题属于本地启动模式问题。后两个问题属于旧 Runtime 镜像兼容性与展示问题;这些镜像早于 AgentKit `Authorization` 适配和 Profile status 契约。 - -## 目标 - -- 本地 Studio 使用无需生产 TOS 的配置启动时,应可启用 MPA operation service。 -- 保持 Runtime 鉴权 fail-closed;不得伪造或转发 `X-Jwt-Token`。 -- 对旧 Runtime 镜像展示可操作、本地化的错误,并保留 Profile 页面中的 Runtime 身份和能力状态。 -- Runtime 契约不兼容时,禁用 Profile 写入、Session 配置和 Debug 控件。 - -## 非目标 - -- 不执行云端 Runtime mutation、镜像发布或自动 Runtime 升级。 -- 不改变 Runtime 鉴权契约:当前 AgentKit-mode Runtime 镜像继续使用 `Authorization`。 -- 不提交本地浏览器证据或其他测试产物。 - -## 需求 - -| ID | 需求 | -| --- | --- | -| `FR-1` | 本地 `veadk studio --dev --vite` 在未配置 Studio TOS 时必须启用进程内 MPA operation repository,使 active operation recovery 返回 `200`。 | -| `FR-2` | 上游 `401` 且 detail 要求 `X-Jwt-Token` 时,必须归类为 `runtime_legacy_auth_unsupported`,不能表现为泛化的 session-list 或 runtime request failure。 | -| `FR-3` | `GET /web/mpa/agents/{mpaInstanceId}/view` 对旧版或缺失 Profile endpoint 的 Runtime 必须返回正常 `MpaAgentView`、Runtime 元数据和 safe error,不能让整个页面失败。 | -| `FR-4` | Profile 页面必须展示本地化说明,并在 Runtime 镜像不兼容时禁用 Profile 写入、Session 配置和 Debug 控件。 | -| `FR-5` | Runtime-backed Agent 详情在 `default` Agent 元数据缺失时,必须回退读取 A2A virtual app 元数据。 | - -## 方案 - -`frontend/server/mpa/runtime_client.py` 现在会对上游字符串错误进行归一: - -- `X-Jwt-Token header is required` 归类为 `runtime_legacy_auth_unsupported`。 -- `GET .../profile-status` 返回的普通 404 文本归类为 `profile_not_found`。 - -`frontend/server/mpa/routes.py` 将 `runtime_legacy_auth_unsupported` 作为 Profile view 的安全错误处理。响应保留 `bindingStatus=bound` 与 Runtime 元数据,不返回 Profile 状态,并设置 `canWrite=false`、`canDebug=false`。 - -`frontend/src/adk/client.ts` 对 Session list failure 使用结构化错误解析。MPA Runtime session 列表遇到旧版 `X-Jwt-Token` 401 时,会展示本地化升级提示,并保留原始 detail 用于诊断。 - -`frontend/src/ui/AgentWorkspace.tsx` 将 `safeError.code` 映射为本地化 MPA 控制面文案,并使用 `capabilities.canWrite` 判断 Profile apply 按钮是否可用。 - -`frontend/src/adk/client.ts` 现在会在 `/web/agent-info/{app}` 返回 404 时回退请求 `/web/agent-info/a2a-default`。非 404、鉴权错误和服务端错误仍继续向上抛出。 - -本地验证使用: - -```bash -APP_ENV=development VEADK_MPA_TEST_SCENARIOS=0 uv run veadk studio --dev --host 127.0.0.1 --port 18179 --vite --no-open -VEADK_API_TARGET=http://127.0.0.1:18179 npm --prefix frontend run dev -- --host 127.0.0.1 --port 5179 -``` - -## 涉及文件 - -- `frontend/server/mpa/runtime_client.py` -- `frontend/server/mpa/routes.py` -- `frontend/src/adk/client.ts` -- `frontend/src/ui/AgentWorkspace.tsx` -- `frontend/src/i18n/resources/en-US/adk.json` -- `frontend/src/i18n/resources/zh-CN/adk.json` -- `frontend/src/i18n/resources/en-US/ui.json` -- `frontend/src/i18n/resources/zh-CN/ui.json` -- `tests/frontend/server/mpa/test_runtime_profile_routes.py` -- `frontend/tests/runSseAbort.test.mjs` -- `frontend/tests/agentWorkspace.test.mjs` - -## 验证 - -| 检查 | 结果 | 证据 | -| --- | --- | --- | -| 后端 Profile routes | pass | `uv run --extra dev pytest tests/frontend/server/mpa/test_runtime_profile_routes.py -q` 返回 28 passed。 | -| 前端源码契约测试 | pass | `npm --prefix frontend test -- agentWorkspace.test.mjs runSseAbort.test.mjs` 返回 1244 passed,因为该脚本仍会执行仓库前端测试集合。 | -| i18n 一致性 | pass | `npm --prefix frontend run check:i18n` 对 2 个 locale、21 个 namespace 检查通过。 | -| 本地 operation recovery endpoint | pass | 带 `--dev` 启动后,经 Vite 访问 `GET /web/mpa/agent-operations?status=active` 返回 `200 {"operations":[]}`。 | -| 旧 Runtime Profile view | pass | `GET /web/mpa/agents/r-yev5cmzp4wzn6n5iodgd/view?...` 返回 `200`、Runtime 元数据、`bindingStatus=orphan_runtime`、`canDebug=false` 和 `safeError.code=profile_not_found`。 | -| 旧 Runtime session list | expected limited | `GET /web/runtime-proxy/r-yev5cmzp4wzn6n5iodgd/api/v1/sessions?...` 仍返回上游 `401 X-Jwt-Token header is required`;客户端现在会将其展示为旧 Runtime 镜像兼容性提示。 | -| A2A Agent 元数据回退 | pass | `GET /web/runtime-proxy/r-yeuujrrcowb21078p9jh/web/agent-info/default?...` 返回 `404`,而 `/web/agent-info/a2a-default?...` 返回 `200`;`getAgentInfo` 现在会重试 A2A 路径,前端回归由 `runSseAbort.test.mjs` 覆盖。 | - -## 风险与后续 - -当前选中的 Runtime version 14 无法仅通过 Studio 侧修复到完全兼容。若要完整验证 Session 列表和聊天链路,需要将该 Runtime 镜像更新到当前 MPA AgentKit 兼容版本,例如已验证过的 version 62 路径。 diff --git a/prd-spec/bugfixes/studio-mpa-writable-state/2026-09-23-use-temporary-state.md b/prd-spec/bugfixes/studio-mpa-writable-state/2026-09-23-use-temporary-state.md deleted file mode 100644 index 5d5b815a6..000000000 --- a/prd-spec/bugfixes/studio-mpa-writable-state/2026-09-23-use-temporary-state.md +++ /dev/null @@ -1,82 +0,0 @@ -# Use writable temporary state for Studio MPA creation - -[中文版](2026-09-23-use-temporary-state.zh.md) - -- Change ID: `studio-mpa-writable-state` -- Created/Revised: 2026-09-23 -- Status: implemented -- Related component: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md) -- User approval: 2026-09-23, use `/tmp` as the temporary cloud fix instead of implementing TOS persistence now. - -## Background and evidence - -The deployed Studio code directory is read-only. The first MPA creation request fails before starting its child process because `CreationTasks` tries to create `.adk/mpa-creation.sqlite3`; automatic PostgreSQL preparation would later fail for the same reason at `.adk/mpa-pg-bootstrap.sqlite3`. A local read-only-directory reproduction and runtime instrumentation confirmed the first failure. Both stores contain nonsecret control state, but require a writable filesystem. - -Expected behavior is that an authorized cloud Studio request can start creation. Actual behavior is HTTP 500 before task submission. - -## Goals and non-goals - -Goals: - -- Put the built-in cloud Studio task database and PostgreSQL bootstrap database under `/tmp/veadk-studio/`. -- Preserve explicit `VEADK_MPA_TASK_DB` overrides and standalone CLI/YAML defaults. -- Keep secret handling, API shapes, task limits, and cloud orchestration unchanged. - -Non-goals: - -- Durable state across function-instance replacement or multiple Studio instances. -- TOS CAS, NAS, database-backed coordination, migration of existing `.adk` files, or deployment. - -## Scenarios and requirements - -- **FR-1:** Given no task DB override, when Studio lazily creates its task service, then it uses `/tmp/veadk-studio/mpa-creation.sqlite3`. -- **FR-2:** Given `VEADK_MPA_TASK_DB`, when Studio creates its task service, then the explicit path wins. -- **FR-3:** Given the built-in Studio profile, automatic PG bootstrap uses `/tmp/veadk-studio/mpa-pg-bootstrap.sqlite3`. -- **FR-4:** Given a standalone YAML profile without `bootstrap-path`, its existing `.adk/mpa-pg-bootstrap.sqlite3` default remains unchanged. -- **FR-5:** No credential or OpenViking API key is added to either SQLite store. - -## Design and contract impact - -The HTTP route owns the Studio task-store default and changes only that default. The code-owned Studio profile owns its PG bootstrap path and changes only that profile value. `CreationTasks` and `BootstrapStore` retain their SQLite implementation, schemas, locking, and file mode. - -`/tmp` is writable in the target VeFaaS runtime but ephemeral and instance-local. Restart or replacement may lose task history and bootstrap identity. Therefore this fix is suitable only as an explicitly accepted temporary single-instance recovery. If a task is lost during an uncertain cloud create, operators must inspect cloud resources before retrying. A durable shared implementation remains follow-up work. - -Security and permissions are unchanged: parent directories use mode `0700`, database files use `0600`, and secrets are not persisted. Compatibility is preserved for HTTP payloads and standalone CLI/YAML callers. No frontend contract changes. - -Alternatives rejected for this temporary fix: - -- TOS CAS: durable and shared, but requires task leases, restart reconciliation, and bootstrap concurrency semantics. -- NAS: durable with minimal code change, but requires deployment storage configuration. -- Removing state: unsafe because idempotency and uncertain-result recovery require it. - -## Implementation tasks - -- **T-1 (FR-1, FR-2):** Add route tests and change the Studio task DB default. -- **T-2 (FR-3, FR-4):** Add profile tests and change the built-in bootstrap path. -- **T-3 (FR-5):** Run existing route/task/config security regression tests. -- **T-4:** Remove temporary runtime instrumentation after post-fix evidence is collected. - -Affected files: `frontend/server/mpa_creation.py`, `veadk/integrations/mpa/managed/studio_profile.py`, `tests/integrations/mpa_managed/test_routes.py`, `tests/integrations/mpa_managed/test_config.py`, and the bilingual component spec. - -## Verification and acceptance - -| Requirement | Task | Acceptance criterion | Verification | Result | -| --- | --- | --- | --- | --- | -| `FR-1`, `FR-2` | `T-1` | `AC-1`: default and override resolve to the expected task paths | `uv run --extra dev pytest tests/integrations/mpa_managed/test_routes.py` | `pass`, 2026-09-23 | -| `FR-3`, `FR-4` | `T-2` | `AC-2`: built-in and YAML profile paths remain correctly separated | `uv run --extra dev pytest tests/integrations/mpa_managed/test_config.py` | `pass`, 2026-09-23 | -| `FR-5` | `T-3` | `AC-3`: existing task and route redaction tests pass | `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q` | `pass`, 396 tests, 2026-09-23 | -| `FR-1`, `FR-3` | `T-4` | `AC-4`: read-only code-directory reproduction initializes both stores under `/tmp` | Local read-only-CWD reproduction; cloud smoke requires separate deployment authorization | local `pass`; cloud `not_run`, 2026-09-23 | - -Required changed-file Ruff and Pyright checks follow `frontend/SPEC.md`. Frontend tests/build are not applicable because no UI or HTTP schema changes. - -## Risks and recovery - -- Instance restart loses `/tmp` state; cloud resources may outlive their local intent record. -- Multiple function instances do not share locks or concurrency counts. The current deployment must remain single-instance while this workaround is active. -- Rollback restores `.adk` defaults but also restores the read-only-directory failure in cloud Studio. - -## Review and delivery record - -Design review on 2026-09-23 found no API, secret-handling, or standalone CLI compatibility change. The known durability and multi-instance limitations are explicit and accepted by the user as a temporary fix. English/Chinese requirement IDs, paths, limits, tasks, and acceptance criteria were checked for equivalence. - -Implementation verification on 2026-09-23 passed 396 MPA/CLI tests, changed-file Ruff, changed-file Pyright, `git diff --check`, and a read-only-CWD initialization of both `/tmp` stores. Cloud deployment and live creation remain `not_run` because deployment requires separate authorization. diff --git a/prd-spec/bugfixes/studio-mpa-writable-state/2026-09-23-use-temporary-state.zh.md b/prd-spec/bugfixes/studio-mpa-writable-state/2026-09-23-use-temporary-state.zh.md deleted file mode 100644 index 53356ddef..000000000 --- a/prd-spec/bugfixes/studio-mpa-writable-state/2026-09-23-use-temporary-state.zh.md +++ /dev/null @@ -1,82 +0,0 @@ -# Studio MPA 创建使用可写临时状态 - -[English](2026-09-23-use-temporary-state.md) - -- 变更 ID:`studio-mpa-writable-state` -- 创建/修订日期:2026-09-23 -- 状态:implemented -- 相关组件:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md) -- 用户批准:2026-09-23,暂不实现 TOS 持久化,先用 `/tmp` 修复云端问题。 - -## 背景与证据 - -已部署 Studio 的代码目录只读。首个 MPA 创建请求在启动子进程前失败,因为 `CreationTasks` 尝试创建 `.adk/mpa-creation.sqlite3`;后续自动准备 PostgreSQL 时也会因 `.adk/mpa-pg-bootstrap.sqlite3` 遇到同类问题。本地只读目录复现及运行时插桩已确认首个失败点。两个存储仅包含非密钥控制状态,但都要求可写文件系统。 - -预期行为是有权限的云端 Studio 请求能够开始创建;实际行为是在提交任务前返回 HTTP 500。 - -## 目标与非目标 - -目标: - -- 将内置云端 Studio 的任务数据库和 PostgreSQL bootstrap 数据库放到 `/tmp/veadk-studio/`。 -- 保留显式 `VEADK_MPA_TASK_DB` 覆盖及独立 CLI/YAML 默认值。 -- 保持密钥处理、API 结构、任务限制及云编排行为不变。 - -非目标: - -- 函数实例替换或多个 Studio 实例间的持久状态。 -- TOS CAS、NAS、数据库协调、已有 `.adk` 文件迁移或部署。 - -## 场景与需求 - -- **FR-1:**未设置任务数据库覆盖时,Studio 延迟创建任务服务应使用 `/tmp/veadk-studio/mpa-creation.sqlite3`。 -- **FR-2:**设置 `VEADK_MPA_TASK_DB` 时,Studio 创建任务服务应优先使用显式路径。 -- **FR-3:**使用内置 Studio 配置时,自动 PG bootstrap 应使用 `/tmp/veadk-studio/mpa-pg-bootstrap.sqlite3`。 -- **FR-4:**独立 YAML 未设置 `bootstrap-path` 时,继续使用现有 `.adk/mpa-pg-bootstrap.sqlite3` 默认值。 -- **FR-5:**任何凭据或 OpenViking API Key 均不得写入两个 SQLite 存储。 - -## 设计与契约影响 - -HTTP 路由拥有 Studio 任务存储默认值,本次仅修改该默认值。代码内置 Studio 配置拥有 PG bootstrap 路径,本次仅修改该配置值。`CreationTasks` 与 `BootstrapStore` 保持现有 SQLite 实现、表结构、锁和文件权限。 - -目标 VeFaaS 运行时的 `/tmp` 可写,但属于临时且实例本地的存储。重启或实例替换可能丢失任务历史及 bootstrap 身份。因此,本修复只作为已明确接受的单实例临时恢复方案。若云资源创建结果不确定时任务记录丢失,运维必须先检查云资源再重试。持久共享实现仍是后续工作。 - -安全及权限不变:父目录权限为 `0700`,数据库文件权限为 `0600`,不持久化密钥。HTTP 请求/响应及独立 CLI/YAML 调用保持兼容。不涉及前端契约。 - -本次临时修复未采用的方案: - -- TOS CAS:可持久共享,但需要任务租约、重启协调及 bootstrap 并发语义。 -- NAS:可通过少量代码改动提供持久化,但要求配置部署存储。 -- 删除状态:不安全,因为幂等和不确定结果恢复依赖这些状态。 - -## 实现任务 - -- **T-1(FR-1、FR-2):**增加路由测试并修改 Studio 任务数据库默认值。 -- **T-2(FR-3、FR-4):**增加配置测试并修改内置 bootstrap 路径。 -- **T-3(FR-5):**运行现有路由、任务和配置安全回归测试。 -- **T-4:**收集修复后证据后移除临时运行时插桩。 - -涉及文件:`frontend/server/mpa_creation.py`、`veadk/integrations/mpa/managed/studio_profile.py`、`tests/integrations/mpa_managed/test_routes.py`、`tests/integrations/mpa_managed/test_config.py` 及双语组件规范。 - -## 验证与验收 - -| 需求 | 任务 | 验收标准 | 验证 | 结果 | -| --- | --- | --- | --- | --- | -| `FR-1`、`FR-2` | `T-1` | `AC-1`:默认值及覆盖值解析到预期任务路径 | `uv run --extra dev pytest tests/integrations/mpa_managed/test_routes.py` | `pass`,2026-09-23 | -| `FR-3`、`FR-4` | `T-2` | `AC-2`:内置配置及 YAML 配置路径保持正确隔离 | `uv run --extra dev pytest tests/integrations/mpa_managed/test_config.py` | `pass`,2026-09-23 | -| `FR-5` | `T-3` | `AC-3`:现有任务及路由脱敏测试通过 | `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q` | `pass`,396 项测试,2026-09-23 | -| `FR-1`、`FR-3` | `T-4` | `AC-4`:只读代码目录复现可在 `/tmp` 初始化两个存储 | 本地只读工作目录复现;云端冒烟需要单独部署授权 | 本地 `pass`;云端 `not_run`,2026-09-23 | - -按 `frontend/SPEC.md` 执行改动 Python 文件的 Ruff 和 Pyright。因为不修改 UI 或 HTTP 结构,前端测试/构建不适用。 - -## 风险与恢复 - -- 实例重启会丢失 `/tmp` 状态;云资源可能比本地意图记录存活更久。 -- 多个函数实例不共享锁或并发计数。使用此临时方案期间,当前部署必须保持单实例。 -- 回滚会恢复 `.adk` 默认值,同时恢复云端只读目录故障。 - -## 审查与交付记录 - -2026-09-23 设计审查确认不修改 API、密钥处理或独立 CLI 兼容性。已明确记录持久性及多实例限制,并由用户作为临时修复接受。英文/中文的需求 ID、路径、限制、任务及验收标准已检查语义一致。 - -2026-09-23 实现验证通过 396 项 MPA/CLI 测试、改动文件 Ruff、改动文件 Pyright、`git diff --check`,并在只读工作目录下成功初始化两个 `/tmp` 存储。云端部署和真实创建为 `not_run`,因为部署需要单独授权。 diff --git a/prd-spec/bugfixes/studio-openviking-wheel/2026-10-07-align-sdk-wheel.md b/prd-spec/bugfixes/studio-openviking-wheel/2026-10-07-align-sdk-wheel.md deleted file mode 100644 index b31ba5d23..000000000 --- a/prd-spec/bugfixes/studio-openviking-wheel/2026-10-07-align-sdk-wheel.md +++ /dev/null @@ -1,99 +0,0 @@ -# Align the Studio OpenViking wheel - -[中文版](2026-10-07-align-sdk-wheel.zh.md) - -- Change ID: `studio-openviking-wheel` -- Created/revised: 2026-10-07 -- Status: implemented -- Scope: Studio dependency packaging; no cloud deployment or commit requested. - -## Evidence and goals - -`pyproject.toml` requires `openviking-sdk>=0.1.9`, and `uv.lock` resolves -0.1.9. However, `veadk/cli/studio_dependencies.py` still stages 0.1.4. -`build_local_studio_requirements(offline_runtime=False)` writes this wheel and -the local VeADK wheel into the same requirements list, creating conflicting -requirements. BytePlus provider staging also uses this list. Complete offline -builds already resolve from `uv.lock` and use 0.1.9. - -Restore compatible packaging without changing SDK APIs, the dependency minimum, -the lockfile, MPA images, or running deployments. Do not redesign dependency -resolution or update unrelated pinned packages. - -## Requirements and scenarios - -- FR-1: For both `volcengine` and `byteplus`, the bundled OpenViking wheel must - satisfy the project's declared SDK requirement. -- FR-2: Its version, filename, download URL and SHA256 must match the locked PyPI - wheel. Existing staging and checksum failure behavior remain unchanged. -- FR-3: Regression checks must fail when the pin becomes older than the required - minimum or differs from the locked artifact. Tests must not access cloud or - package servers. - -Given the current 0.1.4 pin, consistency tests fail. After alignment to the locked -0.1.9 artifact, both provider paths pass. Prepared caches containing only the old -wheel must be refreshed; a missing current wheel continues to fail explicitly. - -## Design and contract assessment - -Copy the 0.1.9 filename, URL and SHA256 from the committed lock into the existing -static wheel record. Update the deployment-test fixture. Add cross-file tests -using `packaging` and the existing TOML parser support, with no new dependency. -The alternative of downgrading the SDK requirement would conflict with its -updated options API and is rejected. - -No component spec change is needed: this restores the existing declared SDK -compatibility and locked-artifact integrity rather than defining a new API, -configuration, state, permission or failure contract. Concurrency, cancellation, -session handling and UI behavior are unaffected. Existing checksum enforcement -remains in place; no credentials or external resource mutations are involved. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | -| --- | --- | --- | -| FR-1 | T-1: add a regression test before the fix | AC-1: both provider pins satisfy the project requirement | -| FR-2 | T-2: update the wheel record and deployment fixture | AC-2: version, filename, URL and hash match `uv.lock` | -| FR-3 | T-3: run affected tests, lint/type and documentation checks | AC-3: pre-fix failures and post-fix passes are recorded | - -Affected implementation/tests: `veadk/cli/studio_dependencies.py`, -`tests/cli/test_studio_dependencies.py`, `tests/cli/test_studio_deploy_target.py`. -Run the new tests, Studio release/deployment/offline-packaging tests and existing -OpenViking backend tests via `uv run --extra dev pytest`; run Ruff and Pyright on -changed Python files and `git diff --check`. - -## Review, risks and delivery - -- User approved the proposed 0.1.9 pin, URL/hash and test changes with “帮改下”. -- Direct design review (`review-spec` unavailable): scope, existing callers, - dependency compatibility, error behavior, security and bilingual equivalence - reviewed; no blocking findings. -- Updating the source does not change existing deployments. Rebuild/redeploy is - required separately. Old prepared dependency caches need regeneration. -- Browser/frontend build: not_applicable; no UI, frontend source or web assets - change. Cloud deployment and full Linux package build: not_run; this task - validates the pin and packaging contracts locally without deploying resources. -- Implementation review: the diff changes only the OpenViking artifact and test - fixture, adds cross-file regression coverage, and preserves staging behavior. - Both document languages and their relative links were reconciled. - -### Verification (2026-10-07, working diff on `056e8acd`) - -| Check | Result | -| --- | --- | -| `uv run --extra dev pytest tests/cli/test_studio_dependencies.py -q` before fix | fail (expected): 4 failures, exposing 0.1.4 versus the SDK requirement and lock | -| Regression command below, 2 workers | pass: 192 tests, including all 4 new cases | -| `uv run --with ruff==0.11.12 ruff check veadk/cli/studio_dependencies.py tests/cli/test_studio_dependencies.py tests/cli/test_studio_deploy_target.py` | pass: repository-pinned Ruff version | -| `uv tool run pyright --pythonpath .venv/bin/python veadk/cli/studio_dependencies.py tests/cli/test_studio_dependencies.py` | pass: 0 errors | -| Same Pyright command including `tests/cli/test_studio_deploy_target.py` | fail: 10 existing errors at lines 1169, 1366, 1713 and 2424; identical diagnostics reproduced against the unchanged HEAD file in a temporary directory | -| `uv run --extra dev pre-commit run --files ` | pass: Ruff lint/format and secret scan; YAML hook not_applicable (no YAML files) | -| `git diff --check`, bilingual content/identifier/link review | pass | - -```bash -uv run --extra dev pytest tests/cli/test_studio_dependencies.py tests/cli/test_studio_release.py tests/cli/test_studio_deploy_target.py tests/cli/test_studio_sidecar_prerequisites.py tests/cli/test_studio_offline_runtime.py tests/test_openviking_knowledgebase.py tests/test_openviking_long_term_memory.py -n 2 -q -``` - -An initial unpinned Ruff run reported 8 findings; the required repository-pinned -0.11.12 check passes without unrelated edits. The existing deployment-test type -errors remain visible and are not claimed as passing. T-1 through T-3 and AC-1 -through AC-3 are complete; live deployment remains outside this change. diff --git a/prd-spec/bugfixes/studio-openviking-wheel/2026-10-07-align-sdk-wheel.zh.md b/prd-spec/bugfixes/studio-openviking-wheel/2026-10-07-align-sdk-wheel.zh.md deleted file mode 100644 index d4bac5059..000000000 --- a/prd-spec/bugfixes/studio-openviking-wheel/2026-10-07-align-sdk-wheel.zh.md +++ /dev/null @@ -1,87 +0,0 @@ -# 对齐 Studio 的 OpenViking wheel - -[English](2026-10-07-align-sdk-wheel.md) - -- 变更 ID:`studio-openviking-wheel` -- 创建/修订日期:2026-10-07 -- 状态:implemented -- 范围:Studio 依赖打包;本次未请求云端部署或提交。 - -## 证据和目标 - -`pyproject.toml` 要求 `openviking-sdk>=0.1.9`,`uv.lock` 解析为 -0.1.9,但 `veadk/cli/studio_dependencies.py` 仍准备 0.1.4。 -`build_local_studio_requirements(offline_runtime=False)` 将此 wheel 和本地 -VeADK wheel 写入同一份 requirements,产生依赖冲突。BytePlus 的依赖准备 -也使用该清单。完整离线构建已经通过 `uv.lock` 解析,使用 0.1.9。 - -恢复打包兼容性,不改变 SDK API、最低依赖、锁文件、MPA 镜像和运行中的部署。 -不重新设计依赖解析,也不升级无关的固定依赖。 - -## 需求和场景 - -- FR-1:`volcengine` 和 `byteplus` 携带的 OpenViking wheel 均须满足项目声明 - 的 SDK 依赖要求。 -- FR-2:版本、文件名、下载 URL 和 SHA256 须与锁定的 PyPI wheel 一致。 - 保持既有依赖准备和校验失败行为。 -- FR-3:固定版本低于最低要求或与锁定产物不一致时,回归检查必须失败。 - 测试不得访问云服务或包服务器。 - -当前固定为 0.1.4 时,一致性测试失败;对齐至锁定的 0.1.9 后,两种云厂商路径 -均通过。仅包含旧 wheel 的预备缓存需要刷新;缺少当前 wheel 时仍明确失败。 - -## 设计和契约评估 - -将已提交锁文件中的 0.1.9 文件名、URL 和 SHA256 复制到现有静态 wheel 记录。 -更新部署测试夹具。使用 `packaging` 和已有 TOML 解析支持增加跨文件测试,不 -增加依赖。降低 SDK 最低要求会与已经更新的 options API 冲突,因此不采用。 - -无需修改组件规格:本次恢复已有 SDK 依赖兼容性和锁定产物完整性,不定义新的 -API、配置、状态、权限或失败契约。并发、取消、会话处理和 UI 行为不受影响。 -保留已有校验机制,不涉及凭据或外部资源修改。 - -## 任务和验收 - -| 需求 | 任务 | 验收 | -| --- | --- | --- | -| FR-1 | T-1:修复前增加回归测试 | AC-1:两种云厂商的固定版本均满足项目依赖要求 | -| FR-2 | T-2:更新 wheel 记录和部署夹具 | AC-2:版本、文件名、URL 和哈希与 `uv.lock` 一致 | -| FR-3 | T-3:运行相关测试、静态检查和文档检查 | AC-3:记录修复前失败与修复后通过的结果 | - -涉及实现和测试:`veadk/cli/studio_dependencies.py`、 -`tests/cli/test_studio_dependencies.py`、`tests/cli/test_studio_deploy_target.py`。 -通过 `uv run --extra dev pytest` 运行新增测试、Studio 发布/部署/离线打包测试 -和已有 OpenViking 后端测试;对修改的 Python 文件运行 Ruff、Pyright,并运行 -`git diff --check`。 - -## 审查、风险和交付 - -- 用户以“帮改下”批准此前提出的 0.1.9 固定版本、URL/哈希和测试改动方案。 -- 直接完成设计审查(`review-spec` 不可用):已核对范围、现有调用方、依赖兼容性、 - 错误行为、安全和双语一致性,无阻塞项。 -- 源码更新不会修改已有部署,需要另行构建和部署。旧的依赖预备缓存需重新生成。 -- 浏览器/前端构建:not_applicable;未修改 UI、前端源码或网页产物。 - 云端部署与完整 Linux 打包:not_run;本次在本地验证固定依赖与打包契约, - 不部署资源。 -- 实现审查:差异仅调整 OpenViking 产物和测试夹具,增加跨文件回归检查, - 保持依赖准备行为。已核对双语文档及相对链接。 - -### 验证(2026-10-07,`056e8acd` 上的工作区差异) - -| 检查 | 结果 | -| --- | --- | -| 修复前 `uv run --extra dev pytest tests/cli/test_studio_dependencies.py -q` | fail(预期):4 项失败,复现 0.1.4 与 SDK 要求和锁文件不一致 | -| 下方回归命令,2 个 worker | pass:192 项测试,包含全部 4 项新增用例 | -| `uv run --with ruff==0.11.12 ruff check veadk/cli/studio_dependencies.py tests/cli/test_studio_dependencies.py tests/cli/test_studio_deploy_target.py` | pass:使用仓库固定的 Ruff 版本 | -| `uv tool run pyright --pythonpath .venv/bin/python veadk/cli/studio_dependencies.py tests/cli/test_studio_dependencies.py` | pass:0 errors | -| 相同 Pyright 命令加上 `tests/cli/test_studio_deploy_target.py` | fail:1169、1366、1713、2424 行共 10 项历史错误;临时目录中的未修改 HEAD 文件复现完全相同的诊断 | -| `uv run --extra dev pre-commit run --files <全部五个改动文件>` | pass:Ruff 检查/格式和密钥扫描通过;YAML 钩子 not_applicable(无 YAML 文件) | -| `git diff --check`、双语内容/标识符/链接审查 | pass | - -```bash -uv run --extra dev pytest tests/cli/test_studio_dependencies.py tests/cli/test_studio_release.py tests/cli/test_studio_deploy_target.py tests/cli/test_studio_sidecar_prerequisites.py tests/cli/test_studio_offline_runtime.py tests/test_openviking_knowledgebase.py tests/test_openviking_long_term_memory.py -n 2 -q -``` - -首次使用未固定版本的 Ruff 报告 8 项问题;使用仓库要求的 0.11.12 后检查通过, -无需无关修改。原有部署测试类型错误保留并如实报告,不宣称该检查通过。 -T-1 至 T-3、AC-1 至 AC-3 已完成;实际部署仍在本次变更范围之外。 diff --git a/prd-spec/bugfixes/studio-session-protocol/2026-09-22-a2a-session-routing.md b/prd-spec/bugfixes/studio-session-protocol/2026-09-22-a2a-session-routing.md deleted file mode 100644 index 735610590..000000000 --- a/prd-spec/bugfixes/studio-session-protocol/2026-09-22-a2a-session-routing.md +++ /dev/null @@ -1,51 +0,0 @@ -# Preserve discovered A2A session routing - -[中文版](2026-09-22-a2a-session-routing.zh.md) - -## Metadata -- ID: `studio-a2a-session-routing` -- Date: 2026-09-22 -- Status: implemented -- Component: [Studio Runtime Diagnostics, CON-9](../../../specs/studio-runtime-diagnostics/README.md) - -## Evidence, goals and non-goals -The last clean committed frontend bundle at `ea1c384d` omitted MPA category/instance metadata when registering Runtime connections. Rebuilding current source preserves this metadata and selects native MPA routes even when discovery returned `a2a-default`. Isolated replay with identical connection input reproduced the switch. The affected Runtime's A2A card was available while native Profile status returned `500 JWT_PUBLIC_KEY is not configured`. This does not establish the exact bundle previously loaded in the user's browser. - -Goal: preserve the discovered A2A session protocol and existing answer deduplication. Non-goals: cloud deployment, JWT configuration, authentication bypass, automatic error fallback, changing Profile management, or migrating session IDs across protocols. - -## Requirements and design -- FR-1: A Runtime app exactly equal to `a2a-default` uses existing A2A create/list/read/delete/run even with MPA category or instance metadata. -- FR-2: Native MPA apps retain Profile initialization, native CRUD/run/SSE and execution-config preflight. Ordinary ADK apps are unchanged. -- FR-3: Chat execution-config preflight uses the same native-protocol predicate; A2A sends do not query native execution config. MPA product metadata remains available to the rail. -- FR-4: Errors, retry, cancellation and timeout stay on the selected protocol. No fallback after 401/403/500. - -Pass the resolved app to the endpoint predicate and exclude the explicit A2A virtual app; reuse it through `isMpaRuntimeApp` for chat preflight. No persisted schema, backend route, credential, or extra discovery request changes. Existing per-run cancellation and terminal handling remain authoritative. Protocol selection is based on discovery, not error text. - -## Tasks, files and acceptance -- T-1 / AC-1 (FR-1): add failing tests in `frontend/tests/runSseAbort.test.mjs` for restored MPA-tagged A2A create/list/read/delete/run. -- T-2 / AC-2 (FR-2, FR-3): update `frontend/src/adk/client.ts` and `frontend/src/App.tsx`; verify native preflight remains and A2A preflight is skipped. -- T-3 / AC-3 (FR-4): test errors/retry/abort without protocol fallback; run full frontend tests/build, asset checks and browser verification. Preserve dedup tests. -- T-4: reconcile bilingual contracts, `frontend/README.md`, generated `veadk/webui` assets and verification records. - -## Review, risks and approval -2026-09-22: user approved the scoped protocol fix with “改下”. `review-spec` is unavailable; direct review covered auth preservation, protocol identity, restored connections, native compatibility, cancellation and bilingual equivalence; no blockers. Existing A2A history depends on the process-local BFF index. Restart clears that index; activation must account for cached HTML and active sessions. This change does not add history persistence. No cloud mutations or live model execution are implied by simulated tests. - -## Verification -Implemented and verified as recorded below. Real-browser transport checks distinguish isolated responses from live Runtime access. Keyboard/IME and layout code are unchanged. No commit or push requested. - -### 2026-09-22 verification record -Scope: current uncommitted protocol diff on top of the existing dedup changes. -- pass: new test-first suite reproduced 8 failures and 3 passes before the fix; all 11 pass after it. The chat preflight test executes the actual function extracted from App.tsx. -- pass: targeted protocol, run/abort and session-config tests: 38 passed. -- pass: `npm --prefix frontend test`: 1,272 Node tests and 25 Vitest tests passed. -- pass: `npm --prefix frontend run build` (includes TypeScript); existing chunk-size/import warnings remain. -- pass: `npm --prefix frontend run test:webui-assets`: 104 files and 248 internal references. -- pass: isolated real-browser fixture importing the actual client: A2A create/empty list/read/run/delete, loading/cancellation, visible 500 error and explicit retry, native MPA Profile/session creation. Responses are mocked; this is not live model verification. -- pass: targeted existing gitleaks pre-commit hook, including the generated app entry; `git diff --check`. Initial sandbox denial accessing uv cache was resolved with scoped execution permission, without changing global configuration. -- not_applicable: new Python checks, generated Python, harness-sidecar coverage, input/IME/layout changes: none changed in this fix. Existing earlier A2A backend checks are recorded in their own PRD. -- not_run: commit-time branch synchronization/all-files pre-commit; no commit requested. Live model generation was not performed. -- Local activation: `/index.html` is served from disk by the existing static fallback and loads the rebuilt entry without restarting the process. `/` retains its startup-cached entry until normal restart. No backend restart, cloud mutation, commit or push was performed. - -- pass: authenticated actual Studio browser at `/index.html` loaded `index-1z730Cp-.js` and the MPA picker, using the existing local server and identity. -- pass: the first actual Studio connection attempt failed during Runtime discovery. Explicit UI retry succeeded: the selected app is `a2a-default`, a new session appears in history and the composer is available, with no JWT creation error. Live model generation remains not_run; streaming is covered by isolated browser/client tests. -- Final review: the only new production edits are the shared native-session predicate and chat preflight gate. MPA connection metadata, authentication and existing dedup changes are preserved. No blocker found in the code review; actual session creation passed after the explicit connection retry. No live model answer is claimed. diff --git a/prd-spec/bugfixes/studio-session-protocol/2026-09-22-a2a-session-routing.zh.md b/prd-spec/bugfixes/studio-session-protocol/2026-09-22-a2a-session-routing.zh.md deleted file mode 100644 index 41e1b142e..000000000 --- a/prd-spec/bugfixes/studio-session-protocol/2026-09-22-a2a-session-routing.zh.md +++ /dev/null @@ -1,51 +0,0 @@ -# 保留探测到的 A2A 会话路由 - -[English](2026-09-22-a2a-session-routing.md) - -## 元信息 -- ID:`studio-a2a-session-routing` -- 日期:2026-09-22 -- 状态:implemented -- 组件:[Studio Runtime Diagnostics,CON-9](../../../specs/studio-runtime-diagnostics/README.zh.md) - -## 证据、目标与非目标 -上一个无冲突的已提交前端包 `ea1c384d` 在注册 Runtime 连接时丢弃 MPA 类型和实例信息。当前源码重建后保留这些信息,即使探测结果为 `a2a-default`,也会选择原生 MPA 路由。相同连接输入的隔离回放复现了切换。受影响 Runtime 的 A2A Card 可用,原生 Profile 状态返回 `500 JWT_PUBLIC_KEY is not configured`。这不代表已确定用户历史浏览器加载的确切构建包。 - -目标:保留探测到的 A2A 会话协议和既有回答去重。非目标:云部署、JWT 配置、绕过鉴权、错误自动降级、修改 Profile 管理或跨协议迁移会话 ID。 - -## 需求与设计 -- FR-1:Runtime 应用严格等于 `a2a-default` 时,即使带 MPA 类型或实例信息,创建/列表/读取/删除/运行仍使用现有 A2A 路径。 -- FR-2:原生 MPA 保留 Profile 初始化、原生 CRUD/run/SSE 和执行配置前置查询。普通 ADK 应用不变。 -- FR-3:聊天执行配置前置查询使用同一原生协议判断;A2A 发送不查原生执行配置。MPA 侧栏仍可使用产品信息。 -- FR-4:错误、重试、取消、超时保持所选协议。401/403/500 后不降级。 - -向端点判断传入解析后的应用名,排除明确的 A2A 虚拟应用;聊天前置查询通过 `isMpaRuntimeApp` 复用该判断。不改变持久化结构、后端路由和凭据,不增加探测请求。每轮现有取消和结束处理保持权威。协议选择依据探测结果,而不是错误文本。 - -## 任务、文件与验收 -- T-1 / AC-1(FR-1):在 `frontend/tests/runSseAbort.test.mjs` 先增加失败测试,覆盖恢复的带 MPA 信息的 A2A 创建/列表/读取/删除/运行。 -- T-2 / AC-2(FR-2、FR-3):修改 `frontend/src/adk/client.ts` 和 `frontend/src/App.tsx`,验证原生前置查询保留、A2A 跳过。 -- T-3 / AC-3(FR-4):测试错误/重试/取消无协议降级;运行完整前端测试/构建、产物检查和浏览器验证。保留去重测试。 -- T-4:同步双语契约、`frontend/README.md`、生成的 `veadk/webui` 产物和验证记录。 - -## 评审、风险与授权 -2026-09-22:用户以“改下”批准局部协议修复。`review-spec` 不可用;直接评审覆盖鉴权保持、协议身份、恢复连接、原生兼容、取消和双语等价性,无阻塞项。既有 A2A 历史依赖 BFF 进程内索引,重启会清空索引;本地生效需考虑缓存的 HTML 和活动会话。本次不增加历史持久化。模拟测试不代表执行了云变更或真实模型运行。 - -## 验证 -已实现并完成下述验证。真实浏览器传输检查区分隔离响应与真实 Runtime 访问。键盘/输入法及布局代码不变。未要求提交或推送。 - -### 2026-09-22 验证记录 -范围:现有去重修改之上的当前未提交协议修复差异。 -- pass:新增测试先复现 8 项失败、3 项通过;修复后 11 项全部通过。聊天前置查询测试执行从 App.tsx 提取的真实函数。 -- pass:协议、运行/取消、会话配置定向测试共 38 项通过。 -- pass:`npm --prefix frontend test`:1,272 项 Node 测试及 25 项 Vitest 测试通过。 -- pass:`npm --prefix frontend run build`(含 TypeScript);保留已有分包大小/导入警告。 -- pass:`npm --prefix frontend run test:webui-assets`:104 个文件、248 个内部引用。 -- pass:真实浏览器隔离页面导入实际客户端,验证 A2A 创建/空列表/读取/运行/删除、加载/取消、500 错误显示与显式重试、原生 MPA Profile/会话创建。响应为模拟,不代表真实模型验证。 -- pass:定向运行现有 gitleaks pre-commit hook,包含生成的应用入口;`git diff --check`。首次因沙箱拒绝读取 uv 缓存,通过限定执行权限解决,未更改全局配置。 -- not_applicable:新增 Python 检查、生成 Python、harness-sidecar 覆盖率、输入法/键盘/布局改动,本次均未涉及。此前 A2A 后端检查在其独立 PRD 中记录。 -- not_run:提交前分支同步/all-files pre-commit,未要求提交。未执行真实模型生成。 -- 本地生效:既有静态文件回退直接从磁盘提供 `/index.html`,无需重启即可加载新入口;`/` 在正常重启前仍保留启动时缓存的入口。未重启后端、修改云端、提交或推送。 - -- pass:已登录的实际 Studio 浏览器通过 `/index.html` 加载 `index-1z730Cp-.js` 和 MPA 选择器,使用既有本地服务及身份。 -- pass:实际 Studio 首次连接在 Runtime 探测阶段失败。通过 UI 显式重试后成功:选中应用为 `a2a-default`,历史栏出现新会话,输入框可用,没有 JWT 创建错误。真实模型生成仍为 not_run;流式行为由隔离浏览器/客户端测试覆盖。 -- 最终评审:新增生产修改仅限共享原生会话判断和聊天前置查询门控。保留 MPA 连接信息、鉴权和既有去重修改。代码审查未发现阻塞项,显式重试连接后实际创建会话通过,不宣称验证了真实模型回答。 diff --git a/prd-spec/bugfixes/studio-startup-assets/2026-09-22-studio-startup-assets.md b/prd-spec/bugfixes/studio-startup-assets/2026-09-22-studio-startup-assets.md deleted file mode 100644 index 6dfb4f7d7..000000000 --- a/prd-spec/bugfixes/studio-startup-assets/2026-09-22-studio-startup-assets.md +++ /dev/null @@ -1,194 +0,0 @@ -# Studio Startup Asset Integrity Hotfix - -[中文版](2026-09-22-studio-startup-assets.zh.md) - -## Metadata - -- Change ID: `studio-startup-assets` -- Created: 2026-09-22 -- Revised: 2026-09-22 -- Status: `implemented` -- User approval: approved in the 2026-09-22 session after the recommended restore-and-prevent-recurrence approach was presented -- Related component specs: no component contract change; see [Component Contract Impact](#component-contract-impact) -- Owning standard: [AgentKit Studio AI Development Specification](../../../frontend/SPEC.md) - -## Background and Evidence - -### Expected behavior - -Running the packaged Studio with the repository-local CLI must serve an executable WebUI: - -```bash -.venv/bin/veadk studio --dev --host 127.0.0.1 --port 8000 -``` - -The browser must execute the entry module and render the Studio login or workspace instead of an empty root element. A clean frontend dependency installation must also be reproducible with `npm ci`. - -### Actual behavior - -The current `origin/main` revision `3bbd260a` serves `/` and its entry JavaScript with HTTP 200, but the browser renders a blank page. The packaged entry file starts with a Git conflict marker: - -```text -<<<<<<<< HEAD:veadk/webui/assets/app/index-CKbnL9AU.js -``` - -Repository inspection found 114 tracked files under `veadk/webui` containing conflict markers. The markers first appear in feature commit `dae75ce8` and are present in merge parent `9ff62d54`; the merge's first parent `fd9bec69` has none. Source files under `frontend/src` do not contain these markers. - -The checked-in `frontend/package.json` and `frontend/package-lock.json` are also inconsistent. In a clean hotfix worktree, `npm ci --dry-run --ignore-scripts --no-audit --no-fund` fails because the lockfile does not include the `esbuild@0.28.2` peer resolution required by the locked Vitest/Vite dependency graph. Therefore the repository cannot currently reproduce a clean frontend build. - -The existing `frontend/scripts/verifyBuiltAssets.mjs` checks directory structure, internal references, and optional byte-for-byte HTTP serving. It does not explicitly reject unresolved conflict markers. Against the current tree it fails on a missing referenced asset, but it does not report the more direct corruption cause. - -### Confirmed root cause - -Generated WebUI artifacts from two incompatible builds were committed with unresolved textual merge conflict markers. Because static serving does not parse JavaScript, the server returned 200 and the corruption surfaced only when the browser parsed the entry module. The unreproducible lockfile prevented a clean rebuild from being a reliable recovery path. - -## Goals - -- Restore a reproducible frontend dependency graph so `npm ci` succeeds from a clean checkout. -- Regenerate `veadk/webui` from the current `frontend` sources so every packaged asset belongs to one coherent build and contains no conflict marker. -- Extend the existing packaged-asset validator to reject unresolved Git conflict markers with a direct diagnostic. -- Prove that ordinary packaged startup renders in a clean Chrome profile without `--vite` or a `--frontend-dir` workaround. -- Keep the hotfix isolated from unrelated application, API, runtime, and UI behavior. - -## Non-Goals - -- No change to Studio CLI options, host/port defaults, HTTP routes, authentication, Session behavior, Runtime behavior, MPA behavior, or UI interaction design. -- No new frontend or Python dependency. Lockfile reconciliation may update transitive resolutions already required by `package.json`. -- No broad CI workflow redesign. The hotfix strengthens the existing `test:webui-assets` command; wiring new jobs across every workflow is deferred unless current repository evidence proves it necessary. -- No commit, push, pull request, publication, or deployment without separate user authorization. -- No modification of the user's main checkout or its local cleanup commit. - -## Scenarios - -### Scenario S-1: corrupt generated asset is rejected - -Given a file under `veadk/webui` contains a standard unresolved merge line beginning with `<<<<<<<`, `|||||||`, `=======`, or `>>>>>>>`, when `npm --prefix frontend run test:webui-assets` runs, then validation fails, names the affected file, and identifies an unresolved conflict marker. - -### Scenario S-2: clean build is reproducible - -Given a clean checkout and the supported local Node/npm toolchain, when `npm --prefix frontend ci` and `npm --prefix frontend run build` run, then dependency installation and both frontend builds complete successfully without manual lockfile edits. - -### Scenario S-3: packaged Studio renders - -Given the regenerated `veadk/webui`, when `.venv/bin/veadk studio --dev` starts without `--vite` or `--frontend-dir`, then `/` and `/web/ui-config` return 200 and a clean Chrome profile renders non-empty Studio UI content without page-level JavaScript errors. - -### Scenario S-4: source and runtime contracts remain unchanged - -Given the hotfix diff, when reviewers inspect source, API, configuration, and user documentation, then no Studio runtime contract or user workflow change is present; only dependency-lock reconciliation, asset validation, generated WebUI artifacts, and this PRD pair change. - -## Requirements - -| ID | Requirement | -| --- | --- | -| `FR-1` | `frontend/package-lock.json` must be synchronized with `frontend/package.json`, and `npm --prefix frontend ci` must succeed from a clean dependency directory. | -| `FR-2` | `frontend/scripts/verifyBuiltAssets.mjs` must inspect packaged text assets and fail on unresolved Git conflict markers before reference validation can obscure the direct cause. | -| `FR-3` | The conflict-marker check must cover `.html`, `.css`, and `.js` files under `veadk/webui`, including diff3's `|||||||` base marker, report the relative filename, and avoid treating ordinary minified operators or source text as markers unless they occur at a line boundary in Git's marker form. | -| `FR-4` | `npm --prefix frontend run build` must regenerate one coherent `veadk/webui` tree from current sources; no stale artifacts or unresolved markers may remain. | -| `FR-5` | `npm --prefix frontend run test:webui-assets` must pass on the regenerated assets and retain all existing directory, reference, and optional HTTP byte-equality checks. | -| `FR-6` | A normal packaged `veadk studio --dev` launch must render visible Studio content in a clean Chrome profile. HTTP 200 alone is insufficient evidence. | -| `FR-7` | The change must not alter public Python imports, CLI arguments, server routes, frontend API types, configuration precedence, authentication, persistence, or Runtime semantics. | - -## Design - -### Chosen approach - -Use the existing build and validation architecture: - -1. Add a narrow content-integrity check to `verifyBuiltAssets.mjs`. For each packaged `.html`, `.css`, and `.js` file already read by the validator, detect standard unresolved Git marker lines with a multiline expression and throw an assertion that includes the relative path. Run this before internal-reference extraction. -2. Use the current `frontend/package.json` as the dependency intent and regenerate only `package-lock.json` with the repository's npm toolchain. Direct dependency declarations and ranges must not change. Confirm that a clean, ordinary `npm ci --no-audit --no-fund` succeeds rather than accepting an install-only or `--ignore-scripts`-only result. -3. Run the existing `npm --prefix frontend run build`. Its Vite configuration empties `veadk/webui` before generation, preventing stale files from the conflicted builds from surviving. -4. Run the strengthened validator, frontend tests, and real packaged startup browser check. - -### Alternatives considered - -- **Rebuild only:** repairs today's files but allows unresolved conflict markers to regress. Rejected because the existing validator is the natural low-cost prevention seam. -- **Replace only the entry JavaScript:** masks one symptom while 113 other corrupted assets and the lockfile remain broken. Rejected as incomplete and non-reproducible. -- **Add new full-build jobs to every CI workflow:** offers broader enforcement but expands hotfix scope and CI cost. Deferred; the reusable asset validator is sufficient for this contained repair. - -### Error behavior - -Validation must fail early with a message shaped around the invariant, for example: - -```text -assets/app/index-*.js contains an unresolved Git conflict marker -``` - -The validator must not print asset contents, because generated bundles can contain configuration-shaped strings even though secrets must never be embedded there. - -### Compatibility and rollback - -- Runtime compatibility: unchanged. The generated UI is built from the same current sources and APIs. -- Dependency compatibility: direct dependency ranges remain unchanged; the lockfile is reconciled to those ranges. -- Rollback: revert the hotfix commit. A rollback would restore the known blank-page defect, so the preferred operational recovery is to use the last clean generated WebUI while repairing main. - -### Security, state, and concurrency - -- Security: no credential, authorization, URL-fetching, or trust-boundary change. Validation reads repository-local generated text and reports filenames only. -- State/data: no application data or persistence change. -- Concurrency/cancellation: not applicable; the changed validator is a single-process build-time command. -- External side effects: dependency installation downloads packages already declared by the repository; no cloud or provider operation is authorized. - -## Component Contract Impact - -No component spec is created or updated. The hotfix preserves all externally observable Studio, CLI, HTTP, Runtime, authentication, state, and configuration contracts. It repairs generated delivery artifacts and their build-time integrity check. The maintained component-spec triggers in `specs/README.md` are therefore not met. This no-impact conclusion must be revisited if implementation needs to change a CLI option, route, configuration default, runtime behavior, or frontend API contract. - -## Affected Files - -Expected authored files: - -- `.gitattributes` -- `frontend/package-lock.json` -- `frontend/scripts/verifyBuiltAssets.mjs` -- `prd-spec/bugfixes/studio-startup-assets/2026-09-22-studio-startup-assets.md` -- `prd-spec/bugfixes/studio-startup-assets/2026-09-22-studio-startup-assets.zh.md` - -Expected generated files: - -- `veadk/webui/**` as produced by `npm --prefix frontend run build` - -No source files under `frontend/src`, Python modules, component specs, or user documentation are expected to change. - -## Implementation Tasks - -| ID | Task | Requirements | -| --- | --- | --- | -| `T-1` | Add the conflict-marker guard to the existing asset validator, then run the strengthened validator against the still-corrupt pre-rebuild tree as the negative control. Preserve the failure output before any generated asset changes. | `FR-2`, `FR-3` | -| `T-2` | Reconcile `package-lock.json`, remove the hotfix worktree's dependency directory, and prove clean `npm ci`. | `FR-1` | -| `T-3` | Rebuild `veadk/webui` using the existing build command and verify no stale/conflicted assets remain. | `FR-4`, `FR-5` | -| `T-4` | Run frontend tests, asset verification, diff hygiene, and secret scanning/pre-commit gates required for the final diff. | `FR-5`, `FR-7` | -| `T-5` | Start packaged Studio without workarounds and use a clean Chrome profile to verify visible rendering, DOM content, console/page errors, and core HTTP endpoints. | `FR-6` | -| `T-6` | Perform two-pass implementation review and synchronize English/Chinese delivery records with actual evidence. | all | - -## Verification and Acceptance - -Execution date is 2026-09-22. The final tested scope is `fix/studio-startup-assets` rebased onto `origin/main` revision `821f0f36`, with the hotfix worktree diff. Frontend dependency checks used Node `v22.23.0` and npm `10.9.8`. Revision `3bbd260a` remains the defect reproduction baseline described above. - -| Requirement | Task | Acceptance criterion | Test or verification command | Result/evidence | -| --- | --- | --- | --- | --- | -| `FR-2`, `FR-3` | `T-1` | `AC-1`: after adding the guard but before rebuilding, the current corrupted tree fails specifically on an unresolved conflict marker and names its file; this proves the new check is sensitive to the original defect. | `npm --prefix frontend run test:webui-assets` before rebuilding | `pass`: exit 1 with `assets/app/index-Ceg2hYta.js contains an unresolved Git conflict marker`; output was bounded to 536 bytes. | -| `FR-1` | `T-2` | `AC-2`: a clean ordinary dependency installation, including package lifecycle scripts, succeeds from the committed lockfile without changing `package.json`. | `npm --prefix frontend ci --no-audit --no-fund`; `git diff --exit-code -- frontend/package.json` | `pass`: 697 packages installed; `package.json` SHA-256 remained `81b2946cc95da4014fef3924b4e6a3a096e69d0b5635bfa00e97148571d19ac8`. | -| `FR-4`, `FR-5` | `T-3` | `AC-3`: build succeeds, the generated tree contains zero conflict-marker files, and all asset references resolve. | `npm --prefix frontend run build`; `npm --prefix frontend run test:webui-assets`; marker scan over `veadk/webui`; repeated-build SHA-256 comparison | `pass`: both Vite builds completed; 104 packaged files and 248 references passed; marker count is 0; two consecutive builds produced identical SHA-256 values for all 104 files. | -| `FR-5`, `FR-7` | `T-4` | `AC-4`: frontend regression tests and build-time checks pass without weakening assertions. | `npm --prefix frontend test`; `npm --prefix frontend run check:i18n`; `git diff --check` | `pass`: after rebasing, 1305 Node tests and 25 Vitest tests passed; 2 locales and 21 namespaces matched; diff check passed. | -| `FR-6` | `T-5` | `AC-5`: packaged Studio returns 200 for `/` and `/web/ui-config`; a fresh Chrome profile produces a non-empty rendered DOM and screenshot with no page-level JavaScript error. | `.venv/bin/veadk studio --dev --host 127.0.0.1 --port 18081` plus isolated Chrome/CDP verification | `pass`: both endpoints returned 200; `rootChildren=1`; rendered login text included `AgentKit Studio`; captured page/console/network error list was empty; screenshot confirmed visible UI. | -| all | `T-6` | `AC-6`: repository-required final gates complete or are honestly recorded with reason; bilingual documents match the delivered diff. | `uv run --extra dev pre-commit run --all-files` and applicable final review | `pass`: Ruff check, Ruff format, and gitleaks passed. Two implementation review passes resolved bounded diagnostics, diff3/CRLF marker coverage, and generated-bundle whitespace handling. | -| repository regression | `T-6` | The required default Python regression is executed and unrelated baseline failures are reported without widening this frontend-only hotfix. | `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` | `fail`: 5013 passed, 12 skipped, 2 xfailed, 11 failed, and 2 collection errors. Failures are in unchanged environment, Skill, MPA managed-service, and Harness tests; collection errors report missing optional `anthropic`. This hotfix changes no Python source. | - -## Risks and Open Questions - -- Lockfile regeneration can create a larger transitive diff because existing ranges are broad. Review must distinguish required synchronization from unrelated upgrades and minimize churn where npm permits. -- Dependency commands use the repository-local `frontend/package.json` and `package-lock.json` with the available Node 22/npm 10 toolchain. The final clean-install evidence must report the exact versions used. -- Generated WebUI diffs are large by nature. Validation must focus on reproducibility, absence of conflict markers, internal references, frontend tests, and real browser behavior rather than manual review of minified text alone. -- `website-integration.js` contains third-party template literals with significant trailing whitespace. The existing `.gitattributes` policy already disables whitespace diagnostics for generated JavaScript under `veadk/webui/assets`; this hotfix extends that exact policy to the generated root-level integration bundle instead of rewriting semantic bundle content. -- The current repository does not appear to run `test:webui-assets` in every general PR workflow. This hotfix strengthens the reusable command but does not claim universal CI enforcement. A separate CI-governance change may be proposed later if desired. -- Open questions: none blocking. The user approved the contained restore-and-prevent-recurrence approach on 2026-09-22. - -## Review and Delivery Record - -- 2026-09-22: scope, alternatives, root-cause evidence, and the recommended approach were presented to the user. The user replied `继续`, approving continuation with the recommended design. -- 2026-09-22 spec review: found and resolved two P1 gaps. The negative control now explicitly runs the strengthened validator before rebuilding, and clean installation now requires ordinary lifecycle scripts instead of relying on `--ignore-scripts`. Bilingual identifiers, commands, affected files, compatibility, component no-impact, security, rollback, and acceptance traceability were checked with no remaining blocker. -- 2026-09-22 implementation review: the second edge-case pass added coverage for the standard diff3 `|||||||` base marker. It also extended the repository's existing generated-JavaScript whitespace attribute to `veadk/webui/website-integration.js`, whose third-party template literals contain significant trailing whitespace. -- Spec review conclusion: approved for implementation. -- 2026-09-22 final synchronization: rebased onto `origin/main` revision `821f0f36`. Upstream had independently replaced the corrupt WebUI while adding later frontend fixes; rebuilding from that latest source produced zero `veadk/webui` diff. The hotfix therefore retains only the reproducible lockfile correction, conflict-marker prevention guard, and this bilingual design record. -- 2026-09-22 delivery: all tasks `T-1` through `T-6` and acceptance criteria `AC-1` through `AC-6` passed. No Python source, frontend application source, public contract, component spec, or user documentation changed. -- The broad Python regression remains red for unrelated latest-main failures recorded above. The frontend-specific delivery gates and repository pre-commit hooks pass; the unrelated Python failures are not suppressed or modified by this hotfix. -- Remaining scope: pull request, publication, and deployment are not part of this delivery. Commit and push were separately authorized by the user. diff --git a/prd-spec/bugfixes/studio-startup-assets/2026-09-22-studio-startup-assets.zh.md b/prd-spec/bugfixes/studio-startup-assets/2026-09-22-studio-startup-assets.zh.md deleted file mode 100644 index fecfd4063..000000000 --- a/prd-spec/bugfixes/studio-startup-assets/2026-09-22-studio-startup-assets.zh.md +++ /dev/null @@ -1,194 +0,0 @@ -# Studio 启动资产完整性 Hotfix - -[English](2026-09-22-studio-startup-assets.md) - -## 元数据 - -- Change ID:`studio-startup-assets` -- 创建日期:2026-09-22 -- 修订日期:2026-09-22 -- 状态:`implemented` -- 用户批准:在推荐的“恢复并防复发”方案于 2026-09-22 会话中展示后,用户已批准 -- 相关组件规范:不变更组件契约;参见[组件契约影响](#组件契约影响) -- 所属规范:[AgentKit Studio AI 开发规范](../../../frontend/SPEC.md) - -## 背景与证据 - -### 预期行为 - -通过仓库本地 CLI 运行打包版 Studio 时,必须提供可执行的 WebUI: - -```bash -.venv/bin/veadk studio --dev --host 127.0.0.1 --port 8000 -``` - -浏览器必须执行入口模块并渲染 Studio 登录页或工作区,而不是留下空的根节点。使用 `npm ci` 也必须能够复现干净的前端依赖安装。 - -### 实际行为 - -当前 `origin/main` 修订 `3bbd260a` 对 `/` 及入口 JavaScript 都返回 HTTP 200,但浏览器显示白屏。打包入口文件以 Git 冲突标记开头: - -```text -<<<<<<<< HEAD:veadk/webui/assets/app/index-CKbnL9AU.js -``` - -仓库检查发现 `veadk/webui` 下有 114 个跟踪文件包含冲突标记。冲突标记最早出现在 feature 提交 `dae75ce8`,并存在于合并提交的第二父提交 `9ff62d54` 中;第一父提交 `fd9bec69` 中数量为 0。`frontend/src` 下的源文件不包含这些标记。 - -已提交的 `frontend/package.json` 与 `frontend/package-lock.json` 也不一致。在干净的 hotfix worktree 中,`npm ci --dry-run --ignore-scripts --no-audit --no-fund` 失败,因为锁文件没有包含已锁定 Vitest/Vite 依赖图所需的 `esbuild@0.28.2` peer resolution。因此仓库当前无法复现干净的前端构建。 - -现有 `frontend/scripts/verifyBuiltAssets.mjs` 会检查目录结构、内部引用以及可选的 HTTP 逐字节一致性,但不会显式拒绝未解决的冲突标记。在当前工作树中,它会因引用的资产缺失而失败,却没有报告更直接的损坏原因。 - -### 已确认根因 - -来自两个不兼容构建的 WebUI 生成产物带着未解决的文本合并冲突标记被提交。静态服务不会解析 JavaScript,因此服务端仍返回 200,只有浏览器解析入口模块时才暴露损坏。不可复现的锁文件又阻断了可靠的干净重建恢复路径。 - -## 目标 - -- 恢复可复现的前端依赖图,使干净检出中的 `npm ci` 成功。 -- 从当前 `frontend` 源码重新生成 `veadk/webui`,确保每个打包资产都来自同一次一致构建且不含冲突标记。 -- 扩展现有打包资产校验器,使用直接诊断拒绝未解决的 Git 冲突标记。 -- 证明普通打包模式启动可以在干净 Chrome Profile 中渲染,无需 `--vite` 或 `--frontend-dir` 临时方案。 -- 将 hotfix 与无关应用、API、Runtime 和 UI 行为隔离。 - -## 非目标 - -- 不改变 Studio CLI 选项、host/port 默认值、HTTP 路由、鉴权、Session 行为、Runtime 行为、MPA 行为或 UI 交互设计。 -- 不新增前端或 Python 依赖。锁文件对齐可以更新 `package.json` 已要求的传递依赖解析。 -- 不进行大范围 CI workflow 重构。Hotfix 会加强现有 `test:webui-assets` 命令;除非当前仓库证据证明有必要,否则不在所有 workflow 中新增构建任务。 -- 未经用户另行授权,不 commit、push、创建 PR、发布或部署。 -- 不修改用户主 checkout 或其中的本地清理提交。 - -## 场景 - -### 场景 S-1:拒绝损坏的生成资产 - -假设 `veadk/webui` 下某文件包含以 `<<<<<<<`、`|||||||`、`=======` 或 `>>>>>>>` 开头的标准未解决合并行,当运行 `npm --prefix frontend run test:webui-assets` 时,校验必须失败、指出受影响文件,并明确说明存在未解决的冲突标记。 - -### 场景 S-2:可复现干净构建 - -假设处于干净 checkout 并使用受支持的本地 Node/npm 工具链,当运行 `npm --prefix frontend ci` 和 `npm --prefix frontend run build` 时,依赖安装和两次前端构建必须成功完成,无需手工修改锁文件。 - -### 场景 S-3:打包版 Studio 正常渲染 - -假设 `veadk/webui` 已重新生成,当不带 `--vite` 或 `--frontend-dir` 启动 `.venv/bin/veadk studio --dev` 时,`/` 和 `/web/ui-config` 必须返回 200,干净 Chrome Profile 必须渲染非空 Studio UI,且无页面级 JavaScript 错误。 - -### 场景 S-4:源码与运行时契约保持不变 - -假设评审 hotfix diff,当检查源码、API、配置和用户文档时,不应存在 Studio 运行时契约或用户流程变更;变更仅限依赖锁对齐、资产校验、生成 WebUI 以及本 PRD 双语文档。 - -## 需求 - -| ID | 需求 | -| --- | --- | -| `FR-1` | `frontend/package-lock.json` 必须与 `frontend/package.json` 同步,并且从干净依赖目录执行 `npm --prefix frontend ci` 必须成功。 | -| `FR-2` | `frontend/scripts/verifyBuiltAssets.mjs` 必须检查打包文本资产,并在引用校验掩盖直接原因之前对未解决的 Git 冲突标记失败。 | -| `FR-3` | 冲突标记检查必须覆盖 `veadk/webui` 下的 `.html`、`.css` 和 `.js` 文件,包括 diff3 的 `|||||||` base 标记,并报告相对文件名;只有标准 Git 标记出现在行边界时才判定失败,不得把普通压缩运算符或源码文本误判为冲突标记。 | -| `FR-4` | `npm --prefix frontend run build` 必须从当前源码重新生成一致的 `veadk/webui` 树;不得残留旧资产或未解决标记。 | -| `FR-5` | `npm --prefix frontend run test:webui-assets` 必须对重新生成的资产通过,并保留所有现有目录、引用和可选 HTTP 字节一致性检查。 | -| `FR-6` | 普通打包模式 `veadk studio --dev` 必须在干净 Chrome Profile 中渲染可见 Studio 内容。仅 HTTP 200 不能作为充分证据。 | -| `FR-7` | 变更不得修改公共 Python import、CLI 参数、服务端路由、前端 API 类型、配置优先级、鉴权、持久化或 Runtime 语义。 | - -## 方案 - -### 选定方案 - -复用现有构建和校验架构: - -1. 在 `verifyBuiltAssets.mjs` 中增加窄范围内容完整性检查。对于校验器已读取的每个 `.html`、`.css` 和 `.js` 打包文件,使用多行表达式检测标准未解决 Git 标记行,并抛出包含相对路径的断言。此检查先于内部引用提取执行。 -2. 以当前 `frontend/package.json` 为依赖意图,使用仓库 npm 工具链仅重新生成 `package-lock.json`。不得改变直接依赖声明及其范围。必须确认普通的干净 `npm ci --no-audit --no-fund` 成功,不能只接受一次 install-only 或仅 `--ignore-scripts` 的结果。 -3. 执行现有 `npm --prefix frontend run build`。其 Vite 配置会在生成前清空 `veadk/webui`,避免冲突构建的旧文件继续残留。 -4. 执行加强后的校验器、前端测试和真实打包启动浏览器检查。 - -### 备选方案 - -- **仅重建:** 可以修复当前文件,但仍允许未解决冲突标记回归。现有校验器是自然且低成本的预防边界,因此不采用。 -- **只替换入口 JavaScript:** 只掩盖一个症状,仍留下另外 113 个损坏资产和破损锁文件。不完整且不可复现,因此不采用。 -- **为所有 CI workflow 新增完整构建任务:** 执行范围更广,但会显著扩大 hotfix 范围和 CI 成本。本次使用可复用资产校验器已足够,故暂缓。 - -### 错误行为 - -校验必须围绕不变量尽早失败,例如: - -```text -assets/app/index-*.js contains an unresolved Git conflict marker -``` - -校验器不得输出资产内容,因为即使生成 bundle 不应嵌入密钥,其中仍可能包含类似配置的字符串。 - -### 兼容与回滚 - -- Runtime 兼容性:不变。生成 UI 仍来自相同的当前源码和 API。 -- 依赖兼容性:直接依赖范围不变;锁文件对齐到这些范围。 -- 回滚:revert hotfix 提交。回滚会恢复已知白屏缺陷,因此首选操作恢复方式是使用上一个干净的生成 WebUI,同时修复 main。 - -### 安全、状态与并发 - -- 安全:不改变凭据、鉴权、URL 获取或信任边界。校验只读取仓库本地生成文本并仅报告文件名。 -- 状态/数据:不改变应用数据或持久化。 -- 并发/取消:不适用;变更后的校验器是单进程构建期命令。 -- 外部副作用:依赖安装只下载仓库已声明的软件包;未授权任何云端或 provider 操作。 - -## 组件契约影响 - -不创建或更新组件规范。本 hotfix 保持所有外部可观察的 Studio、CLI、HTTP、Runtime、鉴权、状态和配置契约不变,只修复生成交付资产及其构建期完整性检查。因此未触发 `specs/README.md` 中的组件规范更新条件。如果实现需要变更 CLI 选项、路由、配置默认值、Runtime 行为或前端 API 契约,必须重新评估此 no-impact 结论。 - -## 涉及文件 - -预期手工维护文件: - -- `.gitattributes` -- `frontend/package-lock.json` -- `frontend/scripts/verifyBuiltAssets.mjs` -- `prd-spec/bugfixes/studio-startup-assets/2026-09-22-studio-startup-assets.md` -- `prd-spec/bugfixes/studio-startup-assets/2026-09-22-studio-startup-assets.zh.md` - -预期生成文件: - -- 由 `npm --prefix frontend run build` 生成的 `veadk/webui/**` - -不应修改 `frontend/src` 下的源码、Python 模块、组件规范或用户文档。 - -## 实现任务 - -| ID | 任务 | 需求 | -| --- | --- | --- | -| `T-1` | 在现有资产校验器中增加冲突标记门禁,然后在生成资产仍损坏且尚未重建的工作树上运行加强后的校验器,作为负例证明;在任何生成资产变更前保留失败输出。 | `FR-2`, `FR-3` | -| `T-2` | 对齐 `package-lock.json`,移除 hotfix worktree 的依赖目录,并证明干净 `npm ci`。 | `FR-1` | -| `T-3` | 使用现有构建命令重新生成 `veadk/webui`,确认不存在旧资产或冲突资产。 | `FR-4`, `FR-5` | -| `T-4` | 执行前端测试、资产校验、diff hygiene 和最终 diff 所需的密钥扫描/pre-commit 门禁。 | `FR-5`, `FR-7` | -| `T-5` | 不带临时方案启动打包版 Studio,使用干净 Chrome Profile 验证可见渲染、DOM 内容、console/page error 和核心 HTTP endpoint。 | `FR-6` | -| `T-6` | 执行两轮实现评审,并用实际证据同步中英文交付记录。 | 全部 | - -## 验证与验收 - -执行日期为 2026-09-22。最终测试范围为 rebase 到 `origin/main` 修订 `821f0f36` 的 `fix/studio-startup-assets` 及其 hotfix worktree diff。前端依赖检查使用 Node `v22.23.0` 和 npm `10.9.8`。修订 `3bbd260a` 仍是上文记录的缺陷复现基线。 - -| 需求 | 任务 | 验收标准 | 测试或验证命令 | 结果/证据 | -| --- | --- | --- | --- | --- | -| `FR-2`, `FR-3` | `T-1` | `AC-1`:增加门禁后且重建前,当前损坏工作树必须针对未解决冲突标记失败并指出文件名,以证明新检查对原始缺陷敏感。 | 重建前运行 `npm --prefix frontend run test:webui-assets` | `pass`:退出码 1,提示 `assets/app/index-Ceg2hYta.js contains an unresolved Git conflict marker`;输出被限制为 536 字节。 | -| `FR-1` | `T-2` | `AC-2`:包括 package lifecycle script 在内的普通干净依赖安装使用已提交锁文件成功,且不修改 `package.json`。 | `npm --prefix frontend ci --no-audit --no-fund`;`git diff --exit-code -- frontend/package.json` | `pass`:安装 697 个 package;`package.json` SHA-256 保持 `81b2946cc95da4014fef3924b4e6a3a096e69d0b5635bfa00e97148571d19ac8`。 | -| `FR-4`, `FR-5` | `T-3` | `AC-3`:构建成功,生成树的冲突标记文件数量为 0,且所有资产引用可解析。 | `npm --prefix frontend run build`;`npm --prefix frontend run test:webui-assets`;扫描 `veadk/webui` 中的标记;重复构建 SHA-256 对比 | `pass`:两次 Vite 构建完成;104 个打包文件和 248 个引用通过;标记数量为 0;连续两次构建的全部 104 个文件 SHA-256 完全一致。 | -| `FR-5`, `FR-7` | `T-4` | `AC-4`:前端回归测试和构建期检查通过,且未弱化断言。 | `npm --prefix frontend test`;`npm --prefix frontend run check:i18n`;`git diff --check` | `pass`:rebase 后 1305 个 Node 测试和 25 个 Vitest 测试通过;2 个 locale、21 个 namespace 一致;diff check 通过。 | -| `FR-6` | `T-5` | `AC-5`:打包版 Studio 的 `/` 与 `/web/ui-config` 返回 200;新 Chrome Profile 产生非空渲染 DOM 和截图,且没有页面级 JavaScript 错误。 | `.venv/bin/veadk studio --dev --host 127.0.0.1 --port 18081` 加隔离 Chrome/CDP 验证 | `pass`:两个 endpoint 均返回 200;`rootChildren=1`;渲染登录文本包含 `AgentKit Studio`;捕获的 page/console/network error 列表为空;截图确认 UI 可见。 | -| 全部 | `T-6` | `AC-6`:仓库要求的最终门禁全部完成,或诚实记录原因;双语文档与交付 diff 一致。 | `uv run --extra dev pre-commit run --all-files` 和适用的最终评审 | `pass`:Ruff check、Ruff format 和 gitleaks 通过。两轮实现评审解决了有界诊断、diff3/CRLF 标记覆盖和生成 bundle 空白处理。 | -| 仓库回归 | `T-6` | 执行要求的默认 Python 回归,并在不扩大本前端 hotfix 范围的前提下记录无关基线失败。 | `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` | `fail`:5013 passed、12 skipped、2 xfailed、11 failed、2 collection errors。失败位于未修改的环境、Skill、MPA managed-service 与 Harness 测试;collection error 提示缺少可选 `anthropic`。本 hotfix 未修改 Python 源码。 | - -## 风险与待确认问题 - -- 现有依赖范围较宽,锁文件重新生成可能产生较大的传递依赖 diff。评审必须区分必要同步与无关升级,并在 npm 允许的范围内减少 churn。 -- 依赖命令使用仓库本地 `frontend/package.json`、`frontend/package-lock.json` 和当前可用的 Node 22/npm 10 工具链。最终干净安装证据必须记录实际版本。 -- 生成 WebUI diff 天然较大。校验必须关注可复现性、无冲突标记、内部引用、前端测试和真实浏览器行为,不能只依赖人工阅读压缩文本。 -- `website-integration.js` 包含带有语义尾随空白的第三方模板字符串。现有 `.gitattributes` 策略已对 `veadk/webui/assets` 下的生成 JavaScript 禁用空白诊断;本 hotfix 将同一策略精确扩展到根目录的生成 integration bundle,而不是改写有语义的 bundle 内容。 -- 当前仓库似乎没有在每个通用 PR workflow 中运行 `test:webui-assets`。本 hotfix 会增强可复用命令,但不声称已经实现全局 CI 强制。若需要,可另行提出 CI 治理变更。 -- 阻塞性待确认问题:无。用户已于 2026-09-22 批准范围受控的“恢复并防复发”方案。 - -## 评审与交付记录 - -- 2026-09-22:已向用户展示范围、备选方案、根因证据和推荐方案。用户回复“继续”,批准按推荐方案推进。 -- 2026-09-22 Spec 评审:发现并解决两个 P1 缺口。负例现在明确要求在重建前运行加强后的校验器;干净安装现在要求执行普通 lifecycle script,不能依赖 `--ignore-scripts`。已检查双语标识、命令、涉及文件、兼容性、组件 no-impact、安全、回滚和验收追踪,无剩余阻塞项。 -- 2026-09-22 实现评审:第二轮边界检查增加了标准 diff3 `|||||||` base 标记覆盖;同时将仓库已有的生成 JavaScript 空白属性扩展到 `veadk/webui/website-integration.js`,因为其中第三方模板字符串的尾随空白具有语义。 -- Spec 评审结论:批准进入实现。 -- 2026-09-22 最终同步:已 rebase 到 `origin/main` 修订 `821f0f36`。上游在加入后续前端修复时已独立替换损坏 WebUI;从该最新源码重新构建后,`veadk/webui` diff 为 0。因此 hotfix 最终只保留可复现 lockfile 修复、冲突标记防复发门禁和本双语设计记录。 -- 2026-09-22 交付:任务 `T-1` 至 `T-6` 与验收标准 `AC-1` 至 `AC-6` 全部通过。未修改 Python 源码、前端应用源码、公共契约、组件规范或用户文档。 -- 默认 Python 全量回归因上述最新 main 无关失败仍为红灯。前端专项交付门禁和仓库 pre-commit 均通过;本 hotfix 未压制或修改这些无关 Python 失败。 -- 剩余范围:PR、发布和部署不属于本次交付;用户已另行授权 commit 与 push。 diff --git a/prd-spec/bugfixes/studio-tos-credentials/2026-10-15-tos-credentials.md b/prd-spec/bugfixes/studio-tos-credentials/2026-10-15-tos-credentials.md deleted file mode 100644 index ddefa2139..000000000 --- a/prd-spec/bugfixes/studio-tos-credentials/2026-10-15-tos-credentials.md +++ /dev/null @@ -1,79 +0,0 @@ -# Studio TOS credential handoff - -- Change ID: `studio-tos-credentials` -- Date: 2026-10-15 -- Status: implemented -- [中文](2026-10-15-tos-credentials.zh.md) -- Contract: [Studio creation](../../../specs/studio-mpa-creation/README.md) - -## Evidence and scope - -DeploymentResourceService receives resolved credentials, but its TOSService -constructor discards them and independently resolves credentials. Cloud BytePlus -resource listing can therefore fail before making a TOS request. CR already -consumes these credentials. No frontend, Runtime image, database, dependency, -permission or cloud-resource changes are required. - -## Requirements and design - -- FR-1: With explicit complete AK/SK, resolve the provider/region TOS endpoint - through VolcConfiguration and construct TosClientV2 with that AK/SK and session - token. Adapt its bucket response through the existing client/actual_region - shape; do not patch SDK globals or mutate process environment. -- FR-2: Without complete explicit credentials, retain the existing TOSService - resolution path. Both Volcengine and BytePlus retain endpoint resolution, - regional filtering, pagination and resource validation. -- FR-3: Route invocations resolve credentials and construct a service per request; - rotated credentials take effect on the next request. No cross-request client - cache or new retry/cancellation policy is introduced. -- FR-4: Errors remain HTTP 400/502 as appropriate; credential values are redacted - by the existing route handler. No secret persistence or response fields added. - -## Tasks and acceptance - -1. T-1: Add failing explicit-credential, rotation and redaction tests in - tests/frontend/server/test_deployment_resources.py (FR-1/3/4). -2. T-2: Adapt frontend/server/deployment_resources.py and preserve fallback; - cover both providers and existing bucket validation (FR-1/2). -3. T-3: Update paired contract and run targeted pytest, Ruff, Pyright and diff - checks. No UI changes, so frontend build/browser gates are not applicable. - -Acceptance: mocked TOS calls receive current credentials including token; -no-credential callers retain SDK fallback; existing list/validation tests pass; -SDK errors cannot expose supplied credentials. Live cloud deployment is separate -and not authorized by this code-fix request. - -## Risks and review - -The raw TOS response differs from the wrapper response: reuse _tos_buckets to -normalize it and test list/validation consumers. Keep endpoint resolution in the -SDK rather than hardcoding hosts. One request keeps one credential snapshot; -rotation during a request is not retried automatically. Rollback is code-only. - -The user approved the proposed scope with “改改”. Direct design review (review-spec -skill unavailable) confirmed scoped changes, provider compatibility, secret -handling, testability and paired-language equivalence; no blockers. Verification -is pending. No production code or tests were modified before this review. - - -## Verification (2026-10-15) - -Scope: local TOS credential handoff diff based on `8ae26a6d`. -- `pass`: regression first reproduced five failures; two fallback cases passed. -- `pass`: `uv run --extra dev pytest -q tests/frontend/server/test_deployment_resources.py` - — 38 passed, one upstream Starlette deprecation warning. All cloud calls were mocked. -- `pass`: Ruff 0.11.12 check and format on both changed Python files. -- `pass`: `uv tool run pyright --pythonpath .venv/bin/python frontend/server/deployment_resources.py tests/frontend/server/test_deployment_resources.py` - — zero errors/warnings. -- `not_applicable`: frontend assets/build/browser; no UI or HTTP shape changes. -- `not_run`: live cloud acceptance and deployment; requires running the updated - Studio service with its IAM credentials. No cloud resources were changed. - -Implementation review confirmed the existing bucket normalization and regional -validation are reused; the fallback remains unchanged. No credentials are added -to logs, persistent state or browser output. Credential refresh is covered for -successive resource-list requests, not automatic mid-request recovery. - -Submission verification on 2026-10-15: fetched origin and rebased onto the -unchanged tracking branch at `8ae26a6d`; `uv run --extra dev pre-commit run ---all-files` passed all hooks, and the targeted suite passed all 38 tests again. diff --git a/prd-spec/bugfixes/studio-tos-credentials/2026-10-15-tos-credentials.zh.md b/prd-spec/bugfixes/studio-tos-credentials/2026-10-15-tos-credentials.zh.md deleted file mode 100644 index 0b7960dea..000000000 --- a/prd-spec/bugfixes/studio-tos-credentials/2026-10-15-tos-credentials.zh.md +++ /dev/null @@ -1,70 +0,0 @@ -# Studio TOS 凭据传递 - -- Change ID:`studio-tos-credentials` -- 日期:2026-10-15 -- 状态:implemented -- [English](2026-10-15-tos-credentials.md) -- 契约:[Studio 创建](../../../specs/studio-mpa-creation/README.zh.md) - -## 证据与范围 - -DeploymentResourceService 收到已解析的凭据,但 TOSService 构造时没有使用, -而是独立解析凭据,导致云端 BytePlus 资源查询可能在请求 TOS 前失败。 -CR 已使用这些凭据。不修改前端、Runtime 镜像、数据库、依赖、权限或云资源。 - -## 需求与设计 - -- FR-1:显式 AK/SK 完整时,通过 VolcConfiguration 解析 provider/region 对应 - 的 TOS endpoint,以该 AK/SK 和 session token 构造 TosClientV2。 - 适配为既有 client/actual_region 结构读取桶响应,不修改 SDK 全局状态或进程环境。 -- FR-2:没有完整显式凭据时,保留既有 TOSService 解析路径。 - 火山和 BytePlus 均保留 endpoint 解析、地域过滤、分页及资源验证。 -- FR-3:每次路由请求重新解析凭据并创建 service,轮换后的凭据在下一次请求生效。 - 不增加跨请求客户端缓存,不改变重试或取消策略。 -- FR-4:错误继续按类型返回 HTTP 400/502;既有路由处理器脱敏凭据值。 - 不增加密钥持久化或响应字段。 - -## 任务与验收 - -1. T-1:在 tests/frontend/server/test_deployment_resources.py 增加先失败的 - 显式凭据、轮换、脱敏测试(FR-1/3/4)。 -2. T-2:适配 frontend/server/deployment_resources.py 并保留回退路径,覆盖 - 两个 provider 和已有桶验证(FR-1/2)。 -3. T-3:更新双语契约,执行定向 pytest、Ruff、Pyright 和 diff 检查。 - 无 UI 改动,前端构建和浏览器检查不适用。 - -验收:模拟 TOS 调用收到当前凭据及 token;无凭据调用保留 SDK 回退; -已有列表及验证测试通过;SDK 错误不暴露传入凭据。 -真实云端部署单独验证,本次代码修复请求不授权部署。 - -## 风险与评审 - -原生 TOS 响应与封装响应不同:复用 _tos_buckets 归一化,并测试列表和验证调用方。 -endpoint 继续由 SDK 解析,不写死域名。单次请求使用一个凭据快照,不自动重试 -请求期间的凭据轮换。回滚仅涉及代码。 - -用户以“改改”批准方案。因无 review-spec 技能,直接评审确认范围、provider 兼容、 -密钥处理、可测试性及双语等价,无阻塞项。验证待执行。 -评审前未修改生产代码或测试。 - - -## 验证(2026-10-15) - -范围:基于 `8ae26a6d` 的本地 TOS 凭据传递改动。 -- `pass`:回归测试先复现五项失败,两项回退测试通过。 -- `pass`:`uv run --extra dev pytest -q tests/frontend/server/test_deployment_resources.py` - — 38 项通过,一条上游 Starlette 弃用警告。所有云调用均使用模拟。 -- `pass`:两个改动 Python 文件的 Ruff 0.11.12 check 和 format。 -- `pass`:`uv tool run pyright --pythonpath .venv/bin/python frontend/server/deployment_resources.py tests/frontend/server/test_deployment_resources.py` - — 零错误、零警告。 -- `not_applicable`:前端产物、构建和浏览器;未修改 UI 或 HTTP 结构。 -- `not_run`:真实云端验收及部署,需使用 IAM 凭据运行更新后的 Studio。 - 未修改任何云资源。 - -实现评审确认复用既有桶归一化与地域验证,回退路径不变。 -不向日志、持久化状态或浏览器输出新增凭据。 -凭据刷新覆盖连续资源列表请求,不包含请求中途自动恢复。 - -2026-10-15 提交前验证:fetch origin 并 rebase 到未变化的跟踪分支 -`8ae26a6d`;`uv run --extra dev pre-commit run --all-files` 全部钩子通过, -定向测试再次全部通过(38 项)。 diff --git a/prd-spec/bugfixes/studio-trace-pagination/2026-09-17-session-trace.md b/prd-spec/bugfixes/studio-trace-pagination/2026-09-17-session-trace.md deleted file mode 100644 index 954e2d525..000000000 --- a/prd-spec/bugfixes/studio-trace-pagination/2026-09-17-session-trace.md +++ /dev/null @@ -1,13 +0,0 @@ -# Studio trace pagination - -## Evidence and approved scope -The user authorized fixing and redeploying local Studio only. Runtime already exports session-tagged HTTP and business roots. Studio discards candidate roots, expands only 200 child spans, and misses roots in its bounded broad scan. - -## Design and requirements -Use exact mpa.session.id then gen_ai.session.id lookup before existing compatibility fallbacks. Validate session/runtime/invocation tags, prefer mpa.agent.turn, select the nearest trace, retain the located root, and page children by trace ID. Deduplicate span IDs and exclude foreign trace rows. Limit expansion to 50 pages; fail explicitly instead of claiming a complete truncated trace. Preserve permission errors and retries. No MPA, API signature, credentials, or file-download behavior changes. - -## Review and acceptance -Self-review: existing endpoint and authorization contracts are unchanged; no component contract change. User approval: “改下前端然后重新部署一下 mpa不要改”. Regression tests cover 201 spans, root retention, duplicate roots, foreign Runtime/trace exclusion, tag aliases, invocation filtering and the page limit. Verify the affected live session through the local endpoint after restart. Source changes are confined to the Studio service and tests; no browser asset rebuild is required. - -## Verification (2026-09-17) -Pass: 16 targeted Python tests; incremental executable-line coverage 24/24 (100%); repository pre-commit including Ruff check/format and secrets scan; live local endpoint HTTP 200 with 778 spans; browser flame graph displays mpa.agent.turn and matching session ID. Browser assets unchanged, frontend build not applicable. Pyright reports the pre-existing SDK Configuration.host assignment typing error; not introduced by this diff. Runtime image unchanged. diff --git a/prd-spec/bugfixes/studio-trace-pagination/2026-09-17-session-trace.zh.md b/prd-spec/bugfixes/studio-trace-pagination/2026-09-17-session-trace.zh.md deleted file mode 100644 index 7f58a338b..000000000 --- a/prd-spec/bugfixes/studio-trace-pagination/2026-09-17-session-trace.zh.md +++ /dev/null @@ -1,13 +0,0 @@ -# Studio 链路分页修复 - -## 证据与授权范围 -用户授权修复并重新部署本地 Studio,MPA 不改。Runtime 已上报带会话标识的请求与业务根 span。Studio 丢弃候选根节点,仅查询最新 200 条子 span,全量扫描也受条数限制。 - -## 设计与要求 -在原有兼容回退之前精确查询 mpa.session.id,再查询 gen_ai.session.id。校验会话、Runtime、调用标识,优先选择 mpa.agent.turn,再选择时间最近的链路。保留已定位根节点,按 trace ID 分页读取子节点,以 span ID 去重并排除异链路返回值。最多 50 页,超限明确报错,不将截断结果作为完整链路。保留权限错误与重试处理。不修改 MPA、接口签名、凭证或文件下载行为。 - -## 评审与验收 -已自审:既有接口和鉴权契约不变,无组件契约变更。用户批准:“改下前端然后重新部署一下 mpa不要改”。回归覆盖 201 条 span、根节点保留、重复根节点、异 Runtime/链路排除、标识别名、调用筛选及分页上限。重启后通过本地接口验证实际故障会话。仅修改 Studio 服务和测试,不涉及浏览器静态资源,无需重建静态包。 - -## 验证(2026-09-17) -通过:16 项相关 Python 测试;新增可执行行覆盖 24/24(100%);全仓 pre-commit 的 Ruff 检查、格式及密钥扫描;本地真实接口 HTTP 200 返回 778 条 span;浏览器火焰图显示 mpa.agent.turn 与正确会话标识。浏览器静态资源未改,前端构建不适用。Pyright 仍报告既有 SDK Configuration.host 赋值类型问题,本次未引入。Runtime 镜像未变。 diff --git a/prd-spec/bugfixes/studio-webui-build-integrity/2026-09-23-rebuild-corrupted-assets.md b/prd-spec/bugfixes/studio-webui-build-integrity/2026-09-23-rebuild-corrupted-assets.md deleted file mode 100644 index 4827ca751..000000000 --- a/prd-spec/bugfixes/studio-webui-build-integrity/2026-09-23-rebuild-corrupted-assets.md +++ /dev/null @@ -1,37 +0,0 @@ -# Rebuild corrupted Studio WebUI assets - -[中文版](2026-09-23-rebuild-corrupted-assets.zh.md) - -- Change ID: `studio-webui-build-integrity` -- Created/revised: 2026-09-23 -- Status: implemented -- Component specs: no change; the HTTP, UI, and deployment contracts are unchanged. - -## Background and evidence - -The deployed Studio root returned HTTP 200, but rendered a blank page. Its referenced `assets/app/index-Ceg2hYta.js` began with `<<<<<<<< HEAD`, and `node --check` failed with `Unexpected token '<<'`. A repository scan found conflict markers in 114 packaged JS files. The existing asset verifier checked references, not merge markers. The source build also encountered an implicit-`any` TypeScript error in the WeCom callback and a locally missing declared SDK dependency. The latter was an installation issue, not an application change. - -## Goals, non-goals, scenarios, and requirements - -- Goal: serve parseable, internally consistent WebUI assets so the Studio login page renders. -- Non-goals: alter login, UserPool, Runtime, MPA, scheduler, or WeCom authorization behavior. -- `FR-1`: Given a clean source build, when assets are packaged, no HTML/CSS/JS file contains a line-start Git conflict marker. -- `FR-2`: Given the deployed Studio URL, when an unauthenticated browser opens `/`, the Identity login UI renders instead of a blank page. -- Boundary: an unauthenticated `/web/runtime-config` request redirects to login; this does not prove the authenticated workbench. - -## Design and contract impact - -Regenerate `veadk/webui/` with the existing `frontend/scripts/build.mjs` instead of editing minified bundles. Add a conflict-marker assertion to `frontend/scripts/verifyBuiltAssets.mjs`. Type the existing WeCom SDK callback result narrowly to let TypeScript build; runtime logic is unchanged. No new dependency, API, state, persistence, permissions, credential handling, or compatibility contract is introduced. No component spec or user documentation changes are needed. Alternative manual edits to 114 bundles would be unrepeatable and were rejected. The generated JavaScript includes template-string whitespace, so `git diff --check` may report generated lines even when syntax and packaging checks pass. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | Evidence (2026-09-23, `15ac109`) | -| --- | --- | --- | --- | -| `FR-1` | `T-1` rebuild packaged assets; `T-2` add merge-marker check | `AC-1` every packaged JS parses and asset references resolve | `npm run build`: pass; `npm run test:webui-assets`: pass, 101 files/236 references; `node --check` on all packaged JS: pass | -| `FR-2` | `T-3` redeploy the existing Studio application | `AC-2` browser shows the Identity login heading and button | Existing VeFaaS application update: pass; real browser DOM and screenshot: pass | - -`npm test`: pass (1,253 Node tests and 25 Vitest tests). Staged-file pre-commit: pass. Full-file pre-commit: fail because Ruff reformatted three unrelated existing Python test files; those incidental edits were reverted. `git diff --check`: fail on whitespace inside generated `website-integration.js` template strings. Authenticated workbench and MPA creation: not_run; outside this blank-page fix. Cloud credentials and raw logs are intentionally excluded. - -## Risks, review, and delivery - -Generated asset hashes and many filenames change as a set; deploying only `index.html` would break references. The existing app and Identity configuration were reused, and the scheduler deployment completed. Source, asset, and browser checks resolve the reported blocker. No `review-spec` skill was available; direct review found no contract change or security expansion. This operational repair was performed under the ongoing cloud-deployment request; no separate design approval was recorded before implementation. diff --git a/prd-spec/bugfixes/studio-webui-build-integrity/2026-09-23-rebuild-corrupted-assets.zh.md b/prd-spec/bugfixes/studio-webui-build-integrity/2026-09-23-rebuild-corrupted-assets.zh.md deleted file mode 100644 index cd7c63ee8..000000000 --- a/prd-spec/bugfixes/studio-webui-build-integrity/2026-09-23-rebuild-corrupted-assets.zh.md +++ /dev/null @@ -1,37 +0,0 @@ -# 重新构建损坏的 Studio WebUI 静态资源 - -[English](2026-09-23-rebuild-corrupted-assets.md) - -- 变更 ID:`studio-webui-build-integrity` -- 创建/修订日期:2026-09-23 -- 状态:implemented -- 组件规格:无变更;HTTP、UI 和部署契约均保持不变。 - -## 背景与证据 - -云上 Studio 根路径返回 HTTP 200,但页面空白。引用的 `assets/app/index-Ceg2hYta.js` 以 `<<<<<<<< HEAD` 开头,`node --check` 报 `Unexpected token '<<'`。仓库扫描发现 114 个打包 JS 文件含冲突标记。原有静态资源检查仅验证引用,不检查冲突标记。源码构建还遇到企业微信回调参数隐式 `any` 的 TypeScript 错误,以及本地缺少已声明的 SDK 依赖;后者属于安装问题,不是应用变更。 - -## 目标、非目标、场景与需求 - -- 目标:提供可解析、内部引用一致的 WebUI 资源,使 Studio 登录页正常渲染。 -- 非目标:改变登录、UserPool、Runtime、MPA、调度器或企业微信授权行为。 -- `FR-1`:给定干净的源码构建,打包后 HTML/CSS/JS 文件不包含行首 Git 冲突标记。 -- `FR-2`:未登录浏览器访问云上 Studio 的 `/` 时,显示 Identity 登录界面而不是空白页。 -- 边界:未登录访问 `/web/runtime-config` 会跳转登录;这不能证明登录后的工作台。 - -## 设计与契约影响 - -使用现有 `frontend/scripts/build.mjs` 重建 `veadk/webui/`,不手工编辑压缩文件。在 `frontend/scripts/verifyBuiltAssets.mjs` 增加冲突标记断言。为现有企业微信 SDK 回调结果补充窄类型以通过 TypeScript 构建;运行逻辑不变。不新增依赖、API、状态、持久化、权限、凭据处理或兼容性契约,因此无需修改组件规格或用户文档。逐个手改 114 个文件不可重复,故不采用。生成的 JavaScript 包含模板字符串空白,即使语法和打包检查通过,`git diff --check` 仍可能报告生成行。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | 证据(2026-09-23,`15ac109`) | -| --- | --- | --- | --- | -| `FR-1` | `T-1` 重建打包资源;`T-2` 增加冲突标记检查 | `AC-1` 所有打包 JS 可解析且资源引用有效 | `npm run build`:pass;`npm run test:webui-assets`:pass,101 个文件/236 处引用;对所有打包 JS 执行 `node --check`:pass | -| `FR-2` | `T-3` 重新部署现有 Studio 应用 | `AC-2` 浏览器显示 Identity 登录标题与按钮 | 现有 VeFaaS 应用更新:pass;真实浏览器 DOM 和截图:pass | - -`npm test`:pass(1,253 个 Node 测试、25 个 Vitest 测试)。针对暂存文件的 pre-commit:pass。全文件 pre-commit:fail,因为 Ruff 格式化了三个无关的既有 Python 测试文件;这些附带修改已撤销。`git diff --check`:fail,原因是生成的 `website-integration.js` 模板字符串中的空白。登录后的工作台及 MPA 创建:not_run,不属于本次空白页修复。云凭据与原始日志未写入文档。 - -## 风险、评审与交付 - -生成资源的哈希和多处文件名须整体更新;只发布 `index.html` 会破坏引用。此次复用了原应用和 Identity 配置,调度器部署也已完成。源码、静态资源及浏览器检查已覆盖报告的问题。没有可用的 `review-spec` skill;直接评审确认无契约变更或安全权限扩展。本次操作性修复沿用正在进行的云上部署请求;实施前未单独记录设计审批。 diff --git a/prd-spec/bugfixes/tracing-memory/2026-10-13-bounded-cache.md b/prd-spec/bugfixes/tracing-memory/2026-10-13-bounded-cache.md deleted file mode 100644 index bac593fb4..000000000 --- a/prd-spec/bugfixes/tracing-memory/2026-10-13-bounded-cache.md +++ /dev/null @@ -1,73 +0,0 @@ -# Bound the local tracing cache - -[中文版](2026-10-13-bounded-cache.zh.md) - -## Metadata - -- Change ID: `tracing-memory` -- Created/revised: 2026-10-13 -- Status: implemented -- Component: [tracing cache](../../../specs/tracing-cache/README.md) - -## Background, goals, and scope - -Published versions 1.0.9 and 1.1.14 append every sampled span to a process-lifetime list. Each tracer also registers another collector with the global provider. The session index grows independently, and `clear()` does not clear it. Large responses attached to repeatedly polled requests amplify retention. Source inspection confirms the defect; the proportion of any production process's RSS attributable to it is not yet measured. - -The fix bounds local retention without disabling cloud exporters or changing span annotation, sampling, or instrumentation suppression. It preserves recent session dumps and the latest trace ID. It does not change AgentKit SDK request payloads, frontend behavior, production configuration, package versions, or deployment. - -## Scenarios and requirements - -- FR-1: Given sustained exports, retain at most 1,024 spans and 16,777,216 estimated bytes by default; evict oldest spans until both limits hold. Reject a single oversized span from the local cache without discarding earlier valid entries or altering cloud export. -- FR-2: Given local caching disabled, retain no spans or session index, but keep the annotation processor, latest trace ID, and cloud pipeline operational. -- FR-3: Given eviction, clear, or shutdown, do not retain stale session mappings. Retrieval returns a consistent snapshot of retained spans; concurrent export/clear/read must be safe. -- FR-4: Given several tracers using one provider, register exactly one shared local collector at the front of its processor pipeline. Each provider has its own cache, with configuration captured when its first collector is created. -- FR-5: Publish validated configuration and document that local dumps contain only retained history; cloud retention is owned by the cloud exporter/service. - -## Design and contract impact - -Use a lock-protected FIFO deque with parallel per-span size accounting. Estimate the known span fields, attribute values, resource, events, links, and instrumentation metadata using Python object sizes without serializing or copying large payloads. This estimate is a cache budget, not an exact process RSS limit; count bounds metadata overhead as well. Skip oversized entries locally. Derive the session-to-trace index from retained `call_llm` spans on read, avoiding an independently growing index. A trace whose session-bearing span was evicted may no longer appear in a local session dump. - -Expose `inmemory_enabled`, `inmemory_max_spans`, and `inmemory_max_bytes` in `OpenTelemetryConfig`, with corresponding `OBSERVABILITY_OPENTELEMETRY_INMEMORY_*` variables. Defaults are true, 1024, and 16777216; nonnegative limits are required and either zero disables storage. Configuration follows existing startup settings/config.yaml handling; changes require a new provider/process. - -The provider owns a shared exporter, initialized under a registration lock. Keep the existing annotation processor even when retention is disabled. Export, read, clear, and shutdown share a cache lock; shutdown is terminal. `force_flush()` does not clear history. No background threads, timers, new dependencies, new network calls, or permission changes are introduced. - -Alternatives rejected: periodic clear leaves peak retention dependent on traffic and loses whole batches; count-only bounds do not adequately handle large responses; removing the processor would remove annotation and suppression behavior. Public tracer/exporter constructor signatures remain compatible. Local history changes from unlimited to bounded by design; no cloud span is truncated or filtered by this cache. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| FR-1, FR-3 | T-1: add failing regression tests, then implement bounded cache in `inmemory_exporter.py` | AC-1: count/byte eviction, oversized payload, session cleanup, shutdown, and concurrent operations pass | `uv run --no-sync --extra dev pytest tests/test_inmemory_exporter.py -q` | -| FR-2, FR-4 | T-2: share the collector in `opentelemetry_tracer.py` | AC-2: multiple tracers share one collector; cloud spans, annotations, and trace IDs survive disabled/limited retention | `uv run --no-sync --extra dev pytest tests/test_tracing.py -q` | -| FR-5 | T-3: add config validation and bilingual user docs/spec | AC-3: defaults, overrides, zero, and invalid values are verified | `uv run --no-sync --extra dev pytest tests/config/test_tracing_config.py -q` | -| All | T-4: inspect diff, run focused tracing regressions and Ruff, reconcile both languages | AC-4: checks pass and limitations are recorded | See delivery record | - -## Risks and review - -Finite local history may truncate long session dumps. A shared cache covers the entire provider, so all tracers compete for the same budget and observe the same recent history. Python object estimates exclude allocator fragmentation and memory owned elsewhere; this does not promise an RSS ceiling. No production memory plateau or cloud behavior may be claimed from local tests alone. - -Design reviewed directly: ownership, errors, concurrency, compatibility, configuration, bilingual equivalence, and verification coverage have no open blockers. User approval: request on 2026-10-13 to fix the confirmed leak following the proposed bounded-cache approach. Complex tests and cloud operations require follow-up; neither is needed to implement the scoped regression fix. - -## Delivery record - -- Baseline: `b8038fb16`; verification date: 2026-10-13 -- Scope: bounded exporter, provider registration, configuration, regression tests, and bilingual cache documentation; T-1 through T-4 and AC-1 through AC-4 complete -- Regression before implementation: fail as expected, 20 failed and 3 passed for cache/config tests; includes old-span retention and stale session state -- Core checks: pass, 46 tests via `uv run --no-sync --extra dev pytest tests/test_inmemory_exporter.py tests/config/test_tracing_config.py tests/test_tracing.py -q` -- Focused compatibility checks: pass, 75 tests total including the core checks, content collection, LLM attribute extraction, runtime tracing, and Studio telemetry; one pre-existing invalid-escape warning -- Existing exporter fixtures also attempted metric delivery to `localhost:8000` during shutdown and logged `UNAVAILABLE`; the test process exited 0. New cache/cloud integration tests use a local in-memory stand-in and no cloud service -- Ruff format/check and `git diff --check`: pass for changed Python files and the patch -- Documentation: pass, both tracing MDX pages compile with `@mdx-js/mdx`; bilingual contract/configuration consistency and relative document links reviewed -- Pre-submission checks: pass, reran 75 focused tests after synchronizing `origin/main`; `uv run --no-sync --extra dev pre-commit run --all-files` passed, including Ruff and both secret scanners -- Full regression and documentation site build/type checks: not_run; this pass is limited to focused checks under the requested workflow, with no frontend application change -- Production/cloud validation: not_run, no production access or changes requested -- UI validation: not_applicable, no frontend behavior changes -- Commit and PR: authorized; package publication and production deployment: not_run, outside this scope - -## CI follow-up - -The initial Python 3.12 CI run had 7023 passing tests and two failures caused by runtime tests slicing the private `_spans` list. Update those two consumers to read session snapshots through `get_finished_spans(session_id)`, avoiding assumptions about the queue type or offsets that change after eviction. Preserve the existing span and session assertions. This follow-up changes no production code or approved contract - -- Failed cases: reproduced both failures locally before the update; both pass afterward -- Extended check: `uv run --no-sync --extra dev pytest tests/runtime/differential tests/runtime/piagent/test_piagent_turn_contract.py -q` finishes with 84 passed, 2 xfailed, and 1 failed after the update -- Remaining failure: `test_model_fallbacks_warn_for_external_runtime` sees empty captured logs; it also fails on the original PR commit `9671acd7`, where the same check finishes with 82 passed, 2 xfailed, and 3 failed. It is outside the cache fix and remains unchanged diff --git a/prd-spec/bugfixes/tracing-memory/2026-10-13-bounded-cache.zh.md b/prd-spec/bugfixes/tracing-memory/2026-10-13-bounded-cache.zh.md deleted file mode 100644 index 08bbac50c..000000000 --- a/prd-spec/bugfixes/tracing-memory/2026-10-13-bounded-cache.zh.md +++ /dev/null @@ -1,73 +0,0 @@ -# 限制本地追踪缓存 - -[English](2026-10-13-bounded-cache.md) - -## 元数据 - -- 变更 ID:`tracing-memory` -- 创建及修订日期:2026-10-13 -- 状态:implemented -- 组件:[追踪缓存](../../../specs/tracing-cache/README.zh.md) - -## 背景、目标与范围 - -已发布的 1.0.9 和 1.1.14 会将每个被采样的 span 追加到进程生命周期内的列表中,每个追踪器还会向全局 provider 注册另一个收集器。会话索引独立增长,`clear()` 不会清理它。反复轮询请求携带的大响应会放大内存占用。源码检查已确认缺陷,尚未测量它在具体生产进程 RSS 中的占比 - -本次修复限制本地保留量,保留云端 exporter、span 字段补充、采样及埋点抑制行为,继续支持近期会话导出和最新 trace ID。不修改 AgentKit SDK 请求内容、前端行为、生产配置、包版本或部署 - -## 场景与需求 - -- FR-1:持续导出时,默认最多保留 1,024 个 span 和 16,777,216 估算字节,淘汰最旧 span 直到同时满足两个限制。单个 span 超限时仅跳过本地缓存,不丢弃之前的有效记录,也不改变云端上报 -- FR-2:关闭本地缓存时,不保留 span 或会话索引,仍保留字段补充处理器、最新 trace ID 和云端链路 -- FR-3:淘汰、清空或关闭后,不保留过期会话映射。查询返回保留数据的一致快照,并发导出、清理及读取安全 -- FR-4:同一 provider 的多个追踪器只在处理流水线首部注册一个共享收集器。不同 provider 拥有各自的缓存,首次创建收集器时确定配置 -- FR-5:提供经过校验的配置,明确本地导出只包含仍被保留的历史,云端保留策略由云端 exporter 或服务负责 - -## 设计与契约影响 - -使用加锁的 FIFO 双端队列和对应的逐 span 大小记录。通过 Python 对象大小估算已知 span 字段、属性值、资源、事件、链接和埋点元数据,不序列化或复制大块内容。该估算是缓存预算,并非进程 RSS 精确限制;数量限制同时约束元数据开销。超大记录仅跳过本地保存。读取时从仍被保留的 `call_llm` span 生成会话到 trace 的索引,避免独立索引持续增长。若包含会话信息的 span 已被淘汰,对应 trace 可能不再出现在本地会话导出中 - -在 `OpenTelemetryConfig` 中提供 `inmemory_enabled`、`inmemory_max_spans`、`inmemory_max_bytes`,对应 `OBSERVABILITY_OPENTELEMETRY_INMEMORY_*` 环境变量。默认值为 true、1024、16777216;限制值必须非负,任一限制为零即关闭保存。沿用已有启动 settings/config.yaml 处理方式,修改后需新建 provider 或重启进程 - -provider 拥有共享 exporter,通过注册锁初始化。即使关闭保存,仍保留现有字段补充处理器。导出、读取、清空和关闭共用缓存锁,关闭为终态。`force_flush()` 不清理历史。不引入后台线程、定时任务、新依赖、额外网络调用或权限变更 - -未选择的方案:定时清空的峰值仍由流量决定且会整批丢失历史;只限制数量不足以控制大响应;删除处理器会破坏字段补充与埋点抑制。公开 tracer/exporter 构造签名兼容。按设计将本地历史从无限保留改为有限保留,缓存不会截断或过滤任何云端 span - -## 任务与验收 - -| 需求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| FR-1、FR-3 | T-1:先增加失败回归,再在 `inmemory_exporter.py` 实现有界缓存 | AC-1:数量及字节淘汰、超大内容、会话清理、关闭和并发操作通过 | `uv run --no-sync --extra dev pytest tests/test_inmemory_exporter.py -q` | -| FR-2、FR-4 | T-2:在 `opentelemetry_tracer.py` 共享收集器 | AC-2:多追踪器共用一个收集器,关闭或限制缓存仍保留云端 span、字段和 trace ID | `uv run --no-sync --extra dev pytest tests/test_tracing.py -q` | -| FR-5 | T-3:增加配置校验和双语用户文档及规范 | AC-3:验证默认值、覆盖、零值和非法值 | `uv run --no-sync --extra dev pytest tests/config/test_tracing_config.py -q` | -| 全部 | T-4:检查差异,运行针对性追踪回归及 Ruff,同步双语记录 | AC-4:检查通过,限制被如实记录 | 见交付记录 | - -## 风险与审查 - -有限本地历史可能导致长会话导出不完整。共享缓存覆盖整个 provider,各追踪器竞争同一预算并观察同一近期历史。Python 对象估算不包含分配器碎片和其他组件拥有的内存,不承诺 RSS 上限。不能用本地测试声称生产内存已稳定或云端行为已验证 - -已直接审查归属、错误、并发、兼容性、配置、双语一致性及验证覆盖,无待解决阻塞项。用户于 2026-10-13 在提出有界缓存方向后要求修复已确认的泄漏。复杂测试及云端操作需后续确认,本次范围内的回归修复不依赖二者 - -## 交付记录 - -- 基线:`b8038fb16`;验证日期:2026-10-13 -- 范围:有界 exporter、provider 注册、配置、回归测试和双语缓存文档;T-1 至 T-4、AC-1 至 AC-4 已完成 -- 实现前回归:按预期 fail,缓存及配置测试 20 项失败、3 项通过,包含旧 span 引用保留和会话状态残留 -- 核心检查:pass,`uv run --no-sync --extra dev pytest tests/test_inmemory_exporter.py tests/config/test_tracing_config.py tests/test_tracing.py -q` 共 46 项通过 -- 针对性兼容检查:pass,连同核心检查共 75 项通过,覆盖内容采集、LLM 属性提取、运行时 tracing 和 Studio 观测;出现一条原有非法转义警告 -- 原有 exporter fixture 在退出时尝试向 `localhost:8000` 发送指标并记录 `UNAVAILABLE`,测试进程退出码为 0;新增缓存及云端导出集成测试使用本地内存替身,不访问云服务 -- Ruff 格式及检查、`git diff --check`:pass,覆盖修改的 Python 文件和差异 -- 文档:pass,两份 tracing MDX 均通过 `@mdx-js/mdx` 编译,已审查双语契约及配置一致性和相对文档链接 -- 提交前检查:pass,同步 `origin/main` 后再次运行 75 项针对性测试,并通过 `uv run --no-sync --extra dev pre-commit run --all-files`,包含 Ruff 和两项密钥扫描 -- 全量回归、文档站构建及类型检查:not_run,按当前流程仅执行针对性检查,未修改前端应用 -- 生产及云端验证:not_run,未要求访问或修改生产环境 -- UI 验证:not_applicable,无前端行为变更 -- 提交及 PR:已获授权;软件包发布及生产部署:not_run,不在本次范围内 - -## CI 后续修正 - -Python 3.12 的首次 CI 有 7023 项通过、2 项失败;失败来自两个运行时测试对私有 `_spans` 列表的切片假设。将这两个使用方改为通过 `get_finished_spans(session_id)` 读取会话快照,避免依赖队列类型或会因淘汰而变化的列表偏移,保留原有 span 及会话断言。本次修正不修改生产代码或已批准契约 - -- 失败用例:修正前本地复现 2 项失败,修正后 2 项通过 -- 扩展检查:`uv run --no-sync --extra dev pytest tests/runtime/differential tests/runtime/piagent/test_piagent_turn_contract.py -q` 在修正后为 84 passed、2 xfailed、1 failed -- 剩余失败:`test_model_fallbacks_warn_for_external_runtime` 的日志捕获断言为空;原始 PR 提交 `9671acd7` 下同样复现,该基线为 82 passed、2 xfailed、3 failed。此项不在本次缓存修复范围,未修改 diff --git a/prd-spec/bugfixes/yaml-secret-guard/2026-09-23-yaml-secret-guard.md b/prd-spec/bugfixes/yaml-secret-guard/2026-09-23-yaml-secret-guard.md deleted file mode 100644 index 590541c25..000000000 --- a/prd-spec/bugfixes/yaml-secret-guard/2026-09-23-yaml-secret-guard.md +++ /dev/null @@ -1,72 +0,0 @@ -# YAML Secret Guard Hotfix - -[中文版](2026-09-23-yaml-secret-guard.zh.md) - -## Metadata - -- Change ID: `yaml-secret-guard` -- Created: 2026-09-23 -- Revised: 2026-09-23 -- Status: `implemented` -- Related component specs: no component contract change - -## Background and Evidence - -`examples/piagent_with_mcp/piagent-mcp-agentkit.yaml` was tracked as an AgentKit launch configuration. The file contained account-specific runtime metadata and a concrete `runtime_apikey` value. Filled launch YAML files are local runtime artifacts and must not be committed. - -The repository already ignores local AgentKit launch configs through `agentkit.yaml` and `agentkit*.yaml`, but that does not protect files that were previously tracked. The existing secret scanners also did not provide a deterministic repo-local YAML rule that rejects concrete values assigned to sensitive YAML keys while allowing documented placeholders. - -## Goals - -- Remove the tracked PiAgent AgentKit launch YAML. -- Keep the example runnable by documenting how to create a local ignored config. -- Add a repo-local pre-commit YAML scanner that rejects concrete secret values. -- Allow safe placeholders and GitHub Actions secret references. - -## Non-Goals - -- No history rewrite or credential rotation. -- No change to AgentKit runtime behavior, MPA creation behavior, or public CLI contracts. -- No broad rewrite of unrelated YAML examples. - -## Requirements - -| ID | Requirement | -| --- | --- | -| `FR-1` | Filled AgentKit launch YAML with runtime IDs, endpoints, and API keys must not be tracked. | -| `FR-2` | YAML files with concrete values for sensitive keys such as `password`, `secret`, `token`, `api-key`, `access-key`, `credential`, or `runtime_apikey` must fail pre-commit. | -| `FR-3` | `.example.yaml` placeholders, environment references, and GitHub Actions `${{ secrets.* }}` references must remain allowed. | -| `FR-4` | The scanner must report only path, line, and key name, never the secret value. | - -## Design - -Delete `examples/piagent_with_mcp/piagent-mcp-agentkit.yaml`. Update the example README so operators generate a local config with `veadk agentkit config`, optionally rename it, and keep it out of Git. Existing `.gitignore` already ignores `agentkit.yaml` and `agentkit*.yaml`. - -Add `scripts/scan_yaml_secrets.py` and run it from a local pre-commit hook. The script scans tracked YAML by default, or hook-provided YAML files when pre-commit passes paths. It uses key-name matching plus value-shape checks so placeholders stay valid while long concrete credential values fail. - -## Affected Files - -- `.pre-commit-config.yaml` -- `scripts/scan_yaml_secrets.py` -- `examples/piagent_with_mcp/README.md` -- `examples/piagent_with_mcp/piagent-mcp-agentkit.yaml` -- `prd-spec/bugfixes/yaml-secret-guard/2026-09-23-yaml-secret-guard.md` -- `prd-spec/bugfixes/yaml-secret-guard/2026-09-23-yaml-secret-guard.zh.md` - -## Verification and Acceptance - -| Requirement | Acceptance criterion | Command | Result | -| --- | --- | --- | --- | -| `FR-1` | The tracked launch YAML is deleted and README points to local ignored config generation. | `git status --short` and README review | `pass`: YAML is deleted; README now instructs generating a local config and keeping it out of Git. | -| `FR-2`, `FR-4` | A temporary YAML with `runtime_apikey` and a concrete value fails without printing the value. | `python3 scripts/scan_yaml_secrets.py ` | `pass`: scanner exits 1, reports path/line/key only, and the concrete value is absent from output. | -| `FR-3` | Placeholder and environment-reference YAML passes. | `python3 scripts/scan_yaml_secrets.py ` | `pass`: placeholder and `${MPA_MODEL_API_KEY}` references pass. | -| all | Pre-commit runs the new YAML hook. | `uv run --extra dev pre-commit run --all-files` | `pass`: Ruff, Ruff format, gitleaks, and YAML secret scan pass. | - -## Risks and Open Questions - -- This removes the tracked leaked-looking YAML from current Git state but does not rewrite repository history. -- Credential rotation, if the value was real, is an operator follow-up outside this code hotfix. - -## Review and Delivery Record - -- 2026-09-23: Implemented scoped deletion and YAML guard. Targeted unit tests, full YAML scan, and pre-commit passed. diff --git a/prd-spec/bugfixes/yaml-secret-guard/2026-09-23-yaml-secret-guard.zh.md b/prd-spec/bugfixes/yaml-secret-guard/2026-09-23-yaml-secret-guard.zh.md deleted file mode 100644 index c6454ca7b..000000000 --- a/prd-spec/bugfixes/yaml-secret-guard/2026-09-23-yaml-secret-guard.zh.md +++ /dev/null @@ -1,72 +0,0 @@ -# YAML 密钥门禁 Hotfix - -[English](2026-09-23-yaml-secret-guard.md) - -## 元数据 - -- Change ID:`yaml-secret-guard` -- 创建日期:2026-09-23 -- 修订日期:2026-09-23 -- 状态:`implemented` -- 相关组件规范:不变更组件契约 - -## 背景与证据 - -`examples/piagent_with_mcp/piagent-mcp-agentkit.yaml` 被 Git 跟踪为 AgentKit 启动配置。该文件包含账号相关 Runtime 元数据和具体的 `runtime_apikey` 值。填写后的启动 YAML 属于本地运行产物,不应提交到仓库。 - -仓库已经通过 `agentkit.yaml` 和 `agentkit*.yaml` 忽略本地 AgentKit 启动配置,但这无法保护此前已经被跟踪的文件。现有密钥扫描也没有提供稳定的仓库本地 YAML 规则,来拒绝敏感 YAML key 的具体值并允许文档占位符。 - -## 目标 - -- 删除被跟踪的 PiAgent AgentKit 启动 YAML。 -- 更新示例文档,说明如何创建本地忽略配置,保持示例可运行。 -- 增加仓库本地 pre-commit YAML 扫描,拒绝具体密钥值。 -- 允许安全占位符和 GitHub Actions secret 引用。 - -## 非目标 - -- 不重写 Git 历史,也不执行凭据轮换。 -- 不改变 AgentKit Runtime 行为、MPA 创建行为或公开 CLI 契约。 -- 不大范围重写无关 YAML 示例。 - -## 需求 - -| ID | 需求 | -| --- | --- | -| `FR-1` | 含 Runtime ID、Endpoint 和 API key 的已填写 AgentKit 启动 YAML 不得被跟踪。 | -| `FR-2` | YAML 文件中 `password`、`secret`、`token`、`api-key`、`access-key`、`credential` 或 `runtime_apikey` 等敏感 key 对应具体值时,pre-commit 必须失败。 | -| `FR-3` | `.example.yaml` 占位符、环境变量引用和 GitHub Actions `${{ secrets.* }}` 引用必须继续允许。 | -| `FR-4` | 扫描器只报告路径、行号和 key 名,不得打印密钥值。 | - -## 方案 - -删除 `examples/piagent_with_mcp/piagent-mcp-agentkit.yaml`。更新示例 README,要求操作者使用 `veadk agentkit config` 生成本地配置,可按需重命名,并保持在 Git 之外。现有 `.gitignore` 已忽略 `agentkit.yaml` 和 `agentkit*.yaml`。 - -新增 `scripts/scan_yaml_secrets.py` 并通过本地 pre-commit hook 执行。脚本默认扫描 Git 跟踪的 YAML;在 pre-commit 传入路径时扫描对应 YAML。规则结合 key 名和 value 形态判断,使占位符保持合法,长的具体凭据值失败。 - -## 涉及文件 - -- `.pre-commit-config.yaml` -- `scripts/scan_yaml_secrets.py` -- `examples/piagent_with_mcp/README.md` -- `examples/piagent_with_mcp/piagent-mcp-agentkit.yaml` -- `prd-spec/bugfixes/yaml-secret-guard/2026-09-23-yaml-secret-guard.md` -- `prd-spec/bugfixes/yaml-secret-guard/2026-09-23-yaml-secret-guard.zh.md` - -## 验证与验收 - -| 需求 | 验收标准 | 命令 | 结果 | -| --- | --- | --- | --- | -| `FR-1` | 已删除被跟踪启动 YAML,README 指向本地忽略配置生成流程。 | `git status --short` 和 README review | `pass`:YAML 已删除;README 现在说明生成本地配置并保持在 Git 之外。 | -| `FR-2`, `FR-4` | 临时 YAML 中 `runtime_apikey` 配具体值时失败,且不打印具体值。 | `python3 scripts/scan_yaml_secrets.py ` | `pass`:扫描器退出 1,只报告路径/行号/key,输出不包含具体值。 | -| `FR-3` | 占位符和环境变量引用 YAML 通过。 | `python3 scripts/scan_yaml_secrets.py ` | `pass`:占位符和 `${MPA_MODEL_API_KEY}` 引用通过。 | -| 全部 | pre-commit 执行新 YAML hook。 | `uv run --extra dev pre-commit run --all-files` | `pass`:Ruff、Ruff format、gitleaks 和 YAML secret scan 通过。 | - -## 风险与待确认问题 - -- 本修复从当前 Git 状态删除疑似泄露 YAML,但不重写仓库历史。 -- 如果该值是真实凭据,凭据轮换属于运维后续动作,不在本代码 hotfix 范围内。 - -## 评审与交付记录 - -- 2026-09-23:已实现范围内删除和 YAML 门禁。定向单测、全仓 YAML 扫描和 pre-commit 通过。 diff --git a/prd-spec/features/bot-binding-types/2026-10-16-bot-binding-types.md b/prd-spec/features/bot-binding-types/2026-10-16-bot-binding-types.md deleted file mode 100644 index 6d666a652..000000000 --- a/prd-spec/features/bot-binding-types/2026-10-16-bot-binding-types.md +++ /dev/null @@ -1,49 +0,0 @@ -# Studio Bot binding types - -[中文](2026-10-16-bot-binding-types.zh.md) - -Date: 2026-10-16. Base: origin/main edae5b58. Status: implemented. - -## Scope and approval - -The user requested a branch from main and a PR for the existing private/shared binding selector. This explicitly authorizes implementation, testing, commit, push and PR creation. A subsequent instruction fixes the private description to “私聊可召回同 Bot 的历史,包括群聊。” No cloud deployment or changes to Runtime recall/authentication rules are included. - -## Background and design - -Runtime already advertises botBindingTypes and accepts bindingType, but Studio's ProviderChannel sends only credentials/domain. Reuse Radio and the configuration-method fieldset styling. Render a separate “Bot type” group before Quick/Manual setup, only for Feishu (including the Lark brand) and only for known advertised shared/private values. Prefer shared by default; a Runtime advertising private only selects private. Missing, empty or unknown-only capabilities retain the exact legacy payload. Both binding modes and QR retry forward the selection. Preserve domain=lark. - -Selection stays in component memory, is locked during requests and after a durable QR binding exists (including failure/retry), and resets when switching Runtime/provider or cancelling/reopening Add bot. Switching setup methods cancels the old QR UI and allows a new choice; it does not mutate the existing server binding. Management of an existing account exposes no editable type. All existing cancellation, late-result guards, administrator authorization and error presentation remain in place. No owner identity is invented or sent by the browser. - -Private copy: “Private chats can recall this Bot’s history, including group chats.” Shared copy: “Sessions are isolated from each other.” These are short UI descriptions, not a replacement for Runtime authorization policy. - -## Requirements and acceptance - -| Requirement/task | Acceptance | Verification | -| --- | --- | --- | -| FR-1 / T-1 | Advertised shared/private options, shared default, requested concise descriptions, no control for unsupported providers/versions | RuntimeChannels component cases and browser fixture | -| FR-2 / T-2 | QR/manual/retry payloads carry selected type; Lark keeps domain; old payload unchanged | Component request assertions | -| FR-3 / T-3 | Busy/pending/retry locks; reset on target/add lifecycle; errors and cancellation cannot leak selection | Component cases and keyboard/narrow browser checks | -| FR-4 / T-4 | Bilingual contract/user docs, rebuilt release assets, >95% changed executable line coverage | Build, i18n, asset checks, coverage diff and pre-commit | - -Affected files: frontend/src/ui/RuntimeChannels.tsx, frontend/src/adk/client.ts, both ui.json locales, frontend/tests/runtimeChannels.test.tsx, frontend/README.md, specs/mpa-channels/README.md and README.zh.md, veadk/webui generated assets. No Python, storage, dependencies or permission policy changes. - -## Review and risks - -Design reviewed directly because review-spec is unavailable: additive capability/field, legacy and Lark compatibility, request ownership, retry type stability, no secret storage, bilingual equivalence, testability and S1–S8 reuse checked; no blocker. frontend-design read; ui-ux-pro-max unavailable in repository/installed skill catalog; use frontend/SPEC.md and foundation instead. Existing Radio controls supply focus/keyboard semantics; no new styles/icons. The user's current request supplies scope approval; no additional approval is needed. - -A compatible Runtime remains required; the server owns immutable type validation and recall rules. Component/browser fixtures do not prove real Feishu binding. Verification below. - -## Verification and delivery (2026-10-16) - -Scope: origin/main edae5b58 plus this change. Implementation review: no blockers; checked capability absence/unknown values, private-only support, immutable retry selection, Lark domain, target lifecycle, no owner spoofing and no type editing on managed accounts. No Python contract or permission implementation changes. - -- pass — TDD: added 15 component cases. Before implementation, 9 new assertions failed and 78 tests passed; after implementation all 87 passed. The initial feature gap is resolved; no additional defect reported. -- pass — `vitest run --environment jsdom tests/runtimeChannels.test.tsx tests/feishuAccounts.test.ts tests/mpaChannelsAutomation.test.tsx src/adk/channels.test.ts --coverage`: 116 passed. An initial invocation omitted jsdom and failed the existing client test with `window is not defined`; rerun with the required browser environment passed. Fixed an incomplete new retry fixture to return a terminal binding rather than an object without polling metadata. -- pass — `npm --prefix frontend test`: 1,389 Node tests and 118 Vitest tests. -- pass — `npm --prefix frontend run build`: TypeScript and both production bundles; generated WebUI assets included. Existing large-chunk/dependency warnings remain. -- pass — `npm --prefix frontend run check:i18n`: 2 locales / 21 namespaces consistent. `npm --prefix frontend run test:webui-assets`: 113 files / 350 internal references. -- pass — changed executable lines: 6/6 (100%) against edae5b58; client.ts changes are type-only. RuntimeChannels overall line coverage is 94.71%, distinct from incremental coverage. Coverage source: V8/Istanbul JSON + Cobertura line hits intersected with git diff; utree's parser returned zero targets for this TSX file and is not used as passing evidence. Raw logs and reproducible diff counts: `/tmp/studio-bot-binding-types/`. -- pass — `uv run --extra dev pytest tests/test_mpa_channel_proxy_policy.py -q`: 6 passed. -- pass — real in-app browser with isolated mocked Runtime: empty list, shared default, keyboard ArrowRight selects private, exact private description, QR and retry private payloads, locked radio state, manual payload, legacy absence, capability error/retry and loading state. Existing Radio/fieldset styling reused; S1–S8 inspected. At the actual 708px width no horizontal overflow; 390px override did not take effect, so 390px is not verified. Temporary fixture files/browser removed; no real credentials or cloud writes. -- not_run — real Feishu registration, cloud deployment, actual IME input and full Python regression (no Python production change). Existing component IME/duplicate-submit regression remains covered. This PR does not assert live authorization or recall behavior. -- pass — fetched origin/main and rebased with autostash (base remained edae5b58), restored all changes; `uv run --extra dev pre-commit run --all-files` passed Ruff, hardcoded-secret, dependency-policy and YAML-secret checks; final focused rerun: 116 passed. `git diff --check` passed. diff --git a/prd-spec/features/bot-binding-types/2026-10-16-bot-binding-types.zh.md b/prd-spec/features/bot-binding-types/2026-10-16-bot-binding-types.zh.md deleted file mode 100644 index 9a8f6d85b..000000000 --- a/prd-spec/features/bot-binding-types/2026-10-16-bot-binding-types.zh.md +++ /dev/null @@ -1,49 +0,0 @@ -# Studio Bot 绑定类型 - -[English](2026-10-16-bot-binding-types.md) - -日期:2026-10-16。基线:origin/main edae5b58。状态:implemented。 - -## 范围与授权 - -用户要求从主分支新建分支,迁入已有的私有/共享绑定选择并提交 PR,已明确授权实现、测试、提交、推送和创建 PR。后续要求私有说明固定为“私聊可召回同 Bot 的历史,包括群聊。”不部署云端,不修改 Runtime 召回或鉴权规则。 - -## 背景与设计 - -Runtime 已声明 botBindingTypes 并接受 bindingType,Studio ProviderChannel 当前仅发送凭据或 domain。复用 Radio 和配置方式 fieldset 样式,在极速/手动配置之前单独显示“Bot 类型”。仅飞书(含 Lark 品牌)且 Runtime 声明已知 shared/private 值时显示。优先默认共享;只声明 private 时选择私有。能力缺失、空数组或仅未知值时保持旧请求体。扫码、手动和扫码重试均传递所选类型,保留 domain=lark。 - -选择仅存在组件内存中,请求中和存在持久扫码绑定后(包括失败重试)锁定。切换 Runtime/渠道、取消后重新添加时重置。切换配置方式会取消旧扫码界面并允许重新选择,不修改旧服务端绑定。已有账号管理不显示可编辑类型。保留现有取消、迟到响应保护、管理员鉴权和错误提示;浏览器不构造或发送 owner 身份。 - -私有文案:“私聊可召回同 Bot 的历史,包括群聊。”共享文案:“各会话相互隔离。”英文语义一致。简短 UI 描述不替代 Runtime 授权规则。 - -## 需求与验收 - -| 需求/任务 | 验收条件 | 验证 | -| --- | --- | --- | -| FR-1 / T-1 | 按能力显示共享/私有,默认共享,使用指定简短说明;旧版本和其他渠道不显示 | RuntimeChannels 组件用例及浏览器夹具 | -| FR-2 / T-2 | 扫码/手动/重试携带类型;Lark 保留 domain;旧请求不变 | 组件请求断言 | -| FR-3 / T-3 | 请求/扫码等待/重试锁定;目标和添加生命周期重置;失败和取消不串选项 | 组件用例、键盘和窄屏浏览器检查 | -| FR-4 / T-4 | 双语契约和用户文档、重建产物、增量可执行行覆盖率 >95% | 构建、国际化、产物、增量覆盖率及 pre-commit | - -涉及文件:frontend/src/ui/RuntimeChannels.tsx、frontend/src/adk/client.ts、双语 ui.json、frontend/tests/runtimeChannels.test.tsx、frontend/README.md、specs/mpa-channels/README.md 及 README.zh.md、veadk/webui 构建产物。不改 Python、存储、依赖和权限规则。 - -## 审查与风险 - -review-spec 不可用,已直接审查设计:增量能力字段、旧版和 Lark 兼容、请求归属、重试类型稳定性、不持久化密钥、双语等价、可测试性及 S1–S8 复用,无阻塞。已读 frontend-design;仓库及已安装技能目录未提供 ui-ux-pro-max,按 frontend/SPEC.md 和 foundation 执行。已有 Radio 提供焦点/键盘语义,不新增样式或图标。当前用户请求已授权此范围,无需重复确认。 - -仍需兼容 Runtime;类型不可变校验和召回规则由服务端负责。组件/浏览器夹具不代表真实飞书绑定验证。验证见下文。 - -## 验证与交付(2026-10-16) - -范围:origin/main edae5b58 加本次改动。实现审查无阻塞:已检查能力缺失/未知值、仅私有选项、重试类型不可变、Lark domain、目标生命周期、不伪造 owner、已有账号不编辑类型。没有 Python 契约或权限实现改动。 - -- pass — TDD:新增 15 个组件用例。实现前 9 个新断言失败、78 个通过;实现后 87 个全部通过。原功能缺口已补齐,未发现额外缺陷。 -- pass — `vitest run --environment jsdom tests/runtimeChannels.test.tsx tests/feishuAccounts.test.ts tests/mpaChannelsAutomation.test.tsx src/adk/channels.test.ts --coverage`:116 个通过。首次命令遗漏 jsdom,已有客户端测试报 `window is not defined`;补齐浏览器环境重跑通过。新重试夹具补齐为终态绑定,避免缺少轮询元数据。 -- pass — `npm --prefix frontend test`:1,389 个 Node 测试和 118 个 Vitest 测试。 -- pass — `npm --prefix frontend run build`:TypeScript 和两套生产 bundle;已包含匹配的 WebUI 产物。保留已有大包/依赖警告。 -- pass — `npm --prefix frontend run check:i18n`:2 个语言、21 个命名空间一致。`npm --prefix frontend run test:webui-assets`:113 个文件、350 个内部引用。 -- pass — 相对 edae5b58 的增量可执行行 6/6(100%),client.ts 仅类型声明。RuntimeChannels 全文件行覆盖率为 94.71%,与增量覆盖率分开统计。依据 V8/Istanbul JSON 和 Cobertura 行命中与 git diff 交集;utree 对此 TSX 返回零目标,不作为通过证据。原始日志和可复核增量计数:`/tmp/studio-bot-binding-types/`。 -- pass — `uv run --extra dev pytest tests/test_mpa_channel_proxy_policy.py -q`:6 个通过。 -- pass — 真实应用内浏览器、隔离模拟 Runtime:空列表、默认共享、ArrowRight 切换私有、指定私有说明、扫码与重试私有请求、单选框锁定、手动请求、旧能力缺失、能力错误/重试、加载态。复用 Radio/fieldset,检查 S1–S8。实际 708px 宽度无横向溢出;390px 覆盖未生效,未将 390px 记为通过。临时夹具及浏览器已清理,未使用真实凭据或执行云写入。 -- not_run — 真实飞书注册、云部署、真实 IME 输入、全量 Python 回归(未改 Python 生产代码)。已有组件 IME/重复提交回归仍被覆盖。本 PR 不声称已验证线上授权或召回。 -- pass — fetch origin/main 后使用 autostash rebase(基线仍为 edae5b58),改动完整恢复;`uv run --extra dev pre-commit run --all-files` 的 Ruff、硬编码密钥、依赖规则、YAML 密钥检查全部通过;最终定向复测 116 个通过。`git diff --check` 通过。 diff --git a/prd-spec/features/channel-auth-methods/2026-09-15-channel-auth-methods.md b/prd-spec/features/channel-auth-methods/2026-09-15-channel-auth-methods.md deleted file mode 100644 index 478a71724..000000000 --- a/prd-spec/features/channel-auth-methods/2026-09-15-channel-auth-methods.md +++ /dev/null @@ -1,34 +0,0 @@ -# Channel authorization methods - -[中文](2026-09-15-channel-auth-methods.zh.md) - -Change ID: channel-auth-methods; date: 2026-09-15; status: implemented. - -## Background and scope -The user explicitly requested the existing Dashboard WeCom SDK authorization flow in Studio and manual DingTalk Client ID / Client Secret binding. Studio currently supports only manual WeCom and QR DingTalk. Preserve both existing flows and Feishu. No gateway, identity, or network changes. - -## Requirements and design -- FR-1: WeCom opens the official SDK 0.1.0 popup synchronously on a user click (`source=mpa-agent`, debug off), validates returned Bot ID/Secret and submits to authenticated `/wecom/bindings`. Never show or persist SDK credentials. Use one disposable SDK instance per attempt, cancel on navigation/user cancellation, ignore late results, bound waiting to five minutes, and show sanitized blocked/cancelled/timeout/malformed-result errors. -- FR-2: DingTalk retains QR authorization and adds masked Client ID / Client Secret manual submission to `/dingtalk/bindings/manual`. Runtime advertises `credentialBindingChannels`; old images show an upgrade hint for the new manual entry. Reject blanks, require admin, sanitize validation/provider failures, return 409 for ambiguous registration, and reuse existing encrypted Gateway registration logic. -- FR-3: Reuse current channel form/Button styles, Chinese/English localization, IME guards, duplicate-submit lock, disabled/loading/uncertain states. Clear secrets after success; do not overwrite another Runtime after navigation. - -## Contract and tasks -Update CON-11 in [MPA channels](../../../specs/mpa-channels/README.md). T-1: failing frontend and backend contract tests. T-2: SDK adapter, UI, Runtime endpoint/capability, dependency/lock and translations. T-3: targeted tests, frontend test/build, browser checks and Runtime release. T-4: reconcile bilingual docs and verification record. No generated Agent code changes; built Studio assets must match source. - -## Acceptance and verification -AC-1/FR-1: popup success submits exact normalized credentials once; cancellation/unmount/timeout/blocked popup/missing fields do not register. AC-2/FR-2: manual DingTalk posts correct payload, preserves QR, rejects unauthenticated/invalid requests, redacts secrets, handles uncertain registration. AC-3/FR-3: frontend tests/build and narrow/keyboard browser checks pass; deployed Runtime exposes manual endpoint and existing Feishu binding is retained. Tests use fake credentials; real user authorization and actual new-channel messaging require user interaction and are reported separately. - -## Review and authorization -Scope is authorized by the user's explicit instruction to use the Dashboard implementation and add manual DingTalk binding. Reviewed directly (review-spec skill unavailable): requirements, API ownership, auth, error redaction, async lifetime, compatibility and paired language agree. No blocking design findings. Referenced frontend-design/ui-ux-pro-max skill files are absent locally; implementation follows frontend/SPEC.md and existing channel controls. SDK tarball is 15,336 bytes, zero runtime dependencies; pin 0.1.0. Source inspection confirms origin checking; do not claim state validation beyond implemented SDK behavior. Risks: browser popup blocking, provider availability, SDK compatibility, ambiguous Gateway outcome. Never automatically re-register an ambiguous bot. - -## Delivery record -Checks: not_run before implementation. User-driven QR authorization and live provider messages: not_run without user action. - -### Verification completed (2026-09-15) -- pass: backend channel tests, 74 cases including new manual authentication, validation, redaction and uncertain-outcome checks. -- pass: `vitest run tests/runtimeChannels.test.tsx`, 24 cases; `npm test`, 1206 cases; `npm run check:i18n`; `npm run build`; `npm run test:webui-assets` (104 files, 248 references). -- pass: isolated real-browser component checks for WeCom SDK waiting/cancellation, manual DingTalk failure/retry/success, keyboard provider switching and 390px viewport. Provider credentials in those checks were fake and never sent to cloud. Browser inspection caught the SDK browser UMD/default-export mismatch; explicit official ESM import and a type declaration fixed it. -- pass: scoped secret-pattern and whitespace checks. Ruff reports 17 pre-existing B008/RUF100 findings; the previous image produces the same findings. Pyright reports one pre-existing dynamic ChannelService import issue; reproduced against the previous image source. These full-file checks are `fail` on the baseline too, not claimed passing. -- pass: new image `studio-channels-20260915-v2` released to the existing Runtime, version 4 Ready. Network/env/auth unchanged; capabilities 1.2 advertises manual DingTalk. Both credential endpoints reject empty payloads with 422. Existing Feishu BOUND and existing DingTalk configuration retained; diagnostics include previous delivered observations. -- pass: local Studio restarted with its original working directory/arguments (without browser auto-open); served index and JS match the final build. -- not_run: new real WeCom authorization completion, new manual DingTalk credentials and subsequent real messages; these require user account interaction. The already bound DingTalk bot was not replaced. diff --git a/prd-spec/features/channel-auth-methods/2026-09-15-channel-auth-methods.zh.md b/prd-spec/features/channel-auth-methods/2026-09-15-channel-auth-methods.zh.md deleted file mode 100644 index 8c89fc6c0..000000000 --- a/prd-spec/features/channel-auth-methods/2026-09-15-channel-auth-methods.zh.md +++ /dev/null @@ -1,34 +0,0 @@ -# 渠道授权方式 - -[English](2026-09-15-channel-auth-methods.md) - -变更 ID:channel-auth-methods;日期:2026-09-15;状态:implemented。 - -## 背景与范围 -用户明确要求将 Dashboard 已有的企微 SDK 扫码授权接入 Studio,并增加钉钉 Client ID / Client Secret 手动绑定。Studio 当前仅支持企微手动填写和钉钉扫码。保留两种已有流程及飞书,不改变网关、身份或网络配置。 - -## 需求与设计 -- FR-1:点击时同步调用官方 SDK 0.1.0 打开企微弹窗(source=mpa-agent,关闭 debug),校验返回的 Bot ID/Secret,提交到鉴权后的 `/wecom/bindings`。不展示或持久化 SDK 凭据。每次操作使用独立 SDK 实例,切页或用户取消时销毁,忽略迟到结果,等待上限五分钟;弹窗被拦截、取消、超时及缺字段使用脱敏提示。 -- FR-2:钉钉保留扫码,新增 Client ID / Client Secret 手动填写(密钥遮罩),提交 `/dingtalk/bindings/manual`。Runtime 通过 `credentialBindingChannels` 声明能力;旧镜像的新手动入口显示升级提示。拒绝空白字段、要求管理员权限、脱敏校验及上游错误;注册结果不明确时返回 409,复用现有加密存储及 Gateway 注册逻辑。 -- FR-3:复用渠道表单、Button 样式、中英文翻译、IME 防误提交、重复提交锁及禁用/加载/不确定状态。成功后清空密钥;切换 Runtime 后旧操作不得写入新 Runtime。 - -## 契约与任务 -更新 [MPA 渠道](../../../specs/mpa-channels/README.zh.md) CON-11。T-1:先补失败的前后端契约测试。T-2:SDK 适配、界面、Runtime 接口及能力声明、依赖锁定和翻译。T-3:定向测试、前端测试及构建、浏览器检查和 Runtime 发布。T-4:同步双语文档及验证结果。不改变生成的 Agent 代码;Studio 构建产物必须与源码匹配。 - -## 验收与验证 -AC-1/FR-1:授权成功时准确规范化凭据并仅提交一次;取消、卸载、超时、弹窗被拦截、缺字段时不注册。AC-2/FR-2:手动钉钉请求准确,保留扫码,拒绝未鉴权及非法请求,脱敏密钥并处理注册不确定性。AC-3/FR-3:前端测试/构建、窄窗口/键盘浏览器检查通过;线上 Runtime 提供手动接口并保留原飞书绑定。测试仅用假凭据;真实授权和新渠道聊天需要用户操作,单独报告。 - -## 审查与授权 -用户明确要求参考 Dashboard 实现并增加钉钉手动绑定,已授权此范围。review-spec 技能不可用,直接审查需求、接口归属、鉴权、错误脱敏、异步生命周期、兼容性及双语一致性,无设计阻塞项。本地缺少规范引用的 frontend-design/ui-ux-pro-max 技能文件,按 frontend/SPEC.md 和现有渠道组件实现。SDK 包大小 15,336 字节,无运行时依赖,固定版本 0.1.0。源码确认校验 Origin,不宣称超出实际实现的 state 校验。风险:浏览器拦截弹窗、平台可用性、SDK 兼容性及 Gateway 注册结果不确定;不自动重复注册结果不明确的机器人。 - -## 交付记录 -实现前检查状态:not_run。用户扫码授权和真实渠道消息:需要用户操作,not_run。 - -### 验证完成(2026-09-15) -- pass:后端渠道测试 74 项,包含新增手动接口鉴权、校验、脱敏和注册结果不确定性检查。 -- pass:`vitest run tests/runtimeChannels.test.tsx` 24 项;`npm test` 1206 项;`npm run check:i18n`;`npm run build`;`npm run test:webui-assets`(104 个文件、248 处引用)。 -- pass:隔离真实浏览器组件验证企微 SDK 等待/取消、钉钉手动失败/重试/成功、键盘切换及 390px 窗口。测试凭据为假数据,不发送到云端。浏览器发现 SDK browser UMD 默认导出差异,已通过官方 ESM 显式导入及类型声明修复。 -- pass:限定范围密钥模式扫描及空白检查。Ruff 报告 17 条已有 B008/RUF100 问题,上一版镜像源码产生相同问题;Pyright 报告一条已有动态 ChannelService 导入问题,旧版源码也可复现。整文件检查在基线上同样为 fail,不宣称通过。 -- pass:新镜像 `studio-channels-20260915-v2` 发布到原 Runtime,版本 4 Ready。网络、环境变量和鉴权不变;能力版本 1.2 声明钉钉手动绑定;两个凭据接口空参数均返回 422。原飞书 BOUND 和已有钉钉配置保留,诊断包含此前已投递记录。 -- pass:本地 Studio 按原工作目录和启动参数重启(去掉自动打开浏览器),实际首页及 JS 与最终构建匹配。 -- not_run:新企微扫码授权完成、新钉钉手动凭据及后续真实消息,需要用户账号操作;未替换已有钉钉机器人。 diff --git a/prd-spec/features/channel-configuration-modes/2026-09-15-channel-configuration-modes.md b/prd-spec/features/channel-configuration-modes/2026-09-15-channel-configuration-modes.md deleted file mode 100644 index eecb2754e..000000000 --- a/prd-spec/features/channel-configuration-modes/2026-09-15-channel-configuration-modes.md +++ /dev/null @@ -1,73 +0,0 @@ -# Channel configuration modes - -[中文](2026-09-15-channel-configuration-modes.zh.md) - -Change ID: channel-configuration-modes. Date: 2026-09-15. Status: implemented. -Contract: [MPA channels](../../../specs/mpa-channels/README.md), CON-15. -Predecessor: [authorization methods](../channel-auth-methods/2026-09-15-channel-auth-methods.md). - -## Background and evidence - -`frontend/src/ui/RuntimeChannels.tsx` presents QR authorization and manual credentials together for unconfigured WeCom/DingTalk channels, and no manual Feishu form. The user requests manual Feishu App ID binding and two exclusive methods named 极速配置 and 手动配置, defaulting to 极速配置. Local MPA source at `app/api/v1/channels.py` already exposes POST `/api/v1/channels/feishu/bindings/manual` and advertises Feishu in `credentialBindingChannels`; `app/services/channel/studio.py` owns credential registration. Availability in the deployed Runtime remains unverified. - -## Goals and non-goals - -Unify configuration method selection for all three providers and connect Feishu credentials to the existing Runtime API. Keep the current Runtime selection, diagnostics, unbinding, and Feishu group-permission ownership. No changes to the automation catalog, standalone Feishu Runtime creation wizard, cloud deployment, SDK dependencies, database schema, or external MPA source are planned. - -## Requirements and design - -- FR-1: Each provider displays mutually exclusive Quick setup / Manual setup options (Chinese: 极速配置 / 手动配置). Initial mount and provider/Runtime changes select Quick setup; selection is not persisted. Only the selected method's content is shown. Choosing Quick setup does not itself launch authorization. -- FR-2: Quick setup preserves the current Feishu/DingTalk QR and WeCom SDK authorization flow, including existing bot replacement guidance and QR regeneration. Manual setup requires Feishu App ID / App Secret, DingTalk Client ID / Client Secret, or WeCom Bot ID / Secret. Existing configured bots may be replaced through either method; retain the current binding until server-confirmed success and show replacement guidance. -- FR-3: Manual Feishu sends `{appId, appSecret}` to POST `/api/v1/channels/feishu/bindings/manual` through `channelRequest`; retain existing DingTalk and WeCom payloads/routes. Require Runtime capability before enabling new manual methods. Unsupported manual setup explains that an upgrade is required while Quick setup remains available when supported. Empty values cannot submit; secret inputs are masked and never persisted, logged, or shown in errors. Success clears credentials and refreshes diagnostics; recoverable failures preserve the active form. -- FR-4: Switching methods cancels QR polling and disposable SDK authorization, discards temporary pairing UI and credentials, and ignores late results. Do not permit switching while a registration mutation is in flight; retain the SDK cancel action. A 409 uncertain outcome blocks new registrations across both methods until reconciled. UI cancellation does not claim to revoke server-side binding. Existing navigation cleanup, bounded SDK timeout, IME guards and duplicate-submit protection remain enforced. -- FR-5: Reuse Studio controls/styles and add keyboard-accessible method selection, visible selection/focus, and narrow-window layout. Keep Chinese/English labels and documentation aligned. Update generated web assets after verification. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | -| --- | --- | --- | -| FR-1, FR-2 | T-1: add failing cases in `frontend/tests/runtimeChannels.test.tsx` | AC-1: all providers default to Quick setup; Manual setup alone shows the appropriate form; configured replacement preserves the old binding on failure | -| FR-3 | T-2: wire Feishu fields and payload in `RuntimeChannels.tsx`, update capabilities mocks | AC-2: exact provider-specific request, capability gating, blank validation, masked secrets, sanitized errors, success clearing | -| FR-4 | T-3: implement method state and cleanup | AC-3: switching/navigation stop frontend work, late responses cannot update the current view, uncertain outcomes and duplicate/IME submissions remain guarded | -| FR-5 | T-4: update `RuntimeChannels.css`, both `ui.json` locales, `frontend/README.md`, specs and build artifacts | AC-4: bilingual labels, keyboard and narrow browser checks, source and generated assets agree | - -## Verification - -Run targeted tests first: `cd frontend && ./node_modules/.bin/vitest run tests/runtimeChannels.test.tsx src/adk/channels.test.ts --environment jsdom --maxWorkers 1`. Then run `npm --prefix frontend test`, `npm --prefix frontend run check:i18n`, `npm --prefix frontend run build`, and `npm --prefix frontend run test:webui-assets`. Real-browser checks use simulated APIs and fake credentials for default/selection, all provider fields, configured replacement, loading/error/retry, cancellation, keyboard/IME and narrow layouts. Live provider authorization and messaging require separate authorization and an isolated target; they are not established by local tests. Before the already requested commit, rerun repository pre-commit and affected tests and record unresolved full-regression failures honestly. - -## Review, risks and delivery record - -Direct review performed because review-spec is unavailable: API ownership, payload, permissions, capability compatibility, secret lifecycle, asynchronous cleanup, replacement behavior, testability and bilingual equivalence reviewed. CON-15 records the changed contract. The user explicitly approved this proposal with “实施” on 2026-09-15. Implementation proceeds within the reviewed scope. - -Risks: deployed Runtime may lack manual Feishu despite local support; cancelled browser work does not revoke server-side operations; ambiguous registration must not trigger automatic replacement retries. Configuration success is distinct from observed message delivery. - -2026-09-15, this proposal only: runtime/browser checks `not_run` because implementation awaits approval. Earlier commit checks apply only to the prior diff: frontend 1208 passed, channel tests 32 passed with jsdom, targeted Python 15 passed, build passed, secret hook passed; Python full regression failed with 6 failures and 2 collection errors (4532 passed). Those failures still require investigation before the pending commit; no commit has been created. - - -## Implementation and verification (2026-09-15) - -Scope: current channel changes based on `f1aa2d75` on `feat/my-feature`, including the previously pending MPA channel diff. T-1 through T-4 and AC-1 through AC-4 are complete locally. The frontend uses the existing Radio component and provider-specific Runtime routes; no MPA backend source or deployed Runtime was changed in this iteration. Existing Python changes received Ruff formatting only. Browser review also found and fixed Feishu group-form overflow at 390px using grid sizing and the Select component's public style prop. - -- pass: test-first run produced 17 failures and 28 passes before implementation. Final `./frontend/node_modules/.bin/vitest run --root frontend tests/runtimeChannels.test.tsx src/adk/channels.test.ts --environment jsdom --maxWorkers 1`: 47 passed. Covers all providers, defaults, exclusive content, credentials, old capability responses, replacement failures, late QR/SDK responses, uncertain QR/manual registration, IME, duplicate requests and navigation. -- pass: `npm --prefix frontend test`: 1208 passed; `npm --prefix frontend run check:i18n`: 2 locales / 21 namespaces consistent; `npm --prefix frontend run build`: both outputs and TypeScript passed, existing bundle-size warnings only. Packaged asset verification is recorded below after the final build. -- pass: real browser with simulated Runtime API and SDK: all three manual binding paths, Feishu error/retry/success and secret clearing, WeCom authorization waiting/method cancellation, native radio keyboard navigation, default reset on provider selection, old-image manual upgrade with quick setup retained, and 390×844 layout. Feishu group inputs and Select no longer overflow; measured preview scroll width equals its client width (379px). Temporary preview files, server and tabs removed; viewport reset. -- pass: targeted Python command `uv run --extra dev pytest tests/test_mpa_channel_proxy_policy.py tests/integrations/test_mpa_provision_env.py -q`: 15 passed. Additional optional-dependency reproduction using sandbox extra and temporary `llama-index-core`, `llama-index-embeddings-openai-like`, `llama-index-llms-openai-like`: 73 passed for `tests/cloud/test_harness_app_contract.py`, `tests/runtime/test_self_host_sandbox_agent.py`, `tests/runtime/test_self_host_sandbox_client.py`. -- fail: full regression with the same temporary dependencies, `uv run --extra dev --extra sandbox --with llama-index-core --with llama-index-embeddings-openai-like --with llama-index-llms-openai-like pytest -n 2 -m "not codex_smoke and not piagent_smoke"`: 4546 passed, 3 failed, 7 skipped, 2 xfailed. Failures: `test_harness_app_exposes_agent_info`, `test_harness_session_create_accepts_id_and_get_agent_config`, `test_run_wraps_child_with_sidecar_environment` (model/port values differ). Their source/tests are unchanged. Repeating the two affected files in isolation passed all 26 tests; suite-order/environment interaction remains unresolved. Two workers were used to limit local resource use. Skips are not represented as verified behavior. -- fail (baseline): `uv run --extra dev --with pyright pyright veadk/cli/cli_frontend.py veadk/integrations/mpa/channel_proxy.py veadk/integrations/mpa/mpa_provision.py tests/test_mpa_channel_proxy_policy.py`: 33 diagnostics, all in cli_frontend.py. Running Pyright on the HEAD copy produces the identical 33 diagnostic messages. No new diagnostics in the channel/provisioning/test files; unrelated typing cleanup is outside scope. -- reviewed false positive: extra Gitleaks default-rule scan of changed files, including tests and generated assets, flags the minified editor expression the Lexical selection anchor/focus key comparison. The identical expression exists in the HEAD MarkdownPromptEditor asset; it is code, not credential material. No scanner configuration or hook was weakened. Required repository pre-commit results are recorded below. -- pass: paired language identifiers, relative links and scoped diff whitespace. Generated third-party JavaScript retains its existing whitespace conventions and is not hand-edited. -- not_run: live provider authorization, replacement and messaging; not authorized for this iteration. Local source support does not prove deployed Runtime capability. Harness/sidecar coverage gate is not_applicable to the configuration-mode change because sidecar contracts are unchanged. - -Review: authentication remains in the existing BFF; passwords never enter browser persistence; method switches release owned resources and preserve uncertain outcomes; both design languages and CON-15 match the implementation. Prior user authorization to commit remains active. No push or deployment is authorized. - -### Final commit checks (2026-09-15) - -- pass: fetched origin and rebased onto `origin/feat/my-feature`; HEAD was already current and the content was preserved. -- pass: after synchronization, `uv run --extra dev pre-commit run --all-files` (Ruff check, Ruff format, Gitleaks), 15 targeted Python tests, 47 channel/client tests, locale parity, and packaged asset verification (104 files / 248 references). -- pass: all 20 changed PRD/spec files have language counterparts, matching requirement identifiers and valid relative links. Temporary debug files remain local and are excluded from the commit. - -### Visual correction (2026-09-15, explicitly requested) - -The user requests the same Bind with QR code label for all providers, visible configuration-method selection, and a lighter group-scope field. The main Studio entry does not load component-preview theme tokens; the previous isolated preview added those tokens and missed this context. Use the Radio public label slot for page-owned selected/unselected text colors, and the Select public style prop for the existing page input background, foreground and border. Do not override component internals or inject a global theme. Reuse `channels.bind`. Direct review passed: this is a visual correction to FR-5 / AC-4 authorized by the current user request; runtime, API, binding lifecycle and public component interfaces remain unchanged, so CON-15 needs no update. Verification must use the production entry CSS without extra component-theme imports. Committing/deploying is outside this new request. - -Visual correction verification (2026-09-15, current diff on `f5f5ab1e`): pass, test-first run had 7 failures; after correction, 49 channel tests passed. `npm --prefix frontend test`: 1208 passed; `npm --prefix frontend run check:i18n`, production build and built-asset verification passed (104 files / 248 references). Real browser without component theme tokens measured selected text `rgb(9, 9, 11)` / 600 and unselected text `rgb(113, 113, 122)` / 400; the Select has a white background, dark text and light border. Click/keyboard selection works, WeCom shows the shared QR binding label, and the 390px viewport has no horizontal overflow. Temporary preview removed. Python, live messaging and full pre-commit: not_run, because this iteration changes only visuals/copy and no commit was requested. diff --git a/prd-spec/features/channel-configuration-modes/2026-09-15-channel-configuration-modes.zh.md b/prd-spec/features/channel-configuration-modes/2026-09-15-channel-configuration-modes.zh.md deleted file mode 100644 index 2e8665f53..000000000 --- a/prd-spec/features/channel-configuration-modes/2026-09-15-channel-configuration-modes.zh.md +++ /dev/null @@ -1,73 +0,0 @@ -# 消息渠道配置方式 - -[English](2026-09-15-channel-configuration-modes.md) - -变更 ID:channel-configuration-modes。日期:2026-09-15。状态:implemented。 -契约:[MPA 渠道](../../../specs/mpa-channels/README.zh.md),CON-15。 -前序设计:[授权方式](../channel-auth-methods/2026-09-15-channel-auth-methods.zh.md)。 - -## 背景与证据 - -`frontend/src/ui/RuntimeChannels.tsx` 在未配置企微、钉钉时同时展示扫码授权和手动凭据,飞书没有手动表单。用户要求飞书支持 App ID 手动绑定,并以“极速配置”和“手动配置”作为互斥方式,默认“极速配置”。本地 MPA 的 `app/api/v1/channels.py` 已提供 POST `/api/v1/channels/feishu/bindings/manual`,且 `credentialBindingChannels` 包含飞书;`app/services/channel/studio.py` 负责凭据注册。已部署 Runtime 是否具备该能力尚未验证。 - -## 目标与非目标 - -统一三个渠道的配置方式选择,将飞书凭据接入既有 Runtime API。保留当前 Runtime 选择、诊断、解绑与飞书群权限职责。本次不修改自动化目录、独立的飞书 Runtime 创建向导、云部署、SDK 依赖、数据库 schema 或外部 MPA 源码。 - -## 需求与设计 - -- FR-1:每个渠道展示互斥的“极速配置 / 手动配置”(英文 Quick setup / Manual setup)。初次挂载、切换渠道或 Runtime 时默认极速配置,不持久化选择。仅展示所选方式的内容;选择极速配置本身不自动发起授权。 -- FR-2:极速配置沿用飞书、钉钉二维码及企微 SDK 授权,保留已有机器人替换说明和重新生成二维码。手动配置分别填写飞书 App ID / App Secret、钉钉 Client ID / Client Secret、企微 Bot ID / Secret。已配置机器人也可通过两种方式替换;服务端确认成功之前保留现有绑定,并展示替换说明。 -- FR-3:飞书手动配置通过 `channelRequest` 将 `{appId, appSecret}` 提交到 POST `/api/v1/channels/feishu/bindings/manual`;钉钉和企微沿用现有载荷与路由。新增手动方式须由 Runtime 能力声明启用;不支持时提示升级,已支持的极速配置仍可用。空值禁止提交;密钥输入遮罩,不持久化、不写日志、不进入错误展示。成功清空凭据并刷新诊断,可恢复错误保留当前表单。 -- FR-4:切换方式取消二维码轮询和可释放的 SDK 授权,丢弃临时配对界面与凭据,并忽略迟到结果。注册写请求进行中禁止切换,保留 SDK 取消授权操作。409 结果不确定时,两种方式均禁止再次注册,直到结果得到核实。取消界面操作不代表撤销服务端绑定。保留导航清理、SDK 超时上限、IME 防护和防重复提交。 -- FR-5:复用 Studio 控件和样式,方式选择支持键盘、清晰的选中与焦点状态、窄窗口布局。中英文文案与文档同步;验证后更新生成的网页资源。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | -| --- | --- | --- | -| FR-1、FR-2 | T-1:在 `frontend/tests/runtimeChannels.test.tsx` 先补失败用例 | AC-1:三渠道默认极速配置,仅手动方式显示对应表单;已配置机器人的替换失败保留原绑定 | -| FR-3 | T-2:在 `RuntimeChannels.tsx` 接入飞书字段和载荷,更新能力模拟 | AC-2:各渠道请求准确,能力限制、空值校验、密钥遮罩、错误脱敏和成功清理有效 | -| FR-4 | T-3:实现方式状态与清理 | AC-3:切换与导航终止前端工作,迟到响应不更新当前视图,结果不确定、防重复及 IME 防护有效 | -| FR-5 | T-4:更新 `RuntimeChannels.css`、两种语言的 `ui.json`、`frontend/README.md`、契约及构建产物 | AC-4:双语文案、键盘与窄窗口浏览器检查通过,源码和生成产物一致 | - -## 验证 - -先执行目标测试:`cd frontend && ./node_modules/.bin/vitest run tests/runtimeChannels.test.tsx src/adk/channels.test.ts --environment jsdom --maxWorkers 1`。再执行 `npm --prefix frontend test`、`npm --prefix frontend run check:i18n`、`npm --prefix frontend run build`、`npm --prefix frontend run test:webui-assets`。真实浏览器使用模拟 API 和假凭据验证默认方式与切换、各渠道字段、已有绑定替换、加载/错误/重试、取消、键盘/IME 和窄窗口。真实渠道授权与消息需要另行授权和隔离目标,本地测试不证明这些行为。在用户已要求的提交前,重跑仓库 pre-commit 与受影响测试,如实记录未解决的全量回归失败。 - -## 评审、风险与交付记录 - -因 review-spec 不可用,直接评审了 API 职责、载荷、权限、能力兼容、凭据生命周期、异步清理、替换行为、可测试性和双语等价。CON-15 记录变化契约。用户于 2026-09-15 明确回复“实施”,批准本方案。实现按已评审范围开展。 - -风险:本地已有接口不代表已部署 Runtime 支持;取消浏览器工作不代表撤销服务端操作;注册结果不确定时不可自动重试替换。配置成功与实际观测到消息投递仍是不同状态。 - -2026-09-15,仅本方案:运行时/浏览器检查为 `not_run`,因为实现等待批准。此前提交检查仅针对旧 diff:前端 1208 项通过,jsdom 下渠道测试 32 项通过,目标 Python 15 项通过,构建通过,密钥扫描 hook 通过;Python 全量回归有 6 项失败、2 项收集错误(4532 项通过)。待提交前仍需调查这些失败;尚未创建提交。 - - -## 实现与验证(2026-09-15) - -范围:`feat/my-feature` 分支基于 `f1aa2d75` 的当前渠道改动,含此前待提交的 MPA 渠道 diff。T-1 至 T-4、AC-1 至 AC-4 已在本地完成。前端复用 Radio 组件及各渠道的 Runtime 路由;本轮未修改 MPA 后端源码或已部署 Runtime。既有 Python 改动仅由 Ruff 调整格式。浏览器评审还发现并修复了飞书群表单在 390px 下的溢出,使用网格尺寸约束与 Select 公开 style 属性。 - -- pass:实现前测试为 17 项失败、28 项通过。最终 `./frontend/node_modules/.bin/vitest run --root frontend tests/runtimeChannels.test.tsx src/adk/channels.test.ts --environment jsdom --maxWorkers 1`:47 项通过。覆盖所有渠道、默认方式、互斥内容、凭据、旧能力响应、替换失败、迟到的二维码/SDK 响应、扫码/手动注册结果不确定、IME、防重复与导航。 -- pass:`npm --prefix frontend test`:1208 项通过;`npm --prefix frontend run check:i18n`:2 种语言 / 21 个命名空间一致;`npm --prefix frontend run build`:两份输出和 TypeScript 均通过,仅有既有包体积警告。最终构建后的资源验证记录在下方。 -- pass:真实浏览器配合模拟 Runtime API 和 SDK:三个渠道手动绑定、飞书错误/重试/成功与密钥清空、企微授权等待及切换取消、原生单选键盘切换、切换渠道默认重置、旧镜像手动升级提示且保留极速配置、390×844 布局。飞书群输入与 Select 已无溢出,预览滚动宽度等于可用宽度(379px)。已移除临时预览文件、服务与标签页,并恢复视口。 -- pass:目标 Python 命令 `uv run --extra dev pytest tests/test_mpa_channel_proxy_policy.py tests/integrations/test_mpa_provision_env.py -q`:15 项通过。补充 sandbox extra 及临时 `llama-index-core`、`llama-index-embeddings-openai-like`、`llama-index-llms-openai-like` 后,`tests/cloud/test_harness_app_contract.py`、`tests/runtime/test_self_host_sandbox_agent.py`、`tests/runtime/test_self_host_sandbox_client.py` 共 73 项通过。 -- fail:同一临时依赖配置下全量回归 `uv run --extra dev --extra sandbox --with llama-index-core --with llama-index-embeddings-openai-like --with llama-index-llms-openai-like pytest -n 2 -m "not codex_smoke and not piagent_smoke"`:4546 项通过、3 项失败、7 项跳过、2 项 xfailed。失败为 `test_harness_app_exposes_agent_info`、`test_harness_session_create_accepts_id_and_get_agent_config`、`test_run_wraps_child_with_sidecar_environment`(模型/端口值不符),相关源码和测试未修改。单独重跑两个受影响测试文件共 26 项全部通过;全套执行顺序/环境交互问题尚未解决。使用两个 worker 限制本机资源占用;跳过项不算行为已验证。 -- fail(基线):`uv run --extra dev --with pyright pyright veadk/cli/cli_frontend.py veadk/integrations/mpa/channel_proxy.py veadk/integrations/mpa/mpa_provision.py tests/test_mpa_channel_proxy_policy.py`:33 项诊断,均位于 cli_frontend.py。对 HEAD 副本执行 Pyright 得到完全一致的 33 条诊断消息。渠道/部署配置/测试文件无新增诊断;无关类型清理不在范围内。 -- 已复核误报:额外 Gitleaks 默认规则扫描覆盖变更文件、测试和生成资源,命中编辑器压缩表达式 Lexical 选区 anchor/focus 的节点 key 比较。HEAD 的 MarkdownPromptEditor 资源包含完全相同表达式,它是代码而非凭据。未放宽扫描配置或 hook;必需的仓库 pre-commit 结果记录在下方。 -- pass:双语标识、相对链接和范围内 diff 空白检查。第三方生成 JavaScript 保留既有空白格式,不手工修改。 -- not_run:真实渠道授权、替换和消息收发,本轮未授权。本地源码支持不证明已部署 Runtime 的能力。配置方式变更不涉及 sidecar 契约,因此 Harness/sidecar 覆盖率门槛为 not_applicable。 - -评审:鉴权仍由既有 BFF 负责;密钥不进入浏览器持久化;切换方式释放所属资源并保留结果不确定状态;双语设计和 CON-15 与实现一致。此前用户提交授权仍有效,未授权推送或部署。 - -### 最终提交检查(2026-09-15) - -- pass:获取 origin 并 rebase 到 `origin/feat/my-feature`;HEAD 已是最新,改动内容完整保留。 -- pass:同步后 `uv run --extra dev pre-commit run --all-files`(Ruff 检查、Ruff 格式、Gitleaks)、15 项目标 Python 测试、47 项渠道/客户端测试、语言键一致性和资源验证(104 个文件 / 248 处引用)全部通过。 -- pass:20 个变更 PRD/spec 文件均有语言配对,需求标识一致,相对链接有效。临时调试文件保留本地,不纳入提交。 - -### 视觉修正(2026-09-15,用户明确要求) - -用户要求三个渠道统一使用“扫码绑定”、配置方式选中态清晰、群会话策略框改色。正式 Studio 入口未加载组件预览的主题变量,上轮隔离预览额外引入主题导致漏检。采用 Radio 公开 label 插槽展示页面所属的选中/未选中颜色,Select 公开 style 属性使用当前页面输入框的背景、文字和边框色,不覆盖组件内部选择器、不全局注入主题。统一复用 `channels.bind` 文案。直接评审通过:这是 FR-5 / AC-4 的视觉修正,用户当前请求授权实施;运行时、API、绑定生命周期和组件公开接口无变化,CON-15 无需修改。验证使用与正式入口相同的 CSS,不额外注入组件主题。提交/部署不属于本次新增请求。 - -视觉修正验证(2026-09-15,基于 `f5f5ab1e` 的本轮 diff):pass,测试先出现 7 项失败,修正后渠道测试 49 项通过;`npm --prefix frontend test` 1208 项通过;`npm --prefix frontend run check:i18n`、生产构建和构建资源验证通过(104 文件 / 248 引用)。真实浏览器未加载组件主题变量,实测选中项 `rgb(9, 9, 11)` / 600,未选中项 `rgb(113, 113, 122)` / 400;选择框白色背景、深色文字、浅色边框。点击和键盘切换有效,企微显示“扫码绑定”,390px 视口无横向溢出。临时预览已清理。Python、真实消息收发及全量 pre-commit:not_run,本轮仅视觉和文案修正且未请求提交。 diff --git a/prd-spec/features/channel-qr-regeneration/2026-09-15-channel-qr-regeneration.md b/prd-spec/features/channel-qr-regeneration/2026-09-15-channel-qr-regeneration.md deleted file mode 100644 index 7b802a54c..000000000 --- a/prd-spec/features/channel-qr-regeneration/2026-09-15-channel-qr-regeneration.md +++ /dev/null @@ -1,36 +0,0 @@ -# Regenerate channel pairing QR code - -[中文](2026-09-15-channel-qr-regeneration.zh.md) - -Date: 2026-09-15. Status: implemented. User approval: request to add “重新生成二维码” and “消息渠道扫码配对” with Feishu replacement guidance. Contract: [MPA channels](../../../specs/mpa-channels/README.md). - -## Background and scope -RuntimeChannels currently disables QR generation when a bot is already configured. Existing MPA upsert registers and saves the replacement before removing the old bot. Preserve the latest WeCom SDK and DingTalk manual-binding changes. No backend/API, provider authorization, deployment or credential-storage changes. - -## Requirements and design -FR-1: A configured Feishu or DingTalk bot exposes an enabled Regenerate QR code action. Loading, an active pairing, uncertain registration and unavailable configuration still disable it. -FR-2: Clicking opens the inline Message channel QR pairing panel with loading/status/QR. For Feishu, explain that a successful scan and binding replaces the connected bot and the authorization flow can bind an existing bot or create a new one. Selection occurs in the provider flow, not a new Studio credential form. -FR-3: Creating the QR performs POST to the existing binding endpoint without DELETE. Keep the current bot summary until BOUND refreshes diagnostics. Request failure and navigation cancellation do not unbind the current bot. - -## Tasks and acceptance -T-1/AC-1: regression tests for configured regeneration, replacement explanation, duplicate prevention and failed request preserving the old connection (FR-1/2/3). -T-2/AC-2: update RuntimeChannels.tsx and both ui.json languages, preserve provider isolation and Feishu-only group controls. -T-3/AC-3: run component Vitest, frontend regression, production build, assets and browser checks; record limitations. - -## Review and risk -Direct design review (review-spec unavailable): additive UI contract, unchanged authentication and persistence; provider selection UI belongs to authorization service. No unresolved blocker. User request authorizes this implementation. Live replacement is not tested without an isolated Runtime/account. Pending QR replacement remains disabled to avoid concurrent active pairing sessions. - -## Verification -2026-09-15, current uncommitted frontend channel diff: pass. The three new regression cases failed before implementation and all 23 component tests passed afterward (`cd frontend && ./node_modules/.bin/vitest run tests/runtimeChannels.test.tsx --maxWorkers 1`). `npm --prefix frontend test`: 1206 passed. `npm --prefix frontend run build`: pass (TypeScript and both Vite outputs; existing chunk-size warnings). `npm --prefix frontend run test:webui-assets`: 104 files and 248 references verified. Locale-key parity, paired identifiers/links and scoped diff whitespace: pass. - -Browser verification: pass using the actual component with isolated simulated responses, covering configured regeneration, pairing explanation, pending/loading, error with retained connection, keyboard navigation/cancellation and 480×850 layout. Temporary preview files/server/tab were cleaned up and viewport restored. Actual provider QR content and existing/new bot selection remain owned by the authorization flow; live bot replacement: not_run (no isolated Runtime/account supplied). Python checks: not_applicable (no Python changes). - -## WeCom amendment (2026-09-15) -The user identified the missing WeCom regeneration entry and approved completing it. FR-1/FR-2 and T-1/T-2 now also cover configured WeCom bots. Regenerate QR code opens the existing WeCom SDK authorization window synchronously in the click gesture, shows the shared pairing title and WeCom replacement guidance, and sends returned credentials to the existing `/wecom/bindings` endpoint. Cancellation/blocked popup/failure retain the current bot; success refreshes diagnostics. No new API, permissions or browser credential storage. Add regressions for configured reauthorization, cancellation and blocked-popup retry. Direct review: reuse existing SDK cleanup and cancellation; no design blocker. New verification pending. - -WeCom amendment verification, 2026-09-15 current uncommitted diff: pass. The three new cases failed first; all 27 channel tests then passed with the same Vitest command. Frontend regression: 1206 passed. Production build and packaged-asset verification: pass (104 files, 248 references). Browser checks using the real component with simulated SDK/API: configured entry, successful replacement summary, pending/cancel preserving the old bot, blocked-popup error, keyboard navigation and 480×850 layout passed. No real WeCom window/account was authorized in these checks. Temporary preview files/server/tab removed and viewport restored. Paired docs, locale keys and whitespace: pass. No backend change; live authorization remains not_run. - -## Button layout and wording amendment (2026-09-15) -User-approved presentation-only adjustment: configured WeCom places Regenerate QR code and Unbind in the same existing horizontal action row, matching Feishu/DingTalk; all three Chinese labels use `解除绑定` and English uses `Unbind`. Reuse the WeCom authorization controls for bound/unbound placement, retaining the unbound form order and all handlers, disabled conditions, confirmation, and cancellation. No component state/API/security contract impact and no new behavior tests required for this formatting/localization change. Direct review found no blockers. - -Verification on the current uncommitted presentation diff: pass — existing channel Vitest 31 tests; frontend suite 1208 tests; production build; packaged assets 104 files/248 references; scoped whitespace. Actual component in browser with mocked APIs: all three providers share button baseline/height/gap; WeCom also remains horizontal at 480×850. No live binding was performed. Python checks not_applicable. Required design skills remain unavailable; existing channel styling was reused. Temporary preview files, tab and server were cleaned up. diff --git a/prd-spec/features/channel-qr-regeneration/2026-09-15-channel-qr-regeneration.zh.md b/prd-spec/features/channel-qr-regeneration/2026-09-15-channel-qr-regeneration.zh.md deleted file mode 100644 index 3d44db2b1..000000000 --- a/prd-spec/features/channel-qr-regeneration/2026-09-15-channel-qr-regeneration.zh.md +++ /dev/null @@ -1,36 +0,0 @@ -# 重新生成渠道配对二维码 - -[English](2026-09-15-channel-qr-regeneration.md) - -日期:2026-09-15。状态:implemented。用户批准:要求增加“重新生成二维码”和“消息渠道扫码配对”及飞书替换说明。契约:[MPA channels](../../../specs/mpa-channels/README.zh.md)。 - -## 背景与范围 -RuntimeChannels 当前在已配置机器人时禁用二维码生成。现有 MPA upsert 先注册并保存替换机器人,再移除旧机器人。保留最新企微 SDK 和钉钉手动绑定修改。不改变后端/API、平台授权、部署或凭据存储。 - -## 需求与设计 -FR-1:已绑定飞书或钉钉时,提供可点击的重新生成二维码。加载中、正在配对、注册结果不确定及配置不可用时仍禁用。 -FR-2:点击后展开内联“消息渠道扫码配对”区域,显示加载/状态/二维码。飞书说明扫码并绑定成功后替换已连接机器人,可在授权流程选择绑定存量机器人或新建机器人。选择由平台授权页承担,不新增 Studio 凭据表单。 -FR-3:生成二维码只 POST 既有绑定接口,不调用 DELETE。保留当前机器人摘要,直到 BOUND 后刷新诊断。请求失败或导航取消不会解绑当前机器人。 - -## 任务与验收 -T-1/AC-1:回归测试覆盖已绑定重新生成、替换说明、防重复请求及失败保留旧连接(FR-1/2/3)。 -T-2/AC-2:更新 RuntimeChannels.tsx 及双语 ui.json,保留渠道隔离和仅飞书群限制。 -T-3/AC-3:执行组件 Vitest、前端回归、生产构建、资源校验和浏览器验证,记录限制。 - -## 评审与风险 -直接设计评审(review-spec 不可用):增加 UI 契约,不改变鉴权与持久化;机器人选择界面由授权服务负责。无未解决阻塞。用户请求授权本次实施。未提供隔离 Runtime/账号前,不验证真实替换。正在配对时仍禁用再次生成,避免多个活跃配对会话并行。 - -## 验证 -2026-09-15,当前未提交前端渠道 diff:pass。新增三个回归用例在实施前失败,实施后组件 23 项全部通过(`cd frontend && ./node_modules/.bin/vitest run tests/runtimeChannels.test.tsx --maxWorkers 1`)。`npm --prefix frontend test`:1206 项通过。`npm --prefix frontend run build`:pass(TypeScript 和两套 Vite 输出,既有大包体警告)。`npm --prefix frontend run test:webui-assets`:104 文件、248 引用通过。语言包键、双语标识/链接及定向 diff 空白检查:pass。 - -浏览器验证:使用真实组件和隔离模拟响应,覆盖已绑定重新生成、配对说明、待扫码/加载、失败保留连接、键盘导航/取消以及 480×850 布局,均 pass。临时预览文件/服务/标签页已清理并恢复视口。实际二维码内容与存量/新建选择仍由授权流程承担;真实机器人替换:not_run(未提供隔离 Runtime/账号)。Python 检查:not_applicable(未修改 Python)。 - -## 企业微信补充(2026-09-15) -用户指出缺少企微重新生成入口,并授权补齐。FR-1/FR-2 和 T-1/T-2 现覆盖已绑定企微机器人。“重新生成二维码”在点击手势中同步打开既有企微 SDK 授权窗口,显示统一配对标题及企微替换说明,将返回凭据提交到既有 `/wecom/bindings` 接口。取消/弹窗被阻止/失败保留当前机器人,成功后刷新诊断。不新增 API、权限或浏览器凭据持久化。新增已绑定重新授权、取消及弹窗被阻止后重试回归测试。直接评审:复用已有 SDK 清理与取消,无设计阻塞。新验证待执行。 - -企微补充验证,2026-09-15 当前未提交 diff:pass。新增三个用例先失败,随后使用相同 Vitest 命令的 27 项渠道测试全部通过。前端回归 1206 项通过。生产构建及资源校验 pass(104 文件、248 引用)。真实组件配合模拟 SDK/API 的浏览器验证通过:已绑定入口、成功替换摘要、等待/取消保留旧机器人、弹窗被阻止提示、键盘导航及 480×850 布局。检查未对真实企微窗口/账号授权。临时预览文件/服务/标签页已移除并恢复视口。双语文档、语言包键和空白检查 pass。未改变后端,真实授权仍为 not_run。 - -## 按钮布局与文案补充(2026-09-15) -用户授权的纯展示调整:已绑定企业微信的“重新生成二维码”和“解除绑定”放在同一现有横向操作行,与飞书/钉钉一致;三个渠道中文统一为 `解除绑定`,英文为 `Unbind`。复用企业微信授权控件用于已绑定/未绑定的位置,保留未绑定时表单顺序及全部事件处理、禁用条件、确认与取消行为。不影响组件状态/API/安全契约;此格式和翻译调整无需新增行为测试。直接评审无阻塞。 - -当前未提交展示差异验证:pass — 既有渠道 Vitest 31 项、前端套件 1208 项、生产构建、产物 104 文件/248 引用和范围内空白检查。真实浏览器配模拟 API 验证实际组件:三个渠道按钮基线/高度/间距一致,企业微信在 480×850 下仍横排。未执行真实绑定。Python 检查 not_applicable。规范要求的设计技能仍不可用,复用现有渠道样式。临时预览文件、标签页和服务已清理。 diff --git a/prd-spec/features/deepseek-harness-runtime/2026-10-15-deepseek-harness-runtime.md b/prd-spec/features/deepseek-harness-runtime/2026-10-15-deepseek-harness-runtime.md deleted file mode 100644 index d79ca54f0..000000000 --- a/prd-spec/features/deepseek-harness-runtime/2026-10-15-deepseek-harness-runtime.md +++ /dev/null @@ -1,176 +0,0 @@ -# DSH Runtime - -[中文版](2026-10-15-deepseek-harness-runtime.zh.md) - -## Metadata - -- **Change ID:** `deepseek-harness-runtime` -- **Created / revised:** 2026-10-15 / 2026-10-15 -- **Lifecycle status:** implemented -- **Related component spec:** [Runtime DeepSeek Harness](../../../specs/runtime-deepseek-harness/README.md) -- **Owner areas:** `veadk.agent`, `veadk.runtime`, optional dependencies, runtime documentation, runtime tests. - -## Background and Evidence - -VeADK currently exposes three `Agent.runtime` values: `adk`, `codex`, and `piagent`. `codex` is installed through the `codex` extra: the Python SDK package pins a matching binary wheel. `piagent` resolves or downloads its binary on first runtime use. - -DeepSeek Harness already ships a Python SDK package named `deepseek-harness-sdk==0.1.5rc1`. Its import package is `deepseek_harness`; the SDK depends on `deepseek-harness-runtime-bin==0.1.5rc1`, which contains the bundled `dsh` executable for supported platforms. The SDK requires an explicit `dsh_home` or non-empty `DSH_HOME`; it never falls back to `~/.dsh`. `DeepSeekHarness` starts its subprocess lazily and reuses it across `run()` calls. - -Live SDK inspection on 2026-10-15 showed that the packaged DSH profile can start with the native provider name `deepseek-official`, while `volcengine`, `ark`, `volcengine-ark`, `openai`, `openai-compatible`, `third-party`, `doubao`, and `deepseek` are not registered DSH adapter names in the current wheel. VeADK therefore owns user-facing provider aliases for Volcengine and third-party OpenAI-compatible endpoints and maps them to the DSH adapter that currently accepts explicit `base_url` and `api_key`. - -Assumptions that remain intentionally bounded: - -- DSH native tool plugins are registered through the documented Cordis `ctx.tools.register(defineTool(...))` extension path. -- DSH skills are registered through the documented native `@deepseek-ai/dsh-skill`, `@deepseek-ai/dsh-skill-filesystem`, and `@deepseek-ai/dsh-tool-skill` plugins. -- DSH notifications are mapped for root-session assistant stream chunks and native tool call/result events; descendant session notifications are retained for future expansion but do not replace the root final response. - -## Goals - -- Add `runtime="dsh"` as a public Python SDK runtime value. -- Install DSH through a VeADK optional extra that depends on the Python SDK and its transitive runtime binary package. -- Start DSH lazily on first turn, not at import or `Agent(...)` construction time. -- Reuse a long-lived DSH SDK object per effective runtime configuration, with stable DSH session IDs derived from VeADK app/user/session/agent identity. -- Bridge ADK function tools and ADK/MCP toolsets into DSH as native tools through a generated DSH plugin and a local authenticated Python callback server. -- Bridge ADK sub-agent transfer through the same injected `transfer_to_agent` tool used by other external runtimes. -- Bridge ADK `SkillToolset` and legacy VeADK skills through DSH's native filesystem skill provider and `skill` loader tool. -- Stream DSH assistant deltas and native tool call/result events back as ADK events while preserving after-model callback semantics for the final response. -- Keep VeADK/ADK session history as the short-term memory source of truth by serializing visible session history into DSH prompts, matching the Codex runtime contract. -- Honor model changes made by before-model callbacks when selecting the DSH SDK config and cached manager. -- Use an explicit isolated `DSH_HOME` by default, with a configuration override for persistent deployments. -- Support omitted provider values and user-facing `openai`, `volcengine`, `volcengine-ark`, `ark`, `openai-compatible`, and `third-party` provider aliases without requiring callers to know the current DSH adapter name. -- Provide clear errors when the optional extra is missing. -- Document the lifecycle differences from Codex and PiAgent. - -## Non-goals - -- Do not build a custom VeADK first-run downloader for the `dsh` binary. -- Do not claim ADK `enable_skills_checklist` enforcement inside DSH's native skill loader. -- Do not implement a complete UI projection for descendant DSH subagent session notifications in this change. -- Do not add frontend Studio DeepSeek Harness behavior; Studio already has a separate DeepSeek Harness sandbox path. -- Do not run live provider calls as part of ordinary unit tests. - -## Scenarios and Requirements - -- **FR-1 — Runtime selection.** Given an agent constructed with `runtime="dsh"`, when it runs, VeADK dispatches to the DSH runtime instead of the ADK LLM flow. -- **FR-2 — Optional dependency boundary.** Given a default VeADK installation without DSH, when code resolves the runtime, the error names the missing extra and installation command. -- **FR-3 — Binary acquisition.** Given `veadk-python[dsh]` is installed, the matching `dsh` binary is acquired through `deepseek-harness-runtime-bin`; VeADK performs no runtime download. -- **FR-4 — Lazy startup.** Given a process imports VeADK or constructs an agent, DSH is not started until a turn reaches `DeepSeekHarnessRuntime.run_async`. -- **FR-5 — Session identity.** Given repeated turns for the same `app_name`, `user_id`, `session_id`, and agent name, VeADK passes the same DSH `session_id`. -- **FR-6 — Isolated home.** Given no `dsh_home` is configured, VeADK creates an explicit process-owned `DSH_HOME` under a temporary root and passes it to the SDK. -- **FR-7 — Callback compatibility.** Given model callbacks are configured, before/after/error model callbacks fire at the same coarse per-turn boundary used by other external runtimes. -- **FR-8 — Tool bridge.** Given ADK function tools or ADK/MCP toolsets are configured, VeADK exposes them as DSH native tools through `defineTool`, executes them in Python, and emits the corresponding ADK tool events. -- **FR-9 — Sub-agent transfer.** Given transfer-capable `sub_agents` are configured, VeADK exposes `transfer_to_agent`; when DSH calls it, VeADK runs the target agent and suppresses the DSH final text for that turn. -- **FR-10 — Skill bridge.** Given ADK `SkillToolset` or legacy VeADK skills are configured, VeADK materializes them to an isolated skill root, registers DSH's native skill plugins with `includeDefaultRoots=false`, and includes the skill content digest in the manager key. -- **FR-11 — Event mapping.** Given DSH emits root-session assistant stream chunks or native tool call/result events, VeADK maps them to ADK partial model events and function call/response events without duplicating VeADK-bridged Python tool events. -- **FR-12 — Codex-aligned short-term context and model callbacks.** Given ADK session history exists, DSH receives it as structured conversation data in the prompt. Given a before-model callback mutates `llm_request.model`, DSH uses the mutated model for manager selection and SDK config. -- **FR-13 — Honest limitations.** Given unsupported ADK flow features, VeADK warns or errors through the existing runtime support matrix and documentation rather than silently claiming equivalence. -- **FR-14 — Provider aliases.** Given config `provider` is omitted, blank, `openai`, `volcengine`, `volcengine-ark`, `ark`, `openai-compatible`, or `third-party`, VeADK resolves a valid user-facing configuration and passes DSH the currently registered OpenAI-compatible adapter provider. - -## Design and Contract Impact - -The selected design matches Codex's dependency-acquisition model: add a `dsh` optional extra that pins `deepseek-harness-sdk==0.1.5rc1`, letting that package bring the matching runtime binary wheel. This is preferred over the PiAgent-style first-run downloader because DSH already publishes an SDK/runtime-bin pair, so VeADK should not duplicate platform selection, cache management, and version coupling logic. - -The runtime package is `veadk.runtime.deepseek_harness`. Its public config class is `DeepSeekHarnessRuntimeConfig`. `Agent` gains `deepseek_harness_runtime_config`, accepting either that config or a dict. - -Effective DSH configuration resolves as: - -1. Agent `deepseek_harness_runtime_config`. -2. Narrow environment overrides with the `VEADK_DEEPSEEK_HARNESS_*` prefix. -3. Agent model fields for model name, API base, and API key when no DSH-specific override is set. -4. Conservative defaults (`provider="deepseek-official"`, `profile="sdk-minimal"`). `sdk-minimal` lets VeADK inject the DSH tool and skill plugins it owns, while a caller may still opt into DSH's fuller `sdk` profile. - -Provider handling is intentionally two-layered. `DeepSeekHarnessRuntimeConfig.provider` remains the user-facing provider name. Blank provider values normalize to `deepseek-official`. The runtime maps the aliases `openai`, `volcengine`, `volcengine-ark`, `ark`, `openai-compatible`, and `third-party` to DSH's current `deepseek-official` adapter before constructing `DeepSeekHarnessConfig`. Unknown provider names are passed through so future DSH-native providers can work without a VeADK release. - -State and lifecycle: - -- One `DeepSeekHarness` manager is cached per effective process configuration. -- The manager starts lazily and is closed at interpreter exit. -- A generated `DSH_HOME` lives under a process-owned temp root unless `dsh_home` is configured. -- The manager key includes the bridged DSH tool schema digest. Each manager owns a long-lived localhost tool bridge and a generated DSH plugin patch when tools are present. -- The manager key includes the materialized skill content digest. Each manager owns its DSH skill patch and skill root when skills are present. -- Turns on the same manager are serialized. During one active turn, the manager binds the current ADK tool bundle to its bridge and clears it after the SDK call returns. -- The DSH session id is a SHA-256 based stable ID over VeADK app/user/session/agent identity. -- The DSH prompt serializes the callback-visible `LlmRequest.contents` into structured prior conversation and current-message blocks. This keeps ADK session history, not DSH's private session state alone, as the portable short-term memory contract shared with Codex. - -Security: - -- The SDK subprocess inherits the process environment by default, matching DSH SDK behavior. Configured `env`, `base_url`, and `api_key` are explicit overrides. -- The runtime does not invent a sandbox policy. DSH profile behavior is owned by DSH. -- Tool bridge calls are accepted only on loopback and require a per-manager bearer token embedded in the generated DSH plugin. -- The generated `DSH_HOME` avoids implicit writes to `~/.dsh`. - -Compatibility: - -- Existing `adk`, `codex`, and `piagent` behavior is unchanged. -- The new runtime is opt-in by both runtime value and extra dependency. -- Missing extra failures are import-time-on-resolution, not package import failures. - -Component contract impact: a new component spec is required because this changes public runtime values, optional dependency behavior, session state, sidecar lifecycle, and configuration semantics. - -## Implementation Tasks - -- **T-1:** Add bilingual PRD/spec entries for the runtime contract. Covers `FR-1` through `FR-8`. -- **T-2:** Add the optional dependency extra and lock metadata. Covers `FR-2`, `FR-3`. -- **T-3:** Extend `Agent.runtime`, add `deepseek_harness_runtime_config`, and dispatch from `veadk.runtime.get_runtime`. Covers `FR-1`, `FR-2`. -- **T-4:** Implement `veadk.runtime.deepseek_harness` config, manager, prompt translation, generated DSH tool plugin, local tool bridge, native skill patch, event translator, and runtime. Covers `FR-4` through `FR-12`. -- **T-5:** Extend runtime compatibility warnings/errors for the new runtime. Covers `FR-13`. -- **T-6:** Add unit tests using a fake `deepseek_harness` module, without requiring the real binary. Covers `FR-1` through `FR-12`. -- **T-7:** Update runtime documentation in Chinese and English. Covers `FR-2`, `FR-3`, `FR-8`, `FR-10` through `FR-14`. - -## Verification and Acceptance - -| Requirement | Task | Acceptance criterion | Test or verification command | Result/evidence | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-3`, `T-6` | `runtime="dsh"` constructs and dispatches. | `uv run --extra dev pytest tests/runtime/deepseek_harness tests/runtime/differential/test_runtime_parity.py::test_model_fallbacks_warn_for_external_runtime` | pass, 2026-10-15 | -| `FR-2` | `T-2`, `T-3`, `T-6` | Missing SDK reports the extra installation command. | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass, 2026-10-15 | -| `FR-3` | `T-2` | The extra declares `deepseek-harness-sdk==0.1.5rc1`; no downloader is added. | `uv lock --check`; source review | pass, 2026-10-15 | -| `FR-4` | `T-4`, `T-6` | Fake SDK is not instantiated until runtime execution. | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass, 2026-10-15 | -| `FR-5` | `T-4`, `T-6` | Same VeADK session maps to the same DSH session id. | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass, 2026-10-15 | -| `FR-6` | `T-4`, `T-6` | Runtime passes a non-empty explicit `dsh_home` by default. | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass, 2026-10-15 | -| `FR-7` | `T-4`, `T-6` | Final DSH response becomes an ADK final event and callbacks can short-circuit. | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass, 2026-10-15 | -| `FR-8` | `T-4`, `T-6` | Fake DSH loads the generated plugin patch and calls an ADK FunctionTool through the localhost bridge. | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass, 2026-10-15 | -| `FR-9` | `T-4`, `T-6` | Sub-agent transfer uses the same injected transfer tool path as other external runtimes. | `uv run --extra dev --extra dsh pytest tests/runtime/deepseek_harness -q` | pass, 2026-10-16 | -| `FR-10` | `T-4`, `T-6` | Fake DSH config includes native DSH skill plugins and a materialized isolated skill root. | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass, 2026-10-15 | -| `FR-11` | `T-4`, `T-6` | Fake DSH notifications/events map to ADK partial model events and native function call/response events. | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass, 2026-10-15 | -| `FR-12` | `T-4`, `T-6`, `T-7` | Fake DSH receives structured ADK session history and uses the model selected by before-model callbacks. | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass, 2026-10-15 | -| `FR-13` | `T-5`, `T-7` | Limitations are represented in support matrix and docs. | Source/doc review | pass, 2026-10-15 | -| `FR-14` | `T-4`, `T-6`, `T-7` | Provider aliases map to the current DSH adapter while preserving explicit model/base/key configuration. | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass, 2026-10-15 | - -Required follow-up verification before release: - -- Dependency install smoke on each published wheel platform supported by DSH. -- Optional live DSH smoke only with an isolated API key and explicit authorization. - -Additional verification run on 2026-10-15: - -- `uv run --extra dev ruff check veadk/runtime/deepseek_harness tests/runtime/deepseek_harness/test_deepseek_harness_runtime.py examples/deepseek_harness_runtime/main.py` — blocked in this checkout because the `ruff` executable is not installed in the active environment. The broader dev-dependency cleanup was not retained because it is outside this runtime change. -- `uv run --extra dev pytest tests/runtime/deepseek_harness -q` — pass, 15 passed, 1 warning. -- `uv run --extra dev --extra dsh pytest tests/runtime/deepseek_harness -q` — pass, 15 passed, 1 warning. -- `uv run --extra dev pytest tests/runtime/deepseek_harness tests/runtime/differential/test_runtime_parity.py::test_model_fallbacks_warn_for_external_runtime -q` — pass, 16 passed, 1 warning. -- `uv run --extra dev --extra dsh python examples/deepseek_harness_runtime/main.py --sqlite-path /tmp/veadk_dsh_runtime_acceptance_final_check.sqlite` — pass with the prepared environment. The live run uses separate skill and memory runners and proved all five example checks: `streaming` reported 2 partial chunks, `skill` returned `DSH-SKILL-OK`, `short_term_memory_sqlite` persisted 6 session events and recalled the session secret, `long_term_memory_auto_save` found 1 direct long-term-memory match after a session switch, and `observability` recorded 15 matched spans including 5 `call_llm` spans. -- `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` — pass, 7065 passed, 50 skipped, 4 xfailed, 82 warnings. The local metrics exporter reported transient `localhost:8000` unavailable messages after the pytest result, but pytest completed successfully. -- `npm --prefix docs run lint` — not retained as a release signal for this change. Temporary docs-toolchain and formatting fixes were reverted because they touched site-wide docs files outside the DSH runtime scope. -- `npm --prefix docs run types:check` — not retained as a release signal for this change for the same scope reason. - -Additional review-fix verification run on 2026-10-16: - -- `uv run --no-sync --extra dev --extra dsh pytest tests/runtime/deepseek_harness -q` — pass, 20 passed, 1 warning. This covers `additionalProperties` preservation, persistent `dsh_home` cleanup registration, process-private tool plugin paths, cancellation without closing the shared manager, and sub-agent transfer execution. -- `uv run --no-sync --extra dev --extra dsh pytest tests/runtime/piagent/test_piagent_runtime.py::test_build_executable_tools_custom_runtime_label_controls_logs -q` — pass, 1 passed, 1 warning. -- `uv run --extra dev pre-commit run --files veadk/runtime/deepseek_harness/tool_runtime.py veadk/runtime/deepseek_harness/runtime.py tests/runtime/deepseek_harness/test_deepseek_harness_runtime.py docs/content/docs/framework/agent/runtime.en.mdx docs/content/docs/framework/agent/runtime.mdx specs/runtime-deepseek-harness/README.md specs/runtime-deepseek-harness/README.zh.md` — pass. - -## Risks and Open Questions - -- DSH SDK is currently `0.1.5rc1`; the release-candidate pin should be revisited before a stable VeADK release. -- `enable_skills_checklist` is not enforced for DSH native skill loading because that checklist is an ADK before-tool callback. -- Descendant DSH subagent session notifications are not yet projected as first-class ADK child-session events. -- A persistent `dsh_home` may contain durable DSH session data; operators are responsible for retention and cleanup when they configure one. - -## Review and Delivery Record - -- User approved the extra-based SDK/runtime-bin direction and requested implementation on 2026-10-15. -- Direct design review was performed because no repository `review-spec` skill is available in this session. Findings: - - Dependency acquisition must not duplicate DSH's runtime-bin wheel. - - `DSH_HOME` must be explicit because the SDK requires it and avoids global state. - - ADK/MCP tools must use the documented DSH tool plugin path; skills must use the documented DSH native skill plugins rather than ordinary tool expansion. - - Runtime support-matrix warnings must include the new non-ADK runtime. -- Blocking findings: none after the limitations above were made explicit. diff --git a/prd-spec/features/deepseek-harness-runtime/2026-10-15-deepseek-harness-runtime.zh.md b/prd-spec/features/deepseek-harness-runtime/2026-10-15-deepseek-harness-runtime.zh.md deleted file mode 100644 index 4593eb308..000000000 --- a/prd-spec/features/deepseek-harness-runtime/2026-10-15-deepseek-harness-runtime.zh.md +++ /dev/null @@ -1,176 +0,0 @@ -# DeepSeek Harness 运行时 - -[English](2026-10-15-deepseek-harness-runtime.md) - -## 元数据 - -- **变更 ID:** `deepseek-harness-runtime` -- **创建 / 修订日期:** 2026-10-15 / 2026-10-15 -- **生命周期状态:** implemented -- **相关组件规格:** [Runtime DeepSeek Harness](../../../specs/runtime-deepseek-harness/README.zh.md) -- **所属区域:** `veadk.agent`、`veadk.runtime`、可选依赖、运行时文档、运行时测试。 - -## 背景与证据 - -VeADK 当前公开三个 `Agent.runtime` 取值:`adk`、`codex` 和 `piagent`。`codex` 通过 `codex` extra 安装:Python SDK 包会精确绑定匹配的二进制 wheel。`piagent` 则在首次运行时解析或下载二进制。 - -DeepSeek Harness 已经发布 Python SDK 包 `deepseek-harness-sdk==0.1.5rc1`。它的导入包名是 `deepseek_harness`;SDK 依赖 `deepseek-harness-runtime-bin==0.1.5rc1`,该包在支持的平台上包含 bundled `dsh` 可执行文件。SDK 要求显式 `dsh_home` 或非空 `DSH_HOME`,不会回退到 `~/.dsh`。`DeepSeekHarness` 会懒启动子进程,并在多次 `run()` 调用之间复用它。 - -2026-10-15 的真实 SDK 检查显示,当前 wheel 中可启动的 DSH 原生 provider 名称是 `deepseek-official`;`volcengine`、`ark`、`volcengine-ark`、`openai`、`openai-compatible`、`third-party`、`doubao` 和 `deepseek` 不是当前已注册的 DSH adapter 名。VeADK 因此负责提供面向用户的火山与第三方 OpenAI-compatible provider 别名,再把它们映射到当前接受显式 `base_url` 和 `api_key` 的 DSH adapter。 - -本变更刻意收窄的假设: - -- DSH 原生工具插件通过已文档化的 Cordis `ctx.tools.register(defineTool(...))` 扩展路径注册。 -- DSH skills 通过已文档化的原生 `@deepseek-ai/dsh-skill`、`@deepseek-ai/dsh-skill-filesystem` 和 `@deepseek-ai/dsh-tool-skill` 插件注册。 -- DSH notifications 会映射 root session 的 assistant stream chunks 和原生 tool call/result events;descendant session notifications 保留给后续扩展,但不替代 root final response。 - -## 目标 - -- 增加 `runtime="dsh"` 作为公开 Python SDK runtime 取值。 -- 通过 VeADK optional extra 安装 DSH SDK 及其传递依赖的 runtime binary 包。 -- 在第一轮真正执行时懒启动 DSH,而不是 import 或 `Agent(...)` 构造时启动。 -- 按有效运行时配置复用长期 DSH SDK 对象,并基于 VeADK app/user/session/agent 身份生成稳定 DSH session ID。 -- 通过生成的 DSH 插件和本地带鉴权的 Python callback server,把 ADK function tools 和 ADK/MCP toolsets 桥接为 DSH 原生工具。 -- 通过其他外部 runtime 使用的同一个注入式 `transfer_to_agent` 工具桥接 ADK 子智能体转交。 -- 通过 DSH 原生 filesystem skill provider 和 `skill` loader tool 桥接 ADK `SkillToolset` 与 legacy VeADK skills。 -- 把 DSH assistant deltas 和原生 tool call/result events 流式转换为 ADK events,同时保留最终回复上的 after-model callback 语义。 -- 通过把可见 session history 序列化进 DSH prompt,保持 VeADK/ADK session history 是短期记忆的事实来源,与 Codex runtime 契约一致。 -- Before-model callback 修改模型后,DSH 会用修改后的模型选择 SDK config 和已缓存 manager。 -- 默认使用显式隔离的 `DSH_HOME`,并允许部署方配置持久目录。 -- 支持不填 provider,以及面向用户的 `openai`、`volcengine`、`volcengine-ark`、`ark`、`openai-compatible` 和 `third-party` provider 别名,不要求调用方知道当前 DSH adapter 名称。 -- 缺少 optional extra 时给出明确错误。 -- 文档说明它与 Codex、PiAgent 生命周期的差异。 - -## 非目标 - -- 不为 `dsh` 二进制实现 VeADK 自有的首次运行下载器。 -- 不声称在 DSH 原生 skill loader 内执行 ADK `enable_skills_checklist` 校验。 -- 本变更不实现 descendant DSH subagent session notifications 的完整 UI 投影。 -- 不修改 Studio 前端 DeepSeek Harness 行为;Studio 已有独立的 DeepSeek Harness sandbox 路径。 -- 普通单元测试不执行真实 provider 调用。 - -## 场景与需求 - -- **FR-1 — Runtime 选择。** 给定使用 `runtime="dsh"` 构造的 agent,当它运行时,VeADK 调度到 DSH runtime,而不是 ADK LLM flow。 -- **FR-2 — 可选依赖边界。** 给定默认 VeADK 安装中没有 DSH,当代码解析该 runtime 时,错误信息说明缺少 extra 和安装命令。 -- **FR-3 — 二进制获取。** 给定安装了 `veadk-python[dsh]`,匹配的 `dsh` 二进制通过 `deepseek-harness-runtime-bin` 获得;VeADK 不做运行时下载。 -- **FR-4 — 懒启动。** 给定进程 import VeADK 或构造 agent,直到 turn 到达 `DeepSeekHarnessRuntime.run_async` 前 DSH 都不会启动。 -- **FR-5 — Session 身份。** 给定相同 `app_name`、`user_id`、`session_id` 和 agent 名称的重复 turn,VeADK 传入相同 DSH `session_id`。 -- **FR-6 — 隔离 home。** 给定未配置 `dsh_home`,VeADK 在临时 root 下创建显式、进程所有的 `DSH_HOME` 并传给 SDK。 -- **FR-7 — Callback 兼容。** 给定配置了 model callback,before/after/error model callback 在与其他外部 runtime 相同的粗粒度 per-turn 边界执行。 -- **FR-8 — 工具桥接。** 给定配置了 ADK function tools 或 ADK/MCP toolsets,VeADK 通过 `defineTool` 暴露为 DSH 原生工具,在 Python 侧执行,并输出对应 ADK tool events。 -- **FR-9 — 子智能体转交。** 给定配置了可转交的 `sub_agents`,VeADK 暴露 `transfer_to_agent`;当 DSH 调用它时,VeADK 运行目标 agent,并抑制该 turn 的 DSH 最终文本。 -- **FR-10 — Skill 桥接。** 给定配置了 ADK `SkillToolset` 或 legacy VeADK skills,VeADK 会把它们物料化到隔离 skill root,通过 `includeDefaultRoots=false` 注册 DSH 原生 skill 插件,并把 skill 内容 digest 纳入 manager key。 -- **FR-11 — Event 映射。** 给定 DSH 输出 root-session assistant stream chunks 或原生 tool call/result events,VeADK 会映射为 ADK partial model events 和 function call/response events,并避免重复输出 VeADK 桥接的 Python tool events。 -- **FR-12 — Codex 对齐的短期上下文与模型 callback。** 给定存在 ADK session history,DSH 会在 prompt 中收到结构化 conversation data。给定 before-model callback 修改 `llm_request.model`,DSH 会用修改后的模型选择 manager 和 SDK config。 -- **FR-13 — 如实表达限制。** 给定不支持的 ADK flow 能力,VeADK 通过现有 runtime 支持矩阵和文档告警或报错,而不是默默宣称等价。 -- **FR-14 — Provider 别名。** 给定 config `provider` 未填写、为空、为 `openai`、`volcengine`、`volcengine-ark`、`ark`、`openai-compatible` 或 `third-party`,VeADK 会解析成有效的用户侧配置,并传给 DSH 当前已注册的 OpenAI-compatible adapter provider。 - -## 设计与契约影响 - -选定设计与 Codex 的依赖获取模式一致:新增 `dsh` optional extra,固定依赖 `deepseek-harness-sdk==0.1.5rc1`,由该包带入匹配的 runtime binary wheel。它优于 PiAgent 风格的首次运行下载器,因为 DSH 已经发布 SDK/runtime-bin 配对,VeADK 不应重复实现平台选择、缓存管理和版本耦合。 - -runtime 包路径为 `veadk.runtime.deepseek_harness`。公开配置类为 `DeepSeekHarnessRuntimeConfig`。`Agent` 增加 `deepseek_harness_runtime_config`,接受该配置或 dict。 - -有效 DSH 配置按以下顺序解析: - -1. Agent 的 `deepseek_harness_runtime_config`。 -2. `VEADK_DEEPSEEK_HARNESS_*` 前缀的窄环境变量覆盖。 -3. 未设置 DSH 专用覆盖时,使用 agent 的 model name、API base 和 API key。 -4. 保守默认值(`provider="deepseek-official"`、`profile="sdk-minimal"`)。`sdk-minimal` 让 VeADK 注入它自己负责的 DSH tool 和 skill 插件;调用方仍可显式选择 DSH 更完整的 `sdk` profile。 - -Provider 处理刻意分成两层。`DeepSeekHarnessRuntimeConfig.provider` 保持用户可见 provider 名称。空 provider 值会归一成 `deepseek-official`。Runtime 在构造 `DeepSeekHarnessConfig` 前,把 `openai`、`volcengine`、`volcengine-ark`、`ark`、`openai-compatible` 和 `third-party` 映射到当前 DSH 的 `deepseek-official` adapter。未知 provider 名会原样透传,让未来 DSH 原生 provider 不需要等待 VeADK 发版也能工作。 - -状态与生命周期: - -- 每个有效进程配置缓存一个 `DeepSeekHarness` manager。 -- manager 懒启动,并在解释器退出时关闭。 -- 除非配置 `dsh_home`,生成的 `DSH_HOME` 位于进程拥有的临时 root 下。 -- Manager key 包含已桥接 DSH tool schema 的 digest。存在工具时,每个 manager 拥有长期本地 tool bridge 和生成的 DSH plugin patch。 -- Manager key 包含已物料化 skill 内容的 digest。存在 skills 时,每个 manager 拥有 DSH skill patch 和 skill root。 -- 同一 manager 内的 turn 串行执行。每个活跃 turn 会把当前 ADK tool bundle 绑定到该 bridge,SDK 调用返回后清理绑定。 -- DSH session id 是基于 VeADK app/user/session/agent 身份计算的 SHA-256 稳定 ID。 -- DSH prompt 会把 callback 可见的 `LlmRequest.contents` 序列化为结构化的历史 conversation 和 current-message blocks。这让 ADK session history,而不是只靠 DSH 私有 session state,成为与 Codex 共享的可移植短期记忆契约。 - -安全: - -- SDK 子进程默认继承进程环境,保持 DSH SDK 行为。配置的 `env`、`base_url` 和 `api_key` 是显式覆盖。 -- runtime 不发明沙箱策略。DSH profile 行为由 DSH 拥有。 -- Tool bridge 只接受 loopback 调用,并要求生成的 DSH 插件携带 per-manager bearer token。 -- 生成的 `DSH_HOME` 避免隐式写入 `~/.dsh`。 - -兼容性: - -- 现有 `adk`、`codex` 和 `piagent` 行为不变。 -- 新 runtime 需要 runtime 取值和 extra 依赖双重显式选择。 -- 缺少 extra 的失败发生在解析 runtime 时,不影响包 import。 - -组件契约影响:本变更改变公开 runtime 取值、optional dependency 行为、session 状态、sidecar 生命周期和配置语义,因此需要新增组件规格。 - -## 实施任务 - -- **T-1:** 新增双语 PRD/spec,定义 runtime 契约。覆盖 `FR-1` 至 `FR-8`。 -- **T-2:** 新增 optional dependency extra 并同步 lock 元数据。覆盖 `FR-2`、`FR-3`。 -- **T-3:** 扩展 `Agent.runtime`、新增 `deepseek_harness_runtime_config`,并从 `veadk.runtime.get_runtime` 调度。覆盖 `FR-1`、`FR-2`。 -- **T-4:** 实现 `veadk.runtime.deepseek_harness` 的 config、manager、prompt 转换、生成式 DSH 工具插件、本地工具桥、原生 skill patch、event translator 和 runtime。覆盖 `FR-4` 至 `FR-12`。 -- **T-5:** 扩展 runtime 兼容性警告/错误。覆盖 `FR-13`。 -- **T-6:** 使用 fake `deepseek_harness` module 增加单元测试,不要求真实 binary。覆盖 `FR-1` 至 `FR-12`。 -- **T-7:** 更新中英文 runtime 文档。覆盖 `FR-2`、`FR-3`、`FR-8`、`FR-10` 至 `FR-14`。 - -## 验证与验收 - -| 需求 | 任务 | 验收标准 | 测试或验证命令 | 结果/证据 | -| --- | --- | --- | --- | --- | -| `FR-1` | `T-3`, `T-6` | `runtime="dsh"` 可以构造并调度。 | `uv run --extra dev pytest tests/runtime/deepseek_harness tests/runtime/differential/test_runtime_parity.py::test_model_fallbacks_warn_for_external_runtime` | pass,2026-10-15 | -| `FR-2` | `T-2`, `T-3`, `T-6` | 缺少 SDK 时错误提示 extra 安装命令。 | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass,2026-10-15 | -| `FR-3` | `T-2` | extra 声明 `deepseek-harness-sdk==0.1.5rc1`,未新增下载器。 | `uv lock --check`;源码审查 | pass,2026-10-15 | -| `FR-4` | `T-4`, `T-6` | fake SDK 在 runtime 执行前不会实例化。 | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass,2026-10-15 | -| `FR-5` | `T-4`, `T-6` | 相同 VeADK session 映射到相同 DSH session id。 | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass,2026-10-15 | -| `FR-6` | `T-4`, `T-6` | runtime 默认传入非空显式 `dsh_home`。 | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass,2026-10-15 | -| `FR-7` | `T-4`, `T-6` | DSH 最终回复成为 ADK final event,callback 可短路。 | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass,2026-10-15 | -| `FR-8` | `T-4`, `T-6` | Fake DSH 加载生成的 plugin patch,并通过 localhost bridge 调用 ADK FunctionTool。 | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass,2026-10-15 | -| `FR-9` | `T-4`, `T-6` | 子智能体转交使用与其他外部 runtime 相同的注入式 transfer tool 路径。 | `uv run --extra dev --extra dsh pytest tests/runtime/deepseek_harness -q` | pass,2026-10-16 | -| `FR-10` | `T-4`, `T-6` | Fake DSH config 包含 DSH 原生 skill 插件和已物料化的隔离 skill root。 | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass,2026-10-15 | -| `FR-11` | `T-4`, `T-6` | Fake DSH notifications/events 映射为 ADK partial model events 与原生 function call/response events。 | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass,2026-10-15 | -| `FR-12` | `T-4`, `T-6`, `T-7` | Fake DSH 收到结构化 ADK session history,并使用 before-model callbacks 选择的模型。 | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass,2026-10-15 | -| `FR-13` | `T-5`, `T-7` | 支持矩阵和文档表达限制。 | 源码/文档审查 | pass,2026-10-15 | -| `FR-14` | `T-4`, `T-6`, `T-7` | Provider 别名映射到当前 DSH adapter,同时保留显式 model/base/key 配置。 | `uv run --extra dev pytest tests/runtime/deepseek_harness` | pass,2026-10-15 | - -发布前还需补充验证: - -- 在 DSH 支持的各发布 wheel 平台做依赖安装 smoke。 -- 只有在显式授权并使用隔离 API key 时运行真实 DSH live smoke。 - -2026-10-15 追加验证: - -- `uv run --extra dev ruff check veadk/runtime/deepseek_harness tests/runtime/deepseek_harness/test_deepseek_harness_runtime.py examples/deepseek_harness_runtime/main.py` — blocked,当前 checkout 的活动环境未安装 `ruff` 可执行文件。更宽泛的 dev dependency 清理不保留,因为它超出本次 runtime 变更范围。 -- `uv run --extra dev pytest tests/runtime/deepseek_harness -q` — pass,15 passed,1 warning。 -- `uv run --extra dev --extra dsh pytest tests/runtime/deepseek_harness -q` — pass,15 passed,1 warning。 -- `uv run --extra dev pytest tests/runtime/deepseek_harness tests/runtime/differential/test_runtime_parity.py::test_model_fallbacks_warn_for_external_runtime -q` — pass,16 passed,1 warning。 -- `uv run --extra dev --extra dsh python examples/deepseek_harness_runtime/main.py --sqlite-path /tmp/veadk_dsh_runtime_acceptance_final_check.sqlite` — pass,使用已准备环境真实运行。该 live run 使用拆分后的 skill 与 memory runners,证明了示例五项检查全部通过:`streaming` 记录 2 个 partial chunks,`skill` 返回 `DSH-SKILL-OK`,`short_term_memory_sqlite` 持久化 6 条 session events 并召回 session secret,`long_term_memory_auto_save` 在切换 session 后直接检索到 1 条长期记忆,`observability` 记录 15 个匹配 spans,其中包含 5 个 `call_llm` spans。 -- `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` — pass,7065 passed,50 skipped,4 xfailed,82 warnings。pytest 结果输出后,本地 metrics exporter 记录了临时 `localhost:8000` unavailable 信息,但 pytest 已成功完成。 -- `npm --prefix docs run lint` — 不作为本变更的发布信号保留。临时 docs toolchain 与格式化修复已撤回,因为它们触及 DSH runtime 范围外的全站 docs 文件。 -- `npm --prefix docs run types:check` — 同样因范围原因不作为本变更的发布信号保留。 - -2026-10-16 追加 review 修复验证: - -- `uv run --no-sync --extra dev --extra dsh pytest tests/runtime/deepseek_harness -q` — pass,20 passed,1 warning。覆盖 `additionalProperties` 保留、持久 `dsh_home` 清理注册、进程私有 tool plugin 路径、取消时不关闭共享 manager,以及子智能体转交执行。 -- `uv run --no-sync --extra dev --extra dsh pytest tests/runtime/piagent/test_piagent_runtime.py::test_build_executable_tools_custom_runtime_label_controls_logs -q` — pass,1 passed,1 warning。 -- `uv run --extra dev pre-commit run --files veadk/runtime/deepseek_harness/tool_runtime.py veadk/runtime/deepseek_harness/runtime.py tests/runtime/deepseek_harness/test_deepseek_harness_runtime.py docs/content/docs/framework/agent/runtime.en.mdx docs/content/docs/framework/agent/runtime.mdx specs/runtime-deepseek-harness/README.md specs/runtime-deepseek-harness/README.zh.md` — pass。 - -## 风险与开放问题 - -- DSH SDK 当前为 `0.1.5rc1`;VeADK 稳定发布前应重新评估该 release-candidate pin。 -- `enable_skills_checklist` 暂不在 DSH 原生 skill loading 中执行,因为该 checklist 是 ADK before-tool callback。 -- Descendant DSH subagent session notifications 尚未投影成一等 ADK child-session events。 -- 持久化 `dsh_home` 可能包含 DSH session 数据;部署方配置后负责保留与清理策略。 - -## 评审与交付记录 - -- 用户在 2026-10-15 认可 extra-based SDK/runtime-bin 方向并要求开始实现。 -- 本会话中没有可用的仓库 `review-spec` skill,因此执行直接设计评审。结论: - - 依赖获取不应重复 DSH 的 runtime-bin wheel 机制。 - - `DSH_HOME` 必须显式传入,因为 SDK 要求如此,也能避免全局状态。 - - ADK/MCP tools 必须使用已文档化的 DSH tool plugin 路径;skills 必须使用已文档化的 DSH 原生 skill 插件,而不是普通 tool 展开。 - - runtime 支持矩阵必须覆盖新的非 ADK runtime。 -- 阻塞问题:上述限制显式写入后无阻塞。 diff --git a/prd-spec/features/harness-tos-skills/2026-10-12-harness-tos-skills.md b/prd-spec/features/harness-tos-skills/2026-10-12-harness-tos-skills.md deleted file mode 100644 index 5c28e7fe5..000000000 --- a/prd-spec/features/harness-tos-skills/2026-10-12-harness-tos-skills.md +++ /dev/null @@ -1,90 +0,0 @@ -# Harness TOS Skills - -[中文版](2026-10-12-harness-tos-skills.zh.md) - -## Metadata - -| Field | Value | -| --- | --- | -| Change ID | `harness-tos-skills` | -| Created | 2026-10-12 | -| Status | implemented | -| Related specs | No component spec update; this extends the existing Harness selected-skill request contract without changing ownership boundaries. | - -## Background And Evidence - -Harness can already load SkillHub skills and Skill Space skills. Skill Space loading eventually downloads TOS-backed Skill archives through the existing materializer, but callers cannot directly provide an object-storage Skill directory. - -The requested behavior should align with AWS AgentCore Harness S3 skills: one object-storage URI points to one Skill directory containing `SKILL.md`; multiple Skills are represented as multiple selected-skill entries. - -## Goals - -- Add `source="tos"` to `HarnessSelectedSkill`. -- Support current-account TOS directories via Runtime cloud identity. -- Support public readable TOS zip URLs without requiring AK/SK. -- Preserve existing SkillHub and Skill Space behavior. - -## Non-Goals - -- No parent-directory auto-discovery of multiple Skills. -- No plaintext AK/SK in Harness requests. -- No cross-account private TOS credential reference in this MVP. - -## Requirements - -| ID | Requirement | -| --- | --- | -| FR-1 | A TOS selected Skill with `tos_url="tos://bucket/path/to/skill/"` loads exactly one Skill directory and requires `SKILL.md` or `skill.md` at that URL root. | -| FR-2 | Multiple TOS Skills are loaded by multiple `selected_skills` entries. | -| FR-3 | A public TOS zip URL can load one Skill archive. | -| FR-4 | Empty/bucket-root TOS URLs, parent directories, unsupported public URLs, non-TOS hosts, credential-bearing public URLs, and redirects to non-TOS hosts fail closed. | -| FR-5 | TOS download failures and invalid Skill directories fail the whole SkillToolset build. | -| FR-6 | Public TOS zip downloads, public zip extraction, and private TOS directory downloads enforce bounded file/object count and byte limits before Skill loading. | - -## Design - -`HarnessSelectedSkill.source` accepts `tos` with either: - -```python -HarnessSelectedSkill(source="tos", tos_url="tos://bucket/skills/reporting/", region="cn-beijing") -HarnessSelectedSkill(source="tos", tos_public_url="https://bucket.tos-cn-beijing.volces.com/skills/reporting.zip") -``` - -Multiple Skills are expressed as multiple entries: - -```python -selected_skills=[ - HarnessSelectedSkill(source="tos", tos_url="tos://bucket/skills/reporting/", region="cn-beijing"), - HarnessSelectedSkill(source="tos", tos_url="tos://bucket/skills/search/", region="cn-beijing"), -] -``` - -Harness serializes these as internal refs and dispatches them in `build_skill_toolset`. TOS selected Skills use `tos_url` and `tos_public_url` as the only locator fields. The materializer parses `tos://` URLs, downloads one directory through `VeTOS`, validates path containment, requires root-level `SKILL.md` or `skill.md`, and loads with ADK. A URL pointing at a parent folder containing child Skills is rejected instead of selecting one child. Public URLs must not contain userinfo or TOS signature query credentials. They are streamed as zip archives from validated TOS hosts, every redirect hop must also target a TOS host before it is requested, and the existing safe zip extraction path enforces expanded file count and uncompressed byte limits for public TOS archives. - -Rejected TOS refs are redacted when wrapped into Harness skill-load errors, so invalid userinfo or signature query values are not returned to callers or written by the Harness error path. - -## Tasks - -| ID | Task | -| --- | --- | -| T-1 | Add schema fields and validation for TOS selected Skills. | -| T-2 | Add materializer helpers for TOS directory and public zip sources, including strict root-manifest validation and resource limits. | -| T-3 | Wire Harness SkillToolset building to TOS refs. | -| T-4 | Add focused contract and materializer tests. | - -## Verification - -| Requirement | Command | Result | -| --- | --- | --- | -| FR-1..FR-6 | `uv run --extra dev pytest tests/skills/test_adk_skill_materializer.py tests/test_ve_tos.py::test_download_can_propagate_tos_error tests/test_ve_tos.py::test_download_rejects_object_over_byte_limit tests/test_ve_tos.py::test_download_directory_rejects_too_many_objects tests/test_ve_tos.py::test_download_directory_rejects_total_bytes_from_listing -q` | pass, 30 passed on 2026-10-12 | -| FR-4..FR-5 | `uv run --extra dev pytest tests/cloud/test_harness_app_contract.py::TestHarnessOverrides::test_tos_selected_skill_rejects_public_url_userinfo tests/cloud/test_harness_app_contract.py::TestHarnessOverrides::test_tos_selected_skill_rejects_public_url_signature_query tests/cloud/test_harness_skill_download.py::test_build_skill_toolset_redacts_tos_ref_on_error -q` | pass, 3 passed on 2026-10-13 | -| FR-1..FR-6 | `uv run --extra dev pytest tests/cloud/test_harness_skill_download.py::test_build_skill_toolset_loads_tos_skill_uri tests/cloud/test_harness_skill_download.py::test_build_skill_toolset_loads_multiple_tos_entries -q` | pass, 2 passed on 2026-10-12 | -| FR-1..FR-6 | `uv run --extra dev ruff check veadk/skills/materializer.py veadk/integrations/ve_tos/ve_tos.py tests/skills/test_adk_skill_materializer.py tests/test_ve_tos.py` | pass on 2026-10-12 | -| FR-1..FR-6 | `uv run --extra dev pre-commit run --all-files` | pass on 2026-10-12 | -| FR-1..FR-6 | `git diff --check` | pass on 2026-10-12 | -| FR-1..FR-6 | `uv run --extra dev pytest tests/skills/test_adk_skill_materializer.py tests/test_ve_tos.py -q` | fail, existing environment limitation: four async tests in `tests/test_ve_tos.py` need an async pytest plugin; TOS-focused tests passed. | - -## Risks - -- Real TOS IAM behavior requires cloud smoke validation outside the offline unit tests. -- Public directory URLs remain excluded because object storage does not provide a stable generic HTTP directory listing contract. diff --git a/prd-spec/features/harness-tos-skills/2026-10-12-harness-tos-skills.zh.md b/prd-spec/features/harness-tos-skills/2026-10-12-harness-tos-skills.zh.md deleted file mode 100644 index 2375d45aa..000000000 --- a/prd-spec/features/harness-tos-skills/2026-10-12-harness-tos-skills.zh.md +++ /dev/null @@ -1,90 +0,0 @@ -# Harness TOS Skills - -[English](2026-10-12-harness-tos-skills.md) - -## 元数据 - -| 字段 | 值 | -| --- | --- | -| Change ID | `harness-tos-skills` | -| 创建日期 | 2026-10-12 | -| 状态 | implemented | -| 相关规格 | 不更新组件规格;本变更扩展现有 Harness selected-skill 请求契约,不改变组件职责边界。 | - -## 背景和证据 - -Harness 当前已经支持加载 SkillHub Skill 和 Skill Space Skill。Skill Space 加载最终会通过现有 materializer 下载 TOS 上的 Skill archive,但调用方不能直接提供对象存储 Skill 目录。 - -目标行为对齐 AWS AgentCore Harness S3 Skills:一个对象存储 URI 指向一个包含 `SKILL.md` 的 Skill 目录;多个 Skill 通过多条 selected-skill entry 表达。 - -## 目标 - -- 为 `HarnessSelectedSkill` 增加 `source="tos"`。 -- 支持通过 Runtime 云身份读取当前账号 TOS 目录。 -- 支持公开读 TOS zip URL,不要求 AK/SK。 -- 保持现有 SkillHub 和 Skill Space 行为不变。 - -## 非目标 - -- 不做父目录自动发现多个 Skill。 -- 不在 Harness 请求中接收明文 AK/SK。 -- MVP 不支持跨账号私有 TOS 的 credential reference。 - -## 需求 - -| ID | 需求 | -| --- | --- | -| FR-1 | TOS selected Skill 使用 `tos_url="tos://bucket/path/to/skill/"` 时只加载一个 Skill directory,且该 URL 根目录必须直接包含 `SKILL.md` 或 `skill.md`。 | -| FR-2 | 多个 TOS Skill 通过多条 `selected_skills` entry 加载。 | -| FR-3 | 公开读 TOS zip URL 可以加载一个 Skill archive。 | -| FR-4 | 空 URL、bucket 根路径 URL、父目录 URL、不支持的公开 URL、非 TOS host、带凭据的公开 URL,以及跳转到非 TOS host 的公开 URL 必须失败关闭。 | -| FR-5 | TOS 下载失败或 Skill 目录非法时,整个 SkillToolset 构建失败。 | -| FR-6 | 公开 TOS zip 下载、公开 zip 解压和私有 TOS 目录下载必须在 Skill 加载前执行文件/对象数和字节数上限。 | - -## 设计 - -`HarnessSelectedSkill.source` 接受 `tos`,并支持以下两种形式: - -```python -HarnessSelectedSkill(source="tos", tos_url="tos://bucket/skills/reporting/", region="cn-beijing") -HarnessSelectedSkill(source="tos", tos_public_url="https://bucket.tos-cn-beijing.volces.com/skills/reporting.zip") -``` - -多个 Skill 通过多条 entry 表达: - -```python -selected_skills=[ - HarnessSelectedSkill(source="tos", tos_url="tos://bucket/skills/reporting/", region="cn-beijing"), - HarnessSelectedSkill(source="tos", tos_url="tos://bucket/skills/search/", region="cn-beijing"), -] -``` - -Harness 将其序列化为内部 ref,并在 `build_skill_toolset` 中分发。TOS selected Skill 只使用 `tos_url` 和 `tos_public_url` 作为 locator 字段。materializer 解析 `tos://` URL,通过 `VeTOS` 下载单个目录,校验路径没有逃逸,要求根目录直接包含 `SKILL.md` 或 `skill.md`,再用 ADK 加载。指向包含多个子 Skill 的父目录 URL 会被拒绝,不会自动选择某个子目录。公开 URL 不允许包含 userinfo 或 TOS 签名 query 凭据。公开 URL 按 zip archive 流式下载,每一跳 redirect 在请求前都必须指向 TOS host,并且现有安全解压路径会对公开 TOS archive 执行展开后的文件数和未压缩字节数上限。 - -被拒绝的 TOS ref 在包装成 Harness skill-load 错误时会脱敏,避免无效 userinfo 或签名 query 值返回给调用方,或被 Harness 错误路径写入日志。 - -## 任务 - -| ID | 任务 | -| --- | --- | -| T-1 | 增加 TOS selected Skill schema 字段和校验。 | -| T-2 | 增加 TOS directory 和 public zip materializer,包括严格根 manifest 校验和资源上限。 | -| T-3 | 将 Harness SkillToolset 构建接入 TOS refs。 | -| T-4 | 增加聚焦的契约测试和 materializer 测试。 | - -## 验证 - -| 需求 | 命令 | 结果 | -| --- | --- | --- | -| FR-1..FR-6 | `uv run --extra dev pytest tests/skills/test_adk_skill_materializer.py tests/test_ve_tos.py::test_download_can_propagate_tos_error tests/test_ve_tos.py::test_download_rejects_object_over_byte_limit tests/test_ve_tos.py::test_download_directory_rejects_too_many_objects tests/test_ve_tos.py::test_download_directory_rejects_total_bytes_from_listing -q` | pass,2026-10-12 执行,30 passed | -| FR-4..FR-5 | `uv run --extra dev pytest tests/cloud/test_harness_app_contract.py::TestHarnessOverrides::test_tos_selected_skill_rejects_public_url_userinfo tests/cloud/test_harness_app_contract.py::TestHarnessOverrides::test_tos_selected_skill_rejects_public_url_signature_query tests/cloud/test_harness_skill_download.py::test_build_skill_toolset_redacts_tos_ref_on_error -q` | pass,2026-10-13 执行,3 passed | -| FR-1..FR-6 | `uv run --extra dev pytest tests/cloud/test_harness_skill_download.py::test_build_skill_toolset_loads_tos_skill_uri tests/cloud/test_harness_skill_download.py::test_build_skill_toolset_loads_multiple_tos_entries -q` | pass,2026-10-12 执行,2 passed | -| FR-1..FR-6 | `uv run --extra dev ruff check veadk/skills/materializer.py veadk/integrations/ve_tos/ve_tos.py tests/skills/test_adk_skill_materializer.py tests/test_ve_tos.py` | pass,2026-10-12 执行 | -| FR-1..FR-6 | `uv run --extra dev pre-commit run --all-files` | pass,2026-10-12 执行 | -| FR-1..FR-6 | `git diff --check` | pass,2026-10-12 执行 | -| FR-1..FR-6 | `uv run --extra dev pytest tests/skills/test_adk_skill_materializer.py tests/test_ve_tos.py -q` | fail,现有环境限制:`tests/test_ve_tos.py` 中 4 个 async 测试需要 pytest async 插件;TOS 聚焦测试已通过。 | - -## 风险 - -- 真实 TOS IAM 行为需要额外的云端 smoke 验证,离线单测无法证明。 -- 公开目录 URL 仍不纳入范围,因为对象存储没有稳定通用的 HTTP 目录清单契约。 diff --git a/prd-spec/features/lark-binding/2026-10-15-lark-binding.md b/prd-spec/features/lark-binding/2026-10-15-lark-binding.md deleted file mode 100644 index 8f61939ff..000000000 --- a/prd-spec/features/lark-binding/2026-10-15-lark-binding.md +++ /dev/null @@ -1,60 +0,0 @@ -# Lark channel binding and delivery - -[中文](2026-10-15-lark-binding.zh.md) - -Date: 2026-10-15. Status: implemented; local verification complete; live rollout not performed. -User approval: implement the reviewed Studio/Runtime Lark plan without changing domestic Feishu. - -## Background and evidence -Studio exposes Feishu only; Runtime registration starts on Feishu and message clients use Feishu hosts. The IM Channel Gateway design section 3.3.2 explicitly registers Lark with adapter=lark and adapter_config.domain=https://open.larksuite.com. Its tables contradict examples on bot_name and incoming platform; preserve Feishu payload and verify Lark live before claiming release readiness. The old plugin provides domain-aware registration, not proof of deployed Gateway compatibility. - -## Goals, scope and non-goals -Enable BytePlus Lark QR/manual binding, durable retries, account management and inbound/reply delivery. Preserve existing Feishu defaults and records. No database migration, Worker protocol change, deployment, cloud resource creation or commit is authorized by this implementation task. Lark CLI user authorization inside Worker is outside the messaging binding contract. - -## Requirements and design -FR-1: Studio adds a Lark tab for BytePlus. Runtime must advertise feishuDomains containing lark and multiBotChannels containing feishu before setup is enabled. Old images display the existing upgrade error. Reuse canonical /feishu management APIs with domain=lark on new binding/manual requests; default/missing domain remains feishu. Filter account lists by persisted config.domain (missing means feishu). Always address operations by appId; preserve admin checks, no browser credential persistence, cancellation and late-response protection. -FR-2: Runtime validates domain against feishu/lark, starts and polls the selected registration host, persists domain with the durable session, rejects conflicting tenant brands for Lark authorization (domestic Feishu retains legacy tenant metadata handling) and exposes only safe brand metadata. Same-app cross-brand rebinding is rejected before provider mutations. Existing credential-equivalent registration reuse remains idempotent. -FR-3: Register Lark using adapter=lark with fixed domain=https://open.larksuite.com plus app_id/app_secret. Feishu registration payload is unchanged. Routing uses existing agent_id, Upstream and APIG. Unknown registration results retain existing uncertain/reconcile behavior, never blind retries. -FR-4: Keep storage channel=feishu for compatibility, with domain=lark as account brand. Accept lark and legacy feishu event envelopes for bound Lark bots; validate bot ownership before mapping Lark into shared Feishu execution semantics. Raw envelope/request IDs remain intact for durable dedupe and Gateway replies. Reject lark events addressed to domestic Feishu bots. Account identity, permissions and sessions stay scoped by appId/bot UUID. -FR-5: All direct message client constructors use the trusted account domain, including token, replies, cards, images/media, bot info, history and scheduled delivery. Lark uses only fixed Lark hosts; domestic read-host configuration remains unchanged. Never derive API hosts from user-supplied arbitrary URLs. - -## Affected files and tasks -T-1: Runtime schemas, binding/studio/config/messages/delivery services, message/registration clients and account-specific client consumers; regression tests. -T-2: Studio RuntimeChannels, FeishuAccounts, account reader, capabilities types, localized strings, automation provider propagation and UI tests; rebuild web assets. -T-3: Update maintained channel contracts, run affected checks and review both language versions. Runtime CHANGELOG records delivery status. - -## Design review -Direct review (review-spec unavailable): additive interfaces and backward defaults; no schema migration; fixed hosts; secrets excluded from public results; existing admin and Gateway auth retained; cross-brand rebinding fails closed; QR expiry/cancellation/uncertainty use existing lifecycle. User-approved plan covers these refinements. No design blockers for local implementation; live Gateway behavior remains an explicit verification prerequisite. - -## Tests and acceptance -AC-1/FR-1: UI tests cover BP Lark, domestic tabs, unsupported Runtime, account filtering, request payloads, cancellation and retry. -AC-2/FR-2/FR-3: Mocked registration and real SQLite store tests verify domain persistence after recreation, tenant mismatch, idempotent registration, unchanged Feishu payload and cross-brand protection. -AC-3/FR-4/FR-5: Test Gateway Lark ingress, bot validation, reply addressing, direct-client host selection and domestic regression. -Run frontend tests/build/i18n/assets and real browser simulated flows; Runtime targeted tests then make lint/make test. Record outcomes and any baseline failures. Live QR/private/group/stream/unbind smoke requires a newly deployed Runtime image and authorized test bots; simulated checks cannot prove that deployed Gateway supports Lark. - -## Risks and evidence -Gateway design tables disagree with examples; do not change domestic adapter or add undocumented required fields. Canonical storage requires consistent account-domain propagation. Release order: Runtime image then Studio. No real provider mutations; verification results follow. - -## Implementation review and verification (2026-10-15) -Scope: uncommitted Lark changes on Studio `d5f9d485` and Runtime `1aa57dc`. T-1/T-2/T-3 are implemented. Direct review checked fixed provider hosts, canonical channel compatibility, per-account identity, durable recovery, cancellation/late responses, secret handling and bilingual equivalence. Only Lark authorization adds tenant-brand rejection; domestic tenant metadata behavior is preserved. - -| Check | Outcome | Evidence | -| --- | --- | --- | -| Studio `npm --prefix frontend test` | pass | 1,389 Node tests and 116 React tests; final focused UI/account run: 85 passed. | -| Studio build, `check:i18n`, `test:webui-assets` | pass | Production assets rebuilt; 2 locales/21 namespaces; 113 files/350 asset references checked. | -| Studio proxy policy tests | pass | `uv run --extra dev pytest -q tests/test_mpa_channel_proxy_policy.py`: 6 passed. | -| Real browser with isolated fake API | pass | QR pending/success, manual binding, error/retry, old-image gating, account filtering, loading/cancellation, keyboard and 390-pixel layout; no real Bot requests. | -| Runtime targeted regression | pass | 304 channel/client/media/delivery tests; final domestic binding/service/Lark run: 82 passed. | -| New Lark regression against original Runtime HEAD | pass | Expected red baseline: 13 failures and 1 pass; current new suite: 14 passed. | -| Runtime `make lint` and complete `make test` | pass | 8,846 passed, 188 skipped, 431 warnings, 0 failed (419 seconds). Run through isolated `npm exec --yes --package @openai/codex@0.155.1 -- sh -c 'make lint && make test'`; no global installation/configuration change. | -| Runtime static checks | pass / fail (baseline) | F821/F811/F401 and new-test Ruff pass. Full changed-file Ruff has 39 findings versus baseline 40; Pyright has the same 27 baseline errors, with no new diagnostic categories/locations introduced. Full static gates are not clean. | -| Secret scan and whitespace | pass | Studio pre-commit gitleaks, Runtime working-diff gitleaks, separate untracked-source scan and `git diff --check`; no real credentials added. | -| Live Lark/Gateway E2E, PostgreSQL coverage, formal CI | not_run | Requires authorized test resources/new Runtime deployment or CI infrastructure; SQLite/fake API checks do not prove these. | -| Commit pre-commit/rebase gate | pass | User subsequently authorized local commits. Fetched remotes; Studio rebase on `upstream/main` and Runtime rebase on `origin/master` were already up to date. Studio `uv run --extra dev pre-commit run --all-files` passes all five hooks. Unrelated local lockfile deletions are excluded. No push authorized. | - -The initial full Runtime run found three domestic tenant-brand regressions and one local Codex version mismatch. The domestic guard was narrowed and its regressions passed. The repository requires Codex 0.155.1; the app binary is 0.162.0-alpha.17.2, so the final full run uses an isolated pinned tool. Skipped tests are not counted as verified behavior. Frontend-design/ui-ux-pro-max and review-spec skills were unavailable; existing UI patterns and direct design/browser review were used. Remaining release prerequisite: build/deploy the new Runtime image, then verify Lark QR/manual authorization, private/group delivery, streaming and unbind against the actual Gateway. This change does not deploy resources. - -### Subsequent live observation -The user reported “link expired” on the Lark authorization page. Runtime capabilities advertise Lark, but no completed Lark binding was observed. The failing authorization URL/session was not available for diagnosis; root cause remains unconfirmed. This local commit does not claim live QR/Gateway acceptance or resolve that report. - -Post-sync commit checks: Studio full frontend tests (1,389 Node + 116 React) and 6 proxy policy tests pass; Runtime `make lint` and 372 targeted channel/client/media/delivery tests pass. The earlier 8,846-test full Runtime result applies to the unchanged production diff. diff --git a/prd-spec/features/lark-binding/2026-10-15-lark-binding.zh.md b/prd-spec/features/lark-binding/2026-10-15-lark-binding.zh.md deleted file mode 100644 index efc390558..000000000 --- a/prd-spec/features/lark-binding/2026-10-15-lark-binding.zh.md +++ /dev/null @@ -1,60 +0,0 @@ -# Lark 渠道绑定与消息投递 - -[English](2026-10-15-lark-binding.md) - -日期:2026-10-15。状态:已实现,本地验证完成,未执行线上发布。 -用户批准:按已审阅方案同时适配 Studio/Runtime,不改变国内飞书。 - -## 背景与证据 -Studio 只有飞书入口;Runtime 注册固定从飞书开始,消息客户端使用飞书域名。IM Channel Gateway 设计 3.3.2 明确要求 Lark 使用 adapter=lark、adapter_config.domain=https://open.larksuite.com。参数表与示例在 bot_name、入站 platform 上不一致;保留飞书请求,真实联调后才能判定可发布。旧插件支持域名切换,但不能证明已部署网关版本兼容。 - -## 目标、范围及非目标 -支持 BytePlus Lark 扫码/手动绑定、持久化重试、账号管理和入站/回复。保留飞书默认值和已有记录。本任务不授权数据库迁移、Worker 协议调整、部署、云资源创建或提交。Worker 内 Lark CLI 用户授权不属于消息绑定契约。 - -## 要求与设计 -FR-1:Studio 在 BytePlus 增加 Lark 页签。仅当 Runtime 声明 feishuDomains 包含 lark 且 multiBotChannels 包含 feishu 时开放配置。旧镜像使用现有升级提示。复用 /feishu 管理接口,新扫码/手动请求带 domain=lark;缺省仍为 feishu。按 config.domain 过滤列表,缺失按 feishu。操作始终携带 appId;保留管理员校验、不在浏览器持久化凭据、取消和迟到响应保护。 -FR-2:Runtime 限定 domain 为 feishu/lark,使用对应注册域名发起及轮询,持久化品牌,Lark 授权返回不匹配的租户品牌时拒绝绑定,国内飞书保留原有租户元数据处理,只公开安全品牌元数据。同 App 跨品牌重绑在外部操作前拒绝。同凭据重试保留幂等复用。 -FR-3:Lark 注册用 adapter=lark,固定 domain=https://open.larksuite.com,加 app_id/app_secret。飞书请求保持不变。沿用 agent_id、Upstream 和 APIG 路由。注册结果未知沿用 uncertain/核对流程,不盲目重试。 -FR-4:兼容保留存储 channel=feishu,以 domain=lark 区分品牌。已绑定 Lark Bot 接受 lark 及旧 feishu 事件格式;核验 Bot 后映射到共用飞书执行语义。保留原始事件/request ID 用于持久化去重和网关回复。国内 Bot 拒绝 lark 事件。账号、权限和会话仍由 appId/Bot UUID 隔离。 -FR-5:直接消息客户端始终使用已绑定账号品牌,包括 Token、回复、卡片、图片/媒体、Bot 信息、历史和定时投递。Lark 仅用固定 Lark 域名;国内读域名配置不变。不接受用户任意 URL 作为 API 域名。 - -## 文件与任务 -T-1:Runtime schema、binding/studio/config/messages/delivery、消息/注册客户端及账号客户端调用点和回归测试。 -T-2:Studio RuntimeChannels、FeishuAccounts、账号读取、能力类型、本地化、自动化入口云提供方透传及 UI 测试;重建发布资源。 -T-3:更新渠道契约,执行检查并核对双语。Runtime CHANGELOG 记录交付状态。 - -## 设计审查 -直接审查(review-spec 不可用):接口增量及兼容默认值;无迁移;固定域名;公开结果不含密钥;保留管理和网关鉴权;跨品牌拒绝;二维码过期/取消/未知结果沿用生命周期。细化内容在用户已批准范围内。本地实施无设计阻塞,网关实测仍是发布验证前提。 - -## 测试与验收 -AC-1/FR-1:UI 测试覆盖 BP Lark、国内页签、旧 Runtime、账号过滤、请求、取消及重试。 -AC-2/FR-2/FR-3:模拟注册及真实 SQLite 验证重建服务后的品牌持久化、租户不匹配、注册幂等、飞书请求不变及跨品牌保护。 -AC-3/FR-4/FR-5:验证 Lark 入站、Bot 校验、回复寻址、客户端域名及国内回归。 -执行前端测试/构建/i18n/发布资源与真实浏览器模拟;Runtime 定向测试后 make lint/make test。记录结果和基线失败。真实扫码/私聊/群聊/流式/解绑需发布新镜像及获授权测试 Bot;模拟结果不证明线上网关支持 Lark。 - -## 风险与证据 -网关表格与示例冲突,不改国内 adapter 或添加未经核实的必填字段。共用存储要求完整透传品牌。发布顺序:Runtime 镜像后 Studio。未执行真实云端写操作;验证结果如下。 - -## 实施审查与验证(2026-10-15) -范围:Studio `d5f9d485` 和 Runtime `1aa57dc` 之上的未提交 Lark 改动。T-1/T-2/T-3 已实现。直接审查覆盖固定服务域名、规范渠道兼容性、账号身份、持久化恢复、取消/迟到响应、密钥处理和双语一致性。仅 Lark 授权新增租户品牌拒绝规则,国内租户元数据行为保留。 - -| 检查 | 结果 | 证据 | -| --- | --- | --- | -| Studio `npm --prefix frontend test` | pass | 1,389 项 Node 测试和 116 项 React 测试;最终定向 UI/账号回归 85 passed。 | -| Studio build、`check:i18n`、`test:webui-assets` | pass | 已重建发布资源;2 种语言/21 个命名空间;核验 113 个文件/350 个资源引用。 | -| Studio 代理策略测试 | pass | `uv run --extra dev pytest -q tests/test_mpa_channel_proxy_policy.py`:6 passed。 | -| 真实浏览器配合隔离模拟 API | pass | 扫码等待/成功、手动绑定、失败/重试、旧镜像门控、账号过滤、加载/取消、键盘和 390 像素布局;未请求真实 Bot。 | -| Runtime 定向回归 | pass | 渠道/客户端/媒体/投递 304 项通过;最终国内绑定/服务/Lark 回归 82 passed。 | -| 新 Lark 回归对原始 Runtime HEAD | pass | 预期红灯基线:13 failed、1 passed;当前新增套件 14 passed。 | -| Runtime `make lint` 和完整 `make test` | pass | 8,846 passed、188 skipped、431 warnings、0 failed(419 秒)。使用隔离命令 `npm exec --yes --package @openai/codex@0.155.1 -- sh -c 'make lint && make test'`;未修改全局安装/配置。 | -| Runtime 静态检查 | pass / fail(基线) | F821/F811/F401 和新增测试 Ruff 通过。改动文件完整 Ruff 为 39 项、基线 40 项;Pyright 与基线同为 27 项错误,没有新增诊断类别/位置。完整静态门禁不是全绿。 | -| 密钥扫描与空白检查 | pass | Studio pre-commit gitleaks、Runtime 工作区差异 gitleaks、独立新增源码扫描和 `git diff --check`;未加入真实凭据。 | -| 真实 Lark/网关 E2E、PostgreSQL coverage、正式 CI | not_run | 需要获授权测试资源/新 Runtime 部署或 CI 环境;SQLite/模拟 API 检查不证明这些行为。 | -| 提交前 pre-commit/rebase 门禁 | pass | 用户随后授权本地提交。已 fetch 远端;Studio rebase 到 `upstream/main`、Runtime rebase 到 `origin/master` 均已是最新。Studio `uv run --extra dev pre-commit run --all-files` 五项钩子全部通过。无关的本地锁文件删除不纳入提交。未授权推送。 | - -首次完整 Runtime 回归发现三项国内租户品牌回归和一项本机 Codex 版本不匹配。已收窄国内校验并通过相关回归。仓库要求 Codex 0.155.1,应用自带版本为 0.162.0-alpha.17.2,最终全量使用隔离的固定版本。跳过项不视为已验证。frontend-design/ui-ux-pro-max 和 review-spec 技能不可用,采用现有 UI 模式及直接设计/浏览器审查。发布前仍需:构建部署新 Runtime 镜像,再针对实际网关验证 Lark 扫码/手动授权、私聊/群聊投递、流式和解绑。本次未部署资源。 - -### 后续线上观察 -用户反馈 Lark 授权页提示“链接已失效”。Runtime 能力已声明支持 Lark,但未观察到完成的 Lark 绑定。尚未取得失败授权链接/会话用于诊断,根因未确认。本次本地提交不宣称通过真实扫码/网关验收,也未解决该反馈。 - -同步后的提交检查:Studio 完整前端测试(1,389 项 Node + 116 项 React)及 6 项代理策略测试通过;Runtime `make lint` 和 372 项渠道/客户端/媒体/投递定向测试通过。生产差异未变,之前 8,846 项完整 Runtime 回归结果仍适用。 diff --git a/prd-spec/features/message-channel-binding/2026-09-15-message-channel-binding.md b/prd-spec/features/message-channel-binding/2026-09-15-message-channel-binding.md deleted file mode 100644 index d4e5a6532..000000000 --- a/prd-spec/features/message-channel-binding/2026-09-15-message-channel-binding.md +++ /dev/null @@ -1,61 +0,0 @@ -# DingTalk and WeCom binding - -[中文版](2026-09-15-message-channel-binding.zh.md) - -Change ID: message-channel-binding. Date: 2026-09-15. Status: approved. -Contract: [MPA channels](../../../specs/mpa-channels/README.md). - -## Evidence and scope -Studio currently renders unavailable placeholders for DingTalk and WeCom. MPA already has DingTalk authorization and WeCom Bot ID/Secret Gateway registration, but the durable Studio binding API only supports Feishu. The user explicitly approved enabling both providers and testing them. This change connects these existing capabilities across both repositories. SDK execution, deployment and changes to provider infrastructure are outside scope. - -## Requirements and scenarios -- FR-1: Selecting DingTalk offers QR authorization. Authorization completion registers the Gateway bot server-side; only successful persistence produces BOUND. Restart, expiry, owner isolation and uncertain registration preserve the existing Feishu guarantees. -- FR-2: Selecting WeCom offers a Bot ID and password-type Secret form. Credentials are sent to an authenticated server endpoint, never persisted in browser storage or returned in responses. Successful binding clears the form; errors remain visible. -- FR-3: All providers support diagnostics and unbinding. Group allowlists and group permission configuration apply only to Feishu; DingTalk and WeCom group messages do not require a local allowlist. Switching providers aborts pending requests and isolates resume IDs and UI state. Unsupported Runtime versions show an upgrade message. -- FR-4: Repeated identical registration reuses the existing bot. Ambiguous remote registration is reported without automatic retry. Diagnostics must not report another provider's delivery as this provider's success. - -## Design and boundaries -Reuse the existing Studio channel component with a provider parameter. Keep Feishu API compatibility, add DingTalk durable bindings and WeCom credential binding, advertise supported providers via capabilities. Runtime remains credential owner and authenticates every new endpoint through require_channel_admin. DingTalk polling is request driven with encrypted database state and fenced leases; legacy background pollers are disabled for this path. WeCom uses the existing serialized upsert and encrypted channel store. No secret values appear in API error details. Live provider tests require an isolated Runtime and accounts; local tests mock all external services. - -## Tasks, affected files and acceptance -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| FR-1 | T-1: MPA studio.py, channels.py and tests | AC-1: QR, completed binding, expiry, ownership and no credentials returned | MPA targeted pytest | -| FR-2 | T-2: MPA credential endpoint and config.py | AC-2: WeCom registers, rejects blanks, hides errors and secrets | MPA targeted pytest | -| FR-3 | T-3: RuntimeChannels.tsx, client.ts, locales, CSS, tests | AC-3: both tabs usable; cancellation, forms and permissions scoped | Vitest, npm test, build, real browser | -| FR-4 | T-4: config and observations | AC-4: idempotence and channel isolation | targeted pytest | - -## Review and risks -Direct design review performed because review-spec is unavailable: responsibilities, compatibility, authentication, encrypted storage, cancellation, failure semantics and paired-language equivalence reviewed; no unresolved design blocker. User approval: “帮我改,钉钉和企微的都要开启可以绑定”. External MPA repository edits require filesystem escalation. Existing unrelated changes are preserved. Runtime must be updated together with Studio; no deployment is authorized. Live testing is pending target configuration, so local evidence cannot establish real message delivery. - -## Verification record -Initial review: implementation pending at approval; final results below. Required gates: targeted MPA pytest, Ruff/Pyright changed Python, frontend Vitest and npm test, npm build, webui asset checks, browser flow and error/cancellation/keyboard/narrow checks. Live smoke: blocked (Runtime and test accounts not provided). - -Scope amendment approved 2026-09-15: user clarified “群聊限制只针对飞书”. FR-3/AC-3 now require hiding group permission forms and skipping permission reads for DingTalk/WeCom. Runtime admission checks enforce the allowlist only for Feishu. Existing group session isolation remains unchanged. Direct design review: no other auth or bot-binding checks change; add positive non-Feishu and negative Feishu regression tests. - -## Implementation and verification update -2026-09-15, both repositories' current uncommitted channel diff. FR-1 through FR-4 implemented, including the Feishu-only amendment. Local acceptance AC-1 through AC-4 verified. No commit, deployment or live message was made. Implementation scope is complete locally; live verification and existing repository static-analysis debt remain visible below. - -- pass: test-first evidence: original new UI tests failed 3 cases, original backend tests failed 3 cases; Feishu-only amendment failed 2 UI and 2 backend cases before implementation. -- pass: MPA channel regression 96 tests; after test typing cleanup, the 9 new backend cases passed again. Includes binding/ownership/persistence, credential validation and encryption, auth, remote error redaction, duplicate registration, bot-isolated observations, Feishu allowlist rejection and non-Feishu bypass. -- pass: frontend regression 1206 tests; channel component 14 tests (single worker), including both binding methods, old Runtime capabilities, unbinding, cancellation, provider isolation, IME and Feishu-only group controls. -- pass: production build (TypeScript and both Vite outputs); packaged assets verified: 104 files, 248 references. Only existing chunk-size warnings. -- pass: browser checks on real component with local simulated fetch: DingTalk pending to BOUND, WeCom credential to bound, Feishu-only group section, error/retry, loading/switching, keyboard and 480×850 layout. Temporary preview files/server/tab removed and viewport reset. Browser unbind click timed out in the dialog automation; both provider unbind paths are covered by component and backend tests, not claimed as browser-verified. -- pass: Ruff on studio.py, delivery.py, channel_control.py and new tests; Pyright on those files (including new tests), zero diagnostics. -- fail (existing findings): broader changed Python files retain Ruff 27 findings (channels.py 17, config.py 2, messages.py 8), with no added diagnostics compared to HEAD. Broader Pyright reports existing mixin/import/type issues in channels.py/config.py/messages.py; the newly introduced override mismatch was fixed. No unrelated source cleanup performed. -- pass: scoped whitespace checks, locale-key parity and paired documentation/relative links checked. No real credentials used; test secrets are explicit placeholders. -- not_run: full VeADK Python regression (no VeADK Python runtime changed); pre-commit/gitleaks (no commit requested; gitleaks executable unavailable). This is not represented as a passing secret-scan gate. -- blocked: real Runtime/provider smoke needs Runtime ID, region and isolated test accounts. Updating local Studio assets does not update a deployed MPA image. Operators must upgrade both sides and retain CHANNEL_STATE_ENCRYPTION_KEY before real binding. - -The MPA repository uses a dev dependency group rather than VeADK's `--extra dev`; the existing local interpreter was used to avoid changing either dependency lock or contacting external services. - -```bash -# MPA: repository-local interpreter; isolated from developer .env and external services -cd /tmp -PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=/Users/bytedance/Desktop/code/agentkit-mpa-agent /Users/bytedance/Desktop/code/agentkit-mpa-agent/.venv/bin/python -m pytest /Users/bytedance/Desktop/code/agentkit-mpa-agent/tests/test_multi_channel_binding.py /Users/bytedance/Desktop/code/agentkit-mpa-agent/tests/test_channel_studio.py /Users/bytedance/Desktop/code/agentkit-mpa-agent/tests/test_channel_binding_edges.py /Users/bytedance/Desktop/code/agentkit-mpa-agent/tests/test_channel_control_store.py /Users/bytedance/Desktop/code/agentkit-mpa-agent/tests/test_channel_service.py /Users/bytedance/Desktop/code/agentkit-mpa-agent/tests/test_channels_auth.py /Users/bytedance/Desktop/code/agentkit-mpa-agent/tests/test_channel_message_edges.py -p no:cacheprovider -o addopts= -q -# VeADK working directory -npm --prefix frontend test -(cd frontend && ./node_modules/.bin/vitest run tests/runtimeChannels.test.tsx --maxWorkers 1) -npm --prefix frontend run build -npm --prefix frontend run test:webui-assets -``` diff --git a/prd-spec/features/message-channel-binding/2026-09-15-message-channel-binding.zh.md b/prd-spec/features/message-channel-binding/2026-09-15-message-channel-binding.zh.md deleted file mode 100644 index 6d19e9a7c..000000000 --- a/prd-spec/features/message-channel-binding/2026-09-15-message-channel-binding.zh.md +++ /dev/null @@ -1,61 +0,0 @@ -# 钉钉和企业微信绑定 - -[English](2026-09-15-message-channel-binding.md) - -变更 ID:message-channel-binding。日期:2026-09-15。状态:approved。 -组件契约:[MPA channels](../../../specs/mpa-channels/README.zh.md)。 - -## 证据与范围 -Studio 当前为钉钉和企业微信显示未开放占位。MPA 已有钉钉授权以及企微 Bot ID/Secret 网关注册,但持久化 Studio 绑定接口仅支持飞书。用户明确同意开启两种渠道并测试。本次跨两个仓库联通既有能力。SDK 执行、部署和平台基础设施变更不在范围内。 - -## 需求与场景 -- FR-1:选择钉钉可扫码授权。授权完成后服务端注册网关机器人,仅持久化成功才返回 BOUND。重启、过期、用户隔离和注册结果不确定场景维持飞书现有保证。 -- FR-2:选择企微可填写 Bot ID 和密码类型 Secret。凭据只发送到鉴权服务端接口,不进入浏览器持久化或响应。成功清空表单,失败可见。 -- FR-3:各渠道均支持诊断和解绑。群白名单和群权限配置仅针对飞书,钉钉与企微群消息不要求本地白名单。切换渠道取消待处理请求,隔离恢复 ID 和界面状态。不支持的新接口显示升级提示。 -- FR-4:相同凭据重复注册复用既有机器人。远端注册结果不确定时明确报告,不自动重试。诊断不得将其他渠道的投递当作本渠道成功。 - -## 设计与边界 -给既有 Studio 渠道组件增加 provider 参数。保持飞书接口兼容,新增钉钉持久化绑定和企微凭据绑定,通过 capabilities 声明支持渠道。Runtime 持有凭据,新接口统一使用 require_channel_admin 鉴权。钉钉以请求驱动轮询,加密数据库状态并使用带代数校验的租约,此路径禁用旧的后台轮询。企微复用串行 upsert 和渠道加密存储。接口错误详情不得包含密钥。真实平台测试需要隔离 Runtime 和账号,本地测试模拟全部外部服务。 - -## 任务、影响文件与验收 -| 需求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| FR-1 | T-1:MPA studio.py、channels.py 和测试 | AC-1:扫码、完成绑定、过期、用户隔离且不返回凭据 | MPA 定向 pytest | -| FR-2 | T-2:MPA 凭据接口及 config.py | AC-2:企微注册、空值校验、错误和凭据脱敏 | MPA 定向 pytest | -| FR-3 | T-3:RuntimeChannels.tsx、client.ts、语言包、CSS、测试 | AC-3:两类 Tab 可用,取消、表单和权限按渠道隔离 | Vitest、npm test、build、真实浏览器 | -| FR-4 | T-4:config 和 observations | AC-4:幂等与渠道隔离 | 定向 pytest | - -## 评审与风险 -review-spec 不可用,已直接完成设计评审:职责、兼容、鉴权、加密存储、取消、失败语义及双语一致性均已检查,无未解决设计阻塞。用户批准:“帮我改,钉钉和企微的都要开启可以绑定”。外部 MPA 仓库写入需文件系统提权。保留已有无关修改。Runtime 需与 Studio 一起更新,未授权部署。真实测试待提供目标配置,本地证据不能证明实际消息投递。 - -## 验证记录 -初次评审:批准时待实施,最终结果见下文。必需检查:MPA 定向 pytest、变更 Python 的 Ruff/Pyright、前端 Vitest 与 npm test、npm build、webui 资源检查、浏览器正常/错误/取消/键盘/窄屏。真实 smoke:blocked(未提供 Runtime 和测试账号)。 - -2026-09-15 范围修订已获批准:用户明确“群聊限制只针对飞书”。FR-3/AC-3 要求隐藏钉钉/企微群权限表单并跳过权限读取,Runtime 准入只对飞书执行白名单检查。既有群会话隔离不变。直接设计评审:不改变其他鉴权或机器人绑定检查;补充非飞书允许及飞书拒绝回归测试。 - -## 实施与验证更新 -2026-09-15,两个仓库当前未提交的渠道 diff。FR-1 至 FR-4 已实施,包含仅飞书群限制修订;本地 AC-1 至 AC-4 已验证。未提交、部署或发送真实消息。本地实施范围已完成,真实验证及仓库既有静态检查问题如下保留。 - -- pass:测试先行证据:新增界面用例初始 3 项失败,后端初始 3 项失败;仅飞书群限制修订在实施前分别有 2 项界面和 2 项后端失败。 -- pass:MPA 渠道回归 96 项;测试类型整理后新增 9 项后端用例再次通过。覆盖绑定/所有者/持久化、凭据校验与加密、鉴权、远端错误脱敏、重复注册、机器人隔离观测、飞书白名单拒绝及非飞书跳过。 -- pass:前端回归 1206 项;渠道组件 14 项(单 worker),覆盖两种绑定方式、旧 Runtime 能力、解绑、取消、渠道隔离、IME 及仅飞书群配置。 -- pass:生产构建(TypeScript 和两套 Vite 输出);资源校验 104 文件、248 引用。仅有既有大包体警告。 -- pass:真实组件配合本地模拟 fetch 的浏览器检查:钉钉等待授权至 BOUND、企微凭据绑定、仅飞书群配置、错误/重试、加载/切换、键盘及 480×850 布局。临时预览文件/服务/标签页已清理,视口已恢复。浏览器解绑点击在对话框自动化中超时;两种渠道解绑由组件和后端测试覆盖,不声称浏览器验证通过。 -- pass:studio.py、delivery.py、channel_control.py 及新增测试的 Ruff;这些文件(含新增测试)的 Pyright 零诊断。 -- fail(既有问题):更大范围的变更 Python 文件仍有 Ruff 27 项(channels.py 17、config.py 2、messages.py 8),相比 HEAD 无新增诊断。更大范围 Pyright 报告 channels.py/config.py/messages.py 既有 mixin/导入/类型问题;本次新增的重写参数不一致已修复。未做无关源码清理。 -- pass:定向空白检查、语言包键一致性及双语文档/相对链接检查。未使用真实凭据,测试密钥均为显式占位值。 -- not_run:VeADK 全量 Python 回归(未改变 VeADK Python 运行逻辑);pre-commit/gitleaks(未请求提交,gitleaks 可执行程序不可用)。不将其称为密钥扫描通过。 -- blocked:真实 Runtime/平台 smoke 需要 Runtime ID、地域及隔离测试账号。更新本地 Studio 资源不会更新已部署 MPA 镜像。实际绑定前需同步升级两端并保留 CHANNEL_STATE_ENCRYPTION_KEY。 - -MPA 仓库使用 dev 依赖组而非 VeADK 的 `--extra dev`;使用既有本地解释器,避免改变任一依赖锁或访问外部服务。 - -```bash -# MPA: repository-local interpreter; isolated from developer .env and external services -cd /tmp -PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=/Users/bytedance/Desktop/code/agentkit-mpa-agent /Users/bytedance/Desktop/code/agentkit-mpa-agent/.venv/bin/python -m pytest /Users/bytedance/Desktop/code/agentkit-mpa-agent/tests/test_multi_channel_binding.py /Users/bytedance/Desktop/code/agentkit-mpa-agent/tests/test_channel_studio.py /Users/bytedance/Desktop/code/agentkit-mpa-agent/tests/test_channel_binding_edges.py /Users/bytedance/Desktop/code/agentkit-mpa-agent/tests/test_channel_control_store.py /Users/bytedance/Desktop/code/agentkit-mpa-agent/tests/test_channel_service.py /Users/bytedance/Desktop/code/agentkit-mpa-agent/tests/test_channels_auth.py /Users/bytedance/Desktop/code/agentkit-mpa-agent/tests/test_channel_message_edges.py -p no:cacheprovider -o addopts= -q -# VeADK working directory -npm --prefix frontend test -(cd frontend && ./node_modules/.bin/vitest run tests/runtimeChannels.test.tsx --maxWorkers 1) -npm --prefix frontend run build -npm --prefix frontend run test:webui-assets -``` diff --git a/prd-spec/features/message-channel-workspace/2026-09-15-message-channel-workspace.md b/prd-spec/features/message-channel-workspace/2026-09-15-message-channel-workspace.md deleted file mode 100644 index d9855fd39..000000000 --- a/prd-spec/features/message-channel-workspace/2026-09-15-message-channel-workspace.md +++ /dev/null @@ -1,50 +0,0 @@ -# Message channel workspace - -[中文](2026-09-15-message-channel-workspace.zh.md) - -- Change ID: message-channel-workspace -- Date: 2026-09-15 -- Status: implemented (UI scope) -- Contract: [MPA channels](../../../specs/mpa-channels/README.md) - -## Background and scope -The current uncommitted RuntimeChannels panel is embedded at the top of AgentWorkspace integrations. Diagnostics are a flat definition list. The user requested a sibling Message channels tab and Feishu, WeCom and DingTalk channel categories, then authorized implementation (企业微信,帮我改). - -## Requirements and scenarios -- FR-1: Opening Message channels from an existing Runtime shows a dedicated workspace, alongside Integrations and Versions. Integrations no longer renders channel management. -- FR-2: The workspace identifies Feishu, WeCom and DingTalk. Preserve working Feishu binding and permissions. Other channels must not imply successful setup or invoke Feishu endpoints; WeCom and DingTalk are explicitly unavailable in this UI scope. -- FR-3: Present binding, channel diagnostics and allowed groups as readable sections. Localize Gateway as 消息网关. Show gateway configuration, inbound route configuration and observed reply delivery independently; configuration is not proof of delivery. -- FR-4: Preserve polling cleanup, retry, form values on error and permission checks. Support keyboard navigation, IME, long names and narrow layouts. - -## Design and contract impact -Add channels to AgentSection and the shared section list. Extract a channel selector around the existing Feishu manager. Reuse UI tokens, Button and Select; no dependencies or server changes. Unavailable providers show an explicit unavailable state with no setup actions. The existing MPA channel HTTP, identity, storage and registration contracts remain unchanged. Add the navigation and availability presentation obligation to both component-spec languages. - -## Tasks and affected files -- T-1 / FR-1: AgentWorkspace.tsx, bilingual ui.json; standalone navigation. -- T-2 / FR-2–4: RuntimeChannels.tsx and CSS; channel selection and structured status/binding/groups. -- T-3: runtimeChannels.test.tsx and agentWorkspace.test.mjs; regressions, browser checks, build and generated assets. -- T-4: Reconcile bilingual design/spec and frontend README. - -## Acceptance and verification -- AC-1: Channels is a sibling section, absent from Integrations. -- AC-2: Channel categories are localized; unsupported providers cannot trigger Feishu mutations. -- AC-3: Real status values remain distinguishable and readable at wide/narrow widths. -- AC-4: Existing binding and cleanup regressions pass; keyboard, loading, empty, errors and retry verified. -Commands: frontend/node_modules/.bin/vitest run tests/runtimeChannels.test.tsx (from frontend); npm --prefix frontend test; npm --prefix frontend run build; npm --prefix frontend run test:webui-assets; git diff --check for changed scope. Browser checks use isolated simulated responses; no cloud binding or deletion is authorized. - -## Risks and review -Uncommitted work predates this task; preserve it. Built asset hashes change on rebuild. Do not change MPA services or global tooling. frontend-design and ui-ux-pro-max skills were unavailable at prescribed and searched locations; the user authorized proceeding after disclosure, so apply frontend/SPEC.md and existing patterns. review-spec is unavailable; direct review checked scope, interfaces, safety, cleanup, compatibility, tests and bilingual parity. No design blocker for navigation/Feishu changes. The optional clarification received no response; the UI-only assumption was announced before implementation. WeCom and DingTalk show unavailable states; implementing their server integration is outside this presentation change. - -## Verification record -2026-09-15, baseline HEAD f1aa2d75 plus pre-existing worktree changes; scope: channel navigation, presentation, locale keys, tests, docs and rebuilt assets. - -- pass: regression first run reproduced 3 new failures / 4 existing passes; final `cd frontend && ./node_modules/.bin/vitest run tests/runtimeChannels.test.tsx --maxWorkers 1`: 8 passed, including IME/Safari keyCode 229, keyboard selection, cleanup, error recovery and unavailable providers. -- pass: `npm --prefix frontend test`: 1206 passed. Updated the existing exact section-union assertion for the new channels section. -- pass: `npm --prefix frontend run build`: TypeScript and both Vite builds. Existing large-chunk warnings remain. -- pass: `npm --prefix frontend run test:webui-assets`: 104 files / 248 references. An earlier check ran before build completion and failed against transient output; rerun after successful build passed. -- pass: browser inspection of the real RuntimeChannels component with isolated mock responses: bound/empty, loading, configuration error and retry, binding pending, keyboard provider switching and 480px layout. Temporary preview files removed. No real cloud actions were taken. -- pass: scoped `git diff --check`, bilingual channel-key parity, paired designs and relative links. -- not_run: live Feishu registration/delivery and end-to-end cloud Agent navigation; no cloud operations authorized. Component browser checks do not prove these integrations. -- not_applicable: Python tests/Ruff/Pyright and pre-commit/branch synchronization; no Python changes or commit in this task. - -Review: no blocking UI findings remain. Existing backend changes and dependency-lock changes were preserved. Local preview initially hit sandbox EPERM; a narrowly scoped approved localhost launch succeeded. No machine-global configuration was changed. diff --git a/prd-spec/features/message-channel-workspace/2026-09-15-message-channel-workspace.zh.md b/prd-spec/features/message-channel-workspace/2026-09-15-message-channel-workspace.zh.md deleted file mode 100644 index 3c091edb6..000000000 --- a/prd-spec/features/message-channel-workspace/2026-09-15-message-channel-workspace.zh.md +++ /dev/null @@ -1,50 +0,0 @@ -# 消息渠道工作区 - -[English](2026-09-15-message-channel-workspace.md) - -- Change ID:message-channel-workspace -- 日期:2026-09-15 -- 状态:implemented(UI 范围) -- 契约:[MPA 渠道](../../../specs/mpa-channels/README.zh.md) - -## 背景与范围 -当前未提交的 RuntimeChannels 面板嵌在 AgentWorkspace 集成页顶部,诊断以平铺定义列表展示。用户要求新增与集成、版本平级的消息渠道,并分为飞书、企业微信、钉钉,随后授权实施(企业微信,帮我改)。 - -## 需求与场景 -- FR-1:已有 Runtime 的消息渠道打开独立工作区,与集成、版本平级;集成不再展示渠道管理。 -- FR-2:工作区区分飞书、企业微信、钉钉。保留可用的飞书绑定与权限操作;其他渠道不得暗示配置成功或调用飞书接口,本次 UI 范围内企业微信与钉钉明确标示为暂未开放。 -- FR-3:绑定、渠道诊断、允许群分别形成清晰区域。Gateway 中文改为消息网关。独立展示网关配置、入站路由配置和观测到的回复投递;配置不等于投递成功。 -- FR-4:保留轮询清理、重试、错误时表单值和权限校验。支持键盘、输入法、长名称和窄窗口。 - -## 设计与契约影响 -AgentSection 与共享栏目列表增加 channels,在现有飞书管理器外增加渠道选择。复用 UI tokens、Button、Select,不增加依赖或服务端修改。未开放渠道明确显示暂未开放,且没有配置操作。MPA 渠道 HTTP、身份、存储和注册契约不变;双语组件契约增加导航与开放状态的展示义务。 - -## 任务与影响文件 -- T-1 / FR-1:AgentWorkspace.tsx、双语 ui.json,独立导航。 -- T-2 / FR-2–4:RuntimeChannels.tsx 与 CSS,渠道切换及结构化状态、绑定、群列表。 -- T-3:runtimeChannels.test.tsx、agentWorkspace.test.mjs,回归、浏览器检查、构建与生成资源。 -- T-4:同步双语设计、契约及 frontend README。 - -## 验收与验证 -- AC-1:消息渠道与其他栏目平级,集成内无渠道面板。 -- AC-2:渠道名称已本地化,未支持渠道无法触发飞书修改。 -- AC-3:真实状态值可区分,在宽窄窗口中清晰可读。 -- AC-4:现有绑定和清理回归通过;验证键盘、加载、空状态、错误及重试。 -命令:frontend/node_modules/.bin/vitest run tests/runtimeChannels.test.tsx(在 frontend 执行);npm --prefix frontend test;npm --prefix frontend run build;npm --prefix frontend run test:webui-assets;变更范围 git diff --check。浏览器使用隔离模拟响应,不授权云端绑定或删除。 - -## 风险与审查 -当前任务之前已有未提交改动,予以保留。重新构建会更新产物哈希。不修改 MPA 服务或全局工具。frontend-design、ui-ux-pro-max 在规定及搜索路径均不可用,已披露后用户授权继续,因此依据 frontend/SPEC.md 和现有模式实施。review-spec 不可用,直接审查范围、接口、安全、清理、兼容、测试及双语一致性;导航与飞书改动无设计阻塞,可选澄清未收到回复,实施前已声明本次按页面调整范围推进。企业微信与钉钉显示暂未开放,其服务端集成不属于本次展示调整。 - -## 验证记录 -2026-09-15,基线 HEAD f1aa2d75 加既有工作区改动;范围:渠道导航、展示、语言键、测试、文档和重新构建的资源。 - -- pass:先执行回归复现 3 项新增失败 / 4 项既有通过;最终 `cd frontend && ./node_modules/.bin/vitest run tests/runtimeChannels.test.tsx --maxWorkers 1`:8 项通过,覆盖 IME/Safari keyCode 229、键盘选择、清理、错误恢复及未开放渠道。 -- pass:`npm --prefix frontend test`:1206 项通过。原有精确栏目联合类型断言已随新增 channels 更新。 -- pass:`npm --prefix frontend run build`:TypeScript 和两次 Vite 构建通过,仍有既有的大 chunk 警告。 -- pass:`npm --prefix frontend run test:webui-assets`:104 个文件 / 248 个引用。较早一次检查在构建结束前执行,因中间产物失败;构建成功后重跑通过。 -- pass:真实 RuntimeChannels 组件的隔离模拟响应浏览器验证,覆盖已绑定/空状态、加载、配置错误和重试、等待扫码、键盘渠道切换及 480px 布局。临时预览文件已删除,未执行真实云端操作。 -- pass:范围内 `git diff --check`、双语渠道键一致性、设计成对完整及相对链接检查。 -- not_run:真实飞书注册/投递及云端智能体导航端到端验证,未获云端操作授权;组件浏览器检查不证明这些集成。 -- not_applicable:Python 测试/Ruff/Pyright 及 pre-commit/分支同步,本任务未修改 Python,也未提交。 - -审查:无剩余阻塞性 UI 问题。保留既有后端和依赖锁文件改动。本地预览最初遇到沙箱 EPERM,经批准的限定 localhost 启动成功,未修改机器全局配置。 diff --git a/prd-spec/features/mpa-agent-info-rail/2026-09-18-mpa-agent-info-rail.md b/prd-spec/features/mpa-agent-info-rail/2026-09-18-mpa-agent-info-rail.md deleted file mode 100644 index ffb79ea4f..000000000 --- a/prd-spec/features/mpa-agent-info-rail/2026-09-18-mpa-agent-info-rail.md +++ /dev/null @@ -1,94 +0,0 @@ -# MPA conversation information rail - -- Change ID: `mpa-agent-info-rail` -- Date: 2026-09-18 -- Status: implemented; real-browser verification blocked -- Chinese: [Chinese design](2026-09-18-mpa-agent-info-rail.zh.md) -- Contract: [Studio MPA control plane](../../../specs/studio-mpa-control-plane/README.md) - -## Background and evidence - -`frontend/src/ui/AgentTopology.tsx` labels generic graph/draft instructions as AGENTS.md and combines generic skills with Session selections. The A2A adapter in `veadk/cli/cli_frontend.py` returns an empty skills list. The inspected local agentkit-mpa-agent revision `f14fa39` exposes `GET /api/v1/agents` with `agentsMd`; its Agent Card resource-topology extension advertises configured Skill Space resource IDs. Studio already exposes paginated `/web/skill-spaces/{space_id}/skills`. No live Runtime was queried. - -## Goals and non-goals - -Show authoritative MPA configuration and bound-space skills in the existing conversation rail. Hide the entire rail for non-MPA or unclassified agents. Preserve Session environment controls and remove temporary Session skill mounting. Allow permission-aware SKILL.md editing and add the required MPA read-only authentication endpoint. No editing of AGENTS.md, change of Skill Space bindings, provider deployment, or new dependency. - -## Requirements and scenarios - -- FR-1: Only a Runtime tagged `veadk:agent-type=mpa` receives MPA rail metadata. Generic/local/unknown agents have no rail or reserved rail width. -- FR-2: Read the current MPA `agentsMd` through the authenticated Runtime connection. Preserve document text; never substitute generic instructions. Empty content, unavailable API, denied access, and failed requests have distinct states and retry. -- FR-3: Read configured `skill-space` nodes from the advertised topology and enumerate their skills with the existing account-authorized SkillSpace API. Respect Runtime region, pagination, resource identity, and degraded results. Show name, description, count; temporary Session additions are hidden and excluded from MPA messages. -- FR-4: Switching Runtime cancels outstanding list requests and discards late results. Refresh retains current readable data where possible; access revocation clears it. Failures never become an empty-success list. Long text scrolls within the existing rail. -- FR-5: Preserve existing model/lifecycle capabilities, Session environment controls, normal chat, and non-MPA API behavior. Requests use existing server-managed credentials; only allowlisted fields enter the UI, never arbitrary upstream errors/configuration. - -## Design and contract impact - -The Runtime proxy recognizes tagged MPA metadata requests, including native app names and `a2a-default`, and extends its normalized AgentInfo with `agentCategory` and an optional typed `mpa` payload. A focused backend helper reads `/api/v1/studio/agent-info` with the server-resolved Runtime key and supports the legacy `/api/v1/agents` fallback, classifies failures, and extracts only configured space IDs from the Agent Card. Missing topology is unsupported, not an empty binding list. Existing generic A2A capability normalization remains in place. The browser's MPA rail controller owns paginated SkillSpace loading and refresh/cancellation; AgentInfoPanel remains presentation-only. AGENTS.md is rendered as escaped text with preserved line breaks. MPA detail retry reloads metadata. Existing skill-page clients gain an optional AbortSignal. The authenticated Skill document editor uses the existing version publication path; see the follow-up design and [document contract](../../../specs/studio-skill-document/README.md). No public SDK or generated Agent contract changes. CON-16/17 are implemented in the existing bilingual control-plane spec. - -## Tasks and acceptance - -| Task | Requirements | Acceptance | Verification | -| --- | --- | --- | --- | -| T-1 backend metadata | FR-1/2/5 | AC-1 native/A2A MPA tagged metadata, content and classified failures; generic A2A unchanged | Targeted Python metadata and Runtime proxy tests | -| T-2 frontend data/state | FR-2/3/4 | AC-2 multi-page skills, no stale data, partial/degraded/error/empty states and retry | Vitest API/controller tests | -| T-3 UI integration | FR-1/3/5 | AC-3 MPA-only rail, no temporary mounting, preserved environment controls | Panel tests, frontend regression, browser | -| T-4 reconcile | all | AC-4 bilingual docs, production assets, no new lint/type errors | i18n/build/assets, Ruff/Pyright, secret scan | - -## Risks and review - -SkillSpace region follows the selected Runtime's region, matching the current MPA deployment contract. Old Runtime images may lack the read API or topology; the UI explains unsupported data without inventing it. Existing SkillSpace authorization is retained. User approved the concrete plan with “帮我改”. Direct design review checked boundaries, cancellation, errors, compatibility, permission safety and bilingual equivalence; no blockers. The referenced frontend-design/ui-ux-pro-max and review-spec skills are not installed; use frontend/SPEC.md and direct review. No graphify index exists; implementation inspected directly. - -## Affected files - -`frontend/server/mpa_agent_info.py`, `veadk/cli/cli_frontend.py`, `frontend/src/adk/client.ts`, `frontend/src/create/skills/skillspace.ts`, `frontend/src/App.tsx`, `frontend/src/ui/AgentTopology.tsx`, `frontend/src/ui/mpa-agent-info/MpaAgentInfoRail.tsx`, `frontend/src/styles.css`, both workspaceTools locale catalogs, corresponding Python/Node/Vitest tests, `frontend/package.json`, `frontend/README.md`, this PRD pair, the control-plane spec pair, and `veadk/webui` generated assets. - -## Initial verification record (before the follow-up) - -Tested on 2026-09-18: working diff against `f497cc6d` on `feat/from-main`, including the metadata helper/proxy, client, rail/controller, locale strings, docs and generated assets. - -| Check | Result | Evidence | -| --- | --- | --- | -| Test-first regression | pass | New helper/controller tests first failed because their modules did not exist, then passed after implementation. | -| `uv run --extra dev pytest tests/frontend/server/test_mpa_agent_info.py tests/cli/test_frontend_runtime_proxy.py` | pass | 91 passed; HTTP failures, malformed data, timeout, allowlist, tagged/native/virtual MPA and generic A2A compatibility. | -| `npm --prefix frontend test` | pass | 1208 Node tests and 11 MPA Vitest tests; the new suite is included in the standard test command. Covers pagination, cancellation/late results, partial/error retry, revoked access, degraded data and escaped document text. | -| `npm --prefix frontend run check:i18n` | pass | Both locales and 21 namespaces consistent. | -| `npm --prefix frontend run build` | pass | TypeScript and both production bundles built; existing bundle-size warning remains. | -| `npm --prefix frontend run test:webui-assets` | pass | 104 packaged files, 248 internal references. | -| Ruff for four changed Python files | pass | `uv run --extra dev --with 'ruff==0.11.12' ruff check ...`; uses repository hook version. | -| Pyright for four changed Python files | fail (baseline) | `uv run --extra dev --with pyright pyright --pythonpath .venv/bin/python ...`; 56 diagnostics, identical to HEAD by file/rule/message (36 in cli_frontend, 20 in existing proxy tests), no new diagnostics. New helper and its tests have none. | -| Gitleaks changed-file snapshot, including untracked code and generated assets | pass | `gitleaks dir --config .gitleaks.toml --redact`; no leaks found. | -| Source whitespace and paired docs/links | pass | `git diff --check -- ':!veadk/webui'`; paired identifiers and relative links reviewed. | -| Real-browser normal/loading/empty/error/retry/keyboard/narrow-window verification | blocked | Local isolated preview prepared; browser rejected access because the admin-enforced safety policy could not be verified. No bypass attempted. Temporary preview and server removed. Component tests do not replace visual/browser evidence. | -| IME; harness coverage; generated Python/deployment; full SDK regressions | not_applicable | No input, sidecar, code generation, deployment or SDK changes; full frontend and Runtime proxy suites cover the affected shared surface. | -| Real cloud/provider smoke | not_run | No live service authorized or invoked; all network regression tests use mocks. | -| Pre-commit all-files; remote synchronization; commit/push | not_run | No commit requested. These remain required before a later authorized commit; targeted Ruff and changed-file secret scanning ran now. | - -Direct implementation review found no unresolved code blockers after checking MPA gating, native/virtual app compatibility, text escaping, credential allowlisting, empty/error distinctions, multi-page skill loading, abort cleanup and bilingual contract alignment. T-1/T-2 are verified; T-3 browser evidence and final acceptance remain blocked as above. T-4 documents/assets are synchronized, with baseline Pyright failures recorded rather than hidden. No production Runtime result is claimed. - -## Approved follow-up: authentication and Skill document editing - -On 2026-09-18 the user requested: complete authenticated AGENTS.md reads, remove temporary mounting, and support editing bound Skill Space SKILL.md. The requirements above reflect this updated scope. Direct design review approves the following bounded implementation; existing JWT authorization and SkillSpace ownership rules remain unchanged. - -- Add a read-only `/api/v1/studio/agent-info` in agentkit-mpa-agent, authenticating a dedicated `X-MPA-Studio-Key` against the current MPA's stored Runtime key with constant-time comparison. Missing/invalid keys fail closed; return only name, description, model and agentsMd. Studio supplies only its server-resolved Runtime key. Never copy an API key into a JWT header. Old images can use the existing JWT endpoint when available; otherwise require upgrade. -- Remove the MPA temporary-skill list and picker, and exclude previously persisted temporary selections from MPA outgoing messages. Keep environment controls and generic Agent behavior. -- Bound skill rows retain space/region/skill/version identity. Open a dialog for the latest SKILL.md with server-provided write permission. Save creates and publishes a new version to the selected space through the existing version service, retaining all other archive members, binary bytes and file attributes. Shared/review spaces remain read-only. Show that this updates shared configuration and may require runtime cache refresh/new sessions. -- New authenticated GET/PUT `/web/skill-management/spaces/{space_id}/skills/{skill_id}/document` returns `content`, `baseVersion`, `canUpdate`; PUT accepts `content` and `baseVersion`, rejects stale versions with 409 under the existing process-local upload lock, and validates archive size/frontmatter/unchanged name. There is no provider compare-and-swap; external writers can still race after validation. Do not claim distributed transaction isolation. -- Dialog supports cancel, keyboard focus/Escape, read-only, error/retry and retained unsaved text. Save is explicit, guarded against duplicate clicks; closing is disabled while saving. Switching agents unmounts editor and ignores late responses. No automatic provider writes or deployments are performed during implementation. -- Affected additional files: MPA router/new metadata route and tests; Studio skill document service/routes and version upload guard, new editor client/UI/tests, existing rail/panel/App, locales, docs and assets. Required tests: missing/wrong credential and allowlist, no MPA temporary selections, editor permission/save/error, byte-preserving archive edit, stale version/name denial. Re-run affected backend, frontend, type/build, lint, secret checks; record browser/service limitations. - -The MPA Runtime image must be updated for this endpoint; changing only Studio cannot deploy the authentication fix. No deployment or commit is authorized by this implementation request. - -## Follow-up verification and delivery - -Tested 2026-09-18 against Studio `f497cc6d` (`feat/from-main`) and MPA `f14fa39` (`feat/mcp-skill-adap`), including all uncommitted changes described above. The user explicitly approved authentication completion, removal of temporary mounts and skill editing; direct review covered auth boundaries, ownership, ZIP integrity, permission denial, stale versions, cancellation and bilingual equivalence. - -- **pass**: `npm --prefix frontend test` — 1208 Node tests and 17 component tests. `npm --prefix frontend run check:i18n`, `npm --prefix frontend run build`, and `npm --prefix frontend run test:webui-assets` (104 files / 248 references). -- **pass**: `uv run --extra dev pytest tests/frontend/server/test_mpa_agent_info.py tests/frontend/server/skills tests/cli/test_frontend_runtime_proxy.py` — 290 passed; covers the full Skill service and Runtime proxy suites. -- **pass**: In MPA, `.venv/bin/python -m pytest tests/test_studio_metadata.py tests/test_auth.py tests/test_channels_auth.py -q` — 29 passed. New metadata endpoint, optional null fields, missing/wrong keys, no write route and existing auth compatibility. -- **pass**: Repository-pinned Ruff 0.11.12 on changed Python code/tests in both repositories. MPA Pyright on its three changed Python files: zero diagnostics. Studio Pyright: **fail (baseline)**, same 56 existing diagnostics in cli_frontend/proxy tests; no diagnostics in the new or edited Skill/metadata modules. -- **pass**: Redacted Gitleaks scan of both repositories' changed files including untracked additions and generated assets; no leaks. Source whitespace and bilingual relative links checked. -- **blocked**: Real browser verification remains unavailable following the browser's admin-policy verification rejection. Automated component tests cover editing, retry, readonly, duplicate-save lock, cancellation and IME/Escape, but are not visual/browser evidence. -- **not_run**: Live cloud editing, MPA deployment and production end-to-end verification. Tests use isolated simulated services. Both repos remain uncommitted; pre-commit all-files and branch synchronization are still required before any later authorized commit. No SDK or sidecar contract change requires their broader gates. - -Acceptance: code for authenticated reads, bound-only skill display and permission-aware editing is implemented and covered by local tests. Browser acceptance and live Runtime verification are outstanding. To enable AGENTS.md on a running agent, publish the updated MPA image while preserving `mpa_meta.runtime_api_key`, then upgrade Studio; old Runtime images receive an explicit upgrade message if their legacy JWT route cannot be used. Save errors can have uncertain provider outcomes; check the latest version before retrying. No live Runtime was changed by this task. diff --git a/prd-spec/features/mpa-agent-info-rail/2026-09-18-mpa-agent-info-rail.zh.md b/prd-spec/features/mpa-agent-info-rail/2026-09-18-mpa-agent-info-rail.zh.md deleted file mode 100644 index 061bd78ff..000000000 --- a/prd-spec/features/mpa-agent-info-rail/2026-09-18-mpa-agent-info-rail.zh.md +++ /dev/null @@ -1,94 +0,0 @@ -# MPA 会话信息侧栏 - -- 变更 ID:`mpa-agent-info-rail` -- 日期:2026-09-18 -- 状态:已实现;真实浏览器验证受阻 -- 英文:[English design](2026-09-18-mpa-agent-info-rail.md) -- 契约:[Studio MPA 控制面](../../../specs/studio-mpa-control-plane/README.zh.md) - -## 背景与证据 - -`frontend/src/ui/AgentTopology.tsx` 将通用 graph/draft instruction 标为 AGENTS.md,并混合通用技能与 Session 选择。`veadk/cli/cli_frontend.py` 的 A2A 适配返回空技能列表。已检查的本地 agentkit-mpa-agent `f14fa39` 提供 `GET /api/v1/agents`,包含 `agentsMd`;其 Agent Card resource-topology 扩展发布已配置的技能空间资源 ID。Studio 已有分页 `/web/skill-spaces/{space_id}/skills`。未查询真实 Runtime。 - -## 目标与非目标 - -在现有会话侧栏展示权威 MPA 配置及绑定空间技能。非 MPA 或类型未知智能体隐藏整个侧栏。保留 Session 环境控制,移除临时 Session 技能挂载。允许按权限编辑 SKILL.md,并补充 MPA 只读鉴权接口。不编辑 AGENTS.md、不修改技能空间绑定、不部署服务、不新增依赖。 - -## 需求与场景 - -- FR-1:仅带有 `veadk:agent-type=mpa` 标签的 Runtime 返回 MPA 侧栏元信息。通用、本地及未知智能体不展示侧栏,也不预留侧栏宽度。 -- FR-2:通过已鉴权的 Runtime 连接读取当前 MPA 的 `agentsMd`。保留文档正文,不使用通用 instruction 替代。空内容、接口不支持、权限不足和请求失败分别展示并支持重试。 -- FR-3:从已发布拓扑读取 configured 的 `skill-space` 节点,通过现有账户授权 SkillSpace API 枚举技能。处理 Runtime 地域、分页、资源标识和降级结果。展示名称、描述、数量;Session 临时添加的技能隐藏且不发送到 MPA。 -- FR-4:切换 Runtime 取消列表请求并丢弃迟到响应。刷新尽可能保留可读数据;权限收回则清除。错误不能伪装成空列表。长文本在现有侧栏内滚动。 -- FR-5:保留既有模型与生命周期能力、Session 环境控制、普通聊天及非 MPA API 行为。沿用服务端管理的凭据,仅白名单字段进入 UI,不返回任意上游错误或配置。 - -## 设计与契约影响 - -Runtime 代理识别带 MPA 标签的元信息请求,同时覆盖原生应用名及 `a2a-default`,在归一化 AgentInfo 中添加 `agentCategory` 和可选的类型化 `mpa` 数据。独立后端辅助模块使用服务端解析的 Runtime key 读取 `/api/v1/studio/agent-info` 并支持旧 `/api/v1/agents` 回退、分类失败,并仅从 Agent Card 提取已配置的空间 ID。缺失拓扑表示不支持,而非空绑定列表。保留既有通用 A2A 能力归一化。浏览器 MPA 侧栏控制组件负责技能空间分页、刷新和取消;AgentInfoPanel 只展示数据。AGENTS.md 以转义文本展示并保留换行。正文重试重新加载元信息。现有技能分页客户端增加可选 AbortSignal。受认证的技能正文编辑器复用既有版本发布流程,详见后续设计及[正文契约](../../../specs/studio-skill-document/README.zh.md)。不改变公开 SDK 或生成 Agent 契约。在既有双语控制面规范中实现 CON-16/17。 - -## 任务与验收 - -| 任务 | 需求 | 验收 | 验证 | -| --- | --- | --- | --- | -| T-1 后端元信息 | FR-1/2/5 | AC-1 原生/A2A MPA 标签元信息、正文及错误分类;通用 A2A 不变 | 定向 Python 元信息及 Runtime 代理测试 | -| T-2 前端数据和状态 | FR-2/3/4 | AC-2 多页技能、无过期数据、部分/降级/错误/空状态及重试 | Vitest API/控制组件测试 | -| T-3 UI 集成 | FR-1/3/5 | AC-3 仅 MPA 侧栏、取消临时挂载、环境控制保留 | 面板测试、前端回归及浏览器 | -| T-4 对齐交付 | 全部 | AC-4 双语文档、生产资源、无新增 lint/type 错误 | i18n/build/assets、Ruff/Pyright、密钥扫描 | - -## 风险与评审 - -技能空间地域沿用所选 Runtime 地域,与当前 MPA 部署契约一致。旧镜像可能缺少读取接口或拓扑,UI 明确展示不支持,不伪造数据。保留现有 SkillSpace 授权。用户通过“帮我改”批准具体方案。直接设计评审覆盖边界、取消、错误、兼容、权限安全及双语一致性,无阻塞问题。引用的 frontend-design/ui-ux-pro-max 和 review-spec 技能未安装,按 frontend/SPEC.md 及直接评审执行。无 graphify 索引,已直接检查实现。 - -## 影响文件 - -`frontend/server/mpa_agent_info.py`、`veadk/cli/cli_frontend.py`、`frontend/src/adk/client.ts`、`frontend/src/create/skills/skillspace.ts`、`frontend/src/App.tsx`、`frontend/src/ui/AgentTopology.tsx`、`frontend/src/ui/mpa-agent-info/MpaAgentInfoRail.tsx`、`frontend/src/styles.css`、两份 workspaceTools 语言文案、对应 Python/Node/Vitest 测试、`frontend/package.json`、`frontend/README.md`、本双语 PRD、双语控制面契约及 `veadk/webui` 构建产物。 - -## 初始验证记录(后续调整前) - -验证日期为 2026-09-18:`feat/from-main` 上相对 `f497cc6d` 的工作区差异,覆盖元信息辅助模块/代理、客户端、侧栏/控制组件、双语文案、文档及构建产物。 - -| 检查 | 结果 | 证据 | -| --- | --- | --- | -| 先失败的回归测试 | pass | 新增辅助模块/控制组件测试先因模块不存在失败,实现后通过。 | -| `uv run --extra dev pytest tests/frontend/server/test_mpa_agent_info.py tests/cli/test_frontend_runtime_proxy.py` | pass | 91 项通过;覆盖 HTTP 失败、非法数据、超时、白名单、MPA 标签/原生/虚拟应用及通用 A2A 兼容。 | -| `npm --prefix frontend test` | pass | 1208 项 Node 测试和 11 项 MPA Vitest 测试通过;新套件已接入标准测试命令。覆盖分页、取消/迟到响应、部分失败重试、权限撤销、降级数据及正文转义。 | -| `npm --prefix frontend run check:i18n` | pass | 两种语言和 21 个命名空间一致。 | -| `npm --prefix frontend run build` | pass | TypeScript 和两个生产包构建通过;保留已有包体积提示。 | -| `npm --prefix frontend run test:webui-assets` | pass | 104 个打包文件、248 个内部引用。 | -| 四个变更 Python 文件的 Ruff | pass | `uv run --extra dev --with 'ruff==0.11.12' ruff check ...`;使用仓库 hook 指定版本。 | -| 四个变更 Python 文件的 Pyright | fail(基线) | `uv run --extra dev --with pyright pyright --pythonpath .venv/bin/python ...`;56 条诊断,按文件/规则/消息与 HEAD 完全一致(cli_frontend 36 条,既有代理测试 20 条),无新增诊断。新增辅助模块及其测试无诊断。 | -| Gitleaks 扫描变更文件快照,包含未跟踪代码及构建产物 | pass | `gitleaks dir --config .gitleaks.toml --redact`;未发现泄露。 | -| 源码空白及双语文档/链接 | pass | `git diff --check -- ':!veadk/webui'`;已核对双语标识和相对链接。 | -| 真实浏览器正常/加载/空/错误/重试/键盘/窄屏验收 | blocked | 已准备隔离本地预览;浏览器因无法验证管理员安全策略拒绝访问,未尝试绕过。临时预览和服务器已清理。组件测试不能替代视觉/浏览器证据。 | -| IME;harness 覆盖率;生成 Python/部署;完整 SDK 回归 | not_applicable | 不修改输入、sidecar、代码生成、部署或 SDK;完整前端及 Runtime 代理套件覆盖受影响共享边界。 | -| 真实云/提供方 smoke | not_run | 未授权或调用真实服务;网络回归均使用模拟。 | -| 全量 pre-commit;远端同步;提交/推送 | not_run | 本次未要求提交;未来获授权提交前仍需执行。当前已执行定向 Ruff 和变更文件密钥扫描。 | - -直接实现审查已核对 MPA 可见性、原生/虚拟应用兼容、正文转义、凭据白名单、空/错误区分、多页技能读取、取消清理及双语契约一致性,无未解决代码阻断项。T-1/T-2 已验证;T-3 的浏览器证据与最终验收仍受上述阻碍。T-4 文档/产物已同步,明确记录基线 Pyright 失败而不掩盖。不声称已验证生产 Runtime 效果。 - -## 已批准后续:鉴权与技能正文编辑 - -2026-09-18 用户要求补齐 AGENTS.md 认证读取、取消临时挂载并支持编辑绑定技能空间的 SKILL.md。上文要求已按此范围更新。直接设计审查批准以下限定范围;既有 JWT 鉴权和技能空间归属规则不变。 - -- agentkit-mpa-agent 新增只读 `/api/v1/studio/agent-info`,使用专用 `X-MPA-Studio-Key` 与当前 MPA 存储的 Runtime key 做常量时间比较。缺失或错误凭据拒绝访问;只返回 name、description、model、agentsMd。Studio 仅使用服务端解析的 Runtime key,不能将 API key 填入 JWT 头。旧镜像可使用已有 JWT 接口,否则需要升级。 -- 移除 MPA 临时技能列表和选择器,MPA 消息也不再发送此前持久化的临时选择。保留环境操作和通用 Agent 行为。 -- 绑定技能行保留空间/地域/技能/版本标识。弹窗展示最新 SKILL.md 及服务端写权限。保存通过既有版本服务创建新版本并发布到所选空间,保留其他压缩包成员、二进制字节和文件属性。共享/审核空间保持只读。提示更新共享配置且 Runtime 缓存刷新或新会话后才可能生效。 -- 新增受认证 GET/PUT `/web/skill-management/spaces/{space_id}/skills/{skill_id}/document`;返回 `content`、`baseVersion`、`canUpdate`。PUT 接收 `content` 和 `baseVersion`,在既有进程内上传锁中用 409 拒绝过期版本,校验包大小、frontmatter、名称不变。提供方没有原子版本比较交换,外部写入仍可能在校验后竞态,不能声称具备分布式事务隔离。 -- 弹窗支持取消、键盘焦点/Escape、只读、错误重试及保留未保存正文。显式保存并防止重复点击,保存中禁止关闭。切换智能体卸载编辑器并忽略迟到响应。实现验证期间不自动写真实提供方或部署。 -- 新增影响文件:MPA 路由/新元信息接口及测试;Studio 技能正文服务/路由与版本上传检查、新编辑客户端/界面/测试、既有侧栏/面板/App、双语文案、文档和产物。测试覆盖缺失/错误凭据和白名单、MPA 不发送临时技能、编辑权限/保存/错误、归档字节保留、过期版本/名称拒绝;重跑相关后端、前端、类型/构建、lint、密钥检查,并记录浏览器/服务限制。 - -MPA Runtime 需要更新镜像才能获得该接口,仅修改 Studio 无法部署鉴权修复。本次实现要求未授权部署或提交。 - -## 后续验证与交付 - -验证日期为 2026-09-18,基线为 Studio `f497cc6d`(`feat/from-main`)及 MPA `f14fa39`(`feat/mcp-skill-adap`),覆盖上述未提交变更。用户明确批准补齐鉴权、取消临时挂载和编辑技能;直接审查覆盖鉴权边界、归属、ZIP 完整性、拒绝权限、过期版本、取消及双语一致性。 - -- **pass**:`npm --prefix frontend test`,1208 项 Node 测试和 17 项组件测试通过。`npm --prefix frontend run check:i18n`、`npm --prefix frontend run build`、`npm --prefix frontend run test:webui-assets`(104 文件 / 248 引用)通过。 -- **pass**:`uv run --extra dev pytest tests/frontend/server/test_mpa_agent_info.py tests/frontend/server/skills tests/cli/test_frontend_runtime_proxy.py`,290 项通过;覆盖完整技能服务及 Runtime 代理套件。 -- **pass**:MPA 仓库 `.venv/bin/python -m pytest tests/test_studio_metadata.py tests/test_auth.py tests/test_channels_auth.py -q`,29 项通过,覆盖新元信息接口、可空字段、缺失/错误密钥、无写路由及既有鉴权兼容。 -- **pass**:两个仓库变更 Python 代码/测试的仓库指定 Ruff 0.11.12 检查。MPA 三个变更 Python 文件 Pyright 无诊断。Studio Pyright 为 **fail(基线)**:cli_frontend/代理测试仍有相同的 56 条既有诊断;新增和修改的技能/元信息模块无诊断。 -- **pass**:两个仓库变更文件(含未跟踪新增文件和构建产物)的脱敏 Gitleaks 扫描未发现泄露。源码空白及双语相对链接已检查。 -- **blocked**:浏览器此前因管理员策略无法校验而拒绝访问,真实浏览器验收仍不可用。组件测试覆盖编辑、重试、只读、重复保存锁、取消及 IME/Escape,但不等于视觉/浏览器证据。 -- **not_run**:真实云编辑、MPA 部署和生产端到端验证。测试使用隔离模拟服务。两个仓库均未提交;未来获授权提交前仍需全量 pre-commit 和分支同步。本次不改变 SDK 或 sidecar 契约,无需其更广门禁。 - -验收状态:认证读取、仅展示绑定空间技能、按权限编辑的代码已实现并由本地测试覆盖;浏览器验收和真实 Runtime 验证仍待完成。要让现有智能体读取 AGENTS.md,需要发布更新后的 MPA 镜像并保留 `mpa_meta.runtime_api_key`,同时更新 Studio;旧镜像无法使用旧 JWT 接口时将明确提示升级。保存失败可能对应提供方结果未确定,重试前需检查最新版本。本次未修改真实 Runtime。 diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-10-mpa-agent-oneclick-provision-design.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-10-mpa-agent-oneclick-provision-design.md deleted file mode 100644 index 94eba8dda..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-10-mpa-agent-oneclick-provision-design.md +++ /dev/null @@ -1,221 +0,0 @@ -# One-Click mpa-agent Provisioning from a Prebuilt Image Design - -## Metadata - -- **Change ID:** `mpa-agent-oneclick-provision` -- **Created / Revised:** 2026-09-10 / 2026-09-10 -- **Lifecycle status:** `approved` -- **Language counterpart:** [2026-09-10-mpa-agent-oneclick-provision-design.zh.md](2026-09-10-mpa-agent-oneclick-provision-design.zh.md) -- **Related component specs:** none created yet; a new CLI contract surface is introduced by this change (see §6). No existing component contract under `specs/` is modified. -- **Predecessor / Successor:** none. - -## 1. Overview - -### 1.1 Problem / Background - -`mpa-agent` (`~/workspace/bytedance/mpa/mpa-agent`) is a FastAPI + veadk enterprise agent that ships as an AgentKit image (`Dockerfile` `CMD ["python", "main.py"]`, `agentkit.yaml`). Standing up one runnable instance today requires an out-of-band control-plane record: at startup `app/services/mpa_meta.py` calls the Arkclaw TOP action `GetMpaInstanceConf` (`app/client/arkclaw.py`, service `arkclaw`, version `2026-03-01`) to resolve a `mi-*` instance config (`account_id`, `resource_account_id`, `runtime_id`, `public_endpoint`, `private_endpoint`, `runtime_api_key`, `apig_instance_id`, and optional `ov_resource_id` / `ov_api_key`). - -A repository-wide search across `arkclaw`, `mpa`, and `veadk-python` confirms the **create side of that record does not exist in any local checkout** — only the read client, its consumer (`app/services/mpa_meta.py`), tests, and `docs/mpa-agent-api.md` reference `GetMpaInstanceConf`. Consequently there is no self-contained way to provision a working `mpa-agent` from the veadk side. - -At the same time the required backing resources are already externally managed: - -- **PostgreSQL:** veadk never creates an RDS instance or database. `veadk/memory/short_term_memory_backends/postgresql_backend.py` only runs `CREATE SCHEMA IF NOT EXISTS`; the deploy IAM policy (`veadk/cli/frontend_deploy_policy.py`) grants `rds_postgresql:DescribeDBInstances/DescribeDBAccounts/DescribeDatabases` and no create action. The database is a precondition; the runtime auto-creates its tables. -- **OpenViking:** `mpa-agent` consumes an OpenViking Server over HTTP via `OPENVIKING_URL` / `OPENVIKING_RESOURCE_ID` / `OPENVIKING_API_KEY` (`app/core/config.py`); it does not create the `ov-*` resource. veadk has no Python `CreateMemorydbInstance` implementation (`veadk/integrations/ve_viking_db_memory/ve_viking_db_memory.py` exposes only collection/message/search actions). The resource is a precondition. - -### 1.2 Goals - -Provide a self-contained, control-plane-independent way to stand up one `mpa-agent` instance and chat with it from AgentKit Studio, using a prebuilt `mpa-agent` image plus externally supplied PostgreSQL and OpenViking parameters, without modifying the `mpa-agent` image or code. - -- Add a `veadk mpa` CLI command group with `veadk mpa create` that deploys a specified image and wires all runtime configuration from external parameters. -- Remove the hard dependency on the unavailable `GetMpaInstanceConf` create side by seeding the `mpa_meta` table so the runtime's own startup skips the TOP call. -- Verify the deployed instance is healthy and Studio-connectable, and that existing `mpa-agent` capabilities (REST sessions, per-session Codex sandboxes, IM/Feishu group chat) continue to work. - -### 1.3 Non-Goals - -- Not creating PostgreSQL RDS instances/databases or OpenViking `ov-*` resources (external preconditions; see §1.1). -- Not implementing or calling a control-plane `CreateMpaInstance` action (deferred; tracked as an open question in §8). -- Not modifying `mpa-agent` source, image, or its public HTTP API. -- Not creating identity resources. In the veadk provisioning scenario the `arkclaw-{space}-userpool/-client/-workload` resources are **not used**: startup identity init is disabled with `IDENTITY_STARTUP_ENABLED=false`, and `CLAW_SPACE_ID` is adapted from the account id (see FR-10). -- Not building OpenViking collection creation into this command (the OpenViking Server path is consumed as-is). - -## 2. Scenarios - -### Scenario 1: Operator provisions an instance from an image - -**Given** a prebuilt `mpa-agent` image URL, an existing PostgreSQL database (host/port/db/user/password), an existing OpenViking resource (`url`/`resource_id`/`api_key`), model credentials, and identity/AgentKit identifiers. -**When** the operator runs `veadk mpa create` with those parameters. -**Then** the CLI deploys the image to VeFaaS behind APIG, seeds the `mpa_meta` row, injects the runtime env, and reports the public endpoint, the A2A agent-card URL, and the runtime API key location, with `/health` and `/readiness` passing. - -### Scenario 2: Chat from Studio - -**Given** a successfully provisioned instance with a reachable A2A agent-card at `/.well-known/agent-card.json`. -**When** the operator connects it as a remote agent in AgentKit Studio (`veadk studio`) and sends a message. -**Then** Studio exchanges A2A `message/send` / `message/stream` with the instance and renders the streamed reply, reusing the same veadk Agent/Runner and PostgreSQL session store. - -### Scenario 3: Different sessions get different sandboxes - -**Given** a provisioned instance with the Codex sandbox tool enabled (`AGENTKIT_TOOL_ID` set, `DISABLE_CODEX_SANDBOX` not `true`). -**When** two distinct sessions each trigger a `sandbox_task` delegation. -**Then** each session gets its own Codex Worker sandbox derived from `sha256(app_name:user_id:session_id)` (`app/integrations/agentkit_sandbox.py:derive_sandbox_session_id`), unchanged by this feature. - -### Scenario 4: Feishu bot group chat - -**Given** `FEISHU_APP_ID` / `FEISHU_APP_SECRET` supplied interactively (or omitted for later QR binding). -**When** the instance starts. -**Then** if supplied, the runtime auto-upserts a Feishu channel binding and registers the IM Gateway bot (`app/services/channel/service.py`); if omitted, QR binding remains available; group chat works via `POST /api/v1/channels/events`. - -### Scenario 5: Existing veadk commands unaffected - -**Given** the new `mpa` command group is registered. -**When** the operator runs any existing command (`veadk deploy`, `veadk frontend`, `veadk studio`, `veadk agentkit`, `veadk kb`, ...). -**Then** behavior is identical to before this change. - -## 3. Functional Requirements - -- **FR-1 — New `mpa` command group, additive.** A new `veadk mpa` `click` group is registered in `veadk/cli/cli.py` alongside existing commands without altering any of them; the console entry point (`pyproject.toml` `veadk = "veadk.cli.cli:veadk"`) is unchanged. -- **FR-2 — External parameters.** `veadk mpa create` accepts, via options and/or env fallbacks: image URL and container registry; provider/region; PostgreSQL connection (`PGHOST/PGPORT/PGDATABASE/PGUSER/PGPASSWORD/PGSSLMODE/PGCHANNELBINDING`) or an equivalent DSN; OpenViking (`OPENVIKING_URL`, `OPENVIKING_RESOURCE_ID`, `OPENVIKING_API_KEY`); model (`MODEL_AGENT_PROVIDER/API_BASE/API_KEY/NAME`); identity (`CLAW_SPACE_ID`, `MPA_AGENT_ID`, `IDENTITY_REGION`); AgentKit (`AGENTKIT_TOOL_ID`, `AGENTKIT_TOOL_REGION`, `SKILL_SPACE_ID`); auth method; and optional Feishu (`FEISHU_APP_ID`, `FEISHU_APP_SECRET`). -- **FR-3 — Image deployment with key auth.** The command deploys the specified image to VeFaaS behind APIG by reusing `veadk/integrations/ve_faas/ve_faas.py:VeFaaS.deploy_image()` with key auth enabled. Because `deploy_image()` today neither enables key auth nor returns the gateway/key, it is extended in a backward-compatible way (new optional `enable_key_auth: bool = False` parameter; unchanged default behavior) so that, when requested, it enables key auth in `_create_application` and returns the public URL, application id, function id, plus the APIG gateway id and the key-auth API key. Existing callers that omit the new parameter are unaffected (AC-10). -- **FR-4 — `mpa_meta` seeding to skip the TOP call.** Using the provided PostgreSQL connection, the command writes the `mpa_meta` row so that `app/services/mpa_meta.py:mpa_instance_conf_ready()` is satisfied and `GetMpaInstanceConf` is skipped at startup. Required fields and their veadk-scenario sources: - - `account_id`: resolved from the deploy credentials' account id. - - `resource_account_id`: same account id (single-account veadk scenario). - - `runtime_id`: the VeFaaS application id returned by `deploy_image`. - - `public_endpoint`: the released application system URL (`framework.url.system_url`). - - `private_endpoint`: set equal to `public_endpoint` in the veadk scenario (public endpoint is authoritative; see FR-11). This keeps the field non-empty so `mpa_instance_conf_ready()` passes and the runtime's `choose_endpoint` resolves a usable base. - - `runtime_api_key`: the key-auth API key from FR-3. - - `apig_instance_id`: the APIG gateway id that veadk auto-creates/reuses together with the function (obtained from the release `CloudResource` `framework.triggers[0].DetailedConfig.GatewayId`, mirroring `VeFaaS.get_application_route`). Confirmed: veadk provisions APIG alongside the function; the gateway id is not an external precondition. - - Seeding is idempotent (fill-empty semantics equivalent to the runtime's `MpaMetaStore.fill_empty`), must not overwrite non-empty fields, and must assert all seven fields are non-empty before writing (otherwise abort without leaving a half-usable instance). -- **FR-5 — Runtime env injection.** All resolved configuration is set as VeFaaS function environment variables (create + release), matching the keys `mpa-agent` reads at startup (`.env.example`, `docs/mpa-agent-api.md` §9). This includes `IDENTITY_STARTUP_ENABLED=false` (FR-10), `MPA_CODEX_WORKER_DEFAULT_MODEL=` so delegated Codex turns do not fall back to the literal `auto`, and `MPA_CODEX_WORKER_ENDPOINT_PREFERENCE`/endpoint choice consistent with the public-endpoint decision (FR-11). -- **FR-6 — Post-deploy verification.** After release the command probes `GET /health` and `GET /readiness` and confirms the A2A agent-card is reachable at `/.well-known/agent-card.json`; failures are reported with actionable detail and do not leave secrets in logs. -- **FR-7 — Studio chat enablement.** The command prints the exact endpoint, agent-card URL, and API-key handling needed to connect the instance as a remote A2A agent in `veadk studio`. Chat, multi-session/multi-sandbox, and IM group chat rely only on the runtime and are not gated by this command. -- **FR-8 — Interactive, optional Feishu secret.** When `--feishu-app-id` is provided without a secret, the CLI prompts for `FEISHU_APP_SECRET` with hidden input; Feishu is fully optional; the secret never appears in stdout or logs. -- **FR-9 — Non-regression of `mpa-agent`.** No change to `mpa-agent` source or image is performed by this feature. The selected image remains responsible for its REST/A2A session consistency, Codex event projection, scheduled-task, and IM behavior. Provisioning must not claim full compatibility when real E2E exposes an image-level failure; it reports the image/tag and the blocking evidence instead. -- **FR-10 — Identity adaptation (no arkclaw identity resources).** The veadk scenario does not use `arkclaw-{space}-userpool/-client/-workload`. The command injects `IDENTITY_STARTUP_ENABLED=false` so startup identity initialization short-circuits, derives `CLAW_SPACE_ID` from the account id, and sets `A2A_TIP_VERIFY_ENABLED=false` because no TIP issuer exists in this topology. The Runtime remains protected by mandatory APIG key auth; disabling the inner TIP gate permits Studio's server-side authenticated proxy to chat without exposing the Runtime key to the browser. -- **FR-11 — Public endpoint in veadk scenario.** The provisioning flow treats the released public application URL as authoritative for `public_endpoint`, `private_endpoint` (mirrored), the agent-card base, and the Codex worker endpoint preference. No private-network endpoint is required. - -## 4. Design and Contract Impact - -### 4.1 Responsibilities and boundaries - -The feature is a veadk-side **orchestration + configuration** command. It owns: parameter collection/validation, image deployment via the existing VeFaaS/APIG integrations, `mpa_meta` seeding, env assembly, and verification. It does **not** own: creating PostgreSQL/OpenViking/identity resources, or the `mi-*` control-plane record. - -### 4.2 Startup contract that drives the design - -`mpa-agent` startup (`app/main.py` lifespan → `get_mpa_meta_service().initialize()`): - -1. `ensure_schema()` creates the `mpa_meta` table if absent. -2. Requires non-empty `MPA_AGENT_ID`. -3. If the cached `mpa_meta` row already has all seven required fields (`mpa_instance_conf_ready`), it **skips `GetMpaInstanceConf`**; otherwise it calls the TOP action, which fails without a control-plane `mi-*` record. -4. Lazily ensures the IM Gateway service from `apig_instance_id` (`ensure_im_gateway_service`). - -Seeding the row (FR-4) is therefore the mechanism that makes provisioning self-contained under the current architecture. - -### 4.3 Chosen approach vs. alternatives - -- **Chosen — seed `mpa_meta` + inject env (this design).** Uses only locally available capabilities; no dependency on an unavailable control-plane action. The CLI computes the seven fields from the deploy result plus provided parameters. The seeding step is written behind a small internal function boundary so a future control-plane backend can replace it without touching the command flow. -- **Alternative A — call a control-plane `CreateMpaInstance`.** Rejected for now: the action is absent from every local checkout; its name, fields, version, and IAM requirements are unknown. Tracked in §8 as the eventual "正统" path. -- **Alternative B — patch `mpa-agent` to make `GetMpaInstanceConf` optional.** Rejected: violates the non-goal of not modifying `mpa-agent`. - -### 4.4 Interfaces - -New CLI surface (additive): - -```text -veadk mpa create \ - --image \ - --registry-name \ - [--provider volcengine|byteplus] [--region cn-beijing] \ - --mpa-agent-id mi-xxxx \ - [--claw-space-id csi-xxxx] # optional; defaults to csi- (FR-10) \ - --pg-host ... --pg-port 5432 --pg-database ... --pg-user ... --pg-password ... \ - [--pg-sslmode require] [--pg-channel-binding require] \ - --model-provider openai --model-api-base ... --model-api-key ... --model-name ... \ - [--openviking-url ...] [--openviking-resource-id ov-xxx] [--openviking-api-key ...] \ - --agentkit-tool-id ... [--agentkit-tool-region cn-beijing] [--skill-space-id ...] \ - [--auth-method none|api-key|oauth2] \ - [--feishu-app-id cli_xxx] [--feishu-app-secret ...] \ - [--app-name mpa-agent] [--gateway-name ...] [--dry-run] -``` - -`--dry-run` resolves and prints the plan (deploy target, env keys, `mpa_meta` fields — secrets masked) without mutating cloud or database state. - -### 4.5 State, data, and idempotency - -- The only datastore this command writes is the external PostgreSQL `mpa_meta` table (one row keyed by `mpa_agent_id`), using fill-empty semantics equivalent to `app/stores/mpa_meta.py:MpaMetaStore.fill_empty`, with a pre-write assertion that all seven required fields are non-empty. -- The veadk-side `mpa_meta` table definition (columns, types, server defaults) mirrors `app/stores/mpa_meta.py` exactly. A comment records that a change to the runtime's table shape must be synchronized here. -- Re-running `veadk mpa create` with the same `--app-name` reuses the existing VeFaaS application (deploy is idempotent by app name in `VeFaaS.deploy`/`deploy_image`) and does not overwrite non-empty `mpa_meta` fields. - -### 4.6 Permissions and security - -- Requires cloud credentials able to deploy VeFaaS + APIG and reach the container registry (see `frontend_deploy_policy.py` for the reference action set; RDS is `Describe`-only and unused here). -- Secrets (`--pg-password`, `--model-api-key`, `--openviking-api-key`, `--feishu-app-secret`, runtime API key) are prompted with hidden input when interactive, masked in `--dry-run` output, and never logged. This aligns with the `secretless runtime` intent in `arkclaw-mpa`: the CLI injects only what the runtime needs and does not write control-plane credentials into the runtime. - -### 4.7 Compatibility and affected callers - -- Additive CLI command; no existing veadk command, public import, or generated project changes (FR-1, FR-9). -- `VeFaaS.deploy_image` gains one optional keyword parameter `enable_key_auth: bool = False` and an extended return value; the default preserves the current three-tuple behavior for existing callers (`CloudAgentEngine`, Studio). This is verified by a compatibility guard test (AC-10). The change is additive-only: no positional parameter is reordered. -- No `mpa-agent` API or image change. -- Studio connects via the already-exposed A2A surface (`app/a2a/app.py`, agent-card at `/.well-known/agent-card.json` with `A2A_MOUNT_PATH=/`). - -## 5. Edge Cases - -| Scenario | Handling | -| --- | --- | -| `mpa_meta` row already complete | Skip seeding; log skip; proceed to verification (idempotent). | -| Partial `mpa_meta` row exists | Fill only empty required fields; never overwrite non-empty values. | -| Any required field unresolved before seeding | Abort with a specific message; do not write a partial row. | -| PostgreSQL unreachable | Fail fast before deploy with a clear connection error; no cloud mutation. | -| OpenViking params omitted | Allowed; runtime runs without OpenViking tools (they mount only when URL+key set). | -| Feishu secret omitted | Allowed; skip auto-bind; QR binding remains available at runtime. | -| `--claw-space-id` omitted | Derive `csi-` (FR-10); `IDENTITY_STARTUP_ENABLED=false` keeps identity init off, so no arkclaw identity pool is required. | -| Image sync still in progress on release | Reuse `deploy_image` bounded retry (`_release_application` retry loop). | -| Deploy fails midway | Report the failure with redacted logs; resources created before the failure are surfaced for manual cleanup (parity with `deploy`'s cleanup is a follow-up if `deploy_image` lacks it). | -| Re-run with same app name | Reuse existing application; update code/env; do not duplicate the function. | -| `--dry-run` | Print resolved plan with masked secrets; perform no cloud or DB writes. | -| Missing required parameter | Fail with a specific message naming the missing option/env. | - -## 6. Affected Files - -- `veadk/cli/cli_mpa.py` — new `mpa` command group and `create` command (orchestration, parameter handling, `mpa_meta` seeding for the first version, verification). -- `veadk/cli/cli.py` — import and `veadk.add_command(mpa)` (single additive registration). -- `veadk/integrations/ve_faas/ve_faas.py` — extend `deploy_image` with `enable_key_auth` + extended return (backward compatible); reuse `_create_application`, `get_application_route`, release `CloudResource` parsing. -- `veadk/integrations/ve_apig/ve_apig.py` — reused, not modified. -- `tests/cli/test_cli_mpa.py` — new unit tests with stubbed cloud SDK and in-memory/SQLite `mpa_meta`. -- `prd-spec/features/mpa-agent-oneclick-provision/` — this bilingual design. -- Component spec: no existing `specs//` contract changes; if a durable CLI contract is desired later, add `specs/mpa-provisioning/` in a follow-up (out of scope here). - -## 7. Acceptance Criteria and Tracking - -| Requirement | Task | Acceptance Criterion | Test / Verification Command | Result / Evidence | -| --- | --- | --- | --- | --- | -| FR-1 | T-1 | `AC-1`: `veadk mpa --help` lists `create`; all pre-existing top-level commands still resolve. | `uv run veadk mpa --help` and `uv run veadk --help` | pass (2026-09-11): mpa lists create; `veadk --help` shows mpa + existing commands | -| FR-2, FR-8 | T-2 | `AC-2`: Missing required params fail with a named error; `--feishu-app-id` without secret prompts hidden input; secrets masked in `--dry-run`. | `uv run pytest tests/cli/test_cli_mpa.py -k params` | pass (2026-09-11): test_cli_mpa 6/6 | -| FR-3 | T-3 | `AC-3`: With a stubbed VeFaaS/APIG client, deploy is invoked via `deploy_image(enable_key_auth=True)` and public endpoint, apig gateway id, and key are captured. | `uv run pytest tests/cli/test_cli_mpa.py -k deploy` | pass (2026-09-11): full_flow_orchestration + deploy_image key-auth | -| FR-3 | T-3b | `AC-10`: `deploy_image()` called without `enable_key_auth` returns the unchanged three-tuple and enables no key auth (compatibility guard). | `uv run pytest tests/ -k deploy_image_compat` | pass (2026-09-11): test_deploy_image_compat_returns_three_tuple_without_key_auth | -| FR-4, FR-11 | T-4 | `AC-4`: Seeding writes the seven required fields (`private_endpoint` mirrors `public_endpoint`); a pre-complete row is left unchanged; a partial row is filled only in empty fields; any unresolved field aborts before write. | `uv run pytest tests/cli/test_cli_mpa.py -k seed` | pass (2026-09-11): test_mpa_meta_seed 6/6 | -| FR-5, FR-10 | T-5 | `AC-5`: Assembled env contains every key `mpa-agent` reads at startup, including `IDENTITY_STARTUP_ENABLED=false` and `CLAW_SPACE_ID=csi-` when not supplied; no secret is logged. | `uv run pytest tests/cli/test_cli_mpa.py -k env` | pass (2026-09-11): test_mpa_provision_env 6/6 | -| FR-6 | T-6 | `AC-6`: Verification passes only when `/health`, `/readiness`, and the agent-card all respond; failures are reported without secrets. | `uv run pytest tests/cli/test_cli_mpa.py -k verify` | pass (2026-09-11): test_mpa_verify 4/4 | -| FR-7 | T-7 | `AC-7`: Command output includes the agent-card URL and Studio connection guidance; documented manual Studio chat succeeds against a real instance, with distinct sessions getting distinct sandboxes and Feishu group chat working. | Manual: `veadk studio` connect + one message | partial/blocked: guidance and distinct session→sandbox mapping verified; selected image fails the final A2A result on an ADK session revision conflict; Feishu lacks credentials/bot installation and a dedicated customer APIG id | -| FR-9 | T-8 | `AC-8`: No file under the `mpa-agent` repo is modified by this change; existing veadk command tests pass. | `git -C ~/workspace/bytedance/mpa/mpa-agent status --porcelain` empty; `uv run pytest tests/cli` | pass (2026-09-11): mpa-agent porcelain empty; tests/cli 1273 passed, 4 skipped | -| all | T-9 | `AC-9`: Repository gates pass (pre-commit + unit tests). | `pre-commit run -a` and `uv run pytest` | pass (2026-09-11): pre-commit on changed files (ruff-check/format/gitleaks) Passed; new suites 25/25 | - -Check states use `pass` / `fail` / `blocked` / `not_run` / `not_applicable`. Record execution date, tested diff range, and brief results when run. - -## 8. Risks and Open Questions - -- **OQ-1 — Control-plane `CreateMpaInstance` (not required for this design).** The create side of the `mi-*` record is absent from all local checkouts. It is intentionally out of scope: the veadk-version `mpa-agent` needs no control-plane record. Route A (CLI-side `mpa_meta` seeding) is the accepted delivery path. Kept only as a note for a future non-veadk deployment. -- **OQ-2 — CLI-side `mpa_meta` seeding — RESOLVED (accepted).** The user approved having the CLI deliver the `mi-*` meta configuration directly; the veadk-version `mpa-agent` runs without a control-plane record. This is the accepted mechanism. -- **Resolved — APIG instance id source.** Confirmed in code: veadk provisions/reuses the APIG serverless gateway together with the function during deploy (`VeFaaS._create_application` + `ve_apig` create/reuse; gateway id readable from release `CloudResource` `framework.triggers[0].DetailedConfig.GatewayId`, per `get_application_route`). `apig_instance_id` is therefore produced by the deploy flow, not an external input. -- **Resolved — Endpoint choice.** In the veadk scenario `public_endpoint` is authoritative and `private_endpoint` mirrors it (FR-11); no private network endpoint is required. -- **Resolved — Identity.** The veadk scenario sets `IDENTITY_STARTUP_ENABLED=false` and derives `CLAW_SPACE_ID=csi-`, so `arkclaw-{space}-userpool/-client/-workload` are not used (FR-10); startup identity init short-circuits at `app/identity/service.py:166`. -- **Risk — secretless runtime.** Injected env carries model/OpenViking/runtime keys; keep control-plane credentials out of the runtime and mask all secrets, consistent with `arkclaw-mpa` guidance. -- **Risk — `deploy_image` failure cleanup.** Unlike `deploy`, `deploy_image` lacks `keep_failed_deploy`/rollback. Either add parity cleanup when extending it, or surface created resources for manual cleanup (edge-case table). - -## 9. Review and Delivery Record - -- Review status: reviewed via `review-spec` on 2026-09-10; P0 findings folded back into FR-3/FR-4/FR-10/FR-11, AC-10, and §4.7. Status advanced to `approved` on 2026-09-10. -- Resolved during review: APIG instance id source (deploy-produced), endpoint choice (public), identity adaptation (`IDENTITY_STARTUP_ENABLED=false` + `csi-`). -- User approval: granted on 2026-09-10 — CLI delivers the `mi-*` meta configuration directly (route A); the veadk-version `mpa-agent` requires no control-plane record. OQ-2 accepted; OQ-1 out of scope. -- Open blockers: Studio A2A final response in the selected image; Feishu credentials/bot installation and dedicated customer APIG id. The orchestration-alignment design records the current details. -- Executed checks: route A unit/CLI coverage passed; later alignment verification is tracked by the 2026-09-11 design and evidence reports. -- Remaining scope: see the 2026-09-11 orchestration-alignment design. diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-10-mpa-agent-oneclick-provision-design.zh.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-10-mpa-agent-oneclick-provision-design.zh.md deleted file mode 100644 index 7375c0df6..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-10-mpa-agent-oneclick-provision-design.zh.md +++ /dev/null @@ -1,221 +0,0 @@ -# 基于预构建镜像一键创建 mpa-agent 设计文档 - -## 元信息 - -- **Change ID:** `mpa-agent-oneclick-provision` -- **创建 / 修订:** 2026-09-10 / 2026-09-10 -- **生命周期状态:** `approved` -- **对应语言版本:** [2026-09-10-mpa-agent-oneclick-provision-design.md](2026-09-10-mpa-agent-oneclick-provision-design.md) -- **相关组件 spec:** 暂未创建;本次变更引入一个新的 CLI 契约面(见 §6),未修改 `specs/` 下任何现有组件契约。 -- **前序 / 后继版本:** 无。 - -## 1. 概述 - -### 1.1 问题 / 背景 - -`mpa-agent`(`~/workspace/bytedance/mpa/mpa-agent`)是基于 FastAPI + veadk 的企业 Agent,以 AgentKit 镜像形式交付(`Dockerfile` `CMD ["python", "main.py"]`,`agentkit.yaml`)。当前拉起一个可运行实例依赖一条外部控制面记录:启动时 `app/services/mpa_meta.py` 调用 Arkclaw TOP 的 `GetMpaInstanceConf`(`app/client/arkclaw.py`,service `arkclaw`,version `2026-03-01`)解析 `mi-*` 实例配置(`account_id`、`resource_account_id`、`runtime_id`、`public_endpoint`、`private_endpoint`、`runtime_api_key`、`apig_instance_id`,以及可选的 `ov_resource_id` / `ov_api_key`)。 - -对 `arkclaw`、`mpa`、`veadk-python` 三处仓库做全量搜索确认:**该记录的创建对端在任何本地 checkout 中都不存在** —— 只有读客户端、其消费方(`app/services/mpa_meta.py`)、测试和 `docs/mpa-agent-api.md` 引用了 `GetMpaInstanceConf`。因此当前没有一条自包含的、可在 veadk 侧创建可运行 `mpa-agent` 的路径。 - -同时,所需的后端资源本就由外部管理: - -- **PostgreSQL:** veadk 从不创建 RDS 实例或 database。`veadk/memory/short_term_memory_backends/postgresql_backend.py` 只执行 `CREATE SCHEMA IF NOT EXISTS`;部署 IAM 策略(`veadk/cli/frontend_deploy_policy.py`)只授予 `rds_postgresql:DescribeDBInstances/DescribeDBAccounts/DescribeDatabases`,无任何创建动作。数据库是前提,表结构由运行时自动创建。 -- **OpenViking:** `mpa-agent` 通过 `OPENVIKING_URL` / `OPENVIKING_RESOURCE_ID` / `OPENVIKING_API_KEY`(`app/core/config.py`)以 HTTP 方式消费 OpenViking Server,不创建 `ov-*` 资源。veadk 无 Python 版 `CreateMemorydbInstance` 实现(`veadk/integrations/ve_viking_db_memory/ve_viking_db_memory.py` 仅暴露 collection/message/search 动作)。资源是前提。 - -### 1.2 目标 - -在**不修改 `mpa-agent` 镜像或代码**的前提下,提供一条自包含、不依赖控制面的路径:使用预构建的 `mpa-agent` 镜像,加上外部提供的 PostgreSQL 与 OpenViking 参数,拉起一个 `mpa-agent` 实例,并在 AgentKit Studio 中可聊天使用。 - -- 新增 `veadk mpa` CLI 命令组及 `veadk mpa create`,部署指定镜像并从外部参数注入全部运行时配置。 -- 通过播种 `mpa_meta` 表,使运行时自身启动跳过不可用的 `GetMpaInstanceConf`,从而解除硬依赖。 -- 验证实例健康且 Studio 可连接,并确认 `mpa-agent` 既有能力(REST 会话、每会话独立 Codex 沙箱、IM/飞书群聊)不受影响。 - -### 1.3 非目标 - -- 不创建 PostgreSQL RDS 实例/database,也不创建 OpenViking `ov-*` 资源(外部前提,见 §1.1)。 -- 不实现或调用控制面 `CreateMpaInstance` 动作(延后;作为待决项记录于 §8)。 -- 不修改 `mpa-agent` 源码、镜像或其公开 HTTP API。 -- 不创建身份资源。在 veadk 创建场景下,`arkclaw-{space}-userpool/-client/-workload` **不使用**:以 `IDENTITY_STARTUP_ENABLED=false` 关闭启动身份初始化,并由 account id 适配出 `CLAW_SPACE_ID`(见 FR-10)。 -- 不将 OpenViking collection 创建纳入本命令(OpenViking Server 路径按现状消费)。 - -## 2. 用户场景 - -### 场景 1:运维方基于镜像创建实例 - -**Given** 一个预构建的 `mpa-agent` 镜像 URL、一个已存在的 PostgreSQL 数据库(host/port/db/user/password)、一个已存在的 OpenViking 资源(`url`/`resource_id`/`api_key`)、模型凭据,以及身份/AgentKit 标识。 -**When** 运维方带上这些参数运行 `veadk mpa create`。 -**Then** CLI 将镜像部署到 VeFaaS + APIG,播种 `mpa_meta` 行,注入运行时 env,并输出 public endpoint、A2A agent-card URL 与 runtime API key 的位置,且 `/health`、`/readiness` 通过。 - -### 场景 2:在 Studio 中聊天 - -**Given** 一个成功创建、`/.well-known/agent-card.json` 可达的实例。 -**When** 运维方在 AgentKit Studio(`veadk studio`)中以远端 Agent 方式连接并发送消息。 -**Then** Studio 与实例通过 A2A `message/send` / `message/stream` 交互并渲染流式回复,复用同一 veadk Agent/Runner 与 PostgreSQL 会话存储。 - -### 场景 3:不同会话对应不同沙箱 - -**Given** 一个启用了 Codex 沙箱工具的实例(已设 `AGENTKIT_TOOL_ID`,`DISABLE_CODEX_SANDBOX` 非 `true`)。 -**When** 两个不同会话各自触发一次 `sandbox_task` 委派。 -**Then** 每个会话获得由 `sha256(app_name:user_id:session_id)` 派生的独立 Codex Worker 沙箱(`app/integrations/agentkit_sandbox.py:derive_sandbox_session_id`),本功能不改变该行为。 - -### 场景 4:飞书机器人群聊 - -**Given** 交互式提供 `FEISHU_APP_ID` / `FEISHU_APP_SECRET`(或省略,留待后续扫码绑定)。 -**When** 实例启动。 -**Then** 若已提供,运行时自动 upsert 飞书渠道绑定并注册 IM Gateway Bot(`app/services/channel/service.py`);若省略,扫码绑定仍可用;群聊通过 `POST /api/v1/channels/events` 工作。 - -### 场景 5:既有 veadk 命令不受影响 - -**Given** 新增的 `mpa` 命令组已注册。 -**When** 运维方运行任一既有命令(`veadk deploy`、`veadk frontend`、`veadk studio`、`veadk agentkit`、`veadk kb` 等)。 -**Then** 行为与本次变更前完全一致。 - -## 3. 功能需求 - -- **FR-1 — 新增 `mpa` 命令组,纯增量。** 在 `veadk/cli/cli.py` 中与现有命令并列注册一个新的 `veadk mpa` `click` 组,不改动任何既有命令;控制台入口(`pyproject.toml` `veadk = "veadk.cli.cli:veadk"`)保持不变。 -- **FR-2 — 外部参数。** `veadk mpa create` 通过选项和/或 env 回退接收:镜像 URL 与容器仓库;provider/region;PostgreSQL 连接(`PGHOST/PGPORT/PGDATABASE/PGUSER/PGPASSWORD/PGSSLMODE/PGCHANNELBINDING`)或等价 DSN;OpenViking(`OPENVIKING_URL`、`OPENVIKING_RESOURCE_ID`、`OPENVIKING_API_KEY`);模型(`MODEL_AGENT_PROVIDER/API_BASE/API_KEY/NAME`);身份(`CLAW_SPACE_ID`、`MPA_AGENT_ID`、`IDENTITY_REGION`);AgentKit(`AGENTKIT_TOOL_ID`、`AGENTKIT_TOOL_REGION`、`SKILL_SPACE_ID`);鉴权方式;以及可选飞书(`FEISHU_APP_ID`、`FEISHU_APP_SECRET`)。 -- **FR-3 — 带 key 鉴权的镜像部署。** 命令复用 `veadk/integrations/ve_faas/ve_faas.py:VeFaaS.deploy_image()` 并启用 key 鉴权将指定镜像部署到 VeFaaS + APIG。由于 `deploy_image()` 当前既不启用 key 鉴权也不返回网关/密钥,故对其做向后兼容扩展(新增可选参数 `enable_key_auth: bool = False`,默认行为不变):当请求时在 `_create_application` 中启用 key 鉴权,并返回 public URL、application id、function id,以及 APIG 网关 id 与 key 鉴权 API key。未传该参数的既有调用方不受影响(AC-10)。 -- **FR-4 — 播种 `mpa_meta` 以跳过 TOP 调用。** 使用所提供的 PostgreSQL 连接写入 `mpa_meta` 行,使 `app/services/mpa_meta.py:mpa_instance_conf_ready()` 成立,从而在启动时跳过 `GetMpaInstanceConf`。必填字段及其在 veadk 场景下的取值来源: - - `account_id`:由部署凭据的 account id 解析。 - - `resource_account_id`:同一 account id(单账号 veadk 场景)。 - - `runtime_id`:`deploy_image` 返回的 VeFaaS application id。 - - `public_endpoint`:release 后的 application system URL(`framework.url.system_url`)。 - - `private_endpoint`:在 veadk 场景下置为与 `public_endpoint` 相同(public endpoint 为权威;见 FR-11)。这样该字段非空以满足 `mpa_instance_conf_ready()`,运行时 `choose_endpoint` 也能解析出可用 base。 - - `runtime_api_key`:来自 FR-3 的 key 鉴权 API key。 - - `apig_instance_id`:veadk 随函数一起自动创建/复用的 APIG 网关 id(从 release 的 `CloudResource` `framework.triggers[0].DetailedConfig.GatewayId` 获取,与 `VeFaaS.get_application_route` 一致)。已确认:veadk 会随函数一并创建 APIG,网关 id 不是外部前提。 - - 播种需幂等(fill-empty 语义等价于运行时的 `MpaMetaStore.fill_empty`),不得覆盖非空字段,且写入前必须断言七个字段均非空(否则中止,不留半可用实例)。 -- **FR-5 — 运行时 env 注入。** 将所有解析出的配置设置为 VeFaaS 函数环境变量(创建 + release),键与 `mpa-agent` 启动时读取的一致(`.env.example`、`docs/mpa-agent-api.md` §9)。这包括 `IDENTITY_STARTUP_ENABLED=false`(FR-10)、`MPA_CODEX_WORKER_DEFAULT_MODEL=`(避免 Codex 委派回退到字面值 `auto`)以及与 public endpoint 决策一致的 `MPA_CODEX_WORKER_ENDPOINT_PREFERENCE`/endpoint 选择(FR-11)。 -- **FR-6 — 部署后验证。** release 后命令探测 `GET /health` 与 `GET /readiness`,并确认 A2A agent-card 在 `/.well-known/agent-card.json` 可达;失败时给出可操作的详情,且日志不含密钥。 -- **FR-7 — Studio 聊天可用。** 命令输出连接实例所需的精确 endpoint、agent-card URL 与 API key 处理方式,以便在 `veadk studio` 中以远端 A2A Agent 方式连接。聊天、多会话/多沙箱、IM 群聊仅依赖运行时,不受本命令门禁限制。 -- **FR-8 — 交互式、可选的飞书密钥。** 当提供 `--feishu-app-id` 但未提供密钥时,CLI 以隐藏输入方式提示 `FEISHU_APP_SECRET`;飞书完全可选;密钥不出现在 stdout 或日志中。 -- **FR-9 — `mpa-agent` 无回归。** 本功能不修改 `mpa-agent` 源码或镜像;所选镜像仍需自行保证 REST/A2A 会话一致性、Codex 事件投影、定时任务与 IM 行为。若真实 E2E 暴露镜像级故障,创建流程与验收报告必须记录镜像/tag 及阻断证据,不得宣称完全兼容。 -- **FR-10 — 身份适配(不使用 arkclaw 身份资源)。** veadk 场景不使用 `arkclaw-{space}-userpool/-client/-workload`。命令注入 `IDENTITY_STARTUP_ENABLED=false` 使启动身份初始化短路,由 account id 派生 `CLAW_SPACE_ID`,并设置 `A2A_TIP_VERIFY_ENABLED=false`,因为该拓扑不存在 TIP 签发方。Runtime 仍由强制 APIG key auth 保护;关闭内层 TIP 门禁后,Studio 服务端鉴权代理可以聊天,且 Runtime key 不会暴露给浏览器。 -- **FR-11 — veadk 场景取 public endpoint。** 创建流程以 release 后的 public application URL 作为 `public_endpoint`、`private_endpoint`(镜像同值)、agent-card base 及 Codex worker endpoint 偏好的权威值。不需要任何私网 endpoint。 - -## 4. 设计与契约影响 - -### 4.1 职责与边界 - -本功能是 veadk 侧的**编排 + 配置**命令。它负责:参数采集/校验、通过既有 VeFaaS/APIG 集成部署镜像、播种 `mpa_meta`、组装 env、验证。它**不**负责:创建 PostgreSQL/OpenViking/身份资源,或 `mi-*` 控制面记录。 - -### 4.2 驱动设计的启动契约 - -`mpa-agent` 启动(`app/main.py` lifespan → `get_mpa_meta_service().initialize()`): - -1. `ensure_schema()` 在缺失时创建 `mpa_meta` 表。 -2. 要求 `MPA_AGENT_ID` 非空。 -3. 若缓存的 `mpa_meta` 行已具备全部七个必填字段(`mpa_instance_conf_ready`),则**跳过 `GetMpaInstanceConf`**;否则调用该 TOP 动作,而在没有控制面 `mi-*` 记录时会失败。 -4. 由 `apig_instance_id` 懒创建 IM Gateway service(`ensure_im_gateway_service`)。 - -因此,在当前架构下,播种该行(FR-4)正是让创建流程自包含的机制。 - -### 4.3 所选方案与备选 - -- **所选 — 播种 `mpa_meta` + 注入 env(本设计)。** 仅使用本地可用能力,不依赖不可用的控制面动作。CLI 由部署结果加所提供参数计算出七个字段。播种步骤写在一个小的内部函数边界之后,便于未来以控制面后端替换而不改动命令流程。 -- **备选 A — 调用控制面 `CreateMpaInstance`。** 暂拒:该动作在所有本地 checkout 中缺失;其名称、字段、version、IAM 要求未知。在 §8 记录为最终"正统"路径。 -- **备选 B — 修改 `mpa-agent` 使 `GetMpaInstanceConf` 可选。** 拒绝:违反"不修改 `mpa-agent`"的非目标。 - -### 4.4 接口 - -新增 CLI 面(增量): - -```text -veadk mpa create \ - --image \ - --registry-name \ - [--provider volcengine|byteplus] [--region cn-beijing] \ - --mpa-agent-id mi-xxxx \ - [--claw-space-id csi-xxxx] # 可选;默认 csi-(FR-10) \ - --pg-host ... --pg-port 5432 --pg-database ... --pg-user ... --pg-password ... \ - [--pg-sslmode require] [--pg-channel-binding require] \ - --model-provider openai --model-api-base ... --model-api-key ... --model-name ... \ - [--openviking-url ...] [--openviking-resource-id ov-xxx] [--openviking-api-key ...] \ - --agentkit-tool-id ... [--agentkit-tool-region cn-beijing] [--skill-space-id ...] \ - [--auth-method none|api-key|oauth2] \ - [--feishu-app-id cli_xxx] [--feishu-app-secret ...] \ - [--app-name mpa-agent] [--gateway-name ...] [--dry-run] -``` - -`--dry-run` 解析并打印计划(部署目标、env 键、`mpa_meta` 字段 —— 密钥掩码),不改动任何云或数据库状态。 - -### 4.5 状态、数据与幂等 - -- 本命令唯一写入的数据存储是外部 PostgreSQL 的 `mpa_meta` 表(按 `mpa_agent_id` 唯一的单行),采用与 `app/stores/mpa_meta.py:MpaMetaStore.fill_empty` 等价的 fill-empty 语义,并在写入前断言七个必填字段均非空。 -- veadk 侧的 `mpa_meta` 表定义(列名/类型/server default)与 `app/stores/mpa_meta.py` 完全一致;以注释记录"运行时表结构变更需在此同步"。 -- 以相同 `--app-name` 重复运行 `veadk mpa create` 会复用既有 VeFaaS 应用(`VeFaaS.deploy`/`deploy_image` 按应用名幂等),且不覆盖非空的 `mpa_meta` 字段。 - -### 4.6 权限与安全 - -- 需要能部署 VeFaaS + APIG 并访问容器仓库的云凭据(参考动作集见 `frontend_deploy_policy.py`;RDS 仅 `Describe`,此处不使用)。 -- 密钥(`--pg-password`、`--model-api-key`、`--openviking-api-key`、`--feishu-app-secret`、runtime API key)在交互时以隐藏输入采集,在 `--dry-run` 输出中掩码,绝不记录日志。这符合 `arkclaw-mpa` 的 `secretless runtime` 原则:CLI 只注入运行时所需内容,不把控制面凭据写入运行时。 - -### 4.7 兼容性与受影响调用方 - -- 增量 CLI 命令;不改动任何既有 veadk 命令、公开导入或生成项目(FR-1、FR-9)。 -- `VeFaaS.deploy_image` 新增一个可选关键字参数 `enable_key_auth: bool = False` 及扩展返回值;默认保持当前三元组行为,既有调用方(`CloudAgentEngine`、Studio)不受影响,由兼容性守卫测试验证(AC-10)。改动仅追加:不重排任何位置参数。 -- 不改动 `mpa-agent` API 或镜像。 -- Studio 通过已暴露的 A2A 面连接(`app/a2a/app.py`,`A2A_MOUNT_PATH=/` 时 agent-card 位于 `/.well-known/agent-card.json`)。 - -## 5. 边界情况 - -| 场景 | 处理方式 | -| --- | --- | -| `mpa_meta` 行已完整 | 跳过播种;记录跳过;继续验证(幂等)。 | -| 存在部分 `mpa_meta` 行 | 仅填充空的必填字段;绝不覆盖非空值。 | -| 写入前有字段未解析出 | 以具体消息中止;不写入部分行。 | -| PostgreSQL 不可达 | 在部署前快速失败并给出清晰连接错误;不产生云变更。 | -| 省略 OpenViking 参数 | 允许;运行时在无 OpenViking 工具下运行(仅在 URL+key 均设置时挂载)。 | -| 省略飞书密钥 | 允许;跳过自动绑定;运行时扫码绑定仍可用。 | -| 省略 `--claw-space-id` | 派生 `csi-`(FR-10);`IDENTITY_STARTUP_ENABLED=false` 使身份初始化关闭,无需任何 arkclaw 身份池。 | -| release 时镜像仍在同步 | 复用 `deploy_image` 的有界重试(`_release_application` 重试循环)。 | -| 部署中途失败 | 以脱敏日志报告失败;失败前已创建的资源被显式列出以便人工清理(若 `deploy_image` 缺乏与 `deploy` 对齐的清理,作为后续项)。 | -| 同名应用重复运行 | 复用既有应用;更新代码/env;不重复创建函数。 | -| `--dry-run` | 打印掩码密钥后的解析计划;不进行任何云或 DB 写入。 | -| 缺少必填参数 | 以指明缺失选项/env 的具体消息失败。 | - -## 6. 涉及文件 - -- `veadk/cli/cli_mpa.py` — 新增 `mpa` 命令组与 `create` 命令(编排、参数处理、首版 `mpa_meta` 播种、验证)。 -- `veadk/cli/cli.py` — 导入并 `veadk.add_command(mpa)`(单条增量注册)。 -- `veadk/integrations/ve_faas/ve_faas.py` — 扩展 `deploy_image` 支持 `enable_key_auth` + 扩展返回(向后兼容);复用 `_create_application`、`get_application_route`、release `CloudResource` 解析。 -- `veadk/integrations/ve_apig/ve_apig.py` — 复用,不修改。 -- `tests/cli/test_cli_mpa.py` — 新增单测,桩掉云 SDK,使用内存/SQLite `mpa_meta`。 -- `prd-spec/features/mpa-agent-oneclick-provision/` — 本双语设计。 -- 组件 spec:不改动任何既有 `specs//` 契约;若日后需要持久化 CLI 契约,可在后续单独新增 `specs/mpa-provisioning/`(本次范围外)。 - -## 7. 验收标准与追踪 - -| 需求 | 任务 | 验收标准 | 测试或验证命令 | 结果/证据 | -| --- | --- | --- | --- | --- | -| FR-1 | T-1 | `AC-1`:`veadk mpa --help` 列出 `create`;所有既有顶层命令仍可解析。 | `uv run veadk mpa --help` 与 `uv run veadk --help` | pass(2026-09-11):mpa 列出 create;`veadk --help` 同时显示 mpa 与既有命令 | -| FR-2、FR-8 | T-2 | `AC-2`:缺失必填参数以具名错误失败;`--feishu-app-id` 无密钥时隐藏输入提示;`--dry-run` 中密钥掩码。 | `uv run pytest tests/cli/test_cli_mpa.py -k params` | pass(2026-09-11):test_cli_mpa 6/6 | -| FR-3 | T-3 | `AC-3`:桩掉 VeFaaS/APIG 客户端后,经 `deploy_image(enable_key_auth=True)` 触发部署并捕获 public endpoint、apig 网关 id 与 key。 | `uv run pytest tests/cli/test_cli_mpa.py -k deploy` | pass(2026-09-11):full_flow_orchestration + deploy_image key-auth | -| FR-3 | T-3b | `AC-10`:不带 `enable_key_auth` 调用 `deploy_image()` 时返回不变的三元组且不启用 key 鉴权(兼容性守卫)。 | `uv run pytest tests/ -k deploy_image_compat` | pass(2026-09-11):test_deploy_image_compat_returns_three_tuple_without_key_auth | -| FR-4、FR-11 | T-4 | `AC-4`:播种写入七个必填字段(`private_endpoint` 镜像 `public_endpoint`);已完整行保持不变;部分行仅填充空字段;任一字段未解析则在写入前中止。 | `uv run pytest tests/cli/test_cli_mpa.py -k seed` | pass(2026-09-11):test_mpa_meta_seed 6/6 | -| FR-5、FR-10 | T-5 | `AC-5`:组装的 env 含 `mpa-agent` 启动读取的全部键,含未提供时的 `IDENTITY_STARTUP_ENABLED=false` 与 `CLAW_SPACE_ID=csi-`;不记录任何密钥。 | `uv run pytest tests/cli/test_cli_mpa.py -k env` | pass(2026-09-11):test_mpa_provision_env 6/6 | -| FR-6 | T-6 | `AC-6`:仅当 `/health`、`/readiness` 与 agent-card 均响应时验证通过;失败报告不含密钥。 | `uv run pytest tests/cli/test_cli_mpa.py -k verify` | pass(2026-09-11):test_mpa_verify 4/4 | -| FR-7 | T-7 | `AC-7`:命令输出含 agent-card URL 与 Studio 连接指引;文档化的手动 Studio 聊天对真实实例成功,且不同会话得到不同沙箱、飞书群聊可用。 | 手动:`veadk studio` 连接 + 一条消息 | 部分通过/阻断:指引与不同 session→sandbox 映射已验证;所选镜像因 ADK session revision 冲突导致 A2A 最终失败;飞书缺凭据/机器人安装与专属客户 APIG id | -| FR-9 | T-8 | `AC-8`:本次变更不修改 `mpa-agent` 仓库任何文件;既有 veadk 命令测试通过。 | `git -C ~/workspace/bytedance/mpa/mpa-agent status --porcelain` 为空;`uv run pytest tests/cli` | pass(2026-09-11):mpa-agent porcelain 为空;tests/cli 1273 passed, 4 skipped | -| 全部 | T-9 | `AC-9`:仓库门禁通过(pre-commit + 单测)。 | `pre-commit run -a` 与 `uv run pytest` | pass(2026-09-11):changed files 上 pre-commit(ruff-check/format/gitleaks)Passed;新增套件 25/25 | - -检查状态使用 `pass` / `fail` / `blocked` / `not_run` / `not_applicable`。执行时记录执行日期、被测 diff 范围与简要结果。 - -## 8. 风险与待决事项 - -- **OQ-1 — 控制面 `CreateMpaInstance`(本设计无需)。** `mi-*` 记录的创建对端在所有本地 checkout 中缺失。刻意置于范围外:veadk 版 `mpa-agent` 无需控制面记录。路线 A(CLI 侧播种 `mpa_meta`)为已接受的交付路径。此项仅作为未来非 veadk 部署的备注保留。 -- **OQ-2 — CLI 侧播种 `mpa_meta` —— 已解决(已接受)。** 用户已批准由 CLI 直接下发 `mi-*` 元信息配置;veadk 版 `mpa-agent` 在无控制面记录的情况下运行。此为已接受的机制。 -- **已解决 — APIG 实例 id 来源。** 代码确认:veadk 在部署时随函数一并创建/复用 APIG serverless 网关(`VeFaaS._create_application` + `ve_apig` 创建/复用;网关 id 可从 release 的 `CloudResource` `framework.triggers[0].DetailedConfig.GatewayId` 读取,见 `get_application_route`)。因此 `apig_instance_id` 由部署流程产出,非外部输入。 -- **已解决 — endpoint 选择。** veadk 场景以 `public_endpoint` 为权威,`private_endpoint` 与其同值(FR-11);不需要私网 endpoint。 -- **已解决 — 身份。** veadk 场景设 `IDENTITY_STARTUP_ENABLED=false` 并派生 `CLAW_SPACE_ID=csi-`,从而不使用 `arkclaw-{space}-userpool/-client/-workload`(FR-10);启动身份初始化在 `app/identity/service.py:166` 短路。 -- **风险 — secretless runtime。** 注入的 env 携带模型/OpenViking/runtime 密钥;应将控制面凭据排除在运行时之外并对所有密钥掩码,与 `arkclaw-mpa` 的原则一致。 -- **风险 — `deploy_image` 失败清理。** 与 `deploy` 不同,`deploy_image` 缺少 `keep_failed_deploy`/回滚。扩展它时补齐对齐清理,或将已创建资源显式列出以便人工清理(见边界表)。 - -## 9. 评审与交付记录 - -- 评审状态:2026-09-10 经 `review-spec` 评审;P0 结论已回写进 FR-3/FR-4/FR-10/FR-11、AC-10 与 §4.7。2026-09-10 状态推进为 `approved`。 -- 评审中已解决:APIG 实例 id 来源(部署产出)、endpoint 选择(public)、身份适配(`IDENTITY_STARTUP_ENABLED=false` + `csi-`)。 -- 用户批准:2026-09-10 已批准 —— 由 CLI 直接下发 `mi-*` 元信息配置(路线 A);veadk 版 `mpa-agent` 无需控制面记录。OQ-2 已接受;OQ-1 置于范围外。 -- 未决阻塞项:所选镜像的 Studio A2A 最终响应;飞书凭据/机器人安装与专属客户 APIG id。当前细节见 2026-09-11 编排对齐设计。 -- 已执行检查:路线 A 单测/CLI 覆盖通过;后续对齐验证由 2026-09-11 设计与证据报告追踪。 -- 剩余范围:见 2026-09-11 编排对齐设计。 diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-11-mpa-agent-oneclick-provision-verification-cases.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-11-mpa-agent-oneclick-provision-verification-cases.md deleted file mode 100644 index 1fa32de02..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-11-mpa-agent-oneclick-provision-verification-cases.md +++ /dev/null @@ -1,182 +0,0 @@ -# mpa-agent One-Click Provisioning — Functional Verification Cases - -> Related design: `2026-09-10-mpa-agent-oneclick-provision-design.md` (`approved`) -> Purpose: dev-loop step 2 up-front design and step 7 execution — executable verification cases. -> Status: `executed-with-blockers` (unit/CLI green; real results recorded in §3.1). - -## 1. Evidence retention - -- Unit-test evidence: `uv run pytest` output + covered assertions, saved under `prd-spec/features/mpa-agent-oneclick-provision/evidence/.log`. -- CLI-behavior evidence: command stdout/stderr (secrets masked) in the same directory. -- Real Studio E2E evidence: request/response summary, SSE snippets, screenshot paths in the same directory; must contain no real secrets. - -## 2. Coverage matrix (requirement → case) - -| Requirement | Case | Level | -| --- | --- | --- | -| FR-1 | VC-1 | unit/CLI | -| FR-2 | VC-2, VC-3 | unit/CLI | -| FR-3 | VC-4 (key-auth deploy), VC-5 (AC-10 compat guard) | unit | -| FR-4 | VC-6, VC-7, VC-8, VC-9 | unit | -| FR-5 | VC-10, VC-23 | unit + real | -| FR-6 | VC-11, VC-12 | unit | -| FR-7 | VC-13 (output guidance), VC-18 (real Studio chat) | unit + real | -| FR-8 | VC-3 (hidden input), VC-14 (dry-run masking) | unit/CLI | -| FR-9 | VC-15 (mpa-agent unchanged), VC-16 (existing-command regression) | gate/regression | -| FR-10 | VC-10 (IDENTITY_STARTUP_ENABLED=false + csi-) | unit | -| FR-11 | VC-7 (private mirrors public) | unit | -| key regression | VC-16, VC-19 (distinct sessions → distinct sandboxes), VC-20 (Feishu group chat) | regression + real | -| FR-12/13 | VC-21 (generated identity + dedicated Tool/Runtime), VC-22 (missing Tool input has no writes), VC-24 (retry idempotency) | unit/CLI | -| FR-18 | VC-25 (APIG id resolution fails closed) | unit + real | - -Every P0/P1 requirement (FR-1..FR-19) is covered by at least one case. - -## 3. Case details - -### Happy path - -- **VC-1 (FR-1 command registration)** - - Command: `uv run veadk mpa --help` and `uv run veadk --help` - - Precondition: none - - Expected: the `mpa` group lists `create`; `veadk --help` still lists all existing commands (deploy/init/create/frontend/studio/agentkit/kb, ...). - - Pass: both exit 0; no subcommand missing. - - On fail: return to T-12/T-1. - -- **VC-4 (FR-3 key-auth deploy + resource capture)** - - Command: `uv run pytest tests/cli/test_cli_mpa.py -k deploy` - - Precondition: stub `VeFaaS`/`APIGateway`; stub release returns `framework.url.system_url` and `framework.triggers[0].DetailedConfig.GatewayId`. - - Input: `enable_key_auth=True` path. - - Expected: orchestration calls `deploy_image(..., enable_key_auth=True)`; captures `public_endpoint`, `apig_instance_id` (=GatewayId), `runtime_api_key`. - - Pass: the four/five-tuple return is destructured correctly; `_create_application` receives `EnableKeyAuth=True`. - - Evidence: `evidence/VC-4.log`. - -- **VC-10 (FR-5/10/11 env assembly)** - - Command: `uv run pytest tests/cli/test_cli_mpa.py -k env` - - Input: omit `--claw-space-id`, omit `IDENTITY_STARTUP_ENABLED`. - - Expected: assembled env contains all mpa-agent startup keys; `IDENTITY_STARTUP_ENABLED=false`; `A2A_TIP_VERIFY_ENABLED=false` (no TIP issuer, APIG key auth remains mandatory); `MPA_CODEX_WORKER_DEFAULT_MODEL=`; `CLAW_SPACE_ID=csi-`; endpoint keys use public. - - Pass: key set equals the expected set; no secret appears in any log string. - - Evidence: `evidence/VC-10.log`. - -- **VC-13 (FR-7 Studio guidance output)** - - Command: `uv run pytest tests/cli/test_cli_mpa.py -k guidance` (or dry-run output assertion) - - Expected: success path stdout includes `public_endpoint`, `/.well-known/agent-card.json`, and how the runtime API key is handled. - - Pass: all three present; the key value itself is masked. - -### Error path - -- **VC-2 (FR-2 missing required → named error)** - - Command: `uv run pytest tests/cli/test_cli_mpa.py -k params` (one case each for missing `--image`/`--mpa-agent-id`/`--pg-*`/`--model-*`) - - Expected: each exits with an error **naming the missing option** (not a generic traceback). - - Pass: error message contains the concrete option name; exit != 0; no cloud/DB write. - -- **VC-11 (FR-6 verification fails on any probe failure)** - - Command: `uv run pytest tests/cli/test_cli_mpa.py -k verify` - - Input: stub `/health` 200 but `/readiness` non-200; and agent-card 404. - - Expected: verification result is fail, reporting which probe failed. - - Pass: any failed probe fails the whole check; report text contains no secret. - -- **VC-8 (FR-4 abort before write on unresolved field)** - - Command: `uv run pytest tests/cli/test_cli_mpa.py -k seed_abort` - - Input: `runtime_api_key` resolves to empty. - - Expected: raise a named error before seeding; **write no row**. - - Pass: DB has no inserted/updated row; error names the missing field. - -- **VC-17 (PostgreSQL unreachable)** - - Command: `uv run pytest tests/cli/test_cli_mpa.py -k pg_unreachable` - - Input: connection string to an unreachable port. - - Expected: fail fast **before deploy**; no cloud deploy call triggered. - - Pass: VeFaaS deploy stub is called zero times; error is a connection error. - -### Boundary - -- **VC-6 (FR-4 empty row writes 7 fields)**: `-k seed` subcase, empty table → 7 fields written complete and correct. -- **VC-7 (FR-4/11 private mirrors public)**: assert `private_endpoint == public_endpoint`. -- **VC-9 (FR-4 idempotency)**: complete row not overwritten; partial row only fills empty fields, non-empty preserved. -- **VC-14 (FR-8 dry-run masking)**: `--dry-run` output contains the plan but all secrets masked; no cloud/DB write. -- **VC-5 (FR-3 AC-10 compat guard)**: `deploy_image()` without `enable_key_auth` → returns the original three-tuple `(url, app_id, function_id)`; `_create_application` receives `EnableKeyAuth=False`. - -### Permission / environment isolation - -- **VC-3 (FR-2/8 Feishu secret hidden input)** - - Command: `uv run pytest tests/cli/test_cli_mpa.py -k feishu_prompt` - - Input: pass `--feishu-app-id` without a secret. - - Expected: triggers `click.prompt(hide_input=True)`; secret not echoed, not logged. - - Pass: prompt invoked with `hide_input=True`; captured output has no secret plaintext. - -### Cross-module / key regression (real, run in step 7/10) - -- **VC-15 (FR-9 mpa-agent unchanged)** - - Command: `git -C ~/workspace/bytedance/mpa/mpa-agent status --porcelain` - - Pass: empty output. - -- **VC-16 (FR-9 existing-command regression)** - - Command: `uv run pytest tests/cli` - - Pass: existing CLI tests all green (new cases do not break existing). - -- **VC-18 (FR-7 real Studio chat)** - - Steps: `veadk studio` → connect the instance agent-card as a remote A2A agent → send one message. - - Expected: streamed reply renders. - - Evidence: request/response summary + SSE snippet (redacted) in `evidence/VC-18.md`. - -- **VC-19 (key regression: distinct sessions → distinct sandboxes)** - - Steps: two distinct sessions each trigger `sandbox_task`. - - Expected: the two sandbox_session_ids differ (derived from `sha256(app_name:user_id:session_id)`). - - Evidence: two recorded sandbox ids (redacted). - -- **VC-20 (key regression: Feishu group chat)** - - Precondition: provide `FEISHU_APP_ID/SECRET` (or QR binding). - - Steps: @ the bot in a group → observe the IM Gateway reply. - - Expected: group session reused; reply delivered. - - Evidence: inbound/outbound envelope summary (redacted). - -### Generated identity and dedicated resources - -- **VC-21 (FR-12/13 generated identity and per-agent resources)** - - Command: `uv run pytest tests/cli/test_cli_mpa.py -k generated_identity` - - Input: omit `--mpa-agent-id` and `--agentkit-tool-id`; provide `--tool-image`. - - Expected: `mi-<12 lowercase alnum>` is used in output/env; Tool name maps `-` to `_`; Runtime name equals the generated id; CreateTool precedes CreateRuntime; two invocations differ. - - Pass: all assertions hold and each invocation binds its own returned `t-*`. - -- **VC-22 (FR-12 missing Tool input has no writes)** - - Command: `uv run pytest tests/cli/test_cli_mpa.py -k missing_tool_input` - - Input: runtime mode with neither `--tool-image` nor `--agentkit-tool-id`. - - Expected: named CLI error before SkillSpace, database, Tool, or Runtime calls. - - Pass: every side-effect stub is called zero times. - -- **VC-23 (FR-5 delegated worker model selection)** - - Steps: create an instance with `--model-name ` and trigger one real `sandbox_task`. - - Expected: worker usage events report `` instead of `auto`; the model proxy returns 2xx and the command reaches terminal `completed`. - - Pass: the binding has `last_run_status=completed`, no `codex_turn_failed`, and at least one usage event has the configured model and `statusCode=200`. - - Evidence: redacted binding/event summary; never persist the model API key. - -- **VC-24 (FR-13 retry idempotency)** - - Command: `uv run pytest tests/integrations/test_mpa_tool.py tests/integrations/test_mpa_runtime.py`. - - Expected: an exact-name Tool/Runtime is reused and converged; a non-Ready Tool is awaited; duplicate exact names fail instead of selecting arbitrarily; Runtime updates wait for a newer Ready version. - - Pass: no duplicate create call and all assertions hold. - -- **VC-25 (FR-18 APIG id integrity)** - - Command: `uv run pytest tests/cli/test_cli_mpa.py -k gateway`. - - Expected: an endpoint-derived gateway is accepted only when it embeds exactly one listed gateway id; AgentKit shared-gateway endpoints fail closed unless `--apig-instance-id` supplies the dedicated customer APIG used for IM routing. - - Pass: no arbitrary account gateway or `pending` placeholder is finalized into `mpa_meta`. - -## 3.1 Real E2E result (2026-09-11) - -- Runtime `r-yeuugf44qob21078l38i`, Tool `t-yeuugdts00zn6n5iqhin`, mpa-agent image tag `20260908154102-d34fdab`. -- VC-23 passed after setting `MPA_CODEX_WORKER_DEFAULT_MODEL`: two fresh contexts used the configured model, returned model HTTP 200, executed `printf` with exit code 0, and persisted `invocation.completed`. -- VC-19 isolation passed at the resource layer: contexts `a2ce8ec0-...` and `e7279624-...` mapped to distinct worker sessions `cw_sess_872f64f2f00b` and `cw_sess_bdf3758b4ddd`. -- VC-18 remains **blocked at the protocol result boundary**: both A2A requests returned `failed` with `The session has been modified in storage since it was loaded`, even though their worker bindings and commands completed. The selected image lets Codex durable events advance the PostgreSQL ADK session while the A2A runner still owns a stale session snapshot. This is an mpa-agent image/session-projection defect, not a Tool creation or model-routing failure. -- VC-20 was not run because Feishu credentials/bot installation were not provided; the existing `arkclaw:ListResources` denial also remains a documented permission risk for MCP discovery. -- VC-25 exposed a second VC-20 blocker: the real Runtime reports `GatewayMode=Shared` and an empty `GatewayInstanceId`; its public endpoint prefix does not match any customer APIG id. The previous row therefore retained `apig_instance_id=pending`. The CLI now fails closed and accepts an explicit dedicated `--apig-instance-id`; automatic customer-gateway allocation remains unresolved. - -## 4. Execution plan - -1. Unit level (VC-1..VC-14, VC-17): stub cloud SDK + SQLite/in-memory `mpa_meta`, run per TDD task. -2. Regression (VC-15, VC-16): run once after implementation. -3. Real (VC-18, VC-19, VC-20, VC-23): after one real `veadk mpa create` deploy, verify in Studio and a Feishu group; retain redacted evidence. -4. On failure: P0/P1 case failure returns to T-8..T-12 to fix implementation, or to this list to fix the case (recording the reason), then rerun affected cases. - -## 5. Exit gate - -- All unit/CLI P0/P1 cases mapped to FR-1..FR-19 pass. -- VC-15/VC-16 regression passes (does not break mpa-agent or existing commands). -- Real VC-18 and VC-20 remain blocked as documented in §3.1; VC-19 and VC-23 pass. diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-11-mpa-agent-oneclick-provision-verification-cases.zh.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-11-mpa-agent-oneclick-provision-verification-cases.zh.md deleted file mode 100644 index dec94eb56..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-11-mpa-agent-oneclick-provision-verification-cases.zh.md +++ /dev/null @@ -1,159 +0,0 @@ -# mpa-agent 一键创建 — 功能验证 Case 清单 - -> 关联设计:`2026-09-10-mpa-agent-oneclick-provision-design.zh.md`(`approved`) -> 用途:dev-loop 步骤 2 前置设计、步骤 7 后置执行的可执行验证 Case。 -> 状态:`executed-with-blockers`(单测/CLI 通过;真机结果见 §3.1)。 - -## 1. 证据留存约定 - -- 单测证据:`uv run pytest` 输出 + 覆盖到的断言,落 `prd-spec/features/mpa-agent-oneclick-provision/evidence/.log`。 -- CLI 行为证据:命令 stdout/stderr(密钥须掩码)落同目录。 -- 真机 Studio E2E 证据:请求/响应摘要、SSE 片段、截图路径,落同目录;不得含真实密钥。 - -## 2. 覆盖矩阵(需求 → Case) - -| 需求 | Case | 层次 | -| --- | --- | --- | -| FR-1 | VC-1 | 单测/CLI | -| FR-2 | VC-2, VC-3 | 单测/CLI | -| FR-3 | VC-4(key-auth 部署), VC-5(AC-10 兼容守卫) | 单测 | -| FR-4 | VC-6, VC-7, VC-8, VC-9 | 单测 | -| FR-5 | VC-10, VC-23 | 单测 + 真机 | -| FR-6 | VC-11, VC-12 | 单测 | -| FR-7 | VC-13(输出指引), VC-18(真机 Studio 聊天) | 单测 + 真机 | -| FR-8 | VC-3(隐藏输入), VC-14(dry-run 掩码) | 单测/CLI | -| FR-9 | VC-15(mpa-agent 零改动), VC-16(既有命令回归) | 门禁/回归 | -| FR-10 | VC-10(IDENTITY_STARTUP_ENABLED=false + csi-) | 单测 | -| FR-11 | VC-7(private 镜像 public) | 单测 | -| 关键回归 | VC-16, VC-19(不同 session 不同沙箱), VC-20(飞书拉群) | 回归 + 真机 | -| FR-12/13 | VC-21(生成身份 + 独立 Tool/Runtime), VC-22(缺 Tool 输入无写入), VC-24(重试幂等) | 单测/CLI | -| FR-18 | VC-25(APIG id 解析 fail-closed) | 单测 + 真机 | - -每个 P0/P1 需求(FR-1..FR-19)均至少 1 个 Case 覆盖。 - -## 3. Case 明细 - -### 正常路径 - -- **VC-1(FR-1 命令注册)** - - 命令:`uv run veadk mpa --help` 且 `uv run veadk --help` - - 前置:无 - - 预期:`mpa` 组列出 `create`;`veadk --help` 仍列出 deploy/init/create/frontend/studio/agentkit/kb 等全部既有命令。 - - 通过标准:两条命令 exit 0;子命令齐全无缺失。 - - 失败处理:回 T-12/T-1。 - -- **VC-4(FR-3 key-auth 部署 + 资源回收)** - - 命令:`uv run pytest tests/cli/test_cli_mpa.py -k deploy` - - 前置:桩化 `VeFaaS`/`APIGateway`;桩 release 返回含 `framework.url.system_url` 与 `framework.triggers[0].DetailedConfig.GatewayId`。 - - 输入:`enable_key_auth=True` 路径。 - - 预期:编排调用 `deploy_image(..., enable_key_auth=True)`;捕获 `public_endpoint`、`apig_instance_id`(=GatewayId)、`runtime_api_key`。 - - 通过标准:断言四元/五元返回被正确解构;`_create_application` 收到 `EnableKeyAuth=True`。 - - 证据:`evidence/VC-4.log`。 - -- **VC-10(FR-5/10/11 env 组装)** - - 命令:`uv run pytest tests/cli/test_cli_mpa.py -k env` - - 输入:不传 `--claw-space-id`、不传 `IDENTITY_STARTUP_ENABLED`。 - - 预期:组装 env 含全部 mpa-agent 启动键;`IDENTITY_STARTUP_ENABLED=false`;`A2A_TIP_VERIFY_ENABLED=false`;`MPA_CODEX_WORKER_DEFAULT_MODEL=`;`CLAW_SPACE_ID=csi-`;endpoint 相关键取 public。 - - 通过标准:键集合等于期望集合;无任一密钥出现在日志字符串。 - - 证据:`evidence/VC-10.log`。 - -- **VC-13(FR-7 输出 Studio 指引)** - - 命令:`uv run pytest tests/cli/test_cli_mpa.py -k guidance`(或 dry-run 输出断言) - - 预期:成功路径 stdout 含 `public_endpoint`、`/.well-known/agent-card.json`、runtime API key 的处理说明。 - - 通过标准:三项均出现;key 值本身掩码。 - -### 异常路径 - -- **VC-2(FR-2 缺必填报错)** - - 命令:`uv run pytest tests/cli/test_cli_mpa.py -k params`(缺 `--image`/`--mpa-agent-id`/`--pg-*`/`--model-*` 各一例) - - 预期:每例以**指明缺失项名**的错误退出(非通用 traceback)。 - - 通过标准:错误消息含具体选项名;exit != 0;无云/DB 写入。 - -- **VC-11(FR-6 探测失败即失败)** - - 命令:`uv run pytest tests/cli/test_cli_mpa.py -k verify` - - 输入:桩 `/health` 200 但 `/readiness` 非 200;及 agent-card 404。 - - 预期:验证判定 fail,报告指出未通过的探测项。 - - 通过标准:任一探测未通过则整体 fail;报告文本不含密钥。 - -- **VC-8(FR-4 字段未解析写前中止)** - - 命令:`uv run pytest tests/cli/test_cli_mpa.py -k seed_abort` - - 输入:`runtime_api_key` 解析为空。 - - 预期:播种前抛具名错误,**不写入任何行**。 - - 通过标准:DB 无新增/修改行;错误指明缺失字段。 - -- **VC-17(PostgreSQL 不可达)** - - 命令:`uv run pytest tests/cli/test_cli_mpa.py -k pg_unreachable` - - 输入:连接串指向不可达端口。 - - 预期:**部署前**快速失败,未触发任何云部署调用。 - - 通过标准:VeFaaS 部署桩零调用;错误为连接类错误。 - -### 边界条件 - -- **VC-6(FR-4 空行写 7 字段)**:`-k seed` 子用例,空表 → 写入 7 字段齐全且值正确。 -- **VC-7(FR-4/11 private 镜像 public)**:断言 `private_endpoint == public_endpoint`。 -- **VC-9(FR-4 幂等)**:完整行不被覆盖;部分行仅补空字段,非空字段保持原值。 -- **VC-14(FR-8 dry-run 掩码)**:`--dry-run` 输出含计划但所有密钥呈掩码;无云/DB 写入。 -- **VC-5(FR-3 AC-10 兼容守卫)**:`deploy_image()` 不传 `enable_key_auth` → 返回原三元组 `(url, app_id, function_id)`,`_create_application` 收到 `EnableKeyAuth=False`。 - -### 权限/环境隔离 - -- **VC-3(FR-2/8 飞书密钥隐藏输入)** - - 命令:`uv run pytest tests/cli/test_cli_mpa.py -k feishu_prompt` - - 输入:给 `--feishu-app-id` 不给 secret。 - - 预期:触发 `click.prompt(hide_input=True)`;secret 不回显、不入日志。 - - 通过标准:prompt 被以 `hide_input=True` 调用;捕获输出无 secret 明文。 - -### 跨模块联动 / 关键回归(真机,步骤 7/10 执行) - -- **VC-15(FR-9 mpa-agent 零改动)** - - 命令:`git -C ~/workspace/bytedance/mpa/mpa-agent status --porcelain` - - 通过标准:输出为空。 - -- **VC-16(FR-9 既有命令回归)** - - 命令:`uv run pytest tests/cli` - - 通过标准:既有 CLI 测试全绿(新增用例不破坏存量)。 - -- **VC-18(FR-7 真机 Studio 聊天)** - - 步骤:`veadk studio` → 以远端 A2A Agent 连接实例 agent-card → 发送一条消息。 - - 预期:SSE 流式回复渲染成功。 - - 证据:请求/响应摘要 + SSE 片段(脱敏)落 `evidence/VC-18.md`。 - -- **VC-19(关键回归 不同 session 不同沙箱)** - - 步骤:两个不同 session 各触发一次 `sandbox_task`。 - - 预期:两个 session 的 sandbox_session_id 不同(`sha256(app_name:user_id:session_id)` 派生)。 - - 证据:两次 sandbox id 记录(脱敏)。 - -- **VC-20(关键回归 飞书拉群)** - - 前置:提供 `FEISHU_APP_ID/SECRET`(或扫码绑定)。 - - 步骤:群内 @ 机器人发消息 → 观察 IM Gateway 回复。 - - 预期:群 session 复用、回复送达。 - - 证据:入站/出站 envelope 摘要(脱敏)。 - -### 生成身份与独立资源 - -- **VC-21(FR-12/13 生成身份与每 Agent 资源)**:省略 agent/tool id、提供 `--tool-image`,断言生成合法且唯一 `mi-*`、派生 Tool/Runtime 名,并先 CreateTool 后 CreateRuntime。 -- **VC-22(FR-12 缺 Tool 输入无写入)**:runtime 模式缺少 tool image/id 时,在 SkillSpace、DB、Tool、Runtime 调用前具名失败。 -- **VC-23(FR-5 worker 模型选择)**:真机 `sandbox_task` 使用 CLI 指定模型,模型请求 2xx 且 binding 最终 `completed`。 -- **VC-24(FR-13 重试幂等)**:同名 Tool/Runtime 复用并收敛配置;非 Ready Tool 等待;重名歧义失败;Runtime 更新等待更新版本 Ready。 -- **VC-25(FR-18 APIG id 完整性)**:只接受 endpoint 唯一映射的 gateway id 或显式 `--apig-instance-id`;共享 gateway 无 ID 时 fail-closed,不写入任意 gateway 或 `pending`。 - -## 3.1 真机 E2E 结果(2026-09-11) - -- Runtime `r-yeuugf44qob21078l38i`、Tool `t-yeuugdts00zn6n5iqhin`,mpa-agent 镜像 tag `20260908154102-d34fdab`。 -- VC-23 通过:两个新 context 均使用指定模型、模型 HTTP 200、命令 exit code 0,并持久化 `invocation.completed`。 -- VC-19 在资源层通过:两个 context 映射到不同 worker session。 -- VC-18 在协议结果边界阻断:worker 已完成,但 A2A 均因 PostgreSQL ADK session revision 冲突返回 failed;属所选 mpa-agent 镜像缺陷。 -- VC-20 未执行:未提供飞书凭据/机器人安装;且 Runtime 为 `GatewayMode=Shared`、`GatewayInstanceId` 为空,现有行仍为 `apig_instance_id=pending`。CLI 已 fail-closed 并允许显式传专属 `--apig-instance-id`,客户 gateway 自动分配仍未解决。 - -## 4. 验证执行计划 - -1. 单测级(VC-1..VC-14, VC-17):桩云 SDK + SQLite/内存 `mpa_meta`,随 TDD 每任务即时执行。 -2. 回归(VC-15, VC-16):实现完成后统一执行。 -3. 真机(VC-18, VC-19, VC-20, VC-23, VC-25):一次真实 `veadk mpa create` 部署后,在 Studio 与飞书群验证;证据脱敏留存。 -4. 失败处理:P0/P1 Case 失败回 T-8..T-12 修实现,或回本清单修 Case(并记录原因),重跑受影响 Case。 - -## 5. 准出门禁 - -- 所有映射到 FR-1..FR-19 的单测/CLI P0/P1 Case 通过。 -- VC-15/VC-16 回归通过(不破坏 mpa-agent 与既有命令)。 -- 真机 VC-18、VC-20 按 §3.1 保持 blocked;VC-19、VC-23 通过。 diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-11-mpa-agent-provision-orchestration-alignment-design.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-11-mpa-agent-provision-orchestration-alignment-design.md deleted file mode 100644 index 62b880e2b..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-11-mpa-agent-provision-orchestration-alignment-design.md +++ /dev/null @@ -1,228 +0,0 @@ -# mpa-agent Provisioning Orchestration Alignment (CreateTool + SkillSpace + Runtime) Design - -## Metadata - -- **Change ID:** `mpa-agent-oneclick-provision` -- **Created / Revised:** 2026-09-11 / 2026-09-11 -- **Lifecycle status:** `approved` -- **Language counterpart:** [2026-09-11-mpa-agent-provision-orchestration-alignment-design.zh.md](2026-09-11-mpa-agent-provision-orchestration-alignment-design.zh.md) -- **Predecessor:** [2026-09-10-mpa-agent-oneclick-provision-design.md](./2026-09-10-mpa-agent-oneclick-provision-design.md) (`approved`, implemented) — this version extends its scope; it does not replace the delivered pieces. -- **Related component specs:** none created yet; CLI contract surface extended (see §6). -- **Reference implementation:** `~/workspace/bytedance/mpa/mpa-runtime/docs/runtime-manager-openapi-map.md` (Runtime Manager creation chain). - -## 1. Overview - -### 1.1 Problem / Background - -The delivered route A (`2026-09-10` design, commit `18fa2485`) provisions a veadk-version mpa-agent by: `deploy_image` (VeFaaS + APIG, key auth) → seed `mpa_meta` → inject env → verify. It treats the Codex sandbox **Tool id** and any **Skill Space** as pre-existing external parameters. - -The Runtime Manager reference chain (`runtime-manager-openapi-map.md`) shows the full orchestration a provisioner is expected to own, in order: - -```text -... -> CreateSkillSpace/GetSkillSpace -> CreateTool -> tool_id - -> CreateRuntime(ArtifactUrl, RoleName, ToolId, Envs=PG*+SkillSpaceId+ToolId) - -> ReleaseRuntime -> GetRuntime Ready -> /run_sse verify -> persist bindings -``` - -Two gaps versus that reference remain in route A: - -- **CreateTool is not orchestrated.** The Codex sandbox `Tool` (`t-*`, built from the `mpa_codex_worker` image) must be created and its id obtained **before** the agent is created. Route A requires the caller to pass an existing `t-*`. -- **Skill Space is not orchestrated.** The reference creates/selects a Skill Space and injects `SKILL_SPACE_ID`; route A only forwards `--skill-space-id` if given. - -There is also a **compute-plane modeling difference**: the reference uses AgentKit `CreateRuntime` (produces an `r-*` runtime; mpa-agent's own `agentkit.yaml` ships as `runtime_id: r-yerdk5srnk54ppxtxy25`), while route A uses `VeFaaS.deploy_image` (VeFaaS App + self-created APIG). Both run the image, but only `CreateRuntime` matches the reference resource model and mpa-agent's native delivery shape. - -### 1.2 Goals - -Bring `veadk mpa create` up to the reference orchestration so it can stand up an mpa-agent end to end from an image, owning Tool and Skill Space creation, while keeping PostgreSQL/OpenViking as external parameters and requiring no control-plane `mi-*` record. - -- **G1 — Orchestrate a per-agent CreateTool.** By default, create one dedicated Codex sandbox `Tool` from the provided worker image for every newly generated mpa-agent id and obtain its `tool_id` before runtime creation. An existing `--agentkit-tool-id` remains an explicit debug/migration escape hatch. -- **G2 — Orchestrate Skill Space.** Optionally create or select an AgentKit Skill Space and inject `SKILL_SPACE_ID`. -- **G3 — Align the compute plane.** Provision the agent through AgentKit `CreateRuntime` (`r-*`) to match the reference and mpa-agent's native runtime model, while keeping the existing `deploy_image` path available as a fallback selected by a flag. -- **G4 — Preserve route A guarantees.** `mpa_meta` seeding, identity adaptation (`IDENTITY_STARTUP_ENABLED=false`, `CLAW_SPACE_ID=csi-`), public-endpoint authority, verification, secret masking, and non-regression of mpa-agent all remain. - -### 1.3 Non-Goals - -- Not creating PostgreSQL instances/databases or OpenViking `ov-*` resources (external preconditions, unchanged from route A). -- Not building the image itself; `--image`/`--tool-image` are inputs (artifact-first, per the reference "Artifact 来源" section). -- Not implementing AIDAP Workspace/Branch/Database/DBAccount creation (the reference does this for RDS; our DB is external). -- Not a control-plane `mi-*` record; the veadk-version mpa-agent runs without one. - -## 2. Scenarios - -### Scenario 1: Full orchestration from images - -**Given** an mpa-agent image, a Codex worker image, an existing external PostgreSQL, model credentials, and account/region. -**When** the operator runs `veadk mpa create` with `--tool-image `, no `--mpa-agent-id`, and no `--agentkit-tool-id`. -**Then** the CLI generates a globally unique `mi-<12 lowercase alnum>` id, derives the Tool name by replacing `-` with `_`, creates that dedicated Tool (obtains `t-*`), optionally creates/selects a Skill Space, provisions a Runtime named from the generated id, seeds `mpa_meta`, verifies, and prints Studio A2A connection guidance. - -### Scenario 2: Reuse an existing Tool - -**Given** an existing Codex worker Tool `t-*` (e.g. `t-yeslt9bv9ckgnctgwaaf`). -**When** the operator passes `--agentkit-tool-id t-*` and omits `--tool-image`. -**Then** the CLI skips CreateTool, reuses the id, and proceeds. (This preserves route A behavior.) - -### Scenario 3: Runtime readiness and verification - -**Given** a provisioned runtime. -**When** creation completes. -**Then** the CLI waits for the runtime to reach Ready (or the VeFaaS app to release), then probes `/health`, `/readiness`, and the A2A agent-card before reporting success. - -### Scenario 4: Idempotent re-run and rollback - -**Given** a previous partial run. -**When** the operator re-runs with the same names/ids. -**Then** the exact-name Tool/Skill Space/Runtime is reused. A non-Ready Tool is awaited, a reused Runtime is updated and released to converge image/Tool/env, and the CLI waits for the newer Ready version. Duplicate exact names fail as ambiguous instead of selecting an arbitrary resource. The two-phase `mpa_meta` row may remain with placeholders after a post-create failure and is never presented as finalized. - -## 3. Functional Requirements - -Route A requirements FR-1..FR-11 (previous design) remain in force. This version adds: - -- **FR-12 — Per-agent CreateTool orchestration.** The CLI generates `mpa_agent_id` as `mi-` plus 12 lowercase alphanumeric characters when omitted. Without `--agentkit-tool-id`, `--tool-image` is required and the CLI creates a Codex sandbox Tool via `CreateTool`, using the agent id with hyphens replaced by underscores as its default name, then obtains `tool_id`. The verified Tool contract remains unchanged. An explicit `--agentkit-tool-id` skips CreateTool as a debug/migration escape hatch. -- **FR-13 — Per-agent identity and idempotency.** A normally generated agent id is unique per invocation and maps to a distinct Tool name and Runtime name. An explicit `--mpa-agent-id` makes a retry idempotent by reusing a Ready Tool with that derived name. `--tool-name` and `--runtime-name` remain explicit debug overrides. -- **FR-14 — Skill Space orchestration.** When `--skill-space-name` is provided, the CLI creates or selects an AgentKit Skill Space via `agentkit.sdk.skills` (`list_skill_spaces`/`create_skill_space`/`get_skill_space`) and injects the resolved `SKILL_SPACE_ID` into the runtime env. When only `--skill-space-id` is provided it is used directly; when neither is provided, Skill Space is omitted. -- **FR-15 — Compute-plane selection.** A `--compute-plane {runtime,vefaas}` option selects provisioning: `runtime` (default) uses AgentKit `CreateRuntime`/`ReleaseRuntime` (`r-*`, matching the reference and mpa-agent's native model); `vefaas` uses the existing `deploy_image` path (route A). Both must yield the same downstream contract: a public endpoint, an APIG-backed key-auth API key, and an APIG instance id for `mpa_meta`. -- **FR-16 — Runtime path env and binding.** For `--compute-plane runtime`, `CreateRuntime` is called with `ArtifactType`/`ArtifactUrl=`, `ToolId`, `RoleName`, key-auth authorizer, and `Envs` = the route A runtime env plus `SKILL_SPACE_ID` (when resolved) and `AGENTKIT_TOOL_ID`. Env changes go in `CreateRuntime`/`UpdateRuntime` (never `ReleaseRuntime`, which has no `Envs`), per the reference constraint. -- **FR-17 — Ordered idempotent orchestration with rollback.** The CLI runs SkillSpace → Tool → **pre-seed `mpa_meta` (FR-19)** → compute-plane create → **finalize `mpa_meta` (FR-19)** → verify, in that order. Each creation is idempotent by name/client-token; a failure after a resource is created surfaces the created ids for cleanup. -- **FR-18 — `mpa_meta` fields from either plane.** `runtime_id` is the AgentKit runtime id (`r-*`) in runtime mode or the VeFaaS app id in vefaas mode; `apig_instance_id`, `runtime_api_key`, `public_endpoint` are resolved from the selected plane. `private_endpoint` mirrors `public_endpoint` (FR-11 unchanged). -- **FR-19 — Two-phase seeding before first container start (verified by smoke).** The mpa-agent container starts as soon as the runtime is released, and on startup it calls `GetMpaInstanceConf` unless the `mpa_meta` row is already complete. On the test account this call returns `403 AccessDenied` for `arkclaw:GetMpaInstanceConf`, so startup fails if seeding happens after release. Therefore seeding is two-phase, keyed by the caller-known `mpa_agent_id`: - - **Phase 1 (before compute-plane create):** seed the `mpa_meta` row with real `account_id`/`resource_account_id` and **non-empty placeholders** for `runtime_id`/`public_endpoint`/`private_endpoint`/`runtime_api_key`/`apig_instance_id`, so `mpa_instance_conf_ready()` is already true at first container start and the runtime skips `GetMpaInstanceConf`. - - **Phase 2 (after Ready):** overwrite the placeholder fields with the real deploy-resolved values. This requires the seeder to support an explicit overwrite update (distinct from route A's fill-empty), applied only to the phase-1 placeholder fields. - - Placeholders do not affect A2A chat startup (the agent-card URL comes from `A2A_PUBLIC_URL`, injected separately); real endpoint/apig/key values matter for IM-gateway paths and are finalized in phase 2. -- **FR-20 — APIG binding integrity.** Finalization requires a real non-empty `runtime_id`, `public_endpoint`, `runtime_api_key`, and `apig_instance_id`. Runtime mode accepts an APIG id only when explicitly supplied with `--apig-instance-id`, returned by the compute plane, or uniquely embedded in/mapped from the public endpoint. It must not fall back to an unrelated account gateway. AgentKit shared-gateway responses with `GatewayInstanceId=""` fail closed and leave phase-1 placeholders unfinalized. - -## 4. Design and Contract Impact - -### 4.1 Orchestration order (matches the reference) - -```text -resolve params and generate mpa_agent_id when omitted - -> [FR-14] ensure SkillSpace (create/select) -> skill_space_id - -> [FR-12/13] derive Tool/Runtime names from mpa_agent_id - -> ensure Tool (reuse same-agent Ready Tool or CreateTool from --tool-image) -> tool_id - -> assemble runtime env (route A env + SKILL_SPACE_ID + AGENTKIT_TOOL_ID) - -> [FR-19 phase 1] pre-seed mpa_meta (real account + non-empty placeholders) - so first container start skips GetMpaInstanceConf (403 on test account) - -> [FR-15/16] compute plane: - runtime : CreateRuntime(ToolId, ArtifactUrl, Envs) -> ReleaseRuntime -> GetRuntime Ready - vefaas : deploy_image(enable_key_auth=True) (route A) - -> public_endpoint, apig_instance_id, runtime_api_key, runtime_id/app_id - -> require real non-placeholder runtime/endpoint/key/APIG binding; shared gateway without a - customer APIG id fails closed (or use explicit --apig-instance-id) - -> [FR-19 phase 2] finalize mpa_meta (overwrite placeholders with real values) - -> [route A] verify /health /readiness /agent-card - -> print Studio A2A guidance -``` - -### 4.2 Chosen approach vs. alternatives - -- **Chosen — extend the CLI to own SkillSpace + Tool + Runtime, default to `CreateRuntime`.** Matches the reference resource model and mpa-agent's native `r-*` delivery; keeps `deploy_image` as a `--compute-plane vefaas` fallback so the delivered route A path is not lost. -- **Alternative — keep Tool/SkillSpace external (route A only).** Rejected for this iteration: the user asked to align with the reference where the provisioner creates the Tool and obtains its id before creating the agent. -- **Alternative — drop `deploy_image` entirely.** Rejected: it is delivered, tested, and useful where an AgentKit Runtime is not desired; retained behind a flag. - -### 4.3 Interfaces (additive to route A) - -```text -veadk mpa create \ - ... (all route A options) ... \ - [--compute-plane runtime|vefaas] # default runtime \ - [--tool-image ] # create Tool when set and no --agentkit-tool-id \ - [--mpa-agent-id mi-*] # omitted => generate mi-<12 lowercase alnum> \ - [--tool-name ] # debug override; default mi-* with '-' mapped to '_' \ - [--tool-role-name IDRoleForArkClawShareAgent] \ - [--skill-space-name ] [--skill-space-id ss-*] \ - [--runtime-role-name ] [--runtime-name ] # default mpa-agent-id \ - [--min-instance 1] [--max-instance 1] \ - [--apig-instance-id ] # explicit dedicated customer APIG for IM routing \ - [--agentkit-tool-id t-*] # when set, skip CreateTool (route A reuse) -``` - -`--dry-run` extends to print the planned SkillSpace/Tool/compute-plane actions and the resolved env (secrets masked), performing no cloud or DB writes. - -### 4.4 SDK facts (verified live in account 2112682748) - -- Tools: `agentkit.sdk.tools.client.AgentkitToolsClient` — `create_tool`, `get_tool`, `list_tools`; `CreateToolRequest` accepts `image_url`, `command`, `port`, `tool_type`, `role_name`, `skill_space_id`, `authorizer_configuration`, `network_configuration`, `envs`, `client_token`. -- Runtime: `agentkit.sdk.runtime.client.AgentkitRuntimeClient` — `create_runtime`, `release_runtime`, `get_runtime`, `list_runtime_instances`, `update_runtime`; `CreateRuntimeRequest` has `artifact_type`, `artifact_url`, `tool_id`, `role_name`, `authorizer_configuration`, `network_configuration`, `envs`, `min_instance`, `max_instance`, `client_token`. -- Skills: `agentkit.sdk.skills.client.AgentkitSkillsClient` — `create_skill_space`, `get_skill_space`, `list_skill_spaces`. -- Existing Codex worker Tool contract (from `t-yeslt9bv9ckgnctgwaaf`, image `mpa_codex_worker:apihelpers-b3818f5-...`): `ToolType=Private`, `command=/opt/gem/run.sh`, `port=8000`, `cpu 2000/mem 4096`, `role IDRoleForArkClawShareAgent`, public+private APIG endpoints, key auth. - -### 4.5 State, data, idempotency, rollback - -- Idempotency keys: SkillSpace by name; Tool and Runtime by exact name. A single match is reused, non-Ready Tool state is awaited, and Runtime configuration is converged through `UpdateRuntime(..., ReleaseEnable=True)` before waiting for a newer Ready version. Multiple exact matches are an explicit ambiguity error. -- `mpa_meta`: phase 1 writes all seven required fields with placeholders; phase 2 overwrites only the deploy-bound fields after all real values pass completeness checks. `runtime_id` source depends on `--compute-plane`. -- Rollback: on post-creation failure, surface created `ss-*`/`t-*`/`r-*` ids. Full auto-teardown is out of scope; phase-1 placeholders may remain for an idempotent retry but are never reported as a completed binding. - -### 4.6 Permissions and security - -- Adds AgentKit Tools/Skills/Runtime create permissions to the route A credential set (VeFaaS + APIG + CR). RDS remains `Describe`-only and unused. -- Secret handling unchanged: hidden prompts, `--dry-run` masking, never logged; no control-plane credentials injected into the runtime. - -### 4.7 Compatibility and affected callers - -- Additive CLI options; `--compute-plane runtime` becomes the default, which **changes default behavior** versus route A (which always used `deploy_image`). This is a deliberate, approved behavior change scoped to `veadk mpa create`; `--compute-plane vefaas` reproduces route A exactly. -- `VeFaaS.deploy_image` signature is unchanged from route A (already extended and compatibility-guarded by AC-10). -- No mpa-agent source/image/API change. - -## 5. Edge Cases - -| Scenario | Handling | -| --- | --- | -| Neither `--tool-image` nor `--agentkit-tool-id` in runtime mode | Fail validation before any cloud or database write. | -| `--tool-image` and `--agentkit-tool-id` both set | Prefer the explicit `--agentkit-tool-id`; warn that `--tool-image` is ignored. | -| Tool with target name exists but not Ready | Wait for Ready up to a bounded timeout; fail with the tool id if it never readies. | -| Multiple exact-name Tools or Runtimes exist | Fail as ambiguous; never bind an arbitrary resource. | -| Neither `--skill-space-name` nor `--skill-space-id` | Omit `SKILL_SPACE_ID`; runtime runs without skill space. | -| `--compute-plane runtime` but artifact type unknown | Fail before create with a clear message; do not create a half-wired runtime. | -| Runtime never reaches Ready | Fail with runtime id and last status; leave phase-1 placeholders unfinalized for explicit retry/cleanup. | -| Re-run after partial failure | Reuse existing SkillSpace/Tool/Runtime by name/token; phase-1 pre-seed is fill-empty, phase-2 finalize overwrites placeholder fields. | -| Runtime startup fails with `GetMpaInstanceConf 403` | Root cause of the smoke failure: seeding after release. Fixed by FR-19 phase-1 pre-seed before compute-plane create. | -| Runtime reports shared gateway and empty `GatewayInstanceId` | Fail closed before phase-2 finalization unless a dedicated customer gateway is supplied through `--apig-instance-id`; never choose another listed gateway. | -| `--dry-run` | Print SkillSpace/Tool/compute-plane plan + masked env; no writes. | - -## 6. Affected Files - -- `veadk/cli/cli_mpa.py` — add `--compute-plane`, `--tool-image`, `--tool-name`, `--skill-space-name`, runtime options; orchestrate SkillSpace → Tool → pre-seed → compute-plane → finalize-seed → verify (FR-19). -- `veadk/integrations/mpa/mpa_tool.py` (new) — CreateTool/reuse-by-name helper mirroring the verified Codex worker Tool contract. -- `veadk/integrations/mpa/mpa_skill_space.py` (new) — create/select Skill Space helper. -- `veadk/integrations/mpa/mpa_runtime.py` (new) — `CreateRuntime`/`ReleaseRuntime`/wait-Ready helper (`ArtifactType="image"`, verified) and result normalization to `(public_endpoint, apig_instance_id, runtime_api_key, runtime_id)`. -- `veadk/integrations/mpa/mpa_meta_seed.py` — add an overwrite update path (`overwrite_fields`) for FR-19 phase 2, distinct from fill-empty. -- `veadk/integrations/mpa/mpa_provision.py` — extend params/env for skill space + tool image; provide placeholder values for phase-1 pre-seed. -- `veadk/integrations/ve_faas/ve_faas.py` — unchanged (vefaas fallback reused). -- `tests/cli/test_cli_mpa.py`, `tests/integrations/test_mpa_tool.py`, `test_mpa_skill_space.py`, `test_mpa_runtime.py`, `test_mpa_meta_seed.py` (new/extended) — stubbed SDK unit tests. -- `prd-spec/features/mpa-agent-oneclick-provision/` — this bilingual design. - -## 7. Acceptance Criteria and Tracking - -Route A AC-1..AC-10 remain green (unchanged code paths). New: - -| Requirement | Task | Acceptance Criterion | Test / Verification | Result | -| --- | --- | --- | --- | --- | -| FR-12/13 | T-10 | `AC-11`: With `--tool-image` and no tool id, CreateTool is invoked with the verified contract and returns `t-*`; a single exact-name tool is reused/awaited and duplicate exact names fail. | `uv run pytest tests/integrations/test_mpa_tool.py` | pass (2026-09-11) | -| FR-12/13 | T-10b | `AC-11b`: Omitting agent/tool ids generates a valid unique `mi-*`, derives a dedicated `mi_*` Tool name and agent-derived Runtime name, and invokes CreateTool before CreateRuntime; missing Tool input fails before DB/cloud writes. | `uv run pytest tests/cli/test_cli_mpa.py -k generated_identity` | pass (2026-09-11) | -| FR-14 | T-11 | `AC-12`: `--skill-space-name` creates or selects a space and injects `SKILL_SPACE_ID`; absent both options, it is omitted. | `uv run pytest tests/integrations/test_mpa_skill_space.py` | pass (2026-09-11): test_mpa_skill_space 3/3 | -| FR-15/16 | T-12 | `AC-13`: `--compute-plane runtime` creates or converges the exact-name Runtime, releases it, waits for the current/newer Ready version, and normalizes endpoint/apig/key/runtime_id; duplicate exact names fail; `ArtifactType="image"`. | `uv run pytest tests/integrations/test_mpa_runtime.py` | pass (2026-09-11); ArtifactType=image verified live | -| FR-15 | T-12b | `AC-14`: `--compute-plane vefaas` reproduces route A exactly (deploy_image path). | `uv run pytest tests/cli/test_cli_mpa.py -k vefaas` | pass (2026-09-11) | -| FR-17/19 | T-13 | `AC-15`: Order SkillSpace→Tool→pre-seed→plane→binding completeness→finalize-seed→verify; pre-seed occurs before compute-plane create. | `uv run pytest tests/cli/test_cli_mpa.py -k orchestration` | pass (2026-09-11) | -| FR-18/20 | T-14 | `AC-16`: runtime/app id is plane-correct; APIG id is endpoint-derived or explicitly supplied; shared gateway without an id fails closed and does not finalize placeholders. | `uv run pytest tests/cli/test_cli_mpa.py -k gateway` | pass (2026-09-11) | -| FR-19 | T-17 | `AC-19`: Phase-1 seed writes a complete placeholder row; phase-2 overwrites only deploy-bound fields with real values. | `uv run pytest tests/integrations/test_mpa_meta_seed.py` | pass (2026-09-11) | -| all | T-15 | `AC-17`: Real E2E provisions Tool/Runtime, reaches Ready, and exercises Studio A2A plus two contexts. | Manual real run + Studio | partial/blocked: runtime and two distinct worker sessions passed; A2A final result fails on the selected image's ADK session revision conflict; Feishu also lacks a dedicated customer APIG id/credentials | -| all | T-16 | `AC-18`: pre-commit + CLI/unit regression pass; mpa-agent repo unchanged by this implementation. | `uv run pre-commit run --files `; targeted MPA suite; `uv run pytest tests/cli` | pass: 57 MPA tests; 1282 CLI passed/4 skipped; pre-commit hooks passed | - -## 8. Risks and Open Questions - -- **OQ-4 — Default compute plane — RESOLVED.** Default to `--compute-plane runtime` (`CreateRuntime` → `r-*`) to match the reference and mpa-agent's native model; `--compute-plane vefaas` reproduces route A. -- **OQ-5 — CreateTool env completeness — RESOLVED.** Clone the env set from a reference Codex worker Tool (e.g. `t-yeslt9bv9ckgnctgwaaf`) as the template, allowing per-key overrides. This is the most reliable path to a Ready sandbox. -- **OQ-6 — ArtifactType value — RESOLVED (verify in implementation).** The user approved a real smoke `CreateRuntime` during implementation on the test account (2112682748) to confirm the image `ArtifactType`/`ArtifactUrl` contract; findings are recorded back into this design. -- **Risk — real-resource cost.** Runtime/Tool/APIG creation is billable and not trivially reversible; `--dry-run` first, then confirm before real create. The test account (2112682748) is authorized for verification runs. -- **Blocker — selected mpa-agent image A2A result.** The runtime and delegated workers complete, but A2A replies fail with `The session has been modified in storage since it was loaded`; Studio cannot yet complete an interactive chat until the image fixes ADK session revision coordination. -- **Blocker — shared APIG cannot identify the customer IM gateway.** The verified Runtime reports `GatewayMode=Shared` and empty `GatewayInstanceId`; the endpoint does not map to an account APIG id. `--apig-instance-id` is the explicit adapter, but automatic dedicated gateway allocation is outside this implementation. -- **Blocker — Feishu verification.** Feishu credentials/bot installation and a dedicated customer APIG id were not available, so group-chat behavior remains unverified. -- Route A open items (control-plane `CreateMpaInstance` out of scope; CLI-side seeding accepted) remain as decided. - -## 9. Review and Delivery Record - -- Review status: reviewed via `review-spec` on 2026-09-11; OQ-4/OQ-5/OQ-6 resolved with the user. Status advanced to `approved` on 2026-09-11. -- Decisions: default compute plane `runtime`; CreateTool env cloned from a reference worker Tool; `ArtifactType` confirmed by a smoke `CreateRuntime` during implementation on test account 2112682748. -- Predecessor route A remains delivered and unchanged; this version is additive except the default compute plane. -- Review fixes: exact-name Tool/Runtime retries are deterministic; non-Ready Tool and newer Runtime versions are awaited; APIG resolution fails closed instead of selecting an unrelated gateway. -- Open blockers: Studio A2A final responses are blocked by the selected image's ADK session revision conflict; Feishu group chat is blocked by missing credentials/bot installation and a dedicated customer APIG id. -- Remaining scope: final local gates, commit, and push. The image/APIG/Feishu blockers are recorded as external residual risks, not reported as passing. diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-11-mpa-agent-provision-orchestration-alignment-design.zh.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-11-mpa-agent-provision-orchestration-alignment-design.zh.md deleted file mode 100644 index d4c425c6f..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-11-mpa-agent-provision-orchestration-alignment-design.zh.md +++ /dev/null @@ -1,226 +0,0 @@ -# mpa-agent 创建编排对齐(CreateTool + SkillSpace + Runtime)设计文档 - -## 元信息 - -- **Change ID:** `mpa-agent-oneclick-provision` -- **创建 / 修订:** 2026-09-11 / 2026-09-11 -- **生命周期状态:** `approved` -- **对应语言版本:** [2026-09-11-mpa-agent-provision-orchestration-alignment-design.md](2026-09-11-mpa-agent-provision-orchestration-alignment-design.md) -- **前序版本:** [2026-09-10-mpa-agent-oneclick-provision-design.zh.md](./2026-09-10-mpa-agent-oneclick-provision-design.zh.md)(`approved`,已实现)—— 本版扩展其范围,不替换已交付部分。 -- **相关组件 spec:** 暂未创建;扩展 CLI 契约面(见 §6)。 -- **参照实现:** `~/workspace/bytedance/mpa/mpa-runtime/docs/runtime-manager-openapi-map.md`(Runtime Manager 创建链路)。 - -## 1. 概述 - -### 1.1 问题 / 背景 - -已交付的路线 A(`2026-09-10` 设计,commit `18fa2485`)通过 `deploy_image`(VeFaaS + APIG,key 鉴权)→ 播种 `mpa_meta` → 注入 env → 验证 来创建 veadk 版 mpa-agent,并把 Codex 沙箱 **Tool id** 与任何 **Skill Space** 当作外部前提参数。 - -Runtime Manager 参照链路(`runtime-manager-openapi-map.md`)展示了创建方应负责的完整编排顺序: - -```text -... -> CreateSkillSpace/GetSkillSpace -> CreateTool -> tool_id - -> CreateRuntime(ArtifactUrl, RoleName, ToolId, Envs=PG*+SkillSpaceId+ToolId) - -> ReleaseRuntime -> GetRuntime Ready -> /run_sse 验证 -> 持久化绑定 -``` - -对照该参照,路线 A 尚缺两处: - -- **未编排 CreateTool。** Codex 沙箱 `Tool`(`t-*`,由 `mpa_codex_worker` 镜像构建)必须在创建 agent **之前**创建并拿到 id。路线 A 要求调用方传入已有 `t-*`。 -- **未编排 Skill Space。** 参照会创建/选择 Skill Space 并注入 `SKILL_SPACE_ID`;路线 A 仅在给定时透传 `--skill-space-id`。 - -还存在**计算面建模差异**:参照使用 AgentKit `CreateRuntime`(产出 `r-*` runtime;mpa-agent 自身 `agentkit.yaml` 即以 `runtime_id: r-yerdk5srnk54ppxtxy25` 交付),而路线 A 使用 `VeFaaS.deploy_image`(VeFaaS App + 自建 APIG)。两者都能起镜像,但只有 `CreateRuntime` 与参照资源模型及 mpa-agent 原生交付形态一致。 - -### 1.2 目标 - -让 `veadk mpa create` 对齐参照编排,能从镜像端到端拉起 mpa-agent,由 CLI 负责 Tool 与 Skill Space 的创建;PostgreSQL/OpenViking 仍为外部参数,且无需控制面 `mi-*` 记录。 - -- **G1 — 为每个 Agent 编排独立 CreateTool。** 默认针对每个新生成的 mpa-agent id,从提供的 worker 镜像创建一个专属 Codex 沙箱 `Tool`,并在创建 Runtime 前取得 `tool_id`;已有 `--agentkit-tool-id` 仅保留为显式调试/迁移逃生口。 -- **G2 — 编排 Skill Space。** 可选地创建或选择 AgentKit Skill Space 并注入 `SKILL_SPACE_ID`。 -- **G3 — 对齐计算面。** 通过 AgentKit `CreateRuntime`(`r-*`)创建 agent 以对齐参照与 mpa-agent 原生 runtime 模型,同时以开关保留既有 `deploy_image` 路径作为回退。 -- **G4 — 保持路线 A 保证。** `mpa_meta` 播种、身份适配(`IDENTITY_STARTUP_ENABLED=false`、`CLAW_SPACE_ID=csi-`)、public endpoint 权威、验证、密钥掩码、mpa-agent 无回归全部保留。 - -### 1.3 非目标 - -- 不创建 PostgreSQL 实例/database 或 OpenViking `ov-*` 资源(外部前提,同路线 A)。 -- 不构建镜像本身;`--image`/`--tool-image` 为输入(artifact 优先,符合参照「Artifact 来源」)。 -- 不实现 AIDAP Workspace/Branch/Database/DBAccount 创建(参照对 RDS 这样做,我们的 DB 是外部的)。 -- 不建控制面 `mi-*` 记录;veadk 版 mpa-agent 无需它。 - -## 2. 用户场景 - -### 场景 1:从镜像完整编排 - -**Given** 一个 mpa-agent 镜像、一个 Codex worker 镜像、一个已存在的外部 PostgreSQL、模型凭据、account/region。 -**When** 运维方带 `--tool-image `,且不带 `--mpa-agent-id`、`--agentkit-tool-id` 运行 `veadk mpa create`。 -**Then** CLI 生成全局唯一的 `mi-<12位小写字母数字>`,将 `-` 替换为 `_` 得到 Tool 名,创建该专属 Tool(拿到 `t-*`),可选创建/选择 Skill Space,以生成的 id 命名 Runtime,播种 `mpa_meta`、验证并输出 Studio A2A 连接指引。 - -### 场景 2:复用已有 Tool - -**Given** 一个已有 Codex worker Tool `t-*`(如 `t-yeslt9bv9ckgnctgwaaf`)。 -**When** 运维方传 `--agentkit-tool-id t-*` 且省略 `--tool-image`。 -**Then** CLI 跳过 CreateTool,复用该 id 继续。(保留路线 A 行为。) - -### 场景 3:Runtime 就绪与验证 - -**Given** 已创建的 runtime。 -**When** 创建完成。 -**Then** CLI 等待 runtime 达到 Ready(或 VeFaaS 应用 release),再探测 `/health`、`/readiness` 与 A2A agent-card,通过后报告成功。 - -### 场景 4:幂等重跑与回滚 - -**Given** 之前的部分执行。 -**When** 运维方以相同 名称/id 重跑。 -**Then** 按精确名称复用 Tool/Skill Space/Runtime。非 Ready Tool 会等待;复用 Runtime 时更新并发布以收敛镜像、Tool、env,并等待更新后的版本 Ready。出现多个精确同名资源时明确失败,不任意选择。创建后失败时,两阶段 `mpa_meta` 可保留占位供重试,但不会被报告为已完成绑定。 - -## 3. 功能需求 - -路线 A 的 FR-1..FR-11(前序设计)继续生效。本版新增: - -- **FR-12 — 每 Agent CreateTool 编排。** 省略 `mpa_agent_id` 时,CLI 生成 `mi-` 加 12 位小写字母数字。未提供 `--agentkit-tool-id` 时必须提供 `--tool-image`;CLI 经 `CreateTool` 创建 Codex 沙箱 Tool,默认以 agent id 将连字符替换为下划线后的值命名,并取得 `tool_id`。已实测的 Tool 契约保持不变。显式 `--agentkit-tool-id` 仅作为调试/迁移逃生口并跳过 CreateTool。 -- **FR-13 — 每 Agent 身份与幂等。** 正常自动生成的 agent id 每次调用唯一,因此映射到不同 Tool 名和 Runtime 名。显式传入同一个 `--mpa-agent-id` 时用于失败重试,可复用该派生名称下已 Ready 的 Tool。`--tool-name`、`--runtime-name` 仅保留为显式调试覆盖。 -- **FR-14 — Skill Space 编排。** 当提供 `--skill-space-name` 时,CLI 经 `agentkit.sdk.skills`(`list_skill_spaces`/`create_skill_space`/`get_skill_space`)创建或选择 Skill Space,并把解析出的 `SKILL_SPACE_ID` 注入 runtime env。仅提供 `--skill-space-id` 时直接使用;两者都不提供时省略 Skill Space。 -- **FR-15 — 计算面选择。** `--compute-plane {runtime,vefaas}` 选择创建方式:`runtime`(默认)用 AgentKit `CreateRuntime`/`ReleaseRuntime`(`r-*`,对齐参照与 mpa-agent 原生模型);`vefaas` 用既有 `deploy_image` 路径(路线 A)。两者须产出相同下游契约:public endpoint、APIG key 鉴权 API key、供 `mpa_meta` 的 APIG 实例 id。 -- **FR-16 — Runtime 路径 env 与绑定。** `--compute-plane runtime` 时,`CreateRuntime` 以 `ArtifactType`/`ArtifactUrl=`、`ToolId`、`RoleName`、key 鉴权 authorizer、`Envs`=路线 A runtime env 加 `SKILL_SPACE_ID`(解析出时)与 `AGENTKIT_TOOL_ID` 调用。env 变更放在 `CreateRuntime`/`UpdateRuntime`(绝不放 `ReleaseRuntime`,其无 `Envs`),符合参照约束。 -- **FR-17 — 有序幂等编排与回滚。** CLI 按 SkillSpace → Tool → **预播种 `mpa_meta`(FR-19)** → 计算面创建 → **完成 `mpa_meta`(FR-19)** → 验证 顺序执行。每个创建按 名称/client-token 幂等;资源创建后失败会显式列出已创建 id 以便清理。 -- **FR-18 — `mpa_meta` 字段来自任一计算面。** runtime 模式下 `runtime_id` 为 AgentKit runtime id(`r-*`),vefaas 模式下为 VeFaaS app id;`apig_instance_id`、`runtime_api_key`、`public_endpoint` 由所选计算面解析。`private_endpoint` 镜像 `public_endpoint`(FR-11 不变)。 -- **FR-19 — 首次容器启动前的两阶段播种(smoke 实测得出)。** mpa-agent 容器在 runtime release 后立即启动,启动时若 `mpa_meta` 行未完整就调用 `GetMpaInstanceConf`。在测试账号上该调用对 `arkclaw:GetMpaInstanceConf` 返回 `403 AccessDenied`,因此若播种发生在 release 之后,启动会失败。故播种改为两阶段,按调用方已知的 `mpa_agent_id` 主键: - - **阶段 1(计算面创建之前)**:以真实 `account_id`/`resource_account_id` 和 `runtime_id`/`public_endpoint`/`private_endpoint`/`runtime_api_key`/`apig_instance_id` 的**非空占位**播种 `mpa_meta` 行,使首次容器启动时 `mpa_instance_conf_ready()` 已为真、runtime 跳过 `GetMpaInstanceConf`。 - - **阶段 2(Ready 之后)**:用真实的部署解析值覆盖占位字段。这要求 seeder 支持显式 overwrite 更新(区别于路线 A 的 fill-empty),仅作用于阶段 1 的占位字段。 - - 占位值不影响 A2A 聊天启动(agent-card URL 来自单独注入的 `A2A_PUBLIC_URL`);真实 endpoint/apig/key 主要供 IM 网关路径使用,在阶段 2 完成。 -- **FR-20 — APIG 绑定完整性。** finalize 前必须得到真实非空的 `runtime_id`、`public_endpoint`、`runtime_api_key`、`apig_instance_id`。Runtime 模式仅接受显式 `--apig-instance-id`、计算面直接返回值或能从 public endpoint 唯一映射出的 APIG id;禁止回退到账号下任意 gateway。AgentKit 共享网关返回 `GatewayInstanceId=""` 时 fail-closed,阶段 1 占位不得 finalize。 - -## 4. 设计与契约影响 - -### 4.1 编排顺序(对齐参照) - -```text -解析参数,省略时生成 mpa_agent_id - -> [FR-14] ensure SkillSpace(创建/选择)-> skill_space_id - -> [FR-12/13] 从 mpa_agent_id 派生 Tool/Runtime 名 - -> ensure Tool(复用同 Agent Ready Tool 或 从 --tool-image CreateTool)-> tool_id - -> 组装 runtime env(路线 A env + SKILL_SPACE_ID + AGENTKIT_TOOL_ID) - -> [FR-19 阶段 1] 预播种 mpa_meta(真实账户 + 非空占位) - 使首次容器启动跳过 GetMpaInstanceConf(测试账号返回 403) - -> [FR-15/16] 计算面: - runtime : CreateRuntime(ToolId, ArtifactUrl, Envs) -> ReleaseRuntime -> GetRuntime Ready - vefaas : deploy_image(enable_key_auth=True) (路线 A) - -> public_endpoint, apig_instance_id, runtime_api_key, runtime_id/app_id - -> 要求 runtime/endpoint/key/APIG 均为真实非占位值;共享网关无客户 APIG id 时 - fail-closed(或显式传 --apig-instance-id) - -> [FR-19 阶段 2] finalize mpa_meta(用真实值覆盖占位) - -> [路线 A] 验证 /health /readiness /agent-card - -> 输出 Studio A2A 指引 -``` - -### 4.2 所选方案与备选 - -- **所选 — 扩展 CLI 负责 SkillSpace + Tool + Runtime,默认 `CreateRuntime`。** 对齐参照资源模型与 mpa-agent 原生 `r-*` 交付;以 `--compute-plane vefaas` 保留 `deploy_image` 回退,不丢弃已交付的路线 A。 -- **备选 — 保持 Tool/SkillSpace 外部(仅路线 A)。** 本迭代拒绝:用户要求对齐参照——由创建方创建 Tool 并在创建 agent 前拿到 id。 -- **备选 — 完全弃用 `deploy_image`。** 拒绝:它已交付、已测、在不需要 AgentKit Runtime 时有用,以开关保留。 - -### 4.3 接口(在路线 A 基础上增量) - -```text -veadk mpa create \ - ...(全部路线 A 选项)... \ - [--compute-plane runtime|vefaas] # 默认 runtime \ - [--tool-image ] # 设置且无 --agentkit-tool-id 时创建 Tool \ - [--mpa-agent-id mi-*] # 省略则生成 mi-<12位小写字母数字> \ - [--tool-name ] # 调试覆盖;默认把 mi-* 的 '-' 替换为 '_' \ - [--tool-role-name IDRoleForArkClawShareAgent] \ - [--skill-space-name ] [--skill-space-id ss-*] \ - [--runtime-role-name ] [--runtime-name ] # 默认 mpa-agent-id \ - [--min-instance 1] [--max-instance 1] \ - [--apig-instance-id ] # IM 路由使用的专属客户 APIG \ - [--agentkit-tool-id t-*] # 设置时跳过 CreateTool(路线 A 复用) -``` - -`--dry-run` 扩展为打印计划中的 SkillSpace/Tool/计算面动作与解析出的 env(密钥掩码),不进行任何云或 DB 写入。 - -### 4.4 SDK 事实(在账号 2112682748 实测) - -- Tools:`agentkit.sdk.tools.client.AgentkitToolsClient` — `create_tool`、`get_tool`、`list_tools`;`CreateToolRequest` 含 `image_url`、`command`、`port`、`tool_type`、`role_name`、`skill_space_id`、`authorizer_configuration`、`network_configuration`、`envs`、`client_token`。 -- Runtime:`agentkit.sdk.runtime.client.AgentkitRuntimeClient` — `create_runtime`、`release_runtime`、`get_runtime`、`list_runtime_instances`、`update_runtime`;`CreateRuntimeRequest` 含 `artifact_type`、`artifact_url`、`tool_id`、`role_name`、`authorizer_configuration`、`network_configuration`、`envs`、`min_instance`、`max_instance`、`client_token`。 -- Skills:`agentkit.sdk.skills.client.AgentkitSkillsClient` — `create_skill_space`、`get_skill_space`、`list_skill_spaces`。 -- 既有 Codex worker Tool 契约(来自 `t-yeslt9bv9ckgnctgwaaf`,镜像 `mpa_codex_worker:apihelpers-b3818f5-...`):`ToolType=Private`、`command=/opt/gem/run.sh`、`port=8000`、`cpu 2000/mem 4096`、`role IDRoleForArkClawShareAgent`、公网+私网 APIG 端点、key 鉴权。 - -### 4.5 状态、数据、幂等、回滚 - -- 幂等键:SkillSpace 按名;Tool 与 Runtime 按精确名称。单一匹配时复用,非 Ready Tool 等待;Runtime 经 `UpdateRuntime(..., ReleaseEnable=True)` 收敛配置并等待更新版本 Ready。多个精确同名资源视为歧义并失败。 -- `mpa_meta`:阶段 1 以占位写齐七个必填字段;仅在所有真实值通过完整性检查后,阶段 2 覆盖部署绑定字段。`runtime_id` 来源取决于 `--compute-plane`。 -- 回滚:创建后失败时显式列出已创建 `ss-*`/`t-*`/`r-*` id。完整自动拆除超出范围;阶段 1 占位可保留供幂等重试,但不报告为完成绑定。 - -### 4.6 权限与安全 - -- 在路线 A 凭据集上新增 AgentKit Tools/Skills/Runtime 创建权限。RDS 仍仅 `Describe` 且不使用。 -- 密钥处理不变:隐藏输入、`--dry-run` 掩码、不记录日志;不向 runtime 注入控制面凭据。 - -### 4.7 兼容性与受影响调用方 - -- 增量 CLI 选项;`--compute-plane runtime` 成为默认,这**改变了路线 A 的默认行为**(路线 A 一贯用 `deploy_image`)。这是经批准、限定于 `veadk mpa create` 的行为变更;`--compute-plane vefaas` 精确复现路线 A。 -- `VeFaaS.deploy_image` 签名相较路线 A 不变(已扩展并由 AC-10 兼容守卫)。 -- 不改 mpa-agent 源码/镜像/API。 - -## 5. 边界情况 - -| 场景 | 处理方式 | -| --- | --- | -| runtime 模式既无 `--tool-image` 也无 `--agentkit-tool-id` | 在任何云端或数据库写入前校验失败。 | -| `--tool-image` 与 `--agentkit-tool-id` 同时给 | 优先显式 `--agentkit-tool-id`;提示 `--tool-image` 被忽略。 | -| 同名 Tool 存在但非 Ready | 有界超时内等待 Ready;始终未就绪则带 tool id 失败。 | -| 存在多个精确同名 Tool 或 Runtime | 以歧义错误失败,绝不任意绑定一个资源。 | -| `--skill-space-name` 与 `--skill-space-id` 都不给 | 省略 `SKILL_SPACE_ID`;runtime 无 skill space 运行。 | -| `--compute-plane runtime` 但 artifact 类型未知 | 创建前带清晰消息失败;不创建半配置 runtime。 | -| Runtime 始终未 Ready | 带 runtime id 与最后状态失败;阶段 1 占位保持未 finalize,供明确重试/清理。 | -| 部分失败后重跑 | 按精确名称复用既有 SkillSpace/Tool/Runtime;阶段 1 fill-empty,阶段 2 仅覆盖占位字段。 | -| Runtime 为共享网关且 `GatewayInstanceId` 为空 | 除非用 `--apig-instance-id` 提供专属客户 gateway,否则 finalize 前 fail-closed;不选择账号下其他 gateway。 | -| `--dry-run` | 打印 SkillSpace/Tool/计算面计划 + 掩码 env;不写入。 | - -## 6. 涉及文件 - -- `veadk/cli/cli_mpa.py` — 新增 `--compute-plane`、`--tool-image`、`--tool-name`、`--skill-space-name`、runtime/APIG 选项;编排 SkillSpace → Tool → 预播种 → 计算面 → 完整性检查 → finalize → 验证。 -- `veadk/integrations/mpa/mpa_tool.py`(新)— CreateTool/按名复用辅助,镜像已实测 Codex worker Tool 契约。 -- `veadk/integrations/mpa/mpa_skill_space.py`(新)— 创建/选择 Skill Space 辅助。 -- `veadk/integrations/mpa/mpa_runtime.py`(新)— `CreateRuntime`/`ReleaseRuntime`/等 Ready 辅助,并把结果归一为 `(public_endpoint, apig_instance_id, runtime_api_key, runtime_id)`。 -- `veadk/integrations/mpa/mpa_provision.py` — 扩展 params/env 以含 skill space + tool 镜像。 -- `veadk/integrations/ve_faas/ve_faas.py` — 不变(复用 vefaas 回退)。 -- `tests/cli/test_cli_mpa.py`、`tests/integrations/test_mpa_tool.py`、`test_mpa_skill_space.py`、`test_mpa_runtime.py`(新增/扩展)— 桩 SDK 单测。 -- `prd-spec/features/mpa-agent-oneclick-provision/` — 本双语设计。 - -## 7. 验收标准与追踪 - -路线 A 的 AC-1..AC-10 保持绿色(代码路径不变)。新增: - -| 需求 | 任务 | 验收标准 | 测试 / 验证 | 结果 | -| --- | --- | --- | --- | --- | -| FR-12/13 | T-10 | `AC-11`:动态 CreateTool;单一同名 Tool 复用/等待,多个精确同名失败。 | `uv run pytest tests/integrations/test_mpa_tool.py` | pass(2026-09-11) | -| FR-12/13 | T-10b | `AC-11b`:自动生成合法唯一 `mi-*`,派生 Tool/Runtime 名;缺少 Tool 输入时在写入前失败。 | `uv run pytest tests/cli/test_cli_mpa.py -k generated_identity` | pass(2026-09-11) | -| FR-14 | T-11 | `AC-12`:创建/选择 Skill Space 并注入;无配置时省略。 | `uv run pytest tests/integrations/test_mpa_skill_space.py` | pass(2026-09-11) | -| FR-15/16 | T-12 | `AC-13`:创建或收敛精确同名 Runtime,发布并等待当前/更新版本 Ready;重名歧义失败;`ArtifactType=image`。 | `uv run pytest tests/integrations/test_mpa_runtime.py` | pass(2026-09-11);真机确认 ArtifactType=image | -| FR-15 | T-12b | `AC-14`:`--compute-plane vefaas` 保持路线 A。 | `uv run pytest tests/cli/test_cli_mpa.py -k vefaas` | pass(2026-09-11) | -| FR-17/19 | T-13 | `AC-15`:SkillSpace→Tool→预播种→计算面→绑定完整性→finalize→验证。 | `uv run pytest tests/cli/test_cli_mpa.py -k orchestration` | pass(2026-09-11) | -| FR-18/20 | T-14 | `AC-16`:APIG id 来自 endpoint 唯一映射或显式参数;共享网关无 id 时 fail-closed,不 finalize 占位。 | `uv run pytest tests/cli/test_cli_mpa.py -k gateway` | pass(2026-09-11) | -| FR-19 | T-17 | `AC-19`:阶段 1 写完整占位;阶段 2 仅以真实值覆盖部署绑定字段。 | `uv run pytest tests/integrations/test_mpa_meta_seed.py` | pass(2026-09-11) | -| 全部 | T-15 | `AC-17`:真机创建 Tool/Runtime、Ready,并验证 Studio A2A 与两个 context。 | 手动真机 + Studio | 部分通过/阻断:Runtime 与两个不同 worker session 通过;所选镜像因 ADK session revision 冲突导致 A2A 最终失败;飞书还缺专属 APIG id/凭据 | -| 全部 | T-16 | `AC-18`:pre-commit + CLI/相关单测回归;本实现不改 mpa-agent 仓库。 | `uv run pre-commit run --files <功能文件>`;MPA 测试;`uv run pytest tests/cli` | pass:MPA 57;CLI 1282 passed/4 skipped;pre-commit hooks 通过 | - -## 8. 风险与待决事项 - -- **OQ-4 — 默认计算面 —— 已解决。** 默认 `--compute-plane runtime`(`CreateRuntime` → `r-*`)以对齐参照与 mpa-agent 原生模型;`--compute-plane vefaas` 复现路线 A。 -- **OQ-5 — CreateTool env 完整性 —— 已解决。** 以参照 Codex worker Tool(如 `t-yeslt9bv9ckgnctgwaaf`)的 env 集作为模板克隆,允许按键覆盖。这是获得 Ready 沙箱最稳的路径。 -- **OQ-6 — ArtifactType 取值 —— 已解决(实现阶段验证)。** 用户批准在实现阶段于测试账号(2112682748)做一次真机 smoke `CreateRuntime`,确认镜像 `ArtifactType`/`ArtifactUrl` 契约;结论回写本设计。 -- **风险 — 真实资源成本。** Runtime/Tool/APIG 创建计费且不易回收;先 `--dry-run` 再确认后真实创建。测试账号(2112682748)已授权用于验证运行。 -- **阻断 — 所选 mpa-agent 镜像的 A2A 结果。** Runtime 与委派 worker 能完成,但 A2A 因 `The session has been modified in storage since it was loaded` 返回失败;镜像修复 ADK session revision 协调前,Studio 无法完成交互聊天。 -- **阻断 — 共享 APIG 无法标识客户 IM gateway。** 实测 Runtime 为 `GatewayMode=Shared` 且 `GatewayInstanceId` 为空,endpoint 不映射到账户 APIG id。`--apig-instance-id` 提供显式适配,但自动分配专属 gateway 不在本实现范围。 -- **阻断 — 飞书验证。** 未具备飞书凭据/机器人安装与专属客户 APIG id,拉群能力保持未验证。 -- 路线 A 待决项(控制面 `CreateMpaInstance` 范围外;CLI 侧播种已接受)维持既定结论。 - -## 9. 评审与交付记录 - -- 评审状态:2026-09-11 经 `review-spec` 评审;OQ-4/OQ-5/OQ-6 已与用户敲定。2026-09-11 状态推进为 `approved`。 -- 决策:默认计算面 `runtime`;CreateTool env 克隆自参照 worker Tool;`ArtifactType` 由实现阶段测试账号 2112682748 的 smoke `CreateRuntime` 确认。 -- 前序路线 A 保持已交付且不变;本版除默认计算面外为增量。 -- Review 修复:Tool/Runtime 精确名称重试已确定化;等待非 Ready Tool 与 Runtime 更新版本;APIG 解析 fail-closed,不选择无关 gateway。 -- 未决阻塞项:Studio A2A 最终响应受所选镜像 ADK session revision 冲突阻断;飞书拉群受凭据/机器人安装和专属客户 APIG id 阻断。 -- 剩余范围:最终本地门禁、提交并推送;镜像/APIG/飞书阻断作为外部残余风险留存,不标记为通过。 diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-12-mpa-agent-studio-a2a-streaming-design.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-12-mpa-agent-studio-a2a-streaming-design.md deleted file mode 100644 index 8ce782a7a..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-12-mpa-agent-studio-a2a-streaming-design.md +++ /dev/null @@ -1,230 +0,0 @@ -# mpa-agent Studio A2A Streaming Design - -## Metadata - -- **Change ID:** `mpa-agent-studio-a2a-streaming` -- **Created / revised:** 2026-09-12 / 2026-09-12 -- **Lifecycle status:** `approved` -- **Chinese version:** [2026-09-12-mpa-agent-studio-a2a-streaming-design.zh.md](2026-09-12-mpa-agent-studio-a2a-streaming-design.zh.md) -- **Previous design:** [2026-09-11-mpa-agent-provision-orchestration-alignment-design.md](2026-09-11-mpa-agent-provision-orchestration-alignment-design.md) -- **Implementation branches:** mpa-agent `fix/a2a-studio-streaming`; veadk-python `feat/mpa-agent-oneclick-provision` - -## 1. Overview - -### 1.1 Problem and evidence - -VeADK Studio can connect to an mpa-agent Runtime through its A2A compatibility bridge, but `/run_sse` is not a streaming path today: - -1. The Studio BFF always calls A2A `message/send` with `blocking=true`. -2. The BFF buffers the complete JSON-RPC response through `_runtime_proxy_buffer()` before emitting one Studio SSE event. -3. The mpa-agent card currently returns `capabilities={}` and does not advertise streaming. -4. Sandbox `CodexNormalizedEvent` objects are sent through `codex_event_handler` to PostgreSQL and the Session Stream, but they do not automatically enter the A2A `EventQueue`. - -Live verification proved that the Runtime, sandbox, and final result work. Runtime `r-yeuujrrcowb21078p9jh` v25 completes the command in about 13–16 seconds, returns a completed A2A task, and places the final result in an ADK function-response data part. The missing component is real-time protocol bridging during execution. - -### 1.2 Goals - -- **G1:** Studio displays A2A task status, sandbox startup, tool calls, command output, text deltas, and final output while execution is in progress. -- **G2:** A user request executes at most once; streaming failure must never trigger an unsafe automatic resubmission. -- **G3:** Preserve ordinary ADK Runtime, legacy non-streaming A2A Runtime, Web, Feishu, scheduled-task, and bot-group behavior. -- **G4:** Add no database schema, direct Studio-to-mpa-agent database access, or process-global event bus. -- **G5:** A task continues after browser disconnect. Initial delivery does not support live stream resumption; refresh can retrieve the final persisted result. - -### 1.3 Non-goals - -- Live resumption from the last event ID is deferred. -- The mpa-codex-worker protocol is unchanged. -- The A2A SDK is not replaced. -- Agent, Tool, Runtime provisioning and database tables are unchanged. -- Feishu and scheduled-task presentation and terminal semantics are unchanged. - -## 2. User scenarios - -### Scenario 1: Observe sandbox execution in real time - -**Given** the mpa-agent card advertises `streaming=true`. -**When** a Studio user asks the sandbox to run a command with staged output. -**Then** Studio displays working, tool start, output deltas, tool completion, and final text before the task ends. - -### Scenario 2: Legacy Runtime compatibility - -**Given** an A2A Runtime does not advertise streaming. -**When** a user starts a chat. -**Then** Studio retains the existing blocking `message/send` behavior and displays the final result. - -### Scenario 3: Connection loss - -**Given** the Runtime accepted the task and started emitting events. -**When** the browser or Studio BFF connection disconnects. -**Then** execution and persistence continue, Studio does not resubmit the request, and refresh can retrieve the final result from the original session/task. Live replay of missed deltas is not required initially. - -### Scenario 4: Concurrent sessions - -**Given** two Studio sessions use the same Runtime concurrently. -**When** both sandboxes execute. -**Then** each session receives only its own invocation events and uses an isolated sandbox. - -## 3. Functional requirements - -- **FR-1 — Streaming capability.** The mpa-agent card advertises `capabilities.streaming=true` while retaining `message/send`. -- **FR-2 — Standard streaming call.** Studio uses `message/stream` when supported and parses upstream SSE incrementally. -- **FR-3 — Invocation-local event bridge.** mpa-agent composes the existing `codex_event_handler` for the current A2A invocation and sends allowlisted `CodexNormalizedEvent` projections to the current A2A `EventQueue`. No process-global relay is introduced. -- **FR-4 — Single terminal owner.** The relay emits only progress and artifacts. The official executor and existing distributed-control linearization remain the sole owners of completed, failed, and canceled task states. -- **FR-5 — Studio mapping.** The BFF maps A2A status, text, data, and artifact events into the existing Studio ADK event contract while preserving event, invocation, command, append, and terminal semantics. -- **FR-6 — Safe fallback.** Missing streaming capability selects `message/send` before execution. A streaming attempt may fall back once only when the server explicitly reports method-not-supported before any event is observed. Timeout, 5xx, 401/403, malformed data, and failures after the first event never resubmit. -- **FR-7 — Disconnect semantics.** Disconnect does not cancel or resubmit the Runtime task. Task/session identity is retained so refresh can read the final persisted result. Live resumption is deferred. -- **FR-8 — Backpressure.** Tool states and terminal events cannot be dropped. Adjacent text deltas may be coalesced at no more than 50 ms or 512 characters, and individual output events are bounded. -- **FR-9 — Safe output.** Only normalized, display-safe fields are emitted. API keys, authorization values, endpoint credentials, full environment variables, and raw worker payloads are excluded. -- **FR-10 — Compatibility.** Ordinary ADK `/run_sse`, Web, Feishu, scheduled tasks, bot-group behavior, and legacy blocking A2A remain unchanged. - -## 4. Design - -### 4.1 Flow - -```text -Studio /run_sse - -> inspect agent-card capability - -> message/stream - -> mpa-agent A2A executor - -> invocation-local codex_event_handler - -> existing path: Session Stream + PostgreSQL - -> new path: A2A progress/artifact event - -> A2A SSE - -> veadk incremental decoder / adapter - -> Studio ADK SSE -``` - -### 4.2 mpa-agent boundary - -`app/a2a/app.py` passes `AgentCapabilities(streaming=True)` when building the agent card. The A2A SDK already registers `message/stream`; no new HTTP route is added. - -`ObservableA2aAgentExecutor.execute()` creates a composed callback scoped to the current invocation: - -1. Invoke the existing handler to preserve Session Stream publication and persistence. -2. Project allowlisted normalized events into A2A progress/artifact events. -3. Enqueue them on the current request's `EventQueue`. -4. Let the closure expire with the invocation; keep no global session-to-queue registry. - -`sandbox_task` retains ADK-native `tool_context.actions.skip_summarization` to prevent repeated model calls. Its final output remains a standard function-response artifact. - -### 4.3 Public event contract - -Progress uses an A2A data part: - -```json -{ - "kind": "data", - "metadata": {"schemaVersion": "mpa.sandbox-event.v1"}, - "data": { - "eventId": "worker-event-id", - "invocationId": "e-...", - "eventType": "tool.call", - "payload": {} - } -} -``` - -Allowed event types are `status.notice`, `message.delta`, `tool.call`, `tool.output`, `tool.result`, `tool.error`, `file.change`, `usage.updated`, `invocation.completed`, `invocation.failed`, and `invocation.cancelled`. `thought.delta` is hidden from Studio by default. - -### 4.4 Studio bridge - -Add `veadk/cli/runtime_a2a_stream.py` with these responsibilities only: - -- Incremental A2A SSE frame decoding. -- JSON-RPC/A2A external response validation. -- A2A event to Studio ADK event conversion. -- Per-request deduplication, tool pairing, delta coalescing, and terminal protection. - -`cli_frontend.py` remains responsible for capability selection, authenticated upstream connection, fallback, and `StreamingResponse`. The streaming branch must not call `_runtime_proxy_buffer()`. - -### 4.5 Event mapping - -| A2A event | Studio event | Presentation | -| --- | --- | --- | -| submitted/working | status metadata | running state | -| `status.notice` | progress metadata | sandbox startup/wait | -| `tool.call` | `functionCall` | tool card start | -| `tool.output` | sandbox progress metadata | live command output | -| `tool.result` | `functionResponse` | tool completion | -| `message.delta` | text part with `partial=true` | incremental text | -| final function response | text part with `partial=false` | final text | -| failed/canceled | error/status event | failure/cancel state | - -### 4.6 Ordering and terminal behavior - -- Prefer `(taskId, eventId)` as the deduplication key. -- Preserve call/output/result order for each `commandId`. -- Ignore late non-terminal events after terminal state. -- Emit at most one final text event and one terminal marker per task. -- The callback relay does not submit terminal task state and therefore cannot bypass distributed control. - -### 4.7 Disconnect and recovery - -- After task acceptance or the first event, connection failure ends only the browser stream. -- Do not automatically call `message/send` or retry `message/stream`. -- The A2A SDK continues consuming and persisting the task in the background. -- Studio retains the session/task mapping and loads the final result from task or session history after refresh. -- Complete replay of missed deltas is deferred. - -## 5. Edge cases - -| Case | Behavior | -| --- | --- | -| Agent card has no streaming field | Use existing `message/send` | -| Method-not-supported before any event | Fall back once to blocking mode | -| Stream drops after submitted | Do not resubmit; offer refresh for final result | -| 401/403 | Surface authentication failure; no fallback | -| Malformed SSE/JSON | Surface protocol failure; no resubmission | -| Duplicate event ID | Drop duplicate | -| Slow consumer | Coalesce text; retain tool result and terminal | -| Empty final text | Do not fabricate output; close with actual task state | -| Concurrent sessions | Bind callback closure to each invocation/EventQueue | - -## 6. Implementation tasks - -| Task | Work | Dependency | Deliverable | -| --- | --- | --- | --- | -| `T-1` | Isolate current mpa-agent work on a feature branch | None | `fix/a2a-studio-streaming` | -| `T-2` | Write failing agent-card capability tests | T-1 | Capability contract test | -| `T-3` | Write failing invocation relay tests | T-2 | Ordering, isolation, terminal tests | -| `T-4` | Implement mpa-agent capability and relay | T-3 | Raw `message/stream` emits progress | -| `T-5` | Define and test the veadk SSE decoder | T-4 | `runtime_a2a_stream.py` | -| `T-6` | Integrate `message/stream` and safe fallback | T-5 | Incremental Studio `/run_sse` | -| `T-7` | Map tool cards, output deltas, and final text | T-6 | Studio streaming presentation | -| `T-8` | Test disconnect, concurrency, cancel, and old Runtime | T-7 | Integration evidence | -| `T-9` | Regress Feishu and bot-group behavior | T-8 | Non-regression evidence | - -## 7. Tests and acceptance - -| Requirement | Acceptance | Verification | -| --- | --- | --- | -| FR-1 | `AC-1`: agent-card returns `streaming=true` | mpa-agent unit test and curl | -| FR-2/3 | `AC-2`: `message/stream` emits before task completion | Raw A2A SSE integration test | -| FR-3/5 | `AC-3`: tool start, output, and completion reach Studio in order | Cross-repo integration test | -| FR-4 | `AC-4`: one terminal event per task | Unit and E2E tests | -| FR-6 | `AC-5`: fallback only on pre-event method-not-supported | veadk unit tests | -| FR-6/7 | `AC-6`: stream loss creates no second task | Request count and task-store assertion | -| FR-7 | `AC-7`: task completes after disconnect and refresh loads final output | Live disconnect test | -| FR-8 | `AC-8`: slow consumers retain tool results and terminal events | Bounded-queue stress test | -| FR-9 | `AC-9`: no secret, credential, or raw environment data in stream | Secret scan and payload assertions | -| FR-10 | `AC-10`: legacy Runtime, Web, Feishu, scheduled task, and bot group pass | Regression and live checks | -| All | `AC-11`: `sleep 2; echo step-1; sleep 2; echo step-2` arrives over time | Timestamped E2E record | -| All | `AC-12`: concurrent sessions use distinct sandboxes without cross-streaming | Concurrent E2E | - -Completion requires unit tests, raw A2A streaming evidence, visible Studio progress and final output, legacy Runtime fallback, and Feishu non-regression. A visible final response alone is insufficient. - -## 8. Risks and rollback - -- **Duplicate execution:** fallback is limited to pre-event method-not-supported. -- **Data exposure:** project only allowlisted normalized payload fields. -- **Terminal races:** progress relay never produces terminal task state. -- **Memory pressure:** use an invocation-local bounded queue and coalesce text. -- **Release constraints:** the test registry has a tag limit; delete only test tags proven unused by any Ready Runtime. -- **Rollback:** restore the previous Ready mpa-agent Runtime version and disable the capability path in veadk to return to `message/send`. No data migration is involved. - -## 9. Review and decision record - -- 2026-09-12: `review-spec` found that switching to `message/stream` alone cannot expose sandbox side-channel events; invocation-local callback relay was added. -- 2026-09-12: PostgreSQL polling and a process-global relay were rejected to avoid database coupling and multi-replica routing complexity. -- 2026-09-12: Initial live stream resumption was explicitly deferred. Tasks must not be resubmitted, and refresh must recover the final persisted result. -- 2026-09-12: mpa-agent work was safely moved from `main` to `fix/a2a-studio-streaming` with all uncommitted changes preserved. diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-12-mpa-agent-studio-a2a-streaming-design.zh.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-12-mpa-agent-studio-a2a-streaming-design.zh.md deleted file mode 100644 index c352d82ef..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-12-mpa-agent-studio-a2a-streaming-design.zh.md +++ /dev/null @@ -1,230 +0,0 @@ -# mpa-agent Studio A2A 流式输出设计文档 - -## 元信息 - -- **Change ID:** `mpa-agent-studio-a2a-streaming` -- **创建 / 修订:** 2026-09-12 / 2026-09-12 -- **生命周期状态:** `approved` -- **对应语言版本:** [2026-09-12-mpa-agent-studio-a2a-streaming-design.md](2026-09-12-mpa-agent-studio-a2a-streaming-design.md) -- **前序设计:** [2026-09-11-mpa-agent-provision-orchestration-alignment-design.zh.md](2026-09-11-mpa-agent-provision-orchestration-alignment-design.zh.md) -- **实现分支:** mpa-agent `fix/a2a-studio-streaming`;veadk-python `feat/mpa-agent-oneclick-provision` - -## 1. 概述 - -### 1.1 问题与证据 - -veadk Studio 已能通过 A2A 兼容桥接连接 mpa-agent Runtime,但当前 `/run_sse` 并不是真正的流式链路: - -1. Studio BFF 固定调用 A2A `message/send`,并设置 `blocking=true`。 -2. BFF 使用 `_runtime_proxy_buffer()` 缓冲完整 JSON-RPC 响应,任务结束前不会向浏览器发送事件。 -3. mpa-agent 的 agent-card 当前返回 `capabilities={}`,未声明 streaming。 -4. sandbox 的 `CodexNormalizedEvent` 通过 `codex_event_handler` 写入 PostgreSQL 和 Session Stream,但不会自动进入 A2A `EventQueue`。 - -真机验证已证明 Runtime、sandbox 与最终结果本身正常:Runtime `r-yeuujrrcowb21078p9jh` v25 能在约 13–16 秒完成命令,A2A task 为 `completed`,最终结果位于 artifact 的 ADK function-response data part。缺失的是任务执行期间的实时协议桥接。 - -### 1.2 目标 - -- **G1:** Studio 实时展示 A2A task 状态、sandbox 启动、工具调用、命令输出、文本增量和最终结果。 -- **G2:** 同一用户请求最多执行一次;流式失败不得通过重新提交请求实现自动降级。 -- **G3:** 不破坏普通 ADK Runtime、旧版非流式 A2A Runtime、Web、飞书、定时任务和机器人拉群。 -- **G4:** 不新增数据库 schema,不让 Studio 直接读取 mpa-agent 数据库,不新增全局事件总线。 -- **G5:** 浏览器断开后任务继续执行;首期不支持实时断点续传,刷新后可读取最终持久化结果。 - -### 1.3 非目标 - -- 首期不实现从最后一个 event id 继续实时订阅。 -- 不修改 mpa-codex-worker 协议。 -- 不替换 A2A SDK。 -- 不修改 Agent、Tool、Runtime 创建流程和数据库表。 -- 不改变飞书和定时任务的展示与终态语义。 - -## 2. 用户场景 - -### 场景 1:实时查看 sandbox 执行 - -**Given** mpa-agent agent-card 声明 `streaming=true`。 -**When** 用户在 Studio 要求 sandbox 执行一个分阶段输出的命令。 -**Then** Studio 在任务结束前依次显示 working、工具开始、输出增量、工具完成和最终文本。 - -### 场景 2:旧 Runtime 兼容 - -**Given** A2A Runtime 未声明 streaming。 -**When** 用户发起聊天。 -**Then** Studio 继续使用现有阻塞式 `message/send`,最终结果正常显示。 - -### 场景 3:连接中断 - -**Given** Runtime 已接受任务并开始返回事件。 -**When** 浏览器或 Studio BFF 连接中断。 -**Then** 任务继续执行并持久化;Studio 不自动重提原请求。刷新后用户可从原 session/task 读取最终结果,但首期不续传中断期间的实时增量。 - -### 场景 4:并发 session - -**Given** 两个 Studio session 同时调用同一个 Runtime。 -**When** 两个 sandbox 并发执行。 -**Then** 每个 session 只收到自己的 invocation 事件,且 sandbox 实例保持隔离。 - -## 3. 功能需求 - -- **FR-1 — Streaming 能力声明。** mpa-agent agent-card 必须声明 `capabilities.streaming=true`,同时继续支持 `message/send`。 -- **FR-2 — 标准流式调用。** Studio 在 capability 为 true 时使用 `message/stream`,并对上游 SSE 逐 frame 解析。 -- **FR-3 — Invocation 内事件桥接。** mpa-agent 在当前 A2A invocation 内组合现有 `codex_event_handler`,将白名单化的 `CodexNormalizedEvent` 同步投递到当前 A2A `EventQueue`;不建立进程级全局 relay。 -- **FR-4 — 单一终态所有权。** callback relay 只发布 progress/artifact;A2A task 的 completed/failed/canceled 终态继续由官方 executor 和现有 distributed-control 线性化逻辑产生。 -- **FR-5 — Studio 事件映射。** BFF 将 A2A status、text、data、artifact 转换为现有 Studio ADK event 结构,并保留 `eventId`、`invocationId`、`commandId`、`eventType` 和 append/terminal 语义。 -- **FR-6 — 安全降级。** agent-card 未声明 streaming 时直接选择 `message/send`;只有 `message/stream` 在尚未产生事件前明确返回 method-not-supported 时,才允许回退一次。网络超时、5xx、401/403、协议损坏或已收到事件后禁止重提。 -- **FR-7 — 断线语义。** 客户端断开不取消 Runtime 任务,不自动重提请求;task/session 标识必须保留,刷新后可读取最终持久化结果。首期不提供实时断点续传。 -- **FR-8 — 背压。** 工具状态和终态不得丢失;连续文本 delta 可按最多 50ms 或 512 字符合并,单个输出事件必须有长度上限。 -- **FR-9 — 安全输出。** 仅转发 normalized 且允许展示的字段,不透传 API key、Authorization、endpoint credential、完整环境变量或未经脱敏的原始 worker payload。 -- **FR-10 — 兼容性。** 普通 ADK `/run_sse`、Web、飞书、定时任务、机器人拉群以及旧版 A2A 阻塞模式行为保持不变。 - -## 4. 方案设计 - -### 4.1 总体流程 - -```text -Studio /run_sse - -> 读取 agent-card capability - -> message/stream - -> mpa-agent A2A executor - -> invocation-local codex_event_handler - -> 原路径:Session Stream + PostgreSQL - -> 新路径:A2A progress/artifact event - -> A2A SSE - -> veadk incremental decoder / adapter - -> Studio ADK SSE -``` - -### 4.2 mpa-agent 边界 - -`app/a2a/app.py` 在构建 agent-card 时传入 `AgentCapabilities(streaming=True)`。A2A SDK 已原生注册 `message/stream`,不新增 HTTP 路由。 - -`ObservableA2aAgentExecutor.execute()` 为当前 invocation 构造组合 callback: - -1. 调用原 `codex_event_handler`,保持现有 Session Stream 和持久化。 -2. 将允许展示的 normalized event 投影为 A2A progress/artifact。 -3. 投递到当前请求的 `EventQueue`。 -4. invocation 结束即释放闭包,不维护全局 session/event queue 映射。 - -`sandbox_task` 继续使用 ADK 原生 `tool_context.actions.skip_summarization`,避免 sandbox 完成后主模型重复调用;A2A 最终结果使用标准 function-response artifact。 - -### 4.3 公开事件契约 - -过程事件采用 A2A data part: - -```json -{ - "kind": "data", - "metadata": {"schemaVersion": "mpa.sandbox-event.v1"}, - "data": { - "eventId": "worker-event-id", - "invocationId": "e-...", - "eventType": "tool.call", - "payload": {} - } -} -``` - -允许的 `eventType`:`status.notice`、`message.delta`、`tool.call`、`tool.output`、`tool.result`、`tool.error`、`file.change`、`usage.updated`、`invocation.completed`、`invocation.failed`、`invocation.cancelled`。`thought.delta` 默认不对 Studio 展示。 - -### 4.4 Studio 桥接 - -新增 `veadk/cli/runtime_a2a_stream.py`,职责限定为: - -- A2A SSE frame 增量解码。 -- JSON-RPC/A2A 外部响应边界校验。 -- A2A event 到 Studio ADK event 的转换。 -- 请求内去重、tool call/result 配对、文本 delta 合并和终态保护。 - -`cli_frontend.py` 只负责 capability 选择、上游连接、鉴权、fallback 和 `StreamingResponse`。流式路径不得调用 `_runtime_proxy_buffer()`。 - -### 4.5 事件映射 - -| A2A 事件 | Studio 事件 | 展示 | -| --- | --- | --- | -| submitted/working | 状态 metadata | 执行中 | -| `status.notice` | progress metadata | 沙箱启动/等待 | -| `tool.call` | `functionCall` | 工具卡片开始 | -| `tool.output` | sandbox progress metadata | 实时命令输出 | -| `tool.result` | `functionResponse` | 工具完成 | -| `message.delta` | text part,`partial=true` | 文本增量 | -| final function response | text part,`partial=false` | 最终文本 | -| failed/canceled | error/status event | 失败或取消 | - -### 4.6 去重、顺序与终态 - -- 去重键优先使用 `(taskId, eventId)`。 -- 同一 `commandId` 的 call/output/result 必须保持顺序。 -- 收到 terminal 后忽略迟到的非终态事件。 -- 每个 task 最多向 Studio发送一个最终文本事件和一个 terminal marker。 -- A2A callback 不自行提交 terminal status,避免绕过 distributed-control。 - -### 4.7 断线与恢复 - -- 上游已接受任务或 Studio 已收到任何事件后,连接失败只结束当前浏览器流。 -- 不自动调用 `message/send` 或再次调用 `message/stream`。 -- A2A SDK 继续后台消费并持久化任务。 -- Studio 保留 session/task 映射;页面刷新后读取 task 或 session history 展示最终结果。 -- 首期不保证恢复中断期间的完整增量序列。 - -## 5. 边界情况 - -| 场景 | 处理方式 | -| --- | --- | -| agent-card 无 streaming 字段 | 使用现有 `message/send` | -| stream 建立前返回 method-not-supported | 回退一次阻塞模式 | -| 已收到 submitted 后断流 | 不重提;提示连接中断,允许刷新查看结果 | -| 401/403 | 返回认证错误,不回退 | -| malformed SSE/JSON | 返回协议错误,不重提 | -| 重复 event id | 丢弃重复事件 | -| 慢消费者 | 合并文本 delta;不得丢工具结果和终态 | -| final text 为空 | 不伪造成功正文,按真实 task 状态收尾 | -| 两个 session 并发 | callback 闭包绑定各自 invocation/EventQueue | - -## 6. 实施任务 - -| 任务 | 内容 | 依赖 | 交付物 | -| --- | --- | --- | --- | -| `T-1` | 整理 mpa-agent 独立分支与现有修复 diff | 无 | `fix/a2a-studio-streaming` | -| `T-2` | 先写 agent-card streaming 红灯测试 | T-1 | capability contract test | -| `T-3` | 先写 invocation callback relay 红灯测试 | T-2 | 顺序、隔离、终态测试 | -| `T-4` | 实现 mpa-agent capability 与 relay | T-3 | 原始 `message/stream` 可见过程事件 | -| `T-5` | 定义并测试 veadk SSE decoder | T-4 | `runtime_a2a_stream.py` | -| `T-6` | 接入 Studio `message/stream` 与安全 fallback | T-5 | `/run_sse` 增量代理 | -| `T-7` | 工具卡片、输出增量、最终文本映射 | T-6 | Studio 流式展示 | -| `T-8` | 断线、并发、取消和旧 Runtime 回归 | T-7 | 集成验证报告 | -| `T-9` | 飞书和机器人拉群回归 | T-8 | 无回归证据 | - -## 7. 测试与验收 - -| 需求 | 验收标准 | 验证方式 | -| --- | --- | --- | -| FR-1 | `AC-1`:agent-card 返回 `streaming=true` | mpa-agent 单测 + curl | -| FR-2/3 | `AC-2`:`message/stream` 在任务结束前返回事件 | 原始 A2A SSE 集成测试 | -| FR-3/5 | `AC-3`:工具开始、输出、完成按顺序进入 Studio | 两仓库集成测试 | -| FR-4 | `AC-4`:每个 task 只有一个 terminal event | 单元 + E2E | -| FR-6 | `AC-5`:只有 method-not-supported 且零事件时回退 | veadk 单元测试 | -| FR-6/7 | `AC-6`:断流不产生第二个 task | 请求计数断言 + task store | -| FR-7 | `AC-7`:断开后任务完成,刷新可读最终结果 | 真机断线测试 | -| FR-8 | `AC-8`:慢消费者不丢 tool result/terminal | 有界队列压力测试 | -| FR-9 | `AC-9`:流中无密钥、credential、原始 env | secret scan + payload 断言 | -| FR-10 | `AC-10`:旧 Runtime、Web、飞书、定时任务和拉群通过 | 回归测试 + 真机验证 | -| 全部 | `AC-11`:`sleep 2; echo step-1; sleep 2; echo step-2` 分时到达 | 带时间戳 E2E 记录 | -| 全部 | `AC-12`:两个 session 使用不同 sandbox 且事件不串流 | 并发 E2E | - -完成标准要求同时满足:单元测试通过、原始 A2A SSE 有过程事件、Studio 页面真机可见过程与最终文本、旧 Runtime fallback 正常、飞书核心链路无回归。只看到最终结果不算完成。 - -## 8. 风险与回滚 - -- **重复执行风险:** fallback 严格限制为零事件的 method-not-supported。 -- **事件泄漏风险:** 对 normalized payload 使用白名单,不透传原始 worker 数据。 -- **终态竞争风险:** progress relay 不生成 terminal,终态仍归官方 executor。 -- **内存风险:** invocation 内有界队列和文本合并;任务结束立即释放。 -- **发布风险:** 测试 CR 仓库存在 tag 上限,发布前仅删除确认未被 Ready Runtime 使用的测试 tag。 -- **回滚:** mpa-agent 回滚到上一 Ready Runtime version;veadk 关闭 capability 路径后自动回到 `message/send`。两者均不涉及数据迁移。 - -## 9. 评审与决策记录 - -- 2026-09-12:`review-spec` 发现仅切换 `message/stream` 无法获得 sandbox 旁路事件,补充 invocation-local callback relay。 -- 2026-09-12:拒绝 PostgreSQL 轮询和进程级全局 relay,避免数据库耦合与多副本路由复杂度。 -- 2026-09-12:确认首期不做断线后实时续传;必须保证任务不重复提交,刷新可读取最终结果。 -- 2026-09-12:mpa-agent 已从 `main` 安全迁移到 `fix/a2a-studio-streaming`,未提交修改完整保留。 diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-12-mpa-agent-studio-a2a-streaming-verification-cases.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-12-mpa-agent-studio-a2a-streaming-verification-cases.md deleted file mode 100644 index a38249131..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-12-mpa-agent-studio-a2a-streaming-verification-cases.md +++ /dev/null @@ -1,48 +0,0 @@ -# mpa-agent Studio A2A Streaming Verification Cases - -## Environment - -- mpa-agent branch: `fix/a2a-studio-streaming` -- veadk-python branch: `feat/mpa-agent-oneclick-provision` -- Test Runtime: `r-yeuujrrcowb21078p9jh` -- Studio: `http://127.0.0.1:8001` -- Evidence directory: `.artifacts/a2a-streaming/` in each repository; secrets are prohibited. - -## Cases - -| Case | Coverage | Preconditions and action | Expected result | Evidence | -| --- | --- | --- | --- | --- | -| `VC-1` | FR-1 | curl the agent card | `capabilities.streaming=true` | `agent-card.json` | -| `VC-2` | FR-2/3/5 | Raw `message/stream` runs `sleep 2; echo step-1; sleep 2; echo step-2` | working, tool events, and timed output arrive before completion | `raw-a2a-stream.jsonl`, timestamp log | -| `VC-3` | FR-2/5 | Run VC-2 through Studio `/run_sse` | Browser stream receives progress and deltas before completion | `studio-stream.txt`, screenshot | -| `VC-4` | FR-4 | Count terminal/final events for one execution | One terminal and one final body | `terminal-count.txt` | -| `VC-5` | FR-6 | Mock a card without streaming capability | Exactly one `message/send` call | pytest output | -| `VC-6` | FR-6 | Mock pre-event method-not-supported | One fallback only; one task creation | pytest output | -| `VC-7` | FR-6/7 | Disconnect Studio after first event | No second request; original task completes | Request count and task JSON | -| `VC-8` | FR-7 | Refresh the original session after VC-7 | Final result is readable; missed deltas need not replay | Studio screenshot and task JSON | -| `VC-9` | FR-8 | Slow consumer with many deltas | Text may coalesce; tool result and terminal remain | pytest output | -| `VC-10` | FR-9 | Scan all streamed payloads | No API key, Authorization, full env, or signed URL | Secret-scan output | -| `VC-11` | FR-10 | Connect to a legacy non-streaming Runtime | Blocking mode still returns final text | `legacy-runtime.txt` | -| `VC-12` | FR-10 | Exercise Web, Feishu DM/group/group-add, and scheduled tasks | Behavior remains unchanged | Regression logs; mark blocked if external prerequisites are unavailable | -| `VC-13` | FR-3/5 | Run different markers in two concurrent Studio sessions | Distinct sandbox/session and no cross-streaming | `concurrent-streams/` | -| `VC-14` | FR-4/7 | Cancel during streaming | One canceled terminal and downstream cancellation | cancel JSONL | -| `VC-15` | FR-2/5 | Direct model answer | Text streams without sandbox events | `direct-answer-stream.txt` | - -## Execution order and failure handling - -1. Unit tests cover VC-1, VC-4 through VC-6, VC-9, and VC-10. -2. Local integration covers VC-2, VC-3, VC-11, and VC-15. -3. The test Runtime covers VC-2 through VC-4, VC-7, VC-8, VC-13, and VC-14. -4. Run VC-12 last to avoid touching external channels before protocol stability. -5. Any P0/P1 case failure returns the loop to TDD implementation and blocks review. - -## Execution result (2026-09-12) - -- `VC-1`–`VC-11`, `VC-13`, and `VC-15`: pass against Runtime version 31 (`mpa_agent:a2a-v8`) and Studio `127.0.0.1:8001`. -- `VC-12`: automated Web/Feishu/scheduling/group-bot regression paths pass; live Feishu delivery is blocked by unavailable bot credentials and installation target. -- `VC-14`: cancellation and single-terminal ownership pass in the executor regression suite; no additional live destructive cancel was required after the earlier disconnect proof. -- Sandbox timeline: tool call 20.23s, output 22.87s, text delta 26.51s, final 27.44s. Refresh returned the complete `v31-final-1` / `v31-final-2` result. -- Concurrent run: 25 and 26 frames, distinct invocation IDs, no marker leakage. -- Direct answer: true ADK deltas are forwarded; a cumulative working snapshot is suppressed and a single durable final remains. Delta granularity is controlled by the upstream model/ADK implementation. -- Secret scan: 13 evidence files, zero matches for supplied PostgreSQL/OpenViking credentials, Authorization headers, or API-key patterns. -- Frontend package command is environment-blocked because this checkout lacks `vite`, `esbuild`, and `typescript`; Python bridge tests pass. diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-12-mpa-agent-studio-a2a-streaming-verification-cases.zh.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-12-mpa-agent-studio-a2a-streaming-verification-cases.zh.md deleted file mode 100644 index 09612f664..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-12-mpa-agent-studio-a2a-streaming-verification-cases.zh.md +++ /dev/null @@ -1,48 +0,0 @@ -# mpa-agent Studio A2A 流式输出验证 Case - -## 环境 - -- mpa-agent 分支:`fix/a2a-studio-streaming` -- veadk-python 分支:`feat/mpa-agent-oneclick-provision` -- 测试 Runtime:`r-yeuujrrcowb21078p9jh` -- Studio:`http://127.0.0.1:8001` -- 证据目录:两个仓库均使用 `.artifacts/a2a-streaming/`,不得写入密钥。 - -## Case - -| Case | 覆盖 | 前置条件与操作 | 预期结果 | 证据 | -| --- | --- | --- | --- | --- | -| `VC-1` | FR-1 | curl agent-card | `capabilities.streaming=true` | `agent-card.json` | -| `VC-2` | FR-2/3/5 | 原始 `message/stream` 执行 `sleep 2; echo step-1; sleep 2; echo step-2` | task 完成前收到 working、tool 和两个分时输出 | `raw-a2a-stream.jsonl`、带时间戳日志 | -| `VC-3` | FR-2/5 | 经 Studio `/run_sse` 执行 VC-2 | 浏览器流在任务完成前收到过程事件和增量输出 | `studio-stream.txt`、截图 | -| `VC-4` | FR-4 | 统计一次执行的 terminal/final | terminal 与最终正文各一次 | `terminal-count.txt` | -| `VC-5` | FR-6 | mock 无 streaming capability | 只调用一次 `message/send` | pytest 输出 | -| `VC-6` | FR-6 | mock stream method-not-supported 且零事件 | 仅回退一次;总任务创建一次 | pytest 输出 | -| `VC-7` | FR-6/7 | 首事件后断开 Studio | 不发起第二个请求;原 task 后台完成 | 请求计数、task 查询 JSON | -| `VC-8` | FR-7 | VC-7 后刷新原 session | 可读取最终结果,不承诺补发全部 delta | Studio 截图、session/task JSON | -| `VC-9` | FR-8 | 慢速消费者和大量 delta | 可合并文本;tool result 与 terminal 不丢 | pytest 输出 | -| `VC-10` | FR-9 | 扫描所有流式 payload | 不含 API key、Authorization、完整 env 或签名 URL | secret-scan 输出 | -| `VC-11` | FR-10 | 连接旧非流式 Runtime | 阻塞模式仍能返回最终文本 | `legacy-runtime.txt` | -| `VC-12` | FR-10 | 普通 Web、飞书私聊/群聊、拉群、定时任务 | 行为与修改前一致 | 回归日志;外部条件不足则明确 blocked | -| `VC-13` | FR-3/5 | 两个 Studio session 并发执行不同 marker | sandbox/session 不同,输出不串流 | `concurrent-streams/` | -| `VC-14` | FR-4/7 | 流中取消任务 | 单一 canceled 终态,worker 收到取消 | cancel JSONL | -| `VC-15` | FR-2/5 | 普通模型直接回答 | 文本 delta 正常流式,无 sandbox 事件 | `direct-answer-stream.txt` | - -## 执行顺序与失败处理 - -1. 单元测试执行 VC-1、VC-4 至 VC-6、VC-9、VC-10。 -2. 本地双端集成执行 VC-2、VC-3、VC-11、VC-15。 -3. 测试 Runtime 执行 VC-2 至 VC-4、VC-7、VC-8、VC-13、VC-14。 -4. 最后执行 VC-12,避免在协议未稳定前影响外部渠道。 -5. 任一 P0/P1 Case 失败即回到 TDD 实现,不进入代码审查。 - -## 执行结果(2026-09-12) - -- `VC-1`–`VC-11`、`VC-13`、`VC-15`:在 Runtime v31(`mpa_agent:a2a-v8`)和 Studio `127.0.0.1:8001` 上通过。 -- `VC-12`:Web、飞书、定时任务、群机器人相关自动化回归路径通过;因未提供机器人凭证和安装目标,飞书在线投递仍受环境条件阻塞。 -- `VC-14`:executor 回归测试验证取消和单一终态归属;已有断线真机证据后未再执行额外破坏性取消。 -- 沙箱时序:20.23s 工具调用、22.87s 命令输出、26.51s 文本 delta、27.44s 唯一 final;刷新后可读完整 `v31-final-1` / `v31-final-2` 结果。 -- 并发验证:两个 session 分别产生 25/26 帧,invocation ID 不同,marker 无串流。 -- 普通直答:转发真实 ADK delta,抑制累计 working 快照,仅保留一个持久 final;delta 粒度取决于上游模型/ADK。 -- 敏感信息扫描:13 个证据文件中,用户提供的 PostgreSQL/OpenViking 凭证、Authorization header 和 API key 模式均为 0 命中。 -- 前端包命令受本地环境阻塞:checkout 缺少 `vite`、`esbuild`、`typescript`;Python 桥接测试通过。 diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-20-studio-mpa-creation.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-20-studio-mpa-creation.md deleted file mode 100644 index e6eaa52f1..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-20-studio-mpa-creation.md +++ /dev/null @@ -1,216 +0,0 @@ -# Studio MPA creation with prerequisite provisioning - -[中文版](2026-09-20-studio-mpa-creation.zh.md) - -- Change ID: `studio-mpa-creation` -- Created/revised: 2026-09-20 -- Status: implemented -- Baseline: VeADK `314d43c8`; inspected MPA deployment implementation `0368873`. -- Related contracts: [MPA provisioning](../../../specs/mpa-runtime-provisioning/README.md), [Studio MPA control plane](../../../specs/studio-mpa-control-plane/README.md). -- Contract extension: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md). - -## Background and evidence - -`frontend/src/ui/MyAgents.tsx` exposes a create card for general agents and sandbox agents, but not the MPA filter. `veadk/cli/cli_mpa.py` accepts the existing example YAML and uses the older placeholder-metadata provisioning flow. That flow does not provision a shared account network or a persistent per-agent database, and cannot infer a customer IM gateway from an AgentKit shared endpoint. - -The separately maintained `agentkit-mpa-agent` repository now owns `app.deployment.runtime.AgentRuntimeDeployer`, account network provisioning, per-agent databases, Skill Space registration, and account-shared APIG/IM Gateway services. Its `docs/agent-runtime-deployment.md` and `docs/account-shared-apig.md` explicitly require external Studio creation to enter this orchestration. The current task retrieval returned empty turn contents; conclusions here come from the inspected source and documentation, not an assumed transcript. - -## Goals and non-goals - -Create a usable MPA agent from the MPA directory, using the same prerequisite configuration as the CLI. Make missing configuration, progress, failures, and resumable creation visible. Preserve the existing legacy CLI path. - -Do not provision a PostgreSQL cloud instance, PostgreSQL login role, IAM role/policies, model service, cross-VPC connectivity, or database allowlists. Those prerequisites must be supplied and checked. Do not clone another agent's channels or Skill Space implicitly. Do not commit credentials or execute a real cloud deployment during implementation without separate authorization. - -## Scenarios and requirements - -- **FR-1:** With agent-management permission and the MPA filter selected, show a `Create MPA agent` card, including an empty directory on Volcengine. Other filters retain their existing creation behavior. -- **FR-2:** Opening creation loads a safe server-side profile summary for the selected region. Missing or incompatible deployment configuration blocks submission and identifies the unmet prerequisite. No password, token, raw environment, executable path, or private YAML is returned to the browser. -- **FR-3:** The operator supplies a stable agent ID and description. A generated ID is retained across retries. The server chooses the allowed profile, execution environment, credentials, and resource account; browser-supplied account IDs cannot select an account. Show the resources to create/reuse before submission. -- **FR-4:** Extend `mpa-create.config.example.yaml` with a versioned managed-deployment section: region, PostgreSQL administrator/registry secret references, Runtime template or reference Runtime, network selection/default CIDR and zone, APIG reuse/adoption policy, worker configuration, and timeouts. CLI and Studio share parsing and validation. Explicit legacy options retain their existing precedence and behavior. -- **FR-5:** Validate dependencies and configuration before cloud mutations. Prepare/reuse the account network, verify/create the shared APIG and IM Gateway, then ensure the business database and Skill Space and deploy the Runtime. Reuse the MPA registry and account/region lock; do not add an unrelated registry or pick an arbitrary gateway. Never hold that lock while waiting for application readiness. -- **FR-6:** Use the current managed metadata initialization rather than seeding placeholder endpoints. Enable public/private networks, KeyAuth, MPA classification, metadata/IM initialization, and a bound worker. Do not inherit a reference agent's identity, channel credentials, Skill Space, or worker identity inadvertently. -- **FR-7:** Return success only after platform readiness and application `/readiness`. Return safe Runtime/Skill Space/APIG identifiers and refresh the MPA list in the original region. Do not represent a local dependency check as proof of cloud permissions or connectivity. -- **FR-8:** Prevent duplicate submissions, scope tasks to the authenticated owner, and persist enough nonsecret identity to resume after restart or a lost response. Same identity/configuration resumes registered resources; conflicting pending configuration fails explicitly. Reuse native durable creation intent/client tokens. -- **FR-9:** Support cancellation, deadlines, process cleanup, and stale-response suppression. Cancellation stops orchestration but is not a rollback of shared/persistent resources. Identify retained resources for recovery. Only newly created temporary resources may be deleted; never automatically remove the registered business database, Skill Space, shared APIG/network, or an existing Runtime. -- **FR-10:** Use localized text, existing create-card and modal components, semantic colors, keyboard focus/IME handling, loading/empty/error/retry states, and a narrow-window layout. - -## Design and alternatives - -Approved decision (2026-09-20): the user selected “迁入 VeADK”. Migrate the network, database, Skill Space, Runtime and shared APIG orchestration into `veadk/integrations/mpa/managed/`, preserving the native registry schema, ownership markers and locking semantics. VeADK owns the migrated implementation and its tests. No external checkout, provider Python environment or subprocess command supplied by the user is required. Run each Studio job in a child of the current VeADK Python interpreter for reliable cancellation of blocking SDK calls. Retain the legacy CLI path unchanged. - -Direct design review: the migration is feasible using existing AgentKit/Volcengine, SQLAlchemy and asyncpg dependencies. Only deployment-related APIG operations migrate; bot routing and application startup remain in the MPA image. The image must support shared-registry metadata bootstrap. The same credential resolver is used for Runtime, network and APIG to prevent cross-account preparation; rotating STS and existing deployment AK/SK are supported. No IAM policy mutation is introduced. Tests and ownership checks address lost cloud responses and multi-user retries before UI integration. - -The adapter must compose account-network preparation and `SharedAPIGService.ensure` before Runtime creation, outside nested account locks. APIG adoption requires an explicit ID and matching account/region/VPC. Deployment credentials and Runtime credentials are separate: the migrated deployment APIG client uses the same refreshed and account-verified credential source as Runtime/network calls. The MPA image still uses its mounted STS source after startup. Missing prerequisites are reported before provisioning. The runtime role still needs its own mounted IAM credentials and cloud permissions. - -Reference Runtime mode reuses deployment settings only; explicit template mode supports a fresh account. PostgreSQL instance and shared registry already exist. New account networks cannot prove reachability to a private PostgreSQL instance; an explicit compatible VPC is required where appropriate. The adapter must preserve the existing managed names and ownership markers and provision a dedicated worker or validate an explicitly configured reusable worker. - -### Approved integration decisions - -The compatible CLI entry point is `veadk mpa provision --config /secure/mpa-create.config.yaml --agent-id customer-service`. It reads the same YAML file, with a new `managed` section; `veadk mpa create` retains its current flat-option semantics. `provision --dry-run` validates the profile and prints only a safe plan, without importing cloud credentials or querying/writing cloud/database resources. Studio locates the same profile through the server-only `VEADK_MPA_CREATE_CONFIG` setting. - -`managed` keys: `version: 1`; `database-admin-url-env` and `shared-database-url-env` as environment variable names; `credential-file` as an optional rotating deployment STS file; mutually exclusive `from-runtime`/`template-file`; `network` containing `vpc-id`, `subnet-ids`, `vpc-cidr`, `subnet-prefix` and `zone`; `apig.adopt-id` for explicit adoption; `worker` containing an explicit reusable ID or image/reference configuration; and `timeout-seconds`. Environment names are references, not literal database URLs. Relative template paths resolve against the YAML directory. An absent region profile is a configuration error, not a fallback to another region. - -The final flat model/image/PG fields remain usable to construct a fresh Runtime template when neither source selector is set. Managed mode derives the business database name from native agent identity; it does not reuse the legacy `pg-database` as a shared business database. A legacy `account-id` can only be an expected-account assertion checked against the authenticated cloud account, never the source of authority. - -### Interfaces - -- CLI: `veadk mpa provision` reads the managed section of the existing YAML; legacy `veadk mpa create` remains supported. -- `GET /web/mpa-creation/config?region=...`: safe profile summary and unmet requirements. -- `POST /web/mpa-creation/tasks`: validated identity, description, region and request ID; returns task ID. -- `GET /web/mpa-creation/tasks/{id}`: owner-scoped stage, state, safe result and error code. -- `POST /web/mpa-creation/tasks/{id}/cancel`: idempotent cancellation after UI confirmation. -- States: `running -> succeeded` or `running -> cancelling -> cancelled`; failures terminate as `failed`. Stages report `queued`, `checking`, `network`, `gateway`, `worker`, `database`, `skills`, `deploying`, `verifying`. Unknown remote outcomes are resumable, never success or a blind fresh create. - -Use structured process messages with a size bound and an allowlist of fields. Never forward raw deployment stdout/stderr, database URLs, environment dumps, SDK exceptions or raw Pydantic validation input to the client. Kill/reap the provider on cancellation/timeout/server shutdown. Close polling requests on dialog unmount; the server task remains queryable in SQLite; no automatic task-history expiration is implemented. - -## Implementation tasks and affected files - -| Task | Scope | Requirements | -| --- | --- | --- | -| T-1 | Review bilingual design/contract and confirm migration ownership | FR-1–FR-10 | -| T-2 | Shared managed YAML schema and safe prerequisite planner under `veadk/integrations/mpa/`; example YAML and CLI integration | FR-2–FR-6 | -| T-3 | VeADK deployment adapter, protocol, resource preparation and recovery tests | FR-5–FR-9 | -| T-4 | `frontend/server/` task routes and registration in `veadk/cli/cli_frontend.py`; authorization, bounded lifecycle | FR-2, FR-3, FR-7–FR-9 | -| T-5 | `frontend/src/ui/MyAgents.tsx`, MPA dialog/client, paired locale updates | FR-1–FR-3, FR-7–FR-10 | -| T-6 | Regression/contract tests, browser checks, production web assets, bilingual evidence reconciliation | FR-1–FR-10 | - -## Verification and acceptance - -Write failing regression/contract tests before production changes. All cloud/database effects use fakes or isolated test storage by default. - -| Acceptance | Evidence required | -| --- | --- | -| AC-1: creation reachable only in the intended MPA context and permissions | Component tests plus real browser normal/empty/narrow/keyboard/IME flows | -| AC-2: missing profile/PG/registry/network/worker/STS inputs block with safe errors | YAML/CLI and server contract tests; dry-run creates nothing | -| AC-3: ordered preparation yields one agent with independent database/skills and shared account resources | Fake-cloud integration tests asserting calls, payloads and ownership; native registry/payload compatibility checks | -| AC-4: double-submit, restart, response loss, conflict, timeout/cancel and region changes are safe | Lifecycle and recovery tests, late-response and process-reaping checks | -| AC-5: credentials do not reach output, task persistence or generated assets | Negative redaction tests and secret scan of the final diff/assets | -| AC-6: legacy provisioning behavior remains compatible | `uv run --extra dev pytest tests/cli/test_cli_mpa.py tests/integrations/` scoped to affected MPA tests | -| AC-7: release checks complete and honestly recorded | Targeted Python tests; changed-file Ruff/Pyright; `npm --prefix frontend test`; `npm --prefix frontend run build`; `npm --prefix frontend run test:webui-assets`; real browser evidence | - -Broaden Python regression for shared API/lifecycle changes using `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`. Before any authorized commit, synchronize with the intended remote base, run `uv run --extra dev pre-commit run --all-files` and unit tests. A live deployment smoke is a separate authorized check and is not implied by fake-cloud tests. - -## Risks, review and delivery record - -- Migration approved by the user on 2026-09-20. Deployment implementation and compatibility tests now belong to VeADK; shared data contracts remain interoperable with the MPA image. -- Native cancellation preserves durable resources. This feature must reconcile the Studio temporary-Runtime cleanup rule with registered persistent agents; no automatic destructive rollback is proposed. -- Native template inheritance can carry a worker belonging to another agent; explicitly validate/provision the worker. -- SDK response schemas and resource readiness are checked at each step. Local configuration checks cannot prove all live IAM permissions before mutation. -- PostgreSQL session advisory locks require direct/session pooling, not transaction pooling. -- Direct review on 2026-09-20 identified two additional implementation blockers and assigned their remedies to T-3/T-4: - - Native `deploy_runtime` prepares networks before creating Runtime, but APIG creation normally happens during Runtime startup. Calling that CLI alone does not satisfy FR-5. The adapter must prepare the account network, release the lock, call `SharedAPIGService.ensure`, and then call `AgentRuntimeDeployer` with the resolved network. Verify matching accounts for both cloud clients before writes; avoid nested locks. - - The existing `ensure_codex_worker_tool` helper reuses by name and generates a fresh ClientToken for creation. It does not establish ownership or recover a lost CreateTool response safely. Managed creation must persist worker intent/token and verified ownership in the native agent deployment record, or validate an explicitly configured external worker. It cannot claim durable retry safety by calling the old helper unchanged. -- Authorization review: use `_require_agent_management` for all four routes. For authenticated multi-user Studio, bind generated agent identity to the owner and keep ownership in durable server state; a second manager guessing an agent ID must not overwrite another owner's deployment. The local unauthenticated development mode remains explicitly scoped to the existing `local` principal behavior. -- `frontend-design`, `ui-ux-pro-max`, and `review-spec` were not found in the repository or available local skill roots. Apply the checked-in frontend standard and perform the design review directly. -- 2026-09-20 implementation review: resolved account-lock ordering and durable worker intent; added universal-SDK raw-response parsing regression, pre-mutation database permission checks, response-loss input locking, task-store connection cleanup and child reaping. Shared `ModalLayout` gains an optional footer slot (default behavior unchanged) so the dialog does not hide component internals with CSS. User migration approval covers T-1; T-2–T-5 are implemented. Verification for T-6 follows below. -- No SDK agent execution, harness/sidecar protocol, or documentation-site contract changes. Existing control-plane and legacy provisioning specs link to the new owner rather than duplicate the contract. [Operator guide](../../../veadk/integrations/mpa/managed/README.md) describes prerequisites and recovery. - - -### Verification record (2026-09-20) - -Scope: working-tree change over `314d43c8`; no commit, push or cloud deployment. The approved migration is implemented; release acceptance is qualified by the existing type-check baseline and the explicit live-cloud exclusion below. - -| Check | Result | Evidence / limits | -| --- | --- | --- | -| Targeted Python | pass | `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q`: 162 passed. Includes final duplicate-cancel/shutdown regression, which failed before shielding cleanup. | -| Frontend suite | pass | `npm --prefix frontend test`: 1208 Node tests and 22 Vitest tests passed. Missing config, double submit, late configuration, lost POST response, and default/custom modal footer covered. | -| Production assets | pass | `npm --prefix frontend run build`; `npm --prefix frontend run test:webui-assets`: 104 files and 248 references verified. Build emits existing large-chunk warnings. | -| Localization | pass | `npm --prefix frontend run check:i18n`: 2 locales, 21 namespaces consistent. | -| Ruff | pass | `uv run --extra dev --with 'ruff==0.11.12' ruff check veadk/integrations/mpa/managed frontend/server/mpa_creation.py tests/integrations/mpa_managed veadk/cli/cli_mpa.py veadk/cli/cli_frontend.py`; formatting checked for new Python and CLI additions. | -| Pyright, new modules | pass | `uv run --extra dev --with pyright pyright veadk/integrations/mpa/managed frontend/server/mpa_creation.py`: 17 files, zero errors. | -| Pyright, complete changed Python scope | fail | Adding `veadk/cli/cli_mpa.py` and `veadk/cli/cli_frontend.py` reports 37 errors. Running the same tool on their unmodified HEAD sources gives the identical 37 diagnostic signatures; no new errors. Existing unrelated errors were not suppressed or changed. | -| Real browser, isolated API | pass | Actual MyAgents/dialog under local Vite: empty MPA create card; missing config and retry; Chinese multiline text, Tab/Enter submit, Escape close/focus return; APIG progress; success IDs; failure/retry; cancellation confirmation/terminal/retry; close/reopen recovery; light/dark at 390×844 and desktop. Mock APIs only. Temporary fixtures/server/tab removed; viewport reset. Native OS IME candidate-window interaction was not automated. | -| Repository secret rules | pass | Gitleaks scan of changed/new-file snapshot with `.gitleaks.toml`; no findings. A public RFC 1918 range literal was removed from production source while preserving range validation, with boundary regression coverage. No scanner rules or hooks disabled. | -| Additional generated-assets scan | fail (reviewed false positive) | Default Gitleaks rules over all `veadk/webui` scanned 19.17 MB and flagged one minified editor expression involving `anchor.key` / `focus.key`, not credential data. Repository rules intentionally exclude vendor bundles; the independent finding was manually inspected, with no sensitive value found. | -| Documentation and whitespace | pass | Bilingual files, FR/CON/AC/task identifiers, relative links and localization keys checked; `git diff --check` clean. | -| Live cloud / Codex / piagent smoke | not_run | Live cloud allocation not authorized; Codex/piagent runtimes are unaffected and excluded from regression. Fake-cloud and browser results do not prove IAM/image/network/real PostgreSQL interoperability. | -| Pre-commit all files / remote sync | not_run | No commit authorized or performed; these gates must run after remote synchronization before an authorized commit. | - -Initial broad regression `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` used two workers: 4687 passed, 6 failed, 2 collection errors, 11 skipped, 2 xfailed. Failures were missing optional `anthropic`/LlamaIndex packages; after loading temporary optional packages, the remaining Harness configuration test required synthetic model keys rather than live IAM discovery. The isolated rerun used `MODEL_AGENT_API_KEY=test-only MODEL_EMBEDDING_API_KEY=test-only` and `--with anthropic --with 'llama-index-embeddings-openai-like>=0.2.2' --with 'llama-index-llms-openai-like>=0.5.1'`; no dependency file or machine-global configuration changed. Final broad result is recorded below. Subsequent scoped fixes are covered by the final 162-test targeted run. - -Final broad regression: **pass**, 4711 passed, 7 skipped, 2 xfailed, 91 warnings in 293.20 seconds using two workers and the temporary optional-dependency environment above. The skipped/xfail cases are not claimed as verified. T-1–T-6 are complete for the approved implementation scope; real cloud validation and existing baseline type errors remain explicit limitations. - - -### Follow-up: actionable local configuration errors (2026-09-20) - -The user reported the generic configuration-load error. Reproduction: the Studio process runs from the VeADK root and the default `mpa-create.config.yaml` is absent; loading this path produces the reported message. This is within approved FR-2/CON-1/CON-8: configuration errors must identify the missing prerequisite without disclosing secrets. Keep the configured/default path contract; do not create cloud resources or auto-select another deployment account. Distinguish missing/unreadable configuration, malformed YAML, and missing/malformed Runtime templates with safe messages. Add negative tests before changing the loader, preserve secret redaction, and run affected config/routes/CLI tests plus Ruff/Pyright. Existing resource reuse is a separate operator choice pending the user's answer. Direct design review: no HTTP schema or deployment behavior change; existing localization displays the safe server detail, and private paths/raw parser exceptions remain hidden. - -Follow-up verification: **pass**, `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q` (167 passed); changed-loader/tests Ruff and loader Pyright pass; `git diff --check` pass. The five new negative cases failed before the loader fix. No frontend behavior or asset changes in this follow-up, so frontend rebuild/browser gates are not applicable. The user selected reuse of existing Beijing resources. Actual local credential transfer remains blocked pending explicit confirmation after automatic approval review rejected process-credential reading and cross-repository database-secret copying; no credentials or cloud resources were changed. - -Local setup resolution (2026-09-20): after the credential scope was explained, the user instructed “帮我加上试试”, authorizing reuse in the local private configuration. The existing Beijing account, reference Runtime and worker were verified using read-only cloud calls. Created Git-ignored `.env` and `mpa-create.config.yaml` with mode 0600; deployment AK/SK were used only in memory and were not written into these files. After checking that no creation tasks were active, restarted local Studio with its existing launch environment. The live configuration endpoint returned HTTP 200 and `configured: true`, region `cn-beijing`, source `reference`, with `requiresLiveChecks: true`. No cloud resources were created and no deployment was executed. This resolves the previous local-setup approval blocker; it is configuration verification, not a live deployment smoke. - -### Follow-up: worker discovery pagination (2026-09-20) - -Evidence from read-only inspection of the user-started creation: the task remains at `worker`, and its native record contains only `studio_owner`, before any worker creation intent. `ListTools(PageNumber=1, PageSize=100)` and page 2 return identical tool IDs. The response exposes `NextToken`; following it returns the remaining 27 tools. A request takes under one second, while the old discovery can repeat 1,000 calls. This is a correctness defect within approved FR-5–FR-9/T-3, not evidence of slow worker startup. - -Plan/design review: use `MaxResults=100` and `NextToken`, terminate only when no continuation token remains, reject repeated tokens and retain the existing 1,000-page safety bound. Deduplicate matching ToolIds across overlapping pages, retaining distinct same-name resources for ownership collision checks. Propagate provider errors rather than interpreting incomplete discovery as absence. No credential handling, ownership, create request, cancellation, task identity, API or UI changes. Maintain CON-2/CON-3; record the pagination invariant in both component specs. The user's approved migration and request to investigate its running creation cover this implementation correction; restarting or retrying the live task remains a separate operational action. - -Tasks: add failing regression cases for short intermediate pages, full final pages, overlapping matches, distinct collisions, repeated cursors and page-limit failure; fix `WorkerCloud.find`; run the managed/CLI suites and changed-file Ruff/Pyright. Acceptance: discovery visits every cursor page once and cannot silently create after partial/failed enumeration. Review found no schema migration, new dependency, breaking consumer contract, or frontend asset impact. Verification pending; live inspection was read-only and did not cancel or restart creation. - -Pagination verification (2026-09-20, working tree over `314d43c8`, worker implementation/tests and paired design/spec only): **pass**. All six new regression cases failed before the fix; `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q` now reports 173 passed. Changed-file Ruff 0.11.12 check/format, worker Pyright and `git diff --check` pass. A read-only cloud check using the corrected `WorkerCloud.find` enumerated both pages in 1.05 seconds with two ListTools calls and no match. The original task is now failed at `worker`; its registry still contains only `studio_owner`. Its persisted error is generic, so the exact terminal exception was not retained; repeated first-page enumeration was independently proven. No task retry or cloud write performed. Frontend/build/browser checks are **not_applicable** to this Python-only follow-up; broader regression is **not_run** again because the scoped managed/CLI suite covers this isolated change and the preceding full-suite evidence remains recorded above. Retrying the same task starts a fresh runner process that imports the corrected source; no Studio restart is required. A complete live creation remains **not_run** by this diagnostic follow-up. - -Observed live creation (2026-09-20): following the user's new creation in Studio, read-only checks observed worker Ready, an isolated database and Skill Space registered, Runtime Creating → Releasing → Ready, and the task reaching `succeeded`. The native deployment record is `state=ready`, `pending=false`; an independent authenticated `/readiness` check returned HTTP 200. The older task remains failed at `worker`; these are distinct agent/task identities. This supplies **pass** evidence for one real Beijing creation with the selected local reference profile, not all configurations, IAM boundaries, or cloud failure/recovery scenarios. This follow-up did not start another creation, cancel tasks, change cloud resources, or commit/push code; it observed the user-started deployment. - -### Follow-up: readable Runtime names (2026-09-20) - -User approval: after proposing to use `MPA_AGENT_ID` as the new Runtime name, the user requested implementation (“帮我改下”). FR-11/T-7/AC-8: newly created managed Runtimes use the stable agent ID directly as `Name`; resource IDs, database/worker/Skill Space naming and ownership remain unchanged. Background: generated `mpa-agent-` names obscure the ID entered in Studio. This affects both Studio and managed CLI through `AgentRuntimeDeployer`. - -Design and direct review: retain the name returned by an existing registered Runtime. For an unfinished legacy create whose response was lost, accept the old hashed name only when the entire legacy request hash exactly matches the persisted intent; reuse the original token and complete payload. Other changed inputs still fail. Fresh deployments use the agent ID, including a reference template with another name. Current SDK `UpdateRuntimeRequest` and the [official UpdateRuntime contract](https://www.volcengine.com/docs/86681/1923461) do not expose `Name`; do not claim existing instances can be renamed or silently replace them. Existing cloud rename is therefore unimplemented, and no cloud write is part of this code change. No frontend API, task lifecycle, IAM, credential, database schema or telemetry identity changes for existing resources. Paired CON-3 and operator docs must describe this compatibility rule. - -Tasks/tests: first add failing tests for direct new names, preservation of existing legacy names, recovery of legacy lost-create responses with exactly the old payload/token, and rejection of changed legacy inputs. Implement only the Runtime name selection and request-hash compatibility path. Acceptance: all naming/recovery cases plus managed/CLI regression pass; changed-file Ruff/Pyright and whitespace checks pass. No frontend asset regeneration applies. Risks: cloud name conflicts still surface as provider failures, without adopting another resource; older binaries cannot resume new pending request hashes. Status: approved, verification pending. The unavailable `review-spec` skill is replaced by this direct boundary/compatibility/bilingual review. - -FR-11 verification (2026-09-20, working tree over `314d43c8`): **pass**, `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q` reports 176 passed. Before implementation the direct-name regression failed (1 failed, 19 passed); legacy compatibility cases establish that prior requests remain resumable. Changed Runtime/tests Ruff 0.11.12 check/format, Runtime Pyright and `git diff --check` pass. Direct implementation review confirms the fallback requires the full legacy request hash and absence of a registered Runtime ID; it cannot ignore arbitrary input changes or allocate a replacement for an existing Runtime. Paired PRD, CON-3 and operator docs synchronized. FR-11/T-7/AC-8 are implemented for new creation and compatibility. Browser/build gates are **not_applicable** (no UI or assets changed); another full Python regression is **not_run** (isolated naming change covered by the managed/CLI suite). Live new-name creation is **not_run** to avoid allocating another agent; existing cloud rename is **blocked** because the supported update contract exposes no Name parameter. No existing resources changed, and no commit/push performed. - -### Follow-up: explicit MPA image (2026-09-20) - -FR-12/T-8/AC-9, approved by the user's request to configure the MPA image like the worker image. Background: `managed.from-runtime` inherits ArtifactType/ArtifactUrl, role, CPU/memory, scaling/concurrency, APM/project, VPC and sanitized environment settings, not just an image. Operators need to pin the MPA image independently while retaining infrastructure/model/database configuration. - -Design: add optional `managed.runtime.image`, parallel to `managed.worker.image`. A supplied nonempty image overrides ArtifactUrl and sets ArtifactType to `image` for reference, JSON-template and flat sources. Omission/null preserves existing source behavior; reject blank/whitespace/placeholder strings and unknown runtime keys during local validation. In flat mode it can replace the legacy top-level `image`; all other prerequisites remain required. No image value is added to the browser summary. Template copying and inherited environment sanitization stay unchanged. Pending deployment request hashes still reject an image change; this does not update existing agents automatically. Update the local ignored YAML to pin the already verified current MPA image, without changing its effective image or other private settings. Reload local Studio only after confirming no active creation tasks; do not deploy or allocate resources. - -Direct design review: this is an additive CON-1 configuration contract; source selection remains mutually exclusive and explicit images cannot remove required credentials/network/DB/model settings. Tests cover override precedence for all sources, omission compatibility, invalid settings, and flat image prerequisites, using fake cloud calls only. Run managed/CLI tests, Ruff/Pyright and live local configuration GET. Update paired component/operator docs and placeholder example YAML. Existing frontend behavior/assets are unaffected. Risks: a chosen image must remain compatible with the inherited configuration; unfinished requests must retain their original effective image. Review-spec is unavailable; direct bilingual/compatibility/security review completed, with no unresolved implementation blockers. Status: approved, verification pending. - -FR-12 verification (2026-09-20, working tree over `314d43c8`, configuration/service/tests plus paired docs/example): **pass**. Before implementation, 5 targeted tests failed and 20 passed. After implementation, `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q` reports 188 passed. Changed-file Ruff 0.11.12 and config/service Pyright pass; formatting applied; example Managed schema, paired documentation review and `git diff --check` pass. The private Git-ignored YAML now pins the previously verified MPA image under `managed.runtime.image` and retains mode 0600; other configuration is preserved. After confirming zero active creation tasks, restarted local Studio with its existing launch environment kept in memory. Live GET `/web/mpa-creation/config?region=cn-beijing` returned HTTP 200, configured=true, source=reference. No secrets printed or newly persisted. FR-12/T-8/AC-9 implemented. Live cloud deployment is **not_run** (no new resource allocation needed to validate this configuration change); frontend/build/browser gates are **not_applicable** (no frontend code/assets changed); full Python suite and pre-commit are **not_run** again (scoped managed/CLI regression completed, no commit requested). No cloud resource changes, commit or push. - -### Follow-up: explicit Runtime infrastructure and environment (2026-09-20) - -FR-13/T-9/AC-10. The user requested concrete inherited settings and asked to make them configurable. Read-only inspection confirms the selected source uses 2000 CPU milli, 4096 MiB, min/max instances 1/1, concurrency 100, APM enabled, project default, a Runtime IAM role, dual public/private networking, and model/PostgreSQL/application environment settings. Goal: make those choices reviewable in YAML and remove the local profile's dependency on `from-runtime`. Existing worker reference behavior is separate and remains unchanged; no cloud deployment or resource replacement is included. - -Design: extend optional `managed.runtime` with role-name, cpu-milli, memory-mb, min-instance, max-instance, max-concurrency, apmplus-enable, project-name and env. Omitted fields preserve the selected template/defaults; explicit values override it. Positive compute/concurrency, nonnegative min instances, positive max instances and effective min<=max are validated before resource mutation. Existing `managed.network.vpc-id/subnet-ids` specifies dual networking; public/private connectivity remains required. Existing flat model/PG fields expose the model endpoint/name/provider and database host/port/login/SSL settings. `runtime.env` adds explicit application environment overrides and resolves whole `${ENV_NAME}` references server-side. Values must be strings and keys valid environment identifiers. Reject fields owned by provisioning (agent/runtime/tool/skill identities, derived database name, runtime endpoints/auth/encryption, shared registry/admin URL, request/telemetry identity); model and database connection parameters remain configurable. Do not return environment values in configuration summaries or validation errors. - -Migration: pin the currently selected effective infrastructure and model/PG/application settings in the ignored local YAML, remove `from-runtime`, place secret values in the existing ignored mode-0600 .env and use references in YAML. Keep both files mode 0600; keep a private rollback copy. Read cloud values with the previously authorized in-memory credentials, print only nonsecret summaries/equivalence results. Compare effective template settings against the old reference before saving; do not change the selected image, network, roles, credentials or resource identities. Restart local Studio only with no active creation tasks and verify the configuration endpoint. The deployment AK/SK remain in memory only. - -Direct review: additive CON-1 overrides; existing profiles preserve defaults. CON-3/CON-8 generated identities and redaction stay enforced. Source model changes during an unfinished deployment still trigger the existing request-hash conflict, and retries must keep original effective settings. Tests first cover all override fields, precedence/omission, false booleans, zero min instances, merged scaling conflicts, secret reference resolution/redaction and reserved-key rejection, then managed/CLI regression and Ruff/Pyright. Paired contract/operator/example docs required. Frontend APIs/assets are unchanged; browser rebuild is not applicable. Risk: explicitly pinned settings no longer follow later source Runtime changes; operators own updates. User request authorizes this configuration work; no commit, deployment or new agent creation. Review-spec unavailable; direct review completed. Status: approved, verification pending. - -FR-13 verification (2026-09-20, working tree over `314d43c8`, config/service/tests and paired docs/example): **pass**. Before implementation 5 targeted regressions failed, 40 passed; afterwards `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q` reports 208 passed. Changed-file Ruff 0.11.12, config/service Pyright, example schema and `git diff --check` pass. Runtime override coverage includes false/zero values, effective min/max constraints, invalid/provisioner-owned environment keys, secret resolution/redaction and original source compatibility. Local profile migration verified equality of the image, all infrastructure fields, VPC and every effective environment value except provisioner-owned fields regenerated during creation. Removed from-runtime, preserved worker reference, moved two secret values to env references, and retained private rollback files; YAML/.env remain Git-ignored and mode 0600. Checked zero active creation tasks before restarting Studio with the existing process environment in memory; live configuration GET returned HTTP 200 and configured=true. No cloud writes, new agent creation, commit or push. Browser/build gates are **not_applicable** (no frontend/assets change). Full Python regression/pre-commit are **not_run** again (scoped managed/CLI suite covers the additive configuration change; no commit requested). Full live creation under the explicit profile is **not_run**; equivalence and live local validation do not prove every cloud combination. FR-13/T-9/AC-10 implemented for configuration and local migration. - -### Follow-up: editable images in the creation dialog (2026-09-20) - -FR-14/T-10/AC-11, explicitly approved: add MPA image and Worker image text inputs, prefilled from the current configuration, accepting manual overrides; blank means use the server configuration. Use existing dialog inputs and localized helper/error text; no registry picker or image listing integration. Existing administrator permissions remain required. - -Contract/design review: authorized configuration GET exposes only locally configured image references (`runtimeImage`, `workerImage`), never environment/credentials. If a legacy reference-only configuration cannot resolve an image locally, leave it blank and retain its original source behavior. Optional POST fields use the same names; trim whitespace, cap at 1024 characters and reject URL/credential/query/fragment or malformed digest forms. Explicit Runtime image overrides the selected source; explicit Worker image selects a dedicated worker instead of an explicitly configured existing worker. Blank preserves defaults. A new SQLite `images` JSON column captures locally resolved effective images on first submission, separate from original request identity, so blank-field retries cannot pick up changed defaults. Existing rows get `{}` and retain prior behavior; no cloud lookup or implicit resource adoption during migration. Runner receives and applies the persisted image snapshot. Same request with changed explicit images is a conflict. Secret-free task responses include effective images; submitted fields are locked and session storage retains the original request, including after a lost response. Late config responses never overwrite edits, cleared fields or recovered submissions. Closing/cancelling keeps existing task lifecycle behavior. - -Tasks: add failing configuration/route/task/runner and frontend regressions first; implement types, validation, snapshot migration/runner handoff and two dialog fields; synchronize locales, paired contracts/operator docs and frontend README; run targeted and full frontend tests, managed/CLI tests, changed Python Ruff/Pyright, production build/asset/i18n checks, and isolated real-browser normal/blank/invalid/loading/retry/recovery/cancel/keyboard/narrow cases. No cloud creation is part of verification. Live image accessibility and IAM compatibility remain provider checks during user-requested deployment. Existing profiles/old clients/old rows are compatible. Frontend-design, ui-ux-pro-max and review-spec remain unavailable; apply frontend/SPEC.md and perform direct design review. Review covers duplicate/lost requests, schema migration, immutable retries, explicit existing-worker override, errors and secret boundaries. Status: approved, verification pending. - -FR-14 verification (2026-09-20, working tree over `314d43c8`, dialog/client/locales, creation route/config/task/runner, tests/docs and rebuilt web assets): **pass**. Before implementation 4 new backend cases and 3 frontend cases failed. Final `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q`: 221 passed, including legacy SQLite migration, snapshot pinning across retries, worker reuse override and actual runner profile application. `npm --prefix frontend test`: 1208 Node tests and 25 Vitest tests passed. `npm --prefix frontend run build`, `test:webui-assets` (104 files/248 references), and `check:i18n` (2 locales/21 namespaces) pass. Changed Python Ruff 0.11.12 check/format and Pyright pass. Repository-rules Gitleaks scan of this source/built-asset snapshot reports no leaks (vendor exclusions retained). `git diff --check` passes; paired documentation and API identifiers reviewed. - -Real browser verification uses the actual dialog with isolated fake APIs: default values, malformed-image error/disabled submission, custom MPA image plus blank worker fallback, multiline Chinese input, Tab/Enter, submitted locks, failure/retry, closing/reopening task recovery, cancellation, loading-disabled fields and 390×844 light/dark layout passed. Missing configuration and lost-POST-response recovery are covered by automated tests; the attempted browser missing-config scenario was not conclusive and is not claimed. Native OS IME candidate interaction is **not_run**. Temporary fixture/server/tabs removed and viewport reset. Production Studio's login page was not advanced; no new account/terms acceptance or live cloud creation occurred. After confirming no active creation tasks, restarted Studio with the existing launch environment and verified live config GET HTTP 200, configured=true and both nonempty image defaults with no environment/credential fields. No existing cloud agent changed. Full Python regression and pre-commit are **not_run** again (scoped lifecycle/route/CLI coverage passed; no commit requested). No commit/push. FR-14/T-10/AC-11 implemented; image pullability and provider permissions are not established by fake deployments. - - -### Integration verification: rebase onto main (2026-09-21) - -Scope and authorization: the user requested resolution of PR #5's merge conflicts. Rebased `feat/from-main` onto `origin/main` at `f55e8918`; reviewed the resulting source at `703859c7` plus the integration diff. Preserved both main's scheduled-task, A2A, sandbox-download, JWT and tracing documentation and this branch's MPA information-rail/managed-creation documentation. Rebuilt all packaged WebUI assets from the combined source. No new behavior or component contract is introduced by conflict resolution; the existing bilingual contracts remain authoritative. The two pre-existing local type-verification document edits were saved separately for restoration after committing this integration. - -Clean installation initially failed because the merged lockfile omitted optional dependency records. Regenerated `frontend/package-lock.json` with npm: no existing dependency version changed, and `npm --prefix frontend ci --no-audit --no-fund` then passed. Local Node 26 experimental web storage caused four sandbox-download test cleanup failures; running the same suite with `NODE_OPTIONS=--no-experimental-webstorage` passed without changing application or test behavior. Pre-commit's first run only reformatted two expressions in the merged Runtime-proxy test; Ruff check and secret scanning passed. Final pre-commit and formatted-file regression results are recorded below. - -Verification on 2026-09-21: - -- **pass** — `uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py tests/cli/test_cli_mpa.py tests/frontend/server/test_mpa_cron.py tests/cli/test_frontend_runtime_proxy.py tests/cli/test_frontend_apmplus_trace.py tests/cli/test_studio_trace_pagination.py -q`: 398 passed. -- **pass** — `MODEL_AGENT_API_KEY=test-only MODEL_EMBEDDING_API_KEY=test-only uv run --extra dev --with anthropic --with 'llama-index-embeddings-openai-like>=0.2.2' --with 'llama-index-llms-openai-like>=0.5.1' pytest -n 2 -m 'not codex_smoke and not piagent_smoke'`: 4,850 passed, 7 skipped, 2 expected failures. Two workers limit local resource use. Skipped/expected-failure cases and real runtime smoke are not proven by this run. -- **pass** — `npm --prefix frontend test`: 1,215 Node tests and 25 Vitest tests; `npm --prefix frontend run test:mpa-cron-coverage`: 71 tests and the configured coverage gate; `NODE_OPTIONS=--no-experimental-webstorage npm --prefix frontend run test:sandbox-download-coverage`: 26 tests and its configured coverage gate. -- **pass** — `npm --prefix frontend run build`, `npm --prefix frontend run test:webui-assets` (104 files, 248 references), and `npm --prefix frontend run check:i18n` (2 locales, 21 namespaces). The existing large-chunk build warning remains. -- **pass** — isolated real-browser smoke using the actual components and fake APIs: scheduled-task no-Runtime state; creation image defaults, invalid-image error/disabled submission, custom MPA image/blank Worker image, multiline Chinese description, submitted-field locks, simulated failure and retry to success. Temporary fixture, server and tab removed. No cloud resources or production user state changed. -- **not_run** — another full browser matrix, native IME and live cloud/runtime smoke: this integration preserves existing behavior; prior feature-specific browser evidence remains above, and automated regressions plus the focused combined-source browser smoke cover the conflict-resolution scope. Real provider image access and IAM combinations remain outside this verification. - -Direct review found no unresolved conflict markers or omitted document sections. The final integration commit contains only regenerated assets, lockfile normalization, merged-test formatting and this paired verification record. Main is not merged or published by this work; the PR branch is updated for normal review and CI. - -Final checks: **pass**, `uv run --extra dev pre-commit run --all-files` (Ruff check, Ruff format, Gitleaks); **pass**, `uv run --extra dev pytest tests/cli/test_frontend_runtime_proxy.py -q` after formatting (80 passed); **pass**, source/document `git diff --cached --check -- . ':!veadk/webui'`. Unfiltered diff whitespace check is **fail** only for seven changed generated-bundle lines containing vendor string whitespace. The generated integration bundle has 36 trailing-whitespace lines both before and after rebuilding; retained generated string contents rather than manually rewriting them. Final remote fetch confirmed the base and original PR head unchanged. No unmerged index entries remain. This integration also includes a README section-spacing correction. diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-20-studio-mpa-creation.zh.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-20-studio-mpa-creation.zh.md deleted file mode 100644 index e49aebe30..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-20-studio-mpa-creation.zh.md +++ /dev/null @@ -1,216 +0,0 @@ -# Studio MPA 创建与前置资源准备 - -[English](2026-09-20-studio-mpa-creation.md) - -- 变更 ID:`studio-mpa-creation` -- 创建/修订日期:2026-09-20 -- 状态:implemented -- 基线:VeADK `314d43c8`;已检查的 MPA 部署实现 `0368873`。 -- 相关契约:[MPA 部署](../../../specs/mpa-runtime-provisioning/README.zh.md)、[Studio MPA 控制面](../../../specs/studio-mpa-control-plane/README.zh.md)。 -- 契约扩展:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md)。 - -## 背景与证据 - -`frontend/src/ui/MyAgents.tsx` 为通用智能体和沙箱智能体提供创建卡片,但 MPA 筛选下没有入口。`veadk/cli/cli_mpa.py` 接收现有示例 YAML,采用旧的占位元数据部署流程,不准备账号共享网络或每个智能体的持久业务库,也无法通过 AgentKit 共享端点推断客户 IM 网关。 - -独立维护的 `agentkit-mpa-agent` 仓库目前拥有 `app.deployment.runtime.AgentRuntimeDeployer`、账号网络准备、独立业务库、技能空间登记和账号共享 APIG/IM Gateway 服务。其 `docs/agent-runtime-deployment.md` 和 `docs/account-shared-apig.md` 明确要求外部 Studio 创建接入该编排。当前任务检索返回空的对话内容,因此这里的结论来自已检查的代码和文档,不假设已读到对话原文。 - -## 目标与非目标 - -在 MPA 目录中创建可用智能体,与 CLI 使用相同的前置配置。展示缺失配置、进度、失败及可恢复创建。保留旧 CLI 路径。 - -不创建 PostgreSQL 云实例、PostgreSQL 登录角色、IAM 角色/策略、模型服务、跨 VPC 连接或数据库白名单。这些前提需提供并检查。不隐式复制其他智能体的渠道或技能空间。不提交凭据;实施过程中未经单独授权不执行真实云部署。 - -## 场景与需求 - -- **FR-1:** 有智能体管理权限且选择 MPA 类型时,展示“创建 MPA 智能体”卡片,火山引擎下空列表也有入口。其他类型保持原有创建行为。 -- **FR-2:** 打开创建窗口,加载所选地域的安全服务端配置摘要。缺失或不兼容的部署配置阻止提交,并指出未满足的前提。不向浏览器返回密码、令牌、原始环境、可执行文件路径或私有 YAML。 -- **FR-3:** 操作者提供稳定智能体 ID 和描述;自动生成的 ID 在重试时保持不变。服务端决定允许使用的配置、运行环境、凭据和资源账号,不允许前端通过账号 ID 选择账号。提交前展示将创建或复用的资源。 -- **FR-4:** 扩展 `mpa-create.config.example.yaml`,增加带版本的托管部署区段:地域、PostgreSQL 管理/注册库密钥引用、Runtime 模板或参考 Runtime、网络选择/默认网段和可用区、APIG 复用/接管策略、worker 配置及超时。CLI 和 Studio 共用解析校验。显式旧 CLI 参数保持原优先级和行为。 -- **FR-5:** 云写入前检查依赖及配置。准备/复用账号网络,验证/创建共享 APIG 和 IM Gateway,再确保业务库与技能空间并部署 Runtime。复用 MPA 注册库及账号/地域锁,不另建无关注册机制、不随意选取网关。等待应用就绪时不得持有该锁。 -- **FR-6:** 使用当前托管元数据初始化,不写占位地址。启用公私网、KeyAuth、MPA 分类、元数据/IM 初始化并绑定 worker。不意外继承参考智能体的身份、渠道凭据、技能空间或 worker 身份。 -- **FR-7:** 平台 Ready 且应用 `/readiness` 成功后才返回成功。返回安全的 Runtime/技能空间/APIG ID,并刷新原地域的 MPA 列表。不把本地依赖检查描述为云权限或连通性证明。 -- **FR-8:** 防止重复提交,按认证用户隔离任务,保存足够的非敏感身份信息以支持重启或响应丢失后的恢复。同身份/配置复用登记资源;未完成配置冲突时明确失败。复用原生持久化创建意图和 client token。 -- **FR-9:** 支持取消、截止时间、进程清理及过期响应隔离。取消停止编排,不等于回滚共享/持久资源;说明保留资源以供恢复。只允许删除本次新建的临时资源,不自动删除已登记业务库、技能空间、共享 APIG/网络或原有 Runtime。 -- **FR-10:** 使用本地化文案、现有创建卡片及模态组件、语义颜色、键盘焦点/输入法处理,以及加载、空态、错误、重试和窄窗口布局。 - -## 设计与备选方案 - -已批准决策(2026-09-20):用户选择“迁入 VeADK”。将网络、数据库、技能空间、Runtime 和共享 APIG 编排迁至 `veadk/integrations/mpa/managed/`,保留原生注册库结构、归属标记及锁语义。VeADK 负责迁入实现及测试,不依赖外部仓库、部署 Python 环境或用户指定的子进程命令。Studio 任务通过当前 VeADK Python 解释器启动子进程,以可靠取消阻塞 SDK 调用。旧 CLI 路径保持不变。 - -直接设计审查:现有 AgentKit/Volcengine、SQLAlchemy 和 asyncpg 依赖可以支持迁移。只迁移部署所需 APIG 操作;机器人路由和应用启动仍在 MPA 镜像内。镜像须支持共享注册库元数据初始化。Runtime、网络、APIG 统一使用同一凭据解析器,防止跨账号准备;支持轮换 STS 和已有部署 AK/SK,不增加 IAM 策略修改。先通过测试和归属校验处理云响应丢失及多用户重试,再接入界面。 - -适配器在创建 Runtime 前组合账号网络准备与 `SharedAPIGService.ensure`,避免嵌套获取账号锁。接管 APIG 需要显式 ID,且账号/地域/VPC 一致。部署凭据与 Runtime 凭据分开:迁入的部署 APIG 客户端与 Runtime/网络调用共用刷新并核验账号的凭据来源。MPA 镜像启动后仍使用自身挂载的 STS 来源。资源准备前报告缺失前提。Runtime 角色仍需自身挂载的 IAM 凭据和云权限。 - -参考 Runtime 模式只复用部署设置;显式模板模式支持新账号。PostgreSQL 实例及共享注册库须已存在。自动新建账号网络不能证明能访问私网 PostgreSQL,需要时必须显式指定兼容 VPC。适配器保留现有托管命名和归属标记,创建专属 worker 或校验显式指定的可复用 worker。 - -### 已批准接入决策 - -采用兼容 CLI 入口 `veadk mpa provision --config /secure/mpa-create.config.yaml --agent-id customer-service`,读取同一个 YAML 文件中的新增 `managed` 区段;`veadk mpa create` 保持当前平铺参数语义。`provision --dry-run` 校验配置并仅输出安全计划,不加载云凭据、不查询或写入云/数据库资源。Studio 通过仅限服务端的 `VEADK_MPA_CREATE_CONFIG` 设置定位同一配置。 - -`managed` 字段:`version: 1`;环境变量名 `database-admin-url-env`、`shared-database-url-env`;可选的部署 STS 轮换文件 `credential-file`;互斥的 `from-runtime`/`template-file`;`network` 包含 `vpc-id`、`subnet-ids`、`vpc-cidr`、`subnet-prefix`、`zone`;显式接管 `apig.adopt-id`;`worker` 包含显式复用 ID 或镜像/参考配置;以及 `timeout-seconds`。环境变量名是引用,不是数据库 URL 字面值。相对模板路径基于 YAML 所在目录解析。未配置对应地域时报配置错误,不回退到其他地域。 - -两个来源选择器均未提供时,仍可使用平铺的模型/镜像/PG 参数构建全新 Runtime 模板。托管模式通过原生智能体身份推导业务库名,不把旧 `pg-database` 复用成多个智能体共享的业务库。旧 `account-id` 只能作为期望账号断言,与云端认证账号比较,不能作为账号可信来源。 - -### 接口 - -- CLI:`veadk mpa provision` 读取现有 YAML 的托管区段;继续支持旧 `veadk mpa create`。 -- `GET /web/mpa-creation/config?region=...`:安全配置摘要及未满足前提。 -- `POST /web/mpa-creation/tasks`:经过校验的身份、描述、地域和请求 ID;返回任务 ID。 -- `GET /web/mpa-creation/tasks/{id}`:用户隔离的阶段、状态、安全结果及错误码。 -- `POST /web/mpa-creation/tasks/{id}/cancel`:界面确认后的幂等取消。 -- 状态:`running -> succeeded` 或 `running -> cancelling -> cancelled`;失败以 `failed` 结束。阶段报告 `queued`、`checking`、`network`、`gateway`、`worker`、`database`、`skills`、`deploying`、`verifying`。远端结果未知时可恢复,不标记成功或盲目重新创建。 - -进程消息使用结构化协议、大小限制和字段白名单。不得向客户端转发原始部署程序 stdout/stderr、数据库 URL、环境变量、SDK 异常或 Pydantic 原始校验输入。取消/超时/服务端关闭时终止并回收进程。弹窗卸载时取消轮询请求;服务端任务保存在 SQLite 中并可查询;没有自动任务历史过期机制。 - -## 实施任务与影响文件 - -| 任务 | 范围 | 需求 | -| --- | --- | --- | -| T-1 | 审查双语设计/契约,确认部署编排归属方式 | FR-1–FR-10 | -| T-2 | `veadk/integrations/mpa/` 下共用托管 YAML 结构与安全前提计划;示例 YAML 和 CLI 接入 | FR-2–FR-6 | -| T-3 | VeADK 部署适配器、协议、资源准备及恢复测试 | FR-5–FR-9 | -| T-4 | `frontend/server/` 任务路由及 `veadk/cli/cli_frontend.py` 注册;鉴权和有界生命周期 | FR-2、FR-3、FR-7–FR-9 | -| T-5 | `frontend/src/ui/MyAgents.tsx`、MPA 弹窗/客户端、双语文案 | FR-1–FR-3、FR-7–FR-10 | -| T-6 | 回归/契约测试、浏览器检查、生产静态资源、双语证据同步 | FR-1–FR-10 | - -## 验证与验收 - -先写会失败的回归/契约测试,再改生产代码。默认使用模拟云接口或隔离测试存储。 - -| 验收 | 所需证据 | -| --- | --- | -| AC-1:仅在预期 MPA 类型及权限下可进入创建 | 组件测试及真实浏览器正常/空态/窄窗口/键盘/输入法流程 | -| AC-2:缺失配置/PG/注册库/网络/worker/STS 时安全报错并阻止执行 | YAML/CLI 和服务端契约测试;dry-run 不创建资源 | -| AC-3:按顺序准备独立业务库/技能空间及账号共享资源 | 模拟部署集成测试断言调用、请求和归属;原生注册库/请求体兼容性检查 | -| AC-4:重复提交、重启、响应丢失、冲突、超时/取消及切换地域安全 | 生命周期和恢复测试、过期响应及进程回收检查 | -| AC-5:凭据不进入输出、任务持久化或生成资源 | 负向脱敏测试、最终 diff/静态资源密钥扫描 | -| AC-6:旧部署行为兼容 | `uv run --extra dev pytest tests/cli/test_cli_mpa.py tests/integrations/` 限定受影响 MPA 测试 | -| AC-7:发布检查完整且如实记录 | 定向 Python 测试;修改文件 Ruff/Pyright;`npm --prefix frontend test`;`npm --prefix frontend run build`;`npm --prefix frontend run test:webui-assets`;真实浏览器证据 | - -共享 API/生命周期变化需要扩大 Python 回归:`uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`。获得提交授权后,提交前同步目标远端基线,运行 `uv run --extra dev pre-commit run --all-files` 和单元测试。真实部署冒烟需单独授权,模拟部署测试不能替代它。 - -## 风险、审查与交付记录 - -- 用户于 2026-09-20 批准迁入 VeADK。部署实现及兼容测试由 VeADK 维护,共享数据契约仍与 MPA 镜像互通。 -- 原生取消保留持久资源;需协调 Studio 临时 Runtime 清理规则与已登记持久智能体,不建议自动破坏性回滚。 -- 原生模板继承可能携带其他智能体的 worker,必须显式校验/准备 worker。 -- 每个步骤校验 SDK 响应结构及资源就绪状态。本地配置检查不能在云写入前证明所有真实 IAM 权限。 -- PostgreSQL session advisory lock 需要直连或 session pooling,不能使用 transaction pooling。 -- 2026-09-20 直接审查发现两个额外实施阻塞项,修复纳入 T-3/T-4: - - 原生 `deploy_runtime` 在创建 Runtime 前准备网络,但通常在 Runtime 启动时才创建 APIG。只调用该 CLI 不满足 FR-5。适配器必须准备账号网络、释放锁、调用 `SharedAPIGService.ensure`,再以解析后的网络调用 `AgentRuntimeDeployer`。写入前核对两个云客户端账号一致,避免嵌套加锁。 - - 现有 `ensure_codex_worker_tool` 按名称复用,并在创建时生成新的 ClientToken,无法证明归属或安全恢复 CreateTool 响应丢失。托管创建必须在原生智能体部署记录中保存 worker 创建意图/token 及已验证归属,或校验显式配置的外部 worker。不能原样调用旧 helper 就宣称具备持久重试安全性。 -- 鉴权审查:四个接口均使用 `_require_agent_management`。启用认证的多用户 Studio 中,生成的智能体身份要绑定所属用户,并在持久服务端状态中记录归属;其他管理者猜到智能体 ID 也不能覆盖该用户的部署。本地无认证开发模式明确沿用已有 `local` 主体行为。 -- 仓库和可用本地技能目录未找到 `frontend-design`、`ui-ux-pro-max`、`review-spec`。遵循已读取的前端标准并直接进行设计审查。 -- 2026-09-20 实现审查:已解决账号锁顺序和 worker 持久创建意图;增加 Universal SDK 原始响应解析回归、云写入前数据库权限检查、响应丢失后输入锁定、任务库连接清理及子进程回收。共享 `ModalLayout` 增加可选 footer 插槽(默认行为不变),弹窗不再通过 CSS 隐藏组件内部结构。用户迁入批准覆盖 T-1;T-2–T-5 已实现。T-6 验证见下文。 -- 不改变 SDK 智能体执行、harness/sidecar 协议或文档站契约。现有控制面与旧部署规范链接到新责任方,避免重复契约。[操作指南](../../../veadk/integrations/mpa/managed/README.zh.md)说明前提和恢复流程。 - - -### 验证记录(2026-09-20) - -范围:基于 `314d43c8` 的工作区改动;未提交、推送或部署云资源。已批准的迁入已实现;发布验收受下列既有类型检查基线和明确排除的真实云验证限制。 - -| 检查 | 结果 | 证据 / 限制 | -| --- | --- | --- | -| 定向 Python | pass | `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q`:162 项通过。含最终重复取消/关闭回归,保护清理前该测试失败。 | -| 前端全量 | pass | `npm --prefix frontend test`:1208 项 Node 测试、22 项 Vitest 测试通过。覆盖缺失配置、重复提交、配置迟到、POST 响应丢失、默认/自定义模态 footer。 | -| 生产资源 | pass | `npm --prefix frontend run build`;`npm --prefix frontend run test:webui-assets`:验证 104 个文件、248 个引用。构建仍有既有大包提示。 | -| 本地化 | pass | `npm --prefix frontend run check:i18n`:2 个语言、21 个命名空间一致。 | -| Ruff | pass | `uv run --extra dev --with 'ruff==0.11.12' ruff check veadk/integrations/mpa/managed frontend/server/mpa_creation.py tests/integrations/mpa_managed veadk/cli/cli_mpa.py veadk/cli/cli_frontend.py`;检查新增 Python 和 CLI 增量格式。 | -| Pyright,新增模块 | pass | `uv run --extra dev --with pyright pyright veadk/integrations/mpa/managed frontend/server/mpa_creation.py`:17 个文件,零错误。 | -| Pyright,完整改动 Python 范围 | fail | 加上 `veadk/cli/cli_mpa.py` 和 `veadk/cli/cli_frontend.py` 后有 37 项错误。对未修改的 HEAD 源文件执行相同工具,得到完全一致的 37 项诊断签名;无新增错误。未屏蔽或修改无关既有错误。 | -| 真实浏览器,隔离接口 | pass | 本地 Vite 中实际 MyAgents/弹窗:空 MPA 创建卡片;缺失配置与重试;中文多行、Tab/Enter 提交、Escape 关闭/焦点返回;APIG 进度;成功 ID;失败/重试;取消确认/终态/重试;关闭重开恢复;390×844 及桌面下浅/深主题。仅模拟接口。已移除临时页面/服务/标签并恢复视口。未自动化操作原生输入法候选窗口。 | -| 仓库密钥规则 | pass | Gitleaks 以 `.gitleaks.toml` 扫描改动/新增文件快照,无发现。已从生产源码移除公开 RFC 1918 范围字面量,保持相同范围校验并增加边界回归。未关闭扫描规则或钩子。 | -| 额外生成资源扫描 | fail(已核验误报) | 默认 Gitleaks 规则扫描全部 `veadk/webui` 共 19.17 MB,报告一处编辑器压缩表达式 `anchor.key` / `focus.key`,并非凭据。仓库规则有意排除第三方构建包;此次独立发现已人工检查,未发现敏感值。 | -| 文档及空白 | pass | 核对双语文件、FR/CON/AC/任务标识、相对链接和本地化键;`git diff --check` 无问题。 | -| 真实云 / Codex / piagent 冒烟 | not_run | 未授权分配真实云资源;Codex/piagent 运行时不受影响并从回归排除。模拟云及浏览器结果不能证明真实 IAM/镜像/网络/PostgreSQL 互通。 | -| 全库 pre-commit / 远端同步 | not_run | 未授权或执行提交;获得提交授权后,必须先同步远端再执行这些门禁。 | - -首次全量回归 `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` 使用两个 worker:4687 项通过、6 项失败、2 项收集错误、11 项跳过、2 项 xfailed。失败原因是可选 `anthropic`/LlamaIndex 包缺失;临时加载可选包后,剩余 Harness 配置测试需要虚拟模型密钥,避免发现真实 IAM。隔离重跑使用 `MODEL_AGENT_API_KEY=test-only MODEL_EMBEDDING_API_KEY=test-only` 和 `--with anthropic --with 'llama-index-embeddings-openai-like>=0.2.2' --with 'llama-index-llms-openai-like>=0.5.1'`;未修改依赖文件或机器全局配置。最终全量结果记录在下方。后续局部修复由最终 162 项定向测试覆盖。 - -最终全量回归:**pass**,使用两个 worker 和上述临时可选依赖环境,4711 项通过、7 项跳过、2 项 xfailed、91 项警告,用时 293.20 秒。跳过/xfail 项不宣称已验证。已批准实施范围内 T-1–T-6 完成;真实云验证和既有基线类型错误仍作为明确限制记录。 - - -### 后续:明确的本地配置错误(2026-09-20) - -用户报告了笼统的配置加载错误。已复现:Studio 进程从 VeADK 根目录运行,默认 `mpa-create.config.yaml` 不存在;加载该路径得到相同错误。此修复属于已批准 FR-2/CON-1/CON-8:配置错误须指出缺失前提且不泄露密钥。保留配置路径/默认路径契约,不创建云资源或自动选择其他部署账号。区分配置缺失/不可读、YAML 格式错误、Runtime 模板缺失/格式错误,并给出安全提示。修改加载器前增加负向测试,保持脱敏,执行相关 config/routes/CLI 测试及 Ruff/Pyright。复用已有资源属于独立运维选择,等待用户回答。直接设计审查:不改变 HTTP 结构或部署行为,现有本地化界面展示安全服务端细节,仍隐藏私有路径/原始解析异常。 - -后续验证:**pass**,`uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q`(167 项通过);修改加载器/测试的 Ruff、加载器 Pyright 通过;`git diff --check` 通过。五个新增负向用例在修复前失败。本次后续不改前端行为或静态资源,前端构建/浏览器门禁不适用。用户已选择复用已有北京资源。实际本地凭据转移因自动审批拒绝读取进程凭据及跨仓库复制数据库密钥而暂停,等待明确授权;未改动凭据或云资源。 - -本地配置完成(2026-09-20):明确说明凭据范围后,用户要求“帮我加上试试”,授权在本地私有配置中复用。通过只读云调用核验已有北京账号、参考 Runtime 和 worker。创建 Git 忽略的 `.env`、`mpa-create.config.yaml`,权限均为 0600;部署 AK/SK 仅在内存中使用,没有写入这两个文件。确认没有活动创建任务后,以现有启动环境重启本地 Studio。真实配置检查接口返回 HTTP 200、`configured: true`,地域 `cn-beijing`,来源 `reference`,保留 `requiresLiveChecks: true`。未创建云资源或执行部署。此前本地配置授权阻塞已解除;这是配置验证,不是真实部署冒烟。 - -### 后续修正:worker 查询分页(2026-09-20) - -对用户发起的创建进行只读检查:任务停在 `worker`,原生记录只有 `studio_owner`,尚未写入 worker 创建意图。`ListTools(PageNumber=1, PageSize=100)` 与第 2 页返回完全相同的工具 ID。响应提供 `NextToken`,按该游标读取能获得剩余 27 个工具。单次请求不到一秒,旧查询却可能重复调用 1,000 次。这是已批准 FR-5–FR-9/T-3 范围内的正确性缺陷,并非 worker 启动缓慢的证据。 - -方案与设计评审:使用 `MaxResults=100` 和 `NextToken`,仅在没有后续游标时结束,拒绝重复游标,保留原有最多 1,000 页的保护。对重叠页中匹配的 ToolId 去重,但保留不同 ID 的同名资源,供归属冲突检查。云端错误向上传递,不能把未完成的查询视为资源不存在。不改变凭据处理、归属、创建请求、取消、任务标识、API 或 UI。维护 CON-2/CON-3,在双语组件规范中补充分页约束。用户已批准迁入实现,并要求排查正在运行的创建,涵盖本次实现修正;重启或重试线上任务仍属于独立的运维操作。 - -任务:先补充失败回归,覆盖未满的中间页、满的末页、重复匹配、不同资源同名、重复游标和页数上限;修复 `WorkerCloud.find`;运行 managed/CLI 测试以及变更文件的 Ruff/Pyright。验收:查询按游标遍历所有页且每个游标只访问一次,不得在查询不完整或失败后静默继续创建。评审确认无需数据库迁移、新依赖、破坏性消费者契约或前端产物变更。验证待执行;线上检查仅为只读,未取消或重新启动创建。 - -分页验证(2026-09-20,基于 `314d43c8` 的工作区,仅本次 worker 实现/测试与双语设计/规范):**pass**。六个新增回归用例在修复前全部失败;修复后 `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q` 为 173 passed。变更文件的 Ruff 0.11.12 检查/格式、worker Pyright 和 `git diff --check` 均通过。使用修复后的 `WorkerCloud.find` 进行云端只读检查,两次 ListTools 调用在 1.05 秒内遍历两页,未找到匹配资源。原任务现已失败在 `worker`,原生记录仍只有 `studio_owner`。持久化错误为统一错误码,未保留最终异常的准确内容;反复查询第一页的问题已独立验证。未重试任务或执行云端写操作。本次仅修改 Python,前端/构建/浏览器检查为 **not_applicable**;未再次运行全量回归(**not_run**),因为本次隔离改动已由 managed/CLI 套件覆盖,此前全量验证仍记录在上方。沿用原任务重试会启动新的 runner 进程并导入修复代码,无需重启 Studio。本次诊断未执行完整线上创建(**not_run**)。 - -线上创建观察(2026-09-20):用户在 Studio 新发起创建后,只读检查观察到 worker Ready、独立数据库和技能空间已登记、Runtime 从 Creating → Releasing → Ready,任务最终达到 `succeeded`。原生部署记录为 `state=ready`、`pending=false`;独立鉴权访问 `/readiness` 返回 HTTP 200。旧任务仍失败在 `worker`;新旧任务的智能体和任务标识不同。这为所选本地参考配置下的一次北京地域真实创建提供 **pass** 证据,不代表所有配置、IAM 边界或云端失败恢复场景均已验证。本次跟进没有再次发起创建、取消任务、修改云资源或提交/推送代码,仅观察用户发起的部署。 - -### 后续调整:直观的 Runtime 名称(2026-09-20) - -用户批准:提出使用 `MPA_AGENT_ID` 作为新 Runtime 名称后,用户要求实施(“帮我改下”)。FR-11/T-7/AC-8:新建托管 Runtime 直接使用稳定的智能体 ID 作为 `Name`;资源 ID、数据库/worker/技能空间的命名和归属保持原样。背景:自动生成的 `mpa-agent-<账号-地域-智能体哈希>` 名称无法直观对应 Studio 中输入的 ID。Studio 和托管 CLI 均通过 `AgentRuntimeDeployer` 生效。 - -方案与直接评审:已登记的现有 Runtime 保留云端返回的名称。旧版创建未完成且响应丢失时,只有完整的旧请求哈希与持久化创建意图完全相同,才沿用旧哈希名称,并重用原 token 和完整请求。其他输入变化仍应失败。新部署直接使用智能体 ID,即使参考模板带有其他名称。当前 SDK `UpdateRuntimeRequest` 与[官方 UpdateRuntime 契约](https://www.volcengine.com/docs/86681/1923461) 均未提供 `Name`,不能声称可以重命名已有实例,也不能静默替换它。因此现有云实例改名尚未实现,本次代码改动不执行云端写入。不改变前端 API、任务生命周期、IAM、凭据、数据库结构或现有资源的遥测标识。双语 CON-3 和操作文档需说明该兼容规则。 - -任务与测试:先添加失败用例,覆盖新名称直接对应 ID、保留已有旧名称、旧版创建响应丢失后用完全相同的请求/token 恢复,以及拒绝旧请求输入变更;仅实现 Runtime 名称选择与请求哈希兼容。验收:命名/恢复用例与 managed/CLI 回归通过,变更文件 Ruff/Pyright 和空白检查通过。无需重新生成前端产物。风险:云端重名仍作为提供方错误返回,不会接管其他资源;旧版程序无法恢复新版的待完成请求哈希。状态:已批准,验证待执行。`review-spec` 技能不可用,以此处的边界、兼容和双语直接评审替代。 - -FR-11 验证(2026-09-20,基于 `314d43c8` 的工作区):**pass**,`uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q` 为 176 passed。实施前,直接名称回归失败(1 failed、19 passed);旧版兼容用例验证已有请求仍可恢复。变更 Runtime/测试文件的 Ruff 0.11.12 检查/格式、Runtime Pyright 和 `git diff --check` 通过。直接实现评审确认:兼容回退要求完整旧请求哈希匹配,且尚未登记 Runtime ID,不会忽略任意输入变化或替换现有 Runtime。双语 PRD、CON-3 与操作文档已同步。FR-11/T-7/AC-8 的新建命名与兼容部分已实现。浏览器/构建检查为 **not_applicable**(没有 UI 或产物变更);未再次运行全量 Python 回归(**not_run**),该独立命名改动已由 managed/CLI 套件覆盖。未创建另一个智能体验证新名称(线上创建 **not_run**);已有云实例改名为 **blocked**,因为受支持的更新契约没有 Name 参数。未修改已有资源,未提交或推送。 - -### 后续调整:显式配置 MPA 镜像(2026-09-20) - -FR-12/T-8/AC-9,用户要求 MPA 镜像像 worker 镜像一样配置,已批准实施。背景:`managed.from-runtime` 继承 ArtifactType/ArtifactUrl、角色、CPU/内存、扩缩容/并发、APM/项目、VPC 和经过身份清理的环境变量,并非只有镜像。操作者需要在保留基础设施、模型和数据库配置时独立固定 MPA 镜像。 - -方案:新增可选 `managed.runtime.image`,与 `managed.worker.image` 并列。显式提供的非空镜像覆盖 ArtifactUrl,并将 ArtifactType 设为 `image`,对参考 Runtime、JSON 模板、平铺配置三种来源均生效。省略/null 保持原有来源行为;本地校验拒绝空白、含空格/占位符的镜像及未知 runtime 配置项。平铺模式可用该字段替代原顶层 `image`,其他前置配置仍然必需。浏览器配置摘要不新增镜像值。模板复制及继承环境变量清理不变。未完成部署的请求哈希仍拒绝镜像变化;不会自动更新已有智能体。将已验证的当前 MPA 镜像写入本地 Git 忽略的 YAML,实际选用镜像和其他私有配置不变。确认没有活跃创建任务后才重新加载本地 Studio,不部署或分配云资源。 - -直接设计评审:这是 CON-1 的增量配置契约,模板来源仍互斥,显式镜像不能替代必要的凭据、网络、数据库或模型设置。测试覆盖所有来源的覆盖优先级、省略兼容性、非法配置和平铺镜像前置条件,仅模拟云调用。运行 managed/CLI 测试、Ruff/Pyright 和本地配置 GET。同步双语组件/操作文档与占位符 YAML 示例。现有前端行为与产物不受影响。风险:指定镜像仍需兼容继承配置;未完成请求必须保留原实际镜像。review-spec 不可用,已直接完成双语、兼容和安全评审,无未解决的实施阻断。状态:已批准,验证待执行。 - -FR-12 验证(2026-09-20,基于 `314d43c8` 的工作区,配置/服务/测试与双语文档/示例):**pass**。实施前针对性测试 5 failed、20 passed;实施后 `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q` 为 188 passed。变更文件 Ruff 0.11.12 和 config/service Pyright 通过,已格式化;示例 Managed schema、双语文档评审和 `git diff --check` 通过。Git 忽略的私有 YAML 已通过 `managed.runtime.image` 固定先前验证的 MPA 镜像,并保留 0600 权限及其他配置。确认活跃创建任务为零后,保留内存中的原启动环境重启了本地 Studio。实际 GET `/web/mpa-creation/config?region=cn-beijing` 返回 HTTP 200、configured=true、source=reference。没有输出或新增持久化秘密。FR-12/T-8/AC-9 已实现。线上部署为 **not_run**(验证该配置变更无需新分配资源);前端/构建/浏览器检查为 **not_applicable**(未修改前端代码或产物);未再次运行全量 Python 和 pre-commit(**not_run**,已完成 managed/CLI 定向回归,用户未要求提交)。未变更云资源、提交或推送。 - -### 后续调整:显式配置 Runtime 基础设施与环境(2026-09-20) - -FR-13/T-9/AC-10。用户要求列出实际继承的设置并将其配置化。只读检查确认所选来源使用 CPU 2000 milli、内存 4096 MiB、最少/最多实例 1/1、并发 100、开启 APM、项目 default、Runtime IAM 角色、公网/私网双网络,以及模型/PostgreSQL/应用环境设置。目标:让这些选择在 YAML 中明确可查,移除本地配置对 `from-runtime` 的依赖。既有 worker 参考配置属于独立范围,保持原样;不部署云资源或替换实例。 - -方案:扩展可选 `managed.runtime`,增加 role-name、cpu-milli、memory-mb、min-instance、max-instance、max-concurrency、apmplus-enable、project-name 和 env。省略字段保持所选模板/默认值,显式值覆盖对应字段。资源变更前验证计算规格/并发为正、最少实例非负、最多实例为正,且合并后的 min<=max。既有 `managed.network.vpc-id/subnet-ids` 指定双网络,仍要求公网与私网连通。现有平铺 model/PG 字段显式设置模型地址/名称/提供方和数据库地址/端口/登录/SSL。`runtime.env` 提供显式应用环境覆盖,并在服务端解析完整 `${ENV_NAME}` 引用。值必须为字符串,键必须是合法环境变量标识。拒绝创建流程拥有的字段(智能体/Runtime/工具/技能标识、派生数据库名、Runtime 端点/鉴权/加密、共享注册库/管理员 URL、请求/遥测标识);模型与数据库连接仍可配置。配置摘要和校验错误不返回环境变量值。 - -迁移:将当前实际基础设施、模型/PG/应用设置固定到被 Git 忽略的本地 YAML,移除 `from-runtime`;秘密值放入现有 Git 忽略且权限为 0600 的 .env,YAML 仅引用变量。两个文件保持 0600,并保留私有回退副本。使用之前已授权的内存凭据读取云端值,仅输出非秘密摘要与等价校验结果。保存前比较新模板设置与旧参考配置,不改变所选镜像、网络、角色、凭据或资源标识。仅在没有活跃创建任务时重启本地 Studio 并验证配置接口。部署 AK/SK 始终只在内存中使用。 - -直接评审:CON-1 增量覆盖字段,旧配置保持默认行为;继续执行 CON-3/CON-8 的生成标识和脱敏规则。未完成部署中修改来源模型仍会触发已有请求哈希冲突,重试必须保持原实际设置。先测试全部覆盖字段、优先级/省略、false 布尔值、最少实例为零、合并后扩缩容冲突、秘密引用解析/脱敏与保留键拒绝,再运行 managed/CLI 回归和 Ruff/Pyright。同步双语契约/操作文档/示例。前端 API 和产物不变,无需浏览器构建。风险:显式固定后不再跟随参考 Runtime 的后续修改,更新由操作者负责。用户请求授权本次配置工作,不包含提交、部署或新建智能体。review-spec 不可用,已完成直接评审。状态:已批准,验证待执行。 - -FR-13 验证(2026-09-20,基于 `314d43c8` 的工作区,config/service/测试与双语文档/示例):**pass**。实施前针对性回归 5 failed、40 passed;实施后 `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q` 为 208 passed。变更文件 Ruff 0.11.12、config/service Pyright、示例 schema 和 `git diff --check` 通过。Runtime 覆盖测试涵盖 false/零值、实际 min/max 约束、非法/创建流程保留环境键、秘密解析/脱敏及原来源兼容。本地配置迁移逐项验证镜像、全部基础设施字段、VPC 及有效环境值相同,仅排除创建阶段重新生成的流程专属字段。已移除 from-runtime,保留 worker 引用,将两个秘密值改为环境引用,并保留私有回退文件;YAML/.env 仍被 Git 忽略且权限为 0600。重启前确认活跃创建任务为零,原启动环境仅在内存保留;实际配置 GET 返回 HTTP 200 和 configured=true。没有云端写入、新建智能体、提交或推送。浏览器/构建为 **not_applicable**(无前端或产物变更)。未再次运行全量 Python 回归/pre-commit(**not_run**,managed/CLI 定向套件覆盖该增量配置变更,用户未要求提交)。显式配置下的完整线上创建为 **not_run**;等价比较与本地配置验证不能证明所有云配置组合。FR-13/T-9/AC-10 的配置与本地迁移已实现。 - -### 后续调整:创建弹窗可编辑镜像(2026-09-20) - -FR-14/T-10/AC-11,用户明确批准:新增 MPA 镜像和 Worker 镜像文本输入框,默认填入当前配置,可手动覆盖;留空则使用服务端配置。复用现有弹窗输入和本地化帮助/错误文案,不接镜像仓库选择或列表。仍要求现有管理员权限。 - -契约与设计评审:鉴权后的配置 GET 仅新增本地可确定的镜像引用(`runtimeImage`、`workerImage`),不返回环境变量/凭据。旧版仅引用 Runtime 的配置无法在本地确定镜像时,留空并保留原来源行为。POST 可选字段同名,去除首尾空白,限制 1024 字符,拒绝 URL/凭据/查询参数/片段及非法 digest。显式 Runtime 镜像覆盖所选来源;显式 Worker 镜像选择独立 worker,不再复用配置指定的已有 worker。留空保持默认值。SQLite 新增 `images` JSON 列,首次提交时保存本地可确定的实际镜像,独立于原请求标识,防止空字段重试时默认配置变化。旧记录填 `{}`,保留旧行为;迁移不调用云端或隐式接管资源。Runner 接收并应用持久化镜像快照。同一请求更改显式镜像构成冲突。不含秘密的任务响应返回实际镜像,提交后输入锁定,session storage 保留原请求,包含响应丢失情况。迟到配置响应不覆盖手动编辑、清空值或恢复的已提交输入。关闭/取消延续现有任务生命周期。 - -任务:先补充失败的配置/接口/任务/runner 与前端回归;实现类型、校验、快照迁移/runner 传递和两个输入框;同步语言包、双语契约/操作文档与 frontend README;运行针对性及全量前端测试、managed/CLI 测试、变更 Python Ruff/Pyright、生产构建/产物/i18n 检查,并用隔离真实浏览器验证正常/空白/非法/加载/重试/恢复/取消/键盘/窄窗口。不在验证中创建云资源;镜像可访问性和 IAM 兼容性仍由用户发起部署时的云端检查确定。兼容旧配置、旧客户端和旧记录。frontend-design、ui-ux-pro-max 和 review-spec 仍不可用,遵循 frontend/SPEC.md 并直接评审。评审覆盖重复/丢失请求、结构迁移、固定重试、显式已有 worker 覆盖、错误和秘密边界。状态:已批准,验证待执行。 - -FR-14 验证(2026-09-20,基于 `314d43c8` 的工作区,弹窗/客户端/语言包、创建接口/配置/任务/runner、测试/文档及重建前端产物):**pass**。实施前 4 个新增后端用例与 3 个前端用例失败。最终 `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q` 为 221 passed,覆盖旧 SQLite 结构迁移、重试固定快照、worker 复用覆盖及实际 runner 配置应用。`npm --prefix frontend test`:1208 个 Node 测试、25 个 Vitest 测试通过。`npm --prefix frontend run build`、`test:webui-assets`(104 文件/248 引用)、`check:i18n`(2 种语言/21 命名空间)通过。变更 Python Ruff 0.11.12 检查/格式与 Pyright 通过。按仓库规则对本次源码/构建产物快照运行 Gitleaks,无发现,保留 vendor 排除规则。`git diff --check` 通过,双语文档与 API 标识已评审。 - -真实浏览器使用实际弹窗和隔离模拟接口,验证通过:默认值、非法镜像提示/禁止提交、自定义 MPA 镜像与 worker 留空回退、多行中文输入、Tab/Enter、提交锁定、失败/重试、关闭后任务恢复、取消、加载时字段禁用,以及 390×844 深浅主题布局。缺失配置与 POST 响应丢失恢复由自动化测试覆盖;尝试的浏览器缺失配置场景结论不明确,不声称通过。原生操作系统 IME 候选交互为 **not_run**。已删除临时测试页、停止测试服务、关闭测试标签并重置视口。没有继续操作生产 Studio 登录页,没有新建账号/接受条款或实际云端创建。确认没有活跃创建任务后保留原启动环境重启 Studio,实际配置 GET 为 HTTP 200、configured=true,两个镜像默认值非空,未返回环境/凭据字段。没有改变已有云端智能体。未再次运行全量 Python 回归或 pre-commit(**not_run**,定向生命周期/接口/CLI 覆盖已通过,用户未要求提交)。未提交/推送。FR-14/T-10/AC-11 已实现;模拟部署不能证明镜像可拉取及云端权限。 - - -### 集成验证:变基到 main(2026-09-21) - -范围与授权:用户要求解决 PR #5 的合并冲突。将 `feat/from-main` 变基到 `origin/main` 的 `f55e8918`,审查合并后的 `703859c7` 及集成差异。保留 main 的定时任务、A2A、沙箱下载、JWT 和 tracing 文档,以及本分支的 MPA 信息侧栏和托管创建文档。从合并后的源码重新构建全部打包 WebUI 产物。冲突解决不引入新行为或组件契约,现有双语契约继续生效。原有两份本地类型验证文档修改已单独保存,待集成提交后恢复。 - -干净安装最初因合并后的锁文件缺少可选依赖记录而失败。使用 npm 重新生成 `frontend/package-lock.json`,未改变任何已有依赖版本,随后 `npm --prefix frontend ci --no-audit --no-fund` 通过。本机 Node 26 的实验性 Web Storage 导致沙箱下载测试的四个清理步骤失败;使用 `NODE_OPTIONS=--no-experimental-webstorage` 运行相同套件后通过,未修改应用或测试行为。首次 pre-commit 仅格式化了合并后 Runtime 代理测试的两处表达式,Ruff 检查和敏感信息扫描通过。最终 pre-commit 与格式化文件回归结果补充在下方。 - -2026-09-21 验证结果: - -- **pass** — `uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py tests/cli/test_cli_mpa.py tests/frontend/server/test_mpa_cron.py tests/cli/test_frontend_runtime_proxy.py tests/cli/test_frontend_apmplus_trace.py tests/cli/test_studio_trace_pagination.py -q`:398 项通过。 -- **pass** — `MODEL_AGENT_API_KEY=test-only MODEL_EMBEDDING_API_KEY=test-only uv run --extra dev --with anthropic --with 'llama-index-embeddings-openai-like>=0.2.2' --with 'llama-index-llms-openai-like>=0.5.1' pytest -n 2 -m 'not codex_smoke and not piagent_smoke'`:4,850 项通过、7 项跳过、2 项预期失败。使用两个 worker 控制本地资源占用。跳过项、预期失败项以及真实运行时 smoke 不由此证明。 -- **pass** — `npm --prefix frontend test`:1,215 项 Node 测试和 25 项 Vitest 测试;`npm --prefix frontend run test:mpa-cron-coverage`:71 项测试及配置的覆盖率门禁;`NODE_OPTIONS=--no-experimental-webstorage npm --prefix frontend run test:sandbox-download-coverage`:26 项测试及配置的覆盖率门禁。 -- **pass** — `npm --prefix frontend run build`、`npm --prefix frontend run test:webui-assets`(104 个文件、248 处引用)和 `npm --prefix frontend run check:i18n`(2 种语言、21 个命名空间)。保留现有的大体积 chunk 构建警告。 -- **pass** — 使用真实组件和模拟 API 的隔离浏览器 smoke:定时任务未选 Runtime 状态;创建镜像默认值、无效镜像提示与禁用提交、自定义 MPA 镜像与空 Worker 镜像、多行中文描述、提交后字段锁定、模拟失败及重试成功。临时测试入口、服务和标签页已删除或关闭,未修改云资源或生产用户状态。 -- **not_run** — 再次完整浏览器矩阵、原生 IME 和真实云端/运行时 smoke:本次集成保留现有行为,前文保留功能级浏览器证据,自动回归与合并源码的定向浏览器 smoke 覆盖冲突解决范围。真实镜像访问及 IAM 组合仍不在本次验证范围内。 - -直接审查未发现残留冲突标记或遗漏文档章节。最终集成提交仅包含重建产物、锁文件规范化、合并后测试的格式化及本双语验证记录。本次不合入或发布 main,而是更新 PR 分支以继续正常审查与 CI。 - -最终检查:**pass**,`uv run --extra dev pre-commit run --all-files`(Ruff 检查、Ruff 格式、Gitleaks);**pass**,格式化后 `uv run --extra dev pytest tests/cli/test_frontend_runtime_proxy.py -q`(80 项通过);**pass**,源码/文档 `git diff --cached --check -- . ':!veadk/webui'`。未过滤的差异空白检查为 **fail**,仅涉及生成 bundle 中七行变更行的第三方字符串空白。集成 bundle 重建前后均有 36 行尾部空白,因此保留生成字符串内容,未手工改写。最后一次获取远端确认 base 和原 PR 分支没有新提交。索引中无未解决冲突项。本次集成同时修正文档章节间距。 diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-mpa-tos-output-mount.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-mpa-tos-output-mount.md deleted file mode 100644 index 5618a9d6d..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-mpa-tos-output-mount.md +++ /dev/null @@ -1,77 +0,0 @@ -# MPA per-session TOS output mount - -- Status: approved -- Date: 2026-09-23 -- Owner: VeADK MPA provisioning -- Approval: the requester approved implementation on 2026-09-23 - -## Problem - -An MPA Runtime provisioned by VeADK can create a dedicated Codex worker Tool, -but the Tool and its Sessions currently have no TOS output mount. Outputs under -`/data/output` therefore have no per-conversation TOS namespace. - -## Goals - -1. Accept `tos-access-key`, `tos-secret-key`, and `tos-bucket` in the existing - private MPA YAML for both `mpa create` and managed `mpa provision`; also let - the Studio creation page override the complete tuple for one creation. -2. Put the TOS credentials only in the Tool `TosMountConfig`; do not copy them - into Runtime environment variables, dry-run output, logs, or registry rows. -3. Mount TOS read-write at `/data/output` and derive its endpoint from the Tool - region. -4. Tell the Runtime to generate `CreateSessionRequest.TosMountPoints` for each - newly created Session. The SDK-standard path is - `/sandbox-session/tool-{tool_id}/session-{session_id}/`. -5. Preserve current behavior when all three settings are absent and reject a - partial tuple before cloud or database side effects. - -## Non-goals - -- Updating a pre-existing Tool or remounting an already running Session. -- Making the bucket prefix, endpoint, local path, or read-only mode configurable. -- Replacing the worker's existing output mirroring behavior. - -## Design - -The flat YAML keys provide server-side defaults. The Studio creation page may -submit a complete override tuple. The server removes AK/SK from the recoverable -request payload and passes them only in memory through the creation child's -stdin; browser sessionStorage, task SQLite, and task responses do not retain or -return them. A newly -created Tool receives an access-key `TosMountConfig` with the canonical base -path `/sandbox-session/default/default` and local path `/data/output`. VeADK -sets `MPA_CODEX_WORKER_TOS_MOUNT_ENABLED=true` and the non-secret bucket name -on the Runtime. AK/SK never enter the Runtime. - -VeADK requires `agentkit-sdk-python>=0.8.5`, the first supported Studio SDK -contract that serializes the access-key `CredentialType`. Thin Studio updates -must therefore upgrade an older installed SDK instead of accepting it as an -already satisfied dependency. - -When that flag is enabled, mpa-agent delegates path derivation to AgentKit -SDK's `build_session_bucket_path` and attaches the result to -`CreateSessionRequest`. It does not call GetTool because the TIP Session client -does not support that operation. A missing bucket fails closed instead of -silently creating an unmounted Session. Existing Sessions remain unchanged. - -## Acceptance criteria - -- Complete TOS YAML produces the expected Tool mount configuration and Runtime - flag in both legacy and managed provisioning. -- Complete Studio TOS input overrides the newly created Worker, partial input is - rejected, and AK/SK never enter browser recovery, task SQLite, or responses. -- Partial TOS YAML fails locally without exposing secret values. -- Two Session IDs produce distinct bucket paths and the same `/data/output` - local path. -- Disabled configuration performs no extra GetTool request. -- A Studio update cannot retain an AgentKit SDK older than 0.8.5. -- Unit tests cover validation, payload construction, secret redaction, and - Session request behavior; changed executable lines remain above 95% coverage. - -## Review record - -The design was checked directly because the repository's optional -`review-spec` skill is unavailable. The contract keeps credentials at the Tool -boundary, uses the SDK's canonical per-session path helper, and introduces no -new public mount knobs. No blocking inconsistency was found. diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-mpa-tos-output-mount.zh.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-mpa-tos-output-mount.zh.md deleted file mode 100644 index 09939d006..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-mpa-tos-output-mount.zh.md +++ /dev/null @@ -1,66 +0,0 @@ -# MPA 会话级 TOS 输出挂载 - -- 状态:已批准 -- 日期:2026-09-23 -- 负责人:VeADK MPA provisioning -- 批准记录:需求方于 2026-09-23 确认进入开发 - -## 问题 - -VeADK 创建 MPA Runtime 及专属 Codex worker Tool 时,目前没有为 Tool 和 -Session 配置 TOS 输出挂载,因此 `/data/output` 没有按会话隔离的 TOS 目录。 - -## 目标 - -1. 现有私有 MPA YAML 接受 `tos-access-key`、`tos-secret-key`、`tos-bucket`, - 同时覆盖 `mpa create` 与托管 `mpa provision`;Studio 创建页也可为单次创建 - 传入完整三元组并覆盖 YAML 默认值。 -2. AK/SK 只进入 Tool 的 `TosMountConfig`,不得进入 Runtime 环境变量、dry-run - 输出、日志或部署注册表。 -3. TOS 以读写方式挂载到 `/data/output`,Endpoint 根据 Tool 地域推导。 -4. Runtime 在每次新建 Session 时生成 `CreateSessionRequest.TosMountPoints`; - 使用 SDK 标准路径 `/sandbox-session/tool-{tool_id}/session-{session_id}/`。 -5. 三个字段都不传时保持原行为;只传一部分时必须在任何云端或数据库副作用前失败。 - -## 非目标 - -- 更新已存在的 Tool,或给已经运行的 Session 重新挂载。 -- 将 bucket 前缀、Endpoint、本地路径或只读模式开放成配置项。 -- 替换 Worker 现有的输出镜像逻辑。 - -## 设计 - -顶层 YAML 三个字段提供服务端默认值。Studio 创建页可选输入完整三元组,提交后从 -可恢复请求 payload 中剥离 AK/SK,仅通过创建子进程 stdin 在内存中覆盖本次 worker -options;浏览器 sessionStorage、任务 SQLite 和任务查询响应均不得保存或返回 AK/SK。 -新 Tool 使用 access-key 类型的 -`TosMountConfig`,基础路径固定为 `/sandbox-session/default/default`,本地路径 -固定为 `/data/output`。VeADK 只向 Runtime 注入 -`MPA_CODEX_WORKER_TOS_MOUNT_ENABLED=true` 和非敏感 bucket 名,不注入 TOS -AK/SK。 - -VeADK 要求 `agentkit-sdk-python>=0.8.5`;这是 Studio 支持的首个会序列化 -access-key `CredentialType` 的 SDK 契约。Studio 的轻量增量更新必须升级更旧的 -已安装 SDK,不能把旧版本视为已满足依赖。 - -启用后,mpa-agent 复用 AgentKit SDK 的 `build_session_bucket_path` 生成会话 -目录,并写入 `CreateSessionRequest`。TIP Session 客户端不支持 GetTool,因此这里不 -读取 Tool;bucket 缺失时关闭式失败,不能静默创建未挂载 Session。已有 Session -不变,需要重建后才能获得挂载。 - -## 验收标准 - -- 完整 TOS YAML 在旧版和托管编排中都生成正确的 Tool 挂载配置及 Runtime 开关。 -- Studio 完整 TOS 输入覆盖本次新建 Worker,部分输入被拒绝,AK/SK 不进入浏览器 - 恢复数据、任务 SQLite 或任务响应。 -- 不完整的 TOS YAML 在本地失败,且错误信息不泄露密钥。 -- 两个 Session ID 生成不同的 BucketPath,本地路径都为 `/data/output`。 -- 未启用时不增加 GetTool 请求。 -- Studio 更新后不能继续保留低于 0.8.5 的 AgentKit SDK。 -- 单测覆盖校验、请求构造、密钥脱敏与 Session 请求;增量可执行代码覆盖率高于 95%。 - -## 评审记录 - -仓库可选的 `review-spec` skill 当前不可用,因此按相同检查项直接评审。方案将凭证 -限制在 Tool 边界,复用 SDK 标准会话路径 helper,且没有增加额外挂载参数;未发现 -阻塞性不一致。 diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-builtin-mpa-profile.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-builtin-mpa-profile.md deleted file mode 100644 index 4100243b1..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-builtin-mpa-profile.md +++ /dev/null @@ -1,52 +0,0 @@ -# Built-in Studio MPA creation profile - -[中文版](2026-09-23-studio-builtin-mpa-profile.zh.md) - -- Change ID: `studio-builtin-mpa-profile` -- Date: 2026-09-23 -- Status: approved by the user's request to remove the Studio YAML dependency -- Component: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md) - -## Background and evidence - -Studio currently reads `VEADK_MPA_CREATE_CONFIG`, defaulting to `mpa-create.config.yaml`, for both configuration inspection and task submission. The child process reads the same file again. A missing file prevents creation before cloud checks. The current private Beijing profile contains nonsecret account/resource/image/model defaults and an environment reference for the model API key. Deployment credentials already come from the server environment or credential service. - -## Goals, non-goals and scenarios - -Studio creation must work without a YAML file. Its existing Beijing account, VPC/subnet, APIG, Runtime/worker images, model and PostgreSQL auto-provisioning defaults remain effective. Secret values remain outside source. The CLI `--config` YAML workflow, existing task records, existing agents and other regions are outside the behavioral change. No live resource creation is part of this change. - -- Given no YAML and a valid model-key environment variable and deployment credentials, configuration inspection returns the normal safe summary and creation starts with the built-in profile. -- Given a stale `VEADK_MPA_CREATE_CONFIG`, Studio ignores it; the CLI still reads an explicitly supplied YAML path. -- Given a missing model key or deployment credentials, Studio reports a safe configuration error before cloud writes. -- Given a task starts, the fixed child uses the same built-in profile without reading a YAML path; retries preserve the original agent ID and inputs. - -## Requirements and design - -- **FR-1:** Add a code-owned, Beijing-only Studio profile matching the current nonsecret private YAML defaults. Keep fixed account/resource IDs, images and Runtime environment settings in this profile. Continue resolving `VEADK_MPA_CONFIG_MODEL_AGENT_API_KEY` at runtime; never embed an API key, PG password, access key or session token. -- **FR-2:** Studio inspection and submission select the built-in profile regardless of `VEADK_MPA_CREATE_CONFIG`. Validate it with the same managed-profile rules and Studio Identity reuse logic as YAML profiles. -- **FR-3:** The task supervisor passes an explicit built-in-profile marker to the fixed child; the child resolves that profile with the requested region. Preserve YAML path handling for CLI and existing task APIs. -- **FR-4:** Keep task payloads, responses and logs free of model/deployment secrets. Unsupported regions and missing environment credentials fail before cloud writes with safe messages. - -The profile remains intentionally scoped to the current Beijing deployment. Moving to another account, region or image requires a code change and review. Studio deployments must receive the required model-key and cloud-credential environment variables; the YAML path is no longer needed. The trusted boundary remains the Studio server and fixed child. No new browser input or persisted task field is introduced. - -Affected files: managed profile loader and built-in defaults, Studio creation route, task-to-child transport, child runner, paired component contracts, bilingual operator docs and focused tests. CLI profile parsing remains supported. - -## Tasks and tests - -1. Add failing tests for built-in inspection without YAML, stale YAML path, matching defaults, missing secret, unsupported region, and child profile selection. -2. Add the built-in profile through the existing validator and switch only Studio to it. -3. Update both component-spec languages and operator docs; run managed creation and frontend tests, build/assets, Ruff, Pyright where installed, and pre-commit checks. - -## Risks and acceptance - -Hard-coded resource IDs can become stale and the pinned images can become obsolete; errors must remain explicit rather than silently selecting another account's resources. Existing task retries must not change their deployment identity. Acceptance requires Studio creation to have no YAML file read, the CLI YAML path to remain functional, no secret literal in source, and a child process that selects the same built-in settings. Tests must use simulated providers; live deployment is separate. - -## Review and delivery record - -Design review: the built-in profile passes through the existing validator, avoids a second configuration parser, keeps only nonsecret constants in source, and distinguishes Studio's file-free path from the CLI's explicit-file path. The user's request authorizes this scope. - -Verification on 2026-09-23 against the uncommitted working-tree diff: - -- **pass:** Managed creation and CLI tests (`394 passed`); frontend tests (`1305` Node and `36` Vitest passed); frontend build and web asset check; pre-commit Ruff and secret checks; built-in and existing private YAML profiles compare equal after validation, without printing secrets. -- **fail (unrelated optional dependency):** Broad parallel Python regression reached `5096 passed, 11 skipped, 2 xfailed` before `tests/cloud/test_harness_app_contract.py::TestHarnessConfig::test_spawn_applies_resource_overrides_to_clone_only` failed because `llama_index` is absent. Isolated rerun reproduced the missing dependency. Two runtime modules requiring absent `anthropic` were excluded from collection. -- **not_run:** Pyright, real-browser creation flow, and live cloud creation. The local Studio was not listening on port 8000 during the final check. Deployment or server restart is required before the new behavior can be observed. diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-builtin-mpa-profile.zh.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-builtin-mpa-profile.zh.md deleted file mode 100644 index 4edcb3a34..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-builtin-mpa-profile.zh.md +++ /dev/null @@ -1,52 +0,0 @@ -# Studio 内置 MPA 创建配置 - -[English](2026-09-23-studio-builtin-mpa-profile.md) - -- Change ID: `studio-builtin-mpa-profile` -- Date: 2026-09-23 -- Status: 用户要求移除 Studio YAML 依赖,已批准该范围 -- Component: [Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md) - -## 背景与证据 - -Studio 当前在配置检查和任务提交时读取 `VEADK_MPA_CREATE_CONFIG`,默认路径为 `mpa-create.config.yaml`;子进程再次读取同一文件。文件缺失会在云资源检查前阻止创建。现有北京地域私有配置包含非密钥的账号、资源、镜像和模型默认值,以及模型 API Key 的环境变量引用。部署凭据已从服务端环境或凭据服务取得。 - -## 目标、非目标与场景 - -Studio 创建流程无需 YAML 文件即可运行。保留现有北京地域账号、VPC/子网、APIG、Runtime/worker 镜像、模型和 PostgreSQL 自动准备默认值。密钥仍留在源码之外。CLI 的 `--config` YAML 流程、既有任务记录、现有智能体及其他地域不改变行为。本改动不执行真实资源创建。 - -- 没有 YAML,但模型密钥环境变量和部署凭据有效时,配置检查返回正常的安全摘要,创建任务使用内置配置。 -- `VEADK_MPA_CREATE_CONFIG` 指向失效路径时,Studio 忽略该变量;CLI 仍读取显式传入的 YAML 路径。 -- 模型密钥或部署凭据缺失时,Studio 在云写入前返回安全的配置错误。 -- 任务开始后,固定子进程不读取 YAML 路径,而是使用同一内置配置;重试保留原智能体 ID 和参数。 - -## 需求与设计 - -- **FR-1:**新增代码内置的北京地域 Studio 配置,匹配当前私有 YAML 中的非密钥默认值。固定账号/资源 ID、镜像和 Runtime 环境参数保留在内置配置中。运行时继续解析 `VEADK_MPA_CONFIG_MODEL_AGENT_API_KEY`;不得写入 API Key、PG 密码、访问密钥或会话令牌字面值。 -- **FR-2:**Studio 的检查和提交均选择内置配置,不受 `VEADK_MPA_CREATE_CONFIG` 影响。内置配置沿用现有托管配置校验及 Studio Identity 复用逻辑。 -- **FR-3:**任务管理器向固定子进程传递明确的内置配置标记;子进程按请求地域加载它。CLI 和既有任务 API 的 YAML 路径处理保持兼容。 -- **FR-4:**任务参数、响应与日志不含模型或部署密钥。不支持的地域或环境凭据缺失时,在云写入前返回安全错误。 - -内置配置有意限定于当前北京地域部署。更换账号、地域或镜像需要修改代码并审查。Studio 部署仍须提供模型密钥及云凭据环境变量,不再需要 YAML 路径。信任边界仍是 Studio 服务端及固定子进程;不增加浏览器输入或持久化任务字段。 - -涉及文件:托管配置加载与内置默认值、Studio 创建路由、任务到子进程传递、子进程入口、双语组件契约、双语运维文档及定向测试。CLI 配置解析继续支持 YAML。 - -## 任务与测试 - -1. 先添加失败测试,覆盖无 YAML 的内置配置检查、失效 YAML 路径、默认值一致性、缺少密钥、不支持地域及子进程配置选择。 -2. 通过现有校验器加入内置配置,并仅将 Studio 切换到该配置。 -3. 更新双语组件契约及运维文档;运行托管创建和前端测试、构建/资源检查、Ruff、环境允许时的 Pyright 及 pre-commit 检查。 - -## 风险与验收 - -写死的资源 ID 可能失效,固定镜像也可能过时;错误须明确,不能静默选择其他账号的资源。已有任务重试不得改变部署身份。验收要求 Studio 创建不读取 YAML,CLI YAML 路径继续可用,源码没有密钥字面值,子进程使用相同的内置配置。测试应模拟云服务;真实部署另行验证。 - -## 审查与交付记录 - -设计审查:内置配置经过现有校验器,不增加第二套解析器;源码只含非密钥常量,Studio 无文件路径与 CLI 显式文件路径边界清晰。用户请求已授权该范围。 - -2026-09-23 对未提交工作区差异的验证记录: - -- **pass:**托管创建与 CLI 测试(`394 passed`);前端测试(`1305` 项 Node 和 `36` 项 Vitest 通过);前端构建与 Web 资源检查;pre-commit Ruff 和密钥检查;内置配置与原私有 YAML 经校验后的内容一致,比较过程未输出密钥。 -- **fail(无关的可选依赖):**并行 Python 回归达到 `5096 passed, 11 skipped, 2 xfailed` 后,`tests/cloud/test_harness_app_contract.py::TestHarnessConfig::test_spawn_applies_resource_overrides_to_clone_only` 因缺少 `llama_index` 失败;单独重跑复现了缺依赖。收集时排除了两个需要缺失的 `anthropic` 依赖的 Runtime 模块。 -- **not_run:**Pyright、真实浏览器创建流程和真实云资源创建。最终检查时本地 Studio 未监听 8000 端口;需要部署或重启服务才能观察到新行为。 diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-openviking-key.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-openviking-key.md deleted file mode 100644 index 04899b8d3..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-openviking-key.md +++ /dev/null @@ -1,42 +0,0 @@ -# Studio MPA OpenViking credentials - -[中文版](2026-09-23-studio-mpa-openviking-key.zh.md) - -- Change ID: `studio-mpa-openviking-key` -- Date: 2026-09-23 -- Component: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), CON-11 - -## Background and evidence - -The prior ArkClaw flow injects `OPENVIKING_USER=default` along with URL, API Key and resource ID. The current managed Studio profile has no user setting or API Key, while the wizard claims the deployment service always supplies one. Creation tasks persist their nonsecret request payload in SQLite and return it in task snapshots, so adding the key to that payload would expose it. - -## Goals, non-goals and scenarios - -Add a masked API Key input to the OpenViking step. Entering URL, resource ID and key together injects the four ArkClaw variables into the new Runtime. Leaving all three blank injects none and removes any values inherited from a template or reference Runtime. The key must reach the child provisioning process without entering browser storage, task SQLite, status responses, logs or error details. Creating an OpenViking resource, changing existing agents or changing the legacy CLI are outside scope. - -## Requirements and design - -- **FR-1:** The OpenViking step displays an optional password-type `openvikingApiKey` field (maximum 512 characters) with accurate guidance. It stays in component memory and is not included in session-storage drafts. Failed retries can re-enter it; after a browser restart the user must re-enter it. -- **FR-2:** Authorized POST accepts the key, removes it before nonsecret task persistence, and passes it separately to the fixed child through stdin. Task snapshots and the SQLite file never include it. Unknown sensitive fields are rejected. -- **FR-3:** The three inputs are all required together or all blank. When filled, the runner injects `OPENVIKING_URL`, `OPENVIKING_RESOURCE_ID`, `OPENVIKING_API_KEY` and `OPENVIKING_USER=default`. When blank, it removes all four from the new Runtime, including inherited values. -- **FR-4:** Preserve existing request identity, retry and cancellation behavior. Do not return credentials from the configuration endpoint. - -Affected files: Studio creation UI/API/i18n, `frontend/server/mpa_creation.py`, managed task/configuration code, paired component contracts and operator docs. The trust boundary remains the authorized Studio server; the key is transient process data, including in the child Runtime deployment request. The task payload remains durable and nonsecret. A server restart cannot recover an unsent key, so the user supplies it again on retry. - -## Tasks, tests and acceptance - -1. Add failing frontend, route, task-store and Runtime-env tests for the key path and non-persistence. -2. Implement the masked field, separated secret transport, Runtime override and default user setting. -3. Update both contract languages and user/operator docs; run targeted Python tests, frontend tests/build, Ruff, Pyright, asset checks and browser validation. - -Acceptance: a submitted test key reaches the child and Runtime env; the browser draft, task response and SQLite do not contain it; all-blank input omits all four variables even when inherited; partial input is rejected; the user is `default` when enabled. No live OV/Runtime operation is necessary for unit validation. - -## Review and verification - -Design review checked the API boundary, secret lifetime, retry after restart, bilingual fields, compatibility and cancellation. The existing task store cannot hold a key securely; this design explicitly keeps it outside the durable payload. On 2026-09-23, targeted managed-creation tests passed (348 before the final no-override regression), frontend tests passed (1,305 Node and 36 Vitest tests), TypeScript and Pyright passed, the production build and packaged-asset check passed, and `git diff --check` passed. The full Ruff check reports pre-existing import/line-length findings in the touched modules; the E/F check excluding pre-existing E501 passed. Browser inspection reached the local login page but did not proceed through its terms acceptance; live OV/Runtime provisioning was not run. - -The all-or-none follow-up on 2026-09-23 passed 363 managed-creation tests, 1,305 Node and 36 Vitest frontend tests, TypeScript, Pyright, Ruff E/F excluding pre-existing E501, the production build, the packaged-asset check and `git diff --check`. Runtime-env tests cover blank and complete input across template, reference and flat sources. Live OV/Runtime provisioning was not run. - -The broader parallel Python regression was not completed: an initial run showed failures whose causes remain unverified and was stopped for diagnosis; a fail-fast rerun stopped during collection because the optional self-host sandbox example requires the unavailable `anthropic` package. This does not change the passing managed-creation result. - -After rebasing onto `17f392f7` on 2026-09-23, the final working-tree diff passed 371 managed-creation tests, 1,305 Node tests, 36 Vitest tests, the production frontend build, packaged-asset validation, pre-commit Ruff/format/secret checks and `git diff --check`. The default two-worker Python regression stopped during collection because the optional self-host sandbox example imports the unavailable `anthropic` package. Pyright was not run in this check because the command is not installed in the current environment; an earlier run before the rebase passed. diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-openviking-key.zh.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-openviking-key.zh.md deleted file mode 100644 index a2ba72b0c..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-openviking-key.zh.md +++ /dev/null @@ -1,42 +0,0 @@ -# Studio MPA OpenViking 凭据 - -[English](2026-09-23-studio-mpa-openviking-key.md) - -- 变更 ID:`studio-mpa-openviking-key` -- 日期:2026-09-23 -- 组件:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-11 - -## 背景与证据 - -旧 ArkClaw 流程会同时注入 `OPENVIKING_USER=default`、地址、API Key 和资源 ID。当前 Studio 托管配置没有用户设置或 API Key,但向导声称部署服务一定提供密钥。创建任务会把非密钥请求参数持久化到 SQLite 并通过任务快照返回;直接把密钥加入该参数会泄漏。 - -## 目标、非目标与场景 - -在 OpenViking 步骤增加遮罩的 API Key 输入框。地址、资源 ID 和密钥三项同时填写时,向新 Runtime 注入与旧 ArkClaw 相同的四个变量;三项全空时不注入,并清除模板或参考 Runtime 继承的旧值。密钥须传至子进程,不得进入浏览器存储、任务 SQLite、状态响应、日志或错误详情。本次不创建 OpenViking 资源,不改已有智能体或旧 CLI。 - -## 需求与设计 - -- **FR-1:**OpenViking 步骤显示可选的密码型 `openvikingApiKey` 字段(最多 512 字符)及准确说明。密钥只保留在组件内存,不进入会话草稿。失败重试可重新输入;浏览器重启后必须重输。 -- **FR-2:**鉴权 POST 接收密钥,在持久化非密钥任务参数前将其移除,并单独通过标准输入传给固定子进程。任务快照和 SQLite 均不包含密钥;未知敏感字段会被拒绝。 -- **FR-3:**三项输入必须全部填写或全部留空。填写时,runner 注入 `OPENVIKING_URL`、`OPENVIKING_RESOURCE_ID`、`OPENVIKING_API_KEY` 和 `OPENVIKING_USER=default`。全空时,新 Runtime 清除全部四项,包括继承值。 -- **FR-4:**保持原有请求身份、重试和取消行为。配置接口不返回凭据。 - -涉及文件:Studio 创建 UI/API/国际化、`frontend/server/mpa_creation.py`、托管任务与配置代码、双语组件契约和运维文档。信任边界仍是鉴权 Studio 服务端;密钥仅为临时进程数据,包括子进程发起的 Runtime 部署请求。任务参数持续保留但不含密钥。服务端重启无法恢复未发送的密钥,用户重试时重新提供。 - -## 任务、测试与验收 - -1. 先添加前端、路由、任务存储和 Runtime 环境变量的失败测试,覆盖密钥传递与不落盘。 -2. 实现遮罩输入、单独传递密钥、Runtime 覆盖及默认用户设置。 -3. 更新双语契约和使用文档;运行定向 Python 测试、前端测试/构建、Ruff、Pyright、资源检查及浏览器验证。 - -验收:测试密钥到达子进程和 Runtime 环境;浏览器草稿、任务响应及 SQLite 不包含它;三项全空时即使有继承值也不注入四项;部分填写会被拒绝;启用时用户值为 `default`。单元验证不需要真实 OV/Runtime 云操作。 - -## 审查与验证 - -设计审查覆盖 API 边界、密钥生命周期、重启后重试、双语字段、兼容性和取消。现有任务库不能安全保存密钥,因此设计明确将其排除在持久化参数之外。2026-09-23,定向托管创建测试通过(最终新增的留空回归测试前为 348 项),前端测试通过(1,305 项 Node 与 36 项 Vitest)、TypeScript 与 Pyright 通过、生产构建和打包资源检查通过,`git diff --check` 通过。完整 Ruff 检查报告所触及模块原有的导入顺序/行长问题;忽略原有 E501 的 E/F 检查通过。浏览器检查到达本地登录页,但未继续接受其服务条款;未执行真实 OV/Runtime 云端创建。 - -2026-09-23 的全填或全空补充修改通过 363 项托管创建测试、1,305 项 Node 和 36 项 Vitest 前端测试、TypeScript、Pyright、忽略原有 E501 的 Ruff E/F、生产构建、打包资源检查及 `git diff --check`。Runtime 环境变量测试覆盖模板、参考 Runtime 和平铺配置下的全空及完整输入。未执行真实 OV/Runtime 云端创建。 - -更广范围的并行 Python 回归未完成:首次运行出现失败,原因尚未核实,随后停止以定位问题;快速失败的重跑在收集测试时因自托管沙箱示例缺少可选 `anthropic` 包而停止。这不影响托管创建定向测试的通过结果。 - -2026-09-23 基于 `17f392f7` rebase 后,最终工作区改动通过 371 项托管创建测试、1,305 项 Node 测试、36 项 Vitest 测试、前端生产构建、打包资源检查、pre-commit Ruff/格式/密钥检查及 `git diff --check`。默认双进程 Python 回归在收集阶段因可选自托管沙箱示例导入了当前环境缺失的 `anthropic` 包而停止。本轮未运行 Pyright,因为当前环境没有安装该命令;rebase 前的较早检查曾通过。 diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-three-step-creation.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-three-step-creation.md deleted file mode 100644 index cc1ea90ea..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-three-step-creation.md +++ /dev/null @@ -1,55 +0,0 @@ -# Three-step Studio MPA creation - -[中文版](2026-09-23-studio-mpa-three-step-creation.zh.md) - -- Change ID: `studio-mpa-three-step-creation` -- Created/revised: 2026-09-23 -- Status: implemented and verified locally; uncommitted -- Component: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md) - -## Background and evidence - -`MpaCreateDialog` currently puts the generated agent ID, images, resource plan and submit button on one screen. The ID is editable. Managed creation uses an existing PostgreSQL instance, with administrator and registry credentials resolved from server environment variables. It creates a distinct business database, but no PostgreSQL cloud instance. OpenViking has no per-creation input in the managed Studio flow, although the MPA Runtime accepts `OPENVIKING_URL` and `OPENVIKING_RESOURCE_ID`. - -## Goals and non-goals - -Make the ID read-only and arrange creation into three steps: agent basics, existing PostgreSQL instance settings, and OpenViking settings. Add the user-supplied Volcengine console links. Ensure submitted nonsecret settings reach the Runtime and task retry path. Preserve server-owned credentials and authorization. Cloud instance creation, browser-held passwords/API keys, unrelated CLI changes, and changes to existing agents are outside scope. - -## Scenarios and requirements - -- **FR-1:** A newly opened dialog retains the existing `mi-[0-9a-f]{24}` ID shape. The user can select/copy but cannot edit it; reopen and retry keep the same ID. An unsubmitted twelve-character draft from the brief regression is expanded using its unchanged request UUID; submitted identities stay unchanged for retry. Flat managed provisioning accepts the Studio ID in addition to the CLI's existing twelve-character ID. -- **FR-2:** The first step shows region, ID, description, images and resource plan. Next opens PostgreSQL, Back returns without submission, Next opens OpenViking, and only the third step starts creation. Keyboard, IME, narrow viewport, invalid values and missing server configuration are handled. -- **FR-3:** PostgreSQL step links to the supplied RDS list and accepts a host and port for an existing instance. Defaults come from the authorized server profile. The backend rejects a host/port that differs from its administrator connection, before cloud writes; database credentials stay server-side. -- **FR-4:** OpenViking step links to the supplied context-management page and accepts an optional HTTPS service URL and resource ID. The selected values are applied to the new Runtime; API keys continue to come from the server profile/reference. No secret is put in browser storage or task responses. -- **FR-5:** The task persists the nonsecret choices with the request ID; a lost response, retry or reopen uses the same values. Existing tasks and clients without the new fields retain their behavior. - -## Design and contract impact - -Extend `GET /web/mpa-creation/config` with nonsecret PostgreSQL host/port defaults. Extend `POST /web/mpa-creation/tasks` with optional `pgHost`, `pgPort`, `openvikingUrl`, `openvikingResourceId`. Validate URL syntax, port bounds, ID length and PostgreSQL administrator target at the server boundary. The task's existing payload persistence carries these nonsecret fields to the child runner. The runner applies them to a copied profile's Runtime environment. `AgentDatabaseProvisioner` still verifies administrator/Runtime host alignment. Empty OpenViking fields keep inherited Runtime settings. Changes to a pending task's choices remain a conflict. The generated ID is still accepted by the API for CLI/backward compatibility. - -Permissions remain the existing agent-management permission; account and region remain server-verified. No new dependency, database schema, secret storage, or provider API call is needed. A provider console link does not grant permission or prove reachability. The supplied OpenViking link is an example resource path, not the service endpoint. - -## Implementation tasks - -- **T-1:** Add failing wizard/ID/link/validation tests and backend override/security tests (`FR-1`–`FR-5`). -- **T-2:** Add localized three-step UI and small responsive styles (`FR-1`–`FR-4`). -- **T-3:** Extend the typed API, backend validation, task payload, and runner profile override (`FR-3`–`FR-5`). -- **T-4:** Update both component specs and operator docs; run the affected tests, type/style checks, build and browser checks (`FR-1`–`FR-5`). - -## Acceptance and verification - -- **AC-1:** The ID cannot be changed in the form; Back/Next preserve edits and do not send POST. -- **AC-2:** The two console links open the requested destinations. Submit occurs only on step three after validation. -- **AC-3:** PostgreSQL mismatch and malformed OpenViking values fail before provisioning; valid choices reach the Runtime environment without changing secrets. -- **AC-4:** Repeated requests with the same identity and choices reuse the task; changed choices conflict. Existing payloads still work. -- **AC-5:** Frontend tests/build, targeted Python tests, changed-file Ruff/Pyright, bilingual checks and a real browser pass. Do not claim live cloud deployment from simulated tests. - -## Risks, review and delivery record - -The existing profile must already point to the chosen PostgreSQL instance and supply a usable login and administrator connection. The wizard cannot establish private network access or create the instance. Direct review found that passing credentials through the current task SQLite payload would violate the existing security contract, so this design keeps them server-owned. The existing OpenViking API key remains configured on the server; choosing a URL/resource ID alone does not create or authorize an OpenViking resource. - -On 2026-09-23, direct design and implementation review confirmed the POST rejects mismatched PostgreSQL targets and malformed OpenViking settings before task creation. A definite HTTP 400/422 rejection unlocks the form for correction; an uncertain network failure retains the submitted request identity and choices for idempotent retry. Existing clients omit the new fields and retain the previous provisioning path. The two translations and component contracts were checked for matching fields, limits, links and behavior. User review caught an unintended shortening of generated Studio IDs. The correction restores the previous 24-character suffix and adds compatible validation for the flat provisioning path; CLI generation stays at 12 characters. - -Verification on the current uncommitted diff: `npm --prefix frontend test` **pass** (1,305 Node tests and 30 Vitest tests); `.venv/bin/pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q` **pass** (284 tests); Ruff check/format of changed Python files **pass**; Pyright with `.venv/bin/python` **pass** (0 errors); TypeScript `tsc --noEmit` **pass**; `npm --prefix frontend run build` **pass**; `npm --prefix frontend run test:webui-assets` **pass** (104 files, 248 references); `git diff --check` and bilingual identifier/pair checks **pass**; diff-based Gitleaks scan **pass**. A local browser preview **pass** for all three steps, both console links, read-only ID, narrow width and invalid OpenViking input; it used mocked configuration and did not call cloud provisioning. Live cloud deployment is **not_run** because it would create provider resources. `uv run --extra dev` was **blocked** by the sandbox's inability to write the global uv cache; equivalent targeted tests and checks used the existing repository environment and cached tools instead. Pre-commit `--all-files` is **not_run** because no commit is requested. - -After restoring the original Studio ID length, focused dialog tests **pass** (15 tests, including safe draft migration and submitted-ID preservation), `.venv/bin/pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/cli/test_cli_mpa.py -q` **pass** (298 tests), changed Python Ruff check/format **pass**, TypeScript check **pass**, rebuild **pass**, packaged assets **pass** (104 files and 248 references), bilingual ID contract check **pass**, and `git diff --check` **pass**. The broad frontend test result above predates this ID correction; the focused dialog suite covers it. Live provisioning remains **not_run**. diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-three-step-creation.zh.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-three-step-creation.zh.md deleted file mode 100644 index ba7adc19b..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-three-step-creation.zh.md +++ /dev/null @@ -1,55 +0,0 @@ -# Studio MPA 三步创建 - -[English](2026-09-23-studio-mpa-three-step-creation.md) - -- Change ID: `studio-mpa-three-step-creation` -- 创建/修订日期:2026-09-23 -- 状态:已在本地实施并验证;尚未提交 -- 组件:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md) - -## 背景与证据 - -`MpaCreateDialog` 目前把生成的智能体 ID、镜像、资源计划和提交按钮放在同一页,ID 可以编辑。托管创建使用已有 PostgreSQL 实例,管理员和共享登记库凭据从服务端环境变量读取;它为智能体创建独立业务库,但不创建 PostgreSQL 云实例。托管 Studio 流程尚无单次创建的 OpenViking 输入,尽管 MPA Runtime 支持 `OPENVIKING_URL` 和 `OPENVIKING_RESOURCE_ID`。 - -## 目标与非目标 - -将 ID 设为只读,创建分为智能体基础信息、已有 PostgreSQL 实例设置、OpenViking 设置三步,并加入用户提供的火山引擎控制台链接。确保提交的非密钥配置进入 Runtime 和任务重试流程,保留服务端管理的凭据与授权。创建云 PG 实例、浏览器保存密码/API Key、修改无关 CLI 或已有智能体均不在范围内。 - -## 场景与需求 - -- **FR-1:** 新打开弹窗时保持原有的 `mi-[0-9a-f]{24}` ID 格式。用户可以选择和复制,但不能编辑;重新打开和重试保留同一 ID。短暂回归期间形成的未提交 12 位草稿会依据不变的请求 UUID 扩展为 24 位;已提交的 ID 保持原样以便重试。托管平铺创建除 CLI 原有的 12 位 ID 外,也接受这个 Studio ID。 -- **FR-2:** 第一步展示地域、ID、描述、镜像和资源计划;下一步进入 PostgreSQL,上一步返回且不提交,再下一步进入 OpenViking;只有第三步能开始创建。处理键盘、输入法、窄屏、无效值和缺失的服务端配置。 -- **FR-3:** PostgreSQL 步骤链接到指定 RDS 列表,并允许填写已有实例的主机与端口。默认值来自授权后的服务端配置。后端在云资源写入前拒绝与管理员连接目标不一致的主机/端口;数据库凭据保留在服务端。 -- **FR-4:** OpenViking 步骤链接到指定的上下文管理页,允许填写可选的 HTTPS 服务地址与资源 ID。所选值应用到新 Runtime;API Key 继续来自服务端配置或参考 Runtime。浏览器缓存和任务响应均不保存密钥。 -- **FR-5:** 任务随请求 ID 持久化非密钥选项;响应丢失、重试或重新打开继续使用同一组值。没有新字段的既有任务和客户端保持原行为。 - -## 设计与契约影响 - -`GET /web/mpa-creation/config` 增加非密钥的 PostgreSQL 主机/端口默认值。`POST /web/mpa-creation/tasks` 增加可选 `pgHost`、`pgPort`、`openvikingUrl`、`openvikingResourceId`。服务端校验 URL 语法、端口范围、ID 长度和 PostgreSQL 管理员连接目标。现有任务 payload 持久化把非密钥字段交给子进程;子进程在复制的 profile 上覆盖 Runtime 环境变量。`AgentDatabaseProvisioner` 继续校验管理员与 Runtime 主机一致。OpenViking 字段为空时保留继承的 Runtime 设置。修改未完成任务的选项仍然冲突。API 仍接受智能体 ID,以兼容 CLI 和旧客户端。 - -权限继续使用智能体管理权限,账号和地域继续由服务端验证。不需要新依赖、数据库迁移、密钥存储或云厂商 API 调用。控制台链接不授予权限,也不证明网络可达。用户提供的 OpenViking 链接是示例资源页面,而不是服务接口地址。 - -## 实施任务 - -- **T-1:** 先加入三步、只读 ID、链接、校验及后端覆盖/安全的失败测试(`FR-1`–`FR-5`)。 -- **T-2:** 加入本地化的三步 UI 和少量响应式样式(`FR-1`–`FR-4`)。 -- **T-3:** 扩展类型化 API、后端校验、任务 payload 和 runner 的 profile 覆盖(`FR-3`–`FR-5`)。 -- **T-4:** 更新双语组件契约和操作文档;运行相关测试、类型/样式检查、构建和浏览器检查(`FR-1`–`FR-5`)。 - -## 验收与验证 - -- **AC-1:** 表单中的 ID 不能修改;上一步/下一步保留输入且不发送 POST。 -- **AC-2:** 两个控制台链接打开指定地址。完成校验后仅第三步可提交。 -- **AC-3:** PG 目标不一致及无效 OpenViking 值在部署前失败;有效值进入 Runtime 环境且不修改密钥。 -- **AC-4:** 相同身份与选项的重复请求复用任务;不同选项冲突。旧 payload 继续可用。 -- **AC-5:** 前端测试/构建、定向 Python 测试、改动文件的 Ruff/Pyright、双语检查及真实浏览器检查通过。模拟测试不等于真实云部署。 - -## 风险、审查与交付记录 - -现有 profile 仍须指向所选 PG 实例,并提供可用的登录及管理员连接。向导不能建立私网连通性或创建 PG 实例。直接设计审查确认:把凭据放入当前任务 SQLite payload 将违反现有安全契约,因此继续由服务端管理凭据。OpenViking API Key 仍在服务端配置;仅填写地址/资源 ID 不会创建资源或授予权限。 - -2026-09-23 的直接设计与实现审查确认:POST 在创建任务前拒绝 PG 目标不一致及无效 OpenViking 设置。明确的 HTTP 400/422 拒绝会解锁表单供修改;无法确定服务端结果的网络失败则保留已提交的请求身份与选项,以便幂等重试。旧客户端不传新字段时沿用原创建路径。已核对双语文档与组件契约的字段、长度限制、链接和行为一致。用户审查发现 Studio 生成的 ID 被意外缩短;修正后恢复原有的 24 位后缀,并让平铺创建路径兼容该格式;CLI 自动生成的 ID 仍为 12 位。 - -当前未提交改动的验证结果:`npm --prefix frontend test` **pass**(1,305 项 Node 测试及 30 项 Vitest 测试);`.venv/bin/pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q` **pass**(284 项);改动 Python 文件的 Ruff 检查/格式化 **pass**;使用 `.venv/bin/python` 的 Pyright **pass**(0 错误);TypeScript `tsc --noEmit` **pass**;`npm --prefix frontend run build` **pass**;`npm --prefix frontend run test:webui-assets` **pass**(104 个文件、248 条内部引用);`git diff --check` 与双语标识/成对文件检查 **pass**;基于 diff 的 Gitleaks 扫描 **pass**。本地浏览器预览对三步、两个控制台链接、只读 ID、窄屏和无效 OpenViking 输入的检查 **pass**;预览使用模拟配置,没有发起云端创建。真实云部署为 **not_run**,因为它会创建服务商资源。沙箱无法写全局 uv 缓存,`uv run --extra dev` 因此为 **blocked**;相应测试及检查改用仓库现有虚拟环境和缓存工具。由于没有提交请求,pre-commit 的 `--all-files` 为 **not_run**。 - -恢复 Studio 原有 ID 长度后,创建弹窗定向测试 **pass**(15 项,涵盖未提交草稿安全迁移和已提交 ID 保留),`.venv/bin/pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/cli/test_cli_mpa.py -q` **pass**(298 项),改动 Python 文件的 Ruff 检查/格式化 **pass**,TypeScript 检查 **pass**,重新构建 **pass**,打包资源 **pass**(104 个文件、248 条内部引用),双语 ID 契约检查 **pass**,`git diff --check` **pass**。前述完整前端测试在此次 ID 修正前运行;定向弹窗测试已覆盖该修正。真实部署仍为 **not_run**。 diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-10-09-named-studio-creation.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-10-09-named-studio-creation.md deleted file mode 100644 index 9750158e8..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-10-09-named-studio-creation.md +++ /dev/null @@ -1,83 +0,0 @@ -# Named Studio MPA creation - -[中文版](2026-10-09-named-studio-creation.zh.md) - -- Change ID: `named-studio-creation` -- Created/revised: 2026-10-09 -- Status: approved; approved by the user on 2026-10-09; implemented; local verification and deployment tracked below. -- Contracts: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), especially CON-1, CON-3, CON-5, CON-6, CON-9 and the HTTP body. - -## Background and scope - -The Studio dialog currently generates and displays an agent ID and submits editable Runtime/Worker image fields. `frontend/server/mpa_creation.py` requires that ID. The managed runner forwards it to provisioning, where `runtime.py` uses it as the new Runtime Name. The built-in `studio_profile.py` still points at older images. - -The requested change replaces these technical inputs with a required Name field, generates the stable agent ID on the server, and creates new agents with the published `mpa/mpa_agent:latest` and `mpa/mpa_codex_worker:latest` images in registry `agentkit-platform-2112682748-cn-beijing.cr.volces.com`. - -Existing agents, database identities and CLI configuration are outside this change. Updating the user-specified cloud Studio is authorized as a separate delivery step. Preserve the existing dialog layout, components and other resource steps. No new dependency, cloud operation, account permission or data migration is required to implement this change. - -## Requirements and scenarios - -- FR-1: Basics contains editable Name and Description, with neither agent ID nor image inputs. Name follows the existing Studio Runtime-name validator: 4–64 ASCII letters, digits, underscores or hyphens; surrounding whitespace is trimmed. Invalid names block forward navigation and submission and are rejected by the server before provisioning. -- FR-2: A fresh browser request contains `name` and `requestId`, without `agentId`, `runtimeImage` or `workerImage`. The server derives a stable `mi-` plus 24 hexadecimal-character ID from trusted owner and request UUID and persists it with the creation task. Different owners or requests have distinct identities; lost responses, duplicate submits, cancellation and retries retain the same identity. -- FR-3: The Name flows through task persistence and the fixed child-process protocol into the new Runtime's `Name`. `MPA_AGENT_ID`, worker bindings, registry keys and other resource identities continue using the generated ID. Existing registered Runtime names are preserved. -- FR-4: New Studio requests use the two server-owned `latest` image defaults. Browser draft restoration must not silently replay old image overrides on a fresh request. Existing submitted drafts/tasks retain their original payload and image selections for safe retry. -- FR-5: Preserve the existing API's legacy `agentId` and image fields for older clients/submitted drafts. Legacy requests without a Name retain the previous Runtime naming behavior. CLI/YAML image overrides remain available. Do not weaken ownership checks or pending-request mismatch detection. -- FR-6: Preserve cancellation, polling, retry, input locking, secret exclusion, localized text and keyboard/IME behavior. The New Agent action creates a fresh name draft and request UUID. No production secrets appear in tests or artifacts. - -Given a valid new form, submitting it creates one task whose Runtime Name is the entered name and whose generated ID is injected into both Agent and Worker. Given a lost POST response, retrying the same request does not create another identity. Given an old submitted draft, reopening and retrying it preserves the original operation. Given an invalid name, no cloud write starts. - -## Design and affected files - -1. Update `frontend/src/adk/mpaCreation.ts`, `frontend/src/ui/mpa-create/MpaCreateDialog.tsx` and localized strings. Reuse `frontend/src/create/runtimeName.ts`; retain the existing ModalLayout, Button and Textarea. New submissions explicitly omit removed fields, while restoring already-submitted legacy drafts preserves their request shape. -2. Extend `frontend/server/mpa_creation.py` with a validated Name and server-generated stable ID for the new request shape. Omit absent optional legacy fields rather than changing old persisted payload equality. -3. Carry the optional Runtime name through `managed/tasks.py`, `runner.py`, `service.py` and `runtime.py`. Name must participate in persisted request equality and pending Runtime request hashes. Preserve the existing name when reconciling an existing Runtime. -4. Update only the built-in Studio image defaults in `managed/studio_profile.py` to the published `/mpa/` images. Keep the shared CLI image-selection mechanisms. -5. Amend both component-spec languages, managed operator docs, frontend documentation and built web assets. The HTTP extension is backward compatible; no database schema change is needed. - -The alternative of using Name as agent ID is rejected: users' display names must not become ownership/database keys. Generating a new random ID on each retry is rejected because it breaks idempotency. Deterministic owner/request generation uses trusted server ownership, not a browser-supplied owner. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| FR-1, FR-6 | T-1: add regression tests before UI changes | AC-1: Name required; removed inputs absent; loading/error/IME/keyboard states usable | frontend MPA dialog tests and real browser against isolated fake APIs | -| FR-2, FR-5 | T-2: extend request handling and recovery | AC-2: stable owner/request identity; legacy retry unchanged; changed input conflicts | route/task tests in `tests/integrations/mpa_managed/` | -| FR-3 | T-3: propagate Runtime name | AC-3: create payload uses Name while all identity bindings use generated ID; existing Runtime names retained | runner/service/runtime tests | -| FR-4 | T-4: update built-in defaults | AC-4: both selected images end in `/mpa/:latest`; submitted tasks preserve selections | profile/image/route tests | -| All | T-5: reconcile docs and artifacts | AC-5: bilingual contracts align; assets match source; no credentials | test/build/i18n/assets, Ruff/Pyright, pre-commit and diff review | - -Commands: `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py`; `npm --prefix frontend test`; `npm --prefix frontend run build`; `npm --prefix frontend run check:i18n`; `npm --prefix frontend run test:webui-assets`; changed-file Ruff/Pyright; `uv run --extra dev pre-commit run --all-files`. Verify meaningful new executable lines exceed 95% incremental coverage. Broader regressions follow repository gates. Live cloud provisioning is not authorized by this code-change request and is reported separately from simulated checks. - -## Risks, review and delivery record - -- `latest` is mutable. Updating the registry tag affects later pulls, not existing running agents. A new creation can use a newer image than a prior creation; no automatic update of existing resources is introduced. -- Unsubmitted legacy drafts must be migrated without restoring hidden overrides. Submitted draft compatibility and duplicate-owner/request behavior need explicit tests. -- 2026-10-09 source review: confirmed UI ID generation, route requirement, persisted exact payload matching, fixed child protocol and Runtime Name override. Design approved before implementation; current verification is recorded below. -- `review-spec` is unavailable; perform the same contract/security/compatibility/testability/bilingual review directly. The repository's referenced UI skill paths are absent; available frontend-design guidance and Studio's own component/interaction rules govern this scoped form change. -- User requested implementation and the established fork/PR workflow. The user approved implementation, updating the supplied Studio deployment, and adding this change to PR #21 on the existing branch. - - -## Verification and release evidence (2026-10-09) - -Scope: the named-creation changes on `czh/fix-mpa-tos-copy`, based on `origin/main` `ef0ad51`; delivered with the existing TOS copy change in PR #21. No dependency or schema migration was introduced. - -- `pass`: targeted Python regression, 481 tests (`uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -n 2 -o addopts='' --cov=veadk.integrations.mpa.managed --cov=frontend.server.mpa_creation`). The coverage run must omit the repository's Pydantic warning-filter preload: with that filter pytest-cov produces duplicate Pydantic classes during collection; without coverage the standard MPA tests passed (457 tests before the last two cases). No tests were excluded to collect coverage. -- `pass`: `npm --prefix frontend test` — 1,377 Node tests and 52 component tests, including 35 creation-dialog cases. Cases cover required/invalid/trimmed names, removed fields, draft migration, legacy retries, late responses, cancellation and composition input. -- `pass`: changed executable-line coverage is 75/75 (100%): Python 27/27 and dialog 48/48. Type-only and non-executable call-argument lines are not separate coverage statements. Runner/child-protocol and Runtime identity assertions independently cover name propagation. -- `pass`: `npm --prefix frontend run build`, `check:i18n` (2 locales, 21 namespaces) and `test:webui-assets` (113 files, 350 internal references). -- `pass`: Ruff and Pyright for changed production Python files and the new regression test; pre-commit Ruff/format and secret scans. Additional Pyright on existing modified tests retains two pre-existing errors in `test_service.py` (confirmed against the pre-change file); none were introduced here. -- `pass`: local browser with isolated fake API responses — empty/invalid names block progress, valid name and description render without ID/image inputs, Tab and Enter navigation work, all three steps render, simulated 503 shows an error and locks submitted fields. At 390×844 the dialog remains usable without horizontal overflow. The captured POST contains Name/requestId and no Agent ID/image override. Composition input and retry/cancellation are additionally covered by automated tests; an OS IME session and real cloud creation were not run. -- `fail` (baseline/environment): full default Python regression excluding opt-in Codex/Pi smoke: 6,239 passed, 19 skipped, 2 xfailed, 7 failed, 2 collection errors. All seven failures and both errors reproduce on pre-change commit `ae39492`: six Harness cases need the optional `llama_index` dependency, two Sandbox modules need `anthropic`, and the artifact writer MIME assertion returns `application/octet-stream` instead of `text/markdown` on this host. The unchanged baseline subset had 108 passes and the same failures/errors. No unrelated dependency or MIME behavior was changed. -- `pass`: the authorized existing Studio code update was released as stable revision 37 (previously 36). The homepage returned HTTP 200 and references `/assets/app/index-BGwZgNyo.js`; that online file returned HTTP 200 and its SHA256 exactly matches the verified local build. All 44 existing environment values have the same aggregate hash before/after the release. The packaged changed backend files also matched local source byte-for-byte. VeADK wheel SHA256: `f1413695218969f8a34765cc1f7392ae44ffea9971ddba96c5c84ce03f12ada3`. No new MPA Runtime was created by this release. Credentials were kept outside the repository/package and removed after deployment verification. -- `not_run`: real cloud MPA provisioning and message delivery. Local tests/build/browser proof do not establish cloud adoption or Agent/Worker execution. - -Direct design/implementation review checked bilingual equivalence, owner-scoped deterministic identity, legacy request equality, hidden draft overrides, name propagation and existing-resource preservation. These local checks found no unresolved issue in the changed behavior. `latest` remains mutable; retry snapshots retain tag references, not immutable digests. - - -## Validation feedback follow-up (2026-10-09) - -The user reported that Next looked active but did nothing for a Chinese Runtime name, and approved a frontend correction. The existing ASCII name contract remains unchanged. The shared Button primitive lacks a base disabled appearance; add its existing 0.45 opacity/not-allowed cursor treatment to all variants, retaining loading overrides. The MPA form uses the existing danger token for the invalid name border/focus and error text, and adds a localized valid-name placeholder. No API, identity, image or resource behavior changes. Review confirmed component ownership, keyboard/native disabled semantics, theme-token reuse and bilingual equivalence. Verify a failing computed-style regression first, then valid/invalid names, all Button variants/loading, build and browser checks before updating the same Studio and PR. - -Follow-up local verification: `pass`. The regression reproduced the enabled-looking disabled Next button and passed after the CSS fix. All 1,377 Node tests and 54 component tests passed, including all six Button variants and loading opacity. The added JSX line is covered (1/1, 100%); CSS is checked through computed styles and browser rendering. Build, i18n, packaged-asset references, pre-commit and whitespace checks passed. Browser checks covered the Chinese-name report, danger border/text, disabled opacity/cursor, correction with Tab/Enter navigation, light/dark themes and 390px width. Backend code is unchanged; prior backend verification and baseline limitations still apply. - -Follow-up deployment: `pass` — stable revision 38, homepage HTTP 200, app JS and stylesheet bytes matching the verified build, and all existing environment values unchanged. Wheel SHA256: `0ff26dd247e26fedd96faa21fd62b96f6b86904fd0168f535db39de2047186da`. Temporary deployment credentials were removed after verification. No cloud MPA creation was performed. diff --git a/prd-spec/features/mpa-agent-oneclick-provision/2026-10-09-named-studio-creation.zh.md b/prd-spec/features/mpa-agent-oneclick-provision/2026-10-09-named-studio-creation.zh.md deleted file mode 100644 index 3cd364439..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/2026-10-09-named-studio-creation.zh.md +++ /dev/null @@ -1,83 +0,0 @@ -# Studio MPA 名称创建流程 - -[English](2026-10-09-named-studio-creation.md) - -- 变更 ID:`named-studio-creation` -- 创建/修订:2026-10-09 -- 状态:approved;用户于 2026-10-09 确认,已实现;本地验证和部署状态见下文。 -- 契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),重点涉及 CON-1、CON-3、CON-5、CON-6、CON-9 和 HTTP 请求体。 - -## 背景与范围 - -当前 Studio 弹窗生成并展示智能体 ID,提交可编辑的 Runtime/Worker 镜像字段。`frontend/server/mpa_creation.py` 要求提供 ID。托管 runner 把 ID 传给部署流程,`runtime.py` 用它作为新 Runtime 的 Name。内置 `studio_profile.py` 仍引用旧镜像。 - -本次按用户要求将这些技术输入替换为必填名称,由服务端生成稳定的智能体 ID,并使用镜像仓库 `agentkit-platform-2112682748-cn-beijing.cr.volces.com` 中已发布的 `mpa/mpa_agent:latest` 和 `mpa/mpa_codex_worker:latest` 创建新智能体。 - -现有智能体、数据库身份和 CLI 配置不在本次范围内。用户指定 Studio 的云端更新已获授权,作为独立交付步骤。复用弹窗布局、组件和其他资源步骤。实现本次变更不需要新增依赖、执行云端操作、变更账号权限或迁移数据。 - -## 需求与场景 - -- FR-1:基本信息显示可编辑的名称和描述,不显示智能体 ID 或镜像输入框。名称沿用 Studio Runtime 校验规则:4–64 个 ASCII 字母、数字、下划线或连字符,去除首尾空白。非法名称阻止向后导航和提交,服务端在部署前拒绝。 -- FR-2:新浏览器请求包含 `name` 和 `requestId`,不包含 `agentId`、`runtimeImage`、`workerImage`。服务端根据可信 owner 和请求 UUID 派生 `mi-` 加 24 位十六进制字符的稳定 ID,随创建任务保存。不同 owner 或请求得到不同身份;响应丢失、重复提交、取消后重试均保持身份不变。 -- FR-3:名称经过任务持久化和固定子进程协议,成为新 Runtime 的 `Name`。`MPA_AGENT_ID`、Worker 绑定、注册表键及其他资源身份仍使用生成的 ID。已注册 Runtime 保留原名称。 -- FR-4:新的 Studio 请求使用服务端的两个 `latest` 镜像默认值。恢复未提交草稿时,不得暗中回传旧镜像覆盖项。已经提交的草稿/任务保留原请求和镜像选择,以安全重试。 -- FR-5:为旧客户端和已提交草稿保留 API 的旧 `agentId` 及镜像字段。没有名称的旧请求保留原 Runtime 命名行为。CLI/YAML 镜像覆盖能力继续可用。不得放宽所有权检查或待完成请求的一致性检查。 -- FR-6:保留取消、轮询、重试、提交后锁定、密钥排除、本地化和键盘/IME 行为。“新建智能体”产生新的名称草稿和请求 UUID。测试和产物不得含生产密钥。 - -有效新表单提交后,只创建一个任务,Runtime Name 为输入名称,生成的 ID 同时注入 Agent 和 Worker。POST 响应丢失后重试同一请求,不创建新身份。旧的已提交草稿恢复后重试,保持原操作。名称无效时不启动任何云写入。 - -## 设计与影响文件 - -1. 修改 `frontend/src/adk/mpaCreation.ts`、`frontend/src/ui/mpa-create/MpaCreateDialog.tsx` 和本地化文字。复用 `frontend/src/create/runtimeName.ts` 以及现有 ModalLayout、Button、Textarea。新请求显式移除隐藏字段;恢复已提交的旧草稿时保留原请求形状。 -2. 在 `frontend/server/mpa_creation.py` 中增加名称校验,为新请求形状在服务端生成稳定 ID。省略未提供的旧可选字段,避免改变历史持久化请求的相等比较。 -3. 通过 `managed/tasks.py`、`runner.py`、`service.py` 和 `runtime.py` 传递可选 Runtime 名称。名称参与持久化请求比较和待完成 Runtime 请求哈希。调和已有 Runtime 时保留其名称。 -4. 仅将 `managed/studio_profile.py` 内置 Studio 镜像默认值改为已发布的 `/mpa/` 镜像,保留共享 CLI 镜像选择机制。 -5. 同步双语组件契约、托管操作文档、前端文档和构建产物。HTTP 扩展保持向后兼容,无需更改数据库结构。 - -不采用“名称即智能体 ID”,因为用户名称不能成为所有权和数据库键。不采用每次重试重新随机生成 ID,因为这会破坏幂等性。owner/request 派生使用服务端可信身份,不采信浏览器传入的 owner。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| FR-1、FR-6 | T-1:先增加回归测试,再改 UI | AC-1:名称必填,旧输入框消失,加载/错误/IME/键盘状态可用 | 前端 MPA 弹窗测试及隔离模拟 API 的真实浏览器检查 | -| FR-2、FR-5 | T-2:扩展请求处理和恢复 | AC-2:owner/request 身份稳定,旧重试不变,输入改变产生冲突 | `tests/integrations/mpa_managed/` 的路由/任务测试 | -| FR-3 | T-3:传递 Runtime 名称 | AC-3:创建参数使用名称,身份绑定使用生成 ID,保留已有 Runtime 名称 | runner/service/runtime 测试 | -| FR-4 | T-4:更新内置默认值 | AC-4:两个镜像均为 `/mpa/:latest`,已提交任务保留选择 | profile/image/route 测试 | -| 全部 | T-5:同步文档和产物 | AC-5:双语契约一致,产物对应源码,无凭据 | test/build/i18n/assets、Ruff/Pyright、pre-commit、diff 审查 | - -命令:`uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py`;`npm --prefix frontend test`;`npm --prefix frontend run build`;`npm --prefix frontend run check:i18n`;`npm --prefix frontend run test:webui-assets`;修改文件的 Ruff/Pyright;`uv run --extra dev pre-commit run --all-files`。有意义的新增可执行行增量覆盖率须超过 95%。按仓库门禁扩大回归。本次代码修改请求未授权真实云端创建,须与模拟检查分开报告。 - -## 风险、评审与交付记录 - -- `latest` 可变。更新仓库标签影响后续拉取,不更新现有运行中的智能体。不同时间的新创建可能使用不同镜像;本次不增加现有资源自动更新。 -- 未提交旧草稿迁移不能恢复隐藏镜像覆盖项。已提交草稿兼容性和同 owner/request 重复请求行为需要显式测试。 -- 2026-10-09 源码审查:已确认前端 ID 生成、路由必填字段、持久化精确请求比较、固定子进程协议和 Runtime Name 覆盖行为。设计在实现前已获批准;当前验证结果见下文。 -- `review-spec` 不可用,直接执行同等契约、安全、兼容、可测性和双语审查。仓库引用的 UI 技能路径缺失,采用可用 frontend-design 指南及 Studio 自身组件/交互规范约束这次表单变更。 -- 用户已要求实现并沿用 fork/PR 流程;用户已确认实现、更新指定 Studio 部署,并与 PR #21 使用同一分支。 - - -## 验证与发布证据(2026-10-09) - -范围:基于 `origin/main` `ef0ad51` 的 `czh/fix-mpa-tos-copy` 分支名称创建变更,与既有 TOS 文案修改一并交付到 PR #21。未新增依赖或数据结构迁移。 - -- `pass`:定向 Python 回归 481 项(`uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -n 2 -o addopts='' --cov=veadk.integrations.mpa.managed --cov=frontend.server.mpa_creation`)。覆盖率运行需移除仓库 Pydantic 警告过滤器的预加载,否则 pytest-cov 收集阶段会产生重复 Pydantic 类;不采集覆盖率时,标准 MPA 测试已通过(最后两个用例加入前为 457 项)。没有为采集覆盖率排除测试。 -- `pass`:`npm --prefix frontend test`,1,377 项 Node 测试和 52 项组件测试,其中创建弹窗 35 项。覆盖必填/非法/去空白名称、删除字段、草稿迁移、旧请求重试、迟到响应、取消和输入法组合事件。 -- `pass`:新增可执行行覆盖率 75/75(100%),Python 27/27、弹窗 48/48。纯类型和非独立可执行的调用参数行不单独作为覆盖率语句;runner/子进程协议及 Runtime 身份断言独立验证名称传递。 -- `pass`:`npm --prefix frontend run build`、`check:i18n`(2 种语言、21 个命名空间)、`test:webui-assets`(113 个文件、350 个内部引用)。 -- `pass`:修改的 Python 生产文件与新回归测试的 Ruff/Pyright,以及 pre-commit Ruff/格式化和密钥扫描。额外检查既有修改测试时,`test_service.py` 保留两个原有 Pyright 错误,已对照修改前文件确认;本次未引入新错误。 -- `pass`:使用隔离模拟 API 的本地真实浏览器检查:空/非法名称阻止继续,合法名称和描述显示正确且无 ID/镜像输入框,Tab/Enter 导航可用,三步均可展示,模拟 503 展示错误并锁定已提交字段。390×844 窄屏可用,无横向溢出。捕获的 POST 包含名称/requestId,不含智能体 ID 或镜像覆盖项。组合输入、重试/取消另由自动化测试覆盖;未执行操作系统输入法会话或真实云端创建。 -- `fail`(基线/环境):排除显式启用 Codex/Pi smoke 的默认全量 Python 回归,6,239 项通过、19 项跳过、2 项预期失败、7 项失败、2 项收集错误。全部七项失败和两项错误均在修改前 `ae39492` 复现:六项 Harness 用例缺少可选 `llama_index` 依赖,两项 Sandbox 模块缺少 `anthropic`,artifact writer 的 MIME 断言在本机得到 `application/octet-stream` 而非 `text/markdown`。未改动基线子集为 108 项通过及相同失败/错误。本次未修改无关依赖或 MIME 行为。 -- `pass`:已按授权更新既有 Studio 代码,稳定版本由 36 升为 37。首页返回 HTTP 200 并引用 `/assets/app/index-BGwZgNyo.js`;该线上文件返回 HTTP 200,SHA256 与已验证本地构建完全一致。既有 44 项环境变量发布前后的整体哈希一致。代码包内修改的后端文件也与本地源码逐字节一致。VeADK wheel SHA256:`f1413695218969f8a34765cc1f7392ae44ffea9971ddba96c5c84ce03f12ada3`。本次发布未创建新的 MPA Runtime。凭据未进入仓库或代码包,部署验证后删除临时副本。 -- `not_run`:真实云端 MPA 创建及消息投递。本地测试、构建和浏览器证据不代表云端已采用代码或 Agent/Worker 已实际执行。 - -直接设计/实现审查覆盖双语一致性、owner 隔离的稳定身份、旧请求精确比较、隐藏草稿覆盖项、名称传递和已有资源保留。修改行为未发现尚未解决的问题。`latest` 仍可变;重试保存标签引用,不锁定不可变 digest。 - - -## 校验反馈后续修复(2026-10-09) - -用户反馈输入含中文的 Runtime 名称时,“下一步”看起来可点但没有反应,并已确认修改前端。原 ASCII 名称契约保持不变。共享 Button 基础组件缺少通用禁用外观,复用已有 0.45 透明度和禁止光标,保留加载态覆盖;MPA 表单使用既有 danger 变量标记非法名称的边框/聚焦及错误文字,增加本地化合法名称示例占位。API、身份、镜像和资源行为不变。直接审查已确认组件职责、键盘和原生禁用语义、主题变量复用及双语一致。先验证计算样式回归失败,再检查合法/非法名称、所有按钮变体/加载态、构建和浏览器,随后更新同一 Studio 和 PR。 - -后续本地验证:`pass`。回归先复现下一步禁用却看似可点的问题,修复 CSS 后通过。1,377 项 Node 测试和 54 项组件测试全部通过,含六种 Button 变体及加载态透明度。新增 JSX 行覆盖率 1/1(100%);CSS 通过计算样式及浏览器渲染验证。构建、本地化、打包资源引用、pre-commit 和空白检查通过。浏览器覆盖中文名称、错误边框和文字、禁用透明度及光标、改正名称后的 Tab/Enter 导航、深浅主题及 390px 窄屏。后端代码未变,沿用前述后端验证及基线限制。 - -后续部署:`pass`,稳定版本为 38,首页 HTTP 200,线上应用 JS 和样式表与已验证构建逐字节一致,所有已有环境变量保持不变。Wheel SHA256:`0ff26dd247e26fedd96faa21fd62b96f6b86904fd0168f535db39de2047186da`。验证后删除临时部署凭据,未执行真实云端 MPA 创建。 diff --git a/prd-spec/features/mpa-agent-oneclick-provision/evidence/2026-09-11-code-review.zh.md b/prd-spec/features/mpa-agent-oneclick-provision/evidence/2026-09-11-code-review.zh.md deleted file mode 100644 index 7615471b7..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/evidence/2026-09-11-code-review.zh.md +++ /dev/null @@ -1,57 +0,0 @@ -# 两轮 Code Review 报告(dev-loop 步骤 8-9) - -> 日期:2026-09-11 -> 分支:`feat/mpa-agent-oneclick-provision` -> 范围:`veadk/cli/cli_mpa.py`、`veadk/integrations/mpa/*`、对应 tests 与双语设计/验证 Case。 - -## 1. 旧问题复核 - -| ID | 严重度 / 分类 | 位置 | 状态 | 依据 | -| --- | --- | --- | --- | --- | -| R1 Runtime 重试重复创建 | medium / Correctness | `mpa_runtime.py` | fixed | 精确同名 Runtime 复用;更新 image/Tool/env 后 `ReleaseEnable=True`,等待更新版本 Ready | -| R2 中英文 Spec 与证据漂移 | medium / Scope | `prd-spec/features/mpa-agent-oneclick-provision/` | fixed | 编排顺序、Case 结果、APIG/A2A/飞书阻断已同步 | -| R3 APIG 回退到任意 gateway | high / Correctness | `cli_mpa.py` | fixed | 仅允许显式 id、计算面 id 或 endpoint 唯一映射;共享网关无 id 时 fail-closed | - -## 2. 第一轮 Findings 与修复 - -| ID | 严重度 / 分类 | 证据与影响 | 处置 | -| --- | --- | --- | --- | -| F1 | medium / Correctness | Runtime 重跑缺少按名复用,失败重试可能创建多个计费资源 | 已补精确同名查找与复用测试 | -| F2 | high / Correctness | APIG endpoint 不包含客户 gateway id 时,枚举 fallback 会把无关 gateway 写入 `mpa_meta`,误导 IM routing | 移除 fallback;增加 `--apig-instance-id`;真实绑定缺字段时禁止 finalize | -| F3 | medium / Scope | 双语编排 Spec 仍写单阶段播种、`not_run` 与“无阻断”,不符合实际实现/验证 | 已同步两阶段 seed、实际测试和残余阻断 | - -## 3. 第二轮对抗式复盘 Findings 与修复 - -| ID | 严重度 / 分类 | 反例与影响 | 处置 | -| --- | --- | --- | --- | -| F4 | medium / Correctness | 精确同名 Tool 已存在但非 Ready 时继续 CreateTool,会产生重复资源 | 改为等待既有 Tool Ready;超时返回其 id/status | -| F5 | medium / Correctness | 多个精确同名 Tool/Runtime 时取首个会产生非确定绑定 | 改为歧义错误并增加测试 | -| F6 | medium / Correctness | Runtime 更新后若轮询先看到旧 Ready 版本,会误判新配置已发布 | `_wait_ready` 增加 `newer_than_version` 门槛,只有更新版本 Ready 才返回 | - -## 4. 对抗式边界结论 - -- phase-1 `mpa_meta` 占位是首次启动绕过 `GetMpaInstanceConf` 403 的必要状态;若后续绑定不完整,行可留待同 Agent ID 重试,但不得作为成功结果输出。 -- `GatewayMode=Shared` 且 `GatewayInstanceId=""` 不足以支持 IM routing;public endpoint 前缀也未匹配到账户下任何 APIG gateway。代码必须 fail-closed。 -- Tool/Runtime 的名称是幂等边界。非 Ready 状态与多个精确同名是两种不同异常,分别等待与失败。 -- 第一轮/第二轮修复后未发现新的 P0/P1 代码问题。 - -## 5. 验证 - -| 命令 / 验证 | 结果 | -| --- | --- | -| `uv run pytest tests/integrations/test_mpa_tool.py tests/integrations/test_mpa_runtime.py` | pass:13 passed(第二轮修复后) | -| `uv run pytest tests/cli/test_cli_mpa.py tests/integrations/test_mpa_runtime.py` | pass:22 passed(gateway/runtime 修复后) | -| targeted ruff | pass:All checks passed | -| 完整 MPA 专项测试 | pass:57 passed | -| `uv run pytest tests/cli` | pass:1282 passed, 4 skipped | -| `uv run pre-commit run --files <本功能文件>` | pass:ruff check、ruff format、Detect hardcoded secrets | - -## 6. 剩余风险 - -- Studio A2A 最终响应仍被所选 mpa-agent image 的 ADK session revision 冲突阻断;worker 完成不等于聊天成功。 -- Runtime 使用共享 APIG 且无客户 gateway id;需显式 `--apig-instance-id` 或控制面提供专属 gateway,才能 finalize IM routing 配置。 -- 飞书凭据/机器人安装未提供,且专属 APIG 尚缺,因此群聊能力未验证。 - -## 7. 结论 - -两轮 review 的六项真实 finding 均已修复并有定向测试。代码层未遗留 P0/P1;上述三个外部/镜像能力风险继续保持 blocked,不作为交付成功项。 diff --git a/prd-spec/features/mpa-agent-oneclick-provision/evidence/2026-09-11-verification-execution.zh.md b/prd-spec/features/mpa-agent-oneclick-provision/evidence/2026-09-11-verification-execution.zh.md deleted file mode 100644 index 2c71b9035..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/evidence/2026-09-11-verification-execution.zh.md +++ /dev/null @@ -1,73 +0,0 @@ -# 功能验证 Case 执行报告(dev-loop 步骤 7 与步骤 10) - -> 执行日期:2026-09-11 -> 分支:`feat/mpa-agent-oneclick-provision` -> 被测范围:`veadk mpa create`、`veadk/integrations/mpa/*`、真实 AgentKit Tool/Runtime、Studio A2A。 - -## 1. 执行汇总 - -| Case | 结果 | 证据 / 说明 | -| --- | --- | --- | -| VC-1..17 | pass | CLI 注册、参数/secret、VeFaaS 兼容、两阶段 `mpa_meta`、env、key-auth probes、dry-run、回归均由单测/CLI 测试覆盖 | -| VC-18 Studio A2A 聊天 | blocked | Runtime/worker 可执行,但两个 A2A 请求最终均返回 `failed`:`The session has been modified in storage since it was loaded` | -| VC-19 不同 session 不同沙箱 | pass(资源隔离层) | 两个不同 A2A context 映射到 `cw_sess_872f64f2f00b` 与 `cw_sess_bdf3758b4ddd` | -| VC-20 飞书拉群 | blocked/not_run | 未提供飞书凭据/机器人安装;Runtime 共享网关没有客户 `apig_instance_id` | -| VC-21 自动身份与独立资源 | pass | Agent `mi-tyfybtw8nnzc`,Tool `t-yeuugdts00zn6n5iqhin`,Runtime `r-yeuugf44qob21078l38i` | -| VC-22 缺 Tool 输入无副作用 | pass | 参数前置校验测试确认 SkillSpace/DB/Tool/Runtime 零调用 | -| VC-23 worker 模型选择 | pass | `MPA_CODEX_WORKER_DEFAULT_MODEL=doubao-seed-2-0-pro-260215`;两个 worker turn 模型 HTTP 200、命令 exit code 0、binding completed | -| VC-24 重试幂等 | pass | 同名 Tool/Runtime 复用;非 Ready Tool 等待;重名歧义失败;Runtime 更新等待新版 Ready | -| VC-25 APIG 完整性 | pass(fail-closed) | 真实 Runtime:`GatewayMode=Shared`、`GatewayInstanceId=""`;endpoint 不匹配账户 gateway;CLI 拒绝 finalize,支持显式 `--apig-instance-id` | - -## 2. 真机资源与状态 - -- Account:`2112682748`。 -- Agent:`mi-tyfybtw8nnzc`。 -- Tool:`t-yeuugdts00zn6n5iqhin`。 -- Runtime:`r-yeuugf44qob21078l38i`,version 4,Ready。 -- Public endpoint:`https://s6rrc31qdjfa7m90q4opi.apigateway-cn-beijing.volceapi.com`。 -- Runtime 外层 APIG key auth 可用;`A2A_TIP_VERIFY_ENABLED=false` 解决镜像内部无 TIP issuer 时的 401,同时不取消外层 key auth。 -- 数据库现有行仍为 `apig_instance_id=pending`;由于无法获得真实客户 APIG id,不能把它视为 finalize 成功。 - -## 3. Studio A2A 与沙箱证据 - -指定模型后,两个独立 context 都成功进入不同 worker session: - -| Turn | Worker session | Worker 结果 | A2A 最终结果 | -| --- | --- | --- | --- | -| `turn_f896d856f1c1` | `cw_sess_872f64f2f00b` | completed,命令 exit code 0 | failed:ADK session revision 冲突 | -| `turn_fe422c33aa56` | `cw_sess_bdf3758b4ddd` | completed,命令 exit code 0 | failed:ADK session revision 冲突 | - -结论:VC-19 的 session→sandbox 隔离得到真实证明,VC-23 的模型路由/worker 执行通过;但 VC-18 要求的是 Studio 收到成功聊天结果,因此必须保持 blocked。根因位于指定 mpa-agent image 的 PostgreSQL ADK session revision 协调:持久事件推进了 session revision,而 A2A runner 仍持有旧快照。veadk 不应吞掉或伪装该失败。 - -## 4. APIG 与飞书阻断 - -控制面原始字段为 `GatewayMode=Shared`、`GatewayInstanceId=""`。public endpoint 前缀 `s6rrc31qdjfa7m90q4opi` 也不匹配账户中的 APIG gateway id。因此: - -- CLI 不再回退选择任意 serverless gateway; -- 可用 `--apig-instance-id` 显式传入 IM routing 对应的专属客户 gateway; -- 未取得真实 id 时,phase-2 `mpa_meta` finalize 失败并保留占位; -- 飞书验证同时缺少 Feishu App 凭据/机器人安装,VC-20 不可执行。 - -此外,`arkclaw:ListResources` 对 `clawspace/csi-2112682748` 返回 403,MCP discovery 会 fallback;这是飞书/集成能力的权限风险,不影响已证明的 Tool/worker 执行。 - -## 5. OpenViking 说明 - -提供的 OpenViking endpoint 与当前 bearer OpenViking Server API 契约不兼容,因此本次真实 Runtime 未注入该配置。PostgreSQL/OpenViking 均按设计作为外部既有资源,不由 CLI 创建。 - -## 6. 回归与门禁 - -- 第二轮 review 后定向 Tool/Runtime:13 passed。 -- gateway/CLI + Runtime:22 passed。 -- targeted ruff:All checks passed。 -- 完整 MPA suite:57 passed。 -- 完整 `tests/cli`:1282 passed,4 skipped(36 条依赖弃用 warning,无失败)。 -- `uv run pre-commit run --files <本功能文件>`:ruff check、ruff format、Detect hardcoded secrets 全部通过。系统 PATH 无独立 `pre-commit`,因此使用仓库虚拟环境入口;门禁内容相同。 - -## 7. 验收结论 - -- 创建编排、动态 Tool/Agent ID、Runtime Ready、模型路由、不同 session 不同 sandbox:通过。 -- APIG 绑定:实现通过 fail-closed 验证;真实共享网关缺少客户 APIG id,需显式适配或控制面能力。 -- Studio 交互聊天:blocked(指定镜像 ADK session revision 冲突)。 -- 飞书拉群:blocked/not_run(凭据/机器人安装 + 专属客户 APIG 缺失)。 - -因此本次交付可提交 veadk 编排实现与风险证据,但不得宣称 mpa-agent 全链路 E2E 已通过。 diff --git a/prd-spec/features/mpa-agent-oneclick-provision/mpa-create.config.example.yaml b/prd-spec/features/mpa-agent-oneclick-provision/mpa-create.config.example.yaml deleted file mode 100644 index 55e8e3058..000000000 --- a/prd-spec/features/mpa-agent-oneclick-provision/mpa-create.config.example.yaml +++ /dev/null @@ -1,146 +0,0 @@ -# Copy to a local file (e.g. mpa-create.config.yaml) and fill in real values. -# Legacy (requires account-id): veadk mpa create --config mpa-create.config.yaml -# Managed creation (prepares network/APIG/database/skills/worker): -# veadk mpa provision --config mpa-create.config.yaml --agent-id customer-service -# Preview without cloud/database calls: add --dry-run. -# Studio uses a built-in Beijing profile and does not read this CLI YAML file. -# The managed path is implemented entirely in VeADK. -# Setup/recovery: veadk/integrations/mpa/managed/README.md (README.zh.md). -# -# SECURITY: the filled-in file contains secrets (pg-password, model-api-key, -# openviking-api-key, feishu-app-secret). It MUST NOT be committed to git. -# `.gitignore` already ignores `mpa-create.config*.yaml` (this .example is the -# only variant allowed in the repo). Explicit CLI options override these values. -# -# Keys accept dashes or underscores (pg-host or pg_host). - -# --- required --- -image: agentkit-platform--cn-beijing.cr.volces.com/agentkit/mpa_agent: -registry-name: agentkit-platform- -# mpa-agent-id is omitted by default; CLI generates mi-<12 lowercase alnum>. -# Optional expected-account assertion; the cloud identity remains authoritative. -# account-id: "" -region: cn-beijing - -# --- Identity (also injected by managed Studio creation for every template source) --- -identity-region: cn-beijing -user-pool-name: agentkit-studio-userpool -user-pool-client-name: agentkit-studio-client -identity-callback-url: https:///oauth/callback - -# --- PostgreSQL --- -# Managed auto mode obtains both Workspace connections using deployment STS. -# No PG credentials are required here. Legacy `mpa create` / manual profiles -# still require pg-host, pg-port, pg-user, pg-password, pg-sslmode, etc. - -# --- model --- -model-provider: openai -model-api-base: https://ark.cn-beijing.volces.com/api/v3/ -model-api-key: "" -model-name: doubao-seed-2-0-pro-260215 - -# --- compute plane / per-agent Tool --- -compute-plane: runtime -tool-image: agentkit-platform--cn-beijing.cr.volces.com/agentkit/mpa_codex_worker: -# Clone the known-good worker env when the image requires it. This reference -# Tool is a template only; the CLI creates a new dedicated Tool for the agent. -tool-reference-id: t-xxxxxxxx -runtime-role-name: IDRoleForArkClawShareAgent -# Optional per-session TOS output mount. Set all three values together. The -# credentials are stored only on the created Tool; every new Session receives a -# distinct bucket path mounted read-write at /data/output. -# tos-access-key: "${MPA_TOS_ACCESS_KEY}" -# tos-secret-key: "${MPA_TOS_SECRET_KEY}" -# tos-bucket: mpa-session-output -# tool-name and runtime-name are omitted: both derive from generated mpa-agent-id. -# agentkit-tool-id: t-xxxxxxxx # explicit debug/migration override only -# AgentKit's shared Runtime endpoint does not expose a customer GatewayId. -# Set this to the dedicated APIG gateway allocated for mpa-agent IM routing. -# apig-instance-id: gda-xxxxxxxx - -# --- Legacy OpenViking (optional; only mounts when a compatible OpenViking Server URL is set) --- -# openviking-url: https:// -# openviking-resource-id: ov-xxxx -# openviking-api-key: "" - -# --- Legacy Feishu channel (optional; managed mode binds channels after creation) --- -# feishu-app-id: cli_xxxx -# feishu-app-secret: "" - -# --- managed provisioning (used by `mpa provision` and Studio) --- -# Prepare deployment STS with AIDAP permissions, Runtime/worker IAM roles and model access. -# Auto mode allocates cloud resources only after an explicit creation submission. -# Keep one durable bootstrap state path for all Studio/CLI creation processes. -managed: - version: 1 - postgres: - mode: auto - admin-workspace-name: mpa_admin_workspace - business-workspace-name: mpa_business_workspace - project-name: default - bootstrap-path: .adk/mpa-pg-bootstrap.sqlite3 - timeout-seconds: 600 - # Optional existing Workspace IDs. Scope/name/engine are validated before use. - # admin-workspace-id: existing-management-workspace-id - # business-workspace-id: existing-business-workspace-id - # Existing installations: stop registry writers and migrate before clearing - # SHARED_APIG_DATABASE_URL. Preserve the existing business Workspace ID/name. - # veadk mpa init-admin-db --config mpa-create.config.yaml --source-url-env OLD_SHARED_APIG_DATABASE_URL - # Follow managed/README.md for coordinated Runtime registry endpoint cutover. - # Manual compatibility: mode: manual requires both IDs, business PG fields, - # DEPLOYMENT_DATABASE_ADMIN_URL and SHARED_APIG_DATABASE_URL. The latter must - # target mpa_admin_db. MPA_ADMIN_DATABASE_ADMIN_URL is used only for manual init. - # Optional rotating deployment STS file. Otherwise use the existing - # VOLCENGINE_ACCESS_KEY / VOLCENGINE_SECRET_KEY / VOLCENGINE_SESSION_TOKEN env. - # credential-file: /var/run/secrets/iam/credential - - # Choose one source, or leave both unset to use the flat image/model/PG fields. - # from-runtime: r-reference - # template-file: /secure/runtime-template.json - # Reference mode does not inherit the source agent's channels or Skill Space. - # Business database names are derived per agent; pg-database is legacy-only. - - # Optional explicit MPA image, overriding any of the three template sources. - # Omit runtime to retain the source image. Other prerequisites remain required. - # runtime: - # image: registry.example/agentkit/mpa_agent:release-tag - # role-name: IDRoleForArkClawShareAgent - # cpu-milli: 2000 - # memory-mb: 4096 - # min-instance: 1 - # max-instance: 1 - # max-concurrency: 100 - # apmplus-enable: true - # project-name: default - # env: - # CLOUD_PROVIDER: volcengine - # CUSTOM_API_KEY: "${MPA_CUSTOM_API_KEY}" - # Omitted fields retain the chosen source/default. Flat model/PG fields above - # let you remove from-runtime entirely. Generated identities and database names - # cannot be supplied through runtime.env. Never put secrets directly in YAML. - - network: - # For private PG, explicitly select its reachable VPC/subnets. Auto-created - # networks do not create database allowlists, peering or private connectivity. - # vpc-id: vpc-existing - # subnet-ids: [subnet-existing] - vpc-cidr: 172.20.0.0/16 - subnet-prefix: 24 - # zone: cn-beijing-a - apig: - # Optional explicit adoption (requires network.vpc-id). Otherwise create/reuse the registered gateway - # for this verified account/region. Prepare IM Gateway before CreateRuntime. - # adopt-id: gda-existing - {} - worker: - image: agentkit-platform--cn-beijing.cr.volces.com/agentkit/mpa_codex_worker: - # reference-id: t-worker-template - role-name: IDRoleForArkClawShareAgent - # To reuse a verified worker, replace image/reference-id with existing-id. - # existing-id: t-existing - timeout-seconds: 1800 - -# Managed flat secrets may use a complete environment reference, e.g. -# pg-password: "${MPA_PG_PASSWORD}" and model-api-key: "${MPA_MODEL_API_KEY}". -# Keep MPA_AGENT_ID stable on retry. Cancellation preserves registered resources. -# Deleting a Runtime does not delete shared APIG/VPC, business DB or Skill Space. diff --git a/prd-spec/features/mpa-agent-pg-workspace/2026-10-13-independent-business-workspace.md b/prd-spec/features/mpa-agent-pg-workspace/2026-10-13-independent-business-workspace.md deleted file mode 100644 index 79dae5522..000000000 --- a/prd-spec/features/mpa-agent-pg-workspace/2026-10-13-independent-business-workspace.md +++ /dev/null @@ -1,73 +0,0 @@ -# Independent business PostgreSQL Workspace per new MPA - -[中文版](2026-10-13-independent-business-workspace.zh.md) - -- Change ID: `mpa-agent-pg-workspace` -- Created/revised: 2026-10-13 -- Status: implemented -- Component: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), CON-19 -- Predecessor: [shared PG preparation](../mpa-space-scoped-resources/2026-09-23-auto-pg-workspaces.md) - -## Background and evidence - -`managed/pg_bootstrap.py` scopes both Workspaces by verified account, region and project. `service.py` pins their IDs and the business endpoint in each deployment record. Thus independent databases currently share compute and maintenance credentials. `deletion.py` reads the registry connection from the Runtime; it does not require a shared business Workspace. The MPA image consumes its injected PG connection independently of the provisioning layout. - -## Goals and non-goals - -New Studio MPA requests use one business Workspace per agent while retaining `mpa_admin_workspace/mpa_admin_db` and account/region network/APIG reuse. Existing agents and submitted tasks keep their database bindings. No migration, business-schema initialization, PG deletion, credential redesign, general-agent changes, image change, or cloud mutation belongs to this implementation. - -## Scenarios and requirements - -- FR-1: Two new agents share management resources but have distinct business Workspaces, endpoints and `mpa_agent_` databases. Both providers use their existing AIDAP adapter. -- FR-2: Retry, cancellation, uncertain creation and restart reuse the agent-scoped bootstrap identity. Do not blindly repeat uncertain purchases; retain current terminal-failure recovery. -- FR-3: Missing `businessWorkspaceMode` means the old shared request contract. New Studio requests explicitly persist `per-agent`. A submitted draft is never upgraded automatically. -- FR-4: Before a per-agent purchase, inspect existing deployment bindings in the prepared management DB. Registered shared agents retain their shared Workspace; recorded per-agent agents reuse theirs. Conflicts fail closed. Do not migrate, overwrite or delete business data. -- FR-5: Inject only that agent's business credentials into its PG settings; keep shared registry injection unchanged. Retain Workspace IDs and layout in the deployment record without secrets. -- FR-6: Keep the three-step wizard, update the database explanation, preserve manual/shared profiles and all generic-agent flows. Deletion continues retaining business PG resources. - -## Design and contract impact - -Add `managed.postgres.business-workspace-mode: shared|per-agent`, default `shared`, allowed only with automatic provisioning for public input; reject an explicitly configured business Workspace ID for per-agent mode. Add optional request/config `businessWorkspaceMode`. The built-in Studio configuration advertises `per-agent` as a capability without changing execution defaults for old requests; new forms explicitly submit it. Existing explicit CLI/YAML configuration may opt in. - -Pass stable internal `agent_id` to PG preparation. Keep management scope unchanged. Per-agent business scope hashes account/region/project plus agent ID; name is `mpa_agent_`, respecting AIDAP name limits. Reuse the bootstrap store, ownership validation, locks and readiness timeout. Prepare management DB first for per-agent mode and read registered binding under the existing registry lock; an old record without a layout marker is shared. Registered identities must match bootstrap state, scope and endpoints. Never infer ownership merely from name or accept conflicting tags. Registry/connection errors stop before purchasing a business Workspace. Management and business preparation remain under the existing bounded account bootstrap lock; no new cross-host lock guarantee is claimed. - -The existing business database provisioner receives the new maintenance URL, so database naming, markers and schema ownership remain unchanged. `bind_postgres_workspaces` pins the resolved layout as well as IDs. Runtime/session/skill/Feishu code and sibling `agentkit-mpa-agent` need no changes. Missing `openviking_users` is a separate image/schema issue. - -Security: use refreshed provider credentials; never persist PG passwords or model/OV keys in bootstrap, registry, request or docs. Existing Runtime registry credentials are unchanged, not redesigned. PG resources remain billable after cancellation/deletion until separately released by an operator. - -## Tasks and affected files - -- T-1 / FR-1,2,4: failing fake-cloud tests, `config.py`, `pg_bootstrap.py`, `service.py`, existing PG tests. -- T-2 / FR-3,5: request validation, task forwarding, config capability and frontend types/form tests; retain request fingerprints. -- T-3 / FR-6: locale copy, paired component spec/module docs, generated assets; inspect deletion without changing its scope. -- T-4: targeted/broader Python, Ruff/Pyright, frontend tests/build/i18n/assets, isolated browser checks and bilingual review. - -## Acceptance and verification - -| Requirement | Task | Acceptance | Verification | Result | -| --- | --- | --- | --- | --- | -| FR-1,2,4,5 | T-1 | AC-1: distinct new business Workspaces; same-agent retry stable; old binding retained; uncertainty never duplicates | `uv run --extra dev pytest tests/integrations/mpa_managed/test_auto_pg.py tests/integrations/mpa_managed/test_service.py` | pass | -| FR-3 | T-2 | AC-2: request mode persists through BFF/task/runner; legacy omission stays shared; resumed form does not upgrade | targeted config/routes/tasks and frontend tests | pass | -| FR-6 | T-3,4 | AC-3: compatible three-step UI, general-agent/deletion regression, synchronized docs/assets | broader managed Python, `npm --prefix frontend test`, build, i18n/assets and browser | pass | - -Tested scope: current working diff on `71175c68`, including pre-existing uncommitted OV work; date 2026-10-13. Live allocation: not_run (no isolated cloud creation authorized). Codex process smoke and harness coverage: not_applicable (contracts unchanged). Commit/push/pre-commit synchronization: not_run (not requested). - -## Risks and recovery - -Additional Workspaces increase cloud resource count and potential minimum compute cost. No migration is performed. Shared admin PG remains a control-plane failure domain. Bootstrap files/locks are local; unknown cross-host creates retain existing fail-closed recovery limitations. Independent Workspaces do not replace MPA business schema initialization. API limits and permissions remain those of the existing PG adapter. - -## Review and approval - -`review-spec` is unavailable; direct review covered boundaries, retry/cancellation, registered bindings, ownership, secrets, compatibility, testability and paired-language equivalence. No design blockers. User approved one Workspace per new MPA with “那帮我改吧” on 2026-10-13, then requested confirmation of other repository impacts. That approval covers creation/request/binding/UI adaptations; no migration or PG deletion is included. No commit, push or deployment is authorized. - -## Final implementation review and verification (2026-10-13) - -All T-1 through T-4 and AC-1 through AC-3 are complete for the isolated implementation scope. Reviewed the request-to-runner mapping, registered legacy binding preservation, agent-scoped naming/ownership, metadata uncertainty, timeouts, registry closure and retained PG deletion behavior. No blocking code/contract findings remain. - -- Regression-first: 11 new Python cases and the new frontend layout case failed before their respective implementation; final new PG suite has 15 passing tests. -- pass: `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py tests/integrations/test_mpa_tool.py tests/integrations/test_mpa_skill_space.py tests/integrations/test_ve_faas_deploy_image_key_auth.py -q --no-cov` — 951 passed, two workers. The full unrelated SDK suite is not_run; changes are isolated to managed MPA creation. -- pass: `npm --prefix frontend test` — 1,379 Node tests and 81 React tests; `npm --prefix frontend run build`, `npm --prefix frontend run check:i18n`, `npm --prefix frontend run test:webui-assets` (113 files, 350 references). -- pass: pinned Ruff 0.11.12 and Pyright over all 14 changed/new Python files, including preceding OV work. The local environment lacks Ruff/Pyright executables; used `uvx --from ruff==0.11.12 ruff check ` and `uvx pyright --pythonpath .venv/bin/python `, no machine-global configuration changes. -- pass: real browser with the actual React dialog and isolated simulated API, new request/legacy request mode, failure and retry, cancellation confirmation, loading/error/empty input states, keyboard Next, 390×844 window and screenshot inspection. Fixture/server/tab removed; no cloud resource created. Existing automated IME/general-agent tests remain passing; no input handlers changed. -- pass: paired-language and relative-link review, `git diff --check`, configured repository Gitleaks over the 94 changed/new files. A separate default-rule scan of generated bundles flagged a minified editor expression (`p.anchor.key`), reviewed as a vendor-code false positive; repository policy already excludes generated vendor bundles. No scan policy or hook was weakened. -- not_run: live AIDAP allocation/Runtime deployment, data migration, commit/push and pre-commit synchronization — not authorized/requested. Generated Runtime PG payloads and both cloud-provider branches were verified offline. Shared control PG and retained Workspace billing are the stated operational limitations. diff --git a/prd-spec/features/mpa-agent-pg-workspace/2026-10-13-independent-business-workspace.zh.md b/prd-spec/features/mpa-agent-pg-workspace/2026-10-13-independent-business-workspace.zh.md deleted file mode 100644 index ef1a9945d..000000000 --- a/prd-spec/features/mpa-agent-pg-workspace/2026-10-13-independent-business-workspace.zh.md +++ /dev/null @@ -1,73 +0,0 @@ -# 新建 MPA 使用独立业务 PostgreSQL Workspace - -[English](2026-10-13-independent-business-workspace.md) - -- Change ID:`mpa-agent-pg-workspace` -- 创建/修订:2026-10-13 -- 状态:implemented -- 组件:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-19 -- 前置设计:[共享 PG 准备](../mpa-space-scoped-resources/2026-09-23-auto-pg-workspaces.zh.md) - -## 背景与证据 - -`managed/pg_bootstrap.py` 使用核验后的账号、地域和项目给两个 Workspace 划定范围。`service.py` 将它们的 ID 和业务连接端点固定到部署记录。独立业务库因此仍共享计算资源和维护凭据。`deletion.py` 从 Runtime 获取管理库连接,不要求业务 Workspace 共享。MPA 镜像使用注入的 PG 连接,不依赖编排布局。 - -## 目标与非目标 - -新 Studio MPA 请求每个智能体使用一个业务 Workspace,保留 `mpa_admin_workspace/mpa_admin_db` 和账号/地域级网络、APIG 复用。已有智能体和已提交任务保持数据库绑定。本次不迁移数据、不初始化业务表、不删除 PG、不重新设计凭据、不改通用智能体、不更换镜像、不执行云资源变更。 - -## 场景与需求 - -- FR-1:两个新智能体共享管理资源,但拥有不同业务 Workspace、端点和 `mpa_agent_` 数据库。两个提供商使用原有 AIDAP 适配器。 -- FR-2:重试、取消、创建结果未知和重启复用智能体范围内的引导身份。未知结果不盲目重复购买,保留现有确定终态失败恢复机制。 -- FR-3:缺少 `businessWorkspaceMode` 表示旧共享请求契约。新 Studio 请求显式持久化 `per-agent`。已提交草稿不自动升级。 -- FR-4:购买独立业务 Workspace 前,读取已准备好的管理库中的部署绑定。已有共享智能体保留共享 Workspace,已有独立智能体复用原 Workspace。冲突明确失败,不迁移、覆盖或删除业务数据。 -- FR-5:PG 设置注入该智能体自己的业务凭据,共享注册库注入保持不变。部署记录保留 Workspace ID 和布局,不记录密钥。 -- FR-6:保留三步向导,更新数据库说明,兼容手动/共享配置及通用智能体流程。删除继续保留业务 PG 资源。 - -## 设计与契约影响 - -增加 `managed.postgres.business-workspace-mode: shared|per-agent`,默认 `shared`;公开输入仅允许自动准备使用独立布局,不接受该布局下显式配置业务 Workspace ID。请求/配置增加可选 `businessWorkspaceMode`。内置 Studio 配置声明 `per-agent` 能力,但不改变旧请求的执行默认值;新表单显式提交。CLI/YAML 可显式启用。 - -向 PG 准备传入稳定内部 `agent_id`。管理范围保持不变。独立业务范围由账号/地域/项目及智能体 ID 的哈希确定,名称为 `mpa_agent_`,满足 AIDAP 名称限制。复用引导存储、归属校验、锁和就绪超时。独立模式先准备管理库,在原有注册库锁下读取已有绑定;无布局标记的旧记录视为共享。登记身份必须与引导状态、范围、端点一致。不仅凭名称判断归属,不接受冲突标签。注册库/连接错误在购买业务 Workspace 前停止。管理和业务准备仍位于有超时限制的原账号引导锁中,不宣称新增跨主机锁保证。 - -现有业务库准备器接收新的维护连接,数据库名称、标记和表结构归属不变。`bind_postgres_workspaces` 固定实际布局和 ID。Runtime/会话/技能/飞书及兄弟仓库 `agentkit-mpa-agent` 不需要修改。`openviking_users` 缺表是独立镜像/初始化问题。 - -安全:使用刷新后的提供商凭据,不向引导文件、注册记录、请求或文档写入 PG 密码及模型/OV Key。现有 Runtime 注册库凭据保持不变,本次不重构。取消/删除后 PG 仍计费,需管理员单独释放。 - -## 任务与受影响文件 - -- T-1 / FR-1,2,4:先写失败的模拟云测试,再改 `config.py`、`pg_bootstrap.py`、`service.py` 及 PG 测试。 -- T-2 / FR-3,5:请求校验、任务透传、配置能力、前端类型/表单测试;保留请求指纹。 -- T-3 / FR-6:国际化文案、双语组件契约/模块说明、构建产物;核对删除,不扩展删除范围。 -- T-4:定向/更广 Python、Ruff/Pyright、前端测试/构建/国际化/资源检查、隔离浏览器验证和双语审查。 - -## 验收与验证 - -| 需求 | 任务 | 验收 | 验证 | 结果 | -| --- | --- | --- | --- | --- | -| FR-1,2,4,5 | T-1 | AC-1:新业务 Workspace 不同;同智能体重试稳定;旧绑定保留;未知结果不重复创建 | `uv run --extra dev pytest tests/integrations/mpa_managed/test_auto_pg.py tests/integrations/mpa_managed/test_service.py` | pass | -| FR-3 | T-2 | AC-2:模式经 BFF/任务/runner 持久化;旧请求缺省为共享;恢复表单不升级 | 定向配置/路由/任务与前端测试 | pass | -| FR-6 | T-3,4 | AC-3:三步 UI 兼容、通用智能体/删除回归、文档/资源同步 | 更广 managed Python,`npm --prefix frontend test`、构建、国际化/资源及浏览器 | pass | - -验证范围:`71175c68` 上的当前工作区差异,包含先前未提交 OV 改动;日期 2026-10-13。真实云创建:not_run(未授权隔离云创建)。Codex 进程 smoke 和 harness 覆盖:not_applicable(契约未变)。提交/推送/提交前同步:not_run(未请求)。 - -## 风险与恢复 - -更多 Workspace 增加云资源数量和可能的最低计算费用。不迁移数据。共享管理 PG 仍是控制面故障域。引导文件/锁位于本地,跨主机未知创建仍遵循现有明确失败的恢复限制。独立 Workspace 不能代替 MPA 业务表初始化。API 限制和权限沿用现有 PG 适配器。 - -## 审查与批准 - -`review-spec` 不可用,已直接审查边界、重试/取消、已有绑定、归属、密钥、兼容性、可测试性和双语一致性,无设计阻塞。用户于 2026-10-13 通过“那帮我改吧”批准新 MPA 独立 Workspace,随后要求确认仓库其他影响。批准包括创建/请求/绑定/UI 适配,不包括迁移或 PG 删除。未授权提交、推送或部署。 - -## 最终实现审查与验证(2026-10-13) - -隔离实现范围内 T-1 至 T-4、AC-1 至 AC-3 已完成。已审查请求到 runner 的透传、旧登记绑定保留、智能体范围名称/归属、元数据未知状态、超时、注册库关闭及 PG 保留删除行为,无阻塞代码/契约问题。 - -- 先写回归:11 项新 Python 用例和新前端布局用例在对应实现前失败;最终新 PG 测试集 15 项通过。 -- pass:`uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py tests/integrations/test_mpa_tool.py tests/integrations/test_mpa_skill_space.py tests/integrations/test_ve_faas_deploy_image_key_auth.py -q --no-cov`,951 项通过,两个 worker。无关完整 SDK 测试集为 not_run,改动限定受管 MPA 创建。 -- pass:`npm --prefix frontend test`,1,379 项 Node 测试和 81 项 React 测试;`npm --prefix frontend run build`、`npm --prefix frontend run check:i18n`、`npm --prefix frontend run test:webui-assets`(113 文件、350 引用)。 -- pass:固定 Ruff 0.11.12 及 Pyright 检查所有 14 个改动/新增 Python 文件,包括前置 OV 改动。本地环境缺少 Ruff/Pyright 可执行文件,使用 `uvx --from ruff==0.11.12 ruff check ` 和 `uvx pyright --pythonpath .venv/bin/python `,未修改机器全局配置。 -- pass:实际 React 弹窗配隔离模拟 API 的真实浏览器验证,包括新/旧请求模式、失败重试、取消确认、加载/错误/空输入状态、键盘下一步、390×844 窗口及截图检查。已移除临时页面/服务/浏览器标签,没有创建云资源。原有 IME/通用智能体自动测试保持通过,未改输入处理器。 -- pass:双语及相对链接审查、`git diff --check`,按仓库 Gitleaks 配置检查 94 个改动/新增文件。额外默认规则扫描构建包命中一段压缩编辑器表达式(`p.anchor.key`),已确认为供应商代码误报;仓库策略原本排除生成的供应商包。未弱化扫描策略或 hook。 -- not_run:实际 AIDAP 分配/Runtime 部署、数据迁移、提交/推送和提交前同步,未授权/未请求。Runtime PG 部署参数及两个提供商分支已离线验证。共享控制 PG 和保留 Workspace 的费用仍是明确的运行限制。 diff --git a/prd-spec/features/mpa-byteplus-openviking/2026-10-14-automatic-creation.md b/prd-spec/features/mpa-byteplus-openviking/2026-10-14-automatic-creation.md deleted file mode 100644 index 114cb38a7..000000000 --- a/prd-spec/features/mpa-byteplus-openviking/2026-10-14-automatic-creation.md +++ /dev/null @@ -1,118 +0,0 @@ -# BytePlus OpenViking preparation for MPA creation - -[中文版](2026-10-14-automatic-creation.zh.md) - -Change ID: `mpa-byteplus-openviking`. Created/revised: 2026-10-14. Status: implemented; live acceptance pending. - -Related contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md). -Predecessor: [Beijing automatic OV](../mpa-openviking-auto/2026-10-13-default-model-creation.md). - -## Background and evidence - -The user requests automatic OV preparation in this VeADK repository for BytePlus MPA creation. Current `managed/openviking.py`, configuration validation and service orchestration explicitly reject automatic OV outside Volcengine Beijing. The Studio dialog additionally restricts automatic mode to Volcengine. BytePlus manual URL, resource ID and API Key injection already exists. - -The supplied overseas SOP describes a personal library created with a Coding Plan model credential and the data-plane URL `https://api.vikingdb.ap-southeast-1.bytepluses.com/openviking`. It contains a Python attachment for collection management. Attachment retrieval on 2026-10-14 was rejected for missing `drive:file:download` scope. The local path mentioned in the SOP does not contain that script. The overseas control-plane host, signing region, model identifiers, credential Source/EndpointID semantics and enterprise availability have therefore not been verified. Do not infer these from the data-plane URL or domestic settings. - -Current local `agentkit-mpa-agent` code separately uses Beijing OV administration for per-user registration/key lookup. Creating an overseas collection in VeADK alone does not prove that Runtime memory works. That repository requires its own adaptation and deployment verification. - -## Goals and non-goals - -Prepare and bind an overseas library before Runtime deployment, wait for readiness, obtain its data-plane key, and inject the existing four OV environment variables. Preserve Volcengine Beijing behavior and submitted manual requests. Initial candidate scope is BytePlus `ap-southeast-1`, subject to provider verification. - -Exclude automatic changes to `agentkit-mpa-agent`, cloud deployment, paid resource creation during development, credential publication, unrelated local changes, general-agent creation and OV deletion. Do not silently replace per-user isolation with a shared default identity. - -## Requirements and scenarios - -- FR-1: Advertise automatic OV only for explicitly verified provider/region combinations. Configuration, API, orchestration and UI must agree. An unsupported region fails before resource mutation. -- FR-2: Use deployment-account BytePlus credentials, including refreshed STS credentials where applicable, to sign verified control-plane calls. Never reuse a domestic account credential or derive a control-plane hostname by guessing. -- FR-3: Verify library edition and model credential requirements before selecting defaults. Coding Plan credentials, ordinary model inference credentials and the resulting library API Key are distinct. A missing prerequisite produces an actionable, redacted error before creating resources; no undocumented fallback is allowed. -- FR-4: Reuse existing deployment locking and durable nonsecret intent/resource bindings. Persist a returned ResourceID immediately. A later failure reuses that ID after ownership/configuration validation. A create timeout with unknown outcome does not trigger blind duplicate creation. Cancellation retains the binding and prevents subsequent steps. -- FR-5: Poll using a bounded deadline and verified provider status values. After readiness, obtain the default-user library Key and inject `OPENVIKING_URL`, `OPENVIKING_RESOURCE_ID`, `OPENVIKING_API_KEY`, and `OPENVIKING_USER`. The URL is provider-specific. Keys stay out of registry records, logs, UI responses and browser persistence. -- FR-6: New eligible Studio requests use automatic mode. Existing submitted manual tasks retain their original mode and inputs on retries. Use the existing three-step dialog and localized status/error presentation. Explain required model credentials and resource retention where applicable. -- FR-7: Domestic requests, general agents and other existing BytePlus dependencies retain their contracts. Runtime per-user memory is a separate acceptance boundary and must not be claimed as verified by collection-creation tests. - -## Proposed design and contract impact - -Add explicit provider/region endpoint and model configuration selection to the existing OV adapter after confirming provider contracts. Keep the Beijing request semantics stable. Reuse the signing helper, deployment-account source, registry lock, safe errors and progress stages. Verify the overseas Create/Get/Access response shape rather than assuming it matches Beijing. - -Extend the existing automatic availability flag and checks consistently through profile loading, request validation, service execution and the dialog. Persist each request's chosen mode. Resolve model credentials on the server with an explicit supported source; the final selection requires the overseas reference script. Any new secret setting must have validation, redaction and restart/retry semantics before implementation. - -Update the bilingual Studio MPA creation contract with the verified provider matrix, model requirements and retained-resource semantics. Existing Runtime OV variable names remain unchanged. No component spec currently promises overseas automatic creation; do not mark a proposed capability active prematurely. - -## Tasks and affected files - -1. T-1 / FR-2–3: Read the SOP attachment or equivalent authoritative overseas contract. Resolve the blocking questions below. Finalize and review this bilingual design and component contract. -2. T-2 / FR-1–5: Add failing provider/configuration/adapter/retry tests; implement in `veadk/integrations/mpa/managed/openviking.py`, `config.py`, `service.py` and related profile/credential code only as needed. -3. T-3 / FR-1,6: Update `frontend/src/ui/mpa-create/MpaCreateDialog.tsx`, corresponding API/task handling and locales if required. Add UI regressions for new versus resumed requests and unavailable prerequisites. -4. T-4 / FR-7: Run domestic and overseas offline regressions, inspect browser behavior and regenerate release assets. Reconcile operator documentation and both design/spec languages. - -## Acceptance and verification - -- AC-1: An eligible new BytePlus request reaches the verified overseas control plane, obtains a bound library, and injects the four variables. No real credential enters test fixtures. -- AC-2: Timeout, cancellation, permission rejection, readiness failure, missing model credentials and same-request retry have explicit tested outcomes, without duplicate libraries or silent success. -- AC-3: Domestic wire payloads and existing manual retry semantics remain unchanged. General-agent creation is unaffected. -- AC-4: UI eligibility matches backend behavior; errors, loading, retry, keyboard and narrow-window states remain usable. Generated frontend assets match source. -- AC-5: Documentation distinguishes offline collection provisioning from live cloud acceptance and Runtime memory integration. - -Planned commands: `uv run --extra dev pytest -n 2 tests/integrations/mpa_managed`; affected Studio server route/task tests; Ruff and Pyright on changed Python files; `npm --prefix frontend test`; `npm --prefix frontend run build`; `npm --prefix frontend run check:i18n`; `npm --prefix frontend run test:webui-assets`; scoped secret scanning. Use isolated mock services for tests. A real paid creation/read-memory smoke requires separate authorization and an explicit retention/cleanup decision. - -## Risks and blocking questions - -- What are the supported overseas control-plane host, signing region, API version and request/response fields? -- Does the overseas edition support MPA per-user registration and key retrieval? The SOP's personal-library OpenClaw example does not establish this. -- Which VLM/Embedding identifiers and credential sources are accepted? Can the existing resolved model key be used, or is a separate Coding Plan credential required? -- Which ready/failure statuses and billing/retention constraints apply? Do not copy domestic billing claims into overseas UI without evidence. -- Runtime readiness alone does not establish functional OV memory; an adapted Runtime image is required for the separate per-user path. - -## Review, approval and verification record - -The user requested this repository's implementation on 2026-10-14. Scope is authorized; unresolved provider details prevent design clearance and production/test edits. Direct review identifies edition compatibility, credential selection and provider endpoint verification as blockers. No `review-spec` skill is available; record a full direct review after resolving them. - -2026-10-14, base `2ffe6e14` plus pre-existing uncommitted changes: source inspection completed; attachment access blocked (missing `drive:file:download`); cloud validation and runtime tests `not_run` (no implementation or cloud authorization); production changes `not_run` (provider contract unverified). Existing contract relocation, OpenSpec deletion and lockfile changes are outside this task and are preserved. - - -## Supplied-script review (2026-10-14) - -The user supplied the attachment directly. It was inspected as source, not executed or copied into the repository. Confirmed script parameters: - -- Control host `vikingdb.ap-southeast-1.byteplusapi.com`, signing region `ap-southeast-1`, service `vikingdb`, version `2025-06-09`, POST path `/api/openviking/{Action}`; headers `X-Top-Service` and `X-Top-Region` plus optional STS `X-Security-Token`. -- Collection `Version=developer`. Top-level `Source=codeplan` with Coding Plan credentials; model `seed-2-0-lite` and `skylark-embedding-vision`, no `ModelVersion` or `EndpointID` in coding mode. Endpoint mode instead uses `Source=volcengine` and separately supplied endpoints for each model. -- Create returns `ResourceID`; poll Get for `READY`/`FAILED`; Access key body contains `ResourceID` only (no `UserID`); default is the returned user. Response parsing accepts `Result` or `result`. -- The script explicitly states that developer libraries reject user registration. Data calls require `Authorization: Bearer` and `x-tt-env: pre`. Current Runtime code does not implement that contract. - -This resolves the script-access and control-plane parameter blockers, but introduces a material edition/isolation decision: implement developer-library provisioning for later Runtime adaptation, or preserve enterprise per-user isolation and obtain a supported overseas enterprise contract first. The user was asked to choose; no default-identity fallback is authorized. Implementation and production tests remain pending that decision. Neither script inspection nor a successful collection create proves Runtime compatibility. - -## Approved implementation decision - -On 2026-10-14 the user approved developer-library provisioning in this repository and separate Runtime adaptation before live memory integration. This supersedes the pending edition decision above. Implement BytePlus ap-southeast-1 only, Source=codeplan and the supplied model names; obtain its dedicated Coding Plan key from server environment `OV_MODEL_API_KEY`, never from the ordinary MPA model key. Missing/invalid keys fail before any cloud resource mutation. New BytePlus Studio requests advertise auto mode; submitted manual requests remain manual. Reuse all current intent, ownership, timeout and cancellation guarantees. Default-user key access omits UserID. Preserve the four existing Runtime variables; do not invent unsupported Runtime settings. The UI explains personal edition/default user, the server credential prerequisite and the separate Runtime integration requirement. Do not claim overseas billing specifics. Retain libraries after cancellation/deletion. - -Implement the exact reference signing headers/path in a BytePlus-only helper, without changing the shared signing utility or Beijing adapter. Cover canonical signing and STS with deterministic offline tests. Validate readback fields required by the selected request; BytePlus does not require ModelVersion or EndpointID, while Beijing still does. Accept the reference's Result/result envelopes for BytePlus only, and reject explicit failure/invalid resource IDs. No automatic retry of uncertain Create responses. - -Direct design review: provider boundary, credential separation, personal-edition isolation, existing manual recovery, secret handling, startup lazy imports, error propagation, tests and bilingual parity reviewed. Blocking decisions resolved; status approved. No cloud operation, commit or push is authorized by this design. - -## Implementation and verification (2026-10-14) - -Implemented the approved developer-only scope. The adapter uses a separate lazy-imported BytePlus signing transport, the reference request path/headers and model names; Beijing still uses the existing signer and request schema. Profile capability and the existing dialog now support new BytePlus automatic requests. Model keys resolve only from the server environment, before cloud construction in direct provisioning and before task submission in Studio validation. No new browser secret field or shared-identity Runtime fallback was introduced. The four existing variables are prepared for the separately adapted Runtime. - -Tested diff: this feature on `2ffe6e14`, preserving pre-existing local contracts relocation, OpenSpec deletion and lockfile edits. Review covered provider scope, exact canonical signing with/without STS, lowercase response envelopes, no explicit UserID for personal libraries, explicit failure handling, secret exclusion, ownership/readback, ambiguous create outcomes, cancellation/retry and old manual tasks. The underlying cloud calls were mocked throughout. - -| Check | Result | Scope | -| --- | --- | --- | -| Test-first Python | pass | Seven new regressions failed before implementation; BytePlus adapter/config/retry. | -| Targeted Python | pass | 12 tests, including later-added STS signing, cancellation and no-mutation missing-key checks. | -| Managed creation + startup regression | pass | 987 tests with two workers. | -| Studio API/RBAC regression | pass | 205 tests: Runtime profile routes and Studio RBAC; two workers. | -| Frontend tests | pass | 1,389 Node tests and 96 React tests. The first UI attempt used the wrong working directory; the subsequent repository npm command passed all cases. The new automatic-mode case failed before the UI fix. | -| Ruff 0.11.12 check/format and Pyright | pass | Six changed Python source/test files; Pyright 0 errors/warnings. | -| Build, i18n and release references | pass | Both frontend targets built; 2 locales/21 namespaces; 113 packaged files/350 references. | -| Browser | pass | Actual dialog and mocked HTTP: overseas automatic mode, no manual fields, visible missing-key error, same-request retry, narrow viewport, Escape, old manual recovery and Beijing copy. Temporary fixture files/server removed. | -| Wheel | pass | Built locally; exact new adapter bytes present in wheel. No publication. | -| Scoped secret scan | pass | Changed/new source, docs, tests and generated assets; no findings. | -| Real cloud / Runtime OV memory | not_run | No paid resource creation authorized. Personal default-user/Bearer/pre routing remains another repository's work. | -| Full repository pytest/pre-commit | not_run | No commit requested. Targeted integration and full frontend gates cover this provider extension; no unrelated source changes. | -| Harness/sidecar smoke | not_applicable | No harness, worker or sidecar protocol changes. | - -AC-1–AC-5 have offline implementation evidence; live collection response details, account entitlement and memory remain unverified. User accepted Runtime adaptation as separate scope. No commit or push performed. - - -Credential decision superseded by [API Key + endpoints](2026-10-14-inference-endpoints.md). diff --git a/prd-spec/features/mpa-byteplus-openviking/2026-10-14-automatic-creation.zh.md b/prd-spec/features/mpa-byteplus-openviking/2026-10-14-automatic-creation.zh.md deleted file mode 100644 index 8aee3f2ad..000000000 --- a/prd-spec/features/mpa-byteplus-openviking/2026-10-14-automatic-creation.zh.md +++ /dev/null @@ -1,118 +0,0 @@ -# BytePlus MPA 创建中的 OpenViking 自动准备 - -[English](2026-10-14-automatic-creation.md) - -变更 ID:`mpa-byteplus-openviking`。创建/修订:2026-10-14。状态:implemented;云端验收待进行。 - -相关契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md)。 -前置设计:[北京自动 OV](../mpa-openviking-auto/2026-10-13-default-model-creation.zh.md)。 - -## 背景与证据 - -用户要求在本 VeADK 仓库为 BytePlus MPA 创建补齐自动准备 OV。目前 `managed/openviking.py`、配置校验和服务编排都明确拒绝火山北京以外的自动 OV。Studio 弹窗也将自动模式限定为火山。BytePlus 已支持手动注入 URL、资源 ID 和 API Key。 - -用户提供的海外 SOP 描述了使用 Coding Plan 模型凭据创建个人版库,数据面地址为 `https://api.vikingdb.ap-southeast-1.bytepluses.com/openviking`。文档附有库管理 Python 脚本。2026-10-14 下载附件因缺少 `drive:file:download` 权限被拒绝;文档所述本地路径没有该脚本。因此海外控制面地址、签名地域、模型标识、凭据 Source/EndpointID 语义及企业版可用性尚未确认,不能根据数据面地址或国内配置推断。 - -本地 `agentkit-mpa-agent` 代码的用户注册和取 Key 仍使用北京 OV 管理接口。仅在 VeADK 中创建海外库不能证明 Runtime 记忆可用;该仓库需要单独适配和部署验证。 - -## 目标与非目标 - -在部署 Runtime 前创建并绑定海外库,等待就绪、获取数据面 Key,注入已有的四个 OV 环境变量。保留火山北京行为和已提交的手动请求。首期候选范围为 BytePlus `ap-southeast-1`,须以接口核实结果为准。 - -不包含自动修改 `agentkit-mpa-agent`、云端部署、开发过程中的付费资源创建、凭据发布、无关本地改动、通用智能体创建及 OV 删除。不得静默将按用户隔离替换成共享 default 身份。 - -## 需求与场景 - -- FR-1:仅向明确验证过的云厂商/地域组合开放自动 OV;配置、API、编排及 UI 必须一致。不支持的地域在资源变更前报错。 -- FR-2:使用部署账号的 BytePlus 凭据签名已核实的控制面调用,适用时刷新 STS。不得复用国内账号凭据或猜测控制面域名。 -- FR-3:选择默认值前核实库版本及模型凭据要求。Coding Plan 凭据、普通模型推理凭据和建库返回的库 API Key 是不同类型。缺少前置条件时,在创建资源前给出可操作、脱敏的错误,不采用未经文档确认的回退。 -- FR-4:复用部署锁及持久化的非敏感意图/资源绑定。返回 ResourceID 后立即保存。后续失败重试先校验归属和配置,再复用原 ID。创建超时、结果不确定时不盲目重复建库。取消保留绑定并阻止后续步骤。 -- FR-5:按已核实的状态值和有界截止时间轮询。就绪后取得 default 用户的库 Key,注入 `OPENVIKING_URL`、`OPENVIKING_RESOURCE_ID`、`OPENVIKING_API_KEY`、`OPENVIKING_USER`。URL 按云厂商区分。Key 不进入注册库、日志、UI 响应或浏览器持久化存储。 -- FR-6:新的符合条件的 Studio 请求使用自动模式;已提交的手动任务重试保留原模式及输入。沿用三步弹窗及本地化状态/错误展示,适用时说明模型凭据前置要求及资源保留行为。 -- FR-7:国内请求、通用智能体及其他已有 BytePlus 依赖保持契约。Runtime 按用户使用记忆是独立验收边界,不得通过建库测试声称其已验证。 - -## 拟议设计与契约影响 - -确认接口契约后,在现有 OV 适配器增加显式的云厂商/地域端点及模型配置选择,保持北京请求语义。复用签名辅助函数、部署账号凭据来源、注册库锁、安全错误和进度阶段。核实海外 Create/Get/Access 响应结构,不假定与北京一致。 - -统一扩展配置加载、请求校验、服务执行和弹窗的自动可用性标识及检查,并持久化每次请求选择的模式。模型凭据在服务端按明确支持的来源解析,最终选取方式需参考海外脚本;如增加密钥配置,实施前必须明确校验、脱敏、重启及重试语义。 - -更新 Studio MPA 创建双语契约,记录已核实的云厂商支持矩阵、模型要求及资源保留语义。现有 Runtime OV 环境变量名称保持不变。当前组件规格没有承诺海外自动创建,不得提前将拟议能力标为已生效。 - -## 任务与涉及文件 - -1. T-1 / FR-2–3:读取 SOP 附件或等效的权威海外契约,解决以下阻塞问题,完成并评审双语设计和组件契约。 -2. T-2 / FR-1–5:先补失败的云厂商/配置/适配器/重试测试,再修改 `veadk/integrations/mpa/managed/openviking.py`、`config.py`、`service.py`,按需调整配置及凭据代码。 -3. T-3 / FR-1,6:修改 `frontend/src/ui/mpa-create/MpaCreateDialog.tsx`,按需更新 API/任务处理和本地化文案,覆盖新请求、恢复请求和缺少前置条件的 UI 回归。 -4. T-4 / FR-7:执行国内与海外离线回归、浏览器检查并重新构建发布产物,同步操作文档和双语设计/规格。 - -## 验收与验证 - -- AC-1:符合条件的新 BytePlus 请求调用已核实的海外控制面,取得绑定库并注入四个变量;测试数据不含真实凭据。 -- AC-2:超时、取消、权限拒绝、就绪失败、缺少模型凭据和同请求重试均有明确测试结果,不重复建库、不静默成功。 -- AC-3:国内线上请求体和已有手动重试语义保持不变,不影响通用智能体创建。 -- AC-4:UI 可用性与后端一致;错误、加载、重试、键盘和窄窗口可用;前端产物与源码一致。 -- AC-5:文档明确区分离线建库编排、云端实测和 Runtime 记忆集成。 - -计划命令:`uv run --extra dev pytest -n 2 tests/integrations/mpa_managed`;受影响的 Studio 服务端路由/任务测试;修改 Python 文件的 Ruff 和 Pyright;`npm --prefix frontend test`;`npm --prefix frontend run build`;`npm --prefix frontend run check:i18n`;`npm --prefix frontend run test:webui-assets`;限定范围密钥扫描。测试使用隔离的模拟服务。真实付费建库/读取记忆冒烟需要单独授权,并明确资源保留或清理方式。 - -## 风险与阻塞问题 - -- 海外支持的控制面地址、签名地域、API 版本及请求/响应字段是什么? -- 海外库版本是否支持 MPA 按用户注册和取 Key?SOP 中个人库的 OpenClaw 示例不能证明这一点。 -- VLM/Embedding 接受哪些标识及凭据来源?已有模型 Key 能否复用,还是必须单独提供 Coding Plan 凭据? -- 就绪/失败状态及计费、保留约束是什么?未经核实不得将国内计费说明直接用于海外 UI。 -- Runtime 就绪不等于 OV 记忆可用;独立的按用户路径需要适配后的 Runtime 镜像。 - -## 评审、授权与验证记录 - -用户在 2026-10-14 要求实施本仓库适配,范围已授权;未解决的接口细节阻止设计通过及生产代码/测试修改。直接评审认定库版本兼容性、凭据选择和云端端点核实为阻塞项。没有可用的 `review-spec` 技能,解决后记录完整直接评审。 - -2026-10-14,基于 `2ffe6e14` 及原有未提交修改:源码检查完成;附件读取 blocked(缺少 `drive:file:download`);云端验证与运行测试 `not_run`(尚未实施、未授权云操作);生产代码修改 `not_run`(云端契约未核实)。已有 contracts 迁移、OpenSpec 删除和 lockfile 修改属于其他任务,予以保留。 - - -## 用户提供脚本的评审(2026-10-14) - -用户直接提供了附件,已按源码阅读,没有执行或复制进仓库。脚本确认的参数如下: - -- 控制面域名 `vikingdb.ap-southeast-1.byteplusapi.com`、签名地域 `ap-southeast-1`、服务 `vikingdb`、版本 `2025-06-09`、POST 路径 `/api/openviking/{Action}`;头包含 `X-Top-Service`、`X-Top-Region`,以及可选 STS `X-Security-Token`。 -- 库为 `Version=developer`。Coding Plan 凭据使用顶层 `Source=codeplan`;模型为 `seed-2-0-lite` 和 `skylark-embedding-vision`,coding 模式不带 `ModelVersion` 或 `EndpointID`。endpoint 模式改用 `Source=volcengine`,分别指定两个模型的接入点。 -- Create 返回 `ResourceID`;Get 轮询 `READY`/`FAILED`;取 Key 请求体只有 `ResourceID`(不带 `UserID`),返回 default 用户;响应兼容 `Result` 和 `result`。 -- 脚本明确说明个人版拒绝用户注册;数据面要求 `Authorization: Bearer` 和 `x-tt-env: pre`。当前 Runtime 代码没有实现此契约。 - -脚本读取及控制面参数阻塞已解除,但出现影响隔离方式的版本决策:先实施个人库准备、后续适配 Runtime,还是保留企业版按用户隔离、先取得支持的海外企业版契约。已请用户选择,尚未授权回退到 default 共享身份。实现和生产测试等待该决策。阅读脚本或建库成功均不证明 Runtime 兼容性。 - -## 已批准的实施决定 - -2026-10-14 用户批准本仓库接入个人版自动建库,Runtime 单独适配后再联调。本节替代前文待定的版本决策。仅实施 BytePlus ap-southeast-1,Source=codeplan 并使用脚本给出的模型名;专用 Coding Plan Key 从服务端环境变量 `OV_MODEL_API_KEY` 读取,不使用普通 MPA 模型 Key。缺少或无效时在任何云资源变更前失败。新的 BytePlus Studio 请求开放 auto,已提交的手动请求保持手动。复用现有意图、归属、超时及取消保证。取 default Key 不传 UserID。保留四个已有 Runtime 变量,不添加没有消费者的 Runtime 配置。UI 说明个人版/default 用户、服务端凭据前置条件和 Runtime 独立联调要求,不声称未经确认的海外计费细节。取消/删除后保留库。 - -BytePlus 专用辅助模块按参考脚本的签名头和路径实现,不修改共享签名工具或北京适配器。用确定性离线测试覆盖规范签名及 STS。按选定请求校验回读字段:BytePlus 不要求 ModelVersion/EndpointID,北京仍要求。仅 BytePlus 接受参考的 Result/result 响应结构,拒绝显式失败及无效资源 ID。Create 结果不确定时不自动重试。 - -直接设计评审已检查云厂商边界、凭据分离、个人版隔离限制、已有手动恢复、密钥处理、启动延迟导入、错误传播、测试及双语一致性。阻塞决策已解决,状态 approved。本设计不授权云操作、提交或推送。 - -## 实施与验证(2026-10-14) - -已实现批准的仅个人版范围。适配器使用独立且延迟导入的 BytePlus 签名传输、脚本给出的路径/请求头及模型名;北京继续使用现有签名函数和请求结构。配置能力和已有弹窗支持新的 BytePlus 自动请求。模型 Key 仅从服务端环境解析,直接部署在构造云客户端前检查,Studio 则在任务提交前校验。没有增加浏览器密钥字段,也没有让 Runtime 回退到共享身份。四个已有变量供另行适配后的 Runtime 使用。 - -验证范围:基于 `2ffe6e14` 的本次功能,保留原有 contracts 迁移、OpenSpec 删除及 lockfile 修改。评审覆盖云厂商范围、带/不带 STS 的精确规范签名、小写响应结构、个人库不显式传 UserID、显式失败处理、密钥排除、归属/回读、创建结果不确定、取消重试及旧手动任务。云端调用全部使用模拟。 - -| 检查 | 结果 | 范围 | -| --- | --- | --- | -| Python 测试先行 | pass | 实施前 7 个新回归失败,覆盖 BytePlus 适配器/配置/重试。 | -| Python 定向测试 | pass | 12 项,包含后续新增的 STS 签名、取消及缺少 Key 不变更资源检查。 | -| 托管创建及启动回归 | pass | 双 worker,987 项。 | -| Studio API/RBAC 回归 | pass | 双 worker,Runtime Profile 路由与 Studio RBAC 共 205 项。 | -| 前端测试 | pass | 1,389 项 Node、96 项 React。首次 UI 命令工作目录错误,随后使用仓库 npm 命令全部通过;新的自动模式用例在修复 UI 前失败。 | -| Ruff 0.11.12 检查/格式及 Pyright | pass | 6 个修改的 Python 源码/测试文件;Pyright 0 错误/警告。 | -| 构建、国际化及发布引用 | pass | 两个前端目标完成构建;2 语言/21 命名空间;113 个打包文件/350 个引用。 | -| 浏览器 | pass | 真实弹窗、模拟 HTTP:海外自动模式、无手动字段、缺少 Key 错误可见、同请求重试、窄屏、Escape、旧手动恢复及北京文案。已移除临时文件及服务器。 | -| Wheel | pass | 本地构建,包含与源码字节一致的新适配器,没有发布。 | -| 限定范围密钥扫描 | pass | 修改/新增的源码、文档、测试及产物,无发现。 | -| 真实云端/Runtime OV 记忆 | not_run | 未授权创建付费资源,个人版 default 用户/Bearer/pre 路由仍属于另一仓库的工作。 | -| 全仓 pytest/pre-commit | not_run | 未要求提交;本次云厂商扩展由定向集成及完整前端检查覆盖,没有修改无关源码。 | -| Harness/sidecar 冒烟 | not_applicable | 未改变 harness、worker 或 sidecar 协议。 | - -AC-1–AC-5 已有离线实现证据,真实库响应细节、账号权益和记忆功能仍未验证。用户同意 Runtime 适配作为独立范围。未提交或推送。 - - -凭据决策已由以下设计替代:[API Key + endpoints](2026-10-14-inference-endpoints.zh.md). diff --git a/prd-spec/features/mpa-byteplus-openviking/2026-10-14-inference-endpoints.md b/prd-spec/features/mpa-byteplus-openviking/2026-10-14-inference-endpoints.md deleted file mode 100644 index 22980a769..000000000 --- a/prd-spec/features/mpa-byteplus-openviking/2026-10-14-inference-endpoints.md +++ /dev/null @@ -1,57 +0,0 @@ -# BytePlus OV inference credentials - -[中文版](2026-10-14-inference-endpoints.zh.md) - -Change ID: `mpa-byteplus-ov-inference-endpoints`. Date: 2026-10-14. Status: implemented; live acceptance pending. -Predecessor: [automatic creation](2026-10-14-automatic-creation.md). -Contract: [CON-18](../../../specs/studio-mpa-creation/README.md). - -## Background and approved scope - -The supplied overseas script supports ordinary inference credentials with `Source=volcengine`, `ApiKey`, and an `EndpointID` for each model. The initial local integration implemented only Coding Plan. The user explicitly requests removing Coding Plan and using ordinary API Key plus inference endpoints; the user also requests trying the domestic endpoint identifiers as overseas defaults. This approval replaces the predecessor's credential decision, not its personal-edition or separate Runtime-adaptation scope. - -## Goals, scenarios and requirements - -- FR-1: BytePlus automatic creation reuses the ordinary model key resolved by the existing MPA creation pipeline, including deployment-credential discovery. Do not read `OV_MODEL_API_KEY`, add a browser secret, or fall back to Coding Plan/AgentPlan. -- FR-2: Create a developer library with top-level and per-model `Source=volcengine`. Keep overseas model names `seed-2-0-lite` and `skylark-embedding-vision`; omit ModelVersion. Default EndpointID values are `doubao-seed-2-0-lite-260428` and `doubao-embedding-vision-251215`, explicitly requested for trial, not verified overseas. Server `OV_VLM_ENDPOINT_ID` and `OV_EMBEDDING_ENDPOINT_ID` override them. Each must be 1–128 ASCII letters, digits, hyphens, underscores or periods, starting with a letter/digit. Explicit blank or invalid values fail before cloud mutation, without echoing the value. -- FR-3: Save nonsecret Source/EndpointID in the existing OV intent. Retries with changed endpoints or a prior Coding Plan intent fail as configuration conflicts without creating or altering another library. Previously missing-key failures without an OV intent can retry. Unknown create outcomes, cancellation, retention, locking and readback validation remain unchanged. -- FR-4: Keep domestic model parameters, submitted manual requests, normal agents and four OV Runtime variables unchanged. Do not create model endpoints or change Runtime memory code. -- FR-5: Update localized UI and bilingual instructions to explain reuse of inference credentials and configurable endpoints, removing the Coding Plan prerequisite. Preserve layout and interactions. - -## Design, affected files and tasks - -T-1 (FR-1/2): Replace the dedicated key resolver in managed/openviking.py with a nonsecret endpoint resolver; config.py validates endpoints, service.py captures them before mutation and passes them to ensure_openviking, which uses the existing resolved model key. Optional keyword arguments preserve domestic callers. -T-2 (FR-3/4): Retain intent comparison and strict Get readback. Do not migrate existing Coding Plan libraries; their source cannot be silently changed. Only current-process endpoint choices are captured; a restart with changed configuration fails safely against a recorded intent. -T-3 (FR-5): Update locale strings, managed README pair, frontend README, CON-18 pair and generated web assets. -T-4: Test first, run managed/startup and UI regressions, Ruff/Pyright, build/i18n/assets checks, browser verification and secret scanning. No live cloud side effects during tests. - -## Review and acceptance - -User approval: explicit request to replace Coding Plan and follow domestic endpoint defaults, 2026-10-14. Direct design review (review-spec unavailable): scope, script schema, credential separation, safe failures, retry compatibility and bilingual equivalence reviewed; no design blockers. Resource IDs, secrets and account entitlements are not inferred. - -- AC-1: With no OV_MODEL_API_KEY, default BytePlus auto configuration succeeds; the created request contains the resolved ordinary key and both endpoints. Existing domestic regression remains passing. -- AC-2: Override, blank/malformed endpoints, changed endpoint retry, prior Coding Plan intent, remote endpoint mismatch, cancellation and ambiguous creation are covered; errors and registry never contain model keys. -- AC-3: UI no longer asks for Coding Plan; tests/build and focused browser verification pass. No new inputs, IME or keyboard behavior changes. -- AC-4: Both document languages, contract and generated assets agree; record actual checks and unverified live scope below. - -## Risks and verification record - -The requested default endpoint identifiers may not exist or be callable with the overseas account's key. Provider rejection must remain an explicit error; operators can set actual endpoints and retry only when no conflicting library was created. This implementation does not prove Runtime personal-user or overseas data-plane support. Live provisioning/memory tests are not_run: no cloud creation authorized for this code change and Runtime adaptation is separate. Commit/push are outside this request. - -2026-10-14: implementation checks pending; scope is the current uncommitted BytePlus OV diff on this branch. Existing unrelated contract relocation, OpenSpec deletion and lockfile edits are preserved. - -## Verification results (2026-10-14) - -Tested scope: current uncommitted BytePlus OV change based on `2ffe6e14`; no commit, push or cloud deployment. - -- pass: test-first OV regression reproduced 14 failures before implementation; after implementation OV-targeted tests passed (52). -- pass: `uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/cli/test_studio_startup_imports.py` — 1,004 passed, 4 warnings. Two workers match repository guidance. The initial added service test used an incorrect Profile constructor and domestic network fixtures; corrected those test fixtures, then reran the complete affected suite successfully. -- pass: `npm --prefix frontend test` — 1,389 Node and 96 React tests. -- pass: Ruff 0.11.12 check/format on five changed Python files; Pyright with `.venv/bin/python` — 0 errors/warnings. -- pass: `npm --prefix frontend run build`, `run check:i18n` (2 locales/21 namespaces), `run test:webui-assets` (113 files/350 references), `git diff --check`. -- pass: real Chromium using the actual dialog and mocked API responses: ordinary-key wording, no Coding Plan prerequisite, no manual secret fields, explicit endpoint error, same-request retry, manual recovery, domestic copy, Escape, and 390px viewport. The first temporary fixture omitted component theme imports; corrected the fixture and repeated the browser check. Temporary pages and their isolated Vite server are removed after checking. -- pass: bilingual requirement identifiers/local links; existing unrelated lockfile hash unchanged. -- not_run: full repository Python suite and all-files pre-commit because scope is the MPA integration and locale copy, no commit requested; affected regression and full frontend gates run instead. Harness/Codex smoke not_applicable: runtime/sidecar contracts unchanged. -- not_run: live overseas endpoint invocation, paid library creation and Runtime memory integration. Do not infer overseas validity of the requested default EndpointIDs from offline success. - -- pass: `uv run --extra dev pre-commit run gitleaks --files ` — source, tests, documents and built assets; no secrets detected. diff --git a/prd-spec/features/mpa-byteplus-openviking/2026-10-14-inference-endpoints.zh.md b/prd-spec/features/mpa-byteplus-openviking/2026-10-14-inference-endpoints.zh.md deleted file mode 100644 index 9afc5db43..000000000 --- a/prd-spec/features/mpa-byteplus-openviking/2026-10-14-inference-endpoints.zh.md +++ /dev/null @@ -1,57 +0,0 @@ -# BytePlus OV 推理凭据 - -[English](2026-10-14-inference-endpoints.md) - -变更 ID:`mpa-byteplus-ov-inference-endpoints`。日期:2026-10-14。状态:implemented;真实联调待验证。 -前置设计:[自动创建](2026-10-14-automatic-creation.zh.md)。 -契约:[CON-18](../../../specs/studio-mpa-creation/README.zh.md)。 - -## 背景与批准范围 - -用户提供的海外脚本支持普通推理凭据,每个模型传 `Source=volcengine`、`ApiKey` 和 `EndpointID`。初次本地接入只实现 Coding Plan。用户明确要求取消 Coding Plan,改用普通 API Key 加推理接入点,并要求先使用国内接入点标识作为海外默认值尝试。本次批准替代前置设计的凭据决策,个人版及 Runtime 单独适配范围不变。 - -## 目标、场景与要求 - -- FR-1:BytePlus 自动创建复用已有 MPA 创建流程解析的普通模型 Key,包括通过部署凭据发现。不读取 `OV_MODEL_API_KEY`,不新增浏览器密钥,不回退到 Coding Plan/AgentPlan。 -- FR-2:创建个人版库,顶层及模型凭据均使用 `Source=volcengine`。保留海外模型名 `seed-2-0-lite` 和 `skylark-embedding-vision`,不传 ModelVersion。EndpointID 默认值为 `doubao-seed-2-0-lite-260428` 和 `doubao-embedding-vision-251215`;这是用户指定的试用值,尚未在海外验证。服务端 `OV_VLM_ENDPOINT_ID` 和 `OV_EMBEDDING_ENDPOINT_ID` 可覆盖。每项须为 1–128 位 ASCII 字母、数字、连字符、下划线或点,且以字母或数字开头。显式空值或非法值在云资源变更前失败,错误不回显输入值。 -- FR-3:在现有 OV 意图中保存非密钥 Source/EndpointID。重试时接入点改变或已有 Coding Plan 意图,按配置冲突失败,不另建或修改库。此前仅缺少 Key、尚未记录 OV 意图的失败可以重试。创建结果未知、取消、保留资源、锁和回读校验保持不变。 -- FR-4:国内模型参数、已提交手动任务、通用智能体及四个 OV Runtime 变量保持不变。不创建模型接入点,不修改 Runtime 记忆代码。 -- FR-5:更新本地化界面和双语说明,解释复用推理凭据与可配置接入点,移除 Coding Plan 前置要求。布局和交互不变。 - -## 设计、影响文件与任务 - -T-1(FR-1/2):managed/openviking.py 的专用 Key 解析替换为非密钥接入点解析;config.py 校验接入点,service.py 在变更资源前捕获并传入 ensure_openviking,使用已有流程取得的普通模型 Key。新增可选关键字参数兼容国内调用方。 -T-2(FR-3/4):保留意图比较和严格 Get 回读。不迁移已有 Coding Plan 库,其来源不能静默修改。仅捕获当前进程的接入点选择;重启后配置改变,与已记录意图不一致时安全失败。 -T-3(FR-5):更新语言包、managed README 双语、frontend README、CON-18 双语及生成的网页产物。 -T-4:先测试,再运行 managed/startup 和前端回归、Ruff/Pyright、构建/国际化/产物检查、浏览器验证和密钥扫描。测试不产生真实云端副作用。 - -## 评审与验收 - -用户批准:2026-10-14 明确要求取消 Coding Plan 并沿用国内接入点默认值。直接设计评审(review-spec 不可用):已审查范围、脚本参数、凭据区分、安全失败、重试兼容及双语一致性,无设计阻塞。不推测资源 ID、密钥或账号权限。 - -- AC-1:没有 OV_MODEL_API_KEY 时,默认 BytePlus auto 配置成功;创建请求包含解析后的普通 Key 及两个接入点。国内既有回归保持通过。 -- AC-2:覆盖覆盖值、空/非法接入点、改变接入点后的重试、旧 Coding Plan 意图、远端接入点不符、取消及创建结果未知;错误及注册库不含模型 Key。 -- AC-3:界面不再要求 Coding Plan;测试/构建及定向浏览器验证通过。无新增输入项,不改变 IME 或键盘行为。 -- AC-4:双语文档、契约及生成产物一致;在下方记录实际检查及未验证的云端范围。 - -## 风险与验证记录 - -用户指定的默认接入点可能在海外账号不存在或该 Key 无权调用。服务端拒绝须明确报错;管理员可配置实际接入点,并仅在未创建冲突库时重试。本实现不证明 Runtime 支持个人用户和海外数据面。真实建库/记忆测试为 not_run:本次代码变更未授权云端创建,Runtime 适配是独立任务。提交/推送不在本次范围内。 - -2026-10-14:实现检查待执行;范围为当前分支未提交的 BytePlus OV 改动。保留无关的 contracts 迁移、OpenSpec 删除及 lockfile 修改。 - -## 验证结果(2026-10-14) - -验证范围:基于 `2ffe6e14` 的当前未提交 BytePlus OV 修改;未提交、推送或部署云资源。 - -- pass:先写 OV 回归,实施前复现 14 项失败;实施后 OV 定向测试 52 项通过。 -- pass:`uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/cli/test_studio_startup_imports.py` — 1,004 项通过、4 项警告。按仓库建议使用两个 worker。新增 service 测试最初使用了错误的 Profile 构造方式和国内网络夹具;修正测试夹具后,完整受影响套件重跑通过。 -- pass:`npm --prefix frontend test` — 1,389 项 Node 与 96 项 React 测试。 -- pass:五个改动 Python 文件的 Ruff 0.11.12 检查/格式,以及使用 `.venv/bin/python` 的 Pyright — 0 错误/警告。 -- pass:`npm --prefix frontend run build`、`run check:i18n`(2 个语言/21 个命名空间)、`run test:webui-assets`(113 个文件/350 个引用)、`git diff --check`。 -- pass:真实 Chromium 加载实际弹窗,API 响应为模拟:普通 Key 文案、无 Coding Plan 要求、无手填密钥字段、接入点错误、同请求重试、旧手动恢复、国内文案、Escape 和 390px 视口。首次临时夹具漏导入组件主题;补齐夹具后重新执行浏览器检查。检查后移除临时页面及独立 Vite 服务。 -- pass:双语需求标识/相对链接检查;已有无关 lockfile 哈希未变化。 -- not_run:全仓库 Python 及 all-files pre-commit,因为范围是 MPA 集成及文案、没有提交请求;执行了受影响回归及完整前端门禁。Harness/Codex smoke 为 not_applicable:未改变 runtime/sidecar 契约。 -- not_run:真实海外接入点调用、付费库创建及 Runtime 记忆联调。离线通过不能证明用户指定的默认 EndpointID 在海外有效。 - -- pass:`uv run --extra dev pre-commit run gitleaks --files ` — 覆盖源码、测试、文档及构建产物,未检测到密钥。 diff --git a/prd-spec/features/mpa-byteplus/2026-10-10-byteplus-creation.md b/prd-spec/features/mpa-byteplus/2026-10-10-byteplus-creation.md deleted file mode 100644 index da58590da..000000000 --- a/prd-spec/features/mpa-byteplus/2026-10-10-byteplus-creation.md +++ /dev/null @@ -1,94 +0,0 @@ -# BytePlus managed MPA creation - -[中文版](2026-10-10-byteplus-creation.zh.md) - -- Change ID: `mpa-byteplus-creation` -- Created/revised: 2026-10-10 -- Status: approved -- Contracts: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md) - -## Background and evidence - -Studio already selects BytePlus region `ap-southeast-1`, but MyAgents hides the MPA create action for BytePlus and the creation route rejects that provider. The managed profile, credential loader and VPC/APIG/AIDAP/model-key adapters still select domestic endpoints. AgentKit's installed SDK supports provider-scoped Runtime, Skills and Tools clients. The repository's Identity client already accepts a provider. The domestic workflow works and must remain compatible. - -## Goals and non-goals - -Enable the same three-step creation flow under BytePlus with isolated provider selection, credentials, configuration and endpoints. Preserve domestic defaults, permissions, resource sharing and retry behavior. No general-agent chat changes, credential persistence, automatic fallback to domestic services, existing-resource migration, image publication, or changes to sibling repositories. - -## Scenarios and requirements - -- **FR-1:** BytePlus Studio exposes MPA creation in its configured region; reject domestic regions and browser-supplied provider/account/endpoint overrides. -- **FR-2:** Keep the zero-argument domestic profile byte-for-byte equivalent in its values. BytePlus defaults to `ap-southeast-1`, the existing BytePlus ModelArk model/base, and `CLOUD_PROVIDER=byteplus` / `AGENTKIT_CLOUD_PROVIDER=byteplus`. Existing public images remain defaults because the user has no overseas images. Trusted server image overrides are permitted; image transport is separately verified. -- **FR-3:** Use only the selected provider's AK/SK/token or the explicit rotating IAM file. Verify account through that provider's STS on each call. Select the provider context inside SDK worker threads; use provider-specific VPC/ECS/APIG/AIDAP/Ark/IAM/Identity endpoints. Unsupported services or policies must report failure, never silently skip preparation or call domestic services. -- **FR-4:** Carry trusted provider identity into the fixed child runner and durable request fingerprint. Provider changes cannot resume the same task. Use separate default task/bootstrap SQLite files for BytePlus; unchanged domestic task inputs and defaults remain compatible. -- **FR-5:** Preserve shared admin/business workspaces and database isolation, locks, bounded retries, unknown-create recovery, cancellation and readiness semantics. BytePlus uses the same required IAM policy contract initially: unavailable policy names are explicit failures, not silently omitted permissions. -- **FR-6:** Runtime receives provider/model/region/APIG endpoint configuration. Optional OpenViking remains all-or-none. The dialog reuses current components and displays provider-specific console links without foreign account/resource IDs. - -## Design and affected files - -Add provider as a server-owned profile property with a domestic default. Reuse existing cloud-provider and ModelArk utilities. Introduce scoped managed-provider endpoint helpers for services not covered by those utilities. Make adapters accept an optional provider retaining existing signatures and defaults. The server selects the provider; browser inputs do not. BytePlus stored tasks add a nonsecret provider marker, passed through stdin to the runner; domestic payloads remain unchanged. Provider context is applied when Runtime/Skills/Tools clients are constructed, not as a machine-global mutation. The IAM singleton bypass remains intact. Apply the overseas APIG override recognized by the MPA image. - -Affected modules: `managed/{studio_profile,config,credentials,runtime,worker,network,network_cloud,gateway_cloud,pg_cloud,model_key,iam,service,tasks,runner}.py`, new managed provider helper, paired managed READMEs, `frontend/server/mpa_creation.py`, `veadk/cli/cli_frontend.py`, `MyAgents.tsx`, `MpaCreateDialog.tsx`, targeted tests and release assets. Component contract changes are limited to provider/configuration/isolation and creation availability. - -Overseas zone validation accepts the provider's `ap-southeast-1a` and `ap-southeast-1-a` formats without rewriting resource IDs. Domestic validation stays intact. `VEADK_MPA_BYTEPLUS_CREDENTIAL_FILE` explicitly selects a rotating file; an absent BytePlus environment key pair does not load the domestic default mounted file. Server image overrides and the optional AIDAP hostname are inspected locally; invalid AIDAP URLs, paths and user information fail before resource mutations. TOS mount endpoints are provider-specific. - -Live preflight correction: the initial regional BytePlus STS host timed out or disconnected. With the same process credentials, the exact AgentKit STS SDK successfully returned a nonempty identity from `open.byteplusapi.com`; use this host only for BytePlus. Domestic STS behavior is unchanged. Direct design review confirms this corrects FR-3 without expanding scope. The user submitted the real creation; diagnostic probes are read-only. - -Live IAM correction: the user retry passed account/model-key checks and failed in `iam_role`. A read-only `GetRole` through `iam.byteplusapi.com` times out; the same SDK, credentials, signing region and request through `open.byteplusapi.com` return `RoleNotExist`. Managed BytePlus IAM selects this working host while preserving the existing trusted `IAM_OPENAPI_HOST` override. Do not change the shared IAM helper or domestic host, permissions and reconciliation. Direct review confirms FR-3 scope and failure semantics are preserved. - -Live PG diagnosis: the user retry passed runtime-role preparation and failed at `admin_workspace`. Read-only DescribeWorkspaces times out through the regional AIDAP host; the same SDK/credentials/region succeed through `open.byteplusapi.com`. Managed BytePlus AIDAP defaults to that host, retaining validation of the trusted server override; domestic behavior is unchanged. The same-name admin Workspace is `CreateFailed` and has no ownership tags in the response. This correction neither deletes/adopts that resource nor clears uncertain creation state. Recovery of the failed resource needs separate evidence and authorization. Direct review confirms the endpoint correction is within FR-3. - -## Tasks and acceptance - -- **T-1 / AC-1:** Add failing provider/region/credentials/default-regression tests; domestic snapshot values and task behavior remain unchanged (FR-1–FR-4). -- **T-2 / AC-2:** Wire profile, credential and SDK/transport propagation; simulated calls assert hosts, signing region and provider without real secrets (FR-2–FR-3). -- **T-3 / AC-3:** Wire route/runner/UI; tests prove overseas entry, requests and links plus rejection, retry and isolation cases (FR-1, FR-4–FR-6). -- **T-4 / AC-4:** Run affected Python tests, Ruff/Pyright, frontend test/build/type/i18n/assets and browser flow/error/cancel/narrow-window checks. Retain domestic regressions (all FR). -- **T-5 / AC-5:** Record separate real-cloud evidence for images, service activation/quotas, IAM policies, AIDAP Workspace and IM Gateway APIs, readiness and chat. An isolated real creation needs explicit authorization. Simulations are not proof that the user's overseas account supports those services. - -## Risks and open questions - -Public BytePlus AgentKit documentation and installed SDK establish Runtime support. AIDAP documents exist, but full overseas Workspace and IM Gateway API availability, IAM policy catalog, model activation and image reachability remain unverified. Regional service adapters require live confirmation and may expose platform restrictions. The sibling MPA/Worker images may have additional domestic assumptions; inspection and explicit limitation reporting are required before claiming end-to-end success. No credentials, raw keys, production logs or private account IDs belong in evidence. - -## Review, approval and verification - -User approval: 2026-10-10, “国内流程是好的了,不要改坏了;帮我适配BytePlus”. User confirms no overseas image addresses. Direct design review performed because `review-spec` is unavailable: provider boundaries, domestic compatibility, trusted child propagation, cancellation, secrets and tests assessed. No new visual design; reuse the existing dialog/layout controls. Referenced frontend-design/ui-ux skills are unavailable locally, so no speculative layout redesign is included. Production changes followed failing regression tests. Final direct review checked configuration/transport/runner/UI consistency, rotating credentials, singleton avoidance, provider-context threading, existing resource hashes, terminal-state races and bilingual equivalence. - -Verification date: 2026-10-10. Scope: working diff against `5644e146c9dfff6d2e290b1663cb2ea41dbb6b91`; no commit or cloud mutation performed. Ruff/Pyright were invoked in temporary `uv --with` environments because they are not installed in the project environment. Ruff is pinned to the repository hook's `0.11.12`. - -| Check | Outcome | Evidence / limitation | -| --- | --- | --- | -| `uv run --extra dev pytest tests/integrations/mpa_managed -q` | pass | 752 tests, including 24 BytePlus cases; existing domestic creation, retry and resource tests retained. | -| `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` | fail | 6,951 passed, 1 failed, 53 skipped, 4 xfailed. Existing artifact writer MIME test expects `text/markdown`; local MIME detection yields `application/octet-stream`. Reproduced the same failure in an isolated unmodified HEAD checkout. Two workers were used as the repository default. | -| `npm --prefix frontend test` | pass | 1,377 Node tests and 67 Vitest tests. | -| `npm --prefix frontend run build` | pass | TypeScript compilation and production/main/website bundles; matching `veadk/webui` assets regenerated. | -| `npm --prefix frontend run check:i18n` | pass | 2 locales, 21 namespaces. | -| `npm --prefix frontend run test:webui-assets` | pass | 113 packaged files, 350 internal references. | -| `uv run --with ruff==0.11.12 ruff check ` | pass | All changed Python source and new regression tests. | -| `uv run --with pyright pyright ` | fail | Managed modules, creation route and new BytePlus tests have 0 errors. The CLI file has 37 errors also present in the unmodified HEAD file; no new diagnostics. | -| Isolated real-browser validation | pass | Actual dialog/routes with a fake child and disposable task DB; three steps, links, disabled fields, failure/retry, cancellation/terminal race, new request, successful close, Chinese/keyboard input and 420px layout checked. No cloud API used. Preview files and servers removed. | -| Codex smoke / harness coverage | not_applicable | No Codex runtime implementation or harness/sidecar event contract changed. | -| Pre-commit all-files and commit synchronization | not_run | No commit authorized or attempted; targeted lint, asset and secret checks are separate. | -| Actual BytePlus creation / account preflight | fail / pass | User-submitted creation stopped at `checking` before PG/VPC/APIG mutations because the regional STS transport failed. After the BytePlus-only host correction, the actual `RuntimeCloud.account_id()` path succeeds using the running Studio process credentials; identity values are not recorded. No automatic task retry was submitted. | -| Actual BytePlus IAM read-only diagnosis | fail / pass | User retry passed account/model-key checks then failed at `iam_role`. The original IAM endpoint timed out. After the managed-only host correction, actual `IamCloud.call("get_role")` returns recognized `RoleNotExist`; all 12 required system policies are present through read-only GetPolicy. Role/policy creation and attachment permissions are not yet verified; no cloud IAM write was submitted by the assistant. | -| Image pull, remaining APIs/policies, ready/chat | not_run | Awaiting user retry and live service preparation. Local simulations cannot satisfy AC-5. | -| Redacted secret scan of changed/new files | pass | Gitleaks scanned 1.43 MB in an isolated copy; no leaks found. | - -AC-1–AC-3 are implemented and verified in isolated tests. AC-4 is qualified by the unchanged full-regression and CLI baseline failures above. Two regression assertions failed against the regional STS host before the correction and passed afterwards; all 752 managed tests were rerun. AC-5 remains open; do not describe the overseas end-to-end path as verified. - -IAM follow-up verification (2026-10-10): two endpoint regression tests failed before the fix; 752 managed tests pass after the fix, including 24 BytePlus cases and trusted override preservation. Ruff check/format, Pyright for the changed helper/tests and diff whitespace pass. No frontend behavior changed in this follow-up, so existing browser/build evidence remains applicable. - -PG follow-up verification (2026-10-10): two regression assertions failed before the endpoint correction; afterwards 752 managed tests pass, as do Ruff check/format, Pyright for the changed helper/tests and diff whitespace. Live audit confirms the user-submitted CreateWorkspace returned outer InternalError with inner 40016:Forbidden because the caller lacks purchasing permission for the configuration; this is not a confirmed quota error. The Workspace is CreateFailed, its detail has no failure summary, and ownership tags are absent. AIDAP read connectivity is verified but actual PG creation remains blocked by purchasing permission. No deletion, bootstrap-state clearing or recreation was performed. Permission enablement and failed-resource recovery are required to continue AC-5; unaffected frontend checks do not need to be repeated. - - -## Commit preparation verification (2026-10-10) - -The user authorized committing and pushing the local changes. Fetched `origin` and `upstream`, then ran `git rebase --autostash upstream/main` on `feat/mpa-account-fixes-upstream-clean-20261010`. The branch was already current; all 159 pre-existing changed file states matched after synchronization. The baseline remains `5644e146c9dfff6d2e290b1663cb2ea41dbb6b91`. The only subsequent production adjustment is Ruff formatting of the creation-route registration in the CLI. - -- **pass:** `uv run --extra dev pre-commit run --all-files`: Ruff check/format and both secret scanners. The first run reformatted the CLI call; the repeat passed. -- **pass:** post-synchronization `uv run --extra dev pytest tests/integrations/mpa_managed -q --tb=short`: 782 passed, 5 existing warnings. -- **pass:** post-synchronization `npm --prefix frontend test`: 1,377 Node tests and 67 Vitest tests. -- **pass:** post-synchronization `npm --prefix frontend run check:i18n` and `npm --prefix frontend run test:webui-assets`: 2 locales / 21 namespaces and 113 files / 350 references. -- **not_run:** repeated full Python regression, frontend build/browser and Pyright. Synchronization preserved their previously tested source; the CLI adjustment changes formatting only. Existing full-regression MIME and CLI Pyright baseline failures remain documented above. No new cloud creation, publication or deployment is part of this commit request; overseas end-to-end readiness remains unverified. - -The commit includes BytePlus adaptation, terminal PG creation-failure recovery, paired documents, tests and matching generated frontend assets. No credentials, local state databases or production logs are included. diff --git a/prd-spec/features/mpa-byteplus/2026-10-10-byteplus-creation.zh.md b/prd-spec/features/mpa-byteplus/2026-10-10-byteplus-creation.zh.md deleted file mode 100644 index 887930993..000000000 --- a/prd-spec/features/mpa-byteplus/2026-10-10-byteplus-creation.zh.md +++ /dev/null @@ -1,95 +0,0 @@ -# BytePlus 托管 MPA 创建 - -[English](2026-10-10-byteplus-creation.md) - -- 变更 ID:`mpa-byteplus-creation` -- 创建/修订:2026-10-10 -- 状态:approved -- 契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md) - -## 背景与证据 - -Studio 已支持选择 BytePlus 地域 `ap-southeast-1`,但 MyAgents 在 BytePlus 下隐藏 MPA 创建入口,创建路由也拒绝该平台。托管配置、凭据加载及 VPC/APIG/AIDAP/模型 Key 适配器仍使用国内端点。已安装的 AgentKit SDK 支持按平台选择 Runtime、Skills 和 Tools 客户端;仓库 Identity 客户端已有 provider 参数。国内流程已经可用,必须保持兼容。 - -## 目标与非目标 - -让 BytePlus 使用相同三步创建流程,隔离平台选择、凭据、配置及端点。保留国内默认值、权限、资源共享及重试行为。不修改通用智能体聊天,不持久化凭据,不自动回退国内服务,不迁移现有资源,不发布镜像,不修改其他仓库。 - -## 场景与需求 - -- **FR-1:** BytePlus Studio 在配置地域显示 MPA 创建入口;拒绝国内地域及浏览器传入的平台、账号、端点覆盖。 -- **FR-2:** 无参数国内配置的值保持完全等价。BytePlus 默认 `ap-southeast-1`、已有 BytePlus ModelArk 模型/地址及 `CLOUD_PROVIDER=byteplus` / `AGENTKIT_CLOUD_PROVIDER=byteplus`。用户没有海外镜像,继续默认现有公开镜像;允许可信服务端镜像覆盖,拉取能力单独验证。 -- **FR-3:** 只使用所选平台 AK/SK/token 或明确配置的轮换 IAM 文件,每次调用通过对应 STS 校验账号。在 SDK 工作线程内选择平台上下文;使用对应 VPC/ECS/APIG/AIDAP/Ark/IAM/Identity 端点。不支持的服务或策略必须明确失败,不能跳过准备或调用国内服务。 -- **FR-4:** 将可信平台信息传入固定子进程及持久请求指纹;平台变化不能恢复同一任务。BytePlus 默认任务和 PG 引导 SQLite 文件单独存储;国内输入及默认路径保持兼容。 -- **FR-5:** 保留管理/业务 Workspace、库级隔离、锁、有限重试、未知创建结果恢复、取消及就绪语义。BytePlus 初始沿用所需 IAM 策略契约;策略名称不可用时明确失败,不能静默遗漏权限。 -- **FR-6:** 向 Runtime 注入平台、模型、地域及 APIG 端点。OpenViking 继续可选且全填或全不填。弹窗复用已有组件,资源链接对应平台,不携带其他账号或资源 ID。 - -## 设计与影响文件 - -Profile 增加服务端拥有的平台属性,默认国内。复用已有平台及 ModelArk 工具,对未覆盖服务增加托管平台端点助手。适配器增加可选 provider,保留现有签名和默认行为。平台由服务端选择,浏览器不可指定。BytePlus 持久任务增加非敏感平台标记,经 stdin 传给 runner;国内负载保持不变。构造 Runtime/Skills/Tools 客户端时应用平台上下文,不改机器全局配置。保留 IAM 绕过单例的机制,注入 MPA 镜像已识别的海外 APIG 覆盖。 - -影响模块:`managed/{studio_profile,config,credentials,runtime,worker,network,network_cloud,gateway_cloud,pg_cloud,model_key,iam,service,tasks,runner}.py`、新增平台助手、托管模块双语 README、`frontend/server/mpa_creation.py`、`veadk/cli/cli_frontend.py`、`MyAgents.tsx`、`MpaCreateDialog.tsx`、相关测试和发布产物。组件契约变化限于平台/配置/隔离与创建可用性。 - -真实预检修正:最初使用的 BytePlus 地域 STS 地址超时或断开连接。使用同一服务进程凭据,AgentKit STS SDK 在 `open.byteplusapi.com` 成功返回非空身份;仅 BytePlus 改用该地址,国内 STS 行为不变。直接设计评审确认这是 FR-3 的修正,未扩大范围。用户提交了真实创建,诊断探测仅为只读。 - -海外可用区校验接受 `ap-southeast-1a` 与 `ap-southeast-1-a`,不改写资源 ID,国内校验保持不变。`VEADK_MPA_BYTEPLUS_CREDENTIAL_FILE` 显式选择轮转文件;没有 BytePlus 环境凭据时,不读取国内默认挂载文件。服务端镜像覆盖项与可选 AIDAP 主机名仅在本地检查;无效 URL、路径、用户信息在资源变更前失败。TOS 挂载地址按平台选择。 - -真实 IAM 修正:用户重试通过账号及模型 Key 检查,在 `iam_role` 失败。只读 `GetRole` 请求发往 `iam.byteplusapi.com` 时超时;同一 SDK、凭据、签名地域及请求改用 `open.byteplusapi.com` 后返回 `RoleNotExist`。托管 BytePlus IAM 选择已验证地址,并保留可信 `IAM_OPENAPI_HOST` 覆盖。共享 IAM 助手、国内地址、权限和协调逻辑不变。直接评审确认保持 FR-3 范围及失败语义。 - -真实 PG 诊断:用户重试通过运行角色准备,在 `admin_workspace` 失败。只读 DescribeWorkspaces 使用地域 AIDAP 地址超时;同一 SDK/凭据/地域改用 `open.byteplusapi.com` 后正常返回。BytePlus 托管 AIDAP 默认改用该地址,继续保留并校验可信服务端主机覆盖;国内行为不变。查询发现同名管理 Workspace 处于 `CreateFailed` 且未返回属主标签,因此本次修正不删除、不接管该资源,也不清空不确定创建记录。失败资源恢复另需证据和授权。直接评审确认地址修正属于 FR-3。 - -## 任务与验收 - -- **T-1 / AC-1:** 先增加失败的平台/地域/凭据/默认值回归测试,国内配置快照和任务行为保持不变(FR-1–FR-4)。 -- **T-2 / AC-2:** 接入配置、凭据及 SDK/传输传播;模拟调用验证 host、签名地域、平台,不使用真实密钥(FR-2–FR-3)。 -- **T-3 / AC-3:** 接入路由/runner/UI;验证海外入口、请求和链接,以及拒绝、重试、隔离场景(FR-1、FR-4–FR-6)。 -- **T-4 / AC-4:** 执行相关 Python 测试、Ruff/Pyright、前端测试/构建/类型/国际化/产物及浏览器正常、错误、取消、窄窗检查,保留国内回归(全部 FR)。 -- **T-5 / AC-5:** 单独记录镜像、服务开通/配额、IAM 策略、AIDAP Workspace/IM Gateway API、就绪和聊天的真实云验证。隔离的真实创建需要明确授权。模拟不能证明用户海外账号具备这些服务。 - -## 风险与待确认事项 - -BytePlus AgentKit 官方文档及已安装 SDK 可证明 Runtime 支持。AIDAP 有文档,但完整海外 Workspace 和 IM Gateway API 可用性、IAM 策略目录、模型开通及镜像可达性尚未验证。地域服务适配器需要实测确认,可能遇到平台限制。MPA/Worker 镜像还可能包含国内假设,宣称端到端成功前必须检查并报告限制。证据不得包含凭据、原始 Key、生产日志或私有账号信息。 - -## 评审、批准与验证 - -用户于 2026-10-10 批准:“国内流程是好的了,不要改坏了;帮我适配BytePlus”,并确认没有海外镜像地址。`review-spec` 不可用,已直接评审平台边界、国内兼容、可信子进程传播、取消、密钥与测试。无新视觉设计,复用当前弹窗和布局控件;本地未找到所引用 frontend-design/ui-ux 技能,因此不包含推测性布局重设计。生产改动在失败测试后实施。生产改动在失败回归测试之后实施。最终直接评审核对了配置/传输/runner/UI 一致性、轮转凭据、单例绕过、线程内平台上下文、已有资源哈希、终态竞态及双语等价性。 - - -验证日期:2026-10-10。范围:相对 `5644e146c9dfff6d2e290b1663cb2ea41dbb6b91` 的工作区差异;未提交代码,未执行云资源变更。项目环境没有 Ruff/Pyright,采用临时 `uv --with` 环境;Ruff 固定为仓库钩子的 `0.11.12`。 - -| 检查 | 结果 | 证据 / 限制 | -| --- | --- | --- | -| `uv run --extra dev pytest tests/integrations/mpa_managed -q` | pass | 752 项,包括 24 项 BytePlus 用例;保留国内创建、重试及资源测试。 | -| `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` | fail | 6,951 通过、1 失败、53 跳过、4 xfailed。已有 artifact writer MIME 测试预期 `text/markdown`,本机返回 `application/octet-stream`;在隔离的未修改 HEAD 中复现。按仓库默认使用两个 worker。 | -| `npm --prefix frontend test` | pass | 1,377 项 Node 测试及 67 项 Vitest 测试。 | -| `npm --prefix frontend run build` | pass | TypeScript 编译及 production/main/website 构建,重新生成匹配的 `veadk/webui` 产物。 | -| `npm --prefix frontend run check:i18n` | pass | 2 种语言、21 个命名空间。 | -| `npm --prefix frontend run test:webui-assets` | pass | 113 个发布文件、350 处内部引用。 | -| `uv run --with ruff==0.11.12 ruff check ` | pass | 全部修改的 Python 源码及新增回归测试。 | -| `uv run --with pyright pyright ` | fail | 托管模块、创建路由及新增 BytePlus 测试 0 错误;CLI 文件的 37 项错误在未修改 HEAD 中同样存在,没有新增诊断。 | -| 隔离真实浏览器验证 | pass | 实际弹窗/路由使用模拟子进程及临时任务库;核对三步、链接、禁用字段、失败/重试、取消/终态竞态、新请求、成功关闭、中文/键盘输入及 420px 布局。未调用云 API,已移除预览文件和服务。 | -| Codex smoke / harness coverage | not_applicable | 未修改 Codex runtime 实现或 harness/sidecar 事件契约。 | -| 全文件 pre-commit 及提交同步 | not_run | 未获提交授权,也未尝试提交;定向 lint、产物及密钥检查单独执行。 | -| 真实 BytePlus 创建 / 账号预检 | fail / pass | 用户提交的创建因地域 STS 传输失败,在 `checking` 阶段停止,尚未修改 PG/VPC/APIG。仅修正 BytePlus 地址后,实际 `RuntimeCloud.account_id()` 使用运行中 Studio 凭据核验成功,不记录身份值。未自动提交任务重试。 | -| 真实 BytePlus IAM 只读诊断 | fail / pass | 用户重试通过账号/模型 Key 检查,在 `iam_role` 失败。原 IAM 地址超时;仅修正托管地址后,实际 `IamCloud.call("get_role")` 返回可识别的 `RoleNotExist`;只读 GetPolicy 确认 12 个所需系统策略均存在。角色/策略创建及绑定权限尚未验证,助手未提交云端 IAM 写操作。 | -| 镜像拉取、后续 API/策略、就绪/聊天 | not_run | 等待用户重试及真实服务准备,模拟不能满足 AC-5。 | -| 修改/新增文件脱敏密钥扫描 | pass | Gitleaks 扫描隔离副本 1.43 MB,未发现泄漏。 | - -AC-1–AC-3 已实现并通过隔离验证。AC-4 受上述未修改基线的完整回归及 CLI 错误限制。STS 修正前两个回归断言失败,修正后通过,并重新执行全部 752 项托管测试。AC-5 仍未完成,不能描述为海外端到端流程已验证。 - -IAM 后续验证(2026-10-10):修正前两个接口回归测试失败;修正后 752 项托管测试通过,包括 24 项 BytePlus 用例及可信覆盖保留。修改的助手/测试通过 Ruff check/format、Pyright 及差异空白检查。本次后续修正不改变前端行为,已有浏览器/构建证据仍适用。 - -PG 后续验证(2026-10-10):地址修正前两个回归断言失败;修正后 752 项托管测试通过,Ruff check/format、修改助手/测试的 Pyright 和差异空白检查通过。真实审计确认用户提交的 CreateWorkspace 外层 InternalError 包含内部 40016:Forbidden,原因是调用者没有购买当前配置的权限;并非已确认的配额错误。Workspace 状态为 CreateFailed,详情没有失败摘要,属主标签未返回。AIDAP 查询可达性已验证,实际 PG 创建仍被购买权限阻塞。没有执行删除、清除引导记录或重新创建。权限开通及失败资源恢复后才能继续 AC-5;无需为此重跑未受影响的前端验证。 - - -## 提交前验证(2026-10-10) - -用户已授权提交并推送本地修改。获取 `origin` 和 `upstream` 后,在 `feat/mpa-account-fixes-upstream-clean-20261010` 执行 `git rebase --autostash upstream/main`。分支已经同步;同步后原有 159 个改动文件状态全部一致。基础版本仍为 `5644e146c9dfff6d2e290b1663cb2ea41dbb6b91`。随后唯一的生产代码调整是 Ruff 格式化 CLI 中的创建路由注册调用。 - -- **pass:** `uv run --extra dev pre-commit run --all-files`:Ruff 检查/格式化及两项敏感信息扫描。首次运行格式化了 CLI 调用,再次运行全部通过。 -- **pass:** 同步后 `uv run --extra dev pytest tests/integrations/mpa_managed -q --tb=short`:782 项通过,5 条已有警告。 -- **pass:** 同步后 `npm --prefix frontend test`:1,377 项 Node 测试和 67 项 Vitest 测试。 -- **pass:** 同步后 `npm --prefix frontend run check:i18n` 及 `npm --prefix frontend run test:webui-assets`:2 种语言 / 21 个命名空间,113 个文件 / 350 个引用。 -- **not_run:** 重复执行完整 Python 回归、前端构建/浏览器及 Pyright。同步保留了此前已验证的源码,CLI 调整仅改变格式。完整回归的 MIME 及 CLI Pyright 基线失败仍按上文记录。本次提交请求不涉及新的云端创建、发布或部署;海外全流程就绪仍未验证。 - -提交包含 BytePlus 适配、PG 创建失败终态恢复、双语文档、测试及配套前端构建产物,不包含凭据、本地状态数据库或生产日志。 diff --git a/prd-spec/features/mpa-channels-automation/2026-09-16-mpa-channels-automation.md b/prd-spec/features/mpa-channels-automation/2026-09-16-mpa-channels-automation.md deleted file mode 100644 index 9af06f28c..000000000 --- a/prd-spec/features/mpa-channels-automation/2026-09-16-mpa-channels-automation.md +++ /dev/null @@ -1,103 +0,0 @@ -# MPA channels in Automations - -[中文](2026-09-16-mpa-channels-automation.zh.md) - -- Change ID: `mpa-channels-automation` -- Created/revised: 2026-09-16 -- Status: implemented -- Component: [MPA channels](../../../specs/mpa-channels/README.md) -- Baseline: `1263a268`; branch: `feat/mpa-channels-automation` - -## Background and scope - -`AgentWorkspace.tsx` currently mounts `RuntimeChannels` inside MPA agent details. Automations already groups Feishu bot creation and website integration under Messaging channels. `getRuntimes` supports authorized scope, `agentCategory=mpa`, region and cursor pagination. `DeploymentSelect` already supports search, keyboard navigation and pagination. - -Move the existing management flow into Automations so users can choose an existing MPA agent in one place. Retain the Feishu bot creation and website integration cards. Do not create/deploy runtimes, alter provider APIs, migrate bindings, change permissions or modify MPA backend behavior. - -## Requirements and scenarios - -- FR-1: Messaging channels includes an adjacent `MPA智能体消息渠道` card (`mpa-channels`). Opening it shows a back action, page title and searchable MPA Runtime selector. Remove the old agent-detail section. -- FR-2: Request `getRuntimes` with `agentCategory: mpa`, the authorized Runtime scope and all regions. Paginate explicitly without dropping later results. Reject explicitly non-MPA records; accept omitted category only because the request explicitly filters MPA. Identify options by region plus Runtime ID; display the name once, followed by description and creator/creation time; selected-target context retains region and ID. Require explicit selection before mounting channels. -- FR-3: Reuse `RuntimeChannels` for Feishu, WeCom and DingTalk with unchanged quick/manual flows. Key the panel by region and Runtime ID; selecting another Runtime unmounts the old panel, aborts polling/SDK work, clears transient credentials and ignores late results. Existing durable bindings remain intact. Cancelling frontend work does not revoke a server-side registration already accepted. -- FR-4: Only Studio admin/super_admin can enter management; other roles see a permission explanation and issue no list/channel requests. The existing backend authorization remains authoritative. Failed requests, empty results, loading, no search matches and unsupported Runtime capabilities have distinct messages; errors allow retry. Pagination errors retain prior options and the current binding panel. -- FR-5: Follow current Automations layout, semantic colors, self-authored SVG icons and existing search/select behavior. Cover keyboard/IME, long names and narrow windows. Both locales must be complete. - -## Design and contract impact - -Add a dedicated automation definition and page under `frontend/src/automations/`; wire its discriminated union, registry, catalog icon and App routing. Pass role and Runtime scope from App. Reuse `DeploymentSelect`; its existing global styles are already loaded by Studio. Keep list loading within the page, guarded by AbortController and request identity. Load subsequent pages on demand with an explicit load-more/retry action, including while a search has no matches. Search filters loaded names, IDs and regions and explains that more pages may exist. No automatic selection or authorization. - -The page owns navigation/list/selection; `RuntimeChannels` retains provider state and adds an optional header flag to avoid duplicate page headings. The selection never enters browser persistence. Switching role/scope resets page state through a keyed child. No secrets are requested by the selector. No Python API, deployment payload, database or event contract changes. Update MPA navigation and CON-14 in both component languages. The old detail selection is component-local, with no public deep-link contract. - -## Tasks and acceptance - -| Task | Requirement | Acceptance / verification | -| --- | --- | --- | -| T-1 | FR-1, FR-5 | AC-1: bilingual card, correct routing/back navigation, old detail entry removed; catalog/navigation tests | -| T-2 | FR-2, FR-4 | AC-2: scoped MPA paging/search, explicit selection, loading/empty/error/retry/permission tests | -| T-3 | FR-3 | AC-3: actual channel panel remounts; old pending responses cannot affect selected Runtime; existing provider regression tests | -| T-4 | FR-5 | AC-4: browser verifies normal flow, cancellation, keyboard/IME and 390px layout; i18n and build/assets gates | -| T-5 | All | AC-5: bilingual specs and frontend README reconciled; diff review and secret scan | - -Affected files: automation types/registry/new definition/page/styles/icon; `Applications.tsx`, `App.tsx`, `AgentWorkspace.tsx`, `RuntimeChannels.tsx`; both automation locale files; targeted tests; `frontend/README.md`; MPA specs; generated `veadk/webui` assets. - -Commands: targeted Vitest tests (`mpaChannelsAutomation.test.tsx`, `runtimeChannels.test.tsx`), `npm --prefix frontend test`, `npm --prefix frontend run check:i18n`, `npm --prefix frontend run build`, `npm --prefix frontend run test:webui-assets`, `git diff --check`, repository pre-commit secret scan. Browser checks use isolated fake Runtime/channel APIs, never real provider credentials. - -## Risks and review - -Search covers loaded pages; an explicit load-more action avoids silently hiding later agents. Runtime reachability/capability is checked after selection by the existing component. Changing agents aborts frontend work but does not roll back an accepted backend operation. Live provider connectivity is outside this navigation change and is not implied by mocked checks. - -2026-09-16: User approved the described move, requested an isolated branch, then explicitly requested pushing it and starting development. Branch pushed before edits; original branch preserved. Direct design review checked scope, bilingual equivalence, compatibility, authorization, pagination, stale responses, secrets and testability; no blockers. Referenced frontend-design/ui-ux-pro-max and review-spec skills were not found in repository/user skill locations; review uses `frontend/SPEC.md` and repository review criteria directly. No new design system or dependencies. - -## Verification record - -Execution date: 2026-09-16. Tested scope: the local diff from `1263a268` on `feat/mpa-channels-automation`, including new files and rebuilt WebUI assets. T-1 through T-5 and AC-1 through AC-5 are complete. - -| Check | Outcome | Evidence | -| --- | --- | --- | -| Test-first selector suite | fail (expected) | New component import did not exist before implementation. | -| `./frontend/node_modules/.bin/vitest run --root frontend tests/mpaChannelsAutomation.test.tsx tests/runtimeChannels.test.tsx --maxWorkers 1` | pass | 62 tests; includes real channel component remount, role/scope changes, polling and late SDK credentials. | -| `npm --prefix frontend test` | pass | 1207 tests; replaced two obsolete detail-channel tests with one migration assertion. An additional old section-union assertion failed initially and was updated to the approved navigation. | -| `node --test frontend/tests/agentWorkspace.test.mjs frontend/tests/applications.test.mjs` | pass | 38 tests after final formatting/cleanup. | -| `npm --prefix frontend run check:i18n` | pass | 2 locales, 21 namespaces. | -| `npm --prefix frontend run build` | pass | TypeScript and both app/widget builds; existing large-chunk advisory remains. | -| `npm --prefix frontend run test:webui-assets` | pass | 104 packaged files, 248 references. | -| Browser with isolated fake APIs | pass | Catalog card, explicit selection, Chinese search and keyboard selection, back-to-channels category, QR panel cleared on target switch, manual binding success, paging, unsupported image, loading/empty/error/retry/permission states. At 390×844, body width 390 and content client/scroll width 347/347. Production styles loaded without component-preview theme tokens. | -| IME composition | pass | Unit event test verifies Enter during composition cannot choose a Runtime; browser exercised Chinese text entry. | -| `uv run --extra dev pre-commit run --all-files` | pass | Ruff check/format and repository Gitleaks. Explicit `--files` scan also passed for new source, tests and PRDs (Python hooks correctly skipped for those files). | -| Additional default Gitleaks scan including changed build assets/tests | fail (reviewed false positive) | 83 changed/new files scanned with redaction; one generic-api-key finding in bundled MarkdownPromptEditor is the Lexical selection anchor/focus key expression. Exact flagged expression is present in baseline. No rule weakened; no credential found on review. | -| `git diff --check -- ':!veadk/webui'` | pass | Source/docs/tests whitespace clean. Full diff check reports whitespace inside generated vendor template strings; baseline and current widget each contain 36 such lines with identical trailing contexts. Generated semantics retained. | -| Bilingual identifiers and relative links | pass | FR/T/AC identifiers match; all paired PRD/spec links resolve. | -| Python/runtime/cloud/provider suites | not_applicable / not_run | No Python or backend contract changes. Browser API simulations do not prove live provider delivery; no live cloud/provider operations performed. | - -Final review found no blocking defects in target identity, category filtering, access enforcement, pagination, cancellation, credential lifetime or navigation. Existing provider binding behavior remains owned by RuntimeChannels. The shared DeploymentSelect uses its existing ProjectPreview stylesheet through an explicit import. Temporary browser fixtures, preview server and tab were cleaned up. Branch baseline was pushed as requested; development changes are left uncommitted for review. - - -### Option details refinement (2026-09-16) - -User requested replacing the repeated option name with description, creator and creation time. Approved within FR-2/FR-5: keep the title once, use a second line for description and a third line for creator/time. Missing/invalid values have localized placeholders; format valid timestamps in the current locale. Selected target still shows region/ID. Add an optional `metadata` string to DeploymentSelectOption without changing existing callers; its tooltip includes all option text. No API, binding, permission or storage changes. Direct review found no contract/security blockers. Add option-content and missing-value regressions, then rerun frontend tests, i18n, build/assets and isolated browser layout checks. Refinement checks: not_run before editing. - -Refinement verification (2026-09-16): test-first 2 failed / 13 passed, then 64 channel/automation tests passed; full frontend suite 1207 passed; i18n, app/widget build and 104-file/248-reference asset validation passed. Browser verified three-line metadata, localized dates, missing-value placeholders and long description/creator wrapping. At 390×844, body width is 390 and option client/scroll widths are both 292; full details remain available in the tooltip. Existing options without metadata retain their previous layout. No live Runtime/provider operations. Temporary fixture/server/tab removed. - -### Compact creator and Runtime context (2026-09-16) - -User refined the option footer to creator name (fallback “Unknown creator”) | relative creation time, with no field-name prefixes, and requested region and Runtime ID inside each option again. Keep the name and description rows; show the compact creator/time row followed by region/Runtime ID. Reuse `formatRelativeTimeLabel`; invalid/missing times show an em dash. This supersedes the preceding absolute-time footer decision. Extend the existing metadata string with a line break and let the shared metadata style preserve that break; callers without metadata remain unchanged. FR-2/FR-5 and CON-14 are updated accordingly. User request authorizes this display refinement; review found no API, permission, state or credential impact. Verification pending. - -Compact-footer verification (2026-09-16): regression-first 2 failed / 13 passed; final channel/automation suite 64 passed, full frontend suite 1207 passed, i18n/build/assets passed. Browser confirmed “未知创建者 | 14 小时前” and region/Runtime ID on separate footer lines. At 390×844, body width 390; both option client/scroll widths 292. Missing time uses an em dash; relative time uses the existing shared helper. Temporary preview files, server and tab removed. Changes remain uncommitted. - - -### Upstream rebase and PR verification (2026-09-18) - -The user authorized rebasing the feature branch onto `superops-team/veadk-python` and opening an upstream PR. Rebased the three feature commits onto `main` at `34658d75`, retaining upstream MPA runtime-filter performance changes. Preserved the original local history in `backup/mpa-channels-before-upstream-rebase-20260918`. Conflicts affected generated `veadk/webui` assets only; source merged cleanly and the final app/widget assets were rebuilt from the combined source. No component contract changes were introduced by the rebase. - -Verification scope: rebased source at `63e6c9bd` plus regenerated WebUI assets and this bilingual verification record. Checks ran on 2026-09-18: - -- **pass**: `npm --prefix frontend test` — 1207 tests. -- **pass**: `./frontend/node_modules/.bin/vitest run --root frontend --environment jsdom tests/mpaChannelsAutomation.test.tsx tests/runtimeChannels.test.tsx src/adk/channels.test.ts --maxWorkers 1` — 65 tests. An initial run without the browser environment failed the API test because `window` was unavailable; the explicit jsdom run passed. -- **pass**: `uv run --extra dev pytest tests/test_mpa_channel_proxy_policy.py tests/cli/test_frontend_runtime_proxy.py tests/integrations/test_mpa_provision_env.py -n 2` — 89 tests; two workers used for the affected proxy/deployment regression scope. The initial invocation used an incorrect provisioning-test filename and collected zero tests; only the corrected run counts as verification. -- **pass**: frontend i18n consistency, production app/widget build, and `test:webui-assets` — 104 packaged files and 248 references. -- **pass**: `uv run --extra dev pre-commit run --all-files` — Ruff checks, formatting, and repository secret scanning. -- **fail (upstream baseline)**: Pyright on `veadk/cli/cli_frontend.py`, `veadk/integrations/mpa/channel_proxy.py`, `veadk/integrations/mpa/mpa_provision.py`, and `tests/test_mpa_channel_proxy_policy.py` reports 36 errors, all in `cli_frontend.py`. The upstream `34658d75` version has exactly the same diagnostic severity/rule/message multiset: zero added or removed diagnostics. Pyright was supplied temporarily with `uv run --with pyright pyright --pythonpath .venv/bin/python`; project dependencies and global configuration were unchanged. -- **pass**: isolated browser smoke verifies compact creator/relative-time metadata, region/Runtime ID, Chinese search and keyboard selection, default quick configuration, switching to Feishu manual credentials, and the 390×844 layout (document width and scroll width both 390). APIs used synthetic fixtures; temporary fixture files, tab, viewport override, and server were cleaned up. -- **not_run**: live provider/cloud operations and full Python repository regression; this rebase retained production source behavior, and affected proxy/deployment tests were run. Prior full interaction coverage remains documented above. - -Remaining limitation: the upstream Python typing baseline is not clean. The PR must disclose it rather than report Pyright as passing. diff --git a/prd-spec/features/mpa-channels-automation/2026-09-16-mpa-channels-automation.zh.md b/prd-spec/features/mpa-channels-automation/2026-09-16-mpa-channels-automation.zh.md deleted file mode 100644 index 852d8e881..000000000 --- a/prd-spec/features/mpa-channels-automation/2026-09-16-mpa-channels-automation.zh.md +++ /dev/null @@ -1,103 +0,0 @@ -# 自动化中的 MPA 消息渠道 - -[English](2026-09-16-mpa-channels-automation.md) - -- 变更 ID:`mpa-channels-automation` -- 创建/修订日期:2026-09-16 -- 状态:implemented -- 组件:[MPA 消息渠道](../../../specs/mpa-channels/README.zh.md) -- 基线:`1263a268`;分支:`feat/mpa-channels-automation` - -## 背景与范围 - -`AgentWorkspace.tsx` 当前在 MPA 智能体详情内挂载 `RuntimeChannels`。自动化已将飞书机器人创建和网站集成归入消息渠道。`getRuntimes` 支持授权范围、`agentCategory=mpa`、地域和游标分页。`DeploymentSelect` 已支持搜索、键盘导航和分页。 - -将现有管理流程移至自动化,让用户在统一入口选择已有 MPA 智能体。保留飞书机器人创建与网站集成卡片。不创建或部署 Runtime,不修改渠道 API、不迁移绑定、不改变权限、不修改 MPA 后端行为。 - -## 需求与场景 - -- FR-1:消息渠道增加并列的 `MPA智能体消息渠道` 卡片(`mpa-channels`)。打开后展示返回操作、页面标题及可搜索的 MPA Runtime 选择器。移除智能体详情中的原栏目。 -- FR-2:使用 `agentCategory: mpa`、授权 Runtime 范围及全部地域调用 `getRuntimes`。显式分页,不丢弃后续结果。拒绝明确非 MPA 的记录;只有请求已明确筛选 MPA 时才接受缺少分类的记录。以地域和 Runtime ID 共同标识选项,名称只显示一次,下方显示描述和创建者/创建时间;选中目标的信息仍保留地域和 ID。用户明确选择后才挂载渠道页面。 -- FR-3:复用 `RuntimeChannels` 的飞书、企微、钉钉及极速/手动流程。以地域和 Runtime ID 为面板 key;切换后卸载旧面板,取消轮询/SDK 工作,清理临时凭据并忽略迟到结果。已有持久绑定保留。取消前端工作不撤销服务端已经接受的注册。 -- FR-4:仅 Studio admin/super_admin 可进入管理;其他角色显示权限说明,不请求列表和渠道接口。后端原有鉴权仍为最终权限边界。请求失败、空结果、加载、搜索无匹配、Runtime 能力不支持分别展示;错误可重试。分页失败保留已有选项和当前绑定面板。 -- FR-5:遵循当前自动化布局、语义颜色、自绘 SVG 图标及现有搜索/选择交互。覆盖键盘/输入法、长名称和窄窗口。完整支持两种语言。 - -## 设计与契约影响 - -在 `frontend/src/automations/` 新增独立定义和页面,接入类型联合、注册表、目录图标和 App 路由。App 传入角色及 Runtime 范围。复用 `DeploymentSelect`,Studio 已加载其全局样式。页面内加载列表,通过 AbortController 和请求身份防止过期写入。后续页按需加载,并提供显式加载更多/重试操作,包括搜索无匹配时。搜索已加载的名称、ID、地域,并说明可能仍有未加载的页。不自动选择或触发授权。 - -页面负责导航、列表、选择;`RuntimeChannels` 继续负责渠道状态,增加可选标题开关避免重复页标题。选择不写入浏览器存储。角色/范围变化通过子组件 key 重置页面。选择器不请求密钥。不影响 Python API、部署参数、数据库及事件契约。同步更新组件两种语言的导航与 CON-14。原详情选项为组件内状态,无公开深链接契约。 - -## 任务与验收 - -| 任务 | 需求 | 验收/验证 | -| --- | --- | --- | -| T-1 | FR-1、FR-5 | AC-1:双语卡片、正确路由/返回、移除旧入口;目录/导航测试 | -| T-2 | FR-2、FR-4 | AC-2:限定范围的 MPA 分页/搜索、明确选择、加载/空/错误/重试/权限测试 | -| T-3 | FR-3 | AC-3:真实渠道组件重新挂载,旧请求不影响新 Runtime;现有渠道回归测试 | -| T-4 | FR-5 | AC-4:浏览器验证正常流程、取消、键盘/输入法及 390px 布局;国际化、构建、产物检查 | -| T-5 | 全部 | AC-5:双语契约和前端 README 同步;差异审查与密钥扫描 | - -影响文件:自动化类型/注册表/新增定义/页面/样式/图标;`Applications.tsx`、`App.tsx`、`AgentWorkspace.tsx`、`RuntimeChannels.tsx`;两种自动化语言文件;定向测试;`frontend/README.md`;MPA 契约;生成的 `veadk/webui` 产物。 - -命令:定向 Vitest(`mpaChannelsAutomation.test.tsx`、`runtimeChannels.test.tsx`)、`npm --prefix frontend test`、`npm --prefix frontend run check:i18n`、`npm --prefix frontend run build`、`npm --prefix frontend run test:webui-assets`、`git diff --check`、仓库 pre-commit 密钥扫描。浏览器检查使用隔离的模拟 Runtime/渠道 API,不使用真实渠道凭据。 - -## 风险与评审 - -搜索覆盖已加载页;显式加载更多避免静默遗漏后续智能体。选中后由已有组件检测 Runtime 可达性/能力。切换会取消前端操作,但不会回滚已接受的后端操作。真实渠道连通性不属于本次导航变更,模拟检查不能证明真实连通性。 - -2026-09-16:用户认可上述迁移方案,要求独立分支,随后明确要求推送并开始开发。编辑前已推送分支,原分支保留。直接设计评审覆盖范围、双语一致性、兼容性、权限、分页、过期响应、密钥及可测性,无阻塞项。仓库/用户技能路径未找到引用的 frontend-design、ui-ux-pro-max 和 review-spec,直接依据 `frontend/SPEC.md` 和仓库评审标准审查。不引入新设计体系或依赖。 - -## 验证记录 - -执行日期:2026-09-16。验证范围:`feat/mpa-channels-automation` 相对 `1263a268` 的本地差异,包含新增文件及重建 WebUI 产物。T-1 至 T-5、AC-1 至 AC-5 均已完成。 - -| 检查 | 结果 | 证据 | -| --- | --- | --- | -| 先写选择器测试 | fail(预期) | 实施前新组件不存在,导入失败。 | -| `./frontend/node_modules/.bin/vitest run --root frontend tests/mpaChannelsAutomation.test.tsx tests/runtimeChannels.test.tsx --maxWorkers 1` | pass | 62 项;覆盖真实渠道组件重新挂载、角色/范围变化、轮询及迟到 SDK 凭据。 | -| `npm --prefix frontend test` | pass | 1207 项;两条旧详情渠道测试合并为一条迁移断言。另一条旧栏目联合类型断言初次失败,已按批准的导航更新。 | -| `node --test frontend/tests/agentWorkspace.test.mjs frontend/tests/applications.test.mjs` | pass | 最终格式化/清理后 38 项通过。 | -| `npm --prefix frontend run check:i18n` | pass | 2 种语言、21 个命名空间。 | -| `npm --prefix frontend run build` | pass | TypeScript 及应用/挂件两次构建通过;保留原有大分块提示。 | -| `npm --prefix frontend run test:webui-assets` | pass | 104 个产物、248 个引用。 | -| 隔离模拟 API 浏览器验证 | pass | 目录卡片、明确选择、中文搜索及键盘选择、返回消息渠道分类、切换目标清理二维码、手动绑定成功、分页、不支持镜像、加载/空/失败/重试/权限状态。在 390×844 下,body 宽 390,内容 client/scroll 宽均为 347。使用正式样式,未加载组件预览主题变量。 | -| 输入法组合输入 | pass | 单元事件测试验证组合输入期间 Enter 不会选择 Runtime;浏览器覆盖中文输入。 | -| `uv run --extra dev pre-commit run --all-files` | pass | Ruff 检查/格式及仓库 Gitleaks。另用明确 `--files` 扫描新增源码、测试、PRD 也通过(这些文件无 Python,Python 钩子正确跳过)。 | -| 额外默认 Gitleaks 扫描,包含变更产物/测试 | fail(已审查为误报) | 脱敏扫描 83 个修改/新增文件;唯一 generic-api-key 命中为 MarkdownPromptEditor 中 Lexical 选区的 anchor/focus key 表达式。完全相同表达式已存在于基线。未削弱规则;审查未发现凭据。 | -| `git diff --check -- ':!veadk/webui'` | pass | 源码/文档/测试空白检查通过。完整差异检查提示生成依赖模板字符串中的空白;基线和当前挂件均有 36 行,行尾上下文完全一致。保留生成代码语义。 | -| 双语标识及相对链接 | pass | FR/T/AC 标识一致,成对 PRD/契约链接全部存在。 | -| Python/Runtime/真实云/渠道测试 | not_applicable / not_run | 无 Python 或后端契约变更。模拟 API 浏览器检查不证明真实渠道投递;未执行真实云或渠道操作。 | - -最终审查未发现目标身份、分类过滤、权限、分页、取消、凭据生命周期和导航的阻塞缺陷。现有渠道绑定行为继续由 RuntimeChannels 负责。共享 DeploymentSelect 显式导入现有 ProjectPreview 样式。临时浏览器测试文件、预览服务及标签页已清理。已按要求推送分支基线;开发修改保留为未提交状态,供审阅。 - - -### 选项详情调整(2026-09-16) - -用户要求将选项内重复的名称替换为描述、创建者和创建时间。属于 FR-2/FR-5 的已批准调整:标题仅保留一次,第二行显示描述,第三行显示创建者/时间。缺失或非法值使用本地化占位,有效时间按当前语言格式化。选中目标仍显示地域/ID。DeploymentSelectOption 增加可选 `metadata` 字符串,既有调用不变;提示包含全部选项信息。不改变 API、绑定、权限或存储。直接评审无契约/安全阻塞。新增内容及缺省值回归,随后重跑前端测试、国际化、构建/产物及隔离浏览器布局检查。编辑前本次检查:not_run。 - -本次调整验证(2026-09-16):先写测试时 2 失败/13 通过,实现后渠道/自动化 64 项通过;前端全量 1207 项通过;国际化、应用/挂件构建和 104 文件/248 引用产物检查通过。浏览器验证三层信息、本地化日期、缺省占位、长描述和创建者换行。在 390×844 下,body 宽 390,选项 client/scroll 宽均为 292;完整信息可在提示中查看。未提供 metadata 的现有选项保留原布局。未操作真实 Runtime/渠道。临时文件/预览服务/标签页已清理。 - -### 简洁创建信息与 Runtime 信息(2026-09-16) - -用户进一步指定选项页脚为“创建者姓名(缺失显示未知创建者) | 相对创建时间”,不显示字段前缀,并要求恢复各选项中的地域和 Runtime ID。保留名称和描述行,其后依次显示简洁创建信息与地域/Runtime ID。复用 `formatRelativeTimeLabel`,缺失或非法时间显示破折号。此决定替代前文绝对时间方案。现有 metadata 字符串增加换行,共享样式保留该换行;未提供 metadata 的调用方不变。同步 FR-2/FR-5 与 CON-14。用户请求授权此展示细化;评审确认不影响 API、权限、状态或凭据。验证待完成。 - -简洁页脚验证(2026-09-16):先写回归时 2 失败/13 通过;最终渠道/自动化 64 项、前端全量 1207 项通过,国际化/构建/产物通过。浏览器确认“未知创建者 | 14 小时前”及单独一行的地域/Runtime ID。390×844 下 body 宽 390,两个选项 client/scroll 宽均为 292。缺失时间显示破折号,相对时间复用已有公共方法。临时预览文件、服务和标签页已清理。修改仍未提交。 - - -### 上游 rebase 与 PR 验证(2026-09-18) - -用户已授权将功能分支 rebase 到 `superops-team/veadk-python` 并向上游创建 PR。三个功能提交已基于 `main` 的 `34658d75` 重放,保留上游 MPA Runtime 过滤性能优化。原本地历史保存在 `backup/mpa-channels-before-upstream-rebase-20260918`。冲突仅涉及生成的 `veadk/webui` 资源;源码自动合并成功,最终应用及组件资源已从合并后的源码重新构建。本次 rebase 未改变组件契约。 - -验证范围:重放后的 `63e6c9bd` 源码、重新生成的 WebUI 资源及本双语验证记录。检查执行日期为 2026-09-18: - -- **pass**:`npm --prefix frontend test`,1207 项测试通过。 -- **pass**:`./frontend/node_modules/.bin/vitest run --root frontend --environment jsdom tests/mpaChannelsAutomation.test.tsx tests/runtimeChannels.test.tsx src/adk/channels.test.ts --maxWorkers 1`,65 项测试通过。最初未指定浏览器环境时,API 测试因缺少 `window` 失败;显式使用 jsdom 后通过。 -- **pass**:`uv run --extra dev pytest tests/test_mpa_channel_proxy_policy.py tests/cli/test_frontend_runtime_proxy.py tests/integrations/test_mpa_provision_env.py -n 2`,89 项测试通过;使用两个 worker 执行受影响的代理和部署回归范围。首次调用使用了错误的部署测试文件名,未收集到测试;仅将修正路径后的结果计入验证。 -- **pass**:前端 i18n 一致性、生产应用及组件构建、`test:webui-assets`,104 个打包文件和 248 个内部引用通过验证。 -- **pass**:`uv run --extra dev pre-commit run --all-files`,Ruff 检查、格式检查及仓库密钥扫描通过。 -- **fail(上游基线)**:对 `veadk/cli/cli_frontend.py`、`veadk/integrations/mpa/channel_proxy.py`、`veadk/integrations/mpa/mpa_provision.py` 和 `tests/test_mpa_channel_proxy_policy.py` 执行 Pyright,报告 36 个错误,均位于 `cli_frontend.py`。上游 `34658d75` 版本的诊断严重度、规则和消息多重集完全相同:无新增或消除诊断。通过 `uv run --with pyright pyright --pythonpath .venv/bin/python` 临时提供检查器,未修改项目依赖或全局配置。 -- **pass**:隔离浏览器冒烟验证了精简创建者和相对时间、地域及 Runtime ID、中文搜索和键盘选择、默认极速配置、切换飞书手动凭据配置,以及 390×844 窄屏布局(文档宽度和滚动宽度均为 390)。API 使用虚构数据;临时页面文件、标签页、视口覆盖及服务已清理。 -- **not_run**:真实渠道和云端操作、Python 全仓回归;本次 rebase 保留生产源码行为,已运行受影响的代理和部署测试。此前完整交互覆盖记录仍见上文。 - -剩余限制:上游 Python 类型检查基线存在错误,PR 必须披露,不能宣称 Pyright 通过。 diff --git a/prd-spec/features/mpa-collaborating-agents/2026-10-20-collaborating-agents-detail-tab.md b/prd-spec/features/mpa-collaborating-agents/2026-10-20-collaborating-agents-detail-tab.md deleted file mode 100644 index 1bf518db2..000000000 --- a/prd-spec/features/mpa-collaborating-agents/2026-10-20-collaborating-agents-detail-tab.md +++ /dev/null @@ -1,47 +0,0 @@ -# MPA collaborating Agents detail tab - -## Background, scope and approval -The existing change exposes management from the chat information rail. On 2026-10-20 the user explicitly requested moving it to Agents → MPA → selected agent, in a tab below Basic, affecting MPAs only. This request approves the relocation; existing administrator authorization is retained. - -## Requirements and scenarios -1. MPA administrators with a Runtime see Basic followed by Collaborating Agents. The manager renders inline only when selected. -2. General agents, drafts, and non-administrators gain no tab or management requests. Unsupported external focus returns to Basic. -3. Switching tabs, Runtime, region, or permission unmounts the manager, aborts pending requests and clears transient forms/results. Late responses cannot update another target. -4. Remove the chat-rail entry. Keep CRUD, errors, revision checks, unknown-outcome recovery and server authorization unchanged. - -## Design and affected files -Reuse CollaboratorManager in AgentWorkspace, keyed by region and Runtime ID. App passes an explicit admin/super_admin permission; AgentTopology loses the unused entry slot. Reuse ResourceDetailLayout navigation and existing localized title. Update frontend README, the bilingual component contract, tests and packaged web assets. - -## Non-goals and risks -No API, persistence, cloud resources, general-agent navigation, chat protocol or authorization changes. Primary risks are stale target state and accidentally exposing the tab to general agents; test both. Existing unrelated lock-file changes are preserved. - -## Design review -Direct review (review-spec skill unavailable): boundaries, permissions, cancellation, failure semantics and bilingual equivalence reviewed. No blockers. The [component contract](../../../specs/mpa-collaborating-agents/README.md) updates UI location/lifecycle only; server contracts remain unchanged. This supersedes the location in the [original design](2026-10-20-collaborating-agents.md). - -## Tasks, tests and acceptance -- Add failing navigation, permission, general-agent and target-switch regressions; implement relocation. -- Run targeted tests, full frontend tests, build, i18n and packaged-asset checks. -- Browser-check normal, loading, empty, error/retry, cancellation, keyboard/IME and narrow layouts using synthetic data; no live cloud writes. -- Synchronize bilingual docs and verification. Accept when the tab is below Basic only for authorized MPAs, old entry is removed and requests remain target-scoped. - -## Verification -Implementation complete. Direct code review found no blocking issue in permission/category gating, lazy mounting, target keys or cleanup. Existing server authorization is unchanged. - -- pass: regression-first run produced 3 expected failures; targeted Vitest checks then passed (30 tests). -- pass: `npm --prefix frontend test` (1,389 Node tests and 127 Vitest tests). -- pass: `npm --prefix frontend run build` (TypeScript and both production bundles); existing bundle-size warnings remain. -- pass: `npm --prefix frontend run check:i18n` (2 locales, 22 namespaces). -- pass: `npm --prefix frontend run test:webui-assets` (114 files, 351 references). -- pass: real browser with an isolated synthetic-fetch fixture: tab below Basic, inline loading/empty/unsupported states, refresh recovery, add/cancel form, Chinese text input, Tab focus, general-agent navigation and 390px layout. Fixture and temporary server removed after verification. -- pass: automated tests verify abort on leaving, Runtime/region switch and permission loss, plus absence for general agents and non-admins. -- not_run: physical IME composition; Chinese text and keyboard focus were checked, composition behavior was not changed. -- not_applicable: Python/Ruff/Pyright and cloud integration for this UI-only relocation; no Python or cloud mutation in this task. Existing collaboration backend changes predate this task. -- pass: paired documents, referenced paths and diff whitespace reviewed. - -Tested scope: working-tree relocation on feat/from-upstream-main-20261020, 2026-10-20. No commit or push authorized. - -## Follow-up: input focus appearance (2026-10-20) - -The user requested removing the extra dark outline inside the name and credential fields. Browser inspection reproduced an inner `2px` workspace outline and the existing outer wrapper outline. A scoped CSS override removes only the inner focus-visible outline of collaborator input components. The outer keyboard focus indicator remains. No API, state or component contract change; this is a visual-only correction under the formatting exception. No new behavior-mirroring test was added. Real browser checks cover name/password input and Tab navigation; an unrelated input retains its original outline. Verification results follow below. - -Verification: pass — `npm --prefix frontend test` (1,389 Node + 129 Vitest), `npm --prefix frontend run build`, `npm --prefix frontend run test:webui-assets` (114 files/351 references), browser focus checks and `git diff --check`. Existing bundle-size warnings remain. No cloud operations, commit or push. diff --git a/prd-spec/features/mpa-collaborating-agents/2026-10-20-collaborating-agents-detail-tab.zh.md b/prd-spec/features/mpa-collaborating-agents/2026-10-20-collaborating-agents-detail-tab.zh.md deleted file mode 100644 index 2fab6080c..000000000 --- a/prd-spec/features/mpa-collaborating-agents/2026-10-20-collaborating-agents-detail-tab.zh.md +++ /dev/null @@ -1,47 +0,0 @@ -# MPA 协作 Agent 详情页 Tab - -## 背景、范围与批准 -现有改动将管理入口放在聊天信息侧栏。2026-10-20 用户明确要求移至“智能体 → MPA → 选中智能体”的详情页,在“基本信息”下增加 Tab,仅影响 MPA。该请求批准本次位置调整;保留现有管理员权限。 - -## 要求与场景 -1. 有 Runtime 的 MPA,管理员看到基本信息及紧随其后的协作 Agent。选中后才内联挂载管理内容。 -2. 普通智能体、草稿及非管理员不增加 Tab,也不发管理请求。不支持的外部定位回到基本信息。 -3. 切换 Tab、Runtime、地域或权限时卸载管理器,取消请求并清空临时表单和结果。迟到响应不能更新其他目标。 -4. 删除聊天侧栏入口。CRUD、错误、版本检查、结果未知后的恢复及服务端鉴权不变。 - -## 设计与影响文件 -在 AgentWorkspace 复用 CollaboratorManager,以地域及 Runtime ID 为 key。App 显式传递 admin/super_admin 权限;AgentTopology 移除无用入口插槽。复用 ResourceDetailLayout 导航及已有翻译标题。更新 frontend README、双语组件契约、测试及打包资源。 - -## 非目标与风险 -不修改 API、持久化、云资源、普通智能体导航、聊天协议或鉴权。主要风险是目标切换后残留状态以及误向普通智能体开放 Tab,均加入测试。保留无关的锁文件改动。 - -## 设计审查 -直接审查(review-spec 技能不可用):已核对边界、权限、取消、失败语义及双语一致性,无阻塞项。[组件契约](../../../specs/mpa-collaborating-agents/README.zh.md)仅更新 UI 位置和生命周期;服务端契约不变。本设计替代[原设计](2026-10-20-collaborating-agents.zh.md)中的入口位置。 - -## 任务、测试与验收 -- 先添加会失败的导航、权限、普通智能体及目标切换回归测试,再实现迁移。 -- 执行定向测试、完整前端测试、构建、国际化及打包资源检查。 -- 使用模拟数据进行浏览器正常、加载、空态、错误重试、取消、键盘/IME、窄屏检查;不写真实云资源。 -- 同步双语文档及验证记录。验收:仅授权 MPA 在基本信息下看到新 Tab,旧入口移除,请求保持目标隔离。 - -## 验证 -实现完成。直接代码审查未发现权限/分类限制、按需挂载、目标 key 或清理行为的阻塞问题。现有服务端鉴权不变。 - -- pass:先执行回归测试,出现 3 项预期失败;实现后定向 Vitest 通过(30 项)。 -- pass:`npm --prefix frontend test`(1,389 项 Node 测试及 127 项 Vitest 测试)。 -- pass:`npm --prefix frontend run build`(TypeScript 及两套生产构建);仍有已有的大包体积警告。 -- pass:`npm --prefix frontend run check:i18n`(2 种语言、22 个命名空间)。 -- pass:`npm --prefix frontend run test:webui-assets`(114 个文件、351 条引用)。 -- pass:真实浏览器配合隔离模拟 fetch 页面:基本信息下的新 Tab、内联加载/空态/不支持状态、刷新恢复、添加/取消表单、中文输入、Tab 焦点、普通智能体导航及 390px 布局。验证后移除临时页面及服务。 -- pass:自动化测试验证离开页面、Runtime/地域切换、权限撤销时取消请求,以及普通智能体和非管理员不显示入口。 -- not_run:真实输入法组合输入;已检查中文文本和键盘焦点,未改动组合输入行为。 -- not_applicable:本次仅 UI 位置调整,Python/Ruff/Pyright 和云端集成不适用;本次未修改 Python 或云资源。已有协作后端改动早于本次任务。 -- pass:已核对双语文档、引用路径及差异空白。 - -测试范围:feat/from-upstream-main-20261020 工作区中的位置调整,日期 2026-10-20。未授权提交或推送。 - -## 后续:输入框焦点外观(2026-10-20) - -用户要求移除名称和密钥输入框内部额外的深色边框。浏览器复现确认内部有详情页施加的 `2px` 轮廓,外层已有组件焦点框。局部 CSS 覆盖仅移除协作输入组件内部的 focus-visible 轮廓,保留外层键盘焦点提示。不修改 API、状态或组件契约,属于纯视觉格式修正的例外。未新增镜像实现的行为测试。真实浏览器已核对名称/密码输入及 Tab 导航,对照输入框仍保留原轮廓。验证结果见下文。 - -验证:pass — `npm --prefix frontend test`(1,389 项 Node + 129 项 Vitest)、`npm --prefix frontend run build`、`npm --prefix frontend run test:webui-assets`(114 文件/351 引用)、浏览器焦点检查及 `git diff --check`。仍有已有的大包体积警告。未执行云资源操作、提交或推送。 diff --git a/prd-spec/features/mpa-collaborating-agents/2026-10-20-collaborating-agents.md b/prd-spec/features/mpa-collaborating-agents/2026-10-20-collaborating-agents.md deleted file mode 100644 index a9ca6f992..000000000 --- a/prd-spec/features/mpa-collaborating-agents/2026-10-20-collaborating-agents.md +++ /dev/null @@ -1,80 +0,0 @@ -# Per-MPA collaborating Agents - -> Credential input/storage decisions below are superseded by the 2026-10-20 direct-credentials design; other registration behavior is unchanged. - -Date: 2026-10-20. Status: implemented; local verification recorded below, not released. - -## Approval and scope -The user approved implementing registration under one selected MPA, then explicitly included the Studio configuration page. This approval covers additive management APIs and persistence. Task dispatch, streaming/event collection, remote deployment, Main/Worker changes and `dispatch_a2a_task` belong to another owner and are excluded. - -## Evidence and intent -Runtime subagent bindings represent Worker execution handles, not durable external Agent registration. Studio already authorizes Runtime access and proxies requests with server-resolved credentials. Reuse this boundary. No global catalog or automatic sharing between MPAs. - -## Requirements and tasks -- R1/T1: persist named registrations scoped by MPA, with description, Agent Card URL, enabled flag, authentication configuration and revision; list/create/update/delete. Deletion never deletes the target Agent. -- R2/T2: explicit connection check fetches a bounded A2A Agent Card, records name, description, skills, endpoint and protocol version. A successful check means discovery only, never task execution. Edits invalidate prior checks; concurrent edits/probes use compare-and-swap revisions. -- R3/T3: none, bearer and API-key authentication use Runtime environment references restricted to `MPA_COLLABORATOR_` names. Values never enter database records, API responses, browser persistence or logs. Missing secrets fail explicitly. Configure values through existing Runtime environment management; this version does not create a secret vault or accept raw secret input. -- R4/T4: Studio selected-MPA information panel opens configuration; supports loading/empty/error/retry, creation, editing, enable/disable, validation and removal confirmation. Switching Runtime closes/aborts outstanding work; late responses cannot overwrite another selection. Old Runtime 404 reports upgrade required, not empty success. Admin-only consistent with channel administration. -- R5/T5: internal server-side `resolve_for_dispatch(mpa_id, registration_id)` returns a frozen configuration snapshot only for enabled, successfully checked registrations. It resolves current secret references, without dispatching a task or exposing credentials in HTTP. Dispatcher must use the guarded transport again at execution and enforce its own caller authorization. - -## Contract -Management base: `/api/v1/studio/collaborators`, scoped by the serving Runtime's configured `MPA_AGENT_ID`, never browser-provided ownership. Dedicated `X-MPA-Studio-Key` must match persisted current Runtime credential; Studio only injects it server-side for administrators. GET list; POST create; PATCH/DELETE `/{id}` and POST `/{id}/check` require positive `revision` (DELETE query). Version mismatch 409, missing record 404, unauthenticated 401, missing durable backend/Runtime identity 503. Public output excludes all credential values. - -Database: additive `mpa_collaborating_agents` table with composite MPA/id key, JSON configuration and monotonically increasing revision. No volatile fallback. Existing Runtime business PG owns records. Migration is additive; rollback keeps the table and deploys the previous binaries. Older Studio/Runtime remain unchanged. - -Connection check: HTTPS only; no userinfo/query/fragment; fixed explicit Card URL. Resolve and pin DNS for the request; reject non-global addresses by default and cloud metadata/loopback/link-local always. Explicit operator-configured `MPA_COLLABORATOR_PRIVATE_ORIGINS` permits private VPC destinations only for listed HTTPS origins. No redirects or environment proxies, bounded timeout and response size, TLS verification on. Card execution endpoint must have the same origin as the configured Card. Store only bounded allowlisted metadata, never arbitrary Card JSON. Check unsupported transport/auth schemes explicitly; no OAuth refresh in this version. - -## Design review -Reviewed ownership, credential handling, SSRF/redirect/DNS rebinding, cancellation, cross-MPA queries, stale results, old Runtime compatibility and bilingual equivalence. Existing dependencies suffice. Runtime registration and Studio proxy tests must establish authorization independently. UI skills referenced by frontend/SPEC.md are absent from this checkout and local skills directories; apply the repository's existing component/foundation standards directly. No speculative executor integration. - -## Acceptance / verification -| Requirement | Evidence | Status | -| --- | --- | --- | -| R1 | Durable store isolation, restart, CAS and delete tests | verified locally | -| R2/R3 | Invalid card, SSRF, authentication, timeout, redaction and stale-check tests | verified locally | -| R4 | Studio proxy tests, UI interaction tests, frontend test/build and browser checks | verified locally | -| R5 | Internal snapshot enablement/revision/auth tests | verified locally | -Required Runtime gates: `make lint`, `make test`; Studio affected Python tests/Ruff/Pyright, `npm --prefix frontend test`, `npm --prefix frontend run build`. Actual results are recorded below. No production release, cloud mutations or live third-party credential tests authorized by this implementation request. - -## Configuration and dispatcher handoff - -As a Studio administrator, select an MPA and open **Collaborating Agents** in its conversation information rail, then **Add Agent**. Enter its name, purpose, full HTTPS Agent Card URL and authentication configuration. Save, then check the connection. Capabilities come from Card `skills`; they are not a second manually maintained list. This entry appears for MPAs only. - -For Bearer authentication, an operator first configures `MPA_COLLABORATOR_RESEARCH` in the Runtime environment. Enter only that variable name in Studio. API key authentication additionally takes a target-specific `X-` header name. Secret values never pass through this form. Apply environment changes through the platform's Runtime release/restart process and check again. - -For a private endpoint, also set `MPA_COLLABORATOR_PRIVATE_ORIGINS=https://research.internal.example:443` on the Runtime and provide network connectivity and trusted TLS. Allowlisting an origin does not create VPCs, routes or firewall rules. Public endpoints need no allowlist. Cross-origin execution endpoints, plaintext HTTP, OAuth refresh, cloud AK/SK signing and non-JSONRPC transports are not supported in this version. - -Example registration body (through the authenticated Studio proxy): -```json -{ - "name": "Research assistant", - "description": "Find sources and organize evidence", - "card_url": "https://research.example/.well-known/agent-card.json", - "enabled": true, - "auth_type": "bearer", - "secret_env": "MPA_COLLABORATOR_RESEARCH", - "header_name": "" -} -``` -The response includes `id`, `mpa_id`, `revision` and `check`. New entries are `not_checked`; `POST /{id}/check` takes the current `revision` and returns a new one. `verified` means Card discovery and format compatibility only, not successful task execution. Editing invalidates the check. If a mutation's outcome is unknown after a connection failure, the UI requires refresh before another mutation, avoiding blind duplicate creation. - -Handoff to the `dispatch_a2a_task` owner: -```python -# Use an injected CollaboratorService; mpa_id comes from trusted execution context. -snapshot = await service.resolve_for_dispatch(mpa_id, registration_id) -# snapshot: mpa_id / registration_id / revision / endpoint / -# headers (server-only, hidden from repr) / protocol_version -``` -The dispatcher must authorize the caller for that MPA, revalidate destination/DNS safety at request time, and implement tasks, SSE, cancellation, results and trace. Never expose `headers` in model context, execution traces or HTTP responses; do not use an unguarded HTTP client. Disabled or unverified records return `registration_not_ready`. This change does not inject registrations into the model or make them callable automatically. - -Persistence borrows the existing Runtime channel-store engine: business PostgreSQL in production, durable SQLite files for local development, with in-memory SQLite rejected. The additive `0013_collaborating_agents.sql` migration also has the existing first-use table-creation path; restricted DB users can have the migration applied by an operator first. Records are scoped to the current MPA; there is no global catalog or new PG instance. Rollback retains the table and registrations. - -## Verification record (2026-10-20) - -- Runtime: Python 3.12; **23 passed** for the new registration module, including PostgreSQL 16 migration SQL, persistence after reconnect, MPA isolation and competing revision updates; **38 passed** including metadata/migration contract regressions. New-module statement coverage **93.77% (286/305)** and combined branch coverage **91.99%**. `make lint`, focused Ruff/Pyright, diff whitespace checks and production-code secret scan passed. -- Full Runtime `make test`: initial **9294 passed / 249 skipped / 1 failed** due to an obsolete local Codex entry (`Codex 0.155.1 is required`). After installing pinned 0.155.1, the affected native test **passed**; the final full rerun completed with **9298 passed / 250 skipped / 0 failed** (635.44 seconds, exit 0). -- Studio: proxy policies and the entire Runtime proxy test module **133 passed**; frontend Node **1389 passed**, Vitest **123 passed (9 files)**; TypeScript `tsc --noEmit`, frontend production build and affected Python Ruff passed. The new Python helper has zero Pyright errors. The whole `cli_frontend.py` has **37 existing Pyright errors**, identical to HEAD after normalizing filenames and line numbers, with no added diagnostics. Full Studio Python type checking is therefore not claimed as passing. -- Browser: real configuration component against local Runtime registration API/storage; create, Card check, edit/disable, removal confirmation, close/reopen, 360px narrow scrolling, keyboard authentication selection and missing-secret feedback passed. The fixture mocks the remote Card and local authentication; Studio BFF authorization is covered separately by integration tests. This is not cloud end-to-end acceptance. -- Not verified / excluded: real external credentials or private connectivity, production release, cloud mutations, full remote formal/coverage CI gates, and `dispatch_a2a_task` execution. Existing full-suite event-loop cleanup warnings remain in reports; they are not described as business failures. No commit, push or deployment performed. - -UI location superseded by the [detail-tab design](2026-10-20-collaborating-agents-detail-tab.md). diff --git a/prd-spec/features/mpa-collaborating-agents/2026-10-20-collaborating-agents.zh.md b/prd-spec/features/mpa-collaborating-agents/2026-10-20-collaborating-agents.zh.md deleted file mode 100644 index 375410283..000000000 --- a/prd-spec/features/mpa-collaborating-agents/2026-10-20-collaborating-agents.zh.md +++ /dev/null @@ -1,80 +0,0 @@ -# 按 MPA 管理协作 Agent - -> 以下凭据输入/存储决策已由 2026-10-20 direct-credentials 方案取代;其他注册行为保持。 - -日期:2026-10-20。状态:已实现,本地验证见下文,未发布。 - -## 授权与范围 -用户已授权为指定 MPA 注册其他 Agent,并明确要求本次包含 Studio 配置页面。范围包含新增管理 API 和持久化。任务派发、流式事件回收、目标 Agent 部署、Main/Worker 改动和 `dispatch_a2a_task` 由其他负责人实现,本次不做。 - -## 现状与目标 -Runtime 的 subagent binding 是 Worker 执行句柄,不是外部 Agent 注册信息。Studio 已有 Runtime 访问授权和服务端注入凭据的代理,复用该边界。不建立全局目录,不在 MPA 间自动共享。 - -## 需求与任务 -- R1/T1:按 MPA 持久化协作 Agent 的名称、描述、Agent Card URL、启用开关、鉴权配置、版本;支持增删改查。删除仅解除关联,不删除目标 Agent。 -- R2/T2:显式连接验证,有界读取 A2A Agent Card,记录名称、描述、技能、端点和协议版本。验证通过只代表发现成功,不代表任务执行成功。修改使原验证失效;并发编辑和验证采用版本条件更新。 -- R3/T3:支持无认证、Bearer 和 API Key,密钥引用限定为 Runtime 中 `MPA_COLLABORATOR_` 前缀的环境变量。密钥值不进入数据库、响应、浏览器持久化或日志;缺失时报错。通过现有 Runtime 环境变量管理配置密钥,本版不新建密钥库、不接受明文密钥输入。 -- R4/T4:选中 MPA 的 Studio 信息面板打开配置,支持加载、空状态、错误重试、新建、编辑、启停、验证和确认移除。切换 Runtime 关闭并取消请求,过期响应不能覆盖新选择。旧 Runtime 的 404 明确提示升级,不伪装为空列表。与渠道管理一致,仅管理员可管理。 -- R5/T5:服务端内部 `resolve_for_dispatch(mpa_id, registration_id)` 仅对启用且验证通过的记录返回不可变配置快照,解析当前密钥引用;不发起任务,不通过 HTTP 暴露密钥。派发方仍需校验调用者权限,并在实际执行时再次使用受保护的传输。 - -## 契约 -管理入口 `/api/v1/studio/collaborators`,归属来自当前 Runtime 的 `MPA_AGENT_ID`,不接受浏览器指定归属。`X-MPA-Studio-Key` 必须匹配数据库中的当前 Runtime 密钥;Studio 仅对管理员在服务端注入。GET 列表、POST 新建;PATCH/DELETE `/{id}` 和 POST `/{id}/check` 要求正整数 `revision`,DELETE 通过查询参数传递。版本冲突 409、记录缺失 404、未认证 401、缺持久化后端或 Runtime 身份 503。响应不含密钥值。 - -数据库新增 `mpa_collaborating_agents`,以 MPA/id 为联合主键,JSON 配置和递增版本。不提供内存降级。使用 Runtime 业务 PG。迁移仅新增,回滚保留表并部署旧二进制。旧 Studio/Runtime 的行为不变。 - -连接验证仅允许 HTTPS,拒绝用户信息、查询参数、fragment;显式填写 Card 地址。解析并固定 DNS,默认拒绝非公网地址,始终禁止云元数据、loopback、link-local;运维配置 `MPA_COLLABORATOR_PRIVATE_ORIGINS` 仅允许列出的 HTTPS origin 访问私网 VPC。禁用跳转和环境代理、限制超时和响应大小、验证 TLS。Card 的执行端点必须与 Card 地址同源。仅保存有界的白名单元数据,不存任意 Card JSON。明确拒绝不支持的传输和鉴权机制,本版不实现 OAuth 刷新。 - -## 方案审查 -已核对归属、凭据、SSRF/跳转/DNS 重绑定、取消、跨 MPA、过期结果、旧 Runtime 兼容及双语一致性。复用现有依赖。Runtime 注册接口与 Studio 代理分别测试鉴权。frontend/SPEC.md 引用的 UI skills 在本 checkout 和本地 skills 目录均缺失,直接遵循已有组件及 Foundation 规范。不会假接执行器。 - -## 验收与验证 -| 需求 | 证据 | 状态 | -| --- | --- | --- | -| R1 | 持久化隔离、重启、版本冲突与删除测试 | 本地已验证 | -| R2/R3 | 无效 Card、SSRF、鉴权、超时、脱敏和过期验证测试 | 本地已验证 | -| R4 | Studio 代理/UI 测试、前端 test/build 和浏览器检查 | 本地已验证 | -| R5 | 内部快照启用/版本/鉴权测试 | 本地已验证 | -Runtime 必跑 `make lint`、`make test`;Studio 相关 Python 测试/Ruff/Pyright、`npm --prefix frontend test`、`npm --prefix frontend run build`。实际结果记录如下。本次实现请求不包含生产发布、云资源改动或真实第三方凭据测试。 - -## 配置与联调交接 - -Studio 以管理员身份选中一个 MPA,在会话侧边的 MPA 信息面板点击「协作 Agent」→「添加 Agent」。填写目标名称、用途、**完整 Agent Card HTTPS URL**和鉴权方式,保存后点击「验证连接」。能力由 Card 的 `skills` 读取,不需要手工复制一份能力列表。此入口仅对 MPA 显示。 - -例如目标使用 Bearer:先由运维通过 Runtime 环境变量配置 `MPA_COLLABORATOR_RESEARCH`,页面只填这个变量名。使用 API Key 时还需填写目标要求的 `X-` 请求头名。实际密钥不经过 Studio 表单。更新环境变量后需要按 Runtime 平台方式重新发布/重启使变量生效,然后重新验证。 - -私网目标还需在 Runtime 配置 `MPA_COLLABORATOR_PRIVATE_ORIGINS=https://research.internal.example:443`,同时保证网络可达和可信 TLS 证书;配置允许 origin 不会自动创建 VPC、路由或放行网络。公网目标无须这个配置。当前不支持跨源 Card 执行端点、HTTP 明文、OAuth 自动刷新、云 AK/SK 请求签名和非 JSONRPC 传输。 - -注册请求示例(经 Studio 可信代理调用): -```json -{ - "name": "研究助手", - "description": "检索资料并整理依据", - "card_url": "https://research.example/.well-known/agent-card.json", - "enabled": true, - "auth_type": "bearer", - "secret_env": "MPA_COLLABORATOR_RESEARCH", - "header_name": "" -} -``` -响应包含 `id`、`mpa_id`、`revision` 和 `check`。新建状态为 `not_checked`;调用 `POST /{id}/check` 并提交当前 `revision` 后返回新版本。`verified` 仅说明 Agent Card 可读取且格式受支持,不能作为远端任务执行成功的证据。修改配置会回到 `not_checked`。网络断开导致写入结果不确定时,页面要求先刷新,不盲目重复创建。 - -`dispatch_a2a_task` 负责人与本实现的交接点: -```python -# 使用已注入的 CollaboratorService;mpa_id 必须来自可信执行上下文。 -snapshot = await service.resolve_for_dispatch(mpa_id, registration_id) -# snapshot: mpa_id / registration_id / revision / endpoint / -# headers(仅服务端,repr 隐藏) / protocol_version -``` -派发方仍须检查调用者与当前 MPA 的授权关系,在真正请求前重新做目标地址/DNS 安全检查,并自行实现 task、SSE、取消、结果和 trace。禁止把 `headers` 放进模型上下文、轨迹或 API 响应;不要直接使用不受保护的 HTTP 客户端。禁用或未验证记录解析时返回 `registration_not_ready`。本次未把登记对象注入模型,也不会使 MPA 自动调用它们。 - -数据库由现有 Runtime 渠道存储的 engine 承载,生产使用 Runtime 业务 PG;本地允许持久化 SQLite 文件,拒绝内存 SQLite。新增迁移 `0013_collaborating_agents.sql`,并沿用存储首次使用建表模式;受限数据库账号可由迁移账号预先执行 SQL。每条记录按当前 MPA 隔离,不新建共享目录或新 PG 实例。回滚应用时保留表,不删除注册数据。 - -## 验证记录(2026-10-20) - -- Runtime:Python 3.12;新增注册模块定向 **23 passed**,包括真实 PostgreSQL 16 的迁移 SQL、连接重建持久化、跨 MPA 隔离和竞争版本更新;结合 metadata/迁移合同回归 **38 passed**。新模块行覆盖率 **93.77%(286/305)**,含分支综合覆盖 **91.99%**。`make lint`、新增代码 Ruff/Pyright、差异空白检查和生产代码密钥扫描通过。 -- 完整 Runtime `make test`:首次 **9294 passed / 249 skipped / 1 failed**;失败来自旧的本地 Codex 入口,错误为 `Codex 0.155.1 is required`。安装固定 0.155.1 后该用例 **1 passed**;最终全量复跑 **9298 passed / 250 skipped / 0 failed**(635.44 秒,退出码 0)。 -- Studio:代理策略与完整 Runtime proxy 模块 **133 passed**;前端 Node **1389 passed**、Vitest **123 passed(9 files)**;TypeScript `tsc --noEmit`、前端生产构建及相关 Python Ruff 通过。新增 Python helper 的 Pyright 零错误;整个 `cli_frontend.py` 的 Pyright 有 **37 项存量错误**,与 HEAD 基线对比,忽略文件名与行号后诊断完全相同,无新增错误。因此不声明整个 Studio Python 类型门禁通过。 -- 浏览器:真实配置组件连接本地 Runtime 注册 API/持久化,验证新增、Card 检查、编辑停用、删除确认、关闭/重开、360px 窄窗口滚动、键盘鉴权选择和缺失密钥提示。测试夹具使用模拟远端 Card 与本地鉴权;Studio BFF 鉴权另由集成测试验证,不把本地组件测试表述为线上端到端验收。 -- 未验证/非目标:真实外部 Agent 凭据与私网互通、生产发布、云资源修改、完整远端 CI formal/coverage 门禁、`dispatch_a2a_task` 执行。现有全量测试存在 event loop 清理 warning,保留报告,不将 warning 表述为业务失败。未提交、推送或部署。 - -UI 入口位置由[详情页 Tab 设计](2026-10-20-collaborating-agents-detail-tab.zh.md)替代。 diff --git a/prd-spec/features/mpa-collaborating-agents/2026-10-20-direct-credentials.md b/prd-spec/features/mpa-collaborating-agents/2026-10-20-direct-credentials.md deleted file mode 100644 index c0ad1a9b4..000000000 --- a/prd-spec/features/mpa-collaborating-agents/2026-10-20-direct-credentials.md +++ /dev/null @@ -1,32 +0,0 @@ -# Collaborator credentials entered in Studio - -[中文](2026-10-20-direct-credentials.zh.md) - -Change: collaborator-direct-credentials | 2026-10-20 | implemented locally, verified; not published. -Supersedes the environment-reference-only decision in the collaborating-agents design; other boundaries remain unchanged. - -## Requirements and design -- FR-1: Name, URL and credential inputs use the description field's border color, scoped to this form. Reuse existing InputWithTailIcon, Textarea, Select, Checkbox and Button; no global theme changes. -- FR-2: Enter Bearer/API Key directly. Default masked, explicitly show/hide the newly entered value; clear it on successful save/cancel/refresh/auth-mode or target switch. Editing leaves the input blank and preserves the saved credential only when authentication, header and destination origin are unchanged. Changed origin requires a new credential to prevent forwarding an old secret to a different service. None clears authentication. -- FR-3: Add write-only `secret` (SecretStr, at most 8192 characters, no control characters); responses expose only `credential_configured`. Legacy `secret_env` remains accepted and usable, but the new form does not request environment setup. Never return plaintext or ciphertext. Validation errors must not echo input. -- FR-4: Encrypt the secret and its MPA/registration binding with Fernet in the existing configuration JSON. Generate a separate persistent `collaborator_credential_key` in existing `mpa_deployment_settings`, under a DB transaction lock. No new dependency, migration or environment variable. Missing key with existing ciphertext must fail, never silently rotate. DB and backups remain privileged: the wrapping key lives in the same DB, so this is not protection from DB administrators. Back up both tables together. -- FR-5: Enabled is dispatch eligibility; disabled retains configuration. Connection verification remains separate and actual task dispatch is out of scope. - -## Tasks and acceptance -T-1/AC-1: Regression tests for direct create/check/resolve, persistence/restart, edit preserve/replace/clear, legacy compatibility, revision and MPA isolation, missing/corrupt key, response/validation redaction. -T-2/AC-2: UI tests for masking/toggle/reset, correct write payload and blank edits; browser check light/dark, narrow layout and input borders. -T-3/AC-3: MPA make lint/test, Studio frontend npm test/build, proxy tests and asset checks. Record executed outcomes; live cloud acceptance is not implied. - -## Review and delivery -User explicitly requested direct input and consistent field appearance. Manual design review: no review-spec/front-end design skills available at repository/catalog paths; used repository SPEC and existing components. Risks addressed: credential replay to changed origin, plaintext leakage, concurrency and key-loss recovery. No new dispatch or cloud resource changes. Validation results follow below. - -## Verification record (2026-10-20) - -| Requirement | Evidence | Result | -| --- | --- | --- | -| FR-1/FR-2 | Browser: light/dark, 360px form, mask/show/hide, create, check, reopen, blank save, recheck; real local Runtime DB/API and simulated remote Card | pass | -| FR-2/FR-3 | Frontend 1,389 Node tests + 129 React tests; TypeScript, i18n and production assets | pass | -| FR-3/FR-4 | Runtime credential/registration tests including isolated PostgreSQL 16: 45 passed; 399/415 executable lines covered (96.14%), branch-inclusive 95% | pass | -| FR-3/FR-5 | Studio proxy/auth regressions 127 passed; Ruff/Pyright for proxy and new Runtime modules | pass | - -Runtime make lint passed; full make test: 9,343 passed / 251 skipped / 0 failed, 645.31 seconds, Python 3.12.13 and Codex 0.155.1. Skipped tests are not verified; the credential suite ran separately against PostgreSQL. Browser fixtures and disposable PostgreSQL were cleaned up. No live external Agent check, cloud deployment, formal CI or image publication for this change. Default Studio Python environment lacks Ruff/Pyright; used installed tools from MPA with the Studio Python interpreter for import resolution. diff --git a/prd-spec/features/mpa-collaborating-agents/2026-10-20-direct-credentials.zh.md b/prd-spec/features/mpa-collaborating-agents/2026-10-20-direct-credentials.zh.md deleted file mode 100644 index 0d93ca3e3..000000000 --- a/prd-spec/features/mpa-collaborating-agents/2026-10-20-direct-credentials.zh.md +++ /dev/null @@ -1,32 +0,0 @@ -# Studio 直接填写协作 Agent 密钥 - -[English](2026-10-20-direct-credentials.md) - -变更:collaborator-direct-credentials | 2026-10-20 | 本地已实现并验证,尚未发布。 -替代原协作 Agent 方案中“仅环境变量引用”的决策,其他边界保持。 - -## 需求与设计 -- FR-1:名称、URL、密钥框使用用途说明的边框颜色,仅作用于此表单。复用 InputWithTailIcon、Textarea、Select、Checkbox、Button,不修改全局主题。 -- FR-2:直接输入 Bearer/API Key。默认隐藏,可主动显示/隐藏新输入值;保存成功、取消、刷新、切换鉴权或目标时清空。编辑时留空,仅当鉴权方式、请求头、目标 origin 不变时保留已有密钥。更换 origin 必须重新填写,防止旧密钥发送到不同服务。无鉴权清除凭据。 -- FR-3:新增只写 `secret`(SecretStr,最多8192字符,无控制字符);响应仅新增 `credential_configured`。兼容旧 `secret_env` 记录与调用,新表单不要求配置环境变量。明文、密文均不回显,校验错误不能携带原始输入。 -- FR-4:Fernet 加密密钥值及其 MPA/注册记录归属,存入现有配置 JSON。在现有 `mpa_deployment_settings` 事务锁下自动生成持久 `collaborator_credential_key`。不新增依赖、迁移或环境变量。有密文但密钥丢失时失败,不自动轮换。包装密钥与数据同库,数据库管理员及备份仍属于可信边界;两个表应共同备份。 -- FR-5:启用表示允许派发;停用保留配置。连接验证独立,实际任务派发不在本次范围。 - -## 任务与验收 -T-1/AC-1:直接创建/验证/解析、重启持久化、编辑保留/替换/清除、旧记录兼容、版本及 MPA 隔离、密钥丢失/损坏、响应及校验脱敏回归。 -T-2/AC-2:前端隐藏/切换/重置、写入请求、留空编辑测试;浏览器检查明暗主题、窄布局与输入框边框。 -T-3/AC-3:MPA make lint/test、Studio 前端 npm test/build、代理测试及资源检查,如实记录,不代表云端验收。 - -## 评审与交付 -用户明确要求直接输入和统一输入框外观。手工评审:仓库及技能目录中没有 review-spec/前端设计技能文件,依照 SPEC 与已有组件实现。已考虑 origin 变更凭据转发、明文泄漏、并发与密钥丢失恢复,不扩展派发或云资源操作。验证结果见下文。 - -## 验证记录(2026-10-20) - -| 需求 | 证据 | 结果 | -| --- | --- | --- | -| FR-1/FR-2 | 浏览器浅色/深色、360px 表单、隐藏/显示、创建、验证、重开、留空保存和再次验证;真实本地 Runtime 数据库/API,远端 Card 为模拟 | pass | -| FR-2/FR-3 | 前端 1,389 项 Node + 129 项 React 测试,TypeScript、翻译和生产构建资源检查 | pass | -| FR-3/FR-4 | Runtime 凭据/注册测试含隔离 PostgreSQL 16:45 passed;可执行行399/415(96.14%),含分支95% | pass | -| FR-3/FR-5 | Studio 代理/鉴权回归127 passed,代理及 Runtime 新模块 Ruff/Pyright | pass | - -Runtime make lint 已通过;Python 3.12.13 / Codex 0.155.1 全量 make test:9,343 passed / 251 skipped / 0 failed,645.31秒。跳过项不视为通过,凭据专项另在 PostgreSQL 验证。已清理浏览器夹具与临时 PostgreSQL。未执行真实外部 Agent 验证、云端部署、正式 CI 或本次镜像发布。Studio 默认 Python 缺少 Ruff/Pyright,使用 MPA 已安装工具并指定 Studio Python 完成导入解析。 diff --git a/prd-spec/features/mpa-collaborating-agents/2026-10-21-a2a-space.md b/prd-spec/features/mpa-collaborating-agents/2026-10-21-a2a-space.md deleted file mode 100644 index 9d3486b1d..000000000 --- a/prd-spec/features/mpa-collaborating-agents/2026-10-21-a2a-space.md +++ /dev/null @@ -1,14 +0,0 @@ -# A2A Center space selection - -Change ID: agentkit-a2a-collaborators. Status: approved. Date: 2026-10-21. -[中文](2026-10-21-a2a-space.zh.md). Contract: specs/mpa-collaborating-agents/README.md. - -User explicitly approved adding an A2A Center tab while retaining manual registration. FR1/AC1: browse the target Runtime account/region's spaces and paginated agents. FR2/AC2: select one space per MPA; save with revision, disconnect without deleting cloud resources. All running agents, including future additions, become eligible. FR3/AC3: errors are not empty results, stale responses cannot overwrite a new selection, secrets never reach the browser. FR4/AC4: preserve manual registration. - -Reuse Runtime proxy administrator checks and requestCollaborators. Runtime owns discovery, persistence, identity, credentials and dispatch. Add no separate Studio database or credential configuration. New GET registry/spaces, registry/agents and GET/PUT registry/binding endpoints under the existing proxy. PUT uses space_id (nullable) and revision. Conflict reload is required. This does not deploy or mutate AgentKit resources. - -Tasks: T1 design/contract; T2 typed API and tab UI; T3 loading/error/empty/cancellation/keyboard tests and browser verification; T4 npm test/build and integration report. Acceptance maps FR1–4 to frontend/tests/collaboratingAgents.test.tsx and Runtime tests. Live cloud and browser results must be reported separately. Preserve pre-existing package-lock changes. - -Review: approved by the user's space-selection request. Self-review found no unresolved design blocker. Risk: Runtime IAM needs center list/get permissions; unsupported Runtime returns explicit error. No streaming or async task polling in this change. Verification: Node tests 1389 passed; Vitest 157 passed; TypeScript and Vite main/widget build passed. The real component was checked in a local browser fixture for tab switching, connection, disconnection confirmation and a 360px content layout. Runtime live MCP/Harness dispatch to the registered a2a1021test completed and replayed once without a second dispatch. This is not deployed Studio-to-Runtime E2E. Runtime full-suite and environment limitations are recorded in agentkit-mpa-agent/docs/verification/agentkit-a2a-collaborators/test-verification-report.md. No commit, image or deployment. - -Proxy verification: allow PUT only for api/v1/studio/collaborators/registry/binding. Preserve server-owned credentials and administrator checks. Runtime proxy and collaborator policy suites: 136 passed. Other PUT paths remain rejected. diff --git a/prd-spec/features/mpa-collaborating-agents/2026-10-21-a2a-space.zh.md b/prd-spec/features/mpa-collaborating-agents/2026-10-21-a2a-space.zh.md deleted file mode 100644 index 1a5b3ea2c..000000000 --- a/prd-spec/features/mpa-collaborating-agents/2026-10-21-a2a-space.zh.md +++ /dev/null @@ -1,18 +0,0 @@ -# A2A 中心空间选择 - -Change ID: agentkit-a2a-collaborators。状态 approved。日期 2026-10-21。 -[English](2026-10-21-a2a-space.md)。合同 specs/mpa-collaborating-agents/README.zh.md。 - -用户已明确批准保留手动注册并新增 A2A 中心页签。FR1/AC1:浏览目标 Runtime 所在账号、地域的空间和分页 Agent。FR2/AC2:一个 MPA 选择一个空间,以 revision 保存;解除关联不删除云资源。空间内全部运行中 Agent(包括未来新增)可被调用。FR3/AC3:失败不伪装空列表,旧响应不覆盖新选择,密钥不进浏览器。FR4/AC4:保留手动注册。 - -复用 Runtime 管理员代理与 requestCollaborators,Runtime 负责发现、持久化、身份、凭据和派发。不新增 Studio 数据库或凭据配置。现有代理下新增 GET registry/spaces、registry/agents 及 GET/PUT registry/binding。PUT 使用可空 space_id 和 revision,冲突要求刷新。不会部署或修改 AgentKit 资源。 - -任务 T1 方案合同;T2 类型化 API 和页签;T3 加载/失败/空/取消/键盘测试与浏览器验证;T4 npm test/build 和集成报告。FR1–4 对应 frontend/tests/collaboratingAgents.test.tsx 与 Runtime 测试。真实云验证与浏览器验证分别记录。保留原有 package-lock 改动。 - -评审:用户空间接入请求即方案授权;自审无未解决设计阻塞。风险:Runtime IAM 需要中心 list/get 权限;旧 Runtime 明确显示不支持。本次不支持流式或异步任务轮询。验证结果见下方。 - -## 验证结果 - -Node 测试 1389 passed,Vitest 157 passed,TypeScript 与 Vite main/widget 构建通过。实际组件在本地浏览器夹具验证双页签、接入、解除确认和 360px 内容布局;夹具使用模拟 API,未修改云端。Runtime 新代码经真实 MCP/Harness 调用中心注册的 a2a1021test 成功,重复请求未重复派发。未部署 Studio/Runtime,不代表部署后 E2E 已验证。Runtime 全量及环境限制见 agentkit-mpa-agent/docs/verification/agentkit-a2a-collaborators/test-verification-report.md。保留用户已有 package-lock 修改。 - -代理验证:仅为空间关联 api/v1/studio/collaborators/registry/binding 开放 PUT 转发,保留服务端凭据和管理员校验;Runtime 代理及协作 Agent 策略测试 136 项通过,其他 PUT 路径仍拒绝。 diff --git a/prd-spec/features/mpa-collaborating-agents/2026-10-22-center-registration.md b/prd-spec/features/mpa-collaborating-agents/2026-10-22-center-registration.md deleted file mode 100644 index ad9820957..000000000 --- a/prd-spec/features/mpa-collaborating-agents/2026-10-22-center-registration.md +++ /dev/null @@ -1,111 +0,0 @@ -# AgentKit A2A Center registration and legacy retirement - -[中文版](2026-10-22-center-registration.zh.md) - -Status: accepted design; implementation and verification tracked separately. Date: 2026-10-22; documentation reconciled 2026-10-23. Feature and configuration retirement, risk L3. The user approved both official registration sources and one MPA-to-space association. The original startup-cleanup proposal was cancelled: retire manual registration APIs and UI while retaining historical records. This supplements the 2026-10-21 space design and replaces its decision to preserve manual registration. Existing dispatch contracts remain valid. - -## Evidence and scenarios - -Administrators select a space and register an existing service in Studio. Registration writes to AgentKit, while MPA discovers members dynamically. Registration alone does not prove remote execution, compatibility, or permissions in another account/region. - -| ID | Kind | Finding | Evidence | Impact | Verification | -|---|---|---|---|---|---| -| FACT-1 | Verified | Runtime already discovers by space | agentkit-mpa-agent/app/services/a2a_space.py A2ASpaceService | Preserve dispatch and association | Existing space tests | -| FACT-2 | Verified | Studio already signs controlled OpenAPI requests | veadk/cli/cli_frontend.py _agentkit_openapi_post and _require_agent_management | Register server-side | Authorization tests | -| FACT-3 | Official API | CreateA2aAgent supports Runtime and Standard; Standard supports json/url | https://docs.volcengine.com/docs/agentkit/CreateA2aAgent_-_Creates_an_A2A_agent?lang=zh | Use official request fields | Payload tests; separate live acceptance | - -## Requirements and scope - -| ID | Source | Priority | Trigger | Required behavior | Observable result | -|---|---|---|---|---|---| -| FR-1 | User request | Required | Admin registration | RuntimeId/NetworkType for Runtime; Card JSON or URL for Standard | Center returns an Agent ID | -| FR-2 | User request, FACT-1 | Required | MPA selects space | Store one space association including future members | No local per-Agent copy | -| FR-3 | Confirmed scope correction | Required | Runtime upgrade | Retire legacy CRUD without startup deletion or migration; preserve existing records, other MPAs and history | Authenticated 410; retained data unused for discovery/dispatch | -| NFR-1 | Studio AGENTS.md, FACT-2 | Required | Registration API | Admin/Runtime authorization, bounded input, no credential reflection, no automatic retry | Authorization/error/double-click tests | - -Non-goals: deploying Runtimes, creating spaces, updating/deleting registered center agents, proxy/VPC wizard, OAuth adaptation, or changing dispatch_a2a_task/Harness terminal semantics. Runtime input accepts an accessible existing Runtime ID; Studio-managed tags must not restrict all account resources. Existing Runtime lists can assist selection, with manual ID entry for other visible resources. - -## Design and interfaces - -DEC-1: POST /web/a2a-agents?region=... accepts targetRuntimeId, spaceId, and source-specific configuration. Existing Studio server credentials sign CreateA2aAgent; response contains only id. Cards/secrets never enter responses, logs, or browser persistence. Runtime-source registration additionally authorizes the source Runtime. AgentKit checks space access. JSON Card must be an object with nonempty name/url/version. URLs require HTTPS without userinfo; AgentKit fetches them, not Studio. ProjectName follows the selected space and defaults to default when absent; SetDefaultVersion=true. Network public/private is an explicit choice. No new dependencies. - -DEC-2: Keep space selection/pagination and add a registration form with progressive source/import fields. Prevent concurrent submission. Cancellation aborts the browser request but may leave a cloud operation submitted. Network/5xx/cancel uncertainty prevents blind retries: refresh the center to reconcile, then let the user explicitly start another registration. Success closes/clears the form and refreshes members; a refresh failure is distinguished from registration failure. Changing space/Runtime cancels stale results and clears form data. - -DEC-3: Runtime startup does not delete or migrate legacy records in mpa_collaborating_agents. Retained records are not used for discovery or dispatch. Preserve table structure, published migrations, space associations, execution ledgers and the shared collaborator_credential_key. Authenticated retired endpoints return 410 legacy_registration_retired and cannot write. The original startup-cleanup proposal is superseded; any future data cleanup requires separate approval. - -## Failure, security, migration and rollback - -Invalid bodies return 422; access denial 401/403/404. Cloud/network errors are sanitized. A successful create means accepted registration, not verified execution. Double clicks issue one request. Timeouts never switch identity, retry automatically, or bind another space. Upgrade and binary rollback do not themselves delete legacy records; retention does not restore legacy registration support in the upgraded Runtime. Keep table structure for migration compatibility. Studio server and Runtime cloud identities should use the same account; mismatches fail through space/permission checks rather than silent fallback. - -## Acceptance, tasks and traceability - -| ID | Given | When | Then | Method | Environment | -|---|---|---|---|---|---| -| AC-1 | Accessible space and Runtime | Runtime source | Exact source-specific CreateA2aAgent fields | Backend tests | Mocked cloud | -| AC-2 | Card JSON or HTTPS URL | Standard source | Correct ImportType; reject invalid/oversized input | Backend tests | Local | -| AC-3 | Non-admin or inaccessible Runtime | Create | No cloud write | Backend tests | Local | -| AC-4 | Current/foreign legacy rows, spaces and ledger | Runtime startup | No legacy-data cleanup or migration; preserve records and shared key; discovery/dispatch use the bound space | Startup/source inspection and existing space tests | Local; no live data writes | -| AC-5 | Form, timeout/cancel | Submit/switch/retry | No stale state, automatic duplicates, or IME submit | React tests/browser | Local | -| AC-6 | Retired endpoint | Old client request | Authenticate then 410; no writes | Runtime tests | Local | - -| Requirement | Design | Acceptance | Verification | Document coverage | -|---|---|---|---|---| -| FR-1 | DEC-1/2 | AC-1/2/5 | Python/React/browser | Covered | -| FR-2 | DEC-1 | Existing space regression | test_a2a_registry_collaborators.py | Covered | -| FR-3 | DEC-3 | AC-4/6 | Startup/source inspection and retired-endpoint tests | Covered | -| NFR-1 | DEC-1/2/3 | AC-2/3/5/6 | Boundary tests | Covered | - -Sequence: contract and retired-endpoint tests, registration route/UI, regression/build, two-pass review and delivery. Risk: official AgentCard field wording is ambiguous; use serialized JSON aligned with GetA2aAgent. Actual cloud creation needs separate acceptance; mocked payload tests do not prove it. Browser verification uses deterministic service responses without live writes. No deployment or real cloud creation is authorized by this request. - -## Design review - -The original review assessed scoped startup cleanup; that proposal was subsequently cancelled. The reconciled design preserves legacy records and published migrations, prevents automatic create retries, and reuses administration boundaries. Space bindings remain intact. No remaining user scope decision; live cloud creation and release must be reported separately and remain unexecuted. Legacy-data cleanup is outside this design. - -## Alternatives, release and risks - -DEC-4: Compare retaining local per-Agent registration, creating center records from Runtime, and signing center creation in Studio. Choose Studio to reuse control-plane credentials and the administration boundary without adding create permissions to every Runtime. Existing Runtime discovery/dispatch permissions remain unchanged. Reassess account consistency if cloud identity becomes per-user. - -| ID | Trigger | Probability/impact | Mitigation | Signal | Recovery | -|---|---|---|---|---|---| -| RISK-1 | Create times out after acceptance | Medium/duplicates | No automatic retry; refresh first | UI uncertain | Admin reconciles center IDs | -| RISK-2 | Retained legacy records mistaken for active registrations | Medium/stale configuration confusion | Document retirement and bound-space discovery | Authenticated 410; center member list | Register the intended service in AgentKit Center | -| RISK-3 | Official API differs from documentation | Medium/create rejection | Payload tests and separate cloud acceptance | Sanitized error type | Existing console registration remains | - -Release order: upgrade Runtime and verify retired endpoints and bound-space discovery, then publish Studio assets. Canary one Runtime, validate all three inputs, center ID readback and existing dispatch before expanding. Roll back binaries on failure; never delete legacy data or create replacement resources automatically. Logs contain error type and region/space ID, not bodies or exception text. No new mandatory performance threshold. - -## Implementation and local verification (2026-10-22) - -Implemented both sources, source-specific validation, same-space refresh, credential masking, unknown-outcome handling, and removal of the legacy UI. Runtime endpoint retirement is implemented in agentkit-mpa-agent; upgrading and starting it does not delete legacy data. The selected space ProjectName is forwarded; absent metadata defaults to default. Existing user package-lock.json changes remain untouched. The following results are historical evidence from 2026-10-22; the cleanup-related result predates cancellation and does not verify current startup behavior. - -| Check | Result | Scope/limitations | -|---|---|---| -| Registration and authorization tests | 23 passed | Mocked cloud; no actual CreateA2aAgent | -| Runtime API/cleanup/space tests (superseded cleanup proposal) | 40 passed historically | SQLite; real PG lock path not run; not current cleanup acceptance | -| Runtime affected module line coverage | 83/86 (96.51%) | Combined line/branch display 94%; not whole-app coverage | -| Proxy single-file regression | 129 passed | Mixed run has two provider/region cases failing; tracked separately | -| npm test | Node suite plus 156 Vitest cases passed | Success/failure/empty/cancel/IME/keyboard/focus and refresh failure | -| Build / assets | pass; 114 files, 351 references | Generated veadk/webui updated, not deployed | -| i18n | 2 locales, 22 namespaces consistent | Both languages | -| TypeScript | pass | tsc --noEmit | -| New backend module/tests Pyright | 0 errors | Existing Studio environment | -| CLI whole-file Pyright | 37 errors | Removing the new mount gives identical 37 diagnostic messages | -| Ruff | pass | Changed Python files | -| Browser | pass with simulated API | Registration success refresh, JSON error/masking, denied/unknown outcome, menu Escape/focus; no overflow at 390px | -| Secret scan | Project rules pass; one baseline vendor false positive with default rules | 21.73 MB including assets; Lexical anchor/focus.key expression matches in old HEAD artifact too; no rule bypass | - -Two-pass self-review fixed shared-key retirement concurrency, Select Escape propagation and React initialization order. The JSON review evidence ledger validator passes. No independent review agent was used. Runtime full frozen-source verification is recorded in the paired Runtime repository report; deployment readiness is separate from these local checks. No cloud creation, live PG cleanup, image release or deployment occurred. Update both Studio and Runtime before live acceptance. - -### Final Runtime and mixed-regression evidence - -Frozen Runtime make test: 10366 passed, 304 skipped, one scheduled Worker baseline failure; source fingerprints before/after match. All 475 production/script Python files match the delivery tree; three additional management-route tests passed separately without production changes. Studio mixed regression: 150 passed, two provider/region assertions failed; removing only the new CLI registration mount reproduces the same two failures in the same mixed run. Both current and counterfactual proxy-only runs pass 129 cases. Whole-file CLI Pyright also has the same 37 messages with/without the mount. These are explicitly retained verification limits, not green full gates. No release or live data cleanup occurred. - -### Documentation reconciliation (2026-10-23) - -The user approved correcting the stale cleanup description and committing/pushing this documentation-only change to PR #1182. Direct review reconciled FR-3, DEC-3, AC-4, release guidance and both component-spec languages with the current Runtime startup and retired-endpoint implementation. No runtime behavior or public interface changes; no new PRD is required for this correction. Current verification is recorded below; historical results above are not rerun evidence. - -Scope: the four documentation files in this correction, based on PR head `846c4005` after fetching both remotes and rebasing with merge preservation onto `upstream/main` (no base changes required). - -- pass: paired-language review, requirement/acceptance identifiers, relative links and `git diff --check`. -- pass: `uv run --extra dev pre-commit run --all-files`, including Ruff and secret scanning. -- pass: `uv run --extra dev pytest -q tests/cli/test_a2a_center_registration.py tests/cli/test_a2a_center_actions.py tests/test_mpa_collaborator_proxy_policy.py` — 95 passed, two dependency deprecation warnings. -- not_run: full regression suite, frontend build/browser and live cloud checks; documentation-only correction, no production code, tests or assets changed. Runtime startup was inspected as source evidence; it was not exercised in a live deployment. diff --git a/prd-spec/features/mpa-collaborating-agents/2026-10-22-center-registration.zh.md b/prd-spec/features/mpa-collaborating-agents/2026-10-22-center-registration.zh.md deleted file mode 100644 index f652e5906..000000000 --- a/prd-spec/features/mpa-collaborating-agents/2026-10-22-center-registration.zh.md +++ /dev/null @@ -1,111 +0,0 @@ -# AgentKit A2A 中心注册与旧配置退役 - -[English](2026-10-22-center-registration.md) - -状态:已接受设计;实现与验证另行记录。日期:2026-10-22;文档于 2026-10-23 对齐。类型:功能与配置退役;风险 L3。用户已确认两种官方注册来源、只维护 MPA 与空间关联。最初的启动清理方案已取消:退役手动注册接口及 UI,保留历史记录。补充 2026-10-21 空间接入设计,替代其中“保留手动注册”的决策,其余派发合同继续有效。 - -## 背景、事实与场景 - -Studio 管理员应在协作 Agent 页面选择空间并注册已有服务。注册成功进入 AgentKit 中心,MPA 动态读取该空间,不维护逐 Agent 本地副本。其他账号/地域、停用或不兼容的服务不得因为注册成功而被当成可执行成功。 - -| ID | 类型 | 结论 | 证据 | 设计影响 | 验证方式 | -|---|---|---|---|---|---| -| FACT-1 | 已验证事实 | Runtime 已按空间动态发现 | agentkit-mpa-agent/app/services/a2a_space.py 的 A2ASpaceService | 保留关联与执行路径 | 原有空间测试 | -| FACT-2 | 已验证事实 | Studio 有受控签名 OpenAPI | veadk/cli/cli_frontend.py 的 _agentkit_openapi_post、_require_agent_management | 注册在 Studio 服务端执行 | 路由授权测试 | -| FACT-3 | 官方接口 | CreateA2aAgent 支持 Runtime、Standard;Standard 支持 json、url | https://docs.volcengine.com/docs/agentkit/CreateA2aAgent_-_Creates_an_A2A_agent?lang=zh | 按官方字段构造请求 | Payload 契约测试;真实云验证单列 | - -## 目标、非目标和需求 - -| ID | 来源 | 优先级 | 触发范围 | 必须行为 | 可观察结果 | -|---|---|---|---|---|---| -| FR-1 | 用户原始请求 | 必须 | 管理员注册 | Runtime 来源提交 RuntimeId、NetworkType;Standard 来源支持 Card JSON 或 URL | 中心返回 Agent ID | -| FR-2 | 用户原始请求、FACT-1 | 必须 | MPA 选择空间 | 只保存一个空间关联,包含后续新增成员 | 无逐 Agent 本地写入 | -| FR-3 | 已确认的范围修正 | 必须 | 升级 Runtime | 退役旧 CRUD,启动时不删除或迁移;保留已有记录、其他 MPA 和执行历史 | 鉴权后 410;保留数据不用于发现或派发 | -| NFR-1 | Studio AGENTS.md、FACT-2 | 必须 | 注册 API | 管理员及目标 Runtime 权限检查;输入有界;不回显 Card 凭证;失败不自动重试 | 授权、错误与重复点击测试 | - -非目标:创建新的 Runtime、创建空间、编辑/删除中心已有 Agent、代理/VPC 配置向导、OAuth 适配、改变已有 dispatch_a2a_task 或 Harness 终态语义。Runtime 来源可输入任意有访问权的已部署 Runtime ID,不把 Studio 创建标签当作全部账号资源。现有 Runtime 列表可供选择,未列出的仍可输入。 - -## 设计与接口 - -DEC-1:Studio 的 POST /web/a2a-agents?region=... 接受 targetRuntimeId、spaceId 以及 source 区分的配置。使用现有 Studio 云凭据调用 CreateA2aAgent,返回仅 id;不将 Card/密钥放进响应、日志、浏览器持久化。Runtime 来源对待注册 Runtime 再做可见性校验;空间由官方 API 检查访问权。JSON Card 必须为对象且有非空 name、url、version;URL 只允许 HTTPS 无用户信息,抓取由 AgentKit 执行,Studio 不直接请求任意 URL。ProjectName 随所选空间传递,缺失时默认 default;SetDefaultVersion=true;网络为显式选择的 public/private。使用 SDK 已有签名链,不引入依赖。 - -DEC-2:UI 沿用当前空间选择和分页,新增“注册 Agent”内联表单。来源、导入方式及字段 progressive disclosure,保存期间防重入,可取消浏览器请求但提示云端结果可能已提交。网络/5xx 或取消后的结果不明,禁止盲目再次提交;先刷新中心确认,再由用户主动发起新的注册。成功后关闭表单、清空输入、刷新空间成员;刷新失败明确区分“注册已成功,列表刷新失败”。切换空间/Runtime 必须清空旧表单并取消旧响应。 - -DEC-3:Runtime 启动时不删除或迁移 mpa_collaborating_agents 中的旧记录。保留记录不再用于发现或派发。保留表结构、已发布迁移、空间关联、执行账本及共享 collaborator_credential_key。旧端点保留鉴权后返回 410 legacy_registration_retired,不能再写入。最初的启动清理方案已被替代;未来如需清理数据,须另行批准。 - -## 失败、安全、迁移及回滚 - -请求体校验失败 422,无权 401/403/404;云端拒绝或网络失败返回脱敏错误。成功表示中心接受注册,不表示远端执行已验证。并发点击仅发送一次注册;超时不会切换身份、自动重试或自动绑定不同空间。升级和回滚二进制本身不会删除旧记录;保留数据不代表升级后的 Runtime 恢复旧注册功能。保留表结构兼容旧迁移。共享凭据使用的 Studio 账号与 Runtime 云账号应一致;否则中心空间/权限校验失败,不能跨账号静默降级。 - -## 验收、计划与追踪 - -| ID | Given | When | Then | 验证方式 | 环境 | -|---|---|---|---|---|---| -| AC-1 | 可访问空间与 Runtime | Runtime 来源注册 | CreateA2aAgent 仅带对应字段 | backend tests | 本地模拟云 | -| AC-2 | Card JSON 或 HTTPS URL | Standard 注册 | 正确 ImportType,非法/过大数据拒绝 | backend tests | 本地 | -| AC-3 | 非管理员或 Runtime 无权 | 注册 | 无云端写请求 | backend tests | 本地 | -| AC-4 | 当前与其他 MPA 的旧记录、空间和账本 | Runtime 启动 | 不清理或迁移旧数据;保留记录及共享密钥;发现和派发使用绑定空间 | 启动/源码检查及原有空间测试 | 本地;不写线上数据 | -| AC-5 | UI 表单或超时取消 | 提交/切换/重试 | 无旧响应污染、无自动重复注册、IME 不提交 | frontend tests + browser | 本地 | -| AC-6 | 已退役路径 | 旧客户端请求 | 鉴权后 410,无旧数据新增 | Runtime tests | 本地 | - -| 需求 | 设计 | 验收 | 验证 | 文档覆盖 | -|---|---|---|---|---| -| FR-1 | DEC-1/2 | AC-1/2/5 | Python/React/browser | 已覆盖 | -| FR-2 | DEC-1 | 原空间回归 | test_a2a_registry_collaborators.py | 已覆盖 | -| FR-3 | DEC-3 | AC-4/6 | 启动/源码检查及退役接口测试 | 已覆盖 | -| NFR-1 | DEC-1/2/3 | AC-2/3/5/6 | 边界测试 | 已覆盖 | - -执行顺序:先契约与退役接口测试;再 Studio 注册路由和 UI;最后回归、构建、双轮评审及交付。风险:官方文档 AgentCard 字段描述存在歧义,按字符串化 JSON 与 GetA2aAgent 对齐;真实创建仍需独立云验收,不能用模拟结果声称已验证云端。UI 无真实云写入时使用确定性服务响应验证交互。当前请求不包含发布或新建云资源。 - -## 设计评审 - -原评审针对限定范围的启动清理,该方案随后已取消。对齐后的设计保留旧记录及已发布迁移,创建不自动重试,复用既有管理权限边界,空间关联保持不变。无待用户裁决的范围问题;真实云创建和发布状态必须单列,尚未执行。旧数据清理不属于此设计。 - -## 备选、发布和风险 - -DEC-4:比较继续保留逐 Agent 本地注册、让 Runtime 承担中心创建、由 Studio 签名中心创建。选择第三项复用 Studio 已有控制面凭据和管理员边界,不给每个 Runtime 新增创建权限。目标 Runtime 的查询/派发权限保持原状。未来切换逐用户云身份时重新评估账号一致性。 - -| ID | 触发 | 概率/影响 | 缓解 | 监测 | 恢复 | -|---|---|---|---|---|---| -| RISK-1 | 云端创建超时但实际已提交 | 中/重复注册 | 不自动重试,结果不明先刷新 | UI uncertain | 管理员核对中心 ID | -| RISK-2 | 将保留的旧记录误认为有效注册 | 中/旧配置引起混淆 | 说明接口退役及绑定空间发现机制 | 鉴权后 410;中心成员列表 | 在 AgentKit 中心注册所需服务 | -| RISK-3 | 官方 API 与文档字段不一致 | 中/注册拒绝 | Payload 契约测试与独立云验收 | 脱敏错误类型 | 保留控制台注册入口 | - -发布顺序:升级 Runtime 验证退役接口和绑定空间发现,再发布 Studio 构建。单 Runtime 灰度验证三种输入、中心 ID 回读和原有派发;没有证据不扩大灰度。失败回滚对应二进制,不自动删除旧数据或创建替代资源。日志仅记录错误类型和地域/空间 ID,不打印请求体或异常正文。无新增强制性能阈值。 - -## 实现与本地验证(2026-10-22) - -已实现两种来源、互斥字段校验、所选空间刷新、凭据遮罩、结果不明处理及旧表单退役。Runtime 接口退役实现在 agentkit-mpa-agent,升级启动不会删除旧数据。转发所选空间的 ProjectName,缺元数据时默认 default。用户原有 package-lock.json 改动未覆盖。以下为 2026-10-22 的历史验证记录;其中清理相关结果早于取消决策,不证明当前启动行为。 - -| 检查 | 结果 | 范围与限制 | -|---|---|---| -| 注册与权限测试 | 23 passed | 模拟云端,未真实调用 CreateA2aAgent | -| Runtime API/清理/空间测试(已替代的清理方案) | 历史结果 40 passed | SQLite;未执行真实 PG 锁路径;不作为当前清理验收 | -| Runtime 受影响模块行覆盖 | 83/86(96.51%) | 行/分支综合显示 94%;不是整个 app 覆盖率 | -| 代理单文件回归 | 129 passed | 混合运行有两项 provider/地域失败,单列记录 | -| npm test | Node suite 和 156 Vitest cases passed | 成功、失败、空、取消、IME、键盘、焦点及刷新失败 | -| 构建/产物 | pass;114 files、351 references | veadk/webui 已更新,未部署 | -| i18n | 2 locales、22 namespaces consistent | 两种语言 | -| TypeScript | pass | tsc --noEmit | -| 新后端模块/测试 Pyright | 0 errors | Studio 现有环境 | -| CLI 整文件 Pyright | 37 errors | 删除新增挂载的对照同样有 37 项,诊断消息完全一致 | -| Ruff | pass | 修改的 Python 文件 | -| 浏览器 | 模拟 API 验证通过 | 注册成功后刷新、JSON 错误/遮罩、拒绝/结果不明、菜单 Escape/焦点;390px 无横向溢出 | -| 密钥扫描 | 仓库规则通过;默认规则 1 项旧第三方误报 | 含产物 21.73 MB;Lexical anchor/focus.key 表达式,HEAD 旧产物同样命中;未绕过规则 | - -两轮自审修正共享密钥退役竞争、Select Escape 冒泡与 React 初始化顺序。JSON review ledger 校验通过,未使用独立评审 Agent。Runtime 冻结源码全量验证记录在对应 Runtime 仓库报告中;发布准出与这些本地检查分开。未创建云资源、未清理线上 PG、未发布镜像或部署。真实验收前需要同时更新 Studio 与 Runtime。 - -### Runtime 和混合回归最终证据 - -Runtime 固定源码 make test:10366 passed、304 skipped、1 项 scheduled Worker 基线失败;前后指纹一致。475 个生产/脚本 Python 文件与交付树一致;3 项补充管理路由测试单独通过,未改生产代码。Studio 混合回归:150 passed、两个 provider/地域断言失败;只移除新增 CLI 注册挂载的相同组合也复现这两项失败。当前与对照的代理单文件均为 129 passed。CLI 整文件 Pyright 在有/无挂载时也有完全相同的 37 项诊断。这些属于明确保留的验证限制,不能宣称全量门禁通过。未发布或清理线上数据。 - -### 文档对齐(2026-10-23) - -用户已批准修正旧清理描述,并将此次纯文档修改提交、推送至 PR #1182。直接评审已将 FR-3、DEC-3、AC-4、发布指引及中英文组件规范与当前 Runtime 启动和退役接口实现对齐。不修改运行行为或公开接口;此次修正无需新建 PRD。当前验证记录见下文,以上历史结果不代表本次重新执行。 - -范围:此次修正的四份文档,基于 PR 提交 `846c4005`;已获取两个远端更新,并保留合并结构 rebase 到 `upstream/main`(无需更新基线)。 - -- pass:双语等价性、需求/验收标识、相对链接及 `git diff --check`。 -- pass:`uv run --extra dev pre-commit run --all-files`,包括 Ruff 和密钥扫描。 -- pass:`uv run --extra dev pytest -q tests/cli/test_a2a_center_registration.py tests/cli/test_a2a_center_actions.py tests/test_mpa_collaborator_proxy_policy.py` — 95 passed,两项依赖弃用警告。 -- not_run:全量回归、前端构建/浏览器及真实云检查;此次仅修正文档,未修改生产代码、测试或产物。Runtime 启动行为以源码检查为依据,未在真实部署中执行验证。 diff --git a/prd-spec/features/mpa-collaborating-agents/2026-10-22-endpoints-and-skills.md b/prd-spec/features/mpa-collaborating-agents/2026-10-22-endpoints-and-skills.md deleted file mode 100644 index ba7cf7a06..000000000 --- a/prd-spec/features/mpa-collaborating-agents/2026-10-22-endpoints-and-skills.md +++ /dev/null @@ -1,58 +0,0 @@ -# A2A Runtime endpoints and Agent details - -[中文版](2026-10-22-endpoints-and-skills.zh.md) - -ID: mpa-a2a-endpoints-and-skills. Created/revised: 2026-10-22. Status: implemented. -Related: [component](../../../specs/mpa-collaborating-agents/README.md), [member actions](2026-10-22-member-actions.md), [authentication fix](../../bugfixes/mpa-a2a-space-deletion/2026-10-22-runtime-auth.md). - -## Background and evidence - -Registration currently always offers public/private without reading actual Runtime addresses. The details projection omits AgentCard service address and skills. The user requested console-aligned options, concrete addresses, and skills in View, and approved: “按此实现”. Official [registration](https://docs.volcengine.com/docs/86681/2229304), [service address](https://docs.volcengine.com/docs/86681/2248691), and [GetA2aAgent](https://docs.volcengine.com/docs/86681/2249666) describe Runtime-specific public/private service addresses and AgentCard metadata. The deployed MPA registry already parses AgentCard as JSON string/object, although the public Get documentation's examples are inconsistent. Missing/unparseable metadata must therefore be explicit rather than invented. - -## Goals, scenarios and requirements - -FR-1: Selecting or entering a source Runtime reads its authorized network configurations in the target region. Show only configured public/private endpoints, with localized Public address/Private address and concrete address. Select the previous type if still available, otherwise the first available type. Switching Runtime immediately invalidates old choices; loading, empty and read error remain distinct. Provide retry and abort stale requests on Runtime/region/source changes or unmount. Do not submit without a current matching endpoint. Registration payload remains RuntimeId plus NetworkType; never accept a browser URL as the registration destination. -FR-2: View shows the effective AgentCard service address and skill name, description, tags and examples as plain text. Service URL is distinct from the AgentCard document URL. Distinguish known empty skills from unavailable/malformed metadata. Preserve basic detail if optional Card metadata is unavailable. Keep bounded projection, identity checks and read retry. -FR-3: Strip URL query/fragment and reject credentials, non-HTTP(S), malformed ports, control characters and oversized URLs. Never return raw Cards, security schemes, headers, keys, provider reasons or environments; do not fetch Card URLs. Bound Card input to 128 KiB, skills to 100 with an explicit truncation notice, each ID/name/description to 256/512/4096 characters, tags/examples to 20 entries of 128/1024 characters. Invalid skill metadata is unavailable, not an empty list. Use existing form controls, ModalButton, typography and scrolling; long addresses/text wrap at narrow widths. - -## Design, contracts and compatibility - -GET /web/a2a-runtime-endpoints/{sourceRuntimeId}?targetRuntimeId=...®ion=... requires Studio administrator plus authorization for both Runtime IDs. Return {runtimeId, endpoints:[{networkType:public|private,url}]}, at most two entries, one per configured type, with explicit empty response when no safe usable endpoint exists. No key is exposed. Add this dedicated client/read rather than expanding the shared Runtime list/detail DTO. The provider remains responsible for registration eligibility and races. - -GET /web/a2a-agents/{agentId} adds serviceAddress:string|null, skills:Array<{id,name,description,tags:string[],examples:string[]}>|null, skillsTruncated:boolean. Additive fields preserve existing callers. Null means unavailable; [] means confirmed empty. Read metadata only on View, not on DELETE validation, so a malformed optional Card never changes deregistration eligibility. No schema/data migration, dependency, SDK public signature, Runtime deployment, dispatch, Harness or IAM change. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| FR-1 | T-1: endpoint route/client/parser, then registration state and selection | AC-1: public-only/private-only/both/no endpoint, retry, stale response, invalid manual ID and submit gates work; no credential leaks | Python endpoint auth/projection tests; frontend/tests/collaboratingAgents.test.tsx | -| FR-2 | T-2: bounded Card projection and detail display | AC-2: Runtime/Standard details show address and skills; empty/unavailable differ; plain text and identity checks retained | Python metadata tests; UI modal tests; browser | -| FR-3 | T-3: security/compatibility, docs and release assets | AC-3: unsafe URLs and raw credentials excluded, malformed/oversized metadata explicit; keyboard/narrow/loading/error/cancel behavior retained | affected Python tests, Ruff/Pyright; npm test/build/check:i18n/test:webui-assets; tsc; browser; secret scan; git diff --check | - -## Review, approval, risks and verification - -No review-spec skill is available; direct review covered bilingual equivalence, API ownership, authorization, security, failures, compatibility, cancellation and tests. No blockers. User confirmed the scoped plan before production/test edits. Existing UI controls and alerts satisfy the layout/feedback baseline; no restyling or new dependencies. Runtime endpoint reads may fail independently of list reads and must remain visible/retryable. Safe addresses intentionally omit query/fragment secrets. No automatic writes or live cloud registrations/deletions during verification. Real end-to-end cloud success is not established by mocked tests. - -Date: 2026-10-22. Scope: current uncommitted A2A diff above HEAD 9ab0ef2d. Final checks are recorded in the delivery evidence. Commit, push and cloud deployment are outside authorization. - - -## Delivery evidence (2026-10-22) - -Implemented the approved scope against the existing uncommitted A2A changes above 9ab0ef2d. Regression-first evidence: 22 selected Python and 8 UI cases failed before implementation. Review resolved stale endpoint selection, safe optional Card projection, browser credential override, and separate read-auth versus cloud-permission failures. No unresolved in-scope findings. - -| Check | Result | Evidence / limits | -| --- | --- | --- | -| `uv run --extra dev pytest tests/cli/test_a2a_center_actions.py tests/cli/test_a2a_center_registration.py tests/cli/test_frontend_runtime_proxy.py tests/test_mpa_collaborator_proxy_policy.py -q` | pass | 227 passed (6 warnings); MockTransport exercises actual Studio mounting and the shared helper, including successful simulated deletion and zero cloud calls on binding-auth refusal. | -| `npm --prefix frontend test` | pass | 1389 Node tests and 183 Vitest tests across 11 files, including 36 collaborating-Agent cases. | -| `npm --prefix frontend run build` | pass | Current packaged WebUI rebuilt; existing large-chunk advisory remains. | -| `npm --prefix frontend run check:i18n`; `npm --prefix frontend run test:webui-assets` | pass | 2 locales / 22 namespaces; 114 packaged files / 351 internal references. | -| `frontend/node_modules/.bin/tsc --noEmit -p frontend/tsconfig.json` | pass | No diagnostics. | -| `uv run --extra dev pre-commit run ruff-check --files ...`; `ruff-format` with the same files | pass | a2a_center.py, test_a2a_center_actions.py, test_frontend_runtime_proxy.py, cli_frontend.py. Formatter changes were reconciled before final tests. | -| Pyright using existing adjacent-repository executable, `--pythonpath .venv/bin/python frontend/server/a2a_center.py tests/cli/test_a2a_center_actions.py` | pass | 0 errors/warnings; no dependency installation or global configuration changes. | -| Whole-file CLI/proxy-test Pyright | fail (baseline) | 37 CLI and 20 existing proxy-test diagnostics. Re-running with only this change's helper block and proxy tests removed produced identical diagnostic-message multisets (57 each), establishing no added diagnostics. Temporary comparison files removed. | -| Browser | pass | Real browser with isolated mocked fetch: endpoint descriptions and full selected URL, mouse and keyboard switching, cancel/focus return, details/skills, 390px iframe and internal scrolling, and actionable preflight-auth alert. Fixtures and Vite process removed. | -| Live local read integration | pass | Current localhost Studio exposes the new endpoint route, serves the matching built app, and returns HTTP 200 for both real source Runtime endpoint reads and both registry detail reads. Observed one Runtime with public/private and another with public only; effective addresses and skills matched the console records. No cloud write performed. | -| Scoped gitleaks with repository config; document links/pairs/IDs; `git diff --check` | pass | No leaked secrets found; existing scanner exclusions retained, no bypass. | -| Full Python regression, commit-time all-files pre-commit, live cloud writes | not_run | Changes are scoped to A2A routes and a path-filtered existing auth policy; affected proxy/API consumers tested. No commit/push authorized, so branch synchronization and commit gates were not invoked. Real cloud deletion/registration is not proven by mocks or read-only checks. | - -T-1/T-2/T-3 and AC-1/AC-2/AC-3 completed. Current Studio was restarted externally during the task; this agent did not interrupt it. New backend and built app are active; refreshing the browser loads the new UI. No commit, push or Runtime deployment. Retained all unrelated user changes. Subsequent name investigation used read-only live values and did not alter AgentCard or registration names. diff --git a/prd-spec/features/mpa-collaborating-agents/2026-10-22-endpoints-and-skills.zh.md b/prd-spec/features/mpa-collaborating-agents/2026-10-22-endpoints-and-skills.zh.md deleted file mode 100644 index e077b33c6..000000000 --- a/prd-spec/features/mpa-collaborating-agents/2026-10-22-endpoints-and-skills.zh.md +++ /dev/null @@ -1,58 +0,0 @@ -# A2A Runtime 端点和 Agent 详情 - -[English](2026-10-22-endpoints-and-skills.md) - -ID:mpa-a2a-endpoints-and-skills。创建/修订:2026-10-22。状态:implemented。 -关联:[组件](../../../specs/mpa-collaborating-agents/README.zh.md)、[成员操作](2026-10-22-member-actions.zh.md)、[认证修复](../../bugfixes/mpa-a2a-space-deletion/2026-10-22-runtime-auth.zh.md)。 - -## 背景与证据 - -注册当前固定提供 public/private,未读取实际 Runtime 地址;详情投影省略 AgentCard 服务地址和技能。用户要求选项与控制台对齐、显示具体地址、查看时显示技能,并以“按此实现”批准。官方[注册](https://docs.volcengine.com/docs/86681/2229304)、[服务地址](https://docs.volcengine.com/docs/86681/2248691)和 [GetA2aAgent](https://docs.volcengine.com/docs/86681/2249666) 描述了 Runtime 实际公私网地址与 AgentCard 元数据。已部署 MPA 注册中心将 AgentCard 解析为 JSON 字符串/对象,但公开 Get 文档示例不一致,因此缺失/不可解析元数据必须明确提示,不能编造。 - -## 目标、场景与需求 - -FR-1:选择或输入来源 Runtime 后,在目标地域读取其已授权网络配置。仅列已配置的公私网端点,显示本地化“公网地址/私网地址”和具体地址。原类型仍可用时保留,否则选择首个可用类型。切换 Runtime 立即使旧选项失效;区分加载、空和读取错误。支持重试;切换 Runtime/地域/来源及卸载时中止旧请求。没有当前匹配端点时禁止提交。注册 Payload 仍为 RuntimeId 加 NetworkType,不把浏览器提供的 URL 作为注册目的地。 -FR-2:查看显示生效 AgentCard 的服务地址,以及技能名称、描述、标签、示例,均为纯文本。服务 URL 与 AgentCard 文件 URL 分开。区分已知无技能与元数据缺失/无效。可选 Card 元数据不可用时保留基本详情。保留有界投影、身份检查和读取重试。 -FR-3:URL 移除 query/fragment,拒绝带凭据、非 HTTP(S)、无效端口、控制字符和超长地址。不返回原始 Card、安全方案、请求头、Key、提供商原因或环境变量,不抓取 Card URL。Card 输入限制 128 KiB;技能最多 100 项并明确提示截断;ID/名称/描述分别最多 256/512/4096 字符;标签/示例各最多 20 项,每项最多 128/1024 字符。技能元数据无效表示不可用,不能伪装为空。复用表单控件、ModalButton、字体和滚动;窄屏长地址/文本可换行。 - -## 设计、合同与兼容 - -GET /web/a2a-runtime-endpoints/{sourceRuntimeId}?targetRuntimeId=...®ion=... 要求 Studio 管理员及两个 Runtime ID 的授权。返回 {runtimeId, endpoints:[{networkType:public|private,url}]},最多两项,每种已配置类型一项;没有安全可用端点时明确返回空。不暴露 Key。增加专用客户端/读取,不扩大共享 Runtime 列表/详情 DTO。注册资格和并发竞态仍由提供商负责。 - -GET /web/a2a-agents/{agentId} 增加 serviceAddress:string|null、skills:Array<{id,name,description,tags:string[],examples:string[]}>|null、skillsTruncated:boolean。增量字段兼容旧调用者。null 为不可用,[] 为已确认无技能。仅查看时读取元数据,DELETE 校验不解析,避免无效可选 Card 改变注销资格。不改变 schema/数据、不新增依赖、不改 SDK 公共签名、Runtime 部署、派发、Harness 或 IAM。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| FR-1 | T-1:端点路由/客户端/解析,再实现注册状态与选择 | AC-1:仅公网/仅私网/两者/无端点、重试、迟到响应、无效手输 ID 和提交门禁正确;凭据不泄露 | Python 端点授权/投影;frontend/tests/collaboratingAgents.test.tsx | -| FR-2 | T-2:有界 Card 投影与详情展示 | AC-2:Runtime/Standard 详情显示地址与技能;空/不可用不同;保留纯文本及身份检查 | Python 元数据测试;UI 弹窗测试;浏览器 | -| FR-3 | T-3:安全/兼容、文档及发布产物 | AC-3:排除不安全 URL 和原始凭据,无效/超限元数据明确;保留键盘/窄屏/加载/错误/取消行为 | 受影响 Python、Ruff/Pyright;npm test/build/check:i18n/test:webui-assets;tsc;浏览器;密钥扫描;git diff --check | - -## 评审、批准、风险和验证 - -没有 review-spec 技能,已直接评审双语等价、API 归属、授权、安全、失败、兼容、取消和测试,无阻塞。用户在生产/测试修改前确认范围方案。复用 UI 控件和提示满足布局/反馈基准,不换风格、不加依赖。Runtime 端点读取可能独立于列表失败,须明确显示并可重试。安全地址有意省略 query/fragment 中的秘密。验证期间不自动写入或真实云注册/删除,模拟测试不能证明真实端到端云成功。 - -日期:2026-10-22。范围:HEAD 9ab0ef2d 之上的当前未提交 A2A 差异。最终检查记录于交付证据。提交、推送和云部署未获本次授权。 - - -## 交付证据(2026-10-22) - -在 9ab0ef2d 之上的已有未提交 A2A 变更中实现已批准范围。测试先行证据:实现前 22 项定向 Python 和 8 项 UI 用例失败。评审解决迟到端点选择、可选 Card 安全投影、浏览器凭据覆盖以及读取认证/云权限错误区分,无范围内未解决发现。 - -| 检查 | 结果 | 证据 / 限制 | -| --- | --- | --- | -| `uv run --extra dev pytest tests/cli/test_a2a_center_actions.py tests/cli/test_a2a_center_registration.py tests/cli/test_frontend_runtime_proxy.py tests/test_mpa_collaborator_proxy_policy.py -q` | pass | 227 passed(6 warnings);MockTransport 覆盖真实 Studio 挂载和共享 helper,包括成功模拟删除以及绑定认证拒绝后零云调用。 | -| `npm --prefix frontend test` | pass | 1389 项 Node 和 11 文件 183 项 Vitest,其中协作 Agent 36 项。 | -| `npm --prefix frontend run build` | pass | 已重建当前 WebUI 产物;保留既有大分块提示。 | -| `npm --prefix frontend run check:i18n`;`npm --prefix frontend run test:webui-assets` | pass | 2 语言 / 22 命名空间;114 产物文件 / 351 内部引用。 | -| `frontend/node_modules/.bin/tsc --noEmit -p frontend/tsconfig.json` | pass | 无诊断。 | -| `uv run --extra dev pre-commit run ruff-check --files ...`;相同文件的 `ruff-format` | pass | a2a_center.py、test_a2a_center_actions.py、test_frontend_runtime_proxy.py、cli_frontend.py;格式化变更已在最终测试前对齐。 | -| 使用相邻仓库已有 Pyright,`--pythonpath .venv/bin/python frontend/server/a2a_center.py tests/cli/test_a2a_center_actions.py` | pass | 0 错误/警告;未安装依赖或改变全局配置。 | -| CLI / 代理测试整文件 Pyright | fail(基线) | CLI 37 项和代理测试既有 20 项诊断。仅移除本次 helper 块和代理测试后,对照诊断消息多重集完全一致(各 57 项),无新增诊断。临时对照文件已删除。 | -| 浏览器 | pass | 隔离模拟 fetch 的真实浏览器:端点描述和选中完整 URL、鼠标/键盘切换、取消/焦点返回、详情/技能、390px iframe 与内部滚动、可操作的前置认证提示。夹具和 Vite 进程已删除。 | -| 本机实时只读集成 | pass | 当前 localhost Studio 已提供新端点路由并服务匹配构建,两个真实来源 Runtime 的端点读取及两个注册详情读取均 HTTP 200。一条同时有公私网,另一条仅公网;生效地址和技能与控制台记录一致。未执行云写入。 | -| 仓库配置的范围内 gitleaks;文档链接/双语/编号;`git diff --check` | pass | 未发现秘密泄露;保留既有扫描排除项,未绕过。 | -| 全量 Python 回归、提交前全文件 pre-commit、真实云写入 | not_run | 变更限 A2A 路由和已有按路径过滤认证策略,已验证受影响代理/API 调用者。未授权提交/推送,因此未执行分支同步及提交门禁。模拟或只读检查不证明真实云删除/注册成功。 | - -T-1/T-2/T-3、AC-1/AC-2/AC-3 完成。任务期间 Studio 由外部重启,本 Agent 未中断进程。新后端和构建已生效,浏览器刷新加载新界面。未提交、推送或部署 Runtime,保留全部无关用户修改。后续名称调查仅读取实时值,未改变 AgentCard 或注册名称。 diff --git a/prd-spec/features/mpa-collaborating-agents/2026-10-22-member-actions.md b/prd-spec/features/mpa-collaborating-agents/2026-10-22-member-actions.md deleted file mode 100644 index fef9b6a3a..000000000 --- a/prd-spec/features/mpa-collaborating-agents/2026-10-22-member-actions.md +++ /dev/null @@ -1,61 +0,0 @@ -# A2A member actions, space deletion and explicit registration errors - -[中文版](2026-10-22-member-actions.zh.md) - -Date: 2026-10-22. ID: mpa-a2a-member-actions. Status: implemented. User approved Studio detail dialogs and deregistration, added space deletion, and reported RuntimeId already exists after registering the same Runtime in Default. Related: [component contract](../../../specs/mpa-collaborating-agents/README.md), [space management](2026-10-22-space-management.md). - -## Evidence and requirements - -FR-1: Each member has View and Deregister actions. View opens the existing ModalButton/ModalLayout dialog and calls GetA2aAgent with Id and SpaceId. Display only bounded name/status/source/default-version/project and Runtime ID/network/import type fields. Never return original Cards, URLs with credentials, provider Reason or raw response bodies. Validate returned agent and space IDs before display or deletion. - -FR-2: Deregister requires a named confirmation explaining removal from this shared space and its effect on discovery/calls while preserving the source Runtime. Authorize Studio management and target Runtime, verify GetA2aAgent identity, then call DeleteA2aAgent with Id. Success refreshes members; it never deletes Runtime or changes binding. No automatic retry on ambiguous outcomes. - -FR-3: Add Delete space beside space selection. Confirmation identifies the selected space and irreversible effect. Before DeleteA2aSpace, authorize the target, read its current Runtime binding and reject the bound space (409 space_bound), verify GetA2aSpace identity, and query ListA2aAgents page 1 size 1. Reject nonempty spaces (409 space_not_empty). Never cascade deregistration or auto-unbind. Cloud API owns races and other callers; users must confirm other callers no longer depend on the space. Success refreshes spaces without retaining a deleted local option. Current-MPA binding check does not prove no other MPA uses this shared space. - -FR-4: Distinguish explicit cloud refusal from uncertain writes. The current create handler collapses all exceptions into 502. The reported RuntimeId already exists message is an explicit rejection, not timeout evidence. Add an opt-in structured-error mode to the existing signed helper, used only by A2A Center. Map known provider status/code/message signatures to safe reason enums (runtime_already_registered, forbidden, invalid_parameter, not_found, conflict, rate_limited). Never return raw provider messages. Network/5xx/malformed success stay uncertain and require refresh. No automatic retry or workaround registration. Public docs do not establish a universal cross-account/region uniqueness rule; diagnostics apply to this actual Runtime-source rejection. - -## Interfaces, concurrency and boundaries - -GET/DELETE /web/a2a-agents/{agentId}?targetRuntimeId=...&spaceId=...®ion=... provide sanitized detail or {deleted:true}. DELETE /web/a2a-spaces/{spaceId}?targetRuntimeId=...®ion=... returns {deleted:true}. Existing admin, region, ID and target authorization apply before cloud effects. Read preflight failures perform no deletion. Result IDs must match the requested target; malformed responses fail closed. Details have explicit loading/error/retry; mutation confirmation shows known rejection or unconfirmed outcome. Closing during submission aborts the browser request, does not claim cloud rollback and locks further mutations until refresh. Runtime/space changes unmount dialogs and abort stale responses. Shared busy state prevents simultaneous create/bind/deregister/delete; independent views remain read-only. Success followed by failed refresh stays a successful mutation with a list error. Reuse existing component tokens/spacing, no dependency or icon additions. - -Non-goals: source Runtime deletion, live registration/deletion during verification, automatic moving between spaces, raw Card editing, credential display, provider-wide policy changes, Runtime/Harness changes. Existing callers of the signed helper retain their prior errors because structured mode defaults off. Default Runtime list pagination is not changed. - -## Tasks, tests and acceptance - -T-1 / AC-1 (FR-1): add failing detail identity/redaction/auth tests and UI modal/loading/retry tests, then implement bounded projections. -T-2 / AC-2 (FR-2): test one explicit unregister write, wrong-space refusal, cancellation, permission denial and post-success refresh; implement confirmation and client. -T-3 / AC-3 (FR-3): test bound/nonempty/malformed checks, zero writes on refusal, exact DeleteA2aSpace payload and stale dialog cleanup; implement deletion. -T-4 / AC-4 (FR-4): test reported duplicate signature and 400/403 vs network/malformed outcomes, preservation of non-A2A helper behavior, and no auto-retry; implement sanitized errors. -T-5 / AC-5: reconcile both document languages, frontend README and assets; run targeted Python API/proxy tests, npm test/build/check:i18n/test:webui-assets, changed-file Ruff/Pyright and tsc, browser mocked normal/error/cancel/narrow cases, scoped secret scan and diff checks. Record actual outcomes below. Do not claim live cloud writes from mocks. - -## Review and approval - -Direct design review substitutes for unavailable review-spec. User approval: “可以,按这个给我实现” and “增加删除空间的功能”. Official docs read on 2026-10-22: [GetA2aAgent](https://docs.volcengine.com/docs/86681/2249666), [DeleteA2aAgent](https://docs.volcengine.com/docs/86681/2249664), [DeleteA2aSpace](https://docs.volcengine.com/docs/86681/2249661), [delete space prerequisite](https://docs.volcengine.com/docs/86681/2248661). DeleteA2aAgent's example mistakenly uses an as- prefix; implementation uses the actual selected a- member identity and verifies it with GetA2aAgent. No production edits before this design review. Scope, identity, permissions, bounds, bilingual equivalence, state recovery and testability reviewed with no blockers. - -Risks: shared-space deletion/deregistration affects other callers; require confirmation, no live writes in this task. Cloud preflight cannot lock registry membership, so provider rejection remains authoritative. Ambiguous writes never silently succeed or auto-retry. Binary rollback does not restore removed cloud registrations/spaces; recovery is explicit recreation by an operator, not an automatic code path. Initial verification: not_run; implementation and actual evidence follow. No commit/push/deployment requested. - -## Implementation, review and verification (2026-10-22) - -Status: implemented locally, no cloud mutation performed. T-1 through T-5 and AC-1 through AC-5 completed for the incremental diff over the existing uncommitted Center changes. Test-first evidence: 19 Python cases and 4 frontend cases failed before the implementation. Final review fixed repeat submission during dialog exit, distinguished failed read preflight (no write executed) from unknown post-write outcomes, and preserved explicit Runtime duplicate errors even when a provider uses an inappropriate 5xx status. Only the known duplicate signature is recognized; unrelated 5xx/network errors stay uncertain. API projections do not expose raw Cards, original configuration URLs or provider error bodies. - -| Check | Result | Evidence / limitation | -| --- | --- | --- | -| uv run --extra dev pytest tests/cli/test_a2a_center_actions.py tests/cli/test_a2a_center_registration.py tests/test_mpa_collaborator_proxy_policy.py -q | pass | 70 passed; local mocked cloud, identity/auth/redaction/empty-space/error classification | -| uv run --extra dev pytest tests/cli/test_frontend_runtime_proxy.py -q | pass | 129 passed; signed-helper default compatibility and existing proxy regressions | -| npm --prefix frontend test | pass | 1389 Node tests; 175 Vitest cases in 11 files, including 28 collaborator cases | -| npm --prefix frontend run build | pass | updated packaged assets; existing large-chunk warning | -| npm --prefix frontend run check:i18n | pass | 2 locales / 22 namespaces | -| npm --prefix frontend run test:webui-assets | pass | 114 packaged files / 351 references | -| frontend/node_modules/.bin/tsc --noEmit -p frontend/tsconfig.json | pass | no diagnostics | -| pre-commit ruff-check / ruff-format for changed Python | pass | frontend/server/a2a_center.py, tests/cli/test_a2a_center_actions.py, veadk/cli/cli_frontend.py | -| Pyright, new server module and tests, .venv/bin/python | pass | 0 errors/warnings; existing Pyright executable with repository interpreter | -| CLI whole-file Pyright | fail (baseline) | 37 diagnostics; exact severity/message/rule multiset equals a comparison restoring the previous signed helper/mount; no new diagnostics | -| Browser | pass | real browser, simulated API: detail dialog, 390px view, Escape/focus restoration, explicit unregister and refresh, empty-space delete and removal of stale selection; preflight failure/uncertainty/late responses also covered in React tests | -| Source secret scan / diff whitespace | pass | gitleaks with repository configuration; existing exclusions remain. No policy bypass | -| Full Python regression / live cloud mutations | not_run | shared helper defaults preserved and affected proxy/API suites pass; no permission to mutate live registrations/spaces during verification | - -The provider's reported duplicate Runtime error explains this incident; list reads alone do not establish global uniqueness or eventual mutation outcome. No automatic relocation, deregistration, deletion, retry, commit, push or cloud deployment occurred. Local service activation and asset checks are recorded separately below. Temporary fixtures and diagnostic comparison files are removed after verification. - -Review refinement: public documentation does not establish a separate local ban on deleting a default-marked space. The UI/server therefore enforce the documented empty-space prerequisite and the current-MPA unbind requirement, then let AgentKit enforce its actual deletion policy. No deletion was attempted against a live default space. - -Local activation verified: restarted the same `veadk studio` command, checked the current built entry and JS assets (HTTP 200), confirmed GET/DELETE member routes and DELETE space route in live OpenAPI, and received HTTP 422 for an invalid target without a cloud request. Temporary browser fixtures, the comparison Python file and the verification Vite process are removed. The latest build, targeted tests, i18n, type checks, secret scan and asset validation were reconciled after the default-space policy refinement. No commit or cloud mutation was performed. diff --git a/prd-spec/features/mpa-collaborating-agents/2026-10-22-member-actions.zh.md b/prd-spec/features/mpa-collaborating-agents/2026-10-22-member-actions.zh.md deleted file mode 100644 index 584df8809..000000000 --- a/prd-spec/features/mpa-collaborating-agents/2026-10-22-member-actions.zh.md +++ /dev/null @@ -1,61 +0,0 @@ -# A2A 成员操作、空间删除与明确注册错误 - -[English](2026-10-22-member-actions.md) - -日期:2026-10-22。ID:mpa-a2a-member-actions。状态:implemented。用户批准 Studio 详情弹窗和注销,新增空间删除,并报告同一 Runtime 在 Default 注册后出现 RuntimeId already exists。关联:[组件合同](../../../specs/mpa-collaborating-agents/README.zh.md)、[空间管理](2026-10-22-space-management.zh.md)。 - -## 证据与需求 - -FR-1:每个成员提供查看、注销。查看使用现有 ModalButton/ModalLayout 弹窗,以 Id、SpaceId 调用 GetA2aAgent。仅展示有长度限制的名称/状态/来源/默认版本/项目,以及 Runtime ID/网络/导入类型。不返回原始 Card、含凭据 URL、提供商 Reason 或原始响应。展示或删除前校验返回的 Agent 和空间 ID。 - -FR-2:注销前显示带名称的确认,说明会移除共享空间内注册、影响发现/调用,同时保留来源 Runtime。校验 Studio 管理权限和目标 Runtime,核对 GetA2aAgent 身份,再以 Id 调用 DeleteA2aAgent。成功刷新成员,不删除 Runtime,不改绑定。结果不明不自动重试。 - -FR-3:空间选择旁增加删除空间。确认指明所选空间和不可恢复影响。调用 DeleteA2aSpace 前,授权目标,读取目标 Runtime 当前绑定,拒绝已绑定空间(409 space_bound);核对 GetA2aSpace 的身份,并查询 ListA2aAgents 第 1 页、大小 1。拒绝非空空间(409 space_not_empty)。不级联注销、不自动解绑。并发及其他调用者由云 API 管理;用户需确认其他调用方已停止依赖该空间。成功刷新空间,不保留已删除的本地选项。当前 MPA 绑定检查不证明其他 MPA 未使用此共享空间。 - -FR-4:区分云端明确拒绝与写入不明。现有创建处理将所有异常归为 502。用户报告的 RuntimeId already exists 是明确拒绝,并非超时证据。为现有签名 helper 增加可选结构化错误模式,仅供 A2A 中心使用。将已知提供商状态/代码/消息特征映射为安全原因枚举(runtime_already_registered、forbidden、invalid_parameter、not_found、conflict、rate_limited)。不返回原始提供商消息。网络/5xx/成功响应损坏仍为不明,须刷新核对。不自动重试或绕过注册。公开文档未定义普遍跨账号/地域唯一规则;诊断针对本次 Runtime 来源拒绝。 - -## 接口、并发与边界 - -GET/DELETE /web/a2a-agents/{agentId}?targetRuntimeId=...&spaceId=...®ion=... 返回脱敏详情或 {deleted:true}。DELETE /web/a2a-spaces/{spaceId}?targetRuntimeId=...®ion=... 返回 {deleted:true}。云操作前执行已有管理员、地域、ID 与目标授权。前置读取失败不删除。返回 ID 必须匹配所请求目标;无效响应快速失败。详情显式展示加载/错误/重试;写操作确认展示明确拒绝或结果不明。提交期间关闭会中止浏览器请求,不声称云端回滚,并锁定后续写操作直至刷新。切换 Runtime/空间卸载弹窗并中止旧响应。共享 busy 防止创建/绑定/注销/删除并发;详情查询仅只读。成功后刷新失败仍为写入成功,并显示列表错误。复用已有组件变量/间距,不新增依赖或图标。 - -非目标:删除来源 Runtime、验证期间真实注册/删除、自动跨空间迁移、编辑原始 Card、显示凭据、提供商全局策略改变、Runtime/Harness 变化。签名 helper 的结构化模式默认关闭,原有调用者错误行为保持不变。不改 Runtime 列表默认分页。 - -## 任务、测试与验收 - -T-1 / AC-1(FR-1):先补详情身份/脱敏/授权失败测试,以及 UI 弹窗/加载/重试测试,再实现受限投影。 -T-2 / AC-2(FR-2):测试一次明确注销写入、错误空间拒绝、取消、权限失败与成功后刷新;实现确认与客户端。 -T-3 / AC-3(FR-3):测试绑定/非空/无效响应检查、拒绝时零写入、精确 DeleteA2aSpace Payload 及旧弹窗清理;实现删除。 -T-4 / AC-4(FR-4):测试用户报告的重复特征、400/403 与网络/无效响应区分、非 A2A helper 行为保留、不自动重试;实现错误脱敏。 -T-5 / AC-5:对齐双语文档、frontend README 与产物;执行定向 Python API/代理测试、npm test/build/check:i18n/test:webui-assets、变更文件 Ruff/Pyright 与 tsc、真实浏览器模拟正常/失败/取消/窄屏,以及范围内密钥扫描和差异检查。在下方记录实际结果,不以模拟测试声称真实云写入通过。 - -## 评审与批准 - -review-spec 不可用,直接评审。用户批准:“可以,按这个给我实现”和“增加删除空间的功能”。2026-10-22 读取官方文档:[GetA2aAgent](https://docs.volcengine.com/docs/86681/2249666)、[DeleteA2aAgent](https://docs.volcengine.com/docs/86681/2249664)、[DeleteA2aSpace](https://docs.volcengine.com/docs/86681/2249661)、[删除空间前提](https://docs.volcengine.com/docs/86681/2248661)。DeleteA2aAgent 示例错误地使用 as- 前缀;实现使用实际选中的 a- 成员身份,并用 GetA2aAgent 核对。此设计评审前不修改生产代码。范围、身份、权限、边界、双语一致性、状态恢复和可测试性均无阻塞项。 - -风险:共享空间删除/注销影响其他调用方,必须确认,本次不做真实写入。云端前置查询不能锁定成员,因此提供商拒绝仍是最终依据。结果不明不静默成功、不自动重试。回滚代码不会恢复云注册/空间;恢复应由操作者明确重新创建,不属于自动路径。初始验证:not_run;实现与实际证据随后记录。未请求提交、推送或部署。 - -## 实现、评审与验证(2026-10-22) - -状态:已本地实现,未执行云端写入。已在原有未提交中心变更之上完成 T-1 至 T-5、AC-1 至 AC-5。测试先行证据:实现前 19 项 Python 用例及 4 项前端用例失败。最终评审修正弹窗退出期间重复提交,区分读取前置检查失败(尚未写入)与写入后结果不明,并在提供商错误使用 5xx 状态时仍保留明确的 Runtime 重复业务错误。仅识别已知重复特征;其他 5xx/网络错误保持结果不明。API 投影不暴露原始 Card、原始配置 URL 或提供商错误体。 - -| 检查 | 结果 | 证据 / 限制 | -| --- | --- | --- | -| uv run --extra dev pytest tests/cli/test_a2a_center_actions.py tests/cli/test_a2a_center_registration.py tests/test_mpa_collaborator_proxy_policy.py -q | pass | 70 passed;本地模拟云,身份/授权/脱敏/空空间/错误分类 | -| uv run --extra dev pytest tests/cli/test_frontend_runtime_proxy.py -q | pass | 129 passed;签名 helper 默认兼容性及原代理回归 | -| npm --prefix frontend test | pass | 1389 项 Node 测试;11 文件 175 项 Vitest 用例,其中协作页面 28 项 | -| npm --prefix frontend run build | pass | 更新发布产物;既有大分块警告 | -| npm --prefix frontend run check:i18n | pass | 2 种语言 / 22 个命名空间 | -| npm --prefix frontend run test:webui-assets | pass | 114 个发布文件 / 351 个引用 | -| frontend/node_modules/.bin/tsc --noEmit -p frontend/tsconfig.json | pass | 无诊断 | -| 变更 Python 的 pre-commit ruff-check / ruff-format | pass | frontend/server/a2a_center.py、tests/cli/test_a2a_center_actions.py、veadk/cli/cli_frontend.py | -| 新增服务端模块及测试 Pyright,.venv/bin/python | pass | 0 错误/警告;复用现有 Pyright 可执行文件和仓库解释器 | -| CLI 整文件 Pyright | fail(基线) | 37 项诊断;与恢复旧签名 helper/挂载的对照在严重性/消息/规则多重集上完全一致,无新增诊断 | -| 浏览器 | pass | 真实浏览器、模拟 API:详情弹窗、390px 视图、Escape/焦点恢复、明确注销及刷新、空空间删除与旧选择移除;React 测试另覆盖前置检查失败/结果不明/迟到响应 | -| 源码密钥扫描 / 差异空白 | pass | 使用仓库配置执行 gitleaks;保留既有排除项,未绕过策略 | -| 全量 Python 回归 / 真实云写入 | not_run | 共享 helper 默认行为保留,受影响代理/API 测试通过;验证期间未授权修改真实注册/空间 | - -提供商报告的 Runtime 重复错误解释了本次问题;只读列表不能证明全局唯一性或最终写入结果。未自动迁移、注销、删除、重试、提交、推送或云部署。本机服务生效与产物检查在下方另记。验证后删除临时夹具及诊断对照文件。 - -评审补充:公开文档未定义默认标记空间的独立本地删除禁令。因此 UI/服务端仅校验官方的空间为空前提及当前 MPA 已解绑要求,实际云端删除策略交给 AgentKit 判断。未尝试删除真实默认空间。 - -本机生效已验证:沿用原 `veadk studio` 命令重启,确认当前构建入口与 JS 产物(HTTP 200),实时 OpenAPI 包含成员 GET/DELETE 和空间 DELETE 路由;无效目标返回 HTTP 422,未触发云请求。临时浏览器夹具、Python 对照文件和验证 Vite 进程已删除。默认空间策略评审修正后,已对齐最终构建、定向测试、i18n、类型检查、密钥扫描及产物检查。未提交代码或执行云端写入。 diff --git a/prd-spec/features/mpa-collaborating-agents/2026-10-22-space-aliases.md b/prd-spec/features/mpa-collaborating-agents/2026-10-22-space-aliases.md deleted file mode 100644 index 74b65b954..000000000 --- a/prd-spec/features/mpa-collaborating-agents/2026-10-22-space-aliases.md +++ /dev/null @@ -1,33 +0,0 @@ -# Space-shared aliases and explicit A2A mentions - -Change ID: agentkit-a2a-collaborators | Date: 2026-10-22 | Status: approved -[中文](2026-10-22-space-aliases.zh.md) - -## Evidence, goals and boundaries - -A2ACenter.tsx lists AgentKit registrations through the selected Runtime; a2aSpace.ts validates responses. The user explicitly approved editing aliases shared by all MPAs connected to a space, plus @alias/@AgentCardName dispatch, without changing AgentKit. Alias metadata belongs in the existing shared management PostgreSQL database, not browser storage or individual Runtime business databases. Runtime owns persistence, resolution and authorization. Studio owns editing and feedback. No autocomplete, multiple-target orchestration, cache or cloud write is included. - -## Requirements and design - -FR-1: Show an optional alias on each center member; edit and clear through the existing administrator-authenticated Runtime proxy. Reuse Input, FormLabel and Button plus existing form styles. Empty means clear; preserve original center name. Explain the alias is shared within the space. Normalization and uniqueness are server authoritative. -FR-2: PUT /api/v1/studio/collaborators/registry/aliases/{agent_id} accepts {space_id, alias:string|null, revision:int}; returns {alias,revision}. List adds Alias, AliasRevision and AliasAvailable. Old Runtime responses leave editing unavailable. Missing shared DB/account configuration is explicit, not local-storage fallback. Revision conflict and duplicate name have distinct feedback. An uncertain write requires refresh; no automatic mutation retry. -FR-3: A current-message prefix @name followed by whitespace/task, or @"name with spaces", identifies exactly one target. Runtime resolves the union of aliases and actual Card.name in the bound space, with NFKC/casefold comparison, then validates the dispatch ID. Ambiguity, missing name, incomplete directory, multiple leading targets or missing task prevents dispatch. Text within quotes/code, emails and ordinary body mentions do not route. This behavior is implemented in Runtime, not as a browser text rewrite. - -## Scenarios, tests and acceptance - -AC-1 / FR-1: Given a listed member, editing/clearing saves and displays server-confirmed data; original name and space selection remain. Tests: frontend/tests/a2aAlias.test.tsx. -AC-2 / FR-2: Conflict retains input; refresh restores latest revision. Abort/unmount discards late replies. IME cannot submit early; duplicate clicks produce one request. Missing capability disables editing with an explanation. Same test file and real browser check. -AC-3 / FR-3: Two Runtime business databases use one alias store; exact name resolution and mismatched dispatch are covered in agentkit-mpa-agent/tests/test_a2a_aliases.py. No remote Agent invocation is claimed by mocked tests. - -## Tasks, risk and review - -T1 write failing UI tests; T2 typed response projection/editor and translations; T3 npm test/build, browser normal/error/keyboard/narrow checks; T4 update paired specs and verification. No Python Studio server changes needed because the existing Runtime proxy carries these routes. No generated Agent code change. No external dependency. - -Shared database configuration and privileges must be present on the Runtime; old instances require updated Runtime code. Reload after uncertain writes before retrying. Rollback the UI/Runtime while retaining new optional metadata. Source review checked account/space isolation, CAS and normalization ownership. User approval is the current request to implement both repositories; detailed @ grammar is the documented implementation default. Local verification is recorded below; no publication/deployment performed. - - -## Local verification - -Implemented in Studio and Runtime. Studio Node tests:1389 passed; Vitest:192 passed; production build passed. A real-browser local fixture of the editor (mock transport) verified save, clear, duplicate-name feedback, Escape/focus and a320px column. No cloud calls, deployment or model end-to-end acceptance is claimed. Runtime evidence: agentkit-mpa-agent/docs/verification/agentkit-a2a-collaborators/alias-verification.md. - -> Superseded storage decision: see `prd-spec/bugfixes/mpa-agent-alias/2026-10-22-agent-alias-business-store.md`. Registered Agent aliases now belong to the current MPA business database. diff --git a/prd-spec/features/mpa-collaborating-agents/2026-10-22-space-aliases.zh.md b/prd-spec/features/mpa-collaborating-agents/2026-10-22-space-aliases.zh.md deleted file mode 100644 index d9e615a1b..000000000 --- a/prd-spec/features/mpa-collaborating-agents/2026-10-22-space-aliases.zh.md +++ /dev/null @@ -1,33 +0,0 @@ -# 空间共享别名与显式 A2A 提及 - -Change ID: agentkit-a2a-collaborators | 日期:2026-10-22 | 状态:approved -[English](2026-10-22-space-aliases.md) - -## 证据、目标和边界 - -A2ACenter.tsx 通过选中 Runtime 列出 AgentKit 注册记录;a2aSpace.ts 校验响应。用户明确批准同一空间内全部 MPA 共享别名,以及 @别名/@AgentCardName 派发,不修改 AgentKit。别名存现有共享管理 PostgreSQL,不存浏览器或各 Runtime 业务库。Runtime 负责持久化、解析与授权,Studio 负责编辑反馈。不做自动补全、多目标编排、缓存或云端写入。 - -## 需求与设计 - -FR-1:中心每个成员展示可选别名,通过现有管理员 Runtime 代理编辑和清空。复用 Input、FormLabel、Button 与现有表单样式。留空即清空;保留中心原名。说明别名在空间内共享。规范化和唯一性以服务器为准。 -FR-2:PUT /api/v1/studio/collaborators/registry/aliases/{agent_id} 输入 {space_id, alias:string|null, revision:int},返回 {alias,revision}。列表增加 Alias、AliasRevision、AliasAvailable。旧 Runtime 无新字段时编辑不可用。共享 DB/账号配置缺失明确提示,不降级存本地。并发冲突与重名分别提示。写入结果不确定时刷新,不自动重试写入。 -FR-3:当前消息前缀 @名称 空白 任务,含空格名称使用 @"name with spaces",指定唯一目标。Runtime 在绑定空间中对别名和真实 Card.name 取并集,NFKC/casefold 比较,再核对派发 ID。歧义、未找到、目录不完整、多个开头目标或缺任务都不派发。引用/代码、邮箱和普通正文提及不路由。由 Runtime 实现,不由浏览器改写正文。 - -## 场景、测试与验收 - -AC-1 / FR-1:给定成员,编辑/清空后展示服务器确认的数据;原名及空间选择保留。测试 frontend/tests/a2aAlias.test.tsx。 -AC-2 / FR-2:冲突保留输入,刷新得到最新 revision。取消/卸载后忽略迟到响应;IME 不提前提交;重复点击只发送一次。缺能力禁用并说明。同一测试文件和真实浏览器检查。 -AC-3 / FR-3:两个 Runtime 业务库共享一份别名,精确解析及错误目标拒绝由 agentkit-mpa-agent/tests/test_a2a_aliases.py 覆盖。模拟测试不宣称真实远端调用成功。 - -## 任务、风险和评审 - -T1 先补失败 UI 测试;T2 类型校验、编辑器和翻译;T3 npm test/build 与浏览器正常/错误/键盘/窄窗检查;T4 同步双语合同及验证。现有 Runtime 代理承载路由,无需修改 Studio Python 服务,无 Agent 生成代码变更、无新增依赖。 - -Runtime 必须具备共享库配置与权限,旧实例需升级 Runtime。写入不确定先刷新。回滚 UI/Runtime 时保留可选新数据。设计已核对账号空间隔离、CAS 与规范化职责。用户授权是本轮两仓实现请求;@详细语法为本文实现默认值。验证待执行,未获发布部署授权。 - - -## 本地验证 - -Studio 和 Runtime 已实现。Studio Node tests1389通过、Vitest192通过、生产构建通过。本地浏览器使用真实编辑器/主题、模拟传输,验证保存、清空、重名提示、Escape/焦点恢复及320px窄列。未发布、未调用云端或完成真实模型端到端验收。Runtime证据见 agentkit-mpa-agent/docs/verification/agentkit-a2a-collaborators/alias-verification.md。 - -> 存储决定已被 `prd-spec/bugfixes/mpa-agent-alias/2026-10-22-agent-alias-business-store.zh.md` 取代:已注册 Agent 别名归当前 MPA 业务库。 diff --git a/prd-spec/features/mpa-collaborating-agents/2026-10-22-space-management.md b/prd-spec/features/mpa-collaborating-agents/2026-10-22-space-management.md deleted file mode 100644 index 0bedd4503..000000000 --- a/prd-spec/features/mpa-collaborating-agents/2026-10-22-space-management.md +++ /dev/null @@ -1,77 +0,0 @@ -# Collaborator layout and A2A space creation - -[中文版](2026-10-22-space-management.zh.md) - -Change ID: mpa-space-management. Date: 2026-10-22. Status: implemented. Predecessor: [Center registration](2026-10-22-center-registration.md). Contract: [MPA collaborating Agents](../../../specs/mpa-collaborating-agents/README.md). - -## Evidence, scope and scenarios - -A2ACenter currently places binding, registration and member content in one vertical flow with identical 16px spacing. The user approved separate binding and member areas and requested AgentKit space creation. Existing local Center work is preserved. The official [CreateA2aSpace API](https://docs.volcengine.com/docs/86681/2249657), read on 2026-10-22, accepts Name, optional ProjectName and IntentEnabled and returns Result.Id. Cloud creation has not been tested live. - -FR-1: Place space selection/binding/disconnection in “Space binding”; place registration, refresh and the member list in “Space members”. Use separate semantic sections, 32px between groups, shared left/right alignment, wrapping actions and existing theme tokens/fonts. Registration identifies its destination. Retain existing loading/error/empty/CAS and registration behavior. - -FR-2: Add “New space” beside the space selector, including when there are no spaces. The inline form collects name, project (default default), and semantic search (default false). Creating a space selects it and refreshes its members, but requires a separate explicit bind. The selected new space remains visible even if omitted from a paginated/eventually consistent list. Success with failed refresh is still creation success. - -FR-3: POST /web/a2a-spaces?region=... accepts targetRuntimeId, name, projectName (default default), intentEnabled (default false). Existing Studio admin and target Runtime authorization run before cloud writes. Studio invokes CreateA2aSpace with Name, ProjectName, IntentEnabled using existing signed cloud identity and returns only {id}. No Runtime API or deployment change. Name/project are trimmed nonempty strings up to 128 characters without control characters (Studio policy, not an asserted provider limit). Reject unknown fields and non-boolean intentEnabled. Body limit 4096 bytes. Provider denial is sanitized; ambiguous failures return 502 with an outcome-unknown code and never retry automatically. - -FR-4: Prevent concurrent creates, respect IME, focus the form on open and return focus on close. Cancel/unmount abort the browser request, never claim cloud rollback; cancel during submission or unknown outcome locks mutations until refresh. Runtime/region changes discard stale responses. Read-only refresh never performs creation. No browser credential persistence or raw provider errors. - -Non-goals: space editing/deletion, tags management, Runtime creation, automatic binding, dispatcher/Harness changes, cloud deployment, live cloud resource creation during verification. SDK, dependencies and generated projects are unaffected. - -## Design and alternatives - -Keep the existing AgentWorkspace detail shell and Button/Select/FormLabel/InputWithTailIcon/Checkbox primitives. CardLayout forces an unrelated icon/close action, so use two semantic sections within the existing shell rather than nesting another page layout. Reuse collaborators form styling, add only group/header/selector container spacing. Avoid additional tabs: selection and members need to remain visible together. No new dependency, icon, font or palette. Use the existing 14px text and studio text/border tokens. - -Extend frontend/server/a2a_center.py with the space route; add the client/type/response parser to adk/client.ts and adk/a2aSpace.ts. A2ASpaceCreation owns submit/abort/uncertain state; A2ACenter owns selection, binding and list refresh. One shared mutation gate prevents space creation and registration/binding overlapping. Unknown creation outcomes do not auto-retry. New space identity/name/project are transient UI state; the cloud remains authoritative. Restore focus only after the form closes and the parent stops being busy. Unmounts cannot apply late responses. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| FR-1 | T-1 group Center and update both locales | AC-1 binding and registration have distinct accessible regions and actions | frontend/tests/collaboratingAgents.test.tsx; browser desktop/narrow | -| FR-2 | T-2 create form/client and selection refresh | AC-2 empty/paginated space lists permit creation; success never writes binding | UI tests and mocked browser | -| FR-3 | T-3 validated authorized server route | AC-3 exact payload, input bounds, auth denial, sanitized failures, no retries | tests/cli/test_a2a_center_registration.py | -| FR-4 | T-4 async and keyboard tests | AC-4 no duplicate creates/stale writes; cancel/unknown require reconciliation; focus/IME work | React tests and browser | -| All | T-5 reconcile bilingual docs/build/review | AC-5 tests/build/i18n/assets/Ruff/Pyright and diff checks recorded | commands below | - -Test-first: add failing focused UI/API tests before production edits. Gates: uv run --extra dev pytest tests/cli/test_a2a_center_registration.py; npm --prefix frontend test; npm --prefix frontend run build; npm --prefix frontend run check:i18n; npm --prefix frontend run test:webui-assets; uv run ruff check frontend/server/a2a_center.py tests/cli/test_a2a_center_registration.py; repository-local Pyright for those Python files; real browser against mocked API without cloud writes. No commit requested; pre-commit and branch synchronization are not triggered by this task. - -## Review, approval, risks and delivery - -User approval: “帮我改下” accepts the proposed upper binding/lower member layout; “然后也加上agentkit这里新建空间的功能” authorizes the creation capability. Review-spec skill unavailable; direct review checks bilingual scope, API fields, authorization, bounds, no retry, cancel/late-response behavior, explicit binding, and testability. No design blockers. Component contract updated in both languages before implementation. - -Risks: provider acceptance may precede a timeout; UI requires read reconciliation before another attempt. Lists may lag or paginate away a new item; keep the confirmed ID selectable without claiming listed status. Studio credentials and Runtime identity must have consistent account/region access; no fallback across providers/accounts. Rollback the UI/server change without deleting created spaces. Live provider integration remains an explicit unverified area; local tests simulate it. - -Verification status: not_run before implementation. Date/scope: 2026-10-22, incremental space-management changes over the pre-existing dirty Center implementation. No commit, push or deployment authorized by this task. - - -## Final verification (2026-10-22) - -All T-1 through T-5 and AC-1 through AC-5 are complete for the incremental local implementation. Test-first evidence: before implementation, 18 new Python cases failed because the route did not exist, and 6 UI cases failed because the new controls/regions did not exist. Additional tests cover explicit project/search selection, empty-list creation and permission correction. Review corrected closing-focus recovery: when an uncertain result disables the opener, focus moves to Refresh; mutually exclusive forms cannot steal focus. Selector container width matches the existing 372px Select primitive without overriding it. No new component style exception was introduced. - -| Check | Result | Evidence and scope | -| --- | --- | --- | -| uv run --extra dev pytest tests/cli/test_a2a_center_registration.py tests/test_mpa_collaborator_proxy_policy.py -q | pass | 41 passed; mocked cloud, no live resources | -| npm --prefix frontend test | pass | 1389 Node tests and 164 Vitest cases, 11 files | -| focused collaboratingAgents.test.tsx | pass | 17 cases including final focus/IME/cancel/unknown/error and no automatic binding | -| npm --prefix frontend run build | pass | matching veadk/webui assets; existing chunk-size warning | -| npm --prefix frontend run check:i18n | pass | 2 locales, 22 namespaces | -| npm --prefix frontend run test:webui-assets | pass | 114 packaged files, 351 internal references | -| frontend/node_modules/.bin/tsc --noEmit -p frontend/tsconfig.json | pass | no diagnostics | -| pre-commit ruff-check and ruff-format for a2a_center.py and test_a2a_center_registration.py | pass | used repository hook environments; standalone uv run ruff is unavailable in the project environment | -| Pyright with .venv/bin/python for the two Python files | pass | 0 errors/warnings; existing adjacent-repository Pyright executable, project interpreter | -| Browser | pass | isolated real browser with simulated API: desktop grouping, creation selects without binding, empty state, permission denial and uncertain-write lock; keyboard focus and 390px narrow view checked | -| git diff --check and paired-document checks | pass | scoped document paths, IDs and translated contracts aligned | -| Live local route discovery | pass | 127.0.0.1:8000/openapi.json includes POST /web/a2a-spaces; no create request sent | -| Secret scan | pass | gitleaks dir with repository .gitleaks.toml over scoped source/docs; existing allowlist excludes tests and generated web assets | -| Full Python regression / real cloud creation | not_run | route-only change covered by targeted API/policy tests; cloud writes outside isolated verification | - -Implementation review checked authorization before cloud effects, strict fields and body bounds, result-ID validation, no raw provider errors, no retries, post-create refresh failure, cancellation/unmount and retained selection across pagination. Live AgentKit creation remains unverified. Existing unrelated local changes are retained. No commit/push/deployment was performed. Temporary browser fixtures are removed after verification. - -Local activation: the running Studio cached its old HTML entry. Restarted the same `veadk studio` launch without opening another browser; verified the current built entry, referenced JS assets (HTTP 200), and POST /web/a2a-spaces in the live OpenAPI. Cloud Runtime was untouched. - -Name-only display refinement approved by the user on 2026-10-22: space selectors, bound-space status, viewed-space context, creation confirmation and registration destination show the space name without its resource ID. IDs remain unchanged in requests and selection values. If metadata has no name, show a localized name-unavailable label instead of exposing the ID. This is a display-only change with no API, persistence or authorization contract impact; direct review found no blockers. - -Approved follow-up FR-5 / T-6 / AC-6 (2026-10-22): unbinding applies only to the selected space when its ID equals the current bound space ID. An unbound selection must not offer an enabled unbind action. Changing selection, including after creation, clears any prior confirmation. Guard the mutation handler as well as the visible controls. API/CAS and permissions remain unchanged. Regression: bind A, open unbind confirmation, select unbound B, verify confirmation/action disappear and no write occurs; reselect A and explicitly confirm to send one null binding with the current revision. Direct review confirms this prevents an action on B from unbinding A without changing backend ownership. Verification pending. - -Follow-up verification (2026-10-22): pass — npm test (1389 Node tests, 167 Vitest cases); build; i18n (2 locales/22 namespaces); tsc --noEmit; asset validation (114 files/351 references); git diff --check; scoped UI source secret scan. Name-only regressions first failed in 2 cases, then passed; the selected-space unbind regression first failed, then passed. Real browser with mocked API confirmed selecting unbound Other hides both unbind and the previous Default confirmation while names omit resource IDs. Studio restarted with its original command; after startup completed, the current entry and JS assets returned HTTP 200. No cloud binding was modified. Temporary fixtures removed. Python tests are not_applicable for this frontend-only refinement; prior backend evidence remains unchanged. FR-5 / T-6 / AC-6 complete. diff --git a/prd-spec/features/mpa-collaborating-agents/2026-10-22-space-management.zh.md b/prd-spec/features/mpa-collaborating-agents/2026-10-22-space-management.zh.md deleted file mode 100644 index 9cc96bb1e..000000000 --- a/prd-spec/features/mpa-collaborating-agents/2026-10-22-space-management.zh.md +++ /dev/null @@ -1,77 +0,0 @@ -# 协作页面分区与 A2A 空间创建 - -[English](2026-10-22-space-management.md) - -变更 ID:mpa-space-management。日期:2026-10-22。状态:implemented。前置设计:[中心注册](2026-10-22-center-registration.zh.md)。组件合同:[MPA 协作 Agent](../../../specs/mpa-collaborating-agents/README.zh.md)。 - -## 证据、范围与场景 - -A2ACenter 将绑定、注册和成员内容放在同一纵向流程中,统一使用 16px 间距。用户确认将绑定和成员分区,并要求新增 AgentKit 空间创建。保留已有中心相关本地修改。2026-10-22 读取的官方 [CreateA2aSpace 接口](https://docs.volcengine.com/docs/86681/2249657) 接受 Name、可选 ProjectName 和 IntentEnabled,返回 Result.Id。尚未真实验证云创建。 - -FR-1:空间选择、绑定、解绑归入“空间绑定”;注册、刷新和成员列表归入“空间成员”。使用独立语义区域、32px 组间距、共享左右对齐、可换行操作,以及已有主题变量和字体。注册标明目标空间。保留原有加载、错误、空状态、CAS 和注册行为。 - -FR-2:空间选择器旁新增“新建空间”,无空间时仍可操作。内联表单填写名称、项目(默认 default)和语义检索(默认关闭)。创建成功后选中新空间并刷新成员,绑定仍须单独明确操作。即使分页或最终一致性列表暂未返回新空间,也保留它的可选项。创建成功后刷新失败仍须表达创建成功。 - -FR-3:POST /web/a2a-spaces?region=... 接受 targetRuntimeId、name、projectName(默认 default)、intentEnabled(默认 false)。云写入前执行现有 Studio 管理员和目标 Runtime 授权。Studio 使用现有签名云身份调用 CreateA2aSpace,传入 Name、ProjectName、IntentEnabled,仅返回 {id}。不改 Runtime API 或部署。名称/项目去除首尾空白后须非空、不超过 128 字符且无控制字符(Studio 策略,并非声称官方限制)。拒绝未知字段及非布尔 intentEnabled。请求体上限 4096 字节。提供商拒绝经过脱敏;不确定失败返回带结果不明代码的 502,绝不自动重试。 - -FR-4:禁止并发创建,兼容输入法;打开时聚焦表单,关闭后恢复焦点。取消或卸载会中止浏览器请求,不声称云端回滚;提交期间取消或结果不明时,在刷新核对前锁定写操作。切换 Runtime/地域丢弃旧响应。只读刷新不执行创建。不在浏览器持久化凭据或暴露原始提供商错误。 - -非目标:编辑/删除空间、标签管理、创建 Runtime、自动绑定、派发/Harness 改动、云部署、验证时真实创建云资源。SDK、依赖和生成项目不受影响。 - -## 设计与备选 - -保留 AgentWorkspace 详情布局,复用 Button/Select/FormLabel/InputWithTailIcon/Checkbox 基础组件。CardLayout 强制无关的图标与关闭操作,因此在现有布局内使用两个语义区域,不嵌套另一个页面布局。复用 collaborators 表单样式,仅新增分组、标题行和选择器容器间距。不增加 Tab:空间选择和成员需要同时可见。不新增依赖、图标、字体或配色;沿用 14px 文字及 studio 文字/边框变量。 - -在 frontend/server/a2a_center.py 新增空间路由;在 adk/client.ts、adk/a2aSpace.ts 新增客户端、类型与响应解析器。A2ASpaceCreation 管理提交、中止和结果不明状态;A2ACenter 管理选择、绑定和列表刷新。共享写操作门禁避免创建、注册、绑定重叠。不自动重试结果不明的创建。新空间 ID、名称、项目仅为临时 UI 状态,云端是权威来源。仅在表单关闭且父级不忙时恢复焦点。卸载后不得应用迟到响应。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| FR-1 | T-1 中心分区与双语文案 | AC-1 绑定和注册具有不同可访问区域及操作 | frontend/tests/collaboratingAgents.test.tsx;桌面/窄屏浏览器 | -| FR-2 | T-2 创建表单/客户端与选择刷新 | AC-2 无空间/分页时可创建;成功不写绑定 | UI 测试与模拟浏览器 | -| FR-3 | T-3 校验并授权的服务端路由 | AC-3 精确 Payload、输入边界、拒绝授权、错误脱敏、不重试 | tests/cli/test_a2a_center_registration.py | -| FR-4 | T-4 异步与键盘测试 | AC-4 无重复创建/旧响应污染;取消/不明须核对;焦点/输入法正常 | React 测试与浏览器 | -| 全部 | T-5 双语文档/构建/评审对齐 | AC-5 记录测试/构建/i18n/产物/Ruff/Pyright 与差异检查 | 下述命令 | - -测试先行:生产修改前先补失败的 UI/API 定向测试。门禁:uv run --extra dev pytest tests/cli/test_a2a_center_registration.py;npm --prefix frontend test;npm --prefix frontend run build;npm --prefix frontend run check:i18n;npm --prefix frontend run test:webui-assets;uv run ruff check frontend/server/a2a_center.py tests/cli/test_a2a_center_registration.py;仓库本地 Pyright 检查上述 Python 文件;使用模拟接口的真实浏览器,不写云资源。本次未请求提交,因此不触发提交前 pre-commit 与分支同步。 - -## 评审、批准、风险与交付 - -用户批准:“帮我改下”接受上方绑定、下方成员的方案;“然后也加上agentkit这里新建空间的功能”授权创建能力。review-spec 技能不可用;直接评审覆盖双语范围、接口字段、授权、输入边界、不重试、取消/迟到响应、显式绑定及可测试性。无设计阻塞项。实现前更新双语组件合同。 - -风险:提供商可能在超时前已接受请求,因此再次创建前须读取核对。列表可能延迟或分页隐藏新项;保留确认的 ID 可选,不声称已被列表返回。Studio 凭据和 Runtime 身份必须有一致的账号/地域权限,不跨提供商/账号降级。回滚 UI/服务端变更不删除已创建空间。真实提供商集成明确未验证;本地测试模拟它。 - -验证状态:实现前 not_run。日期/范围:2026-10-22,已有未提交中心实现之上的空间管理增量。本次未授权提交、推送或部署。 - - -## 最终验证(2026-10-22) - -本地增量实现已完成 T-1 至 T-5 及 AC-1 至 AC-5。测试先行证据:实现前,18 项新增 Python 用例因路由不存在而失败,6 项 UI 用例因新控件/区域不存在而失败。补充测试覆盖显式项目/检索开关、无空间时创建及权限纠正。评审修正关闭焦点恢复:结果不明导致打开按钮禁用时,焦点转到刷新;互斥表单不会争抢焦点。选择器容器对齐现有 372px Select 基础组件,不覆盖基础组件样式。没有新增组件样式例外。 - -| 检查 | 结果 | 证据与范围 | -| --- | --- | --- | -| uv run --extra dev pytest tests/cli/test_a2a_center_registration.py tests/test_mpa_collaborator_proxy_policy.py -q | pass | 41 passed;模拟云端,未创建真实资源 | -| npm --prefix frontend test | pass | 1389 项 Node 测试、164 项 Vitest 用例,11 个文件 | -| 定向 collaboratingAgents.test.tsx | pass | 17 项,包括最终焦点/输入法/取消/不明/错误及不自动绑定 | -| npm --prefix frontend run build | pass | 匹配的 veadk/webui 产物;已有大分块警告 | -| npm --prefix frontend run check:i18n | pass | 2 种语言,22 个命名空间 | -| npm --prefix frontend run test:webui-assets | pass | 114 个发布文件、351 个内部引用 | -| frontend/node_modules/.bin/tsc --noEmit -p frontend/tsconfig.json | pass | 无诊断 | -| 对 a2a_center.py、test_a2a_center_registration.py 执行 pre-commit ruff-check、ruff-format | pass | 使用仓库 hook 环境;项目环境不提供独立 uv run ruff 命令 | -| 使用 .venv/bin/python 对两个 Python 文件执行 Pyright | pass | 0 错误/警告;复用相邻仓库已有 Pyright 可执行文件,解释器使用本项目环境 | -| 浏览器 | pass | 隔离真实浏览器与模拟 API:桌面分区、创建选中但不绑定、空状态、权限拒绝、结果不明锁;检查键盘焦点和 390px 窄视图 | -| git diff --check 与双语文档检查 | pass | 范围内文档路径、ID、翻译合同对齐 | -| 本机路由发现 | pass | 127.0.0.1:8000/openapi.json 包含 POST /web/a2a-spaces;未发送创建请求 | -| 密钥扫描 | pass | 对范围内源码/文档执行 gitleaks dir,使用仓库 .gitleaks.toml;既有白名单排除测试与生成的 Web 产物 | -| 全量 Python 回归 / 真实云创建 | not_run | 独立路由变更由定向 API/策略测试覆盖;云写入不属于隔离验证范围 | - -实现评审检查了云写入前授权、严格字段和请求体边界、返回 ID 校验、不泄露原始提供商错误、不重试、创建后刷新失败、取消/卸载,以及分页时保留选择。真实 AgentKit 创建仍未验证。保留已有无关本地修改。未提交、推送或部署。验证后删除临时浏览器夹具。 - -本机生效:运行中的 Studio 缓存了旧 HTML 入口。沿用原 `veadk studio` 启动方式重启,不额外打开浏览器;确认当前构建入口、引用 JS 产物(HTTP 200)及实时 OpenAPI 中的 POST /web/a2a-spaces。未修改云端 Runtime。 - -用户于 2026-10-22 批准名称展示优化:空间选择器、已接入空间状态、正在查看的空间、创建确认和注册目标仅显示空间名称,不显示资源 ID。请求和选择值中的 ID 保持不变。元数据缺少名称时显示本地化的名称暂不可用提示,不回退展示 ID。本次仅改展示,不影响 API、持久化或授权合同;直接评审无阻塞项。 - -已批准后续需求 FR-5 / T-6 / AC-6(2026-10-22):只有选中空间的 ID 等于当前绑定空间 ID 时,才能解绑。选中未绑定空间时,不得提供可用的解绑操作。切换选择(包括创建后选择新空间)清除旧确认。同时在写入处理函数和可见控件上校验。API/CAS 与权限不变。回归:绑定 A,打开解绑确认,选择未绑定 B,确认提示和操作消失且无写入;重新选 A 并明确确认,仅发送一次带当前 revision 的空绑定。直接评审确认这会阻止操作 B 时解绑 A,不改变后端归属。验证待执行。 - -后续验证(2026-10-22):pass — npm test(1389 项 Node 测试、167 项 Vitest 用例)、构建、i18n(2 种语言/22 个命名空间)、tsc --noEmit、产物检查(114 个文件/351 个引用)、git diff --check、范围内 UI 源码密钥扫描。只显示名称的回归先有 2 项失败,修复后通过;按所选空间解绑的回归先失败,修复后通过。模拟 API 的真实浏览器确认,选中未绑定 Other 时,解绑操作与此前 Default 确认一起消失,空间名称不带资源 ID。沿用原命令重启 Studio;启动完成后确认当前入口和 JS 产物返回 HTTP 200。未修改云端绑定。已删除临时夹具。此纯前端优化的 Python 测试为 not_applicable,沿用此前后端证据。FR-5 / T-6 / AC-6 已完成。 diff --git a/prd-spec/features/mpa-create-scaling/2026-10-13-max-instance-input.md b/prd-spec/features/mpa-create-scaling/2026-10-13-max-instance-input.md deleted file mode 100644 index df716aadb..000000000 --- a/prd-spec/features/mpa-create-scaling/2026-10-13-max-instance-input.md +++ /dev/null @@ -1,70 +0,0 @@ -# MPA creation maximum instances and highlighted errors - -[中文版](2026-10-13-max-instance-input.zh.md) - -- Change ID: `mpa-create-scaling` -- Created/revised: 2026-10-13 -- Status: implemented -- Contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), CON-21 - -## Evidence and scope - -The basic information step exposes name/description only. Managed Runtime settings already support `max_instance` and map it to AgentKit `MaxInstance`; the built-in profile currently selects 1. Request/task/runner inputs have no per-creation maximum. Name availability errors render as ordinary body paragraphs, so they lack the existing red error-container emphasis. - -FR-1: Highlight the existing name-conflict message using the same danger/soft Alert as Studio confirmations; preserve the exact localized message and alert semantics. Reuse the component through public props, without a separate CSS/icon system. -FR-2: Add an editable numeric “Maximum instances” field in basic information. Fresh forms and new editable drafts default to 20, accept positive safe integers, and preserve edits while navigating/loading config. Reject empty, zero, negative and fractional input before Next/submission. Server validation rejects invalid integer types, including booleans/string numbers/floats, and values below configured minimum instances. The minimum and concurrency fields are not changed. A maximum of 20 is a scaling limit, not a request to start 20 instances. -FR-3: Carry explicit `maxInstance` through browser draft, POST, SQLite request identity and runner stdin; apply it to managed Runtime settings before preparing deployment, using the existing `MaxInstance` mapping and Runtime template snapshot. Submitted requests cannot edit the field; new-request action resets to 20. Changed values on retry conflict with the original request fingerprint. Omitted/null fields are not added to old fingerprints and preserve old profile behavior; submitted legacy drafts are not upgraded or shown a fictitious 20. - -No modification of existing deployed Runtimes, generic-agent creation, minimum instances, per-instance concurrency, images, resource cleanup or provider adapters is included. No real cloud creation/scale change is authorized. - -## Design, files and contract assessment - -Reuse current numeric-field layout in the creation dialog and the danger/soft Alert already used by StudioConfirmDialog. Keep a text editing buffer for an empty/in-progress numeric input while recording a numeric canonical draft value. Optional API field `maxInstance` is a strict positive integer. Add a small scaling profile helper used by both the BFF (pre-task validation) and runner (actual deployment); the existing YAML/CLI Runtime default remains unchanged. Task persistence pins the value, and stdin passes it only when present. Preserve cancellation/late-response locks, existing step navigation and old saved inputs. - -Files: MpaCreateDialog/API types/locales, authorized creation request, managed config/tasks/runner, targeted frontend and Python regression tests, paired PRD/spec/frontend documentation and packaged WebUI. Existing service/runtime mapping is reused; test emitted CreateRuntime/update settings rather than adding another mapping. Both domestic and BytePlus MPA use the same input, while generic agents use their existing separate flow. No database schema migration is required. - -## Tasks, tests and acceptance - -- T-1 / FR-1 / AC-1: failing React test for visible danger/soft name-conflict Alert; real browser confirms red emphasis and accessible text. -- T-2 / FR-2 / AC-2: fresh 20, editable integer, invalid numeric cases, navigation/config response, restored draft and old submitted request tests. -- T-3 / FR-3 / AC-3: BFF validation, task idempotency/stdin, runner and actual Runtime payload assertions; preserve old request fingerprint and configured defaults. -- T-4 / AC-4: targeted then managed deployment regression, Ruff/Pyright changed Python, full frontend tests/build/i18n/assets, browser normal/error/retry/keyboard/narrow cases, redacted secret and paired-document checks. - -## Review, risks and authorization - -`review-spec` is unavailable; direct review confirms ownership, reusable UI baseline, optional-field compatibility and separation from generic agents. The user approved this concrete plan with “按此方案实施(推荐)” on 2026-10-13. Direct review is complete and no design blockers remain; implementation and regression tests are authorized within this scope. Provider quotas/maximum limits remain enforced by AgentKit; no undocumented quota guarantee is introduced. Existing task snapshots prevent a retry from silently changing the scaling limit. Pre-implementation results were not_run; no commit, push, restart, deployment or cloud mutation is authorized. - -## Implementation and verification — 2026-10-13 - -Scope: working diff based on `0dd3907d`, limited to FR-1–FR-3, their tests/documents and generated WebUI. T-1–T-4 and AC-1–AC-4 are complete. Direct implementation review confirmed numeric transmission to actual CreateRuntime/update payloads, independent profile copies, immutable task identity, strict server validation and unchanged general-agent routes. No blockers remain. - -Regression-first evidence: before production changes, the targeted Python suite had 13 failed / 6 passed; React creation tests had 7 failed / 47 passed. The failures covered the missing UI/API/scaling behavior. Final results: - -| Check | Result | Evidence / limitations | -| --- | --- | --- | -| Targeted Python | pass | 20 tests; strict types, configured minimum, omitted/null compatibility, subprocess stdin, idempotent retries, runner and emitted Runtime payloads. | -| Managed/deployment integration regression | pass | 998 tests using two workers; external services mocked, smoke markers excluded. | -| Frontend suite | pass | 1,389 Node tests and 95 React tests, including 57 MPA creation cases; existing category, deletion and cancellation coverage stays green. | -| Build / i18n / asset references | pass | Both app and integration widget rebuilt; 2 locales / 21 namespaces consistent; 113 packaged files / 350 references verified. Existing chunk-size/dependency warnings are non-blocking. | -| Changed Python Ruff / format / Pyright | pass | Five files; Ruff uses the repository pre-commit version 0.11.12. Pyright reports 0 errors / 0 warnings using the repository Python. Temporary `uvx` tools were used because these tools are not installed locally; no global configuration changed. | -| Real Chromium UI | pass | Real React dialog in an isolated temporary fixture; simulated APIs only. Checked desktop/narrow 390×844, default 20, numeric validation, keyboard increment, navigation preservation, numeric submission, read-only failed request, new request reset, BytePlus manual OV, legacy omission and both preflight/task-level duplicate alerts. Full Chinese alert text wraps without horizontal overflow. | -| Secret scan / documents / whitespace | pass | Changed-file scan with repository Gitleaks rules and redacted output; paired identifiers/numeric limits/commands/relative links reviewed; `git diff --check`. | -| Live cloud / Codex smoke / sidecar coverage | not_applicable | No cloud operations or harness/sidecar behavior changed; no live provisioning authorization. Actual provider quota/scale behavior was not exercised. | -| Commit / pre-commit / push / restart | not_run | Not requested or authorized. Before a future commit, synchronize the intended base and run required pre-commit/unit gates. | - -An exploratory unpinned `uvx ruff check` selected Ruff 0.17.0 and failed with 17 findings under its expanded rules. It is not the configured repository gate; the pinned 0.11.12 check and format check both passed. No unrelated rules or production code were changed to hide this result. - -Commands executed: - -```bash -uv run --extra dev pytest tests/integrations/mpa_managed/test_creation_scaling.py -q --no-cov -uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py tests/integrations/test_mpa_tool.py tests/integrations/test_mpa_skill_space.py tests/integrations/test_ve_faas_deploy_image_key_auth.py -q --no-cov -uvx --from ruff==0.11.12 ruff check frontend/server/mpa_creation.py veadk/integrations/mpa/managed/config.py veadk/integrations/mpa/managed/runner.py veadk/integrations/mpa/managed/tasks.py tests/integrations/mpa_managed/test_creation_scaling.py -uvx --from ruff==0.11.12 ruff format --check frontend/server/mpa_creation.py veadk/integrations/mpa/managed/config.py veadk/integrations/mpa/managed/runner.py veadk/integrations/mpa/managed/tasks.py tests/integrations/mpa_managed/test_creation_scaling.py -uvx pyright --pythonpath .venv/bin/python frontend/server/mpa_creation.py veadk/integrations/mpa/managed/config.py veadk/integrations/mpa/managed/runner.py veadk/integrations/mpa/managed/tasks.py tests/integrations/mpa_managed/test_creation_scaling.py -npm --prefix frontend test -npm --prefix frontend run build -npm --prefix frontend run check:i18n -npm --prefix frontend run test:webui-assets -git diff --check -``` diff --git a/prd-spec/features/mpa-create-scaling/2026-10-13-max-instance-input.zh.md b/prd-spec/features/mpa-create-scaling/2026-10-13-max-instance-input.zh.md deleted file mode 100644 index 127df87d7..000000000 --- a/prd-spec/features/mpa-create-scaling/2026-10-13-max-instance-input.zh.md +++ /dev/null @@ -1,70 +0,0 @@ -# MPA 创建最大实例数与错误高亮 - -[English](2026-10-13-max-instance-input.md) - -- Change ID:`mpa-create-scaling` -- 创建/修订:2026-10-13 -- 状态:implemented -- 契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-21 - -## 证据与范围 - -基本信息步骤目前仅有名称/描述。受管 Runtime 已支持 `max_instance` 并映射为 AgentKit `MaxInstance`,内置配置当前为 1。请求/task/runner 尚无本次创建最大实例数。名称可用性错误以普通正文段落展示,缺少现有红色错误容器强调。 - -FR-1:用 Studio 确认弹窗同款 danger/soft Alert 高亮现有重名提示,保留原本地化文案及 alert 语义。仅使用组件公开属性,不增加独立 CSS/图标体系。 -FR-2:基本信息增加可编辑数字字段“最大实例数”。新表单及新的可编辑草稿默认 20,接受正安全整数,步骤切换/配置加载期间保留编辑。下一步/提交前拒绝空、0、负数和小数。服务端拒绝无效整数类型,含布尔、数字字符串、小数,以及小于配置最小实例数的值。不改变最小实例数/单实例并发字段。最大值 20 是扩容上限,不代表启动 20 个实例。 -FR-3:显式 `maxInstance` 贯穿浏览器草稿、POST、SQLite 请求身份及 runner stdin,在准备部署前应用到受管 Runtime 配置,复用现有 `MaxInstance` 映射及 Runtime 模板快照。已提交任务不能修改;新建另一请求重置为 20。重试改变数值须与原指纹冲突。省略/null 不加入旧指纹,保留旧配置行为;已提交旧草稿不升级,也不显示虚构的 20。 - -不修改已部署 Runtime、通用创建、最小实例数、单实例并发、镜像、资源清理或提供商适配。不授权真实云创建/扩容。 - -## 设计、文件与契约评估 - -复用创建弹窗现有数字字段排布及 StudioConfirmDialog 使用的 danger/soft Alert。数字输入保留文本编辑缓冲,允许空/输入过程,同时以数字保存规范草稿值。可选 API 字段 `maxInstance` 为严格正整数。新增小型 scaling 配置函数,由 BFF(受理前校验)及 runner(实际部署)共同使用;已有 YAML/CLI Runtime 默认值不变。任务持久化固定数值,仅存在时传入 stdin。保留取消/过期响应锁、原步骤导航及旧保存输入。 - -文件:MpaCreateDialog/API 类型/文案、授权创建请求、managed config/tasks/runner、定向前端/Python 回归、双语 PRD/spec/frontend 文档及 WebUI 发布资源。复用 service/runtime 映射,测试真正发出的 CreateRuntime/update 设置,不增加第二套映射。国内及 BytePlus MPA 共用该输入,通用智能体仍走原独立流程。无需数据库 schema 迁移。 - -## 任务、测试与验收 - -- T-1 / FR-1 / AC-1:先写 React 重名 danger/soft Alert 可见性失败测试;真实浏览器确认红色高亮及可访问文字。 -- T-2 / FR-2 / AC-2:新建 20、可编辑整数、无效数值、步骤/配置响应、恢复草稿及旧已提交请求测试。 -- T-3 / FR-3 / AC-3:BFF 校验、任务幂等/stdin、runner 及真实 Runtime Payload 断言;保留旧请求指纹和配置默认。 -- T-4 / AC-4:先定向再 managed 部署回归、改动 Python Ruff/Pyright、前端全测/build/国际化/资源、浏览器正常/错误/重试/键盘/窄屏、脱敏密钥扫描及双语文档检查。 - -## 审查、风险与授权 - -`review-spec` 不可用,直接审查确认职责、可复用 UI 基准、可选字段兼容及通用流程隔离。用户于 2026-10-13 通过“按此方案实施(推荐)”批准具体方案。直接审查完成,无设计阻塞,可在该范围实现和编写回归测试。提供商额度/最大限制仍由 AgentKit 校验,不新增未文档化配额保证。任务快照防止重试静默更换扩容上限。实现前验证为 not_run;不授权提交、推送、重启、部署或云修改。 - -## 实现与验证 — 2026-10-13 - -范围:基于 `0dd3907d` 的工作区差异,仅含 FR-1–FR-3、对应测试/文档和 WebUI 产物。T-1–T-4 与 AC-1–AC-4 已完成。直接实现审查确认数值传到实际 CreateRuntime/update Payload、配置副本独立、任务身份不可变、服务端严格校验,且通用创建路由未改变。无遗留阻塞。 - -先回归后实现的证据:修改生产代码前,定向 Python 为 13 failed / 6 passed;React 创建测试为 7 failed / 47 passed。失败覆盖当时缺失的 UI/API/实例数行为。最终结果: - -| 检查 | 结果 | 证据 / 限制 | -| --- | --- | --- | -| 定向 Python | pass | 20 项;严格类型、配置最小值、省略/null 兼容、子进程 stdin、幂等重试、runner 与实际 Runtime Payload。 | -| Managed/部署集成回归 | pass | 两个 worker 共 998 项;外部服务使用模拟,排除 smoke 标记。 | -| 前端测试 | pass | 1,389 项 Node、95 项 React,其中 MPA 创建 57 项;已有类型区分、删除、取消覆盖仍通过。 | -| Build / 国际化 / 资源引用 | pass | 应用与集成 widget 均重建;2 个语言 / 21 个 namespace 一致;113 个发布文件 / 350 个引用核验。现有分包大小/依赖警告不阻塞。 | -| 改动 Python Ruff / 格式 / Pyright | pass | 五个文件;Ruff 使用本仓库 pre-commit 的 0.11.12 版本。Pyright 使用本仓库 Python,0 错误 / 0 警告。因本地未安装检查工具,使用临时 `uvx`,未改全局配置。 | -| 真实 Chromium UI | pass | 隔离临时页面中的真实 React 弹窗,API 全为模拟。检查桌面/390×844 窄屏、默认 20、数字校验、键盘增量、步骤保留、数字提交、失败请求只读、新请求重置、BytePlus 手动 OV、旧字段省略,以及预检查/任务阶段重名高亮。完整中文错误可换行,无横向溢出。 | -| 密钥扫描 / 文档 / 空白 | pass | 按本仓库 Gitleaks 规则扫描变更文件并脱敏输出;审查双语标识符/数值限制/命令/相对链接;`git diff --check`。 | -| 真实云 / Codex smoke / sidecar coverage | not_applicable | 未改变云操作或 harness/sidecar 行为,未授权真实创建。未实测提供商额度/扩容。 | -| 提交 / pre-commit / 推送 / 重启 | not_run | 未请求或授权。未来提交前须同步目标基线并执行要求的 pre-commit/单元测试。 | - -探索性未固定版本的 `uvx ruff check` 使用了 Ruff 0.17.0,在其扩展规则下 fail(17 项)。这不是仓库配置的检查版本;固定 0.11.12 的 check/format 均通过。未修改无关规则或生产代码来隐藏结果。 - -已执行命令: - -```bash -uv run --extra dev pytest tests/integrations/mpa_managed/test_creation_scaling.py -q --no-cov -uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py tests/integrations/test_mpa_tool.py tests/integrations/test_mpa_skill_space.py tests/integrations/test_ve_faas_deploy_image_key_auth.py -q --no-cov -uvx --from ruff==0.11.12 ruff check frontend/server/mpa_creation.py veadk/integrations/mpa/managed/config.py veadk/integrations/mpa/managed/runner.py veadk/integrations/mpa/managed/tasks.py tests/integrations/mpa_managed/test_creation_scaling.py -uvx --from ruff==0.11.12 ruff format --check frontend/server/mpa_creation.py veadk/integrations/mpa/managed/config.py veadk/integrations/mpa/managed/runner.py veadk/integrations/mpa/managed/tasks.py tests/integrations/mpa_managed/test_creation_scaling.py -uvx pyright --pythonpath .venv/bin/python frontend/server/mpa_creation.py veadk/integrations/mpa/managed/config.py veadk/integrations/mpa/managed/runner.py veadk/integrations/mpa/managed/tasks.py tests/integrations/mpa_managed/test_creation_scaling.py -npm --prefix frontend test -npm --prefix frontend run build -npm --prefix frontend run check:i18n -npm --prefix frontend run test:webui-assets -git diff --check -``` diff --git a/prd-spec/features/mpa-feishu-channels/2026-09-15-mpa-feishu-channels.md b/prd-spec/features/mpa-feishu-channels/2026-09-15-mpa-feishu-channels.md deleted file mode 100644 index 20c3f2984..000000000 --- a/prd-spec/features/mpa-feishu-channels/2026-09-15-mpa-feishu-channels.md +++ /dev/null @@ -1,31 +0,0 @@ -# MPA Feishu channels - -[中文](2026-09-15-mpa-feishu-channels.zh.md) - -Change ID: mpa-feishu-channels. Date: 2026-09-15. Status: implementation and local verification complete; live deployment awaits test environment. - -## Background and approval - -The MPA Runtime already exposes `/api/v1/channels`, chat permissions and a two-step QR flow. Studio's Runtime proxy supplies the Runtime credential but MPA channel management previously required an administrator JWT. The user explicitly approved implementation of the staged Feishu plan, including Studio adaptation, on 2026-09-15. Real deployment target and test tenant remain unspecified. - -## Goals, scenarios and scope - -FR-1: Administrators configure one Feishu bot per isolated MPA channel database from Agent integrations. Given a compatible Runtime, scanning authorizes the app, then MPA registers and stores it; only BOUND means complete. FR-2: Manage allowed group IDs and session scopes; default new permissions to group_sender. FR-3: Show truthful configuration and delivery diagnostics, pending, failure and retry states. FR-4: Keep secrets server-side and deny channel management to non-admins even when they own a Runtime. Existing JWT and legacy QR contracts remain compatible. General channel registries, media, MCP and Skill management are outside this change. - -## Design and affected files - -Follow [component contract](../../../specs/mpa-channels/README.md). Reuse `frontend/src/adk/client.ts` and the AgentWorkspace integrations layout; create RuntimeChannels using existing Button/form styling, translations and cancellation. The Python proxy uses a small channel-policy helper for both proxy routes. MPA deployment accepts persistent CHANNEL_STATE_ENCRYPTION_KEY through existing extra_env; never generates a new key on update. Add CHANNEL_ADMIN_AUTH_MODE=runtime_key for newly provisioned MPA deployments. No credentials enter browser storage. - -## Tasks and tests - -T-1: Write proxy-policy and API client tests before implementation. T-2: Implement proxy header filtering and admin checks. T-3: Implement capability discovery, QR polling, diagnostics and group permissions UI. T-4: Verify targeted Python tests, Ruff/Pyright, frontend tests/build and browser interaction. Synchronize generated webui assets and frontend README. T-5: Record live smoke results only against an explicitly selected test Runtime/Bot/group. - -## Acceptance and risks - -AC-1: Unauthorized calls cannot cause channel mutations or inject a management header. AC-2: Switching Runtime or leaving cancels polling and ignores stale responses. AC-3: Failed/expired QR can be retried or replaced; missing capability/configuration has actionable feedback. AC-4: Allowed group CRUD and scope values match MPA contracts; errors preserve user input. AC-5: No appSecret returned by the new binding API. AC-6: Simulated tests and live Feishu verification are reported separately. MPA requires persistent DB and persistent encryption key; old images return unsupported. Gateway reachability and delivery are unknown until observed, not implied by configuration. Runtime API key mode is explicit administrator capability; browser access remains protected by Studio admin authorization. - -## Design review - -Manual review performed because review-spec skill is unavailable: checked ownership, security, compatibility, cancellation, error handling, testability and bilingual equivalence. No design blockers. User approval recorded above precedes implementation. Verification (2026-09-15): proxy-policy, MPA provisioning environment and Runtime proxy targeted tests passed; `npm --prefix frontend test` passed 1205 tests; added Vitest contract/interaction tests passed 5 tests; frontend TypeScript and production build passed and webui assets were synchronized. Browser checks used simulated APIs for unbound, QR to BOUND, group save, keyboard scope selection and 390px layout; the observed overflow was fixed. Live delivery/deployment: blocked, no test Runtime/Bot/group selected. Full-file Ruff: fail, cli_frontend.py has 86 findings both at HEAD and after the change; new files and mpa_provision.py pass. Pyright with the repository .venv reports 33 errors in cli_frontend.py both at HEAD and after the change; new proxy and provisioning modules have no type errors. Default whole-repository parallel Python regression and pre-commit: not_run; this change is not committed and affected-path targeted regressions were run. - -Additional verification: the final Studio backend targeted suite passed 88 tests. Automatic approval rejected mounting private source into a third-party Gitleaks container, so Gitleaks did not run. A local Python credential-pattern check of added text across both repositories scanned 83 text inputs with 0 potential matches; it is not a full Gitleaks scan. Source diff whitespace checks pass; generated bundles retain trailing whitespace in upstream template strings without manual alteration. diff --git a/prd-spec/features/mpa-feishu-channels/2026-09-15-mpa-feishu-channels.zh.md b/prd-spec/features/mpa-feishu-channels/2026-09-15-mpa-feishu-channels.zh.md deleted file mode 100644 index b3f3616d4..000000000 --- a/prd-spec/features/mpa-feishu-channels/2026-09-15-mpa-feishu-channels.zh.md +++ /dev/null @@ -1,31 +0,0 @@ -# MPA 飞书渠道 - -[English](2026-09-15-mpa-feishu-channels.md) - -变更 ID:mpa-feishu-channels。日期:2026-09-15。状态:实现及本地验证完成,真实部署待测试环境。 - -## 背景与批准 - -MPA Runtime 已提供 `/api/v1/channels`、群权限和两步扫码流程。Studio Runtime 代理注入 Runtime 凭据,但 MPA 渠道管理此前要求管理员 JWT。用户在 2026-09-15 明确批准按飞书分阶段方案实施,包括 Studio 适配。真实部署目标与测试租户尚未指定。 - -## 目标、场景与范围 - -FR-1:管理员从 Agent 接入页面为每个独立 MPA 渠道数据库配置一个飞书 Bot。兼容 Runtime 扫码授权后由 MPA 注册并保存,只有 BOUND 表示完成。FR-2:管理允许的群 ID 和会话策略,新权限默认 group_sender。FR-3:如实展示配置与投递诊断、等待、失败和重试状态。FR-4:凭据留在服务端;即使用户拥有 Runtime,非管理员也不能管理渠道。兼容现有 JWT 和旧扫码契约。通用渠道目录、媒体、MCP 和 Skill 管理不在此次变更内。 - -## 设计与影响文件 - -遵守[组件契约](../../../specs/mpa-channels/README.zh.md)。复用 `frontend/src/adk/client.ts` 和 AgentWorkspace 接入布局;RuntimeChannels 使用已有 Button/表单样式、翻译与取消机制。Python 的两条代理路由共用渠道策略辅助函数。MPA 部署通过现有 extra_env 接收持久 CHANNEL_STATE_ENCRYPTION_KEY,更新时不自动生成新密钥。新 MPA 部署加入 CHANNEL_ADMIN_AUTH_MODE=runtime_key。凭据不进入浏览器存储。 - -## 任务与测试 - -T-1:先写代理策略与 API 客户端测试。T-2:实现代理请求头过滤与管理员检查。T-3:实现能力发现、扫码轮询、诊断、群权限 UI。T-4:验证定向 Python 测试、Ruff/Pyright、前端测试/构建及浏览器交互,同步 webui 构建产物和 frontend README。T-5:只针对明确选择的测试 Runtime/Bot/群记录真实联调结果。 - -## 验收与风险 - -AC-1:未授权调用不能触发渠道修改或注入管理请求头。AC-2:切换 Runtime 或离开页面取消轮询并忽略旧响应。AC-3:失败/过期二维码可重试或重新生成;能力或配置缺失有可操作提示。AC-4:群权限增删改和策略值符合 MPA 契约,错误保留输入。AC-5:新绑定 API 不返回 appSecret。AC-6:模拟与真实飞书验证分开报告。MPA 需要持久数据库及持久加密密钥;旧镜像返回不支持。配置完成不代表 Gateway 可达或实际投递成功。Runtime API key 模式是显式管理员能力;浏览器入口仍需 Studio 管理员授权。 - -## 设计审查 - -因未提供 review-spec 技能,已人工审查职责、安全、兼容、取消、错误处理、可测试性和双语一致性。无设计阻塞。用户批准已在实施前记录。验证记录(2026-09-15):新增代理策略测试、MPA 部署环境和 Runtime 代理定向测试通过;`npm --prefix frontend test` 1205 项通过;新增 Vitest 契约/交互测试 5 项通过;前端 TypeScript 与生产 build 通过,webui 产物已同步。浏览器用模拟 API 验证未绑定、扫码到 BOUND、群保存、策略键盘选择和 390px 窄屏,发现的溢出已修复。真实收发/部署:blocked,未指定测试 Runtime/Bot/群。全文件 Ruff:fail,既有 cli_frontend.py 在 HEAD 与修改后均有 86 项问题;新文件与 mpa_provision.py 通过。Pyright 使用本仓库 .venv:cli_frontend.py 当前与 HEAD 基线均为 33 个错误,新增代理文件与部署模块无类型错误。默认全仓并行 Python 回归与提交前 pre-commit:not_run,本次未提交,已运行受影响链路的定向回归。 - -补充验证:Studio 后端定向测试最终 88 项通过。自动审批拒绝将私有源码挂载到第三方 Gitleaks 容器,因此 Gitleaks 未执行。已用本地 Python 检查两仓库新增文本的凭据模式:83 个文本输入、0 个疑似匹配,不等同完整 Gitleaks 扫描。源码 diff 空白检查通过,生成 bundle 的上游模板字符串仍有尾随空格,未手动改变字符串。 diff --git a/prd-spec/features/mpa-feishu-multi-bot/2026-10-09-studio-multi-bot.md b/prd-spec/features/mpa-feishu-multi-bot/2026-10-09-studio-multi-bot.md deleted file mode 100644 index 1d3c71e79..000000000 --- a/prd-spec/features/mpa-feishu-multi-bot/2026-10-09-studio-multi-bot.md +++ /dev/null @@ -1,81 +0,0 @@ -# Studio multi-bot Feishu management - -[中文版](2026-10-09-studio-multi-bot.zh.md) - -- Change ID: mpa-feishu-multi-bot -- Created/revised: 2026-10-09 -- Status: implemented; verification limits recorded -- Scope: Studio only; agentkit-mpa-agent owns Bot persistence, Gateway registration, session routing and migration. -- Contracts: [MPA channels](../../../specs/mpa-channels/README.md), [MPA tasks](../../../specs/studio-mpa-cron-tasks/README.md). - -## Background and evidence - -The local agentkit-mpa-agent feat/feishu-bots implementation advertises multiBotChannels=[feishu] and accountScopedPermissions=true. GET channels can contain multiple Feishu accounts identified by appId and enabled. Diagnostics without appId returns 409 when ambiguous. Studio currently queries unscoped diagnostics/permissions and treats every 409 as uncertain registration. Its task editor drops delivery.appId when changing a target. Runtime proxy already supports PATCH and administrator-only channel management. - -## Goals and non-goals - -Expose independent Feishu accounts, additive QR/manual binding, enable/disable/unbind, account-scoped diagnostics and group permissions, and explicit task delivery accounts. Preserve legacy single-bot Runtime and WeCom/DingTalk behavior. Do not change MPA persistence, Runtime creation/images, chat rendering, gateway authentication or deploy services. Live cloud writes and real message delivery are outside this local verification. - -## Scenarios and requirements - -- FR-1: Enable multi-bot UI only when capabilities.multiBotChannels contains feishu. Missing capability retains legacy behavior; failed requests are errors rather than empty lists. -- FR-2: Fetch GET channels and show account name, appId and enabled state. One account may be selected automatically; multiple accounts require an explicit selection. Adding a bot uses existing QR/manual endpoints and selects the returned appId after refreshing. -- FR-3: PATCH feishu/accounts/{appId} with enabled and DELETE root with channel/appId affect only the target. Unbind requires confirmation. Failed writes preserve inputs and existing accounts; no secrets in persistence or errors. -- FR-4: Diagnostics and permission GET/POST/DELETE always carry the selected appId on multi-bot Runtime. Composite identity prevents same-chat collisions. Distinguish ambiguous account selection from REGISTRATION_UNCERTAIN; only registration conflicts block binding. -- FR-5: MPA Feishu task creation/edit/copy selects a Bot and retains delivery.appId with target and unchanged thread metadata. Multiple bots never silently choose the first. Web delivery omits appId. Disabled/missing accounts block new Feishu writes. Existing tasks missing appId require selection when ambiguous. Preserve legacy task behavior if multi-bot capability is absent. -- FR-6: Account/Runtime/provider changes abort reads and polling; late responses cannot replace selection. Single-flight mutations lock account switching; cancellation does not claim to undo server writes. Cover loading, empty, error, retry, keyboard/IME and narrow screens. - -## Design and boundaries - -Reuse existing provider panel for binding and scoped account details, existing Select/Button and task dialog layouts. Add an account container gated by capabilities; fetch list before scoped details. Multi-bot management never probes unscoped diagnostics. Account controls are separate from additive binding. Only nonsecret account summaries are retained in React memory. The existing trusted Runtime proxy injects channel credentials. User-scoped cron requests retain current ownership and use delivery.appId inside the unchanged delivery object. Task account choices use existing authorized channel requests; lack of permission is shown and blocks multi-bot Feishu submission, without all-user or credential fallback. - -No new dependency, database migration, cloud resource or general-agent behavior. Older runtimes do not receive new fields/endpoints. Public API changes are additive and capability-gated. UI inherits existing tokens and uses no new product icons. - -## Tasks and affected files - -| Task | Requirements | Files / verification | -| --- | --- | --- | -| T-1 | FR-1..6 | This bilingual PRD and both component contracts; manual design review | -| T-2 | FR-1..4,6 | adk/client.ts, ui/RuntimeChannels.tsx, new Feishu account container, locale pairs; failing frontend regression tests first | -| T-3 | FR-5..6 | cronjobs/MpaTaskEditor.tsx, MpaCronTasks.tsx, locale pairs; task regression tests first | -| T-4 | FR-1..6 | channel and task tests, frontend test/build, proxy Python tests, browser fixtures, frontend README bilingual section, generated veadk/webui | - -## Acceptance and verification - -AC-1: Two accounts can be displayed, independently selected/toggled/deleted, and adding B leaves A intact (FR-1..3, T-2). -AC-2: Same chat under A/B stays scoped in diagnostics and permission CRUD; ambiguous reads do not disable registration (FR-4, T-2). -AC-3: Task create/edit/copy keeps the intended appId; switching Web removes it; unavailable Bot and stale responses fail safely (FR-5..6, T-3). -AC-4: Legacy Feishu/WeCom/DingTalk regressions pass; normal, loading/error/retry, IME/keyboard and narrow browser fixture checks pass (FR-1,6, T-4). -AC-5: npm --prefix frontend test; npm --prefix frontend run build; targeted Vitest channel/task tests; uv run --extra dev pytest tests/test_mpa_channel_proxy_policy.py tests/frontend/server/test_mpa_cron.py. Record actual outcomes. Existing task coverage gate applies when affected; no harness-sidecar change. - -## Risks and rollout - -The MPA changes are local/unpublished; offline fixtures do not prove live double-bot delivery. Deploy compatible Runtime before enabling multiple accounts. Group management uses administrator-only channel APIs; non-admin task users may not list bots and must see a permission error rather than an invented default. UI refresh cannot revoke in-flight Gateway operations. No automatic rollback/deletion/migration. - -## Review and delivery record - -User approved the complete proposed scope with “改改看” on 2026-10-09. review-spec is unavailable; reviewed requirements, compatibility, identity, permission boundaries, errors, cancellation, tests and bilingual equivalence directly. No design blocker identified. frontend/SPEC.md and foundation Specification read; referenced frontend-design/ui-ux-pro-max skill files absent in repository and installed skill locations; use established components and direct UI review without adding a competing design system. - -Initial baseline: Baseline feat/from-main-20261009 at f8c96211; clean working tree before this design; git pull --ff-only origin main reports already up to date. No commits, cloud writes or deployment authorized by this design. - -## Implementation review and verification (2026-10-09) - -Tested scope: working-tree changes on `feat/from-main-20261009`, based on `f8c96211`. T-1 through T-4 are implemented. `frontend/README.md` retains its existing mixed-language convention; both languages were updated there rather than introducing a second README. Component contracts and locale pairs were updated together. No Python production code changed. Manual implementation review checked account identity, additive binding, permission boundaries, stale reads, cancellation, single-flight writes and task metadata. A QR completion selection race found by regression testing was fixed before delivery. - -| Check | Outcome | Evidence / limits | -| --- | --- | --- | -| Failing regression before implementation | pass | New channel/task tests failed on the original implementation: 8 failed, 50 passed. | -| Targeted frontend regression | pass | `cd frontend && npx vitest run --environment jsdom tests/runtimeChannels.test.tsx tests/feishuAccounts.test.ts tests/mpaTaskBots.test.tsx tests/mpaManagement.test.tsx tests/mpaCronTasks.test.tsx src/adk/channels.test.ts`: 162 passed. | -| Default frontend suite | pass | `npm --prefix frontend test`: 1,376 Node tests and 47 Vitest tests passed. | -| MPA task coverage | pass | `npm --prefix frontend run test:mpa-cron-coverage`: 87 passed; statements 99.31%, branches 97.44%, functions 99.18%, lines 99.74%; existing thresholds unchanged. | -| TypeScript | pass | `cd frontend && npx tsc --noEmit`. | -| Localization | pass | `npm --prefix frontend run check:i18n`. | -| Build / packaged assets | pass | `npm --prefix frontend run build`; `npm --prefix frontend run test:webui-assets`: 113 files / 350 internal references verified. Build retains the existing large-chunk warning. | -| Backend proxy / task regression | pass | `uv run --extra dev pytest tests/test_mpa_channel_proxy_policy.py tests/frontend/server/test_mpa_cron.py`: 18 passed, with a Starlette deprecation warning. Existing proxy forwards appId and PATCH without a new backend contract. | -| Browser fixture flows | pass | Actual components with local simulated APIs: explicit account selection, independent enable/disable, additive manual binding and returned-account selection, scoped group save, task bot selection/payload, loading/provider navigation, empty/error/retry, legacy UI and keyboard navigation. At 390 px, document width equals viewport width and diagnostics use one column. Narrow nested cards were corrected during QA. Temporary fixture removed after checking. | -| Native browser confirmation / IME | blocked | In-app browser could not complete the native unbind confirmation; no real account was touched. Automated tests cover confirmation and selected-account deletion, IME composition guards and cancellation. Chinese input was checked in the browser; native OS IME was not exercised by this tool. | -| Live double-bot delivery | not_run | Requires a separately deployed compatible MPA image and explicit live-operation authorization. Simulated API checks do not establish Gateway delivery. | -| Ruff / Pyright / harness-sidecar coverage | not_applicable | No Python or harness-sidecar implementation changed. | -| Commit-time synchronization / pre-commit | not_run | No commit requested. Fetch/rebase and all-files pre-commit remain required before a future commit. | - -Initial invocation without jsdom failed on browser globals and was corrected; it is not a runtime failure. AC-1 through AC-3 are covered by regression tests and applicable fixture flows; AC-4 browser coverage has the native-tool limits above; AC-5 gates passed. The implementation does not change general-agent chat rendering, cloud resources or deployment images. Production rollout still requires compatible Runtime deployment and a live smoke test. No commit, push or deployment was performed. diff --git a/prd-spec/features/mpa-feishu-multi-bot/2026-10-09-studio-multi-bot.zh.md b/prd-spec/features/mpa-feishu-multi-bot/2026-10-09-studio-multi-bot.zh.md deleted file mode 100644 index 36048bdf1..000000000 --- a/prd-spec/features/mpa-feishu-multi-bot/2026-10-09-studio-multi-bot.zh.md +++ /dev/null @@ -1,81 +0,0 @@ -# Studio 飞书多机器人管理 - -[English](2026-10-09-studio-multi-bot.md) - -- 变更 ID:mpa-feishu-multi-bot -- 创建/修订:2026-10-09 -- 状态:implemented;已记录验证限制 -- 范围:仅 Studio;agentkit-mpa-agent 负责 Bot 持久化、Gateway 注册、会话路由和迁移。 -- 契约:[MPA 消息渠道](../../../specs/mpa-channels/README.zh.md)、[MPA 定时任务](../../../specs/studio-mpa-cron-tasks/README.zh.md)。 - -## 背景与证据 - -本地 agentkit-mpa-agent 的 feat/feishu-bots 实现声明 multiBotChannels=[feishu] 和 accountScopedPermissions=true。GET channels 可返回多个由 appId 和 enabled 标识的飞书账号。诊断未带 appId 且存在歧义时返回 409。Studio 当前请求不区分账号的诊断/群权限,将所有 409 当作注册不确定。任务编辑器修改目标时会丢失 delivery.appId。Runtime 代理已支持 PATCH 及仅管理员可访问的渠道管理。 - -## 目标与非目标 - -支持独立飞书账号、扫码/手动新增绑定、启停/解绑、账号级诊断及群权限,并明确任务投递账号。保留旧单机器人 Runtime 以及企微/钉钉行为。不修改 MPA 持久化、Runtime 创建/镜像、聊天展示、网关鉴权,不发布服务。真实云写操作与真实消息投递不属于本地验证。 - -## 场景与需求 - -- FR-1:仅 capabilities.multiBotChannels 包含 feishu 时启用多机器人 UI。缺少声明时沿用旧行为;请求失败显示错误,不伪装为空列表。 -- FR-2:GET channels 展示账号名称、appId、启停状态。仅一个账号可自动选择,多个账号须明确选择。新增使用既有扫码/手动接口,刷新后选中返回的 appId。 -- FR-3:PATCH feishu/accounts/{appId} 的 enabled 和带 channel/appId 的根 DELETE 仅影响目标账号。解绑须确认。失败保留输入和现有账号;密钥不持久化或进入错误。 -- FR-4:多机器人 Runtime 的诊断和群权限 GET/POST/DELETE 均携带选定 appId。组合身份避免同群冲突。区分账号歧义与 REGISTRATION_UNCERTAIN;仅注册冲突阻止绑定。 -- FR-5:MPA 飞书任务创建/编辑/复制须选择 Bot,并与目标及未变更的话题元数据一起保留 delivery.appId。多个 Bot 禁止默认选择第一项。Web 投递不带 appId。停用/缺失账号阻止新增飞书写操作。旧任务缺 appId 且有歧义时须选择。未声明多机器人能力则保留旧任务行为。 -- FR-6:切换账号/Runtime/渠道取消读取和轮询,迟到响应不能覆盖选择。单次写操作锁住账号切换;取消不表示撤销服务端写入。覆盖加载、空、错误、重试、键盘/IME 与窄窗口。 - -## 设计与边界 - -复用既有渠道面板用于绑定及账号详情,复用 Select/Button 和任务弹窗布局。增加能力控制的账号容器,先取列表再查指定账号详情。多机器人管理不探测无账号诊断。账号操作与新增绑定分开。React 内存仅保留非敏感账号摘要。既有可信 Runtime 代理注入渠道凭据。用户级定时任务保留当前所属身份,在既有 delivery 对象中携带 delivery.appId。任务账号选择使用已授权的渠道请求;无权限明确显示错误并阻止多机器人飞书提交,不回退全用户或其他凭据。 - -不新增依赖、数据库迁移、云资源或通用智能体行为。旧 Runtime 不接收新字段/接口。公开 API 为能力控制的兼容扩展。UI 沿用现有变量,不增加产品图标。 - -## 任务与影响文件 - -| 任务 | 需求 | 文件 / 验证 | -| --- | --- | --- | -| T-1 | FR-1..6 | 本双语 PRD 和两套组件契约;直接设计评审 | -| T-2 | FR-1..4,6 | adk/client.ts、ui/RuntimeChannels.tsx、新飞书账号容器、双语文案;先写失败前端回归测试 | -| T-3 | FR-5..6 | cronjobs/MpaTaskEditor.tsx、MpaCronTasks.tsx、双语文案;先写任务回归测试 | -| T-4 | FR-1..6 | 渠道/任务测试、前端 test/build、代理 Python 测试、浏览器夹具、frontend README 双语段落和 veadk/webui 产物 | - -## 验收与验证 - -AC-1:两个账号可展示、独立选择/启停/解绑,新增 B 保留 A(FR-1..3、T-2)。 -AC-2:A/B 同群的诊断及权限 CRUD 均区分账号;歧义读取不禁用注册(FR-4、T-2)。 -AC-3:任务创建/编辑/复制保留 appId,切换 Web 清除,不可用 Bot 和迟到响应安全失败(FR-5..6、T-3)。 -AC-4:旧飞书/企微/钉钉回归通过;正常、加载/错误/重试、IME/键盘和窄窗口浏览器夹具通过(FR-1,6、T-4)。 -AC-5:执行 npm --prefix frontend test、npm --prefix frontend run build、渠道/任务定向 Vitest、uv run --extra dev pytest tests/test_mpa_channel_proxy_policy.py tests/frontend/server/test_mpa_cron.py,记录真实结果。影响定时任务时执行既有覆盖率门禁;本次不涉及 harness-sidecar。 - -## 风险与上线 - -MPA 改动仍在本地且未发布;离线夹具不能证明真实双 Bot 投递。先部署兼容 Runtime 再启用多账号。群管理使用仅管理员可用的渠道接口,非管理员任务用户可能无法列出账号,必须显示权限错误,不能杜撰默认账号。刷新 UI 不能撤销在途 Gateway 操作。不自动回滚/删除/迁移。 - -## 评审与交付记录 - -用户于 2026-10-09 以“改改看”批准完整建议范围。review-spec 不可用,已直接评审需求、兼容、身份、权限边界、错误、取消、测试及双语等价,无设计阻塞。已读 frontend/SPEC.md 和 foundation Specification;引用的 frontend-design/ui-ux-pro-max 技能文件在仓库及已安装技能路径均不存在,沿用既有组件并直接 UI 评审,不另建竞争设计系统。 - -初始基线:基线 feat/from-main-20261009 的 f8c96211;设计前工作区干净;git pull --ff-only origin main 已最新。本设计不授权提交、云写操作或发布。 - -## 实现评审与验证(2026-10-09) - -测试范围:`feat/from-main-20261009` 基于 `f8c96211` 的工作区改动。T-1 至 T-4 已实现。`frontend/README.md` 沿用既有混合双语约定,在同一文件同步两种语言,未新建第二份 README。组件契约与语言文件已同步更新,未修改 Python 生产代码。直接实现评审检查了账号身份、追加绑定、权限边界、迟到读取、取消、单次写操作和任务元数据。回归测试发现的扫码完成后账号选择竞态已修复。 - -| 检查 | 结果 | 证据 / 限制 | -| --- | --- | --- | -| 实施前失败回归 | pass | 新渠道/任务测试在原实现上为 8 失败、50 通过。 | -| 定向前端回归 | pass | `cd frontend && npx vitest run --environment jsdom tests/runtimeChannels.test.tsx tests/feishuAccounts.test.ts tests/mpaTaskBots.test.tsx tests/mpaManagement.test.tsx tests/mpaCronTasks.test.tsx src/adk/channels.test.ts`:162 通过。 | -| 默认前端测试 | pass | `npm --prefix frontend test`:1,376 项 Node 测试和 47 项 Vitest 测试通过。 | -| MPA 任务覆盖率 | pass | `npm --prefix frontend run test:mpa-cron-coverage`:87 通过;语句 99.31%、分支 97.44%、函数 99.18%、行 99.74%,原阈值未改。 | -| TypeScript | pass | `cd frontend && npx tsc --noEmit`。 | -| 国际化 | pass | `npm --prefix frontend run check:i18n`。 | -| 构建 / 发布产物 | pass | `npm --prefix frontend run build`;`npm --prefix frontend run test:webui-assets`:校验 113 文件、350 内部引用。构建仍有既有大包警告。 | -| 后端代理 / 任务回归 | pass | `uv run --extra dev pytest tests/test_mpa_channel_proxy_policy.py tests/frontend/server/test_mpa_cron.py`:18 通过,有一项 Starlette 弃用警告。既有代理转发 appId 与 PATCH,无需新增后端契约。 | -| 浏览器夹具流程 | pass | 真实组件搭配本地模拟 API:明确选择账号、独立启停、手动追加绑定及返回账号选择、指定账号群权限保存、任务机器人选择与载荷、加载/切换渠道、空列表/错误/重试、旧界面和键盘导航。390 px 时文档与视口等宽,诊断为单列;QA 中已修正窄容器内卡片布局。临时夹具验证后移除。 | -| 浏览器原生确认 / IME | blocked | 应用内浏览器未能完成原生解绑确认框,未操作真实账号。自动化测试覆盖确认与指定账号删除、IME 组合输入保护和取消;浏览器验证了中文输入,工具未实际操作系统 IME。 | -| 真实双 Bot 投递 | not_run | 需要另行部署兼容 MPA 镜像及明确的真实操作授权;模拟接口不能证明 Gateway 投递。 | -| Ruff / Pyright / harness-sidecar 覆盖率 | not_applicable | 未修改 Python 或 harness-sidecar 实现。 | -| 提交前同步 / pre-commit | not_run | 未要求提交;后续提交前仍须 fetch/rebase 和全文件 pre-commit。 | - -初次未指定 jsdom 的命令因浏览器全局对象缺失失败,已更正,不属于运行时故障。AC-1 至 AC-3 由回归测试和适用夹具流程覆盖;AC-4 的浏览器覆盖受上述原生工具限制;AC-5 门禁通过。实现未修改通用智能体聊天展示、云资源或部署镜像。正式上线仍需兼容 Runtime 部署及真实冒烟验证。本次未提交、推送或部署。 diff --git a/prd-spec/features/mpa-feishu-multi-bot/2026-10-10-port-multi-bot.md b/prd-spec/features/mpa-feishu-multi-bot/2026-10-10-port-multi-bot.md deleted file mode 100644 index 76b8447c9..000000000 --- a/prd-spec/features/mpa-feishu-multi-bot/2026-10-10-port-multi-bot.md +++ /dev/null @@ -1,72 +0,0 @@ -# Port Studio multi-bot support to the upstream-based branch - -[中文](2026-10-10-port-multi-bot.zh.md) - -- Change ID: mpa-multi-bot-port; created/revised: 2026-10-10; status: implemented. -- Source: `49dd4696` on `feat/from-main-20261009`; target baseline: `f7576c40` on `feat/mpa-account-fixes-upstream-clean-20261010`. -- Design: [original feature](2026-10-09-studio-multi-bot.md). Contracts: [channels](../../../specs/mpa-channels/README.md), [tasks](../../../specs/studio-mpa-cron-tasks/README.md). - -## Background, goals and scope - -The source implements capability-gated Feishu account management and scheduled-task delivery. Target lacks the new containers and capability fields. Dry application accepts source/tests/spec changes; frontend README requires contextual reconciliation. User requests transferring this feature. Preserve current account-key work and unrelated package-lock edits. Do not merge unrelated branch history, change Runtime images/backend storage/authentication, or operate real bots. Historical source verification is not current-branch evidence. - -## Requirements and design - -- FR-1: Transfer the original FR-1–FR-6 semantics and regression cases: capability-gated multi-bot accounts, selected appId in diagnostics/permissions, additive binding, independent enable/disable/unbind, explicit task delivery, cancellation and stale-response handling. -- FR-2: Preserve older single-bot Runtime, WeCom/DingTalk and general-agent chat. Missing multiBotChannels keeps legacy UI; unsupported task capabilities (404) retain legacy task behavior. Authorization/network failures remain errors; never infer compatibility from an empty/error response. -- FR-3: Apply only feature source/tests/contracts/docs. Rebuild packaged assets from target sources rather than copying old bundles. Preserve all pre-existing local changes byte-for-byte. No dependency/lockfile changes needed; no new public Python or server API contract beyond the original capability-gated additions. - -Reused components: existing RuntimeChannels layout, provider tabs, Select, Radio, Button, task DialogShell and text-shimmer. No new product icon or component design. Ports use public component properties; direct foundation/S1–S8 and UI review substitutes for missing frontend-design/ui-ux-pro-max skill files. Contract responsibility remains with MPA for bot storage/routing and Studio for account selection/proxy requests. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | -| --- | --- | --- | -| FR-1 | T-1 | AC-1: apply tests first and record failure; port source and verify channel/task cases | -| FR-2 | T-2 | AC-2: run legacy regressions and browser fixtures for normal/loading/empty/error/retry/cancellation/keyboard/narrow flows | -| FR-3 | T-3 | AC-3: regenerate assets, verify references/localization/types/security, reconcile bilingual README/spec/design and compare pre-existing file hashes | - -Commands: targeted Vitest (jsdom); npm --prefix frontend test; npm --prefix frontend run test:mpa-cron-coverage; npm --prefix frontend run build; npm --prefix frontend run check:i18n; npm --prefix frontend run test:webui-assets; local tsc --noEmit; uv run --extra dev pytest tests/test_mpa_channel_proxy_policy.py tests/frontend/server/test_mpa_cron.py. No Python production changes require new Ruff/Pyright gates for this port; previous key-feature gates remain separate. - -## Risks and review - -Multi-bot requires a compatible Runtime deployed separately. Non-admin users may be unable to list delivery bots; surface permission failures. Mock browser fixtures cannot establish real Feishu/Gateway delivery. Native confirmation/IME limitations must be recorded. No resource cleanup/migration is performed. - -Direct review (review-spec unavailable): checked source/target boundaries, preservation, capabilities, auth/errors, cancellation, tests and bilingual equivalence; no blocker. User approval: “把那个修改挪过来吧” after the scoped transfer plan. No commit/push/deployment authorized. Design/spec guidelines and frontend foundation/SPEC read; referenced UI skills unavailable in repository and installed skill roots, so established component behavior is retained. - -## Verification record - -Scope: source/test/document and regenerated WebUI diff against `f7576c4080da6230e263eac20ab5d32644405ed5`; execution date: 2026-10-10. T-1–T-3 and AC-1–AC-3 completed with the live-verification limits below. - -| Check | Result | Evidence | -| --- | --- | --- | -| Test-first targeted Vitest | fail, expected | Before source migration: 27 new tests failed and account helper import was missing; 123 existing tests passed | -| Targeted Vitest (jsdom) | pass | 6 files, 162 tests: channels, account response validation, task bots, management, cron and channel API | -| `npm --prefix frontend test` | pass | 1,377 Node tests and 65 Agent info/creation/details Vitest tests | -| `npm --prefix frontend run test:mpa-cron-coverage` | pass | 87 tests; statements 99.31%, branches 97.44%, functions 99.18%, lines 99.74%; configured gates met | -| `frontend/node_modules/.bin/tsc --noEmit` (frontend cwd) | pass | Current TypeScript source compiles | -| `npm --prefix frontend run check:i18n` | pass | Two locales, 21 namespaces consistent | -| `npm --prefix frontend run build` | pass | Studio and website integration regenerated from target sources; existing bundle-size warnings remain | -| `npm --prefix frontend run test:webui-assets` | pass | 113 packaged files, 350 internal references | -| Targeted Python command listed above | pass | 18 proxy-policy/cron tests | -| Isolated real-browser checks | pass | Mock API with real components: account selection, independently toggling B, scoped diagnostic/group requests, additive manual binding C preserving A/B, task B delivery/thread preservation, Web clearing appId, single-bot legacy view, unchanged DingTalk view, empty/error/retry/loading, abort on remount, cancel addition and keyboard selection; 390px viewport has no horizontal overflow, including task editor | -| Native unbind confirmation / real IME | blocked | In-app browser confirmation blocks CDP input; closed the affected test tab. Unbind/cancellation/stale requests and composition safeguards covered by component regressions; no actual IME session asserted | -| Gitleaks scoped directory scan (`--redact=100`) | pass | 24 migration source/test/document files, no finding | -| Preservation and document review | pass | All 20 pre-existing dirty files retain identical SHA-256, including account-key changes and package-lock; paired docs, relative links and whitespace checked | -| Ruff/Pyright and harness-sidecar coverage | not_applicable | No Python production or sidecar contract changes | -| Live Runtime/Feishu/Gateway writes | not_run | Compatible Runtime deployment and real delivery are outside this Studio migration | -| Commit-time fetch/rebase and full pre-commit | not_run | No commit requested | - -Implementation review: capability-gated ownership and backward compatibility match the contracts; bot diagnostics/groups/deletion and task delivery remain scoped; failed reads never become successful empty lists or arbitrary bot choices; abort/single-flight handling is preserved. No production changes to general chat, MPA provisioning, image defaults, IAM or model-key discovery. Temporary fixture/server and browser viewport overrides were removed. Historical source design evidence is retained separately. - -Documentation check note: the full frontend README link check reports an existing unavailable local-only `.agents` example link, already present at HEAD. All migrated links pass; that unrelated link was left unchanged. - -## Commit preparation (2026-10-10) - -User authorization: “把当前的修改都提交吧”. Commit only; no push or deployment. Fetched origin/upstream, then ran `git rebase --autostash upstream/main`: the upstream-based branch was already current and the autostash restored successfully. All 172 changed/deleted/untracked file states matched their pre-sync hashes; no production or test files changed during hooks. The user-authorized existing package-lock changes are included; `npm ci --prefix frontend --dry-run --ignore-scripts --offline` passes without changing the lockfile. - -- pass — `uv run --extra dev pre-commit run --all-files`: Ruff check/format, hardcoded-secret detection and YAML-secret scan. -- pass — changed Python production/test files checked with `uv tool run --from pyright pyright --pythonpath .venv/bin/python`: zero errors/warnings. -- pass — frontend full suite rerun after synchronization: 1,377 Node tests and 65 Vitest tests; multi-bot/channel/task regression rerun: 162 passed. Previous build, localization, coverage, asset and isolated browser evidence remains valid because synchronization/hooks changed no source or artifacts. -- fail, pre-existing baseline issue — `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`: 6,933 passed, 53 skipped, four xfailed, one failed, 55 warnings, 412.63 seconds. The sole failure is `tests/frontend/server/runtime_artifacts/test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes`: expected `text/markdown`, received `application/octet-stream`. The writer and test match HEAD byte-for-byte. An isolated `git archive HEAD` baseline using the repository Python environment reproduces the same failure; this host's `mimetypes.guess_type("summary.md")` returns `(None, None)`. Independent current writer suite reproduces it with 48 other tests passing. No unrelated MIME implementation/test change was added to this commit; the full suite is not represented as passing. This does not identify a regression in the submitted feature scope. -- pass — staged whitespace check and secret hooks; local `.env` remains ignored and excluded. Live Ark/Feishu/cloud verification and native browser confirmation/IME limits remain as recorded above; skipped tests do not establish live behavior. diff --git a/prd-spec/features/mpa-feishu-multi-bot/2026-10-10-port-multi-bot.zh.md b/prd-spec/features/mpa-feishu-multi-bot/2026-10-10-port-multi-bot.zh.md deleted file mode 100644 index 6c15d2727..000000000 --- a/prd-spec/features/mpa-feishu-multi-bot/2026-10-10-port-multi-bot.zh.md +++ /dev/null @@ -1,72 +0,0 @@ -# 将 Studio 多 Bot 支持迁入上游基础分支 - -[English](2026-10-10-port-multi-bot.md) - -- 变更 ID:mpa-multi-bot-port;创建/修订:2026-10-10;状态:implemented。 -- 来源:`feat/from-main-20261009` 的 `49dd4696`;目标基线:`feat/mpa-account-fixes-upstream-clean-20261010` 的 `f7576c40`。 -- 设计:[原功能](2026-10-09-studio-multi-bot.zh.md)。契约:[渠道](../../../specs/mpa-channels/README.zh.md)、[任务](../../../specs/studio-mpa-cron-tasks/README.zh.md)。 - -## 背景、目标和范围 - -来源实现了按能力开启的飞书账号管理和定时任务投递,目标缺少新容器及能力字段。补丁预检查允许源码/测试/规格改动,frontend README 须结合上下文合并。用户要求迁入该功能。保留当前账号 Key 功能与无关 package-lock 修改。不合并无关分支历史,不改 Runtime 镜像、后端存储/鉴权,不操作真实 Bot。原分支验证不是当前分支证据。 - -## 需求和设计 - -- FR-1:迁入原 FR-1–FR-6 语义及回归用例:按能力显示多个账号、诊断/群权限携带选定 appId、追加绑定、独立启停/解绑、明确任务投递账号、取消及迟到响应处理。 -- FR-2:保留旧单 Bot Runtime、企微/钉钉和通用智能体聊天。缺少 multiBotChannels 沿用旧 UI;任务能力接口不支持(404)时保留旧任务行为。鉴权/网络失败仍显示错误,不通过空/异常响应推断兼容能力。 -- FR-3:仅应用该功能源码/测试/契约/文档。基于目标源码重建打包产物,不复制旧压缩包。原有本地修改逐字节保留。无需依赖/锁文件改动;除原有能力控制的扩展外,不新增公开 Python 或服务端 API 契约。 - -复用既有 RuntimeChannels 布局、渠道 Tab、Select、Radio、Button、任务 DialogShell 和 text-shimmer。不新增产品图标或组件设计,使用公开组件属性。frontend-design/ui-ux-pro-max 技能文件缺失,直接执行 foundation/S1–S8 与 UI 审查,保持既有组件行为。Bot 存储/路由仍由 MPA 负责,Studio 负责账号选择与代理请求。 - -## 任务和验收 - -| 需求 | 任务 | 验收 | -| --- | --- | --- | -| FR-1 | T-1 | AC-1:先迁测试并记录失败,再迁源码,验证渠道/任务用例 | -| FR-2 | T-2 | AC-2:执行旧流程回归与浏览器夹具,覆盖正常/加载/空/错误/重试/取消/键盘/窄窗口 | -| FR-3 | T-3 | AC-3:重建产物,验证引用/国际化/类型/安全,同步双语 README/规格/设计并比较原有文件哈希 | - -命令:定向 Vitest(jsdom);npm --prefix frontend test;npm --prefix frontend run test:mpa-cron-coverage;npm --prefix frontend run build;npm --prefix frontend run check:i18n;npm --prefix frontend run test:webui-assets;本地 tsc --noEmit;uv run --extra dev pytest tests/test_mpa_channel_proxy_policy.py tests/frontend/server/test_mpa_cron.py。本次迁入无 Python 生产改动,无新增 Ruff/Pyright 检查对象;先前 Key 功能的检查保持独立。 - -## 风险和审查 - -多 Bot 需要另行部署兼容 Runtime。非管理员可能无法列出投递 Bot,须显示权限错误。模拟浏览器夹具不能证明真实飞书/Gateway 投递。原生确认/IME 限制须记录。不执行资源清理/迁移。 - -review-spec 不可用,直接审查源码/目标边界、原有修改保留、能力、鉴权/错误、取消、测试及双语等价,无阻塞。用户在限定迁入计划后批准:“把那个修改挪过来吧”。未授权提交/推送/部署。已读设计/规格规则及前端 foundation/SPEC;引用的 UI 技能在仓库和已安装路径不存在,沿用既有组件行为。 - -## 验证记录 - -范围:相对 `f7576c4080da6230e263eac20ab5d32644405ed5` 的源码/测试/文档及重建 WebUI 差异;执行日期:2026-10-10。T-1–T-3、AC-1–AC-3 已完成,真实验证限制如下。 - -| 检查 | 结果 | 证据 | -| --- | --- | --- | -| 定向 Vitest,测试先行 | fail,预期失败 | 迁源码前 27 个新增用例失败,账号辅助模块缺失;123 个既有用例通过 | -| 定向 Vitest(jsdom) | pass | 6 个文件、162 个测试:渠道、账号响应校验、任务机器人、管理、定时任务及渠道 API | -| `npm --prefix frontend test` | pass | 1,377 个 Node 测试及 65 个智能体信息/创建/详情 Vitest 测试 | -| `npm --prefix frontend run test:mpa-cron-coverage` | pass | 87 个测试;语句 99.31%、分支 97.44%、函数 99.18%、行 99.74%,满足配置门槛 | -| `frontend/node_modules/.bin/tsc --noEmit`(frontend 工作目录) | pass | 当前 TypeScript 源码编译通过 | -| `npm --prefix frontend run check:i18n` | pass | 两种语言、21 个命名空间一致 | -| `npm --prefix frontend run build` | pass | 基于目标源码重建 Studio 及网站集成,保留既有大包警告 | -| `npm --prefix frontend run test:webui-assets` | pass | 113 个打包文件、350 个内部引用 | -| 上述定向 Python 命令 | pass | 18 个代理策略/定时任务测试 | -| 隔离真实浏览器检查 | pass | 模拟 API、真实组件:账号选择、独立启停 B、按账号诊断/群权限请求、手动追加 C 且保留 A/B、任务 B 的投递/线程元数据保留、Web 清除 appId、旧单 Bot 页面、钉钉原界面、空/错误/重试/加载、重挂载取消请求、取消添加和键盘选择;390px 窗口及任务编辑均无横向溢出 | -| 原生解绑确认 / 真实 IME | blocked | 应用内浏览器确认框阻塞 CDP 输入,已关闭受影响测试标签;解绑/取消/迟到请求及输入法保护由组件回归覆盖,未声称实际 IME 验证 | -| Gitleaks 限定目录扫描(`--redact=100`) | pass | 24 个迁入源码/测试/文档文件,无发现 | -| 原有修改保留和文档审查 | pass | 原有 20 个未提交文件 SHA-256 完全一致,含账号 Key 功能及 package-lock;已检查双语文档、相对链接和空白 | -| Ruff/Pyright 及 harness-sidecar 覆盖率 | not_applicable | 未改 Python 生产代码或 sidecar 契约 | -| 真实 Runtime/飞书/Gateway 写入 | not_run | 兼容 Runtime 部署和真实投递不在本次 Studio 迁入范围 | -| 提交前 fetch/rebase 及全量 pre-commit | not_run | 未要求提交 | - -实现审查:按能力控制的职责和旧版本兼容符合契约;机器人诊断/群权限/解绑及任务投递保持账号范围;读取失败不会变成成功空列表或任意机器人选择,取消及防重复请求处理保留。未改通用聊天、MPA 创建、默认镜像、IAM 或模型 Key 发现生产代码。已删除临时夹具、停止服务并清除浏览器窗口尺寸覆盖;来源设计的历史证据单独保留。 - -文档检查说明:完整 frontend README 链接检查发现 HEAD 已有的 `.agents` 本地示例链接不可用;迁入链接全部通过,未改这个无关链接。 - -## 提交准备(2026-10-10) - -用户授权:“把当前的修改都提交吧”。仅提交,不推送或部署。已 fetch origin/upstream,再执行 `git rebase --autostash upstream/main`:上游基础分支已同步,自动暂存成功恢复。172 个修改/删除/未跟踪文件状态与同步前哈希一致,检查期间未改生产或测试文件。按用户授权包含已有 package-lock 改动;`npm ci --prefix frontend --dry-run --ignore-scripts --offline` 通过,未修改锁文件。 - -- pass — `uv run --extra dev pre-commit run --all-files`:Ruff 检查/格式化、硬编码凭据扫描及 YAML 密钥扫描。 -- pass — 已改 Python 生产/测试文件执行 `uv tool run --from pyright pyright --pythonpath .venv/bin/python`:零错误/警告。 -- pass — 同步后重跑前端完整测试:1,377 个 Node 测试及 65 个 Vitest 测试;重跑多 Bot/渠道/任务回归:162 个通过。同步及检查未改变源码/产物,此前构建、国际化、覆盖率、产物及隔离浏览器证据仍有效。 -- fail,基线已有问题 — `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`:6,933 个通过、53 个跳过、四个 xfailed、一个失败、55 个警告,耗时 412.63 秒。唯一失败为 `tests/frontend/server/runtime_artifacts/test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes`:期望 `text/markdown`,实际 `application/octet-stream`。写入实现及测试与 HEAD 逐字节一致,使用仓库 Python 环境运行隔离 `git archive HEAD` 基线可复现;本机 `mimetypes.guess_type("summary.md")` 返回 `(None, None)`。当前 writer 单独测试也复现,另 48 个用例通过。本提交未加入无关 MIME 实现/测试变更,不将全仓检查表述为通过;该结果未发现提交功能范围的回归。 -- pass — 暂存差异空白检查及密钥钩子;本地 `.env` 保持忽略,未包含到提交。真实方舟/飞书/云验证及浏览器原生确认/IME 限制保持前述记录,跳过的测试不证明真实行为。 diff --git a/prd-spec/features/mpa-image-update/2026-10-22-independent-images.md b/prd-spec/features/mpa-image-update/2026-10-22-independent-images.md deleted file mode 100644 index 651f89443..000000000 --- a/prd-spec/features/mpa-image-update/2026-10-22-independent-images.md +++ /dev/null @@ -1,68 +0,0 @@ -# Independent MPA image updates - -[中文](2026-10-22-independent-images.zh.md) - -Change ID: mpa-image-update. Created/revised: 2026-10-22. Status: implemented. -Component: [MPA image management](../../../specs/mpa-image-update/README.md). - -## Background and evidence - -Studio currently hides the general update flow for MPA in AgentWorkspace.tsx. Managed creation already defines domestic/overseas public mpa_agent and mpa_codex_worker repositories in studio_profile.py. Anonymous OCI tag reads were verified for all four repositories; tag listing supplies names, not publication times. UpdateRuntime supports ArtifactUrl/ArtifactType and ReleaseEnable; UpdateTool supports ImageUrl. See [UpdateRuntime](https://docs.volcengine.com/docs/86681/1923461) and [UpdateTool](https://docs.volcengine.com/docs/86681/1913799). - -## Goals, non-goals and scenarios - -FR-1: MPA navigation is Basic, Image updates, Collaborating Agents, subject to existing management permissions. General Agent navigation stays unchanged. -FR-2: Runtime and Worker have separate current metadata, catalog loading, selection, confirmation, submission, errors and reconciliation. Updating either must issue exactly one corresponding cloud update and leave the other unchanged. -FR-3: Select tags only from fixed provider repositories. Show the full selected address. Paginate registry order without claiming newest-first; search explicitly filters loaded versions and offers load-more. Refresh resets catalog selection. -FR-4: Verify fresh authorized Runtime identity, MPA category, deployment lock/state, Worker binding/ownership/exclusive references, optimistic revision and tag existence before mutation. Reject ambiguity and arbitrary hosts, paths, tags or payload fields. -FR-5: Submission is not application health. Runtime requests immediate release; Worker updates its template, and existing sandbox sessions may retain the old image. Show configured image and cloud status separately. A lost write response blocks duplicate submission until read reconciliation; never silently retry writes. -FR-6: Loading, empty, forbidden, errors, retry, stale selection, cancellation/navigation, late responses, keyboard/IME and narrow layouts have explicit behavior. - -Given an authorized MPA, selecting a Runtime version and confirming updates only Runtime. Selecting Worker updates only its exclusively owned template. A stale preview, shared Worker, pending deployment or failed registry read causes zero writes. Leaving the page aborts browser reads; an already submitted cloud write is not rolled back by closing the dialog. - -## Design and impact - -Use a domain React component and typed API client plus a separate Studio backend module. Reuse Select, Button, FormLabel, InputWithTailIcon, TextShimmer and ModalButton. Existing Agent detail navigation anchors the layout. Component contract owns endpoints, validation, persistence, failure/concurrency semantics and bounds. No generated agent source, Python SDK signature, dependencies, session protocol, Worker binding or cloud credentials are changed. - -Use the existing account/region deployment advisory lock and registry record for mutation ownership and durable image-update intent. Keep Runtime and Worker intents separate. Persist intent before cloud submission and reconcile only when the target image is observed with a usable resource status. Ambiguous outcomes retain the intent and block conflicting management operations; cloud rejections clear it. Do not rewrite creation request hashes. Managed deployment and deletion must refuse unresolved image intents. Read failures cannot become empty catalogs or permission success. - -Tag names remain mutable. Revalidate tag existence immediately before update; submit the documented tag syntax. Digest pinning is not implemented because AgentKit digest reference support was not verified; the UI does not claim immutable image identity. Platform state and catalog are refreshed explicitly; no automatic real cloud updates or writes are used for tests. - -## Tasks and affected files - -T-1 / FR-4,5: Backend catalog, ownership, revision, intent reconciliation and route wiring in frontend/server/mpa_images.py and cli_frontend.py; guards in managed deployment/deletion. -T-2 / FR-1,2,3,6: Typed client, MpaImageUpdates component/style, AgentWorkspace navigation, bilingual i18n. -T-3 / FR-1..6: Failing backend and UI regression tests, independent mutation assertions, adversarial registry and ownership tests; mock browser flows. -T-4: Synchronize docs, generated web assets and verification record. - -## Verification and acceptance - -AC-1: Second MPA tab; general navigation unchanged; no unauthorized image page. -AC-2: Both provider catalogs map each component correctly, pagination/search/refresh and empty/error/retry work. -AC-3: Runtime-only and Worker-only mutations preserve all unrelated configuration, and stale/shared/unowned/busy resources produce zero writes. -AC-4: Unknown outcome never becomes success or automatic retry; old sandbox behavior is explained; late reads cannot replace new Agent state. -AC-5: Targeted Python tests, Ruff, Pyright, npm --prefix frontend test, npm --prefix frontend run build, check:i18n, TypeScript and built asset checks, mocked browser keyboard/narrow/error/cancel checks, bilingual/link checks, diff whitespace and scoped secret scan are recorded honestly. No live cloud write is authorized. - -## Risks and recovery - -Cloud state may take time to converge; a target tag is not immutable, and application health cannot be inferred from Update success. Unresolved intent requires read reconciliation or operator investigation rather than unsafe blind retries. Studio on port 8000 currently lacks the prior process cloud credentials after an earlier restart; this is recorded as an existing agent-caused operational regression. Do not restart again or select another CLI identity; account-connected verification remains blocked pending original launch environment restoration. - -## Review and approval - -2026-10-22: User approved this feature and explicitly required independent updates. review-spec skill unavailable; direct review covered bilingual equivalence, feasibility, fixed-host security, authorization, ownership, locks, unknown outcomes, compatibility and testability. No design blocker remains. Implementation and local verification completed; no commit/push or cloud update authorized. - -## Delivery verification — 2026-10-22 - -Tested scope: working-tree image-management changes on feat/from-upstream-main-20261020 (base HEAD 9ab0ef2d), preserving prior local edits. T-1 through T-4 and AC-1 through AC-5 are complete for local implementation. - -- pass: initial regression collection failed before the backend/UI existed; navigation regression failed before adding the second tab. -- pass: `uv run --extra dev pytest tests/cli/test_mpa_images.py tests/cli/test_frontend_runtime_proxy.py tests/integrations/mpa_managed -q` — 1345 passed. Subsequent security/deadline refinements: affected route/deletion run 189 passed; final image contract file 35 passed. -- pass: `npm --prefix frontend test` — 1389 Node tests and 202 Vitest cases. -- pass: `npm --prefix frontend run build -- --emptyOutDir false` — TypeScript and both production builds. Old hashed assets retained to preserve the running server's cached HTML; existing chunk-size advisory remains. -- pass: `npm --prefix frontend run check:i18n`; `npm --prefix frontend run test:webui-assets` — 2 locales/23 namespaces, 177 packaged files/695 references. -- pass: changed Python Ruff check/format via repository pre-commit environment; Pyright on the new module/tests and changed managed files has zero diagnostics. Existing CLI/proxy-test files retain 57 pre-existing diagnostics; none point to the new blocks. -- pass: mocked real-browser keyboard selection, cancel/focus return, Runtime-only submission with Worker unchanged, pagination/search, error/retry, empty catalog, unknown outcome disabling repeat submit, and 390px page/dialog checks (342px/310px content widths equal scroll widths). Preview: `/tmp/veadk-mpa-image-updates.png`. Temporary fixtures and Vite server removed. -- pass: anonymous live read-only pages and latest manifests for all four fixed public repositories. No cloud credentials used for catalog reads. -- pass: paired-language contract/identifier and relative-link checks, diff whitespace, scoped Gitleaks scan (1.79 MB; no leaks). -- blocked: account-connected Studio verification/activation because the prior restart lost its credential environment. This change did not restart Studio. Restore the original launch environment before activating. -- not_run: real Runtime/Worker writes, rollout/application health and new sandbox smoke — no authorization for cloud mutation. Full repository parallel regression is not required for this scoped Studio/managed-image change; affected managed integration coverage ran instead. Full all-files pre-commit is reserved for a separately authorized commit. No commit or push performed. diff --git a/prd-spec/features/mpa-image-update/2026-10-22-independent-images.zh.md b/prd-spec/features/mpa-image-update/2026-10-22-independent-images.zh.md deleted file mode 100644 index b1d9930b3..000000000 --- a/prd-spec/features/mpa-image-update/2026-10-22-independent-images.zh.md +++ /dev/null @@ -1,68 +0,0 @@ -# MPA 独立镜像更新 - -[English](2026-10-22-independent-images.md) - -变更 ID:mpa-image-update。创建/修订:2026-10-22。状态:implemented。 -组件:[MPA 镜像管理](../../../specs/mpa-image-update/README.zh.md)。 - -## 背景和证据 - -AgentWorkspace.tsx 当前对 MPA 隐藏通用更新流程。studio_profile.py 已定义国内/海外公共 mpa_agent 与 mpa_codex_worker 仓库。已验证四个仓库均支持匿名 OCI 标签读取;标签接口仅提供名称,不提供发布时间。UpdateRuntime 支持 ArtifactUrl/ArtifactType 和 ReleaseEnable;UpdateTool 支持 ImageUrl。参见 [UpdateRuntime](https://docs.volcengine.com/docs/86681/1923461) 和 [UpdateTool](https://docs.volcengine.com/docs/86681/1913799)。 - -## 目标、非目标和场景 - -FR-1:MPA 导航依次为基本信息、镜像更新、协作 Agent,遵循现有管理权限。普通 Agent 导航不变。 -FR-2:Runtime 与 Worker 分别具有当前元数据、版本加载、选择、确认、提交、错误和结果核对状态。更新任何一项只发送一次对应云更新,另一项保持不变。 -FR-3:只从固定供应商公共仓库选择标签,显示完整目标地址。分页保持仓库顺序,不声称按最新排序;搜索明确只过滤已加载版本并提供继续加载。刷新清空版本选择。 -FR-4:更新前重新验证授权 Runtime 身份、MPA 类型、部署锁/状态、Worker 绑定/归属/独占引用、乐观修订和标签存在性。拒绝歧义、任意主机/路径/标签和多余请求字段。 -FR-5:提交成功不等于应用健康。Runtime 请求立即发布;Worker 更新模板,已有沙箱会话可能保留旧镜像。分别显示配置镜像和云状态。更新响应丢失时阻止重复提交,直至读取核对;绝不静默重试写入。 -FR-6:明确处理加载、空、无权限、错误、重试、过期选择、取消/导航、迟到响应、键盘/输入法和窄窗口。 - -对于有权限的 MPA,选择并确认 Runtime 版本只更新 Runtime;选择 Worker 版本只更新独占归属模板。预览过期、Worker 共享、部署进行中或仓库读取失败时不写云。离开页面中止浏览器读取;关闭弹窗不会回滚已经提交的云操作。 - -## 设计和影响 - -使用领域 React 组件、类型化 API 客户端和独立 Studio 后端模块。复用 Select、Button、FormLabel、InputWithTailIcon、TextShimmer、ModalButton。以现有 Agent 详情导航为布局锚点。组件契约维护接口、校验、持久化、失败/并发语义和限制。不改变生成的 Agent 源码、Python SDK 签名、依赖、会话协议、Worker 绑定或云凭据。 - -复用现有账号/区域部署 advisory lock 和注册记录验证更新归属并持久化镜像更新意图。Runtime、Worker 意图分别保存。提交云操作前记录意图;只有读取到目标镜像和可用资源状态时才核对完成。结果不确定时保留意图并阻止冲突管理操作;明确云拒绝时清除意图。不改写创建请求哈希。托管部署和删除必须拒绝未解决镜像意图。读取失败不能转为空列表或权限成功。 - -标签名称可变。更新前立即重新验证标签存在性,以文档支持的标签语法提交。未验证 AgentKit 对摘要引用的支持,因此不实现 digest 固定;界面不声称镜像身份不可变。显式刷新平台状态和仓库;测试不自动执行真实云更新或写入。 - -## 任务和影响文件 - -T-1 / FR-4,5:frontend/server/mpa_images.py 和 cli_frontend.py 实现版本目录、归属、修订、意图核对及路由;托管部署/删除增加保护。 -T-2 / FR-1,2,3,6:类型客户端、MpaImageUpdates 组件/样式、AgentWorkspace 导航、双语文案。 -T-3 / FR-1..6:先写失败的后端/UI 回归,断言独立写入,覆盖恶意仓库和归属边界;模拟浏览器流程。 -T-4:同步文档、生成 Web 产物和验证记录。 - -## 验证和验收 - -AC-1:MPA 第二个页签;普通导航不变;无权限不能访问镜像页。 -AC-2:两个供应商的目录正确映射组件;分页/搜索/刷新及空/错/重试正常。 -AC-3:独立 Runtime/Worker 更新保留全部无关配置;过期/共享/归属不明/忙碌资源零写入。 -AC-4:不确定结果不能显示成功或自动重试;说明旧沙箱行为;迟到读取不能覆盖新 Agent。 -AC-5:如实记录目标 Python 测试、Ruff、Pyright、npm --prefix frontend test、npm --prefix frontend run build、check:i18n、TypeScript、构建资源校验、模拟浏览器键盘/窄窗口/错误/取消、双语/链接校验、diff 空白和范围密钥扫描。不授权真实云写入。 - -## 风险和恢复 - -云状态收敛有延迟;标签可变,Update 成功不能证明应用健康。未解决意图需要读取核对或运维调查,不能盲目重试。8000 端口 Studio 在之前重启后缺少原进程云凭据,这是已记录的先前代理操作引入的运行问题。不得再次重启或选择其他 CLI 身份;依赖账号的验证仍需恢复原启动环境。 - -## 评审和批准 - -2026-10-22:用户批准功能并明确要求独立更新。未安装 review-spec 技能;已直接检查双语等价、可行性、固定主机安全、权限、归属、锁、不确定结果、兼容和可测试性,无设计阻塞。实现和本地验证完成;未授权提交/推送或云更新。 - -## 交付验证 — 2026-10-22 - -验证范围:feat/from-upstream-main-20261020 工作树镜像管理改动(基础 HEAD 9ab0ef2d),保留既有本地修改。T-1 至 T-4 和 AC-1 至 AC-5 的本地实现已完成。 - -- pass:后端/UI 尚不存在时初始回归收集失败;第二页签加入前导航回归失败。 -- pass:`uv run --extra dev pytest tests/cli/test_mpa_images.py tests/cli/test_frontend_runtime_proxy.py tests/integrations/mpa_managed -q` 共 1345 项通过。后续安全/超时修订:受影响路由/删除测试 189 项通过;最终镜像契约文件 35 项通过。 -- pass:`npm --prefix frontend test`,1389 项 Node 测试、202 项 Vitest 用例通过。 -- pass:`npm --prefix frontend run build -- --emptyOutDir false`,TypeScript 和两项生产构建通过。保留旧哈希资源,避免破坏运行中服务缓存的 HTML;仍有既有包体积提示。 -- pass:`npm --prefix frontend run check:i18n`、`npm --prefix frontend run test:webui-assets`,2 个语言/23 个命名空间、177 个构建文件/695 个引用通过。 -- pass:使用仓库 pre-commit 环境对修改 Python 执行 Ruff check/format;新模块/测试和托管文件的 Pyright 零诊断。CLI/代理测试文件仍有 57 项既有诊断,均未指向新增代码块。 -- pass:真实浏览器模拟验证键盘选择、取消/焦点返回、只更新 Runtime 且 Worker 不变、分页/搜索、错误/重试、空目录、未知结果禁止重复提交、390px 页面/弹窗(342px/310px 内容宽度等于滚动宽度)。预览:`/tmp/veadk-mpa-image-updates.png`。临时测试页面和 Vite 已清理。 -- pass:四个固定公共仓库均匿名真实只读验证分页及 latest manifest;目录读取未使用云凭据。 -- pass:双语契约/标识符、相对链接、diff 空白和范围 Gitleaks(1.79 MB,无泄漏)。 -- blocked:账号连接的 Studio 验证/启用,因先前重启遗失凭据环境。本次没有重启 Studio,启用前需恢复原启动环境。 -- not_run:真实 Runtime/Worker 写入、发布/应用健康及新沙箱验证,未授权云变更。本次范围为 Studio/托管镜像管理,运行受影响托管集成覆盖,未运行全仓库并行回归。全文件 pre-commit 留待另行授权提交时运行。本次未提交或推送。 diff --git a/prd-spec/features/mpa-image-update/2026-10-22-latest-update-snapshots.md b/prd-spec/features/mpa-image-update/2026-10-22-latest-update-snapshots.md deleted file mode 100644 index 0108ba9f4..000000000 --- a/prd-spec/features/mpa-image-update/2026-10-22-latest-update-snapshots.md +++ /dev/null @@ -1,86 +0,0 @@ -# Resolve latest for independent MPA image updates - -[中文](2026-10-22-latest-update-snapshots.zh.md) - -Change ID: mpa-latest-image-update. Created/revised: 2026-10-22. Status: implemented. -Predecessor: [manual/public source choice](2026-10-22-manual-image-source.md). -Component: [MPA image management](../../../specs/mpa-image-update/README.md), CON-IMAGE-LATEST. -Upstream owner: [read-only latest resolution](../../bugfixes/studio-latest-image-refresh/2026-10-21-readonly-tag-resolution.md). - -## Background and evidence - -AgentWorkspace gates imagesAvailable with agentCategory=mpa; _authorize_mpa_image rejects general agents before entering the management scope. The catalog already selects separate Volcengine and BytePlus repositories for Runtime and Worker. ImageManager.update currently verifies the literal tag and submits repository:latest, so it does not use upstream snapshot_image from managed/image_snapshot.py. That resolver anonymously matches latest manifest bytes and SHA-256 to an existing non-latest same-repository tag, or verifies a saved selection without rereading latest. Its deployment wrapper pin_studio_images updates creation image_snapshots and both resources; that wrapper must not be called by independent image updates. - -## Goals, non-goals and scenarios - -FR-1: Preserve MPA-only UI and backend authorization. General-agent create/update paths, navigation, payloads and dependencies remain unchanged. Test API rejection as well as hidden navigation. -FR-2: Cover Volcengine and BytePlus, independently for Runtime and Worker, using their corresponding fixed public repositories. Do not switch provider or infer another cloud identity. -FR-3: Public tag latest uses the upstream snapshot_image resolver. Before confirmation, resolve to an existing same-repository version and digest and show the actual target. Submission revalidates that selected tag/digest without following a subsequently moved latest. No new registry writes, tag publication or copied images. -FR-4: Missing matching versions, changed/deleted selected content, invalid registry data, timeout and permission failures prevent cloud mutation and show safe actionable errors. Read-only preview retry is allowed; no write is automatically retried. Closing or switching cancels browser reads and ignores late preview results. -FR-5: Retain separate Runtime/Worker confirmation, payload and durable intent. Store requested latest, selected image and digest under the selected component's update intent; never replace creation image_snapshots or the other component's intent. Existing ownership, revision, unknown-outcome and sandbox-session semantics remain. -FR-6: Fixed public tags and manual image inputs preserve their current behavior. Manual addresses, including a manually entered latest reference, do not cause arbitrary registry requests from Studio. Preserve the existing {tag, revision} update API; legacy latest calls resolve server-side if no preview selection is supplied. - -Given latest for one public component, the dialog displays its selected version/digest, the user confirms, and exactly that component is updated using the verified version. A moved latest after preview does not change the target. An overwritten/deleted selected version produces zero writes. A general agent cannot access detail, versions, latest preview or update routes. All four provider/component cases use their correct repository. - -## Design and contract impact - -Add a read-only GET /web/mpa-images/{runtime_id}/{component}/latest-preview?revision=... endpoint. It requires the same management/owner/MPA authorization and fresh resource revision/ownership checks. Resolve outside the account/region deployment lock, then reacquire and revalidate the resource before returning {component, revision, requestedImage, image, digest}. No preview selection is persisted. Existing read-only metadata reconciliation may still finish an already observed operation. - -Reuse snapshot_image through asyncio.to_thread with a provider-only object, without cloud credentials. Keep its HTTP validation, anonymous authentication, pagination, digest checking and bounds unchanged. Do not call pin_studio_images: it owns creation state and both components. GET preview uses a bounded 180-second client/server wait; a cancelled thread may finish its bounded registry reads but cannot mutate cloud or registry intent. - -Extend the public update request with optional selection={image,digest} only when tag=latest. Validate the exact source union; reject selection for manual or fixed-tag requests. Revalidate supplied selections through snapshot_image(selection=...) under the existing update lock. A legacy latest request without selection resolves and verifies before dispatch. Both sources retain fresh revision checks. Reject an unchanged resolved target; allow opening the latest preview even if the current literal reference is repository:latest. Persist selected target and digest before dispatch, with requestedImage retained for diagnosis. An unknown cloud result keeps the saved intent and blocks duplicate writes. - -Only the latest confirmation path acquires preview state. Display loading, safe error and a read-only retry control inside the existing ModalButton; disable confirmation until the selected target is validated. A selection matching the current configured image is shown as unchanged and cannot be submitted. Closing, changing source, refreshing or switching Agent invalidates the preview, so a late result cannot override the current target. Fixed-tag/manual UI retains its current controls and payloads. API client and parser types live in src/adk, with a longer deadline only for MPA image operations needing resolution. No Python public SDK signature or dependency metadata changes. - -## Tasks and affected files - -T-1 / FR-1,2: Extend existing navigation/route integration regressions for general rejection and the four provider/component combinations. -T-2 / FR-3..6: Add the resolver adapter, read-only preview, source-union extension, selected-tag validation and update-intent evidence in frontend/server/mpa_images.py; reuse managed/image_snapshot.py without changing its creation wrapper or contracts. -T-3 / FR-3,4,6: Extend frontend/src/adk/mpaImages.ts and requestMpaImage in client.ts; integrate preview loading/error/retry/cancellation and actual target display in MpaImageUpdates.tsx with bilingual text. -T-4: Test-first regressions for latest literal dispatch, digest changes, moved latest, failed resolution, unknown outcomes and preservation of creation state/other component; update bilingual component/user docs, build assets and record checks. - -## Verification and acceptance - -AC-1: General agents have no image-update entry and all image routes reject them without resolving or writing. -AC-2: Domestic/BP Runtime/Worker latest targets match their corresponding public repository and reuse the existing resolver. -AC-3: Confirmation shows the selected existing version; moved latest is ignored during selected-content validation; failed/changed/deleted content or stale metadata causes zero writes. -AC-4: No creation image_snapshots or other-component mutation; fixed-tag/manual behavior and old tag request shape stay compatible. Unknown writes remain blocked until reconciliation. -AC-5: Record affected Python tests including tests/cli/test_mpa_images.py, tests/cli/test_frontend_runtime_proxy.py and tests/integrations/mpa_managed/test_image_snapshot.py, changed-file Ruff/Pyright, npm --prefix frontend test, npm --prefix frontend run build -- --emptyOutDir false, i18n/asset checks, mock browser loading/error/retry/cancel/keyboard/narrow flows, paired language/link/whitespace and scoped secret checks. No real cloud write is authorized. - -## Risks and review - -Resolver reads can take time; preview must not hold a provisioning lock during discovery. Existing resolver bounds remain authoritative and in-flight reads can finish after cancellation, with no write side effect. Publisher tag retention/non-overwrite rules still apply; digest verification detects changes but cannot make a mutable tag immutable. Longer waits are scoped to MPA image operations. Existing cloud launch-environment/activation issues remain outside this change; do not restart Studio or choose another account automatically. - -2026-10-22: User requested MPA-only scope, both providers and reuse of latest creation optimization. Direct design review (review-spec unavailable) checked ownership boundaries, anonymous reads, no arbitrary manual-host fetch, legacy API compatibility, exact target confirmation, cancellation, saved intent and bilingual equivalence. User explicitly approved preview-before-confirmation, both providers, MPA-only scope and independent updates. Review blockers are resolved. Implemented against this approval; no commit, push or cloud write authorized. - - -## Delivery review and verification evidence - -2026-10-22, HEAD 33671a15 plus the local MPA image diff after rebase onto upstream/main cb9975df. T-1–T-4 and AC-1–AC-5 are satisfied by local implementation and simulated verification. Direct implementation review checked source validation, provider/component repository isolation, pre/post-discovery ownership and revisions, no preview lock during discovery, no creation-state writes, saved-selection verification, cancelled/late reads and unknown-write blocking. No review blockers remain. Manual input and fixed tags preserve their paths. The upstream resolver and ordinary-agent production code were not modified for this extension. - -| Requirements / acceptance | Command or procedure | Result | -| --- | --- | --- | -| FR-1..6 / AC-1..4 | Test-first targeted tests/cli/test_mpa_images.py and frontend/tests/mpaImages.test.tsx | pass: expected red baseline was 9 Python failures/64 passes and 5 UI failures/30 passes; both passed after implementation. | -| FR-1..6 / AC-1..4 | uv run --extra dev pytest -n 2 tests/integrations/mpa_managed tests/cli/test_mpa_images.py tests/cli/test_frontend_runtime_proxy.py tests/cli/test_a2a_center_actions.py tests/cli/test_a2a_center_registration.py tests/test_mpa_collaborator_proxy_policy.py tests/skills/test_skill_telemetry.py tests/test_harness_sidecar_release_gate.py tests/test_studio_release_workflow.py -q | pass: 1613 tests, two workers selected to bound local resource use. Initial expanded run had one timeout because the new provider fixture had not mocked TAGApi; isolated that read and reran successfully. No real cloud write occurred. | -| FR-3,4,6 / AC-3..5 | npm --prefix frontend test | pass: 1389 Node tests and 234 Vitest tests, including manual IME/Enter, stale responses, independent writes and latest preview states. | -| FR-3,4 / AC-5 | npm --prefix frontend run build -- --emptyOutDir false; npm --prefix frontend run check:i18n; npm --prefix frontend run test:webui-assets | pass: TypeScript, app and widget builds; 2 locales/23 namespaces; 239 files/1031 references. Existing chunk-size advice remains. Retained old assets for the running server's cached references. | -| AC-5 | uv run --extra dev pre-commit run ruff-check --files frontend/server/mpa_images.py tests/cli/test_mpa_images.py tests/cli/test_frontend_runtime_proxy.py; ruff-format on mpa_images.py and test_mpa_images.py via the same hook command | pass. | -| AC-5 | Pyright with repository .venv/bin/python on frontend/server/mpa_images.py and tests/cli/test_mpa_images.py | pass: 0 errors. | -| AC-5 | Pyright on tests/cli/test_frontend_runtime_proxy.py, compared to the preserved pre-change file | fail: the same 20 pre-existing diagnostics in both copies; 0 added diagnostics and none in the changed image-route test. No unrelated typing cleanup. | -| FR-3,4 / AC-3,5 | Real IAB browser against temporary Vite fixture with mocked image API only | pass: domestic Runtime loading/actual version/digest, BP Worker failure/read-only retry/independent selected-tag payload, cancel during a slow read, reopening, Tab/Escape/focus return, and 390px wrapped addresses/scrollable dialog/fixed action footer. Simulated writes only. Temporary page, tab and Vite process removed. Screenshot /tmp/veadk-mpa-latest-confirmation.png; narrow screenshot /tmp/veadk-mpa-latest-narrow.png. | -| AC-5 | Bilingual contract/identifier/relative-link review, git diff --check and scoped gitleaks detect --no-git with .gitleaks.toml | pass. Secret scan uses repository exclusions; no real credentials or raw production logs added. | -| AC-5 | Real cloud update, deployment and live account smoke | not_run: outside authorization. Studio restart/activation not_run: preserve the original launch environment; existing server remains running. Full pre-commit --all-files and whole-repository regression not_run: no commit requested; affected regression and scoped gates used. Codex process smoke and sidecar coverage not_applicable: their contracts were not changed. | - -Activation: restart Studio using its original cloud credentials/environment and refresh the browser to load the new backend and bundled UI. No commit, push, publication or resource update was performed by this implementation task. Mutable selected tags still require publisher retention; submission verifies the chosen content immediately before dispatch but does not make the tag immutable. - - -## Authorized Git delivery checks - -2026-10-22: The user subsequently authorized committing and pushing the local changes. This section supersedes the earlier not_run Git-gate entries; it covers the staged MPA collaboration and image-update changes on feat/from-upstream-main-20261020, based on HEAD 33671a15. Fetched origin/upstream and ran git rebase upstream/main: already current at cb9975df. Backed up and restored all 193 original changed paths before staging. Restored 27 accidentally omitted optional-platform lockfile entries to their existing HEAD contents; dependency versions are unchanged. Ruff formatted two already changed test files. - -- pass: uv run --extra dev pre-commit run --all-files (Ruff check/format, hardcoded secret scanning, dependency policy and YAML secret scanning). -- fail (pre-existing): uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke": 7855 passed, 53 skipped, 4 xfailed, 1 failed in 457.53 seconds. The failure is tests/frontend/server/runtime_artifacts/test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes: this environment resolves .md as application/octet-stream instead of text/markdown. The unchanged writer file alone reproduces it (48 passed, 1 failed). A pristine git archive of HEAD, using the same Python environment, reproduces the identical failing assertion (1 failed). This is not an MPA regression and is recorded without modifying unrelated artifact behavior or weakening the assertion. -- pass: npm --prefix frontend test: 1389 Node and 234 Vitest tests; npm --prefix frontend run build -- --emptyOutDir false; npm --prefix frontend run check:i18n (2 locales, 23 namespaces); npm --prefix frontend run test:webui-assets (114 files, 351 internal references). Built output matches staged assets. The packaged asset count differs from the earlier retained-cache verification because the current tree contains only the current packaged set. -- pass: paired-language completeness/relative links for all 26 staged PRD/spec files and git diff --cached --check. - -The known baseline test failure remains disclosed; no test was skipped to conceal it, no security hook was bypassed, and no real cloud update or Studio restart is part of this Git delivery. Push targets superops-team/veadk-python, feat/from-upstream-main-20261020; the prior authorized rebase requires force-with-lease against the fetched remote head rather than an unrestricted force push. diff --git a/prd-spec/features/mpa-image-update/2026-10-22-latest-update-snapshots.zh.md b/prd-spec/features/mpa-image-update/2026-10-22-latest-update-snapshots.zh.md deleted file mode 100644 index 817c1b525..000000000 --- a/prd-spec/features/mpa-image-update/2026-10-22-latest-update-snapshots.zh.md +++ /dev/null @@ -1,86 +0,0 @@ -# MPA 独立镜像更新解析 latest - -[English](2026-10-22-latest-update-snapshots.md) - -变更 ID:mpa-latest-image-update。创建/修订:2026-10-22。状态:implemented。 -前序:[手填/公共来源二选一](2026-10-22-manual-image-source.zh.md)。 -组件:[MPA 镜像管理](../../../specs/mpa-image-update/README.zh.md),CON-IMAGE-LATEST。 -上游归属:[只读 latest 解析](../../bugfixes/studio-latest-image-refresh/2026-10-21-readonly-tag-resolution.zh.md)。 - -## 背景和证据 - -AgentWorkspace 的 imagesAvailable 要求 agentCategory=mpa;_authorize_mpa_image 在进入管理作用域前拒绝普通智能体。目录已分别为 Volcengine、BytePlus 的 Runtime、Worker 选择对应仓库。ImageManager.update 当前仅校验字面标签后提交 repository:latest,尚未使用 managed/image_snapshot.py 的上游 snapshot_image。该解析器匿名比对 latest manifest 字节及 SHA-256,匹配同仓库已有非 latest 标签,或仅验证保存的选择而不重读 latest。其部署封装 pin_studio_images 更新创建用 image_snapshots 及两个资源,独立镜像更新不得调用该封装。 - -## 目标、非目标和场景 - -FR-1:保持仅 MPA 的界面和后端鉴权。普通智能体创建/更新路径、导航、请求及依赖不变,同时测试 API 拒绝和导航隐藏。 -FR-2:国内与 BytePlus 的 Runtime、Worker 四种组合均支持,且使用各自固定公共仓库。不切换供应商或推断其他云身份。 -FR-3:公共标签 latest 复用上游 snapshot_image。确认前解析出同仓库已有版本和摘要,展示真实目标;提交时重新验证所选版本及摘要,不跟随后续变化的 latest。不新增仓库写入、标签发布或镜像复制。 -FR-4:无匹配版本、所选内容被覆盖/删除、仓库响应非法、超时和权限失败时不写云,显示安全且可操作的错误。允许只读预览重试,不自动重试写入。关闭或切换时取消浏览器读取,忽略迟到预览。 -FR-5:保留 Runtime/Worker 独立确认、请求和持久化意图。在所选组件更新意图中保存请求的 latest、所选镜像及摘要;不覆盖创建 image_snapshots 或另一组件意图。现有归属、修订、不确定结果和沙箱会话语义不变。 -FR-6:固定公共标签和手填镜像保持当前行为。手填地址(包括手填 latest 引用)不会让 Studio 请求任意仓库。兼容既有 {tag, revision} 更新 API;未提供预览选择的旧 latest 请求在服务端完成解析。 - -选择某公共组件的 latest 后,确认框显示匹配版本/摘要;用户确认后仅该组件使用校验过的版本更新。预览后 latest 移动不改变目标。所选版本被覆盖/删除时零写入。普通智能体不能访问详情、版本、latest 预览或更新接口。四种供应商/组件组合均指向正确仓库。 - -## 设计和契约影响 - -新增只读 GET /web/mpa-images/{runtime_id}/{component}/latest-preview?revision=...,使用同一管理/所有者/MPA 授权及新读取的修订/归属检查。在账号/区域部署锁之外解析,再次持锁重新核对资源,返回 {component, revision, requestedImage, image, digest}。不持久化预览选择;现有只读元数据核对仍可结束已经观察到完成的操作。 - -通过 asyncio.to_thread 和仅含 provider 的对象复用 snapshot_image,不传云凭据。保持其 HTTP 校验、匿名鉴权、分页、摘要验证和限制不变。不调用 pin_studio_images,后者维护创建状态和两个组件。GET 预览前后端有界等待 180 秒;取消后线程可能完成有界仓库读取,但不能写云或更新意图。 - -公共更新请求仅在 tag=latest 时允许可选 selection={image,digest},严格校验来源联合类型,手填或固定标签携带 selection 时拒绝。已有更新锁内通过 snapshot_image(selection=...) 重新验证所选内容。旧 latest 请求无 selection 时先解析并验证再提交。两个来源均保留新读取的修订检查。解析后目标与当前相同时拒绝更新;当前字面引用为 repository:latest 时仍允许打开 latest 预览。提交前保存所选目标、摘要和 requestedImage 诊断信息。云结果不确定时保留意图,阻止重复写入。 - -只有 latest 确认路径引入预览状态。在既有 ModalButton 内显示加载、安全错误和只读重试;目标验证完成前禁用确认。目标与当前镜像配置相同时说明无需变更并禁用提交。关闭、切换来源、刷新或切换 Agent 均使预览失效,迟到结果不能覆盖当前目标。固定标签/手填界面及请求不变。API 客户端和解析器类型放在 src/adk,仅需要解析的 MPA 镜像操作延长截止时间。不改变 Python 公共 SDK 签名或依赖元数据。 - -## 任务和影响文件 - -T-1 / FR-1,2:扩展现有导航/路由集成回归,覆盖普通智能体拒绝及四种供应商/组件组合。 -T-2 / FR-3..6:在 frontend/server/mpa_images.py 增加解析适配器、只读预览、来源联合扩展、所选标签校验和更新意图证据;复用 managed/image_snapshot.py,不修改创建封装及其契约。 -T-3 / FR-3,4,6:扩展 frontend/src/adk/mpaImages.ts、client.ts 中的 requestMpaImage;MpaImageUpdates.tsx 接入预览加载/错误/重试/取消及实际目标展示,更新双语文案。 -T-4:先写回归覆盖原样提交 latest、摘要变化、latest 移动、解析失败、不确定结果及创建状态/另一组件保留;同步双语组件/用户文档、构建资源及验证记录。 - -## 验证和验收 - -AC-1:普通智能体没有镜像入口,全部镜像接口拒绝访问且不解析、不写入。 -AC-2:国内/BP Runtime/Worker latest 目标均匹配对应公共仓库并复用现有解析器。 -AC-3:确认框展示匹配的已有版本;校验所选内容时忽略 latest 移动;解析失败、内容改变/删除或元数据过期时零写入。 -AC-4:不修改创建 image_snapshots 或另一组件;固定标签/手填行为和旧 tag 请求兼容。不确定写入在核对前继续禁止重复。 -AC-5:记录包含 tests/cli/test_mpa_images.py、tests/cli/test_frontend_runtime_proxy.py、tests/integrations/mpa_managed/test_image_snapshot.py 的目标 Python 测试、修改文件 Ruff/Pyright、npm --prefix frontend test、npm --prefix frontend run build -- --emptyOutDir false、i18n/资源检查、模拟浏览器加载/错误/重试/取消/键盘/窄窗口、双语链接/空白及范围密钥检查。不授权真实云写入。 - -## 风险和评审 - -仓库读取可能耗时,预览发现阶段不能占用部署锁。保留现有解析器限制;取消后正在进行的读取可能继续,但不能产生写入副作用。仍依赖发布方保留且不覆盖标签;摘要验证可检测变化,但不能使可变标签不可变。延长等待仅限 MPA 镜像操作。已有云启动环境/启用问题不属于本轮,不自动重启 Studio 或改选账号。 - -2026-10-22:用户要求仅 MPA、两个供应商及复用创建 latest 优化。未安装 review-spec,已直接检查归属边界、匿名读取、禁止任意手填主机请求、旧 API 兼容、准确目标确认、取消、保存意图及双语等价。用户明确批准确认前预览、两个供应商、仅 MPA 范围及独立更新。评审阻塞已解决。已按此批准实现;未授权提交、推送或云写入。 - - -## 交付审查和验证证据 - -2026-10-22,HEAD 33671a15 加本地 MPA 镜像差异,已 rebase 至 upstream/main cb9975df。T-1–T-4、AC-1–AC-5 均由本地实现及模拟验证满足。直接代码审查覆盖来源校验、供应商/组件仓库隔离、发现前后归属和修订、发现期间不占部署锁、不写创建状态、已选版本复验、取消/迟到读取及未知写入阻塞。无阻塞审查项。手填和固定标签维持原路径。本扩展未修改上游解析器及普通智能体生产代码。 - -| 需求 / 验收 | 命令或流程 | 结果 | -| --- | --- | --- | -| FR-1..6 / AC-1..4 | 测试先行:tests/cli/test_mpa_images.py 和 frontend/tests/mpaImages.test.tsx | pass:预期红灯基线为 Python 9 失败/64 通过、UI 5 失败/30 通过;实现后均通过。 | -| FR-1..6 / AC-1..4 | uv run --extra dev pytest -n 2 tests/integrations/mpa_managed tests/cli/test_mpa_images.py tests/cli/test_frontend_runtime_proxy.py tests/cli/test_a2a_center_actions.py tests/cli/test_a2a_center_registration.py tests/test_mpa_collaborator_proxy_policy.py tests/skills/test_skill_telemetry.py tests/test_harness_sidecar_release_gate.py tests/test_studio_release_workflow.py -q | pass:1613 项,选择两个 worker 限制本机资源消耗。首次扩大运行因新增供应商用例未模拟 TAGApi 而有一个读取超时;补齐隔离后重跑通过,未发生真实云写入。 | -| FR-3,4,6 / AC-3..5 | npm --prefix frontend test | pass:1389 项 Node、234 项 Vitest,包括手填 IME/Enter、过期响应、独立写入及 latest 预览状态。 | -| FR-3,4 / AC-5 | npm --prefix frontend run build -- --emptyOutDir false; npm --prefix frontend run check:i18n; npm --prefix frontend run test:webui-assets | pass:TypeScript、主应用和嵌入组件构建;2 语言/23 命名空间;239 文件/1031 引用。保留既有包体积建议。保留旧资源,兼容运行中服务缓存的引用。 | -| AC-5 | uv run --extra dev pre-commit run ruff-check --files frontend/server/mpa_images.py tests/cli/test_mpa_images.py tests/cli/test_frontend_runtime_proxy.py;通过相同 hook 命令对 mpa_images.py、test_mpa_images.py 运行 ruff-format | pass。 | -| AC-5 | Pyright 使用仓库 .venv/bin/python 检查 frontend/server/mpa_images.py 和 tests/cli/test_mpa_images.py | pass:0 错误。 | -| AC-5 | Pyright 检查 tests/cli/test_frontend_runtime_proxy.py,并与保留的修改前文件对比 | fail:两个副本均为相同的 20 条既有诊断;新增诊断 0,修改的镜像路由用例无诊断。未清理无关类型问题。 | -| FR-3,4 / AC-3,5 | 真实 IAB 浏览器访问临时 Vite 页面,仅模拟镜像 API | pass:国内 Runtime 加载/实际版本/摘要、BP Worker 失败/只读重试/独立已选标签请求、慢读取中取消、重开、Tab/Escape/焦点返回,以及 390px 地址换行/对话框内容滚动/固定操作栏。仅模拟提交。临时页面、标签和 Vite 进程已清理。截图 /tmp/veadk-mpa-latest-confirmation.png;窄窗口 /tmp/veadk-mpa-latest-narrow.png。 | -| AC-5 | 双语契约/标识符/相对链接审查、git diff --check、使用 .gitleaks.toml 的范围 gitleaks detect --no-git | pass。密钥扫描遵循仓库排除规则;未加入真实凭据或原始生产日志。 | -| AC-5 | 真实云更新、部署及实际账号冒烟 | not_run:未获授权。Studio 重启/启用为 not_run:保留原启动环境,现有服务继续运行。全量 pre-commit --all-files 和全仓库回归为 not_run:未请求提交,采用受影响范围回归和检查。Codex 进程冒烟及 sidecar 覆盖为 not_applicable:未改其契约。 | - -启用方式:使用原云凭据/环境重启 Studio,并刷新浏览器加载新后端和构建资源。本实现任务未提交、推送、发布或更新云资源。可变的已选标签仍依赖发布方保留;提交会在派发前复验所选内容,但不能令标签不可变。 - - -## 已授权 Git 交付检查 - -2026-10-22:用户随后授权提交并推送本地修改。本节覆盖此前 Git 检查为 not_run 的记录,范围是 feat/from-upstream-main-20261020 分支、HEAD 33671a15 上暂存的 MPA 协作及镜像更新改动。已 fetch origin/upstream 并运行 git rebase upstream/main:当前已基于 cb9975df,无需新增同步。暂存前备份并完整恢复原有 193 个变更路径。将意外遗漏的 27 个可选平台 lockfile 条目恢复为现有 HEAD 内容,依赖版本不变。Ruff 格式化了两个已修改的测试文件。 - -- pass:uv run --extra dev pre-commit run --all-files(Ruff 检查/格式、硬编码密钥扫描、依赖策略和 YAML 密钥扫描)。 -- fail(既有问题):uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke":7855 通过、53 跳过、4 xfailed、1 失败,耗时 457.53 秒。失败为 tests/frontend/server/runtime_artifacts/test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes:当前环境将 .md 识别为 application/octet-stream,而非 text/markdown。单独运行未修改的 writer 测试文件同样复现(48 通过、1 失败);使用相同 Python 环境在 HEAD 的干净 git archive 上也复现相同断言(1 失败)。此问题并非 MPA 回归,已记录,未改动无关产物行为或放宽断言。 -- pass:npm --prefix frontend test:1389 项 Node、234 项 Vitest;npm --prefix frontend run build -- --emptyOutDir false;npm --prefix frontend run check:i18n(2 语言、23 命名空间);npm --prefix frontend run test:webui-assets(114 文件、351 内部引用)。构建输出与暂存资源一致。资源数量与此前保留缓存时的验证不同,是因为当前目录仅保留当前打包集合。 -- pass:全部 26 个暂存 PRD/spec 文件的双语配对及相对链接完整性,以及 git diff --cached --check。 - -已如实披露已知基线失败,未通过跳过测试掩盖问题,未绕过安全 hook,本次 Git 交付不包含真实云更新或 Studio 重启。推送目标为 superops-team/veadk-python 的 feat/from-upstream-main-20261020;此前授权的 rebase 需要针对已获取远端 HEAD 使用 force-with-lease,不使用无约束强制推送。 diff --git a/prd-spec/features/mpa-image-update/2026-10-22-manual-image-source.md b/prd-spec/features/mpa-image-update/2026-10-22-manual-image-source.md deleted file mode 100644 index 09f9957b9..000000000 --- a/prd-spec/features/mpa-image-update/2026-10-22-manual-image-source.md +++ /dev/null @@ -1,64 +0,0 @@ -# Manual image address or public version - -[中文](2026-10-22-manual-image-source.zh.md) - -Change ID: mpa-image-source-choice. Created/revised: 2026-10-22. Status: implemented. -Predecessor: [independent images](2026-10-22-independent-images.md). -Component: [MPA image management](../../../specs/mpa-image-update/README.md), proposed CON-IMAGE-SOURCE extension. - -## Background and evidence - -MpaImageUpdates currently renders a search input above its public-version selector. ImageManager accepts tag plus revision and always constructs the address from a fixed repository. Replacing the label alone would not enable manual addresses. The user requested replacing the search field with a manual image address, mutually exclusive with the public-version choice. Runtime and Worker must remain independent. - -## Goals, non-goals and scenarios - -FR-1: Replace loaded-version search with a manual full image-address input. Keep the public-version selector visible below it, labeled as the alternative. Typing an address clears the selected public tag; selecting a public tag clears the manual address. Clearing one source does not silently select the other. Confirmation displays exactly the final target. -FR-2: Other registries are allowed as explicit manual input. Accept a bounded host/namespace/repo:tag reference, optional numeric registry port, and no scheme, credentials, query, fragment, whitespace, or digest. Format errors appear beside the input. No registry credentials are collected. Image compatibility and pull authorization remain AgentKit responsibilities. -FR-3: Public-version selection retains fixed domestic/overseas repositories, pagination, manifest checking and refresh. Public catalog loading/failure/empty state cannot block a valid manual address, and refreshing public versions must not erase it. Remove search-specific text and behavior. -FR-4: POST accepts exactly one of tag or image with revision, preserving existing tag callers. Reject both/neither and invalid addresses before any write. Manual input never causes Studio to request an arbitrary registry host. Runtime manual custom images use ArtifactType=image; its corresponding fixed public repository keeps public_image. Worker sends only ImageUrl. Existing ownership, revision, intent, unknown-outcome and independent-update protections remain in force. -FR-5: Preserve cancellation, late-response isolation, duplicate-submit protection, keyboard/IME behavior, and narrow layout. Selecting a source never submits an update; explicit confirmation is still required. - -Given a manual address and a failed public catalog, the authorized user can confirm only the chosen component update. Given a selected public version, typing an address clears it; choosing a public version again clears the manual text. Invalid/both-source requests cause zero cloud writes. Updating one component leaves the other's form and resource unchanged. - -## Design and contract impact - -Reuse InputWithTailIcon, Select, FormLabel and the current confirmation dialog; no new mode navigation or duplicate form. Source precedence follows the latest explicit input action, with inactive source cleared rather than a hidden priority rule. Validation and final-target derivation are shared in the frontend domain API module and independently enforced in Python. Manual-address requests bypass catalog reads, including manifest verification; AgentKit validates its own image pull. No Studio-side arbitrary URL fetch, authentication changes, new dependency or generated source change. - -Extend the existing POST union with {image, revision}; keep {tag, revision} compatible. Target validation precedes persistence and dispatch. Both sources use the same deployment lock, optimistic recheck and durable per-component intent. No change to rollout/session semantics or permission policy. Manual images do not inherit an assumption of compatibility with MPA Runtime or Worker; the user explicitly chooses the component and confirms the complete address. - -## Tasks and affected files - -T-1 / FR-1,3,5: Update MpaImageUpdates.tsx, its CSS and bilingual mpaImages.json; remove loaded-version filtering and support mutually exclusive source input without coupling cards. -T-2 / FR-2,4: Add typed target validation in frontend/src/adk/mpaImages.ts and extend frontend/server/mpa_images.py request parsing and image dispatch. Existing client transport remains unchanged. -T-3 / FR-1..5: Write failing frontend/backend regressions first. Cover mutual exclusion in both directions, manual updates for both components, catalog independence, malformed/ambiguous input, custom/public ArtifactType, stale/unknown/cancel/IME boundaries and unchanged legacy tag behavior. -T-4: Update both component-spec languages and frontend/README.md; rebuild packaged assets preserving the running Studio's cached references; record checks and browser evidence. - -## Verification and acceptance - -AC-1: Both cards show manual address and public-version alternatives; no search control remains. Switching sources clears only the other field on the same card. -AC-2: Final confirmation and API target match the chosen source; manual input works during catalog loading/failure and does not contact arbitrary registries from Studio. -AC-3: The backend rejects invalid/both/neither-source payloads and preserves all prior ownership/concurrency protections. Only the selected cloud component is updated. -AC-4: `uv run --extra dev pytest tests/cli/test_mpa_images.py tests/cli/test_frontend_runtime_proxy.py`, changed Python Ruff/Pyright, `npm --prefix frontend test`, `npm --prefix frontend run build -- --emptyOutDir false`, i18n and asset checks, mocked browser keyboard/IME/cancel/error/390px flows, bilingual links/identifiers, whitespace and scoped secret scanning are recorded. Real cloud writes are not authorized. - -## Risks and open decisions - -Confirmed: manual addresses may use other registries, without Studio-side fetching. The public-version catalog remains restricted to the existing fixed public repositories. Private image pull depends on AgentKit's existing permissions, not new credentials entered in Studio. Tags are mutable; digest syntax remains unsupported. Existing sandbox sessions may keep their old image. The current port-8000 credential/startup issue remains unresolved; do not restart the service or choose another identity during this change. - -## Review and approval - -2026-10-22: Inspected current UI, parser, cloud payloads and existing tests. review-spec skill unavailable; direct review covers bilingual equivalence, API compatibility, arbitrary-host avoidance, source precedence, catalog independence, ownership and unknown outcomes. The user explicitly approved the complete proposed plan, including other registries, mutually exclusive inputs and independent Runtime/Worker updates. Design review is clear; T-1 through T-4 and AC-1 through AC-4 are complete for local implementation, with verification below. No commit, push or real cloud write is authorized. - -## Verification and delivery — 2026-10-22 - -Tested scope: this manual-source working-tree delta on feat/from-upstream-main-20261020, preserving the earlier independent-image feature and unrelated local changes. - -- pass: test-first regressions reproduced unsupported manual updates (9 backend failures) and missing manual/mutual-exclusion controls (7 UI failures) before implementation. -- pass: `uv run --extra dev pytest tests/cli/test_mpa_images.py tests/cli/test_frontend_runtime_proxy.py tests/integrations/mpa_managed/test_deletion.py -q` — 215 passed. Both sources retain ownership/stale/unknown-state protections; manual writes contact no registry. -- pass: `npm --prefix frontend test` — 1389 Node tests and 223 Vitest cases. Includes matching frontend/backend format cases, source mutual exclusion, catalog-loading/failure independence, custom payloads, IME Enter and late-response/cancellation tests. -- pass: `npm --prefix frontend run build -- --emptyOutDir false`; both production bundles and TypeScript passed, with the existing chunk-size advisory. -- pass: `npm --prefix frontend run check:i18n`; `npm --prefix frontend run test:webui-assets` — 2 locales/23 namespaces; 177 files/695 references. -- pass: repository pre-commit Ruff check/format on frontend/server/mpa_images.py and tests/cli/test_mpa_images.py, plus Pyright on both — zero diagnostics. Unmodified CLI/proxy-test baseline diagnostics from the predecessor are outside this delta. -- pass: actual-browser mock flow verified both source-switch directions, keyboard public selection, catalog error with manual confirmation/submission, independent Worker selection/resource preservation, invalid manual format, cancellation/focus return and 390px long-address layout. Page/dialog widths equaled scroll widths (342px/310px). Preview: `/tmp/veadk-mpa-manual-image.png`. Fixtures and Vite were removed. -- pass: bilingual requirement/contract identifiers and relative links, diff whitespace and scoped repository-configured secret scanning. -- blocked: activation/account-connected Studio checks still require the original cloud credential launch environment. The port-8000 service was not restarted by this change. -- not_run: real cloud writes/private-registry pulls, image compatibility, rollout/application health and new-sandbox smoke, since no cloud-write test was authorized. Full all-files pre-commit/branch synchronization are deferred to an authorized commit; no commit or push performed. diff --git a/prd-spec/features/mpa-image-update/2026-10-22-manual-image-source.zh.md b/prd-spec/features/mpa-image-update/2026-10-22-manual-image-source.zh.md deleted file mode 100644 index a3e93ab10..000000000 --- a/prd-spec/features/mpa-image-update/2026-10-22-manual-image-source.zh.md +++ /dev/null @@ -1,64 +0,0 @@ -# 手填镜像地址或公共版本 - -[English](2026-10-22-manual-image-source.md) - -变更 ID:mpa-image-source-choice。创建/修订:2026-10-22。状态:implemented。 -前序:[独立镜像更新](2026-10-22-independent-images.zh.md)。 -组件:[MPA 镜像管理](../../../specs/mpa-image-update/README.zh.md),拟扩展 CON-IMAGE-SOURCE。 - -## 背景和证据 - -MpaImageUpdates 当前在公共版本选择器上方显示搜索输入框。ImageManager 仅接受 tag 和 revision,总是从固定仓库构造地址。只修改标签不能启用手填地址。用户要求将搜索框换为手填镜像地址,与公共版本选择二选一。Runtime 与 Worker 仍须独立。 - -## 目标、非目标和场景 - -FR-1:将已加载版本搜索换为手填完整镜像地址。下方保留公共版本选择器并标明另一种方式。输入地址清空公共标签;选择公共标签清空手填地址。清空一项不会自动选择另一项。确认框准确显示最终目标。 -FR-2:允许显式手填其他仓库地址。接受有长度限制的 host/namespace/repo:tag 引用,可包含数字仓库端口;不接受协议、凭据、查询、片段、空白和摘要。格式错误在输入旁显示。不收集仓库凭据。镜像兼容性和拉取权限由 AgentKit 验证。 -FR-3:公共版本选择仍使用固定国内/海外仓库、分页、manifest 检查和刷新。公共目录加载/错误/空状态不能阻止有效手填地址;刷新公共版本不能清除手填内容。移除搜索文案和行为。 -FR-4:POST 在 tag、image 中严格二选一,均携带 revision,兼容现有 tag 调用。写入前拒绝同时提供、均未提供或非法地址。手填地址绝不导致 Studio 请求任意仓库主机。Runtime 手填自定义镜像使用 ArtifactType=image,对应固定公共仓库仍使用 public_image;Worker 仅发送 ImageUrl。现有归属、修订、意图、不确定结果和独立更新保护均保留。 -FR-5:保留取消、迟到结果隔离、防重复提交、键盘/输入法和窄窗口行为。选择来源不能触发更新,仍需显式确认。 - -已手填地址而公共目录失败时,有权限用户仍可确认更新所选组件。先选择公共版本再输入地址会清空公共版本;再次选择公共版本则清空手填内容。非法或双来源请求不写云。更新一项不影响另一项表单或资源。 - -## 设计和契约影响 - -复用 InputWithTailIcon、Select、FormLabel 和现有确认框,不新增模式导航或重复表单。最终来源遵循最近一次显式输入,并清除非活动来源,不使用隐藏优先级。前端领域 API 模块统一校验和最终目标计算,Python 独立执行同样校验。手填请求跳过目录读取及 manifest 验证,由 AgentKit 验证自身镜像拉取。不新增 Studio 任意 URL 请求、鉴权变更、依赖或生成代码。 - -现有 POST 联合类型扩展为 {image, revision},保留 {tag, revision} 兼容。目标校验在持久化和提交前完成。两个来源复用部署锁、乐观重查及组件独立的持久化意图。不改变发布/会话语义和权限策略。手填镜像不自动认定与 MPA Runtime/Worker 兼容;用户显式选择组件并确认完整地址。 - -## 任务和影响文件 - -T-1 / FR-1,3,5:修改 MpaImageUpdates.tsx、CSS 和双语 mpaImages.json,移除已加载版本过滤,实现来源互斥且不联动两个组件。 -T-2 / FR-2,4:在 frontend/src/adk/mpaImages.ts 增加类型化目标校验,在 frontend/server/mpa_images.py 扩展请求解析和镜像提交,现有客户端传输不变。 -T-3 / FR-1..5:先写失败的前后端回归,覆盖双向互斥、两个组件手填更新、目录独立、非法/歧义输入、自定义/公共 ArtifactType、过期/不确定/取消/输入法边界和原标签路径兼容。 -T-4:同步组件双语契约和 frontend/README.md;保留运行中 Studio 缓存资源引用完成构建,记录检查和浏览器证据。 - -## 验证和验收 - -AC-1:两个组件均显示手填地址和公共版本二选一,无搜索控件。切换来源只清除同一组件的另一个输入。 -AC-2:确认框和 API 目标与所选来源一致;目录加载/失败时可手填,Studio 不请求任意仓库。 -AC-3:后端拒绝非法/双来源/无来源,保留原归属/并发保护;仅更新所选云组件。 -AC-4:如实记录 `uv run --extra dev pytest tests/cli/test_mpa_images.py tests/cli/test_frontend_runtime_proxy.py`、修改 Python 的 Ruff/Pyright、`npm --prefix frontend test`、`npm --prefix frontend run build -- --emptyOutDir false`、i18n/资源检查、模拟浏览器键盘/输入法/取消/错误/390px、双语链接/标识符、空白和范围密钥扫描。不授权真实云写入。 - -## 风险和待确认决策 - -已确认手填允许其他仓库,且不由 Studio 请求该主机。公共版本目录仍只使用既有固定公共仓库。私有镜像拉取依赖 AgentKit 现有权限,不在 Studio 新增凭据。标签可变,仍不支持摘要语法。已有沙箱会话可能保留旧镜像。8000 端口的凭据/启动问题仍未解决,本次不重启服务或改选身份。 - -## 评审和批准 - -2026-10-22:已检查现有 UI、解析器、云请求和测试。未安装 review-spec,已直接评审双语等价、API 兼容、任意主机请求隔离、来源优先级、目录独立、归属和不确定结果。用户已明确批准完整方案,包括其他仓库、双向互斥输入及 Runtime/Worker 独立更新。设计评审已通过,T-1 至 T-4、AC-1 至 AC-4 的本地实现已完成,验证如下。未授权提交、推送或真实云写入。 - -## 验证和交付 — 2026-10-22 - -验证范围:feat/from-upstream-main-20261020 工作树中本次手填来源增量,保留先前独立更新功能和无关本地改动。 - -- pass:实现前回归复现手填不受支持(9 个后端失败)和手填/互斥控件缺失(7 个 UI 失败)。 -- pass:`uv run --extra dev pytest tests/cli/test_mpa_images.py tests/cli/test_frontend_runtime_proxy.py tests/integrations/mpa_managed/test_deletion.py -q` 共 215 项通过。两种来源保留归属/过期/不确定状态保护;手填写入不请求仓库。 -- pass:`npm --prefix frontend test`,1389 项 Node 测试、223 项 Vitest 用例。覆盖前后端一致格式样例、双向互斥、目录加载/失败独立性、自定义请求、输入法 Enter、迟到响应/取消。 -- pass:`npm --prefix frontend run build -- --emptyOutDir false`,两个生产包及 TypeScript 通过,仍有既有包体积提示。 -- pass:`npm --prefix frontend run check:i18n`、`npm --prefix frontend run test:webui-assets`,2 个语言/23 个命名空间,177 个文件/695 个引用。 -- pass:仓库 pre-commit 对 frontend/server/mpa_images.py 和 tests/cli/test_mpa_images.py 执行 Ruff check/format,两个文件 Pyright 零诊断。前序记录的未改动 CLI/代理测试基线诊断不属于本轮增量。 -- pass:真实浏览器模拟验证双向来源切换、公共版本键盘选择、目录错误下手填确认/提交、Worker 选择及资源独立保留、非法手填格式、取消/焦点返回及 390px 长地址布局。页面/弹窗宽度等于滚动宽度(342px/310px)。预览:`/tmp/veadk-mpa-manual-image.png`。测试页面与 Vite 已清理。 -- pass:双语需求/契约标识符与相对链接、diff 空白及使用仓库配置的范围密钥扫描。 -- blocked:启用和账号连接 Studio 验证仍需恢复原云凭据启动环境。本次未重启 8000 服务。 -- not_run:未授权云写入测试,因此未执行真实云写入/私有仓库拉取、镜像兼容、发布/应用健康和新沙箱验证。全文件 pre-commit/分支同步留待授权提交时执行,本次未提交或推送。 diff --git a/prd-spec/features/mpa-model-key-discovery/2026-10-10-account-ark-key.md b/prd-spec/features/mpa-model-key-discovery/2026-10-10-account-ark-key.md deleted file mode 100644 index 7cae2d327..000000000 --- a/prd-spec/features/mpa-model-key-discovery/2026-10-10-account-ark-key.md +++ /dev/null @@ -1,69 +0,0 @@ -# Deployment-account Ark key discovery - -[中文](2026-10-10-account-ark-key.zh.md) - -- Change ID: mpa-model-key-discovery; created/revised: 2026-10-10; status: implemented. -- Owner: managed MPA creation; contract: [Studio creation](../../../specs/studio-mpa-creation/README.md), CON-1/CON-16. - -- Successor: [newest-created selection](2026-10-10-latest-created-key.md) supersedes the default multiple-key rejection; the original implementation record below is retained. - -## Background and evidence - -The built-in profile currently resolves `VEADK_MPA_CONFIG_MODEL_AGENT_API_KEY` during local inspection. This requires plaintext configuration and permits an unrelated account's key. Existing `get_ark_token` supports ListApiKeys/GetRawApiKey but defaults to the first key and can include raw errors. A dedicated bounded adapter will reuse the stateless signed-request utility without changing general Agent authentication. ArkCLI documents that ListApiKeys contains masked values and GetRawApiKey retrieves the actual key: https://github.com/volcengine/ark-cli/blob/main/skills/arkcli-profile/references/arkcli-profile-keys.md. The legacy ArkClaw client supports Id/Name without Status and returns Total; current VeADK supports TotalCount. Neither proves that a selected key can invoke the configured model. - -## Goals, non-goals and scenarios - -Automatically read an existing Ark key with deployment AK/SK or rotating STS; retain explicit private CLI configuration. Do not create/rotate keys, change existing Runtimes, modify general Agent behavior, or perform paid inference to test model access. All cloud verification in this change is simulated. If no key exists, the operator creates one in Ark; multiple matches require an exact selector. - -## Requirements - -- FR-1: Built-in local inspection needs no model secret or cloud call and ignores the obsolete model-key environment override. Add `managed.model-key` with mode `explicit` (CLI default) or `ark` (Studio default), optional mutually exclusive api-key-id/api-key-name and project-name=default. Studio selectors use `VEADK_MPA_ARK_API_KEY_ID` / `VEADK_MPA_ARK_API_KEY_NAME`; these are identifiers, not raw keys. -- FR-2: Ark mode only accepts a fresh OpenAI-compatible Ark template in the selected region and rejects model-key/provider/base environment overrides. After STS verification and before resource mutations, read keys using the same refreshed, account-checked credential source. List all pages (100 items, at most 100 pages), validate totals/IDs and deduplicate overlapping IDs. Select an exact ID/name or exactly one candidate; zero/multiple matches fail. A present Status must be Active; legacy absent Status is supported without claiming active-state verification. Use only GetRawApiKey.ApiKey, never masked Key fields. -- FR-3: Each HTTP read has 10-second connect and 30-second read timeouts. Retry connection/timeouts, throttling and availability at most three attempts with asynchronous 0.2/0.5-second delays. Permanent errors and malformed responses fail immediately. Cancellation stops new requests and mutations; an already-running read may finish within its timeout, but its result is discarded. Refresh credentials before each attempt; account changes fail. No CreateApiKey or inference call. -- FR-4: Raw values stay in a copied in-memory profile and the authorized Runtime environment. No values in logs, exceptions, config summaries, task/SQLite records or repository files. Emit allowlisted operation/category diagnostics. Existing pending deployment hashes continue rejecting changed keys/configurations on retry; discovery is repeated and never silently bypasses ambiguity. Runtime-held secrets remain platform-owned. -- FR-5: Keep explicit CLI key/reference/template behavior. Remove only the two obsolete local .env assignments after tests; retain other configuration and file permissions. No frontend fields/build artifacts or dependencies change. - -## Design and affected files - -Add managed/model_key.py; update config.py, studio_profile.py, service.py and diagnostic allowlists. Reuse volcengine_signed_request with local signing state; do not use the global-state signer or generic get_ark_token fallback. The adapter returns a fresh Profile. Deployment permissions already include ark:ListApiKeys and ark:GetRawApiKey; customer roles must grant them. Selection does not certify model activation, IP allowlists or model permissions; these remain deployment prerequisites. Configured=true means local validity, not successful live access. API response types and task stages are unchanged. Maintain paired managed READMEs and Studio component contract. - -## Tasks and acceptance - -T-1 / AC-1 (FR-1/FR-5): failing loader/route tests, then local configuration changes; preserve explicit CLI tests. -T-2 / AC-2 (FR-2/FR-3): adapter tests for STS, pagination, ambiguity, exact selectors, legacy/status responses, malformed/empty responses, transient/permanent failures, account changes and cancellation. -T-3 / AC-3 (FR-4): service ordering and safe injection tests; errors, diagnostics and summaries exclude raw values; retries retain existing hash conflict semantics. -T-4 / AC-4 (FR-5): targeted and full managed/CLI authentication regressions, changed-file Ruff/Pyright, paired document/link/whitespace review, scoped secret scan; safely remove obsolete local assignments. Live cloud and browser checks are not_run: no cloud mutations authorized and no UI change. - -## Risks and recovery - -No/multiple accessible keys and missing read permissions stop at checking; configure a selector or correct Ark permissions. An active key can still lack model access or become revoked; no runtime refresh/rotation is promised. Changed key material can block a pending retry rather than silently replacing its credentials. Existing agents are untouched. Restore explicit private CLI settings to retain manual-key provisioning if required; do not restore plaintext into tracked files. - -## Review and approval - -Direct review (review-spec unavailable): consistent boundaries, no added dependencies, bounded reads/cancellation, redacted provider failures, explicit legacy compatibility and paired-language semantics; no blockers. User approved the automatic-account-key approach in “帮我这么改,然后需要删掉这个.env文件是不是”. This approval does not authorize commit/push/deployment. - -## Verification record - -Date: 2026-10-10. Tested scope: working-tree diff on f7576c40; no commit/push/deployment. - -- pass — test-first loader/route run reproduced three missing-key/default failures (82 existing cases passed); new adapter test collection failed before the module existed. -- pass — `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py tests/cli/test_frontend_deploy_iam.py tests/auth/veauth/test_ark_veauth.py -n 2 -q --no-cov`: 739 passed, including discovery, simulated complete Studio creation, explicit CLI/generic Ark authentication compatibility and IAM permissions. Two workers follow the repository default resource budget. Rerun after .env cleanup also passed. -- pass — `uv tool run --from ruff==0.11.12 ruff check <11 changed Python files>` and `ruff format --check`: all pass. `uv tool run --from pyright pyright --pythonpath .venv/bin/python <11 changed Python files>`: zero errors. A baseline copy of test_iam.py reproduced its nine existing type errors; minimal test-only types/assertions and typed Worker construction resolved them without production IAM changes. -- pass — `uv build --wheel --no-build-isolation --out-dir ` and wheel inspection: new model_key.py and stateless signer included, .env excluded. No release artifacts were added to the checkout. -- pass — Gitleaks v8.24.2 directory scan of all changed/new tracked-scope files with redaction, `git diff --check`, paired-language requirement/acceptance identifiers and added relative links review. -- pass — private .env cleanup removed only VEADK_MPA_CONFIG_MODEL_AGENT_API_KEY and VEADK_MPA_CONFIG_PGPASSWORD assignments, retained other lines and file mode, and verified ignored/untracked status. No values were printed or copied to evidence. -- not_applicable — frontend build/tests and Codex smoke: no frontend, generated JavaScript or harness change. Existing HTTP shapes and deployment Runtime payloads are covered by Python route and simulated provisioning tests. -- not_run — browser/live Ark/model inference/cloud deployment: no UI change and no live cloud verification authorized. Actual account permissions, key availability, model activation and network/IP restrictions remain unverified. -- not_run — all-file pre-commit and full-repository tests: no commit requested; the scoped secret scan, static gates and complete affected suites ran instead. - -Implementation review: no raw secrets in persistence/results/errors; stateless signing avoids cross-request global mutations; refreshed credentials preserve account checks; no extra requests after cancellation; existing explicit model auth stays unchanged. All T-1–T-4 and AC-1–AC-4 are complete with the live-verification limit above. - -## Commit preparation (2026-10-10) - -User authorization: “把当前的修改都提交吧”. Commit only; no push or deployment. Fetched origin/upstream, then ran `git rebase --autostash upstream/main`: the upstream-based branch was already current and the autostash restored successfully. All 172 changed/deleted/untracked file states matched their pre-sync hashes; no production or test files changed during hooks. The user-authorized existing package-lock changes are included; `npm ci --prefix frontend --dry-run --ignore-scripts --offline` passes without changing the lockfile. - -- pass — `uv run --extra dev pre-commit run --all-files`: Ruff check/format, hardcoded-secret detection and YAML-secret scan. -- pass — changed Python production/test files checked with `uv tool run --from pyright pyright --pythonpath .venv/bin/python`: zero errors/warnings. -- pass — frontend full suite rerun after synchronization: 1,377 Node tests and 65 Vitest tests; multi-bot/channel/task regression rerun: 162 passed. Previous build, localization, coverage, asset and isolated browser evidence remains valid because synchronization/hooks changed no source or artifacts. -- fail, pre-existing baseline issue — `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`: 6,933 passed, 53 skipped, four xfailed, one failed, 55 warnings, 412.63 seconds. The sole failure is `tests/frontend/server/runtime_artifacts/test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes`: expected `text/markdown`, received `application/octet-stream`. The writer and test match HEAD byte-for-byte. An isolated `git archive HEAD` baseline using the repository Python environment reproduces the same failure; this host's `mimetypes.guess_type("summary.md")` returns `(None, None)`. Independent current writer suite reproduces it with 48 other tests passing. No unrelated MIME implementation/test change was added to this commit; the full suite is not represented as passing. This does not identify a regression in the submitted feature scope. -- pass — staged whitespace check and secret hooks; local `.env` remains ignored and excluded. Live Ark/Feishu/cloud verification and native browser confirmation/IME limits remain as recorded above; skipped tests do not establish live behavior. diff --git a/prd-spec/features/mpa-model-key-discovery/2026-10-10-account-ark-key.zh.md b/prd-spec/features/mpa-model-key-discovery/2026-10-10-account-ark-key.zh.md deleted file mode 100644 index dc12dbb60..000000000 --- a/prd-spec/features/mpa-model-key-discovery/2026-10-10-account-ark-key.zh.md +++ /dev/null @@ -1,69 +0,0 @@ -# 部署账号方舟 Key 自动发现 - -[English](2026-10-10-account-ark-key.md) - -- 变更 ID:mpa-model-key-discovery;创建/修订:2026-10-10;状态:implemented。 -- 归属:托管 MPA 创建;契约:[Studio 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-1/CON-16。 - -- 后续:[最新创建选择](2026-10-10-latest-created-key.zh.md)取代默认拒绝多个 Key 的规则;下文保留原实现记录。 - -## 背景和证据 - -内置配置目前在本地检查时解析 `VEADK_MPA_CONFIG_MODEL_AGENT_API_KEY`,需要明文配置且可能使用其他账号的 Key。现有 `get_ark_token` 支持 ListApiKeys/GetRawApiKey,但默认选第一把且可能包含原始错误。新增有界适配器复用无全局状态的签名请求工具,不改变通用智能体鉴权。ArkCLI 文档说明 ListApiKeys 返回掩码,GetRawApiKey 获取真实 Key:https://github.com/volcengine/ark-cli/blob/main/skills/arkcli-profile/references/arkcli-profile-keys.md。旧 ArkClaw 客户端支持缺少 Status 的 Id/Name,计数字段为 Total;当前 VeADK 支持 TotalCount。两者都不证明选中的 Key 能调用指定模型。 - -## 目标、非目标和场景 - -使用部署 AK/SK 或轮转 STS 自动读取现有方舟 Key;保留独立 CLI 显式私有配置。不创建/轮转 Key,不修改存量 Runtime 或通用智能体行为,不通过付费推理验证模型权限。本次云验证全部模拟。没有 Key 时由操作者在方舟创建;有多个匹配时要求精确选择。 - -## 需求 - -- FR-1:内置本地检查不需要模型密钥,不调用云接口,忽略旧模型 Key 环境变量覆盖。新增 `managed.model-key`,mode 为 `explicit`(CLI 默认)或 `ark`(Studio 默认);可选互斥的 api-key-id/api-key-name,project-name=default。Studio 选择器使用 `VEADK_MPA_ARK_API_KEY_ID` / `VEADK_MPA_ARK_API_KEY_NAME`,它们是标识而非明文 Key。 -- FR-2:Ark 模式只接受所选地域的全新 OpenAI 兼容方舟模板,拒绝环境覆盖模型 Key/provider/base。STS 核验后、资源变更前,使用相同的刷新及账号核验凭据源读取。遍历全部页面(每页 100,最多 100 页),校验总数/ID并去除重叠 ID。按精确 ID/名称选择,或要求恰好一个候选;零个/多个匹配失败。有 Status 时必须为 Active;兼容缺少 Status 的旧响应,不宣称已核验其状态。仅使用 GetRawApiKey.ApiKey,不使用掩码 Key 字段。 -- FR-3:每次 HTTP 读取连接超时 10 秒、读取超时 30 秒。连接/超时、限流、暂时不可用最多尝试三次,异步间隔 0.2/0.5 秒。永久错误和格式错误立即失败。取消后停止新请求和变更;已运行的读取可在超时内结束,但结果被丢弃。每次尝试前刷新凭据,账号变化失败。不调用 CreateApiKey 或推理。 -- FR-4:明文只在复制的内存 Profile 和授权的 Runtime 环境中,不进入日志、异常、配置摘要、任务/SQLite 记录或仓库文件。输出白名单操作/分类诊断。重试仍由现有部署请求哈希拒绝变更的 Key/配置;重新发现不静默绕过歧义。Runtime 保存的密钥归云平台管理。 -- FR-5:保留独立 CLI 显式 Key/引用 Runtime/模板行为。测试后只删除本地 .env 中两个旧赋值,保留其他配置及文件权限。不改前端字段/构建产物或依赖。 - -## 设计和影响文件 - -新增 managed/model_key.py,修改 config.py、studio_profile.py、service.py 和诊断白名单。复用使用局部签名状态的 volcengine_signed_request,不使用全局状态签名器或通用 get_ark_token 默认选择。适配器返回新的 Profile。部署权限已包含 ark:ListApiKeys 和 ark:GetRawApiKey,客户自管角色须自行授权。选择不证明模型开通、IP 白名单或模型权限,这些仍是部署前置条件。configured=true 表示本地有效,不代表真实访问成功。API 响应类型和任务阶段不变。维护双语 managed README 与 Studio 组件契约。 - -## 任务和验收 - -T-1 / AC-1(FR-1/FR-5):先写失败的加载/路由测试,再改本地配置;保留独立 CLI 测试。 -T-2 / AC-2(FR-2/FR-3):适配器覆盖 STS、分页、歧义、精确选择、旧/状态响应、畸形/空响应、暂时/永久错误、账号变化和取消。 -T-3 / AC-3(FR-4):服务调用顺序及安全注入测试;错误、诊断、摘要不含明文;重试保留原哈希冲突语义。 -T-4 / AC-4(FR-5):定向及全部 managed/CLI 鉴权回归、修改文件 Ruff/Pyright、双语文档/链接/空白审查、定向密钥扫描;安全移除旧本地赋值。真实云端和浏览器为 not_run:未授权云变更,且无 UI 改动。 - -## 风险和恢复 - -没有/多个可读 Key 或读取权限缺失时在 checking 阶段停止;配置选择器或修正方舟权限。Active Key 仍可能无模型访问权限或被撤销;不承诺 Runtime 自动刷新/轮转。Key 明文变化可能阻止未完成任务重试,不静默替换凭据。存量智能体不变。需要手动模式时可使用独立 CLI 私有配置,不把明文恢复进受版本控制的文件。 - -## 审查和批准 - -直接审查(review-spec 不可用):边界一致,无新增依赖,读取/取消有界,云错误脱敏,旧行为兼容和双语语义明确,无阻塞。用户在“帮我这么改,然后需要删掉这个.env文件是不是”批准了按账号自动获取方案。该批准不授权提交/推送/部署。 - -## 验证记录 - -日期:2026-10-10。验证范围:f7576c40 上的工作区差异;未提交/推送/部署。 - -- pass — 测试先行的加载/路由检查复现三个缺失 Key/默认值失败(82 个原用例通过);新增适配器测试在模块存在前收集失败。 -- pass — `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py tests/cli/test_frontend_deploy_iam.py tests/auth/veauth/test_ark_veauth.py -n 2 -q --no-cov`:739 个通过,覆盖发现、模拟完整 Studio 创建、显式 CLI/通用方舟鉴权兼容及 IAM 权限。两个 worker 遵循仓库默认资源预算。.env 清理后重跑也通过。 -- pass — `uv tool run --from ruff==0.11.12 ruff check <11 changed Python files>` 和 `ruff format --check` 全部通过。`uv tool run --from pyright pyright --pythonpath .venv/bin/python <11 changed Python files>` 零错误。test_iam.py 基线副本复现原有九个类型错误;通过最小的测试类型/断言和类型化 Worker 构造修复,未改生产 IAM。 -- pass — `uv build --wheel --no-build-isolation --out-dir ` 与 wheel 检查:包含新增 model_key.py 和无全局状态签名工具,排除 .env。未在工作区新增发布产物。 -- pass — Gitleaks v8.24.2 脱敏扫描全部本次修改/新增的版本控制范围文件、`git diff --check`、双语需求/验收标识及新增相对链接审查。 -- pass — 私有 .env 清理只移除 VEADK_MPA_CONFIG_MODEL_AGENT_API_KEY 和 VEADK_MPA_CONFIG_PGPASSWORD 赋值,保留其他行和文件权限,确认仍被忽略且未跟踪。未打印值或复制到证据。 -- not_applicable — 前端构建/测试和 Codex smoke:无前端、生成 JavaScript 或 harness 改动。原 HTTP 结构和部署 Runtime 请求由 Python 路由及模拟创建测试覆盖。 -- not_run — 浏览器/真实方舟/模型推理/云部署:无 UI 改动,未授权真实云验证。实际账号权限、Key 可用性、模型开通及网络/IP 限制仍未实测。 -- not_run — 全量 pre-commit 和全仓库测试:未要求提交;已执行定向密钥扫描、静态检查及全部受影响测试。 - -实现审查:明文不进入持久化/结果/错误;局部签名避免跨请求全局状态变更;刷新凭据保留账号核验;取消后无后续请求;原显式模型鉴权不变。T-1–T-4 与 AC-1–AC-4 已完成,真实验证限制如上。 - -## 提交准备(2026-10-10) - -用户授权:“把当前的修改都提交吧”。仅提交,不推送或部署。已 fetch origin/upstream,再执行 `git rebase --autostash upstream/main`:上游基础分支已同步,自动暂存成功恢复。172 个修改/删除/未跟踪文件状态与同步前哈希一致,检查期间未改生产或测试文件。按用户授权包含已有 package-lock 改动;`npm ci --prefix frontend --dry-run --ignore-scripts --offline` 通过,未修改锁文件。 - -- pass — `uv run --extra dev pre-commit run --all-files`:Ruff 检查/格式化、硬编码凭据扫描及 YAML 密钥扫描。 -- pass — 已改 Python 生产/测试文件执行 `uv tool run --from pyright pyright --pythonpath .venv/bin/python`:零错误/警告。 -- pass — 同步后重跑前端完整测试:1,377 个 Node 测试及 65 个 Vitest 测试;重跑多 Bot/渠道/任务回归:162 个通过。同步及检查未改变源码/产物,此前构建、国际化、覆盖率、产物及隔离浏览器证据仍有效。 -- fail,基线已有问题 — `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`:6,933 个通过、53 个跳过、四个 xfailed、一个失败、55 个警告,耗时 412.63 秒。唯一失败为 `tests/frontend/server/runtime_artifacts/test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes`:期望 `text/markdown`,实际 `application/octet-stream`。写入实现及测试与 HEAD 逐字节一致,使用仓库 Python 环境运行隔离 `git archive HEAD` 基线可复现;本机 `mimetypes.guess_type("summary.md")` 返回 `(None, None)`。当前 writer 单独测试也复现,另 48 个用例通过。本提交未加入无关 MIME 实现/测试变更,不将全仓检查表述为通过;该结果未发现提交功能范围的回归。 -- pass — 暂存差异空白检查及密钥钩子;本地 `.env` 保持忽略,未包含到提交。真实方舟/飞书/云验证及浏览器原生确认/IME 限制保持前述记录,跳过的测试不证明真实行为。 diff --git a/prd-spec/features/mpa-model-key-discovery/2026-10-10-latest-created-key.md b/prd-spec/features/mpa-model-key-discovery/2026-10-10-latest-created-key.md deleted file mode 100644 index f9c4d8546..000000000 --- a/prd-spec/features/mpa-model-key-discovery/2026-10-10-latest-created-key.md +++ /dev/null @@ -1,64 +0,0 @@ -# Select the newest existing Ark key - -[中文](2026-10-10-latest-created-key.zh.md) - -- Change ID: mpa-latest-created-key; created/revised: 2026-10-10; status: implemented. -- Predecessor: [account-key discovery](2026-10-10-account-ark-key.md). -- Component: [Studio creation](../../../specs/studio-mpa-creation/README.md), CON-16. - -## Background and evidence - -`resolve_model_key` currently rejects multiple candidates without an exact selector. The user requests automatic selection of the latest-created key to preserve one-click creation. Existing pagination, account checks, status filtering and secret handling already belong to the discovery adapter. A read-only metadata probe could not load local deployment credentials; the live timestamp shape is unverified. The adapter will accept creation metadata explicitly and fail if it cannot establish which key is newest; list order is not evidence of creation order. - -## Goals, non-goals and scenarios - -Select the newest creation time among eligible keys in the configured project without extra configuration. Preserve explicit ID/name selection, generic Agent authentication, CLI explicit mode and existing Runtime credentials. Do not create keys, perform inference, persist raw keys, or migrate existing tasks. - -## Requirements and design - -- FR-1: With no selector and multiple eligible keys, read all existing bounded pages and select the maximum creation instant. Accept `CreateTime` (or `CreatedAt` / `CreationTime` compatibility fields), positive finite Unix seconds/milliseconds including numeric strings, or timezone-qualified ISO 8601. Normalize to UTC. Missing/invalid metadata on any eligible candidate fails before GetRawApiKey. Exactly one eligible key needs no timestamp. -- FR-2: Creation-time ties use the lexicographically greatest string ID, independent of page order; this is deterministic tie-breaking, not inferred chronology. Explicit ID/name selectors retain precedence and require exactly one match, including failure on duplicate names. Inactive keys are filtered before timestamp comparison; legacy absent Status remains supported without claiming active-state verification. -- FR-3: Account verification, refreshing credentials, cancellation, bounded retry, safe diagnostics and Runtime-only raw-key injection remain unchanged. Retry rediscovers keys; if a newer key appears after partial deployment, existing payload hashes may reject the change rather than silently replacing credentials. Existing agents are untouched. - -Affected files: `managed/model_key.py`, `test_model_key.py`, paired managed READMEs and Studio creation specs. No API shape, frontend, dependency or schema change. Timestamp parsing is an adapter concern; configuration selectors remain optional. - -## Tasks, tests and acceptance - -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| FR-1 | T-1 | AC-1: newest selected across pages and timestamp formats; invalid metadata fails without raw lookup | Adapter regression tests, failing before implementation | -| FR-2 | T-2 | AC-2: explicit override, inactive filtering and tie ordering preserved | Adapter tests with reversed ordering, newer disabled key and older explicit selection | -| FR-3 | T-3 | AC-3: affected complete regression and static/security gates pass; documentation agrees | Managed/CLI/auth suites, Ruff, Pyright, Gitleaks, paired links and whitespace | - -## Risks and recovery - -Newest does not certify model permissions, IP restrictions or inference eligibility. Creation timestamps returned by the real account remain unverified; unsupported metadata fails safely and an explicit selector remains available. A key created during a pending task can change its payload and trigger the existing hash conflict. No implicit rotation or retry fallback is introduced. - -## Review and approval - -Direct review because review-spec is unavailable: bounded parsing, deterministic ordering, selector compatibility, secret isolation, cancellation and bilingual equivalence checked; no blockers. User approval: “账号下有多个key,选最新创建的那个吧”. This authorizes this selection change, not commit/push/deployment. The predecessor retains its historical selection decision; CON-16 and the maintained user docs describe the successor. - -## Verification record - -2026-10-10; working-tree scope on f7576c40. Implementation and regression results follow. Live metadata probe: blocked before a provider request by unavailable local deployment credentials; no raw key was read and no cloud resource was changed. Frontend/browser gates: not_applicable, no UI change. Full pre-commit and repository regression: not_run, no commit requested; complete affected suites and changed-file gates required. - - -- pass — test-first adapter run: 35 failures reproduced the missing newest-selection/error behavior, with 29 unchanged cases passing; after implementation, all 64 adapter cases passed. -- pass — `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py tests/cli/test_frontend_deploy_iam.py tests/auth/veauth/test_ark_veauth.py -n 2 -q --no-cov --tb=short`: 773 passed, rerun after the final type-narrowing repair. Two workers follow repository resource guidance; ten existing deprecation warnings remain. -- pass — `uv tool run --from ruff==0.11.12 ruff check` and `ruff format --check` on model_key.py and test_model_key.py; `uv tool run --from pyright pyright --pythonpath .venv/bin/python` on both files: zero errors after replacing a runtime-only type check with explicit type narrowing. -- pass — Gitleaks v8.24.2 redacted directory scan of all 19 changed/new source/document files, paired requirement/acceptance/task identifiers and relative design links, and `git diff --check`. -- blocked — live account timestamp verification: local deployment credentials unavailable; no ListApiKeys result or raw key was obtained. The API field aliases and timestamp formats are isolated, tested adapter inputs, not claimed live observations. -- not_applicable — frontend build/browser and Codex smoke: no UI/generated JavaScript/harness changes. -- not_run — all-file pre-commit/full repository suite: no commit requested; complete affected suites and scoped gates passed. - -Implementation review: all eligible timestamps are evaluated, inactive records do not affect selection, explicit selection skips time requirements, and ties are independent of list order. Error text never includes provider metadata or raw values. T-1–T-3 / AC-1–AC-3 are complete within the stated live-verification boundary. - -## Commit preparation (2026-10-10) - -User authorization: “把当前的修改都提交吧”. Commit only; no push or deployment. Fetched origin/upstream, then ran `git rebase --autostash upstream/main`: the upstream-based branch was already current and the autostash restored successfully. All 172 changed/deleted/untracked file states matched their pre-sync hashes; no production or test files changed during hooks. The user-authorized existing package-lock changes are included; `npm ci --prefix frontend --dry-run --ignore-scripts --offline` passes without changing the lockfile. - -- pass — `uv run --extra dev pre-commit run --all-files`: Ruff check/format, hardcoded-secret detection and YAML-secret scan. -- pass — changed Python production/test files checked with `uv tool run --from pyright pyright --pythonpath .venv/bin/python`: zero errors/warnings. -- pass — frontend full suite rerun after synchronization: 1,377 Node tests and 65 Vitest tests; multi-bot/channel/task regression rerun: 162 passed. Previous build, localization, coverage, asset and isolated browser evidence remains valid because synchronization/hooks changed no source or artifacts. -- fail, pre-existing baseline issue — `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`: 6,933 passed, 53 skipped, four xfailed, one failed, 55 warnings, 412.63 seconds. The sole failure is `tests/frontend/server/runtime_artifacts/test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes`: expected `text/markdown`, received `application/octet-stream`. The writer and test match HEAD byte-for-byte. An isolated `git archive HEAD` baseline using the repository Python environment reproduces the same failure; this host's `mimetypes.guess_type("summary.md")` returns `(None, None)`. Independent current writer suite reproduces it with 48 other tests passing. No unrelated MIME implementation/test change was added to this commit; the full suite is not represented as passing. This does not identify a regression in the submitted feature scope. -- pass — staged whitespace check and secret hooks; local `.env` remains ignored and excluded. Live Ark/Feishu/cloud verification and native browser confirmation/IME limits remain as recorded above; skipped tests do not establish live behavior. diff --git a/prd-spec/features/mpa-model-key-discovery/2026-10-10-latest-created-key.zh.md b/prd-spec/features/mpa-model-key-discovery/2026-10-10-latest-created-key.zh.md deleted file mode 100644 index 1885e3a02..000000000 --- a/prd-spec/features/mpa-model-key-discovery/2026-10-10-latest-created-key.zh.md +++ /dev/null @@ -1,64 +0,0 @@ -# 选择最新创建的已有方舟 Key - -[English](2026-10-10-latest-created-key.md) - -- 变更 ID:mpa-latest-created-key;创建/修订:2026-10-10;状态:implemented。 -- 前序:[按账号发现 Key](2026-10-10-account-ark-key.zh.md)。 -- 组件:[Studio 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-16。 - -## 背景和证据 - -`resolve_model_key` 当前在没有精确选择器且存在多个候选时拒绝创建。用户要求自动选择最新创建的 Key,保持一键创建。分页、账号核验、状态过滤和密钥处理已由发现适配器负责。只读元数据探测无法加载本地部署凭据,真实时间字段结构尚未验证。适配器将明确接受创建时间元数据,无法判断最新时失败;列表顺序不能证明创建顺序。 - -## 目标、非目标和场景 - -无需额外配置,按配置项目内候选 Key 的创建时间选最新。保留显式 ID/名称选择、通用智能体鉴权、CLI 显式模式和现有 Runtime 凭据。不创建 Key、不调用推理、不持久化明文、不迁移现有任务。 - -## 需求和设计 - -- FR-1:没有选择器且存在多个候选时,遍历现有有界分页,选择最大创建时刻。接受 `CreateTime`(兼容 `CreatedAt` / `CreationTime`)、正且有限的 Unix 秒/毫秒(包括数字字符串)或带时区的 ISO 8601,统一为 UTC。任何候选的元数据缺失/无效时,在 GetRawApiKey 前失败。恰好一个候选无需时间字段。 -- FR-2:创建时间相同时选择字符串 ID 字典序最大者,与分页顺序无关;该规则用于稳定打破平局,不推断创建先后。显式 ID/名称优先且仍要求恰好一个匹配,包括重名时报错。非 Active Key 在时间比较前过滤;兼容缺少 Status 的旧响应,不宣称已核验其状态。 -- FR-3:账号核验、凭据刷新、取消、有界重试、安全诊断和只向 Runtime 注入明文保持不变。重试重新发现 Key;部分部署后出现新 Key 时,现有请求哈希可能拒绝变化,不静默替换凭据。存量智能体不变。 - -影响文件:`managed/model_key.py`、`test_model_key.py`、双语 managed README 和 Studio 创建规格。不改 API 结构、前端、依赖或数据结构。时间解析属于适配器职责,选择器仍可选。 - -## 任务、测试和验收 - -| 需求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| FR-1 | T-1 | AC-1:跨页和不同时间格式选最新;异常元数据不读取明文 | 适配器回归测试,实现前失败 | -| FR-2 | T-2 | AC-2:保留显式覆盖、状态过滤和同时间稳定排序 | 反转列表顺序、较新禁用 Key、显式选较旧 Key 的适配器测试 | -| FR-3 | T-3 | AC-3:全部受影响回归与静态/安全检查通过,文档一致 | managed/CLI/auth 测试、Ruff、Pyright、Gitleaks、双语链接和空白检查 | - -## 风险和恢复 - -最新 Key 不证明模型权限、IP 限制或推理可用性。真实账号返回的创建时间仍未验证;不支持的元数据明确失败,可显式指定选择器恢复。未完成任务期间创建新 Key 可能改变请求并触发现有哈希冲突。不引入隐式轮转或重试回退。 - -## 审查和批准 - -review-spec 不可用,直接审查:确认有界解析、稳定排序、选择器兼容、密钥隔离、取消及双语等价,无阻塞。用户批准:“账号下有多个key,选最新创建的那个吧”。该批准授权选择规则改动,不授权提交/推送/部署。前序保留历史选择决策,CON-16 和维护中的用户文档描述后续规则。 - -## 验证记录 - -2026-10-10;f7576c40 上的工作区范围。实现和回归结果如下。真实元数据探测:blocked,本地部署凭据不可用,在云请求前失败;未读取明文或变更云资源。前端/浏览器:not_applicable,无 UI 改动。全量 pre-commit 和全仓库回归:not_run,未要求提交;须执行全部受影响测试和修改文件检查。 - - -- pass — 测试先行的适配器运行:35 个失败复现缺少最新选择/错误行为,29 个原用例通过;实现后全部 64 个适配器用例通过。 -- pass — `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py tests/cli/test_frontend_deploy_iam.py tests/auth/veauth/test_ark_veauth.py -n 2 -q --no-cov --tb=short`:773 个通过,最终类型收窄修复后重跑。两个 worker 遵循仓库资源规则,仍有十条原有弃用警告。 -- pass — 对 model_key.py 和 test_model_key.py 执行 `uv tool run --from ruff==0.11.12 ruff check`、`ruff format --check`;两文件执行 `uv tool run --from pyright pyright --pythonpath .venv/bin/python`:使用显式类型收窄替代仅运行时类型判断后,零错误。 -- pass — Gitleaks v8.24.2 脱敏目录扫描全部 19 个本次修改/新增的源码及文档、双语需求/验收/任务标识与设计相对链接、`git diff --check`。 -- blocked — 真实账号时间验证:本地部署凭据不可用,未获得 ListApiKeys 结果或明文 Key。字段别名和时间格式属于经过隔离测试的适配器输入,不宣称真实观测。 -- not_applicable — 前端构建/浏览器和 Codex smoke:无 UI、生成 JavaScript 或 harness 改动。 -- not_run — 全量 pre-commit/全仓库测试:未要求提交;全部受影响测试和定向检查已通过。 - -实现审查:比较全部候选时间,非 Active 记录不影响选择,显式选择无需时间字段,同时间排序不依赖列表顺序。错误文本不包含云元数据或明文。T-1–T-3 / AC-1–AC-3 在所述真实验证边界内完成。 - -## 提交准备(2026-10-10) - -用户授权:“把当前的修改都提交吧”。仅提交,不推送或部署。已 fetch origin/upstream,再执行 `git rebase --autostash upstream/main`:上游基础分支已同步,自动暂存成功恢复。172 个修改/删除/未跟踪文件状态与同步前哈希一致,检查期间未改生产或测试文件。按用户授权包含已有 package-lock 改动;`npm ci --prefix frontend --dry-run --ignore-scripts --offline` 通过,未修改锁文件。 - -- pass — `uv run --extra dev pre-commit run --all-files`:Ruff 检查/格式化、硬编码凭据扫描及 YAML 密钥扫描。 -- pass — 已改 Python 生产/测试文件执行 `uv tool run --from pyright pyright --pythonpath .venv/bin/python`:零错误/警告。 -- pass — 同步后重跑前端完整测试:1,377 个 Node 测试及 65 个 Vitest 测试;重跑多 Bot/渠道/任务回归:162 个通过。同步及检查未改变源码/产物,此前构建、国际化、覆盖率、产物及隔离浏览器证据仍有效。 -- fail,基线已有问题 — `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`:6,933 个通过、53 个跳过、四个 xfailed、一个失败、55 个警告,耗时 412.63 秒。唯一失败为 `tests/frontend/server/runtime_artifacts/test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes`:期望 `text/markdown`,实际 `application/octet-stream`。写入实现及测试与 HEAD 逐字节一致,使用仓库 Python 环境运行隔离 `git archive HEAD` 基线可复现;本机 `mimetypes.guess_type("summary.md")` 返回 `(None, None)`。当前 writer 单独测试也复现,另 48 个用例通过。本提交未加入无关 MIME 实现/测试变更,不将全仓检查表述为通过;该结果未发现提交功能范围的回归。 -- pass — 暂存差异空白检查及密钥钩子;本地 `.env` 保持忽略,未包含到提交。真实方舟/飞书/云验证及浏览器原生确认/IME 限制保持前述记录,跳过的测试不证明真实行为。 diff --git a/prd-spec/features/mpa-name-preflight/2026-10-13-runtime-name-and-workspace-labels.md b/prd-spec/features/mpa-name-preflight/2026-10-13-runtime-name-and-workspace-labels.md deleted file mode 100644 index c5f9552ce..000000000 --- a/prd-spec/features/mpa-name-preflight/2026-10-13-runtime-name-and-workspace-labels.md +++ /dev/null @@ -1,74 +0,0 @@ -# MPA name preflight and readable business Workspace names - -[中文版](2026-10-13-runtime-name-and-workspace-labels.zh.md) - -- Change ID: `mpa-name-preflight` -- Created/revised: 2026-10-13 -- Status: implemented -- Owner contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), CON-20 -- Predecessor: [independent business Workspace](../mpa-agent-pg-workspace/2026-10-13-independent-business-workspace.md) - -## Background and evidence - -The form validates Runtime name characters/length but does not query cloud availability. Service preparation reaches PG/network before `ensure_worker` discovers foreign same-name templates. `RuntimeCloud.list_runtimes` expands every Runtime detail for deletion; reusing it for typing preflight would be unnecessarily expensive. A lightweight paginated name lookup is needed. New per-agent PG names currently use a hash, making console lookup difficult. - -[AIDAP CreateWorkspace](https://docs.volcengine.com/docs/87275/2105881) documents names of 2–128 characters, beginning with a letter or Chinese character. Runtime names allow 4–64 ASCII letters, digits, underscores and hyphens. Proposed `mpa_agent_` is at most 74 characters and safely starts with a letter. New dedicated business databases use `aidb`; existing database names remain pinned. - -## Goals, scenarios and requirements - -- FR-1: Check the selected deployment provider/account/region before starting a fresh MPA task. A foreign same-name Runtime, worker template or target business Workspace blocks creation with an explicit localized name-conflict message. Do not return cloud credentials or foreign resource details. -- FR-2: The first wizard Next action checks name availability; final POST and provisioning enforce checks independently of the frontend. Query errors fail closed with a retryable check error, not an available/empty result. Pagination, timeout, cancellation, IME and stale-response behavior must be covered. -- FR-3: Same-request retry may reuse its own resources when identity/binding is proven; foreign or ambiguous ownership fails closed. Existing pending/registered resources retain their bindings and names. Current owner authentication and provider selection remain server-owned. -- FR-4: New independent business Workspaces use `mpa_agent_`. The business database is `aidb` for new dedicated requests; existing requests retain their recorded name. Persist/preserve selected Workspace name with bootstrap/registry identity; never rename an existing Workspace or switch a business endpoint during retry. Explicit/manual/shared profiles remain compatible. -- FR-5: Generic-agent creation, shared admin PG/network/APIG, Runtime business schemas, OV provisioning and resource deletion behavior are unchanged. - -## Design and contract assessment - -Add a Studio-authorized read-only name check and lightweight Runtime pagination that retrieves details only for matching candidates when ownership requires verification. Reuse `WorkerCloud.find` and AIDAP `find`; never use PostgreSQL connection preparation as a read-only preflight because it may change endpoint settings. Check current in-flight local creation requests as well as cloud names and recheck before purchase. Retain platform uniqueness as the final concurrent-creation boundary; a read-then-create check alone cannot provide atomic exclusion across independent Studio hosts. Any additional shared reservation protocol needs a reviewed contract before implementation. - -MPA inputs retain existing validation, while prefixed Workspace naming accommodates digit-leading Runtime names. Same-name foreign retained PG resources block automatic reuse after Runtime deletion; explain the conflict rather than silently sharing them. Registered Workspace IDs, ownership tags, endpoint checks and stable internal identity remain authoritative. Existing hashed per-agent Workspaces use the stored bootstrap name or a verified registered name, not a newly derived name. Private secrets remain server-only. - -Affected files: managed Runtime/worker lookup consumers, name-check helper, PG bootstrap/config/service and runner error mapping; BFF creation endpoints/tasks, frontend API types/dialog/locales, targeted Python/React tests, bilingual maintained specs/docs and generated WebUI assets. The sibling MPA repository needs no change. - -## Tasks and acceptance - -- T-1 / FR-1,2,3 / AC-1: write failing tests for foreign collisions, valid ownership retry, pagination, unavailable checks and stale/cancelled UI responses; implement the read-only endpoint and early server enforcement. -- T-2 / FR-4 / AC-2: naming tests for digits/case/64-character names, existing hashed bootstrap/registry bindings, restart and uncertain purchase; use `aidb` only in verified dedicated Workspaces and avoid existing resource rebinding. -- T-3 / FR-5 / AC-3: generic-agent/deletion regression, paired docs/assets and review. No cloud resources created by tests. - -Run targeted tests via `uv run --extra dev pytest`, then the existing managed/deployment regression with two workers, Ruff/Pyright for changed Python, `npm --prefix frontend test`, build/i18n/asset checks, and an isolated real browser. No live purchase, commit, push or deployment is authorized. Results are recorded below after implementation. - -## Risks and review record - -Cloud listing is eventually consistent; preflight cannot guarantee cross-host atomicity. Fail clearly on a late platform name collision. Additional discovery requires the deployment credentials' existing Runtime/tool/AIDAP read permissions. Retained old Workspace names and failed-task resources must not be silently renamed/adopted. `review-spec` is unavailable; direct review has confirmed ownership boundaries and proposed compatibility requirements. User approval is recorded in the amendment below; final implementation review is recorded at delivery. The user requested pre-creation duplicate detection and asked to replace the Workspace hash with the MPA name on 2026-10-13; a concrete scope/naming question has been presented as required by AGENTS.md. Implementation follows the approved amendment below. - -## Approved aidb and request compatibility amendment - -User approved all three changes with “改成aidb吧 … 这个也一起改了?” on 2026-10-13. Direct design review replaces the unavailable review-spec skill; no design blockers remain. New config/forms explicitly carry `businessDatabaseName: aidb`, requiring `businessWorkspaceMode: per-agent`. Omission preserves the preceding hashed database/Workspace contract and saved request fingerprints. CLI/YAML opt in with `managed.postgres.business-database-name: aidb`. Named Workspaces persist layout in bootstrap intent and name/database choices in registry metadata. Existing two-element bootstrap configurations and old registered hashed names retain their original layout even on a new-form retry. - -AIDAP precreates `aidb`. Before marking it as this agent's database, verify dedicated Workspace identity, DB owner, empty non-system/non-extension relations, no prior Runtime/ready binding, and a transaction advisory lock. A mismatched marker, nonempty initial database or missing registered database fails closed. Persist initialization intent before marking; matching markers make retry idempotent. Runtime validation, PGDATABASE, token/meta seeding and readiness all use the same selected database. Shared admin DB remains mpa_admin_db. - -The BFF checks names before creating a task and uses a private task name-scope snapshot (verified provider/account/region) to reject same-host concurrent same-name requests in the SQLite transaction; legacy scope-less active requests are conservatively checked within their provider/region. No failed-task reservation blocks later tasks. The runner verifies the snapshotted deployment account before new-layout side effects and checks names again. Read-only checks are bounded and return only availability/error codes. General-agent APIs remain unchanged. Cross-host read/create races still use cloud enforcement and ownership validation; no distributed reservation guarantee is claimed. - -Implementation follows this approved scope; actual verification is recorded below. No cloud purchase, commit, push or deployment is authorized. - -## Implementation review and verification (2026-10-13) - -T-1–T-3 and AC-1–AC-3 are implemented. Direct review confirmed fail-closed ownership checks, database marker/empty-data guards, retry compatibility and provider/account boundaries. New-layout web requests must use the server-derived owner/request identity; a supplied different agent ID is rejected before task acceptance. This prevents claiming another request's partial Workspace by copying its internal ID. Legacy requests retain their existing explicit-ID contract. - -Tested scope: the working diff on `71175c68636dc5b951e9804eb292d85402511e68`, including earlier independent PG and automatic OV work already in this workspace. Regression-first checks initially failed (12 Python and 2 React cases); subsequent implementation passes the affected checks. No unrelated user changes were reverted. - -| Check | Result | Evidence / limits | -| --- | --- | --- | -| Managed/deployment Python regression | pass | `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py tests/integrations/test_mpa_tool.py tests/integrations/test_mpa_skill_space.py tests/integrations/test_ve_faas_deploy_image_key_auth.py -q --no-cov`: 978 passed; two workers selected for local resources. | -| Changed Python static checks | pass | Ruff 0.11.12 and Pyright against `.venv/bin/python`, all changed Python files. Repository-local tools were unavailable; temporary `uvx` tools were used without global installation. | -| Frontend tests | pass | `npm --prefix frontend test`: Node and React suites, including duplicate Next/POST, pending-check cancellation and late-result suppression, existing generic-agent/deletion tests. | -| Frontend build | pass | `npm --prefix frontend run build`; generated WebUI assets synchronized. | -| Localized strings and asset references | pass | `npm --prefix frontend run check:i18n` and `npm --prefix frontend run test:webui-assets`: 2 locales / 21 namespaces and 113 assets / 350 references. | -| Browser flow | pass | Actual React dialog with isolated fake HTTP responses: occupied/free name, unavailable query, late POST conflict with editable name, old shared request recovery, BytePlus manual OV and 390×844 layout. No cloud operation dispatched; temporary fixture/server removed. | -| Secret scanning / diff whitespace | pass | Repository-configured Gitleaks over changed files, redacted output; `git diff --check`. | -| Live cloud creation and real PostgreSQL integration | not_run | Not authorized for this task. Provider purchase, DB claim SQL and readiness still require an isolated live acceptance run. | -| Harness/process smoke gates | not_applicable | No harness/sidecar or process contract changed. | -| Commit / pre-commit gate | not_run | No commit authorized; pre-commit and branch synchronization remain required before a later commit. | - -No remaining local review blockers. Cross-host listing/purchase races remain bounded by cloud enforcement and ownership validation; no distributed lock guarantee is claimed. Existing data is not migrated. Restart Studio and start a fresh request to use the new layout. diff --git a/prd-spec/features/mpa-name-preflight/2026-10-13-runtime-name-and-workspace-labels.zh.md b/prd-spec/features/mpa-name-preflight/2026-10-13-runtime-name-and-workspace-labels.zh.md deleted file mode 100644 index ff137bbb7..000000000 --- a/prd-spec/features/mpa-name-preflight/2026-10-13-runtime-name-and-workspace-labels.zh.md +++ /dev/null @@ -1,74 +0,0 @@ -# MPA 名称预检查与可读业务 Workspace 名称 - -[English](2026-10-13-runtime-name-and-workspace-labels.md) - -- Change ID:`mpa-name-preflight` -- 创建/修订:2026-10-13 -- 状态:implemented -- 归属契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),CON-20 -- 前置设计:[独立业务 Workspace](../mpa-agent-pg-workspace/2026-10-13-independent-business-workspace.zh.md) - -## 背景与证据 - -表单校验 Runtime 名称字符和长度,但未查询云端是否可用。创建流程准备 PG/网络后,`ensure_worker` 才发现其他资源占用同名模板。`RuntimeCloud.list_runtimes` 为删除检查逐个展开全部 Runtime 详情,不适合输入时查重,应增加轻量分页名称查询。新独立 PG 当前以哈希命名,在控制台不便查找。 - -[AIDAP CreateWorkspace](https://docs.volcengine.com/docs/87275/2105881)规定名称为 2–128 字符,以字母或中文开头。Runtime 名称允许 4–64 位 ASCII 字母、数字、下划线和连字符。建议的 `mpa_agent_` 最长 74 字符,且以字母开头。新独立业务库使用 `aidb`;已有数据库名称保持固定。 - -## 目标、场景与需求 - -- FR-1:启动新 MPA 任务前检查所选部署提供商/账号/地域。其他智能体占用同名 Runtime、沙箱模板或目标业务 Workspace 时,明确以本地化重名消息阻止创建,不返回云凭据及其他资源详情。 -- FR-2:向导首次“下一步”检查名称,最终 POST 和后台编排独立校验,不依赖前端。查询失败明确阻止并允许重新检查,不伪装成名称可用/空数据。覆盖分页、超时、取消、IME 和过期响应。 -- FR-3:相同请求重试在身份/绑定可证明时复用自己的资源;其他归属或不明确归属明确失败。已有任务/登记资源保留原绑定和名称。用户认证和提供商选择仍由服务端控制。 -- FR-4:新独立业务 Workspace 使用 `mpa_agent_<手填 Runtime 名称>`。新独立请求业务库为 `aidb`,已有请求保留登记的名称。引导/登记身份保存并保持已选 Workspace 名称,不重命名已有 Workspace,不在重试中切换端点。兼容显式/手动/共享配置。 -- FR-5:不改通用智能体创建、共享管理 PG/网络/APIG、Runtime 业务表、OV 创建及资源删除行为。 - -## 设计与契约评估 - -增加 Studio 授权的只读名称检查和轻量 Runtime 分页,仅在同名候选需要验证归属时读取详情。复用 `WorkerCloud.find` 和 AIDAP `find`;PG 连接准备可能修改端点设置,不能用作只读预检查。同时检查本地进行中的创建请求与云资源名称,在购买前复核。平台唯一性仍是并发创建的最终边界,先读后创建不能保证独立 Studio 主机间原子互斥;额外共享占位协议需先审查契约再实现。 - -MPA 输入沿用原校验,Workspace 加前缀兼容数字开头的 Runtime 名称。删除 Runtime 后若同名旧 PG 被其他归属占用,应明确冲突,不自动共享。Workspace ID、归属标签、端点校验和内部稳定 ID 仍是权威绑定。已有哈希 Workspace 使用引导文件保存名称或已核验登记名称,不改用新推导名称。密钥仅留在服务端。 - -受影响文件:受管 Runtime/worker 查询调用、名称检查模块、PG 引导/配置/service 与 runner 错误映射;BFF 创建接口/tasks、前端 API 类型/弹窗/文案、定向 Python/React 测试、双语维护契约/说明和 WebUI 构建资源。兄弟 MPA 仓库不需修改。 - -## 任务与验收 - -- T-1 / FR-1,2,3 / AC-1:先写其他归属重名、合法重试、分页、检查失败与过期/取消响应的失败测试,再实现只读接口和服务端提前拦截。 -- T-2 / FR-4 / AC-2:覆盖数字开头/大小写/64 字符名称、旧哈希引导/登记绑定、重启和购买结果未知;仅在已核验独立 Workspace 使用 `aidb`,不切换已有资源绑定。 -- T-3 / FR-5 / AC-3:通用智能体/删除回归,双语文档/资源及审查。测试不创建云资源。 - -通过 `uv run --extra dev pytest` 执行定向测试,再使用两个 worker 执行已有 managed/部署回归;对改动 Python 跑 Ruff/Pyright;执行 `npm --prefix frontend test`、构建、国际化、资源引用检查及隔离真实浏览器验证。不授权真实购买、提交、推送或部署。实现后的结果在下方记录。 - -## 风险与审查记录 - -云列表存在最终一致性,预检查不能保证跨主机原子性;后续平台重名拒绝须明确展示。新增查询依赖部署凭据现有 Runtime/tool/AIDAP 读取权限。旧 Workspace 名称和失败任务资源不能静默重命名/接管。`review-spec` 不可用,已直接核对归属边界及拟定兼容要求;用户批准见下方补充,最终实现审查在交付时记录。用户于 2026-10-13 要求创建前查重,并询问用 MPA 名称替代 Workspace 哈希;已按 AGENTS.md 提交具体范围/命名确认。实现按下方已批准补充执行。 - -## 已批准的 aidb 与请求兼容补充 - -用户于 2026-10-13 通过“改成aidb吧 … 这个也一起改了?”批准三项修改。直接设计审查替代不可用的 review-spec,无设计阻塞。新配置/表单显式携带 `businessDatabaseName: aidb`,要求 `businessWorkspaceMode: per-agent`。省略时保留前一版哈希库名/Workspace 契约及已保存请求指纹。CLI/YAML 可通过 `managed.postgres.business-database-name: aidb` 启用。名称化 Workspace 的引导意图保留布局标记,注册元数据保留 Workspace 名和数据库选择。旧二元素引导配置及已登记哈希名称,即使用新表单重试也保持原布局。 - -AIDAP 默认预建 `aidb`。标记为该智能体业务库前,核验独立 Workspace 身份、数据库属主、无非系统/非扩展关系对象、无已有 Runtime/ready 绑定,并持有事务 advisory lock。标记不匹配、初始库非空或登记库缺失均明确失败。写标记前保存初始化意图,匹配标记使重试幂等。Runtime 校验、PGDATABASE、token/meta 初始化及就绪检查统一使用已选数据库。共享管理库仍为 mpa_admin_db。 - -BFF 在创建任务前查重,通过私有任务名称范围快照(已核验提供商/账号/地域)在 SQLite 事务中阻止本主机并发同名请求;旧无范围的活跃请求按其提供商/地域保守检查。失败任务占位不会阻塞后续任务。Runner 在新布局副作用前核验快照部署账号,并再次查重。只读检查有超时,只返回是否可用/错误码。通用智能体 API 不变。跨主机先查后创建仍依赖云端约束与归属校验,不宣称分布式占位保证。 - -实现遵循已批准范围;实际验证记录如下。不授权云购买、提交、推送或部署。 - -## 实现审查与验证(2026-10-13) - -T-1–T-3 与 AC-1–AC-3 已实现。直接审查确认了归属检查的失败关闭行为、数据库标记/空库保护、重试兼容性及提供商/账号边界。新布局 Web 请求必须使用服务端按用户/请求生成的内部身份;传入不同 agent ID 会在受理任务前拒绝,防止复制其他请求的内部 ID 接管其部分创建的 Workspace。旧请求保留原显式 ID 契约。 - -验证范围:基于 `71175c68636dc5b951e9804eb292d85402511e68` 的工作区差异,包括工作区此前已有的独立 PG 和自动 OV 改动。先写回归测试时有 12 项 Python、2 项 React 失败,随后实现通过受影响检查。没有撤回无关用户改动。 - -| 检查 | 结果 | 证据 / 限制 | -| --- | --- | --- | -| Managed/部署 Python 回归 | pass | `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/integrations/test_mpa_runtime.py tests/integrations/test_mpa_tool.py tests/integrations/test_mpa_skill_space.py tests/integrations/test_ve_faas_deploy_image_key_auth.py -q --no-cov`:978 项通过;根据本地资源选择两个 worker。 | -| 改动 Python 静态检查 | pass | Ruff 0.11.12 和使用 `.venv/bin/python` 的 Pyright,覆盖所有改动 Python 文件。仓库本地工具不可用,使用临时 `uvx` 工具,无全局安装。 | -| 前端测试 | pass | `npm --prefix frontend test`:Node 和 React 测试,含下一步/POST 重名、进行中检查取消和过期结果抑制、已有通用智能体/删除测试。 | -| 前端构建 | pass | `npm --prefix frontend run build`;同步 WebUI 构建产物。 | -| 国际化与资源引用 | pass | `npm --prefix frontend run check:i18n` 与 `npm --prefix frontend run test:webui-assets`:2 个语言 / 21 个命名空间,113 个资源 / 350 个引用。 | -| 浏览器流程 | pass | 真实 React 弹窗配隔离模拟 HTTP:重名/可用名称、查询不可用、POST 后置冲突后可编辑、旧共享请求恢复、BytePlus 手动 OV,以及 390×844 布局。没有发送云操作;已移除临时夹具/服务。 | -| 密钥扫描 / 差异空白 | pass | 使用仓库配置对改动文件执行 Gitleaks,输出脱敏;`git diff --check`。 | -| 真实云创建与 PostgreSQL 集成 | not_run | 本次未授权。提供商购买、数据库接管 SQL 和就绪仍需独立真实验收。 | -| Harness/进程 smoke 门禁 | not_applicable | 未修改 harness/sidecar 或进程契约。 | -| 提交 / pre-commit 门禁 | not_run | 未授权提交;后续提交前仍须执行 pre-commit 和分支同步。 | - -本地审查无剩余阻塞。跨主机查询/购买竞态仍依赖云端约束及归属校验,不宣称分布式锁保证。已有数据不迁移。重启 Studio 并开启新请求后使用新布局。 diff --git a/prd-spec/features/mpa-openviking-auto/2026-10-13-default-model-creation.md b/prd-spec/features/mpa-openviking-auto/2026-10-13-default-model-creation.md deleted file mode 100644 index d63e876ef..000000000 --- a/prd-spec/features/mpa-openviking-auto/2026-10-13-default-model-creation.md +++ /dev/null @@ -1,62 +0,0 @@ -# Automatic OpenViking preparation for Studio MPA - -[中文版](2026-10-13-default-model-creation.zh.md) - -Change ID: `mpa-openviking-auto`. Date: 2026-10-13. Status: implemented; live acceptance pending. -Contracts: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), [Runtime provisioning](../../../specs/mpa-runtime-provisioning/README.md). - -## Evidence, goals and non-goals -Studio currently collects an optional OV URL/resource ID/API Key. The MPA image already registers per-user OV identities using its runtime STS role. A read-only reference inspection shows enterprise READY, model identities/versions but no model API Key/Key ID; the console reports no custom model credentials and enabled limited-time content understanding. This does not prove a new API-created library can use default models. Official Create/Get/Access documentation defines async INIT/READY/FAILED and default-user key retrieval. -Goal: new Beijing Studio MPA requests prepare an independent enterprise OV library without manual connection inputs, using default VLM/Embedding configuration first. BytePlus, general agents, existing tasks, manual CLI configuration and existing Runtimes keep their behavior. No Ark endpoint/key creation, default-key copying, data migration or OV deletion is included. New OV is a retained paid resource; no live creation is implied by implementation approval. - -## Requirements and scenarios -- FR-1: Beijing built-in configuration advertises automatic OV. New UI requests persist `openvikingMode: auto`; absence/manual preserves old request semantics. Reject auto outside Volcengine Beijing and combinations of auto/manual connection values before mutation. CLI remains opt-in via managed configuration. -- FR-2: Under the existing authorized deployment lock create one enterprise library using legal hash-derived Name and scoped Description, default project, VLM `doubao-seed-2.0-lite`/`260428`, Embedding `doubao-embedding-vision`/`251215`, and explicit Source/EndpointID Credentials; the resolved Ark key is attached only to the copied wire request. Persist nonsecret intent before dispatch and ResourceID immediately after response. Registered IDs require matching name/description/project/version/models before reuse; deleting/deleted agents cannot create. -- FR-3: Poll INIT to READY within a bounded deadline, fail explicitly on FAILED/invalid status/missing ID/permissions. Obtain default-user key through AccessOpenVikingApiKey, inject URL/resource ID/key/user into Runtime before deployment. No key enters SQLite, registry, results, logs or fixtures. Runtime per-user registration remains the MPA image responsibility. -- FR-4: A failed later deployment reuses the registered library. A proven rejected create permits same-intent retry. Ambiguous create outcome remains blocked rather than blindly creating another library; operator reconciliation of the registered ResourceID is required. Cancellation retains known/unknown resources and stops subsequent operations. Key reads retry only transient failures; creates never automatically retry. -- FR-5: Third step shows automatic preparation and enterprise billing information; no manual fields for new auto requests. Old submitted manual tasks and BytePlus retain fields/validation. Safe localized errors/stages remain visible and retry uses the original mode/identity. Existing layout, TOS controls, keyboard/IME, cancellation and focus behavior are preserved. - -## Design and affected files -Add OpenViking options, a signed control-plane adapter and locked resource preparation. Reuse current refreshed account-verified deployment credentials, existing signing helper and diagnostic sink. Only documented CreateOpenVikingCollection/GetOpenVikingCollection/AccessOpenVikingApiKey are used; no undocumented name-discovery API or idempotency token is assumed. Collection and Runtime key lifetimes remain independent. Default model acceptance is an explicitly unverified provider capability; rejection must not silently provision paid Ark endpoints or omit OV. -Affected: managed config/profile/service/runner/tasks/diagnostics; new openviking module; Studio request/client/dialog/locales; focused tests, existing frontend release assets and operator docs. No general generation/chat/deletion or MPA image modification. -The Studio profile advertises availability separately from the execution mode: reading config or calling provision directly does not opt an old request into OV. Only an explicit new request mode or explicit CLI managed setting enables creation. Runtime provisioning keeps its existing environment, ownership and request-hash contracts; the four OV variables are already supported, so no separate Runtime contract change is required. -Deployment credentials require vikingdb CreateOpenVikingCollection/GetOpenVikingCollection/AccessOpenVikingApiKey. Runtime role already includes VikingdbFullAccess for per-user management. Only credentials of the verified deployment account may provision OV. Library bindings remain in mpa_admin_db; no library or memory cleanup is added to Agent deletion. - -## Tasks, verification and acceptance -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| FR-1/5 | T-1 mode propagation and UI | AC-1 new auto requests, old/manual/BytePlus unchanged | config/route/task and React tests, browser | -| FR-2/4 | T-2 locked intent and binding | AC-2 same-agent reuse, conflict/unknown/cancel safe | offline adapter/provisioner tests | -| FR-3 | T-3 readiness/key injection | AC-3 four env values, no secret persistence, explicit failures | service/runner/security tests | -| All | T-4 reconcile | AC-4 paired docs, build and required gates | affected Python regression, frontend test/build, Ruff/Pyright, i18n/assets, secret scan | -Actual verification is recorded below. Live smoke requires a separately authorized isolated library and cleanup decision; without it report default-model creation availability as unverified. - -## Risks, review and approval -The optional-model-credential API behavior and default-model entitlement may differ from an old console-created library; default-path rejection remains an actionable failure. Enterprise hourly billing starts when ready; provider resources are retained after failure/cancellation/deletion. No published discovery/idempotency contract is available for recovering a lost create response, so safety takes priority over blind retry. External registry/resource edits cannot be transactionally serialized with cloud calls. -Direct review performed because review-spec is unavailable: assessed boundaries, credentials, ownership, timeout/cancellation, ambiguous outcomes, compatibility, testability and language equivalence. User approved default-model-first Beijing implementation on 2026-10-13; BytePlus preservation was explicitly confirmed. No further scope expansion is authorized. The two frontend skills referenced by SPEC were not found at their paths or local skill roots; reuse existing ModalLayout/fields and directly verify Foundation S1-S8 instead of inventing a new design system. - -## Implementation review and verification (2026-10-13) - -Tested scope: uncommitted OV change on `71175c68636dc5b951e9804eb292d85402511e68`; no user state or cloud resource mutation. Test-first evidence: missing adapter module failed collection; auto-mode UI regression failed while manual fields were present. The implementation now covers mode propagation, account/region rejection, registry locking and ownership, INIT/READY/FAILED, cancellation, create ambiguity, safe errors, secret exclusion, and pre-Runtime environment injection. Review found and corrected two compatibility issues: account verification must precede constructing the OV adapter, and capability advertising must not enable OV for legacy/direct requests. - -| Gate | Outcome | Evidence / scope | -| --- | --- | --- | -| `uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/frontend/server/mpa/test_runtime_profile_routes.py tests/cli/test_studio_rbac.py` | pass | 1,074 passed; two workers chosen for local resources. | -| `npm --prefix frontend test` | pass | 1,379 Node tests and 79 Vitest tests. | -| `npm --prefix frontend run build` | pass | Matching main/widget release assets regenerated. | -| `npm --prefix frontend run check:i18n` | pass | 2 locales / 21 namespaces. | -| `npm --prefix frontend run test:webui-assets` | pass | 113 packaged files / 350 references. | -| `uvx ruff==0.11.12 check ` and `format --check` | pass | All 12 changed production/test Python files; pinned repository hook version. | -| `uvx pyright --pythonpath .venv/bin/python ` | pass | All 12 changed Python files, 0 errors/warnings. | -| `uv run --extra dev pre-commit run gitleaks --files ` | pass | Changed source, tests, docs and built assets; no credential findings. | -| Browser fixture | pass | Actual dialog, simulated server only: three steps, no auto OV inputs, localized model rejection, same-request retry, BytePlus manual fields, legacy submitted manual recovery, Escape/focus return, 390×844 layout. Temporary fixture removed; no cloud create calls. Existing interaction tests cover loading/keyboard/IME and terminal-state behavior. | -| Isolated `test_runner_scope_covers_async_worker_events` | fail (pre-existing) | When run alone, existing `.env` initialization logs enter captured stdout and break the test JSON parsing. Reproduced the identical failure against a temporary read-only HEAD source snapshot. Broad suite passes; no unrelated logging/test changes made. | -| Real OV create/key/readiness smoke | not_run | Needs separately authorized paid isolated resource. Old-library reads are not proof of new-library default-model entitlement. | -| Codex/sidecar smoke and coverage gates | not_applicable | No worker/image/harness protocol change. | -| Full repository pre-commit / commit / push | not_run | No commit requested; affected lint, type, secret and runtime-independent checks executed. | - -AC-1–AC-4 implementation and offline verification complete. Cloud acceptance remains explicitly pending: if default models are rejected, the task reports an actionable error without creating Ark endpoints or silently omitting OV. Enterprise OV is retained after failure, cancellation or MPA deletion. - -### Live finding and source correction - -The first user-triggered live request was explicitly rejected with InvalidParameter because its model source was empty. This disproves that omitting Credentials is sufficient. The [approved source correction](../../../prd-spec/bugfixes/mpa-openviking-source/2026-10-13-model-source.md) adds Source/EndpointID and uses the existing resolved Ark key only in the wire payload, with safe recovery for the matching rejected intent. Earlier offline results remain evidence of the initial implementation; current verification is recorded in the correction. No successful corrected live creation has yet been claimed. diff --git a/prd-spec/features/mpa-openviking-auto/2026-10-13-default-model-creation.zh.md b/prd-spec/features/mpa-openviking-auto/2026-10-13-default-model-creation.zh.md deleted file mode 100644 index a600c8124..000000000 --- a/prd-spec/features/mpa-openviking-auto/2026-10-13-default-model-creation.zh.md +++ /dev/null @@ -1,62 +0,0 @@ -# Studio MPA 自动准备 OpenViking - -[English](2026-10-13-default-model-creation.md) - -变更 ID:`mpa-openviking-auto`。日期:2026-10-13。状态:已实现;待真实验收。 -契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md)、[Runtime 部署](../../../specs/mpa-runtime-provisioning/README.zh.md)。 - -## 证据、目标与非目标 -Studio 目前收集可选的 OV 地址/资源 ID/API Key。MPA 镜像已使用运行角色 STS 注册 OV 用户身份。参考库只读核对为企业版 READY,返回模型名/版本但没有模型 API Key/Key ID;控制台显示自配凭证为零、内容理解限时免费已启用。这不证明新 API 创建的库可以使用默认模型。官方 Create/Get/Access 文档定义异步 INIT/READY/FAILED 以及默认用户取 Key。 -目标:北京 Studio 的新 MPA 请求自动准备独立企业版 OV,无需手填连接参数,先尝试默认 VLM/Embedding 配置。BytePlus、通用智能体、已有任务、手动 CLI 配置和已有 Runtime 保持行为。不包含创建方舟接入点/Key、复制旧 Key、数据迁移或删除 OV。新 OV 是保留的付费资源,实现批准不等于授权真实建库。 - -## 要求与场景 -- FR-1:北京内置配置提供自动 OV 能力。新 UI 请求持久化 `openvikingMode: auto`;缺省/manual 保留旧请求语义。非火山北京的 auto、auto 与手动连接参数混用均在写操作前拒绝。CLI 通过 managed 配置显式启用。 -- FR-2:在已有授权部署锁内,以合法哈希 Name、作用域 Description、默认项目创建独立企业库;VLM 为 `doubao-seed-2.0-lite`/`260428`,Embedding 为 `doubao-embedding-vision`/`251215`,显式提供 Source/EndpointID Credentials,已解析方舟 Key 仅加入复制的网络请求。发送前保存非秘密意图,响应后立即保存 ResourceID。复用注册 ID 前核验名称/描述/项目/版本/模型;deleting/deleted 智能体不能创建。 -- FR-3:在有界期限内轮询 INIT 至 READY,FAILED/非法状态/缺失 ID/权限失败均明确报错。通过 AccessOpenVikingApiKey 取得 default 用户 Key,部署前注入地址/资源 ID/Key/用户。Key 不进入 SQLite、注册库、结果、日志或夹具。Runtime 用户注册继续由 MPA 镜像负责。 -- FR-4:后续部署失败复用注册库。明确拒绝的创建允许重试相同意图;结果不确定时阻塞,不能盲目新建,由管理员核实并补齐注册 ResourceID。取消保留已知/未知资源并停止后续调用。仅取 Key 等读取操作重试瞬时错误,创建不自动重试。 -- FR-5:第三步显示自动准备及企业版计费信息;新 auto 请求没有手动输入框。已提交手动任务和 BytePlus 保留字段/校验。安全本地化错误和阶段可见,重试保留原模式/身份。沿用现有布局、TOS 控件、键盘/IME、取消和焦点行为。 - -## 设计与影响文件 -新增 OpenViking 配置、签名控制面适配器及持锁资源准备。复用刷新且核验账号的部署凭据、已有签名函数及诊断接收器。仅使用已公开的 CreateOpenVikingCollection/GetOpenVikingCollection/AccessOpenVikingApiKey;不假设未公开的名称发现接口或幂等 token。库与 Runtime Key 生命周期独立。默认模型接受情况是明确未验证的平台能力;拒绝时不能静默新建付费方舟接入点或省略 OV。 -影响:managed config/profile/service/runner/tasks/diagnostics;新增 openviking 模块;Studio 请求/client/dialog/locales;目标测试、既有前端发布产物和使用说明。不修改通用创建/聊天/删除及 MPA 镜像。 -Studio 配置将能力展示与执行模式分开:读取配置或直接调用 provision 不会为旧请求自动开启 OV。只有新请求显式模式或 CLI 显式 managed 设置才启用创建。Runtime 部署保留现有环境、归属及请求哈希契约;四个 OV 变量已受支持,因此不需要单独修改 Runtime 契约。 -部署凭据需要 vikingdb CreateOpenVikingCollection/GetOpenVikingCollection/AccessOpenVikingApiKey 权限。Runtime 角色已有 VikingdbFullAccess 用于用户管理。仅已核验部署账号的凭据能建 OV。库绑定保存在 mpa_admin_db;智能体删除不新增 OV 库或记忆清理。 - -## 任务、验证与验收 -| 要求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| FR-1/5 | T-1 模式透传与 UI | AC-1 新 auto 请求,旧/手动/BytePlus 不变 | config/route/task、React 测试及浏览器 | -| FR-2/4 | T-2 持锁意图与绑定 | AC-2 同智能体复用,冲突/未知/取消安全 | 离线适配器/资源准备测试 | -| FR-3 | T-3 就绪/Key 注入 | AC-3 四个环境变量、无秘密持久化、明确错误 | service/runner/安全测试 | -| 全部 | T-4 同步 | AC-4 双语文档、构建及必需检查 | 受影响 Python 回归、前端 test/build、Ruff/Pyright、i18n/assets、秘密扫描 | -实际验证记录见下文。真实验证需要另行批准的隔离库和清理决定;没有实测时,默认模型新建可用性标记未验证。 - -## 风险、评审与批准 -API 的可选模型凭证行为及默认模型权益可能与旧控制台库不同,默认路径拒绝时明确报错。企业版就绪即按小时计费,失败/取消/删除后保留云资源。未找到公开的名称发现/幂等契约用于恢复创建丢失响应,优先安全阻塞而非盲目重试。外部修改注册库/云资源无法与云调用组成事务。 -review-spec 不可用,已直接评审边界、凭据、归属、超时/取消、未知结果、兼容、可测试性及双语等价。用户在 2026-10-13 批准默认模型优先的北京实现,并明确确认 BytePlus 不变;未授权扩大范围。SPEC 引用的两个前端 skill 在目标路径及本地 skill 根均未找到,复用现有 ModalLayout/字段并直接核验 Foundation S1-S8,不另建设计系统。 - -## 实现评审与验证(2026-10-13) - -测试范围:基于 `71175c68636dc5b951e9804eb292d85402511e68` 的未提交 OV 改动;未修改用户状态或云资源。测试先行证据:适配器模块缺失时收集失败;auto UI 回归在仍显示手动字段时失败。实现覆盖模式透传、账号/地域拒绝、注册锁与归属、INIT/READY/FAILED、取消、未知创建结果、安全错误、秘密排除以及 Runtime 部署前环境注入。评审发现并修正两项兼容问题:必须先核验账号再构造 OV 适配器,能力展示不能替旧请求/直接调用开启 OV。 - -| 检查 | 结果 | 证据 / 范围 | -| --- | --- | --- | -| `uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/frontend/server/mpa/test_runtime_profile_routes.py tests/cli/test_studio_rbac.py` | pass | 1,074 通过;根据本地资源使用两个 worker。 | -| `npm --prefix frontend test` | pass | 1,379 项 Node、79 项 Vitest 测试。 | -| `npm --prefix frontend run build` | pass | 同步生成 main/widget 发布产物。 | -| `npm --prefix frontend run check:i18n` | pass | 2 个语言 / 21 个 namespace。 | -| `npm --prefix frontend run test:webui-assets` | pass | 113 个发布文件 / 350 个引用。 | -| `uvx ruff==0.11.12 check ` 与 `format --check` | pass | 全部 12 个变更生产/测试 Python 文件;使用仓库 hook 固定版本。 | -| `uvx pyright --pythonpath .venv/bin/python ` | pass | 全部 12 个变更 Python 文件,0 错误/警告。 | -| `uv run --extra dev pre-commit run gitleaks --files ` | pass | 变更源码、测试、文档及构建产物,无凭据发现。 | -| 浏览器夹具 | pass | 实际弹窗,仅模拟服务:三步流程、auto 无 OV 输入、本地化模型拒绝、同请求重试、BytePlus 手动字段、已提交旧手动任务恢复、Escape/焦点恢复、390×844 布局。临时夹具已移除,未调用云创建。现有交互测试覆盖加载/键盘/IME 及终态行为。 | -| 单独运行 `test_runner_scope_covers_async_worker_events` | fail(历史问题) | 单独运行时,现有 `.env` 初始化日志进入捕获 stdout,导致测试 JSON 解析失败。以临时只读 HEAD 源码快照复现相同失败。广泛套件通过;未修改无关日志/测试。 | -| 真实 OV 创建/Key/就绪验证 | not_run | 需要另行授权的付费隔离资源。读取旧库不证明新库默认模型权益可用。 | -| Codex/sidecar smoke 与覆盖率检查 | not_applicable | 未修改 worker/镜像/harness 协议。 | -| 全仓库 pre-commit / commit / push | not_run | 本次未要求提交;已执行受影响 lint、类型、秘密及独立于真实 Runtime 的检查。 | - -AC-1–AC-4 实现及离线验证完成。真实云验收明确待完成:默认模型被拒绝时任务返回可处理错误,不创建方舟接入点、不静默省略 OV。企业版 OV 在失败、取消或 MPA 删除后保留。 - -### 真实发现及来源修正 - -首次用户触发的真实请求因模型来源为空明确返回 InvalidParameter,证明不能完全省略 Credentials。[已批准的来源修正](../../../prd-spec/bugfixes/mpa-openviking-source/2026-10-13-model-source.zh.md) 补齐 Source/EndpointID,仅在网络请求中使用现有已解析方舟 Key,并安全恢复完全匹配的已拒绝意图。上面的离线结果仍是初版实现证据,当前验证记录在修正文档中。尚未声称修正后真实建库成功。 diff --git a/prd-spec/features/mpa-owned-resource-deletion/2026-10-12-owned-resource-deletion.md b/prd-spec/features/mpa-owned-resource-deletion/2026-10-12-owned-resource-deletion.md deleted file mode 100644 index cd3c3ce24..000000000 --- a/prd-spec/features/mpa-owned-resource-deletion/2026-10-12-owned-resource-deletion.md +++ /dev/null @@ -1,56 +0,0 @@ -# MPA owned-resource deletion - -[中文版](2026-10-12-owned-resource-deletion.zh.md) - -Change ID: `mpa-owned-resource-deletion`. Date: 2026-10-12. Status: implemented. -Contracts: [Studio MPA](../../../specs/studio-mpa-control-plane/README.md), [provisioning](../../../specs/mpa-runtime-provisioning/README.md). - -## Evidence and scope -MPA delete preview queries Profile before sessions. A Profile authentication failure prevents confirmation; the detail page hides the sidebar containing the error. The current delete handler cleans sessions and Runtime only. Managed deployment records already persist worker/Skill Space IDs and ownership flags, with matching cloud ownership tags. - -## Goals, scenarios, and requirements -- FR-1: General Runtime deletion remains unchanged. MPA deletion must not query Profile. Active lifecycle operations and active sessions still block deletion; failed operations are terminal and do not block by themselves. Session-query failures remain errors, never zero-session success. -- FR-2: Delete only managed, exclusively owned worker templates and Skill Spaces. Verify the deployment Runtime binding, managed flags, account/region/project, ownership tags, and absence of other deployment/Runtime references. Preserve shared, imported, or unproven resources and explain preservation in preview. Discovery/ownership conflicts fail safely. -- FR-3: Cleanup order is idle sessions, worker template, Skill Space, Runtime. Persist resource cleanup intent/completion under the existing account/region advisory lock. Missing resources during cleanup support retry; other errors preserve Runtime for retry. Keep database, Workspace, network, APIG, identity, storage and all unrelated data. -- FR-4: Open confirmation immediately with a loading state. Surface preview/execution errors inside the dialog and allow preview retry. Cancel/unmount aborts preview; late responses cannot reopen or overwrite dialogs. Confirmation revalidates server-side authorization, sessions and resource ownership. - -## Design and affected files -Use the authorized Runtime's server-side registry URL and current provider credentials; do not resolve creation configuration or provision new prerequisites. Add a managed deletion service and resource-plan callback to the existing preview route. Runtime/Tool/Skills SDK calls respect provider and region. Missing registry URL/record preserves associated resources. A registered deployment mismatch blocks cleanup. Record deletion state so provisioning cannot recreate resources while cleanup is pending or complete. Extend preview additively with retained resources and resource IDs; `profile` stays null for compatibility. The existing confirmation component is the UI baseline. -Affected: managed deletion/database/cloud helpers; Studio BFF and cli_frontend; AgentWorkspace/client/i18n; focused Python and React tests; release assets; bilingual component contracts and frontend README. - -## Tasks and acceptance -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| FR-1 | T-1 remove Profile dependence | AC-1 no Profile calls; general unchanged; active work protected | BFF and Studio RBAC regressions | -| FR-2/FR-3 | T-2 owned cleanup | AC-2 exclusive resources removed; shared/unproven resources preserved; partial retry safe | Offline managed deletion tests | -| FR-4 | T-3 visible states | AC-3 loading/error/retry/cancel/late events work | React tests and browser fixture | -| All | T-4 reconcile and verify | AC-4 localized release build and required checks | frontend test/build, targeted pytest, Ruff, Pyright, i18n/assets | - -## Risks and boundaries -External actors can change references outside the advisory lock; perform fresh cloud reads immediately before cleanup. Cloud APIs may reject deletion while references exist; propagate this failure and retain Runtime rather than report success. Session visibility remains limited to the authenticated Runtime API; no global-session claim. Removing Profile lookup does not bypass session authentication. Live deletion of user resources is not part of verification; use isolated mocks/browser fixtures. Existing independent provisioning/operation stores do not provide a cross-system transaction. - -## Review and approval -Direct design review performed because review-spec skill is unavailable: checked ownership, provider isolation, partial failure, concurrency, secrets, compatibility, bilingual equivalence and tests. User explicitly approved this approach on 2026-10-12. No unresolved design blockers. Commit/push/deployment are not authorized by design approval. - -## Verification record -Scope: local changes on d6065d6e, 2026-10-12; no commit or push. - -- pass: reproduced failures first (missing cleanup module, Profile dependency, invisible dialog errors), then implemented; offline and React cases for AC-1 through AC-3 pass. -- pass: `uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/frontend/server/mpa/test_runtime_profile_routes.py tests/frontend/server/mpa/test_runtime_profile_client.py tests/cli/test_studio_rbac.py`: 1004 passed, using 2 workers. -- pass: subsequent ownership review, provider SDK requests and changed references: complete resource-cleanup file 17 passed; final focused deletion selection 21 passed. Covers ordering, shared retention, imported/legacy resources, ownership conflicts, discovery failure, partial retries, NotFound, cancellation intent, domestic/international requests, forged client IDs and resource preview plans. -- pass: `npm --prefix frontend test`: 1379 Node + 78 Vitest = 1457 passed; `npm --prefix frontend run build`, `check:i18n`, `test:webui-assets` (113 files, 350 references), and `git diff --check`. -- pass: real-browser isolated simulated verification: error, preview retry, cleanup failure, successful retry, loading, Escape cancellation/focus return, ignored late results, original general confirmation and 390×844 layout. No live cloud resources are changed by the fixture. -- pass: Ruff 0.11.12 matching `.pre-commit-config.yaml`, via `uvx --from ruff==0.11.12 ruff check --config pyproject.toml `; new/affected managed modules have 0 Pyright errors; `uvx pre-commit run gitleaks --files ` passes. -- fail (existing baseline): `uvx pyright --pythonpath .venv/bin/python frontend/server/mpa/routes.py veadk/cli/cli_frontend.py` reports 49 pre-existing diagnostics; comparison with the two HEAD files shows no added diagnostic. Historical cleanup is outside scope. -- not_run: live cloud deletion (user resources are not verification fixtures); all repository Python tests (all managed MPA and affected Studio authorization/control-plane tests selected); full pre-commit (no commit requested). -- not_applicable: native IME (no text inputs changed). Ruff/Pyright were unavailable locally; temporary tools were used without global configuration changes. AC-4 release assets, documentation and targeted checks are complete, with the verified existing full-file Pyright limitation retained. - -Remaining boundaries: removing Profile does not bypass session authentication; session API failure still blocks deletion and shows its cause. Live cloud cleanup is unverified. External reference edits do not honor registry locks; cloud deletion and registry writes cannot provide a cross-system transaction. - -## Session identity correction (2026-10-12) - -User verification found `x-user-id header is required` (HTTP 422) in preview. Session transport previously sent only Bearer authorization and omitted caller identity. Within approved FR-1/FR-4 scope, session listing/cleanup in preview and execution must forward Studio's server-authenticated `owner_id`, matching chat/scheduled-task identity; browser-supplied `x-user-id` is not trusted. Add optional `user_id` to Runtime client methods, preserving existing calls without it and general deletion. Review covers identity, compatibility and error propagation; add client header and BFF/execution anti-spoofing regressions. Verification: reproduced 2 failing regressions before fixing; 22 focused deletion cases pass; `uv run --extra dev pytest -n 2 -q tests/frontend/server/mpa tests/cli/test_studio_rbac.py` reports 242 passed. Ruff, Runtime client Pyright (0 errors), Gitleaks and diff whitespace pass. General deletion does not enter MPA session calls. Live cloud deletion: not_run; no frontend code changed in this correction, so the matching assets do not require rebuilding. - -## Preview latency correction (2026-10-12) - -User verification remained in checking state; the current Studio root returned HTTP 200 in 0.003 seconds, confirming responsiveness. Verified code bottlenecks are sequential Runtime detail reads and repeated STS verification on each SDK call; step logs have not yet established the slowest call in this specific request. Within approved FR-2/FR-4, preserve complete reference checks and batch detail reads with at most 4 concurrent requests. Only deletion contexts opt into request-local identity caching for unchanged credentials; credential rotation re-verifies identity and account changes remain rejected. Creation retains per-call verification by default. Limit the whole preview to 25 seconds (before the frontend 30-second deadline), returning structured `mpa_delete_preview_timeout` (HTTP 504) for retry; offload the preview’s synchronous authorization read to avoid blocking the event loop. Add timeout, concurrency, default compatibility, credential rotation/account-change regressions and directly review boundaries and bilingual equivalence. Live resource deletion remains outside verification. Verification (2026-10-12, local diff on d6065d6e): pass — `uv run --extra dev pytest -q tests/integrations/mpa_managed/test_deletion.py tests/frontend/server/mpa/test_runtime_profile_routes.py tests/integrations/mpa_managed/test_runtime_deployment_edges.py tests/integrations/mpa_managed/test_byteplus.py` (106 passed); `uv run --extra dev pytest -q tests/cli/test_studio_rbac.py -k 'delete'` (7 passed). Python 3.10 timeout cancellation is covered. Pass — pinned Ruff; `uvx pyright --pythonpath .venv/bin/python` on runtime/deletion (0 errors); Gitleaks; diff whitespace. Full routes/CLI Pyright: fail with the same 49 baseline errors, 0 added diagnostics. Not applicable — frontend rebuild/browser fixture rerun, since this correction changes no frontend source or payload shape. Not run — live cloud deletion, measured live preview timing after restart, all-repository tests and full pre-commit (no commit requested). Existing browser state/error/retry verification remains applicable. Cancellation cannot terminate an already-running synchronous SDK thread; such preview work is read-only and no deletion is performed by preview. diff --git a/prd-spec/features/mpa-owned-resource-deletion/2026-10-12-owned-resource-deletion.zh.md b/prd-spec/features/mpa-owned-resource-deletion/2026-10-12-owned-resource-deletion.zh.md deleted file mode 100644 index 208a8c3e9..000000000 --- a/prd-spec/features/mpa-owned-resource-deletion/2026-10-12-owned-resource-deletion.zh.md +++ /dev/null @@ -1,56 +0,0 @@ -# MPA 独占资源删除 - -[English](2026-10-12-owned-resource-deletion.md) - -变更 ID:`mpa-owned-resource-deletion`。日期:2026-10-12。状态:implemented。 -契约:[Studio MPA](../../../specs/studio-mpa-control-plane/README.zh.md)、[创建流程](../../../specs/mpa-runtime-provisioning/README.zh.md)。 - -## 证据和范围 -MPA 删除预览先查询 Profile,再查询会话。Profile 鉴权失败导致无法确认;详情页隐藏了包含错误的侧栏。当前删除只清理会话和 Runtime。管理部署记录已有 Worker/Skill Space ID、归属标记及对应云资源标签。 - -## 目标、场景和需求 -- FR-1:通用 Runtime 删除不变。MPA 删除不查询 Profile。运行中的生命周期操作和会话仍阻止删除;失败操作为终止状态,本身不再阻止删除。会话查询失败必须返回错误,不得当作零会话成功。 -- FR-2:仅删除受管且独占的沙箱模板和 Skill Space。核验部署 Runtime 绑定、受管标记、账号/地域/项目、归属标签,以及其他部署/Runtime 是否引用。共享、导入或无法证明归属的资源保留,并在预览中解释。发现查询失败或归属冲突时安全失败。 -- FR-3:按空闲会话、沙箱模板、Skill Space、Runtime 顺序清理。在现有账号/地域 advisory lock 下持久保存清理意图和完成状态。清理时资源已不存在允许重试;其他错误保留 Runtime 供重试。数据库、Workspace、网络、APIG、身份、存储及无关数据保留。 -- FR-4:立即打开确认弹窗并显示加载状态。预览和执行错误在弹窗内显示,支持重试预览。取消/卸载终止预览;晚到结果不得重开或覆盖弹窗。执行时重新检查服务端授权、会话和资源归属。 - -## 设计和影响文件 -使用已授权 Runtime 的服务端注册库 URL 和当前 Provider 凭据,不解析创建配置,也不创建前置资源。增加受管删除服务,给现有预览路由接入资源计划回调。Runtime/Tool/Skills SDK 调用遵循 Provider 和地域。缺少注册库 URL/记录时保留相关资源;部署记录不匹配时阻止清理。记录删除状态,防止创建流程在清理进行中或完成后重建资源。预览以兼容方式增加保留资源及资源 ID;`profile` 保持 null。UI 沿用现有确认弹窗。 -影响文件:managed 删除/数据库/云适配器;Studio BFF、cli_frontend;AgentWorkspace/client/i18n;定向 Python 和 React 测试;发布产物;双语组件契约与 frontend README。 - -## 任务和验收 -| 需求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| FR-1 | T-1 移除 Profile 依赖 | AC-1 不调用 Profile;通用不变;保护运行任务 | BFF 与 Studio RBAC 回归 | -| FR-2/FR-3 | T-2 独占清理 | AC-2 删除独占资源;保留共享/归属不明资源;安全重试部分失败 | managed 删除离线测试 | -| FR-4 | T-3 可见状态 | AC-3 加载/错误/重试/取消/晚到事件正确 | React 测试及浏览器 fixture | -| 全部 | T-4 同步与验证 | AC-4 本地化发布构建及必要检查 | frontend test/build、定向 pytest、Ruff、Pyright、i18n/assets | - -## 风险和边界 -外部操作者可能绕过 advisory lock 修改引用;清理前重新读取云端状态。云接口可能拒绝删除被引用资源;应返回错误并保留 Runtime,不能报告成功。会话可见范围仍由已认证 Runtime API 决定,不能宣称全局会话已检查。取消 Profile 查询不绕过会话鉴权。验证使用隔离模拟及浏览器 fixture,不删除用户云资源。现有创建/操作存储之间没有跨系统事务。 - -## 评审和批准 -review-spec skill 不可用,已直接评审归属、Provider 隔离、部分失败、并发、密钥、兼容性、双语等价及测试。用户于 2026-10-12 明确批准本方案。无设计阻断项。设计批准不授权提交、推送或部署。 - -## 验证记录 -范围:d6065d6e 上的本地改动,2026-10-12;未提交或推送。 - -- pass:先复现失败(资源模块未实现、Profile 依赖、弹窗错误不可见),再实现;AC-1 至 AC-3 的离线和 React 用例通过。 -- pass:`uv run --extra dev pytest -n 2 -q tests/integrations/mpa_managed tests/frontend/server/mpa/test_runtime_profile_routes.py tests/frontend/server/mpa/test_runtime_profile_client.py tests/cli/test_studio_rbac.py`,1004 passed,使用 2 个 worker。 -- pass:后续归属复核、Provider SDK 请求和引用变化补充验证:完整资源清理文件 17 passed;最后删除相关定向选择 21 passed。覆盖执行顺序、共享保留、导入/旧资源、归属冲突、发现失败、部分重试、NotFound、取消意图、国内/海外请求、伪造客户端 ID 以及预览资源计划。 -- pass:`npm --prefix frontend test`,1379 Node + 78 Vitest = 1457 passed;`npm --prefix frontend run build`、`check:i18n`、`test:webui-assets`(113 文件、350 引用)和 `git diff --check`。 -- pass:真实浏览器中的隔离模拟验证:错误、重新检查、清理失败、重试成功、加载、Escape 取消/焦点返回、晚到结果不重开、通用原确认、390×844 窄窗口。浏览器不操作真实云资源。 -- pass:按仓库 `.pre-commit-config.yaml` 的 Ruff 0.11.12,使用 `uvx --from ruff==0.11.12 ruff check --config pyproject.toml `;新增/受影响 managed 模块 Pyright 为 0 errors;`uvx pre-commit run gitleaks --files ` 通过。 -- fail(既有基线):`uvx pyright --pythonpath .venv/bin/python frontend/server/mpa/routes.py veadk/cli/cli_frontend.py` 有 49 条既有诊断;与 HEAD 两文件基线逐项比较,没有新增诊断。未扩大范围清理历史问题。 -- not_run:真实云删除(不使用用户资源验证);仓库全部 Python 测试(本次选择所有 MPA managed 和受影响 Studio 权限/控制面测试);完整 pre-commit(未请求提交)。 -- not_applicable:原生 IME(无输入框改动)。本地未安装 Ruff/Pyright,通过临时工具环境验证,未修改全局配置。AC-4 的发布产物、文档和针对性检查完成;全文件 Pyright 保留已确认的历史基线限制。 - -剩余边界:移除 Profile 查询不会绕过会话鉴权;会话接口失败仍阻止删除并显示原因。云端实际清理行为尚未做实时验证。外部引用修改不受注册库锁约束;云删除与注册库保存之间无法提供跨系统事务。 - -## 会话身份补正(2026-10-12) - -用户实测预览返回 `x-user-id header is required`(HTTP 422)。之前会话 transport 只传 Bearer,遗漏用户身份。沿用已批准 FR-1/FR-4 范围:预览和实际删除的会话查询/清理必须传 Studio 服务端已认证的 `owner_id`,与聊天/定时任务一致;不采信浏览器伪造的 `x-user-id`。Runtime client 增加可选 `user_id` 参数,未提供时保持原调用兼容,通用删除不变。评审已核对调用身份、兼容性和错误传播;增加 client 请求头及 BFF/删除执行的反伪造回归测试。验证:先复现 2 条失败回归后修复;删除定向 22 passed;`uv run --extra dev pytest -n 2 -q tests/frontend/server/mpa tests/cli/test_studio_rbac.py` 为 242 passed;Ruff、Runtime client Pyright(0 errors)、Gitleaks 和 diff whitespace 均 pass。通用删除未进入 MPA 会话调用。真实云删除 not_run;本轮无前端代码改动,因此无需重建已匹配的前端产物。 - -## 预览耗时补正(2026-10-12) - -用户实测停在检查状态;当前服务根页面在 0.003 秒内返回 HTTP 200,表明服务仍响应。已确认的代码瓶颈是 Runtime 详情串行读取,以及每次 SDK 调用重复核验 STS;尚未通过逐步骤日志证明本次最慢的具体调用。沿用已批准 FR-2/FR-4,保持完整引用核验,详情分批最多 4 个并发。仅删除上下文可启用同一凭据的请求内身份缓存;凭据变化时重新核验且账号变化继续拒绝,创建流程默认逐次核验不变。预览整体限时 25 秒(早于前端 30 秒),超时返回结构化 `mpa_delete_preview_timeout`(HTTP 504)供重试;删除预览的同步授权读取移入线程,避免阻塞事件循环。补充限时、并发、默认兼容、凭据轮换/账号变化回归,并直接评审边界和中英文等价性。未增加真实云资源删除验证。验证(2026-10-12,d6065d6e 上的本地差异):pass — `uv run --extra dev pytest -q tests/integrations/mpa_managed/test_deletion.py tests/frontend/server/mpa/test_runtime_profile_routes.py tests/integrations/mpa_managed/test_runtime_deployment_edges.py tests/integrations/mpa_managed/test_byteplus.py`(106 项通过);`uv run --extra dev pytest -q tests/cli/test_studio_rbac.py -k 'delete'`(7 项通过)。覆盖 Python 3.10 超时取消。pass — 固定版本 Ruff;runtime/deletion 的 `uvx pyright --pythonpath .venv/bin/python`(0 错误);Gitleaks;差异空白检查。完整 routes/CLI Pyright:fail,仍为基线 49 条错误,新增 0 条诊断。not_applicable — 重新构建前端/重跑浏览器夹具,本次补正没有修改前端源码或响应结构。not_run — 真实云资源删除、重启后的真实预览耗时、全仓测试和完整 pre-commit(未要求提交)。已有浏览器状态、错误和重试验证仍适用。取消无法终止已经运行的同步 SDK 线程;此类预览工作仅为只读,预览不会执行删除。 diff --git a/prd-spec/features/mpa-p0-productionization/2026-09-15-dev-loop-spec-review.md b/prd-spec/features/mpa-p0-productionization/2026-09-15-dev-loop-spec-review.md deleted file mode 100644 index 393cf99b9..000000000 --- a/prd-spec/features/mpa-p0-productionization/2026-09-15-dev-loop-spec-review.md +++ /dev/null @@ -1,56 +0,0 @@ -# MPA AgentKit P0 Dev Loop Spec Review - -- Change ID: `mpa-p0-productionization` -- Date: 2026-09-15 -- Chinese: [2026-09-15-dev-loop-spec-review.zh.md](2026-09-15-dev-loop-spec-review.zh.md) -- Reviewed PRD: [MPA AgentKit P0 Functional Migration](2026-09-15-mpa-p0-productionization-design.md) - -## 1. Overall conclusion - -- Feasibility: medium-high after remediation; delivery is XL across VeADK, agentkit-mpa-agent, AgentKit OpenTOP/Runtime, and the worker protocol. -- Largest risks: identity and live writes, cross-system idempotency, PostgreSQL concurrency/recovery, and refresh-safe creation operations. -- Direction: deliver M0 and S1 through S5 as serial vertical slices, each independently verified, landed, and reversible; do not force the scope into one PR. -- Gate: every P0/P1 below is reflected in the PRD/contracts. Local M0 contracts gate S1; deployed-only identity/Profile checks remain mandatory before release. - -## 2. Findings and remediation - -| Original severity | Finding | Remediation | Status | -| --- | --- | --- | --- | -| P0 | First Profile apply simultaneously required `If-Match` and `If-None-Match` | Made preconditions mutually exclusive; added `400/428` and strong ETag | resolved | -| P0 | Async apply/run/continue had no durable idempotency/status model | Added minimal `runtime_operations`; pending Profile never becomes current | resolved | -| P0 | Continuation promised impossible ACID across external A2A TaskStore | Replaced with Runtime transaction, dispatch outbox, idempotent dispatcher | resolved | -| P0 | REST JWT and A2A TIP identity semantics diverged | Defined one `RuntimePrincipal v1`, issuer/JWKS mapping, and migration matrix | resolved; M0 live gate | -| P0 | Earlier design incorrectly depended on Managed Agent CRUD/version | Removed that product from P0; Studio AgentDraft is normalized directly into an MPA Profile and applied to Runtime | resolved by scope decision | -| P0 | Explicit Session upgrade had no API | Added `profile-upgrade` with ETag, idempotency, active-Turn, downgrade, and unapplied-target semantics | resolved | -| P0 | Creation state lived only in browser memory | Added TOS-CAS creation operation and reconcile/retry API | resolved | -| P0 | AgentKit Session and Runtime Session were dual authorities | P0 uses only mpa-agent business Session/Turn; Managed Agent Session is outside the path | resolved | -| P0 | Cloud E2E and PostgreSQL semantics were not executable | Added live environment manifest, PostgreSQL two-connection lane, fault injection, and cross-repo manifest | resolved; execution hard gate | -| P1 | A2A execution version had no wire contract | Defined `urn:veadk:mpa:execution:v1`, metadata/hash, and legacy behavior | resolved | -| P1 | Unknown SSE cursor silently replayed everything | Selected persistent ADK events plus inbox fence; unknown cursor returns `410` | resolved | -| P1 | Plaintext secrets conflicted with reference-only policy | AgentKit mode rejects new plaintext writes and defines conversion/clear/removal window | resolved | -| P1 | Performance targets lacked measurement method | Added warm-up, sample, concurrency, data-size, and timing boundaries | resolved | -| P1 | Page reuse boundary was abstract | Added MPA intent/view-model and prohibited GitHub-version/generic-draft/evaluation reuse | resolved | -| P1 | Runtime gates and cross-repo compatibility were unspecified | Fixed `make test/coverage`, cross-repo manifest, and compatibility matrix | resolved | -| P1 | Runtime-operation scope/status lookup was incomplete | Limited ledger to Profile/run/upgrade/continue, added common operation GET, and kept config/control in their own CAS stores | resolved | -| P1 | Lost first response and update recovery were incomplete | Unified create/update on `202 + operationId`, client idempotency, active listing, reconciliation, and retry | resolved | -| P1 | Session upgrade did not define override recomputation | Target Profile becomes base, override/clear remains, inherit follows new defaults, invalid resources reject atomically | resolved | -| P1 | MPA detail had no implementable data contract | Added `MpaAgentView` and 0/1/N Runtime-binding/orphan semantics | resolved | -| P1 | Runtime A2A capability was confused with Worker protocol | Split compatibility columns and fixed current Worker REST baseline | resolved | -| P1 | Smoke depended on model tool choice | Split into a tool-disabled model probe and direct existing CodexWorkerClient probe | resolved | - -## 3. Sixteen-dimension conclusion - -Context, scope, terminology, model interpretation, SDD/TDD fitness, minimal implementation, compatibility, existing-data impact, runtime risk, feasibility, extensibility, over-design, minimum change, code boundaries, architecture consistency, and testability were reviewed. Key convergence: no ArkClaw migration; no template/expert/version product; only necessary Runtime persistence; one Session authority; adapters/outbox/reconciliation across external systems rather than distributed-transaction claims. - -## 4. Delivery order - -1. M0: Profile client contract, principal foundation, PostgreSQL, fault injection, and cross-repo manifest; deployed-only identity/Profile proof moves to S5. -2. S1: creation, durable operation, Profile, deterministic smoke, first chat. -3. S2: Session execution config and explicit version upgrade. -4. S3: run idempotency, outbox, persistent event replay. -5. S4: multi-worker pause/resume/continuation. -6. S5: detail/version, Runtime lifecycle, Debug/Trace, CLI, compatibility closure. - -## 5. Gate - -The corrected scope review passes with no P0/P1. Local M0 contracts and PostgreSQL foundation are green, so S1 implementation may proceed; deployed-only identity/Profile verification remains a release gate. diff --git a/prd-spec/features/mpa-p0-productionization/2026-09-15-dev-loop-spec-review.zh.md b/prd-spec/features/mpa-p0-productionization/2026-09-15-dev-loop-spec-review.zh.md deleted file mode 100644 index 95e4a9cbb..000000000 --- a/prd-spec/features/mpa-p0-productionization/2026-09-15-dev-loop-spec-review.zh.md +++ /dev/null @@ -1,56 +0,0 @@ -# MPA AgentKit P0 Dev Loop Spec 评审 - -- Change ID:`mpa-p0-productionization` -- 日期:2026-09-15 -- English:[2026-09-15-dev-loop-spec-review.md](2026-09-15-dev-loop-spec-review.md) -- 被评审 PRD:[MPA AgentKit P0 功能迁移与落地](2026-09-15-mpa-p0-productionization-design.zh.md) - -## 1. 总体结论 - -- 可行性:整改后为中高;实施规模为跨 VeADK、agentkit-mpa-agent、AgentKit OpenTOP/Runtime 和 worker 协议的 XL 级交付。 -- 最大风险:身份与真实写链路、跨外部系统幂等、PostgreSQL 并发恢复、创建 operation 刷新恢复。 -- 推荐方向:按 M0、S1 至 S5 串行垂直切片交付,每个切片独立验证、合入和回滚,不以单个 PR 一次性实现。 -- 门禁结论:下列 P0/P1 已写回 PRD 与组件契约;本地 M0 contract 是进入 S1 的门禁,仅部署后可验证的身份/Profile 项保留为发布门禁。 - -## 2. 问题与整改记录 - -| 原级别 | 问题 | 整改 | 状态 | -| --- | --- | --- | --- | -| P0 | 首次 Profile apply 同时要求 `If-Match` 与 `If-None-Match` | 改为互斥前置条件,补 `400/428` 与强 ETag | resolved | -| P0 | 异步 apply/run/continue 缺少幂等与状态持久化 | 新增最小 `runtime_operations`;pending Profile 不切 current | resolved | -| P0 | continuation 对外部 A2A TaskStore 承诺不可实现的 ACID | 改为 Runtime 事务 + dispatch outbox + 幂等 dispatcher | resolved | -| P0 | REST JWT 与 A2A TIP 身份语义分裂 | 定义统一 `RuntimePrincipal v1`、同 issuer/JWKS、claim 映射和迁移矩阵 | resolved;M0 live gate | -| P0 | 旧方案错误依赖 Managed Agent CRUD/version | 从 P0 删除该产品依赖;Studio AgentDraft 直接规范化为 MPA Profile 并应用到 Runtime | 已按范围决策解决 | -| P0 | 显式升级 Session 缺少 API | 新增 `profile-upgrade`,定义 ETag、幂等、活动 Turn、降级和未 applied 错误 | resolved | -| P0 | 创建状态仅在浏览器内存,刷新不可恢复 | 新增 TOS CAS creation operation 与 reconcile/retry API | resolved | -| P0 | AgentKit Session 与 runtime Session 双权威 | P0 明确只使用 mpa-agent 业务 Session/Turn;AgentKit Managed Agent Session 不进入主链路 | resolved | -| P0 | 云 E2E 和 PostgreSQL 语义不可执行 | 补 live 环境清单、真实 PostgreSQL 双连接 lane、fault injection 与跨仓 manifest | resolved;执行时硬门禁 | -| P1 | A2A execution 版本无 wire contract | 定义 `urn:veadk:mpa:execution:v1` 与 metadata/hash/兼容行为 | resolved | -| P1 | SSE cursor 未知时静默全量重放 | 指定持久 ADK events + inbox fence;未知 cursor 返回 `410` | resolved | -| P1 | 明文 Secret 与“只保存引用”冲突 | AgentKit mode 禁止新明文写,定义转换、清空和兼容删除窗口 | resolved | -| P1 | 性能指标无测量口径 | 补 warm-up、样本、并发、数据规模和统计边界 | resolved | -| P1 | 页面复用边界不明确 | 增加 MPA intent/view-model,禁止复用 GitHub version/generic draft/evaluation 逻辑 | resolved | -| P1 | Runtime 门禁和跨仓兼容不明确 | 固定 `make test/coverage`、跨仓 manifest 和兼容矩阵 | resolved | -| P1 | runtime operation 覆盖范围/查询接口不闭合 | 限定 Profile/run/upgrade/continue,补通用 operation GET;配置与控制各用自己的 CAS store | resolved | -| P1 | create/update 首次响应丢失与更新恢复不闭合 | 两类写操作统一 `202 + operationId`、客户端幂等键、active list、reconcile/retry | resolved | -| P1 | Session upgrade 未定义 override 重算 | 目标 Profile 作新 base,保留 override/clear,inherit 跟随新默认,失效资源原子拒绝 | resolved | -| P1 | MPA detail 缺少可实现数据契约 | 增加 `MpaAgentView` 与 0/1/N Runtime binding/orphan 语义 | resolved | -| P1 | Runtime A2A capability 与 Worker protocol 混淆 | 兼容矩阵拆列,固定当前 Worker REST 静态基线 | resolved | -| P1 | smoke 依赖模型自主调用工具 | 拆成 tool-disabled 模型探针与 Runtime 直接调用现有 CodexWorkerClient 探针 | resolved | - -## 3. 16 维检查结论 - -上下文、范围、术语、模型理解、SDD/TDD、最小实现、兼容、存量影响、运行风险、可行性、扩展性、过度设计、最小变更、代码边界、架构一致性和可验收性均已检查。关键收敛包括:不迁移 ArkClaw 数据;不建设模板/专家/version;只新增必要 Runtime 持久模型;Session 单一权威;外部系统使用 adapter/outbox/reconcile,不宣称分布式事务。 - -## 4. 落地顺序 - -1. M0:Profile client contract、principal 基座、PostgreSQL、fault injection、跨仓 manifest;仅部署后可验证的身份/Profile 证据移至 S5。 -2. S1:创建、持久 operation、Profile、确定性 smoke、首轮对话。 -3. S2:Session execution-config 与显式版本升级。 -4. S3:Run 幂等、outbox 与持久事件重放。 -5. S4:多 worker pause/resume/continuation。 -6. S5:详情/版本、Runtime 生命周期、Debug/Trace、CLI 和兼容收口。 - -## 5. 门禁 - -范围修正后的评审无 P0/P1;本地 M0 contract 与 PostgreSQL 基座已通过,可进入 S1,实现完成后必须通过部署环境 identity/Profile 发布门禁。 diff --git a/prd-spec/features/mpa-p0-productionization/2026-09-15-functional-validation-cases.md b/prd-spec/features/mpa-p0-productionization/2026-09-15-functional-validation-cases.md deleted file mode 100644 index 3c0ef4e0e..000000000 --- a/prd-spec/features/mpa-p0-productionization/2026-09-15-functional-validation-cases.md +++ /dev/null @@ -1,286 +0,0 @@ -# MPA AgentKit P0 Functional Verification Cases - -- Change ID: `mpa-p0-productionization` -- Status: `designed`; execution in progress through the S5-10 compatibility preflight -- Date: 2026-09-15 -- Chinese: [2026-09-15-functional-validation-cases.zh.md](2026-09-15-functional-validation-cases.zh.md) -- PRD: [MPA AgentKit P0 Functional Migration](2026-09-15-mpa-p0-productionization-design.md) -- Browser runbook: [browser-cases.md](browser-cases.md) - -## 1. Common execution and evidence rules - -The raw evidence root is ignored local `evidence/{automated,postgres,browser,contract,live}//`. Run `mkdir -p` for the applicable directory before execution. Each item records `caseId/timestamp/repositorySHA/runtimeImageDigest/agentkitOpenAPIVersion/workerProtocol/environment/command/status/expected/actual/artifactPaths/cleanupResult`; status must be one of `pass|fail|blocked|not_run|not_applicable`. Logs, JSON, screenshots, and manifests must be redacted. - -Commands/files marked "to add" below are deliverables of the corresponding slice. Their paths must be verified before execution; they are not capabilities that already exist. Mock, in-memory, PostgreSQL, browser, and live-cloud evidence cannot replace one another. - -## 2. Coverage matrix - -| AC | Case | Slice | -| --- | --- | --- | -| `AC-1` | `VC-06`, `VC-20A`, `VC-20B` | S1/live | -| `AC-2` | `VC-04`, `VC-05`, `VC-07`, `VC-20B` | M0/S1/live | -| `AC-3` | `VC-08`, `VC-09`, `VC-17`, `VC-18B` | S2/browser/PG | -| `AC-4` | `VC-10`, `VC-11`, `VC-18C` | S3/PG | -| `AC-5` | `VC-12`, `VC-13`, `VC-18D` | S4/PG | -| `AC-6` | `VC-02`, `VC-15`, `VC-18E`, `VC-20B` | M0/continuous/live/PG | -| `AC-7` | `VC-14`, `VC-17`, `VC-18C` | S3/browser/PG | -| `AC-8` | `VC-16`, `VC-17` | S1-S5/browser | -| `AC-9` | `VC-19`, `VC-21` | S5/live | -| `AC-10` | `VC-16`, `VC-19`, `VC-22` | S5 | -| `AC-11` | `VC-02` local subset, `VC-03`, `VC-01`, `VC-20B` | M0 local / S5 live | -| `AC-12` | `VC-18A`, `VC-18B`, `VC-18C`, `VC-18D`, `VC-18E`, `VC-19` | M0-S5/PG | - -## 3. Case details - -### VC-01: Studio-to-Runtime Profile write path - -- Preconditions: an isolated deployed mpa-agent accepts the validated Studio principal; resource prefix is `mpa-p0-e2e-*`; the live manifest is redacted. -- Command: `scripts/verify-mpa-p0-e2e.sh --manifest --case VC-01` (runner exists; the `VC-01` live driver stage is added with S1). -- Input: two Studio Agent draft/Profile apply requests carrying `sourceProfileId + digest` and no caller-assigned revision. -- Expected: Runtime prepare -> Profile apply/status -> update/replay -> delete succeeds; Runtime returns revisions 1 and 2; no Managed Agent API is called and cleanup leaves no residue. -- Raw local evidence: ignored `evidence/live//VC-01.json`, request IDs, version sequence, and cleanup report. -- Failure handling: a deployed failure blocks release and returns to the owning S1/S5 task; it does not invalidate the completed local M0 gate. Do not substitute an arkcli subprocess. - -### VC-02: unified RuntimePrincipal - -- Preconditions: live Studio OAuth/gateway, custom-JWT Runtime, A2A TIP, and a revocable test principal. -- Command: live runner `--case VC-02`; Runtime `uv run pytest -q tests/test_runtime_principal.py` (to add). -- Input: valid bearer/TIP, wrong issuer/audience/kid, expired or missing-claim token, cross-scope principal, and revoked principal. -- Expected: bearer and TIP map to the same principal; every invalid request returns 401/403 before side effects; JWKS rotation converges. -- Raw local evidence: ignored `evidence/live//VC-02.json` and JUnit; record only issuer, audience, TTL, and kid hash. -- Failure handling: a local contract failure blocks S1; a deployed issuer/JWKS/revocation failure blocks release. Do not bypass by disabling JWT, using a Runtime key, or trusting a user header. - -### VC-03: Studio Profile mapping and Runtime client contract - -- Preconditions: fixed Studio AgentDraft and Runtime Profile DTO/error fixtures. -- Command: `uv run --extra dev pytest tests/frontend/server/mpa/test_runtime_profile_client.py tests/frontend/server/mpa/test_profile_mapping.py -q` (`test_runtime_profile_client.py` exists; `test_profile_mapping.py` is added with S1). -- Input: deterministic AgentDraft normalization, supported/unknown fields, create/update preconditions, Runtime timeout, HTTP error, and non-JSON response. -- Expected: Profile DTO/digest are stable; bearer, idempotency key, and exactly one of `If-None-Match`/`If-Match` are forwarded; conflicts map to `409 profile_version_conflict`; request ID is retained; Managed Agent/OpenTOP call count is zero. -- Raw local evidence: ignored `evidence/contract//VC-03.xml` and fixture diff. -- Failure handling: return to Step 1 to fix the contract or adapter; do not loosen the schema or swallow errors. - -### VC-04: Profile preconditions and versions - -- Preconditions: Runtime local backend with empty-Profile and existing-Profile fixtures. -- Command: `PYTHONPATH=. SESSION_BACKEND=local uv run pytest -q tests/test_profile_apply.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-04.xml` (to add). -- Input: `If-None-Match`, `If-Match`, both headers, neither header, stale ETag, same operation key with same/different digest. -- Expected: respectively success, success, 400, 428, 412, idempotent replay/`409 idempotency_mismatch`; Runtime allocates exactly one new revision only on successful CAS and returns a strong ETag. -- Evidence: JUnit plus revision-count/current-row assertions. -- Failure handling: fix precondition/service/store and rerun; do not weaken conflict assertions. - -### VC-05: Runtime operation ledger - -- Preconditions: an in-memory store with an injectable clock and a 24-hour TTL fixture. -- Command: `PYTHONPATH=. SESSION_BACKEND=local uv run pytest -q tests/test_runtime_operations.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-05.xml` (to add). -- Input: same key with same/different hash for Profile/run/upgrade/continue, cross-principal access, and TTL boundaries. -- Expected: unique ledger, same-request replay, different-request 409, unauthorized records hidden, queryable state, and a pending Profile never becomes current. -- Evidence: JUnit, operation state sequence, and secret-free snapshots. -- Failure handling: fix ledger uniqueness/authorization/expiry logic; PostgreSQL races are revalidated by VC-18B/C. - -### VC-06: durable MPA lifecycle operation - -- Preconditions: a TOS fake supporting forbid-overwrite/ETag; Runtime/Profile/smoke adapters with injectable failpoints. -- Command: `uv run --extra dev pytest tests/frontend/server/mpa/test_agent_operations_repository.py tests/frontend/server/mpa/test_agent_operations_service.py tests/frontend/server/mpa/test_agent_routes.py -q` (to add). -- Input: create/update idempotency keys; lost first response; crashes before/after Runtime, Profile, and smoke; cleanup failure. -- Expected: both writes return `202 + operationId`; the same key restores the original operation; active list finds it; retry resumes from a safe stage; each side-effect count is at most one. -- Raw local evidence: ignored `evidence/automated//VC-06.xml`, TOS ETag, attempt/effect counts, and stage sequence. -- Failure handling: any duplicate Runtime/Profile effect or lost refresh state blocks S1. - -### VC-07: Profile field mapping - -- Preconditions: fixed Studio AgentDraft/Profile fixture and runtime Profile DTO. -- Command: VeADK `uv run --extra dev pytest tests/frontend/server/mpa/test_profile_mapping.py -q --junitxml=prd-spec/features/mpa-p0-productionization/evidence/contract//VC-07-veadk.xml`; Runtime `uv run pytest -q tests/test_profile_apply.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/contract//VC-07-runtime.xml` (targets to add). -- Input: all supported fields, Skill without version, unsupported Tool/Multiagent, Secret, and signed URL. -- Expected: map `sourceProfileId`, name, description, System, Model, Tools, Skills, MCP, Multiagent, and allowlisted Metadata; Runtime allocates `profileRevision`; resolve resource versions first; unsupported fields return 422; only Secret references cross the boundary. -- Raw local evidence: ignored `evidence/contract//VC-07.json` and JUnit from both repositories. -- Failure handling: fix mapping/allowlist; do not silently drop unknown fields and then claim applied. - -### VC-08: execution-config CAS - -- Preconditions: Runtime local backend, Session revision 1, and resource-resolver fixture. -- Command: Runtime `uv run pytest -q tests/test_session_execution_config.py tests/test_session_execution_config_service.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-08.xml`. -- Input: missing/stale/correct ETag, replace/clear/inherit, unavailable/revoked resources, and an active Turn. -- Expected: missing ETag returns 428 and stale ETag returns 412; configuration semantics are exact; failure creates no revision; the active Turn is unchanged and the next Turn uses the change. -- Evidence: JUnit plus revision/effective-reference/Turn-record snapshots. -- Failure handling: fix CAS/resolver/transaction; revalidate with two connections in VC-18B. - -### VC-09: explicit Profile-revision upgrade - -- Preconditions: Session pinned to N, N+1 Profile applied, and an invalid-target fixture available. -- Command: Runtime `uv run pytest -q tests/test_session_execution_config_service.py -k profile_upgrade --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-09.xml`. -- Input: valid/repeated/old-ETag/unapplied/missing/downgrade version and override/clear/inherit. -- Expected: target Profile becomes the new base, override/clear are retained, and inherit follows the new defaults; an invalid target is rejected atomically; the active Turn uses N and the next Turn uses N+1. -- Evidence: JUnit and before/after revision and Turn comparison. -- Failure handling: immediately block S2 if a failed upgrade changes the old revision. - -### VC-10: strong Run idempotency - -- Preconditions: Session/config fixture and a countable dispatcher. -- Command: `PYTHONPATH=. SESSION_BACKEND=local uv run pytest -q tests/test_run_acceptance.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-10.xml` (to add). -- Input: missing key/version, stale config, same key with same/different payload, and concurrent requests for the same Session. -- Expected: explicit 4xx; stale config fails before a side effect; same request returns the same invocation; different content returns 409; only one active Turn exists. -- Evidence: JUnit, operation/Turn/inbox rows, and effect count. -- Failure handling: any duplicate effect blocks S3. - -### VC-11: Outbox crash recovery - -- Preconditions: injectable dispatcher and commit/dispatch/writeback/terminal failpoints. -- Command: `PYTHONPATH=. SESSION_BACKEND=local uv run pytest -q tests/test_turn_dispatcher.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-11.xml` (to add). -- Input: each failpoint and Runtime restart. -- Expected: unique dispatch key; dispatcher attempts may retry, but the external A2A Task/Turn/side-effect count is exactly one; operation/Turn/inbox converge. -- Evidence: JUnit, attempt count, effect count, and recovery-state logs. -- Failure handling: duplicate external effects or a lost intent blocks S3; deleting duplicate records afterward is not an acceptable pass. - -### VC-12: Pause barrier - -- Preconditions: fake clock and a participant fixture with a primary executor plus two workers. -- Command: `PYTHONPATH=. SESSION_BACKEND=local uv run pytest -q tests/test_turn_participants.py tests/test_turn_control_api.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-12.xml` (first file to add). -- Input: all/partial ACKs, stale-generation ACK, lease expiry, pause/spawn race, and repeated/conflicting control key. -- Expected: paused only after all participants ACK the same generation; other cases remain pausing or become a classified failure; stale ACK is ignored; idempotent results remain stable. -- Evidence: JUnit and participant generation/ACK/lease timeline. -- Failure handling: an incorrect paused state or a stale ACK changing state blocks S4. - -### VC-13: Resume and continuation - -- Preconditions: fake clock, outbox dispatcher, and recoverable/unrecoverable owner fixtures. -- Command: `PYTHONPATH=. SESSION_BACKEND=local uv run pytest -q tests/test_turn_continuation.py tests/test_turn_safe_point_spike.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-13.xml` (first file to add). -- Input: paused for 299/300/301 seconds, viable/lost lease, restart, and repeated/concurrent continue. -- Expected: resume the original Turn at <=300 seconds; otherwise return only new-turn-required; explicit continue creates one linked Turn; outbox is recoverable. -- Evidence: JUnit, fake-clock timeline, old/new Turn, and dispatch intent. -- Failure handling: real sleeps, automatic new-Turn creation, or duplicate effects block S4. - -### VC-14: persistent SSE replay - -- Preconditions: persistent ADK events, inbox-terminal fixture, and rebuildable StreamHub. -- Command: `PYTHONPATH=. SESSION_BACKEND=local uv run pytest -q tests/test_session_sse_persistent_replay.py tests/test_session_sse_edges.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-14.xml` (first file to add). -- Input: valid/unknown cursor, cross-Pod/restart, persistent/live overlap, queued/running/error/aborted states, and different invocations. -- Expected: replay only events after the cursor; unknown cursor returns 410; text/usage/artifact/terminal each appears once; error/aborted are accurate; no terminal means no success; invocations do not mix. -- Evidence: JUnit and SSE `id/event/data` sequence JSONL. -- Failure handling: never replay everything for an unknown cursor; duplicate or incorrect terminal state blocks S3. - -### VC-15: Secret migration and leakage - -- Preconditions: schema 0 containing a canary secret and a fake secret provider that can succeed or fail. -- Command: `PYTHONPATH=. SESSION_BACKEND=local uv run pytest -q tests/test_secret_reference_migration.py tests/test_session_redaction.py tests/test_runtime_console_api.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-15.xml` (first file to add). -- Input: new plaintext write in AgentKit mode, interrupted/retried migration of an old value, legacy read-only path, and old-image rollback. -- Expected: reject new plaintext writes; clear only after successful conversion to a reference; retain on failure; retry without duplication; no canary in API/log/trace; fence incompatible rollback. -- Evidence: JUnit, before/after DB snapshots showing only empty/ref-type values, and secret scan. -- Failure handling: any leak or data loss blocks release. - -### VC-16: MpaAgentView and page isolation - -- Preconditions: BFF fixtures covering 0/1/N bindings, orphan, applied/target, and old Runtime. -- Command: `uv run --extra dev pytest tests/frontend/server/mpa/test_agent_view.py -q && npm --prefix frontend test` (first file plus `mpaAgents.test.mjs`/`mpaAgentDetail.test.mjs` to add). -- Input: every view state, same ID in different scopes, and a slow response during scope switching. -- Expected: the view model merges correctly; write gates are correct; MPA does not enter GitHub/generic-draft/evaluation paths; caches do not cross scopes; no Secret is exposed. -- Raw local evidence: ignored `evidence/automated//VC-16.json` and pytest/npm output. -- Failure handling: state misclassification or entry into a generic flow blocks S5. - -### VC-17: real browser - -- Preconditions: test-only scenario harness and gstack/Chromium available. -- Command: S1 runs BC-01/02 plus the BC-07 create-busy/IME subset; S2 runs BC-04/05; S3 runs BC-06; S4 runs BC-09; S5 strictly runs the complete BC-01 through BC-09 regression in [browser-cases.md](browser-cases.md). -- Input: creation/failure/refresh/two-client/upgrade/cursor/IME/narrow-viewport scenarios. -- Expected: slice subsets gate only their slice; `VC-17=pass` only after every S5 BC passes with no unhandled console error. -- Raw local evidence: ignored `evidence/browser///`. -- Failure handling: unavailable environment is `blocked`; Node tests do not substitute. - -### VC-18A: PostgreSQL foundation - -- Preconditions: Docker available; Runtime `docker-compose.test.yml`, schema-0 fixture, PG integration test, and Make target to add. -- Command: `make test-postgres TEST_SELECT='foundation or migration_harness'`. -- Input: PostgreSQL 16, schema 0, two connections, and restart/failpoint harness. -- Expected: container, migration runner, two connections, restart, and failpoint infrastructure work; base schema migration is reentrant; finally cleans up container/volume. -- Raw local evidence: ignored `evidence/postgres//VC-18A.xml`, image digest, schema dump, and cleanup. -- Failure handling: block S1; do not substitute in-memory tests. - -### VC-18B: PostgreSQL Profile/Session CAS - -- Preconditions: VC-18A and S2 implementation complete. -- Command: `make test-postgres TEST_SELECT='profile_cas or execution_config_cas or upgrade_cas'`. -- Input: two connections concurrently using the same ETag/Profile revision. -- Expected: one succeeds and one conflicts; no lost update; one current Profile/config revision. -- Raw local evidence: ignored `evidence/postgres//VC-18B.xml` and rows. -- Failure handling: block S2. - -### VC-18C: PostgreSQL Outbox/SSE - -- Preconditions: VC-18A and S3 implementation complete. -- Command: `make test-postgres TEST_SELECT='active_turn or ledger or outbox or sse_restart'`. -- Input: concurrent run, three failpoints, restart, and persistent cursor. -- Expected: one active Turn/effect; outbox converges; cursor replay is correct after restart. -- Raw local evidence: ignored `evidence/postgres//VC-18C.xml`. -- Failure handling: block S3. - -### VC-18D: PostgreSQL Participants/Continuation - -- Preconditions: VC-18A and S4 implementation complete. -- Command: `make test-postgres TEST_SELECT='participant or continuation'`. -- Input: concurrent ACK/lease/generation/continue. -- Expected: row lock/CAS are correct; full barrier; one linked Turn/effect. -- Raw local evidence: ignored `evidence/postgres//VC-18D.xml`. -- Failure handling: block S4. - -### VC-18E: PostgreSQL Secret migration - -- Preconditions: VC-18A and S5 Secret-reference implementation complete; schema 0 contains a canary plaintext value; the fake secret provider can succeed or fail. -- Command: `make test-postgres TEST_SELECT='secret_migration'`. -- Input: successful reference creation, provider failure, process interruption before clearing, restart/retry, and old-image rollback. -- Expected: clear only after reference success; retain on failure/interruption; no duplicate reference on retry; no canary in final DB/log/evidence; incompatible rollback is rejected. -- Raw local evidence: ignored `evidence/postgres//VC-18E.xml`, DB snapshots containing only empty/ref-type data, and secret scan. -- Failure handling: block S5 and release; local/fake results cannot substitute. - -### VC-19: compatibility, regression, and performance - -- Preconditions: S1 through S5 implementation and non-live Cases are complete, but `VC-21` has not run; M0 has recorded both SHAs/image digest/protocol versions. Run the compatibility preflight first, then VC-21, then rerun this Case's full regression/performance section as the final gate. -- Command: `scripts/verify-mpa-p0-contract.sh --manifest --matrix frontend/server/mpa/contracts/mpa-p0/compatibility-matrix.json`, followed by Runtime `make test && make coverage && make test-postgres`, VeADK `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" && npm --prefix frontend test && npm --prefix frontend run build && uv run --extra dev pre-commit run --all-files`. Performance runner: `scripts/benchmark-mpa-p0.py --warmup 5 --requests 100 --concurrency 10 --list-size 20`. -- Input: every PRD compatibility combination and fixed fixtures. -- Expected: matrix results match; MPA update/release/rollback write routes reject incompatible manifests before mutation; Runtime coverage >=95%; no regressions; performance report includes p50/p95/max/error rate. -- Raw local evidence: ignored `evidence/contract//VC-19.json` and test/build/coverage/benchmark output. -- Failure handling: a permitted combination failure or exceeded target blocks S5; a requirement outside the matrix returns to Step 1 first. -- Execution 2026-09-18: the S5-10 preflight passed with pinned VeADK `486dc06e429573ce43d6462b6dc960be4bc34ab6`, Runtime `eecc6e3115685e5cb86dcfbff4fb7c6ac7a10dee`, and deployed v62 image digest `sha256:6ac6a2712ecd1c7950125dc9afc6467373a08142fbd6c3577aba12f126079bef`. The live Runtime was `Ready` at version 62 and registry inspection matched the pinned digest. The real manifest matched `p0-codex-rest-v1`; negative fixtures returned the expected `worker_protocol_incompatible` and `invalid_runtime_image_digest` results. Raw local evidence: ignored `evidence/contract/s5-10-v62-20260918/`. This does not mark the post-VC-21 full regression/performance portion complete; that remains S5-13. - -### VC-20A: S1 live creation to first chat - -- Preconditions: M0 is green; S1 implementation is complete; isolated account/region/project, write/delete permissions, quota, cost cap, image digest, DB, and cleanup owner are in the manifest; ArkClaw endpoint is blocked. -- Command: `scripts/verify-mpa-p0-e2e.sh --manifest --case VC-20A` (to add). -- Input: fresh Agent, minimal Profile, and valid model. -- Expected: creation operation, Runtime, Profile, deterministic smoke, mpa-agent Session, and first A2A/worker/result succeed; Session create/read, A2A acceptance, and worker/tool dispatch use the shared authorizer with zero effects after revocation; S2-S4 upgrade/cursor/continue is not required. -- Raw local evidence: ignored `evidence/live//VC-20A/` containing manifest, operation, HTTP/SSE, trace, and cleanup. -- Failure handling: insufficient permission/quota is blocked; functional failure is fail; EXIT trap cleans up and cleanup failure blocks S1 separately. - -### VC-20B: complete live creation, identity, and Profile regression - -- Preconditions: M0 all green; isolated account/region/project, write/delete permissions, quota, cost cap, image digest, DB, and cleanup owner recorded in the manifest; ArkClaw endpoint blocked. -- Command: `scripts/verify-mpa-p0-e2e.sh --manifest --case VC-20B --cases AC-11,AC-1,AC-2,AC-6` (to add). -- Input: fresh Agent, minimal Profile, valid model, and valid/invalid/revoked identities. -- Expected: creation through first A2A/worker/result; Profile update/replay/conflict; safe rejection; interruption recovery; final zero-residue cleanup. -- Raw local evidence: ignored `evidence/live//VC-20B/` containing manifest, operation, HTTP/SSE, trace, and cleanup. -- Failure handling: insufficient permission/quota is blocked; functional failure is fail; an EXIT trap cleans up, and cleanup failure blocks separately. - -### VC-21: Runtime lifecycle, rollback, and deletion - -- Preconditions: VC-19 permits the target/rollback combination; live manifest contains current/target/rollback image digests; active-Session/shared-resource fixtures exist. -- Command: `VEADK_MPA_P0_LIVE=1 VEADK_MPA_P0_ASYNC_STAGE_TIMEOUT_SECONDS=900 scripts/verify-mpa-p0-e2e.sh --manifest --case VC-21 --execute`. -- Input: valid update/release, controlled failure, compatible rollback, incompatible downgrade, malformed compatibility manifest, delete preview with an active MPA operation, delete preview with an active Runtime Session, delete with idle visible Runtime Sessions, and final delete. -- Expected: every step has an operation ID/request ID/version timeline; controlled failure is recoverable; incompatible downgrade or manifest fails before mutation; compatible rollback passes smoke. When the target Runtime already runs the approved target image and AgentKit would reject a duplicate `ReleaseRuntime` call in `Ready` state, the rollback check records `noRollbackNeeded=true` and still requires execution-ready smoke. Delete preview reports blockers and cleanup stages before mutation; true active operations or active Sessions prevent deletion with `409`, while a recovered `failed_retryable` operation with applied Profile does not permanently block cleanup. Idle visible Sessions are deleted through Runtime `DELETE /api/v1/sessions/{sessionId}` before AgentKit Runtime deletion; final deletion leaves no residue. -- Raw local evidence: ignored `evidence/live//VC-21/` containing version timeline, platform responses, smoke trace, and cleanup report. -- Failure handling: any unsafe mutation, false success, or residual resource makes `AC-9=fail`. -- Execution 2026-09-18: `vc21-20260918-045` passed all VC-21 stages with v29 image digest `sha256:078519c796b03b298a95ef7a6614f92121bc124513dbb472a7496c95821ee723`; the runner returned `{"case":"VC-21","residue":[],"status":"passed"}` and raw evidence was captured locally under ignored `evidence/live/vc21-20260918-045/`. - -### VC-22: CLI parity - -- Preconditions: shared `MpaControlPlaneClient` and CLI commands implemented; controlled BFF fixture available. -- Command: `uv run --extra dev pytest tests/integrations/test_mpa_control_plane_client.py tests/cli/test_cli_mpa_control.py -q`. -- Input: `veadk mpa control` view, create/update, operation list/get/retry, Profile status/apply, Session config get/patch/profile-upgrade, delete preview, 401/403/409/412/428, old Runtime, and timeout/replay; no Managed Agent endpoint or direct Runtime URL is called. The legacy infrastructure command remains `veadk mpa create`. -- Expected: CLI does not import a FastAPI route; it shares schemas and `MpaControlPlaneClient` with Studio; writes carry a caller-persisted key and handle 202; CAS commands require the current ETag or Runtime revision; JSON is parseable; no bearer, Runtime credential, or secret-like Profile field is exposed. Exit codes are `0` success, `2` local usage/input, `3` authorization, `4` conflict/precondition, `5` retryable transport/server failure, and `1` other control-plane failure. Retry after timeout reuses the same key and does not repeat a write. Chat/Turn/Debug are not part of this Case because no shared Studio BFF CLI contract exists for them; the CLI must not compensate by bypassing Studio. -- Raw local evidence: ignored `evidence/automated//VC-22.xml`, UI/CLI response comparison, and secret scan. -- Failure handling: any semantic divergence, duplicate write, or leak blocks S5. - -## 4. Exit rules - -1. M0 requires the local subset of `VC-02`, `VC-03`, `VC-18A`, and the live-runner dry-run. Deployed `VC-01/02` execute at `S5-14`; `VC-04/05` run after S1 implements them. These Cases cannot form an M0 dependency cycle. -2. Each slice's own Cases and continuous security Cases must pass before the next slice begins. -3. Live Cases require explicit authorization; missing permission is `blocked` and cannot be replaced by a mock. -4. Any P0/P1 Case failure returns to the corresponding SDD/TDD Task for repair and rerun. -5. Step 7 may proceed to two-round Review only after all P0/P1 Cases pass; Step 10 must pass E2E before commit preparation. diff --git a/prd-spec/features/mpa-p0-productionization/2026-09-15-functional-validation-cases.zh.md b/prd-spec/features/mpa-p0-productionization/2026-09-15-functional-validation-cases.zh.md deleted file mode 100644 index 0ff0d7dd4..000000000 --- a/prd-spec/features/mpa-p0-productionization/2026-09-15-functional-validation-cases.zh.md +++ /dev/null @@ -1,286 +0,0 @@ -# MPA AgentKit P0 功能验证 Case - -- Change ID:`mpa-p0-productionization` -- 状态:`designed`;已执行至 S5-10 兼容性预检,其余 Case 继续推进 -- 日期:2026-09-15 -- English:[2026-09-15-functional-validation-cases.md](2026-09-15-functional-validation-cases.md) -- PRD:[MPA AgentKit P0 功能迁移](2026-09-15-mpa-p0-productionization-design.zh.md) -- 浏览器 Runbook:[browser-cases.zh.md](browser-cases.zh.md) - -## 1. 统一执行与证据约定 - -证据根目录为 `evidence/{automated,postgres,browser,contract,live}//`。执行前运行 `mkdir -p` 创建对应目录。每项记录 `caseId/timestamp/repositorySHA/runtimeImageDigest/agentkitOpenAPIVersion/workerProtocol/environment/command/status/expected/actual/artifactPaths/cleanupResult`;状态仅允许 `pass|fail|blocked|not_run|not_applicable`。日志、JSON、截图和 manifest 必须脱敏。 - -下列标记“需新增”的命令/文件是对应切片交付物,执行前必须先验证路径存在;它们不是当前已具备能力。Mock、内存、PostgreSQL、浏览器和 live cloud 证据不能相互替代。 - -## 2. 覆盖矩阵 - -| AC | Case | 切片 | -| --- | --- | --- | -| `AC-1` | `VC-06`, `VC-20A`, `VC-20B` | S1/live | -| `AC-2` | `VC-04`, `VC-05`, `VC-07`, `VC-20B` | M0/S1/live | -| `AC-3` | `VC-08`, `VC-09`, `VC-17`, `VC-18B` | S2/browser/PG | -| `AC-4` | `VC-10`, `VC-11`, `VC-18C` | S3/PG | -| `AC-5` | `VC-12`, `VC-13`, `VC-18D` | S4/PG | -| `AC-6` | `VC-02`, `VC-15`, `VC-18E`, `VC-20B` | M0/持续/live/PG | -| `AC-7` | `VC-14`, `VC-17`, `VC-18C` | S3/browser/PG | -| `AC-8` | `VC-16`, `VC-17` | S1-S5/browser | -| `AC-9` | `VC-19`, `VC-21` | S5/live | -| `AC-10` | `VC-16`, `VC-19`, `VC-22` | S5 | -| `AC-11` | `VC-02` 本地子集、`VC-03`、`VC-01`、`VC-20B` | M0 本地 / S5 live | -| `AC-12` | `VC-18A`, `VC-18B`, `VC-18C`, `VC-18D`, `VC-18E`, `VC-19` | M0-S5/PG | - -## 3. Case 明细 - -### VC-01:Studio 到 Runtime 的 Profile 写链路 - -- 前置:隔离部署的 mpa-agent 接受已校验 Studio principal;资源前缀 `mpa-p0-e2e-*`;live manifest 已脱敏。 -- 命令:`scripts/verify-mpa-p0-e2e.sh --manifest --case VC-01`(runner 已存在;`VC-01` live driver stage 随 S1 增加)。 -- 输入:两次 Studio Agent draft/Profile apply 请求,携带 `sourceProfileId + digest`,不携带调用方分配的 revision。 -- 预期:Runtime prepare -> Profile apply/status -> update/replay -> delete 成功;Runtime 分别返回 revision 1 和 2;不调用 Managed Agent API,清理后无残留。 -- 原始本地证据:被忽略的 `evidence/live//VC-01.json`、request IDs、版本序列、cleanup report。 -- 失败处理:部署态失败阻止发布并回到对应 S1/S5 task,不回溯否定已完成的本地 M0 门禁;不得改用 arkcli 子进程。 - -### VC-02:统一 RuntimePrincipal - -- 前置:真实 Studio OAuth/gateway、custom-JWT Runtime、A2A TIP、可撤权测试主体。 -- 命令:live runner `--case VC-02`;Runtime `uv run pytest -q tests/test_runtime_principal.py`(需新增)。 -- 输入:有效 bearer/TIP,错误 issuer/audience/kid,过期/缺 claim token,跨 scope 与已撤权主体。 -- 预期:bearer/TIP 映射相同 principal;所有非法请求在副作用前 401/403;JWKS 轮换收敛。 -- 原始本地证据:被忽略的 `evidence/live//VC-02.json` 与 JUnit;仅记录 issuer/audience/TTL/kid hash。 -- 失败处理:本地 contract 失败阻止 S1;部署态 issuer/JWKS/撤权失败阻止发布。禁止关闭 JWT、使用 Runtime key 或用户 Header 绕过。 - -### VC-03:Studio Profile 映射与 Runtime client contract - -- 前置:固定 Studio AgentDraft、Runtime Profile DTO/error fixture。 -- 命令:`uv run --extra dev pytest tests/frontend/server/mpa/test_runtime_profile_client.py tests/frontend/server/mpa/test_profile_mapping.py -q`(`test_runtime_profile_client.py` 已存在;`test_profile_mapping.py` 随 S1 增加)。 -- 输入:确定性的 AgentDraft 规范化、支持/未知字段、create/update precondition、Runtime timeout、HTTP 错误和非 JSON。 -- 预期:Profile DTO/digest 稳定;转发 bearer、幂等键以及 `If-None-Match`/`If-Match` 二选一;冲突映射 `409 profile_version_conflict`;保留 request ID;Managed Agent/OpenTOP 调用次数为零。 -- 原始本地证据:被忽略的 `evidence/contract//VC-03.xml` 与 fixture diff。 -- 失败处理:回 Step 1 修契约或修 adapter;不得放宽 schema/吞错。 - -### VC-04:Profile precondition 与版本 - -- 前置:Runtime local backend、空/已有 Profile 两种 fixture。 -- 命令:`PYTHONPATH=. SESSION_BACKEND=local uv run pytest -q tests/test_profile_apply.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-04.xml`(需新增)。 -- 输入:`If-None-Match`、`If-Match`、双 header、无 header、stale ETag、相同 operation key 的同/异 digest。 -- 预期:分别为成功、成功、400、428、412、幂等 replay/`409 idempotency_mismatch`;Runtime 只在 CAS 成功时分配一个新 revision 并返回强 ETag。 -- 证据:JUnit、revision count/current row 断言。 -- 失败处理:修 precondition/service/store 后重跑;不得弱化冲突断言。 - -### VC-05:Runtime operation ledger - -- 前置:可注入 clock 的内存 store;24 小时 TTL fixture。 -- 命令:`PYTHONPATH=. SESSION_BACKEND=local uv run pytest -q tests/test_runtime_operations.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-05.xml`(需新增)。 -- 输入:Profile/run/upgrade/continue 的同 key 同/异 hash、跨 principal、TTL 边界。 -- 预期:唯一 ledger、同请求 replay、异请求 409、越权不可见、状态可查询、pending Profile 不切 current。 -- 证据:JUnit、operation 状态序列和无 Secret 快照。 -- 失败处理:修 ledger 唯一键/授权/过期逻辑;PostgreSQL 竞态由 VC-18B/C 复验。 - -### VC-06:持久 MPA lifecycle operation - -- 前置:TOS fake 支持 forbid-overwrite/ETag;Runtime/Profile/smoke adapter 可注入 failpoint。 -- 命令:`uv run --extra dev pytest tests/frontend/server/mpa/test_agent_operations_repository.py tests/frontend/server/mpa/test_agent_operations_service.py tests/frontend/server/mpa/test_agent_routes.py -q`(需新增)。 -- 输入:create/update 幂等键;首次响应丢失;Runtime/Profile/smoke 前后 crash;cleanup 失败。 -- 预期:均返回 `202 + operationId`;同 key 恢复原 operation;active list 找回;retry 从安全阶段继续;副作用 effect 至多一次。 -- 原始本地证据:被忽略的 `evidence/automated//VC-06.xml`、TOS ETag、attempt/effect count、阶段序列。 -- 失败处理:任何重复 Runtime/Profile effect 或刷新丢失均阻断 S1。 - -### VC-07:Profile 参数映射 - -- 前置:固定 Studio AgentDraft/Profile fixture 和 runtime Profile DTO。 -- 命令:VeADK `uv run --extra dev pytest tests/frontend/server/mpa/test_profile_mapping.py -q --junitxml=prd-spec/features/mpa-p0-productionization/evidence/contract//VC-07-veadk.xml`;Runtime `uv run pytest -q tests/test_profile_apply.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/contract//VC-07-runtime.xml`(需新增目标文件)。 -- 输入:全部已支持字段、无版本 Skill、不支持 Tool/Multiagent、Secret/signed URL。 -- 预期:映射 `sourceProfileId`、name、description、System、Model、Tools、Skills、MCP、Multiagent、allowlisted Metadata;`profileRevision` 由 Runtime 分配;资源版本先解析;不支持字段 422;只传 Secret ref。 -- 原始本地证据:被忽略的 `evidence/contract//VC-07.json` 与两仓 JUnit。 -- 失败处理:修映射/allowlist;不得将未知字段静默丢弃后声称 applied。 - -### VC-08:execution-config CAS - -- 前置:Runtime local backend,Session revision 1,资源 resolver fixture。 -- 命令:Runtime `uv run pytest -q tests/test_session_execution_config.py tests/test_session_execution_config_service.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-08.xml`。 -- 输入:缺失/stale/correct ETag,replace/clear/inherit,失效/撤权资源,活动 Turn。 -- 预期:缺 ETag 428、stale 412;配置语义准确;失败无 revision;活动 Turn 不变、下一 Turn 生效。 -- 证据:JUnit、revision/effective refs/Turn record 快照。 -- 失败处理:修 CAS/resolver/transaction;双连接由 VC-18B 复验。 - -### VC-09:显式 Profile revision 升级 - -- 前置:Session 固定 N,N+1 Profile 已 applied;可构造无效 target。 -- 命令:Runtime `uv run pytest -q tests/test_session_execution_config_service.py -k profile_upgrade --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-09.xml`。 -- 输入:合法/重复/旧 ETag/未 applied/不存在/降级版本,override/clear/inherit。 -- 预期:目标 Profile 作新 base,保留 override/clear,inherit 跟新默认;无效目标原子拒绝;活动 Turn 用 N、下一 Turn 用 N+1。 -- 证据:JUnit、升级前后 revision 与 Turn 对照。 -- 失败处理:失败升级若改变旧 revision 立即阻断 S2。 - -### VC-10:Run 强幂等 - -- 前置:Session/config fixture、可计数 dispatcher。 -- 命令:`PYTHONPATH=. SESSION_BACKEND=local uv run pytest -q tests/test_run_acceptance.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-10.xml`(需新增)。 -- 输入:缺 key/version、stale config、同 key 同/异 payload、同 Session 并发。 -- 预期:明确 4xx;stale 在副作用前失败;同请求同 invocation;异内容 409;仅一个 active Turn。 -- 证据:JUnit、operation/Turn/inbox rows 与 effect count。 -- 失败处理:任何重复 effect 阻断 S3。 - -### VC-11:Outbox crash recovery - -- 前置:可注入 dispatcher 与 commit/dispatch/writeback/terminal failpoint。 -- 命令:`PYTHONPATH=. SESSION_BACKEND=local uv run pytest -q tests/test_turn_dispatcher.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-11.xml`(需新增)。 -- 输入:各 failpoint 和 Runtime restart。 -- 预期:唯一 dispatch key;允许 dispatcher attempt 重试,但外部 A2A Task/Turn/副作用 effect count 恰好 1;operation/Turn/inbox 收敛。 -- 证据:JUnit、attempt count、effect count、恢复状态日志。 -- 失败处理:重复外部 effect 或丢失 intent 阻断 S3;不得靠事后删重复记录通过。 - -### VC-12:Pause barrier - -- 前置:fake clock,primary + 两 worker participant fixture。 -- 命令:`PYTHONPATH=. SESSION_BACKEND=local uv run pytest -q tests/test_turn_participants.py tests/test_turn_control_api.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-12.xml`(需新增首个文件)。 -- 输入:全部/部分 ACK、旧 generation ACK、lease expiry、pause 与 spawn 竞态、重复/冲突 control key。 -- 预期:同 generation 全 ACK 才 paused;其余保持 pausing/分类失败;旧 ACK 无效;幂等结果稳定。 -- 证据:JUnit、participant generation/ACK/lease 时间线。 -- 失败处理:误报 paused 或旧 ACK 改状态阻断 S4。 - -### VC-13:Resume 与 continuation - -- 前置:fake clock、outbox dispatcher、可恢复/不可恢复 owner fixture。 -- 命令:`PYTHONPATH=. SESSION_BACKEND=local uv run pytest -q tests/test_turn_continuation.py tests/test_turn_safe_point_spike.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-13.xml`(需新增首个文件)。 -- 输入:暂停 299/300/301 秒、lease viable/lost、restart、重复/并发 continue。 -- 预期:≤300 秒恢复原 Turn;其余只返回 new-turn-required;显式 continue 创建唯一 linked Turn;outbox 可恢复。 -- 证据:JUnit、fake-clock timeline、old/new Turn 与 dispatch intent。 -- 失败处理:真实 sleep、自动建新 Turn 或重复 effect 均阻断 S4。 - -### VC-14:持久 SSE replay - -- 前置:持久 ADK events、inbox terminal fixture、可重建 StreamHub。 -- 命令:`PYTHONPATH=. SESSION_BACKEND=local uv run pytest -q tests/test_session_sse_persistent_replay.py tests/test_session_sse_edges.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-14.xml`(需新增首个文件)。 -- 输入:合法/未知 cursor、跨 Pod/重启、persistent/live 重叠、queued/running/error/aborted、不同 invocation。 -- 预期:仅 replay cursor 后事件;未知 cursor 410;文本/usage/artifact/terminal 各一次;error/aborted 准确;无终态不成功;不串 invocation。 -- 证据:JUnit 和 SSE `id/event/data` 序列 JSONL。 -- 失败处理:禁止未知 cursor 全量重播;重复或错误终态阻断 S3。 - -### VC-15:Secret migration 与泄漏 - -- 前置:schema 0 含 canary secret,fake secret provider 可成功/失败。 -- 命令:`PYTHONPATH=. SESSION_BACKEND=local uv run pytest -q tests/test_secret_reference_migration.py tests/test_session_redaction.py tests/test_runtime_console_api.py --junitxml=/prd-spec/features/mpa-p0-productionization/evidence/automated//VC-15.xml`(需新增首个文件)。 -- 输入:AgentKit mode 新明文写、旧值迁移中断/重试、legacy 只读、旧镜像回滚。 -- 预期:新明文写拒绝;成功转 ref 后清空;失败不清空;重试不重复;API/log/trace 无 canary;不兼容回滚被 fence。 -- 证据:JUnit、仅显示 empty/ref-type 的 DB 前后快照、secret scan。 -- 失败处理:任何泄漏或数据丢失阻断发布。 - -### VC-16:MpaAgentView 与页面隔离 - -- 前置:BFF fixture 覆盖 0/1/N binding、orphan、applied/target、旧 Runtime。 -- 命令:`uv run --extra dev pytest tests/frontend/server/mpa/test_agent_view.py -q && npm --prefix frontend test`(需新增首个文件与 `mpaAgents.test.mjs`/`mpaAgentDetail.test.mjs`)。 -- 输入:所有 view 状态、不同 scope 的同 ID、慢响应切换。 -- 预期:view-model 合并正确;写门禁正确;MPA 不进入 GitHub/generic draft/evaluation;缓存不串;无 Secret。 -- 原始本地证据:被忽略的 `evidence/automated//VC-16.json`、pytest/npm 输出。 -- 失败处理:状态误判或误入通用流阻断 S5。 - -### VC-17:真实浏览器 - -- 前置:test-only scenario harness 与 gstack/Chromium 可用。 -- 命令:S1 执行 BC-01/02 与 BC-07 create-busy/IME 子集;S2 执行 BC-04/05;S3 执行 BC-06;S4 执行 BC-09;S5 严格执行 [browser-cases.zh.md](browser-cases.zh.md) 的 BC-01~BC-09 全量回归。 -- 输入:创建/失败/刷新/双客户端/upgrade/cursor/IME/窄屏场景。 -- 预期:各切片子集通过仅作为切片门禁;S5 全部 BC pass 且 console 无未处理错误后才将 `VC-17=pass`。 -- 原始本地证据:被忽略的 `evidence/browser///`。 -- 失败处理:环境不可用为 `blocked`;Node tests 不替代。 - -### VC-18A:PostgreSQL 基座 - -- 前置:Docker 可用;需新增 Runtime `docker-compose.test.yml`、schema-0 fixture、PG integration test 与 Make target。 -- 命令:`make test-postgres TEST_SELECT='foundation or migration_harness'`。 -- 输入:PostgreSQL 16、schema 0、两连接、restart/failpoint harness。 -- 预期:容器、migration runner、双连接、重启与 failpoint 基础设施可用;基础 schema migration 可重入;finally 清理 container/volume。 -- 原始本地证据:被忽略的 `evidence/postgres//VC-18A.xml`、image digest、schema dump、cleanup。 -- 失败处理:阻断 S1;不可用内存替代。 - -### VC-18B:PostgreSQL Profile/Session CAS - -- 前置:VC-18A 与 S2 实现完成。 -- 命令:`make test-postgres TEST_SELECT='profile_cas or execution_config_cas or upgrade_cas'`。 -- 输入:两连接同 ETag/profile revision 并发。 -- 预期:一个成功、一个冲突;无 lost update;唯一 current Profile/config revision。 -- 原始本地证据:被忽略的 `evidence/postgres//VC-18B.xml` 与 rows。 -- 失败处理:阻断 S2。 - -### VC-18C:PostgreSQL Outbox/SSE - -- 前置:VC-18A 与 S3 实现完成。 -- 命令:`make test-postgres TEST_SELECT='active_turn or ledger or outbox or sse_restart'`。 -- 输入:并发 run、三 failpoint、restart、persistent cursor。 -- 预期:唯一 active Turn/effect;outbox 收敛;重启后 cursor replay 正确。 -- 原始本地证据:被忽略的 `evidence/postgres//VC-18C.xml`。 -- 失败处理:阻断 S3。 - -### VC-18D:PostgreSQL Participants/Continuation - -- 前置:VC-18A 与 S4 实现完成。 -- 命令:`make test-postgres TEST_SELECT='participant or continuation'`。 -- 输入:并发 ACK/lease/generation/continue。 -- 预期:row lock/CAS 正确;全 barrier;唯一 linked Turn/effect。 -- 原始本地证据:被忽略的 `evidence/postgres//VC-18D.xml`。 -- 失败处理:阻断 S4。 - -### VC-18E:PostgreSQL Secret migration - -- 前置:VC-18A 与 S5 Secret reference 实现完成;schema 0 含 canary 明文值;fake secret provider 可成功或失败。 -- 命令:`make test-postgres TEST_SELECT='secret_migration'`。 -- 输入:reference 创建成功、provider 失败、清空前进程中断、重启重跑、旧镜像回滚。 -- 预期:成功建 ref 后才清空;失败/中断保留旧值;重跑不重复 ref;最终 DB/log/evidence 无 canary;不兼容回滚被拒绝。 -- 原始本地证据:被忽略的 `evidence/postgres//VC-18E.xml`、仅显示 empty/ref-type 的 DB 快照和 secret scan。 -- 失败处理:阻断 S5 和发布;不得以 local/fake 结果替代。 - -### VC-19:兼容、回归与性能 - -- 前置:S1~S5 的实现和非 live Case 已完成,但 `VC-21` 尚未执行;M0 已写入双方 SHA/image digest/协议版本。先以兼容矩阵 runner 完成发布前 preflight,再执行 VC-21;VC-21 后重跑本 Case 的全量回归/性能部分作为最终门禁。 -- 命令:`scripts/verify-mpa-p0-contract.sh --manifest --matrix frontend/server/mpa/contracts/mpa-p0/compatibility-matrix.json`,随后 Runtime `make test && make coverage && make test-postgres`,VeADK `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" && npm --prefix frontend test && npm --prefix frontend run build && uv run --extra dev pre-commit run --all-files`。性能 runner 为 `scripts/benchmark-mpa-p0.py --warmup 5 --requests 100 --concurrency 10 --list-size 20`。 -- 输入:PRD 全部兼容组合和固定 fixtures。 -- 预期:矩阵结果吻合;MPA update/release/rollback 写路由在副作用前拒绝不兼容 manifest;Runtime coverage ≥95%;无回归;性能报告含 p50/p95/max/error rate。 -- 原始本地证据:被忽略的 `evidence/contract//VC-19.json`、测试/build/coverage/benchmark 输出。 -- 失败处理:允许组合失败或指标超限阻断 S5;矩阵外需求先回 Step 1。 -- 2026-09-18 执行记录:S5-10 使用固定 VeADK `486dc06e429573ce43d6462b6dc960be4bc34ab6`、Runtime `eecc6e3115685e5cb86dcfbff4fb7c6ac7a10dee` 和已部署 v62 镜像 digest `sha256:6ac6a2712ecd1c7950125dc9afc6467373a08142fbd6c3577aba12f126079bef` 完成预检。实时 Runtime 为 version 62、状态 `Ready`,registry 查询结果与固定 digest 一致。真实 manifest 命中 `p0-codex-rest-v1`;负向 fixture 按预期返回 `worker_protocol_incompatible` 和 `invalid_runtime_image_digest`。原始证据已写入被忽略的本地 `evidence/contract/s5-10-v62-20260918/`。这不代表 VC-21 后的最终全量回归/性能部分已完成;该部分仍属于 S5-13。 - -### VC-20A:S1 live 创建至首轮对话 - -- 前置:M0 全绿;S1 实现完成;隔离账号/region/project、写删权限、配额、成本上限、镜像 digest、DB 和 cleanup owner 已写入 manifest;ArkClaw endpoint 阻断。 -- 命令:`scripts/verify-mpa-p0-e2e.sh --manifest --case VC-20A`(需新增)。 -- 输入:全新 Agent、最小 Profile、合法 model。 -- 预期:创建 operation、Runtime、Profile、deterministic smoke、mpa-agent Session 和首轮 A2A/worker/result 成功;Session create/read、A2A acceptance、worker/tool dispatch 使用共享 authorizer,撤权后 effect count 为 0;不要求 S2~S4 的 upgrade/cursor/continue。 -- 原始本地证据:被忽略的 `evidence/live//VC-20A/` 中 manifest、operation、HTTP/SSE、trace、cleanup。 -- 失败处理:权限/配额不足记 blocked;功能失败记 fail;EXIT trap 清理,清理失败单独阻断 S1。 - -### VC-20B:完整 live 创建、身份与 Profile 回归 - -- 前置:M0 全绿;隔离账号/region/project、写删权限、配额、成本上限、镜像 digest、DB、cleanup owner 已写入 manifest;ArkClaw endpoint 阻断。 -- 命令:`scripts/verify-mpa-p0-e2e.sh --manifest --case VC-20B --cases AC-11,AC-1,AC-2,AC-6`(需新增)。 -- 输入:全新 Agent、最小 Profile、合法 model、有效/非法/撤权身份。 -- 预期:创建到首轮 A2A/worker/result;Profile update/replay/conflict;安全拒绝;中断可续;最终清理零残留。 -- 原始本地证据:被忽略的 `evidence/live//VC-20B/` 中 manifest、operation、HTTP/SSE、trace、cleanup。 -- 失败处理:权限/配额不足记 blocked;功能失败记 fail;EXIT trap 清理,清理失败单独阻断。 - -### VC-21:Runtime 生命周期、回滚与删除 - -- 前置:VC-19 允许目标/回滚组合;live manifest 含 current/target/rollback image digest;有 active Session/shared resource fixture。 -- 命令:`VEADK_MPA_P0_LIVE=1 VEADK_MPA_P0_ASYNC_STAGE_TIMEOUT_SECONDS=900 scripts/verify-mpa-p0-e2e.sh --manifest --case VC-21 --execute`。 -- 输入:合法 update/release、受控失败、兼容 rollback、不兼容 downgrade、malformed compatibility manifest、存在 active MPA operation 的删除预览、存在 active Runtime Session 的删除预览、包含可见 idle Runtime Session 的删除执行、最终 delete。 -- 预期:每步有 operation ID/request ID/version timeline;受控失败可恢复;不兼容 downgrade 或 manifest 在 mutation 前拒绝;兼容回滚后 smoke 通过。若目标 Runtime 已运行被批准的目标镜像,而 AgentKit 会拒绝在 `Ready` 状态重复 `ReleaseRuntime`,rollback 检查记录 `noRollbackNeeded=true`,但仍必须通过 execution-ready smoke。删除预览在 mutation 前返回阻断原因和清理阶段;真正 `active` 的 operation 或 active Session 以 `409` 阻止删除,已恢复且 Profile 为 `applied` 的 `failed_retryable` operation 不再永久阻塞清理。idle 可见 Session 先通过 Runtime `DELETE /api/v1/sessions/{sessionId}` 删除,再删除 AgentKit Runtime;最终删除且零残留。 -- 原始本地证据:被忽略的 `evidence/live//VC-21/` 中版本时间线、平台响应、smoke trace、cleanup report。 -- 失败处理:任一 unsafe mutation、错误成功状态或残留资源均使 `AC-9=fail`。 -- 2026-09-18 执行记录:`vc21-20260918-045` 使用 v29 镜像 digest `sha256:078519c796b03b298a95ef7a6614f92121bc124513dbb472a7496c95821ee723` 通过全部 VC-21 阶段;runner 返回 `{"case":"VC-21","residue":[],"status":"passed"}`,原始证据已写入被忽略的本地 `evidence/live/vc21-20260918-045/`。 - -### VC-22:CLI parity - -- 前置:共享 `MpaControlPlaneClient` 和 CLI 命令已实现,受控 BFF fixture 可用。 -- 命令:`uv run --extra dev pytest tests/integrations/test_mpa_control_plane_client.py tests/cli/test_cli_mpa_control.py -q`。 -- 输入:`veadk mpa control` 的 view、create/update、operation list/get/retry、Profile status/apply、Session config get/patch/profile-upgrade、delete preview,以及 401/403/409/412/428、旧 Runtime、timeout/replay;不调用 Managed Agent endpoint,也不直连 Runtime URL。历史基础设施命令仍为 `veadk mpa create`。 -- 预期:CLI 不 import FastAPI route;与 Studio 共用 schema 和 `MpaControlPlaneClient`;写操作携带由调用方持久复用的 key 并处理 202;CAS 命令要求当前 ETag 或 Runtime revision;JSON 可解析;不输出 bearer、Runtime credential 或含 Secret 的 Profile 字段。退出码固定为:`0` 成功、`2` 本地用法/输入错误、`3` 鉴权错误、`4` 冲突/前置条件错误、`5` 可重试 transport/server 失败、`1` 其他控制面失败。timeout 后重试复用同一 key,不重复写。Chat/Turn/Debug 因缺少共享 Studio BFF CLI 契约,不属于本 Case;CLI 不得通过绕过 Studio 补偿。 -- 原始本地证据:被忽略的 `evidence/automated//VC-22.xml`、UI/CLI 响应对照与 secret scan。 -- 失败处理:任何语义分叉、重复写或泄漏阻断 S5。 - -## 4. 准出规则 - -1. M0 只要求 `VC-02` 本地子集、`VC-03`、`VC-18A` 与 live runner dry-run。部署态 `VC-01/02` 在 `S5-14` 执行;`VC-04/05` 由 S1 实现后执行,不得形成前置环。 -2. 每个切片自身 Case 与持续安全 Case 通过后才进入下一切片。 -3. live Case 需要明确授权;缺权限为 `blocked`,不得用 mock 替代。 -4. 任一 P0/P1 Case 失败,回对应 SDD/TDD Task 修复并重跑。 -5. Step 7 所有 P0/P1 Case 通过后才进入两轮 Review;Step 10 E2E 通过后才进入提交。 diff --git a/prd-spec/features/mpa-p0-productionization/2026-09-15-m0-evidence.md b/prd-spec/features/mpa-p0-productionization/2026-09-15-m0-evidence.md deleted file mode 100644 index 2ac37ff7c..000000000 --- a/prd-spec/features/mpa-p0-productionization/2026-09-15-m0-evidence.md +++ /dev/null @@ -1,26 +0,0 @@ -# M0 Gate Evidence - -Date: 2026-09-15 - -## Result - -After the scope correction, Managed Agent is not a dependency or release gate. M0 local foundation tasks are green, including the replacement typed Runtime Profile client. Live identity/Profile verification moves to the deployed-environment release gate and does not block local S1 TDD. - -## Verified evidence - -| Area | Result | Evidence | -| --- | --- | --- | -| Managed Agent API probe | not_applicable | It proved Managed Agent is a separate product surface; the adapter and fixtures were removed from P0 | -| Runtime principal | pass | 66 principal/auth/TIP/A2A tests | -| Runtime Profile client | pass | 3 mapping/digest/precondition/auth/error contract tests | -| PostgreSQL 16 foundation | pass | `make test-postgres`: 4 passed; container, network, and volume removed | -| Runtime test seams | pass | 4 clock/dispatcher/participant/failpoint tests | -| Compatibility preflight | pass | 3 manifest/matrix tests | -| Browser scenario harness | pass | 4 isolation/auth/state tests | -| Live runner contract | pass | 3 dry-run/authorization/checkpoint/cleanup tests | -| Benchmark report | pass | 2 schema/percentile validation tests | -| Combined VeADK M0 targeted suite | pass | 15 current-scope tests after removing the Managed Agent adapter | -| Managed Agent writes | not_applicable | `ManagedAgentNotOpen` no longer blocks MPA delivery because P0 does not call this product | -| Live OAuth/TIP/JWKS and Profile endpoint | not_run | Execute as a release gate after the new Profile endpoint is deployed; local rejection/projection contracts remain required during implementation | - -No credentials, upstream messages, or personal data are retained in this evidence. diff --git a/prd-spec/features/mpa-p0-productionization/2026-09-15-m0-evidence.zh.md b/prd-spec/features/mpa-p0-productionization/2026-09-15-m0-evidence.zh.md deleted file mode 100644 index fc4a98da1..000000000 --- a/prd-spec/features/mpa-p0-productionization/2026-09-15-m0-evidence.zh.md +++ /dev/null @@ -1,26 +0,0 @@ -# M0 门禁证据 - -日期:2026-09-15 - -## 结论 - -范围修正后,Managed Agent 不再是依赖或发布门禁。M0 本地基座任务均已通过,包括替代后的类型化 Runtime Profile client。live identity/Profile 验证移至部署环境 release gate,不阻塞本地 S1 TDD。 - -## 已验证证据 - -| 范围 | 结果 | 证据 | -| --- | --- | --- | -| Managed Agent API 探测 | not_applicable | 已证明 Managed Agent 是独立产品面;相关 adapter 和 fixture 已从 P0 删除 | -| Runtime principal | pass | 66 个 principal/auth/TIP/A2A 测试通过 | -| Runtime Profile client | pass | 3 个映射/摘要/precondition/鉴权/错误 contract 测试通过 | -| PostgreSQL 16 foundation | pass | `make test-postgres`:4 passed;container、network、volume 均删除 | -| Runtime test seam | pass | 4 个 clock/dispatcher/participant/failpoint 测试通过 | -| 兼容性 preflight | pass | 3 个 manifest/matrix 测试通过 | -| 浏览器 scenario harness | pass | 4 个隔离/鉴权/状态测试通过 | -| Live runner contract | pass | 3 个 dry-run/授权/checkpoint/cleanup 测试通过 | -| Benchmark report | pass | 2 个 schema/percentile 校验测试通过 | -| VeADK M0 targeted 汇总 | pass | 删除 Managed Agent adapter 后,当前范围 15 tests 通过 | -| Managed Agent 写链路 | not_applicable | P0 不调用该产品,`ManagedAgentNotOpen` 不再阻塞 MPA 交付 | -| Live OAuth/TIP/JWKS 与 Profile endpoint | not_run | 新 Profile endpoint 部署后作为 release gate 执行;实现阶段持续要求本地拒绝/投影契约 | - -证据不保留凭据、上游错误消息或个人数据。 diff --git a/prd-spec/features/mpa-p0-productionization/2026-09-15-mpa-p0-productionization-design.md b/prd-spec/features/mpa-p0-productionization/2026-09-15-mpa-p0-productionization-design.md deleted file mode 100644 index 9b3b586e8..000000000 --- a/prd-spec/features/mpa-p0-productionization/2026-09-15-mpa-p0-productionization-design.md +++ /dev/null @@ -1,438 +0,0 @@ -# MPA AgentKit P0 Functional Migration and Delivery - -- Change ID: `mpa-p0-productionization` -- Status: `approved`; implementation and browser validation complete through S5-12, with S5-13 through S5-15 evidence gates pending -- Created / revised: 2026-09-15 / 2026-09-20 -- Chinese: [2026-09-15-mpa-p0-productionization-design.zh.md](2026-09-15-mpa-p0-productionization-design.zh.md) -- Contracts: [Studio MPA Control Plane](../../../specs/studio-mpa-control-plane/README.md), [MPA Runtime Provisioning](../../../specs/mpa-runtime-provisioning/README.md), [MPA Runtime Control](../../../specs/mpa-runtime-control/README.md) - -## 1. Conclusion and boundary - -AgentKit Studio becomes the sole MPA management entry, and `agentkit-mpa-agent` is the execution Runtime. P0 does not develop or depend on the Managed Agent product, its CRUD APIs, its release versions, or its Session API. Studio reuses its existing Agent creation form and resource selectors as the authoring surface, normalizes the submitted configuration into an immutable MPA Profile revision, and applies that Profile directly to mpa-agent. The MPA Agent detail page is the unified place to view Profile status, apply or reapply the Agent-level Profile, manage Session execution configuration, inspect diagnostics, and delete the Runtime. It must not send MPA users to the generic Runtime update capability path when that path depends on `/list-apps` and cannot represent an A2A-only mpa-agent Runtime. mpa-agent does not build a template center, expert marketplace, review/install workflow, or a separate publishing product. `arkclaw-team` is read-only functional reference material, not a new-path dependency; none of its historical users, sessions, templates, credentials, IDs, or run records are migrated. - -P0 delivers the core path: create/update an MPA Agent, prepare a Runtime, apply the Profile, create a Session, execute chat and workers, manage session execution configuration, pause/resume/continue, retrieve history, diagnose with Debug/Trace, and update or recover the Runtime. Current VeADK/Runtime mainline has independently delivered message-channel binding, Feishu/DingTalk/WeCom configuration, shared APIG deployment, persistent Runtime/database provisioning, Skill-Space provisioning, and complete artifact-answer replay. Those are reusable integration foundations, but their provider-specific acceptance remains owned by the MPA-channel/deployment contracts and does not replace this P0's AC/VC gates. Website integration, Resource Library, and global Search remain outside this P0. - -| System | Sole responsibility | Explicit exclusion | -| --- | --- | --- | -| AgentKit platform | Existing Studio creation surface; Skill/Tool/Environment/Vault and other platform resources; Runtime lifecycle; account authorization | P0 does not add or depend on Managed Agent CRUD/version/Session | -| VeADK Studio/BFF | Validate the existing Agent draft, normalize an MPA Profile revision, orchestrate Runtime/Profile application, and present status | Does not create a second platform product or drive workers directly | -| `agentkit-mpa-agent` | Executable Profile projection, Session/Turn, resource resolution, worker orchestration, execution control, diagnostics | No template/expert/review-install or independent Agent-version product | -| `mpa-codex-worker` | Existing isolated execution protocol | No change unless a tested protocol gap is demonstrated | -| `arkclaw-team` | Read-only functional reference | No dependency, dual-write, migration, or old-ID/API compatibility | - -## 2. Verified facts - -The 2026-09-15 static audit used VeADK `34658d75`, agentkit-mpa-agent `2f5e039`, and arkclaw-team `c15d611`. Two test accounts were used for read-only validation; no cloud resource was created, updated, or deleted. Temporary arkcli profiles were removed. - -| Fact | Evidence | Design conclusion | -| --- | --- | --- | -| Studio already has an Agent creation draft covering model, system instruction, tools, skills, MCP, multi-agent structure, and deployment settings | `frontend/src/create/types.ts`, `CustomCreate.tsx`, and `deployAgentkitProject` | Reuse this data and UI; add only MPA Profile normalization and Runtime handoff | -| Managed Agent APIs exist but the current test account reports `ManagedAgentNotOpen` for create | 2026-09-15 live probe | Managed Agent is explicitly outside P0 and cannot gate MPA delivery | -| AgentKit Runtime list returned Ready MPA Runtimes; SDK supports create/get/list/update/release/delete/listVersions/listInstances/getLogs | Test-account and local SDK/VeADK adapter evidence | Reuse platform Runtime APIs; VeADK adds orchestration, readiness, and recovery, not another lifecycle | -| Skill, Environment, and Vault provider queries work with real accounts | Successful read-only cloud validation | Resource selection and grants reuse AgentKit APIs; mpa-agent adapts references for execution | -| Runtime already exposes `/api/v1/agents`, Session CRUD/run/SSE/events/MCP, A2A pause/resume, health/readiness, and console | agentkit-mpa-agent routes and tests | Extend existing interfaces; add only Profile sync, execution-config version, and runtime-owned continuation | -| `mpa_agents` is already a single-Runtime configuration revision table | `app/stores/agent_config.py` | Reuse it as an execution projection and add source/apply fields; no Agent-definition table | -| `RunRequest` currently has only `content` with `extra=allow`, and run has no `Idempotency-Key` | `schemas/session.py`, `api/v1/sessions.py` | Add explicit execution-config version and durable idempotency; do not rely on unchecked extension fields | -| Pause/resume stores only the last command key/action | `a2a_task_control.py` | Reuse generation state, but add request hash and durable result replay | -| Studio timeout continuation constructs a prompt and calls `send` | `frontend/src/App.tsx` | Replace with a runtime-owned continuation API to preserve linkage and side-effect safety | -| My Agents has an MPA category but its create action only covers general/Sandbox | `frontend/src/ui/MyAgents.tsx` | Wire MPA tab/header/empty-state creation to native AgentKit creation | - -The previously recorded “321 runtime tests pass” is historical baseline evidence. Those tests were not rerun for this documentation-only revision and are not reported as current results. - -## 3. User scenarios and requirements - -### Scenario A: create and first-run an MPA Agent - -**Given** a user creates an Agent in AgentKit Studio and selects the MPA Runtime type. - -**When** Studio validates the Agent draft, creates an immutable MPA Profile revision, prepares or reuses its Tool and Runtime, and applies that Profile to the Runtime. - -**Then** the UI shows one staged creation operation; it reports “runnable” only after Profile application and an execution-ready smoke pass, after which the user can create a Session and receive the first streaming result. - -### Scenario B: update an Agent Profile - -**Given** the applied MPA Profile is at revision N. - -**When** the user saves changes to Model, System, Tools, Skills, MCP, or Multiagent. - -**Then** Studio submits the new digest with the current ETag and a stable idempotency key. Runtime atomically allocates revision N+1. The same key/digest returns the original revision, the same key with a different digest returns `409 idempotency_mismatch`, and a stale ETag returns `412 profile_changed`. - -### Scenario C: edit session execution configuration - -**Given** a Session is at execution configuration version 6, composed from Agent defaults plus Session overrides. - -**When** browser A changes the model to M2 while browser B attempts a Skill change against version 6. - -**Then** A creates version 7; B receives `412 execution_config_changed` with current version 7 and cannot overwrite A. An already-running Turn continues with its recorded version 6; the next Turn uses version 7. - -A “session execution configuration version” is a monotonically revisioned runtime record of one Session's execution choices, not a copy of platform resources. For example, it records `skillId=s-1, version=3`, `environmentId=e-2, version=5`, and a model override, while AgentKit continues to own Skill content and Environment secrets. On Turn start, the accepted version and resolved result are recorded so an in-flight Turn cannot be changed by later edits. - -### Scenario D: pause, resume, and timed-out continuation - -**Given** a Turn has a primary executor and two workers. - -**When** the user pauses. - -**Then** it becomes `paused` only after every active participant reaches a safe point; partial acknowledgement remains `pausing` or ends in a classified timeout. Resume continues the same Turn within 300 seconds while its lease is viable. Past the boundary or when unrecoverable, only an explicit user action causes the Runtime to atomically create a linked new Turn. - -### Scenario E: refresh and failure recovery - -**Given** a request fails after its execution record is committed but before dispatch, or the browser refreshes during streaming. - -**When** the client retries with the same idempotency key or reconnects with an event cursor. - -**Then** the Runtime returns the original invocation/result and resumes at the cursor without a duplicate Turn, event, artifact, or usage increment. - -## 4. Core design - -### 4.1 Wiring the Studio MPA Profile to the Runtime - -Create/update is a Studio-to-Runtime operation: - -1. The browser submits the existing Studio Agent draft to VeADK BFF. No Managed Agent API is called. -2. BFF validates resource references and normalizes the accepted draft into `sourceProfileId + profileDigest`. It does not allocate a revision and must not forward unchecked browser fields. -3. BFF calls mpa-agent Profile apply. The Runtime validates version, references, and permission before writing a new `mpa_agents` revision. -4. BFF queries apply status. Failure preserves the last applied MPA Profile revision and a retryable operation; it does not report the MPA Agent as runnable. -5. A new Session pins the selected Profile revision at creation. An active Turn never hot-switches. - -The BFF reuses existing Studio draft parsing and AgentKit resource clients. It adds a typed mpa-agent Profile client rather than a Managed Agent OpenTOP adapter. Runtime allocates the unique `profileRevision` inside the successful ETag/CAS transaction and returns it; BFF never preallocates a revision. A conflict returns `409 profile_version_conflict` plus current revision and never overwrites automatically. - -P0 does not introduce a self-signed assertion service. Studio already holds a middleware-validated user bearer token in OAuth/gateway mode, and AgentKit Runtime custom JWT authorization validates the same issuer/client. BFF forwards only that validated bearer token. mpa-agent projects REST bearer JWT and A2A TIP into one `RuntimePrincipal` and applies business-scope authorization on new writes. The implemented S4-06 Runtime slice adds a shared `RuntimePrincipalAuthorizer` seam and rechecks the current grant before `/run`, resume, `/continue`, model/workload TIP or API-key resolution, sandbox run credential preparation, and tool dispatch. Runtime carries only a normalized, non-secret principal snapshot across accepted Turn/inbox recovery; it never stores the bearer token. Local claim/rejection and fake-revocation contracts gate S1/S4-06; issuer, audience/client, TTL, JWKS rotation, real revocation lookup, and one authorized Profile apply/status request remain mandatory deployed-environment release gates. Neither phase may fall back to browser headers, Runtime keys, or disabled JWT. - -| Studio AgentDraft field | Runtime field/behavior | Mapping rule | -| --- | --- | --- | -| Studio draft ID | `sourceProfileId` | Stable source identity for audit and idempotency; not a Managed Agent ID | -| `Name`, `Description` | `name`, `description` | Copy and validate length | -| `System` | `agentsMd` | Runtime instruction body; retain a source digest | -| `Model.Id` | `model` | Trim; reject provider URLs or credentials | -| `Tools[]` | `tools[]` | Retain type/name/enabled/permission policy; unsupported types return `422 unsupported_profile_field` | -| `Skills[]` | `skills[]` | Pin `SkillId + Version`; BFF resolves omitted versions first | -| `MCPServers[]` | `mcp` | Map to ID allowlists/scoped grants; pass only secret references | -| `Multiagent` | Runtime subagent configuration | Preserve roles, Agent references, and versions; unsupported shapes fail explicitly | -| `Metadata`, `Tags` | `metadata` | Allowlist non-secret keys; classification remains `veadk:agent-type=mpa` | -| Managed Agent-specific fields | Not accepted | Managed Agent CRUD/version/Session is outside P0; MPA Session overrides are owned by runtime `session_execution_configs` | - -### 4.2 Reused, extended, and new interfaces - -| Interface | Type | Change | -| --- | --- | --- | -| `GET /web/mpa/agents/{mpaInstanceId}/view`, `POST /web/mpa/agents`, `PATCH /web/mpa/agents/{mpaInstanceId}` | new MPA BFF orchestration | Browser calls BFF only. Create/update accepts the existing Studio Agent draft shape, normalizes an MPA Profile, and calls Runtime APIs; it never calls Managed Agent CRUD/version APIs. Create/update require a browser-persisted `Idempotency-Key` and return `202 + operationId` | -| `GET /web/mpa/agents/{mpaInstanceId}/delete-preview` | new BFF orchestration | Browser calls this before single MPA Runtime deletion. Query accepts `runtimeId` and `region`. BFF resolves the authorized MPA Runtime binding, reads Runtime `profile-status`, calls existing Runtime `GET /api/v1/sessions?include_a2a=true`, and returns `canDelete`, `blockers`, visible session counts, active-session summaries, and cleanup stages | -| `POST /web/delete-runtime` with MPA Runtime tag | extend existing BFF route | Existing request keeps `runtimeId` and `region`; `agentCategory` and `mpaInstanceId` are optional hints. The server rechecks the Runtime tag `veadk:agent-type=mpa` before using MPA cleanup. MPA delete refuses active operations and active sessions before mutation, deletes visible idle/terminal Runtime Sessions through existing Runtime APIs, then deletes the AgentKit Runtime | -| AgentKit Resource/Environment/Skill/Tool/Vault APIs | reuse | BFF provides authorized lookup and version resolution; Runtime consumes stable references. AgentKit Managed Agent Session is outside the P0 path | -| AgentKit Runtime create/get/list/update/release/delete/versions/instances/logs | reuse | VeADK adapter handles provision, state normalization, and recovery steps | -| `GET /web/mpa/agent-operations?status=active`, `GET /web/mpa/agent-operations/{operationId}`, `POST .../{operationId}/retry` | new BFF orchestration | Persist create/update operations with TOS CAS and authorize by principal. Active list or same-key replay recovers a lost initial response; retry resumes from the last safe stage | -| `PUT /api/v1/agents/{mpaInstanceId}/profile` | new | Payload carries `sourceProfileId` and digest, not a caller-assigned revision. Runtime allocates and returns `profileRevision`. First apply accepts only `If-None-Match: *`; later apply accepts only `If-Match: ""`; both headers returns `400`, neither returns `428`; response carries a strong ETag | -| `GET /api/v1/agents/{mpaInstanceId}/profile-status` | new | Returns `pending/applying/applied/failed`, applied/target versions, safe error, and retryability | -| `GET /api/v1/sessions/{sessionId}/execution-config` | new | Returns current session execution-config version, ETag, Profile-default revision, overrides, effective references, and invalid references. The implemented P0 subset initializes this state only for MPA Sessions that carry `mpaInstanceId + profileRevision` | -| `PATCH /api/v1/sessions/{sessionId}/execution-config` | new | Requires `If-Match` CAS; supports category replace/clear/inherit; affects only the next Turn. The implemented P0 subset supports `model` and `mcpServers`; other categories return `422 unsupported_execution_config_category` until their resource resolver exists | -| `POST /api/v1/sessions/{sessionId}/profile-upgrade` | new | Body contains `targetProfileRevision`; headers contain current execution-config `If-Match` and `Idempotency-Key`; target Profile must be applied. An active Turn may coexist but only the next Turn changes; downgrade returns `409 downgrade_not_allowed`. Runtime persists the request hash and safe result in SQL `runtime_operations`; same-key/same-target retries replay across Runtime processes, while same-key/different-target returns `409 idempotency_mismatch` | -| `POST /api/v1/sessions/{sessionId}/run` | extend | Explicitly adds `executionConfigVersion`; requires `Idempotency-Key`; returns the accepted version and invocation | -| Existing A2A submission | extend | Agent Card advertises `urn:veadk:mpa:execution:v1`; `params.metadata.veadkExecution` carries `executionConfigVersion` and `idempotencyKey`, both included in canonical request hash. Legacy callers without it may use only the default revision and no Session overrides | -| Existing `GET/POST .../control` | extend | Preserves URLs; adds participant state, request hash, and result replay without breaking old-capability clients | -| `POST /api/v1/a2a/tasks/{taskId}/continue` | new | Runtime atomically creates a linked Turn; requires idempotency key and expected generation | -| `GET /api/v1/runtime-operations/{operationId}` | new | Principal/target-authorized async status and safe result for Profile/run/upgrade/continue; never returns secrets | -| Session CRUD/SSE/events/MCP/health/readiness/console | reuse | Add scope checks, cursors, and structured linkage; no duplicate endpoints | - -Minimal Profile apply request: - -```json -{ - "sourceProfileId": "studio-agent-example", - "sourceProfileDigest": "sha256:example", - "profile": { - "name": "Research Agent", - "system": "...", - "model": {"id": "model-example"}, - "tools": [], - "skills": [{"skillId": "s-example", "version": "3"}], - "mcpServers": [], - "multiagent": null, - "metadata": {} - } -} -``` - -First apply carries only `If-None-Match: *`; an existing projection carries only `If-Match: ""`. They are mutually exclusive. Successful apply/status responses carry `profileRevision` and `ETag: ""`. The same operation key/digest is a successful replay, the same key with another digest returns `409 idempotency_mismatch`, and stale ETag returns `412 profile_changed`. - -BFF persists an MPA lifecycle operation before Runtime creation/Profile apply under `veadk-studio/v4/users/{principal}/mpa-agent-operations/{operationId}.json`, using TOS `forbid_overwrite` creation and ETag conditional updates. Create and update require a client-persisted `Idempotency-Key`; BFF uniquely creates by `principal + operationKind + mpaInstanceId/createIntent + key + requestHash`, and both endpoints return `202 + operationId`. The same request returns the original operation; changed content conflicts. `GET /web/mpa/agent-operations?status=active` recovers a lost first response. Creation is `runtime_preparing -> profile_applying -> smoke_running -> runnable`; update is `profile_applying -> succeeded`; both may enter `failed_retryable|failed_terminal`. It stores MPA instance ID, from/target Profile revisions, Profile digest, Runtime ID, Profile operation ID, stage, retry count, safe error, and timestamps, never credentials. Recovery reconciles Runtime binding plus Profile status and resumes from the last safe stage instead of repeating a write. - -Local Studio development may use a process-local operation repository only when `veadk studio --dev` is active and Studio TOS is not configured. This keeps local Profile apply and retry flows testable against a real Runtime while preserving the same operation ID, request-hash idempotency, and ETag compare-and-swap semantics. It is not durable and is not release evidence; non-dev deployments still require Studio TOS and fail Profile write routes with `mpa_operation_service_unavailable` when storage is absent. - -### 4.3 Idempotency, errors, and performance - -Profile apply, profile-upgrade, and continuation use `runtime_operations` where a reusable async status/replay envelope is needed, unique by `principal + operation + target + Idempotency-Key`, retaining canonical request hash, response snapshot, resource IDs, and a 24-hour expiry. Run acceptance uses `turn_execution_records` as its dedicated Turn ledger because the durable replay value is the accepted `turnId/invocationId/operationId` plus the frozen execution configuration. execution-config PATCH uses only revision/ETag CAS; pause/resume uses only `a2a_task_controls` generation, request hash, and replay result, without duplicate ledger writes. Same key/hash returns the original result; same key/different hash returns `409 idempotency_mismatch`. A pending Profile never becomes the current `mpa_agents` revision; only a success transaction switches the applied revision. Run and continuation write `turn_execution_records` plus a dispatch intent in one transaction, followed by an idempotent dispatcher. The contract does not promise distributed ACID with external AgentKit or the A2A SDK TaskStore. - -The common error shape is `error.code/message/requestId/retryable/currentState`. Key codes are `profile_version_conflict`, `execution_config_changed`, `session_busy`, `unsupported_profile_field`, `resource_unavailable`, `idempotency_mismatch`, and `not_execution_ready`. Existing endpoint envelopes change only after capability-version negotiation. - -MPA deletion is intentionally not a new distributed transaction. The preview and execution both recompute current blockers from server-side state. Active operation and active Session blockers are authoritative at execution time, so a stale browser preview cannot force deletion. A repeated delete after one Session has already been removed treats that Session's `404` as already cleaned and continues with the remaining stages; deleting the AgentKit Runtime remains the final platform side effect. Current preview scope is the target Runtime API visible to the authorized caller, not a cross-user global Session index. - -| Operation | P0 server-side target (caller network excluded) | Constraint | -| --- | --- | --- | -| Profile apply acceptance/replay | p95 <= 500 ms | Validate IDs, version, digest, and permission synchronously; resolve remote detail asynchronously | -| Session execution-config GET/PATCH | p95 <= 300 ms | One transaction; do not hold a database lock during remote resource fetch | -| Run acceptance | p95 <= 500 ms | Return accepted/operation ID without waiting for model completion | -| Pause/resume/continue acceptance | p95 <= 500 ms | Return target state; participant convergence is observed asynchronously | -| First SSE event | p95 <= 2 s | Warm Runtime; excludes model first-token SLA | -| List/detail | p95 <= 1 s | Server pagination, default 20/max 100; no N+1 Runtime/resource scan | -| MPA delete preview | p95 <= 1 s | One Runtime binding/profile lookup and one bounded Runtime Session list; no cross-region scan when `runtimeId` is supplied | - -Performance is measured after five warm-up requests over at least 100 requests, concurrency 10, a fixed 20-item list, and same-region networking. Reports include p50/p95/max, error rate, and tested revision. Model first-token and Runtime cold-start latency are reported separately and do not count as API-acceptance latency. Until run, these are targets, not passing evidence. - -### 4.4 State machines - -Runtime operation: `accepted -> running -> succeeded | failed_retryable | failed_terminal`. Profile apply currently completes synchronously after acceptance, so it normally transitions directly from `accepted` to `succeeded` or `failed_terminal`; its public Profile result remains `status=applied` on success. A `failed_retryable` operation may return to `running`; terminal states cannot move to another terminal outcome. State updates use compare-and-swap so concurrent completions cannot overwrite each other. - -Runtime readiness: `prepared -> resource_created -> transport_ready -> profile_applied -> execution_ready -> verified`, with `failed_retryable` or `failed_terminal` reachable from each stage. The UI reports “created and runnable” only at `verified`. - -Turn: `queued -> running -> pausing -> paused -> resuming -> running -> completed|failed|cancelled`. Partial participant acknowledgement remains `pausing`; an ACK from an old generation is ignored. The implemented S4-02 Runtime slice wires the existing control API, A2A primary executor, and Codex worker interruption path through `ParticipantBarrierService`: pause creates the next participant generation, the task becomes `paused` only after every current-generation active participant has acknowledged a safe point, late workers that start after the task is already `paused` still receive pause and ACK the current generation before resume, and completed/failed/cancelled workers are marked terminal so they do not block later pauses. The implemented S4-03 slice keeps same-Turn resume valid at the exact 300-second boundary when the task owner lease is still viable, returns `new_turn_required` when the pause is older than 300 seconds, when the task owner lease is lost, or when a participant is `lost|failed`, and keeps same-key `new_turn_required` retries idempotent. Resume returns the aggregate task to `running` and seeds the next participant generation from the previous paused generation. The implemented S4-04 Runtime slice exposes `POST /api/v1/a2a/tasks/{taskId}/continue`: after `new_turn_required`, it uses the old task invocation to find the previous business Turn, writes a new `turn_execution_records` row with `continuation_of=`, reuses the frozen execution-config snapshot and dispatch payload, closes the old active dispatch row, and schedules the existing inbox/outbox worker. The implemented S4-05 Studio slice removes the prompt-based continuation fallback: when resume returns `new_turn_required`, Studio calls Runtime `/continue`, then subscribes to Runtime `/sse` with the returned `invocationId` and existing `lastEventId`, without issuing an ordinary `/run` or fabricating a prompt. The implemented S4-06 Runtime slice rechecks the current RuntimePrincipal grant before accepting run/resume/continue side effects and before background secret/tool effects. If the injected authorizer reports a revoked grant, resume and continue return `403 runtime_action_forbidden` before state mutation or continuation acceptance, and accepted inbox execution fails before model TIP, workload TIP, OpenViking API key, sandbox run credentials, or sandbox/tool dispatch are used. The implemented S4-07 browser gate verifies the end-to-end Studio loopback behavior: partial worker acknowledgement remains `pausing`, all participants reaching safe points exposes resume, same-Turn resume returns to `running`, an interrupted task with `resumeDisposition=new_turn_required` is shown as a user-readable checkpoint continuation state, browser refresh does not auto-create a continuation, and only an explicit click calls Runtime `/continue` followed by `/sse`. - -Session execution configuration has no separate business status: every successful PATCH creates a monotonic revision. Validation failure creates no revision and returns invalid references; a Turn's accepted revision is immutable. - -Session Profile upgrade shares the configuration revision but must use its dedicated API. The target Profile becomes the new base, explicit override/clear values are retained, inherit categories take target defaults, and effective references are recomputed. Any missing, revoked, or incompatible resource rejects the entire operation before the transaction; the old revision remains intact and no non-executable revision is created. One success transaction updates `session_meta.profile_revision/execution_config_revision`. Same key/hash returns the same new revision; concurrent ETag change returns `412`. - -Delete state machine: `preview -> blocked | ready -> cleaning_sessions -> deleting_runtime -> deleted | failed_retryable`. `blocked` is returned before any mutation when the MPA operation store has a true `active` create/update operation, when the Runtime binding is missing or ambiguous, when the Runtime Profile is orphaned, or when Runtime Sessions are in `queued|running|pausing|paused|resuming`. A stale `failed_retryable` operation no longer blocks deletion after the Runtime binding is `bound` and `profile-status` reports `applied`, because the Runtime has already converged and the user must be able to clean up the resource. `cleaning_sessions` deletes only visible idle/terminal Runtime Sessions and treats a per-session `404` as already cleaned. `deleting_runtime` calls the AgentKit Runtime delete API only after the Runtime-side cleanup stage succeeds. - -## 5. Data model - -### 5.1 Reuse rules - -- Studio is the authoring authority for the accepted MPA Profile; mpa-agent `mpa_agents` persists immutable executable revisions. Do not add Managed Agent, template, expert, review/install, or publishing-version tables. -- AgentKit remains authoritative for Skill, Tool, Environment, Vault, and Resource. Runtime stores only IDs, pinned versions, digests, and secret references. -- Do not modify AgentKit-owned tables, migrate ArkClaw tables, or create old-ID mappings. -- New columns are additive; every new store has PostgreSQL and in-memory implementations. - -### 5.2 Minimum P0 changes - -| Model | Change | Purpose | -| --- | --- | --- | -| `mpa_agents` | Add `source_profile_id`, `source_profile_digest`, `apply_status`, `apply_operation_id`, `apply_error_code`, `agent_api_key_ref`, `model_api_key_ref`, `secret_migration_status`, and `secret_migration_operation_id`; use the existing Runtime-assigned revision as `profileRevision` | Store immutable executable MPA Profile revisions and apply state without a second revision allocator. S5-01 keeps legacy plaintext columns only as migration input/one-version compatibility; AgentKit mode rejects new plaintext writes and clears old values only after protected references exist | -| `mpa_meta` | Add `agentkit_mode`, `workspace_id`, `bootstrap_manifest_revision`, `readiness_phase`, `finalization_operation_id`, `last_error_code`; credentials stay protected or referenced | Prevent ArkClaw fallback in AgentKit mode and support staged recovery | -| `session_meta` | Add `account_id`, `workspace_id`, `mpa_instance_id`, `profile_revision`, `execution_config_revision` | Establish Session scope and current configuration revision | -| `session_execution_configs` | New: `app_name`, `session_id`, `revision`, strong `etag`, `mpa_instance_id`, `profile_revision`, `profile_default_revision`, JSON `overrides`, JSON `effective_refs`, JSON `invalid_refs`, `updated_by`, timestamps. The primary key is `(app_name, session_id, revision)`; current reads use `(app_name, session_id, revision DESC)`. SQL CAS uses a per-session PostgreSQL advisory transaction lock before reading current revision and appending the next immutable row. | Persist the “session execution configuration version” and replace browser localStorage authority. It stores only stable references and choices, not AgentKit resource bodies or secrets. Example: revision 1 records Profile 3 defaults plus Skill `skill-1@3`; revision 2 records a model override and Skill `skill-1@4`; revision 1 remains readable for an already accepted Turn. | -| `turn_execution_records` | New: task/invocation/session, accepted config revision, resolved refs, model, policy revision, secret refs, dispatch key/hash/status, `continuation_of`, timestamps | Freeze actual Turn configuration and support crash recovery/continuation | -| `a2a_turn_participants` | New: task, generation, participant ID/kind/status, safe point, checkpoint, lease, ACK/terminal times | Prove every active worker and primary executor has paused | -| `a2a_task_controls` | Add command request hash/result reference; add approval fields only when a real P0 high-risk tool flow requires them | Complete pause/resume idempotency without prebuilding a generic policy product | -| `runtime_operations` | New: unique principal/operation/target/key, request hash, state, response snapshot, resource IDs, expiry | Durable idempotency and common status for Profile lifecycle and future long-running control operations; run acceptance itself is owned by `turn_execution_records` | -| Trace/lifecycle stores | Add task, config revision, runtime/worker version indexes where needed | Enable structured Debug joins without duplicating large bodies | - -Do not add `mpa_agent_definitions`, `mpa_agent_definition_versions`, `turn_continuations`, `policy_evaluations`, or generic `approval_requests`. MPA Profile revisions use the existing `mpa_agents` table; `turn_execution_records.continuation_of` carries continuation linkage; authorization is rechecked before every side effect and recorded on the Turn/trace. Approval columns are added to `a2a_task_controls` only if a real P0 high-risk tool flow needs durable approval. - -Ordered migration is `runtime_operations -> additive mpa_agents/mpa_meta/session_meta columns -> session_execution_configs -> turn_execution_records -> a2a_turn_participants -> secret reference columns/indexes -> remaining indexes/constraints`, with a persisted schema version. Deploy schema expansion first, then dual-version-readable code, then enable new writes; reject rollback whenever old code cannot safely read new state. AgentKit mode rejects new plaintext `agent_api_key/model_api_key` writes and converts/clears any existing new-platform values into protected references. S5-01a also registers legacy plaintext values read during migration in a process-local value-aware redaction registry before any provider call or diagnostic emission. API access logs, client-visible Session output, Runtime Console responses, lifecycle details, trace persistence, and `runtime_operations.response_snapshot/resource_ids` pass through the same redaction boundary. Runtime operation status responses omit principal, idempotency key, and request hash. - -## 6. Studio page adaptation - -| Entry | P0 handling | Reason | -| --- | --- | --- | -| My Agents / MPA tab | Reuse the existing Studio creation form and submit its draft as an MPA Profile, then start Runtime/Profile orchestration | MPA category is visible but cannot currently create | -| Creation/deployment progress | Show staged operation: Runtime preparing, Profile applied, execution-ready; support retry | Profile acceptance and Runtime readiness are asynchronous stages | -| MPA detail/Profile revisions | BFF `GET /web/mpa/agents/{mpaInstanceId}/view` returns `MpaAgentView`: scope, MPA instance ID, current Profile revision, Runtime ID/version/readiness, active operation/stage, capabilities, and safe error. The detail page exposes a dedicated `Profile configuration` section for MPA Agents, derived from the current Studio draft/profile projection, and applies changes through `/web/mpa/agents` or `/web/mpa/agents/{mpaInstanceId}` | Zero Runtime is `runtime_missing`; one unique MPA Runtime is manageable; multiple is `binding_ambiguous` and blocks writes. An `orphan_runtime` means the Runtime exists but no MPA Profile has been applied yet, so Studio may still perform first Profile apply with `If-None-Match: *`; Session configuration and diagnostics remain blocked until the Profile is applied | -| New chat/Composer | Create/select the mpa-agent business Session and submit explicit execution-config version plus idempotency key | mpa-agent is the sole Session/Turn authority; no dual Session mapping | -| Pause/resume/history | Use runtime authority and continuation API; refresh with event cursor | Current prompt fallback loses linkage | -| Session config/MCP/topology | Server CAS for inherit/override/invalid/next-Turn behavior; show real participants | Replace localStorage execution authority | -| Workspace/Environment | Resolve resource-collection ID, authorization workspace scope, and worker mount ID separately in BFF | These IDs have different semantics and are not interchangeable | -| Usage/Debug/Trace/system | Reuse existing pages and add Agent/Session/Turn/runtime/worker linkage and redaction | The core path must be diagnosable and recoverable | -| MPA deletion | Show delete impact preview before confirmation; block confirmation when active operations or active sessions exist; execute cleanup through BFF | Users must understand which Runtime sessions and diagnostics will be cleaned before the irreversible Runtime delete | -| Review Center | Preserve existing non-MPA behavior; add no MPA template/expert review entry in P0 | This release builds only the MPA Profile execution path, not a catalog product | -| Applications/GitHub/coding | Preserve and capability-gate MPA targets | These workflows are not MPA runtime management | - -Resource Library, Search, scheduled tasks, Feishu, and website integration keep their current behavior in this release. No MPA adapter is added and they are not acceptance gates. - -## 7. Work items and order - -| Task | Main work | Deliverable/dependency | -| --- | --- | --- | -| `M0` Feasibility and test foundation | Freeze Studio Profile mapping; verify Studio-to-runtime principal locally; add PostgreSQL two-connection lane, fault injection, cross-repo version manifest, and E2E resource/cleanup script | **Hard gate:** local contract and PostgreSQL foundation must pass; live identity/Profile validation runs once a deployed endpoint exists | -| `T-2` Bootstrap and identity | Change VeADK provision/meta seed and runtime startup/auth for AgentKit mode, staged readiness, and recoverable operation | Fresh deployment does not call ArkClaw; depends on trusted identity wiring | -| `S1` Create to first chat | Existing Studio create UI, durable MPA operation, Runtime/Profile apply, deterministic smoke, retry/reconcile, minimum UI | Delivers `AC-1`, `AC-2`; security cases run continuously | -| `S2` Session versioning | execution-config, explicit profile-upgrade, ETag/CAS, Turn freeze, real PostgreSQL concurrency | Delivers `AC-3` and D-1 | -| `S3` Run and refresh recovery | `runtime_operations`, Turn/dispatch intent, crash recovery; reuse persistent ADK events plus inbox terminal, unknown cursor returns `410 cursor_expired` | Delivers `AC-4`, `AC-7` | -| `S4` Multi-worker lifecycle | Participant barrier, pause/resume, fake-clock 300-second boundary, lease loss, restart, transactional-outbox continuation | Delivers `AC-5` independently | -| `S5` Management closure | MPA detail/version view-model, Runtime update/release/delete/rollback, Debug/Trace, CLI parity, old-Runtime gate | Delivers `AC-8` through `AC-10` | - -M0 and S1 through S5 pass strict serial gates but may land and roll back independently; this XL scope is not forced into one PR. - -## 8. Test and acceptance plan - -| ID | Required evidence | -| --- | --- | -| `AC-1` | With ArkClaw endpoints blocked, create an MPA Agent from a Studio AgentDraft, reach execution-ready, and complete first A2A/worker/result without calling Managed Agent APIs | -| `AC-2` | MPA Profile revision N+1 applies; repeated apply returns the same result; stale ETag, replay with a different digest, and unsupported fields fail safely | -| `AC-3` | Two-client Session-config CAS: one succeeds and one gets `412`; active Turn stays unchanged and next Turn uses the new version | -| `AC-4` | Same run key does not repeat execution; failure after record/before dispatch recovers; same key/different payload conflicts | -| `AC-5` | Primary plus two workers: full/partial pause, resume, stale ACK, lease loss, restart, before/after 300 seconds, repeated continuation | -| `AC-6` | Forged identity, cross-owner access, and revoked run/resume/secret use fail before side effects; ordinary output contains no secrets | -| `AC-7` | SSE cursor replay has no duplicate text, terminal, usage, or artifact; missing terminal is not displayed as success | -| `AC-8` | MPA create/detail/version/chat/config/history/Debug cover loading/empty/error/denied/stale/retry, narrow viewport, keyboard, and IME | -| `AC-9` | Runtime update/release/delete uses real platform APIs; MPA update/release/rollback writes run compatibility preflight before mutation; failures return recoverable IDs; compatible rollback reaches execution-ready and passes smoke | -| `AC-10` | UI and CLI share principal, versions, and error semantics; old Runtimes are explicitly read-only/unsupported without insecure fallback | -| `AC-11` | REST bearer/TIP map to one RuntimePrincipal in contract tests; before release, a deployed environment verifies issuer/audience/JWKS/revocation and one authorized Profile apply/status flow | -| `AC-12` | PostgreSQL two-connection tests cover Profile CAS, Session CAS, one active Turn, operation replay, outbox crash recovery, schema upgrade, and allowed dual-version combinations | - -During documentation, verify paired languages, links, paths, identifiers, and diff whitespace. During implementation, run targeted agentkit-mpa-agent tests first, then VeADK backend/frontend tests, build, browser checks, and authorized isolated cloud E2E. Record each result as `pass/fail/blocked/not_run/not_applicable`; skipped does not mean passed. - -### 8.1 Executable verification foundation - -- Fault injection: dispatcher interface, injectable clock, participant probe, and failpoints at operation commit, dispatch, and terminal boundaries. Tests must not depend on random sleeps. -- PostgreSQL: M0 adds Runtime `docker-compose.test.yml` (pinned PostgreSQL 16) and `make test-postgres`, executing `tests/integration/test_p0_postgres_contract.py`. It starts an isolated container, loads the old-schema fixture, uses two independent connections and one Runtime process restart for CAS/locking/outbox/migration, then removes container and volume. In-memory stores cannot prove `AC-3/4/5/9/12`. -- Browser: start an isolated fake BFF/Runtime with `VEADK_MPA_TEST_SCENARIOS=1`, then use local gstack/Chromium and `browser-cases.md`. The flag is accepted only with `APP_ENV=test` and a loopback bind; otherwise startup fails. Test-only scenario APIs support seed/reset/barrier/call-count and do not exist in production routing. Raw evidence is written to ignored local `evidence/browser///`; unavailable browser means `AC-8=blocked`. -- Execution-ready smoke: `POST /api/v1/readiness/execution-smoke` does not rely on model tool choice. Runtime first executes a minimal tool-disabled model prompt and verifies its terminal, then directly calls existing Worker APIs through `CodexWorkerClient.create_session/start_turn/get_turn` and replays final events with `follow=false` for an isolated Worker Turn `printf mpa-smoke-`. Idempotency keys derive from the creation operation ID, with at most one retry and 120 seconds total. Worker `/v1/codex-worker/ready` must return `ready=true`; the Worker Turn must reach a terminal state and return readable replay events. The marker match is diagnostic-only (`markerObserved`) because the worker may summarize command output. Failures are classified by stage, such as `smoke_model_failed`, `smoke_worker_not_ready`, `smoke_worker_turn_wait_failed`, or `smoke_worker_events_failed`, with `sandboxStage`, HTTP status, worker phase, tool API action, and provider code when safely available. Session/workspace cleanup must succeed before `runnable`; cleanup failure is `failed_retryable`, with failure evidence retained at most 24 hours. -- Live E2E record: test account, provider/region/project, resource prefix `mpa-p0-e2e-*`, image digest, model, PostgreSQL, Runtime quota, write/delete permission, 120-second step timeout, cost cap, and cleanup owner. Without explicit permission/quota, live Cases are `blocked`, never replaced by mocks. -- Cross-repo manifest: VeADK SHA, runtime SHA/image digest, MPA Profile schema version, `agentkit-sdk-python==0.8.5` for reused platform services, and worker protocol/capability version. - -M0 replaces `` and `` with actual artifacts. Unlisted combinations are unsupported: - -| BFF | Runtime/schema | Runtime A2A capability | Codex Worker protocol | Allowed / expected | -| --- | --- | --- | --- | --- | -| `34658d75` (old) | `2f5e039` / schema 0 (old) | no execution v1 | `codex` query plus current REST endpoints | Existing list/read/chat/control | -| `` | `2f5e039` / schema 0 | no execution v1 | current baseline | list/read; new writes return `runtime_capability_unsupported` | -| `34658d75` (old) | `` / schema 1 | `urn:veadk:mpa:execution:v1` | current baseline | existing list/read/chat/control; no overrides | -| `` | `` / schema 1 | `urn:veadk:mpa:execution:v1` | `codex` query, `/health`, `/ready`, session/turn/events endpoints | all P0; run `AC-1` through `AC-12` | -| any | schema 1 | missing/mismatched | any | block new Session override/continue with `runtime_capability_unsupported` | -| any | schema 1 | execution v1 | `/ready` not ready or current REST endpoint missing | block worker dispatch with `409 worker_protocol_incompatible` | -| rollback candidate | schema 1 | actual target-image value | current baseline | migration-approved read/chat only; reject unless PG contract passes | - -The Worker has no independent capability-discovery/version endpoint today. M0's static compatibility baseline is the `codex` query plus `/health`, `/ready`, session-create, turn-start, and event-stream contracts above. Any change to that set first requires Worker capability/version negotiation and separate review; P0 does not modify the Worker. - -The shared compatibility preflight implementation is `veadk.cli.mpa_p0_contract`. The shell runner `scripts/verify-mpa-p0-contract.sh` calls the same code path, and Studio calls it before MPA Runtime writes. The accepted manifest is a redacted JSON object containing `veadkRevision`, `runtimeRevision`, `runtimeImageDigest`, Profile/config schema versions, AgentKit SDK version, Runtime execution capability, Worker protocol, and required Worker endpoints. The check never echoes the manifest body back to the browser. If a supplied manifest is malformed or not listed in `frontend/server/mpa/contracts/mpa-p0/compatibility-matrix.json`, Studio returns `409` with `code=mpa_compatibility_preflight_failed` and does not call AgentKit SDK or GitHub mutation helpers. - -For local Studio write-path protection, when the UI identifies the target as `agentCategory=mpa` but no explicit manifest is supplied, the BFF constructs a minimal P0 manifest from the current VeADK git revision plus `VEADK_MPA_RUNTIME_REVISION` and `VEADK_MPA_RUNTIME_IMAGE_DIGEST` when available; absent runtime artifact values are represented by syntactically valid zero placeholders. This only proves that the requested operation is still inside the approved P0 protocol shape. It is not release evidence. `S5-10/VC-19` must still run with pinned real VeADK SHA, Runtime SHA, Runtime image digest, and protocol versions before the final release gate. - -The live runner is `scripts/verify-mpa-p0-e2e.sh --manifest ` (added in M0). It runs `AC-11 -> AC-1 -> AC-2 -> AC-6 -> AC-9`, writes redacted JSON/logs to ignored local `evidence/live//`, and resumes from the last safe stage. An EXIT trap lists and deletes Agent, Runtime, Tool, test Session, and database schema by operation Metadata/resource prefix, then lists again to prove zero residue. Stubbed cross-repo contracts and live cloud evidence remain separate. - -The reproducible Runtime-repository gates are `make test` and `make coverage` (95%). That repository currently has no repository-local pre-commit, Ruff/Pyright, or CI configuration to cite, so the plan cannot claim those checks or transplant VeADK commands. VeADK still runs the targeted pytest, parallel regression, frontend test/build, browser, and pre-commit gates required by its AGENTS.md. Cross-repository integration evidence pins both revisions/image digest; green single-repository mocks are not E2E. - -## 9. Review conclusion and confirmed product decisions - -This `review-spec` revision resolves the blockers: duplicate template/expert/version ownership, unclear authority, underspecified API parameters/idempotency, ambiguous “resource snapshot” language, and frontend-created continuation. Anything confirmable from code or read-only execution is now a conclusion rather than a question. - -The user confirmed both product semantics as binding design decisions on 2026-09-15: - -### D-1: an existing Session adopts a new Profile only after explicit upgrade - -- **Confirmed option A: an existing Session pins the MPA Profile revision selected at creation; a newer revision takes effect on the next Turn only after an explicit “upgrade session” action.** -- Studio must show “new version available,” current/target versions, and an upgrade action. Session behavior is unchanged before upgrade. -- An active Turn never switches versions. Upgrade uses CAS and an idempotency key; failure retains the original version. -- Automatic latest-version tracking and per-Agent tracking policy are excluded to prevent silent model, tool, or instruction changes within one Session. - -### D-2: an Agent is runnable only after the complete execution path passes - -- **Confirmed option A: after Studio accepts MPA creation, show “deploying”; show “runnable” only after Runtime, Profile application, and execution-ready smoke all pass.** -- Creation returns a staged operation that can be queried after refresh. Failure shows safe resource IDs, the failed stage, and a retry action. -- When the MPA operation exists but Runtime is not executable, the UI cannot report creation success or start a conversation. -- Synchronous long polling and “Agent persisted means success” are excluded to avoid unrecoverable timeout UX and entry into a non-executable Agent. - -## 10. Risks and rollback - -- Local bearer/TIP claim mapping and rejection paths must pass in M0. The deployed `S5-14` release gate proves issuer, audience/client, JWKS rotation, revocation, and authorized Profile apply/status. Failure blocks release, not local S1 TDD. BFF forwards a validated bearer unchanged and issues no new assertion; never trust browser owner headers, Runtime keys, or the old service. -- Studio operation persistence and Runtime Profile apply are not a distributed transaction. Converge with an operation plus idempotent apply; preserve the last applied Profile revision and show “not applied” on failure rather than claiming runnable. -- Remote resources referenced by a session execution configuration can disappear after commit. Revalidate version and permission before Turn dispatch/materialization and fail before a side effect. -- Runtime database migration is additive. Check old/new schema and worker protocol compatibility before rollback; reject an incompatible downgrade before mutation. -- Delete affects only current MPA Runtime/Profile data, checks active Sessions and shared resources, and performs staged cleanup. It never touches historical ArkClaw data or Managed Agent data. - -## 11. Documentation-stage verification - -| Date | Check | Result | Meaning | -| --- | --- | --- | --- | -| 2026-09-15 | Managed Agent API probe | not_applicable | It confirmed a separate product surface and is excluded from the revised P0 architecture | -| 2026-09-15 | Runtime/Skill/Environment/Vault provider read-only queries | pass | Confirmed resource accessibility; does not prove write flows | -| 2026-09-15 | Three-repository static interface/data-model audit | pass | Confirmed reuse points and required extensions; does not prove runtime behavior | -| 2026-09-15 | M0 targeted tests and PostgreSQL 16 contract lane | pass | Principal, test seams, compatibility, scenario, runner, benchmark, and four real PostgreSQL foundation cases passed; removed the out-of-scope Managed Agent adapter | -| 2026-09-15 | Managed Agent APIs | not_applicable | Product decision excludes Managed Agent CRUD/version/Session from P0; the earlier `ManagedAgentNotOpen` result no longer blocks delivery | -| 2026-09-15 | Live OAuth bearer, TIP, JWKS rotation/revocation | blocked | The current environment has test AK/SK but no isolated live bearer/TIP/JWKS fixtures | - -## 12. S1/S2/S3/S4/S5 implementation verification record - -| Date | Check | Result | Meaning | -| --- | --- | --- | --- | -| 2026-09-15 | Runtime S1 focused regression | pass | `uv run pytest -q tests/test_agentkit_session_auth.py tests/test_runtime_principal.py tests/test_auth.py tests/test_sessions.py::test_create_get_delete tests/test_sessions.py::test_run_response_includes_session_id tests/test_execution_smoke.py tests/test_execution_smoke_api.py tests/test_codex_worker_client.py::test_codex_worker_client_ready_uses_worker_ready_endpoint tests/test_codex_worker_client.py::test_codex_worker_client_cancels_session_with_worker_control_api tests/test_mpa_meta.py tests/test_health.py tests/test_profile_apply_api.py` returned 83 passed | -| 2026-09-15 | Runtime focused Ruff and PostgreSQL 16 gate | pass | Focused Ruff passed on touched Runtime files; `make test-postgres` returned 4 passed and cleaned its container, network, and volume | -| 2026-09-15 | VeADK MPA BFF regression and Ruff | pass | `uv run --extra dev pytest -q tests/frontend/server/mpa/test_agent_operations_service.py tests/frontend/server/mpa/test_operations_repository.py tests/frontend/server/mpa/test_runtime_profile_routes.py tests/frontend/server/mpa/test_runtime_profile_client.py tests/frontend/server/mpa/test_mpa_module.py tests/frontend/server/mpa/test_scenario_harness.py` returned 32 passed; focused Ruff passed | -| 2026-09-15 | Studio frontend regression, i18n, and build | pass | `npm --prefix frontend test` returned 1206 passed; `npm --prefix frontend run check:i18n` passed; `npm --prefix frontend run build` passed. Build output under `veadk/webui` was cleaned from the source diff | -| 2026-09-15 | Repository diff hygiene | pass | `git diff --check` passed in both `veadk-python-mpa-p0-plan` and `agentkit-mpa-agent` | -| 2026-09-15 | S1 browser/live gates | not_run | S1-10 still owns BC-01/02, BC-07 create/IME subset, and live `VC-20A`; these were not converted into a pass by local unit/build results | -| 2026-09-15 | S1-10 local runner subset | pass | Compatibility manifest/e2e-runner/benchmark/scenario harness pytest returned 12 passed. `scripts/verify-mpa-p0-contract.sh` accepted a redacted manifest. `scripts/verify-mpa-p0-e2e.sh --dry-run` returned ordered stages `AC-11, AC-1, AC-2, AC-6, AC-9`; `--execute` without `VEADK_MPA_P0_LIVE=1` returned `live_execution_not_authorized` as designed | -| 2026-09-15 | S1-10 browser fixture API smoke | partial | The loopback fixture started on port `18173`; `/__test/mpa/scenarios` list, seed, call count, barrier release, and state read all worked. The token file was removed after forced cleanup. The S1 BC-01/02/BC-07 create/IME UI paths still need dedicated test-only scenario wiring before they can be marked pass; the S2 `session_revision_6` path is now wired and covered separately by S2-08. | -| 2026-09-15 | Browser runbook drift check | pass | `browser-cases.md` and `.zh.md` now match implemented `VEADK_MPA_SCENARIO_TOKEN_FILE` and `/__test/mpa/scenarios/...` APIs instead of the older `/web/test/mpa/...` draft routes | -| 2026-09-15 | S2-01 Session metadata pointers | pass | Runtime `session_meta` now stores `mpa_instance_id`, `profile_revision`, and `execution_config_revision`; Session create/detail expose them only for MPA Sessions. `0003_session_meta_mpa_pointers.sql` advances schema migration to version 3. Focused tests returned 17 passed, 2 skipped; focused Ruff, `make test-postgres`, and `git diff --check` passed | -| 2026-09-15 | S2-02 execution-config revision store | pass | Runtime now has `app/schemas/session_execution_config.py`, `app/stores/session_execution_config.py`, and `0004_session_execution_configs.sql`. The store creates revision 1, appends revision N+1 only when `If-Match` equals the current strong ETag, returns `ExecutionConfigPreconditionRequired` for missing preconditions, returns `ExecutionConfigConflict` with the safe current record for stale ETags, and keeps old revisions immutable. PostgreSQL CAS is serialized with a per-session advisory transaction lock. Verification: `uv run pytest -q tests/test_session_execution_config.py` returned 8 passed; focused Ruff passed; `make test-postgres` returned 5 passed including real two-connection Session CAS. | -| 2026-09-15 | S2-03 execution-config API/service | pass | Runtime now exposes `GET/PATCH /api/v1/sessions/{sessionId}/execution-config`, returning strong ETag headers and structured error bodies. GET initializes revision 1 only for MPA Sessions with `mpaInstanceId + profileRevision`; PATCH requires `If-Match`, supports `model` and `mcpServers` replace/clear/inherit, rejects unknown MCP IDs before append, maps stale ETags to `412 execution_config_changed`, and maps missing preconditions to `428 precondition_required`. Verification: `uv run pytest -q tests/test_session_execution_config.py tests/test_session_execution_config_service.py tests/test_agentkit_session_auth.py tests/test_sessions.py::test_create_get_include_mpa_profile_pointers_when_supplied tests/test_session_mcp_update.py tests/test_session_mcp_resolution.py::test_create_session_persists_and_enriches_appcenter_mcp_ids tests/test_session_mcp_resolution.py::test_create_session_without_ids_uses_agent_mcp_whitelist_when_requested tests/test_session_mcp_resolution.py::test_create_session_with_empty_agent_mcp_whitelist_selects_no_mcp` returned 24 passed; focused Ruff passed; `make test-postgres` returned 5 passed; both-repository `git diff --check` passed before this documentation update. | -| 2026-09-15 | S2-04 immutable resolved execution-config snapshot | pass | Runtime now defines `SessionExecutionConfigSnapshot` and `resolve_session_execution_config_snapshot(...)`. The snapshot freezes the accepted execution-config revision/ETag, MPA instance, Profile revision, Profile-default revision, overrides, effective refs, invalid refs, and accepted timestamp for future Turn acceptance. It is intentionally not yet persisted to `turn_execution_records` and `/run` does not consume it until S3. Verification: `uv run pytest -q tests/test_session_execution_config.py tests/test_session_execution_config_service.py` returned 15 passed; focused Ruff passed. | -| 2026-09-15 | S2-05 explicit Session Profile upgrade | pass | Runtime now exposes `POST /api/v1/sessions/{sessionId}/profile-upgrade` and service logic for explicit upgrade to a higher applied MPA Profile revision. The flow requires `Idempotency-Key` plus execution-config `If-Match`, preserves explicit Session overrides, recomputes inherited defaults from the target Profile, updates Session Profile/config pointers after success, rejects downgrade/same-version with `409 downgrade_not_allowed`, rejects missing/unapplied targets with `409 profile_revision_unavailable`, and rejects same-key different target with `409 idempotency_mismatch`. The schema-version-8 SQL `runtime_operations` ledger now makes same-key/same-target result replay durable across Runtime processes. Latest verification is recorded in the 2026-09-17 Runtime operation/CAS row below. | -| 2026-09-15 | S2-06 typed execution-config BFF proxy and client | pass | VeADK now exposes `GET/PATCH /web/mpa/sessions/{sessionId}/execution-config` and `POST /web/mpa/sessions/{sessionId}/profile-upgrade`, resolves the Runtime endpoint server-side, forwards the validated bearer token, and does not use browser `/web/runtime-proxy` for configuration writes. The TypeScript client exposes `getMpaSessionExecutionConfig`, `patchMpaSessionExecutionConfig`, `upgradeMpaSessionProfile`, and `getMpaProfileStatus`, including `If-Match`, `Idempotency-Key`, abortable reads, and `MpaExecutionConfigRequestError.currentState` for `412` recovery. Final verification: BFF pytest returned 21 passed; focused Ruff passed; focused frontend source-contract tests returned 95 passed; full frontend tests returned 1209 passed; i18n and build passed. | -| 2026-09-15 | S2-07 MPA Session configuration and explicit upgrade UI | pass | Studio now preserves Runtime `agentCategory` through My Agents, the new-chat selector, persisted runtime connections, and Agent details. The MPA tab can open the existing AgentKit creation surface with an explicit MPA intent. Agent details show a `Session configuration` section only for MPA Runtime entries and only load authoritative Runtime configuration when the selected Agent matches the current Runtime and current business Session. The panel displays config revision/ETag, pinned Session Profile revision, latest applied Profile revision, effective model/MCP refs, model replace/inherit, MCP clear/inherit, and explicit Profile upgrade with a deterministic idempotency key. `412 execution_config_changed` refreshes UI state from Runtime `currentState` and asks the user to review before retrying. Final verification: focused frontend source-contract tests returned 95 passed, full `npm --prefix frontend test` returned 1209 passed, `npm --prefix frontend run check:i18n` passed, `npm --prefix frontend run build` passed, Runtime S2 focused tests returned 46 passed, Runtime `make test-postgres` returned 5 passed, and generated `veadk/webui` assets were cleaned. | -| 2026-09-15 | S2-08 full S2 gate | pass | S2 unit, BFF, frontend, browser, and PostgreSQL gates are closed for the current diff. VeADK BFF pytest returned 28 passed; focused Ruff passed; focused frontend source-contract tests returned 95 passed; full `npm --prefix frontend test` returned 1209 passed; `npm --prefix frontend run check:i18n` and `npm --prefix frontend run build` passed. Runtime S2 focused tests returned 46 passed; Runtime `make test-postgres` returned 5 passed. Real-browser BC-04/05 ran against the loopback fixture on `http://127.0.0.1:18174` using system Google Chrome via Playwright: two browser contexts opened the same MPA Session at ETag `"6"`; browser A saved model override `model-browser-a` and advanced config revision to 7; browser B submitted a stale MCP clear and received expected `412 execution_config_changed` with current state; explicit Session Profile upgrade then advanced profile revision to 7 and config revision to 8 while preserving the explicit model override and inheriting `mcp-upgraded`. Raw local evidence: ignored `evidence/browser/mpa-s2-1789491400/summary.json`, `BC-04/context-a-before.png`, `BC-04/context-a-after.png`, `BC-04/context-b-before.png`, `BC-04/context-b-conflict.png`, `BC-04/network-a.json`, `BC-04/network-b.json`, `BC-04/call-counts.json`, `BC-05/before.png`, `BC-05/upgrade-after.png`, and `BC-05/revision-transcript.json`. Expected browser noise was limited to local-mode `/oauth2/userinfo` 404 probes and the intentional BC-04 412; no unexpected HTTP or console errors were recorded. `uv run --extra dev pyright ...` is `blocked` because this local environment has no `pyright` executable or project script. A real UI issue found during the run, resource-card overlay intercepting the chat action, was fixed by keeping card action buttons above the full-card detail target. | -| 2026-09-16 | S3-01 `turn_execution_records` store and migration | pass | Runtime now persists accepted MPA Turn records in `turn_execution_records` with `turn_id`, `invocation_id`, `operation_id`, scoped idempotency key, canonical request hash, frozen execution-config snapshot, dispatch key/status, and timestamps. SQL and in-memory stores enforce same-key replay, same-key/different-payload conflict, and one active Turn per Session; PostgreSQL uses a per-session advisory transaction lock. Schema migration `0005_turn_execution_records.sql` advances the contract lane to schema version 5. Verification: `uv run pytest -q tests/test_turn_dispatcher.py tests/test_run_acceptance.py` returned 14 passed; expanded Runtime S3 regression returned 64 passed; `make test-postgres` returned 6 passed. | -| 2026-09-16 | S3-02 shared REST/A2A Turn acceptance and Studio run wiring | pass | Runtime `/api/v1/sessions/{sessionId}/run` now requires `Idempotency-Key` and `executionConfigVersion` for MPA Sessions, persists the S2 resolved snapshot before dispatch, returns `turnId`, `operationId`, and `executionConfigRevision`, replays the original accepted response for same-key/same-payload calls, rejects stale config with `412 execution_config_changed`, rejects payload drift with `409 idempotency_mismatch`, and rejects concurrent active Turns with `409 session_busy`. Non-MPA runs keep the legacy response shape. A2A advertises `urn:veadk:mpa:execution:v1`; metadata `params.metadata.veadkExecution` uses the same acceptance service, binds the durable invocation ID to the ADK run, moves dispatch status through `dispatching -> succeeded|failed_retryable`, and reports same-key replay as completed without a second model execution. Studio reads the current MPA Session execution-config revision before send, passes a per-run idempotency key to `runSSE`, calls MPA Runtime `/run` then `/sse`, and BFF forwards the same metadata to A2A. Verification: Runtime S3 focused regression returned 64 passed; Runtime focused Ruff passed; Runtime PostgreSQL gate returned 6 passed; VeADK MPA server/integration pytest returned 47 passed; BFF A2A bridge test returned 3 passed; frontend focused source-contract tests returned 14 passed; full `npm --prefix frontend test` returned 1212 passed; `npm --prefix frontend run check:i18n` and `npm --prefix frontend run build` passed; generated `veadk/webui` assets were cleaned. | -| 2026-09-16 | S3-03 accepted-turn/inbox recovery | pass | Runtime now persists `dispatch_payload` in `turn_execution_records` so accepted Turns can be rehydrated after a restart. `SessionService.initialize()` scans recoverable `accepted|dispatching|failed_retryable` Turn records, synchronizes already-terminal inbox rows back to Turn status, reconstructs missing/error inbox rows with the original invocation/content/source/peer/environment metadata, and schedules the existing inbox worker instead of introducing a second dispatcher. Inbox `enqueue_once()` is idempotent by invocation ID and can reset a prior error row to queued for retry. Lease-expired running inbox rows mark their linked Turn `failed_retryable` before channel notification. Verification: Runtime S3 regression returned 72 passed; Runtime focused Ruff passed; Runtime `make test-postgres` returned 6 passed. | -| 2026-09-16 | S3-04/S3-05/S3-06 run recovery and refresh gates | pass | Runtime cursor replay now returns `410 cursor_expired` for unknown non-empty cursors and preserves the inbox terminal fence. Studio restores persisted Runtime Agent and Session IDs on refresh, hydrates the selected Session even when the ID already matches state, sends MPA runs through Runtime `/run` plus `/sse`, and forwards `lastEventId` from the last completed assistant turn. Verification: Runtime expanded S3 regression returned 251 passed; Runtime focused Ruff passed; Runtime `make test-postgres` returned 6 passed; VeADK focused frontend tests returned 15 passed; scenario harness pytest returned 8 passed; MPA server/integration pytest returned 51 passed; full `npm --prefix frontend test` returned 1213 passed; `npm --prefix frontend run check:i18n` passed; `npm --prefix frontend run build` passed. Real-browser `BC-06` passed on `http://127.0.0.1:18175` with system Google Chrome via Playwright: the refreshed Session restored `event-before-refresh`, send carried `lastEventId=event-before-refresh`, Runtime returned only `event-after-refresh`, the page rendered each event once, and an unknown cursor returned `410 cursor_expired`. Raw local evidence: ignored `evidence/browser/mpa-s3-1789516481/BC-06/`. gstack browser was not used for final evidence because its Playwright browser cache was missing; system Chrome was used instead. | -| 2026-09-16 | S4-01 participant schema/store foundation | pass | Runtime now has `a2a_turn_participants` schema version 6 plus SQL and in-memory stores for per-generation participant registration, owner-scoped lease heartbeat, generation-checked safe-point ACK, terminal marking, and ordered generation snapshots. This is only the S4 data-model foundation; S4-02 still owns aggregate all-participant pause barrier transitions. Verification: `uv run pytest -q tests/test_turn_participants.py` returned 5 passed; `uv run pytest -q tests/test_turn_participants.py tests/test_a2a_task_control.py tests/test_turn_control_api.py tests/test_p0_test_seams.py` returned 37 passed; focused Runtime Ruff passed; `make test-postgres` returned 7 passed and cleaned PostgreSQL resources. | -| 2026-09-16 | S4-02 participant pause barrier and worker registration race handling | pass | Runtime now uses `ParticipantBarrierService` for pause requests when a participant store is available. A2A primary executors and Codex workers register participants, safe-point ACKs are generation checked, partial ACK keeps the aggregate task in `pausing`, all current-generation active participants move the task to `paused`, late workers that join an already paused generation receive worker pause and must ACK before resume, and terminal workers are not copied into later pause generations. Resume seeds the next running generation from the previous paused generation. S4-03 still owns the exact 300-second lease-aware resume boundary and S4-04 still owns linked continuation. Verification: `uv run pytest -q tests/test_turn_participants.py tests/test_participant_barrier.py tests/test_turn_control_api.py tests/test_a2a_app_lifecycle.py tests/test_codex_adapter.py::test_adapter_pauses_worker_and_continues_interrupted_turn_on_resume tests/test_codex_adapter.py::test_adapter_marks_completed_worker_participant_terminal_before_next_pause tests/test_codex_adapter.py::test_adapter_late_worker_joins_paused_generation_before_resume` returned 27 passed; focused Runtime Ruff passed; `make test-postgres` returned 7 passed and cleaned PostgreSQL resources. | -| 2026-09-16 | S4-03 lease-aware resume boundary | pass | Runtime now treats resume as same-Turn only when the aggregate task is `paused`, the requested generation matches, the pause age is not greater than 300 seconds, the task owner lease is still viable, and no participant in the paused generation is `lost|failed` or still running with an expired/missing lease. A participant that is still running with a valid lease blocks resume as `409 invalid_state` instead of silently continuing. Expired or lost resume transitions to `interrupted`, returns `resumeDisposition=new_turn_required`, exposes the continuation prompt in status/replay responses, and records the resume idempotency key so same-key retries replay safely. S4-04 still owns creation of the linked continuation Turn. Verification: S4-03 focused boundary tests returned 8 passed; broader S4 control regression returned 104 passed; focused Runtime Ruff passed; `make test-postgres` returned 7 passed and cleaned PostgreSQL resources. | -| 2026-09-16 | S4-04 runtime-owned linked continuation | pass | Runtime now exposes `POST /api/v1/a2a/tasks/{taskId}/continue`. The API requires `Idempotency-Key` and `expectedGeneration`, accepts only task states that already require a new Turn, resolves the previous business Turn from the old task invocation, and writes the continuation through `turn_execution_records.accept_continuation(...)`. The new Turn keeps `continuationOf=`, reuses the frozen execution-config snapshot and dispatch payload, closes the old active dispatch row, and schedules the existing inbox/outbox worker via `enqueue_once`. Same-key/same-request calls replay the same continuation response; same key with a different request is rejected. Studio prompt-fallback removal and UI/BFF wiring remain S4-05. Verification: S4-04 focused tests returned 4 passed; broader S4 control/acceptance regression returned 125 passed; focused Runtime Ruff passed; `make test-postgres` returned 8 passed and cleaned PostgreSQL resources; both-repository `git diff --check` passed. | -| 2026-09-16 | S4-05 Studio Runtime continue wiring | pass | Studio now calls Runtime `/api/v1/a2a/tasks/{taskId}/continue` through `continueTurnSSE(...)` when resume returns `new_turn_required`, then subscribes to Runtime `/api/v1/sessions/{sessionId}/sse` with the continuation `invocationId` and current `lastEventId`. It no longer builds a `continuationPrompt + Original task` message or sends an ordinary `/run`; the existing control state is cleared when continuation streaming starts. Verification: focused frontend source-contract tests returned 37 passed; `npm --prefix frontend run check:i18n` passed; `npm --prefix frontend run build` passed and generated `veadk/webui` assets were cleaned. | -| 2026-09-16 | S4-06 RuntimePrincipal revocation and side-effect rechecks | pass | Runtime now has a shared `runtime_authorization` helper and an injectable `runtime_principal_authorizer`. REST `/run`, resume, and `/continue` recheck the current grant before accepting side effects. Accepted Turn dispatch payloads and inbox metadata carry a normalized non-secret RuntimePrincipal snapshot so background execution and restart recovery can recheck without storing a bearer token. ADK `before_tool_callback` rechecks before tool calls; sandbox common rechecks before direct sandbox dispatch; model endpoint resolution, workload TIP, OpenViking API-key resolution, and sandbox `begin_run` recheck before using secret material. A fake deny authorizer proves rejected paths produce zero resume/continue/tool/secret side effects. Verification: S4-06 focused auth/secret/tool tests returned 46 passed; broader S4 Runtime regression returned 206 passed; focused Runtime Ruff passed. External AgentKit revocation lookup remains a deployed S5 release gate. | -| 2026-09-16 | S4-07 lifecycle/PostgreSQL/browser gate | pass | Runtime focused auth/secret/tool tests returned 46 passed and `make test-postgres` returned 8 passed with PostgreSQL resources cleaned. VeADK `uv run --extra dev pytest -q tests/frontend/server/mpa/test_scenario_harness.py` returned 9 passed; focused frontend source-contract tests plus i18n returned 37 passed and i18n pass; full `npm --prefix frontend test` returned 1215 passed; `npm --prefix frontend run build` passed and generated `veadk/webui` assets were cleaned. Real-browser `BC-09` passed on `http://127.0.0.1:5174` with backend `http://127.0.0.1:8001` using system Google Chrome via Playwright. Raw evidence was captured locally under ignored `evidence/browser/mpa-s4-1789532050/BC-09/`: `running.png`, `partial.png`, `paused.png`, `resumed.png`, `continue.png`, `summary.json`, `network.json`, `call-counts.json`, `state.json`, and console logs. Summary checks are all true: partial ACK remained `pausing`; `new_turn_required` state was observed; no auto `/continue` occurred before click or after refresh; explicit continuation replay was idempotent; no unexpected HTTP or console errors were recorded. Expected local-mode noise was four `/oauth2/userinfo` 404 probes. | -| 2026-09-16 | S5-01 secret reference migration foundation | pass | Runtime schema version 7 adds `agent_api_key_ref`, `model_api_key_ref`, `secret_migration_status`, and `secret_migration_operation_id` to `mpa_agents`. `AgentConfigService` rejects new plaintext `agentApiKey`/`modelApiKey` writes in AgentKit mode while accepting reference fields. `SecretReferenceMigrationService` creates references before clearing plaintext, preserves plaintext on provider/DB failure, and retries without duplicate provider writes. Runtime model and built-in MCP paths resolve references only at execution-time secret-use boundaries and fail closed if no resolver is configured. Verification: S5-01 focused pytest returned 95 passed; focused Ruff passed; `make test-postgres` returned 9 passed and cleaned PostgreSQL resources. | -| 2026-09-16 | S5-01a canary-safe value-aware redaction | pass | Runtime now registers legacy plaintext secret literals during secret-reference migration and applies value-aware redaction across API access logs, Session visible text/events, Runtime Console responses, lifecycle/trace detail bounding, SQL trace-step persistence, and persisted Runtime operation response/resource snapshots. The redaction masks registered canary values even under non-sensitive keys or inside free text while preserving non-secret business IDs such as `flow_id`, `user_code`, and model `max_tokens`; secret reference fields remain allowed. Runtime operation status omits principal and idempotency internals. Latest verification is recorded in the 2026-09-17 Runtime operation/CAS row below. | -| 2026-09-16 | S5-02 unique Runtime binding and `MpaAgentView` BFF | pass | VeADK now exposes `GET /web/mpa/agents/{mpaInstanceId}/view`. The BFF resolves visible MPA Runtime bindings, returns `runtime_missing` for zero bindings, returns `binding_ambiguous` with candidate bindings and write/debug disabled for multiple bindings, and hydrates the unique binding with Runtime `profile-status`, Runtime/version/readiness metadata, capabilities, and the matching active operation. The main app resolver probes the configured Runtime regions, keeps only `veadk:agent-type=mpa` Runtimes, merges tag-service metadata, and reuses the existing Runtime connection resolver; it does not call Managed Agent APIs. S5-03 still owns the frontend detail page. Verification: focused MPA BFF route/main-app tests returned 32 passed; focused Ruff passed. | -| 2026-09-16 | S5-03 MPA detail view-model consumption | pass | Studio now consumes `MpaAgentView` on MPA Runtime-backed detail pages. The TypeScript client exposes `MpaAgentView` and `getMpaAgentView(...)`; `AgentWorkspace` loads the view for MPA details, renders an MPA control-plane status alert, and requires `bindingStatus=bound` before loading Session configuration. `runtime_missing` and `binding_ambiguous` block writes; `orphan_runtime` blocks Session configuration and diagnostics until a Profile is applied. Non-MPA details continue using the existing Runtime detail/version/update paths. Runtime delete is completed by the later S5-07 slice and richer Debug/Trace UI is completed by S5-05. Verification: focused frontend source-contract tests returned 33 passed; full frontend tests returned 1216 passed; i18n and build passed; focused MPA BFF/main-app tests returned 32 passed; focused Python Ruff passed. | -| 2026-09-16 | S5-04 Runtime Console/Trace structured correlation | pass | Runtime Console trace responses now include a top-level `correlation` object and per-step `correlation` objects. The fields are extracted from existing redacted trace details and normalize MPA instance, Profile revision, execution-config revision/ETag, business Turn, A2A task, Runtime ID/version, and Codex/subagent worker session/turn/profile identifiers. This gives Studio stable linkage fields without parsing arbitrary details JSON. S5-05 still owns the Studio Debug/Trace/history/usage view. Verification: Runtime Console/Trace pytest returned 72 passed; focused Ruff passed. | -| 2026-09-16 | S5-05 Studio MPA diagnostics view | pass | Studio now exposes an MPA-only Diagnostics section in `AgentWorkspace`. It loads Runtime Console admin run history and the latest invocation trace through the existing Runtime proxy, renders the S5-04 structured correlation fields, lists bounded trace steps, and keeps usage on the existing Usage tab. Diagnostics are blocked when the MPA binding is missing, ambiguous, orphaned, not the active chat Runtime, or has no current Session. Verification: focused frontend source-contract tests returned 34 passed; full frontend tests returned 1216 passed; i18n and build passed; focused MPA BFF/main-app tests returned 32 passed; focused Python Ruff passed. | -| 2026-09-16 | S5-06 Runtime update/release/rollback compatibility preflight | pass | VeADK now exposes a shared in-process MPA P0 compatibility evaluator used by both `scripts/verify-mpa-p0-contract.sh` and Studio write routes. MPA direct Runtime update (`/web/deploy-agentkit`), GitHub Actions delivery setup (`/web/github-delivery/cicd-pipeline`), initial delivery branch setup (`/web/github-delivery/init-main`), bound source sync (`/web/github-cicd/runtime-sync`), and rollback PR creation (`/web/github-delivery/rollback-pr`) reject malformed or incompatible manifests with `409 mpa_compatibility_preflight_failed` before AgentKit SDK or GitHub mutations. The browser sends `agentCategory=mpa` on MPA publish/update and rollback paths; non-MPA paths retain existing behavior. Local checks passed: focused pytest for manifest plus MPA update/GitHub release/rollback guards returned 12 passed; focused frontend source-contract tests returned 41 passed; full frontend tests returned 1217 passed; focused Ruff passed; `compileall` passed; `npm --prefix frontend run check:i18n` passed; `npm --prefix frontend run build` passed with pre-existing Vite chunk warnings. Generated `veadk/webui` assets were cleaned after build. | -| 2026-09-16 | S5-07 authorized delete impact preview and staged cleanup | pass | VeADK now exposes `GET /web/mpa/agents/{mpaInstanceId}/delete-preview` for Studio-managed MPA deletion. The preview resolves the authorized Runtime binding, reads Runtime `profile-status`, calls existing Runtime `GET /api/v1/sessions?include_a2a=true`, reports active operations, active/idle/debug visible session counts, blockers, and the staged cleanup plan. `/web/delete-runtime` rechecks the server-side Runtime `veadk:agent-type=mpa` tag before applying MPA cleanup, blocks active operations or active sessions in `queued|running|pausing|paused|resuming` with structured `409`, deletes visible idle/terminal sessions through existing Runtime `DELETE /api/v1/sessions/{sessionId}`, then deletes the AgentKit Runtime. Session cleanup treats per-session `404` as already cleaned for retry safety. The browser fetches and displays the preview before single MPA delete confirmation and disables confirm when blockers exist. No `agentkit-mpa-agent` database schema or API was added for this slice; the current preview can only account for sessions visible through the authorized target Runtime API, not cross-user global session state. Local checks passed: focused MPA BFF/client/delete pytest returned 31 passed; full frontend tests returned 1217 passed; focused frontend source-contract tests returned 33 passed; focused Ruff passed; `compileall` passed; `npm --prefix frontend run check:i18n` passed; `npm --prefix frontend run build` passed with pre-existing Vite chunk warnings. Generated `veadk/webui` assets were cleaned after build. Real browser delete-modal verification is blocked locally because the Playwright package is not resolvable in the frontend environment; no browser pass is claimed for this slice. | -| 2026-09-17 | S5-08 Studio unified Profile management entry | pass | MPA Agent detail now has an Agent-level `Profile configuration` section as the Studio control-plane entry. The section reads the dedicated `MpaAgentView`, shows apply status/revision/ETag/model/system/tool-skill-MCP summaries, applies the current Studio Profile through the typed MPA BFF operation path, and offers `Edit Profile` by reopening the existing Studio Agent workbench in Profile-only mode. Profile-only mode skips environment/deployment steps, does not call `/web/deploy-agentkit`, and saves through `applyMpaProfileAfterDeployment(...)` with create semantics for orphan Runtime or ETag update semantics for bound Runtime. MPA details suppress the generic Runtime update capability path because it depends on `/list-apps` and is misleading for A2A-only mpa-agent Runtime. Orphan Runtime remains writable for first Profile apply while Session configuration and diagnostics remain blocked until the binding becomes `bound`. Local dev now uses a process-local operation repository when Studio TOS is absent so `veadk studio --dev` can exercise Profile apply against a real Runtime; production still requires TOS persistence. Runtime was rebuilt and released as `agentkit-platform-2112682748-cn-beijing.cr.volces.com/agentkit/mpa_agent:mpa-p0-sessioncfg-init-local-20260917` on Runtime `r-yeuujrrcowb21078p9jh` version 58 after fixing MPA Session create to initialize execution-config revision 1. Live checks passed: BFF Profile update returned `202/succeeded/profileRevision=3`, `MpaAgentView` returned `bindingStatus=bound` with `canWrite=true` and `canDebug=true`, native Session create returned `executionConfigRevision=1`, native `/run` returned `turnId/operationId/invocationId`, native `/sse` emitted final text and named `event: done`, Studio BFF `run_sse` bridge also emitted final text, and persisted events contained one user event plus one final model event with trace ID. A command-line direct A2A JSON-RPC request with a valid UUID `contextId` returned HTTP 200 and task state `completed`; the packaged AgentKit CLI `agentkit invoke run --runtime-id/--endpoint --a2a` still returns 404 against this A2A-only Runtime and is tracked as a CLI compatibility gap outside the Studio path. Local checks passed: focused frontend source-contract tests returned 137 passed; MPA operation/BFF pytest returned 43 passed; Runtime Session/config/run pytest returned 186 passed; Runtime focused Ruff passed; VeADK focused Ruff/compileall/diff-check passed; `npx tsc --noEmit` passed; `npm --prefix frontend run check:i18n` passed; full `npm --prefix frontend test` returned 1227 passed; `npm --prefix frontend run build` passed with existing Vite chunk warnings. Browser static load through system Chrome produced the login DOM and screenshot; interactive browser automation remains limited because gstack's Playwright browser cache is missing. | -| 2026-09-17 | Multi-round completed-answer persistence regression | pass | The prior post-SSE event-fenced reconciliation prevented stale persistence from overwriting the newest live answer, but it did not address persisted multi-round replay. Strict DOM inspection reproduced the real defect: two user turns replayed as two user nodes but only one assistant node. Root cause was `eventsToTurns(...)` recreating every history projector with the same `adk-history` prefix; each projector therefore emitted `localId=adk-history-0`, and the later assistant round replaced the earlier one during upsert. Studio now assigns a monotonically increasing history-segment prefix, so projector-local sequence numbers remain unique across the Session transcript. The regression test failed before the fix with only `second answer` and passes after the fix with both answers, both final event IDs, and distinct local IDs. Focused tests returned 43 passed; full frontend tests returned 1236 passed; `npx tsc --noEmit`, i18n, production build, and scoped diff checks passed. A real Studio Session was then checked strictly through ordered `.turn--assistant` nodes: after two additional live turns completed and a 30-second delay, five assistant turns remained; after a full-page reload, all five still remained in order. Whole-page text presence is explicitly excluded as evidence because prompt/reasoning text can create false positives. | -| 2026-09-17 | Live MPA parent sandbox activity terminal convergence | pass | A real Studio run reproduced the defect: the command card and final answer completed while the parent `sandbox_task` card remained `running`. Captured SSE frames proved that the live subscription emitted answer deltas and then only the `sandbox_task` wrapper response with `status=completed` and `finalAlreadyEmitted=true`; the persisted event list additionally contained `source=sandbox,eventType=invocation.completed`, which explained why reload was already correct. Studio no longer discards the wrapper unconditionally. It uses the wrapper to close an active parent tool without truncating streamed answer/tool blocks, marks the projected turn non-streaming with the wrapper event ID, and still ignores the wrapper after persisted completion to avoid duplicates. The focused regression failed on the old behavior and now passes. Full `npm --prefix frontend test` returned 1237 passed; `npx tsc --noEmit`, i18n, production build, and scoped diff checks passed. A fresh real Studio `whoami` run showed the parent and child activities running, then—without reload—the answer `root`, parent `data-status=completed`, no spinner, and zero remaining running activities. After full-page reload, both latest activities remained completed with no spinner and the final answer remained visible. | -| 2026-09-17 | Runtime operation ledger and Profile CAS hardening | pass with repository-baseline gate exceptions | Runtime schema version 8 adds the SQL `runtime_operations` ledger with a 24-hour TTL, canonical request hashes, redacted response snapshots/resource IDs, compare-and-swap state transitions, and principal/agent/action-authorized `GET /api/v1/runtime-operations/{operationId}`. Profile apply now keys idempotency by authenticated principal, hashes the full normalized Profile, maps model/Tool/Skill/MCP/Multiagent fields, and atomically validates `If-None-Match`/`If-Match` while switching `mpa_agents` revisions. Malformed strong ETags return `400 invalid_precondition`; model fields such as `max_tokens` and secret-reference fields are accepted, while inline secret fields are rejected. Profile upgrade replays the persisted result across Runtime service/store instances. Verification: focused Runtime tests returned 39 passed; PostgreSQL migration/restart/cross-store replay/Profile-CAS tests returned 11 passed with cleanup; changed-file Ruff passed; `git diff --check` passed. Final full behavior regression returned 2501 passed and 14 skipped, but `make coverage` is recorded as fail because repository total coverage is 93.47%, below the unchanged 95% threshold. Full-tree Ruff is also fail on 27 pre-existing `origin/master` findings in files outside this P0 diff; no threshold or lint rule was weakened. | -| 2026-09-18 | AgentKit key-auth compatibility for legacy ADK endpoints | pass | Runtime version 61 was `Ready` with AgentKit `key_auth`, but an authorized `GET /list-apps` returned `401` because the legacy `require_auth` dependency still required `X-Jwt-Token` after Session, Profile, and A2A paths had moved to the `Authorization` Runtime principal. Runtime commit `eecc6e3` makes `require_auth` derive that principal from `Authorization` only when `MPA_AGENTKIT_MODE=true`; non-AgentKit deployments retain the legacy header contract, and missing credentials still fail closed. Focused tests returned 49 passed, full `make test` returned 2506 passed and 14 skipped, focused Ruff and diff hygiene passed. Image `agentkit-platform-2112682748-cn-beijing.cr.volces.com/agentkit/mpa_agent:mpa-p0-adk-auth-eecc6e3-20260917` (`sha256:6ac6a2712ecd1c7950125dc9afc6467373a08142fbd6c3577aba12f126079bef`) is deployed as Runtime `r-yeuujrrcowb21078p9jh` version 62 and is `Ready`; direct Runtime and Studio BFF `/list-apps` probes both returned `200 ["default"]`. This is an authentication adapter correction and adds no table, column, or persisted state. | -| 2026-09-18 | Live Studio chat-display closure on Runtime v62 | pass for the exercised chat-display subset | A new Studio Session `bee3db38-edcc-4020-b925-3d9d6a3a7adc` executed `printf 'MPA_V62_BROWSER_OK\n'` through the sandbox. Both the parent `sandbox_task` activity and child command activity reached `completed`; the command exited 0; the final assistant answer was visible at completion, remained visible after 30 seconds, and was restored after a full page refresh. The composer was editable, the send control returned to `aria-label=发送`, and no spinner or running text remained. Raw evidence was captured locally under ignored `evidence/browser/mpa-v62-live/`. This closes the observed stuck-running/result-disappears regression for this real path; it does not replace or complete the full `BC-01`–`BC-09` matrix owned by S5-12. | -| 2026-09-18 | S5-08 shared Python control-plane client | pass, except Pyright blocked | Added public async `veadk.integrations.mpa.MpaControlPlaneClient` plus shared Profile, operation, Agent view/capability, Session execution-config, delete-preview, and safe error schemas. It calls only Studio BFF endpoints; bearer injection, caller-stable idempotency keys, CAS ETags, response validation, timeout classification, and redacted errors are centralized. Unstructured response detail is never surfaced as an error code. Studio Runtime transport now imports shared Profile/execution-config schemas. The existing design listed operation get, but BFF implemented only active-list/retry; owner-scoped `GET /web/mpa/agent-operations/{operationId}` is now present so terminal operation polling is complete. No Managed Agent call, Runtime API, database table, or `agentkit-mpa-agent` change was introduced. Focused client tests returned 19 passed; the complete Studio MPA backend plus shared-client suite returned 73 passed; focused Ruff, compileall, pre-commit, and diff checks passed. The repository regression with declared `dev + extensions + sandbox` extras returned 4639 passed, 8 skipped, and 2 xfailed. `uv run pyright ...` is `blocked` because the repository environment has no `pyright` executable. | -| 2026-09-18 | S5-09 Studio control-plane CLI | pass, except Pyright blocked | Added the separate `veadk mpa control` group while preserving the infrastructure-oriented `veadk mpa create`. It exposes view/create/update, operation list/get/retry, Profile status/apply, Session config get/patch/profile-upgrade, and delete preview exclusively through the shared `MpaControlPlaneClient` and Studio BFF. JSON file/stdin inputs use the shared Pydantic schemas; Profile Secret fields fail before network access; bearer values are never printed; writes preserve explicit caller-stable idempotency keys and CAS revisions/ETags; response ETags are retained; and redacted control-plane failures map to stable exit codes `1/3/4/5` while local usage errors remain Click exit `2`. A loopback BFF proves real request paths, headers, bodies, 202 handling, old-Runtime read-only output, and same-key replay after a server-observed timeout. Two-round review fixed nested help so it works without a configured URL and clarified JSON versus local usage-error output. Verification: focused CLI 22 passed; shared-client plus MPA CLI 57 passed; all CLI tests 1402 passed and 4 skipped; required parallel Python regression 4661 passed, 8 skipped, and 2 xfailed; focused Ruff/format, compileall, and diff checks passed. Pyright is `blocked` because no executable is installed. No Runtime API, Runtime database, frontend, Managed Agent, or `agentkit-mpa-agent` change was required. | -| 2026-09-18 | S5-10 pinned cross-repository compatibility preflight | pass | Executed VC-19's preflight phase with VeADK `486dc06e429573ce43d6462b6dc960be4bc34ab6`, Runtime `eecc6e3115685e5cb86dcfbff4fb7c6ac7a10dee`, AgentKit SDK `0.8.5`, and deployed v62 image digest `sha256:6ac6a2712ecd1c7950125dc9afc6467373a08142fbd6c3577aba12f126079bef`. A live Runtime detail read returned `Ready`, version 62, and the expected artifact tag; registry inspection independently returned the same digest. The real manifest matched `p0-codex-rest-v1`. Negative runs rejected `codex-v2` with exit 3 / `worker_protocol_incompatible` and an invalid image digest with exit 2 / `invalid_runtime_image_digest`. Manifest contract tests returned 4 passed, Studio/GitHub pre-mutation fence tests returned 8 passed, and the VC-15 Runtime redaction suite returned 91 passed. Redacted raw evidence was captured locally under ignored `evidence/contract/s5-10-v62-20260918/`. S5-10 is read-only and required no code, Runtime schema, API, frontend, Managed Agent, or deployed-resource mutation. VC-19's final full regression/coverage/PostgreSQL/build/performance phase remains explicitly open under S5-13 after VC-21. | -| 2026-09-18 | S5-11 live Runtime lifecycle, rollback, and deletion | pass | VC-21 ran against a fresh isolated Runtime using v29 image `agentkit-platform-2112682748-cn-beijing.cr.volces.com/agentkit/mpa_agent_vc21c:mpa-p0-smoke-v29-eecc6e3-20260918` with digest `sha256:078519c796b03b298a95ef7a6614f92121bc124513dbb472a7496c95821ee723`. Run `vc21-20260918-045` completed all stages: compatibility fence, Runtime provisioning, Profile apply, execution-ready smoke, no-op same-image update, controlled failed update plus recovery smoke, compatible rollback smoke, active-operation delete block, active-session delete block, idle-session cleanup, final Runtime delete, and PostgreSQL cleanup. The runner returned `{"case":"VC-21","residue":[],"status":"passed"}`. During live validation, `vc21-20260918-044` exposed a Studio delete bug where a recovered `failed_retryable/smoke_running` operation could remain visible and permanently block deletion even though Runtime Profile status was `applied` and smoke had passed; Studio now treats that state as non-blocking while preserving true `active` operation and active-session blockers. Focused verification after the fix returned 49 passed for CLI/route/driver coverage and focused Ruff passed. Raw evidence was captured locally under ignored `evidence/live/vc21-20260918-045/`. | -| 2026-09-19 | S5-12 partial full-matrix browser run | partial; `BC-03/04/05/06/09` pass, `BC-01/02/07/08` blocked | System Google Chrome driven by gstack v1.87.4.0 exercised the current branch at VeADK `cd34b45c` plus the working diff against the loopback fixture. `BC-03` covered `bound`, `runtime_missing`, `binding_ambiguous`, `orphan_runtime`, and old-Runtime read-only states. `BC-04` used two isolated Chromium profiles with the same local principal and Session at ETag `"6"`: browser A's model PATCH returned `200`/revision 7, browser B's stale MCP clear returned `412`, and the authoritative state retained only A's model override. `BC-05` upgraded Session Profile 6 to 7 and proved same-key replay plus different-target `409 idempotency_mismatch`. `BC-06` rendered the persisted and resumed cursor events once before and after refresh and returned `410 cursor_expired` for an unknown cursor. `BC-09` proved `running -> pausing -> paused -> running`, partial ACK behavior, localized `new_turn_required`, one explicit `/continue` plus `/sse`, no automatic continuation after refresh, and same-key replay of the linked Turn. The run repaired test-only MPA view, Session list/events, cursor persistence, and lifecycle-stream seams, constrained Vite dependency discovery to the product entry, and removed three duplicate `agentCategory` declarations/fields that blocked TypeScript build after branch integration. Verification passed: scenario/Profile route pytest 36 passed, focused frontend tests 82 passed, full frontend tests 1240 passed, Ruff passed, i18n passed, and frontend production build passed with existing chunk warnings. `npm ci` remains `fail` because `frontend/package.json` and `frontend/package-lock.json` disagree about the esbuild 0.28 dependency tree; the build used the repaired local dependency directory without changing the lockfile. `BC-01`, `BC-02`, `BC-07`, and `BC-08` are `blocked` because `create_success`, the four creation-failure variants, and `slow_scope_switch` lack isolated scenario write/scope routes; the real create UI otherwise calls `/web/deploy-agentkit`, so no unauthorized cloud mutation was attempted. Raw local evidence under ignored `evidence/browser/s5-12-20260919-r1/` is intentionally not committed. `S5-12` and `VC-17` remain open. | -| 2026-09-20 | S5-12 complete browser matrix | pass; `BC-01`–`BC-09` pass | The loopback-only harness now intercepts Runtime-name validation, generated project/draft creation, deploy SSE, MPA operation create/list/get/retry, failure stages, deployment/operation barriers, and principal/region-scoped Runtime discovery, so no AgentKit SDK or cloud mutation was reachable. `BC-01` passed normal creation plus refresh recovery from `profile_applying` to `runnable`; `BC-02` passed `profile_apply_failed`, `smoke_worker_not_ready`, `smoke_output_mismatch`, and `cleanup_failed`, with one Runtime deploy, one operation create, and one explicit operation-only retry per case; `BC-07` proved IME Enter suppression (`0` calls while composing, `1` after composition end) and no horizontal overflow at `1024x768`; `BC-08` kept Bob/Shanghai authoritative after a delayed Alice/Beijing response for the same Runtime ID and returned only `404 resource_not_found` to a denied principal. Browser execution found and fixed missing frontend active-operation recovery, unsafe full-deploy retry on both quick and traditional creation paths, missing IME protection, an untranslated recovered-success label, retry rejection handling, and `canDebug` remaining true while an active or retryable failed operation existed. Final checks on the working diff: frontend 1243 passed, focused MPA backend 53 passed, TypeScript pass, focused Ruff pass, i18n pass, production build pass with existing chunk warnings, and diff whitespace pass. `npm ci` remains the previously recorded dependency-lock failure and belongs to S5-13. Raw local evidence under ignored `evidence/browser/s5-12-20260919-r1/` is intentionally not committed. | -| 2026-09-20 | Local Studio Profile page error handling follow-up | pass | User validation against `http://127.0.0.1:5179/` exposed local Profile page errors after S5-12. Root causes were separated: the local backend had been started without `--dev`, so absent Studio TOS disabled the MPA operation repository and `GET /web/mpa/agent-operations?status=active` returned `503 mpa_operation_service_unavailable`; the selected Runtime `r-yev5cmzp4wzn6n5iodgd` was version 14 and still required legacy `X-Jwt-Token`, so native Session list returned upstream `401`; its Profile status endpoint also returned plain `404 Not Found`. Studio is now relaunched with `--dev`, and the BFF/frontend classify old Runtime auth/profile-status failures into stable safe errors with localized read-only guidance while keeping Runtime writes and Debug fail-closed. Verification: backend Profile route tests returned 28 passed, focused frontend source-contract run returned 1244 passed, i18n passed, local operation list returned `200 {"operations":[]}`, the old Runtime Profile view returned `200` with `safeError.code=profile_not_found`, and Session list remains an expected upstream `401 X-Jwt-Token header is required` until that Runtime is upgraded. See bugfix PRD [studio-mpa-profile-errors](../../bugfixes/studio-mpa-profile-errors/2026-09-20-studio-mpa-profile-errors.md). | - -## 13. Implementation change record - -2026-09-16: Closed the S4-07 lifecycle/PostgreSQL/browser gate. The loopback `turn_lifecycle` scenario now serves control status by Session and task, pause/resume commands, `new_turn_required` seeding, explicit Runtime `/continue`, continuation SSE, and call-count/state evidence. Studio displays `new_turn_required` as a localized checkpoint-continuation state instead of exposing the raw protocol enum. Real-browser `BC-09` passed with system Google Chrome via Playwright; raw evidence was captured locally under ignored `evidence/browser/mpa-s4-1789532050/BC-09/`. - -2026-09-16: Implemented the S5-01 secret reference migration foundation in `agentkit-mpa-agent`. Schema version 7 adds secret reference and migration status fields. The migration service is reference-first and retryable: provider failure or interruption before the database update leaves plaintext intact; success clears plaintext and records only references/status. Runtime execution consumes references through resolver seams at model and built-in MCP secret-use boundaries. Real AgentKit Vault provider wiring remains a later release-gate integration. - -2026-09-16: Implemented S5-01a canary-safe redaction in `agentkit-mpa-agent`. Legacy plaintext values discovered by secret-reference migration are registered for value-aware redaction before external reference creation. API access logging, Session output redaction, Runtime Console redaction, lifecycle details, and trace-step persistence remove registered literals from strings and nested structures without blanket-hiding business parameters such as `flow_id` or `user_code`. Schema version 8 subsequently extended the same boundary to Runtime operation response/resource snapshots. - -2026-09-17: Completed the durable Runtime operation and Profile CAS hardening. SQL and in-memory operation stores now share the same identity, TTL, safe replay, and compare-and-swap transition semantics; authorized status never returns principal/idempotency/request-hash internals. Profile create/update preconditions and revision switching execute under one PostgreSQL advisory-lock transaction, and a two-connection test proves only one writer can consume a given ETag. - -2026-09-18: Corrected legacy ADK endpoint authentication for AgentKit key-auth deployments. Runtime version 62 now resolves the authenticated Runtime principal from `Authorization` for `/list-apps` and other `require_auth` consumers in AgentKit mode while preserving legacy `X-Jwt-Token` behavior outside AgentKit mode. The change is confined to the authentication adapter and introduces no data-model migration. - -2026-09-18: Completed a live Studio chat-display acceptance run against Runtime version 62. The sandbox parent/child activities reached terminal state, the final answer remained present after completion and a 30-second delay, and refresh restored the same result. This evidence closes the specific production-like conversation regression but leaves S5-12 open until every documented browser Case is executed. - -2026-09-18: Completed S5-08 shared Python control-plane client extraction. BFF and future CLI consumers now use public MPA Profile/operation/capability/Session schemas from `veadk.integrations.mpa`; `MpaControlPlaneClient` centralizes Studio bearer auth, idempotency and ETag headers, structured safe errors, response validation, connection reuse, and terminal operation lookup. The BFF still owns Runtime authorization and orchestration, and the CLI remains a BFF client rather than importing FastAPI routes or Runtime credentials. This slice changes no Runtime schema or API. - -2026-09-18: S5-09 CLI design review resolved the command and ownership boundary before implementation. The existing `veadk mpa create` remains the infrastructure provisioning workflow; management parity is added under `veadk mpa control` and covers only the view, lifecycle operation, Profile, Session execution-configuration, and delete-preview APIs already owned by the Studio BFF. Inputs use JSON files/stdin, required idempotency keys and ETags are explicit, output/error exit codes are stable, and authentication material is never printed. Chat, Turn execution, and Runtime diagnostics are not reimplemented or reached by a direct Runtime connection; they stay on the current Studio execution/diagnostic surfaces until a separately reviewed shared BFF contract exists. This boundary needs no Runtime API or data-model change. - -2026-09-18: Implemented and verified S5-09 as reviewed. The new control CLI delegates every network operation to `MpaControlPlaneClient`, retains the old provisioning command, and covers the complete VC-22 management surface without adding a direct Runtime path. Success and BFF errors are parseable JSON; Click usage/input errors remain text with exit `2`. Authorization, conflict/precondition, retryable, and other BFF errors exit with `3`, `4`, `5`, and `1` respectively. The verified timeout case confirms that the first write reached the BFF before the caller retried with the exact same persisted idempotency key. - -2026-09-18: Completed the S5-10 compatibility preflight with a manifest pinned to both repository revisions and the live v62 registry digest. The approved combination matched `p0-codex-rest-v1`; unsupported protocol and malformed-digest fixtures failed with their specified exit codes before any mutation path. The deployed Runtime was independently observed as `Ready` at version 62, and the VC-15 redaction suite remained green. This is the preflight half of VC-19 only; S5-13 still owns the final regression, coverage, PostgreSQL, build, and performance gate after S5-11. - -2026-09-18: Completed S5-11 with live VC-21 run `vc21-20260918-045`. The isolated run created its own AgentKit Runtime, Tool, and PostgreSQL database from source Runtime v62, applied the Studio Profile, passed execution-ready smoke through the real Worker protocol, validated no-op update/recovery for the same image, verified compatible rollback with `noRollbackNeeded=true` and smoke, proved active-operation and active-session delete blockers, cleaned idle Sessions through Runtime APIs, then deleted the AgentKit Runtime and PostgreSQL database with final `residue=[]`. A previous live run exposed that recovered `failed_retryable/smoke_running` operation records could permanently block delete despite an applied Profile; Studio preview/delete now treats that recovered state as non-blocking while keeping true active operations and active Sessions blocking. - -2026-09-16: Implemented the S5-02 BFF `MpaAgentView` slice in VeADK. `GET /web/mpa/agents/{mpaInstanceId}/view` now centralizes the 0/1/N Runtime binding decision, active operation summary, Runtime metadata, Profile status, capabilities, and safe error. Zero binding is `runtime_missing`, multiple binding is `binding_ambiguous`, and a unique MPA-tagged Runtime exposes the binding status that later slices use to split Profile write, Session configuration, and Debug capability. The frontend detail UI that consumes this view model remains S5-03. - -2026-09-16: Implemented the S5-03 frontend `MpaAgentView` consumption slice. `AgentWorkspace` now loads the dedicated view model for MPA detail pages, surfaces binding health in the basic information area, blocks Session configuration when Runtime binding is missing/ambiguous/orphaned, and leaves non-MPA detail/update/version flows isolated on their existing logic. - -2026-09-16: Implemented the S5-04 Runtime Console/Trace correlation slice. Runtime trace APIs now return stable correlation fields for Agent/Profile/config/Turn/task/runtime/worker identity alongside the existing bounded, redacted details. Studio can consume these fields directly in S5-05 instead of parsing arbitrary `details` JSON. - -2026-09-16: Implemented the S5-05 Studio diagnostics consumption slice. The MPA Agent detail page now has an MPA-only Diagnostics section that reads Runtime Console run history and the latest trace through the existing Runtime proxy, displays structured correlation, and preserves non-MPA detail/version/update isolation. - -2026-09-16: Implemented the S5-06 compatibility preflight slice for MPA Runtime lifecycle writes. `scripts/mpa_p0_contract.py` now delegates to the shared `veadk.cli.mpa_p0_contract` evaluator. Studio runs the same matrix check before direct Runtime update and before GitHub delivery setup, source sync, and rollback PR creation for MPA targets. Incompatible manifests fail with safe structured `409` details before mutation; compatible manifests continue the existing path. Runtime data model and mpa-agent APIs are unchanged in this slice. - -2026-09-16: Implemented the S5-07 authorized delete preview and staged cleanup slice. The new Studio BFF preview endpoint shows exactly what will be cleaned: visible idle/terminal Runtime sessions first, their diagnostics/mount metadata through the Runtime session cleanup path, then the AgentKit Runtime resource. Delete execution re-runs the same checks server-side, refuses active operations and active sessions in `queued|running|pausing|paused|resuming`, treats per-session delete `404` as already cleaned for retry safety, and ignores untrusted browser `agentCategory` when the Runtime tag does not identify the resource as MPA. This slice deliberately reuses existing mpa-agent session list/delete APIs and AgentKit Runtime delete APIs; no Runtime database migration or new mpa-agent endpoint is required. - -2026-09-17: Implemented S5-08 Studio unified Profile management entry. The MPA detail page now exposes Profile configuration as the primary Agent-level management surface, with status/apply/reapply/edit actions backed by the MPA BFF Profile operation path. `Edit Profile` reuses the existing Studio Agent creation workbench in Profile-only mode, so users modify the same model/system/tool/skill/MCP/multiagent data that creates an Agent, but saving only applies the Profile to mpa-agent and does not run the generic Runtime deployment/update flow. The BFF view now allows `orphan_runtime` write capability for first Profile apply; debug and Session configuration stay blocked until Profile binding is complete. Local `veadk studio --dev` uses a process-local MPA operation repository when Studio TOS is absent, preserving idempotency and ETag semantics for local validation while production continues to require TOS persistence. Runtime Session creation now initializes execution-config revision 1 for new MPA Sessions when Studio supplies `mpaInstanceId + profileRevision` and omits `executionConfigRevision`. This keeps AgentKit Studio as the unified control plane while avoiding Managed Agent APIs and avoiding duplicate mpa-agent product forms. - -2026-09-17: Fixed the completed-answer disappearance path in persisted multi-round history. The first reconciliation fix was necessary but insufficient: strict browser inspection showed that history replay reused `adk-history-0` for each user-delimited assistant segment, so upsert replaced earlier answers. `eventsToTurns(...)` now creates a unique monotonically indexed projector prefix per history segment. Reconciliation remains fenced by the exact final event ID and refuses to replace a transcript after a newer local reply starts. Regression and browser acceptance now assert ordered assistant turn nodes, delayed stability, and full reload persistence rather than searching the whole page for expected text. - -2026-09-17: Fixed live MPA parent sandbox activity convergence. The live `/sse` path can finish with the wrapper `sandbox_task` function response but without replaying the persisted sandbox `invocation.completed` event. The projector now consumes `finalAlreadyEmitted=true` as a terminal control signal only while the invocation remains active: it closes the existing parent card and the turn while preserving streamed output. Persisted replay still ignores that wrapper after the sandbox final event has already closed the turn, preventing a duplicate tool card. diff --git a/prd-spec/features/mpa-p0-productionization/2026-09-15-mpa-p0-productionization-design.zh.md b/prd-spec/features/mpa-p0-productionization/2026-09-15-mpa-p0-productionization-design.zh.md deleted file mode 100644 index 628f01e60..000000000 --- a/prd-spec/features/mpa-p0-productionization/2026-09-15-mpa-p0-productionization-design.zh.md +++ /dev/null @@ -1,438 +0,0 @@ -# MPA AgentKit P0 功能迁移与落地 - -- Change ID:`mpa-p0-productionization` -- 状态:`approved`;实现与浏览器验证已完成至 S5-12,S5-13 至 S5-15 证据门禁待完成 -- 创建 / 修订:2026-09-15 / 2026-09-20 -- English:[2026-09-15-mpa-p0-productionization-design.md](2026-09-15-mpa-p0-productionization-design.md) -- 组件契约:[Studio MPA 控制面](../../../specs/studio-mpa-control-plane/README.zh.md)、[MPA Runtime 部署](../../../specs/mpa-runtime-provisioning/README.zh.md)、[MPA Runtime 控制](../../../specs/mpa-runtime-control/README.zh.md) - -## 1. 结论与边界 - -AgentKit Studio 是未来唯一的 MPA 管理入口;`agentkit-mpa-agent` 是执行 Runtime。本期不开发或依赖 Managed Agent CRUD、发布版本或 Session。Studio 复用现有 Agent 创建表单和资源选择器,把已接受配置规范化为不可变 MPA Profile revision,并直接应用到 mpa-agent。MPA Agent 详情页是查看 Profile 状态、应用/重新应用 Agent 级 Profile、管理 Session 执行配置、查看诊断和删除 Runtime 的统一入口。对于 MPA,不再把用户引导到依赖 `/list-apps` 的通用 Runtime update capability 路径,因为该路径无法准确表达 A2A-only 的 mpa-agent Runtime。mpa-agent 不建设模板中心、专家市场、审核安装或独立发布产品。`arkclaw-team` 只用于核对旧功能,不作为新链路依赖,也不迁移其历史数据。 - -本期完成 P0 核心链路:创建/更新 MPA Agent、Runtime 准备、Profile 应用、Session、对话和 worker 执行、会话执行配置、暂停/恢复/续跑、历史结果、Debug/Trace、运行时更新与故障恢复。当前 VeADK/Runtime 主干已由其他 feature 独立交付消息渠道绑定、飞书/钉钉/企微配置、shared APIG 部署、持久 Runtime/数据库部署、Skill Space 部署和完整 artifact answer replay;这些能力可作为集成基础,但渠道/provider 专项验收仍归 MPA 渠道与部署契约,不替代本 P0 的 AC/VC 门禁。网站集成、资源库和全局搜索仍不属于本 P0。 - -| 系统 | 唯一职责 | 明确不做 | -| --- | --- | --- | -| AgentKit 平台 | 现有 Studio 创作入口、Skill/Tool/Environment/Vault 等平台资源、Runtime 生命周期与账号权限 | P0 不新增或依赖 Managed Agent CRUD/version/Session | -| VeADK Studio/BFF | 校验 Agent draft、规范化 MPA Profile revision、编排 Runtime/Profile 应用并展示状态 | 不创建第二个平台产品,不直接驱动 worker | -| `agentkit-mpa-agent` | Profile 执行投影、Session/Turn、资源解析、worker 编排、运行控制与诊断 | 不提供模板/专家/审核安装/独立 Agent 版本产品 | -| `mpa-codex-worker` | 已有隔离执行协议 | 除非实测证明协议缺口,否则不修改 | -| `arkclaw-team` | 只读功能参考 | 不依赖、不双写、不迁移、不兼容旧 ID/API | - -## 2. 已验证事实 - -2026-09-15 的静态审计版本为 VeADK `34658d75`、agentkit-mpa-agent `2f5e039`、arkclaw-team `c15d611`。使用两个测试账号做了只读验证;未执行创建、更新或删除云资源。临时 arkcli profile 已删除。 - -| 事实 | 证据 | 设计结论 | -| --- | --- | --- | -| Studio 已有 Agent draft,覆盖模型、系统指令、Tools、Skills、MCP、多 Agent 结构和部署设置 | `frontend/src/create/types.ts`、`CustomCreate.tsx`、`deployAgentkitProject` | 复用该 UI/数据,只新增 Profile 规范化和 Runtime 接线 | -| 当前测试账号未开通 Managed Agent create | 2026-09-15 live `CreateAgent` 返回 `ManagedAgentNotOpen` | Managed Agent 明确不在 P0 范围,不能阻塞 MPA 交付 | -| AgentKit Runtime list 可读且存在 Ready 的 MPA Runtime;SDK已有 create/get/list/update/release/delete/listVersions/listInstances/getLogs | 测试账号和本地 SDK/VeADK adapter | 复用平台 Runtime API;VeADK 只补齐编排、readiness 和错误恢复,不造第二套生命周期 | -| Skill、Environment、Vault provider 可通过真实账号查询 | 只读云验证成功 | 资源选择与授权复用 AgentKit API;mpa-agent 做引用解析和执行适配 | -| runtime 已有 `/api/v1/agents`、Session CRUD/run/SSE/events、MCP、A2A pause/resume、health/readiness/console | agentkit-mpa-agent 路由与测试 | 现有接口优先扩展;仅新增 Profile 同步、执行配置版本和 runtime-owned continuation | -| `mpa_agents` 已是单 Runtime 的配置 revision 表 | `app/stores/agent_config.py` | 复用为执行投影;补来源版本/摘要/应用状态,不另建 Agent definition 表 | -| `RunRequest` 目前只有 `content` 且 `extra=allow`;run 无 `Idempotency-Key` | `schemas/session.py`、`api/v1/sessions.py` | 需显式增加执行配置版本和强幂等,不能依赖未校验扩展字段 | -| pause/resume 只记录最后一次 command key/action | `a2a_task_control.py` | 可复用 generation 状态机,但要补 request hash 与结果持久化,才能识别同 key 不同 payload | -| Studio 超时续跑会拼接 prompt 再调用 `send` | `frontend/src/App.tsx` | 改为 runtime-owned continuation API,避免失去 Turn 关联或重复副作用 | -| “我的 Agent”已有 MPA 分类但创建入口只覆盖 general/Sandbox | `frontend/src/ui/MyAgents.tsx` | MPA Tab、Header、空态接入 AgentKit 原生创建流程 | - -现有 “321 个 runtime 测试通过”只代表先前基线,本轮没有重跑,不作为本次文档整改的当前测试结果。 - -## 3. 用户场景与功能要求 - -### 场景 A:创建并首次运行 MPA Agent - -**Given** 用户在 AgentKit Studio 创建 Agent,并选择 MPA Runtime 类型。 - -**When** Studio 校验 Agent draft、创建不可变 MPA Profile revision、准备/复用 Tool 与 Runtime,并将该 Profile 应用到 runtime。 - -**Then** 用户看到统一的创建进度;只有 Profile 已应用且 execution-ready smoke 通过后,状态才为“可运行”;随后可创建 Session 并获得首个流式结果。 - -### 场景 B:更新 Agent Profile - -**Given** 已应用 MPA Profile 当前为 revision N。 - -**When** 用户保存模型、System、Tools、Skills、MCP 或 Multiagent 变化。 - -**Then** Studio 使用当前 ETag 和稳定幂等键向 runtime 提交新 digest;Runtime 原子分配 revision N+1。相同 key/digest 返回原 revision,相同 key 不同 digest 返回 `409 idempotency_mismatch`,stale ETag 返回 `412 profile_changed`。 - -### 场景 C:修改会话执行配置 - -**Given** Session 当前执行配置版本为 6,包含 Agent 默认 Profile 加会话级覆盖。 - -**When** 浏览器 A 将模型改为 M2,同时浏览器 B 仍以版本 6 修改 Skill。 - -**Then** A 成功生成版本 7;B 收到 `412 execution_config_changed` 和当前版本 7,不会覆盖 A。已经运行的 Turn 继续使用启动时记录的版本 6;下一 Turn 使用版本 7。 - -“会话执行配置版本”是 runtime 为一次 Session 保存的、可递增比较的执行配置记录,不是复制平台资源本体。例如它只记录 `skillId=s-1, version=3`、`environmentId=e-2, version=5` 和模型覆盖;Skill 内容、Environment Secret 仍由 AgentKit 管理。Turn 开始时把实际采用的版本号和解析结果写入 Turn 执行记录,以保证执行中配置改变不会污染当前 Turn。 - -### 场景 D:暂停、恢复与超时续跑 - -**Given** 一个 Turn 包含主执行器和两个 worker。 - -**When** 用户暂停。 - -**Then** 所有活动参与方到达安全点后才显示 `paused`;部分确认显示 `pausing` 或超时失败。300 秒内且租约仍有效时恢复原 Turn;超过边界或不可恢复时,只有用户显式点击继续才由 runtime 原子创建关联的新 Turn。 - -### 场景 E:刷新与故障恢复 - -**Given** 请求在写入执行记录后、实际 dispatch 前失败,或浏览器在流式执行中刷新。 - -**When** 客户端用相同幂等键重试或携带 event cursor 重连。 - -**Then** runtime 返回原 invocation/结果并从游标继续,不创建重复 Turn,不重复累计事件和用量。 - -## 4. 核心设计 - -### 4.1 AgentKit Profile 到 runtime 的接线 - -创建/更新分两层完成: - -1. 浏览器把现有 Studio Agent draft 提交给 VeADK BFF;不调用 Managed Agent API。 -2. BFF 校验资源引用,并把已接受 draft 规范化为 `sourceProfileId + profileDigest`;BFF 不分配 revision,且禁止把未校验浏览器字段直接转发给 runtime。 -3. BFF 调用 mpa-agent 的 Profile apply 接口;runtime 校验版本、引用和权限后写入新的 `mpa_agents` revision。 -4. BFF 查询应用状态;失败时保留上一个已应用 MPA Profile revision 和可重试 operation,不把 MPA Agent 标记为可运行。 -5. 新 Session 固定创建时选择的 Profile revision;活动 Turn 永不热切换。 - -BFF 复用现有 Studio draft 解析及 AgentKit 资源 client,新增类型化 mpa-agent Profile client,而不是 Managed Agent OpenTOP adapter。Runtime 在成功的 ETag/CAS 事务内分配唯一 `profileRevision` 并返回;BFF 不预分配 revision。冲突返回 `409 profile_version_conflict` 和当前 revision,不自动覆盖。 - -P0 不引入新的自签 assertion 服务。Studio 在 OAuth/gateway 模式下已经持有经过验证的用户 bearer token,AgentKit Runtime 使用 custom JWT authorizer 校验同一 issuer/client;BFF 只转发已由中间件验证的 bearer token。mpa-agent 将 REST bearer JWT 和 A2A TIP 都投影为统一 `RuntimePrincipal`,并在新写接口执行业务 scope 校验。已实现的 S4-06 Runtime 子集新增共享 `RuntimePrincipalAuthorizer` 接线点,并在 `/run`、resume、`/continue`、模型/workload TIP 或 API key 解析、sandbox run 凭据准备和工具 dispatch 前复核当前授权。Runtime 在 accepted Turn/inbox recovery 中只携带规范化的非 secret principal 快照,不落盘 bearer token。本地 claim/rejection 与 fake-revocation contract 是 S1/S4-06 门禁;issuer、audience/client、TTL、JWKS 轮换、真实撤权查询和一次授权 Profile apply/status 仍是部署环境发布门禁。两个阶段都不能降级为浏览器 Header、Runtime key 或关闭 JWT。 - -| Studio AgentDraft 字段 | runtime 字段/行为 | 转换规则 | -| --- | --- | --- | -| Studio draft ID | `sourceProfileId` | 稳定来源标识,用于审计和幂等;不是 Managed Agent ID | -| `Name`, `Description` | `name`, `description` | 原样传递并执行长度校验 | -| `System` | `agentsMd` | 作为运行指令正文;保留来源摘要 | -| `Model.Id` | `model` | 去除空白;不得含 provider URL 或凭据 | -| `Tools[]` | `tools[]` | 保留 type/name/enabled/permission policy;不支持类型返回 `422 unsupported_profile_field` | -| `Skills[]` | `skills[]` | 固定 `SkillId + Version`;缺版本先由 BFF 解析为明确版本 | -| `MCPServers[]` | `mcp` | 转为 ID 白名单/分组授权;Secret 只传引用 | -| `Multiagent` | runtime subagent 配置 | 保留角色、Agent 引用与版本;不支持结构显式失败 | -| `Metadata`, `Tags` | `metadata` | 只保留 allowlist 的非敏感键;分类继续使用 `veadk:agent-type=mpa` | -| Managed Agent 专属字段 | 不接受 | Managed Agent CRUD/version/Session 不在 P0;MPA Session 覆盖由 runtime `session_execution_configs` 管理 | - -### 4.2 接口复用、扩展与新增 - -| 接口 | 类型 | 调整 | -| --- | --- | --- | -| `GET /web/mpa/agents/{mpaInstanceId}/view`、`POST /web/mpa/agents`、`PATCH /web/mpa/agents/{mpaInstanceId}` | 新增 MPA BFF 编排 | 浏览器只调用 BFF。create/update 接受现有 Studio AgentDraft,规范化为 MPA Profile 后调用 Runtime API;不调用 Managed Agent CRUD/version API。create/update 均要求浏览器持久化 `Idempotency-Key` 并返回 `202 + operationId` | -| `GET /web/mpa/agents/{mpaInstanceId}/delete-preview` | 新增 BFF 编排 | 浏览器在单个 MPA Runtime 删除前调用。Query 接受 `runtimeId` 和 `region`。BFF 解析已授权 MPA Runtime binding,读取 Runtime `profile-status`,调用现有 Runtime `GET /api/v1/sessions?include_a2a=true`,并返回 `canDelete`、`blockers`、可见会话计数、活动会话摘要和清理阶段 | -| 带 MPA Runtime tag 的 `POST /web/delete-runtime` | 扩展既有 BFF 路由 | 既有请求保留 `runtimeId` 与 `region`;`agentCategory` 和 `mpaInstanceId` 只是可选提示。服务端会重新检查 Runtime 标签 `veadk:agent-type=mpa` 后才使用 MPA 清理。MPA 删除会在 mutation 前拒绝 active operation 和 active session,先通过现有 Runtime API 删除可见 idle/terminal Runtime Session,再删除 AgentKit Runtime | -| AgentKit Resource/Environment/Skill/Tool/Vault API | 复用 | BFF 负责权限化读取和版本解析;runtime 仅消费稳定引用。AgentKit Managed Agent Session API 不进入 P0 主链路 | -| AgentKit Runtime create/get/list/update/release/delete/versions/instances/logs | 复用 | VeADK adapter 负责 provision、状态归一和恢复步骤 | -| `GET /web/mpa/agent-operations?status=active`、`GET /web/mpa/agent-operations/{operationId}`、`POST .../{operationId}/retry` | 新增 BFF 编排 | TOS CAS 持久 create/update operation;按 principal 授权。create/update 的首次响应丢失后通过 active list 或同 key 重放恢复;retry 从最后安全阶段继续 | -| `PUT /api/v1/agents/{mpaInstanceId}/profile` | 新增 | payload 带 `sourceProfileId` 和 digest,不携带调用方分配的 revision;Runtime 分配并返回 `profileRevision`。首次仅允许 `If-None-Match: *`,后续仅允许 `If-Match: ""`;两者同传返回 `400`,均缺失返回 `428`;响应返回强 ETag | -| `GET /api/v1/agents/{mpaInstanceId}/profile-status` | 新增 | 返回 `pending/applying/applied/failed`、已应用版本、目标版本、安全错误与 retryable | -| `GET /api/v1/sessions/{sessionId}/execution-config` | 新增 | 返回当前会话执行配置版本、ETag、Profile 默认 revision、overrides、effective 和 invalid references。已实现的 P0 子集只为带 `mpaInstanceId + profileRevision` 的 MPA Session 初始化该状态 | -| `PATCH /api/v1/sessions/{sessionId}/execution-config` | 新增 | `If-Match` 强制 CAS;按类别 replace/clear/inherit;只影响下一 Turn。已实现的 P0 子集支持 `model` 与 `mcpServers`;其他类别在资源 resolver 存在前返回 `422 unsupported_execution_config_category` | -| `POST /api/v1/sessions/{sessionId}/profile-upgrade` | 新增 | body 含 `targetProfileRevision`,Header 含当前 execution-config `If-Match` 与 `Idempotency-Key`;目标 Profile 必须已 applied。活动 Turn 可提交但仅下一 Turn生效;版本回退返回 `409 downgrade_not_allowed`。Runtime 将 request hash 与安全结果持久化到 SQL `runtime_operations`;相同 key/相同目标可跨 Runtime 进程 replay,相同 key/不同目标返回 `409 idempotency_mismatch` | -| `POST /api/v1/sessions/{sessionId}/run` | 扩展 | 请求显式增加 `executionConfigVersion`;要求 `Idempotency-Key`;返回已接受版本与 invocation | -| 现有 A2A submission | 扩展 | Agent Card 声明 `urn:veadk:mpa:execution:v1`;`params.metadata.veadkExecution` 包含 `executionConfigVersion` 与 `idempotencyKey`,两者参与规范化 request hash。缺失扩展的旧客户端只允许 default revision,不能使用 Session override | -| 现有 `GET/POST .../control` | 扩展 | 保留 URL,增加参与方状态、request hash、结果重放;不破坏旧 capability 客户端 | -| `POST /api/v1/a2a/tasks/{taskId}/continue` | 新增 | runtime 原子创建关联 Turn;要求 idempotency key 与 expected generation | -| `GET /api/v1/runtime-operations/{operationId}` | 新增 | 按 principal/target 授权查询 Profile/run/upgrade/continue 的异步状态与安全结果;不返回 Secret | -| Session CRUD/SSE/events/MCP/health/readiness/console | 复用 | 补 scope 校验、cursor 与结构化关联;不另建重复接口 | - -Profile apply 请求的最小形态: - -```json -{ - "sourceProfileId": "studio-agent-example", - "sourceProfileDigest": "sha256:example", - "profile": { - "name": "Research Agent", - "system": "...", - "model": {"id": "model-example"}, - "tools": [], - "skills": [{"skillId": "s-example", "version": "3"}], - "mcpServers": [], - "multiagent": null, - "metadata": {} - } -} -``` - -首次 apply 只带 `If-None-Match: *`;已有投影只带 `If-Match: ""`。两者互斥,成功响应与 status 均返回 `profileRevision` 和 `ETag: ""`。相同 operation key/digest 幂等返回原 revision,相同 key 不同 digest 返回 `409 idempotency_mismatch`,stale ETag 返回 `412 profile_changed`。 - -BFF 在 Runtime 创建/Profile apply 前把 MPA lifecycle operation 写入 TOS,key 为 `veadk-studio/v4/users/{principal}/mpa-agent-operations/{operationId}.json`,使用 `forbid_overwrite` 创建与 ETag 条件更新。create 与 update 均要求客户端先持久化 `Idempotency-Key`;BFF 以 `principal + operationKind + mpaInstanceId/createIntent + key + requestHash` 唯一创建 operation,两个入口都返回 `202 + operationId`。相同请求返回原 `operationId`,同 key 不同内容返回冲突。`GET /web/mpa/agent-operations?status=active` 是首次响应丢失后的刷新兜底。创建状态机为 `runtime_preparing -> profile_applying -> smoke_running -> runnable`;更新状态机为 `profile_applying -> succeeded`,均可进入 `failed_retryable|failed_terminal`。记录 MPA instance ID、from/target Profile revision、Profile digest、Runtime ID、Profile operation ID、阶段、重试次数、安全错误和时间戳,不保存凭据。恢复时按 Runtime binding 与 Profile status 对账,从最后安全阶段继续。 - -本地 Studio 开发态可在 `veadk studio --dev` 且未配置 Studio TOS 时使用进程内 operation repository。它让本地 Profile apply 与 retry 流程可以对真实 Runtime 做验证,同时保留相同的 operation ID、request-hash 幂等和 ETag compare-and-swap 语义。该 store 不具备持久化能力,也不能作为发布证据;非 dev 部署仍要求 Studio TOS,缺失时 Profile 写路由返回 `mpa_operation_service_unavailable`。 - -### 4.3 幂等、错误与性能 - -Profile apply、profile-upgrade 和 continuation 在需要可复用异步状态/replay envelope 时使用 `runtime_operations`,按 `principal + operation + target + Idempotency-Key` 唯一,保存规范化 request hash、response snapshot、资源 ID 和 24 小时过期时间。Run acceptance 使用专用的 `turn_execution_records` 作为 Turn ledger,因为持久重放值是已接受的 `turnId/invocationId/operationId` 与冻结 execution configuration。execution-config PATCH 只使用 revision/ETag CAS;pause/resume 只使用 `a2a_task_controls` 的 generation、request hash 和 replay result,不重复写 ledger。相同 key/相同 hash 返回原结果;相同 key/不同 hash 返回 `409 idempotency_mismatch`。pending Profile 不进入 `mpa_agents` current revision;成功事务才切换 applied revision。Run 与 continuation 在同一数据库事务内写 `turn_execution_records` 和 dispatch intent,再由幂等 dispatcher 执行。不得承诺与外部 AgentKit/A2A TaskStore 的分布式 ACID。 - -统一错误体为 `error.code/message/requestId/retryable/currentState`。关键错误包括 `profile_version_conflict`、`execution_config_changed`、`session_busy`、`unsupported_profile_field`、`resource_unavailable`、`idempotency_mismatch`、`not_execution_ready`。旧接口的 envelope 只在 capability 版本升级后改变。 - -MPA 删除刻意不声明为新的分布式事务。预览和执行都会从服务端当前状态重新计算阻断原因。active operation 和 active Session 的阻断以执行时为准,因此过期浏览器预览不能强制删除。重复删除时,如果某个 Session 已被清理并返回 `404`,视为该 Session 已完成清理并继续剩余阶段;AgentKit Runtime 删除仍是最后一个平台副作用。当前预览范围是已授权调用方在目标 Runtime API 中可见的 Session,不是跨用户全局 Session 索引。 - -| 操作 | P0 服务端目标(不含调用方网络) | 约束 | -| --- | --- | --- | -| Profile apply 接受/幂等重放 | p95 ≤ 500 ms | 外部资源详情校验异步化;同步验证 ID、版本、摘要和权限 | -| Session execution-config GET/PATCH | p95 ≤ 300 ms | 单次事务;远程资源读取不持有数据库锁 | -| Run 接受 | p95 ≤ 500 ms | 返回 accepted/operation ID,不等待模型完成 | -| pause/resume/continue 接受 | p95 ≤ 500 ms | 返回目标状态;多参与方收敛异步观察 | -| SSE 首事件 | p95 ≤ 2 s | Runtime 已 warm 且不包含模型首 token SLA | -| 列表与详情 | p95 ≤ 1 s | 服务端分页,默认 20、最大 100;禁止 N+1 Runtime/资源扫描 | -| MPA 删除预览 | p95 ≤ 1 s | 一次 Runtime binding/profile 查询和一次有界 Runtime Session 列表;传入 `runtimeId` 时不做跨 region 扫描 | - -性能结果以 warm-up 5 次后至少 100 次请求、并发 10、固定 20 项列表和同 region 测量;报告 p50/p95/max、错误率和测试 revision。外部模型首 token、Runtime 冷启动分别统计,不混入 API 接受延迟。未执行基准前这些数值是目标而非通过证据。 - -### 4.4 状态机 - -Runtime operation:`accepted -> running -> succeeded | failed_retryable | failed_terminal`。Profile apply 当前在接受后同步完成,因此通常直接从 `accepted` 进入 `succeeded` 或 `failed_terminal`;其对外成功结果仍为 `status=applied`。`failed_retryable` 可重新进入 `running`,终态不能改写为另一终态。状态更新使用 compare-and-swap,避免并发完成互相覆盖。 - -Runtime 准备:`prepared -> resource_created -> transport_ready -> profile_applied -> execution_ready -> verified`,任一步可进入 `failed_retryable` 或 `failed_terminal`。只有 `verified` 对用户显示“创建成功且可运行”。 - -Turn:`queued -> running -> pausing -> paused -> resuming -> running -> completed|failed|cancelled`。部分参与方确认保持 `pausing`;generation 变化后旧 ACK 无效。已实现的 S4-02 Runtime 子集把现有控制 API、A2A 主执行器和 Codex worker interruption 路径接入 `ParticipantBarrierService`:pause 创建下一代参与方 generation,只有当前 generation 的所有活跃参与方都确认安全点后,聚合 task 才进入 `paused`;在 task 已经 `paused` 后才启动的 late worker 仍会收到 worker pause,并在当前 generation ACK 后才能 resume;completed/failed/cancelled worker 会写入终态,不会阻塞后续 pause。已实现的 S4-03 子集在 task owner lease 仍有效时,允许恰好 300 秒边界内的 same-Turn resume;暂停超过 300 秒、task owner lease 丢失,或当前 generation 存在 `lost|failed` participant 时,resume 返回 `new_turn_required`;相同 key 的 `new_turn_required` 重试会幂等 replay。resume 会把聚合 task 恢复为 `running`,并从上一代 paused generation 生成下一代 running participant 记录。已实现的 S4-04 Runtime 子集暴露 `POST /api/v1/a2a/tasks/{taskId}/continue`:在 `new_turn_required` 后,通过旧 task 的 invocation 找到上一条业务 Turn,写入带 `continuation_of=` 的新 `turn_execution_records` 行,复用冻结的 execution-config snapshot 和 dispatch payload,关闭旧 active dispatch 行,并使用现有 inbox/outbox worker 调度。已实现的 S4-05 Studio 子集移除 prompt-based continuation fallback:当 resume 返回 `new_turn_required` 时,Studio 调用 Runtime `/continue`,再用返回的 `invocationId` 和当前 `lastEventId` 订阅 Runtime `/sse`,不再发普通 `/run`,也不再拼接 prompt。已实现的 S4-06 Runtime 子集会在接受 run/resume/continue 副作用前,以及后台 secret/tool 副作用前复核当前 RuntimePrincipal 授权。若注入的 authorizer 判定授权已撤销,resume 和 continue 会在状态变更或 continuation 接受前返回 `403 runtime_action_forbidden`;已接受的 inbox 执行也会在模型 TIP、workload TIP、OpenViking API key、sandbox run 凭据或 sandbox/tool dispatch 使用前失败。已实现的 S4-07 浏览器门禁验证了端到端 Studio loopback 行为:部分 worker ACK 时仍保持 `pausing`;所有参与方到达安全点后才显示恢复;same-Turn resume 回到 `running`;`resumeDisposition=new_turn_required` 的中断 task 在 UI 中显示为用户可理解的检查点续跑状态;浏览器刷新不会自动创建 continuation;只有用户显式点击恢复时才调用 Runtime `/continue` 并继续订阅 `/sse`。 - -会话执行配置:每次成功 PATCH 产生单调 `revision`;不存在独立业务 status。校验失败不产生 revision,并在响应中返回 invalid reference;Turn 记录 accepted revision 后不可变。 - -Session Profile 升级与普通配置修改共享同一配置 revision,但升级必须走专用 API。以目标 Profile 为新 base,保留显式 override/clear,inherit 类别采用目标 Profile 默认值,再重新解析 effective refs。任何资源缺失、撤权或不兼容均在事务前原子拒绝,旧 revision 完整保留,不生成“不可执行 revision”。成功事务同时把 `session_meta.profile_revision/execution_config_revision` 指向新 revision;重复 key/hash 返回同一新 revision;并发 ETag 变化返回 `412`。 - -删除状态机:`preview -> blocked | ready -> cleaning_sessions -> deleting_runtime -> deleted | failed_retryable`。当 MPA operation store 中存在真正 `active` 的 create/update operation、Runtime binding 缺失或重复、Runtime Profile orphan,或 Runtime Session 处于 `queued|running|pausing|paused|resuming` 时,删除在任何 mutation 前进入 `blocked`。如果某条遗留 operation 为 `failed_retryable`,但 Runtime binding 已是 `bound` 且 `profile-status` 返回 `applied`,说明 Runtime 已收敛,不应继续阻止用户清理资源。`cleaning_sessions` 只删除可见 idle/terminal Runtime Session,并把单个 Session 的 `404` 视为已清理。`deleting_runtime` 只在 Runtime 侧清理成功后调用 AgentKit Runtime delete API。 - -## 5. 数据模型 - -### 5.1 复用原则 - -- Studio 是已接受 MPA Profile 的创作权威;mpa-agent 的 `mpa_agents` 保存不可变执行 revision。不新增 Managed Agent、模板/专家/definition/review/install 或发布版本表。 -- AgentKit Skill、Tool、Environment、Vault、Resource 保持各自权威;runtime 只保存 ID、固定版本、摘要和 Secret reference。 -- 不修改 AgentKit 平台自有表,不迁移 ArkClaw 表,不建立旧 ID 映射。 -- 新列为 additive;新 store 同时实现 PostgreSQL 与内存版本。 - -### 5.2 P0 最小调整 - -| 模型 | 调整 | 用途 | -| --- | --- | --- | -| `mpa_agents` | 增加 `source_profile_id`、`source_profile_digest`、`apply_status`、`apply_operation_id`、`apply_error_code`、`agent_api_key_ref`、`model_api_key_ref`、`secret_migration_status`、`secret_migration_operation_id`;现有 Runtime 分配的 revision 即 `profileRevision` | 保存不可变 MPA Profile revision 和应用状态,不引入第二个 revision allocator。S5-01 仅把 legacy 明文字段保留为迁移输入和一个版本的兼容读取;AgentKit mode 禁止新明文写入,并且只有受保护引用创建成功后才清空旧值 | -| `mpa_meta` | 增加 `agentkit_mode`、`workspace_id`、`bootstrap_manifest_revision`、`readiness_phase`、`finalization_operation_id`、`last_error_code`;凭据仍使用现有受保护字段或服务端引用 | 禁止 AgentKit 模式回退 ArkClaw,并支持分阶段恢复 | -| `session_meta` | 增加 `account_id`、`workspace_id`、`mpa_instance_id`、`profile_revision`、`execution_config_revision` | 建立 Session 所属范围及当前配置版本 | -| `session_execution_configs` | 新表:`app_name`、`session_id`、`revision`、强 `etag`、`mpa_instance_id`、`profile_revision`、`profile_default_revision`、JSON `overrides`、JSON `effective_refs`、JSON `invalid_refs`、`updated_by`、timestamps。主键为 `(app_name, session_id, revision)`;当前版本读取使用 `(app_name, session_id, revision DESC)`。SQL CAS 在读取当前 revision 并追加下一条不可变记录前,先获取 Session 维度的 PostgreSQL advisory transaction lock。 | 保存“会话执行配置版本”,替代浏览器 localStorage 权威。它只保存稳定引用和执行选择,不保存 AgentKit 资源正文或 Secret。例如 revision 1 记录 Profile 3 默认值和 Skill `skill-1@3`;revision 2 记录模型覆盖和 Skill `skill-1@4`;已被 Turn 接受的 revision 1 仍可读取且不会被修改。 | -| `turn_execution_records` | 新表:task/invocation/session、accepted config revision、resolved refs、model、policy revision、secret refs、dispatch key/hash/status、`continuation_of`、timestamps | 冻结每个 Turn 实际配置并支持崩溃恢复/续跑 | -| `a2a_turn_participants` | 新表:task、generation、participant ID/kind/status、安全点、checkpoint、lease、ACK/终态时间 | 证明主执行器与所有活动 worker 均已暂停 | -| `a2a_task_controls` | 增加 command request hash/result reference;只有实际接入高风险工具审批时才增加 approval 字段 | 完整实现 pause/resume 幂等;避免预建通用 policy 产品 | -| `runtime_operations` | 新表:principal/operation/target/key 唯一键、request hash、state、response snapshot、resource IDs、expiry | 承载 Profile 生命周期和后续长耗时控制操作的强幂等与通用状态查询;run acceptance 自身由 `turn_execution_records` 负责 | -| trace/lifecycle stores | 按需增加 task、config revision、runtime/worker version 等索引列 | 通过结构化关联完成 Debug,不复制正文大字段 | - -不新建 `mpa_agent_definitions`、`mpa_agent_definition_versions`、`turn_continuations`、`policy_evaluations` 或通用 `approval_requests`。MPA Profile revision 使用现有 `mpa_agents` 表;continuation 关系放入 `turn_execution_records.continuation_of`;授权在每个副作用前实时重查并记录到 Turn/trace。只有真实 P0 高风险工具流需要持久审批时,才在 `a2a_task_controls` 增量扩展。 - -正式 migration 按 `runtime_operations -> mpa_agents/mpa_meta/session_meta 增量列 -> session_execution_configs -> turn_execution_records -> a2a_turn_participants -> Secret reference 列/索引 -> 其余索引/约束` 顺序执行,并保存 schema version。部署先扩 schema、再上线双版本可读代码、最后启用新写路径;旧代码无法安全读取新状态时禁止回滚。AgentKit mode 禁止新写 `agent_api_key/model_api_key` 明文;现有新平台值转为受保护引用后清空。S5-01a 还会在迁移读到 legacy 明文后、任何 provider 调用或诊断输出前,把这些明文注册到进程内 value-aware redaction registry。API access log、Session 对用户可见输出、Runtime Console 响应、lifecycle details、trace 持久化以及 `runtime_operations.response_snapshot/resource_ids` 均经过同一脱敏边界。Runtime operation 状态响应不返回 principal、幂等键或 request hash。 - -## 6. Studio 页面适配 - -| 入口 | P0 处理 | 原因 | -| --- | --- | --- | -| 我的 Agent / MPA Tab | 复用 Studio 现有创建表单,把 draft 作为 MPA Profile 提交,再启动 Runtime/Profile 应用编排 | 当前 MPA 分类可见但无法创建 | -| 创建与部署进度 | 用阶段化 operation 展示 Runtime 准备、Profile 应用、execution-ready;支持失败重试 | Profile 接受和 Runtime 就绪是异步阶段 | -| MPA 详情/Profile revision | BFF `GET /web/mpa/agents/{mpaInstanceId}/view` 返回 `MpaAgentView`:scope、MPA instance ID/current Profile revision、Runtime ID/version/readiness、active operation/stage、capabilities、safe error。详情页为 MPA Agent 提供专属 `Profile 配置` 分区,基于当前 Studio draft/profile projection 展示配置,并通过 `/web/mpa/agents` 或 `/web/mpa/agents/{mpaInstanceId}` 应用变更 | 0 个 Runtime 显示 `runtime_missing`;唯一 MPA Runtime 可管理;多个返回 `binding_ambiguous` 且禁止写。`orphan_runtime` 表示 Runtime 存在但尚未应用 MPA Profile,因此 Studio 仍可用 `If-None-Match: *` 执行首次 Profile apply;Session 配置和诊断在 Profile 应用前继续阻塞 | -| 新对话/Composer | 创建/选择 mpa-agent 业务 Session,提交明确的执行配置版本和幂等键 | mpa-agent 是 Session/Turn 唯一权威,避免双 Session 映射 | -| 暂停/恢复/历史 | 使用 runtime 权威状态与 continuation API;刷新按 event cursor 恢复 | 当前前端拼 prompt 会丢失关联 | -| 会话配置/MCP/拓扑 | 服务端 CAS 管理会话执行配置,显示继承、覆盖、失效、下个 Turn 生效;展示真实参与方 | 替代 localStorage 执行权威 | -| Workspace/Environment | 区分资源集合 ID、授权 workspace scope 和 worker mount ID,经 BFF 解析 | 三种 ID 语义不同,不能互换 | -| 用量/Debug/Trace/系统页 | 复用现有页面并增加 Agent/Session/Turn/runtime/worker 关联和脱敏 | 核心链路需要可定位和可恢复 | -| MPA 删除 | 确认前展示删除影响预览;存在 active operation 或 active session 时禁止确认;执行通过 BFF 清理 | 不可逆 Runtime 删除前,用户必须知道会清理哪些 Runtime Session 与诊断数据 | -| Review Center | 保留现有非 MPA 功能;P0 不增加 MPA 模板/专家审核入口 | 本期仅建设 MPA Profile 执行链路,不建设目录产品 | -| Applications/GitHub/coding | 保留并对 MPA target 做 capability 门禁 | 这些流程不是 MPA runtime 管理 | - -资源库、搜索、定时任务、飞书和网站集成页面本期保持原行为,不增加 MPA adapter,也不作为验收门禁。 - -## 7. 开发项与顺序 - -| 任务 | 主要工作 | 交付与依赖 | -| --- | --- | --- | -| `M0` 可行性与测试基座 | 固化 Studio Profile 映射;本地验证 Studio-to-runtime principal;建立 PostgreSQL 双连接 lane、fault injection、跨仓版本 manifest 和 E2E 资源/清理脚本 | **硬门禁**:本地 contract 与 PostgreSQL 基座通过;部署后再执行 live identity/Profile release gate | -| `T-2` Bootstrap 与身份 | 修改 VeADK provision/meta seed 和 runtime startup/auth,增加 AgentKit mode、分阶段 readiness、可恢复 operation | 全新部署不访问 ArkClaw;依赖可信身份接线 | -| `S1` 创建到首轮对话 | 复用 Studio 创建 UI、持久 MPA operation、Runtime/Profile apply、确定性 smoke、失败 retry/reconcile、最小页面 | 交付 `AC-1`、`AC-2`;安全 Case 持续执行 | -| `S2` Session 版本 | execution-config、显式 profile-upgrade、ETag/CAS、Turn freeze、真实 PostgreSQL 并发 | 交付 `AC-3` 与 D-1 | -| `S3` Run 与刷新恢复 | `runtime_operations`、turn/dispatch intent、崩溃恢复;复用持久 ADK events + inbox terminal,未知 cursor 返回 `410 cursor_expired` | 交付 `AC-4`、`AC-7` | -| `S4` 多 worker 生命周期 | participant barrier、pause/resume、fake clock 300 秒、lease loss、restart、transactional outbox continuation | 单独交付 `AC-5` | -| `S5` 管理面收口 | MPA detail/version view-model、Runtime update/release/delete/rollback、Debug/Trace、CLI parity、旧 Runtime gate | 交付 `AC-8`~`AC-10` | - -M0、S1 至 S5 严格串行通过各自门禁,但允许拆成独立合入和回滚单元;不把 XL 范围压成单个 PR。 - -## 8. 测试与验收 - -| ID | 必须通过的证据 | -| --- | --- | -| `AC-1` | 阻断 ArkClaw 端点后,从 Studio AgentDraft 创建 MPA Agent,Runtime 达到 execution-ready,完成首轮 A2A/worker/结果链路,且不调用 Managed Agent API | -| `AC-2` | MPA Profile revision N+1 应用成功;重复 apply 返回同结果;stale ETag、相同 revision 不同摘要、unsupported 字段均安全失败 | -| `AC-3` | Session 配置双客户端 CAS:一方成功、一方 `412`;活动 Turn 配置不变,下一 Turn 使用新版本 | -| `AC-4` | 相同 run key 不重复执行;写记录后 dispatch 前故障可恢复;同 key 不同 payload 返回冲突 | -| `AC-5` | 主执行器加两个 worker 完整/部分暂停、恢复、旧 ACK、租约丢失、重启、300 秒前后和重复 continuation | -| `AC-6` | 伪造身份、跨 owner、撤权后的 run/resume/Secret 使用在副作用前失败;普通输出无 Secret | -| `AC-7` | SSE 断线按 cursor 重放,无重复文本、终态、用量或产物;缺失终态不显示成功 | -| `AC-8` | MPA 创建、详情/版本、对话、配置、历史、Debug 页面覆盖 loading/empty/error/denied/stale/retry、窄屏、键盘/IME | -| `AC-9` | Runtime update/release/delete 使用真实平台 API;MPA update/release/rollback 写操作在副作用前执行兼容性 preflight;失败返回可恢复 ID;兼容回退后 execution-ready 和 smoke 通过 | -| `AC-10` | UI/CLI 使用同一 principal、版本和错误语义;旧 Runtime 明确 read-only/unsupported,不做不安全 fallback | -| `AC-11` | contract tests 验证 REST bearer/TIP 映射到同一 RuntimePrincipal;发布前在部署环境验证 issuer/audience/JWKS/撤权及一次授权 Profile apply/status | -| `AC-12` | PostgreSQL 双连接验证 Profile CAS、Session CAS、唯一活动 Turn、operation replay、outbox crash recovery、schema 升级与允许的双版本组合 | - -文档阶段执行双语、链接、路径、标识符和 diff 检查。实现阶段先跑 agentkit-mpa-agent 定向测试,再跑 VeADK backend/frontend 测试、build、浏览器测试和授权的隔离云 E2E。所有结果按 `pass/fail/blocked/not_run/not_applicable` 记录;跳过不算通过。 - -### 8.1 可执行验证基座 - -- 故障注入:dispatcher 接口、可注入 clock、participant probe、在 operation commit/dispatch/terminal 各边界的 failpoint,测试不得依赖随机 sleep。 -- PostgreSQL:M0 在 Runtime 仓新增 `docker-compose.test.yml`(固定 PostgreSQL 16)和 `make test-postgres`,执行 `tests/integration/test_p0_postgres_contract.py`。命令启动隔离容器、加载旧 schema fixture、使用两个独立连接和一次 Runtime 进程重启验证 CAS/锁/outbox/migration,最后删除容器与 volume;内存 store 不能证明 `AC-3/4/5/9/12`。 -- 浏览器:使用本机已安装的 gstack/Chromium;先以 `VEADK_MPA_TEST_SCENARIOS=1` 启动隔离 fake BFF/Runtime,再执行 `browser-cases.md`。该开关只允许 `APP_ENV=test` 和 loopback bind,否则启动失败;scenario API 支持 seed/reset/barrier/call-count,生产路由中不存在。原始证据写入被忽略的本地 `evidence/browser///`;浏览器不可用时 `AC-8=blocked`。 -- execution-ready smoke:`POST /api/v1/readiness/execution-smoke` 不依赖模型选择工具。Runtime 先执行最小模型 prompt(禁止工具)并校验模型终态,再通过现有 Worker API 由 `CodexWorkerClient.create_session/start_turn/get_turn` 发起隔离 Worker Turn `printf mpa-smoke-`,并用 `follow=false` 回放最终事件;幂等键由 creation operation ID 派生,最多重试 1 次,总超时 120 秒。Worker `/v1/codex-worker/ready` 必须返回 `ready=true`;Worker Turn 必须到达终态并返回可读 replay events。marker 匹配仅作为诊断字段 `markerObserved`,因为 worker 可能汇总命令输出。失败按阶段分类,例如 `smoke_model_failed`、`smoke_worker_not_ready`、`smoke_worker_turn_wait_failed`、`smoke_worker_events_failed`,并在安全可用时附带 `sandboxStage`、HTTP status、worker phase、tool API action 和 provider code。Session/workspace 清理成功后才进入 `runnable`;清理失败为 `failed_retryable`,失败证据最多保留 24 小时。 -- Live E2E 环境记录:测试账号、provider/region/project、资源命名前缀 `mpa-p0-e2e-*`、镜像 digest、模型、PostgreSQL、Runtime 配额、写/删权限、120 秒单步超时、成本上限和 cleanup owner。无明确授权/配额时 live Case 标记 `blocked`,不得由 mock 替代。 -- 跨仓 manifest:固定 VeADK SHA、runtime SHA/image digest、MPA Profile schema version、用于复用平台服务的 `agentkit-sdk-python==0.8.5`、worker protocol/capability version。 - -兼容矩阵在 M0 将 ``、`` 固定为实际产物;未列组合默认 unsupported: - -| BFF | Runtime/schema | Runtime A2A capability | Codex Worker protocol | 允许操作 / 预期 | -| --- | --- | --- | --- | --- | -| `34658d75`(旧) | `2f5e039` / schema 0(旧) | 无 execution v1 | `codex` query + 当前 REST endpoints | 既有 list/read/chat/control | -| `` | `2f5e039` / schema 0 | 无 execution v1 | 当前基线 | list/read;新写返回 `runtime_capability_unsupported` | -| `34658d75`(旧) | `` / schema 1 | `urn:veadk:mpa:execution:v1` | 当前基线 | 既有 list/read/chat/control;不创建 override | -| `` | `` / schema 1 | `urn:veadk:mpa:execution:v1` | `codex` query、`/health`、`/ready`、session/turn/events endpoints | 全部 P0,执行 `AC-1` 至 `AC-12` | -| 任意 | schema 1 | 缺失/不匹配 | 任意 | 禁止新 Session override/continue,返回 `runtime_capability_unsupported` | -| 任意 | schema 1 | execution v1 | `/ready` 非 ready 或缺少当前 REST endpoints | 禁止 worker dispatch,返回 `409 worker_protocol_incompatible` | -| 回滚候选 | schema 1 | 按目标镜像实际值 | 当前基线 | 仅 migration 验证的 read/chat;未通过 PG contract 则拒绝回滚 | - -当前 Worker 没有独立 capability discovery/version endpoint;M0 的静态兼容基线就是上述 `codex` query、`/health`、`/ready`、session/create、turn/start、event stream 接口集合。若任一接口契约变化,必须先增加 Worker capability/version 协商并单独评审;本 P0 不修改 Worker。 - -共享兼容性 preflight 实现在 `veadk.cli.mpa_p0_contract`。shell runner `scripts/verify-mpa-p0-contract.sh` 调用同一代码路径,Studio 在 MPA Runtime 写操作前也调用它。接受的 manifest 是脱敏 JSON 对象,包含 `veadkRevision`、`runtimeRevision`、`runtimeImageDigest`、Profile/config schema version、AgentKit SDK version、Runtime execution capability、Worker protocol 和必需 Worker endpoints。检查失败时不会把 manifest 正文回显给浏览器。若传入 manifest 格式非法,或未命中 `frontend/server/mpa/contracts/mpa-p0/compatibility-matrix.json`,Studio 返回 `409`,`code=mpa_compatibility_preflight_failed`,且不会调用 AgentKit SDK 或 GitHub mutation helper。 - -本地 Studio 写路径防护中,如果 UI 标记目标为 `agentCategory=mpa` 但未显式提交 manifest,BFF 会使用当前 VeADK git revision,以及可用时的 `VEADK_MPA_RUNTIME_REVISION`、`VEADK_MPA_RUNTIME_IMAGE_DIGEST` 生成最小 P0 manifest;缺失的 runtime artifact 值使用语法合法的全 0 占位。这个默认 manifest 只证明当前操作仍处于已批准的 P0 协议形态内,不作为发布证据。最终 release gate 仍必须由 `S5-10/VC-19` 使用真实固定的 VeADK SHA、Runtime SHA、Runtime image digest 和协议版本执行。 - -Live runner 固定为 `scripts/verify-mpa-p0-e2e.sh --manifest `(M0 新增)。脚本按 `AC-11 -> AC-1 -> AC-2 -> AC-6 -> AC-9` 执行,每步写脱敏 JSON/日志到被忽略的本地 `evidence/live//`,状态文件允许从最后一个安全阶段继续;EXIT trap 按 operation Metadata/资源前缀列出并删除 Agent、Runtime、Tool、测试 Session/数据库 schema,最终再次 list 验证零残留。stubbed cross-repo contract 和 live cloud 结果分开记录。 - -Runtime 仓库的可复现门禁为 `make test` 和 `make coverage`(95%);该仓当前没有可引用的 pre-commit/Ruff/Pyright 或仓内 CI 配置,因此不得把这些检查写成已通过或强行套用 VeADK 命令。VeADK 仍执行仓库 AGENTS.md 规定的定向 pytest、并行回归、frontend test/build、浏览器和 pre-commit 门禁。跨仓集成结果必须固定双方 revision/image digest,单仓 mock 通过不等于 E2E。 - -## 9. Review 结论与已确认产品决策 - -本轮 `review-spec` 的阻塞问题——重复建设模板/专家/version、数据权威不清、接口缺少参数/幂等、会话“资源快照”语义模糊、前端自建续跑——已在方案中整改。代码或只读实测能确认的内容均已转为结论,不再列为待确认。 - -以下两项业务语义已由用户于 2026-09-15 确认为方案约束: - -### D-1:已有 Session 显式升级后采用新 Profile - -- **已确认采用 A:已有 Session 固定创建时的 MPA Profile revision;用户显式执行“升级会话”后,新 revision 从下一 Turn 生效。** -- Studio 必须展示“有新版本”、当前/目标版本和升级动作;升级前不改变现有 Session 行为。 -- 活动 Turn 永不切换版本;升级操作使用 CAS 与幂等键,失败保留原版本。 -- 不采用自动跟随最新版本或按 Agent 配置跟随策略,避免同一 Session 静默变更模型、工具或指令。 - -### D-2:完整运行链路通过后才显示 Agent 可运行 - -- **已确认采用 A:Studio 接受 MPA 创建后立即进入“部署中”;仅当 Runtime、Profile 应用和 execution-ready smoke 全部通过后才显示“可运行”。** -- 创建接口/页面返回阶段化 operation,允许刷新后继续查询;失败页面展示安全的资源 ID、失败阶段和重试入口。 -- MPA operation 已存在但 Runtime 尚未可执行时,不显示“创建成功”或允许发起对话。 -- 不采用同步长等待或“Agent 已创建即成功”,避免超时不可恢复及用户进入不可执行 Agent。 - -## 10. 风险与回滚 - -- M0 必须通过 bearer/TIP claim 映射与拒绝路径的本地 contract;部署态 `S5-14` 发布门禁再验证 issuer、audience/client、JWKS 轮换、撤权和授权 Profile apply/status。该门禁失败时阻止发布,但不回溯否定本地 S1 TDD。BFF 原样转发已验证 bearer,不签发新 assertion;禁止信任浏览器 owner Header、Runtime key 或回退旧服务。 -- Studio operation 持久化与 Runtime Profile apply 不是分布式事务。用 operation + 幂等 Profile apply 收敛;失败保留上一个已应用 Profile revision 并显示“未应用”,不得谎报可运行。 -- 会话执行配置引用的远程资源可能在提交后失效。Turn dispatch/materialize 时复核版本与权限,失效则在副作用前失败。 -- Runtime 数据库采用 additive migration;回滚前检查新旧 schema/worker protocol 兼容。不兼容降级在写入前拒绝。 -- 删除只处理当前 MPA Runtime/Profile 数据,并在有活动 Session/共享资源时阻止或分阶段清理;绝不触碰 ArkClaw 历史数据或 Managed Agent 数据。 - -## 11. 文档阶段验证记录 - -| 日期 | 检查 | 结果 | 说明 | -| --- | --- | --- | --- | -| 2026-09-15 | Managed Agent API 探测 | not_applicable | 已确认它是独立产品面;修订后的 P0 架构明确排除 | -| 2026-09-15 | Runtime/Skill/Environment/Vault provider 只读查询 | pass | 确认平台资源与 Runtime 可访问;不等于写链路已验证 | -| 2026-09-15 | 三仓静态接口/数据模型审计 | pass | 确认复用点与必要扩展;不等于运行时行为通过 | -| 2026-09-15 | M0 targeted tests 与 PostgreSQL 16 contract lane | pass | principal、test seam、兼容性、scenario、runner、benchmark 及 4 个真实 PostgreSQL foundation case 均通过;已删除出范围的 Managed Agent adapter | -| 2026-09-15 | Managed Agent APIs | not_applicable | 产品决策排除 Managed Agent CRUD/version/Session;此前的 `ManagedAgentNotOpen` 不再阻塞交付 | -| 2026-09-15 | Live OAuth bearer、TIP、JWKS 与 Profile endpoint | blocked | 新 Profile endpoint 部署后作为 release gate 执行;当前环境没有隔离 live bearer/TIP/JWKS fixture,不能把 AK/SK 只读验证替代为发布门禁通过 | - -## 12. S1/S2/S3/S4/S5 实现阶段验证记录 - -| 日期 | 检查 | 结果 | 说明 | -| --- | --- | --- | --- | -| 2026-09-15 | Runtime S1 定向回归 | pass | `uv run pytest -q tests/test_agentkit_session_auth.py tests/test_runtime_principal.py tests/test_auth.py tests/test_sessions.py::test_create_get_delete tests/test_sessions.py::test_run_response_includes_session_id tests/test_execution_smoke.py tests/test_execution_smoke_api.py tests/test_codex_worker_client.py::test_codex_worker_client_ready_uses_worker_ready_endpoint tests/test_codex_worker_client.py::test_codex_worker_client_cancels_session_with_worker_control_api tests/test_mpa_meta.py tests/test_health.py tests/test_profile_apply_api.py` 返回 83 passed | -| 2026-09-15 | Runtime 定向 Ruff 与 PostgreSQL 16 门禁 | pass | touched Runtime 文件的 focused Ruff 通过;`make test-postgres` 返回 4 passed,并清理容器、网络和 volume | -| 2026-09-15 | VeADK MPA BFF 回归与 Ruff | pass | `uv run --extra dev pytest -q tests/frontend/server/mpa/test_agent_operations_service.py tests/frontend/server/mpa/test_operations_repository.py tests/frontend/server/mpa/test_runtime_profile_routes.py tests/frontend/server/mpa/test_runtime_profile_client.py tests/frontend/server/mpa/test_mpa_module.py tests/frontend/server/mpa/test_scenario_harness.py` 返回 32 passed;focused Ruff 通过 | -| 2026-09-15 | Studio 前端回归、i18n 与 build | pass | `npm --prefix frontend test` 返回 1206 passed;`npm --prefix frontend run check:i18n` 通过;`npm --prefix frontend run build` 通过。构建产生的 `veadk/webui` 产物已从源码 diff 清理 | -| 2026-09-15 | 仓库 diff hygiene | pass | `veadk-python-mpa-p0-plan` 与 `agentkit-mpa-agent` 两仓 `git diff --check` 均通过 | -| 2026-09-15 | S1 浏览器/live 门禁 | not_run | S1-10 仍负责 BC-01/02、BC-07 create/IME 子集和 live `VC-20A`;本地单测/build 结果不能替代这些门禁 | -| 2026-09-15 | S1-10 本地 runner 子集 | pass | compatibility manifest/e2e-runner/benchmark/scenario harness pytest 返回 12 passed。`scripts/verify-mpa-p0-contract.sh` 接受脱敏 manifest。`scripts/verify-mpa-p0-e2e.sh --dry-run` 返回有序阶段 `AC-11, AC-1, AC-2, AC-6, AC-9`;未设置 `VEADK_MPA_P0_LIVE=1` 时 `--execute` 按设计返回 `live_execution_not_authorized` | -| 2026-09-15 | S1-10 浏览器 fixture API smoke | partial | loopback fixture 在端口 `18173` 启动;`/__test/mpa/scenarios` list、seed、call count、barrier release、state read 均可用。强制清理后 token 文件已删除。S1 的 BC-01/02/BC-07 create/IME UI 路径仍需补专项 test-only scenario 接线后才能标记 pass;S2 的 `session_revision_6` 路径已完成接线并由 S2-08 单独覆盖。 | -| 2026-09-15 | 浏览器 runbook 漂移检查 | pass | `browser-cases.md` 与 `.zh.md` 已对齐当前实现的 `VEADK_MPA_SCENARIO_TOKEN_FILE` 和 `/__test/mpa/scenarios/...` API,不再使用早期 `/web/test/mpa/...` 草案路由 | -| 2026-09-15 | S2-01 Session metadata 指针 | pass | Runtime `session_meta` 已保存 `mpa_instance_id`、`profile_revision`、`execution_config_revision`;Session create/detail 仅在 MPA Session 中返回这些字段。`0003_session_meta_mpa_pointers.sql` 将 schema migration 推进到 version 3。focused tests 返回 17 passed、2 skipped;focused Ruff、`make test-postgres` 和 `git diff --check` 均通过 | -| 2026-09-15 | S2-02 execution-config revision store | pass | Runtime 已新增 `app/schemas/session_execution_config.py`、`app/stores/session_execution_config.py` 和 `0004_session_execution_configs.sql`。store 创建 revision 1,仅在 `If-Match` 等于当前强 ETag 时追加 revision N+1;缺少前置条件返回 `ExecutionConfigPreconditionRequired`,stale ETag 返回带安全 current record 的 `ExecutionConfigConflict`,旧 revision 保持不可变。PostgreSQL CAS 使用 Session 维度 advisory transaction lock 串行化。验证:`uv run pytest -q tests/test_session_execution_config.py` 返回 8 passed;focused Ruff 通过;`make test-postgres` 返回 5 passed,包含真实双连接 Session CAS。 | -| 2026-09-15 | S2-03 execution-config API/service | pass | Runtime 已暴露 `GET/PATCH /api/v1/sessions/{sessionId}/execution-config`,返回强 ETag header 和结构化错误体。GET 只为带 `mpaInstanceId + profileRevision` 的 MPA Session 初始化 revision 1;PATCH 要求 `If-Match`,支持 `model` 与 `mcpServers` 的 replace/clear/inherit,在 append 前拒绝未知 MCP ID,stale ETag 映射为 `412 execution_config_changed`,缺失前置条件映射为 `428 precondition_required`。验证:`uv run pytest -q tests/test_session_execution_config.py tests/test_session_execution_config_service.py tests/test_agentkit_session_auth.py tests/test_sessions.py::test_create_get_include_mpa_profile_pointers_when_supplied tests/test_session_mcp_update.py tests/test_session_mcp_resolution.py::test_create_session_persists_and_enriches_appcenter_mcp_ids tests/test_session_mcp_resolution.py::test_create_session_without_ids_uses_agent_mcp_whitelist_when_requested tests/test_session_mcp_resolution.py::test_create_session_with_empty_agent_mcp_whitelist_selects_no_mcp` 返回 24 passed;focused Ruff 通过;`make test-postgres` 返回 5 passed;本次文档更新前两仓 `git diff --check` 均通过。 | -| 2026-09-15 | S2-04 immutable resolved execution-config snapshot | pass | Runtime 已定义 `SessionExecutionConfigSnapshot` 和 `resolve_session_execution_config_snapshot(...)`。snapshot 固定已接受的 execution-config revision/ETag、MPA instance、Profile revision、Profile 默认 revision、overrides、effective refs、invalid refs 与 accepted timestamp,供后续 Turn acceptance 使用。本任务刻意不写入 `turn_execution_records`,`/run` 也要等 S3 才消费该 snapshot。验证:`uv run pytest -q tests/test_session_execution_config.py tests/test_session_execution_config_service.py` 返回 15 passed;focused Ruff 通过。 | -| 2026-09-15 | S2-05 explicit Session Profile upgrade | pass | Runtime 已新增 `POST /api/v1/sessions/{sessionId}/profile-upgrade` 和服务逻辑,显式升级到更高且已 applied 的 MPA Profile revision。流程要求 `Idempotency-Key` 与 execution-config `If-Match`,保留显式 Session overrides,基于目标 Profile 重新计算 inherit 默认值,成功后更新 Session Profile/config 指针;同版本/降级返回 `409 downgrade_not_allowed`,目标缺失或未 applied 返回 `409 profile_revision_unavailable`,相同 key 不同目标返回 `409 idempotency_mismatch`。schema version 8 的 SQL `runtime_operations` ledger 现已支持相同 key/相同目标跨 Runtime 进程持久 replay。最新验证见下方 2026-09-17 Runtime operation/CAS 记录。 | -| 2026-09-15 | S2-06 typed execution-config BFF proxy and client | pass | VeADK 已暴露 `GET/PATCH /web/mpa/sessions/{sessionId}/execution-config` 和 `POST /web/mpa/sessions/{sessionId}/profile-upgrade`,由服务端解析 Runtime endpoint、转发已验证 bearer token,并且配置写入不走浏览器 `/web/runtime-proxy`。TypeScript client 暴露 `getMpaSessionExecutionConfig`、`patchMpaSessionExecutionConfig`、`upgradeMpaSessionProfile` 和 `getMpaProfileStatus`,覆盖 `If-Match`、`Idempotency-Key`、可取消读取,以及用于 `412` 恢复的 `MpaExecutionConfigRequestError.currentState`。最终验证:BFF pytest 返回 21 passed;focused Ruff 通过;前端 source-contract 子集返回 95 passed;完整前端测试返回 1209 passed;i18n 和 build 通过。 | -| 2026-09-15 | S2-07 MPA Session configuration and explicit upgrade UI | pass | Studio 已在 My Agents、新对话选择器、持久 runtime connection 与 Agent 详情中保留 Runtime `agentCategory`。MPA tab 可用现有 AgentKit 创建面,并显式携带 MPA intent。Agent 详情只对 MPA Runtime 显示 `Session configuration`,且只在所选 Agent 与当前 Runtime、当前业务 Session 匹配时读取 Runtime 权威配置。面板展示 config revision/ETag、Session 固定的 Profile revision、最新已应用 Profile revision、effective model/MCP refs,支持模型 replace/inherit、MCP clear/inherit,并通过确定性幂等键执行显式 Profile upgrade。`412 execution_config_changed` 会用 Runtime `currentState` 刷新 UI,并提示用户检查后重试。最终验证:前端 source-contract 子集返回 95 passed,完整 `npm --prefix frontend test` 返回 1209 passed,`npm --prefix frontend run check:i18n` 通过,`npm --prefix frontend run build` 通过,Runtime S2 focused tests 返回 46 passed,Runtime `make test-postgres` 返回 5 passed;生成的 `veadk/webui` 产物已清理。 | -| 2026-09-15 | S2-08 full S2 gate | pass | 当前 diff 的 S2 unit、BFF、frontend、browser 与 PostgreSQL 门禁已关闭。VeADK BFF pytest 返回 28 passed;focused Ruff 通过;前端 source-contract 子集返回 95 passed;完整 `npm --prefix frontend test` 返回 1209 passed;`npm --prefix frontend run check:i18n` 与 `npm --prefix frontend run build` 通过。Runtime S2 focused tests 返回 46 passed;Runtime `make test-postgres` 返回 5 passed。真实浏览器 BC-04/05 使用 system Google Chrome via Playwright 访问 `http://127.0.0.1:18174` loopback fixture:两个浏览器上下文打开同一个 MPA Session,初始 ETag 为 `"6"`;浏览器 A 保存模型覆盖 `model-browser-a` 后 config revision 前进到 7;浏览器 B 用 stale ETag 清空 MCP 时收到预期 `412 execution_config_changed` 与 current state;显式 Session Profile upgrade 随后将 profile revision 升到 7、config revision 升到 8,同时保留显式模型覆盖并继承 `mcp-upgraded`。原始证据见被忽略的本地 `evidence/browser/mpa-s2-1789491400/summary.json`、`BC-04/context-a-before.png`、`BC-04/context-a-after.png`、`BC-04/context-b-before.png`、`BC-04/context-b-conflict.png`、`BC-04/network-a.json`、`BC-04/network-b.json`、`BC-04/call-counts.json`、`BC-05/before.png`、`BC-05/upgrade-after.png` 和 `BC-05/revision-transcript.json`。预期浏览器噪声仅包含 local mode `/oauth2/userinfo` 404 探测和 BC-04 刻意触发的 412;没有未预期 HTTP 或 console 错误。`uv run --extra dev pyright ...` 当前为 `blocked`,因为本地环境没有 `pyright` 可执行文件或项目脚本。验证过程中发现资源卡整卡片详情 overlay 会截获对话按钮点击,已通过提升 action 按钮点击层级修复。 | -| 2026-09-16 | S3-01 `turn_execution_records` store and migration | pass | Runtime 已通过 `turn_execution_records` 持久化已接受的 MPA Turn,字段包含 `turn_id`、`invocation_id`、`operation_id`、带作用域的幂等键、规范化 request hash、冻结的 execution-config snapshot、dispatch key/status 和时间戳。SQL 与 in-memory store 都强制相同 key replay、相同 key 不同 payload 冲突、同一 Session 只能有一个 active Turn;PostgreSQL 接受路径在 replay/busy 检查前使用 Session 维度 advisory transaction lock。schema migration `0005_turn_execution_records.sql` 将 contract lane 推进到 schema version 5。验证:`uv run pytest -q tests/test_turn_dispatcher.py tests/test_run_acceptance.py` 返回 14 passed;Runtime S3 扩展回归返回 64 passed;`make test-postgres` 返回 6 passed。 | -| 2026-09-16 | S3-02 shared REST/A2A Turn acceptance and Studio run wiring | pass | Runtime `/api/v1/sessions/{sessionId}/run` 对 MPA Session 强制要求 `Idempotency-Key` 和 `executionConfigVersion`,dispatch 前持久化 S2 resolved snapshot,返回 `turnId`、`operationId`、`executionConfigRevision`;相同 key/相同 payload 重放原始 accepted 响应,stale config 返回 `412 execution_config_changed`,payload drift 返回 `409 idempotency_mismatch`,同一 Session 并发 active Turn 返回 `409 session_busy`。非 MPA run 保持旧响应形态。A2A 声明 `urn:veadk:mpa:execution:v1`;`params.metadata.veadkExecution` 使用同一个接受服务,把 durable invocation ID 绑定到 ADK run,推进 dispatch 状态 `dispatching -> succeeded|failed_retryable`,并将相同 key replay 作为 completed 终态返回且不二次执行模型。Studio 在发送前读取当前 MPA Session execution-config revision,给 `runSSE` 传入 per-run 幂等键,MPA runtime 走 `/run` 后接 `/sse`,BFF A2A 桥接同样转发 metadata。验证:Runtime S3 定向回归返回 64 passed;Runtime focused Ruff 通过;Runtime PostgreSQL gate 返回 6 passed;VeADK MPA server/integration pytest 返回 47 passed;BFF A2A bridge test 返回 3 passed;前端 source-contract 子集返回 14 passed;完整 `npm --prefix frontend test` 返回 1212 passed;`npm --prefix frontend run check:i18n` 与 `npm --prefix frontend run build` 通过;生成的 `veadk/webui` 产物已清理。 | -| 2026-09-16 | S3-03 accepted-turn/inbox recovery | pass | Runtime 现在在 `turn_execution_records` 中持久化 `dispatch_payload`,用于进程重启后重建已接受 Turn。`SessionService.initialize()` 扫描可恢复的 `accepted|dispatching|failed_retryable` Turn 记录,把已终态的 inbox 同步回 Turn 状态,使用原始 invocation/content/source/peer/environment metadata 重建缺失或 error inbox,并调度既有 inbox worker,不新增第二套 dispatcher。Inbox `enqueue_once()` 按 invocation ID 幂等,并可把先前 error row 重置为 queued 以便重试。running inbox lease 过期时,会先把关联 Turn 标为 `failed_retryable`,再通知 channel。验证:Runtime S3 回归返回 72 passed;Runtime focused Ruff 通过;Runtime `make test-postgres` 返回 6 passed。 | -| 2026-09-16 | S3-04/S3-05/S3-06 run recovery 与刷新门禁 | pass | Runtime cursor replay 已对未知非空 cursor 返回 `410 cursor_expired`,并保留 inbox terminal fence。Studio 现在会在刷新后恢复已持久化的 Runtime Agent 和 Session ID;即使当前 Session ID 已匹配但 turns 尚未加载,也会重新拉取该 Session;MPA run 通过 Runtime `/run` 加 `/sse` 执行,并把上一条已完成 assistant turn 的 `lastEventId` 传给 Runtime。验证:Runtime 扩展 S3 回归返回 251 passed;Runtime focused Ruff 通过;Runtime `make test-postgres` 返回 6 passed;VeADK 前端 focused tests 返回 15 passed;scenario harness pytest 返回 8 passed;MPA server/integration pytest 返回 51 passed;完整 `npm --prefix frontend test` 返回 1213 passed;`npm --prefix frontend run check:i18n` 通过;`npm --prefix frontend run build` 通过。真实浏览器 `BC-06` 在 `http://127.0.0.1:18175` 使用 system Google Chrome via Playwright 通过:刷新后的 Session 恢复 `event-before-refresh`,发送请求携带 `lastEventId=event-before-refresh`,Runtime 只返回 `event-after-refresh`,页面上两个事件各出现一次,未知 cursor 返回 `410 cursor_expired`。原始本地证据:被忽略的 `evidence/browser/mpa-s3-1789516481/BC-06/`。最终证据未使用 gstack browser,因为其 Playwright browser cache 缺失;改用 system Chrome 执行。 | -| 2026-09-16 | S4-01 participant schema/store foundation | pass | Runtime 新增 `a2a_turn_participants` schema version 6,并补齐 SQL/in-memory store,支持按 generation 注册参与方、owner-scoped lease heartbeat、带 generation 校验的 safe-point ACK、终态标记和按 generation 有序快照。这只是 S4 数据模型基础;S4-02 仍负责全员 ACK 后的聚合 pause barrier 状态推进。验证:`uv run pytest -q tests/test_turn_participants.py` 返回 5 passed;`uv run pytest -q tests/test_turn_participants.py tests/test_a2a_task_control.py tests/test_turn_control_api.py tests/test_p0_test_seams.py` 返回 37 passed;Runtime focused Ruff 通过;`make test-postgres` 返回 7 passed 并清理 PostgreSQL 资源。 | -| 2026-09-16 | S4-02 participant pause barrier 与 worker 注册竞态处理 | pass | Runtime 现在在存在 participant store 时使用 `ParticipantBarrierService` 处理 pause 请求。A2A 主执行器和 Codex worker 会登记 participant;safe-point ACK 带 generation 校验;部分 ACK 时聚合 task 保持 `pausing`;当前 generation 的所有活跃参与方 ACK 后 task 才进入 `paused`;在 task 已经 paused 后才加入的 late worker 会收到 worker pause,并先 ACK 当前 generation 后再 resume;终态 worker 不会被复制进后续 pause generation。resume 会从上一代 paused generation 生成下一代 running participant 记录。S4-03 仍负责精确 300 秒 lease-aware resume 边界,S4-04 仍负责关联 continuation。验证:`uv run pytest -q tests/test_turn_participants.py tests/test_participant_barrier.py tests/test_turn_control_api.py tests/test_a2a_app_lifecycle.py tests/test_codex_adapter.py::test_adapter_pauses_worker_and_continues_interrupted_turn_on_resume tests/test_codex_adapter.py::test_adapter_marks_completed_worker_participant_terminal_before_next_pause tests/test_codex_adapter.py::test_adapter_late_worker_joins_paused_generation_before_resume` 返回 27 passed;Runtime focused Ruff 通过;`make test-postgres` 返回 7 passed 并清理 PostgreSQL 资源。 | -| 2026-09-16 | S4-03 lease-aware resume 边界 | pass | Runtime 现在只有在聚合 task 为 `paused`、请求 generation 匹配、暂停时长不超过 300 秒、task owner lease 仍有效,且当前 paused generation 没有 `lost|failed` participant 或仍 running 但 lease 已过期/缺失的 participant 时,才允许 same-Turn resume。若仍有 running participant 且 lease 有效,则 resume 返回 `409 invalid_state`,要求等待安全点,而不是静默继续。超时或 lease 丢失的 resume 会把 task 转为 `interrupted`,返回 `resumeDisposition=new_turn_required`,状态/重放响应都携带 continuation prompt,并记录 resume 幂等 key,保证同 key 重试安全 replay。S4-04 仍负责创建关联 continuation Turn。验证:S4-03 focused boundary tests 返回 8 passed;broader S4 control regression 返回 104 passed;Runtime focused Ruff 通过;`make test-postgres` 返回 7 passed 并清理 PostgreSQL 资源。 | -| 2026-09-16 | S4-04 runtime-owned 关联 continuation | pass | Runtime 现在暴露 `POST /api/v1/a2a/tasks/{taskId}/continue`。接口要求 `Idempotency-Key` 和 `expectedGeneration`,只接受已经需要新 Turn 的 task 状态,通过旧 task invocation 找到上一条业务 Turn,并通过 `turn_execution_records.accept_continuation(...)` 写入 continuation。新 Turn 保留 `continuationOf=`,复用冻结的 execution-config snapshot 和 dispatch payload,关闭旧 active dispatch 行,并通过 `enqueue_once` 进入现有 inbox/outbox worker。相同 key/相同请求返回同一 continuation;相同 key 不同请求返回冲突。Studio 去掉 prompt fallback 以及 UI/BFF 接线仍属 S4-05。验证:S4-04 focused tests 返回 4 passed;broader S4 control/acceptance regression 返回 125 passed;Runtime focused Ruff 通过;`make test-postgres` 返回 8 passed 并清理 PostgreSQL 资源;两仓 `git diff --check` 通过。 | -| 2026-09-16 | S4-05 Studio Runtime continue 接线 | pass | Studio 现在在 resume 返回 `new_turn_required` 时通过 `continueTurnSSE(...)` 调用 Runtime `/api/v1/a2a/tasks/{taskId}/continue`,再用 continuation 返回的 `invocationId` 和当前 `lastEventId` 订阅 Runtime `/api/v1/sessions/{sessionId}/sse`。不再构造 `continuationPrompt + Original task` 文案,也不再发普通 `/run`;continuation streaming 开始时会清理旧 control state。验证:focused frontend source-contract tests 返回 37 passed;`npm --prefix frontend run check:i18n` 通过;`npm --prefix frontend run build` 通过,生成的 `veadk/webui` 产物已清理。 | -| 2026-09-16 | S4-06 RuntimePrincipal 撤权与副作用前复核 | pass | Runtime 新增共享 `runtime_authorization` helper 和可注入 `runtime_principal_authorizer`。REST `/run`、resume、`/continue` 会在接受副作用前复核当前授权。accepted Turn 的 dispatch payload 和 inbox metadata 携带规范化非 secret RuntimePrincipal 快照,使后台执行和重启恢复无需保存 bearer token 也能复核。ADK `before_tool_callback` 会在工具调用前复核;sandbox common 会在直接 sandbox dispatch 前复核;模型 endpoint 解析、workload TIP、OpenViking API key 解析和 sandbox `begin_run` 会在使用 secret material 前复核。fake deny authorizer 已证明拒绝路径不会产生 resume/continue/tool/secret 副作用。验证:S4-06 focused auth/secret/tool tests 返回 46 passed;broader S4 Runtime regression 返回 206 passed;Runtime focused Ruff 通过。外部 AgentKit 撤权查询仍属于部署态 S5 release gate。 | -| 2026-09-16 | S4-07 生命周期、PostgreSQL 与浏览器门禁 | pass | Runtime focused auth/secret/tool tests 返回 46 passed,`make test-postgres` 返回 8 passed 且 PostgreSQL 资源已清理。VeADK `uv run --extra dev pytest -q tests/frontend/server/mpa/test_scenario_harness.py` 返回 9 passed;前端 source-contract 子集与 i18n 返回 37 passed 且 i18n 通过;完整 `npm --prefix frontend test` 返回 1215 passed;`npm --prefix frontend run build` 通过,生成的 `veadk/webui` 产物已清理。真实浏览器 `BC-09` 使用 system Google Chrome via Playwright,在 `http://127.0.0.1:5174` 前端和 `http://127.0.0.1:8001` 后端上通过。原始证据已写入被忽略的本地 `evidence/browser/mpa-s4-1789532050/BC-09/`:`running.png`、`partial.png`、`paused.png`、`resumed.png`、`continue.png`、`summary.json`、`network.json`、`call-counts.json`、`state.json` 与 console 日志。summary 全部关键断言为 true:部分 ACK 保持 `pausing`;观察到 `new_turn_required`;点击前和刷新后都没有自动 `/continue`;显式 continuation replay 幂等;无未预期 HTTP 或 console 错误。预期本地噪声为四次 `/oauth2/userinfo` 404 探测。 | -| 2026-09-16 | S5-01 Secret reference migration 基础 | pass | Runtime schema version 7 向 `mpa_agents` 增加 `agent_api_key_ref`、`model_api_key_ref`、`secret_migration_status` 和 `secret_migration_operation_id`。`AgentConfigService` 在 AgentKit mode 下拒绝新的明文 `agentApiKey`/`modelApiKey` 写入,但允许 reference 字段。`SecretReferenceMigrationService` 先创建 reference 再清空明文,provider/DB 失败时保留明文,并且使用同一幂等键重试不会重复创建 provider reference。Runtime 模型与内置 MCP 路径只在执行时 Secret 使用边界解析 reference,缺少 resolver 时 fail closed。验证:S5-01 focused pytest 返回 95 passed;focused Ruff 通过;`make test-postgres` 返回 9 passed 且 PostgreSQL 资源已清理。 | -| 2026-09-16 | S5-01a canary-safe value-aware redaction | pass | Runtime 现在会在 Secret reference migration 中注册 legacy 明文 Secret 字面值,并在 API access log、Session 可见文本/events、Runtime Console 响应、lifecycle/trace detail bounding、SQL trace-step 持久化及 Runtime operation 的 response/resource snapshot 中执行 value-aware redaction。该策略可以在非敏感 key 或自由文本中遮盖已注册 canary 值,同时保留 `flow_id`、`user_code`、模型 `max_tokens` 等非 Secret 业务字段;Secret reference 字段允许保存。Runtime operation 状态不暴露 principal 或幂等内部字段。最新验证见下方 2026-09-17 Runtime operation/CAS 记录。 | -| 2026-09-16 | S5-02 unique Runtime binding 与 `MpaAgentView` BFF | pass | VeADK 现在暴露 `GET /web/mpa/agents/{mpaInstanceId}/view`。BFF 会解析当前用户可见的 MPA Runtime binding:0 个返回 `runtime_missing`;多个返回 `binding_ambiguous`,带候选 binding 且关闭写入/debug;唯一 binding 时聚合 Runtime `profile-status`、Runtime/version/readiness metadata、capabilities 和匹配的 active operation。主 app resolver 会探测配置的 Runtime regions,只保留 `veadk:agent-type=mpa` Runtime,合并 tag service metadata,并复用现有 Runtime connection resolver;不调用 Managed Agent API。S5-03 仍负责前端详情页 UI。验证:focused MPA BFF route/main-app tests 返回 32 passed;focused Ruff 通过。 | -| 2026-09-16 | S5-03 MPA detail view-model consumption | pass | Studio 现在在 MPA Runtime-backed 详情页消费 `MpaAgentView`。TypeScript client 暴露 `MpaAgentView` 和 `getMpaAgentView(...)`;`AgentWorkspace` 会在 MPA 详情页加载该 view,展示 MPA 控制面状态提示,并且只有 `bindingStatus=bound` 时才加载 Session 配置。`runtime_missing` 和 `binding_ambiguous` 会阻塞写入;`orphan_runtime` 在 Profile 应用前阻塞 Session 配置和诊断。非 MPA 详情继续使用现有 Runtime detail/version/update 路径。Runtime delete 已由后续 S5-07 切片完成,更完整 Debug/Trace UI 已由 S5-05 完成。验证:focused frontend source-contract tests 返回 33 passed;完整 frontend tests 返回 1216 passed;i18n 与 build 通过;focused MPA BFF/main-app tests 返回 32 passed;focused Python Ruff 通过。 | -| 2026-09-16 | S5-04 Runtime Console/Trace structured correlation | pass | Runtime Console trace 响应现在包含顶层 `correlation` 和每个 step 的 `correlation`。字段从现有已脱敏 trace details 中抽取并规范化,覆盖 MPA instance、Profile revision、execution-config revision/ETag、业务 Turn、A2A task、Runtime ID/version,以及 Codex/subagent worker session/turn/profile 标识。Studio 后续可读取稳定字段,不需要解析任意 `details` JSON。S5-05 仍负责 Studio Debug/Trace/history/usage 视图。验证:Runtime Console/Trace pytest 返回 72 passed;focused Ruff 通过。 | -| 2026-09-16 | S5-05 Studio MPA diagnostics view | pass | Studio 现在在 `AgentWorkspace` 中提供 MPA-only Diagnostics section。它通过现有 Runtime proxy 读取 Runtime Console admin run history 和最新 invocation trace,展示 S5-04 的结构化 correlation 字段,列出有界 trace steps,并继续复用现有 Usage tab 承载 usage 数据。当 MPA binding 缺失、重复、orphan,或不是当前对话 Runtime,或没有当前 Session 时,Diagnostics 会明确阻塞。验证:focused frontend source-contract tests 返回 34 passed;完整 frontend tests 返回 1216 passed;i18n 与 build 通过;focused MPA BFF/main-app tests 返回 32 passed;focused Python Ruff 通过。 | -| 2026-09-16 | S5-06 Runtime update/release/rollback 兼容性 preflight | pass | VeADK 现在提供共享的进程内 MPA P0 兼容性评估器,`scripts/verify-mpa-p0-contract.sh` 和 Studio 写接口使用同一套规则。MPA 直连 Runtime update(`/web/deploy-agentkit`)、GitHub Actions delivery setup(`/web/github-delivery/cicd-pipeline`)、首次 delivery 分支初始化(`/web/github-delivery/init-main`)、已绑定源码同步(`/web/github-cicd/runtime-sync`)和 rollback PR 创建(`/web/github-delivery/rollback-pr`)都会在 AgentKit SDK 或 GitHub mutation 前拒绝 malformed/incompatible manifest,返回 `409 mpa_compatibility_preflight_failed`。浏览器在 MPA 发布/更新和回滚路径发送 `agentCategory=mpa`;非 MPA 路径保持原行为。本地检查通过:manifest 加 MPA update/GitHub release/rollback guard 的 focused pytest 返回 12 passed;前端 source-contract tests 返回 41 passed;完整 frontend tests 返回 1217 passed;focused Ruff 通过;`compileall` 通过;`npm --prefix frontend run check:i18n` 通过;`npm --prefix frontend run build` 通过且只有既有 Vite chunk warning。build 生成的 `veadk/webui` 产物已清理。 | -| 2026-09-16 | S5-07 授权删除影响预览与分阶段清理 | pass | VeADK 现在提供 `GET /web/mpa/agents/{mpaInstanceId}/delete-preview` 作为 Studio MPA 删除预览。预览会解析已授权 Runtime binding,读取 Runtime `profile-status`,调用现有 Runtime `GET /api/v1/sessions?include_a2a=true`,返回 active operation、active/idle/debug 可见会话计数、阻断原因和分阶段清理计划。`/web/delete-runtime` 会在执行前重新检查服务端 Runtime 标签 `veadk:agent-type=mpa`,只有确认是 MPA Runtime 才进入 MPA 清理;存在 active operation 或处于 `queued|running|pausing|paused|resuming` 的 active session 时以结构化 `409` 拒绝;无阻断时先通过现有 Runtime `DELETE /api/v1/sessions/{sessionId}` 删除可见 idle/terminal 会话,再删除 AgentKit Runtime。Session 清理时单个 session `404` 视为已清理,以便安全重试。浏览器会在单个 MPA 删除确认前拉取并展示预览,存在阻断时禁用确认按钮。该切片不新增 `agentkit-mpa-agent` 数据库 schema 或 API;当前预览只能统计已授权目标 Runtime API 可见的会话,不能声明跨用户全局 session 状态。Local checks 通过:MPA BFF/client/delete focused pytest 返回 31 passed;完整 frontend tests 返回 1217 passed;前端 source-contract focused tests 返回 33 passed;focused Ruff 通过;`compileall` 通过;`npm --prefix frontend run check:i18n` 通过;`npm --prefix frontend run build` 通过且只有既有 Vite chunk warning。build 生成的 `veadk/webui` 产物已清理。 | -| 2026-09-17 | S5-08 Studio 统一 Profile 管理入口 | pass | MPA Agent 详情页现在提供 Agent 级 `Profile 配置` 分区作为 Studio 管控入口。该分区读取专用 `MpaAgentView`,展示 apply 状态、revision/ETag、模型、系统提示词、Tool/Skill/MCP 摘要,通过类型化 MPA BFF operation 路径应用当前 Studio Profile,并提供 `编辑 Profile` 入口。编辑复用现有 Studio Agent 工作台的 Profile-only 模式:跳过环境/部署步骤,不调用 `/web/deploy-agentkit`,保存时通过 `applyMpaProfileAfterDeployment(...)` 写入 mpa-agent;orphan Runtime 走 create/`If-None-Match: *` 语义,已绑定 Runtime 走 ETag update 语义。MPA 详情不再探测依赖 `/list-apps` 的通用 Runtime update capability,因为它无法准确表示 A2A-only mpa-agent Runtime。orphan Runtime 保持可写以支持首次 Profile apply,但 Session 配置和诊断仍在 binding 变成 `bound` 前阻塞。本地 dev 现在在 Studio TOS 缺失时使用进程内 operation repository,保证 `veadk studio --dev` 可以对真实 Runtime 验证 Profile apply;生产仍要求 TOS 持久化。Runtime 已重新构建并发布镜像 `agentkit-platform-2112682748-cn-beijing.cr.volces.com/agentkit/mpa_agent:mpa-p0-sessioncfg-init-local-20260917` 到 Runtime `r-yeuujrrcowb21078p9jh` version 58,并修复 MPA Session create 自动初始化 execution-config revision 1。真实链路检查通过:BFF Profile update 返回 `202/succeeded/profileRevision=3`,`MpaAgentView` 返回 `bindingStatus=bound` 且 `canWrite=true`、`canDebug=true`,native Session create 返回 `executionConfigRevision=1`,native `/run` 返回 `turnId/operationId/invocationId`,native `/sse` 输出最终文本并发送具名 `event: done`,Studio BFF `run_sse` bridge 也输出最终文本,持久事件只包含一条用户消息与一条带 trace ID 的最终模型消息。使用合法 UUID `contextId` 的命令行直连 A2A JSON-RPC 返回 HTTP 200 且 task state 为 `completed`;打包的 AgentKit CLI `agentkit invoke run --runtime-id/--endpoint --a2a` 对该 A2A-only Runtime 仍返回 404,记录为 AgentKit CLI 兼容缺口,不影响 Studio 统一管控路径。本地检查通过:focused frontend source-contract tests 返回 137 passed;MPA operation/BFF pytest 返回 43 passed;Runtime Session/config/run pytest 返回 186 passed;Runtime focused Ruff 通过;VeADK focused Ruff/compileall/diff-check 通过;`npx tsc --noEmit` 通过;`npm --prefix frontend run check:i18n` 通过;完整 `npm --prefix frontend test` 返回 1227 passed;`npm --prefix frontend run build` 通过且只有既有 Vite chunk warning。浏览器静态加载通过 system Chrome 产出登录 DOM 和截图;交互式浏览器自动化受限于 gstack 的 Playwright browser cache 缺失,未声明通过。 | -| 2026-09-17 | 多轮完成回答持久展示回归 | pass | 之前的 post-SSE 事件栅栏对账可以防止过期持久数据覆盖最新实时回答,但没有覆盖持久多轮历史重放。严格 DOM 检查复现了真实缺陷:两条用户轮次重放为两个用户节点,却只剩一个 assistant 节点。根因是 `eventsToTurns(...)` 在每段历史都用相同 `adk-history` 前缀重建 projector;每个 projector 都生成 `localId=adk-history-0`,后续 assistant 轮次因此在 upsert 时替换更早轮次。Studio 现在为每个历史分段使用单调递增前缀,使 projector 本地序号在整个 Session transcript 内保持唯一。回归测试修复前稳定失败且只得到 `second answer`,修复后同时得到两条回答、两个最终事件 ID 和不同 local ID。focused tests 返回 43 passed;完整前端测试返回 1236 passed;`npx tsc --noEmit`、i18n、生产构建与 scoped diff 检查均通过。随后对真实 Studio Session 严格按顺序检查 `.turn--assistant` 节点:新增两轮实时对话完成并额外等待 30 秒后,五个 assistant turn 均保留;整页重载后仍保留五个且顺序正确。由于提问或 reasoning 也可能包含同样文本,整页文本命中被明确排除为验收证据。 | -| 2026-09-17 | MPA 实时父级沙箱活动终态收敛 | pass | 真实 Studio 执行稳定复现:命令卡片和最终回答均已完成,但父级 `sandbox_task` 卡片仍为 `running`。抓取的 SSE 帧证明,实时订阅先输出回答增量,随后只输出 `status=completed`、`finalAlreadyEmitted=true` 的 `sandbox_task` wrapper response;持久事件列表还包含 `source=sandbox,eventType=invocation.completed`,所以刷新后原本就是正确状态。Studio 不再无条件丢弃 wrapper;当 invocation 仍有 active turn 时,它用 wrapper 关闭父级工具,同时保留已推流的回答/工具 block,并以 wrapper event ID 将 projected turn 标记为非 streaming;持久重放已经由 sandbox final event 关闭 Turn 时仍忽略 wrapper,避免重复卡片。focused regression 在旧逻辑上失败,修复后通过。完整 `npm --prefix frontend test` 返回 1237 passed;`npx tsc --noEmit`、i18n、生产 build 与 scoped diff 检查均通过。新的真实 Studio `whoami` 执行先观察到父/子活动进入 running,随后在不刷新页面的情况下出现最终回答 `root`,父级变为 `data-status=completed`,spinner 消失且 remaining running activity 为 0;整页刷新后,最新两条活动仍为 completed、无 spinner,最终回答仍可见。 | -| 2026-09-17 | Runtime operation ledger 与 Profile CAS 加固 | pass,但存在仓库基线门禁例外 | Runtime schema version 8 新增 SQL `runtime_operations` ledger,具备 24 小时 TTL、规范化 request hash、已脱敏 response snapshot/resource IDs、compare-and-swap 状态迁移,以及按 principal/agent/action 授权的 `GET /api/v1/runtime-operations/{operationId}`。Profile apply 现在以认证 principal 作为幂等主体,对完整规范化 Profile 求 hash,映射 model/Tool/Skill/MCP/Multiagent,并在切换 `mpa_agents` revision 的同一事务内原子校验 `If-None-Match`/`If-Match`。非法强 ETag 返回 `400 invalid_precondition`;模型 `max_tokens` 和 Secret reference 字段允许使用,内联 Secret 字段仍被拒绝。Profile upgrade 可跨 Runtime service/store 实例重放持久结果。验证:Runtime focused tests 39 passed;PostgreSQL migration/restart/cross-store replay/Profile-CAS tests 11 passed 且资源已清理;changed-file Ruff 通过;`git diff --check` 通过。最终完整行为回归 2501 passed、14 skipped,但 `make coverage` 记为 fail,因为仓库总覆盖率为 93.47%,低于未修改的 95% 阈值。全仓 Ruff 也因当前 `origin/master` 中 P0 diff 之外文件的 27 个既有问题而 fail;没有降低阈值或放宽规则。 | -| 2026-09-18 | 旧 ADK 接口的 AgentKit key-auth 兼容 | pass | Runtime version 61 虽然处于 `Ready` 且使用 AgentKit `key_auth`,但携带有效凭据访问 `GET /list-apps` 仍返回 `401`,原因是旧 `require_auth` 依赖在 Session、Profile 和 A2A 链路已改用 `Authorization` Runtime principal 后,仍强制要求 `X-Jwt-Token`。Runtime commit `eecc6e3` 仅在 `MPA_AGENTKIT_MODE=true` 时让 `require_auth` 从 `Authorization` 建立 principal;非 AgentKit 部署继续保留旧 Header 契约,缺失凭据仍 fail closed。定向测试 49 passed,完整 `make test` 2506 passed、14 skipped,focused Ruff 和 diff hygiene 均通过。镜像 `agentkit-platform-2112682748-cn-beijing.cr.volces.com/agentkit/mpa_agent:mpa-p0-adk-auth-eecc6e3-20260917`(`sha256:6ac6a2712ecd1c7950125dc9afc6467373a08142fbd6c3577aba12f126079bef`)已发布为 Runtime `r-yeuujrrcowb21078p9jh` version 62,状态为 `Ready`;Runtime 直连与 Studio BFF 的 `/list-apps` 探测均返回 `200 ["default"]`。这是鉴权适配修复,不增加表、字段或持久状态。 | -| 2026-09-18 | Runtime v62 真实 Studio 对话展示闭环 | 已验证的对话展示子集通过 | 新建 Studio Session `bee3db38-edcc-4020-b925-3d9d6a3a7adc`,通过沙箱执行 `printf 'MPA_V62_BROWSER_OK\n'`。父级 `sandbox_task` 活动和子命令活动均进入 `completed`,命令退出码为 0;最终回答在执行完成时可见,30 秒后仍保留,整页刷新后可恢复。输入框可编辑,发送控件恢复为 `aria-label=发送`,页面无 spinner 或运行中文案。原始证据已写入被忽略的本地 `evidence/browser/mpa-v62-live/`。这证明本次真实链路中的“工具一直运行、结果完成后消失”回归已闭环,但不替代 S5-12 负责的完整 `BC-01`–`BC-09` 矩阵。 | -| 2026-09-18 | S5-08 共享 Python 控制面 client | 通过,Pyright 除外(blocked) | 新增公开异步 `veadk.integrations.mpa.MpaControlPlaneClient`,以及共享的 Profile、operation、Agent view/capability、Session execution-config、delete-preview 和安全错误 schema。它只调用 Studio BFF endpoint,并集中处理 bearer 注入、由调用方保持稳定的幂等 key、CAS ETag、响应校验、timeout 分类和脱敏错误;非结构化响应 detail 不会作为错误码透出。Studio Runtime transport 改为导入共享 Profile/execution-config schema。原设计列出了 operation get,但 BFF 只实现了 active-list/retry;现已补充按 owner 授权的 `GET /web/mpa/agent-operations/{operationId}`,使终态 operation 轮询完整。未调用 Managed Agent,未新增 Runtime API、数据库表或 `agentkit-mpa-agent` 改动。定向 client 测试 19 passed;完整 Studio MPA backend 与共享 client 测试 73 passed;focused Ruff、compileall、pre-commit 和 diff check 通过。使用仓库声明的 `dev + extensions + sandbox` extras 执行全仓回归,结果为 4639 passed、8 skipped、2 xfailed。`uv run pyright ...` 因仓库环境没有 `pyright` 可执行文件记为 `blocked`。 | -| 2026-09-18 | S5-09 Studio 控制面 CLI | 通过,Pyright 除外(blocked) | 新增独立的 `veadk mpa control` 命令组,同时保留基础设施用途的 `veadk mpa create`。它仅通过共享 `MpaControlPlaneClient` 和 Studio BFF 提供 view/create/update、operation list/get/retry、Profile status/apply、Session config get/patch/profile-upgrade 和 delete preview。JSON 文件/stdin 输入使用共享 Pydantic schema;Profile Secret 字段在发网前失败;bearer 从不输出;写操作保留显式、由调用方稳定复用的幂等键和 CAS revision/ETag;响应 ETag 保留;脱敏后的控制面失败映射到固定退出码 `1/3/4/5`,本地用法错误继续使用 Click 退出码 `2`。loopback BFF 证明了真实请求路径、Header、body、202 处理、旧 Runtime 只读输出,以及 BFF 已收到请求后的同 key timeout 重放。两轮审查修复了嵌套 help 在未配置 URL 时不可用的问题,并澄清 JSON 与本地 usage-error 文本的边界。验证:focused CLI 22 passed;共享 client 加 MPA CLI 57 passed;全量 CLI 1402 passed、4 skipped;规定的并行 Python 回归 4661 passed、8 skipped、2 xfailed;focused Ruff/format、compileall 和 diff check 通过。Pyright 因环境没有可执行文件记为 `blocked`。无需修改 Runtime API、Runtime 数据库、前端、Managed Agent 或 `agentkit-mpa-agent`。 | -| 2026-09-18 | S5-10 固定跨仓版本的兼容性预检 | pass | 使用 VeADK `486dc06e429573ce43d6462b6dc960be4bc34ab6`、Runtime `eecc6e3115685e5cb86dcfbff4fb7c6ac7a10dee`、AgentKit SDK `0.8.5` 和已部署 v62 镜像 digest `sha256:6ac6a2712ecd1c7950125dc9afc6467373a08142fbd6c3577aba12f126079bef` 执行 VC-19 预检阶段。实时 Runtime detail 查询返回 `Ready`、version 62 和预期 artifact tag;registry 查询独立返回相同 digest。真实 manifest 命中 `p0-codex-rest-v1`。负向执行中,`codex-v2` 以 exit 3 / `worker_protocol_incompatible` 被拒绝,非法镜像 digest 以 exit 2 / `invalid_runtime_image_digest` 被拒绝。manifest 契约测试 4 passed,Studio/GitHub 副作用前阻断测试 8 passed,VC-15 Runtime 脱敏测试 91 passed。脱敏原始证据已写入被忽略的本地 `evidence/contract/s5-10-v62-20260918/`。S5-10 为只读检查,不需要修改代码、Runtime schema、API、前端、Managed Agent 或线上资源。VC-19 的最终全量回归、覆盖率、PostgreSQL、构建和性能阶段仍由 S5-11 之后的 S5-13 执行。 | -| 2026-09-18 | S5-11 真实 Runtime 生命周期、回滚与删除 | pass | VC-21 使用全新隔离 Runtime 执行,镜像为 v29 `agentkit-platform-2112682748-cn-beijing.cr.volces.com/agentkit/mpa_agent_vc21c:mpa-p0-smoke-v29-eecc6e3-20260918`,digest 为 `sha256:078519c796b03b298a95ef7a6614f92121bc124513dbb472a7496c95821ee723`。run `vc21-20260918-045` 完成全部阶段:兼容性栅栏、Runtime 创建、Profile apply、execution-ready smoke、同镜像 no-op update、受控失败和恢复 smoke、兼容 rollback smoke、active operation 删除阻断、active session 删除阻断、idle session 清理、最终 Runtime 删除和 PostgreSQL 清理。runner 返回 `{"case":"VC-21","residue":[],"status":"passed"}`。真实验证中 `vc21-20260918-044` 暴露一个 Studio 删除 bug:Runtime Profile 已 `applied` 且 smoke 已通过时,遗留的 `failed_retryable/smoke_running` operation 仍可能被视为永久 active blocker;Studio 现改为将该已恢复状态视为非阻塞,同时继续保留真正 `active` operation 与 active session 的阻断。修复后的定向 CLI/route/driver 覆盖返回 49 passed,focused Ruff 通过。原始证据已写入被忽略的本地 `evidence/live/vc21-20260918-045/`。 | -| 2026-09-19 | S5-12 全矩阵浏览器阶段执行 | partial;`BC-03/04/05/06/09` pass,`BC-01/02/07/08` blocked | 使用 gstack v1.87.4.0 驱动 system Google Chrome,对 VeADK `cd34b45c` 加当前 working diff 的 loopback fixture 执行真实浏览器验证。`BC-03` 覆盖 `bound`、`runtime_missing`、`binding_ambiguous`、`orphan_runtime` 和旧 Runtime 只读状态。`BC-04` 使用两个隔离 Chromium profile,以相同本地 principal 和 ETag `"6"` 打开同一 Session:浏览器 A 的 model PATCH 返回 `200`/revision 7,浏览器 B 的过期 MCP clear 返回 `412`,权威状态只保留 A 的模型覆盖。`BC-05` 将 Session Profile 从 6 升级到 7,并证明同 key replay 与不同目标的 `409 idempotency_mismatch`。`BC-06` 在发送后和刷新后都只显示一次持久事件与续传事件,未知 cursor 返回 `410 cursor_expired`。`BC-09` 证明 `running -> pausing -> paused -> running`、partial ACK、已本地化的 `new_turn_required`、一次显式 `/continue` 加 `/sse`、刷新后不自动续跑,以及同 key linked Turn replay。执行中修复了 test-only MPA view、Session list/events、cursor 终态持久化和可控生命周期流接线,限制 Vite 依赖发现只扫描产品入口,并移除了分支集成后导致 TypeScript build 失败的 3 处重复 `agentCategory` 声明/字段。验证通过:scenario/Profile route pytest 36 passed、focused frontend tests 82 passed、完整 frontend tests 1240 passed、Ruff pass、i18n pass、frontend production build pass(仅有既有 chunk warning)。`npm ci` 仍为 `fail`,因为 `frontend/package.json` 与 `frontend/package-lock.json` 的 esbuild 0.28 依赖树不一致;本次 build 仅修复本地依赖目录,未修改 lockfile。`BC-01`、`BC-02`、`BC-07` 与 `BC-08` 为 `blocked`:`create_success`、四种创建失败变体和 `slow_scope_switch` 没有隔离的 scenario 写/scope 路由,而真实创建 UI 会调用 `/web/deploy-agentkit`,因此没有执行未经授权的云资源变更。被忽略的本地 `evidence/browser/s5-12-20260919-r1/` 下的原始证据按要求不提交。`S5-12` 与 `VC-17` 仍保持未完成。 | -| 2026-09-20 | S5-12 完整浏览器矩阵 | pass;`BC-01`–`BC-09` 全部通过 | loopback-only harness 已隔离 Runtime 名称校验、生成项目/草稿、部署 SSE、MPA operation create/list/get/retry、失败阶段、部署/operation barrier,以及 principal/region scoped Runtime discovery,因此不可能触达 AgentKit SDK 或云资源写操作。`BC-01` 通过正常创建,以及从 `profile_applying` 刷新恢复到 `runnable`;`BC-02` 覆盖 `profile_apply_failed`、`smoke_worker_not_ready`、`smoke_output_mismatch`、`cleanup_failed`,每种场景均只有一次 Runtime deploy、一次 operation create 和一次显式 operation-only retry;`BC-07` 证明 composition 中 Enter 调用数为 `0`、compositionend 后为 `1`,且 `1024x768` 下无水平溢出;`BC-08` 在同一 Runtime ID 下延迟 Alice/北京响应后仍保持 Bob/上海为权威页面状态,denied principal 只得到 `404 resource_not_found`。浏览器执行发现并修复:前端缺少 active operation 刷新恢复、快速/传统创建入口的失败 retry 会重跑完整部署、IME 防误提交缺失、恢复成功文案未翻译、retry rejection 未处理,以及 active/failed-retryable operation 期间 `canDebug` 仍为 true。当前 working diff 最终检查:frontend 1243 passed、focused MPA backend 53 passed、TypeScript pass、focused Ruff pass、i18n pass、production build pass(仅既有 chunk warning)、diff whitespace pass。`npm ci` 仍为此前记录的 dependency-lock failure,归 S5-13 处理。被忽略的本地 `evidence/browser/s5-12-20260919-r1/` 下的原始浏览器证据按要求不提交。 | -| 2026-09-20 | 本地 Studio Profile 页错误处理补救 | pass | 用户在 `http://127.0.0.1:5179/` 验证时发现 Profile 页仍有报错。根因拆分为三类:本地后端此前未带 `--dev` 启动,未配置 Studio TOS 时 MPA operation repository 为空,导致 `GET /web/mpa/agent-operations?status=active` 返回 `503 mpa_operation_service_unavailable`;当前选中 Runtime `r-yev5cmzp4wzn6n5iodgd` 为 version 14,仍要求旧 `X-Jwt-Token`,因此原生 Session list 返回上游 `401`;其 Profile status endpoint 也返回普通 `404 Not Found`。Studio 已改用 `--dev` 重启;BFF/前端现在把旧 Runtime auth/profile-status 失败归一为稳定 safe error 和本地化只读提示,同时继续保持 Runtime 写入和 Debug fail-closed。验证:后端 Profile route tests 28 passed,前端 focused source-contract run 1244 passed,i18n pass,本地 operation list 返回 `200 {"operations":[]}`,旧 Runtime Profile view 返回 `200` 且 `safeError.code=profile_not_found`;Session list 在该 Runtime 升级前仍是预期的上游 `401 X-Jwt-Token header is required`。见 bugfix PRD [studio-mpa-profile-errors](../../bugfixes/studio-mpa-profile-errors/2026-09-20-studio-mpa-profile-errors.zh.md)。 | - -## 13. 实现变更记录 - -2026-09-16:关闭 S4-07 生命周期、PostgreSQL 与浏览器门禁。loopback `turn_lifecycle` 场景已提供按 Session 和 task 查询 control status、pause/resume 命令、`new_turn_required` 种子、显式 Runtime `/continue`、continuation SSE、调用计数和状态证据。Studio 会把 `new_turn_required` 显示为本地化的检查点续跑状态,不再暴露原始协议枚举。真实浏览器 `BC-09` 使用 system Google Chrome via Playwright 通过;原始证据已写入被忽略的本地 `evidence/browser/mpa-s4-1789532050/BC-09/`。 - -2026-09-16:在 `agentkit-mpa-agent` 实现 S5-01 Secret reference migration 基础。schema version 7 增加 Secret reference 和迁移状态字段。迁移服务采用 reference-first 且可重试:provider 失败或数据库更新前中断时保留明文;成功后清空明文,只记录 reference/status。Runtime 执行在模型和内置 MCP 的 Secret 使用边界通过 resolver seam 消费引用。真实 AgentKit Vault provider 接线仍留给后续 release-gate 集成。 - -2026-09-16:在 `agentkit-mpa-agent` 实现 S5-01a canary-safe redaction。Secret reference migration 读到 legacy 明文后,会先把这些值注册为 value-aware redaction 字面值,再创建外部引用。API access log、Session 输出脱敏、Runtime Console 脱敏、lifecycle details 和 trace-step 持久化会从字符串和嵌套结构中移除已注册字面值,同时不会因为宽泛 key-name 规则误隐藏 `flow_id`、`user_code` 等业务参数。schema version 8 随后将同一脱敏边界扩展到 Runtime operation response/resource snapshot。 - -2026-09-17:完成持久 Runtime operation 与 Profile CAS 加固。SQL 和 in-memory operation store 现在共享一致的 identity、TTL、安全 replay 与 compare-and-swap 状态迁移语义;授权状态接口不返回 principal/idempotency/request-hash 内部字段。Profile create/update 的前置条件校验和 revision 切换在同一个 PostgreSQL advisory-lock 事务内执行,双连接测试证明同一 ETag 只能被一个 writer 消费。 - -2026-09-18:修复 AgentKit key-auth 部署下旧 ADK endpoint 的鉴权接线。Runtime version 62 在 AgentKit mode 下让 `/list-apps` 等 `require_auth` consumer 从 `Authorization` 解析已认证 Runtime principal,同时在非 AgentKit mode 下保留旧 `X-Jwt-Token` 行为。该变更仅调整鉴权 adapter,不涉及数据模型迁移。 - -2026-09-18:完成 Runtime version 62 的真实 Studio 对话展示验收。沙箱父级/子级活动均进入终态,最终回答在执行完成和 30 秒延迟后保持可见,刷新后恢复同一结果。本证据关闭具体的生产态对话回归;在所有已定义浏览器 Case 执行完成前,S5-12 仍保持未完成。 - -2026-09-18:完成 S5-08 共享 Python 控制面 client 提取。BFF 与后续 CLI consumer 现在共用 `veadk.integrations.mpa` 中公开的 MPA Profile/operation/capability/Session schema;`MpaControlPlaneClient` 集中处理 Studio bearer 鉴权、幂等与 ETag Header、结构化安全错误、响应校验、连接复用和终态 operation 查询。BFF 仍持有 Runtime 授权与编排职责,CLI 仍作为 BFF client,不导入 FastAPI route,也不接收 Runtime 凭据。本切片不改变 Runtime schema 或 API。 - -2026-09-18:S5-09 CLI 设计评审在实现前明确了命令和职责边界。既有 `veadk mpa create` 继续作为基础设施部署流程;管理面等价能力新增在 `veadk mpa control` 下,只覆盖 Studio BFF 已持有的 view、生命周期 operation、Profile、Session execution-config 和 delete-preview API。输入使用 JSON 文件/stdin,必须显式传入所需的幂等键和 ETag,输出/错误退出码保持稳定,认证材料绝不输出。Chat、Turn 执行和 Runtime 诊断不重复开发,也不通过 CLI 直连 Runtime;在另行评审共享 BFF 契约前,它们继续使用现有 Studio 执行面/诊断面。该边界不需要修改 Runtime API 或数据模型。 - -2026-09-18:按评审方案实现并验证 S5-09。新控制面 CLI 的所有网络操作都委托 `MpaControlPlaneClient`,保留旧部署命令,并覆盖完整 VC-22 管理面,不新增 Runtime 直连路径。成功结果与 BFF 错误为可解析 JSON;Click 本地用法/输入错误保持文本并退出 `2`。鉴权、冲突/前置条件、可重试及其他 BFF 错误分别退出 `3`、`4`、`5`、`1`。已验证的 timeout Case 确认首个写请求到达 BFF 后,调用方使用完全相同的已持久化幂等键重试。 - -2026-09-18:使用固定双方仓库 revision 和线上 v62 registry digest 完成 S5-10 兼容性预检。已批准组合命中 `p0-codex-rest-v1`;不支持的协议和非法 digest fixture 均在任何 mutation 路径前按约定退出码失败。线上 Runtime 还被独立核验为 version 62 且状态 `Ready`,VC-15 脱敏测试保持通过。本次只完成 VC-19 的 preflight 部分;S5-13 仍在 S5-11 之后负责最终回归、覆盖率、PostgreSQL、构建与性能门禁。 - -2026-09-18:完成 S5-11,真实 VC-21 run 为 `vc21-20260918-045`。该隔离 run 基于 source Runtime v62 创建独立 AgentKit Runtime、Tool 和 PostgreSQL database,应用 Studio Profile,通过真实 Worker 协议完成 execution-ready smoke,验证同镜像 no-op update/recovery、带 `noRollbackNeeded=true` 的兼容 rollback smoke、active operation 与 active session 删除阻断、通过 Runtime API 清理 idle Session,最后删除 AgentKit Runtime 与 PostgreSQL database,最终 `residue=[]`。上一轮真实 run 暴露出一个问题:Profile 已 applied 后,遗留的 `failed_retryable/smoke_running` operation 仍会永久阻塞删除;Studio preview/delete 现已把该已恢复状态视为非阻塞,同时继续保留真正 active operation 和 active Session 的阻断。 - -2026-09-16:在 VeADK 实现 S5-02 BFF `MpaAgentView` 切片。`GET /web/mpa/agents/{mpaInstanceId}/view` 统一输出 0/1/N Runtime binding 判定、active operation 摘要、Runtime metadata、Profile status、capabilities 和 safe error。0 个 binding 为 `runtime_missing`;多个 binding 为 `binding_ambiguous`;唯一 MPA-tagged Runtime 会暴露 binding 状态,后续切片据此区分 Profile 写入、Session 配置和 Debug 能力。消费该 view-model 的前端详情页仍属于 S5-03。 - -2026-09-16:实现 S5-03 前端 `MpaAgentView` 消费切片。`AgentWorkspace` 现在会在 MPA 详情页加载专用 view-model,在基本信息区展示 binding 健康状态;当 Runtime binding 缺失、重复或 orphan 时阻止 Session 配置管理;非 MPA 详情、更新和版本流程仍走原有逻辑。 - -2026-09-16:实现 S5-04 Runtime Console/Trace correlation 切片。Runtime trace API 现在会在既有有界、已脱敏 details 旁返回稳定的 Agent/Profile/config/Turn/task/runtime/worker 关联字段;S5-05 的 Studio 视图可直接消费这些字段,不需要解析任意 `details` JSON。 - -2026-09-16:实现 S5-05 Studio diagnostics 消费切片。MPA Agent 详情页现在有 MPA-only Diagnostics section,通过现有 Runtime proxy 读取 Runtime Console run history 和最新 trace,展示结构化 correlation,并继续保持非 MPA 详情、版本、更新路径隔离。 - -2026-09-16:实现 S5-06 兼容性 preflight 切片。`scripts/mpa_p0_contract.py` 现在委托共享的 `veadk.cli.mpa_p0_contract` 评估器;Studio 在 MPA 直连 Runtime update,以及 GitHub delivery setup、source sync、rollback PR 创建前执行同一矩阵检查。incompatible 或 malformed manifest 会在副作用前以安全结构化 `409` 返回;compatible manifest 继续原路径。本切片不修改 Runtime 数据模型或 mpa-agent API。 - -2026-09-16:实现 S5-07 授权删除预览与分阶段清理切片。新的 Studio BFF 预览接口会明确展示将被清理的内容:先删除可见 idle/terminal Runtime 会话,再通过 Runtime 会话清理路径移除其诊断数据和挂载元信息,最后删除 AgentKit Runtime 资源。删除执行会在服务端重新运行同一组检查,拒绝 active operation 和处于 `queued|running|pausing|paused|resuming` 的 active session;单个 session 删除遇到 `404` 时视为已清理,便于安全重试;如果 Runtime 标签没有标记为 MPA,即使浏览器传了 `agentCategory=mpa` 也不会走 MPA 清理路径。该切片复用已有 mpa-agent session list/delete API 和 AgentKit Runtime delete API,不需要 Runtime 数据库迁移或新增 mpa-agent endpoint。 - -2026-09-17:实现 S5-08 Studio 统一 Profile 管理入口。MPA 详情页现在把 Profile 配置作为 Agent 级主管理面,支持状态查看、应用/重新应用和编辑,并统一走 MPA BFF Profile operation。`编辑 Profile` 复用既有 Studio Agent 创建工作台的 Profile-only 模式,用户修改的是同一份模型、系统提示词、Tool、Skill、MCP 与 Multiagent 配置;保存时只把 Profile 应用到 mpa-agent,不触发通用 Runtime 部署/更新流程。BFF view 对 `orphan_runtime` 开放写能力以支持首次 Profile apply;Debug 和 Session 配置仍需等待 Profile binding 完成。本地 `veadk studio --dev` 在 Studio TOS 缺失时使用进程内 MPA operation repository,保留幂等与 ETag 语义用于本地验证;生产仍要求 TOS 持久化。Runtime Session 创建现在会在 Studio 传入 `mpaInstanceId + profileRevision` 且未传 `executionConfigRevision` 时初始化 execution-config revision 1。该设计保持 AgentKit Studio 是统一管控入口,同时避免使用 Managed Agent API,也避免在 mpa-agent 中重复建设产品配置表单。 - -2026-09-17:修复已完成回答在持久多轮历史中的消失路径。第一次对账修复是必要但不充分的:严格浏览器检查表明,每个由用户消息分隔的 assistant 历史段都复用了 `adk-history-0`,导致 upsert 覆盖更早回答。`eventsToTurns(...)` 现在为每个历史分段创建带单调递增索引的唯一 projector 前缀。对账仍以精确最终事件 ID 为栅栏;如果更新的本地回答已经开始,就拒绝替换 transcript。回归和浏览器验收改为断言有序 assistant turn 节点、延迟后的稳定性以及整页重载后的持久性,不再以整页搜索预期文本作为通过条件。 - -2026-09-17:修复 MPA 实时父级沙箱活动终态收敛。实时 `/sse` 路径可能以 wrapper `sandbox_task` function response 结束,而不会重放持久化的 sandbox `invocation.completed` 事件。projector 现在只在 invocation 仍 active 时把 `finalAlreadyEmitted=true` 作为控制终态:关闭既有父级卡片和 Turn,同时保留已经推流的输出。持久历史重放在 sandbox final event 已关闭 Turn 后仍忽略该 wrapper,避免生成重复工具卡。 diff --git a/prd-spec/features/mpa-p0-productionization/browser-cases.md b/prd-spec/features/mpa-p0-productionization/browser-cases.md deleted file mode 100644 index aff08cebd..000000000 --- a/prd-spec/features/mpa-p0-productionization/browser-cases.md +++ /dev/null @@ -1,101 +0,0 @@ -# MPA AgentKit P0 Browser Verification Runbook - -- Status: `verified`; `BC-01` through `BC-09` passed on 2026-09-20 and cover `VC-17` -- Chinese: [browser-cases.zh.md](browser-cases.zh.md) -- Raw local evidence: ignored `evidence/browser///` -- Delivery note: browser screenshots, network traces, console output, and browser state are local test artifacts and are intentionally not committed. - -## 1. Shared environment - -- M0 adds `scripts/run-mpa-p0-browser-fixture.sh`, accepted only with `APP_ENV=test`, `VEADK_MPA_TEST_SCENARIOS=1`, and loopback bind; otherwise startup fails. -- Test API: `GET /__test/mpa/scenarios`, `PUT /__test/mpa/scenarios/{name}`, `GET /__test/mpa/scenarios/{name}`, `POST /__test/mpa/scenarios/{name}/calls/{call}`, and `POST /__test/mpa/scenarios/{name}/barriers/{barrier}/release`. These routes do not exist in production routing. -- Scenarios: `create_success`, `profile_apply_failed`, `smoke_worker_not_ready`, `smoke_output_mismatch`, `cleanup_failed`, `runtime_missing`, `binding_ambiguous`, `orphan_runtime`, `old_runtime`, `session_revision_6`, `cursor_expired`, `slow_scope_switch`, `turn_lifecycle`. S4-07 implements the `turn_lifecycle` participant barriers, control/continue routes, call counts, and state evidence used by BC-09. -- Start: caller first runs `umask 077; VEADK_MPA_SCENARIO_TOKEN=$(openssl rand -hex 32); printf '%s' "$VEADK_MPA_SCENARIO_TOKEN" > .gstack/-scenario.token`, verifies `scripts/run-mpa-p0-browser-fixture.sh` exists and the token file is mode `0600`, then starts `APP_ENV=test VEADK_MPA_TEST_SCENARIOS=1 VEADK_MPA_SCENARIO_TOKEN_FILE=.gstack/-scenario.token scripts/run-mpa-p0-browser-fixture.sh --host 127.0.0.1 --port 8000`. Delete token/state files at the end; token never enters logs or evidence. -- Browser: `B=$HOME/.claude/skills/gstack/browse/dist/browse`; A runs `env BROWSE_STATE_FILE=.gstack/-a.json $B ...`; B runs `env BROWSE_STATE_FILE=.gstack/-b.json $B ...`. Both separately open the fixture Studio URL with the test login state required by the mounted fixture; assert equal principal hashes while cookie/localStorage/sessionStorage remain isolated. -- Test API reads the token from `VEADK_MPA_SCENARIO_TOKEN_FILE`. Command template: `curl -fsS -H "Authorization: Bearer $(<.gstack/-scenario.token)" -X PUT http://127.0.0.1:8000/__test/mpa/scenarios/create_success -H "Content-Type: application/json" --data '{"barriers":[]}'`; use `GET /__test/mpa/scenarios/{name}` for state, `POST /__test/mpa/scenarios/{name}/calls/{call}` for call counts, and `POST /__test/mpa/scenarios/{name}/barriers/{barrier}/release` to release barriers. The seed response must return exact `barriers{}` state. Use the same Header for every test API request. -- Common browser commands: each side uses its own `BROWSE_STATE_FILE` to run `goto http://127.0.0.1:8000`, `snapshot -i -a -o `, `console --errors`, `network`, and `viewport `. Reset/seed before each Case; save call counts and reset afterward. - -## BC-01: creation and refresh recovery (`AC-1`, `AC-8`) - -- Preconditions: seed `create_success`; record client idempotency key. -- Steps: create from tab/header/empty state; at runtime/profile/smoke stages run `$B reload` and `$B snapshot -D`. -- Input: same minimum Profile and region, three source values. -- Expected: every intent has `category=mpa`; POST is 202; refresh recovers via operation ID/active list; every resource effect count is one; runnable/chat only after cleanup. -- Evidence: `BC-01/{tab,header,empty,refresh}.png`, `network.txt`, `call-counts.json`. -- Failure: duplicate resource, lost operation, or early runnable is `fail`. - -## BC-02: creation failures and retry (`AC-8`) - -- Preconditions: seed each of `profile_apply_failed`, `smoke_worker_not_ready`, `smoke_output_mismatch`, `cleanup_failed`. -- Steps: create, wait for failure, screenshot, click retry, read and release every `barriers[]` item returned by that scenario's seed response, and await terminal. -- Input: same Profile and persistent idempotency key. -- Expected: safe error/stage/retry; chat disabled; retry resumes safely; effect count unchanged. -- Evidence: `BC-02/-before.png`, `after.png`, network/call-counts. -- Failure: early runnable, secret leak, or duplicate effect is `fail`. - -## BC-03: Agent detail and versions (`AC-8`, `AC-10`) - -- Preconditions: seed `runtime_missing`, `binding_ambiguous`, `orphan_runtime`, `old_runtime`, then normal single binding. -- Steps: open list/detail/versions, snapshot, assert button enabled/disabled state. -- Input: same ID in different scopes and target/applied mismatch. -- Expected: 0/1/N/orphan/unsupported follows `MpaAgentView`; versions come from AgentKit; no GitHub delivery/generic draft/evaluation/optimization; no secret. -- Evidence: `BC-03/.png`, console/network, view JSON. -- Failure: binding error, wrong write entry, or cross-scope data is `fail`. - -## BC-04: two-client CAS (`AC-3`, `AC-8`) - -- Preconditions: seed `session_revision_6`; A/B separately authenticate as the same user; record identical principal hash, Session ID, and ETag 6. -- Steps: A/B submit model/Skill PATCH. The fixture uses barriers `config-patch-a` and `config-patch-b`; after both call counts equal one, release them in order through the authenticated barrier API. -- Input: both requests explicitly carry ETag 6. -- Expected: one 200/revision 7 and one 412; loser renders authority and requires explicit retry; both converge. -- Evidence: `BC-04/context-{a,b}.png`, two network transcripts, barrier/call-count. -- Failure: different principal, requests not both on revision 6, or silent overwrite is `fail`. - -## BC-05: explicit Session upgrade (`AC-3`, `AC-8`) - -- Preconditions: Session pins N, N+1 is applied, and an active Turn fixture exists. -- Steps: verify only “new version available”; click upgrade; inspect active and next Turns. -- Input: target N+1, current ETag, persistent idempotency key. -- Expected: active Turn stays N; next Turn uses N+1; override/clear retained and inherit updated; invalid target fails atomically. -- Evidence: `BC-05/{before,active,next,error}.png`, revision transcript. -- Failure: automatic upgrade or active-Turn hot switch is `fail`. - -## BC-06: streaming refresh and cursor (`AC-7`, `AC-8`) - -- Preconditions: controlled stream fixture plus `cursor_expired`. -- Steps: record cursor K, refresh, wait terminal; then use unknown cursor. -- Input: text/usage/artifact/terminal with persistent/live overlap. -- Expected: only events after K; every kind counted once; unknown cursor shows recovery and never success. -- Evidence: `BC-06/{before,after,expired}.png`, SSE JSONL, count JSON. -- Failure: duplicate, missing, or false-success state is `fail`. - -## BC-07: keyboard, IME, and narrow view (`AC-8`) - -- Preconditions: seed `create_success`; Chinese Pinyin IME; viewports `1440x900`, `1024x768`. -- Steps: Enter during composition, Enter after compositionend, repeated Enter/click while busy; Tab/Shift+Tab; long name/operation/error. -- Input: Chinese text and long safe fixture. -- Expected: no submit during composition, one after; one operation while busy; visible focus; no overlap/horizontal overflow and primary action reachable. -- Evidence: `BC-07/{desktop,narrow,ime-steps}.png`, call-count, OS/browser/IME versions. -- Failure: duplicate submit, unreachable action, or overlap is `fail`. - -## BC-08: identity and cache isolation (`AC-6`, `AC-8`) - -- Preconditions: seed `slow_scope_switch`; two user/Agent/region fixtures. -- Steps: switch scope while old response waits, release barrier, inspect page/cache; issue denied request. -- Input: same resource ID under different principals/scopes. -- Expected: stale response cannot overwrite; caches isolated; denied reveals no existence, ID, or secret. -- Evidence: `BC-08/{before,after,denied}.png`, storage, network, call-count. -- Failure: cross-scope data or secret leak blocks release. - -## BC-09: pause, resume, and explicit continuation (`AC-5`, `AC-8`) - -- Preconditions: seed `turn_lifecycle` with primary plus two workers, partial/full ACK, recoverable, and new-turn-required fixtures. -- Steps: pause, release one/all participant barriers, resume; seed timeout and click continue; observe counts during refresh/poll timeout. -- Input: same Session/Turn and stable control/continue idempotency keys. -- Expected: `running -> pausing -> paused -> resuming -> running`; partial ACK is not paused; new-turn-required only shows explicit continue; refresh/poll timeout never creates a Turn; repeated continue creates one linked Turn. -- Evidence: `BC-09/{running,partial,paused,resumed,continue}.png`, control/continue network and participant/call-count JSON. -- Failure: false paused, automatic continue, or duplicate linked Turn blocks release. - -## 2. Exit - -BC-01 through BC-09 pass with no unhandled console error; each runs `snapshot -a`, `console --errors`, and `network` and saves under ignored local `evidence/browser///`. A slice may execute its own BC subset, but only the S5 full regression can mark `VC-17=pass`. If gstack/Chromium or scenario harness is unavailable, `VC-17/AC-8=blocked`; Node tests cannot replace it. Finally reset scenarios, stop A/B through their run-specific state files, stop the fixture service, delete both state files and the token file, and verify zero test resources. diff --git a/prd-spec/features/mpa-p0-productionization/browser-cases.zh.md b/prd-spec/features/mpa-p0-productionization/browser-cases.zh.md deleted file mode 100644 index 413f2146f..000000000 --- a/prd-spec/features/mpa-p0-productionization/browser-cases.zh.md +++ /dev/null @@ -1,101 +0,0 @@ -# MPA AgentKit P0 浏览器验证 Runbook - -- 状态:`verified`;`BC-01` 至 `BC-09` 已于 2026-09-20 通过并覆盖 `VC-17` -- English:[browser-cases.md](browser-cases.md) -- 原始本地证据:被忽略的 `evidence/browser///` -- 交付说明:浏览器截图、网络记录、console 输出和浏览器状态均为本地测试产物,明确不提交。 - -## 1. 公共环境 - -- M0 新增 `scripts/run-mpa-p0-browser-fixture.sh`,仅在 `APP_ENV=test`、`VEADK_MPA_TEST_SCENARIOS=1`、loopback bind 时启动;任一条件不满足则拒绝。 -- 测试 API:`GET /__test/mpa/scenarios`、`PUT /__test/mpa/scenarios/{name}`、`GET /__test/mpa/scenarios/{name}`、`POST /__test/mpa/scenarios/{name}/calls/{call}`、`POST /__test/mpa/scenarios/{name}/barriers/{barrier}/release`。生产路由表中不得存在这些接口。 -- 固定 scenario:`create_success`、`profile_apply_failed`、`smoke_worker_not_ready`、`smoke_output_mismatch`、`cleanup_failed`、`runtime_missing`、`binding_ambiguous`、`orphan_runtime`、`old_runtime`、`session_revision_6`、`cursor_expired`、`slow_scope_switch`、`turn_lifecycle`。S4-07 已实现 `turn_lifecycle` 的 participant barrier、control/continue 路由、call count 和 BC-09 使用的状态证据。 -- 启动:调用方先执行 `umask 077; VEADK_MPA_SCENARIO_TOKEN=$(openssl rand -hex 32); printf '%s' "$VEADK_MPA_SCENARIO_TOKEN" > .gstack/-scenario.token`,确认 `scripts/run-mpa-p0-browser-fixture.sh` 已存在且 token 文件权限为 `0600`,再以 `APP_ENV=test VEADK_MPA_TEST_SCENARIOS=1 VEADK_MPA_SCENARIO_TOKEN_FILE=.gstack/-scenario.token scripts/run-mpa-p0-browser-fixture.sh --host 127.0.0.1 --port 8000` 启动。结束后删除 token/state 文件;token 不进入日志或 evidence。 -- 浏览器:`B=$HOME/.claude/skills/gstack/browse/dist/browse`;A 调用 `env BROWSE_STATE_FILE=.gstack/-a.json $B ...`,B 调用 `env BROWSE_STATE_FILE=.gstack/-b.json $B ...`。两者分别打开带 fixture 所需测试登录态的 Studio URL,断言 principal hash 相同,但 cookie/localStorage/sessionStorage 相互隔离。 -- 测试 API 从 `VEADK_MPA_SCENARIO_TOKEN_FILE` 读取 token。调用模板:`curl -fsS -H "Authorization: Bearer $(<.gstack/-scenario.token)" -X PUT http://127.0.0.1:8000/__test/mpa/scenarios/create_success -H "Content-Type: application/json" --data '{"barriers":[]}'`;用 `GET /__test/mpa/scenarios/{name}` 读取状态,`POST /__test/mpa/scenarios/{name}/calls/{call}` 记录 call count,`POST /__test/mpa/scenarios/{name}/barriers/{barrier}/release` 释放 barrier。seed 响应必须包含准确的 `barriers{}` 状态;所有测试 API 请求都使用同一 Header。 -- 公共浏览器命令:两端分别以各自 `BROWSE_STATE_FILE` 调用 `goto http://127.0.0.1:8000`、`snapshot -i -a -o `、`console --errors`、`network`、`viewport `。每个 Case 前 reset/seed,之后保存 call-count 并 reset。 - -## BC-01:创建与刷新恢复(`AC-1`, `AC-8`) - -- 前置:seed `create_success`;记录客户端幂等键。 -- 操作:从 Tab/Header/空态分别创建;在 runtime/profile/smoke 阶段各执行 `$B reload`、`$B snapshot -D`。 -- 输入:同一最小 Profile、region 和三种 `source`。 -- 预期:intent 均含 `category=mpa`;POST 返回 202;刷新用 operation ID/active list 恢复;每种资源 effect count 为 1;清理后才 runnable/chat enabled。 -- 证据:`BC-01/{tab,header,empty,refresh}.png`、`network.txt`、`call-counts.json`。 -- 失败:重复资源、丢 operation 或提前 runnable 均为 `fail`。 - -## BC-02:创建失败与重试(`AC-8`) - -- 前置:依次 seed `profile_apply_failed`、`smoke_worker_not_ready`、`smoke_output_mismatch`、`cleanup_failed`。 -- 操作:创建、等待失败、截图、点击 retry;从各 seed 响应读取并逐个释放 `barriers[]`,等待终态。 -- 输入:同一 Profile 与持久 idempotency key。 -- 预期:安全错误/阶段/retry 明确;chat disabled;retry 从安全阶段继续;effect count 不增加。 -- 证据:`BC-02/-before.png`、`after.png`、network/call-counts。 -- 失败:误报 runnable、泄密或重复副作用为 `fail`。 - -## BC-03:Agent 详情与版本(`AC-8`, `AC-10`) - -- 前置:逐个 seed `runtime_missing`、`binding_ambiguous`、`orphan_runtime`、`old_runtime` 及正常单 binding。 -- 操作:打开列表/详情/版本,执行 snapshot、检查按钮 enabled/disabled。 -- 输入:同 ID 不同 scope fixture 和 target/applied mismatch。 -- 预期:0/1/N/orphan/unsupported 状态符合 `MpaAgentView`;版本来自 AgentKit;无 GitHub delivery/generic draft/evaluation/optimization;无 Secret。 -- 证据:`BC-03/.png`、console/network、view JSON。 -- 失败:绑定误判、错误写入口或跨 scope 数据为 `fail`。 - -## BC-04:双客户端 CAS(`AC-3`, `AC-8`) - -- 前置:seed `session_revision_6`;A/B 用同一测试用户独立登录;记录相同 principal hash、Session ID、ETag 6。 -- 操作:A/B 分别提交 model/Skill PATCH;fixture 使用 `config-patch-a`、`config-patch-b` 两个 barrier,在两者 call-count 均为 1 后依次调用认证的 barrier release API。 -- 输入:两个请求均显式带 ETag 6。 -- 预期:一方 200/revision 7,另一方 412;失败端显示权威状态并要求显式 retry;最终两端一致。 -- 证据:`BC-04/context-{a,b}.png`、两个 network transcript、barrier/call-count。 -- 失败:不同 principal、请求未同时基于 revision 6、静默覆盖均为 `fail`。 - -## BC-05:显式 Session 升级(`AC-3`, `AC-8`) - -- 前置:Session 固定 N、N+1 已 applied、活动 Turn fixture。 -- 操作:确认只显示“新版本可用”;点击升级;观察活动与下一 Turn。 -- 输入:target N+1、当前 ETag、持久 idempotency key。 -- 预期:活动 Turn 保持 N;下一 Turn N+1;override/clear 保留、inherit 更新;无效 target 原子失败。 -- 证据:`BC-05/{before,active,next,error}.png`、revision transcript。 -- 失败:自动升级或活动 Turn 热切换为 `fail`。 - -## BC-06:流式刷新与 cursor(`AC-7`, `AC-8`) - -- 前置:受控流 fixture;另 seed `cursor_expired`。 -- 操作:记录 cursor K、刷新、等待终态;再用未知 cursor。 -- 输入:含 text/usage/artifact/terminal 与 persistent/live 重叠。 -- 预期:只补发 K 后事件,各类计数一次;未知 cursor 显示恢复入口,不显示成功。 -- 证据:`BC-06/{before,after,expired}.png`、SSE JSONL、计数 JSON。 -- 失败:重复、丢失或错误成功态为 `fail`。 - -## BC-07:键盘、IME 与窄屏(`AC-8`) - -- 前置:seed `create_success`;中文拼音输入法;viewport `1440x900`、`1024x768`。 -- 操作:composition 中 Enter、compositionend 后 Enter、busy 时重复 Enter/点击;Tab/Shift+Tab;长名称/operation/error。 -- 输入:中文文本和超长安全 fixture。 -- 预期:composition 不提交,结束后一次;busy 只一 operation;焦点可见;无重叠/横向溢出,主操作可达。 -- 证据:`BC-07/{desktop,narrow,ime-steps}.png`、call-count、OS/browser/IME 版本。 -- 失败:重复提交、不可达操作或布局遮挡为 `fail`。 - -## BC-08:身份与缓存隔离(`AC-6`, `AC-8`) - -- 前置:seed `slow_scope_switch`;两个用户/Agent/region fixture。 -- 操作:延迟旧请求时切换 scope,释放 barrier,检查页面与缓存;执行 denied 请求。 -- 输入:相同资源 ID、不同 principal/scope。 -- 预期:旧响应不覆盖;缓存隔离;denied 不泄露存在性、ID 或 Secret。 -- 证据:`BC-08/{before,after,denied}.png`、storage、network、call-count。 -- 失败:跨 scope 数据或 Secret 泄漏为阻断。 - -## BC-09:暂停、恢复与显式续跑(`AC-5`, `AC-8`) - -- 前置:seed `turn_lifecycle`,包含 primary + 两 worker、部分/完整 ACK、可恢复和 new-turn-required fixture。 -- 操作:依次触发 pause、释放一个/全部 participant barrier、resume;再 seed 超时状态并点击 continue;刷新和轮询超时期间观察 call-count。 -- 输入:同一 Session/Turn、稳定 control/continue idempotency key。 -- 预期:`running -> pausing -> paused -> resuming -> running`;部分 ACK 不显示 paused;new-turn-required 只展示显式继续;刷新/轮询超时不自动创建 Turn;重复 continue 只产生一个 linked Turn。 -- 证据:`BC-09/{running,partial,paused,resumed,continue}.png`、control/continue network、participant/call-count JSON。 -- 失败:误报 paused、自动 continue 或重复 linked Turn 为阻断。 - -## 2. 准出 - -BC-01~BC-09 全部 pass,console 无未处理 error;每项执行 `snapshot -a`、`console --errors`、`network` 并保存到被忽略的本地 `evidence/browser///`。切片可先执行自己的 BC 子集,但只有 S5 全量回归可标记 `VC-17=pass`。gstack/Chromium 或 scenario harness 不可用则 `VC-17/AC-8=blocked`,Node tests 不替代。结束后调用 reset,分别用 A/B 专属 state file执行 `stop`,停止 fixture 服务,删除两个 state file 和 token file,并确认测试资源为零。 diff --git a/prd-spec/features/mpa-response-grouping/2026-09-22-mpa-response-grouping.md b/prd-spec/features/mpa-response-grouping/2026-09-22-mpa-response-grouping.md deleted file mode 100644 index 1752c8275..000000000 --- a/prd-spec/features/mpa-response-grouping/2026-09-22-mpa-response-grouping.md +++ /dev/null @@ -1,38 +0,0 @@ -# MPA A2A response grouping - -[中文版](2026-09-22-mpa-response-grouping.zh.md) - -## Scope and evidence -ID: `mpa-response-grouping`; date: 2026-09-22; status: implemented. -Contract: [runtime diagnostics CON-11](../../../specs/studio-runtime-diagnostics/README.md). -The transcript renders each projected assistant turn with its own footer. Outer reasoning and sandbox final turns therefore have separate actions. TraceDrawer already retrieves session spans through an end timestamp. The user approved one response area with a unified footer, preserving general-agent behavior. - -## Requirements and scenarios -- FR-1: Only explicitly identified MPA A2A apps group adjacent assistant fragments between user/system boundaries. Keep raw events/turns unchanged. General ADK/A2A, native MPA and parallel-agent layout retain their defaults. -- FR-2: Preserve ordered reasoning, tools, text, attachments, errors and auth cards within one response; a stable first-fragment key avoids remounting during streaming. Hide the single footer until request presentation completes or authorization resolves. Cancellation/failure preserve partial content and existing error handling. A subsequent user request is independent. -- FR-3: Copy/share cover the grouped answer/region; feedback and annotation retain the last answer-bearing event identity. Unified trace uses the latest available fragment timestamp, preserving session spans from all phases; this is not a new per-request trace API. -- FR-4: Record reported A2A usage deltas once per source/event identity, merge request ledgers instead of summing duplicated fragment snapshots. Include metadata-only and trailing usage. Missing event identity uses source/request/invocation and usage value as a conservative replay key. For older turns without ledgers, retain max reported tokens per invocation (or one unidentifiable source), never invent missing worker usage. No billing accuracy or reconstructed missing history claim. - -## Design and boundaries -A pure presentation helper in `transcriptRows.ts` derives grouped turns. App renders and annotates this view, while persistence/stream updates operate on source turns. The MPA-only projector records a usage ledger in optional TurnMeta fields; late usage updates the most recent projected turn without changing completion. Existing Blocks, actions, trace drawer and styles are reused; no new dependency, API/auth/backend/cloud change. Frontend design skills referenced by SPEC were not found at repository/global locations; follow existing components and SPEC directly without redesign. - -## Tasks and acceptance -T-1 / AC-1 (FR-1,2): failing tests for grouping, stable identity, user/system boundaries, default identity, running/completed/cancelled content. -T-2 / AC-2 (FR-3,4): failing tests for live/history usage including replay, trailing/early metadata, copy/feedback target, latest trace timestamp; wire presentation and annotations to the same derived view. -T-3 / AC-3: full frontend tests, build/assets, isolated real-browser normal/running/empty/error/narrow/keyboard checks, bilingual docs and secret scan. No real cloud creation needed; live cloud generation reported separately. - -## Review and authorization -User's “改改看” approves the previously proposed MPA-only grouping. Direct review (review-spec unavailable): no source-turn mutation, no cross-request grouping, existing trace API retained, source-keyed usage, explicit default opt-out, no secret persistence; bilingual consistency checked. No blockers. Risks: missing usage identities cannot distinguish identical unlabelled calls; missing history stays missing. No commit/push/restart is authorized by this design. - -## Verification -Verified 2026-09-22 against the working diff based on `3bbd260a`, including prior uncommitted changes. T-1 through T-3 / AC-1 through AC-3 completed. Direct implementation review: presentation and annotation use the same grouped view; writes still target raw turns and original feedback event IDs; general mode returns the original array; source usage snapshots do not multiply across fragments. No blocking findings. - -- `pass`: test-first baseline failed before implementation; nine new `frontend/tests/mpaResponseGrouping.test.mjs` tests cover boundaries, stable keys, partial/error/auth content, general defaults, usage replay and continuation, live/history and App wiring. -- `pass`: `npm --prefix frontend test` — 1,296 Node tests and 25 Vitest tests. Existing source-wiring assertions were updated for `transcriptTurns` after the first run exposed stale variable-name expectations. -- `pass`: `npm --prefix frontend run build` — TypeScript and both app/widget builds; existing chunk-size warnings remain. -- `pass`: `npm --prefix frontend run test:webui-assets` — 104 files and 248 references. -- `pass`: isolated browser fixture with actual grouping and Blocks, simulated footer actions: one MPA region/footer and 300 reported tokens (100 outer + 200 worker), running footer hidden, general control stays split, empty and stopped/error cases retain content. Keyboard activates the sample trace action; width 390 has no horizontal overflow; no console errors. This does not prove live trace contents or image export. Temporary files/server removed and viewport reset. -- `pass`: actual Studio `/index.html` loads `index-c5Xo62sv.js`, connects the selected MPA through A2A, and displays a usable composer. Backend not restarted. -- `pass`: scoped gitleaks over changed source/tests/docs and app/widget assets; `git diff --check`; paired documents and relative links reviewed. -- `not_run`: new cloud model generation, real trace retrieval, clipboard/image export end-to-end. Their underlying handlers remain unchanged; new derived targets are covered by tests. No claim of newly captured cloud output. -- `not_applicable`: Python and harness/sidecar gates (no backend/protocol/sidecar changes), input IME changes (composer untouched). No commit/push performed. diff --git a/prd-spec/features/mpa-response-grouping/2026-09-22-mpa-response-grouping.zh.md b/prd-spec/features/mpa-response-grouping/2026-09-22-mpa-response-grouping.zh.md deleted file mode 100644 index 4e4729cf1..000000000 --- a/prd-spec/features/mpa-response-grouping/2026-09-22-mpa-response-grouping.zh.md +++ /dev/null @@ -1,38 +0,0 @@ -# MPA A2A 回复区域合并 - -[English](2026-09-22-mpa-response-grouping.md) - -## 范围与证据 -ID:`mpa-response-grouping`;日期:2026-09-22;状态:implemented。 -契约:[runtime diagnostics CON-11](../../../specs/studio-runtime-diagnostics/README.zh.md)。 -当前每个投影助手片段都有独立底栏,所以外层推理和 sandbox 最终答案分别带操作栏。TraceDrawer 已按结束时间查询会话轨迹。用户批准统一回复区域与底栏,并保持通用智能体行为。 - -## 需求与场景 -- FR-1:仅明确识别的 MPA A2A 应用合并用户/系统消息边界之间的相邻助手片段。原始事件/turn 不变。通用 ADK/A2A、原生 MPA 和并行智能体布局保持默认行为。 -- FR-2:同一区域保留有序的推理、工具、文本、附件、错误、鉴权卡片;首片段稳定 key 避免流式重挂载。本轮展示或鉴权未完成时隐藏唯一底栏。取消/失败保留已有内容及既有错误处理;下一条用户请求独立。 -- FR-3:复制/分享覆盖合并后的答案/区域;反馈与批注保留最后一个答案片段的事件身份。统一轨迹使用所有片段最新可用时间,保留各阶段的会话 span;不新增按请求查询的轨迹接口。 -- FR-4:按来源/事件身份记录一次 A2A 用量增量,合并本轮账本而不是累加片段上重复的统计快照。包含仅元数据和末尾用量。缺事件身份时,用来源/请求/调用与用量值作保守回放键。旧 turn 没有账本时按 invocation(或一个无法识别的来源)取已报告最大 token,不虚构缺失 worker 用量。不承诺账单级精度或重建缺失历史。 - -## 设计与边界 -在 `transcriptRows.ts` 用纯展示函数派生合并 turn。App 渲染与批注使用此视图,持久化/流式更新仍使用原 turn。仅 MPA projector 在可选 TurnMeta 字段保存用量账本;晚到用量更新最近片段且不改变完成状态。复用 Blocks、操作栏、轨迹抽屉和样式,不新增依赖、接口/鉴权/后端/云变更。SPEC 引用的前端设计技能在仓库/全局路径未找到,直接遵守既有组件和 SPEC,不重新设计风格。 - -## 任务与验收 -T-1 / AC-1(FR-1,2):先写失败测试,覆盖合并、稳定身份、用户/系统边界、默认引用不变、运行/结束/取消内容。 -T-2 / AC-2(FR-3,4):先写失败测试,覆盖实时/历史用量、回放、前后置元数据、复制/反馈目标、最新轨迹时间;渲染与批注连接同一派生视图。 -T-3 / AC-3:完整前端测试、构建/资源、隔离真实浏览器正常/运行/空/错误/窄屏/键盘检查、双语文档和密钥扫描。无需真实云创建,云模型验证单独报告。 - -## 评审与授权 -用户“改改看”批准此前提出的仅 MPA 合并方案。直接评审(review-spec 不可用):不改原 turn,不跨请求合并,保留轨迹接口,按来源统计,显式默认禁用,无秘密持久化;双语一致,无阻塞。风险:缺用量身份时无法区分数值相同且无标签的调用;不重建缺失历史。本设计不授权提交/推送/重启。 - -## 验证 -2026-09-22 在基于 `3bbd260a` 的工作区差异上验证,包含此前未提交修改。T-1 至 T-3 / AC-1 至 AC-3 完成。直接实现评审:展示与批注使用同一合并视图,写操作仍指向原 turn 和原反馈事件 ID,通用模式返回原数组,来源用量快照不因分散在多个片段而重复计数。无阻塞发现。 - -- `pass`:测试先行基线在实现前失败;新增 `frontend/tests/mpaResponseGrouping.test.mjs` 九项测试覆盖边界、稳定 key、部分/错误/鉴权内容、通用默认行为、用量回放与续接、实时/历史和 App 接线。 -- `pass`:`npm --prefix frontend test` — 1,296 项 Node 测试和 25 项 Vitest 测试。首次完整运行暴露旧源码接线断言中的变量名预期,已更新为 `transcriptTurns` 后通过。 -- `pass`:`npm --prefix frontend run build` — TypeScript 与 app/widget 均通过,保留既有 chunk 大小警告。 -- `pass`:`npm --prefix frontend run test:webui-assets` — 104 个文件、248 条引用。 -- `pass`:隔离浏览器页面使用真实分组函数和 Blocks、模拟底栏操作:MPA 一个区域/底栏,用量 300(外层 100 + worker 200),运行中无底栏,通用对照保持分段,空及停止/错误场景保留内容。键盘可激活示例轨迹操作,390 宽度无横向溢出,无控制台错误。不证明真实轨迹内容或图片导出。临时文件/服务已清理,视口已恢复。 -- `pass`:实际 Studio `/index.html` 加载 `index-c5Xo62sv.js`,所选 MPA 通过 A2A 连接并显示可用输入框。未重启后端。 -- `pass`:针对修改源码/测试/文档和 app/widget 产物执行 gitleaks;`git diff --check`;检查双语文档及相对链接。 -- `not_run`:新增云模型生成、真实轨迹查询、剪贴板/图片导出端到端。底层处理函数不变,新的派生操作对象已由测试覆盖。不声称捕获了新的云端输出。 -- `not_applicable`:Python 和 harness/sidecar 门禁(无后端/协议/sidecar 变更)、输入法修改(Composer 未改)。未提交/推送。 diff --git a/prd-spec/features/mpa-runtime-iam/2026-10-09-managed-runtime-role.md b/prd-spec/features/mpa-runtime-iam/2026-10-09-managed-runtime-role.md deleted file mode 100644 index c14793be6..000000000 --- a/prd-spec/features/mpa-runtime-iam/2026-10-09-managed-runtime-role.md +++ /dev/null @@ -1,74 +0,0 @@ -# Managed MPA runtime role - -[中文](2026-10-09-managed-runtime-role.zh.md) - -Change ID: `mpa-runtime-iam`; created/revised: 2026-10-09; status: implemented; isolated verification passed, live smoke unverified. -Contracts: [Studio creation](../../../specs/studio-mpa-creation/README.md), [provisioning](../../../specs/mpa-runtime-provisioning/README.md). - -## Background, goals and boundaries - -The built-in Studio profile references `IDRoleForArkClawShareAgent` for both Runtime and Worker but never prepares it. A new deployment account can therefore fail during cloud creation. Read-only inspection of the historical role confirmed trust in `vefaas` and `apig` and 12 system policies; historical test/IDrive policies are not copied. - -Prepare the approved role before other resource mutations. This does not make the entire Beijing profile portable: account assertions, images and Worker reference still require valid deployment configuration. General agents, legacy CLI defaults, custom roles, existing trust documents and policy bodies remain outside automatic mutation. No live IAM mutation, commit, push or deployment is authorized by design approval. - -## Scenarios and requirements - -- FR-1: Built-in Studio uses `managed.iam.mode: auto`; explicit managed profiles default to `existing` and skip IAM preparation. Auto mode accepts only the default Runtime/Worker role and a fresh template source; incompatible sources fail before IAM requests. -- FR-2: In the STS-verified deployment account, get or create the default role with unconditional `sts:AssumeRole` trust for `vefaas` and `apig`. Existing trust must contain both and no deny statements; never rewrite it. Validate returned role name and account TRN. -- FR-3: Add missing Global bindings for the 12 agreed system policies and versioned custom policy `VeADKMPARuntimeAccessV1`. Preserve extra bindings. The custom policy contains the 14 approved actions with resource `*`; a conflicting existing document fails without overwrite. -- FR-4: Explicit missing-object responses alone permit creation. Creation races reread after already-exists. Bounded polling handles visibility delay; timeout/cancellation retain resources for retry with the same agent ID. Requests use fresh verified credentials and isolated SDK instances with transport timeouts. -- FR-5: Emit stage `iam_role` and allowlisted errors for permissions, trust/policy conflicts, ownership and verification failure. Never expose raw SDK messages, documents or credentials. Failure stops before PG/network/Runtime preparation. -- FR-6: Add `iam:CreatePolicy` to the Studio execution policy; other necessary IAM actions already exist. Deployment credentials must be permitted to get/create role and policy, list bindings and attach policies. Configuration inspection performs no IAM calls. - -## Permission baseline - -System: `AgentKitSkillsSandboxAccess`, `AgentKitToolAccess`, `LLMShieldProtectSdkAccess`, `Mem0ReadOnlyAccess`, `AgentKitRuntimeAccess`, `AgentKitTosAccess`, `CloudControlReadOnlyAccess`, `TorchlightApiFullAccess`, `IDReadOnlyAccess`, `VikingdbFullAccess`, `APMPlusServerDataExportRolePolicy`, `AgentKitReadOnlyAccess`. - -Custom: `arkclaw:ListResources`, `arkclaw:GetMpaInstanceConf`, `apig:ListGateways`, `apig:GetGateway`, `apig:CreateGateway`, `apig:CreateIMChannelGateway`, `apig:GetIMChannelGatewayStatus`, `apig:CreateUpstream`, `apig:CreateRoute`, `agentkit:DeleteSession`, `agentkit:PauseSession`, `agentkit:ResumeSession`, `agentkit:SetSessionTtl`, `agentkit:CreateSessionSnapshot`. - -This is the user-approved compatibility baseline, not a claim of least privilege. ArkClaw actions are compatibility permissions. IAM resources are account-wide, independent of deployment region. - -## Design, tasks and affected files - -- T-1 (FR-1–6): Review this bilingual design and update both component contracts before implementation. -- T-2 (FR-1–4): Add failing isolated tests; implement `managed/iam.py` and configuration/profile integration. Additive reconciliation plus readback was chosen over cloning historical test policies or overwriting user trust. Poll for at most 120 seconds; each SDK request has 5-second connect/read timeouts. Cancellation stops new operations; an in-flight synchronous SDK call may finish within its transport limit. -- T-3 (FR-5–6): Integrate early in `managed/service.py`; map safe errors through `runner.py`/`tasks.py`; update diagnostics, bilingual UI resources and `frontend_deploy_policy.py`. -- T-4: Update bilingual managed usage docs, targeted Python and frontend tests; build release assets and verify browser behavior with isolated mock responses. - -No new persistent tables or public request fields are needed. Existing IAM state is the recovery record. Policy and role creation are idempotent by fixed names; same-account concurrent creation converges by readback. A cancellation does not roll back shared permissions. Unsupported trust shapes fail conservatively. - -## Acceptance and verification - -AC-1: New role creation binds all 12 system policies plus the custom policy and verifies readback. AC-2: Reuse, missing attachments, races and delayed visibility converge without removing extras. AC-3: Permission failures, wrong account, unsafe trust and policy conflicts fail before unrelated mutations and produce safe errors. AC-4: Existing-mode profiles and general agents retain their behavior. AC-5: UI renders the role stage/error in both languages; built assets match sources. - -Commands: `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_frontend_deploy_iam.py`; changed-file Ruff/Pyright; `npm --prefix frontend test`; `npm --prefix frontend run build`; `npm --prefix frontend run test:webui-assets`; `npm --prefix frontend run check:i18n`; regression `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`. Live new-account IAM smoke is not_run unless separately authorized. Browser checks must not touch real IAM/user state. - -## Risks and review record - -System policies can be unavailable in an account or require product enablement; fail visibly and retry after administrator repair. Existing trust restrictions are not automatically widened. Broad baseline permissions are intentional and documented; no real cloud snapshot is checked in. Shared role changes affect consumers of that role. SDK singleton isolation and cancellation require regression coverage. - -Direct design review (review-spec unavailable): scope, additive semantics, bounded retries, credential rotation, no secret output, legacy compatibility, tests and bilingual equivalence reviewed; no unresolved design blockers. User approved the permission additions and implementation with “嗯,帮我改下,”. Approval precedes production/test edits. Existing `frontend/package-lock.json` changes belong to the user and are preserved. - -## Implementation review and verification results - -Tested scope: uncommitted `mpa-runtime-iam` feature on `feat/test-main`, base `ef0ad519`, 2026-10-09. T-1–4 and AC-1–5 are covered by isolated tests and browser checks; live new-account readiness remains unverified. - -Implementation review corrected the provider binding shape to `PolicyScope[].PolicyScopeType: Global` and the official creation collision code to `PolicyAlreadyExist`. `PolicyAttachConflict` requires readback, never assumed success. Sources: [official IAM errors](https://docs.volcengine.com/docs/IAM/ErrorCodeList?lang=en) and [official SDK binding structure](https://pkg.go.dev/github.com/volcengine/volcengine-go-sdk@v1.2.45/service/iam#AttachedPolicyMetadataForListAttachedRolePoliciesOutput). New regression fixtures first failed for missing implementation and subsequently for the real collision/conflict codes; corrected implementation passes. Permission errors never authorize creation. Singleton isolation, fresh credentials, timeout/cancellation, trust/account conflicts, project-only bindings, retries and safe child errors were reviewed. No new blockers remain in the feature scope. - -| Check | Outcome | Evidence / limits | -| --- | --- | --- | -| `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_frontend_deploy_iam.py -q` | pass | Final correction: 474 passed; no real cloud operations. | -| `uvx --from ruff==0.11.12 ruff check ` and format | pass | Version matches repository hook; temporary tool environment. | -| `uvx pyright --pythonpath .venv/bin/python ` | pass | 0 errors / warnings; repository interpreter. | -| `npm --prefix frontend test` | pass | 1,377 Node tests and 48 Vitest tests. | -| `npm --prefix frontend run build` | pass | TypeScript and both bundles built; existing chunk-size/deprecation warnings only. | -| `npm --prefix frontend run test:webui-assets` | pass | 113 packaged files, 350 references. Hash-renamed dependent chunks are generated assets. | -| `npm --prefix frontend run check:i18n` | pass | 2 locales, 21 namespaces consistent. | -| Isolated real Chrome browser (`node /tmp/mpa-iam-browser.cjs`) | pass | Chinese/English three-step flow, readonly ID, IAM error/stage, Enter-key same-ID retry, cancellation, 375px layout; all service responses mocked. Temporary fixture/server removed. First fixture load caused a Vite dependency optimization reload; warmed final runs passed without page errors. | -| `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` | fail | Earlier iteration: 6,240 passed, 7 failed, 19 skipped, 2 xfailed, 2 collection errors. Six Harness failures lack `llama_index`; two self-host sandbox collection errors lack `anthropic`. One upload retry test captured other background-thread sleeps; isolated rerun passed (1 test). These paths have no feature diff. Final IAM race correction was validated with the 474-test affected suite rather than repeating dependency-blocked unrelated tests. | -| `uv run --extra dev pre-commit run --files ` | pass | Ruff/check/format and gitleaks hooks passed; YAML hook skipped (no YAML files). Full `--all-files` is not_run because no commit is requested. | -| `git diff --check`; bilingual links/identifiers/permission baseline | pass | Paired designs/contracts present, links resolve and identifiers align. | -| Live IAM / real new-account creation | not_run | Separate cloud mutation authorization and isolated account setup required. | -| Codex/PI smoke, sidecar coverage, IME/empty-flow changes | not_applicable | No runtime/sidecar/input/empty-state contracts changed. | - -Local environment lacked Ruff/Pyright; use of temporary tools is recorded here, not a machine-global configuration change. Existing package-lock modifications were preserved. No commit, push or deployment was performed. Existing hosted Studio execution policies need the added `iam:CreatePolicy` permission before this feature can prepare roles; updates are not deployed by this change. The built-in profile still asserts its configured account and uses configured image/reference resources; changing accounts requires updating those settings independently. diff --git a/prd-spec/features/mpa-runtime-iam/2026-10-09-managed-runtime-role.zh.md b/prd-spec/features/mpa-runtime-iam/2026-10-09-managed-runtime-role.zh.md deleted file mode 100644 index d8ae210b4..000000000 --- a/prd-spec/features/mpa-runtime-iam/2026-10-09-managed-runtime-role.zh.md +++ /dev/null @@ -1,74 +0,0 @@ -# 自动准备 MPA 运行角色 - -[English](2026-10-09-managed-runtime-role.md) - -变更 ID:`mpa-runtime-iam`;创建/修订:2026-10-09;状态:已实现,隔离验证通过,真实 smoke 未验证。 -契约:[Studio 创建](../../../specs/studio-mpa-creation/README.zh.md)、[资源准备](../../../specs/mpa-runtime-provisioning/README.zh.md)。 - -## 背景、目标与边界 - -内置 Studio 配置为 Runtime 和 Worker 指定 `IDRoleForArkClawShareAgent`,但没有准备角色,新部署账号可能因此创建失败。历史角色的只读核对确认了 `vefaas`、`apig` 信任及 12 个系统策略;不复制历史测试/IDrive 策略。 - -在其他资源变更之前准备已批准角色。这不代表整个北京配置已支持任意账号:账号断言、镜像、Worker 参考模板仍须配置有效。通用智能体、旧 CLI 默认行为、自定义角色、已有信任文档和策略内容不属于自动改写范围。设计批准不授权真实 IAM 变更、提交、推送或部署。 - -## 场景与要求 - -- FR-1:内置 Studio 使用 `managed.iam.mode: auto`;显式 managed 配置默认 `existing`,跳过 IAM 准备。auto 只接受默认 Runtime/Worker 角色及全新模板来源;不兼容来源在 IAM 请求之前失败。 -- FR-2:在 STS 核验的部署账号下查询或创建默认角色,无条件信任 `vefaas` 和 `apig` 的 `sts:AssumeRole`。已有信任必须包含两者且没有拒绝语句;不重写。校验返回角色名及账号 TRN。 -- FR-3:补齐 12 个约定系统策略及版本化自定义策略 `VeADKMPARuntimeAccessV1` 的 Global 绑定,保留额外绑定。自定义策略包含 14 个已批准动作,资源为 `*`;已有内容冲突则失败,不覆盖。 -- FR-4:仅明确对象不存在响应允许创建;创建竞态的 already-exists 响应后重新读取。有限轮询处理可见性延迟;超时/取消保留资源,同一智能体 ID 可重试。每次请求使用新核验凭据、独立 SDK 实例和传输超时。 -- FR-5:输出阶段 `iam_role` 及权限、信任/策略冲突、属主、核验失败等白名单错误。禁止输出原始 SDK 文本、文档、凭据。失败后不准备 PG/网络/Runtime。 -- FR-6:Studio 执行策略增加 `iam:CreatePolicy`,其他所需 IAM 动作已存在。部署凭据须能查询/创建角色和策略、查询绑定、绑定策略。配置检查不调用 IAM。 - -## 权限基线 - -系统策略:`AgentKitSkillsSandboxAccess`、`AgentKitToolAccess`、`LLMShieldProtectSdkAccess`、`Mem0ReadOnlyAccess`、`AgentKitRuntimeAccess`、`AgentKitTosAccess`、`CloudControlReadOnlyAccess`、`TorchlightApiFullAccess`、`IDReadOnlyAccess`、`VikingdbFullAccess`、`APMPlusServerDataExportRolePolicy`、`AgentKitReadOnlyAccess`。 - -自定义动作:`arkclaw:ListResources`、`arkclaw:GetMpaInstanceConf`、`apig:ListGateways`、`apig:GetGateway`、`apig:CreateGateway`、`apig:CreateIMChannelGateway`、`apig:GetIMChannelGatewayStatus`、`apig:CreateUpstream`、`apig:CreateRoute`、`agentkit:DeleteSession`、`agentkit:PauseSession`、`agentkit:ResumeSession`、`agentkit:SetSessionTtl`、`agentkit:CreateSessionSnapshot`。 - -这是用户批准的兼容基线,不宣称最小权限。ArkClaw 动作为兼容权限。IAM 资源为账号级,不随部署地域隔离。 - -## 设计、任务及影响文件 - -- T-1(FR-1–6):实施前评审双语设计并更新两个组件契约。 -- T-2(FR-1–4):先添加隔离的失败测试;实现 `managed/iam.py` 及配置/profile 接入。采用增量补齐与回读,而非复制历史测试策略或覆盖用户信任。最多轮询 120 秒,每次 SDK 请求连接/读取超时各 5 秒。取消后停止新操作;进行中的同步 SDK 调用可能在传输时限内完成。 -- T-3(FR-5–6):在 `managed/service.py` 提前接入,通过 `runner.py`/`tasks.py` 传递安全错误,更新诊断、双语 UI 资源及 `frontend_deploy_policy.py`。 -- T-4:更新双语使用说明、针对性 Python/前端测试;构建发布资源,用隔离模拟响应验证浏览器。 - -不增加持久表或公开请求字段,已有 IAM 状态即恢复记录。固定名称保证角色/策略创建幂等;同账号并发创建通过回读收敛。取消不回滚共享权限。不支持的信任格式保守失败。 - -## 验收与验证 - -AC-1:新建角色绑定全部 12 个系统策略及自定义策略,回读核验。AC-2:复用、缺少绑定、竞态及延迟可见均可收敛,不移除额外绑定。AC-3:权限错误、账号错误、不安全信任及策略冲突在其他资源变更前失败,返回安全错误。AC-4:existing 配置及通用智能体保持原行为。AC-5:双语 UI 显示角色阶段/错误,构建资源与源码一致。 - -命令:`uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_frontend_deploy_iam.py`;变更文件 Ruff/Pyright;`npm --prefix frontend test`;`npm --prefix frontend run build`;`npm --prefix frontend run test:webui-assets`;`npm --prefix frontend run check:i18n`;回归 `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`。真实新账号 IAM smoke 在单独授权之前为 not_run。浏览器验证不触碰真实 IAM/用户状态。 - -## 风险与评审记录 - -某些系统策略可能在账号中不可用或需要开通产品;明确失败,管理员修复后重试。不自动放宽已有信任限制。广泛权限基线属有意选择并已记录,不提交真实云端快照。共享角色变更会影响使用该角色的其他服务。SDK 单例隔离和取消需要回归覆盖。 - -直接设计评审(review-spec 不可用):已检查范围、增量语义、有限重试、凭据刷新、不输出密钥、旧行为兼容、测试及双语等价,无未解决设计阻塞。用户已批准权限增加,并以“嗯,帮我改下,”授权实施,批准先于生产/测试修改。保留用户已有 `frontend/package-lock.json` 修改。 - -## 实施评审与验证结果 - -验证范围:`feat/test-main` 上未提交的 `mpa-runtime-iam` 功能,基线 `ef0ad519`,日期 2026-10-09。T-1–4、AC-1–5 已由隔离测试及浏览器检查覆盖,真实新账号就绪仍未验证。 - -实施评审修正了云端绑定格式 `PolicyScope[].PolicyScopeType: Global`,以及官方创建冲突码 `PolicyAlreadyExist`。`PolicyAttachConflict` 必须回读,不能视为已成功。来源:[官方 IAM 错误码](https://docs.volcengine.com/docs/IAM/ErrorCodeList?lang=en)、[官方 SDK 绑定结构](https://pkg.go.dev/github.com/volcengine/volcengine-go-sdk@v1.2.45/service/iam#AttachedPolicyMetadataForListAttachedRolePoliciesOutput)。新增测试先因未实现失败,随后因真实冲突码失败,修正实现后通过。权限错误不允许创建。已评审单例隔离、凭据刷新、超时/取消、信任/账号冲突、项目级绑定、重试和安全子进程错误。本功能范围内无剩余阻塞问题。 - -| 检查 | 结果 | 证据 / 限制 | -| --- | --- | --- | -| `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_frontend_deploy_iam.py -q` | pass | 最终修正后 474 项通过,不调用真实云端。 | -| `uvx --from ruff==0.11.12 ruff check ` 及 format | pass | 版本与仓库钩子一致,使用临时工具环境。 | -| `uvx pyright --pythonpath .venv/bin/python ` | pass | 0 错误 / 警告,使用仓库解释器。 | -| `npm --prefix frontend test` | pass | 1,377 项 Node 测试及 48 项 Vitest 测试。 | -| `npm --prefix frontend run build` | pass | TypeScript 及两个 bundle 构建成功,仅有已有体积/弃用警告。 | -| `npm --prefix frontend run test:webui-assets` | pass | 113 个文件,350 个引用。依赖 chunk 的哈希重命名属于生成资源。 | -| `npm --prefix frontend run check:i18n` | pass | 2 种语言、21 个命名空间一致。 | -| 隔离真实 Chrome 浏览器(`node /tmp/mpa-iam-browser.cjs`) | pass | 中英文三步流程、只读 ID、IAM 错误/阶段、Enter 键同 ID 重试、取消、375px 布局,全部服务响应为模拟。临时页面/服务已清理。测试页面首次加载触发 Vite 依赖优化重载;预热后的最终运行无页面错误。 | -| `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` | fail | 较早实施版本:6,240 通过、7 失败、19 跳过、2 xfailed、2 收集错误。6 项 Harness 失败缺少 `llama_index`,2 项自托管沙箱收集错误缺少 `anthropic`。1 项上传重试测试收集到了其他后台线程的 sleep,单独重跑通过(1 项)。这些路径无本功能修改。最终 IAM 竞态修正通过 474 项相关测试验证,未重复受缺失依赖阻塞的无关测试。 | -| `uv run --extra dev pre-commit run --files ` | pass | Ruff/check/format 和 gitleaks 钩子通过,YAML 钩子跳过(无 YAML 文件)。未请求提交,完整 `--all-files` 为 not_run。 | -| `git diff --check`;双语链接/标识符/权限基线 | pass | 设计/契约成对存在,链接有效,标识符一致。 | -| 真实 IAM / 新账号创建 | not_run | 须单独授权云端变更并准备隔离账号。 | -| Codex/PI smoke、sidecar coverage、IME/空态变更 | not_applicable | 未修改运行时/sidecar/输入/空态契约。 | - -本地缺少 Ruff/Pyright,临时工具使用记录在这里,不修改机器全局配置。保留已有 package-lock 修改。未提交、推送或部署。已有云端 Studio 执行策略须增加 `iam:CreatePolicy` 后才能准备角色,本次不会部署该更新。内置配置仍断言配置账号并使用配置的镜像/参考资源,更换账号须独立调整这些设置。 diff --git a/prd-spec/features/mpa-serverless-pg/2026-09-21-deployment-account-pg.md b/prd-spec/features/mpa-serverless-pg/2026-09-21-deployment-account-pg.md deleted file mode 100644 index f784bb584..000000000 --- a/prd-spec/features/mpa-serverless-pg/2026-09-21-deployment-account-pg.md +++ /dev/null @@ -1,89 +0,0 @@ -# Automatic Serverless PostgreSQL for managed MPA creation - -[中文版](2026-09-21-deployment-account-pg.zh.md) - -> Superseded on 2026-09-23 for the current creation flow by [two manually prepared PG Workspaces](../mpa-space-scoped-resources/2026-09-23-two-pg-workspaces.md): one management Workspace and one shared business Workspace with per-agent databases. The automatic/per-agent Workspace design below is historical and is not being implemented. - -## Metadata - -- Change ID: `mpa-serverless-pg` -- Created/revised: 2026-09-21 -- Status: `draft`; implementation has not started. -- Component: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md). -- Predecessor: [managed creation](../mpa-agent-oneclick-provision/2026-09-20-studio-mpa-creation.md). -- User-approved intent: automatically create PG under the account owning VeADK deployment credentials. The user asked to follow ArkClaw for resource granularity; source inspection establishes one business PG Workspace per agent. The detailed VeADK registry adaptation remains under review. - -## Background and evidence - -`managed/config.py:load_profile` currently requires administrator and shared-registry PostgreSQL URLs. `managed/service.py:provision` initializes that registry before network/APIG creation. `managed/database.py` creates a per-agent database on an existing instance; it does not provision cloud PostgreSQL. Therefore adding a cloud call after registry initialization cannot support an account without PG. - -The locally inspected ArkClaw implementation calls AIDAP `CreateWorkspace`, waits for Workspace/main-branch readiness, resolves compute/account/database/endpoint and calls `DescribeDBAccountConnection`. Its `CreateWorkspaceReq` exposes no ClientToken. ArkClaw uses another resource account through STS; VeADK must use its own refreshed, account-verified deployment credentials. Go types are implementation evidence, not proof that this user's account has service access or that every SDK/provider version is compatible. - -## Goals, non-goals and scenarios - -Goal: a configured fresh account can start managed MPA creation without entering PG credentials. Preserve existing-PG profiles and the current CLI/Studio creation entry points. - -Selected business-resource granularity: one VeADK-managed PG Workspace per agent, following ArkClaw. Workspace identity is verified deployment account + region + stable agent ID; only the same agent may reuse it on retry. A different agent receives a separate Workspace, even within the same account and region. Reuse requires matching ownership and recorded creation identity; never pick an arbitrary existing PG. The earlier account-region shared business Workspace proposal is withdrawn. - -Non-goals: copying ArkClaw's tenant/resource-account delegation, Debug PG branches, automatic IAM policy/service enrollment, traditional RDS provisioning, automatic deletion/migration of existing PG, multi-host independent bootstrap coordinators, or a live cloud deployment during implementation. - -Scenarios: - -1. With automatic mode and no managed PG, create it under verified deployment account A, initialize databases, then deploy the agent under A. -2. Retrying the same agent reuses its registered Workspace; a second agent gets a different business Workspace. Both use the independently managed shared registry. -3. An existing-PG profile keeps its current connection and permission behavior. -4. Access denied, account change, unrelated name collision, invalid response, deadline or uncertain create outcome cannot silently continue or allocate another Workspace. - -## Requirements and design - -- **FR-1 / account:** derive the account from the existing credential verification path before cloud mutation. PG uses the same refreshed credential loader and region as Runtime/network/APIG; do not introduce resource-account assumption or browser-supplied account/AK/SK. Credential rotation across accounts fails. Verify Workspace account/region/ownership before reuse. -- **FR-2 / configuration:** add `managed.postgres.mode: existing | serverless`, default `existing`. Existing mode retains required PG/admin/registry parameters. Explicit `serverless` mode permits their omission and treats the discovered connection as authoritative for managed database fields, including reference/template sources. Reject explicit conflicting PG overrides instead of silently connecting to another instance. Server-only project, engine, compute and endpoint/network settings must be validated against the supported provider contract. Add safe `postgresMode` and a localized resource-plan description to config/UI; no database URLs/passwords. Existing clients may ignore the added field. The web request cannot override cloud account or PostgreSQL credentials. -- **FR-3 / bootstrap:** before connecting to the PG registry, use a durable local SQLite bootstrap store under the existing private state directory. Key business bootstrap by verified account, region and agent ID; key a separately managed registry bootstrap by verified account, region and explicit registry purpose. Persist only creation identity, owner, configuration hash, lifecycle state and provider resource IDs. Mode 0600, no credentials. Use transactional ownership/serialization across CLI and Studio processes sharing the same store. Persist intent before CreateWorkspace and ID immediately after response. A single bootstrap coordinator/state store per account-region is required; independent hosts are outside this version's concurrency guarantee. Bootstrap the independent shared registry first, then create or recover the agent-specific business Workspace. Inject its connection only into that agent’s business database path; the shared registry URL continues to reference the independent management database. Existing per-agent business database naming can remain inside its dedicated Workspace, subject to provider SQL permissions. -- **FR-4 / recovery:** resolve all discovery pages; reject malformed/repeated pagination, multiple owned candidates and ownership conflicts. A create request with an uncertain outcome must reconcile its persisted unique marker through discovery. Because ClientToken support is not established, never blindly retry an uncertain CreateWorkspace or interpret eventual absence as permission to create again. Unrecoverable ambiguity yields an actionable redacted recovery error. A recorded missing/deleted ready Workspace must not trigger a replacement. Pending configuration changes fail explicitly. Preserve original account, mode and bootstrap configuration identity on task retry. -- **FR-5 / connectivity and credentials:** use bounded signed requests to an approved provider endpoint, with transport cleanup and sanitized errors. Resolve only the intended branch/compute/account/database/endpoint. Enforce TLS and verify Studio SQL connectivity, CREATEDB/owner rights and required extensions/initialization before proceeding. Define network/public-access settings explicitly; do not add permissive allowlists or claim a new VPC reaches a private PG automatically. Retrieved credentials stay server-side in memory or the existing required runtime secret configuration, never task JSON, SQLite bootstrap state, browser, logs or checked-in YAML. Redact provider connection response errors. -- **FR-6 / lifecycle:** add observable `postgres_creating`, `postgres_waiting`, `postgres_initializing` stages through the runner allowlist, task storage and both UI locales. Use task deadline plus per-request timeout and bounded polling. Cancellation stops local work and reaps the runner; in-flight cloud completion may still occur. Preserve PG resources and intent on cancellation/failure for reconciliation; no automatic delete. Authentication, failure and empty states remain distinct. -- **FR-7 / compatibility:** old YAML, `managed.version: 1`, existing task rows and legacy `veadk mpa create` retain behavior. `veadk mpa provision --dry-run` remains local and shows a safe PG plan without network or SQL calls. No implicit switch to automatic mode after an existing PG connection failure. Existing agents are not repointed or redeployed. Keep prepared account resources reusable through the existing registry. - -Implementation sequence: local config validation → deployment identity → independent shared-registry preparation → agent-scoped durable PG bootstrap/discovery → create if safely absent → wait/resolve business connection → SQL permissions → existing network/APIG/worker/business-database/Skill Space/Runtime flow. Runtime model/image settings must be validated before billable mutations where possible; database permission checks necessarily follow new PG creation. The created PG remains for recovery if later checks fail. - -## Contract impact and affected files - -Update both `specs/studio-mpa-creation/README.*`: CON-1 configuration, CON-2 preparation, CON-3 identity, CON-6 stages, CON-7 cancellation, CON-8 persistence/security, CON-9 UI; add the PG bootstrap/reconciliation invariant. Preserve legacy provisioning contracts; SDK Agent/Runner, harness and channel protocols are unaffected. - -Affected implementation: `veadk/integrations/mpa/managed/{config,service,runner,tasks,diagnostics}.py`, new PG cloud/bootstrap modules; `frontend/server/mpa_creation.py`, `frontend/src/adk/mpaCreation.ts`, creation dialog and both locales. Add focused tests under `tests/integrations/mpa_managed/` and update frontend creation tests. Synchronize managed READMEs, example YAML, CLI help as needed, and generated `veadk/webui/` assets. Do not edit the user's two unrelated pending type-verification documents or enable/change the ignored live profile without a separate concrete operational decision. - -## Tasks and acceptance - -| Task | Requirement | Acceptance | -| --- | --- | --- | -| T-1 | FR-1–7 | AC-1: record ArkClaw-aligned per-agent granularity; resolve independent registry bootstrap and complete bilingual contract/design review; validate provider request/response schemas before production edits. | -| T-2 | FR-1–5 | AC-2: test-first PG adapter/bootstrap covers account mismatch, rotated credentials, pagination, malformed responses, unknown create result, duplicate processes, ownership collisions, distinct Workspaces for two agents, same-agent retry reuse, registry/business connection separation, restart and missing resources. | -| T-3 | FR-2–7 | AC-3: integrate config/service/task retry snapshot and SQL initialization; existing-mode tests pass; no PG URL needed in automatic mode; no cloud calls in dry-run. | -| T-4 | FR-2,6 | AC-4: localized plan/progress and safe error/cancel/retry states work in the real dialog with isolated APIs. | -| T-5 | FR-1–7 | AC-5: reconcile paired docs/contracts, rebuild assets, run required checks, distinguish simulated verification from actual provider evidence. | - -## Verification plan - -Start with failing contract/regression tests. Run `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py`, changed-file Ruff/Pyright, `npm --prefix frontend test`, `npm --prefix frontend run build`, `npm --prefix frontend run test:webui-assets`, `npm --prefix frontend run check:i18n`. Run the default two-worker non-smoke Python regression for the shared configuration/orchestration change. Before any authorized commit, fetch/rebase onto the intended remote base and run `uv run --extra dev pre-commit run --all-files` plus unit tests. Real-browser checks cover normal/loading/error/cancellation/retry/keyboard/narrow views with fake APIs; no real cloud allocation. A separately authorized provider smoke must verify actual account ownership, service access, SQL permissions and connectivity before claiming live readiness. - -## Risks, open decisions and review record - -- Granularity resolved by the user’s instruction to follow ArkClaw: one business Workspace per agent. Remaining design decision is the independent management registry described below; do not reopen business-resource granularity as an arbitrary user choice. -- Unverified live assumptions: current account/region AIDAP access, engine/compute limits, default database account CREATEDB privileges and returned endpoint connectivity. Fail explicitly where unsupported; do not fabricate successful provisioning or relax checks. -- No verified provider create idempotency token: uncertain creates may require operator reconciliation. A local single-coordinator guarantee does not claim distributed cross-host exactly-once behavior. -- Creation allocates billable PG resources; deployment authorization is separate from this code request. No cloud writes, private-profile edits, commits or pushes are included in this design phase. -- `review-spec`, `frontend-design` and `ui-ux-pro-max` are unavailable in the local skill catalog. Direct review checks ownership, compatibility, bootstrap cycle, cancellation, credentials, testability and bilingual equivalence. No new visual layout is planned; reuse the current dialog under `frontend/SPEC.md`. -- 2026-09-21, base `3bbd260a`: code/contract inspection **pass**; implementation/runtime/browser/cloud verification **not_run** (draft design only). Component specs carry a clearly marked proposal for the selected granularity; active behavior remains unchanged until implementation. Detailed design approval remains pending. - -## ArkClaw reference findings and registry adaptation (2026-09-21) - -Verified reference paths in `arkclaw-team`: - -- `pkg/service/multiplayer_agent/create.go:253` allocates an MPA instance identity and stores its own resource fields. -- `pkg/service/multiplayer_agent/create_workflow_executor.go:729` reads `PgWorkspaceID` from that specific instance, calls `CreateWorkspace` only if absent, and persists the result on that instance. `create_workflow.go:334` derives the resource name from its ID. -- `create_workflow_executor.go:775` adds a Debug branch inside the same Workspace; `:831` resolves both connections. This confirms separate Workspaces per agent, not separate Workspaces for formal/debug environments. Debug provisioning is recorded as reference behavior and remains outside this change’s existing single-Runtime scope. -- `pkg/model/model.go:22` initializes ArkClaw’s management database from server configuration independently of the per-agent PG. Its database ownership boundary cannot be replaced with the first created agent’s Workspace. - -VeADK’s `SharedAPIGRegistry` explicitly requires every provisioner to use the same PostgreSQL registry, and the Runtime receives `SHARED_APIG_DATABASE_URL`. Proposed adaptation: reuse a configured shared registry; for a fresh account, an explicitly enabled automatic registry mode bootstraps a separate management PG Workspace once per account-region. The business Workspaces remain one per agent and never host the shared registry. Reuse scope, tags, durable keys and plan text distinguish `registry` from `agent` resources. Missing registry configuration must not silently allocate extra PG. - -This adaptation needs detailed approval because it changes resource count: fully automatic first creation would prepare one management Workspace plus one business Workspace; subsequent agents add only their own business Workspace. Reusing a configured registry creates no additional management Workspace. This is a VeADK adaptation to its existing shared-APIG contract, not a claim that ArkClaw creates this extra Workspace per account. Do not introduce it as an invisible default or weaken the registry contract. No code, tests, cloud resources, local credentials or existing private YAML changed during this reference review. diff --git a/prd-spec/features/mpa-serverless-pg/2026-09-21-deployment-account-pg.zh.md b/prd-spec/features/mpa-serverless-pg/2026-09-21-deployment-account-pg.zh.md deleted file mode 100644 index 098e6c731..000000000 --- a/prd-spec/features/mpa-serverless-pg/2026-09-21-deployment-account-pg.zh.md +++ /dev/null @@ -1,89 +0,0 @@ -# 托管 MPA 创建自动准备 Serverless PostgreSQL - -[English](2026-09-21-deployment-account-pg.md) - -> 2026-09-23 当前创建流程已由[手动预建两个 PG Workspace](../mpa-space-scoped-resources/2026-09-23-two-pg-workspaces.zh.md)方案替代:一个管理 Workspace,另一个共享业务 Workspace 内按智能体分库。下文自动创建/每智能体独立 Workspace 设计仅保留历史记录,本次不实施。 - -## 元信息 - -- Change ID:`mpa-serverless-pg` -- 创建/修订日期:2026-09-21 -- 状态:`draft`,尚未开始实现。 -- 组件:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md)。 -- 前序:[托管创建](../mpa-agent-oneclick-provision/2026-09-20-studio-mpa-creation.zh.md)。 -- 用户已确认的意图:在 VeADK 部署凭据所属账号下自动创建 PG。用户要求参考 ArkClaw 确定粒度;源码已确认每个智能体独立业务 PG Workspace。VeADK 注册库适配的详细设计仍在审查中。 - -## 背景与证据 - -`managed/config.py:load_profile` 当前强制要求管理员和共享注册库的 PostgreSQL URL。`managed/service.py:provision` 在网络/APIG 创建前初始化注册库。`managed/database.py` 仅在已有实例里创建智能体独立业务库,不创建云端 PostgreSQL。因此,在注册库初始化后增加一个云接口调用,无法支持没有 PG 的账号。 - -本地检查的 ArkClaw 实现会调用 AIDAP `CreateWorkspace`,等待 Workspace/主分支就绪,解析计算资源、账号、数据库和端点,再调用 `DescribeDBAccountConnection`。其 `CreateWorkspaceReq` 没有 ClientToken 字段。ArkClaw 通过 STS 使用另一个资源账号;VeADK 必须使用自身可刷新且经过账号核验的部署凭据。Go 类型只证明实现方式,不证明当前用户账号具备服务权限,也不证明所有 SDK/服务版本兼容。 - -## 目标、非目标与场景 - -目标:配置好的新账号无需填写 PG 凭据即可发起托管 MPA 创建。保留已有 PG 配置及当前 CLI/Studio 创建入口。 - -已确定业务资源粒度:参考 ArkClaw,每个智能体独立一个 VeADK 管理的 PG Workspace。Workspace 身份为已核验部署账号 + 地域 + 稳定智能体 ID,仅同一智能体重试时复用。即使属于同账号同地域,不同智能体也创建不同 Workspace。复用必须满足归属标记及已记录创建身份匹配,不随意选择已有 PG。此前账号地域共享业务 Workspace 的建议撤回。 - -非目标:照搬 ArkClaw 的租户/资源账号委托、Debug PG 分支、自动配置 IAM 策略或开通服务、传统 RDS 创建、自动删除/迁移已有 PG、多个独立主机的初始化协调,以及实现期间真实云端部署。 - -场景: - -1. 自动模式下没有受管理的 PG 时,在已核验部署账号 A 下创建、初始化数据库,随后仍在 A 下部署智能体。 -2. 同一智能体重试复用其已登记 Workspace,第二个智能体创建另一个业务 Workspace;两者使用独立管理的共享注册库。 -3. 已有 PG 配置维持现有连接及权限行为。 -4. 权限拒绝、账号变化、无关资源重名、响应不合法、超时或创建结果不确定时,不得静默继续或再分配一个 Workspace。 - -## 需求与设计 - -- **FR-1 / 账号:** 云资源变更前使用现有凭据核验路径获取账号。PG 与 Runtime/网络/APIG 使用同一个可刷新凭据加载器及地域,不引入资源账号扮演或浏览器传入账号/AK/SK。刷新凭据后跨账号则失败。复用前核验 Workspace 的账号、地域及归属。 -- **FR-2 / 配置:** 新增 `managed.postgres.mode: existing | serverless`,默认 `existing`。已有模式保留 PG/管理员/注册库参数要求。显式 `serverless` 模式允许省略这些参数,以发现的连接作为托管数据库字段的权威来源,包括参考 Runtime/模板模式。显式 PG 覆盖项有冲突则拒绝,不静默连接其他实例。服务端项目、引擎、计算规格和端点/网络设置须按支持的服务契约校验。配置/UI 新增安全的 `postgresMode` 和本地化资源计划,不返回数据库 URL/密码。旧客户端可忽略新增字段。网页请求不能覆盖云账号和 PostgreSQL 凭据。 -- **FR-3 / 初始化:** 连接 PG 注册库前,在现有私有状态目录使用持久化 SQLite 初始化记录。业务初始化按已核验账号、地域和智能体 ID 索引;单独管理的注册库初始化按已核验账号、地域和显式注册库用途索引。只保存创建身份、属主、配置哈希、生命周期状态及服务资源 ID。权限 0600,不保存凭据。共享同一状态文件的 CLI/Studio 进程通过事务确认归属并串行协调。CreateWorkspace 前保存意图,响应后立即保存 ID。每个账号地域需要单一初始化协调器/状态存储;独立主机不在本版本并发保证内。先准备独立共享注册库,再创建或恢复该智能体专属业务 Workspace。其连接只注入该智能体业务库路径,共享注册库 URL 继续指向独立管理数据库。满足服务 SQL 权限时,可在独立 Workspace 中保留现有智能体业务数据库命名。 -- **FR-4 / 恢复:** 完整遍历发现接口分页,拒绝无效/重复分页、多个归属匹配候选及归属冲突。创建结果不确定时,必须通过已持久化的唯一标记发现并恢复。尚未确认 ClientToken 支持,因此禁止盲目重试结果不确定的 CreateWorkspace,也不能因为暂未发现资源就允许再次创建。无法恢复的歧义返回可操作且脱敏的恢复错误。已记录就绪的 Workspace 丢失/被删除时不得创建替代资源。未完成任务的配置变化显式失败。任务重试保留原账号、模式和初始化配置身份。 -- **FR-5 / 连通性与凭据:** 对批准的服务端点使用有超时边界的签名请求,清理连接并脱敏错误。只解析目标分支/计算资源/账号/数据库/端点。强制 TLS,在继续之前校验 Studio SQL 连通性、CREATEDB/属主权限及必要扩展/初始化条件。显式定义网络/公网访问设置,不新增宽松白名单,不声称新 VPC 自动能访问私网 PG。获取的凭据只保留在服务端内存或现有必要的运行时密钥配置,不进入任务 JSON、SQLite 初始化状态、浏览器、日志或受版本管理的 YAML。脱敏服务连接响应异常。 -- **FR-6 / 生命周期:** 在 runner 白名单、任务存储及双语 UI 中增加可观察的 `postgres_creating`、`postgres_waiting`、`postgres_initializing` 阶段。使用任务总期限、单请求超时和有界轮询。取消停止本地工作并回收 runner,但在途云请求仍可能完成。取消/失败保留 PG 和创建意图用于恢复,不自动删除。鉴权失败、其他错误和空状态不能混淆。 -- **FR-7 / 兼容:** 旧 YAML、`managed.version: 1`、已有任务行和旧 `veadk mpa create` 行为保持。`veadk mpa provision --dry-run` 仍只做本地操作,展示安全的 PG 计划,不调用网络或 SQL。已有 PG 连接失败时不隐式切换到自动创建。已有智能体不更换数据库、不重新部署。通过现有注册库继续复用已准备的账号资源。 - -实现顺序:本地配置校验 → 部署身份 → 独立共享注册库准备 → 智能体级持久化 PG 初始化/发现 → 确认安全缺失后创建 → 等待/获取业务连接 → SQL 权限 → 原网络/APIG/worker/业务库/Skill Space/Runtime 流程。能够提前校验的 Runtime 模型/镜像参数应在计费资源变更前校验;新 PG 的数据库权限校验必须在 PG 创建后进行。后续校验失败时保留已创建 PG 供恢复。 - -## 契约影响与文件范围 - -更新 `specs/studio-mpa-creation/README.*` 两种语言:CON-1 配置、CON-2 前置准备、CON-3 身份、CON-6 阶段、CON-7 取消、CON-8 持久化/安全、CON-9 UI,并增加 PG 初始化/恢复不变量。旧部署契约保持;SDK Agent/Runner、harness 和渠道协议不受影响。 - -实现涉及 `veadk/integrations/mpa/managed/{config,service,runner,tasks,diagnostics}.py`、新增 PG 云适配/初始化模块;`frontend/server/mpa_creation.py`、`frontend/src/adk/mpaCreation.ts`、创建弹窗和双语文案。在 `tests/integrations/mpa_managed/` 增加定向测试并更新前端创建测试。同步托管创建 README、示例 YAML、必要 CLI 帮助和生成的 `veadk/webui/`。不修改用户原有两份未提交类型验证文档;私有运行配置的启用/修改需另外确认具体操作。 - -## 任务与验收 - -| 任务 | 需求 | 验收 | -| --- | --- | --- | -| T-1 | FR-1–7 | AC-1:记录与 ArkClaw 一致的智能体级粒度;确定独立注册库初始化方案并完成双语契约/设计审查;生产代码修改前核验服务请求/响应 schema。 | -| T-2 | FR-1–5 | AC-2:先写 PG 适配/初始化测试,覆盖账号不符、凭据刷新、分页、无效响应、创建结果未知、重复进程、归属冲突、两个智能体的 Workspace 不同、同智能体重试复用、注册库/业务库连接分离、重启和资源丢失。 | -| T-3 | FR-2–7 | AC-3:接入配置/服务/任务重试快照和 SQL 初始化;已有模式测试通过;自动模式无需预填 PG URL;dry-run 不调用云端。 | -| T-4 | FR-2,6 | AC-4:真实弹窗配合隔离 API 验证双语计划/进度及安全的错误/取消/重试状态。 | -| T-5 | FR-1–7 | AC-5:同步双语文档/契约、重建产物、运行必要检查,区分模拟验证与真实服务证据。 | - -## 验证计划 - -先写失败的契约/回归测试。运行 `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py`、修改文件的 Ruff/Pyright、`npm --prefix frontend test`、`npm --prefix frontend run build`、`npm --prefix frontend run test:webui-assets`、`npm --prefix frontend run check:i18n`。共享配置/编排变更运行默认双 worker 非 smoke Python 回归。任何获授权的提交前获取远端并变基到目标远端基线,运行 `uv run --extra dev pre-commit run --all-files` 和单元测试。真实浏览器通过模拟 API 覆盖正常/加载/错误/取消/重试/键盘/窄窗口,不分配真实云资源。另行授权的服务 smoke 应验证实际账号归属、服务权限、SQL 权限和连通性,之后才能宣称真实部署可用。 - -## 风险、待决项与审查记录 - -- 用户要求参考 ArkClaw,业务粒度已确定为每个智能体独立 Workspace。剩余设计决策是下述独立管理注册库方案,不再让用户任意选择业务资源粒度。 -- 尚未实测:当前账号/地域 AIDAP 权限、引擎/计算规格限制、默认数据库账号 CREATEDB 权限、返回端点连通性。不支持时明确失败,不伪造成功或放宽检查。 -- 尚未确认服务创建幂等 token:结果不确定的创建可能需要人工核对。本地单协调器保证不等于跨主机分布式严格单次创建。 -- 创建会分配计费 PG 资源;实际部署授权与代码修改分开。设计阶段不包含云端写操作、私有配置修改、提交或推送。 -- 本地技能目录没有 `review-spec`、`frontend-design`、`ui-ux-pro-max`。直接审查归属、兼容、初始化循环依赖、取消、凭据、可测试性和双语一致性。不设计新视觉布局,按 `frontend/SPEC.md` 复用现有弹窗。 -- 2026-09-21,基线 `3bbd260a`:代码/契约检查 **pass**;实现/运行时/浏览器/云端验证 **not_run**(仅设计草案)。组件契约以明确的提案标记记录已选粒度;实现前不改变现行行为。详细设计尚待批准。 - -## ArkClaw 参照结论与注册库适配(2026-09-21) - -已核实 `arkclaw-team` 参照路径: - -- `pkg/service/multiplayer_agent/create.go:253` 分配 MPA 实例身份,并保存该实例自己的资源字段。 -- `pkg/service/multiplayer_agent/create_workflow_executor.go:729` 读取该实例的 `PgWorkspaceID`,不存在时调用 `CreateWorkspace`,并将结果保存到该实例。`create_workflow.go:334` 使用实例 ID 推导资源名。 -- `create_workflow_executor.go:775` 在同一 Workspace 内增加 Debug 分支,`:831` 获取两个连接。这证明是每个智能体独立 Workspace,正式/Debug 环境并非分别独立 Workspace。Debug 创建记为参考实现事实,不纳入本次已有单 Runtime 的范围。 -- `pkg/model/model.go:22` 根据服务端配置初始化 ArkClaw 管理数据库,独立于各智能体 PG。不能用第一个智能体的 Workspace 代替这一管理库归属边界。 - -VeADK 的 `SharedAPIGRegistry` 明确要求所有部署器使用同一 PostgreSQL 注册库,并向 Runtime 注入 `SHARED_APIG_DATABASE_URL`。建议适配:已有共享注册库配置则复用;全新账号通过显式启用的自动注册库模式,按账号地域创建一次独立管理 PG Workspace。业务 Workspace 仍每个智能体独立,绝不承载共享注册库。复用范围、标签、持久化键和计划文案区分 `registry` 与 `agent` 资源。缺少注册库配置时不能静默多创建 PG。 - -此适配需要详细确认,因为资源数量发生变化:完全自动模式首次创建会准备一个管理 Workspace 加一个业务 Workspace;后续智能体只增加自身业务 Workspace。配置已有注册库时不额外创建管理 Workspace。这是适配 VeADK 现有共享 APIG 契约的方案,不代表 ArkClaw 每账号也自动创建这个额外 Workspace。不得将其作为不可见默认行为,也不能削弱注册库契约。本次参考审查未修改代码、测试、云资源、本地凭据或已有私有 YAML。 diff --git a/prd-spec/features/mpa-space-scoped-resources/2026-09-23-auto-pg-workspaces.md b/prd-spec/features/mpa-space-scoped-resources/2026-09-23-auto-pg-workspaces.md deleted file mode 100644 index c0e6b1295..000000000 --- a/prd-spec/features/mpa-space-scoped-resources/2026-09-23-auto-pg-workspaces.md +++ /dev/null @@ -1,64 +0,0 @@ -# Automatic management and business PG Workspaces - -[中文版](2026-09-23-auto-pg-workspaces.zh.md) - -Date: 2026-09-23. Status: approved for implementation by the user's request to implement the preceding automatic-PG flow. Owner: [Studio creation contract](../../../specs/studio-mpa-creation/README.md). Supersedes manual preparation as the recommended path in the [two-Workspace design](2026-09-23-two-pg-workspaces.md); retain manual profiles and explicit migration compatibility. - -## Intent and evidence - -Use refreshed VeADK deployment credentials, verified with STS, to prepare one `mpa_admin_workspace/mpa_admin_db` plus one business Workspace (default `mpa_business_workspace`) per account/region/project. Each MPA still owns an independently named business database. The installed `volcenginesdkaidap.AIDAPApi` exposes CreateWorkspace, DescribeWorkspaces/WorkspaceDetail/Branches/Computes/WorkspaceEndpoint/DBAccounts/Databases/DBAccountConnection, matching ArkClaw's inspected source. SDK request fields have no ClientToken; never assume creation retries are idempotent. - -## Requirements and design - -1. `managed.postgres.mode: auto` needs no PG URL/user/password; optional Workspace IDs allow explicit adoption. Legacy/manual profiles keep working. Engine version defaults to PostgreSQL_17; project defaults to default. Business Workspace name can be configured. Config API reports auto mode without cloud calls or credentials; the PG wizard step becomes explanatory, with no editable host/port. OpenViking remains step three. Supplied PG fields are rejected in auto mode; unsubmitted browser drafts clear them, submitted tasks preserve their original payload and fail safely if configuration changes. -2. AIDAP adapter uses SDK signing with current deployment credentials on every request, checks account consistency, and sanitizes provider failures. List all pages with bounded/repeated-page validation. Wait for explicit ready states with cancellation and stage deadlines; reject terminal/unknown statuses, ambiguous branches/compute/accounts/endpoints and incomplete credentials. SQL connections stay server-side/in memory and are injected into the requested Runtime only. -3. Before any PG registry exists, persist nonsecret creation intent, scope/config digest and Workspace IDs in private SQLite (`.adk/mpa-pg-bootstrap.sqlite3`, configurable path). Serialize per account/region/project using an OS file lock, including separate Studio/CLI processes sharing that path. Persist intent before create and ID before readiness waits. Lost create responses reconcile exact names and scope/purpose tags; no second create after an uncertain result, missing previously recorded resource, or ownership/config conflict. Independent deployment hosts must share a coordinator/state location; this release does not claim distributed-lock support. -4. Find/reuse the unique matching Workspace in the verified account/region/project; validate name, engine and scope, and reject conflicting purpose tags. Explicit IDs are validated independently. New resources get stable ownership tags. Prepare management first, then business, derive management DB URL and initialize `mpa_admin_db` using the existing safe SQL initializer. Keep business DB names and account/region APIG sharing unchanged. Do not create one Workspace per MPA. Adopting existing business data requires an explicit business Workspace ID; check the legacy business endpoint before switching. -5. Existing registry endpoints must not silently switch to an empty management DB. Automatic provisioning rejects any configured legacy shared URL by default. An explicit `managed.postgres.legacy-urls: ignore` selection makes new creations start from fresh admin and business Workspaces without reading either old PG URL; existing agents and databases remain untouched and are not imported. The alternative `init-admin-db --source-url-env ...` path copies existing registry records using the stopped-writer migration contract. No automatic live migration, deletion, public-access changes, password resets or paid-cloud test executions in this coding task. -6. New safe stages report management Workspace, business Workspace and management DB initialization. Maintenance credentials never reach the browser/task database/log output. Runtime settings inherited from a reference are overridden with the resolved business connection. Shared Workspace IDs remain bound in agent deployment records. - -## Tasks and acceptance - -- T1: configuration, API contract and wizard; test auto/manual modes, no PG secrets required, optional adoption, draft/submission compatibility and localization. -- T2: SDK adapter and connection resolution; test request/account refresh, pagination, readiness, selection and redaction. -- T3: durable bootstrap and SQL integration; test reuse for two agents, concurrency, cancellation, uncertain create, missing IDs, mismatched scope, legacy migration guard and unchanged business identity. -- T4: service/CLI integration, bilingual docs, generated assets; run managed Python/CLI tests, Ruff/Pyright, frontend tests/build/i18n/assets and browser checks. Full regression may reuse earlier baseline evidence if only known dependency failures recur. Live cloud/SQL verification remains explicitly not_run without an isolated authorized target. - -## Review and risks - -Direct review (review-spec unavailable): user-approved topology is preserved; auto mode is explicit to avoid unexpectedly allocating resources for old configs. SDK credentials replace hand-filled PG credentials, not database authentication. Missing service access/CREATEDB/network connectivity remains a failure. File locks protect one shared state directory, not multiple independent hosts. Pending work survives process termination through persisted intent. Cloud calls run with request deadlines; late responses cannot cause a blind retry. No unresolved design blocker for offline implementation; actual provider readiness values, account privileges and endpoint reachability require live verification. No commit/push/deployment authorized. - -## Verification and delivery record — 2026-09-23 - -Scope: branch `feat/from-main-20260922`, working diff over `821f0f36`, including the already uncommitted three-step wizard and manual two-Workspace work. T1–T4 are implemented. The local private profile was backed up and switched to auto mode; the local Studio process was restarted. Deployment credentials were used for read-only STS/AIDAP checks only. No cloud Workspace creation, live SQL migration, Runtime rollout, commit or push occurred. - -| Check | Status | Evidence | -| --- | --- | --- | -| Managed Python and provisioning integration | pass | `UV_CACHE_DIR=/tmp/veadk-uv-cache uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py -q`: 344 passed. Covers SDK request shape, refreshed credentials, discovery, tags/ownership, idempotent retries, cancellation, timeout, explicit legacy-URL ignore, default migration guard, service and CLI integration. | -| CLI regression | pass | `UV_CACHE_DIR=/tmp/veadk-uv-cache uv run --extra dev pytest tests/cli/test_cli_mpa.py tests/cli/test_cli_mpa_control.py -q`: 41 passed with local mock-server binding allowed. | -| Static and type checks | blocked | Python compilation and `git diff --check` pass. Ruff and Pyright were clean before the latest config field/test change; rerun is blocked because those executables are absent locally and Ruff is unavailable from the offline cache. | -| Frontend tests and release assets | pass | `npm --prefix frontend test`: 1305 Node + 36 Vitest passed. `npm --prefix frontend run build`, `check:i18n`, `test:webui-assets`: pass; 104 packaged files and 248 internal references validated. | -| Browser | pass | Isolated localhost mock: automatic PG explanation and link, three-step navigation/keyboard, progress stage, cancellation/retry, configuration error, loading and narrow 390px layout observed. No provider calls. | -| Local profile and Studio endpoint | pass | The private YAML is in auto mode with explicit `legacy-urls: ignore`. After restarting Studio, `/web/mpa-creation/config` reports `postgresMode=auto`, empty PG host, `configured=true`, and no migration flag. Existing registry records are left untouched. | -| Live AIDAP/PG and migration | not_run | Read-only STS/AIDAP discovery succeeded; the configured admin and business Workspace names do not yet exist in the deployment account. Workspace allocation and registry/Runtime cutover require a separate coordinated procedure and were not run. | -| Full Python non-smoke regression | not_run | Earlier related uncommitted scope had baseline failures from absent optional dependencies and local-port sandboxing; all affected managed and CLI suites above pass. No new shared SDK dependency or core runtime contract was changed. | - -Remaining operator work: verify AIDAP IAM, endpoint reachability and Workspace creation with a controlled new-agent run. The local profile deliberately starts fresh, so old registry records and old business databases are not visible to new creation tasks; existing agents retain their original connections. Keep the bootstrap state file durable on one coordinator host. Manual mode and explicit migration remain available for installations choosing preservation. - -## Main-history alignment and pre-PR verification — 2026-09-23 - -The user approved preserving the current feature changes on a clean branch from rewritten `origin/main`. Created `feat/mpa-pg-workspaces` at `c7b23a9b` and explicitly ran `git pull --ff-only origin main`; the old `feat/from-main-20260922` remains at `821f0f36`. No old commits were merged into the rewritten history. This operation does not change a component contract. - -Before switching, a private local archive recorded all 125 existing files and 62 deletions, including ignored local configuration. All entries matched byte-for-byte after switching. After rebuilding, source and local configuration still matched the archive; the only additional edits are this bilingual verification record and correction of the two readiness-design contract links. Ignored configuration and the backup are not part of the PR. No cloud resource or running service was changed by the branch operation. - -Verification scope: the preserved feature diff on `c7b23a9b`, after alignment. - -| Check | Status | Evidence | -| --- | --- | --- | -| Targeted Python | pass | `uv run --offline --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/cli/test_mpa_a2a_url.py tests/cli/test_frontend_runtime_proxy.py tests/scripts/test_scan_yaml_secrets.py -q`: 470 passed, 5 warnings. | -| Frontend | pass | `npm --prefix frontend test`: 1305 Node tests and 36 Vitest tests passed. Production build, `check:i18n`, and `test:webui-assets` passed; 104 packaged files and 248 references verified. | -| Commit hooks | pass | `uv run --offline --extra dev pre-commit run --all-files` and `pre-commit run --files ` passed Ruff, formatting, Gitleaks and YAML secret scanning. The latter includes untracked new files. Hooks did not alter Python sources. | -| Types | partial | Pyright passed on 23 focused changed Python files. Full-file checks of the other three existing CLI/proxy files report 57 previously recorded diagnostics; this alignment preserves those files exactly and does not claim all-file type success. | -| Preservation | pass | File hashes/deletions verified against the private archive; `git diff --check` passed. The rewritten main history is the branch base. | -| Browser/cloud rerun | not_run | Branch alignment changes no UI or runtime behavior; previously recorded browser/cloud evidence remains historical. No new live deployment or chat was performed during this verification. | -| Full non-smoke Python regression | fail (environment) | `uv run --offline --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`: 5137 passed, 6 failed, 11 skipped, 2 xfailed and 2 collection errors. All six failures require missing `llama_index`; the two collection errors require missing `anthropic`. These match the earlier dependency limitations; no full-green claim. | diff --git a/prd-spec/features/mpa-space-scoped-resources/2026-09-23-auto-pg-workspaces.zh.md b/prd-spec/features/mpa-space-scoped-resources/2026-09-23-auto-pg-workspaces.zh.md deleted file mode 100644 index 76cebab6e..000000000 --- a/prd-spec/features/mpa-space-scoped-resources/2026-09-23-auto-pg-workspaces.zh.md +++ /dev/null @@ -1,64 +0,0 @@ -# 自动准备管理与业务 PG Workspace - -[English](2026-09-23-auto-pg-workspaces.md) - -日期:2026-09-23。状态:用户要求实施前述自动 PG 流程,已批准实施。归属:[Studio 创建契约](../../../specs/studio-mpa-creation/README.zh.md)。本方案将[两 Workspace 设计](2026-09-23-two-pg-workspaces.zh.md)的推荐路径改为自动准备,同时保留手动配置和显式迁移兼容性。 - -## 意图与证据 - -使用可刷新的 VeADK 部署凭据,经 STS 核验后,为每个账号/地域/项目准备一个 `mpa_admin_workspace/mpa_admin_db` 和一个业务 Workspace(默认 `mpa_business_workspace`)。每个 MPA 继续使用独立命名的业务库。已安装 `volcenginesdkaidap.AIDAPApi` 提供 CreateWorkspace、DescribeWorkspaces/WorkspaceDetail/Branches/Computes/WorkspaceEndpoint/DBAccounts/Databases/DBAccountConnection,与检查过的 ArkClaw 源码对应。SDK 创建请求没有 ClientToken,不能假设重复创建幂等。 - -## 需求与设计 - -1. `managed.postgres.mode: auto` 无需 PG URL/账号/密码;可选 Workspace ID 用于显式复用。旧/手动配置继续可用。引擎默认 PostgreSQL_17,项目默认 default,业务 Workspace 名称可配置。配置 API 无云调用、无凭据,只报告自动模式;创建弹窗 PG 步骤变为说明,不提供主机/端口编辑框,OpenViking 仍为第三步。自动模式拒绝提交 PG 字段;未提交草稿清空旧值,已提交任务保留原载荷,配置变化时安全失败。 -2. AIDAP 适配器每次请求使用当前部署凭据进行 SDK 签名,核验账号一致性,脱敏服务错误。完整遍历分页并限制页数、检测重复页。等待明确就绪状态,支持取消和步骤超时;拒绝终态/未知状态、歧义分支/计算/账号/端点及不完整凭据。SQL 连接仅在服务端内存中处理并注入对应 Runtime。 -3. PG 注册库创建前,在私有 SQLite(`.adk/mpa-pg-bootstrap.sqlite3`,路径可配置)保存无密钥的创建意图、范围/配置摘要和 Workspace ID。使用操作系统文件锁按账号/地域/项目串行化,覆盖共享路径的 Studio/CLI 多进程。调用创建前保存意图,等待就绪前保存 ID。创建响应丢失时按精确名称及范围/用途标签恢复;结果不确定、已登记资源丢失、归属或配置冲突时禁止再次创建。独立部署主机必须共享协调器/状态位置,本版不宣称提供分布式锁。 -4. 在已核验账号/地域/项目中查找并复用唯一匹配的 Workspace,检查名称、引擎和范围,拒绝冲突用途标签。显式 ID 单独核验,新资源打稳定归属标签。先准备管理 Workspace,再准备业务 Workspace,推导管理库 URL 并调用现有安全 SQL 初始化器准备 `mpa_admin_db`。保留业务库命名和账号/地域 APIG 共享规则,不为每个 MPA 创建 Workspace。接管已有业务数据须指定业务 Workspace ID,切换前检查旧业务端点。 -5. 不允许把旧注册库端点静默切到空管理库。自动创建默认拒绝仍配置的旧共享 URL。显式设置 `managed.postgres.legacy-urls: ignore` 后,新建流程从全新的管理与业务 Workspace 开始,不读取两个旧 PG URL;已有智能体和数据库保持原状,也不导入旧记录。另一种方案是通过 `init-admin-db --source-url-env ...` 按停写迁移契约复制旧记录。本编码任务不执行自动线上迁移、删除、公开访问设置变更、密码重置或计费云测试。 -6. 新增安全阶段显示管理 Workspace、业务 Workspace 和管理库初始化进度。维护凭据不进入浏览器、任务数据库或日志。参考 Runtime 的 PG 设置由解析出的业务连接覆盖;智能体部署记录继续固定共享 Workspace ID。 - -## 任务与验收 - -- T1:配置、API 契约、弹窗;测试自动/手动模式、无需 PG 密钥、可选复用、草稿/提交兼容及国际化。 -- T2:SDK 适配和连接解析;测试请求/凭据刷新、分页、就绪、选择及脱敏。 -- T3:持久化初始化与 SQL 接入;测试两个智能体复用、并发、取消、不确定创建、ID 丢失、范围冲突、旧库迁移保护及业务身份不变。 -- T4:服务/CLI 集成、双语文档、生成资源;执行 managed Python/CLI 测试、Ruff/Pyright、前端测试/构建/国际化/资源和浏览器检查。全量回归若仅重现已知依赖问题,可引用此前基线证据。没有隔离且授权的目标时,真实云/SQL 验证明确记为 not_run。 - -## 审查与风险 - -直接审查(review-spec 不可用):保留用户批准的拓扑;显式启用自动模式,避免旧配置意外分配资源。SDK 凭据替代人工填写 PG 凭据,不取代数据库认证。服务权限、CREATEDB 或网络缺失仍明确失败。文件锁保护共享状态目录,不保护互不共享的部署主机。持久化意图支持进程终止后恢复。云调用具有请求超时,晚到响应不能触发盲目重试。离线实施无未决设计阻塞;服务实际就绪值、账号权限及端点可达性需真实验证。未授权提交/推送/部署。 - -## 验证与交付记录 — 2026-09-23 - -范围:分支 `feat/from-main-20260922`、基线 `821f0f36` 上的工作区改动,包含原有未提交的三步弹窗和手动双 Workspace 工作。T1–T4 已实现。已备份本机私有配置并切到自动模式,已重启本机 Studio。部署凭据仅用于只读 STS/AIDAP 核验。未创建线上 Workspace、迁移线上 SQL、切换 Runtime、提交或推送。 - -| 检查 | 状态 | 证据 | -| --- | --- | --- | -| 托管 Python 与部署集成 | pass | `UV_CACHE_DIR=/tmp/veadk-uv-cache uv run --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py -q`:344 通过。覆盖 SDK 请求结构、凭据刷新、发现、标签/归属、幂等重试、显式忽略旧 URL、默认迁移保护及服务/CLI 集成。 | -| CLI 回归 | pass | `UV_CACHE_DIR=/tmp/veadk-uv-cache uv run --extra dev pytest tests/cli/test_cli_mpa.py tests/cli/test_cli_mpa_control.py -q`:允许本地模拟服务端口后 41 通过。 | -| 静态和类型检查 | blocked | Python 编译及 `git diff --check` 通过。新增配置字段/测试之前的 Ruff、Pyright 检查通过;本次重跑因本机缺少可执行文件且离线缓存无 Ruff 而受阻。 | -| 前端测试和发布资源 | pass | `npm --prefix frontend test`:1305 项 Node 和 36 项 Vitest 通过。`npm --prefix frontend run build`、`check:i18n`、`test:webui-assets` 通过;核对 104 个打包文件和 248 个内部引用。 | -| 浏览器 | pass | 本地模拟:观察到自动 PG 说明和链接、三步/键盘跳转、进度阶段、取消/重试、配置错误、加载及 390px 窄屏布局。没有服务商调用。 | -| 本机配置和 Studio 接口 | pass | 私有 YAML 已切到自动模式并显式设置 `legacy-urls: ignore`。重启 Studio 后,`/web/mpa-creation/config` 返回 `postgresMode=auto`、空 PG 主机、`configured=true`、无迁移标志。旧注册库记录保持原状。 | -| 真实 AIDAP/PG 与迁移 | not_run | 只读 STS/AIDAP 发现通过;部署账号下尚无配置的管理员和业务 Workspace 名称。Workspace 分配与注册库/Runtime 切换需另行协调,尚未执行。 | -| 全量 Python 非 smoke 回归 | not_run | 此前相关未提交范围存在可选依赖缺失及本地端口沙箱的基线失败;以上受影响的托管与 CLI 测试已通过。未改动共享 SDK 依赖或核心运行时契约。 | - -后续操作:通过受控的新智能体创建,核验 AIDAP 权限、端点连通性和 Workspace 创建。本机配置明确选择从新库开始,因此旧注册记录及旧业务库不会出现在新建任务中;已有智能体仍使用原连接。引导状态文件须保存在同一协调主机的持久目录。需要保留旧数据的部署仍可选手动模式或显式迁移。 - -## main 历史对齐及 PR 前验证 — 2026-09-23 - -用户已批准将当前功能修改完整保留在基于重写后 `origin/main` 的干净分支。已在 `c7b23a9b` 创建 `feat/mpa-pg-workspaces`,并显式执行 `git pull --ff-only origin main`;旧分支 `feat/from-main-20260922` 仍保留在 `821f0f36`。没有将旧提交合并回重写后的历史。本操作不改变组件契约。 - -切换前的本地私有归档保存了 125 个现存文件及 62 项删除,包括被忽略的本地配置;切换后全部逐字节一致。重新构建后,源码及本地配置仍与备份一致;额外编辑仅包括本双语验证记录,以及两份就绪等待设计中的契约相对链接修正。被忽略的配置和备份不进入 PR。分支操作没有修改云资源或运行中的服务。 - -验证范围:对齐后基于 `c7b23a9b` 保留的本次功能差异。 - -| 检查 | 状态 | 证据 | -| --- | --- | --- | -| 定向 Python | pass | `uv run --offline --extra dev pytest tests/integrations/mpa_managed tests/integrations/test_mpa_provision_env.py tests/cli/test_mpa_a2a_url.py tests/cli/test_frontend_runtime_proxy.py tests/scripts/test_scan_yaml_secrets.py -q`:470 通过,5 条警告。 | -| 前端 | pass | `npm --prefix frontend test`:1305 项 Node 测试及 36 项 Vitest 测试通过。生产构建、`check:i18n`、`test:webui-assets` 通过;验证 104 个打包文件及 248 个引用。 | -| 提交钩子 | pass | `uv run --offline --extra dev pre-commit run --all-files` 和 `pre-commit run --files <全部现存改动路径>` 的 Ruff、格式化、Gitleaks、YAML 密钥扫描通过。后者覆盖未跟踪的新文件。钩子没有修改 Python 源码。 | -| 类型检查 | 部分通过 | 23 个重点改动 Python 文件的 Pyright 通过。另外三个既有 CLI/代理文件的全文件检查仍报告此前记录的 57 项诊断;本次对齐逐字节保留这些文件,不宣称全文件类型检查通过。 | -| 完整性 | pass | 对照私有归档核验文件哈希及删除记录;`git diff --check` 通过。分支基线为重写后的 main 历史。 | -| 浏览器/云端重跑 | not_run | 分支对齐不改变 UI 或运行时行为;此前浏览器/云端验证仍作为历史证据。本轮没有新增真实部署或聊天操作。 | -| 全量非 smoke Python 回归 | fail(环境) | `uv run --offline --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`:5137 通过、6 失败、11 跳过、2 xfailed、2 项收集错误。六项失败均缺少 `llama_index`,两项收集错误缺少 `anthropic`;与此前依赖限制一致,不宣称全量全绿。 | diff --git a/prd-spec/features/mpa-space-scoped-resources/2026-09-23-space-scoped-manual-pg.md b/prd-spec/features/mpa-space-scoped-resources/2026-09-23-space-scoped-manual-pg.md deleted file mode 100644 index 10afc3726..000000000 --- a/prd-spec/features/mpa-space-scoped-resources/2026-09-23-space-scoped-manual-pg.md +++ /dev/null @@ -1,49 +0,0 @@ -# Space-scoped MPA resources and pre-created AIDAP PostgreSQL - -> Superseded by [two PG Workspaces](2026-09-23-two-pg-workspaces.md): one management Workspace and one shared business Workspace with per-agent databases. The per-agent Workspace/Space-selector proposal below is historical. - -[中文版](2026-09-23-space-scoped-manual-pg.zh.md) - -- Change ID: `mpa-space-scoped-resources` -- Date: 2026-09-23 -- Status: proposed; implementation decisions about Space selection and PG credential references are pending. -- Contracts: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), [MPA Runtime provisioning](../../../specs/mpa-runtime-provisioning/README.md). -- Supersedes the automatic business-Workspace path proposed in [automatic PG design](../mpa-serverless-pg/2026-09-21-deployment-account-pg.md) for this creation flow. - -## Background and evidence - -Current VeADK uses `account_id + region` for `mpa_account_network` and `mpa_account_apig`, then creates a logical database per MPA on the administrator PG host. `frontend/server/mpa_creation.py` stores only nonsecret PG host/port and requires that they match one server administrator URL. There is no Claw Space ID in the Studio create request. The current `agentkit-mpa-agent` image also looks up its shared APIG registry by account and region, so changing VeADK alone cannot guarantee Space isolation. - -The locally inspected `arkclaw-team/pkg/service/multiplayer_agent/quota.go` resolves an MPA's `SpaceID` and queries `claw_space_application_conf` for that Space's `apig_instance_id`. Its create workflow stores a PG Workspace ID on each MPA. The reference establishes the intended sharing boundary; this change does not depend on the ArkClaw database or service at runtime. - -## Goals, non-goals and scenarios - -Create or reuse one VPC/subnet and APIG/IM Gateway per verified account, region and logical Space ID. Keep each MPA's Runtime, worker, Skill Space, routes and pre-created AIDAP business PG Workspace independent. A shared Studio registry PG remains deployment-scoped and is not an agent's business Workspace. The PG step must link to the [AIDAP console](https://console.volcengine.com/aidap/region:aidap+cn-beijing/). - -Two MPAs in the same Space reuse the same VPC/APIG but use different business Workspaces. Two Spaces in the same account and region must never be assigned the same managed VPC/APIG just because the account matches. Retrying an MPA uses its original Space and PG Workspace. Existing deployments continue to resolve their registered gateway and database during migration. - -Non-goals: creating or deleting AIDAP PG Workspaces; importing ArkClaw's `claw_space_application_conf`; changing generic Studio agents; live resource creation as part of tests. - -## Requirements and design - -- **FR-1 — Space identity:** Studio creation must choose an existing, server-authorized logical Space. Validate the Space against the deployment account and region before cloud mutation. Persist it with request identity, task and deployment record. Set the Runtime's `CLAW_SPACE_ID` to the selected value, not an account-derived fallback. A retry that changes Space fails. -- **FR-2 — shared resources:** Key network/APIG records, locks, stable names and ownership markers by account, region and Space. Verify explicit VPC/subnet/APIG adoption against the chosen Space. Preserve old account-scoped records for existing agents; do not silently reassign an existing gateway or overwrite a legacy row. -- **FR-3 — runtime contract:** The deployed `agentkit-mpa-agent` must resolve the same Space-scoped gateway. Update its lookup contract and image before enabling this flow; otherwise block creation with an actionable compatibility error. Do not expose registry write credentials to Runtime beyond its existing contract during this change. -- **FR-4 — manual PG:** The operator creates one AIDAP business Workspace per MPA before submitting creation. The server resolves its connection and credentials from an administrator-managed secret reference bound to that MPA; the browser/task payload contains only nonsecret Workspace identity and host/port. Verify the Workspace is reachable and distinct from the registry and other agents' Workspace IDs. Record Workspace ID and host without secrets. Do not create a replacement Workspace on retry or failure. -- **FR-5 — compatibility and failure:** Existing agent records remain pinned to their original network/APIG/PG and are not migrated by opening the dialog. Empty, conflicting, missing, rotated or unreachable resource choices fail before paid cloud mutations where possible. Cancellation preserves resources and binding for same-ID retry. Surface safe errors without URLs, passwords or provider response bodies. -- **FR-6 — UI and docs:** Keep the three-step wizard. Identify the selected Space in basics and the manually created AIDAP Workspace on the PG step. Explain Space-level sharing and per-agent PG. Update English/Chinese copy, operator configuration examples, contracts and generated web assets together. - -## Affected files and tasks - -Expected VeADK areas: `veadk/integrations/mpa/managed/{config,registry,database,network,gateway,service,runtime,runner,tasks}.py`, Studio create API/types/dialog/i18n, tests, operator guide and generated assets. The independent `agentkit-mpa-agent` registry/client contract requires a coordinated change and image release. - -1. Resolve the pending choice of Space catalog/selection and server-side per-agent PG credential references; review bilingual contracts and migration before production changes. -2. Write failing tests for two Spaces in one account, two agents in one Space, PG Workspace uniqueness, retry, legacy records, runtime gateway lookup and safe errors. -3. Implement configuration, API, registry, orchestration and runtime integration without weakening ownership checks. -4. Verify targeted tests, frontend build/browser cases, shared regressions and generated assets. Run a separately authorized isolated cloud smoke only if available. - -## Risks and acceptance - -The current Runtime image can recreate or read an account-scoped gateway; a VeADK-only change is unsafe. The existing shared registry schema has account-region primary keys and needs additive migration or new tables. A manually created Workspace may be private to a different VPC; verify connectivity rather than assuming it. Never store PG passwords in browser storage, task SQLite, deployment records, source or logs. - -Acceptance: same-Space agents share exactly the verified VPC/APIG; different Spaces do not; each new MPA uses its own pre-created PG Workspace; the shared registry remains separate; repeated creation keeps original bindings; old agents remain usable; failures do not silently allocate replacement resources. Verification status on 2026-09-23: AIDAP dialog link test `pass`; all new architecture and live checks `not_run` pending design decisions and implementation. diff --git a/prd-spec/features/mpa-space-scoped-resources/2026-09-23-space-scoped-manual-pg.zh.md b/prd-spec/features/mpa-space-scoped-resources/2026-09-23-space-scoped-manual-pg.zh.md deleted file mode 100644 index 65c543193..000000000 --- a/prd-spec/features/mpa-space-scoped-resources/2026-09-23-space-scoped-manual-pg.zh.md +++ /dev/null @@ -1,49 +0,0 @@ -# 按 Space 共享 MPA 资源并使用预先创建的 AIDAP PostgreSQL - -> 已被[两套 PG Workspace 方案](2026-09-23-two-pg-workspaces.zh.md)取代:一个管理 Workspace、一个按智能体分库的共用业务 Workspace。下文每智能体 Workspace/Space 选择方案仅供历史参考。 - -[English](2026-09-23-space-scoped-manual-pg.md) - -- 变更 ID:`mpa-space-scoped-resources` -- 日期:2026-09-23 -- 状态:提案;Space 选择和 PG 凭据引用方式仍待确定。 -- 契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md)、[MPA Runtime 部署](../../../specs/mpa-runtime-provisioning/README.zh.md)。 -- 对本创建流程,以此方案替代[自动创建 PG 设计](../mpa-serverless-pg/2026-09-21-deployment-account-pg.zh.md)中自动创建业务 Workspace 的路径。 - -## 背景与证据 - -当前 VeADK 以 `account_id + region` 为 `mpa_account_network` 和 `mpa_account_apig` 建立共享记录,并在管理员 PG 主机上为每个 MPA 建一个逻辑数据库。`frontend/server/mpa_creation.py` 只持久化非密钥的 PG 主机/端口,且要求与服务端唯一管理员连接一致。Studio 创建请求没有 Claw Space ID。当前 `agentkit-mpa-agent` 镜像同样按账号和地域查共享 APIG,因此只改 VeADK 无法保证 Space 隔离。 - -已检查本地 `arkclaw-team/pkg/service/multiplayer_agent/quota.go`:它从 MPA 读取 `SpaceID`,再从该 Space 的 `claw_space_application_conf` 取得 `apig_instance_id`。其创建流程为每个 MPA 记录 PG Workspace ID。旧实现用于确认共享边界;新链路运行时不依赖 ArkClaw 的数据库或服务。 - -## 目标、非目标与场景 - -在已核验的账号、地域和逻辑 Space ID 内创建或复用一套 VPC/子网及 APIG/IM Gateway。每个 MPA 的 Runtime、Worker、Skill Space、路由和预先创建的 AIDAP 业务 PG Workspace 保持独立。Studio 共享注册库仍按部署环境共享,不放在单个智能体的业务 Workspace 中。PG 步骤链接到 [AIDAP 控制台](https://console.volcengine.com/aidap/region:aidap+cn-beijing/)。 - -同一 Space 的两个 MPA 复用 VPC/APIG,但各用不同业务 Workspace。同账号同地域的两个 Space 不得仅因账号相同而获得同一套托管 VPC/APIG。同一 MPA 重试必须沿用原 Space 和 PG Workspace。迁移期间,已有部署仍能解析原网关和数据库。 - -非目标:创建或删除 AIDAP PG Workspace;导入 ArkClaw 的 `claw_space_application_conf`;改变通用智能体;在测试中创建真实云资源。 - -## 需求与设计 - -- **FR-1 — Space 身份:** Studio 创建时选择服务端授权的已有逻辑 Space。云资源变更前,核验 Space 与部署账号和地域的关系。Space 随请求身份、任务和部署记录持久化。Runtime 的 `CLAW_SPACE_ID` 使用选择值,而非账号推导的默认值。重试时改变 Space 必须失败。 -- **FR-2 — 共享资源:** 网络/APIG 记录、锁、稳定名称和归属标记按账号、地域、Space 建立。显式采用 VPC/子网/APIG 时校验其属于所选 Space。保留旧账号级记录供已有智能体使用;不得静默转移旧网关或覆盖旧行。 -- **FR-3 — Runtime 契约:** 部署的 `agentkit-mpa-agent` 必须查询同一个 Space 范围内的网关。启用新流程前同步修改其查询契约并发布镜像;否则以可操作的兼容性错误阻止创建。本次不扩大 Runtime 对共享注册库的写权限。 -- **FR-4 — 手动 PG:** 提交前由操作员为每个 MPA 创建一个 AIDAP 业务 Workspace。服务端根据与该 MPA 绑定的管理员密钥引用解析连接和凭据;浏览器/任务载荷只包含非密钥的 Workspace 身份和主机/端口。核验 Workspace 可连接,且与共享注册库和其他智能体的 Workspace ID 不同。只记录不含密钥的 Workspace ID、主机。重试或失败时不得创建替代 Workspace。 -- **FR-5 — 兼容与失败:** 已有智能体固定使用原网络/APIG/PG;打开创建弹窗不触发迁移。资源选择缺失、冲突、变化或不可达时,尽量在有费用的云操作前失败。取消后保留资源及绑定,供同一 ID 重试。错误信息不得包含数据库 URL、密码或云接口原始响应。 -- **FR-6 — 页面与文档:** 保持三步弹窗。基础信息展示所选 Space,PG 步骤展示手动创建的 AIDAP Workspace。说明 Space 级共享和每智能体独立 PG;同步中英文文案、运维配置示例、契约与前端构建产物。 - -## 影响文件与任务 - -预计涉及 VeADK 的 `veadk/integrations/mpa/managed/{config,registry,database,network,gateway,service,runtime,runner,tasks}.py`、Studio 创建 API/类型/弹窗/国际化、测试、运维说明和构建资源。独立的 `agentkit-mpa-agent` 注册库/客户端契约也需协同修改并发布镜像。 - -1. 确定 Space 列表与选择方式、服务端每智能体 PG 凭据引用;修改生产代码前审查双语契约及迁移方案。 -2. 先写失败测试,覆盖同账号两个 Space、同 Space 两个智能体、PG Workspace 唯一性、重试、旧记录、Runtime 网关查询和安全错误。 -3. 在不削弱归属校验的前提下实现配置、API、注册库、编排和 Runtime 对接。 -4. 验证定向测试、前端构建及浏览器场景、共享回归和构建资源。仅在另行授权且环境隔离时运行真实云冒烟测试。 - -## 风险与验收 - -当前 Runtime 镜像可能重新创建或读取账号级网关;只改 VeADK 不安全。现有共享注册库以账号和地域为主键,需要增量迁移或新表。手动创建的 Workspace 可能只能从另一个 VPC 访问,必须实测连通性。PG 密码不得进入浏览器存储、任务 SQLite、部署记录、源码或日志。 - -验收:同 Space 智能体恰好共享已核验的 VPC/APIG;不同 Space 不共用;每个新 MPA 使用独立的预建 PG Workspace;共享注册库保持独立;重试保留原绑定;旧智能体可用;失败不静默创建替代资源。2026-09-23 验证状态:AIDAP 弹窗链接测试 `pass`;新架构和真实云检查均 `not_run`,等待设计选择和实现。 diff --git a/prd-spec/features/mpa-space-scoped-resources/2026-09-23-two-pg-workspaces.md b/prd-spec/features/mpa-space-scoped-resources/2026-09-23-two-pg-workspaces.md deleted file mode 100644 index 01813aaf4..000000000 --- a/prd-spec/features/mpa-space-scoped-resources/2026-09-23-two-pg-workspaces.md +++ /dev/null @@ -1,57 +0,0 @@ -# Separate management and business PostgreSQL Workspaces - -[中文版](2026-09-23-two-pg-workspaces.zh.md) - -- Change ID: `mpa-two-pg-workspaces`; date: 2026-09-23; status: implemented; live database cutover pending operator setup. -- User decision: shared records move to `mpa_admin_workspace/mpa_admin_db`; all agent business databases stay together in a different Workspace with database-level isolation. This supersedes the earlier [per-agent Workspace proposal](2026-09-23-space-scoped-manual-pg.md). Workspace creation remains an operator prerequisite, as requested previously. -- Owner: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md). - -## Evidence, goals and boundaries - -`Profile.admin_url` already supplies business database administration while `Profile.shared_url` supplies network/APIG/deployment registries and Runtime bootstrap. Current profiles can accidentally point both at one Workspace. Moving only the connection to an empty registry would lose reuse/ownership records. Existing business databases and Runtime PG identity must remain stable. - -The target contains one management Workspace named `mpa_admin_workspace`, one management database named `mpa_admin_db` with the existing three registry tables, and one separate business Workspace containing `mpa_agent_` databases. Account/region network and APIG scope is preserved. Generic agents, per-agent Workspace creation, Space catalog/selection, automatic cloud Workspace creation, business-data migration, credential removal from the existing Runtime bootstrap contract, and automatic live cutover/deletion are outside this change. - -## Requirements and design - -- FR-1: Optional `managed.postgres` opts into the two-Workspace layout. It requires distinct nonempty `admin-workspace-id` and `business-workspace-id`; `admin-workspace-name` is fixed to `mpa_admin_workspace`. `SHARED_APIG_DATABASE_URL` must address `mpa_admin_db` on a different host from the business administrator URL. IDs and host ownership are operator assertions verified in the AIDAP console, not a claim of cloud API validation. Different hostnames alone do not prove different Workspaces. Legacy profiles without the section retain compatibility. -- FR-2: `admin-database-url-env` in that section names a separate maintenance connection for preparing the management database. It must target the same host/port as the shared registry connection. Normal creation never creates an empty management database automatically. Explicit `veadk mpa init-admin-db --config ...` creates/reuses `mpa_admin_db`, checks the configured registry owner, and initializes the known registry tables. A pre-existing database must belong to that owner and contain only the known public tables. SQL identifiers are quoted; output contains no URL or password. The maintenance credential never enters Runtime or browser payloads. -- FR-3: The command optionally accepts `--source-url-env OLD_SHARED_APIG_DATABASE_URL` to copy the three existing registry tables while retaining the source. Operators stop creation and registry writers for the whole migration/cutover window. The source is read-only, table locks bound concurrent writes, the destination transaction copies all records or none, and a rerun accepts identical records but rejects conflicting or extra target rows. Copy preserves JSON records, owner hashes, agent IDs, network/APIG IDs and database identity. Lock/statement and overall timeouts are bounded; cancellation closes connections. Cloud changes and Runtime rollouts remain explicit operational steps. -- FR-4: New creation uses only the business connection for `mpa_agent_` and only the management connection for registry/bootstrap. Record the two configured Workspace IDs on new or verified legacy deployment records. Reject changing a recorded Workspace binding before cloud resource mutation; reject a changed business host on an existing record. Fresh creation and same-ID retry preserve current database naming, ownership and cleanup rules. -- FR-5: Studio config returns safe layout/name metadata. The PG step explains that it configures the shared business Workspace and creates an independent database for this MPA; the management Workspace is server-configured. Preserve old clients and draft/task recovery. Both languages and example YAML agree. - -## Tasks, acceptance and verification - -Review refinement: migration rejects source deployment records with `pending=true`. Their persisted Runtime request hash includes the old registry URL; operators must finish/reconcile them under the original configuration before copying. This preserves idempotent recovery rather than silently rewriting hashes. - -| Task | Requirements | Acceptance | -| --- | --- | --- | -| T-1 | FR-1 | AC-1: reject same IDs/host, wrong management database and maintenance target; legacy configuration still loads. | -| T-2 | FR-2–3 | AC-2: offline tests cover initialization, missing/foreign tables, copy/retry/conflict/rollback/timeouts; source is never modified. | -| T-3 | FR-4 | AC-3: two agents retain distinct database names on the same business host and receive the same management connection; changed binding fails early. | -| T-4 | FR-5 | AC-4: localized dialog/API tests, browser check, frontend build and generated assets; paired operator instructions show migration and recovery. | - -Files: managed config/service and new admin database module; CLI registration/module; Studio dialog/types/locales; managed tests, paired component specs/operator guide/example YAML. Run targeted `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py`, changed-file Ruff/Pyright, frontend tests/build/i18n/assets, then the two-worker non-smoke Python regression when feasible. No live resource or private-profile modification is part of these tests. - -## Review, risks and delivery record - -### Implementation review and verification, 2026-09-23 - -Scope: base `821f0f36`, branch `feat/from-main-20260922`, uncommitted working diff including the existing three-step creation work. No commit, push, private profile update, cloud Workspace creation, live data copy or Runtime rollout was performed. T-1–T-4 are implemented; AC-1–AC-3 have offline regression coverage, and AC-4 has frontend/browser/build coverage. Database transactions/locks are tested with isolated simulations, not a real PostgreSQL server. - -| Check | Status | Evidence | -| --- | --- | --- | -| Targeted Python | pass | `UV_CACHE_DIR=/tmp/veadk-uv-cache uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q`; 317 passed. Includes separate hosts, copy conflict rollback, cancellation/error cleanup, CLI redaction, two agents sharing business host with distinct databases, and safe config API. | -| Frontend | pass | `npm --prefix frontend test`: 1305 Node tests and 33 Vitest tests; separate `npm --prefix frontend exec -- vitest run frontend/tests/mpaCreation.test.tsx`: 16 passed. | -| Build/localization/assets | pass | `npm --prefix frontend run build`, `check:i18n`, `test:webui-assets`; 2 locales/21 namespaces and 104 packaged files/248 internal references checked. | -| Browser | pass | Actual dialog with mock config API: Chinese and English management/business explanation, AIDAP link, normal navigation and 390 px iframe viewport checked. No cloud submission. Loading/error/cancel/retry coverage comes from automated tests, not a new live resource run. Temporary preview files/server removed. | -| Ruff | pass | Changed Python files checked and formatted using the cached pre-commit Ruff executable; the project dev extra does not install a standalone `ruff` command. | -| Pyright, feature code/tests | pass | Cached Pyright with `--pythonpath .venv/bin/python` over config/service/new database and CLI modules and changed managed tests: no errors. | -| Pyright, CLI registration file | fail (pre-existing) | `cli_mpa.py` has a 3-or-5 tuple return/unpack mismatch in legacy VeFaaS deployment. Reproduced against `git show HEAD:veadk/cli/cli_mpa.py`; the new registration adds no error. | -| Full two-worker Python regression | fail (environment) | `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`: 4980 passed, 76 failed, 11 skipped, 2 xfailed, 5 errors. Failed-node retry with local-port permission: 73 passed, 6 failed, 2 collection errors. Remaining Harness/self-host failures require missing `llama_index`/`anthropic`; those files are unchanged. These results are not a full-green regression claim. | -| Live SQL/cloud cutover | not_run | New management Workspace/credentials and a stopped-writer migration window are not configured. No claim of live lock, ownership, connectivity or existing Runtime verification. | -| Pre-commit full repository | not_run | No commit requested. Required before any later commit after branch synchronization. | - -Direct review confirmed unchanged table keys and business database derivation, management maintenance credential exclusion from Runtime, no overwrite/delete migration, all-table destination rollback, fixed timeout limits, cancellation propagation, and no URL/password in normal output. The caller must coordinate a single registry endpoint for all consumers. Existing database grants are retained; Workspace separation alone does not provide stronger isolation between the business databases. - -Direct design review replaces unavailable `review-spec`: user approved the two-Workspace topology; previous unanswered per-agent Workspace and Space-selector questions are superseded. Preserve wire/table compatibility with the existing MPA image, retain source registry for rollback, and require maintenance credentials only for the explicit initialization command. Existing Runtime instances retain their previous shared URL until an authorized rollout; therefore operators must coordinate their migration before restoring registry writers. No automatic rollback to the old registry after new writes: reconcile records first. Unknown cloud account/Workspace ownership is not inferred from endpoint labels. Status of implementation/tests/live checks: `not_run` at design creation; results will be appended. diff --git a/prd-spec/features/mpa-space-scoped-resources/2026-09-23-two-pg-workspaces.zh.md b/prd-spec/features/mpa-space-scoped-resources/2026-09-23-two-pg-workspaces.zh.md deleted file mode 100644 index 7479a3a42..000000000 --- a/prd-spec/features/mpa-space-scoped-resources/2026-09-23-two-pg-workspaces.zh.md +++ /dev/null @@ -1,57 +0,0 @@ -# 分离管理与业务 PostgreSQL Workspace - -[English](2026-09-23-two-pg-workspaces.md) - -- 变更 ID:`mpa-two-pg-workspaces`;日期:2026-09-23;状态:已实现,真实数据库切换待操作员配置。 -- 用户决定:共享记录迁入 `mpa_admin_workspace/mpa_admin_db`;所有智能体业务库共同放在另一 Workspace,继续按数据库隔离。本方案取代之前的[每智能体一个 Workspace 提案](2026-09-23-space-scoped-manual-pg.zh.md)。沿用此前要求,Workspace 由操作员预先创建。 -- 归属:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md)。 - -## 证据、目标与边界 - -`Profile.admin_url` 已用于业务库管理,`Profile.shared_url` 用于网络/APIG/部署注册库和 Runtime 启动配置。当前配置可能让两者指向同一 Workspace。只把连接切到空注册库会丢失复用和归属记录。已有业务库及 Runtime PG 身份必须保持稳定。 - -目标包含一个名为 `mpa_admin_workspace` 的管理 Workspace,其中 `mpa_admin_db` 存现有三张共享注册表;另一个业务 Workspace 存所有 `mpa_agent_` 库。保留按账号/地域共享网络和 APIG 的规则。通用智能体、每智能体创建 Workspace、Space 列表/选择、自动创建云 Workspace、业务数据迁移、去除现有 Runtime 启动契约中的注册库凭据,以及自动执行线上切换/删除,均不在本次范围。 - -## 需求与设计 - -- FR-1:可选 `managed.postgres` 启用两 Workspace 结构。必须提供不同且非空的 `admin-workspace-id` 和 `business-workspace-id`;`admin-workspace-name` 固定为 `mpa_admin_workspace`。`SHARED_APIG_DATABASE_URL` 必须指向 `mpa_admin_db`,且主机与业务库管理员连接不同。ID 和主机归属由操作员在 AIDAP 控制台核验,本地校验不代表已调用云 API 核验;不同主机名本身不能证明 Workspace 不同。没有该节的旧配置保持兼容。 -- FR-2:该节中的 `admin-database-url-env` 指向准备管理库所需的独立维护连接,主机/端口必须与共享注册库连接一致。正常创建流程不会自动建立空管理库。显式执行 `veadk mpa init-admin-db --config ...` 才会创建/复用 `mpa_admin_db`、校验配置的注册库属主并初始化已知注册表。已有数据库必须属于该用户,且只能含已知的 public 表。SQL 标识符正确引用;输出不含 URL 或密码。维护凭据不会进入 Runtime 或浏览器载荷。 -- FR-3:命令可加 `--source-url-env OLD_SHARED_APIG_DATABASE_URL` 复制现有三张共享表并保留源库。操作员必须在迁移及切换期间停止创建流程和注册库写入者。源库只读;表锁限制并发写入;目标事务保证整批复制或整批回滚;重跑接受相同记录,但拒绝冲突或多余的目标记录。复制保留 JSON、属主哈希、智能体 ID、网络/APIG ID、业务库身份。锁、语句和总体执行时间均有上限;取消时关闭连接。云变更和 Runtime 发布是后续显式运维步骤。 -- FR-4:新创建流程只使用业务连接准备 `mpa_agent_`,只使用管理连接准备注册库/启动配置。为新记录或核验过的旧部署记录保存两套配置的 Workspace ID。已登记 Workspace 绑定变化必须在云资源变更前失败;旧记录的业务主机变化同样失败。首次创建及同 ID 重试沿用现有数据库命名、归属和清理规则。 -- FR-5:Studio 配置接口返回安全的布局/名称信息。PG 步骤说明此处配置共用的业务 Workspace,并为本 MPA 创建独立数据库;管理 Workspace 由服务端配置。保留旧客户端及草稿/任务恢复。双语与 YAML 示例保持一致。 - -## 任务、验收与验证 - -审查补充:迁移拒绝源部署记录中的 `pending=true`。其持久化 Runtime 请求哈希包含旧注册库地址,操作员必须先在原配置下完成或核对这些部署再复制,以保持幂等恢复,不静默改写哈希。 - -| 任务 | 需求 | 验收 | -| --- | --- | --- | -| T-1 | FR-1 | AC-1:拒绝相同 ID/主机、错误管理库名和维护连接目标;旧配置仍可加载。 | -| T-2 | FR-2–3 | AC-2:离线测试覆盖初始化、缺表/异类表、复制/重试/冲突/回滚/超时;不修改源库。 | -| T-3 | FR-4 | AC-3:两个智能体在同一业务主机上使用不同库名并接收同一管理连接;绑定变化提前失败。 | -| T-4 | FR-5 | AC-4:本地化弹窗/API 测试、浏览器检查、前端构建及资源;双语运维说明包含迁移与恢复。 | - -文件:managed 配置/服务及新管理库模块;CLI 注册/模块;Studio 弹窗/类型/本地化;managed 测试、双语组件契约/操作指南/YAML 示例。执行定向 `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py`、变更文件 Ruff/Pyright、前端测试/构建/国际化/资源检查,再在环境允许时执行两个 worker 的非冒烟 Python 回归。这些测试不变更真实资源或私有配置。 - -## 审查、风险及交付记录 - -### 实现审查与验证,2026-09-23 - -范围:基线 `821f0f36`、分支 `feat/from-main-20260922` 的未提交工作区差异,包含已有的三步创建修改。未执行提交、推送、私有配置变更、云 Workspace 创建、真实数据复制或 Runtime 发布。T-1–T-4 已实现;AC-1–AC-3 有离线回归覆盖,AC-4 有前端/浏览器/构建覆盖。数据库事务和锁使用隔离模拟测试,未连接真实 PostgreSQL 服务。 - -| 检查 | 状态 | 证据 | -| --- | --- | --- | -| 定向 Python | pass | `UV_CACHE_DIR=/tmp/veadk-uv-cache uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py -q`,317 项通过。覆盖主机分离、复制冲突回滚、取消/错误清理、CLI 脱敏、两个智能体同业务主机不同数据库及安全配置 API。 | -| 前端 | pass | `npm --prefix frontend test`:1305 项 Node 测试、33 项 Vitest;单独 `npm --prefix frontend exec -- vitest run frontend/tests/mpaCreation.test.tsx`:16 项通过。 | -| 构建/国际化/资源 | pass | `npm --prefix frontend run build`、`check:i18n`、`test:webui-assets`;检查 2 个语言/21 个命名空间、104 个打包文件/248 个内部引用。 | -| 浏览器 | pass | 实际弹窗配合模拟配置 API:检查中英文管理/业务说明、AIDAP 链接、正常步骤导航及 390 px iframe 窄窗口。未提交云创建。加载/错误/取消/重试由自动化测试覆盖,本次未新增真实资源运行。临时预览文件/服务已清理。 | -| Ruff | pass | 使用缓存的 pre-commit Ruff 检查并格式化变更 Python 文件;项目 dev extra 没有安装独立 `ruff` 命令。 | -| Pyright,本功能代码/测试 | pass | 缓存 Pyright 配合 `--pythonpath .venv/bin/python` 检查配置/服务/新增数据库及 CLI 模块、变更 managed 测试,无错误。 | -| Pyright,CLI 注册文件 | fail(既有) | `cli_mpa.py` 旧 VeFaaS 部署存在返回 3 或 5 元组与解包不匹配;对 `git show HEAD:veadk/cli/cli_mpa.py` 复现相同问题,新增注册未引入类型错误。 | -| 双 worker 全量 Python | fail(环境) | `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`:4980 通过、76 失败、11 跳过、2 xfailed、5 错误。允许本地端口后重跑失败节点:73 通过、6 失败、2 收集错误。余下 Harness/self-host 测试缺少 `llama_index`/`anthropic`,相关文件未修改;不宣称全量回归全绿。 | -| 真实 SQL/云切换 | not_run | 尚未配置新管理 Workspace/凭据及停止写入的迁移窗口。不宣称已验证真实锁、属主、连通性或已有 Runtime。 | -| 全仓库 pre-commit | not_run | 未要求提交;后续提交前需同步分支并执行。 | - -直接审查确认:表键及业务库推导不变,管理维护凭据不注入 Runtime,迁移不覆盖/删除,目标三表事务整体回滚,超时有界、取消向上传递,正常输出不含连接 URL/密码。操作员必须协调所有使用方统一注册库端点。保留已有数据库授权;Workspace 分离本身不会增强业务库之间的权限隔离。 - -缺少 `review-spec`,采用直接审查:用户已批准两 Workspace 拓扑;之前每智能体 Workspace 和 Space 选择的未答问题被新方案取代。保留现有 MPA 镜像的协议/表兼容性,保留源注册库以供恢复,只在显式初始化命令中要求维护凭据。已有 Runtime 发布前仍使用旧共享 URL,因此恢复注册库写入前须协调迁移。新库开始写入后不得直接切回旧库,应先对账。不从端点名称推断云账号或 Workspace 归属。设计建立时实现/测试/真实云验证状态均为 `not_run`,后续补充实际结果。 diff --git a/prd-spec/features/mpa-studio-control-plane/2026-09-13-mpa-studio-control-plane-design.md b/prd-spec/features/mpa-studio-control-plane/2026-09-13-mpa-studio-control-plane-design.md deleted file mode 100644 index 2e60e8855..000000000 --- a/prd-spec/features/mpa-studio-control-plane/2026-09-13-mpa-studio-control-plane-design.md +++ /dev/null @@ -1,170 +0,0 @@ -# MPA Studio Turn Control and Resource Workspace - -- Change ID: `mpa-studio-control-plane` -- Status: `approved` -- Created / revised: 2026-09-13 -- Chinese: [design.zh.md](2026-09-13-mpa-studio-control-plane-design.zh.md) -- Components: [Studio Runtime Diagnostics](../../../specs/studio-runtime-diagnostics/README.md), [Studio MPA Control Plane](../../../specs/studio-mpa-control-plane/README.md) - -## Background and evidence - -Live verification found that Agent and model selectors overlap, the deployed model allowlist is incomplete, Token categories are not separately visible, environment failures are too generic, the single-Agent topology omits Sandbox and Session resources, Skill Space and Studio tools are hard to discover, and Studio lacks the whole-Turn lifecycle controls already available in mpa-codex-worker. mpa-agent already validates request-local model overrides and has an A2A control store; VeADK already owns an account model catalog, Skill Space routes, Session environment mounts, Studio tool selection, and structured usage fields. The change composes and extends those contracts rather than building parallel systems. - -## Goals - -- `FR-1`: Query current-account Ark models server-side, filter compatible conversational models, cache successful results, and expose no cloud credential. -- `FR-2`: Freeze model, Skills, tools, and environments into an immutable Turn snapshot on send. Model choice applies to the main Agent and delegated worker and cannot change while non-terminal. -- `FR-3`: Give Agent target, Turn model, usage, lifecycle controls, and send distinct responsive layout slots with no overlap. -- `FR-4`: Display per-Turn and cumulative input, output, reasoning, cache-hit, and total Tokens without replay inflation. -- `FR-5`: Make mpa-agent the sole lifecycle authority for main-model, tools, Sandbox delegation, and worker execution. -- `FR-6`: Provide durable idempotent pause and resume with cooperative safe-point pausing and refresh recovery. Studio exposes no cancel or interrupt action in this release. -- `FR-7`: Persist Session-level Skill Space Skill, Studio tool, and environment mounts; changes apply next Turn and never mutate defaults. -- `FR-8`: Return and render a real Agent-Sandbox-worker-resource topology with actionable node details and no invented active nodes. -- `FR-9`: Explain Studio tool purpose, scenarios, parameters, permissions, examples, source/revision, and Session mounting state. -- `FR-10`: Distinguish environment failures. Authorized administrators/developers may copy a secret without rendering it; ordinary users see configured state only. -- `FR-11`: Advertise capabilities and degrade honestly for old Runtimes. -- `FR-12`: Render each tool call with a human-readable action and its most useful safe subject instead of collapsing different tools into a generic completed label. -- `FR-13`: Preserve complete copyable sanitized tool input/result data while bounding initial rendering; truncation must be explicit and must not silently make the only available data incomplete. -- `FR-14`: Keep model reasoning summaries distinct from Agent working thoughts and suppress only proven transport replays across A2A status and artifact channels. -- `FR-15`: Make the Turn model selector searchable by model ID and display text while retaining keyboard access and the non-terminal Turn lock. - -## Non-goals - -- Mutating Runtime/Agent defaults or account model activation from chat. -- Hard-coding a seasonal model list or trusting browser model IDs. -- Preempting an in-flight provider request; pause completes at the next safe point. -- Resuming cancelled/completed/failed Turns. -- Letting Studio coordinate mpa-agent and worker independently. -- Rendering or persisting secrets, or retrieving them as an ordinary user. -- Inventing Sandbox, Skill, knowledge, tool, or sub-Agent relationships. - -## Scenarios - -1. Selecting a compatible model while idle freezes it on send and locks the selector until terminal. -2. Pause enters `pausing`, finishes only the current non-preemptible operation, starts no next step, and becomes `paused` only when main executor and workers confirm safe points. -3. Refresh restores the authoritative paused/running state and allowed actions from mpa-agent. Pause and resume share the Composer’s single primary action with send; no duplicate lifecycle toolbar is rendered. -4. A paused Turn remains eligible for same-Turn resume for five minutes. A later resume click, or a resume click after the owner is no longer recoverable, terminalizes the old Turn as interrupted and starts a new Turn in the same Session with an incremental continuation instruction. No timeout-triggered work starts without the user’s click. -5. Internal terminalization may stop an unrecoverable old Turn, but Studio exposes no interrupt or cancel action in this release. -5. Skill/tool/environment mounts persist in the Session and enter the next Turn snapshot without changing the active Turn. -6. Environment errors identify authorization, Runtime, version, Manifest, or secret-access boundaries. -7. Authorized secret copy uses a no-store audited action and writes directly to clipboard without rendering; unauthorized copy returns 403. -8. Topology shows observed Agent, Sandbox, worker, Skill Space, Studio tool, MCP, environment, knowledge, and sub-Agent relationships only. -9. Tool activity states the concrete action and safe subject; complete sanitized data remains available through an explicit detail action. -10. A reasoning summary replayed by both A2A status and artifact transport is shown once, while a distinct Agent thought remains separately labelled. -11. The model picker accepts typed filtering and cannot change the frozen model of a non-terminal Turn. - -## Architecture - -### Four-layer configuration - -1. Agent defaults are read-only. -2. Session configuration stores Skill, Studio tool, and environment mounts. -3. Turn snapshot atomically freezes model/resource identifiers and versions. -4. Turn lifecycle is durably owned by mpa-agent. - -Session edits during a non-terminal Turn are explicitly labelled as next-Turn drafts. Deleted or unauthorized resources remain visibly invalid until removed/replaced. - -### Model catalog and Turn freeze - -The Studio server queries Ark with server credentials, filters enabled resources to mpa-agent-compatible conversation models, and caches the last success with freshness metadata. Failure serves labelled stale cache or an explicit unavailable state. mpa-agent validates the selected ID against its effective allowlist before side effects and persists it in the Turn snapshot. Main model and worker delegation read only the snapshot. - -### Whole-Turn lifecycle - -```text -queued -> running -> completed | failed - | -> pausing -> paused -> resuming -> running - | \-> interrupted -> new incremental Turn -``` - -mpa-agent persists control generation, idempotency key, desired/observed state, participant states, safe-point data, pause time, resume deadline, and failure detail. Studio calls only mpa-agent. mpa-agent forwards worker commands and aggregates acknowledgements. A running atomic operation may finish after pause intent, but no next model/tool/delegation starts. Resume within five minutes continues from a proven durable safe point without replaying completed tools or the accepted user message. After the deadline or when same-Turn recovery is unavailable, the user’s resume click interrupts the old execution and creates a new incremental Turn in the same Session. - -### Session resources and topology - -Session mounts store immutable Skill version plus Space ID, Studio tool ID/revision, and environment/workspace bindings. The typed topology uses node kinds `agent`, `sandbox`, `worker-session`, `skill-space`, `skill`, `studio-tool`, `mcp`, `environment`, `knowledge`, and `sub-agent`, with relationships such as `delegates-to`, `runs-on`, `mounts`, `provides`, and `calls`. Optional unmounted categories are graph-external affordances, not fake nodes. Node details expose source, configured/observed status, lifecycle, capabilities, recent calls, and safe navigation identifiers. - -### Composer and usage - -The Composer follows one conventional input surface with one footer row: attachment, Agent target, Turn model, compact status/usage, and one primary button. The primary button means send when idle/terminal, pause while running, progress while pausing/resuming, and resume while paused. Agent and model selectors share the same borderless 36 px trigger style. No second lifecycle toolbar, cancel action, or interrupt action is rendered. Long IDs truncate accessibly and the send/control slot never overlaps the model selector. Usage shows five categories for current Turn and Session cumulative totals; missing values show `—`, and cumulative snapshots contribute positive deltas only. - -### Environment and secrets - -Normal environment responses include name, source, sensitivity, configured state, and allowed non-secret values, never secret values. Secret copy is a separate administrator/developer-only POST with `Cache-Control: no-store`. It audits actor, Runtime, variable, time, and result without value. Frontend writes the response directly to `navigator.clipboard.writeText` and does not render, persist, cache, or emit it in telemetry/errors. - -### Studio tool discoverability - -Tool detail includes purpose, scenarios, input schema, permissions, examples, source/revision, and default/Session mounting state. Add/remove applies next Turn. No tool credential enters browser data. - -### Conversation activity projection and presentation - -The A2A projection layer preserves the semantic kind of model reasoning summary versus Agent working thought. It tracks cumulative text independently per task, invocation, semantic kind, and transport source. Cross-source content is suppressed only when it is an exact replay or a proven cumulative-prefix replay of the same logical stream; arbitrary repeated prose is retained. Stable source event IDs remain the primary deduplication key. - -The frontend derives a safe action title and subject from tool-specific fields, including nested Runtime payloads. Known tools such as `create_goal`, `exec_command`, search, read, and file-change operations receive action-specific labels. Unknown tools display their concrete name. The collapsed row stays concise; expanded details expose sanitized input and result. Large values use bounded DOM rendering plus an explicit complete-copy/download path, and never replace the only accessible representation with an unannounced truncated value. Secret masking and unsafe-control removal remain mandatory before preview or copy. - -The Composer reuses the existing accessible searchable compact selector rather than a native `select`. Filtering covers model ID and display text, preserves arrow/Enter/Escape behavior, reports no matches, and is disabled while the Turn model is immutable. - -## Interface contracts - -- Account model catalog returns compatible models, freshness/stale metadata, and classified failure. -- Agent Card advertises versioned `turnModelSelection`, `turnLifecycleControl`, `sessionResourceMounts`, and `resourceTopology`. -- Turn creation carries validated model ID and Session resource revision; mpa-agent persists resolved snapshot. -- Turn control exposes pause/resume to Studio with idempotency and expected generation. Internal terminalization remains available to mpa-agent but is not a Studio action. -- Turn status returns state, desired state, generation, safe-point/participant summaries, failure, pause time, resume deadline/disposition, and allowed actions. A 409 response carries the same authoritative state shape and Studio consumes it instead of surfacing raw transport text. -- Session resource APIs return defaults, deltas, invalid resources, revision, and next-Turn semantics. -- Topology returns typed nodes/edges for one Session/Turn. -- Secret copy is a no-store role-protected action and never part of list responses. - -## Compatibility and failures - -Old agents without capabilities retain one default model, hide pause/resume, and show verified topology only. Stream disconnect is not cancellation; Studio reconnects persisted state/events. Concurrent controls resolve by generation/idempotency and return authoritative conflicts. Studio absorbs structured 409 state. Exact process-replacement resume is replaced by a user-triggered incremental Turn after five minutes or when the owner cannot resume. Native ArkClaw and non-MPA Studio paths remain unchanged. - -## Tasks - -| ID | Work | Owner | Dependency | -| --- | --- | --- | --- | -| `T-1` | Durable Turn state, safe points, idempotent commands, participant aggregation | mpa-agent | none | -| `T-2` | Wrap worker pause/resume and expired-resume fallback behind mpa-agent | mpa-agent | `T-1` | -| `T-3` | Compose Ark catalog, compatibility, and Turn validation | Studio BFF + mpa-agent | none | -| `T-4` | Persist Session mounts and atomically create Turn snapshots | Studio BFF + mpa-agent | `T-1` | -| `T-5` | Skill Space browse/mount and Studio tool details/examples | Studio | `T-4` | -| `T-6` | Typed topology endpoint, graph, and detail drawer | mpa-agent + Studio | `T-4` | -| `T-7` | Composer layout and Turn model/lifecycle controls | Studio frontend | `T-1`, `T-3` | -| `T-8` | Usage category UI and environment/secret-copy flows | Studio | none | -| `T-9` | Browser, Runtime, race, refresh, security, and legacy E2E | both repos | `T-1`–`T-8` | -| `T-10` | Conversation event semantics, replay-safe thought/reasoning projection, human-readable tool activity, complete sanitized details, and searchable model selection | Studio BFF + Studio frontend | `T-3` | - -## Acceptance - -| Requirement | Acceptance evidence | Initial | -| --- | --- | --- | -| `FR-1`, `FR-2` | Catalog contracts and live two-model Turn proving main/worker consistency and invalid-ID no-side-effect rejection | `not_run` | -| `FR-3` | Browser screenshots/tests at normal/narrow widths with long model IDs | `not_run` | -| `FR-4` | Token tests and replay/refresh live evidence for all five categories | `not_run` | -| `FR-5`, `FR-6` | Integration/live `running -> pausing -> paused -> resuming -> running`, five-minute fallback, race, and idempotency | `not_run` | -| `FR-7`, `FR-9` | Real Skill Space/tool Session mounts survive refresh and apply next Turn | `not_run` | -| `FR-8` | Contract and browser proof that only real relationships render | `not_run` | -| `FR-10` | Precise failures, no rendered secret, authorized copy, ordinary-user 403, value-free audit | `not_run` | -| `FR-11` | Legacy Agent Card fixtures and browser degradation | `not_run` | -| `FR-12`, `FR-13` | Tool-specific/nested/unknown fixtures, large sanitized result copy/download, and browser inspection | `not_run` | -| `FR-14` | Status/artifact replay fixtures plus live/history/reconnect reasoning and thought rendering | `not_run` | -| `FR-15` | Keyboard and typed filtering, no-match behavior, and non-terminal disabled selection | `not_run` | - -Required gates follow `AGENTS.md` and `frontend/SPEC.md`: targeted tests, broad regression for shared contracts, frontend test/build, changed-file Ruff/Pyright, real browser normal/error/cancel/retry/narrow cases, pre-commit, two review rounds, and isolated Runtime E2E. - -## Risks and approval - -Safe-point implementation must first prove which Runner boundaries resume without duplicate side effects. Account resources may be enabled but incompatible with the Runtime credential/base URL, so intersection fails closed. Large topologies require bounds, detail loading, pan/zoom, and an accessible list. Browser clipboard policy failure must never fall back to rendering. - -The user approved phased delivery, live Ark catalog, immutable Turn model, whole-flow cooperative pause, mpa-agent lifecycle ownership, non-overlapping Composer layout, real resource topology, next-Turn Session mounts, and administrator/developer secret copy without rendering. The written bilingual design and component contract, including the single dynamic primary action and user-triggered five-minute fallback, were approved on 2026-09-13. - -## Design review record - -The repository review found and resolved these design blockers: - -- **Worker pause contract difference:** mpa-codex-worker implements pause by interrupting the active Codex Turn and resume through `thread/resume`. mpa-agent must wrap that behavior as a whole-flow safe point and, after five minutes or when the owner is unrecoverable, create an incremental Turn only after the user clicks resume; Studio never coordinates the worker directly. -- **mpa-agent state gap:** its current A2A store owns running/cancel-requested/final states only. `T-1` is an explicit schema/state-machine migration with legacy-row compatibility, not a UI-only extension. -- **Safe-point ambiguity:** the design forbids claiming process-restart resume until a concrete Runner boundary has an atomic checkpoint and duplicate-side-effect proof. The first vertical slice must spike and test that boundary before implementing full pause/resume. -- **Model-catalog duplication risk:** Studio already has `frontend/server/model_catalog`; `T-3` extends its response with freshness/stale/compatibility metadata and intersects it with Runtime capabilities rather than creating another catalog service. -- **Secret endpoint reuse risk:** existing raw API-key routes demonstrate no-store transport but do not satisfy Runtime-variable RBAC/audit. Secret copy gets its own role-protected audited contract and may not reuse a weaker generic resolver. -- **Scope sequencing:** delivery is split into two approved phases. Phase 1 is model catalog/freeze, lifecycle controller, usage, environment errors/secret copy, and Composer layout. Phase 2 is Session Skill/tool mounts, tool guidance, and resource topology. Phase 2 cannot weaken Phase 1 lifecycle/snapshot contracts. - -No P0/P1 product-design blocker remains. Implementation is still gated on the user's review of this written pair and an implementation plan that starts with the safe-point spike. diff --git a/prd-spec/features/mpa-studio-control-plane/2026-09-13-mpa-studio-control-plane-design.zh.md b/prd-spec/features/mpa-studio-control-plane/2026-09-13-mpa-studio-control-plane-design.zh.md deleted file mode 100644 index f48593af2..000000000 --- a/prd-spec/features/mpa-studio-control-plane/2026-09-13-mpa-studio-control-plane-design.zh.md +++ /dev/null @@ -1,170 +0,0 @@ -# MPA Studio Turn 控制与资源工作台 - -- Change ID:`mpa-studio-control-plane` -- 状态:`approved` -- 创建 / 修订日期:2026-09-13 -- English:[design.md](2026-09-13-mpa-studio-control-plane-design.md) -- 组件:[Studio Runtime Diagnostics](../../../specs/studio-runtime-diagnostics/README.zh.md)、[Studio MPA Control Plane](../../../specs/studio-mpa-control-plane/README.zh.md) - -## 背景与证据 - -真实验证发现:Agent 与模型选择器重叠;部署时模型 allowlist 不完整;Token 分类没有分开展示;环境错误过于泛化;单 Agent 拓扑没有体现 Sandbox 和 Session 资源;Skill Space 与 Studio 工具难以发现;Studio 也缺少 mpa-codex-worker 已提供的完整 Turn 生命周期控制。mpa-agent 已支持请求级模型校验并具有 A2A 控制存储;VeADK 已具有账号模型目录、Skill Space 路由、Session 环境挂载、Studio 工具选择及结构化用量。本变更组合和扩展这些契约,而不是建立平行系统。 - -## 目标 - -- `FR-1`:服务端查询当前账号方舟模型,过滤兼容的对话模型,缓存成功结果,不向浏览器暴露云凭据。 -- `FR-2`:发送时把模型、Skills、工具和环境冻结为不可变 Turn 快照;模型同时应用于主 Agent 和委托 worker,非终态期间不可切换。 -- `FR-3`:Agent 目标、Turn 模型、用量、生命周期控制和发送拥有独立响应式布局槽位,禁止重叠。 -- `FR-4`:按 Turn 和累计分别展示输入、输出、推理、缓存命中和总 Token,重放不得膨胀。 -- `FR-5`:mpa-agent 是主模型、工具、Sandbox 委托和 worker 的唯一生命周期权威。 -- `FR-6`:提供持久、幂等的 pause 和 resume,采用协作式安全点暂停并支持刷新恢复。本版本 Studio 不暴露 cancel 或 interrupt 操作。 -- `FR-7`:持久化 Session 级 Skill Space Skill、Studio 工具和环境挂载;变更从下一 Turn 生效,不修改默认值。 -- `FR-8`:返回并展示真实 Agent-Sandbox-worker-资源拓扑,提供可操作详情,不伪造活动节点。 -- `FR-9`:展示 Studio 工具用途、场景、参数、权限、示例、来源/revision 和 Session 挂载状态。 -- `FR-10`:区分环境失败;授权管理员/开发者可复制但不可渲染 Secret,普通用户只看已配置状态。 -- `FR-11`:声明 capability,并让旧 Runtime 真实降级。 -- `FR-12`:每个工具调用展示 human-readable 动作及其最有用的安全对象,不把不同工具都折叠成通用完成文案。 -- `FR-13`:保留完整、可复制、已脱敏的工具输入/结果,同时限制初始渲染;截断必须明确,且不得静默导致唯一可用数据不完整。 -- `FR-14`:区分模型 reasoning summary 与 Agent 工作 thought,仅抑制经证明来自 A2A status/artifact 通道的传输重放。 -- `FR-15`:Turn 模型选择器支持按模型 ID 和展示文本搜索,同时保留键盘可访问性与非终态 Turn 锁定。 - -## 非目标 - -- 从聊天修改 Runtime/Agent 默认值或账号模型开通状态。 -- 在前端硬编码季节性模型列表,或信任浏览器模型 ID。 -- 抢占正在执行的 Provider 请求;暂停在下一个安全点完成。 -- 恢复 cancelled/completed/failed Turn。 -- 让 Studio 分别协调 mpa-agent 与 worker。 -- 渲染或持久化 Secret,或让普通用户获取 Secret。 -- 伪造 Sandbox、Skill、知识、工具或子 Agent 关系。 - -## 场景 - -1. 空闲时选择兼容模型,发送后模型冻结,直到 Turn 终态前选择器禁用。 -2. Pause 进入 `pausing`,只允许当前不可抢占操作完成,不启动下一步;主执行器和所有 worker 到达安全点后才进入 `paused`。 -3. 刷新后从 mpa-agent 恢复权威 paused/running 状态和允许操作。暂停、继续与发送共用 Composer 唯一主按钮,不渲染重复生命周期工具栏。 -4. Turn 暂停后的五分钟内允许恢复原 Turn;超过五分钟,或 owner 已无法恢复时,只有用户再次点击继续,才把旧 Turn 终结为 interrupted,并在同一 Session 创建携带增量继续指令的新 Turn。超时本身不会自动执行。 -5. 内部终态化可以停止无法恢复的旧 Turn,但本版本 Studio 不暴露 interrupt 或 cancel 操作。 -5. Skill/工具/环境挂载持久化到 Session,进入下一 Turn 快照,不改变活动 Turn。 -6. 环境错误明确指出鉴权、Runtime、版本、Manifest 或 Secret 权限边界。 -7. 授权 Secret 复制通过 no-store 审计接口直接写剪贴板,不渲染;未授权返回 403。 -8. 拓扑只展示真实 Agent、Sandbox、worker、Skill Space、Studio 工具、MCP、环境、知识和子 Agent 关系。 -9. 工具活动说明具体动作与安全对象;完整脱敏数据可通过显式详情操作获取。 -10. 同一 reasoning summary 同时经 A2A status 与 artifact 重放时只展示一次;不同的 Agent thought 独立标注。 -11. 模型选择器接受输入过滤,且不能修改非终态 Turn 已冻结的模型。 - -## 架构 - -### 四层配置 - -1. Agent 默认配置只读。 -2. Session 配置保存 Skill、Studio 工具和环境挂载。 -3. Turn 快照原子冻结模型/资源 ID 和版本。 -4. Turn 生命周期由 mpa-agent 持久化并拥有。 - -非终态期间的 Session 编辑明确标记为下一 Turn 草稿。已删除或无权资源保持可见失效状态,直到移除或替换。 - -### 模型目录与 Turn 冻结 - -Studio 服务端使用服务端凭据查询方舟,筛选已开通且兼容 mpa-agent 的对话模型,并缓存最近成功结果及新鲜度。失败时返回标记过期的缓存,或明确不可用。mpa-agent 在副作用前校验模型 ID 并持久化 Turn 快照;主模型和 worker 委托只读取该快照。 - -### 完整 Turn 生命周期 - -```text -queued -> running -> completed | failed - | -> pausing -> paused -> resuming -> running - | \-> interrupted -> 新增量 Turn -``` - -mpa-agent 持久化控制 generation、幂等键、期望/观测状态、参与方状态、安全点、暂停时间、恢复截止时间和失败详情。Studio 只调用 mpa-agent;mpa-agent 下发 worker 控制并聚合确认。暂停意图后,正在执行的原子操作可完成,但禁止启动下一模型/工具/委托。五分钟内 Resume 从已证明的持久安全点继续,不重放已完成工具或已接受用户消息。超过截止时间或无法原 Turn 恢复时,由用户点击继续触发旧执行中断,并在同一 Session 创建新增量 Turn。 - -### Session 资源与拓扑 - -Session 挂载保存不可变 Skill 版本及 Space ID、Studio 工具 ID/revision、环境/Workspace 绑定。Typed topology 节点为 `agent`、`sandbox`、`worker-session`、`skill-space`、`skill`、`studio-tool`、`mcp`、`environment`、`knowledge`、`sub-agent`,关系包括 `delegates-to`、`runs-on`、`mounts`、`provides`、`calls`。未挂载可选类别只作为图外入口,不伪造为活动节点。节点详情展示来源、配置/观测状态、生命周期、能力、最近调用和安全导航 ID。 - -### Composer 与用量 - -Composer 使用一个常规输入面和一排 Footer:附件、Agent 目标、Turn 模型、紧凑状态/用量与唯一主按钮。主按钮在空闲/终态表示发送,在运行时表示暂停,在 pausing/resuming 时表示进度,在 paused 时表示继续。Agent 与模型选择器统一为无边框 36 px trigger;不渲染第二组生命周期工具栏、cancel 或 interrupt。长 ID 可访问地省略,发送/控制槽位不得与模型选择器重叠。用量展示当前 Turn 与 Session 累计五类数据;缺失显示 `—`,累计快照仅贡献正增量。 - -### 环境与 Secret - -普通环境响应返回名称、来源、敏感性、配置状态和允许的非敏感值,绝不包含 Secret。Secret 复制是管理员/开发者专用 POST,设置 `Cache-Control: no-store`,只审计操作者、Runtime、变量、时间和结果,不记录值。前端直接调用 `navigator.clipboard.writeText`,不渲染、持久化、缓存或写入遥测/错误。 - -### Studio 工具可发现性 - -工具详情包含用途、适用场景、输入 schema、权限、示例、来源/revision 以及默认/Session 挂载状态。添加/移除从下一 Turn 生效,浏览器数据不包含工具凭据。 - -### 会话活动投影与展示 - -A2A 投影层保留模型 reasoning summary 与 Agent 工作 thought 的语义类型。它按 task、invocation、语义类型和传输来源分别跟踪累计文本。只有内容属于完全重放或同一逻辑流经证明的累计前缀重放时才跨来源抑制;任意重复措辞必须保留。稳定来源事件 ID 仍是首选去重键。 - -前端从工具特定字段(包括 Runtime 的嵌套 payload)提取安全动作标题和对象。`create_goal`、`exec_command`、搜索、读取和文件修改等已知工具使用专属动作文案;未知工具展示具体名称。折叠行保持简洁,展开详情提供已脱敏输入和结果。大值采用有界 DOM 渲染及显式完整复制/下载路径,不能用未说明的截断值替代唯一可访问内容。预览和复制前都必须继续执行 Secret 遮罩和不安全控制字符清理。 - -Composer 复用现有可访问、可搜索的紧凑选择器,不再使用原生 `select`。过滤覆盖模型 ID 和展示文本,保留方向键/Enter/Escape 行为和无匹配提示,并在 Turn 模型不可变期间禁用。 - -## 接口契约 - -- 账号模型目录返回兼容模型、新鲜度/过期元数据和分类失败。 -- Agent Card 声明带版本的 `turnModelSelection`、`turnLifecycleControl`、`sessionResourceMounts`、`resourceTopology`。 -- Turn 创建携带已校验模型 ID 和 Session 资源 revision;mpa-agent 持久化解析后的快照。 -- Studio 的 Turn control 提供 pause/resume,带幂等键和预期 generation。内部终态化能力保留给 mpa-agent,但不作为 Studio 操作暴露。 -- Turn status 返回状态、期望状态、generation、安全点/参与方摘要、失败、暂停时间、恢复截止时间/处置方式和允许操作。409 返回相同的权威状态结构,Studio 直接吸收状态而不展示原始传输文本。 -- Session resource API 返回默认值、增量、失效资源、revision 和下一 Turn 语义。 -- Topology 返回一个 Session/Turn 的 typed nodes/edges。 -- Secret copy 是 no-store 角色保护接口,绝不进入列表响应。 - -## 兼容与失败 - -旧 Agent 未声明 capability 时保留单一默认模型,隐藏 pause/resume,只展示已验证拓扑。Stream 断开不等于取消;Studio 重连持久状态/事件。并发控制由 generation/幂等性解决并返回权威冲突,Studio 吸收结构化 409 状态。超过五分钟或 owner 无法恢复时,不承诺进程替换恢复,而是在用户点击后创建增量新 Turn。原生 ArkClaw 和非 MPA Studio 路径不变。 - -## 任务 - -| ID | 工作 | 归属 | 依赖 | -| --- | --- | --- | --- | -| `T-1` | 持久 Turn 状态、安全点、幂等命令、参与方聚合 | mpa-agent | 无 | -| `T-2` | 在 mpa-agent 后封装 worker pause/resume 和超时恢复降级 | mpa-agent | `T-1` | -| `T-3` | 组合方舟目录、兼容性和 Turn 校验 | Studio BFF + mpa-agent | 无 | -| `T-4` | 持久 Session 挂载并原子创建 Turn 快照 | Studio BFF + mpa-agent | `T-1` | -| `T-5` | Skill Space 浏览/挂载及 Studio 工具详情/示例 | Studio | `T-4` | -| `T-6` | Typed topology endpoint、图和详情抽屉 | mpa-agent + Studio | `T-4` | -| `T-7` | Composer 布局和 Turn 模型/生命周期控制 | Studio frontend | `T-1`, `T-3` | -| `T-8` | 用量分类 UI 和环境/Secret 复制流程 | Studio | 无 | -| `T-9` | 浏览器、Runtime、竞态、刷新、安全、旧版 E2E | 双仓库 | `T-1`–`T-8` | -| `T-10` | 会话事件语义、重放安全的 thought/reasoning 投影、human-readable 工具活动、完整脱敏详情和可搜索模型选择 | Studio BFF + Studio frontend | `T-3` | - -## 验收 - -| 需求 | 验收证据 | 初始状态 | -| --- | --- | --- | -| `FR-1`, `FR-2` | 目录契约及真实双模型 Turn,证明主/worker 一致及非法 ID 无副作用 | `not_run` | -| `FR-3` | 正常/窄窗口、长模型 ID 浏览器截图与测试 | `not_run` | -| `FR-4` | 五类 Token 测试及重放/刷新证据 | `not_run` | -| `FR-5`, `FR-6` | 集成/真实 `running -> pausing -> paused -> resuming -> running`、五分钟降级、竞态和幂等 | `not_run` | -| `FR-7`, `FR-9` | 真实 Skill Space/工具 Session 挂载刷新保留并从下一 Turn 生效 | `not_run` | -| `FR-8` | 契约和浏览器证明只展示真实关系 | `not_run` | -| `FR-10` | 精确错误、Secret 不渲染、授权复制、普通用户 403、审计无值 | `not_run` | -| `FR-11` | 旧 Agent Card fixture 和浏览器降级 | `not_run` | -| `FR-12`, `FR-13` | 工具专属/嵌套/未知 fixture、大型脱敏结果复制/下载和浏览器检查 | `not_run` | -| `FR-14` | status/artifact 重放 fixture 及实时/历史/重连 reasoning/thought 展示 | `not_run` | -| `FR-15` | 键盘与输入过滤、无匹配状态、非终态禁用选择 | `not_run` | - -门禁遵循 `AGENTS.md` 与 `frontend/SPEC.md`:目标测试、共享契约广泛回归、前端 test/build、变更 Python Ruff/Pyright、真实浏览器正常/错误/取消/重试/窄窗口、pre-commit、两轮 Review 以及隔离 Runtime E2E。 - -## 风险与批准 - -安全点实现必须先证明 Runner 哪些边界可继续且不重复副作用。账号资源可能已开通但与 Runtime credential/base URL 不兼容,因此交集需 fail closed。大拓扑需限制节点数、按需详情、平移缩放及可访问列表。浏览器剪贴板策略失败时不得回退为渲染。 - -用户已批准:分期交付、实时方舟目录、不可变 Turn 模型、完整流程协作暂停、mpa-agent 生命周期所有权、Composer 不重叠、真实资源拓扑、下一 Turn 生效的 Session 挂载、管理员/开发者复制但不渲染 Secret。包含单一动态主按钮和用户触发五分钟降级语义的双语书面设计与组件契约已于 2026-09-13 批准。 - -## 设计评审记录 - -仓库评审发现并解决以下设计阻塞项: - -- **Worker 暂停契约差异:** mpa-codex-worker 的 `pause` 通过中断当前 Codex Turn 进入 paused,`resume` 通过 `thread/resume` 恢复 Session。mpa-agent 必须把该行为封装为完整流程安全点,并在五分钟后或 owner 不可恢复时只在用户点击继续后创建增量新 Turn;Studio 不直接协调 worker。 -- **mpa-agent 状态缺口:** 当前 A2A store 只有 running/cancel-requested/final 状态。`T-1` 是带旧行兼容的 schema/状态机迁移,不是纯 UI 扩展。 -- **安全点歧义:** 在具体 Runner 边界具备原子 checkpoint 和无重复副作用证明之前,禁止承诺进程重启后 resume。第一条纵向切片必须先完成并测试安全点 spike。 -- **模型目录重复风险:** Studio 已有 `frontend/server/model_catalog`;`T-3` 扩展其 freshness/stale/compatibility 元数据并与 Runtime capability 求交,不新建目录服务。 -- **Secret 接口复用风险:** 现有原始 API Key 路由仅证明 no-store 传输,不满足 Runtime 变量 RBAC/审计。Secret copy 使用独立角色保护和审计契约,不复用权限更弱的通用 resolver。 -- **范围顺序:** 按已批准方案分两期交付。第一期为模型目录/冻结、生命周期控制、用量、环境错误/Secret copy 和 Composer 布局;第二期为 Session Skill/工具挂载、工具说明和资源拓扑。第二期不得弱化第一期生命周期/快照契约。 - -当前无 P0/P1 产品设计阻塞。实现仍需用户审核本书面双语设计,并制定从安全点 spike 开始的实施计划。 diff --git a/prd-spec/features/mpa-studio-control-plane/2026-09-13-mpa-studio-control-plane-implementation-plan.md b/prd-spec/features/mpa-studio-control-plane/2026-09-13-mpa-studio-control-plane-implementation-plan.md deleted file mode 100644 index 0bcab7d23..000000000 --- a/prd-spec/features/mpa-studio-control-plane/2026-09-13-mpa-studio-control-plane-implementation-plan.md +++ /dev/null @@ -1,139 +0,0 @@ -# MPA Studio Control Plane Implementation Plan - -- Change ID: `mpa-studio-control-plane` -- Status: Phase 1 implemented; Phase 2 partially implemented -- Date: 2026-09-13 -- Chinese: [implementation-plan.zh.md](2026-09-13-mpa-studio-control-plane-implementation-plan.zh.md) -- Approved design: [design.md](2026-09-13-mpa-studio-control-plane-design.md) - -## Execution strategy - -Deliver two phases through vertical slices. Preserve current unrelated changes in both repositories and the independent mpa-agent branch. Each slice starts with failing contract tests, then implementation, targeted verification, and reconciliation. Do not publish a Runtime image until all local gates for that slice pass. - -## Phase 1 — Turn correctness and lifecycle - -### Slice 0 — Prove resumable safe points - -1. Characterize VeADK Runner boundaries around model response completion, tool start/result, and delegated worker start/result. -2. Add an isolated mpa-agent spike test that requests pause during each boundary and records whether resumption can continue without re-running an accepted model/tool side effect. -3. Classify boundaries as atomic/non-resumable or durable/resumable. Persist only the minimal checkpoint needed by proven boundaries. -4. Gate: no production pause claim until a red/green test proves one end-to-end safe point and a negative test proves no next operation begins after pause intent. - -Spike result (2026-09-13): `mpa-agent/tests/test_turn_safe_point_spike.py` proves -the `after_tool` ADK boundary in one live Invocation. The committed effect occurs -exactly once, the next model request remains blocked while paused, and in-process -resume continues the same Invocation without replaying the accepted user message -or completed tool. The store-backed gate separately verifies a `before_tool` -checkpoint blocks before an operation starts. This authorizes Slice 1 to use -cooperative callback gates. It does not prove process-replacement resume or -arbitrary in-flight provider/tool preemption; those remain explicitly unsupported -until a durable checkpoint can be validated. - -### Slice 1 — Durable whole-Turn controller - -1. Extend `app/stores/a2a_task_control.py` with migration-compatible states, generation, desired state, command idempotency, participant state, safe-point/checkpoint metadata, and classified control failure. -2. Add mpa-agent control/status routes adjacent to A2A ownership, not Studio-specific state. -3. Add cooperative checks before every next main-model, tool, and delegation boundary. -4. Map worker session handles to the owning Turn and wrap worker pause/resume/cancel. Add a worker interrupt contract only if existing cancel/continue cannot meet the approved semantics. -5. Make refresh/reconnect load persisted state and events. -6. Tests: store transition tables, old-row migration, duplicate command, stale generation, concurrent pause/cancel, worker failure, refresh, cancel terminality, interrupt/new-Turn continuation. - -### Slice 2 — Live model catalog and immutable Turn model - -1. Extend `frontend/server/model_catalog` response with observed time, stale state, and classified failure. Reuse its account activation/model join. -2. Add server-side intersection with mpa-agent model capability. Do not send API keys to the Runtime or browser. -3. Replace session-local model semantics with a Turn draft in Studio. Freeze ID on send and persist it in mpa-agent's Turn snapshot before execution. -4. Make primary model and delegated worker resolve the same snapshot. Reject stale/invalid IDs before side effects. -5. Tests: cache/stale/no-cache failure, capability intersection, Turn immutability, main/worker consistency, invalid ID, old Runtime single-model behavior. - -### Slice 3 — Composer, controls, and usage - -1. Refactor Composer layout into target/header and responsive footer slots. Remove competing positioning. -2. Render lifecycle actions from server-provided allowed actions; disable model and conflicting actions while non-terminal. -3. Expose current Turn and Session cumulative input/output/reasoning/cache/total values. Preserve monotonic-delta aggregation. -4. Browser gates at normal and narrow widths, long Agent/model names, every control state, missing Token categories, keyboard/IME, refresh, retry, and error states. - -### Slice 3A — Conversation activity fidelity - -1. Add A2A fixtures where the same cumulative reasoning is emitted through working status and artifact updates, plus legitimate repeated text that must remain. -2. Preserve reasoning versus Agent-thought metadata through projection and frontend blocks. -3. Add tool-specific presentation adapters for goal, command, search, read, file-change, MCP, and unknown tools, including nested Runtime payloads. -4. Bound previews while keeping complete sanitized copy/download access and identical redaction on every path. -5. Replace the Composer native model select with the existing searchable compact selector and retain the Turn lock. -6. Tests/browser: streaming/history/reconnect parity, no duplicate reasoning, distinct thought labels, meaningful tool rows, complete safe details, keyboard filtering, no-match, running-disabled, and narrow layout. - -### Slice 4 — Environment failures and secret copy - -1. Split environment load failures into authorization, Runtime unavailable, version missing, Manifest failure, and unsupported secret access. -2. Add an administrator/developer-only Runtime secret-copy POST with no-store headers and value-free audit. -3. Write clipboard directly and discard the value without rendering/persisting/telemetry. -4. Tests: RBAC matrix, cache headers, audit redaction, missing variable, upstream failure, clipboard rejection, DOM/storage/log absence. - -### Phase 1 release gate - -- Targeted tests in both repositories. -- mpa-agent broad affected suite. -- VeADK Python targeted plus applicable parallel regression. -- frontend test/build and real browser matrix. -- changed-file Ruff/Pyright and pre-commit. -- two review rounds. -- isolated Runtime E2E: catalog, model freeze, pause/resume safe point, cancel, interrupt, refresh recovery, five Token categories, environment errors, authorized/unauthorized secret copy. - -## Phase 2 — Session resources and operational topology - -### Slice 5 — Session resource revision and Turn snapshots - -1. Define one Session resource document/revision for Skill versions, Studio tool revisions, and environment/workspace bindings. -2. Validate updates server-side and atomically resolve one immutable Turn resource snapshot. -3. Preserve invalid/deleted/revoked entries visibly and prevent execution. -4. Tests: revision conflicts, edits during active Turn apply next Turn, refresh persistence, removed versions, defaults unchanged. - -### Slice 6 — Skill Space and Studio tool workspace - -1. Reuse existing Skill Space APIs to list Spaces, Skills, immutable versions, and details in conversation context. -2. Add Session mount/unmount actions with next-Turn labels. -3. Add Studio tool detail containing purpose, scenarios, schema, permissions, examples, source, revision, and mount state. -4. Tests/browser: loading/empty/error/retry, version selection, permissions, invalid resource, refresh persistence, next-Turn execution. - -### Slice 7 — Typed resource topology - -1. Add mpa-agent typed topology data derived from Agent defaults, Turn/Session snapshots, actual worker handles, MCP, environment, knowledge, and sub-Agent configuration. -2. Render an interactive bounded graph with pan/zoom/fit, detail drawer, and accessible list representation. -3. Keep unmounted optional resources outside the active graph as add affordances. -4. Tests/browser: single Agent plus Sandbox, worker state transitions, Skills/tools/MCP/environment, absent knowledge/sub-Agent, large graph bounds, narrow window, keyboard access. - -### Phase 2 release gate - -Repeat repository gates and live Runtime E2E with a real Skill Space, Studio tools, environment, worker delegation, refresh, invalid-resource case, and topology state changes. - -## File ownership expectations - -- mpa-agent: `app/stores/a2a_task_control.py`, A2A executor/app/routes, invocation context, model/delegation integration, Session resource/topology services, tests. -- veadk-python backend: `frontend/server/model_catalog`, Session resource and environment/secret routes, Runtime A2A proxy/types, tests. -- veadk-python frontend: `App.tsx`, `Composer.tsx`, token usage, topology, Skill/tool/resource pickers, i18n/styles/tests, regenerated `veadk/webui` assets. -- mpa-codex-worker: only if Slice 1 proves a missing worker interrupt/checkpoint contract; follow its own PRD/spec and gates before edits. - -## Stop conditions - -Stop and return to design review if the safe-point spike cannot prove side-effect-free resume, Ark model compatibility cannot be resolved server-side, or secret-copy auditing cannot be implemented without exposing the value to logs/state. Do not weaken acceptance criteria to proceed. - -## Verification record — 2026-09-13 - -- `pass` — frontend suite: `npm --prefix frontend test` (`1096` passed). -- `pass` — production assets: `npm --prefix frontend run build`; the final dialog-layout fix produced and served `assets/app/index-CozNp3D8.js`. -- `pass` — VeADK Runtime proxy, A2A projection, and RBAC targets: `235` passed. -- `pass` — mpa-agent executor, delegation, runtime plugin, lifecycle, safe-point, and task-store targets: `173` passed. -- `pass` — Runtime `r-yeuujrrcowb21078p9jh` version `52` reached `Ready` on image `agentkit/mpa_agent_studio:resource-topology-v52b-20260913` (digest `sha256:205a82a5a32094c6da93e1a6b1ae4f7b70a883021b06489b844a28d1214eaf79`). Its Agent Card advertises 23 compatible models; the Studio proxy intersects them with account activation and currently exposes 14 selectable models, plus whole-Turn lifecycle capability and the configured `Agent -> Sandbox` topology. A live Turn using activated alternate model `glm-5-2-260617` completed successfully and retained that model in the Turn UI. -- `pass` — system-Chrome CDP checks at 1440 px and 375 px found no horizontal overflow. The model popover is independent from the Agent picker, exposes `搜索模型`, and filtering returns only matching selectable models. -- `pass` — a live Turn showed `pause`, `interrupt`, and `cancel`; pause moved to `pausing` at a cooperative safe point, and terminal completion removed all lifecycle buttons. The completed Turn showed separated model reasoning and final answer plus input/output usage. -- `pass` — the Session Skill action opened a real Skill Space picker and loaded account spaces and versioned Skills. -- `fixed during review` — the Skill Space dialog header used the three-column tool-dialog grid without an icon, squeezing its title into one-word lines. It now uses the existing iconless two-column variant, with a regression assertion. -- `fixed during review` — Runtime capability alone exposed three unactivated models and a live selection failed with Ark `NotFoundError`. The Studio proxy now intersects Runtime capability with the account model catalog while retaining the Runtime default as a compatibility fallback. -- `fixed after visual acceptance` — the new-chat model trigger now shares the Agent trigger's 36 px borderless treatment and its container reserves a non-overlapping slot before the 36 px send action. Runtime list items now include `agentCategory`; stable `veadk:agent-type=mpa` metadata is preferred and existing `/mpa_agent*:` image repositories provide compatibility classification into a dedicated `MPA Agent` picker category. -- `blocked` — Pyright is not installed in the repository environment (`Failed to spawn: pyright`). -- `blocked by optional dependencies` — the broader VeADK regression previously completed with `4030 passed, 12 skipped, 2 xfailed`, plus seven failures and two collection errors caused by missing optional `llama_index`/`anthropic` dependencies; affected targeted suites pass. -- `known generated-artifact exception` — `git diff --check` passes when excluding generated `veadk/webui/website-integration.js`; that third-party bundle contains trailing whitespace. - -## Explicit remaining Phase 2 scope - -Session Skill/Studio-tool/environment selections currently persist in browser local storage and are submitted with the next Turn. A server-owned Session resource document with revision conflicts, invalid-resource retention, and atomic resource resolution is not implemented. Dynamic worker lifecycle nodes are also not yet projected into the topology. These items must not be reported as complete. diff --git a/prd-spec/features/mpa-studio-control-plane/2026-09-13-mpa-studio-control-plane-implementation-plan.zh.md b/prd-spec/features/mpa-studio-control-plane/2026-09-13-mpa-studio-control-plane-implementation-plan.zh.md deleted file mode 100644 index a02319e13..000000000 --- a/prd-spec/features/mpa-studio-control-plane/2026-09-13-mpa-studio-control-plane-implementation-plan.zh.md +++ /dev/null @@ -1,130 +0,0 @@ -# MPA Studio 控制面实施计划 - -- Change ID:`mpa-studio-control-plane` -- 状态:第一期已实现;第二期部分实现 -- 日期:2026-09-13 -- English:[implementation-plan.md](2026-09-13-mpa-studio-control-plane-implementation-plan.md) -- 已批准设计:[design.zh.md](2026-09-13-mpa-studio-control-plane-design.zh.md) - -## 执行策略 - -按两期纵向切片交付。保留双仓库现有无关变更和 mpa-agent 独立分支。每个切片先写失败契约测试,再实现、目标验证和文档对齐。本地门禁未通过前不发布 Runtime 镜像。 - -## 第一期 — Turn 正确性与生命周期 - -### Slice 0 — 证明可恢复安全点 - -1. 刻画 VeADK Runner 在模型响应完成、工具开始/结果、worker 委托开始/结果处的边界。 -2. 添加隔离 mpa-agent spike 测试,在每个边界请求 pause,记录 resume 是否会重复已接受模型/工具副作用。 -3. 将边界分类为原子不可恢复或持久可恢复,只保存已证明边界所需最小 checkpoint。 -4. 门禁:在一个 E2E 安全点 red/green 测试及“pause 意图后禁止下一操作”反向测试通过前,不声明生产 pause。 - -Spike 结果(2026-09-13):`mpa-agent/tests/test_turn_safe_point_spike.py` 已在 -同一个真实 ADK Invocation 中证明 `after_tool` 边界:已提交的副作用恰好发生一次, -暂停期间下一次模型请求保持阻塞,进程内恢复会继续同一个 Invocation,不会重放已接受 -的用户消息或已完成工具。基于 Store 的 gate 另行验证 `before_tool` checkpoint 会在 -操作启动前阻塞。该结果允许 Slice 1 使用协作式 callback gate,但不证明进程替换恢复 -或任意在途 Provider/工具抢占;在持久 checkpoint 得到验证前,这两项继续明确为不支持。 - -### Slice 1 — 持久完整 Turn 控制器 - -1. 扩展 `app/stores/a2a_task_control.py`,增加兼容迁移的状态、generation、期望状态、命令幂等、参与方状态、安全点/checkpoint 元数据和分类控制失败。 -2. 在 A2A 所有权旁新增 mpa-agent control/status 路由,不建立 Studio 私有状态。 -3. 在每个下一主模型、工具和委托边界前增加协作检查。 -4. 把 worker session handle 映射到 Turn,封装 worker pause/resume/cancel;只有现有 cancel/continue 不能满足批准语义时才新增 worker interrupt 契约。 -5. 刷新/重连读取持久状态和事件。 -6. 测试:状态转换表、旧行迁移、重复命令、过期 generation、并发 pause/cancel、worker 失败、刷新、cancel 终态、interrupt/新 Turn 继续。 - -### Slice 2 — 实时模型目录与不可变 Turn 模型 - -1. 扩展 `frontend/server/model_catalog`,增加观测时间、stale 和分类失败,复用账号 activation/model join。 -2. 服务端与 mpa-agent 模型 capability 求交,不把 API Key 发送给 Runtime 或浏览器。 -3. 把 Studio Session 本地模型语义改为 Turn 草稿;发送时冻结,并在执行前持久化到 mpa-agent Turn 快照。 -4. 主模型和 worker 委托读取同一快照;过期/非法 ID 在副作用前拒绝。 -5. 测试:缓存/stale/无缓存失败、capability 交集、Turn 不可变、主/worker 一致、非法 ID、旧 Runtime 单模型。 - -### Slice 3 — Composer、控制与用量 - -1. Composer 拆成 target/header 和响应式 footer 槽位,删除竞争定位。 -2. 根据服务端 allowed actions 渲染控制;非终态禁用模型和冲突操作。 -3. 展示当前 Turn 与 Session 累计的输入/输出/推理/缓存/总量,保留正增量聚合。 -4. 浏览器门禁覆盖正常/窄宽度、长 Agent/模型名、全部控制态、缺失 Token、键盘/IME、刷新、重试和错误。 - -### Slice 3A — 会话活动保真 - -1. 增加同一累计 reasoning 经 working status 与 artifact update 发出的 A2A fixture,并加入必须保留的合法重复文本。 -2. 在投影和前端 Block 中保留 reasoning 与 Agent thought 元数据。 -3. 为目标、命令、搜索、读取、文件修改、MCP 和未知工具增加专属展示适配,包括嵌套 Runtime payload。 -4. 限制预览,同时保留完整脱敏复制/下载能力,并让所有路径使用相同脱敏规则。 -5. 使用现有可搜索紧凑选择器替换 Composer 原生模型 select,并保留 Turn 锁定。 -6. 测试/浏览器:实时/历史/重连一致、reasoning 不重复、thought 标签区分、工具行有意义、详情完整安全、键盘过滤、无匹配、运行时禁用及窄窗口。 - -### Slice 4 — 环境错误与 Secret 复制 - -1. 把环境读取失败拆分为鉴权、Runtime 不可用、版本不存在、Manifest 失败、不支持 Secret。 -2. 增加管理员/开发者专用 Runtime Secret copy POST,no-store 且审计不含值。 -3. 直接写剪贴板并丢弃,不渲染/持久化/遥测。 -4. 测试:RBAC、缓存头、审计脱敏、变量不存在、上游失败、剪贴板拒绝、DOM/存储/日志无值。 - -### 第一期发布门禁 - -双仓库目标测试、mpa-agent 广泛影响测试、VeADK 适用并行回归、前端 test/build 和真实浏览器矩阵、变更 Python Ruff/Pyright、pre-commit、两轮 Review,以及隔离 Runtime E2E:目录、模型冻结、pause/resume 安全点、cancel、interrupt、刷新恢复、五类 Token、环境错误、授权/未授权 Secret copy。 - -## 第二期 — Session 资源与运行拓扑 - -### Slice 5 — Session 资源 revision 与 Turn 快照 - -1. 定义一个 Session 资源文档/revision,包含 Skill 版本、Studio 工具 revision、环境/Workspace 绑定。 -2. 服务端校验更新并原子解析一个不可变 Turn 资源快照。 -3. 删除/撤权/失效资源保持可见且禁止执行。 -4. 测试:revision 冲突、活动 Turn 编辑下一 Turn 生效、刷新持久、版本删除、默认值不变。 - -### Slice 6 — Skill Space 与 Studio 工具工作区 - -1. 复用 Skill Space API,在会话中列出 Space、Skill、不可变版本和详情。 -2. 增加 Session 挂载/移除并标记下一 Turn 生效。 -3. Studio 工具详情展示用途、场景、schema、权限、示例、来源、revision 和挂载状态。 -4. 测试/浏览器:加载/空/错误/重试、版本选择、权限、失效资源、刷新持久、下一 Turn 执行。 - -### Slice 7 — Typed 资源拓扑 - -1. mpa-agent 从 Agent 默认值、Turn/Session 快照、真实 worker handle、MCP、环境、知识和子 Agent 配置生成 typed topology。 -2. 展示有边界的交互图,支持平移/缩放/适应、详情抽屉和可访问列表。 -3. 未挂载可选资源保持为图外添加入口。 -4. 测试/浏览器:单 Agent + Sandbox、worker 状态、Skill/工具/MCP/环境、缺失知识/子 Agent、大图边界、窄窗口、键盘。 - -### 第二期发布门禁 - -重复仓库门禁,并使用真实 Skill Space、Studio 工具、环境、worker 委托、刷新、失效资源和拓扑状态变化执行 Runtime E2E。 - -## 文件归属预期 - -- mpa-agent:`app/stores/a2a_task_control.py`、A2A executor/app/routes、invocation context、模型/委托集成、Session 资源/拓扑服务及测试。 -- veadk-python 后端:`frontend/server/model_catalog`、Session 资源及环境/Secret 路由、Runtime A2A proxy/types 及测试。 -- veadk-python 前端:`App.tsx`、`Composer.tsx`、Token 用量、拓扑、Skill/工具/资源选择器、i18n/styles/tests 及重新生成 `veadk/webui`。 -- mpa-codex-worker:仅当 Slice 1 证明缺少 worker interrupt/checkpoint 契约时修改;编辑前遵循其独立 PRD/spec 和门禁。 - -## 停止条件 - -如果安全点 spike 无法证明无副作用 resume、方舟模型兼容性无法服务端解析,或 Secret copy 审计无法避免值进入日志/状态,则停止并返回设计评审,不得弱化验收继续。 - -## 验证记录 — 2026-09-13 - -- `pass` — 前端测试:`npm --prefix frontend test`(`1096` 项通过)。 -- `pass` — 生产资源:`npm --prefix frontend run build`;最终弹窗布局修复生成并实际服务 `assets/app/index-CozNp3D8.js`。 -- `pass` — VeADK Runtime proxy、A2A 投影和 RBAC 目标测试:`235` 项通过。 -- `pass` — mpa-agent executor、delegation、runtime plugin、生命周期、安全点和 task-store 目标测试:`173` 项通过。 -- `pass` — Runtime `r-yeuujrrcowb21078p9jh` 版本 `52` 使用镜像 `agentkit/mpa_agent_studio:resource-topology-v52b-20260913`(digest `sha256:205a82a5a32094c6da93e1a6b1ae4f7b70a883021b06489b844a28d1214eaf79`)达到 `Ready`。其 Agent Card 声明 23 个兼容模型;Studio 代理与账号激活状态求交后当前展示 14 个可选模型,并提供完整 Turn 生命周期能力和已配置的 `Agent -> Sandbox` 拓扑。使用已激活备选模型 `glm-5-2-260617` 的真实 Turn 成功完成,且 Turn UI 保持显示该模型。 -- `pass` — 系统 Chrome CDP 在 1440 px 与 375 px 下均无横向溢出。模型弹层与智能体选择器相互独立,提供 `搜索模型`,输入筛选后只返回匹配的可选模型。 -- `pass` — 真实 Turn 展示 `pause`、`interrupt`、`cancel`;pause 在协作式安全点进入 `pausing`,终态完成后所有生命周期按钮消失。完成 Turn 分开展示模型推理、最终回答和输入/输出用量。 -- `pass` — Session Skill 操作打开真实 Skill Space 选择器,并加载账号 Space 和带版本 Skill。 -- `评审中已修复` — Skill Space 弹窗标题误用了带图标的三列工具弹窗网格,导致标题逐字换行;现已使用既有无图标双列变体,并增加回归断言。 -- `评审中已修复` — 仅按 Runtime capability 展示时包含三个未激活模型,真实选择会收到方舟 `NotFoundError`。Studio 代理现与账号模型目录求交,同时保留 Runtime 默认模型作为兼容回退。 -- `视觉验收后已修复` — 新会话模型触发器现与 Agent 触发器统一为 36 px 无边框样式,并在 36 px 发送按钮前保留互不重叠的独立槽位。Runtime 列表新增 `agentCategory`;优先使用稳定的 `veadk:agent-type=mpa` 元数据,并以既有 `/mpa_agent*:` 镜像仓库作为兼容识别依据,归入独立的 `MPA 智能体` 类别。 -- `blocked` — 仓库环境未安装 Pyright(`Failed to spawn: pyright`)。 -- `被可选依赖阻塞` — 更广泛的 VeADK 回归此前得到 `4030 passed, 12 skipped, 2 xfailed`,另有七个失败和两个收集错误,原因是缺少可选 `llama_index`/`anthropic` 依赖;受影响目标测试均通过。 -- `已知生成产物例外` — 排除生成文件 `veadk/webui/website-integration.js` 后 `git diff --check` 通过;该第三方 bundle 自带 trailing whitespace。 - -## 明确剩余的第二期范围 - -Session Skill/Studio 工具/环境选择当前保存在浏览器 localStorage,并随下一 Turn 提交。服务端 Session 资源文档、revision 冲突、失效资源保留和原子资源解析尚未实现;拓扑也尚未投影动态 worker 生命周期节点。不得把这些项目声明为已完成。 diff --git a/prd-spec/features/mpa-studio-workload-identity/2026-09-20-mpa-studio-workload-identity.md b/prd-spec/features/mpa-studio-workload-identity/2026-09-20-mpa-studio-workload-identity.md deleted file mode 100644 index aefca6ebb..000000000 --- a/prd-spec/features/mpa-studio-workload-identity/2026-09-20-mpa-studio-workload-identity.md +++ /dev/null @@ -1,124 +0,0 @@ -# MPA Studio Workload Identity Provisioning - -- **Change ID:** `mpa-studio-workload-identity` -- **Created / revised:** 2026-09-20 -- **Status:** implemented -- **Chinese:** [2026-09-20-mpa-studio-workload-identity.zh.md](2026-09-20-mpa-studio-workload-identity.zh.md) -- **Component:** [MPA Runtime Provisioning](../../../specs/mpa-runtime-provisioning/README.md) -- **Predecessor:** [MPA Agent One-click Provisioning](../mpa-agent-oneclick-provision/2026-09-10-mpa-agent-oneclick-provision-design.md) - -## 1. Background and evidence - -`veadk mpa create` currently generates `MPA_AGENT_ID` as `mi-` plus twelve lowercase alphanumeric characters and disables ArkClaw identity startup. The deployed mpa-agent still derives its outbound workload identity as `arkclaw-{CLAW_SPACE_ID}-workload` plus `MPA_AGENT_ID`. In the Studio topology that pool does not exist, so outbound TIP exchange fails even though the Runtime itself is healthy. - -The approved Studio convention is one account-and-region scoped pool named `agentkit-studio-workload` and one identity per MPA instance named `{MPA_AGENT_ID}-studio`. The base MPA id remains the database, Runtime, Tool, channel, and observability identity. - -## 2. Goals and non-goals - -### Goals - -1. Generate and validate canonical MPA ids in the form `mi-[0-9a-z]{12}`. -2. Idempotently create or reuse `agentkit-studio-workload` and `{MPA_AGENT_ID}-studio` before other provisioning side effects. -3. Inject explicit workload pool and identity names into the Runtime. -4. Preserve the existing ArkClaw-derived identity behavior for non-Studio mpa-agent deployments through a companion consumer change. -5. Produce actionable, redacted failures for missing Identity permissions. - -### Non-goals - -- Creating Identity UserPools, clients, groups, or ArkClaw control-plane records. -- Enabling inbound A2A TIP verification; APIG key authentication remains the Studio boundary. -- Automatically migrating already-running Runtimes or deleting unused workload identities. -- Porting the internal bit layout of ArkClaw's Go `ubw/id` generator. VeADK retains its existing secure random source and twelve-character suffix as approved. - -## 3. Scenarios and requirements - -### Scenario A: new Studio MPA - -Given valid cloud credentials and no Studio workload pool, when the operator runs `veadk mpa create`, VeADK creates the shared pool, creates `{MPA_AGENT_ID}-studio`, injects both names, and continues the existing Skill Space, Tool, metadata, and Runtime flow. - -### Scenario B: retry or second MPA - -Given the pool or identity already exists, the same command reuses exact-name resources. A concurrent create conflict is followed by a read and validation rather than treated as failure. - -### Scenario C: insufficient permission - -Given credentials without an Identity get/create action, provisioning stops before Tool creation, database writes, or Runtime creation and reports the failed resource and required action without printing credentials. - -### Requirements - -- **FR-1:** `generate_mpa_agent_id()` returns `mi-` plus twelve lowercase base36 characters. Explicit `--mpa-agent-id` values must match the same canonical form. -- **FR-2:** The workload pool name is the constant `agentkit-studio-workload`; the identity name is `{MPA_AGENT_ID}-studio`. -- **FR-3:** Workload provisioning is get-or-create and exact-name idempotent. `AlreadyExists` is resolved by re-reading the resource. Other errors fail closed. -- **FR-4:** Identity resources are ensured after local input validation and before Skill Space, Tool, PostgreSQL, VeFaaS, or Runtime mutations. -- **FR-5:** `--dry-run` displays the MPA id, pool, identity, and masked Runtime environment without cloud or database calls. -- **FR-6:** Runtime environments contain `MPA_WORKLOAD_POOL_NAME` and `MPA_WORKLOAD_IDENTITY_NAME`. Existing `IDENTITY_STARTUP_ENABLED=false` and `A2A_TIP_VERIFY_ENABLED=false` remain unchanged. -- **FR-7:** The management identity requires `id:GetWorkloadPool`, `id:CreateWorkloadPool`, `id:GetWorkloadIdentity`, and `id:CreateWorkloadIdentity`; authorization failures identify the required action. -- **FR-8:** The companion mpa-agent change uses both explicit names when both are configured, uses the existing ArkClaw derivation when neither is configured, and rejects a partial pair. - -## 4. Design and contract impact - -### 4.1 VeADK ownership - -A focused `veadk.integrations.mpa.mpa_identity` module owns naming and get-or-create orchestration. It reuses additive, pool-aware methods on the existing `veadk.integrations.ve_identity.IdentityClient`, the installed Volcano Engine Identity SDK, and the same management credentials already used by `veadk mpa create`. Existing IdentityClient signatures remain compatible. - -The orchestration becomes: - -`validate id/input -> ensure pool -> ensure identity -> ensure Skill Space -> ensure Tool -> pre-seed mpa_meta -> provision Runtime -> finalize metadata -> verify`. - -Created identity resources are intentionally retained after a later failure so an identical retry converges. The fixed shared pool is never deleted by this command. - -### 4.2 Runtime consumer contract - -VeADK injects: - -```text -MPA_WORKLOAD_POOL_NAME=agentkit-studio-workload -MPA_WORKLOAD_IDENTITY_NAME=mi-xxxxxxxxxxxx-studio -``` - -The independently versioned mpa-agent must consume the pair. This change is not complete for live TIP exchange until its companion commit is present in the deployed image. - -### 4.3 Permissions and security - -Management AK/SK remain local control-plane inputs and are never injected into Runtime env. Identity API failures expose action and resource names only. Pool and identity names are non-secret. Runtime workload-token permission remains an execution-role concern and is not broadened by management provisioning. - -### 4.4 Compatibility - -Generated ids keep their existing twelve-character suffix. Explicit non-canonical ids are rejected for new `veadk mpa create` invocations; existing deployments are unaffected until reprovisioned. Non-Studio mpa-agent deployments retain the legacy derived names when the two new variables are absent. - -No HTTP, SSE, database schema, frontend, session, or event contract changes. No additional dependency is required. - -## 5. Implementation tasks - -- **T-1:** Add canonical id validation and workload naming helpers in `mpa_provision.py`. -- **T-2:** Add the minimal Identity SDK get-or-create module and error mapping. -- **T-3:** Wire identity provisioning and dry-run output into `cli_mpa.py`; report the required action on authorization failure. -- **T-4:** Update Runtime env assembly and the bilingual component spec. -- **T-5:** Add the companion mpa-agent configuration and resolution change. -- **T-6:** Add tests first, run targeted regression, pre-commit, and the required broader Python gate. - -## 6. Verification and acceptance - -| Requirement | Task | Acceptance criterion | Command | Result | -| --- | --- | --- | --- | --- | -| FR-1, FR-2 | T-1 | AC-1: generated and explicit ids plus derived identity names are canonical | `uv run --extra dev pytest tests/integrations/test_mpa_provision_env.py` | pass; included in the 39-test targeted run | -| FR-3, FR-4, FR-7 | T-2, T-3 | AC-2: create, reuse, race, and authorization failure paths are deterministic and side-effect ordered | `uv run --extra dev pytest tests/integrations/test_mpa_identity.py tests/cli/test_cli_mpa.py` | pass; included in the 39-test targeted run | -| FR-5, FR-6 | T-3, T-4 | AC-3: dry-run is side-effect free and Runtime receives the explicit pair | `uv run --extra dev pytest tests/integrations/test_mpa_provision_env.py tests/cli/test_cli_mpa.py` | pass; included in the 39-test targeted run | -| FR-8 | T-5 | AC-4: the mpa-agent resolves explicit, legacy, and partial-pair cases | companion repository targeted pytest | pass; 47 tests and 100% diff coverage | -| all | T-6 | AC-5: repository pre-commit and affected regression pass | `uv run --extra dev pre-commit run --all-files`; `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` | pass for targeted tests and changed-line coverage (39 tests, 100%); broad run reached 4563 passed, 12 skipped, 2 xfailed, then reported 8 optional-dependency failures; after installing `extensions` and `sandbox`, all 13 affected tests passed | -| all | T-6 | AC-6: an authorized isolated account can obtain a workload token using the new names | manual cloud smoke with redacted output | not_run; requires explicit live credentials and deployed companion image | - -## 7. Risks and recovery - -- Fixed pool creation requires account-level Identity permission. Missing permissions stop before other provisioning mutations. -- A later failure can leave an unused identity. Re-running the same id reuses it; manual cleanup remains an operator action. -- A VeADK Runtime using the new env pair with an old mpa-agent image continues using the legacy names. Release notes and live verification must identify the companion image revision. -- A shared pool concentrates identities in one namespace. Exact canonical MPA ids prevent accidental aliasing; pool capacity monitoring remains an operational concern outside this change. - -## 8. Review and delivery record - -- User approval: 2026-09-20, approved the two-repository plan and retained the twelve-character suffix. -- Design review: 2026-09-20 direct review against repository requirements; no blocking ambiguity remains. The identity side effect, permission set, idempotency, failure ordering, compatibility, and companion consumer contract are explicit. -- Implementation review: two passes covered naming and API contracts, ordering/idempotency/error handling, secret boundaries, compatibility, and test quality; no open findings remain. -- Verification: VeADK targeted tests passed (39), changed-line coverage is 100%, and the initially missing optional-dependency cases passed after installing the declared `extensions` and `sandbox` extras. The companion mpa-agent targeted tests passed (47), its full coverage gate passed (2818 passed, 21 skipped, 95.05% total), and changed-line coverage is 100%. -- Residual verification gap: AC-6 remains `not_run` because it requires an authorized live account and a Runtime deployed with the companion image. diff --git a/prd-spec/features/mpa-studio-workload-identity/2026-09-20-mpa-studio-workload-identity.zh.md b/prd-spec/features/mpa-studio-workload-identity/2026-09-20-mpa-studio-workload-identity.zh.md deleted file mode 100644 index d15f98aab..000000000 --- a/prd-spec/features/mpa-studio-workload-identity/2026-09-20-mpa-studio-workload-identity.zh.md +++ /dev/null @@ -1,124 +0,0 @@ -# MPA Studio 工作负载身份创建 - -- **变更 ID:** `mpa-studio-workload-identity` -- **创建 / 修订日期:** 2026-09-20 -- **状态:** implemented -- **英文版:** [2026-09-20-mpa-studio-workload-identity.md](2026-09-20-mpa-studio-workload-identity.md) -- **组件:** [MPA Runtime 创建](../../../specs/mpa-runtime-provisioning/README.zh.md) -- **前序设计:** [MPA Agent 一键创建](../mpa-agent-oneclick-provision/2026-09-10-mpa-agent-oneclick-provision-design.zh.md) - -## 1. 背景与证据 - -`veadk mpa create` 当前把 `MPA_AGENT_ID` 生成为 `mi-` 加十二位小写字母数字,并关闭 ArkClaw 身份启动。已部署的 mpa-agent 仍把出站工作负载身份推导为 `arkclaw-{CLAW_SPACE_ID}-workload` 和 `MPA_AGENT_ID`。Studio 拓扑中不存在该 Pool,因此即使 Runtime 健康,出站 TIP 交换仍会失败。 - -已批准的 Studio 约定是:账号和地域内共享一个名为 `agentkit-studio-workload` 的 Pool,每个 MPA 实例使用 `{MPA_AGENT_ID}-studio` 作为 Identity。基础 MPA ID 仍然用于数据库、Runtime、Tool、渠道和可观测性。 - -## 2. 目标与非目标 - -### 目标 - -1. 生成并校验 `mi-[0-9a-z]{12}` 形式的规范 MPA ID。 -2. 在其他创建副作用之前,幂等创建或复用 `agentkit-studio-workload` 和 `{MPA_AGENT_ID}-studio`。 -3. 向 Runtime 注入明确的 WorkloadPool 和 WorkloadIdentity 名称。 -4. 通过配套消费者改动,为非 Studio mpa-agent 部署保留现有 ArkClaw 推导行为。 -5. 对 Identity 权限缺失给出可执行且脱敏的错误。 - -### 非目标 - -- 创建 Identity UserPool、client、group 或 ArkClaw 控制面记录。 -- 开启入站 A2A TIP 校验;Studio 边界继续使用 APIG key 鉴权。 -- 自动迁移已运行的 Runtime 或删除未使用的工作负载身份。 -- 移植 ArkClaw Go `ubw/id` 生成器的内部位布局。VeADK 按已批准方案保留现有安全随机源和十二位后缀。 - -## 3. 场景与需求 - -### 场景 A:创建新的 Studio MPA - -在云凭据有效且 Studio WorkloadPool 不存在时,运维执行 `veadk mpa create`,VeADK 创建共享 Pool、创建 `{MPA_AGENT_ID}-studio`、注入两个名称,然后继续既有 Skill Space、Tool、元数据和 Runtime 流程。 - -### 场景 B:重试或创建第二个 MPA - -Pool 或 Identity 已存在时,命令按精确名称复用。并发创建冲突后重新读取并校验,而不是直接失败。 - -### 场景 C:权限不足 - -凭据缺少 Identity get/create 动作时,流程在创建 Tool、写数据库或创建 Runtime 前停止,只报告失败资源和所需动作,不输出凭据。 - -### 功能需求 - -- **FR-1:** `generate_mpa_agent_id()` 返回 `mi-` 加十二位小写 base36 字符;显式 `--mpa-agent-id` 必须符合相同规范。 -- **FR-2:** WorkloadPool 固定为 `agentkit-studio-workload`;Identity 为 `{MPA_AGENT_ID}-studio`。 -- **FR-3:** 工作负载资源采用 get-or-create 和精确名称幂等;`AlreadyExists` 后重新读取,其他错误关闭式失败。 -- **FR-4:** 在本地输入校验后、Skill Space、Tool、PostgreSQL、VeFaaS 或 Runtime 修改前创建身份资源。 -- **FR-5:** `--dry-run` 展示 MPA ID、Pool、Identity 和脱敏 Runtime 环境,不调用云或数据库。 -- **FR-6:** Runtime 环境包含 `MPA_WORKLOAD_POOL_NAME` 和 `MPA_WORKLOAD_IDENTITY_NAME`;保持 `IDENTITY_STARTUP_ENABLED=false` 和 `A2A_TIP_VERIFY_ENABLED=false`。 -- **FR-7:** 管理身份需要 `id:GetWorkloadPool`、`id:CreateWorkloadPool`、`id:GetWorkloadIdentity`、`id:CreateWorkloadIdentity`;鉴权失败时标明所需 action。 -- **FR-8:** 配套 mpa-agent 在两个显式名称都存在时使用它们;都不存在时使用 ArkClaw 旧推导;只配置一个时拒绝启动。 - -## 4. 设计与契约影响 - -### 4.1 VeADK 职责 - -新增聚焦 MPA 的 `veadk.integrations.mpa.mpa_identity` 模块,负责名称和 get-or-create 编排。它复用现有 `veadk.integrations.ve_identity.IdentityClient` 上新增的兼容式、支持 Pool 的方法、已有火山引擎 Identity SDK,以及 `veadk mpa create` 已使用的管理凭据。现有 IdentityClient 方法签名保持兼容。 - -编排顺序变为: - -`校验 ID/输入 -> ensure Pool -> ensure Identity -> ensure Skill Space -> ensure Tool -> 预写 mpa_meta -> 创建 Runtime -> 回写元数据 -> 验证`。 - -后续步骤失败后保留已创建的身份资源,使同 ID 重试可以收敛。该命令不会删除固定共享 Pool。 - -### 4.2 Runtime 消费契约 - -VeADK 注入: - -```text -MPA_WORKLOAD_POOL_NAME=agentkit-studio-workload -MPA_WORKLOAD_IDENTITY_NAME=mi-xxxxxxxxxxxx-studio -``` - -独立版本的 mpa-agent 必须消费这组变量。在部署镜像包含配套提交前,真实 TIP 交换不能视为完成。 - -### 4.3 权限与安全 - -管理 AK/SK 只作为本地控制面输入,绝不注入 Runtime 环境。Identity API 错误只暴露 action 和资源名称。Pool 和 Identity 名称不是秘密。Runtime 获取 workload token 的权限仍由执行角色负责,本次管理面创建不扩大该权限。 - -### 4.4 兼容性 - -自动生成 ID 保持现有十二位后缀。新执行的 `veadk mpa create` 会拒绝显式非规范 ID;已有部署在重新创建前不受影响。非 Studio mpa-agent 在两个新变量都不存在时继续使用旧名称推导。 - -不修改 HTTP、SSE、数据库 schema、前端、session 或 event 契约,也不新增依赖。 - -## 5. 实现任务 - -- **T-1:** 在 `mpa_provision.py` 中增加规范 ID 校验和工作负载名称 helper。 -- **T-2:** 增加最小 Identity SDK get-or-create 模块和错误映射。 -- **T-3:** 在 `cli_mpa.py` 接入身份创建和 dry-run 输出;鉴权失败时报告所需 action。 -- **T-4:** 更新 Runtime 环境组装和双语组件 spec。 -- **T-5:** 增加 mpa-agent 配套配置和解析改动。 -- **T-6:** 测试先行,执行针对性回归、pre-commit 和要求的更广 Python 门禁。 - -## 6. 验证与验收 - -| 需求 | 任务 | 验收标准 | 命令 | 结果 | -| --- | --- | --- | --- | --- | -| FR-1、FR-2 | T-1 | AC-1:自动生成、显式 ID 和派生 Identity 名称符合规范 | `uv run --extra dev pytest tests/integrations/test_mpa_provision_env.py` | pass;已包含在 39 条针对性用例中 | -| FR-3、FR-4、FR-7 | T-2、T-3 | AC-2:创建、复用、并发冲突和鉴权失败路径确定,副作用顺序正确 | `uv run --extra dev pytest tests/integrations/test_mpa_identity.py tests/cli/test_cli_mpa.py` | pass;已包含在 39 条针对性用例中 | -| FR-5、FR-6 | T-3、T-4 | AC-3:dry-run 无副作用,Runtime 收到显式变量对 | `uv run --extra dev pytest tests/integrations/test_mpa_provision_env.py tests/cli/test_cli_mpa.py` | pass;已包含在 39 条针对性用例中 | -| FR-8 | T-5 | AC-4:mpa-agent 覆盖显式、旧推导和变量缺一场景 | 配套仓库针对性 pytest | pass;47 条用例,diff 覆盖率 100% | -| 全部 | T-6 | AC-5:仓库 pre-commit 和受影响回归通过 | `uv run --extra dev pre-commit run --all-files`;`uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` | 针对性测试和增量覆盖率通过(39 条、100%);广泛回归先执行到 4563 passed、12 skipped、2 xfailed,另有 8 个可选依赖缺失失败;安装声明的 `extensions`、`sandbox` extras 后,受影响的 13 条用例全部通过 | -| 全部 | T-6 | AC-6:授权隔离账号可用新名称获取 workload token | 脱敏输出的手工云 smoke | not_run;需要明确提供真实凭据并部署配套镜像 | - -## 7. 风险与恢复 - -- 创建固定 Pool 需要账号级 Identity 权限;权限缺失会在其他创建副作用前停止。 -- 后续失败可能留下未使用 Identity;使用同一 ID 重试会复用,清理由运维手工执行。 -- 新环境变量搭配旧 mpa-agent 镜像时,旧镜像仍使用旧名称。发布记录和真实验证必须标明配套镜像版本。 -- 共享 Pool 把身份集中在同一命名空间;规范 MPA ID 避免误别名,Pool 容量监控属于本次变更外的运维职责。 - -## 8. 评审与交付记录 - -- 用户批准:2026-09-20,批准双仓方案并保留十二位后缀。 -- 设计评审:2026-09-20 按仓库要求直接评审;身份副作用、权限集合、幂等、失败顺序、兼容性和配套消费者契约均已明确,无阻塞歧义。 -- 实现评审:两轮检查覆盖命名和 API 契约、副作用顺序、幂等和错误处理、Secret 边界、兼容性与测试质量;无未关闭 finding。 -- 验证:VeADK 针对性用例 39 条通过,增量覆盖率 100%;初次缺少可选依赖的用例在安装声明的 `extensions`、`sandbox` extras 后 13 条全部通过。配套 mpa-agent 针对性用例 47 条通过,全量覆盖率门禁为 2818 passed、21 skipped、总覆盖率 95.05%,增量覆盖率 100%。 -- 剩余验证缺口:AC-6 仍为 `not_run`,因为需要有权限的真实账号和部署配套镜像后的 Runtime。 diff --git a/prd-spec/features/skill-observability/2026-10-16-skill-observability.md b/prd-spec/features/skill-observability/2026-10-16-skill-observability.md deleted file mode 100644 index 5102bd88b..000000000 --- a/prd-spec/features/skill-observability/2026-10-16-skill-observability.md +++ /dev/null @@ -1,95 +0,0 @@ -# Skill download and instruction-load observability - -[中文版](2026-10-16-skill-observability.zh.md) - -- Change ID: `skill-observability`; created/revised: 2026-10-16; status: implemented; affected checks passed, full regression has existing/environment failures. -- Contract: [skill-observability](../../../specs/skill-observability/README.md). -- Source: AgentKit Skills runtime observability design, revision 2102 (2026-10-16). -- Scope approval: the user approved the proposed Runtime/Python-tool scope and explicitly included `actb-mono/sandboxes/agentkit-skill`. Implementation is local; no commit, publication or cloud operations are authorized. - -## Evidence, goals and boundaries - -`VeSkillRegistry.get_skill` materializes packages but drops remote identity on conversion to ADK Skill. ADK 2.7.1 caches fetched skills per invocation, so registry calls cannot count instruction loads. `SkillsTool` has a latency histogram but no counters from the design. Sandbox Python pins VeADK 1.1.14 / ADK 1.34.0; Go has its own package store and tool wrapper. Both sandbox telemetry setups currently configure traces, not metric export. - -Count package acquisition and successful instruction delivery with the exact source identities and schema in the linked contract. Preserve SDK signatures, cache/install policy, existing metrics, tool results, cancellation and cleanup. Do not count discovery, resource reads, scripts, A2A forwarding, or task success as instruction loading. Do not implement issue reporting, dashboards, new dependencies or dependency upgrades. Changes to AIO protocols, daemon lifecycle and isolation are outside this change. - -## Requirements and scenarios - -- FR-1: A recognized remote package acquisition emits one `gen_ai_skill_download_count` observation on completion, including validated disk-cache restoration and failure. Invalid cache followed by download is one attempt, with `skill.download.cache_hit=false`. -- FR-2: A successful instruction response emits one `gen_ai_skill_load_count`. Two successful `load_skill` calls in one invocation count twice even when ADK fetches once. Unknown skills, read failures and cancellation never count as successful loads. -- FR-3: Cloud identity survives conversion/cache. Sandbox Python distinguishes built-ins, SkillHub slugs, explicit AgentKit IDs and both space kinds. Go retains its existing four-source support. Unattributable local skills are not guessed to be built-ins. -- FR-4: Use existing process-global OTEL providers; sandbox installs a periodic OTLP metric exporter only when no provider exists. Trace and metric disable switches are independent. Export failures do not affect conversations; shutdown flush is bounded. -- FR-5: New telemetry never records archive URLs, credentials, instruction bodies, paths or raw exception messages. Existing content telemetry policy remains unchanged. -- FR-6: Python and Go use the same labels and source semantics for supported sources; prefetch produces downloads only. Existing installed packages/session links are retained. - -The current schema follows technical document revision 2419, read on 2026-10-21, plus the user-approved 2026-10-22 correction from `time` to dimensionless `1`: both counters use the `_count` suffix and unit `1`, with `skill_name` added to identity. Outcome is only `skill.download.outcome`, no longer a metric label. Spans are named `download_skill {skill_name}` / `load_skill {skill_name}` and use `skill.consumer` and `skill.download.cache_hit`. Name provenance and empty-value rules are in the component contract. Preserve fixed Counter instruments; fields and record methods do not need a `gen_ai` prefix. - -## Design and affected consumers - -Runtime: add a small `veadk/skills/telemetry.py` recorder, instrument the existing materializer, preserve cloud identity under reserved ADK frontmatter metadata, and extend the existing VeADK tool tracing callback for successful native ADK `LoadSkillTool` responses. No replacement toolset or new global cache. Runtime load telemetry follows the existing VeADK tracing-hook activation; metrics are recorded independently of span sampling once the hook is installed. - -Sandbox: implement a local recorder using already-installed OTEL packages, because the sandbox's released SDK must work independently of unpublished VeADK changes. Instrument `playground_skills/packages.py`, SkillHub reconciliation, and the existing `ObservedSkillsTool`. Persist the installed SkillHub slug alongside its package so a failed replacement cannot relabel an older built-in. The Go package store records downloads; catalog metadata carries identity into the existing tracked tool wrapper. Built-ins produce no download events. Configure metric export in existing telemetry setup and flush with existing shutdown handling. - -Counters count acquisition attempts after identity resolution. Metadata-list failures without a resolved skill ID cannot produce a per-skill download. Successful acquisition means a validated usable package, not just HTTP success. Python worker-thread downloads may finish after awaiting-task cancellation; count their actual acquisition result, never a cancelled load. Go context cancellation is an error outcome. Metrics contain only contract dimensions; `skill.consumer`, `skill.download.cache_hit` and errors are span attributes. Span duration covers acquisition, including validation/installation. Cache hit rates derived from spans are subject to trace sampling. - -## Tasks, tests and acceptance - -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| FR-1, FR-3 | T-1: Runtime materializer, registry and recorder | AC-1: exact identity/counts for network, cache, invalid package and unknown source | `uv run --extra dev pytest tests/skills/` | -| FR-2, FR-5 | T-2: native ADK load hook and regression tests | AC-2: repeated cached loads count; failure/resource/script calls do not; no secrets | `uv run --extra dev pytest tests/test_portal_metrics.py tests/test_tracing_content.py tests/skills/` | -| FR-1, FR-2, FR-3, FR-6 | T-3: Sandbox Python packages, reconciliation and tool adapter | AC-3: all five sources, install failure/cache, preserved slug, no duplicate load | Sandbox Python targeted/full tests with isolated state | -| FR-1, FR-2, FR-3, FR-6 | T-4: Sandbox Go packages, catalog and tracked tool | AC-4: matching Go counts and cancellation/cleanup | `go run ./cmd/skill-go test ./...` in sandbox `go/` | -| FR-4, FR-5 | T-5: sandbox OTLP metrics and exporter tests | AC-5: local HTTP collector receives counters; switches/provider reuse/shutdown work | Python local collector tests; Go metric exporter tests | -| All | T-6: reconcile bilingual docs, examples and verification | AC-6: paired contracts and actual check evidence | SDK regression; sandbox `make verify`; `git diff --check` | - -SDK final regression: `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` (two workers). No real cloud/provider tests. Before any later authorized commit, synchronize intended base and run repository pre-commit gates. Frontend/browser gates are not applicable: no Studio code or API changes. - -## Review, risks and delivery record - -Direct design review (review-spec unavailable), 2026-10-16: checked source ownership, cache/load separation, metric-label parity, API compatibility, error redaction, cancellation, provider ownership and bilingual equivalence. Resolved double counting by keeping Runtime forwarding uninstrumented and Sandbox counters in its adapter; resolved unpublished dependency coupling with independent sandbox instrumentation. The user confirmed sandbox business-code scope after source-access clarification. The user also explicitly approved keeping Go without `sp-*` support: this change adds observations only, with no SDK upgrade or source-support expansion. No AIO protocol claim is made. - -Risks: native ADK response metadata is a version-sensitive integration covered by real installed ADK tests; inventory joins are still required for never-used skills. New sandbox exporters add periodic background work, not synchronous network calls during tool execution. Existing span sampling limits trace-derived cache statistics. Tested bases: VeADK `d55fe8fa`; ACTB `7354ed5`. Execution date: 2026-10-16. - - -### Implementation review - -T-1 through T-6 are implemented. The actual ADK load response retains registry-owned identity across invocation-cache hits. Sandbox records installation independently of instruction delivery; Go reads the same installed SkillHub provenance receipt as Python. Archives cannot override the installer-written receipt. Source resolution failures do not invent labels. Existing public signatures, install/rollback rules and tool results remain unchanged. Go metric imports promote already-locked indirect modules to direct dependencies; versions and `go.sum` are unchanged. - -Direct implementation review checked cancellation, failed installations, cache reuse, failed provider recording, successful-instruction boundaries, source parity, endpoint configuration and provider ownership. Regression tests failed before implementation (missing recorder functions), and the implementation-switch provenance test reproduced a built-in misclassification before the fix. No blocking finding remains in the affected scope. No deployment/readiness claim is made. - -Startup tradeoff: SkillHub reconciliation is a short-lived process, so its observations must flush before exit. Only actual SkillHub configuration initializes this exporter; empty configuration and built-in disabling add no telemetry initialization. Startup exporters have 200 ms timeouts; regular Python metric export uses 3 s, and Go shutdown shares a 5 s context. A local slow-collector experiment measured 575 ms import/setup plus 533 ms shutdown with both signals timing out. These are local samples, not image startup guarantees; this overhead applies only to the Python startup installer, not each tool call. The local collector tests also verify metric delivery and provider reuse from that startup setup. - -### Verification record (2026-10-16) - -Tested scope is the uncommitted diff over VeADK `d55fe8fa` and ACTB `7354ed5`. SDK uses Python 3.12.15 / ADK 2.7.1; Sandbox uses its own Python environment / ADK 1.34.0 and the existing pinned Go SDK. - -| Check | Status | Actual result | -| --- | --- | --- | -| `uv run --extra dev pytest tests/skills tests/test_portal_metrics.py tests/test_tracing_content.py -q` | pass | 120 tests; actual ADK repeated loads, cache/error/cancellation, identity, redaction and fail-open recorder coverage. | -| `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` | fail | 6971 passed, 16 failed, 59 skipped, 4 xfailed, 2 collection errors. Failures require unavailable optional `llama_index`, `openai_codex` and `anthropic` packages; unrelated dependency installation was not folded into this feature. Two workers were used. | -| Sandbox targeted Python: `TMPDIR=/private/tmp PYTHONPATH=src MODEL_AGENT_API_KEY=local-test-placeholder OTEL_TRACES_EXPORTER=none OTEL_METRICS_EXPORTER=none .venv/bin/python -m pytest tests/test_skill_observability.py tests/test_skill_reconciler.py tests/test_playground_skills.py tests/test_sandbox_agent.py tests/test_a2a_telemetry.py -q` | pass | 42 tests, 7 subtests. Collector tests launch clean child processes with metrics explicitly enabled. | -| Sandbox full Python: same environment, `.venv/bin/python -m pytest tests -q` | fail | 182 passed, 9 failed, 20 skipped, 13 subtests. All nine remaining cleanup/migration-contract failures reproduce against copied, tracked source from ACTB `7354ed5`; no new observability test fails. | -| `TMPDIR=/private/tmp go run ./cmd/skill-go test ./...` (Sandbox `go/`) | pass | All Go packages; canonical macOS temporary directory avoids pre-existing path-alias cleanup assertions. | -| `TMPDIR=/private/tmp go run ./cmd/skill-go test -race -run 'TestSkill|TestInstalledSkillHub' .` | pass | Affected Go acquisition/load/provenance and exporter tests, with race detection. | -| `make verify PYTHON=/Users/bytedance/actb-mono/sandboxes/agentkit-skill/.venv/bin/python` | pass | 28 root smoke tests, shell syntax and diff checks; does not establish container readiness. | -| Ruff 0.11.12 on changed Python files | pass | Used isolated `uvx --from ruff==0.11.12 ruff check `; the repository environment has no Ruff executable. | -| Bilingual pairs, schema identifiers, relative links and `git diff --check` | pass | PRD/spec pairs synchronized; Sandbox README and Unreleased changelog updated. | -| Real cloud/image readiness, deployed collector and grpc transport | not_run | No cloud/image operation authorized; local collector delivery uses HTTP/protobuf. | -| Repository pre-commit and remote synchronization | not_run | No commit requested; these remain mandatory before a later authorized commit. | -| Studio/browser gates | not_applicable | No frontend/backend Studio contract changed. | - -Initial Sandbox full collection required a dummy model API value; the final command uses a placeholder, disables external exporters and supplies `PYTHONPATH=src` for child processes. Tests that create an in-memory SDK provider explicitly remove `OTEL_SDK_DISABLED` locally. No real credentials, real model requests or machine-global configuration were used. The user approved recording these environment/tooling limitations here. Temporary baseline copies are removed after comparison. Affected checks establish local implementation behavior; full-repository acceptance is not green. - -### Unit correction (2026-10-22) - -The user explicitly approved unit `1` for both Skill counters across VeADK and ACTB Python/Go, superseding the earlier `time` decision. This is a correction to FR-1/FR-2/FR-6 and CON-1/CON-2. Update the two SDK instruments, sandbox instruments, existing unit assertions and bilingual contracts; preserve names, labels, increments, spans and provider lifecycle. No new initialization work or dependencies are needed. Existing historical `time` data is not rewritten; consumers filtering units must accept the new value during rollout. - -Direct design review (review-spec unavailable): checked all three producers, existing real-provider/export tests, bilingual parity and compatibility boundaries. The user's current instruction approves this exact scope; no additional design decision is pending. Acceptance is unit `1` with unchanged counts/labels in the existing late-provider and collector tests. Verification on the unit-only diff over VeADK `5a2aea21`, 2026-10-22: - -- `pass`: the updated VeADK/Python-sandbox late-provider tests and Go acquisition test failed against the old `time` instruments, then passed after the correction. -- `pass`: `uv run --no-sync --extra dev pytest tests/skills tests/test_portal_metrics.py tests/test_tracing_content.py -q` — 123 tests; the existing local environment was reused without dependency changes. -- `pass`: `uv run --no-sync --extra dev pre-commit run --all-files` — Ruff lint/format, secret scanners and dependency policy. -- `pass`: ACTB Python observability/reconciler/playground/catalog/agent/local-OTLP regression — 67 tests and 9 subtests; `TMPDIR=/private/tmp go run ./cmd/skill-go test -race -run 'TestSkill|TestInstalledSkillHub|TestTelemetry' -count=1 .`; `make verify PYTHON=/Users/bytedance/actb-mono/.venv/bin/python` — 29 smoke tests. -- `pass`: fetched origin and rebased onto the configured tracking branch before edits; the branch was already current. The follow-up retains the user's one-fix-per-commit delivery instruction; no push or deployment is included. -- `not_run`: repository-wide unrelated tests, image builds and live cloud collection; this correction only changes the two Skill units and has no dependency, startup or other-instrument changes. Historical data and backend unit-filter migration remain outside this local change. diff --git a/prd-spec/features/skill-observability/2026-10-16-skill-observability.zh.md b/prd-spec/features/skill-observability/2026-10-16-skill-observability.zh.md deleted file mode 100644 index 4121f769a..000000000 --- a/prd-spec/features/skill-observability/2026-10-16-skill-observability.zh.md +++ /dev/null @@ -1,95 +0,0 @@ -# 技能下载与指令加载可观测 - -[English](2026-10-16-skill-observability.md) - -- 变更 ID:`skill-observability`;创建/修订:2026-10-16;状态:implemented;受影响检查通过,全量回归存在既有/环境失败。 -- 契约:[skill-observability](../../../specs/skill-observability/README.zh.md)。 -- 来源:AgentKit Skills 运行时可观测方案,revision 2102(2026-10-16)。 -- 范围批准:用户确认提出的 Runtime/Python 工具范围,并明确纳入 `actb-mono/sandboxes/agentkit-skill`。仅本地实现;未授权提交、发布或云端操作。 - -## 证据、目标与边界 - -`VeSkillRegistry.get_skill` 获取技能包,但转换成 ADK Skill 时丢失云端身份。ADK 2.7.1 按 invocation 缓存技能,因此 registry 调用不能计作指令加载。`SkillsTool` 有耗时 histogram,但没有方案要求的 counter。Sandbox Python 固定使用 VeADK 1.1.14 / ADK 1.34.0;Go 有独立的包存储和工具适配层。两种 Sandbox 遥测初始化目前都配置 trace,没有配置 metric 导出。 - -按照关联契约的准确来源身份和数据结构统计技能包获取与成功指令交付。保留 SDK 签名、缓存/安装策略、既有指标、工具结果、取消和清理行为。发现、资源读取、脚本、A2A 转发和任务成功均不计作指令加载。不实现问题主动汇报、看板、新增依赖或依赖升级。AIO 协议、守护进程生命周期和隔离机制不在本次变更范围。 - -## 需求与场景 - -- FR-1:已识别来源的远端技能包获取结束时记录一次 `gen_ai_skill_download_count`,覆盖验证成功的磁盘缓存恢复和失败。无效缓存后重新下载属于一次获取,`skill.download.cache_hit=false`。 -- FR-2:成功返回指令时记录一次 `gen_ai_skill_load_count`。同一 invocation 两次成功 `load_skill` 计两次,即使 ADK 只获取一次。未知技能、读取失败和取消不计成功加载。 -- FR-3:云端身份在转换/缓存后保留。Sandbox Python 区分内置技能、SkillHub slug、显式 AgentKit ID 和两类空间;Go 保留既有四类来源支持。来源不明的本地技能不猜测成内置技能。 -- FR-4:使用既有进程级 OTEL provider;Sandbox 仅在未配置 provider 时安装周期 OTLP metric exporter。trace 与 metric 禁用开关独立。导出失败不影响对话;退出 flush 有时间上限。 -- FR-5:新增遥测不记录包 URL、凭据、指令正文、路径或原始异常消息。既有内容遥测策略保持不变。 -- FR-6:Python 与 Go 对已支持的来源使用相同标签和来源语义;预取只产生下载数据。保留既有安装包和会话链接。 - -当前数据结构以 2026-10-21 读取的技术文档 revision 2419 及用户于 2026-10-22 批准将单位从 `time` 修正为无量纲 `1` 的要求为准:两个 counter 使用 `_count` 后缀和单位 `1`,身份新增 `skill_name`;结果仅放在 `skill.download.outcome`,不再作为 metric 标签。Span 命名为 `download_skill {skill_name}` / `load_skill {skill_name}`,并使用 `skill.consumer` 和 `skill.download.cache_hit`。具体名称来源与空值规则见组件契约。保留固定 Counter 实现;字段和 record 方法名无需 `gen_ai` 前缀。 - -## 设计与受影响消费者 - -Runtime:增加小型 `veadk/skills/telemetry.py` 记录模块,在既有 materializer 采集;将云端身份保存在 ADK frontmatter 的保留 metadata 中;在既有 VeADK 工具 tracing 回调中处理原生 ADK `LoadSkillTool` 的成功响应。不替换 toolset、不增加全局缓存。Runtime 加载采集沿用既有 VeADK tracing hook 的启用方式;hook 启用后,计数不依赖 Span 采样。 - -Sandbox:使用已经安装的 OTEL 包实现本地记录模块,确保已发布 SDK 无需依赖尚未发布的 VeADK 改动。在 `playground_skills/packages.py`、SkillHub 安装和既有 `ObservedSkillsTool` 采集。随安装包保存 SkillHub slug,避免替换失败时把旧内置技能标记成新的 SkillHub 技能。Go 包存储负责下载记录;目录元数据把身份传递给既有 tracked tool。内置技能不产生下载事件。在现有遥测初始化中配置 metric 导出,并复用退出 flush。 - -counter 从身份解析后的技能包获取开始计数。尚无技能 ID 的元数据列表失败无法产生按技能归属的下载。获取成功表示技能包通过验证且可用,不只是 HTTP 成功。Python 工作线程可能在等待任务取消后完成下载;按实际获取结果计数,但不计取消的加载。Go context 取消计入错误结果。metric 仅含契约规定的维度;`skill.consumer`、`skill.download.cache_hit` 和错误放在 Span。Span 耗时覆盖获取、验证和安装。通过 Span 计算的缓存命中率受 trace 采样影响。 - -## 任务、测试与验收 - -| 需求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| FR-1, FR-3 | T-1:Runtime materializer、registry 与记录模块 | AC-1:网络、缓存、无效包和未知来源的身份/次数准确 | `uv run --extra dev pytest tests/skills/` | -| FR-2, FR-5 | T-2:原生 ADK 加载 hook 与回归测试 | AC-2:缓存后的重复加载计数;失败/资源/脚本不计;不泄露敏感信息 | `uv run --extra dev pytest tests/test_portal_metrics.py tests/test_tracing_content.py tests/skills/` | -| FR-1, FR-2, FR-3, FR-6 | T-3:Sandbox Python 包、安装与工具适配 | AC-3:五种来源、安装失败/缓存、slug 保留、不重复计数 | 使用隔离状态运行 Sandbox Python 定向/全模块测试 | -| FR-1, FR-2, FR-3, FR-6 | T-4:Sandbox Go 包、目录与 tracked tool | AC-4:Go 次数一致,取消/清理正确 | Sandbox `go/` 中运行 `go run ./cmd/skill-go test ./...` | -| FR-4, FR-5 | T-5:Sandbox OTLP metric 与导出测试 | AC-5:本地 HTTP collector 收到 counter;开关/provider 复用/退出有效 | Python 本地 collector 测试;Go metric exporter 测试 | -| 全部 | T-6:同步双语文档、示例与验证 | AC-6:契约成对,验证有实际证据 | SDK 回归;Sandbox `make verify`;`git diff --check` | - -SDK 最终回归:`uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`(两个 worker)。不运行真实云端/provider 测试。后续若获提交授权,须先同步目标基线并执行仓库 pre-commit 门禁。前端/浏览器门禁不适用:未改变 Studio 代码或 API。 - -## 评审、风险与交付记录 - -2026-10-16 直接设计评审(review-spec 不可用):检查来源归属、缓存/加载区分、标签一致、API 兼容、错误脱敏、取消、provider 所有权与双语等价。Runtime 转发不采集、Sandbox 在适配层计数,消除重复计数;Sandbox 独立埋点,消除对未发布依赖的耦合。用户在源码访问澄清后确认 Sandbox 业务代码范围。用户还明确批准保留 Go 不支持 `sp-*` 的限制:本次只增加观测,不升级 SDK、不扩展来源支持。不对 AIO 协议作结论。 - -风险:原生 ADK 响应 metadata 是版本相关接入点,须用实际安装的 ADK 测试;“从未使用”仍需关联完整技能清单。新增 Sandbox exporter 增加周期后台工作,不在工具执行时同步请求网络。既有 Span 采样限制基于 trace 的缓存统计。测试基线:VeADK `d55fe8fa`;ACTB `7354ed5`。执行日期:2026-10-16。 - - -### 实现评审 - -T-1 至 T-6 已实现。真实 ADK 加载响应保留 Registry 提供的身份,invocation 缓存命中后仍保留。Sandbox 分别记录安装和指令交付;Go 与 Python 读取同一份已安装 SkillHub 来源记录。技能包不能覆盖安装器写入的来源记录。无法解析来源时不猜测标签。既有公开签名、安装/回滚规则和工具结果不变。Go 仅把已经锁定的间接 metric 模块提升为直接依赖,版本和 `go.sum` 不变。 - -直接实现评审检查了取消、安装失败、缓存复用、provider 记录失败、成功指令边界、来源一致性、endpoint 配置及 provider 所有权。回归测试在实现前因缺少记录函数失败;实现切换来源测试在修复前复现了误标为内置技能的问题。受影响范围无遗留阻断发现。不声明已部署或容器已就绪。 - -启动权衡:SkillHub 同步是短生命周期进程,观测必须在退出前尽力 flush。只有实际配置 SkillHub 才初始化 exporter;空配置和仅禁用内置技能不增加遥测初始化。启动 exporter 超时为 200 ms;常规 Python metric 导出为 3 s,Go 退出共用 5 s context。本地慢 collector 实验中,两种信号均超时时,导入/初始化为 575 ms、退出为 533 ms。这只是本地样本,不代表镜像启动时间保证;开销只发生在 Python 启动安装器,不发生在每次工具调用。本地 collector 测试也验证该启动初始化的 metric 发送和 provider 复用。 - -### 验证记录(2026-10-16) - -测试范围为 VeADK `d55fe8fa` 与 ACTB `7354ed5` 之上的未提交 diff。SDK 使用 Python 3.12.15 / ADK 2.7.1;Sandbox 使用自身 Python 环境 / ADK 1.34.0 及既有固定 Go SDK。 - -| 检查 | 状态 | 实际结果 | -| --- | --- | --- | -| `uv run --extra dev pytest tests/skills tests/test_portal_metrics.py tests/test_tracing_content.py -q` | pass | 120 项;覆盖真实 ADK 重复加载、缓存/错误/取消、身份、脱敏和记录失败不影响结果。 | -| `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` | fail | 6971 passed、16 failed、59 skipped、4 xfailed、2 项收集错误。失败需要当前未安装的可选 `llama_index`、`openai_codex` 和 `anthropic` 包;本功能未纳入无关依赖安装。使用两个 worker。 | -| Sandbox 定向 Python:`TMPDIR=/private/tmp PYTHONPATH=src MODEL_AGENT_API_KEY=local-test-placeholder OTEL_TRACES_EXPORTER=none OTEL_METRICS_EXPORTER=none .venv/bin/python -m pytest tests/test_skill_observability.py tests/test_skill_reconciler.py tests/test_playground_skills.py tests/test_sandbox_agent.py tests/test_a2a_telemetry.py -q` | pass | 42 项、7 个 subtest。collector 测试在干净子进程中显式开启 metrics。 | -| Sandbox 全模块 Python:同上环境,`.venv/bin/python -m pytest tests -q` | fail | 182 passed、9 failed、20 skipped、13 个 subtest。余下九项 cleanup/迁移契约失败均在复制的 ACTB `7354ed5` 已跟踪源码上复现;新增观测测试无失败。 | -| `TMPDIR=/private/tmp go run ./cmd/skill-go test ./...`(Sandbox `go/`) | pass | 所有 Go package 通过;使用 macOS 规范临时目录,避开既有路径别名 cleanup 断言。 | -| `TMPDIR=/private/tmp go run ./cmd/skill-go test -race -run 'TestSkill|TestInstalledSkillHub' .` | pass | 受影响 Go 获取/加载/来源及 exporter 测试,开启 race 检查。 | -| `make verify PYTHON=/Users/bytedance/actb-mono/sandboxes/agentkit-skill/.venv/bin/python` | pass | 28 项根目录 smoke、shell 语法和 diff 检查;不代表容器就绪。 | -| 变更 Python 文件的 Ruff 0.11.12 检查 | pass | 使用隔离的 `uvx --from ruff==0.11.12 ruff check `;项目环境没有 Ruff 可执行文件。 | -| 双语配对、schema 标识符、相对链接及 `git diff --check` | pass | PRD/spec 成对同步;更新 Sandbox README 和 Unreleased 变更记录。 | -| 真实云端/镜像就绪、线上 collector 和 grpc 传输 | not_run | 未授权云端/镜像操作;本地 collector 发送采用 HTTP/protobuf。 | -| 仓库 pre-commit 与远端同步 | not_run | 未请求提交;后续获提交授权时仍必须执行。 | -| Studio/浏览器门禁 | not_applicable | 未改变 Studio 前后端契约。 | - -Sandbox 初次全模块收集需要测试模型 API 值;最终命令使用占位值、禁用外部 exporter,并设置 `PYTHONPATH=src` 供子进程导入。创建内存 SDK provider 的测试局部移除 `OTEL_SDK_DISABLED`。未使用真实凭据、真实模型请求或修改全局机器配置。用户同意在此记录这些环境/工具限制。临时基线副本在对比后删除。受影响检查验证了本地实现行为,全仓验收尚未全绿。 - -### 单位修正(2026-10-22) - -用户明确批准 VeADK 与 ACTB Python/Go 的两个 Skill counter 统一采用单位 `1`,取代此前的 `time` 决策。这是对 FR-1/FR-2/FR-6 和 CON-1/CON-2 的修正。更新 SDK 的两个 instrument、Sandbox instrument、既有单位断言及双语契约;保持名称、标签、计数增量、Span 和 provider 生命周期。无需新增初始化工作或依赖。既有 `time` 历史数据不会重写;按单位过滤的消费者需在发布期间接纳新值。 - -直接设计评审(review-spec 不可用):核对三个生产端、既有真实 provider/导出测试、双语一致性与兼容边界。用户当前指令已批准这一准确范围,无额外待定设计。验收要求为既有延迟 provider 和 collector 测试中的单位变为 `1`,计数和标签保持不变。验证于 2026-10-22 执行,范围为 VeADK `5a2aea21` 之上的单位修正 diff: - -- `pass`:更新后的 VeADK/Python Sandbox 延迟 provider 测试和 Go 获取测试在旧 `time` instrument 上失败,修正后通过。 -- `pass`:`uv run --no-sync --extra dev pytest tests/skills tests/test_portal_metrics.py tests/test_tracing_content.py -q` — 123 项;复用已有本地环境,未改变依赖。 -- `pass`:`uv run --no-sync --extra dev pre-commit run --all-files` — Ruff lint/format、密钥扫描与依赖策略检查。 -- `pass`:ACTB Python 观测/安装器/playground/catalog/agent/本地 OTLP 回归 — 67 项及 9 个子测试;`TMPDIR=/private/tmp go run ./cmd/skill-go test -race -run 'TestSkill|TestInstalledSkillHub|TestTelemetry' -count=1 .`;`make verify PYTHON=/Users/bytedance/actb-mono/.venv/bin/python` — 29 项 smoke。 -- `pass`:编辑前 fetch origin 并 rebase 到已配置的跟踪分支,分支已是最新。本次延续用户每个 fix 独立 commit 的交付要求;不包含推送或部署。 -- `not_run`:全仓无关测试、镜像构建和真实云端采集;本次只修改两个 Skill 单位,不涉及依赖、启动或其他 instrument。历史数据和后端按单位过滤的迁移不属于本地变更范围。 diff --git a/prd-spec/features/studio-agents-md-edit/2026-10-21-studio-agents-md-edit.md b/prd-spec/features/studio-agents-md-edit/2026-10-21-studio-agents-md-edit.md deleted file mode 100644 index 2b864feb4..000000000 --- a/prd-spec/features/studio-agents-md-edit/2026-10-21-studio-agents-md-edit.md +++ /dev/null @@ -1,52 +0,0 @@ -# Studio AGENTS.md editing and application - -[中文](2026-10-21-studio-agents-md-edit.zh.md) - -change-id: `studio-agents-md-edit` - -## Background and authorization -On 2026-10-21 the user explicitly requested implementation in Runtime and Studio, matching ArkClaw editing behavior, excluding caching and Bot Profile changes. This authorizes scoped additive APIs and implementation, not commits, releases or cloud changes. ArkClaw ModifyAgentConfig edits a debug Runtime and publishes separately. Studio edits the selected Runtime; this change does not introduce dual environments. Old ADK injected agentsMd; reused Worker sessions update it by fingerprint. - -## Goals and non-goals -REQ-1: Studio administrators edit, save and clear AGENTS.md in the information rail, with persisted readback. Members retain read access. Preserve unrelated Agent settings. -REQ-2: Ordinary sessions use the latest content on the next turn; an active turn uses one snapshot for Main and Worker. Existing sessions need no recreation. Retain Worker profile fingerprint updates. -REQ-3: Detect concurrent saves and report errors honestly. Older Runtimes remain read-only. No cache, migration, dependency, Bot Profile, MA remote-host instruction, tool permission or model configuration changes. - -## Design and boundaries -Add GET/POST /api/v1/studio/agents-md: GET returns content:string and revision:int (0 when unconfigured). POST requires content (empty allowed, at most 40000 characters) and revision; reject extra fields. Runtime verifies X-MPA-Studio-Key against its current key. The Studio proxy injects the server-owned key only for administrators; browser headers cannot impersonate it. Preserve the existing read-only information route. -Reuse mpa_agents revisions and existing row-lock/CAS primitives. Bootstrap with current MPA_AGENT_ID when absent; concurrent initialization returns409. Preserve model/Skill/MCP settings. Empty content removes custom instructions. -At turn entry in the ordinary Codex Worker architecture, read current configuration and freeze an AGENTS.md-only trusted RuntimeEnvelope snapshot. Inject into the active base instructions on thread start/resume: native Codex 0.155.1 does not propagate changed resume developerInstructions into the next model request. Carry through authorized MCP context and override the same Worker profile field, avoiding mid-turn drift. Carry no credentials and add no grant. Existing Bot snapshots remain unchanged; MA remote-host stays out of scope. - -## Component impact -Runtime metadata API, AgentConfigService, Main envelope/runner/controller, trusted MCP context and Worker profile loading. Studio runtime proxy authorization, client, MPA rail, ModalLayout/Button/Textarea and localized copy. - -## Spec review -review-spec checks cover empty clearing, older Runtime404/405, missing-record bootstrap, concurrent409, running turns, reused threads, identity isolation, late responses/cancellation and40k limit. Reuse storage/security without a registry/cache/new identity system. Platform rules outrank custom content. frontend-design/ui-ux-pro-max skills were not found locally; UI follows existing components and frontend/SPEC.md. - -## Tasks and test plan -1. Design/contracts;2. API/persistence/turn regression RED;3. Runtime implementation;4. Studio implementation/tests;5. required gates/browser;6. separate implementation, verification and publication status. -Cover read/save/clear/bootstrap/replay/conflict/auth/size; Main/Worker snapshot and next-turn updates; UI loading/failure draft retention/retry/cancel/clear confirmation/keyboardIME/narrow viewport/target switch. Runtime make test/lint/mcp-tool-gate and governance; Studio targeted Python and proxy regression, Ruff/Pyright, npm test/build and browser checks. - -## Risks and acceptance -Do not claim old control-plane publishing topology is replicated. Verify configuration delivery, not deterministic model obedience. Record live model/cloud checks separately; no deployment claim. Existing content over40000 characters is not silently truncated on save. Work follows task dependencies; reserve verification effort for cross-repository compatibility and real environments. - -## Verification -Implemented. Targeted tests, real PostgreSQL CAS, native Codex update/clear, full Studio frontend tests/build and browser checks passed. Final full Runtime run: 10397 passed, 306 skipped, 1 failed. The sole failure is a scheduled Worker case reproduced on the pre-change HEAD; the full gate is not passed. Not released. Prior model-switch messages may retain old text as history, while current instructions explicitly replace the old configuration. See the paired Runtime report at docs/verification/studio-agents-md-edit/test-verification-report.md. - -## Delivery clarification (2026-10-21) - -The user authorized committing the local Studio changes and opening a PR to official main. Later Runtime native-protocol evidence supersedes the earlier claim that REQ-2 is fully verified: updating the Worker file does not reload instructions in an already reused native Worker thread. Main next-turn updates and persistence are verified; Worker hot reload remains deferred, including clearing. This PR delivers the Studio editor/proxy and requires the matching Runtime API; it does not fix or claim Worker hot reload. The separate Runtime full-suite baseline failure remains unresolved. No real cloud or model validation is claimed by this publication. - -## Studio pre-commit verification (2026-10-21) - -Scope: Studio editor/proxy changes after rebasing onto official `main` at `5e5deb6a`; the existing collaborator/creation-diagnostic commit was replayed without conflicts. No companion Runtime code is included in this repository commit. - -- pass: `uv run --extra dev pytest tests/test_mpa_agents_md_proxy_policy.py tests/test_mpa_collaborator_proxy_policy.py tests/cli/test_frontend_runtime_proxy.py tests/integrations/mpa_managed -q`: 1,313 passed, 6 warnings. -- pass: `npm --prefix frontend test`: 1,389 Node tests and 154 Vitest tests. -- pass: `npm --prefix frontend run build`, `npm --prefix frontend run check:i18n`, and `npm --prefix frontend run test:webui-assets` (113 packaged files, 351 references). Existing bundle-size warnings remain. -- pass: `uv run --extra dev pre-commit run --all-files` after accepting Ruff formatting; includes hardcoded-secret scanning and dependency policy. -- fail (baseline unchanged): changed-file Pyright using the repository Python environment reports 57 diagnostics: 37 in cli_frontend.py and 20 in test_frontend_runtime_proxy.py. A comparison against the rebased HEAD has identical severity/message/rule counts; the two new Python files have no diagnostics. No global clean type-check claim. -- pass: paired-language local links and diff whitespace checks. -- not_run: new cloud/model operations and repeat browser acceptance during publication; prior isolated browser verification remains recorded above. No deployment is performed. - -- fail: default parallel regression `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`: 7,572 passed, 53 skipped, 4 xfailed, 1 failed (483.08 seconds). The failure is `tests/frontend/server/runtime_artifacts/test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes`: `.md` MIME resolves to `application/octet-stream` instead of `text/markdown`. The same test fails in an isolated rerun (0.52 seconds); both the test and writer implementation are unchanged from official main. This unrelated failure is retained, not bypassed or silently fixed. Open the PR as a draft with this gate failure disclosed. diff --git a/prd-spec/features/studio-agents-md-edit/2026-10-21-studio-agents-md-edit.zh.md b/prd-spec/features/studio-agents-md-edit/2026-10-21-studio-agents-md-edit.zh.md deleted file mode 100644 index 31d20b910..000000000 --- a/prd-spec/features/studio-agents-md-edit/2026-10-21-studio-agents-md-edit.zh.md +++ /dev/null @@ -1,53 +0,0 @@ -# Studio AGENTS.md 编辑与生效 - -[English](2026-10-21-studio-agents-md-edit.md) - -change-id: `studio-agents-md-edit` - -## 背景与授权 -用户于 2026-10-21 明确要求实现 Runtime AGENTS.md 编辑与生效,同步修改 Studio,参照 ArkClaw,不做缓存和 Bot Profile 改造。该请求授权本次范围内的增量接口与实现,不含提交、发布或云端修改。 -旧 ArkClaw ModifyAgentConfig 更新 debug Runtime 配置,发布另行同步;本次 Studio 保存目标为当前所选 Runtime,不新增 debug/正式双环境发布流程。旧 MPA ADK 注入 agentsMd,Worker 复用会话时更新内容指纹。 - -## 目标与非目标 -REQ-1: 管理员在 Studio 信息栏编辑、保存、清空 AGENTS.md,并回读持久化内容;普通用户只读。保留其他 Agent 配置。 -REQ-2: 普通会话下一轮使用最新内容;正在运行的一轮保持同一份快照,Main 与 Worker 一致,已有会话无需重建。Worker 仍通过 profile 指纹更新文件。 -REQ-3: 并发保存冲突明确返回,错误不冒充成功;旧 Runtime 只读兼容。无缓存、迁移、新依赖;不改 Bot Profile、MA remote-host 指令规则、工具权限或模型配置。 - -## 设计与边界 -新增 Runtime GET/POST /api/v1/studio/agents-md。GET 返回 content:string、revision:int(未配置为0);POST 必填 content(可空,最多40000字符)和 revision,拒绝未知字段。Runtime 以 X-MPA-Studio-Key 校验当前 Runtime 密钥。Studio 的代理从服务端取密钥,管理读写仅管理员可用,浏览器不能指定或伪造该头;原有信息栏只读路径保留。 -保存沿用 mpa_agents 修订表与已有行锁/CAS,原记录不存在时使用当前 MPA_AGENT_ID 初始化;并发初始化失败返回409。保存保留模型、Skill、MCP等配置;空内容表示撤销用户自定义指令。 -普通 Codex Worker 架构的 turn 开始时读取当前配置,保存仅含 AGENTS.md 的可信快照到 RuntimeEnvelope。Main 将快照注入本轮 base instructions(原生 Codex 0.155.1 的 resume developerInstructions 更新不会进入下一次模型请求),重新使用 Main thread 时明确替换本轮有效配置;经已授权 MCP 上下文传给 Worker,覆盖 Worker 本轮配置中的同一字段,避免中途重新读到不同版本。不传凭据、不扩大 grant;Bot 已有快照行为保留。MA remote-host 架构不在本次范围。 - -## 组件影响分析 -Runtime: Studio 元数据接口、AgentConfigService、Main envelope/runner/controller、MCP可信上下文、Worker profile加载。Studio: runtime代理授权、API客户端、MPA信息栏、ModalLayout/Button/Textarea、双语文案。 - -## Spec review -采用 review-spec 检查:已确认空字符串清除、旧 Runtime 404/405、无记录初始化、并发409、运行中更新、复用thread、MCP身份隔离、前端晚返回/取消、40k上限。使用已有权限和存储,不添加目录注册、通用缓存或新授权体系。Main平台规则高于自定义内容。缺失的 frontend-design/ui-ux-pro-max 技能在本机未找到,视觉按现有组件与frontend/SPEC.md验证。 - -## 任务与验证计划 -1. 文档与契约;2. API/持久化和跨turn回归RED;3. Runtime实现;4. Studio接口/UI及测试;5. 完整相关门禁与浏览器验证;6. 实现/验证/发布状态分开汇报。 -测试:读取/保存/清空/无记录/重复/冲突/越权/超限;Main与Worker快照、旧会话下一轮、未保存内容;UI加载/失败保留草稿/重试/取消/清空确认/键盘IME/窄窗/切换目标。运行Runtime make test/lint/mcp-tool-gate及治理检查;Studio Python定向+相关代理回归、Ruff/Pyright、npm test/build及浏览器验证。 - -## 风险与验收 -不声称复刻旧控制面发布拓扑。模型遵循内容是概率行为,验证代码确实传入同一配置而非保证所有模型答案。生产模型/云端验证另行记录;未经部署不可称线上生效。若已有字段超过40000字符,不静默截断保存;提示缩短后重试。阶段按上述依赖顺序推进;跨仓库兼容与真实环境为验证风险缓冲。 - -## 验证结果 -实现已完成;定向测试、真实 PostgreSQL CAS、原生 Codex 更新/清空、Studio 全量前端测试与构建、浏览器验证已通过。Runtime 最终全量 10397 passed、306 skipped、1 failed;唯一失败为基线已复现的定时 Worker 用例,全量门禁未通过,尚未发布。历史模型切换消息可以保留旧配置文字,但本轮有效 instructions 明确替代旧版;不删除历史。详见 Runtime 的 docs/verification/studio-agents-md-edit/test-verification-report.md。 - -## 交付澄清(2026-10-21) - -用户已授权提交本地 Studio 修改并创建到官方 main 的 PR。后续 Runtime 原生协议证据取代前文 REQ-2 已完整验证的表述:更新 Worker 文件不会让已复用的原生 Worker 线程重新加载指令。Main 下一轮更新与持久化已验证;Worker 热更新(包括清空)仍为暂缓项。本 PR 交付 Studio 编辑器及代理,依赖配套 Runtime API,不修复也不宣称 Worker 热更新完成。独立 Runtime 全量测试的基线失败仍未解决。本次发布操作不代表真实云端或模型验证。 - -## Studio 提交前验证(2026-10-21) - -范围:rebase 到官方 `main` 的 `5e5deb6a` 后的 Studio 编辑器及代理修改;原协作 Agent/创建诊断提交无冲突重放。本仓库提交不包含配套 Runtime 代码。 - -- pass:`uv run --extra dev pytest tests/test_mpa_agents_md_proxy_policy.py tests/test_mpa_collaborator_proxy_policy.py tests/cli/test_frontend_runtime_proxy.py tests/integrations/mpa_managed -q`:1,313 passed,6 warnings。 -- pass:`npm --prefix frontend test`:Node 1,389 个、Vitest 154 个用例通过。 -- pass:`npm --prefix frontend run build`、`npm --prefix frontend run check:i18n`、`npm --prefix frontend run test:webui-assets`(113 个打包文件、351 个引用)。保留既有 bundle 体积警告。 -- pass:接受 Ruff 格式修正后,`uv run --extra dev pre-commit run --all-files` 通过,包含硬编码密钥扫描和依赖策略检查。 -- fail(基线不变):使用仓库 Python 环境的改动文件 Pyright 共 57 项诊断:cli_frontend.py 37 项、test_frontend_runtime_proxy.py 20 项。与 rebase 后 HEAD 的严重级别/消息/规则计数完全相同;两个新增 Python 文件无诊断。不宣称全仓类型检查通过。 -- pass:双语文档本地链接及 diff 空白检查。 -- not_run:本次发布过程中的新云端/模型操作及重复浏览器验收;此前隔离浏览器验证记录保留于上文。本次不部署。 - -- fail:默认并行回归 `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`:7,572 passed、53 skipped、4 xfailed、1 failed(483.08 秒)。失败为 `tests/frontend/server/runtime_artifacts/test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes`:`.md` MIME 返回 `application/octet-stream` 而非 `text/markdown`。单独复跑同样失败(0.52 秒);该测试与 writer 实现均与官方 main 一致。保留该无关失败,不绕过或静默修改。PR 以草稿形式创建并披露此门禁失败。 diff --git a/prd-spec/features/studio-mpa-basic-only/2026-10-09-basic-only.md b/prd-spec/features/studio-mpa-basic-only/2026-10-09-basic-only.md deleted file mode 100644 index d4398b063..000000000 --- a/prd-spec/features/studio-mpa-basic-only/2026-10-09-basic-only.md +++ /dev/null @@ -1,33 +0,0 @@ -# MPA details: basic information only - -- Date: 2026-10-09; status: implemented, locally verified, and deployed. -- Approval: the user explicitly requested hiding all other tabs and stopping Profile requests on the MPA details page. -- [Chinese](2026-10-09-basic-only.zh.md); contract: [Studio MPA control plane](../../../specs/studio-mpa-control-plane/README.md). - -## Scope and evidence -`AgentWorkspace.tsx` loads `getMpaAgentView` on MPA selection, indirectly invoking Runtime `/profile-status`. Hiding navigation alone does not stop this request. The basic-page footer also links to Profile configuration. - -## Requirements and design -- FR-1: MPA details expose only Basic information, using the existing layout and styles. General agents retain their sections. -- FR-2: Every requested MPA section, including restored/external focus, resolves synchronously to `basic`, before effects run. -- FR-3: Basic information must not fetch MPA Agent View, Profile status, or issue Profile mutations. Gate the existing control-plane loader to its owning Profile/session sections and hide the footer Profile entry. -- FR-4: Preserve Runtime details, execution flow, chat/delete actions and their authorization. Backend APIs, native chat session creation and Profile storage are outside this change. - -## Review and verification plan -Direct review: the change is presentation/request scheduling only; no data migration or authentication changes. Existing hidden implementations remain available for a later explicit re-enable. `ui-ux-pro-max` is unavailable locally; existing component standards and direct review apply. Validate bilingual equivalence and existing styling. - -Regression: render the real workspace with mocked network boundaries; cover all hidden focus sections, MPA switching, general navigation, visible basic content, and zero Profile/control-plane calls. Run frontend tests/build/assets and pre-commit, check changed executable-line coverage above 95%, and verify a browser fixture when available. No cloud deployment is implied by local checks. - -## Delivery -Implemented FR-1 through FR-4 in `frontend/src/ui/AgentWorkspace.tsx`; updated existing source contracts, added `frontend/tests/mpaBasicDetails.test.tsx`, and rebuilt `veadk/webui`. No backend or API contract changes. - -Verification on the diff against `42b50624` (2026-10-09): -- **pass**: `npm --prefix frontend test`: 1,377 Node tests and 64 Vitest tests, including 10 new component cases. -- **pass**: `vitest run --root frontend tests/mpaBasicDetails.test.tsx --coverage --coverage.include=src/ui/AgentWorkspace.tsx --coverage.reporter=json`: changed executable statement-start lines 7/7 (100%). This is changed-line coverage, not whole-component coverage. -- **pass**: `npm --prefix frontend run build`; `npm --prefix frontend run test:webui-assets`: 113 packaged files and 350 references verified. -- **pass**: real Chrome with a temporary loopback fixture using the actual component and mocked fetch: MPA basic-only, General navigation and Integration interaction, General-to-MPA transition, zero Profile requests, narrow viewport layout. Temporary fixture removed before commit. -- **pass**: `uv run --extra dev pre-commit run --all-files`; `git diff --check`; latest `origin/main` fetched and already included. -- **not_applicable**: new input/IME, mutation cancellation/retry flows, and backend tests; these paths were not changed. Hidden section focus is covered by component tests. -- **pass**: user-authorized cloud update of source `4a532405`, stable revision 38 to 39; deployment status `deploy_success`, existing environment hash unchanged, homepage HTTP 200 referencing the new script, and deployed JS/CSS/logo SHA256 values matching the local build. Reloading the existing cloud MPA detail page in Chrome showed only Basic, loaded Runtime information, and no JWT/control-plane error. No cloud mutation or chat E2E was run; native chat session creation retains its existing Profile behavior. - -Direct final review: category gating leaves General navigation and update behavior unchanged, synchronous section resolution prevents stale focus from starting hidden-page requests, and existing effects retain cleanup. English/Chinese requirements and component documentation were reconciled. diff --git a/prd-spec/features/studio-mpa-basic-only/2026-10-09-basic-only.zh.md b/prd-spec/features/studio-mpa-basic-only/2026-10-09-basic-only.zh.md deleted file mode 100644 index c23798b15..000000000 --- a/prd-spec/features/studio-mpa-basic-only/2026-10-09-basic-only.zh.md +++ /dev/null @@ -1,33 +0,0 @@ -# MPA 详情仅保留基本信息 - -- 日期:2026-10-09;状态:已实现、通过本地验证并已部署。 -- 批准依据:用户明确要求隐藏 MPA 详情页其他 Tab,并停止该页面的 Profile 请求。 -- [English](2026-10-09-basic-only.md);契约:[Studio MPA 控制面](../../../specs/studio-mpa-control-plane/README.zh.md)。 - -## 范围与证据 -`AgentWorkspace.tsx` 在选择 MPA 时调用 `getMpaAgentView`,间接请求 Runtime `/profile-status`。仅隐藏导航不能阻止请求。基本信息页底部也有 Profile 配置入口。 - -## 需求与设计 -- FR-1:MPA 详情只显示基本信息,沿用既有布局和样式。普通 Agent 保留原有页面。 -- FR-2:MPA 的任意目标页面(包括恢复或外部焦点)在副作用运行前同步解析为 `basic`。 -- FR-3:基本信息不读取 MPA Agent View、Profile 状态或发起 Profile 修改。控制面加载限定在所属的 Profile/会话页面,并隐藏底部 Profile 入口。 -- FR-4:保留 Runtime 信息、执行流程、聊天/删除操作及其授权。后端 API、原生聊天会话创建和 Profile 存储不在本次范围。 - -## 审查与验证计划 -直接审查:只改变展示和请求调度,不迁移数据或改变鉴权。隐藏的既有实现保留,后续需明确重新启用。本地没有 `ui-ux-pro-max`,采用现有组件规范及直接审查。核对双语一致性和既有样式。 - -回归:渲染真实工作台并模拟网络边界,覆盖全部隐藏页面焦点、MPA 切换、普通 Agent 导航、基本信息展示以及 Profile/控制面零请求。执行前端测试、构建、资源检查和 pre-commit,检查新增可执行行覆盖率超过 95%,在可用时验证浏览器夹具。本地检查不代表已部署云端。 - -## 交付 -在 `frontend/src/ui/AgentWorkspace.tsx` 实现 FR-1 至 FR-4,更新既有源码契约测试,新增 `frontend/tests/mpaBasicDetails.test.tsx`,重新构建 `veadk/webui`。未改变后端或 API 契约。 - -针对 `42b50624` 之上的差异验证(2026-10-09): -- **pass**:`npm --prefix frontend test`:1,377 个 Node 测试和 64 个 Vitest 测试通过,包含 10 个新增组件用例。 -- **pass**:`vitest run --root frontend tests/mpaBasicDetails.test.tsx --coverage --coverage.include=src/ui/AgentWorkspace.tsx --coverage.reporter=json`:变更的可执行语句起始行覆盖 7/7(100%)。这是增量行覆盖率,不是整个组件覆盖率。 -- **pass**:`npm --prefix frontend run build`;`npm --prefix frontend run test:webui-assets`:验证 113 个打包文件及 350 个引用。 -- **pass**:真实 Chrome 加载临时本地夹具,使用真实组件与模拟 fetch:MPA 仅基本信息、普通 Agent 导航及集成页面交互、普通 Agent 切回 MPA、Profile 零请求、窄窗口布局。提交前移除临时夹具。 -- **pass**:`uv run --extra dev pre-commit run --all-files`;`git diff --check`;已获取最新 `origin/main`,当前分支已包含该基线。 -- **not_applicable**:新增输入/IME、写入取消/重试流程及后端测试;本次未修改这些路径。隐藏页面焦点由组件测试覆盖。 -- **pass**:经用户授权,将源码 `4a532405` 更新到云端,稳定版本由 38 升至 39;部署状态 `deploy_success`,既有环境配置哈希不变,首页 HTTP 200 并引用新脚本,线上 JS/CSS/logo 的 SHA256 与本地构建一致。在 Chrome 刷新既有云端 MPA 详情页后,仅显示基本信息,Runtime 信息加载成功,不再显示 JWT/控制面错误。未执行云端写入或聊天 E2E;原生聊天会话创建保留原有 Profile 行为。 - -最终直接审查:类别限制保留普通 Agent 的导航与更新行为,同步解析页面避免旧焦点启动隐藏页面请求,既有副作用保留清理逻辑。已核对中英文需求与组件文档一致性。 diff --git a/prd-spec/features/studio-mpa-cron-tasks/2026-09-15-mono-task-management.md b/prd-spec/features/studio-mpa-cron-tasks/2026-09-15-mono-task-management.md deleted file mode 100644 index 74f868665..000000000 --- a/prd-spec/features/studio-mpa-cron-tasks/2026-09-15-mono-task-management.md +++ /dev/null @@ -1,54 +0,0 @@ -# Runtime task management aligned with mono - -[中文版](2026-09-15-mono-task-management.zh.md) - -Change ID: studio-mpa-cron-management. Created/revised: 2026-09-15. Status: implemented. -Component: [Runtime task viewer](../../../specs/studio-mpa-cron-tasks/README.md). - -## Evidence and intent - -The user requests the Studio Runtime task page match the supplied mono screenshot and implementation. Studio currently exposes only task listing through `frontend/server/mpa_cron.py`. Mono's `SharedAgent/Cron/index.tsx` uses `CronStandalonePanel`; `packages/claw-cron/src/components/` owns list, calendar, form, detail and actions. Its workspace dependencies prevent a simple standalone import into Studio. Reuse its observable behavior and field mapping with Studio's existing UI primitives, without modifying mono. - -MPA commit `91fd6a3` restores user-scoped reads. All existing `/api/v1/esa-cron-tasks` operations require user identity even with JWT disabled. No JWT acquisition or all-user fallback is authorized. The approved identity source is the authenticated Studio principal. - -## Requirements and scenarios - -- FR-1: Selecting a Runtime shows its name/region and a list/calendar switch. The list is the focal point: task, last execution, schedule, last status with filter, executing Agent and actions. Use the supplied white/neutral surface, restrained blue links, green success, muted pending and red failure states, existing system typography and Studio tokens. Place compact overview metrics above the table and creation at top right; avoid dashboard metric cards and raw schedule JSON as the primary view. -- FR-2: Create, edit, copy, delete, enable/disable and run now use existing MPA endpoints. Copy opens a prefilled form and creates only on save; delete requires confirmation. Name opens task detail and paginated execution history. Failed writes keep form input and display actionable errors. -- FR-3: Calendar follows mono's schedule semantics and timezone handling, including one-shot and recurring tasks. It must load all required server pages and never present a single page as a complete calendar. The last execution column uses the last execution timestamp, never nextRunAt. -- FR-4: Use the selected Runtime's server-resolved endpoint and gateway key. Add only allowlisted task routes. Keep credentials out of the browser. JWT-enabled Runtime errors remain errors; do not restore TOP/JWT flows or widen MPA permissions. -- FR-5: Preserve expectedVersion on updates and clientToken on retries of the same create/run operation. Prevent duplicate submits; show version conflicts and allow refresh. Runtime changes abort reads and discard stale results; an aborted write must not be reported as undone. -- FR-6: Handle loading, empty, auth, validation, missing endpoint, network and conflict states distinctly. Calendar/list, menus, dialogs and forms support keyboard, Escape, focus restoration, IME and narrow windows. No cloud task is created, executed or deleted solely for UI verification. - -## Contract impact and implementation - -T-1 / FR-4: Resolve the identity decision and revise the paired component spec. Extend `frontend/server/mpa_cron.py` and CLI route wiring for list/create/update/delete/run/history using existing Runtime authorization. Do not expose an arbitrary upstream URL or generic proxy. - -T-2 / FR-2, FR-5: Extend `frontend/src/adk/mpaCronTasks.ts` and client wrappers with typed task/run responses, scheduling fields, versions and mutation requests. Preserve unknown delivery settings on edits. Do not assume mono TOP response casing or its unpaginated API matches MPA REST. - -T-3 / FR-1, FR-3, FR-6: Split the existing Runtime view into focused list/actions, calendar, editor and detail components as needed. Reuse Studio controls/tokens and compare each view with mono. Metrics must reflect their actual server time window; do not label them seven-day statistics without evidence. - -T-4: Add regression/interaction and proxy contract coverage, update bilingual docs and frontend README, regenerate webui, install into local Studio and perform browser checks. Keep existing Studio/TOS tasks and session behavior unchanged. - -## Acceptance and verification - -- AC-1 / T-3: Screenshot-aligned list and functioning calendar, accurate status/time/Agent rendering, responsive layout and keyboard operation. -- AC-2 / T-1, T-2: Every visible action reaches its specific MPA route with the same user identity, gateway authorization and correct optimistic-concurrency/idempotency fields. Negative cases prove no credential leaks, arbitrary proxying or cross-Runtime stale data. -- AC-3 / T-4: Incremental executable coverage exceeds 95%; run `npm --prefix frontend test`, `npm --prefix frontend run test:mpa-cron-coverage`, `npm --prefix frontend run build`, `npm --prefix frontend run check:i18n`, `npm --prefix frontend run test:webui-assets`, targeted Python tests, Ruff, Pyright and pre-commit. Record simulated versus live outcomes separately. - -## Review and delivery record - -Source review: pass, 2026-09-15. Bilingual design review: pass for scope, endpoint ownership, error isolation and testability; identity is resolved to the authenticated Studio principal. User design approval: granted on 2026-09-15 (go ahead); use the authenticated Studio principal. Production and test implementation completed; verification is recorded below. No deployment is part of this change. Prior GitHub SSH push failure may still block publishing; recheck at delivery and report truthfully. - -## Final verification — 2026-09-15 - -User approval: go ahead; trusted Studio principal selected. Implementation review: pass for route allowlists, credentials, optimistic versions, retry tokens, cancellation, timezone/DST and bilingual consistency. - -- Frontend component/API suite: 71 pass; line coverage 99.71%, branch coverage 97.19%. -- Studio frontend regression suite: 1096 pass. -- Server proxy suite: 12 pass, 100% line coverage. -- TypeScript/Vite production build, i18n (2 locales / 17 namespaces), packaged assets (102 files / 246 references), Ruff and pre-commit secret scanning: pass. -- Scoped server Pyright has no new diagnostics. Full CLI Pyright remains blocked by the same 34 pre-existing diagnostics as before this change; its only change is route callback wiring. -- Real browser fixture: list/actions, calendar, editor, Escape/focus restoration and 760px narrow viewport inspected; pass. No fixture files are shipped. -- Local Studio installed and restarted. Live selected Runtime r-yetmv58y68itpb2txuvg: list HTTP 200, 1 task; task detail shows 1 successful execution. No live task writes were performed. Runtime was already Ready on V10/image 20260915-073a536; this change did not deploy it. -- Source branch synchronized with feat/mpa-agent-oneclick-provision before gates. Git publishing result is recorded in the delivery message. diff --git a/prd-spec/features/studio-mpa-cron-tasks/2026-09-15-mono-task-management.zh.md b/prd-spec/features/studio-mpa-cron-tasks/2026-09-15-mono-task-management.zh.md deleted file mode 100644 index 9b64189ce..000000000 --- a/prd-spec/features/studio-mpa-cron-tasks/2026-09-15-mono-task-management.zh.md +++ /dev/null @@ -1,54 +0,0 @@ -# 对齐 mono 的 Runtime 定时任务管理 - -[English](2026-09-15-mono-task-management.md) - -变更 ID:studio-mpa-cron-management。创建/修订日期:2026-09-15。状态:implemented。 -组件:[Runtime 定时任务查看器](../../../specs/studio-mpa-cron-tasks/README.zh.md)。 - -## 证据和目标 - -用户要求 Studio Runtime 定时任务页面参照所附截图及 mono 实现。Studio 当前通过 `frontend/server/mpa_cron.py` 仅提供列表。mono 的 `SharedAgent/Cron/index.tsx` 使用 `CronStandalonePanel`;`packages/claw-cron/src/components/` 包含列表、日历、表单、详情和操作。其 workspace 依赖使其无法直接作为独立组件导入 Studio。因此使用 Studio 现有控件复用其交互行为和字段映射,不修改 mono。 - -MPA 提交 `91fd6a3` 已恢复按用户读取。现有 `/api/v1/esa-cron-tasks` 操作即使关闭 JWT 也要求用户身份。此次不恢复 JWT 获取或全用户兜底。经用户确认,身份来自当前认证的 Studio 用户。 - -## 需求和场景 - -- FR-1:选择 Runtime 后展示名称/地域及列表/日历切换。以列表为主体,列为任务、上次执行、执行周期、带筛选的上次执行状态、执行 Agent 和操作。沿用截图的白色/中性底色、克制蓝色链接、绿色成功、灰色待执行和红色失败,使用现有系统字体及 Studio 变量。统计摘要放表格右上方,创建入口放页面右上角;不使用大统计卡片或把原始周期 JSON 作为主展示。 -- FR-2:创建、编辑、复制、删除、启停和立即执行调用已有 MPA 接口。复制先打开预填表单,保存后才创建;删除需要确认。任务名称打开详情及分页执行记录。写入失败保留表单内容并给出可操作错误。 -- FR-3:日历参照 mono 的周期语义和时区处理,支持单次与循环任务。加载所需的全部服务端分页,不能把单页冒充完整日历。上次执行列使用实际执行时间,不用 nextRunAt 替代。 -- FR-4:端点及网关密钥由服务端按所选 Runtime 解析,只增加明确允许的任务路由。凭证不进入浏览器。JWT 开启时保留鉴权错误,不恢复 TOP/JWT 流程,不扩大 MPA 权限。 -- FR-5:更新保留 expectedVersion;同一次创建/执行操作的重试保留 clientToken。防重复提交,版本冲突提示刷新。切换 Runtime 取消读取并丢弃过期响应;取消写请求不能宣称撤销了已发生的操作。 -- FR-6:区分加载、空数据、鉴权、校验、接口缺失、网络及版本冲突状态。列表/日历、菜单、弹窗、表单支持键盘、Escape、焦点恢复、IME 和窄窗口。不为验证 UI 而擅自创建、执行或删除云端任务。 - -## 契约影响和实施 - -T-1 / FR-4:明确身份来源,更新双语组件规范。扩展 `frontend/server/mpa_cron.py` 和 CLI 路由装配,在既有 Runtime 授权后支持列表/创建/更新/删除/执行/历史;不开放任意上游 URL 或通用代理。 - -T-2 / FR-2、FR-5:扩展 `frontend/src/adk/mpaCronTasks.ts` 和客户端封装,补齐任务/执行响应、周期字段、版本和写入请求类型。编辑时保留未知投递配置。不把 mono TOP 响应大小写或无分页协议直接套入 MPA REST。 - -T-3 / FR-1、FR-3、FR-6:按需拆分现有 Runtime 页面为列表/操作、日历、编辑器和详情组件。复用 Studio 控件及变量,逐项对照 mono。统计文案依据服务端实际时间窗口;没有证据不能标记为近七日。 - -T-4:补回归/交互和代理契约覆盖,更新双语文档及 frontend README,重新生成 webui,安装到本地 Studio 并浏览器验证。既有 Studio/TOS 定时任务和会话行为保持不变。 - -## 验收和验证 - -- AC-1 / T-3:列表对齐截图且日历可用,状态/时间/Agent 准确,响应式布局和键盘操作正常。 -- AC-2 / T-1、T-2:每个操作以一致用户身份、网关授权及正确的并发控制/幂等字段调用对应 MPA 路由。负向验证覆盖凭证泄漏、任意代理及跨 Runtime 过期数据。 -- AC-3 / T-4:可执行增量覆盖率超过 95%;执行 `npm --prefix frontend test`、`npm --prefix frontend run test:mpa-cron-coverage`、`npm --prefix frontend run build`、`npm --prefix frontend run check:i18n`、`npm --prefix frontend run test:webui-assets`、目标 Python 测试、Ruff、Pyright 和 pre-commit。分别记录模拟与真实验证结果。 - -## 审核和交付记录 - -源码核对:pass,2026-09-15。双语设计审核:范围、端点归属、错误隔离及可测性 pass;身份来源已确定为认证的 Studio 用户。用户设计确认:2026-09-15 用户已确认实施,使用认证的 Studio 用户。生产代码和测试已实现,验证结果见下文。本次不包含部署。此前 GitHub SSH 推送失败可能仍阻碍发布;交付时重新检查并如实报告。 - -## 最终验证 — 2026-09-15 - -用户已确认实施,身份使用认证的 Studio 用户。实现审核:路由白名单、凭证、乐观版本、重试令牌、取消、时区/夏令时及双语一致性 pass。 - -- 前端组件/API 测试:71 项通过;行覆盖率 99.71%,分支覆盖率 97.19%。 -- Studio 前端回归:1096 项通过。 -- 服务端代理测试:12 项通过,行覆盖率 100%。 -- TypeScript/Vite 生产构建、i18n(2 种语言 / 17 个命名空间)、打包资源(102 文件 / 246 引用)、Ruff 和 pre-commit 密钥扫描:pass。 -- 服务端模块 Pyright 无新增诊断。完整 CLI Pyright 仍受此前相同的 34 项既存诊断阻碍;本次仅修改该文件的一行路由回调装配。 -- 真实浏览器模拟数据验收:检查列表/操作、日历、编辑器、Escape/焦点恢复及 760px 窄窗口,pass。模拟数据文件不随产品发布。 -- 已安装并重启本地 Studio。真实目标 Runtime r-yetmv58y68itpb2txuvg 列表 HTTP 200,1 条任务;详情展示 1 条成功执行记录。未进行真实任务写入。Runtime 原已为 Ready/V10,镜像 20260915-073a536,本次未部署 Runtime。 -- 验证前已同步 feat/mpa-agent-oneclick-provision 基线。Git 发布结果在交付消息中报告。 diff --git a/prd-spec/features/studio-mpa-cron-tasks/2026-09-15-remove-agent-input.md b/prd-spec/features/studio-mpa-cron-tasks/2026-09-15-remove-agent-input.md deleted file mode 100644 index f6e40fa37..000000000 --- a/prd-spec/features/studio-mpa-cron-tasks/2026-09-15-remove-agent-input.md +++ /dev/null @@ -1,5 +0,0 @@ -Remove the executor Agent input at the user’s explicit request. New tasks use the selected Runtime ID as agentId; edits and copies retain the original agentId. The Runtime endpoint still determines execution. No backend or authentication changes. Review: minimal scope, required API field retained, bilingual consistency checked. Acceptance: no Agent input; create/edit/copy payloads remain valid; coverage exceeds 95%. User approval: explicit screenshot request to remove the field. - -Verification: 71 targeted tests passed; editor line coverage 100%, overall lines 99.71%. 1096 frontend tests passed; build, asset check and pre-commit passed. Browser refresh was not performed because the user has an unsaved task form open. - -New task creation omits agentId. The Runtime resolves the configured Agent display name, falling back to its runtime agent name. Explicit IDs and edit/copy values remain unchanged. This supersedes the previous Runtime ID default. Approved by the user on 2026-09-15; review found no authentication or execution routing changes. diff --git a/prd-spec/features/studio-mpa-cron-tasks/2026-09-15-remove-agent-input.zh.md b/prd-spec/features/studio-mpa-cron-tasks/2026-09-15-remove-agent-input.zh.md deleted file mode 100644 index 422a65b33..000000000 --- a/prd-spec/features/studio-mpa-cron-tasks/2026-09-15-remove-agent-input.zh.md +++ /dev/null @@ -1,5 +0,0 @@ -按用户截图的明确要求移除执行 Agent 输入框。新任务使用所选 Runtime ID 作为 agentId;编辑和复制保留原任务 agentId。执行目标仍由 Runtime 地址决定。不修改后端和鉴权。设计复核:范围最小,保留接口必填参数,中英文一致。验收:无 Agent 输入框,创建/编辑/复制参数有效,覆盖率超过 95%。用户批准:明确要求移除截图中字段。 - -验证:71 个专项测试通过;编辑器行覆盖率 100%,整体行覆盖率 99.71%。1096 个前端测试通过;构建、资源检查及 pre-commit 通过。用户仍有未保存的任务表单,未刷新其浏览器页面。 - -创建新任务不传 agentId,由 Runtime 使用配置中的 Agent 展示名称,缺失时回退运行时 Agent 名称。显式值及编辑/复制原值不变。此规则替代此前默认填写 Runtime ID 的处理。用户于 2026-09-15 明确批准;复核确认不改变鉴权和执行路由。 diff --git a/prd-spec/features/studio-mpa-cron-tasks/README.md b/prd-spec/features/studio-mpa-cron-tasks/README.md deleted file mode 100644 index 9b0cafcb5..000000000 --- a/prd-spec/features/studio-mpa-cron-tasks/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# Studio MPA Runtime scheduled tasks - -[中文版](README.zh.md) - -Date: 2026-09-15. Change: studio-mpa-cron-tasks. Status: design reviewed; implementation authorized by the current user request. - -## Evidence and scope -Studio lists owner-scoped TOS jobs through /web/cronjobs. MPA separately exposes GET /api/v1/esa-cron-tasks, filtered by its validated user identity. Add a read-only source tab for the currently selected Runtime; do not merge, migrate, create, edit, delete or run tasks. - -## Requirements and design -FR-1: Pass the selected cloud Runtime ID, name and region from App to CronJobs. Show the target explicitly. No selection means no request; never fall back to another Runtime. -FR-2: Use the existing Runtime proxy and API key handling. Forward an optional X-Jwt-Token entered in a masked field. Keep it only in component memory, clearing it on Runtime/region changes and unmount. Never derive or forge identity, persist credentials or weaken authentication. -FR-3: Show name, enabled state, schedule, next execution and latest result. Fetch 20 records per page including disabled tasks. Provide previous/next and refresh. Separate loading, empty, unavailable API, authentication and other failures. A failed fetch is never an empty list. -FR-4: Abort requests on changes/unmount and ignore late responses. Clear prior target data immediately, preserve readable same-page data while refreshing. Keep Studio task behavior unchanged. - -## Files and tasks -T-1: Add the typed client adapter and Runtime panel; wire App/CronJobs and bilingual translations. -T-2: Add HTTP/component regression tests before implementation, cover pagination, failures, cancellation and switching targets. -T-3: Run npm test, build, i18n and incremental coverage above 95%; check browser layout, built assets and secrets; commit/push and update local Studio assets. - -## Review and acceptance -Design review: existing components and proxy suffice; no dependencies or backend changes. Bilingual texts are equivalent. Existing Studio tasks and MPA tasks remain separate. User request explicitly authorizes the module; current session authorizes commit/push. The repo-referenced ui-ux-pro-max skill is unavailable; apply frontend/SPEC.md and available frontend-design guidance directly. -AC-1: Selected target and real upstream path match; pagination and states pass tests. AC-2: No stale target data or stored credentials. AC-3: UI and build pass; live data requires a valid MPA JWT and is reported separately from simulated tests. - -## Verification — 2026-09-15 - -Pass: 1095 frontend tests; 34 isolated HTTP/component tests; new adapter/panel statements, branches, functions and lines 100%; TypeScript and both Vite builds; i18n (2 locales, 17 namespaces); packaged assets (102 files, 246 references). Pre-commit Ruff and secret scan pass. Browser verified no-target state, selection propagation and the real selected Runtime proxy request returning 401 with the JWT-required message. Existing Studio tab remains separate. The local Studio assets are updated; its prior bundle is backed up outside the repository. Authenticated real task data and a narrow-viewport screenshot are not verified; real task data requires a valid user JWT. No production task mutations were performed. - -## Risks and limits -Runtime-level selection does not grant all-user visibility. Missing JWT is an authentication state, not absence of tasks. No scheduler or MPA business logic changes. Generated assets are included for local Studio delivery. Verification results will be recorded here before delivery. - -## Automatic credentials revision — 2026-09-15 -The current user authorizes replacing manual JWT entry with server-side TOP GetMpaInstanceToken (2026-03-01). Resolve MPA_AGENT_ID, MPA_SPACE_ID/CLAW_SPACE_ID, MPA_IS_DEBUG_RUNTIME and ARKCLAW_TOP_SERVICE from the authorized selected Runtime. Use trusted enterprise user_pool_user_uid; local administrators configure VEADK_STUDIO_MPA_USER_UID on the server. Never use a browser-supplied identity to acquire credentials. TOP signs the JWT. Match its HTTPS endpoint against the selected Runtime before forwarding Authorization and X-Jwt-Token. Acquire fresh credentials per list request; do not cache, persist or return credentials to the browser. Keep the read-only scope and add task prompt details plus executionCount/successRate summaries following mono SharedAgent/Cron. Missing configuration and TOP errors remain actionable errors. Tests must cover identity, endpoint mismatch, upstream failures, credential containment and cancellation, with incremental coverage above 95%. This revision supersedes the previous manual-JWT contract and its completed verification applies only to the previous revision. - -## Revision verification — 2026-09-15 - -Pass: 43 frontend contract/component tests and 31 Python tests; both new module scopes have 100% statement/branch coverage. All 1095 frontend regression tests, TypeScript/Vite builds, i18n and 102 packaged asset checks pass. New Python module Pyright passes. The CLI file has 34 pre-existing Pyright diagnostics: baseline comparison confirms identical messages and no new diagnostics. Local Studio was updated and restarted; the browser confirms no JWT field, correct selected Runtime and an actionable missing-user-UID error. A separate read-only TOP probe returned 403 AccessDenied for arkclaw:GetMpaInstanceToken using the configured AK/SK. Authenticated task data remains blocked on this permission and the enterprise UserPoolUserUid. Existing Studio TOS task loading also fails in this environment; that source was not modified. Narrow-viewport verification was not run. - -## Direct Runtime revision (2026-09-15) -The user has disabled Runtime JWT verification and authorizes removal of TOP credential acquisition. Replace the previous credential contract: keep selected-Runtime authorization and gateway authentication, send the current Studio principal owner_id as x-user-id, derive x-space-id and x-mpa-id from Runtime configuration with the existing MPA local defaults, and never acquire or forward X-Jwt-Token. Remove enterprise UID configuration and credential-specific UI messages. Task visibility remains user-scoped. No Runtime setting or business logic is modified. Verify request headers, no TOP calls, pagination, upstream errors and cancellation; incremental coverage must exceed 95%. - -## Final all-user contract — 2026-09-15 -The user explicitly authorizes all-user reads when Runtime JWT authentication is disabled. MPA commit 833c6bc implements that behavior. Studio therefore sends only gateway authorization, without JWT, x-user-id, x-space-id or x-mpa-id, and no longer requires a business identity. This supersedes the preceding user-scoped direct-access revision. Runtime authorization, read-only scope, pagination and existing error handling remain unchanged. - -## Final deployment and verification — 2026-09-15 -Runtime r-yetmv58y68itpb2txuvg is Ready at V9, image tag 20260915-833c6bc-cron-all-read, digest sha256:11d48baadd978f302af899b2bea5fa9b61f6b11bdca1fa9292d85526a5be02ca. Configuration comparison found no changes besides image/version/status/timestamps. Gateway-only public GET and Studio BFF both returned HTTP 200 with total=0. Browser verified the exact selected Runtime and a successful empty list; no test tasks were created. Task selection now survives chat/session loading failures, and resets on a committed agent change. Tests: 1096 frontend regressions, 39 scoped frontend cases and 8 Python cases passed; scoped frontend/backend coverage 100%. Python module Pyright, builds, i18n, packaged assets and pre-commit passed. Existing CLI Pyright baseline issues and Studio TOS source errors remain outside scope. diff --git a/prd-spec/features/studio-mpa-cron-tasks/README.zh.md b/prd-spec/features/studio-mpa-cron-tasks/README.zh.md deleted file mode 100644 index f0a5fe0b7..000000000 --- a/prd-spec/features/studio-mpa-cron-tasks/README.zh.md +++ /dev/null @@ -1,46 +0,0 @@ -# Studio MPA Runtime 定时任务 - -[English](README.md) - -日期:2026-09-15。变更:studio-mpa-cron-tasks。状态:设计已审阅;当前用户请求已授权实现。 - -## 依据和范围 -Studio 通过 /web/cronjobs 查询按所有者隔离的 TOS 任务。MPA 独立提供 GET /api/v1/esa-cron-tasks,按其验证后的用户身份过滤。为当前选择的 Runtime 新增只读来源页签;不合并、迁移、创建、编辑、删除或执行任务。 - -## 需求和设计 -FR-1:将选中云 Runtime 的 ID、名称和地域从 App 传到 CronJobs,明确显示目标。未选择时不请求,不回退其他 Runtime。 -FR-2:复用 Runtime 代理和 API Key 处理,转发遮罩输入框中可选的 X-Jwt-Token。仅保存在组件内存,切换 Runtime/地域或卸载时清除。不推导或伪造身份、不持久化凭据、不降低鉴权。 -FR-3:展示名称、启停、计划、下次执行和最近结果。每页 20 条且包含停用任务,提供上一页、下一页和刷新。区分加载、空数据、不支持接口、鉴权和其他失败,失败不展示为空列表。 -FR-4:变化和卸载时取消请求并忽略迟到响应。立即清除旧目标数据,同页刷新时保留可读内容。现有 Studio 任务行为保持不变。 - -## 文件和任务 -T-1:新增类型化客户端和 Runtime 面板;接入 App/CronJobs,增加双语文案。 -T-2:先写 HTTP/组件回归测试,覆盖分页、错误、取消和切换目标。 -T-3:执行 npm test、构建、国际化检查和超过 95% 的增量覆盖率;检查浏览器布局、产物和密钥;提交推送并更新本地 Studio 产物。 - -## 审阅和验收 -设计审阅:现有组件和代理足够,无新增依赖和后端变更。双语内容等价。Studio 任务和 MPA 任务保持分离。用户已明确授权模块,当前会话授权提交推送。仓库引用的 ui-ux-pro-max 技能不可用,直接遵循 frontend/SPEC.md 和可用 frontend-design 指导。 -AC-1:选中目标与真实请求路径一致,分页与状态通过测试。AC-2:无旧目标数据或持久化凭据。AC-3:UI 和构建通过;真实数据需要有效 MPA JWT,独立报告模拟与真实验证。 - -## 验证 — 2026-09-15 - -通过:1095 项前端测试;34 项独立 HTTP/组件测试;新增适配器和面板语句、分支、函数、行覆盖率均为 100%;TypeScript 和两次 Vite 构建;国际化检查(2 个语言、17 个命名空间);打包资源检查(102 个文件、246 条引用)。Pre-commit 的 Ruff 和密钥扫描通过。浏览器验证了未选目标状态、选择传递,以及选中 Runtime 的真实代理请求返回 401 并显示需要 JWT 的提示。现有 Studio 页签保持独立。本地 Studio 产物已更新,旧产物备份在仓库之外。带有效身份的真实任务数据和窄视口截图未验证;真实数据需要有效用户 JWT。未修改任何线上定时任务。 - -## 风险和限制 -按 Runtime 选择不代表可看所有用户任务。缺少 JWT 属于鉴权状态,不是没有任务。不修改调度器或 MPA 业务逻辑。包含构建产物以交付本地 Studio。交付前在此记录验证结果。 - -## 自动凭证修订 — 2026-09-15 -当前用户授权将手动 JWT 输入替换为服务端调用 TOP GetMpaInstanceToken(2026-03-01)。从已授权的所选 Runtime 读取 MPA_AGENT_ID、MPA_SPACE_ID/CLAW_SPACE_ID、MPA_IS_DEBUG_RUNTIME 和 ARKCLAW_TOP_SERVICE。使用可信企业身份 user_pool_user_uid;本地管理员在服务端配置 VEADK_STUDIO_MPA_USER_UID。禁止使用浏览器传入的身份申请凭证。JWT 由 TOP 签发。转发 Authorization 和 X-Jwt-Token 前校验其 HTTPS 地址与所选 Runtime 相同。每次列表请求获取新凭证,不缓存、持久化或向浏览器返回凭证。保持只读范围,参照 mono SharedAgent/Cron 增加任务提示词详情、执行次数与成功率。配置缺失和 TOP 失败需明确展示。测试覆盖身份、地址不匹配、上游错误、凭证隔离和取消,增量覆盖率超过 95%。本修订替代旧手动 JWT 契约;此前验证结果仅针对旧版本。 - -## 修订验证 — 2026-09-15 - -通过:43 个前端契约/组件测试、31 个 Python 测试;新增模块范围的语句和分支覆盖率均为 100%。全部 1095 个前端回归测试、TypeScript/Vite 构建、国际化与 102 个打包资源检查通过。新增 Python 模块 Pyright 通过。CLI 文件有 34 个原有 Pyright 问题,基线对比确认诊断信息相同,没有新增问题。本地 Studio 已更新并重启;浏览器确认没有 JWT 输入框,所选 Runtime 正确,缺少企业用户 UID 时给出明确提示。使用现有 AK/SK 单独进行的只读 TOP 探测返回 403 AccessDenied,缺少 arkclaw:GetMpaInstanceToken 权限。真实任务数据仍需补齐该权限及企业 UserPoolUserUid。当前环境原有 Studio TOS 任务来源也加载失败,本次没有修改该来源。窄屏验证未执行。 - -## 直接访问 Runtime 修订(2026-09-15) -用户已关闭 Runtime 的 JWT 校验,并授权移除 TOP 凭证申请。替代此前的凭证契约:保留所选 Runtime 的权限检查及网关鉴权,使用当前 Studio 身份的 owner_id 作为 x-user-id;从 Runtime 配置读取 x-space-id 和 x-mpa-id,缺失时沿用 MPA 的本地默认值;不再申请或转发 X-Jwt-Token。移除企业 UID 配置及凭证相关页面提示。任务仍按用户筛选。本次不修改 Runtime 配置或业务逻辑。验证请求头、不调用 TOP、分页、上游异常与取消;增量覆盖率须超过 95%。 - -## 最终全量读取契约 — 2026-09-15 -用户明确授权在 Runtime 关闭 JWT 时开放全量用户读取,MPA 提交 833c6bc 已实现此行为。Studio 只携带网关鉴权,不发送 JWT、x-user-id、x-space-id 或 x-mpa-id,也不再要求业务用户身份。本节替代之前按用户筛选的直接访问修订。保留 Runtime 权限检查、只读范围、分页与现有错误处理。 - -## 最终部署与验证 — 2026-09-15 -Runtime r-yetmv58y68itpb2txuvg 已发布 V9,状态 Ready,镜像标签 20260915-833c6bc-cron-all-read,digest 为 sha256:11d48baadd978f302af899b2bea5fa9b61f6b11bdca1fa9292d85526a5be02ca。配置对比确认除镜像、版本、状态、时间戳外没有变化。公网 GET 仅带网关鉴权,Studio BFF 同样返回 HTTP 200、total=0。浏览器确认目标 Runtime 正确,成功显示空列表;未创建测试任务。任务查看保留聊天/会话加载失败时选中的 Runtime,并在成功切换 Agent 时重置。1096 个前端回归、39 个专项前端用例、8 个 Python 用例通过;专项前后端覆盖率均为 100%。Python 模块 Pyright、构建、国际化、资源检查及 pre-commit 通过。CLI 原有 Pyright 问题和 Studio TOS 来源错误不在本次范围内。 diff --git a/prd-spec/features/studio-mpa-identity-reuse/2026-09-23-deploy-identity-reuse.md b/prd-spec/features/studio-mpa-identity-reuse/2026-09-23-deploy-identity-reuse.md deleted file mode 100644 index 7feb27d2b..000000000 --- a/prd-spec/features/studio-mpa-identity-reuse/2026-09-23-deploy-identity-reuse.md +++ /dev/null @@ -1,35 +0,0 @@ -# Reuse Studio Identity for managed MPA creation - -[中文版](2026-09-23-deploy-identity-reuse.zh.md) - -- Change ID: `studio-mpa-identity-reuse`; created/revised 2026-09-23; status: approved. -- Approved by the user in this task after reviewing the environment-backed approach. -- Components: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md). - -## Background, goals, and scope - -`veadk studio deploy` persists Studio UserPool/client UIDs and later its OAuth callback in VeFaaS. The legacy MPA creation path resolves their names, but managed creation loads Identity names and callback only from YAML. The new shared PostgreSQL Workspace is prepared during MPA creation, not Studio deployment, so it cannot store deployment-time Identity metadata. The goal is for later managed MPA creations to use the Studio login resources automatically. No new PostgreSQL table, browser input, UserPool/client creation, or update of existing Runtimes is in scope. - -## Scenarios and requirements - -- `FR-1`: After Studio deployment identifies its UserPool/client, resolve their names with deploy credentials and persist names, Identity region, and `https:///oauth/callback` as non-secret VeFaaS environment values in the second release. Existing UID and Studio login callback behavior is preserved. -- `FR-2`: Managed MPA creation merges those server-owned values into the profile before the child process performs cloud writes. The child loads the same values from inherited environment after restart. It injects `MPA_USER_POOL_NAME`, `MPA_USER_POOL_CLIENT_NAME`, `IDENTITY_CALLBACK_URL`, and `IDENTITY_REGION` into the new Runtime. -- `FR-3`: A partial deployment identity set, or an explicitly different YAML Identity value, fails locally with a safe configuration error. A complete matching YAML set is accepted. When no deployment identity exists, standalone CLI/YAML behavior is unchanged. Identity values never enter the browser request or task database. - -## Design and impact - -Use four `VEADK_STUDIO_MPA_*` environment keys; reuse existing `IdentityClient.get_user_pool_resource_names` and the managed profile/Runtime environment path. The VeFaaS second release owns the public callback URL. The profile loader is the shared boundary for Studio inspection, task admission, and the fixed runner; it performs all-or-none and conflict checks before provisioning. Studio env wins only where YAML is absent. An explicit mismatch is rejected, including `managed.runtime.env`. Identity region is required because it may differ from deployment region. No schema, frontend API, task payload, or dependency changes. UserPool and callback names are not credentials; AK/SK and client secret remain server-only. - -Failure to resolve resource names stops deployment with a redacted operator error. Callback registration retains its current warning behavior. Existing deployed Studio functions need a redeploy to receive the new values; existing MPA Runtimes are not modified. Live Identity/VeFaaS/Runtime E2E is not performed in local tests. - -## Tasks, acceptance, and review - -- `T-1` (`FR-1`, `AC-1`): Save the four values on the second VeFaaS release; test external UIDs and non-Beijing Identity region. -- `T-2` (`FR-2`, `FR-3`, `AC-2`): Merge and validate server environment in `load_profile`, then inject the resolved region in managed Runtime settings; test missing, matching, conflicting, partial, and standalone cases. -- `T-3` (`AC-3`): Update bilingual contract/operator docs and run affected Python tests, pre-commit, and broader regression as feasible. Record actual results below. - -Review: no new PG table because that PG does not exist at Studio deployment; no browser Identity fields or secrets; validation occurs before cloud writes. The user approved the environment-backed design. Risks: cloud callback registration can still warn and live provider permissions/Runtime startup remain unverified. - -## Verification record - -On 2026-09-23, the implementation passed `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_studio_deploy_target.py tests/cli/test_cli_mpa.py -q` (427 tests). Isolated changed-line coverage is 19/19 executable lines (100%): 13/13 in managed configuration, 2/2 in Runtime identity application, and 4/4 in Studio deploy. A combined pytest-cov run failed due Pydantic/SDK collection behavior under instrumentation; the identical affected suite passed without instrumentation. The broad suite reported 5,189 passed, 9 failed, and 2 collection errors: 6 Harness failures require the absent optional `llama_index`, 2 Skill version failures have an unrelated source-region mismatch, 1 Workspace Editor failure lacks `node` on PATH, and 2 Sandbox test modules require the absent optional `anthropic`. Pre-commit passed. Pyright with the project interpreter reported 36 existing errors, none in added lines. No frontend source/assets changed, so frontend build is not applicable. Real cloud Identity/VeFaaS/Runtime E2E: not run; deployment permissions and callback behavior remain operational verification items. diff --git a/prd-spec/features/studio-mpa-identity-reuse/2026-09-23-deploy-identity-reuse.zh.md b/prd-spec/features/studio-mpa-identity-reuse/2026-09-23-deploy-identity-reuse.zh.md deleted file mode 100644 index 9bdbd34d7..000000000 --- a/prd-spec/features/studio-mpa-identity-reuse/2026-09-23-deploy-identity-reuse.zh.md +++ /dev/null @@ -1,35 +0,0 @@ -# 托管 MPA 创建复用 Studio 身份资源 - -[English](2026-09-23-deploy-identity-reuse.md) - -- 变更 ID:`studio-mpa-identity-reuse`;创建/修订:2026-09-23;状态:approved。 -- 用户在本任务确认以环境变量保存并复用的方案。 -- 组件:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md)。 - -## 背景、目标与范围 - -`veadk studio deploy` 已将 Studio UserPool/客户端 UID 保存到 VeFaaS,并在得到公网地址后保存 OAuth 回调。旧 MPA 创建路径会解析资源名称,但新托管创建只从 YAML 读取 Identity 名称和回调。新的共享 PostgreSQL Workspace 要到创建 MPA 时才准备,因此无法存放 Studio 部署时的 Identity 信息。目标是让后续托管 MPA 自动复用 Studio 登录资源。不新增 PostgreSQL 表、浏览器输入或 UserPool/客户端创建流程,也不修改已有 Runtime。 - -## 场景与需求 - -- `FR-1`:Studio 部署确定 UserPool/客户端后,使用部署凭据解析名称,并在第二次发布中将名称、Identity 地域以及 `https:///oauth/callback` 作为非密钥 VeFaaS 环境变量保存。保留既有 UID 和 Studio 登录回调行为。 -- `FR-2`:托管 MPA 创建在子进程执行云写入前合并这些服务端值。子进程重启后仍从继承环境读取相同值,并向新 Runtime 注入 `MPA_USER_POOL_NAME`、`MPA_USER_POOL_CLIENT_NAME`、`IDENTITY_CALLBACK_URL`、`IDENTITY_REGION`。 -- `FR-3`:部署身份配置不完整,或 YAML 显式配置不同的 Identity 值时,在本地返回安全的配置错误。完整且一致的 YAML 可用;没有部署身份配置时保持独立 CLI/YAML 行为。身份值不进入浏览器请求或任务数据库。 - -## 设计与影响 - -使用四个 `VEADK_STUDIO_MPA_*` 环境变量,复用已有 `IdentityClient.get_user_pool_resource_names` 和托管配置/Runtime 环境链路。VeFaaS 第二次发布负责确定公网回调地址。配置加载器是 Studio 配置检查、任务接纳和固定子进程的共同边界,在云资源写入前校验完整性与冲突;只在 YAML 缺失时采用 Studio 环境值,显式冲突则拒绝,包括 `managed.runtime.env`。Identity 地域必须保存,因为它可能不同于部署地域。不改数据库结构、前端 API、任务载荷和依赖。UserPool 名称与回调不是密钥;AK/SK 和客户端密钥仍仅在服务端。 - -资源名称解析失败时,以脱敏的运维错误停止部署。回调注册沿用现有警告行为。已有云上 Studio 需要重新部署才能获得新环境值;已有 MPA Runtime 不变。本地测试不能证明真实 Identity/VeFaaS/Runtime 端到端链路。 - -## 任务、验收与评审 - -- `T-1`(`FR-1`、`AC-1`):第二次 VeFaaS 发布保存四项;测试外部 UID 和非北京 Identity 地域。 -- `T-2`(`FR-2`、`FR-3`、`AC-2`):在 `load_profile` 合并并校验服务端环境,再在托管 Runtime 注入解析后的地域;测试缺失、一致、冲突、部分配置和独立模式。 -- `T-3`(`AC-3`):同步中英组件契约/操作文档,执行相关 Python 测试、pre-commit 和可行的扩大回归;在下文记录实际结果。 - -评审:Studio 部署时公共 PG 尚不存在,因此不新增表;不向浏览器传身份值或密钥;在云写入前校验。用户已确认环境变量方案。风险:回调注册仍可能只警告,真实服务商权限和 Runtime 启动尚未验证。 - -## 验证记录 - -2026-09-23 实现后,`uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_studio_deploy_target.py tests/cli/test_cli_mpa.py -q` 通过(427 项)。隔离采集的改动可执行行覆盖率为 19/19(100%):托管配置 13/13、Runtime 身份注入 2/2、Studio 部署 4/4。合并运行 pytest-cov 时,Pydantic/SDK 在插桩下出现收集行为异常;相同的受影响测试集在不插桩时通过。全量回归 5189 项通过、9 项失败、2 项收集错误:6 项 Harness 失败缺少可选 `llama_index`,2 项 Skill 版本失败为无关的来源地域不匹配,1 项 Workspace Editor 失败是 PATH 中缺少 `node`,2 个 Sandbox 测试模块缺少可选 `anthropic`。Pre-commit 通过。使用项目解释器的 Pyright 报告 36 个既有错误,新增行无错误。本次不修改前端源码/产物,前端构建不适用。真实云上 Identity/VeFaaS/Runtime 端到端未运行;部署权限与回调仍需运维验证。 diff --git a/prd-spec/features/studio-mpa-login/2026-09-23-studio-mpa-prewarm.md b/prd-spec/features/studio-mpa-login/2026-09-23-studio-mpa-prewarm.md deleted file mode 100644 index bc9ddcd91..000000000 --- a/prd-spec/features/studio-mpa-login/2026-09-23-studio-mpa-prewarm.md +++ /dev/null @@ -1,31 +0,0 @@ -# Studio MPA Login Prewarm - -- Change ID: `studio-mpa-login` -- Date/status: 2026-09-23, approved by the user's request to continue no-second-login development -- Chinese: [2026-09-23-studio-mpa-prewarm.zh.md](2026-09-23-studio-mpa-prewarm.zh.md) -- Components: [Studio OAuth session](../../../specs/studio-oauth-session/README.md), [Studio MPA prewarm](../../../specs/studio-mpa-prewarm/README.md) - -## Background, goal, and scope - -Studio's browser session has a UserPool refresh token, while MPA web chat currently has only Runtime key-auth and may ask the same user to log in again. MPA already accepts a server-authenticated `POST /identity/sessions/put`; the Runtime key-auth proxy supplies the trusted Studio user ID. Retaining a full ID token in the signed browser cookie can exceed 4 KB. This change makes Studio web chat prewarm MPA with a fresh UserPool ID/refresh-token pair, without sending tokens or Runtime keys to browser JavaScript. Feishu login and MPA's existing token validator are out of scope. The Studio/MPA UserPool client match is an operational prerequisite, not proved by source tests. - -## Requirements and design - -- `FR-1`: Before an MPA Runtime web-chat run, Studio attempts to refresh its OAuth session and obtain a fresh ID token. A failed prewarm does not prevent the run; MPA may then request its normal user login. -- `FR-2`: Studio authenticates and authorizes the selected MPA Runtime, verifies the fresh ID token's signature, issuer, expiry, audience, and subject, and posts the ID/refresh tokens to its fixed `/identity/sessions/put` path with the server-held Runtime key. It never accepts a browser-selected upstream URL. -- `FR-3`: Rotated refresh credentials are returned in the existing HttpOnly signed Studio cookie, while the ID token remains request-local and is excluded from the cookie to avoid browser size limits. Old cookies remain readable. -- `FR-4`: Only MPA Runtime web chat invokes prewarm. Feishu and non-MPA transports are unchanged. Prewarm failure is diagnostic only; user cancellation still stops the run. - -The new `POST /web/mpa/identity-prewarm/{runtime_id}` route accepts only a Runtime ID and region. It uses the already-authenticated Studio cookie, the existing control-plane Runtime authorization and credential resolver, and a bounded outbound request. The frontend invokes it immediately before the MPA run request with a ten-second limit and the chat cancellation signal. Its status is logged without tokens; failure continues to the normal run and manual MPA login. The route returns no token. A rotated session must replace middleware's prior cookie before the response is sent, including when the Runtime handoff fails. - -## Tasks, verification, and risks - -| Requirement | Task | Acceptance | Test | -| --- | --- | --- | --- | -| `FR-1`, `FR-3` | `T-1`: keep ID tokens request-local and preserve rotation | `AC-1`: refresh result is usable but cookie contains no ID token | `tests/auth/test_oauth2_auth.py` | -| `FR-2` | `T-2`: add authorized prewarm endpoint | `AC-2`: trusted subject, fixed destination, no token leakage; invalid inputs fail | `tests/cli/test_frontend_runtime_proxy.py` | -| `FR-4` | `T-3`: invoke prewarm before MPA run | `AC-3`: failed handoff still starts MPA run; cancellation stops it; other transports unchanged | frontend tests/build | - -No new dependency or generated Runtime configuration is planned. Risks: UserPool refresh-token rotation across multiple Studio instances, MPA later rotating the same refresh token, and a mismatched UserPool client. The browser's ten-second prewarm limit can end before an unusually slow provider refresh finishes; that case needs live validation. Test with a real browser and cloud Runtime before claiming end-to-end success. Review: existing server-only credential resolver and OAuth validator are reused; a client-supplied token, URL, or sender ID is forbidden. - -Verification on 2026-09-23: `tests/auth/test_oauth2_auth.py` passed (59); `tests/cli/test_frontend_runtime_proxy.py` and auth together passed (144); `npm --prefix frontend test` passed (1305 Node tests and 25 Vitest tests); focused prewarm Vitest tests passed (4); frontend build and pre-commit passed. Changed executable Python lines: 48/48 covered; changed frontend client statements: 11/11 covered. Pyright with the project interpreter still reports 40 errors outside the changed lines. Broad parallel regression: 5069 passed, 11 skipped, 2 xfailed, 8 failed, and 2 collection errors. Six cloud failures reproduce without the optional `llama_index` extension; two self-host Sandbox collection errors reproduce without the optional `anthropic` package. The two Skill version failures did not reproduce in isolation. Real browser/UserPool/Runtime E2E: not run. diff --git a/prd-spec/features/studio-mpa-login/2026-09-23-studio-mpa-prewarm.zh.md b/prd-spec/features/studio-mpa-login/2026-09-23-studio-mpa-prewarm.zh.md deleted file mode 100644 index 716e0969d..000000000 --- a/prd-spec/features/studio-mpa-login/2026-09-23-studio-mpa-prewarm.zh.md +++ /dev/null @@ -1,31 +0,0 @@ -# Studio MPA 登录预热 - -- Change ID: `studio-mpa-login` -- 日期/状态:2026-09-23,用户要求继续开发免二次登录,方案已获同意 -- English: [2026-09-23-studio-mpa-prewarm.md](2026-09-23-studio-mpa-prewarm.md) -- 组件:[Studio OAuth 会话](../../../specs/studio-oauth-session/README.zh.md)、[Studio MPA 预热](../../../specs/studio-mpa-prewarm/README.zh.md) - -## 背景、目标与范围 - -Studio 浏览器会话已有 UserPool refresh token,但 MPA 网页聊天目前只有 Runtime key-auth,可能要求同一用户再次登录。MPA 已提供服务端鉴权的 `POST /identity/sessions/put`;Runtime key-auth 代理已注入可信的 Studio 用户 ID。完整 ID token 若写入签名 Cookie,可能超过 4 KB。本改动让 Studio 网页聊天以新鲜的 UserPool ID/refresh token 预热 MPA,不将令牌或 Runtime 密钥交给浏览器 JavaScript。飞书登录及 MPA 现有令牌校验不在范围内。Studio/MPA 使用同一个 UserPool 客户端是运行前提,源码测试不能证明线上配置一致。 - -## 需求与设计 - -- `FR-1`:发起 MPA Runtime 网页聊天前,Studio 尝试刷新 OAuth 会话并获得新鲜 ID token。预热失败不阻止 run;MPA 随后可按原流程要求用户登录。 -- `FR-2`:Studio 对选定 MPA Runtime 做鉴权和授权,校验新 ID token 的签名、发行方、有效期、受众和用户主体,再用服务端持有的 Runtime key 将 ID/refresh token 发送至固定路径 `/identity/sessions/put`。不接受浏览器指定的上游 URL。 -- `FR-3`:轮换后的 refresh 凭据通过现有 HttpOnly 签名 Studio Cookie 返回;ID token 只在请求内使用,不写入 Cookie,以避开浏览器大小限制。旧 Cookie 继续可读。 -- `FR-4`:只有 MPA Runtime 网页聊天执行预热。飞书和非 MPA 传输不变。预热失败只作诊断;用户取消仍会停止 run。 - -新接口 `POST /web/mpa/identity-prewarm/{runtime_id}` 只接收 Runtime ID 和地域。它使用已鉴权的 Studio Cookie、现有控制面 Runtime 授权与凭据解析,并限制上游请求时长。前端在 MPA run 请求前以十秒上限和聊天取消信号调用预热。只记录不含令牌的状态;失败后仍继续正常 run,允许 MPA 手动登录。不向响应返回令牌。即便 Runtime 传递失败,已轮换的会话也必须覆盖中间件原先准备写回的 Cookie。 - -## 任务、验证与风险 - -| 需求 | 任务 | 验收 | 测试 | -| --- | --- | --- | --- | -| `FR-1`、`FR-3` | `T-1`:ID token 仅在请求内使用并保留轮换 | `AC-1`:刷新结果可用,但 Cookie 不含 ID token | `tests/auth/test_oauth2_auth.py` | -| `FR-2` | `T-2`:新增授权预热接口 | `AC-2`:可信用户主体、固定目的地、不泄露令牌;非法输入失败 | `tests/cli/test_frontend_runtime_proxy.py` | -| `FR-4` | `T-3`:MPA run 前调用预热 | `AC-3`:交接失败仍启动 MPA run;取消会阻止它;其他传输不变 | 前端测试/构建 | - -不新增依赖或 Runtime 生成配置。风险:多实例 Studio 之间的 UserPool refresh token 轮换、MPA 后续轮换同一 refresh token,以及 UserPool 客户端不一致。浏览器预热十秒上限可能先于异常缓慢的提供方刷新结束,此情况需要线上验证。在真实浏览器与云上 Runtime 验证前,不宣称端到端成功。评审结论:复用已有服务端凭据解析和 OAuth 校验;禁止客户端传入 token、URL 或 sender ID。 - -2026-09-23 验证:`tests/auth/test_oauth2_auth.py` 通过(59);与 `tests/cli/test_frontend_runtime_proxy.py` 合并运行通过(144);`npm --prefix frontend test` 通过(1305 个 Node 测试、25 个 Vitest 测试);预热专项 Vitest 测试通过(4);前端构建与 pre-commit 通过。新增/修改的 Python 可执行行覆盖 48/48,前端 client 语句覆盖 11/11。使用项目解释器运行 Pyright 仍报告 40 个变更行以外的错误。并行全量回归:5069 通过、11 跳过、2 预期失败、8 失败、2 个收集错误。其中 6 个云端测试失败可由缺少可选 `llama_index` 扩展复现,2 个自托管 Sandbox 收集错误可由缺少可选 `anthropic` 包复现;另 2 个 Skill 版本测试单独重跑未复现失败。真实浏览器/UserPool/Runtime 端到端验证:未运行。 diff --git a/prd-spec/features/studio-mpa-main-integration/2026-09-20-main-integration.md b/prd-spec/features/studio-mpa-main-integration/2026-09-20-main-integration.md deleted file mode 100644 index 5d4e29715..000000000 --- a/prd-spec/features/studio-mpa-main-integration/2026-09-20-main-integration.md +++ /dev/null @@ -1,9 +0,0 @@ -# Integrate MPA Studio changes into main - -User-approved scope: bring all PolarisCzh Studio/MPA provisioning changes into superops-team main, including trace pagination added after PR #1. Base: f497cc6. Source: c17297c. Native MPA and live Runtime deployment are excluded. - -The final source delta from ba781d3 to c17297c is applied to main; generated assets are rebuilt. Preserve main's channel management, user management, component library and channel encryption settings. Resolve overlapping imports and API additions additively; apply authenticated session identity before channel-specific identity handling. Documentation removes obsolete all-user cron and Agent-ID input descriptions. Existing component contracts accompany the imported features. - -Self-review: all feature source files and tests are included, unrelated upstream changes retained. The download test uses FileReader for the jsdom Blob implementation. Refresh missing optional esbuild lock entries so clean installation works. - -Verification: production build passed; 1214 frontend tests, 136 affected Python tests, 6 channel policy tests, 26 download tests, 6 A2A tests passed. Cron coverage: 99.71% lines and 97.18% branches. Asset check: 104 files and 248 references. Ruff/format/secrets checks passed before final staging and are rerun for delivery. No new cloud E2E or local service replacement performed for this PR. Generated vendor bundle whitespace is retained from the build output. Prior feature coverage reports remain scoped to their tests; broad client/Markdown coverage is not claimed as 95%. diff --git a/prd-spec/features/studio-mpa-main-integration/2026-09-20-main-integration.zh.md b/prd-spec/features/studio-mpa-main-integration/2026-09-20-main-integration.zh.md deleted file mode 100644 index d1353138d..000000000 --- a/prd-spec/features/studio-mpa-main-integration/2026-09-20-main-integration.zh.md +++ /dev/null @@ -1,9 +0,0 @@ -# 将 MPA Studio 改动整合到 main - -用户授权:将 PolarisCzh 的全部 Studio/MPA 部署改动提入 superops-team main,包括 PR #1 之后的 trace 分页修复。基线 f497cc6,来源 c17297c。不修改原生 MPA 或线上 Runtime。 - -将 ba781d3 到 c17297c 的最终源码差异应用到 main,并重新构建静态资源。保留 main 的渠道管理、用户管理、组件库和渠道加密配置。导入和接口新增采用合并保留方式;先设置认证会话身份,再执行渠道专用身份逻辑。文档移除过时的全量定时任务和 Agent ID 输入说明。既有功能契约一并导入。 - -已自审:包含全部功能源码及测试,保留上游其他变更。下载测试使用 FileReader 兼容 jsdom Blob。补齐缺失的 esbuild 可选依赖锁条目,使干净安装可用。 - -验证:生产构建通过;1214 项前端测试、136 项相关 Python 测试、6 项渠道策略测试、26 项下载测试、6 项 A2A 测试通过。定时任务行覆盖率 99.71%,分支覆盖率 97.18%。资源检查覆盖 104 个文件、248 个引用。Ruff、格式、密钥扫描已通过并在最终暂存后重跑。本 PR 未重新执行云端 E2E,也未替换本地服务。保留构建生成的第三方包空白字符。既有功能覆盖率报告仅适用于各自测试范围,不宣称整个 client/Markdown 覆盖率达到 95%。 diff --git a/prd-spec/features/studio-mpa-source-filter/2026-09-28-studio-mpa-source-filter.md b/prd-spec/features/studio-mpa-source-filter/2026-09-28-studio-mpa-source-filter.md deleted file mode 100644 index 1d923b2ad..000000000 --- a/prd-spec/features/studio-mpa-source-filter/2026-09-28-studio-mpa-source-filter.md +++ /dev/null @@ -1,128 +0,0 @@ -# Studio-created MPA Runtime filtering - -[中文版](2026-09-28-studio-mpa-source-filter.zh.md) - -- Change ID: `studio-mpa-source-filter` -- Created/Revised: 2026-09-28 -- Status: `approved` -- Owner repository: `veadk-python` -- Components: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), [Studio MPA control plane](../../../specs/studio-mpa-control-plane/README.md) -- Approval: the user selected progressive source filtering (approach 1) on 2026-09-28. - -## 1. Background and evidence - -Studio currently classifies MPA Runtimes only through `veadk:agent-type=mpa`. Administrators receive `runtimeScope=all`, so MPA Runtimes created by other products or operations can appear in Studio. `scope=mine` is an ownership filter based on `veadk:owner`; it does not prove Studio provenance. - -The generic Studio deployment path writes `veadk:managed=true`, `veadk:owner`, and `veadk:author`, but its MPA branch has no dedicated provisioner marker. Managed one-click creation persists `studio_owner` in its registry yet currently projects only `veadk:agent-type=mpa` into Runtime tags. Names, image references, `mi-` prefixes, and ownership alone are therefore not safe provenance signals. - -## 2. Goals and non-goals - -Goals: - -- Show only Studio-managed MPA Runtimes in Studio's MPA list. -- Filter through the server-side Tag query before hydration and pagination. -- Make both Studio MPA creation paths write consistent provenance and ownership tags. -- Preserve generic Studio-created MPA Runtimes that already have `veadk:managed=true`. -- Keep Runtime authorization independent from provenance filtering. - -Non-goals: - -- No name, image, artifact URL, Runtime ID prefix, environment-value, or registry heuristic. -- No automatic cloud mutation or unattended bulk backfill. A one-time, explicitly authorized and verified operator backfill may designate known historical MPA Runtimes as Studio-managed and must be recorded in this PRD. -- No change to general-agent listing, authorization, enterprise visibility, deletion permissions, response schemas, or UI layout. -- `veadk:managed=true` is not an authorization credential. - -## 3. Alternatives and decision - -1. **Progressive source filtering — selected.** Write existing compatibility marker `veadk:managed=true` plus `veadk:provisioner=studio-mpa`; filter current MPA lists by `agent-type=mpa + managed=true`. -2. **Strict new-tag filtering.** Require `veadk:provisioner=studio-mpa` immediately; exact for new resources but hides all legacy Studio MPA before backfill. -3. **Registry join.** Recover legacy provenance from the deployment registry; this adds data-source coupling, cost, and availability failure modes to every list request. - -Approach 1 is the smallest safe change. A future explicit migration may backfill `veadk:provisioner`; until then `veadk:managed=true` is the compatibility admission marker. - -## 4. Requirements - -### FR-1: Creation provenance - -Both Studio MPA creation paths write: - -- `veadk:agent-type=mpa` -- `veadk:managed=true` -- `veadk:provisioner=studio-mpa` -- `veadk:owner=`; Studio creation fails before cloud mutation if this identity is unavailable -- `veadk:mpa-instance-id=` when that path owns the identity - -Reconciliation replaces these owned keys, preserves unrelated non-system tags, and never copies `sys:*` tags into create/update payloads. -The managed provisioning service is also used by `veadk mpa create`, so Studio provenance is injected only when the Studio route explicitly supplies a trusted Runtime owner. The task database and native deployment registry retain their hashed owner for private task scoping; the raw trusted owner is passed only to the fixed child process and Runtime tag payload, not persisted in local task state. CLI-created MPA Runtimes keep only their MPA category tag and are not admitted by the Studio-managed filter. - -### FR-2: Server-side source filter - -`GET /web/runtimes?agentCategory=mpa` queries the Tag service with both `veadk:agent-type=mpa` and `veadk:managed=true` before hydration and pagination. `scope=mine` additionally requires `veadk:owner=`. Administrator `scope=all` returns all Studio-managed MPA Runtimes, not every account MPA Runtime. - -### FR-3: Compatibility - -An MPA Runtime carrying only `veadk:agent-type=mpa` is hidden. An existing generic Studio MPA with `veadk:managed=true` remains visible without the new provisioner tag. Existing managed one-click Runtimes without `veadk:managed=true` remain hidden until an explicit supported update/retry writes the tags. No heuristic fallback is allowed. - -### FR-4: Security and failures - -Filtering controls discovery only. Existing role, owner, enterprise visibility, Runtime authorization, and mutation checks remain authoritative. Tag-service failure remains fail-closed and must not fall back to an unfiltered scan. Browser parameters cannot disable the managed-MPA filter. - -## 5. Design and tasks - -- Add one internal MPA Runtime-tag module for the category, managed, provisioner, owner, and MPA-instance tag constants. This is not a new public Python API. -- Extend generic Studio MPA deployment with the provisioner marker; retain existing managed/owner/author/instance tags. -- Reconcile trusted owner and provenance tags into the managed one-click Runtime template only when the Studio route explicitly supplies the trusted Runtime owner. Keep task/registry ownership hashed and keep CLI provisioning outside Studio provenance. -- Make `_list_mpa_region` always add category and managed positive filters; owner filters remain additive. -- Keep `CloudRuntime`, cache keys, paging tokens, response payloads, and frontend rendering unchanged. - -| Task | Work | Files | -| --- | --- | --- | -| `T-1` | Add failing creation/list filtering tests. | `tests/cli/test_frontend_runtime_proxy.py`, `tests/cli/test_studio_rbac.py`, `tests/integrations/mpa_managed/` | -| `T-2` | Add internal shared tag constants and generic Studio MPA provisioner tag. | `veadk/integrations/mpa/tags.py`, `veadk/cli/cli_frontend.py` | -| `T-3` | Add Studio-only one-click managed/provisioner/owner/instance tags while preserving hashed task/registry ownership and excluding CLI provisioning. | `frontend/server/mpa_creation.py`, `veadk/integrations/mpa/managed/tasks.py`, `runner.py`, `service.py`, `runtime.py` | -| `T-4` | Enforce managed MPA Tag-service filtering. | `veadk/cli/cli_frontend.py` | -| `T-5` | Reconcile docs and verification; rebuild WebUI only if frontend source changes. | PRD/spec pair and generated assets if applicable | - -## 6. Acceptance and verification - -| Requirement | Acceptance criterion | Verification | Result | -| --- | --- | --- | --- | -| `FR-1` | `AC-1`: both Studio creation paths emit exact managed/provisioner/owner tags and the MPA instance tag; missing trusted owner fails before cloud mutation; CLI provisioning does not emit Studio provenance. | Exact create/update tag payload, missing-owner, owner-propagation, and CLI-negative tests. | `pass` — targeted creation/service/task tests, 2026-09-29 | -| `FR-2` | `AC-2`: MPA Tag lookup always includes category and managed; mine also includes owner. | Admin/all and owner/mine route tests. | `pass` — targeted Runtime-list tests, 2026-09-29 | -| `FR-3` | `AC-3`: agent-type-only external MPA is absent; managed legacy Studio MPA remains visible. | Positive/negative pagination fixtures. | `pass` — targeted Runtime-list tests, 2026-09-29 | -| `FR-4` | `AC-4`: failure is fail-closed and general Runtime behavior is unchanged. | Tag failure/general-category regressions. | `pass` — targeted and repository regression tests, 2026-09-29 | - -```bash -uv run --extra dev pytest -q tests/cli/test_frontend_runtime_proxy.py tests/cli/test_studio_rbac.py tests/integrations/mpa_managed -uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" -npm --prefix frontend test -npm --prefix frontend run build -uv run --extra dev pre-commit run --all-files -``` - -Browser verification is required: use the local Studio against read-only Runtime discovery to prove an untagged external MPA is absent, a managed MPA is present, loading/error/empty states remain distinct, and general-agent discovery is unchanged. No screenshot or raw provider response is committed. A later explicitly authorized one-time backfill is recorded below and does not enable automatic migration behavior in the product. - -Verification record for the uncommitted diff from base `69dd24cd15b2372b76d1666246fc40fbac27235e` on 2026-09-29: - -- `pass`: targeted MPA backend tests after the final code review — 506 passed, 6 warnings. -- `pass`: repository Python regression after the final code review — 6142 passed, 18 skipped, 2 xfailed, 84 warnings; smoke markers were excluded by the repository command above. -- `pass`: frontend tests — 1367 Node tests and 39 Vitest tests passed. -- `pass`: frontend production build; existing Vite chunk-size/dynamic-import warnings only, with no generated-asset diff. -- `pass`: Ruff and format checks for all changed Python/test files; `git diff --check` passed. -- `pass`: Pyright for the six changed/new focused backend modules outside `cli_frontend.py` — 0 errors. -- `fail` (pre-existing baseline): direct Pyright of changed `veadk/cli/cli_frontend.py` reports 36 existing errors outside this change's added lines; this change does not broaden into unrelated type cleanup. -- `pass`: repository pre-commit — Ruff, format, hardcoded-secret detection, and concrete-YAML-secret detection all passed. -- `pass`: two-round implementation/spec review corrected CLI provenance admission and raw-owner/hashed-owner separation. The first code review fixed three medium findings: legacy pending create replay now retains its exact original payload before upgrading tags, non-NotFound hydration failures remain error states instead of empty success, and task ownership must match the hash of the raw Studio Runtime owner. The post-backfill review fixed one medium cross-region hydration amplification issue by filtering authoritative Runtime TRNs before `GetRuntime`, plus one low verification-record ambiguity. No blocking finding remains. -- `pass`: restarted local Studio from this worktree; `/`, `/web/ui-config`, filtered MPA discovery, and general Runtime discovery returned successfully. Before the authorized backfill, the filtered MPA response contained one item and the general first page contained five items; no raw provider response was persisted. -- `not_run`: real-browser read-only discovery verification, pending the user's local Studio validation after restart. -- `pass`: explicitly authorized one-time tag backfill — 18 live Beijing Runtimes carrying `veadk:agent-type=mpa` and missing the managed marker were designated with only `veadk:managed=true`; all 18 writes succeeded, the post-write managed set contained 19 unique live Runtimes, and comparison found no other tag changes. Cross-region duplicate tag mappings that did not resolve through `GetRuntime` were not mutated. -- `not_applicable`: Runtime deployment, image rollout, environment mutation, and automated migration. - -## 7. Risks and delivery record - -- Any future MPA Runtime without `veadk:managed=true` remains hidden until explicit update/retry or another authorized controlled backfill. This is intentional fail-closed behavior. -- Privileged external operators can alter tags; tags are discovery metadata, never authorization. -- Server-side filtering preserves pagination correctness; browser-only filtering is prohibited. -- 2026-09-28: approach 1 approved. Implementation, commit, push, PR, deployment, and cloud backfill remain separately authorized. -- 2026-09-29: implementation and automated/local API verification completed in the independent worktree. No commit, push, PR, deployment, or backfill was performed. -- 2026-09-29: the user explicitly authorized historical MPA tag backfill. Eighteen verified live Beijing MPA Runtimes received only `veadk:managed=true`; the Studio MPA list then returned 19 unique items. No Runtime deployment, image/environment change, commit, push, or PR was performed. diff --git a/prd-spec/features/studio-mpa-source-filter/2026-09-28-studio-mpa-source-filter.zh.md b/prd-spec/features/studio-mpa-source-filter/2026-09-28-studio-mpa-source-filter.zh.md deleted file mode 100644 index 0c096b150..000000000 --- a/prd-spec/features/studio-mpa-source-filter/2026-09-28-studio-mpa-source-filter.zh.md +++ /dev/null @@ -1,128 +0,0 @@ -# Studio 创建的 MPA Runtime 过滤 - -[English version](2026-09-28-studio-mpa-source-filter.md) - -- Change ID:`studio-mpa-source-filter` -- 创建/修订日期:2026-09-28 -- 状态:`approved` -- 所属仓库:`veadk-python` -- 影响组件:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md)、[Studio MPA 控制面](../../../specs/studio-mpa-control-plane/README.zh.md) -- 批准记录:用户于 2026-09-28 选择渐进来源过滤(方案 1)。 - -## 1. 背景与证据 - -Studio 当前仅通过 `veadk:agent-type=mpa` 分类 MPA Runtime。管理员获得 `runtimeScope=all`,因此其他产品或运维流程创建的 MPA Runtime 也会出现。`scope=mine` 是基于 `veadk:owner` 的归属过滤,不能证明 Studio 来源。 - -通用 Studio 部署已写入 `veadk:managed=true`、`veadk:owner` 和 `veadk:author`,但 MPA 分支没有专用 provisioner 标记。托管式一键创建在注册表持久化 `studio_owner`,但目前只把 `veadk:agent-type=mpa` 投影到 Runtime 标签。因此名称、镜像、`mi-` 前缀和仅有 owner 都不是安全来源信号。 - -## 2. 目标与非目标 - -目标: - -- Studio MPA 列表只展示 Studio 管理的 MPA Runtime。 -- 在 Runtime 补齐和分页前使用服务端 Tag 查询过滤。 -- 两条 Studio MPA 创建路径写入一致的来源和归属标签。 -- 保留已经带 `veadk:managed=true` 的通用 Studio 历史 MPA。 -- Runtime 授权与来源过滤保持独立。 - -非目标: - -- 不使用名称、镜像、artifact URL、Runtime ID 前缀、环境值或注册表启发式判断。 -- 不自动修改云资源或执行无人值守的批量回填。可由用户显式授权一次性、可验证的运维回填,把已知历史 MPA Runtime 指定为 Studio-managed,并必须在本 PRD 记录。 -- 不改变通用 Agent 列表、授权、企业可见性、删除权限、响应 schema 或 UI 布局。 -- 不把 `veadk:managed=true` 当作授权凭据。 - -## 3. 备选方案与决策 - -1. **渐进来源过滤——已选择。** 写入既有兼容标记 `veadk:managed=true` 和新标记 `veadk:provisioner=studio-mpa`;当前列表按 `agent-type=mpa + managed=true` 过滤。 -2. **严格新标签过滤。** 立即要求 `veadk:provisioner=studio-mpa`;新资源准确,但全部历史 Studio MPA 在回填前隐藏。 -3. **关联注册表。** 从部署注册表恢复历史来源;这会为每次列表增加数据源耦合、成本和可用性故障面。 - -方案 1 是最小安全变化。未来可显式回填 `veadk:provisioner`;此前 `veadk:managed=true` 作为兼容准入标记。 - -## 4. 功能需求 - -### FR-1:创建来源 - -两条 Studio MPA 创建路径写入: - -- `veadk:agent-type=mpa` -- `veadk:managed=true` -- `veadk:provisioner=studio-mpa` -- 写入 `veadk:owner=<可信 Studio owner id>`;若该身份不可用,Studio 创建必须在云变更前失败 -- 路径拥有 MPA 身份时写 `veadk:mpa-instance-id=` - -标签对账替换这些受管键、保留无关非系统标签,且不把 `sys:*` 标签复制到 create/update payload。 -托管 provisioning 服务也被 `veadk mpa create` CLI 复用,因此仅当 Studio 路由显式提供可信 Runtime owner 时才注入 Studio 来源。任务数据库和原生部署注册表继续保存 owner 哈希以隔离私有任务;原始可信 owner 只传给固定子进程和 Runtime 标签 payload,不持久化到本地任务状态。CLI 创建的 MPA Runtime 仅保留 MPA 分类标签,不进入 Studio-managed 过滤结果。 - -### FR-2:服务端来源过滤 - -`GET /web/runtimes?agentCategory=mpa` 必须在 Runtime 补齐和分页前,同时查询 `veadk:agent-type=mpa` 和 `veadk:managed=true`。`scope=mine` 再增加 `veadk:owner=<当前 principal owner id>`。管理员 `scope=all` 返回全部 Studio 管理的 MPA,而非账号下全部 MPA。 - -### FR-3:兼容 - -仅带 `veadk:agent-type=mpa` 的 Runtime 隐藏。已有通用 Studio MPA 只要带 `veadk:managed=true`,即使没有新 provisioner 标签仍可见。缺少 `veadk:managed=true` 的历史一键 Runtime 在显式更新/重试写入标签前保持隐藏。禁止启发式 fallback。 - -### FR-4:安全与失败 - -来源过滤只控制发现范围。既有角色、owner、企业可见性、Runtime 授权和写操作检查继续作为权威。Tag 服务失败保持 fail-closed,不能回退到未过滤扫描。浏览器参数不能关闭 managed-MPA 强制过滤。 - -## 5. 设计与任务 - -- 新增一个内部 MPA Runtime 标签模块,统一分类、managed、provisioner、owner 和 MPA instance 标签常量;不新增公共 Python API。 -- 通用 Studio MPA 部署增加 provisioner;现有 managed/owner/author/instance 标签不变。 -- 仅当 Studio 路由显式提供可信 Runtime owner 时,才在托管式一键部署前把 owner 和来源标签对账到 Runtime template;任务/注册表 owner 继续使用哈希,CLI provisioning 不获得 Studio 来源。 -- `_list_mpa_region` 始终添加 category 与 managed 正向过滤,owner 过滤继续叠加。 -- `CloudRuntime`、缓存、分页 token、响应 payload 和前端渲染不变。 - -| 任务 | 工作 | 文件 | -| --- | --- | --- | -| `T-1` | 增加创建/列表来源过滤失败测试。 | `tests/cli/test_frontend_runtime_proxy.py`、`tests/cli/test_studio_rbac.py`、`tests/integrations/mpa_managed/` | -| `T-2` | 增加内部共享标签常量,并为通用 Studio MPA 增加 provisioner。 | `veadk/integrations/mpa/tags.py`、`veadk/cli/cli_frontend.py` | -| `T-3` | Studio 一键 MPA 增加 managed/provisioner/owner/instance,同时保留任务/注册表 owner 哈希,并排除 CLI provisioning。 | `frontend/server/mpa_creation.py`、`veadk/integrations/mpa/managed/tasks.py`、`runner.py`、`service.py`、`runtime.py` | -| `T-4` | 强制 Tag 服务过滤 Studio-managed MPA。 | `veadk/cli/cli_frontend.py` | -| `T-5` | 对齐文档/验证;仅当前端源码变化时重建 WebUI。 | PRD/spec 双语文件及必要生成资产 | - -## 6. 验收与验证 - -| 需求 | 验收标准 | 验证 | 结果 | -| --- | --- | --- | --- | -| `FR-1` | `AC-1`:两条 Studio 创建路径写入精确 managed/provisioner/owner 与 MPA instance 标签;可信 owner 缺失时在云变更前失败;CLI provisioning 不写 Studio 来源。 | 检查 create/update 精确 tag payload、owner 缺失、owner 透传与 CLI 负向用例。 | `pass` — 创建/service/task 定向测试,2026-09-29 | -| `FR-2` | `AC-2`:MPA Tag 查询始终包含 category/managed;mine 还包含 owner。 | admin/all 和 owner/mine 路由测试。 | `pass` — Runtime 列表定向测试,2026-09-29 | -| `FR-3` | `AC-3`:仅 agent-type 的外部 MPA 不出现;managed 历史 Studio MPA 仍可见。 | 正向/负向分页 fixture。 | `pass` — Runtime 列表定向测试,2026-09-29 | -| `FR-4` | `AC-4`:失败保持 fail-closed,通用 Runtime 行为不变。 | Tag 失败/general category 回归。 | `pass` — 定向及仓库回归测试,2026-09-29 | - -```bash -uv run --extra dev pytest -q tests/cli/test_frontend_runtime_proxy.py tests/cli/test_studio_rbac.py tests/integrations/mpa_managed -uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke" -npm --prefix frontend test -npm --prefix frontend run build -uv run --extra dev pre-commit run --all-files -``` - -必须执行浏览器验证:使用本地 Studio 的只读 Runtime 发现,证明未打标的外部 MPA 不出现、managed MPA 正常出现、loading/error/empty 状态仍可区分,并且通用 Agent 发现不变。截图和原始 provider 响应不提交。后续经用户显式授权的一次性回填记录如下;该操作不会在产品中启用自动迁移行为。 - -2026-09-29 针对基线 `69dd24cd15b2372b76d1666246fc40fbac27235e` 未提交差异的验证记录: - -- `pass`:最终 code review 后的 MPA 后端定向测试 — 506 passed、6 warnings。 -- `pass`:最终 code review 后的仓库 Python 回归 — 6142 passed、18 skipped、2 xfailed、84 warnings;按上方仓库命令排除 smoke markers。 -- `pass`:前端测试 — 1367 项 Node 测试和 39 项 Vitest 测试通过。 -- `pass`:前端生产构建;只有既有 Vite chunk-size/dynamic-import 警告,且没有生成资产差异。 -- `pass`:全部变更 Python/测试文件通过 Ruff 和格式检查;`git diff --check` 通过。 -- `pass`:除 `cli_frontend.py` 外 6 个变更/新增聚焦后端模块通过 Pyright,0 errors。 -- `fail`(既有基线):对变更文件 `veadk/cli/cli_frontend.py` 直接运行 Pyright 报告 36 个既有错误,均不在本次新增行;本变更不扩展到无关类型清理。 -- `pass`:仓库 pre-commit — Ruff、格式、硬编码密钥检测和具体 YAML 密钥检测全部通过。 -- `pass`:两轮实现/spec 审查修正了 CLI 来源准入及原始 owner/哈希 owner 分离。第一次 code review 修复 3 个 medium finding:旧 pending create 先按原始 payload 精确重放再升级标签;非 NotFound 的 Runtime 补齐失败保持错误态而非空成功;任务 owner 必须匹配原始 Studio Runtime owner 的哈希。回填后的复审又修复 1 个 medium 跨地域补齐放大问题(在 `GetRuntime` 前按权威 Runtime TRN 过滤)和 1 个 low 验证记录歧义。无剩余阻断项。 -- `pass`:已从本 worktree 重启本地 Studio;`/`、`/web/ui-config`、过滤后的 MPA 发现和通用 Runtime 发现均成功返回。在授权回填前,过滤后 MPA 响应包含 1 项,通用 Runtime 首页面包含 5 项;未持久化原始 provider 响应。 -- `not_run`:真实浏览器只读发现验证,等待重启 Studio 后由用户在本机验收。 -- `pass`:经显式授权的一次性标签回填 — 对北京地域 18 个真实存在、带 `veadk:agent-type=mpa` 且缺少 managed 标记的 Runtime 仅补写 `veadk:managed=true`;18 个写入全部成功,写后 managed 集合包含 19 个唯一真实 Runtime,逐项比较未发现其他标签变化。无法通过 `GetRuntime` 解析的跨地域重复标签映射未执行写入。 -- `not_applicable`:Runtime 部署、镜像发布、环境变量修改和自动迁移。 - -## 7. 风险与交付记录 - -- 后续任何缺少 `veadk:managed=true` 的 MPA Runtime 仍会隐藏,直到显式更新/重试或再次获得授权的受控回填。这是有意的 fail-closed 行为。 -- 特权外部运维可修改标签;标签是发现 metadata,不是授权。 -- 服务端过滤保持分页正确,禁止浏览器后过滤。 -- 2026-09-28:方案 1 已批准。实现、commit、push、PR、部署和云回填仍需分别授权。 -- 2026-09-29:已在独立 worktree 完成实现、自动化验证和本地 API 验证;未执行 commit、push、PR、部署或回填。 -- 2026-09-29:用户显式授权历史 MPA 标签回填。已对北京地域 18 个核验存在的 MPA Runtime 仅补写 `veadk:managed=true`;随后 Studio MPA 列表返回 19 个唯一实例。未执行 Runtime 部署、镜像/环境修改、commit、push 或 PR。 diff --git a/prd-spec/features/studio-mpa-tos-iam/2026-10-12-bucket-only.md b/prd-spec/features/studio-mpa-tos-iam/2026-10-12-bucket-only.md deleted file mode 100644 index 26b2c7b9a..000000000 --- a/prd-spec/features/studio-mpa-tos-iam/2026-10-12-bucket-only.md +++ /dev/null @@ -1,313 +0,0 @@ -# Studio MPA TOS IAM mount - -[中文版](2026-10-12-bucket-only.zh.md) - -- Change ID: `studio-mpa-tos-iam` -- Date: 2026-10-12 -- Status: implemented; upstream targeted checks verified; historical V42 live evidence retained; upstream port not deployed; baseline-wide gates blocked -- Base: official `origin/main`, `812faadf`; initial implementation/verification base: `superops/main`, `035387d1` -- Contract: [Studio MPA creation](../../../specs/studio-mpa-creation/README.md), session output storage. - -## Lifecycle integration plan (2026-10-14) - -The user approved an independent branch targeting -`superops-team/veadk-python:feat/mpa-lifecycle-upstream-20261013` at `2ffe6e14`. -Replace fork PR #25 only after the replacement exists; preserve the shared -source branch and official PR #1163 unchanged. Resolve the request/API/dialog -conflicts by retaining automatic OpenViking, per-agent PostgreSQL, dedicated -`aidb` name preflight/reservations and maximum-instance selection, while using -bucket-only IAM Role TOS configuration. Preserve both README release notes and -regenerate packaged WebUI assets with the normal build. No new interface -semantics, cloud deployment or existing-resource migration are intended. - -Manual design review confirms the combination preserves each branch's approved -behavior, name reservations, request identity and transient TOS configuration. -No component semantics change beyond the existing Role design. Remove the -ported TOS section's conflicting CON-17 label; retain the target's BytePlus -CON-17 and all other lifecycle identifiers. - -Verification on 2026-10-14 (base `2ffe6e14`, integration working diff): - -- Added four combined lifecycle/TOS cases (two Python, two Vitest). Python - request-module tests pass 18/18; dialog tests pass 67/67. The manual-mode test's - initial incorrect expectation was corrected to the existing omitted-field - contract; automatic mode retains `openvikingMode=auto`. No independent defect - was confirmed. -- Managed MPA/Tool/resource-list pytest passes 1,038/1,038. Coverage run passes - the same cases. Executable diff coverage against the target is 100%: Python - 102/102 and TypeScript 10/10, using utree with coverage.py XML / Clover XML - and the user's 95% threshold (not an invented CI rule). -- Full frontend passes 1,389 Node tests and 105 Vitest cases; build, - packaged-asset checks, i18n and all-file pre-commit pass. Python is 3.12.12. -- Full Python regression: 7,150 passed, 16 failed, 2 collection errors, - 59 skipped, 4 xfailed and 6 subtests passed (469.04 s, two workers). - The target's clean archive reproduces all 16 failures / 2 errors - (99 other cases pass): missing optional llama_index, openai_codex and anthropic - dependencies. Changed-source Pyright reports the same 15 SDK constructor - errors reproduced on target `mpa_tool.py`. These gates remain blocked/failing; - no smoke execution or Python 3.10 result is claimed. -- Isolated local browser verifies combined automatic-OV/dedicated-PG/bucket - submission without TOS keys, normal/empty/loading/error/retry states, - keyboard bucket selection, task cancellation/retry, step navigation and a - 390px viewport. The wording-only change is visibly verified. APIs are mocked; - this does not prove real cloud creation. Temporary harness files were removed. - -This integration is implemented and targeted checks verified, not deployed. -Historical V42 cloud evidence below does not validate this new integration. - -The user requested a wording-only amendment on 2026-10-14: the Studio hint now -states that without a selected bucket files remain in the sandbox's local -directory. Translate the hint consistently; the built-in no-mount default and -CLI/profile compatibility remain unchanged. - -## Background and scope - -Studio currently requires `tosAccessKey`, `tosSecretKey`, and `tosBucket` together. -`mpa_tool.build_tos_mount_config` always emits access-key authentication. The -AgentKit SDK supports `TOS_CREDENTIAL_TYPE_IAM_ROLE` without Credentials. The -existing managed IAM reconciler preserves extra bindings and rejects conflicting -policy documents. Its platform TOS policy does not grant business-bucket writes. - -The user approved bucket-only Studio creation and automatic, additive bucket -authorization on 2026-10-12. This changes TOS mount authentication, not Studio -login, Runtime KeyAuth/JWT, or the Worker protocol. The initial implementation -scope excluded publication. The subsequent user request on 2026-10-12 authorizes -commit/push, updating the existing cloud Studio and creating a test MPA. Bucket -creation/public-policy changes and existing-Session migration remain out of scope. - -## Requirements and scenarios - -### Approved prefix amendment - -On 2026-10-12 the user narrowed Studio selection to bucket names beginning with -`agentkit-platform-`. The selector requests an optional `namePrefix` on the -existing deployment-resource API; apply exact, case-sensitive prefix filtering -before search, counts and pagination. Unfiltered/general deployment queries keep -their behavior. Studio creation rejects other buckets, including stale drafts, -before cloud calls; standalone CLI/Worker bucket validation remains unchanged. -No role policies, public-bucket policies or Session paths are changed by this -amendment. Test allowed/excluded/lookalike names, empty selection, stale drafts, -query transport and pagination. Manual design review confirms these boundaries. -The earlier version was deployed as V41 with unchanged environment and matching -frontend asset hash. No MPA was submitted or bucket policy attached before the -user requested this amendment; cloud creation/E2E is on hold. - -Prefix amendment verification: added 8 Python cases and 3 Vitest cases. The -related Python suite passes 754/754; dialog tests pass 46/46; full frontend tests -pass 1,377 Node tests plus 73 Vitest cases. Build, packaged assets (113 files/350 -references), i18n, Ruff, changed-file Pyright and pre-commit pass. Executable diff -coverage for the four scoped files against HEAD is 100% (Python 22/22, frontend -10/10), parsed with utree using coverage.py XML and Istanbul Clover XML. The -initial tests failed on missing prefix behavior; related Studio fixtures were -updated without relaxing authorization ordering/failure assertions. Full Python -regression ran again: 6,495 passed, 6 failed, 2 collection errors, 19 skipped, -2 xfailed and 6 subtests passed. The failing Harness cases and missing optional -dependencies are the previously reproduced baseline blockers. The local browser -shows only matching buckets using simulated responses; temporary harness files -and Vite were removed. No existing defect was reported: this is an approved scope -change. This is the pre-publication evidence snapshot; the subsequent authorized -publication and live Role mount verification are pending and will be recorded -separately from these local results. - -- FR-1: Show one optional bucket selector using the existing resource-list hook - and DeploymentSelect; retain loading/error/empty/search/pagination/retry, - disabled state, keyboard behavior, and cancellation. No TOS AK/SK input. -- FR-2: A nonempty Studio bucket chooses IAM Role using the actual Worker Tool - role. Reject malformed names and legacy nonempty credential submissions safely. - Remove credentials from recovered browser drafts. Empty selection retains the - server profile (the built-in profile has no mount). CLI/YAML access-key mode remains compatible; - explicit `tos-credential-type: iam_role` enables bucket-only managed profiles. -- FR-3: Before workload-identity/database/network mutations, verify the selected bucket belongs - to the deployment account and region, then ensure one stable bucket-specific - custom policy and its Global binding on the Worker role. Verify account/trust, - policy content, and readback. Preserve unrelated policies/trust. Reject - conflicts; recover missing-policy/create/attach races with a bounded timeout. -- FR-4: Grant `tos:HeadBucket` and `tos:ListBucket` only on the selected bucket; - grant `tos:GetObject`, `tos:PutObject`, `tos:AbortMultipartUpload`, and - `tos:DeleteObject` only on its `sandbox-session/*` objects. No other buckets, - CreateBucket, bucket-policy/ACL changes, KMS grants, or wildcard object scope. -- FR-5: Emit IAM Role Tool mount without Credentials. Keep `/data/output`, the - canonical per-session TOS path, and nonsecret Runtime mount flag/bucket. - Reuse validation must reject authentication-mode/role mismatches, not just - matching mount paths. Existing Tools and Sessions are never silently mutated. -- FR-6: Permission, ownership, configuration, and policy conflicts stop creation - at the IAM stage with safe localized errors. Cancellation keeps durable IAM - objects for retry. Cloud operations use fresh verified deployment credentials. - -## Design and compatibility - -Add an optional managed Worker credential mode while retaining the historical -access-key default for CLI. Studio's request contains only `tosBucket` and its -fixed runner applies IAM Role. Existing task identity/locking/cancellation remain -unchanged. Reuse existing IAM SDK boundary, canonical document comparison, and -error allowlist for an independently named bucket policy; do not edit -`VeADKMPARuntimeAccessV1`. Stable policy names avoid read/merge/write races -between different buckets. Identical concurrent operations converge. - -The policy exposes bucket metadata through ListBucket but object access is -restricted to session outputs. The shared role grants all of its consumers that -same prefix access: per-session directories are not tenant authorization. -SSE-KMS buckets require separately approved key permissions. Old Studio clients -sending credential fields must refresh; they receive a safe validation error. -Legacy CLI access-key requests and general-agent deployment remain unchanged. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | Evidence | -| --- | --- | --- | --- | -| FR-1, FR-2 | T-1: UI/API/config | AC-1: bucket-only payload, stale-secret stripping, selection states | config/routes tests and 43 dialog cases pass; isolated browser pass | -| FR-3, FR-4, FR-6 | T-2: IAM ensure and service wiring | AC-2: exact scoped policy, repeated/concurrent retry, failure/cancel boundaries | IAM/service tests pass | -| FR-5 | T-3: Tool payload/reuse checks | AC-3: no Credentials and strict mode/role validation; legacy mode unchanged | Tool/worker tests pass | -| All | T-4: docs/assets/gates | AC-4: targeted/full tests, build/assets, browser states, executable diff coverage >95% | targeted/build/assets/browser/coverage pass; full regression and Pyright have reproduced baseline blockers | - -Run affected pytest modules first; then -`uv run --extra dev pytest -n 2 -m 'not codex_smoke and not piagent_smoke'`, -`npm --prefix frontend test`, build, asset verification, changed-file -Ruff/Pyright, i18n, secret scan, and diff whitespace. Coverage is executable -changed lines, not whole-module coverage. Browser checks use isolated mocked -provider responses; real IAM/mount E2E is not_run without separate authorization. - -## Review and delivery - -Manual design review substitutes for the unavailable review-spec skill. It -confirmed role ownership, additive policy scope, legacy CLI compatibility, -retry/cancel behavior, localization, and bilingual equivalence. The unavailable -ui-ux-pro-max skill is replaced by the repository SPEC/Foundation and existing -selector behavior. Test-skill production-edit restrictions do not apply to the -user's explicitly authorized feature implementation; tests retain strict -expected-behavior assertions. - -## Verification and change record — 2026-10-12 - -Changed the Studio request/dialog, managed Worker configuration, IAM reconciliation, -service ordering and Tool metadata validation; updated both component specs and -operator guides. No MPA Agent/Worker source changes are needed: existing Session -mount paths remain unchanged. Reused resource selectors and their existing stylesheet -instead of creating a new UI system. Temporary browser harness/source files were -deleted and its local Vite process stopped; production assets were rebuilt. - -- **pass:** Python 3.12.12, `tests/integrations/mpa_managed` plus - `tests/integrations/test_mpa_tool.py`: 719/719, including 42 net-new cases versus - the 677-case baseline. Initial contract tests failed before implementation. - With coverage enabled, Pydantic lazy import fails during collection in the - plugin environment; explicitly importing `pydantic.RootModel` before - `pytest.main([...])` enables the same 719 tests with coverage, without product - or dependency changes. The baseline's service collection reproduces this - coverage import error too. Python 3.13 was not used as final verification. -- **pass:** executable changed-line coverage 100%: Python 97/97 from - `utree coverage parse` using diff hunks and coverage XML; frontend 9/9 using - Istanbul statement-start locations intersected with changed lines. Unchanged - source, type-only lines, tests, docs and generated bundles are excluded. This - is incremental line coverage, not whole-file or branch coverage. -- **pass:** `npm --prefix frontend test`: 1,377 Node tests and 70 Vitest cases; - `npm --prefix frontend run build`, `check:i18n`, `test:webui-assets` (113 files, - 350 internal references), Ruff, pre-commit all files and `git diff --check`. - Vite reports its existing large-chunk warning. -- **pass:** real local browser with simulated API responses: normal selection, - no TOS key inputs, loading, empty, error/retry, search, pagination, keyboard - selection, Chinese search input, close and 390px viewport. This is not live - cloud IAM/mount verification or a real OS IME composition test. -- **fail, baseline reproduced:** full parallel regression: 6,485 passed, - 6 failed, 2 collection errors, 19 skipped, 2 xfailed, 6 subtests passed. The - exact six Harness cases and two self-host Sandbox collection errors recur in - an isolated archive of `035387d1` using the same interpreter (60 passed); - optional `llama_index`/`anthropic` dependencies are absent. These unrelated - dependencies/implementations were not changed or installed to mask the gate. -- **fail, baseline reproduced:** Pyright with `.venv/bin/python`: 15 SDK - call-signature errors in unchanged legacy `mpa_tool.py` calls. A temporary - copy of the exact baseline file reports the same 15 errors and was deleted. - The other changed production Python files have no Pyright diagnostics. -- **not_run:** cloud IAM writes, live Role-mounted Session E2E, deployment, - image publication, commit and push. Existing Tools/Sessions remain untouched. - Shared-role access, existing denies, public bucket policy and KMS restrictions - remain operator-visible limitations; a granted policy is not proof of cloud - filesystem access. - -Implementation review covered fixed bucket-derived names, account/region/trust -verification, exact resource prefixes, additive idempotency/races, safe failures, -timeouts/cancellation and legacy CLI compatibility. No existing-policy rewrite or -global object permission was introduced. Full-suite/typing gates cannot be called -passing; live acceptance remains separate from this local implementation. - -### Authorized publication and live acceptance - -On 2026-10-12, rebased against unchanged `superops/main` and pushed implementation -commit `267705ef` to `polaris/czh/studio-mpa-tos-iam-role`. After synchronization, -754 related Python cases and 1,377 Node plus 73 Vitest cases passed again; -pre-commit, i18n and packaged assets also passed. The baseline-wide blockers -recorded above remain unchanged. - -Updated only the existing Studio code bundle to V42, preserving its environment -hash and branding. The packaged wheel's seven production source files and all -WebUI assets matched the checkout; remote entry JS/CSS/logo hashes matched the -local package. Wheel SHA256: -`023bf085371cdc7c285b5e60bfcb2dd08e390c1935b7a995475c82c47b925a14`. -No new Runtime/Worker image was built for this Studio-only publication. - -Remote Studio showed four platform-prefix buckets instead of eighteen unfiltered -buckets and rejected the old `agentkit-demo` draft. Created the test MPA -`role-tos-e2e-20261012-a` using `agentkit-platform-2112682748`, with no TOS AK/SK. -Runtime `r-yex3bbopvkwqejues1lc` became Ready and `/readiness` returned 200. -Worker `t-yex3bb51xcwqejuen9oc` was Ready with Role -`IDRoleForArkClawShareAgent`, `TOS_CREDENTIAL_TYPE_IAM_ROLE`, no Credentials, -read/write `/data/output`, and the expected base mount path. IAM readback confirmed -the exact bucket/session-prefix custom policy was attached. The role already had -the platform-bucket system policy; additive reconciliation does not narrow those -pre-existing permissions. The selected bucket had no public bucket policy. - -Two conversations submitted through remote Studio executed actual Sandbox -commands. GetSession readback confirmed separate -`/sandbox-session/tool-{tool_id}/session-{user_session_id}/` mounts. Both wrote -`role-tos-e2e.txt` with different A/B markers; the second initially found no file. -Independent owner-side TOS reads (no owner-side writes) verified both objects and -that A remained unchanged after B. A's Sandbox result, TOS object and browser -download shared SHA256 -`e762fa20c53dd7394b3f89af8e084e42d486a8e241fa37d68acbd695a17b82a7`; -B's Sandbox result and TOS object shared SHA256 -`0d797e5173d581880100a21f25a7e00f4990d82b440c4f01f01d78e8776b1e6e`. -The browser download event waiter expired after 45 seconds, but the download -subsequently completed and its local bytes were verified; this was not an -endpoint failure. The image does not expose `/ready?scope=execution` (404), so -execution proof comes from commands and object reads, not that readiness route. - -The test MPA and its two output objects are retained for inspection. Existing -Tools/Sessions were not mutated. No repository exploration files remain. This -acceptance does not test role permission subtraction, KMS-encrypted buckets, -explicit Deny policies, independent principals or real business-channel delivery. - -### Official upstream PR preparation - -The user subsequently requested a PR to `volcengine/veadk-python:main`. The fork -branch's merge-base would include unrelated history, so the two feature/evidence -commits were transferred onto official main `812faadf` in a separate branch, -`czh/studio-mpa-tos-iam-role-upstream`. The sole production-source conflict was an -import: preserve official `resolve_model_key` together with the TOS IAM ensure. -Generated WebUI conflicts were resolved by rebuilding from the upstream source, -not by choosing either old asset set. No unrelated fork commits were imported. - -The two TOS ordering cases initially failed because their fake cloud did not -provide official main's new Ark key discovery dependency. Only these cases were -updated to mock that dependency, assert it was awaited with the correct profile -and cloud, and retain all grant-before-identity/error propagation assertions. -Both cases then passed. No production logic was changed to accommodate the tests. - -Upstream verification with Python 3.12.12: 872 related Python tests passed; -1,377 Node tests and 75 Vitest cases passed; frontend build, i18n, assets, -Ruff 0.11.12 (repository-pinned version) and pre-commit passed. Incremental -executable-line coverage against official main was 100% (Python 102/102, TS -10/10), parsed with coverage.py XML and Istanbul Clover XML. The focused test -repair followed bits-unit-test-gen Steps 1-7; TOS orchestration coverage was 5/5 -changed executable lines and utree flush completed. - -Full upstream regression completed: 6,950 passed, 16 failed, 2 collection errors, -59 skipped, 4 xfailed, 6 subtests passed. Six Harness failures and two self-host -collection errors were reproduced on an isolated official-main archive with the -same interpreter (64 passed): missing optional `llama_index`/`anthropic`. -The ten Codex runtime failures were also reproduced on that archive: missing -optional `openai_codex`. The 15 legacy SDK call-signature Pyright errors were -reproduced from official main's original `mpa_tool.py`. These gates are not green. - -The V42 cloud evidence above belongs to the original fork implementation. The -official-base port was not redeployed or browser/cloud E2E-tested in this PR -preparation task; its official model-key behavior is retained, not inferred to -have run in V42. The original fork PR and cloud resources remain unchanged. diff --git a/prd-spec/features/studio-mpa-tos-iam/2026-10-12-bucket-only.zh.md b/prd-spec/features/studio-mpa-tos-iam/2026-10-12-bucket-only.zh.md deleted file mode 100644 index 297ae58dd..000000000 --- a/prd-spec/features/studio-mpa-tos-iam/2026-10-12-bucket-only.zh.md +++ /dev/null @@ -1,246 +0,0 @@ -# Studio MPA TOS IAM 挂载 - -## Lifecycle 合入方案(2026-10-14) - -用户确认从 `superops-team/veadk-python:feat/mpa-lifecycle-upstream-20261013` -(`2ffe6e14`)新建独立分支,创建替代 PR 后关闭 #25,保留共享源分支和官方 -PR #1163 不变。解决请求模型、API 和表单冲突时,同时保留自动 OpenViking、 -独立 PostgreSQL Workspace、`aidb` 名称预检/占用和最大实例数配置,TOS 则使用 -仅选桶的 IAM Role 配置。README 保留双方说明,WebUI 生成文件按正常构建刷新。 -不新增接口语义,不部署云端,不迁移已有资源。 - -手工设计评审确认:组合保留双方已批准行为、名称占用、请求身份以及 TOS 临时 -配置;除现有 Role 设计外不改变组件语义。去掉移植 TOS 段落与目标分支冲突的 -CON-17 标号,保留目标的 BytePlus CON-17 和其他 lifecycle 标识。 - -2026-10-14 验证结果(基线 `2ffe6e14`,本次合入工作区 diff): - -- 新增 4 个 lifecycle/TOS 组合用例(Python、Vitest 各 2 个)。创建请求模块 - 18/18 通过,表单 67/67 通过。手动模式测试首次预期错误,已按既有省略字段 - 契约修正;自动模式保留 `openvikingMode=auto`。未确认独立业务缺陷。 -- 托管 MPA/Tool/资源列表 pytest 1,038/1,038 通过,覆盖率运行同样通过。 - 相对目标分支的增量可执行行覆盖率 100%:Python 102/102、TypeScript 10/10, - utree 解析 coverage.py XML / Clover XML,按用户要求的 95% 阈值检查 - (不冒充 CI 规则)。 -- 全量前端 1,389 个 Node 测试、105 个 Vitest 用例通过;构建、打包资源、 - i18n、全文件 pre-commit 通过。Python 版本为 3.12.12。 -- 全量 Python:7,150 通过、16 失败、2 收集错误、59 跳过、4 xfailed、 - 6 个 subtest 通过(双 worker,469.04 秒)。目标分支干净源码同样复现 - 16 失败/2 错误(其他 99 个用例通过),缺少可选 llama_index、openai_codex、 - anthropic 依赖。变更源码 Pyright 有 15 个 SDK 构造参数错误,目标分支 - `mpa_tool.py` 同样复现。这两项门禁仍失败/受阻;未声明 smoke 或 Python 3.10 - 已验证。 -- 隔离的本地浏览器验证:自动 OV/独立 PG/所选桶组合提交且没有 TOS 密钥, - 正常/空列表/加载/错误/重试状态、键盘选桶、任务取消/重试、步骤导航及 - 390px 窄屏。新提示文案已可见验证。接口为模拟,不证明真实云端创建。 - 临时验证文件已删除。 - -本次合入代码已实现、定向检查已验证,尚未部署;下文历史 V42 云端证据不验证 -这次新合入版本。 - -用户于 2026-10-14 要求仅调整提示文案:未选桶时文件保存在沙箱本地目录。 -同步中英文提示;内置配置默认不挂载 TOS、CLI/profile 兼容行为均不变。 - -[English](2026-10-12-bucket-only.md) - -- 变更 ID:`studio-mpa-tos-iam` -- 日期:2026-10-12 -- 状态:implemented;官方分支定向检查 verified;保留历史 V42 云端证据;官方迁移版未部署;基线全量门禁 blocked -- 基线:官方 `origin/main`,`812faadf`;初始实现及验证基线:`superops/main`,`035387d1` -- 契约:[Studio MPA 创建](../../../specs/studio-mpa-creation/README.zh.md),会话输出存储。 - -## 背景与范围 - -Studio 目前要求同时提供 `tosAccessKey`、`tosSecretKey`、`tosBucket`。 -`mpa_tool.build_tos_mount_config` 固定输出 AK/SK 鉴权;AgentKit SDK 支持 -不带 Credentials 的 `TOS_CREDENTIAL_TYPE_IAM_ROLE`。已有 managed IAM -协调器保留额外绑定、拒绝冲突策略,但平台 TOS 策略没有业务桶写权限。 - -用户于 2026-10-12 确认 Studio 只选桶,并自动增量补齐桶授权。本次只修改 -TOS 挂载鉴权,不改 Studio 登录、Runtime KeyAuth/JWT 或 Worker 协议。 -最初实现范围不含发布。用户于 2026-10-12 随后明确授权提交推送、更新现有云端 -Studio 并创建测试 MPA;创建桶、修改公开策略或迁移已有会话仍不在范围内。 - -## 需求与场景 - -### 已批准的桶前缀调整 - -2026-10-12 用户将 Studio 可选桶收窄为桶名以 `agentkit-platform-` 开头。 -选择器在已有资源接口传入可选 `namePrefix`,按大小写敏感的精确前缀匹配,并在 -搜索、计数和分页之前过滤。不传前缀的通用部署查询保持原行为。Studio 创建拒绝 -其他桶及其恢复草稿,且在云调用前失败;独立 CLI/Worker 桶名校验保持不变。 -本调整不修改角色已有策略、桶公开策略或会话路径。测试允许/排除/伪相似桶名、 -空选择、旧草稿、参数传输及分页;人工设计复核确认这些边界。 -此前版本已部署为 V41,环境变量未变,前端文件哈希匹配。用户提出本调整前, -尚未提交 MPA 或绑定桶策略;云端创建及全链路验证暂缓。 - -前缀调整验证:新增 8 个 Python 用例和 3 个 Vitest 用例。相关 Python 754/754, -创建窗口 46/46,完整前端 1,377 个 Node 测试及 73 个 Vitest 用例通过。构建、 -前端产物(113 文件/350 引用)、i18n、Ruff、改动文件 Pyright 和 pre-commit 通过。 -本轮四个目标文件相对 HEAD 的可执行增量行覆盖率为 100%(Python 22/22、前端 -10/10),utree 使用 coverage.py XML 和 Istanbul Clover XML 统计。先确认缺少前缀 -逻辑时测试失败,再同步相关 Studio fixture,没有放宽授权顺序或失败传播断言。 -全量 Python 再跑:6,495 通过、6 失败、2 收集错误、19 跳过、2 xfailed、6 subtests -通过;失败的 Harness 用例和可选依赖缺失均为此前已复现的基线阻塞。 -本地浏览器通过模拟响应确认仅展示匹配桶,临时验证页和 Vite 已清理。未报告存量 -缺陷,这是已批准的范围变更。以上为发布前证据快照;后续已授权的发布和云端 -Role 挂载验证仍待执行,将与本地验证结果分开记录。 - -- FR-1:只展示一个可选桶选择器,复用资源列表 hook 与 DeploymentSelect; - 保留加载、错误、空列表、搜索、分页、重试、禁用、键盘和取消行为。不展示 TOS AK/SK。 -- FR-2:Studio 提供桶时使用实际 Worker Tool 角色的 IAM Role 鉴权;安全拒绝 - 非法桶名和旧客户端非空凭据提交,清除浏览器恢复草稿中的旧凭据。未选桶保留 - 服务端 profile(内置 profile 默认不挂载)。CLI/YAML AK/SK 模式保持兼容;显式 `tos-credential-type: iam_role` - 支持仅配置桶的 managed profile。 -- FR-3:在工作负载身份、数据库、网络写操作前,核验桶属于部署账号且地域匹配,再协调稳定的 - 桶专属自定义策略与 Worker 角色的 Global 绑定。验证账号、信任、策略内容和回读; - 保留额外策略及信任。冲突即失败;策略缺失、并发创建/绑定用有界超时恢复。 -- FR-4:只在所选桶授予 `tos:HeadBucket`、`tos:ListBucket`;只对其 - `sandbox-session/*` 对象授予 `tos:GetObject`、`tos:PutObject`、 - `tos:AbortMultipartUpload`、`tos:DeleteObject`。不涉及其他桶、CreateBucket、 - 桶策略/ACL、KMS 授权或全对象通配范围。 -- FR-5:Tool 挂载使用 IAM Role 且不带 Credentials;保持 `/data/output`、 - 规范的逐会话路径和 Runtime 非敏感挂载开关/桶名。复用核验同时检查鉴权模式 - 与角色,不能只检查路径。不得隐式修改已有 Tool 和 Session。 -- FR-6:权限、归属、配置和策略冲突在 IAM 阶段安全失败,返回本地化错误。 - 取消保留 IAM 资源供重试;云调用使用新读取并核验账号的部署凭据。 - -## 设计与兼容 - -为 managed Worker 增加可选鉴权模式,CLI 历史默认仍为 AK/SK。Studio 请求仅 -包含 `tosBucket`,固定 runner 应用 IAM Role。任务身份、锁定、取消契约保持不变。 -复用已有 IAM SDK 边界、规范策略比较与错误白名单,增加独立命名的桶策略, -不改 `VeADKMPARuntimeAccessV1`。桶专属稳定策略名避免不同桶之间读改写竞争, -同一请求的并发操作可收敛。 - -ListBucket 暴露该桶元数据,但对象访问只允许会话输出前缀。共享角色的所有 -使用者都会获得这个前缀权限,逐会话目录不是租户授权。SSE-KMS 桶需要另行 -批准密钥权限。旧 Studio 客户端提交凭据字段会得到安全校验错误,需要刷新。 -旧 CLI AK/SK 请求和普通智能体部署不变。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | 证据 | -| --- | --- | --- | --- | -| FR-1、FR-2 | T-1:UI/API/config | AC-1:仅桶请求、清除旧密钥、选择器状态 | config/routes 和 43 个窗口用例通过;隔离浏览器通过 | -| FR-3、FR-4、FR-6 | T-2:IAM ensure 与编排接线 | AC-2:精确权限范围、重复/并发重试、失败/取消边界 | IAM/service 测试通过 | -| FR-5 | T-3:Tool 参数与复用核验 | AC-3:无 Credentials,严格核验模式/角色,旧模式不变 | Tool/worker 测试通过 | -| 全部 | T-4:文档/产物/门禁 | AC-4:定向/全量测试、构建/产物、浏览器状态、可执行增量覆盖率 >95% | 定向/构建/产物/浏览器/覆盖率通过;全量回归和 Pyright 存在已复现的基线阻塞 | - -先执行相关 pytest,再执行 -`uv run --extra dev pytest -n 2 -m 'not codex_smoke and not piagent_smoke'`、 -`npm --prefix frontend test`、构建、产物核验、改动 Python 文件 Ruff/Pyright、 -i18n、密钥扫描与 diff 空白检查。覆盖率统计可执行改动行,不以整文件替代。 -浏览器检查使用隔离的模拟云响应;未单独授权的真实 IAM/挂载 E2E 标记 not_run。 - -## 评审与交付 - -review-spec 技能不可用,以人工设计评审替代,检查角色归属、增量策略范围、旧 CLI -兼容、重试/取消、本地化和双语一致性。ui-ux-pro-max 不可用,采用仓库 SPEC、 -Foundation 和已有选择器行为。用户明确授权业务功能实现,单测技能的业务编辑 -限制不覆盖此授权;测试仍严格断言正确行为。 - -## 验证与变更记录 — 2026-10-12 - -修改 Studio 请求/窗口、managed Worker 配置、IAM 协调、服务编排和 Tool 元数据 -核验,同步双语组件契约与操作指南。无需修改 MPA Agent/Worker 源码,已有会话 -挂载路径不变。复用资源选择器及其已有样式,没有新建 UI 系统。已删除临时浏览器 -验证页源码并停止隔离的 Vite 服务,重新生成正式前端产物。 - -- **pass:**Python 3.12.12,`tests/integrations/mpa_managed` 和 - `tests/integrations/test_mpa_tool.py` 共 719/719;相比基线 677 个净新增 42 个。 - 契约测试在实现前确实失败。覆盖率插件环境下,Pydantic 延迟导入导致收集失败; - 在 `pytest.main([...])` 前显式导入 `pydantic.RootModel` 后,同样 719 个测试可以 - 正常统计覆盖率,未修改业务代码或依赖。基线 service 收集也复现了该覆盖率 - 导入错误。最终验证未采用 Python 3.13。 -- **pass:**可执行增量行覆盖率 100%:Python 97/97,由 `utree coverage parse` - 对 diff hunks 和 coverage XML 统计;前端 9/9,由 Istanbul statement 起始行与 - 改动行求交。排除未改动代码、纯类型、测试、文档及生成包;不是整文件或分支覆盖率。 -- **pass:**`npm --prefix frontend test`:1,377 个 Node 测试及 70 个 Vitest 用例; - `npm --prefix frontend run build`、`check:i18n`、`test:webui-assets`(113 文件、 - 350 内部引用)、Ruff、pre-commit 全文件和 `git diff --check`。Vite 保留已有大包警告。 -- **pass:**真实本地浏览器、模拟 API 响应:正常选择、无 TOS 密钥输入、加载、 - 空列表、错误/重试、搜索、分页、键盘选择、中文搜索输入、关闭、390px 窄窗口。 - 这不是云端 IAM/挂载验证,也不是操作系统真实 IME composition 测试。 -- **fail,已复现基线:**全量并行回归:6,485 通过、6 失败、2 收集错误、19 跳过、 - 2 xfailed、6 subtests 通过。使用相同解释器,在隔离的 `035387d1` 归档中复测 - 对应六个 Harness 用例和两个 self-host Sandbox 收集错误,完全一致(60 通过)。 - 原因是缺少可选 `llama_index`/`anthropic` 依赖,未修改无关业务或安装依赖来掩盖门禁。 -- **fail,已复现基线:**Pyright 使用 `.venv/bin/python`,在 `mpa_tool.py` 未改动的 - 历史 SDK 调用中报 15 个签名错误;临时复制的原始基线文件也报相同 15 个错误, - 已删除临时文件。其他改动的生产 Python 文件无 Pyright 诊断。 -- **not_run:**真实 IAM 写入、云端 Role 挂载会话 E2E、部署、镜像发布、提交或推送。 - 既有 Tool/Session 未变更。共享角色权限、已有 Deny、桶公开策略和 KMS 限制仍需 - 操作方知悉;策略成功绑定不代表已证明云端文件读写成功。 - -实现评审检查稳定桶策略名、账号/地域/信任核验、精确对象前缀、增量幂等/并发、 -安全失败、超时/取消及旧 CLI 兼容。未引入已有策略覆盖或全对象权限。全量/类型 -门禁不能表述为通过,云端验收与本地实现仍然独立。 - -### 已授权的发布与云端验收 - -2026-10-12 对齐未变化的 `superops/main`,实现提交 `267705ef` 已推送到 -`polaris/czh/studio-mpa-tos-iam-role`。同步后再次通过相关 Python 754 个用例、 -Node 1,377 个及 Vitest 73 个用例;pre-commit、i18n 和前端产物检查通过。 -前述全量基线阻塞仍然存在。 - -仅更新现有 Studio 代码包至 V42,环境变量哈希和品牌配置保持不变。wheel 中七个 -生产源码文件及全部 WebUI 产物与工作区匹配,远程入口 JS/CSS/logo 哈希与本地 -产物匹配。wheel SHA256: -`023bf085371cdc7c285b5e60bfcb2dd08e390c1935b7a995475c82c47b925a14`。 -本次仅发布 Studio,未重新构建 Runtime/Worker 镜像。 - -远程 Studio 可选桶由未过滤的 18 个收窄为 4 个平台前缀桶,并拒绝旧草稿的 -`agentkit-demo`。选 `agentkit-platform-2112682748` 创建测试 MPA -`role-tos-e2e-20261012-a`,未填写 TOS AK/SK。 -Runtime `r-yex3bbopvkwqejues1lc` 达到 Ready,`/readiness` 返回 200。 -Worker `t-yex3bb51xcwqejuen9oc` 达到 Ready,实际 Role 为 -`IDRoleForArkClawShareAgent`,挂载模式为 `TOS_CREDENTIAL_TYPE_IAM_ROLE`, -没有 Credentials,读写目录为 `/data/output`,基础挂载路径符合预期。 -IAM 回读确认精确桶/会话对象前缀的自定义策略已绑定。角色原本已有平台桶系统 -策略,增量协调不会收窄既有权限。所选桶没有公开桶策略。 - -从远程 Studio 提交两段会话,均实际调用沙箱执行命令。GetSession 回读确认分别 -挂载到 `/sandbox-session/tool-{tool_id}/session-{user_session_id}/`。 -两段会话各写入同名 `role-tos-e2e.txt`,内容为不同 A/B 标记;第二段起初没有该 -文件。账号侧独立 TOS 读取(没有账号侧写入)确认两个对象均持久化,B 写入后 -A 内容未变。A 的沙箱结果、TOS 对象和浏览器下载文件 SHA256 均为 -`e762fa20c53dd7394b3f89af8e084e42d486a8e241fa37d68acbd695a17b82a7`; -B 的沙箱结果和 TOS 对象 SHA256 均为 -`0d797e5173d581880100a21f25a7e00f4990d82b440c4f01f01d78e8776b1e6e`。 -浏览器下载事件等待在 45 秒后超时,但下载随后完成,已核验本地字节;不是接口 -失败。该镜像的 `/ready?scope=execution` 返回 404,执行证明来自实际命令和对象 -读取,不来自这个就绪接口。 - -测试 MPA 和两个输出对象保留供查看,既有 Tool/Session 未修改,仓库中没有残留 -探索文件。此次验收不覆盖收窄角色既有权限、KMS 加密桶、显式 Deny、独立身份 -或真实业务渠道消息交付。 - -### 官方上游 PR 准备 - -用户随后要求向 `volcengine/veadk-python:main` 提 PR。原 fork 分支的共同祖先会让 -PR 夹带无关历史,因此将两个功能/证据提交迁移到官方 `main` 的 `812faadf`,使用 -独立分支 `czh/studio-mpa-tos-iam-role-upstream`。唯一业务源码冲突是导入语句: -同时保留官方 `resolve_model_key` 和 TOS IAM ensure。前端生成物冲突通过官方 -基线源码重新构建解决,没有选择任一旧产物,也没有迁入无关 fork 提交。 - -两个 TOS 授权顺序用例最初失败,是因为模拟云没有提供官方新增的 Ark Key 发现 -依赖。仅在这两个用例中隔离该依赖,并断言它以正确 profile/cloud 被 await; -保留授权先于 identity、错误传播等全部断言,随后 2/2 通过。未为测试修改生产逻辑。 - -官方基线验证使用 Python 3.12.12:相关 Python 872 个用例通过,Node 1,377 个及 -Vitest 75 个用例通过;前端构建、i18n、产物、仓库固定版本 Ruff 0.11.12 和 -pre-commit 通过。相对官方 main 的可执行增量行覆盖率为 100%(Python 102/102、 -TS 10/10),使用 coverage.py XML 和 Istanbul Clover XML 解析。聚焦单测修复 -遵循 bits-unit-test-gen Step1–7;TOS 编排覆盖新增可执行行 5/5,utree flush 已执行。 - -官方全量回归已完成:6,950 通过、16 失败、2 收集错误、59 跳过、4 xfailed、 -6 subtests 通过。使用相同解释器,在隔离的官方 main 归档复现六个 Harness -失败和两个 self-host 收集错误(64 通过),原因是缺少可选 `llama_index`/ -`anthropic`。十个 Codex runtime 失败也在该归档复现,缺少可选 `openai_codex`。 -15 个历史 SDK 签名 Pyright 错误在官方 main 原始 `mpa_tool.py` 上同样复现。 -这些门禁不能表述为全绿。 - -上文 V42 云端证据属于原 fork 实现。本次官方基线迁移没有重新部署或运行浏览器/ -云端 E2E;保留官方模型 Key 行为,不据此推断该行为已在 V42 执行。 -原 fork PR 和云资源均保持不变。 diff --git a/prd-spec/features/studio-oidc-id-token/2026-09-23-retain-id-token.md b/prd-spec/features/studio-oidc-id-token/2026-09-23-retain-id-token.md deleted file mode 100644 index e4ac62f87..000000000 --- a/prd-spec/features/studio-oidc-id-token/2026-09-23-retain-id-token.md +++ /dev/null @@ -1,39 +0,0 @@ -# Retain the OIDC ID Token in Studio Sessions - -- Change ID: `studio-oidc-id-token` -- Created/revised: 2026-09-23 -- Status: superseded by [Studio MPA login prewarm](../studio-mpa-login/2026-09-23-studio-mpa-prewarm.md) -- Chinese: [2026-09-23-retain-id-token.zh.md](2026-09-23-retain-id-token.zh.md) -- Component: [Studio OAuth session](../../../specs/studio-oauth-session/README.md) - -## Background and evidence - -Historical proposal only: a 1,519-byte access token plus a similarly sized ID token produced a roughly 4,490-byte cookie in a local size check. The successor keeps the ID token request-local instead of persisting it in the browser. - -`OAuth2Handler.exchange_code_for_token()` and `_refresh_access_token_once()` receive token responses but currently construct `OAuth2Session` without `id_token`. The signed Studio session cookie therefore cannot retain an OIDC ID token for a later, separately authorized MPA identity handoff. The live Studio and MPA client IDs are assumed equal for this change, not verified here. - -## Goals and non-goals - -Retain an optional `id_token` from authorization-code and refresh responses in the existing Studio session, including cookie round trips. Keep legacy cookies readable. Do not add a token-sharing API, send tokens to MPA, change MPA validation, or change UserPool configuration. - -## Scenarios and requirements - -- `FR-1`: Given an authorization-code response with `id_token`, the resulting Studio session and signed cookie retain it. -- `FR-2`: Given a refresh response with a new `id_token`, replace the old value. If the provider omits it, retain the preceding value; consumers must still validate its expiry before use. -- `FR-3`: A legacy session lacking `id_token` remains valid and yields `None` for that field. - -## Design and contract impact - -Add one optional field to `OAuth2Session` and pass the existing token-response value in the two construction sites. The existing cookie encoder/decoder handles the field without a new storage format. No new HTTP route, configuration, dependency, event, or frontend type is added. The cookie is signed but not encrypted and already carries access/refresh tokens; adding an ID token can increase its size beyond browser limits. This change does not make the ID token safe to expose to browser JavaScript or prove that two OAuth clients match. Before any future MPA handoff, verify token expiry, subject, issuer, audience, authorization, and cookie-size behavior; use server-side storage if the cookie limit is exceeded. - -## Tasks and acceptance - -| Requirement | Task | Acceptance | Verification | -| --- | --- | --- | --- | -| `FR-1` | `T-1`: add a failing login/cookie test, then retain the field | `AC-1`: login ID token survives a cookie round trip | `tests/auth/test_oauth2_auth.py` | -| `FR-2` | `T-2`: add refresh tests, then retain or replace the field | `AC-2`: refresh with/without an ID token has the stated result | `tests/auth/test_oauth2_auth.py` | -| `FR-3` | `T-3`: test legacy-cookie compatibility | `AC-3`: old cookies decode without an ID token | `tests/auth/test_oauth2_auth.py` | - -## Review and delivery record - -Scope approved by the user's 2026-09-23 request to retain `id_token`, explicitly excluding MPA validation changes and treating client alignment as an assumption. Review: the minimal model-and-construction change preserves the existing session/cookie boundary; the cookie-size and cleartext-cookie risks are known and remain outside this scoped change. Verification and delivery results: pending implementation. diff --git a/prd-spec/features/studio-oidc-id-token/2026-09-23-retain-id-token.zh.md b/prd-spec/features/studio-oidc-id-token/2026-09-23-retain-id-token.zh.md deleted file mode 100644 index 18605357d..000000000 --- a/prd-spec/features/studio-oidc-id-token/2026-09-23-retain-id-token.zh.md +++ /dev/null @@ -1,39 +0,0 @@ -# 在 Studio 会话中保留 OIDC ID Token - -- Change ID: `studio-oidc-id-token` -- 创建/修订:2026-09-23 -- 状态:已被 [Studio MPA 登录预热](../studio-mpa-login/2026-09-23-studio-mpa-prewarm.zh.md) 取代 -- English: [2026-09-23-retain-id-token.md](2026-09-23-retain-id-token.md) -- 组件:[Studio OAuth session](../../../specs/studio-oauth-session/README.zh.md) - -## 背景与证据 - -本文件仅记录历史方案:本地长度检查中,1,519 字节的 access token 与同等长度的 ID token 使 Cookie 约为 4,490 字节。后续方案将 ID token 限制在本次请求内,不再保存到浏览器。 - -`OAuth2Handler.exchange_code_for_token()` 和 `_refresh_access_token_once()` 收到 token 响应后,目前构造 `OAuth2Session` 时没有传入 `id_token`。因此,Studio 的签名会话 Cookie 无法为后续经单独授权的 MPA 身份交接保留 OIDC ID token。本次将云上 Studio 与 MPA 的客户端 ID 相同作为假设,尚未验证。 - -## 目标与非目标 - -在现有 Studio 会话中保留授权码及刷新响应里的可选 `id_token`,包括 Cookie 往返,并继续读取旧 Cookie。不新增 token 共享 API,不向 MPA 发送 token,不修改 MPA 校验或 UserPool 配置。 - -## 场景与需求 - -- `FR-1`:授权码响应含 `id_token` 时,生成的 Studio 会话和签名 Cookie 保留它。 -- `FR-2`:刷新响应含新 `id_token` 时替换旧值;提供方未返回时保留旧值。消费者使用前仍须验证过期时间。 -- `FR-3`:没有 `id_token` 的旧会话仍有效,该字段为 `None`。 - -## 设计与契约影响 - -仅为 `OAuth2Session` 增加一个可选字段,并在两个现有构造点传入 token 响应值。现有 Cookie 编解码无需新存储格式即可处理该字段。不增加 HTTP 路由、配置、依赖、事件或前端类型。Cookie 经过签名但未加密,并且已携带 access/refresh token;加入 ID token 可能使长度超过浏览器限制。本改动不意味着可以向浏览器 JavaScript 暴露 ID token,也不证明两个 OAuth 客户端相同。未来交接给 MPA 前,必须验证 token 的有效期、用户、签发方、受众、授权和 Cookie 长度;若超限,应改为服务端存储。 - -## 任务与验收 - -| 需求 | 任务 | 验收 | 验证 | -| --- | --- | --- | --- | -| `FR-1` | `T-1`:先增加失败的登录/Cookie 测试,再保留字段 | `AC-1`:登录 ID token 经 Cookie 往返后仍在 | `tests/auth/test_oauth2_auth.py` | -| `FR-2` | `T-2`:增加刷新测试,再保留或替换字段 | `AC-2`:刷新响应有或没有 ID token 均符合约定 | `tests/auth/test_oauth2_auth.py` | -| `FR-3` | `T-3`:测试旧 Cookie 兼容性 | `AC-3`:旧 Cookie 能解码,且没有 ID token | `tests/auth/test_oauth2_auth.py` | - -## 评审与交付记录 - -用户在 2026-09-23 要求保留 `id_token`,明确排除 MPA 校验改动,并将客户端一致作为假设,视为本范围的批准。评审:最小的模型与构造点改动保留现有会话/Cookie 边界;Cookie 长度与未加密风险已知,属于本次限定范围之外。验证与交付结果:待实施。 diff --git a/prd-spec/features/studio-sandbox-download/2026-09-17-history-download.md b/prd-spec/features/studio-sandbox-download/2026-09-17-history-download.md deleted file mode 100644 index 9a9b8b17a..000000000 --- a/prd-spec/features/studio-sandbox-download/2026-09-17-history-download.md +++ /dev/null @@ -1,36 +0,0 @@ -# Session history Sandbox downloads - -[中文](2026-09-17-history-download.zh.md) - -## Scope and approval -User approved implementation on `czh/studio-cron-e2e` after agreeing that history Markdown paths should invoke MPA downloads. Stored history already contains `[random.txt](/data/output/random.txt)`. No Worker, SSE protocol, artifact registry, or persistence change is needed. - -## Design -Follow [component contract](../../../specs/studio-sandbox-download/README.md). Supply conversation scope around assistant blocks; the shared Markdown renderer recognizes allowed links before video handling. A small link component owns loading, errors, cancellation and blob download. The existing ADK client resolves the selected Runtime and forwards the request through the authenticated Studio proxy. Preserve link labels and use the larger download control approved below. No dependencies or new backend routes. - -## Tasks and acceptance -1. Add failing regression tests for paths, Unicode/spaces, history Markdown, proxy routing and lifecycle. -2. Implement scoped links and localized loading/error feedback; retry by clicking again. -3. Run frontend tests/build and incremental coverage above 95%; inspect local browser and commit generated web assets. -4. Fetch/rebase intended upstream, run pre-commit, commit and push. - -## Review -Direct design review (review-spec unavailable): ownership, session scope, root allowlist, cancellation, credential isolation, unchanged external links and bilingual equivalence checked. No blockers. frontend-design applied; ui-ux-pro-max unavailable locally, existing frontend/SPEC.md controls visual behavior. - -## Risks and verification -Sandbox lifetime limits availability. Blob downloads buffer files in browser memory. No automatic Sandbox creation or recovery. Tests, browser evidence and gate results will be recorded after implementation. - -## Approved presentation update -User confirmed downloads work and requested hiding legacy file-card contents and a larger, more visible download control. Use existing primary colors, 44px minimum height, visible focus and wrapping filenames; suppress file-card/personal-drive-enable-card subtrees only in conversation scope. Regression tests must preserve surrounding Markdown. - -## Verification record (2026-09-17) -- PASS: `npm --prefix frontend run test:sandbox-download-coverage`: 26 tests. New component lines/statements/functions/branches 100%; added instrumented source lines 46/46 (100%). Conservatively counting the uninstrumented App provider expression as uncovered: 46/47 (97.87%). Existing client/Markdown whole-file coverage is not the incremental coverage metric. -- PASS: `npm --prefix frontend test`: 1102 tests; `npm --prefix frontend run build` (TypeScript and both bundles); `npm --prefix frontend run check:i18n`. -- PASS: `git fetch polaris` and `git rebase --autostash polaris/czh/studio-cron-e2e` (already current; changes restored); `UV_PROJECT_ENVIRONMENT=/Users/bytedance/Applications/veadk-studio-mpa/.venv uv run --no-sync --extra dev pre-commit run --all-files` (existing environment, Ruff and secret scan). -- PASS: real Chrome localhost:8765, last MPA agent, existing random-file history: legacy path hidden, visible download button, Enter activates loading and download; Runtime proxy upstream HTTP 200. User independently confirmed download before the presentation update. Error/retry/cancellation and Unicode paths validated in isolated tests, not deliberately induced in live Sandbox. Narrow viewport not run. -- Backend/Python regression: not applicable (no Python edits). Build warns about existing bundle sizes. Generated vendor bundles retain upstream trailing whitespace; authored source diff whitespace clean. -- Review: authentication stays in existing transport, conversation context scopes the feature, path traversal/unsupported roots excluded, abort and object URL cleanup tested. No credentials or production payloads added to fixtures. - -Presentation follow-up: user requested a line break before the download button. Use block-level flex with fit-content width so each button occupies its own line without stretching. CSS-only change; existing functional coverage remains applicable. - -Verification: build, 1102 frontend tests and pre-commit passed; local Studio restarted with rebuilt assets. Narrow-screen browser check not rerun for this CSS-only adjustment. diff --git a/prd-spec/features/studio-sandbox-download/2026-09-17-history-download.zh.md b/prd-spec/features/studio-sandbox-download/2026-09-17-history-download.zh.md deleted file mode 100644 index 0d3a0064f..000000000 --- a/prd-spec/features/studio-sandbox-download/2026-09-17-history-download.zh.md +++ /dev/null @@ -1,36 +0,0 @@ -# 会话历史 Sandbox 文件下载 - -[English](2026-09-17-history-download.md) - -## 范围与批准 -用户已确认历史 Markdown 路径调用 MPA 下载,并要求在 `czh/studio-cron-e2e` 实现。历史已包含 `[random.txt](/data/output/random.txt)`。无需修改 Worker、SSE 协议、artifacts 注册或持久化。 - -## 设计 -遵循[组件契约](../../../specs/studio-sandbox-download/README.zh.md)。在助手消息块外提供会话上下文,共用 Markdown 渲染器在视频判断之前识别允许的链接。小型链接组件负责加载、错误、取消及 blob 下载。现有 ADK 客户端解析选定 Runtime,经 Studio 鉴权代理发送请求。保留链接文案,采用下文已批准的加大下载入口,不增加依赖或后端路由。 - -## 任务与验收 -1. 先写失败回归测试,覆盖路径、中文空格、历史 Markdown、代理路由及生命周期。 -2. 实现会话范围内链接与本地化加载/错误提示,再次点击可重试。 -3. 执行前端测试、构建,增量覆盖率超过 95%;检查本地浏览器并提交生成的 web 资源。 -4. fetch/rebase 目标上游,执行 pre-commit,然后 commit/push。 - -## 评审 -直接评审(review-spec 不可用):已核对职责、会话隔离、目录白名单、取消、凭据隔离、普通外链兼容及双语一致性,无阻塞。应用 frontend-design;本地未找到 ui-ux-pro-max,视觉行为遵循现有 frontend/SPEC.md。 - -## 风险与验证 -下载受 Sandbox 生命周期限制。blob 下载会在浏览器内存缓冲文件。不会自动创建或恢复 Sandbox。实现后记录测试、浏览器证据及门禁结果。 - -## 已批准的展示调整 -用户确认下载正常,要求隐藏旧文件卡片内容、增大并突出下载入口。复用 primary 色、最小 44px 高度、可见焦点与文件名换行;仅在会话上下文隐藏 file-card/personal-drive-enable-card 子树。回归测试保留周围 Markdown。 - -## 验证记录(2026-09-17) -- PASS:`npm --prefix frontend run test:sandbox-download-coverage`:26 项。新增组件行/语句/函数/分支覆盖率均 100%;已插桩增量代码行 46/46(100%)。将未插桩的 App provider 表达式保守计为未覆盖:46/47(97.87%)。既有 client/Markdown 整文件覆盖率不是增量覆盖率。 -- PASS:`npm --prefix frontend test`:1102 项;`npm --prefix frontend run build`(TypeScript 与两个 bundle);`npm --prefix frontend run check:i18n`。 -- PASS:`git fetch polaris`、`git rebase --autostash polaris/czh/studio-cron-e2e`(已是最新,改动恢复);`UV_PROJECT_ENVIRONMENT=/Users/bytedance/Applications/veadk-studio-mpa/.venv uv run --no-sync --extra dev pre-commit run --all-files`(复用现有环境,Ruff 与密钥扫描通过)。 -- PASS:真实 Chrome localhost:8765,最后一个 MPA 智能体的随机文件历史:旧路径隐藏、下载按钮明显、回车触发加载与下载,Runtime 代理上游 HTTP 200。展示调整前用户也独立确认下载正常。错误/重试/取消及中文路径由隔离测试覆盖,未在真实 Sandbox 故意制造故障。窄视口未执行。 -- 后端/Python 回归:不适用(无 Python 修改)。构建有既有包体积警告。生成的 vendor bundle 保留上游尾随空格;手写源码 diff 空白检查通过。 -- 评审:鉴权沿用现有传输,会话上下文限制功能范围,排除路径穿越和非支持目录,已测试取消与对象 URL 释放。测试样例未添加凭据或生产 payload。 - -展示补充:用户要求下载按钮换行。使用块级 flex 与 fit-content 宽度,让每个按钮独占一行且不拉伸。仅修改 CSS,既有功能覆盖率仍适用。 - -验证:构建、1102 项前端测试及 pre-commit 通过;本地 Studio 已重启加载新资源。本次仅 CSS 调整未重跑窄屏浏览器检查。 diff --git a/prd-spec/features/studio-tool-activity-visualization/2026-09-12-studio-tool-activity-visualization-design.md b/prd-spec/features/studio-tool-activity-visualization/2026-09-12-studio-tool-activity-visualization-design.md deleted file mode 100644 index fad678f3e..000000000 --- a/prd-spec/features/studio-tool-activity-visualization/2026-09-12-studio-tool-activity-visualization-design.md +++ /dev/null @@ -1,276 +0,0 @@ -# Studio Tool Activity Visualization Design - -- **Status:** Implemented and verified -- **Date:** 2026-09-12 -- **Change type:** Feature -- **Chinese version:** [2026-09-12-studio-tool-activity-visualization-design.zh.md](2026-09-12-studio-tool-activity-visualization-design.zh.md) -- **Target branch:** `feat/mpa-agent-oneclick-provision` - -## 1. Overview - -### 1.1 Problem - -AgentKit Studio already receives structured ADK tool calls and mpa-agent sandbox progress, but generic tool calls fall back to formatted JSON. One logical sandbox command may also appear as an outer delegation card plus inner `exec_command` and `commandExecution` rows. Users can inspect the protocol, but cannot quickly answer the operational questions that matter: what is running, what command or resource is involved, what output has arrived, whether it succeeded, and how long it took. - -The current implementation already contains useful foundations: `Block.kind === "tool"`, stable `callId` matching, `CodexSandboxActivity`, built-in tool registration, and a shared `eventsToTurns()` history projection. This feature must extend those foundations instead of adding a second transcript model. - -### 1.2 Goals - -- Replace default JSON-first tool rendering with compact Codex-style activity units. -- Use one visual shell for all tools and type-specific detail presenters for commands, reads, searches, file changes, MCP calls, and generic tools. -- Merge tool start, output, result, and error events by stable identity into one updating unit. -- Flatten sandbox-internal activity into the conversation timeline while retaining a lightweight `Codex Sandbox` source label. -- Keep running activity visible, successful activity compact, and failures immediately diagnosable. -- Guarantee that live projection and persisted history reconstruction produce equivalent tool content and ordering. -- Preserve existing specialized cards, session history, search, feedback, attachments, artifacts, A2UI, parallel agents, and final answers. - -### 1.3 Non-goals - -- Changing the mpa-agent/A2A/ADK event protocol or database schema. -- Persisting disclosure state or other presentation-only state to a session. -- Replacing existing specialized renderers such as `create_agents` and `branch_compare`. -- Implementing an unrestricted JSON inspector or exposing values removed by backend redaction. -- Synthesizing fake streaming chunks when the upstream model emits only a final response. -- Redesigning the conversation page, navigation, or global Studio visual language. - -## 2. User Scenarios - -### Scenario 1: Follow a running command - -**Given** a tool starts a sandbox command and streams output, -**When** Studio receives start and output events, -**Then** one expanded activity unit updates in place with the command, elapsed time, and recent output instead of adding JSON rows. - -### Scenario 2: Review a completed conversation - -**Given** a session contains persisted function calls, responses, or a Codex activity snapshot, -**When** the user refreshes Studio or switches back to the session, -**Then** the same ordered, structured activity units are reconstructed without duplicate calls or missing final text. - -### Scenario 3: Diagnose a failure - -**Given** a command or tool fails, -**When** its terminal event arrives or history is restored, -**Then** the activity remains expanded and shows a concise error, status or exit code, retained output, and a secondary raw-data disclosure. - -### Scenario 4: Read a tool-heavy conversation - -**Given** an agent performs multiple adjacent successful read-only operations, -**When** those operations complete, -**Then** Studio may collapse them into one “Explored N items” group while preserving each child operation and its raw data. - -## 3. Functional Requirements - -### FR-1 — Unified activity shell - -Every ordinary tool activity uses the same header geometry: icon, explicit status marker, localized action title, one-line summary, compact metrics, and disclosure affordance. Color supplements but never replaces text or icon semantics. - -### FR-2 — Presentation model - -Tool rendering is derived through a pure presentation layer. The source `Block` retains raw `args` and `response`; React rendering does not contain ad hoc JSON-shape inference. The presentation model includes category, title, summary, status, source, duration, exit code, typed details, and raw values. - -### FR-3 — Lifecycle correlation - -Start, output, result, and error updates with the same stable `callId` update one activity unit. Replayed `eventId` values are ignored. Out-of-order results are buffered when safely correlatable; otherwise they remain visible as an unmatched generic activity instead of being discarded. - -### FR-4 — Adaptive disclosure - -- Queued/running activities default open. -- Successful activities default closed after completion. -- Failed activities default open. -- Once the user manually toggles an activity, later updates must not override that choice. -- Restored history initializes from final status and does not persist transient disclosure state. - -### FR-5 — Typed presenters - -The initial presenter set is: - -| Category | Header summary | Expanded content | -| --- | --- | --- | -| Command | normalized command | command, working directory, stdout/stderr, exit code, duration | -| Read/list | file or resource count | paths, ranges, omission marker | -| Search | query and result count | matched paths/items and snippets | -| File change | file count and diff totals | path, add/update/delete action, diff summary | -| MCP | `server/tool` and result count/status | selected inputs and structured result | -| Authorization | requested resource/tool | explanation and primary authorization action | -| Generic | tool name and best-effort summary | readable key-value fields | - -Authorization keeps its existing dedicated interaction instead of becoming a passive collapsible row. Existing specialized tool renderers remain authoritative. - -### FR-6 — Codex Sandbox flattening - -Nested `CodexSandboxActivity.items` are projected as first-class activity units in the current assistant turn. The outer `delegate_to_codex_sandbox` container is not rendered as a second card when structured child activity exists. A lightweight source label identifies the contiguous activity as `Codex Sandbox`. The final assistant answer stays outside the activity group. - -### FR-7 — Safe read-only aggregation - -Adjacent completed successful read, list, search, or resource-query activities from the same invocation and source may be grouped as “Explored N items”. Grouping occurs only after completion. Commands, writes, deletions, MCP calls, authorization, failures, cancellations, source changes, invocation changes, and final-answer boundaries end the group. Grouping changes presentation only and preserves the original ordered children. - -### FR-8 — Raw data disclosure - -Each activity provides a secondary “View raw data” disclosure inside its expanded details. Input and result are separate, formatted lazily, bounded for rendering, and individually copyable together with the call ID. It is closed by default and cannot reveal data removed by upstream allowlists or redaction. - -### FR-9 — Output handling - -Live command output updates in place. Completed output uses an authoritative aggregate when available. The default preview shows a bounded head and tail with an explicit omitted-line count. Full output uses an independently scrolling region with a height limit. ANSI/control sequences are sanitized or safely rendered; they are never injected as HTML. - -### FR-10 — History compatibility - -`eventsToTurns()` remains the sole history reconstruction entry point. Live and persisted events use the same normalization and presentation functions. Both camelCase and snake_case payloads remain supported. Missing status, duration, source, or call ID uses safe fallbacks. Unknown legacy tools retain a generic visible activity. - -### FR-11 — Responsive and accessible behavior - -At narrow widths, secondary summaries hide before titles or status metadata, details become full width, and no horizontal page scroll is introduced. Interactive headers and secondary disclosures are keyboard reachable, expose `aria-expanded`, retain visible focus, and provide at least a 44px effective hit area. Motion is limited to 120–180ms opacity/transform transitions and disabled under `prefers-reduced-motion`. - -### FR-12 — Localization - -All action, status, omission, copy, and empty/error labels are provided in the existing `conversation` namespace for Chinese and English. Raw tool names remain available only where they carry useful technical meaning or in raw details. - -## 4. Design - -### 4.1 Data flow - -```text -ADK / A2A event - -> existing applyEvent/eventsToTurns projection - -> tool lifecycle correlation by callId - -> pure ToolPresentation derivation - -> optional read-only presentation grouping - -> ToolActivityCard + category detail presenter -``` - -No transport or persistence contract changes. The same source blocks remain available for debugging and backward compatibility. - -### 4.2 Proposed modules - -- `frontend/src/ui/tool-activity/model.ts`: presentation types, safe accessors, normalization, summaries, output truncation, and grouping predicates. -- `frontend/src/ui/tool-activity/ToolActivityCard.tsx`: common header, adaptive disclosure, copy actions, raw-data disclosure, and accessibility semantics. -- `frontend/src/ui/tool-activity/ToolActivityDetails.tsx`: typed command/read/search/file/MCP/generic presenters. -- `frontend/src/ui/tool-activity/tool-activity.css`: component-local layout and state styles using existing semantic tokens. -- `frontend/src/blocks.ts`: minimal lifecycle correlation and sandbox flattening integration; no JSX. -- `frontend/src/ui/Blocks.tsx`: dispatch tool blocks through the new presenter while retaining registered specialized renderers. -- `frontend/src/i18n/resources/{zh-CN,en-US}/conversation.json`: localized activity labels. - -The exact split may be reduced if a module remains trivial, but parsing, state reduction, and React presentation must stay independently testable. - -### 4.3 Visual specification - -- The activity row aligns with the existing 32px tool header and the transcript's horizontal anchor. -- Effective interactive height is at least 44px without forcing the visible row to become visually heavy. -- Status states use neutral/amber/green/destructive semantic treatments plus explicit labels. -- Commands and output use the existing monospace stack; titles, metrics, IDs, and normal summaries use the system UI font. -- Successful collapsed rows remain border-light or borderless. Expanded detail uses one subtle left rail, not nested card borders. -- Running updates reserve metric space to avoid layout shift. -- Long labels truncate with an accessible full-value affordance; output wraps or scrolls inside its own boundary. - -### 4.4 State ownership - -Tool lifecycle data belongs to the transcript projection. Disclosure state belongs to the individual rendered activity instance. Read-only grouping is a pure render-time view over adjacent blocks. No view state is written into ADK session state or server storage. - -## 5. History Safety Contract - -History behavior is a P0 release gate. The implementation must satisfy all of the following: - -1. Refreshing a completed session preserves tool order, status, details, and final answer. -2. Switching between sessions does not share disclosure or activity state. -3. Persisted `functionCall` plus `functionResponse` is sufficient to rebuild a complete activity without live progress. -4. Persisted Codex snapshots and live progress do not create duplicate child activities. -5. Missing or legacy fields degrade to a generic activity instead of throwing or hiding the event. -6. Presentation aggregation never mutates source blocks and never crosses an invocation or final-answer boundary. -7. Existing history listing, search, feedback, attachment, artifact, A2UI, and parallel-agent behavior remains unchanged. - -If live and replayed output cannot be made equivalent for an event type, the release must retain the existing generic renderer for that type rather than shipping an incomplete specialized view. - -## 6. Edge Cases - -| Case | Required behavior | -| --- | --- | -| Duplicate start/output/result | Deduplicate by stable event identity and update one unit. | -| Result arrives before start | Buffer by call ID; fall back to a visible unmatched result. | -| Missing call ID | Generate a deterministic projection-local identity where possible; do not merge ambiguous calls. | -| Same tool runs concurrently | Correlate by call ID, never by tool name alone. | -| Long or binary-looking output | Bound preview, preserve omission metadata, offer safe full view when text-renderable. | -| Malformed response | Show a generic “Unrecognized tool result” state plus raw-data access. | -| Running stream disconnects | Preserve the visible received state; history refresh later rebuilds the terminal state without resubmission. | -| User manually collapses running item | Preserve the user's choice during subsequent updates. | -| Specialized renderer throws | Isolate the error to that activity and use the generic presenter; do not break the transcript. | -| Secret-bearing field | Respect server redaction and apply existing client-side sensitive-key masking before raw display/copy. | - -## 7. Testing and Acceptance - -### 7.1 Unit tests - -- Presenter mapping for command, read/list, search, file change, MCP, authorization, and generic tools. -- Lifecycle merge for start/output/result/error, duplicate event IDs, out-of-order events, and concurrent same-name calls. -- Head/tail output truncation, omitted-line count, malformed values, and sensitive-key masking. -- Safe aggregation boundaries and preservation of child order. -- Adaptive disclosure transitions and manual-choice lock. - -### 7.2 History regression tests - -- Assert live event projection and `eventsToTurns()` replay produce equivalent normalized activity content. -- Refresh sessions containing old camelCase/snake_case tools, missing fields, Codex snapshots, attachments, artifacts, A2UI, and final text. -- Switch repeatedly between two sessions and assert no state leakage or mutation. -- Run all existing history, search, feedback, parallel-agent, and Codex sandbox tests unchanged. - -### 7.3 Component and accessibility tests - -- Running, completed, failed, empty, malformed, and long-output snapshots. -- Keyboard activation, visible focus, `aria-expanded`, accessible status text, and copy feedback. -- Widths 375px, 768px, 1024px, and 1440px; light and dark themes; reduced-motion mode. -- No body-level horizontal scroll or layout shift during streaming updates. - -### 7.4 Real E2E - -Using the existing mpa-agent test Runtime: - -1. Run a command with delayed output and verify one activity updates in place. -2. Verify successful completion collapses and the final answer remains separate. -3. Run a failing command and verify the error remains expanded. -4. Refresh both sessions and compare content/order with the live views. -5. Run two sessions concurrently and verify no cross-session activity. -6. Verify legacy non-streaming Runtime history still renders. -7. Scan rendered raw data and copied content for configured secrets. - -### 7.5 Acceptance criteria - -- `AC-1`: No supported tool displays raw JSON as its primary UI. -- `AC-2`: One logical command produces one updating activity unit. -- `AC-3`: Running opens, success collapses, failure stays open, and manual disclosure choice is respected. -- `AC-4`: Command output, exit status, duration, and omission count remain readable and bounded. -- `AC-5`: Safe read-only operations aggregate only within the defined boundaries. -- `AC-6`: Raw input/result/call ID remain available through a secondary disclosure and copy actions. -- `AC-7`: Refresh produces equivalent structured content and preserves final assistant output. -- `AC-8`: Existing specialized cards and non-tool transcript content do not regress. -- `AC-9`: Keyboard, focus, screen-reader, narrow-screen, theme, and reduced-motion checks pass. -- `AC-10`: No credential or unrestricted environment payload becomes visible or copyable. - -## 8. Delivery Sequence - -1. Add failing presenter and lifecycle tests. -2. Introduce the pure presentation model and typed presenters. -3. Add the unified activity card and adaptive disclosure. -4. Flatten Codex sandbox child activities and add the source marker. -5. Add safe read-only grouping. -6. Add raw-data disclosure with masking, bounds, and copy feedback. -7. Run history equivalence and full frontend regression tests. -8. Run real Runtime/Studio E2E and visual/accessibility verification. - -## 9. Risks and Mitigations - -- **History regression:** gate release on live/replay equivalence and retain generic fallback. -- **Incorrect correlation:** use call ID first and never merge ambiguous concurrent calls by name. -- **Excessive rendering cost:** derive presentation with pure memoizable functions, bound output, and lazily format raw JSON. -- **Visual noise:** successful items collapse and safe read-only work aggregates; failures and active work stay visible. -- **Loss of debugging detail:** preserve original values on the source block and expose them through the secondary disclosure. -- **Sensitive data exposure:** preserve backend allowlists and add value-aware masking before display and clipboard operations. - -## 10. Implementation and Verification Record - -- **Component contract:** [Studio Tool Activity](../../../specs/studio-tool-activity/README.md) -- **Implemented:** pure tool presentation, bounded/redacted raw data, adaptive disclosure, command alias lifecycle correlation, out-of-order response retention, bounded event deduplication, Codex child flattening, source markers, and adjacent read-only grouping. Existing registered specialized renderers remain unchanged. -- **Targeted verification (2026-09-12):** the final targeted tool/model and component reruns passed, including signed-URL masking, explicit partial failure, disclosure, and localized grouping cases; `npx tsc --noEmit` passed; `npm run check:i18n` passed. -- **Full frontend regression (2026-09-12):** `npm --prefix frontend test` passed 1,078 tests; `npm --prefix frontend run build` passed; `npm --prefix frontend run test:webui-assets` verified 102 packaged files and 246 internal references; `uv run --extra dev pre-commit run --all-files` passed. -- **Repository regression:** `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` ran 4,030 tests successfully but ended with 6 failures and 2 collection errors because optional `llama_index` and `anthropic` packages are absent from the local environment. The failures are outside this frontend change. -- **Live Runtime evidence:** Runtime `r-yeuujrrcowb21078p9jh` produced one in-place running command activity. Replaying its 34 captured frames after the lifecycle fix produced one completed activity with exit code `0` and duration `1601 ms`. -- **Browser evidence:** the rebuilt Studio was served successfully and `/web/ui-config` returned the expected Studio configuration. A final headless Chrome capture attempt hung and was stopped; previously completed normal-width screenshots and live DOM checks remain the visual evidence. -- **Known transport limitation:** the tested A2A virtual session persisted only final text, so refresh preserved the final answer but had no tool activity to reconstruct. Studio does not fabricate or resubmit missing activity. diff --git a/prd-spec/features/studio-tool-activity-visualization/2026-09-12-studio-tool-activity-visualization-design.zh.md b/prd-spec/features/studio-tool-activity-visualization/2026-09-12-studio-tool-activity-visualization-design.zh.md deleted file mode 100644 index 1df04f768..000000000 --- a/prd-spec/features/studio-tool-activity-visualization/2026-09-12-studio-tool-activity-visualization-design.zh.md +++ /dev/null @@ -1,276 +0,0 @@ -# Studio 工具活动可视化设计文档 - -- **状态:** 已实施并完成验证 -- **日期:** 2026-09-12 -- **变更类型:** 功能 -- **英文版本:** [2026-09-12-studio-tool-activity-visualization-design.md](2026-09-12-studio-tool-activity-visualization-design.md) -- **目标分支:** `feat/mpa-agent-oneclick-provision` - -## 1. 概述 - -### 1.1 问题 - -AgentKit Studio 已能接收结构化 ADK 工具调用和 mpa-agent 沙箱进度,但普通工具仍回退为格式化 JSON。一个沙箱命令还可能同时显示外层委托卡和内部 `exec_command`、`commandExecution` 行。用户能看到协议数据,却难以快速判断真正重要的信息:正在做什么、涉及什么命令或资源、已经产生什么输出、是否成功以及耗时多久。 - -当前实现已有可复用基础:`Block.kind === "tool"`、稳定 `callId` 匹配、`CodexSandboxActivity`、内置工具 registry,以及统一的 `eventsToTurns()` 历史投影。本功能必须扩展这些能力,不另建第二套 transcript 模型。 - -### 1.2 目标 - -- 用紧凑的 Codex 风格活动单元替换默认 JSON 优先展示。 -- 所有工具共享统一视觉外壳,命令、读取、搜索、文件变更、MCP 和通用工具使用类型化详情。 -- 按稳定身份将工具开始、输出、结果和错误合并成一个持续更新的单元。 -- 将沙箱内部活动扁平化到对话时间线,同时保留轻量 `Codex Sandbox` 来源标记。 -- 运行过程清晰可见、成功结果保持紧凑、失败状态可立即诊断。 -- 保证实时投影和持久历史重建产生等价的工具内容与顺序。 -- 不影响已有专用卡片、session 历史、搜索、反馈、附件、artifact、A2UI、并行 Agent 和最终回答。 - -### 1.3 非目标 - -- 修改 mpa-agent、A2A、ADK 事件协议或数据库 schema。 -- 将展开状态等纯展示状态写入 session。 -- 替换 `create_agents`、`branch_compare` 等已有专用 renderer。 -- 实现不受限制的 JSON 查看器,或暴露已被后端脱敏移除的值。 -- 在上游模型只返回最终结果时人为切片伪造流式输出。 -- 重做对话页、导航或 Studio 全局视觉语言。 - -## 2. 用户场景 - -### 场景 1:查看运行中的命令 - -**Given** 工具启动沙箱命令并持续输出, -**When** Studio 收到开始和输出事件, -**Then** 同一个展开的活动单元原位更新命令、耗时和最近输出,而不是不断新增 JSON 行。 - -### 场景 2:查看已完成历史 - -**Given** session 中持久化了 function call、response 或 Codex activity snapshot, -**When** 用户刷新 Studio 或重新切回该 session, -**Then** 系统重建相同顺序和内容的结构化活动单元,不产生重复调用,也不丢最终回答。 - -### 场景 3:诊断失败 - -**Given** 命令或工具执行失败, -**When** 收到终态事件或恢复历史, -**Then** 活动保持展开,并显示简明错误、状态或退出码、已产生的输出,以及二级原始数据入口。 - -### 场景 4:阅读工具密集型对话 - -**Given** Agent 连续执行多个相邻且成功的只读操作, -**When** 这些操作完成, -**Then** Studio 可将其收纳为一个“已探索 N 项”分组,同时保留每个子操作及其原始数据。 - -## 3. 功能需求 - -### FR-1 — 统一活动外壳 - -所有普通工具活动使用相同的头部结构:图标、明确状态标记、本地化动作标题、单行摘要、紧凑指标和展开入口。颜色只能辅助表达,必须同时提供文字或图标语义。 - -### FR-2 — 展示模型 - -工具展示必须经过纯函数 presentation 层派生。源 `Block` 保留原始 `args` 和 `response`;React 组件不得临时猜测任意 JSON 结构。展示模型包含类别、标题、摘要、状态、来源、耗时、退出码、类型化详情和原始值。 - -### FR-3 — 生命周期关联 - -相同稳定 `callId` 的开始、输出、结果和错误必须更新同一个活动单元。重复 `eventId` 必须忽略。结果先于调用到达时,在可安全关联时暂存;无法关联时显示独立的异常通用活动,不得静默丢弃。 - -### FR-4 — 自适应展开 - -- 排队中或运行中默认展开。 -- 成功后默认折叠。 -- 失败后默认保持展开。 -- 用户手动展开或收起后,后续事件不得覆盖用户选择。 -- 历史恢复按最终状态初始化,不持久化临时展开状态。 - -### FR-5 — 类型化详情 - -首期 presenter 包括: - -| 类型 | 头部摘要 | 展开内容 | -| --- | --- | --- | -| 命令执行 | 归一化后的命令 | 命令、工作目录、stdout/stderr、退出码、耗时 | -| 读取/列举 | 文件或资源数量 | 路径、行号或范围、省略提示 | -| 搜索 | 查询词和结果数量 | 命中路径或条目及摘要 | -| 文件变更 | 文件数量和 diff 统计 | 路径、增加/修改/删除动作、diff 摘要 | -| MCP | `server/tool` 和结果数量或状态 | 关键输入和结构化结果 | -| 授权 | 请求授权的工具或资源 | 授权说明与主操作按钮 | -| 通用 | 工具名称和尽力提取的摘要 | 可读键值信息 | - -授权继续使用现有专用交互,不降级为被动折叠行。已有专用工具 renderer 继续作为权威展示。 - -### FR-6 — Codex Sandbox 扁平化 - -`CodexSandboxActivity.items` 中的内部活动作为当前 assistant turn 的一级活动单元呈现。存在结构化子活动时,不再渲染第二层 `delegate_to_codex_sandbox` 大容器;使用轻量来源标记说明连续活动来自 `Codex Sandbox`。最终回答保持在活动分组之外。 - -### FR-7 — 安全只读聚合 - -同一 invocation、同一来源中,相邻且已成功完成的读取、列举、搜索或资源查询可聚合为“已探索 N 项”。只在完成后聚合。命令、写入、删除、MCP、授权、失败、取消、来源变化、invocation 变化和最终回答都会结束聚合。聚合只改变展示,不改变原始子项和顺序。 - -### FR-8 — 原始数据二级入口 - -每个活动的展开详情底部提供“查看原始数据”。输入和结果分区、延迟格式化、限制渲染规模,并支持分别复制输入、结果和调用 ID。默认关闭,且不得暴露上游 allowlist 或脱敏逻辑已经移除的数据。 - -### FR-9 — 输出处理 - -命令实时输出必须原位更新;完成后若存在权威聚合输出则替换预览。默认预览展示受限的头部和尾部,中间明确显示“省略 N 行”。完整输出使用有高度上限的独立滚动区。ANSI 或控制字符必须清理或安全渲染,禁止直接注入 HTML。 - -### FR-10 — 历史兼容 - -`eventsToTurns()` 保持为历史重建唯一入口。实时与持久事件使用同一归一化和展示函数。继续兼容 camelCase 与 snake_case。状态、耗时、来源或 call ID 缺失时使用安全降级。无法识别的旧工具仍显示通用活动。 - -### FR-11 — 响应式与无障碍 - -窄屏优先隐藏次要摘要,不隐藏标题或状态;详情铺满可用宽度,页面不得产生横向滚动。工具头部和二级折叠支持键盘、`aria-expanded`、清晰焦点和至少 44px 有效点击区。动效仅使用 120–180ms 的透明度或位移过渡,并响应 `prefers-reduced-motion`。 - -### FR-12 — 国际化 - -所有动作、状态、省略、复制、空态和错误文案进入现有 `conversation` namespace,并同时提供中英文。只有在具有明确技术价值或进入原始数据时才展示原始工具名。 - -## 4. 方案设计 - -### 4.1 数据流 - -```text -ADK / A2A event - -> 现有 applyEvent/eventsToTurns 投影 - -> 按 callId 关联工具生命周期 - -> 纯 ToolPresentation 派生 - -> 可选的只读展示聚合 - -> ToolActivityCard + 类型化详情 presenter -``` - -不改变传输和持久化契约。源 Block 继续保留,供调试和旧数据兼容使用。 - -### 4.2 建议模块 - -- `frontend/src/ui/tool-activity/model.ts`:展示类型、安全字段读取、归一化、摘要、输出截断和聚合判断。 -- `frontend/src/ui/tool-activity/ToolActivityCard.tsx`:统一头部、自适应折叠、复制操作、原始数据入口和无障碍语义。 -- `frontend/src/ui/tool-activity/ToolActivityDetails.tsx`:命令、读取、搜索、文件、MCP 和通用详情。 -- `frontend/src/ui/tool-activity/tool-activity.css`:使用现有语义 token 的局部布局与状态样式。 -- `frontend/src/blocks.ts`:最小化接入生命周期关联和沙箱扁平化,不放 JSX。 -- `frontend/src/ui/Blocks.tsx`:将普通工具交给新 presenter,同时保留已注册的专用 renderer。 -- `frontend/src/i18n/resources/{zh-CN,en-US}/conversation.json`:活动文案。 - -若模块内容很小,实施时可合并文件;但解析、状态归并和 React 展示必须能独立测试。 - -### 4.3 视觉规范 - -- 活动行沿用现有 32px 工具头部和 transcript 水平锚点。 -- 有效交互高度至少 44px,不要求视觉行本身变得厚重。 -- 状态使用中性灰、琥珀、绿色和 destructive 语义色,并同时显示明确文字。 -- 命令和输出使用现有等宽字体;标题、指标、ID 和普通摘要使用系统 UI 字体。 -- 成功折叠行保持轻边框或无边框;展开详情只使用一条轻量左侧引导线,不嵌套多层卡片边框。 -- 运行中更新预留指标区域,避免布局跳动。 -- 长标题省略并提供可访问的完整值;输出在自身边界内换行或滚动。 - -### 4.4 状态归属 - -工具生命周期数据归 transcript 投影管理;展开状态归每个渲染实例管理;只读聚合是相邻 Block 上的纯展示视图。任何展示状态都不写入 ADK session state 或服务端存储。 - -## 5. 历史安全契约 - -历史功能是 P0 发布门禁,必须全部满足: - -1. 刷新已完成 session 后,工具顺序、状态、详情和最终回答保持一致。 -2. session 切换不得共享展开状态或活动状态。 -3. 仅有持久化 `functionCall + functionResponse` 时也能重建完整活动,无需依赖实时 progress。 -4. 持久化 Codex snapshot 与实时 progress 不得生成重复子活动。 -5. 缺字段或旧版事件必须降级成通用活动,不得抛错或隐藏。 -6. 展示聚合不得修改源 blocks,也不得跨 invocation 或最终回答边界。 -7. 现有历史列表、搜索、反馈、附件、artifact、A2UI 和并行 Agent 行为保持不变。 - -如果某类事件无法保证实时和历史等价,首期必须保留该类型现有的通用 renderer,不得上线不完整的专用展示。 - -## 6. 边界情况 - -| 场景 | 处理方式 | -| --- | --- | -| 重复开始、输出或结果 | 按稳定事件身份去重,并更新同一个单元。 | -| 结果先于开始到达 | 按 call ID 暂存;无法关联时显示可见的未匹配结果。 | -| 缺少 call ID | 尽可能生成投影范围内的确定性身份;存在歧义时不合并。 | -| 同名工具并发执行 | 只按 call ID 关联,禁止仅按工具名合并。 | -| 超长或疑似二进制输出 | 限制预览、保留省略信息;可安全转成文本时才提供完整视图。 | -| 响应结构异常 | 显示“无法识别的工具结果”和原始数据入口。 | -| 运行中连接断开 | 保留已收到状态;刷新后从历史重建终态,不重新提交任务。 | -| 用户手动收起运行项 | 后续更新保持用户选择。 | -| 专用 renderer 异常 | 隔离到该活动并回退通用 presenter,不使整个 transcript 崩溃。 | -| 字段可能包含密钥 | 延续服务端脱敏,并在展示和复制前使用现有基于值的敏感信息遮罩。 | - -## 7. 测试与验收 - -### 7.1 单元测试 - -- 命令、读取/列举、搜索、文件变更、MCP、授权和通用 presenter 映射。 -- 开始/输出/结果/错误生命周期、重复事件、乱序事件和同名并发调用。 -- 头尾截断、省略行数、异常值和敏感字段遮罩。 -- 安全聚合边界与子项顺序保持。 -- 自适应展开状态及用户手动选择锁定。 - -### 7.2 历史回归测试 - -- 断言实时事件投影与 `eventsToTurns()` 历史重放得到等价的归一化活动内容。 -- 刷新包含旧 camelCase/snake_case 工具、缺失字段、Codex snapshot、附件、artifact、A2UI 和最终文本的 session。 -- 在两个 session 间反复切换,验证无状态泄漏和数据修改。 -- 现有历史、搜索、反馈、并行 Agent 和 Codex sandbox 测试必须原样通过。 - -### 7.3 组件与无障碍测试 - -- 运行、成功、失败、空结果、异常结果和长输出快照。 -- 键盘操作、焦点可见性、`aria-expanded`、可访问状态文案和复制反馈。 -- 验证 375px、768px、1024px、1440px,明暗主题和 reduced-motion。 -- 流式更新期间无页面横向滚动和布局跳动。 - -### 7.4 真实 E2E - -使用现有 mpa-agent 测试 Runtime: - -1. 执行带延时输出的命令,验证一个活动原位更新。 -2. 验证成功后折叠,最终回答保持独立。 -3. 执行失败命令,验证错误保持展开。 -4. 刷新两个 session,对比实时与历史内容和顺序。 -5. 并发运行两个 session,验证活动不串流。 -6. 验证旧版非流式 Runtime 历史仍可展示。 -7. 扫描原始数据视图及复制内容,确保无已配置密钥。 - -### 7.5 验收标准 - -- `AC-1`:所有已支持工具的主界面不展示原始 JSON。 -- `AC-2`:一个逻辑命令只生成一个持续更新的活动单元。 -- `AC-3`:运行自动展开、成功自动折叠、失败保持展开,并尊重用户手动选择。 -- `AC-4`:命令输出、退出状态、耗时和省略行数清楚可读且有界。 -- `AC-5`:只读工具只在已定义安全边界内聚合。 -- `AC-6`:原始输入、结果和 call ID 可通过二级折叠查看和复制。 -- `AC-7`:刷新后得到等价的结构化内容,并保留最终回答。 -- `AC-8`:已有专用卡片和非工具 transcript 内容无回归。 -- `AC-9`:键盘、焦点、读屏、窄屏、主题和 reduced-motion 验证通过。 -- `AC-10`:不得显示或复制凭证及未受限制的环境数据。 - -## 8. 交付顺序 - -1. 先补 presenter 和生命周期失败测试。 -2. 引入纯展示模型和类型化 presenter。 -3. 实现统一活动卡与自适应折叠。 -4. 扁平化 Codex sandbox 子活动并添加来源标记。 -5. 实现安全只读聚合。 -6. 实现带遮罩、大小限制和复制反馈的原始数据入口。 -7. 执行历史等价与前端全量回归测试。 -8. 执行真实 Runtime/Studio E2E 和视觉/无障碍验证。 - -## 9. 风险与缓解 - -- **历史回归:** 以实时/重放等价作为发布门禁,并保留通用 fallback。 -- **错误关联:** 优先使用 call ID;并发调用存在歧义时禁止按名称合并。 -- **渲染开销:** 使用纯函数派生、限制输出,并延迟格式化原始 JSON。 -- **视觉噪音:** 成功项折叠,只读活动安全聚合;失败和运行项保持可见。 -- **调试信息丢失:** 源 Block 保留原始值,通过二级折叠提供访问。 -- **敏感信息泄漏:** 保留后端 allowlist,并在展示和复制前增加基于值的遮罩。 - -## 10. 实施与验证记录 - -- **组件契约:** [Studio 工具活动](../../../specs/studio-tool-activity/README.zh.md) -- **已实现:** 纯工具展示模型、有界且脱敏的原始数据、自适应展开、命令别名生命周期关联、乱序响应保留、有界事件去重、Codex 子活动扁平化、来源标记,以及相邻只读活动聚合。现有已注册专用 renderer 保持不变。 -- **定向验证(2026-09-12):** 最终工具模型和组件定向复测通过,覆盖签名 URL 遮罩、显式 partial 失败、展开状态和分组后本地化文案;`npx tsc --noEmit` 通过;`npm run check:i18n` 通过。 -- **前端全量回归(2026-09-12):** `npm --prefix frontend test` 通过 1,078 项测试;`npm --prefix frontend run build` 通过;`npm --prefix frontend run test:webui-assets` 验证 102 个打包文件和 246 个内部引用;`uv run --extra dev pre-commit run --all-files` 通过。 -- **仓库回归:** `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"` 成功执行 4,030 项测试,但由于本地环境缺少可选的 `llama_index` 和 `anthropic` 包,最终有 6 项失败和 2 项收集错误;这些失败不在本次前端变更范围内。 -- **真实 Runtime 证据:** Runtime `r-yeuujrrcowb21078p9jh` 展示了一个原位更新的运行中命令活动。生命周期修复后重放捕获的 34 帧事件,得到一个完成态活动,退出码为 `0`,耗时为 `1601 ms`。 -- **浏览器证据:** 重建后的 Studio 已成功启动,`/web/ui-config` 返回预期的 Studio 配置。最终一次无头 Chrome 截图尝试卡住并已停止;此前完成的常规宽度截图和实时 DOM 检查仍作为视觉证据。 -- **已知传输限制:** 被测 A2A 虚拟 session 仅持久化最终文本,因此刷新能保留最终回答,但没有可重建的工具活动。Studio 不会伪造或重新提交缺失活动。 diff --git a/prd-spec/refactors/mpa-contract-location/2026-10-14-studio-owned-resources.md b/prd-spec/refactors/mpa-contract-location/2026-10-14-studio-owned-resources.md deleted file mode 100644 index 5f4b38d9a..000000000 --- a/prd-spec/refactors/mpa-contract-location/2026-10-14-studio-owned-resources.md +++ /dev/null @@ -1,61 +0,0 @@ -# Relocate MPA compatibility resources under Studio - -[中文版](2026-10-14-studio-owned-resources.zh.md) - -- Change ID: `mpa-contract-location`; date: 2026-10-14; status: implemented. -- Contract: [Studio MPA control plane](../../../specs/studio-mpa-control-plane/README.md). - -## Evidence, requirements and non-goals - -The root `contracts/mpa-p0` contains only `compatibility-matrix.json` and `benchmark-report.schema.json`. Studio and CLI share the evaluator in `veadk.cli.mpa_p0_contract`; its default loader uses the root matrix when present and otherwise the identical embedded rules. Integration tests and documented `--matrix` examples reference the root file. The benchmark schema has no discovered runtime caller. Studio release staging copies the frontend Python tree, but setuptools currently has no package-data declaration for the new JSON path. Existing unrelated `frontend/package-lock.json` edits must remain byte-identical. - -FR-1: Move both JSON files unchanged to `frontend/server/mpa/contracts/mpa-p0/` and remove the empty root directory. Do not change rule/schema values, decision outcomes, MPA/general-agent behavior or OpenViking behavior. -FR-2: Update `default_matrix_path()` and integration tests/documented commands. Keep public function signatures, the required custom `--matrix` argument and embedded fallback unchanged. Missing packaged files still use embedded rules; malformed present JSON continues to fail visibly. Existing callers explicitly using the old root file path must use the new path. -FR-3: Include both JSON resources in the Python wheel and Studio source staging; access them by sibling filesystem paths without importing the heavy frontend composition package. Verify source and an isolated extracted wheel resolve the same rules. - -Non-goals: moving evaluator ownership, changing SDK/version rules, removing compatibility preflight, deleting OpenSpec/PRD/spec directories, changing UI or cloud resources. No commit, push or deployment is requested. - -## Design, files and tasks - -Retain `mpa-p0` nesting. Compute the default path from the evaluator's installed source root to frontend/server/mpa; add the precise existing-resource package-data glob to `frontend.server` in pyproject.toml. Studio's copytree staging already includes that directory and needs no production change. Update bilingual current design/validation examples and append a relocation note to the maintained control-plane contract. - -T-1 / AC-1: Write a failing path/source-loading regression first and exercise normal compatibility, invalid manifest and rejection before writes. Move both resources byte-for-byte and update the path. -T-2 / AC-2: Test missing fallback and malformed-file failure; update CLI examples/tests without changing explicit custom-file behavior. -T-3 / AC-3: Add a real source-staging assertion for both resources; build a local wheel and inspect/extract it to verify JSON presence and default-loader resolution. No publication occurs. -T-4 / AC-4: Targeted compatibility/CLI/release checks, changed Python Ruff/Pyright, paired documents/links, whitespace and secret checks. Record actual evidence and preserve unrelated local changes. - -## Review, risks and authorization - -Direct review replaces unavailable review-spec: requirements, ownership, consumer paths, filesystem/wheel behavior, error semantics, no-new-import boundary and paired languages are consistent. The user approved the exact whole-directory migration on 2026-10-14 with “按此方案迁移(推荐)”. Changing the file location is intentional; custom CLI argument semantics stay stable. No runtime concurrency, authorization, credential or cloud API changes occur. Both language versions and the maintained contract must be updated together. Main risk is omitted package data; wheel extraction is the acceptance check. Implementation and tests are authorized; approval does not authorize commit/push/deployment. - -## Verification record - -Execution scope starts at `2ffe6e14` plus this change; outcomes are recorded below. Live cloud/browser/sidecar smoke is not_applicable because only internal static-resource location changes. No TypeScript or rendered UI changes are planned, so frontend rebuild and browser interaction tests are not applicable. - -### Completed verification — 2026-10-14 - -Scope: `2ffe6e14` plus this local migration. T-1–T-4 / AC-1–AC-4 completed. Test-first result was 6 failed / 4 passed before moving files/changing the loader. After implementation: - -| Check | Result | Evidence / limitation | -| --- | --- | --- | -| Targeted compatibility tests | pass | 10 tests, including new location, both source-staged JSON files, missing fallback and visible malformed-file errors. | -| Related compatibility, Studio RBAC/GitHub, release and startup regression | pass | 376 tests with two workers; cloud/provider calls mocked. Pre-mutation compatibility rejection and general-agent paths remain covered. | -| Changed Python Ruff/format/Pyright | pass | Repository Ruff 0.11.12; Pyright uses repository Python, 0 errors/warnings. Temporary uvx tools, no global configuration changes. | -| Standard wheel and actual Studio-staged wheel | pass | Both built locally. Isolated extraction finds the two JSON files at the new path, byte-identical to HEAD originals; default loader reads the packaged matrix and returns compatible for the existing baseline. Real stage_studio_wheel_source excludes node_modules/src and includes the resources. Nothing uploaded/installed globally. | -| Review / documents / secret / whitespace | pass | Paired identifiers/commands/relative links reviewed; repository Gitleaks rules on this changed-file scope; git diff whitespace. No credentials added. | -| Existing local changes | pass | Unrelated frontend/package-lock.json SHA-256 unchanged. | -| Full repository regression / frontend/browser / live cloud / sidecar smoke | not_run / not_applicable | Focused regression covers every known path consumer; no further shared logic, TypeScript, UI, cloud or harness protocol change. No external cloud effects exercised. | -| Commit / push / deployment | not_run | Not requested or authorized for this migration. | - -Final review found no additional behavior, import, ownership or packaging change. Explicit old-root CLI paths must use the new path as approved; custom matrix options and builtin fallback stay compatible. Actual commands (MPA_STAGE_ROOT is the temporary source returned by real Studio staging): - -```bash -uv run --extra dev pytest tests/integrations/test_mpa_compatibility_manifest.py -q -uv run --extra dev pytest -n 2 -q tests/integrations/test_mpa_compatibility_manifest.py tests/cli/test_studio_rbac.py tests/cli/test_github_cicd.py tests/cli/test_studio_release.py tests/test_studio_release_server.py tests/cli/test_studio_startup_imports.py -uvx --from ruff==0.11.12 ruff check veadk/cli/mpa_p0_contract.py tests/integrations/test_mpa_compatibility_manifest.py -uvx --from ruff==0.11.12 ruff format --check veadk/cli/mpa_p0_contract.py tests/integrations/test_mpa_compatibility_manifest.py -uvx pyright --pythonpath .venv/bin/python veadk/cli/mpa_p0_contract.py tests/integrations/test_mpa_compatibility_manifest.py -uv build --wheel --out-dir /tmp/veadk-mpa-contract-wheel-20261014 -uv build "$MPA_STAGE_ROOT" --wheel --out-dir /tmp/veadk-mpa-contract-release-wheel-20261014 -git diff --check -``` diff --git a/prd-spec/refactors/mpa-contract-location/2026-10-14-studio-owned-resources.zh.md b/prd-spec/refactors/mpa-contract-location/2026-10-14-studio-owned-resources.zh.md deleted file mode 100644 index 954cc7468..000000000 --- a/prd-spec/refactors/mpa-contract-location/2026-10-14-studio-owned-resources.zh.md +++ /dev/null @@ -1,61 +0,0 @@ -# 将 MPA 兼容性资源迁入 Studio 目录 - -[English](2026-10-14-studio-owned-resources.md) - -- Change ID:`mpa-contract-location`;日期:2026-10-14;状态:implemented。 -- 契约:[Studio MPA 控制面](../../../specs/studio-mpa-control-plane/README.zh.md)。 - -## 证据、需求与非目标 - -根目录 `contracts/mpa-p0` 仅有 `compatibility-matrix.json` 和 `benchmark-report.schema.json`。Studio 与 CLI 共用 `veadk.cli.mpa_p0_contract` 评估器;默认加载器优先读取根目录文件,不存在时使用完全一致的内置规则。集成测试和文档 `--matrix` 示例直接引用根文件。未发现性能报告 schema 的运行时调用方。Studio 发布暂存会复制 frontend Python 树,但 setuptools 目前未声明新路径 JSON 的 package-data。已有无关 `frontend/package-lock.json` 改动须保持字节一致。 - -FR-1:将两个 JSON 原样移至 `frontend/server/mpa/contracts/mpa-p0/`,移除空根目录。不改变规则/schema 内容、检查结果、MPA/通用智能体或 OpenViking 行为。 -FR-2:更新 `default_matrix_path()`、集成测试及文档命令。保留公开函数签名、必填自定义 `--matrix` 参数和内置备用规则。包内文件缺失时仍使用内置规则,存在但格式错误时仍明确失败。明确指定旧根路径的调用方需改用新路径。 -FR-3:Python wheel 及 Studio 源码暂存必须包含两个 JSON;通过相邻文件系统路径读取,不导入较重的 frontend 组合包。验证源码环境与隔离解包 wheel 读取相同规则。 - -非目标:迁移评估器职责、更改 SDK/版本规则、移除兼容性检查、删除 OpenSpec/PRD/spec 目录、更改 UI 或云资源。未请求提交、推送或部署。 - -## 设计、文件与任务 - -保留 `mpa-p0` 层级。从已安装评估器源码根定位 frontend/server/mpa;在 pyproject.toml 的 `frontend.server` package-data 中加入精确资源 glob。Studio copytree 暂存已覆盖该目录,无需改生产实现。同步双语当前设计/验证示例,在维护中的控制面契约追加迁移说明。 - -T-1 / AC-1:先写默认路径/源码加载失败回归,覆盖正常兼容、无效 manifest 和写入前拒绝。逐字节移动资源并更新路径。 -T-2 / AC-2:验证缺文件备用规则及存在但格式错误时失败;更新 CLI 示例/测试,不改显式自定义文件语义。 -T-3 / AC-3:增加两个资源真实源码暂存断言;构建本地 wheel,检查/解包验证 JSON 和默认加载路径,不发布。 -T-4 / AC-4:定向兼容性/CLI/发布检查、改动 Python Ruff/Pyright、双语文档/链接、空白及密钥检查。记录实际证据,保留无关本地改动。 - -## 审查、风险与授权 - -review-spec 不可用,直接审查确认需求、职责、调用方路径、文件系统/wheel、错误语义、不新增 import 边界和双语一致。用户于 2026-10-14 对完整目录迁移明确回复“按此方案迁移(推荐)”。文件位置变化属于预期,自定义 CLI 参数语义不变。不改变运行时并发、权限、凭据或云接口。两种语言及维护契约同步更新。主要风险为漏打包,以 wheel 解包验收。已授权实现/测试,设计批准不包含提交/推送/部署。 - -## 验证记录 - -执行范围从 `2ffe6e14` 加本次差异开始,结果记录如下。真实云/浏览器/sidecar smoke 为 not_applicable,仅改变内部静态资源位置。不计划 TypeScript/界面变更,前端重建及浏览器交互不适用。 - -### 已完成验证 — 2026-10-14 - -范围:`2ffe6e14` 加本地迁移。T-1–T-4 / AC-1–AC-4 完成。移动文件/改加载器前先写回归得到 6 failed / 4 passed。实现后: - -| 检查 | 结果 | 证据 / 限制 | -| --- | --- | --- | -| 定向兼容性测试 | pass | 10 项,含新位置、两个源码暂存 JSON、缺文件备用及格式错误明确失败。 | -| 相关兼容性、Studio RBAC/GitHub、发布及启动回归 | pass | 两个 worker 共 376 项;云/平台调用使用模拟。仍覆盖写入前兼容性拒绝及通用智能体路径。 | -| 改动 Python Ruff/格式/Pyright | pass | 仓库 Ruff 0.11.12;Pyright 使用仓库 Python,0 错误/警告。临时 uvx 工具,未改全局配置。 | -| 普通 wheel 与实际 Studio 暂存 wheel | pass | 均本地构建。隔离解包找到新路径下两个 JSON,与 HEAD 原文件逐字节相同;默认加载器读取包内矩阵,原基线返回兼容。真实 stage_studio_wheel_source 排除 node_modules/src 并包含资源。未上传或全局安装。 | -| 审查 / 文档 / 密钥 / 空白 | pass | 审查双语标识符/命令/相对链接;按仓库 Gitleaks 规则扫描本次变更范围;git diff 空白。未增加凭据。 | -| 已有本地修改 | pass | 无关 frontend/package-lock.json SHA-256 未变。 | -| 全仓回归 / 前端浏览器 / 真实云 / sidecar smoke | not_run / not_applicable | 定向回归覆盖所有已知路径调用方;未改其他共享逻辑、TypeScript、UI、云或 harness 协议,未执行真实云操作。 | -| 提交 / 推送 / 部署 | not_run | 本次迁移未请求或授权。 | - -最终审查没有发现额外行为、import、职责或打包变化。明确指定旧根 CLI 路径须按批准方案改用新路径;自定义矩阵参数与内置备用保持兼容。实际命令(MPA_STAGE_ROOT 为真实 Studio 暂存返回的临时源码目录): - -```bash -uv run --extra dev pytest tests/integrations/test_mpa_compatibility_manifest.py -q -uv run --extra dev pytest -n 2 -q tests/integrations/test_mpa_compatibility_manifest.py tests/cli/test_studio_rbac.py tests/cli/test_github_cicd.py tests/cli/test_studio_release.py tests/test_studio_release_server.py tests/cli/test_studio_startup_imports.py -uvx --from ruff==0.11.12 ruff check veadk/cli/mpa_p0_contract.py tests/integrations/test_mpa_compatibility_manifest.py -uvx --from ruff==0.11.12 ruff format --check veadk/cli/mpa_p0_contract.py tests/integrations/test_mpa_compatibility_manifest.py -uvx pyright --pythonpath .venv/bin/python veadk/cli/mpa_p0_contract.py tests/integrations/test_mpa_compatibility_manifest.py -uv build --wheel --out-dir /tmp/veadk-mpa-contract-wheel-20261014 -uv build "$MPA_STAGE_ROOT" --wheel --out-dir /tmp/veadk-mpa-contract-release-wheel-20261014 -git diff --check -``` diff --git a/prd-spec/refactors/mpa-upstream-reconciliation/2026-10-15-byteplus-rebase.md b/prd-spec/refactors/mpa-upstream-reconciliation/2026-10-15-byteplus-rebase.md deleted file mode 100644 index 1c78b58d5..000000000 --- a/prd-spec/refactors/mpa-upstream-reconciliation/2026-10-15-byteplus-rebase.md +++ /dev/null @@ -1,37 +0,0 @@ -# BytePlus change reconciliation after upstream history consolidation - -[中文版](2026-10-15-byteplus-rebase.zh.md) - -- Date: 2026-10-15 -- Scope: reconcile original local commit `078602ec` onto remote branch base `08197629`. -- Authorization: the user requested resolving rebase conflicts and committing the result. -- Status: reconciled; the unchanged macOS MIME regression failure is recorded below. - -## Evidence and approach - -The interrupted rebase replayed seven commits, starting with `7f2ae0f8`. The remote base already consolidated the six historical lifecycle commits, including generic/MPA deployment separation, deletion, automatic OV creation, maximum instances, dependency-lock repair and license headers. Replaying them caused redundant documentation/test-script conflicts and generated-asset rename conflicts. The original tip and interrupted state were backed up before aborting that rebase. Rebase was restarted with `--onto 08197629 2ffe6e14`, replaying only the local BytePlus commit. - -## Preserved behavior and affected files - -Retain upstream TOS mounting configuration, image-selection rules and `ensure_tos_mount_permissions` together with local BytePlus OV provisioning, IAM transient diagnostics, Runtime diagnostics, original-request name recovery, provider-specific default images and server-side APM AppKey injection. Resolve `names.py` using the approved local Worker provenance check: matching `MPA_AGENT_ID` environment plus canonical `managed_by` and `mpa_agent_key` tags; contradictory ownership cannot authorize retry. Runtime tagless recovery remains limited to the authenticated original creation account. Retain the complete remote `frontend/package-lock.json`, since no package dependency change was requested. Regenerate `veadk/webui` from merged frontend source; never manually combine minified bundles. - -Four Pyright findings in existing upstream test doubles were resolved with non-null assertions in `test_iam.py` and `test_service.py`. These assertions do not change production behavior. Shared database/network resources and deployed Runtimes are outside scope. Existing component contracts are unchanged; their already approved bilingual BytePlus additions are preserved. No new public API, provider permission, dependency or cloud mutation is introduced by this reconciliation. - -## Review and acceptance - -Direct review substitutes for the unavailable review-spec skill. History scope, ownership checks, provider separation, TOS/APM ordering, complete lock data, generated assets, test coverage and paired documentation were reviewed. Acceptance requires a single local feature commit above the remote base, no conflict markers, preservation of both upstream TOS and local BytePlus behavior, regenerated assets and the checks below. Live Worker dispatch, OV data-plane compatibility and overseas tracing remain separate Runtime/image work; local tests cannot establish those outcomes. - -## Verification (2026-10-15) - -- pass: targeted conflict/integration tests, 383 passed; test-only assertion follow-up, 204 passed. -- pass: `npm --prefix frontend ci --ignore-scripts --no-audit --no-fund` using the complete lock file. -- pass: `npm --prefix frontend test`, 1,389 Node tests and 111 component tests. -- pass: `npm --prefix frontend run build`, TypeScript and both production bundles. -- pass: `npm --prefix frontend run check:i18n` and `npm --prefix frontend run test:webui-assets`, 113 files and 350 internal references. -- pass: Pyright on 25 changed Python files after the four test-only assertion corrections. -- pass: `uv run --extra dev pre-commit run --all-files`, initial and final staged-source runs; documentation-only final repeat follows this record. -- fail: `uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`, 7,441 passed, 1 failed, 53 skipped, 4 xfailed in 509.32 seconds. Two workers limit local resource pressure. The sole failure is `tests/frontend/server/runtime_artifacts/test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes`: this macOS Python reports no MIME for `.md`, so unchanged code emits `application/octet-stream` instead of `text/markdown`. Writer, MIME helper and test are identical to remote base `08197629`; the isolated unmodified test file reproduces the failure (48 passed, 1 failed). No unrelated production change or global MIME configuration was made. -- pass: a separate process adding only the expected `text/markdown` mapping for `.md` runs all 49 artifact-writer tests successfully. This controlled environment check does not replace the recorded full-suite failure. -- pass: `uv build --wheel --out-dir /tmp/veadk-rebase-wheel`; the new BytePlus modules and relocated contract JSONs are present, `.env` is absent. All seven newly added local Python modules remain byte-identical to `078602ec`. -- blocked: existing Studio process caches pre-build HTML referencing removed assets, yielding HTTP 404. Browser opened the page but could not exercise the creation dialog. The active user service was not restarted; restart is needed to load the rebuilt HTML. -- not_run: real cloud creation/deletion and Runtime smoke; no cloud writes are authorized by this Git reconciliation task. diff --git a/prd-spec/refactors/mpa-upstream-reconciliation/2026-10-15-byteplus-rebase.zh.md b/prd-spec/refactors/mpa-upstream-reconciliation/2026-10-15-byteplus-rebase.zh.md deleted file mode 100644 index 5d238ce63..000000000 --- a/prd-spec/refactors/mpa-upstream-reconciliation/2026-10-15-byteplus-rebase.zh.md +++ /dev/null @@ -1,37 +0,0 @@ -# 上游历史整理后的 BytePlus 改动协调 - -[English](2026-10-15-byteplus-rebase.md) - -- 日期:2026-10-15 -- 范围:将原本地提交 `078602ec` 协调到远端分支基础 `08197629`。 -- 授权:用户要求解决 rebase 冲突并提交结果。 -- 状态:协调完成;未改动的 macOS MIME 回归失败如实记录如下。 - -## 证据与处理方式 - -中断的 rebase 从 `7f2ae0f8` 开始重放七个提交。远端基础已整理了六个历史生命周期提交,包括通用/MPA 部署隔离、删除、OV 自动创建、最大实例数、依赖锁修复和许可证头。再次重放导致重复的文档、测试脚本冲突和构建产物重命名冲突。终止该 rebase 前已备份原分支顶端及中断状态。通过 `--onto 08197629 2ffe6e14` 重启 rebase,仅重放本地 BytePlus 提交。 - -## 保留行为与涉及文件 - -同时保留上游的 TOS 挂载配置、镜像选择规则和 `ensure_tos_mount_permissions`,以及本地 BytePlus OV 创建、IAM 临时错误诊断、Runtime 诊断、原请求名称恢复、分提供商默认镜像和服务端 APM AppKey 注入。`names.py` 使用已批准的本地 Worker 归属检查:`MPA_AGENT_ID` 环境变量匹配,同时核对标准 `managed_by` 和 `mpa_agent_key` 标签;矛盾归属不能授权重试。Runtime 无标签恢复仍只限已认证的原创建账号。保留远端完整的 `frontend/package-lock.json`,因为此次没有请求改变包依赖。从合并后的前端源码重新生成 `veadk/webui`,不手动拼接压缩包。 - -上游测试替身中的四处 Pyright 问题通过在 `test_iam.py`、`test_service.py` 增加非空断言解决;这些断言不改变生产行为。共享数据库/网络和已部署 Runtime 不在范围内。现有组件契约不变,保留其已批准的双语 BytePlus 补充内容。本次协调不新增公共 API、提供商权限、依赖或云端写操作。 - -## 审查与验收 - -review-spec skill 不可用,直接完成等价审查。已检查历史范围、归属校验、提供商隔离、TOS/APM 顺序、完整锁数据、构建产物、测试覆盖和双语文档。验收要求远端基础上只有一个本地功能提交、无冲突标记、上游 TOS 与本地 BytePlus 行为均保留、产物已重建并完成下列检查。真实 Worker 派发、OV 数据面兼容和海外观测仍是单独的 Runtime/镜像联调,本地测试不能证明这些结果。 - -## 验证(2026-10-15) - -- pass:冲突及集成定向测试 383 项通过;测试断言修正后复验 204 项通过。 -- pass:`npm --prefix frontend ci --ignore-scripts --no-audit --no-fund`,使用完整锁文件。 -- pass:`npm --prefix frontend test`,1,389 项 Node 测试和 111 项组件测试通过。 -- pass:`npm --prefix frontend run build`,TypeScript 和两份生产构建通过。 -- pass:`npm --prefix frontend run check:i18n`、`npm --prefix frontend run test:webui-assets`,113 个文件、350 个内部引用通过。 -- pass:四处测试非空断言修正后,对 25 个改动 Python 文件运行 Pyright。 -- pass:`uv run --extra dev pre-commit run --all-files`,首次及最终暂存源码检查通过;本记录完成后再执行仅文档收尾复验。 -- fail:`uv run --extra dev pytest -n 2 -m "not codex_smoke and not piagent_smoke"`,7,441 项通过、1 项失败、53 项跳过、4 项 xfailed,用时 509.32 秒。使用两个 worker 限制本地资源压力。唯一失败为 `tests/frontend/server/runtime_artifacts/test_writer.py::test_success_uses_trusted_scope_and_hashes_utf8_bytes`:这台 macOS 的 Python 无法识别 `.md` MIME,未改动的代码返回 `application/octet-stream` 而不是 `text/markdown`。Writer、MIME 辅助函数和测试均与远端基础 `08197629` 相同;单独执行原测试文件可复现(48 项通过、1 项失败)。没有改动无关生产代码或机器全局 MIME 配置。 -- pass:仅在单独进程为 `.md` 增加预期的 `text/markdown` 映射后,49 项产物写入测试全部通过;该受控环境检查不能替代已记录的完整回归失败。 -- pass:`uv build --wheel --out-dir /tmp/veadk-rebase-wheel`;新增 BytePlus 模块、迁移后的 contract JSON 均存在,`.env` 不在包内。本地七个新增 Python 模块均与 `078602ec` 逐字节一致。 -- blocked:现有 Studio 进程缓存重建前的 HTML,引用已移除资源并返回 HTTP 404。浏览器打开页面后无法验证创建弹窗。没有重启用户正在使用的服务;需重启才能加载重建后的 HTML。 -- not_run:真实云端创建/删除与 Runtime smoke;本次 Git 协调任务未授权云端写操作。 diff --git a/scripts/benchmark_codex_tools.py b/scripts/benchmark_codex_tools.py new file mode 100644 index 000000000..8ce7ce0d8 --- /dev/null +++ b/scripts/benchmark_codex_tools.py @@ -0,0 +1,405 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Real Codex tool-loop benchmark with a scripted loopback model. + +No provider credentials or tokens are used. Run from the checkout with +``uv run --extra dev python scripts/benchmark_codex_tools.py --scenario parallel``. +Compare ``--pool-size 0`` and ``20``. Model latency here is simulated; report +these mechanism measurements separately from any real-model benchmark. +""" + +from __future__ import annotations + +import argparse +import asyncio +import contextlib +import json +import os +import threading +import time +import uuid +from collections.abc import Awaitable, Callable +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from typing import Any + + +async def bounded_batch_lookup( + keys: list[str], + lookup: Callable[[str], Awaitable[dict]], + *, + max_size: int = 8, + item_timeout: float = 10.0, +) -> dict: + """Application example: bounded independent reads with explicit item errors. + + The lookup must authorize each key. One batch is one SDK tool invocation; + the application owns the separate business-read bound. Dependent reads + and writes must not be automatically combined into this operation. + """ + if not keys or len(keys) > max_size or max_size < 1 or item_timeout <= 0: + raise ValueError("the batch must contain between one and max_size keys") + + async def one(key: str) -> dict: + try: + result = await asyncio.wait_for(lookup(key), item_timeout) + return {"key": key, "status": "completed", "result": result} + except Exception as error: + return {"key": key, "status": "error", "error_type": type(error).__name__} + + results = await asyncio.gather(*(one(key) for key in keys)) + return { + "status": "completed" + if all(r["status"] == "completed" for r in results) + else "partial_failure", + "results": results, + } + + +class _Model: + """Script only the tool ordering; Codex executes the real tool protocol.""" + + def __init__(self, scenario: str, delay: float): + self.scenario, self.delay = scenario, delay + self.turn, self.request = 0, 0 + self.rows: list[dict[str, Any]] = [] + backend = self + + class Handler(BaseHTTPRequestHandler): + protocol_version = "HTTP/1.1" + + def log_message(self, format: str, *args: Any): + pass + + def do_GET(self): + self._send("application/json", b'{"data":[],"models":[]}') + + def do_POST(self): + raw = self.rfile.read(int(self.headers.get("content-length", 0))) + body = json.loads(raw) + backend.request += 1 + messages = [ + i for i in body.get("input", []) if i.get("type") == "message" + ] + backend.rows.append( + { + "request_bytes": len(raw), + "developer_messages": sum( + i.get("role") == "developer" for i in messages + ), + "reset_warnings": sum( + "your working directory was reset" in p.get("text", "") + for i in messages + for p in i.get("content", []) + ), + } + ) + time.sleep(backend.delay) + keys: list[str] = [] + name = "batch_lookup" if backend.scenario == "batch" else "mock_lookup" + if backend.request == 1: + keys = ( + ["alpha", "beta"] + if backend.scenario == "parallel" + else ["alpha"] + ) + elif backend.scenario == "serial" and backend.request == 2: + keys = ["beta"] + items = [] + if keys: + namespace = next( + t["name"] + for t in body.get("tools", []) + if t.get("type") == "namespace" + and any(x.get("name") == name for x in t.get("tools", [])) + ) + for key in keys: + call_id = f"call_{backend.turn}_{key}" + args = ( + {"keys": ["alpha", "beta"]} + if backend.scenario == "batch" + else {"key": key} + ) + items.append( + { + "type": "function_call", + "id": "fc_" + call_id, + "call_id": call_id, + "namespace": namespace, + "name": name, + "arguments": json.dumps(args), + "status": "completed", + } + ) + else: + current_results = "\n".join( + str(i.get("output", "")) + for i in body.get("input", []) + if i.get("type") == "function_call_output" + and str(i.get("call_id", "")).startswith( + f"call_{backend.turn}_" + ) + ) + expected = ( + ["alpha"] if backend.scenario == "single" else ["alpha", "beta"] + ) + backend.rows[-1]["results_visible"] = all( + f"MOCK-{key}" in current_results for key in expected + ) + items.append( + { + "type": "message", + "id": "msg_" + uuid.uuid4().hex, + "role": "assistant", + "status": "completed", + "content": [ + { + "type": "output_text", + "text": f"ok-{backend.turn}", + "annotations": [], + } + ], + } + ) + response_id = "resp_" + uuid.uuid4().hex + events = [{"type": "response.created", "response": {"id": response_id}}] + events.extend( + { + "type": "response.output_item.done", + "output_index": i, + "item": item, + } + for i, item in enumerate(items) + ) + events.append( + { + "type": "response.completed", + "response": { + "id": response_id, + "usage": { + "input_tokens": 1, + "output_tokens": 1, + "total_tokens": 2, + "input_tokens_details": {"cached_tokens": 0}, + "output_tokens_details": {"reasoning_tokens": 0}, + }, + }, + } + ) + self._send( + "text/event-stream", + b"".join( + f"event: {e['type']}\ndata: {json.dumps(e)}\n\n".encode() + for e in events + ), + ) + + def _send(self, content_type, raw): + self.send_response(200) + self.send_header("content-type", content_type) + self.send_header("content-length", str(len(raw))) + self.end_headers() + self.wfile.write(raw) + + self.server = ThreadingHTTPServer(("127.0.0.1", 0), Handler) + self.server.daemon_threads = True + self.worker = threading.Thread(target=self.server.serve_forever, daemon=True) + self.worker.start() + self.url = f"http://127.0.0.1:{self.server.server_address[1]}/v1" + + def close(self): + self.server.shutdown() + self.server.server_close() + self.worker.join(timeout=2) + + +async def run_case( + scenario: str, turns: int, pool_size: int, tool_delay: float, model_delay: float +) -> dict: + """Run isolated synthetic sessions, preserving normal SDK tool behavior.""" + from google.adk.runners import Runner + from google.adk.sessions import InMemorySessionService + from google.genai import types + from veadk import Agent + from veadk.runtime.codex import runtime, metrics + from veadk.runtime.codex.config import CodexRuntimeConfig + from veadk.runtime.codex.mcp_bridge import get_bridge + + await runtime.shutdown_codex_runtime() + backend = _Model(scenario, model_delay) + previous_pool = os.environ.get("VEADK_CODEX_PROCESS_POOL_SIZE") + os.environ["VEADK_CODEX_PROCESS_POOL_SIZE"] = str(pool_size) + outcomes = [] + original_resume = metrics.record_resume + + def resume(outcome): + outcomes.append(outcome) + original_resume(outcome) + + metrics.record_resume = resume + reads: list[str] = [] + active, peak = 0, 0 + + async def mock_lookup(key: str) -> dict: + """Look up a synthetic receipt; independent read with no business service.""" + nonlocal active, peak + if key not in {"alpha", "beta"}: + raise PermissionError("key is outside this synthetic session") + active += 1 + peak = max(peak, active) + try: + await asyncio.sleep(tool_delay) + reads.append(key) + return {"receipt": f"MOCK-{key}"} + finally: + active -= 1 + + async def batch_lookup(keys: list[str]) -> dict: + """Look up at most eight independent synthetic keys, authorizing each.""" + return await bounded_batch_lookup(keys, mock_lookup) + + rows = [] + bridge = None + try: + service = InMemorySessionService() + sid = "tool-bench-" + uuid.uuid4().hex + await service.create_session( + app_name="tool_benchmark", user_id="synthetic", session_id=sid + ) + agent = Agent( + name="tool_bench", + runtime="codex", + instruction="Synthetic tool workload. Answer briefly.", + model_name="scripted-tool-model", + model_api_base=backend.url, + model_api_key="synthetic-model-key", + model_api_key_name="", + tools=[batch_lookup] if scenario == "batch" else [mock_lookup], + codex_runtime_config=CodexRuntimeConfig( + model_transport="direct", + thread_mode="resume", + reasoning_effort="minimal", + turn_timeout_seconds=30, + tool_timeout_seconds=10, + ), + ) + runner = Runner(app_name="tool_benchmark", agent=agent, session_service=service) + for index in range(1, turns + 1): + backend.turn, backend.request = index, 0 + start_requests, start_reads, start_outcomes = ( + len(backend.rows), + len(reads), + len(outcomes), + ) + calls, text = set(), [] + start = time.perf_counter() + + async def consume(): + stream = runner.run_async( + user_id="synthetic", + session_id=sid, + new_message=types.Content( + role="user", parts=[types.Part(text=f"synthetic turn {index}")] + ), + ) + async with contextlib.aclosing(stream): + async for event in stream: + calls.update( + (c.id, c.name) for c in event.get_function_calls() or [] + ) + if event.content and not event.partial: + text.extend( + p.text + for p in event.content.parts or [] + if p.text and not p.thought + ) + + await asyncio.wait_for(consume(), 35) + requests = backend.rows[start_requests:] + expected_reads = ["alpha"] if scenario == "single" else ["alpha", "beta"] + rows.append( + { + "turn": index, + "total_ms": (time.perf_counter() - start) * 1000, + "model_requests": len(requests), + "tool_calls": len(calls), + "reuse_outcome": outcomes[start_outcomes:][-1], + "request_bytes": requests[0]["request_bytes"], + "developer_messages": requests[0]["developer_messages"], + "reset_warnings": requests[0]["reset_warnings"], + "correct": sorted(reads[start_reads:]) == expected_reads + and text[-1:] == [f"ok-{index}"] + and requests[-1].get("results_visible", False), + } + ) + bridge = await get_bridge() + finally: + await runtime.shutdown_codex_runtime() + backend.close() + metrics.record_resume = original_resume + if previous_pool is None: + os.environ.pop("VEADK_CODEX_PROCESS_POOL_SIZE", None) + else: + os.environ["VEADK_CODEX_PROCESS_POOL_SIZE"] = previous_pool + return { + "scenario": scenario, + "pool_size": pool_size, + "model": "scripted loopback, no provider tokens", + "tool_delay_ms": tool_delay * 1000, + "model_delay_ms": model_delay * 1000, + "rows": rows, + "peak_concurrency": peak, + "cleanup": { + "pool_idle": len(runtime._PROCESS_POOL._idle), + "bridge_processes": len(bridge._processes) if bridge else 0, + }, + } + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--scenario", + choices=["single", "parallel", "serial", "batch"], + default="single", + ) + parser.add_argument("--turns", type=int, choices=range(1, 21), default=5) + parser.add_argument("--pool-size", type=int, choices=[0, 20], default=20) + parser.add_argument("--tool-delay-ms", type=float, default=50) + parser.add_argument("--model-delay-ms", type=float, default=0) + parser.add_argument("--output", type=Path) + args = parser.parse_args() + if args.tool_delay_ms < 0 or args.model_delay_ms < 0: + parser.error("delays must be non-negative") + os.environ["LOGGING_LEVEL"] = "CRITICAL" + report = asyncio.run( + run_case( + args.scenario, + args.turns, + args.pool_size, + args.tool_delay_ms / 1000, + args.model_delay_ms / 1000, + ) + ) + rendered = json.dumps(report, indent=2) + if args.output: + args.output.write_text(rendered) + print(rendered) + if not all(row["correct"] for row in report["rows"]): + raise SystemExit(1) + + +if __name__ == "__main__": + main() diff --git a/scripts/codex_sandbox_probe.py b/scripts/codex_sandbox_probe.py new file mode 100644 index 000000000..cfa3f7f41 --- /dev/null +++ b/scripts/codex_sandbox_probe.py @@ -0,0 +1,214 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Probe which Codex permission-profile layouts keep a session read-only. + +Diagnostic only (no model is called): a stub Responses backend makes the real +Codex binary run one shell command per profile variant, and the script prints +what that command could read and write. Run on the platform to check:: + + uv run --extra dev --extra codex python scripts/codex_sandbox_probe.py +""" + +from __future__ import annotations + +import asyncio +import json +import os +import sys +import tempfile +import threading +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer + +from openai_codex import ApprovalMode, AsyncCodex, CodexConfig + +CHECKS = ( + "own_read", + "own_write", + "skill_read", + "skill_write", + "sibling_read", + "home_config_read", + "tmpdir_write", + "slash_tmp_write", +) +STATE: dict = {"cmd": "", "outputs": []} + + +class Handler(BaseHTTPRequestHandler): + protocol_version = "HTTP/1.1" + + def log_message(self, format: str, *args: object) -> None: + return None + + def do_GET(self) -> None: + self._send("application/json", b'{"data":[],"models":[]}') + + def do_POST(self) -> None: + body = json.loads(self.rfile.read(int(self.headers["content-length"]))) + outputs = [ + item + for item in body.get("input", []) + if isinstance(item, dict) and item.get("type") == "function_call_output" + ] + if outputs: + STATE["outputs"].append(str(outputs[-1].get("output"))) + item = { + "type": "message", + "id": "m", + "role": "assistant", + "status": "completed", + "content": [{"type": "output_text", "text": "ok", "annotations": []}], + } + else: + item = { + "type": "function_call", + "id": "fc", + "call_id": "call_probe", + "name": "exec_command", + "arguments": json.dumps({"cmd": STATE["cmd"]}), + "status": "completed", + } + usage = { + "input_tokens": 1, + "input_tokens_details": {"cached_tokens": 0}, + "output_tokens": 1, + "output_tokens_details": {"reasoning_tokens": 0}, + "total_tokens": 2, + } + events = ( + {"type": "response.created", "response": {"id": "r"}}, + {"type": "response.output_item.done", "output_index": 0, "item": item}, + {"type": "response.completed", "response": {"id": "r", "usage": usage}}, + ) + self._send( + "text/event-stream", + b"".join( + f"event: {e['type']}\ndata: {json.dumps(e)}\n\n".encode() + for e in events + ), + ) + + def _send(self, content_type: str, data: bytes) -> None: + self.send_response(200) + self.send_header("content-type", content_type) + self.send_header("content-length", str(len(data))) + self.end_headers() + self.wfile.write(data) + + +def variants(tmp: str, own: str, skills: str) -> dict[str, str]: + """``{name: permissions TOML}``; every variant denies the temp dirs.""" + head = ( + 'default_permissions = "p"\nproject_root_markers = []\n#TABLES\n' + '[permissions.p]\nextends = ":read-only"\n' + ) + denied = sorted({tmp, os.path.realpath("/tmp")}) # the same dir on Linux + deny = "[permissions.p.filesystem]\n" + "".join( + f'"{path}" = "deny"\n' for path in denied + ) + return { + # What VeADK generates on macOS. Linux (Codex 0.159.2) cannot reach a + # read grant under a denied parent: expected to fail there. + "explicit-read": head + deny + f'"{own}" = "read"\n"{skills}" = "read"\n', + # What VeADK generates on Linux: reachable, but writable. + "explicit-write": head + deny + f'"{own}" = "write"\n"{skills}" = "write"\n', + # Workspace-root grants; also unreachable on Linux (Codex 0.159.2). + "workspace-roots-read": head + + f'[permissions.p.workspace_roots]\n"{skills}" = true\n' + + deny + + '[permissions.p.filesystem.":workspace_roots"]\n"." = "read"\n', + } + + +async def probe(port: int, name: str) -> None: + tmp = os.path.realpath(tempfile.gettempdir()) + root = tempfile.mkdtemp(prefix="veadk-probe-ws-", dir=tmp) + own, sibling = os.path.join(root, "own"), os.path.join(root, "other") + os.makedirs(own) + os.makedirs(sibling) + with open(os.path.join(own, "AGENTS.md"), "w") as f: + f.write("probe") + with open(os.path.join(sibling, "secret.txt"), "w") as f: + f.write("SECRET") + home = os.path.realpath(tempfile.mkdtemp(prefix="veadk-probe-home-", dir=tmp)) + skills = os.path.join(home, "skills") + os.makedirs(os.path.join(skills, "probe")) + with open(os.path.join(skills, "probe", "SKILL.md"), "w") as f: + f.write("---\nname: probe\ndescription: probe\n---\n") + top, _, tables = variants(tmp, own, skills)[name].partition("#TABLES\n") + with open(os.path.join(home, "config.toml"), "w") as f: + f.write( + top + + 'model = "m"\nmodel_provider = "b"\napproval_policy = "never"\n' + + "[model_providers.b]\n" + + f'name = "b"\nbase_url = "http://127.0.0.1:{port}/v1"\n' + + 'env_key = "K"\nwire_api = "responses"\n' + + tables + ) + STATE["cmd"] = "; ".join( + ( + f"cat {own}/AGENTS.md >/dev/null 2>&1; echo own_read=$?", + f"echo x > {own}/w 2>/dev/null; echo own_write=$?", + f"cat {skills}/probe/SKILL.md >/dev/null 2>&1; echo skill_read=$?", + f"echo x > {skills}/w 2>/dev/null; echo skill_write=$?", + f"cat {sibling}/secret.txt >/dev/null 2>&1; echo sibling_read=$?", + f"cat {home}/config.toml >/dev/null 2>&1; echo home_config_read=$?", + f"echo x > {tmp}/veadk-w-$$ 2>/dev/null; echo tmpdir_write=$?", + "echo x > /tmp/veadk-w-$$ 2>/dev/null; echo slash_tmp_write=$?", + ) + ) + STATE["outputs"] = [] + try: + config = CodexConfig(cwd=own, env={"CODEX_HOME": home, "K": "x"}) + async with AsyncCodex(config=config) as codex: + thread = await codex.thread_start( + model="m", + model_provider="b", + cwd=own, + ephemeral=True, + approval_mode=ApprovalMode.deny_all, + ) + await asyncio.wait_for((await thread.turn("probe")).run(), 60) + except Exception as e: # noqa: BLE001 - the failure is the result + print(f"{name:22} ERROR {type(e).__name__}: {str(e)[:200]}") + return + text = STATE["outputs"][-1] if STATE["outputs"] else "" + got = dict(t.split("=", 1) for t in text.split() if t.split("=", 1)[0] in CHECKS) + cells = " ".join( + f"{k}={'ok' if got.get(k) == '0' else ('DENY' if k in got else '?')}" + for k in CHECKS + ) + print(f"{name:22} {cells}") + + +async def main() -> None: + server = ThreadingHTTPServer(("127.0.0.1", 0), Handler) + server.daemon_threads = True + threading.Thread(target=server.serve_forever, daemon=True).start() + linux = sys.platform.startswith("linux") + own = "ok" if linux else "DENY" # VeADK grants Linux write; see runtime + print( + f"wanted ({sys.platform}) own_read=ok own_write={own} skill_read=ok " + f"skill_write={own} sibling_read=DENY home_config_read=DENY " + "tmpdir_write=DENY slash_tmp_write=DENY" + ) + print(f"VeADK generates: {'explicit-write' if linux else 'explicit-read'}") + for name in ("explicit-read", "explicit-write", "workspace-roots-read"): + await probe(server.server_address[1], name) + server.shutdown() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/specs/README.md b/specs/README.md deleted file mode 100644 index 85f7fdd6a..000000000 --- a/specs/README.md +++ /dev/null @@ -1,114 +0,0 @@ -# Component Specification Guidelines - -[中文版](README.zh.md) - -This directory holds VeADK's maintained component contracts. A component spec explains what a component owns, what it exposes, and which invariants its implementation and consumers must preserve. It is not a change proposal, implementation diary, or copy of source code. - -## 1. Relationship to other documents - -- [AGENTS.md](../AGENTS.md) defines the repository-wide development workflow and gates in English. -- [prd-spec/](../prd-spec/README.md) describes individual changes, alternatives, implementation tasks, and acceptance evidence. -- `specs//` defines the component's maintained responsibilities and behavioral contracts. -- [frontend/SPEC.md](../frontend/SPEC.md) remains the Studio frontend and supporting backend development standard. Component specs reference it rather than redefining visual, interaction, or coding rules. -- User documentation and examples explain how to use the product. Update them when a contract change affects users; a component spec does not replace them. - -Keep one owner for each contract. A consumer spec references the owner's contract and describes its own obligations instead of copying the protocol. Implementation is evidence of current behavior, not authority to silently change an approved contract; report and resolve mismatches explicitly. - -## 2. Directory structure and naming - -```text -specs/ -├── README.md -├── README.zh.md -└── / - ├── README.md - └── README.zh.md -``` - -- Use a stable English kebab-case component name based on responsibility, not a release, date, task, or individual source file. -- English is `README.md`; Chinese is `README.zh.md`. Both files describe the same component and revision. -- Create a component directory when an actual change or explicit documentation task requires it. Do not pre-populate speculative contracts or claim undocumented components are already specified. -- Maintain component specs in place. Git history records revisions; dated change proposals belong in `prd-spec/`, not parallel dated component specs. -- Keep each component contract in its README pair by default. Split a genuinely large contract only along clear ownership boundaries, with paired language files and an index; do not create a file per endpoint mechanically. - -## 3. Bilingual maintenance - -1. Each component spec and these directory guidelines must have complete English and Chinese versions, linked near the top. -2. Keep component IDs, revision dates, status, contract IDs, interface names, fields, state names, paths, commands, numeric limits, and examples synchronized. Translate explanatory prose, not machine-facing identifiers. -3. Update both versions in the same change. Neither version may omit constraints, errors, risks, or compatibility requirements found in the other. -4. Missing, stale, or contradictory counterparts block approval and delivery. Resolve the discrepancy explicitly; neither language independently authorizes a conflicting implementation. -5. Preserve local documentation conventions outside `specs/`; in particular, the documentation site's Chinese `.mdx` and English `.en.mdx` pairing is unchanged. - -## 4. Choosing component boundaries - -Select boundaries from actual code ownership and runtime responsibilities. The following areas are investigation starting points, not pre-existing component specifications or a mandated directory list: - -| Area | Representative implementation locations | Contracts to consider | -| --- | --- | --- | -| Agent execution | `veadk/agent.py`, `veadk/runner.py`, `veadk/agents/` | Public SDK entry points, invocation, event production, delegation, cancellation, and errors. | -| Configuration and models | `veadk/config.py`, `veadk/configs/`, `veadk/models/` | Configuration precedence, validation, provider selection, retries, and credentials. | -| Memory and knowledge | `veadk/memory/`, `veadk/knowledgebase/` | Session identity, persistence, retrieval, isolation, retention, and failure behavior. | -| Tools and protocols | `veadk/tools/`, `veadk/toolkits/`, `veadk/a2a/`, `veadk/a2ui/` | Tool contracts, discovery, protocol mapping, permissions, and untrusted inputs. | -| Runtime and harness | `veadk/runtime/`, `veadk/extensions/harness/`, `veadk/integrations/agentkit/` | Runtime lifecycle, session/stream behavior, sidecar integration, recovery, and resource cleanup. | -| Studio and generation | `frontend/src/`, `frontend/server/`, `veadk/cli/` | UI/server boundaries, API types, project generation, deployment payloads, and generated artifacts. | -| Observability | `veadk/tracing/` and relevant integration code | Logs, traces, metrics, error diagnostics, and redaction. | - -Confirm the relevant code, tests, and consumers before defining ownership. A component may span several directories, but its spec must explain that responsibility boundary rather than merely list files. - -## 5. Required content - -Use this outline for both language versions. Keep applicable sections concrete; mark non-applicable sections with a reason instead of inventing mechanisms. - -| Section | Required content | -| --- | --- | -| Metadata and scope | Component ID, status (`draft` or `active`), revision date, counterpart link, owned code, tests, related PRDs, and dependent components. | -| Responsibility and non-goals | What the component owns, what it delegates, and which callers depend on it. | -| Entry points and dependencies | Public imports, CLI commands, HTTP routes, events, internal interfaces, and external systems as applicable. Identify the owner on both sides. | -| Contract | Stable `CON-*` IDs, inputs/outputs, field types, required/default values, validation, errors, and side effects. Include concrete examples for exposed interfaces. | -| State and concurrency | Valid states and transitions, terminal behavior, ordering, duplicate/late events, cancellation, timeouts, retry/idempotency, and restart behavior where applicable. | -| Data and configuration | Source of truth, persistence boundaries, schema/version semantics, configuration precedence, environment variables, and defaults. | -| Security and isolation | Authentication/authorization, identity boundaries, untrusted inputs, credential handling, redaction, and permitted external effects. | -| Compatibility and evolution | Supported caller/dependency versions, breaking-change impact, required consumer updates, migration or recovery when necessary, and explicit deprecation decisions. | -| Observability and failures | Diagnostic signals, error propagation, failure modes, recovery guarantees, and bounded resource behavior. Do not invent metrics that do not exist. | -| Verification | Contract-to-test mapping, negative/boundary cases, integration/E2E requirements, executable repository commands, and required environment. | -| Change record | Related approved PRDs, contract changes, implementation state, verification references, and known limitations. | - -A newly drafted component spec must distinguish observed behavior, intended changes, and unresolved questions. `active` means its stated current contract has been reviewed and reconciled with implementation and tests; it does not mean every future proposal is implemented. - -## 6. When a spec must change - -Assess component-spec impact for every feature, refactor, and bugfix PRD. Create or update a spec when the change affects: - -- Component responsibilities, dependency direction, public imports, or shared interfaces. -- HTTP/API/CLI contracts, event schemas, error codes, defaults, or configuration precedence. -- Session/state transitions, stream semantics, persistence, concurrency, cancellation, or recovery. -- Authentication, permissions, credential handling, trust boundaries, or data isolation. -- Compatibility guarantees, generated projects, deployment/runtime behavior, or observability contracts. - -Implementation-only changes that preserve these contracts need not modify component specs. Record the no-impact rationale in the PRD. Do not backfill the entire repository's specs just to deliver one scoped change. - -## 7. Change and review workflow - -1. Inspect the component's current code, tests, consumers, and existing documentation. Separate verified behavior from assumptions. -2. Describe the proposed change in a bilingual PRD and list the affected component contracts by ID. -3. Update both component-spec languages before implementation. Clearly label proposed behavior and link its PRD so it cannot be mistaken for already available behavior. -4. Review PRD/spec consistency, ownership, interface completeness, state/failure behavior, security, compatibility, testability, and bilingual equivalence. Resolve blockers before implementing the contract change. -5. Implement and verify the approved change. Each changed contract must map to a test or explicit validation procedure; runtime or UI boundaries require the relevant integration and end-to-end evidence. -6. Before delivery, reconcile both languages with the actual behavior, remove obsolete proposal labels for implemented changes, and link verification evidence. Do not mark unimplemented guarantees as active. - -For an explicit spec-only task, a reviewed proposal may be delivered as documentation, but must remain clearly labeled as proposed; documentation approval is not implementation evidence. - -## 8. Verification and completion - -Use a compact mapping rather than duplicating the PRD's implementation task list: - -| Contract | Guarantee | Test or validation | Evidence | -| --- | --- | --- | --- | -| `CON-1` | Observable invariant | Exact test path/command or procedure | Related PRD verification record or artifact | - -- Use VeADK's actual tools and affected CI workflows. Do not introduce nonexistent `make` targets or copy another project's coverage thresholds. -- Check current consumers as well as the changed implementation. SDK compatibility, generated code, frontend types, runtime payloads, and examples must agree where affected. -- Distinguish automated tests, local simulated integrations, and real browser/cloud/provider verification. State what each result does and does not prove. -- Record `pass`, `fail`, `blocked`, `not_run`, or `not_applicable` for checks, with reasons for the last three. A skipped or unexecuted check is not a pass. -- Keep raw execution logs and repeated task reports outside the long-lived contract; link relevant evidence and retain only contract-level conclusions and limitations. -- Never include real credentials, signed URLs, personal data, or unredacted production logs in specifications or evidence. diff --git a/specs/README.zh.md b/specs/README.zh.md deleted file mode 100644 index d94a7ec11..000000000 --- a/specs/README.zh.md +++ /dev/null @@ -1,114 +0,0 @@ -# 组件规格规范 - -[English](README.md) - -本目录存放 VeADK 持续维护的组件契约。组件 spec 说明组件负责什么、对外提供什么,以及实现和调用方必须保持哪些不变量。它不是变更提案、实施日志,也不是源码的复制品。 - -## 1. 与其他文档的关系 - -- [AGENTS.md](../AGENTS.md) 使用英文定义仓库级开发流程和门禁。 -- [prd-spec/](../prd-spec/README.zh.md) 描述具体变更、备选方案、实施任务和验收证据。 -- `specs//` 定义组件持续维护的职责和行为契约。 -- [frontend/SPEC.md](../frontend/SPEC.md) 继续作为 Studio 前端及配套后端开发规范。组件 spec 引用它,不重新定义视觉、交互或编码规则。 -- 用户文档和示例说明如何使用产品。契约变化影响用户时应同步更新,组件 spec 不能代替它们。 - -每个契约只有一个归属方。调用方 spec 引用归属方契约并说明自身义务,而不是复制协议。实现是当前行为的证据,不是静默改变已批准契约的依据;发现不一致时应明确报告和解决。 - -## 2. 目录结构与命名 - -```text -specs/ -├── README.md -├── README.zh.md -└── / - ├── README.md - └── README.zh.md -``` - -- 根据职责使用稳定的英文 kebab-case 组件名,不使用版本、日期、任务或单个源码文件作为命名依据。 -- 英文为 `README.md`,中文为 `README.zh.md`。两份文件描述同一组件的同一修订。 -- 实际变更或明确的文档任务需要时再创建组件目录。不预填推测性契约,也不声称尚无文档的组件已具备 spec。 -- 组件 spec 原地持续更新,由 Git 历史保留修订;带日期的变更提案放在 `prd-spec/`,不建立多份按日期并行的组件 spec。 -- 默认将每个组件的契约保留在一组 README 中。确实过大时,只能按明确职责边界拆分,并提供双语文件和索引;不得机械地为每个 Endpoint 建立文件。 - -## 3. 双语维护 - -1. 每份组件 spec 及本目录规范都必须具备完整英文版和中文版,在顶部附近相互链接。 -2. 同步组件 ID、修订日期、状态、契约 ID、接口名、字段、状态名、路径、命令、数值限制和示例。翻译解释性正文,不翻译面向程序的标识符。 -3. 同一次变更更新两版。任何一版都不得遗漏另一版的约束、错误、风险或兼容要求。 -4. 缺少对应版本、翻译过期或相互矛盾会阻塞批准和交付。必须明确消除差异;任一语言版本都不能独立授权相互冲突的实现。 -5. 保持 `specs/` 之外的局部文档惯例,尤其不改变文档站中文 `.mdx` 和英文 `.en.mdx` 的配对方式。 - -## 4. 组件边界选择 - -根据实际代码归属和运行职责划分边界。以下领域仅作为调查起点,不代表已有组件规格,也不是强制目录清单: - -| 领域 | 代表性实现位置 | 应考虑的契约 | -| --- | --- | --- | -| Agent 执行 | `veadk/agent.py`、`veadk/runner.py`、`veadk/agents/` | 公开 SDK 入口、调用、事件产生、委派、取消和错误。 | -| 配置与模型 | `veadk/config.py`、`veadk/configs/`、`veadk/models/` | 配置优先级、校验、Provider 选择、重试和凭据。 | -| 记忆与知识 | `veadk/memory/`、`veadk/knowledgebase/` | Session 身份、持久化、检索、隔离、保留周期和失败行为。 | -| 工具与协议 | `veadk/tools/`、`veadk/toolkits/`、`veadk/a2a/`、`veadk/a2ui/` | 工具契约、发现、协议映射、权限和不可信输入。 | -| 运行时与 Harness | `veadk/runtime/`、`veadk/extensions/harness/`、`veadk/integrations/agentkit/` | 运行时生命周期、Session/流行为、Sidecar 集成、恢复和资源清理。 | -| Studio 与代码生成 | `frontend/src/`、`frontend/server/`、`veadk/cli/` | UI/服务端边界、API 类型、项目生成、部署 Payload 和生成产物。 | -| 可观测性 | `veadk/tracing/` 及相关集成代码 | 日志、Trace、指标、错误诊断和脱敏。 | - -定义归属前先核对相关代码、测试和调用方。组件可以跨越多个目录,但 spec 必须解释其职责边界,而不只是列出文件。 - -## 5. 必需内容 - -两种语言采用以下提纲。适用章节必须具体;不适用章节说明理由,不得虚构机制。 - -| 章节 | 必需内容 | -| --- | --- | -| 元信息与范围 | 组件 ID、状态(`draft` 或 `active`)、修订日期、对应语言链接、归属代码、测试、相关 PRD 和依赖组件。 | -| 职责与非目标 | 组件负责什么、委托什么,以及哪些调用方依赖它。 | -| 入口与依赖 | 适用的公开导入、CLI 命令、HTTP 路由、事件、内部接口及外部系统,明确接口两端的归属方。 | -| 契约 | 稳定的 `CON-*` ID、输入输出、字段类型、必填项/默认值、校验、错误和副作用。对外接口提供具体示例。 | -| 状态与并发 | 适用的合法状态和转换、终态行为、顺序、重复/迟到事件、取消、超时、重试/幂等和重启行为。 | -| 数据与配置 | 事实来源、持久化边界、Schema/版本语义、配置优先级、环境变量及默认值。 | -| 安全与隔离 | 认证授权、身份边界、不可信输入、凭据处理、脱敏和允许的外部副作用。 | -| 兼容性与演进 | 支持的调用方/依赖版本、破坏性变更影响、所需调用方更新、必要的迁移或恢复,以及明确的废弃决策。 | -| 可观测性与失败 | 诊断信号、错误传播、失败模式、恢复保证和资源上限行为,不得虚构尚不存在的指标。 | -| 验证 | 契约到测试的映射、反向/边界用例、集成/E2E 要求、可执行的仓库命令及所需环境。 | -| 变更记录 | 相关已批准 PRD、契约变化、实现状态、验证引用和已知限制。 | - -新起草的组件 spec 必须区分已观察行为、预期变更和待决问题。`active` 表示文档声明的当前契约已经评审并与实现、测试核对一致,不表示所有未来提案均已实现。 - -## 6. 何时必须修改 spec - -每份功能、重构和 Bug 修复 PRD 都必须评估组件 spec 影响。以下内容发生变化时,应创建或更新 spec: - -- 组件职责、依赖方向、公开导入或共享接口。 -- HTTP/API/CLI 契约、事件 Schema、错误码、默认值或配置优先级。 -- Session/状态转换、流语义、持久化、并发、取消或恢复。 -- 认证、权限、凭据处理、信任边界或数据隔离。 -- 兼容性保证、生成项目、部署/运行时行为或可观测性契约。 - -保持以上契约不变的纯实现改动无需修改组件 spec,但应在 PRD 中记录无影响的理由。不得为了交付一个局部变更而补写全仓组件 spec。 - -## 7. 变更与评审流程 - -1. 检查组件当前代码、测试、调用方及已有文档,区分已验证行为和假设。 -2. 在双语 PRD 中描述拟议变更,并按 ID 列出受影响组件契约。 -3. 实现前更新组件 spec 的两种语言。明确标记拟议行为并关联 PRD,避免被误认为已可用行为。 -4. 评审 PRD/spec 一致性、职责归属、接口完整性、状态/失败行为、安全、兼容性、可测试性和双语等价性。在实现契约变化前清零阻塞项。 -5. 实现并验证已批准变更。每个变化的契约都必须映射到测试或明确验证步骤;跨运行时或 UI 边界时,提供相应集成和端到端证据。 -6. 交付前将两版与实际行为对齐,为已实现变化移除过期的提案标记,并关联验证证据。不得把未实现的保证标为 active。 - -明确的纯 spec 任务可以将已评审提案作为文档交付,但必须保留清晰的拟议标记;文档批准不等于实现证据。 - -## 8. 验证与完成标准 - -使用紧凑映射,不重复 PRD 的实施任务清单: - -| 契约 | 保证 | 测试或验证 | 证据 | -| --- | --- | --- | --- | -| `CON-1` | 可观察不变量 | 精确测试路径/命令或操作步骤 | 相关 PRD 验证记录或产物 | - -- 使用 VeADK 的实际工具及受影响 CI 工作流,不引入不存在的 `make` 目标,也不照搬其他项目的覆盖率阈值。 -- 除修改的实现外,还应检查当前调用方。受影响的 SDK 兼容性、生成代码、前端类型、运行时 Payload 和示例必须一致。 -- 区分自动化测试、本地模拟集成和真实浏览器/云/Provider 验证,说明每项结果能证明什么、不能证明什么。 -- 检查结果记录为 `pass`、`fail`、`blocked`、`not_run` 或 `not_applicable`,后三种说明原因。跳过或未执行的检查不算通过。 -- 原始执行日志和重复任务报告不放入长期契约;链接相关证据,仅保留契约层面的结论和限制。 -- Spec 和证据中均不得包含真实凭据、签名 URL、个人数据或未脱敏生产日志。 diff --git a/specs/apmplus-configuration/README.md b/specs/apmplus-configuration/README.md deleted file mode 100644 index c7b5f840c..000000000 --- a/specs/apmplus-configuration/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# APMPlus configuration - -AppKey precedence: nonblank OBSERVABILITY_OPENTELEMETRY_APMPLUS_API_KEY, then TENANT_APMPLUS_APPKEY, then get_apmplus_token using existing environment/IAM credentials. Lookup is lazy and successful values are cached on each APMPlusConfig instance. Failure is not cached and never returned as an empty key. Malformed responses raise a sanitized ValueError. Explicit endpoint overrides the existing provider-aware default; no local collector is required for that remote default. diff --git a/specs/apmplus-configuration/README.zh.md b/specs/apmplus-configuration/README.zh.md deleted file mode 100644 index 50c216aec..000000000 --- a/specs/apmplus-configuration/README.zh.md +++ /dev/null @@ -1,3 +0,0 @@ -# APMPlus 配置 - -AppKey 优先级:非空 OBSERVABILITY_OPENTELEMETRY_APMPLUS_API_KEY,其次 TENANT_APMPLUS_APPKEY,最后 get_apmplus_token 使用现有环境/IAM凭据。延迟获取,成功值缓存在每个 APMPlusConfig 实例。失败不缓存,也不返回空key。异常响应抛脱敏 ValueError。显式 endpoint 覆盖现有按云平台选择的默认值;远程默认地址不依赖本地collector。 diff --git a/specs/mpa-channels/README.md b/specs/mpa-channels/README.md deleted file mode 100644 index 76a17c22b..000000000 --- a/specs/mpa-channels/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# MPA channels - -[中文](README.zh.md) - -## Contract - -Studio administrators manage MPA channels through the existing Runtime proxy. The BFF strips incoming X-MPA-Channel-Key, requires admin authorization for all api/v1/channels paths, refuses events ingress through Studio, and injects the resolved Runtime API key for management. MPA opts in via CHANNEL_ADMIN_AUTH_MODE=runtime_key; default JWT remains available. Missing runtime keys fail closed. CHANNEL_STATE_ENCRYPTION_KEY must be provisioned once and retained across image updates; it is secret configuration. - -GET /api/v1/channels/capabilities returns serverSideBinding, bindingReady, bindingError and diagnostics support. Missing capabilities (404) means unsupported. POST /feishu/bindings creates an encrypted durable binding; GET /feishu/bindings/{id} advances it; POST /feishu/bindings/{id}/retry retries failure. Prefix these paths with /api/v1/channels. States are PENDING, SCANNED, AUTHORIZED, REGISTERING, BOUND, FAILED, EXPIRED. Responses contain id, loginUrl, qrCodeImage, expiresAt, pollAfterSeconds, appId, lastErrorCode and lastErrorMessage. Credentials never appear. A lost registration lease reports uncertainty instead of blindly repeating registration. - -GET /feishu/diagnostics reports actual stored configuration, missing metadata and observed delivery state. Existing GET /chat-permissions, POST /chat-permissions and DELETE /chat-permissions?channel=feishu&chat_id=... manage groups. POST supports channel, chatId and optional groupSessionScope (group, group_sender, group_topic, group_topic_sender); the Studio add-group form omits groupSessionScope. Existing DELETE root uses channel and appId. Binding and polling requests are cancelled on navigation; errors preserve the form, terminal states stop polling. Unsupported images show an upgrade message. No success indicator claims live connectivity without observed evidence. - -Database boundary: each MPA/formal-debug deployment needs an isolated channel database; startup rejects sharing across deployments. New message mappings include bot identity; old sessions remain history without automatic context migration. New ciphertext is not compatible with old images; rollback requires a matching database snapshot. Studio forwards only the trusted administrator identity as binding owner, replacing client X-User-Id. Runtime-key event ingress validates Authorization injected by APIG. - - -## Studio navigation -Message channels is managed through Automations → Messaging channels → MPA agent messaging channels, alongside Feishu bot creation and website integration. Selecting an existing MPA Runtime exposes Feishu, WeCom and DingTalk; Agent detail no longer has a channels section. Only implemented provider flows expose setup actions; unsupported capabilities are explicit. Gateway/route configuration and observed delivery retain distinct semantics. See [migration design](../../prd-spec/features/mpa-channels-automation/2026-09-16-mpa-channels-automation.md). - -## Multi-provider binding (2026-09-15) - -CON-8: Studio Message channels enables Feishu, DingTalk and WeCom when Runtime capabilities advertise the provider. DingTalk exposes POST `/dingtalk/bindings`, GET `/dingtalk/bindings/{id}`, POST `/dingtalk/bindings/{id}/retry` with the same secret-free durable binding shape as Feishu. Binding IDs are provider- and owner-scoped. WeCom exposes POST `/wecom/bindings` with `{botId, secret}` and returns `{status: "BOUND", appId}` only after registration and encrypted storage. Every endpoint requires channel admin authentication. Blank credentials are rejected; remote errors are sanitized. Neither browser persistence nor responses may contain secrets. - -CON-9: GET `/{channel}/diagnostics` and existing DELETE channel APIs operate on the selected provider. Studio requests chat-permissions only for Feishu; legacy non-Feishu permission endpoints remain compatible but do not control group admission. Unsupported Runtime versions require upgrading; no fallback to secret-returning legacy QR endpoints. Provider switches abort frontend work. Ambiguous registration requires Gateway inspection before a new binding; identical persisted credentials reuse the bot. Delivery observations are filtered by Gateway bot ID. Historical unscoped observations are not attributed to a provider. - -Design: [message-channel-binding](../../prd-spec/features/message-channel-binding/2026-09-15-message-channel-binding.md). - -CON-10: Group admission allowlists and Studio group-permission controls apply only to Feishu. DingTalk and WeCom group messages are accepted without local group-permission records, after existing authentication and bot-binding checks. Group session isolation is unchanged. - -CON-11: WeCom additionally supports official SDK popup authorization; returned credentials are submitted to `/wecom/bindings` and never persisted in the browser. Popup attempts are cancelled on navigation and bounded to five minutes. DingTalk supports manual `{clientId, clientSecret}` via POST `/dingtalk/bindings/manual`, advertised by `credentialBindingChannels`, with admin authentication, encrypted storage, sanitized errors and 409 for uncertain registration. QR binding remains unchanged. Both credential routes return only `{status: "BOUND", appId}` on success. See [authorization design](../../prd-spec/features/channel-auth-methods/2026-09-15-channel-auth-methods.md). - -CON-11: Configured Feishu and DingTalk channels allow QR regeneration through the existing POST binding endpoint. The pairing panel explains Feishu replacement and existing/new bot selection in provider authorization. QR creation never deletes the current binding; the summary changes only after successful BOUND diagnostics. Active pairing/loading/uncertain states prevent duplicate generation. - -CON-12: Configured WeCom bots also expose Regenerate QR code. This reopens the existing SDK authorization window from the user gesture, displays pairing/replacement guidance, and saves the authorized bot through `/wecom/bindings`. Failed or cancelled authorization does not delete the existing binding. - -CON-13: Pairing QR display and polling are transient to the mounted provider panel. Channel/page navigation, browser reload and panel Refresh discard pairing UI; persisted binding IDs are no longer resumed and legacy keys are cleaned. Existing bot configuration is still fetched. PENDING waiting text and the authorization-page link are hidden. Server-side binding state/expiry is unchanged; UI dismissal is not revocation. - -CON-14: Automations MPA channels management is limited to Studio admin/super_admin; other roles see an explanation and issue no list or channel requests. List requests use the authorized scope, all regions and agentCategory=mpa with pagination; region and Runtime ID identify the target. Options display the name once, description, creator and localized relative creation time without field-name prefixes, followed by region/ID. Missing creators show “Unknown creator”; missing/invalid times show an em dash. Selected-target context also retains region/ID. Explicit general records are excluded; older responses omitting category may rely on the explicit request filter. Only explicit selection mounts RuntimeChannels. Target, role or scope changes clear transient state and ignore stale responses; durable bindings remain intact. Search covers loaded pages with further pagination available; failures, empty lists and no search matches remain distinct. Existing backend administrator authorization is unchanged. - -## Configuration modes (2026-09-15) - -CON-15: All providers expose mutually exclusive Quick setup / Manual setup, defaulting to Quick setup on mount/provider/Runtime changes without persistence or automatic authorization. Manual Feishu uses POST `/api/v1/channels/feishu/bindings/manual` with `{appId, appSecret}` when `credentialBindingChannels` includes `feishu`; successful registration returns `{status: "BOUND", appId}` without credentials. Existing DingTalk/WeCom routes remain compatible. Both modes support configured bot replacement without first deleting the current binding. Method switches clear transient pairing/credentials, cancel frontend polling/SDK work and ignore late results; registration mutations prevent switching, and uncertain registration blocks new attempts across modes. Unsupported manual capability shows an upgrade message. The design and verification mapping are in [configuration modes](../../prd-spec/features/channel-configuration-modes/2026-09-15-channel-configuration-modes.md). - - -## Feishu multi-bot accounts (2026-10-09) - -CON-16: multiBotChannels containing feishu opts into account management. GET root returns channels with appId and enabled; multiple Feishu records are valid. Select an account before querying diagnostics or permission CRUD, always forwarding appId. POST permission includes appId, DELETE retains chat_id plus appId. PATCH /feishu/accounts/{appId} sets enabled; DELETE root removes only that account after confirmation. QR/manual binding adds a different appId and refreshes selection without replacing other accounts. Legacy capability absence retains single-account flows. Account ambiguity is not registration uncertainty. Target changes abort reads/polling and ignore late completion; mutations prevent switching. Secret handling and administrator proxy authorization remain unchanged. - -See [design](../../prd-spec/features/mpa-feishu-multi-bot/2026-10-09-studio-multi-bot.md). - - -## Runtime-owned group defaults (2026-10-12) - -CON-17: The Studio add-group form exposes no session-isolation selector and sends channel, trimmed chatId, and selected appId when applicable, omitting groupSessionScope entirely. Runtime default/inheritance rules own the resulting scope. Existing group records and their returned scope display remain unchanged; loading, refreshing or adding another group does not migrate or rewrite existing scopes. The optional API field and server behavior remain available to other callers. Adding an existing ID still follows the unchanged Runtime upsert contract. - -Design: [server-default-scope](../../prd-spec/bugfixes/channel-group-actions/2026-10-12-server-default-scope.md). Verification: frontend/tests/runtimeChannels.test.tsx (single/multi-bot payload and existing scope display); local browser fixtures, not cloud E2E. - - -## Lark brand (2026-10-15) - -CON-18 (approved addition): BytePlus exposes Lark only when `feishuDomains` includes `lark` and Feishu multi-account support is advertised. Reuse `/feishu/bindings` and `/feishu/bindings/manual` with `domain=lark`; omission remains Feishu. Canonical storage/API channel is `feishu`; `config.domain` identifies brand, absent means Feishu. Lists filter brand; account operations always carry appId. Runtime owns fixed API hosts and Gateway adapter=lark registration, validates inbound Bot identity and preserves request/delivery IDs. Existing domestic records and APIs remain compatible. See [design](../../prd-spec/features/lark-binding/2026-10-15-lark-binding.md). - - -## Bot binding types (2026-10-16) - -CON-19: Optional botBindingTypes advertises shared/private for Feishu and Lark. Studio shows only known advertised types while adding a Bot, prefers shared by default, and sends bindingType on QR creation, QR retry and manual binding. Lark retains domain=lark. Missing/empty/unknown-only capabilities omit the field and retain legacy behavior. Other providers are unchanged. Existing-account management does not edit type. Selection is memory-only, resets on Runtime/provider or add-panel remount, and is locked while busy or a durable QR binding exists; retries preserve its type. Runtime remains responsible for authorization, immutable type validation and recall isolation. Private UI description is “Private chats can recall this Bot’s history, including group chats.” - -Design: [Bot binding types](../../prd-spec/features/bot-binding-types/2026-10-16-bot-binding-types.md). diff --git a/specs/mpa-channels/README.zh.md b/specs/mpa-channels/README.zh.md deleted file mode 100644 index 39921fbab..000000000 --- a/specs/mpa-channels/README.zh.md +++ /dev/null @@ -1,67 +0,0 @@ -# MPA 渠道 - -[English](README.md) - -## 契约 - -Studio 管理员通过现有 Runtime 代理管理 MPA 渠道。BFF 移除传入 X-MPA-Channel-Key,所有 api/v1/channels 路径要求管理员,禁止通过 Studio 调用 events 入站接口,并为管理请求注入服务端解析的 Runtime API key。MPA 通过 CHANNEL_ADMIN_AUTH_MODE=runtime_key 显式启用,默认 JWT 仍可用。缺失 Runtime key 时拒绝请求。CHANNEL_STATE_ENCRYPTION_KEY 必须首次配置并在镜像更新时保留,属于敏感配置。 - -GET /api/v1/channels/capabilities 返回 serverSideBinding、bindingReady、bindingError 及诊断支持。能力接口 404 表示不支持。POST /feishu/bindings 创建加密持久绑定;GET /feishu/bindings/{id} 推进状态;POST /feishu/bindings/{id}/retry 重试失败。以上相对路径加 /api/v1/channels 前缀。状态为 PENDING、SCANNED、AUTHORIZED、REGISTERING、BOUND、FAILED、EXPIRED。响应含 id、loginUrl、qrCodeImage、expiresAt、pollAfterSeconds、appId、lastErrorCode、lastErrorMessage,不含凭据。注册租约丢失时报告结果不确定,不盲目重复注册。 - -GET /feishu/diagnostics 返回实际保存的配置、缺失元数据及观测投递状态。沿用 GET /chat-permissions、POST /chat-permissions、DELETE /chat-permissions?channel=feishu&chat_id=... 管理群。POST 支持 channel、chatId 及可选 groupSessionScope(group、group_sender、group_topic、group_topic_sender);Studio 新增群表单省略 groupSessionScope。已有根路径 DELETE 使用 channel 和 appId。离开页面取消绑定和轮询请求;错误保留表单,终态停止轮询。不兼容镜像提示升级。没有观测证据时不声称链路连通。 - -数据库边界:每个 MPA/formal-debug 需要独立渠道数据库,启动拒绝不同部署共享。新消息映射包含 Bot 身份,旧会话保留为历史,不自动迁移上下文。新密文不兼容旧镜像,回滚需恢复相应数据库快照。Studio 仅转发受信管理员身份作为绑定 owner,忽略客户端 X-User-Id。Runtime-key 模式事件入口验证 APIG 注入的 Authorization。 - - -## Studio 导航 -消息渠道由“自动化 → 消息渠道 → MPA智能体消息渠道”统一提供,与飞书机器人创建和网站集成并列。选择已有 MPA Runtime 后展示飞书、企业微信、钉钉;智能体详情不再保留该栏目。仅已实现的渠道流程提供配置操作;未支持能力明确提示。网关/路由配置与实际投递状态语义保持独立。参见[迁移设计](../../prd-spec/features/mpa-channels-automation/2026-09-16-mpa-channels-automation.zh.md)。 - -## 多渠道绑定(2026-09-15) - -CON-8:Runtime capabilities 声明支持后,Studio 消息渠道开启飞书、钉钉和企微。钉钉提供 POST `/dingtalk/bindings`、GET `/dingtalk/bindings/{id}`、POST `/dingtalk/bindings/{id}/retry`,沿用飞书不含密钥的持久化绑定结构。绑定 ID 按渠道和用户隔离。企微提供 POST `/wecom/bindings`,输入 `{botId, secret}`,仅注册并加密保存成功后返回 `{status: "BOUND", appId}`。所有接口要求渠道管理员鉴权。拒绝空凭据,远端错误脱敏。浏览器持久化和响应均不得含密钥。 - -CON-9:GET `/{channel}/diagnostics` 和现有 DELETE channel 接口操作所选渠道。Studio 仅为飞书请求 chat-permissions;保留非飞书旧权限接口兼容,但其不控制群准入。旧 Runtime 需升级,不回退到返回密钥的旧二维码接口。切换渠道取消前端工作。注册结果不确定时需检查网关后才能新建绑定;相同已保存凭据复用机器人。投递观测按 Gateway bot ID 过滤,不将历史未分渠道观测归属于某个渠道。 - -设计:[message-channel-binding](../../prd-spec/features/message-channel-binding/2026-09-15-message-channel-binding.zh.md)。 - -CON-10:群准入白名单及 Studio 群权限配置仅针对飞书。钉钉和企微群消息通过既有鉴权和机器人绑定检查后,无需本地群权限记录即可接收。群会话隔离行为不变。 - -CON-11:企微另支持官方 SDK 弹窗授权,返回的凭据提交 `/wecom/bindings`,不在浏览器持久化;切页取消,等待上限五分钟。钉钉支持 POST `/dingtalk/bindings/manual` 手动提交 `{clientId, clientSecret}`,由 `credentialBindingChannels` 声明能力,要求管理员鉴权、加密存储、错误脱敏,注册结果不明确返回 409;原扫码流程保持不变。两种凭据接口成功仅返回 `{status: "BOUND", appId}`。见[授权方式设计](../../prd-spec/features/channel-auth-methods/2026-09-15-channel-auth-methods.zh.md)。 - -CON-11:已配置飞书和钉钉支持通过既有 POST 绑定接口重新生成二维码。配对区说明飞书替换及授权流程中的存量/新建机器人选择。创建二维码不删除当前绑定,仅成功 BOUND 后刷新摘要。正在配对/加载/结果不确定时防止重复生成。 - -CON-12:已绑定企微机器人同样提供“重新生成二维码”,由用户点击重新打开既有 SDK 授权窗口,显示配对/替换说明,通过 `/wecom/bindings` 保存授权机器人。授权失败或取消不删除原绑定。 - -CON-13:配对二维码展示与轮询仅存在于当前挂载的渠道面板。切换渠道/页面、浏览器重载和面板刷新均丢弃配对界面;不再恢复存储的绑定 ID,并清理旧键。仍加载已有机器人配置。隐藏 PENDING 等待文案及授权页面链接。服务端绑定状态/过期不变,关闭界面不代表撤销。 - -CON-14:自动化 MPA 渠道页面仅允许 Studio admin/super_admin 管理;其他角色显示权限说明,不发送列表或渠道请求。列表通过已授权的 scope、全部地域和 agentCategory=mpa 筛选并分页,以地域和 Runtime ID 区分目标;选项名称只显示一次,并显示描述、创建者和本地化相对创建时间,不显示字段前缀,其后显示地域/ID;缺失创建者显示“未知创建者”,缺失/非法时间显示破折号;选中目标仍显示地域/ID;明确为 general 的记录被排除,旧响应缺少分类时可依据显式请求筛选。用户明确选择后才挂载 RuntimeChannels。切换目标、角色或范围后清理临时状态并忽略旧请求;保留已保存的绑定。搜索覆盖已加载页,可继续加载更多;失败、空列表和无搜索结果分别展示。后端原有管理员鉴权保持不变。 - -## 配置方式(2026-09-15) - -CON-15:所有渠道提供互斥的“极速配置 / 手动配置”,挂载、切换渠道或 Runtime 时默认极速配置,不持久化选择、不自动发起授权。`credentialBindingChannels` 包含 `feishu` 时,飞书手动方式通过 POST `/api/v1/channels/feishu/bindings/manual` 提交 `{appId, appSecret}`;注册成功仅返回 `{status: "BOUND", appId}`,不含凭据。钉钉和企微的现有路由保持兼容。两种方式均支持替换已配置机器人,不先删除现有绑定。切换方式清空临时配对与凭据,取消前端轮询/SDK 工作并忽略迟到结果;注册写请求期间禁止切换,结果不确定时禁止通过任一方式再次注册。缺少手动能力时提示升级。完整方案和验证映射见[配置方式设计](../../prd-spec/features/channel-configuration-modes/2026-09-15-channel-configuration-modes.zh.md)。 - - -## 飞书多机器人账号(2026-10-09) - -CON-16:multiBotChannels 包含 feishu 时启用账号管理。根 GET 返回包含 appId 和 enabled 的 channels,允许多个飞书记录。诊断及群权限 CRUD 前先选择账号,始终转发 appId;权限 POST 带 appId,DELETE 保留 chat_id 并附 appId。PATCH /feishu/accounts/{appId} 设置 enabled;根 DELETE 经确认仅删除目标账号。扫码/手动绑定新增不同 appId 并刷新选择,不替换其他账号。缺少能力声明则沿用单账号流程。账号歧义不等于注册不确定。切换目标取消读取/轮询并忽略迟到结果,写入时禁止切换。密钥处理和管理员代理鉴权不变。 - -见[设计](../../prd-spec/features/mpa-feishu-multi-bot/2026-10-09-studio-multi-bot.zh.md)。 - - -## 群策略默认值由 Runtime 决定(2026-10-12) - -CON-17:Studio 新增群表单不提供会话隔离选择器,仅提交 channel、去空白 chatId,以及适用时所选 appId,完全省略 groupSessionScope。最终策略由 Runtime 的默认/继承规则决定。已有群记录及服务端返回策略的展示保持不变;加载、刷新或新增其他群不会迁移或改写既有策略。其他调用方仍可使用可选 API 字段,服务端行为不变。再次添加已有 ID 仍遵循 Runtime 原 upsert 契约。 - -设计:[server-default-scope](../../prd-spec/bugfixes/channel-group-actions/2026-10-12-server-default-scope.zh.md)。验证:frontend/tests/runtimeChannels.test.tsx(单/多机器人请求体及既有策略展示);本地浏览器 fixture,不是云上 E2E。 - - -## Lark 品牌(2026-10-15) - -CON-18(已批准增量):BytePlus 仅在 `feishuDomains` 包含 `lark` 且声明飞书多账号支持时开放 Lark。复用 `/feishu/bindings` 和 `/feishu/bindings/manual`,传 `domain=lark`;省略仍为飞书。存储/API 的 channel 仍为 `feishu`,以 `config.domain` 区分品牌,缺失为飞书。列表按品牌过滤,账号操作始终带 appId。Runtime 负责固定域名及 Gateway adapter=lark 注册,校验入站 Bot 身份并保留 request/delivery ID。国内已有记录及接口兼容。见[设计](../../prd-spec/features/lark-binding/2026-10-15-lark-binding.zh.md)。 - - -## Bot 绑定类型(2026-10-16) - -CON-19:可选 botBindingTypes 为飞书及 Lark 声明 shared/private。Studio 添加 Bot 时仅显示已知且声明的类型,优先默认共享;扫码创建、扫码重试和手动绑定均发送 bindingType。Lark 保留 domain=lark。能力缺失/为空/仅含未知值时省略字段,保持旧行为;其他渠道不变。已有账号管理不编辑类型。选择仅保存在内存中,切换 Runtime/渠道或重新打开添加面板时重置;请求中或存在持久扫码绑定时锁定,重试保持原类型。鉴权、类型不可变校验及召回隔离仍由 Runtime 负责。私有类型 UI 说明为“私聊可召回同 Bot 的历史,包括群聊。” - -设计:[Bot 绑定类型](../../prd-spec/features/bot-binding-types/2026-10-16-bot-binding-types.zh.md)。 diff --git a/specs/mpa-collaborating-agents/README.md b/specs/mpa-collaborating-agents/README.md deleted file mode 100644 index 28a32a898..000000000 --- a/specs/mpa-collaborating-agents/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# MPA collaborating Agents: A2A Center - -[中文版](README.zh.md) - -## Ownership and UI - -Studio administrators manage **MPA → Collaborating Agents → A2A Center**. Select a target Runtime account/region and a space. Runtime GET/PUT `/api/v1/studio/collaborators/registry/binding` persists only one MPA-to-space association with revision CAS; registry/spaces and registry/agents query the target Runtime cloud identity. All compatible running members, including future members, can be discovered by the existing governed dispatcher. Registration acceptance alone is not proof of execution. - -## Registration - -POST `/web/a2a-agents?region=...` is a Studio control-plane endpoint, not a Runtime tool. It requires existing management authorization and access to targetRuntimeId. Source Runtime also authorizes runtimeId and passes RuntimeConfig with networkType public/private. Source Standard supports importType json (agentCard object) or url (cardUrl HTTPS). All requests include spaceId; source-specific fields are mutually exclusive. Studio signs CreateA2aAgent with existing server credentials and returns only `{id}`. Account/region access failures remain explicit. No new Runtime create privilege or external dependency is introduced. - -Input is bounded to 128 KiB, IDs are validated, JSON requires name/url/version, URLs exclude userinfo/fragments. AgentKit fetches imported URLs; Studio does not fetch arbitrary URLs. Cards and credentials are never returned, logged, or persisted by the browser. JSON input is masked until explicitly revealed. No automatic create retry: network/5xx/cancel uncertainty requires refresh/reconciliation. A successful create followed by a failed refresh remains a successful create with a list error. Changing Runtime/region/space aborts stale UI responses and clears input; normal keyboard and IME behavior is retained. - -## Legacy data and compatibility - -Old local list/create/update/delete/check endpoints authenticate then return 410 `legacy_registration_retired`. Their manual form and local write/resolution service are removed. Runtime startup does not delete or migrate legacy rows in `mpa_collaborating_agents`; existing records remain stored but are not used for discovery or dispatch. Preserve other MPAs, space associations, sessions, the execution ledger and the shared `collaborator_credential_key`. Published migrations and table structure remain unchanged. Upgrading or rolling back binaries does not itself delete these records; any future data cleanup requires a separately approved operation. - -## Existing dispatch safety - -Model tools accept IDs resolved from the bound space, never arbitrary URLs/auth headers. Existing Harness identity/scope/grant/fence/ledger/terminal semantics are unchanged. Safe transport enforces HTTPS, DNS/address policy, TLS, no redirects, bounded responses/timeouts. Private origins require explicit Runtime allowlisting and connectivity. Only supported Card protocol/transport/auth configurations are callable. Worker and scheduled Main do not gain registration or collaboration privileges. - -## Verification and design - -[Registration and retirement design](../../prd-spec/features/mpa-collaborating-agents/2026-10-22-center-registration.md). Python route tests: tests/cli/test_a2a_center_registration.py; UI: frontend/tests/collaboratingAgents.test.tsx and mpaBasicDetails.test.tsx. Runtime retired-endpoint tests live in agentkit-mpa-agent/tests/test_collaborator_retirement.py. Mocked tests and local browser checks are distinct from live cloud verification. Automatic legacy-data cleanup is not part of this contract. - -## Space management (2026-10-22) - -The binding and member areas are separate accessible sections. POST `/web/a2a-spaces?region=...` requires administrator and target Runtime authorization, accepts `{targetRuntimeId, name, projectName?, intentEnabled?}`, and returns `{id}` after signed CreateA2aSpace. Body limit is 4096 bytes; name/project are trimmed nonempty strings with at most 128 characters and no control characters; project defaults to default and semantic search to false. Reject unknown fields and non-boolean switches. Creation selects the confirmed space (including when absent from the current list page), never binds it automatically. Failed refresh preserves confirmed creation success. Uncertain writes/cancel require refresh before another mutation, with no automatic retry. Runtime/region switching cancels stale requests. Existing cloud identity/region and dispatch boundaries remain unchanged. Tags and space editing are outside the creation form; deletion follows the member-action contract below. See [space management design](../../prd-spec/features/mpa-collaborating-agents/2026-10-22-space-management.md). - -The unbind action is offered only when the selected space equals the bound space. Switching spaces clears any prior confirmation; the mutation handler rejects unbinding another selected space. Space resource IDs remain in selection/request values but are omitted from user-facing names, status and destination labels. - -## Member actions and deletion - -Implemented locally: [member action design](../../prd-spec/features/mpa-collaborating-agents/2026-10-22-member-actions.md). Studio provides sanitized GET and confirmed DELETE /web/a2a-agents/{agentId}, scoped by targetRuntimeId, spaceId and region. Returned IDs must match. DELETE /web/a2a-spaces/{spaceId} checks current target binding, space identity and an empty member list before cloud deletion. No cascade, automatic unbinding, source Runtime deletion or automatic mutation retry. A2A-specific opt-in structured cloud errors distinguish known refusal from uncertainty; other helper callers retain existing behavior. Details exclude raw Cards, unsanitized URLs and provider errors. Closing pending mutations requires refresh reconciliation; identity changes abort stale UI updates. - - -## Deletion preflight authentication - -CON-DELETE-AUTH: Runtime JSON collaborator requests apply the same administrator-only, server-owned `X-MPA-Studio-Key` policy as the dedicated collaborator proxy. Browser credentials cannot replace this key, and unrelated paths receive no collaborator key. Binding-read 401/403 yields `runtime_auth_failed` with the original status, performs no cloud calls and remains a retryable known preflight failure in the UI. Cloud permission refusals retain their separate behavior. See [authentication fix](../../prd-spec/bugfixes/mpa-a2a-space-deletion/2026-10-22-runtime-auth.md). - - -## Endpoint and skill metadata - -CON-ENDPOINTS: GET /web/a2a-runtime-endpoints/{sourceRuntimeId} authorizes administrator and both targetRuntimeId/sourceRuntimeId in region. Return runtimeId and at most one configured safe HTTP(S) URL per public/private type. Registration offers only current configured types and blocks stale/unavailable selections; payload stays RuntimeId/NetworkType. No arbitrary endpoint URL is accepted. - -CON-CARD-VIEW: Agent detail adds serviceAddress (string|null), skills (array|null) and skillsTruncated (boolean). Project only bounded name/description/tags/examples/ID, never raw Cards or authentication. Null indicates unavailable, [] confirmed empty; over 100 skills shows explicit truncation. Strip query/fragment; reject userinfo, malformed, control-character or non-HTTP(S) URLs. Metadata parsing affects View only, not deregistration. See [endpoint and skill design](../../prd-spec/features/mpa-collaborating-agents/2026-10-22-endpoints-and-skills.md). - - -## Runtime registration picker - -CON-RUNTIME-PICKER: Runtime registration uses one required Select populated by authorized paginated Runtime reads. There is no separate manual ID field. The selected ID remains an internal request value; only a selection still present in the current list with a matching endpoint can submit. Loading, successful empty list and failed list reads remain distinct, with retry for empty/error and append pagination preserving selection. Standard imports and backend APIs are unchanged. See [single-picker fix](../../prd-spec/bugfixes/mpa-a2a-runtime-picker/2026-10-22-single-runtime-picker.md). - -## 2026-10-22 Agent alias correction - -Registered Agent aliases belong to the current MPA business database, scoped by MPA/region/space/Agent. List, discovery and @name resolution share this data. No SHARED_APIG_DATABASE_URL or AGENTKIT_ACCOUNT_ID dependency. Studio keeps revision CAS and authenticated ownership checks; it forwards PUT only for binding and exact valid Agent alias routes. Older Runtime capability absence disables editing with an upgrade message. Shared aliases are not automatically imported. Center names and Cards remain unchanged. See [design](../../prd-spec/bugfixes/mpa-agent-alias/2026-10-22-agent-alias-business-store.md). diff --git a/specs/mpa-collaborating-agents/README.zh.md b/specs/mpa-collaborating-agents/README.zh.md deleted file mode 100644 index fc3f2ee68..000000000 --- a/specs/mpa-collaborating-agents/README.zh.md +++ /dev/null @@ -1,56 +0,0 @@ -# MPA 协作 Agent:A2A 中心 - -[English](README.md) - -## 职责与入口 - -Studio 管理员进入 **MPA → 协作 Agent → A2A 中心**,选择目标 Runtime 的账号/地域和空间。Runtime GET/PUT `/api/v1/studio/collaborators/registry/binding` 只持久化一个 MPA 与空间的关联,使用 revision CAS;registry/spaces、registry/agents 使用目标 Runtime 云身份查询。空间内包含未来新增的、运行中且兼容的成员由既有受控派发器发现。注册被接受不等于执行成功。 - -## 注册 - -POST `/web/a2a-agents?region=...` 是 Studio 控制面接口,不是 Runtime 工具。要求既有管理权限和 targetRuntimeId 的访问权。Runtime 来源还校验 runtimeId 权限,RuntimeConfig 包含 public/private 的 networkType。Standard 来源支持 importType=json(agentCard 对象)或 url(HTTPS cardUrl)。所有请求带 spaceId,来源字段互斥。Studio 使用既有服务端凭据签名 CreateA2aAgent,只返回 `{id}`。账号/地域权限错误明确返回;不增加 Runtime 创建权限或新依赖。 - -输入上限 128 KiB,校验 ID;JSON 要求 name/url/version;URL 不允许用户信息或锚点。导入 URL 由 AgentKit 抓取,Studio 不直接请求任意 URL。Card 和凭据不回显、不写日志、不在浏览器持久化;JSON 默认遮罩,可主动显示。创建不自动重试;网络、5xx、取消导致结果不明,必须先刷新核对。注册成功而刷新失败时保留成功结论并显示列表错误。切换 Runtime/地域/空间取消旧响应、清空输入;保留键盘和 IME 行为。 - -## 旧数据与兼容 - -旧本地列表、新建、修改、删除、检查接口鉴权后返回 410 `legacy_registration_retired`,删除其表单和本地写入/解析服务。Runtime 启动时不删除或迁移 `mpa_collaborating_agents` 中的旧记录;已有记录保留在库中,但不再用于发现或派发。保留其他 MPA、空间关联、会话、执行账本及共享 `collaborator_credential_key`。已发布迁移和表结构不改。升级或回滚二进制本身不会删除这些记录;未来如需清理数据,须另行批准操作。 - -## 既有派发安全 - -模型工具只接受关联空间中解析的 ID,不接受任意 URL/认证头。Harness 的 identity/scope/grant/fence/ledger/terminal 语义不变。安全传输继续限制 HTTPS、DNS/地址、TLS、禁重定向、响应大小和超时;私网来源需显式 Runtime allowlist 和网络连通性。仅兼容的 Card 协议、传输和认证可调用。Worker 与定时 Main 不新增注册或协作权限。 - -## 验证与设计 - -[注册及退役设计](../../prd-spec/features/mpa-collaborating-agents/2026-10-22-center-registration.zh.md)。Python 路由:tests/cli/test_a2a_center_registration.py;UI:frontend/tests/collaboratingAgents.test.tsx、mpaBasicDetails.test.tsx。Runtime 退役接口测试:agentkit-mpa-agent/tests/test_collaborator_retirement.py。模拟测试、本地浏览器检查与真实云验证必须分别报告。自动清理旧数据不属于此合同。 - -## 空间管理(2026-10-22) - -绑定和成员使用独立可访问区域。POST `/web/a2a-spaces?region=...` 要求管理员及目标 Runtime 授权,接受 `{targetRuntimeId, name, projectName?, intentEnabled?}`,签名调用 CreateA2aSpace 后返回 `{id}`。请求体上限 4096 字节;名称/项目去除首尾空白后非空、不超过 128 字符且无控制字符;项目默认 default,语义检索默认 false。拒绝未知字段及非布尔开关。创建后选中已确认的空间(当前列表页未返回时仍可选),不自动绑定。刷新失败保留已确认的创建成功状态。写入结果不明或取消后须刷新核对,才允许再次写入,不自动重试。切换 Runtime/地域取消旧请求。保留原有云身份/地域与派发边界。创建表单不管理标签或空间编辑;删除遵循下方成员操作合同。参见[空间管理设计](../../prd-spec/features/mpa-collaborating-agents/2026-10-22-space-management.zh.md)。 - -仅当所选空间就是已绑定空间时提供解绑操作。切换空间清除旧确认;写入处理函数拒绝解绑与所选空间不匹配的绑定。空间资源 ID 保留在选择值和请求中,但不显示在面向用户的名称、状态及目标标签里。 - -## 成员操作与删除 - -已本地实现:[成员操作设计](../../prd-spec/features/mpa-collaborating-agents/2026-10-22-member-actions.zh.md)。Studio 提供脱敏 GET 及经确认的 DELETE /web/a2a-agents/{agentId},按 targetRuntimeId、spaceId 和 region 限定,返回 ID 必须匹配。DELETE /web/a2a-spaces/{spaceId} 在云删除前检查目标当前绑定、空间身份及成员列表为空。不级联、不自动解绑、不删除来源 Runtime、不自动重试写入。仅 A2A 开启结构化云错误,区分明确拒绝与结果不明;其他 helper 调用保留原行为。详情排除原始 Card、未脱敏 URL 和提供商错误。关闭在途写入须刷新核对;身份切换中止旧 UI 更新。 - - -## 删除前置检查认证 - -CON-DELETE-AUTH:Runtime JSON 协作请求与专用协作代理共用仅管理员、由服务端持有的 `X-MPA-Studio-Key` 策略。浏览器凭据不能替换该 Key,无关路径不添加协作 Key。绑定读取 401/403 保持原状态并返回 `runtime_auth_failed`,不调用云接口,在 UI 中仍是可重试的明确前置失败。云权限拒绝保留独立行为。参见[认证修复](../../prd-spec/bugfixes/mpa-a2a-space-deletion/2026-10-22-runtime-auth.zh.md)。 - - -## 端点与技能元数据 - -CON-ENDPOINTS:GET /web/a2a-runtime-endpoints/{sourceRuntimeId} 校验管理员及 region 中 targetRuntimeId/sourceRuntimeId 的访问权限。返回 runtimeId,每种 public/private 类型最多一个已配置、安全的 HTTP(S) URL。注册仅列当前配置类型,阻止迟到/不可用选择;Payload 仍为 RuntimeId/NetworkType,不接受任意端点 URL。 - -CON-CARD-VIEW:Agent 详情增加 serviceAddress(string|null)、skills(array|null)、skillsTruncated(boolean)。仅投影有界名称/描述/标签/示例/ID,不返回原始 Card 或认证内容。null 为不可用,[] 为已确认无技能;超过 100 项明确提示截断。移除 query/fragment,拒绝 userinfo、无效、控制字符及非 HTTP(S) URL。元数据解析仅影响查看,不影响注销。参见[端点与技能设计](../../prd-spec/features/mpa-collaborating-agents/2026-10-22-endpoints-and-skills.zh.md)。 - - -## Runtime 注册选择控件 - -CON-RUNTIME-PICKER:Runtime 注册使用一个必选 Select,选项来自已授权的分页 Runtime 查询,不再提供独立手动 ID 字段。所选 ID 保留为内部请求值;仅当选择仍存在于当前列表且有匹配端点时才允许提交。加载、成功空列表和读取失败明确区分,空/错误支持重试,追加分页保留选择。Standard 导入和后端 API 不变。参见[单一选择修复](../../prd-spec/bugfixes/mpa-a2a-runtime-picker/2026-10-22-single-runtime-picker.zh.md)。 - -## 2026-10-22 Agent alias correction - -已注册 Agent 别名属于当前 MPA 业务库,按 MPA/地域/空间/Agent 隔离;列表、发现和 @名称解析读取相同数据,不依赖 SHARED_APIG_DATABASE_URL 或 AGENTKIT_ACCOUNT_ID。Studio 保留 revision CAS 和所有者鉴权,仅放行绑定与 Agent ID 合法的精确别名 PUT。旧 Runtime 未声明能力时禁用并提示升级;共享别名不自动导入;中心名称和 Card 不变。参见[设计](../../prd-spec/bugfixes/mpa-agent-alias/2026-10-22-agent-alias-business-store.zh.md). diff --git a/specs/mpa-image-update/README.md b/specs/mpa-image-update/README.md deleted file mode 100644 index 17cbb0e79..000000000 --- a/specs/mpa-image-update/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# MPA image management - -[中文](README.zh.md) - -Component ID: mpa-image-update. Revision: 2026-10-22. Status: active. - -## Responsibility and interfaces - -Studio exposes GET /web/mpa-images/{runtime_id}/{component}, GET .../versions, GET .../latest-preview?revision=... and POST ... for component runtime or worker only. All routes require Agent management, fresh owner/admin Runtime authorization (no shared connection fallback), valid provider region and MPA category. Runtime targets its authorized RuntimeId. Worker targets only the consistent Runtime.ToolId / AGENTKIT_TOOL_ID / deployment record worker_id binding, proven managed ownership tags and project, with no other Runtime/registry reference. - -GET detail returns only component, resourceId, currentImage, status, repository, revision, canUpdate, reason and operation; never credentials/environments. revision hashes fresh identity, binding, image and status. POST accepts exactly {tag, revision}, {image, revision}, or {tag:latest, revision, selection:{image,digest}}; both/neither source and selection on other sources are rejected. Reject unchanged targets and stale revisions. Runtime sends only RuntimeId, ArtifactType, ArtifactUrl and ReleaseEnable=true. ArtifactType is public_image for its matching fixed public repository and image for a custom manual image. Worker sends only ToolId and ImageUrl. No automatic second-component mutation. - -## Catalog and bounds - -Provider volcengine uses agentkit-platform-2112682748-cn-beijing.cr.volces.com/mpa; byteplus uses agentkit-platform-3001037806-ap-southeast-1.cr.bytepluses.com/mpa. runtime suffix mpa_agent; worker suffix mpa_codex_worker. The public catalog accepts no arbitrary repository input. OCI pages contain up to 100 tags, cursor is a validated last tag. UI offers version selection and load-more without a search field. No fabricated publication timestamps or chronological ordering. HTTP timeout 12 seconds, overall catalog operation 25 seconds, response limit 1 MiB, no redirects. Anonymous token requests use only the fixed registry /service/token and expected service/scope; ignore untrusted challenge realms and Link hosts. Validate JSON shape, tags and pagination progress. Verify target manifest existence before a public-tag write; manual-address writes do not perform Studio registry requests. - -## State and concurrency - -Managed deployment registry account/region lock serializes image writes with provisioning/deletion. Pending/deleting deployments reject writes. Separate image_updates.runtime and image_updates.worker records persist target and phase before cloud mutation. Update acknowledgements mean submitted, not healthy. Usable status plus observed target clears active intent to configured. Explicit provider rejection clears active intent; timeout/transport/5xx/aborted submission stays uncertain. Never auto-retry cloud writes. Browser read abort/late response cannot change another Agent/component. Once dispatched, server mutation continues under its lock despite client disconnect; registry intent survives process interruption. Provisioning/deletion reject active image intents. Existing creation hashes remain unchanged because images are not recreated by this flow. - -## Failures and compatibility - -Return safe reason codes for forbidden, invalid_request, invalid_image, stale, busy, ownership, missing_worker, read_failed, catalog_failed, latest_resolution_failed, rejected, unchanged, not_found and outcome_unknown, with no raw provider payload. Cloud permission errors stay permission errors. Shared/unproven workers are read-only with an actionable reason. Existing sandbox sessions may keep their old Worker image; users create a new session to use updated template. Runtime requests immediate release; no application health guarantee. No SDK/config/source generation/public session contract changes. No live cloud write is included in automated tests. - -Related design: [independent updates](../../prd-spec/features/mpa-image-update/2026-10-22-independent-images.md). - -## Contract verification - -| Contract | Guarantee | Validation | -| --- | --- | --- | -| CON-IMAGE-ISOLATION | Independent payloads and navigation | tests/cli/test_mpa_images.py; frontend/tests/mpaImages.test.tsx; frontend/tests/mpaBasicDetails.test.tsx | -| CON-IMAGE-CATALOG | Fixed providers, bounded anonymous OCI reads | tests/cli/test_mpa_images.py; public read-only smoke | -| CON-IMAGE-OWNERSHIP | Fresh ownership, revision and shared-resource guards | tests/cli/test_mpa_images.py; tests/cli/test_frontend_runtime_proxy.py | -| CON-IMAGE-OUTCOME | Durable intent and no blind retry | tests/cli/test_mpa_images.py; tests/integrations/mpa_managed/test_deletion.py | -| CON-IMAGE-SOURCE | Mutually exclusive manual/public targets, format validation and no arbitrary-host requests | tests/cli/test_mpa_images.py; frontend/tests/mpaImages.test.tsx | -| CON-IMAGE-LATEST | Existing same-repository version, saved digest revalidation and creation-state isolation | tests/cli/test_mpa_images.py; tests/integrations/mpa_managed/test_image_snapshot.py; frontend/tests/mpaImages.test.tsx | -| CON-IMAGE-UI | Loading/errors/cancellation/keyboard/narrow layout | frontend/tests/mpaImages.test.tsx; mocked browser verification | - -## Manual source: CON-IMAGE-SOURCE - -[Manual image source design](../../prd-spec/features/mpa-image-update/2026-10-22-manual-image-source.md). Replace the search input with a full image reference as an alternative to public tag selection; the latest explicit input clears the other source on that component only. Extend POST with exactly {image, revision} or the compatible {tag, revision}. Manual-address format validation must not cause Studio to fetch arbitrary registry hosts. A valid manual target must remain usable when catalog loading fails. Runtime custom images use ArtifactType=image; matching fixed public images use public_image. Worker still sends only ImageUrl. All existing authorization, ownership, stale-revision and durable intent rules remain. Manual references may use other registries. Format: explicit registry host with one or more repository path segments and a required tag, at most 2048 characters, lowercase DNS host/path, optional port 1–65535, no scheme, embedded credentials, whitespace, query, fragment or digest. An explicit host contains a dot or port, or is localhost. Leading/trailing input whitespace is trimmed by the UI before submission; the API accepts only canonical references. Pull permissions and image compatibility are checked by AgentKit, using existing permissions. Public list refresh preserves manual input, and catalog loading/errors do not block manual submission. No image update is triggered by input alone. - -## Latest version resolution: CON-IMAGE-LATEST - -[Latest update design](../../prd-spec/features/mpa-image-update/2026-10-22-latest-update-snapshots.md). Public latest updates reuse managed.image_snapshot.snapshot_image for all Volcengine/BytePlus Runtime/Worker combinations. MPA-only authorization remains mandatory. A read-only GET .../latest-preview?revision=... resolves outside the deployment lock and rechecks resource identity before returning {component, revision, requestedImage, image, digest}. Confirmation shows that target; a tag=latest request may carry selection={image,digest}, revalidated without rereading latest. Legacy latest requests resolve server-side. Persist actual target, requestedImage and digest only in the selected component update intent; creation image_snapshots is unchanged. Fixed-tag and manual requests keep existing behavior. No match, changed content, timeout or stale metadata prevents mutation. Preview lookup failures use latest_resolution_failed and do not represent an unknown cloud write. Closing aborts browser preview and discards late results; confirmation stays disabled during loading/error. The preview deadline is 180 seconds. - -The optional selection must contain exactly image and digest: a non-latest tag in the matching fixed repository and lowercase sha256 with 64 hexadecimal digits. Reject malformed selections with invalid_request before registry reads. Read-only preview requires a nonempty revision of at most 128 characters. MPA image POST requests and latest previews have a 180-second browser deadline; other image reads keep 30 seconds. The upstream resolver retains its 120-second discovery budget, bounded responses and pagination. Backend preview has a 180-second deadline; an in-flight cancelled thread can finish read-only registry calls without cloud mutation. diff --git a/specs/mpa-image-update/README.zh.md b/specs/mpa-image-update/README.zh.md deleted file mode 100644 index 800909127..000000000 --- a/specs/mpa-image-update/README.zh.md +++ /dev/null @@ -1,47 +0,0 @@ -# MPA 镜像管理 - -[English](README.md) - -组件 ID:mpa-image-update。修订:2026-10-22。状态:active。 - -## 职责和接口 - -Studio 提供 GET /web/mpa-images/{runtime_id}/{component}、GET .../versions、GET .../latest-preview?revision=... 和 POST ...;component 仅为 runtime 或 worker。全部接口要求 Agent 管理权限、重新读取的所有者/管理员 Runtime 授权(不使用共享连接回退)、有效供应商区域和 MPA 类型。Runtime 只指向授权 RuntimeId。Worker 只指向 Runtime.ToolId / AGENTKIT_TOOL_ID / 部署记录 worker_id 一致的绑定,要求可证明的托管归属标签、项目匹配,且无其他 Runtime/注册记录引用。 - -GET 详情只返回 component、resourceId、currentImage、status、repository、revision、canUpdate、reason、operation,不返回凭据/环境变量。revision 对新读取的身份、绑定、镜像、状态求哈希。POST 仅接受 {tag, revision}、{image, revision} 或 {tag:latest, revision, selection:{image,digest}};同时提供/均未提供来源或其他来源附带 selection 时拒绝;拒绝相同目标和过期修订。Runtime 只发送 RuntimeId、ArtifactType、ArtifactUrl、ReleaseEnable=true。对应固定公共仓库使用 public_image,自定义手填镜像使用 image。Worker 只发送 ToolId、ImageUrl。绝不自动更新另一组件。 - -## 目录和限制 - -volcengine 使用 agentkit-platform-2112682748-cn-beijing.cr.volces.com/mpa;byteplus 使用 agentkit-platform-3001037806-ap-southeast-1.cr.bytepluses.com/mpa。runtime 后缀为 mpa_agent;worker 后缀为 mpa_codex_worker。公共目录不接受任意仓库输入。OCI 每页最多 100 标签,游标为校验后的末标签。界面提供版本选择和加载更多,不再显示搜索框。不虚构发布时间或时间排序。HTTP 超时 12 秒,整个目录操作 25 秒,响应上限 1 MiB,禁止重定向。匿名令牌请求只指向固定仓库的 /service/token 和预期 service/scope;忽略不可信 challenge realm 和 Link 主机。校验 JSON 结构、标签、分页前进。公共标签写入前验证目标 manifest 存在;手填地址写入不会由 Studio 请求镜像仓库。 - -## 状态和并发 - -复用托管部署注册表账号/区域锁,串行化镜像写入与部署/删除。pending/deleting 部署拒绝写入。image_updates.runtime 和 image_updates.worker 分别在云写入前持久化目标及阶段。Update 应答只表示已提交,不表示健康。读取到目标及可用状态后将活动意图核对为 configured。明确云拒绝时清除活动意图;超时/传输/5xx/提交中断保留 uncertain。绝不自动重试云写入。浏览器读取取消/迟到响应不能改变其他 Agent/组件。提交后即使客户端断连,服务端操作仍持锁继续;进程中断后意图仍在注册表。部署/删除拒绝活动镜像意图。创建哈希不变,因为此流程不重新创建镜像资源。 - -## 失败和兼容 - -返回安全原因码 forbidden、invalid_request、invalid_image、stale、busy、ownership、missing_worker、read_failed、catalog_failed、latest_resolution_failed、rejected、unchanged、not_found、outcome_unknown,不返回原始云响应。云权限错误保持权限错误。共享或归属不可证明 Worker 只读并解释原因。已有沙箱会话可能保留旧 Worker 镜像;新建会话使用更新模板。Runtime 请求立即发布,不承诺应用健康。不改变 SDK/配置/代码生成/公共会话契约。自动测试不包含真实云写入。 - -相关设计:[独立更新](../../prd-spec/features/mpa-image-update/2026-10-22-independent-images.zh.md)。 - -## 契约验证 - -| 契约 | 保证 | 验证 | -| --- | --- | --- | -| CON-IMAGE-ISOLATION | 独立请求和导航 | tests/cli/test_mpa_images.py; frontend/tests/mpaImages.test.tsx; frontend/tests/mpaBasicDetails.test.tsx | -| CON-IMAGE-CATALOG | 固定供应商、有界匿名 OCI 读取 | tests/cli/test_mpa_images.py; 公共只读验证 | -| CON-IMAGE-OWNERSHIP | 新读取的归属、修订和共享资源保护 | tests/cli/test_mpa_images.py; tests/cli/test_frontend_runtime_proxy.py | -| CON-IMAGE-OUTCOME | 持久化意图和禁止盲目重试 | tests/cli/test_mpa_images.py; tests/integrations/mpa_managed/test_deletion.py | -| CON-IMAGE-SOURCE | 手填/公共来源互斥、格式校验、禁止任意主机请求 | tests/cli/test_mpa_images.py; frontend/tests/mpaImages.test.tsx | -| CON-IMAGE-LATEST | 同仓库已有版本、保存摘要复验、隔离创建状态 | tests/cli/test_mpa_images.py; tests/integrations/mpa_managed/test_image_snapshot.py; frontend/tests/mpaImages.test.tsx | -| CON-IMAGE-UI | 加载/错误/取消/键盘/窄窗口 | frontend/tests/mpaImages.test.tsx; 模拟浏览器验证 | - -## 手填来源:CON-IMAGE-SOURCE - -[手填镜像来源设计](../../prd-spec/features/mpa-image-update/2026-10-22-manual-image-source.zh.md)。搜索框改为完整镜像引用,与公共标签选择二选一;最近一次显式输入仅清除当前组件的另一来源。POST 扩展为严格 {image, revision} 或兼容的 {tag, revision}。手填格式校验不得导致 Studio 请求任意仓库主机。目录加载失败时,有效手填目标仍可更新。Runtime 自定义镜像使用 ArtifactType=image,匹配固定公共仓库的镜像使用 public_image;Worker 仍只发送 ImageUrl。保留现有授权、归属、过期修订和持久化意图规则。手填引用可使用其他仓库。格式要求为显式仓库主机、至少一级仓库路径和必填标签,最长 2048 字符,DNS 主机及路径小写,可选端口为 1–65535;禁止协议、内嵌凭据、空白、查询、片段和摘要。显式主机包含点或端口,或为 localhost。界面在提交前去除首尾空白;API 仅接受规范引用。拉取权限和镜像兼容性由 AgentKit 使用现有权限验证。刷新公共版本保留手填内容,目录加载/报错不阻止手填提交。单纯输入不会触发更新。 - -## latest 版本解析:CON-IMAGE-LATEST - -[latest 更新设计](../../prd-spec/features/mpa-image-update/2026-10-22-latest-update-snapshots.zh.md)。公共 latest 更新在国内/BytePlus Runtime/Worker 四种组合中复用 managed.image_snapshot.snapshot_image,继续强制仅 MPA 授权。只读 GET .../latest-preview?revision=... 在部署锁外解析,重查资源身份后返回 {component, revision, requestedImage, image, digest}。确认框显示该目标;tag=latest 请求可携带 selection={image,digest},不重读 latest 地重新验证。旧 latest 请求由服务端解析。只在所选组件更新意图保存实际目标、requestedImage、digest;创建 image_snapshots 不变。固定标签及手填保持原行为。无匹配、内容变化、超时或元数据过期时不写入。预览查询失败使用 latest_resolution_failed,不表示云写入结果未知。关闭取消浏览器预览并丢弃迟到结果;加载/错误期间禁用确认。预览截止时间为 180 秒。 - -可选 selection 必须严格包含 image 和 digest:对应固定仓库的非 latest 标签,以及小写 sha256 和 64 位十六进制摘要。格式不合法时在请求仓库前返回 invalid_request。只读预览要求非空且最长 128 字符的 revision。MPA 镜像 POST 与 latest 预览的浏览器截止时间为 180 秒;其他镜像读取维持 30 秒。上游解析器保留 120 秒发现预算、有界响应及分页。后端预览截止时间为 180 秒;取消后仍在执行的线程可完成只读仓库请求,不产生云写入。 diff --git a/specs/mpa-runtime-control/README.md b/specs/mpa-runtime-control/README.md deleted file mode 100644 index d3f8ed769..000000000 --- a/specs/mpa-runtime-control/README.md +++ /dev/null @@ -1,193 +0,0 @@ -# MPA Runtime Control - -- Component ID: `mpa-runtime-control` -- Status: `draft`; S1, S2, S3, S4, S5-01, S5-01a, S5-04, the S5-08 Session creation fix, and S5-11 live lifecycle verification are implemented or recorded; S5-06 and S5-07 no-impact records are captured; remaining S5 contracts remain target behavior -- Revised: 2026-09-19 -- Chinese: [README.zh.md](README.zh.md) -- PRD: [MPA AgentKit P0 Functional Migration](../../prd-spec/features/mpa-p0-productionization/2026-09-15-mpa-p0-productionization-design.md) -- Owning code: Agent, Session, A2A, worker, authorization, and diagnostics modules in `agentkit-mpa-agent` - -## Responsibility and authority - -AgentKit Studio owns the MPA authoring entry and platform Skill, Tool, Environment, Vault, and other resources. P0 does not use Managed Agent CRUD/version/Session. mpa-agent owns immutable executable Profile revisions, session execution configuration versions, Turn execution records, participant state, and diagnostic linkage. It does not provide templates, experts, review/install, or a separate publishing service. Studio/BFF is a client and orchestrator, not execution-state authority. - -At code revision `2f5e039`, the Runtime already had Agent config revisions, Session CRUD/run/SSE/events/MCP, A2A generation/lease and pause/resume, worker interruption, health/readiness, and Runtime Console. Those were reuse points. The current P0 branch has since implemented the S1 Profile/readiness/auth foundation, the S2 Session execution-config/Profile-upgrade Runtime subset, the S3 Turn acceptance, dispatcher recovery, and refresh cursor subset, the S4-01/S4-02 participant data-model plus pause-barrier subset, the S4-03 lease-aware resume boundary, the S4-04 Runtime linked continuation API/outbox, the S5-08 MPA Session creation initialization fix, and the S5-11 execution-smoke Worker endpoint integration described below. Studio control wiring for deletion and lifecycle verification is captured in the Studio control-plane spec. - -Runtime mainline also independently provides encrypted multi-channel binding, shared APIG control-plane support, persistent deployment records, isolated PostgreSQL database creation, Runtime provisioning, and one persistent Skill Space per logical agent. These are reusable infrastructure capabilities governed by their own deployment/channel contracts. They do not alter the P0 authority model: Profile, Session execution configuration, Turn, lifecycle, and diagnostic state remain Runtime-owned, and their presence does not close the remaining P0 evidence gates. - -## CON-1: Identity and authorization - -P0 uses one `RuntimePrincipal v1`: `iss`, `aud`/authorized client, `sub`, `account_id`, `workspace_id`, `agent_id`, `actions[]`, `iat`, `nbf`, `exp`, and `jti`, with 30 seconds of clock skew. Studio's OAuth/gateway middleware first validates the user bearer token and BFF forwards only that validated token. AgentKit Runtime custom JWT authorization and mpa-agent validate the same issuer discovery/JWKS signature, `kid` rotation, audience/client, and time claims. Token TTL follows the existing issuer policy and M0 records the observed value; BFF neither self-signs nor assumes five minutes. Current authorization/revocation is checked before each side effect through the injectable `RuntimePrincipalAuthorizer` seam. Browser owner/account headers are never trusted, and Runtime key-auth is not an end-user identity. - -REST uses `Authorization: Bearer `. A2A uses `X-Ve-TIP-Token`, but its verifier must map the token into the same `RuntimePrincipal` instead of retaining only `sub`. Existing `X-Jwt-Token` is a legacy-capability path: callers without v1 scope claims may read legacy resources but cannot call new Profile, execution-config, run, or continue writes. `DISABLE_JWT_AUTH` is local-test-only; enabling it in AgentKit mode makes execution readiness fail. - -When `MPA_AGENTKIT_MODE=true`, legacy ADK endpoints that still depend on `require_auth`, including `GET /list-apps`, authenticate from the same AgentKit `Authorization` Runtime principal instead of independently requiring `X-Jwt-Token`. This is an adapter compatibility rule, not a second identity mechanism. Outside AgentKit mode, the existing `X-Jwt-Token` contract remains unchanged. Missing or invalid AgentKit credentials fail closed in either the direct Runtime route or Studio BFF proxy path. - -Check relevant scope before Profile apply, Session create/read, execution-config mutation, Turn acceptance, resume/continue, secret resolution, and external tool dispatch. The implemented S4-06 subset persists only a normalized non-secret `RuntimePrincipal` snapshot in accepted Turn dispatch payloads and inbox metadata so background execution and restart recovery can recheck grants without storing bearer tokens. Revocation blocks later effects; already dispatched external effects are not claimed undone. Ordinary responses, logs, traces, errors, and diagnostics contain no secret values. The implemented S5-01a subset registers legacy plaintext secret literals discovered during secret-reference migration in a process-local redaction registry, then redacts those exact values across API access logs, client-visible Session output/events, Runtime Console responses, lifecycle details, and trace-step persistence. This value-aware redaction intentionally preserves non-secret business handles such as `flow_id` and `user_code`. - -## CON-2: Studio MPA Profile execution projection - -`PUT /api/v1/agents/{mpaInstanceId}/profile` follows the existing runtime instance identity and accepts only a Profile validated and normalized by BFF from an accepted Studio draft. The request carries `sourceProfileId`, digest, name/description/system/model/tools/skills/MCP/Multiagent, and allowlisted metadata. BFF does not allocate a revision; Runtime allocates and returns the unique `profileRevision` inside the successful CAS transaction. First apply must carry `If-None-Match: *`; later apply must carry `If-Match: ""`. Supplying both returns `400 invalid_precondition`; supplying neither returns `428 precondition_required`. Successful apply/status responses carry a strong ETag. - -Runtime maps the Profile to a new `mpa_agents` revision and stores source Profile ID/digest plus apply state. Same operation key/digest returns the prior allocated revision; stale ETag returns `412 profile_changed`; replaying one allocated revision with different content returns `409 profile_version_conflict`; unknown Tool/Skill/Multiagent shapes return `422 unsupported_profile_field`. `GET /api/v1/agents/{mpaInstanceId}/profile-status` returns `pending|applying|applied|failed`, target/applied revisions, runtime revision, operation ID, safe error, and retryability. - -## CON-3: Session execution configuration version - -A “session execution configuration version” is a monotonically revisioned record of one Session's execution choices, not a platform-resource copy. It combines a pinned MPA Profile revision with Session overrides and stores only IDs, explicit versions or digests, model override, permission revision, invalid references, and timestamps for Skill/Tool/MCP/Environment/Knowledge/Vault resources. AgentKit still owns resource bodies and secrets. - -The runtime persistence contract is append-only. `session_execution_configs` is keyed by `(app_name, session_id, revision)` and stores strong `etag`, `mpa_instance_id`, `profile_revision`, `profile_default_revision`, JSON `overrides`, JSON `effective_refs`, JSON `invalid_refs`, `updated_by`, `created_at`, and `updated_at`. Current reads use `(app_name, session_id, revision DESC)`. SQL writes serialize one Session's revision stream with a PostgreSQL advisory transaction lock, then compare the supplied ETag and append revision N+1; existing rows are never updated in place. - -`POST /api/v1/sessions` initializes the first execution-config revision when the request carries `mpaInstanceId + profileRevision` and does not explicitly carry `executionConfigRevision`. The response then returns `executionConfigRevision=1`, so Studio can immediately pass a valid version into the first MPA `/run`. If a recovery or migration caller explicitly supplies `executionConfigRevision`, the Runtime preserves that value. - -`GET /api/v1/sessions/{sessionId}/execution-config` returns a revision and strong ETag. `PATCH` requires `If-Match` and applies category-level replace, clear, or inherit. A match atomically creates the next revision; a missing precondition returns `428`; a conflict returns `412 execution_config_changed` with the safe current revision; validation failure creates no revision. Changes affect the next Turn only and never mutate an active Turn. `POST /api/v1/sessions/{sessionId}/profile-upgrade` carries `targetProfileRevision`, the same ETag, and `Idempotency-Key`; the target Profile must already be applied. It may be submitted while a Turn is active but affects only the next Turn. Downgrade returns `409 downgrade_not_allowed`; concurrent change returns `412`. - -The implemented S2-03 subset supports `model` and `mcpServers` category changes. Unsupported categories return `422 unsupported_execution_config_category` until the corresponding AgentKit resource resolver exists. Unknown MCP IDs return `422 resource_unavailable` and do not create a revision. Non-MPA Sessions that do not carry `mpaInstanceId + profileRevision` return `404 execution_config_not_found` instead of implicitly creating MPA state. - -The implemented S2-05 profile-upgrade subset exposes `POST /api/v1/sessions/{sessionId}/profile-upgrade`. It requires `Idempotency-Key` and the current execution-config `If-Match`, accepts only a higher applied `targetProfileRevision`, preserves explicit overrides, and recomputes inherited defaults from the target Profile. The schema-version-8 SQL `runtime_operations` ledger persists the request hash and safe result, so same-key/same-target retries replay across Runtime processes; same-key/different-target returns `409 idempotency_mismatch`. - -Before a Turn is accepted, the Runtime resolves the selected execution-config revision into an immutable `SessionExecutionConfigSnapshot`. The snapshot includes the accepted revision and ETag, MPA instance, Profile revision, Profile-default revision, overrides, effective references, invalid references, and accepted timestamp. It is a value object used by Turn acceptance and trace linkage; it is not a second mutable configuration record. - -Example: Session revision 6 uses Skill `s-1@3`. Client A changes the model to M2 and creates revision 7. Client B tries a Skill update with ETag 6 and receives 412. The running Turn continues with revision 6; a later Turn uses revision 7. - -## CON-4: Turn acceptance and idempotency - -`POST /api/v1/sessions/{sessionId}/run` explicitly accepts `executionConfigVersion` and `Idempotency-Key`. The A2A Agent Card advertises `urn:veadk:mpa:execution:v1`; `params.metadata.veadkExecution` carries the same fields. Legacy A2A calls without the extension may use only the default revision and no Session overrides. Runtime resolves resource versions outside the transaction, then atomically compares Session/permission versions, claims the active Turn, writes immutable `turn_execution_records` and a dispatch intent, and only then triggers external execution. Materialization rechecks version/digest and permission; drift fails before side effects. - -Run idempotency is scoped by `user_id + app_name + session_id + source + Idempotency-Key`, uniquely enforced by `turn_execution_records`, and retained with canonical request hash, frozen config snapshot, `accepted|dispatching|succeeded|failed_retryable|failed_terminal`, and durable `turnId/invocationId/operationId` values. Same key/hash returns the original accepted invocation; same key/different payload returns `409 idempotency_mismatch`. A pending Profile never becomes the current `mpa_agents` revision; only the success transaction switches the applied revision. Crash recovery reuses the dispatch key and cannot create a second Turn. - -The implemented S3 subset uses `turn_execution_records` as the durable run-acceptance ledger. REST `/run` requires the idempotency key and requested execution-config revision for MPA Sessions, stores the frozen `SessionExecutionConfigSnapshot`, returns `turnId`, `operationId`, and `executionConfigRevision`, and preserves the legacy response shape for non-MPA Sessions. A2A accepts the same metadata through `params.metadata.veadkExecution`, binds the accepted invocation ID to the ADK request, updates dispatch status to `dispatching`, then records `succeeded` or `failed_retryable`. Replaying the same A2A key/hash returns a completed idempotent replay notice and does not execute the model again. `dispatch_payload` allows restart recovery to rebuild missing/error inbox rows without introducing a second dispatcher. Studio sends the last completed event ID back to Runtime SSE so browser refresh resumes after the cursor rather than replaying from the beginning. - -## CON-5: Whole-Turn lifecycle and continuation - -The state machine is `queued -> running -> pausing -> paused -> resuming -> running -> completed|failed|cancelled`. `a2a_task_controls` retains aggregate generation. Every active primary executor/worker registers lease, state, safe point, and checkpoint in `a2a_turn_participants`. The Turn becomes `paused` only after all active participants acknowledge the same generation; partial acknowledgement remains `pausing` or ends in a classified timeout. - -The implemented S4-02 subset wires pause requests through `ParticipantBarrierService` when `a2a_turn_participants` is available. A pause command advances the aggregate task generation and copies active participants from the previous generation. A participant ACK only affects its own row; the service calls task-level `acknowledge_safe_point` only after all current-generation active participants are `paused` or terminal. ACKs for old generations or wrong owners are ignored. A Codex worker that starts after the aggregate task is already `paused` still registers in the current generation, receives a worker pause command, acknowledges the same generation, and waits for resume. Completed, failed, or cancelled worker turns mark their participant row terminal so later pause generations do not wait for an already-ended worker. When resume is acknowledged, the Runtime seeds the next running participant generation from the previous paused generation. - -Resume rechecks authorization and participant viability. The implemented S4-03 boundary resumes the same business Turn only when the task is paused for no more than 300 seconds, the task owner lease is still viable, and the participant generation has no `lost|failed` participant or still-running participant with an expired/missing lease. A still-running participant with a valid lease makes resume return `409 invalid_state` so the caller waits for the safe point; it is not converted into continuation. Past 300 seconds, after owner lease loss, after participant loss/failure, or after an unrecoverable restart, resume returns `new_turn_required`. The `new_turn_required` transition records the resume idempotency key and replays safely for the same key. - -Only explicit user action calls `POST /api/v1/a2a/tasks/{taskId}/continue`. The implemented S4-04 Runtime API requires `Idempotency-Key` and `expectedGeneration`, accepts only `interrupted|orphaned` control states, resolves the previous business Turn from the old task invocation, and inserts a linked `turn_execution_records` row with `continuation_of=`. The continuation reuses the frozen execution-config snapshot and dispatch payload, closes the old active dispatch row, and schedules the existing inbox/outbox worker with `enqueue_once`. Same-key/same-request calls replay the same continuation; same-key/different-request calls return `idempotency_mismatch`. The contract does not promise distributed ACID with the SDK `DatabaseTaskStore`; the existing outbox recovers accepted continuation dispatches. Studio no longer constructs a prompt and calls ordinary send once S4-05 is wired. - -## CON-6: Events, refresh, and diagnostics - -SSE replay reuses persistent ADK Session events plus the inbox terminal fence. The in-memory StreamHub is only the low-latency live path and is not refresh authority. The cursor is a persistent event ID; the server replays in persisted Session-event order and then joins the live stream. An unknown non-empty cursor returns `410 cursor_expired` instead of silently replaying from the beginning. Event/invocation/type identity prevents duplicate text, usage, or artifacts; a missing terminal remains unknown/failed rather than success. - -Runtime Console/trace reuses existing stores and adds indexed MPA instance, Profile revision, session execution-config revision, Turn, and worker run/session linkage. Details are redacted and bounded; core correlation does not depend on parsing arbitrary JSON. The implemented S5-04 slice exposes a normalized `correlation` object on invocation trace responses and each trace step, extracting MPA instance, Profile revision, execution-config revision/ETag, business Turn, A2A task, Runtime ID/version, and Codex/subagent worker session/turn/profile identifiers from already-redacted detail payloads. - -Execution configuration and event persistence serve only the mpa-agent business Session. P0 does not create an AgentKit Managed Agent Session or maintain dual Session IDs. AgentKit `AgentWithOverrides` remains a recorded platform capability outside this contract. - -## CON-7: APIs and errors - -| API | Status | Contract | -| --- | --- | --- | -| `PUT /api/v1/agents/{mpaInstanceId}/profile` | new | Always requires `Idempotency-Key`; first apply uses only `If-None-Match: *`, later apply only `If-Match: ""`, mutually exclusive; payload carries Studio Profile source ID/digest and Runtime allocates the revision | -| `GET /api/v1/agents/{mpaInstanceId}/profile-status` | new | Profile apply status and recoverable operation | -| `GET /api/v1/sessions/{sessionId}/execution-config` | new | Authoritative session execution-config version and ETag | -| `PATCH /api/v1/sessions/{sessionId}/execution-config` | new | CAS update affecting the next Turn | -| `POST /api/v1/sessions/{sessionId}/profile-upgrade` | new | Explicit Session Profile upgrade; target Profile must be applied | -| `POST /api/v1/sessions/{sessionId}/run` | extend | Add `executionConfigVersion` and durable idempotency | -| A2A submission | extend | `urn:veadk:mpa:execution:v1` extension uses the same Turn-acceptance service | -| `GET/POST /api/v1/a2a/tasks/{taskId}/control...` | extend | Preserve URLs; add participant aggregation and request-hash idempotency | -| `POST /api/v1/a2a/tasks/{taskId}/continue` | new | Runtime-owned linked continuation | -| `GET /api/v1/runtime-operations/{operationId}` | new | Principal/target-authorized async status and safe replay result for Profile/run/upgrade/continue | -| Session CRUD/SSE/events/MCP/health/readiness/console | reuse | Add scope, cursor, and correlation fields | - -New APIs use `error.code/message/requestId/retryable/currentState`. Existing clients retain their current envelope until a capability-version negotiation permits change. Old Runtime versions stay discoverable as read-only/unsupported; AgentKit P0 cannot disable authorization or fall back to browser/ArkClaw authority. - -Execution readiness uses the existing `POST /api/v1/readiness/execution-smoke` endpoint. In AgentKit mode it obtains a real Codex Worker endpoint through the Sandbox manager, verifies `/v1/codex-worker/ready`, creates an isolated Worker session, starts one smoke turn, polls `GET /v1/codex-worker/sessions/{sessionId}/turns/{turnId}` until a terminal state, then replays events with `follow=false`. The marker observation is diagnostic-only because Worker output can be summarized; readiness requires terminal state plus readable replay events. Errors stay stage-classified and sanitized. - -## CON-8: Data model - -| Model | Proposed change | -| --- | --- | -| `mpa_agents` | Add source Profile ID/digest, apply status/operation/error, `agent_api_key_ref`, `model_api_key_ref`, `secret_migration_status`, and `secret_migration_operation_id`; existing Runtime-assigned revision is the sole `profileRevision` | -| `mpa_meta` | Add AgentKit mode, workspace, bootstrap revision, readiness phase, finalization operation, last error | -| `session_meta` | Add account/workspace/MPA-instance/Profile/execution-config revision | -| `session_execution_configs` | New append-only table for `(app_name, session_id, revision)`, strong ETag, MPA instance, Profile revision, Profile-default revision, overrides/effective/invalid references, updater, and timestamps | -| `turn_execution_records` | New table for accepted config, resolved refs, model/policy/secret refs, dispatch key/hash/status, and `continuation_of` | -| `a2a_turn_participants` | New table for each generation's participants, lease, safe point, checkpoint, ACK, and terminal times | -| `a2a_task_controls` | Add command request hash/result reference; add approval fields only when a real P0 high-risk tool flow needs them | -| `runtime_operations` | New table for Profile lifecycle and future long-running control operations: unique principal/operation/target/key, request hash, state, response snapshot, resource IDs, expiry. Run acceptance uses `turn_execution_records`; execution-config PATCH uses revision/ETag; pause/resume uses `a2a_task_controls`, without duplicate ledger writes | -| Trace/lifecycle | Add indexed task/config/runtime/worker references where needed | - -Do not add Agent definition/version, template/expert, review/install, independent continuation, or generic policy-evaluation tables. The implemented S5-01 slice adds `agent_api_key_ref`, `model_api_key_ref`, `secret_migration_status`, and `secret_migration_operation_id` to `mpa_agents`. AgentKit mode rejects new writes to the existing plaintext `agent_api_key` and `model_api_key` columns. Existing new-platform values are migrated reference-first: the external secret reference is created with a stable idempotency key before the Runtime clears plaintext in one database transaction. Failure before the database update retains plaintext so retry remains possible; successful migration clears plaintext for the MPA agent rows and keeps only references. Runtime execution resolves `agentApiKeyRef`/`modelApiKeyRef` only at secret-use boundaries and fails closed if no resolver is configured. S5-01a adds process-local value-aware redaction for the legacy plaintext values read during migration and for future registered secret literals. Registered literals are redacted inside nested values and free text before API/log/Runtime Console/trace exposure, while non-secret references and business parameters remain visible. Runtime operation response/resource snapshots use that same redaction boundary, and the status API omits principal, idempotency key, and request hash. Legacy mode retains read-only plaintext compatibility for one version and removes the plaintext read path in the next major version. Structural changes use an explicit ordered migration; SQL and in-memory stores stay aligned. AgentKit-owned tables are not extended and ArkClaw data is not migrated. - -Migration order is `runtime_operations -> additive mpa_agents/mpa_meta/session_meta columns -> session_execution_configs -> turn_execution_records -> a2a_turn_participants -> indexes/constraints`, with a schema version after every step. Expand schema first, deploy dual-version-readable code second, and enable new writes last; reject rollback when an older image cannot read the state safely. - -## CON-9: Performance and retention - -- Profile apply, run, pause/resume/continue acceptance: p95 <= 500 ms; asynchronous completion uses operation/status lookup. -- execution-config GET/PATCH: p95 <= 300 ms; remote resource lookup holds no database lock. -- first SSE event on a warm Runtime: p95 <= 2 s, excluding model first-token SLA. -- list/detail: p95 <= 1 s, default 20/max 100, with no N+1 platform scan. -- Idempotency results are retained at least 24 hours; terminal execution evidence defaults to 30 days; active Sessions and referenced configuration versions do not expire automatically. Production retention is explicitly configured. - -## Verification - -Implementation follows TDD: add Profile mapping/version/idempotency, execution-config CAS, run crash recovery, multi-participant barrier, continuation, SSE replay, scope, and redaction tests before implementation. Add a real PostgreSQL two-connection/restart lane for CAS, one active Turn, outbox, migration, and dual-version reads; in-memory stores are not final evidence for these guarantees. Runtime baseline command: - -```bash -uv run --group dev pytest \ - tests/test_session_auth.py \ - tests/test_subagent_binding_store.py \ - tests/test_a2a_task_control.py \ - tests/test_turn_control_api.py \ - tests/test_codex_adapter.py -``` - -This command is required during implementation and is `not_run` for this documentation-only change. Real integration must cover Profile apply, two-client CAS, failure retry, multi-worker pause/resume, the 300-second boundary, replay, and revocation. - -## Confirmed product decisions - -The user confirmed on 2026-09-15 that an existing Session pins its creation-time MPA Profile revision and uses a newer revision from the next Turn only after a successful explicit upgrade; an active Turn never switches. After Studio accepts MPA creation, the UI shows deploying and reports runnable only after Runtime, Profile application, and execution-ready smoke all pass. Both are P0 acceptance constraints. - -## Change record - -2026-09-15: Removed Managed Agent and duplicate template/expert/version products; established Studio authoring authority, Runtime Profile-revision authority, and explicit execution-config, API, idempotency, state-machine, data-model, and verification contracts. - -2026-09-15: Implemented the execution-config storage/API foundation in `agentkit-mpa-agent`: `session_execution_configs` schema version 4, append-only store, per-session PostgreSQL advisory lock for CAS, `GET/PATCH /api/v1/sessions/{sessionId}/execution-config`, and the P0 `model`/`mcpServers` patch subset. - -2026-09-15: Added immutable `SessionExecutionConfigSnapshot` as the handoff value for future Turn acceptance. - -2026-09-15: Implemented the explicit Session Profile upgrade API/service subset for higher applied MPA Profile revisions, including ETag/CAS, downgrade rejection, unavailable-target rejection, override preservation, and current-process idempotent replay. - -2026-09-15: Verified the implemented S2 Runtime subset with `uv run pytest -q tests/test_session_execution_config.py tests/test_session_execution_config_service.py tests/test_agentkit_session_auth.py tests/test_agent_config_service_unit.py tests/test_agent_config_store.py tests/test_profile_apply.py tests/test_profile_apply_api.py tests/test_sessions.py::test_create_get_include_mpa_profile_pointers_when_supplied tests/test_session_mcp_update.py` (46 passed), focused Ruff, `make test-postgres` (5 passed), and pre-documentation `git diff --check`. - -2026-09-16: Implemented `turn_execution_records` schema version 5, SQL/in-memory acceptance store, REST `/run` MPA idempotency and config-version enforcement, A2A execution metadata acceptance, dispatch status progression, and Studio/BFF run metadata forwarding. Verification covered Runtime focused S3 regression (64 passed), Runtime Ruff, Runtime `make test-postgres` (6 passed), VeADK MPA server/integration pytest (47 passed), BFF bridge pytest (3 passed), frontend focused source-contract tests (14 passed), full frontend tests (1212 passed), i18n, build, and two-repository diff hygiene. - -2026-09-16: Implemented the accepted-turn recovery portion of S3-03. `turn_execution_records` now persists `dispatch_payload`, `SessionService.initialize()` rehydrates missing/error inbox rows from active or retryable Turn records and schedules normal workers, already-terminal inboxes synchronize back to Turn status, and lease-expired inbox rows mark linked Turns `failed_retryable`. Verification covered Runtime S3 regression (72 passed), Runtime Ruff, Runtime `make test-postgres` (6 passed), and diff hygiene. - -2026-09-16: Implemented S3-04/S3-05/S3-06 refresh recovery. Runtime SSE rejects unknown non-empty cursors with `410 cursor_expired`; Studio restores persisted Runtime Agent and Session IDs after browser refresh, hydrates the selected Session, and sends `lastEventId` into MPA Runtime `/sse`. Real-browser `BC-06` passed with system Google Chrome via Playwright; raw evidence was captured locally under ignored `evidence/browser/mpa-s3-1789516481/BC-06/`. - -2026-09-16: Implemented S4-01 participant state foundation. Runtime now has `a2a_turn_participants` schema version 6 plus SQL/in-memory stores for per-generation participant registration, owner-scoped lease heartbeat, generation-checked safe-point ACK, terminal marking, and ordered generation snapshots. - -2026-09-16: Implemented the S4-02 participant pause-barrier subset. Runtime pause requests now copy the current active participant set, A2A primary and Codex worker safe-point ACKs aggregate through `ParticipantBarrierService`, partial ACK remains `pausing`, all current-generation active participants advance the task to `paused`, late paused-generation workers receive pause before resume, terminal workers stop blocking future pauses, and resume seeds the next running generation. Verification covered Runtime S4-02 focused regression (27 passed), focused Ruff, and Runtime `make test-postgres` (7 passed). - -2026-09-16: Implemented the S4-03 lease-aware resume boundary. Runtime now returns same-Turn resume only inside the 300-second paused window when the owner lease remains viable and participant state is resumable. It returns `new_turn_required` for pause age greater than 300 seconds, owner lease loss, participant `lost|failed`, or expired/missing running participant lease, and records the resume key for idempotent replay. A valid still-running participant blocks resume with `invalid_state` until it ACKs or expires. Verification covered S4-03 focused boundary tests (8 passed), broader S4 control regression (104 passed), focused Ruff, and Runtime `make test-postgres` (7 passed). - -2026-09-16: Implemented the S4-04 Runtime-owned linked continuation API/outbox. Runtime now accepts `POST /api/v1/a2a/tasks/{taskId}/continue` after `new_turn_required`, writes a linked `turn_execution_records` row through `accept_continuation`, reuses the frozen snapshot and dispatch payload, terminates the old active dispatch row, and schedules the existing inbox worker. Verification covered S4-04 focused tests (4 passed), broader S4 control/acceptance regression (125 passed), focused Ruff, and Runtime `make test-postgres` (8 passed). - -2026-09-16: Implemented the S4-06 RuntimePrincipal revocation and side-effect recheck subset. Runtime now exposes shared authorization helpers plus an injectable authorizer seam; `/run`, resume, and `/continue` recheck the current grant before accepting side effects. Accepted Turn/inbox records carry a non-secret principal snapshot for background execution and restart recovery. ADK before-tool callbacks, direct sandbox dispatch, model/workload TIP resolution, OpenViking API-key resolution, and sandbox `begin_run` all recheck before using a secret or external tool effect. Verification covered S4-06 focused auth/secret/tool tests (46 passed), broader S4 Runtime regression (206 passed), and focused Ruff. Real AgentKit revocation lookup remains a deployed S5 release gate. - -2026-09-16: Closed the S4-07 lifecycle/PostgreSQL/browser gate. Runtime focused auth/secret/tool tests returned 46 passed, and `make test-postgres` returned 8 passed with PostgreSQL resources cleaned. The VeADK loopback `turn_lifecycle` scenario now exercises Session/task control status, pause/resume, `new_turn_required`, explicit `/continue`, continuation SSE, call counts, and persisted state evidence. Real-browser `BC-09` passed with system Google Chrome via Playwright; raw evidence was captured locally under ignored `evidence/browser/mpa-s4-1789532050/BC-09/`. - -2026-09-16: Implemented the S5-01 secret-reference migration foundation. Runtime schema version 7 adds secret reference and migration status columns to `mpa_agents`. `AgentConfigService` rejects new plaintext `agentApiKey`/`modelApiKey` writes in AgentKit mode while accepting reference fields. `SecretReferenceMigrationService` creates protected references before clearing plaintext, preserves plaintext when the provider or database update fails, and is retryable with the same idempotency key. Runtime model and built-in MCP paths can resolve references at execution-time secret-use boundaries; missing resolvers fail closed instead of falling back to another credential. - -2026-09-16: Implemented the S5-01a canary-safe redaction subset. Secret-reference migration registers legacy plaintext values for value-aware redaction before external reference creation. API access logs, Session visible text/events, Runtime Console responses, lifecycle details, and trace-step persistence redact registered canary literals even under non-sensitive keys, while preserving `flow_id` and `user_code`. Verification covered focused S5 redaction pytest (89 passed), broader S5 Runtime regression (178 passed), focused Ruff, and `make test-postgres` (9 passed). - -2026-09-16: Implemented the S5-04 Runtime Console/Trace correlation subset. Invocation trace responses and individual trace steps now expose stable `correlation` fields for MPA instance, Profile/config revisions, Turn/task/runtime identity, and worker linkage. Verification covered Runtime Console/Trace pytest (72 passed) and focused Ruff. - -2026-09-16: S5-06 compatibility preflight is a Studio/VeADK write-path guard and does not change the `agentkit-mpa-agent` Runtime API or database schema. The Runtime contract remains the already implemented Profile, Session execution-config, Turn acceptance, lifecycle, secret-reference, and diagnostics contracts above; final release evidence still depends on the pinned cross-repository manifest in VC-19. - -2026-09-16: S5-07 delete preview and staged cleanup is also a Studio/VeADK orchestration slice and does not require a new `agentkit-mpa-agent` table or endpoint. Studio reuses Runtime `GET /api/v1/agents/{mpaInstanceId}/profile-status`, `GET /api/v1/sessions?include_a2a=true`, and `DELETE /api/v1/sessions/{sessionId}` before calling the AgentKit Runtime delete API. Studio treats `queued|running|pausing|paused|resuming` Sessions as active blockers and treats per-session delete `404` as already cleaned for retry safety. The current contract can only prove the sessions visible through the authorized target Runtime API; a future cross-user global active-session guarantee would require a separate Runtime/global admin API and is not claimed by S5-07. - -2026-09-17: Implemented the S5-08 MPA Session creation initialization fix. Runtime now creates the first `session_execution_configs` revision during `POST /api/v1/sessions` when Studio supplies `mpaInstanceId + profileRevision` and omits an explicit `executionConfigRevision`, then returns `executionConfigRevision=1` in the create response. Explicit caller-supplied revisions remain preserved for migration/recovery callers. Verification covered focused Runtime tests (186 passed), focused Ruff, and a released Runtime image `agentkit-platform-2112682748-cn-beijing.cr.volces.com/agentkit/mpa_agent:mpa-p0-sessioncfg-init-local-20260917` on Runtime `r-yeuujrrcowb21078p9jh` version 58. Live BFF/runtime checks showed native Session create returning revision 1, `/run` accepting `executionConfigVersion=1`, and `/sse` emitting final text plus `event: done`. - -2026-09-18: Implemented the S5-11 execution-smoke hardening used by live lifecycle validation. Runtime readiness now provisions the Worker endpoint through AgentKit Sandbox, supports OpenAPI tool authentication mode for AgentKit-created tools, skips shared mount lookup for smoke-only `local_ephemeral` sessions, polls Worker turn detail to terminal, replays final events with `follow=false`, preserves cleanup diagnostics, and surfaces structured `worker` failure detail from `/api/v1/readiness/execution-smoke`. This adds no database table or new public Runtime API; it extends readiness behavior and the internal Codex Worker client. Focused Runtime tests returned 106 passed, focused Runtime Ruff passed, and VeADK VC-21 live run `vc21-20260918-045` passed with zero residue. - -2026-09-17: Implemented schema version 8 and completed the durable Runtime operation/Profile CAS contract. SQL and in-memory operation stores now enforce the same 24-hour identity/replay policy, persist redacted safe results, and compare-and-swap state transitions. Profile apply uses the authenticated principal and full normalized payload hash, maps all P0 Profile resource categories, and performs ETag validation plus current-revision switching in one PostgreSQL transaction. A real two-connection contract proves that only one concurrent writer can consume a given ETag. - -2026-09-18: Aligned legacy ADK endpoint authentication with AgentKit key-auth in Runtime commit `eecc6e3`. In AgentKit mode, `require_auth` now derives the Runtime principal from `Authorization`; outside AgentKit mode it retains the existing `X-Jwt-Token` behavior. Focused tests returned 49 passed, full `make test` returned 2506 passed and 14 skipped, and focused Ruff plus diff hygiene passed. The resulting image `agentkit-platform-2112682748-cn-beijing.cr.volces.com/agentkit/mpa_agent:mpa-p0-adk-auth-eecc6e3-20260917` (`sha256:6ac6a2712ecd1c7950125dc9afc6467373a08142fbd6c3577aba12f126079bef`) is deployed as Runtime `r-yeuujrrcowb21078p9jh` version 62 and is `Ready`; direct and Studio BFF `/list-apps` calls both returned `200 ["default"]`. No Runtime schema or persisted model changed. - -2026-09-18: Live Studio acceptance against Runtime version 62 created Session `bee3db38-edcc-4020-b925-3d9d6a3a7adc` and executed `printf 'MPA_V62_BROWSER_OK\n'` in the sandbox. The parent sandbox activity and child command activity both reached `completed`; the final answer remained visible at completion and after 30 seconds, and a full refresh restored the answer and terminal tool cards with no spinner or running text. Raw evidence was captured locally under ignored `evidence/browser/mpa-v62-live/`. This validates the exercised chat-display path only and does not close the full S5-12 `BC-01`–`BC-09` gate. - -2026-09-19: Rebased onto Runtime `origin/master` and recorded independently merged capabilities. Mainline channel APIs/services provide encrypted Feishu, DingTalk, and WeCom binding and delivery; deployment modules provide shared APIG, database, Runtime, and Skill-Space provisioning; ADK streaming replays complete artifact answers once. These capabilities are implemented and tested in mainline, but remain outside this P0 ledger's provider/deployment acceptance. diff --git a/specs/mpa-runtime-control/README.zh.md b/specs/mpa-runtime-control/README.zh.md deleted file mode 100644 index b3867a9d3..000000000 --- a/specs/mpa-runtime-control/README.zh.md +++ /dev/null @@ -1,193 +0,0 @@ -# MPA Runtime 控制 - -- Component ID:`mpa-runtime-control` -- 状态:`draft`;S1、S2、S3、S4、S5-01、S5-01a、S5-04、S5-08 Session 创建修复与 S5-11 真实生命周期验证已实现或记录;S5-06 与 S5-07 no-impact 记录已补充;其余 S5 仍为目标契约 -- 修订日期:2026-09-19 -- English:[README.md](README.md) -- PRD:[MPA AgentKit P0 功能迁移](../../prd-spec/features/mpa-p0-productionization/2026-09-15-mpa-p0-productionization-design.zh.md) -- 负责代码:`agentkit-mpa-agent` 的 Agent、Session、A2A、worker、鉴权与诊断模块 - -## 职责与权威边界 - -AgentKit Studio 持有 MPA 创作入口以及 Skill、Tool、Environment、Vault 等平台资源。本期不使用 Managed Agent CRUD/version/Session。mpa-agent 持有不可变的可执行 Profile revision、会话执行配置版本、Turn 执行记录、参与方状态和诊断关联,不提供模板、专家、审核安装或独立发布服务。Studio/BFF 是客户端和编排层,不成为执行状态权威。 - -代码基线 `2f5e039` 已有 Agent config revision、Session CRUD/run/SSE/events/MCP、A2A generation/lease 与 pause/resume、worker interrupt、health/readiness 和 Runtime Console,这些是复用点。当前 P0 分支已实现下文所述 S1 Profile/readiness/auth 基础、S2 Session execution-config/Profile-upgrade Runtime 子集、S3 Turn acceptance、dispatcher recovery 与刷新 cursor 子集、S4-01/S4-02 的 participant 数据模型和 pause-barrier 子集、S4-03 lease-aware resume 边界、S4-04 Runtime linked continuation API/outbox、S5-08 MPA Session 创建初始化修复,以及下文记录的 S5-11 execution-smoke Worker endpoint 接线。删除与生命周期验证的 Studio 控制接线记录在 Studio 控制面 spec 中。 - -Runtime 主干还由其他 feature 独立提供加密多渠道绑定、shared APIG 控制面支撑、持久部署记录、隔离 PostgreSQL database 创建、Runtime 部署和每个逻辑 Agent 一个持久 Skill Space。这些是由独立渠道/部署契约管理的可复用基础能力,不改变 P0 权威边界:Profile、Session execution configuration、Turn、生命周期和诊断状态仍由 Runtime 持有,也不能因这些能力存在就关闭剩余 P0 证据门禁。 - -## CON-1:身份与授权 - -P0 统一使用 `RuntimePrincipal v1`:`iss`、`aud`/authorized client、`sub`、`account_id`、`workspace_id`、`agent_id`、`actions[]`、`iat`、`nbf`、`exp`、`jti`,允许时钟偏差 30 秒。Studio OAuth/gateway 中间件先验证用户 bearer token,BFF 只转发这份已验证 token;AgentKit Runtime custom JWT authorizer 和 mpa-agent 通过同一 issuer discovery/JWKS 按 `kid` 校验签名、轮换、audience/client 和时效。token TTL 由现有 issuer 策略决定,M0 必须记录实测值,不能在 BFF 自签或假定 5 分钟。每次副作用前都通过可注入的 `RuntimePrincipalAuthorizer` 接线查询当前授权/撤权状态。不得信任浏览器 owner/account Header,也不得把 Runtime key-auth 当最终用户身份。 - -REST 使用 `Authorization: Bearer `;A2A 使用 `X-Ve-TIP-Token`,但 TIP verifier 必须映射出同一 `RuntimePrincipal`,不能只保留 `sub`。现有 `X-Jwt-Token` 仅作为旧 capability 路径:缺少 v1 scope claim 的请求可读取旧资源,但不能调用 Profile、execution-config、run/continue 等新写接口。`DISABLE_JWT_AUTH` 只允许本地测试 profile,AgentKit mode 启动时若开启则 execution-ready 失败。 - -当 `MPA_AGENTKIT_MODE=true` 时,仍依赖 `require_auth` 的旧 ADK endpoint(包括 `GET /list-apps`)复用同一个 AgentKit `Authorization` Runtime principal,不再单独强制要求 `X-Jwt-Token`。这是 adapter 兼容规则,不是第二套身份机制。非 AgentKit mode 继续保持既有 `X-Jwt-Token` 契约。无论直连 Runtime 还是经过 Studio BFF proxy,缺失或非法的 AgentKit 凭据都必须 fail closed。 - -在 Profile apply、Session 创建/读取、执行配置修改、Turn 接受、resume/continue、Secret 解析和外部工具 dispatch 前检查对应 scope。已实现的 S4-06 子集只在 accepted Turn dispatch payload 与 inbox metadata 中持久化规范化的非 secret `RuntimePrincipal` 快照,使后台执行和重启恢复无需保存 bearer token 也能复核授权。撤权阻止后续副作用;已发出的外部副作用不声称可撤销。普通响应、日志、trace、错误和诊断不包含 Secret 原值。已实现的 S5-01a 子集会把 Secret reference migration 读到的 legacy 明文 Secret 字面值注册到进程内脱敏 registry,然后在 API access log、Session 对用户可见输出/events、Runtime Console 响应、lifecycle details 和 trace-step 持久化中遮盖这些精确值。该 value-aware redaction 会保留 `flow_id`、`user_code` 等非 Secret 业务标识。 - -## CON-2:Studio MPA Profile 执行投影 - -`PUT /api/v1/agents/{mpaInstanceId}/profile` 的路径沿用现有 runtime 实例 ID,只接受 BFF 从 Studio 已接受 draft 校验并规范化的 Profile。请求包含 `sourceProfileId`、digest 及 name/description/system/model/tools/skills/MCP/Multiagent/allowlisted metadata。BFF 不分配 revision;Runtime 在成功 CAS 事务中分配并返回唯一 `profileRevision`。首次应用必须使用 `If-None-Match: *`,后续应用必须使用 `If-Match: ""`;两者同传返回 `400 invalid_precondition`,均未传返回 `428 precondition_required`。成功响应和 status 响应返回强 ETag。 - -runtime 将 Profile 映射到新的 `mpa_agents` revision,并保存 source Profile ID/digest 和应用状态。相同 operation key/digest 返回原分配 revision;stale ETag 返回 `412 profile_changed`;对已分配 revision 用不同内容重放返回 `409 profile_version_conflict`;未知 Tool/Skill/Multiagent 结构为 `422 unsupported_profile_field`。`GET /api/v1/agents/{mpaInstanceId}/profile-status` 返回 `pending|applying|applied|failed`、目标/已应用 revision、runtime revision、operation ID、安全错误和 retryable。 - -## CON-3:会话执行配置版本 - -“会话执行配置版本”是单个 Session 的递增执行选择记录,并非平台资源副本。它组合固定的 MPA Profile revision 与会话覆盖,只保存 Skill/Tool/MCP/Environment/Knowledge/Vault 等资源 ID、明确版本或 digest、模型覆盖、权限 revision、失效引用和更新时间;资源正文与 Secret 仍由 AgentKit 管理。 - -runtime 持久化契约是 append-only。`session_execution_configs` 以 `(app_name, session_id, revision)` 为主键,保存强 `etag`、`mpa_instance_id`、`profile_revision`、`profile_default_revision`、JSON `overrides`、JSON `effective_refs`、JSON `invalid_refs`、`updated_by`、`created_at`、`updated_at`。当前版本读取使用 `(app_name, session_id, revision DESC)`。SQL 写入先用 PostgreSQL advisory transaction lock 串行化单个 Session 的 revision 流,再比较调用方 ETag 并追加 revision N+1;已有行永不原地更新。 - -`POST /api/v1/sessions` 在请求携带 `mpaInstanceId + profileRevision` 且没有显式携带 `executionConfigRevision` 时,会初始化第一条 execution-config revision。响应随后返回 `executionConfigRevision=1`,使 Studio 可以立即把合法版本传给第一次 MPA `/run`。如果恢复或迁移调用方显式传入 `executionConfigRevision`,Runtime 保留该值。 - -`GET /api/v1/sessions/{sessionId}/execution-config` 返回 revision 与强 ETag。`PATCH` 必须携带 `If-Match`,按资源类别执行 replace、clear 或 inherit。匹配则原子地产生下一 revision;缺失为 `428`;冲突为 `412 execution_config_changed` 并返回安全的当前 revision;校验失败不产生 revision。修改只影响下一 Turn,活动 Turn 不变。`POST /api/v1/sessions/{sessionId}/profile-upgrade` 携带 `targetProfileRevision`、同一 ETag 与 `Idempotency-Key`;目标 Profile 必须已 applied。活动 Turn 可存在,但新 revision 只在下一 Turn 生效;降级返回 `409 downgrade_not_allowed`;并发冲突返回 `412`。 - -已实现的 S2-03 子集支持 `model` 和 `mcpServers` 类别变更。其他类别在对应 AgentKit 资源 resolver 存在前返回 `422 unsupported_execution_config_category`。未知 MCP ID 返回 `422 resource_unavailable`,且不创建 revision。不带 `mpaInstanceId + profileRevision` 的非 MPA Session 返回 `404 execution_config_not_found`,不会隐式创建 MPA 状态。 - -已实现的 S2-05 Profile 升级子集暴露 `POST /api/v1/sessions/{sessionId}/profile-upgrade`。该接口要求 `Idempotency-Key` 和当前 execution-config `If-Match`,只接受更高且已 applied 的 `targetProfileRevision`,保留显式 override,并基于目标 Profile 重新计算 inherit 默认值。schema version 8 的 SQL `runtime_operations` ledger 会持久化 request hash 与安全结果,因此相同 key/相同目标可跨 Runtime 进程 replay;相同 key/不同目标返回 `409 idempotency_mismatch`。 - -Turn 接受前,Runtime 会把选定的 execution-config revision 解析为不可变 `SessionExecutionConfigSnapshot`。snapshot 包含已接受的 revision 与 ETag、MPA instance、Profile revision、Profile 默认 revision、overrides、effective references、invalid references 和 accepted timestamp。它是 Turn acceptance 与 trace 关联使用的值对象,不是第二份可变配置记录。 - -示例:Session revision 6 使用 Skill `s-1@3`。客户端 A 把模型改为 M2,生成 revision 7;客户端 B 仍基于 ETag 6 更新 Skill,收到 412。正在运行的 Turn 仍使用 revision 6,之后新 Turn 使用 revision 7。 - -## CON-4:Turn 接受与幂等 - -`POST /api/v1/sessions/{sessionId}/run` 显式接受 `executionConfigVersion` 与 `Idempotency-Key`。A2A Agent Card 声明 `urn:veadk:mpa:execution:v1`,`params.metadata.veadkExecution` 携带同名字段;缺失扩展的旧调用只允许默认 revision 且不允许 Session override。runtime 在事务外解析资源版本,在事务内比较 Session revision/权限版本、占有活动 Turn、写入不可变 `turn_execution_records` 和 dispatch intent,再触发外部执行。物化时复核资源版本/digest 和权限;漂移在副作用前失败。 - -Run 幂等范围为 `user_id + app_name + session_id + source + Idempotency-Key`,由 `turn_execution_records` 唯一约束,并保存规范化 request hash、冻结 config snapshot、`accepted|dispatching|succeeded|failed_retryable|failed_terminal` 和持久 `turnId/invocationId/operationId`。相同 key/hash 返回原 accepted invocation;相同 key 不同 payload 返回 `409 idempotency_mismatch`。pending Profile 不成为 `mpa_agents` current revision;成功事务才切换 applied revision。崩溃恢复复用 dispatch key,不能产生第二个 Turn。 - -已实现的 S3 子集使用 `turn_execution_records` 作为持久 run-acceptance ledger。REST `/run` 对 MPA Session 要求幂等键和请求的 execution-config revision,保存冻结的 `SessionExecutionConfigSnapshot`,返回 `turnId`、`operationId` 和 `executionConfigRevision`,并保持非 MPA Session 的旧响应形态。A2A 通过 `params.metadata.veadkExecution` 接受相同 metadata,把已接受的 invocation ID 绑定到 ADK request,将 dispatch status 推进到 `dispatching`,再记录 `succeeded` 或 `failed_retryable`。相同 A2A key/hash replay 返回 completed 幂等重放提示,不再次执行模型。`dispatch_payload` 支持重启后重建缺失/error inbox,不引入第二套 dispatcher。Studio 会把上一条已完成事件 ID 传给 Runtime SSE,使浏览器刷新后从 cursor 之后续接而不是从头重播。 - -## CON-5:完整 Turn 生命周期与 continuation - -状态为 `queued -> running -> pausing -> paused -> resuming -> running -> completed|failed|cancelled`。`a2a_task_controls` 保持聚合 generation;每个活动主执行器/worker 在 `a2a_turn_participants` 登记 lease、状态、安全点和 checkpoint。只有同 generation 的所有活动参与方确认后才是 `paused`;部分确认保持 `pausing` 或产生分类超时。 - -已实现的 S4-02 子集在存在 `a2a_turn_participants` 时通过 `ParticipantBarrierService` 处理 pause 请求。pause 命令推进聚合 task generation,并从上一 generation 复制活跃参与方。单个 participant ACK 只更新自己的行;只有当前 generation 的所有活跃参与方都处于 `paused` 或终态后,服务才调用 task-level `acknowledge_safe_point` 推进聚合状态。旧 generation 或 owner 不匹配的 ACK 被忽略。若 Codex worker 在聚合 task 已经 `paused` 后才启动,它仍会登记到当前 generation,收到 worker pause,ACK 当前 generation,并等待 resume 后再继续。completed、failed 或 cancelled 的 worker turn 会把 participant 行标为终态,因此后续 pause generation 不会等待已经结束的 worker。resume ACK 后,Runtime 会从上一代 paused generation 生成下一代 running participant 记录。 - -resume 时重查权限与参与方可恢复性。已实现的 S4-03 边界只在 task 暂停不超过 300 秒、task owner lease 仍有效,且 participant generation 中没有 `lost|failed` participant,也没有 lease 过期/缺失的 running participant 时恢复原业务 Turn。仍处于 running 但 lease 有效的 participant 会让 resume 返回 `409 invalid_state`,要求调用方等待安全点;它不会被直接转为 continuation。超过 300 秒、owner lease 丢失、participant lost/failed 或重启后无法恢复时返回 `new_turn_required`。`new_turn_required` 转换会记录 resume 幂等 key,相同 key 可安全 replay。 - -`POST /api/v1/a2a/tasks/{taskId}/continue` 只有在用户显式操作后调用。已实现的 S4-04 Runtime API 要求 `Idempotency-Key` 与 `expectedGeneration`,只接受 `interrupted|orphaned` 控制状态,通过旧 task invocation 找到上一条业务 Turn,并插入带 `continuation_of=` 的 linked `turn_execution_records` 行。continuation 复用冻结的 execution-config snapshot 和 dispatch payload,关闭旧 active dispatch 行,并通过 `enqueue_once` 调度现有 inbox/outbox worker。相同 key/相同请求返回同一 continuation;相同 key 不同请求返回 `idempotency_mismatch`。不承诺与 SDK `DatabaseTaskStore` 的分布式 ACID;已接受的 continuation dispatch 继续由现有 outbox 恢复。S4-05 接线完成后,Studio 不再拼 prompt 调用普通 send。 - -## CON-6:事件、刷新与诊断 - -SSE 重放复用 ADK 持久 Session events 和 inbox terminal fence,内存 StreamHub 只负责低延迟直播,不是刷新后的权威。cursor 为持久 event ID;服务端按 Session events 的持久顺序从游标后重放,随后接入 live stream。找不到非空 cursor 时返回 `410 cursor_expired`,不再从头静默重放。相同 event/invocation/type 去重,不重复累计文本、usage 或 artifact;缺少 terminal 保持未知/失败态而非成功。 - -Runtime Console/trace 复用现有存储,并提供 MPA instance、Profile revision、session execution-config revision、Turn、worker run/session 的结构化关联。详情统一脱敏,有界返回,不通过解析任意 JSON 才能定位核心链路。已实现的 S5-04 子集在 invocation trace 响应和每个 trace step 上暴露规范化 `correlation` 对象,从已脱敏 details 中抽取 MPA instance、Profile revision、execution-config revision/ETag、业务 Turn、A2A task、Runtime ID/version,以及 Codex/subagent worker session/turn/profile 标识。 - -执行配置和事件持久化只服务 mpa-agent 的业务 Session。P0 不创建 AgentKit Managed Agent Session,也不建立双 Session ID 映射;AgentKit `AgentWithOverrides` 仅作为平台能力记录,不进入本契约。 - -## CON-7:API 与错误 - -| API | 状态 | 契约 | -| --- | --- | --- | -| `PUT /api/v1/agents/{mpaInstanceId}/profile` | 新增 | 始终要求 `Idempotency-Key`;首次只用 `If-None-Match: *`,后续只用 `If-Match: ""`,二者互斥;payload 携带 Studio Profile 的 `sourceProfileId` 和 `sourceProfileDigest`,不携带调用方分配的 revision | -| `GET /api/v1/agents/{mpaInstanceId}/profile-status` | 新增 | Profile 应用状态与可恢复 operation | -| `GET /api/v1/sessions/{sessionId}/execution-config` | 新增 | 权威会话执行配置版本与 ETag | -| `PATCH /api/v1/sessions/{sessionId}/execution-config` | 新增 | CAS 更新,影响下一 Turn | -| `POST /api/v1/sessions/{sessionId}/profile-upgrade` | 新增 | 显式 Session Profile 升级;目标 Profile 必须已应用 | -| `POST /api/v1/sessions/{sessionId}/run` | 扩展 | 增加 `executionConfigVersion` 与强幂等 | -| A2A submission | 扩展 | `urn:veadk:mpa:execution:v1` extension 使用同一 Turn 接受服务 | -| `GET/POST /api/v1/a2a/tasks/{taskId}/control...` | 扩展 | 保留 URL,增加参与方聚合与 request-hash 幂等 | -| `POST /api/v1/a2a/tasks/{taskId}/continue` | 新增 | runtime-owned 关联续跑 | -| `GET /api/v1/runtime-operations/{operationId}` | 新增 | 按 principal/target 授权查询 Profile/run/upgrade/continue 的异步状态和可安全重放结果 | -| Session CRUD/SSE/events/MCP/health/readiness/console | 复用 | 补 scope、cursor 与关联字段 | - -新接口错误体为 `error.code/message/requestId/retryable/currentState`。旧客户端按 capability 版本保持现有 envelope;旧 Runtime 保持可发现并显示 read-only/unsupported,AgentKit P0 不允许关闭鉴权或退回浏览器/ArkClaw 权威。 - -Execution readiness 复用现有 `POST /api/v1/readiness/execution-smoke` endpoint。AgentKit mode 下,它通过 Sandbox manager 获取真实 Codex Worker endpoint,先校验 `/v1/codex-worker/ready`,再创建隔离 Worker session、启动一次 smoke turn、轮询 `GET /v1/codex-worker/sessions/{sessionId}/turns/{turnId}` 到终态,并用 `follow=false` 回放最终 events。marker 命中只作为诊断字段,因为 Worker 可能汇总命令输出;readiness 要求 Worker Turn 终态和可读 replay events。错误继续按阶段分类并脱敏。 - -## CON-8:数据模型 - -| 模型 | 拟议调整 | -| --- | --- | -| `mpa_agents` | 增加 source Profile ID/digest、apply status/operation/error、`agent_api_key_ref`、`model_api_key_ref`、`secret_migration_status`、`secret_migration_operation_id`;现有 Runtime 分配的 revision 是唯一 `profileRevision` | -| `mpa_meta` | 增加 AgentKit mode、workspace、bootstrap revision、readiness phase、finalization operation、last error | -| `session_meta` | 增加 account/workspace/MPA instance/Profile/execution-config revision | -| `session_execution_configs` | 新 append-only 表;保存 `(app_name, session_id, revision)`、强 ETag、MPA instance、Profile revision、Profile 默认 revision、overrides/effective/invalid references、updater 和 timestamps | -| `turn_execution_records` | 新表;保存 accepted config、resolved refs、model/policy/secret refs、dispatch key/hash/status、`continuation_of` | -| `a2a_turn_participants` | 新表;保存每个 generation 的参与方、lease、安全点、checkpoint、ACK/终态时间 | -| `a2a_task_controls` | 增加 command request hash 和 result reference;只有真实 P0 审批流需要时才增加 approval 字段 | -| `runtime_operations` | 新表;保存 Profile 生命周期和后续长耗时控制操作的唯一 principal/operation/target/key、request hash、状态、response snapshot、resource IDs、expiry;run acceptance 使用 `turn_execution_records`,execution-config PATCH 使用 revision/ETag,pause/resume 使用 `a2a_task_controls`,不重复写 ledger | -| trace/lifecycle | 按需增加可索引的 task/config/runtime/worker 引用 | - -不新增 Agent definition/version、template/expert、review/install、独立 continuation、通用 policy evaluation 表。已实现的 S5-01 子集向 `mpa_agents` 增加 `agent_api_key_ref`、`model_api_key_ref`、`secret_migration_status`、`secret_migration_operation_id`。AgentKit mode 禁止向现有 `agent_api_key`、`model_api_key` 明文字段写入新值。已有新平台值按 reference-first 方式迁移:先用稳定幂等键创建外部 Secret reference,再在一个数据库事务内清空明文;provider 或数据库更新失败时保留明文,保证可重试;成功后同一 MPA Agent 的行只保留引用。Runtime 执行在 Secret 使用边界解析 `agentApiKeyRef`/`modelApiKeyRef`,resolver 缺失时 fail closed,不降级使用其他凭据。S5-01a 增加进程内 value-aware redaction,用于迁移时读到的 legacy 明文值以及未来显式注册的 Secret 字面值。已注册字面值会在嵌套值和自由文本进入 API/log/Runtime Console/trace 暴露前被遮盖,而非 Secret 引用与业务参数保持可见。Runtime operation response/resource snapshot 使用同一脱敏边界,状态接口不返回 principal、幂等键或 request hash。旧 mode 在一个兼容版本内只读明文字段,下一主版本删除明文读路径。所有结构变更使用显式顺序 migration,SQL 与 in-memory store 同步;不扩展 AgentKit 自有表,不迁移 ArkClaw 数据。 - -migration 顺序为 `runtime_operations -> mpa_agents/mpa_meta/session_meta 增量列 -> session_execution_configs -> turn_execution_records -> a2a_turn_participants -> 索引/约束`,每步记录 schema version。先扩 schema,再上线双版本可读代码,最后启用新写路径;旧镜像无法安全读取时拒绝回滚。 - -## CON-9:性能与保留 - -- Profile apply、run、pause/resume/continue 接受:p95 ≤ 500 ms;异步完成通过 operation/status 查询。 -- execution-config GET/PATCH:p95 ≤ 300 ms;远程资源查询不持有数据库锁。 -- warm Runtime SSE 首事件:p95 ≤ 2 s,不含模型首 token SLA。 -- list/detail:p95 ≤ 1 s,默认 20、最大 100,禁止 N+1 平台扫描。 -- 幂等结果至少 24 小时;终态执行证据默认 30 天;活动 Session 和被引用配置版本不自动清理。具体生产保留策略在部署配置中显式设置。 - -## 验证 - -实现采用 TDD:先增加 Profile mapping/version/idempotency、execution-config CAS、run crash-recovery、multi-participant barrier、continuation、SSE replay、scope/redaction 测试,再实现。并新增真实 PostgreSQL 两连接/重启 lane,覆盖 CAS、唯一活动 Turn、outbox、migration 和双版本读取;内存 store 不能作为这些验收的最终证据。Runtime 起点为: - -```bash -uv run --group dev pytest \ - tests/test_session_auth.py \ - tests/test_subagent_binding_store.py \ - tests/test_a2a_task_control.py \ - tests/test_turn_control_api.py \ - tests/test_codex_adapter.py -``` - -该命令是实现阶段要求,本次文档整改状态为 `not_run`。真实集成必须覆盖 MPA Profile 应用、双客户端 CAS、故障重试、多 worker pause/resume、300 秒边界、重连重放和撤权。 - -## 已确认产品决策 - -用户于 2026-09-15 确认:既有 Session 固定创建时的 MPA Profile revision,只有显式升级成功后才从下一 Turn 使用新 revision;活动 Turn 不切换。Studio 接受 MPA 创建后显示部署中,只有 Runtime、Profile 应用和 execution-ready smoke 全部通过后才显示可运行。两项均为 P0 验收约束。 - -## 变更记录 - -2026-09-15:删除 Managed Agent 及重复模板/专家/版本产品;明确 Studio 创作权威、Runtime Profile revision 权威,以及执行配置、API、幂等、状态机、最小数据模型和验证要求。 - -2026-09-15:在 `agentkit-mpa-agent` 落地 execution-config 存储/API 基础:`session_execution_configs` schema version 4、append-only store、Session 维度 PostgreSQL advisory lock CAS、`GET/PATCH /api/v1/sessions/{sessionId}/execution-config`,以及 P0 的 `model`/`mcpServers` patch 子集。 - -2026-09-15:新增不可变 `SessionExecutionConfigSnapshot` 作为后续 Turn acceptance 的交接值对象。 - -2026-09-15:实现显式 Session Profile 升级 API/service 子集,支持升级到更高且已应用的 MPA Profile revision,并覆盖 ETag/CAS、禁止降级、目标不可用拒绝、保留 override 和当前进程内幂等 replay。 - -2026-09-15:已用 `uv run pytest -q tests/test_session_execution_config.py tests/test_session_execution_config_service.py tests/test_agentkit_session_auth.py tests/test_agent_config_service_unit.py tests/test_agent_config_store.py tests/test_profile_apply.py tests/test_profile_apply_api.py tests/test_sessions.py::test_create_get_include_mpa_profile_pointers_when_supplied tests/test_session_mcp_update.py`(46 passed)、focused Ruff、`make test-postgres`(5 passed)以及本次文档更新前的 `git diff --check` 验证已实现的 S2 Runtime 子集。 - -2026-09-16:实现 `turn_execution_records` schema version 5、SQL/in-memory acceptance store、REST `/run` 的 MPA 幂等与 config-version 强校验、A2A execution metadata acceptance、dispatch status 推进,以及 Studio/BFF run metadata 转发。验证覆盖 Runtime S3 定向回归(64 passed)、Runtime Ruff、Runtime `make test-postgres`(6 passed)、VeADK MPA server/integration pytest(47 passed)、BFF bridge pytest(3 passed)、前端 source-contract 子集(14 passed)、完整前端测试(1212 passed)、i18n、build 和两仓 diff hygiene。 - -2026-09-16:实现 S3-03 中 accepted-turn recovery 子集。`turn_execution_records` 现在持久化 `dispatch_payload`;`SessionService.initialize()` 从 active 或 retryable Turn 记录重建缺失/error inbox 并调度既有 worker;已终态 inbox 会同步回 Turn status;running inbox lease 过期时会把关联 Turn 标为 `failed_retryable`。验证覆盖 Runtime S3 回归(72 passed)、Runtime Ruff、Runtime `make test-postgres`(6 passed)和 diff hygiene。 - -2026-09-16:实现 S3-04/S3-05/S3-06 刷新恢复。Runtime SSE 对未知非空 cursor 返回 `410 cursor_expired`;Studio 在浏览器刷新后恢复已持久化的 Runtime Agent 和 Session ID,拉取所选 Session,并向 MPA Runtime `/sse` 传入 `lastEventId`。真实浏览器 `BC-06` 使用 system Google Chrome via Playwright 通过;原始证据已写入被忽略的本地 `evidence/browser/mpa-s3-1789516481/BC-06/`。 - -2026-09-16:实现 S4-01 participant 状态基础。Runtime 新增 `a2a_turn_participants` schema version 6,并补齐 SQL/in-memory store,支持按 generation 注册参与方、owner-scoped lease heartbeat、带 generation 校验的 safe-point ACK、终态标记和按 generation 有序快照。 - -2026-09-16:实现 S4-02 participant pause-barrier 子集。Runtime pause 请求现在会复制当前活跃 participant 集合;A2A 主执行器与 Codex worker safe-point ACK 通过 `ParticipantBarrierService` 汇总;部分 ACK 保持 `pausing`;当前 generation 所有活跃 participant ACK 后 task 进入 `paused`;late paused-generation worker 会先收到 pause 再 resume;终态 worker 不再阻塞后续 pause;resume 会生成下一代 running participant。验证覆盖 Runtime S4-02 focused regression(27 passed)、focused Ruff 和 Runtime `make test-postgres`(7 passed)。 - -2026-09-16:实现 S4-03 lease-aware resume 边界。Runtime 现在只在 300 秒暂停窗口内、owner lease 仍有效且 participant 状态可恢复时执行 same-Turn resume;暂停超过 300 秒、owner lease 丢失、participant `lost|failed`,或 running participant lease 过期/缺失时返回 `new_turn_required`,并记录 resume key 以支持幂等 replay。仍 running 且 lease 有效的 participant 会以 `invalid_state` 阻止 resume,直到它 ACK 或过期。验证覆盖 S4-03 focused boundary tests(8 passed)、broader S4 control regression(104 passed)、focused Ruff 和 Runtime `make test-postgres`(7 passed)。 - -2026-09-16:实现 S4-04 Runtime-owned linked continuation API/outbox。Runtime 现在在 `new_turn_required` 后接受 `POST /api/v1/a2a/tasks/{taskId}/continue`,通过 `accept_continuation` 写入 linked `turn_execution_records` 行,复用冻结 snapshot 和 dispatch payload,终止旧 active dispatch 行,并调度现有 inbox worker。验证覆盖 S4-04 focused tests(4 passed)、broader S4 control/acceptance regression(125 passed)、focused Ruff 和 Runtime `make test-postgres`(8 passed)。 - -2026-09-16:实现 S4-06 RuntimePrincipal 撤权与副作用前复核子集。Runtime 现在提供共享授权 helper 和可注入 authorizer 接线;`/run`、resume、`/continue` 在接受副作用前复核当前授权。accepted Turn/inbox 记录携带非 secret principal 快照,用于后台执行和重启恢复。ADK before-tool callback、直接 sandbox dispatch、模型/workload TIP 解析、OpenViking API key 解析和 sandbox `begin_run` 都会在使用 secret 或外部工具副作用前复核。验证覆盖 S4-06 focused auth/secret/tool tests(46 passed)、broader S4 Runtime regression(206 passed)和 focused Ruff。真实 AgentKit 撤权查询仍是部署态 S5 release gate。 - -2026-09-16:关闭 S4-07 生命周期、PostgreSQL 与浏览器门禁。Runtime focused auth/secret/tool tests 返回 46 passed,`make test-postgres` 返回 8 passed 且 PostgreSQL 资源已清理。VeADK loopback `turn_lifecycle` 场景已覆盖 Session/task control status、pause/resume、`new_turn_required`、显式 `/continue`、continuation SSE、调用计数和持久状态证据。真实浏览器 `BC-09` 使用 system Google Chrome via Playwright 通过;原始证据已写入被忽略的本地 `evidence/browser/mpa-s4-1789532050/BC-09/`。 - -2026-09-16:实现 S5-01 Secret reference migration 基础。Runtime schema version 7 向 `mpa_agents` 增加 Secret reference 与迁移状态列。`AgentConfigService` 在 AgentKit mode 下拒绝新的明文 `agentApiKey`/`modelApiKey` 写入,但允许写入 reference 字段。`SecretReferenceMigrationService` 先创建受保护引用,再清空明文;provider 或数据库更新失败时保留明文,使用同一幂等键可重试。Runtime 模型和内置 MCP 执行路径在 Secret 使用边界解析 reference;resolver 缺失时 fail closed,不回退到其他凭据。 - -2026-09-16:实现 S5-01a canary-safe redaction 子集。Secret reference migration 在创建外部引用前,会把 legacy 明文值注册为 value-aware redaction 字面值。API access log、Session 可见文本/events、Runtime Console 响应、lifecycle details 和 trace-step 持久化会在非敏感 key 下也遮盖已注册 canary 字面值,同时保留 `flow_id` 与 `user_code`。验证覆盖 S5 redaction focused pytest(89 passed)、broader S5 Runtime regression(178 passed)、focused Ruff 和 `make test-postgres`(9 passed)。 - -2026-09-16:实现 S5-04 Runtime Console/Trace correlation 子集。Invocation trace 响应和单个 trace step 现在暴露稳定的 `correlation` 字段,用于 MPA instance、Profile/config revision、Turn/task/runtime identity 和 worker linkage。验证覆盖 Runtime Console/Trace pytest(72 passed)和 focused Ruff。 - -2026-09-16:S5-06 compatibility preflight 是 Studio/VeADK 写路径门禁,不改变 `agentkit-mpa-agent` Runtime API 或数据库 schema。Runtime 契约仍是上文已实现的 Profile、Session execution-config、Turn acceptance、lifecycle、Secret reference 与 diagnostics;最终发布证据仍依赖 VC-19 中固定跨仓 manifest 的执行结果。 - -2026-09-16:S5-07 删除预览与分阶段清理同样是 Studio/VeADK 编排切片,不需要新增 `agentkit-mpa-agent` 表或 endpoint。Studio 复用 Runtime `GET /api/v1/agents/{mpaInstanceId}/profile-status`、`GET /api/v1/sessions?include_a2a=true` 和 `DELETE /api/v1/sessions/{sessionId}`,再调用 AgentKit Runtime delete API。Studio 将 `queued|running|pausing|paused|resuming` Session 视为 active blocker,并把单个 Session 删除时的 `404` 视为已清理以支持安全重试。当前契约只能证明已授权目标 Runtime API 可见的 session;若未来要保证跨用户全局 active-session 状态,需要另行新增 Runtime/global admin API,本次 S5-07 不声明该能力。 - -2026-09-17:实现 S5-08 MPA Session 创建初始化修复。Runtime 现在会在 `POST /api/v1/sessions` 收到 Studio 传入的 `mpaInstanceId + profileRevision` 且未显式传入 `executionConfigRevision` 时,创建第一条 `session_execution_configs` revision,并在创建响应中返回 `executionConfigRevision=1`。恢复或迁移调用方显式传入的 revision 仍会原样保留。验证覆盖 Runtime focused tests(186 passed)、focused Ruff,以及已发布到 Runtime `r-yeuujrrcowb21078p9jh` version 58 的镜像 `agentkit-platform-2112682748-cn-beijing.cr.volces.com/agentkit/mpa_agent:mpa-p0-sessioncfg-init-local-20260917`。真实 BFF/runtime 检查显示 native Session create 返回 revision 1,`/run` 可接受 `executionConfigVersion=1`,`/sse` 输出最终文本并发送 `event: done`。 - -2026-09-18:实现 S5-11 真实生命周期验证所需的 execution-smoke 加固。Runtime readiness 现在通过 AgentKit Sandbox 动态获取 Worker endpoint,支持 AgentKit 创建 Tool 使用 OpenAPI 鉴权模式,smoke-only 的 `local_ephemeral` session 不再读取 shared mount,Worker smoke 通过 turn detail 轮询到终态并用 `follow=false` 回放最终事件;cleanup 诊断会保留,`/api/v1/readiness/execution-smoke` 会返回结构化 `worker` 失败细节。本次不新增数据库表或新的公开 Runtime API,只扩展 readiness 行为和内部 Codex Worker client。Runtime 定向测试返回 106 passed,focused Runtime Ruff 通过,VeADK VC-21 live run `vc21-20260918-045` 以零残留通过。 - -2026-09-17:实现 schema version 8,完成持久 Runtime operation/Profile CAS 合约。SQL 与 in-memory operation store 现在执行一致的 24 小时 identity/replay 策略、保存已脱敏安全结果,并以 compare-and-swap 推进状态。Profile apply 使用认证 principal 与完整规范化 payload hash,映射全部 P0 Profile 资源类别,并在同一个 PostgreSQL 事务中完成 ETag 校验与 current revision 切换。真实双连接合约证明同一 ETag 只能被一个并发 writer 消费。 - -2026-09-18:在 Runtime commit `eecc6e3` 中完成旧 ADK endpoint 与 AgentKit key-auth 的鉴权对齐。AgentKit mode 下,`require_auth` 现在从 `Authorization` 建立 Runtime principal;非 AgentKit mode 保留既有 `X-Jwt-Token` 行为。定向测试 49 passed,完整 `make test` 2506 passed、14 skipped,focused Ruff 与 diff hygiene 均通过。产出镜像 `agentkit-platform-2112682748-cn-beijing.cr.volces.com/agentkit/mpa_agent:mpa-p0-adk-auth-eecc6e3-20260917`(`sha256:6ac6a2712ecd1c7950125dc9afc6467373a08142fbd6c3577aba12f126079bef`)已发布为 Runtime `r-yeuujrrcowb21078p9jh` version 62,状态为 `Ready`;Runtime 直连和 Studio BFF 的 `/list-apps` 调用均返回 `200 ["default"]`。本次没有改变 Runtime schema 或持久数据模型。 - -2026-09-18:在 Runtime version 62 上完成真实 Studio 验收,新建 Session `bee3db38-edcc-4020-b925-3d9d6a3a7adc` 并在沙箱执行 `printf 'MPA_V62_BROWSER_OK\n'`。父级沙箱活动和子命令活动均进入 `completed`;最终回答在完成时和 30 秒后持续可见,整页刷新后恢复回答和终态工具卡,页面没有 spinner 或运行中文案。原始证据已写入被忽略的本地 `evidence/browser/mpa-v62-live/`。该结果只验证本次对话展示路径,不关闭 S5-12 的完整 `BC-01`–`BC-09` 门禁。 - -2026-09-19:rebase 到 Runtime `origin/master` 后记录其他分支已合入能力。主干 channel API/service 已提供飞书、钉钉和企微的加密绑定与投递;deployment 模块已提供 shared APIG、database、Runtime 和 Skill Space 部署;ADK streaming 已支持完整 artifact answer 仅回放一次。这些能力在主干已有实现和测试,但其 provider/部署专项验收不归本 P0 台账。 diff --git a/specs/mpa-runtime-provisioning/README.md b/specs/mpa-runtime-provisioning/README.md deleted file mode 100644 index e086927ba..000000000 --- a/specs/mpa-runtime-provisioning/README.md +++ /dev/null @@ -1,148 +0,0 @@ -# MPA Runtime Provisioning - -- **Component ID:** `mpa-runtime-provisioning` -- **Status:** Draft; proposed changes are governed by the related PRD -- **Revision:** 2026-10-12 -- **Chinese version:** [README.zh.md](README.zh.md) -- **Related PRD:** [MPA Runtime Integration Hardening](../../prd-spec/bugfixes/mpa-runtime-integration/2026-09-12-mpa-runtime-integration-hardening.md) -- **Related PRD:** [MPA Studio Workload Identity Provisioning](../../prd-spec/features/mpa-studio-workload-identity/2026-09-20-mpa-studio-workload-identity.md) -- **Related PRD:** [Managed Workload Identity ensure](../../prd-spec/bugfixes/mpa-managed-workload-identity/2026-09-29-ensure-managed-workload-identity.md) -- **Related PRD:** [Fresh managed Runtime command](../../prd-spec/bugfixes/mpa-runtime-fresh-command/2026-09-29-set-fresh-runtime-command.md) -- **Related PRD:** [MPA per-session TOS output mount](../../prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-mpa-tos-output-mount.md) -- **Related PRD:** [Initial Runtime Tool attachment](../../prd-spec/bugfixes/mpa-runtime-initial-tool/2026-09-29-runtime-initial-tool-attachment.md) -- **Related PRD:** [Reference Runtime command preservation](../../prd-spec/bugfixes/mpa-runtime-reference-command/2026-09-29-preserve-runtime-command.md) -- **Owned code:** `veadk/cli/cli_mpa.py`, `veadk/integrations/mpa/mpa_provision.py`, `veadk/integrations/mpa/mpa_runtime.py`, `veadk/integrations/mpa/mpa_tool.py`, `veadk/integrations/mpa/managed/worker.py` - -## Responsibility - -This component converts `veadk mpa create` inputs into one recoverable AgentKit Runtime or VeFaaS deployment, binds its Tool, seeds/finalizes `mpa_meta`, publishes the public A2A endpoint and Runtime credentials to the server-side environment, and verifies that the endpoint is reachable. PostgreSQL, OpenViking, model services, ArkClaw control-plane resources, and Studio presentation remain owned by their respective systems. - -## Entry points and dependencies - -- CLI: `veadk mpa create`. -- Control planes: AgentKit Runtime by default; VeFaaS is a compatibility option. -- Data: caller-supplied external PostgreSQL and optional OpenViking. -- Runtime consumer: the independently versioned mpa-agent image. - -## Contract - -- `CON-1`: The VeADK deployment profile sets `IDENTITY_STARTUP_ENABLED=false`, `MPA_LAZY_LOGIN=false`, `APPCENTER_RESOURCE_DISCOVERY_ENABLED=false`, and retains the derived `CLAW_SPACE_ID` compatibility value. This disables ArkClaw UserPool startup but does not disable outbound workload-token use. -- `CON-2`: Key-auth is enabled by default. After the endpoint and key exist, phase two releases an environment containing both `A2A_PUBLIC_URL` and `CODEX_MCP_RUNTIME_API_KEY`. The key is never printed and is treated as a secret in previews. -- `CON-3`: AgentKit Runtime create and convergent update set `ApmplusEnable=true`. Trace content remains disabled by `APMPLUS_TRACE_CONTENT=false` unless the operator explicitly overrides it. -- `CON-4`: Phase one writes placeholders before startup; phase two overwrites them only with non-empty authoritative Runtime values. A phase-two deployment failure is reported as failure, not partial success. -- `CON-5`: Explicit caller `extra_env` remains last-wins, including an intentional override of VeADK profile defaults. -- `CON-6`: AgentKit Runtime create and convergent update persist `veadk:agent-type=mpa` as the stable Studio classification tag. Untagged Runtime resources remain outside the MPA filter until an explicit tag repair is performed. -- `CON-7`: After optional managed IAM preparation and before other provisioning mutations, both `veadk mpa create` and managed Studio provisioning create or reuse the account-and-region scoped pool `agentkit-studio-workload` and identity `{MPA_AGENT_ID}-studio`, then inject them as `MPA_WORKLOAD_POOL_NAME` and `MPA_WORKLOAD_IDENTITY_NAME`. Managed reference/template values cannot supply another agent's identity; empty explicit values are treated as unspecified and conflicting non-empty values fail before mutation. -- `CON-8`: `veadk mpa create` continues to generate `mi-[0-9a-z]{12}` ids. Explicit ids accept that form and the existing Studio `mi-[0-9a-z]{24}` form for flat managed provisioning. The base id remains the Runtime and metadata identity; only the workload identity receives the `-studio` suffix. -- `CON-9`: Workload get-or-create is exact-name idempotent. Concurrent create conflicts are followed by a read. Other Identity errors fail before Tool, database, or Runtime mutations. Created identity resources are retained for retry. - -## State, security, and compatibility - -The lifecycle is `prepared -> resource-created -> ready -> environment-finalized -> metadata-finalized -> verified`. Reuse-by-name converges the existing Runtime through `UpdateRuntime(ReleaseEnable=True)` and waits for a newer ready version. Secrets remain process/control-plane data and must be redacted from CLI output, documents, logs, and test fixtures. Native mpa-agent deployments retain their own defaults because the opt-out switches are injected by this component rather than changing native defaults. - -When `managed.from-runtime` selects a reference Runtime, provisioning preserves -its explicit container `Command` together with the existing infrastructure -allowlist. It does not invent a command when the reference omits one; agent -identity, credentials, endpoints, Tool binding, and finalization values remain -new-deployment-owned. -The MPA-only fresh managed template uses the standard `bash run.sh` command; -explicit JSON templates remain authoritative. - -## Failure and observability - -Missing endpoint, key, Runtime ID, or APIG ID blocks metadata finalization. Runtime terminal states and readiness timeouts raise `MpaRuntimeError`. The CLI reports created resource identifiers needed for recovery without exposing credentials. APMPlus enablement is verified from Runtime metadata; trace read authorization is a separate Studio concern. - -## Verification - -| Contract | Validation | -| --- | --- | -| `CON-1`, `CON-2`, `CON-5`, `CON-7`, `CON-8` | `uv run --extra dev pytest tests/integrations/test_mpa_provision_env.py tests/cli/test_cli_mpa.py` | -| `CON-2`, `CON-3`, `CON-4`, `CON-6` | `uv run --extra dev pytest tests/integrations/test_mpa_runtime.py` | -| `CON-7`, `CON-9` | `uv run --extra dev pytest tests/integrations/test_mpa_identity.py tests/cli/test_cli_mpa.py` | -| End-to-end | Create/reuse an isolated Runtime, inspect metadata without printing keys, invoke A2A and built-in MCP, and observe two MCP-cache intervals | - -VeADK MPA provisioning defaults to `ENABLE_A2A=true` and `DISABLE_JWT_AUTH=true`, as requested in [Studio key-auth defaults](../../prd-spec/bugfixes/mpa-default-jwt/2026-09-30-studio-key-auth-default.md). The inner legacy REST JWT gate is bypassed, including its admin checks and header-based identity paths; trusted Studio/gateway callers must control identity headers. Outer gateway key-auth and `A2A_TIP_VERIFY_ENABLED=false` are unchanged. With a compatible MPA image, `/list-apps` may advertise ADK instead of forcing A2A discovery; transport compatibility and old-session migration are not guaranteed by this default. Explicit `extra_env` values remain last-wins, including `DISABLE_JWT_AUTH=false`. Managed flat creation uses these defaults; referenced Runtime/template environments are preserved. Existing Runtimes require an explicit update and release. `MPA_AGENTKIT_MODE` is not enabled. General agents are unchanged. - -MPA provisioning defaults `OTEL_PYTHON_DISABLED_INSTRUMENTATIONS` to `sqlalchemy,asyncpg,psycopg,psycopg2,dbapi`, suppressing database auto-instrumentation while preserving business tracing. Explicit `extra_env` overrides win, including an empty string to re-enable instrumentation. This applies to newly provisioned or explicitly redeployed resources, not existing running instances. - -## Managed creation boundary - -Studio MPA creation and `veadk mpa provision` are owned by [Studio MPA creation](../studio-mpa-creation/README.md). That path prepares account resources and uses real Runtime metadata bootstrap. The existing responsibilities and legacy entry points described here remain unchanged. - -## Proposed AgentKit P0 profile - -Governed by [P0 functional migration](../../prd-spec/features/mpa-p0-productionization/2026-09-15-mpa-p0-productionization-design.md). Baseline `CON-1` through `CON-6` remain historical/current integration contracts; the following explicitly proposed profile strengthens readiness and security without claiming implementation. Native/VeFaaS compatibility is separate. No ArkClaw users, metadata or old identifiers are imported. - -- `CON-7`: AgentKit-mode bootstrap uses a versioned manifest of account/workspace/MPA-instance/Runtime identity, endpoint, resource and server-side credential references. `agentkit-mpa-agent` consumes it and never calls ArkClaw configuration/identity/AppCenter fallback for required P0 functionality. Missing configuration returns a classified not-ready state and recoverable finalization step. Internal compatibility names such as `CLAW_SPACE_ID` may remain only as derived values with no old service lookup. Runtime owns MPA Profile revisions; bootstrap records only the binding and applied revision. -- `CON-8`: Preserve prepare/create/finalize phases, but distinguish transport-ready from execution-ready. An unfinalized Runtime may answer bootstrap health while refusing user execution. This avoids requiring finalized endpoint credentials before the platform can report transport readiness. After endpoint/key/Tool and manifest finalization, execution readiness and A2A/worker smoke must pass before success is reported. Repeated create/finalize uses a stable operation ID and resource ownership checks; partial failure reports safe created IDs and next recovery step. -- `CON-9`: Configure AgentKit custom-JWT issuer discovery and allowed client/audience per [runtime identity contract](../mpa-runtime-control/README.md), `CON-1`, and let BFF forward only a validated bearer. API key-auth and user identity are separate. New AgentKit profile validates final merged configuration after caller overrides; insecure JWT bypass or enabled legacy fallback is rejected. This deliberately constrains baseline last-wins `extra_env` only in the opt-in profile and must be tested as a public CLI/config boundary. -- `CON-10`: Key rotation updates server-side references/configuration and reconciles runtime, built-in MCP and worker clients. Neither CLI nor UI receives secret values in normal results. Reconciliation failure remains failure, supports retry with the same operation ID, and never claims the old key was revoked before consumers are ready. Exact key release/rotation operations must be verified against platform APIs in PRD `T-1`. -- `CON-11`: Release metadata pins Runtime image digest, applied MPA Profile revision, session execution-config schema, and worker protocol compatibility. Reuse the AgentKit SDK Runtime create/get/list/update/release/delete/listVersions/listInstances/getLogs methods; Managed Agent APIs are not part of P0. Implementation must still verify Runtime write permissions, idempotency, errors, and rollback behavior. Compatible rollback waits for execution readiness and smoke. New-platform schema evolution is additive; reject incompatible downgrade before mutation. Historical ArkClaw migration is not part of this lifecycle. -- `CON-12`: Delete requires authorized impact preview, active Session/job and resource-reference checks, explicit confirmation, and durable cleanup progress. Shared Tool/Skill/storage resources are retained unless owned exclusively by the operation and separately authorized. Failed cleanup returns retryable/blocked status with safe identifiers; never report full deletion while owned runtime resources remain. - -These guarantees apply to both Studio and CLI through shared orchestration. The target platform operation inventory and bootstrap field/config names are a blocking `T-1` deliverable, not a claim of existing APIs. - -Verification maps `CON-7` through `CON-12` to PRD `AC-1`, `AC-2`, `AC-9`, `AC-10`: fresh bootstrap with legacy endpoints blocked; missing/finalization failure and restart; unsafe overrides; credential rotation/retry; compatible rollback; active/shared-resource deletion. Reuse existing provisioning regression targets above and add failing tests for the new profile. All proposed runtime/live results are `not_run`. - -2026-09-15: profile drafted for functional migration without historical data import; implementation and live evidence pending. -### Automatic PG preparation - -Managed provisioning supports `managed.postgres.mode: auto`. It verifies deployment STS identity before AIDAP calls, prepares the two shared Workspaces and management database before network/APIG/Runtime provisioning, and overrides inherited PG host/port/user/password/TLS values with the resolved business connection. Agent database names and the existing account/region network/APIG sharing contract are unchanged. The bootstrap state, migration guards and API behavior are owned by [Studio creation CON-13](../studio-mpa-creation/README.md#con-13-automatic-shared-postgresql-workspaces). Automatic mode is opt-in; manual/legacy profiles are preserved. - -## Per-session TOS output mount extension - -When the private MPA YAML supplies the complete `tos-access-key`, -`tos-secret-key`, and `tos-bucket` tuple, a newly created worker Tool MUST carry -an access-key TOS mount with `/` as its legacy base and -`/data/output` as its read-write local path. Credentials MUST remain in the -Tool request. The Runtime receives `MPA_CODEX_WORKER_TOS_MOUNT_ENABLED=true` -and the non-secret bucket name, but never AK/SK. -Studio submits only the optional `tosBucket` and overrides Worker credentials -with IAM Role mode, using `/sandbox-session/default/default` as the IAM base. -Explicit managed profiles may use `tos-credential-type: iam_role` and a bucket -without keys; historical CLI/YAML AK/SK mode remains compatible. Empty Studio -selection preserves server defaults. Bucket IAM authorization follows -[CON-17](../studio-mpa-creation/README.md#con-17--bucket-only-iam-tos-mounts). -Credentials MUST NOT enter sessionStorage, task SQLite, or task responses. -A profile using an existing Worker must first select a new Worker image for the -creation; existing Tools and Sessions are not implicitly modified. - -For a newly managed Runtime, the initial `CreateRuntime` request MUST include -the `ToolId` of the already prepared worker Tool. The final convergent update -retains the same `ToolId` while publishing authoritative endpoint/key -environment values. Delaying the first Tool attachment until after Runtime -creation is unsupported because the control plane may fail the unbound Runtime -before it reaches `Ready`. - -For every newly created Sandbox Session, mpa-agent MUST set -`CreateSessionRequest.TosMountPoints` using AgentKit's -canonical per-session path: -`/sandbox-session/tool-{tool_id}/session-{session_id}/`. It MUST fail closed if -the Runtime flag is enabled but the bucket is absent. Existing -Sessions and externally supplied Tools are not mutated. - -## Persistent MCP signing secret - -Runtime provisioning injects `MCP_TOKEN_SECRET` before CreateRuntime/UpdateRuntime. A nonblank explicit value wins; otherwise the target Runtime's existing nonblank value is retained, or 32 random bytes are generated as 64 hex characters. Caller-owned environment mappings are unchanged. Endpoint/API Key finalization reuses the same value. Blank values count as missing. Explicit values are masked in dry-run output. The Runtime stores the key in environment configuration, and provisioning results do not expose it. Existing deployments change only on explicit redeployment. See the [secret provisioning design](../../prd-spec/bugfixes/mpa-runtime-mcp-secret/2026-10-07-persistent-mcp-secret.md) and the managed retry boundary in [Studio creation](../studio-mpa-creation/README.md). - -## Managed Runtime IAM role - -Built-in Studio sets `managed.iam.mode: auto`; explicit profiles default to `existing`. Auto preparation runs after STS account validation and before workload identity or other resource mutations. Only a fresh default `IDRoleForArkClawShareAgent` configuration is supported. IAM is account-scoped. Get/create role and `VeADKMPARuntimeAccessV1`, validate account/name and unconditional trust in `vefaas` and `apig`, add missing Global bindings for the approved 12 system policies and 14 custom actions, then verify readback. Preserve extra bindings; never overwrite trust or an existing policy. Missing-object codes alone allow creation; races and eventual consistency use bounded 120-second recovery. Cancellation retains resources. Permission/trust/policy/ownership/readback failures halt creation at `iam_role` with allowlisted localized errors; raw provider text and secrets are excluded. Studio execution credentials require GetRole, CreateRole, GetPolicy, CreatePolicy, ListAttachedRolePolicies and AttachRolePolicy. Existing-mode CLI and general agents are unaffected. Full permission list and acceptance: [design](../../prd-spec/features/mpa-runtime-iam/2026-10-09-managed-runtime-role.md). - -## Fresh managed account metadata - -Fresh managed templates generate `CLAW_SPACE_ID` from verified STS identity and `RUNTIME_IAM_ROLE_NAME`/`RUNTIME_IAM_ROLE_TRN` from that account and selected Runtime role. Explicit environment overrides retain precedence. Referenced/template Runtime behavior is unchanged. Built-in Studio removes the old fixed expected account; explicit YAML restrictions remain. Source images/reference Tools require separate access verification. See [Studio account contract](../studio-mpa-creation/README.md#studio-deployment-account). - -Managed VPC/subnet descriptions, legacy ownership compatibility and rejected-intent recovery follow the [Studio network contract](../studio-mpa-creation/README.md#network-ownership-descriptions-and-recovery). Names, account/region scope and uncertain-outcome duplicate prevention remain unchanged. - -Empty managed network discovery follows the [Studio contract](../studio-mpa-creation/README.md#empty-network-discovery); it does not alter Runtime payloads, account scope or ownership checks. - -New shared standard gateways and cross-zone companion intents follow the [Studio gateway preparation contract](../studio-mpa-creation/README.md#standard-shared-gateway-preparation). Existing Runtime subnet selections are immutable; new Runtime creation may use the prepared gateway selection. - -Worker environment configuration and reference precedence follow the [Studio contract](../studio-mpa-creation/README.md#independent-worker-configuration). Built-in Studio does not require an old-account Tool. Explicit CLI references/existing IDs remain supported; legacy non-managed creation is unchanged. - -Managed Runtime subnet equality and stable retry ordering follow the [Studio network recovery contract](../studio-mpa-creation/README.md#runtime-subnet-ordering-and-recovery). - -## MPA owned-resource deletion (2026-10-12, implemented) - -This change supersedes the Profile prerequisite in the deletion contract: preview/execution do not query Profile; only active operations and active sessions block deletion, and session-query errors remain visible. After session cleanup, MPA deletion removes proven exclusive managed worker templates and Skill Spaces, then Runtime. Proof requires account/region deployment records, matching ownership tags and checks for other deployment/Runtime references. Shared, imported or unproven resources remain. Persist cleanup state for partial-failure retries; other resources and general Runtime deletion remain unchanged. Loading, errors, preview retry and cancellation are visible in the detail confirmation dialog. See the [design](../../prd-spec/features/mpa-owned-resource-deletion/2026-10-12-owned-resource-deletion.md). diff --git a/specs/mpa-runtime-provisioning/README.zh.md b/specs/mpa-runtime-provisioning/README.zh.md deleted file mode 100644 index 52f16885d..000000000 --- a/specs/mpa-runtime-provisioning/README.zh.md +++ /dev/null @@ -1,138 +0,0 @@ -# MPA Runtime 部署 - -- **Component ID:** `mpa-runtime-provisioning` -- **状态:** 草案;提议变更由关联 PRD 管理 -- **修订日期:** 2026-10-12 -- **English version:** [README.md](README.md) -- **关联 PRD:** [MPA Runtime 集成加固](../../prd-spec/bugfixes/mpa-runtime-integration/2026-09-12-mpa-runtime-integration-hardening.zh.md) -- **关联 PRD:** [MPA Studio 工作负载身份创建](../../prd-spec/features/mpa-studio-workload-identity/2026-09-20-mpa-studio-workload-identity.zh.md) -- **关联 PRD:** [托管 Workload Identity ensure](../../prd-spec/bugfixes/mpa-managed-workload-identity/2026-09-29-ensure-managed-workload-identity.zh.md) -- **关联 PRD:** [全新托管 Runtime 启动命令](../../prd-spec/bugfixes/mpa-runtime-fresh-command/2026-09-29-set-fresh-runtime-command.zh.md) -- **关联 PRD:** [MPA 会话级 TOS 输出挂载](../../prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-mpa-tos-output-mount.zh.md) -- **关联 PRD:** [Runtime 首次挂载 Tool](../../prd-spec/bugfixes/mpa-runtime-initial-tool/2026-09-29-runtime-initial-tool-attachment.zh.md) -- **关联 PRD:** [保留参考 Runtime 启动命令](../../prd-spec/bugfixes/mpa-runtime-reference-command/2026-09-29-preserve-runtime-command.zh.md) -- **负责代码:** `veadk/cli/cli_mpa.py`、`veadk/integrations/mpa/mpa_provision.py`、`veadk/integrations/mpa/mpa_runtime.py`、`veadk/integrations/mpa/mpa_tool.py`、`veadk/integrations/mpa/managed/worker.py` - -## 职责 - -本组件将 `veadk mpa create` 输入转换为一个可恢复的 AgentKit Runtime 或 VeFaaS 部署,绑定 Tool,预写/完成 `mpa_meta`,把公网 A2A Endpoint 和 Runtime 凭据发布到服务端环境,并验证 Endpoint 可访问。PostgreSQL、OpenViking、模型服务、ArkClaw 控制面资源和 Studio 展示仍由各自系统负责。 - -## 入口与依赖 - -- CLI:`veadk mpa create`。 -- 控制面:默认使用 AgentKit Runtime;VeFaaS 为兼容选项。 -- 数据:调用方提供的外部 PostgreSQL 和可选 OpenViking。 -- Runtime 消费方:独立版本的 mpa-agent 镜像。 - -## 契约 - -- `CON-1`:VeADK 部署配置设置 `IDENTITY_STARTUP_ENABLED=false`、`MPA_LAZY_LOGIN=false`、`APPCENTER_RESOURCE_DISCOVERY_ENABLED=false`,并保留派生的 `CLAW_SPACE_ID` 兼容值。这会关闭 ArkClaw UserPool 启动,但不会禁用出站 workload token 使用。 -- `CON-2`:默认开启 key-auth。Endpoint 和 Key 可用后,第二阶段发布同时包含 `A2A_PUBLIC_URL` 与 `CODEX_MCP_RUNTIME_API_KEY` 的环境。Key 不得打印,并在预览中按 Secret 处理。 -- `CON-3`:AgentKit Runtime 创建及收敛更新设置 `ApmplusEnable=true`。除非操作方显式覆盖,`APMPLUS_TRACE_CONTENT=false` 保持内容追踪关闭。 -- `CON-4`:第一阶段在启动前写入占位符;第二阶段只使用非空权威 Runtime 值覆盖。第二阶段部署失败必须报告失败,不得报告部分成功。 -- `CON-5`:调用方显式提供的 `extra_env` 继续保持 last-wins,包括有意覆盖 VeADK 配置默认值。 -- `CON-6`:AgentKit Runtime 创建及收敛更新必须持久化 `veadk:agent-type=mpa`,作为 Studio 稳定分类标签。未打标签 Runtime 在执行显式标签修复前不进入 MPA 筛选。 -- `CON-7`:在可选的托管 IAM 准备之后、其他创建副作用之前,`veadk mpa create` 与 Studio 托管创建都会创建或复用账号和地域范围内的 `agentkit-studio-workload` Pool 与 `{MPA_AGENT_ID}-studio` Identity,然后以 `MPA_WORKLOAD_POOL_NAME` 和 `MPA_WORKLOAD_IDENTITY_NAME` 注入 Runtime。托管参考 Runtime 或模板不得提供其他 Agent 的 Identity;显式空值视为未配置,冲突的非空值在资源修改前失败。 -- `CON-8`:`veadk mpa create` 继续自动生成 `mi-[0-9a-z]{12}` ID。显式 ID 接受该格式及 Studio 原有的 `mi-[0-9a-z]{24}` 格式,以兼容托管平铺创建。基础 ID 仍作为 Runtime 和元数据身份,只有 WorkloadIdentity 增加 `-studio` 后缀。 -- `CON-9`:工作负载资源按精确名称幂等 get-or-create。并发创建冲突后重新读取;其他 Identity 错误在 Tool、数据库或 Runtime 修改前失败。已创建的身份资源保留供重试。 - -## 状态、安全与兼容 - -生命周期为 `prepared -> resource-created -> ready -> environment-finalized -> metadata-finalized -> verified`。按名称复用时通过 `UpdateRuntime(ReleaseEnable=True)` 收敛现有 Runtime,并等待更新版本 Ready。Secret 只属于进程/控制面数据,必须从 CLI 输出、文档、日志及测试夹具中脱敏。退出开关由本组件注入,不改变原生默认值,因此原生 mpa-agent 部署保持兼容。 - -当 `managed.from-runtime` 选择参考 Runtime 时,部署会在现有基础设施白名单中保留其 -显式容器 `Command`。参考 Runtime 未提供命令时不会推断默认值;agent 身份、凭据、 -endpoint、Tool 绑定和最终化值仍由新部署持有。 -MPA 专用的全新托管模板使用标准命令 `bash run.sh`;显式 JSON 模板保持权威。 - -## 失败与可观测性 - -缺少 Endpoint、Key、Runtime ID 或 APIG ID 会阻止 metadata 完成。Runtime 终态失败和 Ready 超时抛出 `MpaRuntimeError`。CLI 报告恢复所需的已创建资源标识,但不暴露凭据。APMPlus 是否开启通过 Runtime metadata 验证;链路读取授权属于独立的 Studio 责任。 - -## 验证 - -| 契约 | 验证 | -| --- | --- | -| `CON-1`、`CON-2`、`CON-5`、`CON-7`、`CON-8` | `uv run --extra dev pytest tests/integrations/test_mpa_provision_env.py tests/cli/test_cli_mpa.py` | -| `CON-2`、`CON-3`、`CON-4`、`CON-6` | `uv run --extra dev pytest tests/integrations/test_mpa_runtime.py` | -| `CON-7`、`CON-9` | `uv run --extra dev pytest tests/integrations/test_mpa_identity.py tests/cli/test_cli_mpa.py` | -| 端到端 | 创建/复用隔离 Runtime,不打印 Key 地检查 metadata,调用 A2A 和内置 MCP,并观察两个 MCP 缓存周期 | - -VeADK MPA 部署按 [Studio key-auth 默认配置](../../prd-spec/bugfixes/mpa-default-jwt/2026-09-30-studio-key-auth-default.zh.md) 默认设置 `ENABLE_A2A=true` 和 `DISABLE_JWT_AUTH=true`。绕过旧版 REST 内层 JWT 校验,包括其管理权限检查和 header 身份路径;可信 Studio/网关调用方必须控制身份 header。外层网关 key-auth 和 `A2A_TIP_VERIFY_ENABLED=false` 不变。使用兼容的 MPA 镜像时,`/list-apps` 可能发现 ADK,而不是强制 A2A;该默认值不保证传输兼容性,也不迁移旧会话。显式 `extra_env` 仍最后覆盖,包括 `DISABLE_JWT_AUTH=false`。Managed flat 创建使用这些默认值;引用 Runtime/模板环境保持不变。已有 Runtime 需要显式更新并发布。不启用 `MPA_AGENTKIT_MODE`。通用智能体不受影响。 - -MPA 部署默认注入 `OTEL_PYTHON_DISABLED_INSTRUMENTATIONS=sqlalchemy,asyncpg,psycopg,psycopg2,dbapi`,关闭数据库自动埋点并保留业务链路。显式 `extra_env` 优先,包括传空字符串重新启用埋点。适用于新部署或显式重新部署的资源,不自动修改已有运行实例。 - -## 托管创建边界 - -Studio MPA 创建和 `veadk mpa provision` 由 [Studio MPA 创建](../studio-mpa-creation/README.zh.md)负责。该路径准备账号资源并通过真实 Runtime 元数据初始化。本文原有职责和旧入口保持不变。 - -## 拟议 AgentKit P0 profile - -由 [P0 功能迁移](../../prd-spec/features/mpa-p0-productionization/2026-09-15-mpa-p0-productionization-design.zh.md) 管理。基线 `CON-1` 至 `CON-6` 保留为历史/当前集成契约;以下拟议 profile 加强 readiness 和安全,不表示已实现。原生/VeFaaS 兼容路径独立保留,不导入 ArkClaw 用户、元数据或旧标识。 - -- `CON-7`:AgentKit 模式使用版本化 bootstrap manifest,包含 account/workspace/MPA-instance/Runtime 身份、endpoint、resource 和服务端 credential 引用。由 `agentkit-mpa-agent` 消费,P0 必需功能不得调用 ArkClaw configuration/identity/AppCenter fallback。缺失配置返回分类 not-ready 状态和可恢复 finalization 步骤。`CLAW_SPACE_ID` 等内部兼容名仅可作为派生值保留,不查询旧服务。Runtime 持有 MPA Profile revision,bootstrap 只记录绑定和已应用 revision。 -- `CON-8`:保留 prepare/create/finalize 阶段,区分 transport-ready 与 execution-ready。未 finalization 的 Runtime 可响应 bootstrap health,但拒绝用户执行,避免平台 transport-ready 前必须已有最终 endpoint 凭据的循环依赖。Endpoint/key/Tool/manifest 完成后,execution readiness 与 A2A/worker smoke 均通过才报告成功。重复 create/finalize 使用稳定 operation ID 并检查资源所有权;部分失败返回安全资源 ID 和下一恢复步骤。 -- `CON-9`:按 [runtime 身份契约](../mpa-runtime-control/README.zh.md) `CON-1` 配置 AgentKit custom JWT issuer discovery、allowed client/audience,并由 BFF 转发已验证 bearer;API key-auth 与用户身份独立。新 AgentKit profile 在 caller override 合并后验证最终配置,拒绝 JWT bypass 或开启旧 fallback。这仅在 opt-in profile 中有意约束基线 `extra_env` last-wins,必须作为公共 CLI/config 边界验证。 -- `CON-10`:Key 轮换更新服务端引用/配置,并对齐 runtime、内置 MCP、worker client。CLI/UI 普通结果不包含原值。Reconciliation 失败保持失败,允许相同 operation ID 重试;消费者未就绪前不声称旧 key 已撤销。实际 key release/rotation operation 由 PRD `T-1` 对照平台 API 验证。 -- `CON-11`:发布 metadata 固定 runtime image digest、已应用 MPA Profile revision、session execution-config schema、worker protocol 兼容。复用 AgentKit SDK 的 Runtime create/get/list/update/release/delete/listVersions/listInstances/getLogs;Managed Agent API 不在 P0。实施时仍需验证 Runtime 写操作的权限、幂等、错误和回滚行为。兼容回滚等待 execution-ready 和 smoke;新平台 schema 增量演进,不兼容降级在写操作前拒绝。历史 ArkClaw 迁移不属于此生命周期。 -- `CON-12`:删除要求授权影响预览、活动 Session/job 与资源引用检查、显式确认、持久化清理进度。共享 Tool/Skill/storage 保留,除非属于该操作独占且另行授权。清理失败返回 retryable/blocked 状态和安全 ID;仍有所属 runtime 资源时不报告全部删除完成。 - -Studio/CLI 通过共享编排遵循相同保证。平台 operation 清单与 bootstrap field/config 名称是阻塞性 `T-1` 交付物,不是现有 API 声明。 - -`CON-7` 至 `CON-12` 验证映射 PRD `AC-1`、`AC-2`、`AC-9`、`AC-10`:阻断旧端点的全新启动、缺失配置/finalization 失败/重启、不安全 override、轮换/重试、兼容回滚、活动/共享资源删除。复用上述 provisioning 回归并为新 profile 增加失败测试。拟议 runtime/live 结果均为 `not_run`。 - -2026-09-15:新增无历史导入的功能迁移 profile 草案,实现与真实证据待补。 -### 自动准备 PG - -托管部署支持 `managed.postgres.mode: auto`。AIDAP 调用前核验部署 STS 身份,在网络/APIG/Runtime 部署前准备两个共享 Workspace 和管理库,并使用解析的业务连接覆盖继承的 PG 地址/端口/用户名/密码/TLS 配置。智能体业务库命名及现有账号/地域网络/APIG 共享契约不变。引导状态、迁移保护和 API 行为由 [Studio 创建 CON-13](../studio-mpa-creation/README.zh.md#con-13自动准备共享-postgresql-workspace) 定义。自动模式显式启用,保留手动/旧配置兼容性。 - -## 会话级 TOS 输出挂载扩展 - -当私有 MPA YAML 同时提供 `tos-access-key`、`tos-secret-key` 和 -`tos-bucket` 时,新建 worker Tool 必须携带 access-key 类型的 TOS 挂载:基础路径 -为历史模式的 `/`,本地读写路径为 `/data/output`。凭证必须只 -保留在 Tool 请求中;Runtime 只收到 -`MPA_CODEX_WORKER_TOS_MOUNT_ENABLED=true` 和非敏感 bucket 名,不接收 AK/SK。 -Studio 仅提交可选 `tosBucket`,覆盖为不带密钥的 IAM Role 模式,其基础路径为 -`/sandbox-session/default/default`。显式 managed profile 可用 -`tos-credential-type: iam_role` 和桶名,不提供密钥;历史 CLI/YAML AK/SK 模式兼容。 -Studio 空选择保留服务端默认配置,桶授权遵循 -[CON-17](../studio-mpa-creation/README.zh.md#con-17--仅选桶-iam-tos-挂载)。 -凭据不得进入 sessionStorage、任务 SQLite 或任务查询响应。使用已有 Worker 时必须 -先通过本次创建的 Worker 镜像覆盖为新建 Worker;不隐式修改既有 Tool/Session。 - -对于新建托管 Runtime,首次 `CreateRuntime` 请求必须携带已经准备完成的 Worker Tool -的 `ToolId`。最终收敛更新在发布权威 endpoint/key 环境时继续保留相同 `ToolId`。 -不支持将首次 Tool 挂载延迟到 Runtime 创建之后,因为控制面可能在未绑定 Runtime -达到 `Ready` 前将其置为失败。 - -每次新建 Sandbox Session 时,mpa-agent 必须按 AgentKit 标准会话路径 -`/sandbox-session/tool-{tool_id}/session-{session_id}/` 设置 -`CreateSessionRequest.TosMountPoints`。如果 Runtime 开关已启用但 bucket 缺失,必须 -关闭式失败。已有 Session 和外部指定 Tool 不做变更。 - -## 固定 MCP 签名密钥 - -Runtime 创建在 CreateRuntime/UpdateRuntime 前注入 `MCP_TOKEN_SECRET`。显式非空值优先;否则保留目标 Runtime 已有非空值,或生成 32 随机字节并编码为 64 位十六进制字符串。不修改调用方的环境变量字典。地址/API Key 回填沿用同一值。空白值视为未配置。dry-run 输出对显式值脱敏。Runtime 将密钥保存于环境变量配置,创建返回值不暴露密钥。存量部署仅在显式重新部署时变更。参见[密钥创建设计](../../prd-spec/bugfixes/mpa-runtime-mcp-secret/2026-10-07-persistent-mcp-secret.zh.md)及 [Studio 创建](../studio-mpa-creation/README.zh.md)的托管重试边界。 - -## 自动准备 Runtime IAM 角色 - -内置 Studio 使用 `managed.iam.mode: auto`,显式配置默认 `existing`。自动准备在 STS 账号核验后、工作负载身份和其他资源变更前执行。仅支持全新来源的默认 `IDRoleForArkClawShareAgent` 配置。IAM 为账号级。查询/创建角色及 `VeADKMPARuntimeAccessV1`,核验账号/名称及无条件信任 `vefaas`、`apig`,补齐已批准的 12 个系统策略和 14 个自定义动作的 Global 绑定,随后回读核验。保留额外绑定,不覆盖信任或已有策略。仅明确不存在错误允许创建,竞态和最终一致性采用最多 120 秒的恢复。取消保留资源。权限/信任/策略/属主/回读失败在 `iam_role` 阶段停止,输出白名单本地化错误,不输出原始云端文本和密钥。Studio 执行凭据须允许 GetRole、CreateRole、GetPolicy、CreatePolicy、ListAttachedRolePolicies、AttachRolePolicy。existing CLI 及通用智能体不受影响。完整权限及验收见[设计](../../prd-spec/features/mpa-runtime-iam/2026-10-09-managed-runtime-role.zh.md)。 - -## 新建托管账号元数据 - -新建托管模板根据核验的 STS 身份生成 `CLAW_SPACE_ID`,并由该账号和所选 Runtime 角色生成 `RUNTIME_IAM_ROLE_NAME`/`RUNTIME_IAM_ROLE_TRN`。显式环境变量覆盖保持优先。引用/模板 Runtime 行为不变。内置 Studio 移除旧的固定预期账号;显式 YAML 限制保留。源镜像/参考 Tool 的访问需要单独核验。见 [Studio 账号契约](../studio-mpa-creation/README.zh.md#studio-部署账号)。 - -托管 VPC/子网描述、旧归属标记兼容及拒绝意图恢复遵守 [Studio 网络契约](../studio-mpa-creation/README.zh.md#网络归属描述与恢复)。名称、账号/地域范围及结果不确定时避免重复创建的规则不变。 - -托管网络空查询遵循 [Studio 契约](../studio-mpa-creation/README.zh.md#空网络查询),不改变 Runtime 载荷、账号范围或归属校验。 - -新共享标准型网关及跨可用区伴随意图遵循 [Studio 网关准备契约](../studio-mpa-creation/README.zh.md#标准型共享网关准备)。已有 Runtime 子网选择不可变,新 Runtime 创建可使用准备好的网关选择。 - -Worker 环境配置及模板优先级遵循 [Studio 契约](../studio-mpa-creation/README.zh.md#独立-worker-配置)。内置 Studio 不依赖旧账号 Tool。保留显式 CLI 模板/已有 ID;非托管旧创建逻辑不变。 - -托管 Runtime 的子网等价判断和稳定重试顺序遵循 [Studio 网络恢复契约](../studio-mpa-creation/README.zh.md#runtime-子网顺序与恢复)。 - -## MPA 独占资源删除(2026-10-12,已实现) - -本变更替代删除契约中的 Profile 前置条件:预览与执行不查询 Profile,仅活动操作和运行会话阻止删除;会话查询失败必须可见。MPA 删除在既有会话清理后,按顺序清理已证明独占的受管沙箱模板、Skill Space,再删除 Runtime。依据为账号/地域注册记录、匹配的资源标签和其他部署/Runtime 引用检查;共享、导入或归属不明资源保留。清理状态持久保存,部分失败可重试;其他资源与通用 Runtime 删除不变。加载、错误、预览重试及取消在详情页确认弹窗可见。详见[设计](../../prd-spec/features/mpa-owned-resource-deletion/2026-10-12-owned-resource-deletion.zh.md)。 diff --git a/specs/runtime-deepseek-harness/README.md b/specs/runtime-deepseek-harness/README.md deleted file mode 100644 index 312e70957..000000000 --- a/specs/runtime-deepseek-harness/README.md +++ /dev/null @@ -1,116 +0,0 @@ -# Runtime DeepSeek Harness Component - -[中文版](README.zh.md) - -## Metadata and Scope - -- **Component ID:** `runtime-deepseek-harness` -- **Status:** active -- **Revision date:** 2026-10-15 -- **Owned code:** `veadk/runtime/deepseek_harness/`, `veadk/runtime/__init__.py`, `veadk/agent.py` runtime fields, runtime support matrix entries. -- **Primary tests:** `tests/runtime/deepseek_harness/` -- **Related PRD:** [DSH Runtime](../../prd-spec/features/deepseek-harness-runtime/2026-10-15-deepseek-harness-runtime.md) -- **Dependent components:** Agent execution, runtime compatibility, optional dependency metadata, user runtime documentation. - -## Responsibility and Non-goals - -This component owns the Python SDK bridge between `Agent(runtime="dsh")` and the local DeepSeek Harness SDK runtime. It resolves runtime configuration, manages the long-lived SDK object and explicit `DSH_HOME`, maps VeADK session identity to DSH session identity, bridges ADK/MCP tools through DSH native tool plugins, bridges skills through DSH's native filesystem skill provider, and emits ADK-compatible partial, tool, and final response events. - -It does not own DSH profile behavior, provider authentication semantics inside DSH, the bundled binary build pipeline, Studio's separate DeepSeek Harness sandbox flow, ADK skill checklist enforcement inside DSH's native skill loader, or descendant-session UI projection. - -## Entry Points and Dependencies - -- Public runtime value: `Agent(runtime="dsh")`. -- Public config import: `from veadk.runtime.deepseek_harness import DeepSeekHarnessRuntimeConfig`. -- Optional dependency: `veadk-python[dsh]`, which installs `deepseek-harness-sdk==0.1.5rc1` and the matching transitive `deepseek-harness-runtime-bin`. -- External SDK import: `deepseek_harness`. -- Runtime dispatch: `veadk.runtime.get_runtime("dsh")`. - -## Contract - -- **CON-1 — Runtime value.** `dsh` is the stable public runtime identifier. Unknown spellings are rejected by `Agent` or `get_runtime`. -- **CON-2 — Optional dependency.** Importing VeADK without the DSH extra succeeds. Resolving the DSH runtime without the SDK raises `ImportError` with `pip install "veadk-python[dsh]"`. -- **CON-3 — Config source.** `deepseek_harness_runtime_config` accepts a `DeepSeekHarnessRuntimeConfig` instance or dict. Environment variables with the `VEADK_DEEPSEEK_HARNESS_*` prefix override matching fields. -- **CON-4 — Model resolution.** If config `model` is unset, the runtime uses the first `Agent.model_name` entry. ADK before-model callbacks may mutate `llm_request.model`; the mutated model is used when selecting the DSH manager and SDK config. -- **CON-4a — Provider aliases.** Config `provider` is the user-facing provider name and defaults to `deepseek-official`; blank values normalize to that default. The aliases `openai`, `volcengine`, `volcengine-ark`, `ark`, `openai-compatible`, and `third-party` are mapped to the current DSH `deepseek-official` adapter before SDK construction. Unknown provider names are passed through unchanged for future DSH-native adapters. -- **CON-5 — Lazy sidecar.** No DSH subprocess starts during module import, runtime lookup, or agent construction. The SDK object starts from the first actual turn. -- **CON-6 — Explicit home.** When `dsh_home` is unset, the runtime creates and passes a non-empty generated DSH home. It never relies on implicit `~/.dsh`. -- **CON-7 — Session mapping and short-term context.** DSH `session_id` is stable for the tuple `(app_name, user_id, session_id, agent name)`. The prompt also serializes the visible ADK session history into a structured `` block, keeping VeADK/ADK session state as the short-term memory source of truth rather than relying only on DSH's internal session state. -- **CON-8 — Tool bridge.** ADK function tools and ADK/MCP toolsets are expanded before the DSH run, registered as DSH native tools through a generated `defineTool` plugin patch, executed in Python through an authenticated loopback bridge, and emitted as ADK tool call/response events. -- **CON-9 — Sub-agent transfer.** Transfer-capable `sub_agents` are exposed through the injected `transfer_to_agent` tool. A transfer response runs the target agent and suppresses the DSH final text for that turn. -- **CON-10 — Skill bridge.** ADK `SkillToolset` and legacy VeADK skills are materialized into an isolated skill root. The runtime registers `@deepseek-ai/dsh-skill`, `@deepseek-ai/dsh-skill-filesystem` with `includeDefaultRoots=false`, and `@deepseek-ai/dsh-tool-skill` through a DSH patch. The materialized skill content digest participates in the manager key. -- **CON-11 — Event mapping.** Root-session DSH assistant stream chunks are emitted as ADK partial model events. DSH native `tool/call` and `tool/result` events are emitted as ADK function call/response events, excluding VeADK-bridged Python tool names to avoid duplicates. -- **CON-12 — Event output.** Each completed non-transfer DSH run emits one ADK model event containing `RunResult.final_response` after ADK after-model callbacks. Empty DSH final responses are represented as an empty final model response. -- **CON-13 — Unsupported surfaces.** ADK flow-only features remain covered by `veadk.runtime.compat`. ADK skill checklist enforcement and full descendant-session projection are not part of this component's active contract. - -## State and Concurrency - -Effective runtime configurations are keyed after resolving model, provider, DSH profile, homes, paths, timeouts, explicit environment overrides, the bridged DSH tool schema digest, and the materialized skill content digest. Each key owns one `DeepSeekHarness` instance, one generated `dsh_home` when applicable, one long-lived tool bridge/plugin patch when tools are present, and one native DSH skill patch/root when skills are present. Tool plugin files are written under a manager-private temporary directory, not under shared `dsh_home`, so persistent homes can be reused across processes without overwriting the per-process bridge token. Managers are closed at process exit. - -Turns sharing a manager are serialized with an async lock. This avoids undocumented concurrent access to one SDK stdio client. Multiple different manager keys may run independently. - -Cancellation from the surrounding task cannot reliably cancel one in-flight DSH `run()` call through the current synchronous SDK. The component preserves the shared manager and keeps its lock until the synchronous SDK call returns, then propagates cancellation. It does not claim graceful per-turn cancellation. - -Tool bridge calls are accepted only while the manager lock owns an active turn. The bridge rejects calls without the per-manager bearer token or without an active ADK tool bundle. - -## Data and Configuration - -Configuration fields: - -- `provider`, `model`, `reasoning_effort`, `max_tokens` -- `cwd`, `runtime_cwd`, `dsh_bin`, `profile`, `patches`, `dsh_home` -- `env`, `initialize_timeout_seconds`, `request_timeout_seconds`, `shutdown_timeout_seconds` -- `base_url`, `api_key` - -Environment overrides: - -- `VEADK_DEEPSEEK_HARNESS_PROVIDER` -- `VEADK_DEEPSEEK_HARNESS_MODEL` -- `VEADK_DEEPSEEK_HARNESS_REASONING_EFFORT` -- `VEADK_DEEPSEEK_HARNESS_MAX_TOKENS` -- `VEADK_DEEPSEEK_HARNESS_CWD` -- `VEADK_DEEPSEEK_HARNESS_RUNTIME_CWD` -- `VEADK_DEEPSEEK_HARNESS_DSH_BIN` -- `VEADK_DEEPSEEK_HARNESS_PROFILE` -- `VEADK_DEEPSEEK_HARNESS_DSH_HOME` -- `VEADK_DEEPSEEK_HARNESS_INITIALIZE_TIMEOUT_SECONDS` -- `VEADK_DEEPSEEK_HARNESS_REQUEST_TIMEOUT_SECONDS` -- `VEADK_DEEPSEEK_HARNESS_SHUTDOWN_TIMEOUT_SECONDS` -- `VEADK_DEEPSEEK_HARNESS_BASE_URL` -- `VEADK_DEEPSEEK_HARNESS_API_KEY` - -The default `profile` is `sdk-minimal`, so VeADK can inject the exact DSH tool and skill plugins it owns. Callers may opt into DSH's fuller `sdk` profile; in that mode VeADK patches the existing skill filesystem plugin configuration rather than inserting a duplicate plugin set. - -`request_timeout_seconds` defaults to `None`, which leaves the SDK run unbounded at the VeADK layer. Production callers should set it when a hard turn deadline is required. - -## Security and Isolation - -Generated `DSH_HOME` paths are process-owned temporary directories. Configured `dsh_home`, `cwd`, and `runtime_cwd` are expanded and resolved before use. The runtime passes `api_key` and `base_url` only through SDK config; redaction of DSH internals belongs to DSH. - -User prompts and session IDs are untrusted input. Session IDs are hashed rather than embedded raw into DSH identifiers. - -## Compatibility and Evolution - -The component is additive. Existing runtime values and configs remain unchanged. Future versions may add checklist enforcement, descendant-session projection, and finer cancellation only after the PRD/spec contract is updated and covered by tests. - -## Observability and Failures - -The runtime logs selected model/provider and session identity at a non-secret level. SDK import errors, initialization errors, and run errors propagate unless an ADK on-model-error callback returns a fallback `LlmResponse`. - -## Verification - -| Contract | Guarantee | Test or validation | Evidence | -| --- | --- | --- | --- | -| `CON-1` to `CON-4a` | Runtime dispatch and config resolution. | `uv run --extra dev pytest tests/runtime/deepseek_harness` | PRD verification record | -| `CON-5` to `CON-7` | Lazy manager, explicit home, stable session IDs. | `uv run --extra dev pytest tests/runtime/deepseek_harness` | PRD verification record | -| `CON-8` | ADK tools bridge through a generated DSH plugin and loopback callback. | `uv run --extra dev pytest tests/runtime/deepseek_harness` | PRD verification record | -| `CON-9` | Sub-agent transfer uses the injected transfer tool path. | `uv run --extra dev pytest tests/runtime/deepseek_harness` | PRD verification record | -| `CON-10` | Skills bridge through DSH native skill plugins and an isolated materialized root. | `uv run --extra dev pytest tests/runtime/deepseek_harness` | PRD verification record | -| `CON-11` | DSH partial and native tool events become ADK events. | `uv run --extra dev pytest tests/runtime/deepseek_harness` | PRD verification record | -| `CON-12` | Final response becomes an ADK event after callbacks. | `uv run --extra dev pytest tests/runtime/deepseek_harness` | PRD verification record | -| `CON-13` | Unsupported surfaces are documented and gated. | Source/doc review plus runtime compatibility tests | PRD verification record | - -## Change Record - -- 2026-10-15: Initial active contract for `runtime="dsh"` introduced by `deepseek-harness-runtime`. -- 2026-10-15: Added VeADK provider aliases for omitted, OpenAI, Volcengine, and third-party OpenAI-compatible endpoints, mapped to the current DSH adapter. diff --git a/specs/runtime-deepseek-harness/README.zh.md b/specs/runtime-deepseek-harness/README.zh.md deleted file mode 100644 index 0ec654863..000000000 --- a/specs/runtime-deepseek-harness/README.zh.md +++ /dev/null @@ -1,116 +0,0 @@ -# Runtime DeepSeek Harness 组件 - -[English](README.md) - -## 元数据与范围 - -- **组件 ID:** `runtime-deepseek-harness` -- **状态:** active -- **修订日期:** 2026-10-15 -- **所属代码:** `veadk/runtime/deepseek_harness/`、`veadk/runtime/__init__.py`、`veadk/agent.py` runtime 字段、runtime 支持矩阵条目。 -- **主要测试:** `tests/runtime/deepseek_harness/` -- **相关 PRD:** [DSH Runtime](../../prd-spec/features/deepseek-harness-runtime/2026-10-15-deepseek-harness-runtime.zh.md) -- **依赖组件:** Agent execution、runtime compatibility、optional dependency metadata、用户 runtime 文档。 - -## 职责与非目标 - -本组件负责 `Agent(runtime="dsh")` 与本地 DeepSeek Harness SDK runtime 之间的 Python SDK 桥接。它解析 runtime 配置,管理长期 SDK 对象和显式 `DSH_HOME`,把 VeADK session 身份映射到 DSH session 身份,通过 DSH 原生工具插件桥接 ADK/MCP 工具,通过 DSH 原生 filesystem skill provider 桥接 skills,并输出 ADK 兼容的 partial、工具和最终回复 event。 - -它不负责 DSH profile 行为、DSH 内部 provider 鉴权语义、bundled binary 构建流水线、Studio 独立的 DeepSeek Harness sandbox 流程、在 DSH 原生 skill loader 内执行 ADK skill checklist,或 descendant session 的 UI 完整投影。 - -## 入口与依赖 - -- 公开 runtime 取值:`Agent(runtime="dsh")`。 -- 公开配置导入:`from veadk.runtime.deepseek_harness import DeepSeekHarnessRuntimeConfig`。 -- 可选依赖:`veadk-python[dsh]`,安装 `deepseek-harness-sdk==0.1.5rc1` 和匹配的传递依赖 `deepseek-harness-runtime-bin`。 -- 外部 SDK import:`deepseek_harness`。 -- Runtime 调度:`veadk.runtime.get_runtime("dsh")`。 - -## 契约 - -- **CON-1 — Runtime 取值。** `dsh` 是稳定公开 runtime 标识。未知拼写由 `Agent` 或 `get_runtime` 拒绝。 -- **CON-2 — 可选依赖。** 未安装 DSH extra 时 import VeADK 成功。解析 DSH runtime 时如果缺少 SDK,会抛出包含 `pip install "veadk-python[dsh]"` 的 `ImportError`。 -- **CON-3 — 配置来源。** `deepseek_harness_runtime_config` 接受 `DeepSeekHarnessRuntimeConfig` 实例或 dict。`VEADK_DEEPSEEK_HARNESS_*` 前缀环境变量覆盖对应字段。 -- **CON-4 — 模型解析。** 若 config `model` 未设置,runtime 使用 `Agent.model_name` 的第一项。ADK before-model callbacks 可以修改 `llm_request.model`;runtime 会用修改后的模型选择 DSH manager 和 SDK config。 -- **CON-4a — Provider 别名。** Config `provider` 是面向用户的 provider 名称,默认 `deepseek-official`;空值会归一到该默认值。`openai`、`volcengine`、`volcengine-ark`、`ark`、`openai-compatible` 和 `third-party` 会在构造 SDK 前映射到当前 DSH 的 `deepseek-official` adapter。未知 provider 名会原样透传,供未来 DSH 原生 adapter 使用。 -- **CON-5 — 懒 sidecar。** 模块 import、runtime lookup 或 agent 构造期间不启动 DSH 子进程。SDK 对象从第一次实际 turn 开始启动。 -- **CON-6 — 显式 home。** 未设置 `dsh_home` 时,runtime 创建并传入非空生成 DSH home。它不依赖隐式 `~/.dsh`。 -- **CON-7 — Session 映射与短期上下文。** DSH `session_id` 对 `(app_name, user_id, session_id, agent name)` 元组稳定。Prompt 也会把可见 ADK session history 序列化进结构化 `` block,保持 VeADK/ADK session state 是短期记忆的事实来源,而不是只依赖 DSH 内部 session state。 -- **CON-8 — 工具桥接。** ADK function tools 和 ADK/MCP toolsets 会在 DSH run 前展开,通过生成的 `defineTool` plugin patch 注册为 DSH 原生工具,经带鉴权的 loopback bridge 在 Python 侧执行,并输出 ADK tool call/response events。 -- **CON-9 — 子智能体转交。** 可转交的 `sub_agents` 通过注入式 `transfer_to_agent` 工具暴露。转交响应会运行目标 agent,并抑制该 turn 的 DSH 最终文本。 -- **CON-10 — Skill 桥接。** ADK `SkillToolset` 与 legacy VeADK skills 会被物料化到隔离 skill root。Runtime 通过 DSH patch 注册 `@deepseek-ai/dsh-skill`、带 `includeDefaultRoots=false` 的 `@deepseek-ai/dsh-skill-filesystem`,以及 `@deepseek-ai/dsh-tool-skill`。已物料化 skill 内容 digest 会纳入 manager key。 -- **CON-11 — Event 映射。** Root-session DSH assistant stream chunks 会输出为 ADK partial model events。DSH 原生 `tool/call` 和 `tool/result` events 会输出为 ADK function call/response events,并排除 VeADK 已桥接的 Python tool 名称以避免重复。 -- **CON-12 — Event 输出。** 每次完成且非转交的 DSH run 会在 ADK after-model callbacks 后输出一个包含 `RunResult.final_response` 的 ADK model event。空 DSH 最终回复表示为空的最终模型响应。 -- **CON-13 — 不支持表面。** ADK flow-only 能力继续由 `veadk.runtime.compat` 覆盖。ADK skill checklist enforcement 与完整 descendant-session projection 不属于本组件当前契约。 - -## 状态与并发 - -有效 runtime 配置在解析 model、provider、DSH profile、home、路径、timeout、显式环境覆盖、已桥接 DSH tool schema digest 和已物料化 skill 内容 digest 后形成 key。每个 key 拥有一个 `DeepSeekHarness` 实例、适用时生成的 `dsh_home`、存在工具时的长期 tool bridge/plugin patch,以及存在 skills 时的 DSH 原生 skill patch/root。Tool plugin 文件会写入 manager 私有临时目录,而不是共享的 `dsh_home`,因此持久 home 可以跨进程复用且不会覆盖每个进程自己的 bridge token。Manager 在进程退出时关闭。 - -共享同一 manager 的 turn 使用 async lock 串行执行。这避免对同一个 SDK stdio client 做未文档化的并发访问。不同 manager key 可以独立运行。 - -当前同步 SDK 无法可靠地从外层 task 取消一个进行中的 DSH `run()`。组件会保留共享 manager,并在同步 SDK 调用返回前继续持有 manager lock,随后再传播 cancellation。它不承诺优雅的 per-turn cancellation。 - -Tool bridge 只在 manager lock 拥有活跃 turn 时接受调用。缺少 per-manager bearer token 或缺少活跃 ADK tool bundle 的请求会被拒绝。 - -## 数据与配置 - -配置字段: - -- `provider`、`model`、`reasoning_effort`、`max_tokens` -- `cwd`、`runtime_cwd`、`dsh_bin`、`profile`、`patches`、`dsh_home` -- `env`、`initialize_timeout_seconds`、`request_timeout_seconds`、`shutdown_timeout_seconds` -- `base_url`、`api_key` - -环境变量覆盖: - -- `VEADK_DEEPSEEK_HARNESS_PROVIDER` -- `VEADK_DEEPSEEK_HARNESS_MODEL` -- `VEADK_DEEPSEEK_HARNESS_REASONING_EFFORT` -- `VEADK_DEEPSEEK_HARNESS_MAX_TOKENS` -- `VEADK_DEEPSEEK_HARNESS_CWD` -- `VEADK_DEEPSEEK_HARNESS_RUNTIME_CWD` -- `VEADK_DEEPSEEK_HARNESS_DSH_BIN` -- `VEADK_DEEPSEEK_HARNESS_PROFILE` -- `VEADK_DEEPSEEK_HARNESS_DSH_HOME` -- `VEADK_DEEPSEEK_HARNESS_INITIALIZE_TIMEOUT_SECONDS` -- `VEADK_DEEPSEEK_HARNESS_REQUEST_TIMEOUT_SECONDS` -- `VEADK_DEEPSEEK_HARNESS_SHUTDOWN_TIMEOUT_SECONDS` -- `VEADK_DEEPSEEK_HARNESS_BASE_URL` -- `VEADK_DEEPSEEK_HARNESS_API_KEY` - -默认 `profile` 是 `sdk-minimal`,因此 VeADK 可以注入自己负责的精确 DSH tool 和 skill 插件。调用方可以选择 DSH 更完整的 `sdk` profile;此时 VeADK 会 patch 已存在的 skill filesystem plugin 配置,而不是插入重复插件组。 - -`request_timeout_seconds` 默认是 `None`,即 VeADK 层不限制 SDK run 的时长。生产调用方如果需要硬性 turn 截止时间,应显式设置它。 - -## 安全与隔离 - -生成的 `DSH_HOME` 路径是进程拥有的临时目录。配置的 `dsh_home`、`cwd` 和 `runtime_cwd` 在使用前会 expand 并 resolve。Runtime 只通过 SDK 配置传入 `api_key` 和 `base_url`;DSH 内部脱敏由 DSH 拥有。 - -用户 prompt 和 session ID 都是不可信输入。Session ID 会 hash 后再嵌入 DSH 标识,不直接使用原始值。 - -## 兼容性与演进 - -本组件是新增能力。现有 runtime 取值和配置保持不变。未来只有在 PRD/spec 契约更新并通过测试覆盖后,才会增加 checklist enforcement、descendant-session projection 和更细粒度 cancellation。 - -## 可观测性与失败 - -Runtime 以不含密钥的方式记录选中的 model/provider 和 session 身份。SDK import 错误、初始化错误和运行错误会继续抛出,除非 ADK on-model-error callback 返回 fallback `LlmResponse`。 - -## 验证 - -| 契约 | 保证 | 测试或验证 | 证据 | -| --- | --- | --- | --- | -| `CON-1` 至 `CON-4a` | Runtime 调度和配置解析。 | `uv run --extra dev pytest tests/runtime/deepseek_harness` | PRD 验证记录 | -| `CON-5` 至 `CON-7` | 懒 manager、显式 home、稳定 session ID。 | `uv run --extra dev pytest tests/runtime/deepseek_harness` | PRD 验证记录 | -| `CON-8` | ADK tools 通过生成式 DSH plugin 和 loopback callback 桥接。 | `uv run --extra dev pytest tests/runtime/deepseek_harness` | PRD 验证记录 | -| `CON-9` | 子智能体转交使用注入式 transfer tool 路径。 | `uv run --extra dev pytest tests/runtime/deepseek_harness` | PRD 验证记录 | -| `CON-10` | Skills 通过 DSH 原生 skill 插件和隔离物料化 root 桥接。 | `uv run --extra dev pytest tests/runtime/deepseek_harness` | PRD 验证记录 | -| `CON-11` | DSH partial 与原生 tool events 会成为 ADK events。 | `uv run --extra dev pytest tests/runtime/deepseek_harness` | PRD 验证记录 | -| `CON-12` | 最终回复成为 ADK event。 | `uv run --extra dev pytest tests/runtime/deepseek_harness` | PRD 验证记录 | -| `CON-13` | 不支持表面已文档化并进入 gate。 | 源码/文档审查和 runtime compatibility 测试 | PRD 验证记录 | - -## 变更记录 - -- 2026-10-15:由 `deepseek-harness-runtime` 引入 `runtime="dsh"` 的初始 active 契约。 -- 2026-10-15:新增面向省略 provider、OpenAI、火山和第三方 OpenAI-compatible endpoint 的 VeADK provider 别名,并映射到当前 DSH adapter。 diff --git a/specs/runtime-dependency-compatibility/README.md b/specs/runtime-dependency-compatibility/README.md deleted file mode 100644 index 378673ae4..000000000 --- a/specs/runtime-dependency-compatibility/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# Runtime Dependency Compatibility - -[中文版](README.zh.md) - -- **Component ID:** `runtime-dependency-compatibility` -- **Status:** draft; implemented changes are governed by the related PRD -- **Revision:** 2026-10-13 -- **Related PRD:** [Align package dependency metadata for Google ADK and OpenTelemetry](../../prd-spec/bugfixes/runtime-dependency-compatibility/2026-10-12-google-adk-opentelemetry-baseline.md) -- **Owned code:** `pyproject.toml`, `uv.lock`, package-metadata regression tests, and narrow ADK compatibility patches covered by the related PRD. -- **Dependent upstream:** `google-adk`, `agentkit-sdk-python`, and OpenTelemetry Python packages. - -## Responsibility and non-goals - -This component owns VeADK's published Python package dependency metadata and the main development lock for the Google ADK, AgentKit SDK, and OpenTelemetry compatibility line. - -It does not own Studio, sandbox, generated project, managed sidecar, or Docker dependency declarations in this scoped change. Those surfaces may have their own rollout constraints and should be updated separately if needed. - -## Entry points and dependencies - -- Package metadata: `pyproject.toml`. -- Development lock: `uv.lock`. -- Regression coverage: tests that assert package metadata, lock metadata, and the ADK 2.7 compatibility paths named in the related PRD. -- External dependency owners: Google ADK, AgentKit SDK, and OpenTelemetry Python. - -## Contract - -- `CON-1`: VeADK package metadata MUST use a verified Google ADK compatibility window. For the 2026-10-13 baseline, that range is `google-adk>=2.7.0,<2.8.0`. -- `CON-2`: VeADK package metadata MUST NOT exact-pin the OpenTelemetry exporter to `1.37.0`. For the 2026-10-13 baseline, it is `opentelemetry-exporter-otlp>=1.37.0`. -- `CON-3`: VeADK package metadata MUST keep AgentKit flexible enough for fresh installs to resolve to the latest compatible AgentKit SDK. For the 2026-10-13 baseline, it is `agentkit-sdk-python>=0.8.5`. -- `CON-4`: The main `uv.lock` is a development resolver output. It SHOULD be regenerated after package metadata changes when referenced package versions are available. -- `CON-5`: Regression tests MUST check package metadata, lock metadata, and the ADK 2.7 compatibility paths affected by this change. - -## Data and configuration - -The source of truth for VeADK package dependencies is `pyproject.toml`. `uv.lock` records a concrete development resolution and does not directly constrain normal `pip install veadk-python` consumers. - -## Security and isolation - -This component must not include credentials, signed URLs, production logs, or personal data in dependency evidence. Dependency resolution may access public package indexes during verification; live cloud/provider operations are outside this contract. - -## Compatibility and evolution - -Changing dependency lower bounds, upper bounds, or exact pins requires a bugfix or feature PRD, resolver evidence, and affected package metadata tests. ADK `2.8.0` and later are outside the current verified window because targeted tests reproduced incompatibility on ADK `2.11.0`. Generated projects, Studio, sandbox, managed sidecar, and Docker dependency changes are outside this scoped contract and should be handled by their owning surfaces. - -## Observability and failures - -Failures surface as resolver errors, package metadata regressions, or lock metadata regressions. They should be reported explicitly with the package and constraint that caused the conflict. - -## Verification - -| Contract | Validation | -| --- | --- | -| `CON-1`, `CON-2`, `CON-3` | Package metadata regression test | -| `CON-4` | `uv lock` | -| `CON-5` | Lock metadata regression test and targeted ADK compatibility tests | - -## Change record - -- 2026-10-13: Scoped the component to main package metadata, the main development lock, and minimal ADK 2.7 compatibility coverage. Package metadata uses `google-adk>=2.7.0,<2.8.0`, keeps `agentkit-sdk-python>=0.8.5`, and keeps `opentelemetry-exporter-otlp>=1.37.0`. diff --git a/specs/runtime-dependency-compatibility/README.zh.md b/specs/runtime-dependency-compatibility/README.zh.md deleted file mode 100644 index f2bfa2470..000000000 --- a/specs/runtime-dependency-compatibility/README.zh.md +++ /dev/null @@ -1,59 +0,0 @@ -# 运行时依赖兼容性 - -[English](README.md) - -- **Component ID:** `runtime-dependency-compatibility` -- **状态:** draft;已实现变更由相关 PRD 管理 -- **修订日期:** 2026-10-13 -- **相关 PRD:** [Align package dependency metadata for Google ADK and OpenTelemetry](../../prd-spec/bugfixes/runtime-dependency-compatibility/2026-10-12-google-adk-opentelemetry-baseline.zh.md) -- **拥有代码:** `pyproject.toml`、`uv.lock`、包元数据回归测试,以及相关 PRD 覆盖的窄范围 ADK 兼容 patch。 -- **依赖上游:** `google-adk`、`agentkit-sdk-python` 和 OpenTelemetry Python 包。 - -## 职责和非目标 - -本组件负责维护 VeADK 发布 Python 包依赖元数据,以及 Google ADK、AgentKit SDK 和 OpenTelemetry 兼容线对应的主开发 lock。 - -本次收窄变更不负责 Studio、sandbox、生成项目、managed sidecar 或 Docker 的依赖声明。这些入口可能有各自的发布和回滚约束,如需更新应单独处理。 - -## 入口点和依赖 - -- 包元数据:`pyproject.toml`。 -- 开发 lock:`uv.lock`。 -- 回归覆盖:断言包元数据、lock 元数据以及相关 PRD 指定的 ADK 2.7 兼容路径的测试。 -- 外部依赖所有者:Google ADK、AgentKit SDK 和 OpenTelemetry Python。 - -## 契约 - -- `CON-1`:VeADK 包元数据必须使用已验证的 Google ADK 兼容窗口。对于 2026-10-13 基线,该范围是 `google-adk>=2.7.0,<2.8.0`。 -- `CON-2`:VeADK 包元数据不得把 OpenTelemetry exporter 精确 pin 到 `1.37.0`。对于 2026-10-13 基线,该范围是 `opentelemetry-exporter-otlp>=1.37.0`。 -- `CON-3`:VeADK 包元数据必须保持 AgentKit 足够灵活,使全新安装可以解析到最新兼容 AgentKit SDK。对于 2026-10-13 基线,该范围是 `agentkit-sdk-python>=0.8.5`。 -- `CON-4`:主 `uv.lock` 是开发 resolver 输出。包元数据变更后,若引用的包版本可获取,应重新生成。 -- `CON-5`:回归测试必须检查包元数据、lock 元数据以及本次变更影响的 ADK 2.7 兼容路径。 - -## 数据和配置 - -VeADK 包依赖的事实来源是 `pyproject.toml`。`uv.lock` 记录一个具体开发解析结果,不会直接约束普通 `pip install veadk-python` 用户。 - -## 安全和隔离 - -本组件不得在依赖证据中包含凭据、签名 URL、生产日志或个人数据。验证时依赖解析可以访问公开包索引;真实云/服务商操作不在本契约范围内。 - -## 兼容性和演进 - -修改依赖下限、上限或精确 pin 需要 bugfix 或 feature PRD、resolver 证据和受影响包元数据测试。ADK `2.8.0` 及更高版本不在当前已验证窗口内,因为目标测试已在 ADK `2.11.0` 复现不兼容。生成项目、Studio、sandbox、managed sidecar 和 Docker 的依赖变更不在本次收窄契约内,应由其所属入口单独处理。 - -## 可观测性和失败 - -失败表现为 resolver 错误、包元数据回退或 lock 元数据回退。报告时应明确冲突的包和约束。 - -## 验证 - -| 契约 | 验证 | -| --- | --- | -| `CON-1`, `CON-2`, `CON-3` | 包元数据回归测试 | -| `CON-4` | `uv lock` | -| `CON-5` | lock 元数据回归测试和目标 ADK 兼容测试 | - -## 变更记录 - -- 2026-10-13:将组件范围收窄到主包元数据、主开发 lock 和最小 ADK 2.7 兼容覆盖。包元数据使用 `google-adk>=2.7.0,<2.8.0`,保持 `agentkit-sdk-python>=0.8.5`,并保持 `opentelemetry-exporter-otlp>=1.37.0`。 diff --git a/specs/skill-observability/README.md b/specs/skill-observability/README.md deleted file mode 100644 index 2f1771966..000000000 --- a/specs/skill-observability/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Skill observability - -[中文版](README.zh.md) - -Component: `skill-observability`; status: implemented (local verification; full regression limitations in PRD); revised 2026-10-22. -Change: [skill-observability](../../prd-spec/features/skill-observability/2026-10-16-skill-observability.md). - -## Ownership and interfaces - -VeADK owns Runtime package acquisition and ADK instruction-load instrumentation in `veadk/skills/` and `veadk/tracing/telemetry/telemetry.py`. `actb-mono/sandboxes/agentkit-skill` owns Python/Go Sandbox installation, tool adaptation and exporter lifecycle. The maintained wire schema below is shared; the sandbox is independently released and does not import an unpublished SDK recorder. Existing tools/signatures/results remain compatible. Skill metrics use the new names and unit below; consumers must update queries. Old metric names are not emitted in parallel. Discovery, resource/script execution, issue reporting and business success are not owned here. - -## Contracts - -- CON-1: `gen_ai_skill_download_count` is an OTEL counter, unit `1`; dimensions are exactly `skill_source`, `skill_id`, `skill_name`, `skill_version`, `skill_space_id`. One acquisition completion, including cache restore and failure, adds one. No event for mere discovery or local symlink creation; outcome is recorded only on the Span. -- CON-2: `gen_ai_skill_load_count` is an OTEL counter, unit `1`; dimensions are exactly the five identity fields above. One successful instruction response adds one. ADK invocation-cache hits still count. Errors, cancellation, forwarding and resource/script access do not. -- CON-3: Download Span `download_skill {skill_name}` has `skill.source`, `skill.id`, `skill.name`, `skill.version`, `skill.space_id`, `skill.consumer`, `skill.download.outcome`, `skill.download.cache_hit`. Outcome is `success` or `error` (including cancellation); failures add `error.type` and a safe static `error.description`, with ERROR status. Successful loading renames the existing tool Span to `load_skill {skill_name}` and adds the five dotted identity fields and `skill.consumer`, without creating a duplicate Span. `skill.consumer` is `runtime` or `skill_sandbox`. Span duration covers the actual operation. Consumer/cache/outcome/error text never becomes a metric label. -- CON-4: Identity follows the table. Missing version/space is the empty string, never a synthesized cache hash. Remote skills without a trustworthy ID and unclassified local skills are excluded. Registry-owned metadata overrides archive metadata for telemetry identity. SkillHub installation records the slug with the installed package, not just the current desired configuration. - -`skill_name` uses the name resolved by the current source: metadata for Runtime/skill spaces, the loaded name for local skills, and the resolved installation name for SkillHub installation. Explicit TOS acquisition first propagates the version API `Name`; when absent, successful acquisition or cache restoration reads the validated package name. Failure before a name is available uses the empty string, never an ID, slug or path as a substitute. Reading a name for telemetry must not change the acquisition result. - -| skill_source | skill_id | skill_name | skill_version | skill_space_id | -| --- | --- | --- | --- | --- | -| builtin_skill | name | name | empty | empty | -| skillhub_skill | slug | resolved name or empty | empty | empty | -| skillhub_skill_space | s-* | resolved name or empty | sv-* when supplied | sp-* | -| agentkit_skill | s-* | resolved name or empty | vN | empty | -| agentkit_skill_space | s-* | resolved name or empty | vN when supplied or present in the legacy package path | ss-* | - -Python supports all five rows. The pinned Sandbox Go SDK still rejects `sp-*`; the user approved retaining that limitation. Local SkillHub receipts are shared across Python/Go selection. - -- CON-5: Record through the global OTEL provider, independently of Span sampling. Runtime load instrumentation is enabled with existing VeADK tracing hooks. No exporter is installed by the SDK recorder. Sandbox periodic OTLP metrics honor `OTEL_SDK_DISABLED`, `OTEL_METRICS_EXPORTER=none`, standard metric/base endpoint configuration and an existing provider. Trace disabling does not disable metrics. Default local collector uses OTLP/HTTP at port 4318; shutdown is bounded and failures are best effort. Metrics use `OTEL_EXPORTER_OTLP_METRICS_PROTOCOL` (or the base protocol), supporting HTTP/protobuf and grpc; interval defaults to 60000 ms. The short-lived SkillHub installer uses 200 ms exporter timeouts and flushes before exit; empty configuration and built-in disabling skip that initialization. Startup cost is recorded in the PRD. -- CON-6: Acquisition owns its observation until completion. Concurrent invocations do not share mutable observation state. Python `to_thread` cancellation does not stop an already-running acquisition: its actual result may arrive later, but no instruction-load success is recorded for the cancelled waiter. No new persistent state except the installed SkillHub provenance receipt. Existing locks, cache validation, retention and cleanup remain authoritative. -- CON-7: New telemetry excludes raw exceptions, URLs, tokens, file paths, instructions and user content. Export/recording failure must not change tool results or suppress application errors. No new credentials/configuration are required. Missing provider gives normal OTEL no-op behavior. - -## Verification and evolution - -CON-1/4/6 map to materializer/cache and sandbox package tests. CON-2/3 map to actual ADK tool and sandbox wrapper tests, including repeated loads, bad arguments, errors and cancellation. CON-5/7 map to in-memory OTEL and local collector tests for metrics, spans, redaction, switches, provider reuse and failures. Commands and actual evidence are maintained in the linked PRD. Adding dimensions or changing count semantics requires revisiting that design and both languages. Implementation status does not claim deployment or full-repository acceptance. diff --git a/specs/skill-observability/README.zh.md b/specs/skill-observability/README.zh.md deleted file mode 100644 index 88fb43c7a..000000000 --- a/specs/skill-observability/README.zh.md +++ /dev/null @@ -1,37 +0,0 @@ -# 技能可观测 - -[English](README.md) - -组件:`skill-observability`;状态:implemented(本地验证;全量回归限制见 PRD);修订:2026-10-22。 -关联变更:[skill-observability](../../prd-spec/features/skill-observability/2026-10-16-skill-observability.zh.md)。 - -## 责任与接口 - -VeADK 在 `veadk/skills/` 与 `veadk/tracing/telemetry/telemetry.py` 负责 Runtime 技能包获取及 ADK 指令加载埋点。`actb-mono/sandboxes/agentkit-skill` 负责 Python/Go Sandbox 安装、工具适配及 exporter 生命周期。共享下述上报结构;Sandbox 独立发布,不导入尚未发布的 SDK 记录模块。既有工具/签名/结果保持兼容。技能指标采用下述新名称和单位,查询方须同步切换;不重复发送旧名称。技能发现、资源/脚本执行、主动问题汇报和业务成功不属于本组件。 - -## 契约 - -- CON-1:`gen_ai_skill_download_count` 是 OTEL counter,单位 `1`;维度严格为 `skill_source`、`skill_id`、`skill_name`、`skill_version`、`skill_space_id`。每次获取结束加一,包含缓存恢复和失败。单纯发现或本地软链接创建不产生事件;结果仅记录在 Span。 -- CON-2:`gen_ai_skill_load_count` 是 OTEL counter,单位 `1`;维度严格为上述五个身份字段。每次成功返回指令加一,ADK invocation 缓存命中仍计数。失败、取消、转发、资源/脚本访问不计数。 -- CON-3:下载 Span `download_skill {skill_name}` 包含 `skill.source`、`skill.id`、`skill.name`、`skill.version`、`skill.space_id`、`skill.consumer`、`skill.download.outcome`、`skill.download.cache_hit`。结果为 `success` 或 `error`(含取消);失败增加 `error.type` 和安全的静态 `error.description`,状态为 ERROR。成功加载将既有工具 Span 命名为 `load_skill {skill_name}`,并添加五个点分身份字段和 `skill.consumer`,不新增重复 Span。`skill.consumer` 为 `runtime` 或 `skill_sandbox`。Span 耗时覆盖实际操作。consumer/cache/结果/错误文本不放入 metric 标签。 -- CON-4:身份遵循下表。缺失版本/空间用空字符串,不使用生成的缓存 hash。缺少可信 ID 的远端技能和无法分类的本地技能不采集。遥测身份以 Registry 元数据为准,覆盖技能包同名 metadata。SkillHub 安装时随实际安装包记录 slug,不能仅依赖当前期望配置。 - -`skill_name` 使用当前来源已解析的技能名称:Runtime/技能空间用元数据,本地技能用已加载名称,SkillHub 安装用已解析的安装名称。显式 TOS 获取优先传递版本接口的 `Name`;缺失时在成功获取或缓存恢复后读取已验证包的名称。获取失败且尚无名称时使用空字符串,不用 ID、slug 或路径冒充名称。名称读取仅用于观测,失败不得影响获取结果。 - -| skill_source | skill_id | skill_name | skill_version | skill_space_id | -| --- | --- | --- | --- | --- | -| builtin_skill | name | name | 空 | 空 | -| skillhub_skill | slug | 已解析名称或空字符串 | 空 | 空 | -| skillhub_skill_space | s-* | 已解析名称或空字符串 | 接口提供时为 sv-* | sp-* | -| agentkit_skill | s-* | 已解析名称或空字符串 | vN | 空 | -| agentkit_skill_space | s-* | 已解析名称或空字符串 | 接口提供或旧包路径中存在的 vN | ss-* | - -Python 支持以上五类来源。Sandbox 固定 Go SDK 仍拒绝 `sp-*`,用户已批准保留这一限制。本地 SkillHub 来源记录供 Python/Go 共同读取。 - -- CON-5:通过全局 OTEL provider 记录,不依赖 Span 采样。Runtime 加载采集随既有 VeADK tracing hook 启用。SDK 记录模块不安装 exporter。Sandbox 周期 OTLP metric 遵循 `OTEL_SDK_DISABLED`、`OTEL_METRICS_EXPORTER=none`、标准 metric/base endpoint 配置,并复用已有 provider。关闭 trace 不关闭 metric。默认本地 collector 使用 4318 端口的 OTLP/HTTP;退出有时间上限,导出失败不阻断业务。Metrics 使用 `OTEL_EXPORTER_OTLP_METRICS_PROTOCOL`(或基础 protocol),支持 HTTP/protobuf 和 grpc;默认间隔为 60000 ms。短生命周期 SkillHub 安装器使用 200 ms exporter 超时,退出前尽力 flush;空配置和仅禁用内置技能跳过初始化。启动开销记录在 PRD。 -- CON-6:每次获取独立持有观测状态直到结束,并发 invocation 不共享可变观测状态。Python `to_thread` 等待取消不会停止已经运行的获取:实际获取结果可能稍后产生,但取消的等待者不计指令加载成功。除已安装 SkillHub 的来源记录外,不新增持久化状态。既有锁、缓存验证、保留和清理规则仍然有效。 -- CON-7:新增遥测不含原始异常、URL、token、文件路径、指令和用户内容。记录/导出失败不得改变工具结果或吞掉业务异常。不要求新凭据或配置。没有 provider 时采用 OTEL 正常空操作行为。 - -## 验证与演进 - -CON-1/4/6 对应 materializer/cache 和 Sandbox package 测试。CON-2/3 对应真实 ADK 工具与 Sandbox wrapper 测试,包含重复加载、参数错误、异常和取消。CON-5/7 对应内存 OTEL 和本地 collector 测试,覆盖 metric、Span、脱敏、开关、provider 复用及故障。具体命令和实际证据在关联 PRD 中维护。增加维度或改变计数语义须重新评审设计并同步双语。implemented 状态不表示已部署或全仓验收通过。 diff --git a/specs/studio-agents-md/README.md b/specs/studio-agents-md/README.md deleted file mode 100644 index 17d3ec288..000000000 --- a/specs/studio-agents-md/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Studio MPA AGENTS.md - -[中文](README.zh.md) · [Design](../../prd-spec/features/studio-agents-md-edit/2026-10-21-studio-agents-md-edit.md) - -## Ownership and scope - -The MPA information rail edits the selected Runtime's document. The Studio backend -owns authorization and credential injection; Runtime owns revisions and application -to Main/Worker. This adds no model/Skill/MCP or Bot Profile editor, cache or deployment. - -## Interface and security - -Proxy GET/POST `/api/v1/studio/agents-md`, scoped by Runtime ID and region. Require a -Studio administrator plus existing Runtime visibility authorization. Strip incoming -`X-MPA-Studio-Key` and inject only the server-resolved current Runtime key. Reject -noncanonical path aliases. Never store that key in browser state. - -GET returns `{content: string, revision: integer}`; POST accepts the same shape, -updates only the document, and returns the saved/current revision. Empty content -clears custom instructions; the limit is 40000 characters. Concurrent edits return -409, missing/invalid credentials 401/503, denied access 403, invalid body 422. -Old Runtimes may return 404/405/501; show an upgrade message and preserve metadata viewing. - -## Interaction and state - -Reuse ModalLayout, Button, Textarea, ScrollArea and Base UI Dialog. Load persisted -text on open; disable saving during loading/saving, when unchanged, over limit, or -when a previous save's result is uncertain. Confirm clearing or discarding edits. -Retain drafts on errors. A conflict requires refreshing and reconciling the current -revision. Network/5xx save errors require readback before another write. Prevent -repeat saves and composition-time submission; abort requests on unmount and discard -late responses. The rail remounts when the Runtime/region target changes. - -## Effective configuration - -Running turns keep their document snapshot. The next Main turn uses saved content, including reused conversations; clearing -removes custom Main content but retains platform rules. Worker files receive the -snapshot, but native reused Worker threads may retain old instructions. Worker -hot reload remains deferred and is not guaranteed by a successful Studio save. History is retained. The API verifies delivery of -configuration, not deterministic model behavior. Editing updates the selected -Runtime directly; ArkClaw's separate debug/publish environments are not introduced. - -## Verification - -Python proxy authorization/forged-header tests, Runtime persistence/CAS and turn -snapshot tests, frontend load/save/error/clear/IME/cancellation tests, browser -checks and production build. Actual results and limitations are in the paired design. diff --git a/specs/studio-agents-md/README.zh.md b/specs/studio-agents-md/README.zh.md deleted file mode 100644 index 434ac7c73..000000000 --- a/specs/studio-agents-md/README.zh.md +++ /dev/null @@ -1,42 +0,0 @@ -# Studio MPA AGENTS.md - -[English](README.md) · [设计](../../prd-spec/features/studio-agents-md-edit/2026-10-21-studio-agents-md-edit.zh.md) - -## 职责和范围 - -MPA 信息侧栏编辑当前选中 Runtime 的文档。Studio 后端负责鉴权和注入凭据, -Runtime 负责修订存储及 Main/Worker 生效。不新增模型、Skill、MCP、Bot Profile -编辑器、缓存或部署能力。 - -## 接口与安全 - -按 Runtime ID 和地域代理 GET/POST `/api/v1/studio/agents-md`。需要 Studio -管理员身份,并通过现有 Runtime 可见性校验。剥离浏览器传入的 -`X-MPA-Studio-Key`,仅注入服务端解析的当前 Runtime 密钥;拒绝非规范路径。 -密钥不得存入浏览器状态。 - -GET 返回 `{content: string, revision: integer}`;POST 接受同样结构,仅更新文档, -返回已保存/当前修订。空内容清除自定义指令,最多 40000 字符。并发修改返回 -409,凭据错误/缺失为 401/503,无权访问 403,非法输入 422。 -旧 Runtime 可能返回 404/405/501;显示升级提示,保留元数据查看能力。 - -## 交互与状态 - -复用 ModalLayout、Button、Textarea、ScrollArea 和 Base UI Dialog。打开时读取 -持久配置;加载、保存中、内容未变、超限或前次保存结果不明时禁用保存。 -清空、放弃编辑需确认,错误保留草稿。冲突时刷新并核对当前修订;网络/5xx -保存异常后需先回读才能重写。防重复提交及中文输入组合过程中提交;卸载时 -取消请求并忽略迟到响应。切换 Runtime/地域时侧栏重建。 - -## 生效配置 - -进行中的回合保持文档快照;下一轮 Main 使用保存的内容,包括已有会话。清空 -移除 Main 自定义部分,保留平台规则。Worker 文件收到快照,但复用中的原生 -Worker 线程可能继续使用旧指令。Worker 热更新仍为暂缓项,不属于 Studio 保存 -成功能够保证的行为。历史记录保留。接口验证配置传递,而非模型回答的确定性。 -直接更新选中 Runtime,不引入 ArkClaw 独立调试/发布双环境。 - -## 验证 - -包含 Python 代理鉴权/伪造头测试、Runtime 持久化/CAS/回合快照、前端加载、保存、 -错误、清空、IME、取消测试、浏览器检查及生产构建。实际结果和限制见双语设计。 diff --git a/specs/studio-mpa-control-plane/README.md b/specs/studio-mpa-control-plane/README.md deleted file mode 100644 index e90238ff1..000000000 --- a/specs/studio-mpa-control-plane/README.md +++ /dev/null @@ -1,157 +0,0 @@ -# Studio MPA Control Plane - -- Component ID: `studio-mpa-control-plane` -- Status: `draft` for remaining P0 amendments; baseline Phase 1, part of Phase 2, and P0 Studio slices through S5-12 browser validation are implemented -- Revision: 2026-10-14 -- Chinese: [README.zh.md](README.zh.md) -- PRD: [MPA Studio Turn Control and Resource Workspace](../../prd-spec/features/mpa-studio-control-plane/2026-09-13-mpa-studio-control-plane-design.md) - -## Responsibility - -Own the Studio-to-mpa-agent contract for Ark model discovery, immutable Turn snapshots, whole-Turn lifecycle commands/status, Session resource mounts, typed runtime topology, Studio tool discovery, and role-protected secret copy. mpa-agent is the lifecycle authority; Studio is a client and presentation layer. - -## Contracts - -- `CON-1`: Account model discovery is server-side, credential-free in browser payloads, compatibility-filtered, cached with freshness, and fails explicitly. -- `CON-2`: An accepted Turn freezes its model and submitted resource payload. Non-terminal Turns never observe later browser-side Session edits. A server-owned Session resource revision is not implemented yet. -- `CON-3`: Lifecycle state is durable and generation-controlled. Studio exposes idempotent pause/resume only. `paused` requires main executor and all active workers at safe points. -- `CON-4`: Pause and resume share the Composer primary button. Resume within five minutes continues the same Turn; an expired or unrecoverable resume requires the user's click and creates an incremental Turn in the same Session. Disconnect and timeout alone never start work. -- `CON-5`: Studio currently stores Session mount selections in browser local storage and submits stable Skill Space IDs and versions with the next Turn. Server-owned revisions, conflict detection, and retained invalid-resource state remain follow-up work. -- `CON-6`: Topology contains only configured/observed nodes and typed edges; absent optional resources are affordances, not active nodes. -- `CON-7`: Agent Card capabilities gate model, lifecycle, mounts, and topology behavior for backward compatibility. -- `CON-8`: Environment list responses never contain secret values. Authorized secret copy is no-store, audited without value, and never rendered or persisted by Studio. -- `CON-9`: Composer uses one footer row for attachment, Agent target, model, usage/status, and one stateful primary button. The button means send, pause, pending, or resume according to authoritative Turn state; responsive layouts cannot overlap. -- `CON-10`: Usage exposes input/output/reasoning/cache/total for current Turn and cumulative Session; replay is monotonic-delta safe. -- `CON-11`: Conversation projection preserves `reasoning` and Agent `thought` as distinct semantic kinds. Stable event identity is preferred; cross-channel text deduplication is limited to exact or cumulative-prefix transport replays within the same task/invocation and kind. -- `CON-11a`: Persisted conversation replay assigns an identity that is unique across the entire Session transcript to every user-delimited assistant segment. A projector-local sequence may be reused only when its prefix contains a monotonically increasing history-segment index; later rounds must never upsert over an earlier assistant turn. After streaming completes, Studio may replace the live transcript with persisted history only when that history contains the exact final assistant event that triggered reconciliation and no newer local assistant turn has started. Browser acceptance must count and inspect `.turn--assistant` nodes in order before and after a delayed persistence refresh and a full-page reload; finding expected text anywhere in the page is not sufficient because the same text can appear in the user prompt or reasoning. -- `CON-11b`: An MPA `sandbox_task` activity must reach a terminal UI state in both live streaming and persisted replay. Runtime persistence can contain a sandbox `invocation.completed` event followed by a wrapper `functionResponse` with `status=completed` and `finalAlreadyEmitted=true`, while the live subscription can expose only the wrapper response after answer deltas. Studio treats that wrapper as a control terminal: when its invocation still has an active projected turn, it marks the existing parent `sandbox_task` block completed, preserves already streamed answer/tool blocks, records the wrapper event as the turn completion fence, and closes streaming. If the persisted `invocation.completed` event already closed the turn, the same wrapper is ignored and must not create a duplicate tool card. -- `CON-12`: Tool activity exposes a tool-specific safe action and subject. Unknown tools expose their concrete name rather than a generic completed label. -- `CON-13`: Sanitized tool inputs/results remain completely retrievable by an explicit copy/download action. Preview rendering is bounded, truncation is disclosed, and all detail paths share the same redaction rules. -- `CON-14`: Turn model selection is searchable and keyboard accessible, and remains disabled while a non-terminal Turn owns an immutable model snapshot. -- `CON-15`: Runtime listing exposes `agentCategory` and accepts `agentCategory=general|mpa` for server-side category filtering before visible pagination. The authoritative and only MPA source is Runtime tag `veadk:agent-type=mpa`; image names and artifact URLs are not category inputs. `agentCategory=mpa` must first query the Tag service with a positive tag filter to obtain candidate Runtime IDs, then hydrate those records by Runtime ID, so sparse MPA results do not require scanning unrelated Runtime pages. Tag reads use the server-selected provider endpoint: BytePlus uses `open.byteplusapi.com`, while Volcengine retains its generated-SDK endpoint resolution. Browser inputs cannot select or override this endpoint. -- `CON-31`: Studio's MPA list narrows `CON-15` discovery to Runtimes carrying both `veadk:agent-type=mpa` and `veadk:managed=true`; administrators with `scope=all` see all Studio-managed MPA Runtimes, while `scope=mine` additionally requires trusted `veadk:owner`. `veadk:provisioner=studio-mpa` is written for future provenance audit but is not yet mandatory for compatibility with existing generic Studio MPA Runtimes. Tag-service failure remains fail-closed, browser inputs cannot disable the mandatory managed filter, and authorization remains independent. Runtime TRN region is applied before hydration because the Tag service can return global mappings. A provider-confirmed missing Runtime may be skipped as a stale tag mapping; any other Runtime-detail hydration failure remains an error rather than being rendered as an empty list. Implemented and locally verified on 2026-09-29 by [Studio-created MPA Runtime filtering](../../prd-spec/features/studio-mpa-source-filter/2026-09-28-studio-mpa-source-filter.md); real-browser discovery remains a separate local acceptance step. - -## State and data - -- Studio-visible Turn states: `queued`, `running`, `pausing`, `paused`, `resuming`, `interrupted`, `completed`, `failed`. Internal terminalization states may exist but are not user actions. -- Turn control stores generation, idempotency, desired/observed state, participant acknowledgement, safe point, snapshot revision, and classified failure. -- Session resources currently store browser-local deltas separately from Agent defaults. The submitted Turn payload stores resolved Skill IDs/versions and remains immutable for that Turn. -- The current topology combines Runtime-configured Agent/Sandbox nodes with browser-selected Session resources. Dynamic worker lifecycle nodes and a server-owned session execution configuration version remain follow-up work. - -## Failure and security - -Invalid model/resource inputs fail before side effects. Transitional control failure is never displayed as paused. Conflicts return authoritative generation/state. Secret values are excluded from normal APIs, DOM, state, storage, logs, telemetry, screenshots, and error details. Clipboard copy is a deliberate authorized disclosure and must be audited. - -## Compatibility - -Old agents keep one default model and their existing non-MPA stop behavior, hide unsupported pause/resume controls, and render verified data only. Native ArkClaw and non-MPA Studio contracts remain unchanged. - -## Verification - -Contract tests cover state transitions, idempotency, races, model snapshots, submitted mounts, topology, authorization, redaction, and Runtime category filtering. Browser tests cover responsive layout, the single dynamic primary button, model filtering, MPA category selection, Skill Space discovery, and terminal control cleanup. Live Runtime tests prove the model catalog, immutable request model, lifecycle status/control path, configured Agent-to-Sandbox topology, and real Skill Space discovery. Main/worker Skill propagation is covered by targeted contract tests. Full live worker pause/resume, five-minute incremental continuation, refresh recovery, and invalid-resource execution gates remain follow-up verification. -Conversation projection tests additionally cover status/artifact replay, legitimate repeated prose, semantic reasoning/thought separation, nested tool payloads, large sanitized values, specific and unknown tool labels, and searchable model keyboard behavior. - -## MPA information rail (implemented 2026-09-18) - -- CON-16: The conversation rail is visible only for server-classified MPA Runtimes. AGENTS.md comes from the current Runtime’s authenticated `/api/v1/studio/agent-info` `agentsMd`, with explicit empty, unsupported, denied and failed states. Generic instructions are not a substitute. -- CON-17: Bound skills come from configured Skill Space nodes in the Agent Card and the authorized paginated SkillSpace API in the Runtime region. Temporary Session skill mounting is removed; previously stored temporary selections are not sent to MPA. Refresh/cancellation cannot mix Runtime identities or turn a failed listing into empty success. - -Design: [MPA information rail](../../prd-spec/features/mpa-agent-info-rail/2026-09-18-mpa-agent-info-rail.md). - -The Runtime proxy handles `GET /web/agent-info/{app}` for a Runtime tagged `veadk:agent-type=mpa`, including native app IDs and `a2a-default`. Its additive `AgentInfo.agentCategory` is `mpa`; generic synthesized A2A responses use `general`. Other existing responses may omit the field, which hides the rail. - -`AgentInfo.mpa` contains `agentsMd: string | null`, `agentsMdStatus`, `skillSpacesStatus`, and `skillSpaces: {id: string, region: string}[]`. Status values are `ready`, `unsupported`, `forbidden`, and `error`. A document read has a 10-second network timeout with a 4-second connect timeout; 401/403 are forbidden and 404/405/501 unsupported. Studio calls the new read-only metadata endpoint with a dedicated `X-MPA-Studio-Key` from its server-resolved Runtime key. On 404/405 it can try the legacy `/api/v1/agents` JWT route without that dedicated header; authorization failure there means the Runtime needs an upgrade. No API key is substituted for a JWT. Invalid payloads and transport failures are errors. Only document/name/description/model strings are allowed through from the upstream details response. Space discovery remains independent from document read failures. - -Space IDs come from configured `skill-space` nodes connected to an `agent` node by a `mounts` edge in `urn:veadk:mpa:resource-topology:v1`. Missing topology is unsupported, not an empty-success binding. Space listings use the existing authorized `/web/skill-spaces/{id}/skills` route, request 100 items per page, and reject no-progress or over-100-page traversal as incomplete errors. Each request retains the existing 30-second client timeout and supports cancellation. Counts are shown only for complete listings; degraded results and partial failures retain readable items with a warning, while denied access clears prior skills. No changes are made to Runtime credentials, bindings, or deployment. Bound skill names open the permission-aware [Skill document editor](../studio-skill-document/README.md). - -## Managed creation boundary - -Studio MPA creation and `veadk mpa provision` are owned by [Studio MPA creation](../studio-mpa-creation/README.md). That path prepares account resources and uses real Runtime metadata bootstrap. The existing responsibilities and legacy entry points described here remain unchanged. - -Conversation projection tests additionally cover status/artifact replay, legitimate repeated prose, semantic reasoning/thought separation, nested tool payloads, large sanitized values, specific and unknown tool labels, searchable model keyboard behavior, distinct identities for successive persisted assistant rounds, and event-fenced live-to-persisted transcript reconciliation. - -## Proposed AgentKit P0 amendments - -Governed by [P0 functional migration PRD](../../prd-spec/features/mpa-p0-productionization/2026-09-15-mpa-p0-productionization-design.md). The sections above describe baseline behavior/requirements and historical verification, not proof of the following changes. The proposed runtime protocol is owned by [MPA Runtime Control](../mpa-runtime-control/README.md); Studio consumes it rather than defining another state authority. These amendments supersede baseline browser-local resource authority for the new AgentKit mode only after implementation. - -- `CON-16`: Current scope reuses the existing Studio creation form and AgentKit platform resources, then adds MPA Runtime provisioning, Profile application, session execution configuration, conversation results, and Debug. Managed Agent CRUD/version/Session and MPA template/expert/review-install products are excluded. Message-channel binding and configuration now exist in mainline under the separate `mpa-channels` contract; shared APIG/Runtime/database/Skill-Space deployment support also exists in Runtime mainline. Their provider-specific acceptance is independent of this P0 ledger. Website integration, Resource Library, and Search remain deferred here. No historical ArkClaw data import. -- `CON-17`: Management backend derives identity from a validated Studio OAuth/gateway bearer and forwards only that validated token to a custom-JWT Runtime; it does not self-issue a new token. CLI authenticates to the same BFF. Never accept browser owner/account headers as authority, expose runtime credentials in the UI, or substitute a cloud service role for user identity. M0 proves issuer/audience/client, JWKS rotation, claim mapping, and revocation before enabling this mode. -- `CON-18`: Replace browser-local MPA execution-config authority with runtime `CON-3` “session execution configuration version.” Preserve unsaved edits only as clearly labelled drafts; GET refreshes authoritative state. PATCH uses strong ETag, handles `428` and `412`, and shows current state plus explicit retry/reapply. Never silently overwrite another client. Turn creation uses the accepted execution-config revision. AgentKit/Studio retains resource bodies; Runtime stores stable references and Turn execution records. -- `CON-19`: UI renders allowed lifecycle actions and observed participant state from runtime `CON-4`. A partial pause/deadline failure is not paused. For `new_turn_required`, explicit continuation calls the runtime-owned idempotent continuation API; it never submits an independent fallback prompt. No disconnect or polling timeout starts work. -- `CON-20`: Studio validates and normalizes the accepted Agent draft into the authoritative MPA Profile revision; Runtime persists immutable executable revisions. Same revision/digest is idempotent; lower revisions or same-revision/different-digest conflict. Existing Sessions pin their creation-time Profile revision and adopt a newer revision from the next Turn only after explicit successful upgrade. Creation shows deploying and cannot report runnable until Runtime, Profile, and execution-ready smoke all pass. -- `CON-20a`: The browser calls only VeADK BFF `/web/mpa/agents`. BFF validates the existing Studio Agent draft and calls the typed mpa-agent Profile API. P0 does not call Managed Agent CRUD/version APIs and does not use server-side arkcli subprocesses. Create/update operations persist through Studio TOS `forbid_overwrite` plus ETag; both write endpoints require a browser-persisted `Idempotency-Key` and return `202 + operationId`. The same principal/operation/target/key/hash returns the same operation. Refresh recovers by active-list then owner-scoped operation polling until terminal. A retryable failure invokes `/web/mpa/agent-operations/{operationId}/retry` with the original normalized request; it must not repeat `/web/deploy-agentkit`. -- `CON-20b`: Local Studio development may use a process-local MPA operation repository only when `veadk studio --dev` is active and Studio TOS is not configured. It must preserve the same operation ID, request-hash idempotency, and ETag compare-and-swap semantics needed by the UI, but it is not durable and is not release evidence. Non-dev deployments without Studio TOS keep returning `mpa_operation_service_unavailable` for Profile write routes. -- `CON-21`: Current interactive/Debug execution delegates to runtime services with authorization and isolation. Mainline channel management is consumed only through its dedicated Automations and Runtime-proxy contract, and complete artifact answers are replayed by the Runtime ADK stream. This P0 does not duplicate those product surfaces or count their provider tests as P0 acceptance. Job scheduling and library-wide artifacts/adapters remain separate contracts. -- `CON-22`: BFF and CLI share the public async `veadk.integrations.mpa.MpaControlPlaneClient` schemas/auth/errors/capability negotiation without CLI importing server routes. The client calls only Studio BFF APIs, never accepts a Runtime Secret, normalizes bearer injection, preserves response ETags, requires caller-stable `Idempotency-Key` for writes and `If-Match` for CAS updates, and maps HTTP/timeout failures into redacted `MpaControlPlaneError` values with `code`, `status_code`, `request_id`, `retryable`, and optional `current_state`. Only bounded machine-readable response codes may populate `code`; free-form response details fall back to `http_` and are not exposed. Lifecycle recovery uses active-list plus owner-scoped `GET /web/mpa/agent-operations/{operationId}` for terminal results and retry uses the original request identity rather than inventing a new key. Normal/loading/empty/error/denied/stale/retry states are distinct; model lock, IME/keyboard and narrow-window rules still apply. Unsupported Runtime capabilities show explicit read-only/unsupported behavior, never insecure fallback. -- `CON-22a`: The existing `veadk mpa create` remains the backward-compatible infrastructure provisioning command. Studio control-plane parity is exposed under the separate `veadk mpa control` group with `view`, `create`, `update`, `operation list|get|retry`, `profile status|apply`, `session config-get|config-patch|profile-upgrade`, and `delete-preview`. The group accepts the Studio BFF base URL and a bearer from explicit options or controlled environment variables, never prints the bearer, and reads Profile/request/change payloads from JSON files or stdin. Mutations require an explicit caller-persisted idempotency key where the BFF contract requires one; CAS commands require an explicit ETag or Runtime revision. Successful results and control-plane errors are JSON; local option/input errors use Click's usage-error text. Exit codes are stable: `0` success, Click's `2` for local usage/input errors, `3` for `401/403`, `4` for `409/412/428`, `5` for retryable transport or server failures, and `1` for other validated control-plane failures. Chat, Turn execution, and Runtime diagnostics remain existing Studio execution/diagnostic surfaces; this CLI does not call Runtime directly or claim parity for a BFF contract that does not exist. - -New-platform data/version compatibility remains required; only historical ArkClaw migration is excluded. Tests for `CON-16` through `CON-22` map to PRD `AC-1`, `AC-2`, `AC-4`, `AC-5`, `AC-7`, `AC-8`, `AC-10`, with unit/BFF tests, real browser flows and isolated cloud E2E. Status: S1 creation/progress, S2 execution-config BFF/UI, S3 run/cursor refresh, S4-05 Studio continuation wiring, S4-06 Runtime authorization/side-effect rechecks, and S4-07 browser lifecycle gates are implemented and locally verified. S2 browser concurrency passes through BC-04/05 against the loopback scenario fixture, S3 refresh recovery passes through BC-06, and S4 pause/resume/continue passes through BC-09. Live cloud and full Debug/Trace closure remain pending. Required frontend/backend/build gates remain in [frontend/SPEC.md](../../frontend/SPEC.md). - -2026-09-15: P0 functional-migration amendments added as draft. - -2026-09-15: Implemented the Studio S2 BFF/client/UI subset for Session execution configuration: typed `/web/mpa/sessions/{sessionId}/execution-config` GET/PATCH, typed `/web/mpa/sessions/{sessionId}/profile-upgrade`, profile-status lookup, ETag/`Idempotency-Key` forwarding, `412 currentState` propagation, MPA Runtime category propagation, MPA tab creation entry, and an MPA-only Session configuration panel scoped to the current Runtime and current business Session. - -## Page-entry and adapter contracts - -PRD section 6 records the actual `frontend/src/App.tsx` and `frontend/src/ui/MyAgents.tsx` entry audit. Current facts: MPA listing and creation entry have been wired for the P0 path, and S4-05 replaces the previous prompt-based Session continuation fallback with the Runtime-owned `/continue` plus `/sse` flow. Website and Feishu are follow-up evidence only. - -- `CON-23`: Preserve category/Runtime/region/agent/business Session and authenticated scope through page navigation, picker, creation, draft, detail and history. Use full scoped keys for caches and invalidate on identity/target changes. Runtime tags determine category; negotiated capabilities determine available actions. Global feature flags and hidden buttons do not authorize requests. MPA create must exist in the MPA tab and empty state and select MPA provisioning explicitly. -- `CON-23a`: MPA creation intent is `{category: "mpa", region, source}`, shared by tab/header/empty state, never inferred from global state. MPA detail uses a dedicated view-model. It may reuse page shells and loading/error components but not GitHub delivery versions, generic Runtime draft/update, evaluation, or optimization business logic. -- `CON-24`: Existing `StudioWorkspace.environmentIds` describes an environment collection. Its ID is distinct from authorization `workspaceId` and worker mount identity. Resolve and authorize this relation server-side; no direct ID substitution or UI-derived tenancy. Resource/default/Session edit uses the runtime revision contract and does not mutate an active Turn. -- `CON-25`: `GET /web/mpa/agents/{mpaInstanceId}/view` returns `MpaAgentView`, combining scope, MPA instance/current Profile revision, the unique Runtime binding/version/readiness, active operation/stage, capabilities, and safe error. Zero Runtime is `runtime_missing`; multiple is `binding_ambiguous` and blocks writes. An `orphan_runtime` has one authorized Runtime but no applied Profile: `capabilities.canWrite` remains true so Studio can perform the first Profile apply, while `canDebug` remains false and Session configuration stays blocked until the binding becomes `bound`. A Runtime whose data plane still requires legacy `X-Jwt-Token` is reported as `runtime_legacy_auth_unsupported`: Studio keeps showing the Runtime binding, but `canWrite=false`, `canDebug=false`, and Session configuration remains unavailable until the Runtime image is updated. Even with a bound Runtime, `canDebug` remains false while its lifecycle operation is `active` or `failed_retryable`; chat/debug becomes available only after recovery reaches a safe terminal state. MPA editing uses the Studio Profile and Runtime persists its immutable revisions. Debug uses an isolated Runtime Session. Do not reuse generic Python projects, personal Sandbox, evaluation, or optimization business logic. GitHub delivery can be reused only through explicit MPA compatibility preflight. The S5-02/S5-03/S5-05/S5-08 slices are implemented: the BFF probes configured Runtime regions, accepts only visible `veadk:agent-type=mpa` bindings, returns 0/1/N binding status, hydrates the unique binding through Runtime `profile-status`, and the frontend consumes this view plus Runtime Console diagnostics. -- `CON-25a`: AgentKit Studio is the unified MPA control entry. MPA detail exposes an Agent-level `Profile configuration` section with current apply status, revision/ETag, model, system prompt, and Tool/Skill/MCP summaries. It applies the current Studio Profile through the typed MPA BFF operation path and never probes generic `/list-apps` Runtime update capability for MPA Agents. First apply uses create semantics and `If-None-Match: *`; later applies use the Runtime revision/ETag. The browser idempotency key is deterministic from Runtime ID, operation kind, and canonical Profile SHA-256, so retrying the same Profile replays the same operation while changed content gets a new key. -- `CON-29`: MPA Runtime write paths must run the shared P0 compatibility preflight before any AgentKit SDK or GitHub mutation. This covers direct Runtime update via `/web/deploy-agentkit`, GitHub Actions delivery setup via `/web/github-delivery/cicd-pipeline`, initial delivery branch setup via `/web/github-delivery/init-main`, bound source sync via `/web/github-cicd/runtime-sync`, and rollback PR creation via `/web/github-delivery/rollback-pr`. The browser marks MPA flows with `agentCategory=mpa`; an explicit `mpaCompatibilityManifest` or legacy `compatibilityManifest` can override the locally generated shape. Failures return a safe `409` detail with `code=mpa_compatibility_preflight_failed`, `status`, and `errorCode`; the manifest body and secret-like fields are never echoed. Absence of a pinned manifest in local UI paths only runs protocol-shape validation and is not release evidence; the final release gate must run VC-19 with real pinned revisions and image digests. -- `CON-30`: MPA deletion previews authorized binding, active lifecycle operations and visible Runtime sessions; it does not query Profile. Active operations or sessions in `queued|running|pausing|paused|resuming` block deletion before side effects; failed operations alone do not. Session/discovery errors are surfaced rather than represented as empty results. The server rechecks Runtime tags, ownership and references. Cleanup removes visible idle sessions, exclusively owned managed sandbox templates and Skill Spaces, then AgentKit Runtime. Shared/imported/unproven resources, databases, Workspaces, APIG and network remain. Account/region registry locking and durable cleanup state support partial retries and prevent provisioning from recreating deleted resources. Missing session/resource responses are idempotent only for structured not-found errors. Confirmation shows cleanup IDs, retained-resource reasons, loading, errors and preview retry; cancellation aborts preview and ignores late responses. Session counts describe only the authenticated Runtime API’s visible sessions. -- `CON-26`: Follow-up only: Resource Library, global Search, and artifact indexing/source adapters. No current development or acceptance. Session resource references and conversation results remain in scope; MPA templates/experts/reviews are not part of this design. -- `CON-27`: Message-channel binding for existing MPA Runtimes is implemented in mainline for Feishu, DingTalk, and WeCom and is governed by `specs/mpa-channels`; it is no longer an unimplemented follow-up. Scheduled-task product acceptance and website visitor/A2A integration remain separate follow-ups. This P0 does not add duplicate channel adapters, CLI parity, or provider live gates. -- `CON-28`: Applications/developer/global-admin pages retain non-MPA semantics. Gate incompatible MPA targets rather than hiding all general features. MPA runtime admin does not imply platform user-management admin. Trace/log/usage/feedback and CLI examples preserve runtime/worker/Session/Turn correlation and redaction. No unsupported coding/GitHub automation is silently relabelled as MPA. - -Implementation note: `CON-23`, the S2 subset of `CON-23a`/`CON-24`, the S4-05 continuation client path, the S4-06 Runtime authorization recheck path, the S4-07 lifecycle browser fixture, the S5-02/S5-03/S5-08 `CON-25`/`CON-25a` view-model and Profile-management path, the dev-only local store in `CON-20b`, the S5-05 diagnostics consumption path, S5-06 `CON-29` compatibility preflight, and S5-07 `CON-30` delete preview/staged cleanup are implemented for MPA category propagation, MPA creation entry, current-Runtime/current-Session scoping, execution-config BFF/UI calls, browser two-client CAS handling, Runtime-owned continuation streaming, localized checkpoint-continuation state display, consuming Runtime `403 runtime_action_forbidden` as the control-plane denial signal, returning `runtime_missing|bound|binding_ambiguous|orphan_runtime|runtime_legacy_auth_unsupported` MPA detail state from the BFF, allowing first Profile apply for one writable orphan Runtime while blocking Session configuration/diagnostics, keeping old `X-Jwt-Token` Runtimes visible but read-only with localized guidance, displaying Runtime Console run/trace correlation in an MPA-only Diagnostics section, rejecting incompatible MPA update/release/rollback requests before mutation, and preventing MPA Runtime deletion while active operations or active sessions exist. - -2026-09-20: `studio-mpa-profile-errors` implemented the local Profile page recovery fix. Local Studio validation must launch with `--dev` when Studio TOS is not configured so MPA operation recovery uses the process-local repository. The BFF now classifies old Runtime `X-Jwt-Token` failures and plain Profile-status 404s into stable safe errors, and the frontend presents localized guidance while preserving fail-closed write/debug behavior. - -2026-09-18: `CON-22` shared-client extraction is implemented. The public Python integration owns the cross-consumer Profile, operation, capability, execution-config, delete-preview, structured-error, ETag, and idempotency contracts; Studio's Runtime transport imports the shared Profile/execution-config schemas instead of defining copies. The BFF now exposes owner-scoped operation lookup in addition to active-list/retry, so CLI polling can recover terminal operations without depending on FastAPI route modules or scanning only active state. This is a Studio/VeADK boundary change only; it adds no Runtime endpoint or persisted model. - -2026-09-18: S5-09 design review added `CON-22a`. It resolves the legacy provisioning-name collision and limits CLI parity to management contracts already exposed by the Studio BFF. No Runtime execution endpoint, Managed Agent API, or new persisted model is introduced by the CLI slice. - -2026-09-18: S5-09 implements `CON-22a` with the separate `veadk mpa control` command tree. Loopback contract tests cover every command group, shared schema validation, Studio bearer injection, 202 operations, ETag/CAS, stable idempotency-key replay after timeout, old-Runtime read-only output, redacted structured errors, and stable exit codes. The existing provisioning command remains compatible, and the Runtime/API/data model is unchanged. - -Validation follows P0 PRD `AC-1` through `AC-12`: Studio AgentDraft normalization, MPA Profile revision application, execution-config CAS, chat/worker, control/continuation, history, Debug/Trace, live identity/Profile apply, and PostgreSQL concurrency. Browser coverage includes refresh recovery, two-client CAS, loading/empty/error/denied/stale/retry; IME and narrow-width retain real-browser evidence. Resource Library, Search, scheduled tasks, Feishu, and website integration are deferred. S2 local verification: BFF pytest returned 28 passed, focused frontend source-contract tests returned 95 passed, full frontend tests returned 1209 passed, i18n check passed, frontend build passed, Runtime S2 focused tests returned 46 passed, Runtime `make test-postgres` returned 5 passed, and `git diff --check` passed in both repos. Browser BC-04/05 passed against the loopback fixture on `http://127.0.0.1:18174` using system Google Chrome via Playwright, with raw evidence captured under ignored local `evidence/browser/mpa-s2-1789491400/`. `uv run --extra dev pyright ...` is blocked locally because no `pyright` executable or project script is installed. -S4-05 local verification: frontend source-contract tests covering `runSSE`, `continueTurnSSE`, and Composer lifecycle routing returned 37 passed; `npm --prefix frontend run check:i18n` passed; `npm --prefix frontend run build` passed and generated `veadk/webui` assets were cleaned. -S4-06 local verification is Runtime-side: focused auth/secret/tool tests returned 46 passed, broader S4 Runtime regression returned 206 passed, and focused Runtime Ruff passed. Studio does not add a new product surface for this slice; it reuses the existing Runtime error handling for denied run/resume/continue. -S4-07 local verification: the loopback `turn_lifecycle` scenario covers Session/task control status, pause/resume, `new_turn_required`, explicit Runtime `/continue`, continuation SSE, call counts, and state evidence. Focused frontend source-contract tests returned 37 passed; full frontend tests returned 1215 passed; i18n and build passed. Real-browser `BC-09` passed with system Google Chrome via Playwright, with raw evidence captured under ignored local `evidence/browser/mpa-s4-1789532050/BC-09/`. -S5-02 BFF verification: focused MPA BFF route and main-app Runtime binding tests returned 32 passed; focused Ruff passed. The implemented endpoint is backend-only in this slice; S5-03 owns the frontend detail page. -S5-03 frontend verification: focused `AgentWorkspace` source-contract tests returned 33 passed, full frontend tests returned 1216 passed, i18n check passed, and frontend build passed with generated `veadk/webui` assets cleaned. Focused MPA BFF/main-app tests remained 32 passed. -S5-05 frontend verification: focused `AgentWorkspace` source-contract tests returned 34 passed, full frontend tests returned 1216 passed, i18n check passed, and frontend build passed with generated `veadk/webui` assets cleaned. The Diagnostics section consumes Runtime Console run/trace data through the existing Runtime proxy. -S5-06 local verification: `uv run --extra dev pytest -q tests/integrations/test_mpa_compatibility_manifest.py tests/cli/test_studio_rbac.py::test_mpa_update_rejects_incompatible_manifest_before_launch tests/cli/test_studio_rbac.py::test_mpa_update_allows_compatible_manifest_to_reach_launch tests/cli/test_github_cicd.py::test_mpa_github_delivery_cicd_rejects_incompatible_manifest_before_push tests/cli/test_github_cicd.py::test_mpa_github_delivery_init_rejects_incompatible_manifest_before_push tests/cli/test_github_cicd.py::test_mpa_github_delivery_attach_rejects_incompatible_manifest_before_push tests/cli/test_github_cicd.py::test_mpa_github_runtime_sync_rejects_incompatible_manifest_before_push tests/cli/test_github_cicd.py::test_mpa_github_delivery_rollback_rejects_incompatible_manifest_before_pr tests/cli/test_github_cicd.py::test_mpa_github_delivery_rollback_allows_compatible_manifest` returned 12 passed; focused frontend source-contract tests returned 41 passed; full frontend tests returned 1217 passed; focused Ruff and `compileall` passed; i18n and build passed with existing Vite chunk warnings. -S5-07 local verification: `uv run --extra dev pytest -q tests/frontend/server/mpa/test_runtime_profile_client.py tests/frontend/server/mpa/test_runtime_profile_routes.py tests/cli/test_studio_rbac.py::test_runtime_detail_proxy_and_delete_enforce_role_and_owner tests/cli/test_studio_rbac.py::test_mpa_runtime_delete_blocks_active_sessions_before_runtime_delete tests/cli/test_studio_rbac.py::test_mpa_runtime_delete_cleans_idle_sessions_then_runtime tests/cli/test_studio_rbac.py::test_delete_runtime_ignores_untrusted_mpa_category_for_general_runtime` returned 29 passed; focused frontend source-contract tests returned 33 passed; full frontend tests returned 1217 passed; focused Ruff and `compileall` passed; i18n and build passed with existing Vite chunk warnings. Generated `veadk/webui` assets were cleaned after build. -S5-11 live verification: `VEADK_MPA_P0_LIVE=1 VEADK_MPA_P0_ASYNC_STAGE_TIMEOUT_SECONDS=900 ./scripts/verify-mpa-p0-e2e.sh --manifest --case VC-21 --execute` returned `{"case":"VC-21","residue":[],"status":"passed"}`. The run created an isolated Runtime, Tool, and PostgreSQL database; applied the Studio Profile; passed execution-ready smoke through the real Worker `ready -> create_session -> start_turn -> get_turn -> follow=false replay` protocol; verified no-op same-image update, controlled failed update recovery, compatible rollback smoke, active-operation and active-session delete blockers, idle-session cleanup, final Runtime delete, and zero residue. Focused CLI/route/driver regression tests returned 49 passed and focused Ruff passed. -CON-11a regression verification: the focused conversation/reconciliation suite returned 43 passed; full frontend tests returned 1236 passed; TypeScript, i18n, production build, and scoped diff checks passed. A strict real-Studio probe observed five ordered `.turn--assistant` nodes after completion plus a 30-second delay and the same five nodes after full-page reload. Existing optional local endpoint 404/503 responses were recorded separately and did not affect transcript persistence. -CON-11b regression verification: full frontend tests returned 1237 passed; TypeScript, i18n, production build, and scoped diff checks passed. A fresh real-Studio `whoami` run observed the parent `sandbox_task` and child command transition from running to completed without reload, no remaining spinner or running activity, and the final answer `root`; full-page reload preserved both terminal activities and the answer. -Scope correction: website/Feishu and other deferred findings in the entry audit above are future references, not current development requirements. - -## MPA details availability (2026-10-09) - -MPA Agent details currently expose only Basic information. Other detail tabs and the footer Profile entry are hidden; restored/external focus resolves to Basic before section effects run. This page does not fetch Agent View or Profile status and cannot submit Profile operations. Runtime details, execution flow, chat/delete actions and their authorization remain. General Agent navigation, backend control-plane APIs, and native chat session Profile handling retain their existing contracts. - -## General deployment isolation (2026-10-12) - -Generic structured/YAML creation defaults to general. Draft presence and quick/traditional UI mode are not MPA intent. Existing targets persist explicit `agentCategory`; legacy `mpaProfileOnly: true` is the only fallback to MPA, and explicit general takes precedence. Only MPA executes or reports Profile/smoke phases, including failure and retry. Local draft filtering follows the same rule. Managed MPA creation retains its dedicated lifecycle. This frontend fix does not migrate existing server Runtime tags. - -## MPA owned-resource deletion (2026-10-12, implemented) - -This change supersedes the Profile prerequisite in the deletion contract: preview/execution do not query Profile; only active operations and active sessions block deletion, and session-query errors remain visible. After session cleanup, MPA deletion removes proven exclusive managed worker templates and Skill Spaces, then Runtime. Proof requires account/region deployment records, matching ownership tags and checks for other deployment/Runtime references. Shared, imported or unproven resources remain. Persist cleanup state for partial-failure retries; other resources and general Runtime deletion remain unchanged. Loading, errors, preview retry and cancellation are visible in the detail confirmation dialog. See the [design](../../prd-spec/features/mpa-owned-resource-deletion/2026-10-12-owned-resource-deletion.md). - -Deletion session listing/cleanup sends Studio’s server-authenticated `owner_id` as `x-user-id`, matching chat/scheduled tasks; browser identity headers are not used. Optional Runtime client `user_id` preserves existing caller compatibility. - -Delete preview has a 25-second deadline and returns HTTP 504 / `mpa_delete_preview_timeout` on expiry. Reference discovery reads at most 4 Runtime details concurrently. Deletion requests may reuse identity verification for unchanged credentials; rotation/account changes require re-verification. Creation retains default per-call verification. - -### CON-30a: MPA deletion deadline and confirmation - -Explicit MPA browser deletion waits up to 180 seconds; general deletion retains 30 seconds. A TimeoutError triggers one authorized, no-store, 30-second read-only confirmation for the same Runtime ID/region. Success requires structured cloud Runtime NotFound plus complete list absence; authorization 404, still-listed runtimes, incomplete pagination and query errors cannot mean success. Unconfirmed results retain a localized error and existing retry UI. No automatic delete replay or change to cleanup order is permitted. See [approved timeout fix](../../prd-spec/bugfixes/mpa-delete-timeout/2026-10-13-confirm-delete-outcome.md). - -Confirmation is restricted to server-authorized MPA delete attempts with the same Studio owner, Runtime, region and private deployment-credential fingerprint. Contexts are bounded to 1024 entries and expire after one hour; restart/rotation/missing context fails closed. - -## Compatibility resource relocation (2026-10-14) - -The MPA matrix and benchmark schema reside in `frontend/server/mpa/contracts/mpa-p0/`. The shared `veadk.cli.mpa_p0_contract` evaluator and required custom `--matrix` CLI option remain stable; only the default resource path and documented examples change. Missing default resources retain the embedded fallback; malformed present resources still fail. Python wheels and Studio staging include both JSON files without importing frontend composition during default lookup. No manifest fields, rules, pre-mutation guards or general-agent behavior change. See [approved relocation design](../../prd-spec/refactors/mpa-contract-location/2026-10-14-studio-owned-resources.md). diff --git a/specs/studio-mpa-control-plane/README.zh.md b/specs/studio-mpa-control-plane/README.zh.md deleted file mode 100644 index 771657048..000000000 --- a/specs/studio-mpa-control-plane/README.zh.md +++ /dev/null @@ -1,157 +0,0 @@ -# Studio MPA 控制面 - -## MPA 详情开放范围(2026-10-09) - -MPA 智能体详情目前只开放基本信息。其他详情 Tab 及底部 Profile 入口隐藏;恢复或外部焦点在页面副作用执行前解析为基本信息。此页面不读取 Agent View、Profile 状态,也不能提交 Profile 操作。保留 Runtime 信息、执行流程、聊天/删除及其授权。普通 Agent 导航、后端控制面 API 和原生聊天会话的 Profile 处理维持原契约。 - -- Component ID:`studio-mpa-control-plane` -- 状态:剩余 P0 修订为 `draft`;基线第一期、第二期部分能力以及 P0 Studio 切片至 S5-12 浏览器验证已实现 -- 修订日期:2026-10-14 -- English:[README.md](README.md) -- PRD:[MPA Studio Turn 控制与资源工作台](../../prd-spec/features/mpa-studio-control-plane/2026-09-13-mpa-studio-control-plane-design.zh.md) - -## 职责 - -负责 Studio 到 mpa-agent 的方舟模型发现、不可变 Turn 快照、完整 Turn 生命周期命令/状态、Session 资源挂载、typed runtime 拓扑、Studio 工具发现和角色保护 Secret 复制契约。mpa-agent 是生命周期权威;Studio 是客户端和展示层。 - -## 契约 - -- `CON-1`:账号模型仅服务端发现,浏览器 Payload 无凭据,经过兼容过滤,带新鲜度缓存并明确失败。 -- `CON-2`:接受 Turn 时冻结模型和已提交资源 Payload;非终态 Turn 不观察后续浏览器侧 Session 编辑。服务端 Session 资源 revision 尚未实现。 -- `CON-3`:生命周期持久化且受 generation 控制;Studio 只暴露幂等 pause/resume;只有主执行器及所有活动 worker 到达安全点才可 `paused`。 -- `CON-4`:暂停、继续与发送共用 Composer 主按钮。五分钟内继续同一 Turn;超时或不可恢复时,只有用户点击继续才在同一 Session 创建增量 Turn。断连与超时本身不会启动工作。 -- `CON-5`:Studio 当前在浏览器 localStorage 中保存 Session 挂载选择,并随下一 Turn 提交稳定的 Skill Space ID 和版本。服务端 revision、冲突检测及失效资源保留仍为后续工作。 -- `CON-6`:拓扑只包含配置/观测节点和 typed edge;缺失可选资源是入口而非活动节点。 -- `CON-7`:Agent Card capability 控制模型、生命周期、挂载和拓扑的向后兼容。 -- `CON-8`:环境列表响应绝不包含 Secret;授权复制 no-store、审计不含值,Studio 不渲染或持久化。 -- `CON-9`:Composer 以一排 Footer 承载附件、Agent 目标、模型、用量/状态和一个状态化主按钮。主按钮根据权威 Turn 状态表示发送、暂停、等待或继续;响应式布局不得重叠。 -- `CON-10`:当前 Turn 和 Session 累计均提供输入/输出/推理/缓存/总量;重放使用单调正增量。 -- `CON-11`:会话投影将 `reasoning` 与 Agent `thought` 保持为不同语义类型。优先使用稳定事件身份;跨通道文本去重仅限同一 task/invocation 和类型内的完全或累计前缀传输重放。 -- `CON-11a`:持久会话历史重放必须为每个由用户消息分隔的 assistant 分段分配在整个 Session transcript 内唯一的身份。只有当 projector 本地序号的前缀包含单调递增的历史分段索引时才可复用该序号;后续轮次不得通过 upsert 覆盖更早的 assistant turn。流式执行完成后,只有当持久历史包含触发本次对账的精确最终 assistant event,且没有更新的本地 assistant turn 已开始时,Studio 才可用持久历史替换实时 transcript。浏览器验收必须在延迟持久化刷新前后及整页重载后,按顺序统计并检查 `.turn--assistant` 节点;仅在整个页面中找到预期文本不构成通过,因为同一文本也可能出现在用户提问或 reasoning 中。 -- `CON-11b`:MPA `sandbox_task` 活动在实时流和持久历史重放中都必须进入 UI 终态。Runtime 持久历史可能包含 sandbox `invocation.completed` 事件,随后再有 `status=completed`、`finalAlreadyEmitted=true` 的 wrapper `functionResponse`;而实时订阅可能在回答增量之后只暴露 wrapper response。Studio 将该 wrapper 作为控制终态:如果同一 invocation 仍有 active projected turn,则把既有父级 `sandbox_task` block 标记为 completed,保留已推流的回答和工具 block,以 wrapper event 作为 Turn 完成栅栏并关闭 streaming;如果持久化 `invocation.completed` 已关闭该 Turn,则忽略同一 wrapper,且不得生成重复工具卡。 -- `CON-12`:工具活动展示工具特定的安全动作和对象;未知工具展示具体名称,不使用通用完成标签。 -- `CON-13`:已脱敏工具输入/结果可通过显式复制/下载完整获取;预览渲染有界、截断有提示,所有详情路径共享同一脱敏规则。 -- `CON-14`:Turn 模型选择支持搜索和键盘访问,并在非终态 Turn 拥有不可变模型快照期间保持禁用。 -- `CON-15`:Runtime 列表暴露 `agentCategory`,并接受 `agentCategory=general|mpa` 在可见分页前执行服务端分类过滤。MPA 的权威且唯一来源是 Runtime 标签 `veadk:agent-type=mpa`;镜像名和 artifact URL 不参与分类。`agentCategory=mpa` 必须先通过 Tag 服务正向标签过滤获得候选 Runtime ID,再按 Runtime ID 补齐详情,避免 MPA 结果稀疏时扫描无关 Runtime 页。Tag 读取使用服务端选择的 provider endpoint:BytePlus 使用 `open.byteplusapi.com`,火山引擎保留生成 SDK 的 endpoint 解析。浏览器输入不能选择或覆盖该 endpoint。 -- `CON-31`:Studio MPA 列表把 `CON-15` 发现范围收窄到同时带 `veadk:agent-type=mpa` 和 `veadk:managed=true` 的 Runtime;`scope=all` 管理员看到全部 Studio 管理的 MPA Runtime,`scope=mine` 还必须匹配可信 `veadk:owner`。创建时写入 `veadk:provisioner=studio-mpa` 用于未来来源审计,但为兼容已有通用 Studio MPA,当前不把它作为强制条件。Tag 服务失败保持 fail-closed,浏览器参数不能关闭强制 managed 过滤,授权继续独立。由于 Tag 服务可能返回全局映射,补齐详情前必须按 Runtime TRN 地域过滤。provider 明确确认 Runtime 不存在时可把它作为过期标签映射跳过;其他 Runtime 详情补齐失败必须保持错误态,不能渲染为空列表。已由 [Studio 创建的 MPA Runtime 过滤](../../prd-spec/features/studio-mpa-source-filter/2026-09-28-studio-mpa-source-filter.zh.md) 于 2026-09-29 实现并完成本地验证;真实浏览器发现仍是独立的本机验收步骤。 - -## 状态与数据 - -- Studio 可见 Turn 状态:`queued`、`running`、`pausing`、`paused`、`resuming`、`interrupted`、`completed`、`failed`。内部可保留终态化状态,但不作为用户操作。 -- Turn 控制保存 generation、幂等、期望/观测状态、参与方确认、安全点、快照 revision 和分类失败。 -- Session 资源当前把浏览器本地增量与 Agent 默认值分开;提交的 Turn Payload 保存解析后的 Skill ID/版本,并在该 Turn 内保持不可变。 -- 当前拓扑合并 Runtime 配置的 Agent/Sandbox 节点和浏览器所选 Session 资源。动态 worker 生命周期节点与服务端会话执行配置版本仍为后续工作。 - -## 失败与安全 - -非法模型/资源在副作用前失败。控制过渡失败绝不显示为已暂停。冲突返回权威 generation/state。Secret 排除在普通 API、DOM、状态、存储、日志、遥测、截图和错误详情之外。剪贴板复制是有意授权披露,必须审计。 - -## 兼容性 - -旧 Agent 保留单一默认模型和既有非 MPA 停止行为,隐藏不支持的暂停/恢复控制,只展示已验证数据。原生 ArkClaw 和非 MPA Studio 契约不变。 - -## 验证 - -契约测试覆盖状态转换、幂等、竞态、模型快照、已提交挂载、拓扑、鉴权、脱敏和 Runtime 分类过滤。浏览器测试覆盖响应式布局、唯一动态主按钮、模型筛选、MPA 分类选择、Skill Space 发现和终态控制清理。真实 Runtime 测试已证明模型目录、不可变请求模型、生命周期状态/控制路径、配置态 Agent 到 Sandbox 拓扑及真实 Skill Space 发现。主 Agent/worker Skill 传递由目标契约测试覆盖。完整的真实 worker pause/resume、五分钟增量续跑、刷新恢复及失效资源执行门禁仍需后续验证。 -会话投影测试还覆盖 status/artifact 重放、合法重复措辞、reasoning/thought 语义区分、嵌套工具 payload、大型脱敏值、已知/未知工具标签,以及可搜索模型的键盘行为。 - -## MPA 信息侧栏(2026-09-18 已实现) - -- CON-16:会话侧栏仅向服务端识别的 MPA Runtime 展示。AGENTS.md 来自当前 Runtime 已认证 `/api/v1/studio/agent-info` 的 `agentsMd`,明确区分空、不支持、无权限和失败状态,不使用通用 instruction 替代。 -- CON-17:绑定技能来自 Agent Card 中已配置的技能空间节点及 Runtime 地域内已授权的分页 SkillSpace API。取消 Session 临时技能挂载,已保存的临时技能选择不再发送给 MPA。刷新和取消不能混淆 Runtime 身份,不能将列表失败视为空成功。 - -设计:[MPA 信息侧栏](../../prd-spec/features/mpa-agent-info-rail/2026-09-18-mpa-agent-info-rail.zh.md)。 - -Runtime 代理为带有 `veadk:agent-type=mpa` 标签的 Runtime 处理 `GET /web/agent-info/{app}`,支持原生 app ID 和 `a2a-default`。新增的 `AgentInfo.agentCategory` 为 `mpa`;通用合成 A2A 响应为 `general`。其他既有响应可以不包含该字段,此时隐藏侧栏。 - -`AgentInfo.mpa` 包含 `agentsMd: string | null`、`agentsMdStatus`、`skillSpacesStatus` 和 `skillSpaces: {id: string, region: string}[]`。状态值为 `ready`、`unsupported`、`forbidden`、`error`。正文读取的网络超时为 10 秒,连接超时为 4 秒;401/403 为无权限,404/405/501 为不支持。Studio 使用服务端解析的 Runtime key,通过专用 `X-MPA-Studio-Key` 调用新只读元信息接口。404/405 时可尝试旧 `/api/v1/agents` JWT 接口,但不转发专用头;此时认证失败表示 Runtime 需要升级。不得将 API key 当作 JWT。非法响应和传输失败为错误。上游详情仅允许透传正文、name、description、model 字符串。空间发现与正文读取失败相互独立。 - -空间 ID 来自 `urn:veadk:mpa:resource-topology:v1` 中通过 `mounts` 边与 `agent` 节点连接且已配置的 `skill-space` 节点。缺失拓扑表示不支持,不能视为空绑定成功。空间列表复用已授权的 `/web/skill-spaces/{id}/skills` 路由,每页请求 100 项;无进展或超过 100 页时标记为不完整错误。各请求保留现有 30 秒客户端超时并支持取消。仅完整列表显示数量;降级结果和部分失败保留可读内容并提示,无权限则清除已有技能。不修改 Runtime 凭据、绑定或部署。绑定技能名称可打开按权限控制的[技能正文编辑器](../studio-skill-document/README.zh.md)。 - -## 托管创建边界 - -Studio MPA 创建和 `veadk mpa provision` 由 [Studio MPA 创建](../studio-mpa-creation/README.zh.md)负责。该路径准备账号资源并通过真实 Runtime 元数据初始化。本文原有职责和旧入口保持不变。 - -会话投影测试还覆盖 status/artifact 重放、合法重复措辞、reasoning/thought 语义区分、嵌套工具 payload、大型脱敏值、已知/未知工具标签、可搜索模型的键盘行为、连续持久 assistant 轮次的唯一身份,以及以最终事件为栅栏的实时/持久 transcript 对账。 - -## 拟议 AgentKit P0 修订 - -由 [P0 功能迁移 PRD](../../prd-spec/features/mpa-p0-productionization/2026-09-15-mpa-p0-productionization-design.zh.md) 管理。上文描述基线行为/要求与历史验证,不证明以下变更已完成。拟议 runtime 协议归 [MPA Runtime 控制](../mpa-runtime-control/README.zh.md),Studio 消费该协议,不另建状态权威。以下修订仅在新 AgentKit 模式实现后替代基线浏览器本地资源权威。 - -- `CON-16`: 本期复用 Studio 现有创建表单和 AgentKit 平台资源,补齐 MPA Runtime 部署、Profile 应用、会话执行配置、会话结果与 Debug;不使用 Managed Agent CRUD/version/Session,不为 MPA 新建模板/专家/审核安装产品。消息渠道绑定与配置已由主干按独立 `mpa-channels` 契约交付,Runtime 主干也已具备 shared APIG、Runtime/数据库和 Skill Space 部署支撑;其 provider 专项验收不计入本 P0 台账。网站集成、资源库和搜索在本 P0 中仍延后;不导入历史 ArkClaw 数据。 -- `CON-17`:管理后端从已验证的 Studio OAuth/gateway bearer 派生身份,并只向 custom-JWT Runtime 转发该已验证 token;不自行签发新 token。CLI 认证到同一 BFF。不信任浏览器 owner/account Header,不向 UI 暴露 runtime 凭据,不用云 service role 代替用户身份。启用前通过 M0 证明 issuer/audience/client、JWKS 轮换、claim 映射与撤权。 -- `CON-18`:以 runtime `CON-3` 的“会话执行配置版本”替代浏览器本地 MPA 执行配置权威。未保存编辑只能作为明确 draft,GET 刷新权威状态。PATCH 使用强 ETag,处理 `428`、`412`,展示当前状态并显式 retry/reapply,不静默覆盖其他客户端。Turn 创建使用已接受 execution-config revision。资源正文仍归 AgentKit/Studio,Runtime 只保存稳定引用与 Turn 执行记录。 -- `CON-19`:UI 按 runtime `CON-4` 展示 allowed action 和参与方 observed state。部分暂停/deadline 失败不是 paused。`new_turn_required` 仅由用户显式继续调用 runtime 幂等 continuation API,不自行提交 fallback prompt。断连和轮询超时不启动执行。 -- `CON-20`:Studio 将已接受 Agent draft 校验并规范化为权威 MPA Profile revision;Runtime 持久化不可变执行 revision。同 revision 同摘要幂等,低 revision 或同 revision 不同摘要冲突。既有 Session 固定创建时的 Profile revision,显式升级成功后从下一 Turn 生效。创建后显示部署中;Runtime、Profile 和 execution-ready smoke 全部通过前不得显示可运行。 -- `CON-20a`:浏览器只调用 VeADK BFF 的 `/web/mpa/agents`。BFF 校验现有 Studio Agent draft 并调用类型化 mpa-agent Profile API。本期不调用 Managed Agent CRUD/version API,也不使用服务端 arkcli 子进程。create/update operation 使用 Studio TOS 的 `forbid_overwrite` + ETag 持久化;两个写入口均要求客户端先持久化 `Idempotency-Key` 并返回 `202 + operationId`。相同 principal/operation/target/key/hash 返回同一 operation;刷新先读取 active-list,再按 owner 授权轮询 operation 直至终态。可重试失败必须使用原规范化请求调用 `/web/mpa/agent-operations/{operationId}/retry`,不得重复 `/web/deploy-agentkit`。 -- `CON-20b`:本地 Studio 开发态可在 `veadk studio --dev` 且未配置 Studio TOS 时使用进程内 MPA operation repository。它必须保持 UI 所需的 operation ID、request-hash 幂等和 ETag compare-and-swap 语义,但不具备持久化能力,也不能作为发布证据。非 dev 部署缺少 Studio TOS 时,Profile 写路由继续返回 `mpa_operation_service_unavailable`。 -- `CON-21`: 本期交互/Debug 执行委托 runtime service,保证授权与隔离。主干渠道管理仅通过独立 Automations 与 Runtime proxy 契约消费;Runtime ADK stream 已支持完整 artifact answer replay。本 P0 不重复建设这些产品面,也不把渠道 provider 测试计为 P0 验收。job scheduling 和资源库全局产物/adapter 继续由独立契约维护。 -- `CON-22`:BFF/CLI 共用公开异步 `veadk.integrations.mpa.MpaControlPlaneClient` 的 schema/auth/error/capability 协商,CLI 不导入 server route。client 只调用 Studio BFF API,不接收 Runtime Secret;统一注入 bearer、保留响应 ETag;写操作要求调用方稳定的 `Idempotency-Key`,CAS 更新要求 `If-Match`;HTTP/timeout 失败统一映射为脱敏的 `MpaControlPlaneError`,包含 `code`、`status_code`、`request_id`、`retryable` 和可选 `current_state`。只有长度受限、机器可读的响应 code 可以写入 `code`;自由文本 detail 统一降级为 `http_` 且不对外暴露。生命周期恢复使用 active-list,并通过按 owner 授权的 `GET /web/mpa/agent-operations/{operationId}` 获取终态结果;retry 复用原请求身份,不生成新 key。区分 normal/loading/empty/error/denied/stale/retry;继续遵循模型锁定、IME/键盘和窄屏规则。Runtime capability 不支持时明确 read-only/unsupported,不做不安全 fallback。 -- `CON-22a`:既有 `veadk mpa create` 保持为向后兼容的基础设施部署命令。Studio 控制面等价能力放在独立的 `veadk mpa control` 命令组,包含 `view`、`create`、`update`、`operation list|get|retry`、`profile status|apply`、`session config-get|config-patch|profile-upgrade` 和 `delete-preview`。命令组从显式参数或受控环境变量接收 Studio BFF 地址和 bearer,绝不输出 bearer;Profile/request/change payload 从 JSON 文件或 stdin 读取。BFF 契约要求幂等键的写操作必须显式传入由调用方持久复用的 key;CAS 命令必须显式传入 ETag 或 Runtime revision。成功结果与控制面错误使用 JSON;本地选项/输入错误使用 Click 的 usage-error 文本。退出码固定为:`0` 成功、Click 的 `2` 表示本地用法/输入错误、`3` 表示 `401/403`、`4` 表示 `409/412/428`、`5` 表示可重试 transport/server 失败、`1` 表示其他已校验的控制面失败。对话、Turn 执行和 Runtime 诊断继续使用现有 Studio 执行面/诊断面;该 CLI 不直连 Runtime,也不为不存在的 BFF 契约宣称等价能力。 - -仍要求新平台数据/版本兼容,仅排除历史 ArkClaw 迁移。`CON-16` 至 `CON-22` 映射 PRD `AC-1`、`AC-2`、`AC-4`、`AC-5`、`AC-7`、`AC-8`、`AC-10`,覆盖 unit/BFF、真实浏览器和隔离云 E2E。状态:S1 创建/进度、S2 execution-config BFF/UI、S3 run/cursor 刷新恢复、S4-05 Studio continuation 接线、S4-06 Runtime 授权/副作用前复核,以及 S4-07 浏览器生命周期门禁已实现并完成本地验证;S2 浏览器并发已通过 loopback scenario fixture 的 BC-04/05,S3 刷新恢复已通过 BC-06,S4 pause/resume/continue 已通过 BC-09。真实云和完整 Debug/Trace 收口仍待完成。前后端与构建门禁仍遵循 [frontend/SPEC.md](../../frontend/SPEC.md)。 - -2026-09-15:新增 P0 功能迁移草案。 - -2026-09-15:已实现 Studio S2 BFF/client/UI 子集:类型化 `/web/mpa/sessions/{sessionId}/execution-config` GET/PATCH、类型化 `/web/mpa/sessions/{sessionId}/profile-upgrade`、profile-status 查询、ETag/`Idempotency-Key` 转发、`412 currentState` 透传、MPA Runtime 分类透传、MPA tab 创建入口,以及只在当前 Runtime 与当前业务 Session 下可用的 MPA-only Session configuration 面板。 - -## 页面入口与 adapter 契约 - -PRD 第 6 节记录实际 `frontend/src/App.tsx` 与 `frontend/src/ui/MyAgents.tsx` 入口审计。当前事实:MPA 列表与创建入口已按 P0 路径接线;S4-05 已把原先的 prompt-based Session continuation fallback 替换为 Runtime-owned `/continue` + `/sse` 流程。网站与飞书仅是后续范围证据。 - -- `CON-23`:导航、选择器、创建、草稿、详情、历史保留 category/Runtime/region/agent/业务 Session/认证 scope。缓存用完整 scope key,身份/目标切换时失效。Runtime tag 决定分类,capability 协商决定可用操作;全局 feature flag 和隐藏按钮不能授权请求。MPA tab 与空态必须可创建,并显式走 MPA provision。 -- `CON-23a`:MPA 创建 intent 是 `{category: "mpa", region, source}`,由 Tab/Header/空态共用;不得从全局 state 猜测 category。MPA 详情使用独立 view-model,复用页面外壳/loading/error 组件,但不复用 GitHub delivery versions、通用 Runtime draft/update、评测或优化业务逻辑。 -- `CON-24`:现有 `StudioWorkspace.environmentIds` 表示环境集合,其 ID 不等于授权 `workspaceId` 或 worker mount 身份。服务端解析并授权此关系,不直接替换 ID 或由 UI 派生租户。Resource/default/Session 编辑走 runtime revision 契约,不修改活动 Turn。 -- `CON-25`:`GET /web/mpa/agents/{mpaInstanceId}/view` 返回 `MpaAgentView`,合并 scope、MPA instance/current Profile revision、唯一 Runtime binding/version/readiness、活动 operation/stage、capabilities 和 safe error。0 个 Runtime 为 `runtime_missing`;多个为 `binding_ambiguous` 并禁止写。`orphan_runtime` 表示存在一个已授权 Runtime 但尚未应用 Profile:`capabilities.canWrite` 保持为 true,使 Studio 可以执行首次 Profile apply;`canDebug` 保持为 false,Session 配置在 binding 变为 `bound` 前继续阻塞。若 Runtime data plane 仍要求旧 `X-Jwt-Token`,则返回 `runtime_legacy_auth_unsupported`:Studio 继续展示 Runtime binding,但 `canWrite=false`、`canDebug=false`,且 Session 配置在 Runtime 镜像更新前不可用。即使 Runtime 已 bound,只要 lifecycle operation 仍为 `active` 或 `failed_retryable`,`canDebug` 也必须保持 false;只有恢复到安全终态后才能开放 chat/debug。MPA 编辑使用 Studio Profile,Runtime 保存其不可变 revision;Debug 使用隔离 Runtime Session。不复用通用 Python 工程、个人 Sandbox、评测或优化业务逻辑;GitHub delivery 只有经过显式 MPA compatibility preflight 才可复用。S5-02/S5-03/S5-05/S5-08 切片已实现:BFF 探测已配置 Runtime regions,只接受当前用户可见且带 `veadk:agent-type=mpa` 的 binding,返回 0/1/N binding 状态,并在唯一 binding 时通过 Runtime `profile-status` 补齐 Profile 状态;前端消费该 view 并展示 Runtime Console diagnostics。 -- `CON-25a`:AgentKit Studio 是 MPA 的统一管控入口。MPA 详情页提供 Agent 级 `Profile 配置` 分区,展示当前 apply 状态、revision/ETag、模型、系统提示词,以及 Tool/Skill/MCP 摘要。该分区通过类型化 MPA BFF operation 路径应用当前 Studio Profile;MPA Agent 不再探测依赖 `/list-apps` 的通用 Runtime update capability。首次 apply 使用 create 语义和 `If-None-Match: *`;后续 apply 使用 Runtime revision/ETag。浏览器幂等键由 Runtime ID、operation kind 与规范化 Profile 的 SHA-256 确定,因此相同 Profile 重试会重放同一 operation,内容变化会得到新的 key。 -- `CON-29`:MPA Runtime 写路径必须在任何 AgentKit SDK 或 GitHub mutation 前执行共享 P0 compatibility preflight。覆盖 `/web/deploy-agentkit` 直连 Runtime update、`/web/github-delivery/cicd-pipeline` GitHub Actions delivery setup、`/web/github-delivery/init-main` 首次 delivery 分支初始化、`/web/github-cicd/runtime-sync` 已绑定源码同步,以及 `/web/github-delivery/rollback-pr` rollback PR 创建。浏览器用 `agentCategory=mpa` 标记 MPA 流程;显式 `mpaCompatibilityManifest` 或兼容旧字段 `compatibilityManifest` 可覆盖本地生成的协议形态。失败返回安全的 `409` detail,包含 `code=mpa_compatibility_preflight_failed`、`status` 和 `errorCode`;不得回显 manifest 正文或 secret-like 字段。没有 pinned manifest 的本地 UI 路径只做协议形态校验,不作为发布证据;最终 release gate 必须用真实固定的 revision 和 image digest 执行 VC-19。 -- `CON-30`:MPA 删除预览检查已授权绑定、活动生命周期操作和 Runtime 可见会话,不查询 Profile。活动操作或状态为 `queued|running|pausing|paused|resuming` 的会话在副作用前阻止删除;失败操作本身不阻止删除。会话/资源发现错误必须显示,不得当作空结果。服务端重新检查 Runtime 标签、归属和引用。按可见空闲会话、独占受管沙箱模板、Skill Space、AgentKit Runtime 的顺序清理。共享/导入/归属不明资源、数据库、Workspace、APIG 和网络保留。账号/地域注册库锁和持久清理状态支持部分失败重试,并阻止创建流程重建已删除资源。会话/资源不存在仅在结构化 not-found 错误下视为幂等成功。确认弹窗展示清理 ID、保留原因、加载、错误和重新检查;取消终止预览并忽略晚到结果。会话统计仅代表已认证 Runtime API 的可见会话。 -- `CON-26`: 仅后续:资源库、全局搜索、产物索引/来源 adapter。本期不开发、不验收。会话资源引用和会话结果仍在本期;MPA 模板/专家/审核不属于本方案。 -- `CON-27`: 已有 MPA Runtime 的消息渠道绑定已在主干实现飞书、钉钉和企微,并由 `specs/mpa-channels` 管理,不再属于“代码未开发”的后续项。定时任务产品验收与网站 visitor/A2A 集成仍是独立后续。本 P0 不新增重复渠道 adapter、CLI 等价能力或 provider 专项 live 门禁。 -- `CON-28`:应用/开发者/全局管理页保留非 MPA 语义。限制不兼容 MPA target,不隐藏所有通用功能。MPA runtime admin 不等于平台用户管理 admin。Trace/log/usage/feedback、CLI 示例保持 runtime/worker/Session/Turn 关联与脱敏,不将不支持的 coding/GitHub 自动化改名成 MPA。 - -实现说明:`CON-23`、`CON-23a`/`CON-24` 的 S2 子集、S4-05 continuation client path、S4-06 Runtime authorization recheck path、S4-07 生命周期浏览器 fixture、S5-02/S5-03/S5-08 的 `CON-25`/`CON-25a` view-model 与 Profile 管理路径、`CON-20b` 的开发态本地 store、S5-05 diagnostics 消费路径、S5-06 `CON-29` compatibility preflight 和 S5-07 `CON-30` 删除预览/分阶段清理已实现,覆盖 MPA category 传递、MPA 创建入口、当前 Runtime/当前 Session 作用域、execution-config BFF/UI 调用、浏览器双客户端 CAS 处理、Runtime-owned continuation streaming、本地化检查点续跑状态展示、把 Runtime `403 runtime_action_forbidden` 作为控制面拒绝信号消费、由 BFF 返回 `runtime_missing|bound|binding_ambiguous|orphan_runtime|runtime_legacy_auth_unsupported` 的 MPA 详情状态、允许唯一可写 orphan Runtime 执行首次 Profile apply,同时继续阻止 Session 配置/诊断、保留旧 `X-Jwt-Token` Runtime 的可见只读状态并展示本地化说明、在 MPA-only Diagnostics section 中展示 Runtime Console run/trace correlation、在 MPA update/release/rollback 副作用前拒绝不兼容请求,以及在 active operation 或 active session 存在时阻止 MPA Runtime 删除。 - -2026-09-20:`studio-mpa-profile-errors` 已实现本地 Profile 页恢复修复。本地 Studio 未配置 Studio TOS 时,验证必须使用 `--dev` 启动,使 MPA operation recovery 使用进程内 repository。BFF 现在会把旧 Runtime 的 `X-Jwt-Token` 失败和普通 Profile-status 404 归一为稳定 safe error;前端展示本地化说明,同时保持写入和 Debug fail-closed。 - -2026-09-18:`CON-22` 共享 client 提取已实现。公开 Python integration 统一持有跨消费者的 Profile、operation、capability、execution-config、delete-preview、结构化错误、ETag 与幂等契约;Studio Runtime transport 改为导入共享的 Profile/execution-config schema,不再各自定义副本。BFF 在 active-list/retry 之外新增按 owner 授权的 operation 查询,使 CLI 轮询可以恢复终态 operation,无需依赖 FastAPI route module,也不必只扫描 active 状态。本切片只改变 Studio/VeADK 边界,不新增 Runtime endpoint 或持久数据模型。 - -2026-09-18:S5-09 设计评审新增 `CON-22a`,解决历史部署命令的名称冲突,并把 CLI parity 限定为 Studio BFF 已公开的管理面契约。该 CLI 切片不新增 Runtime 执行 endpoint、Managed Agent API 或持久数据模型。 - -2026-09-18:S5-09 已通过独立的 `veadk mpa control` 命令树实现 `CON-22a`。loopback 契约测试覆盖全部命令组、共享 schema 校验、Studio bearer 注入、202 operation、ETag/CAS、timeout 后稳定幂等键重放、旧 Runtime 只读输出、脱敏结构化错误和固定退出码。既有部署命令保持兼容,Runtime/API/数据模型不变。 - -验证:以 P0 PRD `AC-1` 至 `AC-12` 为准,覆盖 Studio AgentDraft 规范化、MPA Profile revision 应用、会话配置 CAS、对话/worker、控制/续跑、历史、Debug/Trace、真实身份/Profile apply 和 PostgreSQL 并发。浏览器覆盖刷新恢复、双客户端 CAS、loading/empty/error/denied/stale/retry;IME/窄屏保留真实浏览器证据。资源库、搜索、定时任务、飞书、网站集成延后。S2 本地验证:BFF pytest 返回 28 passed,前端 source-contract 子集返回 95 passed,完整前端测试返回 1209 passed,i18n 检查通过,前端 build 通过,Runtime S2 focused tests 返回 46 passed,Runtime `make test-postgres` 返回 5 passed,两仓 `git diff --check` 通过。浏览器 BC-04/05 已使用 system Google Chrome via Playwright 访问 `http://127.0.0.1:18174` loopback fixture 验证通过,原始证据已写入被忽略的本地 `evidence/browser/mpa-s2-1789491400/`。`uv run --extra dev pyright ...` 本地为 `blocked`,因为当前环境没有 `pyright` 可执行文件或项目脚本。 -S4-05 本地验证:覆盖 `runSSE`、`continueTurnSSE` 和 Composer lifecycle routing 的 frontend source-contract tests 返回 37 passed;`npm --prefix frontend run check:i18n` 通过;`npm --prefix frontend run build` 通过,生成的 `veadk/webui` 产物已清理。 -S4-06 本地验证是 Runtime 侧:focused auth/secret/tool tests 返回 46 passed,broader S4 Runtime regression 返回 206 passed,Runtime focused Ruff 通过。Studio 本轮不新增产品界面;继续复用现有 Runtime error handling 处理被拒绝的 run/resume/continue。 -S4-07 本地验证:loopback `turn_lifecycle` 场景覆盖 Session/task control status、pause/resume、`new_turn_required`、显式 Runtime `/continue`、continuation SSE、调用计数和状态证据。前端 source-contract 子集返回 37 passed;完整前端测试返回 1215 passed;i18n 和 build 通过。真实浏览器 `BC-09` 使用 system Google Chrome via Playwright 通过,原始证据已写入被忽略的本地 `evidence/browser/mpa-s4-1789532050/BC-09/`。 -S5-02 BFF 验证:focused MPA BFF route 与主 app Runtime binding tests 返回 32 passed;focused Ruff 通过。本切片只实现后端 endpoint;前端详情页由 S5-03 承接。 -S5-03 前端验证:focused `AgentWorkspace` source-contract tests 返回 33 passed,完整 frontend tests 返回 1216 passed,i18n 检查通过,frontend build 通过且生成的 `veadk/webui` 产物已清理。focused MPA BFF/main-app tests 仍为 32 passed。 -S5-05 前端验证:focused `AgentWorkspace` source-contract tests 返回 34 passed,完整 frontend tests 返回 1216 passed,i18n 检查通过,frontend build 通过且生成的 `veadk/webui` 产物已清理。Diagnostics section 通过现有 Runtime proxy 消费 Runtime Console run/trace 数据。 -S5-06 本地验证:`uv run --extra dev pytest -q tests/integrations/test_mpa_compatibility_manifest.py tests/cli/test_studio_rbac.py::test_mpa_update_rejects_incompatible_manifest_before_launch tests/cli/test_studio_rbac.py::test_mpa_update_allows_compatible_manifest_to_reach_launch tests/cli/test_github_cicd.py::test_mpa_github_delivery_cicd_rejects_incompatible_manifest_before_push tests/cli/test_github_cicd.py::test_mpa_github_delivery_init_rejects_incompatible_manifest_before_push tests/cli/test_github_cicd.py::test_mpa_github_delivery_attach_rejects_incompatible_manifest_before_push tests/cli/test_github_cicd.py::test_mpa_github_runtime_sync_rejects_incompatible_manifest_before_push tests/cli/test_github_cicd.py::test_mpa_github_delivery_rollback_rejects_incompatible_manifest_before_pr tests/cli/test_github_cicd.py::test_mpa_github_delivery_rollback_allows_compatible_manifest` 返回 12 passed;focused frontend source-contract tests 返回 41 passed;完整 frontend tests 返回 1217 passed;focused Ruff 与 `compileall` 通过;i18n 和 build 通过,只有既有 Vite chunk warning。 -S5-07 本地验证:`uv run --extra dev pytest -q tests/frontend/server/mpa/test_runtime_profile_client.py tests/frontend/server/mpa/test_runtime_profile_routes.py tests/cli/test_studio_rbac.py::test_runtime_detail_proxy_and_delete_enforce_role_and_owner tests/cli/test_studio_rbac.py::test_mpa_runtime_delete_blocks_active_sessions_before_runtime_delete tests/cli/test_studio_rbac.py::test_mpa_runtime_delete_cleans_idle_sessions_then_runtime tests/cli/test_studio_rbac.py::test_delete_runtime_ignores_untrusted_mpa_category_for_general_runtime` 返回 29 passed;前端 source-contract focused tests 返回 33 passed;完整 frontend tests 返回 1217 passed;focused Ruff 与 `compileall` 通过;i18n 和 build 通过且只有既有 Vite chunk warning。build 生成的 `veadk/webui` 产物已清理。 -S5-11 真实验证:`VEADK_MPA_P0_LIVE=1 VEADK_MPA_P0_ASYNC_STAGE_TIMEOUT_SECONDS=900 ./scripts/verify-mpa-p0-e2e.sh --manifest --case VC-21 --execute` 返回 `{"case":"VC-21","residue":[],"status":"passed"}`。该 run 创建了隔离 Runtime、Tool 与 PostgreSQL database;应用 Studio Profile;通过真实 Worker 的 `ready -> create_session -> start_turn -> get_turn -> follow=false replay` 协议完成 execution-ready smoke;验证同镜像 no-op update、受控失败恢复、兼容 rollback smoke、active operation 与 active session 删除阻断、idle session 清理、最终 Runtime 删除和零残留。修复后的 CLI/route/driver 定向回归返回 49 passed,focused Ruff 通过。 -CON-11a 回归验证:会话投影/对账 focused suite 返回 43 passed;完整前端测试返回 1236 passed;TypeScript、i18n、生产构建与 scoped diff 检查均通过。严格真实 Studio 探针在任务完成并额外等待 30 秒后观察到五个有序 `.turn--assistant` 节点,整页重载后仍为相同五个节点。既有可选本地 endpoint 404/503 已单独记录,不影响 transcript 持久展示。 -CON-11b 回归验证:完整前端测试返回 1237 passed;TypeScript、i18n、生产构建与 scoped diff 检查均通过。新的真实 Studio `whoami` 执行观察到父级 `sandbox_task` 和子命令无需刷新即从 running 变为 completed,页面不再有 spinner 或 running activity,最终回答为 `root`;整页刷新后两条终态活动和回答均保持。 -范围修正:上述入口审计中网站/飞书等后续项仅供未来参考,不是本期开发要求。 - -## 通用部署隔离(2026-10-12) - -通用结构化/YAML 创建默认 general。存在草稿或快捷/传统界面模式不代表 MPA 意图。已有目标保存明确的 `agentCategory`;旧 `mpaProfileOnly: true` 是唯一的 MPA 回退依据,明确 general 优先。仅 MPA 执行或报告 Profile/smoke 阶段,包括失败与重试。本地草稿筛选遵循相同规则。托管 MPA 创建保留独立流程。本次前端修复不迁移已有服务端 Runtime 标签。 - -## MPA 独占资源删除(2026-10-12,已实现) - -本变更替代删除契约中的 Profile 前置条件:预览与执行不查询 Profile,仅活动操作和运行会话阻止删除;会话查询失败必须可见。MPA 删除在既有会话清理后,按顺序清理已证明独占的受管沙箱模板、Skill Space,再删除 Runtime。依据为账号/地域注册记录、匹配的资源标签和其他部署/Runtime 引用检查;共享、导入或归属不明资源保留。清理状态持久保存,部分失败可重试;其他资源与通用 Runtime 删除不变。加载、错误、预览重试及取消在详情页确认弹窗可见。详见[设计](../../prd-spec/features/mpa-owned-resource-deletion/2026-10-12-owned-resource-deletion.zh.md)。 - -删除会话查询和清理使用 Studio 服务端已认证的 `owner_id` 作为 `x-user-id`,与聊天/定时任务保持一致;不使用浏览器传入的身份头。Runtime client 的可选 `user_id` 参数维持旧调用兼容。 - -删除预览限时 25 秒,超时返回 HTTP 504 / `mpa_delete_preview_timeout`;引用发现最多并发读取 4 个 Runtime 详情。删除请求内相同凭据可复用身份核验,轮换/账号变化必须重新检查;创建流程保持默认逐次核验。 - -### CON-30a:MPA 删除超时与确认 - -明确选择的 MPA 浏览器删除最多等待 180 秒;通用删除保持 30 秒。TimeoutError 后,对同一 Runtime ID/地域执行一次已授权、no-store、限时 30 秒的只读确认。仅结构化云端 Runtime NotFound 且完整列表不存在时成功;授权 404、仍在列表、分页不完整和查询失败不能视作成功。无法确认时保留本地化错误及既有重试 UI。不自动重复删除,不改变清理顺序。见[已批准超时修复](../../prd-spec/bugfixes/mpa-delete-timeout/2026-10-13-confirm-delete-outcome.zh.md)。 - -结果确认仅限服务端已授权的 MPA 删除尝试,须匹配 Studio 用户、Runtime、地域及私有部署凭据指纹。上下文最多 1024 条、一小时到期;重启/轮换/上下文缺失均失败关闭。 - -## 兼容性资源位置迁移(2026-10-14) - -MPA 矩阵及性能报告 schema 移至 `frontend/server/mpa/contracts/mpa-p0/`。共享 `veadk.cli.mpa_p0_contract` 评估器和必填自定义 `--matrix` CLI 参数保持不变,仅默认资源路径和文档示例调整。缺默认文件继续使用内置备用规则,存在但格式错误时仍失败。Python wheel 和 Studio 暂存须包含两个 JSON,默认查找不导入 frontend 组合模块。不改变 manifest 字段、规则、写入前检查或通用智能体行为。见[已批准迁移设计](../../prd-spec/refactors/mpa-contract-location/2026-10-14-studio-owned-resources.zh.md)。 diff --git a/specs/studio-mpa-creation/README.md b/specs/studio-mpa-creation/README.md deleted file mode 100644 index 8d88c79f9..000000000 --- a/specs/studio-mpa-creation/README.md +++ /dev/null @@ -1,300 +0,0 @@ -# Studio MPA creation - -[中文版](README.zh.md) - -- Component ID: `studio-mpa-creation` -- Status: active -- Revised: 2026-10-21 -- Design and evidence: [Studio MPA creation](../../prd-spec/features/mpa-agent-oneclick-provision/2026-09-20-studio-mpa-creation.md) -- Owned code: `veadk/integrations/mpa/managed/`, `frontend/server/mpa_creation.py`, `frontend/src/adk/mpaCreation.ts`, `frontend/src/ui/mpa-create/`; CLI and directory integration. -- Tests: `tests/integrations/mpa_managed/`, `frontend/tests/mpaCreation.test.tsx`. -- Dependencies: [legacy provisioning](../mpa-runtime-provisioning/README.md), [Studio MPA control plane](../studio-mpa-control-plane/README.md), existing AgentKit/Volcengine SDK, SQLAlchemy/asyncpg, SQLite, MPA image. -- Operator guide: [Managed MPA creation](../../veadk/integrations/mpa/managed/README.md). - -## Responsibility and boundaries - -### Session output storage — bucket-only IAM TOS mounts - -The approved [bucket-only design](../../prd-spec/features/studio-mpa-tos-iam/2026-10-12-bucket-only.md) -supersedes Studio access-key input. Studio accepts an -optional `tosBucket` beginning with `agentkit-platform-`, selects IAM Role using the actual Worker role, strips old -draft credentials, and uses the existing bucket resource selector. Before other -provisioning writes, verify account/region bucket ownership and add a stable, -exact bucket-specific Global IAM policy binding. Bucket actions are HeadBucket -and ListBucket; object actions are GetObject, PutObject, AbortMultipartUpload, -and DeleteObject, restricted to `sandbox-session/*`. Preserve existing role, -trust, policies, and bindings; reject conflicts and bound retries/cancellation. -Tool payloads omit Credentials and reuse checks include mode/role. CLI access-key -mode remains compatible. No implicit existing-Tool/Session or public-bucket-policy -change, no KMS grants, and no strict tenant isolation from the shared role. -Implementation, automated/browser verification, and cloud E2E are separate. -An empty selection preserves the server profile; the built-in profile has no TOS -mount by default. Deployment credentials require TOS ListBuckets and IAM GetRole, -GetPolicy, CreatePolicy, ListAttachedRolePolicies and AttachRolePolicy. Granting -bucket access does not imply KMS access or override existing IAM denies. -The existing `/web/deployment-resources` API accepts optional `namePrefix` for -TOS lists, applied before search/counts/pagination. Only MPA creation supplies the -platform prefix; general queries remain unchanged. Creation validates the prefix -before cloud calls, including recovered drafts; explicit CLI profiles are not -restricted by this Studio-only selection rule. - -VeADK owns managed YAML parsing, cloud/database orchestration, authorized durable creation tasks and the MPA directory dialog. No external source checkout is required. Shared registry/bootstrap contracts remain interoperable with the MPA image. Legacy `veadk mpa create` remains separate and unchanged. Model execution and channel routing remain Runtime responsibilities. Managed runtime IAM preparation follows the contract below. - -## Contracts - -- **CON-15 — Studio Identity reuse:** A cloud Studio deployment saves its UserPool name, client name, Identity region, and public MPA callback in four `VEADK_STUDIO_MPA_*` function environment values during the second release. Managed profile loading uses this complete server-owned set when the built-in Studio profile omits Identity values, rejects partial sets and explicit mismatches (including `managed.runtime.env`) before cloud writes, and passes the resolved values to the fixed runner. Without this set, standalone CLI/YAML behavior is unchanged. The new Runtime receives `MPA_USER_POOL_NAME`, `MPA_USER_POOL_CLIENT_NAME`, `IDENTITY_CALLBACK_URL`, and `IDENTITY_REGION`; browser requests and task persistence contain none of them. Existing Studio deployments require redeployment, and existing MPA Runtimes are unchanged. See [Identity reuse design](../../prd-spec/features/studio-mpa-identity-reuse/2026-09-23-deploy-identity-reuse.md). - -- **CON-16 — Studio MPA provenance tags:** Both the generic Studio MPA deployment path and managed one-click creation write `veadk:agent-type=mpa`, `veadk:managed=true`, `veadk:provisioner=studio-mpa`, trusted `veadk:owner`, and the owned `veadk:mpa-instance-id`. Missing trusted owner fails before cloud mutation, and the raw owner must hash to the task owner before child startup. The one-click route hashes this owner for local task and native registry ownership while passing the raw trusted owner ephemerally to the fixed child process for the Runtime tag; it is not persisted in task state. The shared provisioning service emits Studio provenance only when that route explicitly supplies the trusted Runtime owner, so `veadk mpa create` remains outside Studio discovery. Reconciliation replaces these owned keys, preserves unrelated non-system tags, and never replays `sys:*` tags. A legacy pending create with a persisted pre-provenance request hash replays the exact old create payload and client token, then applies the current provenance tags through the normal Runtime update before completing. Shared constants live in an internal MPA tag module and do not add a public Python API. Tags are discovery metadata, not authorization. Existing managed one-click Runtimes without `veadk:managed=true` require an explicit supported update/retry or future backfill before Studio lists them. Implemented and locally verified on 2026-09-29 by [Studio-created MPA Runtime filtering](../../prd-spec/features/studio-mpa-source-filter/2026-09-28-studio-mpa-source-filter.md); real-browser discovery remains a separate local acceptance step. - -- **CON-1 — configuration:** Studio uses a code-owned Beijing profile and ignores `VEADK_MPA_CREATE_CONFIG`; its non-account Runtime settings retain the former private defaults; Studio image defaults use the two latest references in CON-9; the deployment account comes from verified STS identity; VPC/subnet/APIG IDs are not pinned. After automatic PG preparation, Studio resolves shared resources from `mpa_admin_workspace/mpa_admin_db` by verified account and region: validate/reuse registered resources, or create and persist missing VPC/subnet and APIG/IM Gateway through the existing locked provisioners. Existing records are never reset to bypass quotas. Explicit CLI adoption remains supported. See the [registry-owned network fix](../../prd-spec/bugfixes/mpa-shared-network-bootstrap/2026-09-28-registry-owned-network.md). Deployment credentials remain server-side requirements; the model key is discovered under CON-16, never a source literal. The child receives a built-in-profile marker and validates the same profile. CLI `--config` still accepts private YAML with `managed.version: 1`, kebab/snake aliases and unknown-key rejection. CLI `from-runtime` and `template-file` remain mutually exclusive; otherwise flat image/model/PG fields supply the template. CLI `database-admin-url-env` and `shared-database-url-env` resolve PostgreSQL URLs server-side; files/templates are limited to 256 KiB. HTTP clients cannot choose paths, commands, accounts or deployment/PG credentials; CON-11 allows a per-creation OpenViking API Key. Configuration checks are local; they do not prove live access. See [built-in profile design](../../prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-builtin-mpa-profile.md). -- **CON-2 — preparation:** Operators supply PostgreSQL instance/registry/login/owner, IAM roles, images, model access and network connectivity. Verify cloud account and database permissions before preparing account VPC/subnet, APIG/IM Gateway, worker, isolated business database, Skill Space and Runtime. Explicit APIG adoption requires a configured matching VPC. Never assume a newly created VPC reaches private PostgreSQL. Release account locks before waiting for application readiness. -- **CON-3 — identity:** Durable identity is verified account + region + stable agent ID. Native ownership markers, hashes, client tokens and session advisory locks govern reuse. Reject unrelated same-name resources and conflicting unfinished configuration. Persist `studio_owner` in the native deployment record; other owners and preexisting records without this owner cannot take over implicitly. CLI uses owner `cli`; Studio uses the hash of its authorized principal. -- **CON-4 — initialization:** Use real Runtime metadata and shared APIG bootstrap, not legacy placeholder endpoint seeding. Require public/private networking, KeyAuth, MPA tags, bound worker/Skill Space and metadata/IM startup. Reference Runtime templates remove source identity, channel credentials, Skill Space and worker identity. Success requires platform Ready and application `/readiness`. -- **CON-5 — authorization:** All four routes invoke Studio agent-management authorization before configuration/state access. Authenticated ordinary users cannot create; local development retains existing `local` principal semantics. Lookup/cancel is owner-scoped. Duplicate owner/request IDs reuse the task; altered inputs return 409. At most four active tasks globally and one per owner. -- **CON-6 — lifecycle:** States are `running`, `cancelling`, `succeeded`, `failed`, `cancelled`. Start/resume enters `running`; explicit cancellation enters `cancelling`, then `cancelled`; timeout/failure enters `failed`. Successful tasks do not rerun. Stages are `queued`, `checking`, `network`, `gateway`, `worker`, `database`, `skills`, `deploying`, `verifying`; stage is the last observed progress, not a second state machine. Dead supervisors are reconciled on task lookup/start. Recovery retains the original request and agent ID. -- **CON-7 — cancellation:** Run a fixed child module using the current VeADK interpreter. Cancel, deadline or server shutdown terminates the child, waits up to 3 seconds, then kills/reaps it before reporting terminal state. Default deadline is 1800 seconds (60–7200). Persistent cloud resources are retained, including unknown in-flight outcomes. Cancellation is not rollback; retry uses registered intent/token. This flow creates no temporary debugging Runtime to delete. -- **CON-8 — data/security:** Built-in Studio uses `/tmp/veadk-studio/mpa-creation.sqlite3` (server override `VEADK_MPA_TASK_DB`) to persist hashed owner, nonsecret input, state/stage, safe results and supervisor PID, with mode 0600. This is an explicitly temporary, instance-local workaround for read-only cloud code directories: restart may lose history and multiple instances must not rely on shared task limits or locks. Standalone callers retain the `.adk/mpa-creation.sqlite3` convention when they construct the service explicitly. Connections close after each operation. No automatic history expiration. Native PostgreSQL tables remain `mpa_account_network`, `mpa_account_apig`, `mpa_agent_deployment`; require direct/session pooling. STS files are reread; Runtime/network/APIG/worker share account-verified credentials. Protocol messages are bounded to 16 KiB and allowlisted. Never forward raw child output, SDK errors, environment dumps, database URLs or Runtime keys. See the [temporary writable-state fix](../../prd-spec/bugfixes/studio-mpa-writable-state/2026-09-23-use-temporary-state.md). -- **CON-9 — UI:** The MPA filter on Volcengine shows a create card with agent-management permission, including empty lists. The dialog displays fixed region, required Runtime name, description and resource plan; agent ID is server-generated; MPA/Worker image inputs are omitted, as on main; the server uses main’s local latest defaults. Three steps cover basics, automatic PostgreSQL preparation and OpenViking; only step three submits. Persist request identity before POST; lock submitted inputs to make lost-response retry safe. Abort polling on unmount, ignore late responses, preserve server work and recover from session storage on reopen. A confirmed failed or cancelled task offers both same-ID retry and an explicit new-agent action. The latter replaces only the browser draft with a fresh request UUID, blank name and defaults; the server derives the agent ID on submission; it does not delete or mutate the old server task or resources. Running and uncertain-submission states cannot start a different identity from this dialog. Refresh the original region after success. Reuse localized BaseUI/Studio components, keyboard/IME behavior and semantic theme tokens. `ModalLayout.footer` is an optional React node: omitted preserves existing actions, `null` omits the footer; existing callers are unchanged. See [terminal-task new-agent fix](../../prd-spec/bugfixes/studio-mpa-creation/2026-09-24-start-another-agent-after-failure.md). - - -Invalid Runtime names use a danger-colored border and error text with an accessible alert; the input offers a localized valid-name example. The shared Button primitive visibly dims native disabled buttons and uses a not-allowed cursor, while retaining its loading treatment. Correcting the name restores forward navigation. - -## HTTP contract - -### Named creation (2026-10-09) - -The [named creation design](../../prd-spec/features/mpa-agent-oneclick-provision/2026-10-09-named-studio-creation.md) implements amendments to CON-1, CON-3, CON-5, CON-6 and CON-9. - -New Studio forms submit `name`, `requestId`, `description` and `region` plus the existing resource fields; they omit `agentId`, `runtimeImage` and `workerImage`. Name is trimmed and must contain 4–64 ASCII letters, digits, underscores or hyphens. The server derives a stable `mi-[0-9a-f]{24}` agent ID from the trusted owner and request UUID, stores it in the task payload, and retains it across retries. Name participates in request equality and the Runtime creation hash, becomes the new Runtime `Name`, and never replaces `MPA_AGENT_ID` or other resource identity keys. Existing registered Runtime names remain unchanged. - -The built-in Studio defaults become `agentkit-platform-2112682748-cn-beijing.cr.volces.com/mpa/mpa_agent:latest` and `agentkit-platform-2112682748-cn-beijing.cr.volces.com/mpa/mpa_codex_worker:latest`. New drafts do not recover hidden old image overrides; submitted drafts/tasks retain their original inputs and image selections. Legacy API requests with explicit `agentId` and optional image fields remain supported, and requests without Name retain ID-based Runtime naming. CLI/YAML overrides remain unchanged. The dialog displays Name and Description without ID/image inputs; invalid names block forward navigation and submission. Existing task authorization, cancellation, concurrency, secret handling and error status semantics remain in force. - -Verification maps to route/task/runner/service/runtime/profile tests and dialog/browser tests in the linked design. Cloud creation remains separately verified; new image defaults do not update running agents. - -CON-9 navigation correction (2026-09-30): step headings are keyboard-accessible -buttons. Earlier steps and Previous remain available after restoring a submitted -draft; submitted fields stay locked and retry keeps the same identity. Forward -navigation validates image references and the PG target. Busy actions disable -navigation. See [navigation fix](../../prd-spec/bugfixes/mpa-create-navigation/2026-09-30-recover-step-navigation.md). - -CON-9 explicit-new-request amendment (2026-09-30, implemented): any submitted draft -offers New Agent, including running/uncertain/unavailable tasks. This explicitly -replaces the browser recovery draft with fresh identity and editable defaults, -without cancelling or modifying the old cloud task. Pending UI actions and config -loads disable it; server admission limits remain unchanged. This supersedes the -terminal-only restriction above. See [design](../../prd-spec/bugfixes/mpa-create-navigation/2026-09-30-explicit-new-request.md). - -| Method and path | Response | -| --- | --- | -| `GET /web/mpa-creation/config?region=...` | 200 `{configured,region,error?}`; success also returns safe `source`, resource names, `checks:["configuration"]`, `requiresLiveChecks:true` | -| `POST /web/mpa-creation/tasks` | 202 task snapshot; starts or resumes | -| `GET /web/mpa-creation/tasks/{id}` | 200 owner-scoped task snapshot | -| `POST /web/mpa-creation/tasks/{id}/cancel` | 200 task snapshot; terminal tasks unchanged | - -POST body (maximum 8192 bytes, unknown fields rejected): - -```json -{"requestId":"11111111-1111-4111-8111-111111111111","name":"customer-service","description":"Customer service","region":"cn-beijing"} -``` - -`requestId` is a UUID; `name` is required for new requests as specified above; legacy `agentId` matches `[a-z0-9][a-z0-9_-]{0,63}`; `description` defaults to empty, maximum 512 characters; region matches `cn-[a-z]+`, maximum 32 characters. A snapshot contains those fields plus `taskId`, `state`, `stage`, `result`, `error`. Successful result has exactly `runtime_id`, `skill_space_id`, `gateway_id`, `agent_id`, `region`, `state:"ready"`; all are bounded identifiers. No endpoint or credentials are returned. Errors include safe codes `creationFailed`, `timeout`, `interrupted`, `cancelled`. HTTP errors: 400 configuration, 413 body size, 422 invalid input, 409 conflict/capacity, 404 unknown/other-owner task; authorization uses existing Studio status codes. Unsupported provider configuration is unavailable. - -## Compatibility, verification and change record - -The approved 2026-09-20 migration owns cloud provisioning code and tests in VeADK. No external deployment command or dependency on a provider checkout is supported. API/image/shared-table compatibility changes require review of this contract. Existing CLI behavior and unrelated SDK/harness contracts remain unchanged. Flat-mode supported fields and retry instructions are defined in the operator guide. - -CON-1–4 map to configuration/service/network/gateway/worker/database/Runtime tests. CON-5–8 map to route/task tests (ownership, duplicates, lost results, dead supervisors, timeout/reaping, redaction). CON-9 maps to dialog tests, existing directory tests and real-browser checks. CON-13 execution-role permissions map to `tests/cli/test_frontend_deploy_iam.py`; live cloud smoke remains required. Run `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py`, frontend test/build/asset gates, changed-file Ruff/Pyright and isolated browser checks. Actual outcomes and remaining baseline/environment limitations are recorded in the design. Simulated tests and a browser with fake APIs do not prove live cloud deployment; cloud smoke is separately authorized. - -Studio configuration failures identify missing server environment credentials or an unsupported region without revealing values. CLI YAML profiles still distinguish absent/unreadable files, invalid YAML and absent/invalid Runtime JSON templates; errors identify the relevant setting without returning private paths, file contents or parser exception details. This preserves CON-1/CON-8 and the existing HTTP shape. - -Worker discovery follows the provider's `NextToken` cursor with `MaxResults=100`; page length does not determine completion. Repeated cursors and more than 1,000 pages fail explicitly. Matching ToolIds are deduplicated across overlapping pages, while distinct same-name resources remain ownership collisions. Failed or incomplete discovery must never be treated as resource absence (CON-2/CON-3). - -CON-3 naming: fresh managed Runtime `Name` equals `MPA_AGENT_ID`. Existing registered Runtime names are preserved. A pending legacy create without a returned Runtime ID retains its hashed name only when the complete legacy payload matches the persisted request hash, preserving ClientToken retry semantics. Other input changes remain conflicts. Runtime IDs and database/worker/Skill Space identities are unchanged. The current UpdateRuntime API does not expose Name, so this does not rename existing cloud instances. - -CON-1 image precedence: optional `managed.runtime.image` explicitly selects a custom MPA image (ArtifactType `image`) over the chosen reference/template/flat source. Omission/null preserves source behavior. Empty, whitespace-containing, placeholder values and unknown runtime keys fail local validation. It can supply the required image in flat mode, without replacing other prerequisites. `managed.worker.image` remains independent. No automatic update of existing agents or relaxation of pending-request hash checks occurs. - -CON-1 explicit infrastructure: `managed.runtime` optionally accepts `role-name`, positive `cpu-milli`/`memory-mb`/`max-concurrency`, nonnegative `min-instance`, positive `max-instance`, `apmplus-enable`, `project-name`, and `env` (string environment values, whole `${ENV_NAME}` references resolved server-side). Explicit values override reference/template/flat settings; omitted fields preserve defaults. Validate min<=max after merging the source. Flat `model-*` and `pg-*` plus `managed.network.vpc-id/subnet-ids` permit reference-independent configuration. Runtime env keys must be uppercase environment identifiers; provisioning-owned agent/runtime/tool/skill IDs, derived database name, runtime endpoint/auth/encryption, database-admin/shared-registry URLs and request/telemetry identities are rejected. CON-3/CON-8 generation, secret redaction and pending request consistency remain authoritative. Worker reference settings are independent of the MPA Runtime source. - -CON-9 creation images: New forms have no image inputs and use the built-in latest defaults. Only legacy requests and already-submitted drafts retain explicit images; submitted fields stay locked. POST accepts optional trimmed image references (max 1024 characters), rejects URLs/credentials/query fragments/invalid SHA256 digests, and never treats them as shell commands. Config exposes only locally available image defaults; reference-only defaults may remain empty. CON-6/CON-8 task storage adds an `images` JSON column with `{}` for legacy rows. First submission snapshots locally resolved effective images; retries reuse that column and reject changes to explicit inputs. Task responses include this nonsecret snapshot, and the fixed runner applies it to a copied profile. Explicit worker images clear a configured existing-worker selection for this new task. Authorization and all other resource settings remain server-owned. - - -CON-10 — worker recovery/diagnostics: transient Worker operations have at most 4 attempts with 1/2/4-second waits, within a default 600-second stage budget and the task deadline. Create retries retain the same payload/ClientToken; recognized not-found is recoverable only for a registered managed worker. Permanent/unknown errors and ownership conflicts fail immediately. Safe validated diagnostic fields are logged and stored in `task_diagnostics` (latest 100 per task, retained across retries); raw exception data is never persisted. Task HTTP fields and error codes are unchanged. See [approved design](../../prd-spec/bugfixes/mpa-worker-retry/2026-09-20-worker-retry.md). - -CON-10 metadata visibility: distinguish absent initialization metadata from explicit conflicts. A managed Worker with persisted ID/token/hash may wait for missing ID/project/ownership tags only in Creating/Pending/Starting/Initializing/Provisioning or absent status, polling every 5 seconds within the existing Worker/task deadline, without an independent observation limit. Ready with missing metadata, present conflicts, terminal/unknown states and workers without managed creation intent fail immediately. Fixed field diagnostics never include values; metadata attempts saturate at 4 to preserve the diagnostic protocol. Worker API error retry limits remain unchanged. See [deadline correction and verification limits](../../prd-spec/bugfixes/mpa-worker-retry/2026-09-30-worker-metadata-deadline.md). - -## Proposed automatic PG granularity (not implemented) - -This historical proposal is superseded for the current flow by **CON-12 / CON-13** below: shared management/business Workspaces, with database-level business isolation and optional automatic preparation. - -The [deployment-account PG design](../../prd-spec/features/mpa-serverless-pg/2026-09-21-deployment-account-pg.md) follows ArkClaw’s per-agent business Workspace granularity. Identity is deployment account + region + stable agent ID; same-agent retry reuses its Workspace, while another agent receives a different one. All PG calls use VeADK deployment credentials, not ArkClaw resource-account delegation. Existing-PG mode and active contracts above remain unchanged. Independent shared-registry bootstrap/resource count and the detailed design require approval before implementation; a business Workspace must never implicitly become the shared management registry. - -## Three-step creation inputs - -**CON-11 — prerequisites:** See [three-step creation design](../../prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-three-step-creation.md) and [OpenViking credential extension](../../prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-openviking-key.md). Authorized config inspection additionally returns nonsecret `pgHost` and `pgPort` from the administrator URL. POST optionally accepts `pgHost`, `pgPort`, `openvikingUrl` and `openvikingResourceId`; the fields are nonsecret and limited to 255, 5, 1024 and 128 characters respectively. PostgreSQL host and port must be provided together and match the configured administrator connection before provisioning. The OpenViking URL must be HTTPS, without embedded credentials, port, query or fragment; all three OpenViking fields (URL, resource ID and API Key) are required together; the resource ID matches `ov-[a-zA-Z0-9_-]+`. Nonempty nonsecret choices are retained with the task. When all three OpenViking fields are blank, the new Runtime has no `OPENVIKING_*` variables even if its source template/reference has them. POST also accepts an optional `openvikingApiKey` of at most 512 characters. The masked key stays in browser memory, enters the authorized POST and child process stdin, is injected only for this creation together with URL, resource ID and `OPENVIKING_USER=default`, and is never returned by task endpoints or stored in session storage/SQLite. A failed retry after browser/server restart requires the key to be entered again. PostgreSQL password remains server-owned. The Studio creation flow injects `OPENVIKING_USER=default` only when OpenViking is enabled. The legacy CLI keeps its own profile settings. The console links are navigation aids, not service endpoints or resource provisioning. The legacy CLI is unaffected; Studio requests with blank OpenViking fields create Runtimes without OpenViking variables. - -## Two PostgreSQL Workspaces - -The config response adds optional `postgresLayout: "split-workspaces"`, `adminWorkspaceName: "mpa_admin_workspace"` and `adminDatabaseName: "mpa_admin_db"`. Workspace IDs and maintenance URLs are not returned. The copy command rejects `pending=true` Runtime deployments until they are reconciled under the original configuration; persisted request hashes are never rewritten by migration. - -**CON-12 — separated registry (manual mode; automatic mode is defined in CON-13):** Optional `managed.postgres` selects the [two-Workspace contract](../../prd-spec/features/mpa-space-scoped-resources/2026-09-23-two-pg-workspaces.md). The manually created management Workspace is named `mpa_admin_workspace`, with shared tables in `mpa_admin_db`. A separate business Workspace retains one `mpa_agent_` database per agent. Distinct configured Workspace IDs and connection targets are validated locally; operators verify their cloud ownership. Existing account/region APIG/network keys and Runtime bootstrap wire format are preserved. The separate management maintenance credential is used only by `veadk mpa init-admin-db`; normal creation uses the existing management database. Optional migration via `--source-url-env` copies only the three registry tables, retains the source, rejects target conflicts/extras, and requires a coordinated writer outage and Runtime URL cutover. Deployment records pin Workspace IDs. No automatic business migration or Workspace deletion occurs. Legacy profiles remain supported. Implementation/evidence status is recorded in the linked design. - -## CON-13: Automatic shared PostgreSQL Workspaces - -`managed.postgres.mode: auto` opts into deployment-STS-backed AIDAP preparation. Configuration inspection is read-only and needs no PG credentials; it returns `postgresMode: "auto"` and empty `pgHost`/`pgPort`. The PG wizard step explains preparation; POST rejects nonempty PG host/port fields. Unsubmitted drafts clear old PG targets; submitted payloads remain unchanged and incompatible retries are blocked. OpenViking configuration remains the third step. The Studio execution role must cover caller identity, all VPC/subnet discovery and creation performed by `NetworkCloud`, all APIG/IM Gateway operations performed by `GatewayCloud`, the nine explicit AIDAP operations performed by `PGCloud`, and AgentKit Worker, Skill Space, and Runtime operations. The default role grants the [audited explicit cloud set](../../prd-spec/bugfixes/studio-mpa-aidap-permissions/2026-09-23-complete-mpa-cloud-runtime-policy.md) plus its existing `agentkit:*` coverage and is refreshed during `studio update`; customer-owned roles remain operator-managed. - -For shared-layout requests (CON-19 defines new per-agent requests), prepare `mpa_admin_workspace/mpa_admin_db` and one `mpa_business_workspace` (business name configurable). Each MPA retains its independent business database. AIDAP Workspace IDs are resolved/validated against account, region, project, name, engine and ownership tags. Optional explicit IDs adopt existing Workspaces. Legacy/manual mode is unchanged. New task stages are `admin_workspace`, `business_workspace`, `admin_database`. - -Store nonsecret intent and resource identity in a private SQLite bootstrap file. The built-in Studio profile temporarily uses `/tmp/veadk-studio/mpa-pg-bootstrap.sqlite3` so cloud creation does not write to its read-only code directory; standalone YAML retains `.adk/mpa-pg-bootstrap.sqlite3` unless configured otherwise. All creation processes on one instance must share this state path and its OS locks. The Studio `/tmp` path is not durable across instance replacement and is not shared by independent hosts; after state loss, inspect cloud resources before retrying an uncertain create. Persist intent before CreateWorkspace and ID before readiness waits. Never blindly retry uncertain creation; discover by scope/tags or adopt by ID. Definite authorization/parameter rejections can retry after correction. Preserve resources on cancellation, timeout and downstream failure; never replace a deleted recorded Workspace. Provider errors are sanitized; credentials are refreshed and account-checked per SDK call, used only server-side, and never returned by config/tasks or written to bootstrap state. - -For a recorded Workspace created by this bootstrap, temporarily missing detail fields, expected ownership tags, or provider not-found responses are readiness states within the existing timeout. Discovered owned metadata enters the same readiness loop. A present conflicting identity or ownership value fails immediately even if other metadata is missing. Explicitly adopted Workspaces retain strict validation. Retrying a healthy or still-creating owned Workspace never issues another CreateWorkspace call. - -The [terminal creation recovery](../../prd-spec/bugfixes/mpa-pg-recovery/2026-10-10-terminal-create-failure.md) applies identically to both providers and admin/business purposes. Only confirmed `CreateFailed`, with complete matching account/region/project/name/engine/ID and no present conflicting tags, permits retiring an automatic binding. Missing tags on a failed resource do not authorize adoption or deletion. Additive SQLite `failed_workspaces(scope,purpose,workspace_id)` records preserve failed cloud IDs; local retirement and intent release are atomic under the existing scope lock. With no live candidate, a unique newly observed failed candidate can resolve an ID-less intent; multiple unretired failed candidates with no live candidate remain ambiguous. Old retired failures never resolve a newer uncertain request, and changed retired status is an error. A newly dispatched create that fails terminates the current task without repurchase; another request may create a replacement. Explicit IDs, operational `Failed`, deleted/unknown states and provider errors never trigger replacement. Failed cloud resources are retained; APIs, credentials, timeout and cancellation contracts are unchanged. - -A configured legacy registry URL blocks automatic provisioning by default. Explicit `managed.postgres.legacy-urls: ignore` opts new creations into fresh admin and business Workspaces; both old PG environment URLs are ignored for this profile. Existing agents and databases are not migrated, modified or deleted. Alternatively, `init-admin-db --source-url-env ...` prepares the new management database and atomically copies the stopped source registry. Existing business data requires an explicit business Workspace ID and unchanged endpoint. Only after coordinated writer/Runtime cutover may operators clear old registry URLs and enable migrated creation. This operation does not move existing business databases or repoint running Runtime environments. See [design and verification](../../prd-spec/features/mpa-space-scoped-resources/2026-09-23-auto-pg-workspaces.md). - -## CON-14: Runtime registry URL compatibility - -Managed creation translates `sslmode` to the asyncpg `ssl` query parameter in the Runtime's `SHARED_APIG_DATABASE_URL`, retaining the configured TLS mode and all other connection fields. Compatible URLs remain unchanged; conflicting or repeated TLS parameters fail without exposing credentials. Initial creation and finalization use this representation. Pending hashes differing only by this conversion may resume; an unknown Runtime ID still requires exact replay of the original create payload and client token before normalized finalization. Other input changes remain rejected. See [fix and verification](../../prd-spec/bugfixes/2026-09-23-mpa-registry-tls-url.md). - -### Managed sandbox template naming (CON-3) - -New named Studio requests use the entered, validated Runtime name unchanged as the sandbox template Name (4–64 ASCII letters, digits, hyphens or underscores). Legacy CLI requests without a name still use the trimmed agent ID with `-` replaced by `_`. Internal agent IDs, database/Skill Space/channel identities, scoped ownership tags and Runtime ToolId bindings are unchanged. Existing worker IDs are authoritative and are never renamed. New pending intents persist `worker_name` in deployment JSON and reject later name changes. Older pending intents without that field replay a normalized-ID or hashed name only when the complete candidate payload matches the persisted worker_hash, preserving ClientToken. Other payload changes and unrelated same-name resources remain errors. See [named Worker design](../../prd-spec/bugfixes/mpa-worker-naming/2026-10-09-runtime-named-worker.md). - -## Managed MCP signing secret - -Every managed MPA Runtime receives `MCP_TOKEN_SECRET`. Explicit nonblank template environment values take precedence over the target Runtime's value, which takes precedence over the agent-owned PostgreSQL `mpa_deployment_settings` entry `mcp_token_secret`. If all are absent, generate 32 random bytes as 64 hex characters and persist before cloud creation. Atomic insert-or-retain keeps uncertain-create retries stable. Reconcile an explicit/current value only after validating the pending request hash, so rejected changed retries cannot corrupt the original request. Database failures stop creation and dispose connections. Reference Runtime templates exclude the source Agent's key; explicit JSON templates may configure one. Finalization retains the key; normal results and the shared nonsecret deployment registry exclude it. No frontend fields or automatic existing-deployment migration are added. See the [bilingual design](../../prd-spec/bugfixes/mpa-runtime-mcp-secret/2026-10-07-persistent-mcp-secret.md). - -## Managed Runtime IAM role - -Built-in Studio sets `managed.iam.mode: auto`; explicit profiles default to `existing`. Auto preparation runs after STS account validation and before workload identity or other resource mutations. Only a fresh default `IDRoleForArkClawShareAgent` configuration is supported. IAM is account-scoped. Get/create role and `VeADKMPARuntimeAccessV1`, validate account/name and unconditional trust in `vefaas` and `apig`, add missing Global bindings for the approved 12 system policies and 14 custom actions, then verify readback. Preserve extra bindings; never overwrite trust or an existing policy. Missing-object codes alone allow creation; races and eventual consistency use bounded 120-second recovery. Cancellation retains resources. Permission/trust/policy/ownership/readback failures halt creation at `iam_role` with allowlisted localized errors; raw provider text and secrets are excluded. Studio execution credentials require GetRole, CreateRole, GetPolicy, CreatePolicy, ListAttachedRolePolicies and AttachRolePolicy. Existing-mode CLI and general agents are unaffected. Full permission list and acceptance: [design](../../prd-spec/features/mpa-runtime-iam/2026-10-09-managed-runtime-role.md). - -## Studio deployment account - -The built-in profile has no fixed expected account. Use nonempty deployment STS identity for account-scoped IAM, PG, VPC, APIG and Runtime ownership. Fresh Runtime `CLAW_SPACE_ID` and `RUNTIME_IAM_ROLE_TRN` derive from that account and selected role; explicit caller environment overrides remain authoritative. Explicit YAML expected-account checks and credential-change checks remain in force. Image registry addresses and Worker reference IDs remain source resources with independent access requirements. See [design](../../prd-spec/bugfixes/mpa-studio-account/2026-10-09-sts-account.md). - -## Network ownership descriptions and recovery - -New VPC/subnet descriptions use `mpa-account-network-v1-`. Accept only exact scoped legacy `mpa-account-network:v1:` or current markers when recovering/validating managed resources, without rewriting existing resources. A legacy description-only intent is corrected with a new ClientToken only if its dispatch flag is explicitly false, no resource ID exists and name discovery finds no resource; all other fields must match. Unknown outcomes never issue another Create without successful discovery. Existing locks and persist-before-dispatch semantics remain authoritative. See [design](../../prd-spec/bugfixes/mpa-network-description/2026-10-09-valid-network-marker.md). - -## Empty network discovery - -As defined by the [implemented correction](../../prd-spec/bugfixes/mpa-network-discovery/2026-10-09-empty-network-results.md): the first DescribeVpcs/DescribeSubnets page with a null/absent collection and integer `total_count=0` is empty. Other non-list collections, missing/noninteger/nonzero counts with null collections and later null pages remain invalid. SDK/provider failures never become empty discovery. Recorded managed VPCs may then prepare their first subnet without replacing the VPC. Pagination, ownership, durable intents and uncertain-outcome duplicate protection are unchanged. - -## Standard shared gateway preparation - -Defined by the [implemented standard-gateway correction](../../prd-spec/bugfixes/mpa-standard-gateway/2026-10-09-standard-shared-gateway.md): new managed APIG uses standard, two 1c2g nodes, small_1 CLB, public/private access and traffic billing. Before a new gateway is created, prepare two same-VPC subnets in distinct APIG-supported zones under the shared lock. Optional gateway_subnet_intents persist each companion request/token/dispatch state; uncertain creates require discovery. Existing gateway adoption/reuse and existing Runtime network payloads are preserved. ExceededQuota explicitly permits another dispatch after correction; old unknown flags still require audited recovery. Deployment credentials additionally require apig:GetGatewayAvailableZones. No frontend fields or Runtime role changes. - -## Independent Worker configuration - -Built-in Studio creates a Worker from its image defaults without a reference Tool or duplicate startup environment. CreateTool.Port stays 8000 and its default Envs contains only generated MPA_AGENT_ID. See the [image-default correction](../../prd-spec/bugfixes/mpa-worker-template/2026-10-09-worker-image-defaults.md). Optional `managed.worker.env` is a string mapping; whole `${ENV_NAME}` values resolve server-side. Uppercase keys are required. Agent/Runtime/Tool/Skill Space bindings, inherited channel/Runtime secrets and control database URLs are reserved. Nonempty env with existing-id is invalid. Explicit env overrides a filtered optional reference environment, then the provisioner generates MPA_AGENT_ID. Explicit missing references remain errors. Environment values stay out of config summaries/repr and deployment records. Existing ownership, deadline, cancellation and request hash/ClientToken protections remain; changed pending inputs are conflicts. See [approved design and verification](../../prd-spec/bugfixes/mpa-worker-template/2026-10-09-independent-worker.md). - -## Runtime subnet ordering and recovery - -SubnetIds equality is based on unique nonempty members, independent of provider ordering. VpcId and EnableSharedInternetAccess remain immutable exact comparisons. Changed membership, duplicates and malformed lists fail validation. Resume preserves requested ordering, or the registered ordering when its members match the active Runtime, so existing request hashes and ClientTokens remain usable. A different shared default never replaces the active per-agent selection. No schema or hash-format migration is required. Design and verification: [subnet-order repair](../../prd-spec/bugfixes/mpa-subnet-order/2026-10-09-subnet-order.md). - - - -Image rollback (2026-10-09): public registry discovery and automatic digest conversion are withdrawn. Config inspection and new tasks use built-in local latest defaults; existing task snapshots remain unchanged. See [rollback design](../../prd-spec/bugfixes/mpa-create-images/2026-10-09-restore-main-images.md). - -### Managed Skill Space descriptions (CON-3/CON-4) - -New named deployments create `Description: Skills for MPA agent ` using the validated, trimmed entered name. Unnamed callers retain the internal-ID description. Space `Name`, `display_name`, ownership tags, IDs and bindings remain derived from internal identity. Existing/configured spaces are reused without description updates. Pending pre-upgrade requests retain their original description only when the complete old-description candidate matches the persisted `skill_space_request`; other pending-input changes remain errors. Unknown outcomes use discovery without blindly repeating CreateSkillSpace. See [description design](../../prd-spec/bugfixes/mpa-skill-space-description/2026-10-09-entered-name-description.md). - -## CON-16: Deployment-account Ark model key - -Defined by [account-key design](../../prd-spec/features/mpa-model-key-discovery/2026-10-10-account-ark-key.md): built-in Studio selects `managed.model-key.mode: ark`, with optional exact ID/name selectors (`VEADK_MPA_ARK_API_KEY_ID` / `VEADK_MPA_ARK_API_KEY_NAME`). Local inspection requires no raw model key or cloud access. Creation uses refreshed, STS-account-checked deployment credentials for ListApiKeys/GetRawApiKey before resource mutations. Read all pages and deduplicate IDs. Exact selectors require one match; otherwise multiple eligible keys select the newest creation instant, with lexicographically greatest string ID for ties. Accept CreateTime (CreatedAt/CreationTime compatibility), positive Unix seconds/milliseconds or timezone-qualified ISO 8601; missing/invalid time on any eligible candidate fails before raw lookup. A sole candidate needs no timestamp. Reject explicit non-Active states and support legacy missing Status. See the [newest-key successor](../../prd-spec/features/mpa-model-key-discovery/2026-10-10-latest-created-key.md). Never use masked values or create keys. The copied profile injects the raw key into Runtime only; provider text and credentials stay out of events, logs and task/bootstrap storage. Calls have bounded timeouts and at most three transient attempts; cancellation prevents subsequent mutations. Existing hash checks reject changed pending payloads. CLI defaults to explicit keys; general Agent auth is unchanged. Existing deployment roles already grant the two Ark actions; customer-owned roles need permission. Model activation/access and key IP rules remain operator prerequisites. - -## CON-17: BytePlus managed creation - -Studio's server-selected provider enables managed MPA creation for BytePlus `ap-southeast-1` as well as the existing Volcengine flow. The browser cannot supply provider, account or cloud endpoints. Provider/region mismatches fail before mutation. Domestic profile values remain unchanged. BytePlus uses its own AK/SK/session token or explicit rotating IAM credential file, with STS account verification via `open.byteplusapi.com` on every credential refresh; it never reads Volcengine environment credentials. Runtime, Skills and Tools SDK clients are constructed inside the provider context in their worker threads. Identity and signed VPC/ECS/APIG/AIDAP/Ark/IAM requests select the provider's endpoints. Unsupported APIs and required IAM policies fail explicitly, without domestic fallback or omitted permissions. - -BytePlus defaults to the existing overseas Studio model/base, propagates provider/region to Runtime and Worker, and uses Runtime's `APIG_TOP_ENDPOINT` override. Optional TOS mounts use the overseas endpoint. New BytePlus tasks default to `agentkit-platform-3001037806-ap-southeast-1.cr.bytepluses.com/mpa/mpa_agent:latest` and `agentkit-platform-3001037806-ap-southeast-1.cr.bytepluses.com/mpa/mpa_codex_worker:latest`; trusted server overrides remain `VEADK_MPA_BYTEPLUS_RUNTIME_IMAGE` / `VEADK_MPA_BYTEPLUS_WORKER_IMAGE`. Domestic defaults and existing task snapshots remain unchanged. See the [BytePlus image correction](../../prd-spec/bugfixes/mpa-byteplus-images/2026-10-14-byteplus-default-images.md). `VEADK_MPA_BYTEPLUS_AIDAP_HOST` may override the AIDAP hostname on the server; invalid URL/path/user-info inputs fail local inspection. `VEADK_MPA_BYTEPLUS_CREDENTIAL_FILE` explicitly selects a rotating IAM file; missing BytePlus environment credentials never implicitly read the domestic mounted file. Zone validation accepts both `ap-southeast-1a` and `ap-southeast-1-a` without rewriting IDs, preserving domestic validation. Browser and task responses contain no credential values. A BytePlus task adds a trusted provider marker to its durable fingerprint and child stdin. Default task and PG bootstrap files have `-byteplus` suffixes, so domestic recovery state is preserved. Shared Workspace/database layout, lock/retry/cancellation/readiness and optional OpenViking semantics remain unchanged. Console links use the selected provider. Image pull, overseas API/policy availability and actual ready/chat behavior require separate live verification; local config readiness is not evidence of cloud resource availability. See [design and verification](../../prd-spec/features/mpa-byteplus/2026-10-10-byteplus-creation.md). - -Managed BytePlus IAM defaults to `open.byteplusapi.com`, retaining the trusted server `IAM_OPENAPI_HOST` override; shared IAM utilities and domestic creation behavior remain unchanged. - -Managed BytePlus AIDAP defaults to `open.byteplusapi.com` and retains the validated `VEADK_MPA_BYTEPLUS_AIDAP_HOST` override. API connectivity is not proof of Workspace creation success; failed states and ownership checks remain enforced, with no automatic deletion or adoption of failed same-name resources. - -BytePlus managed Runtime inherits the enabled application and control-plane APM defaults: `ENABLE_APMPLUS=true`, `FORCE_APMPLUS_EXPORTER_REGISTRATION=true`, and `ApmplusEnable=true`, matching Volcengine. Existing Runtime and submitted task snapshots are unchanged. See the [BytePlus APM restoration](../../prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-restore-application-apm.md). - -## CON-18: Automatic OpenViking - -New built-in Beijing Studio requests explicitly select `openvikingMode: auto`; old requests without it, manual CLI profiles and BytePlus retain existing behavior. Third-step automatic preparation replaces manual OV inputs for these new requests. Provision one enterprise library with default VLM/Embedding identities, explicit Credentials Source=volcengine and versioned EndpointID values; add the already resolved Ark key only to a copied create payload. Never persist the model key in the intent; wait INIT to READY, obtain default user API Key server-side and inject OPENVIKING_URL/RESOURCE_ID/API_KEY/USER before Runtime deployment. Record scoped nonsecret intent/ID in the authorized deployment registry, reuse on retry and validate ownership/configuration. Missing registered resources, FAILED status or ambiguous create outcome fail explicitly; no blind recreation or Ark endpoint fallback. Keys are never stored in registry/tasks/results. Cancellation and Agent deletion retain OV. Default-model creation capability remains unverified until isolated live acceptance. General agents are unaffected. [Design](../../prd-spec/features/mpa-openviking-auto/2026-10-13-default-model-creation.md). - -CON-18 source correction: permit a matching previous no-Credentials intent to upgrade only with no OV ID, no pending Runtime operation and an explicitly false create-dispatch flag after proven rejection. Unknown/in-flight/changed or registered intents remain conflicts. Get readback must match Source/EndpointID. No UI fields or model-endpoint creation are added. See [source correction](../../prd-spec/bugfixes/mpa-openviking-source/2026-10-13-model-source.md). - -## CON-19: Independent business Workspace for new MPA requests - -New built-in Studio configurations advertise optional `businessWorkspaceMode: "per-agent"`. New forms explicitly submit/persist it; omission preserves `shared` and submitted drafts are not upgraded. `managed.postgres.business-workspace-mode` defaults to `shared`; explicit `per-agent` requires automatic PG and no configured business Workspace ID. Stable agent-scoped name/scope/tags protect independent business creation and retry. Prepare the shared management DB before reading existing per-agent deployment bindings; registered legacy shared bindings remain shared, conflicts fail closed. The resolved Workspace ID, layout and endpoint are pinned in the management registry; each Runtime receives its own business connection and unchanged shared registry connection. Preserve current uncertainty/cancellation recovery and local-lock limitations. No migration, business table initialization or Workspace deletion occurs; generic agents and runtime business interfaces are unchanged. Both existing provider adapters follow this contract. See [design and verification](../../prd-spec/features/mpa-agent-pg-workspace/2026-10-13-independent-business-workspace.md). - -## CON-20: Name preflight and dedicated aidb requests - -New built-in Studio config/forms explicitly select `businessDatabaseName: aidb` with `businessWorkspaceMode: per-agent`. They use `mpa_agent_` for the Workspace, and `aidb` for the business database. Missing database selection retains legacy hashes and request fingerprints. Existing Workspace/database bindings are never renamed or switched. Initial unmarked aidb adoption requires verified dedicated Workspace ownership, matching database owner, no non-system/non-extension relation data and no previous Runtime/ready binding; missing/mismatched/nonempty databases fail closed. Initialization is transaction-locked and agent-marked before Runtime creation. - -Authorized name checks precede first Next, task acceptance and new-layout cloud preparation. Runtime, worker and target Workspace collisions reject foreign/ambiguous ownership; validated own bindings permit retry. Query failures/timeouts are explicit errors. A private provider/account/region task snapshot guards same-host concurrent names and prevents retry under different credentials; only active tasks reserve names. Cross-host atomic exclusion is not guaranteed by read-only checks. Generic agents, shared control resources and PG deletion policy are unchanged. See [approved design](../../prd-spec/features/mpa-name-preflight/2026-10-13-runtime-name-and-workspace-labels.md). - -New-layout web preflight/submission uses the server-derived owner/request internal identity. A supplied different agent ID is rejected; legacy explicit-ID requests retain their previous contract. - -## CON-21: Per-request maximum instances - -New editable MPA forms submit strict positive integer `maxInstance` in basic information, defaulting to 20 for Volcengine and 10 for BytePlus. Provider-specific help and new-request resets use the same default; explicit saved draft values and submitted/legacy retry inputs remain unchanged. The value is persisted with request identity, forwarded to the runner and applied as Runtime `MaxInstance`. Below-configured-minimum values fail before acceptance. Submitted values are immutable on retry; omitted/null legacy fields preserve existing fingerprints/profile defaults and are not auto-upgraded. Name-conflict errors use the existing danger/soft Alert with unchanged localized text. General-agent creation and existing Runtime settings are unaffected. See [design](../../prd-spec/features/mpa-create-scaling/2026-10-13-max-instance-input.md). - -OV route registration and configuration inspection must preserve lazy Studio startup: the signing/network helper loads on the first actual OV API call, not when importing the adapter. See [upstream-readiness correction](../../prd-spec/bugfixes/mpa-upstream-readiness/2026-10-13-startup-and-lockfile.md). - -CON-18 BytePlus extension: BytePlus `ap-southeast-1` new automatic requests prepare a developer library with top-level and credential Source=volcengine, reusing the resolved MPA inference API Key. Model names remain seed-2-0-lite/skylark-embedding-vision; EndpointID defaults are seed-2-0-lite-260428/skylark-embedding-vision-251215, which BytePlus Ark accepts for the corresponding foundation-model versions. Server OV_VLM_ENDPOINT_ID/OV_EMBEDDING_ENDPOINT_ID override defaults; invalid values fail before cloud mutation. OV_MODEL_API_KEY and Coding Plan are not used. Source/EndpointID are stored as nonsecret intent; changed endpoints or old Coding Plan bindings cause a conflict rather than recreation. Overseas signing, default-key access without UserID, four Runtime variables, retention, unknown-create and secret-exclusion guarantees remain unchanged. Domestic and submitted manual flows stay unchanged. Personal default-user and overseas data-plane support still require separate Runtime adaptation. See [approved successor](../../prd-spec/features/mpa-byteplus-openviking/2026-10-14-inference-endpoints.md) and [model-identity correction](../../prd-spec/bugfixes/mpa-byteplus-model-identities/2026-10-14-byteplus-model-identities.md). - - -CON-10/CON-11 IAM recovery: preserve allowlisted operations/categories and validated optional Request IDs in additive task diagnostics. Retry transient IAM reconciliation at most four times with 1/2/4-second waits under the existing deadline, restarting fixed-name readback after uncertain writes. Permanent/unknown failures are immediate; exhausted transient failures use iamTemporaryFailure. Cancellation prevents further SDK dispatch. Legacy diagnostic fields and existing permission keys remain compatible. See [approved fix](../../prd-spec/bugfixes/mpa-iam-retry/2026-10-14-transient-errors.md). - -CON-10/CON-18 OV diagnostics: structured provider rejections and HTTP/SDK exception bodies retain optional validated errorCode (capitalized ASCII code, at most 128 characters; credential-shaped values excluded) and the existing optional hex/UUID requestId. These identifiers pass through the child diagnostic event into backend logs and additive task_diagnostics.error_code/request_id columns; legacy events/rows and the 100-events/task limit remain compatible. Raw Message, bodies, credentials and exception text are excluded. Public task fields, domestic/BytePlus payloads, error categories and uncertain-create retry protection stay unchanged. Missing/malformed metadata may still leave the original cause unavailable. See [diagnostic fix](../../prd-spec/bugfixes/mpa-openviking-diagnostics/2026-10-14-provider-errors.md). - -CON-17/CON-18 BytePlus default restored: supported ap-southeast-1 new/editable requests advertise automatic personal OV again. Existing submitted auto/manual/no-OV identities remain immutable; testing OV after a no-OV submission requires New Agent. Domestic auto, manual fallback and uncertain-create guards are unchanged. See [restoration](../../prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-json-and-restore-ov.md). - -CON-10 Runtime diagnostics: create_runtime/update_runtime failures retain validated category/errorCode and optional requestId from SDK attributes, structured HTTP/SDK bodies and bounded JSON or bytes-literal exception arguments. Parsing is capped at 65,536 bytes/characters with at most eight cycle-safe causes. Raw messages/arguments/bodies, credentials and ClientToken never enter diagnostics. Same exception, call count, cancellation, category, payload and retry semantics are preserved; absent metadata remains omitted. See [completion](../../prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-json-and-restore-ov.md). - -CON-20 implementation correction: combine matching Runtime list/detail ownership and use canonical Worker managed_by/mpa_agent_key tags plus MPA_AGENT_ID environment. Contract unchanged; see [retry fix](../../prd-spec/bugfixes/mpa-name-retry/2026-10-14-owned-resource-metadata.md). - -CON-20 retry proof: empty cloud owner tags may be filled only by an authenticated accepted request with identical normalized stored inputs, server-derived agent identity and private provider/account/region snapshot verified against STS. Explicit foreign tags still reject. The POST obtains this proof server-side; its existing expectedAccount snapshot carries the same check into child provisioning. No browser retry flag, new public fields, or cloud preflight bypass. - -CON-17 APM key handoff: Studio BytePlus creation obtains `data.app_key` through `GetAppKey` on `apmplus-server.ap-southeast-1.byteplusapi.com` with verified deployment credentials before resource mutations, and injects `OBSERVABILITY_OPENTELEMETRY_APMPLUS_API_KEY` into Runtime Envs. Valid trusted explicit Runtime keys are preserved. Lookup failures stop creation with bounded retries and safe `get_apm_key` diagnostics; the key is never stored in task/bootstrap/deployment records or browser results. Domestic and non-Studio paths remain unchanged. Existing pending-input hash checks are retained; old tasks are not silently upgraded. See the [server-side APM key design](../../prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-server-app-key.md). - -Provider-specific default adjustment: [BytePlus maximum instances](../../prd-spec/bugfixes/mpa-byteplus-scaling/2026-10-15-default-maximum.md). Backend/YAML/CLI profile defaults remain unchanged. - -CON-17 OTLP collector: new BytePlus Studio profiles inject `OBSERVABILITY_OPENTELEMETRY_APMPLUS_ENDPOINT=http://apmplus-ap-southeast-1.bytepluses.com:4317`. Trusted explicit Runtime environment overrides retain precedence. Domestic defaults, generic agents, AppKey retrieval, existing resources and submitted snapshots are unchanged. See [endpoint design](../../prd-spec/bugfixes/mpa-byteplus-apm/2026-10-15-otlp-endpoint.md). - - -### Cloud Studio MPA IAM credentials and OpenViking permissions - -BytePlus `veadk studio deploy` supplies the nonsecret function environment variable -`VEADK_MPA_BYTEPLUS_CREDENTIAL_FILE=/var/run/secrets/iam/credential`. Managed creation -uses the mounted rotating role credentials, rereading the file for each call; missing, -malformed or incomplete credentials fail explicitly. Deployment does not copy the -operator's AK/SK or local credential-file path. Local BytePlus AK/SK and explicit CLI -file selection are unchanged; domestic creation retains its existing IAM fallback. -The shared default Studio custom role policy grants -`vikingdb:CreateOpenVikingCollection`, `vikingdb:GetOpenVikingCollection` and -`vikingdb:AccessOpenVikingApiKey` for both providers, without OV deletion permission. -Redeploy existing Studio installations to apply the environment and update the default -role policy. Custom roles need equivalent authorization separately. Service activation, -quotas, model access and network connectivity remain prerequisites; this change does -not claim live cloud verification. - - -CON-17 TOS resource credentials: `/web/deployment-resources` passes the current server-resolved AK/SK/session token to TOS queries as well as CR. Each request resolves credentials anew; clients are not cached across requests. Without complete explicit credentials, existing SDK discovery remains available. Provider endpoint resolution, filtering and credential-redacted failures are preserved. - -## Studio latest content snapshots (2026-10-21) - -The approved [read-only design](../../prd-spec/bugfixes/studio-latest-image-refresh/2026-10-21-readonly-tag-resolution.md) revises CON-1/3/6/8/9 for Studio-created agents. Inputs keep latest; before Worker/Runtime intents, the existing deployment lock protects anonymous, GET-only resolution of each latest manifest to an already-published same-repository non-latest tag with the identical SHA-256 digest. List all bounded tag pages, prefer an existing digest-named tag, then test remaining tags in descending lexical order. Preserve and validate Docker/OCI manifests and indexes. No cloud credentials, registry management API, write permission, tag publication, image copy, or new pipeline is required. Publishers must retain a matching version tag and must not overwrite it; no match fails explicitly. Fixed tags and standalone CLI are unchanged. - -Native `image_snapshots` retains original `requested` inputs and `selected` `{digest,image}` entries before cloud creation. Retries validate the same original input and saved tag digest without reading latest or listing tags; legacy digest-named snapshots remain supported. Changed/missing saved content fails without reselection. Legacy unfinished intents without selection are not rewritten; existing running MPAs are not upgraded. - -Only the current provider's CR suffix is trusted. Anonymous bearer challenges stay on same-host HTTPS/default port without userinfo or redirects; token scope is pull-only. Disable ambient requests authentication. Responses are at most 1 MiB; discovery is at most 20 pages / 1000 tag entries with a 120-second checked budget and bounded 10/30-second connect/read timeouts. Pagination stays on the same repository tags-list endpoint, allows only n/last parameters, and rejects cycles/unsafe links. Invalid registry responses and sanitized transport failures do not fall back to latest. Private latest repositories requiring credentials are outside this public-discovery contract. Tag retention/immutability is the publisher's responsibility. - -### Failure details (Volcengine and BytePlus) - -Failed MPA tasks expose a localized safe reason with expandable/copyable stage, operation, optional validated errorCode and requestId. No recommendations or raw SDK/SQL/exception text are displayed. Known quota, authorization, timeout and resource-state identifiers get safe explanations; missing details remain explicitly unknown. Successful creation and general-agent views remain unchanged. - -Existing resource adapters and the terminal runner preserve the originating diagnostic through exception wrapping, without adding provider calls or changing creation/retry decisions. Accepted task restarts advance an additive tasks.diagnostic_start_id cursor (legacy default 0); current latestDiagnostic ignores earlier attempts while the bounded diagnostic history remains retained. The UI ignores mismatched-stage and non-failed diagnostics. Existing request IDs, snapshots, error keys, cloud payloads and resource recovery remain unchanged. - -See [failure-details design](../../prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-failure-details.md). - -Additional optional fields `exceptionType` (at most eight allowlisted type labels), `httpStatus` (integer 100–599) and `responseType` (application/json, text/html, text/plain, other) are validated at capture, persistence and rendering. Additive `task_diagnostics.details` defaults to `{}` for legacy rows. IAM preserves this evidence before suppressing causes; Runtime setup labels load_credentials, get_caller_identity and runtime_client independently. This does not alter exception/retry semantics or capture raw stderr. Expanded detail typography matches the 13px reason paragraph. See [diagnostic completeness](../../prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-diagnostic-completeness.md). diff --git a/specs/studio-mpa-creation/README.zh.md b/specs/studio-mpa-creation/README.zh.md deleted file mode 100644 index 401630b66..000000000 --- a/specs/studio-mpa-creation/README.zh.md +++ /dev/null @@ -1,289 +0,0 @@ -# Studio MPA 创建 - -[English](README.md) - -### 会话输出存储 — 仅选桶 IAM TOS 挂载 - -已批准的[仅选桶设计](../../prd-spec/features/studio-mpa-tos-iam/2026-10-12-bucket-only.zh.md) -替代 Studio 的 AK/SK 输入。Studio 接受以 `agentkit-platform-` 开头的可选 `tosBucket`,使用实际 -Worker 角色的 IAM Role,清除旧草稿凭据,复用已有桶资源选择器。在其他准备 -写操作前核验桶账号/地域归属,增量绑定稳定、精确的桶专属 Global IAM 策略。 -桶动作仅 HeadBucket、ListBucket;对象动作仅 GetObject、PutObject、 -AbortMultipartUpload、DeleteObject,限定 `sandbox-session/*`。保留既有角色、 -信任、策略和绑定,冲突失败,重试/取消有界。Tool 不带 Credentials,复用核验 -包含模式/角色;CLI AK/SK 兼容。不得隐式修改已有 Tool/Session 或桶公开策略, -不增加 KMS 授权,共享角色不提供严格租户隔离。实现、自动化/浏览器验证和云端 -E2E 是独立状态。 -空选择保留服务端 profile;内置 profile 默认不挂载 TOS。部署凭据须具备 TOS -ListBuckets 和 IAM GetRole、GetPolicy、CreatePolicy、ListAttachedRolePolicies、 -AttachRolePolicy。桶授权不包含 KMS 授权,也不能覆盖现有 IAM Deny。 -现有 `/web/deployment-resources` 接口接受 TOS 列表可选 `namePrefix`,在搜索、计数 -和分页前过滤。只有 MPA 创建传入平台前缀,通用查询保持原行为。创建在云调用前 -验证前缀,也适用于恢复草稿;显式 CLI profile 不受这项 Studio 选择规则限制。 - -- 组件 ID:`studio-mpa-creation` -- 状态:active -- 修订日期: 2026-10-12 -- 设计及证据:[Studio MPA 创建](../../prd-spec/features/mpa-agent-oneclick-provision/2026-09-20-studio-mpa-creation.zh.md) -- 所属代码:`veadk/integrations/mpa/managed/`、`frontend/server/mpa_creation.py`、`frontend/src/adk/mpaCreation.ts`、`frontend/src/ui/mpa-create/`;CLI 和目录接入。 -- 测试:`tests/integrations/mpa_managed/`、`frontend/tests/mpaCreation.test.tsx`。 -- 依赖:[旧部署](../mpa-runtime-provisioning/README.zh.md)、[Studio MPA 控制面](../studio-mpa-control-plane/README.zh.md)、现有 AgentKit/Volcengine SDK、SQLAlchemy/asyncpg、SQLite、MPA 镜像。 -- 操作指南:[MPA 托管创建](../../veadk/integrations/mpa/managed/README.zh.md)。 - -## 职责与边界 - -VeADK 负责托管 YAML 解析、云服务/数据库编排、有权限约束的持久创建任务和 MPA 目录窗口。无需外部源码仓库。共享注册/初始化协议与 MPA 镜像互通。旧 `veadk mpa create` 独立保留且行为不变。模型执行与渠道路由仍由 Runtime 负责。托管运行角色 IAM 准备遵循下文契约。 - -## 契约 - -- **CON-15 — Studio 身份复用:**云上 Studio 部署在第二次发布中将 UserPool 名称、客户端名称、Identity 地域和公网 MPA 回调保存为四项 `VEADK_STUDIO_MPA_*` 函数环境变量。托管配置加载在 Studio 内置配置未指定身份值时使用这组完整的服务端配置,并在云写入前拒绝不完整配置与显式冲突(包括 `managed.runtime.env`);固定子进程读取相同值。没有这组配置时,独立 CLI/YAML 行为不变。新 Runtime 获得 `MPA_USER_POOL_NAME`、`MPA_USER_POOL_CLIENT_NAME`、`IDENTITY_CALLBACK_URL`、`IDENTITY_REGION`;浏览器请求和任务持久化均不包含这些值。已有云上 Studio 需要重新部署,已有 MPA Runtime 不变。参见[身份复用设计](../../prd-spec/features/studio-mpa-identity-reuse/2026-09-23-deploy-identity-reuse.zh.md)。 - -- **CON-16 — Studio MPA 来源标签:**通用 Studio MPA 部署和托管式一键创建都写入 `veadk:agent-type=mpa`、`veadk:managed=true`、`veadk:provisioner=studio-mpa`、可信 `veadk:owner` 和其持有的 `veadk:mpa-instance-id`;可信 owner 缺失时必须在云变更前失败,且启动子进程前必须验证原始 owner 的哈希与任务 owner 一致。一键创建路由把 owner 哈希后用于本地任务和原生注册表归属,同时仅通过固定子进程把原始可信 owner 临时传递给 Runtime 标签,不把它持久化到任务状态。只有该路由显式提供可信 Runtime owner 时,共享 provisioning 服务才写入 Studio 来源,因此 `veadk mpa create` 不进入 Studio 发现范围。标签对账替换这些受管键、保留无关非系统标签,并且不重放 `sys:*` 标签。对于持久化了旧版无来源标签 request hash 的 pending create,先使用原始 create payload 与 client token 精确重放,再通过正常 Runtime update 写入当前来源标签后完成。共享常量位于内部 MPA 标签模块,不新增公共 Python API。标签是发现 metadata,不是授权凭据。缺少 `veadk:managed=true` 的历史托管式一键 Runtime 必须经过显式受支持的更新/重试或未来回填后,Studio 才会列出。已由 [Studio 创建的 MPA Runtime 过滤](../../prd-spec/features/studio-mpa-source-filter/2026-09-28-studio-mpa-source-filter.zh.md) 于 2026-09-29 实现并完成本地验证;真实浏览器发现仍是独立的本机验收步骤。 - -- **CON-1 — 配置:** Studio 使用代码内置的北京地域配置并忽略 `VEADK_MPA_CREATE_CONFIG`;非账号 Runtime 设置保留原私有默认值;Studio 镜像默认值为 CON-9 中的两个 latest 地址;部署账号来自核验的 STS 身份;不固定 VPC/子网/APIG ID。自动准备 PG 后,Studio 按核验后的账号和地域读取 `mpa_admin_workspace/mpa_admin_db` 中的共享资源:校验并复用已登记资源,或通过现有带锁 provisioner 创建并保存缺失的 VPC/子网及 APIG/IM Gateway。不重置已有记录来绕过配额。CLI 显式接管仍受支持。参见[注册库驱动网络修复](../../prd-spec/bugfixes/mpa-shared-network-bootstrap/2026-09-28-registry-owned-network.zh.md)。部署凭据仍须由服务端提供;模型 Key 按 CON-16 自动发现,不写入源码。子进程收到内置配置标记并校验相同配置。CLI `--config` 继续接受私有 YAML;`managed.version: 1` 支持短横线/下划线别名并拒绝未知字段。CLI 的 `from-runtime` 与 `template-file` 互斥,否则通过平铺镜像/模型/PG 字段构建模板。CLI 的 `database-admin-url-env` 与 `shared-database-url-env` 在服务端解析 PostgreSQL URL;配置/模板文件最多 256 KiB。HTTP 客户端不能选择路径、命令、账号或部署/PG 凭据;CON-11 允许本次创建提供 OpenViking API Key。配置检查仅限本地,不证明真实访问权限。参见[内置配置设计](../../prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-builtin-mpa-profile.zh.md)。 -- **CON-2 — 准备:** 运维提供 PostgreSQL 实例/注册库/登录用户/属主、IAM 角色(内置配置自动准备)、镜像、模型权限和网络连通性。先核验云账号及数据库权限,再准备账号 VPC/子网、APIG/IM Gateway、worker、独立业务库、Skill Space 和 Runtime。显式接管 APIG 要求配置匹配 VPC。不得假设新 VPC 可访问私网 PostgreSQL。等待应用就绪前释放账号锁。 -- **CON-3 — 身份:** 持久身份为已核验账号 + 地域 + 稳定智能体 ID。原生归属标记、哈希、client token 和会话 advisory lock 决定复用。拒绝无关同名资源及未完成配置冲突。在原生部署记录保存 `studio_owner`;其他所属用户或无该所属身份的已有记录不能被隐式接管。CLI 使用 `cli`,Studio 使用授权主体的哈希。 -- **CON-4 — 初始化:** 使用真实 Runtime 元数据及共享 APIG 初始化,不进行旧占位 endpoint 回填。要求公私网、KeyAuth、MPA 标签、绑定 worker/Skill Space 及元数据/IM 启动初始化。参考 Runtime 模板移除来源身份、渠道凭据、Skill Space 和 worker 身份。成功要求平台 Ready 及应用 `/readiness`。 -- **CON-5 — 鉴权:** 四个接口在读取配置/状态前均调用 Studio 智能体管理鉴权。认证普通用户不能创建;本地开发沿用现有 `local` 主体语义。查询/取消按所属用户隔离。同一用户/request ID 复用任务;修改输入返回 409。全局最多四个活动任务,每用户一个。 -- **CON-6 — 生命周期:** 状态为 `running`、`cancelling`、`succeeded`、`failed`、`cancelled`。提交/恢复进入 `running`;显式取消进入 `cancelling`,再到 `cancelled`;超时/失败进入 `failed`。成功任务不重跑。阶段为 `queued`、`checking`、`network`、`gateway`、`worker`、`database`、`skills`、`deploying`、`verifying`;阶段表示最近观察进度,不是另一套状态机。查询/提交时核验已退出的监管进程。恢复保留原请求和智能体 ID。 -- **CON-7 — 取消:** 使用当前 VeADK Python 执行固定子进程模块。取消、截止时间或服务端关闭时终止子进程,最多等待 3 秒,再强制终止/回收,随后报告终态。默认截止时间 1800 秒(60–7200)。保留持久云资源,包括结果未知的进行中请求。取消不是回滚,重试使用登记意图/token。本流程不创建需要删除的临时调试 Runtime。 -- **CON-8 — 数据/安全:**内置 Studio 使用 `/tmp/veadk-studio/mpa-creation.sqlite3`(服务端可用 `VEADK_MPA_TASK_DB` 覆盖)持久化所属用户哈希、不含密钥的输入、状态/阶段、安全结果和监管进程 PID,权限为 0600。这是针对云端代码目录只读问题而明确采用的临时、实例本地方案:重启可能丢失历史,多个实例不能依赖共享任务限制或锁。独立调用方显式构造服务时仍沿用 `.adk/mpa-creation.sqlite3` 约定。每次操作后关闭连接。无自动历史过期。原生 PostgreSQL 表保持 `mpa_account_network`、`mpa_account_apig`、`mpa_agent_deployment`;须使用直连/会话池。每次重读 STS 文件;Runtime/网络/APIG/worker 共用已核验账号的凭据。协议消息最多 16 KiB 并按白名单校验。不转发原始子进程输出、SDK 错误、环境转储、数据库 URL 或 Runtime 密钥。参见[临时可写状态修复](../../prd-spec/bugfixes/studio-mpa-writable-state/2026-09-23-use-temporary-state.zh.md)。 -- **CON-9 — UI:** 火山引擎的 MPA 筛选下,有智能体管理权限时展示创建卡片,含空列表。窗口显示固定地域、必填 Runtime 名称、描述和资源计划,智能体 ID 由服务端生成;与 main 一致,不显示 MPA/Worker 镜像输入框;服务端使用 main 的本地 latest 默认值。三步分别展示基础信息、PostgreSQL 自动准备和 OpenViking;仅第三步提交。POST 前保存请求身份,提交后锁定输入,确保响应丢失后安全重试。卸载时中止轮询并忽略迟到响应,保留服务端工作,重开时从会话存储恢复。明确失败或取消的任务同时提供同 ID 重试和独立的新建操作;后者仅以新请求 UUID、空白名称及默认值替换浏览器草稿,服务端在提交时派生智能体 ID,不删除或修改原服务端任务与资源。运行中和提交结果不明时,弹窗不允许开始另一个身份。成功后刷新原地域。复用本地化 BaseUI/Studio 组件、键盘/输入法行为及语义主题变量。`ModalLayout.footer` 是可选 React 节点:省略保留原操作,`null` 隐藏页脚;原调用者不变。参见[终态任务新建修复](../../prd-spec/bugfixes/studio-mpa-creation/2026-09-24-start-another-agent-after-failure.zh.md)。 - - -Runtime 名称无效时,输入框边框和错误文字使用 danger 状态色,并保留无障碍 alert;输入框提供本地化合法名称示例。共享 Button 基础组件为原生禁用按钮统一置灰并显示禁止光标,保留加载态样式。修改为合法名称后恢复向后导航。 - -## HTTP 契约 - -### 名称创建(2026-10-09) - -[名称创建设计](../../prd-spec/features/mpa-agent-oneclick-provision/2026-10-09-named-studio-creation.zh.md) 已实现 CON-1、CON-3、CON-5、CON-6 和 CON-9 的修订。 - -新的 Studio 表单提交 `name`、`requestId`、`description`、`region` 及既有资源字段,省略 `agentId`、`runtimeImage`、`workerImage`。名称去除首尾空白后须为 4–64 个 ASCII 字母、数字、下划线或连字符。服务端根据可信 owner 和请求 UUID 派生稳定的 `mi-[0-9a-f]{24}` 智能体 ID,随任务请求保存并在重试时复用。名称参与请求相等比较和 Runtime 创建哈希,成为新 Runtime 的 `Name`,不替代 `MPA_AGENT_ID` 或其他资源身份键。已注册 Runtime 名称保持不变。 - -Studio 内置默认镜像改为 `agentkit-platform-2112682748-cn-beijing.cr.volces.com/mpa/mpa_agent:latest` 和 `agentkit-platform-2112682748-cn-beijing.cr.volces.com/mpa/mpa_codex_worker:latest`。新草稿不恢复隐藏的旧镜像覆盖项;已提交草稿/任务保留原输入和镜像选择。继续支持显式 `agentId` 和可选镜像字段的旧 API 请求,没有名称的旧请求保留按 ID 命名 Runtime 的行为。CLI/YAML 覆盖不变。弹窗显示名称和描述,不显示 ID/镜像输入框;无效名称阻止向后导航及提交。既有任务授权、取消、并发、密钥处理和错误状态语义继续有效。 - -验证映射至所链接设计中的路由/任务/runner/service/runtime/profile 测试和弹窗/浏览器检查。真实云端创建单独验证;新的镜像默认值不会更新运行中的智能体。 - -CON-9 导航修复(2026-09-30):步骤标题是支持键盘操作的按钮。 -恢复已提交草稿后,前面的步骤和“上一步”仍可查看;已提交字段保持锁定, -重试保留原请求身份。向前导航校验镜像和 PG 配置,忙碌操作禁用导航。 -参见[导航修复](../../prd-spec/bugfixes/mpa-create-navigation/2026-09-30-recover-step-navigation.zh.md)。 - -CON-9 显式新请求修订(2026-09-30,已实现):任何已提交草稿均提供新建入口, -包括运行中、结果未知或查询不可用的任务。显式以新身份和可编辑默认配置替换 -浏览器恢复草稿,不取消或修改旧云端任务。UI 操作未结束及配置加载时禁用; -服务端提交限制不变。本修订替代上文仅终态可新建的限制。 -参见[设计](../../prd-spec/bugfixes/mpa-create-navigation/2026-09-30-explicit-new-request.zh.md)。 - -| 方法与路径 | 响应 | -| --- | --- | -| `GET /web/mpa-creation/config?region=...` | 200 `{configured,region,error?}`;成功另含安全 `source`、资源名、`checks:["configuration"]`、`requiresLiveChecks:true` | -| `POST /web/mpa-creation/tasks` | 202 任务快照;启动或恢复 | -| `GET /web/mpa-creation/tasks/{id}` | 200 按所属用户隔离的任务快照 | -| `POST /web/mpa-creation/tasks/{id}/cancel` | 200 任务快照;终态不变 | - -POST 请求体(最多 8192 字节,拒绝未知字段): - -```json -{"requestId":"11111111-1111-4111-8111-111111111111","name":"customer-service","description":"Customer service","region":"cn-beijing"} -``` - -`requestId` 为 UUID;新请求必填 `name`,规则见上文;旧请求的 `agentId` 匹配 `[a-z0-9][a-z0-9_-]{0,63}`;`description` 默认空,最多 512 字符;地域匹配 `cn-[a-z]+`,最多 32 字符。快照包含上述字段及 `taskId`、`state`、`stage`、`result`、`error`。成功结果恰好包含 `runtime_id`、`skill_space_id`、`gateway_id`、`agent_id`、`region`、`state:"ready"`,均为有长度限制的标识;不返回 endpoint 或凭据。安全错误码包括 `creationFailed`、`timeout`、`interrupted`、`cancelled`。HTTP 错误:400 配置,413 请求体大小,422 输入无效,409 冲突/容量,404 未知/其他用户任务;鉴权沿用 Studio 状态码。不支持的云厂商配置不可用。 - -## 兼容性、验证及变更记录 - -2026-09-20 批准的迁入由 VeADK 维护云部署代码及测试。不支持外部部署命令或依赖外部代码仓库。API/镜像/共享表兼容性变化须审查本契约。旧 CLI 和无关 SDK/harness 契约不变。平铺模式支持字段及重试说明见操作指南。 - -CON-1–4 对应配置/service/network/gateway/worker/database/Runtime 测试。CON-5–8 对应 route/task 测试(所属关系、重复提交、响应丢失、已退出监管进程、超时/回收、脱敏)。CON-9 对应弹窗测试、已有目录测试和真实浏览器检查。CON-13 执行角色权限对应 `tests/cli/test_frontend_deploy_iam.py`,并仍须真实云端冒烟验证。执行 `uv run --extra dev pytest tests/integrations/mpa_managed tests/cli/test_cli_mpa.py`、前端测试/构建/产物检查、改动文件 Ruff/Pyright 及隔离浏览器检查。实际结果和剩余基线/环境限制记录在设计中。模拟测试及使用模拟接口的浏览器不能证明真实云部署;云冒烟单独授权。 - -Studio 配置错误指出缺少服务端环境凭据或不支持的地域,不泄露实际值。CLI YAML 配置仍区分文件缺失/不可读、YAML 格式错误及 Runtime JSON 模板缺失/格式错误;错误指出相关配置项,不返回私有路径、文件内容或解析器异常细节。保持 CON-1/CON-8 及已有 HTTP 结构不变。 - -Worker 查询按云端返回的 `NextToken` 游标翻页,使用 `MaxResults=100`,不能根据当前页数量判断查询结束。重复游标或超过 1,000 页必须明确失败。重叠页中匹配的 ToolId 应去重,不同 ID 的同名资源仍构成归属冲突。查询失败或不完整时不得视为资源不存在(CON-2/CON-3)。 - -CON-3 命名:新建托管 Runtime 的 `Name` 等于 `MPA_AGENT_ID`。已有登记 Runtime 保留其名称。旧版待完成创建尚未返回 Runtime ID 时,仅在完整旧请求与持久化请求哈希匹配的情况下沿用哈希名称,保持 ClientToken 重试语义。其他输入变化仍构成冲突。Runtime ID 和数据库/worker/技能空间的标识不变。当前 UpdateRuntime API 未提供 Name,因此本调整不会重命名已有云实例。 - -CON-1 镜像优先级:可选 `managed.runtime.image` 显式指定 MPA 自定义镜像(ArtifactType 为 `image`),优先于所选参考 Runtime/模板/平铺来源。省略/null 保持原来源行为。空字符串、含空白字符、占位符的值和未知 runtime 配置项在本地校验失败。平铺模式可用它提供必需的镜像,不替代其他前置配置。`managed.worker.image` 仍独立生效。不会自动更新已有智能体,也不会放宽未完成请求哈希检查。 - -CON-1 显式基础设施:`managed.runtime` 可选字段包括 `role-name`、正数 `cpu-milli`/`memory-mb`/`max-concurrency`、非负 `min-instance`、正数 `max-instance`、`apmplus-enable`、`project-name` 和 `env`(字符串环境值,完整 `${ENV_NAME}` 引用由服务端解析)。显式值覆盖参考 Runtime/模板/平铺设置,省略字段保留默认值。合并来源后验证 min<=max。平铺 `model-*`、`pg-*` 与 `managed.network.vpc-id/subnet-ids` 支持不依赖参考 Runtime 的配置。Runtime env 键须为大写环境变量标识;禁止设置创建流程拥有的智能体/Runtime/工具/技能 ID、派生数据库名、Runtime 端点/鉴权/加密、数据库管理员/共享注册库 URL、请求/遥测标识。CON-3/CON-8 的生成、秘密脱敏和未完成请求一致性仍为准。Worker 参考配置独立于 MPA Runtime 来源。 - -CON-9 创建镜像:新表单没有镜像输入框,使用内置 latest 默认值。仅旧请求和已提交草稿保留显式镜像;提交后锁定输入。POST 接受可选镜像引用(去除首尾空白,最多 1024 字符),拒绝 URL/凭据/查询片段/非法 SHA256 digest,绝不作为 shell 命令执行。配置接口仅返回本地可确定的默认镜像,纯参考配置可为空。CON-6/CON-8 任务存储新增 `images` JSON 列,旧记录为 `{}`。首次提交保存本地可确定的实际镜像,重试沿用该列并拒绝修改显式输入。任务响应包含该非秘密快照,固定 runner 将其应用于配置副本。显式 worker 镜像会在本次新任务中取消配置的已有 worker 选择。权限和其他资源设置仍由服务端管理。 - - -CON-10 — Worker 恢复/诊断:暂时性 Worker 操作最多尝试 4 次,间隔 1/2/4 秒,受默认 600 秒阶段预算和总任务期限限制。创建重试保持同一载荷/ClientToken;仅已登记的托管 Worker 可恢复已识别的不存在错误。永久/未知错误和归属冲突立即失败。已安全验证的诊断字段记录到日志和 `task_diagnostics`(每任务最新 100 条,跨重试保留),绝不持久化原始异常数据。任务 HTTP 字段和错误码不变。见[已批准设计](../../prd-spec/bugfixes/mpa-worker-retry/2026-09-20-worker-retry.zh.md)。 - -CON-10 元数据可见性:区分初始化元数据缺失和显式冲突。具有持久化 ID/令牌/哈希的托管 Worker 仅在 Creating/Pending/Starting/Initializing/Provisioning 或无状态时可等待缺失 ID/项目/归属标签,每 5 秒检查,受现有 Worker/任务期限限制,不设置独立观察次数上限。Ready 缺失字段、已有值冲突、终态/未知状态以及无托管创建意图的 Worker 立即失败。固定字段诊断不包含值;元数据 attempt 最大保持 4,兼容已有诊断协议。Worker API 错误重试限制不变。见[期限修复及验证限制](../../prd-spec/bugfixes/mpa-worker-retry/2026-09-30-worker-metadata-deadline.zh.md)。 - -## 自动 PG 粒度提案(尚未实现) - -当前流程已由下文 **CON-12 / CON-13** 替代该历史提案:共享管理/业务 Workspace,业务保持库级隔离,并可选自动准备。 - -[部署账号 PG 设计](../../prd-spec/features/mpa-serverless-pg/2026-09-21-deployment-account-pg.zh.md) 采用 ArkClaw 每个智能体独立业务 Workspace 的粒度。身份为部署账号 + 地域 + 稳定智能体 ID;同智能体重试复用其 Workspace,另一个智能体使用另一个 Workspace。所有 PG 调用使用 VeADK 部署凭据,不采用 ArkClaw 资源账号委托。已有 PG 模式和上文现行契约保持不变。独立共享注册库的初始化/资源数量和详细设计需要在实施前确认;业务 Workspace 绝不能隐式变成共享管理注册库。 - -## 三步创建输入 - -**CON-11 — 前置资源:**参见[三步创建设计](../../prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-three-step-creation.zh.md)和[OpenViking 密钥扩展](../../prd-spec/features/mpa-agent-oneclick-provision/2026-09-23-studio-mpa-openviking-key.zh.md)。鉴权后的配置检查额外返回管理员连接的非密钥 `pgHost` 和 `pgPort` 默认值。POST 可选接收 `pgHost`、`pgPort`、`openvikingUrl` 和 `openvikingResourceId`,长度上限依次为 255、5、1024、128 字符,均不含密钥。PG 主机和端口必须同时提供,并在部署前与配置的管理员连接目标一致。OpenViking 地址必须为 HTTPS,不能包含嵌入凭据、端口、查询串或片段;OpenViking 地址、资源 ID 和 API Key 三项必须同时填写;资源 ID 匹配 `ov-[a-zA-Z0-9_-]+`。非空的非密钥选项随任务保留。OpenViking 三项全空时,即使模板或参考 Runtime 含有旧值,新 Runtime 也不含 `OPENVIKING_*` 变量。POST 还可选接收最多 512 字符的 `openvikingApiKey`。遮罩后的密钥只存在浏览器内存中,经鉴权 POST 与子进程标准输入传递,仅在本次创建时连同地址、资源 ID 和 `OPENVIKING_USER=default` 一起注入;任务接口不返回,浏览器会话存储及 SQLite 均不保存。浏览器或服务端重启后重试失败任务时需重新输入密钥。PG 密码仍由服务端管理。Studio 创建流程仅在启用 OpenViking 时注入 `OPENVIKING_USER=default`。旧 CLI 保持自己的配置行为。控制台链接仅用于导航,不是服务端点,也不会创建资源。旧 CLI 不受影响;Studio 请求的 OpenViking 字段全空时,新 Runtime 不含相关环境变量。 - -## 两套 PostgreSQL Workspace - -配置响应新增可选 `postgresLayout: "split-workspaces"`、`adminWorkspaceName: "mpa_admin_workspace"` 和 `adminDatabaseName: "mpa_admin_db"`,不返回 Workspace ID 或维护连接。复制命令拒绝 `pending=true` 的 Runtime 部署,必须先在原配置下核对;迁移绝不改写持久化请求哈希。 - -**CON-12 — 注册库分离:** 可选 `managed.postgres` 启用[两 Workspace 契约](../../prd-spec/features/mpa-space-scoped-resources/2026-09-23-two-pg-workspaces.zh.md)。手动预建的管理 Workspace 名称为 `mpa_admin_workspace`,共享表位于 `mpa_admin_db`。另一业务 Workspace 继续为每个智能体保留一个 `mpa_agent_` 数据库。本地校验配置的 Workspace ID 和连接目标不同,云端归属由操作员核验。保留现有按账号/地域共享 APIG/网络的键和 Runtime 启动协议。独立管理维护凭据仅供 `veadk mpa init-admin-db` 使用;正常创建使用已有管理数据库。可选 `--source-url-env` 迁移仅复制三张注册表、保留源库、拒绝目标冲突/多余记录,要求协调停止写入并切换 Runtime URL。部署记录固定 Workspace ID,不自动迁移业务数据或删除 Workspace。旧配置保持兼容。实现及验证状态见关联设计。 自动模式另见 CON-13,本条描述手动模式。 - -## CON-13:自动准备共享 PostgreSQL Workspace - -`managed.postgres.mode: auto` 显式启用基于部署账号 STS 的 AIDAP 准备流程。配置检查只读,无需 PG 凭据;返回 `postgresMode: "auto"` 以及空的 `pgHost`/`pgPort`。创建向导 PG 步骤展示准备说明;POST 拒绝非空 PG 地址/端口。未提交草稿清空旧 PG 目标;已提交请求保持不变,不兼容的重试被拦截。OpenViking 仍为第三步。Studio 执行角色必须覆盖调用方身份、`NetworkCloud` 执行的全部 VPC/子网查询与创建、`GatewayCloud` 执行的全部 APIG/IM Gateway 操作、`PGCloud` 执行的 9 项明确 AIDAP 操作,以及 AgentKit Worker、Skill Space 和 Runtime 操作。默认角色授予[经审计的明确云端权限集合](../../prd-spec/bugfixes/studio-mpa-aidap-permissions/2026-09-23-complete-mpa-cloud-runtime-policy.zh.md)及现有 `agentkit:*` 覆盖,并在 `studio update` 时刷新;客户自有角色仍由运维管理。 - -共享布局请求(新独立布局见 CON-19)按已核验账号/地域/项目准备 `mpa_admin_workspace/mpa_admin_db` 及一个 `mpa_business_workspace`(业务名称可配置)。每个 MPA 保持独立业务库。AIDAP Workspace ID 必须与账号、地域、项目、名称、引擎和归属标签一致。可用显式 ID 接管已有 Workspace。旧配置/手动模式保持兼容。新增任务阶段为 `admin_workspace`、`business_workspace`、`admin_database`。 - -在私有 SQLite 引导文件中保存无密钥的意图与资源身份。内置 Studio 配置临时使用 `/tmp/veadk-studio/mpa-pg-bootstrap.sqlite3`,避免云端创建写入只读代码目录;独立 YAML 未显式配置时仍使用 `.adk/mpa-pg-bootstrap.sqlite3`。同一实例上的所有创建进程必须共享该路径及操作系统锁。Studio 的 `/tmp` 路径在实例替换后不持久,也不被独立主机共享;状态丢失后,必须先检查云资源再重试结果不确定的创建。调用 CreateWorkspace 前保存意图,等待就绪前保存 ID。创建结果不确定时禁止盲目重试,按范围/标签发现或通过 ID 接管。确定的权限/参数拒绝允许修正后重试。取消、超时及后续失败保留资源,不替换已登记但删除的 Workspace。服务商错误脱敏;每次 SDK 请求刷新凭据并核验账号,凭据只在服务端使用,不进入配置/任务响应或引导状态文件。 - -对引导状态已记录且由本流程创建的 Workspace,详情字段、预期归属标签暂时缺失或服务商返回 not-found,均在现有超时范围内视为等待就绪。发现自有资源时,元数据不完整也进入同一就绪循环。已出现但冲突的身份或归属值立即失败,即使其他元数据尚未出现。显式接管的 Workspace 保持严格校验。重试正常或仍在创建中的自有 Workspace 不再次调用 CreateWorkspace。 - -[终态创建失败恢复](../../prd-spec/bugfixes/mpa-pg-recovery/2026-10-10-terminal-create-failure.zh.md) 对两种服务商及管理/业务用途应用相同规则。仅经确认的 `CreateFailed`,且账号/地域/项目/名称/引擎/ID 完整匹配、现有标签没有冲突时,允许将自动绑定退出。失败资源缺失标签不代表允许接管或删除。SQLite 增量表 `failed_workspaces(scope,purpose,workspace_id)` 保存失败云资源 ID;在现有范围锁下,退出记录与解除意图原子执行。没有正常候选时,唯一新发现的失败候选可解释无 ID 意图;没有正常候选且存在多个未退出失败候选时仍视为歧义。旧退出失败不能解释更新请求的未知结果,退出资源状态变化则报错。本次刚发出的创建失败时结束当前任务,不重复购买;另一个请求可创建替代资源。显式 ID、运行状态 `Failed`、删除/未知状态和服务错误绝不触发替换。保留失败云资源,API、凭据、超时和取消规则不变。 - -仍配置旧注册库 URL 时默认禁止自动创建。显式设置 `managed.postgres.legacy-urls: ignore` 后,新建任务使用全新的管理和业务 Workspace,本配置不读取两个旧 PG 环境 URL;已有智能体和数据库不迁移、不修改、不删除。另一种方案是通过 `init-admin-db --source-url-env ...` 准备新管理库,并原子复制已停止写入的源注册库。已有业务数据须指定业务 Workspace ID 且保持端点不变。协调写入方和 Runtime 切换后,管理员才可清除旧注册库 URL 并启用迁移后的创建。本操作不搬迁现有业务库,不修改运行中 Runtime 的环境变量。参见[设计与验证](../../prd-spec/features/mpa-space-scoped-resources/2026-09-23-auto-pg-workspaces.zh.md)。 - -## CON-14:Runtime 注册库 URL 兼容性 - -托管创建将 Runtime 的 `SHARED_APIG_DATABASE_URL` 中 `sslmode` 查询参数转换为 asyncpg 使用的 `ssl`,保留配置的 TLS 模式及所有其他连接字段。已兼容 URL 保持不变;冲突或重复 TLS 参数报错且不暴露凭据。初次创建和最终配置均使用此表示。进行中哈希仅因该转换而不同时允许恢复;Runtime ID 未知时仍须用原创建请求和客户端令牌精确重放,然后进行规范化的最终配置。其他输入变化仍被拒绝。参见[修复与验证](../../prd-spec/bugfixes/2026-09-23-mpa-registry-tls-url.zh.md)。 - -### 托管沙箱模板命名 (CON-3) - -新的 Studio 名称请求直接使用经过校验的手填 Runtime 名称作为沙箱模板 Name(4–64 个 ASCII 字母、数字、连字符或下划线)。未提供名称的旧 CLI 请求仍使用去除首尾空白、将 `-` 替换为 `_` 的智能体 ID。内部智能体 ID、数据库/技能空间/渠道标识、作用域归属标签及 Runtime ToolId 绑定不变。已有 worker ID 为权威绑定,不重命名。新的未完成意图在部署 JSON 中保存 `worker_name`,拒绝后续名称变化。没有该字段的旧意图,仅在完整候选请求与已存 worker_hash 匹配时重放标准化 ID 名称或哈希名称,保留 ClientToken。其他请求变化及无关同名资源仍报错。 参见[名称 Worker 设计](../../prd-spec/bugfixes/mpa-worker-naming/2026-10-09-runtime-named-worker.zh.md)。 - -## 托管 MCP 签名密钥 - -每个托管 MPA Runtime 均配置 `MCP_TOKEN_SECRET`。显式模板环境变量非空值优先于目标 Runtime 值,后者优先于 Agent 专属 PostgreSQL `mpa_deployment_settings` 表中的 `mcp_token_secret`。全部缺失时生成 32 随机字节、编码为 64 位十六进制字符串,并在云创建前持久化。原子插入或保留已有值确保不确定创建结果的重试稳定。仅在校验未完成请求摘要后同步显式值/当前值,避免被拒绝的变更重试破坏原请求。数据库失败终止创建并释放连接。参考 Runtime 模板排除源 Agent 密钥;显式 JSON 模板可配置密钥。回填沿用密钥,正常返回值及共享非敏感部署登记记录不包含密钥。不新增前端字段或自动迁移存量部署。参见[双语设计](../../prd-spec/bugfixes/mpa-runtime-mcp-secret/2026-10-07-persistent-mcp-secret.zh.md)。 - -## 自动准备 Runtime IAM 角色 - -内置 Studio 使用 `managed.iam.mode: auto`,显式配置默认 `existing`。自动准备在 STS 账号核验后、工作负载身份和其他资源变更前执行。仅支持全新来源的默认 `IDRoleForArkClawShareAgent` 配置。IAM 为账号级。查询/创建角色及 `VeADKMPARuntimeAccessV1`,核验账号/名称及无条件信任 `vefaas`、`apig`,补齐已批准的 12 个系统策略和 14 个自定义动作的 Global 绑定,随后回读核验。保留额外绑定,不覆盖信任或已有策略。仅明确不存在错误允许创建,竞态和最终一致性采用最多 120 秒的恢复。取消保留资源。权限/信任/策略/属主/回读失败在 `iam_role` 阶段停止,输出白名单本地化错误,不输出原始云端文本和密钥。Studio 执行凭据须允许 GetRole、CreateRole、GetPolicy、CreatePolicy、ListAttachedRolePolicies、AttachRolePolicy。existing CLI 及通用智能体不受影响。完整权限及验收见[设计](../../prd-spec/features/mpa-runtime-iam/2026-10-09-managed-runtime-role.zh.md)。 - -## Studio 部署账号 - -内置配置不再固定预期账号。使用非空部署 STS 身份确定账号级 IAM、PG、VPC、APIG 和 Runtime 归属。新建 Runtime 的 `CLAW_SPACE_ID` 和 `RUNTIME_IAM_ROLE_TRN` 由该账号和所选角色派生;显式调用方环境变量覆盖保持优先。显式 YAML 预期账号检查和凭据账号变化检查继续生效。镜像仓库地址及 Worker 参考 ID 仍是源资源,具有独立访问要求。见[设计](../../prd-spec/bugfixes/mpa-studio-account/2026-10-09-sts-account.zh.md)。 - -## 网络归属描述与恢复 - -新 VPC/子网描述使用 `mpa-account-network-v1-`。恢复/校验托管资源仅接受当前范围精确的旧 `mpa-account-network:v1:` 或新标记,不改写已有资源。旧意图仅描述不同,派发标记明确为 false、无资源 ID、按名称查询无资源时,才修正描述并生成新 ClientToken;其他字段必须完全一致。结果不确定时,没有成功查询到资源就不得再次 Create。继续遵守现有锁及先保存后派发规则。见[设计](../../prd-spec/bugfixes/mpa-network-description/2026-10-09-valid-network-marker.zh.md)。 - -## 空网络查询 - -[已批准修复](../../prd-spec/bugfixes/mpa-network-discovery/2026-10-09-empty-network-results.zh.md)明确:DescribeVpcs/DescribeSubnets 第一页集合为 null/缺失且整数 `total_count=0` 时为空结果。其他非列表集合、null 集合且数量缺失/非整数/非零,以及后续页面的 null 集合仍为无效响应。SDK/提供方错误不得成为空查询。已记录的托管 VPC 可继续准备第一个子网,无需替换 VPC。分页、归属、持久化意图及未知结果防重复保护不变。 - -## 标准型共享网关准备 - -[已实施标准型网关修复](../../prd-spec/bugfixes/mpa-standard-gateway/2026-10-09-standard-shared-gateway.zh.md)规定:新托管 APIG 使用 standard、两个 1c2g 节点、small_1 CLB、公私网和 traffic 计费。新网关创建前,在共享锁内准备同 VPC、不同 APIG 支持可用区的两个子网。可选 gateway_subnet_intents 持久化每个伴随请求/token/发送状态,未知创建必须查询恢复。保留已有网关接管/复用和已有 Runtime 网络载荷。ExceededQuota 明确允许修正后重新发送;旧未知标记仍需审计恢复。部署凭据额外需要 apig:GetGatewayAvailableZones。不新增前端字段,不修改 Runtime 角色。 - -## 独立 Worker 配置 - -内置 Studio 使用镜像默认值创建 Worker,不引用 Tool,也不重复注入启动环境。CreateTool.Port 仍为 8000,默认 Envs 仅包含生成的 MPA_AGENT_ID。见[镜像默认值修订](../../prd-spec/bugfixes/mpa-worker-template/2026-10-09-worker-image-defaults.zh.md)。可选 `managed.worker.env` 是字符串映射;完整 `${ENV_NAME}` 在服务端解析。键必须使用大写。智能体/Runtime/Tool/技能空间绑定、继承的渠道/Runtime 密钥和控制库地址为保留字段。existing-id 不允许非空 env。显式 env 覆盖已过滤的可选模板环境,最后编排器生成 MPA_AGENT_ID。显式模板不存在仍报错。环境值不进入配置摘要/repr 或部署记录。已有归属、时限、取消及请求哈希/ClientToken 保护不变;修改未完成请求参数仍冲突。见[已批准设计与验证](../../prd-spec/bugfixes/mpa-worker-template/2026-10-09-independent-worker.zh.md)。 - -## Runtime 子网顺序与恢复 - -SubnetIds 按非空、唯一成员判断相同,不受平台返回顺序影响。VpcId、EnableSharedInternetAccess 仍不可变且精确比较。成员变化、重复及格式错误列表仍校验失败。恢复保留请求顺序,或在注册记录与当前 Runtime 成员相同时保留注册顺序,使已有请求哈希及 ClientToken 继续可用。成员不同的共享默认值不得替换当前智能体选择。无需结构或哈希格式迁移。设计与验证:[子网顺序修复](../../prd-spec/bugfixes/mpa-subnet-order/2026-10-09-subnet-order.zh.md)。 - - - -镜像回退(2026-10-09):已撤回公开仓库查询及自动 digest 转换。配置接口和新任务使用内置本地 latest 默认值;已有任务快照保持不变。见[回退设计](../../prd-spec/bugfixes/mpa-create-images/2026-10-09-restore-main-images.zh.md)。 - -### 托管技能空间描述(CON-3/CON-4) - -新的名称部署创建 `Description: Skills for MPA agent `,使用经过校验、去除首尾空白的手填名称。不提供名称的调用保留内部 ID 描述。空间 `Name`、`display_name`、归属标签、ID 及绑定仍由内部标识派生。已有/显式配置空间直接复用,不更新描述。升级前待完成请求仅在完整旧描述候选与持久化 `skill_space_request` 匹配时保留原描述;其他待完成输入变化仍报错。结果未知时通过发现恢复,不盲目重复 CreateSkillSpace。见[描述设计](../../prd-spec/bugfixes/mpa-skill-space-description/2026-10-09-entered-name-description.zh.md)。 - -## CON-16:部署账号方舟模型 Key - -[账号 Key 设计](../../prd-spec/features/mpa-model-key-discovery/2026-10-10-account-ark-key.zh.md)定义:内置 Studio 使用 `managed.model-key.mode: ark`,可选精确 ID/名称选择器(`VEADK_MPA_ARK_API_KEY_ID` / `VEADK_MPA_ARK_API_KEY_NAME`)。本地检查不需要模型明文 Key 或云访问。创建在资源变更前,使用刷新且经 STS 账号核验的部署凭据调用 ListApiKeys/GetRawApiKey。读取所有页面并去重 ID。精确选择器要求唯一匹配;否则多个候选按创建时刻选最新,同时间选择字符串 ID 字典序最大者。接受 CreateTime(兼容 CreatedAt/CreationTime)、正的 Unix 秒/毫秒或带时区 ISO 8601;任何候选时间缺失/异常时,在读取明文前失败。唯一候选无需时间字段。拒绝明确非 Active 状态,兼容缺少 Status 的旧响应。参见[最新 Key 后续设计](../../prd-spec/features/mpa-model-key-discovery/2026-10-10-latest-created-key.zh.md)。不使用掩码、不创建 Key。复制的 Profile 只向 Runtime 注入明文;原始云错误和凭据不进入事件、日志或任务/bootstrap 存储。调用超时有界,暂时错误最多三次尝试;取消后不继续变更。原请求哈希检查拒绝已提交的变更输入。CLI 默认显式 Key,通用智能体鉴权不变。默认部署角色已具备两个方舟动作;客户自管角色须授权。模型开通/访问及 Key IP 规则仍由操作者准备。 - -## CON-17:BytePlus 托管创建 - -Studio 服务端选择的云平台允许 BytePlus `ap-southeast-1` 托管创建 MPA,并保留现有火山引擎流程。浏览器不能指定云平台、账号或云接口地址;平台与地域不匹配时,在资源变更前失败。国内配置值保持不变。BytePlus 使用自己的 AK/SK/会话令牌,或显式指定的轮转 IAM 凭据文件,每次刷新凭据通过 `open.byteplusapi.com` STS 核验账号;不读取火山引擎环境凭据。Runtime、Skills、Tools SDK 客户端在线程内按平台上下文构造。Identity 及签名 VPC/ECS/APIG/AIDAP/Ark/IAM 请求选择对应平台接口。不支持的 API 或必要 IAM 策略显式失败,不回退国内服务,也不省略权限。 - -BytePlus 默认使用现有海外 Studio 模型及地址,将平台/地域传入 Runtime 和 Worker,并使用 Runtime 的 `APIG_TOP_ENDPOINT` 覆盖项。可选 TOS 挂载使用海外地址。新的 BytePlus 任务默认使用 `agentkit-platform-3001037806-ap-southeast-1.cr.bytepluses.com/mpa/mpa_agent:latest` 和 `agentkit-platform-3001037806-ap-southeast-1.cr.bytepluses.com/mpa/mpa_codex_worker:latest`;可信服务端覆盖项仍为 `VEADK_MPA_BYTEPLUS_RUNTIME_IMAGE` / `VEADK_MPA_BYTEPLUS_WORKER_IMAGE`。国内默认值和已有任务快照保持不变。参见 [BytePlus 镜像修复](../../prd-spec/bugfixes/mpa-byteplus-images/2026-10-14-byteplus-default-images.zh.md)。服务端可通过 `VEADK_MPA_BYTEPLUS_AIDAP_HOST` 覆盖 AIDAP 主机名;URL、路径及用户信息等无效输入在本地检查时失败。`VEADK_MPA_BYTEPLUS_CREDENTIAL_FILE` 显式指定轮转 IAM 文件;没有 BytePlus 环境凭据时,不会隐式读取国内挂载文件。可用区校验接受 `ap-southeast-1a` 和 `ap-southeast-1-a`,不改写资源 ID,并保留国内校验。浏览器及任务响应不包含凭据。BytePlus 任务在持久化指纹和子进程 stdin 中增加可信平台标记;默认任务及 PG 引导文件增加 `-byteplus` 后缀,保留国内恢复状态。共享 Workspace/数据库布局、锁/重试/取消/就绪检查及可选 OpenViking 语义不变。控制台链接跟随平台。镜像拉取、海外 API/策略可用性及真实就绪/聊天行为须单独实测;本地配置就绪不能证明云资源可用。参见[设计及验证](../../prd-spec/features/mpa-byteplus/2026-10-10-byteplus-creation.zh.md)。 - -BytePlus 托管 IAM 默认通过 `open.byteplusapi.com` 调用,保留可信服务端 `IAM_OPENAPI_HOST` 覆盖;共享 IAM 工具与国内创建行为不变。 - -BytePlus 托管 AIDAP 默认使用 `open.byteplusapi.com`,仍支持已校验的 `VEADK_MPA_BYTEPLUS_AIDAP_HOST` 覆盖。API 可访问不代表 Workspace 创建成功;继续保留失败状态及属主校验,不自动删除或接管失败的同名资源。 - -BytePlus 托管 Runtime 继承启用的应用及控制面 APM 默认值:`ENABLE_APMPLUS=true`、`FORCE_APMPLUS_EXPORTER_REGISTRATION=true` 和 `ApmplusEnable=true`,与火山引擎一致。已有 Runtime 和已提交任务快照不变。参见 [BytePlus APM 恢复](../../prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-restore-application-apm.zh.md)。 - -## CON-18:自动 OpenViking - -新的北京内置 Studio 请求显式选择 `openvikingMode: auto`;缺少该值的旧请求、手动 CLI 和 BytePlus 保留现有行为。此类新请求第三步以自动准备替代 OV 手填。使用默认 VLM/Embedding 模型身份、显式 Credentials Source=volcengine 和带版本 EndpointID 创建独立企业库,已解析方舟 Key 仅加入复制的创建请求,不进入持久化意图,等待 INIT 至 READY,服务端取得 default 用户 Key,部署前注入 OPENVIKING_URL/RESOURCE_ID/API_KEY/USER。作用域非秘密意图/ID 写入授权部署注册库,重试复用并核验归属/配置。注册资源丢失、FAILED 或创建结果不确定均明确失败;不盲目重建或回退创建方舟接入点。Key 不存入注册库/任务/结果。取消及删除智能体保留 OV。默认模型建库能力在隔离实测前仍未确认。通用智能体不变。[设计](../../prd-spec/features/mpa-openviking-auto/2026-10-13-default-model-creation.zh.md)。 - -CON-18 来源修正:仅在没有 OV ID、没有待完成 Runtime 操作、明确拒绝后创建发送标志为 false,且旧无 Credentials 意图完全匹配时允许升级。未知/进行中/改变及已登记意图保持冲突拒绝。Get 回读须匹配 Source/EndpointID。不增加 UI 字段或模型接入点创建。参见[来源修正](../../prd-spec/bugfixes/mpa-openviking-source/2026-10-13-model-source.zh.md)。 - -## CON-19:新 MPA 请求独立业务 Workspace - -内置 Studio 新配置声明可选 `businessWorkspaceMode: "per-agent"`。新表单显式提交/持久化,缺省保持 `shared`,已提交草稿不升级。`managed.postgres.business-workspace-mode` 默认 `shared`;显式 `per-agent` 要求自动 PG 且未配置业务 Workspace ID。稳定智能体范围的名称/范围/标签保证独立创建和重试。先准备共享管理库再读取智能体部署绑定;旧共享绑定继续共享,冲突明确失败。管理注册库固定实际 Workspace ID、布局及端点;Runtime 获得自己的业务连接及不变的共享注册库连接。保留现有未知结果/取消恢复和本地锁限制。不迁移、不初始化业务表、不删除 Workspace;通用智能体和 Runtime 业务接口不变。两个现有提供商适配器均遵循此契约。见[设计与验证](../../prd-spec/features/mpa-agent-pg-workspace/2026-10-13-independent-business-workspace.zh.md)。 - -## CON-20:名称预检查与独立 aidb 请求 - -内置 Studio 新配置/表单显式选择 `businessDatabaseName: aidb` 及 `businessWorkspaceMode: per-agent`。Workspace 为 `mpa_agent_`,业务库为 `aidb`。缺少库选择时保留旧哈希及请求指纹;已有 Workspace/数据库绑定不重命名、不切换。初始无标记 aidb 的接管要求独立 Workspace 归属已核验、数据库属主匹配、无非系统/非扩展关系数据且无已有 Runtime/ready 绑定;缺失/不匹配/非空明确失败。初始化使用事务锁,在创建 Runtime 前写入智能体标记。 - -授权名称检查发生于首次下一步、任务接收及新布局云准备前。Runtime、worker 和目标 Workspace 重名时拒绝其他/不明确归属,已核验自身绑定可重试。查询失败/超时明确报错。私有提供商/账号/地域任务快照防止本机并发同名及换凭据重试,仅活跃任务占用名称。只读检查不保证跨主机原子互斥。通用智能体、共享控制资源及 PG 删除策略不变。见[批准设计](../../prd-spec/features/mpa-name-preflight/2026-10-13-runtime-name-and-workspace-labels.zh.md)。 - -新布局 Web 预检查/提交使用服务端按用户/请求生成的内部身份;传入不同 agent ID 会被拒绝。旧显式 ID 请求保留原契约。 - -## CON-21:本次创建最大实例数 - -新的可编辑 MPA 表单在基本信息中提交严格正整数 `maxInstance`,火山默认 20、BytePlus 默认 10。提供商帮助文字和新请求重置使用相同默认值;显式已保存草稿及已提交/旧请求重试输入保持原样。数值随请求身份保存、传入 runner 并应用为 Runtime `MaxInstance`。小于配置最小值在受理前拒绝。提交后重试数值不可更改;省略/null 的旧字段保留原指纹/配置默认,不自动升级。重名错误复用既有 danger/soft Alert,本地化文案不变。不影响通用创建及已部署 Runtime 设置。见[设计](../../prd-spec/features/mpa-create-scaling/2026-10-13-max-instance-input.zh.md)。 - -OV 路由注册及配置检查须保留 Studio 延迟加载启动:签名/网络函数只在首次实际 OV API 调用时加载,不在导入适配器时加载。见[官方 PR 检查修正](../../prd-spec/bugfixes/mpa-upstream-readiness/2026-10-13-startup-and-lockfile.zh.md)。 - -CON-18 BytePlus 扩展:BytePlus `ap-southeast-1` 新自动请求创建个人版库,顶层及凭据 Source=volcengine,复用已解析的 MPA 推理 API Key。模型名保留 seed-2-0-lite/skylark-embedding-vision;EndpointID 默认值为 seed-2-0-lite-260428/skylark-embedding-vision-251215,BytePlus Ark 已接受其对应基础模型版本。服务端 OV_VLM_ENDPOINT_ID/OV_EMBEDDING_ENDPOINT_ID 可覆盖;非法值在云资源变更前失败。不使用 OV_MODEL_API_KEY 或 Coding Plan。Source/EndpointID 作为非密钥意图保存;改变接入点或旧 Coding Plan 绑定产生冲突,不重新建库。海外签名、取 default Key 不传 UserID、四个 Runtime 变量、资源保留、创建结果未知及密钥不落盘保证不变。国内及已提交手动流程不变。个人版 default 用户与海外数据面仍需单独适配 Runtime。见[已批准后续设计](../../prd-spec/features/mpa-byteplus-openviking/2026-10-14-inference-endpoints.zh.md)和[模型标识修正](../../prd-spec/bugfixes/mpa-byteplus-model-identities/2026-10-14-byteplus-model-identities.zh.md)。 - - -CON-10/CON-11 IAM 恢复:在增量任务诊断中保留白名单接口/类别及经校验的可选 Request ID。在已有期限内最多四次重试临时 IAM 协调,等待 1/2/4 秒,不确定写入后从固定名称回读开始。永久/未知失败立即结束,临时失败耗尽使用 iamTemporaryFailure。取消后不再发送 SDK 调用。兼容旧诊断字段及已有权限错误 key。见[已批准修复](../../prd-spec/bugfixes/mpa-iam-retry/2026-10-14-transient-errors.zh.md)。 - -CON-10/CON-18 OV 诊断:结构化云端拒绝及 HTTP/SDK 异常响应体保留可选且已验证的 errorCode(大写字母开头的 ASCII 错误码,最多 128 字符;排除凭证形式的值)及现有可选十六进制/UUID requestId。标识通过子进程诊断事件进入后台日志和追加的 task_diagnostics.error_code/request_id 列;兼容旧事件/记录,每任务仍最多 100 条诊断。排除原始 Message、响应体、凭证和异常文本。公开任务字段、国内/BytePlus 载荷、错误分类和不确定建库重试保护均不变。缺失或无效元信息时仍可能无法取得原始原因。见[诊断修复](../../prd-spec/bugfixes/mpa-openviking-diagnostics/2026-10-14-provider-errors.zh.md)。 - -CON-17/CON-18 BytePlus 默认已恢复:支持的 ap-southeast-1 新请求/可编辑草稿重新启用个人版自动 OV。已提交的自动/手动/无 OV 身份保持不变;无 OV 请求提交后要测试 OV,须“新建另一个智能体”。国内自动、手动路径和不确定建库保护不变。见[恢复修复](../../prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-json-and-restore-ov.zh.md)。 - -CON-10 Runtime 诊断:create_runtime/update_runtime 失败从 SDK 属性、结构化 HTTP/SDK 响应体及有界 JSON/字节串异常参数保留已验证的 category/errorCode 和可选 requestId。解析上限 65,536 字节/字符,最多八个防循环 cause。原始文案/参数/响应体、凭证和 ClientToken 不进入诊断。同一异常、调用次数、取消、分类、载荷和重试语义保持不变,缺失元信息仍省略。见[补齐修复](../../prd-spec/bugfixes/mpa-runtime-diagnostics/2026-10-14-sdk-json-and-restore-ov.zh.md)。 - -CON-20 实现纠正:组合对应 Runtime 列表/详情归属证据,Worker 使用真实 managed_by/mpa_agent_key 标签及 MPA_AGENT_ID 环境变量。契约不变,见[重试修复](../../prd-spec/bugfixes/mpa-name-retry/2026-10-14-owned-resource-metadata.zh.md)。 - -CON-20 重试证明:仅认证过的已接受请求,且标准化参数与存储值完全一致、服务端派生 agent 身份及私有 provider/account/region 快照通过 STS 核对时,才能补足空的云端创建者标签。明确外部标签仍拒绝。POST 在服务端取得证明,已有 expectedAccount 快照将同一检查带入子进程创建。不信任浏览器重试标志、不增加公开字段、不直接跳过云端预检。 - -CON-17 APM Key 传递:Studio BytePlus 创建在写资源前使用已核验部署凭据,向 `apmplus-server.ap-southeast-1.byteplusapi.com` 调用 `GetAppKey`,取得 `data.app_key`,注入 Runtime Envs 的 `OBSERVABILITY_OPENTELEMETRY_APMPLUS_API_KEY`。保留可信配置中的有效显式 Runtime Key。获取失败时停止创建,采用有限重试和安全的 `get_apm_key` 诊断;Key 不保存在任务/启动/部署记录或浏览器结果中。国内和非 Studio 路径不变。保留未完成输入哈希检查,不静默升级旧任务。见[服务端 APM Key 设计](../../prd-spec/bugfixes/mpa-byteplus-apm/2026-10-14-server-app-key.zh.md)。 - -分提供商默认值调整:[BytePlus 最大实例数](../../prd-spec/bugfixes/mpa-byteplus-scaling/2026-10-15-default-maximum.zh.md)。后端/YAML/CLI 配置默认值保持不变。 - -CON-17 OTLP 采集地址:新的 BytePlus Studio 配置注入 `OBSERVABILITY_OPENTELEMETRY_APMPLUS_ENDPOINT=http://apmplus-ap-southeast-1.bytepluses.com:4317`。可信的显式 Runtime 环境变量仍按既有优先级覆盖。国内默认值、通用智能体、AppKey 获取、已有资源和已提交快照不变。见[地址设计](../../prd-spec/bugfixes/mpa-byteplus-apm/2026-10-15-otlp-endpoint.zh.md)。 - - -### 云端 Studio MPA IAM 凭据与 OpenViking 权限 - -BytePlus `veadk studio deploy` 自动写入非密钥函数环境变量 -`VEADK_MPA_BYTEPLUS_CREDENTIAL_FILE=/var/run/secrets/iam/credential`。托管创建使用 -挂载的轮转角色凭据,每次调用重新读取;文件缺失、格式错误或字段不完整时明确失败。 -部署不复制操作者的 AK/SK 或本地凭据文件路径。本地 BytePlus AK/SK 和显式 CLI 文件 -选择不变,国内创建保留现有 IAM 回退。 -两个平台共用的默认 Studio 自定义角色策略授权 -`vikingdb:CreateOpenVikingCollection`、`vikingdb:GetOpenVikingCollection` 和 -`vikingdb:AccessOpenVikingApiKey`,不增加 OV 删除权限。 -已有 Studio 需重新部署以应用环境变量并更新默认角色策略;自定义角色需单独具备等价权限。 -服务开通、配额、模型访问和网络连通仍是前置条件,本改动不代表已经完成真实云端验证。 - - -CON-17 TOS 资源凭据:`/web/deployment-resources` 将当前服务端解析的 AK/SK/session token 传给 TOS 和 CR 查询。每次请求重新解析凭据,客户端不跨请求缓存。没有完整显式凭据时保留原有 SDK 发现机制。保持 provider endpoint 解析、过滤和凭据脱敏错误行为。 - -## Studio latest 内容快照(2026-10-21) - -批准的[只读设计](../../prd-spec/bugfixes/studio-latest-image-refresh/2026-10-21-readonly-tag-resolution.zh.md)修订 Studio 创建的 CON-1/3/6/8/9。输入保留 latest;在 Worker/Runtime 意图前,持有既有部署锁,以匿名 GET 将 latest manifest 解析为同仓库已发布、SHA-256 完全相同的非 latest 标签。先完整读取有界分页,优先已有 digest 命名标签,其余按字典倒序核验。保留并校验 Docker/OCI manifest 和 index。不使用云凭据、仓库管理 API、写权限,不发布标签、不搬运镜像、不新增流水线。发布方必须保留匹配版本标签且不得覆盖;无匹配项明确失败。固定 tag 和独立 CLI 不变。 - -原生 `image_snapshots` 在云创建前保留原始 `requested` 输入和 `selected` 的 `{digest,image}`。重试验证原输入和保存 tag 的 digest,不读取 latest、不列举标签;兼容原 digest 命名快照。保存内容变更或缺失时失败,不重新选择。不改写缺少选择记录的旧未完成意图,不升级既有运行 MPA。 - -仅信任当前平台 CR 后缀。匿名 bearer 挑战须为同主机 HTTPS/默认端口,无用户信息或重定向,token 只有 pull scope。禁用 requests 环境鉴权。单响应最多 1 MiB,发现最多 20 页 / 1000 个标签项,检查 120 秒预算,连接/读取上限为 10/30 秒。分页保持同仓库 tags-list 路径,只允许 n/last 参数,拒绝循环和不安全链接。非法仓库响应及脱敏传输错误不回退 latest。要求凭据的私有 latest 仓库不属于公共发现契约;标签保留和不覆盖由发布方保证。 - -### 失败详情(火山与 BytePlus) - -失败的 MPA 任务显示本地化安全原因,可展开/复制阶段、操作、可选的已校验 errorCode 和 requestId。不展示建议及原始 SDK/SQL/异常正文。已知配额、权限、超时及资源状态标识显示安全说明;缺失详情时明确说明原因未知。创建成功及通用智能体视图保持不变。 - -已有资源适配器与终态 runner 在异常包装时保留源头诊断,不新增平台调用或改变创建/重试决策。任务重启被接受后推进新增的 tasks.diagnostic_start_id 游标(旧记录默认 0);当前 latestDiagnostic 忽略之前轮次,仍保留有界历史诊断。页面忽略阶段不匹配及非 failed 诊断。现有请求 ID、快照、错误键、云端参数及资源恢复保持不变。 - -见[失败详情设计](../../prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-failure-details.zh.md)。 - -新增可选字段 `exceptionType`(最多八项固定异常类型)、`httpStatus`(100–599 整数)、`responseType`(application/json、text/html、text/plain、other),在采集、持久化及展示边界校验。增量 `task_diagnostics.details` 对旧行默认 `{}`。IAM 在抑制异常链前保留这些证据;Runtime 准备分别标识 load_credentials、get_caller_identity、runtime_client。不改变异常及重试语义,不采集原始 stderr。展开详情字号与原因段落统一为 13px。参见[诊断补齐](../../prd-spec/bugfixes/mpa-creation-failure-details/2026-10-20-diagnostic-completeness.zh.md)。 diff --git a/specs/studio-mpa-cron-tasks/README.md b/specs/studio-mpa-cron-tasks/README.md deleted file mode 100644 index 4f32cd107..000000000 --- a/specs/studio-mpa-cron-tasks/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# MPA Runtime task management - -[中文版](README.zh.md) - -Revised 2026-10-10. Component ID: studio-mpa-cron-tasks. - -Studio presents the selected Runtime's user-scoped scheduled tasks. MPA owns persistence, authorization, scheduling and execution. The Studio server owns Runtime access checks, trusted principal resolution and gateway credentials. No TOP/JWT acquisition or all-user fallback is performed. This contract supersedes the prior all-user viewer after MPA revert `91fd6a3`. - -## HTTP and identity - -All routes require `region` and existing selected-Runtime authorization. Resolve `x-user-id` from the authenticated Studio principal, never from a browser identity header. Resolve the upstream endpoint/key through existing Runtime connection code. Forward only gateway Authorization and x-user-id. JWT-enabled Runtimes must return their normal authentication failure. - -| Studio route | Method | MPA route | -| --- | --- | --- | -| `/web/mpa-cron/{runtime_id}` | GET / POST | `/api/v1/esa-cron-tasks` | -| `/web/mpa-cron/{runtime_id}/{task_id}` | POST / DELETE | `/api/v1/esa-cron-tasks/{task_id}` | -| `/web/mpa-cron/{runtime_id}/{task_id}/run` | POST | `/api/v1/esa-cron-tasks/{task_id}/run` | -| `/web/mpa-cron/{runtime_id}/{task_id}/runs` | GET | `/api/v1/esa-cron-tasks/{task_id}/runs` | - -Task IDs allow letters, digits, underscore and hyphen. Mutations allow only existing MPA schema field names, with a 32 KiB body limit. Upstream validates field values. Requests time out after 30 seconds and do not follow redirects. HTTP failures preserve their status without echoing upstream bodies; malformed/network/redirect responses fail safely. Listing uses includeDisabled=true and server pages of 20. History remains paginated. Mutations preserve expectedVersion and stable clientToken for retries of the same create/run operation. - -## UI and state - -The mono reference supplies list/calendar, status filter, overview, task detail/history, create/edit/copy, delete confirmation, enabled switch and run-now interactions. Studio uses existing local controls/styles rather than adding mono's workspace dependency graph. Creation requires an Agent ID and prompt; Web and Feishu delivery are supported, and edits preserve unchanged delivery metadata. Copy opens a form without writing until save. - -Read all task pages before presenting a complete calendar or filtered list, deduplicate IDs, paginate the list locally by 10. Aggregate metrics use the upstream response's actual all-history scope, not an invented seven-day window. Task lastRunAt and nextRunAt remain distinct. Calendar expands Once/Interval/Daily/Weekly/Monthly and fixed-time Cron, honoring schedule zones and DST; complex Cron shows only server nextRunAt. Day cells group multiple executions of one task. Calendar times display in the device zone; schedules show their configured zone. - -No target means no request. Target changes abort reads and ignore stale results, including history and mutation completion. Writes use a single-flight guard; aborting an HTTP write does not mean undoing its server effects. Failed writes retain form inputs, and version conflicts require refreshing. Loading, empty and errors are distinct. Existing Studio/TOS scheduling and ADK sessions are unaffected. - -## Verification - -See the [implementation and acceptance record](../../prd-spec/features/studio-mpa-cron-tasks/2026-09-15-mono-task-management.md). Cover proxy boundaries, API schemas, mutation concurrency/idempotency, paging, timezone/DST, stale responses, IME, loading/error/retry and keyboard behavior. Incremental coverage must exceed 95%. Browser fixture verification is distinct from live Runtime reads; do not claim live write E2E from mocked tests. - -The task editor hides executor Agent input. New tasks automatically use the selected Runtime ID as agentId; edits/copies preserve the original value. - -New task creation omits agentId. The Runtime resolves the configured Agent display name, falling back to its runtime agent name. Explicit IDs and edit/copy values remain unchanged. This supersedes the previous Runtime ID default. Approved by the user on 2026-09-15; review found no authentication or execution routing changes. - - -## Feishu delivery accounts (2026-10-09) - -For multi-bot-capable Runtime, create/edit/copy explicitly selects an enabled Feishu appId and saves delivery.appId. Multiple accounts never default to the first; unchanged delivery preserves thread metadata, and Web delivery removes appId. Missing/disabled accounts and permission/read failures block Feishu save with an actionable error. Legacy runtimes retain existing behavior. Bot choices use existing administrator-authorized channel APIs, without weakening task principal boundaries or falling back to another account. See [design](../../prd-spec/features/mpa-feishu-multi-bot/2026-10-09-studio-multi-bot.md). diff --git a/specs/studio-mpa-cron-tasks/README.zh.md b/specs/studio-mpa-cron-tasks/README.zh.md deleted file mode 100644 index c29c21ecb..000000000 --- a/specs/studio-mpa-cron-tasks/README.zh.md +++ /dev/null @@ -1,41 +0,0 @@ -# MPA Runtime 定时任务管理 - -[English](README.md) - -修订日期:2026-10-10。组件 ID:studio-mpa-cron-tasks。 - -Studio 展示所选 Runtime 内按用户隔离的定时任务。MPA 负责持久化、授权、调度和执行;Studio 服务端负责 Runtime 访问检查、可信用户身份及网关凭证。不获取 TOP/JWT,不回退到全用户读取。MPA 撤销提交 `91fd6a3` 后,本契约替代之前的全用户查看器。 - -## HTTP 和身份 - -所有路由要求 `region` 并沿用所选 Runtime 授权。`x-user-id` 来自认证的 Studio 用户,不能使用浏览器传入的身份请求头。上游端点/密钥由已有 Runtime 连接逻辑解析,仅转发网关 Authorization 和 x-user-id。JWT 开启的 Runtime 保留其正常鉴权失败。 - -| Studio 路由 | 方法 | MPA 路由 | -| --- | --- | --- | -| `/web/mpa-cron/{runtime_id}` | GET / POST | `/api/v1/esa-cron-tasks` | -| `/web/mpa-cron/{runtime_id}/{task_id}` | POST / DELETE | `/api/v1/esa-cron-tasks/{task_id}` | -| `/web/mpa-cron/{runtime_id}/{task_id}/run` | POST | `/api/v1/esa-cron-tasks/{task_id}/run` | -| `/web/mpa-cron/{runtime_id}/{task_id}/runs` | GET | `/api/v1/esa-cron-tasks/{task_id}/runs` | - -任务 ID 只允许字母、数字、下划线和连字符。写入字段只允许现有 MPA schema 字段名,请求体上限 32 KiB,字段值由上游校验。请求超时 30 秒,不跟随重定向。HTTP 失败保留状态码,不回显上游响应体;格式错误、网络失败、重定向安全报错。列表使用 includeDisabled=true,每次服务端分页 20 条;执行历史保留分页。更新使用 expectedVersion,同一次创建/执行重试保留 clientToken。 - -## 页面和状态 - -参照 mono 的列表/日历、状态筛选、统计、任务详情/历史、创建/编辑/复制、删除确认、启停和立即执行交互。Studio 复用本地控件及样式,不引入 mono 的 workspace 依赖链。创建要求 Agent ID 和任务内容,支持 Web/飞书投递;编辑保留未改变的投递元数据。复制只打开表单,保存才写入。 - -完整读取任务分页后再展示日历或筛选列表,按 ID 去重,列表前端每页 10 条。统计使用上游实际的全历史范围,不标为不存在的近七日统计。lastRunAt 与 nextRunAt 含义不同。日历展开 Once/Interval/Daily/Weekly/Monthly 和固定时间 Cron,处理周期时区与夏令时;复杂 Cron 仅显示服务端 nextRunAt。单元格按任务合并多次执行。日历按设备时区显示,周期文案标注配置时区。 - -没有 Runtime 时不请求。切换目标取消读取并忽略过期结果,包括执行历史及写入完成响应。写入使用防重复提交保护;取消 HTTP 写请求不等于撤销服务端效果。写入失败保留表单,版本冲突要求刷新。加载、空数据、错误状态明确区分。既有 Studio/TOS 调度及 ADK 会话不受影响。 - -## 验证 - -见[实施和验收记录](../../prd-spec/features/studio-mpa-cron-tasks/2026-09-15-mono-task-management.zh.md)。覆盖代理边界、API schema、写入并发/幂等、分页、时区/夏令时、过期响应、IME、加载/错误/重试和键盘行为。增量覆盖率超过 95%。浏览器模拟数据验收与真实 Runtime 读取分开记录,不把模拟测试宣称为真实写入 E2E。 - -任务编辑器不展示执行 Agent 输入框。新任务自动使用所选 Runtime ID 作为 agentId;编辑和复制保留原值。 - -创建新任务不传 agentId,由 Runtime 使用配置中的 Agent 展示名称,缺失时回退运行时 Agent 名称。显式值及编辑/复制原值不变。此规则替代此前默认填写 Runtime ID 的处理。用户于 2026-09-15 明确批准;复核确认不改变鉴权和执行路由。 - - -## 飞书投递账号(2026-10-09) - -声明多机器人能力的 Runtime 在创建/编辑/复制时须明确选择启用的飞书 appId,并保存 delivery.appId。多个账号不默认选择第一项;未变更投递保留话题元数据,Web 投递移除 appId。缺失/停用账号、权限/读取失败阻止飞书保存并给出可执行错误。旧 Runtime 保留现有行为。账号选择使用既有管理员授权渠道 API,不放宽任务用户身份边界,不回退其他账号。见[设计](../../prd-spec/features/mpa-feishu-multi-bot/2026-10-09-studio-multi-bot.zh.md)。 diff --git a/specs/studio-mpa-prewarm/README.md b/specs/studio-mpa-prewarm/README.md deleted file mode 100644 index 733994f76..000000000 --- a/specs/studio-mpa-prewarm/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# Studio MPA Prewarm - -- Component ID: `studio-mpa-prewarm` -- Status/revision: proposed, 2026-09-23 -- Chinese: [README.zh.md](README.zh.md) -- Change: [Studio MPA login prewarm](../../prd-spec/features/studio-mpa-login/2026-09-23-studio-mpa-prewarm.md) - -## Responsibility and contract - -Studio owns `POST /web/mpa/identity-prewarm/{runtime_id}` and the MPA web-chat call before Runtime run. The route requires an authenticated Studio OAuth browser session, refresh credential, authorized MPA Runtime, and Runtime key-auth. It refreshes the Studio token pair, verifies the new ID token against the configured UserPool JWKS/issuer/client and Studio user subject, then sends `idToken` and `refreshToken` server-to-server to the Runtime's fixed `/identity/sessions/put`. It returns only success or a bounded error; no token enters browser JavaScript or logs. The Runtime endpoint and key come solely from Studio's trusted control-plane resolver. - -## State, failure, security, and compatibility - -The refreshed Studio session replaces the signed HttpOnly cookie, including when Runtime handoff fails after refresh-token rotation. ID tokens are request-local and omitted from newly encoded cookies; old cookies with the optional field remain decodable. A missing OAuth session, refresh token, MPA tag, key-auth credential, mismatched token subject, or failed upstream call is an explicit route error, but the frontend continues the MPA web-chat run so its normal login remains available. User cancellation still stops the run. The prewarm request is bounded to ten seconds on the frontend. Gateway-only sessions and Feishu chat do not use this route. No new browser-visible credential or client-controlled URL is accepted. Multiple Studio instances and independent MPA refresh rotation need live validation. - -## Verification - -`tests/auth/test_oauth2_auth.py` covers token/cookie state; `tests/cli/test_frontend_runtime_proxy.py` covers authorized handoff and failures. Frontend tests/build cover call order and non-MPA behavior. A real browser/UserPool/Runtime check remains necessary for production readiness. diff --git a/specs/studio-mpa-prewarm/README.zh.md b/specs/studio-mpa-prewarm/README.zh.md deleted file mode 100644 index 5b0dfef8f..000000000 --- a/specs/studio-mpa-prewarm/README.zh.md +++ /dev/null @@ -1,18 +0,0 @@ -# Studio MPA 预热 - -- Component ID: `studio-mpa-prewarm` -- 状态/修订:拟议,2026-09-23 -- English: [README.md](README.md) -- 变更:[Studio MPA 登录预热](../../prd-spec/features/studio-mpa-login/2026-09-23-studio-mpa-prewarm.zh.md) - -## 职责与契约 - -Studio 负责 `POST /web/mpa/identity-prewarm/{runtime_id}`,并在 MPA 网页聊天的 Runtime run 前调用。接口要求已鉴权的 Studio OAuth 浏览器会话、refresh 凭据、已授权的 MPA Runtime 和 Runtime key-auth。它刷新 Studio 令牌对,按配置的 UserPool JWKS/issuer/client 与 Studio 用户主体校验新 ID token,再将 `idToken` 和 `refreshToken` 从服务端发送到 Runtime 固定路径 `/identity/sessions/put`。响应只有成功或受限错误;令牌不进入浏览器 JavaScript 或日志。Runtime 地址和密钥只来自 Studio 可信的控制面解析器。 - -## 状态、失败、安全与兼容 - -刷新后的 Studio 会话替换 HttpOnly 签名 Cookie;即使 refresh token 轮换后 Runtime 交接失败也如此。ID token 只存在于本次请求,新的 Cookie 不编码该字段;带有可选字段的旧 Cookie 仍可读取。缺少 OAuth 会话、refresh token、MPA 标签、key-auth 凭据,或 token 主体不匹配、上游调用失败,均使接口明确报错,但前端仍继续 MPA 网页聊天 run,保留正常登录。用户取消仍会停止 run。前端将预热请求限制为十秒。仅网关会话及飞书聊天不调用此接口。不接受新的浏览器可见凭据或客户端指定 URL。多 Studio 实例和 MPA 独立 refresh token 轮换仍需线上验证。 - -## 验证 - -`tests/auth/test_oauth2_auth.py` 覆盖令牌/Cookie 状态;`tests/cli/test_frontend_runtime_proxy.py` 覆盖授权交接与失败。前端测试/构建覆盖调用顺序和非 MPA 行为。生产就绪仍需真实浏览器/UserPool/Runtime 验证。 diff --git a/specs/studio-oauth-session/README.md b/specs/studio-oauth-session/README.md deleted file mode 100644 index cf931261a..000000000 --- a/specs/studio-oauth-session/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Studio OAuth Session - -- Component ID: `studio-oauth-session` -- Status: proposed pending verification -- Revision: 2026-09-23 -- Chinese: [README.zh.md](README.zh.md) -- Change: [Studio MPA login prewarm](../../prd-spec/features/studio-mpa-login/2026-09-23-studio-mpa-prewarm.md) - -## Responsibility and entry points - -`veadk/auth/middleware/oauth2_auth.py` owns Studio's OAuth authorization-code exchange, token refresh, `OAuth2Session`, and signed browser session cookie. The Identity provider owns token issuance; Studio MPA prewarm consumes a request-local ID token. - -## Proposed contract - -- `CON-1`: `OAuth2Session.id_token` is optional and defaults to `None`, so older signed cookies remain decodable. -- `CON-2`: Authorization-code exchange and refresh retain a returned `id_token` only in the request-local session; a refresh without a new ID token does not reuse an old one. -- `CON-3`: New session cookies exclude `id_token`; old signed cookies with that optional field remain readable. Cookies are signed, not encrypted, and HttpOnly. A downstream refresh override wins when the middleware writes the response cookie. -- `CON-4`: `validate_id_token()` checks JWKS signature, issuer, expiry, and configured client audience before the prewarm consumer compares subject. - -## State, data, security, and compatibility - -The token endpoint is the source of truth; the cookie is the existing portable access/refresh-token session store. Concurrent refresh coalescing is unchanged. Old cookies remain valid. A prewarm request returns a rotated refresh token to the cookie even if Runtime handoff subsequently fails. ID tokens never enter new cookies or browser JavaScript. - -## Verification and limitations - -`CON-1`–`CON-4` map to login, refresh, legacy-cookie, validation, and middleware-override tests in `tests/auth/test_oauth2_auth.py`. Live UserPool behavior, client alignment, and production cookie size require external validation. diff --git a/specs/studio-oauth-session/README.zh.md b/specs/studio-oauth-session/README.zh.md deleted file mode 100644 index aaace8bdc..000000000 --- a/specs/studio-oauth-session/README.zh.md +++ /dev/null @@ -1,26 +0,0 @@ -# Studio OAuth 会话 - -- Component ID: `studio-oauth-session` -- 状态:拟议,待验证 -- 修订:2026-09-23 -- English: [README.md](README.md) -- 变更:[Studio MPA 登录预热](../../prd-spec/features/studio-mpa-login/2026-09-23-studio-mpa-prewarm.zh.md) - -## 职责与入口 - -`veadk/auth/middleware/oauth2_auth.py` 负责 Studio 的 OAuth 授权码交换、token 刷新、`OAuth2Session` 和签名浏览器会话 Cookie。Identity 提供方负责签发 token;Studio MPA 预热使用本次请求中的 ID token。 - -## 拟议契约 - -- `CON-1`:`OAuth2Session.id_token` 是可选字段,默认 `None`,旧签名 Cookie 仍能解码。 -- `CON-2`:授权码交换和刷新只在本次请求的会话中保留返回的 `id_token`;刷新未返回新 ID token 时不复用旧值。 -- `CON-3`:新的会话 Cookie 排除 `id_token`;含有该可选字段的旧签名 Cookie 仍可读取。Cookie 有签名但未加密,且为 HttpOnly。中间件写回响应 Cookie 时,下游刷新的会话优先。 -- `CON-4`:`validate_id_token()` 在预热调用方比对用户主体前,校验 JWKS 签名、签发方、有效期与所配客户端受众。 - -## 状态、数据、安全与兼容性 - -token 端点是事实来源;Cookie 是现有的可跨实例 access/refresh-token 会话存储。并发刷新合并不变。旧 Cookie 仍有效。即使 Runtime 交接随后失败,预热请求也会将轮换后的 refresh token 写回 Cookie。ID token 不进入新 Cookie 或浏览器 JavaScript。 - -## 验证与限制 - -`CON-1`–`CON-4` 对应 `tests/auth/test_oauth2_auth.py` 的登录、刷新、旧 Cookie、校验和中间件覆盖测试。真实 UserPool 行为、客户端一致性和生产 Cookie 长度仍需外部验证。 diff --git a/specs/studio-runtime-diagnostics/README.md b/specs/studio-runtime-diagnostics/README.md deleted file mode 100644 index 73e85f5e0..000000000 --- a/specs/studio-runtime-diagnostics/README.md +++ /dev/null @@ -1,63 +0,0 @@ -# Studio Runtime Diagnostics - -- **Component ID:** `studio-runtime-diagnostics` -- **Status:** Draft; proposed changes are governed by the related PRD -- **Revision:** 2026-09-30 -- **Chinese version:** [README.zh.md](README.zh.md) -- **Related PRD:** [MPA Runtime Integration Hardening](../../prd-spec/bugfixes/mpa-runtime-integration/2026-09-12-mpa-runtime-integration-hardening.md) -- **Owned code:** `frontend/server/runtime_logs.py`, `frontend/src/ui/RuntimeLogsDialog.tsx`, `veadk/cli/runtime_a2a_stream.py`, `frontend/src/adk/tokenUsage.ts`, `frontend/src/ui/TraceDrawer.tsx`, and the Runtime A2A BFF in `veadk/cli/cli_frontend.py` - -## Responsibility - -This component gives an authorized Studio user bounded live Runtime logs, a local download of the visible sanitized snapshot, normalized token usage, request-scoped model selection when advertised by an A2A Runtime, and normalized APMPlus trace inspection. It does not own Runtime log retention, provider billing, model credentials, or APMPlus IAM policy. - -## Contract - -- `CON-1`: Runtime logs are authorized through the Studio BFF, refreshed as replacement snapshots, sanitized server-side, and bounded to the latest 1,000 logical lines. Download returns exactly the current visible snapshot and never bypasses BFF authorization or sanitization. -- `CON-2`: A2A usage metadata and worker `usage.updated` are normalized into existing ADK `usageMetadata`/`modelVersion` fields. Replayed cumulative snapshots must not inflate the session total. -- `CON-3`: Model selection is visible only when the connected A2A agent advertises more than one allowed model. Studio sends only a model ID in request metadata; the Runtime validates it. Selection is session-local and immutable during an active turn. -- `CON-4`: Trace loading preserves explicit state semantics: 404 disabled, 425 collecting, 403 permission required, 502 provider failure, and 200 normalized spans. Empty or denied data is never reported as success. -- `CON-5`: Runtime API keys, model keys, provider credentials, and raw unsanitized logs never enter downloaded files, model-selector values, agent-card capabilities, or frontend state. - -## State and concurrency - -The log stream may reconnect; stale streams are cancelled on target/dialog changes and cannot overwrite the latest target. Blob URLs are revoked immediately after download. Token snapshots are keyed by their source/request identity and monotonic within a stream. Model selection cannot change mid-turn. Trace retries stop on close/unmount and distinguish retryable collection from terminal permission/configuration states. - -## Compatibility - -Non-A2A agents and A2A agents without model capabilities retain the existing Composer. Existing token and trace UI components remain the single presentation owners. Existing specialized transcript/tool renderers are unchanged. - -## Verification - -| Contract | Validation | -| --- | --- | -| `CON-1` | Runtime log service tests, dialog tests, browser download and reconnection checks | -| `CON-2` | A2A decoder and token aggregation tests plus live Runtime usage | -| `CON-3` | BFF metadata tests, mpa-agent validation tests, and browser default/override/invalid cases | -| `CON-4` | Endpoint tests for 404/425/403/502/200 and real TraceDrawer check | -| `CON-5` | Secret scan and assertions that keys/raw logs are absent from browser payloads/downloads | - -- `CON-6`: The A2A bridge emits a nonterminal connecting status before upstream waits. The transcript displays connecting/submitted/working as waiting/queued/running in the existing progress placeholder. Status is not an answer or proof of acceptance; content supersedes it. Valid streams wait until completion, error or cancellation, with no automatic retry. - -- `CON-7`: Authorized Runtime proxy requests carry the Studio principal owner as x-user-id, matching task management ownership; incoming identity headers cannot override it. - -- `CON-8` (implemented): For MPA A2A streams, a nonempty sandbox `invocation.completed.payload.finalMessage` is projected as a consolidated `turnComplete` answer with its sandbox invocationId. Legacy `text`/`message` payloads remain accepted. Only subsequent complete answer text parts exactly matching a boundary-trimmed sandbox final for that task are omitted. Distinct/extended text, reasoning, partial deltas, tools/errors and usage/status events remain. Empty, failed, cancelled, and unidentified-task events cannot claim this ownership. Identified sandbox source events project once per task across artifact updates and snapshots. State is decoder/request-local and is discarded on completion, disconnect or cancellation. Non-MPA and direct ADK/native session behavior is unchanged. - -`CON-8` is maintained by [MPA A2A final-answer ownership](../../prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-final.md), verified in `tests/cli/test_runtime_a2a_stream.py` and cross-layer frontend replay. This addition does not promote the other draft contracts to implemented status. - -- `CON-9` (implemented): A Runtime app discovered as `a2a-default` uses the A2A bridge for session create/list/read/delete/run and skips native execution-config preflight, regardless of MPA category/instance metadata. Other MPA apps retain native Profile/session/run/SSE behavior; ordinary ADK apps are unchanged. Errors and cancellation never switch protocols. No session migration, auth change or persistence change is implied. See [A2A session routing](../../prd-spec/bugfixes/studio-session-protocol/2026-09-22-a2a-session-routing.md) and `frontend/tests/runSseAbort.test.mjs`. - -- `CON-10` (implemented): Explicit MPA A2A transcript mode reconciles adjacent matching/extending consolidated outer reasoning snapshots and evaluates empty-response notices across one user request. Partial deltas and sandbox events remain intact; genuinely empty completed requests retain one notice. General ADK/A2A and native MPA default behavior is unchanged. See [MPA A2A reasoning](../../prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-reasoning.md). MPA outer and sandbox consolidated events without authoritative thought text retain thinking blocks from the live-preview region while replacing answer preview. This preserves distinct outer and sandbox reasoning after the final answer, including direct answers without sandbox calls; authoritative thought snapshots retain replacement semantics. Explicit MPA A2A bridge mode normalizes multi-part outer thought snapshots independently of sandbox invocation segments. Additive `reasoningSegmentId` keeps delayed sandbox thought tails in their original block across tool.call; tool results and answer deltas separate phases. General/default decoding is unchanged. MPA final/replacement artifact thought parts must be joined with exact whitespace before snapshot comparison; append-only chunks remain deltas. Coalescing never crosses non-thought parts. - -- `CON-11` (implemented): MPA A2A presents adjacent assistant fragments as one response with one footer, source-keyed request usage, last visible answer feedback identity and latest session-trace cutoff. Source turns and general-agent behavior remain unchanged. See [response grouping](../../prd-spec/features/mpa-response-grouping/2026-09-22-mpa-response-grouping.md). - -CON-8/CON-11 correction (implemented): preserve distinct outer text and reasoning after sandbox completion. Deduplicate only exact complete answer parts in the bridge; MPA grouped view may hide exact answer mirrors without mutating source turns and restores an extended fragment intact. General Codex tool behavior is restored. See [preserve follow-up](../../prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-followup-content.md). - -- [MPA reasoning snapshot normalization](../../prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-reasoning-snapshots.md). - -- `CON-12`: Only MPA A2A restores the trusted control-plane endpoint prefix `/runtime/[a-z0-9-]+` for a same-origin default `/a2a/jsonrpc` card URL. Already-prefixed URLs, other paths, cross-origin URLs, query/fragment/userinfo are not rewritten. Sending and history restoration use the same resolved address; general agents remain unchanged. See [A2A discovery fix](../../prd-spec/bugfixes/2026-09-23-mpa-a2a-discovery-default.md). - -- `CON-13` (implemented): Authorized GET Runtime-proxy `list-apps` prefers a usable Agent Card for control-plane-classified MPA and returns `["a2a-default"]` before ADK probing. General Runtimes and MPA without a usable card retain existing discovery/error behavior. Other requests, authorization, key/header construction and existing sessions are unchanged; reconnect refreshes cached apps, not session migration. See [MPA A2A preference](../../prd-spec/bugfixes/studio-mpa-a2a-preference/2026-09-30-prefer-a2a.md) and `tests/cli/test_frontend_runtime_proxy.py`. - -### CON-8 typed event identity (implemented correction, 2026-09-29) -For `mpa.sandbox-event.v1`, source IDs identify upstream records, not unique normalized event types. Project once per `(task_id, invocation_id, event_type, source_event_id)`; an absent source ID disables identity deduplication. MPA artifact frames reach per-part projection, including snapshots mixing old and new parts. Same-ID usage and completion must both survive in either order. Generic A2A frame deduplication is unchanged. The agentkit-mpa-agent relay uses type/source identity within its invocation closure and preserves wire IDs and its original callback. See [design and verification](../../prd-spec/bugfixes/mpa-sandbox-event-identity/2026-09-29-typed-event-dedup.md). diff --git a/specs/studio-runtime-diagnostics/README.zh.md b/specs/studio-runtime-diagnostics/README.zh.md deleted file mode 100644 index feb5b64e6..000000000 --- a/specs/studio-runtime-diagnostics/README.zh.md +++ /dev/null @@ -1,63 +0,0 @@ -# Studio Runtime 诊断 - -- **Component ID:** `studio-runtime-diagnostics` -- **状态:** 草案;提议变更由关联 PRD 管理 -- **修订日期:** 2026-09-30 -- **English version:** [README.md](README.md) -- **关联 PRD:** [MPA Runtime 集成加固](../../prd-spec/bugfixes/mpa-runtime-integration/2026-09-12-mpa-runtime-integration-hardening.zh.md) -- **负责代码:** `frontend/server/runtime_logs.py`、`frontend/src/ui/RuntimeLogsDialog.tsx`、`veadk/cli/runtime_a2a_stream.py`、`frontend/src/adk/tokenUsage.ts`、`frontend/src/ui/TraceDrawer.tsx` 以及 `veadk/cli/cli_frontend.py` 中的 Runtime A2A BFF - -## 职责 - -本组件为已授权 Studio 用户提供有界实时 Runtime 日志、当前可见脱敏快照的本地下载、规范化 Token 用量、A2A Runtime 广告能力时的请求级模型选择,以及规范化 APMPlus 链路查看。它不负责 Runtime 日志保留、Provider 计费、模型凭据或 APMPlus IAM 策略。 - -## 契约 - -- `CON-1`:Runtime 日志通过 Studio BFF 授权,以替换快照方式刷新,在服务端脱敏并限制为最近 1,000 个逻辑行。下载内容严格等于当前可见快照,不得绕过 BFF 授权或脱敏。 -- `CON-2`:A2A 用量 metadata 与 worker `usage.updated` 规范化为现有 ADK `usageMetadata`/`modelVersion` 字段。重放的累计快照不得放大 Session 总量。 -- `CON-3`:仅在连接的 A2A Agent 广告多个允许模型时显示模型选择。Studio 只在请求 metadata 中发送模型 ID,由 Runtime 校验。选择属于 Session 本地状态,活跃回合中不可改变。 -- `CON-4`:链路加载保持明确状态语义:404 未开启、425 采集中、403 需要权限、502 Provider 失败、200 规范化 Span。空数据或拒绝访问不得报告为成功。 -- `CON-5`:Runtime API Key、模型 Key、Provider 凭据和未脱敏原始日志不得进入下载文件、模型选择值、agent-card capability 或前端状态。 - -## 状态与并发 - -日志流可以重连;目标或弹窗变化时取消旧流,旧流不得覆盖最新目标。下载后立即回收 Blob URL。Token 快照按来源/请求标识隔离,并在一次流内保持单调。活跃回合中不能切换模型。链路重试在关闭/卸载时停止,并区分可重试采集与终态权限/配置错误。 - -## 兼容 - -非 A2A Agent 和未广告模型能力的 A2A Agent 保持现有 Composer。现有 Token 和链路 UI 组件继续作为唯一展示所有者。现有专用 Transcript/Tool renderer 不变。 - -## 验证 - -| 契约 | 验证 | -| --- | --- | -| `CON-1` | Runtime 日志服务测试、弹窗测试、浏览器下载及重连检查 | -| `CON-2` | A2A Decoder 与 Token 聚合测试及真实 Runtime 用量 | -| `CON-3` | BFF metadata 测试、mpa-agent 校验测试及浏览器默认/覆盖/非法场景 | -| `CON-4` | 404/425/403/502/200 Endpoint 测试及真实 TraceDrawer 检查 | -| `CON-5` | Secret scan 及浏览器 Payload/下载不含 Key 或原始日志的断言 | - -- `CON-6`:A2A 桥接在等待上游前输出非终止 connecting 状态。对话通过现有进度占位将 connecting/submitted/working 展示为等待/排队/执行中。状态不算答案或接受请求的证明,实际内容替换占位。有效流等待完成、错误或取消,不自动重试。 - -- `CON-7`:已授权 Runtime 代理请求使用 Studio 主体 owner 作为 x-user-id,与任务管理归属一致,浏览器身份头不得覆盖。 - -- `CON-8`(已实现):MPA A2A 流中非空的 sandbox `invocation.completed.payload.finalMessage` 投影为合并的 `turnComplete` 答案,保留 sandbox invocationId;兼容旧 `text`/`message` payload。仅省略与该任务已知 sandbox final 去除首尾空白后完全相同的后续完整答案文本 part。保留不同/扩展文本、推理、partial 增量、工具/错误及用量/状态事件。空、失败、取消和没有任务标识的事件不能取得此所有权。已标识的 sandbox 源事件在 artifact 更新和任务快照之间按任务仅投影一次。状态仅在本次 decoder/请求内,完成、断连或取消后销毁。非 MPA 和直接 ADK/原生会话行为不变。 - -`CON-8` 由 [MPA A2A 最终回答所有权](../../prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-final.zh.md) 维护,在 `tests/cli/test_runtime_a2a_stream.py` 及跨层前端回放中验证。本次新增不代表其他草案契约已实现。 - -- `CON-9`(已实现):探测为 `a2a-default` 的 Runtime 应用通过 A2A 桥接创建/列表/读取/删除/运行会话,并跳过原生执行配置前置查询,不受 MPA 类型/实例信息影响。其他 MPA 应用保留原生 Profile/session/run/SSE 行为,普通 ADK 应用不变。错误和取消不切换协议。不涉及会话迁移、鉴权或持久化变更。参见 [A2A 会话路由](../../prd-spec/bugfixes/studio-session-protocol/2026-09-22-a2a-session-routing.zh.md) 和 `frontend/tests/runSseAbort.test.mjs`。 - -- `CON-10`(已实现):显式 MPA A2A 转录模式合并相邻匹配/扩展的外层完整推理快照,并按一条用户请求判断空回复提示。保留 partial 增量及 sandbox 事件;真正空的已结束请求保留一次提示。通用 ADK/A2A 和原生 MPA 的默认行为不变。参见 [MPA A2A 推理](../../prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-a2a-reasoning.zh.md)。 MPA 外层及沙箱完整事件不含权威推理文本时,在替换答案预览的同时保留临时预览区的 thinking 块,避免最终答案到达后丢失不同的外层及沙箱推理,包括不调用沙箱的直接回答;权威推理快照仍采用替换语义。 显式 MPA A2A 桥接模式按来源归一化外层多 part 推理快照,并与沙箱调用阶段隔离。追加元数据 `reasoningSegmentId` 让迟于 tool.call 的沙箱推理尾部仍属于原块;工具结果和答案增量分隔阶段。通用/默认解码不变。 MPA 完整/替换 artifact 的推理 part 必须保留精确空白拼接后再比较快照;仅追加分片仍按增量处理。合并不得跨越非推理 part。 - -- `CON-11`(已实现):MPA A2A 将相邻助手片段显示为一个回复和一套底栏,按来源统计本轮用量,使用最后可见答案的反馈身份及最新会话轨迹截止时间。原 turn 和通用智能体行为不变。参见[回复合并](../../prd-spec/features/mpa-response-grouping/2026-09-22-mpa-response-grouping.zh.md)。 - -CON-8/CON-11 修正(已实现):保留 sandbox 完成后的不同外层文本与推理。桥接只去重完全相同的完整答案 part;MPA 合并视图可以隐藏完全相同的答案副本,但不改变原 turn,片段扩展后完整恢复。通用 Codex 工具行为恢复。参见[保留补充内容](../../prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-followup-content.zh.md)。 - -- [MPA 推理快照归一化](../../prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-mpa-reasoning-snapshots.zh.md). - -- `CON-12`:仅 MPA A2A 对同源默认 `/a2a/jsonrpc` 卡片地址补齐可信控制面 endpoint 的 `/runtime/[a-z0-9-]+` 前缀。已经有前缀、其他路径、跨源、查询/片段/用户信息不改写。发送和历史恢复一致使用该地址;通用智能体保持不变。参见 [A2A 发现修复](../../prd-spec/bugfixes/2026-09-23-mpa-a2a-discovery-default.zh.md)。 - -- `CON-13`(已实现):已授权 GET Runtime 代理 `list-apps` 对控制面识别的 MPA 优先探测可用 Agent Card,在 ADK 探测前返回 `["a2a-default"]`。普通 Runtime 和无可用卡片的 MPA 保留原发现/错误行为。其他请求、授权、凭据/header 构造和已有会话不变;重新连接刷新缓存应用,不迁移会话。参见 [MPA A2A 优先](../../prd-spec/bugfixes/studio-mpa-a2a-preference/2026-09-30-prefer-a2a.zh.md) 及 `tests/cli/test_frontend_runtime_proxy.py`。 - -### CON-8 带类型的事件标识(已实施修正,2026-09-29) -在 `mpa.sandbox-event.v1` 中,来源 ID 标识上游记录,不唯一标识标准化事件类型。每个 `(task_id, invocation_id, event_type, source_event_id)` 投影一次;缺失来源 ID 时不做标识去重。MPA artifact 帧交给逐部分投影,包括新旧部分混合的快照。相同 ID 的用量及完成事件无论何种顺序都须保留。通用 A2A 帧去重不变。agentkit-mpa-agent 转发器在调用闭包内按类型/来源标识去重,保留传输 ID 及原回调。参见[设计与验证](../../prd-spec/bugfixes/mpa-sandbox-event-identity/2026-09-29-typed-event-dedup.zh.md)。 diff --git a/specs/studio-sandbox-download/README.md b/specs/studio-sandbox-download/README.md deleted file mode 100644 index 0a90b5c8e..000000000 --- a/specs/studio-sandbox-download/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Studio Sandbox downloads - -[中文](README.zh.md) - -Component: studio-sandbox-download; revision: 2026-09-30; status: active. -Owner: frontend/src/ui/SandboxFileLink.tsx, Markdown.tsx, App.tsx and adk/client.ts. Depends on existing Studio Runtime proxy and MPA session download API. Related [PRD](../../prd-spec/features/studio-sandbox-download/2026-09-17-history-download.md). - -## Contracts -- CON-1: Only assistant conversation Markdown with appName/sessionId context converts absolute links beneath `/data/output/` and `/data/workspace/`. Decode URL escapes once; reject malformed escapes, traversal segments, backslashes, control characters, empty filenames and query/hash suffixes. Chinese and spaces supported. Other Markdown consumers/links retain existing behavior. -- CON-2: GET `/api/v1/sessions/{sessionId}/files/download?path={encodedPath}` uses the current app's resolved endpoint and existing authentication/identity transport. Runtime credentials remain server-side; MPA verifies session ownership and Worker enforces filesystem boundaries. No artifact lookup or new Sandbox. -- CON-3: Link activation prevents navigation, suppresses duplicate requests, shows localized busy state, saves a blob with the path basename and releases object URLs. Failure displays a local alert; activation retries. Scope changes/unmount abort requests and ignore stale responses. Existing transfer timeout applies. - -## Data, compatibility and diagnostics -No persistent data/configuration or protocol changes. Existing history and streaming Markdown share rendering. Normal external links and video behavior remain. Errors use localized generic feedback (no server response bodies). Sandbox expiry/file removal can make old links unavailable; browser buffers complete files. - -## Verification and change record -Tests: frontend/tests/sandboxDownload.test.tsx and sandboxDownloadClient.test.ts. Cover CON-1/2/3 including negative paths, current Runtime/session routing, cancellation and retry. Run `npm --prefix frontend test`, dedicated Vitest coverage and build, plus browser history download. PRD records results and limits. Implemented; see PRD for verification. - -- CON-4: Conversation Markdown hides `file-card` and `personal-drive-enable-card` elements including their children. Download controls use primary theme colors, 16px text, 44px minimum height, visible keyboard focus and wrapping filenames. Each download control occupies its own line with content-sized width. Surrounding message text is retained. Tested in sandboxDownload.test.tsx. - -- CON-5: The composer does not mount the separate `RuntimeArtifacts` entry for native or A2A MPA connections identified by existing Runtime metadata. Its list/preview hooks therefore do not run for those connections. General Runtime sessions retain their entry and existing no-session/Sandbox guards. MPA Markdown Sandbox file downloads remain unchanged; the Studio artifact store is not an adapter for Worker `/data/output`. See [visibility design](../../prd-spec/bugfixes/studio-mpa-artifact-entry/2026-09-30-hide-artifact-entry.md) and `frontend/tests/mpaSessionProtocol.test.mjs`. diff --git a/specs/studio-sandbox-download/README.zh.md b/specs/studio-sandbox-download/README.zh.md deleted file mode 100644 index b75922cd4..000000000 --- a/specs/studio-sandbox-download/README.zh.md +++ /dev/null @@ -1,21 +0,0 @@ -# Studio Sandbox 下载 - -[English](README.md) - -组件:studio-sandbox-download;修订:2026-09-30;状态:active。 -职责代码:frontend/src/ui/SandboxFileLink.tsx、Markdown.tsx、App.tsx、adk/client.ts。依赖现有 Studio Runtime 代理与 MPA 会话下载接口。关联 [PRD](../../prd-spec/features/studio-sandbox-download/2026-09-17-history-download.zh.md)。 - -## 契约 -- CON-1:仅有 appName/sessionId 上下文的助手会话 Markdown 转换 `/data/output/`、`/data/workspace/` 下的绝对链接。URL 转义只解码一次;拒绝非法转义、路径穿越段、反斜杠、控制字符、空文件名及 query/hash 后缀。支持中文和空格。其他 Markdown 消费方和链接保持现有行为。 -- CON-2:GET `/api/v1/sessions/{sessionId}/files/download?path={encodedPath}` 使用当前 app 解析出的端点及既有鉴权/身份传输。Runtime 凭据留在服务端;MPA 校验会话归属,Worker 执行文件系统边界校验。不查询 artifacts,不创建 Sandbox。 -- CON-3:点击阻止导航,抑制重复请求,展示本地化加载状态,以路径文件名保存 blob 并释放对象 URL。失败显示局部 alert,再次点击重试。切换上下文或卸载取消请求并忽略过期响应。沿用现有传输超时。 - -## 数据、兼容与诊断 -无持久化、配置或协议变更。历史及流式 Markdown 共用渲染。普通外链及视频保持既有行为。错误使用本地化通用提示,不显示服务器响应内容。Sandbox 过期或文件删除会使旧链接失效;浏览器缓冲完整文件。 - -## 验证与变更记录 -测试:frontend/tests/sandboxDownload.test.tsx、sandboxDownloadClient.test.ts。覆盖 CON-1/2/3,包括非法路径、当前 Runtime/会话路由、取消与重试。执行 `npm --prefix frontend test`、专用 Vitest 覆盖率、构建与浏览器历史下载。PRD 记录结果和边界。已实现,验证结果见 PRD。 - -- CON-4:会话 Markdown 隐藏 `file-card`、`personal-drive-enable-card` 元素及其内容。下载入口使用主题 primary 色、16px 字体、最小 44px 高度、可见键盘焦点和文件名换行。每个下载入口独占一行,宽度随内容适配。保留周围消息正文。由 sandboxDownload.test.tsx 验证。 - -- CON-5:Composer 对现有 Runtime 元数据识别的原生或 A2A MPA 连接,不挂载独立的 `RuntimeArtifacts` 入口,因此不运行其列表/预览 hook。普通 Runtime 会话保留入口及既有无会话/Sandbox 条件。MPA Markdown Sandbox 文件下载不变;Studio 产物存储不是 Worker `/data/output` 的适配。参见[显示规则设计](../../prd-spec/bugfixes/studio-mpa-artifact-entry/2026-09-30-hide-artifact-entry.zh.md)及 `frontend/tests/mpaSessionProtocol.test.mjs`。 diff --git a/specs/studio-skill-document/README.md b/specs/studio-skill-document/README.md deleted file mode 100644 index ef9f463a4..000000000 --- a/specs/studio-skill-document/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Studio Skill document editing - -[中文](README.zh.md) · [Design](../../prd-spec/features/mpa-agent-info-rail/2026-09-18-mpa-agent-info-rail.md) - -## Ownership and interface - -The existing Skill management identity resolver and version repository own authorization and publication. The MPA rail supplies exact region/space/skill IDs; the editor never chooses another space or changes a Runtime binding. - -GET `/web/skill-management/spaces/{space_id}/skills/{skill_id}/document?region=...` returns `{content, baseVersion, canUpdate}` for the latest visible version. PUT on that path accepts `{content, baseVersion}` and creates/publishes a new native version in the selected space using existing version-upload behavior. Content is limited to 262144 characters; baseVersion to 64 characters; neither may be empty. Ownership is checked server-side. Shared and review spaces remain read-only even for admins; other personal spaces follow existing owner/admin rules. - -## Integrity and concurrency - -Only the validated archive's root (or single wrapper's) SKILL.md is replaced. All other members, binary bytes and attributes are retained. Existing ZIP limits and frontmatter validation apply; changing the Skill name is rejected with 422. The editor receives escaped plain text, never executable document markup. - -Save validates baseVersion before reading the source archive and again inside the existing process-local upload lock. A stale version returns `SKILL_DOCUMENT_CONFLICT` (409) before cloud mutation. External writers can still race after the check because the provider has no compare-and-swap. This is not a distributed lock. Save failures/pending outcomes do not mean rollback; preserve edits and advise checking the current version before retrying. - -## UI lifecycle - -Loading/error/retry, read-only, unsaved-change confirmation, IME-safe Escape, keyboard focus trapping/restoration and disabled controls during save are explicit states. Duplicate saves are locked. Unmount aborts browser requests and ignores late results; it cannot roll back an already accepted server publication. Saving refreshes the bound list. Runtime cache refresh/new sessions may be needed before execution uses the new version. No automatic live edit is part of local verification. - -## Verification - -Backend document tests cover identity, read/write permissions, name preservation, binary/mode preservation, and version conflicts. Component tests cover read-only, read retry, save failure with retained draft, duplicate saves, unmount cancellation, and Escape/IME. See the bilingual design for dated results and browser limitations. diff --git a/specs/studio-skill-document/README.zh.md b/specs/studio-skill-document/README.zh.md deleted file mode 100644 index 479c61a1a..000000000 --- a/specs/studio-skill-document/README.zh.md +++ /dev/null @@ -1,23 +0,0 @@ -# Studio 技能正文编辑 - -[English](README.md) · [设计](../../prd-spec/features/mpa-agent-info-rail/2026-09-18-mpa-agent-info-rail.zh.md) - -## 职责与接口 - -复用技能管理身份解析器和版本仓库负责鉴权与发布。MPA 侧栏传入准确的地域/空间/技能 ID;编辑器不选择其他空间、不改变 Runtime 绑定。 - -GET `/web/skill-management/spaces/{space_id}/skills/{skill_id}/document?region=...` 返回最新可见版本的 `{content, baseVersion, canUpdate}`。同路径 PUT 接收 `{content, baseVersion}`,复用版本上传行为,在所选空间创建并发布原生新版本。content 最多 262144 字符,baseVersion 最多 64 字符,均不可为空。服务端检查归属。共享与审核空间即使管理员也保持只读;其他个人空间沿用所有者/管理员权限。 - -## 完整性与并发 - -仅替换已校验归档根目录或单层包装目录中的 SKILL.md,保留其他成员、二进制字节及属性。沿用 ZIP 限制与 frontmatter 校验;改变技能名称返回 422。编辑器展示转义纯文本,不执行文档中的标记。 - -保存前在读取原归档之前和既有进程内上传锁中分别检查 baseVersion。版本过期时在云写入前返回 `SKILL_DOCUMENT_CONFLICT`(409)。提供方没有原子比较交换,外部写入仍可能在校验后发生竞态;这不是分布式锁。保存失败/结果待定不代表回滚,应保留编辑并提示重试前核对当前版本。 - -## 界面生命周期 - -明确支持加载/错误/重试、只读、未保存修改确认、兼容 IME 的 Escape、焦点圈定与恢复,以及保存中禁用控件。阻止重复保存。卸载取消浏览器请求并忽略迟到响应,但无法回滚服务端已接受的发布。保存后刷新绑定列表;Runtime 执行新版本可能需要缓存刷新或新会话。本地验证不会自动修改真实服务。 - -## 验证 - -后端测试覆盖身份、读写权限、名称保留、二进制/权限位保留及版本冲突。组件测试覆盖只读、读取重试、保存失败保留草稿、重复保存、卸载取消、Escape/IME。日期结果和浏览器限制见双语设计。 diff --git a/specs/studio-tool-activity/README.md b/specs/studio-tool-activity/README.md deleted file mode 100644 index 181f6c21c..000000000 --- a/specs/studio-tool-activity/README.md +++ /dev/null @@ -1,124 +0,0 @@ -# Studio Tool Activity Component Contract - -- **Component ID:** `studio-tool-activity` -- **Status:** active -- **Revision date:** 2026-09-22 -- **Chinese counterpart:** [README.zh.md](README.zh.md) -- **Related PRDs:** [Studio Tool Activity Visualization Design](../../prd-spec/features/studio-tool-activity-visualization/2026-09-12-studio-tool-activity-visualization-design.md) and [Follow-up preservation](../../prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-followup-content.md) -- **Owned implementation:** `frontend/src/blocks.ts`, `frontend/src/ui/Blocks.tsx`, and `frontend/src/ui/tool-activity/` -- **Primary tests:** `frontend/tests/toolActivityModel.test.mjs`, `frontend/tests/toolBlockDefaultOpen.test.mjs`, and `frontend/tests/codexSandboxProgress.test.mjs` - -## 1. Responsibility - -The component turns ADK function calls, function responses, and Codex sandbox progress into bounded, accessible activity units in the Studio transcript. It owns presentation normalization, lifecycle correlation, safe read-only grouping, adaptive disclosure, output previews, and redacted raw-data inspection. - -It does not own event transport, Runtime execution, session persistence, backend redaction, authorization flows, or final-answer generation. Existing specialized tool renderers remain authoritative for their own domain content. - -## 2. Entry Points and Dependencies - -- `applyEvent()` and `eventsToTurns()` in `frontend/src/blocks.ts` own live and history event projection. -- `flattenCodexActivityBlocks()` derives a display-only flat view from nested Codex activity without mutating source blocks. -- `presentToolActivity()` converts a tool block into `ToolPresentation`. -- `ToolActivityCard` renders one ordinary activity. -- `ToolExplorationGroup` renders an eligible sequence of successful read-only activities. -- `Blocks` dispatches ordinary tools through this component and retains existing specialized renderers. - -The component depends on the existing ADK event types, i18n `conversation` namespace, Studio semantic CSS variables, and repository-owned SVG icons. It introduces no runtime dependency and no server contract. - -## 3. Contracts - -### `CON-1` — Source-preserving presentation - -Presentation is derived from source `Block` values. Grouping and sandbox flattening must not mutate source block order or data. Raw `args` and `response` remain available only through bounded, redacted secondary disclosure. - -### `CON-2` — Stable lifecycle correlation - -Events with the same non-empty `callId` represent one logical activity, including known command aliases such as `exec_command` and `commandExecution`. A partial function response updates output and keeps the activity running; a terminal response completes or fails it. Same-name concurrent calls must never merge without matching IDs. - -Duplicate event IDs are ignored within one projector instance. Tracking is bounded to 2,048 IDs. A result that arrives before its call remains visible and merges with a later matching call. - -### `CON-3` — Status and disclosure state - -Valid activity states are `queued`, `running`, `completed`, and `failed`. Running items default open, completed items default closed, and failed items default open. Once a user toggles an item, later lifecycle updates must not override that choice. Disclosure state is local UI state and is never persisted. - -### `CON-4` — Typed presentation with generic fallback - -The maintained categories are `command`, `read`, `search`, `file-change`, `mcp`, `authorization`, and `generic`. Unknown or malformed tools must remain visible through `generic`; a presenter failure must not break the transcript. Existing registered detail renderers keep their specialized content while ordinary built-ins keep their localized action titles. - -### `CON-5` — Sandbox flattening - -When `delegate_to_codex_sandbox` has structured child activity, its child blocks render at the current transcript level under one lightweight `Codex Sandbox` source marker. The outer tool card is retained while no child activity exists. The final assistant answer remains outside the tool activity sequence. - - -### `CON-6` — Safe exploration grouping - -Only adjacent, completed, successful `read` and `search` activities with the same source may form an exploration group. Commands, file changes, MCP, authorization, failures, running activity, source changes, non-tool blocks, and final text terminate the group. Children remain ordered and individually inspectable. - -### `CON-7` — Bounded output - -Visible text is stripped of ANSI CSI/OSC sequences, unsafe control characters, and credential-like values. Multiline output previews retain five leading and five trailing lines with an omission count. A single oversized line retains at most 8,000 leading and 8,000 trailing characters with an omitted-character count. Detail regions scroll independently and do not create body-level horizontal overflow. - -### `CON-8` — Raw-data safety - -Raw data is closed by default and formatted only after disclosure. Sensitive keys and credential patterns embedded in strings are masked before display or clipboard copy. Traversal is bounded to depth 8, 50 items per collection, 500 total nodes, and 4,000 characters per string. Circular references produce `[circular]`; exceeded bounds produce `[truncated]`. Clipboard failure is visible and does not throw into the transcript. - -### `CON-9` — History compatibility - -`eventsToTurns()` remains the history reconstruction authority. Live and replayed events use the same `applyEvent()` lifecycle logic. Persisted `functionCall` plus `functionResponse` must be sufficient to reconstruct activity, including camelCase and snake_case payloads. Missing progress degrades to a generic activity. - -An A2A virtual session that persists only final text cannot reconstruct transient tool activity; Studio must preserve the final answer and must not fabricate missing history. This is a transport persistence limitation, not permission to submit the task again. - -The MPA `sandbox_task` response with `finalAlreadyEmitted: true` closes the parent activity without advancing the live-preview boundary. A later sandbox consolidated final replaces that preview; transport finish without a final retains it. This rule is independent of the `delegate_to_codex_sandbox` activity format. - -For MPA through A2A, the backend projects the sandbox final and suppresses subsequent outer answer mirrors under [Runtime diagnostics CON-8](../studio-runtime-diagnostics/README.md). The native-session wrapper rule above does not perform that A2A normalization. - -### `CON-10` — Accessibility and responsive behavior - -Interactive disclosure uses native buttons, `aria-expanded`, visible focus, textual status, and at least a 44px effective target. Collapsed content is `aria-hidden` and inert. Secondary summaries hide before titles or state at narrow widths. Motion uses bounded transitions and is disabled under `prefers-reduced-motion`. - -## 4. State and Concurrency - -```text -queued -> running -> completed - -> failed -``` - -Output deltas may repeat while running. A terminal event owns final status and authoritative output. Concurrent activities are isolated by `callId`; projector state is isolated per active assistant turn and session. Session switching reconstructs or selects that session's own turns and must not share component-local disclosure state. - - -## 5. Security and Isolation - -Tool payloads are untrusted display data. The component does not use raw HTML injection. It preserves backend allowlists and applies an additional value-aware mask before rendering or copying. It must not expose Authorization values, cookies, passwords, access keys, secret keys, tokens, signed query credentials, or unrestricted environment payloads. - -## 6. Compatibility and Evolution - -- Both camelCase and snake_case event fields remain supported by the owning ADK types and projection. -- New tool categories must keep generic fallback and must add bilingual labels and presenter tests. -- A new merge alias must be proven unambiguous through a lifecycle test. -- Changing persistence or adding resumable tool history requires a separate transport/session contract change; it is outside this component. - -## 7. Failures and Diagnostics - -- Malformed values fall back to readable generic content and bounded raw data. -- Unmatched responses remain visible rather than being discarded. -- Clipboard failures render localized feedback. -- The component emits no independent network request, retry, metric, or log. Existing Studio stream and browser diagnostics remain the observability surfaces. - -## 8. Verification - -| Contract | Verification | Evidence | -| --- | --- | --- | -| `CON-1`–`CON-9` | `npm --prefix frontend test` | Model, lifecycle, Codex, history, and existing transcript regression suites | -| `CON-10` | Component DOM tests plus real browser checks at normal and narrow widths | Activity disclosure/status DOM and screenshots | -| Build assets | `npm --prefix frontend run build` and `npm --prefix frontend run test:webui-assets` | Generated `veadk/webui` references are complete | -| Bilingual labels | `npm --prefix frontend run check:i18n` | Locale namespaces are equivalent | - -## 9. Change Record - -- **2026-09-12:** Contract introduced by the approved Studio Tool Activity Visualization PRD. Initial implementation covers unified tool cards, command lifecycle correlation, sandbox flattening, read-only grouping, bounded output, raw-data redaction, and history-safe fallbacks. -- **2026-09-12:** Non-final A2A `submitted` and message-less `working` updates are forwarded as metadata-only heartbeats. They clear the first-event deadline without creating transcript blocks, completing turns, or resubmitting requests. -- **2026-09-12:** A2A text parts marked `adk_thought` are projected as Studio thinking events. Reasoning and answer cumulative-delta state is isolated so one stream cannot suppress or corrupt the other. -- **2026-09-12:** When an A2A stream closes after answer deltas without explicit final text, the bridge emits the accumulated answer once as a completed Studio event. Reasoning-only streams remain incomplete. -- **2026-09-12:** Parts appended to the same A2A artifact receive distinct projection event IDs; Studio deduplication must not discard later reasoning parts or the final answer. - -- **2026-09-22 correction:** General Codex tool activity retains commentary, reasoning and subsequent outer text under its original contract. No substring or final-ownership filter applies. MPA A2A exact answer deduplication belongs to Runtime diagnostics CON-8/CON-11; distinct text and non-text activity remain visible. diff --git a/specs/studio-tool-activity/README.zh.md b/specs/studio-tool-activity/README.zh.md deleted file mode 100644 index 6dcc58b01..000000000 --- a/specs/studio-tool-activity/README.zh.md +++ /dev/null @@ -1,124 +0,0 @@ -# Studio 工具活动组件契约 - -- **组件 ID:** `studio-tool-activity` -- **状态:** active -- **修订日期:** 2026-09-22 -- **英文版本:** [README.md](README.md) -- **关联 PRD:** [Studio 工具活动可视化设计](../../prd-spec/features/studio-tool-activity-visualization/2026-09-12-studio-tool-activity-visualization-design.zh.md)和 [保留补充内容](../../prd-spec/bugfixes/studio-codex-commentary-dedup/2026-09-22-preserve-followup-content.zh.md) -- **负责实现:** `frontend/src/blocks.ts`、`frontend/src/ui/Blocks.tsx` 和 `frontend/src/ui/tool-activity/` -- **主要测试:** `frontend/tests/toolActivityModel.test.mjs`、`frontend/tests/toolBlockDefaultOpen.test.mjs` 和 `frontend/tests/codexSandboxProgress.test.mjs` - -## 1. 责任 - -本组件将 ADK function call、function response 和 Codex sandbox progress 转换成 Studio transcript 中有界、可访问的工具活动单元。它负责展示归一化、生命周期关联、安全只读聚合、自适应展开、输出预览和脱敏后的原始数据查看。 - -它不负责事件传输、Runtime 执行、session 持久化、后端脱敏、授权流程或最终回答生成。已有专用工具 renderer 继续负责自身领域内容。 - -## 2. 入口与依赖 - -- `frontend/src/blocks.ts` 中的 `applyEvent()` 和 `eventsToTurns()` 负责实时与历史事件投影。 -- `flattenCodexActivityBlocks()` 从嵌套 Codex activity 派生只用于展示的扁平视图,不修改源 blocks。 -- `presentToolActivity()` 将工具 block 转换为 `ToolPresentation`。 -- `ToolActivityCard` 渲染一个普通活动。 -- `ToolExplorationGroup` 渲染一组符合条件的成功只读活动。 -- `Blocks` 将普通工具分发给本组件,并保留已有专用 renderer。 - -组件依赖现有 ADK 事件类型、i18n `conversation` namespace、Studio 语义 CSS 变量和仓库自有 SVG 图标;不新增运行时依赖或服务端契约。 - -## 3. 契约 - -### `CON-1` — 保留源数据的展示派生 - -展示由源 `Block` 派生。聚合和沙箱扁平化不得修改源 block 的顺序或数据。原始 `args` 和 `response` 只能通过有界、脱敏的二级入口访问。 - -### `CON-2` — 稳定生命周期关联 - -相同非空 `callId` 的事件表示一个逻辑活动,包括 `exec_command` 与 `commandExecution` 等已知命令别名。partial function response 只更新输出并保持 running;terminal response 才完成或标记失败。同名并发调用没有匹配 ID 时禁止合并。 - -一个 projector 内重复 event ID 必须忽略,跟踪上限为 2,048 个 ID。先于 call 到达的 result 必须保持可见,并在后续匹配 call 到达时合并。 - -### `CON-3` — 状态与展开状态 - -合法状态为 `queued`、`running`、`completed`、`failed`。running 默认展开,completed 默认折叠,failed 默认展开。用户手动切换后,后续生命周期更新不得覆盖用户选择。展开状态只属于本地 UI,不持久化。 - -### `CON-4` — 类型化展示与通用回退 - -长期支持的类别为 `command`、`read`、`search`、`file-change`、`mcp`、`authorization`、`generic`。未知或异常工具必须通过 `generic` 保持可见,presenter 异常不得破坏 transcript。已有注册详情 renderer 继续使用专用内容,普通内置工具保留已有本地化动作标题。 - -### `CON-5` — Sandbox 扁平化 - -当 `delegate_to_codex_sandbox` 存在结构化子活动时,子 block 在当前 transcript 层级展示,并使用一个轻量 `Codex Sandbox` 来源标记。没有子活动时保留外层工具卡。最终回答保持在工具活动序列之外。 - - -### `CON-6` — 安全探索聚合 - -只有同来源、相邻、已完成且成功的 `read` 和 `search` 活动可形成探索分组。命令、文件变更、MCP、授权、失败、运行中活动、来源变化、非工具 block 和最终文本都会终止分组。子项保持原顺序并可分别查看。 - -### `CON-7` — 有界输出 - -可见文本必须移除 ANSI CSI/OSC、危险控制字符和疑似凭证值。多行输出预览保留前 5 行和后 5 行,并显示省略行数。单行超长输出最多保留前 8,000 和后 8,000 个字符,并显示省略字符数。详情区域独立滚动,不得造成页面级横向溢出。 - -### `CON-8` — 原始数据安全 - -原始数据默认关闭,只在展开后格式化。显示或复制前必须遮罩敏感字段名和字符串内的凭证模式。遍历上限为深度 8、每个集合 50 项、总节点 500 个、单字符串 4,000 字符。循环引用显示 `[circular]`,超过上限显示 `[truncated]`。剪贴板失败必须可见,且不得使 transcript 抛错。 - -### `CON-9` — 历史兼容 - -`eventsToTurns()` 继续作为历史重建权威入口。实时与历史事件共享 `applyEvent()` 生命周期逻辑。仅持久化 `functionCall + functionResponse` 时也必须能恢复活动,并继续兼容 camelCase 与 snake_case;缺少 progress 时降级为通用活动。 - -若 A2A 虚拟 session 只持久化最终文本,则无法恢复临时工具过程;Studio 必须保留最终回答且禁止伪造缺失历史。这是传输持久化限制,不代表允许重新提交任务。 - -携带 `finalAlreadyEmitted: true` 的 MPA `sandbox_task` 响应关闭父活动,但不推进实时预览边界。后到的 sandbox 合并最终事件替换预览;仅传输结束时保留预览。这一规则独立于 `delegate_to_codex_sandbox` 活动格式。 - -MPA 通过 A2A 接入时,由后端按 [Runtime 诊断 CON-8](../studio-runtime-diagnostics/README.zh.md) 投影 sandbox final 并抑制后续外层答案副本。上述原生会话包装规则不负责 A2A 规范化。 - -### `CON-10` — 无障碍与响应式 - -交互式折叠使用原生 button、`aria-expanded`、清晰焦点、文字状态和至少 44px 有效点击区。折叠内容必须 `aria-hidden` 且 inert。窄屏优先隐藏次要摘要,不隐藏标题或状态。动效必须有界,并在 `prefers-reduced-motion` 下关闭。 - -## 4. 状态与并发 - -```text -queued -> running -> completed - -> failed -``` - -running 状态允许重复输出 delta。terminal 事件拥有最终状态和权威输出。并发活动按 `callId` 隔离;projector 状态按 active assistant turn 和 session 隔离。session 切换只能重建或选择该 session 自己的 turns,不得共享组件本地展开状态。 - - -## 5. 安全与隔离 - -工具 payload 是不可信展示数据。组件不得使用原始 HTML 注入。它必须延续后端 allowlist,并在渲染和复制前额外进行基于值的遮罩。不得暴露 Authorization、cookie、password、access key、secret key、token、签名查询凭证或不受限制的环境变量 payload。 - -## 6. 兼容与演进 - -- 继续支持 camelCase 与 snake_case 事件字段。 -- 新工具类别必须保留 generic fallback,并补中英文标签和 presenter 测试。 -- 新增合并别名必须通过生命周期测试证明无歧义。 -- 改变持久化或增加可续传工具历史属于独立的 transport/session 契约变更,不属于本组件。 - -## 7. 失败与诊断 - -- 异常值回退为可读通用内容和有界原始数据。 -- 无法匹配的 response 保持可见,不得丢弃。 -- 剪贴板失败显示本地化反馈。 -- 本组件不独立发起网络请求、重试、指标或日志;沿用现有 Studio stream 和浏览器诊断面。 - -## 8. 验证 - -| 契约 | 验证 | 证据 | -| --- | --- | --- | -| `CON-1`–`CON-9` | `npm --prefix frontend test` | 模型、生命周期、Codex、历史和现有 transcript 回归套件 | -| `CON-10` | 组件 DOM 测试及正常/窄屏真实浏览器检查 | 活动折叠/状态 DOM 与截图 | -| 构建资产 | `npm --prefix frontend run build` 和 `npm --prefix frontend run test:webui-assets` | 生成的 `veadk/webui` 引用完整 | -| 双语标签 | `npm --prefix frontend run check:i18n` | locale namespace 等价 | - -## 9. 变更记录 - -- **2026-09-12:** 由已批准的 Studio 工具活动可视化 PRD 首次建立契约。初始实现覆盖统一工具卡、命令生命周期关联、沙箱扁平化、只读聚合、有界输出、原始数据脱敏和历史安全回退。 -- **2026-09-12:** 非终态 A2A `submitted` 和不含消息的 `working` 更新会作为仅含 metadata 的 heartbeat 转发,用于解除首事件截止时间,但不创建 transcript block、不结束 turn,也不重新提交请求。 -- **2026-09-12:** 标记为 `adk_thought` 的 A2A 文本 part 投影为 Studio thinking 事件;reasoning 与答案的累计 delta 状态相互隔离,避免一条流抑制或污染另一条流。 -- **2026-09-12:** A2A 流在已有答案增量但没有显式最终文本时结束,桥接会将累计答案作为一个完成态 Studio 事件输出一次;纯 reasoning 流仍保持未完成。 -- **2026-09-12:** 追加到同一 A2A artifact 的各 part 使用不同的投影 event ID;Studio 去重不得丢弃后续 reasoning part 或最终答案。 - -- **2026-09-22 修正:** 通用 Codex 工具活动按原有契约保留 commentary、推理与后续外层文本,不应用子串或最终答案所有权过滤。MPA A2A 完全相同答案的去重归属 Runtime diagnostics CON-8/CON-11;不同文本与非文本活动保持可见。 diff --git a/specs/tracing-cache/README.md b/specs/tracing-cache/README.md deleted file mode 100644 index e8f170be2..000000000 --- a/specs/tracing-cache/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Local tracing cache - -[中文版](README.zh.md) - -Component ID: `tracing-cache` · Status: active · Revision: 2026-10-13 - -## Responsibility and interfaces - -Owns bounded diagnostic span retention in `veadk/tracing/telemetry/exporters/inmemory_exporter.py`, wired by `OpentelemetryTracer`. Cloud transport, SDK request instrumentation, and Studio storage are separate responsibilities. Consumers are `OpentelemetryTracer.dump()` and `trace_id`; constructor signatures remain compatible. - -## Contracts - -- CON-1: One cache per provider, shared by all VeADK tracers. Default limits are 1024 spans and 16777216 estimated bytes. Oldest entries are evicted first. Entries larger than the byte budget are not cached. Other exporters receive unchanged spans. -- CON-2: `OpenTelemetryConfig.inmemory_enabled` defaults to true. `inmemory_max_spans` and `inmemory_max_bytes` are nonnegative integers; either zero disables storage. Environment names are `OBSERVABILITY_OPENTELEMETRY_INMEMORY_ENABLED`, `OBSERVABILITY_OPENTELEMETRY_INMEMORY_MAX_SPANS`, and `OBSERVABILITY_OPENTELEMETRY_INMEMORY_MAX_BYTES`. Use existing startup settings and config.yaml-to-environment handling. Invalid values fail configuration validation. First collector creation captures configuration; changes require a new provider/process. -- CON-3: Disabled storage preserves the annotation processor, instrumentation suppression, latest trace ID, and existing cloud export/flush behavior. No retained span or session index remains. `dump()` writes an empty array when no matching history is retained. -- CON-4: The session index is derived from retained `call_llm` spans. Session dumps return retained spans with matching trace IDs in export order, including related spans without session attributes. Evicted session anchors can make a partial trace unavailable to session lookup. `clear()` resets spans, accounting, index, and latest trace ID. `shutdown()` clears data and permanently stops retention. Flush does not clear data. -- CON-5: Export/read/clear/shutdown are serialized by a cache lock; provider registration has its own lock. Reads return snapshots. There are no timers or new background tasks. Retention is process-local and not persistent. Limits cover estimated known span data, not exact RSS, allocator fragmentation, or other components' memory. - -Example: set `OBSERVABILITY_OPENTELEMETRY_INMEMORY_ENABLED=false` before startup to keep cloud telemetry while disabling local history. A bounded enabled cache retains recent diagnostics; use cloud storage for complete history. - -## Security, failures, and compatibility - -The cache introduces no credentials, permissions, external writes, or network operations. Existing content collection/redaction settings remain owned by instrumentation. Eviction changes only diagnostic history, not user sessions or cloud telemetry. Oversized records are skipped locally without logging their content. Finite history and provider-wide sharing replace previous unlimited per-tracer retention. No additional packages or unsupported Python features are required. - -## Verification and change record - -CON-1/4/5 map to `tests/test_inmemory_exporter.py`; CON-2 to `tests/config/test_tracing_config.py`; CON-1/3/5 integration to `tests/test_tracing.py`. Run these using `uv run --no-sync --extra dev pytest -q`, followed by focused tracing compatibility checks and Ruff. Production RSS requires separate workload evidence. - -Design and verification: [tracing-memory](../../prd-spec/bugfixes/tracing-memory/2026-10-13-bounded-cache.md). Contracts above are implemented and covered by the focused regression checks recorded in the linked design; package publication is separate. diff --git a/specs/tracing-cache/README.zh.md b/specs/tracing-cache/README.zh.md deleted file mode 100644 index 0f6e2a81a..000000000 --- a/specs/tracing-cache/README.zh.md +++ /dev/null @@ -1,29 +0,0 @@ -# 本地追踪缓存 - -[English](README.md) - -组件 ID:`tracing-cache` · 状态:active · 修订日期:2026-10-13 - -## 职责与接口 - -负责 `veadk/tracing/telemetry/exporters/inmemory_exporter.py` 中有界的诊断 span 保存,由 `OpentelemetryTracer` 接入。云端传输、SDK 请求埋点和 Studio 存储属于其他组件。使用方为 `OpentelemetryTracer.dump()` 和 `trace_id`,构造签名保持兼容 - -## 契约 - -- CON-1:每个 provider 只有一个缓存,由所有 VeADK 追踪器共用。默认上限为 1024 个 span 和 16777216 估算字节,优先淘汰最旧记录,超过字节预算的单条记录不缓存。其他 exporter 接收未经本缓存修改的 span -- CON-2:`OpenTelemetryConfig.inmemory_enabled` 默认 true。`inmemory_max_spans` 和 `inmemory_max_bytes` 为非负整数,任一为零即关闭保存。环境变量为 `OBSERVABILITY_OPENTELEMETRY_INMEMORY_ENABLED`、`OBSERVABILITY_OPENTELEMETRY_INMEMORY_MAX_SPANS`、`OBSERVABILITY_OPENTELEMETRY_INMEMORY_MAX_BYTES`。使用现有启动 settings 及 config.yaml 到环境变量的处理方式,非法值导致配置校验失败。首次创建收集器时确定配置,修改后需新建 provider 或重启进程 -- CON-3:关闭保存时,保留字段补充处理器、埋点抑制、最新 trace ID 和现有云端导出及 flush 行为,不保留 span 或会话索引。没有对应历史时,`dump()` 写出空数组 -- CON-4:会话索引从仍被保留的 `call_llm` span 生成。会话导出按导出顺序返回 trace ID 匹配的保留 span,包括自身没有会话属性的关联 span。会话关联节点被淘汰后,部分 trace 可能无法再通过会话查询。`clear()` 重置 span、大小记录、索引和最新 trace ID。`shutdown()` 清空数据并永久停止保存,flush 不清空数据 -- CON-5:导出、读取、清空、关闭通过缓存锁串行处理,provider 注册使用独立锁。读取返回快照,无定时器或新增后台任务。缓存仅存在当前进程内,不持久化。预算覆盖已知 span 数据的估算量,不代表精确 RSS,不包含分配器碎片或其他组件内存 - -例如:启动前设置 `OBSERVABILITY_OPENTELEMETRY_INMEMORY_ENABLED=false`,保留云端观测并关闭本地历史。有界缓存用于保存近期诊断,完整历史应使用云端存储 - -## 安全、失败与兼容性 - -缓存不引入凭据、权限、外部写入或网络操作,现有内容采集及脱敏设置仍由埋点层负责。淘汰仅影响诊断历史,不影响用户会话或云端观测。超大记录只在本地跳过,不打印其内容。有限历史及 provider 内共享替代此前每个追踪器无限保存的行为。不增加依赖或不兼容的 Python 语法 - -## 验证与变更记录 - -CON-1/4/5 对应 `tests/test_inmemory_exporter.py`;CON-2 对应 `tests/config/test_tracing_config.py`;CON-1/3/5 集成对应 `tests/test_tracing.py`。使用 `uv run --no-sync --extra dev pytest -q` 运行,再进行针对性追踪兼容检查及 Ruff。生产 RSS 需要另行提供负载证据 - -设计与验证:[tracing-memory](../../prd-spec/bugfixes/tracing-memory/2026-10-13-bounded-cache.zh.md)。上述契约已实现,并通过所链接设计记录中的针对性回归检查;尚未发布软件包 diff --git a/tests/integrations/agentkit/test_app.py b/tests/integrations/agentkit/test_app.py index 570b37b86..9a6866e57 100644 --- a/tests/integrations/agentkit/test_app.py +++ b/tests/integrations/agentkit/test_app.py @@ -644,3 +644,31 @@ def test_run_agentkit_app_uses_explicit_and_environment_configuration( def test_run_agentkit_app_rejects_unmanaged_app() -> None: with pytest.raises(ValueError, match="create_agentkit_app"): agentkit_app.run_agentkit_app(FastAPI()) + + +@pytest.mark.asyncio +async def test_lifespan_shuts_the_codex_runtime_down_only_when_loaded( + monkeypatch, +) -> None: + import sys + + calls: list[str] = [] + + async def shutdown_codex_runtime() -> None: + calls.append("shutdown") + + app = FastAPI() + agentkit_app._configure_codex_runtime_shutdown(app) + monkeypatch.delitem(sys.modules, "veadk.runtime.codex.runtime", raising=False) + async with app.router.lifespan_context(app): + pass + assert calls == [], "the Codex runtime was not loaded" + + monkeypatch.setitem( + sys.modules, + "veadk.runtime.codex.runtime", + SimpleNamespace(shutdown_codex_runtime=shutdown_codex_runtime), + ) + async with app.router.lifespan_context(app): + pass + assert calls == ["shutdown"] diff --git a/tests/runtime/codex/test_codex_crash_recovery_smoke.py b/tests/runtime/codex/test_codex_crash_recovery_smoke.py new file mode 100644 index 000000000..d3ef499e8 --- /dev/null +++ b/tests/runtime/codex/test_codex_crash_recovery_smoke.py @@ -0,0 +1,324 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""A server killed mid-turn loses no conversation and repeats no tool. + +Three VeADK processes serve one session stored in SQLite, each running the +real Codex binary against a stubbed Responses backend (no model is called): + +1. Process A runs turn 1 to completion; its rollout is saved. +2. Process B runs turn 2: the agent books a table (once), then calls a tool + that blocks, and B is killed (``SIGKILL``) inside it, so B never saves. +3. Process C runs turn 3 and must resume turn 1's Codex thread, be handed turn + 2 (message and tool result) back from the session transcript, and not run + the tool again. + +Run it with:: + + CODEX_RUN_SMOKE=1 uv run --extra dev pytest -m codex_smoke -p no:xdist \\ + tests/runtime/codex/test_codex_crash_recovery_smoke.py -rs +""" + +from __future__ import annotations + +import json +import os +import signal +import sqlite3 +import subprocess +import sys +import threading +import time +import uuid +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from typing import Any + +import pytest + +pytest.importorskip("openai_codex") + +_TOOL_RESULT = "BOOKED-42" + +# One invocation per process: argv = db, backend url, session id, message, +# bookings file. Prints the session's stored Codex thread afterwards. +_CHILD = r''' +import asyncio, json, sys + +from google.adk.runners import Runner +from google.adk.sessions import DatabaseSessionService +from google.genai import types + +from veadk import Agent +from veadk.runtime.codex.config import CodexRuntimeConfig +from veadk.runtime.codex.thread_store import ThreadKey, select_thread_store + +db, url, session_id, text, bookings = sys.argv[1:6] + + +def book_table(guests: int) -> dict: + """Book a restaurant table.""" + with open(bookings, "a", encoding="utf-8") as f: + f.write(f"booked {guests}\n") + return {"confirmation": "BOOKED-42"} + + +async def wait_for_kitchen() -> dict: + """Wait until the kitchen confirms.""" + await asyncio.sleep(300) + return {"kitchen": "ready"} + + +async def main() -> None: + service = DatabaseSessionService(db_url=f"sqlite+aiosqlite:///{db}") + if await service.get_session( + app_name="crash", user_id="user", session_id=session_id + ) is None: + await service.create_session( + app_name="crash", user_id="user", session_id=session_id + ) + agent = Agent( + name="crash_agent", + description="Crash recovery probe.", + instruction="Help the user.", + runtime="codex", + model_name="veadk-codex-probe-model", + model_api_base=url, + model_api_key="probe-key", + model_api_key_name="", + tools=[book_table, wait_for_kitchen], + codex_runtime_config=CodexRuntimeConfig( + model_transport="direct", reasoning_effort="minimal" + ), + ) + runner = Runner(app_name="crash", agent=agent, session_service=service) + async for _ in runner.run_async( + user_id="user", + session_id=session_id, + new_message=types.Content(role="user", parts=[types.Part(text=text)]), + ): + pass + record = await select_thread_store(service).load( + ThreadKey("crash", "user", session_id, "crash_agent") + ) + print(json.dumps({"thread_id": record.thread_id if record else None})) + + +asyncio.run(asyncio.wait_for(main(), 120)) +''' + + +class _Backend: + """Responses stub. ``mode`` is ``"answer"`` or ``"book_then_wait"``.""" + + def __init__(self) -> None: + self.mode = "answer" + self.bodies: list[str] = [] + self.tool_calls = 0 + self.waiting = threading.Event() + backend = self + + class Handler(BaseHTTPRequestHandler): + protocol_version = "HTTP/1.1" + + def log_message(self, format: str, *args: Any) -> None: + return None + + def do_GET(self) -> None: + self._send("application/json", b'{"data":[],"models":[]}') + + def do_POST(self) -> None: + raw = self.rfile.read(int(self.headers.get("content-length", 0))) + text = raw.decode("utf-8", "replace") + backend.bodies.append(text) + body = json.loads(raw or b"{}") + outputs = { + item.get("call_id") + for item in body.get("input", []) + if isinstance(item, dict) + and item.get("type") == "function_call_output" + } + call: tuple[str, str, dict[str, Any]] | None = None + if backend.mode == "book_then_wait" and "call_book" not in outputs: + backend.tool_calls += 1 + call = ("call_book", "book_table", {"guests": 2}) + elif backend.mode == "book_then_wait" and "call_wait" not in outputs: + # The booking is done; the process dies in the next tool. + backend.waiting.set() + call = ("call_wait", "wait_for_kitchen", {}) + if call is not None: + item: dict[str, Any] = { + "type": "function_call", + "id": f"fc_{call[0]}", + "call_id": call[0], + "name": call[1], + "arguments": json.dumps(call[2]), + "status": "completed", + } + namespace = _namespace_of(call[1], body.get("tools")) + if namespace: + item["namespace"] = namespace + else: + item = { + "type": "message", + "id": "msg", + "role": "assistant", + "status": "completed", + "content": [ + {"type": "output_text", "text": "ok", "annotations": []} + ], + } + usage = { + "input_tokens": 1, + "input_tokens_details": {"cached_tokens": 0}, + "output_tokens": 1, + "output_tokens_details": {"reasoning_tokens": 0}, + "total_tokens": 2, + } + events = ( + {"type": "response.created", "response": {"id": "resp"}}, + { + "type": "response.output_item.done", + "output_index": 0, + "item": item, + }, + { + "type": "response.completed", + "response": {"id": "resp", "usage": usage}, + }, + ) + self._send( + "text/event-stream", + b"".join( + f"event: {e['type']}\ndata: {json.dumps(e)}\n\n".encode() + for e in events + ), + ) + + def _send(self, content_type: str, data: bytes) -> None: + self.send_response(200) + self.send_header("content-type", content_type) + self.send_header("content-length", str(len(data))) + self.end_headers() + self.wfile.write(data) + + self.server = ThreadingHTTPServer(("127.0.0.1", 0), Handler) + self.server.daemon_threads = True + threading.Thread(target=self.server.serve_forever, daemon=True).start() + self.url = f"http://127.0.0.1:{self.server.server_address[1]}/v1" + + +def _namespace_of(name: str, tools: Any) -> str: + """The namespace Codex advertises an MCP tool in (``mcp__``).""" + for tool in tools or []: + if isinstance(tool, dict) and tool.get("type") == "namespace": + for inner in tool.get("tools") or []: + if isinstance(inner, dict) and inner.get("name") == name: + return str(tool.get("name")) + return "" + + +def _db_contains(db: Path, needle: str) -> bool: + """Whether any row of the session database mentions ``needle``.""" + if not db.exists(): + return False + with sqlite3.connect(db, timeout=5) as conn: + tables = [ + row[0] + for row in conn.execute("SELECT name FROM sqlite_master WHERE type='table'") + ] + return any( + needle in str(row) + for table in tables + for row in conn.execute(f'SELECT * FROM "{table}"') # noqa: S608 + ) + + +@pytest.mark.codex_smoke +def test_a_turn_killed_mid_way_resumes_without_repeating_its_tool(tmp_path) -> None: + if os.getenv("CODEX_RUN_SMOKE") != "1": + pytest.skip( + "set CODEX_RUN_SMOKE=1 to spawn the real Codex binary " + "(no model is called; the backend is stubbed)" + ) + if not hasattr(signal, "SIGKILL"): + pytest.skip("needs SIGKILL") + db, bookings = tmp_path / "sessions.db", tmp_path / "bookings.txt" + child = tmp_path / "child.py" + child.write_text(_CHILD, encoding="utf-8") + session_id = f"crash-{uuid.uuid4().hex[:8]}" + backend = _Backend() + + def command(text: str) -> list[str]: + return [ + sys.executable, + str(child), + str(db), + backend.url, + session_id, + text, + str(bookings), + ] + + def run(text: str) -> dict[str, Any]: + done = subprocess.run( + command(text), capture_output=True, text=True, timeout=150 + ) + assert done.returncode == 0, done.stderr[-3000:] + return json.loads(done.stdout.strip().splitlines()[-1]) + + try: + # 1. Process A: turn 1 completes and saves its rollout. + first = run("remember the word PAPAYA") + assert first["thread_id"], "turn 1 saved no Codex thread" + + # 2. Process B: the tool runs, then B is killed before it can save. + backend.mode = "book_then_wait" + victim = subprocess.Popen( + command("book a table for two"), + stdout=subprocess.DEVNULL, + stderr=subprocess.PIPE, + text=True, + ) + try: + assert backend.waiting.wait(120), "turn 2 never finished booking" + deadline = time.monotonic() + 20 + while not _db_contains(db, _TOOL_RESULT): + assert time.monotonic() < deadline, "tool result never persisted" + time.sleep(0.2) + victim.send_signal(signal.SIGKILL) + victim.wait(30) + finally: + if victim.poll() is None: + victim.kill() + assert victim.returncode == -signal.SIGKILL + + # 3. Process C: a new process continues the session. + backend.mode = "answer" + resumed_from = len(backend.bodies) + third = run("which booking did you make?") + finally: + backend.server.shutdown() + + assert bookings.read_text(encoding="utf-8").splitlines() == ["booked 2"], ( + "the tool ran again after the crash" + ) + assert backend.tool_calls == 1 + assert third["thread_id"] == first["thread_id"], ( + "turn 3 did not resume the stored Codex thread" + ) + turn_three = "\n".join(backend.bodies[resumed_from:]) + assert "remember the word PAPAYA" in turn_three, "turn 1 history was lost" + assert "book a table for two" in turn_three, "the crashed turn was not handed back" + assert _TOOL_RESULT in turn_three, "the crashed turn's tool result was lost" diff --git a/tests/runtime/codex/test_codex_mcp_bridge.py b/tests/runtime/codex/test_codex_mcp_bridge.py index 6f60758e0..6bc193e27 100644 --- a/tests/runtime/codex/test_codex_mcp_bridge.py +++ b/tests/runtime/codex/test_codex_mcp_bridge.py @@ -101,6 +101,272 @@ def _tools_call(rpc_id: int, name: str, args: dict[str, Any], call_id: str) -> d } +def _guarded_call(rpc_id: int, turn_id: str = "turn-1") -> dict: + body = _tools_call(rpc_id, "echo", {"text": "synthetic"}, f"call_{rpc_id}") + body["params"]["_meta"].update( + threadId="thread-1", + **{"x-codex-turn-metadata": {"thread_id": "thread-1", "turn_id": turn_id}}, + ) + return body + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "case", ["old_turn", "missing", "wrong_thread", "inconsistent"] +) +async def test_pooled_tool_identity_rejected_before_execution(bridge, case): + seen = [] + + async def run(args, call_id): + seen.append(call_id) + return "{}" + + token = bridge.register_process([_ECHO_SPEC]) + bridge.register_turn([_ECHO_SPEC], {"echo": run}, token=token) + bridge.activate_turn(token, "thread-1", "turn-2") + body = _guarded_call(101, "turn-2") + meta = body["params"]["_meta"] + if case == "old_turn": + meta["x-codex-turn-metadata"]["turn_id"] = "turn-1" + elif case == "missing": + meta.pop("x-codex-turn-metadata") + elif case == "wrong_thread": + meta["threadId"] = "another-thread" + meta["x-codex-turn-metadata"]["thread_id"] = "another-thread" + else: + meta["threadId"] = "another-thread" + async with httpx.AsyncClient() as client: + response = await client.post(bridge.url, headers=_rpc_headers(token), json=body) + assert response.status_code == 403 + assert seen == [] + assert bridge.turn_state(token).calls == 0 + + +@pytest.mark.asyncio +async def test_pooled_call_waits_for_sdk_activation_and_idle_rejects(bridge): + token = bridge.register_process([_ECHO_SPEC]) + bridge.register_turn([_ECHO_SPEC], {"echo": _echo}, token=token) + async with httpx.AsyncClient() as client: + pending = asyncio.create_task( + client.post( + bridge.url, headers=_rpc_headers(token), json=_guarded_call(101) + ) + ) + await asyncio.sleep(0.03) + assert not pending.done() + assert bridge.turn_state(token).calls == 0 + bridge.activate_turn(token, "thread-1", "turn-1") + assert (await pending).status_code == 200 + assert await bridge.finish_turn(token) + response = await client.post( + bridge.url, headers=_rpc_headers(token), json=_guarded_call(102) + ) + assert response.status_code == 403 + bridge.release_process(token) + response = await client.post( + bridge.url, headers=_rpc_headers(token), json=_guarded_call(103) + ) + assert response.status_code == 401 + + +@pytest.mark.asyncio +async def test_pooled_turn_closing_unblocks_activation_wait(bridge): + token = bridge.register_process([_ECHO_SPEC]) + bridge.register_turn([_ECHO_SPEC], {"echo": _echo}, token=token) + async with httpx.AsyncClient() as client: + pending = asyncio.create_task( + client.post( + bridge.url, headers=_rpc_headers(token), json=_guarded_call(101) + ) + ) + await asyncio.sleep(0.03) + assert await bridge.finish_turn(token) + assert (await asyncio.wait_for(pending, 1)).status_code == 403 + + +@pytest.mark.asyncio +async def test_pooled_old_cancel_cannot_cancel_new_invocation(bridge): + token = bridge.register_process([_ECHO_SPEC]) + bridge.register_turn([_ECHO_SPEC], {"echo": _echo}, token=token) + bridge.activate_turn(token, "thread-1", "turn-1") + started, release, cancelled = asyncio.Event(), asyncio.Event(), asyncio.Event() + + async def block(args, call_id): + started.set() + try: + await release.wait() + return "{}" + except asyncio.CancelledError: + cancelled.set() + raise + + async with httpx.AsyncClient() as client: + assert ( + await client.post( + bridge.url, headers=_rpc_headers(token), json=_guarded_call(101) + ) + ).status_code == 200 + assert await bridge.finish_turn(token) + bridge.register_turn([_ECHO_SPEC], {"echo": block}, token=token) + bridge.activate_turn(token, "thread-1", "turn-2") + pending = asyncio.create_task( + client.post( + bridge.url, + headers=_rpc_headers(token), + json=_guarded_call(102, "turn-2"), + ) + ) + await asyncio.wait_for(started.wait(), 2) + response = await client.post( + bridge.url, + headers=_rpc_headers(token), + json={ + "jsonrpc": "2.0", + "method": "notifications/cancelled", + "params": {"requestId": 101}, + }, + ) + assert response.status_code == 202 + assert not cancelled.is_set() + release.set() + assert (await pending).status_code == 200 + assert await bridge.finish_turn(token) + bridge.register_turn([_ECHO_SPEC], {"echo": _echo}, token=token) + bridge.activate_turn(token, "thread-1", "turn-3") + # Request IDs cannot be reused by a reconnected pooled MCP client: + # an old cancellation without turn metadata would otherwise be ambiguous. + response = await client.post( + bridge.url, headers=_rpc_headers(token), json=_guarded_call(102, "turn-3") + ) + assert response.status_code == 409 + assert bridge.turn_state(token).calls == 0 + assert not await bridge.finish_turn(token) + + +@pytest.mark.asyncio +async def test_pooled_finish_cancels_and_drains_business_tool(bridge): + token = bridge.register_process([_ECHO_SPEC]) + started, cancelled = asyncio.Event(), asyncio.Event() + + async def block(args, call_id): + started.set() + try: + await asyncio.sleep(60) + finally: + cancelled.set() + return "{}" + + bridge.register_turn([_ECHO_SPEC], {"echo": block}, token=token) + bridge.activate_turn(token, "thread-1", "turn-1") + async with httpx.AsyncClient() as client: + pending = asyncio.create_task( + client.post( + bridge.url, headers=_rpc_headers(token), json=_guarded_call(101) + ) + ) + await asyncio.wait_for(started.wait(), 2) + assert await bridge.finish_turn(token) + assert cancelled.is_set() + await asyncio.wait_for(pending, 2) + + +@pytest.mark.asyncio +async def test_pooled_current_cancel_reaches_only_current_tool(bridge): + token = bridge.register_process([_ECHO_SPEC]) + started, cancelled = asyncio.Event(), asyncio.Event() + + async def block(args, call_id): + started.set() + try: + await asyncio.sleep(60) + finally: + cancelled.set() + return "{}" + + bridge.register_turn([_ECHO_SPEC], {"echo": block}, token=token) + bridge.activate_turn(token, "thread-1", "turn-1") + async with httpx.AsyncClient() as client: + pending = asyncio.create_task( + client.post( + bridge.url, headers=_rpc_headers(token), json=_guarded_call(101) + ) + ) + await started.wait() + response = await client.post( + bridge.url, + headers=_rpc_headers(token), + json={ + "jsonrpc": "2.0", + "method": "notifications/cancelled", + "params": {"requestId": 101}, + }, + ) + assert response.status_code == 202 + await asyncio.wait_for(cancelled.wait(), 2) + await asyncio.wait_for(pending, 2) + + +@pytest.mark.asyncio +async def test_pooled_manifest_change_is_rejected_before_binding(bridge): + token = bridge.register_process([_ECHO_SPEC]) + with pytest.raises(RuntimeError, match="manifest"): + bridge.register_turn([_spec("other")], {"other": _echo}, token=token) + assert bridge.turn_state(token) is None + + +@pytest.mark.asyncio +async def test_pooled_request_bookkeeping_has_a_hard_limit(bridge, monkeypatch): + monkeypatch.setattr(mcp_bridge, "_MAX_PROCESS_REQUESTS", 1) + token = bridge.register_process([_ECHO_SPEC]) + bridge.register_turn([_ECHO_SPEC], {"echo": _echo}, token=token) + bridge.activate_turn(token, "thread-1", "turn-1") + async with httpx.AsyncClient() as client: + assert ( + await client.post( + bridge.url, headers=_rpc_headers(token), json=_guarded_call(101) + ) + ).status_code == 200 + assert ( + await client.post( + bridge.url, headers=_rpc_headers(token), json=_guarded_call(102) + ) + ).status_code == 409 + assert bridge.turn_state(token).calls == 1 + assert not await bridge.finish_turn(token) + + +@pytest.mark.asyncio +async def test_pooled_undrained_executor_cannot_be_reused(bridge, monkeypatch): + monkeypatch.setattr(mcp_bridge, "_STOP_TIMEOUT_SECONDS", 0.02) + token = bridge.register_process([_ECHO_SPEC]) + started, release = asyncio.Event(), asyncio.Event() + + async def ignore_cancel(args, call_id): + started.set() + try: + await release.wait() + except asyncio.CancelledError: + await release.wait() + return "{}" + + bridge.register_turn([_ECHO_SPEC], {"echo": ignore_cancel}, token=token) + bridge.activate_turn(token, "thread-1", "turn-1") + async with httpx.AsyncClient() as client: + pending = asyncio.create_task( + client.post( + bridge.url, headers=_rpc_headers(token), json=_guarded_call(101) + ) + ) + try: + await started.wait() + assert not await bridge.finish_turn(token) + with pytest.raises(RuntimeError, match="lifecycle"): + bridge.register_turn([_ECHO_SPEC], {"echo": _echo}, token=token) + finally: + release.set() + await asyncio.wait_for(pending, 2) + + async def _wait_for(predicate, timeout: float = 5.0) -> None: deadline = time.monotonic() + timeout while not predicate(): diff --git a/tests/runtime/codex/test_codex_private_dirs.py b/tests/runtime/codex/test_codex_private_dirs.py new file mode 100644 index 000000000..dc8cbf218 --- /dev/null +++ b/tests/runtime/codex/test_codex_private_dirs.py @@ -0,0 +1,132 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Reclaiming the temp roots a killed VeADK process left behind.""" + +from __future__ import annotations + +import os +import subprocess +import sys +import tempfile +import time + +import pytest + +from veadk.runtime.codex import private_dirs +from veadk.runtime.codex.private_dirs import ( + HOME_ROOT_PREFIX, + LEGACY_MAX_AGE_SECONDS, + OWNER_LOCK, + WORKSPACE_ROOT_PREFIX, + create_owned_root, + reap_orphans, +) + +pytestmark = pytest.mark.skipif( + private_dirs.fcntl is None, reason="ownership locks need fcntl" +) + +_OLD = time.time() - LEGACY_MAX_AGE_SECONDS - 60 + + +@pytest.fixture +def temp_dir(monkeypatch, tmp_path): + """Roots go to an isolated temp dir; no background reaper thread.""" + monkeypatch.setattr(tempfile, "tempdir", str(tmp_path)) + monkeypatch.setattr(private_dirs, "_reaper_started", True) + return tmp_path + + +def _dead_root(temp_dir, prefix: str): + """A root whose owner exited: its lock file exists, nobody holds it.""" + root = temp_dir / f"{prefix}dead" + (root / "session").mkdir(parents=True) + (root / OWNER_LOCK).write_text("") + return root + + +def test_a_root_whose_owner_died_is_removed(temp_dir) -> None: + workspaces = _dead_root(temp_dir, WORKSPACE_ROOT_PREFIX) + homes = _dead_root(temp_dir, HOME_ROOT_PREFIX) + + assert reap_orphans(str(temp_dir)) == 2 + assert not workspaces.exists() and not homes.exists() + + +def test_a_root_owned_by_a_live_process_is_kept(temp_dir) -> None: + mine = create_owned_root(HOME_ROOT_PREFIX) + # Another live process holding its root's lock. + other = temp_dir / f"{WORKSPACE_ROOT_PREFIX}other" + other.mkdir() + holder = subprocess.Popen( + [ + sys.executable, + "-c", + "import fcntl, sys, time; f = open(sys.argv[1], 'w'); " + "fcntl.flock(f, fcntl.LOCK_EX); print('locked', flush=True); " + "time.sleep(60)", + str(other / OWNER_LOCK), + ], + stdout=subprocess.PIPE, + text=True, + ) + try: + assert holder.stdout is not None and holder.stdout.readline() == "locked\n" + assert reap_orphans(str(temp_dir)) == 0 + assert os.path.isdir(mine) and other.is_dir() + finally: + holder.kill() + holder.wait() + # Its owner is gone now. + assert reap_orphans(str(temp_dir)) == 1 + assert not other.exists() and os.path.isdir(mine) + + +def test_legacy_dirs_without_owner_locks_are_never_removed_automatically( + temp_dir, +) -> None: + fresh_root = temp_dir / f"{WORKSPACE_ROOT_PREFIX}legacy-fresh" + old_root = temp_dir / f"{WORKSPACE_ROOT_PREFIX}legacy-old" + fresh_home = temp_dir / "veadk-codex-fresh" + old_home = temp_dir / "veadk-codex-old" + not_a_home = temp_dir / "veadk-codex-something-else" + for path in (fresh_root, old_root, fresh_home, old_home, not_a_home): + path.mkdir() + for home in (fresh_home, old_home): + (home / "config.toml").write_text("") + for path in (old_root, old_home / "config.toml", old_home, not_a_home): + os.utime(path, (_OLD, _OLD)) + + assert reap_orphans(str(temp_dir)) == 0 + assert all( + path.exists() + for path in (fresh_root, old_root, fresh_home, old_home, not_a_home) + ) + + +def test_failed_root_removal_is_not_counted_as_success( + temp_dir, monkeypatch, caplog +) -> None: + root = _dead_root(temp_dir, WORKSPACE_ROOT_PREFIX) + + def fail_remove(*args, **kwargs): + raise PermissionError("not allowed") + + monkeypatch.setattr(private_dirs.shutil, "rmtree", fail_remove) + + assert reap_orphans(str(temp_dir)) == 0 + assert root.exists() + assert "codex_orphan_reap_failed" in caplog.text + assert "PermissionError" in caplog.text diff --git a/tests/runtime/codex/test_codex_process_pool.py b/tests/runtime/codex/test_codex_process_pool.py new file mode 100644 index 000000000..1bf44c12b --- /dev/null +++ b/tests/runtime/codex/test_codex_process_pool.py @@ -0,0 +1,361 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unit tests for the warm Codex process pool (no Codex binary involved).""" + +from __future__ import annotations + +import asyncio +import contextlib +import os +import threading +from pathlib import Path +from types import SimpleNamespace +from typing import Any, cast + +import pytest + +pytest.importorskip("openai_codex") + +from veadk.runtime.codex import process_pool # noqa: E402 +from veadk.runtime.codex.process_pool import ( # noqa: E402 + CodexProcessLease, + CodexProcessPool, + WarmCodex, + tree_digest, +) +from veadk.runtime.codex.turn_control import TurnCompletion # noqa: E402 + + +class _Proc: + def __init__(self) -> None: + self.returncode: int | None = None + + def poll(self) -> int | None: + return self.returncode + + def kill(self) -> None: + self.returncode = -9 + + +class _Codex: + def __init__(self) -> None: + self._client = SimpleNamespace( + _sync=SimpleNamespace(_proc=_Proc(), _reader_thread=None) + ) + self.closed = False + + async def __aenter__(self) -> "_Codex": + return self + + async def __aexit__(self, *exc: Any) -> None: + self.closed = True + self._client._sync._proc.returncode = 0 + + +def _entry(tmp_path, name: str, fingerprint: str = "fp") -> WarmCodex: + home = tmp_path / name + home.mkdir() + return WarmCodex( + codex=_Codex(), + codex_home=str(home), + fingerprint=fingerprint, + bridge_token="", + thread=object(), + thread_id="t", + version=1, + covered_invocation_id="inv", + turns=1, + ) + + +async def _settle(pool: CodexProcessPool) -> None: + await asyncio.gather(*list(pool._closing), return_exceptions=True) + + +@pytest.mark.asyncio +async def test_cancelled_identity_check_closes_removed_process(tmp_path, monkeypatch): + from veadk.runtime.codex import runtime + + pool = CodexProcessPool() + monkeypatch.setattr(runtime, "_PROCESS_POOL", pool) + entry = _entry(tmp_path, "cancelled") + started = asyncio.Event() + + class Store: + async def current_identity(self, key): + started.set() + await asyncio.sleep(60) + + pool.put("key", entry) + store = cast(Any, Store()) + key = cast(Any, object()) + ctx = cast(Any, SimpleNamespace()) + task = asyncio.create_task(runtime._take_warm_process("key", "fp", store, key, ctx)) + await started.wait() + task.cancel() + with pytest.raises(asyncio.CancelledError): + await task + await _settle(pool) + assert entry.codex.closed + assert not Path(entry.codex_home).exists() + + +@pytest.mark.asyncio +async def test_equal_version_different_thread_invalidates_warm_entry( + tmp_path, monkeypatch +): + from veadk.runtime.codex import runtime + + pool = CodexProcessPool() + monkeypatch.setattr(runtime, "_PROCESS_POOL", pool) + entry = _entry(tmp_path, "recreated") + + class Store: + async def current_identity(self, key): + return "a-different-thread", entry.version + + pool.put("key", entry) + store = cast(Any, Store()) + key = cast(Any, object()) + ctx = cast(Any, SimpleNamespace(invocation_id="inv")) + assert await runtime._take_warm_process("key", "fp", store, key, ctx) is None + await _settle(pool) + assert entry.codex.closed + + +@pytest.mark.asyncio +async def test_pool_release_revokes_process_credential(tmp_path): + pool = CodexProcessPool() + entry = _entry(tmp_path, "released") + revoked = [] + entry.on_close = lambda: revoked.append("released") + pool.put("key", entry) + assert await pool.release(lambda key: key == "key") == 1 + assert revoked == ["released"] + assert entry.codex.closed + + +@pytest.mark.asyncio +async def test_borrowed_lease_setup_failure_revokes_its_binding(tmp_path): + entry = _entry(tmp_path, "borrowed") + revoked = [] + entry.on_close = lambda: revoked.append("closed") + lease = CodexProcessLease(lambda: None, entry, reusable=True) + await lease.close() + assert revoked == ["closed"] + assert entry.codex.closed + + +def test_tree_digest_changes_when_a_directory_symlink_target_changes(tmp_path) -> None: + source_a = tmp_path / "source-a" + source_b = tmp_path / "source-b" + source_a.mkdir() + source_b.mkdir() + (source_a / "marker.txt").write_text("same contents") + (source_b / "marker.txt").write_text("same contents") + homes = [] + for name, source in (("home-a", source_a), ("home-b", source_b)): + home = tmp_path / name + (home / "skills").mkdir(parents=True) + os.symlink(source, home / "skills" / "example", target_is_directory=True) + homes.append(home) + + assert tree_digest(str(homes[0])) != tree_digest(str(homes[1])) + + +def test_tree_digest_follows_skill_contents_and_terminates_on_cycles(tmp_path) -> None: + source = tmp_path / "source" + source.mkdir() + marker = source / "marker.txt" + marker.write_text("before") + os.symlink(source, source / "cycle", target_is_directory=True) + home = tmp_path / "home" + (home / "skills").mkdir(parents=True) + os.symlink(source, home / "skills" / "example", target_is_directory=True) + + before = tree_digest(str(home)) + marker.write_text("after") + + assert tree_digest(str(home)) != before + + +@pytest.mark.asyncio +async def test_discard_close_work_is_bounded(monkeypatch, tmp_path) -> None: + monkeypatch.setenv(process_pool.POOL_SIZE_ENV, "2") + blocker = asyncio.Event() + + async def blocked_close(entry): + await blocker.wait() + + monkeypatch.setattr(process_pool, "_close_warm", blocked_close) + pool = CodexProcessPool() + entries = [_entry(tmp_path, f"entry-{i}") for i in range(10)] + + for entry in entries: + pool.discard(entry) + + assert len(pool._closing) <= 2 + assert ( + sum(entry.codex._client._sync._proc.returncode == -9 for entry in entries) >= 8 + ) + blocker.set() + await pool.close_all() + + +@pytest.mark.asyncio +async def test_warm_home_deletion_does_not_block_the_event_loop( + monkeypatch, tmp_path +) -> None: + started = threading.Event() + release = threading.Event() + real_rmtree = process_pool.shutil.rmtree + + def slow_rmtree(path, ignore_errors=False): + started.set() + assert release.wait(1) + real_rmtree(path, ignore_errors=ignore_errors) + + monkeypatch.setattr(process_pool.shutil, "rmtree", slow_rmtree) + pool = CodexProcessPool() + pool.discard(_entry(tmp_path, "slow-delete")) + assert await asyncio.to_thread(started.wait, 1) + + heartbeat = asyncio.Event() + asyncio.get_running_loop().call_soon(heartbeat.set) + await asyncio.wait_for(heartbeat.wait(), 0.1) + + release.set() + await pool.close_all() + + +@pytest.mark.asyncio +async def test_pool_evicts_least_recently_used_beyond_its_size( + monkeypatch, tmp_path +) -> None: + monkeypatch.setenv(process_pool.POOL_SIZE_ENV, "2") + pool = CodexProcessPool() + a, b, c = (_entry(tmp_path, n) for n in "abc") + for key, entry in (("a", a), ("b", b), ("c", c)): + assert pool.put(key, entry) + await _settle(pool) + + assert a.codex.closed and not (tmp_path / "a").exists() + assert pool.take("b", "fp") is b and pool.take("c", "fp") is c + await pool.close_all() + + +@pytest.mark.asyncio +async def test_pool_closes_idle_processes_after_the_ttl(monkeypatch, tmp_path) -> None: + monkeypatch.setenv(process_pool.IDLE_TTL_ENV, "0.01") + pool = CodexProcessPool() + entry = _entry(tmp_path, "a") + assert pool.put("a", entry) + await asyncio.sleep(0.05) + await _settle(pool) + + assert entry.codex.closed + assert pool.take("a", "fp") is None + + +@pytest.mark.asyncio +async def test_pool_refuses_changed_settings_and_dead_processes(tmp_path) -> None: + pool = CodexProcessPool() + changed, dead = _entry(tmp_path, "a"), _entry(tmp_path, "b") + assert pool.put("a", changed) and pool.put("b", dead) + dead.codex._client._sync._proc.returncode = 1 + + assert pool.take("a", "other-fp") is None + assert pool.take("b", "fp") is None + await _settle(pool) + assert changed.codex.closed and dead.codex.closed + + +@pytest.mark.asyncio +async def test_pool_size_zero_and_turn_cap_refuse_to_park( + monkeypatch, tmp_path +) -> None: + pool = CodexProcessPool() + worn = _entry(tmp_path, "a") + worn.turns = process_pool.MAX_TURNS_PER_PROCESS + assert not pool.put("a", worn) + + monkeypatch.setenv(process_pool.POOL_SIZE_ENV, "0") + assert not pool.put("b", _entry(tmp_path, "b")) + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("status", "error", "kept"), + [ + ("completed", None, True), + ("interrupted", None, True), + (None, None, False), # the turn never reported turn/completed + ("completed", RuntimeError("boom"), False), + ], +) +async def test_lease_keeps_a_process_only_after_a_clean_turn( + status, error, kept +) -> None: + lease = CodexProcessLease(_Codex, None, reusable=True) + completion = TurnCompletion("turn-1") + if status is not None: + completion.future.set_result(status) + codex: Any = None + with pytest.raises(RuntimeError) if error else contextlib.nullcontext(): + async with lease: + codex = lease.codex + lease.completion = completion + if error: + raise error + + assert lease.kept is kept + assert codex.closed is (not kept) + + +@pytest.mark.asyncio +async def test_pool_sweeps_expired_processes_whose_timer_never_fired( + monkeypatch, tmp_path +) -> None: + """A stopped loop never runs the expiry timer; the next pool use reclaims.""" + monkeypatch.setenv(process_pool.IDLE_TTL_ENV, "60") + pool = CodexProcessPool() + stale, fresh = _entry(tmp_path, "a"), _entry(tmp_path, "b") + assert pool.put("a", stale) and pool.put("b", fresh) + assert stale.expiry is not None + stale.expiry.cancel() # as if its loop had stopped + stale.idle_since -= 120 + + assert pool.take("b", "fp") is fresh + await _settle(pool) + assert stale.codex.closed and not fresh.codex.closed + assert pool.take("a", "fp") is None + + +@pytest.mark.asyncio +async def test_release_codex_session_closes_only_that_sessions_processes( + monkeypatch, tmp_path +) -> None: + from veadk.runtime.codex import runtime as runtime_module + + pool = CodexProcessPool() + monkeypatch.setattr(runtime_module, "_PROCESS_POOL", pool) + first, second, other = (_entry(tmp_path, name) for name in "abc") + assert pool.put(("app", "user", "s1", "agent_a"), first) + assert pool.put(("app", "user", "s1", "agent_b"), second) + assert pool.put(("app", "user", "s2", "agent_a"), other) + + assert await runtime_module.release_codex_session("app", "user", "s1") == 2 + assert first.codex.closed and second.codex.closed and not other.codex.closed + assert pool.take(("app", "user", "s2", "agent_a"), "fp") is other diff --git a/tests/runtime/codex/test_codex_runtime_sdk.py b/tests/runtime/codex/test_codex_runtime_sdk.py index 357fb51a7..b708211b0 100644 --- a/tests/runtime/codex/test_codex_runtime_sdk.py +++ b/tests/runtime/codex/test_codex_runtime_sdk.py @@ -17,7 +17,9 @@ from __future__ import annotations import logging +from pathlib import Path from types import SimpleNamespace +from typing import Any import pytest from google.adk.sessions.session import Session @@ -25,7 +27,7 @@ pytest.importorskip("openai_codex") -from openai_codex import ApprovalMode, Sandbox # noqa: E402 +from openai_codex import ApprovalMode # noqa: E402 from veadk.runtime.codex.config import CodexRuntimeConfig # noqa: E402 from veadk.runtime.codex.runtime import CodexRuntime # noqa: E402 @@ -33,6 +35,48 @@ from veadk.runtime.codex.runtime import _prepare_workspace # noqa: E402 +def test_existing_empty_workspace_is_not_a_reset(tmp_path): + from types import SimpleNamespace + from veadk.runtime.codex.runtime import _prepare_workspace_state + + ctx: Any = SimpleNamespace( + session=SimpleNamespace(app_name="app", user_id="user", id="session"), + agent=SimpleNamespace(name="agent"), + ) + config = CodexRuntimeConfig(workspace_root=str(tmp_path)) + path, created = _prepare_workspace_state(config, ctx) + assert created + assert not list(Path(path).iterdir()) + assert _prepare_workspace_state(config, ctx) == (path, False) + Path(path).rmdir() + assert _prepare_workspace_state(config, ctx) == (path, True) + + +def test_mcp_reuse_default_and_environment_override(monkeypatch): + from types import SimpleNamespace + + monkeypatch.delenv("VEADK_CODEX_MCP_PROCESS_REUSE", raising=False) + assert CodexRuntimeConfig().mcp_process_reuse + assert CodexRuntimeConfig.from_agent(SimpleNamespace()).mcp_process_reuse + assert not CodexRuntimeConfig.from_agent( + SimpleNamespace( + codex_runtime_config=CodexRuntimeConfig(mcp_process_reuse=False) + ) + ).mcp_process_reuse + monkeypatch.setenv("VEADK_CODEX_MCP_PROCESS_REUSE", "1") + assert CodexRuntimeConfig.from_agent(SimpleNamespace()).mcp_process_reuse + monkeypatch.setenv("VEADK_CODEX_MCP_PROCESS_REUSE", "0") + assert not CodexRuntimeConfig.from_agent( + SimpleNamespace(codex_runtime_config=CodexRuntimeConfig(mcp_process_reuse=True)) + ).mcp_process_reuse + + +@pytest.mark.parametrize("value", ["0", "false", "off", "no"]) +def test_mcp_reuse_can_be_disabled_by_environment(monkeypatch, value): + monkeypatch.setenv("VEADK_CODEX_MCP_PROCESS_REUSE", value) + assert not CodexRuntimeConfig.from_agent(SimpleNamespace()).mcp_process_reuse + + class _FakeShim: url = "http://127.0.0.1:12345" @@ -227,7 +271,8 @@ async def fake_get_shim(api_base, api_key): assert ( _FakeAsyncCodex.calls["thread_start"]["approval_mode"] is ApprovalMode.deny_all ) - assert _FakeAsyncCodex.calls["thread_start"]["sandbox"] is Sandbox.workspace_write + # No preset: it would replace the config's permissions profile. + assert _FakeAsyncCodex.calls["thread_start"]["sandbox"] is None # `base_instructions` *replaces* Codex's 20.9KB built-in system prompt, so # the runtime deliberately never sends it; the agent identity rides along # with the instruction on the developer channel instead. @@ -343,12 +388,17 @@ def test_codex_home_pins_settings_the_pinned_cli_would_break(tmp_path) -> None: import tomli as tomllib from veadk.runtime.codex.model_provider import shim_route - from veadk.runtime.codex.runtime import _prepare_codex_home + from veadk.runtime.codex.runtime import _nested_read_access, _prepare_codex_home + + import os + import tempfile + workspace = os.path.realpath(tempfile.mkdtemp(prefix="veadk-codex-ws-")) home = _prepare_codex_home( shim_route("http://127.0.0.1:9", ""), "model-x", CodexRuntimeConfig(network_access=True), + workspace=workspace, ) try: with open(f"{home}/config.toml", "rb") as f: @@ -357,6 +407,7 @@ def test_codex_home_pins_settings_the_pinned_cli_would_break(tmp_path) -> None: import shutil shutil.rmtree(home, ignore_errors=True) + shutil.rmtree(workspace, ignore_errors=True) # Both default to on in the pinned CLI. Unbounded retries would turn an # unreachable shim into a hung invocation (this runtime has no turn @@ -376,7 +427,25 @@ def test_codex_home_pins_settings_the_pinned_cli_would_break(tmp_path) -> None: "*TOKEN*", } assert config["model_providers"]["veadk"]["wire_api"] == "responses" - assert config["sandbox_workspace_write"]["network_access"] is True + # Sandboxed commands run under VeADK's permissions profile, which Codex + # rejects next to the legacy sandbox keys. + assert config["default_permissions"] == "veadk" + assert "sandbox_mode" not in config and "sandbox_workspace_write" not in config + profile = config["permissions"]["veadk"] + assert profile["network"]["enabled"] is True + assert profile["extends"] == ":read-only" + # Only this session's workspace is writable, and of CODEX_HOME only its + # skills are readable; the temp dirs every other session lives in are not. + tmp = os.path.realpath(tempfile.gettempdir()) + assert profile["filesystem"][tmp] == "deny" + assert profile["filesystem"][os.path.realpath("/tmp")] == "deny" + assert profile["filesystem"][workspace] == "write" + assert profile["filesystem"][os.path.realpath(f"{home}/skills")] == ( + _nested_read_access() + ) + assert config["shell_environment_policy"]["set"] == {"TMPDIR": workspace} + # No AGENTS.md search above the workspace, whose parents are denied. + assert config["project_root_markers"] == [] def test_direct_codex_home_names_the_key_env_var_but_never_holds_the_key() -> None: @@ -390,6 +459,7 @@ def test_direct_codex_home_names_the_key_env_var_but_never_holds_the_key() -> No except ModuleNotFoundError: # Python 3.10: pytest depends on tomli there. import tomli as tomllib import shutil + import tempfile from veadk.runtime.codex.model_provider import DIRECT_KEY_ENV from veadk.runtime.codex.model_provider import direct_route @@ -401,7 +471,9 @@ def test_direct_codex_home_names_the_key_env_var_but_never_holds_the_key() -> No secret, extra_headers={"X-Client": "veadk"}, ) - home = _prepare_codex_home(route, "model-x", CodexRuntimeConfig()) + home = _prepare_codex_home( + route, "model-x", CodexRuntimeConfig(), workspace=tempfile.gettempdir() + ) try: with open(f"{home}/config.toml", "rb") as f: raw = f.read() @@ -416,3 +488,53 @@ def test_direct_codex_home_names_the_key_env_var_but_never_holds_the_key() -> No assert provider["env_key"] == DIRECT_KEY_ENV assert provider["wire_api"] == "responses" assert provider["http_headers"] == {"X-Client": "veadk"} + + +def test_sandbox_profile_denies_configured_host_paths(monkeypatch, tmp_path) -> None: + """Host files outside the temp dirs can be denied too; the workspace stays.""" + import os + + from veadk.runtime.codex.runtime import _sandbox_profile + + secrets_dir, db = tmp_path / "app", tmp_path / "sessions.db" + workspace = tmp_path / "app" / "ws" + monkeypatch.setenv( + "VEADK_CODEX_DENY_READ_PATHS", os.pathsep.join([str(secrets_dir), str(db)]) + ) + profile = _sandbox_profile(CodexRuntimeConfig(), str(workspace), str(tmp_path)) + assert profile is not None + filesystem = profile["permissions"]["veadk"]["filesystem"] + assert filesystem[os.path.realpath(secrets_dir)] == "deny" + assert filesystem[os.path.realpath(db)] == "deny" + assert filesystem[os.path.realpath(workspace)] == "write" + assert ( + _sandbox_profile( + CodexRuntimeConfig(sandbox="full_access", network_access=True), + str(workspace), + str(tmp_path), + ) + is None + ) + + +def test_linux_grants_nested_session_paths_write(monkeypatch, tmp_path) -> None: + """Codex's Linux sandbox cannot reach a `read` grant under a denied parent. + + The session's own workspace (even for `read_only`) and its skills copy are + granted `write` there instead; everything else stays denied. + """ + import os + + from veadk.runtime.codex import runtime as runtime_module + + workspace, home = tmp_path / "ws", tmp_path / "home" + for platform, expected in (("linux", "write"), ("darwin", "read")): + monkeypatch.setattr(runtime_module.sys, "platform", platform) + profile = runtime_module._sandbox_profile( + CodexRuntimeConfig(sandbox="read_only"), str(workspace), str(home) + ) + assert profile is not None + filesystem = profile["permissions"]["veadk"]["filesystem"] + assert filesystem[os.path.realpath(workspace)] == expected + assert filesystem[os.path.realpath(home / "skills")] == expected + assert os.path.realpath(home) not in filesystem diff --git a/tests/runtime/codex/test_codex_runtime_smoke.py b/tests/runtime/codex/test_codex_runtime_smoke.py index 0a813db02..07e0275bb 100644 --- a/tests/runtime/codex/test_codex_runtime_smoke.py +++ b/tests/runtime/codex/test_codex_runtime_smoke.py @@ -58,6 +58,7 @@ import os import platform import shutil +import sys import tempfile import time import uuid @@ -76,6 +77,9 @@ #: which of these comes back says whether the sandbox is actually enforcing. _WRITE_ALLOWED_MARKER = "veadk-codex-write-allowed" _WRITE_DENIED_MARKER = "veadk-codex-write-denied" +#: The same probe outside the workspace, which `read_only` denies everywhere. +_OUTSIDE_WRITE_ALLOWED_MARKER = "veadk-codex-outside-write-allowed" +_OUTSIDE_WRITE_DENIED_MARKER = "veadk-codex-outside-write-denied" #: Path the probe tries to create, relative to the turn's workspace (cwd). _WRITE_PROBE_FILE = "veadk-codex-write-probe" #: Passed to the ADK tool by the stub backend and recorded by the tool body. @@ -288,7 +292,10 @@ def _response(body: dict[str, Any], output: list[dict[str, Any]]) -> dict[str, A f"/bin/echo {_SANDBOX_PROBE_MARKER}; " f"if /usr/bin/touch ./{_WRITE_PROBE_FILE} 2>/dev/null; " f"then /bin/echo {_WRITE_ALLOWED_MARKER}; " - f"else /bin/echo {_WRITE_DENIED_MARKER}; fi" + f"else /bin/echo {_WRITE_DENIED_MARKER}; fi; " + f"if /usr/bin/touch /tmp/{_WRITE_PROBE_FILE}-$$ 2>/dev/null; " + f"then /bin/echo {_OUTSIDE_WRITE_ALLOWED_MARKER}; " + f"else /bin/echo {_OUTSIDE_WRITE_DENIED_MARKER}; fi" ) @@ -460,10 +467,22 @@ async def _drive() -> None: "the OS sandbox did not establish, or refused to run a trivial " f"read-only command. Codex reported: {probe_output!r}" ) - assert _WRITE_DENIED_MARKER in probe_output, ( + assert _OUTSIDE_WRITE_DENIED_MARKER in probe_output, ( "sandbox='read_only' did not actually block a write from inside the " f"sandbox. Codex reported: {probe_output!r}" ) + # The session's own workspace: read-only, except on Linux, where Codex's + # sandbox cannot reach a read grant beneath the denied temp dir and VeADK + # grants it write instead (`runtime._nested_read_access`). Revisit when + # Codex fixes that; until then Linux must keep the workaround. + own_marker = ( + _WRITE_ALLOWED_MARKER + if sys.platform.startswith("linux") + else _WRITE_DENIED_MARKER + ) + assert own_marker in probe_output, ( + f"unexpected own-workspace write result. Codex reported: {probe_output!r}" + ) # -- the ADK tool path over a real socket ------------------------------ assert executed == [_TOOL_MARKER], ( @@ -512,9 +531,18 @@ async def _drive() -> None: # process-owned root or landing in the working directory. workspace = next(iter(new_workspaces)) assert workspace.parent == workspace_root - assert not (workspace / _WRITE_PROBE_FILE).exists(), ( - "the read-only sandbox let Codex create a file in the workspace" - ) + # Same platform split as `own_marker` above: the file shows the write + # really happened (Linux workaround) or really did not (elsewhere). + write_probe = workspace / _WRITE_PROBE_FILE + if sys.platform.startswith("linux"): + assert write_probe.exists(), ( + "the Linux workaround should let Codex write its own workspace: " + "Codex cannot read-grant a path beneath the denied temp dir" + ) + else: + assert not write_probe.exists(), ( + "the read-only sandbox let Codex create a file in the workspace" + ) cwd_after = {name for name in os.listdir(os.getcwd()) if name[0] != "."} assert cwd_after == cwd_before, ( "the turn created entries in the working directory: " @@ -526,14 +554,19 @@ async def _drive() -> None: def _codex_homes(temp_root: Path) -> set[Path]: """Invocation-scoped CODEX_HOME dirs, excluding the workspace root. - ``_prepare_codex_home`` and ``_SESSION_WORKSPACE_ROOT`` share the - ``veadk-codex-`` prefix; only the former must be gone after a turn. + ``_prepare_codex_home`` creates homes inside a ``veadk-codex-homes-*`` + root; the roots, like ``_SESSION_WORKSPACE_ROOT``, outlive a turn. """ - return { - path - for path in temp_root.glob("veadk-codex-*") - if path.is_dir() and not path.name.startswith("veadk-codex-workspaces-") - } + homes: set[Path] = set() + for path in temp_root.glob("veadk-codex-*"): + if not path.is_dir() or path.name.startswith("veadk-codex-workspaces-"): + continue + if path.name.startswith("veadk-codex-homes-"): + # A process-owned root; the homes are its subdirectories. + homes.update(home for home in path.iterdir() if home.is_dir()) + else: + homes.add(path) + return homes def _tool_names(requests: list[dict[str, Any]]) -> list[str]: @@ -582,12 +615,13 @@ async def test_real_codex_binary_fails_turn_when_shim_is_unreachable() -> None: from veadk.runtime.codex.model_provider import shim_route + workspace = tempfile.mkdtemp(prefix="veadk-codex-smoke-dead-shim-") home = runtime_module._prepare_codex_home( shim_route(f"http://127.0.0.1:{dead_port}", ""), "smoke-model", CodexRuntimeConfig(), + workspace=workspace, ) - workspace = tempfile.mkdtemp(prefix="veadk-codex-smoke-dead-shim-") async def _turn_status() -> str: config = CodexConfig( @@ -742,7 +776,7 @@ def do_POST(self) -> None: # noqa: N802 - http.server API extra_headers={"X-Api-Key": header_secret}, ) home = runtime_module._prepare_codex_home( - route, "smoke-model", CodexRuntimeConfig() + route, "smoke-model", CodexRuntimeConfig(), workspace=tempfile.gettempdir() ) with open(os.path.join(home, "config.toml"), encoding="utf-8") as f: config_text = f.read() diff --git a/tests/runtime/codex/test_codex_sandbox_isolation_smoke.py b/tests/runtime/codex/test_codex_sandbox_isolation_smoke.py new file mode 100644 index 000000000..b92418591 --- /dev/null +++ b/tests/runtime/codex/test_codex_sandbox_isolation_smoke.py @@ -0,0 +1,294 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Real-sandbox check that one session's commands cannot reach another's files. + +A stubbed Responses backend makes the real Codex binary run one probe command +through its own ``exec_command`` tool, on the direct transport, via ``Runner``. +No model is called. Run it on every platform VeADK is deployed on (the Linux +sandbox differs from macOS Seatbelt):: + + CODEX_RUN_SMOKE=1 uv run --extra dev pytest -m codex_smoke -p no:xdist \\ + tests/runtime/codex/test_codex_sandbox_isolation_smoke.py -rs +""" + +from __future__ import annotations + +import asyncio +import json +import os +import shutil +import sys +import tempfile +import threading +import uuid +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from pathlib import Path +from types import SimpleNamespace +from typing import Any + +import pytest + +pytest.importorskip("openai_codex") + +_CHECKS = ( + "own_read", + "own_write", + "skill_read", + "sibling_read", + "sibling_write", + "home_read", + "home_write", + "tmpdir_write", + "slash_tmp_write", + "tmp_env", + "proc_environ", +) + + +def _probe_command(own: str, sibling: str, home: str, skill: str) -> str: + tmp = os.path.realpath(tempfile.gettempdir()) + environ = f"/proc/{os.getpid()}/environ" + return "; ".join( + ( + f"cat {own}/AGENTS.md >/dev/null 2>&1; echo own_read=$?", + f"echo x > {own}/probe 2>/dev/null; echo own_write=$?", + f"cat {skill} >/dev/null 2>&1; echo skill_read=$?", + f"cat {sibling}/secret.txt >/dev/null 2>&1; echo sibling_read=$?", + f"echo x > {sibling}/w 2>/dev/null; echo sibling_write=$?", + f"cat {home}/sessions/rollout.jsonl >/dev/null 2>&1; echo home_read=$?", + f"echo x >> {home}/config.toml 2>/dev/null; echo home_write=$?", + f"echo x > {tmp}/veadk-probe-$$ 2>/dev/null; echo tmpdir_write=$?", + "echo x > /tmp/veadk-probe-$$ 2>/dev/null; echo slash_tmp_write=$?", + "python3 -c 'import tempfile; tempfile.NamedTemporaryFile()' " + "2>/dev/null; echo tmp_env=$?", + f"test -e {environ} && (cat {environ} >/dev/null 2>&1; " + "echo proc_environ=$?) || echo proc_environ=absent", + ) + ) + + +class _Backend: + """Responses stub: one ``exec_command`` call, then a final message.""" + + def __init__(self, command: str) -> None: + self.command = command + self.outputs: list[str] = [] + self.bodies: list[str] = [] + backend = self + + class Handler(BaseHTTPRequestHandler): + protocol_version = "HTTP/1.1" + + def log_message(self, format: str, *args: Any) -> None: + return None + + def do_GET(self) -> None: + self._send("application/json", b'{"data":[],"models":[]}') + + def do_POST(self) -> None: + size = int(self.headers.get("content-length", 0)) + raw = self.rfile.read(size) or b"{}" + backend.bodies.append(raw.decode("utf-8", "replace")) + body = json.loads(raw) + outputs = [ + item + for item in body.get("input", []) + if isinstance(item, dict) + and item.get("type") == "function_call_output" + ] + if outputs: + backend.outputs.append(str(outputs[-1].get("output"))) + item: dict[str, Any] = { + "type": "message", + "id": "msg", + "role": "assistant", + "status": "completed", + "content": [ + {"type": "output_text", "text": "done", "annotations": []} + ], + } + else: + item = { + "type": "function_call", + "id": "fc", + "call_id": "call_probe", + "name": "exec_command", + "arguments": json.dumps({"cmd": backend.command}), + "status": "completed", + } + usage = { + "input_tokens": 1, + "input_tokens_details": {"cached_tokens": 0}, + "output_tokens": 1, + "output_tokens_details": {"reasoning_tokens": 0}, + "total_tokens": 2, + } + events = ( + {"type": "response.created", "response": {"id": "resp"}}, + { + "type": "response.output_item.done", + "output_index": 0, + "item": item, + }, + { + "type": "response.completed", + "response": {"id": "resp", "usage": usage}, + }, + ) + self._send( + "text/event-stream", + b"".join( + f"event: {e['type']}\ndata: {json.dumps(e)}\n\n".encode() + for e in events + ), + ) + + def _send(self, content_type: str, data: bytes) -> None: + self.send_response(200) + self.send_header("content-type", content_type) + self.send_header("content-length", str(len(data))) + self.end_headers() + self.wfile.write(data) + + self.server = ThreadingHTTPServer(("127.0.0.1", 0), Handler) + threading.Thread(target=self.server.serve_forever, daemon=True).start() + self.url = f"http://127.0.0.1:{self.server.server_address[1]}/v1" + + +@pytest.mark.codex_smoke +@pytest.mark.asyncio +@pytest.mark.parametrize("sandbox", ["workspace_write", "read_only"]) +async def test_sandboxed_commands_reach_only_their_own_session( + sandbox: str, monkeypatch +) -> None: + if os.getenv("CODEX_RUN_SMOKE") != "1": + pytest.skip( + "set CODEX_RUN_SMOKE=1 to spawn the real Codex binary " + "(no model is called; the backend is stubbed)" + ) + from google.adk.runners import Runner + from google.adk.sessions.in_memory_session_service import ( + InMemorySessionService, + ) + from google.genai import types + + from veadk import Agent + from veadk.runtime import get_runtime + from veadk.runtime.codex import runtime as runtime_module + from veadk.runtime.codex.config import CodexRuntimeConfig + from veadk.runtime.codex.mcp_bridge import shutdown_bridge + + get_runtime.cache_clear() + # Another session's workspace (same root) and another invocation's home. + root = Path(runtime_module._SESSION_WORKSPACE_ROOT) + sibling = root / f"other-{uuid.uuid4().hex[:8]}" + sibling.mkdir() + (sibling / "secret.txt").write_text("SECRET") + home = Path(tempfile.mkdtemp(prefix="veadk-codex-")) + (home / "sessions").mkdir() + (home / "sessions" / "rollout.jsonl").write_text("{}\n") + (home / "config.toml").write_text("") + + # A workspace AGENTS.md is still loaded; Codex's search above the + # workspace (denied) once failed every `read_only` turn on Linux. + session_id = f"isolation-{uuid.uuid4().hex[:8]}" + ctx = SimpleNamespace( + session=SimpleNamespace(id=session_id, app_name="isolation", user_id="user"), + agent=SimpleNamespace(name="isolation_agent"), + ) + own = runtime_module._prepare_workspace(CodexRuntimeConfig(), ctx) # type: ignore[arg-type] + Path(own, "AGENTS.md").write_text("WORKSPACE-AGENTS-MARKER") + backend = _Backend("") + real_sync = runtime_module.sync_skills_to_codex_home + + def sync_with_probe_skill(agent: Any, codex_home: str, **kwargs: Any) -> int: + # The invocation's own CODEX_HOME is only known here. + written = real_sync(agent, codex_home, **kwargs) + skill = Path(codex_home, "skills", "probe_skill", "SKILL.md") + skill.parent.mkdir(parents=True, exist_ok=True) + skill.write_text("---\nname: probe_skill\ndescription: probe\n---\n") + backend.command = _probe_command(own, str(sibling), str(home), str(skill)) + return written + + monkeypatch.setattr( + runtime_module, "sync_skills_to_codex_home", sync_with_probe_skill + ) + agent = Agent( + name="isolation_agent", + description="Sandbox isolation probe.", + instruction="Run the probe.", + runtime="codex", + model_name="veadk-codex-probe-model", + model_api_base=backend.url, + model_api_key="probe-key", + model_api_key_name="", + codex_runtime_config=CodexRuntimeConfig( + model_transport="direct", + sandbox=sandbox, # type: ignore[arg-type] + network_access=False, + reasoning_effort="minimal", + ), + ) + service = InMemorySessionService() + await service.create_session( + app_name="isolation", user_id="user", session_id=session_id + ) + runner = Runner(app_name="isolation", agent=agent, session_service=service) + + async def _drive() -> None: + async for _ in runner.run_async( + user_id="user", + session_id=session_id, + new_message=types.Content(role="user", parts=[types.Part(text="probe")]), + ): + pass + + try: + await asyncio.wait_for(_drive(), 120) + finally: + await runtime_module._PROCESS_POOL.close_all() + await shutdown_bridge() + backend.server.shutdown() + shutil.rmtree(sibling, ignore_errors=True) + shutil.rmtree(home, ignore_errors=True) + + assert backend.outputs, "Codex never ran the probe command" + assert "WORKSPACE-AGENTS-MARKER" in backend.bodies[0], "workspace AGENTS.md lost" + got = dict( + token.split("=", 1) + for token in backend.outputs[-1].split() + if token.split("=", 1)[0] in _CHECKS + ) + assert got.get("own_read") == "0", got + assert got.get("skill_read") == "0", got + if sandbox == "workspace_write": + assert got.get("own_write") == "0", got + assert got.get("tmp_env") == "0", got # TMPDIR points into the workspace + elif not sys.platform.startswith("linux"): + # Linux grants it `write` to work around Codex's sandbox bug; see + # `runtime._nested_read_access`. + assert got.get("own_write") not in (None, "0"), got + for denied in ( + "sibling_read", + "sibling_write", + "home_read", + "home_write", + "tmpdir_write", + "slash_tmp_write", + ): + assert got.get(denied) not in (None, "0"), (denied, got) + if sys.platform.startswith("linux"): + # The VeADK process's environment holds the real credentials. + assert got.get("proc_environ") != "0", got diff --git a/tests/runtime/codex/test_codex_shim_rounds.py b/tests/runtime/codex/test_codex_shim_rounds.py index 2db9629fd..a01d1a239 100644 --- a/tests/runtime/codex/test_codex_shim_rounds.py +++ b/tests/runtime/codex/test_codex_shim_rounds.py @@ -1079,6 +1079,8 @@ async def boom(**kwargs): assert response.status_code == 400 assert response.json()["error"]["type"] == "invalid_request_error" + assert response.json()["error"]["message"] == "Backend rejected the request." + assert "not supported" not in json.dumps(response.json()) assert "sk-secret-key" not in json.dumps(response.json()) recorded = shim.turn_error(token) assert isinstance(recorded, litellm_exceptions.BadRequestError), recorded diff --git a/tests/runtime/codex/test_codex_thread_store.py b/tests/runtime/codex/test_codex_thread_store.py index 210ec273b..b8573d0b1 100644 --- a/tests/runtime/codex/test_codex_thread_store.py +++ b/tests/runtime/codex/test_codex_thread_store.py @@ -256,6 +256,25 @@ async def _make_store(kind: str, tmp_path: Path): STORE_KINDS = ["memory", "localdir", *DB_KINDS] +def test_independent_stores_have_distinct_stable_pool_namespaces(): + a, b = InMemoryThreadStore(), InMemoryThreadStore() + assert a.pool_namespace is a.pool_namespace + assert a.pool_namespace != b.pool_namespace + + +@pytest.mark.asyncio +@pytest.mark.parametrize("kind", STORE_KINDS) +async def test_current_identity_distinguishes_delete_recreate(kind, tmp_path): + async with _make_store(kind, tmp_path) as store: + assert await store.current_identity(KEY) is None + await store.save(KEY, TID, _rollout(), "h", expected_version=None) + assert await store.current_identity(KEY) == (TID, 1) + await store.delete(KEY) + other = str(uuid.uuid4()) + await store.save(KEY, other, _rollout(tid=other), "h", expected_version=None) + assert await store.current_identity(KEY) == (other, 1) + + @pytest.mark.asyncio @pytest.mark.parametrize("kind", STORE_KINDS) async def test_store_create_load_update(kind: str, tmp_path: Path) -> None: diff --git a/tests/runtime/codex/test_codex_tool_workloads.py b/tests/runtime/codex/test_codex_tool_workloads.py new file mode 100644 index 000000000..dedb8e09e --- /dev/null +++ b/tests/runtime/codex/test_codex_tool_workloads.py @@ -0,0 +1,134 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Batch contract checks and real Codex multi-turn tool workloads.""" + +import asyncio +import json +import os +import subprocess +import sys +from pathlib import Path + +import pytest + +from scripts.benchmark_codex_tools import bounded_batch_lookup + + +@pytest.mark.asyncio +async def test_batch_limits_admission_before_any_lookup(): + called = [] + + async def lookup(key): + called.append(key) + return {"key": key} + + with pytest.raises(ValueError): + await bounded_batch_lookup(["a", "b"], lookup, max_size=1) + assert called == [] + + +@pytest.mark.asyncio +async def test_batch_preserves_each_failure_and_timeout(): + async def lookup(key): + if key == "error": + raise ValueError("synthetic failure") + if key == "slow": + await asyncio.sleep(1) + return {"key": key} + + result = await bounded_batch_lookup( + ["ok", "error", "slow"], lookup, item_timeout=0.02 + ) + assert result["status"] == "partial_failure" + assert [r["status"] for r in result["results"]] == ["completed", "error", "error"] + assert result["results"][1]["error_type"] == "ValueError" + assert result["results"][2]["error_type"] in { + "TimeoutError", + "TimeoutErrorException", + } + + +@pytest.mark.asyncio +async def test_batch_cancellation_reaches_all_inflight_lookups(): + both_started = asyncio.Event() + started, cancelled = set(), set() + + async def lookup(key: str) -> dict[str, str]: + started.add(key) + if len(started) == 2: + both_started.set() + try: + await asyncio.sleep(60) + finally: + cancelled.add(key) + return {"key": key} + + task = asyncio.create_task(bounded_batch_lookup(["a", "b"], lookup)) + await both_started.wait() + task.cancel() + with pytest.raises(asyncio.CancelledError): + await task + assert cancelled == {"a", "b"} + + +@pytest.mark.codex_smoke +@pytest.mark.parametrize("scenario", ["single", "parallel", "serial", "batch"]) +@pytest.mark.parametrize("pool_size", [0, 20]) +def test_real_multi_turn_tool_workload(scenario, pool_size, tmp_path): + if os.getenv("CODEX_RUN_SMOKE") != "1": + pytest.skip( + "set CODEX_RUN_SMOKE=1; real Codex, loopback model, no provider tokens" + ) + pytest.importorskip("openai_codex") + root = Path(__file__).resolve().parents[3] + output = tmp_path / "result.json" + # A fresh process isolates the real SDK and all runtime state from the + # differential SDK doubles imported by other tests during collection. + proc = subprocess.run( + [ + sys.executable, + str(root / "scripts/benchmark_codex_tools.py"), + "--scenario", + scenario, + "--turns", + "3", + "--pool-size", + str(pool_size), + "--output", + str(output), + ], + cwd=root, + capture_output=True, + text=True, + timeout=90, + ) + assert proc.returncode == 0, proc.stdout + proc.stderr + report = json.loads(output.read_text()) + rows = report["rows"] + assert len(rows) == 3 and all(r["correct"] for r in rows) + assert [r["reuse_outcome"] for r in rows] == ["new_thread"] + ( + ["warm"] * 2 if pool_size else ["resumed"] * 2 + ) + if pool_size: + assert all(r["developer_messages"] == 1 for r in rows) + assert all(r["reset_warnings"] == 0 for r in rows) + requests = 3 if scenario == "serial" else 2 + assert all(r["model_requests"] == requests for r in rows) + assert all( + r["tool_calls"] == (2 if scenario in {"parallel", "serial"} else 1) + for r in rows + ) + assert report["peak_concurrency"] == (2 if scenario in {"parallel", "batch"} else 1) + assert report["cleanup"] == {"pool_idle": 0, "bridge_processes": 0} diff --git a/tests/runtime/codex/test_codex_turn_contract.py b/tests/runtime/codex/test_codex_turn_contract.py index 4a20a0859..8e25dd1e1 100644 --- a/tests/runtime/codex/test_codex_turn_contract.py +++ b/tests/runtime/codex/test_codex_turn_contract.py @@ -1111,7 +1111,13 @@ async def test_direct_transport_stops_the_turn_when_a_tool_needs_confirmation( async def _direct_session( - monkeypatch, plan, *, codex_class=None, instruction="Answer.", tools=None + monkeypatch, + plan, + *, + codex_class=None, + instruction="Answer.", + tools=None, + runtime_config=None, ): """A Runner on one session, on the direct transport, for multi-turn tests. @@ -1141,7 +1147,7 @@ async def _direct_session( model_api_base="https://backend.invalid/v1", model_api_key="backend-key", runtime="codex", - **_DIRECT, + codex_runtime_config={"model_transport": "direct", **(runtime_config or {})}, ) session_service = InMemorySessionService() session_id = f"session-{uuid.uuid4().hex[:8]}" @@ -1814,3 +1820,436 @@ async def purchase(item: str) -> dict: assert "function_call" in restored and "function_response" in restored assert "completed" in restored assert executions == ["book"] + + +# --------------------------------------------------------------------------- +# Warm Codex processes: one app-server per session thread, reused across turns +# --------------------------------------------------------------------------- + + +class _FakeProc: + """The pinned SDK's ``Popen`` as far as the process pool looks at it.""" + + def __init__(self) -> None: + self.returncode: int | None = None + + def poll(self) -> int | None: + return self.returncode + + +def _warm_capable_codex() -> type: + """A direct fake that exposes a live process, so the pool may keep it.""" + from types import SimpleNamespace + + base = fake_codex_sdk.DirectDrivingCodex.configured() + + class WarmCapableCodex(base): # type: ignore[misc, valid-type] + def __init__(self, **kwargs: Any) -> None: + super().__init__(**kwargs) + self.closed = False + self._client = SimpleNamespace( + _sync=SimpleNamespace(_proc=_FakeProc(), _reader_thread=None) + ) + + async def __aexit__(self, *exc: Any) -> None: + self.closed = True + self._client._sync._proc.returncode = 0 + + return WarmCapableCodex + + +async def _close_process_pool() -> None: + from veadk.runtime.codex import runtime as runtime_module + + await runtime_module._PROCESS_POOL.close_all() + + +@pytest.mark.asyncio +@pytest.mark.parametrize("disable_with", ["config", "environment"]) +async def test_disabled_tool_reuse_keeps_the_cold_process_path( + monkeypatch, + disable_with, +) -> None: + """Both public switches preserve per-invocation process/token ownership.""" + from veadk.runtime.codex.mcp_bridge import shutdown_bridge + + recorded: list[str] = [] + monkeypatch.delenv("VEADK_CODEX_MCP_PROCESS_REUSE", raising=False) + runtime_config = {"mcp_process_reuse": False} if disable_with == "config" else {} + if disable_with == "environment": + monkeypatch.setenv("VEADK_CODEX_MCP_PROCESS_REUSE", "0") + + def remember(fact: str) -> dict: + """Record a fact.""" + recorded.append(fact) + return {"ok": True} + + send, backend, codex_class, _ = await _direct_session( + monkeypatch, + ( + Round(text="first answer", usage=(1, 1)), + Round(tool_calls=(("remember", {"fact": "PAPAYA"}),), usage=(1, 1)), + Round(text="second answer", usage=(1, 1)), + ), + codex_class=_warm_capable_codex(), + tools=[remember], + runtime_config=runtime_config, + ) + try: + assert await send("remember the word PAPAYA") is None + assert await send("store it with the tool") is None + finally: + await _close_process_pool() + await shutdown_bridge() + + assert _starts_and_resumes(codex_class) == [(1, 0), (0, 1)] + assert recorded == ["PAPAYA"], "the warm thread lost the bridged tools" + second = json.dumps(backend.raw_requests[1], default=str) + assert "remember the word PAPAYA" in second, "the warm thread lost its history" + assert all(client.closed for client in codex_class.instances) + + +@pytest.mark.asyncio +async def test_tool_free_turns_still_reuse_their_warm_codex_process( + monkeypatch, +) -> None: + from veadk.runtime.codex.mcp_bridge import shutdown_bridge + + send, _, codex_class, _ = await _direct_session( + monkeypatch, + (Round(text="first", usage=(1, 1)), Round(text="second", usage=(1, 1))), + codex_class=_warm_capable_codex(), + ) + try: + assert await send("hello") is None + assert await send("again") is None + finally: + await _close_process_pool() + await shutdown_bridge() + + assert _starts_and_resumes(codex_class) == [(1, 0)] + assert all(client.closed for client in codex_class.instances) + + +@pytest.mark.asyncio +async def test_default_tool_turns_reuse_and_rebind_invocation(monkeypatch): + from veadk.runtime.codex import runtime as runtime_module + from veadk.runtime.codex.mcp_bridge import shutdown_bridge + + monkeypatch.delenv("VEADK_CODEX_MCP_PROCESS_REUSE", raising=False) + recorded = [] + + def remember(fact: str) -> dict: + """Record a synthetic fact.""" + recorded.append(fact) + return {"ok": True} + + send, _, codex_class, _ = await _direct_session( + monkeypatch, + tuple( + r + for fact in ("ALPHA", "BETA", "GAMMA") + for r in ( + Round(tool_calls=(("remember", {"fact": fact}),), usage=(1, 1)), + Round(text=fact, usage=(1, 1)), + ) + ), + codex_class=_warm_capable_codex(), + tools=[remember], + runtime_config={"max_tool_iterations": 1}, + ) + try: + for fact in ("ALPHA", "BETA", "GAMMA"): + assert await send(f"store {fact}") is None + assert _starts_and_resumes(codex_class) == [(1, 0)] + assert recorded == ["ALPHA", "BETA", "GAMMA"] + assert len(runtime_module._PROCESS_POOL._idle) == 1 + calls = codex_class.instances[0].mcp_calls + assert len({c["meta"]["x-codex-turn-metadata"]["turn_id"] for c in calls}) == 3 + finally: + await _close_process_pool() + await shutdown_bridge() + + +@pytest.mark.asyncio +async def test_independent_store_same_session_never_reuses_process(monkeypatch): + from veadk.runtime.codex.mcp_bridge import shutdown_bridge + from veadk.runtime.codex.thread_store import InMemoryThreadStore + + a, b = InMemoryThreadStore(), InMemoryThreadStore() + send, _, codex_class, _ = await _direct_session( + monkeypatch, + (Round(text="first", usage=(1, 1)), Round(text="second", usage=(1, 1))), + codex_class=_warm_capable_codex(), + ) + try: + _use_thread_store(monkeypatch, a) + assert await send("hello") is None + _use_thread_store(monkeypatch, b) + assert await send("hello in another store") is None + assert _starts_and_resumes(codex_class) == [(1, 0), (1, 0)] + finally: + await _close_process_pool() + await shutdown_bridge() + + +@pytest.mark.asyncio +async def test_cancelling_warm_mcp_turn_revokes_credentials_and_executors(monkeypatch): + from veadk.runtime.codex import runtime as runtime_module + from veadk.runtime.codex.mcp_bridge import get_bridge, shutdown_bridge + + monkeypatch.setenv("VEADK_CODEX_MCP_PROCESS_REUSE", "1") + started, cancelled = asyncio.Event(), asyncio.Event() + + async def slow() -> dict: + """Wait for the synthetic operation.""" + started.set() + try: + await asyncio.sleep(60) + finally: + cancelled.set() + return {"ok": True} + + send, _, codex_class, _ = await _direct_session( + monkeypatch, + ( + Round(text="first", usage=(1, 1)), + Round(tool_calls=(("slow", {}),), usage=(1, 1)), + ), + codex_class=_warm_capable_codex(), + tools=[slow], + ) + try: + assert await send("first") is None + bridge = await get_bridge() + task = asyncio.create_task(send("wait")) + await asyncio.wait_for(started.wait(), 5) + task.cancel() + assert isinstance(await asyncio.wait_for(task, 5), asyncio.CancelledError) + assert cancelled.is_set() + assert not runtime_module._PROCESS_POOL._idle + assert not bridge._turns and not bridge._processes + assert all(client.closed for client in codex_class.instances) + finally: + await _close_process_pool() + await shutdown_bridge() + + +@pytest.mark.asyncio +async def test_zero_pool_overrides_mcp_reuse_opt_in(monkeypatch): + from veadk.runtime.codex.mcp_bridge import shutdown_bridge + + monkeypatch.setenv("VEADK_CODEX_MCP_PROCESS_REUSE", "1") + monkeypatch.setenv("VEADK_CODEX_PROCESS_POOL_SIZE", "0") + send, _, codex_class, _ = await _direct_session( + monkeypatch, + (Round(text="first", usage=(1, 1)), Round(text="second", usage=(1, 1))), + codex_class=_warm_capable_codex(), + tools=[record_fact], + ) + try: + assert await send("first") is None + assert await send("second") is None + assert _starts_and_resumes(codex_class) == [(1, 0), (0, 1)] + assert all(client.closed for client in codex_class.instances) + finally: + await _close_process_pool() + await shutdown_bridge() + + +@pytest.mark.asyncio +async def test_another_writer_invalidates_the_warm_codex_process(monkeypatch) -> None: + """A store record this process did not write is resumed cold, not ignored. + + Another instance serving the same session advanced the thread, so the warm + copy is stale: it is closed and the next turn resumes the stored rollout. + """ + from veadk.runtime.codex.mcp_bridge import shutdown_bridge + from veadk.runtime.codex.thread_store import InMemoryThreadStore + + store = InMemoryThreadStore() + send, _, codex_class, _ = await _direct_session( + monkeypatch, + (Round(text="first", usage=(1, 1)), Round(text="second", usage=(1, 1))), + codex_class=_warm_capable_codex(), + ) + _use_thread_store(monkeypatch, store) + try: + assert await send("hello") is None + key = next(iter(store._records)) + record = await store.load(key) + assert record is not None + await store.save( + key, + record.thread_id, + record.rollout, + record.instruction_hash, + expected_version=record.version, + covered_invocation_id=record.covered_invocation_id, + ) + assert await send("again") is None + finally: + await _close_process_pool() + await shutdown_bridge() + + assert _starts_and_resumes(codex_class) == [(1, 0), (0, 1)] + assert codex_class.instances[0].closed, "the stale process was left running" + + +@pytest.mark.asyncio +async def test_process_pool_size_zero_keeps_one_process_per_invocation( + monkeypatch, +) -> None: + from veadk.runtime.codex.mcp_bridge import shutdown_bridge + + monkeypatch.setenv("VEADK_CODEX_PROCESS_POOL_SIZE", "0") + send, _, codex_class, _ = await _direct_session( + monkeypatch, + (Round(text="first", usage=(1, 1)), Round(text="second", usage=(1, 1))), + codex_class=_warm_capable_codex(), + ) + try: + assert await send("hello") is None + assert await send("again") is None + finally: + await _close_process_pool() + await shutdown_bridge() + + assert _starts_and_resumes(codex_class) == [(1, 0), (0, 1)] + assert all(client.closed for client in codex_class.instances) + + +@pytest.mark.asyncio +async def test_tool_setup_failure_releases_the_turn_slot(monkeypatch) -> None: + """A failure after admission must not strand the process-wide permit.""" + from veadk.runtime.codex import runtime as runtime_module + from veadk.runtime.codex.turn_control import TurnSlots + + slots = TurnSlots() + monkeypatch.setattr(runtime_module, "_TURN_SLOTS", slots) + monkeypatch.setenv("VEADK_CODEX_MAX_CONCURRENT_TURNS", "1") + + async def fail_tool_setup(*args: Any, **kwargs: Any) -> Any: + raise RuntimeError("simulated tool setup failure") + + monkeypatch.setattr(runtime_module, "build_executable_tools", fail_tool_setup) + _events, _session, _backend, error = await _run_turn( + monkeypatch, + plan=(Round(text="never reached"),), + agent_kwargs={ + "codex_runtime_config": { + "turn_timeout_seconds": 0.05, + } + }, + ) + assert isinstance(error, RuntimeError), error + assert str(error) == "simulated tool setup failure" + + async def acquire_from_another_task() -> None: + async with slots.hold(timeout=0.05): + pass + + # Child tasks inherit ContextVars, so this also proves reentrancy is + # granted by exact task identity rather than inherited context alone. + await asyncio.create_task(acquire_from_another_task()) + + +@pytest.mark.asyncio +async def test_turn_admission_times_out_before_allocating_resources( + monkeypatch, +) -> None: + """Queued invocations spend no workspace, home, tool, or session resources.""" + from veadk.runtime.codex import CodexTurnAdmissionTimeout + from veadk.runtime.codex import runtime as runtime_module + from veadk.runtime.codex.turn_control import TurnSlots + + slots = TurnSlots() + monkeypatch.setattr(runtime_module, "_TURN_SLOTS", slots) + monkeypatch.setenv("VEADK_CODEX_MAX_CONCURRENT_TURNS", "1") + entered = asyncio.Event() + release = asyncio.Event() + + async def occupy_only_slot() -> None: + async with slots.hold(): + entered.set() + await release.wait() + + holder = asyncio.create_task(occupy_only_slot()) + await entered.wait() + + touched: list[str] = [] + + def forbidden(name: str): + def fail(*args: Any, **kwargs: Any) -> Any: + touched.append(name) + raise AssertionError(f"{name} ran before turn admission") + + return fail + + async def forbidden_async(name: str, *args: Any, **kwargs: Any) -> Any: + touched.append(name) + raise AssertionError(f"{name} ran before turn admission") + + monkeypatch.setattr(runtime_module, "get_bridge", lambda: forbidden_async("bridge")) + monkeypatch.setattr(runtime_module, "_prepare_workspace", forbidden("workspace")) + monkeypatch.setattr(runtime_module, "_prepare_codex_home", forbidden("home")) + monkeypatch.setattr( + runtime_module, + "build_executable_tools", + lambda *args, **kwargs: forbidden_async("tools", *args, **kwargs), + ) + monkeypatch.setattr(runtime_module._SESSION_LOCKS, "hold", forbidden("session")) + + try: + _events, _session, _backend, error = await _run_turn( + monkeypatch, + plan=(Round(text="never reached"),), + agent_kwargs={ + "codex_runtime_config": { + "model_transport": "direct", + "turn_timeout_seconds": 0.05, + } + }, + ) + finally: + release.set() + await holder + + assert isinstance(error, CodexTurnAdmissionTimeout), error + assert error.timeout == 0.05 + assert touched == [] + + +@pytest.mark.asyncio +async def test_turn_setup_uses_the_same_admission_deadline(monkeypatch) -> None: + """Slow setup fails before a process starts and releases its admitted slot.""" + from veadk.runtime.codex import CodexTurnAdmissionTimeout + from veadk.runtime.codex import runtime as runtime_module + from veadk.runtime.codex.turn_control import TurnSlots + + slots = TurnSlots() + monkeypatch.setattr(runtime_module, "_TURN_SLOTS", slots) + monkeypatch.setenv("VEADK_CODEX_MAX_CONCURRENT_TURNS", "1") + + async def slow_tool_setup(*args: Any, **kwargs: Any) -> Any: + await asyncio.sleep(0.2) + raise AssertionError("setup continued past the shared deadline") + + monkeypatch.setattr(runtime_module, "build_executable_tools", slow_tool_setup) + _events, _session, _backend, error = await _run_turn( + monkeypatch, + plan=(Round(text="never reached"),), + agent_kwargs={ + "codex_runtime_config": { + "turn_timeout_seconds": 0.05, + } + }, + ) + + assert isinstance(error, CodexTurnAdmissionTimeout), error + + async def acquire_from_another_task() -> None: + async with slots.hold(timeout=0.05): + pass + + await asyncio.create_task(acquire_from_another_task()) diff --git a/tests/runtime/codex/test_codex_turn_control.py b/tests/runtime/codex/test_codex_turn_control.py index 2a401ecb0..83f4a22cf 100644 --- a/tests/runtime/codex/test_codex_turn_control.py +++ b/tests/runtime/codex/test_codex_turn_control.py @@ -30,6 +30,7 @@ import os import sys import tempfile +import threading import time import uuid from pathlib import Path @@ -1008,3 +1009,144 @@ async def test_real_compact_and_wait_then_new_turn_succeeds() -> None: for t in read.thread.turns ] assert ["contextCompaction"] in kinds + + +@pytest.mark.asyncio +async def test_turn_slots_cap_concurrent_turns_but_not_nested_ones(monkeypatch): + """Over the cap invocations queue; a nested one in the holder's task does not.""" + from veadk.runtime.codex.turn_control import MAX_CONCURRENT_TURNS_ENV, TurnSlots + + monkeypatch.setenv(MAX_CONCURRENT_TURNS_ENV, "1") + slots = TurnSlots() + running = 0 + peak = 0 + + async def turn() -> None: + nonlocal running, peak + async with slots.hold(): + running += 1 + peak = max(peak, running) + async with slots.hold(): # e.g. a transferred-to Codex agent + await asyncio.sleep(0.01) + running -= 1 + + await asyncio.wait_for(asyncio.gather(turn(), turn(), turn()), 5) + assert peak == 1 + + monkeypatch.delenv(MAX_CONCURRENT_TURNS_ENV) + running = peak = 0 + await asyncio.gather(turn(), turn()) + assert peak == 2, "no cap unless configured" + + +@pytest.mark.asyncio +async def test_turn_slots_child_task_does_not_inherit_reentrant_access(monkeypatch): + from veadk.runtime.codex.turn_control import MAX_CONCURRENT_TURNS_ENV, TurnSlots + + monkeypatch.setenv(MAX_CONCURRENT_TURNS_ENV, "1") + slots = TurnSlots() + child_entered = asyncio.Event() + + async with slots.hold(): + child = asyncio.create_task(_enter_slot(slots, child_entered)) + await asyncio.sleep(0.05) + assert not child_entered.is_set() + await asyncio.wait_for(child, 1) + assert child_entered.is_set() + + +async def _enter_slot(slots, entered) -> None: + async with slots.hold(): + entered.set() + + +def test_turn_slots_limit_is_shared_across_event_loops(monkeypatch) -> None: + from veadk.runtime.codex.turn_control import MAX_CONCURRENT_TURNS_ENV, TurnSlots + + monkeypatch.setenv(MAX_CONCURRENT_TURNS_ENV, "1") + slots = TurnSlots() + first_entered = threading.Event() + release_first = threading.Event() + second_entered = threading.Event() + + def run_first() -> None: + async def first() -> None: + async with slots.hold(): + first_entered.set() + await asyncio.to_thread(release_first.wait) + + asyncio.run(first()) + + def run_second() -> None: + async def second() -> None: + async with slots.hold(): + second_entered.set() + + asyncio.run(second()) + + one = threading.Thread(target=run_first) + two = threading.Thread(target=run_second) + one.start() + assert first_entered.wait(1) + two.start() + assert not second_entered.wait(0.1) + release_first.set() + one.join(2) + two.join(2) + assert second_entered.is_set() + assert not one.is_alive() and not two.is_alive() + + +@pytest.mark.asyncio +async def test_turn_slots_timeout_and_cancelled_waiters_release_cleanly(monkeypatch): + from veadk.runtime.codex.turn_control import ( + MAX_CONCURRENT_TURNS_ENV, + CodexTurnAdmissionTimeout, + TurnSlots, + ) + + monkeypatch.setenv(MAX_CONCURRENT_TURNS_ENV, "1") + slots = TurnSlots() + + async with slots.hold(): + with pytest.raises(CodexTurnAdmissionTimeout): + await asyncio.create_task(_wait_for_slot(slots, timeout=0.01)) + + cancelled = asyncio.create_task(_wait_for_slot(slots)) + await asyncio.sleep(0) + cancelled.cancel() + with pytest.raises(asyncio.CancelledError): + await cancelled + + # Neither waiter consumed nor leaked the permit released above. + await asyncio.wait_for( + asyncio.create_task(_wait_for_slot(slots, timeout=0.1)), timeout=1 + ) + + +@pytest.mark.asyncio +async def test_turn_slots_cancel_reserved_waiter_hands_off_permit(monkeypatch): + from veadk.runtime.codex.turn_control import MAX_CONCURRENT_TURNS_ENV, TurnSlots + + monkeypatch.setenv(MAX_CONCURRENT_TURNS_ENV, "1") + slots = TurnSlots() + holder = slots.hold() + await holder.__aenter__() + waiter = asyncio.create_task(_wait_for_slot(slots)) + await asyncio.sleep(0) + + # Release reserves the permit and schedules the cross-loop-safe callback. + # Cancelling before that callback runs must pass the permit onward. + await holder.__aexit__(None, None, None) + waiter.cancel() + with pytest.raises(asyncio.CancelledError): + await waiter + + await asyncio.wait_for( + asyncio.create_task(_wait_for_slot(slots, timeout=0.1)), timeout=1 + ) + + +async def _wait_for_slot(slots, *, timeout=None) -> None: + async with slots.hold(timeout=timeout): + pass diff --git a/tests/runtime/conformance/conformance_adapters.py b/tests/runtime/conformance/conformance_adapters.py index c6ae51dec..c5a19d279 100644 --- a/tests/runtime/conformance/conformance_adapters.py +++ b/tests/runtime/conformance/conformance_adapters.py @@ -339,25 +339,25 @@ async def thread_start(self, **kwargs: Any) -> Any: return await super().thread_start(**kwargs) original_home = runtime_module._prepare_codex_home - original_workspace = runtime_module._prepare_workspace + original_workspace = runtime_module._prepare_workspace_state def recording_home(*args: Any, **kwargs: Any) -> str: home = original_home(*args, **kwargs) adapter.codex_homes.append(home) return home - def recording_workspace(*args: Any, **kwargs: Any) -> str: - workspace = original_workspace(*args, **kwargs) + def recording_workspace(*args: Any, **kwargs: Any) -> tuple[str, bool]: + workspace, created = original_workspace(*args, **kwargs) if workspace not in adapter.workspaces: adapter.workspaces.append(workspace) - return workspace + return workspace, created mp = self.monkeypatch mp.setattr("veadk.runtime.codex.proxy.litellm.aresponses", route) mp.setattr(runtime_module, "get_shim", fake_get_shim) mp.setattr(runtime_module, "AsyncCodex", _SkillSnoopingCodex) mp.setattr(runtime_module, "_prepare_codex_home", recording_home) - mp.setattr(runtime_module, "_prepare_workspace", recording_workspace) + mp.setattr(runtime_module, "_prepare_workspace_state", recording_workspace) def teardown(self) -> None: from veadk.runtime import get_runtime diff --git a/tests/runtime/differential/fake_codex_sdk.py b/tests/runtime/differential/fake_codex_sdk.py index a31738638..2a17e0023 100644 --- a/tests/runtime/differential/fake_codex_sdk.py +++ b/tests/runtime/differential/fake_codex_sdk.py @@ -272,8 +272,11 @@ def make_notification(name: str, payload: dict[str, Any]) -> Any: class _Note: """The ``note`` wrapper the SDK stream yields; the runtime reads ``payload``.""" - def __init__(self, payload: Any) -> None: + def __init__(self, payload: Any, method: str = "") -> None: self.payload = payload + #: The JSON-RPC method, as on the SDK's ``Notification`` (only + #: ``turn/completed`` is read, by ``TurnCompletion``). + self.method = method class ShimDrivingCodex: @@ -842,6 +845,7 @@ def __init__(self, *, config: Any, model_call: Any = None) -> None: self.requests: list[dict[str, Any]] = [] #: Every MCP ``tools/call`` this client issued. self.mcp_calls: list[dict[str, Any]] = [] + self._mcp_request_seed = 0 #: Every notification payload this client streamed, in order. self.notifications: list[Any] = [] self.threads: list[_DirectThread] = [] @@ -1486,7 +1490,10 @@ def _note(self, name: str, payload: dict[str, Any]) -> _Note: payload.setdefault("completed_at_ms", _now_ms()) elif name == "ReasoningSummaryTextDeltaNotification": payload.setdefault("summary_index", 0) - note = _Note(make_notification(name, payload)) + note = _Note( + make_notification(name, payload), + "turn/completed" if name == "TurnCompletedNotification" else "", + ) self.client.notifications.append(note.payload) return note @@ -1586,6 +1593,10 @@ async def _connect(self, stack: Any) -> dict[str, _McpServer]: streamable_http_client(str(config["url"]), http_client=http) ) session = await stack.enter_async_context(ClientSession(read, write)) + # The fake reconnects each turn; pinned Codex keeps the MCP client + # for its process. Preserve process-wide request identity here too. + session._request_id = self.client._mcp_request_seed + self.client._mcp_request_seed += 65536 await session.initialize() server = _McpServer(name, config, session) enabled = config.get("enabled_tools") diff --git a/veadk/cli/cli_mpa.py b/veadk/cli/cli_mpa.py index c5cf04173..dd8835ea2 100644 --- a/veadk/cli/cli_mpa.py +++ b/veadk/cli/cli_mpa.py @@ -360,7 +360,7 @@ def _load_config_default_map( help=( "YAML config supplying option defaults (e.g. PG/OpenViking settings and " "secrets). Explicit CLI options override it. The file must NOT be " - "committed to git; see mpa-create.config.example.yaml." + "committed to git; see the managed MPA README for YAML examples." ), ) @click.option("--image", required=True, help="Prebuilt mpa-agent container image URL.") diff --git a/veadk/integrations/agentkit/app.py b/veadk/integrations/agentkit/app.py index 49a33cfa1..72bd41a44 100644 --- a/veadk/integrations/agentkit/app.py +++ b/veadk/integrations/agentkit/app.py @@ -20,6 +20,7 @@ import inspect import json import os +import sys import threading import traceback from collections.abc import AsyncIterator, Callable, Iterable, Mapping @@ -427,6 +428,27 @@ async def lifespan(fastapi_app: FastAPI): app.router.lifespan_context = lifespan +def _configure_codex_runtime_shutdown(app: FastAPI) -> None: + """Close warm Codex processes, the MCP bridge and shims on shutdown. + + Only when the Codex runtime was loaded: an app that never ran a Codex turn + must not import the optional SDK just to shut it down. + """ + original_lifespan = app.router.lifespan_context + + @asynccontextmanager + async def lifespan(fastapi_app: FastAPI): + async with original_lifespan(fastapi_app): + try: + yield + finally: + codex_runtime = sys.modules.get("veadk.runtime.codex.runtime") + if codex_runtime is not None: + await codex_runtime.shutdown_codex_runtime() + + app.router.lifespan_context = lifespan + + def _add_introspection_routes( app: FastAPI, root_agent: BaseAgent, @@ -833,8 +855,9 @@ async def event_generator(): ) + "\n\n" ) - except Exception as exc: # noqa: BLE001 - SSE surfaces errors as data. - yield f"data: {json.dumps({'error': str(exc)})}\n\n" + except Exception: # noqa: BLE001 - SSE surfaces failures as data. + logger.exception("dynamic_run_sse_failed") + yield f"data: {json.dumps({'error': 'agent execution failed'})}\n\n" return StreamingResponse(event_generator(), media_type="text/event-stream") @@ -885,8 +908,9 @@ async def event_generator(): ) + "\n\n" ) - except Exception as exc: # noqa: BLE001 - SSE surfaces errors as data. - yield f"data: {json.dumps({'error': str(exc)})}\n\n" + except Exception: # noqa: BLE001 - SSE surfaces failures as data. + logger.exception("dynamic_invoke_failed") + yield f"data: {json.dumps({'error': 'agent execution failed'})}\n\n" return StreamingResponse( event_generator(), @@ -1068,6 +1092,7 @@ def create_agentkit_app( _configure_feishu_lifecycle(fastapi_app, root_agent, short_term_memory) if harness_extension is not None: _configure_harness_extension_lifecycle(fastapi_app, harness_extension) + _configure_codex_runtime_shutdown(fastapi_app) _add_introspection_routes(fastapi_app, root_agent, names, agent_draft) _mount_webui(fastapi_app) _prioritize_platform_routes(fastapi_app) diff --git a/veadk/integrations/mpa/managed/README.md b/veadk/integrations/mpa/managed/README.md index c030797ca..d4dfdde95 100644 --- a/veadk/integrations/mpa/managed/README.md +++ b/veadk/integrations/mpa/managed/README.md @@ -35,8 +35,8 @@ managed: Historical CLI/YAML AK/SK mode remains compatible. Existing Tools/Sessions are not changed automatically. Old Studio pages sending credential fields must be -refreshed. See [the maintained contract](../../../../specs/studio-mpa-creation/README.md#con-17--bucket-only-iam-tos-mounts) -and [implementation/verification evidence](../../../../prd-spec/features/studio-mpa-tos-iam/2026-10-12-bucket-only.md). +refreshed. This section is the maintained reference for the bucket-only IAM TOS +mount behavior. ## Automatic PG setup (recommended for new installations) @@ -59,7 +59,7 @@ After creation submission, prepare/reuse `mpa_admin_workspace/mpa_admin_db`, the All Studio/CLI processes for a scope must use the **same durable bootstrap file path on one coordinator host**. Preserve this file across restarts and redeployments. The private SQLite file contains only intents and IDs, never passwords. Cancelled/failed jobs retain resources. If a create response is lost, retry discovers the tagged Workspace; if its outcome is still uncertain, inspect AIDAP and provide the matching ID. Do not delete state to force another creation. Confirmed IAM/parameter rejections can be retried after correction. No Workspace is automatically deleted. -Confirmed `CreateFailed` Workspaces no longer permanently block later automatic requests. The bootstrap validates their scope, retains their cloud resources and records their IDs separately before releasing the failed binding. Retry may create a replacement; a newly failed purchase is not repeated in the same task. Explicit Workspace IDs, operational failures and uncertain outcomes still require inspection rather than blind replacement. Previously retired failures cannot authorize repeating a newer timed-out request. See CON-13 in the [recovery contract](../../../../specs/studio-mpa-creation/README.md). +Confirmed `CreateFailed` Workspaces no longer permanently block later automatic requests. The bootstrap validates their scope, retains their cloud resources and records their IDs separately before releasing the failed binding. Retry may create a replacement; a newly failed purchase is not repeated in the same task. Explicit Workspace IDs, operational failures and uncertain outcomes still require inspection rather than blind replacement. Previously retired failures cannot authorize repeating a newer timed-out request. ### Independent business Workspace for new Studio agents @@ -68,7 +68,7 @@ New Studio forms submit `businessWorkspaceMode: per-agent` and `businessDatabase The first Next and final submission perform authorized, bounded name checks for Runtime, sandbox template and target Workspace. Same-request own resources may be reused; foreign/ambiguous collisions require another name, and query failures require rechecking. Active requests on one Studio host reserve their names by verified provider/account/region; failed requests do not hold a local reservation. The backend checks again before new-layout prerequisites. Read-only checks do not provide atomic exclusion across independent hosts; cloud ownership/uniqueness enforcement remains authoritative. -Explicit CLI/YAML profiles enable the new layout with `managed.postgres.mode: auto`, `business-workspace-mode: per-agent` and `business-database-name: aidb`, without a configured business Workspace ID. Omitting the database selection preserves the previous hashed per-agent layout; omitting both selects the original shared layout. Initial aidb adoption validates the dedicated Workspace, database owner, absence of non-system/non-extension relations and absence of a previous Runtime/ready binding, then transaction-locks and marks it. Missing, foreign or nonempty initial databases fail closed. `init-admin-db` migration remains a shared-layout operation. Runtime/skill/Feishu logic is unchanged; deleting MPA retains PG resources. See [design and verification](../../../../prd-spec/features/mpa-name-preflight/2026-10-13-runtime-name-and-workspace-labels.md). +Explicit CLI/YAML profiles enable the new layout with `managed.postgres.mode: auto`, `business-workspace-mode: per-agent` and `business-database-name: aidb`, without a configured business Workspace ID. Omitting the database selection preserves the previous hashed per-agent layout; omitting both selects the original shared layout. Initial aidb adoption validates the dedicated Workspace, database owner, absence of non-system/non-extension relations and absence of a previous Runtime/ready binding, then transaction-locks and marks it. Missing, foreign or nonempty initial databases fail closed. `init-admin-db` migration remains a shared-layout operation. Runtime/skill/Feishu logic is unchanged; deleting MPA retains PG resources. ### Existing installation cutover @@ -81,7 +81,7 @@ To preserve and migrate old relationships instead, keep the default `legacy-urls 3. Run `veadk mpa init-admin-db --config mpa-create.config.yaml --source-url-env OLD_SHARED_APIG_DATABASE_URL`. It prepares the Workspaces/management DB and copies registry records atomically using the migration rules below. It does not migrate business databases or alter running Runtime environments. 4. Verify copied resource bindings. Coordinate existing Runtime `SHARED_APIG_DATABASE_URL` updates to the new management database with the existing safe deployment procedure; remove the legacy registry URL from the Studio/CLI environment only after cutover is complete. Preserve old business credentials or explicitly verify the adopted connection before removing obsolete settings. Resume writers only after verification. Keep the source backup for rollback. -No live migration or cloud allocation is performed by the repository tests. See the [automatic PG design](../../../../prd-spec/features/mpa-space-scoped-resources/2026-09-23-auto-pg-workspaces.md) for limitations and verification. +No live migration or cloud allocation is performed by the repository tests. Treat live cutover and cloud allocation as separately authorized operations with isolated verification. ## Shared network and gateway preparation @@ -113,7 +113,7 @@ Runtime subnet IDs may be returned in a different order by AgentKit. An identica ## CLI YAML and manual / legacy server setup -1. Copy the [example YAML](../../../../prd-spec/features/mpa-agent-oneclick-provision/mpa-create.config.example.yaml) to a private `mpa-create.config.yaml`. Keep filled-in configuration outside Git. +1. Create a private `mpa-create.config.yaml` from the options and snippets in this README. Keep filled-in configuration outside Git. 2. Supply an existing PostgreSQL instance, shared registry database, database login/owner, Runtime/worker IAM roles, images and model access. The deployment administrator needs `CREATEDB` and permission to assign the business database owner. The registry needs schema-write permissions and direct/session pooling; transaction pooling is incompatible with advisory locks. 3. For manual CLI profiles, set `DEPLOYMENT_DATABASE_ADMIN_URL` and `SHARED_APIG_DATABASE_URL` in the CLI environment. These are PostgreSQL connection URLs; the YAML stores environment variable names. Flat/template secrets support complete `${ENV_NAME}` references. Do not expose these variables in browser configuration. 4. Configure deployment credentials through a rotating `managed.credential-file` or `VOLCENGINE_ACCESS_KEY` / `VOLCENGINE_SECRET_KEY` and optional `VOLCENGINE_SESSION_TOKEN`. Without an explicit file or environment key pair, the mounted `/var/run/secrets/iam/credential` is used. Each cloud call refreshes credentials; Runtime, VPC, worker and APIG use the same verified account. @@ -197,7 +197,7 @@ Studio stores nonsecret task state in `.adk/mpa-creation.sqlite3` (override with Cancellation is not cloud rollback. APIG/VPC, business database, Skill Space, worker and any Runtime already created remain for recovery; an in-flight cloud request may complete after cancellation. Do not delete shared resources to retry. Unknown create results use persisted intent and client tokens; conflicting unfinished configuration stops with an error. Success requires platform Ready **and** the application's `/readiness`. Task responses contain safe stage/error codes and resource IDs, never raw deployment logs or credentials. -See the [component contract](../../../../specs/studio-mpa-creation/README.md) and [verification record](../../../../prd-spec/features/mpa-agent-oneclick-provision/2026-09-20-studio-mpa-creation.md). Tests simulate cloud operations; live deployment requires separately supplied credentials and an isolated target. +The recovery rules in this section are the maintained operational contract. Tests simulate cloud operations; live deployment requires separately supplied credentials and an isolated target. ### Runtime display names @@ -289,7 +289,7 @@ New named Studio requests use the entered, validated Runtime name unchanged as t ### Automatic MPA runtime role -Built-in Studio prepares `IDRoleForArkClawShareAgent` in the verified deployment account before preparing resources. It reuses a compatible role and adds missing approved policy bindings; it never replaces trust or policy contents. Explicit CLI profiles default to `managed.iam.mode: existing`; `auto` opts in for fresh default-role configurations only. See the [permission baseline and recovery rules](../../../../prd-spec/features/mpa-runtime-iam/2026-10-09-managed-runtime-role.md). The Studio execution role needs `iam:CreatePolicy` in addition to GetRole/CreateRole/GetPolicy/ListAttachedRolePolicies/AttachRolePolicy. Redeploy or update an existing Studio execution policy before using this capability. Retry the same agent ID after administrator repair; cancellation retains shared IAM resources. +Built-in Studio prepares `IDRoleForArkClawShareAgent` in the verified deployment account before preparing resources. It reuses a compatible role and adds missing approved policy bindings; it never replaces trust or policy contents. Explicit CLI profiles default to `managed.iam.mode: existing`; `auto` opts in for fresh default-role configurations only. The Studio execution role needs `iam:CreatePolicy` in addition to GetRole/CreateRole/GetPolicy/ListAttachedRolePolicies/AttachRolePolicy. Redeploy or update an existing Studio execution policy before using this capability. Retry the same agent ID after administrator repair; cancellation retains shared IAM resources. ### Deployment account in Studio @@ -354,7 +354,7 @@ New BytePlus Studio requests select automatic personal-library preparation in `a The adapter uses `vikingdb.ap-southeast-1.byteplusapi.com`, POST `/api/openviking/{Action}`, signing region `ap-southeast-1`, API version `2025-06-09`, and deployment-account BytePlus AK/SK/STS. It creates `Version=developer`, `Source=volcengine`, VLM `seed-2-0-lite` and Embedding `skylark-embedding-vision`, without ModelVersion. Each model credential contains the ordinary ApiKey and its EndpointID. After READY it obtains the default Key without sending UserID and injects the existing four variables, using `https://api.vikingdb.ap-southeast-1.bytepluses.com/openviking`. Retry reuses the bound ResourceID, unknown create results block blind recreation, and cancellation/deletion retains the library. Secrets are not persisted in the resource registry. -Personal libraries only support the default user. The supplied overseas contract requires Bearer authentication and `x-tt-env: pre` for data requests. This repository prepares resources; `agentkit-mpa-agent` must separately implement those semantics before OV memory can work. These changes do not establish live cloud availability or change Beijing enterprise provisioning. See the [design](../../../../prd-spec/features/mpa-byteplus-openviking/2026-10-14-automatic-creation.md). +Personal libraries only support the default user. The supplied overseas contract requires Bearer authentication and `x-tt-env: pre` for data requests. This repository prepares resources; `agentkit-mpa-agent` must separately implement those semantics before OV memory can work. These changes do not establish live cloud availability or change Beijing enterprise provisioning. BytePlus inference EndpointID defaults (requested for trial, not verified overseas): @@ -363,14 +363,14 @@ BytePlus inference EndpointID defaults (requested for trial, not verified overse | VLM | `doubao-seed-2-0-lite-260428` | `OV_VLM_ENDPOINT_ID` | | Embedding | `doubao-embedding-vision-251215` | `OV_EMBEDDING_ENDPOINT_ID` | -The key must have access to both endpoints. Overrides accept 1–128 ASCII letters/digits/hyphens/underscores/periods, starting with a letter or digit. Explicit blank/invalid overrides fail before resource mutation. Restart Studio after changing server settings. Domestic parameters do not use these overrides. Endpoint creation/discovery is not part of this flow. Provider rejection is surfaced; there is no Coding Plan or AgentPlan fallback. A retry after an OV intent was recorded must use the same endpoints and source; a prior Coding Plan intent cannot be silently migrated or recreated. See [inference design](../../../../prd-spec/features/mpa-byteplus-openviking/2026-10-14-inference-endpoints.md). +The key must have access to both endpoints. Overrides accept 1–128 ASCII letters/digits/hyphens/underscores/periods, starting with a letter or digit. Explicit blank/invalid overrides fail before resource mutation. Restart Studio after changing server settings. Domestic parameters do not use these overrides. Endpoint creation/discovery is not part of this flow. Provider rejection is surfaced; there is no Coding Plan or AgentPlan fallback. A retry after an OV intent was recorded must use the same endpoints and source; a prior Coding Plan intent cannot be silently migrated or recreated. ### IAM temporary failures Runtime role preparation retries timeout, connection, throttling and unavailable failures at most four times with 1/2/4-second waits, inside the existing 120-second role deadline. Each retry reads the fixed role/policy names before writing, so a lost response cannot blindly recreate resources. Permission, invalid input, unknown errors and ownership/trust/policy conflicts fail immediately. Exhausted transient requests use `iamTemporaryFailure`; `iamPermissionDenied` continues to indicate deployment authorization failures. Cancellation stops subsequent calls; an already dispatched SDK request may still finish. -Validated IAM operation/category/attempt/outcome and optional provider Request ID are stored in `task_diagnostics.request_id` and server logs; legacy rows receive an empty value through additive migration. Original messages, request bodies and credentials are excluded. Request IDs must be 16–128 hexadecimal characters/hyphens; unrecognized values are omitted. The previous four-field diagnostic protocol remains valid. To inspect, select `operation, category, attempt, outcome, request_id` from the task diagnostics for the affected task. Restart Studio to load this change. See [IAM recovery design](../../../../prd-spec/bugfixes/mpa-iam-retry/2026-10-14-transient-errors.md). +Validated IAM operation/category/attempt/outcome and optional provider Request ID are stored in `task_diagnostics.request_id` and server logs; legacy rows receive an empty value through additive migration. Original messages, request bodies and credentials are excluded. Request IDs must be 16–128 hexadecimal characters/hyphens; unrecognized values are omitted. The previous four-field diagnostic protocol remains valid. To inspect, select `operation, category, attempt, outcome, request_id` from the task diagnostics for the affected task. Restart Studio to load this change. OpenViking failures write safe operation/category/errorCode/requestId diagnostics to the Studio backend log and bounded task_diagnostics records when structured provider metadata is available. errorCode/requestId are optional; transport errors may provide neither. Raw provider messages, request/response bodies and API keys are excluded. Load the updated backend before reproducing; existing uncertain-create tasks remain guarded and are not automatically redispatched. Historical error metadata cannot be recovered. @@ -388,10 +388,9 @@ BytePlus `veadk studio deploy` automatically sets function-role credentials. Local AK/SK usage remains unchanged. Redeploy existing installations to apply this setting and refresh the default Studio role policy, which now includes the three OV provisioning operations on both providers. Custom roles -need equivalent authorization separately. See the maintained -[cloud IAM contract](../../../../specs/studio-mpa-creation/README.md#cloud-studio-mpa-iam-credentials-and-openviking-permissions) -for credential failure behavior and prerequisites. +need equivalent authorization separately. Credential failure behavior and +prerequisites are maintained in this section. ### Studio image freshness -Studio resolves each configured `latest` anonymously to an already-published tag in the same public repository with the exact same manifest digest. It persists both selections before creating the Worker or Runtime; retries verify the saved tags even if latest moves. No registry write permission, image copy, or extra pipeline is required. Every latest publication must retain a matching version tag and must not overwrite that tag. No matching tag, unavailable public read access, invalid/oversized pagination, changed saved content, or legacy unfinished intents without a selection fail explicitly. Fixed tags, existing agents, and standalone CLI deployments are unchanged. Discovery uses only anonymous pull tokens and bounded GET requests, with no cloud credentials. See the [read-only contract](../../../../specs/studio-mpa-creation/README.md#studio-latest-content-snapshots-2026-10-21). +Studio resolves each configured `latest` anonymously to an already-published tag in the same public repository with the exact same manifest digest. It persists both selections before creating the Worker or Runtime; retries verify the saved tags even if latest moves. No registry write permission, image copy, or extra pipeline is required. Every latest publication must retain a matching version tag and must not overwrite that tag. No matching tag, unavailable public read access, invalid/oversized pagination, changed saved content, or legacy unfinished intents without a selection fail explicitly. Fixed tags, existing agents, and standalone CLI deployments are unchanged. Discovery uses only anonymous pull tokens and bounded GET requests, with no cloud credentials. diff --git a/veadk/integrations/mpa/managed/README.zh.md b/veadk/integrations/mpa/managed/README.zh.md index 944621101..bf5c3f421 100644 --- a/veadk/integrations/mpa/managed/README.zh.md +++ b/veadk/integrations/mpa/managed/README.zh.md @@ -29,8 +29,7 @@ managed: ``` 历史 CLI/YAML AK/SK 模式保持兼容,不自动修改已有 Tool/Session。仍发送凭据字段的 -旧 Studio 页面需刷新。参见[维护契约](../../../../specs/studio-mpa-creation/README.zh.md#con-17--仅选桶-iam-tos-挂载) -及[实现与验证记录](../../../../prd-spec/features/studio-mpa-tos-iam/2026-10-12-bucket-only.zh.md)。 +旧 Studio 页面需刷新。本节是仅选桶 IAM TOS 挂载行为的维护来源。 ## 自动准备 PG(新部署推荐) @@ -53,7 +52,7 @@ managed: 同一范围的所有 Studio/CLI 进程必须使用**同一协调主机上的同一持久引导文件路径**,重启和重新部署时须保留。私有 SQLite 文件仅保存意图和 ID,不保存密码。任务取消/失败保留资源。创建响应丢失后,重试会发现带标签的 Workspace;结果仍不明确时,应检查 AIDAP 并配置匹配的 ID。不要通过删除状态强制再次创建。确定的 IAM/参数拒绝可修正后重试。不自动删除任何 Workspace。 -明确为 `CreateFailed` 的 Workspace 不再永久阻塞后续自动创建请求。流程核验其范围,保留失败云资源,将 ID 单独记录后解除失败绑定。重试可创建替代资源,本次新购买失败不会在同一任务内重复购买。显式 Workspace ID、运行故障和未知结果仍需核查,不能盲目替换。之前已退出的失败资源不能用于授权重发更新的超时请求。参见[恢复规则](../../../../specs/studio-mpa-creation/README.zh.md)的 CON-13。 +明确为 `CreateFailed` 的 Workspace 不再永久阻塞后续自动创建请求。流程核验其范围,保留失败云资源,将 ID 单独记录后解除失败绑定。重试可创建替代资源,本次新购买失败不会在同一任务内重复购买。显式 Workspace ID、运行故障和未知结果仍需核查,不能盲目替换。之前已退出的失败资源不能用于授权重发更新的超时请求。 ### Studio 新智能体独立业务 Workspace @@ -61,7 +60,7 @@ Studio 新表单提交 `businessWorkspaceMode: per-agent` 和 `businessDatabaseN 首次下一步和最终提交对 Runtime、沙箱模板及目标 Workspace 执行授权、有超时的名称检查。同请求自身资源可复用;其他/不明确归属重名要求改名,查询失败要求重新检查。同一 Studio 主机活跃请求按已核验提供商/账号/地域占用名称,失败请求不保留本地占位。后端在新布局前置资源准备前再次检查。只读检查不能保证独立主机之间的原子互斥,最终依赖云端归属/唯一性约束。 -显式 CLI/YAML 配置使用 `managed.postgres.mode: auto`、`business-workspace-mode: per-agent`、`business-database-name: aidb` 启用新布局,不配置业务 Workspace ID。省略数据库选择保持前一版独立 Workspace/哈希库,两个字段均省略则沿用原共享布局。接管初始 aidb 须核验独立 Workspace、数据库属主、无非系统/非扩展关系对象且无已有 Runtime/ready 绑定,再持事务锁写归属标记。缺失、其他归属或非空的初始库明确失败。`init-admin-db` 迁移仍是共享布局操作。Runtime/技能/飞书逻辑不变,删除 MPA 保留 PG 资源。见[设计与验证](../../../../prd-spec/features/mpa-name-preflight/2026-10-13-runtime-name-and-workspace-labels.zh.md)。 +显式 CLI/YAML 配置使用 `managed.postgres.mode: auto`、`business-workspace-mode: per-agent`、`business-database-name: aidb` 启用新布局,不配置业务 Workspace ID。省略数据库选择保持前一版独立 Workspace/哈希库,两个字段均省略则沿用原共享布局。接管初始 aidb 须核验独立 Workspace、数据库属主、无非系统/非扩展关系对象且无已有 Runtime/ready 绑定,再持事务锁写归属标记。缺失、其他归属或非空的初始库明确失败。`init-admin-db` 迁移仍是共享布局操作。Runtime/技能/飞书逻辑不变,删除 MPA 保留 PG 资源。 ### 已有部署切换 @@ -74,7 +73,7 @@ Studio 新表单提交 `businessWorkspaceMode: per-agent` 和 `businessDatabaseN 3. 执行 `veadk mpa init-admin-db --config mpa-create.config.yaml --source-url-env OLD_SHARED_APIG_DATABASE_URL`。它准备 Workspace/管理库,并按下文迁移规则原子复制注册记录,不搬迁业务库、不修改运行中 Runtime 的环境变量。 4. 核对复制后的资源绑定。按现有安全部署流程协调运行中 Runtime 的 `SHARED_APIG_DATABASE_URL` 切至新管理库;切换完成后才移除 Studio/CLI 环境中的旧注册库 URL。保留旧业务凭据,或在删除废弃配置前显式核验接管后的连接。验证后才恢复写入;保留源备份以便回滚。 -仓库测试不执行线上迁移或云资源分配。限制和验证情况参见[自动 PG 设计](../../../../prd-spec/features/mpa-space-scoped-resources/2026-09-23-auto-pg-workspaces.zh.md)。 +仓库测试不执行线上迁移或云资源分配;真实切换和云资源分配须单独授权,并在隔离目标上验证。 ## 共享网络与网关准备 @@ -100,7 +99,7 @@ AgentKit 返回的 Runtime 子网 ID 顺序可能不同。创建和重试接受 ## CLI YAML 与手动 / 旧模式服务端配置 -1. 复制[示例 YAML](../../../../prd-spec/features/mpa-agent-oneclick-provision/mpa-create.config.example.yaml) 到私有的 `mpa-create.config.yaml`。填写后的配置不要进入 Git。 +1. 根据本 README 中的选项和片段创建私有的 `mpa-create.config.yaml`。填写后的配置不要进入 Git。 2. 提供已有的 PostgreSQL 实例、共享注册数据库、数据库登录/属主角色、Runtime/worker IAM 角色、镜像和模型访问权限。部署管理员须有 `CREATEDB` 和指定业务库属主的权限。注册库须允许建表,并使用直连或会话级连接池;事务级连接池与 advisory lock 不兼容。 3. 手动 CLI 配置须在 CLI 环境中设置 `DEPLOYMENT_DATABASE_ADMIN_URL` 和 `SHARED_APIG_DATABASE_URL`。它们是 PostgreSQL 连接 URL;YAML 只保存环境变量名。平铺字段/模板中的密钥支持完整 `${ENV_NAME}` 引用。不要将这些变量暴露到浏览器配置。 4. 使用轮换的 `managed.credential-file`,或 `VOLCENGINE_ACCESS_KEY` / `VOLCENGINE_SECRET_KEY` 与可选 `VOLCENGINE_SESSION_TOKEN` 配置部署凭据。没有显式文件和环境密钥对时,使用挂载的 `/var/run/secrets/iam/credential`。每次云调用刷新凭据;Runtime、VPC、worker、APIG 使用同一经核验账号。 @@ -184,7 +183,7 @@ Studio 在 `.adk/mpa-creation.sqlite3` 保存不含密钥的任务状态(可 取消不是云资源回滚。已创建的 APIG/VPC、业务库、Skill Space、worker 和 Runtime 会保留以便恢复;进行中的云请求可能在取消后完成。不要通过删除共享资源来重试。创建结果未知时使用持久化意图和 client token;未完成配置发生冲突时停止并报错。成功要求平台 Ready **且**应用 `/readiness` 通过。任务响应只含安全阶段/错误码和资源 ID,不包含原始部署日志或凭据。 -参见[组件契约](../../../../specs/studio-mpa-creation/README.zh.md)和[验证记录](../../../../prd-spec/features/mpa-agent-oneclick-provision/2026-09-20-studio-mpa-creation.zh.md)。测试模拟云操作;真实部署需要另行提供凭据和隔离目标。 +本节的恢复规则是当前维护的操作契约。测试模拟云操作;真实部署需要另行提供凭据和隔离目标。 ### Runtime 显示名称 @@ -276,7 +275,7 @@ Flat 创建默认设置 `ENABLE_A2A=true` 和 `DISABLE_JWT_AUTH=true`。绕过 ### 自动准备 MPA 运行角色 -内置 Studio 在准备资源之前,在核验的部署账号下准备 `IDRoleForArkClawShareAgent`。复用兼容角色并补齐已批准策略绑定,不替换信任或策略内容。显式 CLI 配置默认 `managed.iam.mode: existing`;仅全新默认角色配置可通过 `auto` 启用。参见[权限基线与恢复规则](../../../../prd-spec/features/mpa-runtime-iam/2026-10-09-managed-runtime-role.zh.md)。Studio 执行角色除 GetRole/CreateRole/GetPolicy/ListAttachedRolePolicies/AttachRolePolicy 外,还需 `iam:CreatePolicy`。使用此能力前,重新部署或更新已有 Studio 执行策略。管理员修复后以同一智能体 ID 重试;取消保留共享 IAM 资源。 +内置 Studio 在准备资源之前,在核验的部署账号下准备 `IDRoleForArkClawShareAgent`。复用兼容角色并补齐已批准策略绑定,不替换信任或策略内容。显式 CLI 配置默认 `managed.iam.mode: existing`;仅全新默认角色配置可通过 `auto` 启用。Studio 执行角色除 GetRole/CreateRole/GetPolicy/ListAttachedRolePolicies/AttachRolePolicy 外,还需 `iam:CreatePolicy`。使用此能力前,重新部署或更新已有 Studio 执行策略。管理员修复后以同一智能体 ID 重试;取消保留共享 IAM 资源。 ### Studio 部署账号 @@ -341,7 +340,7 @@ BytePlus Studio 创建使用部署凭据,向 `apmplus-server.ap-southeast-1.by 适配器使用 `vikingdb.ap-southeast-1.byteplusapi.com`、POST `/api/openviking/{Action}`、签名地域 `ap-southeast-1`、API 版本 `2025-06-09`,及部署账号的 BytePlus AK/SK/STS。创建 `Version=developer`、`Source=volcengine`,VLM 为 `seed-2-0-lite`,Embedding 为 `skylark-embedding-vision`,不带 ModelVersion。每个模型凭据包含普通 ApiKey 和对应 EndpointID。READY 后不传 UserID 获取 default Key,注入已有四个变量,地址使用 `https://api.vikingdb.ap-southeast-1.bytepluses.com/openviking`。重试复用绑定的 ResourceID,创建结果未知时阻止盲目重复创建,取消/删除后保留库。密钥不持久化到资源注册库。 -个人库仅支持 default 用户。用户提供的海外契约要求数据请求使用 Bearer 鉴权和 `x-tt-env: pre`。本仓库负责准备资源;`agentkit-mpa-agent` 需单独实现上述语义,之后才能使用 OV 记忆。这些修改不证明云端实测可用,也不改变北京企业版创建流程。参见[设计](../../../../prd-spec/features/mpa-byteplus-openviking/2026-10-14-automatic-creation.zh.md)。 +个人库仅支持 default 用户。用户提供的海外契约要求数据请求使用 Bearer 鉴权和 `x-tt-env: pre`。本仓库负责准备资源;`agentkit-mpa-agent` 需单独实现上述语义,之后才能使用 OV 记忆。这些修改不证明云端实测可用,也不改变北京企业版创建流程。 BytePlus 推理 EndpointID 默认值(用户指定试用,海外尚未验证): @@ -350,14 +349,14 @@ BytePlus 推理 EndpointID 默认值(用户指定试用,海外尚未验证 | VLM | `doubao-seed-2-0-lite-260428` | `OV_VLM_ENDPOINT_ID` | | Embedding | `doubao-embedding-vision-251215` | `OV_EMBEDDING_ENDPOINT_ID` | -Key 必须有权调用两个接入点。覆盖值允许 1–128 位 ASCII 字母/数字/连字符/下划线/点,并以字母或数字开头。显式空值或非法覆盖值会在资源变更前失败。修改服务端配置后重启 Studio。国内参数不使用这些覆盖变量。流程不负责创建或发现推理接入点。服务端拒绝会明确报错,不回退 Coding Plan 或 AgentPlan。记录 OV 意图后重试必须使用相同接入点和来源;旧 Coding Plan 意图不能静默迁移或重新建库。见[推理凭据设计](../../../../prd-spec/features/mpa-byteplus-openviking/2026-10-14-inference-endpoints.zh.md)。 +Key 必须有权调用两个接入点。覆盖值允许 1–128 位 ASCII 字母/数字/连字符/下划线/点,并以字母或数字开头。显式空值或非法覆盖值会在资源变更前失败。修改服务端配置后重启 Studio。国内参数不使用这些覆盖变量。流程不负责创建或发现推理接入点。服务端拒绝会明确报错,不回退 Coding Plan 或 AgentPlan。记录 OV 意图后重试必须使用相同接入点和来源;旧 Coding Plan 意图不能静默迁移或重新建库。 ### IAM 临时失败 运行角色准备对超时、连接失败、限流和服务不可用最多尝试四次,等待 1/2/4 秒,受已有 120 秒角色期限限制。每次重试先读取固定角色/策略名称,不会因响应丢失而盲目重新创建资源。权限、非法输入、未知错误及归属/信任/策略冲突立即失败。临时请求耗尽返回 `iamTemporaryFailure`;`iamPermissionDenied` 仍表示部署授权不足。取消会停止后续调用;已发送的 SDK 请求可能仍会完成。 -经校验的 IAM 接口/类别/次数/结果及可选平台 Request ID 保存到 `task_diagnostics.request_id` 和服务端日志;旧记录通过增量迁移获得空值。原始消息、请求体和凭据不记录。Request ID 只允许 16–128 位十六进制字符/连字符,其他值忽略。旧四字段诊断协议继续有效。排查时可从受影响任务的诊断中查询 `operation, category, attempt, outcome, request_id`。重启 Studio 加载修改。见[IAM 恢复设计](../../../../prd-spec/bugfixes/mpa-iam-retry/2026-10-14-transient-errors.zh.md)。 +经校验的 IAM 接口/类别/次数/结果及可选平台 Request ID 保存到 `task_diagnostics.request_id` 和服务端日志;旧记录通过增量迁移获得空值。原始消息、请求体和凭据不记录。Request ID 只允许 16–128 位十六进制字符/连字符,其他值忽略。旧四字段诊断协议继续有效。排查时可从受影响任务的诊断中查询 `operation, category, attempt, outcome, request_id`。重启 Studio 加载修改。 OpenViking 失败时,若云端提供结构化元信息,Studio 后台日志及有数量上限的 task_diagnostics 记录会保留安全的 operation/category/errorCode/requestId。errorCode/requestId 为可选字段,传输异常可能没有这两个值。原始云端文案、请求/响应体和 API Key 均不记录。复现前须加载更新后的后端;已有不确定建库任务仍受保护,不会自动再次提交。历史错误元信息无法补回。 @@ -374,9 +373,8 @@ BytePlus `veadk studio deploy` 自动设置 `VEADK_MPA_BYTEPLUS_CREDENTIAL_FILE=/var/run/secrets/iam/credential`,使用轮转的 函数角色凭据,本地 AK/SK 用法不变。已有部署需重新部署以应用此设置并更新默认 Studio 角色策略,两个平台的策略均补齐三个 OV 准备操作。自定义角色需单独具备等价授权。 -凭据失败行为和前置条件见维护中的 -[云端 IAM 契约](../../../../specs/studio-mpa-creation/README.zh.md#云端-studio-mpa-iam-凭据与-openviking-权限)。 +凭据失败行为和前置条件由本节维护。 ### Studio 镜像新鲜度 -Studio 匿名解析配置的各个 `latest`,选择同一公共仓库中已发布且 manifest digest 完全相同的标签。在创建 Worker/Runtime 前持久化两个选择;即使 latest 移动,重试也验证保存的 tag。无需仓库写权限、镜像搬运或额外流水线。每次发布 latest 都必须保留匹配版本标签,并保证不覆盖。无匹配项、公共读取不可用、非法/过大分页、保存内容变更、无选择记录的旧未完成意图均明确失败。固定 tag、已有 Agent 和独立 CLI 部署不变。发现仅使用匿名 pull token 和有界 GET,不读取云凭据。见[只读契约](../../../../specs/studio-mpa-creation/README.zh.md#studio-latest-内容快照2026-10-21)。 +Studio 匿名解析配置的各个 `latest`,选择同一公共仓库中已发布且 manifest digest 完全相同的标签。在创建 Worker/Runtime 前持久化两个选择;即使 latest 移动,重试也验证保存的 tag。无需仓库写权限、镜像搬运或额外流水线。每次发布 latest 都必须保留匹配版本标签,并保证不覆盖。无匹配项、公共读取不可用、非法/过大分页、保存内容变更、无选择记录的旧未完成意图均明确失败。固定 tag、已有 Agent 和独立 CLI 部署不变。发现仅使用匿名 pull token 和有界 GET,不读取云凭据。 diff --git a/veadk/runtime/codex/__init__.py b/veadk/runtime/codex/__init__.py index 959ff58b7..367dfe268 100644 --- a/veadk/runtime/codex/__init__.py +++ b/veadk/runtime/codex/__init__.py @@ -25,17 +25,37 @@ from __future__ import annotations -from typing import Any +from typing import TYPE_CHECKING, Any from veadk.runtime.codex.config import CodexRuntimeConfig from veadk.runtime.codex.workspace import current_workspace -__all__ = ["CodexRuntime", "CodexRuntimeConfig", "current_workspace"] +if TYPE_CHECKING: + from veadk.runtime.codex.runtime import CodexRuntime + from veadk.runtime.codex.runtime import release_codex_session + from veadk.runtime.codex.runtime import shutdown_codex_runtime + from veadk.runtime.codex.turn_control import CodexTurnAdmissionTimeout + +__all__ = [ + "CodexRuntime", + "CodexRuntimeConfig", + "CodexTurnAdmissionTimeout", + "current_workspace", + "release_codex_session", + "shutdown_codex_runtime", +] + +_RUNTIME_LAZY = ("CodexRuntime", "release_codex_session", "shutdown_codex_runtime") +_TURN_CONTROL_LAZY = ("CodexTurnAdmissionTimeout",) def __getattr__(name: str) -> Any: - if name != "CodexRuntime": - raise AttributeError(name) - from veadk.runtime.codex.runtime import CodexRuntime + if name in _RUNTIME_LAZY: + from veadk.runtime.codex import runtime + + return getattr(runtime, name) + if name in _TURN_CONTROL_LAZY: + from veadk.runtime.codex import turn_control - return CodexRuntime + return getattr(turn_control, name) + raise AttributeError(name) diff --git a/veadk/runtime/codex/config.py b/veadk/runtime/codex/config.py index b677f821f..d50fcfcec 100644 --- a/veadk/runtime/codex/config.py +++ b/veadk/runtime/codex/config.py @@ -56,11 +56,11 @@ class CodexRuntimeConfig(BaseModel): escalation and file change without consulting a human or ADK. Only ``"deny_all"`` (the default) actually keeps Codex inside the sandbox. - Security note on ``network_access``: it is written to the - ``[sandbox_workspace_write]`` table of Codex's ``config.toml`` and is read - only by the ``workspace-write`` sandbox. The ``read-only`` and - ``danger-full-access`` sandboxes ignore it entirely, so it cannot restrict - (or grant) network access outside ``sandbox="workspace_write"``. + Security note on ``network_access``: it is written to the network table of + VeADK's Codex permissions profile and honoured only for + ``sandbox="workspace_write"``. ``read_only`` always runs without network and + ``danger-full-access`` has no profile, so it cannot restrict (or grant) + network access outside ``sandbox="workspace_write"``. """ approval_mode: Literal["deny_all", "auto_review"] = Field( @@ -83,6 +83,13 @@ class CodexRuntimeConfig(BaseModel): ) workspace_root: str | None = None reuse_workspace: bool = False + mcp_process_reuse: bool = Field( + default=True, + description=( + "Reuse same-thread MCP processes with per-invocation identity validation. " + "Set False to keep a fresh process and credential for each invocation." + ), + ) reasoning_effort: Literal["minimal", "low", "medium", "high", "xhigh"] = "medium" personality: Literal["none", "friendly", "pragmatic"] = "pragmatic" max_tool_iterations: int = Field( @@ -102,10 +109,12 @@ class CodexRuntimeConfig(BaseModel): default=1800.0, gt=0, description=( - "Upper bound for one Codex turn. Past it the turn is interrupted " - "and the invocation fails with a TimeoutError. Bounded by default: " - "a turn that never ends would also block its session's later " - "invocations, which queue behind it. None means no bound." + "Upper bound for Codex admission, setup, and one running turn. " + "Admission or setup expiry fails with CodexTurnAdmissionTimeout; " + "a running-turn expiry interrupts the turn and fails with " + "CodexTurnTimeout. Bounded by default because a turn that never " + "ends also blocks its session's later invocations. None means no " + "bound." ), ) auto_compact_token_limit: int | None = Field( @@ -152,9 +161,9 @@ def _normalize_workspace_root(cls, value: str | None) -> str | None: def _check_sandbox_network_consistency(self) -> "CodexRuntimeConfig": """Reject sandbox/network combinations that misstate the isolation. - ``network_access`` only reaches Codex through the - ``[sandbox_workspace_write]`` table, which ``danger-full-access`` and - ``read-only`` ignore. Silently accepting those combinations lets a + ``network_access`` only reaches Codex through the ``workspace_write`` + permissions profile, which ``danger-full-access`` and ``read-only`` + do not use. Silently accepting those combinations lets a config read as "no network" while granting full network, so the dangerous direction raises and the harmless one warns. @@ -206,6 +215,13 @@ def from_agent(cls, agent: object) -> "CodexRuntimeConfig": updates["model_transport"] = value.strip().lower() if value := os.getenv("VEADK_CODEX_THREAD_MODE"): updates["thread_mode"] = value.strip().lower() + if value := os.getenv("VEADK_CODEX_MCP_PROCESS_REUSE"): + updates["mcp_process_reuse"] = value.strip().lower() in { + "1", + "true", + "yes", + "on", + } if value := os.getenv("VEADK_CODEX_NETWORK_ACCESS"): updates["network_access"] = value.strip().lower() in { "1", diff --git a/veadk/runtime/codex/mcp_bridge.py b/veadk/runtime/codex/mcp_bridge.py index 00db3eead..a675df18b 100644 --- a/veadk/runtime/codex/mcp_bridge.py +++ b/veadk/runtime/codex/mcp_bridge.py @@ -25,6 +25,11 @@ :meth:`McpBridge.register_turn`: ``tools/list`` and ``tools/call`` only ever see the calling token's tools, and an unknown token is refused with HTTP 401. +Pooled processes retain a credential and immutable manifest, but no +idle executors. Their active invocation requires the SDK-owned thread/turn +identity and process-wide unique request IDs; closing admission drains tasks +before the credential can serve a later invocation. + Codex-specific facts this module relies on (verified against codex 0.159.2): - ``tools/call`` params carry ``_meta.callId`` -- the model's ``call_id`` -- @@ -90,6 +95,8 @@ _SCOPE_TURN = "veadk.mcp_bridge.turn" _SCOPE_SLOT = "veadk.mcp_bridge.slot" +_SCOPE_TOOLS = "veadk.mcp_bridge.tools" +_MAX_PROCESS_REQUESTS = 32768 @dataclass(frozen=True) @@ -122,6 +129,20 @@ class _Turn: state: BridgeTurnState = field(default_factory=BridgeTurnState) # JSON-RPC request id -> slot, so `notifications/cancelled` can find it. slots: dict[str, "_CallSlot"] = field(default_factory=dict) + closed: bool = False + thread_id: str | None = None + turn_id: str | None = None + activated: asyncio.Event = field(default_factory=asyncio.Event) + + +@dataclass +class _ProcessBinding: + """A process credential and manifest, with no invocation executors.""" + + tools: list[types.Tool] + thread_id: str | None = None + request_ids: set[str] = field(default_factory=set) + reusable: bool = True @dataclass @@ -230,6 +251,7 @@ class McpBridge: def __init__(self) -> None: self.url: str | None = None self._turns: dict[str, _Turn] = {} + self._processes: dict[str, _ProcessBinding] = {} self._turns_lock = threading.Lock() self._server: uvicorn.Server | None = None self._task: asyncio.Task[Any] | None = None @@ -242,6 +264,61 @@ def __init__(self) -> None: # -- turn registry ------------------------------------------------------- + def register_process(self, specs: list[dict[str, Any]]) -> str: + """Create a credential for a guarded process with a fixed manifest.""" + token = secrets.token_urlsafe(32) + with self._turns_lock: + self._processes[_token_key(token)] = _ProcessBinding( + tools=[_spec_to_tool(spec) for spec in specs] + ) + return token + + def release_process(self, token: str) -> None: + """Revoke a process credential, including any active invocation.""" + self.unregister_turn(token) + with self._turns_lock: + self._processes.pop(_token_key(token), None) + + def activate_turn(self, token: str, thread_id: str, turn_id: str) -> None: + """Bind a preparing invocation to IDs obtained from the SDK handle.""" + with self._turns_lock: + binding = self._processes.get(_token_key(token)) + turn = self._turns.get(_token_key(token)) + if binding is None or turn is None or turn.closed: + raise RuntimeError("the process has no preparing invocation") + if not thread_id or not turn_id: + raise ValueError("a guarded invocation requires thread and turn IDs") + if binding.thread_id not in (None, thread_id): + binding.reusable = False + raise RuntimeError("the process belongs to a different thread") + if turn.activated.is_set(): + raise RuntimeError("the invocation is already activated") + binding.thread_id = turn.thread_id = thread_id + turn.turn_id = turn_id + turn.activated.set() + + async def finish_turn(self, token: str) -> bool: + """Close admission and drain executors before a process may be parked.""" + turn = self._lookup(token) + self.unregister_turn(token) + tasks = ( + [slot.task for slot in turn.slots.values() if slot.task is not None] + if turn + else [] + ) + drained = True + if tasks: + _, pending = await asyncio.wait(tasks, timeout=_STOP_TIMEOUT_SECONDS) + drained = not pending + with self._turns_lock: + binding = self._processes.get(_token_key(token)) + if binding is not None and ( + not drained + or (turn is not None and (turn.state.errors or turn.state.interrupts)) + ): + binding.reusable = False + return drained and binding is not None and binding.reusable + def register_turn( self, specs: list[dict[str, Any]], @@ -251,8 +328,15 @@ def register_turn( otel_context: Any = None, max_tool_iterations: int | None = None, on_error: Callable[[Exception], Awaitable[None]] | None = None, + token: str | None = None, ) -> str: - """Register one turn's tools and return a fresh bearer token for it.""" + """Register one turn's tools and return the bearer token serving it. + + A token from :meth:`register_process` binds fresh executors to a + preparing invocation, activated later from the SDK handle. Without + it a fresh per-invocation token is minted. Closing an invocation stops + tool admission; only a guarded process retains discovery access. + """ tools: list[types.Tool] = [] for spec in specs: name = spec.get("name") if isinstance(spec, dict) else None @@ -265,7 +349,7 @@ def register_turn( ) continue tools.append(_spec_to_tool(spec)) - token = secrets.token_urlsafe(32) + token = token or secrets.token_urlsafe(32) turn = _Turn( tools=tools, executors=dict(executors), @@ -275,6 +359,11 @@ def register_turn( on_error=on_error, ) with self._turns_lock: + if _token_key(token) in self._turns: + raise RuntimeError("the bearer token already serves a turn") + process = self._processes.get(_token_key(token)) + if process is not None and (not process.reusable or process.tools != tools): + raise RuntimeError("the process tool manifest or lifecycle changed") self._turns[_token_key(token)] = turn logger.debug( "codex_mcp_bridge_turn_registered invocation_id=%s tools=%d", @@ -288,6 +377,8 @@ def unregister_turn(self, token: str) -> None: with self._turns_lock: turn = self._turns.pop(_token_key(token), None) if turn is not None: + turn.closed = True + turn.activated.set() for slot in list(turn.slots.values()): slot.cancel() @@ -340,16 +431,22 @@ def codex_server_config( def _build_mcp_server(self) -> Server: server: Server = Server(self.SERVER_NAME) + def request_scope() -> dict[str, Any]: + request = server.request_context.request + if request is None: + raise RuntimeError("MCP handler has no HTTP request context") + return request.scope + # Handlers are installed directly instead of through the decorators: # the decorators keep a single server-wide tool cache (and validate # calls against it), which is wrong when every turn has its own tools. async def list_tools(_req: Any) -> types.ServerResult: - turn = server.request_context.request.scope.get(_SCOPE_TURN) - tools = list(turn.tools) if turn is not None else [] + scope = request_scope() + tools = list(scope.get(_SCOPE_TOOLS) or []) return types.ServerResult(types.ListToolsResult(tools=tools)) async def call_tool(req: types.CallToolRequest) -> types.ServerResult: - scope = server.request_context.request.scope + scope = request_scope() return await self._call_tool( scope.get(_SCOPE_TURN), scope.get(_SCOPE_SLOT), req ) @@ -378,6 +475,10 @@ async def _call_tool( error = None notify_error = False with self._turns_lock: + if turn.closed: + return _text_result( + "Tool call rejected: invocation closed.", is_error=True + ) if ( turn.max_tool_iterations is not None and turn.state.calls >= turn.max_tool_iterations @@ -492,8 +593,15 @@ async def _asgi(self, scope: Any, receive: Any, send: Any) -> None: auth = value.decode("latin-1") break scheme, _, token = auth.partition(" ") - turn = self._lookup(token.strip()) if scheme.lower() == "bearer" else None - if turn is None: + token = token.strip() + turn = self._lookup(token) if scheme.lower() == "bearer" else None + with self._turns_lock: + process = ( + self._processes.get(_token_key(token)) + if scheme.lower() == "bearer" + else None + ) + if turn is None and process is None: await _send_simple(send, 401, b'{"error":"unauthorized"}') return @@ -518,12 +626,13 @@ async def _asgi(self, scope: Any, receive: Any, send: Any) -> None: rpc = json.loads(body) if body else None except ValueError: rpc = None - method = rpc.get("method") if isinstance(rpc, dict) else None + rpc = rpc if isinstance(rpc, dict) else {} + method = rpc.get("method") if method == "notifications/cancelled": params = rpc.get("params") or {} request_id = params.get("requestId") if isinstance(params, dict) else None - slot = turn.slots.get(str(request_id)) + slot = turn.slots.get(str(request_id)) if turn is not None else None if slot is not None: slot.cancel() await _send_simple(send, 202) @@ -531,13 +640,70 @@ async def _asgi(self, scope: Any, receive: Any, send: Any) -> None: slot: _CallSlot | None = None rpc_id: str | None = None - if method == "tools/call" and "id" in rpc: + if method == "tools/call" and turn is None: + await _send_simple(send, 403) + return + if process is not None and method == "tools/call": + if turn is None: + await _send_simple(send, 403) + return + try: + await asyncio.wait_for(turn.activated.wait(), _START_TIMEOUT_SECONDS) + except asyncio.TimeoutError: + process.reusable = False + await _send_simple(send, 403) + return + params = rpc.get("params") if isinstance(rpc, dict) else None + meta = params.get("_meta") if isinstance(params, dict) else None + if not isinstance(meta, dict): + await _send_simple(send, 403) + return + identity = meta.get("x-codex-turn-metadata") + if ( + turn.closed + or not isinstance(identity, dict) + or identity.get("thread_id") != turn.thread_id + or identity.get("turn_id") != turn.turn_id + or meta.get("threadId", turn.thread_id) != turn.thread_id + or meta.get("sessionId", turn.thread_id) != turn.thread_id + ): + logger.warning("codex_mcp_bridge_rejected reason=invocation_identity") + await _send_simple(send, 403) + return + value = rpc.get("id") + rpc_id = str(value) + if ( + isinstance(value, bool) + or not isinstance(value, (int, str)) + or len(rpc_id) > 256 + or rpc_id in process.request_ids + or len(process.request_ids) >= _MAX_PROCESS_REQUESTS + ): + # A cancel notification only carries this ID. Reject reuse + # across generations, including reconnects, before execution. + process.reusable = False + logger.warning("codex_mcp_bridge_rejected reason=request_id") + await _send_simple(send, 409) + return + process.request_ids.add(rpc_id) + elif ( + process is not None + and turn is None + and method + not in ("initialize", "notifications/initialized", "tools/list", "ping") + ): + await _send_simple(send, 403) + return + if method == "tools/call" and "id" in rpc and turn is not None: slot = _CallSlot() rpc_id = str(rpc["id"]) turn.slots[rpc_id] = slot scope = dict(scope) scope[_SCOPE_TURN] = turn scope[_SCOPE_SLOT] = slot + scope[_SCOPE_TOOLS] = ( + process.tools if process is not None else (turn.tools if turn else []) + ) disconnected = asyncio.Event() @@ -573,7 +739,11 @@ async def _replay() -> dict[str, Any]: watcher.cancel() with contextlib.suppress(BaseException): await watcher - if rpc_id is not None and turn.slots.get(rpc_id) is slot: + if ( + rpc_id is not None + and turn is not None + and turn.slots.get(rpc_id) is slot + ): del turn.slots[rpc_id] @staticmethod @@ -712,7 +882,10 @@ def _drop_turns(self) -> None: with self._turns_lock: turns = list(self._turns.values()) self._turns.clear() + self._processes.clear() for turn in turns: + turn.closed = True + turn.activated.set() for slot in list(turn.slots.values()): slot.cancel() diff --git a/veadk/runtime/codex/metrics.py b/veadk/runtime/codex/metrics.py index 54a448d83..10c710b8e 100644 --- a/veadk/runtime/codex/metrics.py +++ b/veadk/runtime/codex/metrics.py @@ -25,7 +25,7 @@ | Name | Type | Unit | Attributes | Meaning | |---|---|---|---|---| -| `veadk.codex.thread.resume` | Counter | `1` | `outcome`: resumed, new_thread, instructions_changed, retried_then_resumed, fallback_after_error, store_error, incompatible, other | How each invocation obtained its Codex thread. | +| `veadk.codex.thread.resume` | Counter | `1` | `outcome`: warm, resumed, new_thread, instructions_changed, retried_then_resumed, fallback_after_error, store_error, incompatible, other | How each invocation obtained its Codex thread (`warm`: reused the session's running process and loaded thread). | | `veadk.codex.thread.save` | Counter | `1` | `outcome`: saved, conflict, failed, skipped, cancelled, too_large, other | Result of persisting the thread after a turn. | | `veadk.codex.turn` | Counter | `1` | `status`: completed, failed, cancelled, transferred, timeout, other; `transport`: direct, shim, other | Finished Codex turns. | | `veadk.codex.turn.duration` | Histogram | `s` | same as `veadk.codex.turn` | Wall-clock duration of a finished turn. | @@ -66,6 +66,7 @@ RESUME_OUTCOMES = frozenset( { + "warm", "resumed", "new_thread", "instructions_changed", diff --git a/veadk/runtime/codex/private_dirs.py b/veadk/runtime/codex/private_dirs.py new file mode 100644 index 000000000..6b6d9788e --- /dev/null +++ b/veadk/runtime/codex/private_dirs.py @@ -0,0 +1,157 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Process-owned temp roots for the Codex runtime, and reclaiming dead ones. + +Each VeADK process keeps its session workspaces and its ``CODEX_HOME`` dirs +under roots it creates in the temp dir (``veadk-codex-workspaces-*``, +``veadk-codex-homes-*``). An ``atexit`` hook removes them on a clean exit, but +a ``SIGKILL`` (an OOM kill, a container stop) leaves them behind, holding +session files and rollouts. The app-server children do exit with their parent +(their stdin closes), so only the directories remain. + +Ownership is an ``flock`` on ``/.veadk-owner.lock``, held for the +process's lifetime: the kernel drops it whenever the owner dies, however it +dies, and a recycled PID cannot fake it. On first use a background thread +scans the temp dir and removes every root whose lock it can take. Roots and +loose ``CODEX_HOME`` dirs from older VeADK versions carry no lock. Their owner +cannot be proven dead, so they are retained. Without ``fcntl`` (Windows) +nothing is reaped. +""" + +from __future__ import annotations + +import os +import shutil +import tempfile +import threading +from pathlib import Path + +from veadk.utils.logger import get_logger + +try: + import fcntl +except ImportError: # pragma: no cover - Windows + fcntl = None # type: ignore[assignment] + +logger = get_logger(__name__) + +WORKSPACE_ROOT_PREFIX = "veadk-codex-workspaces-" +HOME_ROOT_PREFIX = "veadk-codex-homes-" +#: Loose per-invocation homes written by VeADK before the homes root existed. +LEGACY_HOME_PREFIX = "veadk-codex-" +OWNER_LOCK = ".veadk-owner.lock" +LEGACY_MAX_AGE_SECONDS = 24 * 3600 + +# Open lock files of the roots this process owns; closing one releases it. +_held_locks: list[object] = [] +_reaper_started = False +_reaper_lock = threading.Lock() + + +def create_owned_root(prefix: str) -> str: + """Create a temp root this process owns for its lifetime. + + Also starts, once per process, the background reclaim of roots whose + owner died. + """ + root = tempfile.mkdtemp(prefix=prefix) + if fcntl is not None: + lock = open(os.path.join(root, OWNER_LOCK), "w") # noqa: SIM115 - held + fcntl.flock(lock.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) + _held_locks.append(lock) + _start_reaper(os.path.dirname(root)) + return root + + +def reap_orphans(temp_dir: str, *, now: float | None = None) -> int: + """Remove dead processes' roots and stale legacy homes under ``temp_dir``. + + Returns: + The number of directories removed. + """ + if fcntl is None: + return 0 + del now # retained for caller compatibility; age is not proof of ownership + removed = 0 + try: + entries = list(Path(temp_dir).iterdir()) + except OSError as e: + logger.warning( + "codex_orphan_reap_failed category=temp_root error_type=%s", + type(e).__name__, + ) + return 0 + for entry in entries: + name = entry.name + try: + if not entry.is_dir() or entry.is_symlink(): + continue + if name.startswith((WORKSPACE_ROOT_PREFIX, HOME_ROOT_PREFIX)): + if _reap_root(entry): + removed += 1 + elif name.startswith(LEGACY_HOME_PREFIX): + continue + except OSError as e: + logger.warning( + "codex_orphan_reap_failed category=temp_entry error_type=%s", + type(e).__name__, + ) + continue + if removed: + logger.info("codex_orphaned_dirs_reaped count=%d", removed) + return removed + + +def _reap_root(root: Path) -> bool: + lock_path = root / OWNER_LOCK + if not lock_path.exists(): + return False + assert fcntl is not None + with open(lock_path, "a") as lock: + try: + fcntl.flock(lock.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB) + except OSError: + return False # its owner is alive + # Removed while holding the lock, so a live owner cannot be racing us. + try: + shutil.rmtree(root) + except OSError as e: + logger.warning( + "codex_orphan_reap_failed category=owned_root error_type=%s", + type(e).__name__, + ) + return False + return not root.exists() + + +def _start_reaper(temp_dir: str) -> None: + global _reaper_started + with _reaper_lock: + if _reaper_started or fcntl is None: + return + _reaper_started = True + threading.Thread( + target=_reap_quietly, + args=(temp_dir,), + name="veadk-codex-orphan-reaper", + daemon=True, + ).start() + + +def _reap_quietly(temp_dir: str) -> None: + try: + reap_orphans(temp_dir) + except Exception as e: # noqa: BLE001 - housekeeping must not fail a turn + logger.warning("codex_orphan_reap_failed error_type=%s", type(e).__name__) diff --git a/veadk/runtime/codex/process_pool.py b/veadk/runtime/codex/process_pool.py new file mode 100644 index 000000000..92650f359 --- /dev/null +++ b/veadk/runtime/codex/process_pool.py @@ -0,0 +1,446 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Warm ``codex app-server`` processes, one per persistent session thread. + +Without this, every invocation spawned a fresh app-server, imported the +session's whole rollout into a new ``CODEX_HOME`` and had Codex replay it +(``thread_resume``) before the turn could start, so each turn got slower as +the session grew. A process whose turn ended cleanly is instead parked here, +with its ``CODEX_HOME`` and loaded thread, and the session's next invocation +runs its turn on that thread directly. + +Reuse is deliberately narrow; anything else takes the cold path: + +- Only the direct transport with ``thread_mode="resume"``: those invocations + hold the per-session turn lock, so one process never serves two turns at + once. (The shim's per-turn bearer token is baked into the process env.) +- Tool-bearing processes reuse by default (``mcp_process_reuse=True``). Their bridge + credential owns a fixed manifest; each invocation binds fresh executors and + validates SDK thread/turn identity before executing business tools. +- A process is parked only after its turn reached ``turn/completed`` and its + rollout was saved, so a later ``turn()`` can never join a turn still winding + down, and the store holds exactly what the warm thread holds. +- It is reused only if the invocation's settings produce the same fingerprint + (config.toml, skills, env, thread config, tools, workspace, instructions) + and the store namespace, thread ID and version still match. Another + instance that advanced the thread makes the warm copy stale. +- A process that died, outlived :data:`MAX_TURNS_PER_PROCESS`, or sat idle + past the idle TTL is closed. + +Pools are process-local. Knobs: ``VEADK_CODEX_PROCESS_POOL_SIZE`` (idle +processes kept, default 20; ``0`` disables reuse) and +``VEADK_CODEX_PROCESS_IDLE_TTL_SECONDS`` (default 600). +""" + +from __future__ import annotations + +import asyncio +import atexit +import contextlib +import hashlib +import json +import os +import shutil +import time +from collections import OrderedDict +from dataclasses import dataclass, field +from typing import Any, Callable, Hashable + +from veadk.runtime.codex.turn_control import STATUS_UNKNOWN +from veadk.runtime.codex.turn_control import TurnCompletion +from veadk.utils.logger import get_logger + +logger = get_logger(__name__) + +POOL_SIZE_ENV = "VEADK_CODEX_PROCESS_POOL_SIZE" +IDLE_TTL_ENV = "VEADK_CODEX_PROCESS_IDLE_TTL_SECONDS" +DEFAULT_POOL_SIZE = 20 +DEFAULT_IDLE_TTL_SECONDS = 600.0 +#: The SDK queues every notification no turn consumes and never drains that +#: queue, so a process is retired after this many turns to bound it. +MAX_TURNS_PER_PROCESS = 100 + + +def pool_size() -> int: + """Idle processes kept at most; ``0`` disables reuse.""" + raw = os.getenv(POOL_SIZE_ENV, "").strip() + try: + return max(0, int(raw)) if raw else DEFAULT_POOL_SIZE + except ValueError: + logger.warning("codex_process_pool_invalid_size value_ignored=true") + return DEFAULT_POOL_SIZE + + +def idle_ttl_seconds() -> float: + """How long a parked process may sit idle before it is closed.""" + raw = os.getenv(IDLE_TTL_ENV, "").strip() + try: + value = float(raw) if raw else DEFAULT_IDLE_TTL_SECONDS + except ValueError: + logger.warning("codex_process_pool_invalid_idle_ttl value_ignored=true") + return DEFAULT_IDLE_TTL_SECONDS + return value if value > 0 else DEFAULT_IDLE_TTL_SECONDS + + +def process_alive(codex: Any) -> bool: + """Whether ``codex``'s app-server and the SDK's reader are still running. + + Reads the pinned SDK's internals (``AsyncCodex._client._sync``). Once the + reader thread is gone the SDK fails every pending call but keeps the + client marked initialized, so a dead process is only detectable here. + Anything without those internals is treated as not reusable. + """ + sync = getattr(getattr(codex, "_client", None), "_sync", None) + proc = getattr(sync, "_proc", None) + if proc is None: + return False + reader = getattr(sync, "_reader_thread", None) + return proc.poll() is None and (reader is None or reader.is_alive()) + + +def fingerprint(*parts: Any) -> str: + """Digest of everything a warm process must share with an invocation. + + Only the digest is kept, so credentials among ``parts`` are never stored. + """ + raw = json.dumps(parts, sort_keys=True, default=str, ensure_ascii=False) + return hashlib.sha256(raw.encode("utf-8")).hexdigest() + + +def tree_digest(root: str) -> str: + """Digest every entry under ``root``, including linked skill trees. + + ``root``'s own path is masked in contents: a config naming its own + ``CODEX_HOME`` (the skills read grant) must digest the same in every home. + """ + digest = hashlib.sha256() + own_paths = sorted( + {root.encode("utf-8"), os.path.realpath(root).encode("utf-8")}, + key=len, + reverse=True, + ) + + def add(path: str, relative: str, ancestors: frozenset[tuple[int, int]]) -> None: + try: + stat = os.stat(path, follow_symlinks=True) + identity = (stat.st_dev, stat.st_ino) + if os.path.islink(path): + target = os.path.realpath(path).encode("utf-8") + for own in own_paths: + target = target.replace(own, b"$CODEX_HOME") + digest.update(b"L\0" + relative.encode("utf-8") + b"\0" + target) + if os.path.isdir(path): + if identity in ancestors: + digest.update(b"C\0" + relative.encode("utf-8")) + return + digest.update(b"D\0" + relative.encode("utf-8")) + next_ancestors = ancestors | {identity} + with os.scandir(path) as entries: + children = sorted(entries, key=lambda entry: entry.name) + for child in children: + child_relative = ( + f"{relative}/{child.name}" if relative else child.name + ) + add(child.path, child_relative, next_ancestors) + return + digest.update(b"F\0" + relative.encode("utf-8") + b"\0") + with open(path, "rb") as f: + content = f.read() + for own in own_paths: + content = content.replace(own, b"$CODEX_HOME") + digest.update(hashlib.sha256(content).digest()) + except OSError as e: + digest.update( + b"E\0" + + relative.encode("utf-8") + + b"\0" + + type(e).__name__.encode("ascii", "replace") + ) + + add(root, "", frozenset()) + return digest.hexdigest() + + +@dataclass(eq=False) +class WarmCodex: + """A parked app-server holding one session's loaded thread.""" + + codex: Any + codex_home: str + fingerprint: str + bridge_token: str + thread: Any + thread_id: str + #: Store version of the rollout this process saved last. + version: int + covered_invocation_id: str + turns: int + on_close: Callable[[], None] | None = None + loop: asyncio.AbstractEventLoop = field(default_factory=asyncio.get_running_loop) + expiry: asyncio.TimerHandle | None = None + idle_since: float = field(default_factory=time.monotonic) + + +class CodexProcessLease: + """The Codex process one invocation runs its turn on. + + Used as ``async with lease:``. A cold lease spawns its process on entry. + On exit the process is closed, unless the turn ended cleanly + (``completion`` resolved with a known status) and the process may be + reused, in which case it is ``kept`` for the caller to park or close. + """ + + def __init__( + self, + factory: Callable[[], Any], + warm: WarmCodex | None, + *, + reusable: bool, + ) -> None: + self._factory = factory + self.warm = warm + self.reusable = reusable + self.codex: Any = warm.codex if warm is not None else None + self.completion: TurnCompletion | None = None + self.kept = False + self.detached = False + + @property + def thread(self) -> Any: + """The warm thread to run on, or ``None`` to start or resume one.""" + return self.warm.thread if self.warm is not None else None + + async def __aenter__(self) -> "CodexProcessLease": + if self.codex is None: + self.codex = self._factory() + try: + await self.codex.__aenter__() + except BaseException: + await self.close() + raise + return self + + async def __aexit__(self, exc_type: Any, _exc: Any, _tb: Any) -> bool: + status = self.completion.status if self.completion is not None else None + if ( + exc_type is None + and self.reusable + and status not in (None, STATUS_UNKNOWN) + and process_alive(self.codex) + ): + self.kept = True + else: + await self.close() + return False + + def detach(self) -> None: + """Hand the process to the pool; :meth:`close` no longer touches it.""" + self.codex = None + self.kept = False + self.detached = True + + async def close(self) -> None: + codex, self.codex = self.codex, None + self.kept = False + if codex is not None: + if self.warm is not None: + _revoke_binding(self.warm) + await _close_codex(codex) + + +class CodexProcessPool: + """Idle warm processes by session key, least recently used first.""" + + def __init__(self) -> None: + self._idle: OrderedDict[Hashable, WarmCodex] = OrderedDict() + self._closing: set[asyncio.Task[None]] = set() + self._atexit_registered = False + + def take(self, key: Hashable, process_fingerprint: str) -> WarmCodex | None: + """Remove and return ``key``'s warm process if it can serve this turn.""" + self._sweep() + entry = self._idle.pop(key, None) + if entry is None: + return None + _cancel_expiry(entry) + reason = "" + if entry.fingerprint != process_fingerprint: + reason = "settings_changed" + elif entry.loop is not _running_loop(): + reason = "event_loop_changed" + elif not process_alive(entry.codex): + reason = "process_exited" + if reason: + logger.info("codex_process_discarded reason=%s", reason) + self.discard(entry) + return None + return entry + + def put(self, key: Hashable, entry: WarmCodex) -> bool: + """Park ``entry``; ``False`` means the caller still owns and closes it.""" + size = pool_size() + if ( + size <= 0 + or entry.turns >= MAX_TURNS_PER_PROCESS + or not process_alive(entry.codex) + ): + return False + self._sweep() + previous = self._idle.pop(key, None) + if previous is not None and previous is not entry: + _cancel_expiry(previous) + self.discard(previous) + self._register_atexit() + entry.idle_since = time.monotonic() + entry.expiry = entry.loop.call_later( + idle_ttl_seconds(), self._expire, key, entry + ) + self._idle[key] = entry + while len(self._idle) > size: + _, evicted = self._idle.popitem(last=False) + _cancel_expiry(evicted) + logger.info("codex_process_discarded reason=evicted") + self.discard(evicted) + return True + + def discard(self, entry: WarmCodex) -> None: + """Close ``entry`` in the background.""" + try: + loop = asyncio.get_running_loop() + except RuntimeError: + _kill(entry) + return + if len(self._closing) >= max(1, pool_size()): + logger.warning("codex_process_close_overflow action=kill") + _kill_process(entry) + return + task = loop.create_task(_close_warm(entry)) + self._closing.add(task) + task.add_done_callback(self._closing.discard) + + async def release(self, match: Callable[[Hashable], bool]) -> int: + """Close the parked processes whose key ``match`` accepts. + + A process serving a turn right now is not parked, so it is not + released; it is parked again when its turn ends and expires with the + idle TTL. + + Returns: + The number of processes closed. + """ + entries = [ + (key, entry) for key, entry in list(self._idle.items()) if match(key) + ] + for key, entry in entries: + del self._idle[key] + _cancel_expiry(entry) + await asyncio.gather( + *(_close_warm(entry) for _, entry in entries), return_exceptions=True + ) + return len(entries) + + async def close_all(self) -> None: + """Close every parked process (server shutdown, tests).""" + entries = list(self._idle.values()) + self._idle.clear() + for entry in entries: + _cancel_expiry(entry) + await asyncio.gather( + *(_close_warm(entry) for entry in entries), + *list(self._closing), + return_exceptions=True, + ) + + def _sweep(self) -> None: + """Close idle processes past the TTL whose timer never fired. + + The expiry timer runs on the loop that parked the process; if that + loop stopped (a caller running each request in its own loop), only + this sweep reclaims the process. + """ + deadline = time.monotonic() - idle_ttl_seconds() + for key, entry in list(self._idle.items()): + if entry.idle_since <= deadline: + self._expire(key, entry) + + def _expire(self, key: Hashable, entry: WarmCodex) -> None: + if self._idle.get(key) is entry: + del self._idle[key] + _cancel_expiry(entry) + logger.info("codex_process_discarded reason=idle_ttl") + self.discard(entry) + + def _register_atexit(self) -> None: + if not self._atexit_registered: + self._atexit_registered = True + atexit.register(self._kill_all) + + def _kill_all(self) -> None: + entries = list(self._idle.values()) + self._idle.clear() + for entry in entries: + _kill(entry) + + +def _running_loop() -> asyncio.AbstractEventLoop | None: + try: + return asyncio.get_running_loop() + except RuntimeError: + return None + + +def _cancel_expiry(entry: WarmCodex) -> None: + if entry.expiry is not None: + entry.expiry.cancel() + entry.expiry = None + + +async def _close_codex(codex: Any) -> None: + try: + await codex.__aexit__(None, None, None) + except Exception as e: # noqa: BLE001 - closing must not mask the turn + logger.warning("codex_process_close_failed error_type=%s", type(e).__name__) + + +async def _close_warm(entry: WarmCodex) -> None: + _revoke_binding(entry) + await _close_codex(entry.codex) + await asyncio.to_thread(shutil.rmtree, entry.codex_home, True) + + +def _kill(entry: WarmCodex) -> None: + """Synchronous close for when no event loop is running (``atexit``).""" + _revoke_binding(entry) + sync = getattr(getattr(entry.codex, "_client", None), "_sync", None) + with contextlib.suppress(Exception): + if sync is not None: + sync.close() + shutil.rmtree(entry.codex_home, ignore_errors=True) + + +def _kill_process(entry: WarmCodex) -> None: + """Stop an already-discarded idle child without blocking or deleting.""" + _revoke_binding(entry) + sync = getattr(getattr(entry.codex, "_client", None), "_sync", None) + proc = getattr(sync, "_proc", None) + with contextlib.suppress(Exception): + if proc is not None and proc.poll() is None: + proc.kill() + + +def _revoke_binding(entry: WarmCodex) -> None: + callback, entry.on_close = entry.on_close, None + if callback is not None: + callback() diff --git a/veadk/runtime/codex/proxy.py b/veadk/runtime/codex/proxy.py index b3d3e4e0c..3c9912549 100644 --- a/veadk/runtime/codex/proxy.py +++ b/veadk/runtime/codex/proxy.py @@ -1221,7 +1221,12 @@ async def _on_backend_error(request: Request, exc: Exception) -> JSONResponse: ) return JSONResponse( status_code=status, - content={"error": {"type": _error_type(status), "message": detail}}, + content={ + "error": { + "type": _error_type(status), + "message": _safe_backend_error_message(status), + } + }, ) # Starlette resolves handlers by walking `type(exc).__mro__`, and none @@ -1712,6 +1717,20 @@ def _error_type(status: int) -> str: }.get(status, "api_error") +def _safe_backend_error_message(status: int) -> str: + """Stable client-facing backend error text that cannot expose provider internals.""" + return { + 400: "Backend rejected the request.", + 401: "Backend authentication failed.", + 403: "Backend permission denied.", + 404: "Backend resource was not found.", + 408: "Backend request timed out.", + 422: "Backend rejected the request.", + 429: "Backend rate limit exceeded.", + 503: "Backend service is unavailable.", + }.get(status, "Backend request failed.") + + def _to_dict(obj: Any) -> dict[str, Any]: """Normalize a litellm Responses object into a plain dict.""" if isinstance(obj, dict): @@ -2014,10 +2033,20 @@ def ev(payload: dict[str, Any]) -> bytes: _RETIRED: "list[ResponsesShim]" = [] +_FINGERPRINT_KEY = b"veadk-codex-shim-cache-v1" + + def _credential_fingerprint(api_key: str) -> str: if not api_key: return "" - return hashlib.sha256(api_key.encode("utf-8")).hexdigest()[:32] + digest = hashlib.pbkdf2_hmac( + "sha256", + api_key.encode("utf-8"), + _FINGERPRINT_KEY, + 200_000, + dklen=16, + ) + return digest.hex() async def get_shim(api_base: str, api_key: str) -> ShimLease: diff --git a/veadk/runtime/codex/runtime.py b/veadk/runtime/codex/runtime.py index 0c792fada..86cf959b1 100644 --- a/veadk/runtime/codex/runtime.py +++ b/veadk/runtime/codex/runtime.py @@ -46,11 +46,12 @@ import json import os import shutil +import sys import tempfile import threading import time from pathlib import Path -from typing import TYPE_CHECKING, Any, AsyncGenerator +from typing import TYPE_CHECKING, Any, AsyncGenerator, cast from openai_codex import ( # type: ignore[import-not-found] ApprovalMode, @@ -76,12 +77,23 @@ from veadk.runtime.codex import metrics as codex_metrics from veadk.runtime.codex.mcp_bridge import McpBridge from veadk.runtime.codex.mcp_bridge import get_bridge +from veadk.runtime.codex.mcp_bridge import shutdown_bridge from veadk.runtime.codex.model_provider import CodexModelRoute from veadk.runtime.codex.model_provider import direct_route from veadk.runtime.codex.model_provider import pinned_codex_settings from veadk.runtime.codex.model_provider import resolve_transport from veadk.runtime.codex.model_provider import shim_route +from veadk.runtime.codex.process_pool import CodexProcessLease +from veadk.runtime.codex.process_pool import CodexProcessPool +from veadk.runtime.codex.process_pool import WarmCodex +from veadk.runtime.codex.process_pool import fingerprint as process_fingerprint_of +from veadk.runtime.codex.process_pool import pool_size +from veadk.runtime.codex.process_pool import tree_digest +from veadk.runtime.codex.private_dirs import HOME_ROOT_PREFIX +from veadk.runtime.codex.private_dirs import WORKSPACE_ROOT_PREFIX +from veadk.runtime.codex.private_dirs import create_owned_root from veadk.runtime.codex.proxy import get_shim +from veadk.runtime.codex.proxy import shutdown_shims from veadk.runtime.codex.rollout_io import export_rollout from veadk.runtime.codex.rollout_io import import_rollout from veadk.runtime.codex.skills import sync_skills_to_codex_home @@ -103,9 +115,11 @@ sync_bundle_to_tools_dict, ) from veadk.runtime.codex.turn_control import ActiveTurns +from veadk.runtime.codex.turn_control import CodexTurnAdmissionTimeout from veadk.runtime.codex.turn_control import CodexTurnTimeout from veadk.runtime.codex.turn_control import SessionTurnLocks from veadk.runtime.codex.turn_control import TurnCompletion +from veadk.runtime.codex.turn_control import TurnSlots from veadk.runtime.codex.turn_control import interrupt_turn from veadk.runtime.codex.turn_control import run_with_turn_timeout from veadk.runtime.codex.turn_control import session_key @@ -162,6 +176,10 @@ _SESSION_LOCKS = SessionTurnLocks() # The turn each session is running in this process, for `CodexRuntime.steer`. _ACTIVE_TURNS = ActiveTurns() +# Warm app-servers holding a session's loaded thread between its invocations. +_PROCESS_POOL = CodexProcessPool() +# Optional cap on Codex processes running a turn at once. +_TURN_SLOTS = TurnSlots() # How long a stopped turn gets to wind down (after a timeout or cancellation) # before its stream is abandoned. _TURN_STOP_GRACE_SECONDS = 5.0 @@ -179,7 +197,7 @@ class _QueueSentinel(enum.Enum): _QUEUE_DONE = _QueueSentinel.DONE -_WORKSPACE_ROOT_PREFIX = "veadk-codex-workspaces-" +_WORKSPACE_ROOT_PREFIX = WORKSPACE_ROOT_PREFIX # The process-owned root that holds every session workspace, created on first # use by `_ensure_session_workspace_root`. `None` until then. _session_workspace_root: str | None = None @@ -220,7 +238,9 @@ def _ensure_session_workspace_root() -> str: global _session_workspace_root with _session_workspace_root_lock: if _session_workspace_root is None: - root = tempfile.mkdtemp(prefix=_WORKSPACE_ROOT_PREFIX) + # Owned (locked) for the process's lifetime, so a later process + # can reclaim it if this one is killed; see `private_dirs`. + root = create_owned_root(_WORKSPACE_ROOT_PREFIX) # Registered alongside creation, not at import: a hook over a # directory that was never created is pure noise, and # `ignore_errors=True` would have hidden that it did nothing. @@ -229,6 +249,23 @@ def _ensure_session_workspace_root() -> str: return _session_workspace_root +# The process-owned root holding every invocation's CODEX_HOME, created on +# first use like the workspace root. +_codex_home_root: str | None = None +_codex_home_root_lock = threading.Lock() + + +def _ensure_codex_home_root() -> str: + """Return the process-owned root for ``CODEX_HOME`` dirs, creating it.""" + global _codex_home_root + with _codex_home_root_lock: + if _codex_home_root is None: + root = create_owned_root(HOME_ROOT_PREFIX) + atexit.register(shutil.rmtree, root, ignore_errors=True) + _codex_home_root = root + return _codex_home_root + + def __getattr__(name: str) -> Any: """Keep ``_SESSION_WORKSPACE_ROOT`` readable as a module attribute. @@ -294,55 +331,108 @@ async def run_async( "codex runtime requires model_api_base and model_api_key " "(the chat endpoint Codex is bridged onto)." ) + run_started_at = time.monotonic() + loop = asyncio.get_running_loop() + turn_deadline = ( + loop.time() + runtime_config.turn_timeout_seconds + if runtime_config.turn_timeout_seconds + else None + ) - # "direct": Codex calls a Responses-capable backend itself and reaches - # the agent's ADK tools through the MCP bridge, so Codex owns the tool - # loop. "shim": the in-process Responses->chat shim sits in between and - # runs the ADK tools itself, for backends that only speak chat. - transport = resolve_transport(runtime_config, api_base) - if transport == "direct" and _model_extra_body(agent): - # Codex's provider config has no request-body passthrough, so the - # direct transport would silently drop `extra_body` (e.g. a - # thinking switch). Under "auto" keep the shim, which forwards it. - if runtime_config.model_transport == "auto": - logger.info( - "codex_transport_fallback invocation_id=%s transport=shim " - "reason=extra_body_requires_shim", - ctx.invocation_id, - ) - transport = "shim" - else: - logger.warning( - "codex_extra_body_ignored invocation_id=%s detail=the " - "direct transport cannot forward model_extra_config " - "extra_body; use model_transport='shim' to keep it", - ctx.invocation_id, + async def _await_setup(awaitable: Any) -> Any: + """Await pre-turn work within the invocation's shared deadline.""" + if turn_deadline is None: + return await awaitable + remaining = max(0.0, turn_deadline - loop.time()) + try: + return await asyncio.wait_for(awaitable, timeout=remaining) + except (TimeoutError, asyncio.TimeoutError) as e: + if loop.time() >= turn_deadline: + raise CodexTurnAdmissionTimeout( + runtime_config.turn_timeout_seconds or 0 + ) from e + raise + + def _check_setup_deadline() -> None: + """Stop before the next setup stage after non-cancellable work.""" + if turn_deadline is not None and loop.time() >= turn_deadline: + raise CodexTurnAdmissionTimeout( + runtime_config.turn_timeout_seconds or 0 ) - shim = await get_shim(api_base, api_key) if transport == "shim" else None - bridge = await get_bridge() if transport == "direct" else None - route: CodexModelRoute = ( - shim_route(shim.url or "", "") - if shim is not None - else direct_route( - api_base, api_key, extra_headers=_model_extra_headers(agent) - ) + + lock_stack = contextlib.AsyncExitStack() + admission_timeout = ( + max(0.0, turn_deadline - loop.time()) if turn_deadline is not None else None ) - workspace = _prepare_workspace(runtime_config, ctx) - await _maybe_reap_workspaces(runtime_config) - codex_home = _prepare_codex_home(route, model, runtime_config) - # Expose the agent's skills to Codex by materializing them under - # `$CODEX_HOME/skills/`, where Codex's native skill system discovers - # them. Best-effort: a skill failure must not abort the turn. try: - sync_skills_to_codex_home( - agent, codex_home, invocation_id=ctx.invocation_id + await lock_stack.enter_async_context( + _TURN_SLOTS.hold(timeout=admission_timeout) ) - except Exception as e: # noqa: BLE001 - logger.warning( - "codex_skill_sync_failed invocation_id=%s error_type=%s", - ctx.invocation_id, - type(e).__name__, + except CodexTurnAdmissionTimeout as e: + # The waiter receives the exact remaining deadline, but the public + # error reports the configured invocation budget. + raise CodexTurnAdmissionTimeout( + runtime_config.turn_timeout_seconds or 0 + ) from e + try: + # "direct": Codex calls a Responses-capable backend itself and + # reaches ADK tools through the MCP bridge. "shim" executes them in + # the in-process Responses-to-chat adapter. + transport = resolve_transport(runtime_config, api_base) + if transport == "direct" and _model_extra_body(agent): + if runtime_config.model_transport == "auto": + logger.info( + "codex_transport_fallback invocation_id=%s transport=shim " + "reason=extra_body_requires_shim", + ctx.invocation_id, + ) + transport = "shim" + else: + logger.warning( + "codex_extra_body_ignored invocation_id=%s detail=the " + "direct transport cannot forward model_extra_config " + "extra_body; use model_transport='shim' to keep it", + ctx.invocation_id, + ) + shim = ( + await _await_setup(get_shim(api_base, api_key)) + if transport == "shim" + else None + ) + bridge = await _await_setup(get_bridge()) if transport == "direct" else None + route: CodexModelRoute = ( + shim_route(shim.url or "", "") + if shim is not None + else direct_route( + api_base, api_key, extra_headers=_model_extra_headers(agent) + ) ) + workspace, workspace_created = _prepare_workspace_state(runtime_config, ctx) + _check_setup_deadline() + await _await_setup(_maybe_reap_workspaces(runtime_config)) + codex_home = _prepare_codex_home( + route, + model, + runtime_config, + workspace=workspace, + ) + _check_setup_deadline() + try: + sync_skills_to_codex_home( + agent, + codex_home, + invocation_id=ctx.invocation_id, + ) + _check_setup_deadline() + except Exception as e: # noqa: BLE001 + logger.warning( + "codex_skill_sync_failed invocation_id=%s error_type=%s", + ctx.invocation_id, + type(e).__name__, + ) + except BaseException: + await lock_stack.aclose() + raise event_queue: asyncio.Queue["Event | BaseException | _QueueSentinel"] = ( asyncio.Queue() @@ -350,6 +440,7 @@ async def run_async( use_adk_transfer_scheduler = _uses_adk_transfer_scheduler(ctx) turn_token: str | None = None bridge_token: str | None = None + guarded_bridge = False # The direct transport has no shim to charge `max_llm_calls` per model # call and record the budget error; the stream pump does it instead. direct_turn_error: BaseException | None = None @@ -359,9 +450,13 @@ async def run_async( thread_key: ThreadKey | None = None thread_record: ThreadRecord | None = None thread_instruction_hash = "" + expected_version: int | None = None active_thread_id: str | None = None - lock_stack = contextlib.AsyncExitStack() - run_started_at = time.monotonic() + # The session's warm-process slot (persistent threads only), and the + # process this invocation runs on. + pool_key: Any = None + process_fingerprint = "" + lease: CodexProcessLease | None = None def _turn_error() -> BaseException | None: """The error that aborted this turn outside the event stream.""" @@ -396,11 +491,13 @@ async def _emit_tool_event(event: "Event") -> None: # built" from "setup failed before that" without inspecting locals(). tool_bundle = None try: - tool_bundle = await build_executable_tools( - agent, - ctx, - event_sink=_emit_tool_event, - timeout_seconds=runtime_config.tool_timeout_seconds, + tool_bundle = await _await_setup( + build_executable_tools( + agent, + ctx, + event_sink=_emit_tool_event, + timeout_seconds=runtime_config.tool_timeout_seconds, + ) ) transfer_targets = get_transfer_targets(agent) if transfer_targets: @@ -417,8 +514,8 @@ async def _emit_tool_event(event: "Event") -> None: # did. with bind_workspace(workspace): resumed_events = [ - *await resume_authenticated_tools(tool_bundle, ctx), - *await resume_confirmed_tools(tool_bundle, ctx), + *await _await_setup(resume_authenticated_tools(tool_bundle, ctx)), + *await _await_setup(resume_confirmed_tools(tool_bundle, ctx)), ] except BaseException as e: logger.error( @@ -426,9 +523,17 @@ async def _emit_tool_event(event: "Event") -> None: ctx.invocation_id, type(e).__name__, ) - if tool_bundle is not None: - await close_toolsets(tool_bundle.opened_toolsets) - shutil.rmtree(codex_home, ignore_errors=True) + try: + if tool_bundle is not None: + await close_toolsets(tool_bundle.opened_toolsets) + finally: + try: + await asyncio.to_thread(shutil.rmtree, codex_home, True) + finally: + # Admission now precedes every resource allocation. Release + # its process-wide permit on every setup failure too; this + # path has not installed the normal `_cleanup` helper yet. + await lock_stack.aclose() raise # ADK's `call_llm` span is opened by its own LLM flow, which this @@ -457,13 +562,22 @@ async def _cleanup() -> None: finally: if bridge is not None and bridge_token is not None: bridge.unregister_turn(bridge_token) + if guarded_bridge and (lease is None or not lease.detached): + bridge.release_process(bridge_token) try: - await lock_stack.aclose() - await close_toolsets(tool_bundle.opened_toolsets) + # A process still held here was not parked: close it + # before its session lock lets the next invocation in. + if lease is not None: + await lease.close() + try: + await close_toolsets(tool_bundle.opened_toolsets) + finally: + await lock_stack.aclose() finally: - # Workspaces survive turns; only the private Codex home - # belongs to this invocation. - shutil.rmtree(codex_home, ignore_errors=True) + # Workspaces survive turns; the private Codex home belongs + # to this invocation unless its warm process was parked. + if lease is None or not lease.detached: + await asyncio.to_thread(shutil.rmtree, codex_home, True) _end_span(call_llm_span) # `_emit_call_llm_telemetry`'s contract is one record per invocation: @@ -500,17 +614,21 @@ def _emit_telemetry_once(llm_response: "LlmResponse") -> None: await _cleanup() return - runtime_call = await build_runtime_llm_request( - agent, - ctx, - model=model, - tools_dict=tool_bundle.tools, + runtime_call = await _await_setup( + build_runtime_llm_request( + agent, + ctx, + model=model, + tools_dict=tool_bundle.tools, + ) ) - short_circuit = await run_before_model_callbacks( - agent, - ctx, - runtime_call.llm_request, - runtime_call.model_response_event, + short_circuit = await _await_setup( + run_before_model_callbacks( + agent, + ctx, + runtime_call.llm_request, + runtime_call.model_response_event, + ) ) if short_circuit is not None: event = llm_response_to_event( @@ -531,7 +649,14 @@ def _emit_telemetry_once(llm_response: "LlmResponse") -> None: ) if callback_model != model: model = callback_model - _prepare_codex_home(route, model, runtime_config, codex_home=codex_home) + _prepare_codex_home( + route, + model, + runtime_config, + workspace=workspace, + codex_home=codex_home, + ) + _check_setup_deadline() sync_bundle_to_tools_dict( tool_bundle, @@ -555,6 +680,8 @@ def _emit_telemetry_once(llm_response: "LlmResponse") -> None: bound_executors = bind_workspace_to_executors( tool_bundle.executors, workspace ) + # The MCP bridge registers below, once it is known whether a warm + # process (whose token is fixed in its env) serves this turn. if shim is not None: turn_token = shim.register_turn( tool_bundle.specs, @@ -564,15 +691,6 @@ def _emit_telemetry_once(llm_response: "LlmResponse") -> None: model_extra_config=agent.model_extra_config, on_model_call=lambda: _charge_llm_call(ctx), ) - elif bridge is not None and tool_bundle.specs: - bridge_token = bridge.register_turn( - tool_bundle.specs, - bound_executors, - invocation_id=ctx.invocation_id, - otel_context=_current_otel_context(), - max_tool_iterations=runtime_config.max_tool_iterations, - on_error=event_queue.put, - ) # Keep privileged instructions out of the user transcript. The SDK # exposes a native developer-instruction channel for them. # @@ -623,10 +741,25 @@ def _emit_telemetry_once(llm_response: "LlmResponse") -> None: else "" ), ) + # A resumed thread already holds every earlier turn Codex took # part in, so it gets the current message only -- plus whatever # other agents and the user said while this agent was not running, # which its thread never saw. + def _resumed_input(covered_invocation_id: str) -> list[object]: + return _build_codex_input( + _with_backfill( + build_prompt_from_llm_request( + runtime_call.llm_request, include_history=False + ), + _turns_since_own(ctx, agent.name, covered_invocation_id), + _resumed_tool_results(resumed_events), + workspace_reset=workspace_created, + ), + runtime_call.llm_request, + workspace, + ) + resume_input_items: list[object] = [] resume_thread_id: str | None = None if persistent: @@ -634,24 +767,104 @@ def _emit_telemetry_once(llm_response: "LlmResponse") -> None: if thread_store is None: persistent = False if persistent and thread_key is not None and thread_store is not None: - await lock_stack.enter_async_context( - _SESSION_LOCKS.hold( - session_key( - thread_key.app_name, - thread_key.user_id, - thread_key.session_id, - thread_key.agent_name, + pool_key = session_key( + thread_key.app_name, + thread_key.user_id, + thread_key.session_id, + thread_key.agent_name, + ) + (thread_store.pool_namespace,) + await _await_setup( + lock_stack.enter_async_context(_SESSION_LOCKS.hold(pool_key)) + ) + thread_instruction_hash = instruction_hash(developer_instructions) + # CodexConfig.env is copied into only this subprocess. Never mutate + # process-wide CODEX_HOME or credential variables. The route's own + # credentials go in *after* the masking, which would otherwise + # blank the direct key along with the host's. + # The shim route was built before its turn token existed, so its + # env is not used: `codex_subprocess_env` sets the token itself. + subprocess_env = codex_subprocess_env(codex_home, turn_token or "") + thread_config: dict[str, Any] | None = None + uses_bridge = bridge is not None and bool(tool_bundle.specs) + guarded_bridge = ( + uses_bridge + and runtime_config.mcp_process_reuse + and pool_key is not None + and pool_size() > 0 + ) + if bridge is not None: + subprocess_env.update(route.env) + direct_config = route.thread_config() + if runtime_config.auto_compact_token_limit: + direct_config["model_auto_compact_token_limit"] = ( + runtime_config.auto_compact_token_limit + ) + if uses_bridge: + direct_config["mcp_servers"] = { + McpBridge.SERVER_NAME: bridge.codex_server_config( + bearer_token_env_var=_MCP_TOKEN_ENV, + tool_timeout_seconds=runtime_config.tool_timeout_seconds, ) + } + thread_config = direct_config + warm: WarmCodex | None = None + if ( + pool_key is not None + and pool_size() > 0 + and (not uses_bridge or guarded_bridge) + ): + # Everything the process was spawned with or its thread was + # started with. The per-process paths and token are excluded: + # a warm process brings its own. + home_digest = await asyncio.to_thread(tree_digest, codex_home) + _check_setup_deadline() + process_fingerprint = process_fingerprint_of( + home_digest, + { + name: value + for name, value in subprocess_env.items() + if name not in ("CODEX_HOME", _MCP_TOKEN_ENV) + }, + thread_config, + tool_bundle.specs, + workspace, + model, + thread_instruction_hash, + ) + warm = await _await_setup( + _take_warm_process( + pool_key, + process_fingerprint, + thread_store, + thread_key, + ctx, ) ) - thread_instruction_hash = instruction_hash(developer_instructions) + lease = CodexProcessLease( + lambda: AsyncCodex(config=sdk_config), + warm, + reusable=pool_key is not None and (not uses_bridge or guarded_bridge), + ) + if warm is not None: + # The warm process keeps its own CODEX_HOME, which the + # fingerprint shows matches this invocation's fresh one. + await asyncio.to_thread(shutil.rmtree, codex_home, True) + _check_setup_deadline() + codex_home = warm.codex_home + expected_version = warm.version + resume_input_items = _resumed_input(warm.covered_invocation_id) + elif persistent and thread_key is not None and thread_store is not None: try: - thread_record = await _load_thread(thread_store, thread_key, ctx) + thread_record = await _await_setup( + _load_thread(thread_store, thread_key, ctx) + ) except ThreadStoreIncompatible: # Written by a newer VeADK: run this turn on a new thread # and do not save it, which would overwrite a record the # newer instance still needs. thread_store = None + if thread_record is not None: + expected_version = thread_record.version if ( thread_record is not None and thread_record.instruction_hash == thread_instruction_hash @@ -659,22 +872,10 @@ def _emit_telemetry_once(llm_response: "LlmResponse") -> None: await asyncio.to_thread( import_rollout, codex_home, thread_record.rollout ) + _check_setup_deadline() resume_thread_id = thread_record.thread_id - resume_input_items = _build_codex_input( - _with_backfill( - build_prompt_from_llm_request( - runtime_call.llm_request, include_history=False - ), - _turns_since_own( - ctx, - agent.name, - thread_record.covered_invocation_id, - ), - _resumed_tool_results(resumed_events), - workspace_reset=_workspace_is_empty(workspace), - ), - runtime_call.llm_request, - workspace, + resume_input_items = _resumed_input( + thread_record.covered_invocation_id ) elif thread_record is not None: codex_metrics.record_resume("instructions_changed") @@ -687,6 +888,29 @@ def _emit_telemetry_once(llm_response: "LlmResponse") -> None: "reason=instructions_changed", ctx.invocation_id, ) + if uses_bridge and bridge is not None: + process_token = ( + warm.bridge_token + if warm is not None + else ( + bridge.register_process(tool_bundle.specs) + if guarded_bridge + else None + ) + ) + # Keep ownership even if registration fails during setup. + bridge_token = process_token + bridge_token = bridge.register_turn( + tool_bundle.specs, + bound_executors, + invocation_id=ctx.invocation_id, + otel_context=_current_otel_context(), + max_tool_iterations=runtime_config.max_tool_iterations, + on_error=event_queue.put, + token=process_token, + ) + assert bridge_token is not None + subprocess_env[_MCP_TOKEN_ENV] = bridge_token logger.info( "codex_runtime_start invocation_id=%s agent=%s model=%s " "transport=%s sandbox=%s approval_mode=%s network_access=%s " @@ -733,30 +957,6 @@ def _emit_telemetry_once(llm_response: "LlmResponse") -> None: ctx.invocation_id, runtime_config.approval_mode, ) - # CodexConfig.env is copied into only this subprocess. Never mutate - # process-wide CODEX_HOME or credential variables. The route's own - # credentials go in *after* the masking, which would otherwise - # blank the direct key along with the host's. - # The shim route was built before its turn token existed, so its - # env is not used: `codex_subprocess_env` sets the token itself. - subprocess_env = codex_subprocess_env(codex_home, turn_token or "") - thread_config: dict[str, Any] | None = None - if bridge is not None: - subprocess_env.update(route.env) - direct_config = route.thread_config() - if runtime_config.auto_compact_token_limit: - direct_config["model_auto_compact_token_limit"] = ( - runtime_config.auto_compact_token_limit - ) - if bridge_token is not None: - subprocess_env[_MCP_TOKEN_ENV] = bridge_token - direct_config["mcp_servers"] = { - McpBridge.SERVER_NAME: bridge.codex_server_config( - bearer_token_env_var=_MCP_TOKEN_ENV, - tool_timeout_seconds=runtime_config.tool_timeout_seconds, - ) - } - thread_config = direct_config sdk_config = CodexConfig(cwd=workspace, env=subprocess_env) except BaseException as e: logger.error( @@ -782,27 +982,41 @@ def _emit_telemetry_once(llm_response: "LlmResponse") -> None: # are no use as a target (they are never persisted), so any that follow # the held-back one are buffered behind it rather than overtaking it. merge_target: "Event | None" = None + thread: Any = None try: - async with AsyncCodex(config=sdk_config) as codex: - thread = None + await _await_setup(lease.__aenter__()) + async with lease: + codex = lease.codex + thread = lease.thread turn_input = input_items - if resume_thread_id is not None: + if thread is not None: + # The warm thread already holds the session's history. + turn_input = resume_input_items + codex_metrics.record_resume("warm") + logger.info( + "codex_thread_warm invocation_id=%s thread_id=%s", + ctx.invocation_id, + getattr(thread, "id", None), + ) + elif resume_thread_id is not None: try: # Every setting is passed again: Codex does not carry # the model or sandbox over into a resumed thread in a # new process, it falls back to its own defaults. - thread, resume_retried = await _resume_with_retry( - codex, - resume_thread_id, - ctx, - include_turns=False, - model=model, - model_provider=route.provider_id, - cwd=workspace, - approval_mode=_approval_mode(runtime_config), - sandbox=_sandbox(runtime_config), - personality=runtime_config.personality, - **({"config": thread_config} if thread_config else {}), + thread, resume_retried = await _await_setup( + _resume_with_retry( + codex, + resume_thread_id, + ctx, + include_turns=False, + model=model, + model_provider=route.provider_id, + cwd=workspace, + approval_mode=_approval_mode(runtime_config), + sandbox=_sandbox(runtime_config), + personality=runtime_config.personality, + **({"config": thread_config} if thread_config else {}), + ) ) turn_input = resume_input_items codex_metrics.record_resume( @@ -828,16 +1042,18 @@ def _emit_telemetry_once(llm_response: "LlmResponse") -> None: if thread is None: if persistent and thread_record is None: codex_metrics.record_resume("new_thread") - thread = await codex.thread_start( - model=model, - model_provider=route.provider_id, - developer_instructions=developer_instructions or None, - cwd=workspace, - ephemeral=not persistent, - approval_mode=_approval_mode(runtime_config), - sandbox=_sandbox(runtime_config), - personality=runtime_config.personality, - **({"config": thread_config} if thread_config else {}), + thread = await _await_setup( + codex.thread_start( + model=model, + model_provider=route.provider_id, + developer_instructions=developer_instructions or None, + cwd=workspace, + ephemeral=not persistent, + approval_mode=_approval_mode(runtime_config), + sandbox=_sandbox(runtime_config), + personality=runtime_config.personality, + **({"config": thread_config} if thread_config else {}), + ) ) logger.info( "codex_thread_started invocation_id=%s thread_id=%s " @@ -848,15 +1064,20 @@ def _emit_telemetry_once(llm_response: "LlmResponse") -> None: ) if persistent: active_thread_id = thread.id - turn = await thread.turn( - turn_input, - cwd=workspace, - approval_mode=_approval_mode(runtime_config), - sandbox=_sandbox(runtime_config), - effort=runtime_config.reasoning_effort, + turn = await _await_setup( + thread.turn( + turn_input, + cwd=workspace, + approval_mode=_approval_mode(runtime_config), + sandbox=_sandbox(runtime_config), + effort=runtime_config.reasoning_effort, + ) ) + if guarded_bridge and bridge is not None and bridge_token is not None: + bridge.activate_turn(bridge_token, thread.id, turn.id) stream = turn.stream() completion = TurnCompletion(str(getattr(turn, "id", "") or "")) + lease.completion = completion codex_metrics.record_startup( transport, time.monotonic() - run_started_at ) @@ -871,8 +1092,8 @@ def _emit_telemetry_once(llm_response: "LlmResponse") -> None: { "thread_id": getattr(thread, "id", None), "turn_id": getattr(turn, "id", None), - "thread_resumed": turn_input is resume_input_items - and resume_thread_id is not None, + "thread_resumed": turn_input is resume_input_items, + "process_reused": lease.warm is not None, }, ) # This turn's usage. A resumed thread's `total` includes every @@ -957,7 +1178,12 @@ async def _pump_codex() -> None: await event_queue.put(_QUEUE_DONE) pump = asyncio.create_task(_pump_codex()) - if runtime_config.turn_timeout_seconds: + remaining_timeout = ( + max(0.0, turn_deadline - asyncio.get_running_loop().time()) + if turn_deadline is not None + else None + ) + if remaining_timeout is not None: # Past the deadline the turn is interrupted, given a grace # period to wind down, and the watchdog then raises a # `TimeoutError`; the pump ending (normally or not) lets @@ -967,7 +1193,7 @@ async def _pump_codex() -> None: turn, pump, completion=completion, - timeout=runtime_config.turn_timeout_seconds, + timeout=remaining_timeout, grace=_TURN_STOP_GRACE_SECONDS, ) ) @@ -1259,6 +1485,12 @@ async def _pump_codex() -> None: raise e finally: turn_scope.close() + if guarded_bridge and bridge is not None and bridge_token is not None: + reusable_bridge = await bridge.finish_turn(bridge_token) + if ( + not reusable_bridge or run_status != "completed" + ) and lease is not None: + await lease.close() if watchdog is not None and not watchdog.done(): watchdog.cancel() await asyncio.gather(watchdog, return_exceptions=True) @@ -1269,22 +1501,56 @@ async def _pump_codex() -> None: # the rollout records the steps already taken, so the next turn # resumes from them instead of repeating them. Before `_cleanup`, # which deletes CODEX_HOME and releases the session lock. + saved_version: int | None = None try: if ( thread_store is not None and thread_key is not None and active_thread_id is not None ): - await _save_thread( + saved_version = await _save_thread( thread_store, thread_key, codex_home, active_thread_id, thread_instruction_hash, - thread_record, + expected_version, ctx, ) finally: + # Park the process only once the store holds exactly what its + # thread holds; otherwise `_cleanup` closes it. + if ( + lease is not None + and lease.kept + and saved_version is not None + and active_thread_id is not None + ): + parked = _PROCESS_POOL.put( + pool_key, + WarmCodex( + codex=lease.codex, + codex_home=codex_home, + fingerprint=process_fingerprint, + bridge_token=bridge_token or "", + thread=thread, + thread_id=active_thread_id, + version=saved_version, + covered_invocation_id=ctx.invocation_id, + turns=(lease.warm.turns if lease.warm else 0) + 1, + on_close=( + lambda b=bridge, token=bridge_token: b.release_process( + token + ) + ) + if guarded_bridge + and bridge is not None + and bridge_token is not None + else None, + ), + ) + if parked: + lease.detach() _annotate_span( call_llm_span, { @@ -1336,11 +1602,110 @@ def _resolve_model(self, agent: "Agent") -> str: return name +#: The permissions profile VeADK generates for sandboxed commands. +_PERMISSIONS_PROFILE = "veadk" +#: Extra paths sandboxed commands may not read (``os.pathsep``-separated), for +#: host files the temp-dir denial does not cover: the application's config and +#: ``.env``, a local SQLite session database, credentials. +_DENY_READ_PATHS_ENV = "VEADK_CODEX_DENY_READ_PATHS" + + +def _nested_read_access() -> str: + """The grant for a path that must be readable under a denied parent. + + Codex's Linux sandbox (bubblewrap; 0.159.2 and later at the time of + writing) bind-mounts a ``write`` grant under a denied parent but generates + no ``--ro-bind`` for a ``read`` grant, which therefore stays unreachable: + a ``read_only`` turn failed reading its own workspace's AGENTS.md, and + skills under ``CODEX_HOME`` were unreadable. Seatbelt (macOS) honours both. + Until Codex fixes it, Linux grants such paths ``write``; they are the + session's own workspace and skills copy, so other sessions stay + unreachable either way. + """ + return "write" if sys.platform.startswith("linux") else "read" + + +def _sandbox_profile( + runtime_config: CodexRuntimeConfig, workspace: str, codex_home: str +) -> dict[str, Any] | None: + """Codex settings confining sandboxed commands to this session's files. + + Every session's workspace and ``CODEX_HOME`` live on the same host under + the same OS user (by default all under the temp dir), and Codex's + ``workspace-write`` preset lets commands read the whole disk and write + ``$TMPDIR`` and ``/tmp`` too, so one session's model could read, or + rewrite, another session's files and rollout. This profile (measured on + Codex 0.159.2) starts from ``:read-only`` (read anywhere, write nowhere), + denies the temp dirs, the session-workspace root and any + ``VEADK_CODEX_DENY_READ_PATHS`` outright, and grants + back only this session's workspace (``write``; ``read`` for ``read_only``) + and its own skills (``read``) -- on Linux both as ``write``, see + :func:`_nested_read_access`. A more specific path wins over a denied + parent; deny globs cannot be carved out, so plain paths are used. + Commands get the workspace as ``TMPDIR``, since the temp dirs are denied. + + ``full_access`` gets no profile: it is unrestricted by request. + + Returns: + The settings to merge into the config, or ``None`` for ``full_access``. + """ + if runtime_config.sandbox == "full_access": + return None + own = os.path.realpath(workspace) + denied = { + os.path.realpath(tempfile.gettempdir()), + os.path.realpath("/tmp"), + } + if runtime_config.workspace_root and not runtime_config.reuse_workspace: + denied.add(os.path.realpath(runtime_config.workspace_root)) + denied.update( + os.path.realpath(os.path.expanduser(path.strip())) + for path in os.getenv(_DENY_READ_PATHS_ENV, "").split(os.pathsep) + if path.strip() + ) + filesystem: dict[str, str] = {path: "deny" for path in sorted(denied)} + writable = runtime_config.sandbox == "workspace_write" + filesystem[own] = "write" if writable else _nested_read_access() + filesystem[os.path.realpath(os.path.join(codex_home, "skills"))] = ( + _nested_read_access() + ) + settings: dict[str, Any] = { + "default_permissions": _PERMISSIONS_PROFILE, + # Codex otherwise walks up from the workspace looking for a project + # root and its AGENTS.md: the parents are denied (on Linux that fails + # the turn with "failed to load AGENTS.md instructions ... Permission + # denied"), and instructions from outside the session's workspace must + # not reach it anyway. The workspace's own AGENTS.md is still loaded. + "project_root_markers": [], + "permissions": { + _PERMISSIONS_PROFILE: { + "extends": ":read-only", + "filesystem": filesystem, + # `network_access` only ever applied to `workspace_write`. + "network": {"enabled": writable and runtime_config.network_access}, + } + }, + } + if writable: + settings["shell_environment_policy"] = {"set": {"TMPDIR": own}} + return settings + + +def _merge_settings(target: dict[str, Any], extra: dict[str, Any]) -> None: + """Deep-merge ``extra`` into ``target`` (tables merge, values replace).""" + for key, value in extra.items(): + if isinstance(value, dict) and isinstance(target.get(key), dict): + _merge_settings(target[key], value) + else: + target[key] = value + + def _prepare_codex_home( route: CodexModelRoute, model: str, runtime_config: CodexRuntimeConfig, *, + workspace: str, codex_home: str | None = None, ) -> str: """Create an invocation-isolated CODEX_HOME with a config.toml. @@ -1350,27 +1715,30 @@ def _prepare_codex_home( touches the host's ``~/.codex``. Credentials are never written here: the provider names the env var that carries them. """ - home = codex_home or tempfile.mkdtemp(prefix="veadk-codex-") + home = codex_home or tempfile.mkdtemp(prefix="home-", dir=_ensure_codex_home_root()) os.chmod(home, 0o700) approval_policy = ( "on-request" if runtime_config.approval_mode == "auto_review" else "never" ) - sandbox_mode = { - "read_only": "read-only", - "workspace_write": "workspace-write", - "full_access": "danger-full-access", - }[runtime_config.sandbox] - pinned_scalars, pinned_tables = _toml_settings(pinned_codex_settings()) + settings = pinned_codex_settings() + profile = _sandbox_profile(runtime_config, workspace, home) + if profile is not None: + _merge_settings(settings, profile) + pinned_scalars, pinned_tables = _toml_settings(settings) provider_block = "".join( f"{key} = {_toml_value(value)}\n" for key, value in route.provider_config.items() ) + # A permissions profile must not be combined with `sandbox_mode` or + # `[sandbox_workspace_write]` (Codex's config reference), so those only + # remain for `full_access`, which has no profile. + legacy_sandbox = 'sandbox_mode = "danger-full-access"\n' if profile is None else "" config = ( f"model = {toml_string(model)}\n" f"model_provider = {toml_string(route.provider_id)}\n" f"review_model = {toml_string(model)}\n" f"approval_policy = {toml_string(approval_policy)}\n" - f"sandbox_mode = {toml_string(sandbox_mode)}\n" + f"{legacy_sandbox}" # `disable_response_storage` is intentionally absent: it was removed # upstream (absent from config.schema.json and from the pinned CLI # binary) and `store: false` is now unconditional in Codex's client, @@ -1378,12 +1746,11 @@ def _prepare_codex_home( f"model_reasoning_effort = {toml_string(runtime_config.reasoning_effort)}\n" f"personality = {toml_string(runtime_config.personality)}\n" # Pinned settings shared with the direct transport's thread config; - # see `pinned_codex_settings` for why each one is needed. + # see `pinned_codex_settings` for why each one is needed. They carry + # the sandbox's permissions profile too. f"{pinned_scalars}\n" f"[model_providers.{route.provider_id}]\n" f"{provider_block}\n" - f"[sandbox_workspace_write]\n" - f"network_access = {str(runtime_config.network_access).lower()}\n\n" f"{pinned_tables}" ) with open(os.path.join(home, "config.toml"), "w", encoding="utf-8") as f: @@ -1485,6 +1852,73 @@ def _thread_binding( return None, None +async def release_codex_session(app_name: str, user_id: str, session_id: str) -> int: + """Close the warm Codex processes of one session, for every agent. + + Call it when a session is deleted; otherwise its processes stay until the + idle TTL (``VEADK_CODEX_PROCESS_IDLE_TTL_SECONDS``). Only this process's + pool is affected, and a turn still running is not interrupted. + + Returns: + The number of processes closed. + """ + target = (app_name, user_id, session_id) + return await _PROCESS_POOL.release( + lambda key: isinstance(key, tuple) and tuple(key[:3]) == target + ) + + +async def shutdown_codex_runtime() -> None: + """Close every warm Codex process, the MCP bridge and the model shims. + + For a server's shutdown (the AgentKit app calls it from its lifespan); the + ``atexit`` hooks remain the fallback when nothing calls it. + """ + await _PROCESS_POOL.close_all() + await shutdown_bridge() + await shutdown_shims() + + +async def _take_warm_process( + pool_key: Any, + process_fingerprint: str, + store: CodexThreadStore | None, + key: ThreadKey | None, + ctx: "InvocationContext", +) -> WarmCodex | None: + """The session's warm process, if it is still current; else ``None``. + + Current means: same settings, still running, and the store still holds the + rollout version this process saved -- another instance serving the session + may have advanced the thread since. + """ + if store is None or key is None: + return None + warm = _PROCESS_POOL.take(pool_key, process_fingerprint) + if warm is None: + return None + try: + identity = await store.current_identity(key) + except asyncio.CancelledError: + _PROCESS_POOL.discard(warm) + raise + except Exception as e: # noqa: BLE001 - the cold path reports the store + logger.warning( + "codex_thread_version_check_failed invocation_id=%s error_type=%s", + ctx.invocation_id, + type(e).__name__, + ) + identity = None + if identity != (warm.thread_id, warm.version): + logger.info( + "codex_process_discarded invocation_id=%s reason=store_advanced", + ctx.invocation_id, + ) + _PROCESS_POOL.discard(warm) + return None + return warm + + async def _load_thread( store: CodexThreadStore, key: ThreadKey, ctx: "InvocationContext" ) -> ThreadRecord | None: @@ -1534,15 +1968,18 @@ async def _save_thread( codex_home: str, thread_id: str, instructions_hash: str, - previous: ThreadRecord | None, + expected_version: int | None, ctx: "InvocationContext", -) -> None: +) -> int | None: """Write the thread's rollout back; never fails the invocation. - ``previous`` is the record this invocation started from (or replaced): the - save only succeeds if nobody else wrote since, so of two instances racing - on one session the later writer loses instead of silently dropping the - other's turn. + ``expected_version`` is the version of the record this invocation started + from (or replaced), ``None`` if there was none: the save only succeeds if + nobody else wrote since, so of two instances racing on one session the + later writer loses instead of silently dropping the other's turn. + + Returns: + The saved record's version, or ``None`` if nothing was saved. """ try: rollout = await asyncio.to_thread(export_rollout, codex_home, thread_id) @@ -1552,16 +1989,17 @@ async def _save_thread( "codex_thread_save_skipped invocation_id=%s reason=no_rollout", ctx.invocation_id, ) - return - await store.save( + return None + version = await store.save( key, thread_id, rollout, instructions_hash, - expected_version=previous.version if previous is not None else None, + expected_version=expected_version, covered_invocation_id=ctx.invocation_id, ) codex_metrics.record_save("saved") + return version except RolloutTooLarge as e: # Every later save of this thread would be refused too, while the # stored copy only falls further behind. Drop the binding: the next @@ -1588,7 +2026,7 @@ async def _save_thread( thread_id, key.session_id, key.agent_name, - previous.version if previous is not None else None, + expected_version, ) except asyncio.CancelledError: # Cancellation must reach the caller; the turn's rollout is lost. @@ -1631,9 +2069,9 @@ def _turns_since_own( """ get_events = getattr(ctx, "_get_events", None) events = ( - list(get_events(current_branch=True)) + list(cast(Any, get_events(current_branch=True))) if callable(get_events) - else list(getattr(ctx.session, "events", None) or []) + else list(cast(Any, getattr(ctx.session, "events", None)) or []) ) events = [event for event in events if not getattr(event, "partial", False)] anchor = None @@ -1769,7 +2207,8 @@ def _turn_usage(usage: dict[str, Any], baseline: dict[str, Any]) -> dict[str, An if not isinstance(total, dict): return usage if not baseline: - last = usage.get("last") if isinstance(usage.get("last"), dict) else {} + last_raw = usage.get("last") + last = last_raw if isinstance(last_raw, dict) else {} baseline.update( { key: value - int(last.get(key) or 0) @@ -1902,10 +2341,18 @@ def _prepare_workspace( Returns: str: Absolute path to the workspace directory. """ + return _prepare_workspace_state(runtime_config, ctx)[0] + + +def _prepare_workspace_state( + runtime_config: CodexRuntimeConfig, ctx: InvocationContext +) -> tuple[str, bool]: + """Return the workspace and whether this invocation created its directory.""" root = runtime_config.workspace_root if root and runtime_config.reuse_workspace: + created = not Path(root).is_dir() Path(root).mkdir(parents=True, exist_ok=True) - return root + return root, created session = getattr(ctx, "session", None) session_id = str(getattr(session, "id", "session")) @@ -1922,11 +2369,12 @@ def _prepare_workspace( base = Path(root or _ensure_session_workspace_root()) base.mkdir(parents=True, exist_ok=True) workspace = base / f"{safe_id or 'session'}-{digest}" + created = not workspace.is_dir() workspace.mkdir(mode=0o700, parents=True, exist_ok=True) os.chmod(workspace, 0o700) # Mark the session active so the reaper keeps it for another TTL window. os.utime(workspace) - return str(workspace) + return str(workspace), created async def _maybe_reap_workspaces(runtime_config: CodexRuntimeConfig) -> None: @@ -2135,12 +2583,15 @@ def _approval_mode(config: CodexRuntimeConfig) -> ApprovalMode: ) -def _sandbox(config: CodexRuntimeConfig) -> Sandbox: - return { - "read_only": Sandbox.read_only, - "workspace_write": Sandbox.workspace_write, - "full_access": Sandbox.full_access, - }[config.sandbox] +def _sandbox(config: CodexRuntimeConfig) -> Sandbox | None: + """The SDK sandbox preset to pass, or ``None`` to keep the profile. + + A preset passed on ``thread_start``/``turn`` replaces the config's + permissions profile with the preset's defaults (on a turn, the SDK sends a + bare ``workspaceWrite`` policy, which also re-allows ``$TMPDIR`` and + ``/tmp``), so only ``full_access``, which has no profile, passes one. + """ + return Sandbox.full_access if config.sandbox == "full_access" else None def _build_codex_input( diff --git a/veadk/runtime/codex/thread_store.py b/veadk/runtime/codex/thread_store.py index 6d043f8d6..ea9ec060c 100644 --- a/veadk/runtime/codex/thread_store.py +++ b/veadk/runtime/codex/thread_store.py @@ -57,6 +57,7 @@ import tempfile import threading import weakref +from functools import cached_property from abc import ABC, abstractmethod from collections import OrderedDict from dataclasses import dataclass @@ -277,6 +278,11 @@ class CodexThreadStore(ABC): ``expected_version`` wins. """ + @cached_property + def pool_namespace(self) -> object: + """Opaque process-local identity, distinct even for equal store IDs.""" + return object() + @abstractmethod async def load(self, key: ThreadKey) -> ThreadRecord | None: """Return the record for ``key``, or ``None`` if there is none.""" @@ -298,6 +304,21 @@ async def save( async def delete(self, key: ThreadKey) -> None: """Remove the record for ``key``; a no-op if it does not exist.""" + async def current_version(self, key: ThreadKey) -> int | None: + """The record's version, or ``None`` if there is none. + + Lets a warm Codex process check that the store still holds the rollout + it saved. The default loads the record; stores override it to skip + reading the rollout. + """ + record = await self.load(key) + return record.version if record is not None else None + + async def current_identity(self, key: ThreadKey) -> tuple[str, int] | None: + """Thread identity/version, with a compatible fallback for custom stores.""" + record = await self.load(key) + return (record.thread_id, record.version) if record is not None else None + # --------------------------------------------------------------------------- # In-memory @@ -377,6 +398,22 @@ def __len__(self) -> int: with self._lock: return len(self._records) + async def current_version(self, key: ThreadKey) -> int | None: + with self._lock: + entry = self._records.get(key) + if entry is None: + return None + self._records.move_to_end(key) + return entry.version + + async def current_identity(self, key: ThreadKey) -> tuple[str, int] | None: + with self._lock: + entry = self._records.get(key) + if entry is None: + return None + self._records.move_to_end(key) + return entry.thread_id, entry.version + async def load(self, key: ThreadKey) -> ThreadRecord | None: with self._lock: entry = self._records.get(key) @@ -753,6 +790,40 @@ def _where(self, key: ThreadKey): & (t.c.agent_name == key.agent_name) ) + async def current_version(self, key: ThreadKey) -> int | None: + from sqlalchemy import select + + await self._ensure_table() + t = self._table + async with self._engine.connect() as conn: + row = ( + await conn.execute( + select(t.c.schema_version, t.c.version).where(self._where(key)) + ) + ).first() + if row is None: + return None + _check_schema_version(row.schema_version, "thread record") + return int(row.version) + + async def current_identity(self, key: ThreadKey) -> tuple[str, int] | None: + from sqlalchemy import select + + await self._ensure_table() + t = self._table + async with self._engine.connect() as conn: + row = ( + await conn.execute( + select(t.c.schema_version, t.c.thread_id, t.c.version).where( + self._where(key) + ) + ) + ).first() + if row is None: + return None + _check_schema_version(row.schema_version, "thread record") + return row.thread_id, int(row.version) + async def load(self, key: ThreadKey) -> ThreadRecord | None: from sqlalchemy import select diff --git a/veadk/runtime/codex/turn_control.py b/veadk/runtime/codex/turn_control.py index ed87b74a9..ff6fae938 100644 --- a/veadk/runtime/codex/turn_control.py +++ b/veadk/runtime/codex/turn_control.py @@ -61,8 +61,10 @@ - :func:`interrupt_turn` stops the turn on cancellation (``CancelledError`` or the consumer closing the generator). -Not wired into the runtime today: :func:`start_fresh_turn` (every invocation -runs in a fresh Codex process, so no earlier turn can be joined) and +Not wired into the runtime today: :func:`start_fresh_turn` (an invocation runs +in a fresh Codex process, or in a warm one parked only after its previous turn +reached ``turn/completed`` -- see :mod:`veadk.runtime.codex.process_pool` -- so +no earlier turn can be joined) and :func:`compact_and_wait` (compaction is left to Codex via ``auto_compact_token_limit``). They are kept, tested, for an explicit compaction API and for a long-lived shared Codex client. @@ -75,7 +77,11 @@ import asyncio import contextlib +import contextvars +import os +import threading from collections.abc import AsyncIterator, Awaitable, Hashable, Iterator +from dataclasses import dataclass from typing import Any, Protocol, TypeVar from openai_codex import ( # type: ignore[import-not-found] @@ -682,3 +688,125 @@ async def run_with_turn_timeout( finally: if owned and not task.done(): task.cancel() + + +#: Caps the Codex processes running a turn at once in this process (each one is +#: a ``codex app-server`` of ~100MB); unset or ``0`` means no cap. +MAX_CONCURRENT_TURNS_ENV = "VEADK_CODEX_MAX_CONCURRENT_TURNS" + +# Set while the current task holds a slot, so an agent transferred to from +# inside a turn (same task) does not wait for a slot its caller holds. +_TURN_SLOT_OWNER: contextvars.ContextVar[asyncio.Task[Any] | None] = ( + contextvars.ContextVar("veadk_codex_turn_slot_owner", default=None) +) + + +def max_concurrent_turns() -> int: + raw = os.getenv(MAX_CONCURRENT_TURNS_ENV, "").strip() + try: + return max(0, int(raw)) if raw else 0 + except ValueError: + logger.warning("codex_max_concurrent_turns_invalid value_ignored=true") + return 0 + + +class CodexTurnAdmissionTimeout(TimeoutError): + """A Codex invocation could not obtain a process slot before its deadline.""" + + def __init__(self, timeout: float) -> None: + self.timeout = timeout + super().__init__(f"Codex turn admission exceeded its {timeout:g}s timeout") + + +@dataclass(eq=False) +class _SlotWaiter: + loop: asyncio.AbstractEventLoop + future: asyncio.Future[None] + state: str = "queued" + + +class TurnSlots: + """Process-wide cap on concurrent Codex turns across event loops. + + A lock-protected FIFO registry coordinates Futures owned by different + loops. Nested invocations in the exact task already holding a slot pass + through; child tasks inherit context but not the owning task identity. + """ + + def __init__(self) -> None: + self._lock = threading.Lock() + self._in_use = 0 + self._waiters: list[_SlotWaiter] = [] + + @contextlib.asynccontextmanager + async def hold(self, *, timeout: float | None = None) -> AsyncIterator[None]: + limit = max_concurrent_turns() + task = asyncio.current_task() + if limit <= 0 or _TURN_SLOT_OWNER.get() is task: + yield + return + loop = asyncio.get_running_loop() + waiter: _SlotWaiter | None = None + with self._lock: + if self._in_use < limit and not self._waiters: + self._in_use += 1 + else: + waiter = _SlotWaiter(loop, loop.create_future()) + self._waiters.append(waiter) + if waiter is not None: + try: + await asyncio.wait_for(asyncio.shield(waiter.future), timeout) + except (TimeoutError, asyncio.TimeoutError) as e: + self._cancel_waiter(waiter) + raise CodexTurnAdmissionTimeout(timeout or 0) from e + except BaseException: + self._cancel_waiter(waiter) + raise + token = _TURN_SLOT_OWNER.set(task) + try: + yield + finally: + _TURN_SLOT_OWNER.reset(token) + self._release() + + def _cancel_waiter(self, waiter: _SlotWaiter) -> None: + next_waiter: _SlotWaiter | None = None + with self._lock: + if waiter.state == "queued": + waiter.state = "cancelled" + with contextlib.suppress(ValueError): + self._waiters.remove(waiter) + elif waiter.state in ("reserved", "granted"): + waiter.state = "cancelled" + next_waiter = self._release_locked() + if next_waiter is not None: + self._schedule(next_waiter) + + def _release(self) -> None: + with self._lock: + next_waiter = self._release_locked() + if next_waiter is not None: + self._schedule(next_waiter) + + def _release_locked(self) -> _SlotWaiter | None: + while self._waiters: + waiter = self._waiters.pop(0) + if waiter.state == "queued": + waiter.state = "reserved" + return waiter + self._in_use = max(0, self._in_use - 1) + return None + + def _schedule(self, waiter: _SlotWaiter) -> None: + try: + waiter.loop.call_soon_threadsafe(self._grant, waiter) + except RuntimeError: + self._cancel_waiter(waiter) + + def _grant(self, waiter: _SlotWaiter) -> None: + with self._lock: + if waiter.state != "reserved": + return + waiter.state = "granted" + if not waiter.future.done(): + waiter.future.set_result(None)