Feat/meta plugin - #42
Merged
Merged
Conversation
Delivers a full self-evolving plugin framework inspired by
deepseek-harness's "everything is a plugin" design, adapted to
LeapFlow's Signal-Driven philosophy.
Core layers:
Protocol layer
- ToolPlugin, GatewayAdapterPlugin, LLMProviderPlugin
- SignalSource (transform-only), ActiveSignalSource (lifecycle)
- CVProcessor, FrameStore (upgraded from ABC to Protocol)
Registry + lifecycle
- Delete registry_bootstrap.py (1507 lines) -> ToolPluginRegistry
+ plugins/ modular directory
- ToolMetadata as single source of truth
- EffectScope + PluginFiber state machine
- ScopedRegistry (Tool/Gateway/LLM) with adopt_existing_plugins
for full fiber coverage at boot
Hot-reload
- reload_plugin() explicit API
- version counter drives engine _registry_cache invalidation
- per-turn handler snapshot guarantees concurrent safety
- config-driven disabled_plugins
Self-Modification (7 tools, approval-gated)
- plugin_list / plugin_status / plugin_reload / plugin_disable /
plugin_enable / plugin_generate / plugin_install
- HIGH risk, allow_permanent=False (defense-in-depth in risk.py)
- Cross-subsystem introspection (tools + gateway + llm)
- Progressive Trust auto-approve for PRODUCTION-tier reload only
Learning closed loop
- PluginTrustLedger (DRAFT->CANDIDATE->VERIFIED->PRODUCTION)
- PluginUsageTracker (bounded deque, cross-turn accumulator)
- PluginAdvisor (stateless scoring)
- PluginHealthProducer (MonitorProducer proactive alerts)
- DuckDB persistence via PluginStatsStore
- Data source: TurnUsageTracker.record_tool_call forward hook
Perception signal sources
- 7 transform-only builtin (click/scroll/keyboard/drag/clipboard/
app_switch)
- ActiveSignalSource lifecycle-bearing category:
FileWatchSignalSource (watchdog), WebhookSignalSource,
CronSignalSource, FeishuIMSignalSource (real-world signal demo)
- Queue-serialized downstream via ActiveSourceManager
- thread-safe emit via call_soon_threadsafe
Self-evolution capstone
- Plugin sandbox (subprocess isolation, JSON-RPC over stdin/stdout)
- Marketplace prototype (LocalDirectorySource + HttpMarketplaceSource,
checksum-verified install)
- LLM plugin generator (multi-stage validation:
syntax -> structure -> import -> protocol; sandbox deferred
to install-time for safety)
Developer experience
- PluginFileWatcher (auto hot-reload on file changes)
- /plugin slash command (list/status/reload/disable/enable) --
PENDING HUMAN CONFIRMATION per AGENTS.md
- ToolBridge cleanup assessment (Landing A/B/C plan documented
in bridge_adapter.py)
Documentation:
- temp/deepseek_harness/pluggable_architecture_analysis.md
- temp/deepseek_harness/tool_plugin_refactoring_plan.md
- temp/deepseek_harness/PLUGIN_DEVELOPER_GUIDE.md (1145 lines)
- temp/deepseek_harness/PLUGIN_SYSTEM_TODO.md
Test coverage: 1721 -> 1954 (+233 new tests, zero regressions).
Pending items:
- /plugin slash command needs human verification per AGENTS.md
- Journey cassettes need re-recording (`make seed-cassettes`)
- ToolBridge Landing A/B/C staged cleanup
Implement a 6-stage (P0: stages 1-2) assessment pipeline that evaluates foreign plugins (primarily from deepseek-harness ecosystem) for LeapFlow compatibility before installation is attempted. Core components: - learning/compatibility/protocol.py: Verdict enum (COMPATIBLE/ADAPTABLE/ PARTIAL/INCOMPATIBLE), frozen dataclasses (PluginManifestInput, StageResult, CompatibilityReport, AdapterSpec) - learning/compatibility/taxonomy.py: 30+ entry pluggability boundary map classifying every DSH plugin category against LeapFlow's architecture - learning/compatibility/stages/manifest_parser.py: Stage 1 — auto-detect and parse LeapFlow PluginManifest or DSH package.json formats - learning/compatibility/stages/category_resolver.py: Stage 2 — taxonomy lookup with INCOMPATIBLE short-circuit - learning/compatibility/pipeline.py: assess_plugin() orchestrator with short-circuit on INCOMPATIBLE at any stage Also removes 3 vestigial empty directories (tools/plugins/, tools/sandbox/, tools/marketplace/) left over from the module extraction. Tests: 35 new tests covering parsing, resolution, pipeline e2e, short- circuit, and API contracts. Full suite: 2145 passed, 0 regressions.
Implement all 6 assessment stages + verdict synthesizer + tool exposure + marketplace install gate: - Stage 3 (interface_analyzer): pattern-match declared_interfaces against required interface patterns per target Protocol - Stage 4 (dependency_checker): classify deps as satisfiable/shimmable/ blocking using curated pattern sets - Stage 5 (execution_model): map async/sync/worker/streaming + source language (python/typescript) to LeapFlow equivalents - Stage 6 (security_classifier): map permissions to risk levels, recommend isolation (in_process/sandbox/reject) - verdict.py: aggregate all stages into final CompatibilityReport with AdapterSpec generation for ADAPTABLE verdicts - Pipeline extended to run all 6 stages with lazy imports + short-circuit - Tool #12: assess_compatibility (read-only, no approval needed) - Marketplace install gate: INCOMPATIBLE manifests rejected before install Tests: 76 compatibility + 56 self-management = 2186 total passed, 0 regressions.
…oading + docs update - adapter_generator.py: template-based bridge wrapper generation for ADAPTABLE plugins (DshXxxBridgePlugin skeleton with SandboxHost JSON-RPC delegation); LLM-enhanced mode with graceful fallback to template - manifest_converter.py: convert_dsh_to_leapflow() pure utility for DSH package.json → LeapFlow PluginManifest format mapping - pipeline.py: file-path manifest loading (str/Path → JSON read with graceful error handling for missing/invalid/non-object files) - Docs: README Compatibility Assessment subsection, third_party_dev §5.7, lifecycle governance matrix row, developer guide module paths, TODO marks P0+P1+P2 as complete (~99%) Tests: 111 compatibility assessment tests. Full suite: 2221 passed. Deep review: 3-dim (completeness/correctness/impact) all clear, no Critical or Warning findings.
Inspired by the Cordis spatiotemporal composability formal model:
1. PluginFiber state machine extended with LOADING and FAILED states:
- PENDING→LOADING→ACTIVE (opt-in async init tracking)
- LOADING→FAILED (init failure with error storage)
- FAILED→LOADING (retry semantics, clears error)
- PENDING→DISPOSED (cleanup of never-started fibers)
- Existing PENDING→ACTIVE path preserved (zero breaking changes)
- New methods: begin_loading(), fail(error), retry()
- New properties: is_loading, is_failed, error
2. EventBus scope-bound subscriptions:
- Subscriber storage: list → dict[id, callback] for O(1) unsubscribe
- New scope parameter: subscribe(cb, scope=fiber.scope) auto-registers
unsubscribe cleanup on EffectScope dispose
- Duck-typed scope check avoids import cycles
- Net performance improvement during bulk plugin dispose
Both changes are cold-path only — zero impact on per-turn tool dispatch.
Tests: 2232 passed (+11 new), 0 regressions.
…ogical bind order
Two upgrades inspired by Cordis reactive coeffects and provider-consumer
lifecycle coupling:
1. Dependency-driven fiber activation (scoped_registry.py):
- Plugins with declared dependencies enter LOADING state at boot
- _check_pending_activations() fixpoint loop auto-activates fibers
when all their deps become satisfied (provider plugin ACTIVE or
dep in last_bound_deps)
- Plugins with NO deps activate immediately (zero behavior change)
- Circular/unsatisfiable deps: graceful force-activate with WARNING
- External runtime deps (file_read_gate etc.) log DEBUG, not WARNING
2. Provider-consumer ordering protocol (registry.py):
- bind_runtime() now iterates plugins in topological order using
graphlib.TopologicalSorter (stdlib Python 3.11+)
- Providers are bound before consumers, eliminating init-order bugs
- CycleError: log warning + fallback to registration order
Tests: 11 new (test_dependency_activation.py). Full suite: 2243 passed,
0 regressions.
… + docs 1. Waterfall Tool Execution Pipeline (domain/tool_pipeline.py): - ToolInterceptor runtime_checkable Protocol (name, priority, before, after) - ToolExecutionPipeline: composable pre/post interceptor chain - Before hooks by ascending priority; short-circuit on non-None return - After hooks in reverse priority (innermost first) - Zero overhead when no interceptors registered - Built-in examples: AuditInterceptor, TimeoutInterceptor - Exposed via ToolPluginRegistry.tool_pipeline property 2. Async EffectScope cleanup (domain/effect_scope.py): - async_effect(cleanup) registers async teardown callbacks - async_dispose() awaits children + async effects + sync effects (LIFO) - Sync dispose() graceful degradation: asyncio.run() or fire-and-forget 3. Documentation updates: - plugin_fiber.py docstring: full 6-state machine documentation - README: Lifecycle & Composability section - All docs/plugins/ files: 6-state, pipeline, dep-driven activation - TODO: Cordis P0+P1+P2 marked complete Deep review: 3-dim (completeness + correctness) all clear, 0 findings. Tests: 2284 passed (+40 new pipeline + async tests), 0 regressions.
…rade
Three follow-up items + comprehensive experiment upgrade:
1. Engine ToolExecutionPipeline integration (engine.py):
- _execute_general_tool routes handler calls through registry.tool_pipeline
when interceptors are registered (fast path preserved when empty)
- Timeout enforcement moved into pipeline.execute() via annotations
- Approval/semantic gating unchanged (runs before pipeline)
2. Adapter generator escaping hardening (adapter_generator.py):
- _normalize_name strips all non-identifier chars (re.sub)
- _pascal_case strips non-alnum from parts
- String interpolation uses _py_str_literal()/repr() for safety
- compile() guard at generation time catches template bugs early
3. ActiveSourceManager async dispose: confirmed already correct
(async def dispose() awaited by PerceptionSession.stop() directly;
no EffectScope misregistration to fix)
4. Plugin experiment comprehensive upgrade (temp/plugin_exp/):
- 8 new automated phases (F-M): fiber states, scope-bound EventBus,
dependency activation, waterfall pipeline, async cleanup,
compatibility assessment, proposals, lifecycle storm
- 5 new test plugins (dep_chain × 3, fiber_test, dsh_compat manifest)
- Existing phases B/C/D upgraded with new assertions
- Runner: 484 → 1257 lines, 13 phases total, all passing
Tests: 2286 passed, 0 regressions. Experiment: 13/13 phases green.
Implement /plugin generate — a single-action slash command that generates, validates, and installs a LeapFlow-conformant plugin from a natural-language description with zero intermediate prompts. Command: /plugin generate [--preview|--dry-run|--id <id>] <description> - Config gate: plugin_generation_enabled must be True - LLM gate: provider must be available - Auto-derive plugin_id from description (slugify first 3 words) - Generate via PluginGenerator + bounded refinement retry (1 attempt) - Validate (syntax→structure→import→protocol) - Install via self_management._install_from_code (sandbox + register) - Preview/dry-run modes for inspection without install - Auto-approve (user intent = consent; differs from agent-tool approval gate) Review fixes: - --id without value now returns usage error (not silent bad id) - Retry error snippet capped at 512 chars (prevents token bloat) Docs: README, third_party_dev, lifecycle governance matrix, TODO all updated. PENDING: human confirmation per AGENTS.md (slash command new behavior). Tests: 2286 passed, 0 regressions.
The /plugin generate command and plugin_generate tool should be available out of the box without requiring users to manually flip a config switch. Installation still goes through ApprovalGate; the generation step itself is read-only (produces code without writing), so gating it separately adds friction without meaningful safety benefit.
Root cause: /plugin generate (and any non-streaming RPC with a 30-60s LLM call) timed out because the client's 30s readline deadline fired while the daemon blocked on the handler. Streaming turns (engine.chat) survive via periodic heartbeats, but command.execute used a plain request-response with no heartbeat. Fix: - server.py: _await_with_heartbeat() wraps async non-streaming handlers, sending a heartbeat StreamChunk (metadata.heartbeat=True) every _stream_heartbeat_s (10s) while awaiting. Fast RPCs (<10s) send zero heartbeats (asyncio.wait returns immediately on completion). Handler exceptions propagate via task.result(); on client disconnect the task is cancelled and awaited (no orphan tasks). - client.py: request() skips stream.chunk notifications with metadata.heartbeat=True, resetting its per-read deadline each time, so a live-but-slow server no longer trips the timeout. A truly dead server still times out on the next readline. Verified safe by 2-dim review (correctness + impact): multi-client isolation intact (heartbeats go to the per-connection writer), streaming heartbeats unaffected (separate _dispatch_stream path), exception path unchanged. PENDING human confirmation per AGENTS.md (daemon RPC + slash-command path). Tests: 2299 passed (+1 regression test), 0 failed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Features
/plugin generate, plan inspection, lifecycle controls, and approval-aware progress reporting.Refactor
leapflow.pluginsto a first-class subsystem with unified protocols, registries, lifecycle scopes, sandboxing, and marketplace support.Enhancements
/plugin plan.Fixes
Documentation
Tests