@aws/agentcore 0.14.2 → 0.16.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.
Files changed (44) hide show
  1. package/README.md +24 -7
  2. package/dist/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap +65 -5
  3. package/dist/assets/cdk/bin/cdk.ts +37 -0
  4. package/dist/assets/cdk/lib/cdk-stack.ts +24 -5
  5. package/dist/assets/cdk/test/cdk.test.ts +1 -0
  6. package/dist/assets/datasets/predefined-v1.jsonl +3 -0
  7. package/dist/assets/datasets/simulated-v1.jsonl +3 -0
  8. package/dist/assets/harness/invoke.py.template +74 -0
  9. package/dist/cli/index.mjs +637 -572
  10. package/dist/lib/constants.d.ts +1 -0
  11. package/dist/lib/constants.d.ts.map +1 -1
  12. package/dist/lib/constants.js +3 -1
  13. package/dist/lib/constants.js.map +1 -1
  14. package/dist/lib/schemas/io/config-io.d.ts +9 -1
  15. package/dist/lib/schemas/io/config-io.d.ts.map +1 -1
  16. package/dist/lib/schemas/io/config-io.js +14 -0
  17. package/dist/lib/schemas/io/config-io.js.map +1 -1
  18. package/dist/lib/schemas/io/path-resolver.d.ts +12 -0
  19. package/dist/lib/schemas/io/path-resolver.d.ts.map +1 -1
  20. package/dist/lib/schemas/io/path-resolver.js +18 -0
  21. package/dist/lib/schemas/io/path-resolver.js.map +1 -1
  22. package/dist/schema/schemas/agentcore-project.d.ts +74 -0
  23. package/dist/schema/schemas/agentcore-project.d.ts.map +1 -1
  24. package/dist/schema/schemas/agentcore-project.js +82 -2
  25. package/dist/schema/schemas/agentcore-project.js.map +1 -1
  26. package/dist/schema/schemas/deployed-state.d.ts +76 -0
  27. package/dist/schema/schemas/deployed-state.d.ts.map +1 -1
  28. package/dist/schema/schemas/deployed-state.js +24 -1
  29. package/dist/schema/schemas/deployed-state.js.map +1 -1
  30. package/dist/schema/schemas/primitives/dataset.d.ts +48 -0
  31. package/dist/schema/schemas/primitives/dataset.d.ts.map +1 -0
  32. package/dist/schema/schemas/primitives/dataset.js +58 -0
  33. package/dist/schema/schemas/primitives/dataset.js.map +1 -0
  34. package/dist/schema/schemas/primitives/harness.d.ts +363 -0
  35. package/dist/schema/schemas/primitives/harness.d.ts.map +1 -0
  36. package/dist/schema/schemas/primitives/harness.js +261 -0
  37. package/dist/schema/schemas/primitives/harness.js.map +1 -0
  38. package/dist/schema/schemas/primitives/index.d.ts +4 -0
  39. package/dist/schema/schemas/primitives/index.d.ts.map +1 -1
  40. package/dist/schema/schemas/primitives/index.js +21 -1
  41. package/dist/schema/schemas/primitives/index.js.map +1 -1
  42. package/npm-shrinkwrap.json +184 -217
  43. package/package.json +11 -9
  44. package/scripts/bundle.mjs +86 -9
package/README.md CHANGED
@@ -135,12 +135,13 @@ agentcore invoke
135
135
 
136
136
  ### Utilities
137
137
 
138
- | Command | Description |
139
- | -------------- | ----------------------------------------- |
140
- | `validate` | Validate configuration files |
141
- | `package` | Package agent artifacts without deploying |
142
- | `fetch access` | Fetch access info for deployed resources |
143
- | `update` | Check for and install CLI updates |
138
+ | Command | Description |
139
+ | -------------- | ----------------------------------------------------- |
140
+ | `validate` | Validate configuration files |
141
+ | `package` | Package agent artifacts without deploying |
142
+ | `fetch access` | Fetch access info for deployed resources |
143
+ | `feedback` | Send feedback to the AgentCore team (with screenshot) |
144
+ | `update` | Check for and install CLI updates |
144
145
 
145
146
  ## Project Structure
146
147
 
@@ -191,10 +192,26 @@ Projects use JSON schema files in the `agentcore/` directory:
191
192
  - [Gateway](docs/gateway.md) - Gateway setup, targets, and authentication
192
193
  - [Memory](docs/memory.md) - Memory strategies and sharing
193
194
  - [Local Development](docs/local-development.md) - Dev server and debugging
