This Linux-only smoke uses the Node SDK's startAhpHost() and the standard
@microsoft/agent-host-protocol TypeScript client. There is no application-side
AHP relay, custom protocol implementation, or prototype callback adapter.
The runtime hosts the full AHP listener in-process and connects it as another
SDK participant to that same runtime. /proc verifies the runtime executable,
loaded provider, descendants, and ownership of the listening socket's inode.
There is no companion host or second runtime. Disposing the host closes its
listener and connected AHP client while the runtime and owner's SDK session survive.
host.pid is absent for in-process listeners; stop hosting with dispose(). There is no
host process-isolation boundary, and onExit reports hosting-task termination,
not process death. In-process hosts have no exitCode.
Both source-build and assembled local-candidate modes run the same tests and manual smoke; only artifact selection differs.
- Use the
copilot-agent-runtimecheckout. This SDK iscopilot-agent-runtime/src/sdk, at the same source commit as the runtime. - Build the native
copilot-runtimelauncher and its provider shared library from the localcopilot-agent-runtimecheckout. - The provider links
copilotd-hostingfrom the immutablecopilot-hostGit revision in the runtime'sCargo.lock. No separate host build is staged. - Install this checkout's
nodejsandtest/harnessdependencies. - Set the two absolute source-built artifact paths below. Missing paths fail immediately; this smoke never falls back to a downloaded CLI/runtime package.
The exact Node dev dependency is @microsoft/agent-host-protocol@0.9.0, matching
host's rust/v0.9.0 pin (commit
60706330f2f351b09f150d9a9c3c0eaedfc8e8b9) and wire version 0.9.0.
The old ahp-adapter prototype's 0.7.0 client is not compatible.
From the SDK root (copilot-agent-runtime/src/sdk), with a valid GitHub Copilot credential in
GITHUB_TOKEN or GH_TOKEN:
export COPILOT_CLI_PATH=/absolute/path/to/local/runtime-executable
export COPILOT_RUNTIME_PROVIDER_LIB=/absolute/path/to/local/libcopilot_runtime.so
nodejs/node_modules/.bin/tsx samples/runtime-host/smoke.ts "$PWD"This sends one ordinary arithmetic turn through AHP and one through the SDK,
checks streamed AHP deltas and runtime session coexistence, then verifies
disposal. It never prints the listener's bearer token. The smoke intentionally
uses the same small standard-client helper as the automated tests.
Before creating the AHP session, the client discovers the agent's advertised
GitHub protected resource and sends the standard authenticate command with
the GitHub credential. Listener authentication and GitHub authentication remain
separate; no protected-resource checks are bypassed.
mkdir -p .runtime-host-test-work
export TMPDIR="$PWD/.runtime-host-test-work"
export COPILOT_RUNTIME_HOST_E2E=1
export GITHUB_ACTIONS=true
cd nodejs
npm test -- test/e2e/runtime_host.e2e.test.tsTMPDIR keeps the existing harness's isolated homes, workspaces, and proxy files
inside the checkout. The opt-in flag avoids running a source-build-only suite
against released artifacts. These tests explicitly use the local runtime's TCP
transport so dropping one owner connection does not kill the shared runtime.
Non-model scenarios cover direct-RPC concurrent/repeated disposal, occupied-port startup failure and recovery, listener endpoint/token settings and validation, and graceful runtime shutdown with an attached AHP session. The shutdown case requires a successful RPC and clean exit notification, not just eventual forced termination. To run only those:
npm test -- test/e2e/runtime_host.e2e.test.ts -t 'disposes|startup failure|honors|disables|gracefully'The three inference-backed scenarios prove rejection of a second same-home host,
owner-disconnect recovery with ordinary SDK sessions still usable, and
catalog/history recovery after repeated listener disposal and a full
runtime restart using an SDK baseDirectory. A second runtime using that directory can run ordinary SDK
sessions but cannot start another AHP writer. The catalog excludes ordinary
SDK sessions. These scenarios use the existing CapiProxy, snapshot matcher, and canonical
session/sendandwait_blocks_until_session_idle_and_returns_final_assistant_message.yaml
conversation: model claude-sonnet-5, prompt What is 2+2?. No recordings or
responses are fabricated. GITHUB_ACTIONS=true enforces replay-only matching;
an incompatible request fails rather than silently contacting live inference.
Do not overwrite that shared recording to accommodate a different request. If
the host changes its request shape, record a separate scenario using the existing
harness and a real credential, then review the resulting traffic before use.
Exit assertions use the optional onExit callback, not a handle-owned promise.
The tests independently inspect listener closure and runtime survival, or runtime
reaping on owner shutdown. They do not claim process-crash isolation or injected
task-failure coverage. The
connection-token-disabled case still requires normal AHP resource authentication.
Listener tests also cover supplied/generated tokens, hostname resolution, explicit
non-loopback binding, IPv6 endpoints, and omitted/zero/fixed ports.
These AHP tests remain opt-in until coordinated runtime publication and SDK runtime pin updates land. Local unsigned/debug candidates do not verify macOS notarization, the normal full Bazel build, or other platform release executions.
After building the local runtime's dist-cli assets and its launcher/provider,
commit all source changes in the runtime checkout, then assemble
an unpublished local platform package:
# Keep the two source artifact overrides from the prerequisites for staging.
nodejs/node_modules/.bin/tsx samples/runtime-host/stage-candidate.ts \
/absolute/copilot-agent-runtime \
"$PWD/.runtime-host-test-work/candidate"
export COPILOT_RUNTIME_HOST_CANDIDATE_MANIFEST="$PWD/.runtime-host-test-work/candidate/candidate.json"
unset COPILOT_CLI_PATH COPILOT_RUNTIME_PROVIDER_LIB
cd nodejs
npm test -- test/e2e/runtime_host.e2e.test.tsThe output directory must not already exist. Staging uses the SDK's actual
platform asset materializer over local dist-cli, the runtime's package metadata
and verification helpers.
It then runs npm pack and extracts that archive as the candidate package.
The version 3 manifest records the runtime repository root and full source
commit plus the immutable linked copilotd-hosting source from Cargo.lock.
The SDK source identifies the runtime repository and commit with
path: "src/sdk", rather than claiming a third repository. The runtime argument
must be the repository root containing this SDK.
The manifest also records original paths, binary hashes, and archive hash.
No separate host checkout, host executable, or host-side SDK patch is required.
The hosting library uses its own runtime-free SDK dependency; staging does not
rewrite its Cargo graph.
The private package's 0.0.0-canary.r1.g<SHA>.unsigned version satisfies the
runtime metadata helper's grammar; r1 is a local placeholder, not release
provenance. Nothing is downloaded or published. Rebuild and restage after changing
sources; do not relabel old binaries as a new build.
Set COPILOT_RUNTIME_HOST_CANDIDATE_MANIFEST to an absolute local manifest path
instead of setting development artifact overrides. Keep
COPILOT_RUNTIME_HOST_E2E=1, GITHUB_ACTIONS=true, and checkout-local TMPDIR
for E2Es; run the identical command above.
The package must contain local build outputs at
prebuilds/<platform>/{copilot-runtime,runtime.node} for in-process hosting. Its
package.json must identify @github/copilot-<platform> or
@github/copilot-sdk-<platform> and carry existing copilotRuntime metadata:
sourceRepository: "github/copilot-agent-runtime" and sourceSha equal to the
manifest's runtime source commit.
The staging step writes this local-build attestation (replace all placeholders; checksums are lowercase SHA-256 of the actual local build outputs):
{
"schemaVersion": 3,
"kind": "local-runtime-host-candidate",
"platform": "linux-x64",
"packageRoot": "/absolute/local-candidate/node_modules/@github/copilot-linux-x64",
"sources": {
"runtime": {
"repository": "github/copilot-agent-runtime",
"checkout": "/absolute/copilot-agent-runtime",
"commit": "<full local runtime HEAD>"
},
"hosting": {
"repository": "github/copilot-host",
"commit": "<full locked copilotd-hosting Git revision>"
},
"sdk": {
"repository": "github/copilot-agent-runtime",
"checkout": "/absolute/copilot-agent-runtime",
"commit": "<full local runtime HEAD>",
"path": "src/sdk"
}
},
"artifacts": {
"runtime": {
"path": "prebuilds/linux-x64/copilot-runtime",
"sourcePath": "/absolute/copilot-agent-runtime/local-build/copilot-runtime",
"sha256": "<SHA-256>"
},
"provider": {
"path": "prebuilds/linux-x64/runtime.node",
"sourcePath": "/absolute/copilot-agent-runtime/local-build/runtime.node",
"sha256": "<SHA-256>"
}
}
}Source revisions must match the runtime checkout and its locked hosting library,
including this SDK's runtime commit. Legacy version 1/2 manifests must be restaged; editing an old attestation
does not validate the new sources.
Original build artifacts must live inside the runtime checkout, outside
node_modules; candidate packages may live inside node_modules.
Do not attest downloaded releases as local builds.
The helper checks the original and packaged artifact hashes, then invokes the
SDK's existing materializeRuntimeBundle into .runtime-host-materialized
beside the manifest and rechecks the materialized hashes.
The runtime child environment explicitly omits the provider development
override—even if it is set in the invoking shell. /proc assertions verify
the actual loaded provider and socket-owning runtime are the validated package
copies, with no companion host or second runtime. This exercises bundled lookup rather than
another source-path override.