dsh-subagent-profile 0.1.0 → 0.2.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 +34 -3
- package/README.zh.md +33 -2
- package/cordis.patch.yml +2 -2
- package/index.mjs +359 -172
- package/lib/client.js +250 -56
- package/lib/pure.mjs +331 -0
- package/lib/shims.mjs +215 -0
- package/package.json +7 -3
- package/presets/orchestrator/agent.cordis.yml +7 -9
package/lib/client.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* dsh-subagent-profile — browser
|
|
2
|
+
* dsh-subagent-profile — browser side: a read-only `dispatch` tool-call card
|
|
3
3
|
* plus a settings page that manages the profile registry.
|
|
4
4
|
*
|
|
5
5
|
* Hand-written `__ModuleLoader__` factory (no build step), modeled on
|
|
6
6
|
* @dsh-external/dsh-persona-ref: React comes from the shared module host, every
|
|
7
7
|
* data operation goes through the loopback /subagent-profiles/* HTTP routes
|
|
8
|
-
* served by the host
|
|
8
|
+
* served by the host side (index.mjs), and slots register through the shared
|
|
9
9
|
* `slots` service. The card lives on the keyed `tool.call.toolview` seat (key
|
|
10
10
|
* `dispatch`); the settings page lives on the `settings.section` list.
|
|
11
11
|
*
|
|
@@ -27,7 +27,7 @@ window.__ModuleLoader__.load({
|
|
|
27
27
|
/** Required cordis services: the slot registry only. */
|
|
28
28
|
const inject = ['slots']
|
|
29
29
|
|
|
30
|
-
// ── api (loopback HTTP, host
|
|
30
|
+
// ── api (loopback HTTP, host side serves /subagent-profiles/*) ──────────
|
|
31
31
|
|
|
32
32
|
async function api(path, payload) {
|
|
33
33
|
const init = payload === undefined
|
|
@@ -65,10 +65,15 @@ window.__ModuleLoader__.load({
|
|
|
65
65
|
'.sap-rowTag{border:1px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:1px 6px;font-size:11px;line-height:16px}',
|
|
66
66
|
'.sap-rowActions{align-items:center;gap:4px;margin-left:auto;display:inline-flex}',
|
|
67
67
|
'.sap-desc{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:18px}',
|
|
68
|
-
'.sap-
|
|
69
|
-
'.sap-
|
|
70
|
-
'.sap-
|
|
71
|
-
'.sap-
|
|
68
|
+
'.sap-warn{color:var(--dsw-alias-state-warn-label);margin:0;font-size:12px;line-height:18px}',
|
|
69
|
+
'.sap-chips{flex-wrap:wrap;gap:6px;display:flex}',
|
|
70
|
+
'.sap-chip{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:6px;flex:none;align-items:center;gap:5px;padding:2px 8px;font-size:12px;line-height:18px;display:inline-flex}',
|
|
71
|
+
'.sap-chipLabel{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}',
|
|
72
|
+
'.sap-chipValue{color:var(--dsw-alias-label-primary)}',
|
|
73
|
+
'.sap-chipValueDim{color:var(--dsw-alias-label-tertiary)}',
|
|
74
|
+
'.sap-chipWarn{background:var(--dsw-alias-state-warn-tertiary);border-color:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-state-warn-label);font-weight:500}',
|
|
75
|
+
'.sap-chipSuccess{color:var(--dsw-alias-state-success-primary);font-weight:500}',
|
|
76
|
+
'.sap-chipDisabled{color:var(--dsw-alias-state-warn-label);font-weight:500}',
|
|
72
77
|
'.sap-metaValueDefault{color:var(--dsw-alias-label-tertiary)}',
|
|
73
78
|
'.sap-addBlock{margin-top:4px}',
|
|
74
79
|
'.sap-addActions{flex-wrap:wrap;gap:10px;display:flex}',
|
|
@@ -105,7 +110,8 @@ window.__ModuleLoader__.load({
|
|
|
105
110
|
'.sap-switch input::before{content:"";position:absolute;width:16px;height:16px;border-radius:50%;background:#fff;top:2px;left:2px;transition:left .2s;box-shadow:0 1px 2px rgba(0,0,0,.2)}',
|
|
106
111
|
'.sap-switch input:checked{background:var(--dsw-alias-brand-primary)}',
|
|
107
112
|
'.sap-switch input:checked::before{left:18px}',
|
|
108
|
-
'.sap-toolview{
|
|
113
|
+
'.sap-toolview{flex-direction:column;align-items:flex-start;gap:6px;padding:4px 8px;display:flex}',
|
|
114
|
+
'.sap-toolText{font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary);white-space:pre-wrap;word-break:break-word}',
|
|
109
115
|
'.sap-toolModes{flex-wrap:wrap;gap:4px 16px;display:flex}',
|
|
110
116
|
'.sap-toolGroups{flex-direction:column;gap:8px;display:flex}',
|
|
111
117
|
'.sap-toolLayerCard{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:6px 10px}',
|
|
@@ -130,45 +136,214 @@ window.__ModuleLoader__.load({
|
|
|
130
136
|
|
|
131
137
|
// ── dispatch tool-call card ─────────────────────────────────────────────
|
|
132
138
|
|
|
139
|
+
// 面向用户的固定文案(G1)。注意:此表仅集中 dispatch 卡片文案;设置页文案
|
|
140
|
+
// 仍为 v0.1.0 散落硬编码,属 G1 待迁移债(V2.0 中期一次性迁入)。
|
|
141
|
+
const ZH = {
|
|
142
|
+
inlineProfile: '内联',
|
|
143
|
+
inherit: '继承父',
|
|
144
|
+
effortDefault: '默认(继承父)',
|
|
145
|
+
chipProfile: '方案',
|
|
146
|
+
chipPreset: '预设',
|
|
147
|
+
chipProvider: '提供方',
|
|
148
|
+
chipModel: '模型',
|
|
149
|
+
chipEffort: '推理强度',
|
|
150
|
+
chipTier: '档位',
|
|
151
|
+
chipEnabled: '已启用',
|
|
152
|
+
chipDisabled: '已禁用',
|
|
153
|
+
ignoredMark: '已忽略',
|
|
154
|
+
toolLabel: '派发子 Agent',
|
|
155
|
+
cont: 'continuable 子 Agent',
|
|
156
|
+
canContinue: '可续跑',
|
|
157
|
+
toolBackground: '后台任务',
|
|
158
|
+
toolForeground: '前台',
|
|
159
|
+
toolRunning: '运行中',
|
|
160
|
+
tierZh: { cheap: '省 token(cheap)', balanced: '均衡(balanced)', premium: '高成本(premium)' },
|
|
161
|
+
persistFail: '已保存但未持久化',
|
|
162
|
+
persistFailHint: '(磁盘写入未成功,重启 dsh web 后该改动可能丢失)'
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const effortZh = (id) => {
|
|
166
|
+
if (id === undefined || id === null || id === '') return ZH.inherit
|
|
167
|
+
if (id === '(default)') return ZH.effortDefault
|
|
168
|
+
if (id === '(parent)' || id === 'undefined') return ZH.inherit
|
|
169
|
+
return EFFORT_ZH[id] ? `${EFFORT_ZH[id]}(${id})` : id
|
|
170
|
+
}
|
|
171
|
+
const presetZh = (v) => (v === 'inherit' ? ZH.inherit : (typeof v === 'string' && v !== '' ? v : ZH.inherit))
|
|
172
|
+
const profileZh = (v) => (v === undefined || v === '' || v === '(inline)' ? ZH.inlineProfile : v)
|
|
173
|
+
const rawOrInherit = (v) => (v === '(parent)' ? ZH.inherit : (typeof v === 'string' && v !== '' ? v : ZH.inherit))
|
|
174
|
+
|
|
175
|
+
/** 统一 chip 元素:label + value,dim=继承/默认值弱显。 */
|
|
176
|
+
const chip = (key, label, value, opts) => {
|
|
177
|
+
const valueCls = opts && opts.dim ? 'sap-chipValueDim' : 'sap-chipValue'
|
|
178
|
+
return el('span', {
|
|
179
|
+
key,
|
|
180
|
+
className: 'sap-chip',
|
|
181
|
+
...(opts && typeof opts.title === 'string' && opts.title !== '' ? { title: opts.title } : {})
|
|
182
|
+
},
|
|
183
|
+
el('span', { className: 'sap-chipLabel' }, label),
|
|
184
|
+
el('span', { className: valueCls }, value)
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 请求值:argsRaw(运行中 / 已结算两种 block 形态都有)。
|
|
189
|
+
function readDispatchRequest(block) {
|
|
190
|
+
const out = { profile: '', preset: '', provider: '', model: '', reasoningEffort: '', continuable: false, runInBackground: false }
|
|
191
|
+
if (!block || typeof block !== 'object') return out
|
|
192
|
+
const done = 'kind' in block
|
|
193
|
+
const argsRaw = (done ? (block.call && block.call.argsRaw) : block.argsRaw) ?? ''
|
|
194
|
+
if (typeof argsRaw !== 'string' || argsRaw === '') return out
|
|
195
|
+
try {
|
|
196
|
+
const parsed = JSON.parse(argsRaw)
|
|
197
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
198
|
+
for (const key of ['profile', 'preset', 'provider', 'model', 'reasoningEffort']) {
|
|
199
|
+
if (typeof parsed[key] === 'string' && parsed[key] !== '') out[key] = parsed[key]
|
|
200
|
+
}
|
|
201
|
+
out.continuable = parsed.continuable === true
|
|
202
|
+
out.runInBackground = parsed.run_in_background === true
|
|
203
|
+
}
|
|
204
|
+
} catch {
|
|
205
|
+
// unparsable argsRaw:保持默认(继承/内联口径)。
|
|
206
|
+
}
|
|
207
|
+
return out
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// 结果 meta(生效值):宿主 render 行("[dispatch] …")是持久回放的唯一
|
|
211
|
+
// 生效值来源——结构化 value 只存在于执行局部(dsh-tools 约定),且宿主未
|
|
212
|
+
// 定义 output.presentationMeta(block.meta 为空)。解析成功返回
|
|
213
|
+
// {kind, id, fields, ignored};任何一步不符预期返回 null,调用方回退请求值。
|
|
214
|
+
const DISPATCH_RESULT_KEYS = ['profile', 'preset', 'provider', 'model', 'reasoningEffort', 'tokenTier']
|
|
215
|
+
function parseDispatchText(text) {
|
|
216
|
+
if (typeof text !== 'string') return null
|
|
217
|
+
const firstLine = text.split('\n')[0] ?? ''
|
|
218
|
+
if (!firstLine.startsWith('[dispatch] ')) return null
|
|
219
|
+
let rest = firstLine.slice('[dispatch] '.length)
|
|
220
|
+
let kind = 'foreground'
|
|
221
|
+
let id = ''
|
|
222
|
+
if (rest.startsWith('background job ')) {
|
|
223
|
+
kind = 'background'
|
|
224
|
+
rest = rest.slice('background job '.length)
|
|
225
|
+
} else if (rest.startsWith('started subagent ')) {
|
|
226
|
+
kind = 'continuable'
|
|
227
|
+
rest = rest.slice('started subagent '.length)
|
|
228
|
+
}
|
|
229
|
+
if (kind !== 'foreground') {
|
|
230
|
+
const sep = rest.indexOf(' · ')
|
|
231
|
+
id = sep >= 0 ? rest.slice(0, sep) : rest
|
|
232
|
+
rest = sep >= 0 ? rest.slice(sep + 3) : ''
|
|
233
|
+
}
|
|
234
|
+
const fields = {}
|
|
235
|
+
let ignored = []
|
|
236
|
+
let known = 0
|
|
237
|
+
for (const seg of rest.split(' · ')) {
|
|
238
|
+
const eq = seg.indexOf('=')
|
|
239
|
+
if (eq <= 0) continue
|
|
240
|
+
const key = seg.slice(0, eq)
|
|
241
|
+
if (!DISPATCH_RESULT_KEYS.includes(key)) continue
|
|
242
|
+
let value = seg.slice(eq + 1)
|
|
243
|
+
const m = /^(.+)\(ignored:\s*([^)]*)\)$/.exec(value)
|
|
244
|
+
if (m !== null) {
|
|
245
|
+
value = m[1]
|
|
246
|
+
ignored = m[2].split(',').map((s) => s.trim()).filter((s) => s !== '')
|
|
247
|
+
}
|
|
248
|
+
if (value === 'undefined') value = '' // v0.1.0 渲染该字段为字面 undefined
|
|
249
|
+
fields[key] = value
|
|
250
|
+
known += 1
|
|
251
|
+
}
|
|
252
|
+
if (known <= 0) return null
|
|
253
|
+
return { kind, id, fields, ignored }
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function readDispatchResult(block) {
|
|
257
|
+
if (!block || typeof block !== 'object' || !('kind' in block)) return null
|
|
258
|
+
const content = block.content
|
|
259
|
+
if (!Array.isArray(content)) return null
|
|
260
|
+
for (const item of content) {
|
|
261
|
+
if (item && typeof item === 'object' && typeof item.text === 'string') {
|
|
262
|
+
const parsed = parseDispatchText(item.text)
|
|
263
|
+
if (parsed !== null) return parsed
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return null
|
|
267
|
+
}
|
|
268
|
+
|
|
133
269
|
function DispatchToolview(props) {
|
|
134
270
|
const block = props && props.block
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
271
|
+
const request = readDispatchRequest(block)
|
|
272
|
+
const result = readDispatchResult(block)
|
|
273
|
+
const settled = !!(block && typeof block === 'object' && 'kind' in block)
|
|
274
|
+
// 生效值:结果解析成功用结果(优先),字段缺失回退请求值。
|
|
275
|
+
const eff = {
|
|
276
|
+
profile: result ? (result.fields.profile || request.profile) : request.profile,
|
|
277
|
+
preset: result ? (result.fields.preset || request.preset) : request.preset,
|
|
278
|
+
provider: result ? (result.fields.provider || request.provider) : request.provider,
|
|
279
|
+
model: result ? (result.fields.model || request.model) : request.model,
|
|
280
|
+
reasoningEffort: result ? (result.fields.reasoningEffort || request.reasoningEffort) : request.reasoningEffort
|
|
281
|
+
}
|
|
282
|
+
// ignored 列表:已结算读结果;运行中(无结果)按已知降级规则预判——
|
|
283
|
+
// continuable 恒忽略 preset 换用与 reasoningEffort(与结果同口径)。
|
|
284
|
+
const ignored = result ? result.ignored.slice() : []
|
|
285
|
+
if (!settled && request.continuable) {
|
|
286
|
+
if (request.preset !== '' && request.preset !== 'inherit' && !ignored.includes('preset')) ignored.push('preset')
|
|
287
|
+
if (request.reasoningEffort !== '' && !ignored.includes('reasoningEffort')) ignored.push('reasoningEffort')
|
|
288
|
+
}
|
|
289
|
+
// P2(请求≠生效):被忽略字段的生效值 = 确定性降级值。
|
|
290
|
+
if (ignored.includes('preset')) eff.preset = 'inherit'
|
|
291
|
+
if (ignored.includes('reasoningEffort')) eff.reasoningEffort = '(default)'
|
|
292
|
+
|
|
293
|
+
let kindLabel
|
|
294
|
+
const kindPrefix = request.continuable ? ZH.cont : (request.runInBackground ? ZH.toolBackground : ZH.toolForeground)
|
|
295
|
+
// 运行/结算标签判据 = settled(`'kind' in block`):已结算块即使 render 为
|
|
296
|
+
// 旧格式不可解析(result===null)也绝不落入「运行中」分支。
|
|
297
|
+
if (settled) {
|
|
298
|
+
if (result) {
|
|
299
|
+
if (result.kind === 'background') kindLabel = `${ZH.toolBackground} #${result.id}`
|
|
300
|
+
else if (result.kind === 'continuable') kindLabel = `${ZH.cont} #${result.id}(${ZH.canContinue})`
|
|
301
|
+
else kindLabel = ZH.toolForeground
|
|
302
|
+
} else {
|
|
303
|
+
// 已结算但结果不可解析:kind 由请求值推导,不带「运行中」。
|
|
304
|
+
kindLabel = kindPrefix
|
|
146
305
|
}
|
|
306
|
+
} else {
|
|
307
|
+
kindLabel = `${kindPrefix}(${ZH.toolRunning})`
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const chips = []
|
|
311
|
+
chips.push(chip('profile', ZH.chipProfile, profileZh(eff.profile)))
|
|
312
|
+
// preset 继承值('inherit' 或未请求即空串,均渲染为「继承父」)与
|
|
313
|
+
// provider·model / effort 一样弱显。
|
|
314
|
+
chips.push(chip('preset', ZH.chipPreset, presetZh(eff.preset), { dim: eff.preset === 'inherit' || eff.preset === '' }))
|
|
315
|
+
const p = rawOrInherit(eff.provider)
|
|
316
|
+
const m = rawOrInherit(eff.model)
|
|
317
|
+
chips.push(chip('providerModel', `${ZH.chipProvider}·${ZH.chipModel}`, p === ZH.inherit && m === ZH.inherit ? ZH.inherit : `${p} / ${m}`, { dim: p === ZH.inherit && m === ZH.inherit }))
|
|
318
|
+
chips.push(chip('effort', ZH.chipEffort, effortZh(eff.reasoningEffort), { dim: eff.reasoningEffort === '' || eff.reasoningEffort === '(default)' }))
|
|
319
|
+
// tokenTier:宿主当前结果里未产出(§8.3 属中期切片);字段出现即渲染(向前兼容)。
|
|
320
|
+
let tier = ''
|
|
321
|
+
if (result && result.fields && typeof result.fields.tokenTier === 'string' && result.fields.tokenTier !== '') {
|
|
322
|
+
tier = result.fields.tokenTier
|
|
323
|
+
} else if (block && block.meta && typeof block.meta === 'object' && typeof block.meta.tokenTier === 'string' && block.meta.tokenTier !== '') {
|
|
324
|
+
tier = block.meta.tokenTier
|
|
147
325
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
326
|
+
if (tier !== '') chips.push(chip('tier', ZH.chipTier, ZH.tierZh[tier] || tier))
|
|
327
|
+
// ignored 项:警示色、置于 chip 行末尾(BP-03 根治:非静默)。
|
|
328
|
+
for (const key of ignored) {
|
|
329
|
+
const reqVal = request[key] || ''
|
|
330
|
+
chips.push(el('span', {
|
|
331
|
+
key: `ignored-${key}`,
|
|
332
|
+
className: 'sap-chip sap-chipWarn',
|
|
333
|
+
title: reqVal !== '' ? `请求值 "${reqVal}" 已忽略` : '请求值已忽略'
|
|
334
|
+
}, `⬆${key}(${ZH.ignoredMark})`))
|
|
151
335
|
}
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const continuable = args.continuable === true
|
|
159
|
-
const text = `派发子 Agent:方案=${profile} · 预设=${preset} · 提供方=${provider} · 模型=${model} · 推理强度=${reasoningEffort}${continuable ? ' · 可续跑' : ''}`
|
|
160
|
-
return el('div', { className: 'sap-toolview' }, text)
|
|
336
|
+
|
|
337
|
+
const text = `${ZH.toolLabel}:${kindLabel} · 方案=${profileZh(eff.profile)} · 预设=${presetZh(eff.preset)} · 提供方=${p} · 模型=${m} · 推理强度=${effortZh(eff.reasoningEffort)}`
|
|
338
|
+
return el('div', { className: 'sap-toolview' },
|
|
339
|
+
el('div', { className: 'sap-chips' }, chips),
|
|
340
|
+
el('div', { className: 'sap-toolText' }, text)
|
|
341
|
+
)
|
|
161
342
|
}
|
|
162
343
|
|
|
163
344
|
// ── settings page ───────────────────────────────────────────────────────
|
|
164
345
|
|
|
165
346
|
const EMPTY_FORM = { id: '', description: '', preset: '', provider: '', model: '', reasoningEffort: '', toolMode: 'none', toolList: [] }
|
|
166
|
-
const META_DEFS = [
|
|
167
|
-
['预设', 'preset'],
|
|
168
|
-
['提供方', 'provider'],
|
|
169
|
-
['模型', 'model'],
|
|
170
|
-
['推理强度', 'reasoningEffort'],
|
|
171
|
-
]
|
|
172
347
|
const EFFORT_ZH = { off: '关闭', high: '高', max: '最大' }
|
|
173
348
|
const effortLabel = (effort) => {
|
|
174
349
|
const id = effort && effort.id
|
|
@@ -183,6 +358,7 @@ window.__ModuleLoader__.load({
|
|
|
183
358
|
const [editingId, setEditingId] = useState(null)
|
|
184
359
|
const [error, setError] = useState('')
|
|
185
360
|
const [savedNotice, setSavedNotice] = useState('')
|
|
361
|
+
const [persistWarning, setPersistWarning] = useState('')
|
|
186
362
|
const [form, setForm] = useState(EMPTY_FORM)
|
|
187
363
|
const [tools, setTools] = useState([])
|
|
188
364
|
|
|
@@ -255,16 +431,30 @@ window.__ModuleLoader__.load({
|
|
|
255
431
|
return { ...prev, toolList: [...cur.filter((n) => !toRemove.has(n)), ...toAdd] }
|
|
256
432
|
})
|
|
257
433
|
|
|
434
|
+
// D5/B1 写路径契约:HTTP 200 但 persisted:false 时 host 附
|
|
435
|
+
// persistWarning「已保存但未持久化」——这里改为 amber 警示(非绿色成功态)。
|
|
436
|
+
const applyWrite = (data, successNotice) => {
|
|
437
|
+
if (data && data.persisted === false) {
|
|
438
|
+
setSavedNotice('')
|
|
439
|
+
setPersistWarning(typeof data.persistWarning === 'string' && data.persistWarning !== '' ? data.persistWarning : ZH.persistFail)
|
|
440
|
+
return
|
|
441
|
+
}
|
|
442
|
+
setPersistWarning('')
|
|
443
|
+
if (typeof successNotice === 'string' && successNotice !== '') setSavedNotice(successNotice)
|
|
444
|
+
else setSavedNotice('')
|
|
445
|
+
}
|
|
446
|
+
|
|
258
447
|
const openAdd = () => {
|
|
259
448
|
setError('')
|
|
260
449
|
setSavedNotice('')
|
|
450
|
+
setPersistWarning('')
|
|
261
451
|
setForm(EMPTY_FORM)
|
|
262
452
|
setEditingId(null)
|
|
263
453
|
setAdding(true)
|
|
264
454
|
}
|
|
265
455
|
|
|
266
456
|
const openEdit = (profile) => () => {
|
|
267
|
-
setError(''); setSavedNotice('')
|
|
457
|
+
setError(''); setSavedNotice(''); setPersistWarning('')
|
|
268
458
|
const tf = profile.toolFilter && typeof profile.toolFilter === 'object' ? profile.toolFilter : {}
|
|
269
459
|
const allowArr = Array.isArray(tf.allow) ? tf.allow.slice() : []
|
|
270
460
|
const denyArr = Array.isArray(tf.deny) ? tf.deny.slice() : []
|
|
@@ -287,6 +477,7 @@ window.__ModuleLoader__.load({
|
|
|
287
477
|
setAdding(false)
|
|
288
478
|
setEditingId(null)
|
|
289
479
|
setError('')
|
|
480
|
+
setPersistWarning('')
|
|
290
481
|
setForm(EMPTY_FORM)
|
|
291
482
|
}
|
|
292
483
|
|
|
@@ -312,9 +503,9 @@ window.__ModuleLoader__.load({
|
|
|
312
503
|
if (existing.enabled === false) payload.enabled = false
|
|
313
504
|
}
|
|
314
505
|
api('/add', payload)
|
|
315
|
-
.then(() => {
|
|
506
|
+
.then((data) => {
|
|
316
507
|
setForm(EMPTY_FORM); setAdding(false); setEditingId(null); setError('')
|
|
317
|
-
|
|
508
|
+
applyWrite(data, `已保存 ${payload.id}`)
|
|
318
509
|
refresh()
|
|
319
510
|
})
|
|
320
511
|
.catch((err) => setError(String((err && err.message) || err)))
|
|
@@ -323,7 +514,7 @@ window.__ModuleLoader__.load({
|
|
|
323
514
|
const setProfileEnabled = (profile, next) => () => {
|
|
324
515
|
setError('')
|
|
325
516
|
api('/set-profile-enabled', { id: profile.id, enabled: next })
|
|
326
|
-
.then(() => refresh())
|
|
517
|
+
.then((data) => { applyWrite(data, ''); refresh() })
|
|
327
518
|
.catch((err) => setError(String((err && err.message) || err)))
|
|
328
519
|
}
|
|
329
520
|
|
|
@@ -331,16 +522,16 @@ window.__ModuleLoader__.load({
|
|
|
331
522
|
if (!window.confirm('将所有内置方案重置为默认配置?已删除的内置也会恢复。')) return
|
|
332
523
|
setError('')
|
|
333
524
|
api('/reset-all')
|
|
334
|
-
.then(() => {
|
|
525
|
+
.then((data) => { applyWrite(data, '已重置所有内置方案'); refresh() })
|
|
335
526
|
.catch((err) => setError(String((err && err.message) || err)))
|
|
336
527
|
}
|
|
337
528
|
|
|
338
529
|
const remove = (profile) => () => {
|
|
339
530
|
if (!window.confirm(`删除方案「${profile.name || profile.id}」?${profile.builtin === true ? '内置方案删除后可在上方「重置内置方案」恢复' : ''}`)) return
|
|
340
531
|
api('/remove', { id: profile.id })
|
|
341
|
-
.then(() => {
|
|
532
|
+
.then((data) => {
|
|
342
533
|
setError('')
|
|
343
|
-
|
|
534
|
+
applyWrite(data, `已删除 ${profile.id}`)
|
|
344
535
|
refresh()
|
|
345
536
|
})
|
|
346
537
|
.catch((err) => setError(String((err && err.message) || err)))
|
|
@@ -361,21 +552,23 @@ window.__ModuleLoader__.load({
|
|
|
361
552
|
const builtin = profile.builtin === true
|
|
362
553
|
const isDisabled = profile.enabled === false
|
|
363
554
|
const hasName = typeof profile.name === 'string' && profile.name !== '' && profile.name !== String(profile.id)
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
)
|
|
371
|
-
|
|
555
|
+
const hasPreset = typeof profile.preset === 'string' && profile.preset !== ''
|
|
556
|
+
const hasModel = typeof profile.model === 'string' && profile.model !== ''
|
|
557
|
+
const hasEffort = typeof profile.reasoningEffort === 'string' && profile.reasoningEffort !== ''
|
|
558
|
+
const chipItems = [
|
|
559
|
+
chip('preset', ZH.chipPreset, presetZh(profile.preset), { dim: !hasPreset }),
|
|
560
|
+
chip('model', ZH.chipModel, rawOrInherit(profile.model), { dim: !hasModel }),
|
|
561
|
+
chip('effort', ZH.chipEffort, effortZh(profile.reasoningEffort), { dim: !hasEffort }),
|
|
562
|
+
isDisabled
|
|
563
|
+
? el('span', { key: 'state', className: 'sap-chip sap-chipDisabled' }, ZH.chipDisabled)
|
|
564
|
+
: el('span', { key: 'state', className: 'sap-chip sap-chipSuccess' }, ZH.chipEnabled)
|
|
565
|
+
]
|
|
372
566
|
return el('li', { key: String(profile.id), className: 'sap-rowCard' },
|
|
373
567
|
el('div', { className: 'sap-rowHead' },
|
|
374
568
|
el('span', { className: 'sap-rowIdentity' },
|
|
375
569
|
el('span', { className: 'sap-rowName' }, hasName ? profile.name : String(profile.id)),
|
|
376
570
|
hasName ? el('span', { className: 'sap-rowId' }, String(profile.id)) : null,
|
|
377
|
-
builtin ? el('span', { className: 'sap-rowTag' }, '内置') : null
|
|
378
|
-
isDisabled ? el('span', { className: 'sap-rowTag' }, '已禁用') : null
|
|
571
|
+
builtin ? el('span', { className: 'sap-rowTag' }, '内置') : null
|
|
379
572
|
),
|
|
380
573
|
el('span', { className: 'sap-rowActions' },
|
|
381
574
|
el('label', { className: 'sap-switch', title: '启用/禁用' },
|
|
@@ -386,10 +579,10 @@ window.__ModuleLoader__.load({
|
|
|
386
579
|
el('button', { className: 'sap-dangerButton', onClick: remove(profile) }, '删除')
|
|
387
580
|
)
|
|
388
581
|
),
|
|
582
|
+
el('div', { className: 'sap-chips' }, chipItems),
|
|
389
583
|
typeof profile.description === 'string' && profile.description !== ''
|
|
390
584
|
? el('p', { className: 'sap-desc' }, profile.description)
|
|
391
|
-
: null
|
|
392
|
-
el('div', { className: 'sap-meta' }, metaItems)
|
|
585
|
+
: null
|
|
393
586
|
)
|
|
394
587
|
})
|
|
395
588
|
|
|
@@ -488,6 +681,7 @@ window.__ModuleLoader__.load({
|
|
|
488
681
|
!enabled ? el('p', { className: 'sap-notice' }, '插件已禁用:「派发子 Agent」工具已从模型工具列表中移除,打开开关即可恢复。') : null,
|
|
489
682
|
error !== '' ? el('p', { className: 'sap-notice' }, error) : null,
|
|
490
683
|
savedNotice !== '' ? el('p', { className: 'sap-savedNotice', role: 'status' }, savedNotice) : null,
|
|
684
|
+
persistWarning !== '' ? el('p', { className: 'sap-warn', role: 'alert' }, `${persistWarning}${ZH.persistFailHint}`) : null,
|
|
491
685
|
el('ul', { className: 'sap-rows' }, rows),
|
|
492
686
|
el('div', { className: 'sap-addBlock' },
|
|
493
687
|
adding
|