Skip to content

fix(typescript): TypeScript agent template and streaming fixes#981

Open
jesseturner21 wants to merge 36 commits intomainfrom
fix/strands-ts-stream-events
Open

fix(typescript): TypeScript agent template and streaming fixes#981
jesseturner21 wants to merge 36 commits intomainfrom
fix/strands-ts-stream-events

Conversation

@jesseturner21
Copy link
Copy Markdown
Contributor

@jesseturner21 jesseturner21 commented Apr 27, 2026

Description

Fixes several issues with TypeScript agent support in the CLI:

  1. Stream event types: The TypeScript Strands template was using incorrect stream event field names, causing streaming responses to not render.
  2. HTTP invoke Accept header: The invoke command was missing text/event-stream in the Accept header, breaking SSE streaming for HTTP agents.
  3. Memory not supported in TS: The generate wizard offered memory configuration for TypeScript agents, but the Strands TS SDK doesn't support the memory session manager yet. This disables the memory step and removes the memory-related Handlebars blocks and session.ts capability file from TypeScript templates.
  4. Protocol options UX: Instead of showing disabled protocol options for TypeScript, the wizard now filters to only show HTTP.

Related Issue

Closes #

Documentation PR

N/A

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update
  • Other (please describe):

Testing

How have you tested the change?

  • I ran npm run test:unit and npm run test:integ
  • I ran npm run typecheck
  • I ran npm run lint
  • If I modified src/assets/, I ran npm run test:update-snapshots and committed the updated snapshots

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the
terms of your choice.

Unblocks TypeScript as a language option in the generate path and teaches
the spec builder to emit NODE_22 / main.ts defaults when language=TypeScript.
Templates, node setup helper, dev-server Python-only guard removal, tests,
and docs still to come — see docs/TYPESCRIPT_SUPPORT_HANDOFF.md for the
full remaining scope and plan link.

- schema: add DEFAULT_NODE_VERSION, DEFAULT_ENTRYPOINT_BY_LANGUAGE,
  DEFAULT_RUNTIME_BY_LANGUAGE constants
- tui: remove disabled=true from TypeScript LANGUAGE_OPTIONS entry
- tui: filter SDK list to Strands-only when language=TypeScript
- validate: drop the --language TypeScript reject; add Strands-only gate for TS
- schema-mapper: branch entrypoint/runtime on config.language

Confidence: medium
Scope-risk: narrow
Not-tested: end-to-end create/deploy — templates not yet authored
Living checklist alongside the handoff doc. Tracks phases 0-8,
captures verification-sweep results, and reserves a commit log
so the next person can reconstruct state from this file + git log.

Confidence: high
Scope-risk: narrow
Confidence: high
Scope-risk: narrow
Drop the Python-only guard in isDevSupported. CodeZipDevServer already
branches on entrypoint extension and selects `npx tsx watch` for non-Python
agents, so the guard was the only thing rejecting TS entrypoints.

Constraint: Dev-server spawn path must stay unchanged for Python agents
Rejected: Add an explicit TypeScript allow-list branch | redundant — extension check already distinguishes, extra branch adds drift risk
Confidence: high
Scope-risk: narrow
Not-tested: TS-specific unit test for spawn config (deferred to Phase 6)
Add src/assets/typescript/http/strands/ with gitignore, package.json,
tsconfig.json, main.ts, README.md, mcp_client/client.ts, model/load.ts,
and capabilities/memory/session.ts — mirroring the Python Strands
templates surface-for-surface, keyed off the same Handlebars variables.

Update asset snapshot baseline (8 new TS files + file-listing diff).

Align three stale tests with the Phase 1/2 validator + dev-server
changes: accept TS+Strands in validateAddAgentOptions; treat Node
entrypoints as dev-supported in getDevSupportedAgents / getDevConfig.

Extend .prettierignore to skip src/assets TS/JSON template files —
these are Handlebars templates with {{...}} expressions that do not
parse as TypeScript. ESLint already ignores src/assets via eslint.config.

