@alan512/experienceengine 0.1.2 → 0.1.3
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/.claude-plugin/plugin.json +1 -1
- package/.mcp.json +4 -8
- package/LICENSE +21 -0
- package/README.md +158 -47
- package/README.zh-CN.md +178 -57
- package/dist/adapters/codex/instruction-template.d.ts +3 -0
- package/dist/adapters/codex/instruction-template.js +12 -0
- package/dist/adapters/codex/instruction-template.js.map +1 -0
- package/dist/adapters/codex/mcp-server.d.ts +38 -41
- package/dist/adapters/codex/mcp-server.js +107 -338
- package/dist/adapters/codex/mcp-server.js.map +1 -1
- package/dist/analyzer/llm-learning-gate.js +39 -0
- package/dist/analyzer/llm-learning-gate.js.map +1 -1
- package/dist/cli/commands/claude-hook.js +12 -3
- package/dist/cli/commands/claude-hook.js.map +1 -1
- package/dist/cli/commands/config.js +88 -2
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +4 -28
- package/dist/cli/commands/doctor.js +136 -68
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/feedback.js +4 -0
- package/dist/cli/commands/feedback.js.map +1 -1
- package/dist/cli/commands/init.d.ts +28 -0
- package/dist/cli/commands/init.js +419 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/inspect.js +203 -37
- package/dist/cli/commands/inspect.js.map +1 -1
- package/dist/cli/commands/install.js +9 -0
- package/dist/cli/commands/install.js.map +1 -1
- package/dist/cli/commands/maintenance.js +1 -1
- package/dist/cli/commands/maintenance.js.map +1 -1
- package/dist/cli/commands/mcp-server.js +4 -0
- package/dist/cli/commands/mcp-server.js.map +1 -1
- package/dist/cli/commands/status.js +57 -7
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/dispatch.js +22 -7
- package/dist/cli/dispatch.js.map +1 -1
- package/dist/cli/state-model.d.ts +14 -0
- package/dist/cli/state-model.js +23 -0
- package/dist/cli/state-model.js.map +1 -0
- package/dist/config/config-schema.d.ts +32 -0
- package/dist/config/config-schema.js +26 -0
- package/dist/config/config-schema.js.map +1 -1
- package/dist/config/default-config.js +3 -0
- package/dist/config/default-config.js.map +1 -1
- package/dist/config/load-config.js +19 -1
- package/dist/config/load-config.js.map +1 -1
- package/dist/config/path-resolver.d.ts +0 -1
- package/dist/config/path-resolver.js +0 -2
- package/dist/config/path-resolver.js.map +1 -1
- package/dist/config/runtime-env.d.ts +8 -0
- package/dist/config/runtime-env.js +14 -0
- package/dist/config/runtime-env.js.map +1 -0
- package/dist/config/secrets-store.d.ts +15 -0
- package/dist/config/secrets-store.js +56 -0
- package/dist/config/secrets-store.js.map +1 -0
- package/dist/config/settings-store.d.ts +10 -0
- package/dist/config/settings-store.js +44 -0
- package/dist/config/settings-store.js.map +1 -1
- package/dist/controller/candidate-retriever.d.ts +35 -2
- package/dist/controller/candidate-retriever.js +189 -10
- package/dist/controller/candidate-retriever.js.map +1 -1
- package/dist/controller/injection-renderer.js +52 -1
- package/dist/controller/injection-renderer.js.map +1 -1
- package/dist/controller/injection-scorecard.d.ts +14 -2
- package/dist/controller/injection-scorecard.js +18 -1
- package/dist/controller/injection-scorecard.js.map +1 -1
- package/dist/controller/intervention-controller.d.ts +15 -2
- package/dist/controller/intervention-controller.js +173 -11
- package/dist/controller/intervention-controller.js.map +1 -1
- package/dist/controller/lexical-retriever.d.ts +14 -0
- package/dist/controller/lexical-retriever.js +117 -0
- package/dist/controller/lexical-retriever.js.map +1 -0
- package/dist/controller/model-reranker.d.ts +20 -0
- package/dist/controller/model-reranker.js +187 -0
- package/dist/controller/model-reranker.js.map +1 -0
- package/dist/controller/node-ranker.js +1 -0
- package/dist/controller/node-ranker.js.map +1 -1
- package/dist/controller/query-rewrite.d.ts +8 -0
- package/dist/controller/query-rewrite.js +69 -0
- package/dist/controller/query-rewrite.js.map +1 -0
- package/dist/controller/trigger-evaluator.d.ts +23 -2
- package/dist/controller/trigger-evaluator.js +57 -3
- package/dist/controller/trigger-evaluator.js.map +1 -1
- package/dist/distillation/experience-family.d.ts +4 -0
- package/dist/distillation/experience-family.js +14 -0
- package/dist/distillation/experience-family.js.map +1 -0
- package/dist/distillation/host-llm.d.ts +1 -0
- package/dist/distillation/host-llm.js +5 -1
- package/dist/distillation/host-llm.js.map +1 -1
- package/dist/distillation/llm-distiller.js +4 -0
- package/dist/distillation/llm-distiller.js.map +1 -1
- package/dist/distillation/merge-decider.js +4 -0
- package/dist/distillation/merge-decider.js.map +1 -1
- package/dist/distillation/prompt-contract.d.ts +1 -1
- package/dist/distillation/prompt-contract.js +3 -0
- package/dist/distillation/prompt-contract.js.map +1 -1
- package/dist/distillation/queue-worker.js +57 -7
- package/dist/distillation/queue-worker.js.map +1 -1
- package/dist/feedback/state-transition.js +9 -0
- package/dist/feedback/state-transition.js.map +1 -1
- package/dist/input/input-adapter.js +2 -1
- package/dist/input/input-adapter.js.map +1 -1
- package/dist/input/outcome-resolver.js +5 -5
- package/dist/input/outcome-resolver.js.map +1 -1
- package/dist/input/tasktype-resolver.js +2 -0
- package/dist/input/tasktype-resolver.js.map +1 -1
- package/dist/input/tool-event-significance.d.ts +5 -0
- package/dist/input/tool-event-significance.js +7 -0
- package/dist/input/tool-event-significance.js.map +1 -0
- package/dist/install/claude-code-doctor.d.ts +7 -2
- package/dist/install/claude-code-doctor.js +38 -9
- package/dist/install/claude-code-doctor.js.map +1 -1
- package/dist/install/claude-marketplace-state.d.ts +14 -0
- package/dist/install/claude-marketplace-state.js +47 -0
- package/dist/install/claude-marketplace-state.js.map +1 -0
- package/dist/install/codex-installer.d.ts +18 -0
- package/dist/install/codex-installer.js +91 -1
- package/dist/install/codex-installer.js.map +1 -1
- package/dist/install/openclaw-installer.d.ts +7 -0
- package/dist/install/openclaw-installer.js +16 -0
- package/dist/install/openclaw-installer.js.map +1 -1
- package/dist/install/public-install.d.ts +14 -4
- package/dist/install/public-install.js +20 -7
- package/dist/install/public-install.js.map +1 -1
- package/dist/interaction/repo-summary.d.ts +3 -17
- package/dist/interaction/repo-summary.js +10 -27
- package/dist/interaction/repo-summary.js.map +1 -1
- package/dist/interaction/service.d.ts +44 -95
- package/dist/interaction/service.js +333 -248
- package/dist/interaction/service.js.map +1 -1
- package/dist/maintenance/scope-merge.d.ts +0 -1
- package/dist/maintenance/scope-merge.js +0 -20
- package/dist/maintenance/scope-merge.js.map +1 -1
- package/dist/plugin/openclaw-plugin.d.ts +23 -0
- package/dist/plugin/openclaw-plugin.js +86 -6
- package/dist/plugin/openclaw-plugin.js.map +1 -1
- package/dist/plugin/openclaw-routine-interaction.d.ts +6 -0
- package/dist/plugin/openclaw-routine-interaction.js +296 -0
- package/dist/plugin/openclaw-routine-interaction.js.map +1 -0
- package/dist/runtime/service.d.ts +0 -1
- package/dist/runtime/service.js +20 -29
- package/dist/runtime/service.js.map +1 -1
- package/dist/store/sqlite/db.js +9 -0
- package/dist/store/sqlite/db.js.map +1 -1
- package/dist/store/sqlite/repositories/candidate-repo.js +8 -2
- package/dist/store/sqlite/repositories/candidate-repo.js.map +1 -1
- package/dist/store/sqlite/repositories/injection-repo.d.ts +1 -0
- package/dist/store/sqlite/repositories/injection-repo.js +11 -0
- package/dist/store/sqlite/repositories/injection-repo.js.map +1 -1
- package/dist/store/sqlite/repositories/input-record-repo.d.ts +2 -0
- package/dist/store/sqlite/repositories/input-record-repo.js +22 -0
- package/dist/store/sqlite/repositories/input-record-repo.js.map +1 -1
- package/dist/store/sqlite/repositories/node-repo.js +17 -2
- package/dist/store/sqlite/repositories/node-repo.js.map +1 -1
- package/dist/store/sqlite/repositories/task-run-repo.d.ts +2 -0
- package/dist/store/sqlite/repositories/task-run-repo.js +18 -2
- package/dist/store/sqlite/repositories/task-run-repo.js.map +1 -1
- package/dist/store/sqlite/schema.sql +9 -49
- package/dist/store/vector/api-embedding-provider.d.ts +2 -0
- package/dist/store/vector/api-embedding-provider.js +23 -19
- package/dist/store/vector/api-embedding-provider.js.map +1 -1
- package/dist/store/vector/embeddings.d.ts +1 -1
- package/dist/store/vector/embeddings.js +4 -1
- package/dist/store/vector/embeddings.js.map +1 -1
- package/dist/types/domain.d.ts +30 -46
- package/dist/types/plugin.d.ts +2 -1
- package/docs/releases/v0.1.2.md +3 -3
- package/docs/releases/v0.1.3.md +94 -0
- package/docs/user-guide.md +226 -123
- package/openclaw.plugin.json +1 -1
- package/package.json +3 -2
- package/plugins/claude-code-experienceengine/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code-experienceengine/.mcp.json +4 -3
- package/plugins/claude-code-experienceengine/scripts/claude-hook.sh +30 -1
- package/plugins/claude-code-experienceengine/scripts/install-deps.sh +41 -6
- package/dist/cli/commands/pack.d.ts +0 -1
- package/dist/cli/commands/pack.js +0 -321
- package/dist/cli/commands/pack.js.map +0 -1
- package/dist/compiler/agents-renderer.d.ts +0 -4
- package/dist/compiler/agents-renderer.js +0 -105
- package/dist/compiler/agents-renderer.js.map +0 -1
- package/dist/compiler/claude-renderer.d.ts +0 -2
- package/dist/compiler/claude-renderer.js +0 -40
- package/dist/compiler/claude-renderer.js.map +0 -1
- package/dist/compiler/codex-renderer.d.ts +0 -2
- package/dist/compiler/codex-renderer.js +0 -40
- package/dist/compiler/codex-renderer.js.map +0 -1
- package/dist/compiler/compiler.d.ts +0 -4
- package/dist/compiler/compiler.js +0 -87
- package/dist/compiler/compiler.js.map +0 -1
- package/dist/compiler/deployer.d.ts +0 -21
- package/dist/compiler/deployer.js +0 -64
- package/dist/compiler/deployer.js.map +0 -1
- package/dist/compiler/github-renderer.d.ts +0 -2
- package/dist/compiler/github-renderer.js +0 -63
- package/dist/compiler/github-renderer.js.map +0 -1
- package/dist/compiler/types.d.ts +0 -45
- package/dist/compiler/types.js +0 -2
- package/dist/compiler/types.js.map +0 -1
- package/dist/interaction/pack-actions-service.d.ts +0 -59
- package/dist/interaction/pack-actions-service.js +0 -172
- package/dist/interaction/pack-actions-service.js.map +0 -1
- package/dist/packs/fs-registry.d.ts +0 -27
- package/dist/packs/fs-registry.js +0 -216
- package/dist/packs/fs-registry.js.map +0 -1
- package/dist/packs/index-sync.d.ts +0 -9
- package/dist/packs/index-sync.js +0 -54
- package/dist/packs/index-sync.js.map +0 -1
- package/dist/packs/types.d.ts +0 -55
- package/dist/packs/types.js +0 -2
- package/dist/packs/types.js.map +0 -1
- package/dist/store/sqlite/repositories/pack-repo.d.ts +0 -16
- package/dist/store/sqlite/repositories/pack-repo.js +0 -192
- package/dist/store/sqlite/repositories/pack-repo.js.map +0 -1
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { compilePack } from "../compiler/compiler.js";
|
|
2
|
-
import { deployCompiledPack } from "../compiler/deployer.js";
|
|
3
|
-
import { ExperiencePackRegistry } from "../packs/fs-registry.js";
|
|
4
1
|
import { buildInjectionScorecard } from "../controller/injection-scorecard.js";
|
|
5
2
|
import { buildBenchmarkSummary } from "../evaluation/benchmark-summary.js";
|
|
6
3
|
import { resolveScope } from "../input/scope-resolver.js";
|
|
@@ -14,11 +11,39 @@ import { OutcomeRecordRepository } from "../store/sqlite/repositories/outcome-re
|
|
|
14
11
|
import { ReviewEventRepository } from "../store/sqlite/repositories/review-event-repo.js";
|
|
15
12
|
import { ScopeRepository } from "../store/sqlite/repositories/scope-repo.js";
|
|
16
13
|
import { TaskRunRepository } from "../store/sqlite/repositories/task-run-repo.js";
|
|
17
|
-
import { ExperiencePackRepository } from "../store/sqlite/repositories/pack-repo.js";
|
|
18
14
|
import { transitionState, transitionValidationState } from "../feedback/state-transition.js";
|
|
19
15
|
import { nowIso } from "../utils/clock.js";
|
|
20
16
|
import { createId } from "../utils/ids.js";
|
|
21
17
|
import { buildRepoSummary } from "./repo-summary.js";
|
|
18
|
+
export const deriveStructuredSilenceReason = (input) => {
|
|
19
|
+
const { inspection, readiness } = input;
|
|
20
|
+
const learningReason = inspection.learningReason?.toLowerCase() ?? "";
|
|
21
|
+
const hasEarlyRepoEvidence = readiness.rawRecords < 3 && readiness.taskRuns < 3 && readiness.candidates === 0 && readiness.nodes === 0;
|
|
22
|
+
if (inspection.learningStatus === "not_applicable") {
|
|
23
|
+
return "non_applicable_turn";
|
|
24
|
+
}
|
|
25
|
+
if (inspection.learningStatus === "rejected" && learningReason.includes("expression-layer refinement")) {
|
|
26
|
+
return "non_applicable_turn";
|
|
27
|
+
}
|
|
28
|
+
if (inspection.scorecard?.decisionReason === "ambiguous_same_family_candidate"
|
|
29
|
+
|| inspection.scorecard?.decisionReason === "promising_candidate_quality"
|
|
30
|
+
|| inspection.intervention === "shadow"
|
|
31
|
+
|| inspection.intervention === "holdout") {
|
|
32
|
+
return "withheld_low_confidence";
|
|
33
|
+
}
|
|
34
|
+
if (hasEarlyRepoEvidence) {
|
|
35
|
+
return "warming_up";
|
|
36
|
+
}
|
|
37
|
+
if (inspection.learningStatus === "captured"
|
|
38
|
+
|| learningReason.includes("llm gate failed")
|
|
39
|
+
|| learningReason.includes("rule fallback rejected candidate")) {
|
|
40
|
+
return "unknown";
|
|
41
|
+
}
|
|
42
|
+
if (inspection.intervention === "skip" && (readiness.rawRecords > 0 || readiness.taskRuns > 0 || readiness.candidates > 0 || readiness.nodes > 0)) {
|
|
43
|
+
return "no_strong_match";
|
|
44
|
+
}
|
|
45
|
+
return "unknown";
|
|
46
|
+
};
|
|
22
47
|
const toReviewEvent = (nodeId, eventType, source, taskRunId) => ({
|
|
23
48
|
id: createId("review"),
|
|
24
49
|
node_id: nodeId,
|
|
@@ -27,6 +52,65 @@ const toReviewEvent = (nodeId, eventType, source, taskRunId) => ({
|
|
|
27
52
|
source,
|
|
28
53
|
created_at: nowIso()
|
|
29
54
|
});
|
|
55
|
+
const deriveNodeRisk = (node) => {
|
|
56
|
+
if (node.state === "candidate") {
|
|
57
|
+
return "high";
|
|
58
|
+
}
|
|
59
|
+
if (node.state === "priority_candidate") {
|
|
60
|
+
return "medium";
|
|
61
|
+
}
|
|
62
|
+
if (node.state === "cooling" || node.harmed_count > 0 || node.node_type === "warning") {
|
|
63
|
+
return node.harmed_count > node.helped_count ? "high" : "medium";
|
|
64
|
+
}
|
|
65
|
+
return "low";
|
|
66
|
+
};
|
|
67
|
+
const deriveQualityBand = (node) => {
|
|
68
|
+
if (node.state === "retired" || node.harmed_count > node.helped_count || deriveNodeRisk(node) === "high") {
|
|
69
|
+
return "risky";
|
|
70
|
+
}
|
|
71
|
+
if (node.state === "active" &&
|
|
72
|
+
node.validation_state === "validated_by_reuse" &&
|
|
73
|
+
node.harmed_count === 0) {
|
|
74
|
+
return "strong";
|
|
75
|
+
}
|
|
76
|
+
return "building";
|
|
77
|
+
};
|
|
78
|
+
const buildQualityDrivers = (node) => {
|
|
79
|
+
const drivers = [];
|
|
80
|
+
if (node.validation_state === "validated_by_reuse") {
|
|
81
|
+
drivers.push("This node has already been validated by successful reuse.");
|
|
82
|
+
}
|
|
83
|
+
if (node.helped_count > node.harmed_count) {
|
|
84
|
+
drivers.push("Helpful outcomes still outweigh harmful ones for this node.");
|
|
85
|
+
}
|
|
86
|
+
else if (node.harmed_count > node.helped_count) {
|
|
87
|
+
drivers.push("Harmful outcomes currently outweigh helpful ones for this node.");
|
|
88
|
+
}
|
|
89
|
+
if (node.state === "candidate" || node.state === "priority_candidate") {
|
|
90
|
+
drivers.push("This node is still early in its lifecycle and needs more runtime evidence.");
|
|
91
|
+
}
|
|
92
|
+
else if (node.state === "cooling") {
|
|
93
|
+
drivers.push("This node is in cooling state because recent runtime evidence weakened confidence.");
|
|
94
|
+
}
|
|
95
|
+
return drivers.slice(0, 3);
|
|
96
|
+
};
|
|
97
|
+
const deriveConfidence = (node) => {
|
|
98
|
+
if (node.validation_state === "validated_by_reuse" && node.state === "active" && node.harmed_count === 0) {
|
|
99
|
+
return "high";
|
|
100
|
+
}
|
|
101
|
+
if (node.state === "candidate" || node.harmed_count > node.helped_count) {
|
|
102
|
+
return "low";
|
|
103
|
+
}
|
|
104
|
+
return "medium";
|
|
105
|
+
};
|
|
106
|
+
const formatTaskFamily = (taskType) => taskType === "general" ? "general tasks" : `${taskType} tasks`;
|
|
107
|
+
const buildApplicabilityProfile = (node) => ({
|
|
108
|
+
bestFit: `${formatTaskFamily(node.task_type)} in this repo scope`,
|
|
109
|
+
scopeValidity: node.applicability_notes ?? "Use within the same repo scope unless fresh evidence says otherwise.",
|
|
110
|
+
confidence: deriveConfidence(node),
|
|
111
|
+
risk: deriveNodeRisk(node),
|
|
112
|
+
avoidWhen: node.stop_condition ?? node.escalation_condition ?? node.avoid_steps?.[0]
|
|
113
|
+
});
|
|
30
114
|
const toNodeSummary = (node) => ({
|
|
31
115
|
id: node.id,
|
|
32
116
|
type: node.node_type,
|
|
@@ -36,13 +120,21 @@ const toNodeSummary = (node) => ({
|
|
|
36
120
|
distillationMode: node.distillation_mode_used,
|
|
37
121
|
distillationSource: node.distillation_source,
|
|
38
122
|
redistilledFrom: node.redistilled_from,
|
|
123
|
+
promotionSignal: node.promotion_signal,
|
|
124
|
+
promotionReason: node.promotion_reason,
|
|
125
|
+
mergeDecision: node.merge_decision,
|
|
126
|
+
mergeReason: node.merge_reason,
|
|
127
|
+
priorityPromotionApplied: node.priority_promotion_applied,
|
|
39
128
|
triggerPattern: node.trigger_pattern,
|
|
40
129
|
evidenceSummary: node.evidence_summary,
|
|
41
130
|
originRecordIds: node.origin_record_ids,
|
|
42
131
|
helped: node.helped_count,
|
|
43
132
|
harmed: node.harmed_count,
|
|
44
133
|
lastUsedAt: node.last_used_at,
|
|
45
|
-
hint: node.compact_hint
|
|
134
|
+
hint: node.compact_hint,
|
|
135
|
+
qualityBand: deriveQualityBand(node),
|
|
136
|
+
qualityDrivers: buildQualityDrivers(node),
|
|
137
|
+
applicabilityProfile: buildApplicabilityProfile(node)
|
|
46
138
|
});
|
|
47
139
|
const toNodeDetail = (node) => ({
|
|
48
140
|
...toNodeSummary(node),
|
|
@@ -73,7 +165,6 @@ const applyNodeFeedback = (node, feedback) => {
|
|
|
73
165
|
state: node.state === "retired" ? "retired" : transitionState(next)
|
|
74
166
|
};
|
|
75
167
|
};
|
|
76
|
-
const REPO_SUMMARY_TARGETS = ["codex", "agents", "claude", "github"];
|
|
77
168
|
const summarizeAutomaticFeedback = (events) => {
|
|
78
169
|
const automatic = events.filter((event) => event.source === "automatic");
|
|
79
170
|
if (automatic.some((event) => event.event_type === "mark_harmed")) {
|
|
@@ -149,36 +240,73 @@ const buildLatestTimeline = (input) => {
|
|
|
149
240
|
}
|
|
150
241
|
return entries.sort((left, right) => left.createdAt.localeCompare(right.createdAt));
|
|
151
242
|
};
|
|
152
|
-
const
|
|
153
|
-
if (!
|
|
243
|
+
const compareIsoDesc = (left, right) => {
|
|
244
|
+
if (!left && !right) {
|
|
245
|
+
return 0;
|
|
246
|
+
}
|
|
247
|
+
if (!left) {
|
|
248
|
+
return 1;
|
|
249
|
+
}
|
|
250
|
+
if (!right) {
|
|
251
|
+
return -1;
|
|
252
|
+
}
|
|
253
|
+
return right.localeCompare(left);
|
|
254
|
+
};
|
|
255
|
+
const buildDecisionExplanation = (input) => {
|
|
256
|
+
const scorecard = input.scorecard;
|
|
257
|
+
if (!scorecard) {
|
|
154
258
|
return undefined;
|
|
155
259
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
260
|
+
if (scorecard.mode === "inject_conservative") {
|
|
261
|
+
if (scorecard.decisionReason === "ambiguous_same_family_candidate") {
|
|
262
|
+
return "ExperienceEngine found a promising same-family match and chose conservative injection instead of skipping.";
|
|
263
|
+
}
|
|
264
|
+
if (scorecard.decisionReason === "promising_candidate_quality") {
|
|
265
|
+
return "ExperienceEngine found a credible candidate, but kept the injection conservative until it has stronger runtime proof.";
|
|
266
|
+
}
|
|
267
|
+
return "ExperienceEngine chose conservative injection because the best match still needs more runtime evidence.";
|
|
268
|
+
}
|
|
269
|
+
if (scorecard.decisionReason === "mature_validated_candidate") {
|
|
270
|
+
return "A mature validated candidate cleared the fast path, so ExperienceEngine injected it normally.";
|
|
271
|
+
}
|
|
272
|
+
if (scorecard.decisionReason === "candidate_quality_positive") {
|
|
273
|
+
return "Candidate quality was strong enough to justify intervention for this task.";
|
|
274
|
+
}
|
|
275
|
+
if (scorecard.mode === "inject") {
|
|
276
|
+
return "ExperienceEngine injected the best available reusable guidance for this task.";
|
|
277
|
+
}
|
|
278
|
+
if (input.intervention === "shadow") {
|
|
279
|
+
return "ExperienceEngine found a usable match, but delivery was suppressed because this run was in shadow mode.";
|
|
280
|
+
}
|
|
281
|
+
if (input.intervention === "holdout") {
|
|
282
|
+
return "ExperienceEngine found a usable match, but delivery was withheld for evaluation.";
|
|
283
|
+
}
|
|
284
|
+
return undefined;
|
|
165
285
|
};
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
286
|
+
const buildTrustSummary = (input) => {
|
|
287
|
+
const scorecard = input.scorecard;
|
|
288
|
+
const primaryNode = input.injectedNodes[0];
|
|
289
|
+
if (!scorecard || !primaryNode) {
|
|
290
|
+
return undefined;
|
|
291
|
+
}
|
|
292
|
+
return `${scorecard.riskLevel}-risk ${primaryNode.state} guidance with ${primaryNode.helped} helped and ${primaryNode.harmed} harmed signal(s).`;
|
|
293
|
+
};
|
|
294
|
+
const buildRetrievalNotes = (scorecard) => {
|
|
295
|
+
if (!scorecard) {
|
|
296
|
+
return [];
|
|
297
|
+
}
|
|
298
|
+
const notes = [];
|
|
299
|
+
if (scorecard.queryRewriteApplied) {
|
|
300
|
+
notes.push("Query rewrite preserved retrieval intent for this task.");
|
|
301
|
+
}
|
|
302
|
+
const rerankSource = scorecard.topCandidates?.[0]?.rerankSource;
|
|
303
|
+
if (rerankSource === "model") {
|
|
304
|
+
notes.push("Model reranking participated in the final ordering.");
|
|
305
|
+
}
|
|
306
|
+
if (scorecard.fastPathApplied) {
|
|
307
|
+
notes.push("A strong candidate fast path was used.");
|
|
308
|
+
}
|
|
309
|
+
return notes;
|
|
182
310
|
};
|
|
183
311
|
export class ExperienceInteractionService {
|
|
184
312
|
inputRepo;
|
|
@@ -190,10 +318,7 @@ export class ExperienceInteractionService {
|
|
|
190
318
|
outcomeRepo;
|
|
191
319
|
reviewEventRepo;
|
|
192
320
|
scopeRepo;
|
|
193
|
-
|
|
194
|
-
packRegistry;
|
|
195
|
-
packsDir;
|
|
196
|
-
constructor(config, options = {}) {
|
|
321
|
+
constructor(config) {
|
|
197
322
|
const db = openDatabase(config);
|
|
198
323
|
bootstrapDatabase(db);
|
|
199
324
|
this.inputRepo = new InputRecordRepository(db);
|
|
@@ -205,11 +330,6 @@ export class ExperienceInteractionService {
|
|
|
205
330
|
this.outcomeRepo = new OutcomeRecordRepository(db);
|
|
206
331
|
this.reviewEventRepo = new ReviewEventRepository(db);
|
|
207
332
|
this.scopeRepo = new ScopeRepository(db);
|
|
208
|
-
this.packRepo = new ExperiencePackRepository(db);
|
|
209
|
-
this.packsDir = options.packsDir ?? `${config.dataDir}/packs`;
|
|
210
|
-
this.packRegistry = new ExperiencePackRegistry({
|
|
211
|
-
packsDir: this.packsDir
|
|
212
|
-
});
|
|
213
333
|
}
|
|
214
334
|
inspectRecord(record) {
|
|
215
335
|
if (!record) {
|
|
@@ -248,22 +368,13 @@ export class ExperienceInteractionService {
|
|
|
248
368
|
: "inject";
|
|
249
369
|
const outcomeRecord = taskRun?.id ? this.outcomeRepo.listByTaskRunId(taskRun.id)[0] : undefined;
|
|
250
370
|
const latestAutomaticFeedback = reviewEvents.find((event) => event.source === "automatic");
|
|
251
|
-
const scopePackStatus = this.inspectScopePackStatusByScopeId(record.scope_id);
|
|
252
|
-
const matchedPackIds = new Set();
|
|
253
|
-
for (const activation of scopePackStatus.activations.filter((entry) => entry.enabled)) {
|
|
254
|
-
const version = activation.pinnedVersion ?? activation.currentVersion;
|
|
255
|
-
for (const membership of this.packRepo.listMemberships(activation.packId, version)) {
|
|
256
|
-
if (selectedNodeIds.includes(membership.node_id)) {
|
|
257
|
-
matchedPackIds.add(activation.packId);
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
371
|
const autoFeedbackReason = inferAutoFeedbackReason({
|
|
262
372
|
explicitReason: injectionEvent?.attribution_reason,
|
|
263
373
|
autoFeedback,
|
|
264
374
|
intervention,
|
|
265
375
|
outcome: record.outcome_signal
|
|
266
376
|
});
|
|
377
|
+
const decisionExplanation = buildDecisionExplanation({ intervention, scorecard });
|
|
267
378
|
return {
|
|
268
379
|
sessionId: record.session_id,
|
|
269
380
|
scopeId: record.scope_id,
|
|
@@ -278,6 +389,9 @@ export class ExperienceInteractionService {
|
|
|
278
389
|
hints: injectedNodes.map((node) => node.compact_hint),
|
|
279
390
|
evidence: record.evidence,
|
|
280
391
|
scorecard,
|
|
392
|
+
decisionExplanation,
|
|
393
|
+
trustSummary: buildTrustSummary({ scorecard, injectedNodes: injectedNodes.map(toNodeSummary) }),
|
|
394
|
+
retrievalNotes: buildRetrievalNotes(scorecard),
|
|
281
395
|
timeline: buildLatestTimeline({
|
|
282
396
|
record,
|
|
283
397
|
taskRunCreatedAt: taskRun?.created_at,
|
|
@@ -290,14 +404,110 @@ export class ExperienceInteractionService {
|
|
|
290
404
|
autoFeedback,
|
|
291
405
|
autoFeedbackCreatedAt: latestAutomaticFeedback?.created_at
|
|
292
406
|
}),
|
|
293
|
-
|
|
294
|
-
|
|
407
|
+
learningStatus: taskRun?.learning_status,
|
|
408
|
+
learningReason: taskRun?.learning_reason,
|
|
295
409
|
summary: record.task_summary,
|
|
296
410
|
createdAt: record.created_at
|
|
297
411
|
};
|
|
298
412
|
}
|
|
299
|
-
|
|
300
|
-
|
|
413
|
+
inspectInjectionEvent(event) {
|
|
414
|
+
if (!event) {
|
|
415
|
+
return undefined;
|
|
416
|
+
}
|
|
417
|
+
const taskRun = event.session_id ? this.taskRunRepo.getLatestBySessionId(event.session_id) : undefined;
|
|
418
|
+
const latestRecord = event.session_id ? this.inputRepo.getLatestBySessionId(event.session_id) : undefined;
|
|
419
|
+
if (latestRecord) {
|
|
420
|
+
return this.inspectRecord(latestRecord);
|
|
421
|
+
}
|
|
422
|
+
const injectedNodes = this.nodeRepo.listByIds(event.injected_node_ids);
|
|
423
|
+
const reviewEvents = taskRun?.id ? this.reviewEventRepo.listByTaskRunId(taskRun.id) : [];
|
|
424
|
+
const autoFeedback = summarizeAutomaticFeedback(reviewEvents);
|
|
425
|
+
const latestAutomaticFeedback = reviewEvents.find((reviewEvent) => reviewEvent.source === "automatic");
|
|
426
|
+
const intervention = !event.delivered
|
|
427
|
+
? event.delivery_mode === "holdout"
|
|
428
|
+
? "holdout"
|
|
429
|
+
: "shadow"
|
|
430
|
+
: "inject";
|
|
431
|
+
const outcomeRecord = taskRun?.id ? this.outcomeRepo.listByTaskRunId(taskRun.id)[0] : undefined;
|
|
432
|
+
const outcome = outcomeRecord?.outcome_signal ??
|
|
433
|
+
(taskRun?.final_status === "success" ? "success" : taskRun?.final_status === "failure" ? "failure" : "unknown");
|
|
434
|
+
const summary = event.task_summary ?? taskRun?.task_summary ?? "Latest injection event";
|
|
435
|
+
const decisionExplanation = buildDecisionExplanation({ intervention, scorecard: event.scorecard });
|
|
436
|
+
return {
|
|
437
|
+
sessionId: event.session_id,
|
|
438
|
+
scopeId: event.scope_id,
|
|
439
|
+
taskType: event.task_type,
|
|
440
|
+
intervention,
|
|
441
|
+
deliveryMode: event.delivery_mode,
|
|
442
|
+
delivered: event.delivered,
|
|
443
|
+
autoFeedback,
|
|
444
|
+
autoFeedbackReason: inferAutoFeedbackReason({
|
|
445
|
+
explicitReason: event.attribution_reason,
|
|
446
|
+
autoFeedback,
|
|
447
|
+
intervention,
|
|
448
|
+
outcome
|
|
449
|
+
}),
|
|
450
|
+
outcome,
|
|
451
|
+
injectedNodes: injectedNodes.map(toNodeSummary),
|
|
452
|
+
hints: injectedNodes.map((node) => node.compact_hint),
|
|
453
|
+
evidence: [],
|
|
454
|
+
scorecard: event.scorecard,
|
|
455
|
+
decisionExplanation,
|
|
456
|
+
trustSummary: buildTrustSummary({ scorecard: event.scorecard, injectedNodes: injectedNodes.map(toNodeSummary) }),
|
|
457
|
+
retrievalNotes: buildRetrievalNotes(event.scorecard),
|
|
458
|
+
timeline: buildLatestTimeline({
|
|
459
|
+
record: {
|
|
460
|
+
record_id: `injection:${event.injection_id}`,
|
|
461
|
+
scope_id: event.scope_id,
|
|
462
|
+
session_id: event.session_id,
|
|
463
|
+
task_type: event.task_type,
|
|
464
|
+
task_summary: summary,
|
|
465
|
+
outcome_signal: outcome,
|
|
466
|
+
context_summary: taskRun?.context_summary,
|
|
467
|
+
evidence: [],
|
|
468
|
+
injected_node_ids: event.injected_node_ids,
|
|
469
|
+
created_at: event.created_at
|
|
470
|
+
},
|
|
471
|
+
taskRunCreatedAt: taskRun?.created_at,
|
|
472
|
+
outcomeCreatedAt: outcomeRecord?.created_at,
|
|
473
|
+
outcomeSummary: outcomeRecord?.summary,
|
|
474
|
+
injectionCreatedAt: event.created_at,
|
|
475
|
+
intervention,
|
|
476
|
+
delivered: event.delivered,
|
|
477
|
+
injectedCount: injectedNodes.length,
|
|
478
|
+
autoFeedback,
|
|
479
|
+
autoFeedbackCreatedAt: latestAutomaticFeedback?.created_at
|
|
480
|
+
}),
|
|
481
|
+
learningStatus: taskRun?.learning_status,
|
|
482
|
+
learningReason: taskRun?.learning_reason,
|
|
483
|
+
summary,
|
|
484
|
+
createdAt: event.created_at
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
inspectLast(cwd = process.cwd()) {
|
|
488
|
+
const scope = resolveScope(cwd);
|
|
489
|
+
const latestRecordInScope = this.inputRepo.getLatestByScope(scope.scope_id);
|
|
490
|
+
const latestInjectionInScope = this.injectionRepo.getLatestByScope(scope.scope_id);
|
|
491
|
+
const latestScopedInspection = compareIsoDesc(latestInjectionInScope?.created_at, latestRecordInScope?.created_at) < 0
|
|
492
|
+
? this.inspectInjectionEvent(latestInjectionInScope)
|
|
493
|
+
: this.inspectRecord(latestRecordInScope);
|
|
494
|
+
if (latestScopedInspection) {
|
|
495
|
+
return latestScopedInspection;
|
|
496
|
+
}
|
|
497
|
+
const latestRecord = this.inputRepo.getLatest();
|
|
498
|
+
const latestInjection = this.injectionRepo.getLatest();
|
|
499
|
+
return compareIsoDesc(latestInjection?.created_at, latestRecord?.created_at) < 0
|
|
500
|
+
? this.inspectInjectionEvent(latestInjection)
|
|
501
|
+
: this.inspectRecord(latestRecord);
|
|
502
|
+
}
|
|
503
|
+
inspectLatestInjected(cwd = process.cwd()) {
|
|
504
|
+
const scope = resolveScope(cwd);
|
|
505
|
+
const latestScopedInjected = this.inputRepo.getLatestInjectedByScope(scope.scope_id);
|
|
506
|
+
if (latestScopedInjected) {
|
|
507
|
+
return this.inspectRecord(latestScopedInjected);
|
|
508
|
+
}
|
|
509
|
+
const latestInjected = this.inputRepo.getLatestInjected();
|
|
510
|
+
return latestInjected ? this.inspectRecord(latestInjected) : undefined;
|
|
301
511
|
}
|
|
302
512
|
inspectRecent(options = {}) {
|
|
303
513
|
return this.inputRepo.listRecent(options).map((record) => ({
|
|
@@ -319,149 +529,6 @@ export class ExperienceInteractionService {
|
|
|
319
529
|
const node = this.nodeRepo.getById(nodeId);
|
|
320
530
|
return node ? toNodeDetail(node) : undefined;
|
|
321
531
|
}
|
|
322
|
-
listPacks() {
|
|
323
|
-
return this.packRepo.listPacks().map((pack) => ({
|
|
324
|
-
packId: pack.pack_id,
|
|
325
|
-
name: pack.name,
|
|
326
|
-
description: pack.description,
|
|
327
|
-
owner: pack.owner,
|
|
328
|
-
status: pack.status,
|
|
329
|
-
currentVersion: pack.current_version,
|
|
330
|
-
createdAt: pack.created_at,
|
|
331
|
-
updatedAt: pack.updated_at,
|
|
332
|
-
publishedAt: pack.published_at,
|
|
333
|
-
rolledBackAt: pack.rolled_back_at,
|
|
334
|
-
scopeHints: pack.scope_hints,
|
|
335
|
-
taskFamilies: pack.task_families,
|
|
336
|
-
hostCompatibility: pack.host_compatibility
|
|
337
|
-
}));
|
|
338
|
-
}
|
|
339
|
-
inspectPack(packId) {
|
|
340
|
-
const pack = this.packRepo.getPack(packId);
|
|
341
|
-
if (!pack) {
|
|
342
|
-
return undefined;
|
|
343
|
-
}
|
|
344
|
-
const manifest = this.packRegistry.readVersionManifest(packId, pack.current_version);
|
|
345
|
-
const nodes = this.packRegistry.readVersionNodes(packId, pack.current_version);
|
|
346
|
-
return {
|
|
347
|
-
packId: pack.pack_id,
|
|
348
|
-
name: pack.name,
|
|
349
|
-
description: pack.description,
|
|
350
|
-
owner: pack.owner,
|
|
351
|
-
status: pack.status,
|
|
352
|
-
currentVersion: pack.current_version,
|
|
353
|
-
createdAt: pack.created_at,
|
|
354
|
-
updatedAt: pack.updated_at,
|
|
355
|
-
publishedAt: pack.published_at,
|
|
356
|
-
rolledBackAt: pack.rolled_back_at,
|
|
357
|
-
scopeHints: pack.scope_hints,
|
|
358
|
-
taskFamilies: pack.task_families,
|
|
359
|
-
hostCompatibility: pack.host_compatibility,
|
|
360
|
-
manifest,
|
|
361
|
-
nodeIds: nodes.map((node) => node.id),
|
|
362
|
-
compiledArtifacts: this.packRegistry.listCompiledArtifacts(packId),
|
|
363
|
-
compileStatus: this.packRegistry.getCompileStatus(packId, pack.current_version),
|
|
364
|
-
activations: this.packRepo.listActivationsByPack(packId).map((activation) => ({
|
|
365
|
-
scopeId: activation.scope_id,
|
|
366
|
-
enabled: activation.enabled,
|
|
367
|
-
pinnedVersion: activation.pinned_version,
|
|
368
|
-
updatedAt: activation.updated_at
|
|
369
|
-
}))
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
enablePack(args) {
|
|
373
|
-
const scopeId = resolveScope(args.cwd).scope_id;
|
|
374
|
-
const pack = this.packRepo.getPack(args.packId);
|
|
375
|
-
if (!pack) {
|
|
376
|
-
throw new Error(`Unknown experience pack: ${args.packId}`);
|
|
377
|
-
}
|
|
378
|
-
const existing = this.packRepo.listActivations(scopeId).find((activation) => activation.pack_id === args.packId);
|
|
379
|
-
const updatedAt = nowIso();
|
|
380
|
-
this.packRepo.upsertActivation({
|
|
381
|
-
scope_id: scopeId,
|
|
382
|
-
pack_id: args.packId,
|
|
383
|
-
enabled: true,
|
|
384
|
-
pinned_version: pack.current_version,
|
|
385
|
-
created_at: existing?.created_at ?? updatedAt,
|
|
386
|
-
updated_at: updatedAt
|
|
387
|
-
});
|
|
388
|
-
return {
|
|
389
|
-
scopeId,
|
|
390
|
-
packId: args.packId,
|
|
391
|
-
enabled: true,
|
|
392
|
-
pinnedVersion: pack.current_version,
|
|
393
|
-
updatedAt
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
disablePack(args) {
|
|
397
|
-
const scopeId = resolveScope(args.cwd).scope_id;
|
|
398
|
-
const existing = this.packRepo.listActivations(scopeId).find((activation) => activation.pack_id === args.packId);
|
|
399
|
-
const updatedAt = nowIso();
|
|
400
|
-
this.packRepo.upsertActivation({
|
|
401
|
-
scope_id: scopeId,
|
|
402
|
-
pack_id: args.packId,
|
|
403
|
-
enabled: false,
|
|
404
|
-
pinned_version: existing?.pinned_version,
|
|
405
|
-
created_at: existing?.created_at ?? updatedAt,
|
|
406
|
-
updated_at: updatedAt
|
|
407
|
-
});
|
|
408
|
-
return {
|
|
409
|
-
scopeId,
|
|
410
|
-
packId: args.packId,
|
|
411
|
-
enabled: false,
|
|
412
|
-
pinnedVersion: existing?.pinned_version,
|
|
413
|
-
updatedAt
|
|
414
|
-
};
|
|
415
|
-
}
|
|
416
|
-
compilePack(args) {
|
|
417
|
-
return compilePack({
|
|
418
|
-
packsDir: this.packsDir,
|
|
419
|
-
packId: args.packId,
|
|
420
|
-
version: args.version,
|
|
421
|
-
target: args.target
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
inspectPackDeploymentStatus(args) {
|
|
425
|
-
return deployCompiledPack({
|
|
426
|
-
packsDir: this.packsDir,
|
|
427
|
-
packId: args.packId,
|
|
428
|
-
version: args.version,
|
|
429
|
-
target: args.target,
|
|
430
|
-
repoPath: args.repoPath,
|
|
431
|
-
statusOnly: true
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
deployPackPreview(args) {
|
|
435
|
-
return deployCompiledPack({
|
|
436
|
-
packsDir: this.packsDir,
|
|
437
|
-
packId: args.packId,
|
|
438
|
-
version: args.version,
|
|
439
|
-
target: args.target,
|
|
440
|
-
repoPath: args.repoPath,
|
|
441
|
-
dryRun: true
|
|
442
|
-
});
|
|
443
|
-
}
|
|
444
|
-
inspectScopePackStatus(cwd = process.cwd()) {
|
|
445
|
-
return this.inspectScopePackStatusByScopeId(resolveScope(cwd).scope_id);
|
|
446
|
-
}
|
|
447
|
-
inspectScopePackStatusByScopeId(scopeId) {
|
|
448
|
-
const activations = this.packRepo
|
|
449
|
-
.listActivations(scopeId)
|
|
450
|
-
.map((activation) => toScopePackActivation(this.packRepo.getPack(activation.pack_id), activation))
|
|
451
|
-
.filter((activation) => Boolean(activation));
|
|
452
|
-
const publishedPackIds = Array.from(new Set(activations
|
|
453
|
-
.filter((activation) => activation.status === "published" || activation.status === "rolled_back")
|
|
454
|
-
.map((activation) => activation.packId)));
|
|
455
|
-
const compiler = summarizePackCompiler(this.packRegistry, publishedPackIds, new Map(activations
|
|
456
|
-
.filter((activation) => activation.status === "published" || activation.status === "rolled_back")
|
|
457
|
-
.map((activation) => [activation.packId, activation.currentVersion])));
|
|
458
|
-
return {
|
|
459
|
-
scopeId,
|
|
460
|
-
enabledCount: activations.filter((activation) => activation.enabled).length,
|
|
461
|
-
activations,
|
|
462
|
-
compiler
|
|
463
|
-
};
|
|
464
|
-
}
|
|
465
532
|
listNodesByState(state) {
|
|
466
533
|
return this.nodeRepo.listByState(state).map(toNodeSummary);
|
|
467
534
|
}
|
|
@@ -474,7 +541,7 @@ export class ExperienceInteractionService {
|
|
|
474
541
|
buildLearningSummary(scopeId) {
|
|
475
542
|
const candidateStates = ["pending", "distilled", "failed", "discarded"];
|
|
476
543
|
const jobStates = ["pending", "processing", "succeeded", "failed", "discarded"];
|
|
477
|
-
const nodeStates = ["candidate", "active", "cooling", "retired"];
|
|
544
|
+
const nodeStates = ["candidate", "priority_candidate", "active", "cooling", "retired"];
|
|
478
545
|
const nodeSources = ["explicit_provider", "rule", "disabled"];
|
|
479
546
|
const attributionReasons = [
|
|
480
547
|
"success_outcome",
|
|
@@ -492,10 +559,6 @@ export class ExperienceInteractionService {
|
|
|
492
559
|
: candidateStates.flatMap((state) => this.candidateRepo.listByLifecycleState(state));
|
|
493
560
|
const candidateIds = new Set(candidates.map((candidate) => candidate.id));
|
|
494
561
|
const jobs = jobStates.flatMap((state) => this.jobRepo.listByStatus(state)).filter((job) => candidateIds.has(job.candidate_id));
|
|
495
|
-
const publishedPacks = this.packRepo
|
|
496
|
-
.listPacks()
|
|
497
|
-
.filter((pack) => pack.status === "published" || pack.status === "rolled_back");
|
|
498
|
-
const compiler = summarizePackCompiler(this.packRegistry, publishedPacks.map((pack) => pack.pack_id), new Map(publishedPacks.map((pack) => [pack.pack_id, pack.current_version])));
|
|
499
562
|
const effectiveness = {
|
|
500
563
|
decisions: scopeId ? this.injectionRepo.countByScope(scopeId) : this.injectionRepo.count(),
|
|
501
564
|
live: scopeId ? this.injectionRepo.countByScopeAndDeliveryMode(scopeId, "live") : this.injectionRepo.countByDeliveryMode("live"),
|
|
@@ -530,7 +593,6 @@ export class ExperienceInteractionService {
|
|
|
530
593
|
outcomes: scopeId ? this.outcomeRepo.countByScope(scopeId) : this.outcomeRepo.count(),
|
|
531
594
|
reviews: scopeId ? this.reviewEventRepo.countByNodeScope(scopeId) : this.reviewEventRepo.count()
|
|
532
595
|
},
|
|
533
|
-
compiler,
|
|
534
596
|
latestRecordCreatedAt: latestRecord?.created_at
|
|
535
597
|
};
|
|
536
598
|
}
|
|
@@ -539,11 +601,6 @@ export class ExperienceInteractionService {
|
|
|
539
601
|
const latestRecord = this.inputRepo.getLatestByScope(scope.scope_id);
|
|
540
602
|
const latest = latestRecord ? this.inspectRecord(latestRecord) : undefined;
|
|
541
603
|
const learning = this.buildLearningSummary(scope.scope_id);
|
|
542
|
-
const scopePackStatus = this.inspectScopePackStatusByScopeId(scope.scope_id);
|
|
543
|
-
const activePacks = scopePackStatus.activations.filter((activation) => activation.enabled);
|
|
544
|
-
const matchedPackIds = new Set(latest?.matchedPacks.map((pack) => pack.packId) ?? []);
|
|
545
|
-
const matchedPacks = scopePackStatus.activations.filter((activation) => matchedPackIds.has(activation.packId));
|
|
546
|
-
const deployments = this.inspectRepoDeploymentStatuses(scopePackStatus.activations, cwd);
|
|
547
604
|
return buildRepoSummary({
|
|
548
605
|
scope: {
|
|
549
606
|
scopeId: scope.scope_id,
|
|
@@ -551,14 +608,12 @@ export class ExperienceInteractionService {
|
|
|
551
608
|
rootPath: scope.root_path
|
|
552
609
|
},
|
|
553
610
|
latest: latest && latest.scopeId === scope.scope_id ? latest : undefined,
|
|
554
|
-
learning
|
|
555
|
-
activePacks,
|
|
556
|
-
matchedPacks,
|
|
557
|
-
deployments
|
|
611
|
+
learning
|
|
558
612
|
});
|
|
559
613
|
}
|
|
560
|
-
inspectFirstValueReadiness() {
|
|
561
|
-
const
|
|
614
|
+
inspectFirstValueReadiness(cwd = process.cwd()) {
|
|
615
|
+
const scope = resolveScope(cwd);
|
|
616
|
+
const summary = this.buildLearningSummary(scope.scope_id);
|
|
562
617
|
const rawRecords = summary.runtime.records;
|
|
563
618
|
const taskRuns = summary.runtime.taskRuns;
|
|
564
619
|
const candidates = summary.candidates.pending;
|
|
@@ -582,8 +637,68 @@ export class ExperienceInteractionService {
|
|
|
582
637
|
nextStep
|
|
583
638
|
};
|
|
584
639
|
}
|
|
585
|
-
|
|
586
|
-
const
|
|
640
|
+
inspectDecisionHealth(cwd = process.cwd(), limit = 10) {
|
|
641
|
+
const scope = resolveScope(cwd);
|
|
642
|
+
const recentRecords = this.inputRepo.listRecentByScope(scope.scope_id, limit);
|
|
643
|
+
let recentInjects = 0;
|
|
644
|
+
let recentConservativeInjects = 0;
|
|
645
|
+
let recentSkips = 0;
|
|
646
|
+
let recentFastPathActivations = 0;
|
|
647
|
+
let recentRerankParticipations = 0;
|
|
648
|
+
let recentQueryRewriteUsages = 0;
|
|
649
|
+
const scopedNodes = this.nodeRepo.listByScope(scope.scope_id);
|
|
650
|
+
const recentNodes = scopedNodes.slice(0, limit);
|
|
651
|
+
const currentPriorityCandidates = scopedNodes.filter((node) => node.state === "priority_candidate").length;
|
|
652
|
+
const recentConvergedUpdates = recentNodes.filter((node) => node.merge_decision === "UPDATE").length;
|
|
653
|
+
const recentPriorityPromotions = recentNodes.filter((node) => node.priority_promotion_applied).length;
|
|
654
|
+
let lastDecisionMode;
|
|
655
|
+
for (const record of recentRecords) {
|
|
656
|
+
const injectionEvent = record.session_id
|
|
657
|
+
? this.injectionRepo.getLatestBySessionId(record.session_id)
|
|
658
|
+
: undefined;
|
|
659
|
+
const decisionMode = injectionEvent?.mode ?? "skip";
|
|
660
|
+
if (!lastDecisionMode) {
|
|
661
|
+
lastDecisionMode = decisionMode;
|
|
662
|
+
}
|
|
663
|
+
if (decisionMode === "inject") {
|
|
664
|
+
recentInjects += 1;
|
|
665
|
+
}
|
|
666
|
+
else if (decisionMode === "inject_conservative") {
|
|
667
|
+
recentConservativeInjects += 1;
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
recentSkips += 1;
|
|
671
|
+
}
|
|
672
|
+
if (injectionEvent?.scorecard?.fastPathApplied) {
|
|
673
|
+
recentFastPathActivations += 1;
|
|
674
|
+
}
|
|
675
|
+
if (injectionEvent?.scorecard?.topCandidates?.some((candidate) => typeof candidate.rerankScore === "number")) {
|
|
676
|
+
recentRerankParticipations += 1;
|
|
677
|
+
}
|
|
678
|
+
if (injectionEvent?.scorecard?.queryRewriteApplied) {
|
|
679
|
+
recentQueryRewriteUsages += 1;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
return {
|
|
683
|
+
scopeId: scope.scope_id,
|
|
684
|
+
recentDecisions: recentRecords.length,
|
|
685
|
+
recentInjects,
|
|
686
|
+
recentConservativeInjects,
|
|
687
|
+
recentSkips,
|
|
688
|
+
recentFastPathActivations,
|
|
689
|
+
recentRerankParticipations,
|
|
690
|
+
recentQueryRewriteUsages,
|
|
691
|
+
currentPriorityCandidates,
|
|
692
|
+
recentConvergedUpdates,
|
|
693
|
+
recentPriorityPromotions,
|
|
694
|
+
lastDecisionMode
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
feedbackLast(feedback, cwd) {
|
|
698
|
+
const scope = cwd ? resolveScope(cwd) : undefined;
|
|
699
|
+
const record = scope
|
|
700
|
+
? this.inputRepo.getLatestInjectedByScope(scope.scope_id) ?? this.inputRepo.getLatestInjected()
|
|
701
|
+
: this.inputRepo.getLatestInjected();
|
|
587
702
|
if (!record) {
|
|
588
703
|
return {
|
|
589
704
|
status: "not_found",
|
|
@@ -672,35 +787,5 @@ export class ExperienceInteractionService {
|
|
|
672
787
|
state: updated.state
|
|
673
788
|
};
|
|
674
789
|
}
|
|
675
|
-
inspectRepoDeploymentStatuses(activations, repoPath) {
|
|
676
|
-
const published = activations.find((activation) => activation.enabled && (activation.status === "published" || activation.status === "rolled_back"));
|
|
677
|
-
if (!published) {
|
|
678
|
-
return REPO_SUMMARY_TARGETS.map((target) => ({
|
|
679
|
-
target,
|
|
680
|
-
status: "missing"
|
|
681
|
-
}));
|
|
682
|
-
}
|
|
683
|
-
return REPO_SUMMARY_TARGETS.map((target) => {
|
|
684
|
-
try {
|
|
685
|
-
const result = this.inspectPackDeploymentStatus({
|
|
686
|
-
packId: published.packId,
|
|
687
|
-
version: published.pinnedVersion ?? published.currentVersion,
|
|
688
|
-
target,
|
|
689
|
-
repoPath
|
|
690
|
-
});
|
|
691
|
-
return {
|
|
692
|
-
target,
|
|
693
|
-
status: result.deploymentStatus,
|
|
694
|
-
destination: result.destinationPath
|
|
695
|
-
};
|
|
696
|
-
}
|
|
697
|
-
catch {
|
|
698
|
-
return {
|
|
699
|
-
target,
|
|
700
|
-
status: "missing"
|
|
701
|
-
};
|
|
702
|
-
}
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
790
|
}
|
|
706
791
|
//# sourceMappingURL=service.js.map
|