relay-dsh-plugin-manager 0.2.6 → 0.3.0

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/CHANGELOG.md CHANGED
@@ -2,6 +2,36 @@
2
2
 
3
3
  All notable changes to this project are documented here.
4
4
 
5
+ ## Unreleased
6
+
7
+ ## [0.3.0] - 2026-09-11
8
+
9
+ ### Changed
10
+
11
+ - Combine the bounded Registry keyword pool with semantic-directory routing,
12
+ while preserving exact identifiers, rejecting snapshot mismatch, and safely
13
+ falling back when either read-only route fails.
14
+ - Replace fixed-count presentation guidance with a smallest-complete-answer
15
+ contract: cover every distinct solution role and materially different
16
+ approach, group alternatives, and omit unrelated or duplicate projects.
17
+ - Add the read-only `search_roles` and `assess_solution` workflow. The Agent
18
+ supplies a bounded role decomposition, the manager searches each role through
19
+ the inspected provider path, and a short-lived draft becomes complete only
20
+ after reviewed selections cover every required role with no ambiguity.
21
+
22
+ ### Added
23
+
24
+ - Add a versioned bilingual search acceptance suite that compares keyword and
25
+ hybrid results for required-role coverage, reviewed false positives,
26
+ duplicate project identities, and exact package/repository queries. Recall
27
+ and reciprocal-rank values remain diagnostic rather than release gates.
28
+ - Add a versioned task-solution fixture covering required versus optional
29
+ roles, incomplete and ambiguous tasks, shared multi-role plugins, distinct
30
+ alternatives, invalid cross-role selections, and draft expiry.
31
+ - Add an opt-in bilingual live-model role-planning acceptance command with a
32
+ strict output schema and deterministic checks for minimal role count,
33
+ semantic coverage, optionality, ambiguity, and invented near-neighbours.
34
+
5
35
  ## [0.2.6] - 2026-09-07
6
36
 
7
37
  ### Added
@@ -157,3 +187,4 @@ All notable changes to this project are documented here.
157
187
  [0.1.1]: https://github.com/yangbobo2021/relay-dsh-plugin-manager/compare/v0.1.0...v0.1.1
158
188
  [0.2.3]: https://github.com/yangbobo2021/relay-dsh-plugin-manager/compare/v0.2.2...v0.2.3
159
189
  [0.2.6]: https://github.com/yangbobo2021/relay-dsh-plugin-manager/compare/v0.2.5...v0.2.6
190
+ [0.3.0]: https://github.com/yangbobo2021/relay-dsh-plugin-manager/compare/v0.2.6...v0.3.0
package/README.md CHANGED
@@ -147,10 +147,27 @@ This provider sends only the task query, inferred Chinese/English locale, and
147
147
  result limit. Returned candidates are
148
148
  always inspected locally through the same npm/GitHub flow before any plan can be
149
149
  created; Registry descriptions are untrusted text and cannot authorize changes.
150
- Search returns one relevance-ranked page (twenty candidates by default). The
151
- Agent is instructed to preserve that order and show every possibly relevant
152
- candidate instead of silently shortening the page to a fixed top five; results
153
- whose plugin purpose is clearly unrelated to the request are excluded.
150
+ Search combines a bounded keyword pool with the active semantic directory, then
151
+ locally inspects and merges npm/GitHub aliases by project identity. The Agent
152
+ builds the smallest complete answer from the resulting pool: every distinct
153
+ responsibility and materially different solution approach is covered, related
154
+ alternatives are grouped by role, and clearly unrelated or duplicate candidates
155
+ are omitted. It neither pads the answer to twenty nor silently cuts it to an
156
+ arbitrary top count. Exact package and repository queries remain rank one.
157
+
158
+ For a task with several responsibilities, the Agent uses a two-step read-only
159
+ workflow. It first searches a minimal set of required and optional roles, then
160
+ reviews each role group and asks the manager to assess only the selected direct
161
+ solutions. The manager reports `complete` only when every required role has a
162
+ reviewed candidate and no material user choice remains unresolved. One plugin
163
+ may cover several roles without being repeated; distinct implementations remain
164
+ grouped as primary and alternatives. These drafts expire after ten minutes and
165
+ never authorize installation.
166
+
167
+ For monitoring that must survive a wait and continue the original conversation,
168
+ the Agent searches source-specific state/event reading separately from durable
169
+ waiting and Session resumption. A plugin that covers both responsibilities is
170
+ still shown only once in the assessed solution.
154
171
 
155
172
  ## Part of Relay
156
173
 
package/README.zh.md CHANGED
@@ -133,9 +133,20 @@ RELAY_PLUGIN_MANAGER_TELEMETRY_TEST=1 dsh web
133
133
  该 provider 只发送任务查询、推断出的中英文语言和结果数量。返回候选仍必须进入同一套本地 npm/GitHub
134
134
  检查流程,完成检查后才能生成计划;Registry 描述属于不可信文本,不能授权任何
135
135
  变更。
136
- 搜索返回一页按相关性排序的结果(默认二十个)。Agent 必须保持该顺序并展示所有
137
- 可能相关的候选,不能静默截成固定的前五个;插件用途明确与需求完全无关时必须
138
- 省略。
136
+ 搜索会把有限的关键词候选池与当前语义目录结合,再通过本地检查按项目身份合并
137
+ npm/GitHub 别名。Agent 从候选池中构造“最小完整答案”:覆盖任务需要的每一种职责和
138
+ 实质不同的解决思路,按角色组织可替代方案,并去掉明确无关或重复的候选。结果不会
139
+ 为了凑满二十个而填充,也不会静默截成任意固定数量;精确包名和仓库查询仍保持第一名。
140
+
141
+ 对于包含多项职责的任务,Agent 会执行两步只读流程:先拆成最少的必需/可选角色并
142
+ 分别搜索,再审查每个角色组,只把直接解决该职责的候选交给管理器评估。只有每个
143
+ 必需角色都有经过审查的候选、并且不存在未解决的重要用户选择时,结果才会标记为
144
+ `complete`。同一插件可以承担多个角色而只展示一次;实质不同的实现会按主方案和
145
+ 替代方案分组。草案十分钟后过期,也不会产生任何安装授权。
146
+
147
+ 对于需要跨越等待并继续原对话的监控任务,Agent 会分别搜索“读取特定来源的状态/事件”
148
+ 和“持久等待并恢复 Session”两项职责。如果同一个插件同时覆盖两项职责,最终方案仍只
149
+ 展示一次,不会制造重复结果。
139
150
 
