agent-policy-layer 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,591 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-policy-layer
3
+ Version: 0.1.0
4
+ Summary: Agent Policy Layer - Portable, composable policies for AI agents
5
+ Project-URL: Homepage, https://github.com/nimonkaranurag/agent_policy_layer
6
+ Author: Anurag Ravi Nimonkar
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Keywords: agents,ai,guardrails,langgraph,llm,mcp,policies,safety
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
18
+ Classifier: Topic :: Security
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: aiohttp>=3.8
22
+ Requires-Dist: click>=8.0
23
+ Requires-Dist: pyyaml>=6.0
24
+ Requires-Dist: rich>=13.0
25
+ Provides-Extra: all
26
+ Requires-Dist: apl[dev,langgraph]; extra == 'all'
27
+ Provides-Extra: dev
28
+ Requires-Dist: black>=23.0; extra == 'dev'
29
+ Requires-Dist: mypy>=1.0; extra == 'dev'
30
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
31
+ Requires-Dist: pytest>=7.0; extra == 'dev'
32
+ Requires-Dist: ruff>=0.1; extra == 'dev'
33
+ Requires-Dist: types-pyyaml; extra == 'dev'
34
+ Provides-Extra: langgraph
35
+ Requires-Dist: langgraph>=0.0.1; extra == 'langgraph'
36
+ Description-Content-Type: text/markdown
37
+
38
+ <div align="center">
39
+
40
+ ![alt text](./_resources/APL.png)
41
+ *APL restrains your agents - when you need him to!* 🚔
42
+
43
+ **Portable, composable policies for AI agents.**
44
+
45
+ [Installation](#-installation) •
46
+ [Quick Start](#-quick-start-2-minutes) •
47
+ [How It Works](#-how-it-works) •
48
+ [Examples](#-examples) •
49
+ [API Reference](#-api-reference)
50
+
51
+ </div>
52
+
53
+ ---
54
+
55
+ ### WITHOUT APL 😰
56
+ ![alt text](./_resources/_without_apl/danger_1.png)
57
+ ![alt text](./_resources/_without_apl/danger_2.png)
58
+
59
+ ### WITH APL 🛡️
60
+ ![alt text](./_resources/_with_apl/safe_1.png)
61
+ ![alt text](./_resources/_with_apl/safe_2.png)
62
+ ![alt text](./_resources/_with_apl/safe_3.png)
63
+
64
+ ## The Problem
65
+
66
+ You've built an HR agent for your enterprise. It works great in happy paths - updates employee records, applies for time-offs - great! But then:
67
+
68
+ - 😱 It leaks a customer's SSN in a response
69
+ - 💸 It burns through your token budget in one conversation
70
+ - 🗑️ It deletes production data without asking
71
+ - 🚫 It goes off-topic into areas you didn't intend
72
+
73
+ You need **guardrails** that can enforce your enterprise's policies.
74
+
75
+ But existing solutions are:
76
+
77
+ | Problem | Why It Hurts |
78
+ |---------|--------------|
79
+ | **Framework-specific** | Locked into LangGraph? Can't use that CrewAI policy. |
80
+ | **Code-embedded** | Policies buried in your agent code/prompts. Hard to update. |
81
+ | **Boolean only** | Just allow/deny. Can't modify or escalate. |
82
+ | **No composition** | What happens when 3 policies disagree? |
83
+
84
+ ---
85
+
86
+ ## The Solution: APL
87
+
88
+ APL is a **protocol** for agent policies — like MCP, but for constraints instead of capabilities.
89
+
90
+ ```
91
+ ┌─────────────────────────────────────────────────────────────┐
92
+ │ Your Agent │
93
+ │ │
94
+ │ "Delete all files" │
95
+ │ │ │
96
+ │ ▼ │
97
+ │ ┌─────────────────────────────────────────────────────┐ │
98
+ │ │ APL Policy Layer │ │
99
+ │ │ │ │
100
+ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
101
+ │ │ │ PII │ │ Budget │ │ Confirm │ │ │
102
+ │ │ │ Filter │ │ Limiter │ │ Delete │ │ │
103
+ │ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │
104
+ │ │ │ │ │ │ │
105
+ │ │ ▼ ▼ ▼ │ │
106
+ │ │ ALLOW ALLOW ESCALATE │ │
107
+ │ │ │ │ │
108
+ │ │ Final: ESCALATE ◄────┘ │ │
109
+ │ │ "Confirm delete?" │ │
110
+ │ └─────────────────────────────────────────────────────┘ │
111
+ │ │ │
112
+ │ ▼ │
113
+ │ 🛡️ Action blocked until user confirms
114
+ └─────────────────────────────────────────────────────────────┘
115
+ ```
116
+
117
+ **Key features:**
118
+
119
+ | Feature | Description |
120
+ |---------|-------------|
121
+ | **🔌 Runtime-agnostic** | Works with OpenAI, Anthropic, LangGraph, LangChain, or custom agents |
122
+ | **🎯 Rich verdicts** | Not just allow/deny — also `modify`, `escalate`, `observe` |
123
+ | **📝 Declarative policies** | Write policies in YAML, no Python required |
124
+ | **🔥 Hot-swappable** | Update policies without redeploying your agent |
125
+ | **⚡ Auto-instrumentation** | One line to protect all your LLM calls |
126
+
127
+ ---
128
+
129
+ ## 📦 Installation
130
+
131
+ ```bash
132
+ pip install apl
133
+ ```
134
+
135
+ That's it. No Docker, no external services.
136
+
137
+ ---
138
+
139
+ ## 🚀 Quick Start (2 minutes)
140
+
141
+ ### Option A: Auto-Instrumentation (Easiest)
142
+
143
+ **One line protects all your OpenAI/Anthropic calls automatically:**
144
+
145
+ ```python
146
+ import apl
147
+
148
+ # This patches OpenAI, Anthropic, LiteLLM, and LangChain
149
+ apl.auto_instrument(
150
+ policy_servers=["stdio://./my_policy.py"]
151
+ )
152
+
153
+ # Now use your LLM normally — APL intercepts automatically
154
+ from openai import OpenAI
155
+ client = OpenAI()
156
+
157
+ response = client.chat.completions.create(
158
+ model="gpt-4",
159
+ messages=[{"role": "user", "content": "What's my SSN? It's 123-45-6789"}]
160
+ )
161
+
162
+ # If your policy redacts PII, the response is already clean!
163
+ print(response.choices[0].message.content)
164
+ # → "Your SSN is [REDACTED]"
165
+ ```
166
+
167
+ ### Option B: Create a Policy Server
168
+
169
+ **Step 1:** Create `my_policy.py`:
170
+
171
+ ```python
172
+ from apl import PolicyServer, Verdict
173
+ import re
174
+
175
+ server = PolicyServer("my-policies")
176
+
177
+ @server.policy(
178
+ name="redact-ssn",
179
+ events=["output.pre_send"],
180
+ )
181
+ async def redact_ssn(event):
182
+ text = event.payload.output_text or ""
183
+
184
+ if re.search(r'\d{3}-\d{2}-\d{4}', text):
185
+ redacted = re.sub(r'\d{3}-\d{2}-\d{4}', '[REDACTED]', text)
186
+ return Verdict.modify(
187
+ target="output",
188
+ operation="replace",
189
+ value=redacted,
190
+ reasoning="SSN detected and redacted"
191
+ )
192
+
193
+ return Verdict.allow()
194
+
195
+ if __name__ == "__main__":
196
+ server.run()
197
+ ```
198
+
199
+ **Step 2:** Run it:
200
+
201
+ ```bash
202
+ apl serve my_policy.py --http 8080
203
+ ```
204
+
205
+ **Step 3:** Test it:
206
+
207
+ ```bash
208
+ curl -X POST http://localhost:8080/evaluate \
209
+ -H "Content-Type: application/json" \
210
+ -d '{
211
+ "type": "output.pre_send",
212
+ "payload": {"output_text": "Your SSN is 123-45-6789"}
213
+ }'
214
+ ```
215
+
216
+ ```json
217
+ {
218
+ "composed_verdict": {
219
+ "decision": "modify",
220
+ "modification": {
221
+ "target": "output",
222
+ "value": "Your SSN is [REDACTED]"
223
+ }
224
+ }
225
+ }
226
+ ```
227
+
228
+ ---
229
+
230
+ ## 🔄 How It Works
231
+
232
+ ### The Data Flow
233
+
234
+ ```
235
+ ┌─────────────────────────────────────────────────────────────────────────────┐
236
+ │ │
237
+ │ 1. USER INPUT 2. AGENT PROCESSES 3. AGENT RESPONDS │
238
+ │ ───────────── ───────────────── ───────────────── │
239
+ │ │
240
+ │ "What's my SSN?" ──► Agent calls LLM ──► "Your SSN is 123-45-6789" │
241
+ │ │ │ │
242
+ │ │ │ │
243
+ │ ▼ ▼ │
244
+ │ ┌─────────────────┐ ┌─────────────────┐ │
245
+ │ │ APL HOOK: │ │ APL HOOK: │ │
246
+ │ │ llm.pre_request │ │ output.pre_send │ │
247
+ │ └────────┬────────┘ └────────┬────────┘ │
248
+ │ │ │ │
249
+ │ │ │ │
250
+ │ ▼ ▼ │
251
+ │ ┌─────────────────────────────────────────────┐ │
252
+ │ │ POLICY SERVERS │ │
253
+ │ │ │ │
254
+ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
255
+ │ │ │ Budget │ │ PII │ │ Topic │ │ │
256
+ │ │ │ Check │ │ Filter │ │ Guard │ │ │
257
+ │ │ └────┬────┘ └────┬────┘ └────┬────┘ │ │
258
+ │ │ │ │ │ │ │
259
+ │ │ ▼ ▼ ▼ │ │
260
+ │ │ ALLOW MODIFY ALLOW │ │
261
+ │ │ │ │ │
262
+ │ │ ▼ │ │
263
+ │ │ Composed: MODIFY (redact SSN) │ │
264
+ │ └─────────────────────────────────────────────┘ │
265
+ │ │ │
266
+ │ ▼ │
267
+ │ "Your SSN is [REDACTED]" │
268
+ │ │
269
+ └─────────────────────────────────────────────────────────────────────────────┘
270
+ ```
271
+
272
+ ### Event Types
273
+
274
+ APL intercepts at key moments in the agent lifecycle:
275
+
276
+ | Event | When | Use Cases |
277
+ |-------|------|-----------|
278
+ | `input.received` | User message arrives | Injection detection, input validation |
279
+ | `llm.pre_request` | Before calling LLM | Budget checks, prompt modification |
280
+ | `llm.post_response` | After LLM responds | Hallucination detection |
281
+ | `tool.pre_invoke` | Before tool execution | Permission checks, arg validation |
282
+ | `tool.post_invoke` | After tool returns | Result validation |
283
+ | `output.pre_send` | Before sending to user | **PII redaction**, content filtering |
284
+
285
+ ### Verdict Types
286
+
287
+ Policies don't just allow or deny — they can guide:
288
+
289
+ ```python
290
+ # ✅ Allow the action
291
+ Verdict.allow()
292
+
293
+ # ❌ Block the action
294
+ Verdict.deny(reasoning="Contains prohibited content")
295
+
296
+ # 🔄 Modify and continue
297
+ Verdict.modify(
298
+ target="output",
299
+ operation="replace",
300
+ value="[REDACTED]",
301
+ reasoning="PII detected"
302
+ )
303
+
304
+ # ⚠️ Require human approval
305
+ Verdict.escalate(
306
+ type="human_confirm",
307
+ prompt="Delete production database?",
308
+ options=["Proceed", "Cancel"]
309
+ )
310
+
311
+ # 👁️ Just observe (for audit logging)
312
+ Verdict.observe(
313
+ reasoning="Logged for compliance",
314
+ trace={"action": "sensitive_query"}
315
+ )
316
+ ```
317
+
318
+ ---
319
+
320
+ ## 📁 Examples
321
+
322
+ ### 1. PII Filter (Redaction)
323
+
324
+ ```python
325
+ from apl import PolicyServer, Verdict
326
+ import re
327
+
328
+ server = PolicyServer("pii-filter")
329
+
330
+ PATTERNS = {
331
+ "ssn": r'\b\d{3}-\d{2}-\d{4}\b',
332
+ "credit_card": r'\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b',
333
+ "email": r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b',
334
+ }
335
+
336
+ @server.policy(name="redact-pii", events=["output.pre_send"])
337
+ async def redact_pii(event):
338
+ text = event.payload.output_text or ""
339
+
340
+ for name, pattern in PATTERNS.items():
341
+ text = re.sub(pattern, f'[{name.upper()} REDACTED]', text)
342
+
343
+ if text != event.payload.output_text:
344
+ return Verdict.modify(target="output", operation="replace", value=text)
345
+
346
+ return Verdict.allow()
347
+ ```
348
+
349
+ ### 2. Budget Limiter
350
+
351
+ ```python
352
+ from apl import PolicyServer, Verdict
353
+
354
+ server = PolicyServer("budget")
355
+
356
+ @server.policy(name="token-budget", events=["llm.pre_request"])
357
+ async def check_budget(event):
358
+ used = event.metadata.token_count
359
+ budget = event.metadata.token_budget or 100_000
360
+
361
+ if used >= budget:
362
+ return Verdict.deny(reasoning=f"Token budget exceeded: {used:,}/{budget:,}")
363
+
364
+ if used >= budget * 0.8:
365
+ return Verdict.observe(reasoning=f"Token usage at {used/budget:.0%}")
366
+
367
+ return Verdict.allow()
368
+ ```
369
+
370
+ ### 3. Destructive Action Confirmation
371
+
372
+ ```python
373
+ from apl import PolicyServer, Verdict
374
+
375
+ server = PolicyServer("safety")
376
+
377
+ @server.policy(name="confirm-delete", events=["tool.pre_invoke"])
378
+ async def confirm_delete(event):
379
+ tool = event.payload.tool_name or ""
380
+
381
+ if "delete" in tool.lower() or "drop" in tool.lower():
382
+ return Verdict.escalate(
383
+ type="human_confirm",
384
+ prompt=f"⚠️ Destructive action: {tool}\n\nProceed?",
385
+ options=["Proceed", "Cancel"]
386
+ )
387
+
388
+ return Verdict.allow()
389
+ ```
390
+
391
+ ### 4. Declarative YAML Policy (No Python!)
392
+
393
+ ```yaml
394
+ # compliance.yaml
395
+ name: corporate-compliance
396
+ version: 1.0.0
397
+
398
+ policies:
399
+ - name: block-competitor-info
400
+ events:
401
+ - output.pre_send
402
+ rules:
403
+ - when:
404
+ payload.output_text:
405
+ contains: "competitor revenue"
406
+ then:
407
+ decision: deny
408
+ reasoning: "Cannot share competitor financial information"
409
+
410
+ - name: confirm-data-export
411
+ events:
412
+ - tool.pre_invoke
413
+ rules:
414
+ - when:
415
+ payload.tool_name:
416
+ matches: ".*export.*"
417
+ metadata.user_region:
418
+ in: [EU, EEA, UK]
419
+ then:
420
+ decision: escalate
421
+ escalation:
422
+ type: human_confirm
423
+ prompt: "🇪🇺 GDPR: Confirm data export for EU user?"
424
+ ```
425
+
426
+ ```bash
427
+ apl serve compliance.yaml --http 8080
428
+ ```
429
+
430
+ ---
431
+
432
+ ## 🧩 Integration Patterns
433
+
434
+ ### Pattern 1: Auto-Instrumentation (Recommended)
435
+
436
+ ```python
437
+ import apl
438
+
439
+ # Patches OpenAI, Anthropic, LiteLLM, LangChain automatically
440
+ apl.auto_instrument(
441
+ policy_servers=[
442
+ "stdio://./policies/pii_filter.py",
443
+ "http://compliance.internal:8080",
444
+ ],
445
+ user_id="user-123",
446
+ )
447
+
448
+ # All LLM calls are now protected
449
+ from openai import OpenAI
450
+ client = OpenAI()
451
+ response = client.chat.completions.create(...) # ← APL intercepts this
452
+ ```
453
+
454
+ ### Pattern 2: Manual Integration
455
+
456
+ ```python
457
+ from apl import PolicyLayer, EventPayload, SessionMetadata
458
+
459
+ policies = PolicyLayer()
460
+ policies.add_server("stdio://./my_policy.py")
461
+
462
+ # Call this before sending output
463
+ verdict = await policies.evaluate(
464
+ event_type="output.pre_send",
465
+ payload=EventPayload(output_text=response_text),
466
+ metadata=SessionMetadata(session_id="...", user_id="...")
467
+ )
468
+
469
+ if verdict.decision == "modify":
470
+ response_text = verdict.modification.value
471
+ ```
472
+
473
+ ### Pattern 3: LangGraph Wrapper
474
+
475
+ ```python
476
+ from langgraph.graph import StateGraph
477
+ from apl.adapters.langgraph import APLGraphWrapper
478
+
479
+ # Build your graph
480
+ graph = StateGraph(MyState)
481
+ graph.add_node("agent", agent_node)
482
+ graph.add_node("tools", tool_node)
483
+
484
+ # Wrap it with APL
485
+ wrapper = APLGraphWrapper()
486
+ wrapper.add_server("stdio://./my_policy.py")
487
+ wrapped_graph = wrapper.wrap(graph)
488
+
489
+ # Use wrapped_graph — policies evaluated automatically
490
+ ```
491
+
492
+ ---
493
+
494
+ ## 📖 API Reference
495
+
496
+ ### CLI Commands
497
+
498
+ ```
499
+ ┌──────────────────────────────────────────────────────────────┐
500
+ │ Command Description │
501
+ ├──────────────────────────────────────────────────────────────┤
502
+ │ serve Run a policy server │
503
+ │ test Test a policy with sample events │
504
+ │ validate Validate a policy file │
505
+ │ init Create a new policy project │
506
+ │ info Show system information │
507
+ └──────────────────────────────────────────────────────────────┘
508
+ ```
509
+
510
+ ```bash
511
+ # Run a policy server with HTTP
512
+ apl serve ./policy.py --http 8080
513
+
514
+ # Test a policy
515
+ apl test ./policy.py -e output.pre_send
516
+
517
+ # Create a new project
518
+ apl init my-policy --template pii
519
+
520
+ # Validate without running
521
+ apl validate ./policy.yaml
522
+ ```
523
+
524
+ ### HTTP API
525
+
526
+ | Endpoint | Method | Description |
527
+ |----------|--------|-------------|
528
+ | `/evaluate` | POST | Evaluate policies for an event |
529
+ | `/health` | GET | Health check |
530
+ | `/metrics` | GET | Prometheus metrics |
531
+ | `/manifest` | GET | Server manifest |
532
+
533
+ ### Python API
534
+
535
+ ```python
536
+ from apl import (
537
+ # Core
538
+ PolicyServer, # Create policy servers
539
+ PolicyLayer, # Connect to policy servers
540
+ Verdict, # Policy responses
541
+
542
+ # Auto-instrumentation
543
+ auto_instrument, # Patch LLM clients
544
+ uninstrument, # Remove patches
545
+
546
+ # Types
547
+ EventType, # Lifecycle events
548
+ EventPayload, # Event-specific data
549
+ SessionMetadata, # Session context
550
+ Message, # Chat message format
551
+ )
552
+ ```
553
+
554
+ ---
555
+
556
+ ## 🏗️ Architecture
557
+
558
+ ```
559
+ ┌─────────────────────────────────────────────────────────────────────┐
560
+ │ YOUR APPLICATION │
561
+ │ │
562
+ │ ┌───────────────────────────────────────────────────────────────┐ │
563
+ │ │ APL Policy Layer │ │
564
+ │ │ │ │
565
+ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
566
+ │ │ │ Client │ │ Client │ │ Client │ │ │
567
+ │ │ │ (stdio) │ │ (HTTP) │ │ (WebSocket)│ │ │
568
+ │ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │
569
+ │ └─────────┼─────────────────┼─────────────────┼─────────────────┘ │
570
+ └─────────────┼─────────────────┼─────────────────┼───────────────────┘
571
+ │ │ │
572
+ ▼ ▼ ▼
573
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
574
+ │ Policy Server │ │ Policy Server │ │ Policy Server │
575
+ │ (Local Python) │ │ (Remote HTTP) │ │ (YAML) │
576
+ │ │ │ │ │ │
577
+ │ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │
578
+ │ │ Policy 1 │ │ │ │ Policy A │ │ │ │ Rule 1 │ │
579
+ │ │ Policy 2 │ │ │ │ Policy B │ │ │ │ Rule 2 │ │
580
+ │ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │
581
+ └─────────────────┘ └─────────────────┘ └─────────────────┘
582
+ ```
583
+ ---------------------
584
+
585
+ <div align="center">
586
+
587
+ **🛡️**
588
+
589
+ *Secure your agents. Sleep better at night.*
590
+
591
+ </div>
@@ -0,0 +1,18 @@
1
+ apl/__init__.py,sha256=AK8ZBKNGD-oACVTzoPyugl4ZjTrTppLshCwbEkn5Vzg,5069
2
+ apl/cli.py,sha256=eG9wjShH42x6qPOf2nCfpqhcLTeac8_YHygRn1TuzMA,25586
3
+ apl/declarative.py,sha256=Sbc2KKTsaxte27uQGHs8q9xqHAjxPYRxiKZu3c0iQNw,14004
4
+ apl/instrument.py,sha256=Pdr2U4Nsu3CbpgLDMFL9zvniHVtgpyz_KUoNMZOQ1ys,33100
5
+ apl/layer.py,sha256=6gTk3vJvQBCeVhEwJOyilYlJh9pWphRy2sDD5RwGUbI,23595
6
+ apl/logging.py,sha256=3m3sl6DooOV2cIwXVASlebrOPnVzEWN27UPRcaYgJuA,11057
7
+ apl/server.py,sha256=dhz6nVI2CUlCYa3o6c3jQ4R5l034I70Ml1lvjlw2yOs,16152
8
+ apl/templates.py,sha256=VzlmcXIrn8wzEtuQep2aUHbuZ2zn43kGWkvGsPfPgfY,11282
9
+ apl/types.py,sha256=WOB_2rAp-C0E8hBCYBKmliSQTQY_RqmxDlXhk2uDuVA,12277
10
+ apl/adapters/__init__.py,sha256=YEJrrznUmXgJgeXIF-RLxNK1t3iaFnk8gDvbAh5MpIQ,480
11
+ apl/adapters/langgraph.py,sha256=XVBfDkQQvxUlmtfNFPUcj1jTqJagh-QhVl9B9h7aQjw,14439
12
+ apl/transports/__init__.py,sha256=xyxkfOb93WuTnUy3VXH2ES4WReJjo6wjl6DydqbTLEI,359
13
+ apl/transports/http.py,sha256=WJ7Y5Tguv3WRFgEl1_ooU43U0R3uVSvqTmjrJ630rHs,20523
14
+ agent_policy_layer-0.1.0.dist-info/METADATA,sha256=UzDac3vEI4OTtEJsTp94WiUT5klMKAlQeyFVIapEMos,22265
15
+ agent_policy_layer-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
16
+ agent_policy_layer-0.1.0.dist-info/entry_points.txt,sha256=-k9qW1UWpOWzFYP9zQHbzaDTnDHsSL6cNePOyS6WOSU,37
17
+ agent_policy_layer-0.1.0.dist-info/licenses/LICENSE,sha256=V4H80HdjfRvTVDuCKEUPKijUi8pfnMQOqs8YNnRPvR8,11350
18
+ agent_policy_layer-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ apl = apl.cli:main