Skip to content

dev → 1.0.2 — Registry Toolkit Managers, Setup CRUD & Storage/Filesystem Scopes¤

Summary¤

This release aggregates everything on the branch relative to dev. The headline is the Registry Toolkit: four agent-facing Agno tools (services_manager, kins_manager, tools_manager, load_manager) that collapse the many individual registry tools into four entrypoints, each driven by a single discriminated-union action argument. Around it, the setup service was narrowed to setup-level CRUD + visibility (the version lifecycle is now platform-owned), the storage and filesystem services gained record visibility and context scopes (including the read-only cross-owner USERS / ORGANIZATIONS scopes, detailed in the 1.0.2.dev6 / 1.0.2.dev7 notes), and a set of supporting toolkits, content validation, and framework plumbing landed.

Breaking changes are marked (breaking). Storage/filesystem specifics are covered in docs/changelog/1.0.2.dev6.md and docs/changelog/1.0.2.dev7.md; they are summarised here for completeness.


Added¤

Registry Toolkit — four agent-facing managers¤

A new surface under src/digitalkin/community/agno/toolkits/registry/. Each manager is one Agno tool taking one discriminated action; the union is the tool's LLM schema, and a malformed action comes back as a clean fail envelope the model self-corrects from (never a raised traceback).

  • services_manager (ServicesManager, SERVICE setups) — get, create (shareable service from name + config JSON), search, load (returns the service's stored config content), update, delete, change_visibility.
  • kins_manager (KinsManager, ARCHETYPE setups) — get, search, update, delete, change_visibility.
  • tools_manager (ToolsManager, TOOL_MODULE setups) — get, search, update, delete, change_visibility; it administers tool setups only and never makes a tool callable (no create).
  • load_manager (LoadManager) — an external-execution tool (tool action) that resolves a discovered tool setup into a live ModuleToolkit and appends it to the agent's tool list, making it callable in the same turn. Idempotent per setup_id, with distinct not-found / wrong-family / already-loaded / rebind-conflict messages.

Shared plumbing (registry/base.py, registry/action.py):

  • RegistryObjectToolKit (a DkToolkit) carrying the RegistryActionCtx action context, the ensure_kind type guard that refuses cross-type access (e.g. kins_manager reading a tool, or a mutation on the wrong kind / a deleted id), a fail-safe _guard/_dispatch envelope, best-effort setup-cache invalidation after writes, and _jsonable normalisation that echoes visibility back in caller vocabulary (VISIBILITY_INTERNALinternal).
  • Tools register with skip_entrypoint_processing + an explicit schema, so args are validated in _run via a TypeAdapter (including models that serialise the nested action as a JSON string).
  • Shared actions GetAction / SearchAction / UpdateAction / DeleteAction / ChangeVisibilityAction. search is semantic (nearest-match, capped at 25): it drops versionless (non-instantiable) rows, and its truncated flag is a genuine next-page signal read on the rendered page (reads limit + 1, so a full page of exactly limit rows is not falsely reported as truncated). Content-bearing actions run a name control-char validator and an unsafe-content-key validator.

Content validation¤

  • SetupContentValidator (src/digitalkin/utils/setup_content_validator.py) — validates a setup's content against the backing module's config JSON schema before a create/update by compiling a throwaway strict Pydantic model: resolves $ref/$defs (cycle-breaking), nests objects, types array elements and additionalProperties maps, closes enum/const to Literal, honours nullability, forbids undeclared keys unless opted in, and enforces numeric/string/array/object constraints. Plus reject_control_chars (C0 control chars in strings) and reject_unsafe_keys (recursively rejects content keys carrying non-BMP/control characters that storage would silently drop).

Supporting Agno toolkits & tool loading¤

  • DkToolkit (toolkits/base.py) — shared base for all DigitalKin Agno toolkits: the canonical {"output"|"error", "metadata"} JSON envelope (_ok/_fail) and best-effort AG-UI custom-event emission (_notify); toolkits never raise into the agent loop.
  • ChatHistoryTools (toolkits/chat_history.py) — a token-cheap two-step chat-history surface replacing Agno's full-dump get_chat_history: outline_chat_history (metadata-only index with role/preview/size + paging) then read_chat_messages (full content by id, with truncation and media as references).
  • UserProfileTools (toolkits/user_profile.py) — get_user_profile exposing the current user's name/email/plan/credits, lazily fetched and cached.
  • DefaultToolkits (toolkits/defaults.py) — a one-call build() assembling the default toolkits (chat history, user profile, the three registry managers when context.setup is wired, and LoadManager bound to the live tool list) with bind_host() for two-phase agent construction.
  • ModuleToolkit (module_toolkit.py) — an Agno Toolkit wrapping a remote SDK tool module's tools over gRPC: explicit-schema functions, SDK sentinel-protocol parsing, multimodal image extraction into ToolResult.images, AG-UI event relay, timeouts, and per-call cost/timing metadata.
  • ToolCallMetadata / ToolOutputMetadata (community/agno/models.py) — cost/timing metadata models for module tool calls (response time, API calls, cost estimate, credits, result counts) with serialisers and extract_tool_metadata; both exported from community.agno.

Services, models & settings¤

  • RegistryStrategy.search_setups(query, setup_ids, module_ids, module_types, statuses, visibilities, limit, offset) returning SetupSummary (a search-safe view that never carries config); implemented in DefaultRegistry (in-memory) and GrpcRegistry (new SearchSetups RPC). Plus search_tools / search_kins / search_services convenience views and get_service_setup(setup_id).
  • SetupStrategy.change_visibility(setup_dict) (public/private/internal) and the create_service_setup(name, content) convenience wrapper, both returning SetupData.
  • SetupData gains status: RegistrySetupStatus and visibility: Visibility (default UNSPECIFIED, coerced from proto enum names). StorageRecord gains visibility: Visibility.
  • New SetupSummary model; RegistryModuleType.SERVICE member; SetupInfo.module_name / SetupInfo.module_type.
  • New services.Context enum (filesystem scopes) and storage.Visibility enum (UNSPECIFIED/PUBLIC/PRIVATE/ INTERNAL), with read-only cross-owner USERS / ORGANIZATIONS scopes.
  • StreamErrorCode.SETUP_VALIDATION_ERROR.
  • New models/settings/registry.py: RegistrySettings (search_timeout_s default 10.0, env prefix DIGITALKIN_REGISTRY_) with a cached get_registry_settings() singleton.

Module & context¤

  • ModuleContext.resolve_tool(setup_id) — resolves a registry setup id into a ToolModuleInfo and caches it, while re-running registry.get_setup authorization on every call (even cache hits).
  • ModuleContext.get_module_config_schema(module_id, *, llm_format=False) — fetches a module's config-setup JSON schema (used to validate content before create/update).
  • ModuleContext.setup — a new borrowed SetupStrategy | None field/param, letting setup-CRUD toolkits reach the servicer's shared setup service.
  • BaseModule.build_registry_documentation() — assembles registry docs from the author description plus a markdown non-utility trigger table; BaseModule.registry_type ClassVar (default UNSPECIFIED), overridden to ARCHETYPE on ArchetypeModule and TOOL_MODULE on ToolModule.
  • preload_instance (base + SingleJobManager) gains setup and invalidate_setup params, wired onto context.setup and context.callbacks.invalidate_setup before prepare().

Changed¤

  • (breaking) RegistryModuleType.TOOL = "tool" renamed to TOOL_MODULE = "tool_module" to mirror the proto ModuleType names.
  • (breaking) Setup service narrowed to setup-level CRUD + visibility: create_setup returns SetupData (was str) and takes {name, content} (owner/org/module resolved server-side); get_setup is keyed by setup_id (was name) and returns the current version populated; update_setup returns SetupData (was bool) and takes {setup_id, name, content}. DefaultSetup is now a pure in-memory store; GrpcSetup input-guard failures raise ValueError (was ValidationError).
  • (breaking) Storage: the scope: Literal["mission","setup"] argument became context: Context (default MISSIONS) across store/read/update/remove/list/remove_collection/upsert, and data_type became the DataType enum (was a string). See 1.0.2.dev6.
  • (breaking) Filesystem: get_file / get_files (FileFilter) take context: Context (was a "mission"/ "setup" string); writes stay mission-scoped. See 1.0.2.dev7.
  • RegistryStrategy.search() dropped organization_id, added limit/offset pagination, and now matches module name and documentation (case-insensitive); the gRPC path moved DiscoverModulesSearchModules and honours the tightened search_timeout_s deadline.
  • RegistryStrategy.register() gains module_type and documentation params; ModuleServer registration now sends the module's registry_type and build_registry_documentation() output.
  • GrpcRegistry re-raises PermissionDeniedError unwrapped across its RPCs and fail-closes enum encoding (_encode_enum raises on Python/proto drift rather than silently dropping a filter).
  • GrpcStorage / GrpcFilesystem now send a context- kind wire enum (server resolves the concrete id from request metadata) and map Visibility to the wire enum; visibility is forwarded on writes and visibilities on list.
  • AgnoStreamAdapter suffixes manager tool-call display names with the resolved action (e.g. services_managerservices_manager_create, load_managerload_manager_tool) so the front can tell collapsed operations apart (cosmetic; LLM function name and HITL matching unchanged).
  • AgnoHitlRunner resolves load_manager pauses in-process and auto-continues the run (bounded to 20 iterations), so discover → load → use reads as a single turn; it only surfaces a pause when a genuine frontend tool remains, and emits a RunErrorEvent (auto_continue_limit) on exceeding the bound.
  • BaseModule.description is now optional (defaults to "").
  • ModuleServicer tool-cache methods gained hit/set/expire/build/invalidate logging.

Fixed¤

  • ModuleContext._create_single_tool_function: a fatal stream.error frame (e.g. SETUP_ACCESS_DENIED) in a tool-call stream now raises ToolCallError (new, in communication/exceptions.py) instead of yielding as a benign result, so a denied sub-call aborts the parent run.
  • ModuleRunner wraps setup resolve / model-build / tool-cache in a ValidationError guard that emits SETUP_VALIDATION_ERROR via on_fatal (with a missing-fields summary) rather than crashing the run.
  • ToolReference.resolve_tool returns None (dropping the selection) when no enabled trigger matches, instead of caching a ToolModuleInfo with empty tools.
  • Enum robustness: ModuleInfo normalizes legacy module_type ("tool"tool_module, "kin"archetype); RegistrySetupStatus._missing_ / Visibility._missing_ coerce proto/any-case names and fall back to UNSPECIFIED, so reads never crash on unknown states.
  • GrpcStorage ListRecords/ReadRecord log-and-skip a foreign-shaped record that fails validation instead of failing the whole call.
  • PausedRunStore.collect_pending skips tool calls already resolved in-process, so a load_manager call handled by the runner no longer leaks to the front as an unimplementable frontend tool; on auto-continue after a load, the runner clears Agno's tools-factory cache so the just-loaded function is immediately callable.
  • StorageMixin.store_storage/upsert_storage convert the data_type string to DataType[...] before delegating.
  • BaseServer vCPU detection guarded by sys.platform == "linux" (falls back to os.cpu_count() elsewhere).
  • Gateway BiDi dial-back close: a read parked before outgoing_done now switches to the close-grace timeout once outputs drain, instead of parking to the full RPC deadline.
  • Removed prohibited getattr(...) attribute access in hitl.py in favour of direct typed access.

Removed¤

  • (breaking) All standalone setup-version RPCs and listing from the setup strategy/implementations: create_setup_version, get_setup_version, search_setup_versions, update_setup_version, delete_setup_version, and list_setups — the version lifecycle is now platform-owned via the setup's current_setup_version.
  • The Scope = Literal["mission","setup"] alias in storage_strategy.py (superseded by Context).
  • organization_id from the registry search() API.

Tests¤

  • Agno toolkits — a new tests/community/agno/toolkits/ suite (~9 files): DkToolkit envelope/notifications; the registry managers (manage_* dispatch, ~54 tests) covering setup-schema content validation, control-char and empty-content rejection, and visibility enforcement; plus the load manager, chat-history tools, the default-toolkit assembler, and user-profile tools.
  • Agno adapter/module — dynamic tool-loading tests (real agno dep), ModuleToolkit output/event-stream tests, and adapter coverage for the action-suffixed manager tool names.
  • Modules — registry-documentation assembly and fatal stream.error abort suites; expanded tool-cache / tool-reference tests (resolve+save, cache-hit permission re-checks, permission-denied propagation).
  • Services — new DefaultRegistry and registry-hardening (enum encode/decode symmetry, scoped settings) suites; gRPC registry expanded for trimmed search summaries and search_setups mapping/filtering; storage expanded for record visibility and cross-owner context; filesystem context-kind forwarding; the setup gRPC suite largely rewritten around the reduced CRUD surface (no-RPC-on-missing-fields, version pinning).
  • Gateway / Core — dial-back close-after-fatal and stream-error propagation; module_runner M4 producer regression.

Roughly 13 new test suites added and ~16 existing files expanded.