@warmdrift/kgauto-compiler 2.0.0-alpha.54 → 2.0.0-alpha.56

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.
@@ -58,6 +58,32 @@
58
58
  * must be the base that resolves to `.../api/kgauto/v2` — i.e. the last path
59
59
  * segment of each library POST (`outcomes`, `probe_outcomes`, …) is what
60
60
  * `handle()` receives as `segment`.
61
+ *
62
+ * ## Prefer-header relay (alpha.56)
63
+ *
64
+ * `record()` sends `Prefer: return=representation` on its `/outcomes` POST and
65
+ * parses the inserted row id from the PROXY's response to fire the secondary
66
+ * `compile_outcome_advisories` POST. alpha.54/.55 hardcoded
67
+ * `Prefer: return=minimal` toward the brain and returned `{ok:true}` — so the
68
+ * id-parse found nothing and the advisory secondary silently never fired for
69
+ * factory-mounted consumers (PB + GE convergent finding, 2026-07-06). The
70
+ * factory now honors a caller `Prefer: return=representation` header: it is
71
+ * forwarded to the brain and the brain's response body (the inserted row(s))
72
+ * is relayed verbatim on success. Callers that don't send the header keep the
73
+ * `return=minimal` + `{ok:true}` behavior.
74
+ *
75
+ * ## Quality-segment handle resolution (alpha.55)
76
+ *
77
+ * `recordOutcome()` sends `outcome_id` as `CompileResult.handle` (a string),
78
+ * but `compile_outcome_quality.outcome_id` is a BIGINT FK →
79
+ * `compile_outcomes(id)`. A verbatim forward would 400 every handle-shaped
80
+ * verdict (tt-intel + IC convergent finding, 2026-07-06). On the
81
+ * `compile_outcome_quality` segment the factory resolves non-numeric
82
+ * `outcome_id` values to their BIGINT row ids via
83
+ * `compile_outcomes?handle=eq.<v>` (latest row wins) before forwarding.
84
+ * Numeric ids pass through untouched; an unresolvable handle returns
85
+ * `404 handle_not_found` so the library's route-404 observability names the
86
+ * failure instead of the brain's opaque FK error.
61
87
  */