Constraint: SDK surface for @strands-agents/sdk + bedrock-agentcore TS is still RC; templates are written against the surface described in the handoff
Rejected: Run prettier over the Handlebars TS templates | they do not parse as TS — same reason Python templates with {{...}} aren't formatted as Python
Confidence: medium
Scope-risk: narrow
Directive: Verify SDK event-type names, model-class names, and memory import path against the actual tarballs before shipping — see Known gotchas in docs/TYPESCRIPT_SUPPORT_PROGRESS.md
Not-tested: Rendered templates have not been compiled or run against real SDKs (Phase 6 integ + Phase 8 E2E)
src/assets/container/typescript/Dockerfile uses node:22-slim with a
cached deps layer (npm ci --omit=dev, fallback to npm install when no
lockfile) and runs `npx tsx main.ts`. Exposes 8080/8000/9000 to match
the Python Dockerfile contract. Non-root bedrock_agentcore user mirrors
Python.

Update asset file-listing snapshot for the two new files.

Constraint: Dev and container runtimes should share one entrypoint shape so main.ts stays the single source of truth
Rejected: Add a tsc build step + `node dist/main.js` CMD | adds a build surface and diverges dev vs container semantics; defer until image size or cold start becomes a problem
Confidence: medium
Scope-risk: narrow
Not-tested: Container build + AgentCore deploy against a live runtime (Phase 6 E2E)
Add src/cli/operations/node/setup.ts with setupNodeProject({projectDir})
that runs `npm install` and returns a structured result, mirroring the
Python setup helper. Export via a new node/ operations barrel.

Wire into useCreateFlow so a TypeScript create-path scaffold runs
`npm install` in app/<name>/ after the agent is added. Add a matching
entry to getCreateSteps so the progress UI reflects the step.

Unit tests mirror python/setup.test.ts (8 specs).

checkCreateDependencies requires no change — npm is already checked
unconditionally (needed for CDK synth) and uv is already gated to
Python. Node version is likewise checked unconditionally.

Constraint: Fresh scaffolds don't ship a lockfile
Rejected: Use `npm ci` | fails without package-lock.json; switching to `npm ci` after the first install is a later optimization
Rejected: Mark npm_not_found as a hard error | keep parity with Python (uv failure is a warn, not error) so create still succeeds and the user can install deps manually
Confidence: high
Scope-risk: narrow
Not-tested: End-to-end scaffold run against a real npm registry (Phase 8)
…ix spawn entrypoint rewrite

Adds a TypeScript HTTP spec to codezip-dev-server.test.ts asserting the
spawn is `npx tsx watch <entry>` with LOCAL_DEV env plumbed through.

Adds a TypeScript block to the create integ suite that scaffolds a
Strands/Bedrock TS agent and asserts the core generated files
(main.ts, package.json, tsconfig.json, model/load.ts, mcp_client/client.ts).

Fixes a latent bug in CodeZipDevServer where the non-Python spawn path
was applying a Python-style module-path rewrite (`.replace(/\./g, '/') + '.ts'`),
which turned `main.ts` into `main/ts.ts`. The entrypoint is now passed
literally to tsx.

Constraint: TS create integ must stay offline-safe — skips npm install via runCLI's skipInstall flag.
Rejected: Shelling out to real `npm install` in the integ test | too slow + flaky on CI without a registry mirror.
Confidence: high
Scope-risk: narrow
Directive: The non-Python dev-server spawn path passes the entrypoint literally — do not re-introduce Python module-path rewriting here.
Not-tested: Real `npm install` end-to-end for TS scaffolds (covered manually in Phase 8).
Mirrors the create-flow pattern from lifecycle-config.test.ts. Drives
`agentcore create --language TypeScript --framework Strands` through the
advanced-no path, confirms, and asserts the generated agentcore.json has
runtimeVersion "NODE_22" and entrypoint "main.ts".

Runs with AGENTCORE_SKIP_INSTALL=1 so the scaffold stays fast and
offline-safe.

