Skip to content

[dotnet-port-api] Export reusable skills source types - #1046

Open
Michelle Clayton (michelle-clayton-work) wants to merge 4 commits into
mainfrom
dotnet-port-api-skills-source-types-20260708-alt-26d2f7d2e0ec70cc
Open

[dotnet-port-api] Export reusable skills source types#1046
Michelle Clayton (michelle-clayton-work) wants to merge 4 commits into
mainfrom
dotnet-port-api-skills-source-types-20260708-alt-26d2f7d2e0ec70cc

Conversation

@michelle-clayton-work

Copy link
Copy Markdown
Contributor

Summary

Port the public skills-source composition surface from microsoft/agent-framework#6838 by exposing reusable exported Go source types in agent/skills.

This change adds exported in-memory, aggregating, delegating, filtering, deduplicating, and caching source types, reuses the new in-memory source type from the existing provider path, adds focused tests for the new source surface, and updates the comparison doc to note the reusable source composition support now available in Go.

Upstream commit: microsoft/agent-framework@00e4d4ffde1fef985095de97cfb5e90b8f7dd462

Ported .NET PRs

Breaking Changes

No.

Tests and Examples

  • Ran go test ./agent/skills/....
  • Added focused tests for exported skills source composition and caching behavior.
  • No examples changed; this port adds reusable public API surface rather than a new sample scenario.

Notes

Generated by .NET to Go API Porting Agent · 970 AIC · ⌖ 35.4 AIC · ⊞ 21.7K ·

Closes #450

Port .NET skills source public surface by exposing reusable in-memory, aggregating, delegating, filtering, deduplicating, and caching source types in Go.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings September 9, 2026 20:25
@github-actions github-actions Bot added area:agent Changes files in the agent area size:xlarge More than 300 changed lines or 10 files labels Sep 9, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new reusable sources currently expose/mutate shared slices (and can panic on nil skills), which can cause incorrect behavior across callers and needs fixes before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR ports and exposes a reusable skills-source composition API in agent/skills, enabling consumers to build skill sources by composing in-memory, aggregating, delegating, filtering, deduplicating, and caching behaviors (aligned with the .NET public surface).

Changes:

  • Introduces exported composable Source implementations in agent/skills/sources.go (aggregating/delegating/filtering/deduplicating/caching, plus exported in-memory concrete type).
  • Adds focused unit tests covering the new composable sources and caching concurrency behavior.
  • Updates the .NET vs Go feature comparison doc to reflect the newly available reusable source composition surface.
File summaries
File Description
agent/skills/provider.go Removes internal in-memory/dedup helpers now provided by the reusable sources surface.
agent/skills/sources.go Adds exported, reusable composable skills Source implementations (aggregation, delegation, filtering, deduplication, caching).
agent/skills/sources_test.go Adds tests validating ordering, delegation, filtering, deduplication, and caching concurrency behavior.
docs/dotnet-go-sdk-feature-comparison.md Notes reusable skills source composition support in Go.
Review details

Suppressed comments (2)

agent/skills/sources.go:229

  • deduplicateSkillsByName compacts the input slice in place via skills[:0]. When used by DeduplicatingSource, this can mutate the slice returned by the inner source (including overwriting elements in the inner source’s backing array), producing surprising behavior across calls. Also, nil skills from an inner source would panic when accessing skill.Frontmatter.Name. Allocate a new output slice and defensively skip nil skills.
