@deepseek-ai/dsh-host-plugin-inventory 0.1.6-alpha.1 → 0.1.7-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/host/plugin-inventory/README.md
5
- README.md: b64a017df28716cfd0b5c3a41f437b87b8e4f19e
6
- README.zh.md: a29bc402537d88fa512675bfb768502753ca7929
5
+ README.md: 8450339c1430b2ed1620004343f75d9cc21a0a19
6
+ README.zh.md: 345bf367ce9fe85fdfc80994a09e3b2f26fdc0ce
package/README.md CHANGED
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
9
9
 
10
10
  ## Summary
11
11
 
12
- Clients can call `pluginInventory/list` to display the host’s current plugins in load order, including each entry’s identifier, module specifier, effective enablement, and live phase. Deployments with an agent-preset roster also report each preset’s metadata, health, and flattened plugin composition; without a roster, preset data is absent. Each response is a point-in-time, read-only snapshot for display and diagnostics: it cannot mutate plugins and provides no history, introduction source, or change subscription.
12
+ Clients can call `pluginInventory/list` to display the host’s current plugins in load order, including each entry’s identifier, module specifier, effective enablement, live phase, and available display text. Deployments with an agent-preset roster also report each preset’s metadata, health, and flattened plugin composition; without a roster, preset data is absent. Each response is a point-in-time, read-only snapshot for display and diagnostics: it cannot mutate plugins and provides no history or change subscription.
13
13
 
14
14
  ## Table of Contents
15
15
 
@@ -31,6 +31,8 @@ Call `pluginInventory/list` when a client or settings page needs to show what is
31
31
 
32
32
  Each row is one non-group Loader entry: its entry id, the exact module specifier, the effective enablement (including disabled ancestor groups), and the current root Fiber phase. `pending` means the entry waits to load, `loading` that it is being read, `active` that it is running, `failed` that its fiber rejected, and `unloading` that it is being torn down; `null` means no live root Fiber exists at all. Structural group rows are skipped.
33
33
 
34
+ Loader and preset rows can carry optional `meta` with a title, description, or metadata diagnostic. The Host returns available translations and literal fallbacks; the Client selects its language. Metadata diagnostics do not change enablement or fiber phase.
35
+
34
36
  ### Per-preset compositions
35
37
 
36
38
  With a roster composed, `agentPresets` carries one group per preset in roster order: its id, whether the deployment ships it or the user owns it (`trust`, which clients use to localize shipped names), published display name, whether a session naming no preset composes it, and flattened plugin rows — entry id (null when the file row declares none), module specifier, effective enablement, the row's own `!!js` disabled expression when it carries one, and a root-fiber phase when the composition is live. A preset some session already composed answers from its newest standing generation — even when its file has since broken, because the mount is what those sessions run; one never composed since boot answers from its composition file with disabled gates evaluated against the Loader context, and reading never mounts a preset. `conditional` enablement marks a gate the Host could not evaluate, and a broken preset nothing composed stays listed with its reason and no rows. Without a roster the field is absent.
@@ -49,7 +51,9 @@ The inventory is a snapshot for display and diagnostics: a client can render the
49
51
 
50
52
  ### Design concept
51
53
 
52
- The gateway is a direct projection with no second lifecycle truth: every `list()` call reads `ctx.loader.entries()` and maps each non-group entry to its public row. Cordis's internal `plugin/status` events already maintain `Entry.fiber` and `Fiber.state`, so a cache would only add another lifecycle truth to keep synchronized. The agent-preset roster is an optional peer resolved per call through `ctx.get('agentPresets')`: its `compositionInventory()` owns every preset read, and this package only maps root-fiber states onto the public phase vocabulary.
54
+ The gateway is a direct projection with no second lifecycle truth: every `list()` call reads `ctx.loader.entries()` and maps each non-group entry to its public row. Cordis's internal `plugin/status` events already maintain `Entry.fiber` and `Fiber.state`, so a cache would only add another lifecycle truth to keep synchronized. The agent-preset roster is an optional peer resolved per call through `ctx.get('agentPresets')`: its `compositionInventory()` owns preset composition reads, and this package maps root-fiber states onto the public phases.
55
+
56
+ Display metadata comes from the optional `pluginPackages` service using each Loader tree's resolution base, or the gateway context's base for preset rows. Without the service or the applicable base, `meta` is absent. Reading metadata does not load or activate plugins.
53
57
 
54
58
  ### The phase mapping
55
59
 
