@alan512/experienceengine 0.3.2 → 0.3.4
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/.mcp.json +11 -11
- package/dist/adapters/codex/mcp-server.js +1 -1
- package/dist/analyzer/llm-learning-gate.d.ts +7 -0
- package/dist/analyzer/llm-learning-gate.js +76 -11
- package/dist/analyzer/llm-learning-gate.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +2 -1
- package/dist/cli/commands/doctor.js +23 -0
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/inspect.js +9 -0
- package/dist/cli/commands/inspect.js.map +1 -1
- package/dist/cli/commands/status.js +16 -0
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/index.js +0 -0
- package/dist/controller/injection-renderer.d.ts +7 -2
- package/dist/controller/injection-renderer.js +50 -19
- package/dist/controller/injection-renderer.js.map +1 -1
- package/dist/controller/injection-scorecard.js +3 -0
- package/dist/controller/injection-scorecard.js.map +1 -1
- package/dist/controller/intervention-controller.js +8 -2
- package/dist/controller/intervention-controller.js.map +1 -1
- package/dist/controller/skip-reason.d.ts +12 -0
- package/dist/controller/skip-reason.js +82 -0
- package/dist/controller/skip-reason.js.map +1 -0
- package/dist/controller/skip-scorecard.d.ts +2 -0
- package/dist/controller/skip-scorecard.js +39 -0
- package/dist/controller/skip-scorecard.js.map +1 -0
- package/dist/install/claude-code-doctor.js +6 -4
- package/dist/install/claude-code-doctor.js.map +1 -1
- package/dist/install/claude-code-installer.js +2 -5
- package/dist/install/claude-code-installer.js.map +1 -1
- package/dist/install/claude-runtime-target.js +7 -1
- package/dist/install/claude-runtime-target.js.map +1 -1
- package/dist/install/openclaw-installer.js +5 -9
- package/dist/install/openclaw-installer.js.map +1 -1
- package/dist/interaction/service.d.ts +23 -0
- package/dist/interaction/service.js +85 -0
- package/dist/interaction/service.js.map +1 -1
- package/dist/plugin/openclaw-plugin.d.ts +1 -1
- package/dist/runtime/learning-candidate-factory.d.ts +3 -0
- package/dist/runtime/learning-candidate-factory.js +126 -0
- package/dist/runtime/learning-candidate-factory.js.map +1 -0
- package/dist/runtime/learning-pipeline-service.d.ts +24 -0
- package/dist/runtime/learning-pipeline-service.js +78 -0
- package/dist/runtime/learning-pipeline-service.js.map +1 -0
- package/dist/runtime/prompt-service.d.ts +3 -3
- package/dist/runtime/prompt-service.js +39 -34
- package/dist/runtime/prompt-service.js.map +1 -1
- package/dist/runtime/service.d.ts +5 -5
- package/dist/runtime/service.js +70 -306
- package/dist/runtime/service.js.map +1 -1
- package/dist/runtime/task-finalization-service.d.ts +36 -0
- package/dist/runtime/task-finalization-service.js +97 -0
- package/dist/runtime/task-finalization-service.js.map +1 -0
- package/dist/store/sqlite/repositories/task-run-repo.d.ts +1 -0
- package/dist/store/sqlite/repositories/task-run-repo.js +6 -0
- package/dist/store/sqlite/repositories/task-run-repo.js.map +1 -1
- package/dist/types/domain.d.ts +7 -0
- package/docs/releases/v0.3.3.md +18 -0
- package/docs/releases/v0.3.4.md +22 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +13 -12
|
@@ -2,6 +2,7 @@ import { buildInjectionScorecard } from "../controller/injection-scorecard.js";
|
|
|
2
2
|
import { buildRetrievalContext } from "../controller/retrieval-context.js";
|
|
3
3
|
import { decideIntervention } from "../controller/intervention-controller.js";
|
|
4
4
|
import { renderInlineNotice } from "../controller/inline-notice.js";
|
|
5
|
+
import { buildSkipScorecard } from "../controller/skip-scorecard.js";
|
|
5
6
|
import { buildExperienceInput } from "../input/input-adapter.js";
|
|
6
7
|
import { resolveScope } from "../input/scope-resolver.js";
|
|
7
8
|
import { evaluateRepoPolicy } from "../experience-management/repo-policy.js";
|
|
@@ -59,31 +60,6 @@ const resolveEpisodeId = (session, sessionId, input) => {
|
|
|
59
60
|
session.episodeId ??= stableId("episode", `${sessionId}:${input.scope_id}:${input.task_summary}`);
|
|
60
61
|
return session.episodeId;
|
|
61
62
|
};
|
|
62
|
-
const buildRecordOnlyDiagnosticScorecard = (input, sessionId, diagnostics) => ({
|
|
63
|
-
sessionId,
|
|
64
|
-
scopeId: input.scope_id,
|
|
65
|
-
taskType: input.task_type === "unknown" ? "general" : input.task_type,
|
|
66
|
-
taskSummary: input.task_summary,
|
|
67
|
-
mode: "skip",
|
|
68
|
-
interventionStrength: "diagnostic_hint",
|
|
69
|
-
riskLevel: "high",
|
|
70
|
-
recommendation: "Record-only diagnostic candidate matched; keep it out of prompt text until the live gate clears.",
|
|
71
|
-
reasons: ["A same-scope shadow candidate matched this task but was not delivered."],
|
|
72
|
-
topCandidates: diagnostics.topCandidates,
|
|
73
|
-
topCandidateScore: diagnostics.topCandidateScore,
|
|
74
|
-
scoreMargin: diagnostics.scoreMargin,
|
|
75
|
-
fastPathApplied: diagnostics.fastPathApplied,
|
|
76
|
-
queryRewriteApplied: diagnostics.queryRewriteApplied,
|
|
77
|
-
gateReason: diagnostics.gateReason,
|
|
78
|
-
decisionReason: diagnostics.decisionReason,
|
|
79
|
-
confidence: diagnostics.confidence,
|
|
80
|
-
budgetClass: diagnostics.budgetClass,
|
|
81
|
-
selectedCandidateIds: [],
|
|
82
|
-
recordOnlyDiagnosticCandidateIds: diagnostics.recordOnlyDiagnosticCandidateIds,
|
|
83
|
-
rejectedCandidates: diagnostics.rejectedCandidates,
|
|
84
|
-
nodes: [],
|
|
85
|
-
createdAt: nowIso()
|
|
86
|
-
});
|
|
87
63
|
export class ExperiencePromptRuntimeService {
|
|
88
64
|
config;
|
|
89
65
|
db;
|
|
@@ -127,21 +103,42 @@ export class ExperiencePromptRuntimeService {
|
|
|
127
103
|
const existingScope = this.scopeRepo.getById(resolvedScope.scope_id);
|
|
128
104
|
if (existingScope?.is_disabled) {
|
|
129
105
|
session.injectedNodeIds = [];
|
|
130
|
-
session.lastInjectionEvent = undefined;
|
|
131
106
|
session.context = {
|
|
132
107
|
...session.context,
|
|
133
108
|
injectedNodeIds: []
|
|
134
109
|
};
|
|
110
|
+
const disabledInput = {
|
|
111
|
+
...input,
|
|
112
|
+
scope_id: existingScope.scope_id,
|
|
113
|
+
injected_node_ids: []
|
|
114
|
+
};
|
|
115
|
+
const scorecard = buildSkipScorecard(disabledInput, sessionId, undefined, true);
|
|
116
|
+
const injectionEvent = {
|
|
117
|
+
injection_id: createId("decision"),
|
|
118
|
+
episode_id: resolveEpisodeId(session, sessionId, disabledInput),
|
|
119
|
+
session_id: sessionId,
|
|
120
|
+
scope_id: disabledInput.scope_id,
|
|
121
|
+
task_type: disabledInput.task_type === "unknown" ? "general" : disabledInput.task_type,
|
|
122
|
+
task_summary: disabledInput.task_summary,
|
|
123
|
+
mode: "skip",
|
|
124
|
+
delivery_mode: "live",
|
|
125
|
+
delivered: false,
|
|
126
|
+
injected_node_ids: [],
|
|
127
|
+
injection_count: 0,
|
|
128
|
+
scorecard,
|
|
129
|
+
was_successful: null,
|
|
130
|
+
harm_observed: null,
|
|
131
|
+
created_at: nowIso()
|
|
132
|
+
};
|
|
133
|
+
this.injectionRepo.upsert(injectionEvent);
|
|
134
|
+
session.lastInjectionEvent = injectionEvent;
|
|
135
135
|
return {
|
|
136
136
|
mode: "skip",
|
|
137
137
|
text: undefined,
|
|
138
138
|
notice: undefined,
|
|
139
|
+
scorecard,
|
|
139
140
|
retrievalContext,
|
|
140
|
-
input:
|
|
141
|
-
...input,
|
|
142
|
-
scope_id: existingScope.scope_id,
|
|
143
|
-
injected_node_ids: []
|
|
144
|
-
}
|
|
141
|
+
input: disabledInput
|
|
145
142
|
};
|
|
146
143
|
}
|
|
147
144
|
const stats = input.task_type !== "unknown" ? this.statsRepo.get(input.scope_id, input.task_type) : undefined;
|
|
@@ -168,9 +165,17 @@ export class ExperiencePromptRuntimeService {
|
|
|
168
165
|
};
|
|
169
166
|
const scorecard = decision.mode !== "skip"
|
|
170
167
|
? buildInjectionScorecard(input, decision.mode, decision.selected, sessionId, decision.diagnostics)
|
|
171
|
-
: decision.diagnostics
|
|
172
|
-
|
|
173
|
-
|
|
168
|
+
: buildSkipScorecard(input, sessionId, decision.diagnostics);
|
|
169
|
+
if (scorecard && decision.mode !== "skip" && !delivery.delivered) {
|
|
170
|
+
if (delivery.deliveryMode === "holdout") {
|
|
171
|
+
scorecard.skipReasonCode = "holdout_suppressed";
|
|
172
|
+
scorecard.skipReasonExplanation = "ExperienceEngine found a usable match but withheld it for holdout evaluation.";
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
scorecard.skipReasonCode = "shadow_suppressed";
|
|
176
|
+
scorecard.skipReasonExplanation = "ExperienceEngine found a usable match but shadow mode suppressed prompt delivery.";
|
|
177
|
+
}
|
|
178
|
+
}
|
|
174
179
|
const injectionEvent = {
|
|
175
180
|
injection_id: createId(decision.mode === "skip" ? "decision" : "inject"),
|
|
176
181
|
episode_id: episodeId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-service.js","sourceRoot":"","sources":["../../src/runtime/prompt-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAC/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAE,2BAA2B,EAAE,MAAM,yDAAyD,CAAC;AACtG,OAAO,EAAE,mBAAmB,EAAE,MAAM,gDAAgD,CAAC;AACrF,OAAO,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,kDAAkD,CAAC;AACxF,OAAO,EAAE,eAAe,EAAE,MAAM,4CAA4C,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,4CAA4C,CAAC;AAC7E,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"prompt-service.js","sourceRoot":"","sources":["../../src/runtime/prompt-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type HybridRouteDecision, type HybridRouteSignals } from "../hybrid/router.js";
|
|
2
|
-
import type { EvaluationMode, ExperienceInput,
|
|
2
|
+
import type { EvaluationMode, ExperienceInput, ToolEvent } from "../types/domain.js";
|
|
3
3
|
import type { ExperiencePlugin, HostPromptContext, HostToolResult, OpenClawLogger } from "../types/plugin.js";
|
|
4
4
|
import type { ExperienceEngineConfig } from "../config/config-schema.js";
|
|
5
5
|
import { RuntimeCaptureWriter } from "../plugin/runtime-capture.js";
|
|
@@ -35,6 +35,8 @@ export declare class ExperienceRuntimeService implements ExperiencePlugin {
|
|
|
35
35
|
private readonly repoPolicyRepo;
|
|
36
36
|
private readonly hybridReviewArtifactRepo;
|
|
37
37
|
private readonly hybridTraceRepo;
|
|
38
|
+
private readonly learningPipeline;
|
|
39
|
+
private readonly taskFinalization;
|
|
38
40
|
private readonly runtimeOptions;
|
|
39
41
|
private readonly backgroundLearningEnabled;
|
|
40
42
|
private readonly hybridPosttaskEnabled;
|
|
@@ -50,8 +52,6 @@ export declare class ExperienceRuntimeService implements ExperiencePlugin {
|
|
|
50
52
|
private resolveConservativeCrossScopeCandidates;
|
|
51
53
|
private resolveDiagnosticCandidates;
|
|
52
54
|
recoverToolEvents(sessionId: string, payload: unknown): void;
|
|
53
|
-
private buildFinalizedInput;
|
|
54
|
-
private persistFinalizedInput;
|
|
55
55
|
private trackLearningTask;
|
|
56
56
|
waitForBackgroundLearning(): Promise<void>;
|
|
57
57
|
private getLearningGate;
|
|
@@ -69,6 +69,7 @@ export declare class ExperienceRuntimeService implements ExperiencePlugin {
|
|
|
69
69
|
mode: "skip";
|
|
70
70
|
text: undefined;
|
|
71
71
|
notice: undefined;
|
|
72
|
+
scorecard: import("../types/domain.js").InjectionScorecard;
|
|
72
73
|
retrievalContext: import("../types/domain.js").RetrievalContext;
|
|
73
74
|
input: {
|
|
74
75
|
scope_id: string;
|
|
@@ -79,14 +80,13 @@ export declare class ExperienceRuntimeService implements ExperiencePlugin {
|
|
|
79
80
|
outcome_signal: import("../types/domain.js").OutcomeSignal;
|
|
80
81
|
context_summary?: string;
|
|
81
82
|
};
|
|
82
|
-
scorecard?: undefined;
|
|
83
83
|
deliveryMode?: undefined;
|
|
84
84
|
delivered?: undefined;
|
|
85
85
|
} | {
|
|
86
86
|
mode: import("../types/domain.js").InjectionMode;
|
|
87
87
|
text: string | undefined;
|
|
88
88
|
notice: string | undefined;
|
|
89
|
-
scorecard: InjectionScorecard | undefined;
|
|
89
|
+
scorecard: import("../types/domain.js").InjectionScorecard | undefined;
|
|
90
90
|
deliveryMode: EvaluationMode | undefined;
|
|
91
91
|
delivered: boolean | undefined;
|
|
92
92
|
retrievalContext: import("../types/domain.js").RetrievalContext;
|
package/dist/runtime/service.js
CHANGED
|
@@ -3,12 +3,12 @@ import { buildInjectionScorecard } from "../controller/injection-scorecard.js";
|
|
|
3
3
|
import { classifyFailureAttributionReason } from "../feedback/automatic-attribution.js";
|
|
4
4
|
import { applyFeedback } from "../feedback/feedback-manager.js";
|
|
5
5
|
import { detectHarm } from "../feedback/harm-detector.js";
|
|
6
|
-
import { createEmptyStats, updateStats } from "../feedback/stats-updater.js";
|
|
7
6
|
import { buildExperienceInput } from "../input/input-adapter.js";
|
|
8
7
|
import { buildRetrievalContext } from "../controller/retrieval-context.js";
|
|
9
8
|
import { resolveScope } from "../input/scope-resolver.js";
|
|
10
9
|
import { decideIntervention } from "../controller/intervention-controller.js";
|
|
11
10
|
import { renderInlineNotice } from "../controller/inline-notice.js";
|
|
11
|
+
import { buildSkipScorecard } from "../controller/skip-scorecard.js";
|
|
12
12
|
import { applyGovernedNodeFeedback, deriveNodeOriginProfileForNode } from "../experience-management/node-lifecycle-governance.js";
|
|
13
13
|
import { evaluateRepoPolicy } from "../experience-management/repo-policy.js";
|
|
14
14
|
import { resolveHybridRolloutState } from "../hybrid/rollout.js";
|
|
@@ -33,191 +33,17 @@ import { RuntimeCaptureWriter } from "../plugin/runtime-capture.js";
|
|
|
33
33
|
import { normalizeToolResult } from "../plugin/hooks/tool-result-persist.js";
|
|
34
34
|
import { extractToolResultsFromPayload } from "../plugin/runtime-helpers.js";
|
|
35
35
|
import { HybridReviewArtifactRepository } from "../store/sqlite/repositories/hybrid-review-artifact-repo.js";
|
|
36
|
+
import { LearningPipelineService } from "./learning-pipeline-service.js";
|
|
37
|
+
import { mergeContext, TaskFinalizationService } from "./task-finalization-service.js";
|
|
36
38
|
const loadLlmLearningGate = async () => import("../analyzer/llm-learning-gate.js");
|
|
37
39
|
const loadDistillationQueueWorker = async () => import("../distillation/queue-worker.js");
|
|
38
40
|
const loadHybridWorkerClientModule = async () => import("../hybrid/worker-client.js");
|
|
39
41
|
const loadHybridCapsuleBuilder = async () => import("../hybrid/capsule-builder.js");
|
|
40
42
|
const loadHybridPostmortemProviderClient = async () => import("../hybrid/postmortem-provider-client.js");
|
|
41
|
-
const toEvidence = (input) => input.tool_events.map((event) => [event.tool_name, event.status, event.error_signature ?? event.output_summary]
|
|
42
|
-
.filter(Boolean)
|
|
43
|
-
.join(": "));
|
|
44
|
-
const toInputRecord = (input, sessionId, episodeId) => ({
|
|
45
|
-
record_id: createId("input"),
|
|
46
|
-
episode_id: episodeId,
|
|
47
|
-
scope_id: input.scope_id,
|
|
48
|
-
session_id: sessionId,
|
|
49
|
-
task_type: input.task_type,
|
|
50
|
-
task_summary: input.task_summary,
|
|
51
|
-
outcome_signal: input.outcome_signal,
|
|
52
|
-
context_summary: input.context_summary,
|
|
53
|
-
evidence: toEvidence(input),
|
|
54
|
-
injected_node_ids: input.injected_node_ids,
|
|
55
|
-
created_at: nowIso()
|
|
56
|
-
});
|
|
57
|
-
const toTaskRun = (input, sessionId, context, episodeId) => {
|
|
58
|
-
const timestamp = nowIso();
|
|
59
|
-
const signals = buildCandidateSignals(input);
|
|
60
|
-
return {
|
|
61
|
-
id: stableId("taskrun", `${sessionId}:${input.task_summary}:${timestamp}`),
|
|
62
|
-
episode_id: episodeId,
|
|
63
|
-
host: context.host ?? "openclaw",
|
|
64
|
-
scope_id: input.scope_id,
|
|
65
|
-
session_id: sessionId,
|
|
66
|
-
task_type: input.task_type,
|
|
67
|
-
task_summary: input.task_summary,
|
|
68
|
-
prompt_excerpt: context.userMessage,
|
|
69
|
-
context_summary: input.context_summary,
|
|
70
|
-
started_at: timestamp,
|
|
71
|
-
ended_at: timestamp,
|
|
72
|
-
final_status: input.outcome_signal === "success" ? "success" : input.outcome_signal === "failure" ? "failure" : "unknown",
|
|
73
|
-
failure_signature: signals.failure_signature,
|
|
74
|
-
learning_status: undefined,
|
|
75
|
-
learning_reason: undefined,
|
|
76
|
-
created_at: timestamp,
|
|
77
|
-
updated_at: timestamp
|
|
78
|
-
};
|
|
79
|
-
};
|
|
80
|
-
const toOutcomeRecord = (taskRun, input, episodeId) => ({
|
|
81
|
-
id: createId("outcome"),
|
|
82
|
-
episode_id: episodeId,
|
|
83
|
-
task_run_id: taskRun.id,
|
|
84
|
-
outcome_signal: input.outcome_signal,
|
|
85
|
-
failure_signature: taskRun.failure_signature,
|
|
86
|
-
summary: input.task_summary,
|
|
87
|
-
created_at: nowIso()
|
|
88
|
-
});
|
|
89
|
-
const buildCandidateSourceSignal = (input) => {
|
|
90
|
-
const signals = buildCandidateSignals(input);
|
|
91
|
-
return {
|
|
92
|
-
task_summary: input.task_summary,
|
|
93
|
-
context_summary: input.context_summary,
|
|
94
|
-
outcome_signal: input.outcome_signal,
|
|
95
|
-
tool_events: input.tool_events,
|
|
96
|
-
evidence: toEvidence(input),
|
|
97
|
-
failure_signature: signals.failure_signature,
|
|
98
|
-
retry_count: signals.retry_count,
|
|
99
|
-
correction_signals: signals.correction_signals,
|
|
100
|
-
directional_correction: signals.directional_correction,
|
|
101
|
-
evidence_driven_reversal: signals.evidence_driven_reversal,
|
|
102
|
-
tool_event_summary: signals.tool_event_summary
|
|
103
|
-
};
|
|
104
|
-
};
|
|
105
43
|
const resolveEpisodeId = (session, sessionId, input) => {
|
|
106
44
|
session.episodeId ??= stableId("episode", `${sessionId}:${input.scope_id}:${input.task_summary}`);
|
|
107
45
|
return session.episodeId;
|
|
108
46
|
};
|
|
109
|
-
const mergeDirectionalCorrectionIntoSourceSignal = (sourceSignal, draft) => {
|
|
110
|
-
const directionalCorrection = sourceSignal.directional_correction;
|
|
111
|
-
if (!directionalCorrection) {
|
|
112
|
-
return sourceSignal;
|
|
113
|
-
}
|
|
114
|
-
const semanticDetected = Boolean(draft.experience_kind === "expectation_correction" &&
|
|
115
|
-
draft.correction_category &&
|
|
116
|
-
draft.deviation_pattern &&
|
|
117
|
-
draft.corrected_constraint);
|
|
118
|
-
if (!semanticDetected) {
|
|
119
|
-
return sourceSignal;
|
|
120
|
-
}
|
|
121
|
-
return {
|
|
122
|
-
...sourceSignal,
|
|
123
|
-
directional_correction: {
|
|
124
|
-
...directionalCorrection,
|
|
125
|
-
semantic_detected: true,
|
|
126
|
-
correction_category: draft.correction_category,
|
|
127
|
-
deviation_pattern: draft.deviation_pattern,
|
|
128
|
-
corrected_constraint: draft.corrected_constraint
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
};
|
|
132
|
-
const mergeEvidenceDrivenReversalIntoSourceSignal = (sourceSignal, draft) => {
|
|
133
|
-
const reversal = sourceSignal.evidence_driven_reversal;
|
|
134
|
-
if (!reversal) {
|
|
135
|
-
return sourceSignal;
|
|
136
|
-
}
|
|
137
|
-
const semanticDetected = Boolean(draft.experience_kind === "expectation_correction" &&
|
|
138
|
-
draft.correction_category &&
|
|
139
|
-
draft.deviation_pattern &&
|
|
140
|
-
draft.corrected_constraint);
|
|
141
|
-
if (!semanticDetected) {
|
|
142
|
-
return sourceSignal;
|
|
143
|
-
}
|
|
144
|
-
return {
|
|
145
|
-
...sourceSignal,
|
|
146
|
-
evidence_driven_reversal: {
|
|
147
|
-
...reversal,
|
|
148
|
-
semantic_detected: true,
|
|
149
|
-
correction_category: draft.correction_category,
|
|
150
|
-
deviation_pattern: draft.deviation_pattern,
|
|
151
|
-
corrected_constraint: draft.corrected_constraint
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
};
|
|
155
|
-
const summarizeRawCandidate = (sourceSignal) => {
|
|
156
|
-
const fragments = [...sourceSignal.tool_event_summary];
|
|
157
|
-
if (sourceSignal.failure_signature) {
|
|
158
|
-
fragments.unshift(`failure signature: ${sourceSignal.failure_signature}`);
|
|
159
|
-
}
|
|
160
|
-
return fragments.slice(0, 3).join(" | ");
|
|
161
|
-
};
|
|
162
|
-
const resolveCandidateKind = (input, sourceSignal) => {
|
|
163
|
-
if (input.outcome_signal === "success") {
|
|
164
|
-
return "successful_fix";
|
|
165
|
-
}
|
|
166
|
-
if (sourceSignal.retry_count > 1) {
|
|
167
|
-
return "retry_pattern";
|
|
168
|
-
}
|
|
169
|
-
if (sourceSignal.correction_signals.length > 0) {
|
|
170
|
-
return "correction";
|
|
171
|
-
}
|
|
172
|
-
return "failure";
|
|
173
|
-
};
|
|
174
|
-
const draftToCandidate = (draft, input, originRecordId, taskRunId, directionalCorrectionSignal, evidenceDrivenReversalSignal) => {
|
|
175
|
-
const timestamp = nowIso();
|
|
176
|
-
const baseSourceSignal = buildCandidateSourceSignal(input);
|
|
177
|
-
const sourceSignal = mergeEvidenceDrivenReversalIntoSourceSignal(mergeDirectionalCorrectionIntoSourceSignal({
|
|
178
|
-
...baseSourceSignal,
|
|
179
|
-
directional_correction: directionalCorrectionSignal ?? baseSourceSignal.directional_correction,
|
|
180
|
-
evidence_driven_reversal: evidenceDrivenReversalSignal ?? baseSourceSignal.evidence_driven_reversal
|
|
181
|
-
}, draft), draft);
|
|
182
|
-
const candidateId = stableId("candidate", [draft.scope_id, draft.task_type, draft.node_type, draft.compact_hint, originRecordId].join(":"));
|
|
183
|
-
return {
|
|
184
|
-
id: candidateId,
|
|
185
|
-
task_run_id: taskRunId ?? originRecordId,
|
|
186
|
-
candidate_kind: resolveCandidateKind(input, sourceSignal),
|
|
187
|
-
...draft,
|
|
188
|
-
source_record_id: originRecordId,
|
|
189
|
-
source_context_summary: input.context_summary,
|
|
190
|
-
source_outcome_signal: input.outcome_signal,
|
|
191
|
-
raw_summary: summarizeRawCandidate(sourceSignal),
|
|
192
|
-
failure_signature: sourceSignal.failure_signature,
|
|
193
|
-
source_signal: sourceSignal,
|
|
194
|
-
lifecycle_state: "pending",
|
|
195
|
-
retry_count: 0,
|
|
196
|
-
created_at: timestamp,
|
|
197
|
-
updated_at: timestamp
|
|
198
|
-
};
|
|
199
|
-
};
|
|
200
|
-
const candidateToInitialJob = (candidate, extractorProfile) => {
|
|
201
|
-
const timestamp = nowIso();
|
|
202
|
-
return {
|
|
203
|
-
id: stableId("distill", candidate.id),
|
|
204
|
-
candidate_id: candidate.id,
|
|
205
|
-
status: "pending",
|
|
206
|
-
extractor_profile: extractorProfile,
|
|
207
|
-
retry_count: candidate.retry_count,
|
|
208
|
-
created_at: timestamp,
|
|
209
|
-
updated_at: timestamp
|
|
210
|
-
};
|
|
211
|
-
};
|
|
212
|
-
const mergeContext = (existing, incoming) => ({
|
|
213
|
-
host: incoming.host ?? existing?.host,
|
|
214
|
-
sessionId: incoming.sessionId ?? existing?.sessionId,
|
|
215
|
-
cwd: incoming.cwd ?? existing?.cwd,
|
|
216
|
-
userMessage: incoming.userMessage || existing?.userMessage || "",
|
|
217
|
-
taskSummary: incoming.taskSummary ?? existing?.taskSummary,
|
|
218
|
-
contextSummary: incoming.contextSummary ?? existing?.contextSummary,
|
|
219
|
-
injectedNodeIds: incoming.injectedNodeIds ?? existing?.injectedNodeIds
|
|
220
|
-
});
|
|
221
47
|
const buildToolEventKey = (toolEvent, toolCallId) => toolCallId ??
|
|
222
48
|
[
|
|
223
49
|
toolEvent.tool_name,
|
|
@@ -259,31 +85,6 @@ const resolveDeliveryMode = (evaluationMode, holdoutRate, sessionId, taskSummary
|
|
|
259
85
|
delivered: true
|
|
260
86
|
};
|
|
261
87
|
};
|
|
262
|
-
const buildRecordOnlyDiagnosticScorecard = (input, sessionId, diagnostics) => ({
|
|
263
|
-
sessionId,
|
|
264
|
-
scopeId: input.scope_id,
|
|
265
|
-
taskType: input.task_type === "unknown" ? "general" : input.task_type,
|
|
266
|
-
taskSummary: input.task_summary,
|
|
267
|
-
mode: "skip",
|
|
268
|
-
interventionStrength: "diagnostic_hint",
|
|
269
|
-
riskLevel: "high",
|
|
270
|
-
recommendation: "Record-only diagnostic candidate matched; keep it out of prompt text until the live gate clears.",
|
|
271
|
-
reasons: ["A same-scope shadow candidate matched this task but was not delivered."],
|
|
272
|
-
topCandidates: diagnostics.topCandidates,
|
|
273
|
-
topCandidateScore: diagnostics.topCandidateScore,
|
|
274
|
-
scoreMargin: diagnostics.scoreMargin,
|
|
275
|
-
fastPathApplied: diagnostics.fastPathApplied,
|
|
276
|
-
queryRewriteApplied: diagnostics.queryRewriteApplied,
|
|
277
|
-
gateReason: diagnostics.gateReason,
|
|
278
|
-
decisionReason: diagnostics.decisionReason,
|
|
279
|
-
confidence: diagnostics.confidence,
|
|
280
|
-
budgetClass: diagnostics.budgetClass,
|
|
281
|
-
selectedCandidateIds: [],
|
|
282
|
-
recordOnlyDiagnosticCandidateIds: diagnostics.recordOnlyDiagnosticCandidateIds,
|
|
283
|
-
rejectedCandidates: diagnostics.rejectedCandidates,
|
|
284
|
-
nodes: [],
|
|
285
|
-
createdAt: nowIso()
|
|
286
|
-
});
|
|
287
88
|
const HYBRID_LIGHTWEIGHT_PATTERN = /\b(wording-only|wording only|copy-only|copy only|copy pass|inline notice wording|expression-layer refinement)\b/i;
|
|
288
89
|
const isLightweightHybridExcludedTask = (input) => HYBRID_LIGHTWEIGHT_PATTERN.test(`${input.task_summary} ${input.context_summary ?? ""}`);
|
|
289
90
|
export const decidePosttaskHybridRoute = (config, input, signals, rolloutKey = input.task_summary) => {
|
|
@@ -321,6 +122,8 @@ export class ExperienceRuntimeService {
|
|
|
321
122
|
repoPolicyRepo;
|
|
322
123
|
hybridReviewArtifactRepo;
|
|
323
124
|
hybridTraceRepo;
|
|
125
|
+
learningPipeline;
|
|
126
|
+
taskFinalization;
|
|
324
127
|
runtimeOptions;
|
|
325
128
|
backgroundLearningEnabled;
|
|
326
129
|
hybridPosttaskEnabled;
|
|
@@ -351,6 +154,23 @@ export class ExperienceRuntimeService {
|
|
|
351
154
|
this.repoPolicyRepo = new RepoPolicyRepository(this.db);
|
|
352
155
|
this.hybridReviewArtifactRepo = new HybridReviewArtifactRepository(this.db);
|
|
353
156
|
this.hybridTraceRepo = new HybridInvocationTraceRepository(this.db);
|
|
157
|
+
this.learningPipeline = new LearningPipelineService({
|
|
158
|
+
config: this.config,
|
|
159
|
+
db: this.db,
|
|
160
|
+
candidateRepo: this.candidateRepo,
|
|
161
|
+
jobRepo: this.jobRepo,
|
|
162
|
+
taskRunRepo: this.taskRunRepo,
|
|
163
|
+
logger: this.logger,
|
|
164
|
+
getLearningGate: () => this.getLearningGate(),
|
|
165
|
+
getDistillationWorker: () => this.getDistillationWorker()
|
|
166
|
+
});
|
|
167
|
+
this.taskFinalization = new TaskFinalizationService({
|
|
168
|
+
scopeRepo: this.scopeRepo,
|
|
169
|
+
inputRepo: this.inputRepo,
|
|
170
|
+
taskRunRepo: this.taskRunRepo,
|
|
171
|
+
outcomeRepo: this.outcomeRepo,
|
|
172
|
+
statsRepo: this.statsRepo
|
|
173
|
+
});
|
|
354
174
|
this.captureWriter = new RuntimeCaptureWriter(config, this.logger);
|
|
355
175
|
}
|
|
356
176
|
getSession(sessionId) {
|
|
@@ -397,30 +217,6 @@ export class ExperienceRuntimeService {
|
|
|
397
217
|
}
|
|
398
218
|
}
|
|
399
219
|
}
|
|
400
|
-
buildFinalizedInput(context, session) {
|
|
401
|
-
const mergedContext = mergeContext(session.context, context);
|
|
402
|
-
const injectedNodeIds = session.injectedNodeIds.length > 0 ? session.injectedNodeIds : mergedContext.injectedNodeIds ?? [];
|
|
403
|
-
const input = buildExperienceInput({
|
|
404
|
-
...mergedContext,
|
|
405
|
-
injectedNodeIds
|
|
406
|
-
}, session.toolEvents);
|
|
407
|
-
return input;
|
|
408
|
-
}
|
|
409
|
-
persistFinalizedInput(input, sessionId, session, episodeId, cwd) {
|
|
410
|
-
const resolvedScope = resolveScope(cwd ?? session.context?.cwd);
|
|
411
|
-
const existingScope = this.scopeRepo.getById(resolvedScope.scope_id);
|
|
412
|
-
this.scopeRepo.upsert({
|
|
413
|
-
...resolvedScope,
|
|
414
|
-
is_disabled: existingScope?.is_disabled ?? resolvedScope.is_disabled
|
|
415
|
-
});
|
|
416
|
-
const record = toInputRecord(input, sessionId, episodeId);
|
|
417
|
-
this.inputRepo.upsert(record);
|
|
418
|
-
if (input.task_type !== "unknown") {
|
|
419
|
-
const currentStats = this.statsRepo.get(input.scope_id, input.task_type) ?? createEmptyStats(input.scope_id, input.task_type);
|
|
420
|
-
this.statsRepo.upsert(updateStats(currentStats, input.outcome_signal, input.injected_node_ids.length > 0));
|
|
421
|
-
}
|
|
422
|
-
return record;
|
|
423
|
-
}
|
|
424
220
|
trackLearningTask(task) {
|
|
425
221
|
const tracked = task
|
|
426
222
|
.catch((error) => {
|
|
@@ -661,72 +457,7 @@ export class ExperienceRuntimeService {
|
|
|
661
457
|
}
|
|
662
458
|
}
|
|
663
459
|
async persistCandidatesAsync(input, originRecordId, taskRunId, sessionId) {
|
|
664
|
-
|
|
665
|
-
if (!learningGate) {
|
|
666
|
-
if (taskRunId) {
|
|
667
|
-
const taskRun = this.taskRunRepo.getById(taskRunId);
|
|
668
|
-
if (taskRun) {
|
|
669
|
-
this.taskRunRepo.upsert({
|
|
670
|
-
...taskRun,
|
|
671
|
-
learning_status: "not_applicable",
|
|
672
|
-
learning_reason: "background learning disabled",
|
|
673
|
-
updated_at: nowIso()
|
|
674
|
-
});
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
return;
|
|
678
|
-
}
|
|
679
|
-
const result = await learningGate.generateCandidateDrafts(input);
|
|
680
|
-
if (taskRunId) {
|
|
681
|
-
const taskRun = this.taskRunRepo.getById(taskRunId);
|
|
682
|
-
if (taskRun) {
|
|
683
|
-
this.taskRunRepo.upsert({
|
|
684
|
-
...taskRun,
|
|
685
|
-
learning_status: result.drafts.length
|
|
686
|
-
? "captured"
|
|
687
|
-
: result.source === "disabled" && result.reason === "distillation disabled"
|
|
688
|
-
? "not_applicable"
|
|
689
|
-
: "rejected",
|
|
690
|
-
learning_reason: result.reason,
|
|
691
|
-
updated_at: nowIso()
|
|
692
|
-
});
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
if (!result.drafts.length) {
|
|
696
|
-
this.logger.debug?.("experienceengine.learning_skipped", {
|
|
697
|
-
sessionId,
|
|
698
|
-
taskType: input.task_type,
|
|
699
|
-
reason: result.reason,
|
|
700
|
-
source: result.source
|
|
701
|
-
});
|
|
702
|
-
return;
|
|
703
|
-
}
|
|
704
|
-
const persistedCandidates = result.drafts.map((draft) => draftToCandidate(draft, input, originRecordId, taskRunId, result.directionalCorrectionSignal, result.evidenceDrivenReversalSignal));
|
|
705
|
-
withTransaction(this.db, () => {
|
|
706
|
-
for (const candidate of persistedCandidates) {
|
|
707
|
-
this.candidateRepo.upsert(candidate);
|
|
708
|
-
this.jobRepo.upsert(candidateToInitialJob(candidate, this.config.distillerProfile));
|
|
709
|
-
}
|
|
710
|
-
});
|
|
711
|
-
this.logger.info?.("experienceengine.learning_captured", {
|
|
712
|
-
sessionId,
|
|
713
|
-
taskType: input.task_type,
|
|
714
|
-
candidateCount: persistedCandidates.length,
|
|
715
|
-
source: result.source,
|
|
716
|
-
reason: result.reason
|
|
717
|
-
});
|
|
718
|
-
if (this.config.distillationAutoDrain) {
|
|
719
|
-
const distillationWorker = await this.getDistillationWorker();
|
|
720
|
-
if (!distillationWorker) {
|
|
721
|
-
return;
|
|
722
|
-
}
|
|
723
|
-
await distillationWorker.drain().catch((error) => {
|
|
724
|
-
this.logger.error?.("experienceengine.distillation_drain_failed", {
|
|
725
|
-
sessionId,
|
|
726
|
-
error: error instanceof Error ? error.message : String(error)
|
|
727
|
-
});
|
|
728
|
-
});
|
|
729
|
-
}
|
|
460
|
+
await this.learningPipeline.persistCandidatesAsync(input, originRecordId, taskRunId, sessionId);
|
|
730
461
|
}
|
|
731
462
|
updateInjectedNodes(input, attributionRecordId, taskRunId, injectionEvent, episodeId) {
|
|
732
463
|
if (!input.injected_node_ids.length) {
|
|
@@ -894,11 +625,35 @@ export class ExperienceRuntimeService {
|
|
|
894
625
|
const existingScope = this.scopeRepo.getById(resolvedScope.scope_id);
|
|
895
626
|
if (existingScope?.is_disabled) {
|
|
896
627
|
session.injectedNodeIds = [];
|
|
897
|
-
session.lastInjectionEvent = undefined;
|
|
898
628
|
session.context = {
|
|
899
629
|
...session.context,
|
|
900
630
|
injectedNodeIds: []
|
|
901
631
|
};
|
|
632
|
+
const disabledInput = {
|
|
633
|
+
...input,
|
|
634
|
+
scope_id: existingScope.scope_id,
|
|
635
|
+
injected_node_ids: []
|
|
636
|
+
};
|
|
637
|
+
const scorecard = buildSkipScorecard(disabledInput, sessionId, undefined, true);
|
|
638
|
+
const injectionEvent = {
|
|
639
|
+
injection_id: createId("decision"),
|
|
640
|
+
episode_id: resolveEpisodeId(session, sessionId, disabledInput),
|
|
641
|
+
session_id: sessionId,
|
|
642
|
+
scope_id: disabledInput.scope_id,
|
|
643
|
+
task_type: disabledInput.task_type === "unknown" ? "general" : disabledInput.task_type,
|
|
644
|
+
task_summary: disabledInput.task_summary,
|
|
645
|
+
mode: "skip",
|
|
646
|
+
delivery_mode: "live",
|
|
647
|
+
delivered: false,
|
|
648
|
+
injected_node_ids: [],
|
|
649
|
+
injection_count: 0,
|
|
650
|
+
scorecard,
|
|
651
|
+
was_successful: null,
|
|
652
|
+
harm_observed: null,
|
|
653
|
+
created_at: nowIso()
|
|
654
|
+
};
|
|
655
|
+
this.injectionRepo.upsert(injectionEvent);
|
|
656
|
+
session.lastInjectionEvent = injectionEvent;
|
|
902
657
|
this.logger.info?.("experienceengine.scope_disabled", {
|
|
903
658
|
sessionId,
|
|
904
659
|
scopeId: existingScope.scope_id
|
|
@@ -907,12 +662,9 @@ export class ExperienceRuntimeService {
|
|
|
907
662
|
mode: "skip",
|
|
908
663
|
text: undefined,
|
|
909
664
|
notice: undefined,
|
|
665
|
+
scorecard,
|
|
910
666
|
retrievalContext,
|
|
911
|
-
input:
|
|
912
|
-
...input,
|
|
913
|
-
scope_id: existingScope.scope_id,
|
|
914
|
-
injected_node_ids: []
|
|
915
|
-
}
|
|
667
|
+
input: disabledInput
|
|
916
668
|
};
|
|
917
669
|
}
|
|
918
670
|
const stats = input.task_type !== "unknown" ? this.statsRepo.get(input.scope_id, input.task_type) : undefined;
|
|
@@ -945,9 +697,17 @@ export class ExperienceRuntimeService {
|
|
|
945
697
|
};
|
|
946
698
|
const scorecard = decision.mode !== "skip"
|
|
947
699
|
? buildInjectionScorecard(input, decision.mode, decision.selected, sessionId, decision.diagnostics)
|
|
948
|
-
: decision.diagnostics
|
|
949
|
-
|
|
950
|
-
|
|
700
|
+
: buildSkipScorecard(input, sessionId, decision.diagnostics);
|
|
701
|
+
if (scorecard && decision.mode !== "skip" && !delivery.delivered) {
|
|
702
|
+
if (delivery.deliveryMode === "holdout") {
|
|
703
|
+
scorecard.skipReasonCode = "holdout_suppressed";
|
|
704
|
+
scorecard.skipReasonExplanation = "ExperienceEngine found a usable match but withheld it for holdout evaluation.";
|
|
705
|
+
}
|
|
706
|
+
else {
|
|
707
|
+
scorecard.skipReasonCode = "shadow_suppressed";
|
|
708
|
+
scorecard.skipReasonExplanation = "ExperienceEngine found a usable match but shadow mode suppressed prompt delivery.";
|
|
709
|
+
}
|
|
710
|
+
}
|
|
951
711
|
const injectionEvent = {
|
|
952
712
|
injection_id: createId(decision.mode === "skip" ? "decision" : "inject"),
|
|
953
713
|
episode_id: episodeId,
|
|
@@ -1009,14 +769,18 @@ export class ExperienceRuntimeService {
|
|
|
1009
769
|
async finalizeTask(context) {
|
|
1010
770
|
const sessionId = context.sessionId ?? "global";
|
|
1011
771
|
const session = this.getSession(sessionId);
|
|
1012
|
-
const input = this.buildFinalizedInput(context, session);
|
|
772
|
+
const input = this.taskFinalization.buildFinalizedInput(context, session);
|
|
1013
773
|
let learningTaskContext;
|
|
1014
774
|
withTransaction(this.db, () => {
|
|
1015
775
|
const episodeId = resolveEpisodeId(session, sessionId, input);
|
|
1016
|
-
const record = this.
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
776
|
+
const { record, taskRun } = this.taskFinalization.persistFinalizedRun({
|
|
777
|
+
experienceInput: input,
|
|
778
|
+
sessionId,
|
|
779
|
+
session,
|
|
780
|
+
episodeId,
|
|
781
|
+
context,
|
|
782
|
+
cwd: context.cwd
|
|
783
|
+
});
|
|
1020
784
|
const injectionEvent = session.lastInjectionEvent ?? this.injectionRepo.getLatestBySessionId(sessionId);
|
|
1021
785
|
this.updateInjectedNodes(input, record.record_id, taskRun.id, injectionEvent, episodeId);
|
|
1022
786
|
if (injectionEvent) {
|