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

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,19 @@
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
+ * ## Quality-segment handle resolution (alpha.55)
63
+ *
64
+ * `recordOutcome()` sends `outcome_id` as `CompileResult.handle` (a string),
65
+ * but `compile_outcome_quality.outcome_id` is a BIGINT FK →
66
+ * `compile_outcomes(id)`. A verbatim forward would 400 every handle-shaped
67
+ * verdict (tt-intel + IC convergent finding, 2026-07-06). On the
68
+ * `compile_outcome_quality` segment the factory resolves non-numeric
69
+ * `outcome_id` values to their BIGINT row ids via
70
+ * `compile_outcomes?handle=eq.<v>` (latest row wins) before forwarding.
71
+ * Numeric ids pass through untouched; an unresolvable handle returns
72
+ * `404 handle_not_found` so the library's route-404 observability names the
73
+ * failure instead of the brain's opaque FK error.
61
74
  */
62
75
  interface BrainForwardConfig {
63
76
  /** Bearer token consumers' callers must present (usually KGAUTO_INGEST_SECRET). */
@@ -58,6 +58,19 @@
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
+ * ## Quality-segment handle resolution (alpha.55)
63
+ *
64
+ * `recordOutcome()` sends `outcome_id` as `CompileResult.handle` (a string),
65
+ * but `compile_outcome_quality.outcome_id` is a BIGINT FK →
66
+ * `compile_outcomes(id)`. A verbatim forward would 400 every handle-shaped
67
+ * verdict (tt-intel + IC convergent finding, 2026-07-06). On the
68
+ * `compile_outcome_quality` segment the factory resolves non-numeric
69
+ * `outcome_id` values to their BIGINT row ids via
70
+ * `compile_outcomes?handle=eq.<v>` (latest row wins) before forwarding.
71
+ * Numeric ids pass through untouched; an unresolvable handle returns
72
+ * `404 handle_not_found` so the library's route-404 observability names the
73
+ * failure instead of the brain's opaque FK error.
61
74
  */
62
75
  interface BrainForwardConfig {
63
76
  /** 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,12 @@ 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
+ }
80
139
  let brainRes;
81
140
  try {
82
141
  brainRes = await fetchFn(`${brainUrl}/rest/v1/${table}`, {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createBrainForwardRoutes
3
- } from "./chunk-NGPB3D53.mjs";
3
+ } from "./chunk-WVICNOLA.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,12 @@ 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
+ }
56
115
  let brainRes;
57
116
  try {
58
117
  brainRes = await fetchFn(`${brainUrl}/rest/v1/${table}`, {
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,12 @@ 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
+ }
6820
6884
  let brainRes;
6821
6885
  try {
6822
6886
  brainRes = await fetchFn(`${brainUrl}/rest/v1/${table}`, {
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createBrainForwardRoutes
3
- } from "./chunk-NGPB3D53.mjs";
3
+ } from "./chunk-WVICNOLA.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.55",
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",