@@ -97,8 +101,8 @@ None; this package neither assembles nor sends a provider request.
97
101
  These limits define what a point-in-time inventory cannot tell a client. They are current package constraints, not a task backlog.
98
102
 
99
103
  - **Point-in-time state only** — the result contains no durable failure history or subscription; a missing root Fiber is reported as `null`, regardless of why no live root exists.
100
- - **No introduction source or mutation** — the service does not identify which bundle, profile, or override introduced an entry, and it cannot enable, disable, add, or remove plugins in either plane.
101
- - **Presets appear only with a roster** — a deployment without `dsh-agent-presets` serves Loader entries alone; the `agentPresets` field is absent rather than empty.
104
+ - **No layer attribution or mutation** — the service does not identify which bundle, profile, or override introduced an entry, and it cannot enable, disable, add, or remove plugins in either plane.
105
+ - **Presets appear only with a roster** — a deployment without `dsh-agent-preset-registry` serves Loader entries alone; the `agentPresets` field is absent rather than empty.
102
106
 
103
107
  <a id="dev-note"></a>
104
108
  ### Dev Note
package/README.zh.md CHANGED
@@ -9,7 +9,7 @@ kind: "package-reference"
9
9
 
10
10
  ## 概述
11
11
 
12
- 客户端可以调用 `pluginInventory/list`,按加载顺序展示宿主的当前插件,包括每个条目的标识符、模块标识、有效启用状态与存活阶段。部署组合了 agent preset roster 时,还会报告各预设的元数据、健康状态与压平后的插件组合;没有 roster 时,预设数据缺席。每次响应都是供展示和诊断使用的只读即时快照:它不能修改插件,也不提供历史、来源信息或变更订阅。
12
+ 客户端可以调用 `pluginInventory/list`,按加载顺序展示宿主的当前插件,包括每个条目的标识符、模块标识、有效启用状态、存活阶段与可用的展示文本。部署组合了 agent preset roster 时,还会报告各预设的元数据、健康状态与压平后的插件组合;没有 roster 时,预设数据缺席。每次响应都是供展示和诊断使用的只读即时快照:它不能修改插件,也不提供历史或变更订阅。
13
13
 
14
14
  ## 目录
15
15
 
@@ -31,6 +31,8 @@ kind: "package-reference"
31
31
 
32
32
  每一行是一个非组 Loader 条目:其条目 id、精确模块标识、有效启用状态(含被禁用的祖先组)与当前根 Fiber 阶段。`pending` 表示条目等待加载,`loading` 表示正在读取,`active` 表示正在运行,`failed` 表示其 fiber 被拒绝,`unloading` 表示正在拆除;`null` 表示完全不存在存活的根 Fiber。结构性的 group 行会被跳过。
33
33
 
34
+ Loader 行与预设行可以携带可选的 `meta`,其中包含标题、描述或元信息诊断。Host 返回可用翻译与字面回退文本,由 Client 选择语言。元信息诊断不改变启停状态或 fiber 阶段。
35
+
34
36
  ### 每个预设的组合
35
37
 
36
38
  组合了 roster 时,`agentPresets` 按 roster 顺序携带每个预设一组:其 id、随部署内置还是用户自建(`trust`,客户端据此本地化内置预设名)、发布的显示名、未指名预设的会话是否组合它,以及压平后的插件行——条目 id(文件行未声明时为 null)、模块标识、有效启用状态、行自带的 `!!js` disabled 表达式(如有),以及组合存活时的根 Fiber 阶段。已有会话组合过的预设由其最新仍存续的世代作答——即使其文件事后损坏也是如此,因为挂载才是这些会话实际运行的组合;开机以来从未被组合的预设由其组合文件作答,disabled 门用 Loader 上下文求值,且读取从不挂载预设。`conditional` 表示宿主无法求值的门;无人组合的坏预设保留在列表中,携带原因且没有行。没有 roster 时该字段缺席。
@@ -49,7 +51,9 @@ kind: "package-reference"
49
51
 
50
52
  ### 设计理念
51
53
 
