Releases: polymind-inc/agent-framework-js
Release list
v0.4.0
A hardening and consolidation release: ten breaking changes tighten types, credentials, telemetry
and the supported surface against the reference implementations — Foundry configuration
centralizes in FoundryProject, GenAI message events move to the chat span with v1.36.0
bodies, and the provider request-assembly helpers leave the supported core surface — alongside
safety fixes across streaming, serialization, hosting and the release pipeline.
- [BREAKING]
@polymind-inc/agent-framework-core—setIfDefined,withoutUndefined,
topLevelMediaTypeandarrayToStreamare no longer exported from the root entry. They assemble
a request payload for a specific provider SDK — an implementation detail of writing a
ChatClient, not part of the programming model — and none of the reference implementations
publishes a counterpart in this shape. They now live on the/internalsubpath the framework's
own packages share, which carries no compatibility promise. Each is under ten lines and inlines
directly:setIfDefined/withoutUndefineddrop the entries whose value isundefined,
topLevelMediaTypelowercases the part of a media type before the/, andarrayToStream
yields an array's items from an async generator. - [BREAKING]
@polymind-inc/agent-framework-core— theMiddlewareKindtype is removed. It
was a free-standing alias nothing in the framework consumed; the discriminant lives on the
middleware objects themselves, soMiddleware['kind']expresses the same type where one is
needed. - [BREAKING]
@polymind-inc/agent-framework-agentserver— theTERMINAL_EVENT_TYPES
constant is no longer exported. TheTerminalEventTypetype and theisTerminalEventType
predicate remain: use the predicate to test an individual event type. Code that enumerated or
iterated the exported tuple has no direct replacement. @polymind-inc/agent-framework-core— a new/internalsubpath holds the utilities the
framework's own packages share. Like the other/internalentries, it is a
contract between the framework's packages and not part of the supported surface.@polymind-inc/agent-framework-anthropic— a replayedfunction_callwhose string
arguments parse to a JSON array or scalar no longer reaches the API astool_use.input; it
degrades to{}like every other non-object payload, keepinginputthe JSON object the
Messages API requires.- [BREAKING]
@polymind-inc/agent-framework-foundry— Foundry credentials and the project
endpoint are centralized in the newFoundryProjecthandle
(new FoundryProject(endpoint, credential, { scope?, fetch? }), mirroring
new AIProjectClient(endpoint, credential)from@azure/ai-projects: both arguments are
required, and neither is resolved from the environment or constructed implicitly).
FoundryChatClient,FoundryMemoryProvider,FoundryToolboxandFoundryResponseStoretake a
requiredprojectinstead of the removed per-componentcredential,scopeand
projectEndpointoptions — previously each component silently constructed its own
DefaultAzureCredentialchain, and a staleFOUNDRY_PROJECT_ENDPOINTcould silently decide
where bearer tokens were sent. Every component built from one project shares one per-scope
bearer-token cache. ThetokenProviderexport is removed;project.getToken()is its
successor. The one remaining implicit default is inside the hosting bootstrap: a hosted
container's default response store still assembles its project from the platform-injected
endpoint andDefaultAzureCredential, the same default the Python agent server's Foundry
storage applies. The reference implementations all demand this explicitness: .NET and Python
hang every Foundry feature off an explicitly constructedAIProjectClient(Python requires
credentialwhenever noproject_clientis given), and Go takes a credential as a required
constructor argument. - [BREAKING]
@polymind-inc/agent-framework-core—ResponseBase<T>.valueis now
T | undefined. A suspended run (for example, one waiting for tool approval) and a response
created without a structured-output value have always carriedundefinedat runtime; callers
must now narrowresponse.valuebefore reading it. The response factories and update-folding
helpers no longer claim a value exists when none was supplied. - [BREAKING]
@polymind-inc/agent-framework-core— a raw JSON Schema whose root is not an
object no longer types a tool or skill-script input asRecord<string, unknown>; its input is
unknown. Object-root schemas retain the existing record inference. Raw schemas are now
validated locally before executable tools and skill scripts run, rather than relying on a model
provider to enforce them. - [BREAKING]
@polymind-inc/agent-framework-anthropic—thinkingis a discriminated union:
{ type: 'enabled', budgetTokens }requires a safe-integer budget of at least 1024, while
{ type: 'disabled' }cannot carry a budget. Invalid configurations now fail at construction
instead of reaching Anthropic as an invalid request. - [BREAKING]
@polymind-inc/agent-framework-foundry—FoundryTargetnow enforces exactly one
of its two selectors. When an already-configured SDKclientis supplied,baseURLreports the
client's actual URL. - [BREAKING]
@polymind-inc/agent-framework-foundry—FoundryTarget'smodelDeployment
selector is renamedmodel, and the exported guardisModelDeploymentis renamed
isModelTarget. The value still names a model deployment in the project;modelis what both
.NET (themodelparameter onFoundryAgentandAsAIAgent) and Python (themodelkeyword,
FOUNDRY_MODEL) call it, while Go'sModelDeploymentis a per-mode type name in a design
TypeScript's discriminated union does not share. - [BREAKING]
@polymind-inc/agent-framework-core— with sensitive-data capture enabled, the
per-message GenAI telemetry events (gen_ai.system.message,gen_ai.user.message,
gen_ai.assistant.message,gen_ai.tool.message,gen_ai.choice) are emitted on thechat
span only, with the OTel GenAI v1.36.0 structured bodies carried as JSON on thebody
attribute. Previously every exchange was double-reported — the same events also fired on the
invoke_agentspan — and the payload was a role+parts serialization matching neither semconv
generation; the reference implementations emit message events for the model invocation only.
Theinvoke_agentspan keeps attribute-form content (gen_ai.input.messages/
gen_ai.output.messagesare unchanged on both span kinds). Every event carriesgen_ai.system,
a response without a finish reason emits nogen_ai.choice, and event timestamps step one
microsecond apart so ordering survives backends that collapse tight timestamps. Consumers
reading message events offinvoke_agentspans, or parsing the previouscontentattribute
payload, must switch to thechatspan's events and the JSONbodyattribute. - Security: approval requests are immutable snapshots before they reach callers, so mutating a
streamed or awaited request cannot change the arguments later executed after approval. Raw JSON
Schema arguments are checked locally, executable-only approval bypassing no longer consumes
declaration-only calls, and reused function call ids are correlated by logical occurrence. ResponseStream.finalResponse()now shares concurrent source initialization and draining, so
repeated concurrent calls cannot duplicate model/tool work or leak an iterator. Middleware
cancellation before the first pull and update-hook failures now finalize the run consistently.- A streamed run interrupted under
allowBackgroundResponsesno longer double-stores its
exchange. A streaming run that ends suspended skips history persistence entirely — its
continuation token replays the caller's input and every update already produced — so the run
that finally completes appends the whole exchange exactly once, with partial messages merged by
the fold rather than split across store entries. Previously the suspended run and the resumed
run each persisted the input and the partial updates, so the next turn replayed the question
and the partial answer twice, violating the documentedHistoryProvidercontract
(saveMessagesis handed only the messages new to that turn). Awaited suspensions are
unchanged: their tokens carry nothing, so each half stores its own fragment. The fold takes the
continuation token from the latest update, so a background stream that runs to completion
persists normally. - Serializing content now sanitizes
Contentvalues nested in functionresultfields, while
deserialization leaves plain tool-result JSON untouched so it round-trips exactly. Future content types that
carry theuserInputRequestmarker suspend structured-output parsing without requiring a core
release for each new discriminator. - OpenAI stream error events and incomplete terminal errors are surfaced as error content, unknown
message parts survive awaited and streamed session round trips, and the final transformed
storerequest option is the value retained for follow-up requests. - Anthropic streaming drops unknown delta fragments that cannot be replayed as content blocks,
preserves unknown complete blocks, maps citations to framework annotations, and treats MCP
authorization headers case-insensitively. - A2A task conversion now preserves terminal status messages, falls back to the agent history only
for a terminal task that produced no artifacts (never duplicating a message the status already
carries), and de-duplicates artifacts already emitted by the stream. Resuming with a
continuation token needs the token alone; a session passed alongside is updated from the
resumed task. - Agent Server and Foundry hosting now...
v0.3.0
The first minor since 0.2, and the first release after the public-API surface freeze was lifted:
six breaking changes settle the naming conventions and correct semantics against the reference
implementations, alongside Agent Skills, the Foundry Memory provider, the Invocations protocol,
and file-backed persistence for transcripts and skills.
- [BREAKING]
@polymind-inc/agent-framework-openai— theOpenAIChatClientOptions
interface is renamedOpenAIChatClientConfigBase. It is the construction-time half of
OpenAIChatClientConfig, and the package convention names construction types*Configand
per-call types*Options(OpenAIChatOptionsis unchanged); OpenAI was the one provider whose
construction type broke that rule.OpenAIChatClientConfigitself — whatnew OpenAIChatClient(…)
accepts — is unchanged, so only code that names the base interface directly is affected. - [BREAKING] the MCP acronym in public identifiers is now spelled
Mcp, matching the official
@modelcontextprotocolSDK and this framework's own newer API (McpConnection,
mcpSkillsSource(),withMcpClientSpan). The wire format is unaffected. Renames —
@polymind-inc/agent-framework-mcp:MCPClient→McpClient,MCPClientConfig→
McpClientConfig;@polymind-inc/agent-framework-core:MCPToolOptions→McpToolOptions,
SupportsMCPTool→SupportsMcpTool,supportsMCP→supportsMcp, and the capability method
getMCPTool→getMcpTool(the OpenAI, Anthropic and Foundry clients implement the renamed
method). There are no deprecated aliases; update imports and any customChatClientthat
declares the MCP capability. - [BREAKING]
@polymind-inc/agent-framework-agentserver—resolveUnderand
validatePathSegmentare no longer exported from the package's main entry. They are path-safety
primitives for file-backed stores, not part of the server protocol surface (the reference
implementation exposes only the domain-scoped state-root helper, whichstateRootcontinues to
mirror). The framework's own Foundry hosting adapter now reaches them — together with the shared
atomic JSON file helpersreadJsonFile/writeJsonFile, previously duplicated between the two
packages — through the new./internalsubpath, an internal contract like
@polymind-inc/agent-framework-openai/internalwhose exports may change in any release. - [BREAKING]
@polymind-inc/agent-framework-agentserver—HandlerContextgains two required
fields:agentReference(the resolved agent this turn targets, always with a non-empty name)
andagentSessionId(the resolved sandbox session id, as returned onx-agent-session-id).
Handlers only read the context and are unaffected; code that constructsHandlerContext
values — test doubles, custom protocol frontends — must now supply both. - [BREAKING]
@polymind-inc/agent-framework-core— the function-calling loop no longer
hardcodes OpenAI'sconv_prefix when deciding whether a conversation id advances between tool
rounds. Which ids are stable service-side anchors is now the provider's declaration:
ChatClientMetadata.stableConversationId, a new optional predicate the loop and the agent's
session propagation consult. The built-in OpenAI, Azure OpenAI and Foundry clients declare it,
so their behavior is unchanged. A customChatClientimplementation that relied on the loop
pinningconv_…ids must now declare the predicate on itsmetadata— without it, every
conversation id a round reports advances the chain, matching the .NET and Python loops. - [BREAKING]
@polymind-inc/agent-framework-foundry—FoundryChatClientno longer asks for
reasoning.encrypted_contentimplicitly. Not every Foundry deployment supports encrypted
reasoning, and one that does not rejects a request that asks for it — which the client did on
every call without service-side storage, with no way to turn it off. A caller whose deployment
does support encrypted reasoning, and who replays the transcript from their own side, must now
listreasoning.encrypted_contentinoptions.include; without it a reasoning model's
replayed transcript fails for missing reasoning content. Matches the upstream Python fix and the
function-calling loop specification, which make the request an explicit caller opt-in on
Foundry. @polymind-inc/agent-framework-core— new Agent Skills support
(#21):skillsProvider()is a
ContextProviderthat advertises each available skill's name and description in the system
prompt and registers the three tools the model uses to pull one in on demand —load_skill,
read_skill_resourceandrun_skill_script. Skills are declared in code withinlineSkill()
(withskillResource()andskillScript(), the latter taking a Standard Schema it also
validates against), or built from aSKILL.mddocument withmarkdownSkill();
parseSkillMarkdown()exposes the frontmatter parser on its own. Sources come from
inMemorySkillsSource()or any object implementingSkillsSource, and compose through
aggregateSkills(),filterSkills(),deduplicateSkills()andcacheSkills(). Skills passed
to the provider directly are cached and deduplicated for you; a source you supply is used exactly
as given, because caching one that varies per agent or tenant would replay one run's skills for
another. Every skill tool requires approval by default —approvalsrelaxes it per tool, and a
toolApprovalMiddlewarerule over theSKILL_TOOL_NAMESconstant can reimpose it selectively
on a relaxed tool (a middleware'allow'cannot bypass a tool's own approval requirement). A skill or
resource the model asks for and does not exist comes back as a message it can correct rather than
a failed run; a skill a source cannot load is skipped and reported throughonSkillError.
Walking a directory ofSKILL.mdfiles is not part of the core, which has no filesystem;
directorySkillsSourceon the/nodesubpath covers it.@polymind-inc/agent-framework-mcp— newmcpSkillsSource(), also reachable as
McpClient.skillsSource(): discovers the Agent Skills an MCP server publishes by reading the
well-knownskill://index.jsoncatalogue, fetching eachSKILL.mdbody and any document it
refers to only when the model asks.McpConnection.readResource()is exposed for it, with the
same reconnect-once behaviour ascallTooland aresources/readclient span. A server with no
catalogue contributes no skills; an index entry the framework cannot use — anarchiveskill, a
malformed name — is skipped and named, while a server that refuses the request surfaces the
failure rather than being read as an empty catalogue.@polymind-inc/agent-framework-foundry—FoundryToolboxnow serves the toolbox's Agent
Skills as well as its tools:asSkillsProvider()returns a ready-made provider (and
skillsSource()the source alone) over the connection the tools already use, so discovery
carries the same per-call Entra token andx-agent-foundry-call-id, and aCONSENT_REQUIRED
refusal arrives as the same typedToolboxConsentRequiredError. The newloadTools: false
option hides the toolbox's tools entirely — the gateway is never asked to list them — for an
agent that wants only its skills.@polymind-inc/agent-framework-foundry— newFoundryMemoryProvider: aContextProvider
backed by a Microsoft Foundry Memory Store
(#25). It searches the store
before each run — once per session for the user's profile memories, then per turn for memories
relevant to the input — injects what it finds as a single user message under a configurable
context prompt, and sends the completed turn back for extraction afterwards. A failed run is not
stored. Memories are partitioned by a requiredscope, given as a value or as a function of
the run and pinned to the session on first use, so one provider instance can serve every user of
a hosted container;hostedUserScope()(exported from/hosting) resolves it from the platform
user id of the turn. Service failures default tofailureMode: 'continue'— the run proceeds
without the memories — and'throw'fails the run instead;onFailureobserves both. The
provider also carries the store operations a caller needs around it:
ensureMemoryStoreCreated,getMemoryStore,deleteStoredMemoriesandwhenUpdatesCompleted.
The transport isfetchagainst the preview memory-store routes (Foundry-Features: MemoryStores=V1Preview) and is overridable, so tests need no live credentials.@polymind-inc/agent-framework-agentserver— newInvocationsServer: the Foundry
Invocations protocol (#29),
served alongside the existing Responses protocol. The protocol prescribes no payload — the
request body reaches theInvocationHandlerunread, and the handler'sResponseis what the
caller gets — while the server owns the routes (POST /invocations,GET /invocations/{id},
POST /invocations/{id}/cancel,/readiness), invocation and session id resolution
(x-agent-invocation-id;agent_session_idquery →FOUNDRY_AGENT_SESSION_ID→ generated),
their echo on every response including errors, cancellation viaInvocationContext.signal,
opaqueupstream-classified handler failures, SSE keep-alive injection for
text/event-streambodies, and W3C trace propagation with the
azure.ai.agentserver.invocation_id/.session_idbaggage.serve(on/agentserver/node)
now accepts any{ fetch, drain }protocol server — a widening, existing callers are
unaffected.@polymind-inc/agent-framework-foundry— newInvocationsHostServer(on `/hosti...
v0.2.2
Fixes accumulated since 0.2.1, centred on running agents as Microsoft Foundry Hosted Agents:
persistence against the Foundry storage service, latency of streamed model calls, transcript
fidelity, and telemetry attribution.
@polymind-inc/agent-framework-openai- A streamed Responses API call now releases its SSE stream as soon as the terminal event
(response.completed/response.incomplete/response.failed) has been yielded, instead
of draining to the connection close. Azure AI Foundry's/openai/v1/responsesholds the
socket open for ~5 seconds after the terminal event and never sends the[DONE]sentinel, so
every streamed round paid that tail — for a deployed hosted agent, the measured end-to-end
turn dropped from 9.7s to 3.5s. Against an endpoint that closes promptly after the terminal
event this is a no-op. The wrapper is a workaround for the service-side behavior; its removal
is tracked in #40. - A failed response now surfaces its diagnostic: the parsed response and the
response.failed
stream event carry anerrorcontent built fromresponse.error, with generic substitutes
when the wire carries no usable message or code. Previously the reason a run failed was only
available inrawRepresentation. - A local tool's approval no longer reaches the Responses API input. Only hosted (MCP)
approvals serialize tomcp_approval_request/mcp_approval_responseitems; a local
approval is resolved in-process, and serializing it produced an orphaned request
(server_label: null) or a response referencing an id the API never issued. Mirrors the
upstream Python fix.
- A streamed Responses API call now releases its SSE stream as soon as the terminal event
@polymind-inc/agent-framework-coreAgentResponse.text/ChatResponse.textjoin the texts of multiple messages with a
newline, matching the .NET reference (previously concatenated without a separator).gen_ai.system_instructionsis recorded on chat spans only when sensitive-content capture is
enabled, serialized as a parts array ([{"type":"text","content":…}]). It was previously
stamped unconditionally as a bare string, so the system prompt reached the tracing backend
even with capture off — the OpenTelemetry GenAI conventions treat the attribute as opt-in,
and the .NET and Python implementations gate it the same way.
@polymind-inc/agent-framework-foundryFoundryResponseStoreis production-ready and is now the hosted default. Writes carry the
resolvedagent_referenceand forward the platform call id (the two undocumented
requirements behind the service's opaque 500s), replayed history travels as item-id
references instead of re-sent items, conversation ids resolve through the service's own
linkage, ambiguous failures are retried with bounded backoff and reconciled against what the
service actually holds, and background responses replay from a sandbox-local event mirror —
removing the documented 501 limitation on background execution. A hosted container without a
reachable project endpoint falls back to the sandbox filesystem.- The hosting converters cover the Responses v2 item set: twelve more inbound item types —
provider-run searches, code interpreter and image generation among them — now convert to
framework messages instead of being silently dropped from the replayed transcript, and the
output builder emits the matching wire representations so those items survive into the next
turn's history. Semantics cross-checked against the .NET and Python hosted converters.
@polymind-inc/agent-framework-agentserver- An input item that arrives without an id is assigned a platform id under its type's prefix
before persistence, covering the reference id generator's full per-type dispatch. The Foundry
storage service refuses an id-less item with an opaque 500, so a turn whoseinputwas an
item array without ids — the Foundry Playground's request shape among them — ended in
response.failedwithstorage_errorinstead of completing. An item that already carries an
id keeps it, and an id-less item of an unrecognized type is left out of persistence. GET /responses/{id}reportscancelledas soon as a cancel has been accepted, instead of
in_progressfor as long as the cancel waits out its grace period. Only the status is
overridden; the cancelled terminal still clears the output when the winddown ends, matching
the reference server's refresh.- The hosted observability setup instruments outbound
fetch(undici), so model requests and
Foundry storage writes appear as HTTP client spans nested under thechat/invoke_agent
spans — a 7-second span now shows where the time went. - Hosted telemetry is attributed to the deployed agent: a processor stamps the
microsoft.gen_ai.main_agent.*attributes (the onlymicrosoft.*span attributes the Azure
Monitor JS exporter forwards), and the resource carriesfoundry.*attributes mirroring the
.NET host, so the Foundry portal can associate spans with the agent and project. - New public API from the storage work:
stateRootandresolveAgentReferenceare exported,
andFoundryResponseStoreConfigacceptsreplayRootandretry.
- An input item that arrives without an id is assigned a platform id under its type's prefix
- Repository: issue and pull request labels are applied automatically from paths and templates.
v0.2.1
Documentation and positioning only — no change to any published API.
@polymind-inc/agent-frameworkis now the single supported surface. All examples, the
repository README and every code sample import through the main package
(@polymind-inc/agent-framework,…/openai,…/foundry/hosting, …). The
@polymind-inc/agent-framework-*constituent packages remain published — the main package
depends on them at exact versions, and importing one directly still resolves to the same
modules — but their READMEs now state that the main package is the documented way in. This
removes the awkwardness of two equivalent import styles for the same code.
v0.2.0
One new package; no change to any existing published API.
@polymind-inc/agent-framework— new umbrella package, mirroring the Python
agent-frameworkdistribution. It depends on every granular package and re-exports each
under a subpath: the root entry is the core, and/testing,/openai,/anthropic,/mcp,
/a2a,/foundry,/foundry/hosting,/agentserver,/agentserver/nodeand
/agentserver/observabilitymap one-to-one onto the packages they re-export. The granular
packages are unchanged and remain the smaller install.@polymind-inc/agent-framework-agentserver— routing no longer trims the trailing slash of a
request path with a backtracking regular expression. The old pattern cost time quadratic in the
length of a run of slashes, so an unauthenticatedGETon a path such as/////…/acould hold
the event loop for seconds.@polymind-inc/agent-framework-core— generated ids (Agent.id,AgentSession.sessionId,
themessageIdof framework-emitted messages) always come fromcrypto.randomUUID. They
previously fell back toMath.randomon a runtime without it, which is not a suitable source for
an identifier. A runtime that does not providecrypto.randomUUID— a browser on a page served
over plain HTTP — now throws instead of producing a guessable id.
v0.1.1
Metadata only — no code changes, and no change to any published API. If you are already on 0.1.0, there is nothing here that affects your code.
The repository moved to polymind-inc/agent-framework-js. The Agent Framework names a language implementation that lives outside the .NET/Python monorepo with a language suffix on its own repository — compare microsoft/agent-framework-go — so the previous name both read as a fork of microsoft/agent-framework and pointed away from where this would land if it were adopted upstream.
The repository, homepage and bugs fields of all seven packages now point at the new location. 0.1.0 shipped with the previous one, which only resolves through a GitHub redirect.
Install
npm install @polymind-inc/agent-framework-core @polymind-inc/agent-framework-openaiPackages
All seven are versioned and released in lockstep, and published with npm provenance:
| Package | Purpose |
|---|---|
@polymind-inc/agent-framework-core |
Agent, AgentSession, the Message/Content model, tool(), middleware, the ChatClient seam |
@polymind-inc/agent-framework-openai |
OpenAI and Azure OpenAI (Responses API) |
@polymind-inc/agent-framework-anthropic |
Anthropic (Messages API) |
@polymind-inc/agent-framework-mcp |
Model Context Protocol client |
@polymind-inc/agent-framework-a2a |
Agent2Agent (A2A) protocol client |
@polymind-inc/agent-framework-foundry |
Microsoft Foundry chat client and Hosted Agent hosting |
@polymind-inc/agent-framework-agentserver |
Foundry Responses container protocol v2.0.0 server |
Requires Node.js 24 or newer. ESM only.
Workflows are designed but not implemented — see the README for the full list of known limitations, and CHANGELOG.md for the history.
This is an independent community implementation. It is not an official Microsoft product, and it is not affiliated with or endorsed by Microsoft.