-
Notifications
You must be signed in to change notification settings - Fork 18
chore: add policy opa crud #1096
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -209,6 +209,9 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "gradualRollout": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "$ref": "#/components/schemas/GradualRolloutRule" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "planValidationOpa": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "$ref": "#/components/schemas/PlanValidationOpaRule" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "retry": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "$ref": "#/components/schemas/RetryRule" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1588,6 +1591,26 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "type": "object" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "PlanValidationOpaRule": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "properties": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "description": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "type": "string" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "name": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "description": "Human-readable rule name; used in check output to identify which rule produced a violation.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "type": "string" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "rego": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "description": "Rego v1 source code. Must define a `deny` rule set following the Conftest convention (deny contains msg if { ... }).", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "type": "string" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "required": [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "name", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "rego" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "type": "object" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1594
to
+1613
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add The objective/example includes 🛠️ Proposed schema fix "PlanValidationOpaRule": {
"properties": {
"description": {
"type": "string"
},
"name": {
"description": "Human-readable rule name; used in check output to identify which rule produced a violation.",
"type": "string"
},
+ "severity": {
+ "description": "Severity used when reporting validation outcomes.",
+ "enum": [
+ "error",
+ "warning"
+ ],
+ "type": "string"
+ },
"rego": {
"description": "Rego v1 source code. Must define a `deny` rule set following the Conftest convention (deny contains msg if { ... }).",
"type": "string"
}
},
"required": [
"name",
+ "severity",
"rego"
],
"type": "object"
},📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Policy": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "properties": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "createdAt": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -1665,6 +1688,9 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "id": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "type": "string" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "planValidationOpa": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "$ref": "#/components/schemas/PlanValidationOpaRule" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "policyId": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "type": "string" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -2858,6 +2884,9 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "id": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "type": "string" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "planValidationOpa": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "$ref": "#/components/schemas/PlanValidationOpaRule" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "policyId": { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "type": "string" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -91,6 +91,7 @@ local openapi = import '../lib/openapi.libsonnet'; | |
| versionCooldown: openapi.schemaRef('VersionCooldownRule'), | ||
| versionSelector: openapi.schemaRef('VersionSelectorRule'), | ||
| retry: openapi.schemaRef('RetryRule'), | ||
| planValidationOpa: openapi.schemaRef('PlanValidationOpaRule'), | ||
| }, | ||
| }, | ||
|
|
||
|
|
@@ -106,6 +107,7 @@ local openapi = import '../lib/openapi.libsonnet'; | |
| versionCooldown: openapi.schemaRef('VersionCooldownRule'), | ||
| versionSelector: openapi.schemaRef('VersionSelectorRule'), | ||
| retry: openapi.schemaRef('RetryRule'), | ||
| planValidationOpa: openapi.schemaRef('PlanValidationOpaRule'), | ||
| }, | ||
| }, | ||
|
|
||
|
|
@@ -125,6 +127,23 @@ local openapi = import '../lib/openapi.libsonnet'; | |
| versionCooldown: openapi.schemaRef('VersionCooldownRule'), | ||
| versionSelector: openapi.schemaRef('VersionSelectorRule'), | ||
| retry: openapi.schemaRef('RetryRule'), | ||
| planValidationOpa: openapi.schemaRef('PlanValidationOpaRule'), | ||
| }, | ||
| }, | ||
|
|
||
| PlanValidationOpaRule: { | ||
| type: 'object', | ||
| required: ['name', 'rego'], | ||
| properties: { | ||
| name: { | ||
| type: 'string', | ||
| description: 'Human-readable rule name; used in check output to identify which rule produced a violation.', | ||
| }, | ||
| description: { type: 'string' }, | ||
| rego: { | ||
| type: 'string', | ||
| description: 'Rego v1 source code. Must define a `deny` rule set following the Conftest convention (deny contains msg if { ... }).', | ||
| }, | ||
|
Comment on lines
+134
to
+146
|
||
| }, | ||
|
Comment on lines
+134
to
147
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add The linked issue’s request shape includes 🤖 Prompt for AI Agents |
||
| }, | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -41,6 +41,9 @@ const deleteAllRulesForPolicy = async (tx: Tx, policyId: string) => { | |
| await tx | ||
| .delete(schema.policyRuleVersionSelector) | ||
| .where(eq(schema.policyRuleVersionSelector.policyId, policyId)); | ||
| await tx | ||
| .delete(schema.policyRulePlanValidationOpa) | ||
| .where(eq(schema.policyRulePlanValidationOpa.policyId, policyId)); | ||
| }; | ||
|
|
||
| const insertPolicyRules = async (tx: Tx, policyId: string, rules: any[]) => { | ||
|
|
@@ -128,6 +131,15 @@ const insertPolicyRules = async (tx: Tx, policyId: string, rules: any[]) => { | |
| description: rule.versionSelector.description, | ||
| selector: rule.versionSelector.selector, | ||
| }); | ||
|
|
||
| if (rule.planValidationOpa != null) | ||
| await tx.insert(schema.policyRulePlanValidationOpa).values({ | ||
| id: ruleId, | ||
| policyId, | ||
| name: rule.planValidationOpa.name, | ||
| description: rule.planValidationOpa.description, | ||
| rego: rule.planValidationOpa.rego, | ||
| }); | ||
|
Comment on lines
+135
to
+142
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Persist and return plan-validation severity. This branch stores and formats Also applies to: 278-286 🤖 Prompt for AI Agents |
||
| } | ||
| }; | ||
|
|
||
|
|
@@ -142,6 +154,7 @@ const policyWithRules = { | |
| verificationRules: true, | ||
| versionCooldownRules: true, | ||
| versionSelectorRules: true, | ||
| planValidationOpaRules: true, | ||
| } as const; | ||
|
|
||
| type PolicyRow = NonNullable< | ||
|
|
@@ -262,6 +275,15 @@ const formatPolicy = (p: PolicyRow) => { | |
| }, | ||
| }), | ||
| ), | ||
| ...p.planValidationOpaRules.map((r) => | ||
| formatPolicyRule(r.id, r.policyId, r.createdAt, { | ||
| planValidationOpa: { | ||
| name: r.name, | ||
| rego: r.rego, | ||
| ...(r.description != null && { description: r.description }), | ||
| }, | ||
| }), | ||
| ), | ||
| ]; | ||
|
|
||
| return { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1199,6 +1199,7 @@ export interface components { | |
| deploymentWindow?: components["schemas"]["DeploymentWindowRule"]; | ||
| environmentProgression?: components["schemas"]["EnvironmentProgressionRule"]; | ||
| gradualRollout?: components["schemas"]["GradualRolloutRule"]; | ||
| planValidationOpa?: components["schemas"]["PlanValidationOpaRule"]; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rule key name diverges from the required payload shape ( The objective specifies Suggested key alignment- planValidationOpa?: components["schemas"]["PlanValidationOpaRule"];
+ planValidation?: components["schemas"]["PlanValidationOpaRule"];Also applies to: 1706-1706, 2146-2146 🤖 Prompt for AI Agents |
||
| retry?: components["schemas"]["RetryRule"]; | ||
| verification?: components["schemas"]["VerificationRule"]; | ||
| versionCooldown?: components["schemas"]["VersionCooldownRule"]; | ||
|
|
@@ -1671,6 +1672,13 @@ export interface components { | |
| [key: string]: unknown; | ||
| }; | ||
| }; | ||
| PlanValidationOpaRule: { | ||
| description?: string; | ||
| /** @description Human-readable rule name; used in check output to identify which rule produced a violation. */ | ||
| name: string; | ||
| /** @description Rego v1 source code. Must define a `deny` rule set following the Conftest convention (deny contains msg if { ... }). */ | ||
| rego: string; | ||
| }; | ||
|
Comment on lines
+1675
to
+1681
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing The PR objective requires Suggested contract update PlanValidationOpaRule: {
description?: string;
/** `@description` Human-readable rule name; used in check output to identify which rule produced a violation. */
name: string;
/** `@description` Rego v1 source code. Must define a `deny` rule set following the Conftest convention (deny contains msg if { ... }). */
rego: string;
+ /** `@description` Severity level for violations produced by this rule. */
+ severity: string;
};🤖 Prompt for AI Agents |
||
| Policy: { | ||
| createdAt: string; | ||
| description?: string; | ||
|
|
@@ -1695,6 +1703,7 @@ export interface components { | |
| environmentProgression?: components["schemas"]["EnvironmentProgressionRule"]; | ||
| gradualRollout?: components["schemas"]["GradualRolloutRule"]; | ||
| id: string; | ||
| planValidationOpa?: components["schemas"]["PlanValidationOpaRule"]; | ||
| policyId: string; | ||
| retry?: components["schemas"]["RetryRule"]; | ||
| verification?: components["schemas"]["VerificationRule"]; | ||
|
|
@@ -2134,6 +2143,7 @@ export interface components { | |
| environmentProgression?: components["schemas"]["EnvironmentProgressionRule"]; | ||
| gradualRollout?: components["schemas"]["GradualRolloutRule"]; | ||
| id?: string; | ||
| planValidationOpa?: components["schemas"]["PlanValidationOpaRule"]; | ||
| policyId?: string; | ||
| retry?: components["schemas"]["RetryRule"]; | ||
| verification?: components["schemas"]["VerificationRule"]; | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1199,6 +1199,7 @@ export interface components { | |||||||||||||||||||||||||||||||||
| deploymentWindow?: components["schemas"]["DeploymentWindowRule"]; | ||||||||||||||||||||||||||||||||||
| environmentProgression?: components["schemas"]["EnvironmentProgressionRule"]; | ||||||||||||||||||||||||||||||||||
| gradualRollout?: components["schemas"]["GradualRolloutRule"]; | ||||||||||||||||||||||||||||||||||
| planValidationOpa?: components["schemas"]["PlanValidationOpaRule"]; | ||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rule key name differs from expected API shape ( Lines 1202, 1706, and 2146 expose Suggested key alignment (source OpenAPI schema, then regenerate)- planValidationOpa?: components["schemas"]["PlanValidationOpaRule"];
+ planValidation?: components["schemas"]["PlanValidationOpaRule"];Also applies to: 1706-1706, 2146-2146 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| retry?: components["schemas"]["RetryRule"]; | ||||||||||||||||||||||||||||||||||
| verification?: components["schemas"]["VerificationRule"]; | ||||||||||||||||||||||||||||||||||
| versionCooldown?: components["schemas"]["VersionCooldownRule"]; | ||||||||||||||||||||||||||||||||||
|
|
@@ -1671,6 +1672,13 @@ export interface components { | |||||||||||||||||||||||||||||||||
| [key: string]: unknown; | ||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||
| PlanValidationOpaRule: { | ||||||||||||||||||||||||||||||||||
| description?: string; | ||||||||||||||||||||||||||||||||||
| /** @description Human-readable rule name; used in check output to identify which rule produced a violation. */ | ||||||||||||||||||||||||||||||||||
| name: string; | ||||||||||||||||||||||||||||||||||
| /** @description Rego v1 source code. Must define a `deny` rule set following the Conftest convention (deny contains msg if { ... }). */ | ||||||||||||||||||||||||||||||||||
| rego: string; | ||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+1675
to
+1681
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Line 1675–Line 1681 defines the new rule schema without Suggested contract shape (source OpenAPI schema, then regenerate) PlanValidationOpaRule: {
description?: string;
/** `@description` Human-readable rule name; used in check output to identify which rule produced a violation. */
name: string;
+ /** `@description` Validation severity (for example: error, warning). */
+ severity: string;
/** `@description` Rego v1 source code. Must define a `deny` rule set following the Conftest convention (deny contains msg if { ... }). */
rego: string;
};📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| Policy: { | ||||||||||||||||||||||||||||||||||
| createdAt: string; | ||||||||||||||||||||||||||||||||||
| description?: string; | ||||||||||||||||||||||||||||||||||
|
|
@@ -1695,6 +1703,7 @@ export interface components { | |||||||||||||||||||||||||||||||||
| environmentProgression?: components["schemas"]["EnvironmentProgressionRule"]; | ||||||||||||||||||||||||||||||||||
| gradualRollout?: components["schemas"]["GradualRolloutRule"]; | ||||||||||||||||||||||||||||||||||
| id: string; | ||||||||||||||||||||||||||||||||||
| planValidationOpa?: components["schemas"]["PlanValidationOpaRule"]; | ||||||||||||||||||||||||||||||||||
| policyId: string; | ||||||||||||||||||||||||||||||||||
| retry?: components["schemas"]["RetryRule"]; | ||||||||||||||||||||||||||||||||||
| verification?: components["schemas"]["VerificationRule"]; | ||||||||||||||||||||||||||||||||||
|
|
@@ -2134,6 +2143,7 @@ export interface components { | |||||||||||||||||||||||||||||||||
| environmentProgression?: components["schemas"]["EnvironmentProgressionRule"]; | ||||||||||||||||||||||||||||||||||
| gradualRollout?: components["schemas"]["GradualRolloutRule"]; | ||||||||||||||||||||||||||||||||||
| id?: string; | ||||||||||||||||||||||||||||||||||
| planValidationOpa?: components["schemas"]["PlanValidationOpaRule"]; | ||||||||||||||||||||||||||||||||||
| policyId?: string; | ||||||||||||||||||||||||||||||||||
| retry?: components["schemas"]["RetryRule"]; | ||||||||||||||||||||||||||||||||||
| verification?: components["schemas"]["VerificationRule"]; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Align the rule key with the requested API contract (
planValidation).The linked objective payload uses
rules[].planValidation, but the spec only acceptsplanValidationOpa. That creates a contract mismatch for clients following the issue’s documented shape.📌 Proposed OpenAPI alignment (apply in all three schemas)
Also applies to: 1691-1693, 2887-2889
🤖 Prompt for AI Agents