dsh-decision-layer 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 (61) hide show
  1. package/README.en.md +33 -19
  2. package/README.md +33 -21
  3. package/docs/en/DETAIL.md +20 -0
  4. package/docs/zh/DETAIL.md +20 -0
  5. package/lib/client.js +185 -35
  6. package/lib/host/backend.d.ts +15 -0
  7. package/lib/host/backend.js +149 -0
  8. package/lib/host/config-store.d.ts +12 -0
  9. package/lib/host/config-store.js +254 -0
  10. package/lib/host/danger-gate.d.ts +45 -0
  11. package/lib/host/danger-gate.js +196 -0
  12. package/lib/host/excerpt.d.ts +25 -0
  13. package/lib/host/excerpt.js +62 -0
  14. package/lib/host/index.d.ts +6 -0
  15. package/lib/host/index.js +290 -0
  16. package/lib/host/log-analyze.d.ts +121 -0
  17. package/lib/host/log-analyze.js +254 -0
  18. package/lib/host/log-read.d.ts +4 -0
  19. package/lib/host/log-read.js +34 -0
  20. package/lib/host/log-store.d.ts +15 -0
  21. package/lib/host/log-store.js +98 -0
  22. package/lib/host/loop-guard.d.ts +10 -0
  23. package/lib/host/loop-guard.js +52 -0
  24. package/lib/host/manager-api.d.ts +20 -0
  25. package/lib/host/manager-api.js +174 -0
  26. package/lib/host/narrowing.d.ts +48 -0
  27. package/lib/host/narrowing.js +213 -0
  28. package/lib/host/quality-trend.d.ts +14 -0
  29. package/lib/host/quality-trend.js +22 -0
  30. package/lib/host/self-check.d.ts +27 -0
  31. package/lib/host/self-check.js +104 -0
  32. package/lib/host/session-state.d.ts +19 -0
  33. package/lib/host/session-state.js +288 -0
  34. package/lib/host/state-store.d.ts +14 -0
  35. package/lib/host/state-store.js +109 -0
  36. package/lib/host/steer-once.d.ts +13 -0
  37. package/lib/host/steer-once.js +25 -0
  38. package/lib/host/task-completion.d.ts +29 -0
  39. package/lib/host/task-completion.js +148 -0
  40. package/lib/host/types.d.ts +343 -0
  41. package/lib/host/types.js +11 -0
  42. package/locale/en.json +2 -1
  43. package/locale/zh.json +2 -1
  44. package/package.json +19 -10
  45. package/service/.gitkeep +0 -0
  46. package/service/backend.mjs +0 -127
  47. package/service/config-store.mjs +0 -311
  48. package/service/danger-gate.mjs +0 -182
  49. package/service/dsh.mjs +0 -282
  50. package/service/log-analyze.mjs +0 -256
  51. package/service/log-read.mjs +0 -27
  52. package/service/log-store.mjs +0 -82
  53. package/service/loop-guard.mjs +0 -51
  54. package/service/manager-api.mjs +0 -137
  55. package/service/narrowing.mjs +0 -210
  56. package/service/quality-trend.mjs +0 -29
  57. package/service/self-check.mjs +0 -131
  58. package/service/session-state.mjs +0 -257
  59. package/service/state-store.mjs +0 -100
  60. package/service/steer-once.mjs +0 -25
  61. package/service/task-completion.mjs +0 -163
