@stigmer/server 3.21.0 → 3.22.1-dev.20260922095755
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/dist/authorization/authorizer.d.ts +4 -8
- package/dist/authorization/authorizer.d.ts.map +1 -1
- package/dist/authorization/authorizer.js +5 -9
- package/dist/authorization/authorizer.js.map +1 -1
- package/dist/authorization/derived-tuples.d.ts.map +1 -1
- package/dist/authorization/derived-tuples.js +10 -25
- package/dist/authorization/derived-tuples.js.map +1 -1
- package/dist/authorization/evaluator.d.ts +13 -8
- package/dist/authorization/evaluator.d.ts.map +1 -1
- package/dist/authorization/evaluator.js +4 -20
- package/dist/authorization/evaluator.js.map +1 -1
- package/dist/authorization/list-read-scope.d.ts.map +1 -1
- package/dist/authorization/list-read-scope.js +24 -20
- package/dist/authorization/list-read-scope.js.map +1 -1
- package/dist/authorization/model/agent.d.ts.map +1 -1
- package/dist/authorization/model/agent.js +4 -5
- package/dist/authorization/model/agent.js.map +1 -1
- package/dist/authorization/model/agent_instance.d.ts.map +1 -1
- package/dist/authorization/model/agent_instance.js +2 -2
- package/dist/authorization/model/agent_instance.js.map +1 -1
- package/dist/authorization/model/mcp_server.d.ts.map +1 -1
- package/dist/authorization/model/mcp_server.js +2 -2
- package/dist/authorization/model/mcp_server.js.map +1 -1
- package/dist/authorization/model/organization.d.ts.map +1 -1
- package/dist/authorization/model/organization.js +2 -0
- package/dist/authorization/model/organization.js.map +1 -1
- package/dist/authorization/model/plugin.d.ts.map +1 -1
- package/dist/authorization/model/plugin.js +3 -4
- package/dist/authorization/model/plugin.js.map +1 -1
- package/dist/authorization/model/rewrite.d.ts +8 -10
- package/dist/authorization/model/rewrite.d.ts.map +1 -1
- package/dist/authorization/model/rewrite.js +0 -3
- package/dist/authorization/model/rewrite.js.map +1 -1
- package/dist/authorization/model/skill.d.ts.map +1 -1
- package/dist/authorization/model/skill.js +2 -2
- package/dist/authorization/model/skill.js.map +1 -1
- package/dist/authorization/model/workflow.d.ts.map +1 -1
- package/dist/authorization/model/workflow.js +2 -2
- package/dist/authorization/model/workflow.js.map +1 -1
- package/dist/authorization/model/workflow_instance.d.ts.map +1 -1
- package/dist/authorization/model/workflow_instance.js +2 -2
- package/dist/authorization/model/workflow_instance.js.map +1 -1
- package/dist/authorization/organization-directory.d.ts.map +1 -1
- package/dist/authorization/organization-directory.js +2 -3
- package/dist/authorization/organization-directory.js.map +1 -1
- package/dist/authorization/store-test-kit.d.ts +0 -6
- package/dist/authorization/store-test-kit.d.ts.map +1 -1
- package/dist/authorization/store-test-kit.js +16 -62
- package/dist/authorization/store-test-kit.js.map +1 -1
- package/dist/authorization/tuples.d.ts +13 -32
- package/dist/authorization/tuples.d.ts.map +1 -1
- package/dist/authorization/tuples.js +3 -11
- package/dist/authorization/tuples.js.map +1 -1
- package/dist/domain/agent/controller.d.ts.map +1 -1
- package/dist/domain/agent/controller.js +6 -4
- package/dist/domain/agent/controller.js.map +1 -1
- package/dist/domain/agentchannel/controller.d.ts.map +1 -1
- package/dist/domain/agentchannel/controller.js +9 -2
- package/dist/domain/agentchannel/controller.js.map +1 -1
- package/dist/domain/agentchannel/steps.d.ts +19 -0
- package/dist/domain/agentchannel/steps.d.ts.map +1 -1
- package/dist/domain/agentchannel/steps.js +32 -0
- package/dist/domain/agentchannel/steps.js.map +1 -1
- package/dist/domain/agentexecution/controller.d.ts.map +1 -1
- package/dist/domain/agentexecution/controller.js +3 -1
- package/dist/domain/agentexecution/controller.js.map +1 -1
- package/dist/domain/agentinstance/controller.d.ts +5 -5
- package/dist/domain/agentinstance/controller.d.ts.map +1 -1
- package/dist/domain/agentinstance/controller.js +9 -5
- package/dist/domain/agentinstance/controller.js.map +1 -1
- package/dist/domain/agentinstance/steps.d.ts +43 -8
- package/dist/domain/agentinstance/steps.d.ts.map +1 -1
- package/dist/domain/agentinstance/steps.js +71 -8
- package/dist/domain/agentinstance/steps.js.map +1 -1
- package/dist/domain/agentshare/constants.d.ts +6 -11
- package/dist/domain/agentshare/constants.d.ts.map +1 -1
- package/dist/domain/agentshare/constants.js +8 -16
- package/dist/domain/agentshare/constants.js.map +1 -1
- package/dist/domain/agentshare/controller.d.ts.map +1 -1
- package/dist/domain/agentshare/controller.js +10 -2
- package/dist/domain/agentshare/controller.js.map +1 -1
- package/dist/domain/agentshare/steps.d.ts +61 -10
- package/dist/domain/agentshare/steps.d.ts.map +1 -1
- package/dist/domain/agentshare/steps.js +121 -117
- package/dist/domain/agentshare/steps.js.map +1 -1
- package/dist/domain/artifact/controller.js +21 -25
- package/dist/domain/artifact/controller.js.map +1 -1
- package/dist/domain/channelapp/controller.d.ts.map +1 -1
- package/dist/domain/channelapp/controller.js +2 -0
- package/dist/domain/channelapp/controller.js.map +1 -1
- package/dist/domain/environment/controller.d.ts.map +1 -1
- package/dist/domain/environment/controller.js +4 -3
- package/dist/domain/environment/controller.js.map +1 -1
- package/dist/domain/executioncontext/controller.d.ts.map +1 -1
- package/dist/domain/executioncontext/controller.js +4 -1
- package/dist/domain/executioncontext/controller.js.map +1 -1
- package/dist/domain/executioncontext/steps.d.ts.map +1 -1
- package/dist/domain/executioncontext/steps.js +2 -1
- package/dist/domain/executioncontext/steps.js.map +1 -1
- package/dist/domain/mcpserver/connect.d.ts.map +1 -1
- package/dist/domain/mcpserver/connect.js +8 -7
- package/dist/domain/mcpserver/connect.js.map +1 -1
- package/dist/domain/mcpserver/controller.d.ts.map +1 -1
- package/dist/domain/mcpserver/controller.js +5 -4
- package/dist/domain/mcpserver/controller.js.map +1 -1
- package/dist/domain/mcpserver/start-connect.d.ts.map +1 -1
- package/dist/domain/mcpserver/start-connect.js +8 -7
- package/dist/domain/mcpserver/start-connect.js.map +1 -1
- package/dist/domain/memory/controller.d.ts.map +1 -1
- package/dist/domain/memory/controller.js +6 -1
- package/dist/domain/memory/controller.js.map +1 -1
- package/dist/domain/memory/steps.d.ts +23 -0
- package/dist/domain/memory/steps.d.ts.map +1 -1
- package/dist/domain/memory/steps.js +35 -0
- package/dist/domain/memory/steps.js.map +1 -1
- package/dist/domain/oauthapp/controller.d.ts.map +1 -1
- package/dist/domain/oauthapp/controller.js +2 -0
- package/dist/domain/oauthapp/controller.js.map +1 -1
- package/dist/domain/plugin/controller.d.ts.map +1 -1
- package/dist/domain/plugin/controller.js +6 -27
- package/dist/domain/plugin/controller.js.map +1 -1
- package/dist/domain/plugin/push.d.ts +7 -5
- package/dist/domain/plugin/push.d.ts.map +1 -1
- package/dist/domain/plugin/push.js +8 -14
- package/dist/domain/plugin/push.js.map +1 -1
- package/dist/domain/schedule/controller.d.ts.map +1 -1
- package/dist/domain/schedule/controller.js +10 -2
- package/dist/domain/schedule/controller.js.map +1 -1
- package/dist/domain/schedule/steps.d.ts +19 -0
- package/dist/domain/schedule/steps.d.ts.map +1 -1
- package/dist/domain/schedule/steps.js +32 -0
- package/dist/domain/schedule/steps.js.map +1 -1
- package/dist/domain/session/controller.d.ts.map +1 -1
- package/dist/domain/session/controller.js +3 -2
- package/dist/domain/session/controller.js.map +1 -1
- package/dist/domain/session/steps.d.ts +8 -9
- package/dist/domain/session/steps.d.ts.map +1 -1
- package/dist/domain/session/steps.js +17 -21
- package/dist/domain/session/steps.js.map +1 -1
- package/dist/domain/skill/controller.d.ts.map +1 -1
- package/dist/domain/skill/controller.js +8 -19
- package/dist/domain/skill/controller.js.map +1 -1
- package/dist/domain/workflow/agent-call-references.d.ts +54 -0
- package/dist/domain/workflow/agent-call-references.d.ts.map +1 -0
- package/dist/domain/workflow/agent-call-references.js +97 -0
- package/dist/domain/workflow/agent-call-references.js.map +1 -0
- package/dist/domain/workflow/controller.d.ts.map +1 -1
- package/dist/domain/workflow/controller.js +44 -113
- package/dist/domain/workflow/controller.js.map +1 -1
- package/dist/domain/workflow/registry/data/task-kind-registry.json +1 -1
- package/dist/domain/workflow/validation/task-config-constraints.d.ts +10 -1
- package/dist/domain/workflow/validation/task-config-constraints.d.ts.map +1 -1
- package/dist/domain/workflow/validation/task-config-constraints.js +3 -2
- package/dist/domain/workflow/validation/task-config-constraints.js.map +1 -1
- package/dist/domain/workflow/version-resolution.d.ts +5 -0
- package/dist/domain/workflow/version-resolution.d.ts.map +1 -0
- package/dist/domain/workflow/version-resolution.js +113 -0
- package/dist/domain/workflow/version-resolution.js.map +1 -0
- package/dist/domain/workflowexecution/controller.d.ts.map +1 -1
- package/dist/domain/workflowexecution/controller.js +4 -2
- package/dist/domain/workflowexecution/controller.js.map +1 -1
- package/dist/domain/workflowinstance/controller.d.ts.map +1 -1
- package/dist/domain/workflowinstance/controller.js +9 -5
- package/dist/domain/workflowinstance/controller.js.map +1 -1
- package/dist/domain/workflowinstance/steps.d.ts +20 -0
- package/dist/domain/workflowinstance/steps.d.ts.map +1 -1
- package/dist/domain/workflowinstance/steps.js +39 -0
- package/dist/domain/workflowinstance/steps.js.map +1 -1
- package/dist/extensions/authorization-queries.d.ts +6 -5
- package/dist/extensions/authorization-queries.d.ts.map +1 -1
- package/dist/extensions/identity.d.ts +19 -0
- package/dist/extensions/identity.d.ts.map +1 -1
- package/dist/extensions/identity.js +22 -3
- package/dist/extensions/identity.js.map +1 -1
- package/dist/extensions/list-read-scope.d.ts +42 -8
- package/dist/extensions/list-read-scope.d.ts.map +1 -1
- package/dist/extensions/list-read-scope.js +13 -2
- package/dist/extensions/list-read-scope.js.map +1 -1
- package/dist/extensions/resource-authorization.d.ts +3 -2
- package/dist/extensions/resource-authorization.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pipeline/apiresource-meta.d.ts +12 -3
- package/dist/pipeline/apiresource-meta.d.ts.map +1 -1
- package/dist/pipeline/apiresource-meta.js +21 -10
- package/dist/pipeline/apiresource-meta.js.map +1 -1
- package/dist/pipeline/steps/authorization-tuples.d.ts +9 -3
- package/dist/pipeline/steps/authorization-tuples.d.ts.map +1 -1
- package/dist/pipeline/steps/authorization-tuples.js +9 -11
- package/dist/pipeline/steps/authorization-tuples.js.map +1 -1
- package/dist/pipeline/steps/authorize-resolved-target.d.ts +88 -0
- package/dist/pipeline/steps/authorize-resolved-target.d.ts.map +1 -0
- package/dist/pipeline/steps/authorize-resolved-target.js +36 -0
- package/dist/pipeline/steps/authorize-resolved-target.js.map +1 -0
- package/dist/pipeline/steps/authorize.d.ts +27 -0
- package/dist/pipeline/steps/authorize.d.ts.map +1 -1
- package/dist/pipeline/steps/authorize.js +49 -10
- package/dist/pipeline/steps/authorize.js.map +1 -1
- package/dist/pipeline/steps/guard-memory-capture.d.ts.map +1 -1
- package/dist/pipeline/steps/guard-memory-capture.js +2 -1
- package/dist/pipeline/steps/guard-memory-capture.js.map +1 -1
- package/dist/pipeline/steps/guard-reserved-labels.d.ts.map +1 -1
- package/dist/pipeline/steps/guard-reserved-labels.js +6 -7
- package/dist/pipeline/steps/guard-reserved-labels.js.map +1 -1
- package/dist/pipeline/steps/helpers.d.ts +9 -5
- package/dist/pipeline/steps/helpers.d.ts.map +1 -1
- package/dist/pipeline/steps/helpers.js +11 -15
- package/dist/pipeline/steps/helpers.js.map +1 -1
- package/dist/pipeline/steps/references.d.ts +178 -20
- package/dist/pipeline/steps/references.d.ts.map +1 -1
- package/dist/pipeline/steps/references.js +344 -43
- package/dist/pipeline/steps/references.js.map +1 -1
- package/dist/pipeline/steps/version-history.d.ts +11 -0
- package/dist/pipeline/steps/version-history.d.ts.map +1 -1
- package/dist/pipeline/steps/version-history.js +26 -0
- package/dist/pipeline/steps/version-history.js.map +1 -1
- package/dist/query/search/controller.js +0 -1
- package/dist/query/search/controller.js.map +1 -1
- package/dist/query/search/criteria.d.ts +1 -5
- package/dist/query/search/criteria.d.ts.map +1 -1
- package/dist/query/search/criteria.js +3 -13
- package/dist/query/search/criteria.js.map +1 -1
- package/dist/query/search/handler.d.ts +5 -4
- package/dist/query/search/handler.d.ts.map +1 -1
- package/dist/query/search/handler.js +7 -6
- package/dist/query/search/handler.js.map +1 -1
- package/dist/query/search/query-store.d.ts.map +1 -1
- package/dist/query/search/query-store.js +0 -2
- package/dist/query/search/query-store.js.map +1 -1
- package/dist/store/interface.d.ts +3 -6
- package/dist/store/interface.d.ts.map +1 -1
- package/dist/store/interface.js.map +1 -1
- package/dist/store/postgres/migrations.d.ts +3 -1
- package/dist/store/postgres/migrations.d.ts.map +1 -1
- package/dist/store/postgres/migrations.js +35 -1
- package/dist/store/postgres/migrations.js.map +1 -1
- package/dist/store/postgres/store.d.ts.map +1 -1
- package/dist/store/postgres/store.js +4 -9
- package/dist/store/postgres/store.js.map +1 -1
- package/dist/store/public-visibility-retired.d.ts +67 -0
- package/dist/store/public-visibility-retired.d.ts.map +1 -0
- package/dist/store/public-visibility-retired.js +55 -0
- package/dist/store/public-visibility-retired.js.map +1 -0
- package/dist/store/sqlite/migrations.d.ts +3 -1
- package/dist/store/sqlite/migrations.d.ts.map +1 -1
- package/dist/store/sqlite/migrations.js +36 -1
- package/dist/store/sqlite/migrations.js.map +1 -1
- package/dist/store/sqlite/store.d.ts.map +1 -1
- package/dist/store/sqlite/store.js +4 -8
- package/dist/store/sqlite/store.js.map +1 -1
- package/package.json +6 -6
- package/src/authorization/README.md +24 -26
- package/src/authorization/__tests__/authorizer.test.ts +10 -9
- package/src/authorization/__tests__/derived-tuples.test.ts +4 -11
- package/src/authorization/__tests__/evaluator.test.ts +14 -73
- package/src/authorization/__tests__/facts.test.ts +2 -2
- package/src/authorization/__tests__/fixtures/fga/blueprint-private-visibility.fga.yaml +6 -98
- package/src/authorization/__tests__/fixtures/fga/default-instance-inheritance.fga.yaml +4 -61
- package/src/authorization/__tests__/fixtures/fga/org-admin-owner-inheritance.fga.yaml +1 -21
- package/src/authorization/__tests__/fixtures/fga/platform-visibility.fga.yaml +1 -25
- package/src/authorization/__tests__/fixtures/fga/workflow-execution-sharing.fga.yaml +1 -3
- package/src/authorization/__tests__/list-read-scope.test.ts +41 -29
- package/src/authorization/__tests__/skip-lane-inventory.test.ts +160 -0
- package/src/authorization/__tests__/store-tests.test.ts +1 -6
- package/src/authorization/__tests__/tuples.test.ts +12 -11
- package/src/authorization/__tests__/wire-permissions.test.ts +5 -5
- package/src/authorization/authorizer.ts +4 -9
- package/src/authorization/derived-tuples.ts +14 -31
- package/src/authorization/evaluator.ts +15 -44
- package/src/authorization/list-read-scope.ts +27 -22
- package/src/authorization/model/__tests__/registry.test.ts +2 -0
- package/src/authorization/model/agent.ts +2 -5
- package/src/authorization/model/agent_instance.ts +0 -2
- package/src/authorization/model/mcp_server.ts +0 -2
- package/src/authorization/model/organization.ts +2 -0
- package/src/authorization/model/plugin.ts +1 -4
- package/src/authorization/model/rewrite.ts +8 -13
- package/src/authorization/model/skill.ts +0 -2
- package/src/authorization/model/workflow.ts +0 -2
- package/src/authorization/model/workflow_instance.ts +0 -2
- package/src/authorization/organization-directory.ts +1 -3
- package/src/authorization/store-test-kit.ts +16 -75
- package/src/authorization/tuples.ts +16 -39
- package/src/domain/agent/__tests__/agent.test.ts +11 -4
- package/src/domain/agent/controller.ts +17 -7
- package/src/domain/agentchannel/__tests__/agentchannel.test.ts +223 -48
- package/src/domain/agentchannel/__tests__/create-authorization.test.ts +58 -0
- package/src/domain/agentchannel/controller.ts +25 -1
- package/src/domain/agentchannel/steps.ts +40 -0
- package/src/domain/agentexecution/controller.ts +6 -1
- package/src/domain/agentinstance/__tests__/create-authorization.composed.test.ts +243 -0
- package/src/domain/agentinstance/__tests__/create-authorization.test.ts +144 -0
- package/src/domain/agentinstance/controller.ts +30 -17
- package/src/domain/agentinstance/steps.ts +82 -8
- package/src/domain/agentshare/__tests__/agentshare.test.ts +196 -172
- package/src/domain/agentshare/__tests__/create-authorization.test.ts +71 -0
- package/src/domain/agentshare/__tests__/member-audience.test.ts +113 -0
- package/src/domain/agentshare/constants.ts +14 -26
- package/src/domain/agentshare/controller.ts +27 -1
- package/src/domain/agentshare/steps.ts +168 -155
- package/src/domain/artifact/controller.ts +18 -22
- package/src/domain/channelapp/controller.ts +10 -0
- package/src/domain/environment/controller.ts +15 -8
- package/src/domain/executioncontext/__tests__/executioncontext.test.ts +1 -0
- package/src/domain/executioncontext/controller.ts +15 -1
- package/src/domain/executioncontext/steps.ts +2 -1
- package/src/domain/mcpserver/__tests__/mcpserver.test.ts +115 -28
- package/src/domain/mcpserver/__tests__/oauth-handshake.test.ts +53 -18
- package/src/domain/mcpserver/connect.ts +11 -10
- package/src/domain/mcpserver/controller.ts +19 -13
- package/src/domain/mcpserver/start-connect.ts +17 -11
- package/src/domain/memory/__tests__/create-authorization.test.ts +59 -0
- package/src/domain/memory/controller.ts +11 -0
- package/src/domain/memory/steps.ts +41 -0
- package/src/domain/oauthapp/controller.ts +10 -0
- package/src/domain/plugin/controller.ts +23 -44
- package/src/domain/plugin/push.ts +8 -16
- package/src/domain/schedule/__tests__/create-authorization.test.ts +58 -0
- package/src/domain/schedule/controller.ts +26 -1
- package/src/domain/schedule/steps.ts +45 -2
- package/src/domain/session/controller.ts +12 -4
- package/src/domain/session/steps.ts +24 -32
- package/src/domain/skill/controller.ts +24 -30
- package/src/domain/workflow/__tests__/agent-call-references.test.ts +222 -0
- package/src/domain/workflow/agent-call-references.ts +157 -0
- package/src/domain/workflow/controller.ts +86 -155
- package/src/domain/workflow/registry/data/task-kind-registry.json +1 -1
- package/src/domain/workflow/validation/task-config-constraints.ts +7 -3
- package/src/domain/workflow/version-resolution.ts +166 -0
- package/src/domain/workflowexecution/controller.ts +7 -2
- package/src/domain/workflowinstance/__tests__/create-authorization.test.ts +109 -0
- package/src/domain/workflowinstance/controller.ts +25 -12
- package/src/domain/workflowinstance/steps.ts +49 -1
- package/src/extensions/__tests__/built-in-authorization-composed.test.ts +32 -19
- package/src/extensions/__tests__/composed-support.ts +38 -4
- package/src/extensions/__tests__/extension-composition.test.ts +4 -4
- package/src/extensions/__tests__/identity.test.ts +44 -1
- package/src/extensions/__tests__/list-read-scope-composed.test.ts +96 -7
- package/src/extensions/__tests__/list-read-scope.test.ts +63 -0
- package/src/extensions/authorization-queries.ts +6 -5
- package/src/extensions/identity.ts +23 -5
- package/src/extensions/list-read-scope.ts +48 -8
- package/src/extensions/resource-authorization.ts +3 -5
- package/src/index.ts +0 -1
- package/src/pipeline/__tests__/supports-visibility.test.ts +112 -0
- package/src/pipeline/apiresource-meta.ts +21 -10
- package/src/pipeline/steps/__tests__/authorization-tuples.test.ts +18 -31
- package/src/pipeline/steps/__tests__/authorize-resolved-target.test.ts +285 -0
- package/src/pipeline/steps/__tests__/references.test.ts +703 -0
- package/src/pipeline/steps/authorization-tuples.ts +9 -11
- package/src/pipeline/steps/authorize-resolved-target.ts +129 -0
- package/src/pipeline/steps/authorize.ts +70 -13
- package/src/pipeline/steps/guard-memory-capture.ts +2 -1
- package/src/pipeline/steps/guard-reserved-labels.ts +6 -9
- package/src/pipeline/steps/helpers.ts +11 -15
- package/src/pipeline/steps/references.ts +578 -70
- package/src/pipeline/steps/version-history.ts +34 -0
- package/src/query/search/__tests__/criteria.test.ts +0 -4
- package/src/query/search/__tests__/query-store.test.ts +11 -40
- package/src/query/search/controller.ts +0 -1
- package/src/query/search/criteria.ts +0 -14
- package/src/query/search/handler.ts +6 -7
- package/src/query/search/query-store.ts +0 -2
- package/src/store/__tests__/public-visibility-retired.test.ts +127 -0
- package/src/store/__tests__/store-contract.ts +6 -51
- package/src/store/interface.ts +3 -6
- package/src/store/postgres/__tests__/migrations.test.ts +187 -6
- package/src/store/postgres/__tests__/store-contract.test.ts +0 -2
- package/src/store/postgres/migrations.ts +48 -1
- package/src/store/postgres/store.ts +4 -11
- package/src/store/public-visibility-retired.ts +119 -0
- package/src/store/sqlite/__tests__/migrations.test.ts +191 -5
- package/src/store/sqlite/migrations.ts +45 -1
- package/src/store/sqlite/store.ts +4 -10
- package/dist/pipeline/steps/visibility-gates.d.ts +0 -27
- package/dist/pipeline/steps/visibility-gates.d.ts.map +0 -1
- package/dist/pipeline/steps/visibility-gates.js +0 -139
- package/dist/pipeline/steps/visibility-gates.js.map +0 -1
- package/src/authorization/__tests__/fixtures/fga/cross-org-share-creation.fga.yaml +0 -98
- package/src/authorization/__tests__/fixtures/fga/public-visibility-setter.fga.yaml +0 -52
- package/src/pipeline/steps/__tests__/visibility-gates.test.ts +0 -187
- package/src/pipeline/steps/visibility-gates.ts +0 -174
|
@@ -45,7 +45,6 @@ import {
|
|
|
45
45
|
ApiResourceKind,
|
|
46
46
|
ApiResourceKindSchema,
|
|
47
47
|
} from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
48
|
-
import { IamPermission } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
|
|
49
48
|
import type {
|
|
50
49
|
ApiResourceReference,
|
|
51
50
|
UpdateVisibilityInput,
|
|
@@ -71,14 +70,14 @@ import { callerIdentityOf } from "../../pipeline/interceptors/auth.js";
|
|
|
71
70
|
import { RequestContext } from "../../pipeline/request-context.js";
|
|
72
71
|
import {
|
|
73
72
|
authorizeDirect,
|
|
74
|
-
authorizeResolvedResource,
|
|
75
73
|
newAuthorizeStep,
|
|
76
74
|
} from "../../pipeline/steps/authorize.js";
|
|
77
|
-
import { newGuardReservedLabelsStep } from "../../pipeline/steps/guard-reserved-labels.js";
|
|
78
75
|
import {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
} from "../../pipeline/steps/
|
|
76
|
+
loadedTargetAsMethod,
|
|
77
|
+
newAuthorizeResolvedTargetStep,
|
|
78
|
+
} from "../../pipeline/steps/authorize-resolved-target.js";
|
|
79
|
+
import { versionHistoryTarget } from "../../pipeline/steps/version-history.js";
|
|
80
|
+
import { newGuardReservedLabelsStep } from "../../pipeline/steps/guard-reserved-labels.js";
|
|
82
81
|
import { newBuildUpdateStateStep } from "../../pipeline/steps/build-update-state.js";
|
|
83
82
|
import {
|
|
84
83
|
setAuditFieldsForUpdate,
|
|
@@ -90,10 +89,7 @@ import {
|
|
|
90
89
|
newExtractResourceIdStep,
|
|
91
90
|
newLoadExistingForDeleteStep,
|
|
92
91
|
} from "../../pipeline/steps/delete.js";
|
|
93
|
-
import {
|
|
94
|
-
findResourceBySlug,
|
|
95
|
-
requireOrgForReference,
|
|
96
|
-
} from "../../pipeline/steps/helpers.js";
|
|
92
|
+
import { findResourceBySlug } from "../../pipeline/steps/helpers.js";
|
|
97
93
|
import {
|
|
98
94
|
newDeleteSearchIndexStep,
|
|
99
95
|
newIndexSearchStep,
|
|
@@ -112,7 +108,16 @@ import {
|
|
|
112
108
|
TARGET_RESOURCE_KEY,
|
|
113
109
|
newLoadTargetStep,
|
|
114
110
|
} from "../../pipeline/steps/load-target.js";
|
|
115
|
-
import {
|
|
111
|
+
import {
|
|
112
|
+
collectSpecReferences,
|
|
113
|
+
newGuardReferenceFloorOnEscalationStep,
|
|
114
|
+
newNormalizeReferencesStep,
|
|
115
|
+
newValidateReferencesStep,
|
|
116
|
+
} from "../../pipeline/steps/references.js";
|
|
117
|
+
import {
|
|
118
|
+
collectAgentCallReferences,
|
|
119
|
+
newValidateAgentCallReferencesStep,
|
|
120
|
+
} from "./agent-call-references.js";
|
|
116
121
|
import {
|
|
117
122
|
newCleanupIamPoliciesStep,
|
|
118
123
|
newCreateAuthorizationTuplesStep,
|
|
@@ -150,6 +155,7 @@ import {
|
|
|
150
155
|
newValidateWorkflowSpecStep,
|
|
151
156
|
truncateHash,
|
|
152
157
|
} from "./steps.js";
|
|
158
|
+
import { newLoadWorkflowByReferenceStep } from "./version-resolution.js";
|
|
153
159
|
import type { WorkflowInstanceCreatorProvider } from "./steps.js";
|
|
154
160
|
|
|
155
161
|
export interface WorkflowControllerDeps {
|
|
@@ -192,7 +198,7 @@ export function registerWorkflowServices(
|
|
|
192
198
|
});
|
|
193
199
|
router.service(WorkflowQueryController, {
|
|
194
200
|
get: (id, ctx) => get(deps, id, ctx),
|
|
195
|
-
getByReference: (ref) => getByReference(deps, ref),
|
|
201
|
+
getByReference: (ref, ctx) => getByReference(deps, ref, ctx),
|
|
196
202
|
listVersions: (input, ctx) => listVersions(deps, input, ctx),
|
|
197
203
|
getVersion: (input, ctx) => getVersion(deps, input, callerIdentityOf(ctx)),
|
|
198
204
|
});
|
|
@@ -237,9 +243,10 @@ async function createWorkflow(
|
|
|
237
243
|
.addStep(newResolveSlugStep())
|
|
238
244
|
.addStep(newCheckDuplicateStep(deps.store))
|
|
239
245
|
.addStep(newBuildNewStateStep())
|
|
240
|
-
.addStep(newGuardPublicVisibilityStep(deps.authorizer))
|
|
241
246
|
.addStep(newGuardReservedLabelsStep(deps.authorizer))
|
|
242
247
|
.addStep(newNormalizeReferencesStep())
|
|
248
|
+
.addStep(newValidateReferencesStep(deps.store))
|
|
249
|
+
.addStep(newValidateAgentCallReferencesStep(deps.store))
|
|
243
250
|
.addStep(newPopulateServerlessValidationStep(deps.logger))
|
|
244
251
|
.addStep(newComputeVersionHashStep(deps.logger))
|
|
245
252
|
.addStep(newPopulateVersionHashStep(true))
|
|
@@ -304,6 +311,8 @@ async function update(
|
|
|
304
311
|
.addStep(newBuildUpdateStateStep())
|
|
305
312
|
.addStep(newGuardReservedLabelsStep(deps.authorizer))
|
|
306
313
|
.addStep(newNormalizeReferencesStep())
|
|
314
|
+
.addStep(newValidateReferencesStep(deps.store))
|
|
315
|
+
.addStep(newValidateAgentCallReferencesStep(deps.store))
|
|
307
316
|
.addStep(newPopulateServerlessValidationStepForUpdate(deps.logger))
|
|
308
317
|
.addStep(newComputeVersionHashStep(deps.logger))
|
|
309
318
|
.addStep(newCheckVersionChangedStep(deps.logger))
|
|
@@ -410,8 +419,8 @@ async function deleteWorkflow(
|
|
|
410
419
|
// updateVisibility — update_visibility.go: a targeted metadata update (only
|
|
411
420
|
// metadata.visibility changes; spec/status untouched). The level check runs
|
|
412
421
|
// AFTER load, preserving the cross-edition error precedence: unknown id +
|
|
413
|
-
// bad level = NOT_FOUND on both editions.
|
|
414
|
-
//
|
|
422
|
+
// bad level = NOT_FOUND on both editions. The workflow conformance suite's
|
|
423
|
+
// visibility block is the wire pin.
|
|
415
424
|
// ---------------------------------------------------------------------------
|
|
416
425
|
|
|
417
426
|
const UPDATE_VISIBILITY_WORKFLOW_KEY = "updateVisibilityWorkflow";
|
|
@@ -451,10 +460,20 @@ async function updateVisibility(
|
|
|
451
460
|
newRecordVisibilityBeforeUpdateStep(UPDATE_VISIBILITY_WORKFLOW_KEY),
|
|
452
461
|
)
|
|
453
462
|
.addStep(newValidateVisibilityUpdateStep())
|
|
463
|
+
// The reference floor's second door: a workflow may not be raised above
|
|
464
|
+
// the agents its agent_call tasks run.
|
|
454
465
|
.addStep(
|
|
455
|
-
|
|
466
|
+
newGuardReferenceFloorOnEscalationStep(
|
|
467
|
+
deps.store,
|
|
456
468
|
UPDATE_VISIBILITY_WORKFLOW_KEY,
|
|
457
|
-
|
|
469
|
+
[
|
|
470
|
+
(row) => collectSpecReferences(WorkflowSchema, row),
|
|
471
|
+
(row) =>
|
|
472
|
+
collectAgentCallReferences(
|
|
473
|
+
(row as Workflow).spec,
|
|
474
|
+
(row as Workflow).metadata?.org ?? "",
|
|
475
|
+
),
|
|
476
|
+
],
|
|
458
477
|
),
|
|
459
478
|
)
|
|
460
479
|
.addStep(newSetWorkflowVisibilityStep())
|
|
@@ -831,130 +850,47 @@ async function get(
|
|
|
831
850
|
return reqCtx.get(TARGET_RESOURCE_KEY) as Workflow;
|
|
832
851
|
}
|
|
833
852
|
|
|
834
|
-
/** A 64-hex value is an exact content hash; anything else is a tag. */
|
|
835
|
-
const WORKFLOW_HASH_PATTERN = /^[a-f0-9]{64}$/;
|
|
836
|
-
|
|
837
853
|
/**
|
|
838
|
-
* getByReference —
|
|
839
|
-
*
|
|
840
|
-
*
|
|
841
|
-
*
|
|
842
|
-
*
|
|
843
|
-
*
|
|
844
|
-
*
|
|
854
|
+
* getByReference — the slug/org read plus the version ladder, as a chain:
|
|
855
|
+
* Authorize (a no-op under the lane's skip annotation) → ValidateProto →
|
|
856
|
+
* LoadWorkflowByReference (the ladder, version-resolution.ts) →
|
|
857
|
+
* AuthorizeResolvedTarget, which asks of the loaded head's id exactly what
|
|
858
|
+
* `get` asks by id, so a slug reveals nothing a member could not read
|
|
859
|
+
* through the id. The archived snapshot shares the head's id, so the check
|
|
860
|
+
* on a versioned read is the check on the head.
|
|
845
861
|
*/
|
|
846
862
|
async function getByReference(
|
|
847
863
|
deps: WorkflowControllerDeps,
|
|
848
864
|
ref: ApiResourceReference,
|
|
865
|
+
ctx: HandlerContext,
|
|
849
866
|
): Promise<Workflow> {
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
// empty-org reference is under-specified.
|
|
856
|
-
requireOrgForReference(ApiResourceKind.workflow, ref.org);
|
|
857
|
-
|
|
858
|
-
if (
|
|
859
|
-
ref.kind !== ApiResourceKind.api_resource_kind_unknown &&
|
|
860
|
-
ref.kind !== ApiResourceKind.workflow
|
|
861
|
-
) {
|
|
862
|
-
// Go renders both sides with .String(): the NAME for defined values and
|
|
863
|
-
// the bare NUMBER for unknown ones (proto3 open enums preserve them, and
|
|
864
|
-
// ref.kind carries no defined_only rule) — enumToJson would throw on the
|
|
865
|
-
// unknown arm (panel finding).
|
|
866
|
-
const got = ApiResourceKind[ref.kind] ?? String(ref.kind);
|
|
867
|
-
throw invalidArgumentError(
|
|
868
|
-
`kind mismatch: expected ${ApiResourceKind[ApiResourceKind.workflow]}, got ${got}`,
|
|
869
|
-
);
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
let mainWorkflow: Workflow | undefined;
|
|
873
|
-
try {
|
|
874
|
-
mainWorkflow = await findResourceBySlug(
|
|
875
|
-
deps.store,
|
|
876
|
-
ApiResourceKind.workflow,
|
|
877
|
-
WorkflowSchema,
|
|
878
|
-
ref.slug,
|
|
879
|
-
ref.org,
|
|
880
|
-
);
|
|
881
|
-
} catch (error) {
|
|
882
|
-
// Go wraps the slug scan's store failure (query.go:110) — without this
|
|
883
|
-
// the raw storage error would cross the wire as Unknown (panel finding).
|
|
884
|
-
throw internalError(error, "failed to list workflows");
|
|
885
|
-
}
|
|
886
|
-
if (mainWorkflow === undefined) {
|
|
887
|
-
throw notFoundError("workflow", ref.slug);
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
const version = ref.version.trim();
|
|
891
|
-
if (version === "" || version === "latest") {
|
|
892
|
-
return mainWorkflow;
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
if (workflowMatchesVersion(mainWorkflow, version)) {
|
|
896
|
-
return mainWorkflow;
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
const archived = await findAuditWorkflowByVersion(
|
|
900
|
-
deps.store,
|
|
901
|
-
mainWorkflow.metadata!.id,
|
|
902
|
-
version,
|
|
867
|
+
const reqCtx = new RequestContext(
|
|
868
|
+
WorkflowQueryController.method.getByReference.input,
|
|
869
|
+
ref,
|
|
870
|
+
callerIdentityOf(ctx),
|
|
871
|
+
kindOf(ctx),
|
|
903
872
|
);
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
)
|
|
925
|
-
let rec: AuditRecord;
|
|
926
|
-
try {
|
|
927
|
-
rec = WORKFLOW_HASH_PATTERN.test(version)
|
|
928
|
-
? await store.getAuditRecordByHash(
|
|
929
|
-
ApiResourceKind.workflow,
|
|
930
|
-
workflowId,
|
|
931
|
-
version,
|
|
932
|
-
)
|
|
933
|
-
: await store.getAuditRecordByTag(
|
|
934
|
-
ApiResourceKind.workflow,
|
|
935
|
-
workflowId,
|
|
936
|
-
version,
|
|
937
|
-
);
|
|
938
|
-
} catch (error) {
|
|
939
|
-
if (error instanceof AuditNotFoundError) {
|
|
940
|
-
return undefined;
|
|
941
|
-
}
|
|
942
|
-
throw internalError(error, "failed to query workflow audit by version");
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
let wf: Workflow;
|
|
946
|
-
try {
|
|
947
|
-
wf = fromBinary(WorkflowSchema, rec.data);
|
|
948
|
-
} catch (error) {
|
|
949
|
-
throw internalError(error, "failed to decode archived workflow version");
|
|
950
|
-
}
|
|
951
|
-
// Overlay the authoritative tag (audit column) onto the snapshot's
|
|
952
|
-
// metadata.version.tag — the snapshot's embedded tag is only correct as
|
|
953
|
-
// of archival time.
|
|
954
|
-
wf.metadata ??= create(ApiResourceMetadataSchema);
|
|
955
|
-
wf.metadata.version ??= create(ApiResourceMetadataVersionSchema);
|
|
956
|
-
wf.metadata.version.tag = rec.tag;
|
|
957
|
-
return wf;
|
|
873
|
+
await newPipeline<typeof WorkflowQueryController.method.getByReference.input>(
|
|
874
|
+
"workflow-get-by-reference",
|
|
875
|
+
deps.logger,
|
|
876
|
+
)
|
|
877
|
+
.addStep(
|
|
878
|
+
newAuthorizeStep(
|
|
879
|
+
WorkflowQueryController.method.getByReference,
|
|
880
|
+
deps.authorizer,
|
|
881
|
+
),
|
|
882
|
+
)
|
|
883
|
+
.addStep(newValidateProtoStep())
|
|
884
|
+
.addStep(newLoadWorkflowByReferenceStep(deps.store))
|
|
885
|
+
.addStep(
|
|
886
|
+
newAuthorizeResolvedTargetStep(
|
|
887
|
+
deps.authorizer,
|
|
888
|
+
loadedTargetAsMethod(WorkflowQueryController.method.get),
|
|
889
|
+
),
|
|
890
|
+
)
|
|
891
|
+
.build()
|
|
892
|
+
.execute(reqCtx);
|
|
893
|
+
return reqCtx.get(TARGET_RESOURCE_KEY) as Workflow;
|
|
958
894
|
}
|
|
959
895
|
|
|
960
896
|
// ---------------------------------------------------------------------------
|
|
@@ -996,28 +932,23 @@ async function listVersions(
|
|
|
996
932
|
)
|
|
997
933
|
.addStep(newValidateProtoStep())
|
|
998
934
|
.addStep(newResolveWorkflowBySlugStep(deps.store))
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
deps.authorizer,
|
|
1011
|
-
stepCtx.callerIdentity,
|
|
1012
|
-
{
|
|
1013
|
-
permission: IamPermission.can_view,
|
|
1014
|
-
resourceKind: ApiResourceKind.workflow,
|
|
1015
|
-
resourceId: stepCtx.get(LIST_VERSIONS_WORKFLOW_ID_KEY) as string,
|
|
1016
|
-
},
|
|
935
|
+
// The Java WorkflowListVersionsHandler's mid-chain can_view on the
|
|
936
|
+
// RESOLVED workflow id (the skip annotation exists because the target
|
|
937
|
+
// resolves from org+slug, not a request field); the copy is the Java
|
|
938
|
+
// handler's byte-pinned error_msg, and the resolve step above already
|
|
939
|
+
// answered NOT_FOUND for an unknown slug, the load-first order both
|
|
940
|
+
// editions share.
|
|
941
|
+
.addStep(
|
|
942
|
+
newAuthorizeResolvedTargetStep(
|
|
943
|
+
deps.authorizer,
|
|
944
|
+
versionHistoryTarget(
|
|
945
|
+
ApiResourceKind.workflow,
|
|
1017
946
|
"unauthorized to view workflow version history",
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
947
|
+
LIST_VERSIONS_WORKFLOW_ID_KEY,
|
|
948
|
+
),
|
|
949
|
+
"AuthorizeResolvedWorkflow",
|
|
950
|
+
),
|
|
951
|
+
)
|
|
1021
952
|
.addStep(newLoadAndMapWorkflowVersionsStep(deps.store))
|
|
1022
953
|
.build()
|
|
1023
954
|
.execute(reqCtx);
|
|
@@ -1525,7 +1525,7 @@
|
|
|
1525
1525
|
"type": "string"
|
|
1526
1526
|
},
|
|
1527
1527
|
"org": {
|
|
1528
|
-
"description": "Organization that owns the referenced resource. When non-empty: must be a valid org slug (lowercase alphanumeric with hyphens, starts with a letter, 1-63 characters). Example: \"stigmer\", \"acme-corp\". When empty: the reference is relative — the server resolves it to the parent resource's organization at write time. All stored and returned references always have org populated (absolute form). Use empty org for same-org references (the common case).
|
|
1528
|
+
"description": "Organization that owns the referenced resource. When non-empty: must be a valid org slug (lowercase alphanumeric with hyphens, starts with a letter, 1-63 characters). Example: \"stigmer\", \"acme-corp\". When empty: the reference is relative — the server resolves it to the parent resource's organization at write time. All stored and returned references always have org populated (absolute form). Use empty org for same-org references (the common case). An explicit other org is accepted only when that organization is a platform that shares the resource with yours (visibility_platform).",
|
|
1529
1529
|
"maxLength": 63,
|
|
1530
1530
|
"pattern": "^$|^[a-z][a-z0-9-]*[a-z0-9]$",
|
|
1531
1531
|
"type": "string"
|
|
@@ -105,7 +105,10 @@ function taskConstraintViolations(task: WorkflowTask): string[] {
|
|
|
105
105
|
* task_config is a Struct again — that is what the walker's recursion is
|
|
106
106
|
* for).
|
|
107
107
|
*/
|
|
108
|
-
function configConstraintViolations(
|
|
108
|
+
function configConstraintViolations(
|
|
109
|
+
task: WorkflowTask,
|
|
110
|
+
cfg: Message,
|
|
111
|
+
): string[] {
|
|
109
112
|
const schema = taskConfigSchemaFor(task.kind);
|
|
110
113
|
if (schema === undefined) {
|
|
111
114
|
// Unknown kinds were already rejected by validateTaskKinds; an
|
|
@@ -129,9 +132,10 @@ function configConstraintViolations(task: WorkflowTask, cfg: Message): string[]
|
|
|
129
132
|
* The WorkflowTask lists embedded in a control-flow task config, in
|
|
130
133
|
* declaration order — the same recursion set the converter walks
|
|
131
134
|
* (convertForTask/convertForkTask/convertTryTask) and the cloud Java
|
|
132
|
-
* TaskConfigStrictParser mirrors.
|
|
135
|
+
* TaskConfigStrictParser mirrors. Exported for the write-time reference
|
|
136
|
+
* collector (../agent-call-references.ts), which walks the same tree.
|
|
133
137
|
*/
|
|
134
|
-
function nestedTasks(cfg: Message): WorkflowTask[] {
|
|
138
|
+
export function nestedTasks(cfg: Message): WorkflowTask[] {
|
|
135
139
|
switch (cfg.$typeName) {
|
|
136
140
|
case ForTaskConfigSchema.typeName:
|
|
137
141
|
return (cfg as ForTaskConfig).do;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow version resolution — the getByReference ladder as a pipeline
|
|
3
|
+
* step, so the lane can carry the resolved-target authorization the
|
|
4
|
+
* position-1 annotation cannot express (the target is a slug, not a field).
|
|
5
|
+
*
|
|
6
|
+
* The ladder is the Go query.go read, moved here from the direct handler
|
|
7
|
+
* without a behaviour change: slug required; org required (a workflow's slug
|
|
8
|
+
* is unique only within an org); kind mismatch refused with Go's rendering
|
|
9
|
+
* (the NAME for defined values, the bare NUMBER for unknown ones, because
|
|
10
|
+
* ref.kind carries no defined_only rule and enumToJson would throw on the
|
|
11
|
+
* unknown arm); the slug scan's store failure wrapped so a raw storage error
|
|
12
|
+
* never crosses the wire as Unknown; empty or "latest" is the current head;
|
|
13
|
+
* a version matching the head's hash or tag is the head; otherwise the
|
|
14
|
+
* audit store by hash or tag, with the snapshot's tag overlaid from the
|
|
15
|
+
* audit column (the source of truth) so callers never see a stale embedded
|
|
16
|
+
* tag after a tag move. The resolved row lands under TARGET_RESOURCE_KEY,
|
|
17
|
+
* the loader contract every getByReference chain shares.
|
|
18
|
+
*
|
|
19
|
+
* Deliberately NOT the shared ladder in pipeline/steps/version-history.ts:
|
|
20
|
+
* that one decodes through the typed audit reads and carries no tag
|
|
21
|
+
* overlay, so folding the two would change what one of them returns for a
|
|
22
|
+
* moved tag. Consolidating them is a separate change with its own proof.
|
|
23
|
+
*
|
|
24
|
+
* Proven by __tests__/workflow.test.ts's getByReference block and the
|
|
25
|
+
* conformance suite's workflow reference arms.
|
|
26
|
+
*/
|
|
27
|
+
import { create, fromBinary } from "@bufbuild/protobuf";
|
|
28
|
+
|
|
29
|
+
import { WorkflowSchema } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/api_pb";
|
|
30
|
+
import type { Workflow } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/api_pb";
|
|
31
|
+
import {
|
|
32
|
+
ApiResourceMetadataSchema,
|
|
33
|
+
ApiResourceMetadataVersionSchema,
|
|
34
|
+
} from "@stigmer/protos/ai/stigmer/commons/apiresource/metadata_pb";
|
|
35
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
36
|
+
|
|
37
|
+
import {
|
|
38
|
+
internalError,
|
|
39
|
+
invalidArgumentError,
|
|
40
|
+
notFoundError,
|
|
41
|
+
} from "../../pipeline/errors.js";
|
|
42
|
+
import type { PipelineStep } from "../../pipeline/pipeline.js";
|
|
43
|
+
import type { RequestContext } from "../../pipeline/request-context.js";
|
|
44
|
+
import type { GetByReferenceDesc } from "../../pipeline/steps/authorize-resolved-target.js";
|
|
45
|
+
import {
|
|
46
|
+
findResourceBySlug,
|
|
47
|
+
requireOrgForReference,
|
|
48
|
+
} from "../../pipeline/steps/helpers.js";
|
|
49
|
+
import { TARGET_RESOURCE_KEY } from "../../pipeline/steps/load-target.js";
|
|
50
|
+
import { AuditNotFoundError } from "../../store/interface.js";
|
|
51
|
+
import type { AuditRecord, Store } from "../../store/interface.js";
|
|
52
|
+
|
|
53
|
+
/** A 64-hex value is an exact content hash; anything else is a tag. */
|
|
54
|
+
const WORKFLOW_HASH_PATTERN = /^[a-f0-9]{64}$/;
|
|
55
|
+
|
|
56
|
+
export function newLoadWorkflowByReferenceStep(
|
|
57
|
+
store: Store,
|
|
58
|
+
): PipelineStep<GetByReferenceDesc> {
|
|
59
|
+
return {
|
|
60
|
+
name: "LoadWorkflowByReference",
|
|
61
|
+
async execute(ctx: RequestContext<GetByReferenceDesc>): Promise<void> {
|
|
62
|
+
const ref = ctx.input;
|
|
63
|
+
if (ref.slug === "") {
|
|
64
|
+
throw invalidArgumentError("slug is required in reference");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
requireOrgForReference(ApiResourceKind.workflow, ref.org);
|
|
68
|
+
|
|
69
|
+
if (
|
|
70
|
+
ref.kind !== ApiResourceKind.api_resource_kind_unknown &&
|
|
71
|
+
ref.kind !== ApiResourceKind.workflow
|
|
72
|
+
) {
|
|
73
|
+
const got = ApiResourceKind[ref.kind] ?? String(ref.kind);
|
|
74
|
+
throw invalidArgumentError(
|
|
75
|
+
`kind mismatch: expected ${ApiResourceKind[ApiResourceKind.workflow]}, got ${got}`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let mainWorkflow: Workflow | undefined;
|
|
80
|
+
try {
|
|
81
|
+
mainWorkflow = await findResourceBySlug(
|
|
82
|
+
store,
|
|
83
|
+
ApiResourceKind.workflow,
|
|
84
|
+
WorkflowSchema,
|
|
85
|
+
ref.slug,
|
|
86
|
+
ref.org,
|
|
87
|
+
);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
throw internalError(error, "failed to list workflows");
|
|
90
|
+
}
|
|
91
|
+
if (mainWorkflow === undefined) {
|
|
92
|
+
throw notFoundError("workflow", ref.slug);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const version = ref.version.trim();
|
|
96
|
+
if (
|
|
97
|
+
version === "" ||
|
|
98
|
+
version === "latest" ||
|
|
99
|
+
workflowMatchesVersion(mainWorkflow, version)
|
|
100
|
+
) {
|
|
101
|
+
ctx.set(TARGET_RESOURCE_KEY, mainWorkflow);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const archived = await findAuditWorkflowByVersion(
|
|
106
|
+
store,
|
|
107
|
+
mainWorkflow.metadata!.id,
|
|
108
|
+
version,
|
|
109
|
+
);
|
|
110
|
+
if (archived === undefined) {
|
|
111
|
+
throw notFoundError("workflow version", `${ref.slug}:${version}`);
|
|
112
|
+
}
|
|
113
|
+
ctx.set(TARGET_RESOURCE_KEY, archived);
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function workflowMatchesVersion(wf: Workflow, version: string): boolean {
|
|
119
|
+
if (wf.status === undefined) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
if (WORKFLOW_HASH_PATTERN.test(version)) {
|
|
123
|
+
return wf.status.versionHash === version;
|
|
124
|
+
}
|
|
125
|
+
return wf.metadata?.version?.tag === version;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function findAuditWorkflowByVersion(
|
|
129
|
+
store: Store,
|
|
130
|
+
workflowId: string,
|
|
131
|
+
version: string,
|
|
132
|
+
): Promise<Workflow | undefined> {
|
|
133
|
+
let rec: AuditRecord;
|
|
134
|
+
try {
|
|
135
|
+
rec = WORKFLOW_HASH_PATTERN.test(version)
|
|
136
|
+
? await store.getAuditRecordByHash(
|
|
137
|
+
ApiResourceKind.workflow,
|
|
138
|
+
workflowId,
|
|
139
|
+
version,
|
|
140
|
+
)
|
|
141
|
+
: await store.getAuditRecordByTag(
|
|
142
|
+
ApiResourceKind.workflow,
|
|
143
|
+
workflowId,
|
|
144
|
+
version,
|
|
145
|
+
);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
if (error instanceof AuditNotFoundError) {
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
throw internalError(error, "failed to query workflow audit by version");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
let wf: Workflow;
|
|
154
|
+
try {
|
|
155
|
+
wf = fromBinary(WorkflowSchema, rec.data);
|
|
156
|
+
} catch (error) {
|
|
157
|
+
throw internalError(error, "failed to decode archived workflow version");
|
|
158
|
+
}
|
|
159
|
+
// Overlay the authoritative tag (audit column) onto the snapshot's
|
|
160
|
+
// metadata.version.tag — the snapshot's embedded tag is only correct as
|
|
161
|
+
// of archival time.
|
|
162
|
+
wf.metadata ??= create(ApiResourceMetadataSchema);
|
|
163
|
+
wf.metadata.version ??= create(ApiResourceMetadataVersionSchema);
|
|
164
|
+
wf.metadata.version.tag = rec.tag;
|
|
165
|
+
return wf;
|
|
166
|
+
}
|
|
@@ -72,7 +72,10 @@ import {
|
|
|
72
72
|
TARGET_RESOURCE_KEY,
|
|
73
73
|
newLoadTargetStep,
|
|
74
74
|
} from "../../pipeline/steps/load-target.js";
|
|
75
|
-
import {
|
|
75
|
+
import {
|
|
76
|
+
newNormalizeReferencesStep,
|
|
77
|
+
newValidateReferencesStep,
|
|
78
|
+
} from "../../pipeline/steps/references.js";
|
|
76
79
|
import {
|
|
77
80
|
newCleanupIamPoliciesStep,
|
|
78
81
|
newCreateAuthorizationTuplesStep,
|
|
@@ -299,7 +302,8 @@ async function createExecution(
|
|
|
299
302
|
.addStep(newCheckDuplicateStep(deps.store))
|
|
300
303
|
.addStep(newBuildNewStateStep())
|
|
301
304
|
.addStep(newGuardReservedLabelsStep(deps.authorizer))
|
|
302
|
-
.addStep(newNormalizeReferencesStep())
|
|
305
|
+
.addStep(newNormalizeReferencesStep())
|
|
306
|
+
.addStep(newValidateReferencesStep(deps.store));
|
|
303
307
|
// The ratified sandbox-acquisition gate slot (blueprint 03 §3a; C4):
|
|
304
308
|
// the Java-verified capacity-gate position — after Authorize and every
|
|
305
309
|
// resolution step (the default instance, like Java's, side-effects
|
|
@@ -389,6 +393,7 @@ async function update(
|
|
|
389
393
|
.addStep(newBuildUpdateStateStep())
|
|
390
394
|
.addStep(newGuardReservedLabelsStep(deps.authorizer))
|
|
391
395
|
.addStep(newNormalizeReferencesStep())
|
|
396
|
+
.addStep(newValidateReferencesStep(deps.store))
|
|
392
397
|
.addStep(newPersistStep(deps.store))
|
|
393
398
|
.addStep(
|
|
394
399
|
newIndexSearchStep(
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pins the create lane's authorization question (steps.ts,
|
|
3
|
+
* resolveWorkflowInstanceCreateTargets): a personal instance asks
|
|
4
|
+
* can_execute on the parent workflow with the Java handler's copy; a
|
|
5
|
+
* default instance the server composed in-process for the parent's
|
|
6
|
+
* organization asks nothing; the same label from the wire or for another
|
|
7
|
+
* organization takes the bar; a missing parent throws.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, expect, it } from "vitest";
|
|
10
|
+
import { create } from "@bufbuild/protobuf";
|
|
11
|
+
|
|
12
|
+
import { WorkflowSchema } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/api_pb";
|
|
13
|
+
import { WorkflowInstanceSchema } from "@stigmer/protos/ai/stigmer/agentic/workflowinstance/v1/api_pb";
|
|
14
|
+
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
15
|
+
import { IamPermission } from "@stigmer/protos/ai/stigmer/iam/v1/enum_pb";
|
|
16
|
+
|
|
17
|
+
import type { CallerIdentity } from "../../../extensions/identity.js";
|
|
18
|
+
import {
|
|
19
|
+
DEFAULT_INSTANCE_LABEL,
|
|
20
|
+
RESERVED_LABEL_TRUE,
|
|
21
|
+
} from "../../../pipeline/apiresource-labels.js";
|
|
22
|
+
import { testCallerIdentity } from "../../../pipeline/__tests__/support.js";
|
|
23
|
+
import { RequestContext } from "../../../pipeline/request-context.js";
|
|
24
|
+
import {
|
|
25
|
+
PARENT_WORKFLOW_KEY,
|
|
26
|
+
WORKFLOW_INSTANCE_PARENT_DENIED_MESSAGE,
|
|
27
|
+
resolveWorkflowInstanceCreateTargets,
|
|
28
|
+
} from "../steps.js";
|
|
29
|
+
|
|
30
|
+
const parent = create(WorkflowSchema, {
|
|
31
|
+
metadata: { id: "wf_01parent", name: "nightly", org: "acme" },
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
function ctxFor(
|
|
35
|
+
org: string,
|
|
36
|
+
caller: CallerIdentity,
|
|
37
|
+
labels: Record<string, string> = {},
|
|
38
|
+
stashParent = true,
|
|
39
|
+
) {
|
|
40
|
+
const ctx = new RequestContext(
|
|
41
|
+
WorkflowInstanceSchema,
|
|
42
|
+
create(WorkflowInstanceSchema, {
|
|
43
|
+
metadata: { name: "mine", org, labels },
|
|
44
|
+
spec: { workflowId: parent.metadata!.id },
|
|
45
|
+
}),
|
|
46
|
+
caller,
|
|
47
|
+
ApiResourceKind.workflow_instance,
|
|
48
|
+
);
|
|
49
|
+
if (stashParent) {
|
|
50
|
+
ctx.set(PARENT_WORKFLOW_KEY, parent);
|
|
51
|
+
}
|
|
52
|
+
return ctx;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const DEFAULT_LABELS = { [DEFAULT_INSTANCE_LABEL]: RESERVED_LABEL_TRUE };
|
|
56
|
+
|
|
57
|
+
describe("resolveWorkflowInstanceCreateTargets", () => {
|
|
58
|
+
it("a personal instance asks can_execute on the parent workflow", () => {
|
|
59
|
+
expect(
|
|
60
|
+
resolveWorkflowInstanceCreateTargets(
|
|
61
|
+
ctxFor("acme", testCallerIdentity()),
|
|
62
|
+
),
|
|
63
|
+
).toEqual([
|
|
64
|
+
{
|
|
65
|
+
permission: IamPermission.can_execute,
|
|
66
|
+
resourceKind: ApiResourceKind.workflow,
|
|
67
|
+
resourceId: "wf_01parent",
|
|
68
|
+
deniedMessage: WORKFLOW_INSTANCE_PARENT_DENIED_MESSAGE,
|
|
69
|
+
},
|
|
70
|
+
]);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("a default instance the server composed in-process for the parent's organization asks nothing", () => {
|
|
74
|
+
expect(
|
|
75
|
+
resolveWorkflowInstanceCreateTargets(
|
|
76
|
+
ctxFor(
|
|
77
|
+
"acme",
|
|
78
|
+
testCallerIdentity({ callerClass: "user", origin: "in-process" }),
|
|
79
|
+
DEFAULT_LABELS,
|
|
80
|
+
),
|
|
81
|
+
),
|
|
82
|
+
).toEqual([]);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("the label from the wire, or for another organization, takes the bar", () => {
|
|
86
|
+
expect(
|
|
87
|
+
resolveWorkflowInstanceCreateTargets(
|
|
88
|
+
ctxFor("acme", testCallerIdentity(), DEFAULT_LABELS),
|
|
89
|
+
),
|
|
90
|
+
).toHaveLength(1);
|
|
91
|
+
expect(
|
|
92
|
+
resolveWorkflowInstanceCreateTargets(
|
|
93
|
+
ctxFor(
|
|
94
|
+
"other-org",
|
|
95
|
+
testCallerIdentity({ callerClass: "user", origin: "in-process" }),
|
|
96
|
+
DEFAULT_LABELS,
|
|
97
|
+
),
|
|
98
|
+
),
|
|
99
|
+
).toHaveLength(1);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("a missing parent is a broken chain invariant and throws", () => {
|
|
103
|
+
expect(() =>
|
|
104
|
+
resolveWorkflowInstanceCreateTargets(
|
|
105
|
+
ctxFor("acme", testCallerIdentity(), {}, false),
|
|
106
|
+
),
|
|
107
|
+
).toThrow("parent workflow not found in context");
|
|
108
|
+
});
|
|
109
|
+
});
|