@evomap/evolver-adapter-public 2.0.0-beta.19 → 2.0.0-beta.22

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/hubReuse.js CHANGED
@@ -13,7 +13,7 @@
13
13
  // - payload cache: assetId → phase-3 payload (content-addressed, long/permanent, bounded LRU). A cached
14
14
  // payload → ZERO fetch. Both clocks are injected so TTL/eviction is deterministic and testable.
15
15
  import { createHash } from 'node:crypto';
16
- import { hub, algo, wire } from '@evomap/evolver-core';
16
+ import { hub, algo, signals as signalNs, wire } from '@evomap/evolver-core';
17
17
  const { scoreSearchResults, decideReuse, DEFAULT_MIN_REUSE_SCORE, } = hub;
18
18
  const GENE_WIRE_KEYS = new Set([
19
19
  'type',
@@ -32,10 +32,17 @@ const GENE_WIRE_KEYS = new Set([
32
32
  'routing_hint',
33
33
  'tool_policy',
34
34
  'generation_meta',
35
+ // K_auto projection-key coordinates + runtime authorship (v2-delta; ride-along until gep-sdk 1.13).
36
+ 'model_name',
37
+ 'claims',
38
+ 'scope',
39
+ 'runtime_profile',
40
+ 'verifier_profile',
35
41
  'asset_id',
36
42
  ]);
37
43
  const HUB_DELIVERY_METADATA_KEYS = new Set([
38
44
  'status',
45
+ 'trust_state',
39
46
  'success_streak',
40
47
  'reputation_score',
41
48
  'gdi_score',
@@ -55,8 +62,15 @@ const HUB_DELIVERY_METADATA_KEYS = new Set([
55
62
  'semanticSimilarity',
56
63
  '_search_score',
57
64
  'search_score',
65
+ '_match_score',
66
+ 'match_score',
67
+ '_retrieval_rank',
68
+ 'retrieval_rank',
58
69
  'payload_backfill_reason',
70
+ 'original_asset_id',
59
71
  'asset_type',
72
+ 'local_id',
73
+ 'source',
60
74
  'bundle_id',
61
75
  'callable',
62
76
  'payload_ready',
@@ -131,7 +145,7 @@ export function buildSemanticQuery(signals) {
131
145
  export function signalFingerprint(signals) {
132
146
  return [...signals].map((s) => String(s).trim()).filter(Boolean).sort().join('|');
133
147
  }
134
- export const TASK_DOMAIN_SIGNAL_PREFIX = 'task_domain:';
148
+ export const TASK_DOMAIN_SIGNAL_PREFIX = signalNs.TASK_DOMAIN_SIGNAL_PREFIX;
135
149
  /**
136
150
  * evolver domain slug → hub domain taxonomy (evomap-hub domainDetectionService VALID_DOMAINS).
137
151
  * Only mapped slugs may ride the wire: the hub validates against its own taxonomy and silently
@@ -153,19 +167,10 @@ const HUB_DOMAIN_BY_SLUG = {
153
167
  * fall back to unscoped recall (today's behaviour).
154
168
  */
155
169
  export function hubDomainFromSignals(signals) {
156
- const slugs = new Set();
157
- for (const raw of signals) {
158
- const signal = String(raw).trim().toLowerCase();
159
- if (!signal.startsWith(TASK_DOMAIN_SIGNAL_PREFIX))
160
- continue;
161
- const slug = signal.slice(TASK_DOMAIN_SIGNAL_PREFIX.length).trim();
162
- if (slug)
163
- slugs.add(slug);
164
- }
165
- if (slugs.size !== 1)
166
- return null;
167
- const [slug] = slugs;
168
- return HUB_DOMAIN_BY_SLUG[slug] ?? null;
170
+ const resolution = signalNs.resolveTaskDomainSignals(signals);
171
+ return resolution.status === 'resolved'
172
+ ? HUB_DOMAIN_BY_SLUG[resolution.slug] ?? null
173
+ : null;
169
174
  }
170
175
  /**
171
176
  * The two-layer reuse cache. Bounded + TTL'd, per-process. A search-cache hit means phase 1 makes ZERO hub
@@ -255,10 +260,15 @@ function stripHubPayloadMetadata(rec) {
255
260
  }
256
261
  return out;
257
262
  }
258
- function stripHubDeliveryMetadataForIntegrity(rec) {
263
+ // Shared content projection for by-id verification and sync quarantine classification.
264
+ export function stripHubDeliveryMetadataForIntegrity(rec) {
259
265
  const out = { ...rec };
260
- for (const key of HUB_DELIVERY_METADATA_KEYS)
266
+ for (const key of HUB_DELIVERY_METADATA_KEYS) {
267
+ // Hub ranking streak is metadata for Genes, while Capsule.success_streak is canonical content.
268
+ if (key === 'success_streak' && out['type'] === 'Capsule')
269
+ continue;
261
270
  delete out[key];
271
+ }
262
272
  // Hub ranking confidence is metadata for Genes, while Capsule.confidence is canonical content.
263
273
  if (out['type'] === 'Gene')
264
274
  delete out['confidence'];
@@ -299,6 +309,10 @@ export function toGeneCandidate(rec) {
299
309
  const signalsMatch = strArr(r['signals_match'] ?? r['signalsMatch']) ?? [];
300
310
  const reuseCount = num(r['reuse_count'] ?? r['reuseCount']) ?? 0;
301
311
  const generationSource = algo.geneGenerationSource(r, geneId);
312
+ // Project onto the wire allowlist first, then re-run strict K_auto on the same bytes selection
313
+ // will see. Soft-preference must not stamp membership from hub delivery metadata that strip drops.
314
+ const hubAsset = stripHubPayloadMetadata(rec);
315
+ const kautoMember = algo.decideKauto(hubAsset).inKauto;
302
316
  return {
303
317
  geneId,
304
318
  assetId,
@@ -310,7 +324,8 @@ export function toGeneCandidate(rec) {
310
324
  ...(typeof r['category'] === 'string' ? { category: r['category'] } : {}),
311
325
  ...(typeof r['summary'] === 'string' ? { summary: r['summary'] } : {}),
312
326
  ...(generationSource ? { generationSource } : {}),
313
- hubAsset: stripHubPayloadMetadata(rec),
327
+ ...(kautoMember ? { kautoMember: true } : {}),
328
+ hubAsset,
314
329
  };
315
330
  }
316
331
  /**
@@ -332,7 +347,7 @@ export async function searchHubMetadata(cap, cache, signals, opts = {}) {
332
347
  // Domain fence: derived from the turn's own task_domain:* signals (never from prose), mapped to
333
348
  // the hub taxonomy. Scopes the structured signal leg only — the semantic leg already carries its
334
349
  // own allowlisted free-text and stays domain-agnostic as the discovery fallback.
335
- const hubDomain = hubDomainFromSignals(signalList);
350
+ const hubDomain = hubDomainFromSignals(signals);
336
351
  const signalSearchLimit = opts.searchLimit ? opts.searchLimit : undefined;
337
352
  const limitKey = signalSearchLimit === undefined ? 'all' : String(signalSearchLimit);
338
353
  const domainKey = hubDomain === null ? '' : `:domain:${hubDomain}`;
@@ -9,7 +9,15 @@ export interface HubLearningPacketSinkOptions {
9
9
  /** Optional node identity recorded on the packet (hub nodeId column). */
10
10
  nodeId?: () => string | undefined;
11
11
  }
12
- /** Deterministic content hash over the draft body (hub contentHash column, dedup aid). */
12
+ /**
13
+ * Deterministic content hash over the draft body (hub contentHash column, dedup aid).
14
+ *
15
+ * Bare 64-hex, NOT `sha256:`-prefixed: the hub column is VarChar(64), so a prefixed
16
+ * digest is 71 chars and every upload failed with a Prisma "value too long" 500. The
17
+ * hub schema now rejects over-64 at validation, which would make it a 400 instead —
18
+ * either way the algorithm is fixed at sha256 by this contract, so the prefix carried
19
+ * no information.
20
+ */
13
21
  export declare function learningPacketContentHash(draft: trace.LearningPacketDraft): string;
14
22
  /**
15
23
  * Auth headers for the strict learning-packets routes (requireAuth reads Authorization only).
@@ -19,16 +19,47 @@ function failureCategoryFor(failureKind) {
19
19
  return 'tool_error';
20
20
  return 'other';
21
21
  }
22
- function outcomeStatusFor(status) {
23
- if (status === 'success')
24
- return 'succeeded';
22
+ /**
23
+ * Map the runtime's outcome onto the hub OUTCOME_STATUSES enum, tiered by whether an
24
+ * external verifier actually adjudicated the run.
25
+ *
26
+ * A verified run gets a definite verdict (`succeeded` / `failed`). An unverified one
27
+ * gets `partially_succeeded` -- deliberately NOT `succeeded`, and no longer omitted:
28
+ *
29
+ * - Omitting it (the previous behaviour) threw the run away. The packet reached the
30
+ * hub with no outcome at all, which is indistinguishable from a run nobody looked
31
+ * at, so a consumer could not tell "we don't know" from "not recorded".
32
+ * - Calling it `succeeded` would be worse: the runtime only knows the turn loop
33
+ * ended without crashing, which is not evidence the task was done correctly.
34
+ * Training on that teaches format imitation.
35
+ *
36
+ * `partially_succeeded` says exactly what is true -- it ran to completion and nobody
37
+ * checked the result -- and pairs with `verifier` being absent, so a consumer filters
38
+ * on the verifier rather than having to infer trust from the status. Darwin's training
39
+ * path takes only rows with a real verifier; see docs/rsi-stage1-plan.md.
40
+ * @param status Runtime-side outcome.
41
+ * @param verified True when an external verifier ran (evaluation.placeholder === false).
42
+ * @returns A hub OUTCOME_STATUSES value.
43
+ */
44
+ function outcomeStatusFor(status, verified) {
25
45
  if (status === 'failed')
26
46
  return 'failed';
27
- return undefined;
47
+ if (status === 'success' && verified)
48
+ return 'succeeded';
49
+ // Ran to completion, unadjudicated -- or the runtime itself is unsure.
50
+ return 'partially_succeeded';
28
51
  }
29
- /** Deterministic content hash over the draft body (hub contentHash column, dedup aid). */
52
+ /**
53
+ * Deterministic content hash over the draft body (hub contentHash column, dedup aid).
54
+ *
55
+ * Bare 64-hex, NOT `sha256:`-prefixed: the hub column is VarChar(64), so a prefixed
56
+ * digest is 71 chars and every upload failed with a Prisma "value too long" 500. The
57
+ * hub schema now rejects over-64 at validation, which would make it a 400 instead —
58
+ * either way the algorithm is fixed at sha256 by this contract, so the prefix carried
59
+ * no information.
60
+ */
30
61
  export function learningPacketContentHash(draft) {
31
- return `sha256:${createHash('sha256').update(JSON.stringify(draft)).digest('hex')}`;
62
+ return createHash('sha256').update(JSON.stringify(draft)).digest('hex');
32
63
  }
33
64
  /**
34
65
  * Auth headers for the strict learning-packets routes (requireAuth reads Authorization only).
@@ -47,7 +78,7 @@ export async function learningOpsAuthHeaders(auth, method, path) {
47
78
  export function learningPacketWireBody(draft, nodeId) {
48
79
  const truncated = draft.trajectory.length > HUB_TRACE_EVENTS_MAX;
49
80
  const events = draft.trajectory.slice(0, HUB_TRACE_EVENTS_MAX);
50
- const outcomeStatus = outcomeStatusFor(draft.evaluation.outcomeStatus);
81
+ const outcomeStatus = outcomeStatusFor(draft.evaluation.outcomeStatus, draft.evaluation.placeholder === false);
51
82
  const failureCategory = failureCategoryFor(draft.evaluation.failureCategory);
52
83
  return {
53
84
  schemaVersion: draft.schemaVersion,
@@ -60,7 +91,7 @@ export function learningPacketWireBody(draft, nodeId) {
60
91
  idempotencyKey: `${draft.source.repo}:${draft.source.run}`,
61
92
  contentHash: learningPacketContentHash(draft),
62
93
  ...(nodeId ? { nodeId } : {}),
63
- ...(outcomeStatus ? { outcomeStatus } : {}),
94
+ outcomeStatus,
64
95
  // evaluation fill-in (slice 6): a non-placeholder evaluation carries the runtime's external verifier
65
96
  // ('automated_test' is in the hub VERIFIERS enum); passed/score details ride inside payload.evaluation.
66
97
  ...(draft.evaluation.verifier !== null ? { verifier: draft.evaluation.verifier } : {}),
@@ -116,10 +147,20 @@ export class HubLearningPacketSink {
116
147
  redirect: 'manual',
117
148
  body: JSON.stringify(learningPacketWireBody(draft, this.opts.nodeId?.())),
118
149
  });
119
- if (res.status === 201) {
150
+ // Hub route returns 201 Created. The public website BFF/proxy in front of
151
+ // /api/learning-packets has been observed to surface the same body as 200.
152
+ // Accept both when a packet id is present so a successful write is never
153
+ // reported as hub 200 rejection (which previously made every live upload
154
+ // look failed while the row was already stored).
155
+ if (res.status === 201 || res.status === 200) {
120
156
  const body = await res.json().catch(() => null);
121
157
  const packet = body && typeof body === 'object' ? body.packet : undefined;
122
- return { accepted: true, ...(typeof packet?.id === 'string' ? { reason: packet.id } : {}) };
158
+ if (typeof packet?.id === 'string') {
159
+ return { accepted: true, reason: packet.id };
160
+ }
161
+ if (res.status === 201)
162
+ return { accepted: true };
163
+ // Bare 200 without a packet body is not a create success we can claim.
123
164
  }
124
165
  if (res.status === 409)
125
166
  return { accepted: true, reason: 'duplicate_source' };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evomap/evolver-adapter-public",
3
- "version": "2.0.0-beta.19",
3
+ "version": "2.0.0-beta.22",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "engines": {
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "@evomap/atp-sdk": "^0.1.0",
20
- "@evomap/evolver-core": "2.0.0-beta.19",
20
+ "@evomap/evolver-core": "2.0.0-beta.22",
21
21
  "undici": "^6.27.0"
22
22
  },
23
23
  "optionalDependencies": {