52
- 网关是一层没有第二个生命周期真源的直接投影:每次 `list()` 调用都读取 `ctx.loader.entries()`,并把每个非组条目映射为公共行。Cordis 内部的 `plugin/status` 事件已经维护了 `Entry.fiber` 与 `Fiber.state`,因此再加缓存只会多出一个需要同步的生命周期真源。agent preset roster 是每次调用经 `ctx.get('agentPresets')` 解析的可选伙伴:所有预设读取都由它的 `compositionInventory()` 负责,本包只把根 Fiber 状态映射到公共阶段词汇。
54
+ 网关是一层没有第二个生命周期真源的直接投影:每次 `list()` 调用都读取 `ctx.loader.entries()`,并把每个非组条目映射为公共行。Cordis 内部的 `plugin/status` 事件已经维护了 `Entry.fiber` 与 `Fiber.state`,因此再加缓存只会多出一个需要同步的生命周期真源。agent preset roster 是每次调用经 `ctx.get('agentPresets')` 解析的可选伙伴:预设组合读取由它的 `compositionInventory()` 负责,本包把根 Fiber 状态映射到公共阶段。
55
+
56
+ 展示元信息来自可选的 `pluginPackages` 服务:Loader 行使用其所属树的解析基准,预设行使用网关上下文的基准。服务或对应基准不存在时,`meta` 缺席。读取元信息不会加载或激活插件。
53
57
 
54
58
  ### 阶段映射
55
59
 
@@ -97,8 +101,8 @@ Typert 生成由 `./typert` 与 `./remote` 导出的 Host 和 Client Remote 产
97
101
  这些限制说明即时清单无法向客户端提供哪些信息。它们是当前包约束,不是任务积压。
98
102
 
99
103
  - **仅表示调用当下**——结果不包含持久的失败历史或订阅;只要不存在存活的根 Fiber,就会报告 `null`,而不区分其原因。
100
- - **无来源与修改能力**——服务不识别条目由哪个 bundle、profile 或 override 引入,也不能在任一平面启用、停用、添加或移除插件。
101
- - **预设仅随 roster 出现**——未装 `dsh-agent-presets` 的部署只提供 Loader 条目;`agentPresets` 字段缺席而非为空。
104
+ - **不标识引入层,也不修改插件**——服务不识别条目由哪个 bundle、profile 或 override 引入,也不能在任一平面启用、停用、添加或移除插件。
105
+ - **预设仅随 roster 出现**——未装 `dsh-agent-preset-registry` 的部署只提供 Loader 条目;`agentPresets` 字段缺席而非为空。
102
106
 
103
107
  <a id="dev-note"></a>
104
108
  ### 开发备注
package/lib/index.js CHANGED
@@ -39,7 +39,11 @@ var __esDecorate = function(ctor, descriptorIn, decorators, contextIn, initializ
39
39
  if (target) Object.defineProperty(target, contextIn.name, descriptor);
40
40
  done = true;
41
41
  };
42
- /** Brand an existing Loader-tree entry id at the owning boundary. */
42
+ /**
43
+ * Brand an existing Loader-tree entry id at the owning boundary.
44
+ * @param value - the entry id as the Loader tree spells it.
45
+ * @returns the same id as the inventory's branded entry id.
46
+ */
43
47
  function pluginEntryId(value) {
44
48
  return value;
45
49
  }
@@ -102,34 +106,54 @@ let PluginInventoryGateway = (() => {
102
106
  * preset's composition rows, because those rows — not the Loader's own
103
107
  * entries — are where a deployment that mounts the roster runs its
104
108
  * model-facing plugins.
105
- * @returns Current non-group Loader entries in Loader order, with per-preset
106
- * compositions when a roster is composed.
109
+ * @returns Current non-group Loader entries in Loader order, with optional display metadata
110
+ * and per-preset compositions when a roster is composed.
107
111
  */
108
112
  async list() {
109
- const entries = [];
110
- for (const entry of this.ctx.loader.entries()) {
111
- if (entry.options.group) continue;
112
- entries.push({
113
- entryId: pluginEntryId(entry.id),
114
- moduleName: entry.options.name,
115
- enabled: !entry.disabled,
116
- fiberPhase: entry.fiber === void 0 ? null : FIBER_PHASE[entry.fiber.state]
117
- });
118
- }
119
- const presets = this.ctx.get("agentPresets");
120
- if (presets === void 0) return { entries };
121
- return {
122
- entries,
123
- agentPresets: (await presets.compositionInventory()).map((composition) => ({
124
- ...composition,
125
- rows: composition.rows.map(({ fiberState, ...row }) => ({
126
- ...row,
127
- fiberPhase: fiberState === void 0 ? null : FIBER_PHASE[fiberState]
128
- }))
129
- }))
130
- };
113
+ return readPluginInventory(this.ctx);
131
114
  }
132
115
  };
133
116
  })();