package/lib/client.js CHANGED
@@ -1,8 +1,6 @@
1
- window.__ModuleLoader__.load({
2
- id: 'dsh-decision-layer',
3
- factory: (require) => {
4
- var module = { exports: {} };
5
- var exports = module.exports;
1
+ window.__ModuleLoader__.load({ id: 'dsh-decision-layer', factory: (require) => {
2
+ var module = { exports: {} }; var exports = module.exports;
3
+ "use strict";
6
4
  var __defProp = Object.defineProperty;
7
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
8
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -65,6 +63,7 @@ var zh = {
65
63
  narrowMore: " \u7B49 {count} \u4E2A",
66
64
  narrowObserve: "\u4EC5\u89C2\u5BDF",
67
65
  narrowEnforce: "\u5DF2\u751F\u6548",
66
+ narrowNotApplied: "\u672A\u751F\u6548",
68
67
  checkPassRate: "\u901A\u8FC7\u7387",
69
68
  narrowApplyRate: "\u6536\u7A84\u7387",
70
69
  rateNa: "\u2014",
@@ -81,6 +80,8 @@ var zh = {
81
80
  logCheck: "\u81EA\u68C0",
82
81
  logNarrow: "\u6536\u7A84",
83
82
  logComplete: "\u5B8C\u6210\u6838\u5BF9",
83
+ logTurn: "\u56DE\u5408 {turn}",
84
+ logTask: "\u573A\u666F\uFF08\u4EFB\u52A1\u8282\u9009\uFF09\uFF1A",
84
85
  logSuggested: "\u6A21\u578B\u5EFA\u8BAE",
85
86
  logEvalFailed: "\u8BC4\u4F30\u5931\u8D25",
86
87
  logScore: "\u5F97\u5206",
@@ -90,7 +91,7 @@ var zh = {
90
91
  logPathLabel: "\u8DEF\u5F84\uFF1A",
91
92
  logSubjectLabel: "\u8BF7\u6C42\uFF1A",
92
93
  logScoreHint: "\u5F97\u5206\u4E3A\u5404\u6863\u4F4D\u6309\u6982\u7387\u52A0\u6743\u7684\u7ED3\u679C\uFF0C\u53EF\u80FD\u662F\u5C0F\u6570\uFF08\u5982 1.4\uFF09\uFF0C\u6EE1\u5206\u4E3A\u9876\u6863\u53F7 2\u3002",
93
- logCheckHint: "\u6BCF\u56DE\u5408\u7ED3\u675F\u540E\uFF0C\u628A\u8FD9\u8F6E AI \u7684\u6700\u7EC8\u56DE\u590D\u4EA4\u7ED9\u88C1\u51B3\u6A21\u578B\uFF0C\u6309\u300C\u4E25\u91CD\u9057\u6F0F/\u81EA\u76F8\u77DB\u76FE \u2192 \u6709\u5C0F\u9057\u6F0F \u2192 \u5B8C\u6574\u51C6\u786E\u300D\u4E09\u6863\u6253\u4E00\u4E2A 0\u20132 \u7684\u8D28\u91CF\u5206\uFF0C\u7528\u6765\u53D1\u73B0\u9057\u6F0F\u6216\u77DB\u76FE\u3002",
94
+ logCheckHint: "\u6BCF\u56DE\u5408\u7ED3\u675F\u540E\uFF0C\u628A\u8FD9\u8F6E AI \u7684\u6700\u7EC8\u56DE\u590D\u4EA4\u7ED9\u88C1\u51B3\u6A21\u578B\uFF0C\u6309\u300C\u4E25\u91CD\u9057\u6F0F/\u81EA\u76F8\u77DB\u76FE \u2192 \u6709\u5C0F\u9057\u6F0F \u2192 \u5B8C\u6574\u51C6\u786E\u300D\u4E09\u6863\u6253\u4E00\u4E2A 0\u20132 \u7684\u8D28\u91CF\u5206\uFF0C\u7528\u6765\u53D1\u73B0\u9057\u6F0F\u6216\u77DB\u76FE\uFF1B\u81EA\u68C0\u884C\uFF08\u542B\u540E\u7AEF\u5931\u8D25\u884C\uFF09\u4F1A\u5E26\u672C\u56DE\u5408\u7684\u4EFB\u52A1\u8282\u9009\uFF08\u6700\u591A 512 \u5B57\uFF0C\u5DF2\u906E\u853D\u51ED\u636E\uFF09\u3002",
94
95
  logNarrowHint: "\u6BCF\u56DE\u5408\u5F00\u59CB\u65F6\uFF0C\u628A\u8FD9\u8F6E\u4EFB\u52A1\u4E0E\u5F53\u524D\u53EF\u7528\u5DE5\u5177\u9010\u4E2A\u4EA4\u7ED9\u88C1\u51B3\u6A21\u578B\u5224\u65AD\u76F8\u5173\u6027\uFF08noul\uFF09\uFF0C\u6458\u6389\u660E\u663E\u7528\u4E0D\u4E0A\u7684\u5DE5\u5177\uFF0C\u53EA\u5728\u5F53\u524D\u5BBF\u4E3B\u5DF2\u5141\u8BB8\u7684\u96C6\u5408\u5185\u53D6\u4EA4\u96C6\u3001\u7EDD\u4E0D\u6269\u6743\uFF1B\u300C\u4EC5\u89C2\u5BDF\u300D\u53EA\u8BB0\u5F55\u5EFA\u8BAE\uFF0C\u300C\u5DF2\u751F\u6548\u300D\u624D\u771F\u6B63\u5BF9\u672C\u56DE\u5408\u9650\u5236\u5DE5\u5177\u3002",
95
96
  actionDeny: "\u5DF2\u62D2\u7EDD",
96
97
  actionAsk: "\u8F6C\u4EA4\u5BA1\u6279",
@@ -108,7 +109,7 @@ var zh = {
108
109
  reasonConfig: "\u914D\u7F6E\u65E0\u6548",
109
110
  reasonTooMany: "\u5019\u9009\u8FC7\u591A",
110
111
  reasonTooManyCount: "\u5019\u9009 {count} \u4E2A\u504F\u591A",
111
- reasonLowKeep: "\u4FDD\u7559\u8FC7\u5C11\uFF0C\u672A\u751F\u6548",
112
+ reasonLowKeep: "\u4FDD\u7559\u8FC7\u5C11",
112
113
  selfCheck: "\u4EA7\u51FA\u81EA\u68C0",
113
114
  advancedSwitches: "\u4ECB\u5165\u5F00\u5173",
114
115
  advancedParams: "\u6536\u7A84\u53C2\u6570",
@@ -118,11 +119,11 @@ var zh = {
118
119
  narrowObserveLabel: "\u4EC5\u89C2\u5BDF\uFF08\u4E0D\u771F\u6B63\u9650\u5236\u5DE5\u5177\uFF09",
119
120
  narrowHint: "\u6BCF\u56DE\u5408\u5F00\u59CB\u65F6\uFF0C\u7528\u88C1\u51B3\u6A21\u578B\u5224\u65AD\u5F53\u524D\u53EF\u9009\u5DE5\u5177\u4E0E\u8FD9\u8F6E\u4EFB\u52A1\u7684\u76F8\u5173\u6027\uFF0C\u6458\u6389\u7528\u4E0D\u4E0A\u7684\uFF1B\u8BFB\u5199\u3001\u7F16\u8F91\u3001Shell\u3001\u68C0\u7D22\u3001\u5F85\u529E\u7B49\u6838\u5FC3\u5DE5\u5177\u59CB\u7EC8\u4FDD\u7559\u3001\u4E0D\u53C2\u4E0E\u5224\u5B9A\u3002\u9ED8\u8BA4\u771F\u6B63\u751F\u6548\uFF1A\u672C\u56DE\u5408\u53EA\u4FDD\u7559\u5224\u4E3A\u76F8\u5173\u7684\u5DE5\u5177\uFF0C\u4E14\u53EA\u5728\u5BBF\u4E3B\u5DF2\u5141\u8BB8\u7684\u8303\u56F4\u5185\u53D6\u4EA4\u96C6\u3001\u4E0D\u6269\u6743\u3002\u5F00\u542F\u300C\u4EC5\u89C2\u5BDF\u300D\u540E\u53EA\u628A\u53EF\u6458\u6389\u7684\u5DE5\u5177\u8BB0\u8FDB\u65E5\u5FD7\u548C\u7EDF\u8BA1\uFF0C\u4E0D\u6539\u52A8\u5DE5\u5177\u96C6\u3002\u4E09\u91CD\u4FDD\u62A4\uFF1A\u6838\u5FC3\u5DE5\u5177\u59CB\u7EC8\u4FDD\u7559\uFF1B\u53EF\u9009\u5DE5\u5177\u8D85\u8FC7 20 \u4E2A\u65F6\u81EA\u52A8\u53EA\u89C2\u5BDF\u4E0D\u9650\u5236\uFF1B\u4FDD\u7559\u4E0B\u6765\u4E0D\u8DB3 5 \u4E2A\u65F6\u89C6\u4E3A\u5224\u5B9A\u4E0D\u53EF\u4FE1\u3001\u8DF3\u8FC7\u3002",
120
121
  narrowKeepLabel: "\u59CB\u7EC8\u4FDD\u7559\u7684\u5DE5\u5177\u524D\u7F00",
121
- narrowKeepHint: "\u540D\u5B57\u4EE5\u8FD9\u4E9B\u524D\u7F00\u5F00\u5934\u7684\u5DE5\u5177\u6C38\u8FDC\u4FDD\u7559\u3001\u4E0D\u53C2\u4E0E\u76F8\u5173\u6027\u5224\u5B9A\uFF0C\u9002\u5408\u8BB0\u5FC6/\u68C0\u7D22\u8FD9\u7C7B\u300C\u5E38\u9A7B\u3001\u6309\u9700\u89E6\u53D1\u300D\u7684\u5DE5\u5177\uFF08\u9ED8\u8BA4\u542B mcp__openviking\u3001team_task_\uFF09\u3002\u7528\u82F1\u6587\u9017\u53F7\u6216\u6362\u884C\u5206\u9694\u591A\u4E2A\u524D\u7F00\uFF1B\u7559\u7A7A\u5219\u4E0D\u989D\u5916\u4FDD\u7559\u3002",
122
+ narrowKeepHint: "\u540D\u5B57\u4EE5\u8FD9\u4E9B\u524D\u7F00\u5F00\u5934\u7684\u5DE5\u5177\u6C38\u8FDC\u4FDD\u7559\u3001\u4E0D\u53C2\u4E0E\u76F8\u5173\u6027\u5224\u5B9A\uFF0C\u9002\u5408\u8BB0\u5FC6/\u68C0\u7D22\u8FD9\u7C7B\u300C\u5E38\u9A7B\u3001\u6309\u9700\u89E6\u53D1\u300D\u7684\u5DE5\u5177\uFF08\u9ED8\u8BA4\u53EA\u6709 mcp__openviking\uFF09\u3002\u7528\u82F1\u6587\u9017\u53F7\u6216\u6362\u884C\u5206\u9694\u591A\u4E2A\u524D\u7F00\uFF1B\u7559\u7A7A\u5219\u4E0D\u989D\u5916\u4FDD\u7559\u3002",
122
123
  narrowMaxLabel: "\u5019\u9009\u5DE5\u5177\u4E0A\u9650",
123
124
  narrowMaxHint: "\u4E00\u56DE\u5408\u91CC\u53C2\u4E0E\u5224\u5B9A\u7684\u300C\u53EF\u9009\u5DE5\u5177\u300D\u8D85\u8FC7\u8FD9\u4E2A\u6570\uFF08\u9ED8\u8BA4 20\uFF09\uFF0C\u5C31\u81EA\u52A8\u964D\u7EA7\u4E3A\u4EC5\u89C2\u5BDF\u3001\u4E0D\u771F\u6B63\u6536\u7A84\uFF0C\u907F\u514D\u4E00\u6B21\u585E\u592A\u591A\u5DE5\u5177\u8BA9\u6A21\u578B\u8BEF\u6458\u3002\u53D6\u503C 1\u2013200\uFF0C\u8C03\u5927\u66F4\u503E\u5411\u771F\u6B63\u6536\u7A84\u3002\u6838\u5FC3\u5DE5\u5177\u4E0E\u4FDD\u7559\u524D\u7F00\u547D\u4E2D\u7684\u5DE5\u5177\u4E0D\u8BA1\u5165\u3002",
124
125
  narrowThresholdLabel: "\u6458\u9664\u9608\u503C",
125
- narrowThresholdHint: "\u76F8\u5173\u6027\u6982\u7387\u4F4E\u4E8E\u8FD9\u4E2A\u503C\u624D\u6458\u6389\u5DE5\u5177\uFF08\u9ED8\u8BA4 0.3\uFF09\u3002\u6982\u7387\u63A5\u8FD1 0.5 \u8868\u793A\u6A21\u578B\u62FF\u4E0D\u51C6\u3001\u5E76\u975E\u300C\u4E2D\u7B49\u76F8\u5173\u300D\uFF0C\u6B64\u65F6\u4FDD\u7559\u66F4\u7A33\u59A5\uFF1B\u8C03\u4F4E\u66F4\u4FDD\u5B88\uFF08\u5C11\u6458\u3001\u5B81\u53EF\u591A\u7559\uFF09\uFF0C\u8C03\u9AD8\u66F4\u6FC0\u8FDB\uFF08\u591A\u6458\uFF09\u3002\u53D6\u503C 0\u20131\u3002",
126
+ narrowThresholdHint: "\u76F8\u5173\u6027\u6982\u7387\u4F4E\u4E8E\u8FD9\u4E2A\u503C\u624D\u6458\u6389\u5DE5\u5177\uFF08\u9ED8\u8BA4 0.7\uFF0C\u504F\u6FC0\u8FDB\uFF1A\u53EA\u7559\u6A21\u578B\u8F83\u6709\u628A\u63E1\u5224\u4E3A\u76F8\u5173\u7684\u5DE5\u5177\uFF09\u3002\u8C03\u4F4E\u66F4\u4FDD\u5B88\uFF08\u5C11\u6458\u3001\u5B81\u53EF\u591A\u7559\uFF09\uFF0C\u8C03\u9AD8\u66F4\u6FC0\u8FDB\uFF08\u591A\u6458\uFF09\u3002\u6982\u7387\u63A5\u8FD1 0.5 \u8868\u793A\u6A21\u578B\u62FF\u4E0D\u51C6\uFF0C\u6309\u9ED8\u8BA4\u9608\u503C\u4F1A\u88AB\u6458\u6389\uFF1B\u60F3\u7559\u4F4F\u8FD9\u7C7B\u300C\u53EF\u80FD\u7528\u5230\u300D\u7684\u5DE5\u5177\uFF0C\u628A\u9608\u503C\u8C03\u4F4E\u3002\u53D6\u503C 0\u20131\u3002",
126
127
  batchModeLabel: "\u5206\u6279\u7B56\u7565",
127
128
  batchModeHint: "\u300C\u81EA\u52A8\u300D\uFF08\u9ED8\u8BA4\uFF09\u5148\u6574\u6279\u53D1\u4E00\u6B21\u8BF7\u6C42\uFF0C\u9047\u5230\u4E0A\u4E0B\u6587\u8D85\u9650\u518D\u81EA\u52A8\u964D\u7EA7\u4E3A\u9010\u5DE5\u5177\u5E76\u8BB0\u4F4F\uFF1B\u300C\u6574\u6279\u300D\u4E00\u6B21\u5224\u5B8C\u5168\u90E8\u5DE5\u5177\uFF0C\u9002\u5408\u5927\u4E0A\u4E0B\u6587\u540E\u7AEF\uFF08\u5982 Jev\uFF09\uFF1B\u300C\u9010\u5DE5\u5177\u300D\u6BCF\u4E2A\u5DE5\u5177\u4E00\u6B21\u8BF7\u6C42\u3001\u5E76\u53D1\u6267\u884C\uFF0C\u9002\u5408\u5C0F\u4E0A\u4E0B\u6587\u540E\u7AEF\uFF08\u5982\u672C\u5730 tev1\uFF0C\u7EA6 2050 token \u5355\u8BF7\u6C42\u4E0A\u9650\uFF09\u3002",
128
129
  batchModeAuto: "\u81EA\u52A8",
@@ -159,7 +160,7 @@ var zh = {
159
160
  featureCheck: "\u4EA7\u51FA\u81EA\u68C0",
160
161
  featureCheckHint: "\u5173\u95ED\u540E\u4E0D\u518D\u5728\u56DE\u5408\u7ED3\u675F\u65F6\u7ED9\u6700\u7EC8\u56DE\u590D\u6253\u5206\u3002",
161
162
  featureNarrow: "\u5DE5\u5177\u6536\u7A84",
162
- featureNarrowHint: "\u5173\u95ED\u540E\u4E0D\u518D\u5728\u56DE\u5408\u5F00\u59CB\u65F6\u8BC4\u4F30\u5DE5\u5177\u76F8\u5173\u6027\u3002\u9632\u5FAA\u73AF\u4E3A\u786E\u5B9A\u6027\u672C\u5730\u4FDD\u62A4\uFF0C\u59CB\u7EC8\u751F\u6548\uFF0C\u4E0D\u5728\u6B64\u5F00\u5173\u8303\u56F4\u3002",
163
+ featureNarrowHint: "\u5173\u95ED\u540E\u4E0D\u518D\u5728\u56DE\u5408\u5F00\u59CB\u65F6\u8BC4\u4F30\u5DE5\u5177\u76F8\u5173\u6027\u3002\u9632\u5FAA\u73AF\u4E3A\u786E\u5B9A\u6027\u672C\u5730\u4FDD\u62A4\uFF0C\u4E0D\u53D7\u5404\u51B3\u7B56\u70B9\u5F00\u5173\u5F71\u54CD\uFF0C\u4F46\u4F1A\u8BDD\u603B\u5F00\u5173\u5173\u95ED\u65F6\u540C\u6837\u6682\u505C\u3002",
163
164
  featureComplete: "\u4EFB\u52A1\u5B8C\u6210\u6838\u5BF9",
164
165
  featureCompleteHint: "\u5173\u95ED\u540E\u4E0D\u518D\u5728\u56DE\u5408\u7ED3\u675F\u65F6\u9010\u9879\u6838\u5BF9\u7528\u6237\u660E\u786E\u5217\u51FA\u7684\u4EA4\u4ED8\u6761\u4EF6\u3002",
165
166
  trendWarnHint: "\u672C\u4F1A\u8BDD\u6700\u8FD1\u8FDE\u7EED\u51E0\u8F6E\u4EA7\u51FA\u8D28\u91CF\u504F\u4F4E\uFF08\u9AD8\u7F6E\u4FE1\uFF09\u3002\u53EF\u80FD\u662F\u4EFB\u52A1\u96BE\u5EA6\u4E0A\u5347\u6216\u6A21\u578B\u72B6\u6001\u6CE2\u52A8\uFF0C\u5EFA\u8BAE\u5C1D\u8BD5\u8C03\u6574 prompt \u6216\u66F4\u6362\u6A21\u578B\u3002",
@@ -202,7 +203,15 @@ var zh = {
202
203
  httpWarning: "\u6B64\u5730\u5740\u4F7F\u7528\u672A\u52A0\u5BC6\u7684 HTTP\u3002\u786E\u8BA4\u540E\uFF0CAPI Key \u4E0E\u8BC4\u4F30\u4E0A\u4E0B\u6587\u5C06\u4EE5\u660E\u6587\u53D1\u9001\u81F3\u8BE5\u5730\u5740\u3002",
203
204
  analysisTab: "\u51B3\u7B56\u5206\u6790",
204
205
  analysisTitle: "\u51B3\u7B56\u5206\u6790",
205
- analysisIntro: "\u57FA\u4E8E\u672C\u4F1A\u8BDD\u5DF2\u843D\u76D8\u7684\u51B3\u7B56\u65E5\u5FD7\u505A\u53EA\u8BFB\u805A\u5408\u4E0E\u4EBA\u5DE5\u6807\u6CE8\uFF0C\u4E0D\u6539\u53D8\u4EFB\u4F55 agent \u884C\u4E3A\u3002",
206
+ analysisIntro: "\u57FA\u4E8E\u672C\u4F1A\u8BDD\u5DF2\u843D\u76D8\u7684\u51B3\u7B56\u65E5\u5FD7\u505A\u805A\u5408\u4E0E\u4EBA\u5DE5\u6807\u6CE8\uFF1B\u9875\u5934\u5F00\u5173\u53EA\u63A7\u5236\u672C\u4F1A\u8BDD\u662F\u5426\u81EA\u52A8\u4ECB\u5165\uFF0C\u4E0D\u6539\u52A8\u5386\u53F2\u8BB0\u5F55\u3002",
207
+ analysisDecisionSwitch: "\u672C\u4F1A\u8BDD\u81EA\u52A8\u4ECB\u5165",
208
+ analysisDecisionOn: "\u5DF2\u5F00\u542F",
209
+ analysisDecisionOff: "\u5DF2\u5173\u95ED",
210
+ analysisDecisionUnavailable: "\u4E0D\u53EF\u7528",
211
+ analysisDecisionRetry: "\u8BFB\u53D6\u4F1A\u8BDD\u72B6\u6001\u5931\u8D25\uFF0C\u70B9\u51FB\u91CD\u8BD5\u3002",
212
+ analysisDecisionHintOn: "\u70B9\u51FB\u5173\u95ED\u672C\u4F1A\u8BDD\u7684\u81EA\u52A8\u4ECB\u5165\uFF08\u542B\u9632\u5FAA\u73AF\uFF09\uFF1B\u5DF2\u4EA7\u751F\u7684\u51B3\u7B56\u8BB0\u5F55\u4E0D\u53D7\u5F71\u54CD\u3002",
213
+ analysisDecisionHintOff: "\u70B9\u51FB\u5F00\u542F\u540E\uFF0C\u672C\u4F1A\u8BDD\u7684\u95E8\u63A7\u3001\u81EA\u68C0\u3001\u6536\u7A84\u3001\u5B8C\u6210\u6838\u5BF9\u4E0E\u9632\u5FAA\u73AF\u4F1A\u81EA\u52A8\u4ECB\u5165\u3002",
214
+ analysisDecisionDisabled: "\u672C\u4F1A\u8BDD\u7684\u81EA\u52A8\u4ECB\u5165\u5DF2\u5173\u95ED\u3002\u5F00\u542F\u540E\uFF0C\u65B0\u7684\u56DE\u5408\u624D\u4F1A\u4EA7\u751F\u51B3\u7B56\u8BB0\u5F55\u3002",
206
215
  analysisProfile: "\u8FC7\u7A0B\u753B\u50CF",
207
216
  analysisMeanScore: "\u5E73\u5747\u5206",
208
217
  analysisTrend: "\u8D28\u91CF\u8D8B\u52BF\u56DE\u6D4B",
@@ -251,6 +260,7 @@ var en = {
251
260
  narrowMore: " and {count} in total",
252
261
  narrowObserve: "observe",
253
262
  narrowEnforce: "enforced",
263
+ narrowNotApplied: "not applied",
254
264
  checkPassRate: "Pass rate",
255
265
  narrowApplyRate: "Narrow rate",
256
266
  rateNa: "\u2014",
@@ -267,6 +277,8 @@ var en = {
267
277
  logCheck: "Self-check",
268
278
  logNarrow: "Narrow",
269
279
  logComplete: "Completion",
280
+ logTurn: "Turn {turn}",
281
+ logTask: "Context (task excerpt): ",
270
282
  logSuggested: "model suggested",
271
283
  logEvalFailed: "evaluation failed",
272
284
  logScore: "score",
@@ -276,7 +288,7 @@ var en = {
276
288
  logPathLabel: "path: ",
277
289
  logSubjectLabel: "request: ",
278
290
  logScoreHint: "The score is a probability-weighted mean across the levels, so it can be fractional (e.g. 1.4); the top score is the highest level number, 2.",
279
- logCheckHint: "After each turn, the AI\u2019s final reply for that turn is sent to the adjudication model, which grades its quality on three levels (severe omission/contradiction \u2192 minor gaps \u2192 complete and accurate) as a 0\u20132 score, to catch omissions or contradictions.",
291
+ logCheckHint: "After each turn, the AI\u2019s final reply for that turn is sent to the adjudication model, which grades its quality on three levels (severe omission/contradiction \u2192 minor gaps \u2192 complete and accurate) as a 0\u20132 score, to catch omissions or contradictions; every self-check row (including a failed one) carries this turn\u2019s task excerpt (up to 512 characters, credentials masked).",
280
292
  logNarrowHint: "At the start of each turn, the current task and each available tool are sent to the adjudication model (a noul per tool) to judge relevance, dropping tools clearly not needed. It only intersects the host-allowed set and never expands it; observe merely logs the suggestion, enforced actually restricts the tools for this turn.",
281
293
  actionDeny: "denied",
282
294
  actionAsk: "sent to approval",
@@ -294,7 +306,7 @@ var en = {
294
306
  reasonConfig: "invalid configuration",
295
307
  reasonTooMany: "too many candidates",
296
308
  reasonTooManyCount: "{count} candidates is too many",
297
- reasonLowKeep: "too few kept, not enforced",
309
+ reasonLowKeep: "too few kept",
298
310
  selfCheck: "Output self-check",
299
311
  advancedSwitches: "Intervention switches",
300
312
  advancedParams: "Narrowing parameters",
@@ -304,11 +316,11 @@ var en = {
304
316
  narrowObserveLabel: "Observe only (do not restrict tools)",
305
317
  narrowHint: "At the start of each turn, the adjudication model judges how relevant each optional tool is to the task and drops the ones not needed; core tools (read, write, edit, shell, search, todos) are always kept and never judged. Enforced by default: only the relevant tools are kept for this turn, intersected with the host-allowed set and never expanded. Turn on Observe only to just log the tools that could be dropped, without changing the tool set. Three safeguards: core tools always kept; more than 20 optional tools falls back to observe only; fewer than 5 tools remaining is treated as untrustworthy and skipped.",
306
318
  narrowKeepLabel: "Always-kept tool prefixes",
307
- narrowKeepHint: "Tools whose names start with any of these prefixes are always kept and never judged for relevance \u2014 good for resident, on-demand tools like memory/retrieval (defaults to mcp__openviking and team_task_). Separate prefixes with commas or newlines; leave empty to keep nothing extra.",
319
+ narrowKeepHint: "Tools whose names start with any of these prefixes are always kept and never judged for relevance \u2014 good for resident, on-demand tools like memory/retrieval (defaults to mcp__openviking only). Separate prefixes with commas or newlines; leave empty to keep nothing extra.",
308
320
  narrowMaxLabel: "Candidate tool cap",
309
321
  narrowMaxHint: "When more than this many optional tools are judged in one turn (default 20), narrowing downgrades to observe only instead of actually restricting, so a large surface is never mass-pruned. Range 1\u2013200; a higher value leans toward actually narrowing. Core tools and keep-prefix matches do not count.",
310
322
  narrowThresholdLabel: "Drop threshold",
311
- narrowThresholdHint: 'A tool is dropped only when its relevance probability is below this value (default 0.3). A probability near 0.5 means the model is unsure, not "moderately relevant", so keeping is safer there; lower is more conservative (drops fewer), higher is more aggressive. Range 0\u20131.',
323
+ narrowThresholdHint: "A tool is dropped only when its relevance probability is below this value (default 0.7, leaning aggressive: only tools the model rates as fairly relevant survive). Lower is more conservative (drops fewer), higher is more aggressive. A probability near 0.5 means the model is unsure and falls below the default, so lower the threshold to keep such may-need tools. Range 0\u20131.",
312
324
  batchModeLabel: "Batch strategy",
313
325
  batchModeHint: "Auto (default) sends one whole-batch request first and, on a context overflow, falls back to one request per tool and remembers it; Single judges every tool in one request, best for a large-context backend (e.g. Jev); Split sends one request per tool concurrently, for a small-context backend (e.g. a local tev1 with a ~2050-token request limit).",
314
326
  batchModeAuto: "Auto",
@@ -345,7 +357,7 @@ var en = {
345
357
  featureCheck: "Output self-check",
346
358
  featureCheckHint: "When off, the final reply is no longer scored at the end of a turn.",
347
359
  featureNarrow: "Tool narrowing",
348
- featureNarrowHint: "When off, tool relevance is no longer evaluated at the start of a turn. The loop guard is a deterministic local safeguard that is always on and not covered by this switch.",
360
+ featureNarrowHint: "When off, tool relevance is no longer evaluated at the start of a turn. The loop guard is a deterministic local safeguard outside these switches, but the session master switch pauses it too.",
349
361
  featureComplete: "Task completion check",
350
362
  featureCompleteHint: "When off, the explicit deliverable conditions the user listed are no longer checked at turn end.",
351
363
  trendWarnHint: "Output quality has been low for a few consecutive turns this session (high confidence). It may be rising task difficulty or model drift \u2014 consider adjusting the prompt or switching models.",
@@ -388,7 +400,15 @@ var en = {
388
400
  httpWarning: "This address uses unencrypted HTTP. Your API key and evaluation context will be sent in plaintext to this address.",
389
401
  analysisTab: "Decision analysis",
390
402
  analysisTitle: "Decision analysis",
391
- analysisIntro: "A read-only aggregate and manual annotation over this session\u2019s persisted decision log. It changes no agent behavior.",
403
+ analysisIntro: "An aggregate and manual annotation over this session\u2019s persisted decision log. The switch in the header only controls automatic interventions for this session and never rewrites history.",
404
+ analysisDecisionSwitch: "Automatic interventions",
405
+ analysisDecisionOn: "on",
406
+ analysisDecisionOff: "off",
407
+ analysisDecisionUnavailable: "unavailable",
408
+ analysisDecisionRetry: "Could not read the session state. Click to retry.",
409
+ analysisDecisionHintOn: "Turn off automatic interventions for this session, including the loop guard. Existing decision records are unaffected.",
410
+ analysisDecisionHintOff: "Turn on automatic interventions (gate, self-check, narrowing, completion check, and loop guard) for this session.",
411
+ analysisDecisionDisabled: "Automatic interventions are off for this session. New decision records appear after you enable them.",
392
412
  analysisProfile: "Process profile",
393
413
  analysisMeanScore: "Mean score",
394
414
  analysisTrend: "Quality-trend backtest",
@@ -418,6 +438,23 @@ async function request(path, init) {
418
438
  return body.value;
419
439
  }
420
440
 
441
+ // src/client/session-events.ts
442
+ var SESSION_ENABLED_EVENT = "dsh-decision-layer:session-enabled";
443
+ function announceSessionEnabled(change) {
444
+ if (typeof window === "undefined") return;
445
+ window.dispatchEvent(new window.CustomEvent(SESSION_ENABLED_EVENT, { detail: change }));
446
+ }
447
+ function onSessionEnabled(listener) {
448
+ if (typeof window === "undefined") return () => {
449
+ };
450
+ const handler = (event) => {
451
+ const change = event.detail;
452
+ if (change && typeof change.sessionId === "string" && typeof change.enabled === "boolean") listener(change);
453
+ };
454
+ window.addEventListener(SESSION_ENABLED_EVENT, handler);
455
+ return () => window.removeEventListener(SESSION_ENABLED_EVENT, handler);
456
+ }
457
+
421
458
  // src/client/decision-log.tsx
422
459
  var import_jsx_runtime = require("react/jsx-runtime");
423
460
  var time = (at) => new Date(at).toLocaleTimeString();
@@ -439,6 +476,12 @@ function gateArgs({ t }, entry) {
439
476
  const preview = value.length > 160 ? `${value.slice(0, 160)}\u2026` : value;
440
477
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: "decision-log-cmd", title: `${label}${value}`, children: preview });
441
478
  }
479
+ function turnTag(t, entry) {
480
+ return typeof entry.turn === "number" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
481
+ " \xB7 ",
482
+ t("logTurn", { turn: entry.turn })
483
+ ] }) : null;
484
+ }
442
485
  function renderGate({ t }, entry) {
443
486
  const tool = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: "decision-log-tool", children: entry.tool ?? "\u2014" });
444
487
  const args = gateArgs({ t }, entry);
@@ -506,7 +549,10 @@ function renderGate({ t }, entry) {
506
549
  ] });
507
550
  }
508
551
  function renderCheck({ t }, entry) {
509
- const label = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { title: t("logCheckHint"), children: t("logCheck") });
552
+ const label = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
553
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { title: t("logCheckHint"), children: t("logCheck") }),
554
+ turnTag(t, entry)
555
+ ] });
510
556
  const subject = entry.subject ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-subject", title: `${t("logSubjectLabel")}${entry.subject}`, children: [
511
557
  "\u300C",
512
558
  entry.subject.length > 60 ? `${entry.subject.slice(0, 60)}\u2026` : entry.subject,
@@ -558,7 +604,10 @@ function renderCheck({ t }, entry) {
558
604
  ] });
559
605
  }
560
606
  function renderNarrow({ t }, entry) {
561
- const label = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { title: t("logNarrowHint"), children: t("logNarrow") });
607
+ const label = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
608
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { title: t("logNarrowHint"), children: t("logNarrow") }),
609
+ turnTag(t, entry)
610
+ ] });
562
611
  if (entry.outcome === "error") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
