@master0071/dsh-systemone 0.7.0 → 0.7.2

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.
Files changed (3) hide show
  1. package/README.md +6 -3
  2. package/lib/client.js +163 -73
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -432,7 +432,7 @@ refs:
432
432
 
433
433
  ## 添加自定义场景
434
434
 
435
- **推荐用图形配置页**:插件 → @master0071/dsh-systemone → systemone → 「自定义场景」卡片,粘贴 JSON 即可。编辑框会实时校验(JSON 语法、`id`/`title`/`questions`、问题类型与选项数量),不通过时保存按钮禁用并给出具体原因;保存后场景库立即重建,新场景的 id 也会自动出现在「固定场景」的候选列表里。
435
+ **推荐用图形配置页**:插件 → @master0071/dsh-systemone → systemone → 「自定义场景」卡片,粘贴 JSON 即可。编辑框会实时校验(JSON 语法、`id`/`title`/`questions`、问题类型与选项数量、`patch` 的目标是否存在),不通过时保存按钮禁用并给出具体原因;保存后场景库立即重建,新场景的 id 也会自动出现在「固定场景」的候选列表里。
436
436
 
437
437
  卡片右上角的**「插入意图识别模板」**会填入下面这个通用意图识别场景,改 `criteria` 即可用。
438
438
 
@@ -440,6 +440,7 @@ refs:
440
440
 
441
441
  覆盖与别名的解析规则:
442
442
 
443
+ - **patch 的目标必须已存在**:内置 11 个场景,或**本数组中更早出现**的自定义场景。宿主按数组顺序登记(与 `indexById` 语义一致),指向不存在或更晚出现的 id 会被**整条跳过**——配置页会提前报“patch 目标不存在”,不必等日志。
443
444
  - **patch 式覆盖**:默认同 `id` 是**整体替换**。只想改一两个字段时,给条目加 `"patch": true` 即可做**字段级合并**——`title`/`description`/`recommendation` 给了才覆盖;`questions` 按 qid 合并(只换给出的字段,如只覆盖某个问题的 `criteria`,其余问题原样保留,新 qid 追加);`derive` 同名覆盖;`aliases` 求并集。例如给 `customer_service` 的部门问题加一个 VIP 通道:
444
445
 
445
446
  ```json
@@ -586,7 +587,7 @@ SystemOne 是「结构化问题 → 概率分布」的决策模型,**不是开
586
587
 
587
588
  ```powershell
588
589
  npm run check # 9 个 lib/*.js 语法检查通过
589
- npm run test # 195 项测试:
590
+ npm run test # 203 项测试:
590
591
  # - 场景库完整性、11 大场景端到端(有断言强制 sample 覆盖全部内置场景)、
591
592
  # 自定义场景合并/覆盖/patch/非法跳过
592
593
  # - params 覆盖语义、确定性、失败路径(无网络、无 Key)
@@ -595,7 +596,9 @@ npm run test # 195 项测试:
595
596
  # 缓存与"改配置即失效"、去重、跳过规则、历史捕获、自动路由、
596
597
  # 路由复用与热失效、失败熔断、卸载)
597
598
  # - 用量台账(累计、跨日、重启后从文件恢复、坏行容错、日限判定)
598
- # - 配置页(场景 JSON 校验、数字范围、保存被拒的冲突/校验区分、
599
+ # - 配置页(场景 JSON 校验、patch 目标存在性与顺序语义、数字范围、
600
+ # 保存被拒的冲突/校验区分、
601
+ # 三张卡各自保存(提交的 ops 只含本卡字段)、清除 Key 的 unset 语义、
599
602
  # 加载态与"为什么不能保存"的渲染冒烟)
