@aws/agentcore 1.0.0-preview.16 → 1.0.0-preview.18

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 (40) hide show
  1. package/README.md +1 -7
  2. package/dist/agent-inspector/index.css +1 -1
  3. package/dist/agent-inspector/index.js +60 -60
  4. package/dist/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap +273 -15
  5. package/dist/assets/cdk/bin/cdk.ts +2 -8
  6. package/dist/assets/python/http/strands/base/main.py +13 -3
  7. package/dist/assets/python/http/strands/base/mcp_client/client.py +4 -1
  8. package/dist/assets/python/http/strands/base/model/load.py +116 -0
  9. package/dist/assets/python/http/strands/base/model/mantle_compat.py +21 -0
  10. package/dist/assets/python/http/strands/base/pyproject.toml +3 -0
  11. package/dist/assets/typescript/http/strands/base/main.ts +48 -0
  12. package/dist/assets/typescript/http/strands/base/package.json +3 -2
  13. package/dist/assets/typescript/http/strands/capabilities/memory/memory.ts +52 -0
  14. package/dist/cli/index.mjs +572 -606
  15. package/dist/schema/constants.d.ts +3 -2
  16. package/dist/schema/constants.d.ts.map +1 -1
  17. package/dist/schema/constants.js +7 -4
  18. package/dist/schema/constants.js.map +1 -1
  19. package/dist/schema/schemas/agent-env.d.ts +43 -0
  20. package/dist/schema/schemas/agent-env.d.ts.map +1 -1
  21. package/dist/schema/schemas/agent-env.js +11 -0
  22. package/dist/schema/schemas/agent-env.js.map +1 -1
  23. package/dist/schema/schemas/agentcore-project.d.ts +43 -0
  24. package/dist/schema/schemas/agentcore-project.d.ts.map +1 -1
  25. package/dist/schema/schemas/connections.d.ts +185 -0
  26. package/dist/schema/schemas/connections.d.ts.map +1 -0
  27. package/dist/schema/schemas/connections.js +176 -0
  28. package/dist/schema/schemas/connections.js.map +1 -0
  29. package/dist/schema/schemas/index.d.ts +1 -0
  30. package/dist/schema/schemas/index.d.ts.map +1 -1
  31. package/dist/schema/schemas/index.js +1 -0
  32. package/dist/schema/schemas/index.js.map +1 -1
  33. package/dist/schema/schemas/primitives/harness.d.ts +42 -0
  34. package/dist/schema/schemas/primitives/harness.d.ts.map +1 -1
  35. package/dist/schema/schemas/primitives/harness.js +4 -0
  36. package/dist/schema/schemas/primitives/harness.js.map +1 -1
  37. package/npm-shrinkwrap.json +6 -6
  38. package/package.json +2 -3
  39. package/scripts/bundle.mjs +12 -60
  40. package/scripts/run-e2e-local.sh +0 -5
@@ -124,15 +124,9 @@ async function main() {
124
124
  }
125
125
  }
126
126
 
127
- // Harness is preview-gated. The CLI bundle bakes the preview flag at build time and
128
- // forwards it to this child process via AGENTCORE_PREVIEW (see toolkit-lib/wrapper.ts).
129
- // This app is built separately and cannot see that build-time define, so it gates on the
130
- // env var. Absent/anything-but-'1' defaults to off so a stale harnesses[] entry in a
131
- // non-preview build never synthesizes an AWS::BedrockAgentCore::Harness resource.
132
- const previewEnabled = process.env.AGENTCORE_PREVIEW === '1';
133
-
127
+ // Synthesize an AWS::BedrockAgentCore::Harness resource for each harness entry in the spec.
134
128
  const harnessConfigs: HarnessConfig[] = [];
