diff --git a/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.i18n.yaml b/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.i18n.yaml index 07a35fa473..db91c82d4c 100644 --- a/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.i18n.yaml +++ b/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.md -2026-08-03-per-session-agent-presets.md: ee6303e5f52234d7eaf6768043c92a15e4e5399f -2026-08-03-per-session-agent-presets.zh.md: 5a2e1c3d8d0564f06c75785e1080b53740751355 +2026-08-03-per-session-agent-presets.md: dbe4188fe16a01b51f01cddf8a2387471e5e00a9 +2026-08-03-per-session-agent-presets.zh.md: aa7792a7db3a686b74f19ace130ad8f7b6d2feb6 diff --git a/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.md b/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.md index ee6303e5f5..dbe4188fe1 100644 --- a/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.md +++ b/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.md @@ -29,6 +29,8 @@ Mounting is per-session by default. Measured cost for a twelve-row composition i **A directly-plugged subtree is invisible to the boot audit.** It never links itself to an `Entry`, so it is absent from `ctx.loader.entries()` and `assertEntriesActivated` cannot see it. The mount audits its own rows instead, reading the tree through an `Include` subclass that publishes it. +**A preset can only name a group because the app registers one.** Sharing a realm across rows is a `cordis:group` row, and a preset living outside this workspace — the authored ones under the Harness home, which is the point — cannot resolve `@cordisjs/plugin-group` by name: Node's upward `node_modules` walk never reaches the harness from there. `boot()` therefore registers `cordis:group` beside `cordis:include` as a loader builtin, so both load through the ambient module pipeline rather than through the included tree's own specifier resolution. Without it the `isolate` vocabulary above is expressible one row at a time only, and a provider could never be grouped with its consumers. + **A preset may not publish into the root service realm.** Such a service is process-global rather than per-session, so the second session mounting the same preset collides with the first — and the collision surfaces as an unhandled rejection that `setup` never observes, leaving a half-composed agent that looks healthy. The mount rejects it instead, and the package invariant re-checks on every service notification because a row publishing from a timer or an asynchronous continuation would escape a one-shot audit. **Failure rolls the agent back.** `setup` runs before publication, so a rejected mount fails `ctx.agents.create()` and leaves nothing behind. This is why `setup` is the one supported call site. diff --git a/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.zh.md b/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.zh.md index 5a2e1c3d8d..aa7792a7db 100644 --- a/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.zh.md +++ b/.agents/notes/implemented/architecture/2026-08-03-per-session-agent-presets.zh.md @@ -29,6 +29,8 @@ Status: implemented **直接挂载的子树对启动审计不可见。** 它不会把自己关联到 `Entry`,因此不在 `ctx.loader.entries()` 中,`assertEntriesActivated` 也看不到它。改由挂载过程自行校验各行,通过一个会公开自身 tree 的 `Include` 子类读取。 +**preset 能写出 group,是因为 app 注册了它。** 跨行共享 realm 就是一个 `cordis:group` 行,而住在本工作区之外的 preset——也就是 Harness home 下由人或 agent 创作的那些,正是这套设计的目的——无法按名字解析 `@cordisjs/plugin-group`:Node 向上查找 `node_modules` 的路径从那里永远走不到 harness。因此 `boot()` 把 `cordis:group` 与 `cordis:include` 并排注册为 loader builtin,两者都经由环境模块管线加载,而不依赖被包含树自身的说明符解析。没有它,上文那套 `isolate` 词汇就只能一行一行地表达,提供方也永远无法与它的消费方归入同一组。 + **preset 不得把服务发布进根 realm。** 这类服务是进程级全局而非按会话的,因此第二个挂载同一 preset 的会话会与第一个相撞——而这次相撞表现为 `setup` 永远观察不到的未处理 rejection,留下一个看起来健康、实则组装到一半的 agent。挂载改为直接拒绝它;本包的运行时不变量还会在每次服务通知时复查,因为从定时器或异步续体中发布的行会绕过一次性审计。 **失败会让 agent 回滚。** `setup` 在发布之前运行,因此挂载被拒绝会让 `ctx.agents.create()` 失败且不留残留。这正是 `setup` 是唯一受支持调用点的原因。 diff --git a/packages/preset/agent-presets/README.i18n.yaml b/packages/preset/agent-presets/README.i18n.yaml index b27e89ec7b..da74ba925a 100644 --- a/packages/preset/agent-presets/README.i18n.yaml +++ b/packages/preset/agent-presets/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/preset/agent-presets/README.md -README.md: 5d66c23f24717d1c30a9e729c7b0528715692d41 -README.zh.md: 9cdcd8b11a8a37d6d789c393e9a1500e4fbfd503 +README.md: e53a52f145b3f66ab115b93c614561fed8194719 +README.zh.md: 39b470002816f309c8ec9af1d72e04495b020ad7 diff --git a/packages/preset/agent-presets/README.md b/packages/preset/agent-presets/README.md index 5d66c23f24..e53a52f145 100644 --- a/packages/preset/agent-presets/README.md +++ b/packages/preset/agent-presets/README.md @@ -58,5 +58,4 @@ Prefix-stable for the life of an agent: a composition is installed once, before - **A preset cannot be changed on a live agent** — the mount happens once during creation, so switching a running session's composition would mean unwinding its subtree mid-turn, dropping tools the model may already have called. Changing the default affects only sessions created afterwards. - **Display names are the directory id** — a preset carries no manifest, so pickers and settings surfaces show the id until a consumer needs richer metadata. -- **`isolate` realms cannot be expressed across rows without `cordis:group`** — an entry-local realm works on a single row, but grouping a provider with its consumers under one shared realm needs the group builtin, which `dsh-app-boot` does not register. - **Root scans are not watched** — every read hits the filesystem instead, which keeps the roster fresh but puts one `readdir` per root on each `list()`. diff --git a/packages/preset/agent-presets/README.zh.md b/packages/preset/agent-presets/README.zh.md index 9cdcd8b11a..39b4700028 100644 --- a/packages/preset/agent-presets/README.zh.md +++ b/packages/preset/agent-presets/README.zh.md @@ -58,5 +58,4 @@ Indirectly, through the plugins a mounted composition registers, which own every - **无法在存活的 agent 上更换 preset** —— 挂载只在创建时发生一次,因此切换运行中会话的组装意味着要在轮次进行途中卸载其子树,抽走模型可能已经调用的工具。更改默认值只影响此后创建的会话。 - **展示名称就是目录 id** —— preset 不携带 manifest,因此选择器与设置界面在有消费方需要更丰富的元数据之前,只显示 id。 -- **跨多行的 `isolate` realm 需要 `cordis:group` 才能表达** —— 单行可用 entry 本地 realm,但要把一个提供方与它的消费方归入同一个共享 realm,需要 group 内建插件,而 `dsh-app-boot` 并未注册它。 - **根目录扫描不做监听** —— 每次读取都实际访问文件系统,这让名单保持新鲜,但每次 `list()` 会对每个根目录产生一次 `readdir`。 diff --git a/packages/ui/app-boot/README.i18n.yaml b/packages/ui/app-boot/README.i18n.yaml index 398ec6e923..9ff7452391 100644 --- a/packages/ui/app-boot/README.i18n.yaml +++ b/packages/ui/app-boot/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/ui/app-boot/README.md -README.md: cdd78047b6ad71148c6ebeba598b63b4ae4cfa7b -README.zh.md: ee2b07884e68510e2b59b9f2c27053c263d15f1a +README.md: 56df424b44564d6e8f6ccd0a61c258dacf9748d3 +README.zh.md: ddd8103e849bc6798b056cae92c88e5f84551108 diff --git a/packages/ui/app-boot/README.md b/packages/ui/app-boot/README.md index cdd78047b6..56df424b44 100644 --- a/packages/ui/app-boot/README.md +++ b/packages/ui/app-boot/README.md @@ -14,7 +14,7 @@ Shared boot glue for the app bins ([`dsh`](../../../apps/cli/README.md), [`dsh-c | `assertEntriesActivated(ctx, binName)` | Include the `assertEntriesLoaded` check, then await every enabled entry after the Loader settles; throw with each failed plugin's original stack or each pending plugin's unresolved services | | `loadOptionalPatches(binName, file)` | Parse an optional patch-list file (a profile's `cordis.patch.yml`) — a top-level YAML array of include `PatchOptions` (id-targeted config overrides, `insert` lists, `!!js` allowed); absent file → `undefined`, an unreadable/unparsable/non-array file throws | | `loadOverlayPatches(binName, file)` | Parse a required patch-list file with the same shape; a missing file also throws, because the caller named it | -| `mountRootInclude(ctx, absoluteConfigPath, patches?)` | Mount the statically imported Include builtin and retain the exact root entry used by user patch-layer HMR | +| `mountRootInclude(ctx, absoluteConfigPath, patches?)` | Register the statically imported `cordis:include` and `cordis:group` builtins, mount the include, and retain the exact root entry used by user patch-layer HMR | | `watchUserPatches(ctx, options)` | Register the named patch file with the existing Cordis HMR service; each add/change/removal transactionally recomposes the full patch list through the caller's `compose` closure (app-owned layers around the current user layer) and returns an async disposer | | `resolveProfileDir` / `initProfile` / `loadProfile` / `readProfileManifest` / `writeProfileManifest` / `resolveBundleDir` / `composeEntries` / `healProfilesModuleFallback` / `PROFILE_TEMPLATES` / `DEFAULT_PROFILE_BUNDLES` / `PROFILES_DIR` / `PROFILE_PATCH_FILENAME` | Profile machinery (see [Profiles](#profiles)) | | `boot(binName, absoluteConfigPath, patches?, prepare?)` | Create the root context, expose `dshHomePath(...segments)` to Loader `!!js` config expressions, install Loader, run optional host preparation before config-tree entries mount (`prepare` may use Loader and provide launcher-owned context slots), then mount and await the include tree, assert entries loaded and activated, and return the root context — or dispose the partial context and reject a labelled error | @@ -26,6 +26,8 @@ Loader settlement rejects import and lifecycle failures with the failing entry a The Loader mounts entries concurrently, so a surface can already own the terminal when something else fails: exiting without the tree's own teardown would leave raw mode, bracketed paste, and the keyboard protocol set on the user's shell, and an in-flight terminal query's reply would land as literal text at the next prompt. A config-tree failure settles through `boot()`, whose disposal of the partial context runs the surface's own shutdown before the labelled rejection. For the rejections `boot()` cannot see — a plugin's detached async work rejecting during or after mounting — a terminal-owning bin passes `release` to dispose the tree before the exit commits; `dsh` captures the root context in `boot()`'s `prepare` hook rather than from its return value so the hook covers the whole mounting window. While a release is in flight the handler stays installed and latched: the first rejection is the reported one, and later rejections (teardown's own included) are swallowed rather than becoming uncaught and killing the process mid-teardown. +`cordis:group` is registered beside `cordis:include` so a composition can give one `isolate` realm to a provider and its consumers together. Both load through the ambient module pipeline rather than the included tree's own specifier resolution, which is what lets a composition outside this workspace — an agent preset under the Harness home — use a group row at all. + Bare plugin specifiers in a config (`@deepseek-ai/dsh-*`, npm packages) resolve through the Cordis Loader's internal module loader. Repository bins install Loader's optional `node-addon-require-builtin` peer; external callers must supply it or install plugins where plain Node import resolution can find them. Relative specifiers resolve against the config directory without the native helper. The built `dsh-app-boot` artifact embeds the statically mounted Include implementation while leaving Loader external, so the include tree and host bind to one Loader peer. The `dsh` source launcher additionally maps manifest-declared workspace packages to their TypeScript source; its configuration gate requires every shipped raw/Web bare plugin to appear in the resolver manifest's `dependencies`. This package carries no loader hooks and no dev-mode surface. The [`dsh` app](../../../apps/cli/README.md) owns its Node source-launch hook and consumes these helpers for the boot sequence; built consumers continue to use plain Node package resolution. diff --git a/packages/ui/app-boot/README.zh.md b/packages/ui/app-boot/README.zh.md index ee2b07884e..ddd8103e84 100644 --- a/packages/ui/app-boot/README.zh.md +++ b/packages/ui/app-boot/README.zh.md @@ -14,7 +14,7 @@ | `assertEntriesActivated(ctx, binName)` | 先执行 `assertEntriesLoaded` 检查,再在 Loader 结算后等待每个已启用配置项;抛出的错误包含每个失败插件的原始错误堆栈,或每个等待中插件尚未解析的服务 | | `loadOptionalPatches(binName, file)` | 解析一份可选的 patch 列表文件(即 profile 的 `cordis.patch.yml`):其顶层是一个 YAML 数组,内容为 include 的 `PatchOptions`(按 id 定位的配置覆盖、`insert` 列表,允许 `!!js`);文件不存在时返回 `undefined`,文件不可读、不可解析或内容不是数组时抛出异常 | | `loadOverlayPatches(binName, file)` | 解析一份形状相同的必需 patch 列表文件;文件缺失同样抛出异常,因为该文件是调用方指名的 | -| `mountRootInclude(ctx, absoluteConfigPath, patches?)` | 挂载静态导入的 Include builtin,并保留用户 patch 层 HMR(热模块替换)使用的确切根配置项 | +| `mountRootInclude(ctx, absoluteConfigPath, patches?)` | 注册静态导入的 `cordis:include` 与 `cordis:group` builtin,挂载 include,并保留用户 patch 层 HMR(热模块替换)使用的确切根配置项 | | `watchUserPatches(ctx, options)` | 向现有 Cordis HMR 服务注册指名的 patch 文件;每次新增、变更或移除都会通过调用方的 `compose` 闭包(应用自有层围绕当前用户层)以事务方式重新组合完整 patch 列表,并返回异步 disposer | | `resolveProfileDir` / `initProfile` / `loadProfile` / `readProfileManifest` / `writeProfileManifest` / `resolveBundleDir` / `composeEntries` / `healProfilesModuleFallback` / `PROFILE_TEMPLATES` / `DEFAULT_PROFILE_BUNDLES` / `PROFILES_DIR` / `PROFILE_PATCH_FILENAME` | Profile 机制(见 [Profile](#profiles)) | | `boot(binName, absoluteConfigPath, patches?, prepare?)` | 创建根上下文,向 Loader `!!js` 配置表达式暴露 `dshHomePath(...segments)` 并安装 Loader,在配置树条目挂载前执行可选的宿主准备操作(`prepare` 可以使用 Loader,也可以提供由启动器拥有的上下文插槽),再挂载并等待 include 树结算,断言所有条目均已加载并激活,最后返回根上下文——失败时 dispose(资源释放)部分构造的上下文,并以带标签的错误 reject | @@ -26,6 +26,8 @@ Loader 结算会在导入或生命周期失败时 reject,并携带失败的配 Loader 并发挂载各个条目,因此当其他环节失败时,某个界面可能已经持有终端:此时不经过整棵树自身的拆卸就退出,会把 raw 模式、bracketed paste 和键盘协议残留在用户的 shell 上,而尚未返回的终端查询响应会在下一个提示符处显示为字面文本。配置树失败会经 `boot()` 结算:它先释放部分构建的上下文(从而执行该界面自身的 shutdown),再抛出带标签的 rejection。对于 `boot()` 看不到的 rejection(插件游离的异步工作在挂载期间或挂载完成后失败),持有终端的 bin 会传入 `release`,在提交退出前释放整棵树;`dsh` 在 `boot()` 的 `prepare` 回调中捕获根上下文,而不是取其返回值,使该回调覆盖整个挂载窗口。release 执行期间处理函数保持注册并加闩:被报告的始终是第一个 rejection,后续 rejection(包括拆卸自身的)会被吞掉,而不会变成未捕获错误、在拆卸中途杀死进程。 +`cordis:group` 与 `cordis:include` 一并注册,使一份组装能把一个提供方与它的消费方放进同一个 `isolate` realm。两者都通过宿主的模块管线加载,而非被包含树自身的说明符解析,这正是让本工作区之外的组装——放在 Harness home 下的 agent preset——能够使用 group 行的原因。 + 配置中的裸插件 specifier(`@deepseek-ai/dsh-*`、npm 包(package))通过 Cordis Loader 的内部模块 loader 解析。仓库 bin 会安装 Loader 的可选 peer `node-addon-require-builtin`;外部调用方必须提供该组件,或者把插件安装到普通 Node import 解析可以找到的位置。相对 specifier 无需原生 helper,并以配置目录为基准解析。构建后的 `dsh-app-boot` 产物内嵌静态挂载的 Include 实现,但仍将 Loader 保持为外部依赖,因此 include 树与 host 会绑定到同一个 Loader peer。`dsh` 源码启动器还会将 manifest(元数据清单)声明的 workspace 包映射到其 TypeScript 源码;其配置门禁要求每个已交付的原始/Web 裸插件都出现在解析所用 manifest 的 `dependencies` 中。 此包不包含 loader 钩子,也不提供开发模式接口。[`dsh` 应用](../../../apps/cli/README.md)持有自己的 Node 源码启动钩子,并在启动序列中使用这些 helper;构建后的消费方仍使用普通 Node 包解析。 diff --git a/packages/ui/app-boot/package.json b/packages/ui/app-boot/package.json index c1214953a5..1766ec765b 100644 --- a/packages/ui/app-boot/package.json +++ b/packages/ui/app-boot/package.json @@ -28,6 +28,7 @@ "js-yaml": "^4.2.0" }, "peerDependencies": { + "@cordisjs/plugin-group": "^1.0.0", "@cordisjs/plugin-hmr": "^1.0.15", "@cordisjs/plugin-include": "^1.0.4", "@cordisjs/plugin-loader": "^1.0.0-rc.5", @@ -42,6 +43,7 @@ } }, "devDependencies": { + "@cordisjs/plugin-group": "workspace:^", "@cordisjs/plugin-hmr": "workspace:^", "@cordisjs/plugin-include": "workspace:^", "@cordisjs/plugin-loader": "workspace:^", diff --git a/packages/ui/app-boot/src/index.ts b/packages/ui/app-boot/src/index.ts index e14b249f5c..73f1900ef5 100644 --- a/packages/ui/app-boot/src/index.ts +++ b/packages/ui/app-boot/src/index.ts @@ -13,6 +13,7 @@ import * as yaml from 'js-yaml' import { Context, type FiberState } from 'cordis' import Loader, { type Entry, type EntryOptions } from '@cordisjs/plugin-loader' import Include, { applyEntryPatches, entryListSchema, type PatchOptions } from '@cordisjs/plugin-include' +import Group from '@cordisjs/plugin-group' import { dshHomePath } from '@deepseek-ai/dsh-paths' import type {} from '@cordisjs/plugin-hmr' // Side-effect type import: resolves `ctx.get('systemPrompt')` to the service. @@ -376,6 +377,12 @@ export async function mountRootInclude( patches: readonly PatchOptions[] = [], ): Promise { ctx.loader.builtins.include = Include + // `cordis:group` alongside it: a group row is how a composition gives one + // `isolate` realm to a provider and its consumers together, and an agent + // preset living outside this workspace cannot resolve `@cordisjs/plugin-group` + // by name. Both builtins load through the ambient module pipeline, so neither + // depends on the included tree's own specifier resolution. + ctx.loader.builtins.group = Group // Pinned id: the bootstrap include is app glue, not a config row, and its // id appears in Loader failure chains — a random id would make startup // diagnostics unstable across runs (and snapshot fixtures). diff --git a/packages/ui/app-boot/tests/config-reload.spec.ts b/packages/ui/app-boot/tests/config-reload.spec.ts index 45eab9ea7d..9cbe3d1a58 100644 --- a/packages/ui/app-boot/tests/config-reload.spec.ts +++ b/packages/ui/app-boot/tests/config-reload.spec.ts @@ -8,9 +8,8 @@ import { mkdtempSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' import { describe, expect, it } from 'vitest' -import type { Context } from 'cordis' +import { Context } from 'cordis' import type { Include } from '@cordisjs/plugin-include' -import { Group } from '@cordisjs/plugin-loader' import { boot } from '../src/index.ts' const NAME = 'dsh-test-bin' @@ -219,8 +218,10 @@ describe('loader tree replacement', () => { }) it('stops and restores descendants when an ancestor group is disabled and re-enabled', async () => { + // No manual builtin registration: `boot()` supplies `cordis:group` beside + // `cordis:include`, which is what lets a composition give one `isolate` + // realm to a provider and its consumers together. const { ctx, dir, include } = await bootTree('- id: noop\n name: ./noop.mjs\n') - ctx.loader.builtins.group = Group try { const config = (disabled: boolean) => [ '- id: parent', @@ -253,7 +254,6 @@ describe('loader tree replacement', () => { const { ctx } = await bootTree('- id: noop\n name: ./noop.mjs\n', { 'movable.mjs': plugin('movablePlugin', 'if (config.fail) throw new Error("candidate config failed")'), }) - ctx.loader.builtins.group = Group try { const groupId = await ctx.loader.create({ name: 'cordis:group', group: true, config: [] }) const targetId = await ctx.loader.create({ name: './movable.mjs', config: { fail: false } }) @@ -386,3 +386,46 @@ describe('include patches layered over one base', () => { } }) }) + +describe('shipped builtins', () => { + it('lets a booted composition share one isolate realm across a group of rows', async () => { + // The reason `boot()` registers `cordis:group`: a composition — notably an + // agent preset living outside this workspace, which cannot resolve + // `@cordisjs/plugin-group` by name — gives a provider and its consumer one + // named realm so the service stays out of the root realm while remaining + // visible to the rows that need it. + const { ctx } = await bootTree([ + '- id: realm', + ' name: cordis:group', + ' isolate:', + ' demoRealmSvc: true', + ' config:', + ' - id: provider', + ' name: ./provider.mjs', + ' - id: consumer', + ' name: ./consumer.mjs', + '', + ].join('\n'), { + 'provider.mjs': 'export const name = "provider"\n' + + 'export function apply(ctx) { ctx.effect(() => ctx.reflect.provide("demoRealmSvc", { tag: "realm" })) }\n', + 'consumer.mjs': 'export const name = "consumer"\n' + + 'export const inject = ["demoRealmSvc"]\n' + + 'export function apply(ctx) { globalThis.__REALM_SEEN__ = ctx.get("demoRealmSvc").tag }\n', + }) + try { + expect((globalThis as { __REALM_SEEN__?: string }).__REALM_SEEN__).toBe('realm') + // `provide` mints the root symbol unconditionally (cordis `reflect.ts`), + // so the name IS in the root realm — pinned here because it is the half + // that looks like the claim and is not. The claim is the other half: no + // implementation is stored under that symbol, so the root realm cannot + // resolve the service and a second composition mounting the same rows + // cannot collide with this one. + const rootKey = ctx.root[Context.isolate].demoRealmSvc + expect(rootKey).toBeDefined() + expect(ctx.reflect.store[rootKey!]).toBeUndefined() + } finally { + delete (globalThis as { __REALM_SEEN__?: string }).__REALM_SEEN__ + await ctx.fiber.dispose() + } + }) +}) diff --git a/packages/ui/app-boot/tsconfig.json b/packages/ui/app-boot/tsconfig.json index beb61317dc..9ec64c31fc 100644 --- a/packages/ui/app-boot/tsconfig.json +++ b/packages/ui/app-boot/tsconfig.json @@ -17,6 +17,9 @@ { "path": "../../../vendor/include" }, + { + "path": "../../../vendor/group" + }, { "path": "../../../vendor/hmr" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f18d43bbc8..a612f4af93 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5975,6 +5975,9 @@ importers: specifier: ^4.2.0 version: 4.2.0 devDependencies: + '@cordisjs/plugin-group': + specifier: workspace:^ + version: link:../../../vendor/group '@cordisjs/plugin-hmr': specifier: workspace:^ version: link:../../../vendor/hmr @@ -6570,6 +6573,9 @@ importers: python/sdk-runtime: dependencies: + '@cordisjs/plugin-group': + specifier: workspace:^ + version: link:../../vendor/group '@cordisjs/plugin-include': specifier: workspace:^ version: link:../../vendor/include diff --git a/python/sdk-runtime/package.json b/python/sdk-runtime/package.json index 4fd7291633..450f7d5333 100644 --- a/python/sdk-runtime/package.json +++ b/python/sdk-runtime/package.json @@ -5,6 +5,7 @@ "private": true, "type": "module", "dependencies": { + "@cordisjs/plugin-group": "workspace:^", "@cordisjs/plugin-include": "workspace:^", "@cordisjs/plugin-loader": "workspace:^", "@cordisjs/plugin-timer": "workspace:^",