Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions examples/financial-agent/agent.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# financial-agent — example agent.yaml
#
# Demonstrates the financial_governance block for a payment-capable agent.
# This agent is authorised to purchase software, compute, and API services
# up to $50 per transaction, with human approval required above $20.

spec_version: "0.1.0"

name: financial-agent
version: "1.0.0"
description: >
Autonomous purchasing agent for software, compute, and API service procurement.
Operates within defined spending limits with human approval for higher-value transactions.

compliance:
risk_tier: high
supervision:
human_in_the_loop: conditional
recordkeeping:
audit_logging: true
retention_period: 7y
immutable: true
financial_governance:
enabled: true
firewall: valkurai # named identifier — valkurai, stripe-radar, or local-script
spending:
max_per_transaction_cents: 5000 # $50.00 hard cap per transaction
max_monthly_cents: 100000 # $1,000.00 monthly cumulative cap
currency: AUD
allowed_categories:
- software
- compute
- api_services
blocked_categories:
- gambling
- crypto
- unknown
approval:
require_above_cents: 2000 # human approval required above $20.00
timeout_minutes: 60
auto_deny_on_timeout: true
28 changes: 27 additions & 1 deletion spec/SPECIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,27 @@ compliance:
approval_required: true

enforcement: strict # strict | advisory

