dsh-strata-sync 0.10.7 → 0.10.9

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 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 after DSH becomes idle.
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.
@@ -48,24 +48,26 @@ The main flow appears first: **Check differences → Review changes → Back up
48
48
 
49
49
  1. Select your Strata provider and click **Save preferences**.
50
50
  2. Click **Check differences** to inspect the loaded model and proposed changes.
51
- 3. While DSH is idle, click **Back up and sync**.
51
+ 3. Click **Back up and sync**.
52
52
  4. **Start a new conversation** to use the updated model and Agent preset settings.
53
53
 
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 waits for idle and does not poll continuously.
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.
55
55
 
56
56
  ## Model and output settings
57
57
 
58
58
  | Setting | How it is chosen |
59
59
  |---|---|
60
60
  | Model and context | Strata's loaded text model and verified context capacity. |
61
- | Response output limit | Manual override, if set; otherwise the verified Strata output default. If unavailable or unlimited, use 30% of context, capped at 16,384 tokens. |
61
+ | Response output limit | Manual override, if set; otherwise the verified Strata output default. If unavailable or unlimited, use Strata's formula: 30% of context, at least 4,096 and at most 32,768 tokens. |
62
62
  | Input types | Confirmed server capabilities, such as text or text plus image. |
63
63
  | Reasoning levels | Verified model-template support; unknown templates preserve existing settings. |
64
64
  | Default model | Follows the loaded Strata model when **Follow the loaded Strata model as the default** is enabled. |
65
65
 
66
66
  Keep **Follow Strata output limit** enabled for automatic selection. Turn it off to set a manual override under **Advanced settings**. The preview shows the server default, effective value and source separately.
67
67
 
