Skip to content

feature(customware): add customware bundle interface#44

Open
Magaav wants to merge 1 commit intoagent0ai:mainfrom
Magaav:feature/customware-bundle-interface
Open

feature(customware): add customware bundle interface#44
Magaav wants to merge 1 commit intoagent0ai:mainfrom
Magaav:feature/customware-bundle-interface

Conversation

@Magaav
Copy link
Copy Markdown

@Magaav Magaav commented Apr 29, 2026

Summary

This PR adds a small Customware Bundle Interface on top of Space Agent's existing customware/module system.

Bundles are ordinary installed L1 or L2 modules that include a root space.bundle.yaml manifest. The manifest lets downstream teams describe reusable customizations without monkey-patching core runtime files.

The implementation keeps the mechanics native to Space Agent:

  • module discovery stays under L1/<group>/mod/<author>/<repo>/ and L2/<user>/mod/<author>/<repo>/
  • UI still composes through ext/html
  • behavior still composes through ext/js and space.extend(...)
  • skills still compose through ext/skills
  • theme/background customization uses a documented framework head seam
  • executable browser actions register through a removable browser-side registry
  • external bridge state sync uses named runtime sync handlers

Hermes is the motivating downstream use case, but this PR intentionally keeps all Hermes-specific code out of Space Agent core.

Motivation

Space Agent's customware model is already a strong base for downstream extension: modules, layered L1/L2 roots, /mod/... delivery, ext/html, ext/js, space.extend(...), and ext/skills.

What is missing is a small, explicit package contract that lets a downstream team say:

"This installed module is a reusable customization bundle. Here is what it provides, which seams it uses, which Space Agent versions it expects, and which actions it registers."

Without that contract, downstream integrations tend to drift toward fragile runtime injection or private-file patches. Those are hard to rebase when Space Agent moves quickly, and they make useful third-party work harder to upstream.

This PR turns those patches into documented seams.

Before

Downstream teams that wanted a reusable customization usually had to manage loose module files and out-of-band knowledge:

  • install a module under L1 or L2
  • remember which ext/html or ext/js files it contributes
  • inject browser behavior by reaching into runtime globals or feature internals
  • patch landing/theme behavior by editing core shell or framework files
  • manually track which commands/actions belong to the customization
  • remove files by hand and hope no runtime state remains

That works locally, but it is brittle across core updates.

After

A customization can be shipped as a normal customware module with one manifest:

L1/<group>/mod/<author>/<repo>/space.bundle.yaml
L2/<user>/mod/<author>/<repo>/space.bundle.yaml

Example:

id: acme/fleet
name: Fleet Control
version: 1.0.0
description: Team-owned fleet controls for Space Agent.
capabilities:
  - theme
  - actions
  - browser-runtime
extension_points:
  - _core/framework/theme/end
  - _core/framework/head/end
compatibility:
  space_agent: ">=0.65"
config_defaults:
  accent: teal
actions:
  - id: fleet.open
    title: Open fleet
    capability: actions
    description: Open the fleet dashboard.

The bundle is visible through module/bundle listing APIs, and browser code can register removable actions:

const dispose = space.bundles.actions.register({
  bundleId: "acme/fleet",
  id: "fleet.open",
  title: "Open fleet",
  async run(payload) {
    return payload;
  }
});

When the owning view unmounts, it can call dispose(). Removing the module removes the manifest, extension files, skills, and action registrations after reload or unmount.

Implementation

This PR adds:

  • server/lib/customware/bundles.js

    • parses and normalizes space.bundle.yaml
    • validates bundle ids
    • normalizes capabilities, extension points, config defaults, compatibility, and action metadata
  • server/lib/customware/module_manage.js

    • attaches bundle metadata to module list/info results
    • adds listInstalledBundles(...)
    • adds readBundleInfo(...)
  • server/api/bundle_list.js

    • exposes visible installed bundle manifests through the existing module visibility model
  • server/api/bundle_info.js

    • exposes one module's bundle metadata
  • app/L0/_all/mod/_core/framework/js/bundles.js

    • publishes browser-side space.bundles
    • provides list(...) and info(...)
    • provides space.bundles.actions for removable action handlers
    • provides space.bundles.bridge for external bridge-state sync
  • app/L0/_all/mod/_core/framework/js/extensions.js

    • adds _core/framework/theme/end
    • keeps _core/framework/head/end
    • both are mounted in document.head before extension scanning
  • app/L0/_all/mod/_core/framework/js/api-client.js

    • adds space.api.bundleList(...)
    • adds space.api.bundleInfo(...)
  • app/space-runtime.d.ts

    • documents the new browser/runtime types
  • docs and skills

    • README
    • AGENTS files for touched ownership boundaries
    • documentation module pages
    • development skills
  • tests

    • tests/customware_bundle_test.mjs
    • tests/fixtures/customware_bundle_example/

What This Does Not Do

This PR does not add a separate plugin loader.

It does not allow arbitrary runtime patching.

It does not add Hermes-specific code to Space Agent.

It does not replace ext/html, ext/js, space.extend(...), or ext/skills. It gives those existing seams a package-level manifest and a small browser action/bridge registry.

Why This Fits Space Agent

This follows the existing architecture instead of introducing a foreign plugin system:

  • browser-first
  • customware-native
  • deterministic discovery
  • layered permissions
  • removable modules
  • documented extension seams
  • no private runtime injection

The bundle interface should help any downstream team package reusable customizations while preserving Space Agent's current module model.

Test Plan

Ran:

node --check server/lib/customware/bundles.js
node --check server/lib/customware/module_manage.js
node --check server/api/bundle_list.js
node --check server/api/bundle_info.js
node --check app/L0/_all/mod/_core/framework/js/bundles.js
node --check app/L0/_all/mod/_core/framework/js/api-client.js
node --check app/L0/_all/mod/_core/framework/js/extensions.js
node --check app/L0/_all/mod/_core/framework/js/runtime.js
node --check tests/customware_bundle_test.mjs
node --test tests/customware_bundle_test.mjs
node --test tests/module_discovery_state_test.mjs tests/extensions_load_request_shape_test.mjs
node --test tests/yaml_lite_test.mjs
git diff --check

Coverage includes:

  • Space Agent boots unchanged when no bundles are installed
  • installed module manifests are discovered as bundles
  • module list/info payloads include bundle metadata
  • invalid manifests report structured errors instead of crashing discovery
  • bundle-provided action metadata is normalized
  • browser action handlers register, run, and unregister
  • bridge sync handlers register, run, and unregister
  • extension loading request shape remains unchanged
  • module discovery across L1/L2 remains unchanged

Review Notes

The main thing to review is the public shape:

  • manifest field names
  • API response shape
  • space.bundles.actions contract
  • space.bundles.bridge contract
  • whether _core/framework/theme/end is the right stable seam name for theme/background customization

I kept this intentionally modest so it can be refined without locking Space Agent into a large plugin framework.

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