Skip to content

Commit dad3108

Browse files
committed
feat: support hierachical modules*.json
1 parent 0c26822 commit dad3108

7 files changed

Lines changed: 586 additions & 4 deletions

File tree

frontend/omni/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ dist/
33
src/modules/external-*
44

55
public/modules.json
6+
public/modules*.json
7+
!public/modules_browser_only.json
8+
!public/modules_with_backend.json

frontend/omni/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- i18n support via `i18next` with browser language detection (supports English fallback and German). Translations are module-scoped: each module with user-facing strings has its own `locales/de.json`. New `src/modules/i18n/` module provides the `getT(namespace)` helper used by all components.
1313
- Tools can now be toggled directly from the chat input panel via a wrench-icon popover — no separate Tools page needed.
1414
- External frontend modules under `src/modules/external-*` can now carry their own npm dependencies through pnpm workspace package discovery, without requiring edits to the root `package.json`.
15+
- Manifest `includes` support: the root `modules.json` can now declare an `includes` array to compose the module list from multiple JSON files. Included files are merged left-to-right; the root manifest always wins. Per-module `collisionStrategy` (`merge` | `replace` | `drop`) controls collision behaviour, mirroring the backend YAML config loader.
1516

1617
### Changed
1718

1819
- Switch frontend to Svelte
19-
- Updated tools fetching and chat tool handling to support `/api/tools` responses as a raw list of function tools (instead of requiring a `{ tools: [...] }` envelope); legacy envelope parsing remains supported for compatibility.
20+
- Updated tools fetching and chat tool handling to support `/api/tools` responses as a raw list of function tools (instead of requiring a `{ tools: [...] }` envelope)
2021
- ChatInputPanel redesigned: textarea, model selector, tools selector, and send button are now visually inside a single input box (ai-sdk.dev/examples/chatbot style).
2122
- Removed the Tools sidebar navigation item and `/tools` route from all module configurations.
2223
- Made the chat tools selector popover scrollable with a viewport-constrained height so long tool lists remain fully accessible.

frontend/omni/docs/architecture/core.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,52 @@ To activate a module, add it to `modules*.json`.
115115

116116
> **Auto-discovery**: The module registry automatically discovers all `.svelte` and `.svelte.ts` files under `src/modules/**` via Vite's `import.meta.glob`. No manual TypeScript registry entry is required.
117117
118+
#### `modules*.json` — top-level structure and includes
119+
120+
A manifest file has the following top-level shape:
121+
122+
```json
123+
{
124+
"version": "1.0.0",
125+
"includes": [{ "path": "base.json" }],
126+
"modules": [...]
127+
}
128+
```
129+
130+
- **version**: manifest format version (currently `"1.0.0"`)
131+
- **includes** _(optional, root manifest only)_: list of other manifest files to merge in before this file's own modules. Each entry is an object with a `path` field. Relative paths are resolved relative to the current manifest. Nested includes (an included file itself containing `includes`) are not supported and throw an error.
132+
- **modules**: array of module entries (see below)
133+
134+
**Load order** — mirrors the backend YAML config loader:
135+
1. Included files are fetched and applied left-to-right; later includes win on collision.
136+
2. The root manifest's own `modules` are applied last and always win.
137+
138+
**`collisionStrategy`** on a module entry controls what happens when that module's `id` already exists from an earlier include:
139+
- `"merge"` *(default)* — deep-merges `config`; incoming wins on shared keys. `dependencies` is shallow-merged; incoming wins on key collision.
140+
- `"replace"` — incoming entry fully replaces the existing one.
141+
- `"drop"` — removes the existing entry; does not add the incoming one either.
142+
143+
`collisionStrategy` is stripped from the resolved manifest before it is used.
144+
145+
**Example** — composing a deployment-specific manifest from a base:
146+
147+
```json
148+
{
149+
"version": "1.0.0",
150+
"includes": [{ "path": "modules_with_backend.json" }],
151+
"modules": [
152+
{
153+
"id": "fetch-service",
154+
"path": "@/modules/fetch-service/sessionFetchService/create",
155+
"collisionStrategy": "replace",
156+
"dependencies": {
157+
"module:sessionService": "session-service"
158+
}
159+
}
160+
]
161+
}
162+
```
163+
118164
#### `modules*.json` — regular module entry
119165

120166
```json

frontend/omni/src/core/module-system/ModulesProvider.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { setContext, untrack } from "svelte";
44
import { ComponentResolver } from "./componentResolver";
55
import { MODULES_KEY, type Modules } from "./index";
66
import { resolveManifestDependencies } from "./manifestDependencyResolver";
7-
import { fetchManifestJson } from "./manifestJson";
7+
import { resolveManifest } from "./manifestJson";
88
import { ActiveModulesImpl } from "./module";
99
1010
interface Props {
@@ -28,7 +28,7 @@ const modules: Modules = {
2828
setContext(MODULES_KEY, modules);
2929
3030
// untrack: manifest path is intentionally captured once at mount time
31-
const ready = fetchManifestJson(untrack(() => manifestPath)).then(
31+
const ready = resolveManifest(untrack(() => manifestPath)).then(
3232
async (json) => {
3333
const activeEntries = resolveManifestDependencies(json.modules, []);
3434
const componentResolver =

0 commit comments

Comments
 (0)