140
151
  ## 与 Relay 的关系
141
152
 
package/SPEC.md CHANGED
@@ -50,8 +50,13 @@ service.
50
50
  | PM-023 | `plugin_manage confirm` owns its DSH question: it validates the token, expiry, and exact Session before asking; supplies a stable plan-specific id, visible plan detail, exact approve/decline options, and `plan-review` intent; and executes in that same tool call only for the exact single approve answer. Declines, malformed or unrelated answers, provider failure/cancellation, and cross-session attempts do not execute or consume a still-valid plan. Generic model-authored question results are never mutation authority. |
51
51
  | PM-024 | GitHub owner discovery recognizes `owner:<name>`, owner-only GitHub identities, `<name> DSH plugins`, and conservative bare identifiers containing digits. It sends a typed owner intent to providers, uses GitHub's exact owner qualifier, case-insensitively verifies returned ownership, and ranks verified owner matches first. Owner-only `inspect` fails with an actionable error directing callers to search. Every emitted repository identity is accepted by `inspect`; every recommended immutable source is accepted by `plan`. |
52
52
  | PM-025 | Register a read-only DSH Registry provider against `https://dsh-plugins.tech` by default, with an explicit `registryUrl: false` opt-out and HTTPS-only configuration override. Send only the task query, inferred Chinese/English locale and result limit; accept only source-level untrusted DiscoveryEntry records, convert their npm/GitHub descriptors into core-owned source types, and submit them to the same mandatory local inspection used by all providers. Registry responses cannot supply exact versions, approval, plans, installers, or profile state, and provider failure remains isolated. |
53
- | PM-026 | Return search candidates as one explicit, one-based relevance-ranked result page with a default and maximum size of 20. The model-facing contract requires the Agent to preserve rank order, surface every possibly relevant returned candidate, exclude candidates whose purpose is clearly unrelated, and MUST NOT silently reduce the remaining page to a fixed top-N. Ranking remains distinct from compatibility, security, or installation approval. |
53
+ | PM-026 | Return search candidates as one explicit, one-based relevance-ranked pool with a default and maximum size of 20. The model-facing contract requires the Agent to produce the smallest complete answer: cover every distinct responsibility and materially different solution approach, group alternatives by role, exclude clearly unrelated candidates, merge npm/GitHub aliases of one project, and neither pad nor silently cut the answer to a fixed count. Ranking remains distinct from compatibility, security, or installation approval. |
54
54
  | PM-027 | Send default-on anonymous operational telemetry only to the Registry's canonical first-party HTTPS endpoint under schema `1.1.0`, with explicit `RELAY_PLUGIN_MANAGER_TELEMETRY=0` or `telemetry.enabled: false` opt-out. Events are limited to manager discovery/planning type and manager-executed install start/success/failure, including per-plugin batch outcomes. An operator-only `RELAY_PLUGIN_MANAGER_TELEMETRY_TEST=1` or `telemetry.test: true` mode adds only `is_test: true`; ordinary events omit the marker. Never send raw search text, conversation content, Profile state, installed inventory, paths, credentials, command output, error text, account identity, PostHog configuration, or client timestamps. Store only a stable random anonymous ID locally, create it lazily, and never let storage, validation, network, Registry, or analytics failure affect plugin management. |
55
+ | PM-028 | Use the Registry keyword index as a bounded recall baseline and the active semantic directory as reranking evidence, with exact-identifier dominance, named-technology disambiguation, snapshot-consistency checks, and safe single-source fallback. A versioned bilingual scenario suite evaluates the post-inspection, deduplicated result pool for required-role coverage, forbidden near-neighbours, exact identifiers, and duplicate identities. Release comparison may not hide an individual hard-check regression behind aggregate improvement. |
56
+ | PM-029 | For a task with multiple responsibilities, the Agent authors the smallest mutually distinct role plan. Every role has a stable id, user-facing label, focused search query, and required/optional status; material unresolved user choices are explicit ambiguities rather than guessed defaults. The manager validates bounds, text, uniqueness, and structure before searching. |
57
+ | PM-030 | `search_roles` searches all validated roles concurrently through the normal inspected and identity-deduplicated provider path, preserves candidates in separate role groups, and creates a bounded, short-lived read-only draft. Candidate presence alone is `needs_review`, never proof of relevance or completeness. A required role with no candidate is already `incomplete`; any unresolved ambiguity is `ambiguous`. |
58
+ | PM-031 | `assess_solution` accepts only candidate identities previously returned for their declared role. The first reviewed identity is the primary and additional identities are materially different alternatives selected by the Agent. One candidate selected for multiple roles appears once in the solution set with all role ids. The result is `complete` only when every required role has a reviewed selection and no ambiguity remains; missing optional roles do not block completeness. |
59
+ | PM-032 | Treat cross-time observation as two coverage responsibilities: source-specific state/event reading and durable waiting/scheduling that resumes the original Session. The Agent keeps them as separate search roles even when one plugin may cover both, while the assessed solution still emits that shared plugin only once. An opt-in bilingual live-model suite must also preserve explicit optionality, surface material channel ambiguity, avoid invented near-neighbour roles, and produce the exact minimal role count for every reviewed case. |
55
60
 
56
61
  ## Command Grammar
57
62
 