117
+ /** Read current Loader entries and optional preset compositions.
118
+ * @param ctx Context with the Loader service.
119
+ * @returns Current inventory with optional display metadata and no separate runtime cache.
120
+ */
121
+ async function readPluginInventory(ctx) {
122
+ const entries = [];
123
+ const packages = ctx.get("pluginPackages");
124
+ for (const entry of ctx.loader.entries()) {
125
+ if (entry.options.group) continue;
126
+ const base = entry.parent.tree.ctx.baseUrl;
127
+ const meta = base === void 0 ? void 0 : packages?.metaOf(entry.options.name, base);
128
+ entries.push({
129
+ entryId: pluginEntryId(entry.id),
130
+ moduleName: entry.options.name,
131
+ enabled: !entry.disabled,
132
+ fiberPhase: entry.fiber === void 0 ? null : FIBER_PHASE[entry.fiber.state],
133
+ ...meta === void 0 ? {} : { meta }
134
+ });
135
+ }
136
+ const presets = ctx.get("agentPresets");
137
+ const management = ctx.get("pluginManager") === void 0 ? {} : { managementAvailable: true };
138
+ if (presets === void 0) return {
139
+ entries,
140
+ ...management
141
+ };
142
+ return {
143
+ entries,
144
+ agentPresets: (await presets.compositionInventory()).map((composition) => ({
145
+ ...composition,
146
+ rows: composition.rows.map(({ fiberState, ...row }) => {
147
+ const meta = ctx.baseUrl === void 0 ? void 0 : packages?.metaOf(row.moduleName, ctx.baseUrl);
148
+ return {
149
+ ...row,
150
+ fiberPhase: fiberState === void 0 ? null : FIBER_PHASE[fiberState],
151
+ ...meta === void 0 ? {} : { meta }
152
+ };
153
+ })
154
+ })),
155
+ ...management
156
+ };
157
+ }
134
158
  //#endregion
135
- export { PluginInventoryGateway, PluginInventoryGateway as default };
159
+ export { PluginInventoryGateway, PluginInventoryGateway as default, pluginEntryId, readPluginInventory };
@@ -1,28 +1,49 @@
1
1
  /* Generated by @deepseek-ai/dsh-typert-generator from FaceModel — do not edit. */
2
2
  import { z } from 'zod'
3
3
 
4
- const _deepseek_ai_dsh_host_plugin_inventory_pluginInventory_list_result$schema = z.object({
4
+ let _deepseek_ai_dsh_host_plugin_inventory_pluginInventory_list_result$schema$value
5
+ const _deepseek_ai_dsh_host_plugin_inventory_pluginInventory_list_result$schema = () => (_deepseek_ai_dsh_host_plugin_inventory_pluginInventory_list_result$schema$value ??= z.object({
6
+ 'managementAvailable': z.boolean().readonly().optional(),
5
7
  'entries': z.array(z.object({
6
8
  'entryId': z.intersection(z.string(), z.unknown()).readonly(),
7
9
  'moduleName': z.string().readonly(),
10
+ 'meta': z.object({
11
+ 'title': z.union([z.string(), z.intersection(z.object({
12
+ 'en': z.string().readonly(),
13
+ }), z.record(z.string(), z.string()).readonly())]).readonly().optional(),
14
+ 'description': z.union([z.string(), z.intersection(z.object({
15
+ 'en': z.string().readonly(),
16
+ }), z.record(z.string(), z.string()).readonly())]).readonly().optional(),
17
+ 'icon': z.string().readonly().optional(),
18
+ 'error': z.string().readonly().optional(),
19
+ }).readonly().optional(),
8
20
  'enabled': z.boolean().readonly(),
9
- 'fiberPhase': z.union([z.literal(null), z.literal("pending"), z.literal("active"), z.literal("failed"), z.literal("loading"), z.literal("unloading")]).readonly(),
21
+ 'fiberPhase': z.union([z.literal(null), z.literal("failed"), z.literal("pending"), z.literal("active"), z.literal("loading"), z.literal("unloading")]).readonly(),
10
22
  })).readonly(),
11
23
  'agentPresets': z.array(z.object({
12
24
  'id': z.string().readonly(),
13
- 'trust': z.union([z.literal("system"), z.literal("user")]).readonly(),
14
25
  'name': z.string().readonly().optional(),
15
26
  'isDefault': z.boolean().readonly(),
16
27
  'broken': z.string().readonly().optional(),
17
28
  'rows': z.array(z.object({
18
29
  'entryId': z.union([z.literal(null), z.string()]).readonly(),
19
30
  'moduleName': z.string().readonly(),
31
+ 'meta': z.object({
32
+ 'title': z.union([z.string(), z.intersection(z.object({
33
+ 'en': z.string().readonly(),
34
+ }), z.record(z.string(), z.string()).readonly())]).readonly().optional(),
35
+ 'description': z.union([z.string(), z.intersection(z.object({
36
+ 'en': z.string().readonly(),
37
+ }), z.record(z.string(), z.string()).readonly())]).readonly().optional(),
38
+ 'icon': z.string().readonly().optional(),
39
+ 'error': z.string().readonly().optional(),
40
+ }).readonly().optional(),
20
41
  'enabled': z.union([z.literal(false), z.literal(true), z.literal("conditional")]).readonly(),
21
42
  'condition': z.string().readonly().optional(),
22
- 'fiberPhase': z.union([z.literal(null), z.literal("pending"), z.literal("active"), z.literal("failed"), z.literal("loading"), z.literal("unloading")]).readonly(),
43
+ 'fiberPhase': z.union([z.literal(null), z.literal("failed"), z.literal("pending"), z.literal("active"), z.literal("loading"), z.literal("unloading")]).readonly(),
23
44
  })).readonly(),
24
45
  })).readonly().optional(),
25
- })
46
+ }))
26
47
 
