Repository navigation
feat(skills)!: promote Agent Skills to core - #210
jeffdupont wants to merge 7 commits into
Conversation
Every Agent Skills name is now exported from the launchdarkly_ai_server package root (and so from launchdarkly-ai-python), with the same spelling. launchdarkly_ai_server.experimental.skills is removed; import from launchdarkly_ai_server instead. Nothing else changes. - Root __init__ imports and __all__ gain the 24 skills names. - API reports regenerated; the experimental.skills report is gone. - release-please changelog-sections drop the skills Experimental rows. - READMEs, agents.md, AGENTS.md, core docstrings and error strings no longer point at the experimental module. - Tests import skills names from the root; new tests check the root exports them and the experimental module no longer imports. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A put or delete with no usable key stays warned and ignored, and delivery keeps going. Drop the "experimental stage" and "revisit before 1.0" wording from agents.md and the pinning test's docstring. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Pushed 4825587, which merges origin/main into this branch with no conflicts. This needs a human reviewer. |
XieX
left a comment
There was a problem hiding this comment.
I reviewed the promotion, and the code move itself looks clean: no references to experimental.skills remain, and the root __all__ matches the old module's 24 names. My inline comments are mostly about the docs and packaging around it.
One thing outside the diff: the title's scope is AIC-3655. release-please shows the scope in bold in the changelog entry, so the Jira key would appear in the customer-facing changelog. feat(skills)!: promote Agent Skills to core would land under Features in the same way.
all_skills, get_skill, get_skill_result, get_skills, skill_refs, watch_skills and write_skills each report their client.* usage ID as their first statement (TESTING.md §3.27). The watcher's initial and follow-up reconciles call the non-reporting _write_skills. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y/python-ai-sdk into fix-210-usage
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f5ca75f. Configure here.
…server 1.0.0 launchdarkly-ai-python doesn't pin launchdarkly-ai-server, so upgrading the barrel alone can leave an older server installed, and importing a skills name then raises ImportError. The promotion removed the README warning along with the experimental bullet. This restores it for the root imports. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- agents.md: a rename of ld.skills.integrity_failure needs a ! commit, which is a minor bump on 0.x and a major after 1.0, not always a major. - Test that lifecycle.py imports no skills module, so the claim in agents.md is checked now that test_lifecycle no longer asserts it through launchdarkly_ai_server.skills. - test_skills' SKILLS_SURFACE docstring points at the root snapshot test that actually catches an addition. - test_skills_fdv2 imports the internal skills_core and skills_fdv2 apart from the public names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
XieX
left a comment
There was a problem hiding this comment.
Approving. All eight comments from the first round are settled: six are fixed, and the shim and lazy-import decisions are backed by §0.5 and AIC-3674. Thanks for the spec links on the keyless-object rule; ai-sdks-monorepo#86 §3.25 reads the way you described.
Before merging, could you also deal with the follow-ups below?
Docs vs. the new usage reporting (4d6b3cb). The code is right per §3.27, but three places still say skills never calls client.track() or reports telemetry. Details are in the inline comments.
PR description. It still says "Usage IDs. None… this PR adds none." It should now list the seven client.* IDs: skillRefs, getSkill, getSkillResult, getSkills, allSkills, writeSkills and watchSkills.
Changelog. The squash commit only carries the promotion title. Since skills calls now send a usage event, a BEGIN_COMMIT_OVERRIDE with a second line such as feat(skills): report helper usage for the Agent Skills functions would give that its own changelog entry.
Merge timing. This approval doesn't change the "Do not merge yet" note. Please hold until the AIC-3655 blockers are cleared, and make sure this ships as the 1.0 release (AIC-3672). The barrel README now says skills need launchdarkly-ai-server 1.0.0, but the open release PR (#206) would cut 0.5.0. If this lands in a 0.5.0 release, that README note and its pip install "launchdarkly-ai-server>=1.0.0" command would point at a version that doesn't exist.
| objects with a valid key and an integer version >= 1. This | ||
| includes ``skills: null``. | ||
| """ | ||
| report_usage("client.skillRefs") |
There was a problem hiding this comment.
The docstring just above still says "Pure: no network, store, or telemetry", but this call now reports client.skillRefs, and once a client exists that goes out through client.track(). Maybe: "Pure apart from the one-time helper usage event ($ld:ai:sdk:usage): no network, store, or skills telemetry." agents.md line 210 says the same thing ("no client, no telemetry") and needs the same change.
| @@ -984,6 +984,6 @@ a conversation is out of reach at this layer either way. | |||
| - `Skill.content` is opaque `bytes`. Do not add anything that parses or interprets it — no YAML library in this package's dependencies at any tier, and no accessor that decodes content. | |||
| - Do not route skills telemetry through `client.track()`, and do not introduce an LD context anywhere in the skills path. Signals go through the `skills_core.py` emitter seam, whose default is a no-op, and only via its `record_*` functions. | |||
There was a problem hiding this comment.
This rule, and the Telemetry seam section at lines 586–590 ("Do not use client.track()… No LD context is involved anywhere in this feature"), now contradict the report_usage calls added in 4d6b3cb, which send $ld:ai:sdk:usage through client.track() with SDK_INFO_CONTEXT. The code follows §3.27, so I think the docs should exempt it explicitly. For example: the rule covers the three skills signals; the SDK-wide helper usage event (§3.27, once per helper per client) is the one client.track() path, and it carries no skill data. Otherwise a future contributor following this line could remove the usage reporting as a violation.
| @@ -363,7 +366,7 @@ def test_requires_no_client_or_store(self, mock_ld_client: Any) -> None: | |||
| mock_ld_client.track.assert_not_called() | |||
There was a problem hiding this comment.
Nit: the docstring still calls this a "pure projection". track.assert_not_called() passes only because no client is initialized, since skill_refs now holds a usage event for the next init. Could we reword it so it doesn't read as a no-telemetry check? For example: "No store configured, no client initialized: skill_refs still projects; the usage event is held until init (see test_sdk_usage.py)."

Promotes Agent Skills from
launchdarkly_ai_server.experimental.skillsto the package root. Jira: AIC-3655.Migration
Import Agent Skills names from
launchdarkly_ai_serverinstead oflaunchdarkly_ai_server.experimental.skills. Nothing else changes: the names, signatures and behaviour are the same.This is a breaking change (
!), which bumps the minor version while the package is 0.x.What changed
experimental/skills.pyexported are now inlaunchdarkly_ai_server.__all__, same spelling.launchdarkly-ai-pythonre-exports root__all__withimport *, so it gets them with no code change; its existing re-export tests cover it.experimental/skills.pyis deleted.experimental/__init__.pyis only a docstring, so nothing else there referenced it. Evaluations from Code stays inexperimental.evaluations, untouched.make api-report: the skills names appear inlaunchdarkly_ai_server.txt, andlaunchdarkly_ai_server.experimental.skills.txtis deleted.skills→ Experimental rows are removed fromchangelog-sectionsinrelease-please-config.json, sofeat(skills):now lands under Features.launchdarkly-ai-pythonREADME,packages/client/agents.mdandAGENTS.mdno longer call Agent Skills experimental. Code samples import from the root.skills_core.py,skills_watch.py,types.py,types_validation.py,shutdown_hooks.pyandlifecycle.pyno longer name the experimental module. For example, the "no skill store" error now saysset_skill_store(store) from launchdarkly_ai_server.mainreports no skills usage IDs today, so this PR adds none. When skills usage reporting is added, it must useclient.*IDs, notexperimental.*.parse_ai_configstill passesskillsthrough unvalidated, andskill_refsstill validates it.shutdown()still clears the skill store through the internal shutdown hook. The lifecycle module still imports no skills module.Tests
test_skills.py: every skills name is in root__all__and on the root module.launchdarkly_ai_server.experimental.skillsraisesImportError, andlaunchdarkly_ai_server.experimentalhas none of the names.test_public_surface.py: the skills names are added to theROOT_SURFACEsnapshot.test_lifecycle.py: the "core loads no experimental module" check now checksevaluationsandexperimentalonly, since the root now imports the skills modules.conftest.pyimport from the root.Test plan
Run in the PR worktree after
uv sync(exit 0):make lint— exit 0make format-check— exit 0make typecheck— exit 0 (Success: no issues found in 101 source files)make api-report-check— exit 0 (14 API reports are current)uv run pytest -q packages/client packages/ai— 1709 passed, 1 skippedmake test— exit 2: 3282 passed, 18 skipped, 3 failed. The same 3 tests fail on unmodifiedorigin/main(e3afb3f) in the same checkout, and CI onmainis green. None of them involves skills:test_lifecycle.py::TestInitClientSDKKeyPath::test_uses_options_sdk_key_over_env_varand::test_retries_after_a_failed_init_instead_of_replaying_it: they get a stagingstream_uri. My unverified guess is a local.envabove the checkout leaking into the full run. Both pass when run alone.opencode-agents/tests/test_handler.py::TestProcess::test_a_fake_opencode_is_started_spoken_to_and_stopped: on macOS the temp path comes back as/var/...in one place and/private/var/...in another.🤖 Generated with Claude Code
Related PRs
Releases together with:
Note
Overview
Promotes Agent Skills from experimental to the stable package root, so customers import
get_skill,write_skills,FDv2SkillStore, and the rest fromlaunchdarkly_ai_serverinstead oflaunchdarkly_ai_server.experimental.skills. The experimental re-export module and its API report are removed; the same symbols are added to root__all__and the generatedlaunchdarkly_ai_serverAPI report.Release and docs treat skills as core:
release-pleaseno longer routesfeat(skills):to Experimental (onlyevaluationsstays experimental), and README/agents guidance pluslaunchdarkly-ai-pythondocs describe root imports and thelaunchdarkly-ai-server>=1.0.0pairing.Implementation follow-ups include
client.*usage reporting on the public skills entry points, an internal_write_skillssowatch_skillsreconciles without double-countingwriteSkills, and updated lifecycle/public-surface tests (including thatexperimental.skillsis gone while evaluations remains experimental).Reviewed by Cursor Bugbot for commit 75dc0f4. Bugbot is set up for automated code reviews on this repo. Configure here.