Constraint: Walkthrough must not require a network-reachable npm registry.
Rejected: Exercising advanced-settings branch too | already covered by lifecycle-config.test.ts for Python; TS path reuses the same wizard.
Confidence: medium
Scope-risk: narrow
Not-tested: Real npm install end-to-end (covered manually in Phase 8).
…trands gate

The create command's validator still hard-rejected --language TypeScript
with "TypeScript is not yet supported", which shadowed all the Phase 1–5
plumbing for TS agents. The add-agent validator already performs the
Strands-only gate, but the create path was missed.

Replaces the hard reject with the same Strands-only gate used by the add
command: TypeScript is accepted, but non-Strands frameworks produce a
clear "Framework X is not yet available for TypeScript" error. The
generic "Invalid language" message now also lists TypeScript alongside
Python.

Updates the corresponding unit test (previously asserted the old error
message) to cover both the TS+Strands happy path and the TS+non-Strands
rejection.

Constraint: Create and add validators must agree on which TS frameworks are permitted.
Rejected: Leave the add validator as the only gate | create path is reachable directly from the CLI and must reject non-Strands TS itself.
Confidence: high
Scope-risk: narrow
Directive: Keep create/validate.ts and add/validate.ts TS gates in lockstep — both must restrict TypeScript to Strands until other TS templates land.
Adds TypeScript coverage to the user-facing docs:

- frameworks.md: new "Supported languages" table + Strands-only note, TS
  example in the Strands section.
- local-development.md: new "TypeScript Agents" subsection covering the
  npm + tsx watch dev loop and AGENTCORE_SKIP_INSTALL.
- commands.md: --language row now lists TypeScript (Strands-only) with a
  cross-link, plus a TS create example.
- container-builds.md: new "TypeScript Dockerfile" subsection matching
  the generated template (node:22-slim, non-root, tsx entrypoint, agentcore.json
  example).
- README.md: Strands row annotated "Python + TypeScript".

Also logs Phase 7 completion and Phase 8 partial results in the progress
tracker — unit + integ suites green, manual agentcore dev/deploy runs and
the gated E2E container deploy test are still outstanding.

Confidence: high
Scope-risk: narrow
Not-tested: Manual agentcore create -> dev -> deploy walkthrough deferred until credentials are refreshed.
Adds docs/TYPESCRIPT_SUPPORT_TEST_PLAN.md — a hands-on verification
checklist for TypeScript Strands support that a tester can work through
top-to-bottom.

Structure:
- Metadata block (tester, date, CLI version, Node/npm/platform)
- 9 sections: prerequisites, automated suites, validator checks,
  scaffold, local dev, non-Strands rejection, CodeZip deploy + invoke,
  optional container build + deploy, docs smoke, Python regression
- Each step has explicit commands, expected outcomes, and a [ ] / [x] /
  [!] / [~] status box
- Known limitations and failure-capture guidance at the bottom

Intended as a companion to docs/TYPESCRIPT_SUPPORT_PROGRESS.md — the
progress tracker records what was built; this plan records what was
actually tested end-to-end.

Confidence: high
Scope-risk: narrow
Not-tested: The plan itself — it needs a first human pass to shake out any missing steps.
Extends docs/TYPESCRIPT_SUPPORT_TEST_PLAN.md with two kinds of pointers:

- A top-level "Code map" table listing every concern (validator,
  templates, dev-server, packaging, TUI, etc.) alongside the primary
  source file and its test file. One-stop reference for a fixer.
- Inline "Fix pointers if Section N fails" blocks after sections 2, 3,
  4, 5, 6, and 7. Each explicitly names the file to edit and the
  corresponding test file, so a tester who hits a red box can jump
  straight to the right place without re-deriving the architecture.

Rationale: the progress tracker lists what was built commit-by-commit,
but the tester who follows the plan needs to know where to look when a
step fails. Surfacing the files inline saves them from grepping the
repo mid-bug.