27
48
  export const TYPERT = {
28
49
  package: '@deepseek-ai/dsh-host-plugin-inventory',
@@ -41,9 +62,9 @@ export const TYPERT = {
41
62
  result: {
42
63
  mode: 'strict',
43
64
  typeSymbol: '@deepseek-ai/dsh-host-plugin-inventory/types#PluginInventorySnapshot',
44
- schema: _deepseek_ai_dsh_host_plugin_inventory_pluginInventory_list_result$schema,
65
+ create: _deepseek_ai_dsh_host_plugin_inventory_pluginInventory_list_result$schema,
45
66
  },
46
- sourceLocation: {"file":"packages/host/plugin-inventory/src/index.ts","line":66,"column":9},
67
+ sourceLocation: {"file":"packages/host/plugin-inventory/src/index.ts","line":71,"column":9},
47
68
  },
48
69
  ],
49
70
  model: {
@@ -1,28 +1,49 @@
1
1
  /* Generated by @deepseek-ai/dsh-typert-generator from the Host FaceModel — do not edit. */
2
2
  import { z } from 'zod'
3
3
 
4
- const _deepseek_ai_dsh_host_plugin_inventory_pluginInventory_list_result$schema = z.object({
4
+ let _deepseek_ai_dsh_host_plugin_inventory_pluginInventory_list_result$schema$value
5
+ const _deepseek_ai_dsh_host_plugin_inventory_pluginInventory_list_result$schema = () => (_deepseek_ai_dsh_host_plugin_inventory_pluginInventory_list_result$schema$value ??= z.object({
6
+ 'managementAvailable': z.boolean().readonly().optional(),
5
7
  'entries': z.array(z.object({
6
8
  'entryId': z.intersection(z.string(), z.unknown()).readonly(),
7
9
  'moduleName': z.string().readonly(),
10
+ 'meta': z.object({
11
+ 'title': z.union([z.string(), z.intersection(z.object({
12
+ 'en': z.string().readonly(),
13
+ }), z.record(z.string(), z.string()).readonly())]).readonly().optional(),
14
+ 'description': z.union([z.string(), z.intersection(z.object({
15
+ 'en': z.string().readonly(),
16
+ }), z.record(z.string(), z.string()).readonly())]).readonly().optional(),
17
+ 'icon': z.string().readonly().optional(),
18
+ 'error': z.string().readonly().optional(),
19
+ }).readonly().optional(),
8
20
  'enabled': z.boolean().readonly(),
9
- 'fiberPhase': z.union([z.literal(null), z.literal("pending"), z.literal("active"), z.literal("failed"), z.literal("loading"), z.literal("unloading")]).readonly(),
21
+ 'fiberPhase': z.union([z.literal(null), z.literal("failed"), z.literal("pending"), z.literal("active"), z.literal("loading"), z.literal("unloading")]).readonly(),
10
22
  })).readonly(),
11
23
  'agentPresets': z.array(z.object({
12
24
  'id': z.string().readonly(),
13
- 'trust': z.union([z.literal("system"), z.literal("user")]).readonly(),
14
25
  'name': z.string().readonly().optional(),
15
26
  'isDefault': z.boolean().readonly(),
16
27
  'broken': z.string().readonly().optional(),
17
28
  'rows': z.array(z.object({
18
29
  'entryId': z.union([z.literal(null), z.string()]).readonly(),
19
30
  'moduleName': z.string().readonly(),
31
+ 'meta': z.object({
32
+ 'title': z.union([z.string(), z.intersection(z.object({
33
+ 'en': z.string().readonly(),
34
+ }), z.record(z.string(), z.string()).readonly())]).readonly().optional(),
35
+ 'description': z.union([z.string(), z.intersection(z.object({
36
+ 'en': z.string().readonly(),
37
+ }), z.record(z.string(), z.string()).readonly())]).readonly().optional(),
38
+ 'icon': z.string().readonly().optional(),
39
+ 'error': z.string().readonly().optional(),
40
+ }).readonly().optional(),
20
41
  'enabled': z.union([z.literal(false), z.literal(true), z.literal("conditional")]).readonly(),
21
42
  'condition': z.string().readonly().optional(),
22
- 'fiberPhase': z.union([z.literal(null), z.literal("pending"), z.literal("active"), z.literal("failed"), z.literal("loading"), z.literal("unloading")]).readonly(),
43
+ 'fiberPhase': z.union([z.literal(null), z.literal("failed"), z.literal("pending"), z.literal("active"), z.literal("loading"), z.literal("unloading")]).readonly(),
23
44
  })).readonly(),
24
45
  })).readonly().optional(),
25
- })
46
+ }))
26
47
 
27
48
  export const TYPERT_REMOTE = {
28
49
  package: '@deepseek-ai/dsh-host-plugin-inventory',
@@ -38,9 +59,9 @@ export const TYPERT_REMOTE = {
38
59
  result: {
39
60
  mode: 'strict',
40
61
  typeSymbol: '@deepseek-ai/dsh-host-plugin-inventory/types#PluginInventorySnapshot',
41
- schema: _deepseek_ai_dsh_host_plugin_inventory_pluginInventory_list_result$schema,
62
+ create: _deepseek_ai_dsh_host_plugin_inventory_pluginInventory_list_result$schema,
42
63
  },
43
- sourceLocation: {"file":"packages/host/plugin-inventory/src/index.ts","line":66,"column":9},
64
+ sourceLocation: {"file":"packages/host/plugin-inventory/src/index.ts","line":71,"column":9},
44
65
  },
45
66
  ],
46
67
  }