563
612
  label,
564
613
  " ",
@@ -574,9 +623,13 @@ function renderNarrow({ t }, entry) {
574
623
  " \xB7 ",
575
624
  guardText
576
625
  ] }) : null;
577
- const modeTag = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
626
+ const notApplied = entry.applied === false || entry.reason === "low-keep" || entry.reason === "too-many-candidates";
627
+ const modeTag = entry.mode === "observe" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
578
628
  " \xB7 ",
579
- entry.mode === "enforce" ? t("narrowEnforce") : t("narrowObserve")
629
+ t("narrowObserve")
630
+ ] }) : dropped === 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
631
+ " \xB7 ",
632
+ notApplied ? t("narrowNotApplied") : t("narrowEnforce")
580
633
  ] });
581
634
  if (dropped === 0) {
582
635
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
@@ -682,7 +735,16 @@ function DecisionLogList({ entries, ratings, onRate, t, newestFirst = true }) {
682
735
  const rows = newestFirst ? entries.slice().reverse() : entries.slice();
683
736
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "decision-log", children: rows.map((entry, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("li", { className: `decision-log-item decision-log-${outcomeTagOf(entry)}`, children: [
684
737
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-time", children: time(entry.at) }),
685
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-body", children: renderBody({ t }, entry) }),
738
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decision-log-body", children: [
739
+ renderBody({ t }, entry),
740
+ entry.task ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("details", { className: "decision-log-context", children: [
741
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("summary", { children: [
742
+ t("logTask"),
743
+ entry.task.length > 60 ? `${entry.task.slice(0, 60)}\u2026` : entry.task
744
+ ] }),
745
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: entry.task })
746
+ ] }) : null
747
+ ] }),
686
748
  entry.id && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RatingButtons, { id: entry.id, t, ratings, onRate })
687
749
  ] }, entry.id ?? `${entry.at}-${index}`)) });
