mirror of
https://github.com/lobehub/lobehub.git
synced 2026-09-20 04:56:13 +08:00
1e8767eb2a
There are two Mecha layers doing the same job against different data sources. The browser assembles an agent's effective run config in `src/services/chat/mecha` (~2300 lines including its context builder); the server assembles the same thing in `apps/server/src/modules/Mecha` plus an 851-line `serverCallLlmContextBuilder` that inlines the resolution alongside reads of six database models. Both wrap the same shared `MessagesEngine`, and the primitives underneath (`getActivePluginIds`, `ToolResolver`, `resolveTopicReferences`) were extracted long ago — only the orchestration above them was left duplicated. Spot-checked rather than inferred from size: both sides independently compute `enabledToolIds`, `enabledManifests` and the merged `systemRole` from an agent config, producing the same shape from different sources. This moves the browser's resolution rules into `@lobechat/mecha` unchanged, behind an `AgentConfigSnapshot` of everything they previously read from Zustand. The browser now gathers that snapshot — 76 lines — and delegates. Nothing else changes: the barrel re-exports the same names, so no call site moved, and `getTargetAgentId` stays behind because it reads the active agent. The package takes the name both hosts already use for this layer, because the rest of it follows: context engineering, tool composition and model-parameter resolution are duplicated the same way and belong in the same home. A resolver is the first inhabitant, not the whole tenancy. It is a package of its own rather than part of `@lobechat/agent-runtime` because the runtime keeps exactly three runtime dependencies — everything heavy is type-only — and that is what let the CLI adopt it. These rules need `builtin-agents`, which alone pulls in eight builtin-tool packages. Folding them in would invert the property that makes the runtime portable. The runtime also never resolves config; it reads `state.metadata.agentConfig` as an opaque cast for a single field, so resolution belongs to the host that starts it. `canManage` is an input rather than something the rules compute. The browser answers it from the cache its picker populates and the server from `isResourceAuthorOrAdmin`, which is asynchronous — a seam that only works if the decision arrives already made. Behaviour is pinned by the existing suite: all 77 of `agentConfigResolver.test.ts` pass untouched, and they mock the stores, so they exercise the new gathering and the extracted rules end to end. The package also has its own suite that builds the snapshot by hand and never mocks a store, so a store read creeping back in fails there even while the client tests still pass. The server and a device adopt this in later PRs — one host at a time, so a regression stays attributable. For the device the payoff is the point: its context port stops being an 851-line problem and becomes a snapshot to fill. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
20 lines
618 B
JSON
20 lines
618 B
JSON
{
|
|
"name": "@lobechat/mecha",
|
|
"version": "0.0.1",
|
|
"description": "Host-agnostic Mecha layer: resolving an agent's effective run configuration and the context it runs on",
|
|
"main": "src/index.ts",
|
|
"scripts": {
|
|
"test": "vitest",
|
|
"test:coverage": "vitest --coverage --silent='passed-only'"
|
|
},
|
|
"dependencies": {
|
|
"@lobechat/builtin-agents": "workspace:*",
|
|
"@lobechat/builtin-tool-page-agent": "workspace:*",
|
|
"@lobechat/builtin-tool-task": "workspace:*",
|
|
"@lobechat/context-engine": "workspace:*",
|
|
"@lobechat/types": "workspace:*",
|
|
"debug": "^4.4.3",
|
|
"immer": "^10.1.3"
|
|
}
|
|
}
|