@@ -1,8 +1,14 @@
1
1
  /** Read-only projection of the current Cordis Loader plugin entries. */
2
2
  import type { Context } from '@deepseek-ai/cordis';
3
3
  import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
4
- import type { PluginInventorySnapshot } from './types.ts';
4
+ import type { PluginEntryId, PluginInventorySnapshot } from './types.ts';
5
5
  export type * from './types.ts';
6
+ /**
7
+ * Brand an existing Loader-tree entry id at the owning boundary.
8
+ * @param value - the entry id as the Loader tree spells it.
9
+ * @returns the same id as the inventory's branded entry id.
10
+ */
11
+ export declare function pluginEntryId(value: string): PluginEntryId;
6
12
  /** Remote-only service exposing the Loader's current non-group entry state. */
7
13
  export declare class PluginInventoryGateway extends TypertRemoteService {
8
14
  static inject: string[];
@@ -16,10 +22,15 @@ export declare class PluginInventoryGateway extends TypertRemoteService {
16
22
  * preset's composition rows, because those rows — not the Loader's own
17
23
  * entries — are where a deployment that mounts the roster runs its
18
24
  * model-facing plugins.
19
- * @returns Current non-group Loader entries in Loader order, with per-preset
20
- * compositions when a roster is composed.
25
+ * @returns Current non-group Loader entries in Loader order, with optional display metadata
26
+ * and per-preset compositions when a roster is composed.
21
27
  */
22
28
  list(): Promise<PluginInventorySnapshot>;
23
29
  }
24
30
  export default PluginInventoryGateway;
31
+ /** Read current Loader entries and optional preset compositions.
32
+ * @param ctx Context with the Loader service.
33
+ * @returns Current inventory with optional display metadata and no separate runtime cache.
34
+ */
35
+ export declare function readPluginInventory(ctx: Context): Promise<PluginInventorySnapshot>;
25
36
  //# sourceMappingURL=index.d.ts.map
@@ -34,8 +34,12 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
34
34
  done = true;
35
35
  };
36
36
  import { TypertRemoteService, Remote } from '@deepseek-ai/dsh-typert-protocol';