Confidence: high
Scope-risk: narrow
Not-tested: Link rot — these paths are accurate as of the latest commit but may drift if future refactors move files.
Two bugs blocked `agentcore dev` for scaffolded TypeScript agents:

1. `npm install` failed with ERESOLVE. bedrock-agentcore@0.2.2 declares
   `@strands-agents/sdk >=0.1.0` as a peerOptional, and npm excludes
   pre-releases (1.0.0-rc.4) from plain ranges. Added an `overrides`
   block pinning bedrock-agentcore's peer to the root SDK version. Also
   added the OpenTelemetry / AWS SDK / express / zod peers that Strands
   SDK expects at runtime as direct deps so they resolve deterministically.

2. `app.run()` threw because `new BedrockAgentCoreApp()` with no args is
   rejected by the 0.2.2 constructor. Rewrote the template to pass
   `{ invocationHandler: { process } }` to the constructor instead of
   assigning to `app.invocationHandler` post-construction.

Verified end-to-end: fresh `agentcore create --language TypeScript` →
`agentcore dev` binds :8080, Fastify serves `/invocations`, agent
streams a 200 response in ~2.7s, and `tsx watch` hot-reloads on edits.

Constraint: bedrock-agentcore@0.2.2 peerOptional excludes pre-release SDKs
Rejected: bump Strands SDK past 1.0.0-rc.4 | no stable release available yet
Confidence: high
Scope-risk: narrow
Not-tested: Container build path and MCP gateway variant of this template
… projects

Non-interactive `agentcore create --language TypeScript` scaffolded files
but never ran `npm install`, so the resulting project could not boot
`agentcore dev` without a manual install step. Python had the equivalent
`setupPythonProject` call; TypeScript had no matching branch.

Added a `setupNodeProject` invocation that runs when language=TypeScript
and `--skip-install` is not set. npm failures (ERESOLVE, missing npm,
etc.) are surfaced as warnings on the result rather than aborting the
whole create — the scaffold is still useful and the user can retry.

Also added a TypeScript branch to `getDryRunInfo` so `--dry-run` lists
the TS files that would be created (main.ts, package.json, tsconfig.json,
model/load.ts, mcp_client/client.ts) instead of an empty `app/` entry.

Constraint: Must match Python's behavior so non-interactive flow is symmetric
Confidence: high
Scope-risk: narrow
… TUI

The interactive TS create flow previously collapsed every npm install
failure into "Failed to set up Node environment. Run npm install
manually to see the error." That hid ERESOLVE and other diagnostic
output behind a manual re-run, which was especially painful during
the rc.4/peer-dep work since the user had no signal why install failed.

Now the warn message includes the first meaningful line of npm stderr
(stripped of the `npm error`/`npm warn` prefix, capped at 160 chars),
differentiates `npm_not_found` from install failures, and logs every
non-empty line of npm output as a sub-step so the full trace is visible
in the TUI without re-running.

Confidence: high
Scope-risk: narrow
…bash

Filled-in copy of docs/TYPESCRIPT_SUPPORT_TEST_PLAN.md with run data,
outputs, and checkbox state for each scenario exercised during the
TypeScript support bug bash. Captures what works today, what required
the template and create-flow fixes landing in this batch, and the
residual gaps (container build path, MCP gateway variant) that need
follow-up.

Confidence: high
Scope-risk: narrow
`agentcore dev` with build=Container failed during image build with
`useradd: UID 1000 is not unique`. The node:22-slim base image already
ships a `node` user at UID 1000, so creating `bedrock_agentcore` at
the same UID collided.

Delete the preexisting node user before creating bedrock_agentcore so
UID 1000 is free. Verified with a fresh TS Container scaffold: image
builds in ~12s, container boots, `/ping` returns Healthy, and
`/invocations` routes to the agent.

Constraint: Must keep bedrock_agentcore at UID 1000 to match Python image
Rejected: Pick a different UID | diverges from Python container contract
Confidence: high
Scope-risk: narrow
Directive: node:22-slim base reserves UID 1000 for `node` — leave the
  `userdel -r node` in place if this image is ever rebased onto a
  different tag, verify no other user at UID 1000 first
