create-principles-disciple 1.121.9 → 1.122.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/console/dist/server/index.js +17 -0
- package/console/dist/server/models/OwnerDecisionConsoleModel.d.ts +39 -0
- package/console/dist/server/models/OwnerDecisionConsoleModel.js +233 -0
- package/console/dist/server/models/PrincipleClassifier.d.ts +3 -3
- package/console/dist/server/models/PrincipleClassifier.js +12 -5
- package/console/dist/server/models/PrinciplesConsoleModel.d.ts +1 -1
- package/console/dist/server/models/PrinciplesConsoleModel.js +2 -2
- package/console/dist/server/routes/failed-tasks.js +40 -2
- package/console/dist/server/routes/owner-decisions.d.ts +25 -0
- package/console/dist/server/routes/owner-decisions.js +186 -0
- package/console/dist/server/routes/principles.js +15 -7
- package/console/dist/server/routes/update.js +82 -14
- package/console/dist/server/utils/update-links.d.ts +38 -0
- package/console/dist/server/utils/update-links.js +71 -0
- package/console/dist/ui/api.d.ts +12 -1
- package/console/dist/ui/api.js +9 -2
- package/console/dist/ui/components/notifications/NotificationProvider.js +8 -2
- package/console/dist/ui/i18n/en.json +33 -1
- package/console/dist/ui/i18n/zh-CN.json +33 -1
- package/console/dist/ui/pages/failed-tasks/FailedTasksPage.js +5 -1
- package/console/dist/ui/pages/focus/FocusPage.js +19 -2
- package/console/dist/ui/pages/focus/OwnerDecisionCard.d.ts +6 -0
- package/console/dist/ui/pages/focus/OwnerDecisionCard.js +86 -0
- package/console/dist/ui/pages/principles/PrinciplesPage.js +11 -5
- package/console/dist/ui/utils/validators.d.ts +37 -0
- package/console/dist/ui/utils/validators.js +123 -0
- package/console/dist/web/assets/app.css +11 -0
- package/console/dist/web/assets/app.js +2740 -2350
- package/core/dist/runtime-v2/__tests__/evaluator-repair-loop.test.js +20 -0
- package/core/dist/runtime-v2/__tests__/evaluator-repair-loop.test.js.map +1 -1
- package/core/dist/runtime-v2/__tests__/rollout-reviewer-verdict-paths.test.js +26 -5
- package/core/dist/runtime-v2/__tests__/rollout-reviewer-verdict-paths.test.js.map +1 -1
- package/core/dist/runtime-v2/index.d.ts +7 -1
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +4 -0
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/evaluator-prompt-builder.test.js +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/internalization-transition-decision.test.js +15 -14
- package/core/dist/runtime-v2/internalization/__tests__/internalization-transition-decision.test.js.map +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision-architecture.test.d.ts +2 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision-architecture.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision-architecture.test.js +56 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision-architecture.test.js.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.d.ts +2 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.js +883 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-decision.test.js.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-override-resume.test.d.ts +2 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-override-resume.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-override-resume.test.js +328 -0
- package/core/dist/runtime-v2/internalization/__tests__/owner-override-resume.test.js.map +1 -0
- package/core/dist/runtime-v2/internalization/evaluator-output.d.ts +63 -2
- package/core/dist/runtime-v2/internalization/evaluator-output.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-output.js +139 -2
- package/core/dist/runtime-v2/internalization/evaluator-output.js.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.d.ts +54 -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 +49 -2
- package/core/dist/runtime-v2/internalization/evaluator-prompt-builder.js.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts +34 -0
- package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.js +295 -12
- package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/internalization-orchestrator.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/internalization-orchestrator.js +10 -54
- package/core/dist/runtime-v2/internalization/internalization-orchestrator.js.map +1 -1
- package/core/dist/runtime-v2/internalization/internalization-transition-decision.d.ts +7 -1
- package/core/dist/runtime-v2/internalization/internalization-transition-decision.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/internalization-transition-decision.js +9 -2
- package/core/dist/runtime-v2/internalization/internalization-transition-decision.js.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-resolution-service.d.ts +89 -0
- package/core/dist/runtime-v2/internalization/owner-resolution-service.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/owner-resolution-service.js +272 -0
- package/core/dist/runtime-v2/internalization/owner-resolution-service.js.map +1 -0
- package/core/dist/runtime-v2/internalization/owner-retry.d.ts +12 -0
- package/core/dist/runtime-v2/internalization/owner-retry.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-retry.js +23 -0
- package/core/dist/runtime-v2/internalization/owner-retry.js.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-review.d.ts +195 -0
- package/core/dist/runtime-v2/internalization/owner-review.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/owner-review.js +470 -0
- package/core/dist/runtime-v2/internalization/owner-review.js.map +1 -0
- package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts +5 -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 +68 -0
- package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/pitask-metadata.js +104 -0
- package/core/dist/runtime-v2/internalization/pitask-metadata.js.map +1 -1
- package/core/dist/runtime-v2/internalization/revision-reopen.d.ts +56 -0
- package/core/dist/runtime-v2/internalization/revision-reopen.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/revision-reopen.js +144 -0
- package/core/dist/runtime-v2/internalization/revision-reopen.js.map +1 -0
- package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.d.ts +12 -0
- package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.js +145 -14
- package/core/dist/runtime-v2/internalization/rollout-reviewer-runner.js.map +1 -1
- package/core/dist/runtime-v2/store/context/sqlite-context-assembler.test.js +1 -0
- package/core/dist/runtime-v2/store/context/sqlite-context-assembler.test.js.map +1 -1
- package/core/dist/runtime-v2/store/runtime-state-manager.d.ts +6 -0
- package/core/dist/runtime-v2/store/runtime-state-manager.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/runtime-state-manager.js +9 -0
- package/core/dist/runtime-v2/store/runtime-state-manager.js.map +1 -1
- package/core/dist/runtime-v2/store/task/__tests__/sqlite-task-store-cas.test.d.ts +2 -0
- package/core/dist/runtime-v2/store/task/__tests__/sqlite-task-store-cas.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/store/task/__tests__/sqlite-task-store-cas.test.js +78 -0
- package/core/dist/runtime-v2/store/task/__tests__/sqlite-task-store-cas.test.js.map +1 -0
- package/core/dist/runtime-v2/store/task/memory-task-store.d.ts +1 -0
- package/core/dist/runtime-v2/store/task/memory-task-store.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/task/memory-task-store.js +8 -0
- package/core/dist/runtime-v2/store/task/memory-task-store.js.map +1 -1
- package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts +1 -0
- package/core/dist/runtime-v2/store/task/sqlite-task-store.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/task/sqlite-task-store.js +52 -0
- package/core/dist/runtime-v2/store/task/sqlite-task-store.js.map +1 -1
- package/core/dist/runtime-v2/store/task/task-store.d.ts +9 -0
- package/core/dist/runtime-v2/store/task/task-store.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/workspace-isolation.test.js +1 -0
- package/core/dist/runtime-v2/store/workspace-isolation.test.js.map +1 -1
- package/core/package.json +1 -1
- package/host-runtime/package.json +1 -1
- package/install-layout/package.json +3 -7
- package/package.json +2 -2
- package/pd-cli/dist/commands/runtime-internalization-retry.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-internalization-retry.js +14 -0
- package/pd-cli/dist/commands/runtime-internalization-retry.js.map +1 -1
- package/pd-cli/package.json +1 -1
- package/plugin/dist/bundle.js +580 -569
- package/plugin/dist/governance-audit.js +153 -153
- package/plugin/dist/openclaw.plugin.json +1 -1
- package/plugin/dist/rulehost-evidence.js +180 -180
- package/plugin/openclaw.plugin.json +1 -1
- package/plugin/package.json +1 -1
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { createRuntimeStateHandle, applyOwnerResolution, } from '@principles/core/runtime-v2';
|
|
2
|
+
import { OwnerDecisionConsoleModel } from '../models/OwnerDecisionConsoleModel.js';
|
|
3
|
+
import { sendSuccess, sendError, sendMethodNotAllowed, sendBadRequest } from '../utils/response.js';
|
|
4
|
+
import { readBody } from '../utils/request.js';
|
|
5
|
+
const models = new Map();
|
|
6
|
+
function getModel(workspaceDir) {
|
|
7
|
+
let model = models.get(workspaceDir);
|
|
8
|
+
if (!model) {
|
|
9
|
+
model = new OwnerDecisionConsoleModel(workspaceDir);
|
|
10
|
+
models.set(workspaceDir, model);
|
|
11
|
+
}
|
|
12
|
+
return model;
|
|
13
|
+
}
|
|
14
|
+
const RESOLVE_ACTIONS = new Set(['accept_current', 'revise_once', 'reject_current']);
|
|
15
|
+
/** rc-1/rc-2/rc-3: body 按未知值逐字段校验,非法即 400 fail-loud。 */
|
|
16
|
+
function parseResolveBody(raw) {
|
|
17
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
18
|
+
return { ok: false, error: 'Request body must be a JSON object.' };
|
|
19
|
+
}
|
|
20
|
+
const b = raw;
|
|
21
|
+
if (typeof b.action !== 'string' || !RESOLVE_ACTIONS.has(b.action)) {
|
|
22
|
+
return { ok: false, error: `action must be one of accept_current/revise_once/reject_current, got ${String(b.action)}` };
|
|
23
|
+
}
|
|
24
|
+
for (const field of ['reviewKey', 'expectedSourceRunId', 'expectedSourceArtifactId', 'expectedSourceArtifactHash']) {
|
|
25
|
+
const v = b[field];
|
|
26
|
+
if (typeof v !== 'string' || v.trim() === '') {
|
|
27
|
+
return { ok: false, error: `${field} must be a non-empty string.` };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (typeof b.expectedRevisionEpoch !== 'number' || !Number.isInteger(b.expectedRevisionEpoch) || b.expectedRevisionEpoch < 0) {
|
|
31
|
+
return { ok: false, error: 'expectedRevisionEpoch must be a non-negative integer.' };
|
|
32
|
+
}
|
|
33
|
+
if (b.ownerInstruction !== undefined && b.ownerInstruction !== null
|
|
34
|
+
&& (typeof b.ownerInstruction !== 'string' || b.ownerInstruction.length > 600)) {
|
|
35
|
+
return { ok: false, error: 'ownerInstruction must be a string of at most 600 characters (or null).' };
|
|
36
|
+
}
|
|
37
|
+
const { reviewKey } = b;
|
|
38
|
+
const { expectedSourceRunId } = b;
|
|
39
|
+
const { expectedSourceArtifactId } = b;
|
|
40
|
+
const { expectedSourceArtifactHash } = b;
|
|
41
|
+
if (typeof reviewKey !== 'string' || typeof expectedSourceRunId !== 'string'
|
|
42
|
+
|| typeof expectedSourceArtifactId !== 'string' || typeof expectedSourceArtifactHash !== 'string') {
|
|
43
|
+
return { ok: false, error: 'unreachable: string fields validated above' };
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
ok: true,
|
|
47
|
+
body: {
|
|
48
|
+
action: b.action,
|
|
49
|
+
reviewKey,
|
|
50
|
+
expectedRevisionEpoch: b.expectedRevisionEpoch,
|
|
51
|
+
expectedSourceRunId,
|
|
52
|
+
expectedSourceArtifactId,
|
|
53
|
+
expectedSourceArtifactHash,
|
|
54
|
+
...(typeof b.ownerInstruction === 'string' ? { ownerInstruction: b.ownerInstruction } : {}),
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** OwnerResolutionOutcome → HTTP 响应映射(cli-6: 结构化 reason + nextAction)。 */
|
|
59
|
+
function sendOutcome(res, outcome) {
|
|
60
|
+
switch (outcome.status) {
|
|
61
|
+
case 'resolved':
|
|
62
|
+
sendSuccess(res, {
|
|
63
|
+
status: 'resolved',
|
|
64
|
+
resolutionId: outcome.resolutionId,
|
|
65
|
+
reviewKey: outcome.reviewKey,
|
|
66
|
+
action: outcome.action,
|
|
67
|
+
applied: outcome.applied,
|
|
68
|
+
...(outcome.effectiveDecision !== undefined ? { effectiveDecision: outcome.effectiveDecision } : {}),
|
|
69
|
+
...(outcome.targetTaskId !== undefined ? { targetTaskId: outcome.targetTaskId } : {}),
|
|
70
|
+
runnerWillApply: outcome.runnerWillApply,
|
|
71
|
+
nextAction: outcome.applied
|
|
72
|
+
? 'Resolution applied. Refresh the governance focus.'
|
|
73
|
+
: 'Resolution recorded durably; the pipeline runner will apply it without re-invoking the LLM. Refresh the governance focus.',
|
|
74
|
+
});
|
|
75
|
+
return;
|
|
76
|
+
case 'not_found':
|
|
77
|
+
sendError(res, 404, 'task_not_found', 'Owner decision task not found.');
|
|
78
|
+
return;
|
|
79
|
+
case 'metadata_invalid':
|
|
80
|
+
sendError(res, 409, 'metadata_invalid', 'Task metadata failed PI hydration; refusing to resolve.', {
|
|
81
|
+
nextAction: 'Inspect the task metadata: pd runtime internalization integrity --json',
|
|
82
|
+
});
|
|
83
|
+
return;
|
|
84
|
+
case 'not_decision_capable':
|
|
85
|
+
sendError(res, 409, 'not_decision_capable', 'This task is not currently decision-capable.', {
|
|
86
|
+
nextAction: 'Refresh the governance focus — the decision facts may have changed.',
|
|
87
|
+
blockers: outcome.blockers,
|
|
88
|
+
});
|
|
89
|
+
return;
|
|
90
|
+
case 'stale_owner_decision':
|
|
91
|
+
sendError(res, 409, 'stale_owner_decision', 'The durable decision facts changed since this item was rendered. The decision was NOT applied.', {
|
|
92
|
+
nextAction: 'Refresh the governance focus and decide again on the current version.',
|
|
93
|
+
});
|
|
94
|
+
return;
|
|
95
|
+
case 'already_resolved':
|
|
96
|
+
sendError(res, 409, 'already_resolved', `This decision was already resolved with action '${outcome.existingAction}'.`, {
|
|
97
|
+
nextAction: 'Refresh the governance focus.',
|
|
98
|
+
existingAction: outcome.existingAction,
|
|
99
|
+
});
|
|
100
|
+
return;
|
|
101
|
+
case 'revise_target_unresolved':
|
|
102
|
+
sendError(res, 409, 'revise_target_unresolved', 'The revision target task could not be resolved from the lineage.', {
|
|
103
|
+
nextAction: 'Inspect the task lineage; this is a recovery-class failure.',
|
|
104
|
+
});
|
|
105
|
+
return;
|
|
106
|
+
case 'revise_reopen_failed':
|
|
107
|
+
sendError(res, 409, 'revise_reopen_failed', `Reopening the revision target failed: ${outcome.reason}`, {
|
|
108
|
+
nextAction: 'Retry after the target task is no longer in flight, or inspect the task states.',
|
|
109
|
+
reason: outcome.reason,
|
|
110
|
+
});
|
|
111
|
+
return;
|
|
112
|
+
case 'cas_conflict':
|
|
113
|
+
sendError(res, 409, 'cas_conflict', 'Concurrent write detected while recording the resolution.', {
|
|
114
|
+
nextAction: 'Retry the request — the operation is idempotent.',
|
|
115
|
+
});
|
|
116
|
+
return;
|
|
117
|
+
default: {
|
|
118
|
+
// exhaustive guard
|
|
119
|
+
const exhaustive = outcome;
|
|
120
|
+
sendError(res, 500, 'owner_resolution_error', `Unhandled outcome: ${JSON.stringify(exhaustive)}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export async function handleOwnerDecisionsRoute(req, res, ctx) {
|
|
125
|
+
const { workspaceDir, subPath, ownerIdentity } = ctx;
|
|
126
|
+
const method = req.method ?? 'GET';
|
|
127
|
+
// GET /api/v1/governance/owner-decisions — list
|
|
128
|
+
if (subPath === '' || subPath === '/') {
|
|
129
|
+
if (method !== 'GET') {
|
|
130
|
+
sendMethodNotAllowed(res);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const result = await getModel(workspaceDir).listOwnerDecisionItems();
|
|
135
|
+
sendSuccess(res, result);
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
139
|
+
sendError(res, 500, 'owner_decisions_list_error', message, {
|
|
140
|
+
nextAction: 'Inspect the workspace state (.pd/state.db) and retry.',
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// POST /api/v1/governance/owner-decisions/:taskId/resolve
|
|
146
|
+
const resolveMatch = /^\/([^/]+)\/resolve$/.exec(subPath);
|
|
147
|
+
if (resolveMatch) {
|
|
148
|
+
if (method !== 'POST') {
|
|
149
|
+
sendMethodNotAllowed(res);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- match group is always present
|
|
153
|
+
const taskId = decodeURIComponent(resolveMatch[1]);
|
|
154
|
+
let body;
|
|
155
|
+
try {
|
|
156
|
+
const raw = await readBody(req);
|
|
157
|
+
body = raw.trim() === '' ? {} : JSON.parse(raw);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
sendBadRequest(res, 'Request body must be valid JSON.');
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const parsed = parseResolveBody(body);
|
|
164
|
+
if (!parsed.ok) {
|
|
165
|
+
sendBadRequest(res, parsed.error);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
// 应用走 core 单一服务(writable RuntimeStateManager handle)
|
|
169
|
+
const handle = await createRuntimeStateHandle({ workspaceDir });
|
|
170
|
+
try {
|
|
171
|
+
const outcome = await applyOwnerResolution({ stateManager: handle.stateManager }, { taskId, request: parsed.body, identity: ownerIdentity });
|
|
172
|
+
sendOutcome(res, outcome);
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
176
|
+
sendError(res, 500, 'owner_resolution_error', message, {
|
|
177
|
+
nextAction: 'Inspect the workspace state (.pd/state.db) and retry.',
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
await handle.close();
|
|
182
|
+
}
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
sendError(res, 404, 'not_found', `Route /api/v1/governance/owner-decisions${subPath} not found`);
|
|
186
|
+
}
|
|
@@ -42,7 +42,7 @@ async function getDecidedPrincipleIds(workspaceDir) {
|
|
|
42
42
|
// Runtime V2 uses <workspace>/.pd/state.db (NOT .state/state.db)
|
|
43
43
|
const dbPath = path.join(workspaceDir, '.pd', 'state.db');
|
|
44
44
|
if (!fs.existsSync(dbPath)) {
|
|
45
|
-
return { ids: new Set(), unavailableReason: 'approval_db_not_found' };
|
|
45
|
+
return { ids: new Set(), pendingIds: new Set(), unavailableReason: 'approval_db_not_found' };
|
|
46
46
|
}
|
|
47
47
|
let closeFn = null;
|
|
48
48
|
try {
|
|
@@ -51,25 +51,33 @@ async function getDecidedPrincipleIds(workspaceDir) {
|
|
|
51
51
|
closeFn = () => conn.close();
|
|
52
52
|
const db = conn.getDb();
|
|
53
53
|
// Join approvals with pi_artifacts to find decided principle IDs
|
|
54
|
-
|
|
54
|
+
// PRI-629: also resolve PENDING approvals — a pending approval is a real
|
|
55
|
+
// Owner decision (owner_actionable), unlike candidate lifecycle (in_pipeline).
|
|
56
|
+
const rows = db.prepare("SELECT DISTINCT a.source_principle_id, ap.status " +
|
|
55
57
|
"FROM approvals ap " +
|
|
56
58
|
"JOIN pi_artifacts a ON a.artifact_id = ap.artifact_id " +
|
|
57
|
-
"WHERE ap.status IN ('approved', 'rejected') " +
|
|
59
|
+
"WHERE ap.status IN ('approved', 'rejected', 'pending') " +
|
|
58
60
|
"AND a.source_principle_id IS NOT NULL").all();
|
|
59
61
|
// EP-01 Rule 1: treat DB rows as unknown; validate before use
|
|
60
62
|
// EP-01 Rule 2: no `as` cast — use getOwnStringField instead
|
|
61
63
|
const ids = new Set();
|
|
64
|
+
const pendingIds = new Set();
|
|
62
65
|
for (const row of rows) {
|
|
63
66
|
const val = getOwnStringField(row, 'source_principle_id');
|
|
64
|
-
if (val
|
|
67
|
+
if (val === undefined)
|
|
68
|
+
continue;
|
|
69
|
+
if (getOwnStringField(row, 'status') === 'pending') {
|
|
70
|
+
pendingIds.add(val);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
65
73
|
ids.add(val);
|
|
66
74
|
}
|
|
67
75
|
}
|
|
68
|
-
return { ids };
|
|
76
|
+
return { ids, pendingIds };
|
|
69
77
|
}
|
|
70
78
|
catch (err) {
|
|
71
79
|
const message = err instanceof Error ? err.message : String(err);
|
|
72
|
-
return { ids: new Set(), unavailableReason: `approval_query_failed: ${message}` };
|
|
80
|
+
return { ids: new Set(), pendingIds: new Set(), unavailableReason: `approval_query_failed: ${message}` };
|
|
73
81
|
}
|
|
74
82
|
finally {
|
|
75
83
|
closeFn?.();
|
|
@@ -197,7 +205,7 @@ export async function handlePrinciplesRoute({ req, res, workspaceDir, subPath, f
|
|
|
197
205
|
const VALID_FILTERS = new Set(['all', 'actionable']);
|
|
198
206
|
const filter = (filterRaw !== null && VALID_FILTERS.has(filterRaw) ? filterRaw : 'actionable');
|
|
199
207
|
const decidedResult = await getDecidedPrincipleIds(workspaceDir);
|
|
200
|
-
const result = await model.listPrinciples(filter, decidedResult.ids);
|
|
208
|
+
const result = await model.listPrinciples(filter, decidedResult.ids, decidedResult.pendingIds);
|
|
201
209
|
// Surface approval cross-check unavailability to the UI (ERR-002)
|
|
202
210
|
if (decidedResult.unavailableReason) {
|
|
203
211
|
result.approvalCrossCheckUnavailable = decidedResult.unavailableReason;
|
|
@@ -9,6 +9,7 @@ import { checkOpenClawGateway, stopOpenClawGateway, restartOpenClawGateway, } fr
|
|
|
9
9
|
import { migrateLegacyExtensionBackups, reservePdBackupDestination, resolvePdBackupsRoot, } from '../utils/pd-backups.js';
|
|
10
10
|
import { ActivationCompatibilityReadModel } from '@principles/core/runtime-v2';
|
|
11
11
|
import { getInstallLayoutPaths, resolveInstallLayout } from '@principles/install-layout';
|
|
12
|
+
import { collectFileDepLinkSpecs } from '../utils/update-links.js';
|
|
12
13
|
/**
|
|
13
14
|
* Legacy rule contract preflight (2026-08-19): refuse to swap the runtime
|
|
14
15
|
* while an ACTIVE owner-approved rule still depends on a RuleHost contract
|
|
@@ -724,22 +725,35 @@ function depsMeaningfullyChanged(oldDeps, newDeps) {
|
|
|
724
725
|
return aKeys.some((k, i) => bKeys[i] !== k || a[k] !== b[k]);
|
|
725
726
|
}
|
|
726
727
|
/**
|
|
727
|
-
* Create the node_modules
|
|
728
|
-
*
|
|
728
|
+
* Create the node_modules resolution links for the installed components, if
|
|
729
|
+
* missing.
|
|
729
730
|
*
|
|
730
731
|
* Mirrors installer.ts syncPdCli: junction on Windows (no elevation needed),
|
|
731
732
|
* relative symlink elsewhere. Fresh installs get these links via npm install
|
|
732
|
-
* (the bundled package.json rewrites
|
|
733
|
-
* full update deliberately skips npm install, so it must create them
|
|
734
|
-
* Without
|
|
735
|
-
*
|
|
736
|
-
*
|
|
737
|
-
*
|
|
733
|
+
* (the bundled package.json rewrites internal deps to file:../<component>);
|
|
734
|
+
* the full update deliberately skips npm install, so it must create them
|
|
735
|
+
* itself. Without a link, updated dists crash at startup with
|
|
736
|
+
* ERR_MODULE_NOT_FOUND (host-runtime: PRI-561, 2026-08-21).
|
|
737
|
+
*
|
|
738
|
+
* Two sources, both fail-closed on creation errors:
|
|
739
|
+
* 1. the explicit link list below (known-critical links: a missing one
|
|
740
|
+
* means the updated console cannot start);
|
|
741
|
+
* 2. a data-driven pass that derives links from the STAGED manifests —
|
|
742
|
+
* the freshly extracted release trees under tempDir. Their `file:`
|
|
743
|
+
* declarations are the authoritative list of links the updated tree
|
|
744
|
+
* needs. Reading the deployed (pre-update) manifests instead would
|
|
745
|
+
* miss every dependency this release newly introduced: the running
|
|
746
|
+
* console executes update logic from its own dist (one generation
|
|
747
|
+
* behind the components it installs) — observed 2026-08-29 when the
|
|
748
|
+
* 1.221.2 console updated hosts to 1.222.5 but could not know about
|
|
749
|
+
* the newly introduced install-layout component, leaving
|
|
750
|
+
* host-runtime/node_modules/@principles/install-layout missing and
|
|
751
|
+
* every pd-cli runtime command failing with ERR_MODULE_NOT_FOUND.
|
|
738
752
|
*
|
|
739
753
|
* Returns undefined on success, or an error message (rc-9: observable, never
|
|
740
754
|
* silent — a missing link means the updated console cannot start).
|
|
741
755
|
*/
|
|
742
|
-
function ensureRuntimeResolutionLinks(layout) {
|
|
756
|
+
function ensureRuntimeResolutionLinks(layout, tempDir) {
|
|
743
757
|
const links = [
|
|
744
758
|
{ linkPath: path.join(layout.consoleDir, 'node_modules', '@principles', 'host-runtime'), target: layout.hostRuntimeDir },
|
|
745
759
|
{ linkPath: path.join(layout.pdCliDir, 'node_modules', '@principles', 'host-runtime'), target: layout.hostRuntimeDir },
|
|
@@ -750,11 +764,43 @@ function ensureRuntimeResolutionLinks(layout) {
|
|
|
750
764
|
{ linkPath: path.join(layout.hostRuntimeDir, 'node_modules', '@principles', 'install-layout'), target: layout.installLayoutDir },
|
|
751
765
|
{ linkPath: path.join(layout.consoleDir, 'node_modules', 'principles-disciple'), target: layout.pluginDir },
|
|
752
766
|
];
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
767
|
+
// Data-driven pass: derive links from the STAGED component manifests (see
|
|
768
|
+
// the comment above — staged, never the deployed pre-update manifests).
|
|
769
|
+
const stagedComponents = [
|
|
770
|
+
{ manifestDir: path.join(tempDir, 'console'), deployedDir: layout.consoleDir },
|
|
771
|
+
{ manifestDir: path.join(tempDir, 'pd-cli'), deployedDir: layout.pdCliDir },
|
|
772
|
+
{ manifestDir: path.join(tempDir, 'host-runtime'), deployedDir: layout.hostRuntimeDir },
|
|
773
|
+
{ manifestDir: path.join(tempDir, 'install-layout'), deployedDir: layout.installLayoutDir },
|
|
774
|
+
{ manifestDir: path.join(tempDir, 'core'), deployedDir: layout.coreDir },
|
|
775
|
+
{ manifestDir: path.join(tempDir, 'plugin'), deployedDir: layout.pluginDir },
|
|
776
|
+
];
|
|
777
|
+
const readStagedDependencies = (manifestDir) => {
|
|
778
|
+
try {
|
|
779
|
+
const pkgPath = path.join(manifestDir, 'package.json');
|
|
780
|
+
if (!fs.existsSync(pkgPath))
|
|
781
|
+
return {};
|
|
782
|
+
const parsed = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
783
|
+
if (!isRecord(parsed) || !isRecord(parsed.dependencies))
|
|
784
|
+
return {};
|
|
785
|
+
const out = {};
|
|
786
|
+
for (const [name, ref] of Object.entries(parsed.dependencies)) {
|
|
787
|
+
if (typeof ref === 'string')
|
|
788
|
+
out[name] = ref;
|
|
789
|
+
}
|
|
790
|
+
return out;
|
|
791
|
+
}
|
|
792
|
+
catch {
|
|
793
|
+
// rc-9: an unreadable staged manifest degrades to "no derived links" —
|
|
794
|
+
// the explicit list above still covers the known-critical links.
|
|
795
|
+
return {};
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
const fileDepSpecs = collectFileDepLinkSpecs(stagedComponents, readStagedDependencies);
|
|
799
|
+
const createResolutionLink = (linkPath, target) => {
|
|
800
|
+
// Idempotent: never overwrite an existing link or directory (fresh
|
|
801
|
+
// installs have npm-created real dirs in these slots).
|
|
756
802
|
if (fs.existsSync(linkPath))
|
|
757
|
-
|
|
803
|
+
return undefined;
|
|
758
804
|
try {
|
|
759
805
|
fs.mkdirSync(path.dirname(linkPath), { recursive: true });
|
|
760
806
|
if (process.platform === 'win32') {
|
|
@@ -763,10 +809,32 @@ function ensureRuntimeResolutionLinks(layout) {
|
|
|
763
809
|
else {
|
|
764
810
|
fs.symlinkSync(path.relative(path.dirname(linkPath), target), linkPath, 'dir');
|
|
765
811
|
}
|
|
812
|
+
return undefined;
|
|
766
813
|
}
|
|
767
814
|
catch (error) {
|
|
768
815
|
return `Failed to create runtime resolution link at ${linkPath}: ${error instanceof Error ? error.message : String(error)}`;
|
|
769
816
|
}
|
|
817
|
+
};
|
|
818
|
+
// Pass 1 — explicit known-critical links, fail-closed BEFORE any byte is
|
|
819
|
+
// swapped (a link-creation failure aborts with the installed packages
|
|
820
|
+
// untouched: the PRI-561 ordering contract).
|
|
821
|
+
for (const { linkPath, target } of links) {
|
|
822
|
+
if (!fs.existsSync(target))
|
|
823
|
+
continue;
|
|
824
|
+
const error = createResolutionLink(linkPath, target);
|
|
825
|
+
if (error)
|
|
826
|
+
return error;
|
|
827
|
+
}
|
|
828
|
+
// Pass 2 — data-driven derived links. Their deployed target dir may be
|
|
829
|
+
// created by the copy steps that follow (a brand-new component's dir does
|
|
830
|
+
// not exist yet); each staged target's existence was already proven by the
|
|
831
|
+
// extraction, and the copies below run unconditionally.
|
|
832
|
+
for (const spec of fileDepSpecs) {
|
|
833
|
+
if (fs.existsSync(spec.linkPath))
|
|
834
|
+
continue;
|
|
835
|
+
const error = createResolutionLink(spec.linkPath, spec.target);
|
|
836
|
+
if (error)
|
|
837
|
+
return error;
|
|
770
838
|
}
|
|
771
839
|
return undefined;
|
|
772
840
|
}
|
|
@@ -935,7 +1003,7 @@ async function doInlineFullUpdate(workspaceDir) {
|
|
|
935
1003
|
if (fs.existsSync(path.join(installLayoutSrc, 'package.json')) && fs.existsSync(path.join(installLayoutSrc, 'dist'))) {
|
|
936
1004
|
copyDirRecursive(installLayoutSrc, layout.installLayoutDir, SKIP_DIRS);
|
|
937
1005
|
}
|
|
938
|
-
const linkError = ensureRuntimeResolutionLinks(layout);
|
|
1006
|
+
const linkError = ensureRuntimeResolutionLinks(layout, tempDir);
|
|
939
1007
|
if (linkError) {
|
|
940
1008
|
appendUpdateHistory(workspaceDir, {
|
|
941
1009
|
fromVersion,
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type FileDepLinkSpec = {
|
|
2
|
+
linkPath: string;
|
|
3
|
+
/** Deployed layout dir the link points at; may be created by the copy
|
|
4
|
+
* steps that run after derivation (see stagedTargetExists). */
|
|
5
|
+
target: string;
|
|
6
|
+
/** The staged sibling dir proving this component ships in this release. */
|
|
7
|
+
stagedTarget: string;
|
|
8
|
+
};
|
|
9
|
+
export type StagedComponent = {
|
|
10
|
+
/** Freshly extracted component dir under the update temp dir. */
|
|
11
|
+
manifestDir: string;
|
|
12
|
+
/** Deployed layout dir this component is (or will be) installed into. */
|
|
13
|
+
deployedDir: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Derive the node_modules link specs implied by each staged component's
|
|
17
|
+
* declared `file:` dependencies.
|
|
18
|
+
*
|
|
19
|
+
* - `readDependencies(manifestDir)` returns the component's dependency map
|
|
20
|
+
* (name -> version-or-ref); return {} for unreadable/missing manifests.
|
|
21
|
+
* - A `file:` ref is resolved against its staged manifest dir to find the
|
|
22
|
+
* staged sibling target; the deployed link target is taken from the same
|
|
23
|
+
* staged->deployed pairing the caller provides — component identity is
|
|
24
|
+
* the staged directory itself (normalized absolute path), NOT the
|
|
25
|
+
* deployed directory's basename. In the legacy layout the deployed
|
|
26
|
+
* plugin dir is named `principles-disciple` while the staged one is
|
|
27
|
+
* `plugin`, so a basename lookup would silently drop the derived link
|
|
28
|
+
* (review P1, PR #1457).
|
|
29
|
+
* - Deps whose staged target is not one of the layout components (e.g.
|
|
30
|
+
* @principles/codex-adapter — a separate host install, not part of this
|
|
31
|
+
* layout) are skipped.
|
|
32
|
+
*
|
|
33
|
+
* Note: `target` (the deployed dir) may not exist yet at derivation time —
|
|
34
|
+
* for components whose copy step runs after this call, the caller creates
|
|
35
|
+
* the directory right after deriving and creating these links. Use
|
|
36
|
+
* `stagedTarget` to verify the component actually ships in this release.
|
|
37
|
+
*/
|
|
38
|
+
export declare function collectFileDepLinkSpecs(stagedComponents: readonly StagedComponent[], readDependencies: (manifestDir: string) => Record<string, string>): FileDepLinkSpec[];
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data-driven resolution-link derivation for the full-update pipeline.
|
|
3
|
+
*
|
|
4
|
+
* The deployed console performs full updates with the update logic baked
|
|
5
|
+
* into its own dist — one generation behind the components it installs. A
|
|
6
|
+
* hardcoded link list therefore goes stale the moment a release introduces
|
|
7
|
+
* a new internal `file:` dependency (observed 2026-08-29: the 1.221.2
|
|
8
|
+
* console could not know about the newly introduced install-layout
|
|
9
|
+
* component, so the 1.222.5 update left
|
|
10
|
+
* host-runtime/node_modules/@principles/install-layout missing and every
|
|
11
|
+
* pd-cli runtime command failed with ERR_MODULE_NOT_FOUND).
|
|
12
|
+
*
|
|
13
|
+
* The authoritative source for "which links does the UPDATED tree need" is
|
|
14
|
+
* the STAGED manifests under the update's temp dir (the freshly extracted
|
|
15
|
+
* release trees) — NOT the deployed pre-update manifests, which by
|
|
16
|
+
* definition do not declare newly added dependencies.
|
|
17
|
+
*
|
|
18
|
+
* Each staged component's `file:` refs resolve to a sibling staged
|
|
19
|
+
* component directory; the deployed link target is that sibling's entry in
|
|
20
|
+
* the same staged->deployed pairing.
|
|
21
|
+
*/
|
|
22
|
+
import * as path from 'node:path';
|
|
23
|
+
/**
|
|
24
|
+
* Derive the node_modules link specs implied by each staged component's
|
|
25
|
+
* declared `file:` dependencies.
|
|
26
|
+
*
|
|
27
|
+
* - `readDependencies(manifestDir)` returns the component's dependency map
|
|
28
|
+
* (name -> version-or-ref); return {} for unreadable/missing manifests.
|
|
29
|
+
* - A `file:` ref is resolved against its staged manifest dir to find the
|
|
30
|
+
* staged sibling target; the deployed link target is taken from the same
|
|
31
|
+
* staged->deployed pairing the caller provides — component identity is
|
|
32
|
+
* the staged directory itself (normalized absolute path), NOT the
|
|
33
|
+
* deployed directory's basename. In the legacy layout the deployed
|
|
34
|
+
* plugin dir is named `principles-disciple` while the staged one is
|
|
35
|
+
* `plugin`, so a basename lookup would silently drop the derived link
|
|
36
|
+
* (review P1, PR #1457).
|
|
37
|
+
* - Deps whose staged target is not one of the layout components (e.g.
|
|
38
|
+
* @principles/codex-adapter — a separate host install, not part of this
|
|
39
|
+
* layout) are skipped.
|
|
40
|
+
*
|
|
41
|
+
* Note: `target` (the deployed dir) may not exist yet at derivation time —
|
|
42
|
+
* for components whose copy step runs after this call, the caller creates
|
|
43
|
+
* the directory right after deriving and creating these links. Use
|
|
44
|
+
* `stagedTarget` to verify the component actually ships in this release.
|
|
45
|
+
*/
|
|
46
|
+
export function collectFileDepLinkSpecs(stagedComponents, readDependencies) {
|
|
47
|
+
const deployedDirByStagedDir = new Map();
|
|
48
|
+
for (const component of stagedComponents) {
|
|
49
|
+
deployedDirByStagedDir.set(path.resolve(component.manifestDir), component.deployedDir);
|
|
50
|
+
}
|
|
51
|
+
const specs = [];
|
|
52
|
+
const seen = new Set();
|
|
53
|
+
for (const component of stagedComponents) {
|
|
54
|
+
for (const [name, ref] of Object.entries(readDependencies(component.manifestDir))) {
|
|
55
|
+
if (typeof ref !== 'string' || !ref.startsWith('file:'))
|
|
56
|
+
continue;
|
|
57
|
+
const stagedTarget = path.resolve(component.manifestDir, ref.slice('file:'.length));
|
|
58
|
+
const deployedTarget = deployedDirByStagedDir.get(stagedTarget);
|
|
59
|
+
if (deployedTarget === undefined)
|
|
60
|
+
continue;
|
|
61
|
+
// The link lives in the DEPLOYED component dir (what the updated dist
|
|
62
|
+
// resolves against) — never inside the throwaway temp dir.
|
|
63
|
+
const linkPath = path.join(component.deployedDir, 'node_modules', name);
|
|
64
|
+
if (seen.has(linkPath))
|
|
65
|
+
continue;
|
|
66
|
+
seen.add(linkPath);
|
|
67
|
+
specs.push({ linkPath, target: deployedTarget, stagedTarget });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return specs;
|
|
71
|
+
}
|
package/console/dist/ui/api.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ApiResponse } from "../types.js";
|
|
2
|
+
import type { OwnerDecisionsData, OwnerResolutionResultData } from "./utils/validators.js";
|
|
2
3
|
import { listActiveSignalKeywords, listPendingSignalTerms, fetchKeywordStore, fetchPendingTerms, updateKeywordStore, admitPendingTerm, rejectPendingTerm } from "./utils/signal-keywords-api.js";
|
|
3
4
|
import type { GovernanceExperienceSnapshot, OwnerGovernanceView } from '@principles/core/runtime-v2';
|
|
4
5
|
import type { OwnerIdentityViewData, OwnerIdentityRegisterData, OwnerIdentityUnregisterData } from "./utils/validators.js";
|
|
@@ -148,6 +149,16 @@ declare function updateOutputLanguage(outputLanguage: string): Promise<ApiRespon
|
|
|
148
149
|
declare function fetchGovernanceQueue(): Promise<ApiResponse<GovernanceQueueData>>;
|
|
149
150
|
declare function fetchGovernanceExperience(): Promise<ApiResponse<GovernanceExperienceSnapshot>>;
|
|
150
151
|
declare function recoverFailedTask(taskId: string, reason?: string, force?: boolean): Promise<ApiResponse<RecoveryResultData>>;
|
|
152
|
+
declare function fetchOwnerDecisions(): Promise<ApiResponse<OwnerDecisionsData>>;
|
|
153
|
+
declare function resolveOwnerDecision(taskId: string, body: {
|
|
154
|
+
action: 'accept_current' | 'revise_once' | 'reject_current';
|
|
155
|
+
reviewKey: string;
|
|
156
|
+
expectedRevisionEpoch: number;
|
|
157
|
+
expectedSourceRunId: string;
|
|
158
|
+
expectedSourceArtifactId: string;
|
|
159
|
+
expectedSourceArtifactHash: string;
|
|
160
|
+
ownerInstruction?: string | null;
|
|
161
|
+
}): Promise<ApiResponse<OwnerResolutionResultData>>;
|
|
151
162
|
declare function fetchApprovalsGrouped(): Promise<ApiResponse<ApprovalsGroupedData>>;
|
|
152
163
|
declare function fetchAllActivations(): Promise<ApiResponse<ActivationsData>>;
|
|
153
164
|
declare function disableActivation(activationId: string): Promise<ApiResponse<DisableActivationData>>;
|
|
@@ -214,7 +225,7 @@ export interface FollowUpPayload {
|
|
|
214
225
|
candidateId?: string;
|
|
215
226
|
}
|
|
216
227
|
declare function dispatchFollowUp(decisionId: string, payload: FollowUpPayload): Promise<ApiResponse<FollowUpResponseData>>;
|
|
217
|
-
export { request, listActiveSignalKeywords, listPendingSignalTerms, fetchKeywordStore, fetchPendingTerms, updateKeywordStore, admitPendingTerm, rejectPendingTerm, getToken, setToken, clearToken, checkAuth, approveApproval, rejectApproval, editApproval, fetchPrinciples, fetchPrincipleDetail, fetchPrincipleGovernance, fetchPrincipleTrajectory, fetchPrincipleReceipts, fetchReceiptCounts, archivePrinciple, unarchivePrinciple, createFeedbackReport, listFeedbackReports, getFeedbackReport, deleteFeedbackReport, fetchFeedbackChannels, submitFeedbackReport, markFeedbackReportSent, fetchConfigSummary, fetchConfigCatalog, updateAgentBinding, updateDefaultRuntime, createRuntimeProfile, updateRuntimeProfile, deleteRuntimeProfile, fetchAgentReadiness, patchFeatureFlag, fetchOutputLanguage, updateOutputLanguage, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchGovernanceQueue, fetchGovernanceExperience, recoverFailedTask, fetchApprovalsGrouped, fetchAllActivations, disableActivation, fetchRuleCodeOwnerReview, ruleCodeDecision, pauseAllRuleCode, releaseRuleCodePause, fetchLifecycleMetrics, fetchUpdateStatus, fetchUpdateHistory, applyUpdate, applyFullUpdate, rollbackUpdate, fetchEvidenceChain, fetchIntentSummary, fetchIntentContent, createIntentTemplate, saveIntentContent, fetchIntentVersions, recordIntentDecision, listIntentDecisionsByPainId, listIntentDecisionsByTaskId, fetchIntentDecisionSummary, dispatchFollowUp, };
|
|
228
|
+
export { request, listActiveSignalKeywords, listPendingSignalTerms, fetchKeywordStore, fetchPendingTerms, updateKeywordStore, admitPendingTerm, rejectPendingTerm, getToken, setToken, clearToken, checkAuth, approveApproval, rejectApproval, editApproval, fetchPrinciples, fetchPrincipleDetail, fetchPrincipleGovernance, fetchPrincipleTrajectory, fetchPrincipleReceipts, fetchReceiptCounts, archivePrinciple, unarchivePrinciple, createFeedbackReport, listFeedbackReports, getFeedbackReport, deleteFeedbackReport, fetchFeedbackChannels, submitFeedbackReport, markFeedbackReportSent, fetchConfigSummary, fetchConfigCatalog, updateAgentBinding, updateDefaultRuntime, createRuntimeProfile, updateRuntimeProfile, deleteRuntimeProfile, fetchAgentReadiness, patchFeatureFlag, fetchOutputLanguage, updateOutputLanguage, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchGovernanceQueue, fetchGovernanceExperience, recoverFailedTask, fetchOwnerDecisions, resolveOwnerDecision, fetchApprovalsGrouped, fetchAllActivations, disableActivation, fetchRuleCodeOwnerReview, ruleCodeDecision, pauseAllRuleCode, releaseRuleCodePause, fetchLifecycleMetrics, fetchUpdateStatus, fetchUpdateHistory, applyUpdate, applyFullUpdate, rollbackUpdate, fetchEvidenceChain, fetchIntentSummary, fetchIntentContent, createIntentTemplate, saveIntentContent, fetchIntentVersions, recordIntentDecision, listIntentDecisionsByPainId, listIntentDecisionsByTaskId, fetchIntentDecisionSummary, dispatchFollowUp, };
|
|
218
229
|
export type { FeedbackReportData, FeedbackDraftSummaryData, FeedbackDraftEnvelopeData, FeedbackChannelsData, FeedbackSubmitResultData, DeleteEnvelopeData, WorkspaceEntryData, RemovedEnvelopeData, SyncResultData, ConfigSummaryData, ConfigCatalogData, AgentBindingUpdateData, DefaultRuntimeUpdateData, RuntimeProfileMutationData, ReadinessCheckData, FeatureFlagUpdateData, OutputLanguageData, GovernanceQueueData, RecoveryResultData, ActivationsData, DisableActivationData, RuleCodeOwnerReviewData, RuleCodeMutationData, LifecycleMetricsData, UpdateStatusData, UpdateHistoryData, ApplyUpdateResultData, RollbackResultData, ApprovalRecordData, PrinciplesListData, ApprovalsGroupedData, EvidenceChainData, EvidenceChainRecordData, EvidenceChainStateData, TrajectoryData, TrajectoryStageData, IntentSummaryData, IntentSectionsData, IntentDocWarningData, IntentDecisionRecordData, IntentDecisionResultData, IntentDecisionSummaryData, FollowUpResponseData, LinkCandidateFollowUpData, GuideRulehostFollowUpData, GeneratePatchProposalFollowUpData, IntentVersionEntry, IntentVersionData, } from "./utils/validators.js";
|
|
219
230
|
export type { SignalKeyword, PendingSignalTerm } from "./utils/signal-keywords-types.js";
|
|
220
231
|
export type { ActivationRecordData as ActivationRecord } from "./utils/validators.js";
|
package/console/dist/ui/api.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { listActiveSignalKeywords, listPendingSignalTerms, fetchKeywordStore, fetchPendingTerms, updateKeywordStore, admitPendingTerm, rejectPendingTerm, } from "./utils/signal-keywords-api.js";
|
|
2
|
-
import { validateErrorResponse, validateHeaders, validateFeedbackReport, validateFeedbackDraftsList, validateFeedbackDraftEnvelope, validateFeedbackChannels, validateFeedbackSubmitResult, validateDeleteEnvelope, validateWorkspaceEntry, validateWorkspaceList, validateRemovedEnvelope, validateSyncResult, validateConfigSummary, validateConfigCatalog, validateAgentBindingUpdate, validateDefaultRuntimeUpdate, validateRuntimeProfileMutation, validateReadinessCheck, validateFeatureFlagUpdate, validateOutputLanguage, validateGovernanceQueue, validateRecoveryResult, validateActivations, validateDisableActivation, validateLifecycleMetrics, validateUpdateStatus, validateUpdateHistory, validateApplyUpdateResult, validateRollbackResult, validateApprovalRecordDirect, validatePrinciplesList, validateApprovalsGrouped, validateEvidenceChain, validateTrajectoryData, validateIntentSummary, validateIntentDecisionList, validateIntentDecisionResult, validateIntentDecisionSummary, validateFollowUpResponse, validateIntentInitResult, validateIntentSaveResult, validateIntentRawContent, validateIntentVersions, validateOwnerGovernanceView, validateGovernanceExperienceSnapshot, validateRuleCodeOwnerReview, validateRuleCodeMutation, validateOwnerIdentityView, validateOwnerIdentityRegister, validateOwnerIdentityUnregister, } from "./utils/validators.js";
|
|
2
|
+
import { validateErrorResponse, validateHeaders, validateFeedbackReport, validateFeedbackDraftsList, validateFeedbackDraftEnvelope, validateFeedbackChannels, validateFeedbackSubmitResult, validateDeleteEnvelope, validateWorkspaceEntry, validateWorkspaceList, validateRemovedEnvelope, validateSyncResult, validateConfigSummary, validateConfigCatalog, validateAgentBindingUpdate, validateDefaultRuntimeUpdate, validateRuntimeProfileMutation, validateReadinessCheck, validateFeatureFlagUpdate, validateOutputLanguage, validateGovernanceQueue, validateRecoveryResult, validateOwnerDecisionsData, validateOwnerResolutionResult, validateActivations, validateDisableActivation, validateLifecycleMetrics, validateUpdateStatus, validateUpdateHistory, validateApplyUpdateResult, validateRollbackResult, validateApprovalRecordDirect, validatePrinciplesList, validateApprovalsGrouped, validateEvidenceChain, validateTrajectoryData, validateIntentSummary, validateIntentDecisionList, validateIntentDecisionResult, validateIntentDecisionSummary, validateFollowUpResponse, validateIntentInitResult, validateIntentSaveResult, validateIntentRawContent, validateIntentVersions, validateOwnerGovernanceView, validateGovernanceExperienceSnapshot, validateRuleCodeOwnerReview, validateRuleCodeMutation, validateOwnerIdentityView, validateOwnerIdentityRegister, validateOwnerIdentityUnregister, } from "./utils/validators.js";
|
|
3
3
|
// ── Auth ──────────────────────────────────────────────────────────────────────
|
|
4
4
|
function getToken() {
|
|
5
5
|
return sessionStorage.getItem("pd_token");
|
|
@@ -454,6 +454,13 @@ async function recoverFailedTask(taskId, reason, force) {
|
|
|
454
454
|
body: JSON.stringify(body),
|
|
455
455
|
}, validateRecoveryResult);
|
|
456
456
|
}
|
|
457
|
+
// PRI-629: unified Owner Decision inbox — read projection + resolution.
|
|
458
|
+
async function fetchOwnerDecisions() {
|
|
459
|
+
return request('/api/v1/governance/owner-decisions', undefined, validateOwnerDecisionsData);
|
|
460
|
+
}
|
|
461
|
+
async function resolveOwnerDecision(taskId, body) {
|
|
462
|
+
return request('/api/v1/governance/owner-decisions/' + encodeURIComponent(taskId) + '/resolve', { method: 'POST', body: JSON.stringify(body) }, validateOwnerResolutionResult);
|
|
463
|
+
}
|
|
457
464
|
async function fetchApprovalsGrouped() {
|
|
458
465
|
return request('/api/v1/approvals/grouped', undefined, validateApprovalsGrouped);
|
|
459
466
|
}
|
|
@@ -570,4 +577,4 @@ listActiveSignalKeywords, listPendingSignalTerms, fetchKeywordStore, fetchPendin
|
|
|
570
577
|
// auth
|
|
571
578
|
getToken, setToken, clearToken, checkAuth, approveApproval, rejectApproval, editApproval, fetchPrinciples, fetchPrincipleDetail, fetchPrincipleGovernance, fetchPrincipleTrajectory, fetchPrincipleReceipts, fetchReceiptCounts, archivePrinciple, unarchivePrinciple, createFeedbackReport, listFeedbackReports, getFeedbackReport, deleteFeedbackReport,
|
|
572
579
|
// Feedback submit ladder (Slice 3, spec §8)
|
|
573
|
-
fetchFeedbackChannels, submitFeedbackReport, markFeedbackReportSent, fetchConfigSummary, fetchConfigCatalog, updateAgentBinding, updateDefaultRuntime, createRuntimeProfile, updateRuntimeProfile, deleteRuntimeProfile, fetchAgentReadiness, patchFeatureFlag, fetchOutputLanguage, updateOutputLanguage, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchGovernanceQueue, fetchGovernanceExperience, recoverFailedTask, fetchApprovalsGrouped, fetchAllActivations, disableActivation, fetchRuleCodeOwnerReview, ruleCodeDecision, pauseAllRuleCode, releaseRuleCodePause, fetchLifecycleMetrics, fetchUpdateStatus, fetchUpdateHistory, applyUpdate, applyFullUpdate, rollbackUpdate, fetchEvidenceChain, fetchIntentSummary, fetchIntentContent, createIntentTemplate, saveIntentContent, fetchIntentVersions, recordIntentDecision, listIntentDecisionsByPainId, listIntentDecisionsByTaskId, fetchIntentDecisionSummary, dispatchFollowUp, };
|
|
580
|
+
fetchFeedbackChannels, submitFeedbackReport, markFeedbackReportSent, fetchConfigSummary, fetchConfigCatalog, updateAgentBinding, updateDefaultRuntime, createRuntimeProfile, updateRuntimeProfile, deleteRuntimeProfile, fetchAgentReadiness, patchFeatureFlag, fetchOutputLanguage, updateOutputLanguage, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchGovernanceQueue, fetchGovernanceExperience, recoverFailedTask, fetchOwnerDecisions, resolveOwnerDecision, fetchApprovalsGrouped, fetchAllActivations, disableActivation, fetchRuleCodeOwnerReview, ruleCodeDecision, pauseAllRuleCode, releaseRuleCodePause, fetchLifecycleMetrics, fetchUpdateStatus, fetchUpdateHistory, applyUpdate, applyFullUpdate, rollbackUpdate, fetchEvidenceChain, fetchIntentSummary, fetchIntentContent, createIntentTemplate, saveIntentContent, fetchIntentVersions, recordIntentDecision, listIntentDecisionsByPainId, listIntentDecisionsByTaskId, fetchIntentDecisionSummary, dispatchFollowUp, };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { createContext, useCallback, useEffect, useRef, useState, } from 'react';
|
|
3
|
-
import { fetchGovernanceQueue } from '../../api.js';
|
|
3
|
+
import { fetchGovernanceQueue, fetchOwnerDecisions } from '../../api.js';
|
|
4
4
|
import { useNotificationSound } from '../../hooks/useNotificationSound.js';
|
|
5
5
|
import { diffNotificationCounts } from './notification-reducer.js';
|
|
6
6
|
import { loadSoundEnabled, saveSoundEnabled } from './sound-storage.js';
|
|
@@ -33,8 +33,14 @@ export function NotificationProvider({ children }) {
|
|
|
33
33
|
console.warn('Notification poll failed:', result.error);
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
|
-
const pendingCount = result.data.pendingReviewCount;
|
|
37
36
|
const degradedCount = result.data.degradedSignals?.length ?? 0;
|
|
37
|
+
// PRI-629 (SPEC §27): 侧边栏徽标 N = 真实可执行 Owner 决策数
|
|
38
|
+
// (OwnerDecisionItem.length) — 不是 approvals pending / candidate /
|
|
39
|
+
// needs_human_review 计数。投影失败时回退 approvals pending (保守)。
|
|
40
|
+
const decisionsResult = await fetchOwnerDecisions();
|
|
41
|
+
const pendingCount = decisionsResult.success
|
|
42
|
+
? decisionsResult.data.total
|
|
43
|
+
: result.data.pendingReviewCount;
|
|
38
44
|
hasSuccessfulPollRef.current = true;
|
|
39
45
|
setState((prev) => ({
|
|
40
46
|
...prev,
|