Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
47a2a4a
feat(client): Agent Skills — reference discovery
XieX Aug 25, 2026
27ef12f
feat(client): Agent Skills — retrieval through an injectable store
XieX Aug 25, 2026
1afb690
feat(client): a documented log record for skill integrity failures
XieX Aug 28, 2026
8ad49f2
feat(client): descriptor-pinned filesystem primitives
XieX Aug 25, 2026
f1aad0a
test(client): Agent Skills — the filesystem abuse matrix
XieX Aug 25, 2026
568729d
feat(client): Agent Skills — materialize onto disk under a manifest
XieX Aug 25, 2026
c88d5bf
test(client): assert rename containment through the branching helper
XieX Aug 26, 2026
49c2741
feat(client): Agent Skills — self-healing reconciles, and keys no fil…
XieX Aug 30, 2026
eda4ae1
feat(client): Agent Skills — a distinguishable outcome for integrity …
XieX Aug 31, 2026
6a0e6aa
docs(client): Agent Skills — close out the security review's SDK-side…
XieX Sep 4, 2026
3bdbfab
test(client): Agent Skills — the root-swap races the descriptor walk …
pkaeding Sep 4, 2026
559c985
test(client): let the root-swap races fail outright
pkaeding Sep 4, 2026
dd5db37
fix(client): pin the skills root for the whole reconcile
XieX Sep 4, 2026
5040942
docs(client): deny the agent write access to the root's ancestors
XieX Sep 4, 2026
dd2d55c
feat(client): Agent Skills — re-reconcile on delivery with watch_skills
XieX Sep 10, 2026
0f797c0
feat(client): Agent Skills — the FDv2 delivery protocol, without the …
XieX Sep 10, 2026
e2b54fd
feat(client): an unheld version pin is a miss, not an integrity failure
XieX Sep 11, 2026
5817d89
fix(client): a broken store answer is not an absent skill
XieX Sep 11, 2026
29ad3fe
fix(client): attach the skill watcher's listener before its first rec…
XieX Sep 11, 2026
9571dca
chore: merge split/skills-retrieval into split/skills-safe-fs
XieX Sep 11, 2026
a6aae0a
chore: merge split/skills-safe-fs into split/skills-materialization
XieX Sep 11, 2026
4c6d965
test(client): a broken store answer never deletes managed files
XieX Sep 11, 2026
1cf5235
feat(client): Agent Skills — name the payload a transfer completed
XieX Sep 11, 2026
efc4ca7
fix(client): Agent Skills — read the skill's version off the wire key
XieX Sep 11, 2026
b6a25f9
feat(client): Agent Skills — the FDv2 delivery transport
XieX Sep 10, 2026
c285ff5
fix(client): retry an FDv2 stream that dies mid-read
XieX Sep 11, 2026
daf04a6
fix(client): raise the skill content cap to 10 MiB
XieX Sep 11, 2026
b7ee71a
fix(client): keep FDv2 delivery alive on an idle stream, and shut dow…
XieX Sep 14, 2026
88c225e
fix(client): log a recycled FDv2 stream at debug rather than warning
XieX Sep 14, 2026
d2386c8
feat(client): Agent Skills — the FDv2 delivery transport (#83)
XieX Sep 15, 2026
96576aa
feat(client): Agent Skills — the FDv2 delivery protocol, without the …
XieX Sep 15, 2026
09d7b60
feat(client): Agent Skills — re-reconcile on delivery with watch_skil…
XieX Sep 15, 2026
51f06a6
docs(client): Agent Skills — close out the security review's SDK-side…
XieX Sep 15, 2026
7b59680
feat(client): Agent Skills — a distinguishable outcome for integrity …
XieX Sep 15, 2026
b62571e
feat(client): Agent Skills — self-healing reconciles, and keys no fil…
XieX Sep 15, 2026
ba215ef
test(client): Agent Skills — the filesystem abuse matrix (5/5) (#54)
XieX Sep 15, 2026
71a2531
feat(client): Agent Skills — materialize onto disk under a manifest (…
XieX Sep 15, 2026
dad1d30
feat(client): descriptor-pinned filesystem primitives (3/5) (#52)
XieX Sep 15, 2026
68153ee
feat(client): Agent Skills — retrieval through an injectable store (2…
XieX Sep 15, 2026
d3a8bc1
fix(client): read the skills manifest through the pinned root descriptor
XieX Sep 15, 2026
e1356a5
fix(client): pin the skills root for the whole reconcile (SEC-8985 ro…
XieX Sep 15, 2026
b7ad16f
fix(client): give the key-mismatch resolution its outcome reason
XieX Sep 15, 2026
fe13c23
Merge remote-tracking branch 'origin/main' into split/skills-references
XieX Sep 15, 2026
0902f29
test(client): derive the oversize fixture from the content cap
XieX Sep 15, 2026
9252fb0
fix(client): resolve a missed pin as absent, not as an integrity failure
XieX Sep 15, 2026
ad53fa0
fix(client): probe for os.fchmod instead of assuming it
XieX Sep 16, 2026
7c070d0
fix(client): close two paths that let the reconcile prune what it sho…
XieX Sep 16, 2026
098455f
fix(client): stop reporting a withholding for a key that resolved
XieX Sep 15, 2026
5a896ce
fix(client): do not prune when the store has not received its initial…
XieX Sep 15, 2026
b0ac0e3
fix(client): bound the manifest read, and two smaller reconcile fixes
XieX Sep 15, 2026
a6eeae9
docs(client): sweep the skills comments for a public SDK audience
XieX Sep 15, 2026
e40fbb9
docs(client): move the threat model into agents.md, leave invariants …
XieX Sep 15, 2026
4bc12c0
test(client): make the no-track assertions order-independent
XieX Sep 15, 2026
d30d4b0
fix(client): require https and refuse redirects in the skills transport
XieX Sep 16, 2026
6c08ffe
fix(client): bound transport reads in memory before verification sees…
XieX Sep 16, 2026
45b549c
fix(client): pin the decision reads under the skills root to descriptors
XieX Sep 16, 2026
5e6313c
fix(client): start a replacement thread when a restart races the give-up
XieX Sep 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
442 changes: 442 additions & 0 deletions packages/client/README.md

Large diffs are not rendered by default.

627 changes: 626 additions & 1 deletion packages/client/agents.md

Large diffs are not rendered by default.

53 changes: 53 additions & 0 deletions packages/client/src/launchdarkly_ai_server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,24 @@
resolve_tools,
)
from .sdk_info import SDK_INFO_CONTEXT, SDK_INFO_EVENT, register_ai_sdk_package
from .skills import (
InMemorySkillStore,
all_skills,
get_skill,
get_skill_result,
get_skills,
skill_refs,
)
from .skills_core import SkillStore
from .skills_fdv2 import FDv2SkillStore, StoreDiagnostics
from .skills_fs import (
MANIFEST_FILENAME,
MANIFEST_VERSION,
SKILL_FILENAME,
OnUnavailable,
write_skills,
)
from .skills_watch import SkillWatcher, watch_skills
from .tracking import execute_and_stream, execute_and_track, wrap_tool_handlers
from .types import (
NATIVE_TOOL_KEY,
Expand Down Expand Up @@ -78,6 +96,13 @@
ProviderGraphResponse,
ProviderHandler,
ProviderResponse,
ReconcileAction,
ReconcileActionKind,
ReconcileReport,
Skill,
SkillOutcome,
SkillOutcomeReason,
SkillReference,
StreamChunkEvent,
StreamDoneEvent,
StreamEvent,
Expand Down Expand Up @@ -138,6 +163,12 @@
"ProviderGraphResponse",
"ProviderHandler",
"ProviderResponse",
"ReconcileAction",
"ReconcileActionKind",
"ReconcileReport",
"Skill",
"SkillOutcome",
"SkillReference",
"StreamChunkEvent",
"StreamDoneEvent",
"StreamEvent",
Expand Down Expand Up @@ -221,6 +252,28 @@
"graph",
"resolve_graph",
"GraphInstance",
# skills
"skill_refs",
"get_skill",
"get_skill_result",
"get_skills",
"all_skills",
"write_skills",
"SkillStore",
"InMemorySkillStore",
# skills — the FDv2 delivery transport, and the eager re-reconcile it enables
"FDv2SkillStore",
"StoreDiagnostics",
"watch_skills",
"SkillWatcher",
# skills — the three closed-set unions a typed consumer needs to name
"ReconcileActionKind",
"OnUnavailable",
"SkillOutcomeReason",
# skills — on-disk constants, identical across languages
"SKILL_FILENAME",
"MANIFEST_FILENAME",
"MANIFEST_VERSION",
]

register_ai_sdk_package("launchdarkly-ai-server", __version__)
42 changes: 40 additions & 2 deletions packages/client/src/launchdarkly_ai_server/lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import os
from typing import Any

from . import skills
from .sdk_info import flush_ai_sdk_info, reset_ai_sdk_info
from .types import InitClientOptions

Expand Down Expand Up @@ -131,13 +132,43 @@ async def init_client(

- Pass *client* directly (BYOC) to skip the LaunchDarkly Python SDK path.
- Otherwise, reads ``LD_SDK_KEY`` from env or ``options['sdkKey']``.
- ``options['skillStore']`` configures the store the Agent Skills accessors
read from. Absent by default, in which case they raise an actionable error.

This function is idempotent for the client singleton: a second call returns
the existing client without re-initializing, and every option is ignored —
**except** ``skillStore``, which is applied on every successful call. That
asymmetry is deliberate, and it is what lets a client that was lazily
auto-initialized, or initialized without a store, be given one afterwards.
A ``skillStore`` of ``None`` (or absent) never clears an already-configured
store; use ``shutdown()`` for that. The store is installed only once
initialization has succeeded: a call that raises leaves no global state
behind, so a failed init cannot leave the skill accessors working against a
store the application believes was never installed.

Returns the initialized ``LDClientInterface`` instance.
"""
global _client

opts = options or {}

ld_client = await _resolve_client(opts, client)

# The single success point: every path that raises returns before here, so
# "installed only on success" is one statement rather than a copy per exit.
skill_store = opts.get("skillStore")
if skill_store is not None:
skills._set_store(skill_store)
return ld_client


async def _resolve_client(opts: InitClientOptions, client: Any) -> Any:
"""
Returns the singleton client, initializing it on first call.

Split from ``init_client`` so that function has exactly one success point to
hang the ``skillStore`` carve-out on.
"""
global _client

# Idempotent — if already initialized, return the existing client
if _client is not None:
flush_ai_sdk_info(_client)
Expand Down Expand Up @@ -202,12 +233,18 @@ async def shutdown() -> None:
"""
Shuts down the singleton client. Idempotent — safe to call multiple times
even if the client was never initialized or already shut down.

Also clears the configured skill store (and telemetry emitter): after a
shutdown, re-pass ``skillStore`` to the next ``init_client`` if the skill
accessors should keep working.
"""
global _client, _tracer_provider

local_client = _client
local_provider = _tracer_provider

skills._clear_state()

# Null the singleton before any awaits so a second call is a no-op
_client = None
_tracer_provider = None
Expand Down Expand Up @@ -245,6 +282,7 @@ def _reset_for_testing() -> None:
global _client, _tracer_provider
_client = None
_tracer_provider = None
skills._clear_state()


async def inspect_config(
Expand Down
Loading
Loading