…t re-install

The TS Container Dockerfile runs `npm ci --omit=dev`, which drops tsx
because it was a devDependency. The container then fell back to
`npx tsx main.ts` at runtime, which re-downloaded tsx from the
registry on every boot (~4–6s added latency, plus a hard dep on
network access at container start).

Moved tsx to dependencies. Preinstalled in the image, no network call
at boot, container ready ~5s instead of 10+. Local `tsx watch` dev
behavior is unchanged since runtime-present deps are still resolvable
by the `dev` script.

Snapshot updated to reflect the new dep layout.

Confidence: high
Scope-risk: narrow
… clear error

`agentcore create --protocol MCP --language TypeScript` previously got
past validation and crashed in the renderer with an ENOENT pointing at
a non-existent template directory
(`src/assets/typescript/mcp/standalone/base`). The MCP TS template has
not been authored yet.

Added an explicit guard in both `create` and `add agent` validators
that returns "MCP protocol is not yet supported for TypeScript. Use
--protocol HTTP or --language Python." before we try to render the
missing template. Same message for both commands so users hitting it
from either path see the same guidance.

Follow-up: when the MCP TS template lands, delete these two guards.

Confidence: high
Scope-risk: narrow
Directive: Remove these guards in lockstep with the MCP TS template —
  leaving one in place will continue rejecting a valid combo.
The language picker in the interactive `agentcore create` wizard had
TypeScript marked `disabled: true` with a "(coming soon)" label, so
users could not pick it from the TUI even though non-interactive
`agentcore create --language TypeScript` worked end-to-end.

Removed the disabled flag and "(coming soon)" label now that the TS
HTTP/Strands template runs through create, dev (CodeZip), and dev
(Container) verified end-to-end. Also dropped the now-dead
`'disabled' in o` branch in GenerateWizardUI since no LANGUAGE_OPTIONS
entry carries the field anymore.

Confidence: high
Scope-risk: narrow
…TS templates land

`agentcore create` exposed MCP and A2A in the TUI protocol picker for
any language. Both trigger a renderer ENOENT for TypeScript because
the templates (`typescript/mcp/...`, `typescript/a2a/...`) do not
exist yet.

- TUI: protocol step now disables MCP and A2A when language=TypeScript,
  so the picker reflects what is actually supported.
- Validators: replaced the MCP-only TypeScript guard (landed in
  38e9973) with a general `protocol !== 'HTTP' && language === 'TypeScript'`
  check, so A2A is now rejected with the same friendly message instead
  of crashing in the renderer.

Follow-up: when MCP and/or A2A TS templates land, drop the disabled
flag in GenerateWizardUI and the validator guards in both create and
add — all three must move in lockstep.

Confidence: high
Scope-risk: narrow
Directive: The validator and TUI guards are paired with a missing
  template. Do not remove one without confirming the corresponding
  template directory exists under `src/assets/typescript/`.
…, browser + no-browser)

Unblock TypeScript agents in `agentcore dev` across all four combinations:
CodeZip/Container x browser/no-browser. The underlying dev server already
handled TypeScript in the CodeZip spawn path (npx tsx watch) and the Container
path is language-agnostic - only the user-facing guards and messages still
claimed Python-only. Also wires a node-deps install step parallel to
ensurePythonVenv so a freshly cloned TS project works on first invocation.

- command.tsx: drop "requires Python agents" message
- DevScreen.tsx: drop "requires Python agent" message
- codezip-dev-server.ts: add ensureNodeDeps (detects pnpm/yarn/npm via lockfile)
- codezip-dev-server.test.ts: cover install-on-missing and skip-on-present

Constraint: Container path must remain language-agnostic - no Dockerfile assumptions
Rejected: Split CodeZip into Python/Node subclasses | spawn branch is 8 lines, not worth the abstraction
Confidence: high
Scope-risk: narrow
Not-tested: End-to-end matrix against live AWS; pre-existing dev.test.ts failures unrelated to this change
The useDevServer hook only checked for Python/uvicorn startup strings
("Application startup complete", "Uvicorn running") to transition from
"starting" to "running". TS agents log "Server listening" instead,
causing the TUI to stay stuck on "Starting..." forever.