37
- /** Brand an existing Loader-tree entry id at the owning boundary. */
38
- function pluginEntryId(value) {
37
+ /**
38
+ * Brand an existing Loader-tree entry id at the owning boundary.
39
+ * @param value - the entry id as the Loader tree spells it.
40
+ * @returns the same id as the inventory's branded entry id.
41
+ */
42
+ export function pluginEntryId(value) {
39
43
  return value;
40
44
  }
41
45
  /** Runtime mirror: FiberState is a cross-package const enum. */
@@ -82,35 +86,51 @@ let PluginInventoryGateway = (() => {
82
86
  * preset's composition rows, because those rows — not the Loader's own
83
87
  * entries — are where a deployment that mounts the roster runs its
84
88
  * model-facing plugins.
85
- * @returns Current non-group Loader entries in Loader order, with per-preset
86
- * compositions when a roster is composed.
89
+ * @returns Current non-group Loader entries in Loader order, with optional display metadata
90
+ * and per-preset compositions when a roster is composed.
87
91
  */
88
92
  async list() {
89
- const entries = [];
90
- for (const entry of this.ctx.loader.entries()) {
91
- if (entry.options.group)
92
- continue;
93
- entries.push({
94
- entryId: pluginEntryId(entry.id),
95
- moduleName: entry.options.name,
96
- enabled: !entry.disabled,
97
- fiberPhase: entry.fiber === undefined ? null : FIBER_PHASE[entry.fiber.state],
98
- });
99
- }
100
- const presets = this.ctx.get('agentPresets');
101
- if (presets === undefined)
102
- return { entries };
103
- const agentPresets = (await presets.compositionInventory()).map(composition => ({
104
- ...composition,
105
- rows: composition.rows.map(({ fiberState, ...row }) => ({
106
- ...row,
107
- fiberPhase: fiberState === undefined ? null : FIBER_PHASE[fiberState],
108
- })),
109
- }));
110
- return { entries, agentPresets };
93
+ return readPluginInventory(this.ctx);
111
94
  }
112
95
  };
113
96
  })();
114
97
  export { PluginInventoryGateway };
115
98
  export default PluginInventoryGateway;
99
+ /** Read current Loader entries and optional preset compositions.
100
+ * @param ctx Context with the Loader service.
101
+ * @returns Current inventory with optional display metadata and no separate runtime cache.
102
+ */
103
+ export async function readPluginInventory(ctx) {
104
+ const entries = [];
105
+ const packages = ctx.get('pluginPackages');
106
+ for (const entry of ctx.loader.entries()) {
107
+ if (entry.options.group)
108
+ continue;
109
+ const base = entry.parent.tree.ctx.baseUrl;
110
+ const meta = base === undefined ? undefined : packages?.metaOf(entry.options.name, base);
111
+ entries.push({
112
+ entryId: pluginEntryId(entry.id),
113
+ moduleName: entry.options.name,
114
+ enabled: !entry.disabled,
115
+ fiberPhase: entry.fiber === undefined ? null : FIBER_PHASE[entry.fiber.state],
116
+ ...meta === undefined ? {} : { meta },
117
+ });
118
+ }
119
+ const presets = ctx.get('agentPresets');
120
+ const management = ctx.get('pluginManager') === undefined ? {} : { managementAvailable: true };
121
+ if (presets === undefined)
122
+ return { entries, ...management };
123
+ const agentPresets = (await presets.compositionInventory()).map(composition => ({
124
+ ...composition,
125
+ rows: composition.rows.map(({ fiberState, ...row }) => {
126
+ const meta = ctx.baseUrl === undefined ? undefined : packages?.metaOf(row.moduleName, ctx.baseUrl);
127
+ return {
128
+ ...row,
129
+ fiberPhase: fiberState === undefined ? null : FIBER_PHASE[fiberState],
130
+ ...meta === undefined ? {} : { meta },
131
+ };
132
+ }),
133
+ }));
134
+ return { entries, agentPresets, ...management };
135
+ }
116
136
  //# sourceMappingURL=index.js.map
@@ -1,4 +1,5 @@
1
1
  import type { Branded } from '@deepseek-ai/dsh-brand';
2
+ import type { PluginLocalizedMeta } from '@deepseek-ai/dsh-package-manifest';
2
3
  /** Stable Loader-tree identity of one configured plugin entry. */
3
4
  export type PluginEntryId = Branded<'PluginEntryId'>;
4
5
  /** Lifecycle state of an entry's root Fiber, or null when it has no live root Fiber. */
@@ -8,6 +9,8 @@ export interface PluginInventoryEntry {
8
9
  readonly entryId: PluginEntryId;
9
10
  /** Exact module specifier imported by the Loader entry. */
10
11
  readonly moduleName: string;
12
+ /** Local package display metadata, independent of whether the entry is enabled. */
13
+ readonly meta?: PluginLocalizedMeta;
11
14
  /** Effective Loader enablement, including disabled ancestor groups. */
12
15
  readonly enabled: boolean;
13
16
  readonly fiberPhase: PluginFiberPhase;
@@ -20,6 +23,8 @@ export interface AgentPresetPluginRow {
20
23
  readonly entryId: string | null;
21
24
  /** Module specifier the row names. */
22
25
  readonly moduleName: string;
26
+ /** Local package display metadata, independent of whether the preset is mounted. */
27
+ readonly meta?: PluginLocalizedMeta;
23
28
  /**
24
29
  * Effective enablement, including disabled ancestor groups. `'conditional'`
25
30
  * marks a `!!js` disabled expression on a composition no session has
@@ -35,8 +40,6 @@ export interface AgentPresetPluginRow {
35
40
  export interface AgentPresetPluginGroup {
36
41
  /** Stable preset id. */
37
42
  readonly id: string;
38
- /** Whether the deployment ships the preset or the user owns it. */
39
- readonly trust: 'system' | 'user';
40
43
  /** Display name the preset published; a reader falls back to the id. */
41
44
  readonly name?: string;
42
45
  /** Whether a session naming no preset composes this one. */
@@ -48,6 +51,8 @@ export interface AgentPresetPluginGroup {
48
51
  }
49
52
  /** Point-in-time inventory returned by the plugin inventory Remote. */
50
53
  export interface PluginInventorySnapshot {
54
+ /** Whether this Host exposes persistent current-profile management. */
55
+ readonly managementAvailable?: boolean;
51
56
  readonly entries: readonly PluginInventoryEntry[];
52
57
  /**
53
58
  * Per-preset compositions, present only when an agent-preset roster is
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-host-plugin-inventory",
3
3
  "description": "Read-only Remote projection of current Cordis Loader plugin state",
4
- "version": "0.1.6-alpha.1",
4
+ "version": "0.1.7-alpha.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -47,22 +47,24 @@
47
47
  "zod": "^4.4.3"
48
48
  },
49
49
  "peerDependencies": {
50
- "@deepseek-ai/cordis-plugin-loader": "^1.0.3",
51
- "@deepseek-ai/dsh-agent-presets": "^0.1.6-alpha.1",
52
- "@deepseek-ai/dsh-brand": "^0.1.6-alpha.1",
53
- "@deepseek-ai/dsh-typert-protocol": "^0.1.6-alpha.1",
54
- "@deepseek-ai/cordis": "^4.0.2"
50
+ "@deepseek-ai/cordis-plugin-loader": "^1.0.4",
51
+ "@deepseek-ai/dsh-agent-preset-registry": "^0.1.7-alpha.1",
52
+ "@deepseek-ai/dsh-brand": "^0.1.7-alpha.1",
53
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.7-alpha.1",
54
+ "@deepseek-ai/cordis": "^4.0.3"
55
55
  },
56
56
  "peerDependenciesMeta": {
57
- "@deepseek-ai/dsh-agent-presets": {
57
+ "@deepseek-ai/dsh-agent-preset-registry": {
58
58
  "optional": true
59
59
  }
60
60
  },
61
61
  "devDependencies": {
62
- "@deepseek-ai/cordis-plugin-loader": "^1.0.3",
63
- "@deepseek-ai/dsh-agent-presets": "^0.1.6-alpha.1",
64
- "@deepseek-ai/dsh-brand": "^0.1.6-alpha.1",
65
- "@deepseek-ai/dsh-typert-protocol": "^0.1.6-alpha.1",
66
- "@deepseek-ai/cordis": "^4.0.2"
62
+ "@deepseek-ai/dsh-app-boot": "^0.1.7-alpha.1",
63
+ "@deepseek-ai/dsh-package-manifest": "^0.1.7-alpha.1",
64
+ "@deepseek-ai/cordis-plugin-loader": "^1.0.4",
65
+ "@deepseek-ai/dsh-agent-preset-registry": "^0.1.7-alpha.1",
66
+ "@deepseek-ai/dsh-brand": "^0.1.7-alpha.1",
67
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.7-alpha.1",
68
+ "@deepseek-ai/cordis": "^4.0.3"
67
69
  }
68
70
  }