62
88
  interface BrainForwardConfig {
63
89
  /** Bearer token consumers' callers must present (usually KGAUTO_INGEST_SECRET). */
@@ -58,6 +58,32 @@
58
58
  * must be the base that resolves to `.../api/kgauto/v2` — i.e. the last path
59
59
  * segment of each library POST (`outcomes`, `probe_outcomes`, …) is what
60
60
  * `handle()` receives as `segment`.
61
+ *
62
+ * ## Prefer-header relay (alpha.56)
63
+ *
64
+ * `record()` sends `Prefer: return=representation` on its `/outcomes` POST and
65
+ * parses the inserted row id from the PROXY's response to fire the secondary
66
+ * `compile_outcome_advisories` POST. alpha.54/.55 hardcoded
67
+ * `Prefer: return=minimal` toward the brain and returned `{ok:true}` — so the
68
+ * id-parse found nothing and the advisory secondary silently never fired for
69
+ * factory-mounted consumers (PB + GE convergent finding, 2026-07-06). The
70
+ * factory now honors a caller `Prefer: return=representation` header: it is
71
+ * forwarded to the brain and the brain's response body (the inserted row(s))
72
+ * is relayed verbatim on success. Callers that don't send the header keep the
73
+ * `return=minimal` + `{ok:true}` behavior.
74
+ *
75
+ * ## Quality-segment handle resolution (alpha.55)
76
+ *
77
+ * `recordOutcome()` sends `outcome_id` as `CompileResult.handle` (a string),
78
+ * but `compile_outcome_quality.outcome_id` is a BIGINT FK →
79
+ * `compile_outcomes(id)`. A verbatim forward would 400 every handle-shaped
80
+ * verdict (tt-intel + IC convergent finding, 2026-07-06). On the
81
+ * `compile_outcome_quality` segment the factory resolves non-numeric
82
+ * `outcome_id` values to their BIGINT row ids via
83
+ * `compile_outcomes?handle=eq.<v>` (latest row wins) before forwarding.
84
+ * Numeric ids pass through untouched; an unresolvable handle returns
85
+ * `404 handle_not_found` so the library's route-404 observability names the
86
+ * failure instead of the brain's opaque FK error.
61
87
  */
62
88
  interface BrainForwardConfig {
63
89
  /** Bearer token consumers' callers must present (usually KGAUTO_INGEST_SECRET). */
@@ -45,11 +45,65 @@ function requireString(name, value) {
45
45
  }
46
46
  return value;
47
47
  }
48
+ function isNumericOutcomeId(value) {
49
+ return typeof value === "number" || typeof value === "string" && /^\d+$/.test(value);
50
+ }
48
51
  function createBrainForwardRoutes(config) {
49
52
  const ingestSecret = requireString("ingestSecret", config.ingestSecret);
50
53
  const brainUrl = requireString("brainUrl", config.brainUrl).replace(/\/+$/, "");
51
54
  const serviceKey = requireString("serviceKey", config.serviceKey);
52
55
  const fetchFn = config.fetchImpl ?? fetch;
56
+ async function resolveQualityOutcomeIds(body) {
57
+ const rows = Array.isArray(body) ? body : [body];
58
+ const handles = [
59
+ ...new Set(
60
+ rows.map((r) => r?.outcome_id).filter(
61
+ (v) => typeof v === "string" && !isNumericOutcomeId(v)
62
+ )
63
+ )
64
+ ];
65
+ if (handles.length === 0) return body;
66
+ const list = handles.map((h) => `"${h.replace(/"/g, "")}"`).join(",");
67
+ const url = `${brainUrl}/rest/v1/compile_outcomes?select=id,handle&handle=in.(${encodeURIComponent(list)})&order=id.desc`;
68
+ let idByHandle;
69
+ try {
70
+ const res = await fetchFn(url, {
71
+ headers: { apikey: serviceKey, Authorization: `Bearer ${serviceKey}` }
72
+ });
73
+ if (!res.ok) {
74
+ const text = await res.text().catch(() => "<no body>");
75
+ return jsonResponse(502, {
76
+ error: "handle-resolution-failed",
77
+ table: "compile_outcome_quality",
78
+ status: res.status,
79
+ detail: text
80
+ });
81
+ }
82
+ const found = await res.json();
83
+ idByHandle = /* @__PURE__ */ new Map();
84
+ for (const r of found) {
85
+ if (!idByHandle.has(r.handle)) idByHandle.set(r.handle, r.id);
86
+ }
87
+ } catch (err) {
88
+ return jsonResponse(502, {
89
+ error: "handle-resolution-failed",
90
+ table: "compile_outcome_quality",
91
+ detail: err instanceof Error ? err.message : String(err)
92
+ });
93
+ }
94
+ const unresolved = handles.filter((h) => !idByHandle.has(h));
95
+ if (unresolved.length > 0) {
96
+ return jsonResponse(404, {
97
+ error: "handle_not_found",
98
+ table: "compile_outcome_quality",
99
+ unresolved
100
+ });
101
+ }
102
+ const rewritten = rows.map(
103
+ (r) => typeof r?.outcome_id === "string" && idByHandle.has(r.outcome_id) ? { ...r, outcome_id: idByHandle.get(r.outcome_id) } : r
104
+ );
105
+ return Array.isArray(body) ? rewritten : rewritten[0];
106
+ }
53
107
  async function handle(req, segment) {
54
108
  try {
55
109
  if (req.method !== "POST") {
@@ -76,7 +130,15 @@ function createBrainForwardRoutes(config) {
76
130
  } catch {
77
131
  return jsonResponse(400, { error: "invalid-json" });
78
132
  }
79
- const row = Array.isArray(body) ? body : { ...body };
133
+ let row = Array.isArray(body) ? body : { ...body };
134
+ if (table === "compile_outcome_quality") {
135
+ const outcome = await resolveQualityOutcomeIds(row);
136
+ if (outcome instanceof Response) return outcome;
137
+ row = outcome;
138
+ }
139
+ const wantsRepresentation = /return=representation/i.test(
140
+ req.headers.get("Prefer") ?? ""
141
+ );
80
142
  let brainRes;
81
143
  try {
82
144
  brainRes = await fetchFn(`${brainUrl}/rest/v1/${table}`, {
@@ -85,7 +147,7 @@ function createBrainForwardRoutes(config) {
85
147
  apikey: serviceKey,
86
148
  Authorization: `Bearer ${serviceKey}`,
87
149
  "Content-Type": "application/json",
88
- Prefer: "return=minimal"
150
+ Prefer: wantsRepresentation ? "return=representation" : "return=minimal"
89
151
  },
90
152
  body: JSON.stringify(row)
91
153
  });
@@ -97,6 +159,12 @@ function createBrainForwardRoutes(config) {
97
159
  });
98
160
  }
99
161
  if (brainRes.ok) {
162
+ if (wantsRepresentation) {
163
+ const text2 = await brainRes.text().catch(() => "");
164
+ if (text2.length > 0) {
165
+ return new Response(text2, { status: 201, headers: JSON_HEADERS });
166
+ }
167
+ }
100
168
  return jsonResponse(201, { ok: true });
101
169
  }
102
170
  const text = await brainRes.text().catch(() => "<no body>");
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createBrainForwardRoutes
3
- } from "./chunk-NGPB3D53.mjs";
3
+ } from "./chunk-IUWFML6Z.mjs";
4
4
  export {
5
5
  createBrainForwardRoutes
6
6
  };
@@ -21,11 +21,65 @@ function requireString(name, value) {
21
21
  }
22
22
  return value;
23
23
  }
24
+ function isNumericOutcomeId(value) {
25
+ return typeof value === "number" || typeof value === "string" && /^\d+$/.test(value);
26
+ }
24
27
  function createBrainForwardRoutes(config) {
25
28
  const ingestSecret = requireString("ingestSecret", config.ingestSecret);
26
29
  const brainUrl = requireString("brainUrl", config.brainUrl).replace(/\/+$/, "");
27
30
  const serviceKey = requireString("serviceKey", config.serviceKey);
28
31
  const fetchFn = config.fetchImpl ?? fetch;
32
+ async function resolveQualityOutcomeIds(body) {
33
+ const rows = Array.isArray(body) ? body : [body];
34
+ const handles = [
35
+ ...new Set(
36
+ rows.map((r) => r?.outcome_id).filter(
37
+ (v) => typeof v === "string" && !isNumericOutcomeId(v)
38
+ )
39
+ )
40
+ ];
41
+ if (handles.length === 0) return body;
42
+ const list = handles.map((h) => `"${h.replace(/"/g, "")}"`).join(",");
43
+ const url = `${brainUrl}/rest/v1/compile_outcomes?select=id,handle&handle=in.(${encodeURIComponent(list)})&order=id.desc`;
44
+ let idByHandle;
45
+ try {
46
+ const res = await fetchFn(url, {
47
+ headers: { apikey: serviceKey, Authorization: `Bearer ${serviceKey}` }
48
+ });
49
+ if (!res.ok) {
50
+ const text = await res.text().catch(() => "<no body>");
51
+ return jsonResponse(502, {
52
+ error: "handle-resolution-failed",
53
+ table: "compile_outcome_quality",
54
+ status: res.status,
55
+ detail: text
56
+ });
57
+ }
58
+ const found = await res.json();
59
+ idByHandle = /* @__PURE__ */ new Map();
60
+ for (const r of found) {
61
+ if (!idByHandle.has(r.handle)) idByHandle.set(r.handle, r.id);
62
+ }
63
+ } catch (err) {
64
+ return jsonResponse(502, {
65
+ error: "handle-resolution-failed",
66
+ table: "compile_outcome_quality",
67
+ detail: err instanceof Error ? err.message : String(err)
68
+ });
69
+ }
70
+ const unresolved = handles.filter((h) => !idByHandle.has(h));
71
+ if (unresolved.length > 0) {
72
+ return jsonResponse(404, {
73
+ error: "handle_not_found",
74
+ table: "compile_outcome_quality",
75
+ unresolved
76
+ });
77
+ }
78
+ const rewritten = rows.map(
79
+ (r) => typeof r?.outcome_id === "string" && idByHandle.has(r.outcome_id) ? { ...r, outcome_id: idByHandle.get(r.outcome_id) } : r
80
+ );
81
+ return Array.isArray(body) ? rewritten : rewritten[0];
82
+ }
29
83
  async function handle(req, segment) {
30
84
  try {
31
85
  if (req.method !== "POST") {
@@ -52,7 +106,15 @@ function createBrainForwardRoutes(config) {
52
106
  } catch {
53
107
  return jsonResponse(400, { error: "invalid-json" });
54
108
  }
55
- const row = Array.isArray(body) ? body : { ...body };
109
+ let row = Array.isArray(body) ? body : { ...body };
110
+ if (table === "compile_outcome_quality") {
111
+ const outcome = await resolveQualityOutcomeIds(row);
112
+ if (outcome instanceof Response) return outcome;
113
+ row = outcome;
114
+ }
115
+ const wantsRepresentation = /return=representation/i.test(
116
+ req.headers.get("Prefer") ?? ""
117
+ );
56
118
  let brainRes;
57
119
  try {
58
120
  brainRes = await fetchFn(`${brainUrl}/rest/v1/${table}`, {
@@ -61,7 +123,7 @@ function createBrainForwardRoutes(config) {
61
123
  apikey: serviceKey,
62
124
  Authorization: `Bearer ${serviceKey}`,
63
125
  "Content-Type": "application/json",
64
- Prefer: "return=minimal"
126
+ Prefer: wantsRepresentation ? "return=representation" : "return=minimal"
65
127
  },
66
128
  body: JSON.stringify(row)
67
129
  });
@@ -73,6 +135,12 @@ function createBrainForwardRoutes(config) {
73
135
  });
74
136
  }
75
137
  if (brainRes.ok) {
138
+ if (wantsRepresentation) {
139
+ const text2 = await brainRes.text().catch(() => "");
140
+ if (text2.length > 0) {
141
+ return new Response(text2, { status: 201, headers: JSON_HEADERS });
142
+ }
143
+ }
76
144
  return jsonResponse(201, { ok: true });
77
145
  }
78
146
  const text = await brainRes.text().catch(() => "<no body>");
package/dist/index.d.mts CHANGED
@@ -317,7 +317,10 @@ interface CompileForAISDKv6Result {
317
317
  /**
318
318
  * Index (into post-strip history) for the Anthropic history cache marker
319
319
  * (alpha.33). Mirror of `result.diagnostics.historyCacheMarkIndex` — no
320
- * consumer re-derivation needed. Undefined when no marker fires.
320
+ * consumer re-derivation needed. Undefined when no marker fires, and (since
321
+ * alpha.55) always undefined for non-Anthropic targets: the marker is an
322
+ * Anthropic wire concept, other providers cache prefixes implicitly. The
323
+ * ungated value remains on `diagnostics.historyCacheMarkIndex`.
321
324
  */
322
325
  historyCacheMarkIndex?: number;
323
326
  /** Compile handle — pass to `record()` after the call. */
package/dist/index.d.ts CHANGED
@@ -317,7 +317,10 @@ interface CompileForAISDKv6Result {
317
317
  /**
318
318
  * Index (into post-strip history) for the Anthropic history cache marker
319
319
  * (alpha.33). Mirror of `result.diagnostics.historyCacheMarkIndex` — no
320
- * consumer re-derivation needed. Undefined when no marker fires.
320
+ * consumer re-derivation needed. Undefined when no marker fires, and (since
321
+ * alpha.55) always undefined for non-Anthropic targets: the marker is an
322
+ * Anthropic wire concept, other providers cache prefixes implicitly. The
323
+ * ungated value remains on `diagnostics.historyCacheMarkIndex`.
321
324
  */
322
325
  historyCacheMarkIndex?: number;
323
326
  /** Compile handle — pass to `record()` after the call. */
package/dist/index.js CHANGED
@@ -6734,7 +6734,12 @@ function compileForAISDKv6(ir, opts) {
6734
6734
  },
6735
6735
  keptToolNames,
6736
6736
  ...providerOptions ? { providerOptions } : {},
6737
- ...result.diagnostics.historyCacheMarkIndex !== void 0 ? { historyCacheMarkIndex: result.diagnostics.historyCacheMarkIndex } : {},
6737
+ // Provider-gated (alpha.55, IC finding): the marker is an Anthropic
6738
+ // concept — Gemini/OpenAI/DeepSeek cache prefixes implicitly, and a
6739
+ // consumer attaching Anthropic providerOptions at this index on a
6740
+ // non-Anthropic call would ship a no-op-at-best marker. The ungated value
6741
+ // stays on diagnostics.historyCacheMarkIndex for observability.
6742
+ ...result.provider === "anthropic" && result.diagnostics.historyCacheMarkIndex !== void 0 ? { historyCacheMarkIndex: result.diagnostics.historyCacheMarkIndex } : {},
6738
6743
  mutationsApplied: result.mutationsApplied.map((m) => m.id),
6739
6744
  advisories: result.advisories ?? [],
6740
6745
  diagnostics,
@@ -6785,11 +6790,65 @@ function requireString(name, value) {
6785
6790
  }
6786
6791
  return value;
6787
6792
  }
6793
+ function isNumericOutcomeId(value) {
6794
+ return typeof value === "number" || typeof value === "string" && /^\d+$/.test(value);
6795
+ }
6788
6796
  function createBrainForwardRoutes(config) {
6789
6797
  const ingestSecret = requireString("ingestSecret", config.ingestSecret);
6790
6798
  const brainUrl = requireString("brainUrl", config.brainUrl).replace(/\/+$/, "");
6791
6799
  const serviceKey = requireString("serviceKey", config.serviceKey);
6792
6800
  const fetchFn = config.fetchImpl ?? fetch;
6801
+ async function resolveQualityOutcomeIds(body) {
6802
+ const rows = Array.isArray(body) ? body : [body];
6803
+ const handles = [
6804
+ ...new Set(
6805
+ rows.map((r) => r?.outcome_id).filter(
6806
+ (v) => typeof v === "string" && !isNumericOutcomeId(v)
6807
+ )
6808
+ )
6809
+ ];
6810
+ if (handles.length === 0) return body;
6811
+ const list = handles.map((h) => `"${h.replace(/"/g, "")}"`).join(",");
6812
+ const url = `${brainUrl}/rest/v1/compile_outcomes?select=id,handle&handle=in.(${encodeURIComponent(list)})&order=id.desc`;
6813
+ let idByHandle;
6814
+ try {
6815
+ const res = await fetchFn(url, {
6816
+ headers: { apikey: serviceKey, Authorization: `Bearer ${serviceKey}` }
6817
+ });
6818
+ if (!res.ok) {
6819
+ const text = await res.text().catch(() => "<no body>");
6820
+ return jsonResponse(502, {
6821
+ error: "handle-resolution-failed",
6822
+ table: "compile_outcome_quality",
6823
+ status: res.status,
6824
+ detail: text
6825
+ });
6826
+ }
6827
+ const found = await res.json();
6828
+ idByHandle = /* @__PURE__ */ new Map();
6829
+ for (const r of found) {
6830
+ if (!idByHandle.has(r.handle)) idByHandle.set(r.handle, r.id);
6831
+ }
6832
+ } catch (err) {
6833
+ return jsonResponse(502, {
6834
+ error: "handle-resolution-failed",
6835
+ table: "compile_outcome_quality",
6836
+ detail: err instanceof Error ? err.message : String(err)
6837
+ });
6838
+ }
6839
+ const unresolved = handles.filter((h) => !idByHandle.has(h));
6840
+ if (unresolved.length > 0) {
6841
+ return jsonResponse(404, {
6842
+ error: "handle_not_found",
6843
+ table: "compile_outcome_quality",
6844
+ unresolved
6845
+ });
6846
+ }
6847
+ const rewritten = rows.map(
6848
+ (r) => typeof r?.outcome_id === "string" && idByHandle.has(r.outcome_id) ? { ...r, outcome_id: idByHandle.get(r.outcome_id) } : r
6849
+ );
6850
+ return Array.isArray(body) ? rewritten : rewritten[0];
6851
+ }
6793
6852
  async function handle(req, segment) {
6794
6853
  try {
6795
6854
  if (req.method !== "POST") {
@@ -6816,7 +6875,15 @@ function createBrainForwardRoutes(config) {
6816
6875
  } catch {
6817
6876
  return jsonResponse(400, { error: "invalid-json" });
6818
6877
  }
6819
- const row = Array.isArray(body) ? body : { ...body };
6878
+ let row = Array.isArray(body) ? body : { ...body };
6879
+ if (table === "compile_outcome_quality") {
6880
+ const outcome = await resolveQualityOutcomeIds(row);
6881
+ if (outcome instanceof Response) return outcome;
6882
+ row = outcome;
6883
+ }
6884
+ const wantsRepresentation = /return=representation/i.test(
6885
+ req.headers.get("Prefer") ?? ""
6886
+ );
6820
6887
  let brainRes;
6821
6888
  try {
6822
6889
  brainRes = await fetchFn(`${brainUrl}/rest/v1/${table}`, {
@@ -6825,7 +6892,7 @@ function createBrainForwardRoutes(config) {
6825
6892
  apikey: serviceKey,
6826
6893
  Authorization: `Bearer ${serviceKey}`,
6827
6894
  "Content-Type": "application/json",
6828
- Prefer: "return=minimal"
6895
+ Prefer: wantsRepresentation ? "return=representation" : "return=minimal"
6829
6896
  },
6830
6897
  body: JSON.stringify(row)
6831
6898
  });
@@ -6837,6 +6904,12 @@ function createBrainForwardRoutes(config) {
6837
6904
  });
6838
6905
  }
6839
6906
  if (brainRes.ok) {
6907
+ if (wantsRepresentation) {
6908
+ const text2 = await brainRes.text().catch(() => "");
6909
+ if (text2.length > 0) {
6910
+ return new Response(text2, { status: 201, headers: JSON_HEADERS });
6911
+ }
6912
+ }
6840
6913
  return jsonResponse(201, { ok: true });
6841
6914
  }
6842
6915
  const text = await brainRes.text().catch(() => "<no body>");
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createBrainForwardRoutes
3
- } from "./chunk-NGPB3D53.mjs";
3
+ } from "./chunk-IUWFML6Z.mjs";
4
4
  import {
5
5
  ALL_ARCHETYPES,
6
6
  DIALECT_VERSION,
@@ -4175,7 +4175,12 @@ function compileForAISDKv6(ir, opts) {
4175
4175
  },
4176
4176
  keptToolNames,
4177
4177
  ...providerOptions ? { providerOptions } : {},
4178
- ...result.diagnostics.historyCacheMarkIndex !== void 0 ? { historyCacheMarkIndex: result.diagnostics.historyCacheMarkIndex } : {},
4178
+ // Provider-gated (alpha.55, IC finding): the marker is an Anthropic
4179
+ // concept — Gemini/OpenAI/DeepSeek cache prefixes implicitly, and a
4180
+ // consumer attaching Anthropic providerOptions at this index on a
4181
+ // non-Anthropic call would ship a no-op-at-best marker. The ungated value
4182
+ // stays on diagnostics.historyCacheMarkIndex for observability.
4183
+ ...result.provider === "anthropic" && result.diagnostics.historyCacheMarkIndex !== void 0 ? { historyCacheMarkIndex: result.diagnostics.historyCacheMarkIndex } : {},
4179
4184
  mutationsApplied: result.mutationsApplied.map((m) => m.id),
4180
4185
  advisories: result.advisories ?? [],
4181
4186
  diagnostics,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warmdrift/kgauto-compiler",
3
- "version": "2.0.0-alpha.54",
3
+ "version": "2.0.0-alpha.56",
4
4
  "description": "Prompt compiler + central learning brain for multi-model AI apps. Swap models without rewriting prompts.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",