@warmdrift/kgauto-compiler 2.0.0-alpha.28 → 2.0.0-alpha.29

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/index.mjs CHANGED
@@ -475,6 +475,7 @@ function lowerAnthropic(ir, profile, hints) {
475
475
  const historyCacheableTokens = markIndex >= 0 ? sumHistoryTokens(history, markIndex) : 0;
476
476
  const totalCacheableTokens = cacheableTokens + historyCacheableTokens;
477
477
  const cacheSavings = totalCacheableTokens / 1e6 * profile.costInputPer1m * (1 - (profile.lowering.cache.discount ?? 0.1));
478
+ const toolChoice = hints.wireOverrides?.parallelToolCalls === false && tools && tools.length > 0 ? { type: "auto", disable_parallel_tool_use: true } : void 0;
478
479
  return {
479
480
  request: {
480
481
  provider: "anthropic",
@@ -486,7 +487,8 @@ function lowerAnthropic(ir, profile, hints) {
486
487
  // floor surprised every consumer once (PB-Cairn contract-gaps brief, Gap 3).
487
488
  // Profile is the single source of truth; consumers wanting a tighter
488
489
  // budget can pass providerOverrides.anthropic.max_tokens explicitly.
489
- max_tokens: hints.forceTerseOutput ? 200 : profile.maxOutputTokens
490
+ max_tokens: hints.forceTerseOutput ? 200 : profile.maxOutputTokens,
491
+ tool_choice: toolChoice
490
492
  },
491
493
  diagnostics: {
492
494
  cacheableTokens,
@@ -680,6 +682,7 @@ function lowerOpenAI(ir, profile, hints) {
680
682
  const history = (ir.history ?? []).filter((m) => m.role !== "system");
681
683
  const histMarkIndex = resolveHistoryMarkIndex(history.length, ir.historyCachePolicy);
682
684
  const historyCacheableTokens = histMarkIndex >= 0 ? sumHistoryTokens(history, histMarkIndex) : 0;
685
+ const openaiParallelToolCalls = hints.wireOverrides?.parallelToolCalls === false && ir.tools && ir.tools.length > 0 ? false : void 0;
683
686
  return {
684
687
  request: {
685
688
  provider: "openai",
@@ -687,7 +690,8 @@ function lowerOpenAI(ir, profile, hints) {
687
690
  messages,
688
691
  tools: ir.tools && ir.tools.length > 0 ? toOpenAITools(ir.tools) : void 0,
689
692
  response_format: ir.constraints?.structuredOutput ? { type: "json_object" } : void 0,
690
- reasoning_effort: hints.forceTerseOutput ? "low" : void 0
693
+ reasoning_effort: hints.forceTerseOutput ? "low" : void 0,
694
+ parallel_tool_calls: openaiParallelToolCalls
691
695
  },
692
696
  diagnostics: {
693
697
  cacheableTokens: 0,
@@ -837,9 +841,19 @@ function runAdvisor(ir, result, profile, policy, phase2) {
837
841
  out.push(...detectModelStaleEvidence(ir, profile));
838
842
  out.push(...detectTierDown(ir, profile, phase2));
839
843
  }
840
- out.push(...detectArchetypePerfFloorBreach(ir, profile));
844
+ if (!translatorClearedToolCallCliff(phase2)) {
845
+ out.push(...detectArchetypePerfFloorBreach(ir, profile));
846
+ }
841
847
  return out;
842
848
  }
849
+ function translatorClearedToolCallCliff(phase2) {
850
+ const rewrites = phase2?.sectionRewritesApplied;
851
+ if (!rewrites || rewrites.length === 0) return false;
852
+ for (const rw of rewrites) {
853
+ if (rw.kind === "tool_call_contract") return true;
854
+ }
855
+ return false;
856
+ }
843
857
  function detectCachingOff(ir, profile) {
844
858
  if (profile.provider !== "anthropic") return [];
845
859
  const totalChars = ir.sections.reduce((s, sec) => s + sec.text.length, 0);
@@ -1039,6 +1053,47 @@ function detectArchetypePerfFloorBreach(ir, profile) {
1039
1053
  ];
1040
1054
  }
1041
1055
 
1056
+ // src/translator.ts
1057
+ var TRANSLATOR_FLOOR = ARCHETYPE_FLOOR_DEFAULT;
1058
+ var RULE_SEQUENTIAL_TOOL_CLIFF = "sequential-tool-cliff-below-floor";
1059
+ var SEQUENTIAL_TOOL_PREAMBLE = "IMPORTANT: Use one tool call per response. Wait for the tool result before deciding the next tool. Do NOT batch tool calls in parallel.";
1060
+ function applySectionRewrites(args) {
1061
+ const { ir, profile, archetype } = args;
1062
+ if (!Array.isArray(ir.sections) || ir.sections.length === 0) {
1063
+ return { rewrittenIR: ir, rewrites: [] };
1064
+ }
1065
+ if (!profile.archetypePerf) {
1066
+ return { rewrittenIR: ir, rewrites: [] };
1067
+ }
1068
+ const archetypeScore = profile.archetypePerf[archetype];
1069
+ const cliffFires = typeof archetypeScore === "number" && archetypeScore < TRANSLATOR_FLOOR;
1070
+ if (!cliffFires) {
1071
+ return { rewrittenIR: ir, rewrites: [] };
1072
+ }
1073
+ const rewrites = [];
1074
+ const newSections = ir.sections.map((section) => {
1075
+ if (section.kind !== "tool_call_contract") return section;
1076
+ const originalText = section.text;
1077
+ const transformedText = `${SEQUENTIAL_TOOL_PREAMBLE}
1078
+
1079
+ ${originalText}`;
1080
+ rewrites.push({
1081
+ sectionId: section.id,
1082
+ kind: "tool_call_contract",
1083
+ rule: RULE_SEQUENTIAL_TOOL_CLIFF,
1084
+ originalText,
1085
+ transformedText,
1086
+ wireOverrides: { parallelToolCalls: false }
1087
+ });
1088
+ return { ...section, text: transformedText };
1089
+ });
1090
+ if (rewrites.length === 0) {
1091
+ return { rewrittenIR: ir, rewrites: [] };
1092
+ }
1093
+ const rewrittenIR = { ...ir, sections: newSections };
1094
+ return { rewrittenIR, rewrites };
1095
+ }
1096
+
1042
1097
  // src/compile.ts
1043
1098
  var counter = 0;
1044
1099
  function makeHandle() {
@@ -1082,9 +1137,33 @@ function compile(ir, opts = {}) {
1082
1137
  const cliffs = passApplyCliffs(workingIR, profile, inputTokens);
1083
1138
  workingIR = cliffs.value.ir;
1084
1139
  accumulatedMutations.push(...cliffs.mutations);
1140
+ const translated = applySectionRewrites({
1141
+ ir: workingIR,
1142
+ profile,
1143
+ archetype: ir.intent.archetype
1144
+ });
1145
+ workingIR = translated.rewrittenIR;
1146
+ const sectionRewritesApplied = translated.rewrites;
1147
+ let wireOverrides;
1148
+ for (const rw of sectionRewritesApplied) {
1149
+ if (!rw.wireOverrides) continue;
1150
+ if (!wireOverrides) wireOverrides = {};
1151
+ if (rw.wireOverrides.parallelToolCalls !== void 0) {
1152
+ wireOverrides.parallelToolCalls = rw.wireOverrides.parallelToolCalls;
1153
+ }
1154
+ }
1155
+ for (const rw of sectionRewritesApplied) {
1156
+ accumulatedMutations.push({
1157
+ id: `translator:${rw.rule}:${rw.sectionId}`,
1158
+ source: "translator",
1159
+ passName: "translator",
1160
+ description: `Rewrote section "${rw.sectionId}" (kind=${rw.kind}) via rule "${rw.rule}".`
1161
+ });
1162
+ }
1085
1163
  const lowered = lower(workingIR, profile, {
1086
1164
  forceThinkingZero: cliffs.value.loweringHints.forceThinkingZero,
1087
- forceTerseOutput: cliffs.value.loweringHints.forceTerseOutput
1165
+ forceTerseOutput: cliffs.value.loweringHints.forceTerseOutput,
1166
+ wireOverrides
1088
1167
  });
1089
1168
  validateFinalFit(workingIR, profile, inputTokens);
1090
1169
  const handle = makeHandle();
@@ -1132,7 +1211,13 @@ function compile(ir, opts = {}) {
1132
1211
  opts.policy,
1133
1212
  {
1134
1213
  fallbackChain,
1135
- profileResolver: phase2ProfileResolver
1214
+ profileResolver: phase2ProfileResolver,
1215
+ // alpha.29 — feed translator rewrites to the advisor so the
1216
+ // `archetype-perf-floor-breach` rule can suppress when the translator
1217
+ // already cleared the cliff for the same archetype. Without this,
1218
+ // both the rewrite AND the advisory fire — noisy, and the advisory
1219
+ // would mislead consumers into thinking the cliff is unaddressed.
1220
+ sectionRewritesApplied
1136
1221
  }
1137
1222
  );
1138
1223
  return {
@@ -1145,7 +1230,9 @@ function compile(ir, opts = {}) {
1145
1230
  mutationsApplied: accumulatedMutations,
1146
1231
  fallbackChain,
1147
1232
  advisories,
1148
- diagnostics
1233
+ diagnostics,
1234
+ sectionRewritesApplied,
1235
+ wireOverrides
1149
1236
  };
1150
1237
  }
1151
1238
  function validateIR(ir) {
@@ -1318,7 +1405,11 @@ function registerCompile(appId, archetype, ir, result) {
1318
1405
  // alpha.28: shape fields for Glass-Box renderer.
1319
1406
  toolsCount,
1320
1407
  historyDepth,
1321
- systemPromptChars
1408
+ systemPromptChars,
1409
+ // alpha.29: translator activity — persisted on the brain row so
1410
+ // cross-app aggregates can answer "Sonnet narration rule fired N times,
1411
+ // outcome quality lifted to M."
1412
+ sectionRewritesApplied: result.sectionRewritesApplied
1322
1413
  });
1323
1414
  }
1324
1415
  function estimateSystemPromptChars(sections) {
@@ -1454,7 +1545,11 @@ function buildPayload(input, reg) {
1454
1545
  history_depth: input.historyDepth ?? reg?.historyDepth,
1455
1546
  system_prompt_chars: input.systemPromptChars ?? reg?.systemPromptChars,
1456
1547
  fell_over_from: fellOverFrom,
1457
- fallback_reason: fallbackReason
1548
+ fallback_reason: fallbackReason,
1549
+ // alpha.29 — translator activity (migration 019). Send NULL when no
1550
+ // rewrites fired so the brain's "did the translator do anything?"
1551
+ // queries can use `IS NOT NULL` cleanly.
1552
+ section_rewrites_applied: reg?.sectionRewritesApplied && reg.sectionRewritesApplied.length > 0 ? reg.sectionRewritesApplied : null
1458
1553
  };
1459
1554
  }
1460
1555
  function computeCostUsd(modelId, tokensIn, tokensOut) {
@@ -2209,6 +2304,278 @@ function clamp(n) {
2209
2304
  return Math.max(0, Math.min(1, n));
2210
2305
  }
2211
2306
 
2307
+ // src/advisories-api.ts
2308
+ var SEVERITY_SET = /* @__PURE__ */ new Set(["info", "warn", "critical"]);
2309
+ var STATUS_SET = /* @__PURE__ */ new Set(["open", "snoozed", "resolved"]);
2310
+ var RESOLUTION_SOURCE_SET = /* @__PURE__ */ new Set([
2311
+ "auto",
2312
+ "consumer-marked",
2313
+ "declined"
2314
+ ]);
2315
+ function asString(v) {
2316
+ return typeof v === "string" && v.length > 0 ? v : void 0;
2317
+ }
2318
+ function asSeverity(v) {
2319
+ if (typeof v === "string" && SEVERITY_SET.has(v)) {
2320
+ return v;
2321
+ }
2322
+ return "info";
2323
+ }
2324
+ function asStatus(v) {
2325
+ if (typeof v === "string" && STATUS_SET.has(v)) {
2326
+ return v;
2327
+ }
2328
+ return "open";
2329
+ }
2330
+ function asResolutionSource(v) {
2331
+ if (typeof v === "string" && RESOLUTION_SOURCE_SET.has(v)) {
2332
+ return v;
2333
+ }
2334
+ return void 0;
2335
+ }
2336
+ function rowToAdvisory(row) {
2337
+ const archetype = asString(row.applies_to_archetype);
2338
+ const model = asString(row.applies_to_model);
2339
+ const docsLink = asString(row.docs_url);
2340
+ const suggestion = asString(row.suggestion);
2341
+ let suggestedFix = null;
2342
+ if (docsLink || suggestion) {
2343
+ suggestedFix = { type: "manual" };
2344
+ if (docsLink) suggestedFix.docsLink = docsLink;
2345
+ if (suggestion) suggestedFix.before = suggestion;
2346
+ }
2347
+ const out = {
2348
+ id: typeof row.id === "string" ? row.id : "",
2349
+ rule: typeof row.rule === "string" ? row.rule : "",
2350
+ severity: asSeverity(row.severity),
2351
+ openedAt: typeof row.opened_at === "string" ? row.opened_at : "",
2352
+ lastObservedAt: typeof row.last_observed_at === "string" ? row.last_observed_at : "",
2353
+ observationCount: typeof row.observation_count === "number" ? row.observation_count : 0,
2354
+ appliesTo: {
2355
+ ...archetype ? { archetype } : {},
2356
+ ...model ? { model } : {}
2357
+ },
2358
+ message: typeof row.message === "string" ? row.message : "",
2359
+ suggestedFix,
2360
+ autoApplicable: false,
2361
+ // reserved — alpha.30+
2362
+ status: asStatus(row.status)
2363
+ };
2364
+ const resolvedAt = asString(row.resolved_at);
2365
+ if (resolvedAt) out.resolvedAt = resolvedAt;
2366
+ const resolutionSource = asResolutionSource(row.resolution_source);
2367
+ if (resolutionSource) out.resolutionSource = resolutionSource;
2368
+ const resolutionNote = asString(row.resolution_note);
2369
+ if (resolutionNote) out.resolutionNote = resolutionNote;
2370
+ return out;
2371
+ }
2372
+ function resolveFetch(injected) {
2373
+ return injected ?? ((...args) => globalThis.fetch(...args));
2374
+ }
2375
+ function normalizeEndpoint(endpoint) {
2376
+ return endpoint.replace(/\/+$/, "");
2377
+ }
2378
+ async function getActionableAdvisories(opts) {
2379
+ const {
2380
+ appId,
2381
+ severity,
2382
+ status,
2383
+ brainEndpoint,
2384
+ brainJwt,
2385
+ brainAnonKey,
2386
+ fetch: injectedFetch
2387
+ } = opts;
2388
+ if (!appId) {
2389
+ throw new Error("getActionableAdvisories: appId is required");
2390
+ }
2391
+ const doFetch = resolveFetch(injectedFetch);
2392
+ const base = normalizeEndpoint(brainEndpoint);
2393
+ const qs = new URLSearchParams();
2394
+ qs.set("app_id", `eq.${appId}`);
2395
+ if (severity) qs.set("severity", `eq.${severity}`);
2396
+ const effectiveStatus = status ?? "open";
2397
+ if (effectiveStatus !== "all") {
2398
+ qs.set("status", `eq.${effectiveStatus}`);
2399
+ }
2400
+ qs.set("order", "last_observed_at.desc");
2401
+ const url = `${base}/rest/v1/actionable_advisories_v?${qs.toString()}`;
2402
+ let res;
2403
+ try {
2404
+ res = await doFetch(url, {
2405
+ method: "GET",
2406
+ headers: {
2407
+ Authorization: `Bearer ${brainJwt}`,
2408
+ apikey: brainAnonKey,
2409
+ Accept: "application/json"
2410
+ }
2411
+ });
2412
+ } catch (err) {
2413
+ const msg = err instanceof Error ? err.message : String(err);
2414
+ throw new Error(`getActionableAdvisories: network error: ${msg}`);
2415
+ }
2416
+ if (res.status === 401 || res.status === 403) {
2417
+ throw new Error("getActionableAdvisories: brain auth misconfig");
2418
+ }
2419
+ if (res.status >= 500) {
2420
+ throw new Error(`getActionableAdvisories: brain unavailable (${res.status})`);
2421
+ }
2422
+ if (!res.ok) {
2423
+ throw new Error(`getActionableAdvisories: bad request (${res.status})`);
2424
+ }
2425
+ let rows;
2426
+ try {
2427
+ rows = await res.json();
2428
+ } catch {
2429
+ throw new Error("getActionableAdvisories: malformed brain response");
2430
+ }
2431
+ if (!Array.isArray(rows)) {
2432
+ throw new Error("getActionableAdvisories: expected array from brain");
2433
+ }
2434
+ const out = [];
2435
+ for (const raw of rows) {
2436
+ if (raw && typeof raw === "object") {
2437
+ out.push(rowToAdvisory(raw));
2438
+ }
2439
+ }
2440
+ return out;
2441
+ }
2442
+ async function markAdvisoryResolved(opts) {
2443
+ const {
2444
+ id,
2445
+ resolutionNote,
2446
+ brainEndpoint,
2447
+ brainJwt,
2448
+ brainAnonKey,
2449
+ fetch: injectedFetch
2450
+ } = opts;
2451
+ if (!id) {
2452
+ return { ok: false, reason: "id_required" };
2453
+ }
2454
+ const doFetch = resolveFetch(injectedFetch);
2455
+ const base = normalizeEndpoint(brainEndpoint);
2456
+ const lookupUrl = `${base}/rest/v1/actionable_advisories_v?id=eq.${encodeURIComponent(id)}&select=app_id,rule`;
2457
+ let lookupRes;
2458
+ try {
2459
+ lookupRes = await doFetch(lookupUrl, {
2460
+ method: "GET",
2461
+ headers: {
2462
+ Authorization: `Bearer ${brainJwt}`,
2463
+ apikey: brainAnonKey,
2464
+ Accept: "application/json"
2465
+ }
2466
+ });
2467
+ } catch (err) {
2468
+ const msg = err instanceof Error ? err.message : String(err);
2469
+ return { ok: false, reason: `network_error:${msg}` };
2470
+ }
2471
+ if (lookupRes.status === 401 || lookupRes.status === 403) {
2472
+ return { ok: false, reason: "brain_auth_misconfig" };
2473
+ }
2474
+ if (lookupRes.status >= 500) {
2475
+ return { ok: false, reason: "brain_unavailable" };
2476
+ }
2477
+ if (!lookupRes.ok) {
2478
+ return { ok: false, reason: `brain_lookup_failed:${lookupRes.status}` };
2479
+ }
2480
+ let lookupRows;
2481
+ try {
2482
+ lookupRows = await lookupRes.json();
2483
+ } catch {
2484
+ return { ok: false, reason: "brain_lookup_malformed" };
2485
+ }
2486
+ if (!Array.isArray(lookupRows) || lookupRows.length === 0) {
2487
+ return { ok: false, reason: "advisory_not_found" };
2488
+ }
2489
+ const tuple = lookupRows[0];
2490
+ const appId = typeof tuple.app_id === "string" ? tuple.app_id : "";
2491
+ const code = typeof tuple.rule === "string" ? tuple.rule : "";
2492
+ if (!appId || !code) {
2493
+ return { ok: false, reason: "advisory_tuple_invalid" };
2494
+ }
2495
+ const outcomesUrl = `${base}/rest/v1/compile_outcomes?app_id=eq.${encodeURIComponent(appId)}&select=id`;
2496
+ let outcomesRes;
2497
+ try {
2498
+ outcomesRes = await doFetch(outcomesUrl, {
2499
+ method: "GET",
2500
+ headers: {
2501
+ Authorization: `Bearer ${brainJwt}`,
2502
+ apikey: brainAnonKey,
2503
+ Accept: "application/json"
2504
+ }
2505
+ });
2506
+ } catch (err) {
2507
+ const msg = err instanceof Error ? err.message : String(err);
2508
+ return { ok: false, reason: `network_error:${msg}` };
2509
+ }
2510
+ if (outcomesRes.status === 401 || outcomesRes.status === 403) {
2511
+ return { ok: false, reason: "brain_auth_misconfig" };
2512
+ }
2513
+ if (outcomesRes.status >= 500) {
2514
+ return { ok: false, reason: "brain_unavailable" };
2515
+ }
2516
+ if (!outcomesRes.ok) {
2517
+ return { ok: false, reason: `brain_lookup_failed:${outcomesRes.status}` };
2518
+ }
2519
+ let outcomeRows;
2520
+ try {
2521
+ outcomeRows = await outcomesRes.json();
2522
+ } catch {
2523
+ return { ok: false, reason: "brain_lookup_malformed" };
2524
+ }
2525
+ if (!Array.isArray(outcomeRows)) {
2526
+ return { ok: false, reason: "brain_lookup_malformed" };
2527
+ }
2528
+ const outcomeIds = [];
2529
+ for (const row of outcomeRows) {
2530
+ if (row && typeof row === "object") {
2531
+ const idVal = row.id;
2532
+ if (typeof idVal === "number" && Number.isFinite(idVal)) {
2533
+ outcomeIds.push(idVal);
2534
+ }
2535
+ }
2536
+ }
2537
+ if (outcomeIds.length === 0) {
2538
+ return { ok: true };
2539
+ }
2540
+ const inList = outcomeIds.join(",");
2541
+ const patchUrl = `${base}/rest/v1/compile_outcome_advisories?outcome_id=in.(${inList})&code=eq.${encodeURIComponent(code)}&resolved_at=is.null`;
2542
+ const patchBody = {
2543
+ resolved_at: (/* @__PURE__ */ new Date()).toISOString(),
2544
+ resolution_source: "consumer-marked"
2545
+ };
2546
+ if (resolutionNote !== void 0) {
2547
+ patchBody.resolution_note = resolutionNote;
2548
+ }
2549
+ let patchRes;
2550
+ try {
2551
+ patchRes = await doFetch(patchUrl, {
2552
+ method: "PATCH",
2553
+ headers: {
2554
+ Authorization: `Bearer ${brainJwt}`,
2555
+ apikey: brainAnonKey,
2556
+ "Content-Type": "application/json",
2557
+ Accept: "application/json",
2558
+ // PostgREST default is no return; we don't need the row back.
2559
+ Prefer: "return=minimal"
2560
+ },
2561
+ body: JSON.stringify(patchBody)
2562
+ });
2563
+ } catch (err) {
2564
+ const msg = err instanceof Error ? err.message : String(err);
2565
+ return { ok: false, reason: `network_error:${msg}` };
2566
+ }
2567
+ if (patchRes.status === 401 || patchRes.status === 403) {
2568
+ return { ok: false, reason: "brain_auth_misconfig" };
2569
+ }
2570
+ if (patchRes.status >= 500) {
2571
+ return { ok: false, reason: "brain_unavailable" };
2572
+ }
2573
+ if (!patchRes.ok) {
2574
+ return { ok: false, reason: `patch_failed:${patchRes.status}` };
2575
+ }
2576
+ return { ok: true };
2577
+ }
2578
+
2212
2579
  // src/models-brain.ts
2213
2580
  function isModelRow(x) {
2214
2581
  if (!x || typeof x !== "object") return false;
@@ -2344,7 +2711,10 @@ export {
2344
2711
  INTENT_ARCHETYPES,
2345
2712
  MEASURED_GROUNDING_MIN_N,
2346
2713
  PROVIDER_ENV_KEYS,
2714
+ RULE_SEQUENTIAL_TOOL_CLIFF,
2715
+ TRANSLATOR_FLOOR,
2347
2716
  allProfiles,
2717
+ applySectionRewrites,
2348
2718
  bucketContext,
2349
2719
  bucketHistory,
2350
2720
  bucketToolCount,
@@ -2355,6 +2725,7 @@ export {
2355
2725
  configureBrain,
2356
2726
  countTokens,
2357
2727
  execute,
2728
+ getActionableAdvisories,
2358
2729
  getAllStarterChains,
2359
2730
  getAllStarterChainsWithGrounding,
2360
2731
  getArchetypePerfScore,
@@ -2380,6 +2751,7 @@ export {
2380
2751
  loadChainsFromBrain,
2381
2752
  loadModelsFromBrain,
2382
2753
  loadPricingFromBrain,
2754
+ markAdvisoryResolved,
2383
2755
  profileToRow,
2384
2756
  profilesByProvider,
2385
2757
  record,
@@ -40,7 +40,41 @@ interface PromptSection {
40
40
  * Defaults to insertion order.
41
41
  */
42
42
  weight?: number;
43
+ /**
44
+ * alpha.29+ — declares the section's semantic kind so kgauto can apply
45
+ * model-aware rewrites at compile time. Default `'arbitrary'` (when
46
+ * unset) for full back-compat — pre-alpha.29 sections continue working
47
+ * unchanged.
48
+ *
49
+ * alpha.29 ships rewrites for `tool_call_contract` only. Other kinds are
50
+ * type-accepted but pass through. alpha.30+ will add rewrites for
51
+ * `narration_contract`, `role_intro`, etc.
52
+ *
53
+ * See `translator.ts` for the rewrite engine that consumes this field.
54
+ */
55
+ kind?: SectionKind;
43
56
  }
57
+ /**
58
+ * alpha.29+ — semantic kind tag for a `PromptSection`. The translator
59
+ * (`v2/src/translator.ts`) consumes this to apply model-aware rewrites at
60
+ * compile time. CLOSED union; future kinds extend it explicitly in named
61
+ * alpha releases.
62
+ *
63
+ * alpha.29 ships rewrites for `tool_call_contract` only. Other kinds are
64
+ * type-accepted but pass through.
65
+ *
66
+ * - `role_intro` — "You are a helpful assistant", persona blocks
67
+ * - `tool_call_contract` — tool-use rules ("call X then Y"); the alpha.29
68
+ * translator rewrites this for models with a
69
+ * sequential-tool cliff on the active archetype
70
+ * - `narration_contract` — output-format rules ("don't narrate your steps");
71
+ * alpha.30+ candidate
72
+ * - `user_turn` — when sections carry user content rather than
73
+ * system context (rare)
74
+ * - `reference` — supporting reference data the model may consult
75
+ * - `arbitrary` — explicit pass-through (default when unset)
76
+ */
77
+ type SectionKind = 'role_intro' | 'tool_call_contract' | 'narration_contract' | 'user_turn' | 'reference' | 'arbitrary';
44
78
  interface ToolDefinition {
45
79
  name: string;
46
80
  description?: string;
@@ -262,6 +296,18 @@ type CompiledRequest = {
262
296
  }>;
263
297
  tools?: unknown[];
264
298
  max_tokens?: number;
299
+ /**
300
+ * alpha.29 — emitted only when the translator's wire-overrides set
301
+ * `parallelToolCalls = false`. Shape per Anthropic Messages API docs:
302
+ * `{ type: 'auto', disable_parallel_tool_use: true }`. kgauto defaults
303
+ * to omitting `tool_choice` entirely (Anthropic defaults to auto + parallel),
304
+ * so this field's presence signals an explicit override.
305
+ */
306
+ tool_choice?: {
307
+ type: 'auto' | 'any' | 'tool' | 'none';
308
+ disable_parallel_tool_use?: boolean;
309
+ name?: string;
310
+ };
265
311
  } | {
266
312
  provider: 'google';
267
313
  model: string;
@@ -288,6 +334,12 @@ type CompiledRequest = {
288
334
  tools?: unknown[];
289
335
  response_format?: unknown;
290
336
  reasoning_effort?: string;
337
+ /**
338
+ * alpha.29 — emitted only when the translator's wire-overrides set
339
+ * `parallelToolCalls = false`. OpenAI defaults parallel_tool_calls=true
340
+ * server-side; we explicit-set to false only when overriding.
341
+ */
342
+ parallel_tool_calls?: boolean;
291
343
  } | {
292
344
  provider: 'deepseek';
293
345
  model: string;
@@ -397,6 +449,40 @@ type Adapter = {
397
449
  value: 'sequential';
398
450
  consequence: string;
399
451
  };
452
+ /**
453
+ * alpha.29+ — record of a single section rewrite fired by the translator at
454
+ * compile time. Surfaces on `CompileResult.sectionRewritesApplied` and (in
455
+ * scrubbed wire form, without original/transformed text) on
456
+ * `TraceDetail.sectionRewritesApplied` for Glass-Box Coaching-card rendering.
457
+ *
458
+ * `originalText` / `transformedText` stay package-internal — they may carry
459
+ * consumer PII. The wire-shape variant (`TraceSectionRewrite` in
460
+ * `glassbox-routes/types.ts`) carries only `summary` for renderer use.
461
+ */
462
+ interface SectionRewrite {
463
+ /** Stable id of the `PromptSection` that was rewritten. */
464
+ sectionId: string;
465
+ /** The `kind` discriminator that matched the rewrite rule. */
466
+ kind: SectionKind;
467
+ /**
468
+ * Stable identifier of the rule that fired (e.g.
469
+ * `'sequential-tool-cliff-below-floor'`). Future rules add named ids; the
470
+ * brain aggregates by this value for cross-app learning.
471
+ */
472
+ rule: string;
473
+ /** The section's text BEFORE the rewrite fired. */
474
+ originalText: string;
475
+ /** The text the translator emitted into the IR for this section. */
476
+ transformedText: string;
477
+ /**
478
+ * Wire-level overrides emitted alongside the text rewrite. Merged into
479
+ * `CompileResult.wireOverrides` by `applySectionRewrites`. alpha.29 ships
480
+ * `parallelToolCalls`; the union extends as more wire-overrides surface.
481
+ */
482
+ wireOverrides?: {
483
+ parallelToolCalls?: boolean;
484
+ };
485
+ }
400
486
  interface CompileResult {
401
487
  /** Unique handle for this call — pass to record() to correlate the outcome. */
402
488
  handle: string;
@@ -419,6 +505,29 @@ interface CompileResult {
419
505
  * array when no rules fired. alpha.6 Phase 1.
420
506
  */
421
507
  advisories: BestPracticeAdvisory[];
508
+ /**
509
+ * alpha.29+ — per-section rewrites applied by the translator at compile
510
+ * time. Empty array means no rewrites fired (or pre-alpha.29 behavior —
511
+ * all sections default `kind: 'arbitrary'`, which is pass-through).
512
+ *
513
+ * Surfaces to:
514
+ * - Glass-Box Coaching card (via `TraceDetail.sectionRewritesApplied`,
515
+ * scrubbed of original/transformed text)
516
+ * - brain `compile_outcomes.section_rewrites_applied` (migration 019)
517
+ * for cross-app learning
518
+ */
519
+ sectionRewritesApplied: SectionRewrite[];
520
+ /**
521
+ * alpha.29+ — wire-level overrides emitted by translator rewrites. The
522
+ * provider lowering pass threads these through to the wire request before
523
+ * emit. Today only `parallelToolCalls: boolean`; the type extends as more
524
+ * wire-overrides surface.
525
+ *
526
+ * Undefined when no rewrite emitted overrides — the common case.
527
+ */
528
+ wireOverrides?: {
529
+ parallelToolCalls?: boolean;
530
+ };
422
531
  /** Diagnostics for caller-side logging. */
423
532
  diagnostics: {
424
533
  sectionsKept: number;
@@ -919,4 +1028,4 @@ interface PerAxisMetrics {
919
1028
  /** Per-axis metrics keyed by model — used for chain-comparison views. */
920
1029
  type PerAxisMetricsByModel = Record<string, PerAxisMetrics>;
921
1030
 
922
- export { type ApiKeys as A, type BestPracticeAdvisory as B, type CompilePolicy as C, type FallbackReason as F, type Grounding as G, type HistoryCachePolicy as H, type IntentDeclaration as I, type Message as M, type NormalizedResponse as N, type OutcomeResult as O, type ProviderOverrides as P, type RecordInput as R, type ToolCall as T, type CompiledRequest as a, type PromptIR as b, type CallOptions as c, type CallResult as d, type RecordOutcomeInput as e, type OracleScore as f, type CompileResult as g, type Adapter as h, type PerAxisMetrics as i, type Provider as j, type ChainEntry as k, type CallAttempt as l, CallError as m, type ChainWithGrounding as n, type Constraints as o, type MutationApplied as p, type NormalizedTokens as q, type OutcomeKind as r, type PerAxisMetricsByModel as s, type PromptSection as t, type ToolDefinition as u };
1031
+ export { type ApiKeys as A, type BestPracticeAdvisory as B, type CompilePolicy as C, type FallbackReason as F, type Grounding as G, type HistoryCachePolicy as H, type IntentDeclaration as I, type Message as M, type NormalizedResponse as N, type OutcomeResult as O, type ProviderOverrides as P, type RecordInput as R, type SectionRewrite as S, type ToolCall as T, type CompiledRequest as a, type PromptIR as b, type CallOptions as c, type CallResult as d, type RecordOutcomeInput as e, type OracleScore as f, type CompileResult as g, type Adapter as h, type PerAxisMetrics as i, type Provider as j, type ChainEntry as k, type CallAttempt as l, CallError as m, type ChainWithGrounding as n, type Constraints as o, type MutationApplied as p, type NormalizedTokens as q, type OutcomeKind as r, type PerAxisMetricsByModel as s, type PromptSection as t, type SectionKind as u, type ToolDefinition as v };