@@ -69,15 +74,25 @@ behavior drift between direct commands and conversation.
69
74
 
70
75
  ### `plugin_discover`
71
76
 
72
- Read-only actions: `list`, `search`, `inspect`, `status`.
77
+ Read-only actions: `list`, `search`, `search_roles`, `assess_solution`, `inspect`, `status`.
73
78
 
74
79
  `search` accepts capability text or GitHub owner forms such as
75
80
  `owner:yangbobo2021`. `inspect` accepts npm sources and all three GitHub
76
81
  repository forms from PM-007. An owner without a repository belongs to
77
82
  `search`, not `inspect`. Search defaults to a twenty-candidate ranked page. Each
78
- candidate carries its explicit display rank; the Agent presents all possibly
79
- relevant returned candidates in that order instead of silently selecting an
80
- arbitrary top five.
83
+ candidate carries its explicit display rank and semantic-directory evidence
84
+ when available. The Agent builds a minimal complete answer grouped by required
85
+ role and materially different approach. It does not repeat source aliases, pad
86
+ the answer, or silently impose an arbitrary top count.
87
+
88
+ For a multi-responsibility task, `search_roles` receives an Agent-authored role
89
+ plan and returns a short-lived grouped candidate draft. The Agent reviews each
90
+ group and calls `assess_solution` with only direct solutions. Assessment rejects
91
+ unknown roles and cross-role or duplicate selections, groups primary and
92
+ alternative implementations, merges one plugin used by multiple roles, and
93
+ reports explicit required-role coverage. If a material choice remains unknown,
94
+ the draft and assessment remain `ambiguous` until the user resolves it and the
95
+ Agent creates a new plan.
81
96
 
82
97
  ### `plugin_manage`
83
98
 
@@ -97,7 +112,10 @@ The manager owns inspection, immutable resolution, deduplication, ranking,
97
112
  confirmation, installation, rollback, activation, and restart.
98
113
 
99
114
  Provider scores are not globally comparable. The manager uses them only within
100
- one provider before deterministic cross-provider ordering.
115
+ one provider before deterministic cross-provider ordering. Inside the Registry
116
+ provider, bounded reciprocal-rank fusion combines keyword order with the active
117
+ semantic-directory route; the keyword pool limits semantic drift and one
118
+ additional challenger permits a directory-supported boundary correction.
101
119
 
102
120
  The `dsh-registry` provider defaults to `https://dsh-plugins.tech`. Plugin
103
121
  configuration `registryUrl` or environment variable `DSH_PLUGIN_REGISTRY_URL`
