create-principles-disciple 1.145.34 → 1.145.36
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 +2 -2
- package/console/dist/server/models/ApprovalsConsoleModel.d.ts +22 -2
- package/console/dist/server/models/ApprovalsConsoleModel.js +56 -16
- package/console/dist/server/models/ApprovalsGroupedConsoleModel.d.ts +8 -0
- package/console/dist/server/models/ApprovalsGroupedConsoleModel.js +3 -2
- package/console/dist/server/models/approval-review-material.d.ts +15 -0
- package/console/dist/server/models/approval-review-material.js +32 -0
- package/console/dist/server/routes/approvals.js +32 -7
- package/console/dist/ui/api.d.ts +8 -2
- package/console/dist/ui/api.js +8 -4
- package/console/dist/ui/i18n/en.json +98 -7
- package/console/dist/ui/i18n/zh-CN.json +98 -7
- package/console/dist/ui/pages/focus/ApprovalSubjectReading.d.ts +24 -0
- package/console/dist/ui/pages/focus/ApprovalSubjectReading.js +133 -0
- package/console/dist/ui/pages/focus/FocusPage.d.ts +5 -26
- package/console/dist/ui/pages/focus/FocusPage.js +166 -459
- package/console/dist/ui/pages/focus/OwnerWorkbench.d.ts +19 -0
- package/console/dist/ui/pages/focus/OwnerWorkbench.js +75 -0
- package/console/dist/ui/pages/focus/RuleCandidateBindingPanel.d.ts +4 -1
- package/console/dist/ui/pages/focus/RuleCandidateBindingPanel.js +7 -3
- package/console/dist/ui/pages/focus/focus-validation.js +31 -0
- package/console/dist/ui/pages/principles/PrincipleDetailPage.js +110 -86
- package/console/dist/ui/pages/principles/PrinciplesPage.js +39 -30
- package/console/dist/ui/utils/approval-review.d.ts +30 -0
- package/console/dist/ui/utils/approval-review.js +50 -0
- package/console/dist/ui/utils/reading-layout.d.ts +1 -0
- package/console/dist/ui/utils/reading-layout.js +1 -1
- package/console/dist/ui/utils/validators.d.ts +4 -0
- package/console/dist/ui/utils/validators.js +10 -2
- package/console/dist/web/assets/app.css +119 -11
- package/console/dist/web/assets/app.js +40 -30
- package/core/dist/runtime-v2/activation/activation-types.d.ts +12 -2
- package/core/dist/runtime-v2/activation/activation-types.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/activation-types.js.map +1 -1
- package/core/dist/runtime-v2/activation/approval-queue.d.ts +3 -2
- package/core/dist/runtime-v2/activation/approval-queue.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/approval-queue.js +6 -4
- package/core/dist/runtime-v2/activation/approval-queue.js.map +1 -1
- package/core/dist/runtime-v2/activation/approval-review-guard.d.ts +26 -0
- package/core/dist/runtime-v2/activation/approval-review-guard.d.ts.map +1 -0
- package/core/dist/runtime-v2/activation/approval-review-guard.js +25 -0
- package/core/dist/runtime-v2/activation/approval-review-guard.js.map +1 -0
- package/core/dist/runtime-v2/activation/memory-approval-store.d.ts +6 -2
- package/core/dist/runtime-v2/activation/memory-approval-store.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/memory-approval-store.js +26 -2
- package/core/dist/runtime-v2/activation/memory-approval-store.js.map +1 -1
- package/core/dist/runtime-v2/activation/sqlite-approval-store.d.ts +6 -2
- package/core/dist/runtime-v2/activation/sqlite-approval-store.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/sqlite-approval-store.js +59 -2
- package/core/dist/runtime-v2/activation/sqlite-approval-store.js.map +1 -1
- package/core/dist/runtime-v2/diagnostician-association-shadow.d.ts +24 -0
- package/core/dist/runtime-v2/diagnostician-association-shadow.d.ts.map +1 -0
- package/core/dist/runtime-v2/diagnostician-association-shadow.js +84 -0
- package/core/dist/runtime-v2/diagnostician-association-shadow.js.map +1 -0
- package/core/dist/runtime-v2/diagnostician-association-validation.d.ts +18 -0
- package/core/dist/runtime-v2/diagnostician-association-validation.d.ts.map +1 -0
- package/core/dist/runtime-v2/diagnostician-association-validation.js +47 -0
- package/core/dist/runtime-v2/diagnostician-association-validation.js.map +1 -0
- package/core/dist/runtime-v2/diagnostician-output.d.ts +123 -0
- package/core/dist/runtime-v2/diagnostician-output.d.ts.map +1 -1
- package/core/dist/runtime-v2/diagnostician-output.js +41 -0
- package/core/dist/runtime-v2/diagnostician-output.js.map +1 -1
- package/core/dist/runtime-v2/index.d.ts +2 -0
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +1 -0
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/internalization/diag-router-runner.d.ts +12 -1
- package/core/dist/runtime-v2/internalization/diag-router-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/diag-router-runner.js +50 -2
- package/core/dist/runtime-v2/internalization/diag-router-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/router-context-evidence.d.ts +45 -0
- package/core/dist/runtime-v2/internalization/router-context-evidence.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/router-context-evidence.js +64 -0
- package/core/dist/runtime-v2/internalization/router-context-evidence.js.map +1 -0
- package/core/dist/runtime-v2/signal-collector/types.d.ts +8 -0
- package/core/dist/runtime-v2/signal-collector/types.d.ts.map +1 -1
- package/core/dist/runtime-v2/signal-collector/types.js.map +1 -1
- package/core/package.json +1 -1
- package/package.json +2 -2
- package/pd-cli/dist/commands/config-doctor.js +1 -1
- package/pd-cli/dist/commands/config-doctor.js.map +1 -1
- package/pd-cli/dist/services/config-doctor.d.ts +2 -0
- package/pd-cli/dist/services/config-doctor.d.ts.map +1 -1
- package/pd-cli/dist/services/config-doctor.js +1 -0
- package/pd-cli/dist/services/config-doctor.js.map +1 -1
- package/pd-cli/package.json +1 -1
- package/plugin/dist/bundle.js +462 -462
- package/plugin/dist/core/signal-collector-host.d.ts +9 -0
- package/plugin/dist/core/signal-health.d.ts +6 -0
- package/plugin/dist/core/trajectory.d.ts +8 -0
- package/plugin/dist/openclaw.plugin.json +1 -1
- package/plugin/dist/rulehost-evidence.js +6 -6
- package/plugin/openclaw.plugin.json +1 -1
- package/plugin/package.json +1 -1
- package/release-manager/package.json +2 -2
|
@@ -48,6 +48,97 @@
|
|
|
48
48
|
},
|
|
49
49
|
"pages": {
|
|
50
50
|
"focus": {
|
|
51
|
+
"workbench": {
|
|
52
|
+
"textPreview": "正文预览",
|
|
53
|
+
"codePreview": "规则实现预览",
|
|
54
|
+
"fullText": "展开完整正文",
|
|
55
|
+
"fullCode": "展开完整规则实现",
|
|
56
|
+
"scope": "适用范围",
|
|
57
|
+
"compare": "对照上一版本与保留的原意",
|
|
58
|
+
"intentReviewed": "我已核对前后版本与需要保留的原意",
|
|
59
|
+
"promptConsequence": "批准后进入现有提示原则激活流程;实际投递仍受宿主与容量限制。",
|
|
60
|
+
"ruleConsequence": "批准执行方案不等于正式上线,观察与上线决定仍然独立。",
|
|
61
|
+
"approveOne": "批准这个版本",
|
|
62
|
+
"rejectOne": "拒绝这个版本",
|
|
63
|
+
"replaceOne": "替换待审版本",
|
|
64
|
+
"replacementId": "已验证的新草案标识",
|
|
65
|
+
"reason": "请说明理由",
|
|
66
|
+
"confirm": "确认提交",
|
|
67
|
+
"cancel": "取消",
|
|
68
|
+
"resultUnknown": "提交结果尚未确认,请重新读取状态后再决定,勿重复提交。",
|
|
69
|
+
"notRecorded": "这次决定尚未完成,请按最新材料核对。",
|
|
70
|
+
"approved": "已批准。实际激活与投递请查看生效情况。",
|
|
71
|
+
"rejected": "已拒绝这个版本,其他对象未被处理。",
|
|
72
|
+
"activationFailed": "审批已完成,但激活失败。请查看生效情况与恢复入口,不要重复批准。",
|
|
73
|
+
"approvedRuntimeUnknown": "审批已记录,激活尚未完成或暂不可确认。请查看生效情况。",
|
|
74
|
+
"replaced": "已替换待审版本。请重新核对当前正文后决定。",
|
|
75
|
+
"versionNeedsReview": "待核对版本 {{index}}",
|
|
76
|
+
"title": "需要你处理",
|
|
77
|
+
"subtitle": "选一件事,看清建议和决定后果,再继续下一件。",
|
|
78
|
+
"list": "待处理事项",
|
|
79
|
+
"count": "{{count}} 项",
|
|
80
|
+
"uncertain": "部分资料暂未读取,当前列表可能不完整。",
|
|
81
|
+
"empty": "当前没有可确认的待处理事项。",
|
|
82
|
+
"back": "返回事项列表",
|
|
83
|
+
"next": "查看下一件",
|
|
84
|
+
"readingEnd": "本件材料到这里结束。你可以返回列表,或查看下一件。",
|
|
85
|
+
"completed": "处理结果",
|
|
86
|
+
"notCurrent": "这件事已不在当前列表",
|
|
87
|
+
"startTitle": "从一件事开始",
|
|
88
|
+
"startBody": "先选择一个待办。这里会集中显示它的材料、需要核对的地方和可执行操作。",
|
|
89
|
+
"notCurrentBody": "它可能已处理,或当前资料暂不可用。请以重新读取后的状态为准。",
|
|
90
|
+
"start": "开始处理",
|
|
91
|
+
"loading": "正在读取这次审批的准确材料…",
|
|
92
|
+
"why": "这次想解决什么",
|
|
93
|
+
"proposal": "PD 建议怎样改变",
|
|
94
|
+
"decision": "本次决定",
|
|
95
|
+
"proposedBehavior": "拟议行为(来自本次草案)",
|
|
96
|
+
"currentApproved": "当前已批准",
|
|
97
|
+
"currentRejected": "当前已拒绝",
|
|
98
|
+
"refusedCurrentState": "本次操作未执行;{{state}}。",
|
|
99
|
+
"currentStateConfirmed": "已重新读取:{{state}}。本次提交是否成功暂无法确认。",
|
|
100
|
+
"runtimeCheckLink": "实际激活与投递请查看生效情况。",
|
|
101
|
+
"sourceUnlinked": "尚未解析出这项审批对应的原则,也没有找到可读的名称。请先核对准确正文与来源;材料不足时可以拒绝。",
|
|
102
|
+
"risksUnknown": "这份版本的风险说明尚未记录或暂不可读,风险仍需核对。",
|
|
103
|
+
"risksEmpty": "这份版本未列出风险;这不代表已验证安全。",
|
|
104
|
+
"approvalNote": "批准备注(可选)",
|
|
105
|
+
"budgetBasis": "查看提示词容量的来源与估算依据",
|
|
106
|
+
"sourcesPending": "资料待确认",
|
|
107
|
+
"countExplanation": "这些事项如何计数",
|
|
108
|
+
"archiveReview": "暂缓与归档审查",
|
|
109
|
+
"archiveConsequence": "同意后会将这项提案暂缓或归档,不会启用提示词或执行规则。",
|
|
110
|
+
"approveArchive": "同意暂缓或归档",
|
|
111
|
+
"beforeDecision": "决定前需要知道",
|
|
112
|
+
"subjectUnconfirmed": "尚未确认这份版本对应的完整决策材料,请先核对来源。",
|
|
113
|
+
"revisionChanged": "待审版本已变化。请返回列表重新读取,核对新版与原意后再决定。",
|
|
114
|
+
"scopeUnknown": "未写明的适用范围或例外仍属未知,不应理解为适用于所有情况。",
|
|
115
|
+
"principleLimits": "查看原则层面的风险与已知缺口(不代表本次版本已验证)",
|
|
116
|
+
"reasonAndScope": "为什么这样建议,以及决定后果",
|
|
117
|
+
"materialUnavailable": "这次事项的材料暂不可用。",
|
|
118
|
+
"exactMaterialMissing": "尚未找到与这个审批对象和版本一致的正文,请先核对材料。",
|
|
119
|
+
"versionLock": "材料可以阅读。当前审批服务尚未保证提交的就是你看过的版本,因此这里暂不提供批准、拒绝或替换操作。",
|
|
120
|
+
"openPrinciple": "查看原则详情与现有记录",
|
|
121
|
+
"subjectDetails": "查看审批对象与版本标识",
|
|
122
|
+
"permissionsUnknown": "暂时无法确认审批权限。可以阅读材料,决定操作暂不可用。",
|
|
123
|
+
"taskDecision": "本轮方案决定",
|
|
124
|
+
"revisionDecision": "方案修订决定",
|
|
125
|
+
"validationReview": "行为验证评审",
|
|
126
|
+
"approvalMaterialReview": "审批材料核对",
|
|
127
|
+
"unknownReview": "事项类型待核对",
|
|
128
|
+
"promptReview": "提示原则审查",
|
|
129
|
+
"ruleReview": "执行方案审查",
|
|
130
|
+
"approvalSummary": "核对这次准备改变的行为、范围与具体版本。",
|
|
131
|
+
"bindingReview": "规则归属核对",
|
|
132
|
+
"bindingSummary": "确认这条规则应当落实哪条原则。",
|
|
133
|
+
"decisionRecorded": "服务已接收本次决定,列表正在按最新状态更新。",
|
|
134
|
+
"retry": "重新读取",
|
|
135
|
+
"runtimeRecords": "运行与历史记录",
|
|
136
|
+
"rulePromotion": "规则上线审查",
|
|
137
|
+
"promotionSummary": "这条规则仍在观察中。请核对安全证据,再决定继续观察、拒绝或上线。",
|
|
138
|
+
"openActivation": "查看规则运行与紧急控制",
|
|
139
|
+
"safetyAlert": "{{count}} 条规则已被安全隔离,请关注运行状态。",
|
|
140
|
+
"approvalsUnavailable": "部分审批材料未能读取,其他已知事项仍可查看。"
|
|
141
|
+
},
|
|
51
142
|
"ruleBinding": {
|
|
52
143
|
"title": "规则的正式原则归属",
|
|
53
144
|
"separate": "关联仅声明规则属于哪条原则;不批准规则、不启用规则,也不代替开发设计批准。",
|
|
@@ -522,13 +613,7 @@
|
|
|
522
613
|
},
|
|
523
614
|
"conclusion": {
|
|
524
615
|
"sectionTitle": "当前状态",
|
|
525
|
-
"
|
|
526
|
-
"pendingTotal": "待你处理 {{count}} 项",
|
|
527
|
-
"actionableUnlocked": "其中现在可操作 {{count}} 项",
|
|
528
|
-
"actionableLocked": "可操作对象 {{count}} 项——治理动作已锁定,原因见上方说明",
|
|
529
|
-
"countsNote": "口径:「待你处理」= 屏上可操作对象 + 决定动态中未上屏的待决定/被阻塞(需逐条关注)/需要恢复事项;同一对象只计一次;历史未知项只作说明,不计入。",
|
|
530
|
-
"pendingUncertain": "待处理数暂不可确认——决定动态不可用。",
|
|
531
|
-
"actionableWithoutTotal": "现在可操作 {{count}} 项",
|
|
616
|
+
"countsNote": "口径:计数为本页工作台列出的可处理事项数;被阻塞与需要恢复的事项在下方单独列出。",
|
|
532
617
|
"degradedLabel": "决定动态降级:"
|
|
533
618
|
},
|
|
534
619
|
"ownerAttention": {
|
|
@@ -756,6 +841,9 @@
|
|
|
756
841
|
"metricLatest": "最近更新",
|
|
757
842
|
"blockBasis": "形成依据",
|
|
758
843
|
"blockBasisFallback": "该原则由历史行为校准沉淀而来,详细来源尚未关联。",
|
|
844
|
+
"contextDisclosure": "形成过程与其他信息",
|
|
845
|
+
"libraryOverview": "原则概况",
|
|
846
|
+
"openDetails": "查看原则详情",
|
|
759
847
|
"blockImpact": "当前影响",
|
|
760
848
|
"blockImpactSeeDecisionView": "当前是否影响行为,以详情页「当前实际执行」为准(本卡片不据生命周期状态断言影响)。",
|
|
761
849
|
"blockImpactActive": "该原则正在影响智能体后续行为。",
|
|
@@ -812,6 +900,9 @@
|
|
|
812
900
|
"detail": {
|
|
813
901
|
"loading": "加载中…",
|
|
814
902
|
"backToList": "← 返回列表",
|
|
903
|
+
"formationDisclosure": "这条原则如何形成",
|
|
904
|
+
"fullRule": "展开完整原则正文",
|
|
905
|
+
"extraContext": "其他来源说明与回顾问题",
|
|
815
906
|
"conclusion": "结论",
|
|
816
907
|
"policyNote": "这是面向未来相似场景的行为政策,可批准、修改、拒绝或暂存。",
|
|
817
908
|
"modifyWording": "修改措辞",
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ApprovalsGroupedData } from '../../api.js';
|
|
2
|
+
export declare function ApprovalSubjectReading({ principleId, approvalId, artifactId, channel, lockedReason, targetHost, onResolved, revisionReview, revisionReviewUnavailable, allowedActions, budgetNotice, promptInjection, sourceUnlinked, showPrincipleLink }: {
|
|
3
|
+
principleId: string;
|
|
4
|
+
approvalId: string;
|
|
5
|
+
artifactId: string;
|
|
6
|
+
channel: string;
|
|
7
|
+
lockedReason?: string;
|
|
8
|
+
targetHost?: 'openclaw' | 'codex' | 'workbuddy';
|
|
9
|
+
onResolved?: (message: string) => void;
|
|
10
|
+
revisionReview?: {
|
|
11
|
+
artifactId?: string;
|
|
12
|
+
artifactDigest?: string;
|
|
13
|
+
oldStatement: string;
|
|
14
|
+
newStatement: string;
|
|
15
|
+
intentFields: Record<string, string>;
|
|
16
|
+
};
|
|
17
|
+
revisionReviewUnavailable?: boolean;
|
|
18
|
+
/** Backend-assessed actions for THIS approval; absent means no per-action gate (the guarded submit still applies). */
|
|
19
|
+
allowedActions?: readonly ('approve' | 'reject' | 'edit_approval')[];
|
|
20
|
+
budgetNotice?: string;
|
|
21
|
+
promptInjection?: ApprovalsGroupedData['promptInjection'];
|
|
22
|
+
sourceUnlinked?: boolean;
|
|
23
|
+
showPrincipleLink?: boolean;
|
|
24
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { Link, useLocation } from 'react-router-dom';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { fetchApprovalReview, fetchOwnerDecisionView, approveApproval, rejectApproval, editApproval, fetchAllActivations } from '../../api.js';
|
|
6
|
+
import { localizeApprovalWarning } from '../../utils/approval-warning-localization.js';
|
|
7
|
+
import { truncateByCodePoints } from '../../utils/reading-layout.js';
|
|
8
|
+
export function ApprovalSubjectReading({ principleId, approvalId, artifactId, channel, lockedReason, targetHost, onResolved, revisionReview, revisionReviewUnavailable, allowedActions, budgetNotice, promptInjection, sourceUnlinked, showPrincipleLink = true }) {
|
|
9
|
+
const { t } = useTranslation();
|
|
10
|
+
const location = useLocation();
|
|
11
|
+
const [review, setReview] = useState(null);
|
|
12
|
+
const [view, setView] = useState(null);
|
|
13
|
+
const [viewSettled, setViewSettled] = useState(false);
|
|
14
|
+
const [error, setError] = useState('');
|
|
15
|
+
const [resultText, setResultText] = useState('');
|
|
16
|
+
const [loading, setLoading] = useState(true);
|
|
17
|
+
const [busy, setBusy] = useState(false);
|
|
18
|
+
const [mode, setMode] = useState(null);
|
|
19
|
+
const [reason, setReason] = useState('');
|
|
20
|
+
const [newArtifact, setNewArtifact] = useState('');
|
|
21
|
+
const [intentReviewed, setIntentReviewed] = useState(false);
|
|
22
|
+
const [approveNote, setApproveNote] = useState('');
|
|
23
|
+
const epoch = useRef(0);
|
|
24
|
+
const read = useCallback(async () => {
|
|
25
|
+
const current = ++epoch.current;
|
|
26
|
+
setReview(null);
|
|
27
|
+
setLoading(true);
|
|
28
|
+
setIntentReviewed(false);
|
|
29
|
+
setError('');
|
|
30
|
+
const result = await fetchApprovalReview(approvalId);
|
|
31
|
+
if (current !== epoch.current)
|
|
32
|
+
return null;
|
|
33
|
+
setLoading(false);
|
|
34
|
+
if (result.success) {
|
|
35
|
+
setReview(result.data);
|
|
36
|
+
return result.data;
|
|
37
|
+
}
|
|
38
|
+
setError([result.error, result.nextAction].filter(Boolean).join(' '));
|
|
39
|
+
return null;
|
|
40
|
+
}, [approvalId]);
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
setError('');
|
|
43
|
+
setResultText('');
|
|
44
|
+
setView(null);
|
|
45
|
+
setViewSettled(false);
|
|
46
|
+
setMode(null);
|
|
47
|
+
setReason('');
|
|
48
|
+
setNewArtifact('');
|
|
49
|
+
setApproveNote('');
|
|
50
|
+
let current = true;
|
|
51
|
+
void read();
|
|
52
|
+
void fetchOwnerDecisionView(principleId).then(result => { if (current) {
|
|
53
|
+
setView(result.success ? result.data : null);
|
|
54
|
+
setViewSettled(true);
|
|
55
|
+
} });
|
|
56
|
+
return () => { current = false; epoch.current++; };
|
|
57
|
+
}, [principleId, approvalId, artifactId, channel, read]);
|
|
58
|
+
const narrative = (field) => field.status === 'known' ? field.value.map(item => item.text).join('\n\n') : field.reason.ownerText;
|
|
59
|
+
const subject = view?.decisionSubjects.find(item => item.key === approvalId && item.channel === channel
|
|
60
|
+
&& item.revisionArtifactId.status === 'known' && item.revisionArtifactId.value === review?.expectedReview.artifactId);
|
|
61
|
+
const materialBlocks = subject?.materialGates.find(gate => gate.actionSemantic === 'approve')?.requiredItems.filter(item => item.status !== 'satisfied') ?? [];
|
|
62
|
+
const revisionMatches = !revisionReview || (review?.expectedReview.artifactState === 'present' && review.expectedReview.artifactId === artifactId
|
|
63
|
+
&& revisionReview.artifactId === review.expectedReview.artifactId && revisionReview.artifactDigest === review.expectedReview.artifactDigest);
|
|
64
|
+
const approveReady = review?.approval.status === 'pending' && subject !== undefined && review.expectedReview.artifactState === 'present' && review.material.exactBody !== null && review.material.availability === 'available' && revisionMatches && !revisionReviewUnavailable && materialBlocks.length === 0;
|
|
65
|
+
const may = (semantic) => allowedActions === undefined || allowedActions.includes(semantic);
|
|
66
|
+
async function submit(action) {
|
|
67
|
+
if (!review || busy || lockedReason || review.approval.status !== 'pending')
|
|
68
|
+
return;
|
|
69
|
+
if (!may(action === 'approve' ? 'approve' : action === 'reject' ? 'reject' : 'edit_approval'))
|
|
70
|
+
return;
|
|
71
|
+
if (action === 'approve' && (!approveReady || (revisionReview && !intentReviewed)))
|
|
72
|
+
return;
|
|
73
|
+
if (action !== 'approve' && !reason.trim())
|
|
74
|
+
return;
|
|
75
|
+
if (action === 'edit' && !newArtifact.trim())
|
|
76
|
+
return;
|
|
77
|
+
const snapshot = review;
|
|
78
|
+
const current = epoch.current;
|
|
79
|
+
setBusy(true);
|
|
80
|
+
setError('');
|
|
81
|
+
setResultText('');
|
|
82
|
+
const result = action === 'approve' ? await approveApproval(approvalId, approveNote.trim() || undefined, { expectedReview: snapshot.expectedReview, targetHost,
|
|
83
|
+
...(revisionReview ? { intentReviewed, reviewedArtifactId: snapshot.expectedReview.artifactId } : {}) })
|
|
84
|
+
: action === 'reject' ? await rejectApproval(approvalId, reason.trim(), snapshot.expectedReview)
|
|
85
|
+
: await editApproval(approvalId, newArtifact.trim(), { reason: reason.trim(), expectedReview: snapshot.expectedReview });
|
|
86
|
+
if (current !== epoch.current)
|
|
87
|
+
return;
|
|
88
|
+
const fresh = await read();
|
|
89
|
+
setBusy(false);
|
|
90
|
+
if (!fresh) {
|
|
91
|
+
setError(t('pages.focus.workbench.resultUnknown'));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (fresh.approval.status === 'approved' || fresh.approval.status === 'rejected') {
|
|
95
|
+
const state = t(fresh.approval.status === 'approved' ? 'pages.focus.workbench.currentApproved' : 'pages.focus.workbench.currentRejected');
|
|
96
|
+
const ownOutcome = result.success && ((action === 'approve' && fresh.approval.status === 'approved') || (action === 'reject' && fresh.approval.status === 'rejected'));
|
|
97
|
+
const refused = !result.success && result.status !== undefined && result.status >= 400 && result.status < 500;
|
|
98
|
+
let message = ownOutcome ? t(fresh.approval.status === 'approved' ? 'pages.focus.workbench.approved' : 'pages.focus.workbench.rejected')
|
|
99
|
+
: refused ? t('pages.focus.workbench.refusedCurrentState', { state }) + ' ' + [result.error, result.nextAction].filter(Boolean).join(' ')
|
|
100
|
+
: t('pages.focus.workbench.currentStateConfirmed', { state });
|
|
101
|
+
if (fresh.approval.status === 'approved' && !result.success && !refused) {
|
|
102
|
+
const activations = await fetchAllActivations(targetHost);
|
|
103
|
+
if (epoch.current !== current + 1)
|
|
104
|
+
return;
|
|
105
|
+
const active = activations.success && activations.data.activations.some(item => item.artifactId === fresh.approval.artifactId && item.status === 'active');
|
|
106
|
+
message += ' ' + t(active ? 'pages.focus.workbench.runtimeCheckLink' : result.reason === 'activation_failed' ? 'pages.focus.workbench.activationFailed' : 'pages.focus.workbench.approvedRuntimeUnknown');
|
|
107
|
+
}
|
|
108
|
+
if (result.success && result.data.warning) {
|
|
109
|
+
const warning = localizeApprovalWarning(result.data.warning, t);
|
|
110
|
+
message += ` ${warning.title} ${warning.body ?? warning.detail}`;
|
|
111
|
+
}
|
|
112
|
+
setResultText(message);
|
|
113
|
+
onResolved?.(message);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (!result.success)
|
|
117
|
+
setError(t(result.status === undefined ? 'pages.focus.workbench.resultUnknown' : 'pages.focus.workbench.notRecorded') + ' ' + [result.error, result.nextAction].filter(Boolean).join(' '));
|
|
118
|
+
else if (action === 'edit') {
|
|
119
|
+
setResultText(t('pages.focus.workbench.replaced'));
|
|
120
|
+
setMode(null);
|
|
121
|
+
setReason('');
|
|
122
|
+
setNewArtifact('');
|
|
123
|
+
setApproveNote('');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const body = review?.material.exactBody;
|
|
127
|
+
const bodyText = body?.text ?? review?.material.statement ?? '';
|
|
128
|
+
return _jsxs("div", { className: "space-y-7", children: [_jsxs("section", { children: [_jsx("h3", { className: "mb-3 text-[15px] font-medium text-ink", children: t('pages.focus.workbench.why') }), sourceUnlinked && _jsx("p", { role: "alert", className: "mb-3 text-sm leading-relaxed text-amber", children: t('pages.focus.workbench.sourceUnlinked') }), _jsx("p", { className: "whitespace-pre-wrap text-[14px] leading-relaxed text-ink-2", children: view ? narrative(view.incidentSummary) : t('pages.focus.workbench.materialUnavailable') })] }), _jsxs("section", { children: [_jsx("h3", { className: "mb-3 text-[15px] font-medium text-ink", children: t('pages.focus.workbench.proposal') }), loading ? _jsx("p", { role: "status", className: "text-sm text-ink-3", children: t('pages.focus.workbench.loading') }) : _jsxs(_Fragment, { children: [bodyText ? _jsxs(_Fragment, { children: [_jsx("p", { className: "mb-2 text-[12px] text-ink-4", children: t(body?.kind === 'rule_code' ? 'pages.focus.workbench.codePreview' : 'pages.focus.workbench.textPreview') }), _jsx("p", { className: "whitespace-pre-wrap break-words text-[15px] leading-[1.85] text-ink", children: truncateByCodePoints(bodyText, 240) }), _jsxs("details", { className: "mt-4 rounded-[4px] border border-line px-4 py-3 text-[13px]", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t(body?.kind === 'rule_code' ? 'pages.focus.workbench.fullCode' : 'pages.focus.workbench.fullText') }), _jsx("div", { className: "mt-4 whitespace-pre-wrap break-words text-[15px] leading-[1.85] text-ink-2", children: bodyText })] })] }) : _jsx("p", { className: "text-sm text-amber", children: t('pages.focus.workbench.exactMaterialMissing') }), review?.material.scope.length ? _jsxs("div", { className: "mt-4", children: [_jsx("h4", { className: "text-sm font-medium text-ink", children: t('pages.focus.workbench.scope') }), _jsx("ul", { className: "mt-2 list-disc space-y-1 pl-5 text-[13px] leading-relaxed text-ink-2", children: review.material.scope.map((scope, index) => _jsx("li", { children: scope }, index)) })] }) : null, review?.material.rationale && _jsxs("details", { className: "mt-4 text-[13px] text-ink-3", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.focus.workbench.reasonAndScope') }), _jsx("p", { className: "mt-3 whitespace-pre-wrap leading-relaxed", children: review.material.rationale })] }), revisionReview && _jsxs("details", { className: "mt-4 text-[13px] leading-relaxed text-ink-2", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.focus.workbench.compare') }), _jsx("p", { className: "mt-3 whitespace-pre-wrap", children: revisionReview.oldStatement }), _jsx("p", { className: "mt-3 whitespace-pre-wrap", children: revisionReview.newStatement }), _jsx("dl", { className: "mt-3", children: Object.entries(revisionReview.intentFields).map(([key, value]) => _jsxs("div", { children: [_jsx("dt", { className: "font-medium", children: t(`pages.focus.intentField.${key}`, { defaultValue: key }) }), _jsx("dd", { children: value })] }, key)) })] })] })] }), _jsxs("section", { className: "border-t border-line pt-5", "data-testid": "approval-review-limits", children: [_jsx("h3", { className: "mb-3 text-[15px] font-medium text-ink", children: t('pages.focus.workbench.beforeDecision') }), viewSettled && !subject && _jsx("p", { className: "text-sm leading-relaxed text-amber", children: t('pages.focus.workbench.subjectUnconfirmed') }), review && _jsx("div", { className: "mb-3 text-sm leading-relaxed text-ink-2", "data-testid": "approval-exact-risks", children: review.material.risks?.length ? _jsx("ul", { className: "list-disc space-y-1 pl-5", children: review.material.risks.map((risk, index) => _jsx("li", { children: risk }, index)) }) : _jsx("p", { children: t(review.material.risks === null ? 'pages.focus.workbench.risksUnknown' : 'pages.focus.workbench.risksEmpty') }) }), materialBlocks.map(item => _jsx("p", { className: "mb-2 text-sm leading-relaxed text-amber", children: item.ownerText }, item.key)), !revisionMatches && _jsx("p", { role: "alert", className: "mb-2 text-sm text-amber", children: t('pages.focus.workbench.revisionChanged') }), revisionReviewUnavailable && _jsx("p", { role: "alert", className: "mb-2 text-sm text-amber", children: t('pages.focus.revisionReviewUnavailable') }), budgetNotice && channel === 'prompt' && _jsx("p", { role: "alert", className: "mb-2 text-sm leading-relaxed text-amber", "data-testid": `approve-queue-badge-${principleId}`, children: budgetNotice }), promptInjection && channel === 'prompt' && _jsxs("details", { className: "mb-3 text-[12px] leading-relaxed text-ink-3", "data-testid": `approve-queue-badge-scope-${principleId}`, children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.focus.workbench.budgetBasis') }), _jsxs("p", { className: "mt-2", children: [promptInjection.route === 'shared_render' ? t('pages.focus.promptScopeShared') : t('pages.focus.promptScopeList', { full: promptInjection.fullRenderChars !== undefined ? t('pages.focus.promptScopeFullRender', { count: promptInjection.fullRenderChars }) : '' }), promptInjection.hostKind ? ` · ${promptInjection.hostKind}` : ''] }), _jsx("p", { className: "mt-2", children: promptInjection.budgetCapacityStatus === 'confirmed' && promptInjection.budgetContextWindowTokens !== undefined
|
|
129
|
+
? promptInjection.budgetObservedAt !== undefined ? t('pages.focus.promptBudgetObservedNote', { source: t(promptInjection.budgetCapacitySource === 'pi_catalog_exact' ? 'pages.focus.budgetCapacitySourcePiCatalog' : promptInjection.budgetCapacitySource === 'operator_declared' ? 'pages.focus.budgetCapacitySourceOperatorDeclared' : 'pages.focus.budgetCapacitySourceHostWindow'), window: promptInjection.budgetContextWindowTokens, observedAt: promptInjection.budgetObservedAt })
|
|
130
|
+
: t('pages.focus.promptBudgetModelAwareNote', { window: promptInjection.budgetContextWindowTokens, sourceSuffix: promptInjection.budgetCapacitySource === 'operator_declared' ? t('pages.focus.budgetCapacitySourceOperatorDeclaredSuffix') : '' })
|
|
131
|
+
: promptInjection.budgetObservedAt !== undefined ? t('pages.focus.promptBudgetFallbackObservedNote', { reason: t(`pages.focus.budgetFallbackReason.${promptInjection.budgetFallbackReason}`, { defaultValue: promptInjection.budgetFallbackReason ?? 'unknown' }), observedAt: promptInjection.budgetObservedAt })
|
|
132
|
+
: t('pages.focus.promptBudgetFallbackNote', { reason: promptInjection.budgetFallbackReason ? t('pages.focus.promptBudgetFallbackReasonSuffix', { code: t(`pages.focus.budgetFallbackReason.${promptInjection.budgetFallbackReason}`, { defaultValue: promptInjection.budgetFallbackReason }) }) : '' }) })] }), review?.material.targetBehavior && _jsxs("div", { className: "mb-3 text-[14px] leading-relaxed text-ink-2", "data-testid": "approval-exact-target-behavior", children: [_jsx("h4", { className: "mb-1 font-medium", children: t('pages.focus.workbench.proposedBehavior') }), _jsx("p", { className: "whitespace-pre-wrap break-words", children: review.material.targetBehavior })] }), subject && _jsx("p", { className: "text-sm leading-relaxed text-ink-2", children: narrative(subject.decisionMaterial.consequence) }), _jsx("p", { className: "mt-2 text-[13px] leading-relaxed text-ink-3", children: t('pages.focus.workbench.scopeUnknown') }), view && _jsxs("details", { className: "mt-3 text-[13px] leading-relaxed text-ink-3", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.focus.workbench.principleLimits') }), _jsx("p", { className: "mt-3", children: view.nonApplicabilityOrUnknown.status === 'known' ? view.nonApplicabilityOrUnknown.value.map(item => item.text).join(' ') : view.nonApplicabilityOrUnknown.reason.ownerText }), _jsx("p", { className: "mt-2", children: view.risk.status === 'known' ? view.risk.value?.items.map(item => item.text).join(' ') || t('pages.principles.detail.ownerDecision.riskNotAssessed') : view.risk.reason?.ownerText }), _jsx("p", { className: "mt-2", children: view.uncertainty.status === 'known' ? view.uncertainty.value.map(item => item.text).join(' ') : view.uncertainty.reason.ownerText })] })] }), _jsxs("section", { className: "border-t border-line pt-5", children: [_jsx("h3", { className: "mb-3 text-[15px] font-medium text-ink", children: t('pages.focus.workbench.decision') }), lockedReason && _jsx("p", { role: "alert", className: "mb-3 text-[13px] leading-relaxed text-amber", children: lockedReason }), error && _jsx("p", { role: "alert", className: "mb-3 text-[13px] leading-relaxed text-amber", children: error }), resultText && _jsx("p", { role: "status", className: "mb-3 text-sm leading-relaxed text-ink", children: resultText }), !review && !loading && _jsx("button", { type: "button", className: "text-sm text-gov underline", onClick: () => { void read(); }, children: t('pages.focus.workbench.retry') }), review?.approval.status === 'pending' && _jsxs(_Fragment, { children: [_jsxs("details", { className: "mb-4 text-sm text-ink-3", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.focus.workbench.approvalNote') }), _jsxs("label", { className: "mt-3 block", children: [t('pages.focus.workbench.approvalNote'), _jsx("textarea", { value: approveNote, maxLength: 2000, onChange: event => setApproveNote(event.target.value), className: "mt-2 block w-full rounded border border-line bg-panel px-3 py-2" })] })] }), revisionReview && _jsxs("label", { className: "mb-4 block text-sm text-ink-2", children: [_jsx("input", { type: "checkbox", checked: intentReviewed, onChange: event => setIntentReviewed(event.target.checked), className: "mr-2" }), t('pages.focus.workbench.intentReviewed')] }), _jsx("p", { className: "mb-4 text-[12.5px] leading-relaxed text-ink-3", children: t(channel === 'code_tool_hook' ? 'pages.focus.workbench.ruleConsequence' : channel === 'defer_archive' ? 'pages.focus.workbench.archiveConsequence' : 'pages.focus.workbench.promptConsequence') }), _jsxs("div", { className: "flex flex-wrap gap-3", children: [_jsx("button", { type: "button", disabled: !approveReady || busy || Boolean(lockedReason) || !may('approve') || Boolean(revisionReview && !intentReviewed), "data-testid": `approve-btn-${principleId}`, onClick: () => { void submit('approve'); }, className: "rounded-[4px] border border-gov bg-gov px-4 py-2.5 text-[13px] text-paper disabled:opacity-50", children: t(channel === 'defer_archive' ? 'pages.focus.workbench.approveArchive' : 'pages.focus.workbench.approveOne') }), _jsx("button", { type: "button", disabled: busy || Boolean(lockedReason) || !may('reject'), onClick: () => setMode('reject'), className: "rounded-[4px] border border-line px-4 py-2.5 text-[13px] text-ink disabled:opacity-50", children: t('pages.focus.workbench.rejectOne') }), _jsx("button", { type: "button", "data-testid": `edit-btn-${principleId}`, disabled: busy || Boolean(lockedReason) || !may('edit_approval') || review.expectedReview.artifactState === 'missing', onClick: () => setMode('edit'), className: "text-[13px] text-gov underline disabled:opacity-50", children: t('pages.focus.workbench.replaceOne') })] }), mode && _jsxs("form", { className: "mt-4 space-y-3", onSubmit: event => { event.preventDefault(); void submit(mode); }, children: [mode === 'edit' && _jsxs("label", { className: "block text-sm text-ink-2", children: [t('pages.focus.workbench.replacementId'), _jsx("input", { value: newArtifact, onChange: event => setNewArtifact(event.target.value), className: "mt-2 block w-full rounded border border-line bg-panel px-3 py-2" })] }), _jsxs("label", { className: "block text-sm text-ink-2", children: [t('pages.focus.workbench.reason'), _jsx("textarea", { value: reason, maxLength: 2000, onChange: event => setReason(event.target.value), className: "mt-2 block w-full rounded border border-line bg-panel px-3 py-2" })] }), _jsx("button", { type: "submit", disabled: busy || !reason.trim() || (mode === 'edit' && !newArtifact.trim()), className: "rounded border border-line px-4 py-2 text-sm disabled:opacity-50", children: t('pages.focus.workbench.confirm') }), _jsx("button", { type: "button", onClick: () => setMode(null), className: "ml-3 text-sm text-ink-3", children: t('pages.focus.workbench.cancel') })] })] }), showPrincipleLink && _jsx(Link, { className: "mt-5 inline-block text-[13px] text-gov underline underline-offset-4", state: { returnTo: `/focus${location.search}` }, to: `/principles/${encodeURIComponent(principleId)}`, children: t('pages.focus.workbench.openPrinciple') }), _jsxs("details", { className: "mt-4 text-[12px] text-ink-4", children: [_jsx("summary", { className: "cursor-pointer", children: t('pages.focus.workbench.subjectDetails') }), _jsxs("dl", { className: "mt-2 break-all font-mono", children: [_jsxs("div", { children: ["approval: ", approvalId] }), _jsxs("div", { children: ["artifact: ", review?.expectedReview.artifactId ?? artifactId] }), _jsxs("div", { children: ["channel: ", review?.expectedReview.channel ?? channel] })] })] })] })] });
|
|
133
|
+
}
|
|
@@ -64,24 +64,16 @@ export declare function partitionInboxForDisplay<T extends {
|
|
|
64
64
|
};
|
|
65
65
|
/**
|
|
66
66
|
* SPEC-PD-UIUX-001 R2 (Owner review round 2): the conclusion strip's headline
|
|
67
|
-
*
|
|
68
|
-
*
|
|
67
|
+
* must never claim the system is clear when the inbox response cannot back
|
|
68
|
+
* that claim.
|
|
69
69
|
*
|
|
70
70
|
* Modes (display-only; no permission derivation):
|
|
71
71
|
* - 'unavailable' — inbox projection off (feature_disabled surfaces as
|
|
72
|
-
* ownerInbox===null with NO error) or the request failed
|
|
73
|
-
* CANNOT be confirmed; never allClear.
|
|
72
|
+
* ownerInbox===null with NO error) or the request failed.
|
|
74
73
|
* - 'degraded' — the inbox answered HTTP-success but carries
|
|
75
74
|
* `degraded: { reason, nextAction }` (e.g. principle ledger unreadable, the
|
|
76
|
-
* server then returns EMPTY groups):
|
|
77
|
-
*
|
|
78
|
-
* - 'ok' — counts are trusted.
|
|
79
|
-
*
|
|
80
|
-
* Counting (S2): displayTotal = actionableCount + inbox pending items NOT
|
|
81
|
-
* already represented by an on-screen action card. The association is the
|
|
82
|
-
* verified principleId field (partition.unmatchedDecisions are inbox items
|
|
83
|
-
* WITHOUT a card). One inbox item matching N cards of the same principle
|
|
84
|
-
* counts the N cards (each is a distinct decision); disjoint sets sum.
|
|
75
|
+
* server then returns EMPTY groups): the strip shows the server's reason.
|
|
76
|
+
* - 'ok' — the inbox answered without degradation.
|
|
85
77
|
*/
|
|
86
78
|
export declare function deriveConclusionDisplay(input: {
|
|
87
79
|
ownerInbox: {
|
|
@@ -94,26 +86,13 @@ export declare function deriveConclusionDisplay(input: {
|
|
|
94
86
|
};
|
|
95
87
|
} | null;
|
|
96
88
|
ownerInboxError: string | null;
|
|
97
|
-
partition: {
|
|
98
|
-
pendingTotal: number;
|
|
99
|
-
unmatchedDecisions: unknown[];
|
|
100
|
-
} | null;
|
|
101
|
-
actionableCount: number;
|
|
102
89
|
}): {
|
|
103
90
|
mode: 'ok' | 'degraded' | 'unavailable';
|
|
104
|
-
displayTotal: number;
|
|
105
|
-
actionableCount: number;
|
|
106
91
|
degraded?: {
|
|
107
92
|
reason: string;
|
|
108
93
|
nextAction: string;
|
|
109
94
|
};
|
|
110
95
|
};
|
|
111
|
-
/**
|
|
112
|
-
* SPEC-PD-UIUX-001 R2 (Owner review S3): EVERY conclusion branch must express
|
|
113
|
-
* governance readiness the same way — "actionable now" is only claimable when
|
|
114
|
-
* the actions are not locked. One matrix, one source.
|
|
115
|
-
*/
|
|
116
|
-
export declare function resolveConclusionActionableKey(mode: 'ok' | 'degraded' | 'unavailable', locked: boolean): string;
|
|
117
96
|
export interface OwnerConfigureCommand {
|
|
118
97
|
/** i18n key for the shell label, e.g. "pages.focus.experience.ownerGuide.cmdPowerShell". */
|
|
119
98
|
labelKey: string;
|