688
750
  }
@@ -796,6 +858,9 @@ function SessionPanel({ sessionId, t, useProjection }) {
796
858
  live = false;
797
859
  };
798
860
  }, [sessionId, t]);
861
+ (0, import_react2.useEffect)(() => onSessionEnabled((change) => {
862
+ if (change.sessionId === sessionId) setEnabled(change.enabled);
863
+ }), [sessionId]);
799
864
  const checkStatus = () => {
800
865
  setStatus("checking");
801
866
  void request("probe", { method: "POST" }).then((value) => {
@@ -837,6 +902,7 @@ function SessionPanel({ sessionId, t, useProjection }) {
837
902
  body: JSON.stringify({ sessionId, enabled: next })
838
903
  });
839
904
  setEnabled(value.enabled);
905
+ announceSessionEnabled({ sessionId, enabled: value.enabled });
840
906
  } catch {
841
907
  setMessage(t("error"));
842
908
  } finally {
@@ -1044,7 +1110,30 @@ function AnalysisView({ sessionId, t }) {
1044
1110
  const [logPage, setLogPage] = (0, import_react3.useState)(1);
1045
1111
  const [loaded, setLoaded] = (0, import_react3.useState)(false);
1046
1112
  const [message, setMessage] = (0, import_react3.useState)("");
1113
+ const [enabled, setEnabled] = (0, import_react3.useState)(null);
1114
+ const [sessionError, setSessionError] = (0, import_react3.useState)(false);
1115
+ const [busy, setBusy] = (0, import_react3.useState)(false);
1047
1116
  const { ratings, seed, annotate } = useAnnotations(sessionId, () => setMessage(t("annotateFailed")));
1117
+ const active = (0, import_react3.useRef)(sessionId);
1118
+ const loadSession = () => {
1119
+ const session = sessionId;
1120
+ setEnabled(null);
1121
+ setSessionError(false);
1122
+ void request(`session?sessionId=${encodeURIComponent(session)}`).then((value) => {
1123
+ if (active.current === session) setEnabled(value.enabled);
1124
+ }).catch(() => {
1125
+ if (active.current === session) setSessionError(true);
1126
+ });
1127
+ };
1128
+ (0, import_react3.useEffect)(() => {
1129
+ active.current = sessionId;
1130
+ setBusy(false);
1131
+ setMessage("");
1132
+ loadSession();
1133
+ }, [sessionId]);
1134
+ (0, import_react3.useEffect)(() => onSessionEnabled((change) => {
1135
+ if (change.sessionId === sessionId) setEnabled(change.enabled);
1136
+ }), [sessionId]);
1048
1137
  (0, import_react3.useEffect)(() => {
1049
1138
  let alive = true;
1050
1139
  setMessage("");
@@ -1064,6 +1153,32 @@ function AnalysisView({ sessionId, t }) {
1064
1153
  alive = false;
1065
1154
  };
1066
1155
  }, [sessionId, seed]);
1156
+ const toggleEnabled = async () => {
1157
+ if (sessionError) {
1158
+ loadSession();
1159
+ return;
1160
+ }
1161
+ if (enabled === null || busy) return;
1162
+ const next = !enabled;
1163
+ const session = sessionId;
1164
+ setBusy(true);
1165
+ try {
1166
+ const value = await request("session", {
1167
+ method: "PUT",
1168
+ headers: { "content-type": "application/json" },
1169
+ body: JSON.stringify({ sessionId: session, enabled: next })
1170
+ });
1171
+ if (active.current === session) {
1172
+ setEnabled(value.enabled);
1173
+ setMessage("");
1174
+ announceSessionEnabled({ sessionId: session, enabled: value.enabled });
1175
+ }
1176
+ } catch {
1177
+ if (active.current === session) setMessage(t("error"));
1178
+ } finally {
1179
+ if (active.current === session) setBusy(false);
1180
+ }
1181
+ };
1067
1182
  (0, import_react3.useEffect)(() => {
1068
1183
  let alive = true;
1069
1184
  void request(`logrows?sessionId=${encodeURIComponent(sessionId)}&page=${logPage}&pageSize=${LOG_PAGE_SIZE}`).then((value) => {
@@ -1080,9 +1195,30 @@ function AnalysisView({ sessionId, t }) {
1080
1195
  const meanScore = profile && profile.check.scoreCount > 0 ? Math.round(profile.check.scoreSum / profile.check.scoreCount * 10) / 10 : null;
1081
1196
  const completeTotal = profile ? profile.complete.satisfied + profile.complete.unsatisfied + profile.complete.insufficient : 0;
1082
1197
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "decision-analysis", role: "region", "aria-label": t("analysisTitle"), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-analysis-inner", children: [
1083
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { className: "decision-analysis-title", children: t("analysisTitle") }),
1084
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted decision-analysis-intro", children: t("analysisIntro") }),
1085
- loaded && (!analysis || analysis.totalDecisions === 0) ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-empty", role: "status", children: t("logEmpty") }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1198
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("header", { className: "decision-analysis-head", children: [
1199
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-analysis-heading", children: [
1200
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { className: "decision-analysis-title", children: t("analysisTitle") }),
1201
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted decision-analysis-intro", children: t("analysisIntro") })
1202
+ ] }),
1203
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1204
+ "button",
1205
+ {
1206
+ type: "button",
1207
+ className: "decision-analysis-switch",
1208
+ "aria-pressed": enabled === true,
1209
+ "aria-busy": busy || enabled === null && !sessionError,
1210
+ disabled: busy || enabled === null && !sessionError,
1211
+ title: sessionError ? t("analysisDecisionRetry") : enabled === false ? t("analysisDecisionHintOff") : enabled === true ? t("analysisDecisionHintOn") : void 0,
1212
+ onClick: () => void toggleEnabled(),
1213
+ children: [
1214
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-analysis-switch-dot", "aria-hidden": "true" }),
1215
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: t("analysisDecisionSwitch") }),
1216
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-analysis-switch-state", children: sessionError ? t("analysisDecisionUnavailable") : enabled === null ? t("loading") : enabled ? t("analysisDecisionOn") : t("analysisDecisionOff") })
1217
+ ]
1218
+ }
1219
+ )
1220
+ ] }),
1221
+ loaded && (!analysis || analysis.totalDecisions === 0) ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-empty", role: "status", children: enabled === false ? t("analysisDecisionDisabled") : t("logEmpty") }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1086
1222
  profile && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "decision-analysis-section", children: [
1087
1223
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { children: t("analysisProfile") }),
1088
1224
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-analysis-cards", children: [
@@ -1162,9 +1298,9 @@ async function request2(path, fetchFn, init) {
1162
1298
  if (!response.ok || !body.ok || body.value === void 0) throw new Error(body.error || "Request failed");
1163
1299
  return body.value;
1164
1300
  }
