dsh-better-copilot 0.1.1 → 0.2.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
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0 — read-only subagent route badge
4
+
5
+ - Add a browser client half that shows an addressed subagent's effective model
6
+ and reasoning effort in the Web session header, beside the title. The label is
7
+ text only: no selector, no click target, no Host write, because the child's
8
+ route is fixed at creation and reused on resume.
9
+ - Read the route from the existing `modelSelection` session projection and show
10
+ an adapter-defaulted effort as `default`. Provider/model/effort ids render
11
+ verbatim; catalog display names are not available to client plugins.
12
+ - Keep ordinary conversations and the DSH-rendered subagent catalog untouched.
13
+ Ship the bundle through `./client` and a `dsh.client` declaration, with render
14
+ tests and the publication allowlist updated.
15
+
3
16
  ## 0.1.1 — DSH 0.2.2-rc.1 compatibility
4
17
 
5
18
  - Retarget the manifest, peer ranges and development pins to DSH `0.2.2-rc.1`
package/README.md CHANGED
@@ -101,6 +101,25 @@ tool schemas that lack one for a configured Copilot route and model family, and
101
101
  leaves every other byte of the request untouched. It never replaces an adapter or
102
102
  another plugin's settings, and a guard failure dispatches the original body.
103
103
 
104
+ ## Subagent model badge (Web)
105
+
106
+ The package now carries a small browser client half. When a subagent
107
+ conversation is open in the Web GUI, the session header shows the child's
108
+ effective route as muted text next to the title — for example
109
+ `gpt-6.1-sol · high`. The label is read-only: the plugin renders no selector and
110
+ writes nothing back, because the Host fixes a child's provider, model and
111
+ reasoning effort when the subagent is created and reuses the recorded route on
112
+ resume. An adapter-defaulted effort appears as `default`, and the tooltip and
113
+ accessible name say that the route is fixed.
114
+
115
+ The badge reads the child Session's `modelSelection` projection; it adds no Host
116
+ service, configuration field or RPC. Ids are shown exactly as the Host records
117
+ them, because catalog display names are not exposed to third-party client
118
+ plugins. Ordinary conversations are unchanged, and the subagent catalog
119
+ dropdown is untouched: DSH renders those rows in its own client package rather
120
+ than through an extension slot. The browser half is published as `./client`
121
+ and declared through `dsh.client`; no additional wiring is needed.
122
+
104
123
  ## Migration from the three predecessor packages
105
124
 
106
125
  | Was | Now |
package/dist/client.js ADDED
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ /// <reference path="./client.types.d.ts" />
3
+ /* Read-only subagent route badge for the Web conversation header.
4
+ * The Host fixes a subagent's provider, model and reasoning effort when the
5
+ * child is created, so this client half only presents them. It never renders a
6
+ * selection control and writes nothing back to the Session.
7
+ */
8
+ window.__ModuleLoader__.load({
9
+ id: 'dsh-better-copilot',
10
+ factory(require) {
11
+ const React = require('react');
12
+ const h = React.createElement;
13
+ const NS = 'better-copilot.subagent';
14
+ const SLOT = 'conversation.session.header.actions';
15
+ const ID = 'better-copilot-subagent-model';
16
+ const en = {
17
+ 'badge.defaultEffort': 'default',
18
+ 'badge.fixed': "Model and effort are fixed for this subagent's lifetime.",
19
+ 'badge.aria': 'Subagent model {model} from {provider}, reasoning effort {effort}. {fixed}',
20
+ };
21
+ const zh = {
22
+ 'badge.defaultEffort': '默认',
23
+ 'badge.fixed': '该子智能体的模型与推理强度在创建时已固定。',
24
+ 'badge.aria': '子智能体模型 {model}({provider}),推理强度 {effort}。{fixed}',
25
+ };
26
+ /** Validate one projected selection; anything malformed is not rendered. */
27
+ function readSelection(value) {
28
+ if (typeof value !== 'object' || value === null)
29
+ return null;
30
+ const candidate = value;
31
+ const provider = typeof candidate.provider === 'string' ? candidate.provider : '';
32
+ const model = typeof candidate.model === 'string' ? candidate.model : '';
33
+ if (provider === '' || model === '')
34
+ return null;
35
+ const reasoningEffort = typeof candidate.reasoningEffort === 'string' && candidate.reasoningEffort !== ''
36
+ ? candidate.reasoningEffort
37
+ : undefined;
38
+ return reasoningEffort === undefined ? { provider, model } : { provider, model, reasoningEffort };
39
+ }
40
+ /** Read the effective selection from the modelSelection wire view. */
41
+ function selectedOf(view) {
42
+ if (typeof view !== 'object' || view === null)
43
+ return null;
44
+ const projected = view;
45
+ return readSelection(projected.next) ?? readSelection(projected.lastUsed);
46
+ }
47
+ /** Compose the visible label, tooltip and accessible name for one route. */
48
+ function subagentBadge(selection, t) {
49
+ const explicit = selection.reasoningEffort;
50
+ const effort = explicit === undefined ? t('badge.defaultEffort') : explicit;
51
+ const route = explicit === undefined
52
+ ? `${selection.provider}/${selection.model}`
53
+ : `${selection.provider}/${selection.model} · ${explicit}`;
54
+ return {
55
+ label: `${selection.model} · ${effort}`,
56
+ title: `${route} — ${t('badge.fixed')}`,
57
+ aria: t('badge.aria', {
58
+ provider: selection.provider,
59
+ model: selection.model,
60
+ effort,
61
+ fixed: t('badge.fixed'),
62
+ }),
63
+ };
64
+ }
65
+ const badgeStyle = {
66
+ display: 'inline-block',
67
+ maxWidth: '26ch',
68
+ overflow: 'hidden',
69
+ color: 'var(--dsw-alias-label-tertiary)',
70
+ fontSize: '12px',
71
+ lineHeight: '18px',
72
+ whiteSpace: 'nowrap',
73
+ textOverflow: 'ellipsis',
74
+ verticalAlign: 'middle',
75
+ cursor: 'default',
76
+ };
77
+ /** Read-only header cell: visible only for an addressed subagent's own route. */
78
+ function SubagentModelBadge({ sessionId, useSessions, useProjection, t }) {
79
+ const subagent = useSessions((state) => state.byId[sessionId]?.origin === 'subagent');
80
+ const view = useProjection('modelSelection');
81
+ if (!subagent)
82
+ return null;
83
+ const selection = selectedOf(view);
84
+ if (selection === null)
85
+ return null;
86
+ const badge = subagentBadge(selection, t);
87
+ return h('span', { style: badgeStyle, title: badge.title, 'aria-label': badge.aria }, badge.label);
88
+ }
89
+ function apply(ctx) {
90
+ ctx.effect(() => ctx.locale.register(NS, { en, zh }));
91
+ ctx.slots.inject(SLOT, () => ctx.slots.register({ name: SLOT, id: ID, order: 15, locale: NS }, SubagentModelBadge));
92
+ }
93
+ return {
94
+ inject: ['slots', 'locale'],
95
+ apply,
96
+ };
97
+ },
98
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-better-copilot",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Account-aware GitHub Copilot model discovery, Copilot-compatible wire identity, native Responses web search and a Copilot Gemini tool-declaration guard in one DeepSeek Harness plugin.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -30,6 +30,7 @@
30
30
  "types": "./dist/guard.d.ts",
31
31
  "import": "./dist/guard.js"
32
32
  },
33
+ "./client": "./dist/client.js",
33
34
  "./package.json": "./package.json"
34
35
  },
