create-principles-disciple 1.145.25 → 1.145.27
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/_release/product-identity.json +1 -1
- package/console/dist/server/config/pd-config-store.d.ts +1 -0
- package/console/dist/server/config/pd-config-store.js +28 -6
- package/console/dist/server/models/RuleCandidateBindingConsoleModel.d.ts +26 -0
- package/console/dist/server/models/RuleCandidateBindingConsoleModel.js +309 -19
- package/console/dist/server/routes/config.js +7 -3
- package/console/dist/server/routes/rule-candidate-bindings.js +26 -1
- package/console/dist/shared/rule-candidate-binding-contract.d.ts +93 -8
- package/console/dist/shared/rule-candidate-binding-contract.js +4 -0
- package/console/dist/ui/api.d.ts +28 -0
- package/console/dist/ui/api.js +13 -1
- package/console/dist/ui/i18n/en.json +93 -25
- package/console/dist/ui/i18n/zh-CN.json +79 -11
- package/console/dist/ui/pages/control-center/RuntimeProfileManager.js +32 -67
- package/console/dist/ui/pages/focus/RuleCandidateBindingPanel.d.ts +29 -0
- package/console/dist/ui/pages/focus/RuleCandidateBindingPanel.js +229 -3
- package/console/dist/ui/utils/rule-binding-validators.d.ts +5 -0
- package/console/dist/ui/utils/rule-binding-validators.js +149 -7
- package/console/dist/web/assets/app.css +6 -0
- package/console/dist/web/assets/app.js +32 -32
- package/core/dist/runtime-v2/adapter/index.d.ts +0 -2
- package/core/dist/runtime-v2/adapter/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/adapter/index.js +0 -1
- package/core/dist/runtime-v2/adapter/index.js.map +1 -1
- package/core/dist/runtime-v2/adapter/output-schema-registry.d.ts +3 -3
- package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.d.ts.map +1 -1
- package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js +1 -2
- package/core/dist/runtime-v2/adapter/pi-ai-runtime-adapter.js.map +1 -1
- package/core/dist/runtime-v2/cli/probe.d.ts +4 -15
- package/core/dist/runtime-v2/cli/probe.d.ts.map +1 -1
- package/core/dist/runtime-v2/cli/probe.js +24 -44
- package/core/dist/runtime-v2/cli/probe.js.map +1 -1
- package/core/dist/runtime-v2/config/index.d.ts +1 -1
- package/core/dist/runtime-v2/config/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/config/index.js.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-agent-binding.d.ts +10 -17
- package/core/dist/runtime-v2/config/pd-config-agent-binding.d.ts.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-agent-binding.js +23 -27
- package/core/dist/runtime-v2/config/pd-config-agent-binding.js.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-defaults.d.ts +3 -5
- package/core/dist/runtime-v2/config/pd-config-defaults.d.ts.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-defaults.js +17 -20
- package/core/dist/runtime-v2/config/pd-config-defaults.js.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-effective.d.ts.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-effective.js +4 -1
- package/core/dist/runtime-v2/config/pd-config-effective.js.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-redaction.d.ts.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-redaction.js +4 -7
- package/core/dist/runtime-v2/config/pd-config-redaction.js.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-types.d.ts +5 -1
- package/core/dist/runtime-v2/config/pd-config-types.d.ts.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-types.js +6 -0
- package/core/dist/runtime-v2/config/pd-config-types.js.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-validate.d.ts.map +1 -1
- package/core/dist/runtime-v2/config/pd-config-validate.js +6 -0
- package/core/dist/runtime-v2/config/pd-config-validate.js.map +1 -1
- package/core/dist/runtime-v2/golden-trace.d.ts.map +1 -1
- package/core/dist/runtime-v2/golden-trace.js +8 -1
- package/core/dist/runtime-v2/golden-trace.js.map +1 -1
- package/core/dist/runtime-v2/index.d.ts +4 -4
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +2 -3
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-intent-preflight.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-intent-preflight.js +26 -95
- package/core/dist/runtime-v2/internalization/artificer-intent-preflight.js.map +1 -1
- package/core/dist/runtime-v2/internalization/bash-risk.d.ts +32 -0
- package/core/dist/runtime-v2/internalization/bash-risk.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/bash-risk.js +785 -0
- package/core/dist/runtime-v2/internalization/bash-risk.js.map +1 -0
- package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts +25 -2
- package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js +177 -14
- package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.js +14 -2
- package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/internalization-orchestrator.d.ts +47 -0
- package/core/dist/runtime-v2/internalization/internalization-orchestrator.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/internalization-orchestrator.js +164 -3
- package/core/dist/runtime-v2/internalization/internalization-orchestrator.js.map +1 -1
- package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts +9 -1
- package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/peer-runner-contracts.js.map +1 -1
- package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts +39 -0
- package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/pitask-metadata.js +59 -0
- package/core/dist/runtime-v2/internalization/pitask-metadata.js.map +1 -1
- package/core/dist/runtime-v2/internalization/rule-source-correction.d.ts +60 -0
- package/core/dist/runtime-v2/internalization/rule-source-correction.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/rule-source-correction.js +214 -0
- package/core/dist/runtime-v2/internalization/rule-source-correction.js.map +1 -0
- package/core/dist/runtime-v2/internalization/rulecode-verification-obligations.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/rulecode-verification-obligations.js +65 -0
- package/core/dist/runtime-v2/internalization/rulecode-verification-obligations.js.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-output.d.ts +14 -2
- package/core/dist/runtime-v2/internalization/scribe-output.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-output.js +57 -2
- package/core/dist/runtime-v2/internalization/scribe-output.js.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-prompt-builder.d.ts +13 -1
- package/core/dist/runtime-v2/internalization/scribe-prompt-builder.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-prompt-builder.js +15 -3
- package/core/dist/runtime-v2/internalization/scribe-prompt-builder.js.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-runner.d.ts +34 -0
- package/core/dist/runtime-v2/internalization/scribe-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/scribe-runner.js +128 -4
- package/core/dist/runtime-v2/internalization/scribe-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/textual-basis-anchor.d.ts +72 -0
- package/core/dist/runtime-v2/internalization/textual-basis-anchor.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/textual-basis-anchor.js +100 -0
- package/core/dist/runtime-v2/internalization/textual-basis-anchor.js.map +1 -0
- package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts +9 -7
- package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts.map +1 -1
- package/core/dist/runtime-v2/pain-signal-runtime-factory.js +59 -121
- package/core/dist/runtime-v2/pain-signal-runtime-factory.js.map +1 -1
- package/core/dist/runtime-v2/runtime-protocol.d.ts +2 -3
- package/core/dist/runtime-v2/runtime-protocol.d.ts.map +1 -1
- package/core/dist/runtime-v2/runtime-protocol.js +15 -10
- package/core/dist/runtime-v2/runtime-protocol.js.map +1 -1
- package/core/dist/runtime-v2/store/lifecycle/lease-manager.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/lifecycle/lease-manager.js +11 -2
- package/core/dist/runtime-v2/store/lifecycle/lease-manager.js.map +1 -1
- package/core/dist/runtime-v2/tools/artificer-l2-tool-contract.d.ts.map +1 -1
- package/core/dist/runtime-v2/tools/artificer-l2-tool-contract.js +26 -0
- package/core/dist/runtime-v2/tools/artificer-l2-tool-contract.js.map +1 -1
- package/core/dist/telemetry-event.d.ts +2 -2
- package/core/dist/telemetry-event.d.ts.map +1 -1
- package/core/dist/telemetry-event.js +5 -0
- package/core/dist/telemetry-event.js.map +1 -1
- package/core/dist/workflow-funnel-loader.d.ts +0 -2
- package/core/dist/workflow-funnel-loader.d.ts.map +1 -1
- package/core/dist/workflow-funnel-loader.js.map +1 -1
- package/core/package.json +5 -1
- package/dist/mvp-config.d.ts.map +1 -1
- package/dist/mvp-config.js +8 -10
- package/dist/mvp-config.js.map +1 -1
- package/host-runtime/dist/internalization-consumer-cycle.js +4 -7
- package/host-runtime/dist/production-rulehost-gate.js +11 -1
- package/host-runtime/package.json +1 -1
- package/package.json +2 -2
- package/pd-cli/dist/commands/diagnose.d.ts +0 -3
- package/pd-cli/dist/commands/diagnose.d.ts.map +1 -1
- package/pd-cli/dist/commands/diagnose.js +20 -57
- package/pd-cli/dist/commands/diagnose.js.map +1 -1
- package/pd-cli/dist/commands/pain-retry.d.ts +0 -3
- package/pd-cli/dist/commands/pain-retry.d.ts.map +1 -1
- package/pd-cli/dist/commands/pain-retry.js +11 -39
- package/pd-cli/dist/commands/pain-retry.js.map +1 -1
- package/pd-cli/dist/commands/runtime-internalization-enqueue-successors.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-internalization-enqueue-successors.js +32 -0
- package/pd-cli/dist/commands/runtime-internalization-enqueue-successors.js.map +1 -1
- package/pd-cli/dist/commands/runtime-internalization-run-once.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-internalization-run-once.js +4 -2
- package/pd-cli/dist/commands/runtime-internalization-run-once.js.map +1 -1
- package/pd-cli/dist/commands/runtime.d.ts +1 -4
- package/pd-cli/dist/commands/runtime.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime.js +25 -95
- package/pd-cli/dist/commands/runtime.js.map +1 -1
- package/pd-cli/dist/index.js +2 -5
- package/pd-cli/dist/index.js.map +1 -1
- package/pd-cli/dist/services/config-doctor.d.ts.map +1 -1
- package/pd-cli/dist/services/config-doctor.js +9 -21
- package/pd-cli/dist/services/config-doctor.js.map +1 -1
- package/pd-cli/dist/services/mainline-snapshot-assembler.d.ts.map +1 -1
- package/pd-cli/dist/services/mainline-snapshot-assembler.js +2 -2
- package/pd-cli/dist/services/mainline-snapshot-assembler.js.map +1 -1
- package/pd-cli/dist/services/resolve-runtime-from-pd-config.d.ts +0 -7
- package/pd-cli/dist/services/resolve-runtime-from-pd-config.d.ts.map +1 -1
- package/pd-cli/dist/services/resolve-runtime-from-pd-config.js +0 -5
- package/pd-cli/dist/services/resolve-runtime-from-pd-config.js.map +1 -1
- package/pd-cli/dist/services/runtime-adapter-resolver.d.ts +1 -8
- package/pd-cli/dist/services/runtime-adapter-resolver.d.ts.map +1 -1
- package/pd-cli/dist/services/runtime-adapter-resolver.js +13 -39
- package/pd-cli/dist/services/runtime-adapter-resolver.js.map +1 -1
- package/pd-cli/package.json +1 -1
- package/plugin/dist/bundle.js +468 -458
- package/plugin/dist/openclaw.plugin.json +1 -1
- package/plugin/openclaw.plugin.json +1 -1
- package/plugin/package.json +1 -1
- package/release-manager/dist/mvp-config.d.ts.map +1 -1
- package/release-manager/dist/mvp-config.js +8 -10
- package/release-manager/dist/mvp-config.js.map +1 -1
- package/release-manager/package.json +2 -2
- package/core/dist/runtime-v2/adapter/openclaw-cli-runtime-adapter.d.ts +0 -72
- package/core/dist/runtime-v2/adapter/openclaw-cli-runtime-adapter.d.ts.map +0 -1
- package/core/dist/runtime-v2/adapter/openclaw-cli-runtime-adapter.js +0 -837
- package/core/dist/runtime-v2/adapter/openclaw-cli-runtime-adapter.js.map +0 -1
- package/core/dist/runtime-v2/utils/cli-process-runner.d.ts +0 -35
- package/core/dist/runtime-v2/utils/cli-process-runner.d.ts.map +0 -1
- package/core/dist/runtime-v2/utils/cli-process-runner.js +0 -263
- package/core/dist/runtime-v2/utils/cli-process-runner.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import { useTranslation } from "react-i18next";
|
|
4
4
|
import { toast } from "sonner";
|
|
@@ -7,12 +7,10 @@ import { enumLabel } from "../../utils/enum-labels.js";
|
|
|
7
7
|
import { createRuntimeProfile, updateRuntimeProfile, deleteRuntimeProfile, } from "../../api.js";
|
|
8
8
|
const EMPTY_FORM = {
|
|
9
9
|
id: "",
|
|
10
|
-
type: "openclaw",
|
|
11
10
|
provider: "",
|
|
12
11
|
model: "",
|
|
13
12
|
apiKeyEnv: "",
|
|
14
13
|
baseUrl: "",
|
|
15
|
-
source: "",
|
|
16
14
|
};
|
|
17
15
|
/** 状态点样式:与 AgentCard 一致 (ready=green, needs_setup/not_ready/unknown=amber, disabled=ink-4) */
|
|
18
16
|
function readinessDotClasses(readiness) {
|
|
@@ -32,69 +30,45 @@ function readinessDotClasses(readiness) {
|
|
|
32
30
|
}
|
|
33
31
|
/**
|
|
34
32
|
* Build the profile object for a create request.
|
|
35
|
-
*
|
|
33
|
+
* Always pi-ai (ADR-0029); empty optional fields are omitted.
|
|
36
34
|
*/
|
|
37
35
|
function buildCreateProfile(form) {
|
|
38
|
-
const profile = {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
else {
|
|
48
|
-
if (form.source.trim() !== "")
|
|
49
|
-
profile.source = form.source.trim();
|
|
50
|
-
if (form.provider.trim() !== "")
|
|
51
|
-
profile.provider = form.provider.trim();
|
|
52
|
-
if (form.model.trim() !== "")
|
|
53
|
-
profile.model = form.model.trim();
|
|
36
|
+
const profile = {
|
|
37
|
+
type: "pi-ai",
|
|
38
|
+
provider: form.provider.trim(),
|
|
39
|
+
model: form.model.trim(),
|
|
40
|
+
apiKeyEnv: form.apiKeyEnv.trim(),
|
|
41
|
+
};
|
|
42
|
+
if (form.baseUrl.trim() !== "") {
|
|
43
|
+
profile.baseUrl = form.baseUrl.trim();
|
|
54
44
|
}
|
|
55
45
|
return profile;
|
|
56
46
|
}
|
|
57
47
|
/**
|
|
58
48
|
* Build the patch object for an update request.
|
|
59
|
-
* Sends
|
|
60
|
-
*
|
|
61
|
-
* values are preserved (merge-patch semantics on the server).
|
|
49
|
+
* Sends only pi-ai fields. Empty optionals are omitted so existing values are
|
|
50
|
+
* preserved (merge-patch semantics on the server).
|
|
62
51
|
*/
|
|
63
52
|
function buildUpdatePatch(form) {
|
|
64
|
-
const patch = {
|
|
65
|
-
if (form.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
patch.baseUrl = form.baseUrl.trim();
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
if (form.source.trim() !== "")
|
|
77
|
-
patch.source = form.source.trim();
|
|
78
|
-
if (form.provider.trim() !== "")
|
|
79
|
-
patch.provider = form.provider.trim();
|
|
80
|
-
if (form.model.trim() !== "")
|
|
81
|
-
patch.model = form.model.trim();
|
|
82
|
-
}
|
|
53
|
+
const patch = {};
|
|
54
|
+
if (form.provider.trim() !== "")
|
|
55
|
+
patch.provider = form.provider.trim();
|
|
56
|
+
if (form.model.trim() !== "")
|
|
57
|
+
patch.model = form.model.trim();
|
|
58
|
+
if (form.apiKeyEnv.trim() !== "")
|
|
59
|
+
patch.apiKeyEnv = form.apiKeyEnv.trim();
|
|
60
|
+
if (form.baseUrl.trim() !== "")
|
|
61
|
+
patch.baseUrl = form.baseUrl.trim();
|
|
83
62
|
return patch;
|
|
84
63
|
}
|
|
85
|
-
/** Pre-fill form from an existing profile summary (edit mode). */
|
|
64
|
+
/** Pre-fill form from an existing pi-ai profile summary (edit mode). */
|
|
86
65
|
function formFromProfile(p) {
|
|
87
|
-
// type is validated to be a known ProfileType by the caller before invoking;
|
|
88
|
-
// here we guard defensively without `as`.
|
|
89
|
-
const type = p.type === "pi-ai" ? "pi-ai" : "openclaw";
|
|
90
66
|
return {
|
|
91
67
|
id: p.id,
|
|
92
|
-
type,
|
|
93
68
|
provider: p.provider ?? "",
|
|
94
69
|
model: p.model ?? "",
|
|
95
70
|
apiKeyEnv: p.apiKeyEnv ?? "",
|
|
96
71
|
baseUrl: "",
|
|
97
|
-
source: "",
|
|
98
72
|
};
|
|
99
73
|
}
|
|
100
74
|
export function RuntimeProfileManager({ profiles, defaultRuntimeId, onMutated, }) {
|
|
@@ -129,16 +103,14 @@ export function RuntimeProfileManager({ profiles, defaultRuntimeId, onMutated, }
|
|
|
129
103
|
if (mode === "create" && form.id.trim() === "") {
|
|
130
104
|
return t("pages.controlCenter.profiles.validationIdRequired");
|
|
131
105
|
}
|
|
132
|
-
if (form.
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
return t("pages.controlCenter.profiles.validationApiKeyEnvRequired");
|
|
141
|
-
}
|
|
106
|
+
if (form.provider.trim() === "") {
|
|
107
|
+
return t("pages.controlCenter.profiles.validationProviderRequired");
|
|
108
|
+
}
|
|
109
|
+
if (form.model.trim() === "") {
|
|
110
|
+
return t("pages.controlCenter.profiles.validationModelRequired");
|
|
111
|
+
}
|
|
112
|
+
if (form.apiKeyEnv.trim() === "") {
|
|
113
|
+
return t("pages.controlCenter.profiles.validationApiKeyEnvRequired");
|
|
142
114
|
}
|
|
143
115
|
return null;
|
|
144
116
|
}
|
|
@@ -206,21 +178,14 @@ export function RuntimeProfileManager({ profiles, defaultRuntimeId, onMutated, }
|
|
|
206
178
|
? t("pages.controlCenter.profiles.formTitleCreate")
|
|
207
179
|
: t("pages.controlCenter.profiles.formTitleEdit", {
|
|
208
180
|
id: form.id,
|
|
209
|
-
}) }), _jsx("button", { type: "button", onClick: closeForm, className: "text-[12px] text-ink-4 hover:text-ink-2 transition-colors", children: t("pages.controlCenter.profiles.cancelButton") })] }), _jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-x-[14px] gap-y-[12px]", children: [_jsxs("div", { children: [_jsx("label", { className: labelClass, htmlFor: "rpf-id", children: t("pages.controlCenter.profiles.fieldId") }), _jsx("input", { id: "rpf-id", type: "text", value: form.id, onChange: (e) => handleFieldChange("id", e.target.value), disabled: mode === "edit" || submitting, placeholder: t("pages.controlCenter.profiles.fieldIdPlaceholder"), className: inputClass, autoComplete: "off", spellCheck: false }), _jsx("p", { className: hintClass, children: t("pages.controlCenter.profiles.fieldIdHint") })] }), _jsxs("div", { children: [_jsx("label", { className: labelClass, htmlFor: "rpf-type", children: t("pages.controlCenter.profiles.
|
|
210
|
-
// rc-2: no `as` bypass — narrow via runtime check.
|
|
211
|
-
// The select only emits "openclaw" | "pi-ai", but we guard
|
|
212
|
-
// defensively so the value is provably ProfileType.
|
|
213
|
-
const v = e.target.value;
|
|
214
|
-
const typed = v === "pi-ai" ? "pi-ai" : "openclaw";
|
|
215
|
-
handleFieldChange("type", typed);
|
|
216
|
-
}, disabled: mode === "edit" || submitting, className: inputClass, children: [_jsx("option", { value: "openclaw", children: t("pages.controlCenter.profiles.typeOpenclaw") }), _jsx("option", { value: "pi-ai", children: t("pages.controlCenter.profiles.typePiAi") })] }), mode === "edit" && (_jsx("p", { className: hintClass, children: t("pages.controlCenter.profiles.fieldIdHint") }))] }), form.type === "pi-ai" && (_jsxs(_Fragment, { children: [_jsxs("div", { children: [_jsx("label", { className: labelClass, htmlFor: "rpf-provider", children: t("pages.controlCenter.profiles.fieldProvider") }), _jsx("input", { id: "rpf-provider", type: "text", value: form.provider, onChange: (e) => handleFieldChange("provider", e.target.value), disabled: submitting, placeholder: t("pages.controlCenter.profiles.fieldProviderPlaceholder"), className: inputClass, autoComplete: "off", spellCheck: false })] }), _jsxs("div", { children: [_jsx("label", { className: labelClass, htmlFor: "rpf-model", children: t("pages.controlCenter.profiles.fieldModel") }), _jsx("input", { id: "rpf-model", type: "text", value: form.model, onChange: (e) => handleFieldChange("model", e.target.value), disabled: submitting, placeholder: t("pages.controlCenter.profiles.fieldModelPlaceholder"), className: inputClass, autoComplete: "off", spellCheck: false })] }), _jsxs("div", { children: [_jsx("label", { className: labelClass, htmlFor: "rpf-apikeyenv", children: t("pages.controlCenter.profiles.fieldApiKeyEnv") }), _jsx("input", { id: "rpf-apikeyenv", type: "text", value: form.apiKeyEnv, onChange: (e) => handleFieldChange("apiKeyEnv", e.target.value), disabled: submitting, placeholder: t("pages.controlCenter.profiles.fieldApiKeyEnvPlaceholder"), className: cn(inputClass, "font-mono"), autoComplete: "off", spellCheck: false }), _jsx("p", { className: hintClass, children: t("pages.controlCenter.profiles.fieldApiKeyEnvHint") })] }), _jsxs("div", { children: [_jsx("label", { className: labelClass, htmlFor: "rpf-baseurl", children: t("pages.controlCenter.profiles.fieldBaseUrl") }), _jsx("input", { id: "rpf-baseurl", type: "text", value: form.baseUrl, onChange: (e) => handleFieldChange("baseUrl", e.target.value), disabled: submitting, placeholder: t("pages.controlCenter.profiles.fieldBaseUrlPlaceholder"), className: inputClass, autoComplete: "off", spellCheck: false })] })] })), form.type === "openclaw" && (_jsxs(_Fragment, { children: [_jsxs("div", { children: [_jsx("label", { className: labelClass, htmlFor: "rpf-source", children: t("pages.controlCenter.profiles.fieldSource") }), _jsx("input", { id: "rpf-source", type: "text", value: form.source, onChange: (e) => handleFieldChange("source", e.target.value), disabled: submitting, placeholder: t("pages.controlCenter.profiles.fieldSourcePlaceholder"), className: inputClass, autoComplete: "off", spellCheck: false })] }), _jsxs("div", { children: [_jsx("label", { className: labelClass, htmlFor: "rpf-provider-oc", children: t("pages.controlCenter.profiles.fieldProvider") }), _jsx("input", { id: "rpf-provider-oc", type: "text", value: form.provider, onChange: (e) => handleFieldChange("provider", e.target.value), disabled: submitting, placeholder: t("pages.controlCenter.profiles.fieldProviderPlaceholder"), className: inputClass, autoComplete: "off", spellCheck: false })] }), _jsxs("div", { children: [_jsx("label", { className: labelClass, htmlFor: "rpf-model-oc", children: t("pages.controlCenter.profiles.fieldModel") }), _jsx("input", { id: "rpf-model-oc", type: "text", value: form.model, onChange: (e) => handleFieldChange("model", e.target.value), disabled: submitting, placeholder: t("pages.controlCenter.profiles.fieldModelPlaceholder"), className: inputClass, autoComplete: "off", spellCheck: false })] })] }))] }), formError !== null && (_jsx("div", { className: "mt-3 px-[12px] py-[8px] border border-danger/35 rounded-[4px] bg-danger/10 text-[12px] text-danger font-mono", children: formError })), _jsxs("div", { className: "mt-3 flex items-center gap-[8px]", children: [_jsx("button", { type: "submit", disabled: submitting, className: "border border-gov bg-gov text-paper rounded-[3px] px-[14px] py-[6px] text-[12.5px] font-medium hover:bg-gov-2 transition-colors disabled:opacity-50 focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: submitting
|
|
181
|
+
}) }), _jsx("button", { type: "button", onClick: closeForm, className: "text-[12px] text-ink-4 hover:text-ink-2 transition-colors", children: t("pages.controlCenter.profiles.cancelButton") })] }), _jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-x-[14px] gap-y-[12px]", children: [_jsxs("div", { children: [_jsx("label", { className: labelClass, htmlFor: "rpf-id", children: t("pages.controlCenter.profiles.fieldId") }), _jsx("input", { id: "rpf-id", type: "text", value: form.id, onChange: (e) => handleFieldChange("id", e.target.value), disabled: mode === "edit" || submitting, placeholder: t("pages.controlCenter.profiles.fieldIdPlaceholder"), className: inputClass, autoComplete: "off", spellCheck: false }), _jsx("p", { className: hintClass, children: t("pages.controlCenter.profiles.fieldIdHint") })] }), _jsxs("div", { children: [_jsx("label", { className: labelClass, htmlFor: "rpf-provider", children: t("pages.controlCenter.profiles.fieldProvider") }), _jsx("input", { id: "rpf-provider", type: "text", value: form.provider, onChange: (e) => handleFieldChange("provider", e.target.value), disabled: submitting, placeholder: t("pages.controlCenter.profiles.fieldProviderPlaceholder"), className: inputClass, autoComplete: "off", spellCheck: false })] }), _jsxs("div", { children: [_jsx("label", { className: labelClass, htmlFor: "rpf-model", children: t("pages.controlCenter.profiles.fieldModel") }), _jsx("input", { id: "rpf-model", type: "text", value: form.model, onChange: (e) => handleFieldChange("model", e.target.value), disabled: submitting, placeholder: t("pages.controlCenter.profiles.fieldModelPlaceholder"), className: inputClass, autoComplete: "off", spellCheck: false })] }), _jsxs("div", { children: [_jsx("label", { className: labelClass, htmlFor: "rpf-apikeyenv", children: t("pages.controlCenter.profiles.fieldApiKeyEnv") }), _jsx("input", { id: "rpf-apikeyenv", type: "text", value: form.apiKeyEnv, onChange: (e) => handleFieldChange("apiKeyEnv", e.target.value), disabled: submitting, placeholder: t("pages.controlCenter.profiles.fieldApiKeyEnvPlaceholder"), className: cn(inputClass, "font-mono"), autoComplete: "off", spellCheck: false }), _jsx("p", { className: hintClass, children: t("pages.controlCenter.profiles.fieldApiKeyEnvHint") })] }), _jsxs("div", { children: [_jsx("label", { className: labelClass, htmlFor: "rpf-baseurl", children: t("pages.controlCenter.profiles.fieldBaseUrl") }), _jsx("input", { id: "rpf-baseurl", type: "text", value: form.baseUrl, onChange: (e) => handleFieldChange("baseUrl", e.target.value), disabled: submitting, placeholder: t("pages.controlCenter.profiles.fieldBaseUrlPlaceholder"), className: inputClass, autoComplete: "off", spellCheck: false })] })] }), formError !== null && (_jsx("div", { className: "mt-3 px-[12px] py-[8px] border border-danger/35 rounded-[4px] bg-danger/10 text-[12px] text-danger font-mono", children: formError })), _jsxs("div", { className: "mt-3 flex items-center gap-[8px]", children: [_jsx("button", { type: "submit", disabled: submitting, className: "border border-gov bg-gov text-paper rounded-[3px] px-[14px] py-[6px] text-[12.5px] font-medium hover:bg-gov-2 transition-colors disabled:opacity-50 focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: submitting
|
|
217
182
|
? mode === "create"
|
|
218
183
|
? t("pages.controlCenter.profiles.creating")
|
|
219
184
|
: t("pages.controlCenter.profiles.saving")
|
|
220
185
|
: t("pages.controlCenter.profiles.saveButton") }), _jsx("button", { type: "button", onClick: closeForm, disabled: submitting, className: "border border-line bg-surface text-ink-3 rounded-[3px] px-[14px] py-[6px] text-[12.5px] font-medium hover:border-line-2 hover:text-ink transition-colors disabled:opacity-50", children: t("pages.controlCenter.profiles.cancelButton") })] })] })), profiles.length === 0 ? (_jsx("div", { className: "bg-surface border border-line-2 rounded-[4px] px-[16px] py-[14px] text-[12.5px] text-ink-4 italic", children: t("pages.controlCenter.profiles.empty") })) : (_jsxs("div", { className: "bg-surface border border-line-2 rounded-[4px] overflow-hidden", children: [_jsxs("div", { className: "grid grid-cols-[12px_1fr_84px_1fr_1fr_1fr_auto] gap-[10px] items-center px-[16px] py-[9px] bg-paper-2 border-b border-line-2 font-mono text-[10.5px] uppercase tracking-[0.04em] text-ink-4", children: [_jsx("span", {}), _jsx("span", { children: t("pages.controlCenter.profiles.columnId") }), _jsx("span", { children: t("pages.controlCenter.profiles.columnType") }), _jsx("span", { children: t("pages.controlCenter.profiles.columnProvider") }), _jsx("span", { children: t("pages.controlCenter.profiles.columnModel") }), _jsx("span", { children: t("pages.controlCenter.profiles.columnApiKeyEnv") }), _jsx("span", { className: "text-right", children: t("pages.controlCenter.profiles.columnActions") })] }), profiles.map((p) => {
|
|
221
186
|
const isDefault = p.id === defaultRuntimeId;
|
|
222
187
|
const isDeleteTarget = deleteTarget === p.id;
|
|
223
|
-
return (_jsxs("div", { className: "border-b border-line-2 last:border-b-0", children: [_jsxs("div", { className: "grid grid-cols-[12px_1fr_84px_1fr_1fr_1fr_auto] gap-[10px] items-center px-[16px] py-[10px] text-[12.5px]", children: [_jsx("div", { className: readinessDotClasses(p.readiness), "aria-label": enumLabel("readiness", p.readiness, t), title: enumLabel("readiness", p.readiness, t) }), _jsxs("div", { className: "min-w-0 flex items-center gap-[6px]", children: [_jsx("span", { className: "font-mono text-[12px] text-ink truncate", children: p.id }), isDefault && (_jsx("span", { className: "shrink-0 text-[9.5px] font-mono px-[5px] py-[1px] rounded-[2px] bg-gov/10 text-gov border border-gov/22 tracking-[0.02em]", children: "default" }))] }),
|
|
188
|
+
return (_jsxs("div", { className: "border-b border-line-2 last:border-b-0", children: [_jsxs("div", { className: "grid grid-cols-[12px_1fr_84px_1fr_1fr_1fr_auto] gap-[10px] items-center px-[16px] py-[10px] text-[12.5px]", children: [_jsx("div", { className: readinessDotClasses(p.readiness), "aria-label": enumLabel("readiness", p.readiness, t), title: enumLabel("readiness", p.readiness, t) }), _jsxs("div", { className: "min-w-0 flex items-center gap-[6px]", children: [_jsx("span", { className: "font-mono text-[12px] text-ink truncate", children: p.id }), isDefault && (_jsx("span", { className: "shrink-0 text-[9.5px] font-mono px-[5px] py-[1px] rounded-[2px] bg-gov/10 text-gov border border-gov/22 tracking-[0.02em]", children: "default" }))] }), _jsxs("span", { className: "font-mono text-[11.5px] text-ink-3 flex items-center gap-[5px]", children: [p.type, p.type === "openclaw" && (_jsx("span", { className: "text-[9.5px] font-mono px-[5px] py-[1px] rounded-[2px] bg-amber/10 text-amber border border-amber/22 tracking-[0.02em]", children: t("pages.controlCenter.profiles.retiredBadge") }))] }), _jsx("span", { className: "text-ink-3 truncate", children: p.provider ?? "—" }), _jsx("span", { className: "text-ink-3 truncate", children: p.model ?? "—" }), _jsx("span", { className: "font-mono text-[11.5px] text-ink-3 truncate", children: p.apiKeyEnv ?? "—" }), _jsxs("div", { className: "flex items-center justify-end gap-[6px]", children: [p.type !== "openclaw" && (_jsx("button", { type: "button", onClick: () => openEdit(p), disabled: mode !== "closed" || deleting, className: "text-[11.5px] px-[10px] py-[4px] rounded-[3px] border border-line bg-surface text-ink-3 hover:border-line-2 hover:text-ink transition-colors disabled:opacity-40 disabled:cursor-not-allowed", children: t("pages.controlCenter.profiles.editButton") })), _jsx("button", { type: "button", onClick: () => {
|
|
224
189
|
setDeleteTarget(p.id);
|
|
225
190
|
setDeleteError(null);
|
|
226
191
|
}, disabled: mode !== "closed" ||
|
|
@@ -1,2 +1,31 @@
|
|
|
1
|
+
import type { RuleBindingQueueItem } from '../../../shared/rule-candidate-binding-contract.js';
|
|
2
|
+
export type RuleBindingCardState = 'awaiting_owner' | 'bound_generating' | 'bound_blocked' | 'bound_advanced' | 'bound_not_built' | 'bound_ambiguous' | 'revoked' | 'attention';
|
|
3
|
+
/** ERR-106: exhaustive display map over the card state union — no default branch. */
|
|
4
|
+
export declare const RULE_BINDING_CARD_STATE_KEY: Record<RuleBindingCardState, string>;
|
|
5
|
+
export declare const RULE_BINDING_CARD_STATE_CLASS: Record<RuleBindingCardState, string>;
|
|
6
|
+
export declare function deriveRuleBindingCardState(item: RuleBindingQueueItem): RuleBindingCardState;
|
|
7
|
+
export interface RuleBindingPainGroup {
|
|
8
|
+
key: string;
|
|
9
|
+
pain: RuleBindingQueueItem['pain'];
|
|
10
|
+
items: RuleBindingQueueItem[];
|
|
11
|
+
}
|
|
12
|
+
/** Group by canonical pain id; a missing pain id gets its own per-candidate
|
|
13
|
+
* group so unattributable candidates never vanish into someone else's group. */
|
|
14
|
+
export declare function groupRuleBindingQueue(items: RuleBindingQueueItem[]): RuleBindingPainGroup[];
|
|
15
|
+
/** The resume action may only appear when the real backend says ready AND the
|
|
16
|
+
* chain is not currently running — a revision during an active run would be
|
|
17
|
+
* a fake retry, not the bounded source revision. */
|
|
18
|
+
export declare function canOfferRuleBindingResume(item: RuleBindingQueueItem): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* PRI-1048 R6 #4: the source-correction entry applies when the bounded
|
|
21
|
+
* source-correction budget is available on a bound+blocked chain — the shape
|
|
22
|
+
* where the binding revision budget is typically already spent (the 52d
|
|
23
|
+
* production candidate). Real eligibility is resolved server-side
|
|
24
|
+
* (previewRuleSourceCorrection); click-time authority is reviseRuleSourceOnce.
|
|
25
|
+
*/
|
|
26
|
+
export declare function canOfferSourceCorrection(item: Pick<RuleBindingQueueItem, 'binding' | 'generation' | 'sourceCorrection'>): boolean;
|
|
27
|
+
/** Model-synthesized fallback sources must stay localized (CodeRabbit PRI-1048:
|
|
28
|
+
* `unknown` previously fell through to raw English in the zh-CN group header). */
|
|
29
|
+
export declare const PAIN_SOURCE_KEY: Record<string, string>;
|
|
1
30
|
/** Inline in Governance Focus; assignment, rule approval and live promotion remain separate. */
|
|
2
31
|
export declare function RuleCandidateBindingPanel(): import("react").JSX.Element | null;
|
|
@@ -2,18 +2,155 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import { Link } from 'react-router-dom';
|
|
5
|
-
import { fetchRuleBindingQueue, fetchRuleBindingReview, mutateRuleBinding } from '../../api.js';
|
|
5
|
+
import { fetchRuleBindingQueue, fetchRuleBindingReview, mutateRuleBinding, reviseRuleBindingOnce, correctRuleSource } from '../../api.js';
|
|
6
|
+
import { splitReadingTitleBody } from '../../utils/reading-layout.js';
|
|
7
|
+
/**
|
|
8
|
+
* PRI-1048 治理焦点「规则的正式原则归属」队列。
|
|
9
|
+
* 按痛苦信号分组的有界卡片列表;每张卡默认回答三问:
|
|
10
|
+
* 这是哪次反馈 / 现在卡在哪 / 我要做什么。技术 ID 与原始 JSON 只进「技术详情」。
|
|
11
|
+
* 归属、规则审批、激活三权分立不变(文案见 separate/declaration)。
|
|
12
|
+
*/
|
|
13
|
+
/** Collapsed first screen shows at most this many pain groups. */
|
|
14
|
+
const COLLAPSED_GROUP_LIMIT = 3;
|
|
15
|
+
/** ERR-106: exhaustive display map over the card state union — no default branch. */
|
|
16
|
+
export const RULE_BINDING_CARD_STATE_KEY = {
|
|
17
|
+
awaiting_owner: 'pages.focus.ruleBinding.stateAwaitingOwner',
|
|
18
|
+
bound_generating: 'pages.focus.ruleBinding.stateBoundGenerating',
|
|
19
|
+
bound_blocked: 'pages.focus.ruleBinding.stateBoundBlocked',
|
|
20
|
+
bound_advanced: 'pages.focus.ruleBinding.stateBoundAdvanced',
|
|
21
|
+
bound_not_built: 'pages.focus.ruleBinding.stateBoundNotBuilt',
|
|
22
|
+
bound_ambiguous: 'pages.focus.ruleBinding.stateBoundAmbiguous',
|
|
23
|
+
revoked: 'pages.focus.ruleBinding.stateRevoked',
|
|
24
|
+
attention: 'pages.focus.ruleBinding.stateAttention',
|
|
25
|
+
};
|
|
26
|
+
export const RULE_BINDING_CARD_STATE_CLASS = {
|
|
27
|
+
awaiting_owner: 'border-gov/40 text-gov',
|
|
28
|
+
bound_generating: 'border-green/40 text-green',
|
|
29
|
+
bound_blocked: 'border-amber/45 text-amber',
|
|
30
|
+
bound_advanced: 'border-green/40 text-green',
|
|
31
|
+
bound_not_built: 'border-line text-ink-3',
|
|
32
|
+
bound_ambiguous: 'border-amber/45 text-amber',
|
|
33
|
+
revoked: 'border-line text-ink-3',
|
|
34
|
+
attention: 'border-amber/45 text-amber',
|
|
35
|
+
};
|
|
36
|
+
export function deriveRuleBindingCardState(item) {
|
|
37
|
+
switch (item.binding.status) {
|
|
38
|
+
case 'unlinked': return 'awaiting_owner';
|
|
39
|
+
case 'binding_revoked': return 'revoked';
|
|
40
|
+
case 'attention': return 'attention';
|
|
41
|
+
case 'bound':
|
|
42
|
+
switch (item.generation?.stage) {
|
|
43
|
+
case null:
|
|
44
|
+
case undefined:
|
|
45
|
+
case 'not_built': return 'bound_not_built';
|
|
46
|
+
case 'in_progress': return 'bound_generating';
|
|
47
|
+
case 'blocked': return 'bound_blocked';
|
|
48
|
+
case 'advanced': return 'bound_advanced';
|
|
49
|
+
case 'ambiguous': return 'bound_ambiguous';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** Group by canonical pain id; a missing pain id gets its own per-candidate
|
|
54
|
+
* group so unattributable candidates never vanish into someone else's group. */
|
|
55
|
+
export function groupRuleBindingQueue(items) {
|
|
56
|
+
const groups = new Map();
|
|
57
|
+
for (const item of items) {
|
|
58
|
+
const key = item.pain.painId === '' ? `candidate:${item.candidateId}` : item.pain.painId;
|
|
59
|
+
const group = groups.get(key) ?? { key, pain: item.pain, items: [] };
|
|
60
|
+
group.items.push(item);
|
|
61
|
+
groups.set(key, group);
|
|
62
|
+
}
|
|
63
|
+
return [...groups.values()];
|
|
64
|
+
}
|
|
65
|
+
/** The resume action may only appear when the real backend says ready AND the
|
|
66
|
+
* chain is not currently running — a revision during an active run would be
|
|
67
|
+
* a fake retry, not the bounded source revision. */
|
|
68
|
+
export function canOfferRuleBindingResume(item) {
|
|
69
|
+
return item.binding.status === 'bound'
|
|
70
|
+
&& item.resume?.status === 'ready'
|
|
71
|
+
&& item.generation?.stage !== 'in_progress'
|
|
72
|
+
&& item.generation?.stage !== 'ambiguous';
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* PRI-1048 R6 #4: the source-correction entry applies when the bounded
|
|
76
|
+
* source-correction budget is available on a bound+blocked chain — the shape
|
|
77
|
+
* where the binding revision budget is typically already spent (the 52d
|
|
78
|
+
* production candidate). Real eligibility is resolved server-side
|
|
79
|
+
* (previewRuleSourceCorrection); click-time authority is reviseRuleSourceOnce.
|
|
80
|
+
*/
|
|
81
|
+
export function canOfferSourceCorrection(item) {
|
|
82
|
+
return item.binding.status === 'bound'
|
|
83
|
+
&& item.generation?.stage === 'blocked'
|
|
84
|
+
&& item.sourceCorrection?.status === 'ready';
|
|
85
|
+
}
|
|
86
|
+
/** ERR-106: next-step copy and the left bar also use exhaustive maps. */
|
|
87
|
+
const RULE_BINDING_NEXT_STEP_KEY = {
|
|
88
|
+
awaiting_owner: 'pages.focus.ruleBinding.nextAwaitingOwner',
|
|
89
|
+
bound_generating: 'pages.focus.ruleBinding.nextBoundGenerating',
|
|
90
|
+
bound_blocked: 'pages.focus.ruleBinding.nextBoundBlocked',
|
|
91
|
+
bound_advanced: 'pages.focus.ruleBinding.nextBoundAdvanced',
|
|
92
|
+
bound_not_built: 'pages.focus.ruleBinding.nextBoundNotBuilt',
|
|
93
|
+
bound_ambiguous: 'pages.focus.ruleBinding.nextBoundAmbiguous',
|
|
94
|
+
revoked: 'pages.focus.ruleBinding.nextRevoked',
|
|
95
|
+
attention: 'pages.focus.ruleBinding.nextAttention',
|
|
96
|
+
};
|
|
97
|
+
const RULE_BINDING_CARD_STATE_BAR = {
|
|
98
|
+
awaiting_owner: 'bg-gov',
|
|
99
|
+
bound_generating: 'bg-green',
|
|
100
|
+
bound_blocked: 'bg-amber',
|
|
101
|
+
bound_advanced: 'bg-green',
|
|
102
|
+
bound_not_built: 'bg-line-2',
|
|
103
|
+
bound_ambiguous: 'bg-amber',
|
|
104
|
+
revoked: 'bg-line-2',
|
|
105
|
+
attention: 'bg-amber',
|
|
106
|
+
};
|
|
107
|
+
/** Model-synthesized fallback sources must stay localized (CodeRabbit PRI-1048:
|
|
108
|
+
* `unknown` previously fell through to raw English in the zh-CN group header). */
|
|
109
|
+
export const PAIN_SOURCE_KEY = {
|
|
110
|
+
unreadable: 'pages.focus.ruleBinding.sourceUnreadable',
|
|
111
|
+
unknown: 'pages.focus.ruleBinding.sourceUnknown',
|
|
112
|
+
manual: 'pages.focus.ruleBinding.sourceManual',
|
|
113
|
+
tool_failure: 'pages.focus.ruleBinding.sourceToolFailure',
|
|
114
|
+
};
|
|
115
|
+
const STAGE_KIND_KEY = {
|
|
116
|
+
dreamer: 'pages.focus.ruleBinding.stageDreamer',
|
|
117
|
+
philosopher: 'pages.focus.ruleBinding.stagePhilosopher',
|
|
118
|
+
scribe: 'pages.focus.ruleBinding.stageScribe',
|
|
119
|
+
artificer: 'pages.focus.ruleBinding.stageArtificer',
|
|
120
|
+
evaluator: 'pages.focus.ruleBinding.stageEvaluator',
|
|
121
|
+
rollout_reviewer: 'pages.focus.ruleBinding.stageRolloutReviewer',
|
|
122
|
+
};
|
|
123
|
+
/** i18n with graceful fallback to the raw server code (visible, not dropped). */
|
|
124
|
+
function codeText(t, prefix, code) {
|
|
125
|
+
if (!code)
|
|
126
|
+
return '';
|
|
127
|
+
const key = `${prefix}.${code}`;
|
|
128
|
+
const text = t(key);
|
|
129
|
+
return text === key ? code : text;
|
|
130
|
+
}
|
|
131
|
+
function painSourceLabel(t, source) {
|
|
132
|
+
const key = PAIN_SOURCE_KEY[source];
|
|
133
|
+
return key ? t(key) : source;
|
|
134
|
+
}
|
|
135
|
+
function stageKindLabel(t, stageKind) {
|
|
136
|
+
if (!stageKind)
|
|
137
|
+
return null;
|
|
138
|
+
const key = STAGE_KIND_KEY[stageKind];
|
|
139
|
+
return key ? t(key) : stageKind;
|
|
140
|
+
}
|
|
6
141
|
/** Inline in Governance Focus; assignment, rule approval and live promotion remain separate. */
|
|
7
142
|
export function RuleCandidateBindingPanel() {
|
|
8
143
|
const { t } = useTranslation();
|
|
9
144
|
const [queue, setQueue] = useState(null);
|
|
10
145
|
const [review, setReview] = useState(null);
|
|
146
|
+
const [openCandidateId, setOpenCandidateId] = useState('');
|
|
11
147
|
const [search, setSearch] = useState('');
|
|
12
148
|
const [selectedId, setSelectedId] = useState('');
|
|
13
149
|
const [reason, setReason] = useState('');
|
|
14
150
|
const [busy, setBusy] = useState(false);
|
|
15
151
|
const [message, setMessage] = useState('');
|
|
16
152
|
const [error, setError] = useState('');
|
|
153
|
+
const [showAllGroups, setShowAllGroups] = useState(false);
|
|
17
154
|
useEffect(() => {
|
|
18
155
|
let active = true;
|
|
19
156
|
void fetchRuleBindingQueue().then(result => {
|
|
@@ -32,12 +169,19 @@ export function RuleCandidateBindingPanel() {
|
|
|
32
169
|
const result = await fetchRuleBindingReview(candidateId, query);
|
|
33
170
|
if (result.success) {
|
|
34
171
|
setReview(result.data);
|
|
172
|
+
setOpenCandidateId(candidateId);
|
|
35
173
|
setSelectedId('');
|
|
36
174
|
}
|
|
37
175
|
else
|
|
38
176
|
setError([result.error, result.nextAction].filter(Boolean).join(' ') || t('pages.focus.ruleBinding.unavailable'));
|
|
39
177
|
setBusy(false);
|
|
40
178
|
}
|
|
179
|
+
function closeReview() { setOpenCandidateId(''); setReview(null); setSearch(''); setReason(''); }
|
|
180
|
+
async function refreshQueue() {
|
|
181
|
+
const result = await fetchRuleBindingQueue();
|
|
182
|
+
if (result.success)
|
|
183
|
+
setQueue(result.data);
|
|
184
|
+
}
|
|
41
185
|
async function decide(action) {
|
|
42
186
|
if (!review)
|
|
43
187
|
return;
|
|
@@ -56,13 +200,95 @@ export function RuleCandidateBindingPanel() {
|
|
|
56
200
|
setMessage(action === 'revoke' ? t('pages.focus.ruleBinding.revoked')
|
|
57
201
|
: result.data.revisionStatus === 'refused' ? t('pages.focus.ruleBinding.resumeFailed') : t('pages.focus.ruleBinding.bound'));
|
|
58
202
|
await load(review.candidateId, search);
|
|
203
|
+
await refreshQueue();
|
|
204
|
+
}
|
|
205
|
+
else
|
|
206
|
+
setError([result.error, result.nextAction].filter(Boolean).join(' ') || t('pages.focus.ruleBinding.unavailable'));
|
|
207
|
+
setBusy(false);
|
|
208
|
+
}
|
|
209
|
+
/** Only reachable when the real resolver said ready (canOfferRuleBindingResume). */
|
|
210
|
+
async function revise() {
|
|
211
|
+
if (!review?.resume || review.resume.status !== 'ready' || review.resume.scribeTaskId === null || review.resume.taskRevisionEpoch === null)
|
|
212
|
+
return;
|
|
213
|
+
setBusy(true);
|
|
214
|
+
setError('');
|
|
215
|
+
setMessage('');
|
|
216
|
+
const result = await reviseRuleBindingOnce(review.candidateId, {
|
|
217
|
+
expectedBindingRevision: review.currentRevision,
|
|
218
|
+
expectedScribeTaskId: review.resume.scribeTaskId,
|
|
219
|
+
expectedTaskRevisionEpoch: review.resume.taskRevisionEpoch,
|
|
220
|
+
requestId: crypto.randomUUID(),
|
|
221
|
+
});
|
|
222
|
+
if (result.success) {
|
|
223
|
+
setMessage(result.data.status === 'revision_already_started'
|
|
224
|
+
? t('pages.focus.ruleBinding.reviseAlreadyStarted')
|
|
225
|
+
: t('pages.focus.ruleBinding.reviseStarted'));
|
|
226
|
+
await load(review.candidateId, search);
|
|
227
|
+
await refreshQueue();
|
|
228
|
+
}
|
|
229
|
+
else
|
|
230
|
+
setError([result.error, result.nextAction].filter(Boolean).join(' ') || t('pages.focus.ruleBinding.unavailable'));
|
|
231
|
+
setBusy(false);
|
|
232
|
+
}
|
|
233
|
+
/** PRI-1048 R6 #4: only reachable when the server preview said ready
|
|
234
|
+
* (canOfferSourceCorrection); the server re-validates every CAS input. */
|
|
235
|
+
async function correctSource() {
|
|
236
|
+
const preview = review?.sourceCorrection;
|
|
237
|
+
if (!review || !preview || preview.status !== 'ready'
|
|
238
|
+
|| preview.scribeTaskId === null || preview.artificerTaskId === null
|
|
239
|
+
|| preview.scribeArtifactDigest === null || preview.expectedBindingRevision === null)
|
|
240
|
+
return;
|
|
241
|
+
setBusy(true);
|
|
242
|
+
setError('');
|
|
243
|
+
setMessage('');
|
|
244
|
+
const result = await correctRuleSource(review.candidateId, {
|
|
245
|
+
expectedBindingRevision: preview.expectedBindingRevision,
|
|
246
|
+
expectedScribeTaskId: preview.scribeTaskId,
|
|
247
|
+
expectedArtificerTaskId: preview.artificerTaskId,
|
|
248
|
+
expectedScribeArtifactDigest: preview.scribeArtifactDigest,
|
|
249
|
+
reason,
|
|
250
|
+
requestId: crypto.randomUUID(),
|
|
251
|
+
});
|
|
252
|
+
if (result.success) {
|
|
253
|
+
setMessage(result.data.status === 'revision_already_started'
|
|
254
|
+
? t('pages.focus.ruleBinding.sourceCorrectionAlreadyStarted')
|
|
255
|
+
: t('pages.focus.ruleBinding.sourceCorrectionStarted'));
|
|
256
|
+
await load(review.candidateId, search);
|
|
257
|
+
await refreshQueue();
|
|
59
258
|
}
|
|
60
259
|
else
|
|
61
260
|
setError([result.error, result.nextAction].filter(Boolean).join(' ') || t('pages.focus.ruleBinding.unavailable'));
|
|
62
261
|
setBusy(false);
|
|
63
262
|
}
|
|
64
|
-
if (queue
|
|
263
|
+
if ((queue === null || queue.items.length === 0) && !error)
|
|
65
264
|
return null;
|
|
265
|
+
const groups = groupRuleBindingQueue(queue?.items ?? []);
|
|
266
|
+
const visibleGroups = showAllGroups ? groups : groups.slice(0, COLLAPSED_GROUP_LIMIT);
|
|
66
267
|
const bound = review?.currentBinding?.action === 'bind';
|
|
67
|
-
return _jsxs("section", { className: "mb-7 rounded border border-line p-4", "data-testid": "rule-binding-panel", "aria-label": t('pages.focus.ruleBinding.title'), children: [_jsx("h2", { className: "text-sm font-medium", children: t('pages.focus.ruleBinding.title') }), _jsx("p", { className: "mt-2 text-sm text-ink-3", children: t('pages.focus.ruleBinding.separate') }),
|
|
268
|
+
return _jsxs("section", { className: "mb-7 rounded border border-line p-4", "data-testid": "rule-binding-panel", "aria-label": t('pages.focus.ruleBinding.title'), children: [_jsx("h2", { className: "text-sm font-medium", children: t('pages.focus.ruleBinding.title') }), _jsx("p", { className: "mt-2 text-sm text-ink-3", children: t('pages.focus.ruleBinding.separate') }), queue && _jsx("p", { className: "mt-2 text-[13px] text-ink-3", role: "status", "data-testid": "rule-binding-counts", children: t('pages.focus.ruleBinding.countsLine', { candidates: queue.summary.candidates, painGroups: queue.summary.painGroups, bound: queue.summary.bound, unlinked: queue.summary.unlinked, blocked: queue.summary.blocked }) }), queue?.truncated && _jsx("p", { className: "mt-1 text-[12.5px] text-amber", children: t('pages.focus.ruleBinding.truncatedNote', { count: queue.items.length }) }), error && _jsx("p", { role: "alert", className: "my-2 text-amber", children: error }), message && _jsx("p", { role: "status", className: "my-2", children: message }), visibleGroups.map(group => _jsxs("div", { className: "mt-4", "data-testid": "rule-binding-pain-group", children: [_jsxs("div", { className: "border-l-2 border-line pl-3", children: [_jsxs("div", { className: "text-[13px] text-ink-2 leading-relaxed", children: [_jsx("span", { className: "font-medium text-ink", children: t('pages.focus.ruleBinding.groupPrefix') }), ' ', group.pain.text
|
|
269
|
+
? splitReadingTitleBody(group.pain.text).title
|
|
270
|
+
: t('pages.focus.ruleBinding.painUnrecorded')] }), _jsxs("div", { className: "mt-0.5 text-[11.5px] text-ink-4", children: [painSourceLabel(t, group.pain.source), group.pain.createdAt ? ` · ${group.pain.createdAt.slice(0, 10)}` : '', ' · ', t('pages.focus.ruleBinding.groupCount', { count: group.items.length })] })] }), group.items.map(item => {
|
|
271
|
+
const cardState = deriveRuleBindingCardState(item);
|
|
272
|
+
const layout = splitReadingTitleBody(item.text);
|
|
273
|
+
const open = openCandidateId === item.candidateId;
|
|
274
|
+
return _jsxs("article", { className: "relative mt-3 pl-[22px] py-[14px] pr-[18px] bg-panel border border-line rounded-[6px]", "data-testid": `rule-binding-card-${item.candidateId}`, children: [_jsx("div", { className: `absolute left-0 top-0 bottom-0 w-[3px] rounded-l-[6px] ${RULE_BINDING_CARD_STATE_BAR[cardState]}` }), _jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: `inline-flex items-center border rounded-[2px] px-[7px] py-1 font-mono text-[11px] uppercase ${RULE_BINDING_CARD_STATE_CLASS[cardState]}`, children: t(RULE_BINDING_CARD_STATE_KEY[cardState]) }), (() => {
|
|
275
|
+
const stageLabel = stageKindLabel(t, item.generation?.stageKind ?? null);
|
|
276
|
+
return stageLabel
|
|
277
|
+
? _jsxs("span", { className: "text-[11.5px] text-ink-4", children: [t('pages.focus.ruleBinding.currentStage'), stageLabel] })
|
|
278
|
+
: null;
|
|
279
|
+
})()] }), _jsx("div", { className: "mt-[10px] mb-1 font-semibold text-ink leading-snug", "data-testid": `rule-binding-title-${item.candidateId}`, children: layout.title }), layout.body !== null && _jsx("div", { className: "mb-2 text-[14px] leading-relaxed text-ink-2 whitespace-pre-wrap break-words", children: layout.body }), item.binding.status === 'bound' && _jsxs("div", { className: "mt-2 px-3 py-2 bg-surface/60 border-l-2 border-gov text-[13px] leading-relaxed", children: [_jsx("span", { className: "text-ink-3", children: t('pages.focus.ruleBinding.boundTo') }), ' ', _jsx("span", { className: "text-ink", children: splitReadingTitleBody(item.binding.principle.text).title }), ' · ', _jsxs("span", { className: "text-ink-4", children: [t(`principles.status${item.binding.principle.status[0]?.toUpperCase()}${item.binding.principle.status.slice(1)}`, { defaultValue: item.binding.principle.status }), " \u00B7 ", t('pages.focus.ruleBinding.version'), " ", item.binding.principle.version] }), ' · ', _jsx(Link, { to: `/principles/${item.binding.principle.principleId}`, className: "text-gov underline", children: t('pages.focus.ruleBinding.viewPrinciple') })] }), cardState === 'bound_blocked' && item.generation && _jsxs("div", { className: "mt-2 text-[12.5px] leading-relaxed text-amber", "data-testid": `rule-binding-blocked-${item.candidateId}`, children: [t('pages.focus.ruleBinding.blockedLine', {
|
|
280
|
+
stage: stageKindLabel(t, item.generation.stageKind) ?? '',
|
|
281
|
+
reason: codeText(t, 'pages.focus.ruleBinding.blockReason', item.generation.reasonCode) || t('pages.focus.ruleBinding.blockReasonUnknown'),
|
|
282
|
+
}), _jsx("div", { className: "mt-0.5 text-ink-3", children: t('pages.focus.ruleBinding.blockedNoOwnerApproval') }), item.generation.detail && _jsxs("details", { className: "mt-1 text-[11.5px]", children: [_jsx("summary", { className: "cursor-pointer select-none", children: t('pages.focus.ruleBinding.blockedDetail') }), _jsx("pre", { className: "mt-1 max-h-40 overflow-auto whitespace-pre-wrap break-words font-mono text-[11px]", children: item.generation.detail })] })] }), cardState === 'attention' && _jsx("div", { className: "mt-2 text-[12.5px] leading-relaxed text-amber", "data-testid": `rule-binding-attention-${item.candidateId}`, children: t('pages.focus.ruleBinding.attentionLine', { reason: item.binding.status === 'attention' ? item.binding.reason : '' }) }), _jsxs("div", { className: "mt-2 text-[12.5px] text-ink-3 leading-relaxed", "data-testid": `rule-binding-next-${item.candidateId}`, children: [_jsx("span", { className: "font-medium", children: t('pages.focus.ruleBinding.nextLabel') }), ' ', cardState === 'bound_blocked' && item.resume?.status === 'refused'
|
|
283
|
+
? t('pages.focus.ruleBinding.nextBoundBlockedNoResume', { reason: codeText(t, 'pages.focus.ruleBinding.resumeReason', item.resume.reason) })
|
|
284
|
+
: t(RULE_BINDING_NEXT_STEP_KEY[cardState])] }), _jsxs("details", { className: "mt-2 text-[11.5px] text-ink-4", children: [_jsx("summary", { className: "cursor-pointer select-none", children: t('pages.focus.ruleBinding.techDetails') }), _jsxs("dl", { className: "mt-1 font-mono text-[11px] break-all", children: [_jsxs("div", { children: ["candidateId: ", item.candidateId] }), _jsxs("div", { children: ["painId: ", item.pain.painId || '—'] }), item.binding.status === 'bound' && _jsxs("div", { children: ["bindingRevision: ", item.binding.principle.revision, " \u00B7 decidedAt: ", item.binding.principle.decidedAt || '—'] }), item.resume && _jsxs("div", { children: ["resume: ", item.resume.status, item.resume.reason ? ` (${item.resume.reason})` : '', item.resume.scribeTaskId ? ` · ${item.resume.scribeTaskId}` : ''] })] })] }), _jsx("button", { type: "button", className: "mt-3 inline-flex items-center border border-line bg-surface text-ink rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed", disabled: busy, "aria-expanded": open, onClick: () => { if (open)
|
|
285
|
+
closeReview();
|
|
286
|
+
else {
|
|
287
|
+
setSearch('');
|
|
288
|
+
setReason('');
|
|
289
|
+
void load(item.candidateId);
|
|
290
|
+
} }, children: open ? t('pages.focus.ruleBinding.closeReview') : t('pages.focus.ruleBinding.openReview') }), open && review && review.candidateId === item.candidateId && _jsxs("div", { className: "mt-4 border-t border-line pt-3", children: [_jsx("h3", { className: "font-medium text-[13px]", children: t('pages.focus.ruleBinding.candidate') }), _jsx("p", { className: "mt-1 whitespace-pre-wrap text-sm", children: review.candidateText }), _jsxs("p", { className: "mt-2 text-sm", children: [t('pages.focus.ruleBinding.pain'), "\uFF1A", review.sourcePain.text ?? t('pages.focus.ruleBinding.painUnrecorded')] }), _jsxs("details", { className: "mt-2 text-sm", children: [_jsx("summary", { children: t('pages.focus.ruleBinding.evidence') }), _jsx("pre", { className: "whitespace-pre-wrap break-words", children: review.evidence })] }), bound && review.boundPrinciple && _jsxs(_Fragment, { children: [_jsxs("div", { className: "mt-3 px-3 py-2 bg-surface/60 border-l-2 border-gov text-[13px] leading-relaxed", children: [_jsx("div", { className: "whitespace-pre-wrap", children: review.boundPrinciple.text }), _jsxs("div", { className: "mt-1 text-ink-4", children: [t('pages.focus.ruleBinding.boundSince', { revision: review.boundPrinciple.revision, date: review.boundPrinciple.decidedAt.slice(0, 10) || '—' }), " \u00B7 ", _jsx(Link, { to: `/principles/${review.boundPrinciple.principleId}`, className: "text-gov underline", children: t('pages.focus.ruleBinding.viewPrinciple') })] })] }), canOfferRuleBindingResume({ ...item, resume: review.resume }) && _jsx("button", { type: "button", className: "mt-3 rounded border border-gov bg-gov text-paper px-3 py-2 text-sm disabled:opacity-50", disabled: busy, onClick: () => void revise(), children: t('pages.focus.ruleBinding.reviseCta') }), canOfferSourceCorrection({ ...item, sourceCorrection: review.sourceCorrection }) && _jsx("button", { type: "button", className: "mt-3 ml-2 rounded border border-gov bg-gov text-paper px-3 py-2 text-sm disabled:opacity-50", disabled: busy || !reason.trim(), "data-testid": "rule-binding-source-correct-cta", onClick: () => void correctSource(), children: t('pages.focus.ruleBinding.sourceCorrectionCta') }), review.resume?.status === 'refused' && _jsx("p", { className: "mt-3 text-[12.5px] text-ink-3 leading-relaxed", children: t('pages.focus.ruleBinding.resumeUnavailable', { reason: codeText(t, 'pages.focus.ruleBinding.resumeReason', review.resume.reason) }) }), review.generation?.stage === 'blocked' && review.sourceCorrection?.status === 'unavailable' && _jsx("p", { className: "mt-2 text-[12.5px] text-ink-4 leading-relaxed", children: t('pages.focus.ruleBinding.sourceCorrectionUnavailable', { reason: review.sourceCorrection.reason ?? '' }) }), review.generation?.stage === 'in_progress' && _jsx("p", { className: "mt-3 text-[12.5px] text-ink-3", children: t('pages.focus.ruleBinding.nextBoundGenerating') })] }), !bound && _jsxs(_Fragment, { children: [_jsx("p", { className: "mt-3 text-sm text-ink-3", children: t('pages.focus.ruleBinding.chooseTarget') }), _jsxs("form", { className: "mt-3 flex gap-2", onSubmit: event => { event.preventDefault(); void load(review.candidateId, search); }, children: [_jsx("input", { value: search, onChange: event => setSearch(event.target.value), maxLength: 200, "aria-label": t('pages.focus.ruleBinding.search'), className: "border border-line p-2 text-sm" }), _jsx("button", { disabled: busy, type: "submit", children: t('pages.focus.ruleBinding.search') })] }), review.eligibleTargets.length === 0 && _jsxs("p", { className: "mt-2 text-sm", children: [t('pages.focus.ruleBinding.noTarget'), " ", _jsx(Link, { to: "/principles", className: "underline", children: t('pages.focus.ruleBinding.formation') })] }), review.eligibleTargets.map(target => _jsxs("label", { className: "my-3 block border border-line p-3 text-sm", children: [_jsx("input", { type: "radio", name: "binding-target", value: target.principleId, checked: selectedId === target.principleId, onChange: () => setSelectedId(target.principleId) }), _jsxs("span", { className: "ml-2", children: [t(`principles.status${target.status[0]?.toUpperCase()}${target.status.slice(1)}`, { defaultValue: target.status }), " \u00B7 ", t('pages.focus.ruleBinding.version'), " ", target.version] }), _jsx("p", { className: "mt-2 whitespace-pre-wrap", children: target.text })] }, target.principleId)), _jsx("p", { className: "mt-3 text-sm text-ink-3", children: review.resumeNotice }), _jsx("p", { className: "mt-2 text-sm", children: t('pages.focus.ruleBinding.declaration') })] }), _jsxs("label", { className: "mt-3 block text-sm", children: [t('pages.focus.ruleBinding.reason'), _jsx("textarea", { className: "mt-1 block w-full border border-line p-2", maxLength: 2000, value: reason, onChange: event => setReason(event.target.value) })] }), _jsxs("div", { className: "mt-3 flex gap-2 flex-wrap", children: [!bound && _jsx("button", { type: "button", className: "rounded border border-gov px-3 py-2 text-sm disabled:opacity-50", disabled: busy || !reason.trim() || !selectedId, onClick: () => void decide('bind'), children: t('pages.focus.ruleBinding.bind') }), bound && _jsx("button", { type: "button", className: "rounded border border-line px-3 py-2 text-sm disabled:opacity-50", disabled: busy || !reason.trim(), onClick: () => void decide('revoke'), children: t('pages.focus.ruleBinding.revokeSecondary') })] }), _jsxs("p", { className: "mt-3 text-sm text-ink-3", children: [t('pages.focus.ruleBinding.revokeBoundary'), " ", _jsx(Link, { to: "/activation", className: "underline", children: t('pages.focus.ruleBinding.stop') })] })] })] }, item.candidateId);
|
|
291
|
+
})] }, group.key)), groups.length > COLLAPSED_GROUP_LIMIT && _jsx("button", { type: "button", className: "mt-3 text-[12.5px] text-gov underline", onClick: () => setShowAllGroups(value => !value), "aria-expanded": showAllGroups, "data-testid": "rule-binding-groups-toggle", children: showAllGroups
|
|
292
|
+
? t('pages.focus.ruleBinding.hideRemaining')
|
|
293
|
+
: t('pages.focus.ruleBinding.viewRemaining', { count: groups.length - COLLAPSED_GROUP_LIMIT }) })] });
|
|
68
294
|
}
|
|
@@ -6,3 +6,8 @@ export declare function validateBindingMutation(value: unknown): {
|
|
|
6
6
|
revisionStatus?: string;
|
|
7
7
|
nextAction?: string;
|
|
8
8
|
} | null;
|
|
9
|
+
export declare function validateBindingRevise(value: unknown): {
|
|
10
|
+
status: string;
|
|
11
|
+
reason?: string;
|
|
12
|
+
nextAction?: string;
|
|
13
|
+
} | null;
|