@aws/nx-plugin 1.0.0-rc.67 → 1.0.0-rc.69
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 +7 -0
- package/migrations.json +6 -1
- package/package.json +1 -1
- package/src/agentcore-harness/__snapshots__/generator.spec.ts.snap +238 -0
- package/src/agentcore-harness/files/project/README.md.template +83 -0
- package/src/agentcore-harness/files/project/scripts/chat.ts.template +99 -0
- package/src/agentcore-harness/files/project/src/PROMPT.md.template +16 -0
- package/src/agentcore-harness/generator.d.ts +62 -0
- package/src/agentcore-harness/generator.js +158 -0
- package/src/agentcore-harness/generator.js.map +1 -0
- package/src/agentcore-harness/schema.d.js +8 -0
- package/src/agentcore-harness/schema.d.js.map +1 -0
- package/src/agentcore-harness/schema.d.ts +17 -0
- package/src/agentcore-harness/schema.json +53 -0
- package/src/migrations/latest/translate-script-whole-file-writes/files/build-system-prompt.ts.fixture +23 -0
- package/src/migrations/latest/translate-script-whole-file-writes/files/build-user-prompt.ts.fixture +96 -0
- package/src/migrations/latest/translate-script-whole-file-writes/files/config-path.ts.fixture +1 -0
- package/src/migrations/latest/translate-script-whole-file-writes/files/file-to-translate.ts.fixture +10 -0
- package/src/migrations/latest/translate-script-whole-file-writes/files/get-files-to-translate.ts.fixture +96 -0
- package/src/migrations/latest/translate-script-whole-file-writes/files/header.ts.fixture +15 -0
- package/src/migrations/latest/translate-script-whole-file-writes/files/log.ts.fixture +18 -0
- package/src/migrations/latest/translate-script-whole-file-writes/files/main-call.ts.fixture +6 -0
- package/src/migrations/latest/translate-script-whole-file-writes/files/main.ts.fixture +79 -0
- package/src/migrations/latest/translate-script-whole-file-writes/files/project-root.ts.fixture +1 -0
- package/src/migrations/latest/translate-script-whole-file-writes/files/run-with-concurrency.ts.fixture +57 -0
- package/src/migrations/latest/translate-script-whole-file-writes/files/scripts-dir.ts.fixture +1 -0
- package/src/migrations/latest/translate-script-whole-file-writes/files/translate-file-for-language.ts.fixture +61 -0
- package/src/migrations/latest/translate-script-whole-file-writes/grit/already-migrated.grit +1 -0
- package/src/migrations/latest/translate-script-whole-file-writes/grit/build-system-prompt.grit +6 -0
- package/src/migrations/latest/translate-script-whole-file-writes/grit/build-user-prompt.grit +6 -0
- package/src/migrations/latest/translate-script-whole-file-writes/grit/config-path.grit +1 -0
- package/src/migrations/latest/translate-script-whole-file-writes/grit/file-to-translate.grit +11 -0
- package/src/migrations/latest/translate-script-whole-file-writes/grit/get-files-to-translate.grit +7 -0
- package/src/migrations/latest/translate-script-whole-file-writes/grit/log.grit +3 -0
- package/src/migrations/latest/translate-script-whole-file-writes/grit/main-call.grit +1 -0
- package/src/migrations/latest/translate-script-whole-file-writes/grit/main.grit +8 -0
- package/src/migrations/latest/translate-script-whole-file-writes/grit/project-root.grit +1 -0
- package/src/migrations/latest/translate-script-whole-file-writes/grit/run-with-concurrency.grit +6 -0
- package/src/migrations/latest/translate-script-whole-file-writes/grit/translate-file-for-language.grit +6 -0
- package/src/migrations/latest/translate-script-whole-file-writes/metadata.json +3 -0
- package/src/migrations/latest/translate-script-whole-file-writes/migration.d.ts +2 -0
- package/src/migrations/latest/translate-script-whole-file-writes/migration.js +103 -0
- package/src/migrations/latest/translate-script-whole-file-writes/migration.js.map +1 -0
- package/src/migrations/latest/translate-script-whole-file-writes/test-fixtures/released-script-first.ts.fixture +430 -0
- package/src/migrations/latest/translate-script-whole-file-writes/test-fixtures/released-script.ts.fixture +431 -0
- package/src/preset/__snapshots__/generator.spec.ts.snap +2 -0
- package/src/ts/astro-docs/files/translation/scripts/translate.ts.template +269 -148
- package/src/utils/agent-core-constructs/agent-core-constructs.d.ts +25 -0
- package/src/utils/agent-core-constructs/agent-core-constructs.js +59 -0
- package/src/utils/agent-core-constructs/agent-core-constructs.js.map +1 -1
- package/src/utils/agent-core-constructs/files/cdk/app/agentcore-harness/__nameKebabCase__/__nameKebabCase__.ts.template +366 -0
- package/src/utils/agent-core-constructs/files/terraform/app/agentcore-harness/__nameKebabCase__/__nameKebabCase__.tf.template +282 -0
package/README.md
CHANGED
|
@@ -177,6 +177,7 @@ pnpm nx g @aws/nx-plugin:ts#infra
|
|
|
177
177
|
| `py#lambda-function` | Python Lambda with type-safe event sources |
|
|
178
178
|
| `py#mcp-server` | MCP server (Python) |
|
|
179
179
|
| `py#agent` | [Strands Agent](https://strandsagents.com/) (Python) |
|
|
180
|
+
| `agentcore-harness` | [AgentCore Harness](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness.html) agent loop |
|
|
180
181
|
| `connection` | Connect projects together (e.g. frontend to API) |
|
|
181
182
|
| `terraform#project` | Terraform project |
|
|
182
183
|
| `license` | Manage LICENSE files and source headers |
|
package/generators.json
CHANGED
|
@@ -44,6 +44,13 @@
|
|
|
44
44
|
"metric": "g70",
|
|
45
45
|
"hidden": true
|
|
46
46
|
},
|
|
47
|
+
"agentcore-harness": {
|
|
48
|
+
"factory": "./src/agentcore-harness/generator",
|
|
49
|
+
"schema": "./src/agentcore-harness/schema.json",
|
|
50
|
+
"description": "Generate an AgentCore Harness project",
|
|
51
|
+
"metric": "g71",
|
|
52
|
+
"guidePages": ["agentcore-harness"]
|
|
53
|
+
},
|
|
47
54
|
"connection": {
|
|
48
55
|
"factory": "./src/connection/generator",
|
|
49
56
|
"schema": "./src/connection/schema.json",
|
package/migrations.json
CHANGED
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
"description": "Move Smithy APIs onto the renamed @smithy/server-* packages",
|
|
8
8
|
"implementation": "./src/migrations/latest/smithy-server-package-rename/migration"
|
|
9
9
|
},
|
|
10
|
+
"latest-translate-script-whole-file-writes": {
|
|
11
|
+
"version": "1.0.0-rc.68",
|
|
12
|
+
"description": "Update the docs translate script to write whole translated files rather than editing them in place",
|
|
13
|
+
"implementation": "./src/migrations/latest/translate-script-whole-file-writes/migration"
|
|
14
|
+
},
|
|
10
15
|
"v1.0.0-rc.50-0001-modernize-function-props-cast": {
|
|
11
16
|
"version": "1.0.0-rc.50",
|
|
12
17
|
"description": "Replace the legacy angle-bracket FunctionProps type assertion with the modern as syntax in generated API constructs",
|
|
@@ -103,7 +108,7 @@
|
|
|
103
108
|
"implementation": "./src/migrations/v1.0.0-rc.65/0002-rolldown-code-splitting/migration"
|
|
104
109
|
},
|
|
105
110
|
"sync-vended-versions": {
|
|
106
|
-
"version": "1.0.0-rc.
|
|
111
|
+
"version": "1.0.0-rc.69",
|
|
107
112
|
"description": "Sync vended dependency versions and the tracked plugin version to those vended by this release",
|
|
108
113
|
"implementation": "./src/utils/version-upgrade-migration/migration"
|
|
109
114
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
|
+
|
|
3
|
+
exports[`agentcore-harness generator > snapshot > snapshots the four rendered harness project files > packages/my-harness/README.md 1`] = `
|
|
4
|
+
"# @proj/my-harness
|
|
5
|
+
|
|
6
|
+
An [Amazon Bedrock AgentCore Harness](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness.html)
|
|
7
|
+
project generated with [@aws/nx-plugin](https://github.com/awslabs/nx-plugin-for-aws/).
|
|
8
|
+
A Harness is a managed agent loop that AWS runs for you, so this project
|
|
9
|
+
holds only the prompt that shapes it and a script for talking to it:
|
|
10
|
+
|
|
11
|
+
- \`src/PROMPT.md\` — the system prompt
|
|
12
|
+
- \`scripts/chat.ts\` — a multi-turn chat client for the deployed Harness
|
|
13
|
+
- \`project.json\` — declares the \`chat\` target
|
|
14
|
+
- \`README.md\` — this file
|
|
15
|
+
|
|
16
|
+
The Harness itself is deployed by the generated CDK construct or Terraform
|
|
17
|
+
module through your usual infrastructure workflow.
|
|
18
|
+
|
|
19
|
+
## The system prompt
|
|
20
|
+
|
|
21
|
+
\`src/PROMPT.md\` is the Harness system prompt. Your infrastructure reads the
|
|
22
|
+
file when you synthesize (CDK) or plan (Terraform), so edit it and redeploy
|
|
23
|
+
to change how the Harness behaves.
|
|
24
|
+
|
|
25
|
+
The prompt is deployment configuration rather than secret storage: its
|
|
26
|
+
contents appear in the synthesized CloudFormation template or in Terraform
|
|
27
|
+
state.
|
|
28
|
+
|
|
29
|
+
## Chatting with your Harness
|
|
30
|
+
|
|
31
|
+
\`\`\`bash
|
|
32
|
+
pnpm nx run @proj/my-harness:chat
|
|
33
|
+
\`\`\`
|
|
34
|
+
|
|
35
|
+
Every turn of a run shares one session, so the Harness keeps the
|
|
36
|
+
conversation context until you exit.
|
|
37
|
+
|
|
38
|
+
The Harness ARN is resolved in this order:
|
|
39
|
+
|
|
40
|
+
1. \`HARNESS_ARN\`, used directly:
|
|
41
|
+
|
|
42
|
+
\`\`\`bash
|
|
43
|
+
HARNESS_ARN=<harness-arn> pnpm nx run @proj/my-harness:chat
|
|
44
|
+
\`\`\`
|
|
45
|
+
|
|
46
|
+
2. \`RUNTIME_CONFIG_APP_ID\`, which reads the
|
|
47
|
+
\`agentcore.harnesses.MyHarness\` entry your deployed
|
|
48
|
+
infrastructure publishes:
|
|
49
|
+
|
|
50
|
+
\`\`\`bash
|
|
51
|
+
RUNTIME_CONFIG_APP_ID=<application-id> pnpm nx run @proj/my-harness:chat
|
|
52
|
+
\`\`\`
|
|
53
|
+
|
|
54
|
+
With neither set, the script fails with an error naming both. The AWS Region
|
|
55
|
+
is derived from the Harness ARN, and credentials come from the standard AWS
|
|
56
|
+
SDK credential provider chain.
|
|
57
|
+
|
|
58
|
+
A caller needs \`bedrock-agentcore:InvokeHarness\` and
|
|
59
|
+
\`bedrock-agentcore:InvokeAgentRuntime\` on the Harness ARN. The CDK
|
|
60
|
+
construct's \`grantInvokeAccess\` grants exactly those two actions.
|
|
61
|
+
|
|
62
|
+
## Customizing your Harness
|
|
63
|
+
|
|
64
|
+
Edit \`src/PROMPT.md\` for the system prompt. Everything else is configured
|
|
65
|
+
where you instantiate the infrastructure — including tools, which the
|
|
66
|
+
Harness deploys without:
|
|
67
|
+
|
|
68
|
+
- CDK construct props, for example
|
|
69
|
+
\`new MyHarness(this, 'MyHarness', { allowedTools: ['@builtin'] })\`
|
|
70
|
+
- Terraform, by setting \`allowed_tools = ["@builtin"]\` on the module's
|
|
71
|
+
\`aws_bedrockagentcore_harness\` resource, or by adding an \`allowed_tools\`
|
|
72
|
+
variable if you would rather set it as a module argument
|
|
73
|
+
|
|
74
|
+
The generated infrastructure is also yours to edit directly:
|
|
75
|
+
|
|
76
|
+
- \`packages/common/constructs/src/app/harnesses/my-harness/my-harness.ts\`
|
|
77
|
+
- \`packages/common/terraform/src/app/harnesses/my-harness/my-harness.tf\`
|
|
78
|
+
|
|
79
|
+
Re-running the generator only adds missing files, so your edits to these
|
|
80
|
+
files and to \`src/PROMPT.md\` are preserved.
|
|
81
|
+
|
|
82
|
+
## Further reading
|
|
83
|
+
|
|
84
|
+
- [AgentCore Harness guide](https://awslabs.github.io/nx-plugin-for-aws/guides/agentcore-harness/)
|
|
85
|
+
- [Amazon Bedrock AgentCore Harness documentation](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness.html)
|
|
86
|
+
- [Learn more about Nx](https://nx.dev/getting-started/intro)
|
|
87
|
+
"
|
|
88
|
+
`;
|
|
89
|
+
|
|
90
|
+
exports[`agentcore-harness generator > snapshot > snapshots the four rendered harness project files > packages/my-harness/project.json 1`] = `
|
|
91
|
+
"{
|
|
92
|
+
"name": "@proj/my-harness",
|
|
93
|
+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
94
|
+
"projectType": "application",
|
|
95
|
+
"sourceRoot": "packages/my-harness",
|
|
96
|
+
"tags": [],
|
|
97
|
+
"metadata": {
|
|
98
|
+
"generator": "agentcore-harness",
|
|
99
|
+
"name": "my-harness",
|
|
100
|
+
"rc": "MyHarness",
|
|
101
|
+
"auth": "iam",
|
|
102
|
+
"iac": "cdk"
|
|
103
|
+
},
|
|
104
|
+
"targets": {
|
|
105
|
+
"chat": {
|
|
106
|
+
"executor": "nx:run-commands",
|
|
107
|
+
"options": {
|
|
108
|
+
"commands": ["tsx ./scripts/chat.ts"],
|
|
109
|
+
"cwd": "{projectRoot}"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
"
|
|
115
|
+
`;
|
|
116
|
+
|
|
117
|
+
exports[`agentcore-harness generator > snapshot > snapshots the four rendered harness project files > packages/my-harness/scripts/chat.ts 1`] = `
|
|
118
|
+
"// Chat CLI for MyHarness. Invokes the deployed Harness with InvokeHarness.
|
|
119
|
+
import { randomUUID } from 'node:crypto';
|
|
120
|
+
import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig';
|
|
121
|
+
import {
|
|
122
|
+
BedrockAgentCoreClient,
|
|
123
|
+
InvokeHarnessCommand,
|
|
124
|
+
} from '@aws-sdk/client-bedrock-agentcore';
|
|
125
|
+
import { chatLoop, type ChatAdapter } from 'agent-chat-cli';
|
|
126
|
+
|
|
127
|
+
// The deployed infrastructure publishes the Harness ARN at
|
|
128
|
+
// \`agentcore.harnesses.MyHarness\`.
|
|
129
|
+
const HARNESS_RUNTIME_CONFIG_KEY = 'MyHarness';
|
|
130
|
+
|
|
131
|
+
// One session id per run, so every turn continues the same Harness session.
|
|
132
|
+
const SESSION_ID = randomUUID();
|
|
133
|
+
|
|
134
|
+
// Empty strings count as absent, so \`HARNESS_ARN= nx run ...\` still discovers.
|
|
135
|
+
const isPresent = (value: string | undefined): value is string =>
|
|
136
|
+
value !== undefined && value !== '';
|
|
137
|
+
|
|
138
|
+
// \`HARNESS_ARN\` wins; \`RUNTIME_CONFIG_APP_ID\` reads the runtime configuration.
|
|
139
|
+
const resolveHarnessArn = async (): Promise<string> => {
|
|
140
|
+
if (isPresent(process.env.HARNESS_ARN)) {
|
|
141
|
+
return process.env.HARNESS_ARN;
|
|
142
|
+
}
|
|
143
|
+
const application = process.env.RUNTIME_CONFIG_APP_ID;
|
|
144
|
+
if (!isPresent(application)) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
'No Harness ARN source is configured. Set HARNESS_ARN to the deployed ' +
|
|
147
|
+
'Harness ARN directly, or set RUNTIME_CONFIG_APP_ID to the AppConfig ' +
|
|
148
|
+
'application ID emitted by your infrastructure stack.',
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
const config = (await getAppConfig('agentcore', {
|
|
152
|
+
application,
|
|
153
|
+
environment: 'default',
|
|
154
|
+
transform: 'json',
|
|
155
|
+
})) as { harnesses?: Record<string, string | undefined> };
|
|
156
|
+
const harnessArn = config?.harnesses?.[HARNESS_RUNTIME_CONFIG_KEY];
|
|
157
|
+
if (!harnessArn) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
\`Runtime configuration for application '\${application}' has no value \` +
|
|
160
|
+
\`at 'agentcore.harnesses.\${HARNESS_RUNTIME_CONFIG_KEY}'. Deploy the \` +
|
|
161
|
+
'Harness infrastructure first, or set HARNESS_ARN directly.',
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
return harnessArn;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const HARNESS_ARN = await resolveHarnessArn();
|
|
168
|
+
|
|
169
|
+
// Region comes from the ARN, and credentials from the standard AWS SDK
|
|
170
|
+
// default provider chain, so no Region is embedded in this project.
|
|
171
|
+
const client = new BedrockAgentCoreClient({
|
|
172
|
+
region: HARNESS_ARN.split(':')[3],
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
class HarnessAdapter implements ChatAdapter {
|
|
176
|
+
async connect() {
|
|
177
|
+
return { agentName: 'MyHarness' };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async *sendMessage(text: string): AsyncIterable<string> {
|
|
181
|
+
const { stream } = await client.send(
|
|
182
|
+
new InvokeHarnessCommand({
|
|
183
|
+
harnessArn: HARNESS_ARN,
|
|
184
|
+
runtimeSessionId: SESSION_ID,
|
|
185
|
+
messages: [{ role: 'user', content: [{ text }] }],
|
|
186
|
+
}),
|
|
187
|
+
);
|
|
188
|
+
if (!stream) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
'AgentCore returned no event stream for the invocation. Confirm the ' +
|
|
191
|
+
'Harness is deployed and the caller has the ' +
|
|
192
|
+
'bedrock-agentcore:InvokeHarness permission.',
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
for await (const event of stream) {
|
|
196
|
+
const serviceError =
|
|
197
|
+
event.internalServerException ??
|
|
198
|
+
event.validationException ??
|
|
199
|
+
event.runtimeClientError;
|
|
200
|
+
if (serviceError) {
|
|
201
|
+
const detail = [serviceError.name, serviceError.message]
|
|
202
|
+
.filter(Boolean)
|
|
203
|
+
.join(': ');
|
|
204
|
+
throw new Error(
|
|
205
|
+
\`Harness invocation failed: \${detail || JSON.stringify(serviceError)}\`,
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
const chunk = event.contentBlockDelta?.delta?.text;
|
|
209
|
+
if (chunk) {
|
|
210
|
+
yield chunk;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
await chatLoop(new HarnessAdapter(), '');
|
|
217
|
+
"
|
|
218
|
+
`;
|
|
219
|
+
|
|
220
|
+
exports[`agentcore-harness generator > snapshot > snapshots the four rendered harness project files > packages/my-harness/src/PROMPT.md 1`] = `
|
|
221
|
+
"# MyHarness
|
|
222
|
+
|
|
223
|
+
You are a helpful AI assistant.
|
|
224
|
+
|
|
225
|
+
## Responding
|
|
226
|
+
|
|
227
|
+
- Lead with the answer, then the supporting detail.
|
|
228
|
+
- Keep the response proportional to the request: short answers to simple
|
|
229
|
+
questions, thorough answers to involved ones.
|
|
230
|
+
- Ask for the detail you need when a request is ambiguous instead of guessing.
|
|
231
|
+
- Say when you do not know something, and say what would settle it.
|
|
232
|
+
|
|
233
|
+
## Using tools
|
|
234
|
+
|
|
235
|
+
When tools are available, prefer them over recollection for anything you can
|
|
236
|
+
look up, and report what a tool returned, including when it fails.
|
|
237
|
+
"
|
|
238
|
+
`;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# <%- fullyQualifiedName %>
|
|
2
|
+
|
|
3
|
+
An [Amazon Bedrock AgentCore Harness](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness.html)
|
|
4
|
+
project generated with [@aws/nx-plugin](https://github.com/awslabs/nx-plugin-for-aws/).
|
|
5
|
+
A Harness is a managed agent loop that AWS runs for you, so this project
|
|
6
|
+
holds only the prompt that shapes it and a script for talking to it:
|
|
7
|
+
|
|
8
|
+
- `src/PROMPT.md` — the system prompt
|
|
9
|
+
- `scripts/chat.ts` — a multi-turn chat client for the deployed Harness
|
|
10
|
+
- `project.json` — declares the `chat` target
|
|
11
|
+
- `README.md` — this file
|
|
12
|
+
|
|
13
|
+
The Harness itself is deployed by the generated CDK construct or Terraform
|
|
14
|
+
module through your usual infrastructure workflow.
|
|
15
|
+
|
|
16
|
+
## The system prompt
|
|
17
|
+
|
|
18
|
+
`src/PROMPT.md` is the Harness system prompt. Your infrastructure reads the
|
|
19
|
+
file when you synthesize (CDK) or plan (Terraform), so edit it and redeploy
|
|
20
|
+
to change how the Harness behaves.
|
|
21
|
+
|
|
22
|
+
The prompt is deployment configuration rather than secret storage: its
|
|
23
|
+
contents appear in the synthesized CloudFormation template or in Terraform
|
|
24
|
+
state.
|
|
25
|
+
|
|
26
|
+
## Chatting with your Harness
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pnpm nx run <%- fullyQualifiedName %>:chat
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Every turn of a run shares one session, so the Harness keeps the
|
|
33
|
+
conversation context until you exit.
|
|
34
|
+
|
|
35
|
+
The Harness ARN is resolved in this order:
|
|
36
|
+
|
|
37
|
+
1. `HARNESS_ARN`, used directly:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
HARNESS_ARN=<harness-arn> pnpm nx run <%- fullyQualifiedName %>:chat
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
2. `RUNTIME_CONFIG_APP_ID`, which reads the
|
|
44
|
+
`agentcore.harnesses.<%- nameClassName %>` entry your deployed
|
|
45
|
+
infrastructure publishes:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
RUNTIME_CONFIG_APP_ID=<application-id> pnpm nx run <%- fullyQualifiedName %>:chat
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
With neither set, the script fails with an error naming both. The AWS Region
|
|
52
|
+
is derived from the Harness ARN, and credentials come from the standard AWS
|
|
53
|
+
SDK credential provider chain.
|
|
54
|
+
|
|
55
|
+
A caller needs `bedrock-agentcore:InvokeHarness` and
|
|
56
|
+
`bedrock-agentcore:InvokeAgentRuntime` on the Harness ARN. The CDK
|
|
57
|
+
construct's `grantInvokeAccess` grants exactly those two actions.
|
|
58
|
+
|
|
59
|
+
## Customizing your Harness
|
|
60
|
+
|
|
61
|
+
Edit `src/PROMPT.md` for the system prompt. Everything else is configured
|
|
62
|
+
where you instantiate the infrastructure — including tools, which the
|
|
63
|
+
Harness deploys without:
|
|
64
|
+
|
|
65
|
+
- CDK construct props, for example
|
|
66
|
+
`new <%- nameClassName %>(this, '<%- nameClassName %>', { allowedTools: ['@builtin'] })`
|
|
67
|
+
- Terraform, by setting `allowed_tools = ["@builtin"]` on the module's
|
|
68
|
+
`aws_bedrockagentcore_harness` resource, or by adding an `allowed_tools`
|
|
69
|
+
variable if you would rather set it as a module argument
|
|
70
|
+
|
|
71
|
+
The generated infrastructure is also yours to edit directly:
|
|
72
|
+
|
|
73
|
+
- `packages/common/constructs/src/app/harnesses/<%- nameKebabCase %>/<%- nameKebabCase %>.ts`
|
|
74
|
+
- `packages/common/terraform/src/app/harnesses/<%- nameKebabCase %>/<%- nameKebabCase %>.tf`
|
|
75
|
+
|
|
76
|
+
Re-running the generator only adds missing files, so your edits to these
|
|
77
|
+
files and to `src/PROMPT.md` are preserved.
|
|
78
|
+
|
|
79
|
+
## Further reading
|
|
80
|
+
|
|
81
|
+
- [AgentCore Harness guide](https://awslabs.github.io/nx-plugin-for-aws/guides/agentcore-harness/)
|
|
82
|
+
- [Amazon Bedrock AgentCore Harness documentation](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness.html)
|
|
83
|
+
- [Learn more about Nx](https://nx.dev/getting-started/intro)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Chat CLI for <%- nameClassName %>. Invokes the deployed Harness with InvokeHarness.
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig';
|
|
4
|
+
import {
|
|
5
|
+
BedrockAgentCoreClient,
|
|
6
|
+
InvokeHarnessCommand,
|
|
7
|
+
} from '@aws-sdk/client-bedrock-agentcore';
|
|
8
|
+
import { chatLoop, type ChatAdapter } from 'agent-chat-cli';
|
|
9
|
+
|
|
10
|
+
// The deployed infrastructure publishes the Harness ARN at
|
|
11
|
+
// `agentcore.harnesses.<%- nameClassName %>`.
|
|
12
|
+
const HARNESS_RUNTIME_CONFIG_KEY = '<%- nameClassName %>';
|
|
13
|
+
|
|
14
|
+
// One session id per run, so every turn continues the same Harness session.
|
|
15
|
+
const SESSION_ID = randomUUID();
|
|
16
|
+
|
|
17
|
+
// Empty strings count as absent, so `HARNESS_ARN= nx run ...` still discovers.
|
|
18
|
+
const isPresent = (value: string | undefined): value is string =>
|
|
19
|
+
value !== undefined && value !== '';
|
|
20
|
+
|
|
21
|
+
// `HARNESS_ARN` wins; `RUNTIME_CONFIG_APP_ID` reads the runtime configuration.
|
|
22
|
+
const resolveHarnessArn = async (): Promise<string> => {
|
|
23
|
+
if (isPresent(process.env.HARNESS_ARN)) {
|
|
24
|
+
return process.env.HARNESS_ARN;
|
|
25
|
+
}
|
|
26
|
+
const application = process.env.RUNTIME_CONFIG_APP_ID;
|
|
27
|
+
if (!isPresent(application)) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
'No Harness ARN source is configured. Set HARNESS_ARN to the deployed ' +
|
|
30
|
+
'Harness ARN directly, or set RUNTIME_CONFIG_APP_ID to the AppConfig ' +
|
|
31
|
+
'application ID emitted by your infrastructure stack.',
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
const config = (await getAppConfig('agentcore', {
|
|
35
|
+
application,
|
|
36
|
+
environment: 'default',
|
|
37
|
+
transform: 'json',
|
|
38
|
+
})) as { harnesses?: Record<string, string | undefined> };
|
|
39
|
+
const harnessArn = config?.harnesses?.[HARNESS_RUNTIME_CONFIG_KEY];
|
|
40
|
+
if (!harnessArn) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Runtime configuration for application '${application}' has no value ` +
|
|
43
|
+
`at 'agentcore.harnesses.${HARNESS_RUNTIME_CONFIG_KEY}'. Deploy the ` +
|
|
44
|
+
'Harness infrastructure first, or set HARNESS_ARN directly.',
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return harnessArn;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const HARNESS_ARN = await resolveHarnessArn();
|
|
51
|
+
|
|
52
|
+
// Region comes from the ARN, and credentials from the standard AWS SDK
|
|
53
|
+
// default provider chain, so no Region is embedded in this project.
|
|
54
|
+
const client = new BedrockAgentCoreClient({
|
|
55
|
+
region: HARNESS_ARN.split(':')[3],
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
class HarnessAdapter implements ChatAdapter {
|
|
59
|
+
async connect() {
|
|
60
|
+
return { agentName: '<%- nameClassName %>' };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async *sendMessage(text: string): AsyncIterable<string> {
|
|
64
|
+
const { stream } = await client.send(
|
|
65
|
+
new InvokeHarnessCommand({
|
|
66
|
+
harnessArn: HARNESS_ARN,
|
|
67
|
+
runtimeSessionId: SESSION_ID,
|
|
68
|
+
messages: [{ role: 'user', content: [{ text }] }],
|
|
69
|
+
}),
|
|
70
|
+
);
|
|
71
|
+
if (!stream) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
'AgentCore returned no event stream for the invocation. Confirm the ' +
|
|
74
|
+
'Harness is deployed and the caller has the ' +
|
|
75
|
+
'bedrock-agentcore:InvokeHarness permission.',
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
for await (const event of stream) {
|
|
79
|
+
const serviceError =
|
|
80
|
+
event.internalServerException ??
|
|
81
|
+
event.validationException ??
|
|
82
|
+
event.runtimeClientError;
|
|
83
|
+
if (serviceError) {
|
|
84
|
+
const detail = [serviceError.name, serviceError.message]
|
|
85
|
+
.filter(Boolean)
|
|
86
|
+
.join(': ');
|
|
87
|
+
throw new Error(
|
|
88
|
+
`Harness invocation failed: ${detail || JSON.stringify(serviceError)}`,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
const chunk = event.contentBlockDelta?.delta?.text;
|
|
92
|
+
if (chunk) {
|
|
93
|
+
yield chunk;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
await chatLoop(new HarnessAdapter(), '');
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# <%- nameClassName %>
|
|
2
|
+
|
|
3
|
+
You are a helpful AI assistant.
|
|
4
|
+
|
|
5
|
+
## Responding
|
|
6
|
+
|
|
7
|
+
- Lead with the answer, then the supporting detail.
|
|
8
|
+
- Keep the response proportional to the request: short answers to simple
|
|
9
|
+
questions, thorough answers to involved ones.
|
|
10
|
+
- Ask for the detail you need when a request is ambiguous instead of guessing.
|
|
11
|
+
- Say when you do not know something, and say what would settle it.
|
|
12
|
+
|
|
13
|
+
## Using tools
|
|
14
|
+
|
|
15
|
+
When tools are available, prefer them over recollection for anything you can
|
|
16
|
+
look up, and report what a tool returned, including when it fails.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import { type GeneratorCallback, type ProjectConfiguration, type Tree } from '@nx/devkit';
|
|
6
|
+
import { type NxGeneratorInfo } from '../utils/nx';
|
|
7
|
+
import type { IacMetadata } from '../utils/shared-constructs-constants';
|
|
8
|
+
import type { AgentcoreHarnessGeneratorSchema } from './schema';
|
|
9
|
+
export declare const DEPENDENCIES: import("../utils/declared-dependencies").DependencyDeclaration<readonly [{
|
|
10
|
+
readonly name: "@aws-sdk/client-bedrock-agentcore";
|
|
11
|
+
readonly root: true;
|
|
12
|
+
}, {
|
|
13
|
+
readonly name: "@aws-sdk/client-appconfigdata";
|
|
14
|
+
readonly root: true;
|
|
15
|
+
}, {
|
|
16
|
+
readonly name: "@aws-lambda-powertools/parameters";
|
|
17
|
+
readonly root: true;
|
|
18
|
+
}, {
|
|
19
|
+
readonly name: "agent-chat-cli";
|
|
20
|
+
readonly root: true;
|
|
21
|
+
}, {
|
|
22
|
+
readonly name: "@types/node";
|
|
23
|
+
readonly dev: true;
|
|
24
|
+
readonly root: true;
|
|
25
|
+
}, {
|
|
26
|
+
readonly name: "tsx";
|
|
27
|
+
readonly dev: true;
|
|
28
|
+
readonly root: true;
|
|
29
|
+
}, {
|
|
30
|
+
readonly name: "constructs";
|
|
31
|
+
readonly when: (m: IacMetadata) => boolean;
|
|
32
|
+
} & {
|
|
33
|
+
versionOnly: true;
|
|
34
|
+
}, {
|
|
35
|
+
readonly name: "aws-cdk-lib";
|
|
36
|
+
readonly when: (m: IacMetadata) => boolean;
|
|
37
|
+
} & {
|
|
38
|
+
versionOnly: true;
|
|
39
|
+
}, {
|
|
40
|
+
readonly name: "@types/node";
|
|
41
|
+
} & {
|
|
42
|
+
versionOnly: true;
|
|
43
|
+
}], readonly import("../utils/declared-dependencies").DeclaredPyDependency<"a2a-sdk" | "ag-ui-langgraph" | "ag-ui-protocol" | "ag-ui-strands" | "aws-lambda-powertools" | "aws-lambda-powertools[tracer]" | "aws-lambda-powertools[parser]" | "aws-opentelemetry-distro" | "bedrock-agentcore" | "boto3" | "checkov" | "fastapi" | "fastapi[standard]" | "httpx" | "langchain" | "langchain-aws" | "langchain-mcp-adapters" | "langgraph" | "mcp" | "pip-check-updates" | "pip-licenses" | "ruff" | "strands-agents" | "strands-agents[a2a]" | "strands-agents-tools" | "ty" | "pynamodb" | "uvicorn" | "sqlmodel" | "alembic" | "aiomysql" | "asyncpg" | "greenlet", AgentCoreHarnessMetadata>[]>;
|
|
44
|
+
export declare const AGENTCORE_HARNESS_GENERATOR_INFO: NxGeneratorInfo;
|
|
45
|
+
export declare const agentcoreHarnessGenerator: (tree: Tree, options: AgentcoreHarnessGeneratorSchema) => Promise<GeneratorCallback>;
|
|
46
|
+
/**
|
|
47
|
+
* Harness details stored in the Harness Project's metadata. `iac` records which
|
|
48
|
+
* provider's infrastructure was written, so the version sync can tell which
|
|
49
|
+
* shared constructs packages this project owns.
|
|
50
|
+
*/
|
|
51
|
+
export interface AgentCoreHarnessMetadata extends IacMetadata {
|
|
52
|
+
generator: string;
|
|
53
|
+
name: string;
|
|
54
|
+
rc: string;
|
|
55
|
+
auth: 'iam';
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Read a Harness Project's metadata, validating it was generated by the
|
|
59
|
+
* agentcore-harness generator before returning typed data.
|
|
60
|
+
*/
|
|
61
|
+
export declare const readAgentCoreHarnessMetadata: (project: ProjectConfiguration) => AgentCoreHarnessMetadata;
|
|
62
|
+
export default agentcoreHarnessGenerator;
|