35
36
  "files": [
@@ -89,18 +90,26 @@
89
90
  "@deepseek-ai/dsh-system-prompt": "0.2.2-rc.1",
90
91
  "@deepseek-ai/dsh-web": "0.2.2-rc.1",
91
92
  "@deepseek-ai/schemastery": "3.19.0",
92
- "@earendil-works/pi-ai": "1.3.0"
93
+ "@earendil-works/pi-ai": "1.3.0",
94
+ "@types/react": "^19.3.0",
95
+ "@types/react-dom": "^19.3.0",
96
+ "react": "^19.3.0",
97
+ "react-dom": "^19.3.0"
93
98
  },
94
99
  "dsh": {
95
100
  "manifestVersion": 1,
96
101
  "bundle": {
97
102
  "patch": "./cordis.patch.yml"
103
+ },
104
+ "client": {
105
+ "platform": "web",
106
+ "immediately": true
98
107
  }
99
108
  },
100
109
  "scripts": {
101
- "build": "tsc -p tsconfig.json",
102
- "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p test/tsconfig.json",
103
- "test": "pnpm run build && node --import tsx --test test/catalog.test.ts test/discovery.test.ts test/identity.test.ts test/secrets.test.ts test/catalog-plugin.test.ts test/config.test.ts test/composition.test.ts test/auth.test.ts test/refresh.test.ts test/provider.test.ts test/responses.test.ts test/search-plugin.test.ts test/schema.test.ts test/guard.test.ts test/guard-plugin.test.ts test/patch.test.ts",
110
+ "build": "tsc -p tsconfig.json && tsc -p tsconfig.client.json",
111
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p test/tsconfig.json && tsc -p tsconfig.client.json --noEmit",
112
+ "test": "pnpm run build && node --import tsx --test test/catalog.test.ts test/discovery.test.ts test/identity.test.ts test/secrets.test.ts test/catalog-plugin.test.ts test/config.test.ts test/composition.test.ts test/auth.test.ts test/refresh.test.ts test/provider.test.ts test/responses.test.ts test/search-plugin.test.ts test/schema.test.ts test/guard.test.ts test/guard-plugin.test.ts test/patch.test.ts test/client.render.test.ts",
104
113
  "test:integration": "pnpm run build && node --import tsx --test test/integration/nested-layout.test.ts test/integration/catalog-runtime.test.ts test/integration/enterprise.test.ts test/integration/identity-wire.test.ts test/integration/search-runtime.test.ts test/integration/guard-runtime.test.ts test/integration/wire.test.ts",
105
114
  "test:types": "pnpm run build && tsc -p test/types/tsconfig.json",
106
115
  "test:pack": "pnpm run build && tsx ../../scripts/check-pack.ts .",