Skip to content

ci: add GitHub Actions workflows for CI + release-notes (POC stage)#10

Merged
manojp99 merged 1 commit into
mainfrom
ci/github-actions-poc
May 26, 2026
Merged

ci: add GitHub Actions workflows for CI + release-notes (POC stage)#10
manojp99 merged 1 commit into
mainfrom
ci/github-actions-poc

Conversation

@manojp99

Copy link
Copy Markdown
Collaborator

Summary

Adds two GitHub Actions workflows for the POC stage of packaging. Scope is deliberately minimal — CI and release-notes only, no publish-to-registry workflows yet.

What's in this PR

.github/workflows/ci.yml

Runs on every push to main and every PR. Two parallel jobs:

  • Python tests + lint + types: uv syncruff check src/ tests/pyright src/pytest tests/ -q. ~3–5 min.
  • TypeScript wrapper tests: in wrappers/typescript/, runs bun installbun run build (tsc) → bun run test (vitest). ~1–2 min.

Concurrency group cancels in-flight runs for the same ref when a new commit arrives. 15-min timeout on the Python job, 10-min on the wrapper job.

.github/workflows/release-notes.yml

Triggered on tag push matching v* (engine releases) or wrapper-v* (TS wrapper releases). Uses softprops/action-gh-release with generate_release_notes: true to auto-create a GitHub Release with changelog notes derived from PRs/commits since the previous tag.

Tags with a - suffix (e.g. v0.3.0-rc1) are auto-flagged as prerelease. This gives consumers of the POC-stage git+install pattern a stable, pinnable reference point.

What's deliberately NOT in this PR

Publish-to-registry workflows (PyPI for amplifier-agent, npm for amplifier-agent-client-ts) are not included.

Rationale: in the POC stage, we want package names to stay swappable without consumer-facing migration. The git+install path (uv tool install "amplifier-agent @ git+https://github.com/microsoft/amplifier-agent@<ref>" for Python; gitpkg.vercel.app proxy or future npm publish for TS) gives full naming freedom and zero token friction for consumers.

We'll add publish workflows in a follow-up PR once names stabilize.

Tag conventions established here

Tag pattern Releases Triggers
v0.3.0, v0.3.0-rc1, etc. amplifier-agent engine release-notes.yml
wrapper-v0.3.0, etc. amplifier-agent-client-ts release-notes.yml

Separate tag namespaces let engine and wrapper version independently, which matches the design where the engine is at v0.2.0 (additive-only changes) while the wrapper is at v0.3.0 (CR-C breaking DisplayEvent shape change).

Verification

  • Workflow YAML files parsed locally with yq — both valid.
  • Each job's commands match what's in pyproject.toml (uv run pytest, uv run ruff, uv run pyright) and wrappers/typescript/package.json (bun run build, bun run test).
  • This PR's own CI run will be the first execution of the new workflows.

Test Plan

  • CI workflow runs and goes green on this PR
  • Both python-tests and ts-wrapper-tests jobs complete successfully
  • Release-notes workflow won't fire until first tag push (no test needed on this PR)

Two workflows:

1. .github/workflows/ci.yml — runs on push to main + every PR.
   - Python: uv sync, ruff check, pyright, pytest. ~3-5 min.
   - TypeScript wrapper: bun install, tsc build, vitest run. ~1-2 min.
   - Concurrency group cancels in-flight runs for the same ref on new pushes.

2. .github/workflows/release-notes.yml — runs on tag push (v* or wrapper-v*).
   - Auto-generates GitHub Release with changelog notes derived from PRs/commits.
   - Tag with '-' suffix is auto-flagged as prerelease (v0.3.0-rc1, etc.).
   - Gives consumers a stable pin target for git+install workflows.

Publish-to-registry workflows (PyPI, npm) deliberately NOT added yet — POC
stage prefers git+install paths from public GitHub so package names stay
swappable without consumer-facing migration. Will add publish workflows
once names stabilize.

