dsh-plan-completion 0.1.6 → 0.1.7

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
@@ -3,6 +3,18 @@
3
3
  All notable changes to this project are documented here.
4
4
  Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), versioning: SemVer.
5
5
 
6
+ ## [0.1.7] - 2026-11-02
7
+
8
+ ### Fixed
9
+ - Settings page model lists now load per provider (`GET /settings?provider=…`), so third-party
10
+ providers' models appear after switching Provider; fixed an internal error caused by a
11
+ provider-scope bug in the settings snapshot.
12
+
13
+ ### Changed
14
+ - Settings page redesign matched to DSH General Settings: flat 66px rows (title + description left,
15
+ control right), filled controls without borders, DSH-sized toggle, right-aligned save button,
16
+ unified typography/theme tokens for light and dark themes.
17
+
6
18
  ## [0.1.6] - 2026-11-02
7
19
 
8
20
  ### Added
@@ -152,3 +164,4 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), versioning: Se
152
164
  `enableDebugRoutes: true`; production endpoints are `state`, `debug`, `diag`, `replan`, `warm`.
153
165
  - No credentials are stored in the plugin; model calls use the host `llm` service.
154
166
 
167
+
package/client.js CHANGED
@@ -113,7 +113,10 @@ window.__ModuleLoader__.load({
113
113
  'settings.policy.auto': '自动(优先非推理)',
114
114
  'settings.provider': 'Provider',
115
115
  'settings.modelId': 'Model ID',
116
- 'settings.modelCalls': '允许本插件调用模型',
116
+ 'settings.modelCalls': '允许本插件调用模型',
117
+ 'settings.modelDesc': '选择插件生成计划与建议时使用的模型来源',
118
+ 'settings.providerDesc': '固定使用的 provider',
119
+ 'settings.callsDesc': '关闭后插件只使用启发式,不向模型发送内容',
117
120
  'settings.defaultRoute': '宿主默认:{value}',
118
121
  'settings.save': '保存',
119
122
  'settings.saving': '保存中…',
@@ -171,7 +174,10 @@ window.__ModuleLoader__.load({
171
174
  'settings.policy.auto': 'Auto (prefer non-reasoning)',
172
175
  'settings.provider': 'Provider',
173
176
  'settings.modelId': 'Model ID',
174
- 'settings.modelCalls': 'Allow this plugin to call models',
177
+ 'settings.modelCalls': 'Allow this plugin to call models',
178
+ 'settings.modelDesc': 'Model source used for plan and suggestion generation',
179
+ 'settings.providerDesc': 'Provider used by the fixed policy',
180
+ 'settings.callsDesc': 'When off, the plugin only uses heuristics and sends nothing',
175
181
  'settings.defaultRoute': 'Host default: {value}',
176
182
  'settings.save': 'Save',
177
183
  'settings.saving': 'Saving…',
@@ -820,9 +826,10 @@ window.__ModuleLoader__.load({
820
826
  /**
821
827
  * Host settings used by the settings page (model policy + modelCalls).
822
828
  */
823
- function fetchSettings() {
829
+ function fetchSettings(provider) {
824
830
  try {
825
- return Promise.resolve(fetch('/plan-completion/settings', {
831
+ const query = typeof provider === 'string' && provider ? '?provider=' + encodeURIComponent(provider) : '';
832
+ return Promise.resolve(fetch('/plan-completion/settings' + query, {
826
833
  method: 'GET',
827
834
  credentials: 'same-origin',
828
835
  headers: { accept: 'application/json' }
@@ -2138,176 +2145,138 @@ window.__ModuleLoader__.load({
2138
2145
  const SETTINGS_ID = 'plan-completion-settings';
2139
2146
  const SETTINGS_ORDER = 60;
2140
2147
 
2141
- function settingsCss() {
2142
- return [
2143
- '.dpc-settings-panel{display:flex;flex-direction:column;gap:14px;padding:16px 18px;font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary);max-width:560px}',
2144
- '.dpc-settings-title{font-size:15px;font-weight:600;line-height:24px}',
2145
- '.dpc-settings-group{display:flex;flex-direction:column;gap:8px}',
2146
- '.dpc-settings-label{font-size:12px;color:var(--dsw-alias-label-tertiary)}',
2147
- '.dpc-settings-radio{display:flex;align-items:center;gap:8px;cursor:pointer}',
2148
- '.dpc-settings-fixed{display:flex;gap:8px;padding:8px 0 0 24px;flex-wrap:wrap}',
2149
- '.dpc-settings-input{flex:1 1 180px;min-width:140px;border:1px solid var(--dsw-alias-border-l1);border-radius:8px;padding:5px 10px;font-size:12px;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1));color:var(--dsw-alias-label-primary)}',
2150
- '.dpc-settings-check{display:flex;align-items:center;gap:8px;cursor:pointer}',
2151
- '.dpc-settings-hint{font-size:11px;color:var(--dsw-alias-label-tertiary)}',
2152
- '.dpc-settings-error{font-size:12px;color:var(--dsw-alias-state-error-primary,var(--dsw-alias-label-secondary))}',
2153
- '.dpc-settings-save{align-self:flex-start;border:1px solid var(--dsw-alias-border-l1);border-radius:999px;padding:5px 14px;font-size:12px;line-height:18px;cursor:pointer;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1));color:var(--dsw-alias-label-primary)}',
2154
- '.dpc-settings-save:disabled{opacity:.5;cursor:default}',
2155
- '.dpc-settings-status{padding:16px 18px;font-size:13px;color:var(--dsw-alias-label-secondary)}'
2156
- ].join('\n');
2157
- }
2158
-
2159
- function PlanSettingsPage(props) {
2160
- const t = makeT(props);
2161
- const [settings, setSettings] = React.useState(null);
2162
- const [status, setStatus] = React.useState('loading');
2163
- const [saving, setSaving] = React.useState(false);
2164
- const [error, setError] = React.useState('');
2165
- const [saved, setSaved] = React.useState(false);
2166
-
2167
- React.useEffect(function loadSettings() {
2168
- let alive = true;
2169
- fetchSettings().then(function applySettings(result) {
2170
- if (!alive) return;
2171
- if (!result || result.ok !== true || !result.settings) {
2172
- setStatus('error');
2173
- return;
2174
- }
2175
- setSettings(result.settings);
2176
- setStatus('ready');
2177
- });
2178
- return function cleanup() {
2179
- alive = false;
2180
- };
2181
- }, []);
2182
-
2183
- function patch(next) {
2184
- setSaved(false);
2185
- setSettings(function keep(previous) {
2186
- return previous ? { ...previous, ...next } : previous;
2187
- });
2188
- }
2189
-
2190
- function save() {
2191
- if (!settings || saving) return;
2192
- setSaving(true);
2193
- setError('');
2194
- saveSettings({
2195
- modelPolicy: settings.modelPolicy,
2196
- fixedProvider: settings.fixedProvider || '',
2197
- fixedModel: settings.fixedModel || '',
2198
- modelCalls: settings.modelCalls === true
2199
- }).then(function applied(result) {
2200
- setSaving(false);
2201
- if (!result || result.ok !== true) {
2202
- const detail = result && typeof result.error === 'string' ? result.error : '';
2203
- setError(detail ? t('settings.saveError') + ': ' + detail : t('settings.saveError'));
2204
- return;
2205
- }
2206
- setSettings(result.settings || settings);
2207
- setSaved(true);
2208
- setError('');
2209
- });
2210
- }
2211
-
2212
- const styleTag = h('style', { key: 'style', 'data-plan-completion-style': 'settings' }, settingsCss());
2213
-
2214
- if (status === 'loading') {
2215
- return h(React.Fragment, null, styleTag, h('div', { key: 'loading', className: 'dpc-settings-status' }, t('settings.loading')));
2216
- }
2217
- if (status === 'error' || !settings) {
2218
- return h(
2219
- React.Fragment,
2220
- null,
2221
- styleTag,
2222
- h('div', { key: 'error', className: 'dpc-settings-status dpc-settings-error' }, t('settings.error'))
2223
- );
2224
- }
2225
-
2226
- const policies = [
2227
- { value: 'default', label: t('settings.policy.default') },
2228
- { value: 'fixed', label: t('settings.policy.fixed') },
2229
- { value: 'auto', label: t('settings.policy.auto') }
2230
- ];
2231
- const defaultRoute = settings.defaultSelection
2232
- ? settings.defaultSelection.provider + '/' + settings.defaultSelection.model
2233
- : '-';
2234
-
2235
- return h(
2236
- React.Fragment,
2237
- null,
2238
- styleTag,
2239
- h(
2240
- 'div',
2241
- { key: 'panel', className: 'dpc-settings-panel' },
2242
- h('div', { key: 'title', className: 'dpc-settings-title' }, t('settings.title')),
2243
- h(
2244
- 'div',
2245
- { key: 'model', className: 'dpc-settings-group' },
2246
- h('div', { key: 'label', className: 'dpc-settings-label' }, t('settings.model')),
2247
- ...policies.map(function renderPolicy(option) {
2248
- return h(
2249
- 'label',
2250
- { key: 'policy-' + option.value, className: 'dpc-settings-radio' },
2251
- h('input', {
2252
- type: 'radio',
2253
- name: 'dpc-model-policy',
2254
- value: option.value,
2255
- checked: settings.modelPolicy === option.value,
2256
- onChange: function onPolicy() {
2257
- patch({ modelPolicy: option.value });
2258
- }
2259
- }),
2260
- h('span', null, option.label)
2261
- );
2262
- }),
2263
- h('div', { key: 'hint', className: 'dpc-settings-hint' }, t('settings.defaultRoute', { value: defaultRoute })),
2264
- settings.modelPolicy === 'fixed'
2265
- ? h(
2266
- 'div',
2267
- { key: 'fixed', className: 'dpc-settings-fixed' },
2268
- h('input', {
2269
- key: 'provider',
2270
- className: 'dpc-settings-input',
2271
- placeholder: t('settings.provider'),
2272
- value: settings.fixedProvider || '',
2273
- onChange: function onProvider(event) {
2274
- patch({ fixedProvider: event.target.value });
2275
- }
2276
- }),
2277
- h('input', {
2278
- key: 'model',
2279
- className: 'dpc-settings-input',
2280
- placeholder: t('settings.modelId'),
2281
- value: settings.fixedModel || '',
2282
- onChange: function onModel(event) {
2283
- patch({ fixedModel: event.target.value });
2284
- }
2285
- })
2286
- )
2287
- : null
2288
- ),
2289
- h(
2290
- 'label',
2291
- { key: 'calls', className: 'dpc-settings-check' },
2292
- h('input', {
2293
- type: 'checkbox',
2294
- checked: settings.modelCalls === true,
2295
- onChange: function onCalls(event) {
2296
- patch({ modelCalls: event.target.checked });
2297
- }
2298
- }),
2299
- h('span', null, t('settings.modelCalls'))
2300
- ),
2301
- error ? h('div', { key: 'error', className: 'dpc-settings-error' }, error) : null,
2302
- saved && !error ? h('div', { key: 'saved', className: 'dpc-settings-hint' }, t('settings.saved')) : null,
2303
- h(
2304
- 'button',
2305
- { key: 'save', type: 'button', className: 'dpc-settings-save', disabled: saving, onClick: save },
2306
- saving ? t('settings.saving') : t('settings.save')
2307
- )
2308
- )
2309
- );
2310
- }
2148
+ function settingsCss() {
2149
+ return [
2150
+ '.dpc-settings-page{display:flex;flex-direction:column;padding:8px 24px 20px;max-width:720px;box-sizing:border-box;font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary)}',
2151
+ '.dpc-settings-head{display:flex;flex-direction:column;gap:4px;padding:6px 0 10px}',
2152
+ '.dpc-settings-title{font-size:16px;font-weight:600;line-height:26px}',
2153
+ '.dpc-settings-sub{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}',
2154
+ '.dpc-settings-row{display:flex;align-items:center;justify-content:space-between;gap:24px;min-height:66px;padding:12px 0;box-sizing:border-box;border-bottom:1px solid var(--dsw-alias-border-l1)}',
2155
+ '.dpc-settings-row:last-of-type{border-bottom:0}',
2156
+ '.dpc-settings-row-main{display:flex;flex-direction:column;gap:3px;min-width:0}',
2157
+ '.dpc-settings-row-label{font-size:14px;line-height:22px;color:var(--dsw-alias-label-primary)}',
2158
+ '.dpc-settings-row-desc{font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}',
2159
+ '.dpc-settings-control{flex:none;display:flex;justify-content:flex-end;align-items:center;min-width:132px;max-width:240px}',
2160
+ '.dpc-settings-select{width:100%;border:1px solid transparent;border-radius:8px;padding:7px 12px;font-size:13px;line-height:20px;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1));color:var(--dsw-alias-label-primary);cursor:pointer;appearance:auto}',
2161
+ '.dpc-settings-select:hover{background:var(--dsw-alias-bg-layer-1)}',
2162
+ '.dpc-settings-select:focus{outline:none;border-color:var(--dsw-alias-brand-primary,var(--dsw-alias-state-business-primary))}',
2163
+ '.dpc-settings-toggle{width:40px;height:24px;border-radius:999px;border:0;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1));position:relative;cursor:pointer;padding:0;flex:none}',
2164
+ '.dpc-settings-toggle[data-on="true"]{background:var(--dsw-alias-brand-primary,var(--dsw-alias-state-business-primary))}',
2165
+ '.dpc-settings-toggle span{position:absolute;top:2px;left:2px;width:20px;height:20px;border-radius:999px;background:#fff;transition:left .12s ease;box-shadow:0 1px 2px rgba(0,0,0,.18)}',
2166
+ '.dpc-settings-toggle[data-on="true"] span{left:18px}',
2167
+ '.dpc-settings-footer{display:flex;align-items:center;justify-content:flex-end;gap:12px;padding-top:18px}',
2168
+ '.dpc-settings-status{font-size:12px;line-height:18px;color:var(--dsw-alias-state-success-primary,var(--dsw-alias-label-secondary));margin-right:auto}',
2169
+ '.dpc-settings-error{font-size:12px;line-height:18px;color:var(--dsw-alias-state-error-primary,var(--dsw-alias-label-secondary));margin-right:auto}',
2170
+ '.dpc-settings-save{border:0;border-radius:8px;padding:7px 16px;font-size:13px;line-height:20px;cursor:pointer;background:var(--dsw-alias-bg-layer-2,var(--dsw-alias-bg-layer-1));color:var(--dsw-alias-label-primary);flex:none}',
2171
+ '.dpc-settings-save:hover{background:var(--dsw-alias-bg-layer-1)}',
2172
+ '.dpc-settings-save:disabled{opacity:.5;cursor:default}',
2173
+ '.dpc-settings-loading{padding:18px 24px;font-size:13px;color:var(--dsw-alias-label-secondary)}'
2174
+ ].join('\n');
2175
+ }
2176
+
2177
+ function PlanSettingsPage(props) {
2178
+ const t = makeT(props);
2179
+ const [settings, setSettings] = React.useState(null);
2180
+ const [status, setStatus] = React.useState('loading');
2181
+ const [saving, setSaving] = React.useState(false);
2182
+ const [error, setError] = React.useState('');
2183
+ const [saved, setSaved] = React.useState(false);
2184
+
2185
+ React.useEffect(function load() {
2186
+ let alive = true;
2187
+ fetchSettings().then(function applied(result) {
2188
+ if (!alive) return;
2189
+ if (!result || result.ok !== true || !result.settings) { setStatus('error'); return; }
2190
+ setSettings(result.settings);
2191
+ setStatus('ready');
2192
+ });
2193
+ return function cleanup() { alive = false; };
2194
+ }, []);
2195
+
2196
+ function patch(next) {
2197
+ setSaved(false);
2198
+ setSettings(function keep(previous) { return previous ? { ...previous, ...next } : previous; });
2199
+ }
2200
+ function chooseProvider(provider) {
2201
+ patch({ fixedProvider: provider, fixedModel: '', models: [] });
2202
+ fetchSettings(provider).then(function applied(result) {
2203
+ if (result && result.ok === true && result.settings) patch({ models: result.settings.models || [] });
2204
+ });
2205
+ }
2206
+ function save() {
2207
+ if (!settings || saving) return;
2208
+ setSaving(true); setError('');
2209
+ saveSettings({
2210
+ modelPolicy: settings.modelPolicy,
2211
+ fixedProvider: settings.fixedProvider || '',
2212
+ fixedModel: settings.fixedModel || '',
2213
+ modelCalls: settings.modelCalls === true
2214
+ }).then(function applied(result) {
2215
+ setSaving(false);
2216
+ if (!result || result.ok !== true) {
2217
+ const detail = result && typeof result.error === 'string' ? result.error : '';
2218
+ setError(detail ? t('settings.saveError') + ': ' + detail : t('settings.saveError'));
2219
+ return;
2220
+ }
2221
+ setSettings(result.settings || settings);
2222
+ setSaved(true); setError('');
2223
+ });
2224
+ }
2225
+
2226
+ const styleTag = h('style', { key: 'style', 'data-plan-completion-style': 'settings' }, settingsCss());
2227
+ if (status === 'loading') return h(React.Fragment, null, styleTag, h('div', { key: 'loading', className: 'dpc-settings-loading' }, t('settings.loading')));
2228
+ if (status === 'error' || !settings) return h(React.Fragment, null, styleTag, h('div', { key: 'error', className: 'dpc-settings-loading dpc-settings-error' }, t('settings.error')));
2229
+
2230
+ const defaultRoute = settings.defaultSelection ? settings.defaultSelection.provider + '/' + settings.defaultSelection.model : '-';
2231
+ const providers = settings.providers || [];
2232
+ const models = settings.models || [];
2233
+ const row = function row(key, label, desc, control) {
2234
+ return h('div', { key: key, className: 'dpc-settings-row' },
2235
+ h('div', { key: 'main', className: 'dpc-settings-row-main' },
2236
+ h('div', { key: 'label', className: 'dpc-settings-row-label' }, label),
2237
+ desc ? h('div', { key: 'desc', className: 'dpc-settings-row-desc' }, desc) : null),
2238
+ h('div', { key: 'control', className: 'dpc-settings-control' }, control));
2239
+ };
2240
+
2241
+ return h(React.Fragment, null, styleTag,
2242
+ h('div', { key: 'page', className: 'dpc-settings-page' },
2243
+ h('div', { key: 'head', className: 'dpc-settings-head' },
2244
+ h('div', { key: 'title', className: 'dpc-settings-title' }, t('settings.title')),
2245
+ h('div', { key: 'sub', className: 'dpc-settings-sub' }, t('settings.defaultRoute', { value: defaultRoute }))),
2246
+ row('policy', t('settings.model'), t('settings.modelDesc'),
2247
+ h('select', { className: 'dpc-settings-select', value: settings.modelPolicy,
2248
+ onChange: function onPolicy(event) { patch({ modelPolicy: event.target.value }); } },
2249
+ [h('option', { key: 'default', value: 'default' }, t('settings.policy.default')),
2250
+ h('option', { key: 'fixed', value: 'fixed' }, t('settings.policy.fixed')),
2251
+ h('option', { key: 'auto', value: 'auto' }, t('settings.policy.auto'))])),
2252
+ settings.modelPolicy === 'fixed'
2253
+ ? row('provider', t('settings.provider'), t('settings.providerDesc'),
2254
+ h('select', { className: 'dpc-settings-select', value: settings.fixedProvider || '',
2255
+ onChange: function onProvider(event) { chooseProvider(event.target.value); } },
2256
+ [h('option', { key: 'empty', value: '' }, '—')].concat(providers.map(function option(p) {
2257
+ return h('option', { key: p.id, value: p.id }, p.name || p.id);
2258
+ }))))
2259
+ : null,
2260
+ settings.modelPolicy === 'fixed'
2261
+ ? row('model', t('settings.modelId'), t('settings.modelDesc'),
2262
+ h('select', { className: 'dpc-settings-select', value: settings.fixedModel || '',
2263
+ onChange: function onModel(event) { patch({ fixedModel: event.target.value }); } },
2264
+ [h('option', { key: 'empty', value: '' }, '—')].concat(models.map(function option(m) {
2265
+ return h('option', { key: m.id, value: m.id }, m.name || m.id);
2266
+ }))))
2267
+ : null,
2268
+ row('calls', t('settings.modelCalls'), t('settings.callsDesc'),
2269
+ h('button', { type: 'button', className: 'dpc-settings-toggle', 'data-on': settings.modelCalls === true ? 'true' : 'false',
2270
+ 'aria-pressed': settings.modelCalls === true ? 'true' : 'false',
2271
+ onClick: function onCalls() { patch({ modelCalls: settings.modelCalls !== true }); } },
2272
+ h('span', { key: 'knob' }))),
2273
+ h('div', { key: 'footer', className: 'dpc-settings-footer' },
2274
+ error ? h('div', { key: 'e', className: 'dpc-settings-error' }, error)
2275
+ : saved ? h('div', { key: 's', className: 'dpc-settings-status' }, t('settings.saved')) : null,
2276
+ h('button', { key: 'save', type: 'button', className: 'dpc-settings-save', disabled: saving, onClick: save },
2277
+ saving ? t('settings.saving') : t('settings.save')))));
2278
+ }
2279
+
2311
2280
 
2312
2281
  // =====================================================================
2313
2282
  // Plugin entry
@@ -2442,3 +2411,8 @@ window.__ModuleLoader__.load({
2442
2411
  // hot-reload marker 2026-11-01T13:57Z
2443
2412
 
2444
2413
 
2414
+
2415
+
2416
+
2417
+
2418
+
@@ -1,7 +1,9 @@
1
1
  # GitHub 发布评估 · 会话计划补全插件(2026-11-02)
2
2
 
3
- > 历史文档:以下保留 v0.1.0 的交付/评估/计划记录。**当前最新为 v0.1.5**(计划停更检测 + 建议文本保真 + 核对进度 + 本地计划持久化 + 鉴权/状态一致性/隐私边界修复),详见 [security.md](security.md) 与 [CHANGELOG](../CHANGELOG.md);旧记录中的“无鉴权”、原始诊断字段与独立评审不代表当前实现。
3
+ > 历史文档:以下保留 v0.1.0 的交付/评估/计划记录。**当前最新为 v0.1.6**(计划停更检测 + 建议文本保真 + 核对进度 + 本地计划持久化 + 鉴权/状态一致性/隐私边界修复),详见 [security.md](security.md) 与 [CHANGELOG](../CHANGELOG.md);旧记录中的“无鉴权”、原始诊断字段与独立评审不代表当前实现。
4
4
  >
5
+ > v0.1.6 发布记录(2026-11-02):模型设置页(settings.plugins.tab)+ settings 持久化;Tab 建议日志仅本地不发布。tag `v0.1.6`;npm `dsh-plan-completion@0.1.6`;138/138 tests;CI run 41393937067。
6
+ >
5
7
  > v0.1.5 发布记录(2026-11-02):计划停更检测——连续 2 次 op=none 后建议变为 `/replan …` 并标记 stale。tag `v0.1.5` + [Release](https://github.com/Xumi0920/dsh-plan-completion/releases/tag/v0.1.5);Actions [run 41391402601](https://github.com/Xumi0920/dsh-plan-completion/actions/runs/41391402601)(四 job 全绿,133/133 tests);npm `dsh-plan-completion@0.1.5`(shasum `c53c6d7ef79d8634913054db79fa9e5b1e86b182`)。
6
8
  >
7
9
  > v0.1.4 发布记录(2026-11-02):建议文本保真修复(标识符/路径/命令保留 + 语义化截断)。tag `v0.1.4` + [Release](https://github.com/Xumi0920/dsh-plan-completion/releases/tag/v0.1.4);GitHub Actions [run 41389845733](https://github.com/Xumi0920/dsh-plan-completion/actions/runs/41389845733)(四 job 全绿,132/132 tests);npm `dsh-plan-completion@0.1.4`(31 files,shasum `a6f8953445e330081243f9f9d6b429fd4402b341`)。
@@ -111,3 +113,4 @@ git tag v0.1.0 && git push --tags
111
113
 
112
114
 
113
115
 
116
+
package/index.js CHANGED
@@ -419,7 +419,7 @@ export function apply(ctx, config = {}) {
419
419
  }
420
420
  }
421
421
 
422
- function settingsSnapshot() {
422
+ async function settingsSnapshot(providerHint = '') {
423
423
  const selection = services.agentDefaultModel?.currentSelection?.() ?? null
424
424
  let providers = []
425
425
  try {
@@ -427,6 +427,14 @@ export function apply(ctx, config = {}) {
427
427
  } catch {
428
428
  providers = []
429
429
  }
430
+ let models = []
431
+ let modelsProvider = ''
432
+ try {
433
+ modelsProvider = providerHint || selection?.provider || (Array.isArray(providers) ? providers[0]?.id : '')
434
+ if (modelsProvider && typeof services.llm?.listModels === 'function') {
435
+ models = (await services.llm.listModels(modelsProvider))?.map(m => ({ provider: modelsProvider, id: m?.id ?? '', name: m?.name ?? m?.id ?? '' })) ?? []
436
+ }
437
+ } catch { models = [] }
430
438
  return {
431
439
  modelPolicy: settings.modelPolicy,
432
440
  fixedProvider: settings.fixedProvider,
@@ -435,6 +443,8 @@ export function apply(ctx, config = {}) {
435
443
  defaultSelection: selection && selection.provider && selection.model
436
444
  ? { provider: selection.provider, model: selection.model }
437
445
  : null,
446
+ models,
447
+ modelsProvider,
438
448
  providers: Array.isArray(providers)
439
449
  ? providers.map(provider => ({ id: provider?.id ?? '', name: provider?.name ?? provider?.id ?? '' }))
440
450
  : [],
@@ -1182,12 +1192,12 @@ export function apply(ctx, config = {}) {
1182
1192
  return
1183
1193
  }
1184
1194
  if (pathname === `${ROUTE_PREFIX}settings` && method === 'GET') {
1185
- jsonResponse(res, 200, { ok: true, settings: settingsSnapshot() })
1195
+ jsonResponse(res, 200, { ok: true, settings: await settingsSnapshot(url?.searchParams?.get('provider') ?? '') })
1186
1196
  return
1187
1197
  }
1188
1198
  if (pathname === `${ROUTE_PREFIX}settings` && method === 'POST') {
1189
1199
  const body = (await readJsonBody(req)) ?? {}
1190
- const next = { ...settingsSnapshot() }
1200
+ const next = { ...(await settingsSnapshot()) }
1191
1201
  if (body.modelPolicy !== undefined) {
1192
1202
  const policy = normalizeModelPolicy(body.modelPolicy)
1193
1203
  if (policy !== String(body.modelPolicy).trim()) {
@@ -1226,7 +1236,7 @@ export function apply(ctx, config = {}) {
1226
1236
  modelCalls: settings.modelCalls,
1227
1237
  })
1228
1238
  debugLog({ phase: 'settings-updated', modelPolicy: settings.modelPolicy, saved })
1229
- jsonResponse(res, 200, { ok: true, saved, settings: settingsSnapshot() })
1239
+ jsonResponse(res, 200, { ok: true, saved, settings: await settingsSnapshot() })
1230
1240
  return
1231
1241
  }
1232
1242
  if (pathname === `${ROUTE_PREFIX}state` && method === 'GET') {
@@ -1649,3 +1659,4 @@ export function apply(ctx, config = {}) {
1649
1659
 
1650
1660
 
1651
1661
 
1662
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plan-completion",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Implicit session plans with Tab-completable next-message suggestions for DeepSeek Harness",