@aws/nx-plugin 1.2.0 → 1.3.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/README.md +1 -0
- package/generators.json +8 -0
- package/migrations.json +6 -1
- package/package.json +1 -1
- package/src/internal/test-matrix/generator.js +6 -1
- package/src/internal/test-matrix/generator.js.map +1 -1
- package/src/migrations/latest/ts-agent-guardrails-terraform-version-retention/metadata.json +3 -0
- package/src/migrations/latest/ts-agent-guardrails-terraform-version-retention/migration.d.ts +6 -0
- package/src/migrations/latest/ts-agent-guardrails-terraform-version-retention/migration.js +118 -0
- package/src/migrations/latest/ts-agent-guardrails-terraform-version-retention/migration.js.map +1 -0
- package/src/preset/__snapshots__/generator.spec.ts.snap +2 -0
- package/src/py/agent/guardrails/__snapshots__/generator.spec.ts.snap +3813 -0
- package/src/py/agent/guardrails/files/langchain/guardrail.py.template +135 -0
- package/src/py/agent/guardrails/files/strands/guardrail.py.template +93 -0
- package/src/py/agent/guardrails/generator.d.ts +10 -0
- package/src/py/agent/guardrails/generator.js +175 -0
- package/src/py/agent/guardrails/generator.js.map +1 -0
- package/src/py/agent/guardrails/runtime-check/run_checks.py +250 -0
- package/src/py/agent/guardrails/schema.d.js +6 -0
- package/src/py/agent/guardrails/schema.d.js.map +1 -0
- package/src/py/agent/guardrails/schema.d.ts +9 -0
- package/src/py/agent/guardrails/schema.json +25 -0
- package/src/sdk/py.d.ts +2 -0
- package/src/sdk/py.js +1 -0
- package/src/sdk/py.js.map +1 -1
- package/src/ts/agent/guardrails/__snapshots__/generator.spec.ts.snap +54 -3
- package/src/ts/agent/guardrails/generator.js +2 -1
- package/src/ts/agent/guardrails/generator.js.map +1 -1
- package/src/utils/guardrail-constructs/files/terraform/app/guardrails/__nameKebabCase__/__nameKebabCase__-guardrail.tf.template +18 -1
- package/src/utils/guardrail-constructs/guardrail-constructs.d.ts +5 -0
- package/src/utils/guardrail-constructs/guardrail-constructs.js +8 -0
- package/src/utils/guardrail-constructs/guardrail-constructs.js.map +1 -1
- package/src/utils/guardrail-constructs/guardrail-wiring.d.ts +7 -0
- package/src/utils/guardrail-constructs/guardrail-wiring.js +6 -5
- package/src/utils/guardrail-constructs/guardrail-wiring.js.map +1 -1
- package/src/utils/test/fake-aws/fake_aws.py +205 -0
- package/src/utils/test/python-dependencies.d.ts +6 -0
- package/src/utils/test/python-dependencies.js +19 -0
- package/src/utils/test/python-dependencies.js.map +1 -1
- package/src/utils/test/warm-python-cache.js +5 -1
- package/src/utils/test/warm-python-cache.js.map +1 -1
- /package/src/{ts/agent/guardrails/files/agent → utils/guardrail-constructs/files/policy}/guardrail.json.template +0 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
#
|
|
5
|
+
"""Run a generated, guarded Python agent against fake_aws.py and report what
|
|
6
|
+
the real SDK sent. Used by guardrail-runtime.spec.ts.
|
|
7
|
+
|
|
8
|
+
Usage: python run_checks.py <project root> <connection root> <fake_aws.py> <framework>
|
|
9
|
+
<agent package> <agent-connection module> [<scenario>]
|
|
10
|
+
|
|
11
|
+
<project root> holds the agent project's module and pyproject.toml, and
|
|
12
|
+
<connection root> the agent-connection module, as in a workspace.
|
|
13
|
+
Without a scenario, every scenario runs in a fresh interpreter (so the
|
|
14
|
+
guardrail cache and warn-once state start clean), then the static checks, and
|
|
15
|
+
one line "RESULTS <json>" maps each name to its result.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import asyncio
|
|
19
|
+
import importlib
|
|
20
|
+
import importlib.util
|
|
21
|
+
import json
|
|
22
|
+
import logging
|
|
23
|
+
import os
|
|
24
|
+
import shutil
|
|
25
|
+
import subprocess
|
|
26
|
+
import sys
|
|
27
|
+
import tempfile
|
|
28
|
+
import time
|
|
29
|
+
from contextlib import contextmanager
|
|
30
|
+
from typing import Any
|
|
31
|
+
|
|
32
|
+
ROOT, CONNECTION_ROOT, FAKE_AWS, FRAMEWORK, AGENT_PACKAGE, CONNECTION_MODULE = sys.argv[1:7]
|
|
33
|
+
|
|
34
|
+
ENTRY = {"guardrailId": "grtest", "guardrailVersion": "1"}
|
|
35
|
+
# fake_aws.py blocks a guarded request whose checked text contains "stocks".
|
|
36
|
+
BLOCKED_PROMPT = "Which stocks should I buy this year?"
|
|
37
|
+
|
|
38
|
+
# scenario: (LOCAL_DEV, RUNTIME_CONFIG_APP_ID, guardrail entry or None, number of calls)
|
|
39
|
+
SCENARIOS: dict[str, tuple[bool, str | None, dict[str, str] | None, int]] = {
|
|
40
|
+
"guarded": (False, "fake-app", ENTRY, 1),
|
|
41
|
+
"missing": (False, "fake-app", None, 1),
|
|
42
|
+
"no-app-id": (False, None, ENTRY, 1),
|
|
43
|
+
"local-dev": (True, None, ENTRY, 2),
|
|
44
|
+
"local-dev-broken": (True, "broken", ENTRY, 2),
|
|
45
|
+
"local-dev-guarded": (True, "fake-app", ENTRY, 1),
|
|
46
|
+
"draft": (False, "fake-app", {**ENTRY, "guardrailVersion": "DRAFT"}, 1),
|
|
47
|
+
"draft-local": (True, "fake-app", {**ENTRY, "guardrailVersion": "DRAFT"}, 1),
|
|
48
|
+
"zero": (False, "fake-app", {**ENTRY, "guardrailVersion": "0"}, 1),
|
|
49
|
+
"zero-local": (True, "fake-app", {**ENTRY, "guardrailVersion": "0"}, 1),
|
|
50
|
+
"reread": (False, "fake-app", ENTRY, 0),
|
|
51
|
+
"concurrent": (False, "fake-app", ENTRY, 0),
|
|
52
|
+
"blocked-session": (False, "fake-app", ENTRY, 0),
|
|
53
|
+
"blocked-session-stream": (False, "fake-app", ENTRY, 0),
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def write_config(path: str, entry: dict[str, str] | None, class_name: str) -> None:
|
|
58
|
+
data: dict[str, Any] = {"agentRuntimes": {}}
|
|
59
|
+
if entry is not None:
|
|
60
|
+
data["guardrails"] = {class_name: entry}
|
|
61
|
+
with open(path, "w") as f:
|
|
62
|
+
json.dump(data, f)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def read_log(path: str) -> list[dict[str, Any]]:
|
|
66
|
+
if not os.path.exists(path):
|
|
67
|
+
return []
|
|
68
|
+
with open(path) as f:
|
|
69
|
+
return [json.loads(line) for line in f if line.strip()]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class WarningRecorder(logging.Handler):
|
|
73
|
+
def __init__(self) -> None:
|
|
74
|
+
super().__init__(logging.WARNING)
|
|
75
|
+
self.messages: list[str] = []
|
|
76
|
+
|
|
77
|
+
def emit(self, record: logging.LogRecord) -> None:
|
|
78
|
+
if record.name.endswith(".guardrail"):
|
|
79
|
+
self.messages.append(record.getMessage())
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def run_scenario(name: str) -> dict[str, Any]:
|
|
83
|
+
local_dev, app_id, entry, calls = SCENARIOS[name]
|
|
84
|
+
work = tempfile.mkdtemp(prefix=f"{name}-")
|
|
85
|
+
config_file, log_file = os.path.join(work, "config.json"), os.path.join(work, "requests.log")
|
|
86
|
+
# The vended local session storage is relative to the project root, two levels down.
|
|
87
|
+
cwd = os.path.join(work, "packages", "project")
|
|
88
|
+
os.makedirs(cwd)
|
|
89
|
+
os.chdir(cwd)
|
|
90
|
+
|
|
91
|
+
spec = importlib.util.spec_from_file_location("fake_aws", FAKE_AWS)
|
|
92
|
+
assert spec and spec.loader
|
|
93
|
+
fake_aws = importlib.util.module_from_spec(spec)
|
|
94
|
+
spec.loader.exec_module(fake_aws)
|
|
95
|
+
_server, port = fake_aws.start_server(config_file, log_file)
|
|
96
|
+
os.environ["AWS_ENDPOINT_URL_APPCONFIGDATA"] = f"http://127.0.0.1:{port}"
|
|
97
|
+
os.environ["AWS_ENDPOINT_URL_BEDROCK_RUNTIME"] = f"http://127.0.0.1:{port}"
|
|
98
|
+
os.environ.pop("LOCAL_DEV", None)
|
|
99
|
+
os.environ.pop("RUNTIME_CONFIG_APP_ID", None)
|
|
100
|
+
if local_dev:
|
|
101
|
+
os.environ["LOCAL_DEV"] = "true"
|
|
102
|
+
if app_id:
|
|
103
|
+
os.environ["RUNTIME_CONFIG_APP_ID"] = app_id
|
|
104
|
+
|
|
105
|
+
recorder = WarningRecorder()
|
|
106
|
+
logging.getLogger().addHandler(recorder)
|
|
107
|
+
|
|
108
|
+
sys.path[:0] = [ROOT, CONNECTION_ROOT]
|
|
109
|
+
agent_module = importlib.import_module(f"{AGENT_PACKAGE}.agent")
|
|
110
|
+
guardrail_module = importlib.import_module(f"{AGENT_PACKAGE}.guardrail")
|
|
111
|
+
class_name = guardrail_module.RUNTIME_CONFIG_KEY.split(".")[-1]
|
|
112
|
+
write_config(config_file, entry, class_name)
|
|
113
|
+
|
|
114
|
+
result: dict[str, Any] = {"calls": []}
|
|
115
|
+
|
|
116
|
+
def snapshot() -> dict[str, Any]:
|
|
117
|
+
log = read_log(log_file)
|
|
118
|
+
return {
|
|
119
|
+
"models": [r for r in log if "modelId" in r],
|
|
120
|
+
"appConfigCalls": len([r for r in log if "modelId" not in r]),
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async def scenario() -> None:
|
|
124
|
+
if FRAMEWORK == "strands":
|
|
125
|
+
connection = importlib.import_module(CONNECTION_MODULE)
|
|
126
|
+
|
|
127
|
+
@contextmanager
|
|
128
|
+
def agent_scope():
|
|
129
|
+
with connection.session_id_context("s1"), agent_module.get_agent() as agent:
|
|
130
|
+
yield agent
|
|
131
|
+
|
|
132
|
+
async def call(agent: Any, prompt: str = "2+2?") -> None:
|
|
133
|
+
await agent.invoke_async(prompt)
|
|
134
|
+
|
|
135
|
+
if name == "concurrent":
|
|
136
|
+
from strands import Agent
|
|
137
|
+
|
|
138
|
+
shared = guardrail_module.get_guardrailed_model()
|
|
139
|
+
agents = [Agent(model=shared, callback_handler=None) for _ in range(10)]
|
|
140
|
+
await asyncio.gather(*[a.invoke_async("2+2?") for a in agents])
|
|
141
|
+
return
|
|
142
|
+
with agent_scope() as agent:
|
|
143
|
+
await run_calls(agent, call)
|
|
144
|
+
else:
|
|
145
|
+
graph = agent_module.get_agent()
|
|
146
|
+
counter = iter(range(1000))
|
|
147
|
+
|
|
148
|
+
async def call(agent: Any, prompt: str | None = None) -> None:
|
|
149
|
+
# A call with a prompt continues one session; without, each call is a new session.
|
|
150
|
+
if name == "blocked-session-stream":
|
|
151
|
+
await stream(agent, prompt)
|
|
152
|
+
return
|
|
153
|
+
thread = {"configurable": {"thread_id": "session" if prompt else f"t{next(counter)}"}}
|
|
154
|
+
await agent.ainvoke({"messages": [{"role": "user", "content": prompt or "2+2?"}]}, thread)
|
|
155
|
+
|
|
156
|
+
async def stream(agent: Any, prompt: str | None = None) -> None:
|
|
157
|
+
thread = {"configurable": {"thread_id": "session" if prompt else f"t{next(counter)}"}}
|
|
158
|
+
async for _ in agent.astream(
|
|
159
|
+
{"messages": [{"role": "user", "content": prompt or "2+2?"}]}, thread, stream_mode="messages"
|
|
160
|
+
):
|
|
161
|
+
pass
|
|
162
|
+
|
|
163
|
+
if name == "concurrent":
|
|
164
|
+
await asyncio.gather(*[call(graph) for _ in range(10)])
|
|
165
|
+
return
|
|
166
|
+
await run_calls(graph, call)
|
|
167
|
+
if name == "guarded":
|
|
168
|
+
await stream(graph)
|
|
169
|
+
result["streamed"] = snapshot()["models"][-1:]
|
|
170
|
+
|
|
171
|
+
async def run_calls(agent: Any, call: Any) -> None:
|
|
172
|
+
if name.startswith("blocked-session"):
|
|
173
|
+
# A blocked message, then a normal one, in the same session.
|
|
174
|
+
for prompt in (BLOCKED_PROMPT, "2+2?"):
|
|
175
|
+
await call(agent, prompt)
|
|
176
|
+
result["calls"].append(snapshot())
|
|
177
|
+
return
|
|
178
|
+
if name == "reread":
|
|
179
|
+
await call(agent)
|
|
180
|
+
result["calls"].append(snapshot())
|
|
181
|
+
write_config(config_file, {**ENTRY, "guardrailVersion": "2"}, class_name)
|
|
182
|
+
await call(agent)
|
|
183
|
+
result["calls"].append(snapshot())
|
|
184
|
+
time.sleep(5.5)
|
|
185
|
+
await call(agent)
|
|
186
|
+
result["calls"].append(snapshot())
|
|
187
|
+
return
|
|
188
|
+
for _ in range(calls):
|
|
189
|
+
await call(agent)
|
|
190
|
+
result["calls"].append(snapshot())
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
asyncio.run(scenario())
|
|
194
|
+
except Exception as e: # noqa: BLE001 - reported to the spec
|
|
195
|
+
result["error"] = {"type": type(e).__name__, "message": str(e)}
|
|
196
|
+
result.update(snapshot())
|
|
197
|
+
result["warnings"] = recorder.messages
|
|
198
|
+
shutil.rmtree(work, ignore_errors=True)
|
|
199
|
+
return result
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def run_static() -> dict[str, Any]:
|
|
203
|
+
package_dir = os.path.join(ROOT, *AGENT_PACKAGE.split("."))
|
|
204
|
+
files = [os.path.join(package_dir, f) for f in ("guardrail.py", "agent.py")]
|
|
205
|
+
# uv's --with environment layers several site-packages directories, which
|
|
206
|
+
# ty does not discover from the interpreter alone.
|
|
207
|
+
search_paths = [CONNECTION_ROOT, *(p for p in sys.path if p.endswith("site-packages"))]
|
|
208
|
+
commands = {
|
|
209
|
+
"ty": [
|
|
210
|
+
"ty",
|
|
211
|
+
"check",
|
|
212
|
+
"--python",
|
|
213
|
+
sys.executable,
|
|
214
|
+
*(arg for path in search_paths for arg in ("--extra-search-path", path)),
|
|
215
|
+
"--no-progress",
|
|
216
|
+
*files,
|
|
217
|
+
],
|
|
218
|
+
"ruffFormat": ["ruff", "format", "--check", *files],
|
|
219
|
+
"ruffCheck": ["ruff", "check", "--select", "E,F,UP,B,SIM,I", *files],
|
|
220
|
+
}
|
|
221
|
+
results = {}
|
|
222
|
+
for key, command in commands.items():
|
|
223
|
+
done = subprocess.run(command, cwd=ROOT, capture_output=True, text=True)
|
|
224
|
+
results[key] = {"code": done.returncode, "output": done.stdout + done.stderr}
|
|
225
|
+
return results
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
if __name__ == "__main__":
|
|
229
|
+
if len(sys.argv) > 7:
|
|
230
|
+
print("\nRESULT " + json.dumps(run_scenario(sys.argv[7])), flush=True)
|
|
231
|
+
# A failed LangChain call in local dev leaves the SQLite checkpointer's
|
|
232
|
+
# non-daemon thread running, which would keep the interpreter alive.
|
|
233
|
+
os._exit(0)
|
|
234
|
+
else:
|
|
235
|
+
results: dict[str, Any] = {}
|
|
236
|
+
for scenario_name in SCENARIOS:
|
|
237
|
+
done = subprocess.run(
|
|
238
|
+
[sys.executable, __file__, *sys.argv[1:7], scenario_name],
|
|
239
|
+
capture_output=True,
|
|
240
|
+
text=True,
|
|
241
|
+
timeout=180,
|
|
242
|
+
)
|
|
243
|
+
lines = [line for line in done.stdout.splitlines() if line.startswith("RESULT ")]
|
|
244
|
+
results[scenario_name] = (
|
|
245
|
+
json.loads(lines[-1][len("RESULT ") :])
|
|
246
|
+
if lines
|
|
247
|
+
else {"crashed": done.returncode, "stdout": done.stdout[-4000:], "stderr": done.stderr[-4000:]}
|
|
248
|
+
)
|
|
249
|
+
results["static"] = run_static()
|
|
250
|
+
print("RESULTS " + json.dumps(results), flush=True)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/py/agent/guardrails/schema.d.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nexport interface PyAgentGuardrailsGeneratorSchema {\n project: string;\n agent?: string;\n}\n"],"names":[],"mappings":"AAAA;;;CAGC,GAED,WAGC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/schema",
|
|
3
|
+
"$id": "py#agent#guardrails",
|
|
4
|
+
"title": "py#agent#guardrails",
|
|
5
|
+
"x-label": "Agent Guardrails",
|
|
6
|
+
"description": "Add an Amazon Bedrock Guardrail to a Python Agent",
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"project": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "The project containing the Python Agent",
|
|
12
|
+
"$default": {
|
|
13
|
+
"$source": "argv",
|
|
14
|
+
"index": 0
|
|
15
|
+
},
|
|
16
|
+
"x-prompt": "Select the project containing the Python Agent to guard",
|
|
17
|
+
"x-dropdown": "projects"
|
|
18
|
+
},
|
|
19
|
+
"agent": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "The name of the Agent component to guard. Required when the project contains more than one Python Agent."
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"required": ["project"]
|
|
25
|
+
}
|
package/src/sdk/py.d.ts
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/
|
|
5
5
|
export { pyAgentGenerator } from '../py/agent/generator.js';
|
|
6
|
+
export { pyAgentGuardrailsGenerator } from '../py/agent/guardrails/generator.js';
|
|
7
|
+
export type { PyAgentGuardrailsGeneratorSchema } from '../py/agent/guardrails/schema';
|
|
6
8
|
export type { PyAgentGeneratorSchema } from '../py/agent/schema';
|
|
7
9
|
export { pyApiGenerator } from '../py/api/generator.js';
|
|
8
10
|
export type { PyApiGeneratorSchema } from '../py/api/schema';
|
package/src/sdk/py.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
3
|
* SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
*/ export { pyAgentGenerator } from "../py/agent/generator.js";
|
|
5
|
+
export { pyAgentGuardrailsGenerator } from "../py/agent/guardrails/generator.js";
|
|
5
6
|
export { pyApiGenerator } from "../py/api/generator.js";
|
|
6
7
|
export { pyDynamoDBGenerator } from "../py/dynamodb/generator.js";
|
|
7
8
|
export { pyLambdaFunctionGenerator } from "../py/lambda-function/generator.js";
|
package/src/sdk/py.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../packages/nx-plugin/src/sdk/py.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nexport { pyAgentGenerator } from '../py/agent/generator.js';\nexport type { PyAgentGeneratorSchema } from '../py/agent/schema';\n\nexport { pyApiGenerator } from '../py/api/generator.js';\nexport type { PyApiGeneratorSchema } from '../py/api/schema';\n\nexport { pyDynamoDBGenerator } from '../py/dynamodb/generator.js';\nexport type { PyDynamoDBGeneratorSchema } from '../py/dynamodb/schema';\n\nexport { pyLambdaFunctionGenerator } from '../py/lambda-function/generator.js';\nexport type { PyLambdaFunctionGeneratorSchema } from '../py/lambda-function/schema';\n\nexport { pyMcpServerGenerator } from '../py/mcp-server/generator.js';\nexport type { PyMcpServerGeneratorSchema } from '../py/mcp-server/schema';\nexport { pyProjectGenerator } from '../py/project/generator.js';\nexport type { PyProjectGeneratorSchema } from '../py/project/schema';\n\nexport { pyRdbGenerator } from '../py/rdb/generator.js';\nexport type { PyRdbGeneratorSchema } from '../py/rdb/schema';\n"],"names":["pyAgentGenerator","pyApiGenerator","pyDynamoDBGenerator","pyLambdaFunctionGenerator","pyMcpServerGenerator","pyProjectGenerator","pyRdbGenerator"],"mappings":"AAAA;;;CAGC,GAED,SAASA,gBAAgB,QAAQ,2BAA2B;
|
|
1
|
+
{"version":3,"sources":["../../../../../packages/nx-plugin/src/sdk/py.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\n\nexport { pyAgentGenerator } from '../py/agent/generator.js';\nexport { pyAgentGuardrailsGenerator } from '../py/agent/guardrails/generator.js';\nexport type { PyAgentGuardrailsGeneratorSchema } from '../py/agent/guardrails/schema';\nexport type { PyAgentGeneratorSchema } from '../py/agent/schema';\n\nexport { pyApiGenerator } from '../py/api/generator.js';\nexport type { PyApiGeneratorSchema } from '../py/api/schema';\n\nexport { pyDynamoDBGenerator } from '../py/dynamodb/generator.js';\nexport type { PyDynamoDBGeneratorSchema } from '../py/dynamodb/schema';\n\nexport { pyLambdaFunctionGenerator } from '../py/lambda-function/generator.js';\nexport type { PyLambdaFunctionGeneratorSchema } from '../py/lambda-function/schema';\n\nexport { pyMcpServerGenerator } from '../py/mcp-server/generator.js';\nexport type { PyMcpServerGeneratorSchema } from '../py/mcp-server/schema';\nexport { pyProjectGenerator } from '../py/project/generator.js';\nexport type { PyProjectGeneratorSchema } from '../py/project/schema';\n\nexport { pyRdbGenerator } from '../py/rdb/generator.js';\nexport type { PyRdbGeneratorSchema } from '../py/rdb/schema';\n"],"names":["pyAgentGenerator","pyAgentGuardrailsGenerator","pyApiGenerator","pyDynamoDBGenerator","pyLambdaFunctionGenerator","pyMcpServerGenerator","pyProjectGenerator","pyRdbGenerator"],"mappings":"AAAA;;;CAGC,GAED,SAASA,gBAAgB,QAAQ,2BAA2B;AAC5D,SAASC,0BAA0B,QAAQ,sCAAsC;AAIjF,SAASC,cAAc,QAAQ,yBAAyB;AAGxD,SAASC,mBAAmB,QAAQ,8BAA8B;AAGlE,SAASC,yBAAyB,QAAQ,qCAAqC;AAG/E,SAASC,oBAAoB,QAAQ,gCAAgC;AAErE,SAASC,kBAAkB,QAAQ,6BAA6B;AAGhE,SAASC,cAAc,QAAQ,yBAAyB"}
|
|
@@ -445,6 +445,10 @@ exports[`ts#agent#guardrails generator > protocol a2a > should generate the guar
|
|
|
445
445
|
source = "hashicorp/random"
|
|
446
446
|
version = "3.9.1"
|
|
447
447
|
}
|
|
448
|
+
time = {
|
|
449
|
+
source = "hashicorp/time"
|
|
450
|
+
version = "0.14.2"
|
|
451
|
+
}
|
|
448
452
|
}
|
|
449
453
|
}
|
|
450
454
|
|
|
@@ -509,12 +513,25 @@ resource "aws_bedrock_guardrail_version" "this" {
|
|
|
509
513
|
}
|
|
510
514
|
}
|
|
511
515
|
|
|
516
|
+
resource "time_sleep" "version_retention" {
|
|
517
|
+
destroy_duration = "15s"
|
|
518
|
+
|
|
519
|
+
triggers = {
|
|
520
|
+
guardrail_id = aws_bedrock_guardrail.this.guardrail_id
|
|
521
|
+
guardrail_version = aws_bedrock_guardrail_version.this.version
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
lifecycle {
|
|
525
|
+
create_before_destroy = true
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
512
529
|
module "add_guardrail_to_runtime_config" {
|
|
513
530
|
source = "../../../core/runtime-config/entry"
|
|
514
531
|
|
|
515
532
|
namespace = "agentcore"
|
|
516
533
|
key = "guardrails"
|
|
517
|
-
value = { "TestProjectAgent" = { guardrailId =
|
|
534
|
+
value = { "TestProjectAgent" = { guardrailId = time_sleep.version_retention.triggers.guardrail_id, guardrailVersion = time_sleep.version_retention.triggers.guardrail_version } }
|
|
518
535
|
}
|
|
519
536
|
|
|
520
537
|
output "guardrail_id" {
|
|
@@ -977,6 +994,10 @@ exports[`ts#agent#guardrails generator > protocol ag-ui > should generate the gu
|
|
|
977
994
|
source = "hashicorp/random"
|
|
978
995
|
version = "3.9.1"
|
|
979
996
|
}
|
|
997
|
+
time = {
|
|
998
|
+
source = "hashicorp/time"
|
|
999
|
+
version = "0.14.2"
|
|
1000
|
+
}
|
|
980
1001
|
}
|
|
981
1002
|
}
|
|
982
1003
|
|
|
@@ -1041,12 +1062,25 @@ resource "aws_bedrock_guardrail_version" "this" {
|
|
|
1041
1062
|
}
|
|
1042
1063
|
}
|
|
1043
1064
|
|
|
1065
|
+
resource "time_sleep" "version_retention" {
|
|
1066
|
+
destroy_duration = "15s"
|
|
1067
|
+
|
|
1068
|
+
triggers = {
|
|
1069
|
+
guardrail_id = aws_bedrock_guardrail.this.guardrail_id
|
|
1070
|
+
guardrail_version = aws_bedrock_guardrail_version.this.version
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
lifecycle {
|
|
1074
|
+
create_before_destroy = true
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1044
1078
|
module "add_guardrail_to_runtime_config" {
|
|
1045
1079
|
source = "../../../core/runtime-config/entry"
|
|
1046
1080
|
|
|
1047
1081
|
namespace = "agentcore"
|
|
1048
1082
|
key = "guardrails"
|
|
1049
|
-
value = { "TestProjectAgent" = { guardrailId =
|
|
1083
|
+
value = { "TestProjectAgent" = { guardrailId = time_sleep.version_retention.triggers.guardrail_id, guardrailVersion = time_sleep.version_retention.triggers.guardrail_version } }
|
|
1050
1084
|
}
|
|
1051
1085
|
|
|
1052
1086
|
output "guardrail_id" {
|
|
@@ -1523,6 +1557,10 @@ exports[`ts#agent#guardrails generator > protocol http > should generate the gua
|
|
|
1523
1557
|
source = "hashicorp/random"
|
|
1524
1558
|
version = "3.9.1"
|
|
1525
1559
|
}
|
|
1560
|
+
time = {
|
|
1561
|
+
source = "hashicorp/time"
|
|
1562
|
+
version = "0.14.2"
|
|
1563
|
+
}
|
|
1526
1564
|
}
|
|
1527
1565
|
}
|
|
1528
1566
|
|
|
@@ -1587,12 +1625,25 @@ resource "aws_bedrock_guardrail_version" "this" {
|
|
|
1587
1625
|
}
|
|
1588
1626
|
}
|
|
1589
1627
|
|
|
1628
|
+
resource "time_sleep" "version_retention" {
|
|
1629
|
+
destroy_duration = "15s"
|
|
1630
|
+
|
|
1631
|
+
triggers = {
|
|
1632
|
+
guardrail_id = aws_bedrock_guardrail.this.guardrail_id
|
|
1633
|
+
guardrail_version = aws_bedrock_guardrail_version.this.version
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
lifecycle {
|
|
1637
|
+
create_before_destroy = true
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1590
1641
|
module "add_guardrail_to_runtime_config" {
|
|
1591
1642
|
source = "../../../core/runtime-config/entry"
|
|
1592
1643
|
|
|
1593
1644
|
namespace = "agentcore"
|
|
1594
1645
|
key = "guardrails"
|
|
1595
|
-
value = { "TestProjectAgent" = { guardrailId =
|
|
1646
|
+
value = { "TestProjectAgent" = { guardrailId = time_sleep.version_retention.triggers.guardrail_id, guardrailVersion = time_sleep.version_retention.triggers.guardrail_version } }
|
|
1596
1647
|
}
|
|
1597
1648
|
|
|
1598
1649
|
output "guardrail_id" {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/ import { generateFiles, joinPathFragments, logger, OverwriteStrategy } from "@nx/devkit";
|
|
5
5
|
import { addDestructuredImport, applyGritQL, matchGritQL } from "../../../utils/ast.js";
|
|
6
6
|
import { formatFilesInSubtree } from "../../../utils/format.js";
|
|
7
|
-
import { addGuardrailInfra } from "../../../utils/guardrail-constructs/guardrail-constructs.js";
|
|
7
|
+
import { addGuardrailInfra, addGuardrailPolicy } from "../../../utils/guardrail-constructs/guardrail-constructs.js";
|
|
8
8
|
import { guardrailWiringMessage } from "../../../utils/guardrail-constructs/guardrail-wiring.js";
|
|
9
9
|
import { addGeneratorMetricsIfApplicable } from "../../../utils/metrics.js";
|
|
10
10
|
import { esmVars, isEsmWorkspace } from "../../../utils/module-format.js";
|
|
@@ -74,6 +74,7 @@ export const tsAgentGuardrailsGenerator = async (tree, options)=>{
|
|
|
74
74
|
}, {
|
|
75
75
|
overwriteStrategy: OverwriteStrategy.KeepExisting
|
|
76
76
|
});
|
|
77
|
+
addGuardrailPolicy(tree, agentDir);
|
|
77
78
|
await addGuardrailedModelToAgent(tree, joinPathFragments(agentDir, 'agent.ts'));
|
|
78
79
|
const iac = agent.iac;
|
|
79
80
|
let wiringMessage;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/ts/agent/guardrails/generator.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n type GeneratorCallback,\n generateFiles,\n joinPathFragments,\n logger,\n OverwriteStrategy,\n type Tree,\n} from '@nx/devkit';\nimport {\n addDestructuredImport,\n applyGritQL,\n matchGritQL,\n} from '../../../utils/ast.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport { addGuardrailInfra } from '../../../utils/guardrail-constructs/guardrail-constructs.js';\nimport { guardrailWiringMessage } from '../../../utils/guardrail-constructs/guardrail-wiring.js';\nimport type { Iac } from '../../../utils/iac.js';\nimport { addGeneratorMetricsIfApplicable } from '../../../utils/metrics.js';\nimport { esmVars, isEsmWorkspace } from '../../../utils/module-format.js';\nimport { kebabCase, toClassName } from '../../../utils/names.js';\nimport { getNpmScope } from '../../../utils/npm-scope.js';\nimport {\n addComponentGeneratorMetadata,\n type ComponentMetadata,\n getGeneratorInfo,\n type NxGeneratorInfo,\n readProjectConfigurationUnqualified,\n} from '../../../utils/nx.js';\nimport { TS_AGENT_GENERATOR_INFO } from '../generator.js';\nimport type { TsAgentGuardrailsGeneratorSchema } from './schema';\nimport { addGuardrailPolicySynthInputs } from './synth-inputs.js';\n\nexport const TS_AGENT_GUARDRAILS_GENERATOR_INFO: NxGeneratorInfo =\n getGeneratorInfo(import.meta.filename);\n\n/**\n * Find the `ts#agent` component to guard, by name when given, otherwise the\n * project's only one.\n */\nconst resolveAgent = (\n projectName: string,\n components: ComponentMetadata[],\n agent?: string,\n): ComponentMetadata => {\n const agents = components.filter(\n (c) => c.generator === TS_AGENT_GENERATOR_INFO.id,\n );\n const available = agents.map((c) => c.name).join(', ') || 'none';\n if (agent) {\n const match = agents.find((c) => c.name === agent);\n if (!match) {\n throw new Error(\n `No TypeScript Agent named ${agent} in project ${projectName}. Available agents: ${available}`,\n );\n }\n return match;\n }\n if (agents.length !== 1) {\n throw new Error(\n agents.length === 0\n ? `Project ${projectName} has no TypeScript Agent. Add one with the ts#agent generator first.`\n : `Project ${projectName} has more than one TypeScript Agent. Specify which to guard with --agent. Available agents: ${available}`,\n );\n }\n return agents[0];\n};\n\n/**\n * Add `model: getGuardrailedModel()` to the `new Agent({...})` call in\n * agent.ts. An agent that already sets its own model is left unchanged, and\n * the lines to add by hand are logged instead.\n */\nconst addGuardrailedModelToAgent = async (\n tree: Tree,\n agentFile: string,\n): Promise<void> => {\n const guardrailImport = isEsmWorkspace(tree)\n ? './guardrail.js'\n : './guardrail';\n\n if (tree.exists(agentFile)) {\n if (await matchGritQL(tree, agentFile, '`getGuardrailedModel()`')) {\n return;\n }\n if (\n await applyGritQL(\n tree,\n agentFile,\n '`new Agent({ $props })` => `new Agent({ model: getGuardrailedModel(), $props })` where { $props <: not some or { `model: $_`, `model` } }',\n )\n ) {\n await addDestructuredImport(\n tree,\n agentFile,\n ['getGuardrailedModel'],\n './guardrail.js',\n );\n return;\n }\n }\n\n logger.warn(\n `Could not add the guardrail to ${agentFile}, because ${tree.exists(agentFile) ? 'its new Agent({...}) call already sets a model or is not an inline object' : 'the file does not exist'}. Set the model of your Agent to the guardrailed model by hand:\n\n model: getGuardrailedModel(),\n\nand import it:\n\n import { getGuardrailedModel } from '${guardrailImport}';`,\n );\n};\n\nexport const tsAgentGuardrailsGenerator = async (\n tree: Tree,\n options: TsAgentGuardrailsGeneratorSchema,\n): Promise<GeneratorCallback> => {\n const project = readProjectConfigurationUnqualified(tree, options.project);\n const agent = resolveAgent(\n project.name,\n (project.metadata as any)?.components ?? [],\n options.agent,\n );\n\n const agentTargetPrefix = agent.name ?? 'agent';\n const agentNameClassName = agent.rc ?? toClassName(agentTargetPrefix);\n const nameKebabCase = kebabCase(agentNameClassName);\n const agentPath = agent.path ?? joinPathFragments('src', agentTargetPrefix);\n const agentDir = joinPathFragments(project.root, agentPath);\n\n // The policy is the user's to edit, so a re-run never touches these files.\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files', 'agent'),\n agentDir,\n {\n ...esmVars(tree),\n agentConnectionImport: `@${getNpmScope(tree)}/agent-connection`,\n agentNameClassName,\n nameKebabCase,\n },\n { overwriteStrategy: OverwriteStrategy.KeepExisting },\n );\n\n await addGuardrailedModelToAgent(\n tree,\n joinPathFragments(agentDir, 'agent.ts'),\n );\n\n const iac = agent.iac as Iac | undefined;\n let wiringMessage: string | undefined;\n if (iac) {\n const policyPath = joinPathFragments(agentDir, 'guardrail.json');\n await addGuardrailInfra(tree, {\n iac,\n nameClassName: agentNameClassName,\n nameKebabCase,\n policyPath,\n });\n if (iac === 'cdk') {\n for (const message of addGuardrailPolicySynthInputs(tree, [policyPath])) {\n logger.warn(message);\n }\n }\n wiringMessage = guardrailWiringMessage({\n iac,\n nameClassName: agentNameClassName,\n nameKebabCase,\n npmScope: getNpmScope(tree),\n });\n } else {\n logger.warn(\n `Agent ${agentTargetPrefix} in project ${project.name} has no infrastructure, so no guardrail infrastructure was generated. Create the guardrail yourself and write its ID and version to runtime config at agentcore.guardrails.${agentNameClassName} as { guardrailId, guardrailVersion }.`,\n );\n }\n\n addComponentGeneratorMetadata(\n tree,\n project.name,\n TS_AGENT_GUARDRAILS_GENERATOR_INFO,\n agentPath,\n agentTargetPrefix,\n );\n\n await addGeneratorMetricsIfApplicable(tree, [\n TS_AGENT_GUARDRAILS_GENERATOR_INFO,\n ]);\n\n await formatFilesInSubtree(tree);\n\n // Printed from the callback so it appears after Nx lists the changed\n // files, on every run, since the generator cannot tell whether the stack is\n // already wired.\n return () => {\n if (wiringMessage) {\n logger.info(wiringMessage);\n }\n };\n};\n\nexport default tsAgentGuardrailsGenerator;\n"],"names":["generateFiles","joinPathFragments","logger","OverwriteStrategy","addDestructuredImport","applyGritQL","matchGritQL","formatFilesInSubtree","addGuardrailInfra","guardrailWiringMessage","addGeneratorMetricsIfApplicable","esmVars","isEsmWorkspace","kebabCase","toClassName","getNpmScope","addComponentGeneratorMetadata","getGeneratorInfo","readProjectConfigurationUnqualified","TS_AGENT_GENERATOR_INFO","addGuardrailPolicySynthInputs","TS_AGENT_GUARDRAILS_GENERATOR_INFO","filename","resolveAgent","projectName","components","agent","agents","filter","c","generator","id","available","map","name","join","match","find","Error","length","addGuardrailedModelToAgent","tree","agentFile","guardrailImport","exists","warn","tsAgentGuardrailsGenerator","options","project","metadata","agentTargetPrefix","agentNameClassName","rc","nameKebabCase","agentPath","path","agentDir","root","dirname","agentConnectionImport","overwriteStrategy","KeepExisting","iac","wiringMessage","policyPath","nameClassName","message","npmScope","info"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../packages/nx-plugin/src/ts/agent/guardrails/generator.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n type GeneratorCallback,\n generateFiles,\n joinPathFragments,\n logger,\n OverwriteStrategy,\n type Tree,\n} from '@nx/devkit';\nimport {\n addDestructuredImport,\n applyGritQL,\n matchGritQL,\n} from '../../../utils/ast.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport {\n addGuardrailInfra,\n addGuardrailPolicy,\n} from '../../../utils/guardrail-constructs/guardrail-constructs.js';\nimport { guardrailWiringMessage } from '../../../utils/guardrail-constructs/guardrail-wiring.js';\nimport type { Iac } from '../../../utils/iac.js';\nimport { addGeneratorMetricsIfApplicable } from '../../../utils/metrics.js';\nimport { esmVars, isEsmWorkspace } from '../../../utils/module-format.js';\nimport { kebabCase, toClassName } from '../../../utils/names.js';\nimport { getNpmScope } from '../../../utils/npm-scope.js';\nimport {\n addComponentGeneratorMetadata,\n type ComponentMetadata,\n getGeneratorInfo,\n type NxGeneratorInfo,\n readProjectConfigurationUnqualified,\n} from '../../../utils/nx.js';\nimport { TS_AGENT_GENERATOR_INFO } from '../generator.js';\nimport type { TsAgentGuardrailsGeneratorSchema } from './schema';\nimport { addGuardrailPolicySynthInputs } from './synth-inputs.js';\n\nexport const TS_AGENT_GUARDRAILS_GENERATOR_INFO: NxGeneratorInfo =\n getGeneratorInfo(import.meta.filename);\n\n/**\n * Find the `ts#agent` component to guard, by name when given, otherwise the\n * project's only one.\n */\nconst resolveAgent = (\n projectName: string,\n components: ComponentMetadata[],\n agent?: string,\n): ComponentMetadata => {\n const agents = components.filter(\n (c) => c.generator === TS_AGENT_GENERATOR_INFO.id,\n );\n const available = agents.map((c) => c.name).join(', ') || 'none';\n if (agent) {\n const match = agents.find((c) => c.name === agent);\n if (!match) {\n throw new Error(\n `No TypeScript Agent named ${agent} in project ${projectName}. Available agents: ${available}`,\n );\n }\n return match;\n }\n if (agents.length !== 1) {\n throw new Error(\n agents.length === 0\n ? `Project ${projectName} has no TypeScript Agent. Add one with the ts#agent generator first.`\n : `Project ${projectName} has more than one TypeScript Agent. Specify which to guard with --agent. Available agents: ${available}`,\n );\n }\n return agents[0];\n};\n\n/**\n * Add `model: getGuardrailedModel()` to the `new Agent({...})` call in\n * agent.ts. An agent that already sets its own model is left unchanged, and\n * the lines to add by hand are logged instead.\n */\nconst addGuardrailedModelToAgent = async (\n tree: Tree,\n agentFile: string,\n): Promise<void> => {\n const guardrailImport = isEsmWorkspace(tree)\n ? './guardrail.js'\n : './guardrail';\n\n if (tree.exists(agentFile)) {\n if (await matchGritQL(tree, agentFile, '`getGuardrailedModel()`')) {\n return;\n }\n if (\n await applyGritQL(\n tree,\n agentFile,\n '`new Agent({ $props })` => `new Agent({ model: getGuardrailedModel(), $props })` where { $props <: not some or { `model: $_`, `model` } }',\n )\n ) {\n await addDestructuredImport(\n tree,\n agentFile,\n ['getGuardrailedModel'],\n './guardrail.js',\n );\n return;\n }\n }\n\n logger.warn(\n `Could not add the guardrail to ${agentFile}, because ${tree.exists(agentFile) ? 'its new Agent({...}) call already sets a model or is not an inline object' : 'the file does not exist'}. Set the model of your Agent to the guardrailed model by hand:\n\n model: getGuardrailedModel(),\n\nand import it:\n\n import { getGuardrailedModel } from '${guardrailImport}';`,\n );\n};\n\nexport const tsAgentGuardrailsGenerator = async (\n tree: Tree,\n options: TsAgentGuardrailsGeneratorSchema,\n): Promise<GeneratorCallback> => {\n const project = readProjectConfigurationUnqualified(tree, options.project);\n const agent = resolveAgent(\n project.name,\n (project.metadata as any)?.components ?? [],\n options.agent,\n );\n\n const agentTargetPrefix = agent.name ?? 'agent';\n const agentNameClassName = agent.rc ?? toClassName(agentTargetPrefix);\n const nameKebabCase = kebabCase(agentNameClassName);\n const agentPath = agent.path ?? joinPathFragments('src', agentTargetPrefix);\n const agentDir = joinPathFragments(project.root, agentPath);\n\n // The policy is the user's to edit, so a re-run never touches these files.\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files', 'agent'),\n agentDir,\n {\n ...esmVars(tree),\n agentConnectionImport: `@${getNpmScope(tree)}/agent-connection`,\n agentNameClassName,\n nameKebabCase,\n },\n { overwriteStrategy: OverwriteStrategy.KeepExisting },\n );\n addGuardrailPolicy(tree, agentDir);\n\n await addGuardrailedModelToAgent(\n tree,\n joinPathFragments(agentDir, 'agent.ts'),\n );\n\n const iac = agent.iac as Iac | undefined;\n let wiringMessage: string | undefined;\n if (iac) {\n const policyPath = joinPathFragments(agentDir, 'guardrail.json');\n await addGuardrailInfra(tree, {\n iac,\n nameClassName: agentNameClassName,\n nameKebabCase,\n policyPath,\n });\n if (iac === 'cdk') {\n for (const message of addGuardrailPolicySynthInputs(tree, [policyPath])) {\n logger.warn(message);\n }\n }\n wiringMessage = guardrailWiringMessage({\n iac,\n nameClassName: agentNameClassName,\n nameKebabCase,\n npmScope: getNpmScope(tree),\n });\n } else {\n logger.warn(\n `Agent ${agentTargetPrefix} in project ${project.name} has no infrastructure, so no guardrail infrastructure was generated. Create the guardrail yourself and write its ID and version to runtime config at agentcore.guardrails.${agentNameClassName} as { guardrailId, guardrailVersion }.`,\n );\n }\n\n addComponentGeneratorMetadata(\n tree,\n project.name,\n TS_AGENT_GUARDRAILS_GENERATOR_INFO,\n agentPath,\n agentTargetPrefix,\n );\n\n await addGeneratorMetricsIfApplicable(tree, [\n TS_AGENT_GUARDRAILS_GENERATOR_INFO,\n ]);\n\n await formatFilesInSubtree(tree);\n\n // Printed from the callback so it appears after Nx lists the changed\n // files, on every run, since the generator cannot tell whether the stack is\n // already wired.\n return () => {\n if (wiringMessage) {\n logger.info(wiringMessage);\n }\n };\n};\n\nexport default tsAgentGuardrailsGenerator;\n"],"names":["generateFiles","joinPathFragments","logger","OverwriteStrategy","addDestructuredImport","applyGritQL","matchGritQL","formatFilesInSubtree","addGuardrailInfra","addGuardrailPolicy","guardrailWiringMessage","addGeneratorMetricsIfApplicable","esmVars","isEsmWorkspace","kebabCase","toClassName","getNpmScope","addComponentGeneratorMetadata","getGeneratorInfo","readProjectConfigurationUnqualified","TS_AGENT_GENERATOR_INFO","addGuardrailPolicySynthInputs","TS_AGENT_GUARDRAILS_GENERATOR_INFO","filename","resolveAgent","projectName","components","agent","agents","filter","c","generator","id","available","map","name","join","match","find","Error","length","addGuardrailedModelToAgent","tree","agentFile","guardrailImport","exists","warn","tsAgentGuardrailsGenerator","options","project","metadata","agentTargetPrefix","agentNameClassName","rc","nameKebabCase","agentPath","path","agentDir","root","dirname","agentConnectionImport","overwriteStrategy","KeepExisting","iac","wiringMessage","policyPath","nameClassName","message","npmScope","info"],"mappings":"AAAA;;;CAGC,GACD,SAEEA,aAAa,EACbC,iBAAiB,EACjBC,MAAM,EACNC,iBAAiB,QAEZ,aAAa;AACpB,SACEC,qBAAqB,EACrBC,WAAW,EACXC,WAAW,QACN,wBAAwB;AAC/B,SAASC,oBAAoB,QAAQ,2BAA2B;AAChE,SACEC,iBAAiB,EACjBC,kBAAkB,QACb,8DAA8D;AACrE,SAASC,sBAAsB,QAAQ,0DAA0D;AAEjG,SAASC,+BAA+B,QAAQ,4BAA4B;AAC5E,SAASC,OAAO,EAAEC,cAAc,QAAQ,kCAAkC;AAC1E,SAASC,SAAS,EAAEC,WAAW,QAAQ,0BAA0B;AACjE,SAASC,WAAW,QAAQ,8BAA8B;AAC1D,SACEC,6BAA6B,EAE7BC,gBAAgB,EAEhBC,mCAAmC,QAC9B,uBAAuB;AAC9B,SAASC,uBAAuB,QAAQ,kBAAkB;AAE1D,SAASC,6BAA6B,QAAQ,oBAAoB;AAElE,OAAO,MAAMC,qCACXJ,iBAAiB,YAAYK,QAAQ,EAAE;AAEzC;;;CAGC,GACD,MAAMC,eAAe,CACnBC,aACAC,YACAC;IAEA,MAAMC,SAASF,WAAWG,MAAM,CAC9B,CAACC,IAAMA,EAAEC,SAAS,KAAKX,wBAAwBY,EAAE;IAEnD,MAAMC,YAAYL,OAAOM,GAAG,CAAC,CAACJ,IAAMA,EAAEK,IAAI,EAAEC,IAAI,CAAC,SAAS;IAC1D,IAAIT,OAAO;QACT,MAAMU,QAAQT,OAAOU,IAAI,CAAC,CAACR,IAAMA,EAAEK,IAAI,KAAKR;QAC5C,IAAI,CAACU,OAAO;YACV,MAAM,IAAIE,MACR,CAAC,0BAA0B,EAAEZ,MAAM,YAAY,EAAEF,YAAY,oBAAoB,EAAEQ,WAAW;QAElG;QACA,OAAOI;IACT;IACA,IAAIT,OAAOY,MAAM,KAAK,GAAG;QACvB,MAAM,IAAID,MACRX,OAAOY,MAAM,KAAK,IACd,CAAC,QAAQ,EAAEf,YAAY,oEAAoE,CAAC,GAC5F,CAAC,QAAQ,EAAEA,YAAY,4FAA4F,EAAEQ,WAAW;IAExI;IACA,OAAOL,MAAM,CAAC,EAAE;AAClB;AAEA;;;;CAIC,GACD,MAAMa,6BAA6B,OACjCC,MACAC;IAEA,MAAMC,kBAAkB/B,eAAe6B,QACnC,mBACA;IAEJ,IAAIA,KAAKG,MAAM,CAACF,YAAY;QAC1B,IAAI,MAAMrC,YAAYoC,MAAMC,WAAW,4BAA4B;YACjE;QACF;QACA,IACE,MAAMtC,YACJqC,MACAC,WACA,8IAEF;YACA,MAAMvC,sBACJsC,MACAC,WACA;gBAAC;aAAsB,EACvB;YAEF;QACF;IACF;IAEAzC,OAAO4C,IAAI,CACT,CAAC,+BAA+B,EAAEH,UAAU,UAAU,EAAED,KAAKG,MAAM,CAACF,aAAa,8EAA8E,0BAA0B;;;;;;uCAMtJ,EAAEC,gBAAgB,EAAE,CAAC;AAE5D;AAEA,OAAO,MAAMG,6BAA6B,OACxCL,MACAM;IAEA,MAAMC,UAAU9B,oCAAoCuB,MAAMM,QAAQC,OAAO;IACzE,MAAMtB,QAAQH,aACZyB,QAAQd,IAAI,EACZ,AAACc,QAAQC,QAAQ,EAAUxB,cAAc,EAAE,EAC3CsB,QAAQrB,KAAK;IAGf,MAAMwB,oBAAoBxB,MAAMQ,IAAI,IAAI;IACxC,MAAMiB,qBAAqBzB,MAAM0B,EAAE,IAAItC,YAAYoC;IACnD,MAAMG,gBAAgBxC,UAAUsC;IAChC,MAAMG,YAAY5B,MAAM6B,IAAI,IAAIvD,kBAAkB,OAAOkD;IACzD,MAAMM,WAAWxD,kBAAkBgD,QAAQS,IAAI,EAAEH;IAEjD,2EAA2E;IAC3EvD,cACE0C,MACAzC,kBAAkB,YAAY0D,OAAO,EAAE,SAAS,UAChDF,UACA;QACE,GAAG7C,QAAQ8B,KAAK;QAChBkB,uBAAuB,CAAC,CAAC,EAAE5C,YAAY0B,MAAM,iBAAiB,CAAC;QAC/DU;QACAE;IACF,GACA;QAAEO,mBAAmB1D,kBAAkB2D,YAAY;IAAC;IAEtDrD,mBAAmBiC,MAAMe;IAEzB,MAAMhB,2BACJC,MACAzC,kBAAkBwD,UAAU;IAG9B,MAAMM,MAAMpC,MAAMoC,GAAG;IACrB,IAAIC;IACJ,IAAID,KAAK;QACP,MAAME,aAAahE,kBAAkBwD,UAAU;QAC/C,MAAMjD,kBAAkBkC,MAAM;YAC5BqB;YACAG,eAAed;YACfE;YACAW;QACF;QACA,IAAIF,QAAQ,OAAO;YACjB,KAAK,MAAMI,WAAW9C,8BAA8BqB,MAAM;gBAACuB;aAAW,EAAG;gBACvE/D,OAAO4C,IAAI,CAACqB;YACd;QACF;QACAH,gBAAgBtD,uBAAuB;YACrCqD;YACAG,eAAed;YACfE;YACAc,UAAUpD,YAAY0B;QACxB;IACF,OAAO;QACLxC,OAAO4C,IAAI,CACT,CAAC,MAAM,EAAEK,kBAAkB,YAAY,EAAEF,QAAQd,IAAI,CAAC,2KAA2K,EAAEiB,mBAAmB,sCAAsC,CAAC;IAEjS;IAEAnC,8BACEyB,MACAO,QAAQd,IAAI,EACZb,oCACAiC,WACAJ;IAGF,MAAMxC,gCAAgC+B,MAAM;QAC1CpB;KACD;IAED,MAAMf,qBAAqBmC;IAE3B,qEAAqE;IACrE,4EAA4E;IAC5E,iBAAiB;IACjB,OAAO;QACL,IAAIsB,eAAe;YACjB9D,OAAOmE,IAAI,CAACL;QACd;IACF;AACF,EAAE;AAEF,eAAejB,2BAA2B"}
|
|
@@ -8,6 +8,10 @@ terraform {
|
|
|
8
8
|
source = "hashicorp/random"
|
|
9
9
|
version = "<%- randomProviderVersion %>"
|
|
10
10
|
}
|
|
11
|
+
time = {
|
|
12
|
+
source = "hashicorp/time"
|
|
13
|
+
version = "<%- timeProviderVersion %>"
|
|
14
|
+
}
|
|
11
15
|
}
|
|
12
16
|
}
|
|
13
17
|
|
|
@@ -72,12 +76,25 @@ resource "aws_bedrock_guardrail_version" "this" {
|
|
|
72
76
|
}
|
|
73
77
|
}
|
|
74
78
|
|
|
79
|
+
resource "time_sleep" "version_retention" {
|
|
80
|
+
destroy_duration = "15s"
|
|
81
|
+
|
|
82
|
+
triggers = {
|
|
83
|
+
guardrail_id = aws_bedrock_guardrail.this.guardrail_id
|
|
84
|
+
guardrail_version = aws_bedrock_guardrail_version.this.version
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
lifecycle {
|
|
88
|
+
create_before_destroy = true
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
75
92
|
module "add_guardrail_to_runtime_config" {
|
|
76
93
|
source = "../../../core/runtime-config/entry"
|
|
77
94
|
|
|
78
95
|
namespace = "agentcore"
|
|
79
96
|
key = "guardrails"
|
|
80
|
-
value = { "<%= nameClassName %>" = { guardrailId =
|
|
97
|
+
value = { "<%= nameClassName %>" = { guardrailId = time_sleep.version_retention.triggers.guardrail_id, guardrailVersion = time_sleep.version_retention.triggers.guardrail_version } }
|
|
81
98
|
}
|
|
82
99
|
|
|
83
100
|
output "guardrail_id" {
|
|
@@ -16,6 +16,11 @@ export interface AddGuardrailInfraOptions {
|
|
|
16
16
|
/** Workspace-root-relative path of the guardrail.json policy file. */
|
|
17
17
|
policyPath: string;
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Write the guardrail.json policy next to the agent. The policy is the
|
|
21
|
+
* user's to edit, so a re-run never touches an existing file.
|
|
22
|
+
*/
|
|
23
|
+
export declare const addGuardrailPolicy: (tree: Tree, agentDir: string) => void;
|
|
19
24
|
/**
|
|
20
25
|
* Add the Amazon Bedrock Guardrail infrastructure for the selected IaC
|
|
21
26
|
* provider: a CDK construct in the shared constructs project, or a Terraform
|
|
@@ -6,6 +6,14 @@ import { addStarExport } from "../ast.js";
|
|
|
6
6
|
import { esmVars } from "../module-format.js";
|
|
7
7
|
import { PACKAGES_DIR, SHARED_CONSTRUCTS_DIR, SHARED_TERRAFORM_DIR } from "../shared-constructs-constants.js";
|
|
8
8
|
import { terraformProviderVersions } from "../versions.js";
|
|
9
|
+
/**
|
|
10
|
+
* Write the guardrail.json policy next to the agent. The policy is the
|
|
11
|
+
* user's to edit, so a re-run never touches an existing file.
|
|
12
|
+
*/ export const addGuardrailPolicy = (tree, agentDir)=>{
|
|
13
|
+
generateFiles(tree, joinPathFragments(import.meta.dirname, 'files', 'policy'), agentDir, {}, {
|
|
14
|
+
overwriteStrategy: OverwriteStrategy.KeepExisting
|
|
15
|
+
});
|
|
16
|
+
};
|
|
9
17
|
/**
|
|
10
18
|
* Add the Amazon Bedrock Guardrail infrastructure for the selected IaC
|
|
11
19
|
* provider: a CDK construct in the shared constructs project, or a Terraform
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../packages/nx-plugin/src/utils/guardrail-constructs/guardrail-constructs.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n generateFiles,\n joinPathFragments,\n OverwriteStrategy,\n type Tree,\n} from '@nx/devkit';\nimport { addStarExport } from '../ast.js';\nimport type { Iac } from '../iac.js';\nimport { esmVars } from '../module-format.js';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../shared-constructs-constants.js';\nimport { terraformProviderVersions } from '../versions.js';\n\nexport interface AddGuardrailInfraOptions {\n iac: Iac;\n /** Class name of the guarded agent, eg `MyAgent` gives `MyAgentGuardrail`. */\n nameClassName: string;\n /**\n * Kebab-case name of the guarded agent, used for the directory and, with a\n * `-guardrail` suffix, the file.\n */\n nameKebabCase: string;\n /** Workspace-root-relative path of the guardrail.json policy file. */\n policyPath: string;\n}\n\n/**\n * Add the Amazon Bedrock Guardrail infrastructure for the selected IaC\n * provider: a CDK construct in the shared constructs project, or a Terraform\n * module in the shared Terraform project. Both read the policy file at synth\n * or plan time.\n *\n * Files are written with `KeepExisting`, so a re-run leaves them unchanged.\n * No build dependency is added on the agent's project, since the\n * infrastructure reads a source file and imports none of the agent's code;\n * for CDK the generator lists that file as a `synth` input instead, so the\n * infrastructure's cached synth follows it.\n */\nexport const addGuardrailInfra = async (\n tree: Tree,\n options: AddGuardrailInfraOptions,\n): Promise<void> => {\n const templateContext = {\n nameClassName: options.nameClassName,\n nameKebabCase: options.nameKebabCase,\n policyPath: options.policyPath,\n };\n\n switch (options.iac) {\n case 'cdk': {\n const appDir = joinPathFragments(\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n 'src',\n 'app',\n );\n generateFiles(\n tree,\n joinPathFragments(\n import.meta.dirname,\n 'files',\n 'cdk',\n 'app',\n 'guardrails',\n ),\n joinPathFragments(appDir, 'guardrails'),\n { ...templateContext, ...esmVars(tree) },\n { overwriteStrategy: OverwriteStrategy.KeepExisting },\n );\n await addStarExport(\n tree,\n joinPathFragments(appDir, 'guardrails', 'index.ts'),\n `./${options.nameKebabCase}/${options.nameKebabCase}-guardrail.js`,\n );\n await addStarExport(\n tree,\n joinPathFragments(appDir, 'index.ts'),\n './guardrails/index.js',\n );\n break;\n }\n case 'terraform':\n generateFiles(\n tree,\n joinPathFragments(\n import.meta.dirname,\n 'files',\n 'terraform',\n 'app',\n 'guardrails',\n ),\n joinPathFragments(\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n 'src',\n 'app',\n 'guardrails',\n ),\n {\n ...templateContext,\n // Guardrail names are at most 50 characters: the prefix, a hyphen\n // and the 8 hex characters of the random suffix.\n guardrailNamePrefix: `${options.nameKebabCase}-guardrail`.slice(\n 0,\n 41,\n ),\n ...terraformProviderVersions(),\n },\n { overwriteStrategy: OverwriteStrategy.KeepExisting },\n );\n break;\n }\n};\n"],"names":["generateFiles","joinPathFragments","OverwriteStrategy","addStarExport","esmVars","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","SHARED_TERRAFORM_DIR","terraformProviderVersions","
|
|
1
|
+
{"version":3,"sources":["../../../../../../packages/nx-plugin/src/utils/guardrail-constructs/guardrail-constructs.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n generateFiles,\n joinPathFragments,\n OverwriteStrategy,\n type Tree,\n} from '@nx/devkit';\nimport { addStarExport } from '../ast.js';\nimport type { Iac } from '../iac.js';\nimport { esmVars } from '../module-format.js';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../shared-constructs-constants.js';\nimport { terraformProviderVersions } from '../versions.js';\n\nexport interface AddGuardrailInfraOptions {\n iac: Iac;\n /** Class name of the guarded agent, eg `MyAgent` gives `MyAgentGuardrail`. */\n nameClassName: string;\n /**\n * Kebab-case name of the guarded agent, used for the directory and, with a\n * `-guardrail` suffix, the file.\n */\n nameKebabCase: string;\n /** Workspace-root-relative path of the guardrail.json policy file. */\n policyPath: string;\n}\n\n/**\n * Write the guardrail.json policy next to the agent. The policy is the\n * user's to edit, so a re-run never touches an existing file.\n */\nexport const addGuardrailPolicy = (tree: Tree, agentDir: string): void => {\n generateFiles(\n tree,\n joinPathFragments(import.meta.dirname, 'files', 'policy'),\n agentDir,\n {},\n { overwriteStrategy: OverwriteStrategy.KeepExisting },\n );\n};\n\n/**\n * Add the Amazon Bedrock Guardrail infrastructure for the selected IaC\n * provider: a CDK construct in the shared constructs project, or a Terraform\n * module in the shared Terraform project. Both read the policy file at synth\n * or plan time.\n *\n * Files are written with `KeepExisting`, so a re-run leaves them unchanged.\n * No build dependency is added on the agent's project, since the\n * infrastructure reads a source file and imports none of the agent's code;\n * for CDK the generator lists that file as a `synth` input instead, so the\n * infrastructure's cached synth follows it.\n */\nexport const addGuardrailInfra = async (\n tree: Tree,\n options: AddGuardrailInfraOptions,\n): Promise<void> => {\n const templateContext = {\n nameClassName: options.nameClassName,\n nameKebabCase: options.nameKebabCase,\n policyPath: options.policyPath,\n };\n\n switch (options.iac) {\n case 'cdk': {\n const appDir = joinPathFragments(\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n 'src',\n 'app',\n );\n generateFiles(\n tree,\n joinPathFragments(\n import.meta.dirname,\n 'files',\n 'cdk',\n 'app',\n 'guardrails',\n ),\n joinPathFragments(appDir, 'guardrails'),\n { ...templateContext, ...esmVars(tree) },\n { overwriteStrategy: OverwriteStrategy.KeepExisting },\n );\n await addStarExport(\n tree,\n joinPathFragments(appDir, 'guardrails', 'index.ts'),\n `./${options.nameKebabCase}/${options.nameKebabCase}-guardrail.js`,\n );\n await addStarExport(\n tree,\n joinPathFragments(appDir, 'index.ts'),\n './guardrails/index.js',\n );\n break;\n }\n case 'terraform':\n generateFiles(\n tree,\n joinPathFragments(\n import.meta.dirname,\n 'files',\n 'terraform',\n 'app',\n 'guardrails',\n ),\n joinPathFragments(\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n 'src',\n 'app',\n 'guardrails',\n ),\n {\n ...templateContext,\n // Guardrail names are at most 50 characters: the prefix, a hyphen\n // and the 8 hex characters of the random suffix.\n guardrailNamePrefix: `${options.nameKebabCase}-guardrail`.slice(\n 0,\n 41,\n ),\n ...terraformProviderVersions(),\n },\n { overwriteStrategy: OverwriteStrategy.KeepExisting },\n );\n break;\n }\n};\n"],"names":["generateFiles","joinPathFragments","OverwriteStrategy","addStarExport","esmVars","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","SHARED_TERRAFORM_DIR","terraformProviderVersions","addGuardrailPolicy","tree","agentDir","dirname","overwriteStrategy","KeepExisting","addGuardrailInfra","options","templateContext","nameClassName","nameKebabCase","policyPath","iac","appDir","guardrailNamePrefix","slice"],"mappings":"AAAA;;;CAGC,GACD,SACEA,aAAa,EACbC,iBAAiB,EACjBC,iBAAiB,QAEZ,aAAa;AACpB,SAASC,aAAa,QAAQ,YAAY;AAE1C,SAASC,OAAO,QAAQ,sBAAsB;AAC9C,SACEC,YAAY,EACZC,qBAAqB,EACrBC,oBAAoB,QACf,oCAAoC;AAC3C,SAASC,yBAAyB,QAAQ,iBAAiB;AAe3D;;;CAGC,GACD,OAAO,MAAMC,qBAAqB,CAACC,MAAYC;IAC7CX,cACEU,MACAT,kBAAkB,YAAYW,OAAO,EAAE,SAAS,WAChDD,UACA,CAAC,GACD;QAAEE,mBAAmBX,kBAAkBY,YAAY;IAAC;AAExD,EAAE;AAEF;;;;;;;;;;;CAWC,GACD,OAAO,MAAMC,oBAAoB,OAC/BL,MACAM;IAEA,MAAMC,kBAAkB;QACtBC,eAAeF,QAAQE,aAAa;QACpCC,eAAeH,QAAQG,aAAa;QACpCC,YAAYJ,QAAQI,UAAU;IAChC;IAEA,OAAQJ,QAAQK,GAAG;QACjB,KAAK;YAAO;gBACV,MAAMC,SAASrB,kBACbI,cACAC,uBACA,OACA;gBAEFN,cACEU,MACAT,kBACE,YAAYW,OAAO,EACnB,SACA,OACA,OACA,eAEFX,kBAAkBqB,QAAQ,eAC1B;oBAAE,GAAGL,eAAe;oBAAE,GAAGb,QAAQM,KAAK;gBAAC,GACvC;oBAAEG,mBAAmBX,kBAAkBY,YAAY;gBAAC;gBAEtD,MAAMX,cACJO,MACAT,kBAAkBqB,QAAQ,cAAc,aACxC,CAAC,EAAE,EAAEN,QAAQG,aAAa,CAAC,CAAC,EAAEH,QAAQG,aAAa,CAAC,aAAa,CAAC;gBAEpE,MAAMhB,cACJO,MACAT,kBAAkBqB,QAAQ,aAC1B;gBAEF;YACF;QACA,KAAK;YACHtB,cACEU,MACAT,kBACE,YAAYW,OAAO,EACnB,SACA,aACA,OACA,eAEFX,kBACEI,cACAE,sBACA,OACA,OACA,eAEF;gBACE,GAAGU,eAAe;gBAClB,kEAAkE;gBAClE,iDAAiD;gBACjDM,qBAAqB,GAAGP,QAAQG,aAAa,CAAC,UAAU,CAAC,CAACK,KAAK,CAC7D,GACA;gBAEF,GAAGhB,2BAA2B;YAChC,GACA;gBAAEK,mBAAmBX,kBAAkBY,YAAY;YAAC;YAEtD;IACJ;AACF,EAAE"}
|
|
@@ -11,9 +11,16 @@ export interface GuardrailWiringOptions {
|
|
|
11
11
|
nameKebabCase: string;
|
|
12
12
|
/** npm scope of the workspace without the `@`, eg `my-scope`. */
|
|
13
13
|
npmScope: string;
|
|
14
|
+
/**
|
|
15
|
+
* The agent guide section that shows how to deploy the agent's Terraform
|
|
16
|
+
* module. Defaults to the ts#agent guide.
|
|
17
|
+
*/
|
|
18
|
+
deployGuideUrl?: string;
|
|
14
19
|
}
|
|
15
20
|
/** The ts#agent guide section that shows how to deploy the agent's Terraform module. */
|
|
16
21
|
export declare const TS_AGENT_TERRAFORM_DEPLOY_GUIDE_URL = "https://awslabs.github.io/nx-plugin-for-aws/en/guides/ts-agent/#infrastructure-as-code";
|
|
22
|
+
/** The py#agent guide section that shows how to deploy the agent's Terraform module. */
|
|
23
|
+
export declare const PY_AGENT_TERRAFORM_DEPLOY_GUIDE_URL = "https://awslabs.github.io/nx-plugin-for-aws/en/guides/py-agent/#infrastructure-as-code";
|
|
17
24
|
/**
|
|
18
25
|
* The code a user adds to their CDK stack to guard the agent.
|
|
19
26
|
* The guide shows the same code for `my-agent` in scope `my-scope`, which
|