dsh-harbor-evolution 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -7
- package/index.js +59 -1
- package/lib/candidate.js +29 -2
- package/lib/client.js +189 -263
- package/lib/dashboard.js +182 -59
- package/lib/evolution.js +105 -26
- package/lib/service.js +39 -2
- package/lib/web.js +28 -15
- package/package.json +2 -1
- package/skills/evolve-agent-with-harbor/SKILL.md +105 -78
- package/skills/evolve-agent-with-harbor/references/initialization.md +89 -83
package/lib/client.js
CHANGED
|
@@ -48,220 +48,154 @@ var harbor_ocean_default = "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4
|
|
|
48
48
|
|
|
49
49
|
// src/client/index.jsx
|
|
50
50
|
var NS = "harbor-evolution";
|
|
51
|
-
var
|
|
51
|
+
var API = "/_dsh/harbor-evolution";
|
|
52
52
|
var dictionaries = {
|
|
53
53
|
zh: {
|
|
54
54
|
tab: "Harbor",
|
|
55
55
|
settings: "Harbor \u81EA\u8FDB\u5316",
|
|
56
|
-
eyebrow: "
|
|
57
|
-
heroTitle: "\
|
|
58
|
-
heroBody: "
|
|
56
|
+
eyebrow: "EVALUATION WORKBENCH",
|
|
57
|
+
heroTitle: "\u7A33\u5B9A\u5730\u770B\u89C1 Agent \u662F\u5426\u771F\u7684\u8FDB\u6B65",
|
|
58
|
+
heroBody: "Evaluation Stack \u56FA\u5B9A\u8BC4\u6D4B\u542B\u4E49\uFF0CContext v2 \u56FA\u5B9A\u53EF\u6BD4\u8F83\u6027\uFF0CPromotion Gate \u53EA\u63A5\u53D7\u6709\u8BC1\u636E\u4E14\u65E0\u56DE\u5F52\u7684 Candidate\u3002",
|
|
59
59
|
refresh: "\u5237\u65B0",
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
jobsHint: "\u6700\u591A\u5C55\u793A\u6700\u8FD1 50 \u4E2A\u6279\u6B21\u3002\u7ED3\u679C\u76F4\u63A5\u8BFB\u53D6\u7A33\u5B9A\u7684 evaluation-summary.json\u3002",
|
|
68
|
-
emptyTitle: "\u8FD8\u6CA1\u6709\u8BC4\u6D4B\u7ED3\u679C",
|
|
69
|
-
emptyBody: "\u8BA9 Agent \u8C03\u7528\u5B98\u65B9 Skill\uFF0C\u5148\u6F84\u6E05 Candidate\u3001Dataset \u4E0E Promotion Policy\uFF0C\u518D\u542F\u52A8\u7B2C\u4E00\u4E2A Job\u3002",
|
|
70
|
-
startStep1: "1. \u56FA\u5316 Candidate",
|
|
71
|
-
startStep2: "2. \u8FD0\u884C Harbor Job",
|
|
72
|
-
startStep3: "3. \u6BD4\u8F83\u5E76\u51B3\u5B9A\u664B\u7EA7",
|
|
60
|
+
jobs: "\u8BC4\u6D4B\u6279\u6B21",
|
|
61
|
+
jobsHint: "\u8F7B\u91CF\u603B\u89C8\uFF1B\u70B9\u51FB Job \u6253\u5F00\u5B8C\u6574\u8BC4\u6D4B\u5DE5\u4F5C\u53F0\u3002",
|
|
62
|
+
empty: "\u8FD8\u6CA1\u6709 Context v2 Job\u3002\u5148\u8C03\u7528\u5B98\u65B9 Skill \u5B8C\u6210\u67B6\u6784\u6F84\u6E05\u548C\u521D\u59CB\u5316\u3002",
|
|
63
|
+
completed: "\u5B8C\u6210",
|
|
64
|
+
partial: "\u5E26\u5F02\u5E38",
|
|
65
|
+
failed: "\u5931\u8D25",
|
|
66
|
+
pending: "\u8FD0\u884C\u4E2D",
|
|
73
67
|
candidate: "Candidate",
|
|
74
|
-
context: "
|
|
68
|
+
context: "Context v2",
|
|
75
69
|
trials: "Trials",
|
|
76
|
-
exceptions: "
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
toolRun: "\u8FD0\u884C Harbor \u8BC4\u6D4B",
|
|
104
|
-
toolResult: "\u8BFB\u53D6\u8BC4\u6D4B\u7ED3\u679C",
|
|
105
|
-
toolCompare: "\u6267\u884C Promotion Gate",
|
|
106
|
-
running: "\u6267\u884C\u4E2D",
|
|
107
|
-
toolFailed: "\u5DE5\u5177\u8C03\u7528\u5931\u8D25",
|
|
108
|
-
noResult: "\u6CA1\u6709\u53EF\u89E3\u6790\u7684\u7ED3\u6784\u5316\u7ED3\u679C",
|
|
109
|
-
digest: "Digest",
|
|
70
|
+
exceptions: "\u5F02\u5E38",
|
|
71
|
+
mode: "\u6A21\u5F0F",
|
|
72
|
+
result: "\u7ED3\u679C\u6458\u8981",
|
|
73
|
+
process: "\u8BC4\u6D4B\u8FC7\u7A0B",
|
|
74
|
+
contract: "\u8BC4\u6D4B\u5951\u7EA6",
|
|
75
|
+
stack: "Evaluation Stack",
|
|
76
|
+
dataset: "Dataset",
|
|
77
|
+
assessments: "Trial \u8BC4\u6D4B",
|
|
78
|
+
reasons: "\u539F\u56E0\u4E0E\u8BC1\u636E",
|
|
79
|
+
optimization: "\u4F18\u5316\u5EFA\u8BAE",
|
|
80
|
+
promotion: "\u664B\u7EA7\u51B3\u7B56",
|
|
81
|
+
audit: "\u5BA1\u8BA1\u4EA7\u7269",
|
|
82
|
+
close: "\u5173\u95ED",
|
|
83
|
+
search: "\u641C\u7D22 Trial",
|
|
84
|
+
all: "\u5168\u90E8",
|
|
85
|
+
assessed: "\u5DF2\u8BC4\u6D4B",
|
|
86
|
+
infra: "\u57FA\u7840\u8BBE\u65BD\u5F02\u5E38",
|
|
87
|
+
previous: "\u4E0A\u4E00\u9875",
|
|
88
|
+
next: "\u4E0B\u4E00\u9875",
|
|
89
|
+
noData: "\u6682\u65E0\u4EA7\u7269",
|
|
90
|
+
validation: "\u4EA7\u7269\u6821\u9A8C",
|
|
91
|
+
ready: "\u53EF\u7528\u4E8E\u6B63\u5F0F\u8BC4\u6D4B",
|
|
92
|
+
blocked: "\u5B58\u5728\u963B\u65AD\u9879",
|
|
93
|
+
doctor: "Architecture Doctor",
|
|
94
|
+
primaryMetric: "\u4E3B\u6307\u6807",
|
|
95
|
+
population: "\u6837\u672C\u5206\u5E03",
|
|
96
|
+
component: "\u7EC4\u4EF6",
|
|
110
97
|
version: "\u7248\u672C",
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
98
|
+
digest: "Digest",
|
|
99
|
+
source: "\u8BC1\u636E",
|
|
100
|
+
findings: "Findings",
|
|
101
|
+
output: "\u8F93\u51FA\u4E0E\u4E2D\u95F4\u7ED3\u679C",
|
|
102
|
+
setupDoctor: "\u5B89\u88C5\u4E0E\u67B6\u6784\u68C0\u67E5",
|
|
103
|
+
setupHint: "\u8FD9\u91CC\u4EC5\u5C55\u793A\u72B6\u6001\uFF0C\u4E0D\u4F1A\u4ECE\u6D4F\u89C8\u5668\u6539\u5199 Cordis \u914D\u7F6E\u3002",
|
|
104
|
+
retry: "\u91CD\u8BD5",
|
|
105
|
+
loading: "\u6B63\u5728\u8BFB\u53D6\u2026"
|
|
116
106
|
},
|
|
117
107
|
en: {
|
|
118
108
|
tab: "Harbor",
|
|
119
109
|
settings: "Harbor Evolution",
|
|
120
|
-
eyebrow: "
|
|
121
|
-
heroTitle: "
|
|
122
|
-
heroBody: "
|
|
110
|
+
eyebrow: "EVALUATION WORKBENCH",
|
|
111
|
+
heroTitle: "See whether the Agent actually improved",
|
|
112
|
+
heroBody: "Evaluation Stack fixes meaning, Context v2 fixes comparability, and the Promotion Gate accepts only evidenced, non-regressing Candidates.",
|
|
123
113
|
refresh: "Refresh",
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
jobsHint: "Shows up to 50 recent jobs from stable evaluation-summary.json files.",
|
|
132
|
-
emptyTitle: "No evaluation results yet",
|
|
133
|
-
emptyBody: "Ask the official Skill to clarify the Candidate, Dataset, and Promotion Policy before starting the first Job.",
|
|
134
|
-
startStep1: "1. Freeze Candidate",
|
|
135
|
-
startStep2: "2. Run Harbor Job",
|
|
136
|
-
startStep3: "3. Compare and promote",
|
|
114
|
+
jobs: "Evaluation jobs",
|
|
115
|
+
jobsHint: "Lightweight overview; open a Job for the full workbench.",
|
|
116
|
+
empty: "No Context v2 Jobs yet. Use the official Skill to clarify and initialize the architecture.",
|
|
117
|
+
completed: "Completed",
|
|
118
|
+
partial: "Partial",
|
|
119
|
+
failed: "Failed",
|
|
120
|
+
pending: "Running",
|
|
137
121
|
candidate: "Candidate",
|
|
138
|
-
context: "
|
|
122
|
+
context: "Context v2",
|
|
139
123
|
trials: "Trials",
|
|
140
124
|
exceptions: "Exceptions",
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
toolRun: "Run Harbor evaluation",
|
|
168
|
-
toolResult: "Read evaluation result",
|
|
169
|
-
toolCompare: "Run Promotion Gate",
|
|
170
|
-
running: "Running",
|
|
171
|
-
toolFailed: "Tool call failed",
|
|
172
|
-
noResult: "No structured result could be parsed",
|
|
173
|
-
digest: "Digest",
|
|
125
|
+
mode: "Mode",
|
|
126
|
+
result: "Outcome",
|
|
127
|
+
process: "Evaluation process",
|
|
128
|
+
contract: "Evaluation contract",
|
|
129
|
+
stack: "Evaluation Stack",
|
|
130
|
+
dataset: "Dataset",
|
|
131
|
+
assessments: "Trial assessments",
|
|
132
|
+
reasons: "Reasons and evidence",
|
|
133
|
+
optimization: "Optimization",
|
|
134
|
+
promotion: "Promotion",
|
|
135
|
+
audit: "Audit artifacts",
|
|
136
|
+
close: "Close",
|
|
137
|
+
search: "Search trials",
|
|
138
|
+
all: "All",
|
|
139
|
+
assessed: "Assessed",
|
|
140
|
+
infra: "Infrastructure error",
|
|
141
|
+
previous: "Previous",
|
|
142
|
+
next: "Next",
|
|
143
|
+
noData: "No artifact",
|
|
144
|
+
validation: "Artifact validation",
|
|
145
|
+
ready: "Promotion ready",
|
|
146
|
+
blocked: "Blocked",
|
|
147
|
+
doctor: "Architecture Doctor",
|
|
148
|
+
primaryMetric: "Primary metric",
|
|
149
|
+
population: "Population",
|
|
150
|
+
component: "Component",
|
|
174
151
|
version: "Version",
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
152
|
+
digest: "Digest",
|
|
153
|
+
source: "Evidence",
|
|
154
|
+
findings: "Findings",
|
|
155
|
+
output: "Output and intermediate results",
|
|
156
|
+
setupDoctor: "Installation and architecture checks",
|
|
157
|
+
setupHint: "Read-only status; the browser never rewrites Cordis configuration.",
|
|
158
|
+
retry: "Retry",
|
|
159
|
+
loading: "Loading\u2026"
|
|
180
160
|
}
|
|
181
161
|
};
|
|
182
162
|
var CSS = `
|
|
183
|
-
.hse-root{--
|
|
184
|
-
.hse-
|
|
185
|
-
.hse-
|
|
186
|
-
.hse-
|
|
187
|
-
.hse-
|
|
188
|
-
.hse-
|
|
189
|
-
.hse-
|
|
190
|
-
.hse-
|
|
191
|
-
.hse-
|
|
192
|
-
.hse-
|
|
193
|
-
.hse-refresh{position:absolute;z-index:2;top:24px;right:24px;display:inline-flex;align-items:center;gap:7px;border:1px solid rgba(255,255,255,.3);border-radius:999px;padding:8px 13px;background:rgba(3,21,55,.4);color:#fff;font:inherit;font-size:12px;cursor:pointer;backdrop-filter:blur(10px);transition:.18s ease}
|
|
194
|
-
.hse-refresh:hover{background:rgba(34,112,235,.55);transform:translateY(-1px)}
|
|
195
|
-
.hse-refresh:disabled{opacity:.55;cursor:wait}
|
|
196
|
-
.hse-stats{position:relative;z-index:2;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin-top:30px;max-width:670px}
|
|
197
|
-
.hse-stat{min-height:64px;padding:12px 14px;border:1px solid rgba(255,255,255,.16);border-radius:14px;background:rgba(4,27,65,.42);backdrop-filter:blur(8px)}
|
|
198
|
-
.hse-stat span{display:block;font-size:11px;color:rgba(225,243,255,.68)}
|
|
199
|
-
.hse-stat strong{display:block;margin-top:4px;font-size:22px;line-height:1.1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
200
|
-
.hse-section{margin-top:28px}
|
|
201
|
-
.hse-section-head{display:flex;align-items:flex-end;justify-content:space-between;gap:20px;margin-bottom:13px}
|
|
202
|
-
.hse-section h2,.hse-doctor h2{margin:0;font-size:18px;letter-spacing:-.01em}
|
|
203
|
-
.hse-section-head p,.hse-doctor-intro{margin:4px 0 0;color:var(--dsw-alias-label-secondary,#67728a);font-size:12px;line-height:1.55}
|
|
204
|
-
.hse-job-list{display:grid;gap:12px}
|
|
205
|
-
.hse-job{position:relative;padding:18px;border:1px solid var(--dsw-alias-border-l1,#dde5f2);border-radius:17px;background:var(--dsw-alias-bg-layer-2,#fff);box-shadow:var(--dsw-shadow-lv1,0 5px 18px rgba(27,54,93,.06));overflow:hidden}
|
|
206
|
-
.hse-job:before{content:"";position:absolute;inset:0 auto 0 0;width:3px;background:#5e8cff}
|
|
207
|
-
.hse-job[data-status="completed"]:before{background:#23ba83}.hse-job[data-status="partial"]:before{background:#f1a23c}.hse-job[data-status="failed"]:before{background:#ed5b6c}
|
|
208
|
-
.hse-job-top{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}
|
|
209
|
-
.hse-job-title{min-width:0}.hse-job-title strong{display:block;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.hse-job-title small{display:block;margin-top:5px;color:var(--dsw-alias-label-secondary,#748096);font-size:11px}
|
|
210
|
-
.hse-status{flex:none;display:inline-flex;align-items:center;gap:6px;border-radius:999px;padding:5px 9px;background:rgba(39,105,255,.1);color:#2769e8;font-size:11px;font-weight:700}
|
|
211
|
-
.hse-status:before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor;box-shadow:0 0 0 3px currentColor;opacity:.7}
|
|
212
|
-
.hse-status[data-status="completed"]{color:#12835d;background:rgba(35,186,131,.11)}.hse-status[data-status="partial"]{color:#b36a08;background:rgba(241,162,60,.13)}.hse-status[data-status="failed"]{color:#ca3145;background:rgba(237,91,108,.12)}
|
|
213
|
-
.hse-job-meta{display:grid;grid-template-columns:1.4fr 1.4fr .55fr .55fr;gap:8px;margin-top:15px}
|
|
214
|
-
.hse-meta{min-width:0;padding:9px 10px;border-radius:10px;background:var(--dsw-alias-bg-layer-1,#f5f8fc)}.hse-meta span{display:block;color:var(--dsw-alias-label-secondary,#748096);font-size:10px}.hse-meta code,.hse-meta strong{display:block;margin-top:4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;color:inherit}
|
|
215
|
-
.hse-metrics{display:flex;flex-wrap:wrap;gap:7px;margin-top:11px}.hse-metric{display:inline-flex;gap:6px;align-items:center;border:1px solid var(--dsw-alias-border-l1,#dde5f2);border-radius:8px;padding:6px 8px;font-size:11px}.hse-metric b{color:#2769e8}
|
|
216
|
-
.hse-promotion{margin-top:11px;padding:10px 12px;border-radius:10px;background:rgba(39,105,255,.07);font-size:11px}.hse-promotion[data-decision="PROMOTE"]{background:rgba(35,186,131,.1);color:#08734e}.hse-promotion[data-decision="REJECT"]{background:rgba(237,91,108,.09);color:#a92135}.hse-promotion strong{margin-right:8px}
|
|
217
|
-
.hse-empty,.hse-error{padding:34px;border:1px dashed var(--dsw-alias-border-l1,#cfd9e8);border-radius:18px;text-align:center;background:var(--dsw-alias-bg-layer-2,#fff)}.hse-empty h3,.hse-error h3{margin:10px 0 7px}.hse-empty p,.hse-error p{max-width:600px;margin:0 auto;color:var(--dsw-alias-label-secondary,#748096);font-size:13px;line-height:1.6}
|
|
218
|
-
.hse-steps{display:flex;justify-content:center;flex-wrap:wrap;gap:8px;margin-top:19px}.hse-steps span{padding:8px 11px;border-radius:9px;background:rgba(39,105,255,.08);color:#2769e8;font-size:11px;font-weight:700}
|
|
219
|
-
.hse-spinner{width:28px;height:28px;margin:0 auto 12px;border:3px solid rgba(39,105,255,.14);border-top-color:#2769ff;border-radius:50%;animation:hse-spin .8s linear infinite}@keyframes hse-spin{to{transform:rotate(360deg)}}
|
|
220
|
-
.hse-button{margin-top:13px;border:0;border-radius:9px;padding:8px 13px;background:#2769ff;color:#fff;font:inherit;font-size:12px;cursor:pointer}
|
|
221
|
-
.hse-doctor{width:min(860px,calc(100% - 40px));margin:0 auto;padding:30px 0 54px}
|
|
222
|
-
.hse-doctor-banner{display:flex;align-items:center;justify-content:space-between;gap:20px;margin:20px 0;padding:17px 19px;border-radius:15px;background:linear-gradient(135deg,rgba(39,105,255,.12),rgba(62,216,255,.08));border:1px solid rgba(39,105,255,.18)}
|
|
223
|
-
.hse-doctor-banner strong{font-size:14px}.hse-doctor-banner span{font-size:12px;color:var(--dsw-alias-label-secondary,#748096)}
|
|
224
|
-
.hse-checks{display:grid;grid-template-columns:1fr 1fr;gap:10px}.hse-check{display:flex;align-items:center;gap:11px;padding:13px;border:1px solid var(--dsw-alias-border-l1,#dde5f2);border-radius:12px;background:var(--dsw-alias-bg-layer-2,#fff)}
|
|
225
|
-
.hse-check-dot{width:9px;height:9px;border-radius:50%;background:#27bb83;box-shadow:0 0 0 4px rgba(39,187,131,.12)}.hse-check[data-status="warning"] .hse-check-dot{background:#e6a036;box-shadow:0 0 0 4px rgba(230,160,54,.12)}.hse-check[data-status="error"] .hse-check-dot{background:#ed5b6c;box-shadow:0 0 0 4px rgba(237,91,108,.12)}
|
|
226
|
-
.hse-check div{min-width:0}.hse-check strong,.hse-check small{display:block}.hse-check strong{font-size:12px}.hse-check small{margin-top:3px;color:var(--dsw-alias-label-secondary,#748096);font-size:10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
227
|
-
.hse-contract{margin-top:20px;padding:18px;border:1px solid var(--dsw-alias-border-l1,#dde5f2);border-radius:15px;background:var(--dsw-alias-bg-layer-2,#fff)}.hse-contract h3{margin:0 0 12px;font-size:14px}.hse-contract dl{display:grid;grid-template-columns:130px minmax(0,1fr);gap:8px;margin:0;font-size:11px}.hse-contract dt{color:var(--dsw-alias-label-secondary,#748096)}.hse-contract dd{margin:0;overflow-wrap:anywhere}.hse-contract code{font-size:10px}
|
|
228
|
-
.hse-skill-guide{margin-top:20px;padding:18px;border-radius:15px;color:#dff7ff;background:linear-gradient(135deg,#08275b,#0b4381);box-shadow:0 14px 35px rgba(5,39,91,.15)}.hse-skill-guide h3{margin:0 0 7px;font-size:14px}.hse-skill-guide p{margin:0;color:rgba(223,247,255,.78);font-size:12px;line-height:1.65}
|
|
229
|
-
.hse-tool{border:1px solid var(--dsw-alias-border-l1,#dce4f1);border-radius:13px;background:var(--dsw-alias-bg-layer-2,#fff);overflow:hidden}.hse-tool-head{display:flex;align-items:center;gap:10px;width:100%;padding:11px 13px;border:0;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer}.hse-tool-icon{display:grid;place-items:center;width:27px;height:27px;border-radius:9px;color:#2769ff;background:rgba(39,105,255,.1)}.hse-tool-title{font-size:12px;font-weight:700}.hse-tool-summary{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-secondary,#748096);font-size:11px}.hse-tool-state{margin-left:auto;font-size:10px;color:#238260}.hse-tool[data-state="running"] .hse-tool-state{color:#2769ff}.hse-tool[data-state="error"] .hse-tool-state{color:#cf3549}.hse-tool-body{display:grid;gap:8px;padding:0 13px 13px}.hse-tool-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:7px}.hse-tool-field{min-width:0;padding:8px 9px;border-radius:9px;background:var(--dsw-alias-bg-layer-1,#f5f8fc)}.hse-tool-field span,.hse-tool-field strong,.hse-tool-field code{display:block}.hse-tool-field span{font-size:9px;color:var(--dsw-alias-label-secondary,#748096)}.hse-tool-field strong,.hse-tool-field code{margin-top:3px;font-size:10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hse-reasons{margin:0;padding-left:18px;color:var(--dsw-alias-label-secondary,#748096);font-size:10px;line-height:1.5}
|
|
230
|
-
@media(max-width:760px){.hse-page,.hse-doctor{width:min(100% - 24px,1180px);padding-top:12px}.hse-hero{padding:26px 20px;min-height:310px}.hse-refresh{top:14px;right:14px}.hse-hero-copy{padding-top:28px}.hse-stats{grid-template-columns:repeat(2,1fr)}.hse-job-meta{grid-template-columns:1fr 1fr}.hse-checks{grid-template-columns:1fr}.hse-contract dl{grid-template-columns:1fr}.hse-contract dd{margin-bottom:5px}}
|
|
231
|
-
@media(prefers-reduced-motion:reduce){.hse-spinner{animation:none}.hse-refresh{transition:none}}
|
|
163
|
+
.hse-root{--blue:#2769ff;--cyan:#44d9ff;--deep:#061b44;height:100%;min-height:0;overflow:auto;color:var(--dsw-alias-label-primary,#172033);background:var(--dsw-alias-bg-layer-1,#f4f7fc);font-family:inherit}.hse-page{width:min(1240px,calc(100% - 36px));margin:auto;padding:24px 0 56px}
|
|
164
|
+
.hse-hero{position:relative;isolation:isolate;overflow:hidden;min-height:230px;padding:30px;border-radius:24px;color:#fff;background:#061b44 var(--ocean) center/cover no-repeat;box-shadow:0 20px 60px rgba(4,27,74,.23)}.hse-hero:before{content:"";position:absolute;inset:0;z-index:-1;background:linear-gradient(90deg,rgba(3,17,48,.96),rgba(4,31,79,.82) 52%,rgba(4,31,79,.2))}.hse-hero h1{max-width:650px;margin:16px 0 10px;font-size:clamp(28px,4vw,45px);line-height:1.08;letter-spacing:-.04em}.hse-hero p{max-width:670px;margin:0;color:#dceeff;font-size:14px;line-height:1.7}.hse-eyebrow{color:#76e4ff;font-size:11px;font-weight:800;letter-spacing:.17em}.hse-refresh{position:absolute;right:22px;top:22px;padding:8px 12px;border:1px solid #ffffff4a;border-radius:999px;color:#fff;background:#06245e99;cursor:pointer}
|
|
165
|
+
.hse-stats{display:flex;gap:9px;margin-top:25px;flex-wrap:wrap}.hse-stat{min-width:125px;padding:11px 13px;border:1px solid #ffffff26;border-radius:13px;background:#031a41a3;backdrop-filter:blur(8px)}.hse-stat span{display:block;color:#cde5fa;font-size:10px}.hse-stat b{display:block;margin-top:4px;font-size:20px}.hse-head{display:flex;justify-content:space-between;align-items:flex-end;gap:16px;margin:28px 0 12px}.hse-head h2{margin:0;font-size:18px}.hse-head p{margin:4px 0 0;color:#748096;font-size:12px}
|
|
166
|
+
.hse-list{display:grid;gap:10px}.hse-job{display:block;width:100%;padding:0;border:1px solid var(--dsw-alias-border-l1,#dce4f0);border-radius:16px;color:inherit;background:var(--dsw-alias-bg-layer-2,#fff);text-align:left;cursor:pointer;overflow:hidden;box-shadow:0 5px 18px #1b365d0d}.hse-job:hover{border-color:#78a3ff;transform:translateY(-1px)}.hse-job-body{padding:16px 18px}.hse-job-top{display:flex;justify-content:space-between;gap:14px}.hse-job-title{min-width:0}.hse-job-title strong{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px}.hse-job-title small{display:block;margin-top:4px;color:#7b879c;font-size:10px}.hse-status{flex:none;padding:5px 9px;border-radius:999px;color:#126d50;background:#23ba8318;font-size:10px;font-weight:700}.hse-status[data-status=failed]{color:#b5283d;background:#ed5b6c18}.hse-status[data-status=partial]{color:#9c620e;background:#f1a23c1c}.hse-status[data-status=pending]{color:#225cce;background:#2769ff18}.hse-meta-grid{display:grid;grid-template-columns:1.5fr 1.2fr .55fr .55fr .8fr;gap:7px;margin-top:13px}.hse-meta{min-width:0;padding:8px 9px;border-radius:9px;background:var(--dsw-alias-bg-layer-1,#f4f7fb)}.hse-meta span{display:block;color:#7b879c;font-size:9px}.hse-meta b,.hse-meta code{display:block;margin-top:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:10px}.hse-metrics{display:flex;gap:6px;flex-wrap:wrap;margin-top:10px}.hse-pill{padding:5px 7px;border:1px solid var(--dsw-alias-border-l1,#dce4f0);border-radius:7px;font-size:10px}.hse-pill b{margin-left:5px;color:var(--blue)}
|
|
167
|
+
.hse-empty,.hse-error{padding:34px;border:1px dashed #cbd6e6;border-radius:16px;text-align:center;background:var(--dsw-alias-bg-layer-2,#fff);color:#748096;font-size:12px}.hse-spin{width:25px;height:25px;margin:0 auto 10px;border:3px solid #2769ff22;border-top-color:var(--blue);border-radius:50%;animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}
|
|
168
|
+
.hse-overlay{position:fixed;inset:0;z-index:1000;display:flex;justify-content:flex-end;background:#06142c7a;backdrop-filter:blur(3px)}.hse-drawer{width:min(1040px,94vw);height:100%;overflow:auto;background:var(--dsw-alias-bg-layer-1,#f4f7fc);box-shadow:-24px 0 70px #04142c52}.hse-drawer-head{position:sticky;top:0;z-index:3;display:flex;justify-content:space-between;gap:15px;padding:18px 22px;border-bottom:1px solid var(--dsw-alias-border-l1,#dce4f0);background:color-mix(in srgb,var(--dsw-alias-bg-layer-2,#fff) 92%,transparent);backdrop-filter:blur(12px)}.hse-drawer-head h2{margin:0;font-size:18px}.hse-drawer-head p{margin:5px 0 0;color:#748096;font-size:10px}.hse-close,.hse-button{border:0;border-radius:9px;padding:8px 11px;color:#fff;background:var(--blue);cursor:pointer}.hse-close{align-self:flex-start;background:#142a4f}.hse-workbench{padding:18px 22px 45px}.hse-section{margin-bottom:14px;padding:16px;border:1px solid var(--dsw-alias-border-l1,#dce4f0);border-radius:14px;background:var(--dsw-alias-bg-layer-2,#fff)}.hse-section h3{margin:0 0 11px;font-size:14px}.hse-kpis{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px}.hse-kpi{padding:11px;border-radius:10px;background:linear-gradient(135deg,#2769ff12,#44d9ff0a)}.hse-kpi span{display:block;color:#748096;font-size:9px}.hse-kpi b{display:block;margin-top:4px;font-size:17px}.hse-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.hse-card{min-width:0;padding:11px;border-radius:10px;background:var(--dsw-alias-bg-layer-1,#f4f7fb)}.hse-card span,.hse-card b,.hse-card code{display:block}.hse-card span{color:#748096;font-size:9px}.hse-card b,.hse-card code{margin-top:4px;overflow-wrap:anywhere;font-size:10px}.hse-findings{display:grid;gap:6px}.hse-finding{padding:9px 10px;border-left:3px solid #6d9cff;border-radius:7px;background:#2769ff0c;font-size:10px}.hse-finding[data-level=error]{border-color:#e75267;background:#e752670c}.hse-finding[data-level=warning]{border-color:#e6a036;background:#e6a0360c}.hse-finding b{margin-right:7px}.hse-components{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:7px}.hse-component{padding:9px;border-radius:9px;background:#0b2e6421}.hse-component span{display:block;color:#748096;font-size:9px}.hse-component b,.hse-component code{display:block;margin-top:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:9px}
|
|
169
|
+
.hse-trial-tools{display:flex;gap:7px;margin-bottom:9px}.hse-input,.hse-select{min-width:0;padding:8px 9px;border:1px solid #cad6e7;border-radius:8px;color:inherit;background:transparent;font:inherit;font-size:10px}.hse-input{flex:1}.hse-table{width:100%;border-collapse:collapse;font-size:10px}.hse-table th,.hse-table td{padding:8px;border-bottom:1px solid #e4eaf2;text-align:left}.hse-table button{border:0;color:var(--blue);background:none;cursor:pointer;font:inherit}.hse-pager{display:flex;justify-content:flex-end;align-items:center;gap:8px;margin-top:9px;font-size:10px}.hse-pager button{padding:5px 8px;border:1px solid #cad6e7;border-radius:7px;background:transparent;color:inherit;cursor:pointer}.hse-trial-detail{margin-top:10px;padding:12px;border-radius:10px;background:#071d48;color:#dcecff}.hse-trial-detail pre,.hse-audit pre{max-height:340px;overflow:auto;margin:8px 0 0;white-space:pre-wrap;word-break:break-word;font-size:9px;line-height:1.5}.hse-reasons{margin:0;padding-left:18px;font-size:10px;line-height:1.6}.hse-audit summary{cursor:pointer;font-size:11px;font-weight:700}.hse-valid{color:#14815d}.hse-invalid{color:#c33148}
|
|
170
|
+
.hse-settings{width:min(850px,calc(100% - 32px));margin:auto;padding:28px 0}.hse-checks{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:9px;margin-top:16px}.hse-check{padding:12px;border:1px solid #dce4f0;border-radius:10px;background:var(--dsw-alias-bg-layer-2,#fff)}.hse-check b,.hse-check small{display:block}.hse-check small{margin-top:4px;color:#748096}.hse-tool{border:1px solid #dce4f0;border-radius:11px;background:var(--dsw-alias-bg-layer-2,#fff);overflow:hidden}.hse-tool button{display:flex;gap:8px;width:100%;padding:10px;border:0;color:inherit;background:transparent;text-align:left;cursor:pointer}.hse-tool strong{font-size:11px}.hse-tool small{margin-left:auto}.hse-tool pre{max-height:260px;overflow:auto;margin:0;padding:11px;border-top:1px solid #e3e9f1;white-space:pre-wrap;font-size:9px}
|
|
171
|
+
@media(max-width:800px){.hse-page{width:calc(100% - 20px)}.hse-hero{padding:24px 18px}.hse-meta-grid,.hse-kpis{grid-template-columns:repeat(2,1fr)}.hse-components{grid-template-columns:repeat(2,1fr)}.hse-grid,.hse-checks{grid-template-columns:1fr}.hse-drawer{width:100vw}.hse-workbench{padding:12px}.hse-drawer-head{padding:14px}.hse-table th:nth-child(3),.hse-table td:nth-child(3){display:none}}
|
|
172
|
+
@media(prefers-reduced-motion:reduce){.hse-spin{animation:none}.hse-job:hover{transform:none}}
|
|
232
173
|
`;
|
|
233
174
|
function installStyles() {
|
|
234
175
|
const id = "dsh-harbor-evolution/client";
|
|
235
176
|
if (document.querySelector(`style[data-plugin-css="${id}"]`)) return () => {
|
|
236
177
|
};
|
|
237
178
|
const style = document.createElement("style");
|
|
238
|
-
style.dataset.plugin = "dsh-harbor-evolution";
|
|
239
179
|
style.dataset.pluginCss = id;
|
|
240
180
|
style.textContent = CSS;
|
|
241
181
|
document.head.appendChild(style);
|
|
242
182
|
return () => style.remove();
|
|
243
183
|
}
|
|
244
184
|
function isRecord(value) {
|
|
245
|
-
return typeof value === "object" &&
|
|
185
|
+
return value && typeof value === "object" && !Array.isArray(value);
|
|
246
186
|
}
|
|
247
|
-
function
|
|
248
|
-
|
|
249
|
-
return block.content.filter((entry) => entry?.type === "text").map((entry) => entry.text).join("\n");
|
|
187
|
+
function format(value) {
|
|
188
|
+
return typeof value === "number" ? value.toFixed(4).replace(/0+$/, "").replace(/\.$/, "") : String(value ?? "\u2014");
|
|
250
189
|
}
|
|
251
|
-
function
|
|
252
|
-
|
|
253
|
-
if (isRecord(block.meta)) return block.meta;
|
|
254
|
-
const text = blockText(block).trim();
|
|
255
|
-
if (!text) return void 0;
|
|
256
|
-
try {
|
|
257
|
-
const parsed = JSON.parse(text);
|
|
258
|
-
return isRecord(parsed) ? parsed : void 0;
|
|
259
|
-
} catch {
|
|
260
|
-
return void 0;
|
|
261
|
-
}
|
|
190
|
+
function short(value) {
|
|
191
|
+
return typeof value === "string" && value.length > 22 ? `${value.slice(0, 15)}\u2026${value.slice(-5)}` : value ?? "\u2014";
|
|
262
192
|
}
|
|
263
|
-
|
|
264
|
-
|
|
193
|
+
function reasonText(reason) {
|
|
194
|
+
return isRecord(reason) ? `${reason.code}: ${reason.message}` : String(reason);
|
|
195
|
+
}
|
|
196
|
+
async function api(route, params = {}) {
|
|
197
|
+
const query = new URLSearchParams(Object.entries(params).filter(([, value]) => value !== void 0 && value !== ""));
|
|
198
|
+
const response = await fetch(`${API}/${route}${query.size ? `?${query}` : ""}`, { credentials: "same-origin", cache: "no-store" });
|
|
265
199
|
const body = await response.json();
|
|
266
200
|
if (!response.ok || !body?.ok) throw new Error(body?.error?.message ?? `HTTP ${response.status}`);
|
|
267
201
|
return body.value;
|
|
@@ -269,97 +203,104 @@ async function fetchDashboard() {
|
|
|
269
203
|
function useDashboard(poll = true) {
|
|
270
204
|
const [state, setState] = (0, import_react.useState)({ status: "loading" });
|
|
271
205
|
const load = (0, import_react.useCallback)(async (quiet = false) => {
|
|
272
|
-
if (!quiet) setState((current) => ({ ...current, status: current.value ? "refreshing" : "loading"
|
|
206
|
+
if (!quiet) setState((current) => ({ ...current, status: current.value ? "refreshing" : "loading" }));
|
|
273
207
|
try {
|
|
274
|
-
|
|
275
|
-
setState({ status: "ready", value });
|
|
208
|
+
setState({ status: "ready", value: await api("dashboard") });
|
|
276
209
|
} catch (error) {
|
|
277
|
-
setState((current) => ({ ...current, status: "error", error: error
|
|
210
|
+
setState((current) => ({ ...current, status: "error", error: error.message }));
|
|
278
211
|
}
|
|
279
212
|
}, []);
|
|
280
213
|
(0, import_react.useEffect)(() => {
|
|
281
214
|
void load();
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
215
|
+
}, [load]);
|
|
216
|
+
(0, import_react.useEffect)(() => {
|
|
217
|
+
if (!poll || !state.value) return void 0;
|
|
218
|
+
const interval = state.value.overview?.activeJobs ? 3e3 : 15e3;
|
|
219
|
+
const timer = window.setTimeout(() => void load(true), interval);
|
|
220
|
+
return () => window.clearTimeout(timer);
|
|
221
|
+
}, [load, poll, state.value]);
|
|
288
222
|
return { ...state, load };
|
|
289
223
|
}
|
|
290
|
-
function
|
|
291
|
-
return /* @__PURE__ */ import_react.default.createElement("
|
|
224
|
+
function MetricPills({ metrics }) {
|
|
225
|
+
return /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-metrics" }, Object.entries(metrics ?? {}).map(([key, value]) => /* @__PURE__ */ import_react.default.createElement("span", { className: "hse-pill", key }, key, /* @__PURE__ */ import_react.default.createElement("b", null, format(value)))));
|
|
292
226
|
}
|
|
293
|
-
function
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
return
|
|
227
|
+
function JobCard({ job, t, open }) {
|
|
228
|
+
const candidate = job.candidate ?? {};
|
|
229
|
+
const context = job.evaluationContext ?? {};
|
|
230
|
+
return /* @__PURE__ */ import_react.default.createElement("button", { type: "button", className: "hse-job", onClick: () => open(job.name) }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-job-body" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-job-top" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-job-title" }, /* @__PURE__ */ import_react.default.createElement("strong", null, job.name), /* @__PURE__ */ import_react.default.createElement("small", null, new Date(job.updatedAt).toLocaleString())), /* @__PURE__ */ import_react.default.createElement("span", { className: "hse-status", "data-status": job.status }, t(job.status))), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-meta-grid" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-meta" }, /* @__PURE__ */ import_react.default.createElement("span", null, t("candidate")), /* @__PURE__ */ import_react.default.createElement("b", null, candidate.candidate_id ?? "\u2014", " \xB7 ", candidate.version ?? "\u2014")), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-meta" }, /* @__PURE__ */ import_react.default.createElement("span", null, t("context")), /* @__PURE__ */ import_react.default.createElement("code", { title: context.digest }, short(context.digest))), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-meta" }, /* @__PURE__ */ import_react.default.createElement("span", null, t("trials")), /* @__PURE__ */ import_react.default.createElement("b", null, job.nTrials)), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-meta" }, /* @__PURE__ */ import_react.default.createElement("span", null, t("exceptions")), /* @__PURE__ */ import_react.default.createElement("b", null, job.nExceptions)), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-meta" }, /* @__PURE__ */ import_react.default.createElement("span", null, t("mode")), /* @__PURE__ */ import_react.default.createElement("b", null, job.mode ?? "\u2014"))), /* @__PURE__ */ import_react.default.createElement(MetricPills, { metrics: job.metrics })));
|
|
297
231
|
}
|
|
298
|
-
function
|
|
299
|
-
|
|
300
|
-
return value.length > 19 ? `${value.slice(0, 15)}\u2026${value.slice(-4)}` : value;
|
|
232
|
+
function ArtifactSection({ title, value, children }) {
|
|
233
|
+
return /* @__PURE__ */ import_react.default.createElement("section", { className: "hse-section" }, /* @__PURE__ */ import_react.default.createElement("h3", null, title), children ?? (value ? /* @__PURE__ */ import_react.default.createElement("pre", null, JSON.stringify(value, null, 2)) : /* @__PURE__ */ import_react.default.createElement("span", null, "\u2014")));
|
|
301
234
|
}
|
|
302
|
-
function
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
|
|
235
|
+
function TrialTable({ job, t }) {
|
|
236
|
+
const [query, setQuery] = (0, import_react.useState)("");
|
|
237
|
+
const [status, setStatus] = (0, import_react.useState)("");
|
|
238
|
+
const [offset, setOffset] = (0, import_react.useState)(0);
|
|
239
|
+
const [page, setPage] = (0, import_react.useState)();
|
|
240
|
+
const [detail, setDetail] = (0, import_react.useState)();
|
|
241
|
+
(0, import_react.useEffect)(() => {
|
|
242
|
+
const timer = window.setTimeout(() => {
|
|
243
|
+
void api("trials", { job, offset, limit: 50, query, status }).then(setPage);
|
|
244
|
+
}, 180);
|
|
245
|
+
return () => window.clearTimeout(timer);
|
|
246
|
+
}, [job, offset, query, status]);
|
|
247
|
+
const choose = async (trial) => setDetail(await api("trial", { job, trial }));
|
|
248
|
+
return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-trial-tools" }, /* @__PURE__ */ import_react.default.createElement("input", { className: "hse-input", value: query, placeholder: t("search"), onChange: (event) => {
|
|
249
|
+
setQuery(event.target.value);
|
|
250
|
+
setOffset(0);
|
|
251
|
+
} }), /* @__PURE__ */ import_react.default.createElement("select", { className: "hse-select", value: status, onChange: (event) => {
|
|
252
|
+
setStatus(event.target.value);
|
|
253
|
+
setOffset(0);
|
|
254
|
+
} }, /* @__PURE__ */ import_react.default.createElement("option", { value: "" }, t("all")), /* @__PURE__ */ import_react.default.createElement("option", { value: "assessed" }, t("assessed")), /* @__PURE__ */ import_react.default.createElement("option", { value: "infrastructure-error" }, t("infra")))), /* @__PURE__ */ import_react.default.createElement("table", { className: "hse-table" }, /* @__PURE__ */ import_react.default.createElement("thead", null, /* @__PURE__ */ import_react.default.createElement("tr", null, /* @__PURE__ */ import_react.default.createElement("th", null, "Trial"), /* @__PURE__ */ import_react.default.createElement("th", null, "Status"), /* @__PURE__ */ import_react.default.createElement("th", null, "Metrics"))), /* @__PURE__ */ import_react.default.createElement("tbody", null, page?.items?.map((trial) => /* @__PURE__ */ import_react.default.createElement("tr", { key: trial.id }, /* @__PURE__ */ import_react.default.createElement("td", null, /* @__PURE__ */ import_react.default.createElement("button", { onClick: () => void choose(trial.id) }, trial.name ?? trial.id)), /* @__PURE__ */ import_react.default.createElement("td", null, trial.status), /* @__PURE__ */ import_react.default.createElement("td", null, Object.entries(trial.rewards ?? {}).map(([key, value]) => `${key} ${format(value)}`).join(" \xB7 ")))))), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-pager" }, /* @__PURE__ */ import_react.default.createElement("span", null, offset + 1, "\u2013", Math.min(offset + (page?.items?.length ?? 0), page?.total ?? 0), " / ", page?.total ?? 0), /* @__PURE__ */ import_react.default.createElement("button", { disabled: !offset, onClick: () => setOffset(Math.max(0, offset - 50)) }, t("previous")), /* @__PURE__ */ import_react.default.createElement("button", { disabled: !page?.hasMore, onClick: () => setOffset(offset + 50) }, t("next"))), detail ? /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-trial-detail" }, /* @__PURE__ */ import_react.default.createElement("b", null, t("output")), /* @__PURE__ */ import_react.default.createElement("pre", null, JSON.stringify(detail.assessment, null, 2))) : null);
|
|
317
255
|
}
|
|
318
|
-
function
|
|
319
|
-
|
|
256
|
+
function Workbench({ job, close, t }) {
|
|
257
|
+
const [state, setState] = (0, import_react.useState)({ status: "loading" });
|
|
258
|
+
(0, import_react.useEffect)(() => {
|
|
259
|
+
let active = true;
|
|
260
|
+
void api("job", { job }).then((value) => active && setState({ status: "ready", value }), (error) => active && setState({ status: "error", error: error.message }));
|
|
261
|
+
return () => {
|
|
262
|
+
active = false;
|
|
263
|
+
};
|
|
264
|
+
}, [job]);
|
|
265
|
+
const detail = state.value;
|
|
266
|
+
const a = detail?.artifacts ?? {};
|
|
267
|
+
const summary = a.summary ?? {};
|
|
268
|
+
const context = a.context ?? {};
|
|
269
|
+
const doctor = a.doctor ?? {};
|
|
270
|
+
const contract = a.contract ?? {};
|
|
271
|
+
const stack = a.stack ?? {};
|
|
272
|
+
const reasons = a.promotion?.reasons ?? summary.exceptions ?? [];
|
|
273
|
+
return /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-overlay", role: "presentation", onMouseDown: (event) => event.target === event.currentTarget && close() }, /* @__PURE__ */ import_react.default.createElement("aside", { className: "hse-drawer", role: "dialog", "aria-modal": "true", "aria-label": job }, /* @__PURE__ */ import_react.default.createElement("header", { className: "hse-drawer-head" }, /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("h2", null, job), /* @__PURE__ */ import_react.default.createElement("p", null, summary.candidate?.candidate_id ?? "\u2014", " \xB7 ", summary.candidate?.version ?? "\u2014", " \xB7 ", context.mode ?? "\u2014")), /* @__PURE__ */ import_react.default.createElement("button", { type: "button", className: "hse-close", onClick: close }, t("close"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-workbench" }, state.status === "loading" ? /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-empty" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-spin" }), t("loading")) : state.status === "error" ? /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-error" }, state.error) : /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement(ArtifactSection, { title: t("result") }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-kpis" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-kpi" }, /* @__PURE__ */ import_react.default.createElement("span", null, t("primaryMetric")), /* @__PURE__ */ import_react.default.createElement("b", null, format(summary.metrics?.[contract.primary_metric]))), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-kpi" }, /* @__PURE__ */ import_react.default.createElement("span", null, t("trials")), /* @__PURE__ */ import_react.default.createElement("b", null, summary.n_trials ?? 0)), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-kpi" }, /* @__PURE__ */ import_react.default.createElement("span", null, t("exceptions")), /* @__PURE__ */ import_react.default.createElement("b", null, summary.n_exceptions ?? 0)), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-kpi" }, /* @__PURE__ */ import_react.default.createElement("span", null, t("validation")), /* @__PURE__ */ import_react.default.createElement("b", { className: summary.artifact_validation?.valid ? "hse-valid" : "hse-invalid" }, summary.artifact_validation?.valid ? "VALID" : "INVALID"))), /* @__PURE__ */ import_react.default.createElement(MetricPills, { metrics: summary.metrics })), /* @__PURE__ */ import_react.default.createElement(ArtifactSection, { title: t("process") }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-grid" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-card" }, /* @__PURE__ */ import_react.default.createElement("span", null, t("doctor")), /* @__PURE__ */ import_react.default.createElement("b", { className: doctor.promotion_ready ? "hse-valid" : "hse-invalid" }, doctor.promotion_ready ? t("ready") : t("blocked"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-card" }, /* @__PURE__ */ import_react.default.createElement("span", null, t("context")), /* @__PURE__ */ import_react.default.createElement("code", null, short(context.digest)))), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-findings" }, doctor.findings?.map((finding, index) => /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-finding", "data-level": finding.level, key: `${finding.code}-${index}` }, /* @__PURE__ */ import_react.default.createElement("b", null, finding.code), finding.message)))), /* @__PURE__ */ import_react.default.createElement(ArtifactSection, { title: t("contract") }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-grid" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-card" }, /* @__PURE__ */ import_react.default.createElement("span", null, "ID / ", t("version")), /* @__PURE__ */ import_react.default.createElement("b", null, contract.contract_id ?? "\u2014", " \xB7 ", contract.version ?? "\u2014")), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-card" }, /* @__PURE__ */ import_react.default.createElement("span", null, t("primaryMetric")), /* @__PURE__ */ import_react.default.createElement("b", null, contract.primary_metric ?? "\u2014"))), /* @__PURE__ */ import_react.default.createElement(MetricPills, { metrics: Object.fromEntries((contract.metrics ?? []).map((metric) => [metric.id, metric.direction ?? metric.label ?? "metric"])) })), /* @__PURE__ */ import_react.default.createElement(ArtifactSection, { title: t("stack") }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-components" }, Object.entries(stack.components ?? {}).map(([role, component]) => /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-component", key: role }, /* @__PURE__ */ import_react.default.createElement("span", null, role, component.reward_affecting ? " \xB7 reward" : ""), /* @__PURE__ */ import_react.default.createElement("b", null, component.id, " \xB7 ", component.version), /* @__PURE__ */ import_react.default.createElement("code", { title: component.digest }, short(component.digest)))))), /* @__PURE__ */ import_react.default.createElement(ArtifactSection, { title: t("dataset") }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-grid" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-card" }, /* @__PURE__ */ import_react.default.createElement("span", null, "ID / ", t("version")), /* @__PURE__ */ import_react.default.createElement("b", null, a.dataset?.dataset_id ?? "\u2014", " \xB7 ", a.dataset?.version ?? "\u2014")), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-card" }, /* @__PURE__ */ import_react.default.createElement("span", null, t("population")), /* @__PURE__ */ import_react.default.createElement("b", null, a.dataset?.task_count ?? 0, " tasks \xB7 ", a.dataset?.file_count ?? 0, " files")))), /* @__PURE__ */ import_react.default.createElement(ArtifactSection, { title: t("assessments") }, /* @__PURE__ */ import_react.default.createElement(TrialTable, { job, t })), /* @__PURE__ */ import_react.default.createElement(ArtifactSection, { title: t("reasons") }, /* @__PURE__ */ import_react.default.createElement("ul", { className: "hse-reasons" }, reasons.length ? reasons.map((reason, index) => /* @__PURE__ */ import_react.default.createElement("li", { key: index }, reasonText(reason))) : /* @__PURE__ */ import_react.default.createElement("li", null, t("noData")))), /* @__PURE__ */ import_react.default.createElement(ArtifactSection, { title: t("optimization"), value: a.optimization }), /* @__PURE__ */ import_react.default.createElement(ArtifactSection, { title: t("promotion"), value: a.promotion }), /* @__PURE__ */ import_react.default.createElement("details", { className: "hse-section hse-audit" }, /* @__PURE__ */ import_react.default.createElement("summary", null, t("audit")), /* @__PURE__ */ import_react.default.createElement("pre", null, JSON.stringify({ validation: detail.validation, candidate: a.candidate, context: a.context, stack: a.stack, dataset: a.dataset, population: a.population }, null, 2)))))));
|
|
320
274
|
}
|
|
321
275
|
function DashboardView({ t }) {
|
|
322
276
|
const state = useDashboard(true);
|
|
277
|
+
const [selected, setSelected] = (0, import_react.useState)();
|
|
323
278
|
const snapshot = state.value;
|
|
324
|
-
const stats = [
|
|
325
|
-
|
|
326
|
-
["completedJobs", snapshot?.overview?.completedJobs ?? "\u2014"],
|
|
327
|
-
["activeJobs", snapshot?.overview?.activeJobs ?? "\u2014"],
|
|
328
|
-
["latestMetric", snapshot?.overview?.latestMetric ? `${snapshot.overview.latestMetric.name} ${formatValue(snapshot.overview.latestMetric.value)}` : "\u2014"]
|
|
329
|
-
];
|
|
330
|
-
return /* @__PURE__ */ import_react.default.createElement("main", { className: "hse-root" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-page" }, /* @__PURE__ */ import_react.default.createElement("section", { className: "hse-hero", style: { "--hse-ocean": `url(${harbor_ocean_default})` } }, /* @__PURE__ */ import_react.default.createElement("button", { type: "button", className: "hse-refresh", disabled: state.status === "loading" || state.status === "refreshing", onClick: () => void state.load() }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u21BB"), t("refresh")), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-hero-copy" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-eyebrow" }, t("eyebrow")), /* @__PURE__ */ import_react.default.createElement("h1", null, t("heroTitle")), /* @__PURE__ */ import_react.default.createElement("p", null, t("heroBody"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-stats" }, stats.map(([label, value]) => /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-stat", key: label }, /* @__PURE__ */ import_react.default.createElement("span", null, t(label)), /* @__PURE__ */ import_react.default.createElement("strong", null, value))))), /* @__PURE__ */ import_react.default.createElement("section", { className: "hse-section" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-section-head" }, /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("h2", null, t("jobs")), /* @__PURE__ */ import_react.default.createElement("p", null, t("jobsHint"))), snapshot?.generatedAt ? /* @__PURE__ */ import_react.default.createElement("p", null, t("refreshed"), " ", new Date(snapshot.generatedAt).toLocaleTimeString()) : null), state.status === "loading" ? /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-empty" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-spinner" }), /* @__PURE__ */ import_react.default.createElement("p", null, t("loading"))) : state.status === "error" && !snapshot ? /* @__PURE__ */ import_react.default.createElement(ErrorState, { error: state.error, retry: state.load, t }) : !snapshot?.jobs?.length ? /* @__PURE__ */ import_react.default.createElement(EmptyState, { t }) : /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-job-list" }, snapshot.jobs.map((job) => /* @__PURE__ */ import_react.default.createElement(JobCard, { job, t, key: job.name }))))));
|
|
279
|
+
const stats = [[t("jobs"), snapshot?.overview?.totalJobs ?? "\u2014"], [t("completed"), snapshot?.overview?.completedJobs ?? "\u2014"], [t("pending"), snapshot?.overview?.activeJobs ?? "\u2014"], [snapshot?.overview?.latestMetric?.name ?? t("primaryMetric"), format(snapshot?.overview?.latestMetric?.value)]];
|
|
280
|
+
return /* @__PURE__ */ import_react.default.createElement("main", { className: "hse-root" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-page" }, /* @__PURE__ */ import_react.default.createElement("section", { className: "hse-hero", style: { "--ocean": `url(${harbor_ocean_default})` } }, /* @__PURE__ */ import_react.default.createElement("button", { className: "hse-refresh", onClick: () => void state.load() }, t("refresh")), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-eyebrow" }, t("eyebrow")), /* @__PURE__ */ import_react.default.createElement("h1", null, t("heroTitle")), /* @__PURE__ */ import_react.default.createElement("p", null, t("heroBody")), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-stats" }, stats.map(([label, value]) => /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-stat", key: label }, /* @__PURE__ */ import_react.default.createElement("span", null, label), /* @__PURE__ */ import_react.default.createElement("b", null, value))))), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-head" }, /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("h2", null, t("jobs")), /* @__PURE__ */ import_react.default.createElement("p", null, t("jobsHint")))), state.status === "loading" ? /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-empty" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-spin" }), t("loading")) : state.status === "error" && !snapshot ? /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-error" }, state.error, /* @__PURE__ */ import_react.default.createElement("br", null), /* @__PURE__ */ import_react.default.createElement("button", { className: "hse-button", onClick: () => void state.load() }, t("retry"))) : !snapshot?.jobs?.length ? /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-empty" }, t("empty")) : /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-list" }, snapshot.jobs.map((job) => /* @__PURE__ */ import_react.default.createElement(JobCard, { job, t, open: setSelected, key: job.name })))), selected ? /* @__PURE__ */ import_react.default.createElement(Workbench, { job: selected, close: () => setSelected(void 0), t }) : null);
|
|
331
281
|
}
|
|
332
282
|
function DoctorView({ t }) {
|
|
333
283
|
const state = useDashboard(false);
|
|
334
|
-
|
|
335
|
-
const checks = snapshot?.checks ?? {};
|
|
336
|
-
const allReady = Object.values(checks).every((check) => check?.status === "ok");
|
|
337
|
-
const labels = { projectRoot: "projectRoot", jobsDir: "jobsDir", harbor: "harbor", harborDsh: "harborDsh" };
|
|
338
|
-
return /* @__PURE__ */ import_react.default.createElement("main", { className: "hse-root" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-doctor" }, /* @__PURE__ */ import_react.default.createElement("h2", null, t("doctorTitle")), /* @__PURE__ */ import_react.default.createElement("p", { className: "hse-doctor-intro" }, t("doctorBody")), state.status === "loading" ? /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-empty", style: { marginTop: 20 } }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-spinner" }), /* @__PURE__ */ import_react.default.createElement("p", null, t("loading"))) : state.status === "error" && !snapshot ? /* @__PURE__ */ import_react.default.createElement("div", { style: { marginTop: 20 } }, /* @__PURE__ */ import_react.default.createElement(ErrorState, { error: state.error, retry: state.load, t })) : /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-doctor-banner" }, /* @__PURE__ */ import_react.default.createElement("strong", null, allReady ? t("ready") : t("attention")), /* @__PURE__ */ import_react.default.createElement("span", null, "Plugin v", snapshot?.pluginVersion ?? "0.4.0")), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-checks" }, Object.entries(labels).map(([key, label]) => /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-check", "data-status": checks[key]?.status ?? "error", key }, /* @__PURE__ */ import_react.default.createElement("span", { className: "hse-check-dot" }), /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("strong", null, t(label)), /* @__PURE__ */ import_react.default.createElement("small", { title: checks[key]?.detail }, checks[key]?.detail ?? "\u2014"))))), /* @__PURE__ */ import_react.default.createElement("section", { className: "hse-contract" }, /* @__PURE__ */ import_react.default.createElement("h3", null, t("runtime")), /* @__PURE__ */ import_react.default.createElement("dl", null, /* @__PURE__ */ import_react.default.createElement("dt", null, t("projectRoot")), /* @__PURE__ */ import_react.default.createElement("dd", null, /* @__PURE__ */ import_react.default.createElement("code", null, snapshot?.config?.projectRoot ?? "\u2014")), /* @__PURE__ */ import_react.default.createElement("dt", null, t("jobsDir")), /* @__PURE__ */ import_react.default.createElement("dd", null, /* @__PURE__ */ import_react.default.createElement("code", null, snapshot?.config?.jobsDir ?? "\u2014")), /* @__PURE__ */ import_react.default.createElement("dt", null, t("dshVersion")), /* @__PURE__ */ import_react.default.createElement("dd", null, snapshot?.config?.dshVersion ?? "\u2014"), /* @__PURE__ */ import_react.default.createElement("dt", null, t("agentImport")), /* @__PURE__ */ import_react.default.createElement("dd", null, /* @__PURE__ */ import_react.default.createElement("code", null, snapshot?.config?.agentImportPath ?? "\u2014")), /* @__PURE__ */ import_react.default.createElement("dt", null, t("pluginImport")), /* @__PURE__ */ import_react.default.createElement("dd", null, /* @__PURE__ */ import_react.default.createElement("code", null, snapshot?.config?.pluginImportPath ?? "\u2014")))), /* @__PURE__ */ import_react.default.createElement("section", { className: "hse-skill-guide" }, /* @__PURE__ */ import_react.default.createElement("h3", null, t("skillGuide")), /* @__PURE__ */ import_react.default.createElement("p", null, t("skillBody"))))));
|
|
284
|
+
return /* @__PURE__ */ import_react.default.createElement("main", { className: "hse-root" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-settings" }, /* @__PURE__ */ import_react.default.createElement("h2", null, t("setupDoctor")), /* @__PURE__ */ import_react.default.createElement("p", null, t("setupHint")), /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-checks" }, Object.entries(state.value?.checks ?? {}).map(([key, check]) => /* @__PURE__ */ import_react.default.createElement("div", { className: "hse-check", key }, /* @__PURE__ */ import_react.default.createElement("b", { className: check.status === "ok" ? "hse-valid" : "hse-invalid" }, key, " \xB7 ", check.status), /* @__PURE__ */ import_react.default.createElement("small", null, check.detail))))));
|
|
339
285
|
}
|
|
340
|
-
function
|
|
341
|
-
return
|
|
286
|
+
function blockText(block) {
|
|
287
|
+
return isRecord(block) && Array.isArray(block.content) ? block.content.filter((item) => item?.type === "text").map((item) => item.text).join("\n") : "";
|
|
342
288
|
}
|
|
343
|
-
function
|
|
344
|
-
if (
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
if (toolName === "harbor_eval_result") {
|
|
352
|
-
return { title: t("toolResult"), summary: value?.job, metrics: value?.metrics, fields: [[t("job"), value?.job, true], [t("trials"), value?.n_trials], [t("exceptions"), value?.n_exceptions], [t("context"), shortDigest(value?.evaluation_context?.digest), true]] };
|
|
289
|
+
function decodeToolResult(block) {
|
|
290
|
+
if (!isRecord(block) || block.isError) return void 0;
|
|
291
|
+
if (isRecord(block.meta)) return block.meta;
|
|
292
|
+
try {
|
|
293
|
+
const value = JSON.parse(blockText(block));
|
|
294
|
+
return isRecord(value) ? value : void 0;
|
|
295
|
+
} catch {
|
|
296
|
+
return void 0;
|
|
353
297
|
}
|
|
354
|
-
return { title: t("toolCompare"), summary: value?.decision, metrics: value?.candidate_metrics, fields: [[t("decision"), value?.decision], [t("candidate"), value?.candidate?.version ?? value?.candidate_job], [t("comparable"), value?.baseline_evaluation_context?.digest === value?.candidate_evaluation_context?.digest ? "\u2713" : "\u2014"], [t("changed"), value?.baseline_candidate?.digest !== value?.candidate?.digest ? "\u2713" : "\u2014"]], reasons: value?.reasons };
|
|
355
298
|
}
|
|
356
|
-
function HarborToolView({ block, toolName
|
|
299
|
+
function HarborToolView({ block, toolName }) {
|
|
357
300
|
const [open, setOpen] = (0, import_react.useState)(true);
|
|
358
|
-
const running = !isRecord(block) || !("kind" in block);
|
|
359
|
-
const failed = !running && block.isError;
|
|
360
301
|
const value = decodeToolResult(block);
|
|
361
|
-
const
|
|
362
|
-
return /* @__PURE__ */ import_react.default.createElement("section", { className: "hse-tool"
|
|
302
|
+
const running = !isRecord(block) || !("kind" in block);
|
|
303
|
+
return /* @__PURE__ */ import_react.default.createElement("section", { className: "hse-tool" }, /* @__PURE__ */ import_react.default.createElement("button", { type: "button", onClick: () => setOpen(!open) }, /* @__PURE__ */ import_react.default.createElement("strong", null, "\u{1F433} ", toolName), /* @__PURE__ */ import_react.default.createElement("small", null, running ? "running" : block.isError ? "error" : "\u2713")), open ? /* @__PURE__ */ import_react.default.createElement("pre", null, value ? JSON.stringify(value, null, 2) : blockText(block) || "Running\u2026") : null);
|
|
363
304
|
}
|
|
364
305
|
var name = "dsh-harbor-evolution";
|
|
365
306
|
var inject = ["slots", "locale"];
|
|
@@ -368,25 +309,10 @@ function apply(ctx) {
|
|
|
368
309
|
ctx.effect(() => ctx.locale.register(NS, dictionaries), "harbor-evolution: locale");
|
|
369
310
|
const t = ctx.locale.bind(NS);
|
|
370
311
|
const injected = () => ({ t });
|
|
371
|
-
ctx.slots.inject("conversation.view", () => ctx.slots.register({
|
|
372
|
-
|
|
373
|
-
id: "harbor-evolution",
|
|
374
|
-
order: 30,
|
|
375
|
-
locale: NS,
|
|
376
|
-
label: () => t("tab"),
|
|
377
|
-
inject: injected
|
|
378
|
-
}, DashboardView));
|
|
379
|
-
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
380
|
-
name: "settings.section",
|
|
381
|
-
id: "harbor-evolution",
|
|
382
|
-
order: 35,
|
|
383
|
-
label: () => t("settings"),
|
|
384
|
-
inject: injected
|
|
385
|
-
}, DoctorView));
|
|
312
|
+
ctx.slots.inject("conversation.view", () => ctx.slots.register({ name: "conversation.view", id: "harbor-evolution", order: 30, locale: NS, label: () => t("tab"), inject: injected }, DashboardView));
|
|
313
|
+
ctx.slots.inject("settings.section", () => ctx.slots.register({ name: "settings.section", id: "harbor-evolution", order: 35, label: () => t("settings"), inject: injected }, DoctorView));
|
|
386
314
|
ctx.slots.inject("tool.call.toolview", function* registerTools() {
|
|
387
|
-
for (const key of ["harbor_candidate_snapshot", "harbor_eval_run", "harbor_eval_result", "harbor_candidate_compare"]) {
|
|
388
|
-
yield ctx.slots.register({ name: "tool.call.toolview", key, inject: injected }, HarborToolView);
|
|
389
|
-
}
|
|
315
|
+
for (const key of ["harbor_candidate_snapshot", "harbor_evolution_init", "harbor_evolution_doctor", "harbor_dataset_validate", "harbor_context_preview", "harbor_eval_run", "harbor_eval_result", "harbor_candidate_compare"]) yield ctx.slots.register({ name: "tool.call.toolview", key, inject: injected }, HarborToolView);
|
|
390
316
|
});
|
|
391
317
|
}
|
|
392
318
|
module.exports = { name, inject, apply };
|