oh-my-knowledge 0.47.0 → 0.49.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/README.md +50 -18
- package/README.zh.md +55 -23
- package/dist/analysis/coverage-analyzer.d.ts +1 -0
- package/dist/analysis/coverage-analyzer.js +125 -62
- package/dist/analysis/failure-clusterer.js +2 -1
- package/dist/analysis/gap-analyzer.d.ts +2 -2
- package/dist/analysis/gap-analyzer.js +13 -3
- package/dist/analysis/hedging-classifier.d.ts +2 -2
- package/dist/analysis/hedging-classifier.js +3 -4
- package/dist/analysis/report-diagnostics.js +9 -7
- package/dist/analysis/sample-diagnostics.js +6 -6
- package/dist/artifact-graph/doctor.js +15 -7
- package/dist/assets/agent-skills/omk/SKILL.md +27 -7
- package/dist/assets/agent-skills/omk/references/commands.md +19 -17
- package/dist/authoring/evolver.d.ts +10 -6
- package/dist/authoring/evolver.js +496 -83
- package/dist/authoring/generator.d.ts +3 -3
- package/dist/authoring/generator.js +5 -10
- package/dist/authoring/sample-fixer.d.ts +8 -6
- package/dist/authoring/sample-fixer.js +76 -5
- package/dist/cli/commands/doctor.js +31 -14
- package/dist/cli/commands/eval/index.d.ts +3 -0
- package/dist/cli/commands/eval/index.js +163 -17
- package/dist/cli/commands/evolve.d.ts +4 -4
- package/dist/cli/commands/evolve.js +27 -13
- package/dist/cli/commands/init.js +16 -3
- package/dist/cli/commands/observe/inbox.js +44 -21
- package/dist/cli/commands/observe/index.js +20 -11
- package/dist/cli/commands/observe/ingest.d.ts +3 -0
- package/dist/cli/commands/observe/ingest.js +35 -4
- package/dist/cli/commands/sample.d.ts +9 -3
- package/dist/cli/commands/sample.js +91 -74
- package/dist/cli/lib/cmd-flags.d.ts +1 -0
- package/dist/cli/lib/codex-model-hint.d.ts +9 -0
- package/dist/cli/lib/codex-model-hint.js +45 -0
- package/dist/cli/lib/generation-failure-hint.d.ts +2 -0
- package/dist/cli/lib/generation-failure-hint.js +61 -0
- package/dist/cli/lib/i18n-dict/common.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/common.js +4 -0
- package/dist/cli/lib/i18n-dict/gen.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/gen.js +38 -6
- package/dist/cli/lib/i18n-dict/help.js +6 -6
- package/dist/cli/lib/i18n-dict/init.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/init.js +13 -9
- package/dist/cli/lib/i18n-dict/run.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/run.js +34 -2
- package/dist/cli/lib/llm-failure-classifier.d.ts +2 -0
- package/dist/cli/lib/llm-failure-classifier.js +8 -0
- package/dist/cli/lib/parse-run-config.d.ts +6 -5
- package/dist/cli/lib/parse-run-config.js +16 -9
- package/dist/cli/lib/runtime-defaults.d.ts +21 -0
- package/dist/cli/lib/runtime-defaults.js +79 -0
- package/dist/diagnosis/observe-mapper.js +14 -15
- package/dist/diagnosis/observe-producer.js +3 -1
- package/dist/diagnosis/studio-projection.js +14 -7
- package/dist/diagnosis/types.d.ts +2 -0
- package/dist/diagnosis/types.js +12 -0
- package/dist/doctor/endpoint-rule.js +2 -1
- package/dist/eval-core/artifact-file-names.js +18 -1
- package/dist/eval-core/artifact-index.d.ts +7 -11
- package/dist/eval-core/artifact-index.js +139 -80
- package/dist/eval-core/cache.d.ts +12 -3
- package/dist/eval-core/cache.js +89 -29
- package/dist/eval-core/comparability.js +10 -6
- package/dist/eval-core/evaluation-execution.d.ts +2 -1
- package/dist/eval-core/evaluation-execution.js +122 -37
- package/dist/eval-core/evaluation-job.d.ts +4 -1
- package/dist/eval-core/evaluation-job.js +4 -1
- package/dist/eval-core/evaluation-reporting.d.ts +15 -13
- package/dist/eval-core/evaluation-reporting.js +54 -52
- package/dist/eval-core/execution-strategy.d.ts +2 -0
- package/dist/eval-core/execution-strategy.js +11 -9
- package/dist/eval-core/fact-checker.js +15 -7
- package/dist/eval-core/holdout.js +3 -2
- package/dist/eval-core/judge-independence.d.ts +2 -2
- package/dist/eval-core/mock-hook.cjs +23 -6
- package/dist/eval-core/mocks-runtime.js +30 -8
- package/dist/eval-core/report-document.d.ts +12 -0
- package/dist/eval-core/report-document.js +1151 -0
- package/dist/eval-core/report-extensions.d.ts +4 -0
- package/dist/eval-core/report-extensions.js +500 -0
- package/dist/eval-core/report-file-migration.js +7 -2
- package/dist/eval-core/resume-compatibility.d.ts +31 -0
- package/dist/eval-core/resume-compatibility.js +141 -0
- package/dist/eval-core/sample-fingerprint.d.ts +12 -0
- package/dist/eval-core/sample-fingerprint.js +193 -0
- package/dist/eval-core/schema.js +86 -31
- package/dist/eval-core/verdict.d.ts +8 -4
- package/dist/eval-core/verdict.js +24 -10
- package/dist/eval-workflows/batch-evaluation-workflow.d.ts +2 -1
- package/dist/eval-workflows/batch-evaluation-workflow.js +25 -12
- package/dist/eval-workflows/evaluation-pipeline/preflight-warnings.d.ts +10 -5
- package/dist/eval-workflows/evaluation-pipeline/preflight-warnings.js +58 -21
- package/dist/eval-workflows/evaluation-pipeline/report-finalize.js +3 -1
- package/dist/eval-workflows/evaluation-pipeline/run-state.d.ts +4 -1
- package/dist/eval-workflows/evaluation-pipeline/run-state.js +4 -1
- package/dist/eval-workflows/evaluation-pipeline/test-set-hash.d.ts +6 -5
- package/dist/eval-workflows/evaluation-pipeline/test-set-hash.js +17 -10
- package/dist/eval-workflows/evaluation-pipeline.js +12 -7
- package/dist/eval-workflows/run-evaluation.d.ts +9 -7
- package/dist/eval-workflows/run-evaluation.js +79 -51
- package/dist/executors/anthropic-api.js +65 -9
- package/dist/executors/claude-cli.js +16 -79
- package/dist/executors/claude-protocol.d.ts +28 -0
- package/dist/executors/claude-protocol.js +180 -0
- package/dist/executors/claude-sdk-trace.js +56 -28
- package/dist/executors/claude-sdk.d.ts +1 -0
- package/dist/executors/claude-sdk.js +39 -93
- package/dist/executors/codex-cli-trace.js +166 -31
- package/dist/executors/codex-cli.d.ts +6 -8
- package/dist/executors/codex-cli.js +49 -151
- package/dist/executors/codex-protocol.d.ts +24 -0
- package/dist/executors/codex-protocol.js +234 -0
- package/dist/executors/codex-sdk.js +68 -120
- package/dist/executors/gemini.js +88 -13
- package/dist/executors/index.d.ts +2 -3
- package/dist/executors/index.js +5 -3
- package/dist/executors/openai-api.js +70 -9
- package/dist/executors/runtime-fingerprint.js +88 -11
- package/dist/executors/script-command.d.ts +8 -0
- package/dist/executors/script-command.js +87 -0
- package/dist/executors/script.js +202 -29
- package/dist/executors/shared.d.ts +35 -3
- package/dist/executors/shared.js +113 -15
- package/dist/grading/assertions.d.ts +1 -1
- package/dist/grading/assertions.js +19 -9
- package/dist/grading/diagnostic.d.ts +9 -2
- package/dist/grading/diagnostic.js +25 -2
- package/dist/grading/index.js +10 -4
- package/dist/grading/judge.js +19 -6
- package/dist/grading/layered-scores.d.ts +2 -3
- package/dist/grading/layered-scores.js +2 -3
- package/dist/inputs/load-samples.d.ts +1 -2
- package/dist/inputs/load-samples.js +23 -1
- package/dist/inputs/mcp-resolver.js +6 -3
- package/dist/inputs/sample-document.d.ts +11 -0
- package/dist/inputs/sample-document.js +96 -0
- package/dist/managed/evidence.d.ts +1 -0
- package/dist/managed/evidence.js +1 -1
- package/dist/managed/store.js +200 -91
- package/dist/observability/codex-trace-adapter.d.ts +5 -0
- package/dist/observability/codex-trace-adapter.js +850 -0
- package/dist/observability/experience.d.ts +32 -6
- package/dist/observability/experience.js +2695 -459
- package/dist/observability/feedback-matchers.js +16 -1
- package/dist/observability/inbox-view-model.d.ts +2 -1
- package/dist/observability/inbox-view-model.js +20 -14
- package/dist/observability/inbox.d.ts +7 -1
- package/dist/observability/inbox.js +632 -124
- package/dist/observability/problem-patterns.js +2 -0
- package/dist/observability/review-state.d.ts +6 -0
- package/dist/observability/review-state.js +235 -63
- package/dist/observability/skill-chain-advisories.js +1 -1
- package/dist/observability/skill-chain.js +17 -4
- package/dist/observability/skill-health-analyzer.d.ts +32 -7
- package/dist/observability/skill-health-analyzer.js +194 -121
- package/dist/observability/skill-health-report.d.ts +10 -0
- package/dist/observability/skill-health-report.js +620 -0
- package/dist/observability/soft-standards/constants.d.ts +0 -1
- package/dist/observability/soft-standards/constants.js +0 -1
- package/dist/observability/soft-standards/index.d.ts +1 -1
- package/dist/observability/soft-standards/index.js +1 -1
- package/dist/observability/soft-standards/llm-extractor.js +8 -10
- package/dist/observability/soft-standards/skill-standards-store.d.ts +2 -1
- package/dist/observability/soft-standards/skill-standards-store.js +59 -18
- package/dist/observability/soft-standards/types.d.ts +2 -2
- package/dist/observability/trace-adapter.d.ts +12 -7
- package/dist/observability/trace-adapter.js +11 -9
- package/dist/observability/trace-attribution.d.ts +13 -5
- package/dist/observability/trace-attribution.js +315 -21
- package/dist/observability/trace-ingestion.d.ts +9 -0
- package/dist/observability/trace-ingestion.js +80 -0
- package/dist/observability/trace-ir.d.ts +113 -0
- package/dist/observability/trace-ir.js +87 -0
- package/dist/observability/trace-segmenter.d.ts +19 -6
- package/dist/observability/trace-segmenter.js +377 -196
- package/dist/observability/trace-session-index.d.ts +19 -0
- package/dist/observability/trace-session-index.js +68 -0
- package/dist/observability/trace-source.d.ts +12 -4
- package/dist/observability/trace-source.js +939 -215
- package/dist/renderer/html-renderer.js +37 -6
- package/dist/renderer/icons.js +3 -0
- package/dist/renderer/observation-inbox-renderer.js +227 -91
- package/dist/renderer/skill-detail-renderer.js +452 -109
- package/dist/renderer/skill-health-renderer.js +69 -12
- package/dist/renderer/summary.js +28 -7
- package/dist/renderer/table.js +21 -4
- package/dist/renderer/test-view.d.ts +1 -0
- package/dist/renderer/test-view.js +44 -9
- package/dist/server/indexed-report-store.js +14 -18
- package/dist/server/job-store.js +64 -26
- package/dist/server/report-server.js +190 -78
- package/dist/server/report-store.js +57 -80
- package/dist/server/skill-index.js +143 -49
- package/dist/server/skill-insights.js +44 -5
- package/dist/shared/artifact-graph.d.ts +3 -0
- package/dist/shared/artifact-graph.js +224 -0
- package/dist/shared/assertion-types.d.ts +8 -0
- package/dist/shared/assertion-types.js +46 -0
- package/dist/shared/atomic-json.d.ts +8 -0
- package/dist/shared/atomic-json.js +33 -0
- package/dist/shared/diagnosis-schema.d.ts +9 -0
- package/dist/shared/diagnosis-schema.js +181 -0
- package/dist/shared/doctor-report.d.ts +3 -0
- package/dist/shared/doctor-report.js +103 -0
- package/dist/shared/evaluation-job.d.ts +6 -0
- package/dist/shared/evaluation-job.js +217 -0
- package/dist/shared/executor-result.d.ts +17 -0
- package/dist/shared/executor-result.js +221 -0
- package/dist/shared/file-lock.d.ts +12 -0
- package/dist/shared/file-lock.js +129 -0
- package/dist/shared/json-value.d.ts +5 -0
- package/dist/shared/json-value.js +36 -0
- package/dist/shared/keyed-mutex.d.ts +7 -0
- package/dist/shared/keyed-mutex.js +24 -0
- package/dist/shared/record-count.d.ts +8 -0
- package/dist/shared/record-count.js +43 -0
- package/dist/shared/sample-contract.d.ts +3 -0
- package/dist/shared/sample-contract.js +332 -0
- package/dist/shared/shell-quote.d.ts +2 -0
- package/dist/shared/shell-quote.js +7 -0
- package/dist/shared/timestamp.d.ts +6 -0
- package/dist/shared/timestamp.js +64 -0
- package/dist/shared/token-usage.d.ts +19 -0
- package/dist/shared/token-usage.js +50 -0
- package/dist/shared/tool-call-status.d.ts +8 -0
- package/dist/shared/tool-call-status.js +28 -0
- package/dist/shared/tool-identity.d.ts +21 -0
- package/dist/shared/tool-identity.js +84 -0
- package/dist/shared/tool-search.js +73 -16
- package/dist/shared/trace-projection.d.ts +5 -0
- package/dist/shared/trace-projection.js +20 -0
- package/dist/shared/trace-source-kind.d.ts +3 -0
- package/dist/shared/trace-source-kind.js +12 -0
- package/dist/types/diagnosis.d.ts +2 -0
- package/dist/types/eval.d.ts +4 -0
- package/dist/types/executor.d.ts +32 -5
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/judge.d.ts +2 -0
- package/dist/types/observability.d.ts +116 -9
- package/dist/types/report.d.ts +58 -6
- package/dist/types/skill-index.d.ts +7 -0
- package/dist/types/trace.d.ts +2 -0
- package/dist/types/trace.js +1 -0
- package/package.json +9 -5
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { DEFAULT_LANG, layout, e } from './layout.js';
|
|
2
|
-
import { severityReasonFor, observationMetricAnnotationTargetId, getSkillChainAdvisory, resolveAdvisoryCommand, ASSISTANT_DELIVERABLE_ARTIFACT_RE, hasAssistantDeliverableArtifactText, hasAssistantDeliverySignalText, hasUserHardRuleText, HARD_RULE_TEXT_RE, isAssistantProgressUpdateText, isScheduledTaskPromptText, isSyntheticUserMessageText, isUserInteractionMetricText, } from '../observability/inbox-view-model.js';
|
|
2
|
+
import { severityReasonFor, observationMetricAnnotationEntry, observationMetricAnnotationTargetId, getSkillChainAdvisory, resolveAdvisoryCommand, ASSISTANT_DELIVERABLE_ARTIFACT_RE, hasAssistantDeliverableArtifactText, hasAssistantDeliverySignalText, hasUserHardRuleText, HARD_RULE_TEXT_RE, isAssistantProgressUpdateText, isScheduledTaskPromptText, isSyntheticUserMessageText, isUserInteractionMetricText, } from '../observability/inbox-view-model.js';
|
|
3
3
|
import { findNegativeFeedbackMatches, findPositiveFeedbackMatches, findUserCorrectionMatches, findUserGoalShiftMatches, hasUserCorrectionSignal, hasUserGoalShiftSignal } from '../observability/feedback-projection.js';
|
|
4
4
|
import { durationMsBetween } from '../shared/time.js';
|
|
5
|
+
import { incrementRecordCount } from '../shared/record-count.js';
|
|
5
6
|
import { OBSERVATION_INBOX_STYLES } from './observation-inbox/styles.js';
|
|
6
7
|
import { experienceSkillAnchor, formatTimeRange as formatTimeRangeImpl, formatTimestamp, renderArtifactVersion, renderField, renderJson, renderFeedbackAttributionLabel, skillAnchor, truncateText, } from './observation-inbox/helpers.js';
|
|
8
|
+
import { shellQuoteArg } from '../shared/shell-quote.js';
|
|
7
9
|
export { renderFeedbackAttributionLabel } from './observation-inbox/helpers.js';
|
|
8
10
|
export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
9
|
-
const { activeSkill, allItems, items, reports, experienceReports, skillInvocationCounts, skillSessionCounts, skillInvocationLastSeen, skillToolCallCounts, skillChains, skillDerivedStandards, totalSkillInvocations, severitySkillCounts, skillCount, reportCount, latestSeenLabel, reviewState, resolvedReviewSessions, } = model;
|
|
11
|
+
const { observationsDir, activeSkill, allItems, items, reports, experienceReports, skillInvocationCounts, skillSessionCounts, skillInvocationLastSeen, skillToolCallCounts, skillChains, skillDerivedStandards, totalSkillInvocations, severitySkillCounts, skillCount, reportCount, latestSeenLabel, reviewState, resolvedReviewSessions, } = model;
|
|
10
12
|
const experience = experienceReports.find((report) => report.skills.length > 0 || report.sessions.length > 0 || report.invocations.length > 0);
|
|
11
13
|
const pageTitle = activeSkill ? `Observe Inbox · ${activeSkill}` : 'Observe Inbox';
|
|
12
14
|
const experienceToolCountsBySkill = new Map();
|
|
@@ -16,18 +18,18 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
16
18
|
for (const invocation of experience?.invocations ?? []) {
|
|
17
19
|
const toolCounts = experienceToolCountsBySkill.get(invocation.skillName) ?? {};
|
|
18
20
|
for (const [tool, count] of Object.entries(invocation.toolCounts ?? {})) {
|
|
19
|
-
toolCounts
|
|
21
|
+
incrementRecordCount(toolCounts, tool, count);
|
|
20
22
|
}
|
|
21
23
|
experienceToolCountsBySkill.set(invocation.skillName, toolCounts);
|
|
22
24
|
const entrypoint = invocation.entrypoint ?? invocation.sourceKind ?? 'unknown';
|
|
23
25
|
const entrypointCounts = experienceEntrypointCountsBySkill.get(invocation.skillName) ?? {};
|
|
24
|
-
entrypointCounts
|
|
26
|
+
incrementRecordCount(entrypointCounts, entrypoint);
|
|
25
27
|
experienceEntrypointCountsBySkill.set(invocation.skillName, entrypointCounts);
|
|
26
28
|
const originLabel = invocation.attribution.pluginName
|
|
27
29
|
? `Skill 来源:插件 ${invocation.attribution.pluginName}`
|
|
28
30
|
: 'Skill 来源:本地 skill';
|
|
29
31
|
const originCounts = experienceSkillOriginCountsBySkill.get(invocation.skillName) ?? {};
|
|
30
|
-
originCounts
|
|
32
|
+
incrementRecordCount(originCounts, originLabel);
|
|
31
33
|
experienceSkillOriginCountsBySkill.set(invocation.skillName, originCounts);
|
|
32
34
|
const attributionMethod = invocation.attribution.source === 'command-name'
|
|
33
35
|
? '通过斜杠命令触发'
|
|
@@ -43,7 +45,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
43
45
|
const attributionTarget = invocation.attribution.commandName ?? invocation.attribution.rawSkillRef ?? invocation.skillName;
|
|
44
46
|
const attributionLabel = attributionTarget ? `${attributionMethod}:${attributionTarget}` : attributionMethod;
|
|
45
47
|
const attributionCounts = experienceAttributionCountsBySkill.get(invocation.skillName) ?? {};
|
|
46
|
-
attributionCounts
|
|
48
|
+
incrementRecordCount(attributionCounts, attributionLabel);
|
|
47
49
|
experienceAttributionCountsBySkill.set(invocation.skillName, attributionCounts);
|
|
48
50
|
}
|
|
49
51
|
const countSkillsBySeverity = (...severities) => new Set(allItems.filter((item) => severities.includes(item.severity)).map((item) => item.skillName)).size;
|
|
@@ -52,6 +54,27 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
52
54
|
const latestInvocationLabel = lang === 'zh' ? '最近调用' : 'Latest invocation';
|
|
53
55
|
const invocationWindowLabel = lang === 'zh' ? '调用窗口' : 'Invocation window';
|
|
54
56
|
const formatTimeRange = (start, end, durationMs) => formatTimeRangeImpl(start, end, durationMs, lang);
|
|
57
|
+
const timestampedOccurrences = (item) => item.timestampedOccurrences
|
|
58
|
+
?? (item.firstSeen === '1970-01-01T00:00:00.000Z' ? 0 : item.occurrences);
|
|
59
|
+
const observedItemTimestamp = (item, value) => timestampedOccurrences(item) > 0
|
|
60
|
+
? value.slice(0, 19).replace('T', ' ')
|
|
61
|
+
: (lang === 'zh' ? '未记录' : 'Not recorded');
|
|
62
|
+
const sessionTimestampedInvocationCount = (session) => session.timestampedInvocationCount
|
|
63
|
+
?? (session.startTimestamp === '1970-01-01T00:00:00.000Z' ? 0 : session.invocationIds.length);
|
|
64
|
+
const observedSessionRange = (session) => {
|
|
65
|
+
const count = sessionTimestampedInvocationCount(session);
|
|
66
|
+
if (count === 0)
|
|
67
|
+
return lang === 'zh' ? '未记录' : 'Not recorded';
|
|
68
|
+
const range = formatTimeRange(session.startTimestamp, session.endTimestamp);
|
|
69
|
+
return count < session.invocationIds.length
|
|
70
|
+
? `${range}${lang === 'zh' ? '(部分调用无时间)' : ' (partial timestamps)'}`
|
|
71
|
+
: range;
|
|
72
|
+
};
|
|
73
|
+
const observedSessionTimestamp = (session, value) => sessionTimestampedInvocationCount(session) > 0
|
|
74
|
+
? value.slice(0, 19).replace('T', ' ')
|
|
75
|
+
: (lang === 'zh' ? '未记录' : 'Not recorded');
|
|
76
|
+
const skillTimestampedInvocationCount = (skill) => skill.timestampedInvocationCount
|
|
77
|
+
?? (skill.firstSeen === '1970-01-01T00:00:00.000Z' ? 0 : skill.invocationCount);
|
|
55
78
|
const reviewSeverityMeta = (item) => {
|
|
56
79
|
if (item.severity === 'high') {
|
|
57
80
|
return { label: '高风险/需关注', decision: '优先看,可能要补 SKILL.md 或改 skill 说明', color: 'var(--red)', bg: 'rgba(220,38,38,.08)' };
|
|
@@ -165,8 +188,8 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
165
188
|
</div>`;
|
|
166
189
|
};
|
|
167
190
|
const renderSourceBadge = (item) => {
|
|
168
|
-
const label = item.sourceKind === 'openclaw' ? 'OpenClaw' : item.sourceKind === 'markdown_log' ? 'Markdown log' : item.sourceKind === 'claude' ? 'Claude' : 'Unknown';
|
|
169
|
-
const color = item.sourceKind === 'openclaw' ? '#7c3aed' : item.sourceKind === 'markdown_log' ? 'var(--green)' : item.sourceKind === 'claude' ? 'var(--accent)' : 'var(--text-muted)';
|
|
191
|
+
const label = item.sourceKind === 'openclaw' ? 'OpenClaw' : item.sourceKind === 'codex' ? 'Codex' : item.sourceKind === 'markdown_log' ? 'Markdown log' : item.sourceKind === 'claude' ? 'Claude' : 'Unknown';
|
|
192
|
+
const color = item.sourceKind === 'openclaw' ? '#7c3aed' : item.sourceKind === 'codex' ? '#1677ff' : item.sourceKind === 'markdown_log' ? 'var(--green)' : item.sourceKind === 'claude' ? 'var(--accent)' : 'var(--text-muted)';
|
|
170
193
|
return `<span title="调用日志来源:${e(label)}" style="display:inline-flex;margin-top:4px;padding:2px 6px;border-radius:999px;background:var(--bg-muted);color:${color};font-size:11px;font-weight:650">${e(label)}</span>`;
|
|
171
194
|
};
|
|
172
195
|
const confidenceHeaderHelp = '判断把握:OMK 对“这条 过程发现 是否需要处理/是否高风险/需关注”的规则判断有多确定。归属把握:OMK 把这条 过程发现 归到当前 skill 名下有多确定,例如明确调用 skill 通常更高。';
|
|
@@ -182,6 +205,8 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
182
205
|
repeatedExecution: '重复执行',
|
|
183
206
|
toolCall: '工具调用',
|
|
184
207
|
toolFailure: '工具执行失败',
|
|
208
|
+
toolCancelled: '工具调用取消',
|
|
209
|
+
toolUnknown: '工具状态未知',
|
|
185
210
|
highObservation: '高优先级过程发现',
|
|
186
211
|
mediumObservation: '低风险/抽样过程发现',
|
|
187
212
|
hedging: '不确定表达',
|
|
@@ -215,6 +240,8 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
215
240
|
repeatedExecution: '统计同类步骤、工具或流程被重复执行的信号。高频出现通常对应绕路、工具策略不清晰或 workflow 缺少明确顺序。',
|
|
216
241
|
toolCall: '统计该 skill 运行片段里的 tool_use 调用总数,包括 Bash、Read、Grep 等工具。',
|
|
217
242
|
toolFailure: '统计该 skill 运行片段里失败的工具执行结果,例如 tool_result 标记 is_error=true。注意:工具执行失败不等于整个 skill 调用失败。',
|
|
243
|
+
toolCancelled: '统计 runtime 明确标记为 cancelled 的工具调用。取消与工具执行失败分开统计,也不作为知识缺口证据。',
|
|
244
|
+
toolUnknown: '统计 runtime 未提供可信终态的工具调用。状态未知不计入工具成功率或失败率分母。',
|
|
218
245
|
highObservation: '统计 severity=high 的过程发现,通常表示可能需要优先复盘的执行问题。',
|
|
219
246
|
mediumObservation: '统计 severity=medium/low 的过程发现,通常进入抽样复盘,不直接等同于必须改 skill。',
|
|
220
247
|
hedging: '统计回答或过程发现里的“不确定 / 可能 / 需要确认”等低置信文本信号。',
|
|
@@ -242,6 +269,11 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
242
269
|
'claude-desktop': 'Claude Code App',
|
|
243
270
|
cli: 'Claude CLI',
|
|
244
271
|
'sdk-ts': 'Claude SDK',
|
|
272
|
+
'codex-desktop': 'ChatGPT 桌面端(Codex)',
|
|
273
|
+
'codex-cli': 'Codex CLI',
|
|
274
|
+
'codex-sdk': 'Codex SDK',
|
|
275
|
+
'codex-vscode': 'Codex VS Code',
|
|
276
|
+
claudian: 'Claudian',
|
|
245
277
|
openclaw: 'OpenClaw',
|
|
246
278
|
markdown_log: 'Markdown 日志',
|
|
247
279
|
};
|
|
@@ -265,15 +297,19 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
265
297
|
const renderInvocationSummary = (indicators, invocationCount) => {
|
|
266
298
|
const total = indicators.toolCallCount;
|
|
267
299
|
const failed = Math.max(0, indicators.toolFailureCount);
|
|
300
|
+
const cancelled = Math.max(0, indicators.toolCancelledCount ?? 0);
|
|
301
|
+
const unknown = Math.max(0, indicators.toolUnknownCount ?? 0);
|
|
268
302
|
const interrupted = Math.max(0, indicators.userInterruptionCount);
|
|
269
|
-
const success = Math.max(0, total - failed);
|
|
303
|
+
const success = Math.max(0, total - failed - cancelled - unknown);
|
|
270
304
|
const pct = (value) => total > 0 ? `${Math.round(value / total * 100)}%` : '—';
|
|
271
305
|
return `<div class="invocation-summary" title="这是 trace 中可观测到的调用过程汇总。工具执行失败/人工中断是过程信号,不直接等同于整个 skill 失败或用户目标失败。">
|
|
272
306
|
<div class="invocation-total">工具调用总次数 <strong>${total}</strong></div>
|
|
273
307
|
<div class="invocation-breakdown">
|
|
274
308
|
<span>工具执行成功 ${success} / ${pct(success)}</span>
|
|
275
309
|
<span>工具执行失败 ${failed} / ${pct(failed)}</span>
|
|
276
|
-
|
|
310
|
+
${cancelled > 0 ? `<span>工具调用取消 ${cancelled} / ${pct(cancelled)}</span>` : ''}
|
|
311
|
+
${unknown > 0 ? `<span>结果状态未知 ${unknown} / ${pct(unknown)}</span>` : ''}
|
|
312
|
+
<span>人工中断 ${interrupted} 次</span>
|
|
277
313
|
</div>
|
|
278
314
|
<div class="invocation-footnote">Skill 调用段 ${invocationCount}</div>
|
|
279
315
|
</div>`;
|
|
@@ -342,7 +378,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
342
378
|
const next = {};
|
|
343
379
|
for (const [key, count] of Object.entries(counts ?? {})) {
|
|
344
380
|
const display = label(key);
|
|
345
|
-
next
|
|
381
|
+
incrementRecordCount(next, display, count);
|
|
346
382
|
}
|
|
347
383
|
return next;
|
|
348
384
|
};
|
|
@@ -410,7 +446,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
410
446
|
if (legacy && typeof legacy === 'object' && !Array.isArray(legacy)) {
|
|
411
447
|
for (const [key, value] of Object.entries(legacy)) {
|
|
412
448
|
if (typeof value === 'number')
|
|
413
|
-
merged
|
|
449
|
+
incrementRecordCount(merged, key, value);
|
|
414
450
|
}
|
|
415
451
|
}
|
|
416
452
|
return merged;
|
|
@@ -504,6 +540,8 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
504
540
|
repeatedExecutionCount: 0,
|
|
505
541
|
toolCallCount: 0,
|
|
506
542
|
toolFailureCount: 0,
|
|
543
|
+
toolCancelledCount: 0,
|
|
544
|
+
toolUnknownCount: 0,
|
|
507
545
|
highObservationCount: 0,
|
|
508
546
|
mediumObservationCount: 0,
|
|
509
547
|
hedgingCount: 0,
|
|
@@ -515,8 +553,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
515
553
|
return pattern.test(value);
|
|
516
554
|
};
|
|
517
555
|
const displayMetricVerdict = (event, metricKey, metricScopeId) => {
|
|
518
|
-
const
|
|
519
|
-
const entry = reviewState.entries[`evidence_metric:${targetId}`];
|
|
556
|
+
const entry = observationMetricAnnotationEntry(reviewState, { ...event, metricScopeId }, metricKey);
|
|
520
557
|
return entry?.verdict === 'confirmed' || entry?.verdict === 'rejected' ? entry.verdict : '';
|
|
521
558
|
};
|
|
522
559
|
const displayMetricIsActive = (event, metricKey, ruleDetected, metricScopeId) => {
|
|
@@ -719,6 +756,8 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
719
756
|
repeatedExecutionCount: current.repeatedExecutionCount + (next.repeatedExecutionCount ?? 0),
|
|
720
757
|
toolCallCount: current.toolCallCount + next.toolCallCount,
|
|
721
758
|
toolFailureCount: current.toolFailureCount + next.toolFailureCount,
|
|
759
|
+
toolCancelledCount: (current.toolCancelledCount ?? 0) + (next.toolCancelledCount ?? 0),
|
|
760
|
+
toolUnknownCount: (current.toolUnknownCount ?? 0) + (next.toolUnknownCount ?? 0),
|
|
722
761
|
highObservationCount: current.highObservationCount + next.highObservationCount,
|
|
723
762
|
mediumObservationCount: current.mediumObservationCount + next.mediumObservationCount,
|
|
724
763
|
hedgingCount: current.hedgingCount + next.hedgingCount,
|
|
@@ -869,8 +908,10 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
869
908
|
const displayToolCounts = mergeCountsByLabel(toolCounts, userFacingToolLabel);
|
|
870
909
|
const total = indicators.toolCallCount;
|
|
871
910
|
const failed = Math.max(0, indicators.toolFailureCount);
|
|
911
|
+
const cancelled = Math.max(0, indicators.toolCancelledCount ?? 0);
|
|
912
|
+
const unknown = Math.max(0, indicators.toolUnknownCount ?? 0);
|
|
872
913
|
const interrupted = Math.max(0, indicators.userInterruptionCount);
|
|
873
|
-
const success = Math.max(0, total - failed);
|
|
914
|
+
const success = Math.max(0, total - failed - cancelled - unknown);
|
|
874
915
|
const processMetrics = [
|
|
875
916
|
renderDecisionMetricIfPositive('高优先级', indicators.highObservationCount, 'priority'),
|
|
876
917
|
renderDecisionMetricIfPositive('抽样过程发现', indicators.mediumObservationCount, 'sample'),
|
|
@@ -881,9 +922,9 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
881
922
|
renderDecisionMetricIfPositive('显式缺口', indicators.explicitMarkerCount, 'sample'),
|
|
882
923
|
].filter(Boolean).join('');
|
|
883
924
|
return `<div class="skill-evidence-summary">
|
|
884
|
-
<div class="summary-row"><span class="summary-title">【skill 运行】</span>${renderMetric('调用段', skill.invocationCount, '次')}<span class="summary-muted">分布 ${skill.sessionCount} 个 session</span>${renderMetric('工具调用', total, '次')}${renderMetricShare('成功', success, total)}${renderDecisionMetricShare('失败', failed, total, 'sample')}${
|
|
925
|
+
<div class="summary-row"><span class="summary-title">【skill 运行】</span>${renderMetric('调用段', skill.invocationCount, '次')}<span class="summary-muted">分布 ${skill.sessionCount} 个 session</span>${renderMetric('工具调用', total, '次')}${renderMetricShare('成功', success, total)}${renderDecisionMetricShare('失败', failed, total, 'sample')}${cancelled > 0 ? renderMetricShare('取消', cancelled, total) : ''}${unknown > 0 ? renderMetricShare('状态未知', unknown, total) : ''}${renderDecisionMetric('人工中断', interrupted, 'priority')}${renderDecisionMetricIfPositive('自我纠正', indicators.selfCorrectionCount ?? 0, 'sample')}${renderDecisionMetricIfPositive('重复执行', indicators.repeatedExecutionCount ?? 0, 'sample')}${renderSoftMetric('有结果', indicators.assistantDeliverySignalCount ?? 0, '助手回复里出现明确完成态或结果反馈。')}${renderSoftMetric('有产物', indicators.deliverableArtifactSignalCount ?? 0, '助手回复里出现文档链接、Demo 地址、文件路径、代码块或上传产物。')}</div>
|
|
885
926
|
<div class="summary-row"><span class="summary-title">【用户交互】</span>${renderDecisionMetric('用户纠正', indicators.userCorrectionCount, 'priority')}${renderDecisionMetric('人工中断', indicators.userInterruptionCount, 'priority')}${renderMetric('追问', indicators.userFollowUpCount)}${renderDecisionMetric('负向反馈', indicators.negativeFeedbackCount ?? 0, 'priority')}${renderMetric('正向反馈', indicators.positiveFeedbackCount ?? 0)}${renderMetric('目标切换', indicators.userGoalShiftCount ?? 0)}</div>
|
|
886
|
-
<div class="summary-row"><span class="summary-title">【工具调用】</span>${renderMetric('总计', total, '次')}<span class="summary-detail">(${renderRankedCounts(displayToolCounts, total)})</span>${renderDecisionMetricShare('工具执行失败', failed, total, 'sample')}</div>
|
|
927
|
+
<div class="summary-row"><span class="summary-title">【工具调用】</span>${renderMetric('总计', total, '次')}<span class="summary-detail">(${renderRankedCounts(displayToolCounts, total)})</span>${renderDecisionMetricShare('工具执行失败', failed, total, 'sample')}${cancelled > 0 ? renderMetricShare('工具调用取消', cancelled, total) : ''}${unknown > 0 ? renderMetricShare('结果状态未知', unknown, total) : ''}</div>
|
|
887
928
|
<div class="summary-row"><span class="summary-title">【过程发现】</span>${processMetrics || '<span class="summary-muted">未发现需要展示的过程信号</span>'}</div>
|
|
888
929
|
${renderSkillProblemPatterns(skill.skillName, skill.problemPatterns)}
|
|
889
930
|
</div>`;
|
|
@@ -1855,6 +1896,10 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
1855
1896
|
};
|
|
1856
1897
|
const metrics = report.oneLookMetrics;
|
|
1857
1898
|
const tokenUsage = metrics.tokenUsage;
|
|
1899
|
+
const tokenCoverage = tokenUsage.coverage ?? 0;
|
|
1900
|
+
const tokenUsageText = tokenCoverage > 0
|
|
1901
|
+
? `输入 ${tokenUsage.inputTokens} / 输出 ${tokenUsage.outputTokens} / 缓存读取 ${tokenUsage.cacheReadTokens} / 缓存写入 ${tokenUsage.cacheCreationTokens}${tokenCoverage < 1 ? `(覆盖 ${Math.round(tokenCoverage * 100)}% 调用)` : ''}`
|
|
1902
|
+
: '—(trace 未提供可归因的 token 用量)';
|
|
1858
1903
|
const traceLinks = report.traceLinks.slice(0, 8);
|
|
1859
1904
|
const story = report.sessionStory ?? fallbackSessionStory(report);
|
|
1860
1905
|
const storyEvidenceButtons = (refs) => refs.length > 0
|
|
@@ -2148,6 +2193,8 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2148
2193
|
${[
|
|
2149
2194
|
['工具调用', metrics.toolCallCount],
|
|
2150
2195
|
['工具失败', metrics.toolFailureCount],
|
|
2196
|
+
['工具取消', metrics.toolCancelledCount ?? 0],
|
|
2197
|
+
['状态未知', metrics.toolUnknownCount ?? 0],
|
|
2151
2198
|
['用户消息', metrics.userMessageCount],
|
|
2152
2199
|
['追问/补充', metrics.userFollowUpCount],
|
|
2153
2200
|
['有结果', metrics.assistantDeliverySignalCount],
|
|
@@ -2158,7 +2205,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2158
2205
|
].map(([label, value]) => `<div style="padding:7px;border:1px solid var(--border);border-radius:6px;background:var(--bg-surface)"><div style="font-size:10px;color:var(--text-muted)">${e(String(label))}</div><strong style="font-size:13px;color:var(--text-primary)">${e(String(value))}</strong></div>`).join('')}
|
|
2159
2206
|
</div>
|
|
2160
2207
|
<div style="font-size:11px;color:var(--text-muted);line-height:1.5;margin-bottom:8px">
|
|
2161
|
-
Token
|
|
2208
|
+
Token 使用量(按本次能力片段归因):${tokenUsageText}
|
|
2162
2209
|
</div>
|
|
2163
2210
|
<div style="font-size:12px;font-weight:700;color:var(--text-primary);margin-bottom:6px">证据定位</div>
|
|
2164
2211
|
<div style="display:flex;flex-wrap:wrap;gap:5px;margin-bottom:10px">
|
|
@@ -2334,8 +2381,12 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2334
2381
|
if (event.kind === 'tool_use')
|
|
2335
2382
|
return { icon: 'TU', label: '工具调用', tone: 'tool-use' };
|
|
2336
2383
|
if (event.kind === 'tool_result') {
|
|
2337
|
-
if (event.isError)
|
|
2384
|
+
if (event.toolStatus === 'failure' || (event.toolStatus === undefined && event.isError))
|
|
2338
2385
|
return { icon: 'TR', label: '工具执行失败', tone: 'tool-error' };
|
|
2386
|
+
if (event.toolStatus === 'cancelled')
|
|
2387
|
+
return { icon: 'TR', label: '工具调用取消', tone: 'runtime' };
|
|
2388
|
+
if (event.toolStatus === 'unknown')
|
|
2389
|
+
return { icon: 'TR', label: '工具状态未知', tone: 'runtime' };
|
|
2339
2390
|
return { icon: 'TR', label: isSkillLaunchResult(event) ? '工具调用成功' : '工具执行成功', tone: 'tool-result' };
|
|
2340
2391
|
}
|
|
2341
2392
|
if (event.kind === 'skill_context')
|
|
@@ -2377,8 +2428,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2377
2428
|
return /重复(?:执行|尝试|读取|搜索|调用)|再次(?:执行|读取|搜索|调用)|重新(?:执行|读取|搜索|调用|跑|运行)|再(?:执行|读取|搜索|调用|跑)一遍|重试|\b(?:retry|rerun)\b/i.test(text);
|
|
2378
2429
|
}
|
|
2379
2430
|
const evidenceMetricVerdictFor = (event, metricKey, metricScopeId) => {
|
|
2380
|
-
const
|
|
2381
|
-
const entry = reviewState.entries[reviewStateKey('evidence_metric', targetId)];
|
|
2431
|
+
const entry = observationMetricAnnotationEntry(reviewState, { ...event, metricScopeId }, metricKey);
|
|
2382
2432
|
return entry?.verdict === 'confirmed' || entry?.verdict === 'rejected' ? entry.verdict : '';
|
|
2383
2433
|
};
|
|
2384
2434
|
const evidenceMetricIsActive = (event, metricKey, ruleDetected, metricScopeId) => {
|
|
@@ -2557,9 +2607,15 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2557
2607
|
if (event.kind === 'tool_result') {
|
|
2558
2608
|
if (evidenceMetricIsActive(event, 'repeated_execution', hasRepeatedExecutionSignal(event)))
|
|
2559
2609
|
badges.push({ label: evidenceMetricBadgeLabel('重复执行', event, 'repeated_execution', hasRepeatedExecutionSignal(event)), className: 'metric-repeated-execution', title: '同类工具结果或失败恢复路径被重复出现。高频出现时通常对应绕路或 workflow 不清晰。' });
|
|
2560
|
-
if (event.isError) {
|
|
2610
|
+
if (event.toolStatus === 'failure' || (event.toolStatus === undefined && event.isError)) {
|
|
2561
2611
|
badges.push({ label: '工具执行失败', className: 'metric-tool-failure', title: 'tool_result 标记 is_error=true,表示这次工具执行失败。' });
|
|
2562
2612
|
}
|
|
2613
|
+
else if (event.toolStatus === 'cancelled') {
|
|
2614
|
+
badges.push({ label: '工具调用取消', className: 'metric-neutral', title: 'runtime 明确标记这次工具调用已取消;它不计作工具执行失败。' });
|
|
2615
|
+
}
|
|
2616
|
+
else if (event.toolStatus === 'unknown') {
|
|
2617
|
+
badges.push({ label: '工具状态未知', className: 'metric-neutral', title: 'runtime 没有提供可确认的成功或失败状态;这次调用不计入工具成功率分母。' });
|
|
2618
|
+
}
|
|
2563
2619
|
else if (isSkillLaunchResult(event)) {
|
|
2564
2620
|
badges.push({ label: '工具调用成功', className: 'metric-tool-success', title: 'Launching skill 表示 Skill 工具调用已被 runtime 接受并启动;这不是 skill 执行结束。' });
|
|
2565
2621
|
}
|
|
@@ -2649,13 +2705,11 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2649
2705
|
};
|
|
2650
2706
|
const metricAnnotationTarget = (event, metricKey, metricScopeId) => observationMetricAnnotationTargetId({ ...event, metricScopeId }, metricKey);
|
|
2651
2707
|
const metricAnnotationVerdict = (event, metricKey, metricScopeId) => {
|
|
2652
|
-
const
|
|
2653
|
-
const entry = reviewState.entries[reviewStateKey('evidence_metric', targetId)];
|
|
2708
|
+
const entry = observationMetricAnnotationEntry(reviewState, { ...event, metricScopeId }, metricKey);
|
|
2654
2709
|
return entry?.verdict === 'confirmed' || entry?.verdict === 'rejected' ? entry.verdict : '';
|
|
2655
2710
|
};
|
|
2656
2711
|
const metricAnnotationReason = (event, metricKey, metricScopeId) => {
|
|
2657
|
-
const
|
|
2658
|
-
const entry = reviewState.entries[reviewStateKey('evidence_metric', targetId)];
|
|
2712
|
+
const entry = observationMetricAnnotationEntry(reviewState, { ...event, metricScopeId }, metricKey);
|
|
2659
2713
|
return typeof entry?.reason === 'string' ? entry.reason : '';
|
|
2660
2714
|
};
|
|
2661
2715
|
const timelineManualMetricKeys = () => [
|
|
@@ -2691,10 +2745,12 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2691
2745
|
? '已加入窗口'
|
|
2692
2746
|
: '加入窗口';
|
|
2693
2747
|
const source = {
|
|
2748
|
+
traceId: event.traceId,
|
|
2694
2749
|
sourceTrace: event.sourceTrace,
|
|
2695
2750
|
sessionId: event.sessionId,
|
|
2696
2751
|
messageIndex: event.messageIndex,
|
|
2697
2752
|
messageUuid: event.messageUuid,
|
|
2753
|
+
callInstanceId: event.callInstanceId,
|
|
2698
2754
|
toolUseId: event.toolUseId,
|
|
2699
2755
|
snippet: (event.snippet ?? '').slice(0, 240),
|
|
2700
2756
|
};
|
|
@@ -2706,10 +2762,12 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2706
2762
|
data-manual-mark-goal-action="${e(goalAction)}"
|
|
2707
2763
|
data-manual-mark-metrics="${e(JSON.stringify(metrics))}"
|
|
2708
2764
|
data-manual-mark-source="${e(JSON.stringify(source))}"
|
|
2765
|
+
data-trace-id="${e(event.traceId ?? '')}"
|
|
2709
2766
|
data-source-trace="${e(event.sourceTrace)}"
|
|
2710
2767
|
data-session-id="${e(event.sessionId)}"
|
|
2711
2768
|
data-message-index="${event.messageIndex ?? ''}"
|
|
2712
2769
|
data-message-uuid="${e(event.messageUuid ?? '')}"
|
|
2770
|
+
data-call-instance-id="${e(event.callInstanceId ?? '')}"
|
|
2713
2771
|
data-tool-use-id="${e(event.toolUseId ?? '')}"
|
|
2714
2772
|
data-snippet="${e((event.snippet ?? '').slice(0, 240))}"
|
|
2715
2773
|
onclick="openTimelineManualMark(this)"
|
|
@@ -2732,16 +2790,6 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2732
2790
|
const isInsideSkillWindow = (event) => {
|
|
2733
2791
|
if (options.activeEventIds)
|
|
2734
2792
|
return options.activeEventIds.has(event.id);
|
|
2735
|
-
if (options.activeSourceTrace && event.sourceTrace !== options.activeSourceTrace)
|
|
2736
|
-
return false;
|
|
2737
|
-
if (typeof event.messageIndex !== 'number')
|
|
2738
|
-
return true;
|
|
2739
|
-
const start = options.activeStartRecordIndex;
|
|
2740
|
-
const end = options.activeEndRecordIndex;
|
|
2741
|
-
if (typeof start === 'number' && event.messageIndex < start)
|
|
2742
|
-
return false;
|
|
2743
|
-
if (typeof end === 'number' && event.messageIndex > end)
|
|
2744
|
-
return false;
|
|
2745
2793
|
return true;
|
|
2746
2794
|
};
|
|
2747
2795
|
const isAddedToSkillWindow = (event) => goalSliceCorrectionAction(reviewSessionId, event) === 'add_to_current_skill_window';
|
|
@@ -2880,16 +2928,19 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2880
2928
|
</div>`;
|
|
2881
2929
|
};
|
|
2882
2930
|
const renderSessionTimelineTree = (session) => {
|
|
2883
|
-
const
|
|
2931
|
+
const sessionInvocationIds = new Set(session.invocationIds);
|
|
2932
|
+
const invocationEventIds = (experience?.invocations ?? [])
|
|
2933
|
+
.filter((invocation) => sessionInvocationIds.has(invocation.id))
|
|
2934
|
+
.flatMap((invocation) => invocation.timelineEventIds ?? invocation.timeline.map((event) => event.id));
|
|
2935
|
+
const activeEventIds = new Set(invocationEventIds.length > 0
|
|
2936
|
+
? invocationEventIds
|
|
2937
|
+
: session.timelinePreview.map((event) => event.id));
|
|
2884
2938
|
const fullTimelineOptions = {
|
|
2885
2939
|
reviewSessionId: session.id,
|
|
2886
2940
|
currentSkillName: session.skillName,
|
|
2887
2941
|
includeDownstreamFeedback: shouldIncludeDownstreamFeedbackForDisplay(session),
|
|
2888
2942
|
feedbackSignals: session.sessionStory?.episodes?.flatMap((episode) => episode.feedbackSignals ?? []) ?? [],
|
|
2889
2943
|
activeEventIds,
|
|
2890
|
-
activeSourceTrace: session.sourceTrace,
|
|
2891
|
-
activeStartRecordIndex: session.timelineScope?.segmentStartRecordIndex,
|
|
2892
|
-
activeEndRecordIndex: session.timelineScope?.segmentEndRecordIndex,
|
|
2893
2944
|
showSkillWindowMarkers: true,
|
|
2894
2945
|
suppressMetricTagsOutsideSkillWindow: true,
|
|
2895
2946
|
};
|
|
@@ -2926,7 +2977,14 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2926
2977
|
</section>
|
|
2927
2978
|
</div>`;
|
|
2928
2979
|
};
|
|
2929
|
-
const
|
|
2980
|
+
const recordRangesText = (ranges) => {
|
|
2981
|
+
if (!ranges || ranges.length === 0)
|
|
2982
|
+
return '无可定位 record';
|
|
2983
|
+
return ranges.map((range) => {
|
|
2984
|
+
const label = range.sourceTrace.split(/[\\/]/).pop() || range.traceId;
|
|
2985
|
+
return `${label} #${range.startRecordIndex} - #${range.endRecordIndex}(${range.eventCount} 个事件)`;
|
|
2986
|
+
}).join(';');
|
|
2987
|
+
};
|
|
2930
2988
|
const renderTimelineScopeNotice = (session) => {
|
|
2931
2989
|
const scope = session.timelineScope;
|
|
2932
2990
|
if (!scope) {
|
|
@@ -2939,17 +2997,19 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2939
2997
|
<button type="button" disabled title="当前 report JSON 没有 fullSessionTimeline 字段,无法在前端还原完整 session。">需要重新生成报告</button>
|
|
2940
2998
|
</div>`;
|
|
2941
2999
|
}
|
|
2942
|
-
const truncatedText = scope.truncated ? '当前
|
|
2943
|
-
const omittedText =
|
|
3000
|
+
const truncatedText = scope.truncated ? '当前 Skill 事件窗口不是完整 session 链路' : '当前展示覆盖本次 Skill 事件窗口';
|
|
3001
|
+
const omittedText = `规范时间线中,窗口前有 ${scope.omittedBeforeCount} 个事件,窗口后有 ${scope.omittedAfterCount} 个事件`;
|
|
2944
3002
|
const branchCount = session.timelineTree?.branches.length ?? 0;
|
|
2945
3003
|
const chainText = branchCount > 0
|
|
2946
|
-
? `链路结构:主线 main + ${branchCount} 条 subagent
|
|
2947
|
-
: '
|
|
3004
|
+
? `链路结构:主线 main + ${branchCount} 条 subagent 子链路;record 编号只在各自物理 trace 内有效。`
|
|
3005
|
+
: '链路结构:单物理 trace 时间线。';
|
|
2948
3006
|
return `<div class="timeline-scope-notice" data-timeline-scope-notice>
|
|
2949
3007
|
<div>
|
|
2950
3008
|
<strong>${e(truncatedText)}</strong>
|
|
2951
|
-
<span
|
|
2952
|
-
<span
|
|
3009
|
+
<span>当前预览:${scope.previewEventCount} 条 / Skill 事件窗口 ${scope.segmentEventCount ?? scope.previewEventCount} 条 / 完整 session ${scope.fullSessionEventCount} 条</span>
|
|
3010
|
+
<span>${e(omittedText)}</span>
|
|
3011
|
+
<span>Skill record 范围:${e(recordRangesText(scope.segmentRecordRanges))}</span>
|
|
3012
|
+
<span>完整 session record 范围:${e(recordRangesText(scope.sessionRecordRanges))}</span>
|
|
2953
3013
|
<span>${e(chainText)}</span>
|
|
2954
3014
|
</div>
|
|
2955
3015
|
<button type="button" data-full-session-toggle onclick="toggleFullSessionTimeline(this)">查看完整 session 时间线</button>
|
|
@@ -3020,7 +3080,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3020
3080
|
<td style="padding:8px 10px">${renderSignalLabel(item)}</td>
|
|
3021
3081
|
<td class="num" style="padding:8px 10px;text-align:right">${renderOccurrences(item)}</td>
|
|
3022
3082
|
<td class="num" style="padding:8px 10px;text-align:right">${item.confidence.toFixed(2)} / ${item.attributionConfidence.toFixed(2)}</td>
|
|
3023
|
-
<td style="padding:8px 10px;color:var(--text-muted);font-size:12px">${item
|
|
3083
|
+
<td style="padding:8px 10px;color:var(--text-muted);font-size:12px">${e(observedItemTimestamp(item, item.lastSeen))}</td>
|
|
3024
3084
|
<td style="padding:8px 10px">${renderEvidenceCell(item)}</td>
|
|
3025
3085
|
<td class="num" style="padding:8px 10px;text-align:right"><button type="button" onclick="toggleObservationDetail('${detailsId}', this)" style="font-size:12px;padding:4px 8px;border:1px solid var(--border);background:var(--bg);border-radius:4px;cursor:pointer">${lang === 'zh' ? '展开' : 'Details'}</button></td>
|
|
3026
3086
|
</tr>
|
|
@@ -3039,8 +3099,9 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3039
3099
|
${renderField('signalMeaning', `原始信号=${item.signalType};OMK 判断出的失败原因=${item.signalSubtype}。例如 failed_search 表示工具搜索/读取失败,bash_probe/not_found/tool_limit 是 OMK 根据命令和输出内容进一步判断出来的原因。`)}
|
|
3040
3100
|
${renderField('occurrencesMeaning', `出现次数=${item.occurrences};按 skill/cwd/signal/subtype/query/path 聚合去重后的同类事件数量。`)}
|
|
3041
3101
|
${renderField('reviewDecision', reviewSeverityMeta(item).decision)}
|
|
3042
|
-
${renderField('firstSeen', item.firstSeen)}
|
|
3043
|
-
${renderField('lastSeen', item.lastSeen)}
|
|
3102
|
+
${renderField('firstSeen', timestampedOccurrences(item) > 0 ? item.firstSeen : '未记录')}
|
|
3103
|
+
${renderField('lastSeen', timestampedOccurrences(item) > 0 ? item.lastSeen : '未记录')}
|
|
3104
|
+
${renderField('timestampCoverage', `${timestampedOccurrences(item)}/${item.occurrences}`)}
|
|
3044
3105
|
${renderField('recentSessionIds', item.recentSessionIds.join(', '))}
|
|
3045
3106
|
<button type="button" onclick="openObservationTrace('${e(item.id)}', this)" style="margin-top:8px;font-size:12px;padding:5px 8px;border:1px solid var(--border);background:var(--bg);border-radius:4px;cursor:pointer">Open in trace</button>
|
|
3046
3107
|
<pre id="trace-${e(item.id)}" style="display:none;margin:8px 0 0;padding:9px;background:var(--bg-muted);border:1px solid var(--border);border-radius:6px;white-space:pre-wrap;word-break:break-word;font-size:11px;line-height:1.45;max-height:360px;overflow:auto;text-align:left"></pre>
|
|
@@ -3091,7 +3152,9 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3091
3152
|
low: groupItems.filter((item) => item.severity === 'low').length,
|
|
3092
3153
|
noise: groupItems.filter((item) => item.severity === 'noise').length,
|
|
3093
3154
|
};
|
|
3094
|
-
const latestObservation = groupItems
|
|
3155
|
+
const latestObservation = groupItems
|
|
3156
|
+
.filter((item) => timestampedOccurrences(item) > 0)
|
|
3157
|
+
.reduce((value, item) => item.lastSeen > value ? item.lastSeen : value, '');
|
|
3095
3158
|
const latest = latestObservation || skillInvocationLastSeen[skillName] || '';
|
|
3096
3159
|
const invocationCount = skillInvocationCounts[skillName] ?? groupItems.reduce((sum, item) => sum + item.occurrences, 0);
|
|
3097
3160
|
const sessionCount = skillSessionCounts[skillName] ?? new Set(groupItems.flatMap((item) => item.recentSessionIds)).size;
|
|
@@ -3135,7 +3198,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3135
3198
|
<span style="font-family:ui-monospace,monospace;font-size:14px;font-weight:700">${e(skillName)}</span>
|
|
3136
3199
|
<span title="这个 skill 在当前 trace 里被归因/触发的次数" style="padding:2px 7px;border-radius:999px;background:var(--info-bg);color:var(--accent);font-size:12px;font-weight:650">调用 ${invocationCount} 次</span>
|
|
3137
3200
|
</div>
|
|
3138
|
-
<div style="color:var(--text-muted);font-size:12px;margin-top:3px">${groupItems.length} 过程发现 · ${sessionCount} sessions · source ${e(sourceKinds.join(', '))} · latest ${e(latest.slice(0, 19).replace('T', ' '))}</div>
|
|
3201
|
+
<div style="color:var(--text-muted);font-size:12px;margin-top:3px">${groupItems.length} 过程发现 · ${sessionCount} sessions · source ${e(sourceKinds.join(', '))} · latest ${e(latest ? latest.slice(0, 19).replace('T', ' ') : '未记录')}</div>
|
|
3139
3202
|
</div>
|
|
3140
3203
|
<div style="display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end">
|
|
3141
3204
|
<span title="高风险/需关注:优先看,可能需要补 SKILL.md 或改 skill 说明" style="padding:3px 7px;border-radius:999px;background:rgba(220,38,38,.08);color:var(--red);font-size:12px">高风险/需关注 ${counts.high}</span>
|
|
@@ -3167,7 +3230,9 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3167
3230
|
};
|
|
3168
3231
|
const invocationCount = skillInvocationCounts[skillName] ?? groupItems.reduce((sum, item) => sum + item.occurrences, 0);
|
|
3169
3232
|
const sessionCount = skillSessionCounts[skillName] ?? new Set(groupItems.flatMap((item) => item.recentSessionIds)).size;
|
|
3170
|
-
const lastProblemSeen = groupItems
|
|
3233
|
+
const lastProblemSeen = groupItems
|
|
3234
|
+
.filter((item) => timestampedOccurrences(item) > 0)
|
|
3235
|
+
.reduce((value, item) => item.lastSeen > value ? item.lastSeen : value, '');
|
|
3171
3236
|
const lastUsed = skillInvocationLastSeen[skillName] || lastProblemSeen || '';
|
|
3172
3237
|
const sources = Array.from(new Set(groupItems.map((item) => item.sourceKind))).sort();
|
|
3173
3238
|
const observationCount = groupItems.length;
|
|
@@ -3496,7 +3561,9 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3496
3561
|
<div>触发判断:${e(attributionText)}</div>
|
|
3497
3562
|
${sourceMetadataHtml}
|
|
3498
3563
|
</td>
|
|
3499
|
-
<td style="padding:9px 10px;color:var(--text-muted);font-size:12px">${e(skill
|
|
3564
|
+
<td style="padding:9px 10px;color:var(--text-muted);font-size:12px">${e(skillTimestampedInvocationCount(skill) > 0
|
|
3565
|
+
? skill.lastSeen.slice(0, 19).replace('T', ' ')
|
|
3566
|
+
: '未记录')}</td>
|
|
3500
3567
|
</tr>
|
|
3501
3568
|
<tr id="${e(chainId)}" data-context-chain-template style="display:none">
|
|
3502
3569
|
<td colspan="8">${renderSkillChainTemplate(skill.skillName)}</td>
|
|
@@ -3539,11 +3606,15 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3539
3606
|
${metric('自我纠正', indicators.selfCorrectionCount ?? 0, 'selfCorrection')} ·
|
|
3540
3607
|
${metric('重复执行', indicators.repeatedExecutionCount ?? 0, 'repeatedExecution')} ·
|
|
3541
3608
|
${metric('工具执行失败', indicators.toolFailureCount, 'toolFailure')} ·
|
|
3609
|
+
${metric('工具调用取消', indicators.toolCancelledCount ?? 0, 'toolCancelled')} ·
|
|
3610
|
+
${metric('工具状态未知', indicators.toolUnknownCount ?? 0, 'toolUnknown')} ·
|
|
3542
3611
|
${metric('高优先级过程发现', indicators.highObservationCount, 'highObservation')}
|
|
3543
3612
|
</td>
|
|
3544
3613
|
<td class="session-time-cell" style="padding:9px 10px;color:var(--text-muted);font-size:12px;white-space:normal">
|
|
3545
|
-
<div>${e(
|
|
3546
|
-
|
|
3614
|
+
<div>${e(session.sourceSessionStartTimestamp || session.sourceSessionEndTimestamp
|
|
3615
|
+
? formatTimeRange(session.sourceSessionStartTimestamp, session.sourceSessionEndTimestamp, session.sourceSessionDurationMs)
|
|
3616
|
+
: observedSessionRange(session))}</div>
|
|
3617
|
+
<div style="margin-top:3px;color:var(--text-muted);font-size:11px">${e(invocationWindowLabel)}: ${e(observedSessionRange(session))}</div>
|
|
3547
3618
|
</td>
|
|
3548
3619
|
<td class="num" style="padding:9px 10px;text-align:right"><button type="button" data-open-experience-detail onclick="event.stopPropagation(); openExperienceDetailModal('${detailId}', this, 'evidence')" style="font-size:12px;padding:5px 10px;border:1px solid var(--border);background:var(--bg);border-radius:4px;cursor:pointer;white-space:nowrap">证据片段</button></td>
|
|
3549
3620
|
</tr>
|
|
@@ -3570,7 +3641,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3570
3641
|
<div style="font-size:12px;color:var(--text-muted);line-height:1.5;margin-bottom:8px">这块只根据固定规则给“是否值得先看”的建议,例如优先复盘、抽样确认、上下文不足。它不是“符合预期/不符合预期”的最终结论。</div>
|
|
3571
3642
|
${renderAssistiveInference(shownInference)}
|
|
3572
3643
|
<div style="font-size:12px;color:var(--text-secondary);line-height:1.55">
|
|
3573
|
-
<div>调用摘要:调用段 ${session.invocationIds.length} · 工具调用 ${indicators.toolCallCount} · 工具执行失败 ${indicators.toolFailureCount} · 人工中断 ${indicators.userInterruptionCount}</div>
|
|
3644
|
+
<div>调用摘要:调用段 ${session.invocationIds.length} · 工具调用 ${indicators.toolCallCount} · 工具执行失败 ${indicators.toolFailureCount}${(indicators.toolCancelledCount ?? 0) > 0 ? ` · 工具调用取消 ${indicators.toolCancelledCount ?? 0}` : ''} · 人工中断 ${indicators.userInterruptionCount}</div>
|
|
3574
3645
|
<div>复盘分数:${session.reviewPriorityScore}</div>
|
|
3575
3646
|
<div>关联 invocation:${session.invocationIds.length}</div>
|
|
3576
3647
|
<div>关联过程发现:${session.relatedObservationIds.length}</div>
|
|
@@ -3602,22 +3673,27 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3602
3673
|
}, new Map()).entries())
|
|
3603
3674
|
.sort((a, b) => (sessionSkillOrder.get(a[0]) ?? Number.MAX_SAFE_INTEGER) - (sessionSkillOrder.get(b[0]) ?? Number.MAX_SAFE_INTEGER));
|
|
3604
3675
|
const experienceSessionGroupsHtml = experienceSessionGroups.map(([skillName, sessions], groupIndex) => {
|
|
3605
|
-
const
|
|
3676
|
+
const timestampedSessions = sessions.filter((session) => sessionTimestampedInvocationCount(session) > 0);
|
|
3677
|
+
const latest = timestampedSessions.reduce((max, session) => session.endTimestamp > max ? session.endTimestamp : max, '');
|
|
3606
3678
|
const earliestSessionStart = sessions.reduce((min, session) => {
|
|
3607
|
-
const value = session.sourceSessionStartTimestamp
|
|
3679
|
+
const value = session.sourceSessionStartTimestamp
|
|
3680
|
+
?? (sessionTimestampedInvocationCount(session) > 0 ? session.startTimestamp : '');
|
|
3608
3681
|
return value && value < min ? value : min;
|
|
3609
|
-
},
|
|
3682
|
+
}, '');
|
|
3610
3683
|
const latestSessionEnd = sessions.reduce((max, session) => {
|
|
3611
|
-
const value = session.sourceSessionEndTimestamp
|
|
3684
|
+
const value = session.sourceSessionEndTimestamp
|
|
3685
|
+
?? (sessionTimestampedInvocationCount(session) > 0 ? session.endTimestamp : '');
|
|
3612
3686
|
return value && value > max ? value : max;
|
|
3613
|
-
},
|
|
3687
|
+
}, '');
|
|
3614
3688
|
const reviewFirst = sessions.filter((session) => inboxResolvedPriority(session) === 'review_first').length;
|
|
3615
3689
|
const sampleReview = sessions.filter((session) => inboxResolvedPriority(session) === 'sample_review').length;
|
|
3616
3690
|
return `<details id="${e(experienceSkillAnchor(skillName))}" class="experience-session-group" open style="scroll-margin-top:16px">
|
|
3617
3691
|
<summary>
|
|
3618
3692
|
<div>
|
|
3619
3693
|
<span class="experience-session-skill">${e(skillName)}</span>
|
|
3620
|
-
<span class="experience-session-meta">${sessions.length} sessions · ${e(sessionTimeLabel)} ${e(
|
|
3694
|
+
<span class="experience-session-meta">${sessions.length} sessions · ${e(sessionTimeLabel)} ${e(earliestSessionStart || latestSessionEnd
|
|
3695
|
+
? formatTimeRange(earliestSessionStart, latestSessionEnd)
|
|
3696
|
+
: (lang === 'zh' ? '未记录' : 'Not recorded'))} · ${e(latestInvocationLabel)} ${e(latest ? formatTimestamp(latest) : (lang === 'zh' ? '未记录' : 'Not recorded'))}</span>
|
|
3621
3697
|
</div>
|
|
3622
3698
|
<div class="experience-session-tags">
|
|
3623
3699
|
<span style="color:var(--red)">优先复盘 ${reviewFirst}</span>
|
|
@@ -3646,16 +3722,6 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3646
3722
|
</details>`;
|
|
3647
3723
|
}).join('');
|
|
3648
3724
|
void experienceSessionGroupsHtml;
|
|
3649
|
-
const experienceIndicators = experience?.sessions.reduce((acc, session) => {
|
|
3650
|
-
const indicators = displayIndicatorsForSession(session);
|
|
3651
|
-
return {
|
|
3652
|
-
toolCallCount: acc.toolCallCount + indicators.toolCallCount,
|
|
3653
|
-
toolFailureCount: acc.toolFailureCount + indicators.toolFailureCount,
|
|
3654
|
-
userInterruptionCount: acc.userInterruptionCount + indicators.userInterruptionCount,
|
|
3655
|
-
};
|
|
3656
|
-
}, { toolCallCount: 0, toolFailureCount: 0, userInterruptionCount: 0 }) ?? { toolCallCount: 0, toolFailureCount: 0, userInterruptionCount: 0 };
|
|
3657
|
-
const experienceToolSuccessCount = Math.max(0, experienceIndicators.toolCallCount - experienceIndicators.toolFailureCount);
|
|
3658
|
-
void experienceToolSuccessCount;
|
|
3659
3725
|
const experienceReviewSkillCount = experience?.skills.filter((skill) => skill.reviewFirstSessionCount + skill.sampleReviewSessionCount > 0).length ?? 0;
|
|
3660
3726
|
void experienceReviewSkillCount;
|
|
3661
3727
|
const experienceReviewSessionCount = experience?.sessions.filter((session) => inboxResolvedPriority(session) !== 'routine_sample').length ?? 0;
|
|
@@ -3706,7 +3772,8 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3706
3772
|
inboxSiblingsMap.set(skill.sessionId, arr);
|
|
3707
3773
|
}
|
|
3708
3774
|
for (const arr of inboxSiblingsMap.values()) {
|
|
3709
|
-
arr.sort((a, b) =>
|
|
3775
|
+
arr.sort((a, b) => Number(sessionTimestampedInvocationCount(b) > 0) - Number(sessionTimestampedInvocationCount(a) > 0)
|
|
3776
|
+
|| a.startTimestamp.localeCompare(b.startTimestamp));
|
|
3710
3777
|
}
|
|
3711
3778
|
const inboxSkillsMap = new Map();
|
|
3712
3779
|
for (const s of inboxSessions) {
|
|
@@ -3716,13 +3783,16 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3716
3783
|
}
|
|
3717
3784
|
const inboxPriorityRank = (priority) => priority === 'review_first' ? 0 : priority === 'sample_review' ? 1 : 2;
|
|
3718
3785
|
const inboxSkillCards = Array.from(inboxSkillsMap.entries()).map(([skillName, sessions]) => {
|
|
3719
|
-
sessions.sort((a, b) => b
|
|
3786
|
+
sessions.sort((a, b) => Number(sessionTimestampedInvocationCount(b) > 0) - Number(sessionTimestampedInvocationCount(a) > 0)
|
|
3787
|
+
|| b.endTimestamp.localeCompare(a.endTimestamp));
|
|
3720
3788
|
const priority = sessions.find((s) => inboxResolvedPriority(s) === 'review_first')
|
|
3721
3789
|
? 'review_first'
|
|
3722
3790
|
: sessions.find((s) => inboxResolvedPriority(s) === 'sample_review')
|
|
3723
3791
|
? 'sample_review'
|
|
3724
3792
|
: 'routine_sample';
|
|
3725
|
-
const latestEnd = sessions
|
|
3793
|
+
const latestEnd = sessions
|
|
3794
|
+
.filter((session) => sessionTimestampedInvocationCount(session) > 0)
|
|
3795
|
+
.reduce((latest, s) => s.endTimestamp > latest ? s.endTimestamp : latest, '');
|
|
3726
3796
|
return { skillName, sessions, priority, latestEnd };
|
|
3727
3797
|
});
|
|
3728
3798
|
inboxSkillCards.sort((a, b) => inboxPriorityRank(a.priority) - inboxPriorityRank(b.priority) || b.latestEnd.localeCompare(a.latestEnd) || a.skillName.localeCompare(b.skillName));
|
|
@@ -3882,7 +3952,9 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3882
3952
|
};
|
|
3883
3953
|
const inboxEntrypointShort = (session) => formatEntrypoint(session.entrypoint) || '未记录';
|
|
3884
3954
|
const inboxFormatSessionLabel = (session) => {
|
|
3885
|
-
const time = session
|
|
3955
|
+
const time = sessionTimestampedInvocationCount(session) > 0
|
|
3956
|
+
? observedSessionTimestamp(session, session.endTimestamp).slice(0, 16)
|
|
3957
|
+
: '';
|
|
3886
3958
|
const entrypoint = inboxEntrypointShort(session);
|
|
3887
3959
|
const sessionShort = session.sessionId.length > 10 ? `${session.sessionId.slice(0, 8)}…` : session.sessionId;
|
|
3888
3960
|
return [`Session ${sessionShort}`, entrypoint !== '未记录' ? entrypoint : '', time].filter(Boolean).join(' · ') || 'Session 调用记录';
|
|
@@ -4002,8 +4074,10 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
4002
4074
|
const roleLabel = link ? inboxSkillRoleLabel(link.role) : '执行';
|
|
4003
4075
|
const roleHelpKey = inboxSkillRoleHelpKey(link?.role ?? 'executor');
|
|
4004
4076
|
const indicators = displayIndicatorsForSession(skill);
|
|
4005
|
-
const dur =
|
|
4006
|
-
const startTime = skill
|
|
4077
|
+
const dur = observedSessionRange(skill);
|
|
4078
|
+
const startTime = sessionTimestampedInvocationCount(skill) > 0
|
|
4079
|
+
? skill.startTimestamp.slice(5, 16).replace('T', ' ')
|
|
4080
|
+
: '未记录';
|
|
4007
4081
|
const resolvedPriority = inboxResolvedPriority(skill);
|
|
4008
4082
|
const priorityLabel = resolvedPriority === 'review_first' ? '要看一眼' : resolvedPriority === 'sample_review' ? '抽样' : '常规';
|
|
4009
4083
|
const priorityCls = resolvedPriority === 'review_first' ? 'is-priority-high' : resolvedPriority === 'sample_review' ? 'is-priority-medium' : 'is-priority-low';
|
|
@@ -4024,6 +4098,8 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
4024
4098
|
<span>调用段 ${skill.invocationIds.length}</span>
|
|
4025
4099
|
<span>工具 ${indicators.toolCallCount}</span>
|
|
4026
4100
|
<span>失败 ${indicators.toolFailureCount}</span>
|
|
4101
|
+
${(indicators.toolCancelledCount ?? 0) > 0 ? `<span>取消 ${indicators.toolCancelledCount ?? 0}</span>` : ''}
|
|
4102
|
+
${(indicators.toolUnknownCount ?? 0) > 0 ? `<span>状态未知 ${indicators.toolUnknownCount ?? 0}</span>` : ''}
|
|
4027
4103
|
</div>
|
|
4028
4104
|
<div class="inbox-flow-range">${e(dur)}</div>
|
|
4029
4105
|
</div>
|
|
@@ -4776,7 +4852,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
4776
4852
|
const out = {};
|
|
4777
4853
|
for (const inv of inboxGetSessionInvocations(session)) {
|
|
4778
4854
|
for (const [k, v] of Object.entries(inv.toolCounts ?? {}))
|
|
4779
|
-
out
|
|
4855
|
+
incrementRecordCount(out, k, v);
|
|
4780
4856
|
}
|
|
4781
4857
|
return out;
|
|
4782
4858
|
};
|
|
@@ -4792,7 +4868,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
4792
4868
|
for (const inv of invocations) {
|
|
4793
4869
|
for (const event of inv.timeline) {
|
|
4794
4870
|
if (event.kind === 'tool_result' && event.isError && event.toolName) {
|
|
4795
|
-
failures
|
|
4871
|
+
incrementRecordCount(failures, event.toolName);
|
|
4796
4872
|
}
|
|
4797
4873
|
}
|
|
4798
4874
|
}
|
|
@@ -4958,15 +5034,19 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
4958
5034
|
skill.sourceMetadata?.model,
|
|
4959
5035
|
skill.sourceMetadata?.modelApi,
|
|
4960
5036
|
].filter(Boolean).join(' / ') || '未记录模型';
|
|
4961
|
-
const
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
{ name: '
|
|
4965
|
-
{ name: '
|
|
5037
|
+
const tokenUsage = metrics?.tokenUsage;
|
|
5038
|
+
const tokenCoverage = tokenUsage?.coverage ?? 0;
|
|
5039
|
+
const tokenDetail = tokenCoverage > 0 ? [
|
|
5040
|
+
{ name: '输入 token', count: tokenUsage.inputTokens },
|
|
5041
|
+
{ name: '输出 token', count: tokenUsage.outputTokens },
|
|
5042
|
+
{ name: 'cache 读取', count: tokenUsage.cacheReadTokens },
|
|
5043
|
+
{ name: 'cache 写入', count: tokenUsage.cacheCreationTokens },
|
|
4966
5044
|
] : [];
|
|
4967
5045
|
const cards = !metrics ? [] : [
|
|
4968
5046
|
{ key: 'toolCall', label: '工具调用', value: indicators.toolCallCount, detail: toolDetail, note: '本次能力调用段内触发的工具调用总数及各类工具的命中分布。', anomaly: false },
|
|
4969
5047
|
{ key: 'toolFailure', label: '工具失败', value: indicators.toolFailureCount, detail: toolFailureDetail, note: '工具执行返回失败的次数。命中失败可在版块 ④ 看具体上下文。', anomaly: indicators.toolFailureCount > 0 },
|
|
5048
|
+
{ key: 'toolCancelled', label: '工具取消', value: indicators.toolCancelledCount ?? 0, detail: [], note: 'runtime 明确取消的工具调用次数。取消与执行失败分开统计。', anomaly: false },
|
|
5049
|
+
{ key: 'toolUnknown', label: '状态未知', value: indicators.toolUnknownCount ?? 0, detail: [], note: 'runtime 没有提供可信终态的工具调用次数。这些调用不计入工具成功率或失败率分母。', anomaly: false },
|
|
4970
5050
|
{ key: 'userMessage', label: '用户消息', value: indicators.userMessageCount, detail: [], note: '本次能力调用段内的真实人工用户消息条数(已剔除 Skill 文档注入和运行时注入)。', anomaly: false },
|
|
4971
5051
|
{ key: 'userFollowUp', label: '追问', value: indicators.userFollowUpCount, detail: [], note: '按当前 skill 的归因结果统计用户追问 / 补充;点击版块 ④ 可用同名标签定位原文。', anomaly: indicators.userFollowUpCount > 0 },
|
|
4972
5052
|
{ key: 'userCorrection', label: '纠正', value: indicators.userCorrectionCount, detail: [], note: '用户明确纠正、否决前一轮交付的次数。出现即建议进版块 ④ 看上下文。', anomaly: indicators.userCorrectionCount > 0 },
|
|
@@ -4977,8 +5057,10 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
4977
5057
|
{ key: 'progress', label: '过程进展', value: metrics.assistantProgressUpdateCount ?? 0, detail: [], note: '回答中出现"正在 / 仍在 / 进度更新"等过程进展信号的次数。这类不算最终交付。', anomaly: false },
|
|
4978
5058
|
{ key: 'selfCorrection', label: '自我纠正', value: metrics.selfCorrectionCount ?? indicators.selfCorrectionCount ?? 0, detail: [], note: 'agent 在没有用户介入的情况下发现问题并主动修正执行策略。少量说明有恢复能力,高频说明流程不稳。', anomaly: (metrics.selfCorrectionCount ?? indicators.selfCorrectionCount ?? 0) > 0 },
|
|
4979
5059
|
{ key: 'repeatedExecution', label: '重复执行', value: metrics.repeatedExecutionCount ?? indicators.repeatedExecutionCount ?? 0, detail: [], note: '同类步骤、工具或流程被重复执行。高频出现时通常对应绕路或 workflow 不清晰。', anomaly: (metrics.repeatedExecutionCount ?? indicators.repeatedExecutionCount ?? 0) > 0 },
|
|
4980
|
-
|
|
4981
|
-
|
|
5060
|
+
...(tokenCoverage > 0 ? [
|
|
5061
|
+
{ key: 'tokenInput', label: '输入 token', value: tokenUsage.inputTokens, detail: tokenDetail, note: `仅累计 trace 已上报 token 的能力调用,覆盖率 ${Math.round(tokenCoverage * 100)}%。`, anomaly: false },
|
|
5062
|
+
{ key: 'tokenOutput', label: '输出 token', value: tokenUsage.outputTokens, detail: tokenDetail, note: `仅累计 trace 已上报 token 的能力调用,覆盖率 ${Math.round(tokenCoverage * 100)}%。`, anomaly: false },
|
|
5063
|
+
] : []),
|
|
4982
5064
|
];
|
|
4983
5065
|
const metricRow = cards.length === 0
|
|
4984
5066
|
? `<div id="inbox-sec-runtime-metrics-${e(skill.id)}"><p class="inbox-skill-empty">这条能力调用没有运行指标。</p></div>`
|
|
@@ -5043,11 +5125,22 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
5043
5125
|
}
|
|
5044
5126
|
if (indicators.toolFailureCount > 0)
|
|
5045
5127
|
runtimeIssues.push(`工具失败 ${indicators.toolFailureCount} 次`);
|
|
5046
|
-
const
|
|
5047
|
-
const
|
|
5128
|
+
const cancelledToolOutcomes = indicators.toolCancelledCount ?? 0;
|
|
5129
|
+
const unknownToolOutcomes = indicators.toolUnknownCount ?? 0;
|
|
5130
|
+
const runtimeSummaryText = runtimeIssues.length > 0
|
|
5131
|
+
? runtimeIssues.join(' · ')
|
|
5132
|
+
: cancelledToolOutcomes > 0 || unknownToolOutcomes > 0
|
|
5133
|
+
? [
|
|
5134
|
+
cancelledToolOutcomes > 0 ? `${cancelledToolOutcomes} 次工具调用取消` : '',
|
|
5135
|
+
unknownToolOutcomes > 0 ? `${unknownToolOutcomes} 次工具结果状态未知` : '',
|
|
5136
|
+
].filter(Boolean).join(' · ')
|
|
5137
|
+
: '运行指标无异常';
|
|
5138
|
+
const runtimeSummaryClass = runtimeIssues.length > 0
|
|
5139
|
+
? 'is-attention'
|
|
5140
|
+
: unknownToolOutcomes > 0 ? 'is-neutral' : 'is-ok';
|
|
5048
5141
|
const flowSummaryText = siblings.length > 1 ? `${siblings.length} 个能力调用段` : '单一能力调用段';
|
|
5049
5142
|
const flowTemplateId = `inbox-flow-template-${safeId}`;
|
|
5050
|
-
const evidenceSummaryText = `工具 ${indicators.toolCallCount} · 失败 ${indicators.toolFailureCount} · 用户消息 ${indicators.userMessageCount}`;
|
|
5143
|
+
const evidenceSummaryText = `工具 ${indicators.toolCallCount} · 失败 ${indicators.toolFailureCount}${cancelledToolOutcomes > 0 ? ` · 取消 ${cancelledToolOutcomes}` : ''}${unknownToolOutcomes > 0 ? ` · 状态未知 ${unknownToolOutcomes}` : ''} · 用户消息 ${indicators.userMessageCount}`;
|
|
5051
5144
|
const navItems = [
|
|
5052
5145
|
{ id: `inbox-sec-completion-${safeId}`, label: '① 这次跑得怎么样' },
|
|
5053
5146
|
{ id: `inbox-sec-log-chain-${safeId}`, label: '② 日志上下游链路' },
|
|
@@ -5167,6 +5260,22 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
5167
5260
|
const empty = items.length === 0 && !experience
|
|
5168
5261
|
? `<p style="color:var(--text-muted);margin-top:24px">${activeSkill ? `当前 skill 没有可展示的调用或过程发现:${e(activeSkill)}` : (lang === 'zh' ? '暂无 inbox item。运行 omk observe ingest <sessions-dir> 生成。' : 'No inbox items. Run omk observe ingest <sessions-dir> first.')}</p>`
|
|
5169
5262
|
: '';
|
|
5263
|
+
const recyclableObservationCount = allItems.filter((item) => item.severity !== 'noise').reduce((sum, item) => sum + item.occurrences, 0);
|
|
5264
|
+
const sampleFromTracesBaseCommand = `omk sample --from-traces --observations-dir ${shellQuoteArg(observationsDir || '.omk/observe-inbox')}`;
|
|
5265
|
+
const sampleFromTracesCommand = activeSkill
|
|
5266
|
+
? `${sampleFromTracesBaseCommand} --skill ${shellQuoteArg(activeSkill)}`
|
|
5267
|
+
: sampleFromTracesBaseCommand;
|
|
5268
|
+
const observeLoopCta = recyclableObservationCount > 0
|
|
5269
|
+
? `<section data-observe-feedback-loop style="margin-top:14px;border:1px solid var(--border);border-radius:8px;background:var(--bg-muted);padding:12px 14px;font-size:13px;line-height:1.55">
|
|
5270
|
+
<div style="display:flex;justify-content:space-between;gap:12px;align-items:flex-start;flex-wrap:wrap">
|
|
5271
|
+
<div>
|
|
5272
|
+
<div style="font-weight:700;color:var(--text-primary)">${lang === 'zh' ? '把已确认的 observe gap 回流成 eval sample' : 'Recycle confirmed observe gaps into eval samples'}</div>
|
|
5273
|
+
<div style="color:var(--text-muted);margin-top:3px">${lang === 'zh' ? `当前有 ${recyclableObservationCount} 个非噪声信号。先 review 高风险或抽样信号,确认可复现后生成草稿。` : `${recyclableObservationCount} non-noise signal(s) are available. Review high-risk / sampled signals first, then draft reproducible cases.`}</div>
|
|
5274
|
+
</div>
|
|
5275
|
+
<code style="display:block;max-width:100%;overflow:auto;white-space:nowrap;padding:6px 8px;border:1px solid var(--border);border-radius:6px;background:var(--bg-surface);color:var(--text-primary)">${e(sampleFromTracesCommand)}</code>
|
|
5276
|
+
</div>
|
|
5277
|
+
</section>`
|
|
5278
|
+
: '';
|
|
5170
5279
|
const v0SummarySection = `
|
|
5171
5280
|
<section class="observe-summary-grid" style="display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px;margin-top:14px">
|
|
5172
5281
|
<div style="padding:12px;border:1px solid var(--border);border-radius:8px;background:var(--bg-surface)">
|
|
@@ -5193,6 +5302,25 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
5193
5302
|
<div style="color:var(--text-muted);font-size:12px">当前只展示最新一次 ingest 的结果</div>
|
|
5194
5303
|
</div>
|
|
5195
5304
|
</section>`;
|
|
5305
|
+
const ingestionIssues = reports.reduce((totals, report) => {
|
|
5306
|
+
const ingestion = report.meta.ingestion;
|
|
5307
|
+
if (!ingestion)
|
|
5308
|
+
return totals;
|
|
5309
|
+
totals.malformed += ingestion.malformedRecordCount;
|
|
5310
|
+
totals.ignored += ingestion.ignoredValueCount;
|
|
5311
|
+
totals.unknown += ingestion.unknownEventCount;
|
|
5312
|
+
return totals;
|
|
5313
|
+
}, { malformed: 0, ignored: 0, unknown: 0 });
|
|
5314
|
+
const ingestionNotice = ingestionIssues.malformed > 0
|
|
5315
|
+
|| ingestionIssues.ignored > 0
|
|
5316
|
+
|| ingestionIssues.unknown > 0
|
|
5317
|
+
? `<div style="margin:12px 0;padding:10px 12px;border:1px solid var(--yellow);border-radius:8px;background:var(--yellow-bg);color:var(--text-secondary);font-size:13px">
|
|
5318
|
+
<strong style="color:var(--yellow)">${lang === 'zh' ? '观测输入需要复核' : 'Observation input needs review'}</strong>
|
|
5319
|
+
<span style="margin-left:8px">${lang === 'zh'
|
|
5320
|
+
? `${ingestionIssues.malformed} 条格式损坏记录,${ingestionIssues.ignored} 个非对象值,${ingestionIssues.unknown} 个未识别事件。`
|
|
5321
|
+
: `${ingestionIssues.malformed} malformed records, ${ingestionIssues.ignored} non-object values, ${ingestionIssues.unknown} unrecognized events.`}</span>
|
|
5322
|
+
</div>`
|
|
5323
|
+
: '';
|
|
5196
5324
|
return layout(pageTitle, `
|
|
5197
5325
|
<main class="observe-report-root">
|
|
5198
5326
|
<nav style="margin-bottom:12px"><a href="/observe-health" style="color:var(--accent);text-decoration:none">${lang === 'zh' ? '能力健康度日报' : 'Skill health reports'}</a></nav>
|
|
@@ -5203,6 +5331,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
5203
5331
|
</div>
|
|
5204
5332
|
${activeSkill ? `<a href="/observe-inbox" style="color:var(--accent);text-decoration:none;font-size:13px">查看全量</a>` : ''}
|
|
5205
5333
|
</div>
|
|
5334
|
+
${ingestionNotice}
|
|
5206
5335
|
<style>${OBSERVATION_INBOX_STYLES}</style>
|
|
5207
5336
|
<div id="signal-global-tooltip" role="tooltip"></div>
|
|
5208
5337
|
<div id="timeline-fulltext-tooltip" role="dialog" aria-modal="true" aria-hidden="true" aria-label="时间线消息详情"></div>
|
|
@@ -5222,6 +5351,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
5222
5351
|
<div class="metric-guide-body">${metricGuideHtml}</div>
|
|
5223
5352
|
</aside>
|
|
5224
5353
|
${empty}
|
|
5354
|
+
${observeLoopCta}
|
|
5225
5355
|
${experienceSection}
|
|
5226
5356
|
<div data-v0-observation-view style="display:none">
|
|
5227
5357
|
<div class="report-version-divider" aria-label="1.0 和 2.0 报告分隔">
|
|
@@ -6567,10 +6697,12 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
6567
6697
|
targetId: targetId,
|
|
6568
6698
|
verdict: 'reviewed',
|
|
6569
6699
|
note: action,
|
|
6700
|
+
traceId: btn ? btn.getAttribute('data-trace-id') || undefined : undefined,
|
|
6570
6701
|
sourceTrace: btn ? btn.getAttribute('data-source-trace') || undefined : undefined,
|
|
6571
6702
|
sessionId: btn ? btn.getAttribute('data-session-id') || undefined : undefined,
|
|
6572
6703
|
messageIndex: btn && btn.getAttribute('data-message-index') ? Number(btn.getAttribute('data-message-index')) : undefined,
|
|
6573
6704
|
messageUuid: btn ? btn.getAttribute('data-message-uuid') || undefined : undefined,
|
|
6705
|
+
callInstanceId: btn ? btn.getAttribute('data-call-instance-id') || undefined : undefined,
|
|
6574
6706
|
toolUseId: btn ? btn.getAttribute('data-tool-use-id') || undefined : undefined,
|
|
6575
6707
|
snippet: btn ? btn.getAttribute('data-snippet') || undefined : undefined
|
|
6576
6708
|
})
|
|
@@ -6703,10 +6835,12 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
6703
6835
|
verdict: next,
|
|
6704
6836
|
metricKey: metric.metricKey,
|
|
6705
6837
|
metricScopeId: metric.metricScopeId || undefined,
|
|
6838
|
+
traceId: source.traceId || undefined,
|
|
6706
6839
|
sourceTrace: source.sourceTrace || undefined,
|
|
6707
6840
|
sessionId: source.sessionId || undefined,
|
|
6708
6841
|
messageIndex: source.messageIndex === undefined ? undefined : Number(source.messageIndex),
|
|
6709
6842
|
messageUuid: source.messageUuid || undefined,
|
|
6843
|
+
callInstanceId: source.callInstanceId || undefined,
|
|
6710
6844
|
toolUseId: source.toolUseId || undefined,
|
|
6711
6845
|
snippet: source.snippet || undefined
|
|
6712
6846
|
})
|
|
@@ -6833,10 +6967,12 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
6833
6967
|
targetId: targetId,
|
|
6834
6968
|
verdict: next,
|
|
6835
6969
|
metricKey: metricKey,
|
|
6970
|
+
traceId: btn.getAttribute('data-trace-id') || undefined,
|
|
6836
6971
|
sourceTrace: btn.getAttribute('data-source-trace') || undefined,
|
|
6837
6972
|
sessionId: btn.getAttribute('data-session-id') || undefined,
|
|
6838
6973
|
messageIndex: btn.getAttribute('data-message-index') ? Number(btn.getAttribute('data-message-index')) : undefined,
|
|
6839
6974
|
messageUuid: btn.getAttribute('data-message-uuid') || undefined,
|
|
6975
|
+
callInstanceId: btn.getAttribute('data-call-instance-id') || undefined,
|
|
6840
6976
|
toolUseId: btn.getAttribute('data-tool-use-id') || undefined,
|
|
6841
6977
|
snippet: btn.getAttribute('data-snippet') || undefined,
|
|
6842
6978
|
reason: reason || undefined
|