@@ -0,0 +1,61 @@
1
+ # Live DSH Task-Solution Acceptance — 2026-09-11
2
+
3
+ This record captures A-038: the task-solution workflow running through a real
4
+ DSH Web Session, rather than a direct manager or model-only harness.
5
+
6
+ ## Frozen inputs
7
+
8
+ - Candidate: `relay-dsh-plugin-manager@0.2.6`, packed and installed into a
9
+ disposable DSH Profile.
10
+ - DSH upstream commit: `76fda729799fe9b3848dbe2c211d4b231032b81e`.
11
+ - Agent backend: `gpt-5.6-luna`, high reasoning effort, through
12
+ `relay-dsh-plugin-codex`.
13
+ - Registry export:
14
+ `discovery.composite.2026-09-06.v1-0-5.b92b985e31df`, 14,829 records.
15
+ - Semantic directory: `plugin-directory-semantic-v3-18`.
16
+ - Export file digest:
17
+ `sha256:d03f7dbe79a0a9cda3129a5945b08e5b97aebe6dd346736d3232c2a94bbe6e4a`.
18
+ - Reconstructed DiscoverySnapshot canonical-entry digest:
19
+ `sha256:62f266eaab41de08419c854d7953952c1ca2983f8ef63d9719904c3d1c0997e6`.
20
+
21
+ The two digests intentionally cover different byte representations. The first
22
+ is the frozen JSONL export; the second is the Registry contract's canonical
23
+ JSON-array digest. Snapshot id and entry count are checked before DSH starts,
24
+ and the semantic directory must reference that exact snapshot id.
25
+
26
+ ## Scenario and result
27
+
28
+ The user task was: keep watching a local program and send its result to Lark
29
+ when it exits. The live Agent had to keep process-state reading, durable
30
+ monitoring/session resumption, and Lark delivery as three distinct required
31
+ responsibilities.
32
+
33
+ The passing session made exactly two read-only calls:
34
+
35
+ 1. one `search_roles` call with three focused role queries and eight candidates
36
+ per role;
37
+ 2. one `assess_solution` call selecting one primary candidate per role.
38
+
39
+ The assessed solution was complete, with three covered required roles, no
40
+ missing roles, no alternatives, and three globally unique plugins:
41
+
42
+ - `relay-dsh-plugin-monitor-process@0.1.1` for process-state reading;
43
+ - `relay-dsh-plugin-monitors@0.3.1` for durable repeated checks and resumption;
44
+ - `dsh-lark-channel@0.0.7` for Lark delivery.
45
+
46
+ There were no failed discovery workflow calls, redundant `inspect` calls, or
47
+ `plugin_manage` calls. Four candidates in the reviewed pools carried semantic
48
+ directory evidence. Directory evidence is recorded as a metric rather than a
49
+ per-selected-plugin gate: a correct resolved candidate may be supplied by the
50
+ keyword side of hybrid retrieval when the broader directory path does not
51
+ match a highly specific role query. Aggregate directory contribution and
52
+ retrieval regression remain gated separately by A-035.
53
+
54
+ Run the acceptance with:
55
+
56
+ ```sh
57
+ npm run acceptance:live:dsh-task-solution
58
+ ```
59
+
60
+ The command is read-only outside temporary files. It does not install into the
61
+ source Profile and does not access or mutate the production Registry database.
@@ -0,0 +1,32 @@
1
+ # Search Scenario Acceptance — 2026-09-11
2
+
3
+ This record captures A-035 against the local production-sized Registry snapshot
4
+ and semantic directory. It compares the former keyword strategy with the new
5
+ hybrid candidate strategy after simulated identity inspection and project
6
+ deduplication.
7
+
8
+ - Registry origin: local read-only server at `127.0.0.1:4199`
9
+ - Discovery entries: 14,829
10
+ - Snapshot: `discovery.composite.2026-09-06.v1-0-5.b92b985e31df`
11
+ - Directory: `plugin-directory-semantic-v3-18`
12
+ - Suite: `plugin-manager-search.2026-09-11.v1`
13
+ - Evaluations: 16 scenarios in Chinese and English, plus 3 exact queries
14
+
15
+ | Gate | Keyword baseline | Hybrid candidate |
16
+ | --- | ---: | ---: |
17
+ | Scenario evaluations passed | 30 / 32 | 32 / 32 |
18
+ | Required solution roles covered | 35 / 36 | 36 / 36 |
19
+ | Reviewed forbidden neighbours suppressed | 7 / 8 | 8 / 8 |
20
+ | Duplicate project identities | 0 | 0 |
21
+ | Exact identifiers at required rank | 3 / 3 | 3 / 3 |
22
+ | Individual hard-check regressions | — | 0 |
23
+
24
+ The hybrid strategy passed every hard gate. Its recall at rank 10 was lower
25
+ (`0.888889` versus `0.944444`) because some required roles moved to ranks 11–20,
26
+ but no required role was lost from the bounded result pool. This rank metric is
27
+ therefore retained for diagnosis and is not allowed to override complete role
28
+ coverage or non-redundancy.
29
+
30
+ The run used Registry identity simulation for the large scenario comparison;
31
+ immutable npm/GitHub resolution is covered separately by unit, integration, and
32
+ live Registry smoke acceptance. No production state was changed.
@@ -0,0 +1,45 @@
1
+ # Live Task-Role Model Acceptance — 2026-09-11
2
+
3
+ This record captures A-037 for Agent-authored semantic role plans. It is
4
+ separate from the deterministic A-036 runtime workflow: passing JSON validation
5
+ or finding candidates is not accepted as proof that the plan is complete.
6
+
7
+ The checked-in suite is
8
+ `fixtures/evaluation/task-role-model-scenarios.v1.json`. Six tasks are evaluated
9
+ in Chinese and English, for twelve independent plans. The model receives only
10
+ the task text and role-planning rules; expected concepts and forbidden phrases
11
+ remain evaluator-only.
12
+
13
+ The hard checks require:
14
+
15
+ 1. The exact reviewed minimal role count.
16
+ 2. A distinct observed role for every expected semantic responsibility.
17
+ 3. Correct required versus explicitly optional status.
18
+ 4. No invented terminal or shell role for workspace file browsing.
19
+ 5. The exact ambiguity count and recognition of an unspecified notification
20
+ channel.
21
+ 6. Separate state/event-reading and durable Session-resumption coverage for
22
+ cross-time monitoring, even when one plugin can later satisfy both roles.
23
+
24
+ Negative unit cases prove that merged/missing roles, wrong optionality, missing
25
+ ambiguity, forbidden roles, and missing/duplicate/unexpected plans fail. The
26
+ semantic matcher uses one-to-one maximum matching, so one observed role cannot
27
+ claim two expected responsibilities.
28
+
29
+ An opt-in run with `gpt-5.6-luna` at high reasoning effort produced these final
30
+ results:
31
+
32
+ | Check | Result |
33
+ | --- | ---: |
34
+ | Plans | 12 / 12 |
35
+ | Exact role counts | 12 / 12 |
36
+ | Distinct role concepts | 26 / 26 |
37
+ | Required/optional flags | 26 / 26 |
38
+ | Forbidden-role checks | 6 / 6 |
39
+ | Exact ambiguity counts | 12 / 12 |
40
+ | Ambiguity concepts | 2 / 2 |
41
+
42
+ Run it with `npm run acceptance:live:task-roles`. The command uses an ephemeral,
43
+ read-only Codex execution, writes no credentials, performs no plugin search or
44
+ mutation, and only writes a full report when
45
+ `DSH_TASK_ROLE_ACCEPTANCE_OUTPUT` is explicitly set.
@@ -0,0 +1,42 @@
1
+ # Task Solution Acceptance — 2026-09-11
2
+
3
+ This record captures A-036 for the role-decomposition, grouped-search, and
4
+ completeness-assessment workflow.
5
+
6
+ The checked-in suite is
7
+ `fixtures/evaluation/task-solution-scenarios.v1.json`. It fixes six reviewed
8
+ examples:
9
+
10
+ 1. A three-role process-monitoring and Lark-delivery task is complete only after
11
+ all three required roles have a selected direct solution.
12
+ 2. Missing Lark delivery leaves the same task incomplete.
13
+ 3. Missing an explicitly optional dashboard does not block completeness; one
14
+ combined monitor may cover the state-reader and durable-resumption roles.
15
+ 4. One selected plugin can cover two roles while appearing once globally.
16
+ 5. Two materially different Lark implementations remain grouped as primary and
17
+ alternative, while an adjacent document plugin is not selected.
18
+ 6. An unresolved notification-channel choice keeps an otherwise covered task
19
+ ambiguous, while the same monitoring coverage boundary is preserved.
20
+
21
+ Negative tests reject duplicate role plans, duplicate labels or focused
22
+ queries, ambiguity ids that collide with role ids, candidates selected for the
23
+ wrong role, repeated candidate selection, and expired drafts.
24
+
25
+ The runtime guarantees are exercised at three layers:
26
+
27
+ - pure `TaskSolutionStore` scenario tests;
28
+ - `PluginManager` integration with independent per-role provider queries and
29
+ inspected candidates;
30
+ - the real DSH `ToolRuntime` schema and execution path for `search_roles`
31
+ followed by `assess_solution`.
32
+
33
+ The semantic role plan is intentionally Agent-authored: the manager does not
34
+ pretend that candidate presence proves relevance. It validates the structure,
35
+ preserves candidate boundaries, and computes completeness only from the
36
+ Agent-reviewed selections. Model-specific role-decomposition quality is
37
+ evaluated separately by A-037 so this deterministic runtime fixture remains a
38
+ release gate.
39
+
40
+ The full verification passed with 120 automated tests, package build, and local
41
+ DSH package installation against upstream commit
42
+ `76fda729799fe9b3848dbe2c211d4b231032b81e`. No production state was changed.
@@ -38,8 +38,12 @@ This matrix is the release contract. Tests name the relevant `PM-*` ids.
38
38
  | A-030 owner-only inspect forms return actionable search guidance while malformed, unsafe, and non-GitHub inputs remain rejected without weakening source validation | PM-018, PM-024 | source/manager negative unit |