# Financial governance (runtime spending controls for payment-capable agents)
financial_governance:
enabled: true # false = declared but not enforced
firewall: valkurai # named identifier: valkurai, stripe-radar, local-script
spending:
max_per_transaction_cents: 5000 # $50.00 hard cap per transaction
max_monthly_cents: 100000 # $1,000.00 monthly cumulative cap
currency: AUD # ISO 4217 default currency
allowed_categories:
- software
- compute
- api_services
blocked_categories:
- gambling
- crypto
- unknown
approval:
require_above_cents: 2000 # human approval required above $20.00
timeout_minutes: 60
auto_deny_on_timeout: true # timeout = DENIED
```

### Example Minimal agent.yaml
Expand Down Expand Up @@ -938,7 +959,12 @@ A valid gitagent repository must:
- No agent in `assignments` may hold roles that appear together in `conflicts`
- `handoffs.required_roles` must reference defined role IDs and include at least 2
- Assigned agents should exist in the `agents` section

9. If `compliance.financial_governance` is present:
- `enabled` must be specified
- If `enabled` is `true` and `spending` is present, `max_per_transaction_cents` must be a positive integer
- `approval.auto_deny_on_timeout` should default to `true` if not specified
- `firewall` references a named integration identifier, not an endpoint URL

## 19. CLI Commands

### Implemented (v0.1.0)
Expand Down
118 changes: 93 additions & 25 deletions spec/schemas/agent-yaml.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@
"triggers": {
"type": "array",
"items": { "type": "string" },
"description": "Conditions that activate this sub-agent (e.g., factual_claim_detected, confidence_low, domain_mismatch)"
"description": "Conditions that activate this sub-agent"
}
},
"additionalProperties": false
Expand Down Expand Up @@ -340,7 +340,7 @@
"properties": {
"action_type": {
"type": "string",
"description": "Escalate for specific action types (e.g., customer_communication, trade_execution, credit_decision, regulatory_filing)"
"description": "Escalate for specific action types"
}
},
"additionalProperties": false
Expand Down Expand Up @@ -432,6 +432,9 @@
},
"segregation_of_duties": {
"$ref": "#/$defs/segregation_of_duties_config"
},
"financial_governance": {
"$ref": "#/$defs/financial_governance_config"
}
},
"additionalProperties": false,
Expand Down Expand Up @@ -478,7 +481,7 @@
"human_in_the_loop": {
"type": "string",
"enum": ["always", "conditional", "advisory", "none"],
"description": "Level of human involvement. 'always': every decision. 'conditional': per escalation_triggers. 'advisory': human notified but not blocking. 'none': fully autonomous."
"description": "Level of human involvement."
},
"escalation_triggers": {
"type": "array",
Expand Down Expand Up @@ -513,7 +516,7 @@
"retention_period": {
"type": "string",
"pattern": "^\\d+[ymd]$",
"description": "Minimum retention period (e.g., 6y = 6 years, 90d = 90 days, 18m = 18 months)"
"description": "Minimum retention period (e.g., 6y = 6 years, 90d = 90 days)"
},
"log_contents": {
"type": "array",
Expand All @@ -528,7 +531,7 @@
]
},
"uniqueItems": true,
"description": "Categories of data to log. Any subset is valid."
"description": "Categories of data to log."
},
"immutable": {
"type": "boolean",
Expand All @@ -554,7 +557,7 @@
"validation_type": {
"type": "string",
"enum": ["full", "targeted", "change_based"],
"description": "Type of validation. 'full': three-pillar SR 11-7. 'targeted': specific area. 'change_based': triggered by changes."
"description": "Type of validation."
},
"conceptual_soundness": {
"type": ["string", "null"],
Expand All @@ -566,7 +569,7 @@
},
"outcomes_analysis": {
"type": "boolean",
"description": "Whether model outputs are compared to actual results (back-testing)"
"description": "Whether model outputs are compared to actual results"
},
"drift_detection": {
"type": "boolean",
Expand All @@ -587,7 +590,7 @@
"pii_handling": {
"type": "string",
"enum": ["redact", "encrypt", "prohibit", "allow"],
"description": "'redact': strip PII from outputs. 'encrypt': encrypt at rest. 'prohibit': reject PII input. 'allow': no restrictions."
"description": "How PII is handled"
},
"data_classification": {
"type": "string",
Expand All @@ -608,7 +611,7 @@
},
"lda_search": {
"type": "boolean",
"description": "Whether Less Discriminatory Alternative search is required (CFPB Circular 2022-03)"
"description": "Whether Less Discriminatory Alternative search is required"
}
},
"additionalProperties": false
Expand All @@ -621,7 +624,7 @@
"type": {
"type": "string",
"enum": ["correspondence", "retail", "institutional"],
"description": "'correspondence': <=25 retail investors/30 days. 'retail': >25 retail investors/30 days. 'institutional': institutional investors only."
"description": "Communication type classification"
},
"pre_review_required": {
"type": "boolean",
Expand All @@ -633,7 +636,7 @@
},
"no_misleading": {
"type": "boolean",
"description": "Whether misleading, exaggerated, or promissory statements are prohibited"
"description": "Whether misleading statements are prohibited"
},
"disclosures_required": {
"type": "boolean",
Expand Down Expand Up @@ -661,15 +664,15 @@
},
"subcontractor_assessment": {
"type": "boolean",
"description": "Whether fourth-party (subcontractor) risk has been assessed"
"description": "Whether fourth-party risk has been assessed"
}
},
"additionalProperties": false
},

"segregation_of_duties_config": {
"type": "object",
"description": "Segregation of duties configuration for multi-agent systems. Ensures no single agent has complete control over critical processes.",
"description": "Segregation of duties configuration for multi-agent systems.",
"properties": {
"roles": {
"type": "array",
Expand Down Expand Up @@ -703,12 +706,10 @@
},
"conflicts": {
"type": "array",
"description": "Pairs of role IDs that cannot be held by the same agent (SOD matrix)",
"description": "Pairs of role IDs that cannot be held by the same agent",
"items": {
"type": "array",
"items": {
"type": "string"
},
"items": { "type": "string" },
"minItems": 2,
"maxItems": 2
}
Expand All @@ -718,9 +719,7 @@
"description": "Maps agent names to their assigned roles",
"additionalProperties": {
"type": "array",
"items": {
"type": "string"
},
"items": { "type": "string" },
"minItems": 1
}
},
Expand All @@ -730,13 +729,11 @@
"properties": {
"state": {
"type": "string",
"enum": ["full", "shared", "none"],
"description": "'full': agents cannot access each other's state. 'shared': read-only cross-access. 'none': no isolation."
"enum": ["full", "shared", "none"]
},
"credentials": {
"type": "string",
"enum": ["separate", "shared"],
"description": "'separate': each role has its own credential scope. 'shared': agents share credentials."
"enum": ["separate", "shared"]
}
},
"additionalProperties": false
Expand All @@ -750,7 +747,7 @@
"properties": {
"action": {
"type": "string",
"description": "Action type requiring handoff (e.g., credit_decision, loan_disbursement)"
"description": "Action type requiring handoff"
},
"required_roles": {
"type": "array",
Expand All @@ -775,6 +772,77 @@
}
},
"additionalProperties": false
},

"financial_governance_config": {
"type": "object",
"description": "Runtime financial controls for payment-capable agents. Declarative — enforcement is handled by a pre_tool_use hook calling a compliant financial firewall. A block with no compliant enforcement layer is advisory only.",
"properties": {
"enabled": {
"type": "boolean",
"description": "If false, block is declared but not enforced. Default: false."
},
"firewall": {
"type": "string",
"description": "Named identifier of the financial firewall implementation (e.g. valkurai, stripe-radar, local-script). Not an endpoint — endpoint config belongs in runtime environment config."
},
"spending": {
"type": "object",
"description": "Spending cap and category controls",
"properties": {
"max_per_transaction_cents": {
"type": "integer",
"minimum": 1,
"description": "Hard cap per transaction in smallest currency unit (cents for AUD/USD, pence for GBP). No floats."
},
"max_monthly_cents": {
"type": "integer",
"minimum": 1,
"description": "Cumulative monthly cap in smallest currency unit. Omit to disable."
},
"currency": {
"type": "string",
"description": "Default ISO 4217 currency code (e.g. AUD, USD, GBP)."
},
"allowed_categories": {
"type": "array",
"items": { "type": "string" },
"description": "Permitted spending categories (e.g. software, compute, api_services). Empty array = all categories permitted."
},
"blocked_categories": {
"type": "array",
"items": { "type": "string" },
"description": "Explicitly blocked spending categories. Evaluated before allowed_categories."
}
},
"required": ["max_per_transaction_cents"],
"additionalProperties": false
},
"approval": {
"type": "object",
"description": "Human approval threshold and timeout controls",
"properties": {
"require_above_cents": {
"type": "integer",
"minimum": 0,
"description": "Transactions above this amount require human approval before execution. Set to 0 to require approval on all transactions."
},
"timeout_minutes": {
"type": "integer",
"minimum": 1,
"description": "Minutes before an unanswered approval request times out."
},
"auto_deny_on_timeout": {
"type": "boolean",
"description": "If true, timeout = DENIED. If false, timeout = APPROVED. Recommended default: true."
}
},
"required": ["require_above_cents", "timeout_minutes", "auto_deny_on_timeout"],
"additionalProperties": false
}
},
"required": ["enabled"],
"additionalProperties": false
}
}
}
29 changes: 28 additions & 1 deletion src/adapters/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,38 @@ export function buildComplianceSection(compliance: NonNullable<ReturnType<typeof
if (sod.isolation?.credentials === 'separate') {
constraints.push('- Credentials are segregated per role');
}
if (sod.enforcement === 'strict') {
if (sod.enforcement === 'strict') {
constraints.push('- SOD enforcement is STRICT — violations will block execution');
}
}

// Financial governance constraints
if (c.financial_governance?.enabled) {
const fg = c.financial_governance;
constraints.push('- Financial governance is enforced:');
if (fg.spending?.max_per_transaction_cents) {
constraints.push(` - Maximum per transaction: ${fg.spending.max_per_transaction_cents} cents`);
}
if (fg.spending?.max_monthly_cents) {
constraints.push(` - Maximum monthly spend: ${fg.spending.max_monthly_cents} cents`);
}
if (fg.spending?.allowed_categories && fg.spending.allowed_categories.length > 0) {
constraints.push(` - Allowed categories: ${fg.spending.allowed_categories.join(', ')}`);
}
if (fg.spending?.blocked_categories && fg.spending.blocked_categories.length > 0) {
constraints.push(` - Blocked categories: ${fg.spending.blocked_categories.join(', ')}`);
}
if (fg.approval?.require_above_cents !== undefined) {
constraints.push(` - Human approval required above: ${fg.approval.require_above_cents} cents`);
}
if (fg.approval?.auto_deny_on_timeout) {
constraints.push(' - Unanswered approval requests are automatically DENIED');
}
if (fg.firewall) {
constraints.push(` - Financial firewall: ${fg.firewall}`);
}
}

if (constraints.length === 0) return '';
return `## Compliance Constraints\n\n${constraints.join('\n')}`;
}
Loading