dsh-run2skill 0.1.0-alpha → 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.en.md +137 -0
- package/README.md +82 -32
- package/lib/client.js +2462 -1610
- package/lib/index.d.ts +3956 -265
- package/lib/index.js +19055 -5533
- package/package.json +20 -6
package/lib/client.js
CHANGED
|
@@ -5,6 +5,7 @@ window.__ModuleLoader__.load({
|
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
let react = require("react");
|
|
8
|
+
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
8
9
|
//#region node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/core/core.js
|
|
9
10
|
var _a$1;
|
|
10
11
|
function $constructor(name, initializer, params) {
|
|
@@ -4153,11 +4154,200 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4153
4154
|
//#endregion
|
|
4154
4155
|
//#region src/adapters/dsh-connection/observe-summary-rpc.ts
|
|
4155
4156
|
const RUN2SKILL_RPC_CHANNEL = "/run2skill";
|
|
4156
|
-
const OBSERVE_SUMMARY_ENDPOINT = "observe-summary";
|
|
4157
4157
|
object({ apiVersion: literal(1) }).strict();
|
|
4158
4158
|
//#endregion
|
|
4159
|
-
//#region src/client/
|
|
4160
|
-
const
|
|
4159
|
+
//#region src/client/dialog-focus.ts
|
|
4160
|
+
const focusableSelector = [
|
|
4161
|
+
"button:not([disabled])",
|
|
4162
|
+
"[href]",
|
|
4163
|
+
"input:not([disabled])",
|
|
4164
|
+
"select:not([disabled])",
|
|
4165
|
+
"textarea:not([disabled])",
|
|
4166
|
+
"[tabindex]:not([tabindex=\"-1\"])"
|
|
4167
|
+
].join(",");
|
|
4168
|
+
function trapDialogTab(key, backwards, preventDefault, root, activeElement) {
|
|
4169
|
+
if (key !== "Tab") return;
|
|
4170
|
+
const focusable = Array.from(root.querySelectorAll(focusableSelector));
|
|
4171
|
+
if (focusable.length === 0) {
|
|
4172
|
+
preventDefault();
|
|
4173
|
+
return;
|
|
4174
|
+
}
|
|
4175
|
+
const first = focusable[0];
|
|
4176
|
+
const last = focusable.at(-1);
|
|
4177
|
+
if (!focusable.includes(activeElement)) {
|
|
4178
|
+
preventDefault();
|
|
4179
|
+
(backwards ? last : first).focus();
|
|
4180
|
+
return;
|
|
4181
|
+
}
|
|
4182
|
+
if (backwards && activeElement === first) {
|
|
4183
|
+
preventDefault();
|
|
4184
|
+
last.focus();
|
|
4185
|
+
} else if (!backwards && activeElement === last) {
|
|
4186
|
+
preventDefault();
|
|
4187
|
+
first.focus();
|
|
4188
|
+
}
|
|
4189
|
+
}
|
|
4190
|
+
//#endregion
|
|
4191
|
+
//#region src/client/style-lifecycle.ts
|
|
4192
|
+
const RUN2SKILL_STYLE_ID = "dsh-run2skill/native-ui";
|
|
4193
|
+
function styleSelector() {
|
|
4194
|
+
return `style[data-plugin-css="${RUN2SKILL_STYLE_ID}"]`;
|
|
4195
|
+
}
|
|
4196
|
+
function upsertRun2skillStyle(css) {
|
|
4197
|
+
const existing = document.querySelector(styleSelector());
|
|
4198
|
+
const tag = existing ?? document.createElement("style");
|
|
4199
|
+
tag.dataset.plugin = "dsh-run2skill";
|
|
4200
|
+
tag.dataset.pluginCss = RUN2SKILL_STYLE_ID;
|
|
4201
|
+
tag.textContent = css;
|
|
4202
|
+
if (existing === null) document.head.appendChild(tag);
|
|
4203
|
+
return tag;
|
|
4204
|
+
}
|
|
4205
|
+
function acquireRun2skillStyle() {
|
|
4206
|
+
if (typeof document === "undefined") return () => void 0;
|
|
4207
|
+
const tag = document.querySelector(styleSelector());
|
|
4208
|
+
if (tag === null) return () => void 0;
|
|
4209
|
+
const count = Number(tag.dataset.pluginMounts ?? "0");
|
|
4210
|
+
tag.dataset.pluginMounts = String(Number.isSafeInteger(count) && count >= 0 ? count + 1 : 1);
|
|
4211
|
+
let disposed = false;
|
|
4212
|
+
return () => {
|
|
4213
|
+
if (disposed) return;
|
|
4214
|
+
disposed = true;
|
|
4215
|
+
const current = Number(tag.dataset.pluginMounts ?? "1");
|
|
4216
|
+
if (current <= 1) tag.remove();
|
|
4217
|
+
else tag.dataset.pluginMounts = String(current - 1);
|
|
4218
|
+
};
|
|
4219
|
+
}
|
|
4220
|
+
//#endregion
|
|
4221
|
+
//#region \0run2skill-css:run2skill-settings-page.module.css.mjs
|
|
4222
|
+
const css = ".run2skill_page_H3ahsG{width:min(100%,1040px);color:var(--dsw-alias-label-primary);gap:24px;padding:16px 0 48px;display:grid}.run2skill_intro_H3ahsG,.run2skill_muted_H3ahsG{color:var(--dsw-alias-label-secondary);margin:0;font-size:13px;line-height:1.5}.run2skill_section_H3ahsG{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);box-shadow:0 1px 2px var(--dsw-alias-border-l1);border-radius:16px;overflow:hidden}.run2skill_sectionHeader_H3ahsG{box-sizing:border-box;height:auto;min-height:56px;padding:14px 20px}.run2skill_sectionTitle_H3ahsG{font-size:15px}.run2skill_sectionBody_H3ahsG{gap:18px;padding:12px 24px 24px 56px;display:grid}.run2skill_empty_H3ahsG{min-height:56px;color:var(--dsw-alias-label-secondary);place-items:center;margin:0;font-size:13px;display:grid}.run2skill_toolbar_H3ahsG,.run2skill_actions_H3ahsG,.run2skill_modeGroup_H3ahsG{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.run2skill_attentionList_H3ahsG,.run2skill_proposalList_H3ahsG,.run2skill_activityList_H3ahsG{gap:8px;margin:0;padding:0;list-style:none;display:grid}.run2skill_activityItem_H3ahsG{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:12px;justify-content:space-between;align-items:center;gap:12px;padding:12px 14px;display:flex}.run2skill_activitySummary_H3ahsG{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.run2skill_proposalLayout_H3ahsG{grid-template-columns:minmax(220px,.8fr) minmax(0,1.6fr);gap:20px;display:grid}.run2skill_proposalListButton_H3ahsG{text-align:start;white-space:normal;justify-content:flex-start;width:100%}.run2skill_detail_H3ahsG{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:12px;min-width:0;max-height:640px;padding:18px;overflow:auto}.run2skill_detail_H3ahsG pre{white-space:pre-wrap;unicode-bidi:isolate;overflow:auto}.run2skill_status_H3ahsG{min-height:20px;margin:0;font-size:13px}.run2skill_danger_H3ahsG{color:var(--dsw-alias-state-error-primary)}.run2skill_backdrop_H3ahsG,.run2skill_rejectOverlay_H3ahsG{background:var(--dsw-alias-bg-mask-1);position:fixed;inset:0}.run2skill_backdrop_H3ahsG{z-index:999}.run2skill_rejectOverlay_H3ahsG{z-index:1001;place-items:center;padding:16px;display:grid}.run2skill_legacyDialog_H3ahsG{z-index:1000;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);box-shadow:0 16px 48px var(--dsw-alias-bg-mask-1);border-radius:16px;grid-template-columns:minmax(16rem,24rem) minmax(0,1fr);gap:16px;padding:16px;display:grid;position:fixed;inset:5vh 5vw;overflow:hidden}.run2skill_blocked_H3ahsG{pointer-events:none;display:contents}.run2skill_queue_H3ahsG,.run2skill_legacyDetail_H3ahsG{min-width:0;overflow:auto}.run2skill_legacyHeader_H3ahsG{justify-content:space-between;align-items:center;gap:8px;display:flex}.run2skill_legacyList_H3ahsG{margin:0;padding:0;list-style:none}.run2skill_rejectCard_H3ahsG{background:var(--dsw-alias-bg-layer-1);border-radius:16px;max-width:32rem;padding:16px}.run2skill_textBlock_H3ahsG{white-space:pre-wrap;overflow-wrap:anywhere;unicode-bidi:isolate;overflow:auto}.run2skill_previewFacts_H3ahsG{grid-template-columns:max-content 1fr;gap:6px 12px;display:grid}.run2skill_previewFacts_H3ahsG dt,.run2skill_previewFacts_H3ahsG dd{margin:0}@media (width<=720px){.run2skill_proposalLayout_H3ahsG{grid-template-columns:1fr}.run2skill_sectionBody_H3ahsG{padding:8px 18px 20px}.run2skill_activityItem_H3ahsG{flex-direction:column;align-items:flex-start}}";
|
|
4223
|
+
if (typeof document !== "undefined") upsertRun2skillStyle(css);
|
|
4224
|
+
var _run2skill_css_run2skill_settings_page_module_css_default = {
|
|
4225
|
+
"danger": "run2skill_danger_H3ahsG",
|
|
4226
|
+
"rejectOverlay": "run2skill_rejectOverlay_H3ahsG",
|
|
4227
|
+
"backdrop": "run2skill_backdrop_H3ahsG",
|
|
4228
|
+
"sectionBody": "run2skill_sectionBody_H3ahsG",
|
|
4229
|
+
"legacyList": "run2skill_legacyList_H3ahsG",
|
|
4230
|
+
"muted": "run2skill_muted_H3ahsG",
|
|
4231
|
+
"activitySummary": "run2skill_activitySummary_H3ahsG",
|
|
4232
|
+
"detail": "run2skill_detail_H3ahsG",
|
|
4233
|
+
"queue": "run2skill_queue_H3ahsG",
|
|
4234
|
+
"section": "run2skill_section_H3ahsG",
|
|
4235
|
+
"rejectCard": "run2skill_rejectCard_H3ahsG",
|
|
4236
|
+
"page": "run2skill_page_H3ahsG",
|
|
4237
|
+
"activityList": "run2skill_activityList_H3ahsG",
|
|
4238
|
+
"proposalLayout": "run2skill_proposalLayout_H3ahsG",
|
|
4239
|
+
"proposalListButton": "run2skill_proposalListButton_H3ahsG",
|
|
4240
|
+
"sectionHeader": "run2skill_sectionHeader_H3ahsG",
|
|
4241
|
+
"status": "run2skill_status_H3ahsG",
|
|
4242
|
+
"modeGroup": "run2skill_modeGroup_H3ahsG",
|
|
4243
|
+
"intro": "run2skill_intro_H3ahsG",
|
|
4244
|
+
"proposalList": "run2skill_proposalList_H3ahsG",
|
|
4245
|
+
"empty": "run2skill_empty_H3ahsG",
|
|
4246
|
+
"actions": "run2skill_actions_H3ahsG",
|
|
4247
|
+
"legacyDetail": "run2skill_legacyDetail_H3ahsG",
|
|
4248
|
+
"previewFacts": "run2skill_previewFacts_H3ahsG",
|
|
4249
|
+
"toolbar": "run2skill_toolbar_H3ahsG",
|
|
4250
|
+
"blocked": "run2skill_blocked_H3ahsG",
|
|
4251
|
+
"textBlock": "run2skill_textBlock_H3ahsG",
|
|
4252
|
+
"activityItem": "run2skill_activityItem_H3ahsG",
|
|
4253
|
+
"attentionList": "run2skill_attentionList_H3ahsG",
|
|
4254
|
+
"legacyDialog": "run2skill_legacyDialog_H3ahsG",
|
|
4255
|
+
"legacyHeader": "run2skill_legacyHeader_H3ahsG",
|
|
4256
|
+
"sectionTitle": "run2skill_sectionTitle_H3ahsG"
|
|
4257
|
+
};
|
|
4258
|
+
//#endregion
|
|
4259
|
+
//#region src/client/purge-settings.ts
|
|
4260
|
+
const PURGE_IDLE_POLL_INTERVAL_MS = 1e4;
|
|
4261
|
+
const PURGE_ACTIVE_POLL_INTERVAL_MS = 2e3;
|
|
4262
|
+
const previewId = string().regex(/^purv_[a-f0-9]{64}$/);
|
|
4263
|
+
const purgeId = string().regex(/^purge_[a-f0-9]{64}$/);
|
|
4264
|
+
const digest = string().regex(/^[a-f0-9]{64}$/);
|
|
4265
|
+
const isoDateTime = string().datetime({ offset: true });
|
|
4266
|
+
const count = number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
|
|
4267
|
+
const projectBinding = object({
|
|
4268
|
+
scope: literal("PROJECT"),
|
|
4269
|
+
workspaceId: string().min(1).max(256)
|
|
4270
|
+
}).strict();
|
|
4271
|
+
const scopeBinding = discriminatedUnion("scope", [
|
|
4272
|
+
object({ scope: literal("ALL") }).strict(),
|
|
4273
|
+
projectBinding,
|
|
4274
|
+
object({ scope: literal("USER") }).strict()
|
|
4275
|
+
]);
|
|
4276
|
+
const previewSchema = object({
|
|
4277
|
+
apiVersion: literal(1),
|
|
4278
|
+
previewId,
|
|
4279
|
+
digest,
|
|
4280
|
+
expiresAt: isoDateTime,
|
|
4281
|
+
scopeBinding,
|
|
4282
|
+
hideBefore: isoDateTime,
|
|
4283
|
+
workItemCount: count,
|
|
4284
|
+
lineageCount: count,
|
|
4285
|
+
derivedRecordCount: count,
|
|
4286
|
+
blockedOrUnprovenCount: count,
|
|
4287
|
+
willDelete: array(object({
|
|
4288
|
+
kind: _enum(["WORK_ITEMS", "LINEAGES"]),
|
|
4289
|
+
count
|
|
4290
|
+
}).strict()).length(2),
|
|
4291
|
+
willKeep: array(object({
|
|
4292
|
+
reason: _enum([
|
|
4293
|
+
"KEEP_NEW",
|
|
4294
|
+
"KEEP_SCOPE",
|
|
4295
|
+
"KEEP_UNPROVEN"
|
|
4296
|
+
]),
|
|
4297
|
+
count
|
|
4298
|
+
}).strict()).length(3),
|
|
4299
|
+
busyPublicationCount: count
|
|
4300
|
+
}).strict();
|
|
4301
|
+
const receiptSchema = object({
|
|
4302
|
+
apiVersion: literal(1),
|
|
4303
|
+
purgeId,
|
|
4304
|
+
state: _enum(["COMPLETED", "IN_PROGRESS"]),
|
|
4305
|
+
phase: _enum([
|
|
4306
|
+
"HIDING",
|
|
4307
|
+
"DELETING_LINEAGES",
|
|
4308
|
+
"DELETING_WORK_ITEMS",
|
|
4309
|
+
"VERIFYING"
|
|
4310
|
+
]).optional(),
|
|
4311
|
+
deletedWorkItems: count,
|
|
4312
|
+
deletedLineages: count
|
|
4313
|
+
}).strict();
|
|
4314
|
+
const statusSchema = discriminatedUnion("state", [object({
|
|
4315
|
+
apiVersion: literal(1),
|
|
4316
|
+
state: literal("IDLE")
|
|
4317
|
+
}).strict(), object({
|
|
4318
|
+
apiVersion: literal(1),
|
|
4319
|
+
state: literal("IN_PROGRESS"),
|
|
4320
|
+
purgeId,
|
|
4321
|
+
hideBefore: isoDateTime,
|
|
4322
|
+
startedAt: isoDateTime,
|
|
4323
|
+
phase: _enum([
|
|
4324
|
+
"HIDING",
|
|
4325
|
+
"DELETING_LINEAGES",
|
|
4326
|
+
"DELETING_WORK_ITEMS",
|
|
4327
|
+
"VERIFYING"
|
|
4328
|
+
]),
|
|
4329
|
+
deletedWorkItems: count,
|
|
4330
|
+
deletedLineages: count,
|
|
4331
|
+
lastError: object({
|
|
4332
|
+
code: string().regex(/^[A-Z0-9_]+$/),
|
|
4333
|
+
occurredAt: isoDateTime
|
|
4334
|
+
}).strict().optional()
|
|
4335
|
+
}).strict()]);
|
|
4336
|
+
const errorSchema = object({
|
|
4337
|
+
ok: literal(false),
|
|
4338
|
+
error: object({
|
|
4339
|
+
code: string().min(1).max(128),
|
|
4340
|
+
message: string().max(512),
|
|
4341
|
+
details: object({ busyPublicationCount: count.optional() }).strict().optional()
|
|
4342
|
+
}).strict()
|
|
4343
|
+
}).strict();
|
|
4344
|
+
var PurgeResponseError = class extends Error {
|
|
4345
|
+
facts;
|
|
4346
|
+
constructor(facts) {
|
|
4347
|
+
super(facts.code);
|
|
4348
|
+
this.facts = facts;
|
|
4349
|
+
}
|
|
4350
|
+
};
|
|
4161
4351
|
function browserEnvironment$2() {
|
|
4162
4352
|
return {
|
|
4163
4353
|
isVisible: () => typeof document === "undefined" || document.visibilityState === "visible",
|
|
@@ -4165,13 +4355,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4165
4355
|
clearInterval: (handle) => {
|
|
4166
4356
|
globalThis.clearInterval(handle);
|
|
4167
4357
|
},
|
|
4168
|
-
onFocus: (listener) => {
|
|
4169
|
-
if (typeof window === "undefined") return () => void 0;
|
|
4170
|
-
window.addEventListener("focus", listener);
|
|
4171
|
-
return () => {
|
|
4172
|
-
window.removeEventListener("focus", listener);
|
|
4173
|
-
};
|
|
4174
|
-
},
|
|
4175
4358
|
onVisibilityChange: (listener) => {
|
|
4176
4359
|
if (typeof document === "undefined") return () => void 0;
|
|
4177
4360
|
document.addEventListener("visibilitychange", listener);
|
|
@@ -4181,25 +4364,63 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4181
4364
|
}
|
|
4182
4365
|
};
|
|
4183
4366
|
}
|
|
4184
|
-
function
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4367
|
+
function parseValue$1(schema, response) {
|
|
4368
|
+
const success = object({
|
|
4369
|
+
ok: literal(true),
|
|
4370
|
+
value: schema
|
|
4371
|
+
}).strict().safeParse(response);
|
|
4372
|
+
if (success.success) return success.data.value;
|
|
4373
|
+
const failure = errorSchema.safeParse(response);
|
|
4374
|
+
if (failure.success) {
|
|
4375
|
+
const busy = failure.data.error.details?.busyPublicationCount;
|
|
4376
|
+
throw new PurgeResponseError({
|
|
4377
|
+
code: failure.data.error.code,
|
|
4378
|
+
...typeof busy === "number" && Number.isSafeInteger(busy) && busy >= 0 ? { busyPublicationCount: busy } : {}
|
|
4379
|
+
});
|
|
4380
|
+
}
|
|
4381
|
+
throw new PurgeResponseError({ code: "PURGE_STORAGE_UNAVAILABLE" });
|
|
4188
4382
|
}
|
|
4189
|
-
|
|
4383
|
+
function completionAnnouncement(scope, receipt) {
|
|
4384
|
+
return `${scope === "ALL" ? "Run2Skill 缓存" : scope === void 0 ? "Run2Skill 数据" : `${scope} Run2Skill 数据`}清理完成:${String(receipt.deletedWorkItems)} 条待处理数据,${String(receipt.deletedLineages)} 条 Skill 关联记录。`;
|
|
4385
|
+
}
|
|
4386
|
+
function polledCompletionAnnouncement(scope) {
|
|
4387
|
+
return scope === "ALL" ? "Run2Skill 缓存清理完成。" : `${scope === void 0 ? "" : `${scope} `}Run2Skill 数据清理完成。`;
|
|
4388
|
+
}
|
|
4389
|
+
function errorAnnouncement(error, durableBoundary = false) {
|
|
4390
|
+
if (error.code === "PURGE_PREVIEW_STALE") return "清理预览已失效,请重新预览后确认。";
|
|
4391
|
+
if (error.code === "PURGE_BUSY") return `当前有 ${String(error.busyPublicationCount ?? 0)} 份技能草稿正在保存;请等待保存完成后重新预览。`;
|
|
4392
|
+
if (error.code === "PURGE_ALREADY_RUNNING") return "已有一项数据清理正在运行,请等待完成。";
|
|
4393
|
+
if (error.code === "PURGE_SCOPE_UNAVAILABLE") return "当前作用域无法可靠确认,未执行清理。";
|
|
4394
|
+
if (error.code === "PURGE_INCOMPATIBLE") return "当前数据版本不兼容,未执行清理。";
|
|
4395
|
+
if (error.code === "PURGE_FENCE_LIMIT") return "缓存清理保护记录已达上限,未执行清理。";
|
|
4396
|
+
return durableBoundary ? "数据清理未完成;已建立的清理边界继续隐藏,可重试。" : "数据清理暂不可用;未确认已建立清理边界,请稍后重试。";
|
|
4397
|
+
}
|
|
4398
|
+
const initialState$1 = {
|
|
4399
|
+
hostDataEpoch: 0,
|
|
4400
|
+
statusPhase: "LOADING",
|
|
4401
|
+
previewPending: false,
|
|
4402
|
+
mutationPending: false,
|
|
4403
|
+
announcement: ""
|
|
4404
|
+
};
|
|
4405
|
+
var PurgeSettingsController = class {
|
|
4190
4406
|
call;
|
|
4407
|
+
getWorkspaceId;
|
|
4191
4408
|
environment;
|
|
4192
4409
|
#listeners = /* @__PURE__ */ new Set();
|
|
4193
|
-
#state =
|
|
4410
|
+
#state = initialState$1;
|
|
4194
4411
|
#started = false;
|
|
4412
|
+
#active = true;
|
|
4195
4413
|
#disposed = false;
|
|
4196
|
-
#timer;
|
|
4197
4414
|
#pending;
|
|
4198
4415
|
#abort;
|
|
4199
|
-
#
|
|
4416
|
+
#statusPending;
|
|
4417
|
+
#statusAbort;
|
|
4418
|
+
#timer;
|
|
4419
|
+
#timerDelay;
|
|
4200
4420
|
#removeVisibility;
|
|
4201
|
-
constructor(call, environment = browserEnvironment$2()) {
|
|
4421
|
+
constructor(call, getWorkspaceId, environment = browserEnvironment$2()) {
|
|
4202
4422
|
this.call = call;
|
|
4423
|
+
this.getWorkspaceId = getWorkspaceId;
|
|
4203
4424
|
this.environment = environment;
|
|
4204
4425
|
}
|
|
4205
4426
|
snapshot = () => this.#state;
|
|
@@ -4212,648 +4433,247 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4212
4433
|
start() {
|
|
4213
4434
|
if (this.#started || this.#disposed) return;
|
|
4214
4435
|
this.#started = true;
|
|
4215
|
-
this.#removeFocus = this.environment.onFocus(() => {
|
|
4216
|
-
if (this.environment.isVisible()) this.#refresh();
|
|
4217
|
-
});
|
|
4218
4436
|
this.#removeVisibility = this.environment.onVisibilityChange(() => {
|
|
4219
|
-
if (this.environment.isVisible()) {
|
|
4437
|
+
if (this.#active && this.environment.isVisible()) {
|
|
4220
4438
|
this.#schedule();
|
|
4221
|
-
this.#
|
|
4439
|
+
this.#refreshStatus();
|
|
4222
4440
|
} else this.#unschedule();
|
|
4223
4441
|
});
|
|
4224
|
-
if (this.environment.isVisible()) {
|
|
4442
|
+
if (this.#active && this.environment.isVisible()) {
|
|
4225
4443
|
this.#schedule();
|
|
4226
|
-
this.#
|
|
4444
|
+
this.#refreshStatus();
|
|
4227
4445
|
}
|
|
4228
4446
|
}
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
dispose() {
|
|
4233
|
-
if (this.#disposed) return;
|
|
4234
|
-
this.#disposed = true;
|
|
4447
|
+
pause() {
|
|
4448
|
+
if (this.#disposed || !this.#active) return;
|
|
4449
|
+
this.#active = false;
|
|
4235
4450
|
this.#unschedule();
|
|
4236
|
-
this.#
|
|
4237
|
-
this.#removeVisibility?.();
|
|
4238
|
-
this.#removeFocus = void 0;
|
|
4239
|
-
this.#removeVisibility = void 0;
|
|
4240
|
-
this.#abort?.abort();
|
|
4241
|
-
this.#listeners.clear();
|
|
4242
|
-
}
|
|
4243
|
-
#schedule() {
|
|
4244
|
-
if (this.#timer !== void 0 || this.#disposed) return;
|
|
4245
|
-
this.#timer = this.environment.setInterval(() => {
|
|
4246
|
-
this.#refresh();
|
|
4247
|
-
}, OBSERVE_SUMMARY_POLL_INTERVAL_MS);
|
|
4248
|
-
}
|
|
4249
|
-
#unschedule() {
|
|
4250
|
-
if (this.#timer === void 0) return;
|
|
4251
|
-
this.environment.clearInterval(this.#timer);
|
|
4252
|
-
this.#timer = void 0;
|
|
4451
|
+
this.#statusAbort?.abort();
|
|
4253
4452
|
}
|
|
4254
|
-
|
|
4255
|
-
if (this.#disposed || this.#
|
|
4256
|
-
|
|
4257
|
-
this.#
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
if (this.#disposed) return;
|
|
4262
|
-
if (summary === void 0) this.#markUnavailable();
|
|
4263
|
-
else this.#publish({
|
|
4264
|
-
phase: "READY",
|
|
4265
|
-
summary
|
|
4266
|
-
});
|
|
4267
|
-
} catch {
|
|
4268
|
-
if (!this.#disposed && !abort.signal.aborted) this.#markUnavailable();
|
|
4269
|
-
}
|
|
4270
|
-
})();
|
|
4271
|
-
this.#pending = pending;
|
|
4272
|
-
pending.finally(() => {
|
|
4273
|
-
if (this.#pending === pending) this.#pending = void 0;
|
|
4274
|
-
if (this.#abort === abort) this.#abort = void 0;
|
|
4453
|
+
resume() {
|
|
4454
|
+
if (this.#disposed || this.#active) return;
|
|
4455
|
+
this.#active = true;
|
|
4456
|
+
if (!this.#started || !this.environment.isVisible()) return;
|
|
4457
|
+
this.#schedule();
|
|
4458
|
+
this.whenIdle().then(() => {
|
|
4459
|
+
if (this.#active && !this.#disposed) this.#refreshStatus();
|
|
4275
4460
|
});
|
|
4276
|
-
return pending;
|
|
4277
|
-
}
|
|
4278
|
-
#markUnavailable() {
|
|
4279
|
-
this.#publish("summary" in this.#state ? {
|
|
4280
|
-
phase: "STALE",
|
|
4281
|
-
summary: this.#state.summary
|
|
4282
|
-
} : { phase: "UNAVAILABLE" });
|
|
4283
4461
|
}
|
|
4284
|
-
|
|
4285
|
-
this.#
|
|
4286
|
-
for (const listener of this.#listeners) listener();
|
|
4462
|
+
whenIdle() {
|
|
4463
|
+
return Promise.all([this.#pending ?? Promise.resolve(), this.#statusPending ?? Promise.resolve()]).then(() => void 0);
|
|
4287
4464
|
}
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
revision: positiveInteger,
|
|
4300
|
-
digest: sha256
|
|
4301
|
-
}).strict();
|
|
4302
|
-
const processingState = _enum([
|
|
4303
|
-
"READY_FOR_REVIEW",
|
|
4304
|
-
"PUBLISHING",
|
|
4305
|
-
"NEEDS_ATTENTION",
|
|
4306
|
-
"TERMINAL"
|
|
4307
|
-
]);
|
|
4308
|
-
const publicationOutcome = _enum([
|
|
4309
|
-
"PENDING_REVIEW",
|
|
4310
|
-
"DISCARDED",
|
|
4311
|
-
"NEEDS_ATTENTION",
|
|
4312
|
-
"NEEDS_REFRESH",
|
|
4313
|
-
"PUBLISHED",
|
|
4314
|
-
"PUBLISH_FAILED"
|
|
4315
|
-
]);
|
|
4316
|
-
const rootDisplay = object({
|
|
4317
|
-
state: _enum(["EXISTING", "ABSENT"]),
|
|
4318
|
-
scope: _enum(["PROJECT", "USER"]),
|
|
4319
|
-
expectedProvider: identity,
|
|
4320
|
-
expectedSource: _enum(["project-dsh", "user-dsh"]),
|
|
4321
|
-
resolverVersion: identity,
|
|
4322
|
-
rootContractVersion: identity,
|
|
4323
|
-
resolutionContractDigest: sha256,
|
|
4324
|
-
declaredRootPath: string().min(1).max(8192)
|
|
4325
|
-
}).passthrough();
|
|
4326
|
-
const targetDisplay = object({
|
|
4327
|
-
bundlePath: string().min(1).max(8192),
|
|
4328
|
-
skillFilePath: string().min(1).max(8192)
|
|
4329
|
-
}).passthrough();
|
|
4330
|
-
const actionBinding = discriminatedUnion("kind", [
|
|
4331
|
-
object({
|
|
4332
|
-
kind: literal("CREATE"),
|
|
4333
|
-
rootBinding: rootDisplay,
|
|
4334
|
-
targetBinding: targetDisplay,
|
|
4335
|
-
expectedAbsence: object({
|
|
4336
|
-
observedAt: string().min(1).max(64),
|
|
4337
|
-
flatSkillFilePath: string().min(1).max(8192)
|
|
4338
|
-
}).passthrough()
|
|
4339
|
-
}).passthrough(),
|
|
4340
|
-
object({
|
|
4341
|
-
kind: literal("MERGE"),
|
|
4342
|
-
rootBinding: rootDisplay,
|
|
4343
|
-
targetBinding: targetDisplay,
|
|
4344
|
-
baseBinding: object({
|
|
4345
|
-
candidateKey: string().regex(/^cand_[a-f0-9]{64}$/),
|
|
4346
|
-
path: string().min(1).max(8192),
|
|
4347
|
-
exactBytes: string().min(1).max(65536),
|
|
4348
|
-
bytesDigest: sha256,
|
|
4349
|
-
observedAt: string().min(1).max(64)
|
|
4350
|
-
}).passthrough()
|
|
4351
|
-
}).passthrough(),
|
|
4352
|
-
object({
|
|
4353
|
-
kind: literal("DISCARD"),
|
|
4354
|
-
coveringCandidateBinding: object({
|
|
4355
|
-
candidateKey: string().regex(/^cand_[a-f0-9]{64}$/),
|
|
4356
|
-
name: string().min(1).max(128),
|
|
4357
|
-
source: identity,
|
|
4358
|
-
content: string().min(1).max(65536),
|
|
4359
|
-
contentDigest: sha256,
|
|
4360
|
-
observedAt: string().min(1).max(64),
|
|
4361
|
-
path: string().min(1).max(8192).optional()
|
|
4362
|
-
}).passthrough()
|
|
4363
|
-
}).passthrough()
|
|
4364
|
-
]);
|
|
4365
|
-
const proposal = object({
|
|
4366
|
-
schemaVersion: literal(1),
|
|
4367
|
-
revision: positiveInteger,
|
|
4368
|
-
createdAt: string().min(1).max(64),
|
|
4369
|
-
sourceLearningProposalId: string().regex(/^lp_[a-f0-9]{64}$/),
|
|
4370
|
-
kind: _enum([
|
|
4371
|
-
"CREATE",
|
|
4372
|
-
"MERGE",
|
|
4373
|
-
"DISCARD"
|
|
4374
|
-
]),
|
|
4375
|
-
name: string().min(1).max(128),
|
|
4376
|
-
description: string().min(1).max(2048),
|
|
4377
|
-
whenToUse: string().min(1).max(4096),
|
|
4378
|
-
invocation: object({
|
|
4379
|
-
modelInvocable: literal(true),
|
|
4380
|
-
userInvocable: literal(false)
|
|
4381
|
-
}).strict(),
|
|
4382
|
-
exactSkillBytes: string().min(1).max(65536),
|
|
4383
|
-
skillBytesDigest: sha256,
|
|
4384
|
-
rendererVersion: identity,
|
|
4385
|
-
persistenceScope: _enum(["PROJECT", "USER"]),
|
|
4386
|
-
workspaceBinding: object({
|
|
4387
|
-
workspaceId: identity,
|
|
4388
|
-
canonicalPath: string().min(1).max(8192),
|
|
4389
|
-
observedAt: string().min(1).max(64)
|
|
4390
|
-
}).strict().optional(),
|
|
4391
|
-
dshHomeBinding: object({
|
|
4392
|
-
resolutionKind: _enum([
|
|
4393
|
-
"CONFIGURATION",
|
|
4394
|
-
"ENVIRONMENT",
|
|
4395
|
-
"DEFAULT"
|
|
4396
|
-
]),
|
|
4397
|
-
canonicalPath: string().min(1).max(8192),
|
|
4398
|
-
identityDigest: sha256,
|
|
4399
|
-
observedAt: string().min(1).max(64)
|
|
4400
|
-
}).strict().optional(),
|
|
4401
|
-
supportingExperienceIds: array(string().regex(/^exp_[a-f0-9]{64}$/)).min(1).max(3),
|
|
4402
|
-
catalogObservationDigest: sha256,
|
|
4403
|
-
curationRationale: string().min(1).max(4096),
|
|
4404
|
-
actionBinding,
|
|
4405
|
-
proposalId,
|
|
4406
|
-
digest: sha256
|
|
4407
|
-
}).strict().refine((value) => value.kind === value.actionBinding.kind, {
|
|
4408
|
-
path: ["actionBinding"],
|
|
4409
|
-
message: "action binding kind must match proposal kind"
|
|
4410
|
-
});
|
|
4411
|
-
const evidence = object({
|
|
4412
|
-
source: literal("USER_DIRECT"),
|
|
4413
|
-
messageSeq: nonNegativeInteger,
|
|
4414
|
-
excerpt: string().max(8192),
|
|
4415
|
-
excerptDigest: sha256,
|
|
4416
|
-
redactionKinds: array(string().min(1).max(64)).max(16),
|
|
4417
|
-
truncated: boolean()
|
|
4418
|
-
}).strict();
|
|
4419
|
-
const experience = object({
|
|
4420
|
-
experienceId: string().regex(/^exp_[a-f0-9]{64}$/),
|
|
4421
|
-
type: _enum([
|
|
4422
|
-
"CORRECTION",
|
|
4423
|
-
"CONSTRAINT",
|
|
4424
|
-
"WORKFLOW"
|
|
4425
|
-
]),
|
|
4426
|
-
lesson: string().min(1).max(4096),
|
|
4427
|
-
persistenceScope: _enum(["PROJECT", "USER"]),
|
|
4428
|
-
evidenceStrength: literal("HIGH"),
|
|
4429
|
-
supportingEvidence: array(object({
|
|
4430
|
-
messageSeq: nonNegativeInteger,
|
|
4431
|
-
excerptDigest: sha256
|
|
4432
|
-
}).strict()).min(1).max(4),
|
|
4433
|
-
contextSummary: string().min(1).max(4096).optional()
|
|
4434
|
-
}).strict();
|
|
4435
|
-
const summary = object({
|
|
4436
|
-
apiVersion: literal(1),
|
|
4437
|
-
status: _enum([
|
|
4438
|
-
"READY",
|
|
4439
|
-
"RECOVERING",
|
|
4440
|
-
"DEGRADED",
|
|
4441
|
-
"INCOMPATIBLE"
|
|
4442
|
-
]),
|
|
4443
|
-
recoveryLag: boolean(),
|
|
4444
|
-
lastHealthCode: string().min(1).max(128).optional(),
|
|
4445
|
-
queue: discriminatedUnion("completeness", [object({ completeness: literal("UNKNOWN") }).strict(), object({
|
|
4446
|
-
completeness: literal("KNOWN"),
|
|
4447
|
-
pendingReview: nonNegativeInteger,
|
|
4448
|
-
publishing: nonNegativeInteger,
|
|
4449
|
-
needsAttention: nonNegativeInteger
|
|
4450
|
-
}).strict()])
|
|
4451
|
-
}).strict();
|
|
4452
|
-
const listItem = object({
|
|
4453
|
-
workItemId,
|
|
4454
|
-
workItemRevision: positiveInteger,
|
|
4455
|
-
proposalRef,
|
|
4456
|
-
kind: _enum([
|
|
4457
|
-
"CREATE",
|
|
4458
|
-
"MERGE",
|
|
4459
|
-
"DISCARD"
|
|
4460
|
-
]),
|
|
4461
|
-
name: string().min(1).max(128),
|
|
4462
|
-
description: string().min(1).max(2048),
|
|
4463
|
-
persistenceScope: _enum(["PROJECT", "USER"]),
|
|
4464
|
-
createdAt: string().min(1).max(64),
|
|
4465
|
-
processingState: _enum([
|
|
4466
|
-
"READY_FOR_REVIEW",
|
|
4467
|
-
"PUBLISHING",
|
|
4468
|
-
"NEEDS_ATTENTION"
|
|
4469
|
-
]),
|
|
4470
|
-
publicationOutcome: _enum([
|
|
4471
|
-
"PENDING_REVIEW",
|
|
4472
|
-
"NEEDS_ATTENTION",
|
|
4473
|
-
"NEEDS_REFRESH",
|
|
4474
|
-
"PUBLISH_FAILED"
|
|
4475
|
-
])
|
|
4476
|
-
}).strict();
|
|
4477
|
-
const listPage = object({
|
|
4478
|
-
apiVersion: literal(1),
|
|
4479
|
-
items: array(listItem).max(20),
|
|
4480
|
-
nextCursor: string().regex(/^c_[0-9]+$/).optional()
|
|
4481
|
-
}).strict();
|
|
4482
|
-
const detail = object({
|
|
4483
|
-
apiVersion: literal(1),
|
|
4484
|
-
workItemId,
|
|
4485
|
-
workItemRevision: positiveInteger,
|
|
4486
|
-
processingState,
|
|
4487
|
-
reviewDecision: _enum([
|
|
4488
|
-
"PENDING",
|
|
4489
|
-
"APPROVED",
|
|
4490
|
-
"REJECTED"
|
|
4491
|
-
]),
|
|
4492
|
-
publicationOutcome,
|
|
4493
|
-
proposal,
|
|
4494
|
-
sessionCoordinate: object({
|
|
4495
|
-
rootSessionId: identity,
|
|
4496
|
-
sessionCreatedAt: nonNegativeInteger,
|
|
4497
|
-
turn: nonNegativeInteger,
|
|
4498
|
-
turnEndSeq: nonNegativeInteger
|
|
4499
|
-
}).strict(),
|
|
4500
|
-
evidenceRefs: array(evidence).max(16),
|
|
4501
|
-
experiences: array(experience).max(3)
|
|
4502
|
-
}).strict();
|
|
4503
|
-
const receipt = object({
|
|
4504
|
-
apiVersion: literal(1),
|
|
4505
|
-
workItemId,
|
|
4506
|
-
workItemRevision: positiveInteger,
|
|
4507
|
-
proposalRef,
|
|
4508
|
-
changed: boolean(),
|
|
4509
|
-
processingState,
|
|
4510
|
-
reviewDecision: _enum([
|
|
4511
|
-
"PENDING",
|
|
4512
|
-
"APPROVED",
|
|
4513
|
-
"REJECTED"
|
|
4514
|
-
]),
|
|
4515
|
-
publicationOutcome
|
|
4516
|
-
}).strict();
|
|
4517
|
-
function parseValue$1(schema, value) {
|
|
4518
|
-
const parsed = object({
|
|
4519
|
-
ok: literal(true),
|
|
4520
|
-
value: schema
|
|
4521
|
-
}).strict().safeParse(value);
|
|
4522
|
-
return parsed.success ? parsed.data.value : void 0;
|
|
4523
|
-
}
|
|
4524
|
-
function parseProposalSummary(value) {
|
|
4525
|
-
return parseValue$1(summary, value);
|
|
4526
|
-
}
|
|
4527
|
-
function parseProposalList(value) {
|
|
4528
|
-
return parseValue$1(listPage, value);
|
|
4529
|
-
}
|
|
4530
|
-
function parseProposalDetail(value) {
|
|
4531
|
-
return parseValue$1(detail, value);
|
|
4532
|
-
}
|
|
4533
|
-
function parseMutationReceipt(value) {
|
|
4534
|
-
return parseValue$1(receipt, value);
|
|
4535
|
-
}
|
|
4536
|
-
//#endregion
|
|
4537
|
-
//#region src/client/proposal-inbox.ts
|
|
4538
|
-
const PROPOSAL_POLL_INTERVAL_MS = 1e4;
|
|
4539
|
-
const PROPOSAL_ACTIVE_POLL_INTERVAL_MS = 2e3;
|
|
4540
|
-
const endpoint = {
|
|
4541
|
-
summary: "summary",
|
|
4542
|
-
list: "proposals/list",
|
|
4543
|
-
get: "proposals/get",
|
|
4544
|
-
approve: "proposals/approve",
|
|
4545
|
-
reject: "proposals/reject",
|
|
4546
|
-
retry: "proposals/retry",
|
|
4547
|
-
confirmDiscard: "coverage/confirm-discard"
|
|
4548
|
-
};
|
|
4549
|
-
function describeProposalOutcome(detail) {
|
|
4550
|
-
if (detail.publicationOutcome === "PUBLISHED") return "Skill 已发布并完成 Registry 回读";
|
|
4551
|
-
if (detail.publicationOutcome === "DISCARDED") return "Proposal 已离开待处理队列,Skill 未更改";
|
|
4552
|
-
if (detail.publicationOutcome === "NEEDS_REFRESH") return "发布绑定已变化,需要生成新的 Proposal";
|
|
4553
|
-
if (detail.publicationOutcome === "PUBLISH_FAILED") return "发布失败,可重试";
|
|
4554
|
-
if (detail.processingState === "NEEDS_ATTENTION") return "需要处理后才能继续";
|
|
4555
|
-
if (detail.processingState === "PUBLISHING") return "已批准,正在发布";
|
|
4556
|
-
return "待审核";
|
|
4557
|
-
}
|
|
4558
|
-
function describeProposalListItem(item) {
|
|
4559
|
-
return describeProposalOutcome(item);
|
|
4560
|
-
}
|
|
4561
|
-
function describeReviewDecision(decision) {
|
|
4562
|
-
if (decision === "APPROVED") return "已批准";
|
|
4563
|
-
if (decision === "REJECTED") return "已拒绝";
|
|
4564
|
-
return "待决定";
|
|
4565
|
-
}
|
|
4566
|
-
function describeProcessingState(state) {
|
|
4567
|
-
if (state === "PUBLISHING") return "正在发布";
|
|
4568
|
-
if (state === "NEEDS_ATTENTION") return "需要处理";
|
|
4569
|
-
if (state === "TERMINAL") return "已结束";
|
|
4570
|
-
return "待审核";
|
|
4571
|
-
}
|
|
4572
|
-
function describePublicationOutcome(outcome) {
|
|
4573
|
-
if (outcome === "PUBLISHED") return "已发布并完成 Registry 回读";
|
|
4574
|
-
if (outcome === "DISCARDED") return "未修改 Skill,Proposal 已离开队列";
|
|
4575
|
-
if (outcome === "NEEDS_REFRESH") return "发布绑定已变化,需要新 Proposal";
|
|
4576
|
-
if (outcome === "PUBLISH_FAILED") return "发布失败,可重试";
|
|
4577
|
-
if (outcome === "NEEDS_ATTENTION") return "发布前事实需要处理";
|
|
4578
|
-
return "尚未产生发布结果";
|
|
4579
|
-
}
|
|
4580
|
-
function browserEnvironment$1() {
|
|
4581
|
-
return {
|
|
4582
|
-
isVisible: () => typeof document === "undefined" || document.visibilityState === "visible",
|
|
4583
|
-
setInterval: (callback, delayMs) => globalThis.setInterval(callback, delayMs),
|
|
4584
|
-
clearInterval: (handle) => {
|
|
4585
|
-
globalThis.clearInterval(handle);
|
|
4586
|
-
},
|
|
4587
|
-
onFocus: (listener) => {
|
|
4588
|
-
if (typeof window === "undefined") return () => void 0;
|
|
4589
|
-
window.addEventListener("focus", listener);
|
|
4590
|
-
return () => {
|
|
4591
|
-
window.removeEventListener("focus", listener);
|
|
4592
|
-
};
|
|
4593
|
-
},
|
|
4594
|
-
onVisibilityChange: (listener) => {
|
|
4595
|
-
if (typeof document === "undefined") return () => void 0;
|
|
4596
|
-
document.addEventListener("visibilitychange", listener);
|
|
4597
|
-
return () => {
|
|
4598
|
-
document.removeEventListener("visibilitychange", listener);
|
|
4599
|
-
};
|
|
4600
|
-
},
|
|
4601
|
-
onOnline: (listener) => {
|
|
4602
|
-
if (typeof window === "undefined") return () => void 0;
|
|
4603
|
-
window.addEventListener("online", listener);
|
|
4604
|
-
return () => {
|
|
4605
|
-
window.removeEventListener("online", listener);
|
|
4606
|
-
};
|
|
4607
|
-
}
|
|
4608
|
-
};
|
|
4609
|
-
}
|
|
4610
|
-
const initialState$1 = {
|
|
4611
|
-
open: false,
|
|
4612
|
-
summaryPhase: "LOADING",
|
|
4613
|
-
listPhase: "IDLE",
|
|
4614
|
-
items: [],
|
|
4615
|
-
detailPhase: "IDLE",
|
|
4616
|
-
mutationPending: false,
|
|
4617
|
-
announcement: ""
|
|
4618
|
-
};
|
|
4619
|
-
var ProposalInboxController = class {
|
|
4620
|
-
workspaceId;
|
|
4621
|
-
call;
|
|
4622
|
-
environment;
|
|
4623
|
-
#listeners = /* @__PURE__ */ new Set();
|
|
4624
|
-
#state = initialState$1;
|
|
4625
|
-
#started = false;
|
|
4626
|
-
#disposed = false;
|
|
4627
|
-
#pending;
|
|
4628
|
-
#abort;
|
|
4629
|
-
#timer;
|
|
4630
|
-
#timerDelay;
|
|
4631
|
-
#removeFocus;
|
|
4632
|
-
#removeVisibility;
|
|
4633
|
-
#removeOnline;
|
|
4634
|
-
constructor(workspaceId, call, environment = browserEnvironment$1()) {
|
|
4635
|
-
this.workspaceId = workspaceId;
|
|
4636
|
-
this.call = call;
|
|
4637
|
-
this.environment = environment;
|
|
4638
|
-
}
|
|
4639
|
-
snapshot = () => this.#state;
|
|
4640
|
-
subscribe = (listener) => {
|
|
4641
|
-
this.#listeners.add(listener);
|
|
4642
|
-
return () => {
|
|
4643
|
-
this.#listeners.delete(listener);
|
|
4644
|
-
};
|
|
4645
|
-
};
|
|
4646
|
-
start() {
|
|
4647
|
-
if (this.#started || this.#disposed) return;
|
|
4648
|
-
this.#started = true;
|
|
4649
|
-
const refresh = () => {
|
|
4650
|
-
if (this.environment.isVisible()) this.#refreshVisible();
|
|
4651
|
-
};
|
|
4652
|
-
this.#removeFocus = this.environment.onFocus(refresh);
|
|
4653
|
-
this.#removeOnline = this.environment.onOnline(refresh);
|
|
4654
|
-
this.#removeVisibility = this.environment.onVisibilityChange(() => {
|
|
4655
|
-
if (this.environment.isVisible()) {
|
|
4656
|
-
this.#schedule();
|
|
4657
|
-
this.#refreshVisible();
|
|
4658
|
-
} else this.#unschedule();
|
|
4659
|
-
});
|
|
4660
|
-
if (this.environment.isVisible()) {
|
|
4661
|
-
this.#schedule();
|
|
4662
|
-
this.#refreshVisible();
|
|
4465
|
+
async preview(scope) {
|
|
4466
|
+
if (this.#disposed || this.#state.previewPending || this.#state.mutationPending) return;
|
|
4467
|
+
const workspaceId = this.getWorkspaceId();
|
|
4468
|
+
if (scope === "PROJECT" && workspaceId === void 0) {
|
|
4469
|
+
const error = { code: "PURGE_SCOPE_UNAVAILABLE" };
|
|
4470
|
+
this.#publish({
|
|
4471
|
+
...this.#state,
|
|
4472
|
+
error,
|
|
4473
|
+
announcement: errorAnnouncement(error)
|
|
4474
|
+
});
|
|
4475
|
+
return;
|
|
4663
4476
|
}
|
|
4664
|
-
}
|
|
4665
|
-
whenIdle() {
|
|
4666
|
-
return this.#pending ?? Promise.resolve();
|
|
4667
|
-
}
|
|
4668
|
-
async open() {
|
|
4669
|
-
if (this.#disposed) return;
|
|
4670
|
-
this.#publish({
|
|
4671
|
-
...this.#state,
|
|
4672
|
-
open: true
|
|
4673
|
-
});
|
|
4674
|
-
await this.whenIdle();
|
|
4675
|
-
if (!this.#disposed && this.#state.open) await this.#loadList();
|
|
4676
|
-
}
|
|
4677
|
-
close() {
|
|
4678
|
-
if (this.#disposed) return;
|
|
4679
|
-
this.#publish({
|
|
4680
|
-
...this.#state,
|
|
4681
|
-
open: false
|
|
4682
|
-
});
|
|
4683
|
-
}
|
|
4684
|
-
async select(proposalId) {
|
|
4685
|
-
if (this.#disposed || !/^prop_[a-f0-9]{64}$/.test(proposalId)) return;
|
|
4686
|
-
await this.whenIdle();
|
|
4687
|
-
if (this.#disposed || !this.#state.open) return;
|
|
4688
4477
|
await this.#execute(async (signal) => {
|
|
4689
4478
|
this.#publish({
|
|
4690
4479
|
...this.#state,
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4480
|
+
previewPending: true,
|
|
4481
|
+
previewScope: scope,
|
|
4482
|
+
preview: void 0,
|
|
4483
|
+
error: void 0,
|
|
4694
4484
|
announcement: ""
|
|
4695
4485
|
});
|
|
4696
|
-
const
|
|
4486
|
+
const preview = parseValue$1(previewSchema, await this.call("purge/preview", scope === "PROJECT" ? {
|
|
4487
|
+
apiVersion: 1,
|
|
4488
|
+
scope,
|
|
4489
|
+
workspaceId
|
|
4490
|
+
} : {
|
|
4697
4491
|
apiVersion: 1,
|
|
4698
|
-
|
|
4492
|
+
scope
|
|
4699
4493
|
}, signal));
|
|
4700
|
-
if (detail === void 0) throw new Error("invalid detail response");
|
|
4701
4494
|
this.#publish({
|
|
4702
4495
|
...this.#state,
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4496
|
+
previewPending: false,
|
|
4497
|
+
preview,
|
|
4498
|
+
error: void 0
|
|
4706
4499
|
});
|
|
4707
|
-
}, () => {
|
|
4500
|
+
}, (error) => {
|
|
4708
4501
|
this.#publish({
|
|
4709
4502
|
...this.#state,
|
|
4710
|
-
|
|
4711
|
-
|
|
4503
|
+
previewPending: false,
|
|
4504
|
+
preview: void 0,
|
|
4505
|
+
error,
|
|
4506
|
+
announcement: errorAnnouncement(error)
|
|
4712
4507
|
});
|
|
4713
4508
|
});
|
|
4714
4509
|
}
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
};
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4510
|
+
cancelPreview() {
|
|
4511
|
+
if (this.#disposed || this.#state.mutationPending) return;
|
|
4512
|
+
this.#publish({
|
|
4513
|
+
...this.#state,
|
|
4514
|
+
preview: void 0,
|
|
4515
|
+
previewScope: void 0,
|
|
4516
|
+
error: void 0,
|
|
4517
|
+
announcement: ""
|
|
4518
|
+
});
|
|
4519
|
+
}
|
|
4520
|
+
async confirm() {
|
|
4521
|
+
const preview = this.#state.preview;
|
|
4522
|
+
const scope = this.#state.previewScope;
|
|
4523
|
+
if (this.#disposed || preview === void 0 || scope === void 0 || this.#state.mutationPending) return;
|
|
4524
|
+
const currentWorkspaceId = this.getWorkspaceId();
|
|
4525
|
+
if (scope === "PROJECT" && (currentWorkspaceId === void 0 || preview.scopeBinding.scope !== "PROJECT" || preview.scopeBinding.workspaceId !== currentWorkspaceId)) {
|
|
4526
|
+
const error = { code: "PURGE_PREVIEW_STALE" };
|
|
4527
|
+
this.#publish({
|
|
4528
|
+
...this.#state,
|
|
4529
|
+
preview: void 0,
|
|
4530
|
+
previewScope: void 0,
|
|
4531
|
+
error,
|
|
4532
|
+
announcement: errorAnnouncement(error)
|
|
4533
|
+
});
|
|
4534
|
+
return;
|
|
4535
|
+
}
|
|
4732
4536
|
await this.#execute(async (signal) => {
|
|
4733
4537
|
this.#publish({
|
|
4734
4538
|
...this.#state,
|
|
4539
|
+
hostDataEpoch: this.#state.hostDataEpoch + 1,
|
|
4735
4540
|
mutationPending: true,
|
|
4541
|
+
error: void 0,
|
|
4736
4542
|
announcement: ""
|
|
4737
4543
|
});
|
|
4738
|
-
const receipt =
|
|
4739
|
-
|
|
4740
|
-
|
|
4544
|
+
const receipt = parseValue$1(receiptSchema, await this.call("purge/confirm", scope === "PROJECT" ? {
|
|
4545
|
+
apiVersion: 1,
|
|
4546
|
+
scope,
|
|
4547
|
+
workspaceId: currentWorkspaceId,
|
|
4548
|
+
previewId: preview.previewId,
|
|
4549
|
+
digest: preview.digest
|
|
4550
|
+
} : {
|
|
4551
|
+
apiVersion: 1,
|
|
4552
|
+
scope,
|
|
4553
|
+
previewId: preview.previewId,
|
|
4554
|
+
digest: preview.digest
|
|
4555
|
+
}, signal));
|
|
4556
|
+
this.#acceptReceipt(scope, receipt);
|
|
4557
|
+
}, async (error) => {
|
|
4741
4558
|
this.#publish({
|
|
4742
4559
|
...this.#state,
|
|
4743
4560
|
mutationPending: false,
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4561
|
+
error,
|
|
4562
|
+
announcement: errorAnnouncement(error),
|
|
4563
|
+
preview: void 0,
|
|
4564
|
+
previewScope: void 0
|
|
4565
|
+
});
|
|
4566
|
+
await this.#readStatusAfterFailure(error);
|
|
4567
|
+
});
|
|
4568
|
+
}
|
|
4569
|
+
async retry() {
|
|
4570
|
+
const status = this.#state.status;
|
|
4571
|
+
if (this.#disposed || status?.state !== "IN_PROGRESS" || status.lastError === void 0 || this.#state.mutationPending) return;
|
|
4572
|
+
await this.#execute(async (signal) => {
|
|
4573
|
+
this.#publish({
|
|
4574
|
+
...this.#state,
|
|
4575
|
+
hostDataEpoch: this.#state.hostDataEpoch + 1,
|
|
4576
|
+
mutationPending: true,
|
|
4577
|
+
error: void 0,
|
|
4578
|
+
announcement: "正在重试数据清理。"
|
|
4752
4579
|
});
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
listPhase: this.#state.listPhase === "LOADING" ? "ERROR" : this.#state.listPhase
|
|
4760
|
-
});
|
|
4761
|
-
}
|
|
4762
|
-
}, () => {
|
|
4580
|
+
const receipt = parseValue$1(receiptSchema, await this.call("purge/retry", {
|
|
4581
|
+
apiVersion: 1,
|
|
4582
|
+
purgeId: status.purgeId
|
|
4583
|
+
}, signal));
|
|
4584
|
+
this.#acceptReceipt(this.#state.previewScope, receipt);
|
|
4585
|
+
}, async (error) => {
|
|
4763
4586
|
this.#publish({
|
|
4764
4587
|
...this.#state,
|
|
4765
4588
|
mutationPending: false,
|
|
4766
|
-
|
|
4589
|
+
error,
|
|
4590
|
+
announcement: errorAnnouncement(error, true)
|
|
4767
4591
|
});
|
|
4592
|
+
await this.#readStatusAfterFailure(error, true);
|
|
4768
4593
|
});
|
|
4769
4594
|
}
|
|
4770
4595
|
dispose() {
|
|
4771
4596
|
if (this.#disposed) return;
|
|
4772
4597
|
this.#disposed = true;
|
|
4773
4598
|
this.#unschedule();
|
|
4774
|
-
this.#removeFocus?.();
|
|
4775
4599
|
this.#removeVisibility?.();
|
|
4776
|
-
this.#removeOnline?.();
|
|
4777
|
-
this.#removeFocus = void 0;
|
|
4778
4600
|
this.#removeVisibility = void 0;
|
|
4779
|
-
this.#removeOnline = void 0;
|
|
4780
4601
|
this.#abort?.abort();
|
|
4602
|
+
this.#statusAbort?.abort();
|
|
4781
4603
|
this.#listeners.clear();
|
|
4782
4604
|
}
|
|
4783
|
-
|
|
4784
|
-
if (
|
|
4785
|
-
await this.#execute(async (signal) => {
|
|
4786
|
-
await this.#refreshWithin(signal);
|
|
4787
|
-
}, () => {
|
|
4605
|
+
#acceptReceipt(scope, receipt) {
|
|
4606
|
+
if (receipt.state === "COMPLETED") {
|
|
4788
4607
|
this.#publish({
|
|
4789
4608
|
...this.#state,
|
|
4790
|
-
|
|
4609
|
+
hostDataEpoch: this.#state.hostDataEpoch + 1,
|
|
4610
|
+
statusPhase: "READY",
|
|
4611
|
+
status: {
|
|
4612
|
+
apiVersion: 1,
|
|
4613
|
+
state: "IDLE"
|
|
4614
|
+
},
|
|
4615
|
+
inProgressReceipt: void 0,
|
|
4616
|
+
preview: void 0,
|
|
4617
|
+
previewScope: void 0,
|
|
4618
|
+
mutationPending: false,
|
|
4619
|
+
error: void 0,
|
|
4620
|
+
announcement: completionAnnouncement(scope, receipt)
|
|
4791
4621
|
});
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
async #refreshWithin(signal) {
|
|
4795
|
-
const summary = parseProposalSummary(await this.call(endpoint.summary, {
|
|
4796
|
-
apiVersion: 1,
|
|
4797
|
-
workspaceId: this.workspaceId
|
|
4798
|
-
}, signal));
|
|
4799
|
-
if (summary === void 0) throw new Error("invalid summary response");
|
|
4622
|
+
return;
|
|
4623
|
+
}
|
|
4800
4624
|
this.#publish({
|
|
4801
4625
|
...this.#state,
|
|
4802
|
-
|
|
4803
|
-
|
|
4626
|
+
mutationPending: false,
|
|
4627
|
+
preview: void 0,
|
|
4628
|
+
inProgressReceipt: receipt,
|
|
4629
|
+
announcement: `Run2Skill 数据清理进行中:${receipt.phase ?? "HIDING"}。`
|
|
4804
4630
|
});
|
|
4805
|
-
if (!this.#state.open) return;
|
|
4806
|
-
await this.#loadListWithin(signal);
|
|
4807
|
-
const selected = this.#state.selectedProposalId;
|
|
4808
|
-
if (selected !== void 0 && this.#state.detail?.processingState === "PUBLISHING") {
|
|
4809
|
-
const detail = parseProposalDetail(await this.call(endpoint.get, {
|
|
4810
|
-
apiVersion: 1,
|
|
4811
|
-
proposalId: selected
|
|
4812
|
-
}, signal));
|
|
4813
|
-
if (detail !== void 0) this.#publish({
|
|
4814
|
-
...this.#state,
|
|
4815
|
-
detailPhase: "READY",
|
|
4816
|
-
detail,
|
|
4817
|
-
announcement: describeProposalOutcome(detail) || this.#state.announcement
|
|
4818
|
-
});
|
|
4819
|
-
}
|
|
4820
4631
|
}
|
|
4821
|
-
async #
|
|
4822
|
-
|
|
4823
|
-
await this
|
|
4824
|
-
}, () => {
|
|
4632
|
+
async #readStatusAfterFailure(error, durableBoundary = false) {
|
|
4633
|
+
try {
|
|
4634
|
+
const status = parseValue$1(statusSchema, await this.call("purge/status", { apiVersion: 1 }, new AbortController().signal));
|
|
4825
4635
|
this.#publish({
|
|
4826
4636
|
...this.#state,
|
|
4827
|
-
|
|
4637
|
+
statusPhase: "READY",
|
|
4638
|
+
status,
|
|
4639
|
+
inProgressReceipt: status.state === "IDLE" ? void 0 : this.#state.inProgressReceipt,
|
|
4640
|
+
announcement: errorAnnouncement(error, durableBoundary || status.state === "IN_PROGRESS")
|
|
4828
4641
|
});
|
|
4829
|
-
}
|
|
4642
|
+
} catch {}
|
|
4830
4643
|
}
|
|
4831
|
-
async #
|
|
4832
|
-
this.#
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
const
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
...cursor === void 0 ? {} : { cursor }
|
|
4843
|
-
}, signal));
|
|
4844
|
-
if (page === void 0) throw new Error("invalid list response");
|
|
4845
|
-
items.push(...page.items);
|
|
4846
|
-
cursor = page.nextCursor;
|
|
4847
|
-
if (cursor === void 0) {
|
|
4644
|
+
async #refreshStatus() {
|
|
4645
|
+
if (this.#disposed || !this.#active || !this.environment.isVisible()) return;
|
|
4646
|
+
if (this.#statusPending !== void 0) return this.#statusPending;
|
|
4647
|
+
const abort = new AbortController();
|
|
4648
|
+
this.#statusAbort = abort;
|
|
4649
|
+
const pending = (async () => {
|
|
4650
|
+
try {
|
|
4651
|
+
const status = parseValue$1(statusSchema, await this.call("purge/status", { apiVersion: 1 }, abort.signal));
|
|
4652
|
+
if (this.#disposed || abort.signal.aborted) return;
|
|
4653
|
+
const completed = status.state === "IDLE" && (this.#state.status?.state === "IN_PROGRESS" || this.#state.inProgressReceipt?.state === "IN_PROGRESS");
|
|
4654
|
+
const started = status.state === "IN_PROGRESS" && this.#state.status?.state !== "IN_PROGRESS" && this.#state.inProgressReceipt?.state !== "IN_PROGRESS" && !this.#state.mutationPending;
|
|
4848
4655
|
this.#publish({
|
|
4849
4656
|
...this.#state,
|
|
4850
|
-
|
|
4851
|
-
|
|
4657
|
+
hostDataEpoch: started || completed ? this.#state.hostDataEpoch + 1 : this.#state.hostDataEpoch,
|
|
4658
|
+
statusPhase: "READY",
|
|
4659
|
+
status,
|
|
4660
|
+
inProgressReceipt: status.state === "IDLE" ? void 0 : this.#state.inProgressReceipt,
|
|
4661
|
+
previewScope: completed ? void 0 : this.#state.previewScope,
|
|
4662
|
+
announcement: completed ? polledCompletionAnnouncement(this.#state.previewScope) : this.#state.announcement
|
|
4663
|
+
});
|
|
4664
|
+
} catch {
|
|
4665
|
+
if (this.#disposed || abort.signal.aborted) return;
|
|
4666
|
+
this.#publish({
|
|
4667
|
+
...this.#state,
|
|
4668
|
+
statusPhase: this.#state.status === void 0 ? "UNAVAILABLE" : "STALE"
|
|
4852
4669
|
});
|
|
4853
|
-
return;
|
|
4854
4670
|
}
|
|
4855
|
-
}
|
|
4856
|
-
|
|
4671
|
+
})();
|
|
4672
|
+
this.#statusPending = pending;
|
|
4673
|
+
await pending.finally(() => {
|
|
4674
|
+
if (this.#statusPending === pending) this.#statusPending = void 0;
|
|
4675
|
+
if (this.#statusAbort === abort) this.#statusAbort = void 0;
|
|
4676
|
+
});
|
|
4857
4677
|
}
|
|
4858
4678
|
async #execute(task, onError) {
|
|
4859
4679
|
if (this.#disposed || this.#pending !== void 0) return this.#pending ?? Promise.resolve();
|
|
@@ -4862,8 +4682,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4862
4682
|
const pending = (async () => {
|
|
4863
4683
|
try {
|
|
4864
4684
|
await task(abort.signal);
|
|
4865
|
-
} catch {
|
|
4866
|
-
if (!this.#disposed && !abort.signal.aborted) onError();
|
|
4685
|
+
} catch (error) {
|
|
4686
|
+
if (!this.#disposed && !abort.signal.aborted) await onError(error instanceof PurgeResponseError ? error.facts : { code: "PURGE_STORAGE_UNAVAILABLE" });
|
|
4867
4687
|
}
|
|
4868
4688
|
})();
|
|
4869
4689
|
this.#pending = pending;
|
|
@@ -4872,776 +4692,594 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4872
4692
|
if (this.#abort === abort) this.#abort = void 0;
|
|
4873
4693
|
});
|
|
4874
4694
|
}
|
|
4875
|
-
#schedule() {
|
|
4876
|
-
if (this.#disposed) return;
|
|
4877
|
-
const delay = this.#state.
|
|
4878
|
-
if (this.#timer !== void 0 && this.#timerDelay === delay) return;
|
|
4879
|
-
this.#unschedule();
|
|
4880
|
-
this.#timerDelay = delay;
|
|
4881
|
-
this.#timer = this.environment.setInterval(() => {
|
|
4882
|
-
this.#
|
|
4883
|
-
}, delay);
|
|
4884
|
-
}
|
|
4885
|
-
#unschedule() {
|
|
4886
|
-
if (this.#timer === void 0) return;
|
|
4887
|
-
this.environment.clearInterval(this.#timer);
|
|
4888
|
-
this.#timer = void 0;
|
|
4889
|
-
this.#timerDelay = void 0;
|
|
4890
|
-
}
|
|
4891
|
-
#publish(state) {
|
|
4892
|
-
if (this.#disposed) return;
|
|
4893
|
-
this.#state = state;
|
|
4894
|
-
if (this.#started && this.environment.isVisible()) this.#schedule();
|
|
4895
|
-
for (const listener of this.#listeners) listener();
|
|
4896
|
-
}
|
|
4897
|
-
};
|
|
4898
|
-
const formatCharacters = /* @__PURE__ */ new Map([
|
|
4899
|
-
[0, "NULL"],
|
|
4900
|
-
[9, "CHARACTER TABULATION"],
|
|
4901
|
-
[1564, "ARABIC LETTER MARK"],
|
|
4902
|
-
[8203, "ZERO WIDTH SPACE"],
|
|
4903
|
-
[8204, "ZERO WIDTH NON-JOINER"],
|
|
4904
|
-
[8205, "ZERO WIDTH JOINER"],
|
|
4905
|
-
[8206, "LEFT-TO-RIGHT MARK"],
|
|
4906
|
-
[8207, "RIGHT-TO-LEFT MARK"],
|
|
4907
|
-
[8234, "LEFT-TO-RIGHT EMBEDDING"],
|
|
4908
|
-
[8235, "RIGHT-TO-LEFT EMBEDDING"],
|
|
4909
|
-
[8236, "POP DIRECTIONAL FORMATTING"],
|
|
4910
|
-
[8237, "LEFT-TO-RIGHT OVERRIDE"],
|
|
4911
|
-
[8238, "RIGHT-TO-LEFT OVERRIDE"],
|
|
4912
|
-
[8288, "WORD JOINER"],
|
|
4913
|
-
[8294, "LEFT-TO-RIGHT ISOLATE"],
|
|
4914
|
-
[8295, "RIGHT-TO-LEFT ISOLATE"],
|
|
4915
|
-
[8296, "FIRST STRONG ISOLATE"],
|
|
4916
|
-
[8297, "POP DIRECTIONAL ISOLATE"],
|
|
4917
|
-
[65279, "ZERO WIDTH NO-BREAK SPACE"]
|
|
4918
|
-
]);
|
|
4919
|
-
function makeSafeText(value) {
|
|
4920
|
-
let result = "";
|
|
4921
|
-
for (const character of value) {
|
|
4922
|
-
const codePoint = character.codePointAt(0);
|
|
4923
|
-
if (character === "\n") {
|
|
4924
|
-
result += character;
|
|
4925
|
-
continue;
|
|
4926
|
-
}
|
|
4927
|
-
const name = formatCharacters.get(codePoint) ?? (/\p{Cf}/u.test(character) ? "FORMAT" : codePoint < 32 || codePoint >= 127 && codePoint <= 159 ? "CONTROL" : void 0);
|
|
4928
|
-
result += name === void 0 ? character : `[U+${codePoint.toString(16).toUpperCase().padStart(4, "0")} ${name}]`;
|
|
4929
|
-
}
|
|
4930
|
-
return result;
|
|
4931
|
-
}
|
|
4932
|
-
function ProposalTextView(props) {
|
|
4933
|
-
return (0, react.createElement)("pre", {
|
|
4934
|
-
"aria-label": props.label,
|
|
4935
|
-
"data-run2skill-text-mode": props.mode,
|
|
4936
|
-
style: {
|
|
4937
|
-
whiteSpace: "pre-wrap",
|
|
4938
|
-
overflowWrap: "anywhere",
|
|
4939
|
-
maxHeight: "24rem",
|
|
4940
|
-
overflow: "auto",
|
|
4941
|
-
padding: "0.75rem",
|
|
4942
|
-
border: "1px solid currentColor",
|
|
4943
|
-
borderRadius: "0.375rem",
|
|
4944
|
-
unicodeBidi: "isolate"
|
|
4945
|
-
}
|
|
4946
|
-
}, props.mode === "SAFE" ? makeSafeText(props.value) : props.value);
|
|
4947
|
-
}
|
|
4948
|
-
function makeExactLineDiff(before, after) {
|
|
4949
|
-
const left = before.split("\n");
|
|
4950
|
-
const right = after.split("\n");
|
|
4951
|
-
if (left.length * right.length > 25e4) return [...left.map((text) => ({
|
|
4952
|
-
kind: "REMOVE",
|
|
4953
|
-
text
|
|
4954
|
-
})), ...right.map((text) => ({
|
|
4955
|
-
kind: "ADD",
|
|
4956
|
-
text
|
|
4957
|
-
}))];
|
|
4958
|
-
const table = Array.from({ length: left.length + 1 }, () => new Uint32Array(right.length + 1));
|
|
4959
|
-
for (let leftIndex = left.length - 1; leftIndex >= 0; leftIndex -= 1) for (let rightIndex = right.length - 1; rightIndex >= 0; rightIndex -= 1) table[leftIndex][rightIndex] = left[leftIndex] === right[rightIndex] ? table[leftIndex + 1][rightIndex + 1] + 1 : Math.max(table[leftIndex + 1][rightIndex], table[leftIndex][rightIndex + 1]);
|
|
4960
|
-
const result = [];
|
|
4961
|
-
let leftIndex = 0;
|
|
4962
|
-
let rightIndex = 0;
|
|
4963
|
-
while (leftIndex < left.length || rightIndex < right.length) if (leftIndex < left.length && rightIndex < right.length && left[leftIndex] === right[rightIndex]) {
|
|
4964
|
-
result.push({
|
|
4965
|
-
kind: "CONTEXT",
|
|
4966
|
-
text: left[leftIndex]
|
|
4967
|
-
});
|
|
4968
|
-
leftIndex += 1;
|
|
4969
|
-
rightIndex += 1;
|
|
4970
|
-
} else if (leftIndex < left.length && (rightIndex >= right.length || table[leftIndex + 1][rightIndex] >= table[leftIndex][rightIndex + 1])) {
|
|
4971
|
-
result.push({
|
|
4972
|
-
kind: "REMOVE",
|
|
4973
|
-
text: left[leftIndex]
|
|
4974
|
-
});
|
|
4975
|
-
leftIndex += 1;
|
|
4976
|
-
} else {
|
|
4977
|
-
result.push({
|
|
4978
|
-
kind: "ADD",
|
|
4979
|
-
text: right[rightIndex]
|
|
4980
|
-
});
|
|
4981
|
-
rightIndex += 1;
|
|
4982
|
-
}
|
|
4983
|
-
return result;
|
|
4984
|
-
}
|
|
4985
|
-
//#endregion
|
|
4986
|
-
//#region src/client/dialog-focus.ts
|
|
4987
|
-
const focusableSelector = [
|
|
4988
|
-
"button:not([disabled])",
|
|
4989
|
-
"[href]",
|
|
4990
|
-
"input:not([disabled])",
|
|
4991
|
-
"select:not([disabled])",
|
|
4992
|
-
"textarea:not([disabled])",
|
|
4993
|
-
"[tabindex]:not([tabindex=\"-1\"])"
|
|
4994
|
-
].join(",");
|
|
4995
|
-
function trapDialogTab(key, backwards, preventDefault, root, activeElement) {
|
|
4996
|
-
if (key !== "Tab") return;
|
|
4997
|
-
const focusable = Array.from(root.querySelectorAll(focusableSelector));
|
|
4998
|
-
if (focusable.length === 0) {
|
|
4999
|
-
preventDefault();
|
|
5000
|
-
return;
|
|
5001
|
-
}
|
|
5002
|
-
const first = focusable[0];
|
|
5003
|
-
const last = focusable.at(-1);
|
|
5004
|
-
if (!focusable.includes(activeElement)) {
|
|
5005
|
-
preventDefault();
|
|
5006
|
-
(backwards ? last : first).focus();
|
|
5007
|
-
return;
|
|
4695
|
+
#schedule() {
|
|
4696
|
+
if (this.#disposed || !this.#active) return;
|
|
4697
|
+
const delay = this.#state.status?.state === "IN_PROGRESS" || this.#state.inProgressReceipt?.state === "IN_PROGRESS" || this.#state.mutationPending ? PURGE_ACTIVE_POLL_INTERVAL_MS : PURGE_IDLE_POLL_INTERVAL_MS;
|
|
4698
|
+
if (this.#timer !== void 0 && this.#timerDelay === delay) return;
|
|
4699
|
+
this.#unschedule();
|
|
4700
|
+
this.#timerDelay = delay;
|
|
4701
|
+
this.#timer = this.environment.setInterval(() => {
|
|
4702
|
+
this.#refreshStatus();
|
|
4703
|
+
}, delay);
|
|
5008
4704
|
}
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
first.focus();
|
|
4705
|
+
#unschedule() {
|
|
4706
|
+
if (this.#timer === void 0) return;
|
|
4707
|
+
this.environment.clearInterval(this.#timer);
|
|
4708
|
+
this.#timer = void 0;
|
|
4709
|
+
this.#timerDelay = void 0;
|
|
5015
4710
|
}
|
|
4711
|
+
#publish(state) {
|
|
4712
|
+
if (this.#disposed) return;
|
|
4713
|
+
this.#state = state;
|
|
4714
|
+
if (this.#started && this.#active && this.environment.isVisible()) this.#schedule();
|
|
4715
|
+
for (const listener of this.#listeners) listener();
|
|
4716
|
+
}
|
|
4717
|
+
};
|
|
4718
|
+
const phaseCopy = {
|
|
4719
|
+
HIDING: "正在隐藏命中数据",
|
|
4720
|
+
DELETING_LINEAGES: "正在删除 Skill 关联记录",
|
|
4721
|
+
DELETING_WORK_ITEMS: "正在删除待处理数据",
|
|
4722
|
+
VERIFYING: "正在验证清理结果"
|
|
4723
|
+
};
|
|
4724
|
+
function PreviewSummary({ preview }) {
|
|
4725
|
+
return (0, react.createElement)("dl", null, (0, react.createElement)("dt", null, "将删除"), (0, react.createElement)("dd", null, `${String(preview.workItemCount)} 条待处理数据;${String(preview.lineageCount)} 条 Skill 关联记录;${String(preview.derivedRecordCount)} 条其他中间缓存`), (0, react.createElement)("dt", null, "将保留"), (0, react.createElement)("dd", null, "已发布 Skill、DSH 原始会话记录和非 Run2Skill 设置"), (0, react.createElement)("dt", null, "正在保存的技能草稿"), (0, react.createElement)("dd", null, `${String(preview.busyPublicationCount)} 份;存在时不会开始清理`));
|
|
5016
4726
|
}
|
|
5017
|
-
|
|
5018
|
-
//#region src/client/status-copy.ts
|
|
5019
|
-
function describeRun2skillHealth(status) {
|
|
5020
|
-
if (status === "INCOMPATIBLE") return "run2skill 当前版本不兼容";
|
|
5021
|
-
if (status === "DEGRADED") return "run2skill 暂时降级";
|
|
5022
|
-
if (status === "RECOVERING") return "run2skill 正在恢复历史观察";
|
|
5023
|
-
return "run2skill 已就绪";
|
|
5024
|
-
}
|
|
5025
|
-
//#endregion
|
|
5026
|
-
//#region src/client/proposal-inbox-view.ts
|
|
5027
|
-
function proposalInboxContentBlocked(mutationPending, rejectConfirm) {
|
|
5028
|
-
return mutationPending || rejectConfirm;
|
|
5029
|
-
}
|
|
5030
|
-
function useDialogFocus(open, triggerRef, dialogRef, focusTrapSuspended) {
|
|
5031
|
-
(0, react.useEffect)(() => {
|
|
5032
|
-
if (!open) return;
|
|
5033
|
-
if (typeof document === "undefined") return;
|
|
5034
|
-
const dialog = dialogRef.current;
|
|
5035
|
-
const initial = dialog?.querySelector("[data-initial-focus]") ?? dialog?.querySelector(focusableSelector);
|
|
5036
|
-
const recapture = (event) => {
|
|
5037
|
-
if (focusTrapSuspended.current) return;
|
|
5038
|
-
if (dialog !== null && event.target instanceof Node && !dialog.contains(event.target)) initial?.focus();
|
|
5039
|
-
};
|
|
5040
|
-
document.addEventListener("focusin", recapture, true);
|
|
5041
|
-
initial?.focus();
|
|
5042
|
-
return () => {
|
|
5043
|
-
document.removeEventListener("focusin", recapture, true);
|
|
5044
|
-
triggerRef.current?.focus();
|
|
5045
|
-
};
|
|
5046
|
-
}, [
|
|
5047
|
-
dialogRef,
|
|
5048
|
-
focusTrapSuspended,
|
|
5049
|
-
open,
|
|
5050
|
-
triggerRef
|
|
5051
|
-
]);
|
|
5052
|
-
}
|
|
5053
|
-
function countLabel(state) {
|
|
5054
|
-
const queue = state.summary?.queue;
|
|
5055
|
-
if (queue === void 0 || queue.completeness === "UNKNOWN") return "Skill 提案,待处理数量未知";
|
|
5056
|
-
const total = queue.pendingReview + queue.publishing + queue.needsAttention;
|
|
5057
|
-
const facts = [
|
|
5058
|
-
queue.pendingReview > 0 ? `${String(queue.pendingReview)} 条待审核` : void 0,
|
|
5059
|
-
queue.publishing > 0 ? `${String(queue.publishing)} 条正在发布` : void 0,
|
|
5060
|
-
queue.needsAttention > 0 ? `${String(queue.needsAttention)} 条需要处理` : void 0
|
|
5061
|
-
].filter((fact) => fact !== void 0);
|
|
5062
|
-
return facts.length === 0 ? "当前没有待处理 Skill 提案" : `${String(total)} 条 Skill 提案待处理:${facts.join(",")}`;
|
|
5063
|
-
}
|
|
5064
|
-
function describeProposalSummaryState(state) {
|
|
5065
|
-
if (state.summaryPhase === "LOADING") return "Proposal 队列状态加载中";
|
|
5066
|
-
if (state.summaryPhase === "UNAVAILABLE") return "Proposal 队列状态暂不可用";
|
|
5067
|
-
if (state.summary === void 0) return "Proposal 队列状态未知";
|
|
5068
|
-
const health = describeRun2skillHealth(state.summary.status);
|
|
5069
|
-
const queue = state.summary.queue.completeness === "UNKNOWN" ? "待处理数量未知" : countLabel(state);
|
|
5070
|
-
return state.summaryPhase === "STALE" ? `Proposal 队列状态可能已过期:${health};${queue}` : `${health};${queue}`;
|
|
5071
|
-
}
|
|
5072
|
-
function ProposalInboxHeaderAction(props) {
|
|
5073
|
-
const controller = (0, react.useMemo)(() => new ProposalInboxController(props.workspaceId, props.callReview), [props.callReview, props.workspaceId]);
|
|
5074
|
-
(0, react.useEffect)(() => {
|
|
5075
|
-
controller.start();
|
|
5076
|
-
return () => {
|
|
5077
|
-
controller.dispose();
|
|
5078
|
-
};
|
|
5079
|
-
}, [controller]);
|
|
5080
|
-
const state = (0, react.useSyncExternalStore)(controller.subscribe, controller.snapshot, controller.snapshot);
|
|
5081
|
-
const triggerRef = (0, react.useRef)(null);
|
|
5082
|
-
const dialogRef = (0, react.useRef)(null);
|
|
5083
|
-
const [textMode, setTextMode] = (0, react.useState)("SAFE");
|
|
5084
|
-
const [rejectConfirm, setRejectConfirm] = (0, react.useState)(false);
|
|
5085
|
-
const focusTrapSuspended = (0, react.useRef)(false);
|
|
5086
|
-
focusTrapSuspended.current = rejectConfirm;
|
|
5087
|
-
useDialogFocus(state.open, triggerRef, dialogRef, focusTrapSuspended);
|
|
5088
|
-
const label = countLabel(state);
|
|
5089
|
-
return (0, react.createElement)(react.Fragment, null, (0, react.createElement)("button", {
|
|
5090
|
-
ref: triggerRef,
|
|
5091
|
-
type: "button",
|
|
5092
|
-
"aria-label": label,
|
|
5093
|
-
"aria-haspopup": "dialog",
|
|
5094
|
-
"aria-expanded": state.open,
|
|
5095
|
-
"data-run2skill-proposal-trigger": true,
|
|
5096
|
-
onClick: () => {
|
|
5097
|
-
controller.open();
|
|
5098
|
-
},
|
|
5099
|
-
style: { outlineOffset: "0.2rem" }
|
|
5100
|
-
}, label), state.open ? (0, react.createElement)(ProposalInboxPanel, {
|
|
5101
|
-
controller,
|
|
5102
|
-
state,
|
|
5103
|
-
dialogRef,
|
|
5104
|
-
textMode,
|
|
5105
|
-
setTextMode,
|
|
5106
|
-
rejectConfirm,
|
|
5107
|
-
setRejectConfirm
|
|
5108
|
-
}) : null);
|
|
5109
|
-
}
|
|
5110
|
-
function closeOnKeyboard(event, controller, dialog) {
|
|
5111
|
-
if (event.key === "Escape") {
|
|
5112
|
-
event.preventDefault();
|
|
5113
|
-
controller.close();
|
|
5114
|
-
} else if (dialog !== null) trapDialogTab(event.key, event.shiftKey, () => {
|
|
5115
|
-
event.preventDefault();
|
|
5116
|
-
}, dialog, typeof document === "undefined" ? void 0 : document.activeElement);
|
|
5117
|
-
}
|
|
5118
|
-
function ProposalInboxPanel(props) {
|
|
5119
|
-
const { controller, state } = props;
|
|
5120
|
-
const contentBlocked = proposalInboxContentBlocked(state.mutationPending, props.rejectConfirm);
|
|
5121
|
-
return (0, react.createElement)(react.Fragment, null, (0, react.createElement)("div", {
|
|
5122
|
-
"aria-hidden": true,
|
|
5123
|
-
"data-run2skill-proposal-backdrop": true,
|
|
5124
|
-
style: {
|
|
5125
|
-
position: "fixed",
|
|
5126
|
-
inset: 0,
|
|
5127
|
-
zIndex: 999,
|
|
5128
|
-
background: "rgb(0 0 0 / 35%)"
|
|
5129
|
-
}
|
|
5130
|
-
}), (0, react.createElement)("div", {
|
|
5131
|
-
ref: props.dialogRef,
|
|
5132
|
-
role: "dialog",
|
|
5133
|
-
"aria-modal": true,
|
|
5134
|
-
"aria-labelledby": "run2skill-proposal-inbox-title",
|
|
5135
|
-
onKeyDown: (event) => {
|
|
5136
|
-
closeOnKeyboard(event, controller, props.dialogRef.current);
|
|
5137
|
-
},
|
|
5138
|
-
style: {
|
|
5139
|
-
position: "fixed",
|
|
5140
|
-
inset: "5vh 5vw",
|
|
5141
|
-
zIndex: 1e3,
|
|
5142
|
-
display: "grid",
|
|
5143
|
-
gridTemplateColumns: "minmax(16rem, 24rem) minmax(0, 1fr)",
|
|
5144
|
-
gap: "1rem",
|
|
5145
|
-
padding: "1rem",
|
|
5146
|
-
overflow: "hidden",
|
|
5147
|
-
background: "Canvas",
|
|
5148
|
-
color: "CanvasText",
|
|
5149
|
-
border: "2px solid currentColor",
|
|
5150
|
-
borderRadius: "0.5rem",
|
|
5151
|
-
boxShadow: "0 1rem 3rem rgb(0 0 0 / 35%)"
|
|
5152
|
-
}
|
|
5153
|
-
}, (0, react.createElement)("div", {
|
|
5154
|
-
"aria-hidden": props.rejectConfirm || void 0,
|
|
5155
|
-
inert: props.rejectConfirm ? "" : void 0,
|
|
5156
|
-
style: {
|
|
5157
|
-
display: "contents",
|
|
5158
|
-
pointerEvents: props.rejectConfirm ? "none" : void 0
|
|
5159
|
-
}
|
|
5160
|
-
}, (0, react.createElement)("section", {
|
|
5161
|
-
"aria-label": "Proposal 待处理队列",
|
|
5162
|
-
style: { overflow: "auto" }
|
|
5163
|
-
}, (0, react.createElement)("div", { style: {
|
|
5164
|
-
display: "flex",
|
|
5165
|
-
alignItems: "center",
|
|
5166
|
-
justifyContent: "space-between"
|
|
5167
|
-
} }, (0, react.createElement)("h2", { id: "run2skill-proposal-inbox-title" }, "Skill Proposal Inbox"), (0, react.createElement)("button", {
|
|
5168
|
-
type: "button",
|
|
5169
|
-
"data-initial-focus": true,
|
|
5170
|
-
onClick: () => {
|
|
5171
|
-
controller.close();
|
|
5172
|
-
}
|
|
5173
|
-
}, "关闭")), (0, react.createElement)("p", { role: "status" }, countLabel(state)), (0, react.createElement)("p", {
|
|
5174
|
-
role: "status",
|
|
5175
|
-
"aria-live": "polite"
|
|
5176
|
-
}, describeProposalSummaryState(state)), state.listPhase === "LOADING" ? (0, react.createElement)("p", null, "正在加载待处理队列…") : null, state.listPhase === "ERROR" ? (0, react.createElement)("p", { role: "alert" }, "待处理队列暂不可用") : null, state.listPhase === "READY" && state.items.length === 0 ? (0, react.createElement)("p", null, "当前没有待处理 Proposal") : null, (0, react.createElement)("ul", { style: {
|
|
5177
|
-
listStyle: "none",
|
|
5178
|
-
padding: 0
|
|
5179
|
-
} }, ...state.items.map((item) => (0, react.createElement)("li", { key: item.proposalRef.proposalId }, (0, react.createElement)("button", {
|
|
5180
|
-
type: "button",
|
|
5181
|
-
disabled: contentBlocked || state.detailPhase === "LOADING",
|
|
5182
|
-
"aria-current": state.selectedProposalId === item.proposalRef.proposalId ? "true" : void 0,
|
|
5183
|
-
onClick: () => {
|
|
5184
|
-
controller.select(item.proposalRef.proposalId);
|
|
5185
|
-
},
|
|
5186
|
-
style: {
|
|
5187
|
-
display: "block",
|
|
5188
|
-
width: "100%",
|
|
5189
|
-
padding: "0.75rem",
|
|
5190
|
-
marginBlock: "0.25rem",
|
|
5191
|
-
textAlign: "start",
|
|
5192
|
-
outlineOffset: "0.15rem"
|
|
5193
|
-
}
|
|
5194
|
-
}, `${item.kind} · ${makeSafeText(item.name)} · ${item.persistenceScope} · ${describeProposalListItem(item)}`))))), (0, react.createElement)("section", {
|
|
5195
|
-
"aria-label": "Proposal 详情",
|
|
5196
|
-
style: {
|
|
5197
|
-
overflow: "auto",
|
|
5198
|
-
minWidth: 0
|
|
5199
|
-
}
|
|
5200
|
-
}, state.detailPhase === "IDLE" ? (0, react.createElement)("p", null, "选择一个 Proposal 查看完整事实") : null, state.detailPhase === "LOADING" ? (0, react.createElement)("p", null, "正在加载 Proposal 详情…") : null, state.detailPhase === "ERROR" ? (0, react.createElement)("p", { role: "alert" }, "Proposal 详情暂不可用") : null, state.detail === void 0 ? null : (0, react.createElement)(ProposalDetailView, {
|
|
5201
|
-
detail: state.detail,
|
|
5202
|
-
textMode: props.textMode,
|
|
5203
|
-
setTextMode: props.setTextMode,
|
|
5204
|
-
mutationPending: contentBlocked,
|
|
5205
|
-
onApprove: () => {
|
|
5206
|
-
controller.mutate("APPROVE");
|
|
5207
|
-
},
|
|
5208
|
-
onReject: () => {
|
|
5209
|
-
props.setRejectConfirm(true);
|
|
5210
|
-
},
|
|
5211
|
-
onRetry: () => {
|
|
5212
|
-
controller.mutate("RETRY");
|
|
5213
|
-
},
|
|
5214
|
-
onConfirmDiscard: () => {
|
|
5215
|
-
controller.mutate("CONFIRM_DISCARD");
|
|
5216
|
-
}
|
|
5217
|
-
}), (0, react.createElement)("div", {
|
|
5218
|
-
"aria-live": "polite",
|
|
5219
|
-
"aria-atomic": true
|
|
5220
|
-
}, state.announcement))), props.rejectConfirm ? (0, react.createElement)(RejectConfirmation, {
|
|
5221
|
-
disabled: state.mutationPending,
|
|
5222
|
-
onCancel: () => {
|
|
5223
|
-
props.setRejectConfirm(false);
|
|
5224
|
-
},
|
|
5225
|
-
onConfirm: () => {
|
|
5226
|
-
props.setRejectConfirm(false);
|
|
5227
|
-
controller.mutate("REJECT");
|
|
5228
|
-
}
|
|
5229
|
-
}) : null));
|
|
5230
|
-
}
|
|
5231
|
-
function factsFromAction(detail) {
|
|
5232
|
-
const action = detail.proposal.actionBinding;
|
|
5233
|
-
if (action.kind === "CREATE") return [
|
|
5234
|
-
`Root contract: ${action.rootBinding.rootContractVersion}`,
|
|
5235
|
-
`Root resolver: ${action.rootBinding.resolverVersion}`,
|
|
5236
|
-
`Expected provider/source: ${action.rootBinding.expectedProvider} / ${action.rootBinding.expectedSource}`,
|
|
5237
|
-
`Resolution digest: ${action.rootBinding.resolutionContractDigest}`,
|
|
5238
|
-
`Root state: ${action.rootBinding.state}`,
|
|
5239
|
-
`Declared root: ${action.rootBinding.declaredRootPath}`,
|
|
5240
|
-
`Bundle target: ${action.targetBinding.bundlePath}`,
|
|
5241
|
-
`Skill target: ${action.targetBinding.skillFilePath}`,
|
|
5242
|
-
`Flat target checked: ${action.expectedAbsence.flatSkillFilePath}`,
|
|
5243
|
-
`Expected absence observed: ${action.expectedAbsence.observedAt}`,
|
|
5244
|
-
"Bundle absent: yes",
|
|
5245
|
-
"Skill file absent: yes",
|
|
5246
|
-
"Flat Skill file absent: yes"
|
|
5247
|
-
].join("\n");
|
|
5248
|
-
if (action.kind === "MERGE") return [
|
|
5249
|
-
`Root contract: ${action.rootBinding.rootContractVersion}`,
|
|
5250
|
-
`Root resolver: ${action.rootBinding.resolverVersion}`,
|
|
5251
|
-
`Expected provider/source: ${action.rootBinding.expectedProvider} / ${action.rootBinding.expectedSource}`,
|
|
5252
|
-
`Resolution digest: ${action.rootBinding.resolutionContractDigest}`,
|
|
5253
|
-
`Root state: ${action.rootBinding.state}`,
|
|
5254
|
-
`Declared root: ${action.rootBinding.declaredRootPath}`,
|
|
5255
|
-
`Target identity: ${action.baseBinding.candidateKey}`,
|
|
5256
|
-
`Observed target path: ${action.baseBinding.path}`,
|
|
5257
|
-
`Bundle target: ${action.targetBinding.bundlePath}`,
|
|
5258
|
-
`Skill target: ${action.targetBinding.skillFilePath}`,
|
|
5259
|
-
`Base digest: ${action.baseBinding.bytesDigest}`,
|
|
5260
|
-
`Base observed: ${action.baseBinding.observedAt}`
|
|
5261
|
-
].join("\n");
|
|
5262
|
-
return [
|
|
5263
|
-
`Covering candidate: ${action.coveringCandidateBinding.candidateKey}`,
|
|
5264
|
-
`Name: ${action.coveringCandidateBinding.name}`,
|
|
5265
|
-
`Source: ${action.coveringCandidateBinding.source}`,
|
|
5266
|
-
`Digest: ${action.coveringCandidateBinding.contentDigest}`,
|
|
5267
|
-
`Observed: ${action.coveringCandidateBinding.observedAt}`,
|
|
5268
|
-
...action.coveringCandidateBinding.path === void 0 ? [] : [`Path: ${action.coveringCandidateBinding.path}`]
|
|
5269
|
-
].join("\n");
|
|
5270
|
-
}
|
|
5271
|
-
function ProposalDetailView(props) {
|
|
5272
|
-
const { detail, mutationPending } = props;
|
|
5273
|
-
const proposal = detail.proposal;
|
|
5274
|
-
const actionable = detail.reviewDecision === "PENDING" && detail.processingState === "READY_FOR_REVIEW" && !mutationPending;
|
|
5275
|
-
const coordinate = detail.sessionCoordinate;
|
|
5276
|
-
const baseBytes = proposal.actionBinding.kind === "MERGE" ? proposal.actionBinding.baseBinding.exactBytes : void 0;
|
|
5277
|
-
const coveringBytes = proposal.actionBinding.kind === "DISCARD" ? proposal.actionBinding.coveringCandidateBinding.content : void 0;
|
|
5278
|
-
const diff = baseBytes === void 0 ? [] : makeExactLineDiff(baseBytes, proposal.exactSkillBytes);
|
|
5279
|
-
return (0, react.createElement)(react.Fragment, null, (0, react.createElement)("h3", null, `${proposal.kind}: ${makeSafeText(proposal.name)}`), (0, react.createElement)("dl", null, (0, react.createElement)("dt", null, "说明"), (0, react.createElement)("dd", null, makeSafeText(proposal.description)), (0, react.createElement)("dt", null, "何时使用"), (0, react.createElement)("dd", null, makeSafeText(proposal.whenToUse)), (0, react.createElement)("dt", null, "Scope"), (0, react.createElement)("dd", null, proposal.persistenceScope), (0, react.createElement)("dt", null, "Proposal revision"), (0, react.createElement)("dd", null, String(proposal.revision)), (0, react.createElement)("dt", null, "Proposal digest"), (0, react.createElement)("dd", null, proposal.digest), (0, react.createElement)("dt", null, "Session / Turn"), (0, react.createElement)("dd", null, `${coordinate.rootSessionId} / ${String(coordinate.turn)} / seq ${String(coordinate.turnEndSeq)}`), proposal.workspaceBinding === void 0 ? null : (0, react.createElement)(react.Fragment, null, (0, react.createElement)("dt", null, "Workspace"), (0, react.createElement)("dd", null, `${proposal.workspaceBinding.workspaceId} · ${makeSafeText(proposal.workspaceBinding.canonicalPath)}`)), proposal.dshHomeBinding === void 0 ? null : (0, react.createElement)(react.Fragment, null, (0, react.createElement)("dt", null, "DSH Home"), (0, react.createElement)("dd", null, `${proposal.dshHomeBinding.resolutionKind} · ${makeSafeText(proposal.dshHomeBinding.canonicalPath)}`), (0, react.createElement)("dt", null, "DSH Home identity"), (0, react.createElement)("dd", null, proposal.dshHomeBinding.identityDigest)), (0, react.createElement)("dt", null, "审核决定"), (0, react.createElement)("dd", null, describeReviewDecision(detail.reviewDecision)), (0, react.createElement)("dt", null, "处理状态"), (0, react.createElement)("dd", null, describeProcessingState(detail.processingState)), (0, react.createElement)("dt", null, "发布结果"), (0, react.createElement)("dd", null, describePublicationOutcome(detail.publicationOutcome))), (0, react.createElement)("h4", null, "Why learned"), (0, react.createElement)("p", null, makeSafeText(proposal.curationRationale)), ...detail.experiences.map((experience) => (0, react.createElement)("article", { key: experience.experienceId }, (0, react.createElement)("strong", null, `${experience.type} · ${experience.evidenceStrength}`), (0, react.createElement)("p", null, makeSafeText(experience.lesson)))), (0, react.createElement)("h4", null, "过滤后的 Evidence"), ...detail.evidenceRefs.map((evidence) => (0, react.createElement)("article", { key: `${String(evidence.messageSeq)}:${evidence.excerptDigest}` }, (0, react.createElement)("strong", null, `message seq ${String(evidence.messageSeq)}${evidence.truncated ? " · 已截断" : ""}`), (0, react.createElement)(ProposalTextView, {
|
|
5280
|
-
value: evidence.excerpt,
|
|
5281
|
-
mode: "SAFE",
|
|
5282
|
-
label: `Evidence ${String(evidence.messageSeq)}`
|
|
5283
|
-
}))), (0, react.createElement)("h4", null, "绑定事实"), (0, react.createElement)(ProposalTextView, {
|
|
5284
|
-
value: factsFromAction(detail),
|
|
5285
|
-
mode: "SAFE",
|
|
5286
|
-
label: "绑定事实"
|
|
5287
|
-
}), (0, react.createElement)("div", {
|
|
5288
|
-
role: "group",
|
|
5289
|
-
"aria-label": "内容显示方式"
|
|
5290
|
-
}, (0, react.createElement)("button", {
|
|
5291
|
-
type: "button",
|
|
5292
|
-
"aria-pressed": props.textMode === "SAFE",
|
|
5293
|
-
onClick: () => {
|
|
5294
|
-
props.setTextMode("SAFE");
|
|
5295
|
-
}
|
|
5296
|
-
}, "安全视图"), (0, react.createElement)("button", {
|
|
5297
|
-
type: "button",
|
|
5298
|
-
"aria-pressed": props.textMode === "RAW",
|
|
5299
|
-
onClick: () => {
|
|
5300
|
-
props.setTextMode("RAW");
|
|
5301
|
-
}
|
|
5302
|
-
}, "原始内容")), (0, react.createElement)("h4", null, "将要批准的完整 SKILL.md"), (0, react.createElement)(ProposalTextView, {
|
|
5303
|
-
value: proposal.exactSkillBytes,
|
|
5304
|
-
mode: props.textMode,
|
|
5305
|
-
label: "将要批准的完整 SKILL.md"
|
|
5306
|
-
}), baseBytes === void 0 ? null : (0, react.createElement)(react.Fragment, null, (0, react.createElement)("h4", null, "MERGE Base"), (0, react.createElement)(ProposalTextView, {
|
|
5307
|
-
value: baseBytes,
|
|
5308
|
-
mode: props.textMode,
|
|
5309
|
-
label: "MERGE Base"
|
|
5310
|
-
}), (0, react.createElement)("h4", null, "精确 Diff"), (0, react.createElement)("pre", {
|
|
5311
|
-
"aria-label": "精确 Diff",
|
|
5312
|
-
style: {
|
|
5313
|
-
whiteSpace: "pre-wrap",
|
|
5314
|
-
unicodeBidi: "isolate"
|
|
5315
|
-
}
|
|
5316
|
-
}, diff.map((line) => `${line.kind === "ADD" ? "+" : line.kind === "REMOVE" ? "-" : " "} ${makeSafeText(line.text)}`).join("\n"))), coveringBytes === void 0 ? null : (0, react.createElement)(react.Fragment, null, (0, react.createElement)("h4", null, "覆盖已有 Skill 的完整内容"), (0, react.createElement)(ProposalTextView, {
|
|
5317
|
-
value: coveringBytes,
|
|
5318
|
-
mode: props.textMode,
|
|
5319
|
-
label: "覆盖已有 Skill 的完整内容"
|
|
5320
|
-
})), (0, react.createElement)("div", {
|
|
5321
|
-
role: "group",
|
|
5322
|
-
"aria-label": "Proposal 操作"
|
|
5323
|
-
}, proposal.kind === "DISCARD" ? (0, react.createElement)(react.Fragment, null, (0, react.createElement)("button", {
|
|
5324
|
-
type: "button",
|
|
5325
|
-
disabled: !actionable,
|
|
5326
|
-
onClick: props.onConfirmDiscard
|
|
5327
|
-
}, "确认无需新建 Skill"), (0, react.createElement)("button", {
|
|
5328
|
-
type: "button",
|
|
5329
|
-
disabled: !actionable,
|
|
5330
|
-
onClick: props.onRetry
|
|
5331
|
-
}, "不同意,重新分析一次")) : (0, react.createElement)(react.Fragment, null, (0, react.createElement)("button", {
|
|
5332
|
-
type: "button",
|
|
5333
|
-
disabled: !actionable,
|
|
5334
|
-
onClick: props.onApprove
|
|
5335
|
-
}, detail.processingState === "PUBLISHING" ? "正在发布…" : "批准并发布"), (0, react.createElement)("button", {
|
|
5336
|
-
type: "button",
|
|
5337
|
-
disabled: !actionable,
|
|
5338
|
-
onClick: props.onReject
|
|
5339
|
-
}, "拒绝 Proposal"))));
|
|
5340
|
-
}
|
|
5341
|
-
function RejectConfirmationBody(props) {
|
|
5342
|
-
return (0, react.createElement)(react.Fragment, null, (0, react.createElement)("h3", { id: "run2skill-reject-title" }, "确认拒绝 Proposal?"), (0, react.createElement)("p", { id: "run2skill-reject-description" }, "现有 Skill 不会改变;该 Proposal 将离开待处理队列;过滤后的 Evidence 仍按项目策略保留。"), (0, react.createElement)("button", {
|
|
5343
|
-
type: "button",
|
|
5344
|
-
"data-initial-focus": true,
|
|
5345
|
-
disabled: props.disabled,
|
|
5346
|
-
onClick: props.onCancel
|
|
5347
|
-
}, "取消"), (0, react.createElement)("button", {
|
|
5348
|
-
type: "button",
|
|
5349
|
-
disabled: props.disabled,
|
|
5350
|
-
onClick: props.onConfirm
|
|
5351
|
-
}, "确认拒绝"));
|
|
5352
|
-
}
|
|
5353
|
-
function RejectConfirmation(props) {
|
|
4727
|
+
function PurgeConfirmationDialog(props) {
|
|
5354
4728
|
const dialogRef = (0, react.useRef)(null);
|
|
4729
|
+
const preview = props.state.preview;
|
|
4730
|
+
const scope = props.state.previewScope;
|
|
5355
4731
|
(0, react.useEffect)(() => {
|
|
5356
|
-
if (typeof document === "undefined") return;
|
|
5357
|
-
const
|
|
5358
|
-
const
|
|
5359
|
-
const initial = dialog?.querySelector("[data-initial-focus]");
|
|
4732
|
+
if (preview === void 0 || typeof document === "undefined") return;
|
|
4733
|
+
const dialog = dialogRef.current?.closest("[role=\"dialog\"]");
|
|
4734
|
+
const initial = dialog?.querySelector("[autofocus]");
|
|
5360
4735
|
const recapture = (event) => {
|
|
5361
|
-
if (dialog
|
|
4736
|
+
if (dialog != null && event.target instanceof Node && !dialog.contains(event.target)) initial?.focus();
|
|
4737
|
+
};
|
|
4738
|
+
const captureKeyboard = (event) => {
|
|
4739
|
+
if (dialog == null || event.key !== "Escape" && event.key !== "Tab") return;
|
|
4740
|
+
if (event.key === "Escape") {
|
|
4741
|
+
event.preventDefault();
|
|
4742
|
+
event.stopImmediatePropagation();
|
|
4743
|
+
props.controller.cancelPreview();
|
|
4744
|
+
return;
|
|
4745
|
+
}
|
|
4746
|
+
trapDialogTab(event.key, event.shiftKey, () => {
|
|
4747
|
+
event.preventDefault();
|
|
4748
|
+
}, dialog, document.activeElement);
|
|
5362
4749
|
};
|
|
5363
4750
|
document.addEventListener("focusin", recapture, true);
|
|
4751
|
+
document.addEventListener("keydown", captureKeyboard, true);
|
|
5364
4752
|
initial?.focus();
|
|
5365
4753
|
return () => {
|
|
5366
4754
|
document.removeEventListener("focusin", recapture, true);
|
|
5367
|
-
|
|
4755
|
+
document.removeEventListener("keydown", captureKeyboard, true);
|
|
4756
|
+
props.restoreFocusRef.current?.focus();
|
|
5368
4757
|
};
|
|
5369
|
-
}, []);
|
|
5370
|
-
|
|
4758
|
+
}, [preview?.previewId, props.restoreFocusRef]);
|
|
4759
|
+
if (preview === void 0 || scope === void 0) return null;
|
|
4760
|
+
return (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
4761
|
+
open: true,
|
|
4762
|
+
title: scope === "ALL" ? "确认清理所有缓存?" : `确认清理 ${scope} Run2Skill 数据?`,
|
|
4763
|
+
description: "此操作只清理 Run2Skill 自己产生的中间缓存数据,不会卸载插件。",
|
|
4764
|
+
closeLabel: "关闭清理确认框",
|
|
4765
|
+
onClose: () => {
|
|
4766
|
+
props.controller.cancelPreview();
|
|
4767
|
+
},
|
|
4768
|
+
footer: (0, react.createElement)("div", { className: _run2skill_css_run2skill_settings_page_module_css_default.actions }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4769
|
+
variant: "ghost",
|
|
4770
|
+
autoFocus: true,
|
|
4771
|
+
disabled: props.state.mutationPending,
|
|
4772
|
+
onClick: () => {
|
|
4773
|
+
props.controller.cancelPreview();
|
|
4774
|
+
}
|
|
4775
|
+
}, "取消清理"), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4776
|
+
variant: "primary",
|
|
4777
|
+
icon: (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16),
|
|
4778
|
+
disabled: props.state.mutationPending,
|
|
4779
|
+
onClick: () => {
|
|
4780
|
+
props.controller.confirm();
|
|
4781
|
+
}
|
|
4782
|
+
}, props.state.mutationPending ? "正在清理…" : "确认清理"))
|
|
4783
|
+
}, (0, react.createElement)("div", {
|
|
5371
4784
|
ref: dialogRef,
|
|
5372
|
-
role: "alertdialog",
|
|
5373
|
-
"aria-modal": true,
|
|
5374
|
-
"aria-labelledby": "run2skill-reject-title",
|
|
5375
|
-
"aria-describedby": "run2skill-reject-description",
|
|
5376
4785
|
onKeyDown: (event) => {
|
|
5377
4786
|
event.stopPropagation();
|
|
5378
4787
|
if (event.key === "Escape") {
|
|
5379
4788
|
event.preventDefault();
|
|
5380
|
-
props.
|
|
4789
|
+
props.controller.cancelPreview();
|
|
5381
4790
|
} else if (dialogRef.current !== null) trapDialogTab(event.key, event.shiftKey, () => {
|
|
5382
4791
|
event.preventDefault();
|
|
5383
4792
|
}, dialogRef.current, typeof document === "undefined" ? void 0 : document.activeElement);
|
|
5384
|
-
},
|
|
5385
|
-
style: {
|
|
5386
|
-
position: "fixed",
|
|
5387
|
-
inset: 0,
|
|
5388
|
-
zIndex: 1001,
|
|
5389
|
-
display: "grid",
|
|
5390
|
-
placeItems: "center",
|
|
5391
|
-
padding: "1rem",
|
|
5392
|
-
background: "rgb(0 0 0 / 35%)"
|
|
5393
4793
|
}
|
|
5394
|
-
}, (0, react.createElement)("
|
|
5395
|
-
maxWidth: "32rem",
|
|
5396
|
-
padding: "1rem",
|
|
5397
|
-
background: "Canvas",
|
|
5398
|
-
color: "CanvasText",
|
|
5399
|
-
border: "2px solid currentColor",
|
|
5400
|
-
borderRadius: "0.5rem"
|
|
5401
|
-
} }, (0, react.createElement)(RejectConfirmationBody, props)));
|
|
4794
|
+
}, (0, react.createElement)("ul", null, (0, react.createElement)("li", null, "删除 Run2Skill 产生的中间缓存、待处理技能草稿、失败与非敏感诊断记录。"), (0, react.createElement)("li", null, "保留 DSH 的原始会话记录。"), (0, react.createElement)("li", null, "保留所有已发布的原生 Skill。"), (0, react.createElement)("li", null, "删除 Skill 关联记录后,保留的 Skill 仍可作为普通 Skill 使用。")), (0, react.createElement)(PreviewSummary, { preview })));
|
|
5402
4795
|
}
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
const
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
4796
|
+
function PurgeSettingsSection(props) {
|
|
4797
|
+
const surfaceActive = props.active ?? true;
|
|
4798
|
+
(0, react.useEffect)(() => {
|
|
4799
|
+
if (!surfaceActive) props.controller.pause();
|
|
4800
|
+
props.controller.start();
|
|
4801
|
+
}, [props.controller]);
|
|
4802
|
+
(0, react.useEffect)(() => {
|
|
4803
|
+
if (surfaceActive) props.controller.resume();
|
|
4804
|
+
else props.controller.pause();
|
|
4805
|
+
}, [surfaceActive, props.controller]);
|
|
4806
|
+
const state = (0, react.useSyncExternalStore)(props.controller.subscribe, props.controller.snapshot, props.controller.snapshot);
|
|
4807
|
+
const lastCompletion = (0, react.useRef)("");
|
|
4808
|
+
(0, react.useEffect)(() => {
|
|
4809
|
+
if (!state.announcement.includes("清理完成") || state.announcement === lastCompletion.current) return;
|
|
4810
|
+
lastCompletion.current = state.announcement;
|
|
4811
|
+
props.onCompleted?.();
|
|
4812
|
+
}, [props.onCompleted, state.announcement]);
|
|
4813
|
+
const restoreRef = (0, react.useMemo)(() => ({ current: null }), []);
|
|
4814
|
+
const active = state.status?.state === "IN_PROGRESS" ? state.status : void 0;
|
|
4815
|
+
const activeReceipt = state.inProgressReceipt?.state === "IN_PROGRESS" ? state.inProgressReceipt : void 0;
|
|
4816
|
+
const activePhase = active?.phase ?? activeReceipt?.phase ?? (activeReceipt === void 0 ? void 0 : "HIDING");
|
|
4817
|
+
const disabled = state.previewPending || state.mutationPending || active !== void 0 || activeReceipt !== void 0;
|
|
4818
|
+
return (0, react.createElement)("section", { "aria-labelledby": "run2skill-purge-heading" }, (0, react.createElement)("h3", { id: "run2skill-purge-heading" }, "缓存清理"), (0, react.createElement)("p", null, "清理 Run2Skill 自己产生的中间缓存数据"), (0, react.createElement)("div", { className: _run2skill_css_run2skill_settings_page_module_css_default.actions }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4819
|
+
variant: "outline",
|
|
4820
|
+
icon: (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16),
|
|
4821
|
+
disabled,
|
|
4822
|
+
onClick: (event) => {
|
|
4823
|
+
restoreRef.current = event.currentTarget;
|
|
4824
|
+
props.controller.preview("ALL");
|
|
4825
|
+
}
|
|
4826
|
+
}, "清理所有缓存")), state.statusPhase === "LOADING" ? (0, react.createElement)("p", { role: "status" }, "正在读取数据清理状态…") : null, state.statusPhase === "UNAVAILABLE" ? (0, react.createElement)("p", { role: "alert" }, "数据清理状态暂不可用") : null, state.statusPhase === "STALE" ? (0, react.createElement)("p", { role: "status" }, "数据清理状态可能已过期") : null, state.previewPending ? (0, react.createElement)("p", { role: "status" }, "正在生成清理预览…") : null, activePhase === void 0 ? null : (0, react.createElement)("div", {
|
|
5430
4827
|
role: "status",
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
"
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
alignItems: "center",
|
|
5439
|
-
maxWidth: "28rem",
|
|
5440
|
-
padding: "0.25rem 0.5rem",
|
|
5441
|
-
border: "1px solid currentColor",
|
|
5442
|
-
borderRadius: "0.375rem",
|
|
5443
|
-
fontSize: "0.75rem",
|
|
5444
|
-
lineHeight: 1.25
|
|
4828
|
+
"aria-label": "当前数据清理状态"
|
|
4829
|
+
}, (0, react.createElement)("p", null, phaseCopy[activePhase]), (0, react.createElement)("p", null, `已删除 ${String(active?.deletedWorkItems ?? activeReceipt?.deletedWorkItems ?? 0)} 条待处理数据和 ${String(active?.deletedLineages ?? activeReceipt?.deletedLineages ?? 0)} 条 Skill 关联记录。`), active?.lastError === void 0 ? null : (0, react.createElement)(react.Fragment, null, (0, react.createElement)("p", { role: "alert" }, `清理失败:${active.lastError.code}`), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
4830
|
+
variant: "outline",
|
|
4831
|
+
icon: (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutline16),
|
|
4832
|
+
disabled: state.mutationPending,
|
|
4833
|
+
onClick: () => {
|
|
4834
|
+
props.controller.retry();
|
|
5445
4835
|
}
|
|
5446
|
-
},
|
|
4836
|
+
}, state.mutationPending ? "正在重试…" : "重试清理"))), state.error === void 0 ? null : (0, react.createElement)("p", { role: "alert" }, state.announcement), (0, react.createElement)("div", {
|
|
4837
|
+
role: "status",
|
|
4838
|
+
"aria-label": "数据清理状态播报",
|
|
4839
|
+
"aria-live": "polite",
|
|
4840
|
+
"aria-atomic": true
|
|
4841
|
+
}, state.announcement), (0, react.createElement)(PurgeConfirmationDialog, {
|
|
4842
|
+
controller: props.controller,
|
|
4843
|
+
state,
|
|
4844
|
+
restoreFocusRef: restoreRef
|
|
4845
|
+
}));
|
|
5447
4846
|
}
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
4847
|
+
//#endregion
|
|
4848
|
+
//#region src/client/automatic-learning-settings.ts
|
|
4849
|
+
var AutomaticLearningSettingsController = class {
|
|
4850
|
+
scope;
|
|
4851
|
+
#listeners = /* @__PURE__ */ new Set();
|
|
4852
|
+
#snapshot;
|
|
4853
|
+
#saving = false;
|
|
4854
|
+
#error;
|
|
4855
|
+
#unsubscribe;
|
|
4856
|
+
constructor(scope) {
|
|
4857
|
+
this.scope = scope;
|
|
4858
|
+
this.#snapshot = this.#project();
|
|
4859
|
+
this.#unsubscribe = scope.subscribe(() => {
|
|
4860
|
+
this.#publish();
|
|
4861
|
+
});
|
|
4862
|
+
}
|
|
4863
|
+
getSnapshot = () => this.#snapshot;
|
|
4864
|
+
subscribe = (listener) => {
|
|
4865
|
+
this.#listeners.add(listener);
|
|
5452
4866
|
return () => {
|
|
5453
|
-
|
|
4867
|
+
this.#listeners.delete(listener);
|
|
5454
4868
|
};
|
|
5455
|
-
}
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
4869
|
+
};
|
|
4870
|
+
async setAutomaticLearning(value) {
|
|
4871
|
+
const before = this.scope.getSnapshot();
|
|
4872
|
+
if (before.status !== "ready" || !before.writable || this.#saving) return;
|
|
4873
|
+
this.#saving = true;
|
|
4874
|
+
this.#error = void 0;
|
|
4875
|
+
this.#publish();
|
|
4876
|
+
try {
|
|
4877
|
+
await this.scope.set("automaticLearning", value);
|
|
4878
|
+
const after = this.scope.getSnapshot();
|
|
4879
|
+
this.#error = after.status === "ready" && after.value?.automaticLearning === value ? void 0 : "SETTINGS_CHANGED";
|
|
4880
|
+
} catch {
|
|
4881
|
+
this.#error = "SETTINGS_CHANGED";
|
|
4882
|
+
} finally {
|
|
4883
|
+
this.#saving = false;
|
|
4884
|
+
this.#publish();
|
|
4885
|
+
}
|
|
4886
|
+
}
|
|
4887
|
+
dispose() {
|
|
4888
|
+
this.#unsubscribe();
|
|
4889
|
+
this.#listeners.clear();
|
|
4890
|
+
}
|
|
4891
|
+
#project() {
|
|
4892
|
+
const snapshot = this.scope.getSnapshot();
|
|
4893
|
+
return Object.freeze({
|
|
4894
|
+
status: snapshot.status,
|
|
4895
|
+
automaticLearning: snapshot.value?.automaticLearning,
|
|
4896
|
+
writable: snapshot.writable,
|
|
4897
|
+
saving: this.#saving,
|
|
4898
|
+
error: this.#error
|
|
4899
|
+
});
|
|
4900
|
+
}
|
|
4901
|
+
#publish() {
|
|
4902
|
+
this.#snapshot = this.#project();
|
|
4903
|
+
for (const listener of this.#listeners) listener();
|
|
4904
|
+
}
|
|
4905
|
+
};
|
|
4906
|
+
function AutomaticLearningSettingsCard(props) {
|
|
4907
|
+
const state = (0, react.useSyncExternalStore)(props.controller.subscribe, props.controller.getSnapshot, props.controller.getSnapshot);
|
|
4908
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
4909
|
+
if (state.status === "unavailable") return null;
|
|
4910
|
+
const disabled = state.status !== "ready" || !state.writable || state.saving;
|
|
4911
|
+
return (0, react.createElement)("li", { "data-run2skill-settings-card": true }, (0, react.createElement)("button", {
|
|
4912
|
+
type: "button",
|
|
4913
|
+
"aria-expanded": open,
|
|
4914
|
+
onClick: () => {
|
|
4915
|
+
setOpen((value) => !value);
|
|
4916
|
+
}
|
|
4917
|
+
}, "Run2Skill", (0, react.createElement)("span", null, "将真实执行中的明确经验整理为待确认的技能草稿")), open ? (0, react.createElement)("div", null, (0, react.createElement)("label", null, (0, react.createElement)("input", {
|
|
4918
|
+
type: "checkbox",
|
|
4919
|
+
checked: state.automaticLearning === true,
|
|
4920
|
+
disabled,
|
|
4921
|
+
onChange: (event) => {
|
|
4922
|
+
props.controller.setAutomaticLearning(event.currentTarget.checked);
|
|
4923
|
+
}
|
|
4924
|
+
}), "自动学习"), (0, react.createElement)("p", null, "关闭后暂停普通自动学习;显式“保存为 Skill”仍会持久保存。已开始的分析继续使用启动时快照。"), (0, react.createElement)("p", null, "分析时沿用发起会话已经选择的模型。"), state.error === void 0 ? null : (0, react.createElement)("p", { role: "status" }, "设置已变化,请刷新后重试。"), (0, react.createElement)(PurgeSettingsSection, { controller: props.purgeController })) : null);
|
|
5464
4925
|
}
|
|
5465
|
-
function
|
|
5466
|
-
const
|
|
5467
|
-
const
|
|
5468
|
-
const
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
4926
|
+
function applyAutomaticLearningSettingsClient(context) {
|
|
4927
|
+
const controller = new AutomaticLearningSettingsController(context.settingsScope.bind({ namespace: "run2skill" }));
|
|
4928
|
+
const callPurge = async (endpoint, payload, signal) => await context.connection.rpc.call(RUN2SKILL_RPC_CHANNEL, endpoint, payload, signal);
|
|
4929
|
+
const purgeController = new PurgeSettingsController(callPurge, () => {
|
|
4930
|
+
return context.workspaces.list.getSnapshot().recentWorkspaceId;
|
|
4931
|
+
});
|
|
4932
|
+
purgeController.start();
|
|
4933
|
+
context.effect?.(() => () => {
|
|
4934
|
+
controller.dispose();
|
|
4935
|
+
purgeController.dispose();
|
|
4936
|
+
}, "run2skill: settings and purge");
|
|
4937
|
+
context.slots.inject("settings.plugin.item", () => context.slots.register({
|
|
4938
|
+
name: "settings.plugin.item",
|
|
4939
|
+
key: "run2skill",
|
|
5473
4940
|
inject: () => ({
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
getWorkspaceId
|
|
4941
|
+
controller,
|
|
4942
|
+
purgeController
|
|
5477
4943
|
})
|
|
5478
|
-
},
|
|
4944
|
+
}, AutomaticLearningSettingsCard));
|
|
5479
4945
|
}
|
|
5480
4946
|
//#endregion
|
|
5481
|
-
//#region src/client/
|
|
5482
|
-
const
|
|
5483
|
-
const
|
|
5484
|
-
const
|
|
5485
|
-
const
|
|
5486
|
-
const
|
|
5487
|
-
const
|
|
5488
|
-
const
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
const
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
4947
|
+
//#region src/client/proposal-inbox-wire.ts
|
|
4948
|
+
const identity = string().min(1).max(256);
|
|
4949
|
+
const sha256 = string().regex(/^[a-f0-9]{64}$/);
|
|
4950
|
+
const positiveInteger = number().int().safe().positive();
|
|
4951
|
+
const nonNegativeInteger = number().int().safe().nonnegative();
|
|
4952
|
+
const proposalId = string().regex(/^prop_[a-f0-9]{64}$/);
|
|
4953
|
+
const workItemId = string().regex(/^wi_[a-f0-9]{64}$/);
|
|
4954
|
+
const proposalRef = object({
|
|
4955
|
+
proposalId,
|
|
4956
|
+
revision: positiveInteger,
|
|
4957
|
+
digest: sha256
|
|
4958
|
+
}).strict();
|
|
4959
|
+
const processingState = _enum([
|
|
4960
|
+
"READY_FOR_REVIEW",
|
|
4961
|
+
"PUBLISHING",
|
|
4962
|
+
"NEEDS_ATTENTION",
|
|
4963
|
+
"TERMINAL"
|
|
4964
|
+
]);
|
|
4965
|
+
const publicationOutcome = _enum([
|
|
4966
|
+
"PENDING_REVIEW",
|
|
4967
|
+
"DISCARDED",
|
|
4968
|
+
"NEEDS_ATTENTION",
|
|
4969
|
+
"NEEDS_REFRESH",
|
|
4970
|
+
"PUBLISHED",
|
|
4971
|
+
"PUBLISH_FAILED"
|
|
4972
|
+
]);
|
|
4973
|
+
const rootDisplay = object({
|
|
4974
|
+
state: _enum(["EXISTING", "ABSENT"]),
|
|
4975
|
+
scope: _enum(["PROJECT", "USER"]),
|
|
4976
|
+
expectedProvider: identity,
|
|
4977
|
+
expectedSource: _enum(["project-dsh", "user-dsh"]),
|
|
4978
|
+
resolverVersion: identity,
|
|
4979
|
+
rootContractVersion: identity,
|
|
4980
|
+
resolutionContractDigest: sha256
|
|
4981
|
+
}).strict();
|
|
4982
|
+
const targetDisplay = object({ skillName: identity }).strict();
|
|
4983
|
+
const actionBinding = discriminatedUnion("kind", [
|
|
4984
|
+
object({
|
|
4985
|
+
kind: literal("CREATE"),
|
|
4986
|
+
rootBinding: rootDisplay,
|
|
4987
|
+
targetBinding: targetDisplay,
|
|
4988
|
+
expectedAbsence: object({ observedAt: string().min(1).max(64) }).strict()
|
|
4989
|
+
}).strict(),
|
|
4990
|
+
object({
|
|
4991
|
+
kind: literal("MERGE"),
|
|
4992
|
+
rootBinding: rootDisplay,
|
|
4993
|
+
targetBinding: targetDisplay,
|
|
4994
|
+
baseBinding: object({
|
|
4995
|
+
candidateKey: string().regex(/^cand_[a-f0-9]{64}$/),
|
|
4996
|
+
exactBytes: string().min(1).max(65536),
|
|
4997
|
+
bytesDigest: sha256,
|
|
4998
|
+
observedAt: string().min(1).max(64)
|
|
4999
|
+
}).strict()
|
|
5000
|
+
}).strict(),
|
|
5001
|
+
object({
|
|
5002
|
+
kind: literal("DISCARD"),
|
|
5003
|
+
coveringCandidateBinding: object({
|
|
5004
|
+
candidateKey: string().regex(/^cand_[a-f0-9]{64}$/),
|
|
5005
|
+
name: string().min(1).max(128),
|
|
5006
|
+
source: identity,
|
|
5007
|
+
content: string().min(1).max(65536),
|
|
5008
|
+
contentDigest: sha256,
|
|
5009
|
+
observedAt: string().min(1).max(64)
|
|
5010
|
+
}).strict()
|
|
5011
|
+
}).strict()
|
|
5012
|
+
]);
|
|
5013
|
+
const proposal = object({
|
|
5014
|
+
schemaVersion: literal(1),
|
|
5015
|
+
revision: positiveInteger,
|
|
5016
|
+
createdAt: string().min(1).max(64),
|
|
5017
|
+
sourceLearningProposalId: string().regex(/^lp_[a-f0-9]{64}$/),
|
|
5018
|
+
kind: _enum([
|
|
5019
|
+
"CREATE",
|
|
5020
|
+
"MERGE",
|
|
5021
|
+
"DISCARD"
|
|
5022
|
+
]),
|
|
5023
|
+
name: string().min(1).max(128),
|
|
5024
|
+
description: string().min(1).max(2048),
|
|
5025
|
+
whenToUse: string().min(1).max(4096),
|
|
5026
|
+
invocation: object({
|
|
5027
|
+
modelInvocable: literal(true),
|
|
5028
|
+
userInvocable: literal(false)
|
|
5029
|
+
}).strict(),
|
|
5030
|
+
exactSkillBytes: string().min(1).max(65536),
|
|
5031
|
+
skillBytesDigest: sha256,
|
|
5032
|
+
rendererVersion: identity,
|
|
5033
|
+
persistenceScope: _enum(["PROJECT", "USER"]),
|
|
5034
|
+
workspaceBinding: object({ workspaceId: identity }).strict().optional(),
|
|
5035
|
+
dshHomeBinding: object({
|
|
5036
|
+
resolutionKind: _enum([
|
|
5037
|
+
"CONFIGURATION",
|
|
5038
|
+
"ENVIRONMENT",
|
|
5039
|
+
"DEFAULT"
|
|
5518
5040
|
]),
|
|
5519
|
-
|
|
5520
|
-
}).strict()
|
|
5521
|
-
|
|
5041
|
+
identityDigest: sha256
|
|
5042
|
+
}).strict().optional(),
|
|
5043
|
+
supportingExperienceIds: array(string().regex(/^exp_[a-f0-9]{64}$/)).min(1).max(3),
|
|
5044
|
+
catalogObservationDigest: sha256,
|
|
5045
|
+
curationRationale: string().min(1).max(4096),
|
|
5046
|
+
actionBinding,
|
|
5047
|
+
proposalId,
|
|
5048
|
+
digest: sha256
|
|
5049
|
+
}).strict().refine((value) => value.kind === value.actionBinding.kind, {
|
|
5050
|
+
path: ["actionBinding"],
|
|
5051
|
+
message: "action binding kind must match proposal kind"
|
|
5052
|
+
});
|
|
5053
|
+
const evidence = object({
|
|
5054
|
+
source: literal("USER_DIRECT"),
|
|
5055
|
+
messageSeq: nonNegativeInteger,
|
|
5056
|
+
excerpt: string().max(8192),
|
|
5057
|
+
excerptDigest: sha256,
|
|
5058
|
+
redactionKinds: array(string().min(1).max(64)).max(16),
|
|
5059
|
+
truncated: boolean()
|
|
5522
5060
|
}).strict();
|
|
5523
|
-
const
|
|
5061
|
+
const experience = object({
|
|
5062
|
+
experienceId: string().regex(/^exp_[a-f0-9]{64}$/),
|
|
5063
|
+
type: _enum([
|
|
5064
|
+
"CORRECTION",
|
|
5065
|
+
"CONSTRAINT",
|
|
5066
|
+
"WORKFLOW"
|
|
5067
|
+
]),
|
|
5068
|
+
lesson: string().min(1).max(4096),
|
|
5069
|
+
persistenceScope: _enum(["PROJECT", "USER"]),
|
|
5070
|
+
evidenceStrength: literal("HIGH"),
|
|
5071
|
+
supportingEvidence: array(object({
|
|
5072
|
+
messageSeq: nonNegativeInteger,
|
|
5073
|
+
excerptDigest: sha256
|
|
5074
|
+
}).strict()).min(1).max(4),
|
|
5075
|
+
contextSummary: string().min(1).max(4096).optional()
|
|
5076
|
+
}).strict();
|
|
5077
|
+
const summary = object({
|
|
5524
5078
|
apiVersion: literal(1),
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
"
|
|
5529
|
-
"
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5079
|
+
status: _enum([
|
|
5080
|
+
"READY",
|
|
5081
|
+
"RECOVERING",
|
|
5082
|
+
"DEGRADED",
|
|
5083
|
+
"INCOMPATIBLE"
|
|
5084
|
+
]),
|
|
5085
|
+
recoveryLag: boolean(),
|
|
5086
|
+
lastHealthCode: string().min(1).max(128).optional(),
|
|
5087
|
+
queue: discriminatedUnion("completeness", [object({ completeness: literal("UNKNOWN") }).strict(), object({
|
|
5088
|
+
completeness: literal("KNOWN"),
|
|
5089
|
+
pendingReview: nonNegativeInteger,
|
|
5090
|
+
publishing: nonNegativeInteger,
|
|
5091
|
+
needsAttention: nonNegativeInteger
|
|
5092
|
+
}).strict()])
|
|
5535
5093
|
}).strict();
|
|
5536
|
-
const
|
|
5094
|
+
const listItem = object({
|
|
5095
|
+
workItemId,
|
|
5096
|
+
workItemRevision: positiveInteger,
|
|
5097
|
+
proposalRef,
|
|
5098
|
+
kind: _enum([
|
|
5099
|
+
"CREATE",
|
|
5100
|
+
"MERGE",
|
|
5101
|
+
"DISCARD"
|
|
5102
|
+
]),
|
|
5103
|
+
name: string().min(1).max(128),
|
|
5104
|
+
description: string().min(1).max(2048),
|
|
5105
|
+
persistenceScope: _enum(["PROJECT", "USER"]),
|
|
5106
|
+
createdAt: string().min(1).max(64),
|
|
5107
|
+
processingState: _enum([
|
|
5108
|
+
"READY_FOR_REVIEW",
|
|
5109
|
+
"PUBLISHING",
|
|
5110
|
+
"NEEDS_ATTENTION"
|
|
5111
|
+
]),
|
|
5112
|
+
publicationOutcome: _enum([
|
|
5113
|
+
"PENDING_REVIEW",
|
|
5114
|
+
"NEEDS_ATTENTION",
|
|
5115
|
+
"NEEDS_REFRESH",
|
|
5116
|
+
"PUBLISH_FAILED"
|
|
5117
|
+
])
|
|
5118
|
+
}).strict();
|
|
5119
|
+
const listPage = object({
|
|
5537
5120
|
apiVersion: literal(1),
|
|
5538
|
-
|
|
5539
|
-
|
|
5121
|
+
items: array(listItem).max(20),
|
|
5122
|
+
nextCursor: string().regex(/^c_[1-9][0-9]*_[1-9][0-9]*_[a-f0-9]{64}$/).optional()
|
|
5123
|
+
}).strict();
|
|
5124
|
+
const detail = object({
|
|
5540
5125
|
apiVersion: literal(1),
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
"
|
|
5547
|
-
"
|
|
5548
|
-
"DELETING_WORK_ITEMS",
|
|
5549
|
-
"VERIFYING"
|
|
5126
|
+
workItemId,
|
|
5127
|
+
workItemRevision: positiveInteger,
|
|
5128
|
+
processingState,
|
|
5129
|
+
reviewDecision: _enum([
|
|
5130
|
+
"PENDING",
|
|
5131
|
+
"APPROVED",
|
|
5132
|
+
"REJECTED"
|
|
5550
5133
|
]),
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
code: string().min(1).max(128),
|
|
5562
|
-
message: string().max(512),
|
|
5563
|
-
details: object({ busyPublicationCount: count.optional() }).strict().optional()
|
|
5564
|
-
}).strict()
|
|
5134
|
+
publicationOutcome,
|
|
5135
|
+
proposal,
|
|
5136
|
+
sessionCoordinate: object({
|
|
5137
|
+
rootSessionId: identity,
|
|
5138
|
+
sessionCreatedAt: nonNegativeInteger,
|
|
5139
|
+
turn: nonNegativeInteger,
|
|
5140
|
+
turnEndSeq: nonNegativeInteger
|
|
5141
|
+
}).strict(),
|
|
5142
|
+
evidenceRefs: array(evidence).max(16),
|
|
5143
|
+
experiences: array(experience).max(3)
|
|
5565
5144
|
}).strict();
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5145
|
+
const receipt = object({
|
|
5146
|
+
apiVersion: literal(1),
|
|
5147
|
+
workItemId,
|
|
5148
|
+
workItemRevision: positiveInteger,
|
|
5149
|
+
proposalRef,
|
|
5150
|
+
changed: boolean(),
|
|
5151
|
+
processingState,
|
|
5152
|
+
reviewDecision: _enum([
|
|
5153
|
+
"PENDING",
|
|
5154
|
+
"APPROVED",
|
|
5155
|
+
"REJECTED"
|
|
5156
|
+
]),
|
|
5157
|
+
publicationOutcome
|
|
5158
|
+
}).strict();
|
|
5159
|
+
function parseValue(schema, value) {
|
|
5160
|
+
const parsed = object({
|
|
5161
|
+
ok: literal(true),
|
|
5162
|
+
value: schema
|
|
5163
|
+
}).strict().safeParse(value);
|
|
5164
|
+
return parsed.success ? parsed.data.value : void 0;
|
|
5165
|
+
}
|
|
5166
|
+
function parseProposalSummary(value) {
|
|
5167
|
+
return parseValue(summary, value);
|
|
5168
|
+
}
|
|
5169
|
+
function parseProposalList(value) {
|
|
5170
|
+
return parseValue(listPage, value);
|
|
5171
|
+
}
|
|
5172
|
+
function parseProposalDetail(value) {
|
|
5173
|
+
return parseValue(detail, value);
|
|
5174
|
+
}
|
|
5175
|
+
function parseMutationReceipt(value) {
|
|
5176
|
+
return parseValue(receipt, value);
|
|
5177
|
+
}
|
|
5178
|
+
//#endregion
|
|
5179
|
+
//#region src/client/proposal-inbox.ts
|
|
5180
|
+
const PROPOSAL_POLL_INTERVAL_MS = 1e4;
|
|
5181
|
+
const PROPOSAL_ACTIVE_POLL_INTERVAL_MS = 2e3;
|
|
5182
|
+
const endpoint = {
|
|
5183
|
+
summary: "summary",
|
|
5184
|
+
list: "proposals/list",
|
|
5185
|
+
get: "proposals/get",
|
|
5186
|
+
approve: "proposals/approve",
|
|
5187
|
+
reject: "proposals/reject",
|
|
5188
|
+
retry: "proposals/retry",
|
|
5189
|
+
confirmDiscard: "coverage/confirm-discard"
|
|
5572
5190
|
};
|
|
5573
|
-
function
|
|
5191
|
+
function describeProposalOutcome(detail) {
|
|
5192
|
+
if (detail.publicationOutcome === "PUBLISHED") return "Skill 已保存,DSH 已确认可以使用";
|
|
5193
|
+
if (detail.publicationOutcome === "DISCARDED") return "技能草稿已放弃,Skill 未更改";
|
|
5194
|
+
if (detail.publicationOutcome === "NEEDS_REFRESH") return "保存目标已变化,需要生成新的技能草稿";
|
|
5195
|
+
if (detail.publicationOutcome === "PUBLISH_FAILED") return "保存失败,可重试";
|
|
5196
|
+
if (detail.processingState === "NEEDS_ATTENTION") return "需要处理后才能继续";
|
|
5197
|
+
if (detail.processingState === "PUBLISHING") return "已确认,正在保存";
|
|
5198
|
+
return "待审核";
|
|
5199
|
+
}
|
|
5200
|
+
function describeProposalListItem(item) {
|
|
5201
|
+
return describeProposalOutcome(item);
|
|
5202
|
+
}
|
|
5203
|
+
function describeReviewDecision(decision) {
|
|
5204
|
+
if (decision === "APPROVED") return "已确认";
|
|
5205
|
+
if (decision === "REJECTED") return "已放弃";
|
|
5206
|
+
return "待决定";
|
|
5207
|
+
}
|
|
5208
|
+
function describeProcessingState(state) {
|
|
5209
|
+
if (state === "PUBLISHING") return "正在保存";
|
|
5210
|
+
if (state === "NEEDS_ATTENTION") return "需要处理";
|
|
5211
|
+
if (state === "TERMINAL") return "已结束";
|
|
5212
|
+
return "待审核";
|
|
5213
|
+
}
|
|
5214
|
+
function describePublicationOutcome(outcome) {
|
|
5215
|
+
if (outcome === "PUBLISHED") return "已保存,DSH 已确认可以使用";
|
|
5216
|
+
if (outcome === "DISCARDED") return "未修改 Skill,技能草稿已放弃";
|
|
5217
|
+
if (outcome === "NEEDS_REFRESH") return "保存目标已变化,需要新的技能草稿";
|
|
5218
|
+
if (outcome === "PUBLISH_FAILED") return "保存失败,可重试";
|
|
5219
|
+
if (outcome === "NEEDS_ATTENTION") return "保存前需要先处理当前问题";
|
|
5220
|
+
return "尚未产生保存结果";
|
|
5221
|
+
}
|
|
5222
|
+
function browserEnvironment$1() {
|
|
5574
5223
|
return {
|
|
5575
5224
|
isVisible: () => typeof document === "undefined" || document.visibilityState === "visible",
|
|
5576
5225
|
setInterval: (callback, delayMs) => globalThis.setInterval(callback, delayMs),
|
|
5577
5226
|
clearInterval: (handle) => {
|
|
5578
5227
|
globalThis.clearInterval(handle);
|
|
5579
5228
|
},
|
|
5229
|
+
onFocus: (listener) => {
|
|
5230
|
+
if (typeof window === "undefined") return () => void 0;
|
|
5231
|
+
window.addEventListener("focus", listener);
|
|
5232
|
+
return () => {
|
|
5233
|
+
window.removeEventListener("focus", listener);
|
|
5234
|
+
};
|
|
5235
|
+
},
|
|
5580
5236
|
onVisibilityChange: (listener) => {
|
|
5581
5237
|
if (typeof document === "undefined") return () => void 0;
|
|
5582
5238
|
document.addEventListener("visibilitychange", listener);
|
|
5583
5239
|
return () => {
|
|
5584
5240
|
document.removeEventListener("visibilitychange", listener);
|
|
5585
5241
|
};
|
|
5242
|
+
},
|
|
5243
|
+
onOnline: (listener) => {
|
|
5244
|
+
if (typeof window === "undefined") return () => void 0;
|
|
5245
|
+
window.addEventListener("online", listener);
|
|
5246
|
+
return () => {
|
|
5247
|
+
window.removeEventListener("online", listener);
|
|
5248
|
+
};
|
|
5586
5249
|
}
|
|
5587
|
-
};
|
|
5588
|
-
}
|
|
5589
|
-
function parseValue(schema, response) {
|
|
5590
|
-
const success = object({
|
|
5591
|
-
ok: literal(true),
|
|
5592
|
-
value: schema
|
|
5593
|
-
}).strict().safeParse(response);
|
|
5594
|
-
if (success.success) return success.data.value;
|
|
5595
|
-
const failure = errorSchema.safeParse(response);
|
|
5596
|
-
if (failure.success) {
|
|
5597
|
-
const busy = failure.data.error.details?.busyPublicationCount;
|
|
5598
|
-
throw new PurgeResponseError({
|
|
5599
|
-
code: failure.data.error.code,
|
|
5600
|
-
...typeof busy === "number" && Number.isSafeInteger(busy) && busy >= 0 ? { busyPublicationCount: busy } : {}
|
|
5601
|
-
});
|
|
5602
|
-
}
|
|
5603
|
-
throw new PurgeResponseError({ code: "PURGE_STORAGE_UNAVAILABLE" });
|
|
5604
|
-
}
|
|
5605
|
-
function completionAnnouncement(scope, receipt) {
|
|
5606
|
-
return `${scope === void 0 ? "run2skill 数据" : `${scope} run2skill 数据`}清理完成:${String(receipt.deletedWorkItems)} 条待处理数据,${String(receipt.deletedLineages)} 条发布沿袭记录。`;
|
|
5607
|
-
}
|
|
5608
|
-
function polledCompletionAnnouncement(scope) {
|
|
5609
|
-
return `${scope === void 0 ? "" : `${scope} `}run2skill 数据清理完成。`;
|
|
5610
|
-
}
|
|
5611
|
-
function errorAnnouncement(error, durableBoundary = false) {
|
|
5612
|
-
if (error.code === "PURGE_PREVIEW_STALE") return "清理预览已失效,请重新预览后确认。";
|
|
5613
|
-
if (error.code === "PURGE_BUSY") return `当前有 ${String(error.busyPublicationCount ?? 0)} 条正在发布;请等待发布完成后重新预览。`;
|
|
5614
|
-
if (error.code === "PURGE_ALREADY_RUNNING") return "已有一项数据清理正在运行,请等待完成。";
|
|
5615
|
-
if (error.code === "PURGE_SCOPE_UNAVAILABLE") return "当前作用域无法可靠确认,未执行清理。";
|
|
5616
|
-
if (error.code === "PURGE_INCOMPATIBLE") return "当前数据版本不兼容,未执行清理。";
|
|
5617
|
-
if (error.code === "PURGE_FENCE_LIMIT") return "PROJECT 清理保护记录已达上限,未执行清理。";
|
|
5618
|
-
return durableBoundary ? "数据清理未完成;已建立的清理边界继续隐藏,可重试。" : "数据清理暂不可用;未确认已建立清理边界,请稍后重试。";
|
|
5250
|
+
};
|
|
5619
5251
|
}
|
|
5620
5252
|
const initialState = {
|
|
5621
|
-
|
|
5622
|
-
|
|
5253
|
+
open: false,
|
|
5254
|
+
summaryPhase: "LOADING",
|
|
5255
|
+
listPhase: "IDLE",
|
|
5256
|
+
items: [],
|
|
5257
|
+
detailPhase: "IDLE",
|
|
5623
5258
|
mutationPending: false,
|
|
5624
5259
|
announcement: ""
|
|
5625
5260
|
};
|
|
5626
|
-
var
|
|
5261
|
+
var ProposalInboxController = class {
|
|
5262
|
+
workspaceId;
|
|
5627
5263
|
call;
|
|
5628
|
-
getWorkspaceId;
|
|
5629
5264
|
environment;
|
|
5265
|
+
options;
|
|
5630
5266
|
#listeners = /* @__PURE__ */ new Set();
|
|
5631
5267
|
#state = initialState;
|
|
5632
5268
|
#started = false;
|
|
5269
|
+
#active = true;
|
|
5633
5270
|
#disposed = false;
|
|
5634
5271
|
#pending;
|
|
5635
5272
|
#abort;
|
|
5636
|
-
#statusPending;
|
|
5637
|
-
#statusAbort;
|
|
5638
5273
|
#timer;
|
|
5639
5274
|
#timerDelay;
|
|
5275
|
+
#removeFocus;
|
|
5640
5276
|
#removeVisibility;
|
|
5641
|
-
|
|
5277
|
+
#removeOnline;
|
|
5278
|
+
constructor(workspaceId, call, environment = browserEnvironment$1(), options = {}) {
|
|
5279
|
+
this.workspaceId = workspaceId;
|
|
5642
5280
|
this.call = call;
|
|
5643
|
-
this.getWorkspaceId = getWorkspaceId;
|
|
5644
5281
|
this.environment = environment;
|
|
5282
|
+
this.options = options;
|
|
5645
5283
|
}
|
|
5646
5284
|
snapshot = () => this.#state;
|
|
5647
5285
|
subscribe = (listener) => {
|
|
@@ -5653,208 +5291,275 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5653
5291
|
start() {
|
|
5654
5292
|
if (this.#started || this.#disposed) return;
|
|
5655
5293
|
this.#started = true;
|
|
5294
|
+
const refresh = () => {
|
|
5295
|
+
if (this.#active && this.environment.isVisible()) this.#refreshVisible();
|
|
5296
|
+
};
|
|
5297
|
+
this.#removeFocus = this.environment.onFocus(refresh);
|
|
5298
|
+
this.#removeOnline = this.environment.onOnline(refresh);
|
|
5656
5299
|
this.#removeVisibility = this.environment.onVisibilityChange(() => {
|
|
5657
|
-
if (this.environment.isVisible()) {
|
|
5300
|
+
if (this.#active && this.environment.isVisible()) {
|
|
5658
5301
|
this.#schedule();
|
|
5659
|
-
this.#
|
|
5302
|
+
this.#refreshVisible();
|
|
5660
5303
|
} else this.#unschedule();
|
|
5661
5304
|
});
|
|
5662
|
-
if (this.environment.isVisible()) {
|
|
5305
|
+
if (this.#active && this.environment.isVisible()) {
|
|
5663
5306
|
this.#schedule();
|
|
5664
|
-
this.#
|
|
5307
|
+
this.#refreshVisible();
|
|
5665
5308
|
}
|
|
5666
5309
|
}
|
|
5310
|
+
pause() {
|
|
5311
|
+
if (this.#disposed || !this.#active) return;
|
|
5312
|
+
this.#active = false;
|
|
5313
|
+
this.#unschedule();
|
|
5314
|
+
this.#abort?.abort();
|
|
5315
|
+
}
|
|
5316
|
+
resume() {
|
|
5317
|
+
if (this.#disposed || this.#active) return;
|
|
5318
|
+
this.#active = true;
|
|
5319
|
+
if (!this.#started || !this.environment.isVisible()) return;
|
|
5320
|
+
this.#schedule();
|
|
5321
|
+
this.whenIdle().then(() => {
|
|
5322
|
+
if (this.#active && !this.#disposed) this.#refreshVisible();
|
|
5323
|
+
});
|
|
5324
|
+
}
|
|
5667
5325
|
whenIdle() {
|
|
5668
|
-
return
|
|
5326
|
+
return this.#pending ?? Promise.resolve();
|
|
5669
5327
|
}
|
|
5670
|
-
async
|
|
5671
|
-
if (this.#disposed
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5328
|
+
async open() {
|
|
5329
|
+
if (this.#disposed) return;
|
|
5330
|
+
this.#publish({
|
|
5331
|
+
...this.#state,
|
|
5332
|
+
open: true
|
|
5333
|
+
});
|
|
5334
|
+
await this.whenIdle();
|
|
5335
|
+
if (this.#disposed || !this.#state.open) return;
|
|
5336
|
+
if (this.options.attentionDriven === true && this.#scopeAccess().actions.length === 0) {
|
|
5675
5337
|
this.#publish({
|
|
5676
5338
|
...this.#state,
|
|
5677
|
-
|
|
5678
|
-
|
|
5339
|
+
listPhase: "READY",
|
|
5340
|
+
items: [],
|
|
5341
|
+
selectedProposalId: void 0,
|
|
5342
|
+
detailPhase: "IDLE",
|
|
5343
|
+
detail: void 0
|
|
5679
5344
|
});
|
|
5680
5345
|
return;
|
|
5681
5346
|
}
|
|
5347
|
+
await this.#loadList();
|
|
5348
|
+
}
|
|
5349
|
+
close() {
|
|
5350
|
+
if (this.#disposed) return;
|
|
5351
|
+
this.#publish({
|
|
5352
|
+
...this.#state,
|
|
5353
|
+
open: false
|
|
5354
|
+
});
|
|
5355
|
+
}
|
|
5356
|
+
async select(proposalId) {
|
|
5357
|
+
if (this.#disposed || !/^prop_[a-f0-9]{64}$/.test(proposalId)) return;
|
|
5358
|
+
await this.whenIdle();
|
|
5359
|
+
if (this.#disposed || !this.#state.open) return;
|
|
5682
5360
|
await this.#execute(async (signal) => {
|
|
5683
5361
|
this.#publish({
|
|
5684
5362
|
...this.#state,
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
error: void 0,
|
|
5363
|
+
selectedProposalId: proposalId,
|
|
5364
|
+
detailPhase: "LOADING",
|
|
5365
|
+
detail: void 0,
|
|
5689
5366
|
announcement: ""
|
|
5690
5367
|
});
|
|
5691
|
-
const
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
} : {
|
|
5368
|
+
const scopeAccess = this.#scopeAccess();
|
|
5369
|
+
const action = scopeAccess.actions.find((candidate) => candidate.proposalRef.proposalId === proposalId);
|
|
5370
|
+
if (action === void 0) throw new Error("proposal action is stale");
|
|
5371
|
+
const detail = parseProposalDetail(await this.call(endpoint.get, {
|
|
5696
5372
|
apiVersion: 1,
|
|
5697
|
-
|
|
5373
|
+
proposalId,
|
|
5374
|
+
currentScope: scopeAccess.currentScope,
|
|
5375
|
+
action
|
|
5698
5376
|
}, signal));
|
|
5377
|
+
if (detail === void 0) throw new Error("invalid detail response");
|
|
5699
5378
|
this.#publish({
|
|
5700
5379
|
...this.#state,
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5380
|
+
detailPhase: "READY",
|
|
5381
|
+
detail,
|
|
5382
|
+
announcement: describeProposalOutcome(detail)
|
|
5704
5383
|
});
|
|
5705
|
-
}, (
|
|
5384
|
+
}, () => {
|
|
5706
5385
|
this.#publish({
|
|
5707
5386
|
...this.#state,
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
error,
|
|
5711
|
-
announcement: errorAnnouncement(error)
|
|
5387
|
+
detailPhase: "ERROR",
|
|
5388
|
+
detail: void 0
|
|
5712
5389
|
});
|
|
5713
5390
|
});
|
|
5714
5391
|
}
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
this.#
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
}
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5392
|
+
async mutate(action) {
|
|
5393
|
+
await this.whenIdle();
|
|
5394
|
+
const detail = this.#state.detail;
|
|
5395
|
+
if (this.#disposed || detail === void 0 || this.#state.mutationPending) return;
|
|
5396
|
+
const proposalRef = {
|
|
5397
|
+
proposalId: detail.proposal.proposalId,
|
|
5398
|
+
revision: detail.proposal.revision,
|
|
5399
|
+
digest: detail.proposal.digest
|
|
5400
|
+
};
|
|
5401
|
+
const targetEndpoint = action === "APPROVE" ? endpoint.approve : action === "REJECT" ? endpoint.reject : action === "RETRY" ? endpoint.retry : endpoint.confirmDiscard;
|
|
5402
|
+
const request = {
|
|
5403
|
+
apiVersion: 1,
|
|
5404
|
+
workItemId: detail.workItemId,
|
|
5405
|
+
workItemRevision: detail.workItemRevision,
|
|
5406
|
+
proposalRef,
|
|
5407
|
+
currentScope: this.#scopeAccess().currentScope,
|
|
5408
|
+
action: this.#scopeAccess().actions.find((candidate) => candidate.proposalRef.proposalId === proposalRef.proposalId),
|
|
5409
|
+
...action === "REJECT" ? { confirm: true } : {}
|
|
5410
|
+
};
|
|
5729
5411
|
await this.#execute(async (signal) => {
|
|
5412
|
+
if (request.action === void 0) throw new Error("proposal action is stale");
|
|
5730
5413
|
this.#publish({
|
|
5731
5414
|
...this.#state,
|
|
5732
5415
|
mutationPending: true,
|
|
5733
|
-
error: void 0,
|
|
5734
5416
|
announcement: ""
|
|
5735
5417
|
});
|
|
5736
|
-
const receipt =
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
digest: preview.digest
|
|
5740
|
-
}, signal));
|
|
5741
|
-
this.#acceptReceipt(scope, receipt);
|
|
5742
|
-
}, async (error) => {
|
|
5418
|
+
const receipt = parseMutationReceipt(await this.call(targetEndpoint, request, signal));
|
|
5419
|
+
if (receipt === void 0) throw new Error("invalid mutation receipt");
|
|
5420
|
+
const announcement = describeProposalOutcome(receipt) || "技能草稿状态已更新";
|
|
5743
5421
|
this.#publish({
|
|
5744
5422
|
...this.#state,
|
|
5745
5423
|
mutationPending: false,
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
async retry() {
|
|
5755
|
-
const status = this.#state.status;
|
|
5756
|
-
if (this.#disposed || status?.state !== "IN_PROGRESS" || status.lastError === void 0 || this.#state.mutationPending) return;
|
|
5757
|
-
await this.#execute(async (signal) => {
|
|
5758
|
-
this.#publish({
|
|
5759
|
-
...this.#state,
|
|
5760
|
-
mutationPending: true,
|
|
5761
|
-
error: void 0,
|
|
5762
|
-
announcement: "正在重试数据清理。"
|
|
5424
|
+
announcement,
|
|
5425
|
+
detail: {
|
|
5426
|
+
...detail,
|
|
5427
|
+
workItemRevision: receipt.workItemRevision,
|
|
5428
|
+
processingState: receipt.processingState,
|
|
5429
|
+
reviewDecision: receipt.reviewDecision,
|
|
5430
|
+
publicationOutcome: receipt.publicationOutcome
|
|
5431
|
+
}
|
|
5763
5432
|
});
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5433
|
+
try {
|
|
5434
|
+
await this.#refreshWithin(signal);
|
|
5435
|
+
} catch {
|
|
5436
|
+
if (!this.#disposed && !signal.aborted) this.#publish({
|
|
5437
|
+
...this.#state,
|
|
5438
|
+
summaryPhase: this.#state.summary === void 0 ? "UNAVAILABLE" : "STALE",
|
|
5439
|
+
listPhase: this.#state.listPhase === "LOADING" ? "ERROR" : this.#state.listPhase
|
|
5440
|
+
});
|
|
5441
|
+
}
|
|
5442
|
+
}, () => {
|
|
5770
5443
|
this.#publish({
|
|
5771
5444
|
...this.#state,
|
|
5772
5445
|
mutationPending: false,
|
|
5773
|
-
|
|
5774
|
-
announcement: errorAnnouncement(error, true)
|
|
5446
|
+
announcement: "操作未完成,请刷新后重试"
|
|
5775
5447
|
});
|
|
5776
|
-
await this.#readStatusAfterFailure(error, true);
|
|
5777
5448
|
});
|
|
5778
5449
|
}
|
|
5779
5450
|
dispose() {
|
|
5780
5451
|
if (this.#disposed) return;
|
|
5781
5452
|
this.#disposed = true;
|
|
5782
5453
|
this.#unschedule();
|
|
5454
|
+
this.#removeFocus?.();
|
|
5783
5455
|
this.#removeVisibility?.();
|
|
5456
|
+
this.#removeOnline?.();
|
|
5457
|
+
this.#removeFocus = void 0;
|
|
5784
5458
|
this.#removeVisibility = void 0;
|
|
5459
|
+
this.#removeOnline = void 0;
|
|
5785
5460
|
this.#abort?.abort();
|
|
5786
|
-
this.#statusAbort?.abort();
|
|
5787
5461
|
this.#listeners.clear();
|
|
5788
5462
|
}
|
|
5789
|
-
#
|
|
5790
|
-
if (
|
|
5463
|
+
async #refreshVisible() {
|
|
5464
|
+
if (this.#disposed || !this.#active || !this.environment.isVisible()) return;
|
|
5465
|
+
await this.#execute(async (signal) => {
|
|
5466
|
+
await this.#refreshWithin(signal);
|
|
5467
|
+
}, () => {
|
|
5791
5468
|
this.#publish({
|
|
5792
5469
|
...this.#state,
|
|
5793
|
-
|
|
5794
|
-
status: {
|
|
5795
|
-
apiVersion: 1,
|
|
5796
|
-
state: "IDLE"
|
|
5797
|
-
},
|
|
5798
|
-
inProgressReceipt: void 0,
|
|
5799
|
-
preview: void 0,
|
|
5800
|
-
previewScope: void 0,
|
|
5801
|
-
mutationPending: false,
|
|
5802
|
-
error: void 0,
|
|
5803
|
-
announcement: completionAnnouncement(scope, receipt)
|
|
5470
|
+
summaryPhase: this.#state.summary === void 0 ? "UNAVAILABLE" : "STALE"
|
|
5804
5471
|
});
|
|
5472
|
+
});
|
|
5473
|
+
}
|
|
5474
|
+
async #refreshWithin(signal) {
|
|
5475
|
+
if (this.options.attentionDriven === true) {
|
|
5476
|
+
if (!this.#state.open) return;
|
|
5477
|
+
if (this.#scopeAccess().actions.length === 0) {
|
|
5478
|
+
this.#publish({
|
|
5479
|
+
...this.#state,
|
|
5480
|
+
listPhase: "READY",
|
|
5481
|
+
items: [],
|
|
5482
|
+
selectedProposalId: void 0,
|
|
5483
|
+
detailPhase: "IDLE",
|
|
5484
|
+
detail: void 0
|
|
5485
|
+
});
|
|
5486
|
+
return;
|
|
5487
|
+
}
|
|
5488
|
+
await this.#loadListWithin(signal);
|
|
5489
|
+
await this.#refreshPublishingDetailWithin(signal);
|
|
5805
5490
|
return;
|
|
5806
5491
|
}
|
|
5492
|
+
const summary = parseProposalSummary(await this.call(endpoint.summary, {
|
|
5493
|
+
apiVersion: 1,
|
|
5494
|
+
workspaceId: this.workspaceId
|
|
5495
|
+
}, signal));
|
|
5496
|
+
if (summary === void 0) throw new Error("invalid summary response");
|
|
5807
5497
|
this.#publish({
|
|
5808
5498
|
...this.#state,
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
inProgressReceipt: receipt,
|
|
5812
|
-
announcement: `run2skill 数据清理进行中:${receipt.phase ?? "HIDING"}。`
|
|
5499
|
+
summaryPhase: "READY",
|
|
5500
|
+
summary
|
|
5813
5501
|
});
|
|
5502
|
+
if (!this.#state.open) return;
|
|
5503
|
+
await this.#loadListWithin(signal);
|
|
5504
|
+
await this.#refreshPublishingDetailWithin(signal);
|
|
5814
5505
|
}
|
|
5815
|
-
async #
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
this.#
|
|
5506
|
+
async #refreshPublishingDetailWithin(signal) {
|
|
5507
|
+
const selected = this.#state.selectedProposalId;
|
|
5508
|
+
if (selected !== void 0 && this.#state.detail?.processingState === "PUBLISHING") {
|
|
5509
|
+
const scopeAccess = this.#scopeAccess();
|
|
5510
|
+
const action = scopeAccess.actions.find((candidate) => candidate.proposalRef.proposalId === selected);
|
|
5511
|
+
if (action === void 0) return;
|
|
5512
|
+
const detail = parseProposalDetail(await this.call(endpoint.get, {
|
|
5513
|
+
apiVersion: 1,
|
|
5514
|
+
proposalId: selected,
|
|
5515
|
+
currentScope: scopeAccess.currentScope,
|
|
5516
|
+
action
|
|
5517
|
+
}, signal));
|
|
5518
|
+
if (detail !== void 0) this.#publish({
|
|
5819
5519
|
...this.#state,
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
announcement: errorAnnouncement(error, durableBoundary || status.state === "IN_PROGRESS")
|
|
5520
|
+
detailPhase: "READY",
|
|
5521
|
+
detail,
|
|
5522
|
+
announcement: describeProposalOutcome(detail) || this.#state.announcement
|
|
5824
5523
|
});
|
|
5825
|
-
}
|
|
5826
|
-
}
|
|
5827
|
-
async #
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5524
|
+
}
|
|
5525
|
+
}
|
|
5526
|
+
async #loadList() {
|
|
5527
|
+
await this.#execute(async (signal) => {
|
|
5528
|
+
await this.#loadListWithin(signal);
|
|
5529
|
+
}, () => {
|
|
5530
|
+
this.#publish({
|
|
5531
|
+
...this.#state,
|
|
5532
|
+
listPhase: "ERROR"
|
|
5533
|
+
});
|
|
5534
|
+
});
|
|
5535
|
+
}
|
|
5536
|
+
async #loadListWithin(signal) {
|
|
5537
|
+
if (this.#state.listPhase !== "READY") this.#publish({
|
|
5538
|
+
...this.#state,
|
|
5539
|
+
listPhase: "LOADING"
|
|
5540
|
+
});
|
|
5541
|
+
const items = [];
|
|
5542
|
+
let cursor;
|
|
5543
|
+
for (let pageIndex = 0; pageIndex < 10; pageIndex += 1) {
|
|
5544
|
+
const page = parseProposalList(await this.call(endpoint.list, {
|
|
5545
|
+
apiVersion: 1,
|
|
5546
|
+
currentScope: this.#scopeAccess().currentScope,
|
|
5547
|
+
limit: 20,
|
|
5548
|
+
...cursor === void 0 ? {} : { cursor }
|
|
5549
|
+
}, signal));
|
|
5550
|
+
if (page === void 0) throw new Error("invalid list response");
|
|
5551
|
+
items.push(...page.items);
|
|
5552
|
+
cursor = page.nextCursor;
|
|
5553
|
+
if (cursor === void 0) {
|
|
5847
5554
|
this.#publish({
|
|
5848
5555
|
...this.#state,
|
|
5849
|
-
|
|
5556
|
+
listPhase: "READY",
|
|
5557
|
+
items
|
|
5850
5558
|
});
|
|
5559
|
+
return;
|
|
5851
5560
|
}
|
|
5852
|
-
}
|
|
5853
|
-
|
|
5854
|
-
await pending.finally(() => {
|
|
5855
|
-
if (this.#statusPending === pending) this.#statusPending = void 0;
|
|
5856
|
-
if (this.#statusAbort === abort) this.#statusAbort = void 0;
|
|
5857
|
-
});
|
|
5561
|
+
}
|
|
5562
|
+
throw new Error("proposal list page limit exceeded");
|
|
5858
5563
|
}
|
|
5859
5564
|
async #execute(task, onError) {
|
|
5860
5565
|
if (this.#disposed || this.#pending !== void 0) return this.#pending ?? Promise.resolve();
|
|
@@ -5863,8 +5568,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5863
5568
|
const pending = (async () => {
|
|
5864
5569
|
try {
|
|
5865
5570
|
await task(abort.signal);
|
|
5866
|
-
} catch
|
|
5867
|
-
if (!this.#disposed && !abort.signal.aborted)
|
|
5571
|
+
} catch {
|
|
5572
|
+
if (!this.#disposed && !abort.signal.aborted) onError();
|
|
5868
5573
|
}
|
|
5869
5574
|
})();
|
|
5870
5575
|
this.#pending = pending;
|
|
@@ -5874,13 +5579,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5874
5579
|
});
|
|
5875
5580
|
}
|
|
5876
5581
|
#schedule() {
|
|
5877
|
-
if (this.#disposed) return;
|
|
5878
|
-
const
|
|
5582
|
+
if (this.#disposed || !this.#active) return;
|
|
5583
|
+
const hasVisibleWork = this.options.attentionDriven !== true || this.#scopeAccess().actions.length > 0;
|
|
5584
|
+
const delay = this.#state.open && hasVisibleWork ? PROPOSAL_ACTIVE_POLL_INTERVAL_MS : PROPOSAL_POLL_INTERVAL_MS;
|
|
5879
5585
|
if (this.#timer !== void 0 && this.#timerDelay === delay) return;
|
|
5880
5586
|
this.#unschedule();
|
|
5881
5587
|
this.#timerDelay = delay;
|
|
5882
5588
|
this.#timer = this.environment.setInterval(() => {
|
|
5883
|
-
this.#
|
|
5589
|
+
this.#refreshVisible();
|
|
5884
5590
|
}, delay);
|
|
5885
5591
|
}
|
|
5886
5592
|
#unschedule() {
|
|
@@ -5892,26 +5598,406 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5892
5598
|
#publish(state) {
|
|
5893
5599
|
if (this.#disposed) return;
|
|
5894
5600
|
this.#state = state;
|
|
5895
|
-
if (this.#started && this.environment.isVisible()) this.#schedule();
|
|
5601
|
+
if (this.#started && this.#active && this.environment.isVisible()) this.#schedule();
|
|
5896
5602
|
for (const listener of this.#listeners) listener();
|
|
5897
5603
|
}
|
|
5604
|
+
#scopeAccess() {
|
|
5605
|
+
return this.options.scopeAccess?.() ?? {
|
|
5606
|
+
currentScope: {
|
|
5607
|
+
kind: "WORKSPACE",
|
|
5608
|
+
generation: 1,
|
|
5609
|
+
workspaceId: this.workspaceId
|
|
5610
|
+
},
|
|
5611
|
+
actions: []
|
|
5612
|
+
};
|
|
5613
|
+
}
|
|
5898
5614
|
};
|
|
5899
|
-
const
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5615
|
+
const formatCharacters = /* @__PURE__ */ new Map([
|
|
5616
|
+
[0, "NULL"],
|
|
5617
|
+
[9, "CHARACTER TABULATION"],
|
|
5618
|
+
[1564, "ARABIC LETTER MARK"],
|
|
5619
|
+
[8203, "ZERO WIDTH SPACE"],
|
|
5620
|
+
[8204, "ZERO WIDTH NON-JOINER"],
|
|
5621
|
+
[8205, "ZERO WIDTH JOINER"],
|
|
5622
|
+
[8206, "LEFT-TO-RIGHT MARK"],
|
|
5623
|
+
[8207, "RIGHT-TO-LEFT MARK"],
|
|
5624
|
+
[8234, "LEFT-TO-RIGHT EMBEDDING"],
|
|
5625
|
+
[8235, "RIGHT-TO-LEFT EMBEDDING"],
|
|
5626
|
+
[8236, "POP DIRECTIONAL FORMATTING"],
|
|
5627
|
+
[8237, "LEFT-TO-RIGHT OVERRIDE"],
|
|
5628
|
+
[8238, "RIGHT-TO-LEFT OVERRIDE"],
|
|
5629
|
+
[8288, "WORD JOINER"],
|
|
5630
|
+
[8294, "LEFT-TO-RIGHT ISOLATE"],
|
|
5631
|
+
[8295, "RIGHT-TO-LEFT ISOLATE"],
|
|
5632
|
+
[8296, "FIRST STRONG ISOLATE"],
|
|
5633
|
+
[8297, "POP DIRECTIONAL ISOLATE"],
|
|
5634
|
+
[65279, "ZERO WIDTH NO-BREAK SPACE"]
|
|
5635
|
+
]);
|
|
5636
|
+
function makeSafeText(value) {
|
|
5637
|
+
let result = "";
|
|
5638
|
+
for (const character of value) {
|
|
5639
|
+
const codePoint = character.codePointAt(0);
|
|
5640
|
+
if (character === "\n") {
|
|
5641
|
+
result += character;
|
|
5642
|
+
continue;
|
|
5643
|
+
}
|
|
5644
|
+
const name = formatCharacters.get(codePoint) ?? (/\p{Cf}/u.test(character) ? "FORMAT" : codePoint < 32 || codePoint >= 127 && codePoint <= 159 ? "CONTROL" : void 0);
|
|
5645
|
+
result += name === void 0 ? character : `[U+${codePoint.toString(16).toUpperCase().padStart(4, "0")} ${name}]`;
|
|
5646
|
+
}
|
|
5647
|
+
return result;
|
|
5648
|
+
}
|
|
5649
|
+
function ProposalTextView(props) {
|
|
5650
|
+
return (0, react.createElement)("pre", {
|
|
5651
|
+
"aria-label": props.label,
|
|
5652
|
+
"data-run2skill-text-mode": props.mode,
|
|
5653
|
+
className: _run2skill_css_run2skill_settings_page_module_css_default.textBlock
|
|
5654
|
+
}, props.mode === "SAFE" ? makeSafeText(props.value) : props.value);
|
|
5655
|
+
}
|
|
5656
|
+
function makeExactLineDiff(before, after) {
|
|
5657
|
+
const left = before.split("\n");
|
|
5658
|
+
const right = after.split("\n");
|
|
5659
|
+
if (left.length * right.length > 25e4) return [...left.map((text) => ({
|
|
5660
|
+
kind: "REMOVE",
|
|
5661
|
+
text
|
|
5662
|
+
})), ...right.map((text) => ({
|
|
5663
|
+
kind: "ADD",
|
|
5664
|
+
text
|
|
5665
|
+
}))];
|
|
5666
|
+
const table = Array.from({ length: left.length + 1 }, () => new Uint32Array(right.length + 1));
|
|
5667
|
+
for (let leftIndex = left.length - 1; leftIndex >= 0; leftIndex -= 1) for (let rightIndex = right.length - 1; rightIndex >= 0; rightIndex -= 1) table[leftIndex][rightIndex] = left[leftIndex] === right[rightIndex] ? table[leftIndex + 1][rightIndex + 1] + 1 : Math.max(table[leftIndex + 1][rightIndex], table[leftIndex][rightIndex + 1]);
|
|
5668
|
+
const result = [];
|
|
5669
|
+
let leftIndex = 0;
|
|
5670
|
+
let rightIndex = 0;
|
|
5671
|
+
while (leftIndex < left.length || rightIndex < right.length) if (leftIndex < left.length && rightIndex < right.length && left[leftIndex] === right[rightIndex]) {
|
|
5672
|
+
result.push({
|
|
5673
|
+
kind: "CONTEXT",
|
|
5674
|
+
text: left[leftIndex]
|
|
5675
|
+
});
|
|
5676
|
+
leftIndex += 1;
|
|
5677
|
+
rightIndex += 1;
|
|
5678
|
+
} else if (leftIndex < left.length && (rightIndex >= right.length || table[leftIndex + 1][rightIndex] >= table[leftIndex][rightIndex + 1])) {
|
|
5679
|
+
result.push({
|
|
5680
|
+
kind: "REMOVE",
|
|
5681
|
+
text: left[leftIndex]
|
|
5682
|
+
});
|
|
5683
|
+
leftIndex += 1;
|
|
5684
|
+
} else {
|
|
5685
|
+
result.push({
|
|
5686
|
+
kind: "ADD",
|
|
5687
|
+
text: right[rightIndex]
|
|
5688
|
+
});
|
|
5689
|
+
rightIndex += 1;
|
|
5690
|
+
}
|
|
5691
|
+
return result;
|
|
5692
|
+
}
|
|
5693
|
+
//#endregion
|
|
5694
|
+
//#region src/client/status-copy.ts
|
|
5695
|
+
function describeRun2skillHealth(status) {
|
|
5696
|
+
if (status === "INCOMPATIBLE") return "Run2Skill 当前版本不兼容";
|
|
5697
|
+
if (status === "DEGRADED") return "Run2Skill 当前功能受限";
|
|
5698
|
+
if (status === "RECOVERING") return "Run2Skill 正在恢复历史观察";
|
|
5699
|
+
return "Run2Skill 已就绪";
|
|
5700
|
+
}
|
|
5701
|
+
//#endregion
|
|
5702
|
+
//#region src/client/proposal-inbox-view.ts
|
|
5703
|
+
function proposalInboxContentBlocked(mutationPending, rejectConfirm) {
|
|
5704
|
+
return mutationPending || rejectConfirm;
|
|
5705
|
+
}
|
|
5706
|
+
function useDialogFocus(open, triggerRef, dialogRef, focusTrapSuspended) {
|
|
5707
|
+
(0, react.useEffect)(() => {
|
|
5708
|
+
if (!open) return;
|
|
5709
|
+
if (typeof document === "undefined") return;
|
|
5710
|
+
const dialog = dialogRef.current;
|
|
5711
|
+
const initial = dialog?.querySelector("[data-initial-focus]") ?? dialog?.querySelector(focusableSelector);
|
|
5712
|
+
const recapture = (event) => {
|
|
5713
|
+
if (focusTrapSuspended.current) return;
|
|
5714
|
+
if (dialog !== null && event.target instanceof Node && !dialog.contains(event.target)) initial?.focus();
|
|
5715
|
+
};
|
|
5716
|
+
document.addEventListener("focusin", recapture, true);
|
|
5717
|
+
initial?.focus();
|
|
5718
|
+
return () => {
|
|
5719
|
+
document.removeEventListener("focusin", recapture, true);
|
|
5720
|
+
triggerRef.current?.focus();
|
|
5721
|
+
};
|
|
5722
|
+
}, [
|
|
5723
|
+
dialogRef,
|
|
5724
|
+
focusTrapSuspended,
|
|
5725
|
+
open,
|
|
5726
|
+
triggerRef
|
|
5727
|
+
]);
|
|
5728
|
+
}
|
|
5729
|
+
function countLabel(state) {
|
|
5730
|
+
const queue = state.summary?.queue;
|
|
5731
|
+
if (queue === void 0 || queue.completeness === "UNKNOWN") return "技能草稿待处理数量未知";
|
|
5732
|
+
const total = queue.pendingReview + queue.publishing + queue.needsAttention;
|
|
5733
|
+
const facts = [
|
|
5734
|
+
queue.pendingReview > 0 ? `${String(queue.pendingReview)} 份待审核` : void 0,
|
|
5735
|
+
queue.publishing > 0 ? `${String(queue.publishing)} 份正在保存` : void 0,
|
|
5736
|
+
queue.needsAttention > 0 ? `${String(queue.needsAttention)} 份需要处理` : void 0
|
|
5737
|
+
].filter((fact) => fact !== void 0);
|
|
5738
|
+
return facts.length === 0 ? "当前没有待处理的技能草稿" : `${String(total)} 份技能草稿待处理:${facts.join(",")}`;
|
|
5739
|
+
}
|
|
5740
|
+
function describeProposalSummaryState(state) {
|
|
5741
|
+
if (state.summaryPhase === "LOADING") return "正在加载技能草稿队列";
|
|
5742
|
+
if (state.summaryPhase === "UNAVAILABLE") return "技能草稿队列暂不可用";
|
|
5743
|
+
if (state.summary === void 0) return "技能草稿队列状态未知";
|
|
5744
|
+
const health = describeRun2skillHealth(state.summary.status);
|
|
5745
|
+
const queue = state.summary.queue.completeness === "UNKNOWN" ? "技能草稿待处理数量未知" : countLabel(state);
|
|
5746
|
+
return state.summaryPhase === "STALE" ? `技能草稿队列状态可能已过期:${health};${queue}` : `${health};${queue}`;
|
|
5747
|
+
}
|
|
5748
|
+
function ProposalInboxHeaderAction(props) {
|
|
5749
|
+
const controller = (0, react.useMemo)(() => new ProposalInboxController(props.workspaceId, props.callReview, void 0, props.scopeAccess === void 0 ? {} : { scopeAccess: props.scopeAccess }), [
|
|
5750
|
+
props.scopeAccess,
|
|
5751
|
+
props.callReview,
|
|
5752
|
+
props.workspaceId
|
|
5753
|
+
]);
|
|
5754
|
+
(0, react.useEffect)(() => {
|
|
5755
|
+
controller.start();
|
|
5756
|
+
return () => {
|
|
5757
|
+
controller.dispose();
|
|
5758
|
+
};
|
|
5759
|
+
}, [controller]);
|
|
5760
|
+
const state = (0, react.useSyncExternalStore)(controller.subscribe, controller.snapshot, controller.snapshot);
|
|
5761
|
+
const triggerRef = (0, react.useRef)(null);
|
|
5762
|
+
const dialogRef = (0, react.useRef)(null);
|
|
5763
|
+
const [textMode, setTextMode] = (0, react.useState)("SAFE");
|
|
5764
|
+
const [rejectConfirm, setRejectConfirm] = (0, react.useState)(false);
|
|
5765
|
+
const focusTrapSuspended = (0, react.useRef)(false);
|
|
5766
|
+
focusTrapSuspended.current = rejectConfirm;
|
|
5767
|
+
useDialogFocus(state.open, triggerRef, dialogRef, focusTrapSuspended);
|
|
5768
|
+
const label = countLabel(state);
|
|
5769
|
+
return (0, react.createElement)(react.Fragment, null, (0, react.createElement)("button", {
|
|
5770
|
+
ref: triggerRef,
|
|
5771
|
+
type: "button",
|
|
5772
|
+
"aria-label": label,
|
|
5773
|
+
"aria-haspopup": "dialog",
|
|
5774
|
+
"aria-expanded": state.open,
|
|
5775
|
+
"data-run2skill-proposal-trigger": true,
|
|
5776
|
+
onClick: () => {
|
|
5777
|
+
controller.open();
|
|
5778
|
+
}
|
|
5779
|
+
}, label), state.open ? (0, react.createElement)(ProposalInboxPanel, {
|
|
5780
|
+
controller,
|
|
5781
|
+
state,
|
|
5782
|
+
dialogRef,
|
|
5783
|
+
textMode,
|
|
5784
|
+
setTextMode,
|
|
5785
|
+
rejectConfirm,
|
|
5786
|
+
setRejectConfirm
|
|
5787
|
+
}) : null);
|
|
5788
|
+
}
|
|
5789
|
+
function closeOnKeyboard(event, controller, dialog) {
|
|
5790
|
+
if (event.key === "Escape") {
|
|
5791
|
+
event.preventDefault();
|
|
5792
|
+
controller.close();
|
|
5793
|
+
} else if (dialog !== null) trapDialogTab(event.key, event.shiftKey, () => {
|
|
5794
|
+
event.preventDefault();
|
|
5795
|
+
}, dialog, typeof document === "undefined" ? void 0 : document.activeElement);
|
|
5796
|
+
}
|
|
5797
|
+
function ProposalInboxPanel(props) {
|
|
5798
|
+
const { controller, state } = props;
|
|
5799
|
+
const contentBlocked = proposalInboxContentBlocked(state.mutationPending, props.rejectConfirm);
|
|
5800
|
+
return (0, react.createElement)(react.Fragment, null, (0, react.createElement)("div", {
|
|
5801
|
+
"aria-hidden": true,
|
|
5802
|
+
"data-run2skill-proposal-backdrop": true,
|
|
5803
|
+
className: _run2skill_css_run2skill_settings_page_module_css_default.backdrop
|
|
5804
|
+
}), (0, react.createElement)("div", {
|
|
5805
|
+
ref: props.dialogRef,
|
|
5806
|
+
role: "dialog",
|
|
5807
|
+
"aria-modal": true,
|
|
5808
|
+
"aria-labelledby": "run2skill-proposal-inbox-title",
|
|
5809
|
+
onKeyDown: (event) => {
|
|
5810
|
+
closeOnKeyboard(event, controller, props.dialogRef.current);
|
|
5811
|
+
},
|
|
5812
|
+
className: _run2skill_css_run2skill_settings_page_module_css_default.legacyDialog
|
|
5813
|
+
}, (0, react.createElement)("div", {
|
|
5814
|
+
"aria-hidden": props.rejectConfirm || void 0,
|
|
5815
|
+
inert: props.rejectConfirm ? "" : void 0,
|
|
5816
|
+
className: props.rejectConfirm ? _run2skill_css_run2skill_settings_page_module_css_default.blocked : void 0
|
|
5817
|
+
}, (0, react.createElement)("section", {
|
|
5818
|
+
"aria-label": "技能草稿待处理队列",
|
|
5819
|
+
className: _run2skill_css_run2skill_settings_page_module_css_default.queue
|
|
5820
|
+
}, (0, react.createElement)("div", { className: _run2skill_css_run2skill_settings_page_module_css_default.legacyHeader }, (0, react.createElement)("h2", { id: "run2skill-proposal-inbox-title" }, "技能草稿"), (0, react.createElement)("button", {
|
|
5821
|
+
type: "button",
|
|
5822
|
+
"data-initial-focus": true,
|
|
5823
|
+
onClick: () => {
|
|
5824
|
+
controller.close();
|
|
5825
|
+
}
|
|
5826
|
+
}, "关闭")), (0, react.createElement)("p", { role: "status" }, countLabel(state)), (0, react.createElement)("p", {
|
|
5827
|
+
role: "status",
|
|
5828
|
+
"aria-live": "polite"
|
|
5829
|
+
}, describeProposalSummaryState(state)), state.listPhase === "LOADING" ? (0, react.createElement)("p", null, "正在加载待处理队列…") : null, state.listPhase === "ERROR" ? (0, react.createElement)("p", { role: "alert" }, "待处理队列暂不可用") : null, state.listPhase === "READY" && state.items.length === 0 ? (0, react.createElement)("p", null, "当前没有待处理的技能草稿") : null, (0, react.createElement)("ul", { className: _run2skill_css_run2skill_settings_page_module_css_default.legacyList }, ...state.items.map((item) => (0, react.createElement)("li", { key: item.proposalRef.proposalId }, (0, react.createElement)("button", {
|
|
5830
|
+
type: "button",
|
|
5831
|
+
disabled: contentBlocked || state.detailPhase === "LOADING",
|
|
5832
|
+
"aria-current": state.selectedProposalId === item.proposalRef.proposalId ? "true" : void 0,
|
|
5833
|
+
onClick: () => {
|
|
5834
|
+
controller.select(item.proposalRef.proposalId);
|
|
5835
|
+
},
|
|
5836
|
+
className: _run2skill_css_run2skill_settings_page_module_css_default.proposalListButton
|
|
5837
|
+
}, `${item.kind} · ${makeSafeText(item.name)} · ${item.persistenceScope} · ${describeProposalListItem(item)}`))))), (0, react.createElement)("section", {
|
|
5838
|
+
"aria-label": "技能草稿详情",
|
|
5839
|
+
className: _run2skill_css_run2skill_settings_page_module_css_default.legacyDetail
|
|
5840
|
+
}, state.detailPhase === "IDLE" ? (0, react.createElement)("p", null, "选择一份技能草稿查看完整内容") : null, state.detailPhase === "LOADING" ? (0, react.createElement)("p", null, "正在加载技能草稿详情…") : null, state.detailPhase === "ERROR" ? (0, react.createElement)("p", { role: "alert" }, "技能草稿详情暂不可用") : null, state.detail === void 0 ? null : (0, react.createElement)(ProposalDetailView, {
|
|
5841
|
+
detail: state.detail,
|
|
5842
|
+
textMode: props.textMode,
|
|
5843
|
+
setTextMode: props.setTextMode,
|
|
5844
|
+
mutationPending: contentBlocked,
|
|
5845
|
+
onApprove: () => {
|
|
5846
|
+
controller.mutate("APPROVE");
|
|
5847
|
+
},
|
|
5848
|
+
onReject: () => {
|
|
5849
|
+
props.setRejectConfirm(true);
|
|
5850
|
+
},
|
|
5851
|
+
onRetry: () => {
|
|
5852
|
+
controller.mutate("RETRY");
|
|
5853
|
+
},
|
|
5854
|
+
onConfirmDiscard: () => {
|
|
5855
|
+
controller.mutate("CONFIRM_DISCARD");
|
|
5856
|
+
}
|
|
5857
|
+
}), (0, react.createElement)("div", {
|
|
5858
|
+
"aria-live": "polite",
|
|
5859
|
+
"aria-atomic": true
|
|
5860
|
+
}, state.announcement))), props.rejectConfirm ? (0, react.createElement)(RejectConfirmation, {
|
|
5861
|
+
disabled: state.mutationPending,
|
|
5862
|
+
onCancel: () => {
|
|
5863
|
+
props.setRejectConfirm(false);
|
|
5864
|
+
},
|
|
5865
|
+
onConfirm: () => {
|
|
5866
|
+
props.setRejectConfirm(false);
|
|
5867
|
+
controller.mutate("REJECT");
|
|
5868
|
+
}
|
|
5869
|
+
}) : null));
|
|
5908
5870
|
}
|
|
5909
|
-
function
|
|
5871
|
+
function factsFromAction(detail) {
|
|
5872
|
+
const action = detail.proposal.actionBinding;
|
|
5873
|
+
if (action.kind === "CREATE") return [
|
|
5874
|
+
`Skill 存储规则版本:${action.rootBinding.rootContractVersion}`,
|
|
5875
|
+
`Skill 存储定位器版本:${action.rootBinding.resolverVersion}`,
|
|
5876
|
+
`预期存储方式 / 来源:${action.rootBinding.expectedProvider} / ${action.rootBinding.expectedSource}`,
|
|
5877
|
+
`存储定位规则校验值:${action.rootBinding.resolutionContractDigest}`,
|
|
5878
|
+
`存储目录状态:${action.rootBinding.state}`,
|
|
5879
|
+
`Skill 名称:${action.targetBinding.skillName}`,
|
|
5880
|
+
`Skill 内容校验值:${detail.proposal.skillBytesDigest}`,
|
|
5881
|
+
`确认目标不存在的时间:${action.expectedAbsence.observedAt}`,
|
|
5882
|
+
"目标文件夹不存在:是",
|
|
5883
|
+
"目标 SKILL.md 不存在:是",
|
|
5884
|
+
"同名扁平 Skill 文件不存在:是"
|
|
5885
|
+
].join("\n");
|
|
5886
|
+
if (action.kind === "MERGE") return [
|
|
5887
|
+
`Skill 存储规则版本:${action.rootBinding.rootContractVersion}`,
|
|
5888
|
+
`Skill 存储定位器版本:${action.rootBinding.resolverVersion}`,
|
|
5889
|
+
`预期存储方式 / 来源:${action.rootBinding.expectedProvider} / ${action.rootBinding.expectedSource}`,
|
|
5890
|
+
`存储定位规则校验值:${action.rootBinding.resolutionContractDigest}`,
|
|
5891
|
+
`存储目录状态:${action.rootBinding.state}`,
|
|
5892
|
+
`现有 Skill 标识:${action.baseBinding.candidateKey}`,
|
|
5893
|
+
`Skill 名称:${action.targetBinding.skillName}`,
|
|
5894
|
+
`原内容校验值:${action.baseBinding.bytesDigest}`,
|
|
5895
|
+
`原内容确认时间:${action.baseBinding.observedAt}`
|
|
5896
|
+
].join("\n");
|
|
5897
|
+
return [
|
|
5898
|
+
`匹配到的现有 Skill 标识:${action.coveringCandidateBinding.candidateKey}`,
|
|
5899
|
+
`名称:${action.coveringCandidateBinding.name}`,
|
|
5900
|
+
`来源:${action.coveringCandidateBinding.source}`,
|
|
5901
|
+
`内容校验值:${action.coveringCandidateBinding.contentDigest}`,
|
|
5902
|
+
`确认时间:${action.coveringCandidateBinding.observedAt}`
|
|
5903
|
+
].join("\n");
|
|
5904
|
+
}
|
|
5905
|
+
function proposalDetailAction(detail, mutationPending) {
|
|
5906
|
+
if (mutationPending) return "NONE";
|
|
5907
|
+
if (detail.reviewDecision === "PENDING" && detail.processingState === "READY_FOR_REVIEW" && detail.publicationOutcome === "PENDING_REVIEW") return "REVIEW";
|
|
5908
|
+
if (detail.reviewDecision === "APPROVED" && detail.processingState === "NEEDS_ATTENTION" && detail.publicationOutcome === "PUBLISH_FAILED") return "RETRY_PUBLICATION";
|
|
5909
|
+
return "NONE";
|
|
5910
|
+
}
|
|
5911
|
+
function ProposalDetailView(props) {
|
|
5912
|
+
const { detail, mutationPending } = props;
|
|
5913
|
+
const proposal = detail.proposal;
|
|
5914
|
+
const action = proposalDetailAction(detail, mutationPending);
|
|
5915
|
+
const actionable = action === "REVIEW";
|
|
5916
|
+
const coordinate = detail.sessionCoordinate;
|
|
5917
|
+
const baseBytes = proposal.actionBinding.kind === "MERGE" ? proposal.actionBinding.baseBinding.exactBytes : void 0;
|
|
5918
|
+
const coveringBytes = proposal.actionBinding.kind === "DISCARD" ? proposal.actionBinding.coveringCandidateBinding.content : void 0;
|
|
5919
|
+
const diff = baseBytes === void 0 ? [] : makeExactLineDiff(baseBytes, proposal.exactSkillBytes);
|
|
5920
|
+
return (0, react.createElement)(react.Fragment, null, (0, react.createElement)("h3", null, `${proposal.kind}: ${makeSafeText(proposal.name)}`), (0, react.createElement)("dl", null, (0, react.createElement)("dt", null, "说明"), (0, react.createElement)("dd", null, makeSafeText(proposal.description)), (0, react.createElement)("dt", null, "何时使用"), (0, react.createElement)("dd", null, makeSafeText(proposal.whenToUse)), (0, react.createElement)("dt", null, "保存范围"), (0, react.createElement)("dd", null, proposal.persistenceScope === "PROJECT" ? "当前项目(PROJECT)" : "当前用户(USER)"), (0, react.createElement)("dt", null, "草稿版本"), (0, react.createElement)("dd", null, String(proposal.revision)), (0, react.createElement)("dt", null, "草稿校验值"), (0, react.createElement)("dd", null, proposal.digest), (0, react.createElement)("dt", null, "会话位置"), (0, react.createElement)("dd", null, `${coordinate.rootSessionId} / 回合 ${String(coordinate.turn)} / 消息序号 ${String(coordinate.turnEndSeq)}`), proposal.workspaceBinding === void 0 ? null : (0, react.createElement)(react.Fragment, null, (0, react.createElement)("dt", null, "项目工作区"), (0, react.createElement)("dd", null, `当前项目(PROJECT)· ${proposal.workspaceBinding.workspaceId}`)), proposal.dshHomeBinding === void 0 ? null : (0, react.createElement)(react.Fragment, null, (0, react.createElement)("dt", null, "DSH 用户目录"), (0, react.createElement)("dd", null, `当前用户(USER)· ${proposal.dshHomeBinding.resolutionKind}`), (0, react.createElement)("dt", null, "DSH 用户目录校验值"), (0, react.createElement)("dd", null, proposal.dshHomeBinding.identityDigest)), (0, react.createElement)("dt", null, "审核决定"), (0, react.createElement)("dd", null, describeReviewDecision(detail.reviewDecision)), (0, react.createElement)("dt", null, "处理状态"), (0, react.createElement)("dd", null, describeProcessingState(detail.processingState)), (0, react.createElement)("dt", null, "保存结果"), (0, react.createElement)("dd", null, describePublicationOutcome(detail.publicationOutcome))), (0, react.createElement)("h4", null, "为什么生成这份草稿"), (0, react.createElement)("p", null, makeSafeText(proposal.curationRationale)), ...detail.experiences.map((experience) => (0, react.createElement)("article", { key: experience.experienceId }, (0, react.createElement)("strong", null, `${experience.type} · ${experience.evidenceStrength}`), (0, react.createElement)("p", null, makeSafeText(experience.lesson)))), (0, react.createElement)("h4", null, "相关对话片段(已过滤)"), ...detail.evidenceRefs.map((evidence) => (0, react.createElement)("article", { key: `${String(evidence.messageSeq)}:${evidence.excerptDigest}` }, (0, react.createElement)("strong", null, `消息序号 ${String(evidence.messageSeq)}${evidence.truncated ? " · 已截断" : ""}`), (0, react.createElement)(ProposalTextView, {
|
|
5921
|
+
value: evidence.excerpt,
|
|
5922
|
+
mode: "SAFE",
|
|
5923
|
+
label: `相关对话片段 ${String(evidence.messageSeq)}`
|
|
5924
|
+
}))), (0, react.createElement)("h4", null, "保存前核对信息"), (0, react.createElement)(ProposalTextView, {
|
|
5925
|
+
value: factsFromAction(detail),
|
|
5926
|
+
mode: "SAFE",
|
|
5927
|
+
label: "保存前核对信息"
|
|
5928
|
+
}), (0, react.createElement)("div", {
|
|
5929
|
+
role: "group",
|
|
5930
|
+
"aria-label": "内容显示方式",
|
|
5931
|
+
className: _run2skill_css_run2skill_settings_page_module_css_default.modeGroup
|
|
5932
|
+
}, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Pill, {
|
|
5933
|
+
"aria-pressed": props.textMode === "SAFE",
|
|
5934
|
+
active: props.textMode === "SAFE",
|
|
5935
|
+
onClick: () => {
|
|
5936
|
+
props.setTextMode("SAFE");
|
|
5937
|
+
}
|
|
5938
|
+
}, "安全视图"), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Pill, {
|
|
5939
|
+
"aria-pressed": props.textMode === "RAW",
|
|
5940
|
+
active: props.textMode === "RAW",
|
|
5941
|
+
onClick: () => {
|
|
5942
|
+
props.setTextMode("RAW");
|
|
5943
|
+
}
|
|
5944
|
+
}, "原始内容")), (0, react.createElement)("h4", null, "将要保存的完整 SKILL.md"), (0, react.createElement)(ProposalTextView, {
|
|
5945
|
+
value: proposal.exactSkillBytes,
|
|
5946
|
+
mode: props.textMode,
|
|
5947
|
+
label: "将要保存的完整 SKILL.md"
|
|
5948
|
+
}), baseBytes === void 0 ? null : (0, react.createElement)(react.Fragment, null, (0, react.createElement)("h4", null, "合并前的 Skill 内容"), (0, react.createElement)(ProposalTextView, {
|
|
5949
|
+
value: baseBytes,
|
|
5950
|
+
mode: props.textMode,
|
|
5951
|
+
label: "合并前的 Skill 内容"
|
|
5952
|
+
}), (0, react.createElement)("h4", null, "内容差异(精确对比)"), (0, react.createElement)("pre", { "aria-label": "内容差异(精确对比)" }, diff.map((line) => `${line.kind === "ADD" ? "+" : line.kind === "REMOVE" ? "-" : " "} ${makeSafeText(line.text)}`).join("\n"))), coveringBytes === void 0 ? null : (0, react.createElement)(react.Fragment, null, (0, react.createElement)("h4", null, "覆盖已有 Skill 的完整内容"), (0, react.createElement)(ProposalTextView, {
|
|
5953
|
+
value: coveringBytes,
|
|
5954
|
+
mode: props.textMode,
|
|
5955
|
+
label: "覆盖已有 Skill 的完整内容"
|
|
5956
|
+
})), (0, react.createElement)("div", {
|
|
5957
|
+
role: "group",
|
|
5958
|
+
"aria-label": "技能草稿操作",
|
|
5959
|
+
className: _run2skill_css_run2skill_settings_page_module_css_default.actions
|
|
5960
|
+
}, action === "RETRY_PUBLICATION" ? (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5961
|
+
variant: "primary",
|
|
5962
|
+
disabled: mutationPending,
|
|
5963
|
+
onClick: props.onRetry
|
|
5964
|
+
}, "重试保存") : proposal.kind === "DISCARD" ? (0, react.createElement)(react.Fragment, null, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5965
|
+
variant: "primary",
|
|
5966
|
+
disabled: !actionable,
|
|
5967
|
+
onClick: props.onConfirmDiscard
|
|
5968
|
+
}, "确认无需新建 Skill"), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5969
|
+
variant: "outline",
|
|
5970
|
+
disabled: !actionable,
|
|
5971
|
+
onClick: props.onRetry
|
|
5972
|
+
}, "不同意,重新分析一次")) : (0, react.createElement)(react.Fragment, null, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5973
|
+
variant: "primary",
|
|
5974
|
+
disabled: !actionable,
|
|
5975
|
+
onClick: props.onApprove
|
|
5976
|
+
}, detail.processingState === "PUBLISHING" ? "正在保存…" : "确认并保存"), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
5977
|
+
variant: "outline",
|
|
5978
|
+
disabled: !actionable,
|
|
5979
|
+
onClick: (event) => {
|
|
5980
|
+
props.onReject(event.currentTarget);
|
|
5981
|
+
}
|
|
5982
|
+
}, "放弃草稿"))));
|
|
5983
|
+
}
|
|
5984
|
+
function RejectConfirmationBody(props) {
|
|
5985
|
+
return (0, react.createElement)(react.Fragment, null, (0, react.createElement)("h3", { id: "run2skill-reject-title" }, "确认放弃这份技能草稿?"), (0, react.createElement)("p", { id: "run2skill-reject-description" }, "现有 Skill 不会改变;这份技能草稿将离开待处理队列;经过筛选的学习材料仍按项目规则保留。"), (0, react.createElement)("button", {
|
|
5986
|
+
type: "button",
|
|
5987
|
+
"data-initial-focus": true,
|
|
5988
|
+
disabled: props.disabled,
|
|
5989
|
+
onClick: props.onCancel
|
|
5990
|
+
}, "取消"), (0, react.createElement)("button", {
|
|
5991
|
+
type: "button",
|
|
5992
|
+
disabled: props.disabled,
|
|
5993
|
+
onClick: props.onConfirm
|
|
5994
|
+
}, "确认放弃"));
|
|
5995
|
+
}
|
|
5996
|
+
function RejectConfirmation(props) {
|
|
5910
5997
|
const dialogRef = (0, react.useRef)(null);
|
|
5911
|
-
const preview = props.state.preview;
|
|
5912
|
-
const scope = props.state.previewScope;
|
|
5913
5998
|
(0, react.useEffect)(() => {
|
|
5914
|
-
if (
|
|
5999
|
+
if (typeof document === "undefined") return;
|
|
6000
|
+
const previous = document.activeElement;
|
|
5915
6001
|
const dialog = dialogRef.current;
|
|
5916
6002
|
const initial = dialog?.querySelector("[data-initial-focus]");
|
|
5917
6003
|
const recapture = (event) => {
|
|
@@ -5921,205 +6007,966 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5921
6007
|
initial?.focus();
|
|
5922
6008
|
return () => {
|
|
5923
6009
|
document.removeEventListener("focusin", recapture, true);
|
|
5924
|
-
|
|
6010
|
+
previous?.focus();
|
|
5925
6011
|
};
|
|
5926
|
-
}, [
|
|
5927
|
-
|
|
5928
|
-
const titleId = "run2skill-purge-confirm-title";
|
|
5929
|
-
const descriptionId = "run2skill-purge-confirm-description";
|
|
5930
|
-
return (0, react.createElement)(react.Fragment, null, (0, react.createElement)("div", {
|
|
5931
|
-
"aria-hidden": true,
|
|
5932
|
-
style: {
|
|
5933
|
-
position: "fixed",
|
|
5934
|
-
inset: 0,
|
|
5935
|
-
zIndex: 1999,
|
|
5936
|
-
background: "rgb(0 0 0 / 35%)"
|
|
5937
|
-
}
|
|
5938
|
-
}), (0, react.createElement)("div", {
|
|
6012
|
+
}, []);
|
|
6013
|
+
return (0, react.createElement)("div", {
|
|
5939
6014
|
ref: dialogRef,
|
|
5940
6015
|
role: "alertdialog",
|
|
5941
6016
|
"aria-modal": true,
|
|
5942
|
-
"aria-labelledby":
|
|
5943
|
-
"aria-describedby":
|
|
6017
|
+
"aria-labelledby": "run2skill-reject-title",
|
|
6018
|
+
"aria-describedby": "run2skill-reject-description",
|
|
5944
6019
|
onKeyDown: (event) => {
|
|
6020
|
+
event.stopPropagation();
|
|
5945
6021
|
if (event.key === "Escape") {
|
|
5946
6022
|
event.preventDefault();
|
|
5947
|
-
|
|
5948
|
-
props.controller.cancelPreview();
|
|
6023
|
+
props.onCancel();
|
|
5949
6024
|
} else if (dialogRef.current !== null) trapDialogTab(event.key, event.shiftKey, () => {
|
|
5950
6025
|
event.preventDefault();
|
|
5951
6026
|
}, dialogRef.current, typeof document === "undefined" ? void 0 : document.activeElement);
|
|
5952
6027
|
},
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
6028
|
+
className: _run2skill_css_run2skill_settings_page_module_css_default.rejectOverlay
|
|
6029
|
+
}, (0, react.createElement)("div", { className: _run2skill_css_run2skill_settings_page_module_css_default.rejectCard }, (0, react.createElement)(RejectConfirmationBody, props)));
|
|
6030
|
+
}
|
|
6031
|
+
//#endregion
|
|
6032
|
+
//#region src/client/run2skill-settings-page.ts
|
|
6033
|
+
const recentSkillActivityResultSchema = object({
|
|
6034
|
+
ok: literal(true),
|
|
6035
|
+
value: object({
|
|
6036
|
+
apiVersion: literal(1),
|
|
6037
|
+
visibilityRevision: string().regex(/^visibility_[a-f0-9]{64}$/),
|
|
6038
|
+
items: array(object({
|
|
6039
|
+
activityId: string().regex(/^activity_[a-f0-9]{64}$/),
|
|
6040
|
+
skillName: string().min(1).max(128),
|
|
6041
|
+
operation: _enum(["CREATED", "UPDATED"]),
|
|
6042
|
+
scope: _enum(["PROJECT", "USER"]),
|
|
6043
|
+
occurredAt: string().datetime({ offset: true })
|
|
6044
|
+
}).strict()).max(256)
|
|
6045
|
+
}).strict()
|
|
6046
|
+
}).strict();
|
|
6047
|
+
const recentSkillActivityVisibilityStaleSchema = object({
|
|
6048
|
+
ok: literal(false),
|
|
6049
|
+
error: object({ code: literal("visibility-stale") }).passthrough()
|
|
6050
|
+
}).passthrough();
|
|
6051
|
+
const RECENT_SKILL_ACTIVITY_VISIBILITY_POLL_MS = 2e3;
|
|
6052
|
+
function waitForActivityVisibilityPoll(signal, delayMs) {
|
|
6053
|
+
if (signal.aborted) return Promise.resolve();
|
|
6054
|
+
return new Promise((resolve) => {
|
|
6055
|
+
const finish = () => {
|
|
6056
|
+
globalThis.clearTimeout(timer);
|
|
6057
|
+
signal.removeEventListener("abort", finish);
|
|
6058
|
+
resolve();
|
|
6059
|
+
};
|
|
6060
|
+
const timer = globalThis.setTimeout(finish, delayMs);
|
|
6061
|
+
signal.addEventListener("abort", finish, { once: true });
|
|
6062
|
+
});
|
|
6063
|
+
}
|
|
6064
|
+
function formatActivityTime(value) {
|
|
6065
|
+
return new Intl.DateTimeFormat("zh-CN", {
|
|
6066
|
+
dateStyle: "medium",
|
|
6067
|
+
timeStyle: "short"
|
|
6068
|
+
}).format(new Date(value));
|
|
6069
|
+
}
|
|
6070
|
+
function usefulActivityScope(items, selected) {
|
|
6071
|
+
return items.some((item) => item.skillName === selected.skillName && item.scope !== selected.scope);
|
|
6072
|
+
}
|
|
6073
|
+
function RecentSkillActivitySection(props) {
|
|
6074
|
+
const scopeIdentity = JSON.stringify([
|
|
6075
|
+
props.workspaceId ?? null,
|
|
6076
|
+
props.scopeGeneration,
|
|
6077
|
+
props.hostDataEpoch ?? 0
|
|
6078
|
+
]);
|
|
6079
|
+
const [state, setState] = (0, react.useState)({
|
|
6080
|
+
phase: "IDLE",
|
|
6081
|
+
items: []
|
|
6082
|
+
});
|
|
6083
|
+
const visibleState = state.scopeIdentity === scopeIdentity ? state : {
|
|
6084
|
+
phase: "IDLE",
|
|
6085
|
+
items: []
|
|
6086
|
+
};
|
|
6087
|
+
(0, react.useEffect)(() => {
|
|
6088
|
+
if (!props.active) return;
|
|
6089
|
+
const abort = new AbortController();
|
|
6090
|
+
setState({
|
|
6091
|
+
phase: "LOADING",
|
|
6092
|
+
items: [],
|
|
6093
|
+
scopeIdentity
|
|
6094
|
+
});
|
|
6095
|
+
(async () => {
|
|
6096
|
+
const readStable = async () => {
|
|
6097
|
+
let expectedVisibilityRevision;
|
|
6098
|
+
let confirmedReads = 0;
|
|
6099
|
+
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
6100
|
+
const result = await props.call({
|
|
6101
|
+
apiVersion: 1,
|
|
6102
|
+
currentScope: currentScope(props.workspaceId, props.scopeGeneration),
|
|
6103
|
+
...expectedVisibilityRevision === void 0 ? {} : { expectedVisibilityRevision }
|
|
6104
|
+
}, abort.signal);
|
|
6105
|
+
if (abort.signal.aborted) return void 0;
|
|
6106
|
+
if (recentSkillActivityVisibilityStaleSchema.safeParse(result).success) {
|
|
6107
|
+
expectedVisibilityRevision = void 0;
|
|
6108
|
+
confirmedReads = 0;
|
|
6109
|
+
continue;
|
|
6110
|
+
}
|
|
6111
|
+
const parsed = recentSkillActivityResultSchema.safeParse(result);
|
|
6112
|
+
if (!parsed.success) throw new Error("RECENT_ACTIVITY_UNAVAILABLE");
|
|
6113
|
+
if (parsed.data.value.visibilityRevision === expectedVisibilityRevision) {
|
|
6114
|
+
confirmedReads += 1;
|
|
6115
|
+
if (confirmedReads >= 2) return parsed.data.value;
|
|
6116
|
+
} else {
|
|
6117
|
+
expectedVisibilityRevision = parsed.data.value.visibilityRevision;
|
|
6118
|
+
confirmedReads = 0;
|
|
6119
|
+
}
|
|
6120
|
+
}
|
|
6121
|
+
throw new Error("RECENT_ACTIVITY_VISIBILITY_UNSTABLE");
|
|
6122
|
+
};
|
|
6123
|
+
let current = await readStable();
|
|
6124
|
+
if (current === void 0 || abort.signal.aborted) return;
|
|
6125
|
+
setState({
|
|
6126
|
+
phase: "READY",
|
|
6127
|
+
items: current.items,
|
|
6128
|
+
scopeIdentity
|
|
6129
|
+
});
|
|
6130
|
+
while (!abort.signal.aborted) {
|
|
6131
|
+
await waitForActivityVisibilityPoll(abort.signal, props.visibilityPollMs ?? RECENT_SKILL_ACTIVITY_VISIBILITY_POLL_MS);
|
|
6132
|
+
if (abort.signal.aborted) return;
|
|
6133
|
+
const result = await props.call({
|
|
6134
|
+
apiVersion: 1,
|
|
6135
|
+
currentScope: currentScope(props.workspaceId, props.scopeGeneration),
|
|
6136
|
+
expectedVisibilityRevision: current.visibilityRevision
|
|
6137
|
+
}, abort.signal);
|
|
6138
|
+
if (abort.signal.aborted) return;
|
|
6139
|
+
const parsed = recentSkillActivityResultSchema.safeParse(result);
|
|
6140
|
+
if (recentSkillActivityVisibilityStaleSchema.safeParse(result).success || !parsed.success || parsed.data.value.visibilityRevision !== current.visibilityRevision) {
|
|
6141
|
+
setState({
|
|
6142
|
+
phase: "LOADING",
|
|
6143
|
+
items: [],
|
|
6144
|
+
scopeIdentity
|
|
6145
|
+
});
|
|
6146
|
+
current = await readStable();
|
|
6147
|
+
if (current === void 0 || abort.signal.aborted) return;
|
|
6148
|
+
} else current = parsed.data.value;
|
|
6149
|
+
setState({
|
|
6150
|
+
phase: "READY",
|
|
6151
|
+
items: current.items,
|
|
6152
|
+
scopeIdentity
|
|
6153
|
+
});
|
|
6154
|
+
}
|
|
6155
|
+
})().catch(() => {
|
|
6156
|
+
if (!abort.signal.aborted) setState({
|
|
6157
|
+
phase: "ERROR",
|
|
6158
|
+
items: [],
|
|
6159
|
+
scopeIdentity
|
|
6160
|
+
});
|
|
6161
|
+
});
|
|
6162
|
+
return () => {
|
|
6163
|
+
abort.abort();
|
|
6164
|
+
};
|
|
6165
|
+
}, [
|
|
6166
|
+
props.active,
|
|
6167
|
+
props.call,
|
|
6168
|
+
props.hostDataEpoch,
|
|
6169
|
+
props.scopeGeneration,
|
|
6170
|
+
props.visibilityPollMs,
|
|
6171
|
+
props.workspaceId,
|
|
6172
|
+
scopeIdentity
|
|
6173
|
+
]);
|
|
6174
|
+
return (0, react.createElement)("div", { className: _run2skill_css_run2skill_settings_page_module_css_default.sectionBody }, (0, react.createElement)("p", { className: _run2skill_css_run2skill_settings_page_module_css_default.muted }, "展示最近沉淀的 Skill"), visibleState.phase === "LOADING" ? (0, react.createElement)("p", { role: "status" }, "正在读取最近活动…") : null, visibleState.phase === "ERROR" ? (0, react.createElement)("p", { role: "alert" }, "最近活动暂不可用。") : null, visibleState.phase === "READY" && visibleState.items.length === 0 ? (0, react.createElement)("p", null, "最近 7 天没有成功沉淀的 Skill。") : null, visibleState.items.length === 0 ? null : (0, react.createElement)("ul", { className: _run2skill_css_run2skill_settings_page_module_css_default.activityList }, ...visibleState.items.map((item) => (0, react.createElement)("li", {
|
|
6175
|
+
className: _run2skill_css_run2skill_settings_page_module_css_default.activityItem,
|
|
6176
|
+
key: item.activityId
|
|
6177
|
+
}, (0, react.createElement)("div", { className: _run2skill_css_run2skill_settings_page_module_css_default.activitySummary }, (0, react.createElement)("strong", null, item.skillName), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Pill, null, item.operation === "CREATED" ? "创建" : "更新"), usefulActivityScope(visibleState.items, item) ? (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Pill, null, item.scope === "PROJECT" ? "项目" : "用户") : null), (0, react.createElement)("time", {
|
|
6178
|
+
dateTime: item.occurredAt,
|
|
6179
|
+
className: _run2skill_css_run2skill_settings_page_module_css_default.muted
|
|
6180
|
+
}, formatActivityTime(item.occurredAt))))));
|
|
6181
|
+
}
|
|
6182
|
+
const learningIssueSchema = object({
|
|
6183
|
+
workItemId: string().regex(/^wi_[a-f0-9]{64}$/),
|
|
6184
|
+
workItemRevision: number().int().safe().positive(),
|
|
6185
|
+
createdAt: string().datetime({ offset: true }),
|
|
6186
|
+
updatedAt: string().datetime({ offset: true }),
|
|
6187
|
+
failureCode: string().min(1).max(256),
|
|
6188
|
+
failureDetail: string().min(1).max(256).optional(),
|
|
6189
|
+
retryable: boolean(),
|
|
6190
|
+
attempt: number().int().nonnegative().max(3),
|
|
6191
|
+
requestBudgetUsed: number().int().nonnegative().max(2),
|
|
6192
|
+
modelRoute: object({
|
|
6193
|
+
provider: string().min(1).max(256),
|
|
6194
|
+
model: string().min(1).max(256)
|
|
6195
|
+
}).strict().optional(),
|
|
6196
|
+
calls: array(object({
|
|
6197
|
+
requestOrdinal: number().int().positive(),
|
|
6198
|
+
kind: string().min(1).max(256),
|
|
6199
|
+
inputTokens: number().int().nonnegative().optional(),
|
|
6200
|
+
outputTokens: number().int().nonnegative().optional(),
|
|
6201
|
+
outcome: string().min(1).max(256)
|
|
6202
|
+
}).strict()).max(2)
|
|
6203
|
+
}).strict();
|
|
6204
|
+
const learningIssuePageSchema = object({
|
|
6205
|
+
ok: literal(true),
|
|
6206
|
+
value: object({
|
|
6207
|
+
apiVersion: literal(1),
|
|
6208
|
+
items: array(learningIssueSchema).max(20),
|
|
6209
|
+
nextCursor: string().regex(/^c_[1-9][0-9]*_[1-9][0-9]*_[a-f0-9]{64}$/).optional()
|
|
6210
|
+
}).strict()
|
|
6211
|
+
}).strict();
|
|
6212
|
+
function attentionValue(value) {
|
|
6213
|
+
if (value === null || typeof value !== "object" || !("ok" in value) || value.ok !== true || !("value" in value)) return;
|
|
6214
|
+
const projection = value.value;
|
|
6215
|
+
if (projection.apiVersion !== 1 || !Array.isArray(projection.actions) || !Array.isArray(projection.runtimeWarnings)) return void 0;
|
|
6216
|
+
return projection;
|
|
6217
|
+
}
|
|
6218
|
+
const seenAttentionKeys = /* @__PURE__ */ new Set();
|
|
6219
|
+
const ATTENTION_SETTINGS_POLL_INTERVAL_MS = 1e4;
|
|
6220
|
+
function currentScope(workspaceId, generation) {
|
|
6221
|
+
return workspaceId === void 0 ? {
|
|
6222
|
+
kind: "USER_ONLY",
|
|
6223
|
+
generation
|
|
6224
|
+
} : {
|
|
6225
|
+
kind: "WORKSPACE",
|
|
6226
|
+
generation,
|
|
6227
|
+
workspaceId
|
|
6228
|
+
};
|
|
6229
|
+
}
|
|
6230
|
+
function Run2skillAttentionToast(props) {
|
|
6231
|
+
const [toast, setToast] = (0, react.useState)();
|
|
6232
|
+
const scopeGeneration = (0, react.useRef)(0);
|
|
6233
|
+
(0, react.useEffect)(() => {
|
|
6234
|
+
const abort = new AbortController();
|
|
6235
|
+
let timer;
|
|
6236
|
+
let sequence = 0;
|
|
6237
|
+
const generation = ++scopeGeneration.current;
|
|
6238
|
+
const refresh = async () => {
|
|
6239
|
+
try {
|
|
6240
|
+
const projection = attentionValue(await props.callAttention({
|
|
6241
|
+
apiVersion: 1,
|
|
6242
|
+
currentScope: currentScope(props.workspaceId, generation),
|
|
6243
|
+
sessionId: props.sessionId
|
|
6244
|
+
}, abort.signal));
|
|
6245
|
+
if (projection === void 0 || abort.signal.aborted) return;
|
|
6246
|
+
const fresh = [...projection.actions.map((action) => action.actionKey), ...projection.runtimeWarnings.map((warning) => warning.noticeKey)].filter((key) => /^act_[a-f0-9]{64}$|^notice_[a-f0-9]{64}$/.test(key)).filter((key) => !seenAttentionKeys.has(key));
|
|
6247
|
+
if (fresh.length > 0) {
|
|
6248
|
+
for (const key of fresh) seenAttentionKeys.add(key);
|
|
6249
|
+
sequence += 1;
|
|
6250
|
+
setToast({
|
|
6251
|
+
sequence,
|
|
6252
|
+
count: fresh.length
|
|
6253
|
+
});
|
|
6254
|
+
}
|
|
6255
|
+
} catch {} finally {
|
|
6256
|
+
if (!abort.signal.aborted) timer = setTimeout(() => {
|
|
6257
|
+
refresh();
|
|
6258
|
+
}, 1e4);
|
|
6259
|
+
}
|
|
6260
|
+
};
|
|
6261
|
+
refresh();
|
|
6262
|
+
return () => {
|
|
6263
|
+
abort.abort();
|
|
6264
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
6265
|
+
};
|
|
6266
|
+
}, [
|
|
6267
|
+
props.callAttention,
|
|
6268
|
+
props.sessionId,
|
|
6269
|
+
props.workspaceId
|
|
6270
|
+
]);
|
|
6271
|
+
if (toast === void 0) return null;
|
|
6272
|
+
return (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Toast, {
|
|
6273
|
+
key: toast.sequence,
|
|
6274
|
+
icon: (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16),
|
|
6275
|
+
text: `Run2Skill 有 ${String(toast.count)} 项需要处理,请前往设置 → 插件 → Run2Skill`,
|
|
6276
|
+
onDone: () => {
|
|
6277
|
+
setToast(void 0);
|
|
5963
6278
|
}
|
|
5964
|
-
}
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
6279
|
+
});
|
|
6280
|
+
}
|
|
6281
|
+
function AutomaticLearningSection(props) {
|
|
6282
|
+
const state = (0, react.useSyncExternalStore)(props.controller.subscribe, props.controller.getSnapshot, props.controller.getSnapshot);
|
|
6283
|
+
const disabled = state.status !== "ready" || !state.writable || state.saving;
|
|
6284
|
+
return (0, react.createElement)("div", { className: _run2skill_css_run2skill_settings_page_module_css_default.sectionBody }, (0, react.createElement)("p", { className: _run2skill_css_run2skill_settings_page_module_css_default.muted }, "在后台识别可复用经验并生成待确认的技能草稿;关闭后,明确说“保存为 Skill”仍然有效。"), (0, react.createElement)("div", { className: _run2skill_css_run2skill_settings_page_module_css_default.actions }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
6285
|
+
variant: state.automaticLearning ? "primary" : "outline",
|
|
6286
|
+
"aria-pressed": state.automaticLearning === true,
|
|
6287
|
+
disabled,
|
|
5968
6288
|
onClick: () => {
|
|
5969
|
-
props.controller.
|
|
6289
|
+
props.controller.setAutomaticLearning(state.automaticLearning !== true);
|
|
5970
6290
|
}
|
|
5971
|
-
}, "
|
|
5972
|
-
|
|
5973
|
-
|
|
6291
|
+
}, state.saving ? "正在保存…" : state.automaticLearning ? "自动学习已开启" : "自动学习已关闭"), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Pill, null, "沿用当前会话模型")), state.error === void 0 ? null : (0, react.createElement)("p", {
|
|
6292
|
+
role: "status",
|
|
6293
|
+
className: _run2skill_css_run2skill_settings_page_module_css_default.danger
|
|
6294
|
+
}, "设置已变化,请刷新后重试。"));
|
|
6295
|
+
}
|
|
6296
|
+
function ProposalSettingsSection(props) {
|
|
6297
|
+
const scopeAccessRef = (0, react.useRef)({
|
|
6298
|
+
currentScope: currentScope(props.workspaceId, props.scopeGeneration),
|
|
6299
|
+
actions: []
|
|
6300
|
+
});
|
|
6301
|
+
scopeAccessRef.current = {
|
|
6302
|
+
currentScope: currentScope(props.workspaceId, props.scopeGeneration),
|
|
6303
|
+
actions: props.actions.flatMap((action) => action.subjectId === void 0 || action.proposalRef === void 0 || !["REVIEW_PROPOSAL", "RETRY_PUBLICATION"].includes(action.kind ?? "") ? [] : [{
|
|
6304
|
+
actionKey: action.actionKey,
|
|
6305
|
+
subjectId: action.subjectId,
|
|
6306
|
+
kind: action.kind,
|
|
6307
|
+
proposalRef: action.proposalRef
|
|
6308
|
+
}])
|
|
6309
|
+
};
|
|
6310
|
+
const controller = (0, react.useMemo)(() => new ProposalInboxController(props.workspaceId ?? "__run2skill_user_only__", props.callReview, void 0, {
|
|
6311
|
+
attentionDriven: true,
|
|
6312
|
+
scopeAccess: () => scopeAccessRef.current
|
|
6313
|
+
}), [
|
|
6314
|
+
props.callReview,
|
|
6315
|
+
props.scopeGeneration,
|
|
6316
|
+
props.workspaceId
|
|
6317
|
+
]);
|
|
6318
|
+
(0, react.useEffect)(() => {
|
|
6319
|
+
if (!props.active) controller.pause();
|
|
6320
|
+
controller.start();
|
|
6321
|
+
return () => {
|
|
6322
|
+
controller.dispose();
|
|
6323
|
+
};
|
|
6324
|
+
}, [controller]);
|
|
6325
|
+
(0, react.useEffect)(() => {
|
|
6326
|
+
if (props.active) {
|
|
6327
|
+
controller.resume();
|
|
6328
|
+
controller.open();
|
|
6329
|
+
} else {
|
|
6330
|
+
controller.pause();
|
|
6331
|
+
controller.close();
|
|
6332
|
+
}
|
|
6333
|
+
}, [controller, props.active]);
|
|
6334
|
+
const state = (0, react.useSyncExternalStore)(controller.subscribe, controller.snapshot, controller.snapshot);
|
|
6335
|
+
const [filter, setFilter] = (0, react.useState)("");
|
|
6336
|
+
const [textMode, setTextMode] = (0, react.useState)("SAFE");
|
|
6337
|
+
const [rejectConfirm, setRejectConfirm] = (0, react.useState)(false);
|
|
6338
|
+
const rejectTriggerRef = (0, react.useRef)(null);
|
|
6339
|
+
const items = actionableProposalItems(props.actions, state.items).filter((item) => `${item.name} ${item.description} ${item.kind}`.toLocaleLowerCase().includes(filter.trim().toLocaleLowerCase()));
|
|
6340
|
+
const selectedActionable = items.some((item) => item.proposalRef.proposalId === state.selectedProposalId);
|
|
6341
|
+
if (props.actions.length === 0) return (0, react.createElement)(react.Fragment);
|
|
6342
|
+
return (0, react.createElement)("div", { className: _run2skill_css_run2skill_settings_page_module_css_default.sectionBody }, (0, react.createElement)("div", { className: _run2skill_css_run2skill_settings_page_module_css_default.toolbar }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Input, {
|
|
6343
|
+
value: filter,
|
|
6344
|
+
placeholder: "筛选技能草稿",
|
|
6345
|
+
"aria-label": "筛选技能草稿",
|
|
6346
|
+
onChange: (event) => {
|
|
6347
|
+
setFilter(event.currentTarget.value);
|
|
6348
|
+
}
|
|
6349
|
+
}), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
6350
|
+
variant: "outline",
|
|
6351
|
+
size: "sm",
|
|
6352
|
+
icon: (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutline16),
|
|
6353
|
+
onClick: () => {
|
|
6354
|
+
controller.open();
|
|
6355
|
+
}
|
|
6356
|
+
}, "刷新")), state.listPhase === "LOADING" ? (0, react.createElement)("p", { role: "status" }, "正在加载待处理事项…") : null, state.listPhase === "ERROR" ? (0, react.createElement)("p", { role: "alert" }, "待处理事项暂不可用。") : null, (0, react.createElement)("div", { className: _run2skill_css_run2skill_settings_page_module_css_default.proposalLayout }, (0, react.createElement)("ul", { className: _run2skill_css_run2skill_settings_page_module_css_default.proposalList }, ...items.map((item) => (0, react.createElement)("li", { key: item.proposalRef.proposalId }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
6357
|
+
variant: state.selectedProposalId === item.proposalRef.proposalId ? "primary" : "outline",
|
|
6358
|
+
className: _run2skill_css_run2skill_settings_page_module_css_default.proposalListButton,
|
|
6359
|
+
disabled: state.mutationPending || state.detailPhase === "LOADING",
|
|
6360
|
+
onClick: () => {
|
|
6361
|
+
controller.select(item.proposalRef.proposalId);
|
|
6362
|
+
}
|
|
6363
|
+
}, `${item.kind} · ${item.name} · ${item.persistenceScope} · ${describeProposalListItem(item)}`)))), (0, react.createElement)("div", { className: _run2skill_css_run2skill_settings_page_module_css_default.detail }, !selectedActionable || state.detailPhase === "IDLE" ? (0, react.createElement)("p", null, "选择一项查看审核事实。") : null, selectedActionable && state.detailPhase === "LOADING" ? (0, react.createElement)("p", { role: "status" }, "正在加载详情…") : null, selectedActionable && state.detailPhase === "ERROR" ? (0, react.createElement)("p", { role: "alert" }, "详情暂不可用。") : null, !selectedActionable || state.detail === void 0 ? null : (0, react.createElement)(ProposalDetailView, {
|
|
6364
|
+
detail: state.detail,
|
|
6365
|
+
textMode,
|
|
6366
|
+
setTextMode,
|
|
6367
|
+
mutationPending: state.mutationPending,
|
|
6368
|
+
onApprove: () => {
|
|
6369
|
+
controller.mutate("APPROVE").finally(props.onMutationSettled);
|
|
6370
|
+
},
|
|
6371
|
+
onReject: (trigger) => {
|
|
6372
|
+
rejectTriggerRef.current = trigger ?? null;
|
|
6373
|
+
setRejectConfirm(true);
|
|
6374
|
+
},
|
|
6375
|
+
onRetry: () => {
|
|
6376
|
+
controller.mutate("RETRY").finally(props.onMutationSettled);
|
|
6377
|
+
},
|
|
6378
|
+
onConfirmDiscard: () => {
|
|
6379
|
+
controller.mutate("CONFIRM_DISCARD").finally(props.onMutationSettled);
|
|
6380
|
+
}
|
|
6381
|
+
}))), (0, react.createElement)("p", {
|
|
6382
|
+
role: "status",
|
|
6383
|
+
"aria-live": "polite",
|
|
6384
|
+
"aria-atomic": true,
|
|
6385
|
+
className: _run2skill_css_run2skill_settings_page_module_css_default.status
|
|
6386
|
+
}, state.announcement), (0, react.createElement)(RejectProposalModal, {
|
|
6387
|
+
open: rejectConfirm,
|
|
6388
|
+
disabled: state.mutationPending,
|
|
6389
|
+
triggerRef: rejectTriggerRef,
|
|
6390
|
+
onClose: () => {
|
|
6391
|
+
setRejectConfirm(false);
|
|
6392
|
+
},
|
|
6393
|
+
onConfirm: () => {
|
|
6394
|
+
setRejectConfirm(false);
|
|
6395
|
+
controller.mutate("REJECT").finally(props.onMutationSettled);
|
|
6396
|
+
}
|
|
6397
|
+
}));
|
|
6398
|
+
}
|
|
6399
|
+
function LearningFailureSection(props) {
|
|
6400
|
+
const [phase, setPhase] = (0, react.useState)("IDLE");
|
|
6401
|
+
const [items, setItems] = (0, react.useState)([]);
|
|
6402
|
+
const [refreshGeneration, setRefreshGeneration] = (0, react.useState)(0);
|
|
6403
|
+
const [pending, setPending] = (0, react.useState)();
|
|
6404
|
+
const [dismiss, setDismiss] = (0, react.useState)();
|
|
6405
|
+
const dismissTriggerRef = (0, react.useRef)(null);
|
|
6406
|
+
const learningActions = (0, react.useMemo)(() => new Map(props.actions.flatMap((action) => action.subjectId === void 0 || !["RETRY_LEARNING", "DISMISS_LEARNING"].includes(action.kind ?? "") ? [] : [[action.subjectId, action]])), [props.actions]);
|
|
6407
|
+
(0, react.useEffect)(() => {
|
|
6408
|
+
if (!props.active || learningActions.size === 0) {
|
|
6409
|
+
setPhase("IDLE");
|
|
6410
|
+
setItems([]);
|
|
6411
|
+
return;
|
|
6412
|
+
}
|
|
6413
|
+
const abort = new AbortController();
|
|
6414
|
+
setPhase("LOADING");
|
|
6415
|
+
(async () => {
|
|
6416
|
+
const next = [];
|
|
6417
|
+
let cursor;
|
|
6418
|
+
for (let page = 0; page < 10; page += 1) {
|
|
6419
|
+
const parsed = learningIssuePageSchema.safeParse(await props.call("learning/issues/list", {
|
|
6420
|
+
apiVersion: 1,
|
|
6421
|
+
currentScope: currentScope(props.workspaceId, props.scopeGeneration ?? 1),
|
|
6422
|
+
limit: 20,
|
|
6423
|
+
...cursor === void 0 ? {} : { cursor }
|
|
6424
|
+
}, abort.signal));
|
|
6425
|
+
if (!parsed.success) throw new Error("invalid learning issue response");
|
|
6426
|
+
next.push(...parsed.data.value.items.filter((item) => learningActions.has(item.workItemId)));
|
|
6427
|
+
cursor = parsed.data.value.nextCursor;
|
|
6428
|
+
if (cursor === void 0) {
|
|
6429
|
+
if (!abort.signal.aborted) {
|
|
6430
|
+
setItems(next);
|
|
6431
|
+
setPhase("READY");
|
|
6432
|
+
}
|
|
6433
|
+
return;
|
|
6434
|
+
}
|
|
6435
|
+
}
|
|
6436
|
+
throw new Error("learning issue page limit exceeded");
|
|
6437
|
+
})().catch(() => {
|
|
6438
|
+
if (!abort.signal.aborted) {
|
|
6439
|
+
setItems([]);
|
|
6440
|
+
setPhase("ERROR");
|
|
6441
|
+
}
|
|
6442
|
+
});
|
|
6443
|
+
return () => {
|
|
6444
|
+
abort.abort();
|
|
6445
|
+
};
|
|
6446
|
+
}, [
|
|
6447
|
+
learningActions,
|
|
6448
|
+
props.active,
|
|
6449
|
+
props.call,
|
|
6450
|
+
props.scopeGeneration,
|
|
6451
|
+
props.workspaceId,
|
|
6452
|
+
refreshGeneration
|
|
6453
|
+
]);
|
|
6454
|
+
const mutate = (endpoint, item) => {
|
|
6455
|
+
if (pending !== void 0) return;
|
|
6456
|
+
const selectedAction = learningActions.get(item.workItemId);
|
|
6457
|
+
if (selectedAction?.subjectId === void 0 || selectedAction.kind === void 0) return;
|
|
6458
|
+
setPending(item.workItemId);
|
|
6459
|
+
props.call(endpoint, {
|
|
6460
|
+
apiVersion: 1,
|
|
6461
|
+
workItemId: item.workItemId,
|
|
6462
|
+
workItemRevision: item.workItemRevision,
|
|
6463
|
+
currentScope: currentScope(props.workspaceId, props.scopeGeneration ?? 1),
|
|
6464
|
+
action: {
|
|
6465
|
+
actionKey: selectedAction.actionKey,
|
|
6466
|
+
subjectId: selectedAction.subjectId,
|
|
6467
|
+
kind: selectedAction.kind,
|
|
6468
|
+
...selectedAction.proposalRef === void 0 ? {} : { proposalRef: selectedAction.proposalRef }
|
|
6469
|
+
},
|
|
6470
|
+
...endpoint === "learning/issues/dismiss" ? { confirm: true } : {}
|
|
6471
|
+
}, new AbortController().signal).then((result) => {
|
|
6472
|
+
if (result === null || typeof result !== "object" || !("ok" in result) || result.ok !== true) throw new Error("learning issue mutation rejected");
|
|
6473
|
+
setItems((current) => current.filter((candidate) => candidate.workItemId !== item.workItemId));
|
|
6474
|
+
props.onMutationSettled();
|
|
6475
|
+
}).catch(() => {
|
|
6476
|
+
setPhase("ERROR");
|
|
6477
|
+
}).finally(() => {
|
|
6478
|
+
setPending(void 0);
|
|
6479
|
+
});
|
|
6480
|
+
};
|
|
6481
|
+
if (learningActions.size === 0) return (0, react.createElement)(react.Fragment);
|
|
6482
|
+
return (0, react.createElement)("section", {
|
|
6483
|
+
className: _run2skill_css_run2skill_settings_page_module_css_default.sectionBody,
|
|
6484
|
+
"aria-label": "学习失败恢复"
|
|
6485
|
+
}, (0, react.createElement)("div", { className: _run2skill_css_run2skill_settings_page_module_css_default.toolbar }, (0, react.createElement)("strong", null, `学习失败 · ${String(learningActions.size)} 项`), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
6486
|
+
variant: "outline",
|
|
6487
|
+
size: "sm",
|
|
6488
|
+
icon: (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutline16),
|
|
6489
|
+
disabled: !props.active || pending !== void 0,
|
|
5974
6490
|
onClick: () => {
|
|
5975
|
-
|
|
6491
|
+
setRefreshGeneration((value) => value + 1);
|
|
6492
|
+
}
|
|
6493
|
+
}, "刷新失败详情")), phase === "LOADING" ? (0, react.createElement)("p", { role: "status" }, "正在加载学习失败详情…") : null, phase === "ERROR" ? (0, react.createElement)("p", { role: "alert" }, "学习失败详情暂不可用,请保持 DSH 运行并稍后重试。") : null, ...items.map((item) => {
|
|
6494
|
+
const action = learningActions.get(item.workItemId);
|
|
6495
|
+
const busy = pending === item.workItemId;
|
|
6496
|
+
return (0, react.createElement)("article", {
|
|
6497
|
+
className: _run2skill_css_run2skill_settings_page_module_css_default.detail,
|
|
6498
|
+
key: item.workItemId
|
|
6499
|
+
}, (0, react.createElement)("div", { className: _run2skill_css_run2skill_settings_page_module_css_default.toolbar }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Pill, null, item.failureCode), item.failureDetail === void 0 ? null : (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Pill, null, item.failureDetail)), (0, react.createElement)("p", null, `第 ${String(item.attempt)} 轮 · 已使用 ${String(item.requestBudgetUsed)} 次模型请求`), item.modelRoute === void 0 ? null : (0, react.createElement)("p", null, `模型路由:${item.modelRoute.provider} / ${item.modelRoute.model}`), (0, react.createElement)("div", { className: _run2skill_css_run2skill_settings_page_module_css_default.actions }, action?.availableActions?.includes("RETRY") === true ? (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
6500
|
+
variant: "primary",
|
|
6501
|
+
disabled: busy || pending !== void 0,
|
|
6502
|
+
onClick: () => {
|
|
6503
|
+
mutate("learning/issues/retry", item);
|
|
6504
|
+
}
|
|
6505
|
+
}, busy ? "正在重试…" : "重试学习") : null, action?.availableActions?.includes("DISMISS") === true ? (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
6506
|
+
variant: "outline",
|
|
6507
|
+
disabled: busy || pending !== void 0,
|
|
6508
|
+
onClick: (event) => {
|
|
6509
|
+
dismissTriggerRef.current = event.currentTarget;
|
|
6510
|
+
setDismiss(item);
|
|
6511
|
+
}
|
|
6512
|
+
}, "关闭此失败") : null));
|
|
6513
|
+
}), (0, react.createElement)(ManagedConfirmationModal, {
|
|
6514
|
+
open: dismiss !== void 0,
|
|
6515
|
+
title: "确认关闭此学习失败?",
|
|
6516
|
+
description: "该失败会从待处理列表隐藏;已有 Skill 和 DSH 的原始会话记录不会改变。",
|
|
6517
|
+
disabled: pending !== void 0,
|
|
6518
|
+
triggerRef: dismissTriggerRef,
|
|
6519
|
+
onClose: () => {
|
|
6520
|
+
setDismiss(void 0);
|
|
6521
|
+
},
|
|
6522
|
+
confirmLabel: "确认关闭",
|
|
6523
|
+
onConfirm: () => {
|
|
6524
|
+
const selected = dismiss;
|
|
6525
|
+
setDismiss(void 0);
|
|
6526
|
+
if (selected !== void 0) mutate("learning/issues/dismiss", selected);
|
|
6527
|
+
}
|
|
6528
|
+
}));
|
|
6529
|
+
}
|
|
6530
|
+
function RejectProposalModal(props) {
|
|
6531
|
+
return (0, react.createElement)(ManagedConfirmationModal, {
|
|
6532
|
+
...props,
|
|
6533
|
+
title: "确认放弃这份技能草稿?",
|
|
6534
|
+
description: "现有 Skill 不会改变;这份技能草稿将离开待处理队列。",
|
|
6535
|
+
confirmLabel: "确认放弃"
|
|
6536
|
+
});
|
|
6537
|
+
}
|
|
6538
|
+
function ManagedConfirmationModal(props) {
|
|
6539
|
+
const contentRef = (0, react.useRef)(null);
|
|
6540
|
+
const onCloseRef = (0, react.useRef)(props.onClose);
|
|
6541
|
+
onCloseRef.current = props.onClose;
|
|
6542
|
+
(0, react.useEffect)(() => {
|
|
6543
|
+
if (!props.open || typeof document === "undefined") return;
|
|
6544
|
+
const dialog = contentRef.current?.closest("[role=\"dialog\"]");
|
|
6545
|
+
const initial = dialog?.querySelector("[autofocus]") ?? dialog?.querySelector(focusableSelector);
|
|
6546
|
+
const onFocus = (event) => {
|
|
6547
|
+
if (dialog != null && event.target instanceof Node && !dialog.contains(event.target)) initial?.focus();
|
|
6548
|
+
};
|
|
6549
|
+
const onKeyboardEvent = (event) => {
|
|
6550
|
+
if (dialog == null) return;
|
|
6551
|
+
if (event.key === "Escape") {
|
|
6552
|
+
event.preventDefault();
|
|
6553
|
+
event.stopImmediatePropagation();
|
|
6554
|
+
onCloseRef.current?.();
|
|
6555
|
+
} else if (event.key === "Tab") trapDialogTab(event.key, event.shiftKey, () => {
|
|
6556
|
+
event.preventDefault();
|
|
6557
|
+
}, dialog, document.activeElement);
|
|
6558
|
+
};
|
|
6559
|
+
document.addEventListener("focusin", onFocus, true);
|
|
6560
|
+
document.addEventListener("keydown", onKeyboardEvent, true);
|
|
6561
|
+
initial?.focus();
|
|
6562
|
+
return () => {
|
|
6563
|
+
document.removeEventListener("focusin", onFocus, true);
|
|
6564
|
+
document.removeEventListener("keydown", onKeyboardEvent, true);
|
|
6565
|
+
props.triggerRef.current?.focus();
|
|
6566
|
+
};
|
|
6567
|
+
}, [props.open, props.triggerRef]);
|
|
6568
|
+
return (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Modal, {
|
|
6569
|
+
open: props.open,
|
|
6570
|
+
title: props.title,
|
|
6571
|
+
closeLabel: "取消",
|
|
6572
|
+
description: props.description,
|
|
6573
|
+
onClose: props.onClose,
|
|
6574
|
+
footer: (0, react.createElement)("div", { className: _run2skill_css_run2skill_settings_page_module_css_default.actions }, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
6575
|
+
variant: "ghost",
|
|
6576
|
+
autoFocus: true,
|
|
6577
|
+
disabled: props.disabled,
|
|
6578
|
+
onClick: props.onClose
|
|
6579
|
+
}, "取消"), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
6580
|
+
variant: "primary",
|
|
6581
|
+
disabled: props.disabled,
|
|
6582
|
+
onClick: props.onConfirm
|
|
6583
|
+
}, props.confirmLabel))
|
|
6584
|
+
}, (0, react.createElement)("div", { ref: contentRef }));
|
|
6585
|
+
}
|
|
6586
|
+
function actionableProposalItems(actions, items) {
|
|
6587
|
+
const identities = new Set(actions.flatMap((action) => action.subjectId === void 0 || action.proposalRef === void 0 || !["REVIEW_PROPOSAL", "RETRY_PUBLICATION"].includes(action.kind ?? "") ? [] : [`${action.subjectId}\u0000${action.proposalRef.proposalId}\u0000${String(action.proposalRef.revision)}\u0000${action.proposalRef.digest}`]));
|
|
6588
|
+
return items.filter((item) => identities.has(`${item.workItemId}\u0000${item.proposalRef.proposalId}\u0000${String(item.proposalRef.revision)}\u0000${item.proposalRef.digest}`));
|
|
6589
|
+
}
|
|
6590
|
+
function hostTabVisible(element) {
|
|
6591
|
+
return element !== null && element.closest("[hidden]") === null;
|
|
6592
|
+
}
|
|
6593
|
+
function useHostTabVisibility() {
|
|
6594
|
+
const ref = (0, react.useRef)(null);
|
|
6595
|
+
const [visible, setVisible] = (0, react.useState)(true);
|
|
6596
|
+
(0, react.useEffect)(() => {
|
|
6597
|
+
const update = () => {
|
|
6598
|
+
setVisible(hostTabVisible(ref.current));
|
|
6599
|
+
};
|
|
6600
|
+
update();
|
|
6601
|
+
if (ref.current === null || typeof MutationObserver === "undefined") return;
|
|
6602
|
+
const observer = new MutationObserver(update);
|
|
6603
|
+
let current = ref.current;
|
|
6604
|
+
while (current !== null) {
|
|
6605
|
+
observer.observe(current, {
|
|
6606
|
+
attributes: true,
|
|
6607
|
+
attributeFilter: ["hidden"]
|
|
6608
|
+
});
|
|
6609
|
+
current = current.parentElement;
|
|
5976
6610
|
}
|
|
5977
|
-
|
|
6611
|
+
return () => {
|
|
6612
|
+
observer.disconnect();
|
|
6613
|
+
};
|
|
6614
|
+
}, []);
|
|
6615
|
+
return {
|
|
6616
|
+
ref,
|
|
6617
|
+
visible
|
|
6618
|
+
};
|
|
5978
6619
|
}
|
|
5979
|
-
function
|
|
5980
|
-
const state = (0, react.
|
|
5981
|
-
const
|
|
5982
|
-
const
|
|
5983
|
-
const
|
|
5984
|
-
const
|
|
5985
|
-
const
|
|
5986
|
-
const
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
6620
|
+
function AttentionSettingsSummary(props) {
|
|
6621
|
+
const [state, setState] = (0, react.useState)({ phase: "LOADING" });
|
|
6622
|
+
const requestGeneration = (0, react.useRef)(0);
|
|
6623
|
+
const activeAbort = (0, react.useRef)();
|
|
6624
|
+
const currentValue = (0, react.useRef)();
|
|
6625
|
+
const currentScopeIdentity = (0, react.useRef)();
|
|
6626
|
+
const scopeIdentity = `${props.sessionId ?? ""}:${props.workspaceId ?? ""}`;
|
|
6627
|
+
const refresh = (0, react.useCallback)(() => {
|
|
6628
|
+
activeAbort.current?.abort();
|
|
6629
|
+
const abort = new AbortController();
|
|
6630
|
+
activeAbort.current = abort;
|
|
6631
|
+
const generation = ++requestGeneration.current;
|
|
6632
|
+
const scopeChanged = currentScopeIdentity.current !== scopeIdentity;
|
|
6633
|
+
currentScopeIdentity.current = scopeIdentity;
|
|
6634
|
+
if (scopeChanged) currentValue.current = void 0;
|
|
6635
|
+
if (currentValue.current === void 0) {
|
|
6636
|
+
setState({ phase: "LOADING" });
|
|
6637
|
+
props.onProjection(void 0);
|
|
5995
6638
|
}
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
|
|
6002
|
-
|
|
6639
|
+
props.callAttention({
|
|
6640
|
+
apiVersion: 1,
|
|
6641
|
+
currentScope: currentScope(props.workspaceId, generation),
|
|
6642
|
+
...props.sessionId === void 0 ? {} : { sessionId: props.sessionId }
|
|
6643
|
+
}, abort.signal).then((result) => {
|
|
6644
|
+
if (abort.signal.aborted || requestGeneration.current !== generation) return;
|
|
6645
|
+
const value = attentionValue(result);
|
|
6646
|
+
if (value === void 0) {
|
|
6647
|
+
if (currentValue.current === void 0) {
|
|
6648
|
+
setState({ phase: "UNAVAILABLE" });
|
|
6649
|
+
props.onProjection(void 0);
|
|
6650
|
+
}
|
|
6651
|
+
return;
|
|
6652
|
+
}
|
|
6653
|
+
currentValue.current = value;
|
|
6654
|
+
setState({
|
|
6655
|
+
phase: "READY",
|
|
6656
|
+
value
|
|
6657
|
+
});
|
|
6658
|
+
props.onProjection(value);
|
|
6659
|
+
}, () => {
|
|
6660
|
+
if (abort.signal.aborted || requestGeneration.current !== generation) return;
|
|
6661
|
+
if (currentValue.current === void 0) {
|
|
6662
|
+
setState({ phase: "UNAVAILABLE" });
|
|
6663
|
+
props.onProjection(void 0);
|
|
6664
|
+
}
|
|
6665
|
+
});
|
|
6666
|
+
return () => {
|
|
6667
|
+
abort.abort();
|
|
6668
|
+
if (activeAbort.current === abort) activeAbort.current = void 0;
|
|
6669
|
+
};
|
|
6670
|
+
}, [
|
|
6671
|
+
props.callAttention,
|
|
6672
|
+
props.onProjection,
|
|
6673
|
+
props.refreshGeneration,
|
|
6674
|
+
props.sessionId,
|
|
6675
|
+
props.workspaceId,
|
|
6676
|
+
scopeIdentity
|
|
6677
|
+
]);
|
|
6678
|
+
(0, react.useEffect)(() => {
|
|
6679
|
+
if (props.active === false) {
|
|
6680
|
+
activeAbort.current?.abort();
|
|
6681
|
+
activeAbort.current = void 0;
|
|
6682
|
+
return;
|
|
6003
6683
|
}
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
6011
|
-
|
|
6684
|
+
refresh();
|
|
6685
|
+
const timer = setInterval(() => {
|
|
6686
|
+
refresh();
|
|
6687
|
+
}, ATTENTION_SETTINGS_POLL_INTERVAL_MS);
|
|
6688
|
+
return () => {
|
|
6689
|
+
clearInterval(timer);
|
|
6690
|
+
activeAbort.current?.abort();
|
|
6691
|
+
activeAbort.current = void 0;
|
|
6692
|
+
};
|
|
6693
|
+
}, [props.active, refresh]);
|
|
6694
|
+
const value = state.value;
|
|
6695
|
+
if (value === void 0 || value.actions.length === 0 && value.runtimeWarnings.length === 0 && value.projectCompleteness !== "UNKNOWN" && value.runtimeCompleteness !== "UNKNOWN") return (0, react.createElement)(react.Fragment);
|
|
6696
|
+
return (0, react.createElement)("div", { className: _run2skill_css_run2skill_settings_page_module_css_default.toolbar }, (0, react.createElement)(react.Fragment, null, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Pill, null, `${String(value.actions.length)} 项可操作事项`), value.projectCompleteness === "UNAVAILABLE" ? (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Pill, null, "未选择当前项目") : value.projectCompleteness === "UNKNOWN" ? (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Pill, null, "PROJECT 状态未知") : null, value.runtimeCompleteness === "UNKNOWN" ? (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Pill, null, "未保存数量未知") : null, ...value.runtimeWarnings.map((warning) => (0, react.createElement)("span", {
|
|
6697
|
+
key: warning.noticeKey,
|
|
6698
|
+
role: "alert"
|
|
6699
|
+
}, warning.message ?? "存在尚未持久化的学习信号。"))));
|
|
6700
|
+
}
|
|
6701
|
+
function Run2skillSettingsPage(props) {
|
|
6702
|
+
const liveSessionId = props.useSessions?.((state) => state.current);
|
|
6703
|
+
const liveWorkspaceId = props.useWorkspaces?.((state) => state.items.find((workspace) => workspace.sessionIds?.includes(liveSessionId ?? ""))?.workspaceId);
|
|
6704
|
+
const workspaceId = props.useSessions === void 0 ? props.workspaceId : liveWorkspaceId;
|
|
6705
|
+
const sessionId = props.useSessions === void 0 ? props.sessionId : liveSessionId;
|
|
6706
|
+
const [open, setOpen] = (0, react.useState)(() => /* @__PURE__ */ new Set(["attention"]));
|
|
6707
|
+
const hostTab = useHostTabVisibility();
|
|
6708
|
+
const [attention, setAttention] = (0, react.useState)();
|
|
6709
|
+
const [attentionRefresh, setAttentionRefresh] = (0, react.useState)(0);
|
|
6710
|
+
const [scopeGeneration, setScopeGeneration] = (0, react.useState)(1);
|
|
6711
|
+
const purgeState = (0, react.useSyncExternalStore)(props.purgeController.subscribe, props.purgeController.snapshot, props.purgeController.snapshot);
|
|
6712
|
+
const purgeDataChanging = purgeState.mutationPending || purgeState.status?.state === "IN_PROGRESS" || purgeState.inProgressReceipt?.state === "IN_PROGRESS";
|
|
6713
|
+
(0, react.useEffect)(() => {
|
|
6714
|
+
setAttention(void 0);
|
|
6715
|
+
setScopeGeneration((value) => value + 1);
|
|
6716
|
+
}, [sessionId, workspaceId]);
|
|
6717
|
+
const attentionEmpty = attention !== void 0 && attention.actions.length === 0 && attention.runtimeWarnings.length === 0 && attention.projectCompleteness !== "UNKNOWN" && attention.runtimeCompleteness !== "UNKNOWN";
|
|
6718
|
+
const disclosure = (id, title, icon, content, collapsedContent) => (0, react.createElement)("section", {
|
|
6719
|
+
className: _run2skill_css_run2skill_settings_page_module_css_default.section,
|
|
6720
|
+
key: id
|
|
6721
|
+
}, (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.DisclosureRow, {
|
|
6722
|
+
icon,
|
|
6723
|
+
title,
|
|
6724
|
+
rowClassName: _run2skill_css_run2skill_settings_page_module_css_default.sectionHeader,
|
|
6725
|
+
titleClassName: _run2skill_css_run2skill_settings_page_module_css_default.sectionTitle,
|
|
6726
|
+
open: open.has(id),
|
|
6727
|
+
expandable: true,
|
|
6728
|
+
expandOnRowClick: true,
|
|
6729
|
+
collapsedContent,
|
|
6730
|
+
onToggle: () => {
|
|
6731
|
+
setOpen((previous) => {
|
|
6732
|
+
const next = new Set(previous);
|
|
6733
|
+
if (next.has(id)) next.delete(id);
|
|
6734
|
+
else next.add(id);
|
|
6735
|
+
return next;
|
|
6736
|
+
});
|
|
6012
6737
|
}
|
|
6013
|
-
},
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
"
|
|
6018
|
-
},
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6738
|
+
}, content));
|
|
6739
|
+
return (0, react.createElement)("div", {
|
|
6740
|
+
ref: hostTab.ref,
|
|
6741
|
+
className: _run2skill_css_run2skill_settings_page_module_css_default.page,
|
|
6742
|
+
"data-run2skill-settings-page": true
|
|
6743
|
+
}, (0, react.createElement)("p", { className: _run2skill_css_run2skill_settings_page_module_css_default.intro }, "Run2Skill 在后台自动沉淀经验;这里只展示需要处理的事项和持久设置。"), disclosure("attention", "需要处理", (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconWarningOutline16), (0, react.createElement)("div", { className: _run2skill_css_run2skill_settings_page_module_css_default.sectionBody }, (0, react.createElement)(AttentionSettingsSummary, {
|
|
6744
|
+
active: hostTab.visible && open.has("attention"),
|
|
6745
|
+
...sessionId === void 0 ? {} : { sessionId },
|
|
6746
|
+
...workspaceId === void 0 ? {} : { workspaceId },
|
|
6747
|
+
callAttention: props.callAttention,
|
|
6748
|
+
refreshGeneration: attentionRefresh,
|
|
6749
|
+
onProjection: setAttention
|
|
6750
|
+
}), attention?.actions.some((action) => ["REVIEW_PROPOSAL", "RETRY_PUBLICATION"].includes(action.kind ?? "")) === true ? (0, react.createElement)(ProposalSettingsSection, {
|
|
6751
|
+
...workspaceId === void 0 ? {} : { workspaceId },
|
|
6752
|
+
callReview: props.callReview,
|
|
6753
|
+
active: hostTab.visible && open.has("attention"),
|
|
6754
|
+
actions: attention.actions,
|
|
6755
|
+
onMutationSettled: () => {
|
|
6756
|
+
setAttentionRefresh((value) => value + 1);
|
|
6757
|
+
},
|
|
6758
|
+
scopeGeneration
|
|
6759
|
+
}) : null, (0, react.createElement)(LearningFailureSection, {
|
|
6760
|
+
...workspaceId === void 0 ? {} : { workspaceId },
|
|
6761
|
+
call: props.callReview,
|
|
6762
|
+
active: hostTab.visible && open.has("attention"),
|
|
6763
|
+
actions: attention?.actions ?? [],
|
|
6764
|
+
onMutationSettled: () => {
|
|
6765
|
+
setAttentionRefresh((value) => value + 1);
|
|
6766
|
+
},
|
|
6767
|
+
scopeGeneration
|
|
6768
|
+
}), attentionEmpty ? (0, react.createElement)("p", { className: _run2skill_css_run2skill_settings_page_module_css_default.empty }, "暂无") : null)), disclosure("activity", "最近活动", (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconSparkle16), (0, react.createElement)(RecentSkillActivitySection, {
|
|
6769
|
+
key: JSON.stringify([
|
|
6770
|
+
workspaceId ?? null,
|
|
6771
|
+
scopeGeneration,
|
|
6772
|
+
purgeState.hostDataEpoch,
|
|
6773
|
+
hostTab.visible && open.has("activity") && !purgeDataChanging
|
|
6774
|
+
]),
|
|
6775
|
+
...workspaceId === void 0 ? {} : { workspaceId },
|
|
6776
|
+
call: props.callActivity,
|
|
6777
|
+
active: hostTab.visible && open.has("activity") && !purgeDataChanging,
|
|
6778
|
+
scopeGeneration,
|
|
6779
|
+
hostDataEpoch: purgeState.hostDataEpoch
|
|
6780
|
+
}), (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.Pill, null, "最近 7 天")), disclosure("automatic", "自动学习", (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconSettingsOutline16), (0, react.createElement)(AutomaticLearningSection, { controller: props.controller })), disclosure("purge", "缓存清理", (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16), (0, react.createElement)("div", { className: _run2skill_css_run2skill_settings_page_module_css_default.sectionBody }, (0, react.createElement)(PurgeSettingsSection, {
|
|
6781
|
+
controller: props.purgeController,
|
|
6782
|
+
active: hostTab.visible && open.has("purge"),
|
|
6783
|
+
onCompleted: () => {
|
|
6784
|
+
setAttentionRefresh((value) => value + 1);
|
|
6785
|
+
}
|
|
6786
|
+
}))));
|
|
6787
|
+
}
|
|
6788
|
+
function currentSessionId(context) {
|
|
6789
|
+
return context.sessions?.list.getSnapshot().current;
|
|
6790
|
+
}
|
|
6791
|
+
function workspaceFor(context, sessionId) {
|
|
6792
|
+
if (sessionId === void 0) return void 0;
|
|
6793
|
+
return context.workspaces.list.getSnapshot().items.find((workspace) => workspace.sessionIds?.includes(sessionId))?.workspaceId;
|
|
6794
|
+
}
|
|
6795
|
+
function applyRun2skillClient(context) {
|
|
6796
|
+
const controller = new AutomaticLearningSettingsController(context.settingsScope.bind({ namespace: "run2skill" }));
|
|
6797
|
+
const callReview = async (endpoint, payload, signal) => await context.connection.rpc.call(RUN2SKILL_RPC_CHANNEL, endpoint, payload, signal);
|
|
6798
|
+
const callAttention = async (payload, signal) => await context.connection.rpc.call(RUN2SKILL_RPC_CHANNEL, "attention", payload, signal);
|
|
6799
|
+
const callActivity = async (payload, signal) => await context.connection.rpc.call(RUN2SKILL_RPC_CHANNEL, "recent-activity/list", payload, signal);
|
|
6800
|
+
const callPurge = async (endpoint, payload, signal) => await context.connection.rpc.call(RUN2SKILL_RPC_CHANNEL, endpoint, payload, signal);
|
|
6801
|
+
const purgeController = new PurgeSettingsController(callPurge, () => workspaceFor(context, currentSessionId(context)));
|
|
6802
|
+
context.effect(() => {
|
|
6803
|
+
const disposeStyle = acquireRun2skillStyle();
|
|
6804
|
+
return () => {
|
|
6805
|
+
controller.dispose();
|
|
6806
|
+
purgeController.dispose();
|
|
6807
|
+
disposeStyle();
|
|
6808
|
+
};
|
|
6809
|
+
}, "run2skill: native settings surface");
|
|
6810
|
+
context.slots.inject("settings.plugins.tab", () => context.slots.register({
|
|
6811
|
+
name: "settings.plugins.tab",
|
|
6812
|
+
id: "run2skill",
|
|
6813
|
+
order: 20,
|
|
6814
|
+
label: "Run2Skill",
|
|
6815
|
+
inject: () => ({
|
|
6816
|
+
controller,
|
|
6817
|
+
purgeController,
|
|
6818
|
+
workspaceId: workspaceFor(context, currentSessionId(context)),
|
|
6819
|
+
sessionId: currentSessionId(context),
|
|
6820
|
+
callAttention,
|
|
6821
|
+
callReview,
|
|
6822
|
+
callActivity
|
|
6823
|
+
})
|
|
6824
|
+
}, Run2skillSettingsPage));
|
|
6825
|
+
context.slots.inject("conversation.session.header.actions", () => context.slots.register({
|
|
6826
|
+
name: "conversation.session.header.actions",
|
|
6827
|
+
id: "run2skill-attention",
|
|
6828
|
+
order: 30,
|
|
6829
|
+
inject: () => ({ callAttention })
|
|
6830
|
+
}, function HeaderAttention(props) {
|
|
6831
|
+
const workspaceId = props.useWorkspaces((state) => state.items.find((workspace) => workspace.sessionIds?.includes(props.sessionId))?.workspaceId);
|
|
6832
|
+
return (0, react.createElement)(Run2skillAttentionToast, {
|
|
6833
|
+
sessionId: props.sessionId,
|
|
6834
|
+
...workspaceId === void 0 ? {} : { workspaceId },
|
|
6835
|
+
callAttention: props.callAttention
|
|
6836
|
+
});
|
|
6022
6837
|
}));
|
|
6023
6838
|
}
|
|
6024
6839
|
//#endregion
|
|
6025
|
-
//#region src/client/
|
|
6026
|
-
|
|
6027
|
-
|
|
6840
|
+
//#region src/client/observe-summary-poller.ts
|
|
6841
|
+
const OBSERVE_SUMMARY_POLL_INTERVAL_MS = 1e4;
|
|
6842
|
+
function browserEnvironment() {
|
|
6843
|
+
return {
|
|
6844
|
+
isVisible: () => typeof document === "undefined" || document.visibilityState === "visible",
|
|
6845
|
+
setInterval: (callback, delayMs) => globalThis.setInterval(callback, delayMs),
|
|
6846
|
+
clearInterval: (handle) => {
|
|
6847
|
+
globalThis.clearInterval(handle);
|
|
6848
|
+
},
|
|
6849
|
+
onFocus: (listener) => {
|
|
6850
|
+
if (typeof window === "undefined") return () => void 0;
|
|
6851
|
+
window.addEventListener("focus", listener);
|
|
6852
|
+
return () => {
|
|
6853
|
+
window.removeEventListener("focus", listener);
|
|
6854
|
+
};
|
|
6855
|
+
},
|
|
6856
|
+
onVisibilityChange: (listener) => {
|
|
6857
|
+
if (typeof document === "undefined") return () => void 0;
|
|
6858
|
+
document.addEventListener("visibilitychange", listener);
|
|
6859
|
+
return () => {
|
|
6860
|
+
document.removeEventListener("visibilitychange", listener);
|
|
6861
|
+
};
|
|
6862
|
+
}
|
|
6863
|
+
};
|
|
6864
|
+
}
|
|
6865
|
+
function parsedSummary(result) {
|
|
6866
|
+
if (result === null || typeof result !== "object" || !("ok" in result) || result.ok !== true) return;
|
|
6867
|
+
const parsed = ObserveSummaryV1Schema.safeParse("value" in result ? result.value : void 0);
|
|
6868
|
+
return parsed.success ? parsed.data : void 0;
|
|
6869
|
+
}
|
|
6870
|
+
var ObserveSummaryPoller = class {
|
|
6871
|
+
call;
|
|
6872
|
+
environment;
|
|
6028
6873
|
#listeners = /* @__PURE__ */ new Set();
|
|
6029
|
-
#
|
|
6030
|
-
#
|
|
6031
|
-
#
|
|
6032
|
-
#
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6874
|
+
#state = { phase: "LOADING" };
|
|
6875
|
+
#started = false;
|
|
6876
|
+
#disposed = false;
|
|
6877
|
+
#timer;
|
|
6878
|
+
#pending;
|
|
6879
|
+
#abort;
|
|
6880
|
+
#removeFocus;
|
|
6881
|
+
#removeVisibility;
|
|
6882
|
+
constructor(call, environment = browserEnvironment()) {
|
|
6883
|
+
this.call = call;
|
|
6884
|
+
this.environment = environment;
|
|
6039
6885
|
}
|
|
6040
|
-
|
|
6886
|
+
snapshot = () => this.#state;
|
|
6041
6887
|
subscribe = (listener) => {
|
|
6042
6888
|
this.#listeners.add(listener);
|
|
6043
6889
|
return () => {
|
|
6044
6890
|
this.#listeners.delete(listener);
|
|
6045
6891
|
};
|
|
6046
6892
|
};
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
this.#
|
|
6051
|
-
|
|
6052
|
-
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
this.#
|
|
6061
|
-
this.#
|
|
6893
|
+
start() {
|
|
6894
|
+
if (this.#started || this.#disposed) return;
|
|
6895
|
+
this.#started = true;
|
|
6896
|
+
this.#removeFocus = this.environment.onFocus(() => {
|
|
6897
|
+
if (this.environment.isVisible()) this.#refresh();
|
|
6898
|
+
});
|
|
6899
|
+
this.#removeVisibility = this.environment.onVisibilityChange(() => {
|
|
6900
|
+
if (this.environment.isVisible()) {
|
|
6901
|
+
this.#schedule();
|
|
6902
|
+
this.#refresh();
|
|
6903
|
+
} else this.#unschedule();
|
|
6904
|
+
});
|
|
6905
|
+
if (this.environment.isVisible()) {
|
|
6906
|
+
this.#schedule();
|
|
6907
|
+
this.#refresh();
|
|
6062
6908
|
}
|
|
6063
6909
|
}
|
|
6910
|
+
whenIdle() {
|
|
6911
|
+
return this.#pending ?? Promise.resolve();
|
|
6912
|
+
}
|
|
6064
6913
|
dispose() {
|
|
6065
|
-
this.#
|
|
6914
|
+
if (this.#disposed) return;
|
|
6915
|
+
this.#disposed = true;
|
|
6916
|
+
this.#unschedule();
|
|
6917
|
+
this.#removeFocus?.();
|
|
6918
|
+
this.#removeVisibility?.();
|
|
6919
|
+
this.#removeFocus = void 0;
|
|
6920
|
+
this.#removeVisibility = void 0;
|
|
6921
|
+
this.#abort?.abort();
|
|
6066
6922
|
this.#listeners.clear();
|
|
6067
6923
|
}
|
|
6068
|
-
#
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6924
|
+
#schedule() {
|
|
6925
|
+
if (this.#timer !== void 0 || this.#disposed) return;
|
|
6926
|
+
this.#timer = this.environment.setInterval(() => {
|
|
6927
|
+
this.#refresh();
|
|
6928
|
+
}, OBSERVE_SUMMARY_POLL_INTERVAL_MS);
|
|
6929
|
+
}
|
|
6930
|
+
#unschedule() {
|
|
6931
|
+
if (this.#timer === void 0) return;
|
|
6932
|
+
this.environment.clearInterval(this.#timer);
|
|
6933
|
+
this.#timer = void 0;
|
|
6934
|
+
}
|
|
6935
|
+
#refresh() {
|
|
6936
|
+
if (this.#disposed || this.#pending !== void 0 || !this.environment.isVisible()) return this.#pending ?? Promise.resolve();
|
|
6937
|
+
const abort = new AbortController();
|
|
6938
|
+
this.#abort = abort;
|
|
6939
|
+
const pending = (async () => {
|
|
6940
|
+
try {
|
|
6941
|
+
const summary = parsedSummary(await this.call({ apiVersion: 1 }, abort.signal));
|
|
6942
|
+
if (this.#disposed) return;
|
|
6943
|
+
if (summary === void 0) this.#markUnavailable();
|
|
6944
|
+
else this.#publish({
|
|
6945
|
+
phase: "READY",
|
|
6946
|
+
summary
|
|
6947
|
+
});
|
|
6948
|
+
} catch {
|
|
6949
|
+
if (!this.#disposed && !abort.signal.aborted) this.#markUnavailable();
|
|
6950
|
+
}
|
|
6951
|
+
})();
|
|
6952
|
+
this.#pending = pending;
|
|
6953
|
+
pending.finally(() => {
|
|
6954
|
+
if (this.#pending === pending) this.#pending = void 0;
|
|
6955
|
+
if (this.#abort === abort) this.#abort = void 0;
|
|
6076
6956
|
});
|
|
6957
|
+
return pending;
|
|
6077
6958
|
}
|
|
6078
|
-
#
|
|
6079
|
-
this.#
|
|
6959
|
+
#markUnavailable() {
|
|
6960
|
+
this.#publish("summary" in this.#state ? {
|
|
6961
|
+
phase: "STALE",
|
|
6962
|
+
summary: this.#state.summary
|
|
6963
|
+
} : { phase: "UNAVAILABLE" });
|
|
6964
|
+
}
|
|
6965
|
+
#publish(state) {
|
|
6966
|
+
this.#state = state;
|
|
6080
6967
|
for (const listener of this.#listeners) listener();
|
|
6081
6968
|
}
|
|
6082
6969
|
};
|
|
6083
|
-
function AutomaticLearningSettingsCard(props) {
|
|
6084
|
-
const state = (0, react.useSyncExternalStore)(props.controller.subscribe, props.controller.getSnapshot, props.controller.getSnapshot);
|
|
6085
|
-
const [open, setOpen] = (0, react.useState)(false);
|
|
6086
|
-
if (state.status === "unavailable") return null;
|
|
6087
|
-
const disabled = state.status !== "ready" || !state.writable || state.saving;
|
|
6088
|
-
return (0, react.createElement)("li", { "data-run2skill-settings-card": true }, (0, react.createElement)("button", {
|
|
6089
|
-
type: "button",
|
|
6090
|
-
"aria-expanded": open,
|
|
6091
|
-
onClick: () => {
|
|
6092
|
-
setOpen((value) => !value);
|
|
6093
|
-
}
|
|
6094
|
-
}, "run2skill", (0, react.createElement)("span", null, "将真实执行中的明确经验转为待审核 Skill Proposal")), open ? (0, react.createElement)("div", null, (0, react.createElement)("label", null, (0, react.createElement)("input", {
|
|
6095
|
-
type: "checkbox",
|
|
6096
|
-
checked: state.automaticLearning === true,
|
|
6097
|
-
disabled,
|
|
6098
|
-
onChange: (event) => {
|
|
6099
|
-
props.controller.setAutomaticLearning(event.currentTarget.checked);
|
|
6100
|
-
}
|
|
6101
|
-
}), "Automatic Learning"), (0, react.createElement)("p", null, "关闭后暂停普通自动学习;显式“保存为 Skill”仍会持久保存。已开始的分析继续使用启动时快照。"), (0, react.createElement)("p", null, "Learning 模型沿用发起 Session 的 provider/model(inherit-session)。"), state.error === void 0 ? null : (0, react.createElement)("p", { role: "status" }, "设置已变化,请刷新后重试。"), (0, react.createElement)(PurgeSettingsSection, { controller: props.purgeController })) : null);
|
|
6102
|
-
}
|
|
6103
|
-
function applyAutomaticLearningSettingsClient(context) {
|
|
6104
|
-
const controller = new AutomaticLearningSettingsController(context.settingsScope.bind({ namespace: "run2skill" }));
|
|
6105
|
-
const callPurge = async (endpoint, payload, signal) => await context.connection.rpc.call(RUN2SKILL_RPC_CHANNEL, endpoint, payload, signal);
|
|
6106
|
-
const purgeController = new PurgeSettingsController(callPurge, () => {
|
|
6107
|
-
return context.workspaces.list.getSnapshot().recentWorkspaceId;
|
|
6108
|
-
});
|
|
6109
|
-
purgeController.start();
|
|
6110
|
-
context.effect?.(() => () => {
|
|
6111
|
-
controller.dispose();
|
|
6112
|
-
purgeController.dispose();
|
|
6113
|
-
}, "run2skill: settings and purge");
|
|
6114
|
-
context.slots.inject("settings.plugin.item", () => context.slots.register({
|
|
6115
|
-
name: "settings.plugin.item",
|
|
6116
|
-
key: "run2skill",
|
|
6117
|
-
inject: () => ({
|
|
6118
|
-
controller,
|
|
6119
|
-
purgeController
|
|
6120
|
-
})
|
|
6121
|
-
}, AutomaticLearningSettingsCard));
|
|
6122
|
-
}
|
|
6123
6970
|
//#endregion
|
|
6124
6971
|
//#region src/client/index.ts
|
|
6125
6972
|
const name = "run2skill-client";
|
|
@@ -6127,19 +6974,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6127
6974
|
"connection",
|
|
6128
6975
|
"slots",
|
|
6129
6976
|
"workspaces",
|
|
6977
|
+
"sessions",
|
|
6130
6978
|
"remote",
|
|
6131
6979
|
"settingsScope"
|
|
6132
6980
|
];
|
|
6133
6981
|
function apply(context) {
|
|
6134
|
-
|
|
6135
|
-
applyAutomaticLearningSettingsClient(context);
|
|
6982
|
+
applyRun2skillClient(context);
|
|
6136
6983
|
}
|
|
6137
6984
|
//#endregion
|
|
6985
|
+
exports.AttentionSettingsSummary = AttentionSettingsSummary;
|
|
6138
6986
|
exports.AutomaticLearningSettingsCard = AutomaticLearningSettingsCard;
|
|
6139
6987
|
exports.AutomaticLearningSettingsController = AutomaticLearningSettingsController;
|
|
6988
|
+
exports.LearningFailureSection = LearningFailureSection;
|
|
6140
6989
|
exports.OBSERVE_SUMMARY_POLL_INTERVAL_MS = OBSERVE_SUMMARY_POLL_INTERVAL_MS;
|
|
6141
|
-
exports.ObserveHeaderAction = ObserveHeaderAction;
|
|
6142
|
-
exports.ObserveStatusPill = ObserveStatusPill;
|
|
6143
6990
|
exports.ObserveSummaryPoller = ObserveSummaryPoller;
|
|
6144
6991
|
exports.PROPOSAL_ACTIVE_POLL_INTERVAL_MS = PROPOSAL_ACTIVE_POLL_INTERVAL_MS;
|
|
6145
6992
|
exports.PROPOSAL_POLL_INTERVAL_MS = PROPOSAL_POLL_INTERVAL_MS;
|
|
@@ -6152,12 +6999,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6152
6999
|
exports.ProposalTextView = ProposalTextView;
|
|
6153
7000
|
exports.PurgeSettingsController = PurgeSettingsController;
|
|
6154
7001
|
exports.PurgeSettingsSection = PurgeSettingsSection;
|
|
7002
|
+
exports.RecentSkillActivitySection = RecentSkillActivitySection;
|
|
6155
7003
|
exports.RejectConfirmationBody = RejectConfirmationBody;
|
|
6156
|
-
exports.
|
|
7004
|
+
exports.RejectProposalModal = RejectProposalModal;
|
|
7005
|
+
exports.Run2skillAttentionToast = Run2skillAttentionToast;
|
|
7006
|
+
exports.Run2skillSettingsPage = Run2skillSettingsPage;
|
|
7007
|
+
exports.actionableProposalItems = actionableProposalItems;
|
|
6157
7008
|
exports.apply = apply;
|
|
6158
7009
|
exports.applyAutomaticLearningSettingsClient = applyAutomaticLearningSettingsClient;
|
|
6159
|
-
exports.
|
|
6160
|
-
exports.describeObserveState = describeObserveState;
|
|
7010
|
+
exports.applyRun2skillClient = applyRun2skillClient;
|
|
6161
7011
|
exports.describeProcessingState = describeProcessingState;
|
|
6162
7012
|
exports.describeProposalListItem = describeProposalListItem;
|
|
6163
7013
|
exports.describeProposalOutcome = describeProposalOutcome;
|
|
@@ -6166,10 +7016,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6166
7016
|
exports.describeReviewDecision = describeReviewDecision;
|
|
6167
7017
|
exports.describeRun2skillHealth = describeRun2skillHealth;
|
|
6168
7018
|
exports.factsFromAction = factsFromAction;
|
|
7019
|
+
exports.hostTabVisible = hostTabVisible;
|
|
6169
7020
|
exports.inject = inject;
|
|
6170
7021
|
exports.makeExactLineDiff = makeExactLineDiff;
|
|
6171
7022
|
exports.makeSafeText = makeSafeText;
|
|
6172
7023
|
exports.name = name;
|
|
7024
|
+
exports.proposalDetailAction = proposalDetailAction;
|
|
6173
7025
|
exports.proposalInboxContentBlocked = proposalInboxContentBlocked;
|
|
6174
7026
|
exports.trapDialogTab = trapDialogTab;
|
|
6175
7027
|
return module.exports;
|