Add "Server listening" to the readiness detection pattern.

Confidence: high
Scope-risk: narrow
The Strands TypeScript SDK (@strands-agents/sdk 1.0.0-rc.4) emits
modelStreamUpdateEvent with nested modelContentBlockDeltaEvent, not
contentBlockDelta. The template filter matched nothing, so the async
generator yielded zero chunks and agentcore dev showed empty response.
The SigV4 invoke paths sent Accept: application/json, causing 406 Not
Acceptable from container agents whose bedrock-agentcore runtime only
produces SSE streams. The bearer-token, MCP, A2A, and AGUI paths
already included text/event-stream — this aligns the two remaining
HTTP invoke functions.

Constraint: bedrock-agentcore Node.js runtime returns 406 when Accept header lacks text/event-stream and handler is an async generator
Rejected: Change only the streaming path | non-streaming path also needs it for containers that always stream
Confidence: high
Scope-risk: narrow
…mplates

TypeScript Strands SDK does not yet support the memory session manager,
so the generate wizard now skips the memory step for TypeScript agents
and the template removes the memory-related Handlebars blocks.

Also filters protocol options to only show HTTP for TypeScript (instead
of showing disabled options) for a cleaner UX.
@jesseturner21 jesseturner21 requested a review from a team April 27, 2026 17:41
@github-actions github-actions Bot added the size/xl PR size: XL label Apr 27, 2026
@github-actions github-actions Bot added the agentcore-harness-reviewing AgentCore Harness review in progress label Apr 27, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Package Tarball

aws-agentcore-0.9.1.tgz

How to install

npm install https://github.com/aws/agentcore-cli/releases/download/pr-981-tarball/aws-agentcore-0.9.1.tgz

@github-actions github-actions Bot added agentcore-harness-reviewing AgentCore Harness review in progress and removed agentcore-harness-reviewing AgentCore Harness review in progress labels Apr 27, 2026
Add VercelAI as a new framework option alongside Strands, LangChain,
GoogleADK, and OpenAIAgents. Supports all 4 model providers (Bedrock,
Anthropic, OpenAI, Gemini) with HTTP protocol, both CodeZip and
Container deploy modes.

Includes template assets (main.ts, model/load.ts with per-provider
Handlebars conditionals, package.json, tsconfig.json), VercelAIRenderer,
renderer factory wiring, TUI wizard option, and validation gate updates.

Confidence: high
Scope-risk: narrow
…ials

The Vercel AI template had outdated dependency versions (ai@^4.3.0)
incompatible with bedrock-agentcore@0.2.2 which requires ai@>=6.0.0.
The Bedrock model ID was invalid, and the provider lacked credential
chain support needed for deployed AgentCore Runtime environments.

Constraint: @ai-sdk/amazon-bedrock does not use AWS SDK default credential chain
Rejected: Explicit env var credentials | not available in AgentCore Runtime container
Confidence: high
Scope-risk: narrow
@github-actions github-actions Bot removed the size/xl PR size: XL label Apr 28, 2026
@github-actions github-actions Bot added the size/xl PR size: XL label Apr 28, 2026
When both AWS_ACCESS_KEY_ID and AWS_PROFILE are set, the SDK credential
chain inside the container prefers AWS_PROFILE. But the mounted ~/.aws
files have 600 permissions owned by the host user, so the container's
bedrock_agentcore user cannot read them — falling back to EC2 instance
metadata which uses the wrong account.

Now omits AWS_PROFILE from the container env when explicit credentials
are available, so the credential chain finds env var creds first.

Constraint: Container user uid differs from host user uid
Rejected: chmod the mounted credentials | security risk, read-only mount
Confidence: high
Scope-risk: moderate
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Apr 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xl PR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant