dsh-strata-sync 0.10.6 → 0.10.8
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.md +5 -3
- package/dist/client.js +74 -50
- package/dist/index.js +13 -16
- package/docs/CAPABILITIES.md +1 -1
- package/docs/README.zh-CN.md +5 -3
- package/docs/README.zh-TW.md +5 -3
- package/docs/VALIDATION.md +3 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ The plugin reads Strata's model information, previews the changes, and updates D
|
|
|
11
11
|
- **Sync model settings:** context window, response output limit, and confirmed input types and reasoning levels.
|
|
12
12
|
- **Align compaction budgets:** update the matching policies in enabled DSH Agent presets.
|
|
13
13
|
- **Control summary length:** edit the supplemental prompt and adjust two target-length ratios.
|
|
14
|
-
- **Sync on startup:** optionally synchronize once
|
|
14
|
+
- **Sync on startup:** optionally synchronize once when DSH starts.
|
|
15
15
|
- **Review and recover:** preview differences, back up settings, and view the last synchronization result.
|
|
16
16
|
|
|
17
17
|
Strata Server is read-only to this plugin. Synchronization does not switch or load server models, change server settings, or send test inference requests. Unconfirmed model capabilities keep their existing DSH values.
|
|
@@ -44,12 +44,14 @@ Reopen DSH if needed to load the settings page. If the plugin is disabled, enabl
|
|
|
44
44
|
|
|
45
45
|
Open **Settings → dsh-strata-sync**:
|
|
46
46
|
|
|
47
|
+
The main flow appears first: **Check differences → Review changes → Back up and sync**. **Sync preferences** and the optional **Dynamic summary target** are collapsed below it; expand them to adjust options or edit the supplemental prompt.
|
|
48
|
+
|
|
47
49
|
1. Select your Strata provider and click **Save preferences**.
|
|
48
50
|
2. Click **Check differences** to inspect the loaded model and proposed changes.
|
|
49
|
-
3.
|
|
51
|
+
3. Click **Back up and sync**.
|
|
50
52
|
4. **Start a new conversation** to use the updated model and Agent preset settings.
|
|
51
53
|
|
|
52
|
-
Existing conversations keep their selected model route and preset revision. To make future setup automatic, enable **Automatically sync when DSH starts** and save. This option is off by default and takes effect on the next start; it
|
|
54
|
+
Existing conversations keep their selected model route and preset revision. To make future setup automatic, enable **Automatically sync when DSH starts** and save. This option is off by default and takes effect on the next start; it can run during tasks and does not poll continuously.
|
|
53
55
|
|
|
54
56
|
## Model and output settings
|
|
55
57
|
|
package/dist/client.js
CHANGED
|
@@ -13,9 +13,9 @@ window.__ModuleLoader__.load({
|
|
|
13
13
|
"Server output limit could not be verified.": {"zh-Hant":"無法確認伺服器的輸出上限。","zh-Hans":"无法确认服务器的输出上限。"},
|
|
14
14
|
"Leave blank to use the server output default; if absent or unlimited, use 30% of context, up to 16,384. An explicit override takes priority.": {"zh-Hant":"留空會採用伺服器輸出預設值;未提供或無固定上限時,使用上下文的 30%,最多 16,384。明確填寫的覆寫值優先。","zh-Hans":"留空采用服务器输出默认值;未提供或无固定上限时,使用上下文的 30%,最多 16,384。明确填写的覆盖值优先。"},
|
|
15
15
|
"Strata returned an invalid output limit. Review the server setting before syncing.": {"zh-Hant":"Strata 回傳無效的輸出上限,請先檢查伺服器設定。","zh-Hans":"Strata 返回无效的输出上限,请先检查服务器设置。"},
|
|
16
|
-
"Runs once on the next DSH start
|
|
17
|
-
"zh-Hant": "下次啟動 DSH
|
|
18
|
-
"zh-Hans": "下次启动 DSH
|
|
16
|
+
"Runs once on the next DSH start; no periodic server checks.": {
|
|
17
|
+
"zh-Hant": "下次啟動 DSH 時同步一次,不再定期檢查伺服器。",
|
|
18
|
+
"zh-Hans": "下次启动 DSH 时同步一次,不再定期检查服务器。"
|
|
19
19
|
},
|
|
20
20
|
"Image": {"zh-Hant":"圖片","zh-Hans":"图片"},
|
|
21
21
|
"View mapping": {"zh-Hant":"查看參數對應","zh-Hans":"查看参数映射"},
|
|
@@ -239,9 +239,9 @@ window.__ModuleLoader__.load({
|
|
|
239
239
|
"zh-Hant": "正在載入同步設定…",
|
|
240
240
|
"zh-Hans": "正在加载同步设置…"
|
|
241
241
|
},
|
|
242
|
-
"
|
|
243
|
-
"zh-Hant": "
|
|
244
|
-
"zh-Hans": "
|
|
242
|
+
"A synchronization is applying settings. Please wait.": {
|
|
243
|
+
"zh-Hant": "正在套用同步設定,請稍候。",
|
|
244
|
+
"zh-Hans": "正在应用同步设置,请稍候。"
|
|
245
245
|
},
|
|
246
246
|
"Strata provider": {
|
|
247
247
|
"zh-Hant": "Strata 供應者",
|
|
@@ -365,6 +365,17 @@ window.__ModuleLoader__.load({
|
|
|
365
365
|
}
|
|
366
366
|
};
|
|
367
367
|
Object.assign(translations, {
|
|
368
|
+
'Sync the model and context budgets from Strata Server to DSH.': {'zh-Hant':'將 Strata Server 的模型與上下文預算同步至 DSH。','zh-Hans':'将 Strata Server 的模型与上下文预算同步到 DSH。'},
|
|
369
|
+
'Start here': {'zh-Hant':'從這裡開始','zh-Hans':'从这里开始'},
|
|
370
|
+
'First, check differences to read the loaded model and preview the changes.': {'zh-Hant':'先點「檢查差異」,讀取目前載入的模型並預覽變更。','zh-Hans':'先点击“检查差异”,读取当前加载的模型并预览更改。'},
|
|
371
|
+
'Review changes': {'zh-Hant':'預覽變更','zh-Hans':'预览更改'},
|
|
372
|
+
'Review the changes below, then back up and sync.': {'zh-Hant':'確認下方變更後,點「備份並同步」。','zh-Hans':'确认下方更改后,点击“备份并同步”。'},
|
|
373
|
+
'Check differences before syncing.': {'zh-Hant':'檢查差異後即可同步。','zh-Hans':'检查差异后即可同步。'},
|
|
374
|
+
'Sync preferences': {'zh-Hant':'同步偏好','zh-Hans':'同步偏好'},
|
|
375
|
+
'Output limit, default model and startup sync': {'zh-Hant':'輸出上限、預設模型與啟動同步','zh-Hans':'输出上限、默认模型与启动同步'},
|
|
376
|
+
'Optional': {'zh-Hant':'選用','zh-Hans':'可选'},
|
|
377
|
+
'Enabled': {'zh-Hant':'已啟用','zh-Hans':'已启用'},
|
|
378
|
+
'Save option changes below before checking differences again.': {'zh-Hant':'請先在下方儲存選項變更,再重新檢查差異。','zh-Hans':'请先在下方保存选项更改,再重新检查差异。'},
|
|
368
379
|
'Follow Strata output limit': {'zh-Hant':'輸出上限跟隨 Strata','zh-Hans':'输出上限跟随 Strata'},
|
|
369
380
|
'Use the verified server default. If none is available, calculate 30% of context, up to 16,384 tokens.': {'zh-Hant':'採用已確認的伺服器預設值;無固定值時,以上下文的 30% 計算,最多 16,384 tokens。','zh-Hans':'采用已确认的服务器默认值;无固定值时,按上下文的 30% 计算,最多 16,384 tokens。'},
|
|
370
381
|
'Advanced settings': {'zh-Hant':'進階設定','zh-Hans':'高级设置'},
|
|
@@ -396,6 +407,7 @@ window.__ModuleLoader__.load({
|
|
|
396
407
|
thresholdRatio: 'Compaction ratio ceiling', retainRatio: 'Recent conversation retention ratio',
|
|
397
408
|
};
|
|
398
409
|
const css = `.strata-sync{max-width:880px;color:inherit;font:inherit;line-height:1.6}.strata-sync *{box-sizing:border-box}.strata-sync header{border-bottom:1px solid #8884;padding-bottom:16px;margin-bottom:20px}.strata-sync h2{margin:0;font-size:22px}.strata-sync .ss-muted{opacity:.72;font-size:13px}.strata-sync .ss-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:14px}.strata-sync label{display:flex;flex-direction:column;gap:5px;font-size:14px}.strata-sync select,.strata-sync input[type=number],.strata-sync textarea{padding:9px;border:1px solid #8886;border-radius:7px;background:transparent;color:inherit;width:100%}.strata-sync textarea{resize:vertical;min-height:112px;font:inherit;line-height:1.5}.strata-sync .ss-prompt-label{margin:12px 0 8px}.strata-sync option{color:#17252b;background:#fff}.strata-sync button{padding:9px 15px;border:1px solid #8885;border-radius:7px;background:transparent;color:inherit;cursor:pointer}.strata-sync button.primary{background:#126c65;border-color:#126c65;color:#fff}.strata-sync button:disabled{opacity:.45;cursor:default}.strata-sync .ss-actions{display:flex;gap:9px;flex-wrap:wrap;margin:18px 0}.strata-sync .ss-check{display:flex;flex-direction:row;align-items:center;margin-top:13px}.strata-sync .ss-card{padding:16px;border:1px solid #8884;border-radius:9px;margin:16px 0}.strata-sync .ss-status{padding:10px 13px;border-left:3px solid #21867b;background:#21867b12;margin:13px 0;overflow-wrap:anywhere}.strata-sync .ss-error{border-color:#ce7140;background:#ce714012}.strata-sync table{width:100%;border-collapse:collapse;font-size:14px}.strata-sync td,.strata-sync th{padding:9px 8px;text-align:left;border-bottom:1px solid #8883;overflow-wrap:anywhere}.strata-sync code{font-size:12px;overflow-wrap:anywhere}.strata-sync .ss-scroll{overflow-x:auto;container-type:inline-size;border:1px solid #8883;border-radius:8px;margin-top:16px}.strata-sync table{table-layout:fixed}.strata-sync th,.strata-sync td{vertical-align:top;padding:12px;line-height:1.5}.strata-sync thead th{font-size:12px;font-weight:600;background:#8881}.strata-sync th:first-child{width:34%}.strata-sync thead th:not(:first-child){width:33%;text-align:right}.strata-sync tbody th{font-weight:500}.strata-sync tbody tr:last-child>*{border-bottom:0}.strata-sync .ss-before,.strata-sync .ss-after{text-align:right;font-variant-numeric:tabular-nums}.strata-sync .ss-before{color:inherit;opacity:.7}.strata-sync .ss-after{background:#21867b0b;font-weight:600}.strata-sync .ss-target{display:block;font-size:11px;font-weight:400;opacity:.65;margin-top:5px;overflow-wrap:anywhere}.strata-sync .ss-values{display:flex;gap:5px;flex-wrap:wrap;justify-content:flex-end}.strata-sync .ss-chip{display:inline-block;padding:2px 7px;border:1px solid #8883;border-radius:5px;font-size:12px;font-weight:400;max-width:100%;overflow-wrap:anywhere}.strata-sync .ss-details{text-align:left;font-weight:400;font-size:12px}.strata-sync .ss-details summary{cursor:pointer;overflow-wrap:anywhere}.strata-sync .ss-details pre{white-space:pre-wrap;overflow-wrap:anywhere;margin:8px 0 0;font-size:11px;line-height:1.5}.strata-sync .ss-empty{font-weight:400;font-size:12px;opacity:.65}@container(max-width:440px){.strata-sync tbody th:first-child{width:100%}.strata-sync table,.strata-sync tbody,.strata-sync tr,.strata-sync tbody th,.strata-sync td{display:block;width:100%}.strata-sync thead{display:none}.strata-sync tbody tr{padding:10px 12px;border-bottom:1px solid #8883}.strata-sync tbody th,.strata-sync td{border:0;padding:5px 0}.strata-sync td::before{content:attr(data-label);float:left;font-size:12px;font-weight:400;opacity:.7}.strata-sync td{display:flow-root;padding:7px}.strata-sync .ss-values{max-width:72%;margin-left:auto}.strata-sync .ss-details{margin-top:24px}}`;
|
|
410
|
+
const workflowCss = ".strata-sync h3{font-size:16px;margin:0 0 8px}.strata-sync .ss-workflow{border:1px solid #21867b66;border-radius:10px;padding:20px;margin-bottom:18px}.strata-sync .ss-workflow>p:first-of-type{margin:0 0 16px}.strata-sync .ss-steps{display:flex;flex-wrap:wrap;gap:10px 20px;list-style:none;margin:0 0 20px;padding:0;font-size:13px}.strata-sync .ss-steps li{display:flex;align-items:center;gap:7px;opacity:.65}.strata-sync .ss-steps li[aria-current]{opacity:1;font-weight:600}.strata-sync .ss-step-number{display:inline-grid;place-items:center;width:25px;height:25px;flex-shrink:0;border:1px solid #8885;border-radius:50%;font-variant-numeric:tabular-nums}.strata-sync [aria-current] .ss-step-number{background:#126c65;color:#fff;border-color:#126c65}.strata-sync .ss-options>summary{cursor:pointer;font-size:14px}.strata-sync .ss-option-title::marker{color:#888}.strata-sync .ss-option-title>span:first-child{font-weight:600}.strata-sync .ss-badge{display:inline-block;margin-left:10px;padding:1px 7px;border:1px solid #8884;border-radius:5px;font-size:11px;vertical-align:middle}.strata-sync .ss-option-description{display:inline-block;margin-left:12px;opacity:.65;font-size:12px}.strata-sync button:focus-visible,.strata-sync summary:focus-visible,.strata-sync select:focus-visible{outline:2px solid #21867b;outline-offset:3px}@media(max-width:520px){.strata-sync .ss-workflow{padding:14px}.strata-sync .ss-option-description{display:block;margin-left:16px;margin-top:3px}.strata-sync .ss-steps{gap:8px 12px}}";
|
|
399
411
|
const fmt = (v) => v == null ? 'Not set' : typeof v === 'number' ? v.toLocaleString('en-US') : typeof v === 'object' ? JSON.stringify(v) : String(v);
|
|
400
412
|
const warningText = {
|
|
401
413
|
INPUT_UNCONFIRMED: 'Input types are missing or unsupported in server metadata. Existing input settings are preserved.',
|
|
@@ -496,60 +508,29 @@ REASONING_DEFAULT_CONFLICT: 'The provider default reasoning level is not support
|
|
|
496
508
|
h('div',null,h('strong',null,outputSource(budget)),h('div',{className:'ss-muted'},t('Output limit source: '))));
|
|
497
509
|
const warnings=(items)=>(items||[]).map((w,i)=>h('div',{key:i,className:'ss-status ss-error'},typeof w==='string'?(t(warningText[w])||(w.startsWith('SLOTS_')?t('Live slot information is unavailable. Using the model context reported by Strata.'):w)):JSON.stringify(w)));
|
|
498
510
|
return h('section',{className:'strata-sync','aria-label':'dsh-strata-sync'},
|
|
499
|
-
h('style',null,css),
|
|
500
|
-
h('header',null,h('h2',null,'dsh-strata-sync'),h('p',{className:'ss-muted'},t('
|
|
511
|
+
h('style',null,css+workflowCss),
|
|
512
|
+
h('header',null,h('h2',null,'dsh-strata-sync'),h('p',{className:'ss-muted'},t('Sync the model and context budgets from Strata Server to DSH.'))),
|
|
501
513
|
!state&&!loadError&&h('div',{className:'ss-status',role:'status'},t('Loading sync settings...')),
|
|
502
|
-
state?.busy&&h('div',{className:'ss-status',role:'status'},t('
|
|
514
|
+
state?.busy&&h('div',{className:'ss-status',role:'status'},t('A synchronization is applying settings. Please wait.')),
|
|
503
515
|
(error||loadError)&&h('div',{className:'ss-status ss-error',role:'alert'},diagnostic(error||loadError)),
|
|
504
516
|
notice&&h('div',{className:'ss-status',role:'status'},t(notice)),
|
|
505
517
|
conflict&&h('div',{className:'ss-status ss-error'},
|
|
506
518
|
h('p',null,t('Preferences changed in another window. Reload the latest preferences before saving; this discards your unsaved edits.')),
|
|
507
519
|
h('button',{disabled:pending,onClick:()=>action(()=>refresh(true))},t('Reload latest preferences'))),
|
|
520
|
+
h('div',{className:'ss-workflow','data-testid':'sync-workflow'},
|
|
521
|
+
h('h3',null,t('Start here')),
|
|
522
|
+
h('p',{className:'ss-muted'},t('First, check differences to read the loaded model and preview the changes.')),
|
|
523
|
+
h('ol',{className:'ss-steps','aria-label':t('Sync the model and context budgets from Strata Server to DSH.')},
|
|
524
|
+
...['Check differences','Review changes','Back up and sync'].map((label,index)=>h('li',{key:label,'aria-current':index===(preview?1:result&&!dirty?2:0)?'step':undefined},h('span',{className:'ss-step-number','aria-hidden':true},index+1),t(label)))),
|
|
508
525
|
h('label',null,t('Strata provider'),h('select',{value:form.providerId,onChange:e=>edit({providerId:e.target.value}),disabled:pending||!state},
|
|
509
526
|
h('option',{value:''},t('Choose an existing provider')),...(state?.providers||[]).map(p=>h('option',{key:p.id,value:p.id},p.id)))),
|
|
510
527
|
h('p',{className:'ss-muted'},t('Uses the URL and credentials already configured in DSH. API keys are never displayed or copied here.')),
|
|
511
|
-
h('div',{className:'ss-card'},
|
|
512
|
-
h('label',{className:'ss-check'},h('input',{type:'checkbox',checked:!form.manual,'aria-label':t('Follow Strata output limit'),onChange:e=>edit({manual:!e.target.checked}),disabled:pending||!state}),t('Follow Strata output limit')),
|
|
513
|
-
h('p',{className:'ss-muted'},t('Use the verified server default. If none is available, calculate 30% of context, up to 16,384 tokens.')),
|
|
514
|
-
activeBudget?outputValues(activeBudget,preview.server?.model):h('p',{className:'ss-muted'},t('Check differences to read the server and effective limits.')),
|
|
515
|
-
h('details',{className:'ss-details',open:form.manual},h('summary',null,t('Advanced settings')),
|
|
516
|
-
h('label',null,t('Manual output limit'),h('input',{type:'number',min:1,step:1,value:form.output,onChange:e=>edit({output:e.target.value}),disabled:pending||!state||!form.manual})),
|
|
517
|
-
h('p',{className:'ss-muted'},t(form.manual?'A manual limit takes priority over the server default and automatic calculation.':'Turn off Follow Strata output limit to enter a manual override.')))),
|
|
518
|
-
h('label',{className:'ss-check'},h('input',{type:'checkbox',checked:form.follow,onChange:e=>edit({follow:e.target.checked}),disabled:pending||!state}),t('Follow the loaded Strata model as the default')),
|
|
519
|
-
h('label',{className:'ss-check'},h('input',{type:'checkbox',checked:form.auto,onChange:e=>edit({auto:e.target.checked}),disabled:pending||!state}),t('Automatically sync when DSH starts')),
|
|
520
|
-
h('p',{className:'ss-muted'},t('Runs once on the next DSH start. Waits for running tasks to finish; no periodic server checks.')),
|
|
521
|
-
h('div',{className:'ss-card','data-testid':'summary-target'},
|
|
522
|
-
h('strong',null,t('Dynamic summary target')),
|
|
523
|
-
h('label',{className:'ss-check'},h('input',{type:'checkbox','data-testid':'summary-guidance-toggle','aria-label':t('Enable summary guidance'),checked:form.summaryGuidance,onChange:e=>edit({summaryGuidance:e.target.checked}),disabled:pending||!state}),t('Enable summary guidance')),
|
|
524
|
-
h('div',{className:'ss-grid'},
|
|
525
|
-
h('label',null,t('Output budget divisor (estimated budget ÷ O)'),h('input',{type:'number','data-testid':'summary-output-divisor','aria-label':t('Output budget divisor (estimated budget ÷ O)'),'aria-invalid':!isDivisor(draft.summaryOutputDivisor),min:1,max:Number.MAX_SAFE_INTEGER,step:'any',value:form.summaryOutputDivisor,onChange:e=>edit({summaryOutputDivisor:e.target.value}),disabled:pending||!state})),
|
|
526
|
-
h('label',null,t('Source length divisor (source estimate ÷ S)'),h('input',{type:'number','data-testid':'summary-source-divisor','aria-label':t('Source length divisor (source estimate ÷ S)'),'aria-invalid':!isDivisor(draft.summarySourceDivisor),min:1,max:Number.MAX_SAFE_INTEGER,step:'any',value:form.summarySourceDivisor,onChange:e=>edit({summarySourceDivisor:e.target.value}),disabled:pending||!state}))),
|
|
527
|
-
state&&!validDivisor&&h('p',{className:'ss-status ss-error',role:'alert'},t('Enter a finite divisor of at least 1; decimals are allowed.')),
|
|
528
|
-
h('label',{className:'ss-prompt-label'},t('Supplemental summary prompt'),h('textarea',{'data-testid':'summary-prompt','aria-label':t('Supplemental summary prompt'),'aria-invalid':!validPrompt,rows:5,value:form.summaryPrompt,onChange:e=>edit({summaryPrompt:e.target.value}),disabled:pending||!state,spellCheck:false})),
|
|
529
|
-
state&&!validPrompt&&h('p',{className:'ss-status ss-error',role:'alert'},t('Use 1–2,048 characters and include {target}.')),
|
|
530
|
-
h('button',{type:'button','data-testid':'summary-prompt-reset',disabled:pending||!state?.defaultSummaryPromptTemplate||form.summaryPrompt===state.defaultSummaryPromptTemplate,onClick:()=>edit({summaryPrompt:state.defaultSummaryPromptTemplate})},t('Restore default prompt')),
|
|
531
|
-
h('p',{className:'ss-muted'},t("Appended to DSH's native prompt; {target} becomes the calculated token target. Save preferences to apply.")),
|
|
532
|
-
summaryTargetCeiling!==null&&(summaryTargetCeiling>=256
|
|
533
|
-
? h('p',{'data-testid':'summary-target-ceiling'},t('Maximum target for this preview: '),format(summaryTargetCeiling),' tokens')
|
|
534
|
-
: h('p',{className:'ss-status','data-testid':'summary-target-native'},t('This preview is below the 256-token minimum. The native summary prompt remains unchanged.'))),
|
|
535
|
-
h('details',{className:'ss-details','data-testid':'summary-details'},h('summary',null,t('Calculation and limits')),
|
|
536
|
-
h('p',{className:'ss-muted'},t('Target = floor(min(estimated effective output budget / O, selected content estimate / S)). Default divisors: 2 and 2 (50% each).')),
|
|
537
|
-
h('p',{className:'ss-muted'},t('The preview uses the proposed summary cap. Actual targets also account for the estimated remaining context and source length; they may be lower.'),' ',t('When guidance cannot be applied, DSH keeps its native summary behavior.')))),
|
|
538
528
|
h('div',{className:'ss-actions'},
|
|
539
|
-
h('button',{disabled:pending||!state||!form.providerId
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
}
|
|
544
|
-
h('button',{disabled:pending||!state||!form.providerId||dirty||conflict||!valid,onClick:()=>action(async()=>{setPreview(await rpc('preview',{providerId:form.providerId}));await refresh();})},pending?t('Working...'):t('Check differences')),
|
|
545
|
-
h('button',{className:'primary',disabled:pending||!state||dirty||conflict||!preview||state.busy||!preview.changes?.length,onClick:()=>action(async()=>{
|
|
546
|
-
const r=await rpc('apply',{fingerprint:preview.fingerprint,providerId:form.providerId});
|
|
547
|
-
const view=r.preview||preview;
|
|
548
|
-
setResult({at:new Date().toISOString(),modelId:view.server?.model?.id,providerId:view.providerId||form.providerId,budget:view.budget,capabilityStatus:view.capabilityStatus,serverOutputTokens:view.server?.model?.serverOutputTokens,outputLimitStatus:view.server?.model?.outputLimitStatus,backupPath:r.backupPath,applied:r.applied,warnings:view.server?.warnings});
|
|
549
|
-
setPreview(null);setNotice(r.applied?'Synced and backed up the original settings. Start a new conversation to use the updated compaction policy.':'Settings already match. No changes needed.');await refresh();
|
|
550
|
-
})},t('Back up and sync'))),
|
|
551
|
-
dirty&&!conflict&&h('p',{className:'ss-muted'},t('Save preferences before checking for differences.')),
|
|
552
|
-
preview&&h('div',{className:'ss-card'},
|
|
529
|
+
h('button',{className:preview?'':'primary','data-testid':'check-differences',disabled:pending||!state||!form.providerId||dirty||conflict||!valid,onClick:()=>action(async()=>{setPreview(await rpc('preview',{providerId:form.providerId}));await refresh();})},pending?t('Working...'):t('Check differences'))),
|
|
530
|
+
dirty&&!conflict&&h('p',{className:'ss-status'},t('Save option changes below before checking differences again.')),
|
|
531
|
+
preview&&h('div',{className:'ss-card','data-testid':'sync-preview'},
|
|
532
|
+
h('h3',null,t('Review changes')),
|
|
533
|
+
preview.changes?.length>0&&h('p',{className:'ss-muted'},t('Review the changes below, then back up and sync.')),
|
|
553
534
|
h('strong',null,'Strata Server'),h('p',null,h('code',null,preview.server?.model?.id)),
|
|
554
535
|
h('div',{className:'ss-grid'},
|
|
555
536
|
h('div',null,h('strong',null,format(preview.budget?.contextWindow)),h('div',{className:'ss-muted'},t('Context tokens'))),
|
|
@@ -570,6 +551,14 @@ REASONING_DEFAULT_CONFLICT: 'The provider default reasoning level is not support
|
|
|
570
551
|
!preview.changes?.length&&alignment(preview),
|
|
571
552
|
h('p',null,t('Effective compaction threshold: '),format(preview.budget?.thresholdTokens),' tokens (',preview.budget?.thresholdPercent?.toFixed(2),'%)'),
|
|
572
553
|
h('p',{className:'ss-muted'},t('Only DSH settings are updated. Sync does not switch server models, change Strata configuration, or send inference requests. Start a new conversation after syncing.'))),
|
|
554
|
+
h('div',{className:'ss-actions','data-testid':'sync-apply'},
|
|
555
|
+
h('button',{className:preview?.changes?.length?'primary':'',disabled:pending||!state||dirty||conflict||!preview||state.busy||!preview.changes?.length,onClick:()=>action(async()=>{
|
|
556
|
+
const r=await rpc('apply',{fingerprint:preview.fingerprint,providerId:form.providerId});
|
|
557
|
+
const view=r.preview||preview;
|
|
558
|
+
setResult({at:new Date().toISOString(),modelId:view.server?.model?.id,providerId:view.providerId||form.providerId,budget:view.budget,capabilityStatus:view.capabilityStatus,serverOutputTokens:view.server?.model?.serverOutputTokens,outputLimitStatus:view.server?.model?.outputLimitStatus,backupPath:r.backupPath,applied:r.applied,warnings:view.server?.warnings});
|
|
559
|
+
setPreview(null);setNotice(r.applied?'Synced and backed up the original settings. Start a new conversation to use the updated compaction policy.':'Settings already match. No changes needed.');await refresh();
|
|
560
|
+
})},t('Back up and sync'))),
|
|
561
|
+
!preview&&!result&&h('p',{className:'ss-muted'},t('Check differences before syncing.'))),
|
|
573
562
|
displayedResult?.budget&&h('div',{className:'ss-card','data-testid':'sync-result'},
|
|
574
563
|
h('strong',null,t('Latest synchronization result')),h('p',{className:'ss-muted'},t('This records the last synchronization, not a live server check.')),
|
|
575
564
|
h('p',null,t('Model'),': ',h('code',null,displayedResult.modelId)),h('p',null,t('Context window'),': ',format(displayedResult.budget.contextWindow)),
|
|
@@ -577,6 +566,41 @@ REASONING_DEFAULT_CONFLICT: 'The provider default reasoning level is not support
|
|
|
577
566
|
h('p',null,t(displayedResult.backupPath?'Backup created':'No settings changed; no new backup was needed.')),
|
|
578
567
|
displayedResult.backupPath&&h('p',{className:'ss-muted'},t('Original settings backup: '),h('code',null,displayedResult.backupPath)),
|
|
579
568
|
h('p',{className:'ss-muted'},t('Last sync: '),format(displayedResult.at)),...warnings(displayedResult.warnings)),
|
|
569
|
+
h('details',{className:'ss-card ss-options','data-testid':'sync-preferences'},
|
|
570
|
+
h('summary',{className:'ss-option-title'},h('span',null,t('Sync preferences')),h('span',{className:'ss-option-description'},t('Output limit, default model and startup sync'))),
|
|
571
|
+
h('div',{className:'ss-card'},
|
|
572
|
+
h('label',{className:'ss-check'},h('input',{type:'checkbox',checked:!form.manual,'aria-label':t('Follow Strata output limit'),onChange:e=>edit({manual:!e.target.checked}),disabled:pending||!state}),t('Follow Strata output limit')),
|
|
573
|
+
h('p',{className:'ss-muted'},t('Use the verified server default. If none is available, calculate 30% of context, up to 16,384 tokens.')),
|
|
574
|
+
activeBudget?outputValues(activeBudget,preview.server?.model):h('p',{className:'ss-muted'},t('Check differences to read the server and effective limits.')),
|
|
575
|
+
h('details',{className:'ss-details',open:form.manual},h('summary',null,t('Advanced settings')),
|
|
576
|
+
h('label',null,t('Manual output limit'),h('input',{type:'number',min:1,step:1,value:form.output,onChange:e=>edit({output:e.target.value}),disabled:pending||!state||!form.manual})),
|
|
577
|
+
h('p',{className:'ss-muted'},t(form.manual?'A manual limit takes priority over the server default and automatic calculation.':'Turn off Follow Strata output limit to enter a manual override.')))),
|
|
578
|
+
h('label',{className:'ss-check'},h('input',{type:'checkbox',checked:form.follow,onChange:e=>edit({follow:e.target.checked}),disabled:pending||!state}),t('Follow the loaded Strata model as the default')),
|
|
579
|
+
h('label',{className:'ss-check'},h('input',{type:'checkbox',checked:form.auto,onChange:e=>edit({auto:e.target.checked}),disabled:pending||!state}),t('Automatically sync when DSH starts')),
|
|
580
|
+
h('p',{className:'ss-muted'},t('Runs once on the next DSH start; no periodic server checks.'))),
|
|
581
|
+
h('details',{className:'ss-card ss-options','data-testid':'summary-target'},
|
|
582
|
+
h('summary',{className:'ss-option-title'},h('span',null,t('Dynamic summary target')),h('span',{className:'ss-badge'},t(form.summaryGuidance?'Enabled':'Optional'))),
|
|
583
|
+
h('label',{className:'ss-check'},h('input',{type:'checkbox','data-testid':'summary-guidance-toggle','aria-label':t('Enable summary guidance'),checked:form.summaryGuidance,onChange:e=>edit({summaryGuidance:e.target.checked}),disabled:pending||!state}),t('Enable summary guidance')),
|
|
584
|
+
h('div',{className:'ss-grid'},
|
|
585
|
+
h('label',null,t('Output budget divisor (estimated budget ÷ O)'),h('input',{type:'number','data-testid':'summary-output-divisor','aria-label':t('Output budget divisor (estimated budget ÷ O)'),'aria-invalid':!isDivisor(draft.summaryOutputDivisor),min:1,max:Number.MAX_SAFE_INTEGER,step:'any',value:form.summaryOutputDivisor,onChange:e=>edit({summaryOutputDivisor:e.target.value}),disabled:pending||!state})),
|
|
586
|
+
h('label',null,t('Source length divisor (source estimate ÷ S)'),h('input',{type:'number','data-testid':'summary-source-divisor','aria-label':t('Source length divisor (source estimate ÷ S)'),'aria-invalid':!isDivisor(draft.summarySourceDivisor),min:1,max:Number.MAX_SAFE_INTEGER,step:'any',value:form.summarySourceDivisor,onChange:e=>edit({summarySourceDivisor:e.target.value}),disabled:pending||!state}))),
|
|
587
|
+
state&&!validDivisor&&h('p',{className:'ss-status ss-error',role:'alert'},t('Enter a finite divisor of at least 1; decimals are allowed.')),
|
|
588
|
+
h('label',{className:'ss-prompt-label'},t('Supplemental summary prompt'),h('textarea',{'data-testid':'summary-prompt','aria-label':t('Supplemental summary prompt'),'aria-invalid':!validPrompt,rows:5,value:form.summaryPrompt,onChange:e=>edit({summaryPrompt:e.target.value}),disabled:pending||!state,spellCheck:false})),
|
|
589
|
+
state&&!validPrompt&&h('p',{className:'ss-status ss-error',role:'alert'},t('Use 1–2,048 characters and include {target}.')),
|
|
590
|
+
h('button',{type:'button','data-testid':'summary-prompt-reset',disabled:pending||!state?.defaultSummaryPromptTemplate||form.summaryPrompt===state.defaultSummaryPromptTemplate,onClick:()=>edit({summaryPrompt:state.defaultSummaryPromptTemplate})},t('Restore default prompt')),
|
|
591
|
+
h('p',{className:'ss-muted'},t("Appended to DSH's native prompt; {target} becomes the calculated token target. Save preferences to apply.")),
|
|
592
|
+
summaryTargetCeiling!==null&&(summaryTargetCeiling>=256
|
|
593
|
+
? h('p',{'data-testid':'summary-target-ceiling'},t('Maximum target for this preview: '),format(summaryTargetCeiling),' tokens')
|
|
594
|
+
: h('p',{className:'ss-status','data-testid':'summary-target-native'},t('This preview is below the 256-token minimum. The native summary prompt remains unchanged.'))),
|
|
595
|
+
h('details',{className:'ss-details','data-testid':'summary-details'},h('summary',null,t('Calculation and limits')),
|
|
596
|
+
h('p',{className:'ss-muted'},t('Target = floor(min(estimated effective output budget / O, selected content estimate / S)). Default divisors: 2 and 2 (50% each).')),
|
|
597
|
+
h('p',{className:'ss-muted'},t('The preview uses the proposed summary cap. Actual targets also account for the estimated remaining context and source length; they may be lower.'),' ',t('When guidance cannot be applied, DSH keeps its native summary behavior.')))),
|
|
598
|
+
h('div',{className:'ss-actions'},
|
|
599
|
+
h('button',{className:dirty?'primary':'',disabled:pending||!state||!form.providerId||!valid||!dirty||conflict,onClick:()=>action(async()=>{
|
|
600
|
+
const changes=Object.fromEntries(Object.entries(values(formRef.current)).filter(([key,value])=>!Object.is(value,baseline.current.values[key])));
|
|
601
|
+
const s=await rpc('configure',{...changes,expectedRevision:baseline.current.revision});
|
|
602
|
+
requestSeq.current++;acceptStatus(s,true);setPreview(null);setNotice('Sync preferences saved.');
|
|
603
|
+
})},t('Save preferences'))),
|
|
580
604
|
state?.lastError&&h('div',{className:'ss-status ss-error'},diagnostic(typeof state.lastError==='string'?state.lastError:state.lastError.message)));
|
|
581
605
|
}
|
|
582
606
|
function apply(ctx) {
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import z from '@deepseek-ai/schemastery';
|
|
|
5
5
|
import { discoverStrata, planBudget, StrataSyncError } from './core.mjs';
|
|
6
6
|
import { DEFAULT_SUMMARY_TEMPLATE, validSummaryDivisor, validSummaryTemplate, installSummaryGuidance } from './summary.mjs';
|
|
7
7
|
|
|
8
|
-
export const inject = ['configEditor', 'connection', '
|
|
8
|
+
export const inject = ['configEditor', 'connection', 'credentials', 'agentPresets'];
|
|
9
9
|
export const Config = z.object({
|
|
10
10
|
providerId: z.string().default(''),
|
|
11
11
|
autoSync: z.boolean().default(false),
|
|
@@ -134,10 +134,6 @@ export function apply(ctx, initialConfig) {
|
|
|
134
134
|
if (candidates.length !== 1) refuse('provider-required', 'Choose which existing provider connects to Strata Server.');
|
|
135
135
|
return candidates[0];
|
|
136
136
|
}
|
|
137
|
-
function busy() {
|
|
138
|
-
// DSH 0.2.1-rc.3 reports maintenance as idle while manual compaction can still run.
|
|
139
|
-
return ctx.agents.list().some((agent) => agent.status !== 'idle' || agent.phase?.kind === 'maintenance');
|
|
140
|
-
}
|
|
141
137
|
function refreshSettings() {
|
|
142
138
|
settings = { providerId: '', autoSync: false, followLoadedModel: true, ...entry(PACKAGE).options.config };
|
|
143
139
|
}
|
|
@@ -160,7 +156,8 @@ export function apply(ctx, initialConfig) {
|
|
|
160
156
|
});
|
|
161
157
|
} catch { /* The page remains usable when the required provider is absent. */ }
|
|
162
158
|
return { ...publicSettings(), defaultSummaryPromptTemplate: DEFAULT_SUMMARY_TEMPLATE, settingsRevision: hash(publicSettings()),
|
|
163
|
-
|
|
159
|
+
// UI busy describes a configuration write, never a running Agent task.
|
|
160
|
+
busy: !!writeBarrier, providers, lastPreview, lastApplied, lastError };
|
|
164
161
|
}
|
|
165
162
|
async function credential(providerId, route) {
|
|
166
163
|
if (route.apiKeyEnv) {
|
|
@@ -272,7 +269,7 @@ export function apply(ctx, initialConfig) {
|
|
|
272
269
|
const warnings = [...discovered.warnings ?? [], ...(before.owners.some((owner) => owner.targets.some((target) => target.config.auto === false)) ? ['DSH_AUTO_COMPACTION_DISABLED'] : [])];
|
|
273
270
|
const presetIds = before.owners.flatMap((owner) => owner.presetId ? [owner.presetId] : []);
|
|
274
271
|
const view = { providerId, server: { baseURL: discovered.baseURL, model: discovered.model, warnings }, budget, current, compactions, capabilityEdits, capabilityStatus,
|
|
275
|
-
presetIds, needsNewConversation: presetIds.length > 0, changes, fingerprint, busy:
|
|
272
|
+
presetIds, needsNewConversation: presetIds.length > 0, changes, fingerprint, busy: !!writeBarrier };
|
|
276
273
|
previewSnapshots.set(view, before);
|
|
277
274
|
lastPreview = view;
|
|
278
275
|
lastError = null;
|
|
@@ -302,7 +299,7 @@ export function apply(ctx, initialConfig) {
|
|
|
302
299
|
ensureOpen();
|
|
303
300
|
payload(request, ['fingerprint', 'providerId', 'outputTokens']);
|
|
304
301
|
if (typeof request.fingerprint !== 'string' || !request.fingerprint) refuse('preview-required', 'Preview the proposed changes before applying them.');
|
|
305
|
-
|
|
302
|
+
|
|
306
303
|
const view = await preview({ ...(request.providerId ? { providerId: request.providerId } : {}), ...(own(request, 'outputTokens') ? { outputTokens: request.outputTokens } : {}) });
|
|
307
304
|
ensureOpen();
|
|
308
305
|
if (view.fingerprint !== request.fingerprint) refuse('stale-preview', 'The server or DSH settings changed. Review a fresh preview.');
|
|
@@ -312,7 +309,7 @@ export function apply(ctx, initialConfig) {
|
|
|
312
309
|
lastApplied = appliedSummary(view, null, false);
|
|
313
310
|
return { applied: false, backupPath: null, preview: view };
|
|
314
311
|
}
|
|
315
|
-
|
|
312
|
+
|
|
316
313
|
writeBarrier = new Promise((resolve) => { releaseWrite = resolve; });
|
|
317
314
|
let backupPath;
|
|
318
315
|
const completed = [];
|
|
@@ -320,7 +317,7 @@ export function apply(ctx, initialConfig) {
|
|
|
320
317
|
backupPath = await backup();
|
|
321
318
|
ensureOpen();
|
|
322
319
|
assertSnapshot(before);
|
|
323
|
-
|
|
320
|
+
|
|
324
321
|
const modelId = view.server.model.id;
|
|
325
322
|
const llmNext = clone(before.llm);
|
|
326
323
|
const route = llmNext.providers[view.providerId];
|
|
@@ -354,7 +351,7 @@ export function apply(ctx, initialConfig) {
|
|
|
354
351
|
if (hash(original) === hash(next)) continue;
|
|
355
352
|
await ctx.configEditor.edit(entry(name, id), (current) => {
|
|
356
353
|
ensureOpen();
|
|
357
|
-
|
|
354
|
+
|
|
358
355
|
// ConfigEditor may queue this callback behind another settings edit.
|
|
359
356
|
// Recheck every owner when our first write actually begins.
|
|
360
357
|
if (completed.length === 0) assertSnapshot(before);
|
|
@@ -393,7 +390,7 @@ export function apply(ctx, initialConfig) {
|
|
|
393
390
|
if (typeof request.expectedRevision !== 'string' || request.expectedRevision !== hash(publicSettings())) {
|
|
394
391
|
refuse('stale-settings', 'Sync preferences changed. Reload the latest preferences before saving.');
|
|
395
392
|
}
|
|
396
|
-
|
|
393
|
+
|
|
397
394
|
const next = { ...settings };
|
|
398
395
|
if (own(request, 'providerId')) {
|
|
399
396
|
if (typeof request.providerId !== 'string') refuse('invalid-request', 'Provider must be a string.');
|
|
@@ -432,7 +429,7 @@ export function apply(ctx, initialConfig) {
|
|
|
432
429
|
if (hash(current) !== hash(original) || hash(publicSettings(current)) !== request.expectedRevision) {
|
|
433
430
|
refuse('stale-settings', 'Sync preferences changed while saving. Reload the latest preferences.');
|
|
434
431
|
}
|
|
435
|
-
|
|
432
|
+
|
|
436
433
|
return next;
|
|
437
434
|
});
|
|
438
435
|
settings = next;
|
|
@@ -517,7 +514,7 @@ export function apply(ctx, initialConfig) {
|
|
|
517
514
|
try {
|
|
518
515
|
refreshSettings();
|
|
519
516
|
if (!settings.autoSync) { startup.pending = false; clearInterval(timer); return; }
|
|
520
|
-
if (
|
|
517
|
+
if (inFlight || startup.running) return;
|
|
521
518
|
} catch (error) {
|
|
522
519
|
// The host can withdraw a required entry during reload. Do not leave an
|
|
523
520
|
// unhandled timer rejection or retry an unsupported profile indefinitely.
|
|
@@ -526,7 +523,7 @@ export function apply(ctx, initialConfig) {
|
|
|
526
523
|
clearInterval(timer);
|
|
527
524
|
return;
|
|
528
525
|
}
|
|
529
|
-
//
|
|
526
|
+
// Concurrent sync/disposed attempts keep the one
|
|
530
527
|
// startup operation pending; successful/no-op and terminal errors consume it.
|
|
531
528
|
startup.running = true;
|
|
532
529
|
let deferred = false;
|
|
@@ -548,7 +545,7 @@ export function apply(ctx, initialConfig) {
|
|
|
548
545
|
// edits are rejected as nested HMR transactions. Mirror Hmr.watchConfig():
|
|
549
546
|
// detach only timer creation, while every write still uses ConfigEditor and
|
|
550
547
|
// its normal runExclusive queue. `executing` is a version-specific host seam.
|
|
551
|
-
// This timer only waits for startup readiness
|
|
548
|
+
// This timer only waits for startup readiness; no recurring server polling.
|
|
552
549
|
const createTimer = () => setInterval(() => { void poll(); }, 1_000);
|
|
553
550
|
const executing = ctx.get('hmr')?.executing;
|
|
554
551
|
if (startup.pending) timer = typeof executing?.exit === 'function' ? executing.exit(createTimer) : createTimer();
|
package/docs/CAPABILITIES.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Verified model capabilities
|
|
2
2
|
|
|
3
|
-
Capability synchronization uses the preview, backup, rollback and
|
|
3
|
+
Capability synchronization uses the preview, backup, rollback and coordinated configuration-write workflow. It does not send generation requests or alter Strata Server.
|
|
4
4
|
|
|
5
5
|
## Input types
|
|
6
6
|
|
package/docs/README.zh-CN.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
- **同步模型设置:**上下文窗口、回复输出上限,以及已确认的输入类型和推理等级。
|
|
12
12
|
- **对齐压缩预算:**更新已启用的 DSH Agent 预设中,对应模型的压缩策略。
|
|
13
13
|
- **控制摘要长度:**编辑补充提示词,调整两项目标长度比例。
|
|
14
|
-
- **启动时同步:**可选择在 DSH
|
|
14
|
+
- **启动时同步:**可选择在 DSH 启动后同步一次。
|
|
15
15
|
- **预览与恢复:**检查差异、备份设置,并查看最近一次同步结果。
|
|
16
16
|
|
|
17
17
|
插件只读访问 Strata Server。同步不会切换或加载服务器模型、修改服务器设置,也不会发送测试推理请求。无法确认的模型能力会保留 DSH 原有值。
|
|
@@ -44,12 +44,14 @@ dsh plugin --profile desktop add dsh-strata-sync --registry=https://registry.npm
|
|
|
44
44
|
|
|
45
45
|
打开**设置 → dsh-strata-sync**:
|
|
46
46
|
|
|
47
|
+
页面上方是主要流程:**检查差异 → 预览更改 → 备份并同步**。下方的**同步偏好**与可选的**动态摘要目标**默认折叠;展开后即可调整选项或编辑摘要补充提示词。
|
|
48
|
+
|
|
47
49
|
1. 选择 Strata 提供方,点击**保存偏好**。
|
|
48
50
|
2. 点击**检查差异**,查看已加载模型与拟修改的设置。
|
|
49
|
-
3.
|
|
51
|
+
3. 点击**备份并同步**。
|
|
50
52
|
4. **开启新对话**,使用更新后的模型与 Agent 预设。
|
|
51
53
|
|
|
52
|
-
已有对话会保留原先选定的模型路由和预设版本。如需自动设置,可勾选**启动 DSH
|
|
54
|
+
已有对话会保留原先选定的模型路由和预设版本。如需自动设置,可勾选**启动 DSH 时自动同步**并保存。此选项默认关闭,下次启动才生效;可在任务执行中同步,不会持续轮询。
|
|
53
55
|
|
|
54
56
|
## 模型与输出设置
|
|
55
57
|
|
package/docs/README.zh-TW.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
- **同步模型設定:**上下文視窗、回覆輸出上限,以及已確認的輸入類型和推理等級。
|
|
12
12
|
- **對齊壓縮預算:**更新已啟用的 DSH Agent 預設中,對應模型的壓縮政策。
|
|
13
13
|
- **控制摘要長度:**編輯補充提示詞,調整兩項目標長度比例。
|
|
14
|
-
- **啟動時同步:**可選擇在 DSH
|
|
14
|
+
- **啟動時同步:**可選擇在 DSH 啟動後同步一次。
|
|
15
15
|
- **預覽與復原:**檢查差異、備份設定,並查看最近一次同步結果。
|
|
16
16
|
|
|
17
17
|
外掛僅以唯讀方式存取 Strata Server。同步不會切換或載入伺服器模型、修改伺服器設定,也不會傳送測試推論請求。無法確認的模型能力會保留 DSH 原有值。
|
|
@@ -44,12 +44,14 @@ dsh plugin --profile desktop add dsh-strata-sync --registry=https://registry.npm
|
|
|
44
44
|
|
|
45
45
|
開啟**設定 → dsh-strata-sync**:
|
|
46
46
|
|
|
47
|
+
頁面上方是主要流程:**檢查差異 → 預覽變更 → 備份並同步**。下方的**同步偏好**與選用的**動態摘要目標**預設收合;展開後即可調整選項或編輯摘要補充提示詞。
|
|
48
|
+
|
|
47
49
|
1. 選擇 Strata 提供方,點選**儲存偏好**。
|
|
48
50
|
2. 點選**檢查差異**,查看已載入模型與預計修改的設定。
|
|
49
|
-
3.
|
|
51
|
+
3. 點選**備份並同步**。
|
|
50
52
|
4. **開啟新對話**,使用更新後的模型與 Agent 預設。
|
|
51
53
|
|
|
52
|
-
既有對話會保留原先選定的模型路由和預設版本。如需自動設定,可勾選**啟動 DSH
|
|
54
|
+
既有對話會保留原先選定的模型路由和預設版本。如需自動設定,可勾選**啟動 DSH 時自動同步**並儲存。此選項預設關閉,下次啟動才生效;可在任務執行中同步,不會持續輪詢。
|
|
53
55
|
|
|
54
56
|
## 模型與輸出設定
|
|
55
57
|
|
package/docs/VALIDATION.md
CHANGED
|
@@ -11,12 +11,13 @@ Validation date: 2026-10-30 (Asia/Taipei).
|
|
|
11
11
|
|
|
12
12
|
## Automated verification
|
|
13
13
|
|
|
14
|
-
All **
|
|
14
|
+
All **160 automated tests pass** for the current runtime. Coverage includes:
|
|
15
15
|
|
|
16
16
|
- Actual pi-ai context clamping across multiple input sizes, near-full and exhausted context, independent source length, additional prompt costs, disabled-mode zero discovery and restored native requests.
|
|
17
17
|
- Native Cordis waterfall dispatch, immutable messages, concurrent summaries, duplicate hooks, cancellation, metadata failures, early stream closure, native errors and unload.
|
|
18
18
|
- Verified model discovery, selected-provider/loaded-model matching, alias validation, input types, model-specific reasoning choices and eight mocked SDK wire cases.
|
|
19
|
-
- Preference validation,
|
|
19
|
+
- Preference validation, synchronization write guards, stale-preview rejection, concurrent edits, backup and rollback, startup synchronization, hot reload and enabled Agent preset policies.
|
|
20
|
+
- Synchronization during suspended generation and summary streams, including maintenance status, uses synthetic responses with a mocked ConfigEditor. Requests complete unchanged; simultaneous sync is rejected, new steps wait during writes, and both success and rollback release the barrier. These tests do not exercise a live desktop task or native ConfigEditor reload during a real model stream.
|
|
20
21
|
- Three-language settings rendering, editable prompt preservation, custom divisors, preview ceilings, default-off behavior and preference conflicts.
|
|
21
22
|
|
|
22
23
|
The package verifier checks the public file list, English root README selection, translated documentation links, entry points, source/build consistency and publishing metadata. It excludes private configuration, user conversations, credentials, backups and the native-prompt test fixture from the public package.
|