1165
- var DEFAULT_KEEP_PREFIXES = ["mcp__openviking", "team_task_"];
1301
+ var DEFAULT_KEEP_PREFIXES = ["mcp__openviking"];
1166
1302
  var DEFAULT_MAX_CANDIDATES = 20;
1167
- var DEFAULT_THRESHOLD = 0.3;
1303
+ var DEFAULT_THRESHOLD = 0.7;
1168
1304
  var DEFAULT_BATCH_MODE = "auto";
1169
1305
  var DEFAULT_RUBRIC = [
1170
1306
  "\u660E\u663E\u7B54\u975E\u6240\u95EE\uFF0C\u6216\u5B58\u5728\u4E25\u91CD\u9057\u6F0F / \u81EA\u76F8\u77DB\u76FE",
@@ -1641,7 +1777,7 @@ ${url}`)) return;
1641
1777
  step: "any",
1642
1778
  value: threshold,
1643
1779
  disabled: !canEdit || !narrowOn,
1644
- placeholder: "0.3",
1780
+ placeholder: "0.7",
1645
1781
  onChange: (event) => {
1646
1782
  setThreshold(event.target.value);
1647
1783
  setAdvancedDirty(true);
@@ -1999,6 +2135,10 @@ var css = `
1999
2135
  .decision-log-tool{font-size:.76rem;padding:.05rem .35rem;border-radius:5px;background:var(--dsw-alias-bg-layer-1,#f2f4f6);color:var(--dsw-alias-label-primary,#202124)}
2000
2136
  .decision-log-cmd{font-size:.74rem;padding:.05rem .35rem;border-radius:5px;background:var(--dsw-alias-bg-layer-1,#f2f4f6);color:var(--dsw-alias-label-secondary,#59636e);word-break:break-all}
2001
2137
  .decision-log-subject{font-size:.74rem;color:var(--dsw-alias-label-secondary,#59636e);font-style:italic}
2138
+ .decision-log-context{margin-top:.25rem;font-size:.74rem;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere}
2139
+ .decision-log-context summary{cursor:pointer}
2140
+ .decision-log-context summary:focus-visible{outline:2px solid var(--dsw-alias-label-secondary);outline-offset:2px}
2141
+ .decision-log-context p{margin:.25rem 0 0}
2002
2142
  .decision-log-verdict{font-weight:600}
2003
2143
  .decision-log-deny .decision-log-verdict{color:#c0392b}
2004
2144
  .decision-log-ok .decision-log-verdict{color:#1f8a70}
@@ -2101,10 +2241,22 @@ var css = `
2101
2241
  /* Conversation-view "Decision analysis" tab. It reuses the card / stat / log
2102
2242
  primitives above; this block only supplies the scroll container and section
2103
2243
  rhythm, since the tab is not inside the modal's .decision-body wrapper. */
2104
- .decision-analysis{height:100%;overflow:auto;color:var(--dsw-alias-label-primary,#202124);font:inherit}
2244
+ .decision-analysis{height:100%;overflow:auto;color:var(--dsw-alias-label-primary,#202124);font:inherit;--decision-analysis-accent:#1f5d50;--decision-analysis-accent-bg:rgba(31,93,80,.08);--decision-analysis-dot:#1f8a70;--decision-analysis-ring:rgba(31,138,112,.16)}
2105
2245
  .decision-analysis-inner{max-width:52rem;margin:0 auto;padding:1.4rem 1.5rem 3rem}
2246
+ .decision-analysis-head{display:flex;align-items:flex-start;justify-content:space-between;gap:1rem;flex-wrap:wrap}
2247
+ .decision-analysis-heading{flex:1 1 18rem;min-width:0}
2106
2248
  .decision-analysis-title{font-size:1.15rem;margin:0 0 .3rem}
2107
- .decision-analysis-intro{margin:0 0 .4rem}
2249
+ .decision-analysis-intro{margin:0}
2250
+ /* The session-level automatic-intervention switch, mirrored from the dock panel.
2251
+ A pill button with a status dot: pressed = decisions on for this session. */
2252
+ .decision-analysis-switch{display:inline-flex;align-items:center;gap:.45rem;flex:0 0 auto;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2,#ccd3da);border-radius:999px;background:transparent;color:var(--dsw-alias-label-secondary,#59636e);padding:.38rem .85rem;font:inherit;font-size:.8rem;font-weight:500;line-height:1.3;white-space:nowrap;cursor:pointer;transition:color .15s,border-color .15s,background .15s}
2253
+ .decision-analysis-switch:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#eef1f3);color:var(--dsw-alias-label-primary,#202124)}
2254
+ .decision-analysis-switch[aria-pressed="true"]{border-color:var(--decision-analysis-accent);color:var(--decision-analysis-accent);background:var(--decision-analysis-accent-bg)}
2255
+ .decision-analysis-switch-dot{flex:0 0 auto;width:.5rem;height:.5rem;border-radius:50%;background:var(--dsw-alias-label-tertiary,#9aa5b1);transition:background .15s,box-shadow .15s}
2256
+ .decision-analysis-switch[aria-pressed="true"] .decision-analysis-switch-dot{background:var(--decision-analysis-dot);box-shadow:0 0 0 2px var(--decision-analysis-ring)}
2257
+ .decision-analysis-switch:disabled{opacity:.55;cursor:default}
2258
+ .decision-analysis-switch-state{font-weight:400;opacity:.8}
2259
+ .decision-analysis-switch:focus-visible{outline:2px solid var(--decision-analysis-accent);outline-offset:2px}
2108
2260
  .decision-analysis-section{border-top:1px solid var(--dsw-alias-border-l2,#eceff1);margin-top:1.1rem;padding-top:1rem}
2109
2261
  .decision-analysis-section>h3{font-size:.92rem;margin:0 0 .6rem}
2110
2262
  .decision-analysis-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(14rem,1fr));gap:.7rem}
@@ -2130,7 +2282,7 @@ var css = `
2130
2282
  .decision-pager button:disabled{opacity:.45;cursor:default}
2131
2283
  .decision-pager-status{font-size:.8rem;color:var(--dsw-alias-label-secondary,#59636e);font-variant-numeric:tabular-nums}
2132
2284
  @media (prefers-color-scheme:dark){
2133
- .decision-analysis{color:var(--dsw-alias-label-primary,#e6eaee)}
2285
+ .decision-analysis{color:var(--dsw-alias-label-primary,#e6eaee);--decision-analysis-accent:#8fdcc6;--decision-analysis-accent-bg:rgba(95,191,165,.14);--decision-analysis-dot:#3ecf9e;--decision-analysis-ring:rgba(62,207,158,.2)}
2134
2286
  }
2135
2287
  `;
2136
2288
  function installStyles() {
@@ -2172,7 +2324,5 @@ function apply(ctx) {
2172
2324
  (props) => props.view === "summary" ? props.t("summary") : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ConfigForm, { t: props.t })
2173
2325
  ));
2174
2326
  }
2175
-
2176
- return module.exports;
2177
- }
2178
- });
2327
+ return module.exports;
2328
+ } });
@@ -0,0 +1,15 @@
1
+ import type { BackendConfig, EvaluateInput, EvaluateResult } from './types.js';
2
+ export type BackendError = Error & {
3
+ reason: string;
4
+ };
5
+ export declare function validateRequest(input: unknown): EvaluateInput;
6
+ export declare function createBackend({ config, fetcher, timeoutMs, retryDelayMs }: {
7
+ config: () => Promise<BackendConfig>;
8
+ fetcher?: typeof fetch;
9
+ timeoutMs?: number;
10
+ retryDelayMs?: number;
11
+ }): {
12
+ evaluate(input: EvaluateInput, { signal }?: {
13
+ signal?: AbortSignal;
14
+ }): Promise<EvaluateResult>;
15
+ };
@@ -0,0 +1,149 @@
1
+ import { setTimeout as wait } from "node:timers/promises";
2
+ import { DEFAULT_MODEL } from "./config-store.js";
3
+ import { isObject, isProbability } from "./types.js";
4
+ const REQUEST_LIMIT = 256 * 1024;
5
+ const RESPONSE_LIMIT = 1024 * 1024;
6
+ const isContent = (value) => typeof value === "string" ? value.trim().length > 0 : isObject(value) || Array.isArray(value);
7
+ function backendError(reason, message) {
8
+ const error = new Error(message);
9
+ error.reason = reason;
10
+ return error;
11
+ }
12
+ function validateRequest(input) {
13
+ if (!isObject(input) || !isContent(input.state) || !isObject(input.questions)) throw new Error("Invalid state or questions");
14
+ const questions = Object.entries(input.questions);
15
+ if (questions.length < 1 || questions.length > 64) throw new Error("Invalid question count");
16
+ for (const [id, question] of questions) {
17
+ if (!id.trim() || id.length > 160 || !isObject(question) || !isContent(question.instructions)) throw new Error("Invalid question");
18
+ if (question.type === "choice") {
19
+ const criteria = question.criteria;
20
+ if (!isObject(criteria) || Object.keys(criteria).length < 2 || Object.keys(criteria).length > 255 || Object.entries(criteria).some(([key, value]) => !key.trim() || value !== null && !isContent(value))) throw new Error("Invalid choice criteria");
21
+ } else if (question.type === "score") {
22
+ const criteria = question.criteria;
23
+ if (!Array.isArray(criteria) || criteria.length < 2 || criteria.length > 10 || !criteria.every(isContent)) throw new Error("Invalid score criteria");
24
+ } else if (question.type !== "noul" || question.criteria !== void 0 && (!isObject(question.criteria) || Object.keys(question.criteria).some((key) => !["true", "false"].includes(key)))) {
25
+ throw new Error("Invalid question type or criteria");
26
+ }
27
+ }
28
+ let encoded;
29
+ try {
30
+ encoded = JSON.stringify(input, (_key, value) => {
31
+ if (typeof value === "function" || typeof value === "symbol" || typeof value === "bigint" || typeof value === "number" && !Number.isFinite(value)) throw new Error("Invalid JSON");
32
+ return value;
33
+ });
34
+ } catch {
35
+ throw new Error("Invalid JSON payload");
36
+ }
37
+ if (Buffer.byteLength(encoded) > REQUEST_LIMIT) throw new Error("Request too large");
38
+ return { state: input.state, questions: input.questions };
39
+ }
40
+ function validateAnswer(value, question) {
41
+ if (!isObject(value) || value.type !== question.type) throw backendError("invalid-response", "Invalid response answer");
42
+ if (question.type === "noul") {
43
+ if (!isProbability(value.noul)) throw backendError("invalid-response", "Invalid response probability");
44
+ return { type: "noul", noul: value.noul };
45
+ }
46
+ const keys = question.type === "choice" ? Object.keys(question.criteria) : question.criteria.map((_, i) => String(i));
47
+ const probabilities = value.probabilities;
48
+ if (!isObject(probabilities) || !isProbability(value.confidence) || Object.keys(probabilities).length !== keys.length || keys.some((key) => !isProbability(probabilities[key])) || Math.abs(keys.reduce((sum, key) => sum + Number(probabilities[key]), 0) - 1) > 0.02) throw backendError("invalid-response", "Invalid response probabilities");
49
+ const common = {
50
+ confidence: value.confidence,
51
+ probabilities: Object.fromEntries(keys.map((key) => [key, probabilities[key]]))
52
+ };
53
+ if (question.type === "choice") {
54
+ if (typeof value.choice !== "string" || !keys.includes(value.choice)) throw backendError("invalid-response", "Invalid response choice");
55
+ return { type: "choice", ...common, choice: value.choice };
56
+ }
57
+ if (typeof value.score !== "number" || !Number.isFinite(value.score) || value.score < 0 || value.score > keys.length - 1) throw backendError("invalid-response", "Invalid response score");
58
+ return { type: "score", ...common, score: value.score };
59
+ }
60
+ function validateResponse(value, questions) {
61
+ if (!isObject(value) || !isObject(value.answers) || typeof value.model !== "string") throw backendError("invalid-response", "Invalid response");
62
+ const raw = value.answers;
63
+ const answers = /* @__PURE__ */ Object.create(null);
64
+ for (const [id, question] of Object.entries(questions)) answers[id] = validateAnswer(raw[id], question);
65
+ const usage = value.usage;
66
+ return { model: value.model, answers, usage: isObject(usage) ? {
67
+ input_tokens: Number.isSafeInteger(usage.input_tokens) ? usage.input_tokens : 0,
68
+ output_tokens: Number.isSafeInteger(usage.output_tokens) ? usage.output_tokens : 0
69
+ } : void 0 };
70
+ }
71
+ async function boundedJson(response) {
72
+ const reader = response.body?.getReader();
73
+ if (!reader) throw backendError("invalid-response", "Invalid response body");
74
+ const chunks = [];
75
+ let size = 0;
76
+ try {
77
+ while (true) {
78
+ const { done, value } = await reader.read();
79
+ if (done) break;
80
+ size += value.byteLength;
81
+ if (size > RESPONSE_LIMIT) throw backendError("invalid-response", "Invalid response size");
82
+ chunks.push(value);
83
+ }
84
+ } finally {
85
+ await reader.cancel().catch(() => {
86
+ });
87
+ }
88
+ try {
89
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
90
+ } catch {
91
+ throw backendError("invalid-response", "Invalid response JSON");
92
+ }
93
+ }
94
+ function createBackend({ config, fetcher = globalThis.fetch, timeoutMs = 2e4, retryDelayMs = 200 }) {
95
+ if (typeof config !== "function" || typeof fetcher !== "function" || !Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || !Number.isSafeInteger(retryDelayMs) || retryDelayMs < 0 || retryDelayMs > 5e3) {
96
+ throw new Error("Invalid backend dependencies");
97
+ }
98
+ return {
99
+ async evaluate(input, { signal } = {}) {
100
+ const { state, questions } = validateRequest(input);
101
+ const settings = await config();
102
+ const key = settings.apiKey?.trim();
103
+ if (!key || /[\r\n]/.test(key)) throw backendError("config", "API key is required");
104
+ let url;
105
+ try {
106
+ url = new URL(settings.url);
107
+ } catch {
108
+ throw backendError("config", "Invalid backend URL");
109
+ }
110
+ if (!["https:", "http:"].includes(url.protocol) || url.username || url.password || url.search || url.hash) throw backendError("config", "Invalid backend URL");
111
+ if (url.protocol === "http:" && settings.httpApprovedUrl !== settings.url) throw backendError("config", "HTTP URL requires confirmation");
112
+ const model = settings.model || DEFAULT_MODEL;
113
+ if (typeof model !== "string" || !model.trim() || /[\r\n]/.test(model)) throw backendError("config", "Invalid model");
114
+ const endpoint = new URL(url.href);
115
+ endpoint.pathname = `${url.pathname.replace(/\/+$/, "")}/v1/systemone`;
116
+ if (endpoint.origin !== url.origin) throw backendError("config", "Invalid backend endpoint");
117
+ const timeout = AbortSignal.timeout(timeoutMs);
118
+ const combined = signal ? AbortSignal.any([signal, timeout]) : timeout;
119
+ for (let attempt = 0; attempt < 3; attempt++) {
120
+ let reply;
121
+ try {
122
+ reply = await fetcher(endpoint.href, {
123
+ method: "POST",
124
+ redirect: "error",
125
+ signal: combined,
126
+ headers: { authorization: `Bearer ${key}`, "content-type": "application/json" },
127
+ body: JSON.stringify({ state, questions, model })
128
+ });
129
+ } catch {
130
+ throw backendError("unreachable", "Backend request failed or timed out");
131
+ }
132
+ if (reply.ok) return validateResponse(await boundedJson(reply), questions);
133
+ await reply.body?.cancel().catch(() => {
134
+ });
135
+ if (![429, 529].includes(reply.status) || attempt === 2) throw backendError("http-error", `Backend HTTP ${reply.status}`);
136
+ try {
137
+ await wait(retryDelayMs * (attempt + 1), void 0, { signal: combined });
138
+ } catch {
139
+ throw backendError("unreachable", "Backend request failed or timed out");
140
+ }
141
+ }
142
+ throw backendError("http-error", "Backend retries exhausted");
143
+ }
144
+ };
145
+ }
146
+ export {
147
+ createBackend,
148
+ validateRequest
149
+ };
@@ -0,0 +1,12 @@
1
+ import type { ConfigView, FeatureKey, Features, ResolvedConfig } from './types.js';
2
+ export declare const DEFAULT_URL = "https://api.typesafe.ai";
3
+ export declare const DEFAULT_MODEL = "jev-latest";
4
+ export declare function configPath(env?: Record<string, string | undefined>): string;
5
+ export declare const FEATURE_KEYS: readonly FeatureKey[];
6
+ export declare function featureEnabled(features: Features | undefined, key: FeatureKey): boolean;
7
+ export declare function loadConfig(path?: string): Promise<ConfigView>;
8
+ export declare function resolveConfig({ path, env }?: {
9
+ path?: string;
10
+ env?: Record<string, string | undefined>;
11
+ }): Promise<ResolvedConfig>;
12
+ export declare function saveConfig(input: Record<string, unknown>, path?: string): Promise<ConfigView>;