dsh-decision-layer 0.7.3 → 0.7.5

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/lib/client.js CHANGED
@@ -38,6 +38,7 @@ var zh = {
38
38
  metrics: "\u81EA\u52A8\u88C1\u51B3\u7EDF\u8BA1",
39
39
  triggerDecisions: "\u51B3\u7B56",
40
40
  empty: "\u5C1A\u65E0\u81EA\u52A8\u88C1\u51B3\u3002\u624B\u52A8\u88C1\u51B3\u4E0D\u8BA1\u5165\u8FD9\u91CC\u3002",
41
+ checkpointDockHint: "\u53E6\u6709 {count} \u6B21\u53EA\u8BFB\u8FC7\u7A0B\u68C0\u67E5\uFF0C\u89C1\u300C\u51B3\u7B56\u5206\u6790\u300D\u9875\u3002",
41
42
  attempts: "\u8BC4\u4F30\u6B21\u6570",
42
43
  failures: "\u5931\u8D25\u56DE\u9000",
43
44
  suggestions: "\u6A21\u578B\u5EFA\u8BAE\uFF08\u653E\u884C / \u8BE2\u95EE / \u62D2\u7EDD\uFF09",
@@ -62,7 +63,7 @@ var zh = {
62
63
  narrowKeptCount: "\u4FDD\u7559",
63
64
  narrowMore: " \u7B49 {count} \u4E2A",
64
65
  narrowObserve: "\u4EC5\u89C2\u5BDF",
65
- narrowEnforce: "\u5DF2\u751F\u6548",
66
+ narrowEnforce: "\u5DF2\u751F\u6548\uFF08\u4E0B\u4E00\u6B65\u8D77\uFF09",
66
67
  narrowNotApplied: "\u672A\u751F\u6548",
67
68
  checkPassRate: "\u901A\u8FC7\u7387",
68
69
  narrowApplyRate: "\u6536\u7A84\u7387",
@@ -92,8 +93,8 @@ var zh = {
92
93
  logPathLabel: "\u8DEF\u5F84\uFF1A",
93
94
  logSubjectLabel: "\u8BF7\u6C42\uFF1A",
94
95
  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",
95
- logCheckHint: "\u6BCF\u56DE\u5408\u7ED3\u675F\u540E\uFF0C\u628A\u8FD9\u8F6E AI \u7684\u6700\u7EC8\u56DE\u590D\u8FDE\u540C\u672C\u8F6E\u8BF7\u6C42\u4E0E\u5FC5\u8981\u524D\u6587\uFF08\u6700\u8FD1\u4E00\u6761\u7528\u6237\u8981\u6C42\u4E0E AI \u56DE\u590D\uFF0C\u4F5C\u80CC\u666F\uFF0C\u5747\u5DF2\u906E\u853D\u51ED\u636E\uFF09\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",
96
- logNarrowHint: "\u6BCF\u56DE\u5408\u5F00\u59CB\u65F6\uFF0C\u4EE5\u53CA\u540C\u4E00\u56DE\u5408\u5185\u4F60\u63D0\u51FA\u65B0\u8981\u6C42\u65F6\uFF0C\u628A\u5F53\u524D\u8BF7\u6C42\u3001\u5FC5\u8981\u524D\u6587\u4E0E\u6700\u8FD1\u6267\u884C\u8FDB\u5C55\u8FDE\u540C\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\u65F6\u95F4\u63D0\u793A\u3001\u540E\u53F0\u901A\u77E5\u7B49\u975E\u7528\u6237\u8981\u6C42\u4E0D\u4F1A\u89E6\u53D1\u91CD\u65B0\u5224\u65AD\u3002",
96
+ logCheckHint: "\u6BCF\u56DE\u5408\u7ED3\u675F\u540E\uFF0C\u628A\u8FD9\u8F6E AI \u7684\u6700\u7EC8\u56DE\u590D\u8FDE\u540C\u672C\u8F6E\u8BF7\u6C42\u4E0E\u5FC5\u8981\u524D\u6587\uFF08\u6700\u8FD1\u4E00\u6761\u7528\u6237\u8981\u6C42\u4E0E AI \u56DE\u590D\uFF0C\u4F5C\u80CC\u666F\uFF0C\u5747\u5DF2\u906E\u853D\u51ED\u636E\uFF09\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\u4F4E\u5206\u884C\u5E26\u300C\u4F4E\u8BC4\u5206\u300D\u6807\u8BB0\uFF0C\u53EF\u5728\u51B3\u7B56\u5206\u6790\u9875\u53EA\u770B\u4F4E\u8BC4\u5206\u3002",
97
+ logNarrowHint: "\u6BCF\u56DE\u5408\u5F00\u59CB\u65F6\uFF0C\u4EE5\u53CA\u540C\u4E00\u56DE\u5408\u5185\u4F60\u63D0\u51FA\u65B0\u8981\u6C42\u65F6\uFF0C\u628A\u5F53\u524D\u8BF7\u6C42\u3001\u5FC5\u8981\u524D\u6587\u4E0E\u6700\u8FD1\u6267\u884C\u8FDB\u5C55\u8FDE\u540C\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\u9650\u5236\u5DE5\u5177\uFF08\u5BBF\u4E3B\u5148\u89E3\u6790\u672C\u6B65\u5DE5\u5177\u96C6\uFF0C\u56E0\u6B64\u4ECE\u4E0B\u4E00\u6B65\u8D77\u751F\u6548\uFF09\u3002\u65F6\u95F4\u63D0\u793A\u3001\u540E\u53F0\u901A\u77E5\u7B49\u975E\u7528\u6237\u8981\u6C42\u4E0D\u4F1A\u89E6\u53D1\u91CD\u65B0\u5224\u65AD\u3002",
97
98
  actionDeny: "\u5DF2\u62D2\u7EDD",
98
99
  actionAsk: "\u8F6C\u4EA4\u5BA1\u6279",
99
100
  actionPass: "\u672A\u5E72\u9884",
@@ -101,6 +102,10 @@ var zh = {
101
102
  checkResultLow: "\u5F97\u5206\u504F\u4F4E",
102
103
  checkResultOk: "\u901A\u8FC7",
103
104
  checkResultLowNoisy: "\u5F97\u5206\u504F\u4F4E\uFF08\u4F4E\u7F6E\u4FE1\uFF09",
105
+ logLowBadge: "\u4F4E\u8BC4\u5206",
106
+ logLowOnly: "\u53EA\u770B\u4F4E\u8BC4\u5206",
107
+ logLowOnlyHint: "\u4EC5\u663E\u793A\u81EA\u68C0\u5F97\u5206\u504F\u4F4E\u7684\u8BB0\u5F55\uFF08\u542B\u4F4E\u7F6E\u4FE1\u566A\u58F0\uFF09\uFF0C\u7528\u4E8E\u5FEB\u901F\u5B9A\u4F4D\u662F\u54EA\u4E00\u56DE\u5408\u3002",
108
+ logLowEmpty: "\u672C\u4F1A\u8BDD\u6CA1\u6709\u4F4E\u8BC4\u5206\u8BB0\u5F55\u3002",
104
109
  checkNoiseTag: "\u566A\u58F0\uFF0C\u53EF\u5FFD\u7565",
105
110
  checkNoiseHint: "\u6A21\u578B\u5BF9\u8FD9\u6B21\u6253\u5206\u51E0\u4E4E\u6CA1\u6709\u628A\u63E1\uFF08\u7F6E\u4FE1\u5EA6\u6781\u4F4E\uFF09\uFF0C\u8FD9\u6761\u4F4E\u5206\u4E0D\u8BA1\u5165\u8D28\u91CF\u8D8B\u52BF\u3001\u4E5F\u4E0D\u89E6\u53D1\u4EFB\u4F55\u8865\u5B8C\uFF0C\u89C6\u4F5C\u566A\u58F0\u5FFD\u7565\u5373\u53EF\u3002\u771F\u6B63\u503C\u5F97\u5173\u6CE8\u7684\u662F\u8FDE\u7EED\u591A\u6B21\u9AD8\u7F6E\u4FE1\u7684\u4F4E\u5206\u3002",
106
111
  reasonUnavailable: "\u540E\u7AEF\u4E0D\u53EF\u8FBE",
@@ -118,7 +123,7 @@ var zh = {
118
123
  selfCheckHint: "\u6BCF\u56DE\u5408\u7ED3\u675F\u7ED9\u6700\u7EC8\u56DE\u590D\u6253\u5206\uFF080\u20132\uFF0C\u6309\u5404\u6863\u4F4D\u6982\u7387\u52A0\u6743\uFF0C\u53EF\u80FD\u662F\u5C0F\u6570\u5982 1.4\uFF09\u3002\u5173\u95ED\uFF1A\u53EA\u628A\u4F4E\u5206\u8BB0\u8FDB\u65E5\u5FD7\u548C\u7EDF\u8BA1\uFF0C\u4E0D\u6253\u65AD\u5BF9\u8BDD\u3002\u5F00\u542F\uFF1A\u5F97\u5206\u504F\u4F4E\uFF08\u22641\uFF09\u4E14\u6A21\u578B\u6709\u628A\u63E1\u65F6\uFF0C\u81EA\u52A8\u4EE5\u4F60\u7684\u540D\u4E49\u8FFD\u52A0\u4E00\u6761\u63D0\u793A\uFF0C\u8BA9 AI \u590D\u6838\u6709\u65E0\u9057\u6F0F\u6216\u77DB\u76FE\u5E76\u8865\u5B8C\uFF1B\u7F6E\u4FE1\u5EA6\u504F\u4F4E\u7684\u6253\u5206\u53EA\u8BB0\u5F55\u3001\u4E0D\u6253\u65AD\uFF0C\u540C\u4E00\u56DE\u5408\u6700\u591A\u8865\u4E00\u6B21\u3002",
119
124
  narrow: "\u5DE5\u5177\u6536\u7A84",
120
125
  narrowObserveLabel: "\u4EC5\u89C2\u5BDF\uFF08\u4E0D\u771F\u6B63\u9650\u5236\u5DE5\u5177\uFF09",
121
- narrowHint: "\u6BCF\u56DE\u5408\u5F00\u59CB\u65F6\u3001\u4EE5\u53CA\u540C\u4E00\u56DE\u5408\u5185\u4F60\u63D0\u51FA\u65B0\u8981\u6C42\u65F6\uFF0C\u7528\u88C1\u51B3\u6A21\u578B\u5224\u65AD\u5F53\u524D\u53EF\u9009\u5DE5\u5177\u4E0E\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",
126
+ narrowHint: "\u6BCF\u56DE\u5408\u5F00\u59CB\u65F6\u3001\u4EE5\u53CA\u540C\u4E00\u56DE\u5408\u5185\u4F60\u63D0\u51FA\u65B0\u8981\u6C42\u65F6\uFF0C\u7528\u88C1\u51B3\u6A21\u578B\u5224\u65AD\u5F53\u524D\u53EF\u9009\u5DE5\u5177\u4E0E\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\u4ECE\u4E0B\u4E00\u6B65\u8D77\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\uFF1B\u5BBF\u4E3B\u5728\u672C\u6B65\u51B3\u7B56\u70B9\u89E6\u53D1\u524D\u5DF2\u786E\u5B9A\u5DE5\u5177\u96C6\uFF0C\u56E0\u6B64\u672C\u6B65\u81EA\u8EAB\u7684\u8C03\u7528\u4ECD\u6309\u539F\u96C6\u5408\u653E\u884C\u3002\u6536\u7A84\u53EA\u4F5C\u7528\u4E8E\u5DE5\u5177\u901A\u9053\uFF1A\u56DE\u590D\u6B63\u6587\u91CC\u7684 UI \u56F4\u680F\u3001\u6280\u80FD\u8BF4\u660E\u7B49\u975E\u5DE5\u5177\u80FD\u529B\u4E0D\u53D7\u5F71\u54CD\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",
122
127
  narrowKeepLabel: "\u59CB\u7EC8\u4FDD\u7559\u7684\u5DE5\u5177\u524D\u7F00",
123
128
  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",
124
129
  narrowMaxLabel: "\u5019\u9009\u5DE5\u5177\u4E0A\u9650",
@@ -181,6 +186,49 @@ var zh = {
181
186
  completeUnsatisfied: "\u672A\u6EE1\u8DB3",
182
187
  completeInsufficient: "\u8BC1\u636E\u4E0D\u8DB3",
183
188
  completeSteered: "\u5DF2\u8865\u4E00\u8F6E",
189
+ checkpointSettings: "\u8FC7\u7A0B\u68C0\u67E5\uFF08\u53EA\u8BFB\uFF09",
190
+ checkpointEnabled: "\u542F\u7528\u53EA\u8BFB\u8FC7\u7A0B\u68C0\u67E5",
191
+ checkpointHint: "\u9ED8\u8BA4\u5173\u95ED\uFF0C\u5E76\u4ECD\u53D7\u4F1A\u8BDD\u603B\u5F00\u5173\u63A7\u5236\u3002\u663E\u5F0F\u5F00\u542F\u5E76\u4FDD\u5B58\u540E\uFF0C\u4F1A\u628A\u5F53\u524D\u8981\u6C42\u4E0E\u6709\u754C\u7684\u8131\u654F\u8FC7\u7A0B\u8BC1\u636E\uFF08\u5DE5\u5177\u540D\u3001\u6210\u8D25\u3001\u547D\u4EE4\u3001\u8DEF\u5F84\u548C Shell \u6458\u5F55\uFF09\u53D1\u9001\u5230\u5DF2\u914D\u7F6E\u7684\u88C1\u51B3\u7AEF\u70B9\uFF0C\u5728\u540E\u53F0\u505A\u53EA\u8BFB\u5224\u65AD\uFF1B\u4E0D\u81EA\u52A8\u8FFD\u52A0\u63D0\u793A\uFF0C\u4E5F\u4E0D\u6539\u53D8\u5DE5\u5177\u9650\u5236\u3002\u4E0D\u53D1\u9001\u8BFB\u6587\u4EF6\u3001\u641C\u7D22\u6216\u7F51\u9875\u7ED3\u679C\u539F\u6587\u3002",
192
+ checkpointHeartbeatLabel: "\u5FC3\u8DF3\u6B65\u6570",
193
+ checkpointHeartbeatHint: "\u7D2F\u8BA1\u591A\u5C11\u6B65\u540E\u5B89\u6392\u4E00\u6B21\u8FC7\u7A0B\u68C0\u67E5\uFF0C\u9ED8\u8BA4 6\uFF1B\u6574\u6570\uFF0C2\u2013100\u3002",
194
+ checkpointFailureLabel: "\u8FDE\u7EED\u5931\u8D25\u9608\u503C",
195
+ checkpointFailureHint: "\u672A\u89E3\u51B3\u5931\u8D25\u8FBE\u5230\u6B64\u6570\u91CF\u65F6\u5B89\u6392\u68C0\u67E5\uFF0C\u9ED8\u8BA4 3\uFF1B\u6574\u6570\uFF0C2\u201310\u3002",
196
+ checkpointTimeoutLabel: "\u8FC7\u7A0B\u68C0\u67E5\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09",
197
+ checkpointTimeoutHint: "\u5355\u6B21\u540E\u53F0\u68C0\u67E5\u7684\u622A\u6B62\u65F6\u95F4\uFF0C\u9ED8\u8BA4 1500\uFF1B\u6574\u6570\uFF0C100\u201310000\u3002\u8D85\u65F6\u4E0D\u5F71\u54CD\u6267\u884C\u3002",
198
+ logCheckpoint: "\u8FC7\u7A0B\u68C0\u67E5",
199
+ checkpointReadOnly: "\u53EA\u8BFB",
200
+ checkpointHeartbeat: "\u5FC3\u8DF3\u68C0\u67E5",
201
+ checkpointFailure: "\u672A\u89E3\u51B3\u5931\u8D25",
202
+ checkpointVerification: "\u4FEE\u6539\u540E\u9A8C\u8BC1",
203
+ checkpointTriggerUnknown: "\u68C0\u67E5\u89E6\u53D1",
204
+ checkpointOk: "\u89C2\u5BDF\u672A\u89C1\u7591\u70B9",
205
+ checkpointConcern: "\u89C2\u5BDF\u5230\u5F85\u590D\u6838\u4FE1\u53F7",
206
+ checkpointUncertain: "\u5224\u65AD\u4E0D\u786E\u5B9A",
207
+ checkpointSkipped: "\u5DF2\u8DF3\u8FC7",
208
+ checkpointThroughSeq: "\u8BC1\u636E\u622A\u81F3 #{seq}",
209
+ checkpointEvidenceCount: "\u5019\u9009\u8BC1\u636E {count} \u6761",
210
+ checkpointReasonUnknown: "\u672A\u4EA7\u751F\u53EF\u7528\u5224\u65AD",
211
+ checkpointReasonCanceled: "\u5DF2\u53D6\u6D88 / \u7ED3\u679C\u5DF2\u5931\u6548",
212
+ checkpointReasonDisabled: "\u8FC7\u7A0B\u68C0\u67E5\u5DF2\u5173\u95ED",
213
+ checkpointReasonNoTask: "\u65E0\u7ED1\u5B9A\u4EFB\u52A1",
214
+ checkpointReasonStale: "\u4E0A\u4E0B\u6587\u5DF2\u53D8\u5316",
215
+ checkpointReasonTimeout: "\u68C0\u67E5\u8D85\u65F6",
216
+ checkpointReasonBusy: "\u68C0\u67E5\u9884\u7B97\u6216\u961F\u5217\u5DF2\u6EE1",
217
+ checkpointReasonUnchanged: "\u6CA1\u6709\u65B0\u8BC1\u636E",
218
+ checkpointProfileNote: "\u53EA\u8BFB\u89C2\u5BDF\u4FE1\u53F7\uFF1B\u8DF3\u8FC7\u4E0D\u8BA1\u5165\u8BC4\u4F30\u6B21\u6570\uFF0C\u4E5F\u4E0D\u5F71\u54CD\u6700\u7EC8\u56DE\u590D\u8D28\u91CF\u8D8B\u52BF\u3002\u89C2\u5BDF\u672A\u89C1\u7591\u70B9\u4E0D\u4EE3\u8868\u4EFB\u52A1\u901A\u8FC7\u9A8C\u6536\u3002",
219
+ cardCheckpoint: "\u8FC7\u7A0B\u68C0\u67E5",
220
+ telemetryTitle: "\u51B3\u7B56\u6D4B\u91CF",
221
+ telemetryCoverage: "\u8017\u65F6\u6D4B\u91CF\u8986\u76D6",
222
+ telemetryUsageCoverage: "\u7528\u91CF\u6D4B\u91CF\u8986\u76D6",
223
+ telemetryMeanLatency: "\u5E73\u5747\u8017\u65F6",
224
+ telemetryP95Latency: "P95 \u8017\u65F6",
225
+ telemetryInputTokens: "\u8F93\u5165 token",
226
+ telemetryOutputTokens: "\u8F93\u51FA token",
227
+ telemetryBackendAttempts: "\u540E\u7AEF\u8BF7\u6C42\u6B21\u6570",
228
+ telemetryMeasuredCount: "\u5DF2\u6D4B {count} \u6B21",
229
+ telemetryUnknown: "\u672A\u77E5",
230
+ telemetryMs: "{value} ms",
231
+ telemetryNote: "\u8986\u76D6\u6309\u5DF2\u8BC4\u4F30\u7684\u51B3\u7B56\u884C\u8BA1\u7B97\uFF0C\u4E0D\u542B\u8FC7\u7A0B\u68C0\u67E5\u7684\u8DF3\u8FC7\uFF1B\u7528\u91CF\u4EC5\u6C47\u603B\u5DF2\u8BB0\u5F55\u6837\u672C\u3002\u7F3A\u5931\u4E0D\u7B49\u4E8E\u96F6\uFF0C\u8FD9\u4E9B\u6D4B\u91CF\u4E0D\u8BC1\u660E\u51C6\u786E\u7387\u6216\u6210\u672C\u6536\u76CA\u3002",
184
232
  backend: "\u88C1\u51B3\u540E\u7AEF",
185
233
  url: "\u670D\u52A1\u5730\u5740",
186
234
  model: "\u6A21\u578B",
@@ -236,6 +284,7 @@ var en = {
236
284
  metrics: "Automatic decisions",
237
285
  triggerDecisions: "decisions",
238
286
  empty: "No automatic decisions yet. Manual verdicts are not counted here.",
287
+ checkpointDockHint: "Also {count} read-only process checks; see the Decision analysis tab.",
239
288
  attempts: "Evaluations",
240
289
  failures: "Fallback failures",
241
290
  suggestions: "Model suggestions (allow / ask / deny)",
@@ -260,7 +309,7 @@ var en = {
260
309
  narrowKeptCount: "kept",
261
310
  narrowMore: " and {count} in total",
262
311
  narrowObserve: "observe",
263
- narrowEnforce: "enforced",
312
+ narrowEnforce: "enforced (from next step)",
264
313
  narrowNotApplied: "not applied",
265
314
  checkPassRate: "Pass rate",
266
315
  narrowApplyRate: "Narrow rate",
@@ -290,8 +339,8 @@ var en = {
290
339
  logPathLabel: "path: ",
291
340
  logSubjectLabel: "request: ",
292
341
  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.",
293
- logCheckHint: "After each turn, the AI\u2019s final reply for that turn, together with the request and the necessary recent background (the last user ask and AI reply, credentials masked), 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).",
294
- logNarrowHint: "At the start of each turn, and again when a new user request arrives inside it, the current request, the necessary background and the latest execution progress are sent with each available tool 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.",
342
+ logCheckHint: "After each turn, the AI\u2019s final reply for that turn, together with the request and the necessary recent background (the last user ask and AI reply, credentials masked), 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). Low rows carry a \u201Clow score\u201D badge and can be filtered in the decision analysis tab.",
343
+ logNarrowHint: "At the start of each turn, and again when a new user request arrives inside it, the current request, the necessary background and the latest execution progress are sent with each available tool 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, starting from the next step because the host resolves the tool set for the current step before the hook runs.",
295
344
  actionDeny: "denied",
296
345
  actionAsk: "sent to approval",
297
346
  actionPass: "no intervention",
@@ -299,6 +348,10 @@ var en = {
299
348
  checkResultLow: "low score",
300
349
  checkResultOk: "passed",
301
350
  checkResultLowNoisy: "low score (low confidence)",
351
+ logLowBadge: "low score",
352
+ logLowOnly: "Low scores only",
353
+ logLowOnlyHint: "Show only low self-check scores (including low-confidence noise), to find which turn was scored low.",
354
+ logLowEmpty: "No low self-check scores in this session.",
302
355
  checkNoiseTag: "noise, ignore",
303
356
  checkNoiseHint: "The model had almost no confidence in this score (near-zero confidence). This low score does not count toward the quality trend and triggers no re-prompt \u2014 treat it as noise. What matters is several consecutive high-confidence low scores.",
304
357
  reasonUnavailable: "backend unreachable",
@@ -316,7 +369,7 @@ var en = {
316
369
  selfCheckHint: "Each turn ends by scoring the final reply (0\u20132, a probability-weighted mean across the levels, so it can be fractional like 1.4). Off: low scores are only logged, the conversation is not interrupted. On: when the score is low (\u22641) and the model is confident enough, one prompt is appended on your behalf asking the AI to review for gaps or contradictions and finish the work; low-confidence scores are only logged, never interrupt, and at most one prompt per turn.",
317
370
  narrow: "Tool narrowing",
318
371
  narrowObserveLabel: "Observe only (do not restrict tools)",
319
- narrowHint: "At the start of each turn, and again when a new user request arrives inside it, 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.",
372
+ narrowHint: "At the start of each turn, and again when a new user request arrives inside it, 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: from the next step on, only the relevant tools are kept, intersected with the host-allowed set and never expanded; the host fixes the tool set for the current step before the decision point fires, so that step still runs with the original set. Narrowing covers the tool channel only: UI fences in the reply body and skill instructions are unaffected. 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.",
320
373
  narrowKeepLabel: "Always-kept tool prefixes",
321
374
  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.",
322
375
  narrowMaxLabel: "Candidate tool cap",
@@ -379,6 +432,49 @@ var en = {
379
432
  completeUnsatisfied: "unmet",
380
433
  completeInsufficient: "insufficient",
381
434
  completeSteered: "re-prompted",
435
+ checkpointSettings: "Process checks (read-only)",
436
+ checkpointEnabled: "Enable read-only process checks",
437
+ checkpointHint: "Off by default and still subject to the session switch. After you explicitly enable and save, the current request and bounded, redacted process evidence (tool names, outcomes, commands, paths and shell excerpts) are sent to the configured decision endpoint for a background, read-only judgement. No prompts are added and no tool restrictions change. File-read, search and webpage result bodies are not sent.",
438
+ checkpointHeartbeatLabel: "Heartbeat steps",
439
+ checkpointHeartbeatHint: "Schedule a process check after this many steps; default 6. Integer, 2\u2013100.",
440
+ checkpointFailureLabel: "Consecutive failure threshold",
441
+ checkpointFailureHint: "Schedule a check when unresolved failures reach this count; default 3. Integer, 2\u201310.",
442
+ checkpointTimeoutLabel: "Process check timeout (ms)",
443
+ checkpointTimeoutHint: "Deadline for one background check; default 1500. Integer, 100\u201310000. A timeout does not affect execution.",
444
+ logCheckpoint: "Process check",
445
+ checkpointReadOnly: "read-only",
446
+ checkpointHeartbeat: "heartbeat",
447
+ checkpointFailure: "unresolved failures",
448
+ checkpointVerification: "verification after change",
449
+ checkpointTriggerUnknown: "check trigger",
450
+ checkpointOk: "no concern observed",
451
+ checkpointConcern: "signal for review observed",
452
+ checkpointUncertain: "uncertain judgement",
453
+ checkpointSkipped: "skipped",
454
+ checkpointThroughSeq: "evidence through #{seq}",
455
+ checkpointEvidenceCount: "{count} candidate evidence items",
456
+ checkpointReasonUnknown: "no usable judgement",
457
+ checkpointReasonCanceled: "canceled / result discarded",
458
+ checkpointReasonDisabled: "process checks disabled",
459
+ checkpointReasonNoTask: "no bound task",
460
+ checkpointReasonStale: "context changed",
461
+ checkpointReasonTimeout: "check timed out",
462
+ checkpointReasonBusy: "check budget or queue full",
463
+ checkpointReasonUnchanged: "no new evidence",
464
+ checkpointProfileNote: "Read-only observation signals. Skips do not count as evaluations or affect final-reply quality trends. No concern observed does not mean the task passed acceptance.",
465
+ cardCheckpoint: "Process checks",
466
+ telemetryTitle: "Decision measurements",
467
+ telemetryCoverage: "Latency coverage",
468
+ telemetryUsageCoverage: "Usage coverage",
469
+ telemetryMeanLatency: "Mean latency",
470
+ telemetryP95Latency: "P95 latency",
471
+ telemetryInputTokens: "Input tokens",
472
+ telemetryOutputTokens: "Output tokens",
473
+ telemetryBackendAttempts: "Backend requests",
474
+ telemetryMeasuredCount: "{count} measured",
475
+ telemetryUnknown: "unknown",
476
+ telemetryMs: "{value} ms",
477
+ telemetryNote: "Coverage uses evaluated decision rows and excludes skipped process checks. Usage totals cover measured samples only. Missing is not zero; these measurements do not establish accuracy or cost benefits.",
382
478
  backend: "Decision backend",
383
479
  url: "Server URL",
384
480
  model: "Model",
@@ -556,9 +652,10 @@ function renderCheck({ t }, entry) {
556
652
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { title: t("logCheckHint"), children: t("logCheck") }),
557
653
  turnTag(t, entry)
558
654
  ] });
559
- const subject = entry.subject ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-subject", title: `${t("logSubjectLabel")}${entry.subject}`, children: [
655
+ const subjectText = entry.subject ?? entry.task;
656
+ const subject = subjectText ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-subject", title: `${t("logSubjectLabel")}${subjectText}`, children: [
560
657
  "\u300C",
561
- entry.subject.length > 60 ? `${entry.subject.slice(0, 60)}\u2026` : entry.subject,
658
+ subjectText.length > 60 ? `${subjectText.slice(0, 60)}\u2026` : subjectText,
562
659
  "\u300D"
563
660
  ] }) : null;
564
661
  if (entry.outcome === "error") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
@@ -573,8 +670,10 @@ function renderCheck({ t }, entry) {
573
670
  entry.reason ? ` \xB7 ${failDetail(t, entry)}` : ""
574
671
  ] })
575
672
  ] });
576
- const noisy = entry.outcome === "low" && entry.lowConfidence === true;
577
- const result = entry.outcome === "low" ? noisy ? t("checkResultLowNoisy") : t("checkResultLow") : t("checkResultOk");
673
+ const low = entry.outcome === "low";
674
+ const noisy = low && entry.lowConfidence === true;
675
+ const badge = low ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `decision-log-badge ${noisy ? "decision-log-badge-noise" : "decision-log-badge-low"}`, children: t("logLowBadge") }) : null;
676
+ const result = low ? noisy ? t("checkResultLowNoisy") : t("checkResultLow") : t("checkResultOk");
578
677
  const verdict = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: noisy ? "decision-log-muted-verdict" : "decision-log-verdict", children: result });