39
39
  | A-031 search results feed one immutable multi-install plan through the real DSH tool and user-question registries; no mutation starts before one exact UI approval, then child installs execute serially and approval replay is rejected | PM-002, PM-005, PM-009, PM-010, PM-015, PM-023, PM-024 | combined host/tool/search/question/manager integration |
40
40
  | A-032 The default formal Registry searches at least 3,000 source records for Chinese task text, English task text, exact plugin names and an honest empty result; every returned source is locally inspected to an immutable npm version or GitHub commit, while Registry authority remains source-only, untrusted, non-recommending and non-approving | PM-005, PM-006, PM-025 | provider/unit/host integration tests plus `npm run acceptance:live:registry` |
41
- | A-033 Search defaults to a complete twenty-candidate result page, assigns stable one-based display ranks after merge and deduplication, and tells the Agent to exclude clearly unrelated purposes while surfacing every remaining possibly relevant candidate without silent fixed-count truncation | PM-005, PM-026 | search orchestration and conversation-tool contract unit tests plus Agent answer acceptance |
41
+ | A-033 Search defaults to a bounded twenty-candidate pool, assigns stable one-based display ranks after merge and deduplication, and tells the Agent to produce the smallest complete answer: cover each distinct responsibility and materially different approach, group alternatives by role, exclude clearly unrelated or duplicate solutions, and neither pad nor silently truncate to a fixed count | PM-005, PM-026 | search orchestration and conversation-tool contract unit tests plus Agent answer acceptance |
42
42
  | A-034 Default manager actions send only schema-valid anonymous envelopes to the first-party Registry endpoint, reuse a lazily created random ID, record each attempted batch install outcome, and continue normally through storage or delivery failure; explicit opt-out creates no ID and makes no telemetry request | PM-027 | telemetry unit tests + manager integration event assertions + Registry endpoint contract and end-to-end PostHog verification |
43
+ | A-035 A versioned bilingual scenario suite covers named-backend and adjacent-capability confusion, compositions, third-party npm and GitHub-only plugins, exact identifiers, and post-inspection alias deduplication. The hybrid Registry candidate pool must cover every required role, suppress reviewed forbidden neighbours, preserve exact rank one, contain no duplicate project identity, and report every individual regression against the keyword baseline | PM-005, PM-006, PM-025, PM-026, PM-028 | scenario schema/evaluator unit tests plus `npm run acceptance:live:registry` against one frozen Registry snapshot and directory version |
44
+ | A-036 A versioned task-solution suite covers a complete three-role composition, missing required and optional roles, one plugin covering multiple roles, distinct alternatives, and unresolved ambiguity. Tool integration must search each role, preserve role groups, reject duplicate/cross-role/expired selection, globally deduplicate selected solutions, and report complete only after reviewed coverage of every required role | PM-005, PM-026, PM-029, PM-030, PM-031 | checked-in task-solution fixture, store unit tests, conversation schema test, and manager integration test |
45
+ | A-037 A separate bilingual live-model suite gives the model only user tasks, then deterministically checks exact minimal role count, distinct semantic coverage, required/optional flags, forbidden invented roles, and unresolved ambiguity. Cross-time monitoring must retain separate state-reader and durable-resumption coverage even when one selected plugin can satisfy both | PM-029, PM-031, PM-032 | checked-in task-role fixture and output schema, positive/negative evaluator tests, plus opt-in `npm run acceptance:live:task-roles` |
46
+ | A-038 A real disposable DSH Web conversation uses the packed release candidate and a snapshot-aligned local Registry to perform one `search_roles` and one `assess_solution` for a three-capability task. The gate requires exact role coverage, one reviewed gold solution per role, global non-duplication, no workflow-call errors, no redundant inspection, and no mutation; directory contribution remains an observed metric because final relevance can validly come from keyword evidence | PM-005, PM-025, PM-026, PM-029-PM-032 | pure evaluator tests plus opt-in `npm run acceptance:live:dsh-task-solution` against frozen Registry export `discovery.composite.2026-09-06.v1-0-5.b92b985e31df` and directory `plugin-directory-semantic-v3-18` |
43
47
 
44
48
  ## Test Layers
45
49
 
@@ -22,10 +22,10 @@ natural language --------------------------+
22
22
  v
23
23
  PluginManager core
24
24
  +-----------------+------------------+
25
- | |
26
- PluginSearchRuntime Operation runtime
27
- | |
28
- built-in / external providers official DSH CLI
25
+ | | |
26
+ PluginSearchRuntime TaskSolutionStore Operation runtime
27
+ | | |
28
+ built-in / external providers official DSH CLI
29
29
  |
30
30
  postcondition -> hot activation
31
31
  |
@@ -115,6 +115,59 @@ directly into `inspect`; `recommendedSource` is immutable and can flow directly
115
115
  into `plan`. Owner-only identities fail inspection with guidance to use owner
116
116
  search instead of falling through to npm validation.
117
117
 
