@stigmer/server 3.23.1-dev.20260923075205 → 3.25.0
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 +10 -4
- package/dist/authorization/authorizer.d.ts.map +1 -1
- package/dist/authorization/authorizer.js +11 -4
- package/dist/authorization/authorizer.js.map +1 -1
- package/dist/authorization/model/index.d.ts +4 -4
- package/dist/authorization/model/index.d.ts.map +1 -1
- package/dist/authorization/model/index.js +2 -0
- package/dist/authorization/model/index.js.map +1 -1
- package/dist/authorization/model/oauth_app.d.ts.map +1 -1
- package/dist/authorization/model/oauth_app.js +3 -2
- package/dist/authorization/model/oauth_app.js.map +1 -1
- package/dist/authorization/model/platform_client.d.ts +2 -0
- package/dist/authorization/model/platform_client.d.ts.map +1 -0
- package/dist/authorization/model/platform_client.js +31 -0
- package/dist/authorization/model/platform_client.js.map +1 -0
- package/dist/boot/compose.d.ts.map +1 -1
- package/dist/boot/compose.js +88 -5
- package/dist/boot/compose.js.map +1 -1
- package/dist/boot/route-shadowing.d.ts +25 -0
- package/dist/boot/route-shadowing.d.ts.map +1 -0
- package/dist/boot/route-shadowing.js +17 -0
- package/dist/boot/route-shadowing.js.map +1 -0
- package/dist/domain/iampolicy/membership.d.ts +6 -1
- package/dist/domain/iampolicy/membership.d.ts.map +1 -1
- package/dist/domain/iampolicy/membership.js +8 -2
- package/dist/domain/iampolicy/membership.js.map +1 -1
- package/dist/domain/identityaccount/constants.d.ts +13 -0
- package/dist/domain/identityaccount/constants.d.ts.map +1 -1
- package/dist/domain/identityaccount/constants.js +17 -0
- package/dist/domain/identityaccount/constants.js.map +1 -1
- package/dist/domain/identityaccount/controller.d.ts +3 -1
- package/dist/domain/identityaccount/controller.d.ts.map +1 -1
- package/dist/domain/identityaccount/controller.js +15 -6
- package/dist/domain/identityaccount/controller.js.map +1 -1
- package/dist/domain/identityaccount/operator.d.ts.map +1 -1
- package/dist/domain/identityaccount/operator.js +1 -0
- package/dist/domain/identityaccount/operator.js.map +1 -1
- package/dist/domain/identityaccount/provisioning.d.ts +18 -0
- package/dist/domain/identityaccount/provisioning.d.ts.map +1 -1
- package/dist/domain/identityaccount/provisioning.js +1 -0
- package/dist/domain/identityaccount/provisioning.js.map +1 -1
- package/dist/domain/identityaccount/resource-store.js +9 -5
- package/dist/domain/identityaccount/resource-store.js.map +1 -1
- package/dist/domain/identityaccount/steps.d.ts +11 -2
- package/dist/domain/identityaccount/steps.d.ts.map +1 -1
- package/dist/domain/identityaccount/steps.js +42 -4
- package/dist/domain/identityaccount/steps.js.map +1 -1
- package/dist/domain/identityaccount/store-contract.d.ts.map +1 -1
- package/dist/domain/identityaccount/store-contract.js +33 -0
- package/dist/domain/identityaccount/store-contract.js.map +1 -1
- package/dist/domain/identityaccount/store.d.ts +7 -5
- package/dist/domain/identityaccount/store.d.ts.map +1 -1
- package/dist/domain/identityaccount/store.js.map +1 -1
- package/dist/domain/platform/controller.d.ts +10 -0
- package/dist/domain/platform/controller.d.ts.map +1 -1
- package/dist/domain/platform/controller.js +2 -1
- package/dist/domain/platform/controller.js.map +1 -1
- package/dist/domain/platformclient/constants.d.ts +60 -0
- package/dist/domain/platformclient/constants.d.ts.map +1 -0
- package/dist/domain/platformclient/constants.js +78 -0
- package/dist/domain/platformclient/constants.js.map +1 -0
- package/dist/domain/platformclient/controller.d.ts +59 -0
- package/dist/domain/platformclient/controller.d.ts.map +1 -0
- package/dist/domain/platformclient/controller.js +191 -0
- package/dist/domain/platformclient/controller.js.map +1 -0
- package/dist/domain/platformclient/credentials.d.ts +13 -0
- package/dist/domain/platformclient/credentials.d.ts.map +1 -0
- package/dist/domain/platformclient/credentials.js +56 -0
- package/dist/domain/platformclient/credentials.js.map +1 -0
- package/dist/domain/platformclient/mint.d.ts +23 -0
- package/dist/domain/platformclient/mint.d.ts.map +1 -0
- package/dist/domain/platformclient/mint.js +254 -0
- package/dist/domain/platformclient/mint.js.map +1 -0
- package/dist/domain/platformclient/origin-guard.d.ts +9 -0
- package/dist/domain/platformclient/origin-guard.d.ts.map +1 -0
- package/dist/domain/platformclient/origin-guard.js +81 -0
- package/dist/domain/platformclient/origin-guard.js.map +1 -0
- package/dist/domain/platformclient/resource-store.d.ts +4 -0
- package/dist/domain/platformclient/resource-store.d.ts.map +1 -0
- package/dist/domain/platformclient/resource-store.js +117 -0
- package/dist/domain/platformclient/resource-store.js.map +1 -0
- package/dist/domain/platformclient/steps.d.ts +51 -0
- package/dist/domain/platformclient/steps.d.ts.map +1 -0
- package/dist/domain/platformclient/steps.js +339 -0
- package/dist/domain/platformclient/steps.js.map +1 -0
- package/dist/domain/platformclient/store-contract.d.ts +8 -0
- package/dist/domain/platformclient/store-contract.d.ts.map +1 -0
- package/dist/domain/platformclient/store-contract.js +208 -0
- package/dist/domain/platformclient/store-contract.js.map +1 -0
- package/dist/domain/platformclient/store.d.ts +58 -0
- package/dist/domain/platformclient/store.d.ts.map +1 -0
- package/dist/domain/platformclient/store.js +13 -0
- package/dist/domain/platformclient/store.js.map +1 -0
- package/dist/domain/platformclient/system-managed.d.ts +18 -0
- package/dist/domain/platformclient/system-managed.d.ts.map +1 -0
- package/dist/domain/platformclient/system-managed.js +80 -0
- package/dist/domain/platformclient/system-managed.js.map +1 -0
- package/dist/domain/platformclient/token-controller.d.ts +11 -0
- package/dist/domain/platformclient/token-controller.d.ts.map +1 -0
- package/dist/domain/platformclient/token-controller.js +32 -0
- package/dist/domain/platformclient/token-controller.js.map +1 -0
- package/dist/domain/platformclient/verifier.d.ts +10 -0
- package/dist/domain/platformclient/verifier.d.ts.map +1 -0
- package/dist/domain/platformclient/verifier.js +76 -0
- package/dist/domain/platformclient/verifier.js.map +1 -0
- package/dist/domain/workflow/agent-call-references.d.ts +60 -33
- package/dist/domain/workflow/agent-call-references.d.ts.map +1 -1
- package/dist/domain/workflow/agent-call-references.js +43 -23
- package/dist/domain/workflow/agent-call-references.js.map +1 -1
- package/dist/domain/workflow/registry/data/task-kind-registry.json +2 -2
- package/dist/encryption/key-manager.d.ts +20 -3
- package/dist/encryption/key-manager.d.ts.map +1 -1
- package/dist/encryption/key-manager.js +64 -42
- package/dist/encryption/key-manager.js.map +1 -1
- package/dist/extensions/caller-guards.d.ts +8 -3
- package/dist/extensions/caller-guards.d.ts.map +1 -1
- package/dist/extensions/drivers.d.ts +37 -0
- package/dist/extensions/drivers.d.ts.map +1 -1
- package/dist/extensions/guest-token-minting.d.ts +26 -0
- package/dist/extensions/guest-token-minting.d.ts.map +1 -0
- package/dist/extensions/guest-token-minting.js +2 -0
- package/dist/extensions/guest-token-minting.js.map +1 -0
- package/dist/extensions/identity.d.ts +6 -4
- package/dist/extensions/identity.d.ts.map +1 -1
- package/dist/extensions/identity.js.map +1 -1
- package/dist/extensions/registry.d.ts +30 -1
- package/dist/extensions/registry.d.ts.map +1 -1
- package/dist/extensions/registry.js +34 -1
- package/dist/extensions/registry.js.map +1 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/pipeline/apiresource-labels.d.ts +10 -9
- package/dist/pipeline/apiresource-labels.d.ts.map +1 -1
- package/dist/pipeline/apiresource-labels.js +10 -9
- package/dist/pipeline/apiresource-labels.js.map +1 -1
- package/dist/pipeline/interceptors/auth.d.ts +14 -0
- package/dist/pipeline/interceptors/auth.d.ts.map +1 -1
- package/dist/pipeline/interceptors/auth.js +24 -0
- package/dist/pipeline/interceptors/auth.js.map +1 -1
- package/dist/pipeline/steps/build-update-state.d.ts +11 -1
- package/dist/pipeline/steps/build-update-state.d.ts.map +1 -1
- package/dist/pipeline/steps/build-update-state.js +5 -2
- package/dist/pipeline/steps/build-update-state.js.map +1 -1
- package/dist/pipeline/steps/references.d.ts +30 -4
- package/dist/pipeline/steps/references.d.ts.map +1 -1
- package/dist/pipeline/steps/references.js +13 -1
- package/dist/pipeline/steps/references.js.map +1 -1
- package/dist/platformtoken/envelope.d.ts +100 -0
- package/dist/platformtoken/envelope.d.ts.map +1 -0
- package/dist/platformtoken/envelope.js +184 -0
- package/dist/platformtoken/envelope.js.map +1 -0
- package/dist/platformtoken/key-ring.d.ts +85 -0
- package/dist/platformtoken/key-ring.d.ts.map +1 -0
- package/dist/platformtoken/key-ring.js +164 -0
- package/dist/platformtoken/key-ring.js.map +1 -0
- package/package.json +6 -6
- package/src/authorization/README.md +1 -1
- package/src/authorization/__tests__/authorizer.test.ts +6 -2
- package/src/authorization/__tests__/wire-permissions.test.ts +1 -0
- package/src/authorization/authorizer.ts +11 -4
- package/src/authorization/model/__tests__/registry.test.ts +8 -1
- package/src/authorization/model/index.ts +6 -4
- package/src/authorization/model/oauth_app.ts +3 -2
- package/src/authorization/model/platform_client.ts +43 -0
- package/src/boot/__tests__/auth-enabled-composition.test.ts +2 -1
- package/src/boot/__tests__/route-shadowing.test.ts +67 -0
- package/src/boot/compose.ts +90 -5
- package/src/boot/route-shadowing.ts +47 -0
- package/src/domain/iampolicy/__tests__/membership.test.ts +7 -0
- package/src/domain/iampolicy/membership.ts +8 -2
- package/src/domain/identityaccount/__tests__/platform-client-accounts.test.ts +124 -0
- package/src/domain/identityaccount/__tests__/resource-store.test.ts +1 -0
- package/src/domain/identityaccount/constants.ts +20 -0
- package/src/domain/identityaccount/controller.ts +16 -4
- package/src/domain/identityaccount/operator.ts +1 -0
- package/src/domain/identityaccount/provisioning.ts +17 -0
- package/src/domain/identityaccount/resource-store.ts +9 -5
- package/src/domain/identityaccount/steps.ts +58 -3
- package/src/domain/identityaccount/store-contract.ts +51 -0
- package/src/domain/identityaccount/store.ts +7 -5
- package/src/domain/platform/__tests__/platform.test.ts +7 -1
- package/src/domain/platform/controller.ts +12 -1
- package/src/domain/platformclient/__tests__/controller.test.ts +397 -0
- package/src/domain/platformclient/__tests__/credentials.test.ts +46 -0
- package/src/domain/platformclient/__tests__/mint.test.ts +415 -0
- package/src/domain/platformclient/__tests__/resource-store.test.ts +140 -0
- package/src/domain/platformclient/__tests__/system-managed.test.ts +101 -0
- package/src/domain/platformclient/__tests__/verifier-and-guard.test.ts +207 -0
- package/src/domain/platformclient/constants.ts +120 -0
- package/src/domain/platformclient/controller.ts +449 -0
- package/src/domain/platformclient/credentials.ts +68 -0
- package/src/domain/platformclient/mint.ts +412 -0
- package/src/domain/platformclient/origin-guard.ts +117 -0
- package/src/domain/platformclient/resource-store.ts +150 -0
- package/src/domain/platformclient/steps.ts +472 -0
- package/src/domain/platformclient/store-contract.ts +318 -0
- package/src/domain/platformclient/store.ts +63 -0
- package/src/domain/platformclient/system-managed.ts +115 -0
- package/src/domain/platformclient/token-controller.ts +49 -0
- package/src/domain/platformclient/verifier.ts +106 -0
- package/src/domain/workflow/__tests__/agent-call-references.test.ts +178 -27
- package/src/domain/workflow/agent-call-references.ts +95 -48
- package/src/domain/workflow/registry/data/task-kind-registry.json +2 -2
- package/src/encryption/key-manager.ts +87 -44
- package/src/extensions/__tests__/extension-composition.test.ts +3 -2
- package/src/extensions/__tests__/platform-client-composed.test.ts +288 -0
- package/src/extensions/__tests__/registry.test.ts +4 -1
- package/src/extensions/__tests__/tier-truthfulness.test.ts +8 -0
- package/src/extensions/caller-guards.ts +8 -3
- package/src/extensions/drivers.ts +37 -0
- package/src/extensions/guest-token-minting.ts +32 -0
- package/src/extensions/identity.ts +6 -4
- package/src/extensions/registry.ts +77 -3
- package/src/index.ts +55 -0
- package/src/pipeline/apiresource-labels.ts +10 -9
- package/src/pipeline/interceptors/auth.ts +27 -0
- package/src/pipeline/steps/__tests__/references.test.ts +37 -1
- package/src/pipeline/steps/build-update-state.ts +5 -2
- package/src/pipeline/steps/references.ts +49 -5
- package/src/platformtoken/__tests__/envelope.test.ts +277 -0
- package/src/platformtoken/__tests__/key-ring.test.ts +205 -0
- package/src/platformtoken/envelope.ts +276 -0
- package/src/platformtoken/key-ring.ts +266 -0
- package/src/runnerauth/__tests__/runner-subject-composed.test.ts +9 -3
|
@@ -1,40 +1,50 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pins the workflow domain's collector for the reference rule
|
|
3
|
-
* (../agent-call-references.ts): the
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
3
|
+
* (../agent-call-references.ts): the classifier's four forms (a literal
|
|
4
|
+
* `org/slug`, a relative bare slug, a value holding `${` that is fixed
|
|
5
|
+
* only at run, anything else malformed) and the refusal copy; the walk
|
|
6
|
+
* over top-level, nested and compensate tasks in declaration order, the
|
|
7
|
+
* relative form filled with the workflow's organization and marked, a
|
|
8
|
+
* run-time value collected as nothing; a config that does not decode
|
|
9
|
+
* skipped rather than double-reported; the step over a real store
|
|
10
|
+
* answering the rule's verdicts — a missing agent with the rule's copy,
|
|
11
|
+
* another organization's org-visible agent with the one
|
|
12
|
+
* cross-organization sentence, a platform-visible one admitted, the floor
|
|
13
|
+
* for a literal, the floor capped at org for a bare slug, a run-time value
|
|
14
|
+
* saved with no agent behind it; and the workflow's escalation door
|
|
15
|
+
* applying the same cap when a workflow calling a bare slug is raised to
|
|
16
|
+
* platform.
|
|
11
17
|
*/
|
|
12
18
|
import { create } from "@bufbuild/protobuf";
|
|
13
19
|
import { Code, ConnectError } from "@connectrpc/connect";
|
|
14
20
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
15
21
|
|
|
16
22
|
import { AgentSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
|
|
23
|
+
import type { Workflow } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/api_pb";
|
|
17
24
|
import { WorkflowSchema } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/api_pb";
|
|
18
25
|
import { WorkflowTaskKind } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/enum_pb";
|
|
19
26
|
import type { WorkflowSpec } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/spec_pb";
|
|
20
27
|
import { WorkflowSpecSchema } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/spec_pb";
|
|
21
28
|
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
22
29
|
import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
|
|
30
|
+
import { UpdateVisibilityInputSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/io_pb";
|
|
23
31
|
|
|
24
32
|
import { RequestContext } from "../../../pipeline/request-context.js";
|
|
25
33
|
import { testCallerIdentity } from "../../../pipeline/__tests__/support.js";
|
|
26
34
|
import {
|
|
35
|
+
belowFloorMessage,
|
|
27
36
|
missingReferencesMessage,
|
|
37
|
+
newGuardReferenceFloorOnEscalationStep,
|
|
28
38
|
notAvailableReferenceMessage,
|
|
29
39
|
referenceTargetKind,
|
|
30
40
|
} from "../../../pipeline/steps/references.js";
|
|
31
41
|
import type { SqliteStore } from "../../../store/sqlite/store.js";
|
|
32
42
|
import { tempStore } from "../../../store/sqlite/__tests__/support.js";
|
|
33
43
|
import {
|
|
44
|
+
classifyAgentReference,
|
|
34
45
|
collectAgentCallReferences,
|
|
35
46
|
malformedAgentReferenceMessage,
|
|
36
47
|
newValidateAgentCallReferencesStep,
|
|
37
|
-
parseAgentReference,
|
|
38
48
|
} from "../agent-call-references.js";
|
|
39
49
|
|
|
40
50
|
const V = ApiResourceVisibility;
|
|
@@ -52,24 +62,48 @@ function specOf(tasks: ReadonlyArray<Record<string, unknown>>): WorkflowSpec {
|
|
|
52
62
|
return create(WorkflowSpecSchema, { tasks } as never);
|
|
53
63
|
}
|
|
54
64
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
/** The three shapes the runner evaluates, whole and embedded, as authors write them. */
|
|
66
|
+
const RUN_TIME_VALUES = [
|
|
67
|
+
"${.env_vars.TEAM_ORG}/assistant",
|
|
68
|
+
"${.secrets.AGENT_REF}",
|
|
69
|
+
'${ "acme/reviewer" }',
|
|
70
|
+
"${ .input.agent }",
|
|
71
|
+
"acme/${ .input.slug }",
|
|
72
|
+
"${ .input.org }/a/b",
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
describe("classifyAgentReference", () => {
|
|
76
|
+
it("reads `org/slug` as a literal and a bare slug as relative", () => {
|
|
77
|
+
expect(classifyAgentReference("acme/reviewer")).toEqual({
|
|
78
|
+
kind: "literal",
|
|
79
|
+
org: "acme",
|
|
59
80
|
slug: "reviewer",
|
|
60
81
|
});
|
|
61
|
-
expect(
|
|
62
|
-
|
|
82
|
+
expect(classifyAgentReference("reviewer")).toEqual({
|
|
83
|
+
kind: "relative",
|
|
63
84
|
slug: "reviewer",
|
|
64
85
|
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("reads any value holding `${` as fixed only at run, whatever its shape", () => {
|
|
89
|
+
for (const value of RUN_TIME_VALUES) {
|
|
90
|
+
expect(classifyAgentReference(value), value).toEqual({
|
|
91
|
+
kind: "run-time",
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("reads everything else as malformed", () => {
|
|
65
97
|
for (const malformed of ["", "/reviewer", "acme/", "a/b/c", "//"]) {
|
|
66
|
-
expect(
|
|
98
|
+
expect(classifyAgentReference(malformed), malformed).toEqual({
|
|
99
|
+
kind: "malformed",
|
|
100
|
+
});
|
|
67
101
|
}
|
|
68
102
|
});
|
|
69
103
|
});
|
|
70
104
|
|
|
71
105
|
describe("collectAgentCallReferences", () => {
|
|
72
|
-
it("collects top-level, nested and compensate agent_call tasks in declaration order,
|
|
106
|
+
it("collects top-level, nested and compensate agent_call tasks in declaration order, a bare slug filled with the workflow's organization and marked relative", () => {
|
|
73
107
|
const spec = specOf([
|
|
74
108
|
agentCall("first", "reviewer"),
|
|
75
109
|
{
|
|
@@ -96,9 +130,29 @@ describe("collectAgentCallReferences", () => {
|
|
|
96
130
|
},
|
|
97
131
|
]);
|
|
98
132
|
expect(collectAgentCallReferences(spec, "acme")).toEqual([
|
|
99
|
-
{
|
|
133
|
+
{
|
|
134
|
+
kind: ApiResourceKind.agent,
|
|
135
|
+
org: "acme",
|
|
136
|
+
slug: "reviewer",
|
|
137
|
+
resolvesIn: "running-organization",
|
|
138
|
+
},
|
|
139
|
+
{ kind: ApiResourceKind.agent, org: "globex", slug: "summarizer" },
|
|
140
|
+
{
|
|
141
|
+
kind: ApiResourceKind.agent,
|
|
142
|
+
org: "acme",
|
|
143
|
+
slug: "cleaner",
|
|
144
|
+
resolvesIn: "running-organization",
|
|
145
|
+
},
|
|
146
|
+
]);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("collects nothing for a value fixed only at run, beside the references it does collect", () => {
|
|
150
|
+
const spec = specOf([
|
|
151
|
+
...RUN_TIME_VALUES.map((value, i) => agentCall(`dynamic_${i}`, value)),
|
|
152
|
+
agentCall("fixed", "globex/summarizer"),
|
|
153
|
+
]);
|
|
154
|
+
expect(collectAgentCallReferences(spec, "acme")).toEqual([
|
|
100
155
|
{ kind: ApiResourceKind.agent, org: "globex", slug: "summarizer" },
|
|
101
|
-
{ kind: ApiResourceKind.agent, org: "acme", slug: "cleaner" },
|
|
102
156
|
]);
|
|
103
157
|
});
|
|
104
158
|
|
|
@@ -162,16 +216,19 @@ describe("ValidateAgentCallReferences over a store", () => {
|
|
|
162
216
|
);
|
|
163
217
|
}
|
|
164
218
|
|
|
165
|
-
function workflowCalling(
|
|
219
|
+
function workflowCalling(
|
|
220
|
+
visibility: ApiResourceVisibility,
|
|
221
|
+
...agents: string[]
|
|
222
|
+
): Workflow {
|
|
166
223
|
return create(WorkflowSchema, {
|
|
167
224
|
apiVersion: "agentic.stigmer.ai/v1",
|
|
168
225
|
kind: "Workflow",
|
|
169
|
-
metadata: { name: "Pipeline", org: "acme", visibility
|
|
226
|
+
metadata: { id: "wfl_1", name: "Pipeline", org: "acme", visibility },
|
|
170
227
|
spec: specOf(agents.map((agent, i) => agentCall(`call_${i}`, agent))),
|
|
171
228
|
});
|
|
172
229
|
}
|
|
173
230
|
|
|
174
|
-
async function run(workflow:
|
|
231
|
+
async function run(workflow: Workflow): Promise<unknown> {
|
|
175
232
|
const ctx = new RequestContext(
|
|
176
233
|
WorkflowSchema,
|
|
177
234
|
workflow,
|
|
@@ -187,21 +244,27 @@ describe("ValidateAgentCallReferences over a store", () => {
|
|
|
187
244
|
}
|
|
188
245
|
|
|
189
246
|
it("a missing agent is refused with the rule's copy; an existing one passes", async () => {
|
|
190
|
-
const error = await run(workflowCalling("reviewer"));
|
|
247
|
+
const error = await run(workflowCalling(V.visibility_org, "reviewer"));
|
|
191
248
|
expect(error).toBeInstanceOf(ConnectError);
|
|
192
249
|
expect((error as ConnectError).code).toBe(Code.FailedPrecondition);
|
|
193
250
|
expect((error as ConnectError).rawMessage).toBe(
|
|
194
251
|
missingReferencesMessage(AGENT, [{ slug: "reviewer", org: "acme" }]),
|
|
195
252
|
);
|
|
196
253
|
await seedAgent("agt_1", "acme", "reviewer", V.visibility_org);
|
|
197
|
-
expect(
|
|
254
|
+
expect(
|
|
255
|
+
await run(workflowCalling(V.visibility_org, "reviewer")),
|
|
256
|
+
).toBeUndefined();
|
|
198
257
|
});
|
|
199
258
|
|
|
200
259
|
it("another organization's agent is admitted only at platform visibility, with the one sentence otherwise", async () => {
|
|
201
260
|
await seedAgent("agt_shared", "globex", "shared", V.visibility_platform);
|
|
202
261
|
await seedAgent("agt_internal", "globex", "internal", V.visibility_org);
|
|
203
|
-
expect(
|
|
204
|
-
|
|
262
|
+
expect(
|
|
263
|
+
await run(workflowCalling(V.visibility_org, "globex/shared")),
|
|
264
|
+
).toBeUndefined();
|
|
265
|
+
const error = await run(
|
|
266
|
+
workflowCalling(V.visibility_org, "globex/internal"),
|
|
267
|
+
);
|
|
205
268
|
expect((error as ConnectError).rawMessage).toBe(
|
|
206
269
|
notAvailableReferenceMessage(AGENT, {
|
|
207
270
|
kind: ApiResourceKind.agent,
|
|
@@ -213,10 +276,98 @@ describe("ValidateAgentCallReferences over a store", () => {
|
|
|
213
276
|
|
|
214
277
|
it("the floor holds for the agent a workflow calls: an org-visible workflow may not call a private agent", async () => {
|
|
215
278
|
await seedAgent("agt_mine", "acme", "mine", V.visibility_private);
|
|
216
|
-
const error = await run(workflowCalling("mine"));
|
|
279
|
+
const error = await run(workflowCalling(V.visibility_org, "mine"));
|
|
217
280
|
expect((error as ConnectError).code).toBe(Code.FailedPrecondition);
|
|
218
281
|
expect((error as ConnectError).rawMessage).toContain(
|
|
219
282
|
"referenced agent 'acme/mine' is visibility_private while this resource is visibility_org",
|
|
220
283
|
);
|
|
221
284
|
});
|
|
285
|
+
|
|
286
|
+
it("a platform-visible workflow may call its organization's org-visible agent by bare slug, never by literal, and never a private one", async () => {
|
|
287
|
+
await seedAgent("agt_team", "acme", "team", V.visibility_org);
|
|
288
|
+
await seedAgent("agt_mine", "acme", "mine", V.visibility_private);
|
|
289
|
+
expect(
|
|
290
|
+
await run(workflowCalling(V.visibility_platform, "team")),
|
|
291
|
+
).toBeUndefined();
|
|
292
|
+
// The literal names a fixed row that every organization's runs read.
|
|
293
|
+
const literal = await run(
|
|
294
|
+
workflowCalling(V.visibility_platform, "acme/team"),
|
|
295
|
+
);
|
|
296
|
+
expect((literal as ConnectError).rawMessage).toContain(
|
|
297
|
+
"referenced agent 'acme/team' is visibility_org while this resource is visibility_platform",
|
|
298
|
+
);
|
|
299
|
+
const below = await run(workflowCalling(V.visibility_platform, "mine"));
|
|
300
|
+
expect((below as ConnectError).rawMessage).toContain(
|
|
301
|
+
"referenced agent 'acme/mine' is visibility_private while this resource is visibility_platform",
|
|
302
|
+
);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it("a value fixed only at run saves with no agent behind it, at every level", async () => {
|
|
306
|
+
for (const visibility of [
|
|
307
|
+
V.visibility_private,
|
|
308
|
+
V.visibility_org,
|
|
309
|
+
V.visibility_platform,
|
|
310
|
+
]) {
|
|
311
|
+
expect(
|
|
312
|
+
await run(workflowCalling(visibility, ...RUN_TIME_VALUES)),
|
|
313
|
+
V[visibility],
|
|
314
|
+
).toBeUndefined();
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
describe("the workflow's escalation door", () => {
|
|
319
|
+
const TARGET = "loadedWorkflow";
|
|
320
|
+
|
|
321
|
+
async function escalate(
|
|
322
|
+
stored: Workflow,
|
|
323
|
+
requested: ApiResourceVisibility,
|
|
324
|
+
): Promise<unknown> {
|
|
325
|
+
const ctx = new RequestContext(
|
|
326
|
+
UpdateVisibilityInputSchema,
|
|
327
|
+
create(UpdateVisibilityInputSchema, {
|
|
328
|
+
resourceId: "wfl_1",
|
|
329
|
+
visibility: requested,
|
|
330
|
+
}),
|
|
331
|
+
testCallerIdentity(),
|
|
332
|
+
ApiResourceKind.workflow,
|
|
333
|
+
);
|
|
334
|
+
ctx.set(TARGET, stored);
|
|
335
|
+
try {
|
|
336
|
+
await newGuardReferenceFloorOnEscalationStep(store, TARGET, [
|
|
337
|
+
(row) =>
|
|
338
|
+
collectAgentCallReferences(
|
|
339
|
+
(row as Workflow).spec,
|
|
340
|
+
(row as Workflow).metadata?.org ?? "",
|
|
341
|
+
),
|
|
342
|
+
]).execute(ctx);
|
|
343
|
+
return undefined;
|
|
344
|
+
} catch (error) {
|
|
345
|
+
return error;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
it("raising a workflow that calls a bare slug to platform asks the capped floor", async () => {
|
|
350
|
+
await seedAgent("agt_team", "acme", "team", V.visibility_org);
|
|
351
|
+
await seedAgent("agt_mine", "acme", "mine", V.visibility_private);
|
|
352
|
+
expect(
|
|
353
|
+
await escalate(
|
|
354
|
+
workflowCalling(V.visibility_org, "team"),
|
|
355
|
+
V.visibility_platform,
|
|
356
|
+
),
|
|
357
|
+
).toBeUndefined();
|
|
358
|
+
const error = await escalate(
|
|
359
|
+
workflowCalling(V.visibility_private, "mine"),
|
|
360
|
+
V.visibility_platform,
|
|
361
|
+
);
|
|
362
|
+
expect((error as ConnectError).code).toBe(Code.FailedPrecondition);
|
|
363
|
+
expect((error as ConnectError).rawMessage).toBe(
|
|
364
|
+
belowFloorMessage(
|
|
365
|
+
AGENT,
|
|
366
|
+
{ kind: ApiResourceKind.agent, org: "acme", slug: "mine" },
|
|
367
|
+
V.visibility_private,
|
|
368
|
+
V.visibility_platform,
|
|
369
|
+
),
|
|
370
|
+
);
|
|
371
|
+
});
|
|
372
|
+
});
|
|
222
373
|
});
|
|
@@ -1,31 +1,50 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ValidateAgentCallReferences — the second collector for the reference rule
|
|
3
3
|
* (pipeline/steps/references.ts): a workflow's `agent_call` tasks name their
|
|
4
|
-
* agent as a STRING
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* and asks the rule's one function — same clauses, same copy. It
|
|
11
|
-
* the workflow domain and not inside `InProcessValidator` because
|
|
12
|
-
* validator is pure over the spec by design and a rule that reads the
|
|
4
|
+
* agent as a STRING inside a `google.protobuf.Struct` config the generic
|
|
5
|
+
* spec walker cannot see, so the rule would never meet them. This step
|
|
6
|
+
* parses each task's config through the converter's own strict decoder
|
|
7
|
+
* (`unmarshalTaskConfig`, the one reader of a task config), walks nested
|
|
8
|
+
* control-flow tasks exactly as the constraint validator does, turns every
|
|
9
|
+
* `agent` string the write can judge into a reference with the workflow as
|
|
10
|
+
* parent, and asks the rule's one function — same clauses, same copy. It
|
|
11
|
+
* lives in the workflow domain and not inside `InProcessValidator` because
|
|
12
|
+
* the validator is pure over the spec by design and a rule that reads the
|
|
13
13
|
* target's row cannot be.
|
|
14
14
|
*
|
|
15
|
-
* The
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
15
|
+
* The string has three forms (the `agent` field's contract in
|
|
16
|
+
* agent_call.proto), told apart by `classifyAgentReference`:
|
|
17
|
+
*
|
|
18
|
+
* - `org/slug`, a LITERAL: judged now, like any stored reference;
|
|
19
|
+
* - a bare `slug`, RELATIVE: the runner resolves it in the organization
|
|
20
|
+
* the workflow runs in (`__stigmer_org_id`, the execution's), which is
|
|
21
|
+
* not the workflow's when a person runs it from another organization by
|
|
22
|
+
* id. It is judged now in the workflow's own organization with its
|
|
23
|
+
* floor capped at org (references.ts, "RELATIVE");
|
|
24
|
+
* - a value holding `${`, fixed only AT RUN: the runner's jq phase
|
|
25
|
+
* (`${ expr }`, whole or embedded) or its activity phase
|
|
26
|
+
* (`${.env_vars.KEY}`, `${.secrets.KEY}`) produces the reference when
|
|
27
|
+
* the task runs. The write collects nothing for it; the run reads the
|
|
28
|
+
* resolved agent as the person who ran the workflow, as it reads every
|
|
29
|
+
* id-bound binding. The slug grammar (metadata.proto) can never contain
|
|
30
|
+
* `${`, so the test is exact. It deliberately does not recognise only
|
|
31
|
+
* the runner's shapes: that would be a second copy of the runner's
|
|
32
|
+
* grammar in a package that shares only the protos with it, and a
|
|
33
|
+
* mistyped expression already fails at run with the read's not-found.
|
|
34
|
+
*
|
|
35
|
+
* Anything else is refused at write naming the task. The runner's own
|
|
36
|
+
* parser (`parseAgentReference` in runner/src/activities/call-agent.ts) is
|
|
37
|
+
* lenient — it splits on the first `/` and drops a third segment — and the
|
|
38
|
+
* CLI's `parseReference` (client-apps/cli/src/resources/reference.ts)
|
|
39
|
+
* refuses a leading slash; refusing every malformed literal here means
|
|
40
|
+
* neither ever meets one. The three cannot share code; the conformance
|
|
41
|
+
* suites are where their agreement is proven.
|
|
24
42
|
*
|
|
25
43
|
* Runs after NormalizeReferences and ValidateReferences on the workflow
|
|
26
|
-
* create and update chains,
|
|
27
|
-
*
|
|
28
|
-
*
|
|
44
|
+
* create and update chains, and feeds the workflow's escalation door
|
|
45
|
+
* (controller.ts). Proven by __tests__/agent-call-references.test.ts, the
|
|
46
|
+
* workflow conformance suite's agent_call reference arm, and the
|
|
47
|
+
* runner-as-subject execution arm for a reference fixed only at run.
|
|
29
48
|
*/
|
|
30
49
|
import type { WorkflowSchema } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/api_pb";
|
|
31
50
|
import type { AgentCallTaskConfig } from "@stigmer/protos/ai/stigmer/agentic/workflow/v1/tasks/agent_call_pb";
|
|
@@ -49,7 +68,7 @@ import { metadataOf } from "../../pipeline/steps/shapes.js";
|
|
|
49
68
|
import { unmarshalTaskConfig } from "./converter/unmarshal.js";
|
|
50
69
|
import { nestedTasks } from "./validation/task-config-constraints.js";
|
|
51
70
|
|
|
52
|
-
/** The one sentence for an `agent` string that is
|
|
71
|
+
/** The one sentence for an `agent` string that is none of the three forms. */
|
|
53
72
|
export function malformedAgentReferenceMessage(
|
|
54
73
|
taskName: string,
|
|
55
74
|
agent: string,
|
|
@@ -57,29 +76,38 @@ export function malformedAgentReferenceMessage(
|
|
|
57
76
|
return `task '${taskName}': agent '${agent}' is not a reference; write 'slug' for an agent of this organization or 'org/slug' for another organization's.`;
|
|
58
77
|
}
|
|
59
78
|
|
|
60
|
-
/**
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
79
|
+
/** What an `agent` string is before the rule reads it (the module header). */
|
|
80
|
+
export type AgentReferenceForm =
|
|
81
|
+
| { readonly kind: "literal"; readonly org: string; readonly slug: string }
|
|
82
|
+
| { readonly kind: "relative"; readonly slug: string }
|
|
83
|
+
| { readonly kind: "run-time" }
|
|
84
|
+
| { readonly kind: "malformed" };
|
|
85
|
+
|
|
86
|
+
/** Opens every runtime expression and placeholder the runner evaluates; never part of a slug. */
|
|
87
|
+
const RUNTIME_EXPRESSION_MARKER = "${";
|
|
88
|
+
|
|
89
|
+
export function classifyAgentReference(agent: string): AgentReferenceForm {
|
|
90
|
+
if (agent.includes(RUNTIME_EXPRESSION_MARKER)) {
|
|
91
|
+
return { kind: "run-time" };
|
|
92
|
+
}
|
|
67
93
|
const parts = agent.split("/");
|
|
68
|
-
if (parts.length === 1) {
|
|
69
|
-
return
|
|
94
|
+
if (parts.length === 1 && parts[0] !== "") {
|
|
95
|
+
return { kind: "relative", slug: parts[0]! };
|
|
70
96
|
}
|
|
71
97
|
if (parts.length === 2 && parts[0] !== "" && parts[1] !== "") {
|
|
72
|
-
return { org: parts[0]!, slug: parts[1]! };
|
|
98
|
+
return { kind: "literal", org: parts[0]!, slug: parts[1]! };
|
|
73
99
|
}
|
|
74
|
-
return
|
|
100
|
+
return { kind: "malformed" };
|
|
75
101
|
}
|
|
76
102
|
|
|
77
103
|
/**
|
|
78
|
-
* Every `agent_call` task's agent as a reference,
|
|
79
|
-
* declaration order
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
104
|
+
* Every `agent_call` task's agent the write can judge, as a reference,
|
|
105
|
+
* nested tasks included in declaration order: a literal as written, a
|
|
106
|
+
* relative slug in `parentOrg` and marked relative, a value fixed only at
|
|
107
|
+
* run not at all. Throws INVALID_ARGUMENT on the first malformed string. A
|
|
108
|
+
* config that does not decode is skipped: the spec validation step has
|
|
109
|
+
* already refused the workflow for it, and this collector never
|
|
110
|
+
* double-reports.
|
|
83
111
|
*/
|
|
84
112
|
export function collectAgentCallReferences(
|
|
85
113
|
spec: WorkflowSpec | undefined,
|
|
@@ -100,17 +128,36 @@ export function collectAgentCallReferences(
|
|
|
100
128
|
if (config !== undefined) {
|
|
101
129
|
if (task.kind === WorkflowTaskKind.agent_call) {
|
|
102
130
|
const agent = (config as AgentCallTaskConfig).agent;
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
131
|
+
const form = classifyAgentReference(agent);
|
|
132
|
+
switch (form.kind) {
|
|
133
|
+
case "literal":
|
|
134
|
+
refs.push({
|
|
135
|
+
kind: ApiResourceKind.agent,
|
|
136
|
+
org: form.org,
|
|
137
|
+
slug: form.slug,
|
|
138
|
+
});
|
|
139
|
+
break;
|
|
140
|
+
case "relative":
|
|
141
|
+
refs.push({
|
|
142
|
+
kind: ApiResourceKind.agent,
|
|
143
|
+
org: parentOrg,
|
|
144
|
+
slug: form.slug,
|
|
145
|
+
resolvesIn: "running-organization",
|
|
146
|
+
});
|
|
147
|
+
break;
|
|
148
|
+
case "run-time":
|
|
149
|
+
break;
|
|
150
|
+
case "malformed":
|
|
151
|
+
throw invalidArgumentError(
|
|
152
|
+
malformedAgentReferenceMessage(task.name, agent),
|
|
153
|
+
);
|
|
154
|
+
default: {
|
|
155
|
+
const exhaustive: never = form;
|
|
156
|
+
throw new Error(
|
|
157
|
+
`unknown agent reference form: ${JSON.stringify(exhaustive)}`,
|
|
158
|
+
);
|
|
159
|
+
}
|
|
108
160
|
}
|
|
109
|
-
refs.push({
|
|
110
|
-
kind: ApiResourceKind.agent,
|
|
111
|
-
org: parsed.org === "" ? parentOrg : parsed.org,
|
|
112
|
-
slug: parsed.slug,
|
|
113
|
-
});
|
|
114
161
|
}
|
|
115
162
|
for (const nested of nestedTasks(config)) {
|
|
116
163
|
visit(nested);
|
|
@@ -1362,7 +1362,7 @@
|
|
|
1362
1362
|
{
|
|
1363
1363
|
"name": "agent",
|
|
1364
1364
|
"displayName": "Agent",
|
|
1365
|
-
"description": "Agent
|
|
1365
|
+
"description": "Agent to invoke, as \"slug\", \"org/slug\", or a value holding a runtime expression. - \"slug\": an agent of the organization the workflow runs in; checked when the workflow is saved, against the workflow's own organization - \"org/slug\": that organization's agent; checked when the workflow is saved, and another organization's agent must be shared at platform visibility - a value holding \"${ ... }\", \"${.env_vars.KEY}\" or \"${.secrets.KEY}\": resolved to \"slug\" or \"org/slug\" when the task runs, and read as the person who ran the workflow Examples: \"code-reviewer\", \"acme/data-analyst\", \"${.env_vars.TEAM_ORG}/assistant\" Required field.",
|
|
1366
1366
|
"type": "string",
|
|
1367
1367
|
"required": true,
|
|
1368
1368
|
"groupId": "core",
|
|
@@ -1470,7 +1470,7 @@
|
|
|
1470
1470
|
"additionalProperties": false,
|
|
1471
1471
|
"properties": {
|
|
1472
1472
|
"agent": {
|
|
1473
|
-
"description": "Agent
|
|
1473
|
+
"description": "Agent to invoke, as \"slug\", \"org/slug\", or a value holding a runtime expression. - \"slug\": an agent of the organization the workflow runs in; checked when the workflow is saved, against the workflow's own organization - \"org/slug\": that organization's agent; checked when the workflow is saved, and another organization's agent must be shared at platform visibility - a value holding \"${ ... }\", \"${.env_vars.KEY}\" or \"${.secrets.KEY}\": resolved to \"slug\" or \"org/slug\" when the task runs, and read as the person who ran the workflow Examples: \"code-reviewer\", \"acme/data-analyst\", \"${.env_vars.TEAM_ORG}/assistant\" Required field.",
|
|
1474
1474
|
"maxLength": 127,
|
|
1475
1475
|
"minLength": 1,
|
|
1476
1476
|
"type": "string"
|