579
678
  const score = entry.score !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", title: t("logScoreHint"), children: [
580
679
  " \xB7 ",
@@ -600,6 +699,7 @@ function renderCheck({ t }, entry) {
600
699
  subject
601
700
  ] }) : null,
602
701
  " \xB7 ",
702
+ badge,
603
703
  verdict,
604
704
  score,
605
705
  conf,
@@ -709,7 +809,86 @@ function renderComplete({ t }, entry) {
709
809
  steer
710
810
  ] });
711
811
  }
812
+ function checkpointReason(t, reason) {
813
+ switch (reason) {
814
+ case "disabled":
815
+ return t("checkpointReasonDisabled");
816
+ case "no-task":
817
+ return t("checkpointReasonNoTask");
818
+ case "stale":
819
+ case "superseded":
820
+ return t("checkpointReasonStale");
821
+ case "canceled":
822
+ case "aborted":
823
+ return t("checkpointReasonCanceled");
824
+ case "timeout":
825
+ return t("checkpointReasonTimeout");
826
+ case "queue-full":
827
+ case "budget":
828
+ return t("checkpointReasonBusy");
829
+ case "unchanged":
830
+ case "no-evidence":
831
+ return t("checkpointReasonUnchanged");
832
+ case "low-confidence":
833
+ return t("reasonLowConfidence");
834
+ case "unavailable":
835
+ case "unreachable":
836
+ return t("reasonUnavailable");
837
+ case "http-error":
838
+ return t("reasonHttpError");
839
+ case "invalid-response":
840
+ return t("reasonInvalidResponse");
841
+ case "config":
842
+ return t("reasonConfig");
843
+ default:
844
+ return t("checkpointReasonUnknown");
845
+ }
846
+ }
847
+ var checkpointOutcome = (entry) => entry.outcome === "concern" && entry.lowConfidence === true ? "uncertain" : entry.outcome;
848
+ function renderCheckpoint({ t }, entry) {
849
+ const outcome = checkpointOutcome(entry);
850
+ const discarded = outcome === "error" && (entry.reason === "canceled" || entry.reason === "aborted");
851
+ const result = discarded ? t("checkpointReasonCanceled") : outcome === "ok" ? t("checkpointOk") : outcome === "concern" ? t("checkpointConcern") : outcome === "error" ? t("logEvalFailed") : outcome === "skipped" ? t("checkpointSkipped") : t("checkpointUncertain");
852
+ const trigger = entry.trigger === "heartbeat" ? t("checkpointHeartbeat") : entry.trigger === "unresolved_failure" ? t("checkpointFailure") : entry.trigger === "verification_after_change" ? t("checkpointVerification") : t("checkpointTriggerUnknown");
853
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
854
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("logCheckpoint") }),
855
+ turnTag(t, entry),
856
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
857
+ " \xB7 ",
858
+ t("checkpointReadOnly"),
859
+ " \xB7 ",
860
+ trigger
861
+ ] }),
862
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: outcome === "uncertain" || outcome === "skipped" || discarded ? "decision-log-muted-verdict" : "decision-log-verdict", children: [
863
+ " \xB7 ",
864
+ result
865
+ ] }),
866
+ Number.isSafeInteger(entry.throughSeq) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
867
+ " \xB7 ",
868
+ t("checkpointThroughSeq", { seq: entry.throughSeq })
869
+ ] }),
870
+ Number.isSafeInteger(entry.evidenceCount) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
871
+ " \xB7 ",
872
+ t("checkpointEvidenceCount", { count: entry.evidenceCount })
873
+ ] }),
874
+ typeof entry.confidence === "number" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
875
+ " \xB7 ",
876
+ t("logConfidence"),
877
+ " ",
878
+ entry.confidence
879
+ ] }),
880
+ entry.reason && !discarded && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
881
+ " \xB7 ",
882
+ checkpointReason(t, entry.reason)
883
+ ] })
884
+ ] });
885
+ }
712
886
  function outcomeTagOf(entry) {
887
+ if (entry.kind === "checkpoint") {
888
+ const outcome = checkpointOutcome(entry);
889
+ const discarded = outcome === "error" && (entry.reason === "canceled" || entry.reason === "aborted");
890
+ return discarded ? "muted" : outcome === "concern" ? "warn" : outcome === "error" ? "error" : outcome === "ok" ? "ok" : "muted";
891
+ }
713
892
  if (entry.kind === "narrow") return entry.outcome === "error" ? "error" : "ok";
714
893
  if (entry.kind === "complete") return entry.outcome === "error" ? "error" : entry.outcome === "unsatisfied" ? "warn" : "ok";
715
894
  if (entry.outcome === "deny") return "deny";
@@ -718,7 +897,7 @@ function outcomeTagOf(entry) {
718
897
  return "ok";
719
898
  }
720
899
  function renderBody(props, entry) {
721
- return entry.kind === "gate" ? renderGate(props, entry) : entry.kind === "narrow" ? renderNarrow(props, entry) : entry.kind === "complete" ? renderComplete(props, entry) : renderCheck(props, entry);
900
+ return entry.kind === "gate" ? renderGate(props, entry) : entry.kind === "narrow" ? renderNarrow(props, entry) : entry.kind === "complete" ? renderComplete(props, entry) : entry.kind === "checkpoint" ? renderCheckpoint(props, entry) : renderCheck(props, entry);
722
901
  }
723
902
  function RatingButtons({ id, t, ratings, onRate }) {
724
903
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-rate", role: "group", "aria-label": t("rateGroup"), children: [
@@ -739,7 +918,7 @@ function RatingButtons({ id, t, ratings, onRate }) {
739
918
  }
740
919
  function DecisionLogList({ entries, ratings, onRate, t, newestFirst = true }) {
741
920
  const rows = newestFirst ? entries.slice().reverse() : entries.slice();
742
- 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: [
921
+ 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)}${entry.kind === "check" && entry.outcome === "low" ? " decision-log-low" : ""}`, children: [
743
922
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-time", children: time(entry.at) }),
744
923
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "decision-log-body", children: [
745
924
  renderBody({ t }, entry),
@@ -1002,7 +1181,10 @@ function SessionPanel({ sessionId, t, useProjection }) {
1002
1181
  ] }),
1003
1182
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { children: [
1004
1183
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: t("metrics") }),
1005
- !metrics?.gate && !metrics?.check && !metrics?.narrow && !metrics?.complete ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-empty", role: "status", children: t("empty") }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-cards", children: [
1184
+ !metrics?.gate && !metrics?.check && !metrics?.narrow && !metrics?.complete ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1185
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-empty", role: "status", children: t("empty") }),
1186
+ metrics?.checkpoint && metrics.checkpoint.attempts + metrics.checkpoint.skipped > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-muted", children: t("checkpointDockHint", { count: metrics.checkpoint.attempts + metrics.checkpoint.skipped }) })
1187
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-cards", children: [
1006
1188
  metrics.gate && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "decision-card decision-card-wide", children: [
1007
1189
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { className: "decision-card-head", children: [
1008
1190
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-card-title", children: t("cardGate") }),
@@ -1114,6 +1296,7 @@ function AnalysisView({ sessionId, t }) {
1114
1296
  const [analysis, setAnalysis] = (0, import_react3.useState)(null);
1115
1297
  const [rows, setRows] = (0, import_react3.useState)(null);
1116
1298
  const [logPage, setLogPage] = (0, import_react3.useState)(1);
1299
+ const [lowOnly, setLowOnly] = (0, import_react3.useState)(false);
1117
1300
  const [loaded, setLoaded] = (0, import_react3.useState)(false);
1118
1301
  const [message, setMessage] = (0, import_react3.useState)("");
1119
1302
  const [enabled, setEnabled] = (0, import_react3.useState)(null);
@@ -1135,6 +1318,7 @@ function AnalysisView({ sessionId, t }) {
1135
1318
  active.current = sessionId;
1136
1319
  setBusy(false);
1137
1320
  setMessage("");
1321
+ setLowOnly(false);
1138
1322
  loadSession();
1139
1323
  }, [sessionId]);
1140
1324
  (0, import_react3.useEffect)(() => onSessionEnabled((change) => {
@@ -1187,17 +1371,23 @@ function AnalysisView({ sessionId, t }) {
1187
1371
  };
1188
1372
  (0, import_react3.useEffect)(() => {
1189
1373
  let alive = true;
1190
- void request(`logrows?sessionId=${encodeURIComponent(sessionId)}&page=${logPage}&pageSize=${LOG_PAGE_SIZE}`).then((value) => {
1374
+ void request(`logrows?sessionId=${encodeURIComponent(sessionId)}&page=${logPage}&pageSize=${LOG_PAGE_SIZE}${lowOnly ? "&lowOnly=1" : ""}`).then((value) => {
1191
1375
  if (alive) setRows(value);
1192
1376
  }).catch(() => {
1193
1377
  });
1194
1378
  return () => {
1195
1379
  alive = false;
1196
1380
  };
1197
- }, [sessionId, logPage]);
1381
+ }, [sessionId, logPage, lowOnly]);
1198
1382
  const profile = analysis?.profile;
1199
1383
  const backtest = analysis?.trendBacktest;
1200
1384
  const annotations = analysis?.annotations;
1385
+ const checkpoint = profile?.checkpoint;
1386
+ const telemetry = profile?.telemetry;
1387
+ const measuredTotal = profile ? profile.gate.attempts + profile.check.attempts + profile.narrow.attempts + profile.complete.attempts + (checkpoint?.attempts ?? 0) : 0;
1388
+ const latency = (value) => typeof value === "number" && Number.isFinite(value) ? t("telemetryMs", { value: Math.round(value * 10) / 10 }) : t("telemetryUnknown");
1389
+ const transport = telemetry && typeof telemetry.transportMeasured === "number" && telemetry.transportMeasured > 0 ? `${telemetry.backendAttempts} \xB7 ${t("telemetryMeasuredCount", { count: telemetry.transportMeasured })}` : t("telemetryUnknown");
1390
+ const lowCount = profile?.check.low ?? 0;
1201
1391
  const meanScore = profile && profile.check.scoreCount > 0 ? Math.round(profile.check.scoreSum / profile.check.scoreCount * 10) / 10 : null;
1202
1392
  const completeTotal = profile ? profile.complete.satisfied + profile.complete.unsatisfied + profile.complete.insufficient : 0;
1203
1393
  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: [
@@ -1261,6 +1451,30 @@ function AnalysisView({ sessionId, t }) {
1261
1451
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("completeUnsatisfied"), value: profile.complete.unsatisfied, pct: share(profile.complete.unsatisfied, completeTotal), tone: "warn" }),
1262
1452
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("completeInsufficient"), value: profile.complete.insufficient, pct: share(profile.complete.insufficient, completeTotal), tone: "muted" })
1263
1453
  ] })
1454
+ ] }),
1455
+ checkpoint && checkpoint.attempts + checkpoint.skipped > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("article", { className: "decision-card", children: [
1456
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardHead, { title: t("cardCheckpoint"), total: checkpoint.attempts, unit: t("cardTimes") }),
1457
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-card-stats", children: [
1458
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("checkpointOk"), value: Math.max(0, checkpoint.attempts - checkpoint.concern - checkpoint.uncertain - checkpoint.error) }),
1459
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("checkpointConcern"), value: checkpoint.concern, tone: "warn" }),
1460
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("checkpointUncertain"), value: checkpoint.uncertain, tone: "muted" }),
1461
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("logEvalFailed"), value: checkpoint.error, tone: "muted" }),
1462
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("checkpointSkipped"), value: checkpoint.skipped, tone: "muted" })
1463
+ ] }),
1464
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted decision-analysis-note", children: t("checkpointProfileNote") })
1465
+ ] }),
1466
+ telemetry && telemetry.measured + telemetry.usageMeasured + (telemetry.transportMeasured ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("article", { className: "decision-card", children: [
1467
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(CardHead, { title: t("telemetryTitle"), total: telemetry.measured, unit: t("cardTimes") }),
1468
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-card-stats", children: [
1469
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("telemetryCoverage"), value: measuredTotal > 0 ? `${telemetry.measured}/${measuredTotal}` : t("telemetryUnknown") }),
1470
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("telemetryUsageCoverage"), value: measuredTotal > 0 ? `${telemetry.usageMeasured}/${measuredTotal}` : t("telemetryUnknown") }),
1471
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("telemetryMeanLatency"), value: latency(telemetry.measured > 0 ? telemetry.latencyMsSum / telemetry.measured : null) }),
1472
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("telemetryP95Latency"), value: latency(telemetry.measured > 0 ? telemetry.latencyMsP95 : null) }),
1473
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("telemetryInputTokens"), value: telemetry.usageMeasured > 0 ? telemetry.inputTokens : t("telemetryUnknown") }),
1474
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("telemetryOutputTokens"), value: telemetry.usageMeasured > 0 ? telemetry.outputTokens : t("telemetryUnknown") }),
1475
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Stat, { label: t("telemetryBackendAttempts"), value: transport })
1476
+ ] }),
1477
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted decision-analysis-note", children: t("telemetryNote") })
1264
1478
  ] })
1265
1479
  ] })
1266
1480
  ] }),
@@ -1278,8 +1492,28 @@ function AnalysisView({ sessionId, t }) {
1278
1492
  annotations.rated === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("analysisAccuracyEmpty") }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted decision-rate-summary", children: t("rateSummary", { rated: annotations.rated, good: annotations.good, bad: annotations.bad, unsure: annotations.unsure }) })
1279
1493
  ] }),
1280
1494
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { className: "decision-analysis-section", children: [
1281
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { children: t("log") }),
1282
- !rows || rows.total === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-empty", role: "status", children: t("analysisLogTruncated") }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1495
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-analysis-log-head", children: [
1496
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { children: t("log") }),
1497
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1498
+ "button",
1499
+ {
1500
+ type: "button",
1501
+ className: "decision-log-filter",
1502
+ "aria-pressed": lowOnly,
1503
+ disabled: !analysis || lowCount === 0,
1504
+ title: t("logLowOnlyHint"),
1505
+ onClick: () => {
1506
+ setLowOnly((value) => !value);
1507
+ setLogPage(1);
1508
+ },
1509
+ children: [
1510
+ t("logLowOnly"),
1511
+ lowCount > 0 ? ` \xB7 ${lowCount}` : ""
1512
+ ]
1513
+ }
1514
+ )
1515
+ ] }),
1516
+ !rows || rows.total === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-empty", role: "status", children: lowOnly ? t("logLowEmpty") : t("analysisLogTruncated") }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1283
1517
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DecisionLogList, { entries: rows.entries, ratings, onRate: (id, rating) => void annotate(id, rating), t, newestFirst: false }),
1284
1518
  rows.pages > 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("nav", { className: "decision-pager", "aria-label": t("log"), children: [
1285
1519
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", disabled: rows.page <= 1, onClick: () => setLogPage((page) => Math.max(1, page - 1)), children: t("pagerPrev") }),
@@ -1308,6 +1542,13 @@ var DEFAULT_KEEP_PREFIXES = ["mcp__openviking"];
1308
1542
  var DEFAULT_MAX_CANDIDATES = 20;
1309
1543
  var DEFAULT_THRESHOLD = 0.7;
1310
1544
  var DEFAULT_BATCH_MODE = "auto";
1545
+ var CHECKPOINT_DEFAULTS = { enabled: false, heartbeatSteps: 6, failureThreshold: 3, timeoutMs: 1500 };
1546
+ var checkpointFieldsOf = (settings) => ({
1547
+ enabled: settings?.enabled === true,
1548
+ heartbeatSteps: String(settings?.heartbeatSteps ?? CHECKPOINT_DEFAULTS.heartbeatSteps),
1549
+ failureThreshold: String(settings?.failureThreshold ?? CHECKPOINT_DEFAULTS.failureThreshold),
1550
+ timeoutMs: String(settings?.timeoutMs ?? CHECKPOINT_DEFAULTS.timeoutMs)
1551
+ });
1311
1552
  var DEFAULT_RUBRIC = [
1312
1553
  "\u660E\u663E\u7B54\u975E\u6240\u95EE\uFF0C\u6216\u5B58\u5728\u4E25\u91CD\u9057\u6F0F / \u81EA\u76F8\u77DB\u76FE",
1313
1554
  "\u57FA\u672C\u7B54\u5230\u95EE\u9898\uFF0C\u4F46\u6709\u53EF\u5BDF\u89C9\u7684\u9057\u6F0F\u6216\u4E0D\u4E25\u8C28",
@@ -1411,6 +1652,7 @@ function ConfigForm({ t, fetchFn = fetch }) {
1411
1652
  const [narrowOn, setNarrowOn] = (0, import_react4.useState)(true);
1412
1653
  const [completeOn, setCompleteOn] = (0, import_react4.useState)(true);
1413
1654
  const [completeMode, setCompleteMode] = (0, import_react4.useState)("observe");
1655
+ const [checkpoint, setCheckpoint] = (0, import_react4.useState)(() => checkpointFieldsOf());
1414
1656
  const [key, setKey] = (0, import_react4.useState)("");
1415
1657
  const [busy, setBusy] = (0, import_react4.useState)(false);
1416
1658
  const [advancedDirty, setAdvancedDirty] = (0, import_react4.useState)(false);
@@ -1431,6 +1673,7 @@ function ConfigForm({ t, fetchFn = fetch }) {
1431
1673
  setCheckMode(value.checkSettings?.mode ?? "observe");
1432
1674
  setNarrowMode(value.narrowSettings?.mode ?? "enforce");
1433
1675
  setCompleteMode(value.completeSettings?.mode ?? "observe");
1676
+ setCheckpoint(checkpointFieldsOf(value.checkpointSettings));
1434
1677
  setKeepPrefixes(prefixesText(value.narrowSettings));
1435
1678
  setMaxCandidates(String(maxCandidatesOf(value.narrowSettings)));
1436
1679
  setThreshold(String(thresholdOf(value.narrowSettings)));
@@ -1536,6 +1779,13 @@ function ConfigForm({ t, fetchFn = fetch }) {
1536
1779
  mode: completeMode,
1537
1780
  minConditions: clampNumber(thresholds.minConditions, THRESHOLD_DEFAULTS.minConditions, 2, 20, true),
1538
1781
  minConfidence: clampNumber(thresholds.completeMin, THRESHOLD_DEFAULTS.completeMin, 0, 1)
1782
+ },
1783
+ checkpointSettings: {
1784
+ ...config.checkpointSettings,
1785
+ enabled: checkpoint.enabled,
1786
+ heartbeatSteps: clampNumber(checkpoint.heartbeatSteps, CHECKPOINT_DEFAULTS.heartbeatSteps, 2, 100, true),
1787
+ failureThreshold: clampNumber(checkpoint.failureThreshold, CHECKPOINT_DEFAULTS.failureThreshold, 2, 10, true),
1788
+ timeoutMs: clampNumber(checkpoint.timeoutMs, CHECKPOINT_DEFAULTS.timeoutMs, 100, 1e4, true)
1539
1789
  }
1540
1790
  })
1541
1791
  });
@@ -1544,7 +1794,8 @@ function ConfigForm({ t, fetchFn = fetch }) {
1544
1794
  gateSettings: value.gateSettings ?? previous.gateSettings,
1545
1795
  checkSettings: value.checkSettings ?? previous.checkSettings,
1546
1796
  narrowSettings: value.narrowSettings ?? previous.narrowSettings,
1547
- completeSettings: value.completeSettings ?? previous.completeSettings
1797
+ completeSettings: value.completeSettings ?? previous.completeSettings,
1798
+ checkpointSettings: value.checkpointSettings ?? previous.checkpointSettings
1548
1799
  }));
1549
1800
  applyAdvanced(value);
1550
1801
  setAdvancedDirty(false);
@@ -1955,6 +2206,84 @@ ${url}`)) return;
1955
2206
  ] })
1956
2207
  ] })
1957
2208
  ] }),
2209
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "decision-field-block decision-field-block-wide", children: [
2210
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "decision-subhead", children: t("checkpointSettings") }),
2211
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2212
+ Toggle,
2213
+ {
2214
+ label: t("checkpointEnabled"),
2215
+ checked: checkpoint.enabled,
2216
+ disabled: !canEdit,
2217
+ onChange: (enabled) => {
2218
+ setCheckpoint((previous) => ({ ...previous, enabled }));
2219
+ setAdvancedDirty(true);
2220
+ }
2221
+ }
2222
+ ),
2223
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "decision-muted", children: t("checkpointHint") }),
2224
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "decision-threshold-grid", children: [
2225
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "decision-field decision-field-inline", children: [
2226
+ t("checkpointHeartbeatLabel"),
2227
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2228
+ "input",
2229
+ {
2230
+ className: "decision-narrow-cap",
2231
+ type: "number",
2232
+ min: 2,
2233
+ max: 100,
2234
+ step: 1,
2235
+ value: checkpoint.heartbeatSteps,
2236
+ disabled: !canEdit || !checkpoint.enabled,
2237
+ onChange: (event) => {
2238
+ setCheckpoint((previous) => ({ ...previous, heartbeatSteps: event.target.value }));
2239
+ setAdvancedDirty(true);
2240
+ }
2241
+ }
2242
+ ),
2243
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("small", { className: "decision-muted", children: t("checkpointHeartbeatHint") })
2244
+ ] }),
2245
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "decision-field decision-field-inline", children: [
2246
+ t("checkpointFailureLabel"),
2247
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2248
+ "input",
2249
+ {
2250
+ className: "decision-narrow-cap",
2251
+ type: "number",
2252
+ min: 2,
2253
+ max: 10,
2254
+ step: 1,
2255
+ value: checkpoint.failureThreshold,
2256
+ disabled: !canEdit || !checkpoint.enabled,
2257
+ onChange: (event) => {
2258
+ setCheckpoint((previous) => ({ ...previous, failureThreshold: event.target.value }));
2259
+ setAdvancedDirty(true);
2260
+ }
2261
+ }
2262
+ ),
2263
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("small", { className: "decision-muted", children: t("checkpointFailureHint") })
2264
+ ] }),
2265
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "decision-field decision-field-inline", children: [
2266
+ t("checkpointTimeoutLabel"),
2267
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2268
+ "input",
2269
+ {
2270
+ className: "decision-narrow-cap",
2271
+ type: "number",
2272
+ min: 100,
2273
+ max: 1e4,
2274
+ step: 1,
2275
+ value: checkpoint.timeoutMs,
2276
+ disabled: !canEdit || !checkpoint.enabled,
2277
+ onChange: (event) => {
2278
+ setCheckpoint((previous) => ({ ...previous, timeoutMs: event.target.value }));
2279
+ setAdvancedDirty(true);
2280
+ }
2281
+ }
2282
+ ),
2283
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("small", { className: "decision-muted", children: t("checkpointTimeoutHint") })
2284
+ ] })
2285
+ ] })
2286
+ ] }),
1958
2287
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "decision-field-block decision-field-block-wide", children: [
1959
2288
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "decision-subhead", children: t("advancedRubric") }),
1960
2289
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "decision-field", children: [
@@ -2155,6 +2484,14 @@ var css = `
2155
2484
  real quality dip. */
2156
2485
  .decision-log-muted-verdict{font-weight:600;color:var(--dsw-alias-label-tertiary,#9aa5b1)}
2157
2486
  .decision-log-muted .decision-log-time{opacity:.75}
2487
+ /* Every low self-check row gets an amber accent and a badge, even when the score
2488
+ is low-confidence noise. The noise row keeps its muted verdict and dashed
2489
+ accent, so it is findable without reading as a real quality dip. */
2490
+ .decision-log-item.decision-log-low{border-left-color:#c98a1e}
2491
+ .decision-log-item.decision-log-low.decision-log-muted{border-left-style:dashed}
2492
+ .decision-log-badge{display:inline-block;margin-right:.35rem;padding:0 .38rem;border:1px solid transparent;border-radius:999px;font-size:.68rem;font-weight:600;line-height:1.6;vertical-align:.05em}
2493
+ .decision-log-badge-low{color:#8a5a12;background:rgba(201,138,30,.16);border-color:rgba(201,138,30,.4)}
2494
+ .decision-log-badge-noise{color:var(--dsw-alias-label-secondary,#59636e);background:var(--dsw-alias-bg-layer-1,#f2f4f6);border-color:var(--dsw-alias-border-l2,#dce1e6)}
2158
2495
 
2159
2496
  .decision-status{display:flex;align-items:center;gap:.5rem;margin-top:.7rem;font-size:.82rem}
2160
2497
  .decision-status-dot{width:8px;height:8px;border-radius:50%;flex:0 0 auto;background:#9aa5b1}
@@ -2265,6 +2602,20 @@ var css = `
2265
2602
  .decision-analysis-switch:focus-visible{outline:2px solid var(--decision-analysis-accent);outline-offset:2px}
2266
2603
  .decision-analysis-section{border-top:1px solid var(--dsw-alias-border-l2,#eceff1);margin-top:1.1rem;padding-top:1rem}
2267
2604
  .decision-analysis-section>h3{font-size:.92rem;margin:0 0 .6rem}
2605
+ /* Section header with the low-score-only filter on the right. Keeping the
2606
+ filter next to the log heading makes the affordance discoverable exactly
2607
+ where a long list is being scanned. */
2608
+ .decision-analysis-log-head{display:flex;align-items:center;justify-content:space-between;gap:.75rem;margin-bottom:.6rem}
2609
+ .decision-analysis-log-head>h3{font-size:.92rem;margin:0}
2610
+ .decision-log-filter{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:.24rem .7rem;font:inherit;font-size:.76rem;line-height:1.3;white-space:nowrap;cursor:pointer;transition:color .15s,border-color .15s,background .15s}
2611
+ .decision-log-filter:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#eef1f3);color:var(--dsw-alias-label-primary,#202124)}
2612
+ .decision-log-filter[aria-pressed="true"]{border-color:#c98a1e;color:#8a5a12;background:rgba(201,138,30,.12)}
2613
+ .decision-log-filter:disabled{opacity:.5;cursor:default}
2614
+ .decision-log-filter:focus-visible{outline:2px solid var(--decision-analysis-accent);outline-offset:2px}
2615
+ @media (prefers-color-scheme:dark){
2616
+ .decision-log-badge-low{color:#e0a83e;background:rgba(224,168,62,.14);border-color:rgba(224,168,62,.4)}
2617
+ .decision-log-filter[aria-pressed="true"]{border-color:#e0a83e;color:#e0a83e;background:rgba(224,168,62,.14)}
2618
+ }
2268
2619
  .decision-analysis-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(14rem,1fr));gap:.7rem}
2269
2620
  .decision-analysis-trend{max-width:26rem}
2270
2621
  .decision-analysis-note{margin:.5rem 0 0}