118
+ The built-in Registry provider requests a bounded keyword challenger pool and
119
+ the active semantic-directory route concurrently. It validates both responses
120
+ as source-only, non-authorizing discovery data and refuses to combine different
121
+ snapshot ids. Reciprocal-rank fusion lets directory evidence rerank the bounded
122
+ keyword candidates; directory-only candidates are used only when keyword search
123
+ fails, which limits semantic sibling flooding. Exact identifiers and explicit
124
+ technology terms receive deterministic priority. If one route fails, the other
125
+ remains usable; if both fail, the provider reports an isolated provider error.
126
+
127
+ The final model-facing pool carries canonical path and matched-capability
128
+ evidence. The Agent contract asks for the smallest complete answer rather than a
129
+ fixed top-N: cover every required role and materially different approach, group
130
+ alternatives by role, exclude unrelated rows, and never repeat aliases of one
131
+ project. A checked-in bilingual scenario suite tests role coverage, reviewed
132
+ near-neighbour exclusions, duplicate project identities, and exact identifiers
133
+ against the keyword baseline. Rank metrics remain diagnostics and cannot mask
134
+ either a missed role or a hard-check regression.
135
+
136
+ ## Task Solution Workflow
137
+
138
+ Complex task discovery is deliberately a two-step read-only protocol. The DSH
139
+ Agent provides semantic judgment while the manager owns structural enforcement:
140
+
141
+ 1. The Agent decomposes the user task into the smallest mutually distinct set
142
+ of required and explicitly optional responsibilities. It supplies a focused
143
+ query per role and records any material unresolved choice as an ambiguity.
144
+ A cross-time observation keeps source-specific state/event reading separate
145
+ from durable waiting/scheduling and Session resumption. This is a coverage
146
+ boundary rather than a plugin-count assumption: one plugin may later satisfy
147
+ both roles.
148
+ 2. `search_roles` validates the plan and searches every role concurrently via
149
+ the same provider, inspection, and alias-deduplication path as ordinary
150
+ search. It stores at most 32 drafts for ten minutes and returns grouped
151
+ candidate summaries without claiming that presence proves relevance.
152
+ 3. The Agent reviews the groups, excludes adjacent or unrelated rows, retains
153
+ materially different alternatives, and calls `assess_solution` with the
154
+ selected candidate identities for each role.
155
+ 4. Assessment rejects identities that were not returned for that role and
156
+ duplicate selection rows. It emits one global solution record per identity,
157
+ annotates a shared solution with every role it covers, and computes required
158
+ and optional coverage. Missing required roles produce `incomplete`; any
159
+ unresolved ambiguity produces `ambiguous`; only reviewed full required-role
160
+ coverage with no ambiguity produces `complete`.
161
+
162
+ Draft ids confer no mutation authority. Installation still requires the normal
163
+ immutable inspection, plan, and separate confirmation path.
164
+
165
+ Role-plan semantics have a separate opt-in live-model gate. The model sees only
166
+ the bilingual tasks and a JSON output contract; a deterministic evaluator then
167
+ checks reviewed semantic concepts using distinct matching, exact role and
168
+ ambiguity counts, required/optional flags, and forbidden invented roles. The
169
+ live gate never searches, installs, or changes a Profile.
170
+
118
171
  No provider callback participates after discovery.
119
172
 
120
173
  ## Confirmation Plans