func deduplicateSkillsByName(skills []*Skill, logger *slog.Logger) []*Skill {
	seen := make(map[string]struct{}, len(skills))
	deduplicated := skills[:0]
	for _, skill := range skills {
		resolvedKey := strings.ToLower(skill.Frontmatter.Name)

agent/skills/sources.go:207

  • CachingSource currently stores and serves the exact slice instance returned by the inner source (and returns the same slice to callers), so any caller mutation can corrupt the cached value for all future callers. Also, storing time.Now().UTC() strips the monotonic clock reading, making refresh comparisons vulnerable to wall-clock adjustments. Cache a shallow copy, keep monotonic time by using time.Now(), and return a copy from cachedSkillsLocked.
	s.mu.Lock()
	if err == nil {
		s.cached = loaded
		s.lastLoaded = time.Now().UTC()
	}
  • Files reviewed: 4/4 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread agent/skills/sources.go
Comment thread agent/skills/sources.go
@github-actions github-actions Bot added kind:code Changes production behavior or code kind:docs Changes documentation or comments kind:tests Changes tests, fixtures, or test infrastructure pending-auto-risk Automatic risk classification is in progress labels Sep 9, 2026
@michelle-clayton-work

Copy link
Copy Markdown
Contributor Author

Copilot fix merge conflicts and address PR feedback

@github-actions github-actions Bot added risk:medium Contained production impact requiring normal review depth and removed pending-auto-risk Automatic risk classification is in progress labels Sep 9, 2026
Copilot AI and others added 2 commits September 9, 2026 20:40
…s-source-types-20260708-alt-26d2f7d2e0ec70cc

# Conflicts:
#	agent/skills/provider.go

Co-authored-by: michelle-clayton-work <262183035+michelle-clayton-work@users.noreply.github.com>
Co-authored-by: michelle-clayton-work <262183035+michelle-clayton-work@users.noreply.github.com>

Copilot AI commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Copilot fix merge conflicts and address PR feedback

Merged origin/main into the branch (conflict was in agent/skills/provider.go, resolved by keeping the in-memory source in sources.go; also renamed a duplicated test helper) and addressed the review feedback in commit 7e42e2c:

  • InMemorySource.Skills and the CachingSource cached result now return defensive copies, and the cache stores its own copy.
  • deduplicateSkillsByName builds a new slice instead of mutating the caller's backing array.
  • FilteringSource and the deduplication helper skip nil skills instead of panicking.
  • Added tests for copy semantics and nil-skill handling; go test ./agent/skills/... passes.

@github-actions github-actions Bot added pending-auto-risk Automatic risk classification is in progress risk:medium Contained production impact requiring normal review depth and removed risk:medium Contained production impact requiring normal review depth pending-auto-risk Automatic risk classification is in progress labels Sep 9, 2026
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Scope: public API

Changed Go contract: New exported types/constructors in agent/skills: InMemorySource, AggregatingSource/NewAggregatingSource, DelegatingSource/NewDelegatingSource, FilteringSource/NewFilteringSource, DeduplicatingSource/NewDeduplicatingSource, CachingSource/NewCachingSource. NewInMemorySource (previously returning an unexported *skillSliceSource) now returns the newly-exported *InMemorySource concrete type. No breaking signature changes; existing provider.go behavior (dedup-by-name, skill validation) is preserved by delegating to the same helper logic.

Upstream evidence reviewed:

  • microsoft/agent-framework@00e4d4ffde1fef985095de97cfb5e90b8f7dd462 ("Make skills source classes public and sealed with Experimental attribute", #6838) — the linked port target, which made AgentInMemorySkillsSource, AggregatingAgentSkillsSource, DelegatingAgentSkillsSource, FilteringAgentSkillsSource, DeduplicatingAgentSkillsSource, CachingAgentSkillsSource public (some with [Experimental]).
  • dotnet/src/Microsoft.Agents.AI/Skills/Decorators/CachingAgentSkillsSourceOptions.cs (added in #6827, still present at HEAD) — exposes RefreshInterval (nullable TimeSpan) and CacheIsolationKeySelector via a CachingAgentSkillsSource(AgentSkillsSource, CachingAgentSkillsSourceOptions? options = null) constructor.
  • dotnet/src/Microsoft.Agents.AI/Skills/Decorators/FilteringAgentSkillsSource.cs, DeduplicatingAgentSkillsSource.cs — predicate/logger constructor shapes match the Go NewFilteringSource/NewDeduplicatingSource closely (modulo the separately-tracked AgentSkillsSourceContext parameter from #6797, already out of scope per the PR notes and tracked in [dotnet-port-api] Port skills source context from .NET #440).
  • microsoft/agent-framework@c1e20632f ("Remove Experimental attribute from Skills API", #6861) confirms the upstream repo's current HEAD (fb4be3bb1f..., the head inspected per the PR description) has no [Experimental] gating left on any of these source classes, so the Go port correctly does not need to add opt-in/experimental gating for this surface.

Result: findings reported (one inline comment). The new source types (InMemorySource, AggregatingSource, DelegatingSource, FilteringSource, DeduplicatingSource) are aligned with their .NET counterparts in naming, constructor shape, and behavior. CachingSource, however, ports the type shape but not its configuration surface: NewCachingSource accepts no options, so the internal refreshAfter field is permanently zero and the cache-expiry branch in cachedSkillsLocked is unreachable dead code — unlike upstream's CachingAgentSkillsSourceOptions.RefreshInterval/CacheIsolationKeySelector, which are real, exercised configuration knobs. See the inline comment for the specific gap and a suggested resolution (add an options parameter or drop the unused field).

Generated by Go API Consistency Review Agent for #1046 · copilot · auto · 212 AIC · ⌖ 6.62 AIC · ⊞ 9.6K ·

@github-actions github-actions Bot added the public-api-change Pull Request changes public APIs label Sep 9, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generated by Go API Consistency Review Agent for #1046 · copilot · auto · 212 AIC · ⌖ 6.62 AIC · ⊞ 9.6K

Comment thread agent/skills/sources.go
return &CachingSource{
DelegatingSource: NewDelegatingSource(inner),
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upstream evidence: microsoft/agent-framework dotnet/src/Microsoft.Agents.AI/Skills/Decorators/CachingAgentSkillsSourceOptions.cs (commit b7fc23c61, present at the ported commit 00e4d4ffd and still in HEAD) exposes CachingAgentSkillsSourceOptions.RefreshInterval (nullable TimeSpan, default null = never expire) and CacheIsolationKeySelector, both settable via the CachingAgentSkillsSource(AgentSkillsSource, CachingAgentSkillsSourceOptions? options = null) constructor.

Go gap: CachingSource (agent/skills/sources.go) has an internal refreshAfter time.Duration field that gates cache expiry in cachedSkillsLocked, but NewCachingSource(inner Source) *CachingSource takes no options and never sets refreshAfter, so it is permanently zero and this code path is dead. There is no exported way for Go callers to configure cache refresh/expiry or cache-key isolation, unlike the .NET decorator this ports. Either expose an options parameter (e.g. NewCachingSource(inner Source, opts CachingSourceOptions) with a RefreshAfter time.Duration field mirroring RefreshInterval) or remove the unreachable refreshAfter plumbing until it is wired up.

@michelle-clayton-work

Copy link
Copy Markdown
Contributor Author

Copilot resolve merge conflicts and address PR feedback

…s-source-types-20260708-alt-26d2f7d2e0ec70cc

# Conflicts:
#	docs/dotnet-go-sdk-feature-comparison.md

Co-authored-by: michelle-clayton-work <262183035+michelle-clayton-work@users.noreply.github.com>

Copilot AI commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Copilot resolve merge conflicts and address PR feedback

Merged the latest origin/main in 1e9ff9f. The only conflict this time was in docs/dotnet-go-sdk-feature-comparison.md, resolved by keeping main's updated "MCP client tools" and "OpenAI provider" rows plus this PR's "Skills" row.

The earlier review feedback is already addressed and still intact after the merge: defensive slice copies in InMemorySource.Skills/CachingSource, non-mutating deduplication, and nil-skill skipping in the filtering/deduplicating sources. go test ./agent/skills/... passes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:agent Changes files in the agent area kind:code Changes production behavior or code kind:docs Changes documentation or comments kind:tests Changes tests, fixtures, or test infrastructure public-api-change Pull Request changes public APIs risk:medium Contained production impact requiring normal review depth size:xlarge More than 300 changed lines or 10 files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[dotnet-port-api] Export reusable skills source types

3 participants