oh-my-knowledge 0.48.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 +18 -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 +28 -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 +30 -2
- package/dist/cli/commands/sample.d.ts +6 -3
- package/dist/cli/commands/sample.js +72 -68
- 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 +1 -1
- package/dist/observability/inbox-view-model.js +19 -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 +208 -90
- 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/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,7 +1,8 @@
|
|
|
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';
|
|
7
8
|
import { shellQuoteArg } from '../shared/shell-quote.js';
|
|
@@ -17,18 +18,18 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
17
18
|
for (const invocation of experience?.invocations ?? []) {
|
|
18
19
|
const toolCounts = experienceToolCountsBySkill.get(invocation.skillName) ?? {};
|
|
19
20
|
for (const [tool, count] of Object.entries(invocation.toolCounts ?? {})) {
|
|
20
|
-
toolCounts
|
|
21
|
+
incrementRecordCount(toolCounts, tool, count);
|
|
21
22
|
}
|
|
22
23
|
experienceToolCountsBySkill.set(invocation.skillName, toolCounts);
|
|
23
24
|
const entrypoint = invocation.entrypoint ?? invocation.sourceKind ?? 'unknown';
|
|
24
25
|
const entrypointCounts = experienceEntrypointCountsBySkill.get(invocation.skillName) ?? {};
|
|
25
|
-
entrypointCounts
|
|
26
|
+
incrementRecordCount(entrypointCounts, entrypoint);
|
|
26
27
|
experienceEntrypointCountsBySkill.set(invocation.skillName, entrypointCounts);
|
|
27
28
|
const originLabel = invocation.attribution.pluginName
|
|
28
29
|
? `Skill 来源:插件 ${invocation.attribution.pluginName}`
|
|
29
30
|
: 'Skill 来源:本地 skill';
|
|
30
31
|
const originCounts = experienceSkillOriginCountsBySkill.get(invocation.skillName) ?? {};
|
|
31
|
-
originCounts
|
|
32
|
+
incrementRecordCount(originCounts, originLabel);
|
|
32
33
|
experienceSkillOriginCountsBySkill.set(invocation.skillName, originCounts);
|
|
33
34
|
const attributionMethod = invocation.attribution.source === 'command-name'
|
|
34
35
|
? '通过斜杠命令触发'
|
|
@@ -44,7 +45,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
44
45
|
const attributionTarget = invocation.attribution.commandName ?? invocation.attribution.rawSkillRef ?? invocation.skillName;
|
|
45
46
|
const attributionLabel = attributionTarget ? `${attributionMethod}:${attributionTarget}` : attributionMethod;
|
|
46
47
|
const attributionCounts = experienceAttributionCountsBySkill.get(invocation.skillName) ?? {};
|
|
47
|
-
attributionCounts
|
|
48
|
+
incrementRecordCount(attributionCounts, attributionLabel);
|
|
48
49
|
experienceAttributionCountsBySkill.set(invocation.skillName, attributionCounts);
|
|
49
50
|
}
|
|
50
51
|
const countSkillsBySeverity = (...severities) => new Set(allItems.filter((item) => severities.includes(item.severity)).map((item) => item.skillName)).size;
|
|
@@ -53,6 +54,27 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
53
54
|
const latestInvocationLabel = lang === 'zh' ? '最近调用' : 'Latest invocation';
|
|
54
55
|
const invocationWindowLabel = lang === 'zh' ? '调用窗口' : 'Invocation window';
|
|
55
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);
|
|
56
78
|
const reviewSeverityMeta = (item) => {
|
|
57
79
|
if (item.severity === 'high') {
|
|
58
80
|
return { label: '高风险/需关注', decision: '优先看,可能要补 SKILL.md 或改 skill 说明', color: 'var(--red)', bg: 'rgba(220,38,38,.08)' };
|
|
@@ -166,8 +188,8 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
166
188
|
</div>`;
|
|
167
189
|
};
|
|
168
190
|
const renderSourceBadge = (item) => {
|
|
169
|
-
const label = item.sourceKind === 'openclaw' ? 'OpenClaw' : item.sourceKind === 'markdown_log' ? 'Markdown log' : item.sourceKind === 'claude' ? 'Claude' : 'Unknown';
|
|
170
|
-
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)';
|
|
171
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>`;
|
|
172
194
|
};
|
|
173
195
|
const confidenceHeaderHelp = '判断把握:OMK 对“这条 过程发现 是否需要处理/是否高风险/需关注”的规则判断有多确定。归属把握:OMK 把这条 过程发现 归到当前 skill 名下有多确定,例如明确调用 skill 通常更高。';
|
|
@@ -183,6 +205,8 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
183
205
|
repeatedExecution: '重复执行',
|
|
184
206
|
toolCall: '工具调用',
|
|
185
207
|
toolFailure: '工具执行失败',
|
|
208
|
+
toolCancelled: '工具调用取消',
|
|
209
|
+
toolUnknown: '工具状态未知',
|
|
186
210
|
highObservation: '高优先级过程发现',
|
|
187
211
|
mediumObservation: '低风险/抽样过程发现',
|
|
188
212
|
hedging: '不确定表达',
|
|
@@ -216,6 +240,8 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
216
240
|
repeatedExecution: '统计同类步骤、工具或流程被重复执行的信号。高频出现通常对应绕路、工具策略不清晰或 workflow 缺少明确顺序。',
|
|
217
241
|
toolCall: '统计该 skill 运行片段里的 tool_use 调用总数,包括 Bash、Read、Grep 等工具。',
|
|
218
242
|
toolFailure: '统计该 skill 运行片段里失败的工具执行结果,例如 tool_result 标记 is_error=true。注意:工具执行失败不等于整个 skill 调用失败。',
|
|
243
|
+
toolCancelled: '统计 runtime 明确标记为 cancelled 的工具调用。取消与工具执行失败分开统计,也不作为知识缺口证据。',
|
|
244
|
+
toolUnknown: '统计 runtime 未提供可信终态的工具调用。状态未知不计入工具成功率或失败率分母。',
|
|
219
245
|
highObservation: '统计 severity=high 的过程发现,通常表示可能需要优先复盘的执行问题。',
|
|
220
246
|
mediumObservation: '统计 severity=medium/low 的过程发现,通常进入抽样复盘,不直接等同于必须改 skill。',
|
|
221
247
|
hedging: '统计回答或过程发现里的“不确定 / 可能 / 需要确认”等低置信文本信号。',
|
|
@@ -243,6 +269,11 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
243
269
|
'claude-desktop': 'Claude Code App',
|
|
244
270
|
cli: 'Claude CLI',
|
|
245
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',
|
|
246
277
|
openclaw: 'OpenClaw',
|
|
247
278
|
markdown_log: 'Markdown 日志',
|
|
248
279
|
};
|
|
@@ -266,15 +297,19 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
266
297
|
const renderInvocationSummary = (indicators, invocationCount) => {
|
|
267
298
|
const total = indicators.toolCallCount;
|
|
268
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);
|
|
269
302
|
const interrupted = Math.max(0, indicators.userInterruptionCount);
|
|
270
|
-
const success = Math.max(0, total - failed);
|
|
303
|
+
const success = Math.max(0, total - failed - cancelled - unknown);
|
|
271
304
|
const pct = (value) => total > 0 ? `${Math.round(value / total * 100)}%` : '—';
|
|
272
305
|
return `<div class="invocation-summary" title="这是 trace 中可观测到的调用过程汇总。工具执行失败/人工中断是过程信号,不直接等同于整个 skill 失败或用户目标失败。">
|
|
273
306
|
<div class="invocation-total">工具调用总次数 <strong>${total}</strong></div>
|
|
274
307
|
<div class="invocation-breakdown">
|
|
275
308
|
<span>工具执行成功 ${success} / ${pct(success)}</span>
|
|
276
309
|
<span>工具执行失败 ${failed} / ${pct(failed)}</span>
|
|
277
|
-
|
|
310
|
+
${cancelled > 0 ? `<span>工具调用取消 ${cancelled} / ${pct(cancelled)}</span>` : ''}
|
|
311
|
+
${unknown > 0 ? `<span>结果状态未知 ${unknown} / ${pct(unknown)}</span>` : ''}
|
|
312
|
+
<span>人工中断 ${interrupted} 次</span>
|
|
278
313
|
</div>
|
|
279
314
|
<div class="invocation-footnote">Skill 调用段 ${invocationCount}</div>
|
|
280
315
|
</div>`;
|
|
@@ -343,7 +378,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
343
378
|
const next = {};
|
|
344
379
|
for (const [key, count] of Object.entries(counts ?? {})) {
|
|
345
380
|
const display = label(key);
|
|
346
|
-
next
|
|
381
|
+
incrementRecordCount(next, display, count);
|
|
347
382
|
}
|
|
348
383
|
return next;
|
|
349
384
|
};
|
|
@@ -411,7 +446,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
411
446
|
if (legacy && typeof legacy === 'object' && !Array.isArray(legacy)) {
|
|
412
447
|
for (const [key, value] of Object.entries(legacy)) {
|
|
413
448
|
if (typeof value === 'number')
|
|
414
|
-
merged
|
|
449
|
+
incrementRecordCount(merged, key, value);
|
|
415
450
|
}
|
|
416
451
|
}
|
|
417
452
|
return merged;
|
|
@@ -505,6 +540,8 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
505
540
|
repeatedExecutionCount: 0,
|
|
506
541
|
toolCallCount: 0,
|
|
507
542
|
toolFailureCount: 0,
|
|
543
|
+
toolCancelledCount: 0,
|
|
544
|
+
toolUnknownCount: 0,
|
|
508
545
|
highObservationCount: 0,
|
|
509
546
|
mediumObservationCount: 0,
|
|
510
547
|
hedgingCount: 0,
|
|
@@ -516,8 +553,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
516
553
|
return pattern.test(value);
|
|
517
554
|
};
|
|
518
555
|
const displayMetricVerdict = (event, metricKey, metricScopeId) => {
|
|
519
|
-
const
|
|
520
|
-
const entry = reviewState.entries[`evidence_metric:${targetId}`];
|
|
556
|
+
const entry = observationMetricAnnotationEntry(reviewState, { ...event, metricScopeId }, metricKey);
|
|
521
557
|
return entry?.verdict === 'confirmed' || entry?.verdict === 'rejected' ? entry.verdict : '';
|
|
522
558
|
};
|
|
523
559
|
const displayMetricIsActive = (event, metricKey, ruleDetected, metricScopeId) => {
|
|
@@ -720,6 +756,8 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
720
756
|
repeatedExecutionCount: current.repeatedExecutionCount + (next.repeatedExecutionCount ?? 0),
|
|
721
757
|
toolCallCount: current.toolCallCount + next.toolCallCount,
|
|
722
758
|
toolFailureCount: current.toolFailureCount + next.toolFailureCount,
|
|
759
|
+
toolCancelledCount: (current.toolCancelledCount ?? 0) + (next.toolCancelledCount ?? 0),
|
|
760
|
+
toolUnknownCount: (current.toolUnknownCount ?? 0) + (next.toolUnknownCount ?? 0),
|
|
723
761
|
highObservationCount: current.highObservationCount + next.highObservationCount,
|
|
724
762
|
mediumObservationCount: current.mediumObservationCount + next.mediumObservationCount,
|
|
725
763
|
hedgingCount: current.hedgingCount + next.hedgingCount,
|
|
@@ -870,8 +908,10 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
870
908
|
const displayToolCounts = mergeCountsByLabel(toolCounts, userFacingToolLabel);
|
|
871
909
|
const total = indicators.toolCallCount;
|
|
872
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);
|
|
873
913
|
const interrupted = Math.max(0, indicators.userInterruptionCount);
|
|
874
|
-
const success = Math.max(0, total - failed);
|
|
914
|
+
const success = Math.max(0, total - failed - cancelled - unknown);
|
|
875
915
|
const processMetrics = [
|
|
876
916
|
renderDecisionMetricIfPositive('高优先级', indicators.highObservationCount, 'priority'),
|
|
877
917
|
renderDecisionMetricIfPositive('抽样过程发现', indicators.mediumObservationCount, 'sample'),
|
|
@@ -882,9 +922,9 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
882
922
|
renderDecisionMetricIfPositive('显式缺口', indicators.explicitMarkerCount, 'sample'),
|
|
883
923
|
].filter(Boolean).join('');
|
|
884
924
|
return `<div class="skill-evidence-summary">
|
|
885
|
-
<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>
|
|
886
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>
|
|
887
|
-
<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>
|
|
888
928
|
<div class="summary-row"><span class="summary-title">【过程发现】</span>${processMetrics || '<span class="summary-muted">未发现需要展示的过程信号</span>'}</div>
|
|
889
929
|
${renderSkillProblemPatterns(skill.skillName, skill.problemPatterns)}
|
|
890
930
|
</div>`;
|
|
@@ -1856,6 +1896,10 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
1856
1896
|
};
|
|
1857
1897
|
const metrics = report.oneLookMetrics;
|
|
1858
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 用量)';
|
|
1859
1903
|
const traceLinks = report.traceLinks.slice(0, 8);
|
|
1860
1904
|
const story = report.sessionStory ?? fallbackSessionStory(report);
|
|
1861
1905
|
const storyEvidenceButtons = (refs) => refs.length > 0
|
|
@@ -2149,6 +2193,8 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2149
2193
|
${[
|
|
2150
2194
|
['工具调用', metrics.toolCallCount],
|
|
2151
2195
|
['工具失败', metrics.toolFailureCount],
|
|
2196
|
+
['工具取消', metrics.toolCancelledCount ?? 0],
|
|
2197
|
+
['状态未知', metrics.toolUnknownCount ?? 0],
|
|
2152
2198
|
['用户消息', metrics.userMessageCount],
|
|
2153
2199
|
['追问/补充', metrics.userFollowUpCount],
|
|
2154
2200
|
['有结果', metrics.assistantDeliverySignalCount],
|
|
@@ -2159,7 +2205,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2159
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('')}
|
|
2160
2206
|
</div>
|
|
2161
2207
|
<div style="font-size:11px;color:var(--text-muted);line-height:1.5;margin-bottom:8px">
|
|
2162
|
-
Token
|
|
2208
|
+
Token 使用量(按本次能力片段归因):${tokenUsageText}
|
|
2163
2209
|
</div>
|
|
2164
2210
|
<div style="font-size:12px;font-weight:700;color:var(--text-primary);margin-bottom:6px">证据定位</div>
|
|
2165
2211
|
<div style="display:flex;flex-wrap:wrap;gap:5px;margin-bottom:10px">
|
|
@@ -2335,8 +2381,12 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2335
2381
|
if (event.kind === 'tool_use')
|
|
2336
2382
|
return { icon: 'TU', label: '工具调用', tone: 'tool-use' };
|
|
2337
2383
|
if (event.kind === 'tool_result') {
|
|
2338
|
-
if (event.isError)
|
|
2384
|
+
if (event.toolStatus === 'failure' || (event.toolStatus === undefined && event.isError))
|
|
2339
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' };
|
|
2340
2390
|
return { icon: 'TR', label: isSkillLaunchResult(event) ? '工具调用成功' : '工具执行成功', tone: 'tool-result' };
|
|
2341
2391
|
}
|
|
2342
2392
|
if (event.kind === 'skill_context')
|
|
@@ -2378,8 +2428,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2378
2428
|
return /重复(?:执行|尝试|读取|搜索|调用)|再次(?:执行|读取|搜索|调用)|重新(?:执行|读取|搜索|调用|跑|运行)|再(?:执行|读取|搜索|调用|跑)一遍|重试|\b(?:retry|rerun)\b/i.test(text);
|
|
2379
2429
|
}
|
|
2380
2430
|
const evidenceMetricVerdictFor = (event, metricKey, metricScopeId) => {
|
|
2381
|
-
const
|
|
2382
|
-
const entry = reviewState.entries[reviewStateKey('evidence_metric', targetId)];
|
|
2431
|
+
const entry = observationMetricAnnotationEntry(reviewState, { ...event, metricScopeId }, metricKey);
|
|
2383
2432
|
return entry?.verdict === 'confirmed' || entry?.verdict === 'rejected' ? entry.verdict : '';
|
|
2384
2433
|
};
|
|
2385
2434
|
const evidenceMetricIsActive = (event, metricKey, ruleDetected, metricScopeId) => {
|
|
@@ -2558,9 +2607,15 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2558
2607
|
if (event.kind === 'tool_result') {
|
|
2559
2608
|
if (evidenceMetricIsActive(event, 'repeated_execution', hasRepeatedExecutionSignal(event)))
|
|
2560
2609
|
badges.push({ label: evidenceMetricBadgeLabel('重复执行', event, 'repeated_execution', hasRepeatedExecutionSignal(event)), className: 'metric-repeated-execution', title: '同类工具结果或失败恢复路径被重复出现。高频出现时通常对应绕路或 workflow 不清晰。' });
|
|
2561
|
-
if (event.isError) {
|
|
2610
|
+
if (event.toolStatus === 'failure' || (event.toolStatus === undefined && event.isError)) {
|
|
2562
2611
|
badges.push({ label: '工具执行失败', className: 'metric-tool-failure', title: 'tool_result 标记 is_error=true,表示这次工具执行失败。' });
|
|
2563
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
|
+
}
|
|
2564
2619
|
else if (isSkillLaunchResult(event)) {
|
|
2565
2620
|
badges.push({ label: '工具调用成功', className: 'metric-tool-success', title: 'Launching skill 表示 Skill 工具调用已被 runtime 接受并启动;这不是 skill 执行结束。' });
|
|
2566
2621
|
}
|
|
@@ -2650,13 +2705,11 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2650
2705
|
};
|
|
2651
2706
|
const metricAnnotationTarget = (event, metricKey, metricScopeId) => observationMetricAnnotationTargetId({ ...event, metricScopeId }, metricKey);
|
|
2652
2707
|
const metricAnnotationVerdict = (event, metricKey, metricScopeId) => {
|
|
2653
|
-
const
|
|
2654
|
-
const entry = reviewState.entries[reviewStateKey('evidence_metric', targetId)];
|
|
2708
|
+
const entry = observationMetricAnnotationEntry(reviewState, { ...event, metricScopeId }, metricKey);
|
|
2655
2709
|
return entry?.verdict === 'confirmed' || entry?.verdict === 'rejected' ? entry.verdict : '';
|
|
2656
2710
|
};
|
|
2657
2711
|
const metricAnnotationReason = (event, metricKey, metricScopeId) => {
|
|
2658
|
-
const
|
|
2659
|
-
const entry = reviewState.entries[reviewStateKey('evidence_metric', targetId)];
|
|
2712
|
+
const entry = observationMetricAnnotationEntry(reviewState, { ...event, metricScopeId }, metricKey);
|
|
2660
2713
|
return typeof entry?.reason === 'string' ? entry.reason : '';
|
|
2661
2714
|
};
|
|
2662
2715
|
const timelineManualMetricKeys = () => [
|
|
@@ -2692,10 +2745,12 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2692
2745
|
? '已加入窗口'
|
|
2693
2746
|
: '加入窗口';
|
|
2694
2747
|
const source = {
|
|
2748
|
+
traceId: event.traceId,
|
|
2695
2749
|
sourceTrace: event.sourceTrace,
|
|
2696
2750
|
sessionId: event.sessionId,
|
|
2697
2751
|
messageIndex: event.messageIndex,
|
|
2698
2752
|
messageUuid: event.messageUuid,
|
|
2753
|
+
callInstanceId: event.callInstanceId,
|
|
2699
2754
|
toolUseId: event.toolUseId,
|
|
2700
2755
|
snippet: (event.snippet ?? '').slice(0, 240),
|
|
2701
2756
|
};
|
|
@@ -2707,10 +2762,12 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2707
2762
|
data-manual-mark-goal-action="${e(goalAction)}"
|
|
2708
2763
|
data-manual-mark-metrics="${e(JSON.stringify(metrics))}"
|
|
2709
2764
|
data-manual-mark-source="${e(JSON.stringify(source))}"
|
|
2765
|
+
data-trace-id="${e(event.traceId ?? '')}"
|
|
2710
2766
|
data-source-trace="${e(event.sourceTrace)}"
|
|
2711
2767
|
data-session-id="${e(event.sessionId)}"
|
|
2712
2768
|
data-message-index="${event.messageIndex ?? ''}"
|
|
2713
2769
|
data-message-uuid="${e(event.messageUuid ?? '')}"
|
|
2770
|
+
data-call-instance-id="${e(event.callInstanceId ?? '')}"
|
|
2714
2771
|
data-tool-use-id="${e(event.toolUseId ?? '')}"
|
|
2715
2772
|
data-snippet="${e((event.snippet ?? '').slice(0, 240))}"
|
|
2716
2773
|
onclick="openTimelineManualMark(this)"
|
|
@@ -2733,16 +2790,6 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2733
2790
|
const isInsideSkillWindow = (event) => {
|
|
2734
2791
|
if (options.activeEventIds)
|
|
2735
2792
|
return options.activeEventIds.has(event.id);
|
|
2736
|
-
if (options.activeSourceTrace && event.sourceTrace !== options.activeSourceTrace)
|
|
2737
|
-
return false;
|
|
2738
|
-
if (typeof event.messageIndex !== 'number')
|
|
2739
|
-
return true;
|
|
2740
|
-
const start = options.activeStartRecordIndex;
|
|
2741
|
-
const end = options.activeEndRecordIndex;
|
|
2742
|
-
if (typeof start === 'number' && event.messageIndex < start)
|
|
2743
|
-
return false;
|
|
2744
|
-
if (typeof end === 'number' && event.messageIndex > end)
|
|
2745
|
-
return false;
|
|
2746
2793
|
return true;
|
|
2747
2794
|
};
|
|
2748
2795
|
const isAddedToSkillWindow = (event) => goalSliceCorrectionAction(reviewSessionId, event) === 'add_to_current_skill_window';
|
|
@@ -2881,16 +2928,19 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2881
2928
|
</div>`;
|
|
2882
2929
|
};
|
|
2883
2930
|
const renderSessionTimelineTree = (session) => {
|
|
2884
|
-
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));
|
|
2885
2938
|
const fullTimelineOptions = {
|
|
2886
2939
|
reviewSessionId: session.id,
|
|
2887
2940
|
currentSkillName: session.skillName,
|
|
2888
2941
|
includeDownstreamFeedback: shouldIncludeDownstreamFeedbackForDisplay(session),
|
|
2889
2942
|
feedbackSignals: session.sessionStory?.episodes?.flatMap((episode) => episode.feedbackSignals ?? []) ?? [],
|
|
2890
2943
|
activeEventIds,
|
|
2891
|
-
activeSourceTrace: session.sourceTrace,
|
|
2892
|
-
activeStartRecordIndex: session.timelineScope?.segmentStartRecordIndex,
|
|
2893
|
-
activeEndRecordIndex: session.timelineScope?.segmentEndRecordIndex,
|
|
2894
2944
|
showSkillWindowMarkers: true,
|
|
2895
2945
|
suppressMetricTagsOutsideSkillWindow: true,
|
|
2896
2946
|
};
|
|
@@ -2927,7 +2977,14 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2927
2977
|
</section>
|
|
2928
2978
|
</div>`;
|
|
2929
2979
|
};
|
|
2930
|
-
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
|
+
};
|
|
2931
2988
|
const renderTimelineScopeNotice = (session) => {
|
|
2932
2989
|
const scope = session.timelineScope;
|
|
2933
2990
|
if (!scope) {
|
|
@@ -2940,17 +2997,19 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
2940
2997
|
<button type="button" disabled title="当前 report JSON 没有 fullSessionTimeline 字段,无法在前端还原完整 session。">需要重新生成报告</button>
|
|
2941
2998
|
</div>`;
|
|
2942
2999
|
}
|
|
2943
|
-
const truncatedText = scope.truncated ? '当前
|
|
2944
|
-
const omittedText =
|
|
3000
|
+
const truncatedText = scope.truncated ? '当前 Skill 事件窗口不是完整 session 链路' : '当前展示覆盖本次 Skill 事件窗口';
|
|
3001
|
+
const omittedText = `规范时间线中,窗口前有 ${scope.omittedBeforeCount} 个事件,窗口后有 ${scope.omittedAfterCount} 个事件`;
|
|
2945
3002
|
const branchCount = session.timelineTree?.branches.length ?? 0;
|
|
2946
3003
|
const chainText = branchCount > 0
|
|
2947
|
-
? `链路结构:主线 main + ${branchCount} 条 subagent
|
|
2948
|
-
: '
|
|
3004
|
+
? `链路结构:主线 main + ${branchCount} 条 subagent 子链路;record 编号只在各自物理 trace 内有效。`
|
|
3005
|
+
: '链路结构:单物理 trace 时间线。';
|
|
2949
3006
|
return `<div class="timeline-scope-notice" data-timeline-scope-notice>
|
|
2950
3007
|
<div>
|
|
2951
3008
|
<strong>${e(truncatedText)}</strong>
|
|
2952
|
-
<span
|
|
2953
|
-
<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>
|
|
2954
3013
|
<span>${e(chainText)}</span>
|
|
2955
3014
|
</div>
|
|
2956
3015
|
<button type="button" data-full-session-toggle onclick="toggleFullSessionTimeline(this)">查看完整 session 时间线</button>
|
|
@@ -3021,7 +3080,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3021
3080
|
<td style="padding:8px 10px">${renderSignalLabel(item)}</td>
|
|
3022
3081
|
<td class="num" style="padding:8px 10px;text-align:right">${renderOccurrences(item)}</td>
|
|
3023
3082
|
<td class="num" style="padding:8px 10px;text-align:right">${item.confidence.toFixed(2)} / ${item.attributionConfidence.toFixed(2)}</td>
|
|
3024
|
-
<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>
|
|
3025
3084
|
<td style="padding:8px 10px">${renderEvidenceCell(item)}</td>
|
|
3026
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>
|
|
3027
3086
|
</tr>
|
|
@@ -3040,8 +3099,9 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3040
3099
|
${renderField('signalMeaning', `原始信号=${item.signalType};OMK 判断出的失败原因=${item.signalSubtype}。例如 failed_search 表示工具搜索/读取失败,bash_probe/not_found/tool_limit 是 OMK 根据命令和输出内容进一步判断出来的原因。`)}
|
|
3041
3100
|
${renderField('occurrencesMeaning', `出现次数=${item.occurrences};按 skill/cwd/signal/subtype/query/path 聚合去重后的同类事件数量。`)}
|
|
3042
3101
|
${renderField('reviewDecision', reviewSeverityMeta(item).decision)}
|
|
3043
|
-
${renderField('firstSeen', item.firstSeen)}
|
|
3044
|
-
${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}`)}
|
|
3045
3105
|
${renderField('recentSessionIds', item.recentSessionIds.join(', '))}
|
|
3046
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>
|
|
3047
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>
|
|
@@ -3092,7 +3152,9 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3092
3152
|
low: groupItems.filter((item) => item.severity === 'low').length,
|
|
3093
3153
|
noise: groupItems.filter((item) => item.severity === 'noise').length,
|
|
3094
3154
|
};
|
|
3095
|
-
const latestObservation = groupItems
|
|
3155
|
+
const latestObservation = groupItems
|
|
3156
|
+
.filter((item) => timestampedOccurrences(item) > 0)
|
|
3157
|
+
.reduce((value, item) => item.lastSeen > value ? item.lastSeen : value, '');
|
|
3096
3158
|
const latest = latestObservation || skillInvocationLastSeen[skillName] || '';
|
|
3097
3159
|
const invocationCount = skillInvocationCounts[skillName] ?? groupItems.reduce((sum, item) => sum + item.occurrences, 0);
|
|
3098
3160
|
const sessionCount = skillSessionCounts[skillName] ?? new Set(groupItems.flatMap((item) => item.recentSessionIds)).size;
|
|
@@ -3136,7 +3198,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3136
3198
|
<span style="font-family:ui-monospace,monospace;font-size:14px;font-weight:700">${e(skillName)}</span>
|
|
3137
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>
|
|
3138
3200
|
</div>
|
|
3139
|
-
<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>
|
|
3140
3202
|
</div>
|
|
3141
3203
|
<div style="display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end">
|
|
3142
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>
|
|
@@ -3168,7 +3230,9 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3168
3230
|
};
|
|
3169
3231
|
const invocationCount = skillInvocationCounts[skillName] ?? groupItems.reduce((sum, item) => sum + item.occurrences, 0);
|
|
3170
3232
|
const sessionCount = skillSessionCounts[skillName] ?? new Set(groupItems.flatMap((item) => item.recentSessionIds)).size;
|
|
3171
|
-
const lastProblemSeen = groupItems
|
|
3233
|
+
const lastProblemSeen = groupItems
|
|
3234
|
+
.filter((item) => timestampedOccurrences(item) > 0)
|
|
3235
|
+
.reduce((value, item) => item.lastSeen > value ? item.lastSeen : value, '');
|
|
3172
3236
|
const lastUsed = skillInvocationLastSeen[skillName] || lastProblemSeen || '';
|
|
3173
3237
|
const sources = Array.from(new Set(groupItems.map((item) => item.sourceKind))).sort();
|
|
3174
3238
|
const observationCount = groupItems.length;
|
|
@@ -3497,7 +3561,9 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3497
3561
|
<div>触发判断:${e(attributionText)}</div>
|
|
3498
3562
|
${sourceMetadataHtml}
|
|
3499
3563
|
</td>
|
|
3500
|
-
<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>
|
|
3501
3567
|
</tr>
|
|
3502
3568
|
<tr id="${e(chainId)}" data-context-chain-template style="display:none">
|
|
3503
3569
|
<td colspan="8">${renderSkillChainTemplate(skill.skillName)}</td>
|
|
@@ -3540,11 +3606,15 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3540
3606
|
${metric('自我纠正', indicators.selfCorrectionCount ?? 0, 'selfCorrection')} ·
|
|
3541
3607
|
${metric('重复执行', indicators.repeatedExecutionCount ?? 0, 'repeatedExecution')} ·
|
|
3542
3608
|
${metric('工具执行失败', indicators.toolFailureCount, 'toolFailure')} ·
|
|
3609
|
+
${metric('工具调用取消', indicators.toolCancelledCount ?? 0, 'toolCancelled')} ·
|
|
3610
|
+
${metric('工具状态未知', indicators.toolUnknownCount ?? 0, 'toolUnknown')} ·
|
|
3543
3611
|
${metric('高优先级过程发现', indicators.highObservationCount, 'highObservation')}
|
|
3544
3612
|
</td>
|
|
3545
3613
|
<td class="session-time-cell" style="padding:9px 10px;color:var(--text-muted);font-size:12px;white-space:normal">
|
|
3546
|
-
<div>${e(
|
|
3547
|
-
|
|
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>
|
|
3548
3618
|
</td>
|
|
3549
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>
|
|
3550
3620
|
</tr>
|
|
@@ -3571,7 +3641,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3571
3641
|
<div style="font-size:12px;color:var(--text-muted);line-height:1.5;margin-bottom:8px">这块只根据固定规则给“是否值得先看”的建议,例如优先复盘、抽样确认、上下文不足。它不是“符合预期/不符合预期”的最终结论。</div>
|
|
3572
3642
|
${renderAssistiveInference(shownInference)}
|
|
3573
3643
|
<div style="font-size:12px;color:var(--text-secondary);line-height:1.55">
|
|
3574
|
-
<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>
|
|
3575
3645
|
<div>复盘分数:${session.reviewPriorityScore}</div>
|
|
3576
3646
|
<div>关联 invocation:${session.invocationIds.length}</div>
|
|
3577
3647
|
<div>关联过程发现:${session.relatedObservationIds.length}</div>
|
|
@@ -3603,22 +3673,27 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3603
3673
|
}, new Map()).entries())
|
|
3604
3674
|
.sort((a, b) => (sessionSkillOrder.get(a[0]) ?? Number.MAX_SAFE_INTEGER) - (sessionSkillOrder.get(b[0]) ?? Number.MAX_SAFE_INTEGER));
|
|
3605
3675
|
const experienceSessionGroupsHtml = experienceSessionGroups.map(([skillName, sessions], groupIndex) => {
|
|
3606
|
-
const
|
|
3676
|
+
const timestampedSessions = sessions.filter((session) => sessionTimestampedInvocationCount(session) > 0);
|
|
3677
|
+
const latest = timestampedSessions.reduce((max, session) => session.endTimestamp > max ? session.endTimestamp : max, '');
|
|
3607
3678
|
const earliestSessionStart = sessions.reduce((min, session) => {
|
|
3608
|
-
const value = session.sourceSessionStartTimestamp
|
|
3679
|
+
const value = session.sourceSessionStartTimestamp
|
|
3680
|
+
?? (sessionTimestampedInvocationCount(session) > 0 ? session.startTimestamp : '');
|
|
3609
3681
|
return value && value < min ? value : min;
|
|
3610
|
-
},
|
|
3682
|
+
}, '');
|
|
3611
3683
|
const latestSessionEnd = sessions.reduce((max, session) => {
|
|
3612
|
-
const value = session.sourceSessionEndTimestamp
|
|
3684
|
+
const value = session.sourceSessionEndTimestamp
|
|
3685
|
+
?? (sessionTimestampedInvocationCount(session) > 0 ? session.endTimestamp : '');
|
|
3613
3686
|
return value && value > max ? value : max;
|
|
3614
|
-
},
|
|
3687
|
+
}, '');
|
|
3615
3688
|
const reviewFirst = sessions.filter((session) => inboxResolvedPriority(session) === 'review_first').length;
|
|
3616
3689
|
const sampleReview = sessions.filter((session) => inboxResolvedPriority(session) === 'sample_review').length;
|
|
3617
3690
|
return `<details id="${e(experienceSkillAnchor(skillName))}" class="experience-session-group" open style="scroll-margin-top:16px">
|
|
3618
3691
|
<summary>
|
|
3619
3692
|
<div>
|
|
3620
3693
|
<span class="experience-session-skill">${e(skillName)}</span>
|
|
3621
|
-
<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>
|
|
3622
3697
|
</div>
|
|
3623
3698
|
<div class="experience-session-tags">
|
|
3624
3699
|
<span style="color:var(--red)">优先复盘 ${reviewFirst}</span>
|
|
@@ -3647,16 +3722,6 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3647
3722
|
</details>`;
|
|
3648
3723
|
}).join('');
|
|
3649
3724
|
void experienceSessionGroupsHtml;
|
|
3650
|
-
const experienceIndicators = experience?.sessions.reduce((acc, session) => {
|
|
3651
|
-
const indicators = displayIndicatorsForSession(session);
|
|
3652
|
-
return {
|
|
3653
|
-
toolCallCount: acc.toolCallCount + indicators.toolCallCount,
|
|
3654
|
-
toolFailureCount: acc.toolFailureCount + indicators.toolFailureCount,
|
|
3655
|
-
userInterruptionCount: acc.userInterruptionCount + indicators.userInterruptionCount,
|
|
3656
|
-
};
|
|
3657
|
-
}, { toolCallCount: 0, toolFailureCount: 0, userInterruptionCount: 0 }) ?? { toolCallCount: 0, toolFailureCount: 0, userInterruptionCount: 0 };
|
|
3658
|
-
const experienceToolSuccessCount = Math.max(0, experienceIndicators.toolCallCount - experienceIndicators.toolFailureCount);
|
|
3659
|
-
void experienceToolSuccessCount;
|
|
3660
3725
|
const experienceReviewSkillCount = experience?.skills.filter((skill) => skill.reviewFirstSessionCount + skill.sampleReviewSessionCount > 0).length ?? 0;
|
|
3661
3726
|
void experienceReviewSkillCount;
|
|
3662
3727
|
const experienceReviewSessionCount = experience?.sessions.filter((session) => inboxResolvedPriority(session) !== 'routine_sample').length ?? 0;
|
|
@@ -3707,7 +3772,8 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3707
3772
|
inboxSiblingsMap.set(skill.sessionId, arr);
|
|
3708
3773
|
}
|
|
3709
3774
|
for (const arr of inboxSiblingsMap.values()) {
|
|
3710
|
-
arr.sort((a, b) =>
|
|
3775
|
+
arr.sort((a, b) => Number(sessionTimestampedInvocationCount(b) > 0) - Number(sessionTimestampedInvocationCount(a) > 0)
|
|
3776
|
+
|| a.startTimestamp.localeCompare(b.startTimestamp));
|
|
3711
3777
|
}
|
|
3712
3778
|
const inboxSkillsMap = new Map();
|
|
3713
3779
|
for (const s of inboxSessions) {
|
|
@@ -3717,13 +3783,16 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3717
3783
|
}
|
|
3718
3784
|
const inboxPriorityRank = (priority) => priority === 'review_first' ? 0 : priority === 'sample_review' ? 1 : 2;
|
|
3719
3785
|
const inboxSkillCards = Array.from(inboxSkillsMap.entries()).map(([skillName, sessions]) => {
|
|
3720
|
-
sessions.sort((a, b) => b
|
|
3786
|
+
sessions.sort((a, b) => Number(sessionTimestampedInvocationCount(b) > 0) - Number(sessionTimestampedInvocationCount(a) > 0)
|
|
3787
|
+
|| b.endTimestamp.localeCompare(a.endTimestamp));
|
|
3721
3788
|
const priority = sessions.find((s) => inboxResolvedPriority(s) === 'review_first')
|
|
3722
3789
|
? 'review_first'
|
|
3723
3790
|
: sessions.find((s) => inboxResolvedPriority(s) === 'sample_review')
|
|
3724
3791
|
? 'sample_review'
|
|
3725
3792
|
: 'routine_sample';
|
|
3726
|
-
const latestEnd = sessions
|
|
3793
|
+
const latestEnd = sessions
|
|
3794
|
+
.filter((session) => sessionTimestampedInvocationCount(session) > 0)
|
|
3795
|
+
.reduce((latest, s) => s.endTimestamp > latest ? s.endTimestamp : latest, '');
|
|
3727
3796
|
return { skillName, sessions, priority, latestEnd };
|
|
3728
3797
|
});
|
|
3729
3798
|
inboxSkillCards.sort((a, b) => inboxPriorityRank(a.priority) - inboxPriorityRank(b.priority) || b.latestEnd.localeCompare(a.latestEnd) || a.skillName.localeCompare(b.skillName));
|
|
@@ -3883,7 +3952,9 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
3883
3952
|
};
|
|
3884
3953
|
const inboxEntrypointShort = (session) => formatEntrypoint(session.entrypoint) || '未记录';
|
|
3885
3954
|
const inboxFormatSessionLabel = (session) => {
|
|
3886
|
-
const time = session
|
|
3955
|
+
const time = sessionTimestampedInvocationCount(session) > 0
|
|
3956
|
+
? observedSessionTimestamp(session, session.endTimestamp).slice(0, 16)
|
|
3957
|
+
: '';
|
|
3887
3958
|
const entrypoint = inboxEntrypointShort(session);
|
|
3888
3959
|
const sessionShort = session.sessionId.length > 10 ? `${session.sessionId.slice(0, 8)}…` : session.sessionId;
|
|
3889
3960
|
return [`Session ${sessionShort}`, entrypoint !== '未记录' ? entrypoint : '', time].filter(Boolean).join(' · ') || 'Session 调用记录';
|
|
@@ -4003,8 +4074,10 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
4003
4074
|
const roleLabel = link ? inboxSkillRoleLabel(link.role) : '执行';
|
|
4004
4075
|
const roleHelpKey = inboxSkillRoleHelpKey(link?.role ?? 'executor');
|
|
4005
4076
|
const indicators = displayIndicatorsForSession(skill);
|
|
4006
|
-
const dur =
|
|
4007
|
-
const startTime = skill
|
|
4077
|
+
const dur = observedSessionRange(skill);
|
|
4078
|
+
const startTime = sessionTimestampedInvocationCount(skill) > 0
|
|
4079
|
+
? skill.startTimestamp.slice(5, 16).replace('T', ' ')
|
|
4080
|
+
: '未记录';
|
|
4008
4081
|
const resolvedPriority = inboxResolvedPriority(skill);
|
|
4009
4082
|
const priorityLabel = resolvedPriority === 'review_first' ? '要看一眼' : resolvedPriority === 'sample_review' ? '抽样' : '常规';
|
|
4010
4083
|
const priorityCls = resolvedPriority === 'review_first' ? 'is-priority-high' : resolvedPriority === 'sample_review' ? 'is-priority-medium' : 'is-priority-low';
|
|
@@ -4025,6 +4098,8 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
4025
4098
|
<span>调用段 ${skill.invocationIds.length}</span>
|
|
4026
4099
|
<span>工具 ${indicators.toolCallCount}</span>
|
|
4027
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>` : ''}
|
|
4028
4103
|
</div>
|
|
4029
4104
|
<div class="inbox-flow-range">${e(dur)}</div>
|
|
4030
4105
|
</div>
|
|
@@ -4777,7 +4852,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
4777
4852
|
const out = {};
|
|
4778
4853
|
for (const inv of inboxGetSessionInvocations(session)) {
|
|
4779
4854
|
for (const [k, v] of Object.entries(inv.toolCounts ?? {}))
|
|
4780
|
-
out
|
|
4855
|
+
incrementRecordCount(out, k, v);
|
|
4781
4856
|
}
|
|
4782
4857
|
return out;
|
|
4783
4858
|
};
|
|
@@ -4793,7 +4868,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
4793
4868
|
for (const inv of invocations) {
|
|
4794
4869
|
for (const event of inv.timeline) {
|
|
4795
4870
|
if (event.kind === 'tool_result' && event.isError && event.toolName) {
|
|
4796
|
-
failures
|
|
4871
|
+
incrementRecordCount(failures, event.toolName);
|
|
4797
4872
|
}
|
|
4798
4873
|
}
|
|
4799
4874
|
}
|
|
@@ -4959,15 +5034,19 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
4959
5034
|
skill.sourceMetadata?.model,
|
|
4960
5035
|
skill.sourceMetadata?.modelApi,
|
|
4961
5036
|
].filter(Boolean).join(' / ') || '未记录模型';
|
|
4962
|
-
const
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
{ name: '
|
|
4966
|
-
{ 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 },
|
|
4967
5044
|
] : [];
|
|
4968
5045
|
const cards = !metrics ? [] : [
|
|
4969
5046
|
{ key: 'toolCall', label: '工具调用', value: indicators.toolCallCount, detail: toolDetail, note: '本次能力调用段内触发的工具调用总数及各类工具的命中分布。', anomaly: false },
|
|
4970
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 },
|
|
4971
5050
|
{ key: 'userMessage', label: '用户消息', value: indicators.userMessageCount, detail: [], note: '本次能力调用段内的真实人工用户消息条数(已剔除 Skill 文档注入和运行时注入)。', anomaly: false },
|
|
4972
5051
|
{ key: 'userFollowUp', label: '追问', value: indicators.userFollowUpCount, detail: [], note: '按当前 skill 的归因结果统计用户追问 / 补充;点击版块 ④ 可用同名标签定位原文。', anomaly: indicators.userFollowUpCount > 0 },
|
|
4973
5052
|
{ key: 'userCorrection', label: '纠正', value: indicators.userCorrectionCount, detail: [], note: '用户明确纠正、否决前一轮交付的次数。出现即建议进版块 ④ 看上下文。', anomaly: indicators.userCorrectionCount > 0 },
|
|
@@ -4978,8 +5057,10 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
4978
5057
|
{ key: 'progress', label: '过程进展', value: metrics.assistantProgressUpdateCount ?? 0, detail: [], note: '回答中出现"正在 / 仍在 / 进度更新"等过程进展信号的次数。这类不算最终交付。', anomaly: false },
|
|
4979
5058
|
{ key: 'selfCorrection', label: '自我纠正', value: metrics.selfCorrectionCount ?? indicators.selfCorrectionCount ?? 0, detail: [], note: 'agent 在没有用户介入的情况下发现问题并主动修正执行策略。少量说明有恢复能力,高频说明流程不稳。', anomaly: (metrics.selfCorrectionCount ?? indicators.selfCorrectionCount ?? 0) > 0 },
|
|
4980
5059
|
{ key: 'repeatedExecution', label: '重复执行', value: metrics.repeatedExecutionCount ?? indicators.repeatedExecutionCount ?? 0, detail: [], note: '同类步骤、工具或流程被重复执行。高频出现时通常对应绕路或 workflow 不清晰。', anomaly: (metrics.repeatedExecutionCount ?? indicators.repeatedExecutionCount ?? 0) > 0 },
|
|
4981
|
-
|
|
4982
|
-
|
|
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
|
+
] : []),
|
|
4983
5064
|
];
|
|
4984
5065
|
const metricRow = cards.length === 0
|
|
4985
5066
|
? `<div id="inbox-sec-runtime-metrics-${e(skill.id)}"><p class="inbox-skill-empty">这条能力调用没有运行指标。</p></div>`
|
|
@@ -5044,11 +5125,22 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
5044
5125
|
}
|
|
5045
5126
|
if (indicators.toolFailureCount > 0)
|
|
5046
5127
|
runtimeIssues.push(`工具失败 ${indicators.toolFailureCount} 次`);
|
|
5047
|
-
const
|
|
5048
|
-
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';
|
|
5049
5141
|
const flowSummaryText = siblings.length > 1 ? `${siblings.length} 个能力调用段` : '单一能力调用段';
|
|
5050
5142
|
const flowTemplateId = `inbox-flow-template-${safeId}`;
|
|
5051
|
-
const evidenceSummaryText = `工具 ${indicators.toolCallCount} · 失败 ${indicators.toolFailureCount} · 用户消息 ${indicators.userMessageCount}`;
|
|
5143
|
+
const evidenceSummaryText = `工具 ${indicators.toolCallCount} · 失败 ${indicators.toolFailureCount}${cancelledToolOutcomes > 0 ? ` · 取消 ${cancelledToolOutcomes}` : ''}${unknownToolOutcomes > 0 ? ` · 状态未知 ${unknownToolOutcomes}` : ''} · 用户消息 ${indicators.userMessageCount}`;
|
|
5052
5144
|
const navItems = [
|
|
5053
5145
|
{ id: `inbox-sec-completion-${safeId}`, label: '① 这次跑得怎么样' },
|
|
5054
5146
|
{ id: `inbox-sec-log-chain-${safeId}`, label: '② 日志上下游链路' },
|
|
@@ -5210,6 +5302,25 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
5210
5302
|
<div style="color:var(--text-muted);font-size:12px">当前只展示最新一次 ingest 的结果</div>
|
|
5211
5303
|
</div>
|
|
5212
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
|
+
: '';
|
|
5213
5324
|
return layout(pageTitle, `
|
|
5214
5325
|
<main class="observe-report-root">
|
|
5215
5326
|
<nav style="margin-bottom:12px"><a href="/observe-health" style="color:var(--accent);text-decoration:none">${lang === 'zh' ? '能力健康度日报' : 'Skill health reports'}</a></nav>
|
|
@@ -5220,6 +5331,7 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
5220
5331
|
</div>
|
|
5221
5332
|
${activeSkill ? `<a href="/observe-inbox" style="color:var(--accent);text-decoration:none;font-size:13px">查看全量</a>` : ''}
|
|
5222
5333
|
</div>
|
|
5334
|
+
${ingestionNotice}
|
|
5223
5335
|
<style>${OBSERVATION_INBOX_STYLES}</style>
|
|
5224
5336
|
<div id="signal-global-tooltip" role="tooltip"></div>
|
|
5225
5337
|
<div id="timeline-fulltext-tooltip" role="dialog" aria-modal="true" aria-hidden="true" aria-label="时间线消息详情"></div>
|
|
@@ -6585,10 +6697,12 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
6585
6697
|
targetId: targetId,
|
|
6586
6698
|
verdict: 'reviewed',
|
|
6587
6699
|
note: action,
|
|
6700
|
+
traceId: btn ? btn.getAttribute('data-trace-id') || undefined : undefined,
|
|
6588
6701
|
sourceTrace: btn ? btn.getAttribute('data-source-trace') || undefined : undefined,
|
|
6589
6702
|
sessionId: btn ? btn.getAttribute('data-session-id') || undefined : undefined,
|
|
6590
6703
|
messageIndex: btn && btn.getAttribute('data-message-index') ? Number(btn.getAttribute('data-message-index')) : undefined,
|
|
6591
6704
|
messageUuid: btn ? btn.getAttribute('data-message-uuid') || undefined : undefined,
|
|
6705
|
+
callInstanceId: btn ? btn.getAttribute('data-call-instance-id') || undefined : undefined,
|
|
6592
6706
|
toolUseId: btn ? btn.getAttribute('data-tool-use-id') || undefined : undefined,
|
|
6593
6707
|
snippet: btn ? btn.getAttribute('data-snippet') || undefined : undefined
|
|
6594
6708
|
})
|
|
@@ -6721,10 +6835,12 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
6721
6835
|
verdict: next,
|
|
6722
6836
|
metricKey: metric.metricKey,
|
|
6723
6837
|
metricScopeId: metric.metricScopeId || undefined,
|
|
6838
|
+
traceId: source.traceId || undefined,
|
|
6724
6839
|
sourceTrace: source.sourceTrace || undefined,
|
|
6725
6840
|
sessionId: source.sessionId || undefined,
|
|
6726
6841
|
messageIndex: source.messageIndex === undefined ? undefined : Number(source.messageIndex),
|
|
6727
6842
|
messageUuid: source.messageUuid || undefined,
|
|
6843
|
+
callInstanceId: source.callInstanceId || undefined,
|
|
6728
6844
|
toolUseId: source.toolUseId || undefined,
|
|
6729
6845
|
snippet: source.snippet || undefined
|
|
6730
6846
|
})
|
|
@@ -6851,10 +6967,12 @@ export function renderObservationInboxPage(model, lang = DEFAULT_LANG) {
|
|
|
6851
6967
|
targetId: targetId,
|
|
6852
6968
|
verdict: next,
|
|
6853
6969
|
metricKey: metricKey,
|
|
6970
|
+
traceId: btn.getAttribute('data-trace-id') || undefined,
|
|
6854
6971
|
sourceTrace: btn.getAttribute('data-source-trace') || undefined,
|
|
6855
6972
|
sessionId: btn.getAttribute('data-session-id') || undefined,
|
|
6856
6973
|
messageIndex: btn.getAttribute('data-message-index') ? Number(btn.getAttribute('data-message-index')) : undefined,
|
|
6857
6974
|
messageUuid: btn.getAttribute('data-message-uuid') || undefined,
|
|
6975
|
+
callInstanceId: btn.getAttribute('data-call-instance-id') || undefined,
|
|
6858
6976
|
toolUseId: btn.getAttribute('data-tool-use-id') || undefined,
|
|
6859
6977
|
snippet: btn.getAttribute('data-snippet') || undefined,
|
|
6860
6978
|
reason: reason || undefined
|