@@ -1 +1 @@
1
- {"version":3,"file":"errors-BGx4K2uq.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["export type PluginManagerErrorCode =\n | 'INVALID_SOURCE'\n | 'INVALID_NPM_SPEC'\n | 'INVALID_NPM_VERSION'\n | 'INVALID_GITHUB_SPEC'\n | 'INVALID_GITHUB_REF'\n | 'GITHUB_OWNER_REQUIRES_SEARCH'\n | 'IMMUTABLE_SOURCE_REQUIRED'\n | 'NETWORK_ERROR'\n | 'SOURCE_HTTP_ERROR'\n | 'INVALID_SOURCE_METADATA'\n | 'INVALID_PLUGIN_MANIFEST'\n | 'NOT_DSH_PLUGIN'\n | 'PACKAGE_NAME_MISMATCH'\n | 'NPM_INTEGRITY_MISSING'\n | 'INVALID_SEARCH_QUERY'\n | 'INVALID_ACTION'\n | 'INVALID_BATCH'\n | 'DUPLICATE_SEARCH_PROVIDER'\n | 'PROFILE_READ_FAILED'\n | 'PROFILE_WRITE_FAILED'\n | 'PLUGIN_NOT_INSTALLED'\n | 'PLUGIN_ALREADY_INSTALLED'\n | 'ENABLEMENT_UNSUPPORTED'\n | 'ENABLEMENT_CONFLICT'\n | 'PROTECTED_PLUGIN'\n | 'CONFIRMATION_REQUIRED'\n | 'CONFIRMATION_INVALID'\n | 'CONFIRMATION_EXPIRED'\n | 'CONFIRMATION_REPLAYED'\n | 'PLAN_STALE'\n | 'OPERATION_NOT_FOUND'\n | 'DSH_COMMAND_FAILED'\n | 'BATCH_INSTALL_FAILED'\n | 'POSTCONDITION_FAILED'\n | 'RESTART_UNAVAILABLE'\n\nexport class PluginManagerError extends Error {\n readonly code: PluginManagerErrorCode\n readonly details?: Record<string, unknown>\n\n constructor(code: PluginManagerErrorCode, message: string, details?: Record<string, unknown>) {\n super(message)\n this.name = 'PluginManagerError'\n this.code = code\n this.details = details\n }\n}\n\nexport function fail(\n code: PluginManagerErrorCode,\n message: string,\n details?: Record<string, unknown>,\n): never {\n throw new PluginManagerError(code, message, details)\n}\n"],"mappings":";AAqCA,IAAa,qBAAb,cAAwC,MAAM;CAC5C;CACA;CAEA,YAAY,MAA8B,SAAiB,SAAmC;EAC5F,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,KAAK,UAAU;CACjB;AACF;AAEA,SAAgB,KACd,MACA,SACA,SACO;CACP,MAAM,IAAI,mBAAmB,MAAM,SAAS,OAAO;AACrD"}
1
+ {"version":3,"file":"errors-BGx4K2uq.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["export type PluginManagerErrorCode =\n | 'INVALID_SOURCE'\n | 'INVALID_NPM_SPEC'\n | 'INVALID_NPM_VERSION'\n | 'INVALID_GITHUB_SPEC'\n | 'INVALID_GITHUB_REF'\n | 'GITHUB_OWNER_REQUIRES_SEARCH'\n | 'IMMUTABLE_SOURCE_REQUIRED'\n | 'NETWORK_ERROR'\n | 'SOURCE_HTTP_ERROR'\n | 'INVALID_SOURCE_METADATA'\n | 'INVALID_PLUGIN_MANIFEST'\n | 'NOT_DSH_PLUGIN'\n | 'PACKAGE_NAME_MISMATCH'\n | 'NPM_INTEGRITY_MISSING'\n | 'INVALID_SEARCH_QUERY'\n | 'INVALID_ACTION'\n | 'INVALID_BATCH'\n | 'DUPLICATE_SEARCH_PROVIDER'\n | 'PROFILE_READ_FAILED'\n | 'PROFILE_WRITE_FAILED'\n | 'PLUGIN_NOT_INSTALLED'\n | 'PLUGIN_ALREADY_INSTALLED'\n | 'ENABLEMENT_UNSUPPORTED'\n | 'ENABLEMENT_CONFLICT'\n | 'PROTECTED_PLUGIN'\n | 'CONFIRMATION_REQUIRED'\n | 'CONFIRMATION_INVALID'\n | 'CONFIRMATION_EXPIRED'\n | 'CONFIRMATION_REPLAYED'\n | 'PLAN_STALE'\n | 'OPERATION_NOT_FOUND'\n | 'DSH_COMMAND_FAILED'\n | 'BATCH_INSTALL_FAILED'\n | 'POSTCONDITION_FAILED'\n | 'RESTART_UNAVAILABLE'\n | 'INVALID_TASK_SOLUTION'\n | 'TASK_SOLUTION_NOT_FOUND'\n | 'TASK_SOLUTION_EXPIRED'\n\nexport class PluginManagerError extends Error {\n readonly code: PluginManagerErrorCode\n readonly details?: Record<string, unknown>\n\n constructor(code: PluginManagerErrorCode, message: string, details?: Record<string, unknown>) {\n super(message)\n this.name = 'PluginManagerError'\n this.code = code\n this.details = details\n }\n}\n\nexport function fail(\n code: PluginManagerErrorCode,\n message: string,\n details?: Record<string, unknown>,\n): never {\n throw new PluginManagerError(code, message, details)\n}\n"],"mappings":";AAwCA,IAAa,qBAAb,cAAwC,MAAM;CAC5C;CACA;CAEA,YAAY,MAA8B,SAAiB,SAAmC;EAC5F,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO;EACZ,KAAK,UAAU;CACjB;AACF;AAEA,SAAgB,KACd,MACA,SACA,SACO;CACP,MAAM,IAAI,mBAAmB,MAAM,SAAS,OAAO;AACrD"}
package/lib/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as PluginSearchRuntime, c as PluginSource, i as PluginSearchRequest, l as inspectPluginSource, n as PluginSearchMatch, o as FetchOptions, r as PluginSearchProvider, s as PluginInspection, t as PluginSearchCandidate } from "./search-runtime-DsmnI673.js";
1
+ import { a as PluginSearchRuntime, c as PluginSource, i as PluginSearchRequest, l as inspectPluginSource, n as PluginSearchMatch, o as FetchOptions, r as PluginSearchProvider, s as PluginInspection, t as PluginSearchCandidate } from "./search-runtime-gzUZ5Yb3.js";
2
2
  import { spawn } from "node:child_process";
3
3
  import { Context } from "@deepseek-ai/cordis";
4
4
 
@@ -16,6 +16,50 @@ interface TelemetryConfig {
16
16
  test?: boolean;
17
17
  }
18
18
  //#endregion
19
+ //#region src/search.d.ts
20
+ interface SearchResultSource {
21
+ inspection: PluginInspection;
22
+ providers: string[];
23
+ evidence: string[];
24
+ }
25
+ interface SearchResult {
26
+ query: string;
27
+ candidates: Array<{
28
+ rank: number;
29
+ identity: string;
30
+ packageName: string;
31
+ description: string | null;
32
+ repository: string | null;
33
+ repositoryOwner: string | null;
34
+ providers: string[];
35
+ matchReasons: string[];
36
+ semanticMatches: Array<{
37
+ directoryVersion: string | null;
38
+ canonicalPathKey: string | null;
39
+ canonicalPath: string[];
40
+ matchedCapabilities: string[];
41
+ retrievalSources: string[];
42
+ }>;
43
+ sources: SearchResultSource[];
44
+ recommendedSource: string;
45
+ }>;
46
+ presentation: {
47
+ order: 'rank_ascending';
48
+ returnedCandidates: number;
49
+ requestedMaximum: number;
50
+ includeEveryDistinctRelevantSolution: true;
51
+ excludeClearlyIrrelevant: true;
52
+ deduplicateEquivalentSources: true;
53
+ padToRequestedMaximum: false;
54
+ silentTopNTruncation: false;
55
+ };
56
+ providerErrors: Array<{
57
+ provider: string;
58
+ error: string;
59
+ }>;
60
+ rejectedCandidates: number;
61
+ }
62
+ //#endregion
19
63
  //#region src/profile.d.ts
20
64
  interface PackageSurface {
21
65
  packageName: string;
@@ -225,6 +269,88 @@ declare class DshRestarter {
225
269
  };
226
270
  }
227
271
  //#endregion
272
+ //#region src/task-solutions.d.ts
273
+ interface TaskRoleInput {
274
+ id: string;
275
+ label: string;
276
+ query: string;
277
+ required?: boolean;
278
+ }
279
+ interface TaskRole {
280
+ id: string;
281
+ label: string;
282
+ query: string;
283
+ required: boolean;
284
+ }
285
+ interface TaskAmbiguityInput {
286
+ id: string;
287
+ question: string;
288
+ options: string[];
289
+ }
290
+ type TaskSolutionCandidate = Omit<SearchResult['candidates'][number], 'sources'>;
291
+ interface TaskRoleSearchGroup extends TaskRole {
292
+ candidates: TaskSolutionCandidate[];
293
+ providerErrors: SearchResult['providerErrors'];
294
+ rejectedCandidates: number;
295
+ }
296
+ interface TaskSolutionDraft {
297
+ schemaVersion: '1.0.0';
298
+ solutionId: string;
299
+ task: string;
300
+ status: 'needs_review' | 'incomplete' | 'ambiguous';
301
+ createdAt: number;
302
+ expiresAt: number;
303
+ roles: TaskRoleSearchGroup[];
304
+ ambiguities: TaskAmbiguityInput[];
305
+ }
306
+ interface TaskRoleSelection {
307
+ roleId: string;
308
+ candidateIdentities: string[];
309
+ }
310
+ interface AssessedTaskRole extends TaskRole {
311
+ status: 'covered' | 'missing_required' | 'missing_optional';
312
+ primaryCandidateIdentity: string | null;
313
+ alternativeCandidateIdentities: string[];
314
+ }
315
+ interface TaskSolutionAssessment {
316
+ schemaVersion: '1.0.0';
317
+ solutionId: string;
318
+ task: string;
319
+ status: 'complete' | 'incomplete' | 'ambiguous';
320
+ roles: AssessedTaskRole[];
321
+ ambiguities: TaskAmbiguityInput[];
322
+ solutions: Array<TaskSolutionCandidate & {
323
+ roleIds: string[];
324
+ }>;
325
+ coverage: {
326
+ requiredRoles: number;
327
+ coveredRequiredRoles: number;
328
+ optionalRoles: number;
329
+ coveredOptionalRoles: number;
330
+ missingRequiredRoleIds: string[];
331
+ complete: boolean;
332
+ };
333
+ }
334
+ interface TaskSolutionStoreOptions {
335
+ now?: () => number;
336
+ id?: () => string;
337
+ ttlMs?: number;
338
+ }
339
+ declare class TaskSolutionStore {
340
+ private readonly now;
341
+ private readonly id;
342
+ private readonly ttlMs;
343
+ private readonly drafts;
344
+ constructor(options?: TaskSolutionStoreOptions);
345
+ create(input: {
346
+ task: string;
347
+ roles: TaskRoleInput[];
348
+ ambiguities?: TaskAmbiguityInput[];
349
+ searches: Record<string, SearchResult>;
350
+ }): TaskSolutionDraft;
351
+ assess(solutionIdValue: string, selectionValues: TaskRoleSelection[]): TaskSolutionAssessment;
352
+ }
353
+ //#endregion
228
354
  //#region src/manager.d.ts
229
355
  interface LoaderEntryLike {
230
356
  id?: string;
@@ -253,13 +379,19 @@ interface PluginManagerDependencies {
253
379
  fetchOptions?: Omit<FetchOptions, 'signal'>;
254
380
  hmrTimeoutMs?: number;
255
381
  telemetry?: Telemetry;
382
+ taskSolutions?: TaskSolutionStore;
256
383
  }
257
384
  interface DiscoverRequest {
258
- action: 'list' | 'search' | 'inspect' | 'status';
385
+ action: 'list' | 'search' | 'search_roles' | 'assess_solution' | 'inspect' | 'status';
259
386
  query?: string;
260
387
  target?: string;
261
388
  operationId?: string;
262
389
  maxResults?: number;
390
+ maxResultsPerRole?: number;
391
+ roles?: TaskRoleInput[];
392
+ ambiguities?: TaskAmbiguityInput[];
393
+ solutionId?: string;
394
+ selections?: TaskRoleSelection[];
263
395
  }
264
396
  interface PlanRequest {
265
397
  operation: PlanAction;
@@ -317,6 +449,7 @@ declare class PluginManager {
317
449
  private readonly fetchOptions;
318
450
  private readonly hmrTimeoutMs;
319
451
  private readonly telemetry;
452
+ private readonly taskSolutions;
320
453
  constructor(dependencies: PluginManagerDependencies);
321
454
  private capture;
322
455
  private loaderEntries;
@@ -352,5 +485,5 @@ interface Config {
352
485
  }
353
486
  declare function apply(ctx: Context, config?: Config): void;
354
487
  //#endregion
355
- export { Config, DEFAULT_REGISTRY_ORIGIN, type DiscoverRequest, type InstallManyItemResult, type InstallManyItemStatus, type InstallManyResult, type MutationResult, type PlanRequest, type PluginInspection, PluginManager, type PluginSearchCandidate, type PluginSearchMatch, type PluginSearchProvider, type PluginSearchRequest, type PluginSource, type TelemetryConfig, apply, inject, name };
488
+ export { Config, DEFAULT_REGISTRY_ORIGIN, type DiscoverRequest, type InstallManyItemResult, type InstallManyItemStatus, type InstallManyResult, type MutationResult, type PlanRequest, type PluginInspection, PluginManager, type PluginSearchCandidate, type PluginSearchMatch, type PluginSearchProvider, type PluginSearchRequest, type PluginSource, type TaskAmbiguityInput, type TaskRoleInput, type TaskRoleSelection, type TaskSolutionAssessment, type TaskSolutionDraft, type TelemetryConfig, apply, inject, name };
356
489
  //# sourceMappingURL=index.d.ts.map