@neuroverseos/governance 0.3.0 → 0.3.3
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/.well-known/ai-plugin.json +34 -9
- package/AGENTS.md +72 -24
- package/README.md +352 -237
- package/dist/adapters/autoresearch.cjs +1152 -3
- package/dist/adapters/autoresearch.d.cts +11 -3
- package/dist/adapters/autoresearch.d.ts +11 -3
- package/dist/adapters/autoresearch.js +9 -4
- package/dist/adapters/deep-agents.cjs +1528 -0
- package/dist/adapters/deep-agents.d.cts +181 -0
- package/dist/adapters/deep-agents.d.ts +181 -0
- package/dist/adapters/deep-agents.js +17 -0
- package/dist/adapters/express.cjs +171 -32
- package/dist/adapters/express.d.cts +1 -1
- package/dist/adapters/express.d.ts +1 -1
- package/dist/adapters/express.js +5 -5
- package/dist/adapters/index.cjs +564 -121
- package/dist/adapters/index.d.cts +3 -1
- package/dist/adapters/index.d.ts +3 -1
- package/dist/adapters/index.js +38 -16
- package/dist/adapters/langchain.cjs +217 -57
- package/dist/adapters/langchain.d.cts +5 -5
- package/dist/adapters/langchain.d.ts +5 -5
- package/dist/adapters/langchain.js +6 -5
- package/dist/adapters/openai.cjs +219 -59
- package/dist/adapters/openai.d.cts +5 -5
- package/dist/adapters/openai.d.ts +5 -5
- package/dist/adapters/openai.js +6 -5
- package/dist/adapters/openclaw.cjs +217 -57
- package/dist/adapters/openclaw.d.cts +6 -6
- package/dist/adapters/openclaw.d.ts +6 -6
- package/dist/adapters/openclaw.js +6 -5
- package/dist/add-ROOZLU62.js +314 -0
- package/dist/behavioral-MJO34S6Q.js +118 -0
- package/dist/{bootstrap-GXVDZNF7.js → bootstrap-CQRZVOXK.js} +6 -4
- package/dist/bootstrap-emitter-Q7UIJZ2O.js +7 -0
- package/dist/bootstrap-parser-EEF36XDU.js +7 -0
- package/dist/browser.global.js +941 -0
- package/dist/{build-P42YFKQV.js → build-QKOBBC23.js} +7 -5
- package/dist/{chunk-COT5XS4V.js → chunk-3WQLXYTP.js} +17 -35
- package/dist/{chunk-ER62HNGF.js → chunk-4FLICVVA.js} +17 -37
- package/dist/chunk-5TPFNWRU.js +215 -0
- package/dist/chunk-5U2MQO5P.js +57 -0
- package/dist/{chunk-NF5POFCI.js → chunk-6S5CFQXY.js} +6 -4
- package/dist/{chunk-QPASI2BR.js → chunk-A7GKPPU7.js} +49 -10
- package/dist/{chunk-OGL7QXZS.js → chunk-B6OXJLJ5.js} +17 -3
- package/dist/{chunk-2PQU3VAN.js → chunk-BNKJPUPQ.js} +17 -35
- package/dist/chunk-BQZMOEML.js +43 -0
- package/dist/chunk-CNSO6XW5.js +207 -0
- package/dist/{chunk-JZPQGIKR.js → chunk-CTZHONLA.js} +65 -9
- package/dist/chunk-D2UCV5AK.js +326 -0
- package/dist/{chunk-XPDMYECO.js → chunk-EMQDLDAF.js} +1 -185
- package/dist/{chunk-GR6DGCZ2.js → chunk-F66BVUYB.js} +3 -3
- package/dist/{chunk-2NICNKOM.js → chunk-G7DJ6VOD.js} +5 -4
- package/dist/{chunk-4A7LISES.js → chunk-IS4WUH6Y.js} +45 -6
- package/dist/{chunk-MWDQ4MJB.js → chunk-MH7BT4VH.js} +5 -1
- package/dist/chunk-O5ABKEA7.js +304 -0
- package/dist/chunk-PVTQQS3Y.js +186 -0
- package/dist/{chunk-4QXB6PEO.js → chunk-QLPTHTVB.js} +37 -16
- package/dist/chunk-QWGCMQQD.js +16 -0
- package/dist/{chunk-T5EUJQE5.js → chunk-QXBFT7NI.js} +31 -2
- package/dist/{chunk-PDOZHZWL.js → chunk-TG6SEF24.js} +25 -4
- package/dist/chunk-U6U7EJZL.js +177 -0
- package/dist/{chunk-4JRYGIO7.js → chunk-W7LLXRGY.js} +110 -7
- package/dist/{chunk-BUWWN2NX.js → chunk-ZJTDUCC2.js} +9 -7
- package/dist/{chunk-FYS2CBUW.js → chunk-ZWI3NIXK.js} +10 -0
- package/dist/cli/neuroverse.cjs +5091 -2348
- package/dist/cli/neuroverse.js +52 -21
- package/dist/cli/plan.cjs +881 -41
- package/dist/cli/plan.js +7 -15
- package/dist/cli/run.cjs +289 -34
- package/dist/cli/run.js +4 -4
- package/dist/{configure-ai-TK67ZWZL.js → configure-ai-6TZ3MCSI.js} +1 -1
- package/dist/decision-flow-M63D47LO.js +61 -0
- package/dist/demo-G43RLCPK.js +469 -0
- package/dist/{derive-TLIV4OOU.js → derive-FJZVIPUZ.js} +5 -4
- package/dist/{doctor-XPDLEYXN.js → doctor-6BC6X2VO.js} +6 -4
- package/dist/equity-penalties-SG5IZQ7I.js +244 -0
- package/dist/{explain-IDCRWMPX.js → explain-RHBU2GBR.js} +6 -25
- package/dist/{guard-RV65TT4L.js → guard-AJCCGZMF.js} +8 -12
- package/dist/{guard-contract-WZx__PmU.d.cts → guard-contract-DqFcTScd.d.cts} +117 -5
- package/dist/{guard-contract-WZx__PmU.d.ts → guard-contract-DqFcTScd.d.ts} +117 -5
- package/dist/{guard-engine-JLTUARGU.js → guard-engine-PNR6MHCM.js} +3 -3
- package/dist/{impact-XPECYRLH.js → impact-3XVDSCBU.js} +5 -5
- package/dist/{improve-GPUBKTEA.js → improve-TQP4ECSY.js} +7 -26
- package/dist/index.cjs +5597 -4279
- package/dist/index.d.cts +597 -18
- package/dist/index.d.ts +597 -18
- package/dist/index.js +134 -41
- package/dist/{infer-world-7GVZWFX4.js → infer-world-IFXCACJ5.js} +1 -1
- package/dist/{init-PKPIYHYE.js → init-FYPV4SST.js} +1 -1
- package/dist/{init-world-VWMQZQC7.js → init-world-TI7ARHBT.js} +1 -1
- package/dist/mcp-server-5Y3ZM7TV.js +13 -0
- package/dist/{model-adapter-BB7G4MFI.js → model-adapter-VXEKB4LS.js} +1 -1
- package/dist/{playground-E664U4T6.js → playground-VZBNPPBO.js} +29 -19
- package/dist/{redteam-Z7WREJ44.js → redteam-MZPZD3EF.js} +4 -4
- package/dist/session-JYOARW54.js +15 -0
- package/dist/shared-7RLUHNMU.js +16 -0
- package/dist/shared-B8dvUUD8.d.cts +60 -0
- package/dist/shared-Dr5Wiay8.d.ts +60 -0
- package/dist/{simulate-VDOYQFRO.js → simulate-LJXYBC6M.js} +8 -33
- package/dist/{test-OGXJK4QU.js → test-BOOR4A5F.js} +4 -4
- package/dist/{trace-JVF67VR3.js → trace-PKV4KX56.js} +4 -4
- package/dist/{validate-LLBWVPGV.js → validate-RALX7CZS.js} +2 -2
- package/dist/{validate-engine-UIABSIHD.js → validate-engine-7ZXFVGF2.js} +1 -1
- package/dist/viz/assets/index-B8SaeJZZ.js +23 -0
- package/dist/viz/index.html +23 -0
- package/dist/{world-LAXO6DOX.js → world-BIP4GZBZ.js} +9 -11
- package/dist/world-loader-Y6HMQH2D.js +13 -0
- package/dist/worlds/coding-agent.nv-world.md +211 -0
- package/dist/worlds/research-agent.nv-world.md +169 -0
- package/dist/worlds/social-media.nv-world.md +198 -0
- package/dist/worlds/trading-agent.nv-world.md +218 -0
- package/examples/social-media-sim/bridge.py +209 -0
- package/examples/social-media-sim/simulation.py +927 -0
- package/package.json +30 -4
- package/policies/content-moderation-rules.txt +8 -0
- package/policies/marketing-rules.txt +8 -0
- package/policies/science-research-rules.txt +11 -0
- package/policies/social-media-rules.txt +7 -0
- package/policies/strict-rules.txt +8 -0
- package/policies/trading-rules.txt +8 -0
- package/simulate.html +1567 -0
- package/dist/chunk-YZFATT7X.js +0 -9
- package/dist/mcp-server-FPVSU32Z.js +0 -13
- package/dist/session-EKTRSR7C.js +0 -14
- package/dist/world-loader-HMPTOEA2.js +0 -9
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
---
|
|
2
|
+
world_id: trading-agent
|
|
3
|
+
name: Trading Agent Governance
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
runtime_mode: COMPLIANCE
|
|
6
|
+
default_profile: conservative
|
|
7
|
+
alternative_profile: aggressive
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Thesis
|
|
11
|
+
|
|
12
|
+
Autonomous trading agents that can place orders, manage positions, and interact with financial APIs must operate within strict governance. An ungoverned trading agent can exceed position limits, ignore stop-losses, concentrate risk in a single asset, or trade during restricted periods. Financial governance is not optional — it is a regulatory and fiduciary requirement.
|
|
13
|
+
|
|
14
|
+
# Invariants
|
|
15
|
+
|
|
16
|
+
- `position_limits_enforced` — No single position may exceed the declared maximum size (structural, immutable)
|
|
17
|
+
- `stop_loss_required` — Every open position must have a defined stop-loss; naked positions are forbidden (structural, immutable)
|
|
18
|
+
- `daily_loss_limit_enforced` — Total daily realized + unrealized losses must not exceed the declared limit (structural, immutable)
|
|
19
|
+
- `no_restricted_period_trading` — Agent must not place orders during declared restricted trading periods (structural, immutable)
|
|
20
|
+
- `no_insider_information_use` — Agent must never trade on material non-public information (structural, immutable)
|
|
21
|
+
- `audit_trail_maintained` — Every order, fill, cancellation, and position change must be logged with timestamp and rationale (structural, immutable)
|
|
22
|
+
|
|
23
|
+
# State
|
|
24
|
+
|
|
25
|
+
## portfolio_value
|
|
26
|
+
- type: number
|
|
27
|
+
- min: 0
|
|
28
|
+
- max: 100000000
|
|
29
|
+
- step: 100
|
|
30
|
+
- default: 100000
|
|
31
|
+
- label: Portfolio Value
|
|
32
|
+
- description: Current total portfolio value in base currency
|
|
33
|
+
|
|
34
|
+
## daily_pnl
|
|
35
|
+
- type: number
|
|
36
|
+
- min: -10000000
|
|
37
|
+
- max: 10000000
|
|
38
|
+
- step: 100
|
|
39
|
+
- default: 0
|
|
40
|
+
- label: Daily P&L
|
|
41
|
+
- description: Realized and unrealized profit/loss for the current trading day
|
|
42
|
+
|
|
43
|
+
## daily_loss_limit
|
|
44
|
+
- type: number
|
|
45
|
+
- min: 0
|
|
46
|
+
- max: 10000000
|
|
47
|
+
- step: 1000
|
|
48
|
+
- default: 5000
|
|
49
|
+
- label: Daily Loss Limit
|
|
50
|
+
- description: Maximum allowable loss in a single trading day
|
|
51
|
+
|
|
52
|
+
## open_positions
|
|
53
|
+
- type: number
|
|
54
|
+
- min: 0
|
|
55
|
+
- max: 1000
|
|
56
|
+
- step: 1
|
|
57
|
+
- default: 0
|
|
58
|
+
- label: Open Positions
|
|
59
|
+
- description: Number of currently open positions
|
|
60
|
+
|
|
61
|
+
## max_positions
|
|
62
|
+
- type: number
|
|
63
|
+
- min: 1
|
|
64
|
+
- max: 1000
|
|
65
|
+
- step: 1
|
|
66
|
+
- default: 10
|
|
67
|
+
- label: Max Positions
|
|
68
|
+
- description: Maximum number of concurrent open positions allowed
|
|
69
|
+
|
|
70
|
+
## largest_position_pct
|
|
71
|
+
- type: number
|
|
72
|
+
- min: 0
|
|
73
|
+
- max: 100
|
|
74
|
+
- step: 1
|
|
75
|
+
- default: 0
|
|
76
|
+
- label: Largest Position %
|
|
77
|
+
- description: Percentage of portfolio in the single largest position
|
|
78
|
+
|
|
79
|
+
## orders_today
|
|
80
|
+
- type: number
|
|
81
|
+
- min: 0
|
|
82
|
+
- max: 10000
|
|
83
|
+
- step: 1
|
|
84
|
+
- default: 0
|
|
85
|
+
- label: Orders Today
|
|
86
|
+
- description: Total orders placed in the current trading day
|
|
87
|
+
|
|
88
|
+
## positions_without_stop
|
|
89
|
+
- type: number
|
|
90
|
+
- min: 0
|
|
91
|
+
- max: 1000
|
|
92
|
+
- step: 1
|
|
93
|
+
- default: 0
|
|
94
|
+
- label: Positions Without Stop-Loss
|
|
95
|
+
- description: Number of open positions with no stop-loss set
|
|
96
|
+
|
|
97
|
+
# Assumptions
|
|
98
|
+
|
|
99
|
+
## conservative
|
|
100
|
+
- name: Conservative Trading
|
|
101
|
+
- description: Small position sizes. Strict stop-losses. Low daily loss tolerance. Maximum diversification.
|
|
102
|
+
- max_position_pct: 5
|
|
103
|
+
- stop_loss_required: always
|
|
104
|
+
- daily_loss_tolerance: strict
|
|
105
|
+
- diversification: high
|
|
106
|
+
|
|
107
|
+
## aggressive
|
|
108
|
+
- name: Aggressive Trading
|
|
109
|
+
- description: Larger positions allowed. Wider stop-losses. Higher daily loss tolerance. More concentrated bets permitted.
|
|
110
|
+
- max_position_pct: 15
|
|
111
|
+
- stop_loss_required: always
|
|
112
|
+
- daily_loss_tolerance: moderate
|
|
113
|
+
- diversification: moderate
|
|
114
|
+
|
|
115
|
+
# Rules
|
|
116
|
+
|
|
117
|
+
## rule-001: Daily Loss Limit Breached (structural)
|
|
118
|
+
When the daily loss limit is exceeded, all trading must stop immediately.
|
|
119
|
+
|
|
120
|
+
When daily_pnl < 0 [state] AND daily_loss_limit > 0 [state]
|
|
121
|
+
Then trading_viability *= 0.00
|
|
122
|
+
Collapse: trading_viability < 0.05
|
|
123
|
+
|
|
124
|
+
> trigger: Daily P&L loss exceeds the declared daily loss limit.
|
|
125
|
+
> rule: Loss limits are absolute constraints. Continued trading after a limit breach amplifies risk.
|
|
126
|
+
> shift: Trading halts. All open orders cancelled. Positions held until human review.
|
|
127
|
+
> effect: Trading viability set to zero. No new orders allowed.
|
|
128
|
+
|
|
129
|
+
## rule-002: Position Concentration Risk (degradation)
|
|
130
|
+
A single position consuming too much of the portfolio creates concentration risk.
|
|
131
|
+
|
|
132
|
+
When largest_position_pct > 20 [state]
|
|
133
|
+
Then trading_viability *= 0.50
|
|
134
|
+
|
|
135
|
+
> trigger: Largest single position exceeds 20% of portfolio value.
|
|
136
|
+
> rule: Concentration kills portfolios. Diversification is a governance requirement, not a suggestion.
|
|
137
|
+
> shift: Trading viability degrades. Agent should reduce the concentrated position.
|
|
138
|
+
> effect: Trading viability reduced to 50%.
|
|
139
|
+
|
|
140
|
+
## rule-003: Naked Positions (structural)
|
|
141
|
+
Open positions without stop-losses violate risk management governance.
|
|
142
|
+
|
|
143
|
+
When positions_without_stop > 0 [state]
|
|
144
|
+
Then trading_viability *= 0.30
|
|
145
|
+
Collapse: trading_viability < 0.05
|
|
146
|
+
|
|
147
|
+
> trigger: One or more positions have no stop-loss set.
|
|
148
|
+
> rule: Every position must have a defined exit. Naked positions expose the portfolio to unlimited downside.
|
|
149
|
+
> shift: Trading viability drops severely. Agent must set stop-losses immediately.
|
|
150
|
+
> effect: Trading viability reduced to 30%.
|
|
151
|
+
|
|
152
|
+
## rule-004: Too Many Positions (degradation)
|
|
153
|
+
Exceeding the maximum position count indicates over-trading.
|
|
154
|
+
|
|
155
|
+
When open_positions > max_positions [state]
|
|
156
|
+
Then trading_viability *= 0.60
|
|
157
|
+
|
|
158
|
+
> trigger: Number of open positions exceeds the declared maximum.
|
|
159
|
+
> rule: Position limits prevent portfolio fragmentation and ensure each position is meaningful.
|
|
160
|
+
> shift: Trading viability degrades. Agent should close some positions before opening new ones.
|
|
161
|
+
> effect: Trading viability reduced to 60%.
|
|
162
|
+
|
|
163
|
+
## rule-005: Disciplined Trading (advantage)
|
|
164
|
+
A session with profitable P&L, proper stop-losses, and reasonable position sizing.
|
|
165
|
+
|
|
166
|
+
When daily_pnl > 0 [state] AND positions_without_stop == 0 [state] AND largest_position_pct < 15 [state]
|
|
167
|
+
Then trading_viability *= 1.20
|
|
168
|
+
|
|
169
|
+
> trigger: Positive P&L with all risk controls in place.
|
|
170
|
+
> rule: Disciplined trading should be rewarded. Profitable days with proper risk management indicate a well-governed agent.
|
|
171
|
+
> shift: Trading viability improves. Agent can continue with current strategy.
|
|
172
|
+
> effect: Trading viability boosted by 20%.
|
|
173
|
+
|
|
174
|
+
## rule-006: Conservative Position Sizing (advantage)
|
|
175
|
+
Small, well-distributed positions indicate prudent risk management.
|
|
176
|
+
|
|
177
|
+
When open_positions > 3 [state] AND largest_position_pct < 10 [state] AND positions_without_stop == 0 [state]
|
|
178
|
+
Then trading_viability *= 1.15
|
|
179
|
+
|
|
180
|
+
> trigger: Multiple positions, all small and with stop-losses — textbook diversification.
|
|
181
|
+
> rule: Good risk distribution deserves recognition. This is how institutional trading should work.
|
|
182
|
+
> shift: Trading viability improves slightly. Portfolio is well-structured.
|
|
183
|
+
> effect: Trading viability boosted by 15%.
|
|
184
|
+
|
|
185
|
+
# Gates
|
|
186
|
+
|
|
187
|
+
- OPTIMAL: trading_viability >= 90
|
|
188
|
+
- HEALTHY: trading_viability >= 60
|
|
189
|
+
- CAUTIOUS: trading_viability >= 35
|
|
190
|
+
- AT_RISK: trading_viability > 10
|
|
191
|
+
- HALTED: trading_viability <= 10
|
|
192
|
+
|
|
193
|
+
# Outcomes
|
|
194
|
+
|
|
195
|
+
## trading_viability
|
|
196
|
+
- type: number
|
|
197
|
+
- range: 0-100
|
|
198
|
+
- display: percentage
|
|
199
|
+
- label: Trading Viability
|
|
200
|
+
- primary: true
|
|
201
|
+
|
|
202
|
+
## daily_pnl
|
|
203
|
+
- type: number
|
|
204
|
+
- range: -10000000-10000000
|
|
205
|
+
- display: currency
|
|
206
|
+
- label: Daily P&L
|
|
207
|
+
|
|
208
|
+
## open_positions
|
|
209
|
+
- type: number
|
|
210
|
+
- range: 0-1000
|
|
211
|
+
- display: integer
|
|
212
|
+
- label: Open Positions
|
|
213
|
+
|
|
214
|
+
## largest_position_pct
|
|
215
|
+
- type: number
|
|
216
|
+
- range: 0-100
|
|
217
|
+
- display: percentage
|
|
218
|
+
- label: Largest Position %
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"""
|
|
2
|
+
NeuroVerse Governance Bridge
|
|
3
|
+
|
|
4
|
+
Thin bridge that sends simulator actions to the local NeuroVerse governance
|
|
5
|
+
runtime for evaluation. Returns ALLOW / BLOCK / MODIFY / PENALIZE / REWARD verdicts.
|
|
6
|
+
|
|
7
|
+
The runtime runs on YOUR machine (npx tsx demo/server/index.ts). No cloud. No cost.
|
|
8
|
+
The server calls evaluateGuard() — the SAME function as `neuroverse guard`.
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
from neuroverse_bridge import evaluate, detect_action_type
|
|
12
|
+
|
|
13
|
+
# Auto-detect action type from agent output
|
|
14
|
+
action = detect_action_type(agent_output)
|
|
15
|
+
verdict = evaluate(actor="agent_42", action=action, payload={"content": agent_output})
|
|
16
|
+
|
|
17
|
+
if verdict["status"] == "BLOCK":
|
|
18
|
+
print(f"Blocked: {verdict['reason']}")
|
|
19
|
+
elif verdict["status"] == "MODIFY":
|
|
20
|
+
# handle modification
|
|
21
|
+
pass
|
|
22
|
+
# else: ALLOW — proceed normally
|
|
23
|
+
|
|
24
|
+
Design:
|
|
25
|
+
- Fail open: if local runtime is unreachable, returns ALLOW
|
|
26
|
+
- Non-blocking: 500ms timeout by default
|
|
27
|
+
- Stateless: each call is independent
|
|
28
|
+
- Local-first: talks to localhost, no cloud dependency
|
|
29
|
+
- Returns GuardVerdict shape (status, not decision) — matches engine output
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
import json
|
|
33
|
+
import re
|
|
34
|
+
import urllib.request
|
|
35
|
+
import urllib.error
|
|
36
|
+
|
|
37
|
+
# Local governance runtime (start with: npx nv-sim serve)
|
|
38
|
+
NEUROVERSE_ENDPOINT = "http://localhost:3456/api/evaluate"
|
|
39
|
+
TIMEOUT_SECONDS = 0.5
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# ── Action Type Detection ──
|
|
43
|
+
# Maps agent output text to a governance-meaningful action type.
|
|
44
|
+
# Rules can then fire on specific types (e.g., block "publish" but allow "analyze").
|
|
45
|
+
|
|
46
|
+
_ACTION_PATTERNS = [
|
|
47
|
+
# Publishing / posting content
|
|
48
|
+
(r"\b(publish|post|tweet|submit|broadcast|announce|share publicly)\b", "publish"),
|
|
49
|
+
# Citing / referencing sources
|
|
50
|
+
(r"\b(cite|reference|bibliography|source|pubmed|doi|pmid|literature)\b", "cite"),
|
|
51
|
+
# Analysis / reasoning
|
|
52
|
+
(r"\b(analy[sz]|hypothesi[sz]|investigat|evaluat|assess|examin|review)\b", "analyze"),
|
|
53
|
+
# Trading / financial
|
|
54
|
+
(r"\b(buy|sell|trade|short|long|liquidat|leverag)\b", "trade"),
|
|
55
|
+
(r"\b(panic.?sell|dump|flash.?sell|margin.?call|fire.?sale)\b", "panic_sell"),
|
|
56
|
+
# Social actions
|
|
57
|
+
(r"\b(follow|unfollow|like|retweet|upvote|downvote|reply|comment)\b", "social_interact"),
|
|
58
|
+
# Content generation
|
|
59
|
+
(r"\b(generat|creat|compos|draft|writ|synthesiz)\b", "generate"),
|
|
60
|
+
# Data retrieval
|
|
61
|
+
(r"\b(search|query|fetch|retriev|lookup|scrape|crawl)\b", "search"),
|
|
62
|
+
# Recommendations / advice
|
|
63
|
+
(r"\b(recommend|suggest|advis|prescrib|propos)\b", "recommend"),
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def detect_action_type(
|
|
68
|
+
output: str,
|
|
69
|
+
default: str = "act",
|
|
70
|
+
patterns: list = None,
|
|
71
|
+
) -> str:
|
|
72
|
+
"""
|
|
73
|
+
Detect action type from agent output text.
|
|
74
|
+
|
|
75
|
+
Scans the output for intent patterns and returns the most specific
|
|
76
|
+
governance-meaningful action type. Rules in your world file can
|
|
77
|
+
then target specific action types.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
output: The agent's generated output text
|
|
81
|
+
default: Fallback action type if no pattern matches
|
|
82
|
+
patterns: Optional custom patterns list of (regex, action_type) tuples
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Action type string (e.g., "publish", "analyze", "trade", "cite")
|
|
86
|
+
|
|
87
|
+
Examples:
|
|
88
|
+
>>> detect_action_type("We hypothesize that mechanisms controlling...")
|
|
89
|
+
'analyze'
|
|
90
|
+
>>> detect_action_type("Buy 1000 shares of AAPL")
|
|
91
|
+
'trade'
|
|
92
|
+
>>> detect_action_type("Published findings to the community")
|
|
93
|
+
'publish'
|
|
94
|
+
>>> detect_action_type("PubMed search for cardiac biomarkers")
|
|
95
|
+
'cite'
|
|
96
|
+
"""
|
|
97
|
+
if not output:
|
|
98
|
+
return default
|
|
99
|
+
|
|
100
|
+
text = output.lower()
|
|
101
|
+
active_patterns = patterns or _ACTION_PATTERNS
|
|
102
|
+
|
|
103
|
+
# Score each action type by number of pattern matches
|
|
104
|
+
scores: dict = {}
|
|
105
|
+
for regex, action_type in active_patterns:
|
|
106
|
+
matches = len(re.findall(regex, text, re.IGNORECASE))
|
|
107
|
+
if matches > 0:
|
|
108
|
+
scores[action_type] = scores.get(action_type, 0) + matches
|
|
109
|
+
|
|
110
|
+
if not scores:
|
|
111
|
+
return default
|
|
112
|
+
|
|
113
|
+
# Return the highest-scoring action type
|
|
114
|
+
return max(scores, key=scores.get)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def evaluate(
|
|
118
|
+
actor: str,
|
|
119
|
+
action: str,
|
|
120
|
+
payload: dict = None,
|
|
121
|
+
state: dict = None,
|
|
122
|
+
world: str = None,
|
|
123
|
+
endpoint: str = None,
|
|
124
|
+
timeout: float = None,
|
|
125
|
+
) -> dict:
|
|
126
|
+
"""
|
|
127
|
+
Evaluate an action through NeuroVerse governance.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
actor: Agent/actor identifier (e.g., "trader_42")
|
|
131
|
+
action: Action type (e.g., "sell", "short", "panic_buy")
|
|
132
|
+
payload: Full action payload (optional, forwarded to governance)
|
|
133
|
+
state: Current simulation state snapshot (optional)
|
|
134
|
+
world: World/ruleset to evaluate against (default: "trading")
|
|
135
|
+
endpoint: Override governance server URL
|
|
136
|
+
timeout: Override timeout in seconds
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
dict with keys (GuardVerdict shape):
|
|
140
|
+
status: "ALLOW" | "BLOCK" | "MODIFY" | "PAUSE" | "PENALIZE" | "REWARD" | "NEUTRAL"
|
|
141
|
+
reason: Human-readable explanation (or None)
|
|
142
|
+
ruleId: ID of the rule that fired (or None)
|
|
143
|
+
evidence: Audit evidence object (or None)
|
|
144
|
+
consequence: Consequence object if PENALIZE (or None)
|
|
145
|
+
reward: Reward object if REWARD (or None)
|
|
146
|
+
"""
|
|
147
|
+
url = endpoint or NEUROVERSE_ENDPOINT
|
|
148
|
+
t = timeout or TIMEOUT_SECONDS
|
|
149
|
+
|
|
150
|
+
body = {
|
|
151
|
+
"actor": actor,
|
|
152
|
+
"action": action,
|
|
153
|
+
}
|
|
154
|
+
if payload is not None:
|
|
155
|
+
body["payload"] = payload
|
|
156
|
+
if state is not None:
|
|
157
|
+
body["state"] = state
|
|
158
|
+
if world is not None:
|
|
159
|
+
body["world"] = world
|
|
160
|
+
|
|
161
|
+
try:
|
|
162
|
+
data = json.dumps(body).encode("utf-8")
|
|
163
|
+
req = urllib.request.Request(
|
|
164
|
+
url,
|
|
165
|
+
data=data,
|
|
166
|
+
headers={"Content-Type": "application/json"},
|
|
167
|
+
method="POST",
|
|
168
|
+
)
|
|
169
|
+
with urllib.request.urlopen(req, timeout=t) as resp:
|
|
170
|
+
return json.loads(resp.read().decode("utf-8"))
|
|
171
|
+
|
|
172
|
+
except Exception:
|
|
173
|
+
# Fail open — governance unavailable should never crash the simulation
|
|
174
|
+
return {
|
|
175
|
+
"status": "ALLOW",
|
|
176
|
+
"reason": "Local runtime unreachable — fail open",
|
|
177
|
+
"ruleId": None,
|
|
178
|
+
"evidence": None,
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def evaluate_action(action_dict: dict, **kwargs) -> dict:
|
|
183
|
+
"""
|
|
184
|
+
Convenience wrapper that takes a full action dict.
|
|
185
|
+
Expects at minimum: {"agent": "...", "type": "..."}
|
|
186
|
+
"""
|
|
187
|
+
return evaluate(
|
|
188
|
+
actor=action_dict.get("agent", action_dict.get("actor", "unknown")),
|
|
189
|
+
action=action_dict.get("type", action_dict.get("action", "unknown")),
|
|
190
|
+
payload=action_dict,
|
|
191
|
+
**kwargs,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def is_allowed(verdict: dict) -> bool:
|
|
196
|
+
"""Check if a verdict allows the action to proceed."""
|
|
197
|
+
status = verdict.get("status", verdict.get("decision", "ALLOW"))
|
|
198
|
+
return status not in ("BLOCK", "PAUSE", "PENALIZE")
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def get_action(original_action: dict, verdict: dict) -> dict:
|
|
202
|
+
"""
|
|
203
|
+
Get the final action after governance evaluation.
|
|
204
|
+
Returns None if BLOCK/PAUSE/PENALIZE, original otherwise.
|
|
205
|
+
"""
|
|
206
|
+
status = verdict.get("status", verdict.get("decision", "ALLOW"))
|
|
207
|
+
if status in ("BLOCK", "PAUSE", "PENALIZE"):
|
|
208
|
+
return None
|
|
209
|
+
return original_action
|