create-principles-disciple 1.136.0 → 1.137.1
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/codex-adapter/dist/worker/workspace-worker.js +3 -0
- package/console/dist/server/routes/failed-tasks.js +31 -3
- package/console/dist/ui/api.d.ts +4 -3
- package/console/dist/ui/api.js +9 -2
- package/console/dist/ui/i18n/en.json +12 -1
- package/console/dist/ui/i18n/zh-CN.json +12 -1
- package/console/dist/ui/pages/failed-tasks/FailedTasksPage.js +80 -4
- package/console/dist/ui/pages/pain/PainPage.js +13 -3
- package/console/dist/ui/utils/validators.d.ts +25 -0
- package/console/dist/ui/utils/validators.js +87 -0
- package/console/dist/web/assets/app.css +29 -2
- package/console/dist/web/assets/app.js +259 -46
- package/core/dist/runtime-v2/index.d.ts +2 -1
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +2 -0
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/artificer-prompt-builder.test.js +75 -5
- package/core/dist/runtime-v2/internalization/__tests__/artificer-prompt-builder.test.js.map +1 -1
- package/core/dist/runtime-v2/internalization/__tests__/evaluator-host-alias-replay.test.d.ts +2 -0
- package/core/dist/runtime-v2/internalization/__tests__/evaluator-host-alias-replay.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/evaluator-host-alias-replay.test.js +279 -0
- package/core/dist/runtime-v2/internalization/__tests__/evaluator-host-alias-replay.test.js.map +1 -0
- package/core/dist/runtime-v2/internalization/__tests__/tool-semantic-registry.test.js +43 -0
- package/core/dist/runtime-v2/internalization/__tests__/tool-semantic-registry.test.js.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-prompt-builder.d.ts +36 -2
- package/core/dist/runtime-v2/internalization/artificer-prompt-builder.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js +64 -7
- package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-runner.d.ts +10 -1
- package/core/dist/runtime-v2/internalization/artificer-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-runner.js +6 -0
- package/core/dist/runtime-v2/internalization/artificer-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts +31 -0
- package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.js +93 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/index.d.ts +2 -1
- package/core/dist/runtime-v2/internalization/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/index.js +2 -0
- package/core/dist/runtime-v2/internalization/index.js.map +1 -1
- package/core/dist/runtime-v2/internalization/tool-semantic-registry.d.ts +7 -0
- package/core/dist/runtime-v2/internalization/tool-semantic-registry.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/tool-semantic-registry.js +9 -0
- package/core/dist/runtime-v2/internalization/tool-semantic-registry.js.map +1 -1
- package/core/dist/runtime-v2/tools/artificer-l2-tool-contract.d.ts.map +1 -1
- package/core/dist/runtime-v2/tools/artificer-l2-tool-contract.js +6 -0
- package/core/dist/runtime-v2/tools/artificer-l2-tool-contract.js.map +1 -1
- package/core/dist/telemetry-event.d.ts +2 -2
- package/core/dist/telemetry-event.d.ts.map +1 -1
- package/core/dist/telemetry-event.js +4 -0
- package/core/dist/telemetry-event.js.map +1 -1
- package/host-runtime/dist/evaluator-runtime-context.d.ts +10 -0
- package/host-runtime/dist/evaluator-runtime-context.js +3 -0
- package/host-runtime/dist/internalization-consumer-cycle.d.ts +6 -0
- package/host-runtime/dist/internalization-consumer-cycle.js +14 -2
- package/package.json +2 -2
- package/pd-cli/dist/commands/pain-record.d.ts +6 -0
- package/pd-cli/dist/commands/pain-record.d.ts.map +1 -1
- package/pd-cli/dist/commands/pain-record.js +78 -29
- package/pd-cli/dist/commands/pain-record.js.map +1 -1
- package/pd-cli/dist/commands/runtime-internalization-run-once.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-internalization-run-once.js +29 -2
- package/pd-cli/dist/commands/runtime-internalization-run-once.js.map +1 -1
- package/pd-cli/dist/index.js +4 -1
- package/pd-cli/dist/index.js.map +1 -1
- package/pd-cli/dist/services/rulehost-pipeline-runner.d.ts.map +1 -1
- package/pd-cli/dist/services/rulehost-pipeline-runner.js +18 -3
- package/pd-cli/dist/services/rulehost-pipeline-runner.js.map +1 -1
- package/pd-cli/package.json +1 -1
- package/plugin/dist/bundle.js +459 -449
- package/plugin/dist/constants/tools.d.ts +13 -5
- package/plugin/dist/governance-audit.js +3 -3
- package/plugin/dist/rulehost-evidence.js +127 -127
- package/plugin/dist/service/internalization-auto-consumer-service.d.ts +13 -0
- package/plugin/openclaw.plugin.json +1 -1
- package/plugin/package.json +1 -1
- package/release-manager/package.json +2 -2
- package/console/dist/ui/utils/format.d.ts +0 -3
- package/console/dist/ui/utils/format.js +0 -20
|
@@ -213,6 +213,9 @@ export async function runCodexWorkspaceWorkerCycle(options) {
|
|
|
213
213
|
logger,
|
|
214
214
|
emitEvent,
|
|
215
215
|
toolSemantics: CODEX_TOOL_SEMANTICS,
|
|
216
|
+
// PRI-741: artificer generation gets the Codex host projection
|
|
217
|
+
// (Bash/apply_patch — evidence-bound, intentionally sparse).
|
|
218
|
+
hostKinds: ['codex'],
|
|
216
219
|
// No hostToolCatalog: PD has not declared a Codex tool catalog; a wrong
|
|
217
220
|
// (OpenClaw) catalog would be worse than none (PRI-630 follow-up).
|
|
218
221
|
});
|
|
@@ -30,6 +30,24 @@ const entries = new Map();
|
|
|
30
30
|
function stateDbExists(workspaceDir) {
|
|
31
31
|
return fs.existsSync(path.join(workspaceDir, '.pd', 'state.db'));
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Decode a percent-encoded path segment into the raw task id (PRI-747 F22
|
|
35
|
+
* review round). The UI client sends `encodeURIComponent(taskId)`; without
|
|
36
|
+
* this decode the store lookup would compare the encoded string ('task%2Fwith
|
|
37
|
+
* %20spaces') against raw ids and 404. Malformed sequences ('%ZZ') are
|
|
38
|
+
* rejected as invalid_encoding rather than thrown (400, same shape as the
|
|
39
|
+
* lifecycle route's decode guard).
|
|
40
|
+
*/
|
|
41
|
+
function decodeTaskIdSegment(raw) {
|
|
42
|
+
if (!raw.includes('%'))
|
|
43
|
+
return { ok: true, value: raw };
|
|
44
|
+
try {
|
|
45
|
+
return { ok: true, value: decodeURIComponent(raw) };
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return { ok: false };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
33
51
|
function getStore(workspaceDir) {
|
|
34
52
|
let entry = entries.get(workspaceDir);
|
|
35
53
|
if (!entry) {
|
|
@@ -371,8 +389,13 @@ export async function handleFailedTasksRoute(req, res, ctx) {
|
|
|
371
389
|
const recoverMatch = subPath.match(/^\/([^/]+)\/recover$/);
|
|
372
390
|
if (recoverMatch) {
|
|
373
391
|
// recoverMatch[1] = captured task id (segment between the leading '/'
|
|
374
|
-
// and the trailing '/recover')
|
|
375
|
-
const
|
|
392
|
+
// and the trailing '/recover'); percent-decoded before store lookup
|
|
393
|
+
const decodedTaskId = decodeTaskIdSegment(recoverMatch[1] ?? '');
|
|
394
|
+
if (!decodedTaskId.ok) {
|
|
395
|
+
sendError(res, 400, 'invalid_encoding', 'task id path segment is not valid percent-encoding');
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
const taskId = decodedTaskId.value;
|
|
376
399
|
// Fail-closed gate: this is a mutation endpoint — recovery is enabled by
|
|
377
400
|
// default (2026-08-24 owner decision) and 403 only when explicitly disabled
|
|
378
401
|
// via config (features.failed_task_recovery_console.enabled: false).
|
|
@@ -441,7 +464,12 @@ export async function handleFailedTasksRoute(req, res, ctx) {
|
|
|
441
464
|
return;
|
|
442
465
|
}
|
|
443
466
|
// Detail: GET /api/v1/failed-tasks/:id
|
|
444
|
-
const
|
|
467
|
+
const decodedId = decodeTaskIdSegment(subPath.replace(/^\//, ''));
|
|
468
|
+
if (!decodedId.ok) {
|
|
469
|
+
sendError(res, 400, 'invalid_encoding', 'task id path segment is not valid percent-encoding');
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
const id = decodedId.value;
|
|
445
473
|
if (id.length === 0) {
|
|
446
474
|
sendNotFound(res, `Route /api/v1/failed-tasks${subPath} not found`);
|
|
447
475
|
return;
|
package/console/dist/ui/api.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { OwnerDecisionsData, OwnerResolutionResultData } from "./utils/vali
|
|
|
3
3
|
import { listActiveSignalKeywords, listPendingSignalTerms, fetchKeywordStore, fetchPendingTerms, updateKeywordStore, admitPendingTerm, rejectPendingTerm } from "./utils/signal-keywords-api.js";
|
|
4
4
|
import type { GovernanceExperienceSnapshot, OwnerGovernanceView } from '@principles/core/runtime-v2';
|
|
5
5
|
import type { OwnerIdentityViewData, OwnerIdentityRegisterData, OwnerIdentityUnregisterData } from "./utils/validators.js";
|
|
6
|
-
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, CorePrinciplesData, ApprovalsGroupedData, EvidenceChainData, TrajectoryData, IntentSummaryData, IntentDecisionRecordData, IntentDecisionResultData, IntentDecisionSummaryData, FollowUpResponseData, IntentInitResultData, IntentSaveResultData, IntentRawContentData, IntentVersionData, RuleCodeOwnerReviewData, RuleCodeMutationData } from "./utils/validators.js";
|
|
6
|
+
import type { FeedbackReportData, FeedbackDraftSummaryData, FeedbackDraftEnvelopeData, FeedbackChannelsData, FeedbackSubmitResultData, DeleteEnvelopeData, WorkspaceEntryData, RemovedEnvelopeData, SyncResultData, ConfigSummaryData, ConfigCatalogData, AgentBindingUpdateData, DefaultRuntimeUpdateData, RuntimeProfileMutationData, ReadinessCheckData, FeatureFlagUpdateData, OutputLanguageData, GovernanceQueueData, RecoveryResultData, FailedTaskDetailData, ActivationsData, DisableActivationData, LifecycleMetricsData, UpdateStatusData, UpdateHistoryData, ApplyUpdateResultData, RollbackResultData, ApprovalRecordData, PrinciplesListData, CorePrinciplesData, ApprovalsGroupedData, EvidenceChainData, TrajectoryData, IntentSummaryData, IntentDecisionRecordData, IntentDecisionResultData, IntentDecisionSummaryData, FollowUpResponseData, IntentInitResultData, IntentSaveResultData, IntentRawContentData, IntentVersionData, RuleCodeOwnerReviewData, RuleCodeMutationData } from "./utils/validators.js";
|
|
7
7
|
declare function getToken(): string | null;
|
|
8
8
|
declare function setToken(token: string): void;
|
|
9
9
|
declare function clearToken(): void;
|
|
@@ -161,6 +161,7 @@ declare function updateOutputLanguage(outputLanguage: string): Promise<ApiRespon
|
|
|
161
161
|
declare function fetchGovernanceQueue(): Promise<ApiResponse<GovernanceQueueData>>;
|
|
162
162
|
declare function fetchGovernanceExperience(): Promise<ApiResponse<GovernanceExperienceSnapshot>>;
|
|
163
163
|
declare function recoverFailedTask(taskId: string, reason?: string, force?: boolean): Promise<ApiResponse<RecoveryResultData>>;
|
|
164
|
+
declare function fetchFailedTaskDetail(taskId: string): Promise<ApiResponse<FailedTaskDetailData>>;
|
|
164
165
|
declare function fetchOwnerDecisions(): Promise<ApiResponse<OwnerDecisionsData>>;
|
|
165
166
|
declare function resolveOwnerDecision(taskId: string, body: {
|
|
166
167
|
action: 'accept_current' | 'revise_once' | 'reject_current';
|
|
@@ -242,8 +243,8 @@ export interface FollowUpPayload {
|
|
|
242
243
|
candidateId?: string;
|
|
243
244
|
}
|
|
244
245
|
declare function dispatchFollowUp(decisionId: string, payload: FollowUpPayload): Promise<ApiResponse<FollowUpResponseData>>;
|
|
245
|
-
export { request, listActiveSignalKeywords, listPendingSignalTerms, fetchKeywordStore, fetchPendingTerms, updateKeywordStore, admitPendingTerm, rejectPendingTerm, getToken, setToken, clearToken, checkAuth, approveApproval, rejectApproval, editApproval, fetchPrinciples, fetchCorePrinciples, 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, };
|
|
246
|
-
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, CorePrinciplesData, CorePrincipleData, ApprovalsGroupedData, EvidenceChainData, EvidenceChainRecordData, EvidenceChainStateData, TrajectoryData, TrajectoryStageData, IntentSummaryData, IntentSectionsData, IntentDocWarningData, IntentDecisionRecordData, IntentDecisionResultData, IntentDecisionSummaryData, FollowUpResponseData, LinkCandidateFollowUpData, GuideRulehostFollowUpData, GeneratePatchProposalFollowUpData, IntentVersionEntry, IntentVersionData, } from "./utils/validators.js";
|
|
246
|
+
export { request, listActiveSignalKeywords, listPendingSignalTerms, fetchKeywordStore, fetchPendingTerms, updateKeywordStore, admitPendingTerm, rejectPendingTerm, getToken, setToken, clearToken, checkAuth, approveApproval, rejectApproval, editApproval, fetchPrinciples, fetchCorePrinciples, 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, fetchFailedTaskDetail, 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, };
|
|
247
|
+
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, FailedTaskDetailData, LifecycleMetricsData, UpdateStatusData, UpdateHistoryData, ApplyUpdateResultData, RollbackResultData, ApprovalRecordData, PrinciplesListData, CorePrinciplesData, CorePrincipleData, ApprovalsGroupedData, EvidenceChainData, EvidenceChainRecordData, EvidenceChainStateData, TrajectoryData, TrajectoryStageData, IntentSummaryData, IntentSectionsData, IntentDocWarningData, IntentDecisionRecordData, IntentDecisionResultData, IntentDecisionSummaryData, FollowUpResponseData, LinkCandidateFollowUpData, GuideRulehostFollowUpData, GeneratePatchProposalFollowUpData, IntentVersionEntry, IntentVersionData, } from "./utils/validators.js";
|
|
247
248
|
export type { SignalKeyword, PendingSignalTerm } from "./utils/signal-keywords-types.js";
|
|
248
249
|
export type { ActivationRecordData as ActivationRecord } from "./utils/validators.js";
|
|
249
250
|
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, validateRecoveryResult, validateOwnerDecisionsData, validateOwnerResolutionResult, validateActivations, validateDisableActivation, validateLifecycleMetrics, validateUpdateStatus, validateUpdateHistory, validateApplyUpdateResult, validateRollbackResult, validateApprovalRecordDirect, validatePrinciplesList, validateCorePrinciples, 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, validateFailedTaskDetail, validateOwnerDecisionsData, validateOwnerResolutionResult, validateActivations, validateDisableActivation, validateLifecycleMetrics, validateUpdateStatus, validateUpdateHistory, validateApplyUpdateResult, validateRollbackResult, validateApprovalRecordDirect, validatePrinciplesList, validateCorePrinciples, 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");
|
|
@@ -474,6 +474,13 @@ async function recoverFailedTask(taskId, reason, force) {
|
|
|
474
474
|
body: JSON.stringify(body),
|
|
475
475
|
}, validateRecoveryResult);
|
|
476
476
|
}
|
|
477
|
+
// PRI-747 F22 / PRI-749: consume the existing single-task detail endpoint
|
|
478
|
+
// (task record + run history). The server route and
|
|
479
|
+
// SqliteTaskStore.getFailedTaskDetail already existed; this wrapper is the
|
|
480
|
+
// missing UI-side consumer the Failed Tasks page expands into.
|
|
481
|
+
async function fetchFailedTaskDetail(taskId) {
|
|
482
|
+
return request('/api/v1/failed-tasks/' + encodeURIComponent(taskId), undefined, validateFailedTaskDetail);
|
|
483
|
+
}
|
|
477
484
|
// PRI-629: unified Owner Decision inbox — read projection + resolution.
|
|
478
485
|
async function fetchOwnerDecisions() {
|
|
479
486
|
return request('/api/v1/governance/owner-decisions', undefined, validateOwnerDecisionsData);
|
|
@@ -597,4 +604,4 @@ listActiveSignalKeywords, listPendingSignalTerms, fetchKeywordStore, fetchPendin
|
|
|
597
604
|
// auth
|
|
598
605
|
getToken, setToken, clearToken, checkAuth, approveApproval, rejectApproval, editApproval, fetchPrinciples, fetchCorePrinciples, fetchPrincipleDetail, fetchPrincipleGovernance, fetchPrincipleTrajectory, fetchPrincipleReceipts, fetchReceiptCounts, archivePrinciple, unarchivePrinciple, createFeedbackReport, listFeedbackReports, getFeedbackReport, deleteFeedbackReport,
|
|
599
606
|
// Feedback submit ladder (Slice 3, spec §8)
|
|
600
|
-
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, };
|
|
607
|
+
fetchFeedbackChannels, submitFeedbackReport, markFeedbackReportSent, fetchConfigSummary, fetchConfigCatalog, updateAgentBinding, updateDefaultRuntime, createRuntimeProfile, updateRuntimeProfile, deleteRuntimeProfile, fetchAgentReadiness, patchFeatureFlag, fetchOutputLanguage, updateOutputLanguage, fetchWorkspaces, addWorkspace, removeWorkspace, syncWorkspace, fetchGovernanceQueue, fetchGovernanceExperience, recoverFailedTask, fetchFailedTaskDetail, 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, };
|
|
@@ -1668,7 +1668,18 @@
|
|
|
1668
1668
|
"attempts": "Attempts",
|
|
1669
1669
|
"lastAttempt": "Last Attempt",
|
|
1670
1670
|
"noPainId": "—",
|
|
1671
|
-
"partialResults": "Showing first {shown} of {total} records. Narrow filters or use 'pd errors list' with --offset to see more."
|
|
1671
|
+
"partialResults": "Showing first {shown} of {total} records. Narrow filters or use 'pd errors list' with --offset to see more.",
|
|
1672
|
+
"detailToggle": "Toggle task detail",
|
|
1673
|
+
"detailError": "Failed to load task detail",
|
|
1674
|
+
"createdAt": "Created",
|
|
1675
|
+
"updatedAt": "Updated",
|
|
1676
|
+
"runsTitle": "Run history",
|
|
1677
|
+
"runAttempt": "Attempt",
|
|
1678
|
+
"runStarted": "Started",
|
|
1679
|
+
"runEnded": "Ended",
|
|
1680
|
+
"runReason": "Reason",
|
|
1681
|
+
"runErrorCategory": "Error category",
|
|
1682
|
+
"noRuns": "No run records for this task."
|
|
1672
1683
|
}
|
|
1673
1684
|
},
|
|
1674
1685
|
"components": {
|
|
@@ -1668,7 +1668,18 @@
|
|
|
1668
1668
|
"attempts": "尝试次数",
|
|
1669
1669
|
"lastAttempt": "最近尝试",
|
|
1670
1670
|
"noPainId": "—",
|
|
1671
|
-
"partialResults": "仅显示前 {shown} 条,共 {total} 条。请缩小筛选范围或使用 'pd errors list --offset' 查看更多。"
|
|
1671
|
+
"partialResults": "仅显示前 {shown} 条,共 {total} 条。请缩小筛选范围或使用 'pd errors list --offset' 查看更多。",
|
|
1672
|
+
"detailToggle": "展开/收起任务详情",
|
|
1673
|
+
"detailError": "加载任务详情失败",
|
|
1674
|
+
"createdAt": "创建时间",
|
|
1675
|
+
"updatedAt": "更新时间",
|
|
1676
|
+
"runsTitle": "执行历史",
|
|
1677
|
+
"runAttempt": "尝试",
|
|
1678
|
+
"runStarted": "开始时间",
|
|
1679
|
+
"runEnded": "结束时间",
|
|
1680
|
+
"runReason": "原因",
|
|
1681
|
+
"runErrorCategory": "错误类别",
|
|
1682
|
+
"noRuns": "该任务暂无执行记录。"
|
|
1672
1683
|
}
|
|
1673
1684
|
},
|
|
1674
1685
|
"components": {
|
|
@@ -18,13 +18,19 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
18
18
|
* /api/v1/failed-tasks/:id/recover. The success state means "Recovery
|
|
19
19
|
* Accepted" — the task is pending again; execution is asynchronous.
|
|
20
20
|
*
|
|
21
|
+
* PRI-747 F22 / PRI-749: each row can be expanded into its detail — the full
|
|
22
|
+
* task record plus run history — fetched lazily from the pre-existing
|
|
23
|
+
* GET /api/v1/failed-tasks/:id endpoint (server route +
|
|
24
|
+
* SqliteTaskStore.getFailedTaskDetail already existed; the UI simply never
|
|
25
|
+
* consumed them).
|
|
26
|
+
*
|
|
21
27
|
* Runtime Contract (rc-*):
|
|
22
28
|
* - rc-1: API response is treated as `unknown` and validated before use
|
|
23
29
|
* - rc-2: no `as` type assertions — runtime type guards only
|
|
24
30
|
* - rc-5: `Object.hasOwn()` for untrusted object keys
|
|
25
31
|
* - rc-9: every error/disabled path surfaces a reason + next action
|
|
26
32
|
*/
|
|
27
|
-
import { useState, useEffect, useCallback } from "react";
|
|
33
|
+
import { useState, useEffect, useCallback, useRef, Fragment } from "react";
|
|
28
34
|
import { useNavigate } from "react-router-dom";
|
|
29
35
|
import { useTranslation } from "react-i18next";
|
|
30
36
|
import { toast } from "sonner";
|
|
@@ -35,8 +41,23 @@ import { Card, CardContent, CardHeader, CardTitle } from "../../components/ui/ca
|
|
|
35
41
|
import { Button } from "../../components/ui/button.js";
|
|
36
42
|
import { AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogFooter, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel, } from "../../components/ui/alert-dialog.js";
|
|
37
43
|
import { ShinyText } from "../../components/ui/shiny-text.js";
|
|
38
|
-
import { formatDate } from "../../utils/format.js";
|
|
39
|
-
import
|
|
44
|
+
import { formatDate } from "../../utils/format-date.js";
|
|
45
|
+
import i18n from "../../i18n/index.js";
|
|
46
|
+
import { recoverFailedTask, fetchConfigSummary, fetchFailedTaskDetail, request } from "../../api.js";
|
|
47
|
+
// Same component set the former utils/format.js rendered, now served by the
|
|
48
|
+
// defensive single-owner formatter: an invalid date string returns the raw
|
|
49
|
+
// input instead of Intl throwing RangeError mid-render (review round of
|
|
50
|
+
// PRI-747 F22 — utils/format.js was retired with this migration).
|
|
51
|
+
const DATETIME_FORMAT_OPTS = {
|
|
52
|
+
year: "numeric",
|
|
53
|
+
month: "2-digit",
|
|
54
|
+
day: "2-digit",
|
|
55
|
+
hour: "2-digit",
|
|
56
|
+
minute: "2-digit",
|
|
57
|
+
};
|
|
58
|
+
function formatDateTime(iso) {
|
|
59
|
+
return formatDate(iso, i18n.language, DATETIME_FORMAT_OPTS);
|
|
60
|
+
}
|
|
40
61
|
// ── Runtime validators (rc-1, rc-2, rc-5) ───────────────────────────────────
|
|
41
62
|
//
|
|
42
63
|
// The API response is `unknown` until these guards run. No `as` casts.
|
|
@@ -280,5 +301,60 @@ function LoadedContent({ data, onCreateDraft, onRecover, t }) {
|
|
|
280
301
|
.replace("{total}", String(data.total)) }))] })] }));
|
|
281
302
|
}
|
|
282
303
|
function TaskTable({ tasks, onCreateDraft, onRecover, t }) {
|
|
283
|
-
|
|
304
|
+
// PRI-747 F22: one row at a time is expanded into its detail panel.
|
|
305
|
+
const [expandedId, setExpandedId] = useState(null);
|
|
306
|
+
const [detail, setDetail] = useState({ status: "idle" });
|
|
307
|
+
// Drop responses from a superseded expand/collapse (rc-7: only the newest
|
|
308
|
+
// request's result may become current state).
|
|
309
|
+
const detailRequestIdRef = useRef(0);
|
|
310
|
+
// Fetch (or refetch) one task's detail; a stale response from a superseded
|
|
311
|
+
// request is dropped (rc-7: only the newest request's result may land).
|
|
312
|
+
const requestDetail = useCallback(async (taskId) => {
|
|
313
|
+
const requestId = ++detailRequestIdRef.current;
|
|
314
|
+
setDetail({ status: "loading", taskId });
|
|
315
|
+
// Lazy fetch on expand: the list view stays cheap; the run history is
|
|
316
|
+
// only loaded when the Owner actually asks for this task's detail.
|
|
317
|
+
const result = await fetchFailedTaskDetail(taskId);
|
|
318
|
+
if (requestId !== detailRequestIdRef.current)
|
|
319
|
+
return;
|
|
320
|
+
if (result.success) {
|
|
321
|
+
setDetail({ status: "loaded", taskId, data: result.data });
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
// rc-9: surface the server-provided reason + next action (404 = the
|
|
325
|
+
// task recovered/changed state after the list was loaded).
|
|
326
|
+
setDetail({ status: "error", taskId, message: result.error, nextAction: result.nextAction });
|
|
327
|
+
}
|
|
328
|
+
}, []);
|
|
329
|
+
const toggleDetail = useCallback((taskId) => {
|
|
330
|
+
if (expandedId === taskId) {
|
|
331
|
+
// Invalidate any in-flight response, then collapse.
|
|
332
|
+
detailRequestIdRef.current += 1;
|
|
333
|
+
setExpandedId(null);
|
|
334
|
+
setDetail({ status: "idle" });
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
setExpandedId(taskId);
|
|
338
|
+
void requestDetail(taskId);
|
|
339
|
+
}, [expandedId, requestDetail]);
|
|
340
|
+
return (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "grid grid-cols-[auto_minmax(0,1fr)_minmax(0,1fr)_auto_minmax(0,1fr)_auto_minmax(0,1fr)_auto_auto] gap-3 pb-2 border-b border-line font-mono text-[10px] uppercase tracking-[0.08em] text-ink-4", children: [_jsx("span", { "aria-hidden": "true" }), _jsx("span", { children: t("pages.failedTasks.taskId") }), _jsx("span", { children: t("pages.failedTasks.painId") }), _jsx("span", { children: t("pages.failedTasks.status") }), _jsx("span", { children: t("pages.failedTasks.lastError") }), _jsx("span", { children: t("pages.failedTasks.attempts") }), _jsx("span", { children: t("pages.failedTasks.lastAttempt") }), _jsx("span", { "aria-hidden": "true" }), _jsx("span", { "aria-hidden": "true" })] }), tasks.map((task) => {
|
|
341
|
+
const isExpanded = expandedId === task.taskId;
|
|
342
|
+
return (_jsxs(Fragment, { children: [_jsxs("div", { className: "grid grid-cols-[auto_minmax(0,1fr)_minmax(0,1fr)_auto_minmax(0,1fr)_auto_minmax(0,1fr)_auto_auto] gap-3 py-2 items-center text-[13px] border-b border-line/50 last:border-b-0", children: [_jsx(Button, { variant: "quiet", size: "icon", className: "h-6 w-6 shrink-0 text-[12px]", onClick: () => toggleDetail(task.taskId), "aria-label": t("pages.failedTasks.detailToggle"), "aria-expanded": isExpanded, "data-testid": `detail-toggle-${task.taskId}`, children: isExpanded ? "▾" : "▸" }), _jsx("span", { className: "font-mono text-ink-2 truncate", title: task.taskId, children: truncateId(task.taskId) }), _jsx("span", { className: "font-mono text-ink-3 truncate", title: task.painId ?? "", children: task.painId ? truncateId(task.painId) : t("pages.failedTasks.noPainId") }), _jsx(Badge, { variant: statusToVariant(task.status), children: task.status }), _jsx("span", { className: "text-ink-3 truncate", title: task.lastError ?? "", children: task.lastError ?? "—" }), _jsx("span", { className: "font-mono text-ink-2 text-center", children: task.attemptCount }), _jsx("span", { className: "font-mono text-ink-3 text-[12px]", children: task.lastAttemptAt ? formatDateTime(task.lastAttemptAt) : "—" }), task.ownerDecisionRequired === true ? (_jsxs("span", { className: "whitespace-nowrap text-[12px]", children: [_jsx("span", { className: "text-amber", children: t("pages.failedTasks.awaitingOwnerDecision") }), " ", _jsx("a", { href: "#/focus", className: "text-gov underline underline-offset-2 hover:text-gov/80", "data-testid": `go-focus-${task.taskId}`, children: t("pages.failedTasks.goGovernanceFocus") })] })) : onRecover ? (_jsx(Button, { variant: "default", size: "sm", onClick: () => onRecover(task), className: "whitespace-nowrap", children: t("pages.failedTasks.recover") })) : null, _jsx(Button, { variant: "outline", size: "sm", onClick: () => onCreateDraft(task), className: "whitespace-nowrap", children: t("pages.failedTasks.createDraft") })] }), isExpanded && (_jsx("div", { className: "border border-line/60 rounded-[var(--radius-md)] bg-panel/60 p-4 mb-2", "data-testid": `detail-panel-${task.taskId}`, children: _jsx(TaskDetailPanel, { state: detail.status === "idle" ? { status: "loading", taskId: task.taskId } : detail, taskId: task.taskId, onRetry: requestDetail, t: t }) }))] }, task.taskId));
|
|
343
|
+
})] }));
|
|
344
|
+
}
|
|
345
|
+
function TaskDetailPanel({ state, taskId, onRetry, t }) {
|
|
346
|
+
if (state.status === "loading") {
|
|
347
|
+
return (_jsx("div", { className: "text-ink-3 text-[13px]", "data-testid": "detail-loading", children: t("common.loading") }));
|
|
348
|
+
}
|
|
349
|
+
if (state.status === "error") {
|
|
350
|
+
// rc-9: reason + next action + an explicit retry affordance
|
|
351
|
+
return (_jsxs("div", { className: "text-[13px] space-y-2", "data-testid": "detail-error", children: [_jsxs("div", { className: "text-danger", children: [t("pages.failedTasks.detailError"), ": ", state.message] }), state.nextAction && _jsx("div", { className: "text-ink-3", children: state.nextAction }), _jsx("div", { children: _jsx(Button, { variant: "outline", size: "sm", onClick: () => onRetry(taskId), children: t("common.refresh") }) })] }));
|
|
352
|
+
}
|
|
353
|
+
// The idle state only occurs if the panel renders for a task whose fetch was
|
|
354
|
+
// superseded mid-flight; treat it as loading for the row just expanded.
|
|
355
|
+
if (state.status === "idle" || state.taskId !== taskId) {
|
|
356
|
+
return (_jsx("div", { className: "text-ink-3 text-[13px]", "data-testid": "detail-loading", children: t("common.loading") }));
|
|
357
|
+
}
|
|
358
|
+
const { data } = state;
|
|
359
|
+
return (_jsxs("div", { className: "space-y-4 text-[13px]", children: [_jsxs("div", { className: "grid grid-cols-[auto_minmax(0,1fr)] gap-x-4 gap-y-1 font-mono text-[12px]", children: [_jsx("span", { className: "text-ink-4 uppercase tracking-[0.08em] self-center", children: t("pages.failedTasks.taskId") }), _jsx("span", { className: "text-ink-2 break-all", children: data.taskId }), _jsx("span", { className: "text-ink-4 uppercase tracking-[0.08em] self-center", children: t("pages.failedTasks.kind") }), _jsx("span", { className: "text-ink-2", children: data.taskKind }), _jsx("span", { className: "text-ink-4 uppercase tracking-[0.08em] self-center", children: t("pages.failedTasks.status") }), _jsx("span", { className: "text-ink-2", children: data.status }), _jsx("span", { className: "text-ink-4 uppercase tracking-[0.08em] self-center", children: t("pages.failedTasks.createdAt") }), _jsx("span", { className: "text-ink-3", children: formatDateTime(data.createdAt) }), data.updatedAt && (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-ink-4 uppercase tracking-[0.08em] self-center", children: t("pages.failedTasks.updatedAt") }), _jsx("span", { className: "text-ink-3", children: formatDateTime(data.updatedAt) })] })), _jsx("span", { className: "text-ink-4 uppercase tracking-[0.08em] self-center", children: t("pages.failedTasks.attempts") }), _jsxs("span", { className: "text-ink-2", children: [data.attemptCount, " / ", data.maxAttempts] }), _jsx("span", { className: "text-ink-4 uppercase tracking-[0.08em] self-center", children: t("pages.failedTasks.lastError") }), _jsx("span", { className: data.lastError ? "text-danger" : "text-ink-3", children: data.lastError ?? "—" })] }), _jsxs("div", { children: [_jsxs("div", { className: "font-mono text-[10px] uppercase tracking-[0.08em] text-ink-4 mb-2", children: [t("pages.failedTasks.runsTitle"), " (", data.runs.length, ")"] }), data.runs.length === 0 ? (_jsx("div", { className: "text-ink-3", children: t("pages.failedTasks.noRuns") })) : (_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "grid grid-cols-[auto_auto_minmax(0,1fr)_minmax(0,1fr)] gap-3 pb-1 border-b border-line/50 font-mono text-[10px] uppercase tracking-[0.08em] text-ink-4", children: [_jsx("span", { children: t("pages.failedTasks.runAttempt") }), _jsx("span", { children: t("pages.failedTasks.status") }), _jsx("span", { children: t("pages.failedTasks.runStarted") }), _jsx("span", { children: t("pages.failedTasks.runReason") })] }), data.runs.map((run) => (_jsxs("div", { className: "grid grid-cols-[auto_auto_minmax(0,1fr)_minmax(0,1fr)] gap-3 py-1 text-[12px] border-b border-line/30 last:border-b-0", title: `${t("pages.failedTasks.runEnded")}: ${run.endedAt ? formatDateTime(run.endedAt) : "—"}${run.errorCategory ? ` · ${t("pages.failedTasks.runErrorCategory")}: ${run.errorCategory}` : ""}`, children: [_jsxs("span", { className: "font-mono text-ink-2", children: ["#", run.attemptNumber] }), _jsx("span", { className: "font-mono text-ink-3", children: run.executionStatus }), _jsx("span", { className: "font-mono text-ink-3", children: formatDateTime(run.startedAt) }), _jsx("span", { className: "text-ink-3 break-words", title: run.reason ?? undefined, children: run.reason ?? (run.errorCategory ?? "—") })] }, run.runId)))] }))] })] }));
|
|
284
360
|
}
|
|
@@ -20,9 +20,19 @@ import { Card, CardContent, CardHeader, CardTitle } from '../../components/ui/ca
|
|
|
20
20
|
import { Button } from '../../components/ui/button.js';
|
|
21
21
|
import { ShinyText } from '../../components/ui/shiny-text.js';
|
|
22
22
|
import { fetchEvidenceChain, recordIntentDecision, listIntentDecisionsByPainId, listIntentDecisionsByTaskId, dispatchFollowUp } from '../../api.js';
|
|
23
|
-
import { formatDate } from '../../utils/format.js';
|
|
23
|
+
import { formatDate } from '../../utils/format-date.js';
|
|
24
|
+
import i18n from '../../i18n/index.js';
|
|
24
25
|
import { buildCardLayers, buildDebugIdSummary, isLayer2EffectivelyEmpty, shouldRenderIntentTensionPanel, shouldRenderFollowUpActions, buildIntentDecisionPayload } from './pain-card-helpers.js';
|
|
25
26
|
import { enumLabel } from '../../utils/enum-labels.js';
|
|
27
|
+
// utils/format.js was retired into the defensive single-owner formatter
|
|
28
|
+
// (format-date.ts) — same datetime components, invalid dates now render the
|
|
29
|
+
// raw string instead of Intl throwing.
|
|
30
|
+
const DATETIME_FORMAT_OPTS = {
|
|
31
|
+
year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit',
|
|
32
|
+
};
|
|
33
|
+
function formatDateTime(iso) {
|
|
34
|
+
return formatDate(iso, i18n.language, DATETIME_FORMAT_OPTS);
|
|
35
|
+
}
|
|
26
36
|
function groupForState(state) {
|
|
27
37
|
switch (state) {
|
|
28
38
|
case 'evidence-only':
|
|
@@ -117,7 +127,7 @@ function LoadedContent({ data, expandedIds, onToggle, onRefresh, t }) {
|
|
|
117
127
|
return (_jsxs("div", { children: [_jsxs("div", { className: "font-mono text-[11px] uppercase tracking-[0.12em] text-ink-4 mb-3", children: [t(`pages.pain.group_${group}`), " (", records.length, ")"] }), _jsxs("div", { className: "space-y-4", children: [others.map((record) => (_jsx(EvidenceChainCard, { record: record, expanded: expandedIds.has(record.id), onToggle: () => onToggle(record.id), t: t }, record.id))), foldReviewQueue && !reviewQueueExpanded && (_jsxs("button", { type: "button", onClick: () => setReviewQueueExpanded(true), className: "w-full text-left p-3 bg-panel border border-line rounded-[var(--radius-md)] text-ink-3 text-sm hover:bg-paper-2 transition-colors", children: [reviewQueueLabel, " \u00B7 ", t('pages.pain.goToGovernance')] })), (!foldReviewQueue || reviewQueueExpanded) && inReview.map((record) => (_jsx(EvidenceChainCard, { record: record, expanded: expandedIds.has(record.id), onToggle: () => onToggle(record.id), t: t }, record.id))), foldReviewQueue && reviewQueueExpanded && (_jsxs("button", { type: "button", onClick: () => setReviewQueueExpanded(false), className: "font-mono text-[11px] text-ink-4 hover:text-ink-3 transition-colors", children: [reviewQueueLabel, " \u25B2"] }))] })] }, group));
|
|
118
128
|
}
|
|
119
129
|
return (_jsxs("div", { children: [_jsxs("div", { className: "font-mono text-[11px] uppercase tracking-[0.12em] text-ink-4 mb-3", children: [t(`pages.pain.group_${group}`), " (", records.length, ")"] }), _jsx("div", { className: "space-y-4", children: records.map((record) => (_jsx(EvidenceChainCard, { record: record, expanded: expandedIds.has(record.id), onToggle: () => onToggle(record.id), t: t }, record.id))) })] }, group));
|
|
120
|
-
}) })), _jsxs("div", { className: "mt-8 font-mono text-[11px] text-ink-4", children: [t('pages.pain.generatedAt'), ": ",
|
|
130
|
+
}) })), _jsxs("div", { className: "mt-8 font-mono text-[11px] text-ink-4", children: [t('pages.pain.generatedAt'), ": ", formatDateTime(data.generatedAt)] })] }));
|
|
121
131
|
}
|
|
122
132
|
// ── Empty state (E section: guide next step, not "暂无数据") ──────────────────
|
|
123
133
|
function EmptyState({ t }) {
|
|
@@ -138,7 +148,7 @@ function EvidenceChainCard({ record, expanded, onToggle, t }) {
|
|
|
138
148
|
return (_jsxs(Card, { className: "overflow-hidden", children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx(Badge, { variant: stateVariant, children: stateLabel }), _jsx(Badge, { variant: "outline", children: sourceLabel }), record.hostKind && record.hostKind !== 'unknown' && (_jsx(Badge, { variant: "secondary", children: t(`pages.pain.hostKind.${record.hostKind}`) })), record.admissionDecision && (_jsx(Badge, { variant: "secondary", children: enumLabel('admission', record.admissionDecision, t) }))] }), _jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [_jsx(Button, { variant: "ghost", size: "sm", "data-testid": `pain-feedback-${record.id}`, onClick: () => {
|
|
139
149
|
const params = new URLSearchParams({ source: "pain_page", painId: record.id });
|
|
140
150
|
navigate(`/report-problem?${params.toString()}`);
|
|
141
|
-
}, children: t('pages.reportProblem.entryFeedback') }), _jsx("span", { className: "font-mono text-[11px] text-ink-4 whitespace-nowrap", children:
|
|
151
|
+
}, children: t('pages.reportProblem.entryFeedback') }), _jsx("span", { className: "font-mono text-[11px] text-ink-4 whitespace-nowrap", children: formatDateTime(record.observedAt) })] })] }) }), _jsxs(CardContent, { children: [_jsxs("div", { className: "space-y-3 mb-4", children: [_jsxs("div", { children: [_jsx("div", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4 mb-1", children: t('pages.pain.fieldTrigger') }), layer2Empty ? (_jsx("p", { className: "text-ink-3 text-sm italic leading-relaxed", children: t('pages.pain.noHumanSummary') })) : (_jsx(CardTitle, { className: "text-[15px] leading-relaxed", children: layers.layer2.triggerSummary }))] }), layers.layer2.conclusion && (_jsxs("div", { children: [_jsx("div", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4 mb-1", children: t('pages.pain.fieldConclusion') }), _jsx("p", { className: "text-ink text-sm leading-relaxed font-medium", children: layers.layer2.conclusion })] })), layers.layer2.applicability && (_jsxs("div", { children: [_jsx("div", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4 mb-1", children: t('pages.pain.fieldApplicability') }), _jsx("p", { className: "text-ink-2 text-sm leading-relaxed", children: layers.layer2.applicability })] })), confidenceDisplay && (_jsxs("div", { children: [_jsx("div", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4 mb-1", children: t('pages.pain.fieldConfidence') }), _jsx("p", { className: "text-ink-2 text-sm", children: confidenceDisplay })] })), layers.layer2.failureReason && (_jsxs("div", { children: [_jsx("div", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4 mb-1", children: t('pages.pain.fieldFailureReason') }), _jsx("p", { className: "text-danger text-sm leading-relaxed", children: layers.layer2.failureReason })] })), layers.layer2.degradedReason && (_jsxs("div", { children: [_jsx("div", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4 mb-1", children: t('pages.pain.fieldDegradedReason') }), _jsx("p", { className: "text-amber text-sm leading-relaxed", children: layers.layer2.degradedReason })] })), layers.layer2.nextAction && (_jsxs("div", { children: [_jsx("div", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4 mb-1", children: t('pages.pain.fieldNextAction') }), _jsx("p", { className: "text-ink-2 text-sm leading-relaxed", children: layers.layer2.nextAction })] }))] }), shouldRenderIntentTensionPanel(layers.layer2.intentTension) && (_jsx(IntentTensionPanel, { tension: layers.layer2.intentTension, recordId: record.id, painId: record.id, taskId: record.linkedTaskId ?? undefined, linkedCandidateId: record.linkedCandidateId ?? undefined, t: t })), _jsxs("div", { className: "mt-4 pt-3 border-t border-line", children: [_jsxs("div", { className: "flex items-center gap-3 flex-wrap", children: [_jsx(Button, { variant: "ghost", size: "sm", onClick: async () => {
|
|
142
152
|
try {
|
|
143
153
|
await navigator.clipboard.writeText(buildDebugIdSummary(record));
|
|
144
154
|
setCopied(true);
|
|
@@ -222,6 +222,31 @@ export interface GovernanceQueueData {
|
|
|
222
222
|
note?: string;
|
|
223
223
|
generatedAt?: string;
|
|
224
224
|
}
|
|
225
|
+
/** One execution attempt of a failed task (RunRecord projection). */
|
|
226
|
+
export interface FailedTaskDetailRun {
|
|
227
|
+
runId: string;
|
|
228
|
+
/** Owning task id — validated to equal the detail's taskId (rc-6 lineage). */
|
|
229
|
+
taskId: string;
|
|
230
|
+
attemptNumber: number;
|
|
231
|
+
executionStatus: string;
|
|
232
|
+
startedAt: string;
|
|
233
|
+
endedAt: string | null;
|
|
234
|
+
reason: string | null;
|
|
235
|
+
errorCategory: string | null;
|
|
236
|
+
}
|
|
237
|
+
/** Validated shape of GET /api/v1/failed-tasks/:id. */
|
|
238
|
+
export interface FailedTaskDetailData {
|
|
239
|
+
taskId: string;
|
|
240
|
+
taskKind: string;
|
|
241
|
+
status: string;
|
|
242
|
+
createdAt: string;
|
|
243
|
+
updatedAt: string;
|
|
244
|
+
attemptCount: number;
|
|
245
|
+
maxAttempts: number;
|
|
246
|
+
lastError: string | null;
|
|
247
|
+
runs: FailedTaskDetailRun[];
|
|
248
|
+
}
|
|
249
|
+
export declare function validateFailedTaskDetail(v: unknown): FailedTaskDetailData | null;
|
|
225
250
|
export interface RecoveryResultData {
|
|
226
251
|
taskId: string;
|
|
227
252
|
previousStatus: string;
|
|
@@ -735,6 +735,93 @@ function validateDegradedSignal(v) {
|
|
|
735
735
|
source: v.source,
|
|
736
736
|
};
|
|
737
737
|
}
|
|
738
|
+
function validateFailedTaskDetailRun(v) {
|
|
739
|
+
if (!isObject(v))
|
|
740
|
+
return null;
|
|
741
|
+
if (!Object.hasOwn(v, 'runId') || !isString(v.runId))
|
|
742
|
+
return null;
|
|
743
|
+
// Required by the RunRecord contract and by the lineage check in
|
|
744
|
+
// validateFailedTaskDetail (rc-6): a run must name the task it belongs to.
|
|
745
|
+
if (!Object.hasOwn(v, 'taskId') || !isString(v.taskId))
|
|
746
|
+
return null;
|
|
747
|
+
if (!Object.hasOwn(v, 'attemptNumber') || !isNumber(v.attemptNumber))
|
|
748
|
+
return null;
|
|
749
|
+
if (!Object.hasOwn(v, 'executionStatus') || !isString(v.executionStatus))
|
|
750
|
+
return null;
|
|
751
|
+
if (!Object.hasOwn(v, 'startedAt') || !isString(v.startedAt))
|
|
752
|
+
return null;
|
|
753
|
+
const endedAt = readNullableString(v, 'endedAt');
|
|
754
|
+
if (!endedAt.valid)
|
|
755
|
+
return null;
|
|
756
|
+
const reason = readNullableString(v, 'reason');
|
|
757
|
+
if (!reason.valid)
|
|
758
|
+
return null;
|
|
759
|
+
const errorCategory = readNullableString(v, 'errorCategory');
|
|
760
|
+
if (!errorCategory.valid)
|
|
761
|
+
return null;
|
|
762
|
+
return {
|
|
763
|
+
runId: v.runId,
|
|
764
|
+
taskId: v.taskId,
|
|
765
|
+
attemptNumber: v.attemptNumber,
|
|
766
|
+
executionStatus: v.executionStatus,
|
|
767
|
+
startedAt: v.startedAt,
|
|
768
|
+
endedAt: endedAt.value,
|
|
769
|
+
reason: reason.value,
|
|
770
|
+
errorCategory: errorCategory.value,
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
export function validateFailedTaskDetail(v) {
|
|
774
|
+
if (!isObject(v))
|
|
775
|
+
return null;
|
|
776
|
+
if (!Object.hasOwn(v, 'task') || !isObject(v.task))
|
|
777
|
+
return null;
|
|
778
|
+
if (!Object.hasOwn(v, 'runs') || !Array.isArray(v.runs))
|
|
779
|
+
return null;
|
|
780
|
+
const t = v.task;
|
|
781
|
+
if (!Object.hasOwn(t, 'taskId') || !isString(t.taskId))
|
|
782
|
+
return null;
|
|
783
|
+
if (!Object.hasOwn(t, 'taskKind') || !isString(t.taskKind))
|
|
784
|
+
return null;
|
|
785
|
+
if (!Object.hasOwn(t, 'status') || !isString(t.status))
|
|
786
|
+
return null;
|
|
787
|
+
if (!Object.hasOwn(t, 'createdAt') || !isString(t.createdAt))
|
|
788
|
+
return null;
|
|
789
|
+
// updatedAt is required on the server's TaskRecord (typebox: no .optional())
|
|
790
|
+
// — required here too, so contract drift reaches the validation-error state
|
|
791
|
+
// instead of a silently partial record (rc-3).
|
|
792
|
+
if (!Object.hasOwn(t, 'updatedAt') || !isString(t.updatedAt))
|
|
793
|
+
return null;
|
|
794
|
+
if (!Object.hasOwn(t, 'attemptCount') || !isNumber(t.attemptCount))
|
|
795
|
+
return null;
|
|
796
|
+
if (!Object.hasOwn(t, 'maxAttempts') || !isNumber(t.maxAttempts))
|
|
797
|
+
return null;
|
|
798
|
+
const taskLastError = readNullableString(t, 'lastError');
|
|
799
|
+
if (!taskLastError.valid)
|
|
800
|
+
return null;
|
|
801
|
+
const topLastError = readNullableString(v, 'lastError');
|
|
802
|
+
if (!topLastError.valid)
|
|
803
|
+
return null;
|
|
804
|
+
const runs = validateArray(v.runs, validateFailedTaskDetailRun);
|
|
805
|
+
if (runs === null)
|
|
806
|
+
return null;
|
|
807
|
+
// rc-6-lineage-consistency: every run must belong to THIS task — a detail
|
|
808
|
+
// response mixing another task's runs would display foreign failure reasons.
|
|
809
|
+
for (const run of runs) {
|
|
810
|
+
if (run.taskId !== t.taskId)
|
|
811
|
+
return null;
|
|
812
|
+
}
|
|
813
|
+
return {
|
|
814
|
+
taskId: t.taskId,
|
|
815
|
+
taskKind: t.taskKind,
|
|
816
|
+
status: t.status,
|
|
817
|
+
createdAt: t.createdAt,
|
|
818
|
+
updatedAt: t.updatedAt,
|
|
819
|
+
attemptCount: t.attemptCount,
|
|
820
|
+
maxAttempts: t.maxAttempts,
|
|
821
|
+
lastError: topLastError.value ?? taskLastError.value,
|
|
822
|
+
runs,
|
|
823
|
+
};
|
|
824
|
+
}
|
|
738
825
|
export function validateRecoveryResult(v) {
|
|
739
826
|
if (!isObject(v))
|
|
740
827
|
return null;
|
|
@@ -957,8 +957,14 @@
|
|
|
957
957
|
.grid-cols-\[auto_1fr\] {
|
|
958
958
|
grid-template-columns: auto 1fr;
|
|
959
959
|
}
|
|
960
|
-
.grid-cols-\[
|
|
961
|
-
grid-template-columns:
|
|
960
|
+
.grid-cols-\[auto_auto_minmax\(0\,1fr\)_minmax\(0\,1fr\)\] {
|
|
961
|
+
grid-template-columns: auto auto minmax(0,1fr) minmax(0,1fr);
|
|
962
|
+
}
|
|
963
|
+
.grid-cols-\[auto_minmax\(0\,1fr\)\] {
|
|
964
|
+
grid-template-columns: auto minmax(0,1fr);
|
|
965
|
+
}
|
|
966
|
+
.grid-cols-\[auto_minmax\(0\,1fr\)_minmax\(0\,1fr\)_auto_minmax\(0\,1fr\)_auto_minmax\(0\,1fr\)_auto_auto\] {
|
|
967
|
+
grid-template-columns: auto minmax(0,1fr) minmax(0,1fr) auto minmax(0,1fr) auto minmax(0,1fr) auto auto;
|
|
962
968
|
}
|
|
963
969
|
.flex-col {
|
|
964
970
|
flex-direction: column;
|
|
@@ -1099,6 +1105,9 @@
|
|
|
1099
1105
|
-moz-column-gap: 14px;
|
|
1100
1106
|
column-gap: 14px;
|
|
1101
1107
|
}
|
|
1108
|
+
.gap-y-1 {
|
|
1109
|
+
row-gap: var(--spacing);
|
|
1110
|
+
}
|
|
1102
1111
|
.gap-y-2 {
|
|
1103
1112
|
row-gap: calc(var(--spacing) * 2);
|
|
1104
1113
|
}
|
|
@@ -1108,6 +1117,9 @@
|
|
|
1108
1117
|
.gap-y-\[12px\] {
|
|
1109
1118
|
row-gap: 12px;
|
|
1110
1119
|
}
|
|
1120
|
+
.self-center {
|
|
1121
|
+
align-self: center;
|
|
1122
|
+
}
|
|
1111
1123
|
.truncate {
|
|
1112
1124
|
overflow: hidden;
|
|
1113
1125
|
text-overflow: ellipsis;
|
|
@@ -1380,6 +1392,12 @@
|
|
|
1380
1392
|
.border-line-2 {
|
|
1381
1393
|
border-color: var(--color-line-2);
|
|
1382
1394
|
}
|
|
1395
|
+
.border-line\/30 {
|
|
1396
|
+
border-color: color-mix(in srgb, #d7d1c4 30%, transparent);
|
|
1397
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
1398
|
+
border-color: color-mix(in oklab, var(--color-line) 30%, transparent);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1383
1401
|
.border-line\/50 {
|
|
1384
1402
|
border-color: color-mix(in srgb, #d7d1c4 50%, transparent);
|
|
1385
1403
|
@supports (color: color-mix(in lab, red, red)) {
|
|
@@ -1569,6 +1587,12 @@
|
|
|
1569
1587
|
.bg-panel {
|
|
1570
1588
|
background-color: var(--color-panel);
|
|
1571
1589
|
}
|
|
1590
|
+
.bg-panel\/60 {
|
|
1591
|
+
background-color: color-mix(in srgb, #fffdf7 60%, transparent);
|
|
1592
|
+
@supports (color: color-mix(in lab, red, red)) {
|
|
1593
|
+
background-color: color-mix(in oklab, var(--color-panel) 60%, transparent);
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1572
1596
|
.bg-paper {
|
|
1573
1597
|
background-color: var(--color-paper);
|
|
1574
1598
|
}
|
|
@@ -2065,6 +2089,9 @@
|
|
|
2065
2089
|
--tw-tracking: var(--tracking-wider);
|
|
2066
2090
|
letter-spacing: var(--tracking-wider);
|
|
2067
2091
|
}
|
|
2092
|
+
.break-words {
|
|
2093
|
+
overflow-wrap: break-word;
|
|
2094
|
+
}
|
|
2068
2095
|
.break-all {
|
|
2069
2096
|
word-break: break-all;
|
|
2070
2097
|
}
|