Skip to content

Release 0.21.0

Latest

Choose a tag to compare

@tercel tercel released this 06 May 14:04
· 23 commits to main since this release

Added

  • Module.preview() + PreflightResult.predicted_changes (PROTOCOL_SPEC v0.21.0 §5.6 / §12.8 — promoted from RFC apcore/docs/spec/rfc-preview-method.md, apcore commit c191b85) — New optional preview(inputs, context) method on the Module Protocol. Modules implementing preview() return a PreviewResult (or None when prediction is unavailable) whose changes list is a structured prediction of the state changes the call would produce — answering the AI-orchestrator-driven question "if I were to call this module with these inputs, what would change in the world?". Detection mirrors the existing preflight() optional-method pattern (hasattr(module, "preview") and callable(module.preview)). Executor.validate() invokes the method (awaiting the result if it's a coroutine — both sync and async implementations are supported), folds PreviewResult.changes into the new PreflightResult.predicted_changes field, and records a module_preview advisory check on PreflightResult.checks. Exception semantics match preflight(): a raised exception is surfaced as a warning on the module_preview check and does not fail validation. New Change and PreviewResult pydantic models exported from the top-level apcore package. Change uses the Python idiomatic encoding called out in the RFC's "Change.x-* extension fields" cross-SDK schema-encoding table — pydantic.ConfigDict(extra='allow') paired with a model-validator that rejects extra keys not matching ^x-, mirroring the ^x- extension convention used elsewhere in the protocol (§4.6). Reference parity: apcore-typescript PR #29.

  • ephemeral.* namespace + discoverable annotation pilot (apcore RFC docs/spec/rfc-ephemeral-modules.md, #25) — Pilot implementation ahead of upstream RFC acceptance (RFC is in Draft / RFC state). Reserves the ephemeral.* namespace for programmatically-registered modules synthesized at runtime by LLM-agent pipelines (e.g. ToolMaker, ACL 2025, arXiv 2502.11705). Filesystem discovery now refuses to register any ID that falls under ephemeral.* and raises InvalidInputError with code INVALID_MODULE_ID; the namespace is reachable only via Registry.register(). New discoverable: bool = True field on ModuleAnnotations — when False, the module is excluded from Registry.list() (default behaviour; include_hidden=True returns the full set), Registry.iter(), Registry.module_ids, and downstream manifest export, while remaining callable via Registry.get() / Executor.execute(). New Registry.set_event_emitter(emitter) opt-in: when wired, ephemeral.* registrations / unregistrations emit canonical apcore.registry.module_registered / apcore.registry.module_unregistered events whose data payload mirrors the D-35 contextual-audit shape (caller_id defaulting to "@external", plus a redacted identity snapshot when context.identity is set). Without an emitter the same audit information is logged at INFO so it never silently disappears. Registry.register() / Registry.unregister() accept an optional context= keyword used solely to enrich those audit payloads. A soft logging.warning(...) fires when an ephemeral.* module is registered without requires_approval=True (per the RFC). Lifecycle is caller-managed via Registry.unregister(); TTL/GC sweeper and host-side sandboxing are deliberately out of scope for the v1 pilot. New top-level constant EPHEMERAL_NAMESPACE_PREFIX exported from apcore.registry.registry. Pilot disclaimer: the upstream RFC is not yet accepted; downstream SDKs (apcore-typescript, apcore-rust) will follow once Python pilot findings are reported back.

Changed

  • iter-11 alignment with upstream apcore RFC rfc-ephemeral-modules.md (apcore commit 81df336) — Tightens the ephemeral.* pilot against two new normative rules added during the RFC iter-11 reconciliation round:
  1. Audit-event single-emit rule (RFC §"Audit-event single-emit rule"). The legacy _bridge_registry_events callback in apcore.sys_modules.registration now short-circuits for ephemeral.* module IDs so that exactly one apcore.registry.module_registered / apcore.registry.module_unregistered event is emitted per registration — the rich registry-side direct emit carrying the full D-35 contextual payload — instead of being followed by a second empty-payload copy from the bridge. Non-ephemeral registrations are unaffected (legacy bridge behaviour preserved for backwards compatibility).
  2. register_internal() rejection (RFC §"register_internal() interaction"). Registry.register_internal() now raises ValueError when called with an ephemeral.* module ID, directing the caller to Registry.register(). Rationale: namespace → registration-mechanism is a 1:1 mapping; mixing the two paths blurs the audit-trail distinction between framework-emitted (system.*) and caller-emitted (ephemeral.*) modules.