Repository navigation
v0.3.0
·
53 commits
to master
since this release
Immutable
release. Only release title and notes can be modified.
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/hosting):
publishes anAgentover the Invocations protocol with the same wire contract as the Python
adapter — request{ "message": string, "stream"?: boolean }, plain-text responses, raw text
chunks undertext/event-streamwhen streaming. Conversations are held in process, partitioned
per session id and — hosted — per platform user; callers continue a conversation by pinning the
agent_session_idquery parameter to the previous response'sx-agent-session-idheader.
Hosted requests without the protocol-2.0.0 call id fail closed with 501, without a user id with
400, as the Responses adapter does.@polymind-inc/agent-framework-foundry— the hosting layer now exposes the turn's typed
execution context to code running inside the agent
(#23).getHostedAgentContext()
(exported from/hosting) returns a frozen per-turnHostedAgentContext— the platform user id
and call id, the correlation id, the response and conversation ids, the resolved agent
reference, the sandbox session id, and the turn'sAbortSignal— from any tool, context
provider or middleware, andundefinedoutside a hosted turn. Concurrent turns on one container
each see their own context; the stores that persist per-user state keep taking their partition
key as an explicit argument. There is deliberately no per-request tenant field: the platform's
trust boundary injects only the user id and call id, and a hosted container is deployed per
agent inside one tenant.@polymind-inc/agent-framework-openai— newincludeReasoningEncryptedContentconstruction
option (defaulttrue, so OpenAI and Azure OpenAI are unchanged). Setting it tofalse
suppresses the implicitreasoning.encrypted_contentrequest for an endpoint that rejects it;
an entry the caller lists inincludethemselves is always sent either way. Mirrors the .NET
clients' option of the same name.@polymind-inc/agent-framework-core— a history provider can opt into storing the
messages other context providers injected into a run:HistoryStoreOptions.storeContextMessages
takestruefor all of them, or a list ofsourceIds for just those providers. The default
stays as before — the run's input and the response — matching the reference implementations'
default; the opt-in is the knob they offer that was missing here.@polymind-inc/agent-framework-core— newFileHistoryProvideron the/nodesubpath:
keeps the transcript in a JSON Lines file per session, using the same layout as the Python
file-backed provider so the files interoperate, and a session survives a process restart without
a custom provider.@polymind-inc/agent-framework-core— newdirectorySkillsSourceon the/node
subpath: walks a directory ofSKILL.mdskill folders — the convention all three reference
implementations support — and serves them as aSkillsSourceforskillsProvider().@polymind-inc/agent-framework-mcp—McpClientaccepts a header provider
(McpHeaderProvider, sync or async) in place of a staticheadersrecord. It is re-evaluated
per request, so a credential that refreshes can be expressed without a customfetchwrapper,
and the headers are scoped to the server's origin so a redirect elsewhere cannot carry them
along.@polymind-inc/agent-framework-core— theENABLE_SENSITIVE_DATAcompatibility alias
accepts the same spellings as the Python reader:onnow enables message-content capture —
previously it silently read as unset, the exact cross-language case the alias exists for — and
offis an explicit negative rather than falling through to the OTel-named variables.@polymind-inc/agent-framework-openai— the Responses wire mappings the Foundry hosting
adapter replays (themcp_call/ search / code-interpreter items, MCP output stringification,
and the encrypted-reasoning lookup) are shared through an undocumented./internalentry
instead of hand-maintained mirrors, so the replay path and the live provider path cannot drift;
the call-id fallback order now matches the Python reference on both.@polymind-inc/agent-framework-core— what happens when a caller stops consuming a run
early (break, an aborted signal) and how token usage is read after draining a stream are now
documented contracts onAgentRunStream/ResponseStream, each pinned by tests.- Repository: the Baseline v0.1 public-API surface freeze is lifted — surface changes now require
grounding in the reference implementations, and the gate on new feature areas stays. Large
internal modules were split into focused ones and duplicated internal logic consolidated, with
reduced streaming hot-path overhead and no behavior change; CI actions and dependencies were
bumped.