Skip to content

feat(AIC-3655)!: promote Agent Skills to core - #14

Draft
jeffdupont wants to merge 1 commit into
mainfrom
jdupont/aic-3655-skills-core
Draft

jeffdupont wants to merge 1 commit into
mainfrom
jdupont/aic-3655-skills-core

Conversation

@jeffdupont

@jeffdupont jeffdupont commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Jira: AIC-3655 (Go coverage: AIC-3656)

What changed

  • Agent Skills are core. Every Agent Skills name that aiserver/experimental exported is now exported by aiserver with the same name and signature (Skill, SkillStore, SetSkillStore, SkillRefs, GetSkill*, AllSkills, WriteSkills, WriteAllSkills, WatchSkills, WatchAllSkills, FDv2SkillStore, the Reason* / Action* / OnUnavailable* / FDv2Mode* constants, the Err* sentinels, …). Nothing is renamed.
  • Breaking (0.x): the skills names are removed from aiserver/experimental. That package keeps Evaluations from Code.
  • Import cycle removed. internal/skills/accessors.go imported aiserver, so aiserver could not export skills. The implementation now lives in package aiserver (git mv internal/skills/*.go aiserver/skills_*.go, so history follows). Unexported helpers stay unexported. Three names that were exported only inside the internal package are now unexported: ClearState → clearSkillState, SetEmitterForTesting → setSkillEmitterForTesting, SetLogger → setSkillLogger. A duplicate skillKeyRE in parse.go was dropped; it had the same pattern as the skills one.
  • Shutdown is unchanged. The skills state is still cleared by the internal/bridge OnShutdown hook, registered at package init, in the same order as before. Core does not import aiserver/experimental.
  • Error and validation behaviour is unchanged. The only text change is that the "no skill store" error now names aiserver.SetSkillStore / aiserver.NewFDv2SkillStore / aiserver.NewInMemorySkillStore.
  • Usage IDs: main reports no skills usage IDs (skills never track, and a test asserts it), so none were added. AIC-3555's SDK work must use client.*.
  • Tests moved with the code. The internal skills tests are in aiserver/skills_*_test.go. The public-surface skills tests from experimental/skills_test.go are in aiserver/skills_public_test.go (package aiserver_test). rootSurface in aiserver/surface_test.go now lists the skills names, and TestNoExperimentalNameOnRoot also checks that skills names are on the root and that the wire constants stay unexported. The experimental surface test pins evaluations only, so a skills name left in experimental would fail it.
  • Docs: the Agent Skills sections move from aiserver/experimental/README.md and agents.md to aiserver/README.md and aiserver/agents.md. The root README, AGENTS.md, experimental/doc.go and the aiserver package doc no longer call skills experimental.

Migration

For Agent Skills, import github.com/launchdarkly/go-ai-sdk/aiserver instead of github.com/launchdarkly/go-ai-sdk/aiserver/experimental, then change experimental.X to aiserver.X. Nothing else changes.

Release

This releases together with the JS and Python promotion PRs (links added later). It is not on the 30 Oct critical path.

Test plan

  • make format-check: exit 0
  • make vet (go vet ./...): exit 0
  • make build (go build ./...): exit 0
  • go test ./...: exit 0
  • go test -race -shuffle=on ./... (CI's command): exit 0, 15 packages ok
  • GOOS=windows go vet ./aiserver/...: exit 0 (non-POSIX skills_materialize_other.go still compiles)

🤖 Generated with Claude Code

Related PRs

Releases together with:

Agent Skills move from aiserver/experimental to package aiserver.
The implementation moves from internal/skills into aiserver
(git mv, skills_*.go), which removes the import cycle that kept
aiserver from exporting skills. Helpers that were exported only
within the internal package (ClearState, SetEmitterForTesting,
SetLogger) are unexported. Shutdown still clears skills state through
the internal/bridge hook.

aiserver/experimental no longer exports any skills name; it keeps
Evaluations from Code. Import github.com/launchdarkly/go-ai-sdk/aiserver
instead of aiserver/experimental for skills; nothing else changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jeffdupont added a commit to launchdarkly/js-ai-sdk that referenced this pull request Oct 26, 2026
Jira: [AIC-3655](https://launchdarkly.atlassian.net/browse/AIC-3655)

> **Do not merge** until the blockers linked on AIC-3655 are fixed. This
PR releases together with the Python and Go promotion PRs (links added
later).

## What changed

- **Agent Skills is core.** All 35 Agent Skills names (functions,
stores, watcher, constants, factories and types) are exported from the
`@launchdarkly/ai-server` root with the same spelling, and are
**removed** from `@launchdarkly/ai-server/experimental`. This is
breaking under the 0.x rule, so it bumps the minor version.
- `@launchdarkly/ai-node` gets the names from its root through its
existing `export *`. Its `/experimental` entry keeps working and still
re-exports Evaluations from Code.
- Evaluations from Code stays experimental. The `/experimental` entry
point stays.
- The API reports (`etc/ai-server.api.md`,
`etc/ai-server-experimental.api.md`) are regenerated. The skills names
are added to `api-surface/ai-server.txt` as `+` (joining) and marked `-`
(leaving) in `api-surface/ai-server.experimental.txt`.
- The `skills` rows are removed from `changelog-sections` in
`release-please-config.json`. AGENTS.md now says `skills` is a core
scope.
- The shutdown hook is renamed from `experimental Agent Skills` to
`Agent Skills`. The no-store error message now points to
`@launchdarkly/ai-server`. Core doc comments no longer call Agent Skills
experimental.
- Tests: the root export snapshot now includes the skills values, the
experimental entry exports only the Evaluations values, a new test
checks that the API reports list every skills name at the root and none
in the experimental report, and the lifecycle test now expects the
`Agent Skills` hook after loading the root and no experimental hook.
- The integration fixtures import skills from the root and check that
`getSkill` is on the root and not on either `/experimental` subpath.
- The READMEs and both agents.md files are updated, including the
promotion note.
- **Usage IDs:** `main` reports no `experimental.*` skills usage IDs, so
this PR adds none. When the skills usage-reporting work (AIC-3555) lands
in this SDK, it must use `client.*` IDs.

## Migration

Change the import path from `'@launchdarkly/ai-server/experimental'` to
`'@launchdarkly/ai-server'` (or from
`'@launchdarkly/ai-node/experimental'` to `'@launchdarkly/ai-node'`).
Nothing else changes: names, signatures and behavior are the same.

## Test plan

Run in the worktree after `yarn install`:

- [x] `yarn code:check`: exit 0
- [x] `yarn build`: exit 0
- [x] `yarn typecheck`: exit 0
- [x] `yarn test`: exit 0 (client: 1826 passed, 11 skipped)
- [x] `yarn test:integration`: exit 0 (36 passed)
- [x] `yarn api:surface`: exit 0
- [x] `yarn test:api-surface`, `yarn test:api-breaking`, `yarn
lint:pkg`: exit 0
- [x] `yarn workspace @launchdarkly/ai-server api:check`: exit 0
- [ ] `yarn api:check`: exit 1, only because
`packages/claude-agents/etc/ai-claude-agents.api.md` is already out of
date on `main` (`ClaudeAgentsHostOptions` came in after the report was
last generated). This PR doesn't touch that package.

🤖 Generated with [Claude Code](https://claude.com/claude-code)


[AIC-3655]:
https://launchdarkly.atlassian.net/browse/AIC-3655?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

## Related PRs

Releases together with:
- launchdarkly/python-ai-sdk#210
- launchdarkly/go-ai-sdk#14

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Overview**
> **Agent Skills moves from `@launchdarkly/ai-server/experimental` to
the package root** (and the same via `@launchdarkly/ai-node`). All
skills APIs—stores, accessors, `writeSkills`/`watchSkills`, types—are
removed from the experimental entry point; **Evaluations from Code**
remains on `/experimental` only. This is a **breaking** 0.x change:
imports must switch from `.../experimental` to the root; names and
behavior are unchanged.
> 
> API Extractor reports and `api-surface/*.txt` are updated accordingly;
`skills` is dropped from release-please **Experimental** changelog
sections and AGENTS.md now treats `skills` as a core scope. Docs call
out that loading the **root** now pulls in Node built-ins (`node:fs`,
`node:path`, etc.) for skills, which can break non-Node edge runtimes
(tracked as AIC-3674).
> 
> Implementation follow-ups: shutdown hook renamed to `Agent Skills`;
skills helpers emit **`client.*` `$ld:ai:sdk:usage` IDs**; `watchSkills`
uses `writeSkillsInternal` so only the watcher reports usage. Tests,
webpack integration fixtures, and re-export assertions are aligned with
the new surface.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
ecef311. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
jeffdupont added a commit to launchdarkly/python-ai-sdk that referenced this pull request Oct 26, 2026
Promotes Agent Skills from `launchdarkly_ai_server.experimental.skills`
to the package root. Jira:
[AIC-3655](https://launchdarkly.atlassian.net/browse/AIC-3655).

> **Do not merge yet.** This PR must not merge until the blockers linked
on AIC-3655 are fixed. It releases together with the JS and Go promotion
PRs (links to be added).

## Migration

Import Agent Skills names from `launchdarkly_ai_server` instead of
`launchdarkly_ai_server.experimental.skills`. Nothing else changes: the
names, signatures and behaviour are the same.

```python
# before
from launchdarkly_ai_server.experimental.skills import get_skill, write_skills
# after
from launchdarkly_ai_server import get_skill, write_skills
```

This is a breaking change (`!`), which bumps the minor version while the
package is 0.x.

## What changed

- **Root exports.** All 24 names that `experimental/skills.py` exported
are now in `launchdarkly_ai_server.__all__`, same spelling.
`launchdarkly-ai-python` re-exports root `__all__` with `import *`, so
it gets them with no code change; its existing re-export tests cover it.
- **Experimental module removed.** `experimental/skills.py` is deleted.
`experimental/__init__.py` is only a docstring, so nothing else there
referenced it. Evaluations from Code stays in
`experimental.evaluations`, untouched.
- **API reports.** Regenerated with `make api-report`: the skills names
appear in `launchdarkly_ai_server.txt`, and
`launchdarkly_ai_server.experimental.skills.txt` is deleted.
- **Changelog.** The three `skills` → Experimental rows are removed from
`changelog-sections` in `release-please-config.json`, so `feat(skills):`
now lands under Features.
- **Docs.** The client README, the `launchdarkly-ai-python` README,
`packages/client/agents.md` and `AGENTS.md` no longer call Agent Skills
experimental. Code samples import from the root.
- **Core strings.** Docstrings and error messages in `skills_core.py`,
`skills_watch.py`, `types.py`, `types_validation.py`,
`shutdown_hooks.py` and `lifecycle.py` no longer name the experimental
module. For example, the "no skill store" error now says
`set_skill_store(store) from launchdarkly_ai_server`.
- **Usage IDs.** None. `main` reports no skills usage IDs today, so this
PR adds none. When skills usage reporting is added, it must use
`client.*` IDs, not `experimental.*`.
- **Unchanged behaviour.** `parse_ai_config` still passes `skills`
through unvalidated, and `skill_refs` still 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.skills` raises
`ImportError`, and `launchdarkly_ai_server.experimental` has none of the
names.
- `test_public_surface.py`: the skills names are added to the
`ROOT_SURFACE` snapshot.
- `test_lifecycle.py`: the "core loads no experimental module" check now
checks `evaluations` and `experimental` only, since the root now imports
the skills modules.
- The skills test suites and `conftest.py` import from the root.

## Test plan

Run in the PR worktree after `uv sync` (exit 0):

- [x] `make lint` — exit 0
- [x] `make format-check` — exit 0
- [x] `make typecheck` — exit 0 (`Success: no issues found in 101 source
files`)
- [x] `make api-report-check` — exit 0 (`14 API reports are current`)
- [x] `uv run pytest -q packages/client packages/ai` — 1709 passed, 1
skipped
- [ ] `make test` — exit 2: 3282 passed, 18 skipped, 3 failed. The same
3 tests fail on unmodified `origin/main` (e3afb3f) in the same checkout,
and CI on `main` is green. None of them involves skills:
-
`test_lifecycle.py::TestInitClientSDKKeyPath::test_uses_options_sdk_key_over_env_var`
and `::test_retries_after_a_failed_init_instead_of_replaying_it`: they
get a staging `stream_uri`. My unverified guess is a local `.env` above
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](https://claude.com/claude-code)


[AIC-3655]:
https://launchdarkly.atlassian.net/browse/AIC-3655?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

## Related PRs

Releases together with:
- launchdarkly/js-ai-sdk#159
- launchdarkly/go-ai-sdk#14

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Overview**
> **Promotes Agent Skills from experimental to the stable
`launchdarkly_ai_server` package root**, with a breaking migration from
`launchdarkly_ai_server.experimental.skills` to root imports (same names
and behavior). The experimental skills module is removed, API reports
and `ROOT_SURFACE` tests are updated, and release-please no longer
routes `feat(skills):` to the Experimental changelog.
> 
> **Skills helpers now emit the standard SDK helper usage event**
(`report_usage` with `client.*` IDs); `watch_skills` reconciles via
internal `_write_skills` so background reconciles do not double-count
`writeSkills`. Docs and samples (`client`/`ai` READMEs, `AGENTS.md`,
claude-agents) are aligned; **Evaluations from Code** remains the only
experimental surface.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
a6e1fde. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
XieX added a commit that referenced this pull request Oct 28, 2026
## Summary

Renames the Agent Skills delivery store, so Go matches
launchdarkly/python-ai-sdk#223 and launchdarkly/js-ai-sdk#172. The spec
change is launchdarkly/ai-sdks-monorepo#96, which follows
launchdarkly/ai-sdks-monorepo#95.

**Why.** FDv2 is still Early Access, and the base SDKs keep it out of
their public type names. The store is now named for its source rather
than its protocol.

| Before | After |
| --- | --- |
| `FDv2SkillStore` | `LaunchDarklySkillStore` |
| `NewFDv2SkillStore` | `NewLaunchDarklySkillStore` |
| `FDv2SkillStoreOptions` | `LaunchDarklySkillStoreOptions` |
| `FDv2Mode` | `LaunchDarklySkillStoreMode` |
| `FDv2ModeStream` / `FDv2ModePoll` | `LaunchDarklySkillStoreModeStream`
/ `LaunchDarklySkillStoreModePoll` |

**No deprecated aliases.** No Go version has been released: there are no
tags or GitHub releases, and the release-please manifest is at `0.0.0`.
So this is a clean rename, as in Python. TypeScript kept aliases because
0.5.0 shipped the old names.

The rename covers `internal/skills`, the `aiserver/experimental` aliases
and wrappers, the documented-surface test list, error and log strings
that name the type, `README.md` and `agents.md`.

**Unchanged:** the `internal/skills/fdv2_*.go` file names, the wire-kind
constants (`FDv2PayloadKind`, `FDv2ObjectKind`), the unexported
`newFDv2Reader`, the `TestFDv2*` test names, and prose that describes
the FDv2 protocol.

**Conflicts with #14.** #14 (promote Agent Skills to core) moves these
files into `aiserver/skills_*.go` and keeps the FDv2 names. Whichever
lands second has to carry the rename across. If #14 lands first,
rerunning the same word-boundary replacement over `aiserver/` redoes
this PR.

## Checks

- `go build ./...`, `go vet ./...` and `go test -race ./...` pass.
- `gofmt -l .` is clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Overview**
> **Breaking rename** of the experimental Agent Skills delivery store
public API so Go aligns with the Python and JS AI SDKs: types are named
for LaunchDarkly as the content source rather than the FDv2 wire
protocol.
> 
> Public symbols move from `FDv2SkillStore` / `NewFDv2SkillStore` /
`FDv2SkillStoreOptions` / `FDv2Mode` to **`LaunchDarklySkillStore`**,
**`NewLaunchDarklySkillStore`**, **`LaunchDarklySkillStoreOptions`**,
and **`LaunchDarklySkillStoreMode`** (with
`LaunchDarklySkillStoreModeStream` / `LaunchDarklySkillStoreModePoll`).
The change threads through `internal/skills`, `aiserver/experimental`
aliases, README/agents docs, the pinned `skillsSurface` test list, and
user-facing error/log strings (including `ErrStoreClosed` and
`noStoreMessage`).
> 
> **No deprecated aliases** — the package has not shipped a release yet.
**Behavior and FDv2 transport are unchanged**: `fdv2_store.go` /
`fdv2_reader.go` filenames, internal reader helpers, and `TestFDv2*`
names stay as-is.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
4cd495a. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant