dsh-decision-layer 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +7 -4
- package/README.md +8 -5
- package/lib/client.js +793 -356
- package/package.json +4 -2
- package/service/config-store.mjs +47 -4
- package/service/danger-gate.mjs +33 -16
- package/service/dsh.mjs +72 -1
- package/service/log-analyze.mjs +155 -0
- package/service/log-read.mjs +27 -0
- package/service/log-store.mjs +12 -0
- package/service/loop-guard.mjs +3 -4
- package/service/manager-api.mjs +26 -1
- package/service/quality-trend.mjs +29 -0
- package/service/self-check.mjs +12 -5
- package/service/session-state.mjs +52 -15
- package/service/task-completion.mjs +155 -0
package/lib/client.js
CHANGED
|
@@ -24,6 +24,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
24
24
|
// src/client/index.tsx
|
|
25
25
|
var index_exports = {};
|
|
26
26
|
__export(index_exports, {
|
|
27
|
+
AnalysisView: () => AnalysisView,
|
|
27
28
|
ConfigForm: () => ConfigForm,
|
|
28
29
|
apply: () => apply,
|
|
29
30
|
inject: () => inject
|
|
@@ -51,6 +52,7 @@ var dictionaries = {
|
|
|
51
52
|
cardGate: "\u5371\u9669\u95E8\u63A7",
|
|
52
53
|
cardCheck: "\u4EA7\u51FA\u81EA\u68C0",
|
|
53
54
|
cardNarrow: "\u5DE5\u5177\u6536\u7A84",
|
|
55
|
+
cardComplete: "\u5B8C\u6210\u6838\u5BF9",
|
|
54
56
|
cardTimes: " \u6B21",
|
|
55
57
|
allow: "\u653E\u884C",
|
|
56
58
|
ask: "\u8BE2\u95EE",
|
|
@@ -79,6 +81,7 @@ var dictionaries = {
|
|
|
79
81
|
logGate: "\u95E8\u63A7",
|
|
80
82
|
logCheck: "\u81EA\u68C0",
|
|
81
83
|
logNarrow: "\u6536\u7A84",
|
|
84
|
+
logComplete: "\u5B8C\u6210\u6838\u5BF9",
|
|
82
85
|
logSuggested: "\u6A21\u578B\u5EFA\u8BAE",
|
|
83
86
|
logEvalFailed: "\u8BC4\u4F30\u5931\u8D25",
|
|
84
87
|
logScore: "\u5F97\u5206",
|
|
@@ -89,13 +92,14 @@ var dictionaries = {
|
|
|
89
92
|
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",
|
|
90
93
|
actionDeny: "\u5DF2\u62D2\u7EDD",
|
|
91
94
|
actionAsk: "\u8F6C\u4EA4\u5BA1\u6279",
|
|
95
|
+
actionPass: "\u672A\u5E72\u9884",
|
|
96
|
+
gatePassThrough: "\u672A\u5E72\u9884\uFF08\u6309\u539F\u8DEF\u5F84\u6267\u884C\uFF09",
|
|
92
97
|
checkResultLow: "\u5F97\u5206\u504F\u4F4E",
|
|
93
98
|
checkResultOk: "\u901A\u8FC7",
|
|
94
99
|
reasonUnavailable: "\u540E\u7AEF\u4E0D\u53EF\u8FBE",
|
|
95
100
|
reasonHttpError: "\u540E\u7AEF\u8FD4\u56DE\u9519\u8BEF",
|
|
96
101
|
reasonInvalidResponse: "\u54CD\u5E94\u683C\u5F0F\u4E0D\u7B26",
|
|
97
102
|
reasonLowConfidence: "\u7F6E\u4FE1\u5EA6\u4E0D\u8DB3",
|
|
98
|
-
reasonCapacity: "\u4F1A\u8BDD\u72B6\u6001\u5DF2\u6EE1",
|
|
99
103
|
reasonConfig: "\u914D\u7F6E\u65E0\u6548",
|
|
100
104
|
reasonTooMany: "\u5019\u9009\u8FC7\u591A",
|
|
101
105
|
reasonTooManyCount: "\u5019\u9009 {count} \u4E2A\u504F\u591A",
|
|
@@ -123,6 +127,25 @@ var dictionaries = {
|
|
|
123
127
|
featureCheckHint: "\u5173\u95ED\u540E\u4E0D\u518D\u5728\u56DE\u5408\u7ED3\u675F\u65F6\u7ED9\u6700\u7EC8\u56DE\u590D\u6253\u5206\u3002",
|
|
124
128
|
featureNarrow: "\u5DE5\u5177\u6536\u7A84",
|
|
125
129
|
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",
|
|
130
|
+
featureComplete: "\u4EFB\u52A1\u5B8C\u6210\u6838\u5BF9",
|
|
131
|
+
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",
|
|
132
|
+
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",
|
|
133
|
+
trendSevereHint: "\u672C\u4F1A\u8BDD\u4EA7\u51FA\u8D28\u91CF\u6301\u7EED\u504F\u4F4E\uFF08\u9AD8\u7F6E\u4FE1\uFF09\u3002\u5EFA\u8BAE\u8C03\u6574 prompt \u6216\u66F4\u6362\u6A21\u578B\uFF0C\u6216\u91CD\u5F00\u4F1A\u8BDD\u3002",
|
|
134
|
+
rateGroup: "\u6807\u6CE8\u8FD9\u6B21\u5224\u65AD",
|
|
135
|
+
rateGood: "\u5224\u5BF9\u4E86",
|
|
136
|
+
rateBad: "\u5224\u9519\u4E86",
|
|
137
|
+
rateUnsure: "\u8BF4\u4E0D\u51C6",
|
|
138
|
+
rateSummary: "\u5DF2\u6807\u6CE8 {rated} \u6761\uFF08\u5224\u5BF9 {good} / \u5224\u9519 {bad} / \u8BF4\u4E0D\u51C6 {unsure}\uFF09",
|
|
139
|
+
annotateFailed: "\u6807\u6CE8\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002",
|
|
140
|
+
completeSteer: "\u6709\u672A\u6EE1\u8DB3\u6761\u4EF6\u65F6\u81EA\u52A8\u8865\u4E00\u8F6E",
|
|
141
|
+
completeHint: "\u56DE\u5408\u7ED3\u675F\u65F6\uFF0C\u5BF9\u7528\u6237\u660E\u786E\u5217\u51FA\u7684\u591A\u4E2A\u4EA4\u4ED8\u6761\u4EF6\uFF08\u7F16\u53F7 / \u9879\u76EE\u7B26\u53F7 / \u591A\u547D\u4EE4\uFF09\u9010\u9879\u5224\u5B9A\u300C\u5DF2\u6EE1\u8DB3 / \u672A\u6EE1\u8DB3 / \u8BC1\u636E\u4E0D\u8DB3\u300D\uFF0C\u4EE5\u5DE5\u5177\u8BC1\u636E\u4E3A\u51C6\u3001\u56DE\u590D\u81EA\u79F0\u5B8C\u6210\u4E0D\u7B97\u8BC1\u636E\u3002\u5173\u95ED\uFF1A\u53EA\u628A\u7ED3\u679C\u8BB0\u8FDB\u65E5\u5FD7\u548C\u7EDF\u8BA1\uFF0C\u4E0D\u6253\u65AD\u5BF9\u8BDD\u3002\u5F00\u542F\uFF1A\u786E\u6709\u6761\u4EF6\u88AB\u5224\u300C\u672A\u6EE1\u8DB3\u300D\u4E14\u6A21\u578B\u6709\u628A\u63E1\u65F6\uFF0C\u81EA\u52A8\u4EE5\u4F60\u7684\u540D\u4E49\u8FFD\u52A0\u4E00\u6761\u63D0\u793A\u8BA9 AI \u590D\u6838\u8865\u5B8C\uFF1B\u300C\u8BC1\u636E\u4E0D\u8DB3\u300D\u4E0D\u89E6\u53D1\u8865\u5B8C\uFF0C\u540C\u4E00\u56DE\u5408\u6700\u591A\u8865\u4E00\u6B21\u3002",
|
|
142
|
+
completeResultOk: "\u6761\u4EF6\u9F50\u5907",
|
|
143
|
+
completeResultUnsatisfied: "\u6709\u672A\u6EE1\u8DB3",
|
|
144
|
+
completeConditions: "\u6761\u4EF6",
|
|
145
|
+
completeSatisfied: "\u6EE1\u8DB3",
|
|
146
|
+
completeUnsatisfied: "\u672A\u6EE1\u8DB3",
|
|
147
|
+
completeInsufficient: "\u8BC1\u636E\u4E0D\u8DB3",
|
|
148
|
+
completeSteered: "\u5DF2\u8865\u4E00\u8F6E",
|
|
126
149
|
backend: "\u88C1\u51B3\u540E\u7AEF",
|
|
127
150
|
url: "\u670D\u52A1\u5730\u5740",
|
|
128
151
|
model: "\u6A21\u578B",
|
|
@@ -142,9 +165,22 @@ var dictionaries = {
|
|
|
142
165
|
unavailable: "\u8FDE\u63A5\u4E0D\u53EF\u7528",
|
|
143
166
|
error: "\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002",
|
|
144
167
|
loading: "\u6B63\u5728\u8BFB\u53D6\u4F1A\u8BDD\u72B6\u6001",
|
|
145
|
-
capacity: "\u4F1A\u8BDD\u72B6\u6001\u5BB9\u91CF\u5DF2\u6EE1\uFF1A\u6B64\u4F1A\u8BDD\u81EA\u52A8\u4ECB\u5165\u5DF2\u6682\u505C\uFF1B\u64CD\u4F5C\u4ECD\u53D7\u5BBF\u4E3B\u6743\u9650\u7B56\u7565\u7EA6\u675F\u3002",
|
|
146
168
|
destination: "\u88C1\u51B3\u65F6\u4F1A\u5411\u4EE5\u4E0B\u540E\u7AEF\u53D1\u9001\u5FC5\u8981\u7684\u8BC4\u4F30\u4E0A\u4E0B\u6587\uFF1A",
|
|
147
|
-
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"
|
|
169
|
+
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",
|
|
170
|
+
analysisTab: "\u51B3\u7B56\u5206\u6790",
|
|
171
|
+
analysisTitle: "\u51B3\u7B56\u5206\u6790",
|
|
172
|
+
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",
|
|
173
|
+
analysisProfile: "\u8FC7\u7A0B\u753B\u50CF",
|
|
174
|
+
analysisMeanScore: "\u5E73\u5747\u5206",
|
|
175
|
+
analysisTrend: "\u8D28\u91CF\u8D8B\u52BF\u56DE\u6D4B",
|
|
176
|
+
analysisTrendWarn: "\u544A\u8B66\u89E6\u53D1",
|
|
177
|
+
analysisTrendSevere: "\u4E25\u91CD\u89E6\u53D1",
|
|
178
|
+
analysisTrendMaxRun: "\u6700\u957F\u8FDE\u7EED\u4F4E\u5206",
|
|
179
|
+
analysisTrendNote: "\u7528\u5F53\u524D\u9608\u503C\u628A\u672C\u4F1A\u8BDD\u7684\u81EA\u68C0\u5206\u6570\u6309\u65F6\u95F4\u91CD\u653E\uFF0C\u7EDF\u8BA1\u544A\u8B66\u672C\u4F1A\u300C\u4F1A\u300D\u89E6\u53D1\u51E0\u6B21\uFF0C\u7528\u4E8E\u6821\u51C6\u9608\u503C\uFF0C\u4E0D\u662F\u5B9E\u65F6\u4FE1\u53F7\u3002",
|
|
180
|
+
analysisAccuracy: "\u5224\u65AD\u51C6\u786E\u5EA6",
|
|
181
|
+
analysisAccuracyEmpty: "\u5C1A\u672A\u6807\u6CE8\u4EFB\u4F55\u51B3\u7B56\u3002\u5728\u4E0B\u65B9\u51B3\u7B56\u5217\u8868\u9010\u6761\u6807\u6CE8\u300C\u5224\u5BF9 / \u5224\u9519 / \u8BF4\u4E0D\u51C6\u300D\u540E\uFF0C\u8FD9\u91CC\u7ED9\u51FA\u6C47\u603B\u3002",
|
|
182
|
+
analysisLogNote: "\u6807\u6CE8\u5728\u6B64\u4E0E\u4ECB\u5165\u9762\u677F\u540C\u6E90\uFF1A\u4EFB\u4E00\u5904\u6807\u6CE8\u90FD\u5199\u5165\u540C\u4E00\u4EFD\u65E5\u5FD7\u3001\u6309\u51B3\u7B56 id \u5173\u8054\u3001\u53D6\u6700\u65B0\u3002",
|
|
183
|
+
analysisLogTruncated: "\u672C\u4F1A\u8BDD\u6682\u65E0\u53EF\u5C55\u793A\u7684\u51B3\u7B56\u8BB0\u5F55\uFF08\u5185\u5B58\u65E5\u5FD7\u53EF\u80FD\u5DF2\u6EDA\u52A8\u6E05\u7406\uFF09\u3002"
|
|
148
184
|
},
|
|
149
185
|
en: {
|
|
150
186
|
summary: "A danger-call gate, output self-check, and loop guard driven by a pluggable adjudication model.",
|
|
@@ -165,6 +201,7 @@ var dictionaries = {
|
|
|
165
201
|
cardGate: "Danger gate",
|
|
166
202
|
cardCheck: "Output self-check",
|
|
167
203
|
cardNarrow: "Tool narrowing",
|
|
204
|
+
cardComplete: "Task completion",
|
|
168
205
|
cardTimes: "",
|
|
169
206
|
allow: "Allow",
|
|
170
207
|
ask: "Ask",
|
|
@@ -193,6 +230,7 @@ var dictionaries = {
|
|
|
193
230
|
logGate: "Gate",
|
|
194
231
|
logCheck: "Self-check",
|
|
195
232
|
logNarrow: "Narrow",
|
|
233
|
+
logComplete: "Completion",
|
|
196
234
|
logSuggested: "model suggested",
|
|
197
235
|
logEvalFailed: "evaluation failed",
|
|
198
236
|
logScore: "score",
|
|
@@ -203,13 +241,14 @@ var dictionaries = {
|
|
|
203
241
|
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.",
|
|
204
242
|
actionDeny: "denied",
|
|
205
243
|
actionAsk: "sent to approval",
|
|
244
|
+
actionPass: "no intervention",
|
|
245
|
+
gatePassThrough: "no intervention (proceeded on its original path)",
|
|
206
246
|
checkResultLow: "low score",
|
|
207
247
|
checkResultOk: "passed",
|
|
208
248
|
reasonUnavailable: "backend unreachable",
|
|
209
249
|
reasonHttpError: "backend returned an error",
|
|
210
250
|
reasonInvalidResponse: "unexpected response shape",
|
|
211
251
|
reasonLowConfidence: "low confidence",
|
|
212
|
-
reasonCapacity: "session state full",
|
|
213
252
|
reasonConfig: "invalid configuration",
|
|
214
253
|
reasonTooMany: "too many candidates",
|
|
215
254
|
reasonTooManyCount: "{count} candidates is too many",
|
|
@@ -237,6 +276,25 @@ var dictionaries = {
|
|
|
237
276
|
featureCheckHint: "When off, the final reply is no longer scored at the end of a turn.",
|
|
238
277
|
featureNarrow: "Tool narrowing",
|
|
239
278
|
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.",
|
|
279
|
+
featureComplete: "Task completion check",
|
|
280
|
+
featureCompleteHint: "When off, the explicit deliverable conditions the user listed are no longer checked at turn end.",
|
|
281
|
+
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.",
|
|
282
|
+
trendSevereHint: "Output quality has stayed low this session (high confidence). Consider adjusting the prompt, switching models, or starting a new session.",
|
|
283
|
+
rateGroup: "Rate this decision",
|
|
284
|
+
rateGood: "Correct",
|
|
285
|
+
rateBad: "Wrong",
|
|
286
|
+
rateUnsure: "Unsure",
|
|
287
|
+
rateSummary: "Rated {rated} ({good} correct / {bad} wrong / {unsure} unsure)",
|
|
288
|
+
annotateFailed: "Could not save the rating. Please retry.",
|
|
289
|
+
completeSteer: "Auto re-prompt on an unmet condition",
|
|
290
|
+
completeHint: "At turn end, each explicit deliverable condition the user listed (numbered / bulleted / multi-command) is judged satisfied / unsatisfied / insufficient evidence, with tool evidence as the source of truth \u2014 a reply claiming completion is not evidence. Off: results are only logged, the conversation is not interrupted. On: when a condition is confidently judged unsatisfied, one prompt is appended on your behalf asking the AI to review and finish it; insufficient evidence never triggers a re-prompt, and at most one per turn.",
|
|
291
|
+
completeResultOk: "conditions met",
|
|
292
|
+
completeResultUnsatisfied: "unmet found",
|
|
293
|
+
completeConditions: "conditions",
|
|
294
|
+
completeSatisfied: "met",
|
|
295
|
+
completeUnsatisfied: "unmet",
|
|
296
|
+
completeInsufficient: "insufficient",
|
|
297
|
+
completeSteered: "re-prompted",
|
|
240
298
|
backend: "Decision backend",
|
|
241
299
|
url: "Server URL",
|
|
242
300
|
model: "Model",
|
|
@@ -256,53 +314,319 @@ var dictionaries = {
|
|
|
256
314
|
unavailable: "Connection unavailable",
|
|
257
315
|
error: "Request failed. Please retry.",
|
|
258
316
|
loading: "Loading session settings",
|
|
259
|
-
capacity: "Session capacity reached: automatic intervention is paused here; host permission rules still apply.",
|
|
260
317
|
destination: "Decisions send the necessary evaluation context to this backend:",
|
|
261
|
-
httpWarning: "This address uses unencrypted HTTP. Your API key and evaluation context will be sent in plaintext to this address."
|
|
318
|
+
httpWarning: "This address uses unencrypted HTTP. Your API key and evaluation context will be sent in plaintext to this address.",
|
|
319
|
+
analysisTab: "Decision analysis",
|
|
320
|
+
analysisTitle: "Decision analysis",
|
|
321
|
+
analysisIntro: "A read-only aggregate and manual annotation over this session\u2019s persisted decision log. It changes no agent behavior.",
|
|
322
|
+
analysisProfile: "Process profile",
|
|
323
|
+
analysisMeanScore: "Mean score",
|
|
324
|
+
analysisTrend: "Quality-trend backtest",
|
|
325
|
+
analysisTrendWarn: "Warn fires",
|
|
326
|
+
analysisTrendSevere: "Severe fires",
|
|
327
|
+
analysisTrendMaxRun: "Longest low run",
|
|
328
|
+
analysisTrendNote: "Replays this session\u2019s self-check scores in time order under the current thresholds to count how often the alarm would fire \u2014 a calibration aid, not a live signal.",
|
|
329
|
+
analysisAccuracy: "Decision accuracy",
|
|
330
|
+
analysisAccuracyEmpty: "No decisions rated yet. Rate rows below as correct / wrong / unsure and a summary appears here.",
|
|
331
|
+
analysisLogNote: "Ratings here share one source with the intervention panel: a rating on either surface writes to the same log, keyed by decision id, latest wins.",
|
|
332
|
+
analysisLogTruncated: "No decision rows to show for this session (the in-memory log may have rolled over)."
|
|
262
333
|
}
|
|
263
334
|
};
|
|
264
335
|
|
|
265
336
|
// src/client/panel.tsx
|
|
266
|
-
var
|
|
267
|
-
|
|
268
|
-
|
|
337
|
+
var import_react2 = require("react");
|
|
338
|
+
|
|
339
|
+
// src/client/api.ts
|
|
340
|
+
var API_BASE = "/plugins/dsh-decision-layer/api";
|
|
269
341
|
async function request(path, init) {
|
|
270
|
-
const response = await fetch(`${
|
|
342
|
+
const response = await fetch(`${API_BASE}/${path}`, init);
|
|
271
343
|
const body = await response.json();
|
|
272
344
|
if (!response.ok || !body.ok || body.value === void 0) throw new Error(body.error || "Request failed");
|
|
273
345
|
return body.value;
|
|
274
346
|
}
|
|
347
|
+
|
|
348
|
+
// src/client/decision-log.tsx
|
|
349
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
350
|
+
var time = (at) => new Date(at).toLocaleTimeString();
|
|
351
|
+
function reasonText(t, reason) {
|
|
352
|
+
return reason === "unavailable" || reason === "unreachable" ? t("reasonUnavailable") : reason === "http-error" ? t("reasonHttpError") : reason === "invalid-response" ? t("reasonInvalidResponse") : reason === "low-confidence" ? t("reasonLowConfidence") : reason === "too-many-candidates" ? t("reasonTooMany") : reason === "low-keep" ? t("reasonLowKeep") : reason === "config" ? t("reasonConfig") : reason;
|
|
353
|
+
}
|
|
354
|
+
function actionText(t, action) {
|
|
355
|
+
return action === "deny" ? t("actionDeny") : action === "ask" ? t("actionAsk") : action === "pass" ? t("actionPass") : action;
|
|
356
|
+
}
|
|
357
|
+
function failDetail(t, entry) {
|
|
358
|
+
const reason = reasonText(t, entry.reason);
|
|
359
|
+
if (entry.reason === "low-confidence" && typeof entry.confidence === "number") return `${reason}\uFF08${t("logConfidence")} ${entry.confidence}\uFF09`;
|
|
360
|
+
return reason;
|
|
361
|
+
}
|
|
362
|
+
function renderGate({ t }, entry) {
|
|
363
|
+
const tool = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: "decision-log-tool", children: entry.tool ?? "\u2014" });
|
|
364
|
+
if (entry.outcome === "error") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
365
|
+
t("logGate"),
|
|
366
|
+
" \xB7 ",
|
|
367
|
+
tool,
|
|
368
|
+
" ",
|
|
369
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
370
|
+
t("gatePassThrough"),
|
|
371
|
+
entry.reason ? ` \xB7 ${failDetail(t, entry)}` : ""
|
|
372
|
+
] })
|
|
373
|
+
] });
|
|
374
|
+
if (entry.outcome === "deny") {
|
|
375
|
+
const suggestion2 = entry.suggestion && entry.suggestion !== "deny" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
376
|
+
" \xB7 ",
|
|
377
|
+
t("logSuggested"),
|
|
378
|
+
" ",
|
|
379
|
+
actionText(t, entry.suggestion)
|
|
380
|
+
] }) : null;
|
|
381
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
382
|
+
t("logGate"),
|
|
383
|
+
" \xB7 ",
|
|
384
|
+
tool,
|
|
385
|
+
" ",
|
|
386
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-verdict", children: [
|
|
387
|
+
"\u2192 ",
|
|
388
|
+
t("actionDeny")
|
|
389
|
+
] }),
|
|
390
|
+
suggestion2
|
|
391
|
+
] });
|
|
392
|
+
}
|
|
393
|
+
const suggestion = entry.suggestion ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
394
|
+
" \xB7 ",
|
|
395
|
+
t("logSuggested"),
|
|
396
|
+
" ",
|
|
397
|
+
actionText(t, entry.suggestion)
|
|
398
|
+
] }) : null;
|
|
399
|
+
const lowConf = entry.reason === "low-confidence" && typeof entry.confidence === "number" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
400
|
+
" \xB7 ",
|
|
401
|
+
t("reasonLowConfidence"),
|
|
402
|
+
" ",
|
|
403
|
+
entry.confidence
|
|
404
|
+
] }) : null;
|
|
405
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
406
|
+
t("logGate"),
|
|
407
|
+
" \xB7 ",
|
|
408
|
+
tool,
|
|
409
|
+
" ",
|
|
410
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-verdict", children: t("gatePassThrough") }),
|
|
411
|
+
suggestion,
|
|
412
|
+
lowConf
|
|
413
|
+
] });
|
|
414
|
+
}
|
|
415
|
+
function renderCheck({ t }, entry) {
|
|
416
|
+
const label = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { title: t("logCheckHint"), children: t("logCheck") });
|
|
417
|
+
if (entry.outcome === "error") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
418
|
+
label,
|
|
419
|
+
" ",
|
|
420
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
421
|
+
t("logEvalFailed"),
|
|
422
|
+
entry.reason ? ` \xB7 ${failDetail(t, entry)}` : ""
|
|
423
|
+
] })
|
|
424
|
+
] });
|
|
425
|
+
const result = entry.outcome === "low" ? t("checkResultLow") : t("checkResultOk");
|
|
426
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
427
|
+
label,
|
|
428
|
+
" \xB7 ",
|
|
429
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-verdict", children: result }),
|
|
430
|
+
entry.score !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", title: t("logScoreHint"), children: [
|
|
431
|
+
" \xB7 ",
|
|
432
|
+
t("logScore"),
|
|
433
|
+
" ",
|
|
434
|
+
entry.score,
|
|
435
|
+
"/2"
|
|
436
|
+
] }) : null,
|
|
437
|
+
typeof entry.confidence === "number" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
438
|
+
" \xB7 ",
|
|
439
|
+
t("logConfidence"),
|
|
440
|
+
" ",
|
|
441
|
+
entry.confidence
|
|
442
|
+
] }) : null
|
|
443
|
+
] });
|
|
444
|
+
}
|
|
445
|
+
function renderNarrow({ t }, entry) {
|
|
446
|
+
const label = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { title: t("logNarrowHint"), children: t("logNarrow") });
|
|
447
|
+
if (entry.outcome === "error") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
448
|
+
label,
|
|
449
|
+
" ",
|
|
450
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
451
|
+
t("logEvalFailed"),
|
|
452
|
+
entry.reason ? ` \xB7 ${failDetail(t, entry)}` : ""
|
|
453
|
+
] })
|
|
454
|
+
] });
|
|
455
|
+
const dropped = entry.dropped ?? 0;
|
|
456
|
+
const kept = typeof entry.kept === "number" ? entry.kept : void 0;
|
|
457
|
+
const guardText = entry.reason === "too-many-candidates" ? typeof entry.candidates === "number" ? t("reasonTooManyCount", { count: entry.candidates }) : t("reasonTooMany") : entry.reason === "low-keep" ? t("reasonLowKeep") : void 0;
|
|
458
|
+
const guard = guardText ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
459
|
+
" \xB7 ",
|
|
460
|
+
guardText
|
|
461
|
+
] }) : null;
|
|
462
|
+
const modeTag = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
463
|
+
" \xB7 ",
|
|
464
|
+
entry.mode === "enforce" ? t("narrowEnforce") : t("narrowObserve")
|
|
465
|
+
] });
|
|
466
|
+
if (dropped === 0) {
|
|
467
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
468
|
+
label,
|
|
469
|
+
" \xB7 ",
|
|
470
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-verdict", children: t("narrowKept") }),
|
|
471
|
+
kept !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
472
|
+
" \xB7 ",
|
|
473
|
+
t("narrowKeptCount"),
|
|
474
|
+
" ",
|
|
475
|
+
kept
|
|
476
|
+
] }) : null,
|
|
477
|
+
guard,
|
|
478
|
+
modeTag
|
|
479
|
+
] });
|
|
480
|
+
}
|
|
481
|
+
const names = Array.isArray(entry.tools) && entry.tools.length > 0 ? entry.tools.join("\u3001") : void 0;
|
|
482
|
+
const overflow = names && Array.isArray(entry.tools) && entry.tools.length < dropped ? t("narrowMore", { count: dropped }) : "";
|
|
483
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
484
|
+
label,
|
|
485
|
+
" \xB7 ",
|
|
486
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-verdict", children: [
|
|
487
|
+
t("narrowDropped"),
|
|
488
|
+
" ",
|
|
489
|
+
dropped,
|
|
490
|
+
kept !== void 0 ? ` / ${t("narrowKeptCount")} ${kept}` : ""
|
|
491
|
+
] }),
|
|
492
|
+
names ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
493
|
+
" \xB7 ",
|
|
494
|
+
names,
|
|
495
|
+
overflow
|
|
496
|
+
] }) : null,
|
|
497
|
+
guard,
|
|
498
|
+
modeTag
|
|
499
|
+
] });
|
|
500
|
+
}
|
|
501
|
+
function renderComplete({ t }, entry) {
|
|
502
|
+
const label = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("logComplete") });
|
|
503
|
+
if (entry.outcome === "error") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
504
|
+
label,
|
|
505
|
+
" ",
|
|
506
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
507
|
+
t("logEvalFailed"),
|
|
508
|
+
entry.reason ? ` \xB7 ${failDetail(t, entry)}` : ""
|
|
509
|
+
] })
|
|
510
|
+
] });
|
|
511
|
+
const result = entry.outcome === "unsatisfied" ? t("completeResultUnsatisfied") : t("completeResultOk");
|
|
512
|
+
const parts = [];
|
|
513
|
+
if (typeof entry.satisfied === "number") parts.push(`${t("completeSatisfied")} ${entry.satisfied}`);
|
|
514
|
+
if (typeof entry.unsatisfied === "number" && entry.unsatisfied > 0) parts.push(`${t("completeUnsatisfied")} ${entry.unsatisfied}`);
|
|
515
|
+
if (typeof entry.insufficient === "number" && entry.insufficient > 0) parts.push(`${t("completeInsufficient")} ${entry.insufficient}`);
|
|
516
|
+
const tally = parts.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
517
|
+
" \xB7 ",
|
|
518
|
+
parts.join(" / ")
|
|
519
|
+
] }) : null;
|
|
520
|
+
const steer = entry.steered ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
521
|
+
" \xB7 ",
|
|
522
|
+
t("completeSteered")
|
|
523
|
+
] }) : null;
|
|
524
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
525
|
+
label,
|
|
526
|
+
" \xB7 ",
|
|
527
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-verdict", children: result }),
|
|
528
|
+
typeof entry.conditions === "number" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
529
|
+
" \xB7 ",
|
|
530
|
+
t("completeConditions"),
|
|
531
|
+
" ",
|
|
532
|
+
entry.conditions
|
|
533
|
+
] }) : null,
|
|
534
|
+
tally,
|
|
535
|
+
steer
|
|
536
|
+
] });
|
|
537
|
+
}
|
|
538
|
+
function outcomeTagOf(entry) {
|
|
539
|
+
if (entry.kind === "narrow") return entry.outcome === "error" ? "error" : "ok";
|
|
540
|
+
if (entry.kind === "complete") return entry.outcome === "error" ? "error" : entry.outcome === "unsatisfied" ? "warn" : "ok";
|
|
541
|
+
return entry.outcome === "deny" ? "deny" : entry.outcome === "error" ? "error" : entry.outcome === "low" ? "warn" : "ok";
|
|
542
|
+
}
|
|
543
|
+
function renderBody(props, entry) {
|
|
544
|
+
return entry.kind === "gate" ? renderGate(props, entry) : entry.kind === "narrow" ? renderNarrow(props, entry) : entry.kind === "complete" ? renderComplete(props, entry) : renderCheck(props, entry);
|
|
545
|
+
}
|
|
546
|
+
function RatingButtons({ id, t, ratings, onRate }) {
|
|
547
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-rate", role: "group", "aria-label": t("rateGroup"), children: [
|
|
548
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: `decision-rate-btn${ratings[id] === "good" ? " decision-rate-on decision-rate-good" : ""}`, "aria-label": t("rateGood"), title: t("rateGood"), "aria-pressed": ratings[id] === "good", onClick: () => onRate(id, "good"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
|
|
549
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 7.2 8.1 2.4c.6-.9 2-.5 2 .6V6h3.2c.9 0 1.5.8 1.3 1.6l-1.1 4.6c-.2.8-.9 1.3-1.7 1.3H5V7.2Z", stroke: "currentColor", "stroke-width": "1.2", "stroke-linejoin": "round" }),
|
|
550
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 7.2H2.6v6.3H5", stroke: "currentColor", "stroke-width": "1.2", "stroke-linejoin": "round" })
|
|
551
|
+
] }) }),
|
|
552
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: `decision-rate-btn${ratings[id] === "bad" ? " decision-rate-on decision-rate-bad" : ""}`, "aria-label": t("rateBad"), title: t("rateBad"), "aria-pressed": ratings[id] === "bad", onClick: () => onRate(id, "bad"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
|
|
553
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M11 8.8 7.9 13.6c-.6.9-2 .5-2-.6V10H2.7c-.9 0-1.5-.8-1.3-1.6l1.1-4.6C2.7 3 3.4 2.5 4.2 2.5H11v6.3Z", stroke: "currentColor", "stroke-width": "1.2", "stroke-linejoin": "round" }),
|
|
554
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M11 8.8h2.4V2.5H11", stroke: "currentColor", "stroke-width": "1.2", "stroke-linejoin": "round" })
|
|
555
|
+
] }) }),
|
|
556
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: `decision-rate-btn${ratings[id] === "unsure" ? " decision-rate-on decision-rate-unsure" : ""}`, "aria-label": t("rateUnsure"), title: t("rateUnsure"), "aria-pressed": ratings[id] === "unsure", onClick: () => onRate(id, "unsure"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "14", height: "14", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
|
|
557
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8", cy: "8", r: "6.2", stroke: "currentColor", "stroke-width": "1.2" }),
|
|
558
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6.3 6.2c0-1 .8-1.7 1.7-1.7s1.7.7 1.7 1.6c0 1.3-1.6 1.4-1.7 2.6", stroke: "currentColor", "stroke-width": "1.2", "stroke-linecap": "round", "stroke-linejoin": "round" }),
|
|
559
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8", cy: "11.4", r: ".8", fill: "currentColor" })
|
|
560
|
+
] }) })
|
|
561
|
+
] });
|
|
562
|
+
}
|
|
563
|
+
function DecisionLogList({ entries, ratings, onRate, t, newestFirst = true }) {
|
|
564
|
+
const rows = newestFirst ? entries.slice().reverse() : entries.slice();
|
|
565
|
+
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: [
|
|
566
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-time", children: time(entry.at) }),
|
|
567
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-body", children: renderBody({ t }, entry) }),
|
|
568
|
+
entry.id && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RatingButtons, { id: entry.id, t, ratings, onRate })
|
|
569
|
+
] }, entry.id ?? `${entry.at}-${index}`)) });
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// src/client/use-annotations.ts
|
|
573
|
+
var import_react = require("react");
|
|
574
|
+
function useAnnotations(sessionId, onFail) {
|
|
575
|
+
const [ratings, setRatings] = (0, import_react.useState)({});
|
|
576
|
+
const seed = (0, import_react.useCallback)((analysis) => {
|
|
577
|
+
setRatings(analysis?.ratings ?? {});
|
|
578
|
+
}, []);
|
|
579
|
+
const annotate = (0, import_react.useCallback)(async (id, rating) => {
|
|
580
|
+
setRatings((prev) => ({ ...prev, [id]: rating }));
|
|
581
|
+
try {
|
|
582
|
+
await request("annotate", {
|
|
583
|
+
method: "POST",
|
|
584
|
+
headers: { "content-type": "application/json" },
|
|
585
|
+
body: JSON.stringify({ sessionId, target: id, rating })
|
|
586
|
+
});
|
|
587
|
+
} catch {
|
|
588
|
+
onFail();
|
|
589
|
+
setRatings((prev) => {
|
|
590
|
+
const next = { ...prev };
|
|
591
|
+
delete next[id];
|
|
592
|
+
return next;
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
}, [sessionId, onFail]);
|
|
596
|
+
return { ratings, seed, annotate };
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// src/client/panel.tsx
|
|
600
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
275
601
|
function DecisionPanel(props) {
|
|
276
|
-
return /* @__PURE__ */ (0,
|
|
602
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SessionPanel, { ...props }, props.sessionId);
|
|
277
603
|
}
|
|
278
604
|
function Bar({ allow, ask, deny }) {
|
|
279
605
|
const total = allow + ask + deny;
|
|
280
|
-
if (total === 0) return /* @__PURE__ */ (0,
|
|
606
|
+
if (total === 0) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "decision-bar decision-bar-empty", "aria-hidden": "true" });
|
|
281
607
|
const pct = (n) => `${n / total * 100}%`;
|
|
282
|
-
return /* @__PURE__ */ (0,
|
|
283
|
-
allow > 0 && /* @__PURE__ */ (0,
|
|
284
|
-
ask > 0 && /* @__PURE__ */ (0,
|
|
285
|
-
deny > 0 && /* @__PURE__ */ (0,
|
|
608
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-bar", "aria-hidden": "true", children: [
|
|
609
|
+
allow > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-bar-allow", style: { width: pct(allow) } }),
|
|
610
|
+
ask > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-bar-ask", style: { width: pct(ask) } }),
|
|
611
|
+
deny > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-bar-deny", style: { width: pct(deny) } })
|
|
286
612
|
] });
|
|
287
613
|
}
|
|
288
614
|
function SessionPanel({ sessionId, t }) {
|
|
289
|
-
const dialog = (0,
|
|
290
|
-
const trigger = (0,
|
|
291
|
-
const [open, setOpen] = (0,
|
|
292
|
-
const [enabled, setEnabled] = (0,
|
|
293
|
-
const [
|
|
294
|
-
const [
|
|
295
|
-
const [
|
|
296
|
-
const [
|
|
297
|
-
const [status, setStatus] = (0,
|
|
298
|
-
const [message, setMessage] = (0,
|
|
299
|
-
(
|
|
615
|
+
const dialog = (0, import_react2.useRef)(null);
|
|
616
|
+
const trigger = (0, import_react2.useRef)(null);
|
|
617
|
+
const [open, setOpen] = (0, import_react2.useState)(false);
|
|
618
|
+
const [enabled, setEnabled] = (0, import_react2.useState)(null);
|
|
619
|
+
const [busy, setBusy] = (0, import_react2.useState)(false);
|
|
620
|
+
const [metrics, setMetrics] = (0, import_react2.useState)(null);
|
|
621
|
+
const [log, setLog] = (0, import_react2.useState)([]);
|
|
622
|
+
const [analysis, setAnalysis] = (0, import_react2.useState)(null);
|
|
623
|
+
const [status, setStatus] = (0, import_react2.useState)("idle");
|
|
624
|
+
const [message, setMessage] = (0, import_react2.useState)("");
|
|
625
|
+
const { ratings, seed, annotate } = useAnnotations(sessionId, () => setMessage(t("annotateFailed")));
|
|
626
|
+
(0, import_react2.useEffect)(() => {
|
|
300
627
|
let live = true;
|
|
301
628
|
void request(`session?sessionId=${encodeURIComponent(sessionId)}`).then((value) => {
|
|
302
|
-
if (live)
|
|
303
|
-
setEnabled(value.enabled);
|
|
304
|
-
setCapacityExceeded(Boolean(value.capacityExceeded));
|
|
305
|
-
}
|
|
629
|
+
if (live) setEnabled(value.enabled);
|
|
306
630
|
}).catch(() => {
|
|
307
631
|
if (live) setMessage(t("error"));
|
|
308
632
|
});
|
|
@@ -323,7 +647,7 @@ function SessionPanel({ sessionId, t }) {
|
|
|
323
647
|
if (live()) setStatus("down");
|
|
324
648
|
});
|
|
325
649
|
};
|
|
326
|
-
(0,
|
|
650
|
+
(0, import_react2.useEffect)(() => {
|
|
327
651
|
if (!open) return;
|
|
328
652
|
const element = dialog.current;
|
|
329
653
|
if (!element) return;
|
|
@@ -338,11 +662,18 @@ function SessionPanel({ sessionId, t }) {
|
|
|
338
662
|
if (alive) setLog(value.entries);
|
|
339
663
|
}).catch(() => {
|
|
340
664
|
});
|
|
665
|
+
void request(`logs?sessionId=${encodeURIComponent(sessionId)}`).then((value) => {
|
|
666
|
+
if (alive) {
|
|
667
|
+
setAnalysis(value);
|
|
668
|
+
seed(value);
|
|
669
|
+
}
|
|
670
|
+
}).catch(() => {
|
|
671
|
+
});
|
|
341
672
|
return () => {
|
|
342
673
|
alive = false;
|
|
343
674
|
element.close();
|
|
344
675
|
};
|
|
345
|
-
}, [open, t, sessionId]);
|
|
676
|
+
}, [open, t, sessionId, seed]);
|
|
346
677
|
const updateEnabled = async (next) => {
|
|
347
678
|
setBusy(true);
|
|
348
679
|
try {
|
|
@@ -352,159 +683,34 @@ function SessionPanel({ sessionId, t }) {
|
|
|
352
683
|
body: JSON.stringify({ sessionId, enabled: next })
|
|
353
684
|
});
|
|
354
685
|
setEnabled(value.enabled);
|
|
355
|
-
setCapacityExceeded(Boolean(value.capacityExceeded));
|
|
356
686
|
} catch {
|
|
357
687
|
setMessage(t("error"));
|
|
358
688
|
} finally {
|
|
359
689
|
setBusy(false);
|
|
360
690
|
}
|
|
361
691
|
};
|
|
362
|
-
const
|
|
363
|
-
const reasonText = (reason) => reason === "unavailable" || reason === "unreachable" ? t("reasonUnavailable") : reason === "http-error" ? t("reasonHttpError") : reason === "invalid-response" ? t("reasonInvalidResponse") : reason === "low-confidence" ? t("reasonLowConfidence") : reason === "capacity" ? t("reasonCapacity") : reason === "too-many-candidates" ? t("reasonTooMany") : reason === "low-keep" ? t("reasonLowKeep") : reason === "config" ? t("reasonConfig") : reason;
|
|
364
|
-
const actionText = (action) => action === "deny" ? t("actionDeny") : action === "ask" ? t("actionAsk") : action;
|
|
365
|
-
const failDetail = (entry) => {
|
|
366
|
-
const reason = reasonText(entry.reason);
|
|
367
|
-
if (entry.reason === "low-confidence" && typeof entry.confidence === "number") return `${reason}\uFF08${t("logConfidence")} ${entry.confidence}\uFF09`;
|
|
368
|
-
return reason;
|
|
369
|
-
};
|
|
370
|
-
const outcomeTag = (entry) => entry.outcome === "deny" ? "deny" : entry.outcome === "error" ? "error" : entry.outcome === "low" ? "warn" : "ok";
|
|
371
|
-
const renderGate = (entry) => {
|
|
372
|
-
const tool = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: "decision-log-tool", children: entry.tool ?? "\u2014" });
|
|
373
|
-
if (entry.outcome === "error") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
374
|
-
t("logGate"),
|
|
375
|
-
" \xB7 ",
|
|
376
|
-
tool,
|
|
377
|
-
" ",
|
|
378
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
379
|
-
t("logEvalFailed"),
|
|
380
|
-
entry.reason ? ` \xB7 ${failDetail(entry)}` : ""
|
|
381
|
-
] })
|
|
382
|
-
] });
|
|
383
|
-
const verdict = entry.outcome === "deny" ? t("actionDeny") : actionText(entry.action) ?? entry.outcome;
|
|
384
|
-
const suggestion = entry.suggestion && entry.suggestion !== entry.action ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
385
|
-
" \xB7 ",
|
|
386
|
-
t("logSuggested"),
|
|
387
|
-
" ",
|
|
388
|
-
actionText(entry.suggestion)
|
|
389
|
-
] }) : null;
|
|
390
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
391
|
-
t("logGate"),
|
|
392
|
-
" \xB7 ",
|
|
393
|
-
tool,
|
|
394
|
-
" ",
|
|
395
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-verdict", children: [
|
|
396
|
-
"\u2192 ",
|
|
397
|
-
verdict
|
|
398
|
-
] }),
|
|
399
|
-
suggestion
|
|
400
|
-
] });
|
|
401
|
-
};
|
|
402
|
-
const renderCheck = (entry) => {
|
|
403
|
-
const label = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { title: t("logCheckHint"), children: t("logCheck") });
|
|
404
|
-
if (entry.outcome === "error") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
405
|
-
label,
|
|
406
|
-
" ",
|
|
407
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
408
|
-
t("logEvalFailed"),
|
|
409
|
-
entry.reason ? ` \xB7 ${failDetail(entry)}` : ""
|
|
410
|
-
] })
|
|
411
|
-
] });
|
|
412
|
-
const result = entry.outcome === "low" ? t("checkResultLow") : t("checkResultOk");
|
|
413
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
414
|
-
label,
|
|
415
|
-
" \xB7 ",
|
|
416
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-verdict", children: result }),
|
|
417
|
-
entry.score !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", title: t("logScoreHint"), children: [
|
|
418
|
-
" \xB7 ",
|
|
419
|
-
t("logScore"),
|
|
420
|
-
" ",
|
|
421
|
-
entry.score,
|
|
422
|
-
"/2"
|
|
423
|
-
] }) : null,
|
|
424
|
-
typeof entry.confidence === "number" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
425
|
-
" \xB7 ",
|
|
426
|
-
t("logConfidence"),
|
|
427
|
-
" ",
|
|
428
|
-
entry.confidence
|
|
429
|
-
] }) : null
|
|
430
|
-
] });
|
|
431
|
-
};
|
|
432
|
-
const renderNarrow = (entry) => {
|
|
433
|
-
const label = /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { title: t("logNarrowHint"), children: t("logNarrow") });
|
|
434
|
-
if (entry.outcome === "error") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
435
|
-
label,
|
|
436
|
-
" ",
|
|
437
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
438
|
-
t("logEvalFailed"),
|
|
439
|
-
entry.reason ? ` \xB7 ${failDetail(entry)}` : ""
|
|
440
|
-
] })
|
|
441
|
-
] });
|
|
442
|
-
const dropped = entry.dropped ?? 0;
|
|
443
|
-
const kept = typeof entry.kept === "number" ? entry.kept : void 0;
|
|
444
|
-
const guardText = entry.reason === "too-many-candidates" ? typeof entry.candidates === "number" ? t("reasonTooManyCount", { count: entry.candidates }) : t("reasonTooMany") : entry.reason === "low-keep" ? t("reasonLowKeep") : void 0;
|
|
445
|
-
const guard = guardText ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
446
|
-
" \xB7 ",
|
|
447
|
-
guardText
|
|
448
|
-
] }) : null;
|
|
449
|
-
const modeTag = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
450
|
-
" \xB7 ",
|
|
451
|
-
entry.mode === "enforce" ? t("narrowEnforce") : t("narrowObserve")
|
|
452
|
-
] });
|
|
453
|
-
if (dropped === 0) {
|
|
454
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
455
|
-
label,
|
|
456
|
-
" \xB7 ",
|
|
457
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-verdict", children: t("narrowKept") }),
|
|
458
|
-
kept !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
459
|
-
" \xB7 ",
|
|
460
|
-
t("narrowKeptCount"),
|
|
461
|
-
" ",
|
|
462
|
-
kept
|
|
463
|
-
] }) : null,
|
|
464
|
-
guard,
|
|
465
|
-
modeTag
|
|
466
|
-
] });
|
|
467
|
-
}
|
|
468
|
-
const names = Array.isArray(entry.tools) && entry.tools.length > 0 ? entry.tools.join("\u3001") : void 0;
|
|
469
|
-
const overflow = names && Array.isArray(entry.tools) && entry.tools.length < dropped ? t("narrowMore", { count: dropped }) : "";
|
|
470
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
471
|
-
label,
|
|
472
|
-
" \xB7 ",
|
|
473
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-verdict", children: [
|
|
474
|
-
t("narrowDropped"),
|
|
475
|
-
" ",
|
|
476
|
-
dropped,
|
|
477
|
-
kept !== void 0 ? ` / ${t("narrowKeptCount")} ${kept}` : ""
|
|
478
|
-
] }),
|
|
479
|
-
names ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "decision-log-detail", children: [
|
|
480
|
-
" \xB7 ",
|
|
481
|
-
names,
|
|
482
|
-
overflow
|
|
483
|
-
] }) : null,
|
|
484
|
-
guard,
|
|
485
|
-
modeTag
|
|
486
|
-
] });
|
|
487
|
-
};
|
|
488
|
-
const outcomeTagOf = (entry) => entry.kind === "narrow" ? entry.outcome === "error" ? "error" : entry.outcome === "applied" ? "ok" : "ok" : outcomeTag(entry);
|
|
692
|
+
const time2 = (at) => new Date(at).toLocaleTimeString();
|
|
489
693
|
const statusLabel = status === "ok" ? t("statusOk") : status === "down" ? t("statusDown") : status === "checking" ? t("statusChecking") : t("statusIdle");
|
|
490
694
|
const attempts = metrics?.attempts ?? 0;
|
|
695
|
+
const severity = metrics?.check?.trend?.severity ?? "normal";
|
|
696
|
+
const trendTitle = severity === "severe" ? t("trendSevereHint") : severity === "warn" ? t("trendWarnHint") : void 0;
|
|
491
697
|
const checkValid = metrics?.check ? metrics.check.attempts - metrics.check.failures : 0;
|
|
492
698
|
const checkPassRate = metrics?.check && checkValid > 0 ? Math.round((checkValid - metrics.check.low) / checkValid * 100) : null;
|
|
493
699
|
const narrowValid = metrics?.narrow ? metrics.narrow.attempts - metrics.narrow.failures : 0;
|
|
494
700
|
const narrowApplyRate = metrics?.narrow && narrowValid > 0 ? Math.round(metrics.narrow.applied / narrowValid * 100) : null;
|
|
495
|
-
return /* @__PURE__ */ (0,
|
|
496
|
-
/* @__PURE__ */ (0,
|
|
497
|
-
/* @__PURE__ */ (0,
|
|
498
|
-
/* @__PURE__ */ (0,
|
|
499
|
-
/* @__PURE__ */ (0,
|
|
701
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
702
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { ref: trigger, className: `decision-trigger decision-trend-${severity}`, type: "button", "aria-haspopup": "dialog", "aria-expanded": open, "aria-busy": enabled === null, "aria-label": t("button"), title: trendTitle, onClick: () => setOpen(true), children: [
|
|
703
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { className: "decision-trigger-icon", width: "15", height: "15", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: [
|
|
704
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "8", r: "6.4", stroke: "currentColor", "stroke-width": "1.3" }),
|
|
705
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 4.6 11.3 11H4.7L8 4.6Z", fill: "currentColor", stroke: "none" })
|
|
500
706
|
] }),
|
|
501
|
-
/* @__PURE__ */ (0,
|
|
707
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-trigger-text", children: [
|
|
502
708
|
t("triggerDecisions"),
|
|
503
709
|
" ",
|
|
504
|
-
/* @__PURE__ */ (0,
|
|
710
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: attempts })
|
|
505
711
|
] })
|
|
506
712
|
] }),
|
|
507
|
-
/* @__PURE__ */ (0,
|
|
713
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
508
714
|
"dialog",
|
|
509
715
|
{
|
|
510
716
|
ref: dialog,
|
|
@@ -529,150 +735,174 @@ function SessionPanel({ sessionId, t }) {
|
|
|
529
735
|
trigger.current?.focus();
|
|
530
736
|
},
|
|
531
737
|
children: [
|
|
532
|
-
/* @__PURE__ */ (0,
|
|
533
|
-
/* @__PURE__ */ (0,
|
|
534
|
-
/* @__PURE__ */ (0,
|
|
738
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-head", children: [
|
|
739
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h2", { id: "decision-panel-title", children: t("title") }),
|
|
740
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", onClick: () => {
|
|
535
741
|
dialog.current?.close();
|
|
536
742
|
}, "aria-label": t("close"), children: "\xD7" })
|
|
537
743
|
] }),
|
|
538
|
-
/* @__PURE__ */ (0,
|
|
539
|
-
/* @__PURE__ */ (0,
|
|
540
|
-
enabled === null && /* @__PURE__ */ (0,
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
/* @__PURE__ */ (0,
|
|
544
|
-
|
|
545
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
744
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-body", children: [
|
|
745
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { children: [
|
|
746
|
+
enabled === null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { role: "status", children: t("loading") }),
|
|
747
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "decision-toggle", children: [
|
|
748
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-toggle-text", children: t("enabled") }),
|
|
749
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-toggle-box", children: [
|
|
750
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
546
751
|
"input",
|
|
547
752
|
{
|
|
548
753
|
className: "decision-toggle-input",
|
|
549
754
|
type: "checkbox",
|
|
550
755
|
checked: enabled === true,
|
|
551
|
-
disabled: busy || enabled === null
|
|
756
|
+
disabled: busy || enabled === null,
|
|
552
757
|
onChange: (event) => void updateEnabled(event.target.checked)
|
|
553
758
|
}
|
|
554
759
|
),
|
|
555
|
-
/* @__PURE__ */ (0,
|
|
760
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-toggle-track", "aria-hidden": "true" })
|
|
556
761
|
] })
|
|
557
762
|
] }),
|
|
558
|
-
/* @__PURE__ */ (0,
|
|
559
|
-
/* @__PURE__ */ (0,
|
|
560
|
-
/* @__PURE__ */ (0,
|
|
763
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-status", children: [
|
|
764
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `decision-status-dot decision-status-${status}`, "aria-hidden": "true" }),
|
|
765
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-status-label", children: [
|
|
561
766
|
t("backendStatus"),
|
|
562
767
|
"\uFF1A",
|
|
563
768
|
statusLabel
|
|
564
769
|
] }),
|
|
565
|
-
/* @__PURE__ */ (0,
|
|
770
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "decision-status-recheck", disabled: status === "checking", onClick: () => checkStatus(() => true), children: t("recheck") })
|
|
566
771
|
] })
|
|
567
772
|
] }),
|
|
568
|
-
/* @__PURE__ */ (0,
|
|
569
|
-
/* @__PURE__ */ (0,
|
|
570
|
-
!metrics?.gate && !metrics?.check && !metrics?.narrow ? /* @__PURE__ */ (0,
|
|
571
|
-
metrics.gate && /* @__PURE__ */ (0,
|
|
572
|
-
/* @__PURE__ */ (0,
|
|
573
|
-
/* @__PURE__ */ (0,
|
|
574
|
-
/* @__PURE__ */ (0,
|
|
773
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { children: [
|
|
774
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: t("metrics") }),
|
|
775
|
+
!metrics?.gate && !metrics?.check && !metrics?.narrow && !metrics?.complete ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "decision-empty", role: "status", children: t("empty") }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-cards", children: [
|
|
776
|
+
metrics.gate && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("article", { className: "decision-card decision-card-wide", children: [
|
|
777
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("header", { className: "decision-card-head", children: [
|
|
778
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-card-title", children: t("cardGate") }),
|
|
779
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-total", children: [
|
|
575
780
|
metrics.gate.attempts,
|
|
576
|
-
/* @__PURE__ */ (0,
|
|
781
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { children: t("cardTimes") })
|
|
577
782
|
] })
|
|
578
783
|
] }),
|
|
579
|
-
/* @__PURE__ */ (0,
|
|
580
|
-
/* @__PURE__ */ (0,
|
|
581
|
-
/* @__PURE__ */ (0,
|
|
582
|
-
/* @__PURE__ */ (0,
|
|
784
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Bar, { allow: metrics.gate.allow, ask: metrics.gate.ask, deny: metrics.gate.deny }),
|
|
785
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("ul", { className: "decision-legend", children: [
|
|
786
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { children: [
|
|
787
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-legend-dot decision-dot-allow", "aria-hidden": "true" }),
|
|
583
788
|
t("allow"),
|
|
584
|
-
/* @__PURE__ */ (0,
|
|
789
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: metrics.gate.allow })
|
|
585
790
|
] }),
|
|
586
|
-
/* @__PURE__ */ (0,
|
|
587
|
-
/* @__PURE__ */ (0,
|
|
791
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { children: [
|
|
792
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-legend-dot decision-dot-ask", "aria-hidden": "true" }),
|
|
588
793
|
t("ask"),
|
|
589
|
-
/* @__PURE__ */ (0,
|
|
794
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: metrics.gate.ask })
|
|
590
795
|
] }),
|
|
591
|
-
/* @__PURE__ */ (0,
|
|
592
|
-
/* @__PURE__ */ (0,
|
|
796
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { children: [
|
|
797
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-legend-dot decision-dot-deny", "aria-hidden": "true" }),
|
|
593
798
|
t("deny"),
|
|
594
|
-
/* @__PURE__ */ (0,
|
|
799
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: metrics.gate.deny })
|
|
595
800
|
] })
|
|
596
801
|
] }),
|
|
597
|
-
/* @__PURE__ */ (0,
|
|
598
|
-
/* @__PURE__ */ (0,
|
|
802
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("footer", { className: "decision-card-foot", children: [
|
|
803
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
|
|
599
804
|
t("actualShort"),
|
|
600
|
-
/* @__PURE__ */ (0,
|
|
805
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("b", { children: [
|
|
601
806
|
metrics.gate.actual.allow,
|
|
602
807
|
" / ",
|
|
603
808
|
metrics.gate.actual.deny
|
|
604
809
|
] })
|
|
605
810
|
] }),
|
|
606
|
-
/* @__PURE__ */ (0,
|
|
811
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
|
|
607
812
|
t("failures"),
|
|
608
|
-
/* @__PURE__ */ (0,
|
|
813
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: metrics.gate.failures })
|
|
609
814
|
] })
|
|
610
815
|
] })
|
|
611
816
|
] }),
|
|
612
|
-
metrics.check && /* @__PURE__ */ (0,
|
|
613
|
-
/* @__PURE__ */ (0,
|
|
614
|
-
/* @__PURE__ */ (0,
|
|
615
|
-
/* @__PURE__ */ (0,
|
|
817
|
+
metrics.check && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("article", { className: "decision-card", children: [
|
|
818
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("header", { className: "decision-card-head", children: [
|
|
819
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-card-title", children: t("cardCheck") }),
|
|
820
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-total", children: [
|
|
616
821
|
metrics.check.attempts,
|
|
617
|
-
/* @__PURE__ */ (0,
|
|
822
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { children: t("cardTimes") })
|
|
618
823
|
] })
|
|
619
824
|
] }),
|
|
620
|
-
/* @__PURE__ */ (0,
|
|
621
|
-
/* @__PURE__ */ (0,
|
|
622
|
-
/* @__PURE__ */ (0,
|
|
623
|
-
/* @__PURE__ */ (0,
|
|
825
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-card-stats", children: [
|
|
826
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
|
|
827
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num decision-stat-warn", children: metrics.check.low }),
|
|
828
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("checkLow") })
|
|
624
829
|
] }),
|
|
625
|
-
/* @__PURE__ */ (0,
|
|
626
|
-
/* @__PURE__ */ (0,
|
|
627
|
-
/* @__PURE__ */ (0,
|
|
830
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
|
|
831
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num", children: metrics.check.attempts - metrics.check.low - metrics.check.failures }),
|
|
832
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("checkOk") })
|
|
628
833
|
] }),
|
|
629
|
-
/* @__PURE__ */ (0,
|
|
630
|
-
/* @__PURE__ */ (0,
|
|
631
|
-
/* @__PURE__ */ (0,
|
|
834
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
|
|
835
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num decision-stat-muted", children: metrics.check.failures }),
|
|
836
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("failures") })
|
|
632
837
|
] })
|
|
633
838
|
] }),
|
|
634
|
-
/* @__PURE__ */ (0,
|
|
839
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("footer", { className: "decision-card-foot decision-card-rate", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
|
|
635
840
|
t("checkPassRate"),
|
|
636
|
-
/* @__PURE__ */ (0,
|
|
841
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: checkPassRate === null ? t("rateNa") : `${checkPassRate}%` })
|
|
637
842
|
] }) })
|
|
638
843
|
] }),
|
|
639
|
-
metrics.narrow && /* @__PURE__ */ (0,
|
|
640
|
-
/* @__PURE__ */ (0,
|
|
641
|
-
/* @__PURE__ */ (0,
|
|
642
|
-
/* @__PURE__ */ (0,
|
|
844
|
+
metrics.narrow && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("article", { className: "decision-card", children: [
|
|
845
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("header", { className: "decision-card-head", children: [
|
|
846
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-card-title", children: t("cardNarrow") }),
|
|
847
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-total", children: [
|
|
643
848
|
metrics.narrow.attempts,
|
|
644
|
-
/* @__PURE__ */ (0,
|
|
849
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { children: t("cardTimes") })
|
|
645
850
|
] })
|
|
646
851
|
] }),
|
|
647
|
-
/* @__PURE__ */ (0,
|
|
648
|
-
/* @__PURE__ */ (0,
|
|
649
|
-
/* @__PURE__ */ (0,
|
|
650
|
-
/* @__PURE__ */ (0,
|
|
852
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-card-stats", children: [
|
|
853
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
|
|
854
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num", children: metrics.narrow.applied }),
|
|
855
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("narrowAppliedLabel") })
|
|
651
856
|
] }),
|
|
652
|
-
/* @__PURE__ */ (0,
|
|
653
|
-
/* @__PURE__ */ (0,
|
|
654
|
-
/* @__PURE__ */ (0,
|
|
857
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
|
|
858
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num", children: metrics.narrow.dropped }),
|
|
859
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("narrowDroppedLabel") })
|
|
655
860
|
] }),
|
|
656
|
-
/* @__PURE__ */ (0,
|
|
657
|
-
/* @__PURE__ */ (0,
|
|
658
|
-
/* @__PURE__ */ (0,
|
|
861
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
|
|
862
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num decision-stat-muted", children: metrics.narrow.failures }),
|
|
863
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("failures") })
|
|
659
864
|
] })
|
|
660
865
|
] }),
|
|
661
|
-
/* @__PURE__ */ (0,
|
|
866
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("footer", { className: "decision-card-foot decision-card-rate", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
|
|
662
867
|
t("narrowApplyRate"),
|
|
663
|
-
/* @__PURE__ */ (0,
|
|
868
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: narrowApplyRate === null ? t("rateNa") : `${narrowApplyRate}%` })
|
|
869
|
+
] }) })
|
|
870
|
+
] }),
|
|
871
|
+
metrics.complete && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("article", { className: "decision-card", children: [
|
|
872
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("header", { className: "decision-card-head", children: [
|
|
873
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-card-title", children: t("cardComplete") }),
|
|
874
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "decision-card-total", children: [
|
|
875
|
+
metrics.complete.attempts,
|
|
876
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("small", { children: t("cardTimes") })
|
|
877
|
+
] })
|
|
878
|
+
] }),
|
|
879
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-card-stats", children: [
|
|
880
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
|
|
881
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num", children: metrics.complete.satisfied }),
|
|
882
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("completeSatisfied") })
|
|
883
|
+
] }),
|
|
884
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
|
|
885
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num decision-stat-warn", children: metrics.complete.unsatisfied }),
|
|
886
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("completeUnsatisfied") })
|
|
887
|
+
] }),
|
|
888
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "decision-stat", children: [
|
|
889
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-num decision-stat-muted", children: metrics.complete.insufficient }),
|
|
890
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "decision-stat-label", children: t("completeInsufficient") })
|
|
891
|
+
] })
|
|
892
|
+
] }),
|
|
893
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("footer", { className: "decision-card-foot decision-card-rate", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
|
|
894
|
+
t("failures"),
|
|
895
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("b", { children: metrics.complete.failures })
|
|
664
896
|
] }) })
|
|
665
897
|
] })
|
|
666
898
|
] })
|
|
667
899
|
] }),
|
|
668
|
-
/* @__PURE__ */ (0,
|
|
669
|
-
/* @__PURE__ */ (0,
|
|
670
|
-
log.length === 0 ? /* @__PURE__ */ (0,
|
|
671
|
-
|
|
672
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "decision-log-body", children: entry.kind === "gate" ? renderGate(entry) : entry.kind === "narrow" ? renderNarrow(entry) : renderCheck(entry) })
|
|
673
|
-
] }, `${entry.at}-${index}`)) })
|
|
900
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("section", { children: [
|
|
901
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { children: t("log") }),
|
|
902
|
+
log.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "decision-empty", role: "status", children: t("logEmpty") }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DecisionLogList, { entries: log, ratings, onRate: (id, rating) => void annotate(id, rating), t }),
|
|
903
|
+
analysis && analysis.annotations.rated > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "decision-muted decision-rate-summary", children: t("rateSummary", { rated: analysis.annotations.rated, good: analysis.annotations.good, bad: analysis.annotations.bad, unsure: analysis.annotations.unsure }) })
|
|
674
904
|
] }),
|
|
675
|
-
message && /* @__PURE__ */ (0,
|
|
905
|
+
message && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { role: "status", className: "decision-message decision-message-warn", children: message })
|
|
676
906
|
] })
|
|
677
907
|
]
|
|
678
908
|
}
|
|
@@ -680,12 +910,144 @@ function SessionPanel({ sessionId, t }) {
|
|
|
680
910
|
] });
|
|
681
911
|
}
|
|
682
912
|
|
|
913
|
+
// src/client/analysis-view.tsx
|
|
914
|
+
var import_react3 = require("react");
|
|
915
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
916
|
+
function Stat({ label, value, tone }) {
|
|
917
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-stat", children: [
|
|
918
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `decision-stat-num${tone ? ` decision-stat-${tone}` : ""}`, children: value }),
|
|
919
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-stat-label", children: label })
|
|
920
|
+
] });
|
|
921
|
+
}
|
|
922
|
+
function AnalysisView({ sessionId, t }) {
|
|
923
|
+
const [analysis, setAnalysis] = (0, import_react3.useState)(null);
|
|
924
|
+
const [log, setLog] = (0, import_react3.useState)([]);
|
|
925
|
+
const [loaded, setLoaded] = (0, import_react3.useState)(false);
|
|
926
|
+
const [message, setMessage] = (0, import_react3.useState)("");
|
|
927
|
+
const { ratings, seed, annotate } = useAnnotations(sessionId, () => setMessage(t("annotateFailed")));
|
|
928
|
+
(0, import_react3.useEffect)(() => {
|
|
929
|
+
let alive = true;
|
|
930
|
+
setMessage("");
|
|
931
|
+
setLoaded(false);
|
|
932
|
+
void Promise.allSettled([
|
|
933
|
+
request(`logs?sessionId=${encodeURIComponent(sessionId)}`).then((value) => {
|
|
934
|
+
if (alive) {
|
|
935
|
+
setAnalysis(value);
|
|
936
|
+
seed(value);
|
|
937
|
+
}
|
|
938
|
+
}),
|
|
939
|
+
request(`log?sessionId=${encodeURIComponent(sessionId)}`).then((value) => {
|
|
940
|
+
if (alive) setLog(value.entries);
|
|
941
|
+
})
|
|
942
|
+
]).finally(() => {
|
|
943
|
+
if (alive) setLoaded(true);
|
|
944
|
+
});
|
|
945
|
+
return () => {
|
|
946
|
+
alive = false;
|
|
947
|
+
};
|
|
948
|
+
}, [sessionId, seed]);
|
|
949
|
+
const profile = analysis?.profile;
|
|
950
|
+
const backtest = analysis?.trendBacktest;
|
|
951
|
+
const annotations = analysis?.annotations;
|
|
952
|
+
const meanScore = profile && profile.check.scoreCount > 0 ? Math.round(profile.check.scoreSum / profile.check.scoreCount * 10) / 10 : null;
|
|
953
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "decision-analysis", role: "region", "aria-label": t("analysisTitle"), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-analysis-inner", children: [
|
|
954
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h2", { className: "decision-analysis-title", children: t("analysisTitle") }),
|
|
955
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-muted decision-analysis-intro", children: t("analysisIntro") }),
|
|
956
|
+
loaded && (!analysis || analysis.totalDecisions === 0) ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-empty", role: "status", children: t("logEmpty") }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
957
|
+
profile && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "decision-analysis-section", children: [
|
|
958
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: t("analysisProfile") }),
|
|
959
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-analysis-cards", children: [
|
|
960
|
+
profile.gate.attempts > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "decision-card", children: [
|
|
961
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { className: "decision-card-head", children: [
|
|
962
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-card-title", children: t("cardGate") }),
|
|
963
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "decision-card-total", children: [
|
|
964
|
+
profile.gate.attempts,
|
|
965
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("small", { children: t("cardTimes") })
|
|
966
|
+
] })
|
|
967
|
+
] }),
|
|
968
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-card-stats", children: [
|
|
969
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("deny"), value: profile.gate.deny, tone: "warn" }),
|
|
970
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("allow"), value: profile.gate.allow }),
|
|
971
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("ask"), value: profile.gate.ask }),
|
|
972
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("failures"), value: profile.gate.error, tone: "muted" })
|
|
973
|
+
] })
|
|
974
|
+
] }),
|
|
975
|
+
profile.check.attempts > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "decision-card", children: [
|
|
976
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { className: "decision-card-head", children: [
|
|
977
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-card-title", children: t("cardCheck") }),
|
|
978
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "decision-card-total", children: [
|
|
979
|
+
profile.check.attempts,
|
|
980
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("small", { children: t("cardTimes") })
|
|
981
|
+
] })
|
|
982
|
+
] }),
|
|
983
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-card-stats", children: [
|
|
984
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("checkLow"), value: profile.check.low, tone: "warn" }),
|
|
985
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("checkOk"), value: profile.check.ok }),
|
|
986
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("analysisMeanScore"), value: meanScore === null ? t("rateNa") : `${meanScore}/2` }),
|
|
987
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("failures"), value: profile.check.error, tone: "muted" })
|
|
988
|
+
] })
|
|
989
|
+
] }),
|
|
990
|
+
profile.narrow.attempts > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "decision-card", children: [
|
|
991
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { className: "decision-card-head", children: [
|
|
992
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-card-title", children: t("cardNarrow") }),
|
|
993
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "decision-card-total", children: [
|
|
994
|
+
profile.narrow.attempts,
|
|
995
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("small", { children: t("cardTimes") })
|
|
996
|
+
] })
|
|
997
|
+
] }),
|
|
998
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-card-stats", children: [
|
|
999
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("narrowAppliedLabel"), value: profile.narrow.applied }),
|
|
1000
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("narrowDroppedLabel"), value: profile.narrow.droppedSum }),
|
|
1001
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("failures"), value: profile.narrow.error, tone: "muted" })
|
|
1002
|
+
] })
|
|
1003
|
+
] }),
|
|
1004
|
+
profile.complete.attempts > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("article", { className: "decision-card", children: [
|
|
1005
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { className: "decision-card-head", children: [
|
|
1006
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "decision-card-title", children: t("cardComplete") }),
|
|
1007
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "decision-card-total", children: [
|
|
1008
|
+
profile.complete.attempts,
|
|
1009
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("small", { children: t("cardTimes") })
|
|
1010
|
+
] })
|
|
1011
|
+
] }),
|
|
1012
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-card-stats", children: [
|
|
1013
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("completeSatisfied"), value: profile.complete.satisfied }),
|
|
1014
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("completeUnsatisfied"), value: profile.complete.unsatisfied, tone: "warn" }),
|
|
1015
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("completeInsufficient"), value: profile.complete.insufficient, tone: "muted" })
|
|
1016
|
+
] })
|
|
1017
|
+
] })
|
|
1018
|
+
] })
|
|
1019
|
+
] }),
|
|
1020
|
+
backtest && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "decision-analysis-section", children: [
|
|
1021
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: t("analysisTrend") }),
|
|
1022
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "decision-card-stats decision-analysis-trend", children: [
|
|
1023
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("analysisTrendWarn"), value: backtest.warnHits }),
|
|
1024
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("analysisTrendSevere"), value: backtest.severeHits, tone: "warn" }),
|
|
1025
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Stat, { label: t("analysisTrendMaxRun"), value: backtest.maxRun })
|
|
1026
|
+
] }),
|
|
1027
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-muted decision-analysis-note", children: t("analysisTrendNote") })
|
|
1028
|
+
] }),
|
|
1029
|
+
annotations && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "decision-analysis-section", children: [
|
|
1030
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: t("analysisAccuracy") }),
|
|
1031
|
+
annotations.rated === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-muted", children: t("analysisAccuracyEmpty") }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-muted decision-rate-summary", children: t("rateSummary", { rated: annotations.rated, good: annotations.good, bad: annotations.bad, unsure: annotations.unsure }) })
|
|
1032
|
+
] }),
|
|
1033
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: "decision-analysis-section", children: [
|
|
1034
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("h3", { children: t("log") }),
|
|
1035
|
+
log.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-empty", role: "status", children: t("analysisLogTruncated") }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
1036
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DecisionLogList, { entries: log, ratings, onRate: (id, rating) => void annotate(id, rating), t }),
|
|
1037
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "decision-muted decision-analysis-note", children: t("analysisLogNote") })
|
|
1038
|
+
] })
|
|
1039
|
+
] })
|
|
1040
|
+
] }),
|
|
1041
|
+
message && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { role: "status", className: "decision-message decision-message-warn", children: message })
|
|
1042
|
+
] }) });
|
|
1043
|
+
}
|
|
1044
|
+
|
|
683
1045
|
// src/client/config-form.tsx
|
|
684
|
-
var
|
|
685
|
-
var
|
|
686
|
-
var
|
|
1046
|
+
var import_react4 = require("react");
|
|
1047
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1048
|
+
var api = "/plugins/dsh-decision-layer/api";
|
|
687
1049
|
async function request2(path, fetchFn, init) {
|
|
688
|
-
const response = await fetchFn(`${
|
|
1050
|
+
const response = await fetchFn(`${api}/${path}`, init);
|
|
689
1051
|
const body = await response.json();
|
|
690
1052
|
if (!response.ok || !body.ok || body.value === void 0) throw new Error(body.error || "Request failed");
|
|
691
1053
|
return body.value;
|
|
@@ -699,31 +1061,31 @@ var maxCandidatesOf = (settings) => settings?.maxCandidates ?? DEFAULT_MAX_CANDI
|
|
|
699
1061
|
var thresholdOf = (settings) => settings?.threshold ?? DEFAULT_THRESHOLD;
|
|
700
1062
|
function HeadIcon({ name }) {
|
|
701
1063
|
const glyphs = {
|
|
702
|
-
features: /* @__PURE__ */ (0,
|
|
703
|
-
/* @__PURE__ */ (0,
|
|
704
|
-
/* @__PURE__ */ (0,
|
|
705
|
-
/* @__PURE__ */ (0,
|
|
706
|
-
/* @__PURE__ */ (0,
|
|
1064
|
+
features: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
1065
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "2", y: "3", width: "12", height: "4.2", rx: "2.1", stroke: "currentColor", "stroke-width": "1.3" }),
|
|
1066
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "11.9", cy: "5.1", r: "1.25", fill: "currentColor" }),
|
|
1067
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "2", y: "8.8", width: "12", height: "4.2", rx: "2.1", stroke: "currentColor", "stroke-width": "1.3" }),
|
|
1068
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "4.1", cy: "10.9", r: "1.25", fill: "currentColor" })
|
|
707
1069
|
] }),
|
|
708
|
-
advanced: /* @__PURE__ */ (0,
|
|
709
|
-
/* @__PURE__ */ (0,
|
|
710
|
-
/* @__PURE__ */ (0,
|
|
711
|
-
/* @__PURE__ */ (0,
|
|
1070
|
+
advanced: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
1071
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M2.5 5h11M2.5 11h11", stroke: "currentColor", "stroke-width": "1.3", "stroke-linecap": "round" }),
|
|
1072
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "10.5", cy: "5", r: "1.9", fill: "var(--dsw-alias-bg-layer-2,#fff)", stroke: "currentColor", "stroke-width": "1.3" }),
|
|
1073
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "5.5", cy: "11", r: "1.9", fill: "var(--dsw-alias-bg-layer-2,#fff)", stroke: "currentColor", "stroke-width": "1.3" })
|
|
712
1074
|
] }),
|
|
713
|
-
backend: /* @__PURE__ */ (0,
|
|
714
|
-
/* @__PURE__ */ (0,
|
|
715
|
-
/* @__PURE__ */ (0,
|
|
716
|
-
/* @__PURE__ */ (0,
|
|
717
|
-
/* @__PURE__ */ (0,
|
|
1075
|
+
backend: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
1076
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "2.3", y: "2.6", width: "11.4", height: "4.6", rx: "1.1", stroke: "currentColor", "stroke-width": "1.3" }),
|
|
1077
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("rect", { x: "2.3", y: "8.8", width: "11.4", height: "4.6", rx: "1.1", stroke: "currentColor", "stroke-width": "1.3" }),
|
|
1078
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "5", cy: "4.9", r: ".7", fill: "currentColor" }),
|
|
1079
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: "5", cy: "11.1", r: ".7", fill: "currentColor" })
|
|
718
1080
|
] })
|
|
719
1081
|
};
|
|
720
|
-
return /* @__PURE__ */ (0,
|
|
1082
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `decision-head-icon decision-head-icon-${name}`, "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { width: "15", height: "15", viewBox: "0 0 16 16", fill: "none", children: glyphs[name] }) });
|
|
721
1083
|
}
|
|
722
1084
|
function Toggle({ label, checked, disabled, onChange }) {
|
|
723
|
-
return /* @__PURE__ */ (0,
|
|
724
|
-
/* @__PURE__ */ (0,
|
|
725
|
-
/* @__PURE__ */ (0,
|
|
726
|
-
/* @__PURE__ */ (0,
|
|
1085
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-toggle", children: [
|
|
1086
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-toggle-text", children: label }),
|
|
1087
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "decision-toggle-box", children: [
|
|
1088
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
727
1089
|
"input",
|
|
728
1090
|
{
|
|
729
1091
|
className: "decision-toggle-input",
|
|
@@ -733,42 +1095,46 @@ function Toggle({ label, checked, disabled, onChange }) {
|
|
|
733
1095
|
onChange: (event) => onChange(event.target.checked)
|
|
734
1096
|
}
|
|
735
1097
|
),
|
|
736
|
-
/* @__PURE__ */ (0,
|
|
1098
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-toggle-track", "aria-hidden": "true" })
|
|
737
1099
|
] })
|
|
738
1100
|
] });
|
|
739
1101
|
}
|
|
740
1102
|
function ConfigForm({ t, fetchFn = fetch }) {
|
|
741
|
-
const [config, setConfig] = (0,
|
|
742
|
-
const [checkMode, setCheckMode] = (0,
|
|
743
|
-
const [narrowMode, setNarrowMode] = (0,
|
|
744
|
-
const [keepPrefixes, setKeepPrefixes] = (0,
|
|
745
|
-
const [maxCandidates, setMaxCandidates] = (0,
|
|
746
|
-
const [threshold, setThreshold] = (0,
|
|
747
|
-
const [gateOn, setGateOn] = (0,
|
|
748
|
-
const [checkOn, setCheckOn] = (0,
|
|
749
|
-
const [narrowOn, setNarrowOn] = (0,
|
|
750
|
-
const [
|
|
751
|
-
const [
|
|
752
|
-
const [
|
|
753
|
-
const [
|
|
754
|
-
const [
|
|
755
|
-
const [
|
|
756
|
-
const [
|
|
1103
|
+
const [config, setConfig] = (0, import_react4.useState)({ url: "", model: "", apiKeySet: false, httpApprovedUrl: "", effectiveUrl: "https://api.typesafe.ai" });
|
|
1104
|
+
const [checkMode, setCheckMode] = (0, import_react4.useState)("observe");
|
|
1105
|
+
const [narrowMode, setNarrowMode] = (0, import_react4.useState)("enforce");
|
|
1106
|
+
const [keepPrefixes, setKeepPrefixes] = (0, import_react4.useState)(DEFAULT_KEEP_PREFIXES.join("\n"));
|
|
1107
|
+
const [maxCandidates, setMaxCandidates] = (0, import_react4.useState)(String(DEFAULT_MAX_CANDIDATES));
|
|
1108
|
+
const [threshold, setThreshold] = (0, import_react4.useState)(String(DEFAULT_THRESHOLD));
|
|
1109
|
+
const [gateOn, setGateOn] = (0, import_react4.useState)(true);
|
|
1110
|
+
const [checkOn, setCheckOn] = (0, import_react4.useState)(true);
|
|
1111
|
+
const [narrowOn, setNarrowOn] = (0, import_react4.useState)(true);
|
|
1112
|
+
const [completeOn, setCompleteOn] = (0, import_react4.useState)(true);
|
|
1113
|
+
const [completeMode, setCompleteMode] = (0, import_react4.useState)("observe");
|
|
1114
|
+
const [key, setKey] = (0, import_react4.useState)("");
|
|
1115
|
+
const [busy, setBusy] = (0, import_react4.useState)(false);
|
|
1116
|
+
const [advancedDirty, setAdvancedDirty] = (0, import_react4.useState)(false);
|
|
1117
|
+
const [backendDirty, setBackendDirty] = (0, import_react4.useState)(false);
|
|
1118
|
+
const [ready, setReady] = (0, import_react4.useState)(false);
|
|
1119
|
+
const [message, setMessage] = (0, import_react4.useState)(null);
|
|
1120
|
+
const [probeResult, setProbeResult] = (0, import_react4.useState)(null);
|
|
757
1121
|
const applyFeatures = (features) => {
|
|
758
1122
|
setGateOn(features?.gate !== false);
|
|
759
1123
|
setCheckOn(features?.check !== false);
|
|
760
1124
|
setNarrowOn(features?.narrow !== false);
|
|
1125
|
+
setCompleteOn(features?.complete !== false);
|
|
761
1126
|
};
|
|
762
1127
|
const applyConfig = (value) => {
|
|
763
1128
|
setConfig(value);
|
|
764
1129
|
setCheckMode(value.checkSettings?.mode ?? "observe");
|
|
765
1130
|
setNarrowMode(value.narrowSettings?.mode ?? "enforce");
|
|
1131
|
+
setCompleteMode(value.completeSettings?.mode ?? "observe");
|
|
766
1132
|
setKeepPrefixes(prefixesText(value.narrowSettings));
|
|
767
1133
|
setMaxCandidates(String(maxCandidatesOf(value.narrowSettings)));
|
|
768
1134
|
setThreshold(String(thresholdOf(value.narrowSettings)));
|
|
769
1135
|
applyFeatures(value.features);
|
|
770
1136
|
};
|
|
771
|
-
(0,
|
|
1137
|
+
(0, import_react4.useEffect)(() => {
|
|
772
1138
|
let live = true;
|
|
773
1139
|
setMessage(null);
|
|
774
1140
|
setReady(false);
|
|
@@ -800,7 +1166,8 @@ function ConfigForm({ t, fetchFn = fetch }) {
|
|
|
800
1166
|
body: JSON.stringify({
|
|
801
1167
|
checkSettings: { mode: checkMode },
|
|
802
1168
|
narrowSettings: { mode: narrowMode, keepPrefixes: parsePrefixes(keepPrefixes), maxCandidates: cap, threshold: thresh },
|
|
803
|
-
|
|
1169
|
+
completeSettings: { mode: completeMode },
|
|
1170
|
+
features: { gate: gateOn, check: checkOn, narrow: narrowOn, complete: completeOn }
|
|
804
1171
|
})
|
|
805
1172
|
});
|
|
806
1173
|
applyConfig(value);
|
|
@@ -870,17 +1237,17 @@ ${url}`)) return;
|
|
|
870
1237
|
setBusy(false);
|
|
871
1238
|
}
|
|
872
1239
|
};
|
|
873
|
-
return /* @__PURE__ */ (0,
|
|
874
|
-
/* @__PURE__ */ (0,
|
|
875
|
-
/* @__PURE__ */ (0,
|
|
876
|
-
/* @__PURE__ */ (0,
|
|
877
|
-
/* @__PURE__ */ (0,
|
|
1240
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-config", children: [
|
|
1241
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("details", { className: "decision-collapse", open: true, children: [
|
|
1242
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("summary", { className: "decision-collapse-head", children: [
|
|
1243
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("h3", { children: [
|
|
1244
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HeadIcon, { name: "features" }),
|
|
878
1245
|
t("basicFeatures")
|
|
879
1246
|
] }),
|
|
880
|
-
/* @__PURE__ */ (0,
|
|
1247
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-collapse-icon", "aria-hidden": "true" })
|
|
881
1248
|
] }),
|
|
882
|
-
/* @__PURE__ */ (0,
|
|
883
|
-
/* @__PURE__ */ (0,
|
|
1249
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-collapse-body", children: [
|
|
1250
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
884
1251
|
Toggle,
|
|
885
1252
|
{
|
|
886
1253
|
label: t("featureGate"),
|
|
@@ -892,8 +1259,8 @@ ${url}`)) return;
|
|
|
892
1259
|
}
|
|
893
1260
|
}
|
|
894
1261
|
),
|
|
895
|
-
/* @__PURE__ */ (0,
|
|
896
|
-
/* @__PURE__ */ (0,
|
|
1262
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("featureGateHint") }),
|
|
1263
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
897
1264
|
Toggle,
|
|
898
1265
|
{
|
|
899
1266
|
label: t("featureCheck"),
|
|
@@ -905,8 +1272,8 @@ ${url}`)) return;
|
|
|
905
1272
|
}
|
|
906
1273
|
}
|
|
907
1274
|
),
|
|
908
|
-
/* @__PURE__ */ (0,
|
|
909
|
-
/* @__PURE__ */ (0,
|
|
1275
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("featureCheckHint") }),
|
|
1276
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
910
1277
|
Toggle,
|
|
911
1278
|
{
|
|
912
1279
|
label: t("featureNarrow"),
|
|
@@ -918,22 +1285,35 @@ ${url}`)) return;
|
|
|
918
1285
|
}
|
|
919
1286
|
}
|
|
920
1287
|
),
|
|
921
|
-
/* @__PURE__ */ (0,
|
|
1288
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("featureNarrowHint") }),
|
|
1289
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1290
|
+
Toggle,
|
|
1291
|
+
{
|
|
1292
|
+
label: t("featureComplete"),
|
|
1293
|
+
checked: completeOn,
|
|
1294
|
+
disabled: !ready,
|
|
1295
|
+
onChange: (next) => {
|
|
1296
|
+
setCompleteOn(next);
|
|
1297
|
+
setAdvancedDirty(true);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
),
|
|
1301
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("featureCompleteHint") })
|
|
922
1302
|
] })
|
|
923
1303
|
] }),
|
|
924
|
-
/* @__PURE__ */ (0,
|
|
925
|
-
/* @__PURE__ */ (0,
|
|
926
|
-
/* @__PURE__ */ (0,
|
|
927
|
-
/* @__PURE__ */ (0,
|
|
1304
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("details", { className: "decision-collapse", children: [
|
|
1305
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("summary", { className: "decision-collapse-head", children: [
|
|
1306
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("h3", { children: [
|
|
1307
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HeadIcon, { name: "advanced" }),
|
|
928
1308
|
t("advancedConfig")
|
|
929
1309
|
] }),
|
|
930
|
-
/* @__PURE__ */ (0,
|
|
1310
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-collapse-icon", "aria-hidden": "true" })
|
|
931
1311
|
] }),
|
|
932
|
-
/* @__PURE__ */ (0,
|
|
933
|
-
/* @__PURE__ */ (0,
|
|
934
|
-
/* @__PURE__ */ (0,
|
|
935
|
-
/* @__PURE__ */ (0,
|
|
936
|
-
/* @__PURE__ */ (0,
|
|
1312
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("form", { className: "decision-collapse-body", onSubmit: (event) => void saveAdvanced(event), children: [
|
|
1313
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-advanced-grid", children: [
|
|
1314
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-field-block", children: [
|
|
1315
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-subhead", children: t("advancedSwitches") }),
|
|
1316
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
937
1317
|
Toggle,
|
|
938
1318
|
{
|
|
939
1319
|
label: t("selfCheckSteer"),
|
|
@@ -945,8 +1325,8 @@ ${url}`)) return;
|
|
|
945
1325
|
}
|
|
946
1326
|
}
|
|
947
1327
|
),
|
|
948
|
-
/* @__PURE__ */ (0,
|
|
949
|
-
/* @__PURE__ */ (0,
|
|
1328
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("selfCheckHint") }),
|
|
1329
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
950
1330
|
Toggle,
|
|
951
1331
|
{
|
|
952
1332
|
label: t("narrowObserveLabel"),
|
|
@@ -958,13 +1338,26 @@ ${url}`)) return;
|
|
|
958
1338
|
}
|
|
959
1339
|
}
|
|
960
1340
|
),
|
|
961
|
-
/* @__PURE__ */ (0,
|
|
1341
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("narrowHint") }),
|
|
1342
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1343
|
+
Toggle,
|
|
1344
|
+
{
|
|
1345
|
+
label: t("completeSteer"),
|
|
1346
|
+
checked: completeMode === "steer",
|
|
1347
|
+
disabled: !ready || !completeOn,
|
|
1348
|
+
onChange: (next) => {
|
|
1349
|
+
setCompleteMode(next ? "steer" : "observe");
|
|
1350
|
+
setAdvancedDirty(true);
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
),
|
|
1354
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "decision-muted", children: t("completeHint") })
|
|
962
1355
|
] }),
|
|
963
|
-
/* @__PURE__ */ (0,
|
|
964
|
-
/* @__PURE__ */ (0,
|
|
965
|
-
/* @__PURE__ */ (0,
|
|
1356
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-field-block", children: [
|
|
1357
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-subhead", children: t("advancedParams") }),
|
|
1358
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-field", children: [
|
|
966
1359
|
t("narrowKeepLabel"),
|
|
967
|
-
/* @__PURE__ */ (0,
|
|
1360
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
968
1361
|
"textarea",
|
|
969
1362
|
{
|
|
970
1363
|
className: "decision-prefixes",
|
|
@@ -978,11 +1371,11 @@ ${url}`)) return;
|
|
|
978
1371
|
}
|
|
979
1372
|
}
|
|
980
1373
|
),
|
|
981
|
-
/* @__PURE__ */ (0,
|
|
1374
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("small", { className: "decision-muted", children: t("narrowKeepHint") })
|
|
982
1375
|
] }),
|
|
983
|
-
/* @__PURE__ */ (0,
|
|
1376
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-field decision-field-inline", children: [
|
|
984
1377
|
t("narrowMaxLabel"),
|
|
985
|
-
/* @__PURE__ */ (0,
|
|
1378
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
986
1379
|
"input",
|
|
987
1380
|
{
|
|
988
1381
|
className: "decision-narrow-cap",
|
|
@@ -999,11 +1392,11 @@ ${url}`)) return;
|
|
|
999
1392
|
}
|
|
1000
1393
|
}
|
|
1001
1394
|
),
|
|
1002
|
-
/* @__PURE__ */ (0,
|
|
1395
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("small", { className: "decision-muted", children: t("narrowMaxHint") })
|
|
1003
1396
|
] }),
|
|
1004
|
-
/* @__PURE__ */ (0,
|
|
1397
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-field decision-field-inline", children: [
|
|
1005
1398
|
t("narrowThresholdLabel"),
|
|
1006
|
-
/* @__PURE__ */ (0,
|
|
1399
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1007
1400
|
"input",
|
|
1008
1401
|
{
|
|
1009
1402
|
className: "decision-narrow-cap",
|
|
@@ -1020,26 +1413,26 @@ ${url}`)) return;
|
|
|
1020
1413
|
}
|
|
1021
1414
|
}
|
|
1022
1415
|
),
|
|
1023
|
-
/* @__PURE__ */ (0,
|
|
1416
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("small", { className: "decision-muted", children: t("narrowThresholdHint") })
|
|
1024
1417
|
] })
|
|
1025
1418
|
] })
|
|
1026
1419
|
] }),
|
|
1027
|
-
/* @__PURE__ */ (0,
|
|
1420
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "decision-actions", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { disabled: busy || !ready || !advancedDirty, type: "submit", children: t("saveAdvanced") }) })
|
|
1028
1421
|
] })
|
|
1029
1422
|
] }),
|
|
1030
|
-
/* @__PURE__ */ (0,
|
|
1031
|
-
/* @__PURE__ */ (0,
|
|
1032
|
-
/* @__PURE__ */ (0,
|
|
1423
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { children: [
|
|
1424
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("h3", { children: [
|
|
1425
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(HeadIcon, { name: "backend" }),
|
|
1033
1426
|
t("backend")
|
|
1034
1427
|
] }),
|
|
1035
|
-
ready && /* @__PURE__ */ (0,
|
|
1428
|
+
ready && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "decision-muted decision-destination", children: [
|
|
1036
1429
|
t("destination"),
|
|
1037
|
-
/* @__PURE__ */ (0,
|
|
1430
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("code", { children: config.effectiveUrl })
|
|
1038
1431
|
] }),
|
|
1039
|
-
/* @__PURE__ */ (0,
|
|
1040
|
-
/* @__PURE__ */ (0,
|
|
1432
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("form", { onSubmit: (event) => void saveBackend(event), children: [
|
|
1433
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-field", children: [
|
|
1041
1434
|
t("url"),
|
|
1042
|
-
/* @__PURE__ */ (0,
|
|
1435
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1043
1436
|
"input",
|
|
1044
1437
|
{
|
|
1045
1438
|
type: "url",
|
|
@@ -1054,9 +1447,9 @@ ${url}`)) return;
|
|
|
1054
1447
|
}
|
|
1055
1448
|
)
|
|
1056
1449
|
] }),
|
|
1057
|
-
/* @__PURE__ */ (0,
|
|
1450
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-field", children: [
|
|
1058
1451
|
t("model"),
|
|
1059
|
-
/* @__PURE__ */ (0,
|
|
1452
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1060
1453
|
"input",
|
|
1061
1454
|
{
|
|
1062
1455
|
value: config.model,
|
|
@@ -1070,9 +1463,9 @@ ${url}`)) return;
|
|
|
1070
1463
|
}
|
|
1071
1464
|
)
|
|
1072
1465
|
] }),
|
|
1073
|
-
/* @__PURE__ */ (0,
|
|
1466
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "decision-field", children: [
|
|
1074
1467
|
t("key"),
|
|
1075
|
-
/* @__PURE__ */ (0,
|
|
1468
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1076
1469
|
"input",
|
|
1077
1470
|
{
|
|
1078
1471
|
type: "password",
|
|
@@ -1087,23 +1480,23 @@ ${url}`)) return;
|
|
|
1087
1480
|
}
|
|
1088
1481
|
}
|
|
1089
1482
|
),
|
|
1090
|
-
/* @__PURE__ */ (0,
|
|
1483
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("small", { className: "decision-muted", children: config.apiKeySet ? t("keyHintSaved") : t("keyHintEmpty") })
|
|
1091
1484
|
] }),
|
|
1092
|
-
/* @__PURE__ */ (0,
|
|
1093
|
-
/* @__PURE__ */ (0,
|
|
1094
|
-
/* @__PURE__ */ (0,
|
|
1095
|
-
probeResult && /* @__PURE__ */ (0,
|
|
1485
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-actions", children: [
|
|
1486
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { disabled: busy || !ready, type: "submit", children: t("save") }),
|
|
1487
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "decision-secondary", disabled: busy || !ready || backendDirty, type: "button", onClick: () => void probe(), children: t("probe") }),
|
|
1488
|
+
probeResult && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { role: "status", className: `decision-probe-result${probeResult.ok ? " decision-message-ok" : " decision-message-warn"}`, children: probeResult.text })
|
|
1096
1489
|
] })
|
|
1097
1490
|
] }),
|
|
1098
|
-
config.apiKeySet && /* @__PURE__ */ (0,
|
|
1099
|
-
/* @__PURE__ */ (0,
|
|
1100
|
-
/* @__PURE__ */ (0,
|
|
1101
|
-
/* @__PURE__ */ (0,
|
|
1491
|
+
config.apiKeySet && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-danger-row", children: [
|
|
1492
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "decision-danger-text", children: [
|
|
1493
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "decision-danger-title", children: t("removeKey") }),
|
|
1494
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("small", { className: "decision-muted", children: t("removeKeyHint") })
|
|
1102
1495
|
] }),
|
|
1103
|
-
/* @__PURE__ */ (0,
|
|
1496
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", className: "decision-danger-button", disabled: busy || !ready, onClick: () => void removeSavedKey(), children: t("removeKeyButton") })
|
|
1104
1497
|
] })
|
|
1105
1498
|
] }),
|
|
1106
|
-
message && /* @__PURE__ */ (0,
|
|
1499
|
+
message && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { role: "status", className: `decision-message${message.ok ? " decision-message-ok" : " decision-message-warn"}`, children: message.text })
|
|
1107
1500
|
] });
|
|
1108
1501
|
}
|
|
1109
1502
|
|
|
@@ -1112,6 +1505,16 @@ var css = `
|
|
|
1112
1505
|
.decision-trigger{position:relative;display:inline-flex;align-items:center;gap:6px;min-width:0;max-width:14rem;border:none;border-radius:var(--dsw-radius-sm,6px);background:0 0;color:var(--dsw-alias-label-tertiary,#77818b);padding:1px 8px;font:inherit;font-size:calc(var(--dsh-content-font-size-secondary,13px) - 1px);font-weight:400;line-height:calc(20px + var(--dsh-content-font-delta-secondary,0px));font-variant-numeric:tabular-nums;white-space:nowrap;cursor:pointer}
|
|
1113
1506
|
.decision-trigger:hover,.decision-trigger[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover,#eef1f3);color:var(--dsw-alias-label-secondary,#59636e)}
|
|
1114
1507
|
.decision-trigger-icon{flex:none;color:inherit}
|
|
1508
|
+
/* Quality-trend severity on the radar-triangle icon. Only the icon is tinted, via
|
|
1509
|
+
a CSS variable so light/dark are one pair of overrides. Multimodal: the tooltip
|
|
1510
|
+
text and the icon shape carry the meaning too, not color alone. First version
|
|
1511
|
+
is color-only; a pulse animation is added later behind prefers-reduced-motion. */
|
|
1512
|
+
.decision-trend-warn .decision-trigger-icon{color:var(--decision-trend-warn,#c98a1e)}
|
|
1513
|
+
.decision-trend-severe .decision-trigger-icon{color:var(--decision-trend-severe,#c0392b)}
|
|
1514
|
+
@media (prefers-color-scheme:dark){
|
|
1515
|
+
.decision-trend-warn .decision-trigger-icon{color:var(--decision-trend-warn,#e0a83e)}
|
|
1516
|
+
.decision-trend-severe .decision-trigger-icon{color:var(--decision-trend-severe,#f0645a)}
|
|
1517
|
+
}
|
|
1115
1518
|
.decision-trigger-text{min-width:0;flex:0 1 auto;text-overflow:ellipsis;white-space:nowrap;overflow:hidden}
|
|
1116
1519
|
.decision-trigger-text b{color:inherit;font-weight:500;font-variant-numeric:tabular-nums}
|
|
1117
1520
|
.decision-trigger:focus-visible,.decision-dialog button:focus-visible,.decision-dialog input:focus-visible,.decision-config input:focus-visible,.decision-config button:focus-visible{outline:2px solid #1f5d50;outline-offset:2px}
|
|
@@ -1163,6 +1566,14 @@ var css = `
|
|
|
1163
1566
|
.decision-log-item.decision-log-ok{border-left-color:#1f8a70}
|
|
1164
1567
|
.decision-log-time{color:var(--dsw-alias-label-tertiary,#77818b);font-variant-numeric:tabular-nums;flex:0 0 auto}
|
|
1165
1568
|
.decision-log-body{color:var(--dsw-alias-label-primary,#202124);flex:1 1 auto;min-width:0}
|
|
1569
|
+
.decision-rate{flex:0 0 auto;display:inline-flex;gap:1px;align-self:center}
|
|
1570
|
+
.decision-rate-btn{display:inline-flex;align-items:center;justify-content:center;width:1.55rem;height:1.55rem;border:1px solid transparent;background:0 0;border-radius:7px;padding:0;cursor:pointer;color:var(--dsw-alias-label-tertiary,#9aa5b1);transition:color .12s,background .12s}
|
|
1571
|
+
.decision-rate-btn:hover{color:var(--dsw-alias-label-secondary,#59636e);background:var(--dsw-alias-interactive-bg-hover,#eef1f3)}
|
|
1572
|
+
.decision-rate-btn.decision-rate-on{background:var(--dsw-alias-interactive-bg-hover,#eef1f3)}
|
|
1573
|
+
.decision-rate-btn.decision-rate-good{color:#1f8a70}
|
|
1574
|
+
.decision-rate-btn.decision-rate-bad{color:#c0392b}
|
|
1575
|
+
.decision-rate-btn.decision-rate-unsure{color:#c98a1e}
|
|
1576
|
+
.decision-rate-summary{margin-top:.5rem}
|
|
1166
1577
|
.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)}
|
|
1167
1578
|
.decision-log-verdict{font-weight:600}
|
|
1168
1579
|
.decision-log-deny .decision-log-verdict{color:#c0392b}
|
|
@@ -1252,6 +1663,22 @@ var css = `
|
|
|
1252
1663
|
.decision-message{font-size:.82rem;margin:.2rem 0 0}
|
|
1253
1664
|
.decision-message-ok{color:#1f6f57}
|
|
1254
1665
|
.decision-message-warn{color:#9d4b1f}
|
|
1666
|
+
/* Conversation-view "Decision analysis" tab. It reuses the card / stat / log
|
|
1667
|
+
primitives above; this block only supplies the scroll container and section
|
|
1668
|
+
rhythm, since the tab is not inside the modal's .decision-body wrapper. */
|
|
1669
|
+
.decision-analysis{height:100%;overflow:auto;color:var(--dsw-alias-label-primary,#202124);font:inherit}
|
|
1670
|
+
.decision-analysis-inner{max-width:52rem;margin:0 auto;padding:1.4rem 1.5rem 3rem}
|
|
1671
|
+
.decision-analysis-title{font-size:1.15rem;margin:0 0 .3rem}
|
|
1672
|
+
.decision-analysis-intro{margin:0 0 .4rem}
|
|
1673
|
+
.decision-analysis-section{border-top:1px solid var(--dsw-alias-border-l2,#eceff1);margin-top:1.1rem;padding-top:1rem}
|
|
1674
|
+
.decision-analysis-section>h3{font-size:.92rem;margin:0 0 .6rem}
|
|
1675
|
+
.decision-analysis-cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(13rem,1fr));gap:.7rem}
|
|
1676
|
+
.decision-analysis-cards .decision-card-stats{flex-wrap:wrap}
|
|
1677
|
+
.decision-analysis-trend{max-width:26rem}
|
|
1678
|
+
.decision-analysis-note{margin:.5rem 0 0}
|
|
1679
|
+
@media (prefers-color-scheme:dark){
|
|
1680
|
+
.decision-analysis{color:var(--dsw-alias-label-primary,#e6eaee)}
|
|
1681
|
+
}
|
|
1255
1682
|
`;
|
|
1256
1683
|
function installStyles() {
|
|
1257
1684
|
if (typeof document === "undefined" || document.querySelector('style[data-plugin="dsh-decision-layer"]')) return () => {
|
|
@@ -1264,9 +1691,10 @@ function installStyles() {
|
|
|
1264
1691
|
}
|
|
1265
1692
|
|
|
1266
1693
|
// src/client/index.tsx
|
|
1267
|
-
var
|
|
1694
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1268
1695
|
var inject = ["slots", "locale"];
|
|
1269
1696
|
function apply(ctx) {
|
|
1697
|
+
const t = ctx.locale.bind("dsh-decision-layer");
|
|
1270
1698
|
ctx.effect(installStyles);
|
|
1271
1699
|
ctx.effect(() => ctx.locale.register("dsh-decision-layer", dictionaries));
|
|
1272
1700
|
ctx.slots.inject("conversation.composer.dock", () => ctx.slots.register({
|
|
@@ -1277,9 +1705,18 @@ function apply(ctx) {
|
|
|
1277
1705
|
registrant: "dsh-decision-layer",
|
|
1278
1706
|
inject: (sessionId) => ({ sessionId })
|
|
1279
1707
|
}, DecisionPanel));
|
|
1708
|
+
ctx.slots.inject("conversation.view", () => ctx.slots.register({
|
|
1709
|
+
name: "conversation.view",
|
|
1710
|
+
id: "decision-layer-analysis",
|
|
1711
|
+
order: 40,
|
|
1712
|
+
locale: "dsh-decision-layer",
|
|
1713
|
+
registrant: "dsh-decision-layer",
|
|
1714
|
+
label: () => t("analysisTab"),
|
|
1715
|
+
inject: (sessionId) => ({ sessionId })
|
|
1716
|
+
}, AnalysisView));
|
|
1280
1717
|
ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register(
|
|
1281
1718
|
{ name: "plugins.bundle.config", key: "dsh-decision-layer", locale: "dsh-decision-layer" },
|
|
1282
|
-
(props) => props.view === "summary" ? props.t("summary") : /* @__PURE__ */ (0,
|
|
1719
|
+
(props) => props.view === "summary" ? props.t("summary") : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ConfigForm, { t: props.t })
|
|
1283
1720
|
));
|
|
1284
1721
|
}
|
|
1285
1722
|
|