@evomap/evolver-core 2.0.0-beta.13 → 2.0.0-beta.15
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/dist/algo/index.d.ts +1 -0
- package/dist/algo/index.js +1 -0
- package/dist/algo/publishEligibility.d.ts +34 -0
- package/dist/algo/publishEligibility.js +52 -0
- package/dist/events/paths.d.ts +3 -1
- package/dist/events/paths.js +4 -0
- package/dist/events/public.d.ts +1 -1
- package/dist/events/public.js +1 -1
- package/dist/exec/autoExec.d.ts +15 -0
- package/dist/exec/autoExec.js +34 -0
- package/dist/exec/autonomousCycle.d.ts +3 -0
- package/dist/exec/autonomousCycle.js +1 -0
- package/dist/exec/claudeBridge.d.ts +7 -0
- package/dist/exec/claudeBridge.js +17 -0
- package/dist/hub/capability.d.ts +79 -2
- package/dist/trace/index.d.ts +2 -1
- package/dist/trace/index.js +2 -1
- package/dist/trace/learningTrace.d.ts +194 -0
- package/dist/trace/learningTrace.js +274 -0
- package/dist/verify/sandboxRunner.d.ts +28 -0
- package/dist/verify/sandboxRunner.js +218 -19
- package/dist/verify/sandboxedValidation.d.ts +9 -0
- package/dist/verify/sandboxedValidation.js +113 -13
- package/package.json +1 -1
package/dist/algo/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * from './conversationSniffer.js';
|
|
|
8
8
|
export * from './geneIntake.js';
|
|
9
9
|
export * from './candidateAssembly.js';
|
|
10
10
|
export * from './genePromotion.js';
|
|
11
|
+
export * from './publishEligibility.js';
|
|
11
12
|
export * from './bans.js';
|
|
12
13
|
export * from './antiDistill.js';
|
|
13
14
|
export * from './orchestrator.js';
|
package/dist/algo/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export * from './conversationSniffer.js';
|
|
|
8
8
|
export * from './geneIntake.js';
|
|
9
9
|
export * from './candidateAssembly.js';
|
|
10
10
|
export * from './genePromotion.js';
|
|
11
|
+
export * from './publishEligibility.js';
|
|
11
12
|
export * from './bans.js';
|
|
12
13
|
export * from './antiDistill.js';
|
|
13
14
|
export * from './orchestrator.js';
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type GeneLearningView } from '../assetstore/learningHistory.js';
|
|
2
|
+
import type { AssetStoreProvider } from '../assetstore/provider.js';
|
|
3
|
+
export type PublishEligibilityReason = 'eligible' | 'no_proven_success';
|
|
4
|
+
export interface GenePublishEvidence {
|
|
5
|
+
geneId: string;
|
|
6
|
+
/** Real, value-producing successes (inert zero-work successes excluded upstream). */
|
|
7
|
+
success: number;
|
|
8
|
+
failed: number;
|
|
9
|
+
inert: number;
|
|
10
|
+
total: number;
|
|
11
|
+
eligible: boolean;
|
|
12
|
+
reason: PublishEligibilityReason;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The single publish/upload eligibility predicate: a gene may be published iff
|
|
16
|
+
* it has at least one capsule with a `success` outcome.
|
|
17
|
+
*
|
|
18
|
+
* We count `success + inert`, NOT just `success`. aggregateLearningHistory
|
|
19
|
+
* demotes a `success` capsule that carries no `proof_of_work` to `inert`
|
|
20
|
+
* (#195), but the evox capsule builder (asset_builder.rs) does NOT emit
|
|
21
|
+
* `proof_of_work` — a genuine successful run surfaces there as `inert`. Gating
|
|
22
|
+
* publish on the proof-only `success` count would therefore block genes that
|
|
23
|
+
* actually succeeded in production. "Has a success outcome" is the honest bar
|
|
24
|
+
* for the publish gate; the proof/inert distinction stays where it belongs
|
|
25
|
+
* (success-rate, auto-promote), not here. A gene with only failures — or one
|
|
26
|
+
* never run at all — has `success + inert === 0` and is not publishable.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isGenePublishEligible(view: Pick<GeneLearningView, 'success' | 'inert'>): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Read-only: derive a gene's outcome evidence and whether it clears the publish
|
|
31
|
+
* bar. Returns `no_proven_success` for a gene the store has never seen succeed,
|
|
32
|
+
* so callers can surface a precise, self-serviceable reason.
|
|
33
|
+
*/
|
|
34
|
+
export declare function assessGenePublishEvidence(store: AssetStoreProvider, geneId: string): Promise<GenePublishEvidence>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Proven-success gate for publishing / uploading a gene (#581).
|
|
2
|
+
//
|
|
3
|
+
// A gene's `confidence` is minted from model self-report (solidify), text
|
|
4
|
+
// completeness (conversationDistiller), or is absent (like) — none of which
|
|
5
|
+
// prove the gene ever WORKED. Publishing such a gene to the market pairs it
|
|
6
|
+
// with a capsule and lets the Hub quality gate reject it after a round-trip
|
|
7
|
+
// ("never truly succeeded"). This predicate is the single, outcome-driven bar
|
|
8
|
+
// every publish/upload surface routes through, so an unproven gene is stopped
|
|
9
|
+
// locally with a clear reason instead of leaking to the Hub.
|
|
10
|
+
//
|
|
11
|
+
// Deliberately DISTINCT from `probationWouldPromote` (genePromotion.ts):
|
|
12
|
+
// auto-promotion runs with NO human in the loop, so it demands a strong, clean
|
|
13
|
+
// record (>= minSuccess successes AND zero failures). Publishing is human-
|
|
14
|
+
// initiated, so the bar is only "has this gene truly succeeded at least once"
|
|
15
|
+
// — a gene with many successes and one stray failure is still worth publishing,
|
|
16
|
+
// and blocking it on `failed === 0` would be wrong.
|
|
17
|
+
import { aggregateLearningHistory } from '../assetstore/learningHistory.js';
|
|
18
|
+
/**
|
|
19
|
+
* The single publish/upload eligibility predicate: a gene may be published iff
|
|
20
|
+
* it has at least one capsule with a `success` outcome.
|
|
21
|
+
*
|
|
22
|
+
* We count `success + inert`, NOT just `success`. aggregateLearningHistory
|
|
23
|
+
* demotes a `success` capsule that carries no `proof_of_work` to `inert`
|
|
24
|
+
* (#195), but the evox capsule builder (asset_builder.rs) does NOT emit
|
|
25
|
+
* `proof_of_work` — a genuine successful run surfaces there as `inert`. Gating
|
|
26
|
+
* publish on the proof-only `success` count would therefore block genes that
|
|
27
|
+
* actually succeeded in production. "Has a success outcome" is the honest bar
|
|
28
|
+
* for the publish gate; the proof/inert distinction stays where it belongs
|
|
29
|
+
* (success-rate, auto-promote), not here. A gene with only failures — or one
|
|
30
|
+
* never run at all — has `success + inert === 0` and is not publishable.
|
|
31
|
+
*/
|
|
32
|
+
export function isGenePublishEligible(view) {
|
|
33
|
+
return view.success + (view.inert ?? 0) >= 1;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Read-only: derive a gene's outcome evidence and whether it clears the publish
|
|
37
|
+
* bar. Returns `no_proven_success` for a gene the store has never seen succeed,
|
|
38
|
+
* so callers can surface a precise, self-serviceable reason.
|
|
39
|
+
*/
|
|
40
|
+
export async function assessGenePublishEvidence(store, geneId) {
|
|
41
|
+
const view = await aggregateLearningHistory(store, geneId);
|
|
42
|
+
const eligible = isGenePublishEligible(view);
|
|
43
|
+
return {
|
|
44
|
+
geneId,
|
|
45
|
+
success: view.success,
|
|
46
|
+
failed: view.failed,
|
|
47
|
+
inert: view.inert ?? 0,
|
|
48
|
+
total: view.total,
|
|
49
|
+
eligible,
|
|
50
|
+
reason: eligible ? 'eligible' : 'no_proven_success',
|
|
51
|
+
};
|
|
52
|
+
}
|
package/dist/events/paths.d.ts
CHANGED
|
@@ -19,4 +19,6 @@ export declare function tracesDir(env?: Readonly<Record<string, string | undefin
|
|
|
19
19
|
* reads it. Defaults to the evomap home root, beside assets/ + evolution/.
|
|
20
20
|
* NOTE(reuse/#234): confirm this matches where the proxy/RecallHistory reads — best-effort append, so a path
|
|
21
21
|
* mismatch degrades the audit line, never the reuse write itself. */
|
|
22
|
-
export declare function assetCallLogPath(env?: Readonly<Record<string, string | undefined>>): string;
|
|
22
|
+
export declare function assetCallLogPath(env?: Readonly<Record<string, string | undefined>>): string;
|
|
23
|
+
/** Learning Ops local output dir: per-run LearningPacket drafts + trace-event JSONL (hub upload is a later slice). */
|
|
24
|
+
export declare function learningTraceDir(env?: Readonly<Record<string, string | undefined>>): string;
|
package/dist/events/paths.js
CHANGED
|
@@ -41,4 +41,8 @@ export function tracesDir(env = process.env) {
|
|
|
41
41
|
* mismatch degrades the audit line, never the reuse write itself. */
|
|
42
42
|
export function assetCallLogPath(env = process.env) {
|
|
43
43
|
return join(evomapHome(env), 'asset_call_log.jsonl');
|
|
44
|
+
}
|
|
45
|
+
/** Learning Ops local output dir: per-run LearningPacket drafts + trace-event JSONL (hub upload is a later slice). */
|
|
46
|
+
export function learningTraceDir(env = process.env) {
|
|
47
|
+
return env['EVOLVER_LEARNING_TRACE_DIR'] ?? join(evomapHome(env), 'evolution', 'learning-trace');
|
|
44
48
|
}
|
package/dist/events/public.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export type { EventCountsMV } from './projectors.js';
|
|
|
7
7
|
export { LineTooLargeError, MAX_LINE_BYTES } from './eventStore.js';
|
|
8
8
|
export { ArchiveSegmentConflictError, InvalidRootEventArchiveError, InvalidRootEventLogError, RootEventHistoryGapError, archiveRootEvents, inspectRootEventArchive, planRootEventArchive, readRootEventHistory, rootEventArchiveDir, rootEventArchiveSegmentName, validateRootEventHistory, ROOT_EVENT_ARCHIVE_DEFAULT_KEEP_EVENTS, } from './eventArchive.js';
|
|
9
9
|
export type { RootEventArchiveOptions, RootEventArchivePlan, RootEventArchiveResult, RootEventArchiveStats, } from './eventArchive.js';
|
|
10
|
-
export { rootEventsPath, evomapHome, mvDir, personalityStatePath, assetsDir, materialDir, materialStorePath, materialWatermarkPath, tracesDir, assetCallLogPath } from './paths.js';
|
|
10
|
+
export { rootEventsPath, evomapHome, mvDir, personalityStatePath, assetsDir, materialDir, materialStorePath, materialWatermarkPath, tracesDir, assetCallLogPath, learningTraceDir } from './paths.js';
|
|
11
11
|
export { EVENT_SCHEMA_VERSION } from './eventSchema.js';
|
|
12
12
|
export type { RootEvent, RawEvent, HumanNarrative, Actor, Replayability } from './eventSchema.js';
|
|
13
13
|
export { readEvents, statusReport, listCycles, showCycle, listTriggers, dailySummary, buildNarrativeSnapshot, NARRATIVE_DEFAULT_LIMIT, NARRATIVE_MAX_LIMIT, } from './reports.js';
|
package/dist/events/public.js
CHANGED
|
@@ -3,7 +3,7 @@ export { Replayer } from './replayer.js';
|
|
|
3
3
|
export { eventCountsProjector, DEFAULT_PROJECTORS } from './projectors.js';
|
|
4
4
|
export { LineTooLargeError, MAX_LINE_BYTES } from './eventStore.js';
|
|
5
5
|
export { ArchiveSegmentConflictError, InvalidRootEventArchiveError, InvalidRootEventLogError, RootEventHistoryGapError, archiveRootEvents, inspectRootEventArchive, planRootEventArchive, readRootEventHistory, rootEventArchiveDir, rootEventArchiveSegmentName, validateRootEventHistory, ROOT_EVENT_ARCHIVE_DEFAULT_KEEP_EVENTS, } from './eventArchive.js';
|
|
6
|
-
export { rootEventsPath, evomapHome, mvDir, personalityStatePath, assetsDir, materialDir, materialStorePath, materialWatermarkPath, tracesDir, assetCallLogPath } from './paths.js';
|
|
6
|
+
export { rootEventsPath, evomapHome, mvDir, personalityStatePath, assetsDir, materialDir, materialStorePath, materialWatermarkPath, tracesDir, assetCallLogPath, learningTraceDir } from './paths.js';
|
|
7
7
|
export { EVENT_SCHEMA_VERSION } from './eventSchema.js';
|
|
8
8
|
export { readEvents, statusReport, listCycles, showCycle, listTriggers, dailySummary, buildNarrativeSnapshot, NARRATIVE_DEFAULT_LIMIT, NARRATIVE_MAX_LIMIT, } from './reports.js';
|
|
9
9
|
export { buildRetentionReport, defaultMaterialCursorPath, defaultMaterialCursorPaths, RETENTION_DEFAULT_MAX_MATERIAL_BYTES, RETENTION_DEFAULT_MAX_MATERIAL_RECORDS, RETENTION_DEFAULT_MAX_ROOT_BYTES, RETENTION_DEFAULT_MAX_ROOT_EVENTS, RETENTION_DEFAULT_ROOT_TAIL_EVENTS, RETENTION_DEFAULT_WATCH_RATIO, } from './retention.js';
|
package/dist/exec/autoExec.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { type OpenPrLister } from './openPrRegistry.js';
|
|
|
10
10
|
import type { ReuseOutcomeSummary, ReuseOutcomeEvent } from '../ops/reuseOutcomes.js';
|
|
11
11
|
import type { PersonalityStore } from '../personality/store.js';
|
|
12
12
|
import type { MemoryGraphProvider } from '../algo/memoryGraph.js';
|
|
13
|
+
import { type LearningPacketSink, type TraceSink } from '../trace/learningTrace.js';
|
|
13
14
|
export interface AutoExecTask {
|
|
14
15
|
id: string;
|
|
15
16
|
repo: string;
|
|
@@ -110,6 +111,20 @@ export interface AutoExecDeps {
|
|
|
110
111
|
agent?: AgentRunner;
|
|
111
112
|
/** Test/custom seam: inject git instead of spawning git. */
|
|
112
113
|
git?: GitRunner;
|
|
114
|
+
/**
|
|
115
|
+
* Learning trace (Learning Ops slice 2): when set, each task run gets its own AgentRunTraceRecorder
|
|
116
|
+
* (traceId = the cycleId) emitting run.started/model.called/tool.failed/run.completed, and a
|
|
117
|
+
* LearningPacket draft is submitted here after the cycle. Best-effort: trace/packet failures never
|
|
118
|
+
* change the verdict. Omit → zero trace work, byte-identical to today.
|
|
119
|
+
*/
|
|
120
|
+
learningTrace?: {
|
|
121
|
+
/** Where packet drafts go (file/memory/hub-adapter implementation). */
|
|
122
|
+
packetSink: LearningPacketSink;
|
|
123
|
+
/** Optional live per-event sink (e.g. FileTraceSink JSONL tail). */
|
|
124
|
+
traceSink?: TraceSink;
|
|
125
|
+
/** Hub packet sourceRepo column; default 'evolver-v2'. */
|
|
126
|
+
sourceRepo?: string;
|
|
127
|
+
};
|
|
113
128
|
}
|
|
114
129
|
export interface ForcedGeneFields {
|
|
115
130
|
forcedGeneId?: unknown;
|
package/dist/exec/autoExec.js
CHANGED
|
@@ -11,6 +11,7 @@ import { intakeGene } from '../algo/geneIntake.js';
|
|
|
11
11
|
import { runEvolutionCycle } from '../algo/orchestrator.js';
|
|
12
12
|
import { makeSafeExecute, makeTrustedGeneResolver } from './autonomousCycle.js';
|
|
13
13
|
import { findSignalHints } from './openPrRegistry.js';
|
|
14
|
+
import { AgentRunTraceRecorder, buildLearningPacketDraft } from '../trace/learningTrace.js';
|
|
14
15
|
/** Same path-containment as the bridge guard — used here to refuse before running anything (clean verdict). */
|
|
15
16
|
function withinAllowlist(repo, roots) {
|
|
16
17
|
const c = resolvePath(repo);
|
|
@@ -136,6 +137,20 @@ export async function runAutoExecTask(deps, rawTask, safety) {
|
|
|
136
137
|
// that throws degrades to solving fresh (no hub candidates) rather than failing the task.
|
|
137
138
|
let hubCandidates = [];
|
|
138
139
|
const cycleId = `autoexec-${task.id}`;
|
|
140
|
+
// Learning trace (slice 2): one recorder per task run, traceId = cycleId so the trace joins the event log.
|
|
141
|
+
// Purely observational — every recorder call and the packet submit are wrapped so they can never change
|
|
142
|
+
// the verdict or fail the task.
|
|
143
|
+
const traceRecorder = deps.learningTrace
|
|
144
|
+
? new AgentRunTraceRecorder({
|
|
145
|
+
runId: cycleId,
|
|
146
|
+
taskId: task.id,
|
|
147
|
+
...(deps.learningTrace.traceSink ? { sink: deps.learningTrace.traceSink } : {}),
|
|
148
|
+
})
|
|
149
|
+
: undefined;
|
|
150
|
+
try {
|
|
151
|
+
traceRecorder?.runStarted({ taskSummary: task.expectedEffect, signals: cycleSignals, metadata: { repo: task.repo, target: task.target } });
|
|
152
|
+
}
|
|
153
|
+
catch { /* observability only */ }
|
|
139
154
|
if (deps.hubReuse) {
|
|
140
155
|
try {
|
|
141
156
|
hubCandidates = await executableHubCandidates(deps, await deps.hubReuse(cycleSignals, { cycleId }));
|
|
@@ -153,6 +168,7 @@ export async function runAutoExecTask(deps, rawTask, safety) {
|
|
|
153
168
|
...(deps.personality ? { personality: deps.personality } : {}),
|
|
154
169
|
...(deps.agent ? { agent: deps.agent } : {}),
|
|
155
170
|
...(deps.git ? { git: deps.git } : {}),
|
|
171
|
+
...(traceRecorder ? { traceRecorder } : {}),
|
|
156
172
|
});
|
|
157
173
|
const strategyName = task.strategyName ?? deps.strategyName;
|
|
158
174
|
// Intentional semantics (see #308 review M1): a task carrying `strategy` — even without an
|
|
@@ -194,6 +210,24 @@ export async function runAutoExecTask(deps, rawTask, safety) {
|
|
|
194
210
|
});
|
|
195
211
|
const status = res.finalStage === 'solidified' ? 'solidified' : res.finalStage === 'failed' ? 'failed' : 'innovated';
|
|
196
212
|
const cap = res.capsule;
|
|
213
|
+
if (traceRecorder && deps.learningTrace) {
|
|
214
|
+
try {
|
|
215
|
+
traceRecorder.runCompleted({
|
|
216
|
+
status: res.finalStage === 'solidified' ? 'success' : 'failed',
|
|
217
|
+
...(cap?.outcome?.score !== undefined ? { score: cap.outcome.score } : {}),
|
|
218
|
+
...(res.reasons.length > 0 ? { reason: res.reasons.join('; ') } : {}),
|
|
219
|
+
...(res.producedValue !== undefined ? { producedValue: res.producedValue } : {}),
|
|
220
|
+
...(res.failureKind !== undefined ? { failureKind: res.failureKind } : {}),
|
|
221
|
+
});
|
|
222
|
+
await deps.learningTrace.packetSink.submit(buildLearningPacketDraft(traceRecorder, {
|
|
223
|
+
sourceRepo: deps.learningTrace.sourceRepo ?? 'evolver-v2',
|
|
224
|
+
taskSummary: task.expectedEffect,
|
|
225
|
+
signals: cycleSignals,
|
|
226
|
+
environment: { repo: task.repo, runner: safety.runner ?? 'claude' },
|
|
227
|
+
}));
|
|
228
|
+
}
|
|
229
|
+
catch { /* packet delivery is best-effort; never fail the task */ }
|
|
230
|
+
}
|
|
197
231
|
if (deps.memoryGraph && res.decision?.selectedGeneId && (res.finalStage === 'solidified' || res.finalStage === 'failed')) {
|
|
198
232
|
const producedSuccess = res.finalStage === 'solidified' && res.producedValue === true;
|
|
199
233
|
try {
|
|
@@ -6,6 +6,7 @@ import type { GeneDecision } from '../algo/geneSelection.js';
|
|
|
6
6
|
import type { ExecutionResult } from '../algo/cycleEngine.js';
|
|
7
7
|
import { type GeneResolver, type ValidateHook, type AgentRunnerOptions, type RunnerName, type AgentRunner, type GitRunner } from './claudeBridge.js';
|
|
8
8
|
import type { PersonalityStore } from '../personality/store.js';
|
|
9
|
+
import type { AgentRunTraceRecorder } from '../trace/learningTrace.js';
|
|
9
10
|
/**
|
|
10
11
|
* Resolve a gene's strategy from the store and whether it is safe to EMBED into an autonomous agent's prompt —
|
|
11
12
|
* the exec-side link from #30 (provenance ledger) and the review-state gate to #45 (requireTrustedGene gate). A
|
|
@@ -47,4 +48,6 @@ export declare function makeSafeExecute(repo: string, store: AssetStoreProvider,
|
|
|
47
48
|
personality?: PersonalityStore;
|
|
48
49
|
agent?: AgentRunner;
|
|
49
50
|
git?: GitRunner;
|
|
51
|
+
/** Optional learning-trace recorder forwarded to the exec bridge (Learning Ops slice 2). */
|
|
52
|
+
traceRecorder?: AgentRunTraceRecorder;
|
|
50
53
|
}): (mutation: Mutation, decision: GeneDecision) => Promise<ExecutionResult>;
|
|
@@ -67,6 +67,7 @@ export function makeSafeExecute(repo, store, safety, opts = {}) {
|
|
|
67
67
|
enabled: true,
|
|
68
68
|
...(opts.agent ? { agent: opts.agent } : {}),
|
|
69
69
|
...(opts.git ? { git: opts.git } : {}),
|
|
70
|
+
...(opts.traceRecorder ? { traceRecorder: opts.traceRecorder } : {}),
|
|
70
71
|
allowedRoots: safety.allowedRoots,
|
|
71
72
|
...(safety.runner ? { runner: safety.runner } : {}),
|
|
72
73
|
...(safety.isolation === 'none' ? {} : { isolation: 'worktree' }),
|
|
@@ -3,6 +3,7 @@ import type { GeneDecision } from '../algo/geneSelection.js';
|
|
|
3
3
|
import type { ExecutionResult } from '../algo/cycleEngine.js';
|
|
4
4
|
import { type GeneStrategyInfo } from './prompt.js';
|
|
5
5
|
import type { PersonalityStore } from '../personality/store.js';
|
|
6
|
+
import type { AgentRunTraceRecorder } from '../trace/learningTrace.js';
|
|
6
7
|
import { type AgentRunner, type AgentRunnerOptions, type RunnerName } from './runnerRegistry.js';
|
|
7
8
|
export { resolveSpawnCommand, spawnCapture, DEFAULT_MAX_CAPTURE_BYTES, UnboundedSkipPermissionsError, UnsupportedCursorSkipPermissionsError, UnsupportedGeminiPermissionOptionsError, claudeRunnerArgs, makeClaudeHeadlessRunner, claudeHeadlessRunner, codexRunnerArgs, makeCodexHeadlessRunner, cursorRunnerArgs, makeCursorHeadlessRunner, getRunnerSpec, geminiRunnerArgs, makeGeminiHeadlessRunner, classifyGeminiRunnerResult, } from './runnerRegistry.js';
|
|
8
9
|
export type { AgentRunContext, AgentRunResult, AgentRunner, RunnerName, AgentRunnerOptions, ClaudeRunnerOptions, CodexRunnerOptions, AgentRunnerSpec, } from './runnerRegistry.js';
|
|
@@ -86,6 +87,12 @@ export interface ExecBridgeOptions {
|
|
|
86
87
|
* the store's default state (store.load never throws), it never fails the run.
|
|
87
88
|
*/
|
|
88
89
|
personality?: PersonalityStore;
|
|
90
|
+
/**
|
|
91
|
+
* Optional learning-trace recorder (Learning Ops slice 2): when set, the bridge emits `model.called` around
|
|
92
|
+
* each agent spawn and `tool.failed`/`retry.attempted` metadata on failures. Best-effort observability — a
|
|
93
|
+
* recorder/sink failure must never affect the execution result, so every emission is wrapped defensively.
|
|
94
|
+
*/
|
|
95
|
+
traceRecorder?: AgentRunTraceRecorder;
|
|
89
96
|
}
|
|
90
97
|
export declare class ExecBridgeDisabledError extends Error {
|
|
91
98
|
constructor();
|
|
@@ -429,6 +429,23 @@ export function makeClaudeExecBridge(opts) {
|
|
|
429
429
|
...(opts.signal ? { signal: opts.signal } : {}),
|
|
430
430
|
});
|
|
431
431
|
observedRun = run;
|
|
432
|
+
// Learning trace (slice 2): one model.called per agent spawn — the headless runner is one opaque
|
|
433
|
+
// model-driven turn from the bridge's viewpoint (per-request fidelity arrives via the proxy's llm_turn
|
|
434
|
+
// records; recordLlmTurn folds those when a caller has them). Best-effort: never affects the result.
|
|
435
|
+
try {
|
|
436
|
+
opts.traceRecorder?.modelCalled({
|
|
437
|
+
...(opts.runner !== undefined ? { provider: opts.runner } : { provider: 'claude' }),
|
|
438
|
+
...(opts.agentOptions?.model !== undefined ? { model: opts.agentOptions.model } : {}),
|
|
439
|
+
...(run.exitCode !== undefined && run.exitCode !== null ? { stopReason: `exit_${run.exitCode}` } : {}),
|
|
440
|
+
});
|
|
441
|
+
if (!run.ok) {
|
|
442
|
+
opts.traceRecorder?.toolFailed({
|
|
443
|
+
toolName: 'agent_runner',
|
|
444
|
+
error: run.error ?? run.failureKind ?? 'agent run failed',
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
catch { /* trace emission is observability only */ }
|
|
432
449
|
if (run.failureKind === 'cancelled' || opts.signal?.aborted)
|
|
433
450
|
throw new ExecBridgeRunCancelledError();
|
|
434
451
|
// A worktree can contain three independent change surfaces after the agent exits: staged tracked changes,
|
package/dist/hub/capability.d.ts
CHANGED
|
@@ -111,6 +111,75 @@ export interface ReuseResultReceipt {
|
|
|
111
111
|
reason?: string;
|
|
112
112
|
id?: string;
|
|
113
113
|
}
|
|
114
|
+
export type LearningAssetType = 'memory' | 'skill' | 'playbook' | 'eval';
|
|
115
|
+
export type LearningAssetStatus = 'pending_review' | 'active' | 'disabled' | 'stale' | 'archived';
|
|
116
|
+
export type LearningAssetEffectiveStatus = LearningAssetStatus | 'expired';
|
|
117
|
+
export type LearningAssetUsageOutcome = ReuseResultOutcome;
|
|
118
|
+
export interface LearningAssetOwner {
|
|
119
|
+
node_id?: string | null;
|
|
120
|
+
user_id?: string | null;
|
|
121
|
+
org_id?: string | null;
|
|
122
|
+
workspace_id?: string | null;
|
|
123
|
+
}
|
|
124
|
+
/** Hub-owned learning asset record. Core keeps it opaque; adapters own wire shape and auth. */
|
|
125
|
+
export interface LearningAssetRecord {
|
|
126
|
+
asset_id: string;
|
|
127
|
+
type: LearningAssetType;
|
|
128
|
+
source_packet_id?: string;
|
|
129
|
+
scope?: string[];
|
|
130
|
+
owner?: LearningAssetOwner;
|
|
131
|
+
title?: string;
|
|
132
|
+
summary?: string;
|
|
133
|
+
confidence?: number;
|
|
134
|
+
last_used_at?: string | null;
|
|
135
|
+
usage_count?: number;
|
|
136
|
+
success_count?: number;
|
|
137
|
+
success_rate?: number;
|
|
138
|
+
expires_at?: string | null;
|
|
139
|
+
conflicts_with?: string[];
|
|
140
|
+
status?: LearningAssetStatus;
|
|
141
|
+
effective_status?: LearningAssetEffectiveStatus;
|
|
142
|
+
status_reason?: string;
|
|
143
|
+
provenance?: Record<string, unknown>;
|
|
144
|
+
payload?: Record<string, unknown>;
|
|
145
|
+
created_at?: string | null;
|
|
146
|
+
updated_at?: string | null;
|
|
147
|
+
[k: string]: unknown;
|
|
148
|
+
}
|
|
149
|
+
export interface LearningAssetListOptions {
|
|
150
|
+
type?: LearningAssetType;
|
|
151
|
+
scope?: readonly string[];
|
|
152
|
+
status?: LearningAssetStatus | readonly LearningAssetStatus[];
|
|
153
|
+
includeExpired?: boolean;
|
|
154
|
+
includePayload?: boolean;
|
|
155
|
+
limit?: number;
|
|
156
|
+
}
|
|
157
|
+
export interface LearningAssetListResult {
|
|
158
|
+
assets: LearningAssetRecord[];
|
|
159
|
+
limit: number;
|
|
160
|
+
reason?: string;
|
|
161
|
+
}
|
|
162
|
+
export interface LearningAssetUsageReport {
|
|
163
|
+
assetId?: string;
|
|
164
|
+
usedAssetIds?: readonly string[];
|
|
165
|
+
outcome: LearningAssetUsageOutcome;
|
|
166
|
+
score?: number;
|
|
167
|
+
sourceEventId: string;
|
|
168
|
+
reason?: string;
|
|
169
|
+
}
|
|
170
|
+
export interface LearningAssetUsageResultRow {
|
|
171
|
+
asset_id: string;
|
|
172
|
+
recorded: boolean;
|
|
173
|
+
duplicated?: boolean;
|
|
174
|
+
error?: string;
|
|
175
|
+
[k: string]: unknown;
|
|
176
|
+
}
|
|
177
|
+
/** Learning-asset usage reporting is best-effort; failures must not break runtime solve paths. */
|
|
178
|
+
export interface LearningAssetUsageReceipt {
|
|
179
|
+
recorded: boolean;
|
|
180
|
+
reason?: string;
|
|
181
|
+
results: LearningAssetUsageResultRow[];
|
|
182
|
+
}
|
|
114
183
|
/**
|
|
115
184
|
* Pre-publish dry-run result. The hub runs the same hub-side quality + content-safety
|
|
116
185
|
* gate as publish but stores nothing and charges no credits, so an agent can check a
|
|
@@ -141,6 +210,10 @@ export interface RecipeCreateRequest {
|
|
|
141
210
|
pricePerExecution?: number;
|
|
142
211
|
currency?: string;
|
|
143
212
|
maxConcurrent?: number;
|
|
213
|
+
idempotencyKey?: string;
|
|
214
|
+
}
|
|
215
|
+
export interface RecipePublishOptions {
|
|
216
|
+
idempotencyKey?: string;
|
|
144
217
|
}
|
|
145
218
|
export interface RecipeReceipt {
|
|
146
219
|
recipeId?: string;
|
|
@@ -159,11 +232,11 @@ export interface RecipeExpressionReceipt extends RecipeReceipt {
|
|
|
159
232
|
/** Optional Hub recipe/DNA capability. Core stays wire-agnostic; adapters own REST shape. */
|
|
160
233
|
export interface RecipeCapability {
|
|
161
234
|
create(request: RecipeCreateRequest): Promise<RecipeReceipt>;
|
|
162
|
-
publish(recipeId: string): Promise<RecipeReceipt>;
|
|
235
|
+
publish(recipeId: string, options?: RecipePublishOptions): Promise<RecipeReceipt>;
|
|
163
236
|
get(recipeId: string): Promise<RecipeFetchReceipt>;
|
|
164
237
|
express(recipeId: string, request?: RecipeExpressRequest): Promise<RecipeExpressionReceipt>;
|
|
165
238
|
}
|
|
166
|
-
export type HubCapabilityName = 'publish' | 'fetch' | 'search' | 'task' | 'mailbox' | 'auth' | 'audit' | 'air_gap' | 'tenant_isolation' | 'marketplace' | 'economy' | 'questions' | 'recipes' | 'agent_directory';
|
|
239
|
+
export type HubCapabilityName = 'publish' | 'fetch' | 'search' | 'task' | 'mailbox' | 'auth' | 'audit' | 'air_gap' | 'tenant_isolation' | 'marketplace' | 'economy' | 'questions' | 'recipes' | 'agent_directory' | 'learning_assets';
|
|
167
240
|
/** hub 能力清单(可选). core 据此动态适配 UI/行为, "按需耦合". */
|
|
168
241
|
export interface HubManifest {
|
|
169
242
|
capabilities: HubCapabilityName[] | string[];
|
|
@@ -231,6 +304,10 @@ export interface HubCapability {
|
|
|
231
304
|
fetchAssetById?(assetId: string): Promise<AssetRecord | null>;
|
|
232
305
|
/** 可选: 报告某个资产被复用后的结果. PHub/enterprise adapter 实现; 不支持时 runtime 应明确降级. */
|
|
233
306
|
recordReuseResult?(report: ReuseResultReport): Promise<ReuseResultReceipt>;
|
|
307
|
+
/** 可选: 列出 hub 端 runtime learning assets. Missing means the adapter does not support this MVP surface. */
|
|
308
|
+
listLearningAssets?(options?: LearningAssetListOptions): Promise<LearningAssetListResult>;
|
|
309
|
+
/** 可选: 上报 learning asset 使用结果. Best-effort; failures must never break solve paths. */
|
|
310
|
+
recordLearningAssetUsage?(report: LearningAssetUsageReport): Promise<LearningAssetUsageReceipt>;
|
|
234
311
|
/**
|
|
235
312
|
* 可选: 发布前 dry-run 校验(公版 POST /a2a/validate). 跑与 publish 相同的 hub 端质量门禁 + 内容安全扫描,
|
|
236
313
|
* 但不落库、不计费; 经由 Proxy 暴露时调用方需先执行与 publish 相同的客户端脱敏/泄漏拦截.
|
package/dist/trace/index.d.ts
CHANGED
package/dist/trace/index.js
CHANGED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import type { Observer } from '../observers/observerBus.js';
|
|
2
|
+
import type { TraceTurnDraft } from './trajectory.js';
|
|
3
|
+
export declare const TRACE_EVENT_SCHEMA = "trace_event.v0";
|
|
4
|
+
export declare const LEARNING_PACKET_SCHEMA = "learning_packet.v0";
|
|
5
|
+
/** Lifecycle vocabulary for slice 1. Kept flat + closed so hub-side eventType stays queryable. */
|
|
6
|
+
export declare const TRACE_EVENT_TYPES: readonly ["run.started", "run.completed", "model.called", "tool.called", "tool.failed", "retry.attempted", "reflection.recorded", "intervention.received"];
|
|
7
|
+
export type TraceEventType = (typeof TRACE_EVENT_TYPES)[number];
|
|
8
|
+
/** Mirrors hub LearningOpsTraceEvent columns (eventId/eventType/traceId/sessionId/taskId/sequence/occurredAt/payload/metadata). */
|
|
9
|
+
export interface TraceEvent {
|
|
10
|
+
schemaVersion: typeof TRACE_EVENT_SCHEMA;
|
|
11
|
+
eventId: string;
|
|
12
|
+
eventType: TraceEventType;
|
|
13
|
+
/** Run-scoped trace id; every event of one agent run shares it. */
|
|
14
|
+
traceId: string;
|
|
15
|
+
sessionId?: string;
|
|
16
|
+
taskId?: string;
|
|
17
|
+
/** 1-based, strictly increasing per run — hub-side ordering key. */
|
|
18
|
+
sequence: number;
|
|
19
|
+
occurredAt: string;
|
|
20
|
+
payload: Record<string, unknown>;
|
|
21
|
+
metadata: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
export interface TraceSink {
|
|
24
|
+
emit(event: TraceEvent): void;
|
|
25
|
+
}
|
|
26
|
+
export declare class InMemoryTraceSink implements TraceSink {
|
|
27
|
+
readonly events: TraceEvent[];
|
|
28
|
+
emit(event: TraceEvent): void;
|
|
29
|
+
}
|
|
30
|
+
/** JSONL append sink for local inspection / offline replay. */
|
|
31
|
+
export declare class FileTraceSink implements TraceSink {
|
|
32
|
+
private readonly path;
|
|
33
|
+
constructor(path: string);
|
|
34
|
+
emit(event: TraceEvent): void;
|
|
35
|
+
}
|
|
36
|
+
export declare class ConsoleTraceSink implements TraceSink {
|
|
37
|
+
private readonly log;
|
|
38
|
+
constructor(log?: (line: string) => void);
|
|
39
|
+
emit(event: TraceEvent): void;
|
|
40
|
+
}
|
|
41
|
+
export interface AgentRunTraceRecorderOptions {
|
|
42
|
+
/** Stable run identity; becomes traceId and the packet sourceRun. */
|
|
43
|
+
runId: string;
|
|
44
|
+
sessionId?: string;
|
|
45
|
+
taskId?: string;
|
|
46
|
+
sink?: TraceSink;
|
|
47
|
+
/** Injected clock for deterministic tests. */
|
|
48
|
+
now?: () => number;
|
|
49
|
+
/** Injected id factory for deterministic tests. */
|
|
50
|
+
eventIdFactory?: (sequence: number) => string;
|
|
51
|
+
}
|
|
52
|
+
export interface RunStartedInput {
|
|
53
|
+
taskSummary?: string;
|
|
54
|
+
signals?: readonly string[];
|
|
55
|
+
geneId?: string;
|
|
56
|
+
metadata?: Record<string, unknown>;
|
|
57
|
+
}
|
|
58
|
+
export interface RunCompletedInput {
|
|
59
|
+
status: 'success' | 'failed';
|
|
60
|
+
score?: number;
|
|
61
|
+
reason?: string;
|
|
62
|
+
producedValue?: boolean;
|
|
63
|
+
failureKind?: string;
|
|
64
|
+
}
|
|
65
|
+
export interface ModelCalledInput {
|
|
66
|
+
provider?: string;
|
|
67
|
+
model?: string;
|
|
68
|
+
requestId?: string;
|
|
69
|
+
latencyMs?: number;
|
|
70
|
+
usage?: Record<string, unknown>;
|
|
71
|
+
stopReason?: string;
|
|
72
|
+
}
|
|
73
|
+
export interface ToolCalledInput {
|
|
74
|
+
toolName: string;
|
|
75
|
+
callId?: string;
|
|
76
|
+
durationMs?: number;
|
|
77
|
+
metadata?: Record<string, unknown>;
|
|
78
|
+
}
|
|
79
|
+
export interface ToolFailedInput {
|
|
80
|
+
toolName: string;
|
|
81
|
+
callId?: string;
|
|
82
|
+
error: string;
|
|
83
|
+
}
|
|
84
|
+
export interface RetryInput {
|
|
85
|
+
attempt: number;
|
|
86
|
+
reason?: string;
|
|
87
|
+
target?: string;
|
|
88
|
+
}
|
|
89
|
+
export interface ReflectionInput {
|
|
90
|
+
outcome: string;
|
|
91
|
+
action?: string;
|
|
92
|
+
summary?: string;
|
|
93
|
+
}
|
|
94
|
+
export interface InterventionInput {
|
|
95
|
+
kind: string;
|
|
96
|
+
actorId?: string;
|
|
97
|
+
detail?: string;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Per-run trace recorder: the single hook surface the runtime calls at lifecycle points.
|
|
101
|
+
* Sequence numbers are assigned here (monotonic per run), so downstream ordering never depends on
|
|
102
|
+
* sink latency or clock resolution.
|
|
103
|
+
*/
|
|
104
|
+
export declare class AgentRunTraceRecorder {
|
|
105
|
+
private readonly opts;
|
|
106
|
+
private sequence;
|
|
107
|
+
private readonly recorded;
|
|
108
|
+
constructor(opts: AgentRunTraceRecorderOptions);
|
|
109
|
+
get events(): readonly TraceEvent[];
|
|
110
|
+
runStarted(input?: RunStartedInput): TraceEvent;
|
|
111
|
+
modelCalled(input?: ModelCalledInput): TraceEvent;
|
|
112
|
+
toolCalled(input: ToolCalledInput): TraceEvent;
|
|
113
|
+
toolFailed(input: ToolFailedInput): TraceEvent;
|
|
114
|
+
retryAttempted(input: RetryInput): TraceEvent;
|
|
115
|
+
reflectionRecorded(input: ReflectionInput): TraceEvent;
|
|
116
|
+
interventionReceived(input: InterventionInput): TraceEvent;
|
|
117
|
+
runCompleted(input: RunCompletedInput): TraceEvent;
|
|
118
|
+
/** Fold one normalized llm_turn (trace/trajectory.ts) into model.called + tool.called/tool.failed events. */
|
|
119
|
+
recordLlmTurn(turn: TraceTurnDraft): TraceEvent[];
|
|
120
|
+
private record;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Bypass-side bridge (never blocks the write path): maps the engine's existing root events onto the
|
|
124
|
+
* run's TraceEvent stream, so CycleEngine needs no code change for run start/completion, reflection,
|
|
125
|
+
* or human intervention coverage.
|
|
126
|
+
*/
|
|
127
|
+
export declare function learningTraceObserver(deps: {
|
|
128
|
+
recorder: AgentRunTraceRecorder;
|
|
129
|
+
timeoutMs?: number;
|
|
130
|
+
}): Observer;
|
|
131
|
+
export interface LearningPacketDraftInput {
|
|
132
|
+
/** e.g. 'evolver-v2'. Hub column sourceRepo. */
|
|
133
|
+
sourceRepo: string;
|
|
134
|
+
taskSummary?: string;
|
|
135
|
+
signals?: readonly string[];
|
|
136
|
+
environment?: Record<string, unknown>;
|
|
137
|
+
}
|
|
138
|
+
/** Local draft aligned with hub LearningOpsPacket ingest fields; placeholders are explicit, not implied. */
|
|
139
|
+
export interface LearningPacketDraft {
|
|
140
|
+
schemaVersion: typeof LEARNING_PACKET_SCHEMA;
|
|
141
|
+
status: 'draft';
|
|
142
|
+
source: {
|
|
143
|
+
repo: string;
|
|
144
|
+
run: string;
|
|
145
|
+
type: 'agent_run';
|
|
146
|
+
id: string;
|
|
147
|
+
};
|
|
148
|
+
task: {
|
|
149
|
+
taskId: string | null;
|
|
150
|
+
summary: string | null;
|
|
151
|
+
signals: string[];
|
|
152
|
+
};
|
|
153
|
+
context: {
|
|
154
|
+
sessionId: string | null;
|
|
155
|
+
traceId: string;
|
|
156
|
+
environment: Record<string, unknown>;
|
|
157
|
+
};
|
|
158
|
+
trajectory: TraceEvent[];
|
|
159
|
+
artifacts: {
|
|
160
|
+
placeholder: true;
|
|
161
|
+
items: never[];
|
|
162
|
+
};
|
|
163
|
+
evaluation: {
|
|
164
|
+
placeholder: true;
|
|
165
|
+
outcomeStatus: 'success' | 'failed' | 'unknown';
|
|
166
|
+
verifier: null;
|
|
167
|
+
failureCategory: string | null;
|
|
168
|
+
};
|
|
169
|
+
governance: {
|
|
170
|
+
placeholder: true;
|
|
171
|
+
redactionStatus: 'metadata_only';
|
|
172
|
+
consentStatus: 'unknown';
|
|
173
|
+
trainingEligible: false;
|
|
174
|
+
retentionPolicy: 'standard';
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
export declare function buildLearningPacketDraft(recorder: AgentRunTraceRecorder, input: LearningPacketDraftInput): LearningPacketDraft;
|
|
178
|
+
export interface LearningPacketSubmitResult {
|
|
179
|
+
accepted: boolean;
|
|
180
|
+
reason?: string;
|
|
181
|
+
}
|
|
182
|
+
export interface LearningPacketSink {
|
|
183
|
+
submit(draft: LearningPacketDraft): Promise<LearningPacketSubmitResult>;
|
|
184
|
+
}
|
|
185
|
+
export declare class InMemoryLearningPacketSink implements LearningPacketSink {
|
|
186
|
+
readonly drafts: LearningPacketDraft[];
|
|
187
|
+
submit(draft: LearningPacketDraft): Promise<LearningPacketSubmitResult>;
|
|
188
|
+
}
|
|
189
|
+
/** Writes one JSON file per run — the offline/no-hub path (and a manual-inspection artifact). */
|
|
190
|
+
export declare class FileLearningPacketSink implements LearningPacketSink {
|
|
191
|
+
private readonly dir;
|
|
192
|
+
constructor(dir: string);
|
|
193
|
+
submit(draft: LearningPacketDraft): Promise<LearningPacketSubmitResult>;
|
|
194
|
+
}
|