135
- for (const entry of previewEnabled ? (specAny.harnesses ?? []) : []) {
129
+ for (const entry of specAny.harnesses ?? []) {
136
130
  const harnessDir = path.resolve(projectRoot, entry.path);
137
131
  const harnessPath = path.resolve(harnessDir, 'harness.json');
138
132
  try {
@@ -837,6 +831,7 @@ exports[`Assets Directory Snapshots > File listing > should match the expected f
837
831
  "python/http/strands/base/mcp_client/client.py",
838
832
  "python/http/strands/base/model/__init__.py",
839
833
  "python/http/strands/base/model/load.py",
834
+ "python/http/strands/base/model/mantle_compat.py",
840
835
  "python/http/strands/base/pyproject.toml",
841
836
  "python/http/strands/base/skills/fetcher.py",
842
837
  "python/http/strands/capabilities/execution-limits/hooks/execution_limits.py",
@@ -856,6 +851,7 @@ exports[`Assets Directory Snapshots > File listing > should match the expected f
856
851
  "typescript/http/strands/base/model/load.ts",
857
852
  "typescript/http/strands/base/package.json",
858
853
  "typescript/http/strands/base/tsconfig.json",
854
+ "typescript/http/strands/capabilities/memory/memory.ts",
859
855
  "typescript/http/vercelai/base/README.md",
860
856
  "typescript/http/vercelai/base/gitignore.template",
861
857
  "typescript/http/vercelai/base/main.ts",
@@ -5282,7 +5278,7 @@ from mcp_client.client import get_streamable_http_mcp_client
5282
5278
  from memory.session import get_memory_session_manager
5283
5279
  {{/if}}
5284
5280
  {{#unless hasFileOperations}}
5285
- {{#if (or needsOs (some gitSkills "credentialArn"))}}
5281
+ {{#if (or needsOs browserIdentifierEnvVar codeInterpreterIdentifierEnvVar (some gitSkills "credentialArn"))}}
5286
5282
  import os
5287
5283
  {{/if}}
5288
5284
  {{/unless}}
@@ -5368,10 +5364,20 @@ tools.append(add_numbers)
5368
5364
  {{/unless}}
5369
5365
  {{/if}}
5370
5366
  {{#if hasBrowser}}
5371
- tools.append(AgentCoreBrowser({{#if browserIdentifier}}identifier="{{browserIdentifier}}"{{/if}}).browser)
5367
+ {{#if browserIdentifierEnvVar}}
5368
+ _browser_id = os.getenv("{{browserIdentifierEnvVar}}")
5369
+ tools.append(AgentCoreBrowser(**({"identifier": _browser_id} if _browser_id else {})).browser)
5370
+ {{else}}
5371
+ tools.append(AgentCoreBrowser().browser)
5372
+ {{/if}}
5372
5373
  {{/if}}
5373
5374
  {{#if hasCodeInterpreter}}
5374
- tools.append(AgentCoreCodeInterpreter({{#if codeInterpreterIdentifier}}identifier="{{codeInterpreterIdentifier}}"{{/if}}).code_interpreter)
5375
+ {{#if codeInterpreterIdentifierEnvVar}}
5376
+ _code_interpreter_id = os.getenv("{{codeInterpreterIdentifierEnvVar}}")
5377
+ tools.append(AgentCoreCodeInterpreter(**({"identifier": _code_interpreter_id} if _code_interpreter_id else {})).code_interpreter)
5378
+ {{else}}
5379
+ tools.append(AgentCoreCodeInterpreter().code_interpreter)
5380
+ {{/if}}
5375
5381
  {{/if}}
5376
5382
  {{#if hasShell}}
5377
5383
  @tool
@@ -5914,7 +5920,10 @@ from bedrock_agentcore.identity import requires_access_token
5914
5920
  @requires_access_token(
5915
5921
  provider_name="{{credentialProviderName}}",
5916
5922
  scopes=[{{#if scopes}}"{{scopes}}"{{/if}}],
5917
- auth_flow="M2M",
5923
+ auth_flow="{{#if authFlow}}{{authFlow}}{{else}}M2M{{/if}}",
5924
+ {{#if customParameters}}
5925
+ custom_parameters={{safeJson customParameters}},
5926
+ {{/if}}
5918
5927
  )
5919
5928
  def _get_bearer_token_{{snakeCase name}}(*, access_token: str):
5920
5929
  """Obtain OAuth access token via AgentCore Identity for {{name}}."""
@@ -6017,6 +6026,67 @@ exports[`Assets Directory Snapshots > Python framework assets > python/python/ht
6017
6026
 
6018
6027
  exports[`Assets Directory Snapshots > Python framework assets > python/python/http/strands/base/model/load.py should match snapshot 1`] = `
6019
6028
  "{{#if (eq modelProvider "Bedrock")}}
6029
+ {{#if bedrockMantle}}
6030
+ import os
6031
+
6032
+ from aws_bedrock_token_generator import provide_token
6033
+ {{#if (eq mantleApiFormat "chat_completions")}}
6034
+ from strands.models.openai import OpenAIModel
6035
+ {{else}}
6036
+ {{#if mantleProprietary}}
6037
+ from strands.models.openai_responses import OpenAIResponsesModel
6038
+ {{else}}
6039
+ from model.mantle_compat import MantleCompatResponsesModel
6040
+ {{/if}}
6041
+ {{/if}}
6042
+
6043
+ MODEL_ID = "{{modelId}}"
6044
+
6045
+
6046
+ def load_model():
6047
+ """
6048
+ Get a Bedrock Mantle model client. These OpenAI-compatible models (e.g. openai.gpt-5.5,
6049
+ openai.gpt-oss-120b) are served via the Bedrock Mantle endpoint, NOT the Converse API — so they
6050
+ are invoked through an OpenAI-style client authenticated with a short-lived Bedrock bearer token.
6051
+ Region is read from AWS_REGION (set by the AgentCore runtime).
6052
+ """
6053
+ region = os.environ.get("AWS_REGION", os.environ.get("AWS_DEFAULT_REGION", "us-east-1"))
6054
+ token = provide_token(region=region)
6055
+ {{#if mantleProprietary}}
6056
+ # Proprietary OpenAI models only work on the /openai/v1 Mantle path.
6057
+ base_url = f"https://bedrock-mantle.{region}.api.aws/openai/v1"
6058
+ {{else}}
6059
+ # Open-source OpenAI models (gpt-oss-*) only work on the /v1 Mantle path.
6060
+ base_url = f"https://bedrock-mantle.{region}.api.aws/v1"
6061
+ {{/if}}
6062
+ client_args = {"api_key": token, "base_url": base_url}
6063
+
6064
+ params = {}
6065
+ {{#if modelMaxTokens}}
6066
+ {{#if (eq mantleApiFormat "chat_completions")}}
6067
+ params["max_completion_tokens"] = {{modelMaxTokens}}
6068
+ {{else}}
6069
+ params["max_output_tokens"] = {{modelMaxTokens}}
6070
+ {{/if}}
6071
+ {{/if}}
6072
+ {{#if modelTemperature}}
6073
+ params["temperature"] = {{modelTemperature}}
6074
+ {{/if}}
6075
+ {{#if modelTopP}}
6076
+ params["top_p"] = {{modelTopP}}
6077
+ {{/if}}
6078
+ {{#if (eq mantleApiFormat "chat_completions")}}
6079
+ return OpenAIModel(client_args=client_args, model_id=MODEL_ID, params=params)
6080
+ {{else}}
6081
+ # Responses API: Mantle does not persist responses, so disable server-side storage.
6082
+ params["store"] = False
6083
+ {{#if mantleProprietary}}
6084
+ return OpenAIResponsesModel(client_args=client_args, model_id=MODEL_ID, params=params)
6085
+ {{else}}
6086
+ return MantleCompatResponsesModel(client_args=client_args, model_id=MODEL_ID, params=params)
6087
+ {{/if}}
6088
+ {{/if}}
6089
+ {{else}}
6020
6090
  from strands.models.bedrock import BedrockModel
6021
6091
 
6022
6092
 
@@ -6024,6 +6094,7 @@ def load_model() -> BedrockModel:
6024
6094
  """Get Bedrock model client using IAM credentials."""
6025
6095
  return BedrockModel(model_id="{{#if modelId}}{{modelId}}{{else}}global.anthropic.claude-sonnet-4-5-20250929-v1:0{{/if}}")
6026
6096
  {{/if}}
6097
+ {{/if}}
6027
6098
  {{#if (eq modelProvider "Anthropic")}}
6028
6099
  import os
6029
6100
 
@@ -6139,6 +6210,85 @@ def load_model() -> GeminiModel:
6139
6210
  model_id="{{#if modelId}}{{modelId}}{{else}}gemini-2.5-flash{{/if}}",
6140
6211
  )
6141
6212
  {{/if}}
6213
+ {{#if (eq modelProvider "LiteLLM")}}
6214
+ import os
6215
+ {{#if litellmAdditionalParams}}
6216
+ import json
6217
+ {{/if}}
6218
+
6219
+ from strands.models.litellm import LiteLLMModel
6220
+ {{#if identityProviders.[0].name}}
6221
+ from bedrock_agentcore.identity.auth import requires_api_key
6222
+
6223
+ IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
6224
+ IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
6225
+
6226
+
6227
+ @requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
6228
+ def _agentcore_identity_api_key_provider(api_key: str) -> str:
6229
+ """Fetch API key from AgentCore Identity."""
6230
+ return api_key
6231
+
6232
+
6233
+ def _get_api_key() -> str:
6234
+ """
6235
+ Uses AgentCore Identity for API key management in deployed environments.
6236
+ For local development, run via 'agentcore dev' which loads agentcore/.env.
6237
+ """
6238
+ if os.getenv("LOCAL_DEV") == "1":
6239
+ api_key = os.getenv(IDENTITY_ENV_VAR)
6240
+ if not api_key:
6241
+ raise RuntimeError(
6242
+ f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
6243
+ )
6244
+ return api_key
6245
+ return _agentcore_identity_api_key_provider()
6246
+ {{/if}}
6247
+
6248
+
6249
+
6250
+
6251
+ def load_model() -> LiteLLMModel:
6252
+ """Get a LiteLLM model client (proxies to the provider encoded in model_id)."""
6253
+ client_args = {}
6254
+ {{#if identityProviders.[0].name}}
6255
+ client_args["api_key"] = _get_api_key()
6256
+ {{/if}}
6257
+ {{#if litellmApiBase}}
6258
+ client_args["api_base"] = {{safeJson litellmApiBase}}
6259
+ {{/if}}
6260
+ params = {{#if litellmAdditionalParams}}json.loads({{pyJsonStr litellmAdditionalParams}}){{else}}{}{{/if}}
6261
+ return LiteLLMModel(
6262
+ client_args=client_args,
6263
+ model_id="{{#if modelId}}{{modelId}}{{else}}bedrock/us.anthropic.claude-sonnet-4-5-20250514-v1:0{{/if}}",
6264
+ params=params,
6265
+ )
6266
+ {{/if}}
6267
+ "
6268
+ `;
6269
+
6270
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/http/strands/base/model/mantle_compat.py should match snapshot 1`] = `
6271
+ "from strands.models.openai_responses import OpenAIResponsesModel
6272
+
6273
+
6274
+ class MantleCompatResponsesModel(OpenAIResponsesModel):
6275
+ """Workaround for Bedrock Mantle rejecting output_text in EasyInputMessage content arrays.
6276
+
6277
+ Mantle's Pydantic validation only accepts content as a plain string for assistant messages, while
6278
+ real OpenAI accepts both formats. Flatten assistant content arrays to strings so multi-turn works.
6279
+ Used for open-source OpenAI models (gpt-oss-*) on the /v1 Mantle path; proprietary models use the
6280
+ plain OpenAIResponsesModel on /openai/v1.
6281
+ """
6282
+
6283
+ @classmethod
6284
+ def _format_request_messages(cls, messages):
6285
+ formatted = super()._format_request_messages(messages)
6286
+ for msg in formatted:
6287
+ if msg.get("role") == "assistant" and isinstance(msg.get("content"), list):
6288
+ msg["content"] = "".join(
6289
+ part.get("text", "") for part in msg["content"] if part.get("type") == "output_text"
6290
+ )
6291
+ return formatted
6142
6292
  "
6143
6293
  `;
6144
6294
 
@@ -6161,6 +6311,9 @@ dependencies = [
6161
6311
  {{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
6162
6312
  {{/if}}"mcp >= 1.19.0",
6163
6313
  {{#if (eq modelProvider "OpenAI")}}"openai >= 1.0.0",
6314
+ {{/if}}{{#if (eq modelProvider "LiteLLM")}}"litellm >= 1.0.0",
6315
+ {{/if}}{{#if bedrockMantle}}"openai >= 1.0.0",
6316
+ "aws-bedrock-token-generator >= 1.0.0",
6164
6317
  {{/if}}"strands-agents >= 1.15.0",
6165
6318
  {{#if (or hasBrowser hasCodeInterpreter)}}"strands-agents-tools >= 0.1.0",
6166
6319
  {{/if}}{{#if hasBrowser}}"nest-asyncio >= 1.5.0",
@@ -7385,6 +7538,9 @@ import { Agent, McpClient, tool, type ToolList } from '@strands-agents/sdk';
7385
7538
  import { z } from 'zod';
7386
7539
  import { loadModel } from './model/load.js';
7387
7540
  import { getStreamableHttpMcpClient } from './mcp_client/client.js';
7541
+ {{#if hasMemory}}
7542
+ import { getActorId, getOrCreateMemoryManager } from './memory/memory.js';
7543
+ {{/if}}
7388
7544
 
7389
7545
  // Define a collection of MCP clients (filter out anything that failed to initialize)
7390
7546
  const mcpClients: McpClient[] = [getStreamableHttpMcpClient()].filter(
@@ -7413,6 +7569,25 @@ const SYSTEM_PROMPT = \`
7413
7569
  You are a helpful assistant. Use tools when appropriate.
7414
7570
  \`;
7415
7571
 
7572
+ {{#if hasMemory}}
7573
+ const agentCache = new Map<string, Agent>();
7574
+
7575
+ async function getOrCreateAgent(sessionId: string, actorId: string): Promise<Agent> {
7576
+ const key = \`\${actorId}:\${sessionId}\`;
7577
+ let agent = agentCache.get(key);
7578
+ if (agent) return agent;
7579
+
7580
+ const model = await loadModel();
7581
+ agent = new Agent({
7582
+ model,
7583
+ systemPrompt: SYSTEM_PROMPT,
7584
+ tools,
7585
+ memoryManager: getOrCreateMemoryManager(sessionId, actorId) ?? undefined,
7586
+ });
7587
+ agentCache.set(key, agent);
7588
+ return agent;
7589
+ }
7590
+ {{else}}
7416
7591
  let cachedAgent: Agent | null = null;
7417
7592
 
7418
7593
  async function getOrCreateAgent(): Promise<Agent> {
@@ -7426,12 +7601,37 @@ async function getOrCreateAgent(): Promise<Agent> {
7426
7601
  }
7427
7602
  return cachedAgent;
7428
7603
  }
7604
+ {{/if}}
7429
7605
 
7430
7606
  const app = new BedrockAgentCoreApp({
7431
7607
  invocationHandler: {
7432
7608
  async *process(payload: any, context: any) {
7609
+ {{#if hasMemory}}
7610
+ const sessionId = context?.sessionId ?? 'default-session';
7611
+ const actorId = getActorId(payload, context);
7612
+ const agent = await getOrCreateAgent(sessionId, actorId);
7613
+ {{else}}
7433
7614
  const agent = await getOrCreateAgent();
7434
-
7615
+ {{/if}}
7616
+
7617
+ {{#if hasMemory}}
7618
+ try {
7619
+ for await (const event of agent.stream(payload.prompt ?? '')) {
7620
+ if (
7621
+ event.type === 'modelStreamUpdateEvent' &&
7622
+ event.event?.type === 'modelContentBlockDeltaEvent' &&
7623
+ event.event.delta?.type === 'textDelta'
7624
+ ) {
7625
+ yield { data: event.event.delta.text };
7626
+ }
7627
+ }
7628
+ } finally {
7629
+ // Drain in-flight createEvent calls before the runtime can reclaim
7630
+ // the session microVM. flush() is the durability mechanism — without
7631
+ // it, an idle reclamation can lose the tail of the conversation.
7632
+ await agent.memoryManager?.flush();
7633
+ }
7634
+ {{else}}
7435
7635
  for await (const event of agent.stream(payload.prompt ?? '')) {
7436
7636
  if (
7437
7637
  event.type === 'modelStreamUpdateEvent' &&
@@ -7441,6 +7641,7 @@ const app = new BedrockAgentCoreApp({
7441
7641
  yield { data: event.event.delta.text };
7442
7642
  }
7443
7643
  }
7644
+ {{/if}}
7444
7645
  },
7445
7646
  },
7446
7647
  });
@@ -7593,8 +7794,9 @@ exports[`Assets Directory Snapshots > TypeScript assets > typescript/typescript/
7593
7794
  "@google/genai": "^1.40.0",
7594
7795
  {{/if}}
7595
7796
  "@modelcontextprotocol/sdk": "^1.25.2",
7596
- "@strands-agents/sdk": "1.0.0-rc.4",
7597
- "bedrock-agentcore": "^0.2.4",
7797
+ "@opentelemetry/api": "^1.9.0",
7798
+ "@strands-agents/sdk": "^1.5.0",
7799
+ "bedrock-agentcore": "^0.3.0",
7598
7800
  "tsx": "^4.19.0",
7599
7801
  "zod": "^4.4.3"
7600
7802
  },
@@ -7634,6 +7836,62 @@ exports[`Assets Directory Snapshots > TypeScript assets > typescript/typescript/
7634
7836
  "
7635
7837
  `;
7636
7838
 
7839
+ exports[`Assets Directory Snapshots > TypeScript assets > typescript/typescript/http/strands/capabilities/memory/memory.ts should match snapshot 1`] = `
7840
+ "import { randomUUID } from 'node:crypto';
7841
+ import { MemoryManager } from '@strands-agents/sdk';
7842
+ import { createAgentCoreMemoryStores } from 'bedrock-agentcore/experimental/memory/strands';
7843
+
7844
+ const MEMORY_ID = process.env.{{memoryProviders.[0].envVarName}};
7845
+
7846
+ const CUSTOM_ACTOR_ID_HEADER = 'x-amzn-bedrock-agentcore-runtime-custom-actor-id';
7847
+
7848
+ export function getActorId(payload: any, context: any): string {
7849
+ const raw =
7850
+ context?.headers?.[CUSTOM_ACTOR_ID_HEADER] ||
7851
+ payload?.userId ||
7852
+ context?.sessionId;
7853
+ return typeof raw === 'string' && raw.trim().length > 0 ? raw.trim() : randomUUID();
7854
+ }
7855
+
7856
+ const memoryManagerCache = new Map<string, MemoryManager>();
7857
+
7858
+ export function getOrCreateMemoryManager(sessionId: string, actorId: string): MemoryManager | null {
7859
+ if (!MEMORY_ID) return null;
7860
+
7861
+ const key = \`\${actorId}:\${sessionId}\`;
7862
+ let manager = memoryManagerCache.get(key);
7863
+ if (manager) return manager;
7864
+
7865
+ const stores = createAgentCoreMemoryStores({
7866
+ memoryId: MEMORY_ID,
7867
+ actorId,
7868
+ sessionId,
7869
+ namespaces: [
7870
+ {{#if (includes memoryProviders.[0].strategies "SEMANTIC")}}
7871
+ { namespace: '/users/{actorId}/facts' },
7872
+ {{/if}}
7873
+ {{#if (includes memoryProviders.[0].strategies "USER_PREFERENCE")}}
7874
+ { namespace: '/users/{actorId}/preferences' },
7875
+ {{/if}}
7876
+ {{#if (includes memoryProviders.[0].strategies "EPISODIC")}}
7877
+ { namespace: '/episodes/{actorId}/{sessionId}' },
7878
+ {{/if}}
7879
+ {{#if (includes memoryProviders.[0].strategies "SUMMARIZATION")}}
7880
+ { namespace: '/summaries/{actorId}/{sessionId}' },
7881
+ {{/if}}
7882
+ ],
7883
+ // readMode defaults to 'per-namespace' (one retrieve call per namespace).
7884
+ // Switch to 'subtree' to consolidate to a single hierarchical recall call.
7885
+ extraction: true,
7886
+ });
7887
+
7888
+ manager = new MemoryManager({ stores });
7889
+ memoryManagerCache.set(key, manager);
7890
+ return manager;
7891
+ }
7892
+ "
7893
+ `;
7894
+
7637
7895
  exports[`Assets Directory Snapshots > TypeScript assets > typescript/typescript/http/vercelai/base/README.md should match snapshot 1`] = `
7638
7896
  "This is a project generated by the AgentCore CLI!
7639
7897
 
@@ -79,15 +79,9 @@ async function main() {
79
79
  }
80
80
  }
81
81
 
82
- // Harness is preview-gated. The CLI bundle bakes the preview flag at build time and
83
- // forwards it to this child process via AGENTCORE_PREVIEW (see toolkit-lib/wrapper.ts).
84
- // This app is built separately and cannot see that build-time define, so it gates on the
85
- // env var. Absent/anything-but-'1' defaults to off so a stale harnesses[] entry in a
86
- // non-preview build never synthesizes an AWS::BedrockAgentCore::Harness resource.
87
- const previewEnabled = process.env.AGENTCORE_PREVIEW === '1';
88
-
82
+ // Synthesize an AWS::BedrockAgentCore::Harness resource for each harness entry in the spec.
89
83
  const harnessConfigs: HarnessConfig[] = [];
90
- for (const entry of previewEnabled ? (specAny.harnesses ?? []) : []) {
84
+ for (const entry of specAny.harnesses ?? []) {
91
85
  const harnessDir = path.resolve(projectRoot, entry.path);
92
86
  const harnessPath = path.resolve(harnessDir, 'harness.json');
93
87
  try {
@@ -66,7 +66,7 @@ from mcp_client.client import get_streamable_http_mcp_client
66
66
  from memory.session import get_memory_session_manager
67
67
  {{/if}}
68
68
  {{#unless hasFileOperations}}
69
- {{#if (or needsOs (some gitSkills "credentialArn"))}}
69
+ {{#if (or needsOs browserIdentifierEnvVar codeInterpreterIdentifierEnvVar (some gitSkills "credentialArn"))}}
70
70
  import os
71
71
  {{/if}}
72
72
  {{/unless}}
@@ -152,10 +152,20 @@ tools.append(add_numbers)
152
152
  {{/unless}}
153
153
  {{/if}}
154
154
  {{#if hasBrowser}}
155
- tools.append(AgentCoreBrowser({{#if browserIdentifier}}identifier="{{browserIdentifier}}"{{/if}}).browser)
155
+ {{#if browserIdentifierEnvVar}}
156
+ _browser_id = os.getenv("{{browserIdentifierEnvVar}}")
157
+ tools.append(AgentCoreBrowser(**({"identifier": _browser_id} if _browser_id else {})).browser)
158
+ {{else}}
159
+ tools.append(AgentCoreBrowser().browser)
160
+ {{/if}}
156
161
  {{/if}}
157
162
  {{#if hasCodeInterpreter}}
158
- tools.append(AgentCoreCodeInterpreter({{#if codeInterpreterIdentifier}}identifier="{{codeInterpreterIdentifier}}"{{/if}}).code_interpreter)
163
+ {{#if codeInterpreterIdentifierEnvVar}}
164
+ _code_interpreter_id = os.getenv("{{codeInterpreterIdentifierEnvVar}}")
165
+ tools.append(AgentCoreCodeInterpreter(**({"identifier": _code_interpreter_id} if _code_interpreter_id else {})).code_interpreter)
166
+ {{else}}
167
+ tools.append(AgentCoreCodeInterpreter().code_interpreter)
168
+ {{/if}}
159
169
  {{/if}}
160
170
  {{#if hasShell}}
161
171
  @tool
@@ -18,7 +18,10 @@ from bedrock_agentcore.identity import requires_access_token
18
18
  @requires_access_token(
19
19
  provider_name="{{credentialProviderName}}",
20
20
  scopes=[{{#if scopes}}"{{scopes}}"{{/if}}],
21
- auth_flow="M2M",
21
+ auth_flow="{{#if authFlow}}{{authFlow}}{{else}}M2M{{/if}}",
22
+ {{#if customParameters}}
23
+ custom_parameters={{safeJson customParameters}},
24
+ {{/if}}
22
25
  )
23
26
  def _get_bearer_token_{{snakeCase name}}(*, access_token: str):
24
27
  """Obtain OAuth access token via AgentCore Identity for {{name}}."""
@@ -1,4 +1,65 @@
1
1
  {{#if (eq modelProvider "Bedrock")}}
2
+ {{#if bedrockMantle}}
3
+ import os
4
+
5
+ from aws_bedrock_token_generator import provide_token
6
+ {{#if (eq mantleApiFormat "chat_completions")}}
7
+ from strands.models.openai import OpenAIModel
8
+ {{else}}
9
+ {{#if mantleProprietary}}
10
+ from strands.models.openai_responses import OpenAIResponsesModel
11
+ {{else}}
12
+ from model.mantle_compat import MantleCompatResponsesModel
13
+ {{/if}}
14
+ {{/if}}
15
+
16
+ MODEL_ID = "{{modelId}}"
17
+
18
+
19
+ def load_model():
20
+ """
21
+ Get a Bedrock Mantle model client. These OpenAI-compatible models (e.g. openai.gpt-5.5,
22
+ openai.gpt-oss-120b) are served via the Bedrock Mantle endpoint, NOT the Converse API — so they
23
+ are invoked through an OpenAI-style client authenticated with a short-lived Bedrock bearer token.
24
+ Region is read from AWS_REGION (set by the AgentCore runtime).
25
+ """
26
+ region = os.environ.get("AWS_REGION", os.environ.get("AWS_DEFAULT_REGION", "us-east-1"))
27
+ token = provide_token(region=region)
28
+ {{#if mantleProprietary}}
29
+ # Proprietary OpenAI models only work on the /openai/v1 Mantle path.
30
+ base_url = f"https://bedrock-mantle.{region}.api.aws/openai/v1"
31
+ {{else}}
32
+ # Open-source OpenAI models (gpt-oss-*) only work on the /v1 Mantle path.
33
+ base_url = f"https://bedrock-mantle.{region}.api.aws/v1"
34
+ {{/if}}
35
+ client_args = {"api_key": token, "base_url": base_url}
36
+
37
+ params = {}
38
+ {{#if modelMaxTokens}}
39
+ {{#if (eq mantleApiFormat "chat_completions")}}
40
+ params["max_completion_tokens"] = {{modelMaxTokens}}
41
+ {{else}}
42
+ params["max_output_tokens"] = {{modelMaxTokens}}
43
+ {{/if}}
44
+ {{/if}}
45
+ {{#if modelTemperature}}
46
+ params["temperature"] = {{modelTemperature}}
47
+ {{/if}}
48
+ {{#if modelTopP}}
49
+ params["top_p"] = {{modelTopP}}
50
+ {{/if}}
51
+ {{#if (eq mantleApiFormat "chat_completions")}}
52
+ return OpenAIModel(client_args=client_args, model_id=MODEL_ID, params=params)
53
+ {{else}}
54
+ # Responses API: Mantle does not persist responses, so disable server-side storage.
55
+ params["store"] = False
56
+ {{#if mantleProprietary}}
57
+ return OpenAIResponsesModel(client_args=client_args, model_id=MODEL_ID, params=params)
58
+ {{else}}
59
+ return MantleCompatResponsesModel(client_args=client_args, model_id=MODEL_ID, params=params)
60
+ {{/if}}
61
+ {{/if}}
62
+ {{else}}
2
63
  from strands.models.bedrock import BedrockModel
3
64
 
4
65
 
@@ -6,6 +67,7 @@ def load_model() -> BedrockModel:
6
67
  """Get Bedrock model client using IAM credentials."""
7
68
  return BedrockModel(model_id="{{#if modelId}}{{modelId}}{{else}}global.anthropic.claude-sonnet-4-5-20250929-v1:0{{/if}}")
8
69
  {{/if}}
70
+ {{/if}}
9
71
  {{#if (eq modelProvider "Anthropic")}}
10
72
  import os
11
73
 
@@ -121,3 +183,57 @@ def load_model() -> GeminiModel:
121
183
  model_id="{{#if modelId}}{{modelId}}{{else}}gemini-2.5-flash{{/if}}",
122
184
  )
123
185
  {{/if}}
186
+ {{#if (eq modelProvider "LiteLLM")}}
187
+ import os
188
+ {{#if litellmAdditionalParams}}
189
+ import json
190
+ {{/if}}
191
+
192
+ from strands.models.litellm import LiteLLMModel
193
+ {{#if identityProviders.[0].name}}
194
+ from bedrock_agentcore.identity.auth import requires_api_key
195
+
196
+ IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
197
+ IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
198
+
199
+
200
+ @requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
201
+ def _agentcore_identity_api_key_provider(api_key: str) -> str:
202
+ """Fetch API key from AgentCore Identity."""
203
+ return api_key
204
+
205
+
206
+ def _get_api_key() -> str:
207
+ """
208
+ Uses AgentCore Identity for API key management in deployed environments.
209
+ For local development, run via 'agentcore dev' which loads agentcore/.env.
210
+ """
211
+ if os.getenv("LOCAL_DEV") == "1":
212
+ api_key = os.getenv(IDENTITY_ENV_VAR)
213
+ if not api_key:
214
+ raise RuntimeError(
215
+ f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
216
+ )
217
+ return api_key
218
+ return _agentcore_identity_api_key_provider()
219
+ {{/if}}
220
+
221
+
222
+
223
+
224
+ def load_model() -> LiteLLMModel:
225
+ """Get a LiteLLM model client (proxies to the provider encoded in model_id)."""
226
+ client_args = {}
227
+ {{#if identityProviders.[0].name}}
228
+ client_args["api_key"] = _get_api_key()
229
+ {{/if}}
230
+ {{#if litellmApiBase}}
231
+ client_args["api_base"] = {{safeJson litellmApiBase}}
232
+ {{/if}}
233
+ params = {{#if litellmAdditionalParams}}json.loads({{pyJsonStr litellmAdditionalParams}}){{else}}{}{{/if}}
234
+ return LiteLLMModel(
235
+ client_args=client_args,
236
+ model_id="{{#if modelId}}{{modelId}}{{else}}bedrock/us.anthropic.claude-sonnet-4-5-20250514-v1:0{{/if}}",
237
+ params=params,
238
+ )
239
+ {{/if}}
@@ -0,0 +1,21 @@
1
+ from strands.models.openai_responses import OpenAIResponsesModel
2
+
3
+
4
+ class MantleCompatResponsesModel(OpenAIResponsesModel):
5
+ """Workaround for Bedrock Mantle rejecting output_text in EasyInputMessage content arrays.
6
+
7
+ Mantle's Pydantic validation only accepts content as a plain string for assistant messages, while
8
+ real OpenAI accepts both formats. Flatten assistant content arrays to strings so multi-turn works.
9
+ Used for open-source OpenAI models (gpt-oss-*) on the /v1 Mantle path; proprietary models use the
10
+ plain OpenAIResponsesModel on /openai/v1.
11
+ """
12
+
13
+ @classmethod
14
+ def _format_request_messages(cls, messages):
15
+ formatted = super()._format_request_messages(messages)
16
+ for msg in formatted:
17
+ if msg.get("role") == "assistant" and isinstance(msg.get("content"), list):
18
+ msg["content"] = "".join(
19
+ part.get("text", "") for part in msg["content"] if part.get("type") == "output_text"
20
+ )
21
+ return formatted
@@ -16,6 +16,9 @@ dependencies = [
16
16
  {{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
17
17
  {{/if}}"mcp >= 1.19.0",
18
18
  {{#if (eq modelProvider "OpenAI")}}"openai >= 1.0.0",
19
+ {{/if}}{{#if (eq modelProvider "LiteLLM")}}"litellm >= 1.0.0",
20
+ {{/if}}{{#if bedrockMantle}}"openai >= 1.0.0",
21
+ "aws-bedrock-token-generator >= 1.0.0",
19
22
  {{/if}}"strands-agents >= 1.15.0",
20
23
  {{#if (or hasBrowser hasCodeInterpreter)}}"strands-agents-tools >= 0.1.0",
21
24
  {{/if}}{{#if hasBrowser}}"nest-asyncio >= 1.5.0",