Template actually pins node:22-slim for both builder and runner stages,
and eclipse-temurin:21-jre (not -jre-alpine) for spring-ai runner. README
called out node:24 + temurin:21-jre-alpine. Align docs with reality.
Node 24 bump is a separate decision not made in this body of work.
Completes the NODE_ENV scope cleanup started in the prior Python
packages commit. Image-scope ENV NODE_ENV=production leaks into every
child process (agent subprocesses, healthchecks, shell scripts) —
several frameworks change behavior under NODE_ENV and should NOT see
'production' when running as an agent backend.
- mastra: Dockerfile drops image ENV; entrypoint.sh now invokes
'env NODE_ENV=production npx next start' (was relying on image ENV).
- spring-ai: Dockerfile drops image ENV; entrypoint.sh now scopes
NODE_ENV=production to the Next.js child only (Java agent never
interpreted NODE_ENV but the scoping matches every other package).
- langgraph-python entrypoint: 'npx next start' now inherits
NODE_ENV=production via 'env' wrapper (Dockerfile already removed
image ENV in prior commit).
All 11 Python runners previously executed as root. Bring them into parity
with langroid (the one Python package that already had the pattern):
- Create uid/gid 1001 'app' user/group before any COPY
- Add --chown=app:app to runner-stage COPY directives
- USER app before EXPOSE/CMD
- langgraph-python: add the missing COPY --from=frontend /app/public line
(every other Python package has it) plus an empty public/.gitkeep so
the copy resolves
Packages touched: ag2, agno, claude-sdk-python, crewai-crews, google-adk,
langgraph-fastapi, langgraph-python, llamaindex, ms-agent-python,
pydantic-ai, strands.
- entrypoint.template.sh: cleanup trap + final kill now tolerate no-match
(|| true) so trap doesn't abort shell when one of the three PIDs has
already exited. Package-level entrypoint.sh already had this shape.
- generate-starters.ts: claude-sdk-typescript tsc now uses --rootDir .
so emitted artifact is /app/dist/agent/index.js (matches entrypoint
expectation + runner-stage COPY). tsc was inferring agent/ as rootDir
and stripping the prefix.
- generate-starters.ts: drop server.mjs from langgraph-typescript starter
output. The starter uses 'npx @langchain/langgraph-cli dev' and never
invokes server.mjs; the file imports @langchain/langgraph-api/server
which is not in the starter's extraDependencies (resolution relied on
transitive hoist via @langchain/langgraph-cli). The file stays in the
PACKAGE where prod mode does use it.
- generate-starters.ts: assert agentDir non-empty / non-absolute before
Dockerfile substitution so the 'rm -f {{AGENT_DIR}}/package*.json' line
can never degrade into 'rm -f /package*.json' across /app.
Verify Marketplace credential expects an active Azure session via
azure/login, but login was gated only on published==false. When
dry_run=true and version is already published, the verify step ran
without a session and failed. Widening the guard to include dry_run.
Add platforms: linux/amd64 to the depot/build-push-action invocation in
showcase_deploy.yml. Railway and GHCR serve x86 hosts, so an arm64-only
image crashes on pull. Matches the platform requirement enforced for
local docker build invocations (documented in
showcase/starters/template/README.md).
Add showcase/starters/template/README.md covering the multi-stage build
convention (builder + runtime split), the >=40% image-size reduction
target, the linux/amd64 platform requirement for local builds, and the
dockerfile_hygiene probe rules (>=2 FROM stages, no dev-dep installs in
the runtime stage).
Expand the header comment on showcase/scripts/generate-starters.ts to
document the >=40% size target, the getAgentBuildSteps/getAgentBuildCopy
handoff in more detail, and the linux/amd64 platform pin in the deploy
workflow.
Package-level service claims to listen on :8000 but Railway health probes
fail. Narrow the cold-start window by timestamping each phase from
entrypoint exec to first /health-serving moment:
- entrypoint.sh: print `[entrypoint] pre-node <ISO>` immediately before
`node /app/agent_server.js`, pairing with the agent_server.ts prints
below to chain timestamps across the exec boundary.
- agent_server.ts: print `[agent_server] module loaded <ISO>` before any
side-effect imports (so import-time hangs surface distinctly from
init-time hangs), `[agent_server] pre-Anthropic <ISO>` right before
`new Anthropic(...)`, and include an ISO timestamp in the existing
app.listen() callback so we can read the delta directly.
No behavior change — only logs added. Next deploy cycle should reveal
which phase consumes the budget and whether the failure mode is
listen-but-unreachable vs. listen-never-reached.
Verify Marketplace credential expects an active Azure session via azure/login, but login was gated only on published==false. When dry_run=true and version is already published, the verify step ran without a session and failed. Widening the guard to include dry_run.
Split the runner image into three stages:
- frontend (node:22-slim) builds Next.js artifacts
- agent-builder (python:3.12.13) builds /opt/venv from requirements.txt
- runner (python:3.12.13-slim) installs Node.js + copies artifacts, with no
pip and no build-essential
Mirrors the pattern already used by showcase/starters/strands/Dockerfile.
Split the runner image into three stages:
- frontend (node:22-slim) builds Next.js artifacts
- agent-builder (python:3.12.13) builds /opt/venv from requirements.txt
- runner (python:3.12.13-slim) installs Node.js + copies artifacts, with no
pip and no build-essential
Mirrors the pattern already used by showcase/starters/pydantic-ai/Dockerfile.
Split the runner image into three stages:
- frontend (node:22-slim) builds Next.js artifacts
- agent-builder (python:3.12.13) builds /opt/venv from requirements.txt
- runner (python:3.12.13-slim) installs Node.js + copies artifacts, with no
pip and no build-essential
Mirrors the pattern already used by showcase/starters/ms-agent-python/Dockerfile.
Split the runner image into three stages:
- frontend (node:22-slim) builds Next.js artifacts
- agent-builder (python:3.12.13) builds /opt/venv from requirements.txt
- runner (python:3.12.13-slim) installs Node.js + copies artifacts, with no
pip and no build-essential
Mirrors the pattern already used by showcase/starters/llamaindex/Dockerfile.
## Summary
- Local `scripts/release/vscode-extension-release.sh` — bumps
`packages/vscode-extension/package.json`, commits, tags
`vscode-extension-v<ver>`, and pushes. Supports `patch` / `minor` /
`major` / explicit SemVer, plus `--dry-run`.
- `.github/workflows/publish-vscode-extension.yml` — tag-triggered
workflow that builds once, packages a single VSIX, and dual-publishes to
VS Code Marketplace + Open VSX. Partial-failure-aware.
- `packages/vscode-extension/RELEASING.md` — runbook covering
prerequisites, first-time Open VSX setup, cutting a release,
verification, and rollback.
## Why this shape
- **Independent of PR #3935.** The extension package itself lives on
#3935's branch and is not yet on `main`. This PR adds the release
*pipeline* only — the workflow references `packages/vscode-extension/`
which will be a valid path once #3935 merges.
- **Outside the existing npm release scope.** The monorepo's
`scripts/release/` pipeline (driven by `release.config.json` scopes
`monorepo` / `cli` / `angular`) is npm-only. VSIX publishing doesn't fit
that model, so this lives next to it as a sibling tag-triggered workflow
rather than being bolted onto `publish-release.ts`.
- **Immutable-artifact pattern.** `vsce package` runs once; the same
`.vsix` is uploaded as a workflow artifact *and* published to both
registries. No version arg is passed to `vsce publish` / `ovsx publish`
— `--packagePath` ensures we ship the exact bits we packaged.
- **Partial-failure visibility.** Both registry publishes use
`continue-on-error` with a final reconciliation step that fails the job
if either failed. You will never end up in \"Marketplace succeeded, Open
VSX silently skipped.\"
## One-time setup required before first CI publish
1. Create Open VSX account + personal access token at
<https://open-vsx.org/user-settings/tokens>.
2. Claim the publisher namespace (one-time): `npx --yes ovsx
create-namespace copilotkit -p <token>`
3. Add `OVSX_PAT` to GH repo → Settings → Environments → `production` →
Environment secrets.
4. Confirm `VSCE_PAT` is already in the same environment (used for the
manual v0.1.0 publish).
Until step 3 lands, tag pushes will fail loudly at the \"Publish to Open
VSX\" step with a clear error — no silent skip.
## Test plan
- [ ] Dry-run the local helper (already verified against the #3935
worktree): `scripts/release/vscode-extension-release.sh patch --dry-run`
previews `0.1.0 → 0.1.1`, `minor` previews `0.2.0`, explicit `1.2.3` is
honored.
- [ ] Once #3935 merges and an `OVSX_PAT` is set, maintainer runs
`scripts/release/vscode-extension-release.sh patch` on `main` and
watches
<https://github.com/CopilotKit/CopilotKit/actions/workflows/publish-vscode-extension.yml>.
- [ ] Workflow triggers on the `vscode-extension-v*` tag, builds,
packages, uploads artifact.
- [ ] VSIX published to both registries; listings update within minutes.
- [ ] `code --install-extension copilotkit.copilotkit-vscode-extension
--force` installs the new version.
## Caveats
- This PR will have a minor merge conflict with #3935 if #3935 adds its
own `RELEASING.md` (it does not today). Otherwise the paths are
disjoint: this PR only touches `scripts/release/`, `.github/workflows/`,
and creates a new file in `packages/vscode-extension/`.
- Azure DevOps long-lived Marketplace PATs retire on 2026-12-01 — OIDC
migration is out of scope for this PR and tracked separately in the
Notion playbook.
Split Python install into a dedicated agent-builder stage (python:3.12.13)
that produces /opt/venv, which the slim runner COPYs in. The runner no
longer runs pip install, so no compiler toolchain or build-essential bloats
the final image.
Preserves behavior: entrypoint.sh, port 10000, src/aimock_toggle.py,
shared_python/ -> /app/shared/python/ + PYTHONPATH. No image-level
NODE_ENV (entrypoint.sh scopes it to Next.js only).
Split Python install into a dedicated agent-builder stage (python:3.12.13)
that produces /opt/venv, which the slim runner COPYs in. The runner no
longer runs pip install, so no compiler toolchain or build-essential bloats
the final image.
Preserves behavior: entrypoint.sh, port 10000, ENV NODE_ENV=production,
shared_python/ -> /app/shared/python/ + PYTHONPATH.
Split Python install into a dedicated agent-builder stage (python:3.12.13)
that produces /opt/venv, which the slim runner COPYs in. The runner no
longer runs pip install, so no compiler toolchain or build-essential bloats
the final image.
Preserves behavior: entrypoint.sh, port 10000, ENV NODE_ENV=production,
shared_python/ -> /app/shared/python/ + PYTHONPATH.
Split Python install into a dedicated agent-builder stage (python:3.12.13)
that produces /opt/venv, which the slim runner COPYs in. The runner no
longer runs pip install, so no compiler toolchain or build-essential bloats
the final image.
Preserves behavior: entrypoint.sh, port 10000, ENV NODE_ENV=production,
shared_python/ -> /app/shared/python/ + PYTHONPATH.
The dry-run summary referenced scripts/release/vscode-extension-release.sh,
which was removed in 17c4daee3 (drop release helper script). Point
maintainers at the new CHANGELOG-edit flow instead.
Rewrites the 'Cutting a release' section to match the new
changelog-sync automation: maintainers prepend a CHANGELOG entry,
commit with any message, open a PR, and the sync workflow auto-bumps
package.json with 'chore: release vX.Y.Z'. Adds a manual escape hatch
for when the sync workflow is unavailable.
Other sections (Prerequisites, Open VSX setup, CI publish flow,
Rollback, Notes) are unchanged.
Adds .github/workflows/vscode-extension-changelog-sync.yml: on PRs that
touch packages/vscode-extension/CHANGELOG.md, read the top '## X.Y.Z'
entry and bump packages/vscode-extension/package.json to match via an
auto-committed 'chore: release vX.Y.Z' on the PR branch.
Collapses the release maintainer flow to a single CHANGELOG edit.
Forks are skipped (GITHUB_TOKEN can't push to fork branches). A
guardrail rejects CHANGELOG entries older than the current package.json
version (catches history edits vs. prepends).
Lets us validate the Marketplace-OIDC and Open VSX auth chains end-to-end
without actually publishing. workflow_dispatch now accepts a dry_run input
(default true); when set, the job runs checkout, Azure login,
vsce verify-pat, and a new Open VSX /api/user token introspection, then
stops before any publish, tag, release, or Slack success notify.
Push trigger is unchanged — dry_run is only meaningful for manual runs.
- Use slackapi/slack-github-action@v2.1.0 matching 8+ existing workflows
- Switch from SLACK_WEBHOOK to SLACK_WEBHOOK_OSS_ALERTS (the secret
actually wired up in the repo, routed to #oss-alerts)
- Guard with env.SLACK_WEBHOOK_OSS_ALERTS != '' for repo-fork safety
- Add failure-path notification with failed job + per-registry outcomes
+ run URL (follows memory rule: red alerts carry actionable detail)
- Expose publisher as a step output to construct Open VSX URL cleanly
(https://open-vsx.org/extension/<publisher>/<name>) instead of inline
tr substitution
Rewrite the 'Cutting a release' + 'CI publish flow' sections to describe
the aimock-pattern flow (hand-bump + PR-merge + self-gated CI publish)
and the new multi-bullet --summary/--type CLI. Retain the Open VSX
one-time-setup block, the transient-failure handling notes, verification
steps, rollback guidance, and ADO PAT retirement note.
Rewrite vscode-extension-release.sh to cut an aimock-style release commit
(bump package.json + prepend CHANGELOG.md section) without tagging or
pushing — CI now owns tag/release creation on main-push. Takes one or
more --summary bullets grouped by --type (Added/Changed/Fixed/Removed/
Deprecated/Security, Keep-a-Changelog style). Supports --dry-run.
Seed packages/vscode-extension/CHANGELOG.md with a retroactive v0.1.0
entry covering the three launch surfaces (A2UI Preview, Hook Explorer,
AG-UI Inspector) so the first real release has prior history to anchor
against and the GH Release step has notes to pull from.
Replace tag-triggered publish with push-to-main + version-on-Marketplace
self-gate, matching CopilotKit/aimock. CI reads version from package.json,
queries vsce show for that version, and no-ops if already published. On a
new version it builds once, dual-publishes (Marketplace + Open VSX with
the existing retry wrappers and idempotent 'already exists' handling),
tags vscode-extension-vX.Y.Z, cuts a GitHub Release from the CHANGELOG
section, and posts to SLACK_WEBHOOK if configured. Path-scoped to
packages/vscode-extension/** so unrelated pushes don't trigger the job.
Wrap both registry publish steps in a bash retry helper that retries up
to 5 times with staggered backoff (10s/20s/40s/60s/90s) on transient
conditions (5xx, timeouts, connection resets, DNS). Auth and validation
errors still fail fast with no retry.
Critically, 'version already exists' is treated as idempotent success:
if attempt N-1 landed on the registry but its response was lost to a
502 after commit, attempt N sees the already-published version and
short-circuits rather than failing the job.
Motivated by Open VSX /publish returning intermittent 502 Bad Gateway
errors from Eclipse Foundation infra. Each attempt is wrapped in
::group:: markers so per-attempt logs are collapsible in the Actions
UI. Reconciliation step updated to reflect retry semantics and to
tell the operator to rerun the job (not bump the version) on exhausted
retries. RELEASING.md gets a 'Transient registry failures' section
documenting the behavior for both CI and manual publish paths.