600
603
  ```
601
604
 
package/lib/client.js CHANGED
@@ -42,7 +42,7 @@ window.__ModuleLoader__.load({
42
42
  tabCustom: '自定义场景',
43
43
  tabCustomHint: '用 JSON 定义自己的场景(含「通用意图识别」)。保存后场景库立即重建,无需重启。',
44
44
  customScenarios: '场景定义(JSON 数组)',
45
- customScenariosHint: '同 id 默认整体覆盖内置场景;加 "patch": true 则只改给出的字段。留空 [] 表示只用内置 11 个。每个场景最多 16 个问题,choice 选项 2~26 个。',
45
+ customScenariosHint: '同 id 默认整体覆盖内置场景;加 "patch": true 则只改给出的字段。patch 的目标必须是内置场景,或本数组中更早出现的自定义场景。留空 [] 表示只用内置 11 个。每个场景最多 16 个问题,choice 选项 2~26 个。',
46
46
  customScenariosPlaceholder: '[\n {\n "id": "intent",\n "title": "通用意图识别",\n "questions": {\n "intent": {\n "type": "choice",\n "instructions": "用户最想做什么?",\n "criteria": { "query": "查询", "action": "执行操作", "chat": "闲聊" }\n }\n }\n }\n]',
47
47
  insertTemplate: '插入意图识别模板',
48
48
  jsonEmpty: '(空 = 只用内置场景)',
@@ -137,7 +137,7 @@ window.__ModuleLoader__.load({
137
137
  tabCustom: 'Custom scenarios',
138
138
  tabCustomHint: 'Define your own scenarios in JSON (including general intent recognition). Saving rebuilds the library immediately.',
139
139
  customScenarios: 'Scenario definitions (JSON array)',
140
- customScenariosHint: 'An id matching a built-in overrides it; add "patch": true to merge only the given fields. Empty [] keeps only the 11 built-ins. Max 16 questions per scenario, 2~26 choice options.',
140
+ customScenariosHint: 'An id matching a built-in overrides it; add "patch": true to merge only the given fields. A patch target must be a built-in, or a custom scenario declared earlier in this array. Empty [] keeps only the 11 built-ins. Max 16 questions per scenario, 2~26 choice options.',
141
141
  customScenariosPlaceholder: '[\n {\n "id": "intent",\n "title": "General intent",\n "questions": {\n "intent": {\n "type": "choice",\n "instructions": "What does the user want?",\n "criteria": { "query": "Look up", "action": "Do something", "chat": "Small talk" }\n }\n }\n }\n]',
142
142
  insertTemplate: 'Insert intent template',
143
143
  jsonEmpty: '(empty = built-in scenarios only)',
@@ -359,8 +359,9 @@ window.__ModuleLoader__.load({
359
359
  * 客户端侧校验自定义场景 JSON。
360
360
  *
361
361
  * 只做「能立刻发现」的检查:JSON 语法 + 每条的 id/title/questions 与
362
- * 问题类型/选项数量。宿主侧 `resolveScenarios` 还会做一遍同样的校验,
363
- * 语义问题(非法条目)宿主会跳过并写入日志;这里提前拦住,用户不必去翻日志。
362
+ * 问题类型/选项数量,以及 `patch` 的目标是否存在。宿主侧 `resolveScenarios`
363
+ * 还会做一遍同样的校验,语义问题(非法条目)宿主会跳过并写入日志;
364
+ * 这里提前拦住,用户不必去翻日志。
364
365
  *
365
366
  * @param {string} text - 编辑框里的原始文本
366
367
  * @returns {{ ok: boolean, error?: string, ids: string[], count: number }}
@@ -378,6 +379,9 @@ window.__ModuleLoader__.load({
378
379
  if (!Array.isArray(parsed)) return { ok: false, error: 'notArray', ids: [], count: 0 }
379
380
 
380
381
  const ids = []
382
+ // patch 只能改「已有」场景:内置 11 个,或本数组里**更早**出现的自定义条目。
383
+ // 与宿主 `resolveScenarios` 的 indexById 语义一致(内置先行、按数组顺序登记)。
384
+ const known = new Set(SCENARIO_OPTIONS.map((option) => option.id.toLowerCase()))
381
385
  for (let i = 0; i < parsed.length; i += 1) {
382
386
  const spec = parsed[i]
383
387
  const at = i + 1
@@ -387,6 +391,11 @@ window.__ModuleLoader__.load({
387
391
  const issues = []
388
392
  const isPatch = spec.patch === true
389
393
  if (typeof spec.id !== 'string' || spec.id.trim() === '') issues.push('缺少 id')
394
+ // 目标不存在时宿主会整条跳过(只写日志),界面若仍报「JSON 有效」,
395
+ // 用户会以为改好了——所以这里必须提前拦住。
396
+ else if (isPatch && !known.has(spec.id.trim().toLowerCase())) {
397
+ issues.push('patch 目标不存在(只能是内置场景,或本数组中更早出现的自定义场景)')
398
+ }
390
399
  if (!isPatch && (typeof spec.title !== 'string' || spec.title.trim() === '')) issues.push('缺少 title')
391
400
  const questions = spec.questions
392
401
  if (!isPatch || questions !== undefined) {
@@ -442,7 +451,11 @@ window.__ModuleLoader__.load({
442
451
  if (issues.length > 0) {
443
452
  return { ok: false, error: `issues:${at}:${spec.id ?? '?'}:${issues.join(';')}`, ids, count: 0 }
444
453
  }
445
- if (typeof spec.id === 'string') ids.push(spec.id.trim())
454
+ if (typeof spec.id === 'string') {
455
+ ids.push(spec.id.trim())
456
+ // 后续条目的 patch 可以指向它(与宿主按顺序登记一致)
457
+ known.add(spec.id.trim().toLowerCase())
458
+ }
446
459
  }
447
460
  return { ok: true, ids, count: ids.length }
448
461
  }
@@ -532,6 +545,45 @@ window.__ModuleLoader__.load({
532
545
  return latestRevision !== revision ? t('conflict') : t('saveRefused')
533
546
  }
534
547
 
548
+ /**
549
+ * 构造一张卡片需要提交的写操作(纯函数)。
550
+ *
551
+ * 只取**该卡片内**、且与当前快照不同的字段——这是"三张卡各自保存"的核心:
552
+ * 保存「模型服务」时,不能顺手把「自动决策」卡里还没填完的草稿一起写下去。
553
+ *
554
+ * 规则与保存路径保持一致:数字从字符串转回 number(非法值不发写操作)、
555
+ * `redact` 下拉从字符串转布尔、`apiKey` 留空表示"不修改已保存的值"(清空走 unset)。
556
+ *
557
+ * @param {string} cardKey - service / custom / auto
558
+ * @param {object} input
559
+ * @param {string[]} input.fields - 该卡片的字段 key 列表
560
+ * @param {object} input.draft - 草稿(只含用户改过的键)
561
+ * @param {object} input.current - 当前快照(已 toDraft)
562
+ * @param {object[]} input.allFields - 全部字段元数据(用于判断类型)
563
+ * @param {boolean} input.clearApiKey - 是否点了「清除已保存的 Key」
564
+ * @returns {object[]} SettingsPathOpView 数组
565
+ */
566
+ function cardOps(cardKey, { fields, draft, current, allFields, clearApiKey }) {
567
+ const ops = []
568
+ for (const [key, value] of Object.entries(draft ?? {})) {
569
+ if (!fields.includes(key)) continue
570
+ if (value === current[key]) continue
571
+ // apiKey 留空表示「保持已保存的值」,不发写操作(清空见下面的 unset)
572
+ if (key === 'apiKey' && value === '') continue
573
+ const field = allFields.find((item) => item.key === key)
574
+ let next = value
575
+ if (field?.kind === 'number') {
576
+ const parsed = Number(value)
577
+ if (!Number.isFinite(parsed)) continue
578
+ next = parsed
579
+ }
580
+ if (key === 'redact') next = value === 'true'
581
+ ops.push({ op: 'set', path: [key], value: next })
582
+ }
583
+ if (cardKey === 'service' && clearApiKey) ops.push({ op: 'unset', path: ['apiKey'] })
584
+ return ops
585
+ }
586
+
535
587
  /* ────────────────────────────── 组件 ────────────────────────────── */
536
588
 
537
589
  /**
@@ -750,69 +802,73 @@ window.__ModuleLoader__.load({
750
802
  numberExtras[key] = { error }
751
803
  }
752
804
 
753
- const dirty = clearApiKey || (draft !== null && Object.keys(draft).some((key) => draft[key] !== current[key]))
754
- const canSave = dirty && !busy && writable && customCheck.ok && Object.keys(numberErrors).length === 0
805
+ const changedKeys = Object.keys(draft ?? {}).filter((key) => draft[key] !== current[key])
755
806
 
756
- // 保存按钮为什么是灰的,必须写在界面上:三张卡共用一个 canSave,
757
- // 而错误只出现在出问题的那张卡里(「自定义场景」卡还默认折叠)。
758
- // 否则用户看到的是一个没有任何解释的灰按钮。
759
- const blockers = []
760
- if (!customCheck.ok && customErrorText) blockers.push(`${t('tabCustom')}:${customErrorText}`)
761
- for (const [key, error] of Object.entries(numberErrors)) blockers.push(`${t(key)}:${error}`)
762
- const blockedText = blockers.length > 0
763
- ? t('saveBlocked', { count: blockers.length, reason: blockers[0] })
764
- : undefined
807
+ const CARD_FIELDS = {
808
+ service: SERVICE_FIELDS.map((f) => f.key),
809
+ custom: CUSTOM_FIELDS.map((f) => f.key),
810
+ auto: AUTO_FIELDS.map((f) => f.key),
811
+ }
765
812
 
766
- // 「固定场景」候选 = 11 个内置 + 当前生效的自定义场景 id(patch 条目指向已有 id,去重避免重复项)
767
- const scenarioOptions = [
768
- ...SCENARIO_OPTIONS,
769
- ...(customCheck.ok
770
- ? [...new Set(customCheck.ids)]
771
- .filter((id) => !SCENARIO_OPTIONS.some((option) => option.id === id))
772
- .map((id) => ({ id, label: id }))
773
- : []),
774
- ]
813
+ /** 单张卡片的脏态:该卡片内有字段被改过,或服务卡的 Key 被清空 */
814
+ const cardDirty = (cardKey) =>
815
+ (cardKey === 'service' && clearApiKey) ||
816
+ changedKeys.some((key) => CARD_FIELDS[cardKey].includes(key))
775
817
 
776
- const onFieldChange = (key, value) => {
777
- setNotice(null)
778
- // 用户开始输入新 Key 时,「清除」这个意图就作废了——否则会先 set 再 unset。
779
- if (key === 'apiKey' && clearApiKey) setClearApiKey(false)
780
- setDraft((previous) => ({ ...(previous ?? {}), [key]: value }))
818
+ /** 单张卡片的错误提示:该卡片内字段的数字越界,或自定义场景卡的 JSON 问题 */
819
+ const cardErrors = (cardKey) => {
820
+ const errors = []
821
+ if (cardKey === 'custom' && !customCheck.ok && customErrorText) {
822
+ errors.push(`${t('tabCustom')}:${customErrorText}`)
823
+ }
824
+ for (const [fieldKey, error] of Object.entries(numberErrors)) {
825
+ if (CARD_FIELDS[cardKey].includes(fieldKey)) errors.push(`${t(fieldKey)}:${error}`)
826
+ }
827
+ return errors
781
828
  }
782
829
 
783
- const onSave = async () => {
784
- if (!canSave) return
830
+ /** 单张卡片的保存状态行文案 */
831
+ const cardBlockedText = (cardKey) => {
832
+ const errors = cardErrors(cardKey)
833
+ return errors.length > 0 ? t('saveBlocked', { count: errors.length, reason: errors[0] }) : undefined
834
+ }
835
+
836
+ /** 单张卡片能否保存:本卡有改动、非忙、可写、且本卡内无校验错误 */
837
+ const cardCanSave = (cardKey) =>
838
+ cardDirty(cardKey) && !busy && writable && cardErrors(cardKey).length === 0
839
+
840
+ /** 把某张卡片的键从草稿里移除(撤销、或保存成功后);空草稿归一成 null */
841
+ const dropCardDraft = (cardKey) => {
842
+ setDraft((prev) => {
843
+ const next = { ...prev }
844
+ for (const key of Object.keys(prev)) {
845
+ if (CARD_FIELDS[cardKey].includes(key)) delete next[key]
846
+ }
847
+ return Object.keys(next).length ? next : null
848
+ })
849
+ }
850
+
851
+ /** 单张卡片保存:只提交该卡片内改过的字段,保存后只清掉该卡片的草稿 */
852
+ const onSaveCard = async (cardKey) => {
853
+ if (!cardCanSave(cardKey)) return
785
854
  setBusy(true)
786
855
  setNotice(null)
787
856
  try {
788
- // 数字字段在草稿里保持字符串,这里再转数字(合法性已由上方 numberErrors 拦截)
789
- const ops = []
790
- for (const [key, value] of Object.entries(draft ?? {})) {
791
- if (value === current[key]) continue
792
- // apiKey 留空表示「保持已保存的值」,不发写操作
793
- if (key === 'apiKey' && value === '') continue
794
- const field = ALL_FIELDS.find((item) => item.key === key)
795
- let next = value
796
- if (field?.kind === 'number') {
797
- const parsed = Number(value)
798
- if (!Number.isFinite(parsed)) continue
799
- next = parsed
800
- }
801
- // redact 下拉在草稿里是字符串,schema 要的是布尔
802
- if (key === 'redact') next = value === 'true'
803
- ops.push({ op: 'set', path: [key], value: next })
804
- }
805
- // 「清除已保存的 Key」是独立意图:schema 的 unset 才是"取消覆盖、回到默认",
806
- // 用空字符串 set 只会写进一个空值。
807
- if (clearApiKey) ops.push({ op: 'unset', path: ['apiKey'] })
857
+ const ops = cardOps(cardKey, {
858
+ fields: CARD_FIELDS[cardKey],
859
+ draft,
860
+ current,
861
+ allFields: ALL_FIELDS,
862
+ clearApiKey,
863
+ })
808
864
  if (ops.length === 0) {
809
- setDraft(null)
865
+ dropCardDraft(cardKey)
810
866
  return
811
867
  }
812
868
  const ok = await form.mutate(ops, revision)
813
869
  if (ok) {
814
- setDraft(null)
815
- setClearApiKey(false)
870
+ dropCardDraft(cardKey)
871
+ if (cardKey === 'service') setClearApiKey(false)
816
872
  setNotice({ kind: 'ok', text: t('saved') })
817
873
  } else {
818
874
  setNotice({
@@ -827,6 +883,30 @@ window.__ModuleLoader__.load({
827
883
  }
828
884
  }
829
885
 
886
+ /** 单张卡片撤销:只撤销该卡片里的改动 */
887
+ const onRevertCard = (cardKey) => {
888
+ dropCardDraft(cardKey)
889
+ if (cardKey === 'service') setClearApiKey(false)
890
+ }
891
+
892
+ // 「固定场景」候选 = 11 个内置 + 当前生效的自定义场景 id(patch 条目指向已有 id,去重避免重复项)
893
+ const scenarioOptions = [
894
+ ...SCENARIO_OPTIONS,
895
+ ...(customCheck.ok
896
+ ? [...new Set(customCheck.ids)]
897
+ .filter((id) => !SCENARIO_OPTIONS.some((option) => option.id === id))
898
+ .map((id) => ({ id, label: id }))
899
+ : []),
900
+ ]
901
+
902
+ const onFieldChange = (key, value) => {
903
+ setNotice(null)
904
+ // 用户开始输入新 Key 时,「清除」这个意图就作废了——否则会先 set 再 unset。
905
+ if (key === 'apiKey' && clearApiKey) setClearApiKey(false)
906
+ setDraft((previous) => ({ ...(previous ?? {}), [key]: value }))
907
+ }
908
+
909
+
830
910
  /** 渲染字段;可按 key 附加错误提示、候选、右上角附加元素。 */
831
911
  const renderFields = (fields, extras = {}) =>
832
912
  fields.map((field) => {
@@ -844,34 +924,41 @@ window.__ModuleLoader__.load({
844
924
  })
845
925
  })
846
926
 
847
- const statusLine = hs(Fragment, {
927
+ // 每张卡片自己的 footer:保存只提交该卡片内改过的字段,撤销也只撤销该卡片
928
+ const renderFooter = ({ cardKey, showRevert, extra }) => hs('div', {
929
+ key: 'footer',
930
+ style: footerStyle,
848
931
  children: [
849
- h('span', { key: 'now', children: t('current', { provider: values.provider || 'unisound', model: values.model || 'u2-decision' }) }),
850
- notice !== null
851
- ? h('span', { key: 'notice', style: notice.kind === 'ok' ? statusStyle : errorStyle, children: notice.text })
852
- : null,
853
- blockedText !== undefined
854
- ? h('span', { key: 'blocked', style: errorStyle, children: blockedText })
932
+ h('button', { key: 'save', type: 'button', style: { ...primaryButton, opacity: cardCanSave(cardKey) ? 1 : 0.5 }, disabled: !cardCanSave(cardKey), onClick: () => onSaveCard(cardKey), children: busy ? t('saving') : t('save') }),
933
+ ...(showRevert ? [h('button', { key: 'revert', type: 'button', style: ghostButton, disabled: !cardDirty(cardKey) || busy, onClick: () => onRevertCard(cardKey), children: t('revert') })] : []),
934
+ ...(extra ? [extra] : []),
935
+ cardBlockedText(cardKey) !== undefined
936
+ ? h('span', { key: 'blocked', style: errorStyle, children: cardBlockedText(cardKey) })
855
937
  : null,
856
- !writable ? h('span', { key: 'ro', style: statusStyle, children: t('readOnly') }) : null,
857
938
  ],
858
939
  })
859
940
 
860
- // 三张卡片共用同一套 footer:save(可选 revert / 额外状态)+ 状态行
861
- const renderFooter = ({ showRevert, extra }) => hs('div', {
862
- key: 'footer',
863
- style: footerStyle,
941
+ // 全局状态(当前提供商 / 保存结果 / 只读)只显示一次;
942
+ // 每张卡自己的"为什么不能保存"由各自的 footer 渲染(见 renderFooter)。
943
+ const globalStatus = hs(Fragment, {
944
+ key: 'status',
864
945
  children: [
865
- h('button', { key: 'save', type: 'button', style: { ...primaryButton, opacity: canSave ? 1 : 0.5 }, disabled: !canSave, onClick: onSave, children: busy ? t('saving') : t('save') }),
866
- ...(showRevert ? [h('button', { key: 'revert', type: 'button', style: ghostButton, disabled: !dirty || busy, onClick: () => { setDraft(null); setNotice(null); setClearApiKey(false) }, children: t('revert') })] : []),
867
- ...(extra ? [extra] : []),
868
- statusLine,
946
+ h('span', { key: 'now', children: t('current', { provider: values.provider || 'unisound', model: values.model || 'u2-decision' }) }),
947
+ notice !== null
948
+ ? h('span', { key: 'notice', style: notice.kind === 'ok' ? statusStyle : errorStyle, children: notice.text })
949
+ : null,
950
+ !writable ? h('span', { key: 'ro', style: statusStyle, children: t('readOnly') }) : null,
869
951
  ],
870
952
  })
871
953
 
872
954
  return hs('ul', {
873
955
  style: listStyle,
874
956
  children: [
957
+ h('li', {
958
+ key: 'global-status',
959
+ style: { ...statusStyle, display: 'flex', gap: 10, flexWrap: 'wrap' },
960
+ children: globalStatus,
961
+ }),
875
962
  h(ConfigCard, {
876
963
  key: 'service',
877
964
  title: t('tabService'),
@@ -898,7 +985,7 @@ window.__ModuleLoader__.load({
898
985
  }),
899
986
  },
900
987
  }),
901
- renderFooter({ showRevert: true }),
988
+ renderFooter({ cardKey: 'service', showRevert: true }),
902
989
  ],
903
990
  }),
904
991
  }),
@@ -961,6 +1048,7 @@ window.__ModuleLoader__.load({
961
1048
  },
962
1049
  }),
963
1050
  renderFooter({
1051
+ cardKey: 'custom',
964
1052
  showRevert: true,
965
1053
  extra: customCheck.ok
966
1054
  ? h('span', {
@@ -984,7 +1072,7 @@ window.__ModuleLoader__.load({
984
1072
  children: hs(Fragment, {
985
1073
  children: [
986
1074
  ...renderFields(AUTO_FIELDS, numberExtras),
987
- renderFooter({ showRevert: false }),
1075
+ renderFooter({ cardKey: 'auto', showRevert: false }),
988
1076
  ],
989
1077
  }),
990
1078
  }),
@@ -1036,6 +1124,8 @@ window.__ModuleLoader__.load({
1036
1124
  describeCustomError,
1037
1125
  numberErrorFor,
1038
1126
  saveResultText,
1127
+ cardOps,
1128
+ SCENARIO_OPTIONS,
1039
1129
  SERVICE_FIELDS,
1040
1130
  CUSTOM_FIELDS,
1041
1131
  AUTO_FIELDS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@master0071/dsh-systemone",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "DeepSeek Harness 插件:将 SystemOne 决策模型(默认云知声 u2-decision,可切换提供商)接入软件开发、需求预检、客服、内容审核、路由、销售线索、风控、招聘、数据治理、教育、需求变更等 11 大业务场景,为 Agent 返回带概率分布的决策与处置建议。",
5
5
  "license": "MIT",
6
6
  "type": "module",