create-windy 0.3.2 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -9
- package/dist/cli.js +825 -117
- package/package.json +1 -1
- package/template/.agents/skills/windy-business-module/SKILL.md +16 -3
- package/template/.windy-template.json +2 -2
- package/template/AGENTS.md +4 -0
- package/template/README.md +18 -11
- package/template/_gitignore +1 -0
- package/template/apps/agent-server/README.md +2 -0
- package/template/apps/agent-server/pyproject.toml +8 -0
- package/template/apps/agent-server/src/southwind_agent_server/adk_adapter.py +87 -10
- package/template/apps/agent-server/src/southwind_agent_server/adk_model.py +18 -3
- package/template/apps/agent-server/src/southwind_agent_server/adk_tools.py +78 -11
- package/template/apps/agent-server/src/southwind_agent_server/app.py +120 -115
- package/template/apps/agent-server/src/southwind_agent_server/artifact_input.py +153 -0
- package/template/apps/agent-server/src/southwind_agent_server/config.py +29 -0
- package/template/apps/agent-server/src/southwind_agent_server/contracts.py +81 -1
- package/template/apps/agent-server/src/southwind_agent_server/deterministic.py +8 -0
- package/template/apps/agent-server/src/southwind_agent_server/engine.py +28 -0
- package/template/apps/agent-server/src/southwind_agent_server/fingerprint.py +41 -0
- package/template/apps/agent-server/src/southwind_agent_server/http_helpers.py +62 -0
- package/template/apps/agent-server/src/southwind_agent_server/lease.py +15 -0
- package/template/apps/agent-server/src/southwind_agent_server/multimodal.py +76 -0
- package/template/apps/agent-server/src/southwind_agent_server/openai_engine.py +112 -20
- package/template/apps/agent-server/src/southwind_agent_server/openai_provider.py +14 -1
- package/template/apps/agent-server/src/southwind_agent_server/postgres_store.py +329 -0
- package/template/apps/agent-server/src/southwind_agent_server/postgres_store_backend.py +332 -0
- package/template/apps/agent-server/src/southwind_agent_server/provider_governance.py +64 -3
- package/template/apps/agent-server/src/southwind_agent_server/runtime_factory.py +68 -0
- package/template/apps/agent-server/src/southwind_agent_server/service.py +218 -56
- package/template/apps/agent-server/src/southwind_agent_server/signal_events.py +36 -0
- package/template/apps/agent-server/src/southwind_agent_server/sqlite_catalog.py +107 -0
- package/template/apps/agent-server/src/southwind_agent_server/sqlite_schema.py +65 -0
- package/template/apps/agent-server/src/southwind_agent_server/sqlite_serialization.py +16 -0
- package/template/apps/agent-server/src/southwind_agent_server/sqlite_store.py +89 -57
- package/template/apps/agent-server/src/southwind_agent_server/store.py +105 -2
- package/template/apps/agent-server/src/southwind_agent_server/store_cipher.py +40 -0
- package/template/apps/agent-server/src/southwind_agent_server/telemetry.py +89 -0
- package/template/apps/agent-server/src/southwind_agent_server/tool_host.py +72 -0
- package/template/apps/agent-server/tests/test_adk_adapter.py +6 -2
- package/template/apps/agent-server/tests/test_api.py +24 -1
- package/template/apps/agent-server/tests/test_artifact_input.py +110 -0
- package/template/apps/agent-server/tests/test_config.py +4 -0
- package/template/apps/agent-server/tests/test_fingerprint.py +10 -0
- package/template/apps/agent-server/tests/test_multimodal.py +51 -0
- package/template/apps/agent-server/tests/test_openai_provider.py +6 -2
- package/template/apps/agent-server/tests/test_postgres_store.py +131 -0
- package/template/apps/agent-server/tests/test_provider_governance.py +61 -0
- package/template/apps/agent-server/tests/test_sqlite_store.py +95 -10
- package/template/apps/agent-server/tests/test_telemetry.py +25 -0
- package/template/apps/agent-server/tests/test_tool_approval.py +28 -0
- package/template/apps/agent-server/uv.lock +165 -0
- package/template/apps/document-server/Dockerfile +19 -0
- package/template/apps/document-server/pyproject.toml +43 -0
- package/template/apps/document-server/src/southwind_document_server/__init__.py +1 -0
- package/template/apps/document-server/src/southwind_document_server/adapter.py +337 -0
- package/template/apps/document-server/src/southwind_document_server/app.py +151 -0
- package/template/apps/document-server/src/southwind_document_server/config.py +94 -0
- package/template/apps/document-server/src/southwind_document_server/contracts.py +103 -0
- package/template/apps/document-server/src/southwind_document_server/service.py +149 -0
- package/template/apps/document-server/src/southwind_document_server/store.py +185 -0
- package/template/apps/document-server/tests/__init__.py +1 -0
- package/template/apps/document-server/tests/test_config.py +42 -0
- package/template/apps/document-server/tests/test_service.py +140 -0
- package/template/apps/document-server/uv.lock +430 -0
- package/template/apps/embedding-server/Dockerfile +17 -0
- package/template/apps/embedding-server/pyproject.toml +44 -0
- package/template/apps/embedding-server/src/southwind_embedding_server/__init__.py +1 -0
- package/template/apps/embedding-server/src/southwind_embedding_server/adapter.py +254 -0
- package/template/apps/embedding-server/src/southwind_embedding_server/app.py +119 -0
- package/template/apps/embedding-server/src/southwind_embedding_server/config.py +120 -0
- package/template/apps/embedding-server/src/southwind_embedding_server/contracts.py +106 -0
- package/template/apps/embedding-server/src/southwind_embedding_server/error.py +6 -0
- package/template/apps/embedding-server/src/southwind_embedding_server/image_input.py +87 -0
- package/template/apps/embedding-server/tests/__init__.py +1 -0
- package/template/apps/embedding-server/tests/test_adapter.py +92 -0
- package/template/apps/embedding-server/tests/test_config.py +38 -0
- package/template/apps/embedding-server/uv.lock +449 -0
- package/template/apps/server/src/agent/background-operation-port.ts +37 -0
- package/template/apps/server/src/agent/background-operation.test.ts +231 -0
- package/template/apps/server/src/agent/background-operation.ts +252 -0
- package/template/apps/server/src/agent/drizzle-run-binding-repository.ts +114 -0
- package/template/apps/server/src/agent/execution-grants.ts +7 -1
- package/template/apps/server/src/agent/fake-runtime.test-support.ts +43 -0
- package/template/apps/server/src/agent/idempotency-key.test.ts +28 -0
- package/template/apps/server/src/agent/idempotency-key.ts +11 -0
- package/template/apps/server/src/agent/remote-runtime.test.ts +115 -1
- package/template/apps/server/src/agent/remote-runtime.ts +173 -5
- package/template/apps/server/src/agent/run-authorization.test-support.ts +9 -0
- package/template/apps/server/src/agent/run-authorization.test.ts +126 -0
- package/template/apps/server/src/agent/run-authorization.ts +180 -0
- package/template/apps/server/src/agent/run-binding-repository.ts +93 -0
- package/template/apps/server/src/agent/run-contracts.ts +88 -1
- package/template/apps/server/src/agent/run-definition-support.ts +58 -0
- package/template/apps/server/src/agent/run-errors.ts +25 -0
- package/template/apps/server/src/agent/run-facade.test.ts +57 -46
- package/template/apps/server/src/agent/run-facade.ts +185 -65
- package/template/apps/server/src/agent/run-release.ts +78 -0
- package/template/apps/server/src/agent/run-route-input.ts +169 -0
- package/template/apps/server/src/agent/run-routes.ts +63 -76
- package/template/apps/server/src/agent/runtime-bootstrap.ts +169 -13
- package/template/apps/server/src/agent/sandbox-client.ts +200 -0
- package/template/apps/server/src/agent/sandbox-protocol.ts +76 -0
- package/template/apps/server/src/agent/sandbox-registry.ts +205 -0
- package/template/apps/server/src/agent/sandbox-runtime.test.ts +137 -0
- package/template/apps/server/src/agent/sandbox-worker-server.ts +195 -0
- package/template/apps/server/src/agent/sandbox-worker.ts +12 -0
- package/template/apps/server/src/agent/tool-approval.test.ts +75 -0
- package/template/apps/server/src/agent/tool-approval.ts +115 -0
- package/template/apps/server/src/agent/tool-audit.ts +62 -0
- package/template/apps/server/src/agent/tool-host.test.ts +39 -11
- package/template/apps/server/src/agent/tool-host.ts +184 -17
- package/template/apps/server/src/agent/tool-registry.ts +52 -47
- package/template/apps/server/src/agent/vision-facade.test.ts +256 -0
- package/template/apps/server/src/agent/vision-input.ts +120 -0
- package/template/apps/server/src/ai-artifact/codec.test.ts +44 -0
- package/template/apps/server/src/ai-artifact/codec.ts +137 -0
- package/template/apps/server/src/ai-artifact/routes.ts +37 -0
- package/template/apps/server/src/ai-artifact/runtime.ts +205 -0
- package/template/apps/server/src/application-services.ts +62 -2
- package/template/apps/server/src/auth/reauthentication.test.ts +40 -0
- package/template/apps/server/src/auth/reauthentication.ts +19 -0
- package/template/apps/server/src/auth/runtime.ts +9 -1
- package/template/apps/server/src/data-access/context.ts +6 -4
- package/template/apps/server/src/data-access/scope-snapshot.ts +27 -0
- package/template/apps/server/src/data-governance/export/approval-port.ts +6 -3
- package/template/apps/server/src/data-governance/export/authorization-port.test.ts +23 -4
- package/template/apps/server/src/data-governance/export/authorization-port.ts +89 -25
- package/template/apps/server/src/data-governance/export/drizzle-repository.integration.test.ts +2 -0
- package/template/apps/server/src/data-governance/export/drizzle-repository.ts +1 -0
- package/template/apps/server/src/data-governance/export/job-runtime.test.ts +15 -4
- package/template/apps/server/src/data-governance/export/job-runtime.ts +68 -15
- package/template/apps/server/src/data-governance/export/ndjson-stream.ts +25 -6
- package/template/apps/server/src/data-governance/export/postgres-recovery.integration.test.ts +4 -2
- package/template/apps/server/src/data-governance/export/renderer-job-runtime.test.ts +235 -0
- package/template/apps/server/src/data-governance/export/renderer-runtime.ts +293 -0
- package/template/apps/server/src/data-governance/export/renderer-selection.test.ts +67 -0
- package/template/apps/server/src/data-governance/export/repository.test.ts +2 -0
- package/template/apps/server/src/data-governance/export/request-fingerprint.test.ts +13 -0
- package/template/apps/server/src/data-governance/export/request-fingerprint.ts +2 -0
- package/template/apps/server/src/data-governance/export/routes.ts +11 -1
- package/template/apps/server/src/data-governance/export/scope-snapshot.ts +6 -18
- package/template/apps/server/src/data-governance/export/service-support.ts +188 -0
- package/template/apps/server/src/data-governance/export/service.ts +90 -113
- package/template/apps/server/src/data-governance/export/source.ts +101 -0
- package/template/apps/server/src/data-governance/export/types.ts +27 -0
- package/template/apps/server/src/document/contracts.ts +76 -0
- package/template/apps/server/src/document/facade.ts +339 -0
- package/template/apps/server/src/document/remote-runtime.test.ts +96 -0
- package/template/apps/server/src/document/remote-runtime.ts +223 -0
- package/template/apps/server/src/document/routes.ts +193 -0
- package/template/apps/server/src/embedding/contracts.ts +83 -0
- package/template/apps/server/src/embedding/error.ts +10 -0
- package/template/apps/server/src/embedding/facade.ts +217 -0
- package/template/apps/server/src/embedding/input.ts +172 -0
- package/template/apps/server/src/embedding/remote-runtime.test.ts +66 -0
- package/template/apps/server/src/embedding/remote-runtime.ts +218 -0
- package/template/apps/server/src/embedding/routes.ts +152 -0
- package/template/apps/server/src/index.ts +40 -43
- package/template/apps/server/src/jobs/artifact-service.test.ts +14 -2
- package/template/apps/server/src/jobs/artifact-service.ts +10 -0
- package/template/apps/server/src/jobs/drizzle-execution-repository.ts +17 -18
- package/template/apps/server/src/jobs/drizzle-execution.integration.test.ts +19 -1
- package/template/apps/server/src/jobs/drizzle-mappers.ts +4 -1
- package/template/apps/server/src/jobs/drizzle-recovery-repository.ts +49 -2
- package/template/apps/server/src/jobs/drizzle-repository.ts +1 -0
- package/template/apps/server/src/jobs/execution-repository.ts +17 -0
- package/template/apps/server/src/jobs/execution.test.ts +34 -0
- package/template/apps/server/src/jobs/in-memory-control.ts +135 -0
- package/template/apps/server/src/jobs/in-memory-execution.ts +34 -65
- package/template/apps/server/src/jobs/legacy-service-runtime.ts +5 -2
- package/template/apps/server/src/jobs/repository.ts +3 -0
- package/template/apps/server/src/jobs/worker.ts +80 -41
- package/template/apps/server/src/module-host/ai-operation-port.ts +170 -0
- package/template/apps/server/src/module-host/durable-context.test.ts +312 -0
- package/template/apps/server/src/module-host/durable-context.ts +124 -0
- package/template/apps/server/src/module-host/durable-handler-registration.ts +72 -0
- package/template/apps/server/src/module-host/governed-export-route-support.ts +171 -0
- package/template/apps/server/src/module-host/governed-export-routes.test.ts +327 -0
- package/template/apps/server/src/module-host/governed-export-routes.ts +238 -0
- package/template/apps/server/src/module-host/governed-renderer-registration.test.ts +247 -0
- package/template/apps/server/src/module-host/governed-resource-routes.ts +331 -0
- package/template/apps/server/src/module-host/governed-resources.test.ts +344 -0
- package/template/apps/server/src/module-host/governed-resources.ts +203 -0
- package/template/apps/server/src/module-host/http-runtime.ts +95 -0
- package/template/apps/server/src/module-host/initialize-contracts.ts +14 -0
- package/template/apps/server/src/module-host/initialize.test.ts +49 -1
- package/template/apps/server/src/module-host/initialize.ts +39 -16
- package/template/apps/server/src/module-host/job-port.test.ts +262 -0
- package/template/apps/server/src/module-host/job-port.ts +334 -0
- package/template/apps/server/src/module-host/request-context.test.ts +88 -2
- package/template/apps/server/src/module-host/request-context.ts +56 -22
- package/template/apps/server/src/module-host/route-installer.test.ts +10 -0
- package/template/apps/server/src/module-host/route-installer.ts +36 -1
- package/template/apps/server/src/module-host/sandbox-tool-registration.ts +42 -0
- package/template/apps/server/src/module-host/tool-handlers.ts +67 -31
- package/template/apps/server/src/module-host.ts +1 -0
- package/template/apps/server/src/module-runtime-validation.ts +7 -3
- package/template/apps/server/src/speech/contracts.ts +76 -0
- package/template/apps/server/src/speech/facade.ts +316 -0
- package/template/apps/server/src/speech/remote-runtime.test.ts +88 -0
- package/template/apps/server/src/speech/remote-runtime.ts +227 -0
- package/template/apps/server/src/speech/routes.ts +180 -0
- package/template/apps/server/src/system/actor.ts +39 -0
- package/template/apps/server/src/system/route-helpers.ts +1 -0
- package/template/apps/server/src/workflow/service.ts +2 -1
- package/template/apps/speech-server/Dockerfile +22 -0
- package/template/apps/speech-server/pyproject.toml +43 -0
- package/template/apps/speech-server/src/southwind_speech_server/__init__.py +1 -0
- package/template/apps/speech-server/src/southwind_speech_server/adapter.py +249 -0
- package/template/apps/speech-server/src/southwind_speech_server/app.py +183 -0
- package/template/apps/speech-server/src/southwind_speech_server/config.py +100 -0
- package/template/apps/speech-server/src/southwind_speech_server/contracts.py +103 -0
- package/template/apps/speech-server/src/southwind_speech_server/errors.py +6 -0
- package/template/apps/speech-server/src/southwind_speech_server/normalizer.py +108 -0
- package/template/apps/speech-server/src/southwind_speech_server/output_validation.py +128 -0
- package/template/apps/speech-server/src/southwind_speech_server/service.py +162 -0
- package/template/apps/speech-server/src/southwind_speech_server/store.py +209 -0
- package/template/apps/speech-server/tests/__init__.py +1 -0
- package/template/apps/speech-server/tests/test_config.py +33 -0
- package/template/apps/speech-server/tests/test_service.py +154 -0
- package/template/apps/speech-server/uv.lock +430 -0
- package/template/apps/web/src/app/business-routes.ts +22 -0
- package/template/apps/web/src/components/common/AccessSnapshotBoundary.vue +112 -0
- package/template/apps/web/src/components/common/AccessSnapshotBoundary.webtest.ts +130 -0
- package/template/apps/web/src/components/common/AccessSnapshotNotice.vue +117 -0
- package/template/apps/web/src/components/common/FormDialog.vue +6 -0
- package/template/apps/web/src/components/common/ModalLayout.webtest.ts +13 -0
- package/template/apps/web/src/composables/access-snapshot-change.ts +131 -0
- package/template/apps/web/src/composables/access-snapshot-change.webtest.ts +103 -0
- package/template/apps/web/src/composables/useAccessSnapshot.ts +48 -2
- package/template/apps/web/src/composables/useAccessSnapshot.webtest.ts +54 -0
- package/template/apps/web/src/composables/useAccessSnapshotMonitor.ts +42 -0
- package/template/apps/web/src/layout/AppShell.vue +28 -25
- package/template/apps/web/src/layout/AppShell.webtest.ts +1 -0
- package/template/apps/web/src/router/business-routes.ts +68 -0
- package/template/apps/web/src/router/business-routes.webtest.ts +85 -0
- package/template/apps/web/src/router/index.ts +7 -0
- package/template/apps/web/src/router/shared-scaffold-neutrality.webtest.ts +12 -0
- package/template/apps/web/src/services/access-api.ts +5 -1
- package/template/apps/web/src/services/access-api.webtest.ts +35 -0
- package/template/docker-compose.yml +127 -0
- package/template/docs/architecture/ai-egress.md +12 -9
- package/template/docs/architecture/ai-runtime.md +23 -11
- package/template/docs/architecture/durable-jobs.md +25 -3
- package/template/docs/architecture/foundation-infrastructure.md +163 -0
- package/template/docs/architecture/module-host.md +115 -0
- package/template/docs/architecture/server-module-interface.md +39 -0
- package/template/docs/architecture/web-access-snapshot-resilience.md +60 -0
- package/template/docs/development/business-web-routes.md +66 -0
- package/template/docs/development/custom-login-page.md +3 -3
- package/template/docs/development/module-lifecycle.md +71 -0
- package/template/docs/operations/observability.md +5 -1
- package/template/docs/platform/agent-runtime.md +21 -11
- package/template/docs/platform/agent-tool-sandbox.md +58 -0
- package/template/docs/platform/agent-tools.md +5 -0
- package/template/docs/platform/document-ai-runtime.md +204 -0
- package/template/docs/platform/governed-export-renderers.md +132 -0
- package/template/docs/platform/navigation-registry.md +5 -0
- package/template/docs/platform/organization-membership.md +2 -1
- package/template/docs/platform/speech-ai-runtime.md +156 -0
- package/template/docs/platform/system-crud-api.md +7 -1
- package/template/docs/platform/vision-ai-runtime.md +97 -0
- package/template/docs/platform/web-system-crud.md +6 -0
- package/template/package.json +16 -2
- package/template/packages/ai-client/index.ts +4 -0
- package/template/packages/ai-client/package.json +5 -2
- package/template/packages/ai-client/src/agent/client/artifact.ts +26 -0
- package/template/packages/ai-client/src/agent/client/attach.test.ts +124 -0
- package/template/packages/ai-client/src/agent/client/client.ts +119 -7
- package/template/packages/ai-client/src/agent/client/run.ts +14 -0
- package/template/packages/ai-client/src/agent/client/types.ts +18 -0
- package/template/packages/ai-client/src/agent/client/vision.test.ts +53 -0
- package/template/packages/ai-client/src/agent/contracts/contracts.test.ts +1 -0
- package/template/packages/ai-client/src/agent/contracts/events.ts +33 -0
- package/template/packages/ai-client/src/agent/contracts/runs.ts +71 -0
- package/template/packages/ai-client/src/agent/index.ts +1 -0
- package/template/packages/ai-client/src/artifact-ref.ts +8 -0
- package/template/packages/ai-client/src/document/client.test.ts +114 -0
- package/template/packages/ai-client/src/document/client.ts +134 -0
- package/template/packages/ai-client/src/document/contracts.ts +152 -0
- package/template/packages/ai-client/src/document/error.ts +43 -0
- package/template/packages/ai-client/src/document/index.ts +3 -0
- package/template/packages/ai-client/src/document/run.ts +104 -0
- package/template/packages/ai-client/src/document/validation.ts +115 -0
- package/template/packages/ai-client/src/embedding/client.test.ts +77 -0
- package/template/packages/ai-client/src/embedding/client.ts +96 -0
- package/template/packages/ai-client/src/embedding/contracts.ts +46 -0
- package/template/packages/ai-client/src/embedding/error.ts +61 -0
- package/template/packages/ai-client/src/embedding/index.ts +3 -0
- package/template/packages/ai-client/src/embedding/validation.ts +117 -0
- package/template/packages/ai-client/src/speech/client.test.ts +135 -0
- package/template/packages/ai-client/src/speech/client.ts +104 -0
- package/template/packages/ai-client/src/speech/contracts.ts +129 -0
- package/template/packages/ai-client/src/speech/error.ts +43 -0
- package/template/packages/ai-client/src/speech/index.ts +3 -0
- package/template/packages/ai-client/src/speech/run.ts +133 -0
- package/template/packages/ai-client/src/speech/validation.ts +132 -0
- package/template/packages/config/package.json +2 -2
- package/template/packages/database/drizzle/0039_early_ezekiel_stane.sql +1 -0
- package/template/packages/database/drizzle/0040_colossal_baron_strucker.sql +63 -0
- package/template/packages/database/drizzle/0041_yummy_prowler.sql +21 -0
- package/template/packages/database/drizzle/0042_puzzling_rumiko_fujikawa.sql +27 -0
- package/template/packages/database/drizzle/meta/0039_snapshot.json +7755 -0
- package/template/packages/database/drizzle/meta/0040_snapshot.json +8150 -0
- package/template/packages/database/drizzle/meta/0041_snapshot.json +8323 -0
- package/template/packages/database/drizzle/meta/0042_snapshot.json +8541 -0
- package/template/packages/database/drizzle/meta/_journal.json +28 -0
- package/template/packages/database/package.json +2 -2
- package/template/packages/database/src/agent-runtime-schema.test.ts +54 -0
- package/template/packages/database/src/schema/agent-runtime.ts +149 -0
- package/template/packages/database/src/schema/common.ts +2 -14
- package/template/packages/database/src/schema/durable-jobs.ts +5 -0
- package/template/packages/database/src/schema/governed-exports.ts +17 -5
- package/template/packages/database/src/schema/index.ts +3 -0
- package/template/packages/database/src/schema-durable-recovery.test.ts +31 -0
- package/template/packages/database/src/schema-governed-exports.test.ts +21 -1
- package/template/packages/database/src/schema.test.ts +6 -23
- package/template/packages/jobs/package.json +3 -2
- package/template/packages/jobs/src/handlers.test.ts +13 -0
- package/template/packages/jobs/src/handlers.ts +7 -1
- package/template/packages/jobs/src/types.ts +4 -0
- package/template/packages/modules/package.json +2 -2
- package/template/packages/modules/src/ai-operation-composition.test-support.ts +165 -0
- package/template/packages/modules/src/ai-operation-composition.test.ts +91 -127
- package/template/packages/modules/src/ai-operation-composition.ts +59 -1
- package/template/packages/modules/src/manifest.ts +25 -1
- package/template/packages/modules/src/tool-composition.ts +18 -0
- package/template/packages/server-sdk/index.ts +50 -0
- package/template/packages/server-sdk/package.json +10 -4
- package/template/packages/server-sdk/src/ai-artifact-port.ts +34 -0
- package/template/packages/server-sdk/src/ai-operation-port.ts +65 -0
- package/template/packages/server-sdk/src/ai-operation-registration.ts +42 -1
- package/template/packages/server-sdk/src/background-ai-port.ts +43 -0
- package/template/packages/server-sdk/src/data-governance-registration.ts +116 -0
- package/template/packages/server-sdk/src/durable-handler.ts +29 -0
- package/template/packages/server-sdk/src/jobs-port.ts +69 -0
- package/template/packages/server-sdk/src/module-host.ts +16 -2
- package/template/packages/server-sdk/src/request-context.ts +6 -2
- package/template/packages/server-sdk/src/sandbox-tool.ts +21 -0
- package/template/packages/server-sdk/src/tool-registration.ts +2 -0
- package/template/packages/shared/package.json +1 -1
- package/template/packages/shared/src/rbac.ts +30 -0
|
@@ -30,6 +30,7 @@ _REDACTED = "[REDACTED]"
|
|
|
30
30
|
class GovernedAttempt:
|
|
31
31
|
messages: list[dict[str, JsonValue]]
|
|
32
32
|
tools: tuple[RuntimeToolDefinition, ...]
|
|
33
|
+
output_schema: dict[str, JsonValue] | None
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
class ProviderRequestGovernance:
|
|
@@ -47,8 +48,15 @@ class ProviderRequestGovernance:
|
|
|
47
48
|
tools: tuple[RuntimeToolDefinition, ...],
|
|
48
49
|
policy: ProviderDataPolicy,
|
|
49
50
|
input_value: JsonValue = None,
|
|
51
|
+
artifact_bytes: int = 0,
|
|
52
|
+
output_schema: dict[str, JsonValue] | None = None,
|
|
50
53
|
measure: Callable[
|
|
51
|
-
[
|
|
54
|
+
[
|
|
55
|
+
list[dict[str, JsonValue]],
|
|
56
|
+
tuple[RuntimeToolDefinition, ...],
|
|
57
|
+
dict[str, JsonValue] | None,
|
|
58
|
+
],
|
|
59
|
+
int,
|
|
52
60
|
]
|
|
53
61
|
| None = None,
|
|
54
62
|
) -> GovernedAttempt:
|
|
@@ -64,7 +72,7 @@ class ProviderRequestGovernance:
|
|
|
64
72
|
if rule is None:
|
|
65
73
|
raise GovernanceDenied(f"Provider 未授权发送字段:{field}")
|
|
66
74
|
classified.add(rule.classification)
|
|
67
|
-
value, changed =
|
|
75
|
+
value, changed = govern_message(message, rule.dlp_action)
|
|
68
76
|
redacted = redacted or changed
|
|
69
77
|
governed_messages.append(cast(dict[str, JsonValue], value))
|
|
70
78
|
governed_tools = tools
|
|
@@ -80,9 +88,27 @@ class ProviderRequestGovernance:
|
|
|
80
88
|
RuntimeToolDefinition.model_validate(item)
|
|
81
89
|
for item in cast(list[object], governed)
|
|
82
90
|
)
|
|
91
|
+
governed_schema = output_schema
|
|
92
|
+
if artifact_bytes:
|
|
93
|
+
artifact_rule = rules.get("artifact-content")
|
|
94
|
+
if artifact_rule is None:
|
|
95
|
+
raise GovernanceDenied("Provider 未授权发送字段:artifact-content")
|
|
96
|
+
classified.add(artifact_rule.classification)
|
|
97
|
+
if output_schema is not None:
|
|
98
|
+
schema_rule = rules.get("output-schema")
|
|
99
|
+
if schema_rule is None:
|
|
100
|
+
raise GovernanceDenied("Provider 未授权发送字段:output-schema")
|
|
101
|
+
classified.add(schema_rule.classification)
|
|
102
|
+
raw_schema, changed = govern_value(
|
|
103
|
+
output_schema,
|
|
104
|
+
schema_rule.dlp_action,
|
|
105
|
+
)
|
|
106
|
+
redacted = redacted or changed
|
|
107
|
+
governed_schema = cast(dict[str, JsonValue], raw_schema)
|
|
83
108
|
sent_bytes = (measure or payload_size)(
|
|
84
109
|
governed_messages,
|
|
85
110
|
governed_tools,
|
|
111
|
+
governed_schema,
|
|
86
112
|
)
|
|
87
113
|
decision = "redact" if redacted else "allow"
|
|
88
114
|
self._record(
|
|
@@ -94,7 +120,11 @@ class ProviderRequestGovernance:
|
|
|
94
120
|
sent_bytes,
|
|
95
121
|
decision,
|
|
96
122
|
)
|
|
97
|
-
return GovernedAttempt(
|
|
123
|
+
return GovernedAttempt(
|
|
124
|
+
governed_messages,
|
|
125
|
+
governed_tools,
|
|
126
|
+
governed_schema,
|
|
127
|
+
)
|
|
98
128
|
except GovernanceDenied:
|
|
99
129
|
self._record(
|
|
100
130
|
deployment_key,
|
|
@@ -210,13 +240,44 @@ def govern_value(value: object, action: str) -> tuple[object, bool]:
|
|
|
210
240
|
return value, False
|
|
211
241
|
|
|
212
242
|
|
|
243
|
+
def govern_message(
|
|
244
|
+
message: dict[str, JsonValue],
|
|
245
|
+
action: str,
|
|
246
|
+
) -> tuple[object, bool]:
|
|
247
|
+
content = message.get("content")
|
|
248
|
+
if message.get("role") != "user" or not isinstance(content, list):
|
|
249
|
+
return govern_value(message, action)
|
|
250
|
+
changed = False
|
|
251
|
+
governed_parts: list[object] = []
|
|
252
|
+
for part in content:
|
|
253
|
+
if not isinstance(part, dict):
|
|
254
|
+
raise GovernanceDenied("Provider 多模态消息结构无效")
|
|
255
|
+
if part.get("type") == "text":
|
|
256
|
+
governed, item_changed = govern_value(part, action)
|
|
257
|
+
governed_parts.append(governed)
|
|
258
|
+
changed = changed or item_changed
|
|
259
|
+
continue
|
|
260
|
+
image = part.get("image_url")
|
|
261
|
+
url = image.get("url") if isinstance(image, dict) else None
|
|
262
|
+
if (
|
|
263
|
+
part.get("type") != "image_url"
|
|
264
|
+
or not isinstance(url, str)
|
|
265
|
+
or not url.startswith("data:image/png;base64,")
|
|
266
|
+
):
|
|
267
|
+
raise GovernanceDenied("Provider 图片输入结构无效")
|
|
268
|
+
governed_parts.append(part)
|
|
269
|
+
return {**message, "content": governed_parts}, changed
|
|
270
|
+
|
|
271
|
+
|
|
213
272
|
def payload_size(
|
|
214
273
|
messages: list[dict[str, JsonValue]],
|
|
215
274
|
tools: tuple[RuntimeToolDefinition, ...],
|
|
275
|
+
output_schema: dict[str, JsonValue] | None = None,
|
|
216
276
|
) -> int:
|
|
217
277
|
payload = {
|
|
218
278
|
"messages": messages,
|
|
219
279
|
"tools": [tool.model_dump(by_alias=True) for tool in tools],
|
|
280
|
+
"outputSchema": output_schema,
|
|
220
281
|
}
|
|
221
282
|
return len(
|
|
222
283
|
json.dumps(
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from .adk_adapter import AdkEngineAdapter
|
|
6
|
+
from .config import EnvironmentSecretResolver, OpenAiCompatibleConfig
|
|
7
|
+
from .engine import AgentEngineAdapter
|
|
8
|
+
from .openai_provider import OpenAiCompatibleProvider
|
|
9
|
+
from .postgres_store import PostgresRunStore
|
|
10
|
+
from .provider_audit import ProviderAuditStore
|
|
11
|
+
from .provider_governance import ProviderRequestGovernance
|
|
12
|
+
from .sqlite_store import SqliteRunStore
|
|
13
|
+
from .store import InMemoryRunStore, RunStore
|
|
14
|
+
from .store_cipher import RunStoreCipher
|
|
15
|
+
from .tool_host import ToolHostClient
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def default_engine() -> AgentEngineAdapter:
|
|
19
|
+
config = OpenAiCompatibleConfig.from_environment()
|
|
20
|
+
if config is None:
|
|
21
|
+
return AdkEngineAdapter()
|
|
22
|
+
internal_token = os.environ.get("AGENT_SERVER_INTERNAL_TOKEN", "").strip()
|
|
23
|
+
if not internal_token:
|
|
24
|
+
raise ValueError("Agent Server Provider 已配置但内部 Token 缺失")
|
|
25
|
+
return AdkEngineAdapter(
|
|
26
|
+
OpenAiCompatibleProvider(
|
|
27
|
+
config,
|
|
28
|
+
EnvironmentSecretResolver(os.environ),
|
|
29
|
+
),
|
|
30
|
+
ToolHostClient(internal_token),
|
|
31
|
+
ProviderRequestGovernance(
|
|
32
|
+
ProviderAuditStore(
|
|
33
|
+
required_environment("AI_PROVIDER_AUDIT_STORE_PATH"),
|
|
34
|
+
retention_days=provider_audit_retention_days(),
|
|
35
|
+
)
|
|
36
|
+
),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def default_store() -> RunStore:
|
|
41
|
+
database_url = os.environ.get("AGENT_DATABASE_URL", "").strip()
|
|
42
|
+
path = os.environ.get("AGENT_RUN_STORE_PATH", "").strip()
|
|
43
|
+
if not database_url and not path:
|
|
44
|
+
return InMemoryRunStore()
|
|
45
|
+
cipher = RunStoreCipher(required_environment("AGENT_RUN_ENCRYPTION_SECRET"))
|
|
46
|
+
return (
|
|
47
|
+
PostgresRunStore(database_url, cipher)
|
|
48
|
+
if database_url
|
|
49
|
+
else SqliteRunStore(path, cipher)
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def required_environment(key: str) -> str:
|
|
54
|
+
value = os.environ.get(key, "").strip()
|
|
55
|
+
if not value:
|
|
56
|
+
raise ValueError(f"Provider 已配置但缺少 {key}")
|
|
57
|
+
return value
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def provider_audit_retention_days() -> int:
|
|
61
|
+
raw = os.environ.get("AI_PROVIDER_AUDIT_RETENTION_DAYS", "90")
|
|
62
|
+
try:
|
|
63
|
+
value = int(raw)
|
|
64
|
+
except ValueError as error:
|
|
65
|
+
raise ValueError("AI_PROVIDER_AUDIT_RETENTION_DAYS 必须是整数") from error
|
|
66
|
+
if not 1 <= value <= 3650:
|
|
67
|
+
raise ValueError("AI_PROVIDER_AUDIT_RETENTION_DAYS 超出允许范围")
|
|
68
|
+
return value
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
-
import
|
|
5
|
-
import json
|
|
4
|
+
import logging
|
|
6
5
|
import uuid
|
|
7
6
|
|
|
8
|
-
from .
|
|
7
|
+
from .artifact_input import ArtifactLoader
|
|
8
|
+
from .contracts import AgentError, StartRunRequest
|
|
9
9
|
from .engine import (
|
|
10
10
|
AgentEngineAdapter,
|
|
11
|
-
ApprovalRequired,
|
|
12
11
|
EngineCompleted,
|
|
13
12
|
EngineRunRequest,
|
|
14
|
-
OutputDelta,
|
|
15
13
|
SafeEngineError,
|
|
16
|
-
ToolCompleted,
|
|
17
|
-
ToolRequested,
|
|
18
|
-
UsageReported,
|
|
19
14
|
)
|
|
15
|
+
from .fingerprint import request_fingerprint, resolved_release
|
|
16
|
+
from .lease import cancel_on_lease_loss, lease_failed
|
|
17
|
+
from .signal_events import canonical_signal_event
|
|
20
18
|
from .store import IdempotencyConflictError, RunRecord, RunStore
|
|
19
|
+
from .telemetry import AgentRunTrace
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
class RunNotFoundError(Exception):
|
|
@@ -29,10 +30,18 @@ class OperationNotFoundError(Exception):
|
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
class AgentRunService:
|
|
32
|
-
def __init__(
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
engine: AgentEngineAdapter,
|
|
36
|
+
store: RunStore,
|
|
37
|
+
artifact_loader: ArtifactLoader | None = None,
|
|
38
|
+
) -> None:
|
|
33
39
|
self.engine = engine
|
|
34
40
|
self.store = store
|
|
41
|
+
self.artifact_loader = artifact_loader
|
|
35
42
|
self._tasks: set[asyncio.Task[None]] = set()
|
|
43
|
+
self._active_run_ids: set[str] = set()
|
|
44
|
+
self._recovery_task: asyncio.Task[None] | None = None
|
|
36
45
|
|
|
37
46
|
async def start(
|
|
38
47
|
self,
|
|
@@ -41,48 +50,148 @@ class AgentRunService:
|
|
|
41
50
|
) -> RunRecord:
|
|
42
51
|
if not self.engine.supports(request.operation_key):
|
|
43
52
|
raise OperationNotFoundError
|
|
44
|
-
|
|
53
|
+
missing = set(request.required_capabilities) - set(self.engine.capabilities)
|
|
54
|
+
unsupported = set(request.input_modalities) - set(self.engine.input_modalities)
|
|
55
|
+
if missing or unsupported:
|
|
56
|
+
raise SafeEngineError(
|
|
57
|
+
"MODEL_ROUTE_UNAVAILABLE",
|
|
58
|
+
"模型 Deployment 缺少 Operation 所需能力或输入模态",
|
|
59
|
+
retryable=True,
|
|
60
|
+
)
|
|
61
|
+
fingerprint = request_fingerprint(request)
|
|
45
62
|
candidate = RunRecord(
|
|
46
63
|
run_id=f"run_{uuid.uuid4().hex}",
|
|
47
64
|
operation_key=request.operation_key,
|
|
48
65
|
input=request.input,
|
|
49
66
|
idempotency_key=idempotency_key,
|
|
50
67
|
fingerprint=fingerprint,
|
|
68
|
+
release=resolved_release(request),
|
|
69
|
+
trace_id=request.trace_id,
|
|
70
|
+
)
|
|
71
|
+
record, created = await self.store.create_or_get(
|
|
72
|
+
candidate,
|
|
73
|
+
request.model_dump(by_alias=True),
|
|
51
74
|
)
|
|
52
|
-
record, created = await self.store.create_or_get(candidate)
|
|
53
75
|
if created:
|
|
54
|
-
|
|
55
|
-
self._tasks.add(task)
|
|
56
|
-
task.add_done_callback(self._tasks.discard)
|
|
76
|
+
self._spawn(record, request)
|
|
57
77
|
return record
|
|
58
78
|
|
|
79
|
+
async def recover(self) -> None:
|
|
80
|
+
await self._recover_once()
|
|
81
|
+
if self._recovery_task is None:
|
|
82
|
+
self._recovery_task = asyncio.create_task(self._recovery_loop())
|
|
83
|
+
|
|
84
|
+
async def close(self) -> None:
|
|
85
|
+
if self._recovery_task:
|
|
86
|
+
self._recovery_task.cancel()
|
|
87
|
+
for task in list(self._tasks):
|
|
88
|
+
task.cancel()
|
|
89
|
+
await asyncio.gather(
|
|
90
|
+
*([self._recovery_task] if self._recovery_task else []),
|
|
91
|
+
*self._tasks,
|
|
92
|
+
return_exceptions=True,
|
|
93
|
+
)
|
|
94
|
+
await self.store.close()
|
|
95
|
+
|
|
96
|
+
async def _recover_once(self) -> None:
|
|
97
|
+
for record, raw in await self.store.recoverable():
|
|
98
|
+
if record.run_id in self._active_run_ids:
|
|
99
|
+
continue
|
|
100
|
+
self._spawn(
|
|
101
|
+
record,
|
|
102
|
+
StartRunRequest.model_validate(raw),
|
|
103
|
+
resumed=True,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
async def _recovery_loop(self) -> None:
|
|
107
|
+
while True:
|
|
108
|
+
await asyncio.sleep(10)
|
|
109
|
+
try:
|
|
110
|
+
await self._recover_once()
|
|
111
|
+
except asyncio.CancelledError:
|
|
112
|
+
raise
|
|
113
|
+
except Exception:
|
|
114
|
+
logger.exception("Agent Run 恢复轮询失败,将继续重试")
|
|
115
|
+
|
|
116
|
+
async def record_evaluation(
|
|
117
|
+
self,
|
|
118
|
+
release_id: str,
|
|
119
|
+
evaluation_set_key: str,
|
|
120
|
+
evaluation_set_version: str,
|
|
121
|
+
metrics: dict[str, float],
|
|
122
|
+
) -> str:
|
|
123
|
+
return await self.store.record_evaluation(
|
|
124
|
+
release_id,
|
|
125
|
+
evaluation_set_key,
|
|
126
|
+
evaluation_set_version,
|
|
127
|
+
metrics,
|
|
128
|
+
)
|
|
129
|
+
|
|
59
130
|
async def get(self, run_id: str) -> RunRecord:
|
|
60
131
|
record = await self.store.get(run_id)
|
|
61
132
|
if record is None:
|
|
62
133
|
raise RunNotFoundError
|
|
63
134
|
return record
|
|
64
135
|
|
|
136
|
+
async def probe(self) -> None:
|
|
137
|
+
await self.store.probe()
|
|
138
|
+
|
|
65
139
|
async def cancel(self, run_id: str) -> RunRecord:
|
|
66
140
|
record = await self.get(run_id)
|
|
67
141
|
if record.terminal:
|
|
68
142
|
return record
|
|
69
143
|
await self.engine.cancel(run_id)
|
|
144
|
+
await self._resolve_pending_approval(record, "cancelled")
|
|
70
145
|
await self.store.append(run_id, "run.cancelled")
|
|
71
|
-
return
|
|
146
|
+
return await self.get(run_id)
|
|
72
147
|
|
|
73
148
|
async def _execute(
|
|
74
149
|
self,
|
|
75
150
|
record: RunRecord,
|
|
76
151
|
invocation: StartRunRequest,
|
|
152
|
+
resumed: bool = False,
|
|
77
153
|
) -> None:
|
|
78
|
-
|
|
154
|
+
execution_task = asyncio.current_task()
|
|
155
|
+
if execution_task is None:
|
|
156
|
+
raise RuntimeError("Agent Run 缺少执行任务")
|
|
157
|
+
lease_task = asyncio.create_task(self._renew_lease(record.run_id))
|
|
158
|
+
run_trace = AgentRunTrace(record)
|
|
159
|
+
run_trace.start()
|
|
160
|
+
final_status = "unknown"
|
|
161
|
+
lease_task.add_done_callback(
|
|
162
|
+
lambda completed: cancel_on_lease_loss(completed, execution_task)
|
|
163
|
+
)
|
|
79
164
|
try:
|
|
165
|
+
await self.store.append(
|
|
166
|
+
record.run_id,
|
|
167
|
+
"run.resumed" if resumed else "run.started",
|
|
168
|
+
)
|
|
169
|
+
if invocation.artifacts and not self.artifact_loader:
|
|
170
|
+
raise SafeEngineError(
|
|
171
|
+
"RUNTIME_UNAVAILABLE",
|
|
172
|
+
"Agent Artifact Runtime 未配置",
|
|
173
|
+
retryable=True,
|
|
174
|
+
)
|
|
175
|
+
prepared = (
|
|
176
|
+
await self.artifact_loader.prepare(
|
|
177
|
+
invocation.artifacts,
|
|
178
|
+
invocation.input_modalities,
|
|
179
|
+
invocation.limits,
|
|
180
|
+
)
|
|
181
|
+
if self.artifact_loader
|
|
182
|
+
else ()
|
|
183
|
+
)
|
|
80
184
|
request = EngineRunRequest(
|
|
81
185
|
run_id=record.run_id,
|
|
82
186
|
operation_key=record.operation_key,
|
|
83
|
-
input=
|
|
187
|
+
input=invocation.input,
|
|
188
|
+
input_modalities=tuple(invocation.input_modalities),
|
|
189
|
+
required_capabilities=tuple(invocation.required_capabilities),
|
|
190
|
+
artifacts=prepared,
|
|
84
191
|
instructions=invocation.instructions,
|
|
85
192
|
user_prompt=invocation.user_prompt,
|
|
193
|
+
output_schema_key=invocation.output_schema_key,
|
|
194
|
+
output_schema=invocation.output_schema,
|
|
86
195
|
allowed_tools=tuple(invocation.allowed_tools),
|
|
87
196
|
limits=invocation.limits,
|
|
88
197
|
provider_egress=invocation.provider_egress,
|
|
@@ -91,12 +200,26 @@ class AgentRunService:
|
|
|
91
200
|
execution_grant=invocation.execution_grant,
|
|
92
201
|
tool_host_url=invocation.tool_host_url,
|
|
93
202
|
)
|
|
94
|
-
|
|
203
|
+
approval_timeout = max(
|
|
204
|
+
(
|
|
205
|
+
tool.approval_timeout_seconds or 0
|
|
206
|
+
for tool in invocation.allowed_tools
|
|
207
|
+
if tool.approval == "required"
|
|
208
|
+
),
|
|
209
|
+
default=0,
|
|
210
|
+
)
|
|
211
|
+
async with asyncio.timeout(
|
|
212
|
+
max(
|
|
213
|
+
invocation.limits.max_duration_seconds,
|
|
214
|
+
approval_timeout + (5 if approval_timeout else 0),
|
|
215
|
+
)
|
|
216
|
+
):
|
|
95
217
|
async for signal in self.engine.execute(request):
|
|
96
218
|
if isinstance(signal, EngineCompleted):
|
|
97
219
|
await self.store.complete(record.run_id, signal.output)
|
|
98
220
|
return
|
|
99
|
-
|
|
221
|
+
event_type, fields = canonical_signal_event(signal)
|
|
222
|
+
await self.store.append(record.run_id, event_type, fields)
|
|
100
223
|
await self._fail(
|
|
101
224
|
record.run_id,
|
|
102
225
|
SafeEngineError(
|
|
@@ -108,6 +231,8 @@ class AgentRunService:
|
|
|
108
231
|
except SafeEngineError as error:
|
|
109
232
|
await self._fail(record.run_id, error)
|
|
110
233
|
except TimeoutError:
|
|
234
|
+
current = await self.get(record.run_id)
|
|
235
|
+
await self._resolve_pending_approval(current, "timed-out")
|
|
111
236
|
await self._fail(
|
|
112
237
|
record.run_id,
|
|
113
238
|
SafeEngineError(
|
|
@@ -116,6 +241,18 @@ class AgentRunService:
|
|
|
116
241
|
retryable=True,
|
|
117
242
|
),
|
|
118
243
|
)
|
|
244
|
+
except asyncio.CancelledError:
|
|
245
|
+
if lease_failed(lease_task):
|
|
246
|
+
await self._fail(
|
|
247
|
+
record.run_id,
|
|
248
|
+
SafeEngineError(
|
|
249
|
+
"RUNTIME_UNAVAILABLE",
|
|
250
|
+
"Agent Run 租约已丢失",
|
|
251
|
+
retryable=True,
|
|
252
|
+
),
|
|
253
|
+
)
|
|
254
|
+
return
|
|
255
|
+
raise
|
|
119
256
|
except Exception:
|
|
120
257
|
await self._fail(
|
|
121
258
|
record.run_id,
|
|
@@ -125,36 +262,71 @@ class AgentRunService:
|
|
|
125
262
|
retryable=True,
|
|
126
263
|
),
|
|
127
264
|
)
|
|
265
|
+
finally:
|
|
266
|
+
try:
|
|
267
|
+
lease_task.cancel()
|
|
268
|
+
await asyncio.gather(lease_task, return_exceptions=True)
|
|
269
|
+
final_record = await self.store.get(record.run_id)
|
|
270
|
+
if final_record and final_record.terminal:
|
|
271
|
+
await self.store.clear_invocation(record.run_id)
|
|
272
|
+
if final_record:
|
|
273
|
+
final_status = final_record.status
|
|
274
|
+
try:
|
|
275
|
+
await self.store.release_lease(record.run_id)
|
|
276
|
+
except KeyError:
|
|
277
|
+
pass
|
|
278
|
+
finally:
|
|
279
|
+
run_trace.finish(final_status)
|
|
128
280
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
281
|
+
def _spawn(
|
|
282
|
+
self,
|
|
283
|
+
record: RunRecord,
|
|
284
|
+
request: StartRunRequest,
|
|
285
|
+
resumed: bool = False,
|
|
286
|
+
) -> None:
|
|
287
|
+
if record.run_id in self._active_run_ids:
|
|
288
|
+
return
|
|
289
|
+
self._active_run_ids.add(record.run_id)
|
|
290
|
+
task = asyncio.create_task(self._execute(record, request, resumed))
|
|
291
|
+
self._tasks.add(task)
|
|
292
|
+
task.add_done_callback(
|
|
293
|
+
lambda completed: self._completed(record.run_id, completed)
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
async def _renew_lease(self, run_id: str) -> None:
|
|
297
|
+
while True:
|
|
298
|
+
await asyncio.sleep(10)
|
|
299
|
+
await self.store.renew_lease(run_id)
|
|
300
|
+
|
|
301
|
+
def _completed(self, run_id: str, task: asyncio.Task[None]) -> None:
|
|
302
|
+
self._tasks.discard(task)
|
|
303
|
+
self._active_run_ids.discard(run_id)
|
|
304
|
+
|
|
305
|
+
async def _resolve_pending_approval(
|
|
306
|
+
self,
|
|
307
|
+
record: RunRecord,
|
|
308
|
+
outcome: str,
|
|
309
|
+
) -> None:
|
|
310
|
+
pending: dict[str, object] | None = None
|
|
311
|
+
for event in record.events:
|
|
312
|
+
if event.get("type") == "approval.required":
|
|
313
|
+
pending = event
|
|
314
|
+
elif event.get("type") == "approval.resolved":
|
|
315
|
+
pending = None
|
|
316
|
+
if not pending:
|
|
317
|
+
return
|
|
318
|
+
work_item_id = pending.get("workItemId")
|
|
319
|
+
tool_call_id = pending.get("toolCallId")
|
|
320
|
+
if isinstance(work_item_id, str) and isinstance(tool_call_id, str):
|
|
151
321
|
await self.store.append(
|
|
152
|
-
run_id,
|
|
153
|
-
"
|
|
154
|
-
{
|
|
322
|
+
record.run_id,
|
|
323
|
+
"approval.resolved",
|
|
324
|
+
{
|
|
325
|
+
"workItemId": work_item_id,
|
|
326
|
+
"toolCallId": tool_call_id,
|
|
327
|
+
"outcome": outcome,
|
|
328
|
+
},
|
|
155
329
|
)
|
|
156
|
-
else:
|
|
157
|
-
raise TypeError("unsupported engine signal")
|
|
158
330
|
|
|
159
331
|
async def _fail(self, run_id: str, failure: SafeEngineError) -> None:
|
|
160
332
|
error = AgentError(
|
|
@@ -170,14 +342,4 @@ class AgentRunService:
|
|
|
170
342
|
)
|
|
171
343
|
|
|
172
344
|
|
|
173
|
-
def request_fingerprint(operation_key: str, input_value: JsonValue) -> str:
|
|
174
|
-
encoded = json.dumps(
|
|
175
|
-
{"operationKey": operation_key, "input": input_value},
|
|
176
|
-
ensure_ascii=False,
|
|
177
|
-
separators=(",", ":"),
|
|
178
|
-
sort_keys=True,
|
|
179
|
-
).encode()
|
|
180
|
-
return hashlib.sha256(encoded).hexdigest()
|
|
181
|
-
|
|
182
|
-
|
|
183
345
|
__all__ = ["AgentRunService", "IdempotencyConflictError"]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .engine import (
|
|
4
|
+
ApprovalRequired,
|
|
5
|
+
ApprovalResolved,
|
|
6
|
+
OutputDelta,
|
|
7
|
+
ToolCompleted,
|
|
8
|
+
ToolRequested,
|
|
9
|
+
UsageReported,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def canonical_signal_event(signal: object) -> tuple[str, dict[str, object]]:
|
|
14
|
+
if isinstance(signal, OutputDelta):
|
|
15
|
+
return "output.delta", {"text": signal.text}
|
|
16
|
+
if isinstance(signal, ToolRequested):
|
|
17
|
+
return "tool.requested", {
|
|
18
|
+
"toolCallId": signal.tool_call_id,
|
|
19
|
+
"toolKey": signal.tool_key,
|
|
20
|
+
}
|
|
21
|
+
if isinstance(signal, ApprovalRequired):
|
|
22
|
+
return "approval.required", {
|
|
23
|
+
"workItemId": signal.work_item_id,
|
|
24
|
+
"toolCallId": signal.tool_call_id,
|
|
25
|
+
}
|
|
26
|
+
if isinstance(signal, ApprovalResolved):
|
|
27
|
+
return "approval.resolved", {
|
|
28
|
+
"workItemId": signal.work_item_id,
|
|
29
|
+
"toolCallId": signal.tool_call_id,
|
|
30
|
+
"outcome": signal.outcome,
|
|
31
|
+
}
|
|
32
|
+
if isinstance(signal, ToolCompleted):
|
|
33
|
+
return "tool.completed", {"toolCallId": signal.tool_call_id}
|
|
34
|
+
if isinstance(signal, UsageReported):
|
|
35
|
+
return "usage.reported", {"usage": signal.usage}
|
|
36
|
+
raise TypeError("unsupported engine signal")
|