195
+ - [Feedback](docs/feedback.md) - Submit feedback from your terminal
196
+
197
+ ## Examples
198
+
199
+ See the [AgentCore Samples](https://github.com/awslabs/agentcore-samples) repository for end-to-end examples using the
200
+ CLI, including multi-agent workflows, MCP gateway targets, and framework integrations.
194
201
 
195
202
  ## Feedback & Issues
196
203
 
197
- Found a bug or have a feature request? [Open an issue](https://github.com/aws/agentcore-cli/issues/new) on GitHub.
204
+ Have a quick comment or suggestion? Send it from your terminal:
205
+
206
+ ```bash
207
+ agentcore feedback "your message" --screenshot path/to/screenshot.png
208
+ ```
209
+
210
+ The CLI will display the AWS Customer Agreement and prompt for consent before submitting. See
211
+ [docs/feedback.md](docs/feedback.md) for usage details.
212
+
213
+ For bugs, regressions, or feature requests that need discussion,
214
+ [open an issue](https://github.com/aws/agentcore-cli/issues/new) on GitHub instead.
198
215
 
199
216
  ## Security
200
217
 
@@ -99,6 +99,42 @@ async function main() {
99
99
  throw new Error('No deployment targets configured. Please define targets in agentcore/aws-targets.json');
100
100
  }
101
101
 
102
+ // Read harness configs for role creation.
103
+ const projectRoot = path.resolve(configRoot, '..');
104
+ const harnessConfigs: {
105
+ name: string;
106
+ executionRoleArn?: string;
107
+ memoryName?: string;
108
+ containerUri?: string;
109
+ hasDockerfile?: boolean;
110
+ dockerfile?: string;
111
+ codeLocation?: string;
112
+ tools?: { type: string; name: string }[];
113
+ apiKeyArn?: string;
114
+ }[] = [];
115
+ for (const entry of specAny.harnesses ?? []) {
116
+ const harnessDir = path.resolve(projectRoot, entry.path);
117
+ const harnessPath = path.resolve(harnessDir, 'harness.json');
118
+ try {
119
+ const harnessSpec = JSON.parse(fs.readFileSync(harnessPath, 'utf-8'));
120
+ harnessConfigs.push({
121
+ name: entry.name,
122
+ executionRoleArn: harnessSpec.executionRoleArn,
123
+ memoryName: harnessSpec.memory?.name,
124
+ containerUri: harnessSpec.containerUri,
125
+ hasDockerfile: !!harnessSpec.dockerfile,
126
+ dockerfile: harnessSpec.dockerfile,
127
+ codeLocation: harnessSpec.dockerfile ? harnessDir : undefined,
128
+ tools: harnessSpec.tools,
129
+ apiKeyArn: harnessSpec.model?.apiKeyArn,
130
+ });
131
+ } catch (err) {
132
+ throw new Error(
133
+ \`Could not read harness.json for "\${entry.name}" at \${harnessPath}: \${err instanceof Error ? err.message : err}\`
134
+ );
135
+ }
136
+ }
137
+
102
138
  const app = new App();
103
139
 
104
140
  for (const target of targets) {
@@ -118,6 +154,7 @@ async function main() {
118
154
  spec,
119
155
  mcpSpec,
120
156
  credentials,
157
+ harnesses: harnessConfigs.length > 0 ? harnessConfigs : undefined,
121
158
  env,
122
159
  description: \`AgentCore stack for \${spec.name} deployed to \${target.name} (\${target.region})\`,
123
160
  tags: {
@@ -265,6 +302,18 @@ exports[`Assets Directory Snapshots > CDK assets > cdk/cdk/lib/cdk-stack.ts shou
265
302
  import { CfnOutput, Stack, type StackProps } from 'aws-cdk-lib';
266
303
  import { Construct } from 'constructs';
267
304
 
305
+ export interface HarnessConfig {
306
+ name: string;
307
+ executionRoleArn?: string;
308
+ memoryName?: string;
309
+ containerUri?: string;
310
+ hasDockerfile?: boolean;
311
+ dockerfile?: string;
312
+ codeLocation?: string;
313
+ tools?: { type: string; name: string }[];
314
+ apiKeyArn?: string;
315
+ }
316
+
268
317
  export interface AgentCoreStackProps extends StackProps {
269
318
  /**
270
319
  * The AgentCore project specification containing agents, memories, and credentials.
@@ -278,6 +327,10 @@ export interface AgentCoreStackProps extends StackProps {
278
327
  * Credential provider ARNs from deployed state, keyed by credential name.
279
328
  */
280
329
  credentials?: Record<string, { credentialProviderArn: string; clientSecretArn?: string }>;
330
+ /**
331
+ * Harness role configurations.
332
+ */
333
+ harnesses?: HarnessConfig[];
281
334
  }
282
335
 
283
336
  /**
@@ -293,12 +346,15 @@ export class AgentCoreStack extends Stack {
293
346
  constructor(scope: Construct, id: string, props: AgentCoreStackProps) {
294
347
  super(scope, id, props);
295
348
 
296
- const { spec, mcpSpec, credentials } = props;
349
+ const { spec, mcpSpec, credentials, harnesses } = props;
297
350
 
298
- // Create AgentCoreApplication with all agents
299
- this.application = new AgentCoreApplication(this, 'Application', {
300
- spec,
301
- });
351
+ // Create AgentCoreApplication with all agents and harness roles
352
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
353
+ const appProps: Record<string, unknown> = { spec };
354
+ if (harnesses?.length) {
355
+ appProps.harnesses = harnesses;
356
+ }
357
+ this.application = new AgentCoreApplication(this, 'Application', appProps as any);
302
358
 
303
359
  // Create AgentCoreMcp if there are gateways configured
304
360
  if (mcpSpec?.agentCoreGateways && mcpSpec.agentCoreGateways.length > 0) {
@@ -386,6 +442,7 @@ test('AgentCoreStack synthesizes with empty spec', () => {
386
442
  agentCoreGateways: [],
387
443
  mcpRuntimeTools: [],
388
444
  unassignedTargets: [],
445
+ datasets: [],
389
446
  },
390
447
  });
391
448
  const template = Template.fromStack(stack);
@@ -448,9 +505,12 @@ exports[`Assets Directory Snapshots > File listing > should match the expected f
448
505
  "container/python/dockerignore.template",
449
506
  "container/typescript/Dockerfile",
450
507
  "container/typescript/dockerignore.template",
508
+ "datasets/predefined-v1.jsonl",
509
+ "datasets/simulated-v1.jsonl",
451
510
  "evaluators/python-lambda/execution-role-policy.json",
452
511
  "evaluators/python-lambda/lambda_function.py",
453
512
  "evaluators/python-lambda/pyproject.toml",
513
+ "harness/invoke.py.template",
454
514
  "mcp/python-lambda/README.md",
455
515
  "mcp/python-lambda/handler.py",
456
516
  "mcp/python-lambda/pyproject.toml",
@@ -54,6 +54,42 @@ async function main() {
54
54
  throw new Error('No deployment targets configured. Please define targets in agentcore/aws-targets.json');
55
55
  }
56
56
 
57
+ // Read harness configs for role creation.
58
+ const projectRoot = path.resolve(configRoot, '..');
59
+ const harnessConfigs: {
60
+ name: string;
61
+ executionRoleArn?: string;
62
+ memoryName?: string;
63
+ containerUri?: string;
64
+ hasDockerfile?: boolean;
65
+ dockerfile?: string;
66
+ codeLocation?: string;
67
+ tools?: { type: string; name: string }[];
68
+ apiKeyArn?: string;
69
+ }[] = [];
70
+ for (const entry of specAny.harnesses ?? []) {
71
+ const harnessDir = path.resolve(projectRoot, entry.path);
72
+ const harnessPath = path.resolve(harnessDir, 'harness.json');
73
+ try {
74
+ const harnessSpec = JSON.parse(fs.readFileSync(harnessPath, 'utf-8'));
75
+ harnessConfigs.push({
76
+ name: entry.name,
77
+ executionRoleArn: harnessSpec.executionRoleArn,
78
+ memoryName: harnessSpec.memory?.name,
79
+ containerUri: harnessSpec.containerUri,
80
+ hasDockerfile: !!harnessSpec.dockerfile,
81
+ dockerfile: harnessSpec.dockerfile,
82
+ codeLocation: harnessSpec.dockerfile ? harnessDir : undefined,
83
+ tools: harnessSpec.tools,
84
+ apiKeyArn: harnessSpec.model?.apiKeyArn,
85
+ });
86
+ } catch (err) {
87
+ throw new Error(
88
+ `Could not read harness.json for "${entry.name}" at ${harnessPath}: ${err instanceof Error ? err.message : err}`
89
+ );
90
+ }
91
+ }
92
+
57
93
  const app = new App();
58
94
 
59
95
  for (const target of targets) {
@@ -73,6 +109,7 @@ async function main() {
73
109
  spec,
74
110
  mcpSpec,
75
111
  credentials,
112
+ harnesses: harnessConfigs.length > 0 ? harnessConfigs : undefined,
76
113
  env,
77
114
  description: `AgentCore stack for ${spec.name} deployed to ${target.name} (${target.region})`,
78
115
  tags: {
@@ -7,6 +7,18 @@ import {
7
7
  import { CfnOutput, Stack, type StackProps } from 'aws-cdk-lib';
8
8
  import { Construct } from 'constructs';
9
9
 
10
+ export interface HarnessConfig {
11
+ name: string;
12
+ executionRoleArn?: string;
13
+ memoryName?: string;
14
+ containerUri?: string;
15
+ hasDockerfile?: boolean;
16
+ dockerfile?: string;
17
+ codeLocation?: string;
18
+ tools?: { type: string; name: string }[];
19
+ apiKeyArn?: string;
20
+ }
21
+
10
22
  export interface AgentCoreStackProps extends StackProps {
11
23
  /**
12
24
  * The AgentCore project specification containing agents, memories, and credentials.
@@ -20,6 +32,10 @@ export interface AgentCoreStackProps extends StackProps {
20
32
  * Credential provider ARNs from deployed state, keyed by credential name.
21
33
  */
22
34
  credentials?: Record<string, { credentialProviderArn: string; clientSecretArn?: string }>;
35
+ /**
36
+ * Harness role configurations.
37
+ */
38
+ harnesses?: HarnessConfig[];
23
39
  }
24
40
 
25
41
  /**
@@ -35,12 +51,15 @@ export class AgentCoreStack extends Stack {
35
51
  constructor(scope: Construct, id: string, props: AgentCoreStackProps) {
36
52
  super(scope, id, props);
37
53
 
38
- const { spec, mcpSpec, credentials } = props;
54
+ const { spec, mcpSpec, credentials, harnesses } = props;
39
55
 
40
- // Create AgentCoreApplication with all agents
41
- this.application = new AgentCoreApplication(this, 'Application', {
42
- spec,
43
- });
56
+ // Create AgentCoreApplication with all agents and harness roles
57
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
+ const appProps: Record<string, unknown> = { spec };
59
+ if (harnesses?.length) {
60
+ appProps.harnesses = harnesses;
61
+ }
62
+ this.application = new AgentCoreApplication(this, 'Application', appProps as any);
44
63
 
45
64
  // Create AgentCoreMcp if there are gateways configured
46
65
  if (mcpSpec?.agentCoreGateways && mcpSpec.agentCoreGateways.length > 0) {
@@ -18,6 +18,7 @@ test('AgentCoreStack synthesizes with empty spec', () => {
18
18
  agentCoreGateways: [],
19
19
  mcpRuntimeTools: [],
20
20
  unassignedTargets: [],
21
+ datasets: [],
21
22
  },
22
23
  });
23
24
  const template = Template.fromStack(stack);
@@ -0,0 +1,3 @@
1
+ {"scenario_id": "refund-policy", "turns": [{"input": "What is your refund policy?", "expectedResponse": "We offer full refunds within 30 days of purchase. After 30 days, we can provide store credit."}, {"input": "What if I lost my receipt?", "expectedResponse": "No problem! We can look up your purchase using your email address or payment method."}], "assertions": ["Agent should clearly state the 30-day refund window", "Agent should offer alternatives for lost receipts"], "expected_trajectory": ["lookup_policy", "check_eligibility"]}
2
+ {"scenario_id": "order-tracking", "turns": [{"input": "Where is my order #12345?", "expectedResponse": "Let me look up order #12345 for you. I can see it shipped on Monday and is expected to arrive by Thursday."}], "assertions": ["Agent should reference the specific order number", "Agent should provide estimated delivery date"], "expected_trajectory": ["lookup_order", "get_shipping_status"]}
3
+ {"scenario_id": "account-locked", "turns": [{"input": "I can't log into my account", "expectedResponse": "I'm sorry you're having trouble logging in. Let me help you regain access. Can you provide the email address associated with your account?"}, {"input": "It's john@example.com", "expectedResponse": "I've sent a password reset link to john@example.com. Please check your inbox and spam folder. The link expires in 24 hours."}], "assertions": ["Agent should ask for identifying information", "Agent should explain the reset process clearly"], "expected_trajectory": ["verify_identity", "send_reset_link"]}
@@ -0,0 +1,3 @@
1
+ {"scenario_id": "frustrated-refund-customer", "input": "I want a refund for my cancelled flight BK-98765", "actor_profile": {"traits": {"personality": "impatient and frustrated", "communication_style": "direct and demanding"}, "context": "Has been waiting 3 days for a refund with no response. Previously had a bad experience with customer service.", "goal": "Get a full cash refund for cancelled flight BK-98765, not a voucher or credit"}, "max_turns": 10, "assertions": ["Agent should acknowledge the frustration", "Agent should not offer only vouchers when cash refund is requested", "Agent should provide a timeline for the refund"]}
2
+ {"scenario_id": "confused-new-user", "input": "How do I set up my new account?", "actor_profile": {"traits": {"personality": "polite but confused", "communication_style": "asks many follow-up questions", "technical_level": "beginner"}, "context": "First time using the service. Not familiar with technical terminology. English is a second language.", "goal": "Successfully create and configure a new account with basic settings"}, "max_turns": 15, "assertions": ["Agent should use simple non-technical language", "Agent should break instructions into small steps", "Agent should confirm understanding at each step"]}
3
+ {"scenario_id": "edge-case-multi-issue", "input": "I need to change my flight AND get a refund for the hotel that was bundled with it", "actor_profile": {"traits": {"personality": "methodical and detail-oriented", "communication_style": "provides lots of context upfront"}, "context": "Has a bundled flight+hotel booking. Flight was changed by the airline, making the hotel dates wrong. Wants partial refund for hotel and rebooking for flight.", "goal": "Get the flight rebooked to new dates AND get a refund for the hotel nights that no longer align"}, "max_turns": 12, "assertions": ["Agent should handle both issues without losing track", "Agent should clarify which parts are refundable", "Agent should not close the conversation until both issues are resolved"]}
@@ -0,0 +1,74 @@
1
+ """
2
+ Standalone invoke script for AgentCore Harness.
3
+ Generated by: agentcore create --with-invoke-script
4
+
5
+ Usage:
6
+ pip install boto3
7
+ export HARNESS_ARN="arn:aws:bedrock-agentcore:<region>:<account>:harness/<harness-id>"
8
+ python invoke.py "Hello, what can you do?"
9
+ python invoke.py --raw-events "Hello"
10
+ """
11
+
12
+ import argparse
13
+ import json
14
+ import os
15
+ import sys
16
+ import uuid
17
+
18
+ import boto3
19
+
20
+ # --- Configuration ---
21
+ HARNESS_ARN = os.environ.get("HARNESS_ARN", "{{HARNESS_ARN}}")
22
+ REGION = os.environ.get("AWS_REGION", "{{REGION}}")
23
+ SESSION_ID = os.environ.get("SESSION_ID", str(uuid.uuid4()))
24
+
25
+ parser = argparse.ArgumentParser(description="Invoke an AgentCore Harness")
26
+ parser.add_argument("prompt", nargs="?", default="Hello!", help="Prompt to send to the agent")
27
+ parser.add_argument("--raw-events", action="store_true", help="Print raw streaming events as JSON")
28
+ parser.add_argument("--session-id", default=SESSION_ID, help="Session ID for conversation continuity")
29
+ args = parser.parse_args()
30
+
31
+ client = boto3.client("bedrock-agentcore", region_name=REGION)
32
+
33
+ response = client.invoke_harness(
34
+ harnessArn=HARNESS_ARN,
35
+ runtimeSessionId=args.session_id,
36
+ messages=[
37
+ {
38
+ "role": "user",
39
+ "content": [{"text": args.prompt}],
40
+ }
41
+ ],
42
+ )
43
+
44
+ for event in response["stream"]:
45
+ if args.raw_events:
46
+ print(json.dumps(event, default=str))
47
+ else:
48
+ if "contentBlockStart" in event:
49
+ start = event["contentBlockStart"].get("start", {})
50
+ if "toolUse" in start:
51
+ tool = start["toolUse"]
52
+ print(f"\n🔧 Tool: {tool.get('name', 'unknown')}", flush=True)
53
+ elif "contentBlockDelta" in event:
54
+ delta = event["contentBlockDelta"].get("delta", {})
55
+ if "text" in delta:
56
+ print(delta["text"], end="", flush=True)
57
+ elif "messageStop" in event:
58
+ stop_reason = event["messageStop"].get("stopReason", "")
59
+ if stop_reason == "end_turn":
60
+ print()
61
+ elif "metadata" in event:
62
+ usage = event["metadata"].get("usage", {})
63
+ metrics = event["metadata"].get("metrics", {})
64
+ latency = metrics.get("latencyMs", 0) / 1000
65
+ print(
66
+ f"\n⚡ {usage.get('inputTokens', 0)} in · "
67
+ f"{usage.get('outputTokens', 0)} out · "
68
+ f"{latency:.1f}s",
69
+ file=sys.stderr,
70
+ )
71
+ elif "internalServerException" in event:
72
+ print(f"\nError: {event['internalServerException']}", file=sys.stderr)
73
+
74
+ print(f"\n🔗 Session: {args.session_id}", file=sys.stderr)