68
- When synchronizing, the plugin sets a 4,096-token summary output cap and 4,096-token compaction headroom. The effective compaction trigger is the smaller of **80% of context** and **context − response output − 4,096**. Recent-content retention is **(context − response output) × 16%**, rounded down. These are client-side budgets calculated by the plugin, not settings supplied by Strata.
68
+ The fallback follows [Strata's local output formula](https://github.com/Niko1221/Strata/blob/main/apps/host/server/agent/profile.mjs): `max(4096, min(32768, floor(context × 0.3)))`.
69
+
70
+ When synchronizing, the plugin normally sets a 4,096-token summary output cap and compaction headroom. For small windows using calculated output, both are limited to half the remaining conversation budget, rounded down; an 8K window therefore uses 2,048. The effective compaction trigger is the smaller of **80% of context** and **context − response output − headroom**. Recent-content retention is **(context − response output) × 16%**, rounded down. These compaction budgets are calculated by the plugin, not supplied by Strata.
69
71
 
70
72
  For example, with automatic output calculation, a 32K context uses a 9,830-token response allowance and an 18,842-token compaction trigger. It does **not** necessarily compact at 80%.
71
73
 
package/dist/client.js CHANGED
@@ -11,11 +11,11 @@ window.__ModuleLoader__.load({
11
11
  "Automatic calculation": {"zh-Hant":"自動計算","zh-Hans":"自动计算"},
12
12
  "Server has no fixed output limit.": {"zh-Hant":"伺服器未設定固定輸出上限。","zh-Hans":"服务器未设置固定输出上限。"},
13
13
  "Server output limit could not be verified.": {"zh-Hant":"無法確認伺服器的輸出上限。","zh-Hans":"无法确认服务器的输出上限。"},
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。明确填写的覆盖值优先。"},
14
+ "Leave blank to use the server output default; if absent or unlimited, use 30% of context, at least 4,096 and at most 32,768. An explicit override takes priority.": {"zh-Hant":"留空會採用伺服器輸出預設值;未提供或無固定上限時,使用上下文的 30%,最低 4,096、最高 32,768。明確填寫的覆寫值優先。","zh-Hans":"留空采用服务器输出默认值;未提供或无固定上限时,使用上下文的 30%,最低 4,096、最高 32,768。明确填写的覆盖值优先。"},
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. Waits for running tasks to finish; no periodic server checks.": {
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
- "DSH is running a task. Synchronization will wait until all tasks are idle.": {
243
- "zh-Hant": "DSH 正在執行工作,同步會等到所有工作閒置後進行。",
244
- "zh-Hans": "DSH 正在执行任务,同步会等到所有任务空闲后进行。"
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 供應者",
@@ -377,7 +377,7 @@ window.__ModuleLoader__.load({
377
377
  'Enabled': {'zh-Hant':'已啟用','zh-Hans':'已启用'},
378
378
  'Save option changes below before checking differences again.': {'zh-Hant':'請先在下方儲存選項變更,再重新檢查差異。','zh-Hans':'请先在下方保存选项更改,再重新检查差异。'},
379
379
  'Follow Strata output limit': {'zh-Hant':'輸出上限跟隨 Strata','zh-Hans':'输出上限跟随 Strata'},
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。'},
380
+ 'Use the verified server default. If none is available, calculate 30% of context, at least 4,096 and at most 32,768 tokens.': {'zh-Hant':'採用已確認的伺服器預設值;無固定值時,以上下文的 30% 計算,最低 4,096、最高 32,768 tokens。','zh-Hans':'采用已确认的服务器默认值;无固定值时,按上下文的 30% 计算,最低 4,096、最高 32,768 tokens。'},
381
381
  'Advanced settings': {'zh-Hant':'進階設定','zh-Hans':'高级设置'},
382
382
  'Manual output limit': {'zh-Hant':'手動輸出上限','zh-Hans':'手动输出上限'},
383
383
  'Turn off Follow Strata output limit to enter a manual override.': {'zh-Hant':'關閉「輸出上限跟隨 Strata」後,可輸入手動覆寫值。','zh-Hans':'关闭“输出上限跟随 Strata”后,可输入手动覆盖值。'},
@@ -511,7 +511,7 @@ REASONING_DEFAULT_CONFLICT: 'The provider default reasoning level is not support
511
511
  h('style',null,css+workflowCss),
512
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.'))),
513
513
  !state&&!loadError&&h('div',{className:'ss-status',role:'status'},t('Loading sync settings...')),
514
- state?.busy&&h('div',{className:'ss-status',role:'status'},t('DSH is running a task. Synchronization will wait until all tasks are idle.')),
514
+ state?.busy&&h('div',{className:'ss-status',role:'status'},t('A synchronization is applying settings. Please wait.')),
515
515
  (error||loadError)&&h('div',{className:'ss-status ss-error',role:'alert'},diagnostic(error||loadError)),
516
516
  notice&&h('div',{className:'ss-status',role:'status'},t(notice)),
517
517
  conflict&&h('div',{className:'ss-status ss-error'},
@@ -570,14 +570,14 @@ REASONING_DEFAULT_CONFLICT: 'The provider default reasoning level is not support
570
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
571
  h('div',{className:'ss-card'},
572
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.')),
573
+ h('p',{className:'ss-muted'},t('Use the verified server default. If none is available, calculate 30% of context, at least 4,096 and at most 32,768 tokens.')),
574
574
  activeBudget?outputValues(activeBudget,preview.server?.model):h('p',{className:'ss-muted'},t('Check differences to read the server and effective limits.')),
575
575
  h('details',{className:'ss-details',open:form.manual},h('summary',null,t('Advanced settings')),
576
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
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
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
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. Waits for running tasks to finish; no periodic server checks.'))),
580
+ h('p',{className:'ss-muted'},t('Runs once on the next DSH start; no periodic server checks.'))),
581
581
  h('details',{className:'ss-card ss-options','data-testid':'summary-target'},
582
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
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')),
package/dist/core.mjs CHANGED
@@ -59,18 +59,21 @@ export function normalizeBaseURL(value) {
59
59
 
60
60
  /**
61
61
  * Select the output allowance, then derive DSH's client-side compaction policy.
62
- * The 4096 headroom also matches the native pi-ai context safety margin.
62
+ * The usual 4096 headroom also matches the native pi-ai context safety margin.
63
63
  */
64
64
  export function planBudget(contextWindow, { outputTokens, serverOutputTokens } = {}) {
65
65
  if (!isContext(contextWindow) || contextWindow < 8192) {
66
66
  fail('UNSUPPORTED_CONTEXT', 'A verified context window of at least 8,192 tokens is required.');
67
67
  }
68
- const output = outputTokens ?? serverOutputTokens ?? Math.min(16384, Math.floor(contextWindow * 0.3));
68
+ const output = outputTokens ?? serverOutputTokens ?? Math.max(4096, Math.min(32768, Math.floor(contextWindow * 0.3)));
69
69
  const outputSource = outputTokens != null ? 'manual' : serverOutputTokens != null ? 'server' : 'calculated';
70
70
  if (!Number.isSafeInteger(output) || output < 1 || output >= contextWindow) {
71
71
  fail('INVALID_OUTPUT_BUDGET', 'The output budget must be a positive integer smaller than the context window.');
72
72
  }
73
- const headroomTokens = 4096;
73
+ // The 4K output floor must still leave compaction room in an 8K window.
74
+ // Keep fixed/manual budgets and the usual 4K reserve unchanged.
75
+ const headroomTokens = outputSource === 'calculated'
76
+ ? Math.min(4096, Math.floor((contextWindow - output) / 2)) : 4096;
74
77
  const thresholdRatio = 0.8;
75
78
  const retainRatio = 0.16;
76
79
  const messageBudgetTokens = contextWindow - output;
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', 'agents', 'credentials', 'agentPresets'];
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
- busy: busy() || !!writeBarrier, providers, lastPreview, lastApplied, lastError };
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: 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
- if (busy()) refuse('busy', 'A DSH task is running. Wait until all tasks are idle before syncing.');
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
- if (busy()) refuse('busy', 'A DSH task started. Sync has been deferred.');
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
- if (busy()) refuse('busy', 'A DSH task started. Sync has been deferred.');
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
- if (busy()) refuse('busy', 'A DSH task started. Sync has been deferred.');
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
- if (busy()) refuse('busy', 'Wait until all DSH tasks are idle before changing sync settings.');
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
- if (busy()) refuse('busy', 'Wait until all DSH tasks are idle before changing sync settings.');
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 (busy() || inFlight || startup.running) return;
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
- // Discovery can outlive an idle period. Busy/disposed attempts keep the one
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/idle; no recurring server polling.
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();
@@ -1,6 +1,6 @@
1
1
  # Verified model capabilities
2
2
 
3
- Capability synchronization uses the preview, backup, rollback and idle-only synchronization workflow. It does not send generation requests or alter Strata Server.
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
 
@@ -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 原有值。
@@ -48,24 +48,26 @@ dsh plugin --profile desktop add dsh-strata-sync --registry=https://registry.npm
48
48
 
49
49
  1. 选择 Strata 提供方,点击**保存偏好**。
50
50
  2. 点击**检查差异**,查看已加载模型与拟修改的设置。
51
- 3. 在 DSH 空闲时,点击**备份并同步**。
51
+ 3. 点击**备份并同步**。
52
52
  4. **开启新对话**,使用更新后的模型与 Agent 预设。
53
53
 
54
- 已有对话会保留原先选定的模型路由和预设版本。如需自动设置,可勾选**启动 DSH 时自动同步**并保存。此选项默认关闭,下次启动才生效;会等待空闲,不会持续轮询。
54
+ 已有对话会保留原先选定的模型路由和预设版本。如需自动设置,可勾选**启动 DSH 时自动同步**并保存。此选项默认关闭,下次启动才生效;可在任务执行中同步,不会持续轮询。
55
55
 
56
56
  ## 模型与输出设置
57
57
 
58
58
  | 设置 | 如何决定 |
59
59
  |---|---|
60
60
  | 模型与上下文 | 采用 Strata 已加载的文本模型及已确认的上下文容量。 |
61
- | 回复输出上限 | 优先使用手动覆盖值;否则采用已确认的 Strata 输出默认值。无法取得或未限制时,使用上下文的 30%,最多 16,384 tokens。 |
61
+ | 回复输出上限 | 优先使用手动覆盖值;否则采用已确认的 Strata 输出默认值。无法取得或未限制时,使用 Strata 官方公式:上下文的 30%,最低 4,096、最高 32,768 tokens。 |
62
62
  | 输入类型 | 采用服务器已确认的能力,例如文本或文本+图片。 |
63
63
  | 推理等级 | 根据已验证的模型模板确定;未知模板保留原有设置。 |
64
64
  | 默认模型 | 启用**默认模型跟随 Strata 已加载的模型**时,跟随服务器当前模型。 |
65
65
 
66
66
  保持**输出上限跟随 Strata**开启,即可自动选择。关闭后可在**高级设置**输入手动覆盖值。预览会分别显示服务器默认值、实际应用值及其来源。
67
67
 
68
- 同步时,插件将摘要输出上限和压缩预留空间均设为 4,096 tokens。实际压缩阈值取**上下文的 80%**与**上下文 − 回复输出预算 − 4,096**中的较小值,近期内容保留量为 **(上下文 − 回复输出预算)× 16%**,向下取整。这些是插件计算的客户端预算,并非 Strata 提供的设置。
68
+ 自动计算采用 [Strata 官方输出公式](https://github.com/Niko1221/Strata/blob/main/apps/host/server/agent/profile.mjs):`max(4096, min(32768, floor(context × 0.3)))`。
69
+
70
+ 同步时,摘要输出上限和压缩余量通常均为 4,096 tokens。小窗口使用自动计算输出时,两者最多为剩余对话预算的一半,向下取整;因此 8K 窗口使用 2,048。实际压缩阈值取**上下文的 80%**与**上下文 − 回复输出预算 − 压缩余量**中的较小值,近期内容保留量为 **(上下文 − 回复输出预算)× 16%**,向下取整。这些压缩预算由插件计算,并非 Strata 提供的设置。
69
71
 
70
72
  例如,采用自动输出计算时,32K 上下文的回复预算为 9,830 tokens,压缩阈值为 18,842 tokens,**不一定在 80% 时压缩**。
71
73
 
@@ -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 原有值。
@@ -48,24 +48,26 @@ dsh plugin --profile desktop add dsh-strata-sync --registry=https://registry.npm
48
48
 
49
49
  1. 選擇 Strata 提供方,點選**儲存偏好**。
50
50
  2. 點選**檢查差異**,查看已載入模型與預計修改的設定。
51
- 3. 在 DSH 閒置時,點選**備份並同步**。
51
+ 3. 點選**備份並同步**。
52
52
  4. **開啟新對話**,使用更新後的模型與 Agent 預設。
53
53
 
54
- 既有對話會保留原先選定的模型路由和預設版本。如需自動設定,可勾選**啟動 DSH 時自動同步**並儲存。此選項預設關閉,下次啟動才生效;會等待閒置,不會持續輪詢。
54
+ 既有對話會保留原先選定的模型路由和預設版本。如需自動設定,可勾選**啟動 DSH 時自動同步**並儲存。此選項預設關閉,下次啟動才生效;可在任務執行中同步,不會持續輪詢。
55
55
 
56
56
  ## 模型與輸出設定
57
57
 
58
58
  | 設定 | 如何決定 |
59
59
  |---|---|
60
60
  | 模型與上下文 | 採用 Strata 已載入的文字模型及已確認的上下文容量。 |
61
- | 回覆輸出上限 | 優先使用手動覆寫值;否則採用已確認的 Strata 輸出預設值。無法取得或未限制時,使用上下文的 30%,最多 16,384 tokens。 |
61
+ | 回覆輸出上限 | 優先使用手動覆寫值;否則採用已確認的 Strata 輸出預設值。無法取得或未限制時,使用 Strata 官方公式:上下文的 30%,最低 4,096、最高 32,768 tokens。 |
62
62
  | 輸入類型 | 採用伺服器已確認的能力,例如文字或文字+圖片。 |
63
63
  | 推理等級 | 依已驗證的模型範本決定;未知範本保留原有設定。 |
64
64
  | 預設模型 | 啟用**預設模型跟隨 Strata 已載入的模型**時,跟隨伺服器目前模型。 |
65
65
 
66
66
  保持**輸出上限跟隨 Strata**開啟,即可自動選擇。關閉後可在**進階設定**輸入手動覆寫值。預覽會分別顯示伺服器預設值、實際套用值及其來源。
67
67
 
68
- 同步時,外掛將摘要輸出上限和壓縮預留空間均設為 4,096 tokens。實際壓縮門檻取**上下文的 80%**與**上下文 − 回覆輸出預算 − 4,096**中的較小值,近期內容保留量為 **(上下文 − 回覆輸出預算)× 16%**,向下取整。這些是外掛計算的客戶端預算,並非 Strata 提供的設定。
68
+ 自動計算採用 [Strata 官方輸出公式](https://github.com/Niko1221/Strata/blob/main/apps/host/server/agent/profile.mjs):`max(4096, min(32768, floor(context × 0.3)))`。
69
+
70
+ 同步時,摘要輸出上限和壓縮餘裕通常均為 4,096 tokens。小視窗使用自動計算輸出時,兩者最多為剩餘對話預算的一半,向下取整;因此 8K 視窗使用 2,048。實際壓縮門檻取**上下文的 80%**與**上下文 − 回覆輸出預算 − 壓縮餘裕**中的較小值,近期內容保留量為 **(上下文 − 回覆輸出預算)× 16%**,向下取整。這些壓縮預算由外掛計算,並非 Strata 提供的設定。
69
71
 
70
72
  例如,採用自動輸出計算時,32K 上下文的回覆預算為 9,830 tokens,壓縮門檻為 18,842 tokens,**不一定在 80% 時壓縮**。
71
73
 
@@ -11,12 +11,15 @@ Validation date: 2026-10-30 (Asia/Taipei).
11
11
 
12
12
  ## Automated verification
13
13
 
14
- All **156 automated tests pass** for the current runtime. Coverage includes:
14
+ All **162 automated tests pass** for the current runtime. Coverage includes:
15
+
16
+ - Automatic output matches Strata's 30% formula with a 4,096 floor and 32,768 ceiling. Tests cover 8K through 1M contexts, rounding at the ceiling, unchanged server/manual precedence, valid small-window compaction budgets and all three UI translations. The 8K calculated-output case limits compaction headroom and summary cap to 2,048; 32K and larger retain 4,096.
15
17
 
16
18
  - 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
19
  - Native Cordis waterfall dispatch, immutable messages, concurrent summaries, duplicate hooks, cancellation, metadata failures, early stream closure, native errors and unload.
18
20
  - 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, idle guards, stale-preview rejection, concurrent edits, backup and rollback, startup synchronization, hot reload and enabled Agent preset policies.
21
+ - Preference validation, synchronization write guards, stale-preview rejection, concurrent edits, backup and rollback, startup synchronization, hot reload and enabled Agent preset policies.
22
+ - 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
23
  - Three-language settings rendering, editable prompt preservation, custom divisors, preview ceilings, default-off behavior and preference conflicts.
21
24
 
22
25
  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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-strata-sync",
3
- "version": "0.10.7",
3
+ "version": "0.10.9",
4
4
  "description": "Native DSH settings UI for synchronizing Strata Server models, context and compaction budgets",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",