create-principles-disciple 1.120.5 → 1.121.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 +4 -0
- package/console/dist/server/models/GovernanceConsoleModel.d.ts +10 -2
- package/console/dist/server/models/GovernanceConsoleModel.js +28 -3
- package/console/dist/server/routes/failed-tasks.d.ts +9 -5
- package/console/dist/server/routes/failed-tasks.js +220 -8
- package/console/dist/ui/api.d.ts +4 -3
- package/console/dist/ui/api.js +14 -2
- package/console/dist/ui/i18n/en.json +12 -0
- package/console/dist/ui/i18n/zh-CN.json +12 -0
- package/console/dist/ui/pages/failed-tasks/FailedTasksPage.js +78 -5
- package/console/dist/ui/pages/focus/FocusPage.js +5 -1
- package/console/dist/ui/utils/validators.d.ts +11 -0
- package/console/dist/ui/utils/validators.js +32 -0
- package/console/dist/web/assets/app.js +169 -6
- package/core/dist/runtime-v2/__tests__/owner-retry.test.d.ts +2 -0
- package/core/dist/runtime-v2/__tests__/owner-retry.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/__tests__/owner-retry.test.js +144 -0
- package/core/dist/runtime-v2/__tests__/owner-retry.test.js.map +1 -0
- package/core/dist/runtime-v2/__tests__/pain-diagnosis-persistence.test.d.ts +2 -0
- package/core/dist/runtime-v2/__tests__/pain-diagnosis-persistence.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/__tests__/pain-diagnosis-persistence.test.js +293 -0
- package/core/dist/runtime-v2/__tests__/pain-diagnosis-persistence.test.js.map +1 -0
- package/core/dist/runtime-v2/__tests__/recovery-actions-log.test.d.ts +2 -0
- package/core/dist/runtime-v2/__tests__/recovery-actions-log.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/__tests__/recovery-actions-log.test.js +112 -0
- package/core/dist/runtime-v2/__tests__/recovery-actions-log.test.js.map +1 -0
- package/core/dist/runtime-v2/diagnostician/rootcause-prompt-builder.d.ts +10 -0
- package/core/dist/runtime-v2/diagnostician/rootcause-prompt-builder.d.ts.map +1 -1
- package/core/dist/runtime-v2/diagnostician/rootcause-prompt-builder.js +40 -4
- package/core/dist/runtime-v2/diagnostician/rootcause-prompt-builder.js.map +1 -1
- package/core/dist/runtime-v2/diagnostician-prompt-builder.d.ts +6 -0
- package/core/dist/runtime-v2/diagnostician-prompt-builder.d.ts.map +1 -1
- package/core/dist/runtime-v2/diagnostician-prompt-builder.js.map +1 -1
- package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js +28 -0
- package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js.map +1 -1
- package/core/dist/runtime-v2/feature-flags/__tests__/pain-diagnosis-persistence-flag.test.d.ts +2 -0
- package/core/dist/runtime-v2/feature-flags/__tests__/pain-diagnosis-persistence-flag.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/feature-flags/__tests__/pain-diagnosis-persistence-flag.test.js +44 -0
- package/core/dist/runtime-v2/feature-flags/__tests__/pain-diagnosis-persistence-flag.test.js.map +1 -0
- package/core/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts.map +1 -1
- package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js +18 -0
- package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js.map +1 -1
- package/core/dist/runtime-v2/index.d.ts +5 -0
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +5 -0
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/internalization/diag-rootcause-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/diag-rootcause-runner.js +5 -0
- package/core/dist/runtime-v2/internalization/diag-rootcause-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-retry.d.ts +49 -0
- package/core/dist/runtime-v2/internalization/owner-retry.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/owner-retry.js +51 -0
- package/core/dist/runtime-v2/internalization/owner-retry.js.map +1 -0
- package/core/dist/runtime-v2/pain-signal-bridge.d.ts +19 -0
- package/core/dist/runtime-v2/pain-signal-bridge.d.ts.map +1 -1
- package/core/dist/runtime-v2/pain-signal-bridge.js +61 -0
- package/core/dist/runtime-v2/pain-signal-bridge.js.map +1 -1
- package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts +12 -0
- package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts.map +1 -1
- package/core/dist/runtime-v2/pain-signal-runtime-factory.js +48 -2
- package/core/dist/runtime-v2/pain-signal-runtime-factory.js.map +1 -1
- package/core/dist/runtime-v2/recovery-actions-log.d.ts +46 -0
- package/core/dist/runtime-v2/recovery-actions-log.d.ts.map +1 -0
- package/core/dist/runtime-v2/recovery-actions-log.js +116 -0
- package/core/dist/runtime-v2/recovery-actions-log.js.map +1 -0
- package/core/dist/runtime-v2/recovery-sweep-service.d.ts +8 -0
- package/core/dist/runtime-v2/recovery-sweep-service.d.ts.map +1 -1
- package/core/dist/runtime-v2/recovery-sweep-service.js +4 -0
- package/core/dist/runtime-v2/recovery-sweep-service.js.map +1 -1
- package/core/dist/runtime-v2/store/pain-diagnosis/pain-diagnosis-store.d.ts +77 -0
- package/core/dist/runtime-v2/store/pain-diagnosis/pain-diagnosis-store.d.ts.map +1 -0
- package/core/dist/runtime-v2/store/pain-diagnosis/pain-diagnosis-store.js +40 -0
- package/core/dist/runtime-v2/store/pain-diagnosis/pain-diagnosis-store.js.map +1 -0
- package/core/dist/runtime-v2/store/pain-diagnosis/sqlite-pain-diagnosis-store.d.ts +9 -0
- package/core/dist/runtime-v2/store/pain-diagnosis/sqlite-pain-diagnosis-store.d.ts.map +1 -0
- package/core/dist/runtime-v2/store/pain-diagnosis/sqlite-pain-diagnosis-store.js +118 -0
- package/core/dist/runtime-v2/store/pain-diagnosis/sqlite-pain-diagnosis-store.js.map +1 -0
- package/core/dist/runtime-v2/store/runtime-state-manager.d.ts +11 -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 +18 -0
- package/core/dist/runtime-v2/store/runtime-state-manager.js.map +1 -1
- package/core/dist/runtime-v2/store/sqlite-connection.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/sqlite-connection.js +23 -0
- package/core/dist/runtime-v2/store/sqlite-connection.js.map +1 -1
- package/host-runtime/dist/production-pain-evidence.js +9 -2
- package/package.json +1 -2
- package/pd-cli/dist/commands/runtime-internalization-retry.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-internalization-retry.js +32 -19
- package/pd-cli/dist/commands/runtime-internalization-retry.js.map +1 -1
- package/plugin/dist/bundle.js +438 -415
- package/templates/langs/en/core/AGENTS.md +0 -205
- package/templates/langs/en/core/BOOT.md +0 -60
- package/templates/langs/en/core/BOOTSTRAP.md +0 -260
- package/templates/langs/en/core/HEARTBEAT.md +0 -74
- package/templates/langs/en/core/IDENTITY.md +0 -8
- package/templates/langs/en/core/PRINCIPLES.md +0 -10
- package/templates/langs/en/core/SOUL.md +0 -46
- package/templates/langs/en/core/TOOLS.md +0 -58
- package/templates/langs/en/core/USER.md +0 -10
- package/templates/langs/en/pain/00_seed_samples.md +0 -23
- package/templates/langs/en/pain_dictionary.json +0 -22
- package/templates/langs/en/principles/THINKING_OS.md +0 -77
- package/templates/langs/zh/core/AGENTS.md +0 -197
- package/templates/langs/zh/core/BOOT.md +0 -60
- package/templates/langs/zh/core/BOOTSTRAP.md +0 -260
- package/templates/langs/zh/core/HEARTBEAT.md +0 -98
- package/templates/langs/zh/core/IDENTITY.md +0 -8
- package/templates/langs/zh/core/SOUL.md +0 -46
- package/templates/langs/zh/core/TOOLS.md +0 -58
- package/templates/langs/zh/core/USER.md +0 -10
- package/templates/langs/zh/pain/00_seed_samples.md +0 -24
- package/templates/langs/zh/pain_dictionary.json +0 -18
- package/templates/langs/zh/principles/THINKING_OS.md +0 -77
- package/templates/pain_dictionary.json +0 -36
- package/templates/pain_settings.json +0 -129
- package/templates/workspace/.principles/THINKING_OS.md +0 -65
- package/templates/workspace/AUDIT.md +0 -15
- package/templates/workspace/okr/CURRENT_FOCUS.md +0 -56
- package/templates/workspace/okr/RECOVERY_PROTOCOL.md +0 -56
- package/templates/workspace/okr/TASK_CHANGES.jsonl +0 -6
- package/templates/workspace/okr/WEEK_TASKS.json +0 -6
|
@@ -211,10 +211,14 @@ async function initServices(workspaceDir, authConfig) {
|
|
|
211
211
|
const feedbackChannelEnabled = pdFlags.flags.feedback_channel?.enabled ?? false;
|
|
212
212
|
const failedTasksObservabilityEnabled = pdFlags.flags.failed_tasks_observability?.enabled ?? true;
|
|
213
213
|
const governanceProjectionEnabled = pdFlags.flags.principle_governance_projection_v2?.enabled ?? false;
|
|
214
|
+
// Governance Recovery Actions v1: fail-closed — recovery stays disabled unless
|
|
215
|
+
// explicitly enabled (default off keeps the Console read-only).
|
|
216
|
+
const failedTaskRecoveryEnabled = pdFlags.flags.failed_task_recovery_console?.enabled ?? false;
|
|
214
217
|
const feedbackFlags = {
|
|
215
218
|
feedback_channel: { enabled: feedbackChannelEnabled },
|
|
216
219
|
failed_tasks_observability: { enabled: failedTasksObservabilityEnabled },
|
|
217
220
|
principle_governance_projection_v2: { enabled: governanceProjectionEnabled },
|
|
221
|
+
failed_task_recovery_console: { enabled: failedTaskRecoveryEnabled },
|
|
218
222
|
};
|
|
219
223
|
if (!configResult.ok) {
|
|
220
224
|
console.warn('[pd-console] PD config loading failed (using defaults for feedback channel):', configResult.errors.map(e => e.reason).join('; '));
|
|
@@ -64,11 +64,19 @@ export interface DegradedFailureSummary {
|
|
|
64
64
|
* Frontend maps these via i18n; some codes support interpolation
|
|
65
65
|
* (e.g. {{count}} from pendingReviewCount).
|
|
66
66
|
*/
|
|
67
|
-
export type StateReasonCode = 'state_db_missing' | 'no_pipeline_activity' | 'pending_approvals' | 'pipeline_active' | 'consumed_candidates' | 'degraded_state';
|
|
68
|
-
export type NextActionCode = 'run_config_doctor' | 'wait_for_pipeline' | 'review_approvals' | 'check_degraded_signals' | 'check_pipeline_status';
|
|
67
|
+
export type StateReasonCode = 'state_db_missing' | 'no_pipeline_activity' | 'pending_approvals' | 'tasks_need_human_review' | 'pipeline_active' | 'consumed_candidates' | 'degraded_state';
|
|
68
|
+
export type NextActionCode = 'run_config_doctor' | 'wait_for_pipeline' | 'review_approvals' | 'review_failed_tasks' | 'check_degraded_signals' | 'check_pipeline_status';
|
|
69
69
|
export interface GovernanceQueueResponse {
|
|
70
70
|
/** Number of pending approvals awaiting owner review */
|
|
71
71
|
pendingReviewCount: number;
|
|
72
|
+
/**
|
|
73
|
+
* Governance Recovery Actions v1 (Phase 0 signal fix): number of
|
|
74
|
+
* internalization tasks in needs_human_review status (all peer-runner
|
|
75
|
+
* kinds, incl. rollout_reviewer). These are owner-attention items — they
|
|
76
|
+
* wait for an explicit recovery decision, so unlike degraded signals they
|
|
77
|
+
* carry no time window. Set only when > 0 (mirrors evidenceInProgressCount).
|
|
78
|
+
*/
|
|
79
|
+
pendingHumanReviewCount?: number;
|
|
72
80
|
/** Number of high/critical risk pending approvals */
|
|
73
81
|
behaviorDeviationCount: number;
|
|
74
82
|
/** Stagnation signals for approvals older than 7 days */
|
|
@@ -124,14 +124,27 @@ export class GovernanceConsoleModel {
|
|
|
124
124
|
// 2. Check internalization pipeline activity (tasks)
|
|
125
125
|
// PRI-556: pipeline activity still counts ALL tasks (drives in_progress),
|
|
126
126
|
// but only in-window failures/retries produce degraded signals.
|
|
127
|
+
// Governance Recovery Actions v1 (Phase 0 signal fix): the kind list now
|
|
128
|
+
// includes rollout_reviewer (previously invisible even for degraded
|
|
129
|
+
// signals), and needs_human_review tasks surface as owner-attention
|
|
130
|
+
// items via pendingHumanReviewCount — previously NO stage's
|
|
131
|
+
// needs_human_review entered this queue.
|
|
127
132
|
let hasInternalizationTasks = false;
|
|
128
133
|
const retryWaitDetails = [];
|
|
129
134
|
const failedDetails = [];
|
|
135
|
+
let pendingHumanReviewCount = 0;
|
|
130
136
|
try {
|
|
131
|
-
const tasks = db.prepare(`SELECT task_id, task_kind, status, last_error, updated_at FROM tasks WHERE task_kind IN ('dreamer', 'philosopher', 'scribe', 'artificer', 'evaluator')`).all();
|
|
137
|
+
const tasks = db.prepare(`SELECT task_id, task_kind, status, last_error, updated_at FROM tasks WHERE task_kind IN ('dreamer', 'philosopher', 'scribe', 'artificer', 'evaluator', 'rollout_reviewer')`).all();
|
|
132
138
|
hasInternalizationTasks = tasks.length > 0;
|
|
133
139
|
const actionableCutoff = Date.now() - DEGRADED_SIGNAL_WINDOW_MS;
|
|
134
140
|
for (const task of tasks) {
|
|
141
|
+
if (task.status === 'needs_human_review') {
|
|
142
|
+
// Owner-attention item — NO time window: it waits for an explicit
|
|
143
|
+
// recovery decision (unlike terminal `failed` rows, which PRI-556
|
|
144
|
+
// windowed to avoid permanent signal pollution).
|
|
145
|
+
pendingHumanReviewCount += 1;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
135
148
|
if (task.status !== 'retry_wait' && task.status !== 'failed')
|
|
136
149
|
continue;
|
|
137
150
|
// updated_at is untrusted row data — malformed timestamps fall
|
|
@@ -207,7 +220,9 @@ export class GovernanceConsoleModel {
|
|
|
207
220
|
});
|
|
208
221
|
}
|
|
209
222
|
// ── Determine governance state ──────────────────────────────────────
|
|
210
|
-
|
|
223
|
+
// needs_human_review tasks are owner-attention items alongside pending
|
|
224
|
+
// approvals (Governance Recovery Actions v1 Phase 0): both flip the
|
|
225
|
+
// queue into owner_review_ready.
|
|
211
226
|
const hasPipelineActivity = hasInternalizationTasks || hasConsumedCandidates || hasPendingCandidates;
|
|
212
227
|
const hasDegradation = degradedSignals.length > 0;
|
|
213
228
|
let governanceState;
|
|
@@ -216,13 +231,20 @@ export class GovernanceConsoleModel {
|
|
|
216
231
|
let stateReason;
|
|
217
232
|
let nextAction;
|
|
218
233
|
let inProgressSummary;
|
|
219
|
-
if (
|
|
234
|
+
if (pendingReviewCount > 0) {
|
|
220
235
|
governanceState = 'owner_review_ready';
|
|
221
236
|
stateReasonCode = 'pending_approvals';
|
|
222
237
|
nextActionCode = 'review_approvals';
|
|
223
238
|
stateReason = `${pendingReviewCount} principle(s) pending your review and decision.`;
|
|
224
239
|
nextAction = 'Review pending principles and approve, reject, or park.';
|
|
225
240
|
}
|
|
241
|
+
else if (pendingHumanReviewCount > 0) {
|
|
242
|
+
governanceState = 'owner_review_ready';
|
|
243
|
+
stateReasonCode = 'tasks_need_human_review';
|
|
244
|
+
nextActionCode = 'review_failed_tasks';
|
|
245
|
+
stateReason = `${pendingHumanReviewCount} internalization task(s) are waiting for your review decision (needs_human_review).`;
|
|
246
|
+
nextAction = 'Open Failed Tasks to inspect and recover needs_human_review tasks.';
|
|
247
|
+
}
|
|
226
248
|
else if (hasDegradation) {
|
|
227
249
|
governanceState = 'degraded';
|
|
228
250
|
stateReasonCode = 'degraded_state';
|
|
@@ -350,6 +372,9 @@ export class GovernanceConsoleModel {
|
|
|
350
372
|
if (evidenceInProgressCount > 0) {
|
|
351
373
|
response.evidenceInProgressCount = evidenceInProgressCount;
|
|
352
374
|
}
|
|
375
|
+
if (pendingHumanReviewCount > 0) {
|
|
376
|
+
response.pendingHumanReviewCount = pendingHumanReviewCount;
|
|
377
|
+
}
|
|
353
378
|
if (gateBlocksToday > 0) {
|
|
354
379
|
response.gateBlocksToday = gateBlocksToday;
|
|
355
380
|
}
|
|
@@ -14,11 +14,15 @@ export type FailedTasksContext = {
|
|
|
14
14
|
* Handle requests to `/api/v1/failed-tasks*`.
|
|
15
15
|
*
|
|
16
16
|
* Routes:
|
|
17
|
-
* GET
|
|
18
|
-
* GET
|
|
19
|
-
*
|
|
17
|
+
* GET /api/v1/failed-tasks — list failed/needs_human_review tasks
|
|
18
|
+
* GET /api/v1/failed-tasks/:id — fetch single task detail (task + runs + lastError)
|
|
19
|
+
* POST /api/v1/failed-tasks/:id/recover — Owner recovery (failed | needs_human_review → pending)
|
|
20
|
+
* other methods — 405 Method Not Allowed
|
|
20
21
|
*
|
|
21
|
-
* Feature flag
|
|
22
|
-
*
|
|
22
|
+
* Feature flag gates (checked before any DB access so a disabled flag
|
|
23
|
+
* short-circuits cleanly — rc-9: error responses include reason + next action):
|
|
24
|
+
* - `failed_tasks_observability.enabled === false` → 403 for the whole route
|
|
25
|
+
* - `failed_task_recovery_console.enabled !== true` → 403 for POST recover
|
|
26
|
+
* (fail-closed mutation gate; default on, explicit disable keeps the Console read-only)
|
|
23
27
|
*/
|
|
24
28
|
export declare function handleFailedTasksRoute(req: IncomingMessage, res: ServerResponse, ctx: FailedTasksContext): Promise<void>;
|
|
@@ -5,14 +5,26 @@
|
|
|
5
5
|
// detail (task record + run history + last error category). Gated by the
|
|
6
6
|
// failed_tasks_observability feature flag.
|
|
7
7
|
//
|
|
8
|
+
// Governance Recovery Actions v1: POST /api/v1/failed-tasks/:id/recover lets the
|
|
9
|
+
// Owner recover a task from the Console. failed → pending reuses
|
|
10
|
+
// RecoverySweepService.recoverFailedTask; needs_human_review → pending reuses
|
|
11
|
+
// the extracted owner-retry sequence (ownerRetryNeedsHumanReviewTask — the same
|
|
12
|
+
// implementation `pd runtime internalization retry --confirm` calls). No
|
|
13
|
+
// recovery logic is reimplemented here. Gated by failed_task_recovery_console
|
|
14
|
+
// (default off → Console stays read-only). Every successful recovery appends a
|
|
15
|
+
// RecoveryAction audit record (.state/recovery_actions.jsonl).
|
|
16
|
+
//
|
|
8
17
|
// Trust boundary (rc-1, rc-2): all query string values are read via
|
|
9
18
|
// URLSearchParams.get() and treated as `unknown` until validated with typeof /
|
|
10
|
-
// Number.isFinite.
|
|
11
|
-
//
|
|
19
|
+
// Number.isFinite. POST bodies are parsed to `unknown` and field-checked with
|
|
20
|
+
// Object.hasOwn + typeof before use. Store return values are typed by the core
|
|
21
|
+
// contract and further validated by SqliteTaskStore's row readers (rc-1 at the
|
|
22
|
+
// DB boundary).
|
|
12
23
|
import * as fs from 'node:fs';
|
|
13
24
|
import * as path from 'node:path';
|
|
14
|
-
import { SqliteConnection, SqliteTaskStore, } from '@principles/core/runtime-v2';
|
|
25
|
+
import { SqliteConnection, SqliteTaskStore, PDRuntimeError, createRecoverySweepService, appendRecoveryAction, } from '@principles/core/runtime-v2';
|
|
15
26
|
import { sendSuccess, sendError, sendNotFound, sendMethodNotAllowed, sendBadRequest, } from '../utils/response.js';
|
|
27
|
+
import { readBody } from '../utils/request.js';
|
|
16
28
|
const entries = new Map();
|
|
17
29
|
function stateDbExists(workspaceDir) {
|
|
18
30
|
return fs.existsSync(path.join(workspaceDir, '.pd', 'state.db'));
|
|
@@ -79,16 +91,143 @@ function extractQueryParams(req) {
|
|
|
79
91
|
return null;
|
|
80
92
|
return new URLSearchParams(urlStr.slice(qIdx + 1));
|
|
81
93
|
}
|
|
94
|
+
// ── Recovery helpers (Governance Recovery Actions v1) ───────────────────────
|
|
95
|
+
const MAX_REASON_LENGTH = 2000;
|
|
96
|
+
function isRecord(value) {
|
|
97
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
98
|
+
}
|
|
99
|
+
function tryParseJson(body) {
|
|
100
|
+
try {
|
|
101
|
+
return JSON.parse(body);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Parse the optional recover request body. Accepts an empty body (reason is
|
|
109
|
+
* optional) or a JSON object with an optional string `reason` field
|
|
110
|
+
* (rc-1/rc-2: parsed value stays `unknown` until Object.hasOwn + typeof
|
|
111
|
+
* checks pass). Returns a discriminated result; `ok === false` carries the
|
|
112
|
+
* bad-request message.
|
|
113
|
+
*/
|
|
114
|
+
function parseRecoverBody(body) {
|
|
115
|
+
if (body.trim().length === 0)
|
|
116
|
+
return { ok: true };
|
|
117
|
+
const raw = tryParseJson(body);
|
|
118
|
+
if (raw === undefined)
|
|
119
|
+
return { ok: false, error: 'Request body must be valid JSON' };
|
|
120
|
+
if (!isRecord(raw))
|
|
121
|
+
return { ok: false, error: 'Request body must be a JSON object' };
|
|
122
|
+
if (Object.hasOwn(raw, 'reason')) {
|
|
123
|
+
if (typeof raw.reason !== 'string') {
|
|
124
|
+
return { ok: false, error: 'reason must be a string' };
|
|
125
|
+
}
|
|
126
|
+
if (raw.reason.length > MAX_REASON_LENGTH) {
|
|
127
|
+
return { ok: false, error: `reason must be at most ${MAX_REASON_LENGTH} characters` };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const reason = typeof raw.reason === 'string' && raw.reason.length > 0 ? raw.reason : undefined;
|
|
131
|
+
return { ok: true, reason };
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Dispatch one recovery attempt through the shared core services.
|
|
135
|
+
*
|
|
136
|
+
* Status dispatch (SPEC §6.1/§6.2): failed → recoverFailedTask; then
|
|
137
|
+
* needs_human_review → owner authority reset. A null first result means
|
|
138
|
+
* "missing or not failed"; the second call distinguishes not_found /
|
|
139
|
+
* wrong-status / metadata_invalid. No recovery logic lives here.
|
|
140
|
+
*/
|
|
141
|
+
async function dispatchRecovery(workspaceDir, taskId) {
|
|
142
|
+
const { service, close } = await createRecoverySweepService({ workspaceDir });
|
|
143
|
+
try {
|
|
144
|
+
// failed → pending (crash-retry semantics: completionIntent preserved)
|
|
145
|
+
try {
|
|
146
|
+
const failedResult = await service.recoverFailedTask(taskId);
|
|
147
|
+
if (failedResult) {
|
|
148
|
+
return {
|
|
149
|
+
ok: true,
|
|
150
|
+
result: 'recovered',
|
|
151
|
+
previousStatus: failedResult.previousStatus,
|
|
152
|
+
newStatus: failedResult.newStatus,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
if (err instanceof PDRuntimeError && err.category === 'input_invalid') {
|
|
158
|
+
// attempt budget exhausted; Console v1 does not expose force
|
|
159
|
+
return {
|
|
160
|
+
ok: false,
|
|
161
|
+
failure: {
|
|
162
|
+
httpStatus: 409,
|
|
163
|
+
errorCode: 'task_attempts_exhausted',
|
|
164
|
+
message: err.message,
|
|
165
|
+
nextAction: 'This task exhausted its attempt budget. Recover it with force via CLI: pd runtime recovery failed-tasks --confirm --force',
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
throw err;
|
|
170
|
+
}
|
|
171
|
+
// not failed (or missing) → owner authority reset path
|
|
172
|
+
const outcome = await service.recoverNeedsHumanReviewTask(taskId);
|
|
173
|
+
if (outcome.status === 'requeued') {
|
|
174
|
+
return {
|
|
175
|
+
ok: true,
|
|
176
|
+
result: 'requeued',
|
|
177
|
+
previousStatus: outcome.previousStatus,
|
|
178
|
+
newStatus: 'pending',
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
if (outcome.status === 'not_found') {
|
|
182
|
+
return {
|
|
183
|
+
ok: false,
|
|
184
|
+
failure: {
|
|
185
|
+
httpStatus: 404,
|
|
186
|
+
errorCode: 'task_not_found',
|
|
187
|
+
message: `Task ${taskId} not found`,
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
if (outcome.status === 'metadata_invalid') {
|
|
192
|
+
return {
|
|
193
|
+
ok: false,
|
|
194
|
+
failure: {
|
|
195
|
+
httpStatus: 409,
|
|
196
|
+
errorCode: 'metadata_invalid',
|
|
197
|
+
message: 'Task metadata failed PI hydration; a recovery now would risk a partial authority reset.',
|
|
198
|
+
nextAction: 'Run pd runtime internalization integrity --json to inspect the task metadata',
|
|
199
|
+
},
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
ok: false,
|
|
204
|
+
failure: {
|
|
205
|
+
httpStatus: 409,
|
|
206
|
+
errorCode: 'task_not_recoverable',
|
|
207
|
+
message: `Task ${taskId} is in status '${outcome.previousStatus}' — only failed / needs_human_review tasks are recoverable.`,
|
|
208
|
+
nextAction: 'Wait for the current attempt to finish, or check pipeline status: pd runtime internalization queue --json',
|
|
209
|
+
extras: { previousStatus: outcome.previousStatus },
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
finally {
|
|
214
|
+
await close();
|
|
215
|
+
}
|
|
216
|
+
}
|
|
82
217
|
/**
|
|
83
218
|
* Handle requests to `/api/v1/failed-tasks*`.
|
|
84
219
|
*
|
|
85
220
|
* Routes:
|
|
86
|
-
* GET
|
|
87
|
-
* GET
|
|
88
|
-
*
|
|
221
|
+
* GET /api/v1/failed-tasks — list failed/needs_human_review tasks
|
|
222
|
+
* GET /api/v1/failed-tasks/:id — fetch single task detail (task + runs + lastError)
|
|
223
|
+
* POST /api/v1/failed-tasks/:id/recover — Owner recovery (failed | needs_human_review → pending)
|
|
224
|
+
* other methods — 405 Method Not Allowed
|
|
89
225
|
*
|
|
90
|
-
* Feature flag
|
|
91
|
-
*
|
|
226
|
+
* Feature flag gates (checked before any DB access so a disabled flag
|
|
227
|
+
* short-circuits cleanly — rc-9: error responses include reason + next action):
|
|
228
|
+
* - `failed_tasks_observability.enabled === false` → 403 for the whole route
|
|
229
|
+
* - `failed_task_recovery_console.enabled !== true` → 403 for POST recover
|
|
230
|
+
* (fail-closed mutation gate; default on, explicit disable keeps the Console read-only)
|
|
92
231
|
*/
|
|
93
232
|
export async function handleFailedTasksRoute(req, res, ctx) {
|
|
94
233
|
const { workspaceDir, subPath } = ctx;
|
|
@@ -173,6 +312,79 @@ export async function handleFailedTasksRoute(req, res, ctx) {
|
|
|
173
312
|
}
|
|
174
313
|
return;
|
|
175
314
|
}
|
|
315
|
+
// Recovery: POST /api/v1/failed-tasks/:id/recover (Governance Recovery
|
|
316
|
+
// Actions v1). Checked before the generic /:id detail branch.
|
|
317
|
+
// eslint-disable-next-line @typescript-eslint/prefer-regexp-exec -- String#match is semantically identical for a non-global regex; the repo's security write-gate false-positives on `<expr>.exec(userInput)` as command injection, so RegExp#exec is not writable here.
|
|
318
|
+
const recoverMatch = subPath.match(/^\/([^/]+)\/recover$/);
|
|
319
|
+
if (recoverMatch) {
|
|
320
|
+
// recoverMatch[1] = captured task id (segment between the leading '/'
|
|
321
|
+
// and the trailing '/recover')
|
|
322
|
+
const taskId = recoverMatch[1] ?? '';
|
|
323
|
+
// Fail-closed gate: this is a mutation endpoint — recovery is enabled by
|
|
324
|
+
// default (2026-08-24 owner decision) and 403 only when explicitly disabled
|
|
325
|
+
// via config (features.failed_task_recovery_console.enabled: false).
|
|
326
|
+
if (ctx.featureFlags?.failed_task_recovery_console?.enabled !== true) {
|
|
327
|
+
sendError(res, 403, 'failed_task_recovery_console_disabled', 'failed_task_recovery_console feature flag is disabled. Enable it via .pd/config.yaml (features.failed_task_recovery_console.enabled: true) to allow recovery from Console.');
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (method !== 'POST') {
|
|
331
|
+
sendMethodNotAllowed(res);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
try {
|
|
335
|
+
if (taskId.length === 0 || !stateDbExists(workspaceDir)) {
|
|
336
|
+
sendNotFound(res, `Task ${taskId} not found`);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
let bodyResult;
|
|
340
|
+
try {
|
|
341
|
+
const body = await readBody(req);
|
|
342
|
+
bodyResult = parseRecoverBody(body);
|
|
343
|
+
}
|
|
344
|
+
catch (err) {
|
|
345
|
+
sendBadRequest(res, err instanceof Error ? err.message : 'Failed to read request body');
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
if (bodyResult.ok === false) {
|
|
349
|
+
sendBadRequest(res, bodyResult.error);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const dispatch = await dispatchRecovery(workspaceDir, taskId);
|
|
353
|
+
if (!dispatch.ok) {
|
|
354
|
+
sendError(res, dispatch.failure.httpStatus, dispatch.failure.errorCode, dispatch.failure.message, dispatch.failure.nextAction ? { nextAction: dispatch.failure.nextAction, ...dispatch.failure.extras } : dispatch.failure.extras);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
// Audit the owner action (SPEC §10). Best-effort: the recovery is
|
|
358
|
+
// already committed; an audit-write failure must not report the whole
|
|
359
|
+
// action as failed, and it is never silent (rc-9).
|
|
360
|
+
try {
|
|
361
|
+
appendRecoveryAction(workspaceDir, {
|
|
362
|
+
taskId,
|
|
363
|
+
previousStatus: dispatch.previousStatus,
|
|
364
|
+
result: dispatch.result,
|
|
365
|
+
operator: 'console',
|
|
366
|
+
reason: bodyResult.reason ?? null,
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
catch (auditErr) {
|
|
370
|
+
console.warn('[failed-tasks] recovery audit append failed:', auditErr instanceof Error ? auditErr.message : String(auditErr));
|
|
371
|
+
}
|
|
372
|
+
// SPEC §6.4: "Recovery Accepted", not "Task Completed" — execution is
|
|
373
|
+
// asynchronous; the task merely re-enters the pending → leased →
|
|
374
|
+
// running → succeeded cycle.
|
|
375
|
+
sendSuccess(res, {
|
|
376
|
+
taskId,
|
|
377
|
+
previousStatus: dispatch.previousStatus,
|
|
378
|
+
newStatus: dispatch.newStatus,
|
|
379
|
+
result: dispatch.result,
|
|
380
|
+
nextAction: 'Recovery accepted. The task is pending again and will be picked up by the internalization consumer (or advance it manually: pd runtime internalization run-once).',
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
catch (err) {
|
|
384
|
+
sendError(res, 500, 'failed_task_recovery_error', errorMessage(err));
|
|
385
|
+
}
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
176
388
|
// Detail: GET /api/v1/failed-tasks/:id
|
|
177
389
|
const id = subPath.replace(/^\//, '');
|
|
178
390
|
if (id.length === 0) {
|
package/console/dist/ui/api.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ApiResponse } from "../types.js";
|
|
2
2
|
import { listActiveSignalKeywords, listPendingSignalTerms, fetchKeywordStore, fetchPendingTerms, updateKeywordStore, admitPendingTerm, rejectPendingTerm } from "./utils/signal-keywords-api.js";
|
|
3
3
|
import type { OwnerGovernanceView } from '@principles/core/runtime-v2';
|
|
4
|
-
import type { FeedbackReportData, FeedbackDraftSummaryData, FeedbackDraftEnvelopeData, FeedbackChannelsData, FeedbackSubmitResultData, DeleteEnvelopeData, WorkspaceEntryData, RemovedEnvelopeData, SyncResultData, ConfigSummaryData, ConfigCatalogData, AgentBindingUpdateData, DefaultRuntimeUpdateData, RuntimeProfileMutationData, ReadinessCheckData, FeatureFlagUpdateData, OutputLanguageData, GovernanceQueueData, ActivationsData, DisableActivationData, LifecycleMetricsData, UpdateStatusData, UpdateHistoryData, ApplyUpdateResultData, RollbackResultData, ApprovalRecordData, PrinciplesListData, ApprovalsGroupedData, EvidenceChainData, TrajectoryData, IntentSummaryData, IntentDecisionRecordData, IntentDecisionResultData, IntentDecisionSummaryData, FollowUpResponseData, IntentInitResultData, IntentSaveResultData, IntentRawContentData, IntentVersionData, RuleCodeOwnerReviewData, RuleCodeMutationData } from "./utils/validators.js";
|
|
4
|
+
import type { FeedbackReportData, FeedbackDraftSummaryData, FeedbackDraftEnvelopeData, FeedbackChannelsData, FeedbackSubmitResultData, DeleteEnvelopeData, WorkspaceEntryData, RemovedEnvelopeData, SyncResultData, ConfigSummaryData, ConfigCatalogData, AgentBindingUpdateData, DefaultRuntimeUpdateData, RuntimeProfileMutationData, ReadinessCheckData, FeatureFlagUpdateData, OutputLanguageData, GovernanceQueueData, RecoveryResultData, ActivationsData, DisableActivationData, LifecycleMetricsData, UpdateStatusData, UpdateHistoryData, ApplyUpdateResultData, RollbackResultData, ApprovalRecordData, PrinciplesListData, ApprovalsGroupedData, EvidenceChainData, TrajectoryData, IntentSummaryData, IntentDecisionRecordData, IntentDecisionResultData, IntentDecisionSummaryData, FollowUpResponseData, IntentInitResultData, IntentSaveResultData, IntentRawContentData, IntentVersionData, RuleCodeOwnerReviewData, RuleCodeMutationData } from "./utils/validators.js";
|
|
5
5
|
declare function getToken(): string | null;
|
|
6
6
|
declare function setToken(token: string): void;
|
|
7
7
|
declare function clearToken(): void;
|
|
@@ -129,6 +129,7 @@ declare function patchFeatureFlag(featureName: string, enabled: boolean): Promis
|
|
|
129
129
|
declare function fetchOutputLanguage(): Promise<ApiResponse<OutputLanguageData>>;
|
|
130
130
|
declare function updateOutputLanguage(outputLanguage: string): Promise<ApiResponse<OutputLanguageData>>;
|
|
131
131
|
declare function fetchGovernanceQueue(): Promise<ApiResponse<GovernanceQueueData>>;
|
|
132
|
+
declare function recoverFailedTask(taskId: string, reason?: string): Promise<ApiResponse<RecoveryResultData>>;
|
|
132
133
|
declare function fetchApprovalsGrouped(): Promise<ApiResponse<ApprovalsGroupedData>>;
|
|
133
134
|
declare function fetchAllActivations(): Promise<ApiResponse<ActivationsData>>;
|
|
134
135
|
declare function disableActivation(activationId: string): Promise<ApiResponse<DisableActivationData>>;
|
|
@@ -195,8 +196,8 @@ export interface FollowUpPayload {
|
|
|
195
196
|
candidateId?: string;
|
|
196
197
|
}
|
|
197
198
|
declare function dispatchFollowUp(decisionId: string, payload: FollowUpPayload): Promise<ApiResponse<FollowUpResponseData>>;
|
|
198
|
-
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, fetchApprovalsGrouped, fetchAllActivations, disableActivation, fetchRuleCodeOwnerReview, ruleCodeDecision, pauseAllRuleCode, releaseRuleCodePause, fetchLifecycleMetrics, fetchUpdateStatus, fetchUpdateHistory, applyUpdate, applyFullUpdate, rollbackUpdate, fetchEvidenceChain, fetchIntentSummary, fetchIntentContent, createIntentTemplate, saveIntentContent, fetchIntentVersions, recordIntentDecision, listIntentDecisionsByPainId, listIntentDecisionsByTaskId, fetchIntentDecisionSummary, dispatchFollowUp, };
|
|
199
|
-
export type { FeedbackReportData, FeedbackDraftSummaryData, FeedbackDraftEnvelopeData, FeedbackChannelsData, FeedbackSubmitResultData, DeleteEnvelopeData, WorkspaceEntryData, RemovedEnvelopeData, SyncResultData, ConfigSummaryData, ConfigCatalogData, AgentBindingUpdateData, DefaultRuntimeUpdateData, RuntimeProfileMutationData, ReadinessCheckData, FeatureFlagUpdateData, OutputLanguageData, GovernanceQueueData, 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";
|
|
199
|
+
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, 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, };
|
|
200
|
+
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";
|
|
200
201
|
export type { SignalKeyword, PendingSignalTerm } from "./utils/signal-keywords-types.js";
|
|
201
202
|
export type { ActivationRecordData as ActivationRecord } from "./utils/validators.js";
|
|
202
203
|
export type { ApprovalRecordData as ApprovalRecord } 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, validateActivations, validateDisableActivation, validateLifecycleMetrics, validateUpdateStatus, validateUpdateHistory, validateApplyUpdateResult, validateRollbackResult, validateApprovalRecordDirect, validatePrinciplesList, validateApprovalsGrouped, validateEvidenceChain, validateTrajectoryData, validateIntentSummary, validateIntentDecisionList, validateIntentDecisionResult, validateIntentDecisionSummary, validateFollowUpResponse, validateIntentInitResult, validateIntentSaveResult, validateIntentRawContent, validateIntentVersions, validateOwnerGovernanceView, validateRuleCodeOwnerReview, validateRuleCodeMutation, } 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, validateActivations, validateDisableActivation, validateLifecycleMetrics, validateUpdateStatus, validateUpdateHistory, validateApplyUpdateResult, validateRollbackResult, validateApprovalRecordDirect, validatePrinciplesList, validateApprovalsGrouped, validateEvidenceChain, validateTrajectoryData, validateIntentSummary, validateIntentDecisionList, validateIntentDecisionResult, validateIntentDecisionSummary, validateFollowUpResponse, validateIntentInitResult, validateIntentSaveResult, validateIntentRawContent, validateIntentVersions, validateOwnerGovernanceView, validateRuleCodeOwnerReview, validateRuleCodeMutation, } from "./utils/validators.js";
|
|
3
3
|
// ── Auth ──────────────────────────────────────────────────────────────────────
|
|
4
4
|
function getToken() {
|
|
5
5
|
return sessionStorage.getItem("pd_token");
|
|
@@ -382,6 +382,18 @@ async function updateOutputLanguage(outputLanguage) {
|
|
|
382
382
|
async function fetchGovernanceQueue() {
|
|
383
383
|
return request('/api/v1/governance/queue', undefined, validateGovernanceQueue);
|
|
384
384
|
}
|
|
385
|
+
// Governance Recovery Actions v1: Owner-triggered recovery of a failed /
|
|
386
|
+
// needs_human_review internalization task (failed→pending | needs_human_review→pending).
|
|
387
|
+
async function recoverFailedTask(taskId, reason) {
|
|
388
|
+
const body = {};
|
|
389
|
+
if (reason !== undefined && reason.length > 0) {
|
|
390
|
+
body.reason = reason;
|
|
391
|
+
}
|
|
392
|
+
return request('/api/v1/failed-tasks/' + encodeURIComponent(taskId) + '/recover', {
|
|
393
|
+
method: 'POST',
|
|
394
|
+
body: JSON.stringify(body),
|
|
395
|
+
}, validateRecoveryResult);
|
|
396
|
+
}
|
|
385
397
|
async function fetchApprovalsGrouped() {
|
|
386
398
|
return request('/api/v1/approvals/grouped', undefined, validateApprovalsGrouped);
|
|
387
399
|
}
|
|
@@ -498,4 +510,4 @@ listActiveSignalKeywords, listPendingSignalTerms, fetchKeywordStore, fetchPendin
|
|
|
498
510
|
// auth
|
|
499
511
|
getToken, setToken, clearToken, checkAuth, approveApproval, rejectApproval, editApproval, fetchPrinciples, fetchPrincipleDetail, fetchPrincipleGovernance, fetchPrincipleTrajectory, fetchPrincipleReceipts, fetchReceiptCounts, archivePrinciple, unarchivePrinciple, createFeedbackReport, listFeedbackReports, getFeedbackReport, deleteFeedbackReport,
|
|
500
512
|
// Feedback submit ladder (Slice 3, spec §8)
|
|
501
|
-
fetchFeedbackChannels, submitFeedbackReport, markFeedbackReportSent, fetchConfigSummary, fetchConfigCatalog, updateAgentBinding, updateDefaultRuntime, createRuntimeProfile, updateRuntimeProfile, deleteRuntimeProfile, fetchAgentReadiness, patchFeatureFlag, fetchOutputLanguage, updateOutputLanguage, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchGovernanceQueue, fetchApprovalsGrouped, fetchAllActivations, disableActivation, fetchRuleCodeOwnerReview, ruleCodeDecision, pauseAllRuleCode, releaseRuleCodePause, fetchLifecycleMetrics, fetchUpdateStatus, fetchUpdateHistory, applyUpdate, applyFullUpdate, rollbackUpdate, fetchEvidenceChain, fetchIntentSummary, fetchIntentContent, createIntentTemplate, saveIntentContent, fetchIntentVersions, recordIntentDecision, listIntentDecisionsByPainId, listIntentDecisionsByTaskId, fetchIntentDecisionSummary, dispatchFollowUp, };
|
|
513
|
+
fetchFeedbackChannels, submitFeedbackReport, markFeedbackReportSent, fetchConfigSummary, fetchConfigCatalog, updateAgentBinding, updateDefaultRuntime, createRuntimeProfile, updateRuntimeProfile, deleteRuntimeProfile, fetchAgentReadiness, patchFeatureFlag, fetchOutputLanguage, updateOutputLanguage, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchGovernanceQueue, 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, };
|
|
@@ -149,6 +149,7 @@
|
|
|
149
149
|
"state_db_missing": "State database not initialized. PD has not run in this workspace yet.",
|
|
150
150
|
"no_pipeline_activity": "No governance activity recorded yet.",
|
|
151
151
|
"pending_approvals": "{{count}} principle(s) pending your review and decision.",
|
|
152
|
+
"tasks_need_human_review": "{{count}} internalization task(s) are waiting for your review decision.",
|
|
152
153
|
"pipeline_active": "Pipeline has activity, but candidate principles are not ready for review.",
|
|
153
154
|
"consumed_candidates": "Candidate records exist, but have not entered the review stage.",
|
|
154
155
|
"degraded_state": "Part of the governance pipeline or data source is degraded."
|
|
@@ -157,6 +158,7 @@
|
|
|
157
158
|
"run_config_doctor": "Ensure the OpenClaw plugin is enabled, or run pd config doctor.",
|
|
158
159
|
"wait_for_pipeline": "PD will surface principle candidates here once behavior deviations are captured and reviewable artifacts are generated.",
|
|
159
160
|
"review_approvals": "Review pending principles and approve, reject, or park.",
|
|
161
|
+
"review_failed_tasks": "Open Failed Tasks to inspect and recover tasks awaiting your decision.",
|
|
160
162
|
"check_degraded_signals": "Check degraded signal details and follow suggested actions.",
|
|
161
163
|
"check_pipeline_status": "Wait for internalization tasks to complete. If no change for a long time, check pipeline status."
|
|
162
164
|
},
|
|
@@ -1366,6 +1368,16 @@
|
|
|
1366
1368
|
"error": "Failed to load failed tasks",
|
|
1367
1369
|
"disabled": "Failed tasks observability is disabled. Enable 'failed_tasks_observability' in .pd/config.yaml to view.",
|
|
1368
1370
|
"createDraft": "Create Feedback Draft",
|
|
1371
|
+
"recover": "Resume Evolution",
|
|
1372
|
+
"recoverConfirmTitle": "Recover this evolution task?",
|
|
1373
|
+
"recoverConfirmStatus": "Current status",
|
|
1374
|
+
"recoverConfirmActionLabel": "Action",
|
|
1375
|
+
"recoverConfirmActionDesc": "Reset the task and allow runtime execution",
|
|
1376
|
+
"recoverConfirmImpactLabel": "Impact",
|
|
1377
|
+
"recoverConfirmImpactDesc": "Existing artifacts are preserved. A new execution attempt will be created.",
|
|
1378
|
+
"recoverConfirmButton": "Recover",
|
|
1379
|
+
"recoverSuccess": "Recovery accepted — the task is pending again",
|
|
1380
|
+
"recoverFailed": "Recovery failed",
|
|
1369
1381
|
"taskId": "Task ID",
|
|
1370
1382
|
"painId": "Pain ID",
|
|
1371
1383
|
"kind": "Kind",
|
|
@@ -149,6 +149,7 @@
|
|
|
149
149
|
"state_db_missing": "状态数据库未初始化。PD 尚未在此工作空间中运行。",
|
|
150
150
|
"no_pipeline_activity": "尚未记录到任何治理链路活动。",
|
|
151
151
|
"pending_approvals": "有 {{count}} 条原则需要你审查并决定是否批准。",
|
|
152
|
+
"tasks_need_human_review": "有 {{count}} 个内化任务正在等待你的审查决定。",
|
|
152
153
|
"pipeline_active": "管线中有活动,但候选原则尚未准备好审查。",
|
|
153
154
|
"consumed_candidates": "已存在候选人记录,但尚未进入审查阶段。",
|
|
154
155
|
"degraded_state": "部分治理链路或数据源处于降级状态。"
|
|
@@ -157,6 +158,7 @@
|
|
|
157
158
|
"run_config_doctor": "确保 OpenClaw 插件已启用,或运行 pd config doctor 检查配置。",
|
|
158
159
|
"wait_for_pipeline": "PD 会在捕获到行为偏差并生成可审查候选后,将其放到这里。",
|
|
159
160
|
"review_approvals": "审查待审批的原则,做出批准、拒绝或暂存的决定。",
|
|
161
|
+
"review_failed_tasks": "打开「失败任务」页面,查看并恢复等待你决定的任务。",
|
|
160
162
|
"check_degraded_signals": "检查降级信号详情,按建议操作修复。",
|
|
161
163
|
"check_pipeline_status": "等待内化任务完成。如果长期无变化,检查内化管线状态。"
|
|
162
164
|
},
|
|
@@ -1366,6 +1368,16 @@
|
|
|
1366
1368
|
"error": "加载失败任务失败",
|
|
1367
1369
|
"disabled": "失败任务可观测性已禁用。请在 .pd/config.yaml 中启用 'failed_tasks_observability' 以查看。",
|
|
1368
1370
|
"createDraft": "创建反馈草稿",
|
|
1371
|
+
"recover": "继续演化",
|
|
1372
|
+
"recoverConfirmTitle": "恢复此演化任务?",
|
|
1373
|
+
"recoverConfirmStatus": "当前状态",
|
|
1374
|
+
"recoverConfirmActionLabel": "恢复动作",
|
|
1375
|
+
"recoverConfirmActionDesc": "重置任务并允许运行时重新执行",
|
|
1376
|
+
"recoverConfirmImpactLabel": "影响",
|
|
1377
|
+
"recoverConfirmImpactDesc": "既有产物将被保留,并将创建一次新的执行尝试。",
|
|
1378
|
+
"recoverConfirmButton": "恢复",
|
|
1379
|
+
"recoverSuccess": "恢复已接受——任务已重新进入待处理状态",
|
|
1380
|
+
"recoverFailed": "恢复失败",
|
|
1369
1381
|
"taskId": "任务 ID",
|
|
1370
1382
|
"painId": "Pain ID",
|
|
1371
1383
|
"kind": "类型",
|