Co-Authored-By: Amplifier <amplifier@microsoft.com>
@manojp99 manojp99 merged commit 02bbe60 into main May 26, 2026
1 of 3 checks passed
manojp99 added a commit that referenced this pull request May 26, 2026
…m git+install (#11)

* feat(packaging): add root package.json + commit wrapper dist for git+install

Enables bun/npm consumers to install amplifier-agent-client-ts via native
git URL syntax:

    bun add github:microsoft/amplifier-agent#main

Pattern: a minimal shipping-only package.json at the repo root points
(via 'main', 'types', 'exports') at wrappers/typescript/dist/. The
wrappers/typescript/package.json remains the dev manifest with scripts,
devDependencies, and tsconfig — unchanged.

Trade-off: wrappers/typescript/dist/ is now committed to the repo
(~108KB, 20 files). The CI workflow added in #10 already builds the
wrapper on every push; a follow-up can add an auto-commit step so dist
stays current without manual rebuild discipline. For now, the
'wrapper-v*' tag workflow gives consumers stable pinnable refs.

Why this pattern (vs alternatives):
- npm publish: requires registry account, name commitment, token for
  consumers in some flows. Deferred until POC graduates.
- gitpkg.vercel.app subdir proxy: third-party service, currently
  disabled (HTTP 402 DEPLOYMENT_DISABLED).
- Dist branch (separate refs): requires force-push CI; this is simpler.
- Separate repo for wrapper: needs new repo + code-sync overhead.

Consumer install path (in any project's package.json):

    "amplifier-agent-client-ts": "github:microsoft/amplifier-agent#main"

bun/npm clones the repo, finds package.json at root, the 'files' field
restricts to wrappers/typescript/dist/, the 'exports' field resolves
the entry point. No tokens, no proxies, no registries.

Co-Authored-By: Amplifier <amplifier@microsoft.com>

* fix(packaging): use conditional exports with explicit types resolution

Updates /package.json exports from string form:

  "exports": { ".": "./wrappers/typescript/dist/index.js" }

to conditional form with explicit types resolution:

  "exports": {
    ".": {
      "types": "./wrappers/typescript/dist/index.d.ts",
      "import": "./wrappers/typescript/dist/index.js"
    }
  }

Modern TypeScript (with moduleResolution: bundler/nodenext) follows the
exports field and looks for a 'types' condition before falling back to
the top-level 'types' field. The string-form exports left only 'import'
resolvable, so consumer projects (e.g. NanoClaw's agent-runner) saw
'McpServerConfig' as 'any' or unresolved, surfacing as TS2339 errors
like 'Property transport does not exist on type McpServerConfig'.

Verified locally: NC's bun run typecheck went from 5 errors to 0 after
this change.

Order matters in conditional exports: 'types' MUST appear FIRST so TS
checks it before module conditions. This follows the TypeScript handbook
guidance for the exports field.

Co-Authored-By: Amplifier <amplifier@microsoft.com>

---------

Co-authored-by: Manoj Prabhakar Paidiparthy <mpaidiparthy@microsoft.com>
Co-authored-by: Amplifier <amplifier@microsoft.com>
manojp99 pushed a commit that referenced this pull request Jun 3, 2026
…th (#1)

Engine PR #27 / v0.4.0 added the --config <path> flag and the
host_config layer (approval mode, MCP servers, provider defaults,
allowProtocolSkew, etc.). The wrapper had no surface to forward this,
so callers had to fall back to AMPLIFIER_AGENT_CONFIG in env.extra.

This change:

  - Adds SpawnAgentParams.configPath?: string (public, @public TSDoc).
  - Adds AssembleArgvInput.configPath?: string.
  - assembleArgv emits --config <path> when configPath is set.
  - Threads configPath through SessionHandleParams to the per-submit
    argv assembly.

Also drive-by adds approvalMode field to AssembleArgvInput (used by
#10's commit). The argv-builder now reads input.approvalMode and emits
-y / -n / nothing accordingly. Default remains -y for backward compat
with callers that haven't opted into the approval API.

Closes #1.
manojp99 pushed a commit that referenced this pull request Jun 3, 2026
…10)

Previously, SpawnAgentParams.approval threw AaaError(
approval_not_supported_in_v1) whenever set because it required the
mid-turn onRequest callback that v1 doesn't support.

This change extends SpawnAgentParams.approval to also accept the
static-policy shape { mode: 'yes' | 'no' | 'prompt' }, which maps to
engine argv:

  - 'yes'    -> -y (auto-allow every tool call)
  - 'no'     -> -n (auto-deny every tool call)
  - 'prompt' -> emit no flag; engine falls back to
                host_config.approval.mode or the bundle's TTY-based
                default. This is how a host hands policy resolution
                back to the engine.

The legacy { onRequest, timeoutMs } form still throws
approval_not_supported_in_v1 — the Mode A wire has no mid-turn
channel. Mid-turn callbacks will return when WG-4 lands.

Engine compatibility: { mode: 'prompt' } requires
amplifier-agent >= 0.4.0 (PR #34 added host_config.approval.mode).

Closes #10.

BREAKING CHANGE: SpawnAgentParams.approval is now a union shape;
callers passing { mode } no longer hit approval_not_supported_in_v1.
Callers that defensively catch that error need to remove the try/catch
when migrating to the mode shape.
manojp99 pushed a commit that referenced this pull request Jun 3, 2026
Mirrors PR #29 / #31 pattern: dist/ is tracked so consumers installing
from the git tarball get the compiled artifacts without a build step.

Regenerated from npm run build after issues #1, #2, #3, #4, #5, #6, #7,
#9, #10 landed.
manojp99 pushed a commit that referenced this pull request Jun 3, 2026
Wrapper hardening release closing 8 consumer-reported gaps at 0.5.0:
  #1  configPath surface
  #2  stderr NDJSON parsing
  #3  runChildProcess injection
  #4  display.onEvent dispatch
  #5  public re-exports
  #6  Transport dead code (root cause of #2/#4)
  #7  getEngineInfo() implementation
  #9  checkProtocolVersion() wired into init path
  #10 approval API mapped to engine -y/-n + approval.mode

Issue #8 in the consumer report was a misread — InitializeParams.
mcpConfigPath is intentionally retained in protocol-0.3.0. No
type change needed; the schema is canonical and correct.

This is a minor bump per 0.x convention even though some changes
are BREAKING — the wrapper hasn't shipped a 1.0 yet, so breaking
changes ride minor bumps. See CHANGELOG for the BREAKING list.

Engine compatibility: requires amplifier-agent >= 0.4.0.
Pinned protocol: 0.3.0.
manojp99 added a commit that referenced this pull request Jun 3, 2026
…, approval, getEngineInfo, +5 more) (#36)

* feat(wrapper-ts): re-export internal helpers from index.ts (#5)

Adds named re-exports from the package entry point so consumers can
import internal helpers without reaching into private deep paths:

  assembleArgv, AssembleArgvInput
  resolveMcpConfigPath, cleanupSpillFile, McpSpillResult
  buildEnv, resolveBinaryPath, probeEngineVersion,
    DEFAULT_ALLOWLIST, BLOCKED_ENV_KEYS,
    ResolveBinaryPathOptions, BuildEnvOptions
  Transport, TransportOptions, ExitInfo
  checkProtocolVersion, VersionCheckResult, VersionCheckOk,
    VersionCheckFail, CheckProtocolVersionOptions
  parseRunOutput, STDERR_TAIL_BYTES, SubprocessOutcome
  makeApprovalHandler, ApprovalAdapter, ApprovalRequest,
    ApprovalHandler

Each export is annotated @public.

Closes #5.

* feat(wrapper-ts): wire checkProtocolVersion() into init path (#9)

spawnAgent() now probes the engine's protocol version once during
initialization (via amplifier-agent version --json) and runs
checkProtocolVersion() against PROTOCOL_VERSION_REQUIRED_BY_WRAPPER
BEFORE constructing a SessionHandle. Mismatch fails fast wrapper-side
with AaaError(protocol_version_mismatch), saving a full subprocess
roundtrip later.

Adds two new SpawnAgentParams fields:
  - allowProtocolSkew?: boolean — bypass the check (mirrors engine's
    host_config.allowProtocolSkew)
  - _engineVersionProbe?: () => Promise<EngineVersionPayload> —
    test-only injection point for the probe

Also bumps PROTOCOL_VERSION_REQUIRED_BY_WRAPPER from "0.2.0" to
"0.3.0" to match the engine's current wire protocol
(amplifier_agent_lib.protocol.methods.PROTOCOL_VERSION). The wrapper
was shipping with a stale pin; the new check would have surfaced this
at startup.

Closes #9.

* feat(wrapper-ts): add runChildProcess injection point (#3)

Adds SpawnAgentParams.runChildProcess?: ChildProcessFactory — a public
seam to substitute the subprocess factory used inside SessionHandle.
When set, the wrapper invokes the factory in place of
child_process.spawn, preserving the same options shape (detached, stdio,
env, optional cwd).

Useful for:
  - Sandboxing (e.g. wrapping the child in a container or namespace)
  - Test doubles (e.g. EventEmitter fakes that drive scripted outputs)
  - Harness wrapping (e.g. observing the subprocess from outside)

ChildProcessFactory is exported as a @public type from index.ts.

Closes #3.

* feat(wrapper-ts)!: wire Transport NDJSON pipeline + dispatch to display.onEvent (#2, #4, #6)

The engine emits one JSON object per line on the child subprocess's
stderr stream for each wire-protocol notification (progress,
result/delta, result/final, thinking/delta, thinking/final,
tool/started, tool/completed, approval/request, approval/timeout,
plus wire-level error). Before this change the wrapper buffered
stderr as raw text and silently dropped every event — the existing
Transport class implemented NDJSON parsing but was never wired
anywhere (dead code).

This change:

  - Adds parseNdjsonStream(stream, {onJson, onNonJson?}) — a
    standalone helper extracted from the parsing logic Transport
    already had. Resolves when the stream emits 'close'. Exported
    @public.

  - Wires parseNdjsonStream onto child.stderr inside
    SessionHandle.makeIterable(). JSON lines are parsed into
    'notification' DisplayEvents and dispatched to
    params.display?.onEvent. Non-JSON lines (and JSON lines, for
    completeness) are still accumulated into stderrBuf so the
    stderrTail surface on parseRunOutput remains diagnostically
    useful.

  - Extends the DisplayEvent discriminated union with a new
    {type: 'notification', method: string, params: unknown}
    variant. **BREAKING**: existing exhaustive switch statements
    on event.type will no longer be exhaustive without a
    notification branch.

  - Threads SpawnAgentParams.display through to SessionHandle so
    the callback that was previously silently dropped is now
    actually fired (Issue #4).

Closes #2, #4, #6.

BREAKING CHANGE: display.onEvent callbacks are now actually invoked
with wire-event notifications. Callers that registered onEvent
expecting it to be a no-op may observe new event flow. The
DisplayEvent union has a new 'notification' variant; exhaustive
switch statements need a corresponding branch.

* feat(wrapper-ts): surface --config flag via SpawnAgentParams.configPath (#1)

Engine PR #27 / v0.4.0 added the --config <path> flag and the
host_config layer (approval mode, MCP servers, provider defaults,
allowProtocolSkew, etc.). The wrapper had no surface to forward this,
so callers had to fall back to AMPLIFIER_AGENT_CONFIG in env.extra.

This change:

  - Adds SpawnAgentParams.configPath?: string (public, @public TSDoc).
  - Adds AssembleArgvInput.configPath?: string.
  - assembleArgv emits --config <path> when configPath is set.
  - Threads configPath through SessionHandleParams to the per-submit
    argv assembly.

Also drive-by adds approvalMode field to AssembleArgvInput (used by
#10's commit). The argv-builder now reads input.approvalMode and emits
-y / -n / nothing accordingly. Default remains -y for backward compat
with callers that haven't opted into the approval API.

Closes #1.

* feat(wrapper-ts)!: wire approval API to engine -y/-n + approval.mode (#10)

Previously, SpawnAgentParams.approval threw AaaError(
approval_not_supported_in_v1) whenever set because it required the
mid-turn onRequest callback that v1 doesn't support.

This change extends SpawnAgentParams.approval to also accept the
static-policy shape { mode: 'yes' | 'no' | 'prompt' }, which maps to
engine argv:

  - 'yes'    -> -y (auto-allow every tool call)
  - 'no'     -> -n (auto-deny every tool call)
  - 'prompt' -> emit no flag; engine falls back to
                host_config.approval.mode or the bundle's TTY-based
                default. This is how a host hands policy resolution
                back to the engine.

The legacy { onRequest, timeoutMs } form still throws
approval_not_supported_in_v1 — the Mode A wire has no mid-turn
channel. Mid-turn callbacks will return when WG-4 lands.

Engine compatibility: { mode: 'prompt' } requires
amplifier-agent >= 0.4.0 (PR #34 added host_config.approval.mode).

Closes #10.

BREAKING CHANGE: SpawnAgentParams.approval is now a union shape;
callers passing { mode } no longer hit approval_not_supported_in_v1.
Callers that defensively catch that error need to remove the try/catch
when migrating to the mode shape.

* feat(wrapper-ts): implement getEngineInfo() — engineVersion + bundleDigest (#7)

Closes the Task-9 TODO: getEngineInfo() now returns the values
captured during the engine version probe that spawnAgent() runs at
init (Issue #9). Previously both fields were hardcoded empty strings.

  - engineVersion populated from `amplifier-agent version --json`
    payload's `version` field.
  - bundleDigest populated from the probe payload's optional
    `bundleDigest` field. The engine's current `version --json`
    output (from admin/version_info.py) only emits {version,
    protocolVersion} — bundleDigest will be empty string until a
    future engine release exposes it. Forward-compatible: when the
    engine adds it, the wrapper picks it up automatically with no
    further changes.

DONE_WITH_CONCERNS for the bundleDigest follow-up: filed as an
engine-side ask for a future PR. The wrapper does what it can with
the data the engine surface exposes today; the contract is wired
so the field will populate the moment the engine emits it.

Closes #7.

* chore(wrapper-ts): rebuild dist after hardening release changes

Mirrors PR #29 / #31 pattern: dist/ is tracked so consumers installing
from the git tarball get the compiled artifacts without a build step.

Regenerated from npm run build after issues #1, #2, #3, #4, #5, #6, #7,
#9, #10 landed.

* chore(release): bump amplifier-agent-ts to 0.6.0 + CHANGELOG

Wrapper hardening release closing 8 consumer-reported gaps at 0.5.0:
  #1  configPath surface
  #2  stderr NDJSON parsing
  #3  runChildProcess injection
  #4  display.onEvent dispatch
  #5  public re-exports
  #6  Transport dead code (root cause of #2/#4)
  #7  getEngineInfo() implementation
  #9  checkProtocolVersion() wired into init path
  #10 approval API mapped to engine -y/-n + approval.mode

Issue #8 in the consumer report was a misread — InitializeParams.
mcpConfigPath is intentionally retained in protocol-0.3.0. No
type change needed; the schema is canonical and correct.

This is a minor bump per 0.x convention even though some changes
are BREAKING — the wrapper hasn't shipped a 1.0 yet, so breaking
changes ride minor bumps. See CHANGELOG for the BREAKING list.

Engine compatibility: requires amplifier-agent >= 0.4.0.
Pinned protocol: 0.3.0.

---------

Co-authored-by: Manoj Prabhakar Paidiparthy <mpaidiparthy@microsoft.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant