blun-king-cli 9.1.597 → 9.1.600

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.
Files changed (106) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.md +17 -1
  3. package/agent-spine-plugin/CHANGELOG.md +1 -1
  4. package/agent-spine-plugin/docs/host-integration.md +1 -15
  5. package/agent-spine-plugin/docs/preflight-recall.md +1 -1
  6. package/agent-spine-plugin/scripts/check-install.js +1 -6
  7. package/agent-spine-plugin/scripts/hermetic-process.js +8 -0
  8. package/agent-spine-plugin/scripts/release-check.js +2 -3
  9. package/agent-spine-plugin/scripts/run-tests-hermetic.js +8 -11
  10. package/agent-spine-plugin/src/hook.js +40 -40
  11. package/agent-spine-plugin/src/lib/briefing.js +6 -3
  12. package/agent-spine-plugin/src/lib/hook-context.js +63 -64
  13. package/agent-spine-plugin/src/lib/hook-output.js +115 -94
  14. package/agent-spine-plugin/src/lib/hook-timeline.js +20 -11
  15. package/agent-spine-plugin/src/lib/host-instruction-budget.js +8 -14
  16. package/agent-spine-plugin/src/lib/mcp-timeline-tools.js +0 -7
  17. package/agent-spine-plugin/src/lib/pre-answer-timeline-recall.js +104 -0
  18. package/agent-spine-plugin/src/lib/session-timeline-codex.js +0 -4
  19. package/agent-spine-plugin/src/lib/session-timeline-enrollment-source.js +0 -3
  20. package/agent-spine-plugin/src/lib/session-timeline-enrollment-storage.js +0 -4
  21. package/agent-spine-plugin/src/lib/session-timeline-enrollment-transport.js +0 -2
  22. package/agent-spine-plugin/src/lib/session-timeline-enrollment.js +0 -22
  23. package/agent-spine-plugin/src/lib/session-timeline-host-origin.js +0 -4
  24. package/agent-spine-plugin/src/lib/session-timeline-prior.js +42 -50
  25. package/agent-spine-plugin/src/lib/session-timeline-provider.js +0 -3
  26. package/agent-spine-plugin/src/lib/session-timeline-query.js +0 -1
  27. package/agent-spine-plugin/src/lib/session-timeline-sid-acl.js +0 -4
  28. package/agent-spine-plugin/src/lib/session-timeline-source.js +0 -3
  29. package/agent-spine-plugin/src/lib/session-timeline-transport.js +0 -8
  30. package/agent-spine-plugin/src/lib/session-timeline-windows-acl.js +0 -3
  31. package/agent-spine-plugin/src/lib/session-timeline.js +370 -408
  32. package/agent-spine-plugin/src/lib/source-roots.js +26 -16
  33. package/agent-spine-plugin/src/lib/timeline-user-feedback.js +0 -1
  34. package/bin/core-bootstrap.js +18 -10
  35. package/bin/launcher-mode.js +5 -0
  36. package/bin/launcher-runtime.js +27 -0
  37. package/bin/verify-bundled-agent-sources.cjs +28 -2
  38. package/blun.mjs +798 -143
  39. package/bundled-agent-sources.json +192 -155
  40. package/package.json +1 -1
  41. package/standard-skills/translate-native/README.md +1334 -1
  42. package/standard-skills/translate-native/SKILL.md +1 -1
  43. package/standard-skills/translate-native/VERSION +1 -1
  44. package/standard-skills/translate-native/docs/PREMORTEM.md +901 -0
  45. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION.md +208 -41
  46. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_API.md +1319 -77
  47. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_EVIDENCE_HTTP.md +66 -11
  48. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_HTTP_PROVIDER.md +19 -1
  49. package/standard-skills/translate-native/docs/WEBSITE_LOCALIZATION_RECEIPT_VERIFIER_HTTP.md +24 -6
  50. package/standard-skills/translate-native/integrations/commercial_localization_profile.py +23 -0
  51. package/standard-skills/translate-native/integrations/website_localization.py +234 -10
  52. package/standard-skills/translate-native/integrations/website_localization_benchmark.py +39 -3
  53. package/standard-skills/translate-native/integrations/website_localization_cms.py +512 -10
  54. package/standard-skills/translate-native/integrations/website_localization_cms_client.py +116 -0
  55. package/standard-skills/translate-native/integrations/website_localization_cms_receiver.py +101 -11
  56. package/standard-skills/translate-native/integrations/website_localization_cms_receiver_runtime.py +12 -1
  57. package/standard-skills/translate-native/integrations/website_localization_cms_receiver_store.py +186 -24
  58. package/standard-skills/translate-native/integrations/website_localization_cms_source_client.py +64 -1
  59. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery.py +528 -5
  60. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_auth.py +918 -0
  61. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_auth_runtime.py +609 -0
  62. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_client.py +885 -0
  63. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_http.py +1244 -0
  64. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_runtime.py +220 -0
  65. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_sidecar_adapter.py +331 -0
  66. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_capabilities_http.py +444 -0
  67. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_client.py +673 -0
  68. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch.py +1317 -0
  69. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_client.py +826 -0
  70. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_http.py +1152 -0
  71. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_openapi.py +896 -0
  72. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_dispatch_runtime.py +621 -0
  73. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_http.py +1180 -0
  74. package/standard-skills/translate-native/integrations/website_localization_cms_source_delivery_submission_runtime.py +1479 -0
  75. package/standard-skills/translate-native/integrations/website_localization_cms_source_http.py +133 -17
  76. package/standard-skills/translate-native/integrations/website_localization_cms_source_runtime.py +319 -4
  77. package/standard-skills/translate-native/integrations/website_localization_cms_source_service.py +72 -2
  78. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_http.py +13 -0
  79. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver.py +56 -13
  80. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver_openapi.py +335 -0
  81. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_notification_receiver_runtime.py +66 -1
  82. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_processing_monitor.py +73 -7
  83. package/standard-skills/translate-native/integrations/website_localization_cms_terminal_receiver_client.py +93 -9
  84. package/standard-skills/translate-native/integrations/website_localization_evidence_http.py +221 -11
  85. package/standard-skills/translate-native/integrations/website_localization_health.py +6 -0
  86. package/standard-skills/translate-native/integrations/website_localization_http_provider.py +1 -1
  87. package/standard-skills/translate-native/integrations/website_localization_quality_profiles.py +350 -0
  88. package/standard-skills/translate-native/integrations/website_localization_queue.py +126 -56
  89. package/standard-skills/translate-native/integrations/website_localization_receipt_verifier_http.py +166 -13
  90. package/standard-skills/translate-native/integrations/website_localization_release.py +423 -17
  91. package/standard-skills/translate-native/integrations/website_localization_release_coordinator.py +168 -13
  92. package/standard-skills/translate-native/integrations/website_localization_runner.py +10 -0
  93. package/standard-skills/translate-native/integrations/website_localization_worker.py +213 -9
  94. package/standard-skills/translate-native/provenance.json +2 -2
  95. package/standard-skills/translate-native/references/commercial-localization.md +96 -17
  96. package/standard-skills/translate-native/scripts/blun_language_guard.py +3 -1
  97. package/standard-skills/translate-native/scripts/check_commercial_review.py +69 -9
  98. package/standard-skills/translate-native/scripts/commercial_localization_profile.py +829 -31
  99. package/standard-tools/language-guard/VERSION +1 -1
  100. package/standard-tools/language-guard/blun_language_guard.py +3 -1
  101. package/standard-tools/language-guard/check_commercial_review.py +69 -9
  102. package/standard-tools/language-guard/commercial_localization_profile.py +829 -31
  103. package/standard-tools/language-guard/provenance.json +2 -2
  104. package/standard-tools/manifest.json +1 -1
  105. package/worker-host.mjs +215 -82
  106. package/agent-spine-plugin/scripts/check-install-king.js +0 -37
package/worker-host.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // BLUN_BUILD_INPUT_SHA256:631215107f223f910482352d64f8081de5c7ebb282f9e66a53e7cd6dc93ba305
2
+ // BLUN_BUILD_INPUT_SHA256:c0b3db57102f82b976cc669375212221a4788318dbd6d1bfc28e943f7bf649db
3
3
  import { fileURLToPath as __cjsShimFileURLToPath } from 'node:url';
4
4
  import { dirname as __cjsShimDirname } from 'node:path';
5
5
  const __filename = __cjsShimFileURLToPath(import.meta.url);
@@ -1311,7 +1311,7 @@ function normalizeBlunToolSchema(schema) {
1311
1311
  }
1312
1312
  function ensureBlunPropertyTypes(schema) {
1313
1313
  const normalized = cloneJsonValue(schema);
1314
- if (!isRecord$9(normalized)) throw new Error("JSON Schema root must normalize to an object.");
1314
+ if (!isRecord$10(normalized)) throw new Error("JSON Schema root must normalize to an object.");
1315
1315
  recurseSchema(normalized);
1316
1316
  return normalized;
1317
1317
  }
@@ -1372,7 +1372,7 @@ function resolveLocalJsonPointer(root, ref) {
1372
1372
  let current = root;
1373
1373
  for (const rawPart of ref.slice(2).split("/")) {
1374
1374
  const part = unescapeJsonPointerPart(rawPart);
1375
- if (isRecord$9(current)) {
1375
+ if (isRecord$10(current)) {
1376
1376
  if (!hasOwn(current, part)) return { found: false };
1377
1377
  current = current[part];
1378
1378
  } else if (Array.isArray(current)) {
@@ -1394,20 +1394,20 @@ function parseJsonPointerArrayIndex(part) {
1394
1394
  return Number(part);
1395
1395
  }
1396
1396
  function recurseSchema(node) {
1397
- if (!isRecord$9(node)) return;
1397
+ if (!isRecord$10(node)) return;
1398
1398
  visitChildSchemas(node, normalizeProperty);
1399
1399
  }
1400
1400
  function visitChildSchemas(node, visit) {
1401
1401
  for (const { key, kind } of CHILD_SCHEMA_SLOTS) {
1402
1402
  const value = node[key];
1403
1403
  if (kind === "single") {
1404
- if (isRecord$9(value)) visit(value);
1404
+ if (isRecord$10(value)) visit(value);
1405
1405
  } else if (kind === "array") {
1406
1406
  if (Array.isArray(value)) for (const item of value) visit(item);
1407
1407
  } else if (kind === "map") {
1408
- if (isRecord$9(value)) for (const item of Object.values(value)) visit(item);
1408
+ if (isRecord$10(value)) for (const item of Object.values(value)) visit(item);
1409
1409
  } else if (kind === "schema-or-array") {
1410
- if (isRecord$9(value)) visit(value);
1410
+ if (isRecord$10(value)) visit(value);
1411
1411
  else if (Array.isArray(value)) for (const item of value) visit(item);
1412
1412
  }
1413
1413
  }
@@ -1419,7 +1419,7 @@ function childSchemaKeysForParentType(parentType) {
1419
1419
  });
1420
1420
  }
1421
1421
  function normalizeProperty(node) {
1422
- if (!isRecord$9(node)) return;
1422
+ if (!isRecord$10(node)) return;
1423
1423
  if (!hasOwn(node, "type") && !hasAnyKey(node, TYPE_COMPLETION_SKIP_KEYS)) {
1424
1424
  const enumValues = node["enum"];
1425
1425
  if (Array.isArray(enumValues) && enumValues.length > 0) node["type"] = inferTypeFromValues(enumValues);
@@ -1503,14 +1503,14 @@ function hasAnyKey(obj, keys) {
1503
1503
  }
1504
1504
  function cloneJsonValue(value) {
1505
1505
  if (Array.isArray(value)) return value.map((item) => cloneJsonValue(item));
1506
- if (isRecord$9(value)) {
1506
+ if (isRecord$10(value)) {
1507
1507
  const cloned = {};
1508
1508
  for (const [key, child] of Object.entries(value)) cloned[key] = cloneJsonValue(child);
1509
1509
  return cloned;
1510
1510
  }
1511
1511
  return value;
1512
1512
  }
1513
- function isRecord$9(value) {
1513
+ function isRecord$10(value) {
1514
1514
  return typeof value === "object" && value !== null && !Array.isArray(value);
1515
1515
  }
1516
1516
  function hasOwn(obj, key) {
@@ -2703,14 +2703,14 @@ var BlunStreamedMessage = class {
2703
2703
  };
2704
2704
  function contextOverflowOutputCap(error, currentCap) {
2705
2705
  const maxContext = error.maxContextTokens;
2706
- const counts = /requested ([\d,_]+) output tokens and your prompt contains (?:at least )?([\d,_]+) input tokens/i.exec(error.message);
2706
+ const counts = /requested ([\d,_]+) output tokens and your prompt contains (at least )?([\d,_]+) input tokens/i.exec(error.message);
2707
2707
  if (maxContext === void 0 || counts === null || !Number.isSafeInteger(maxContext) || maxContext <= 0) return;
2708
2708
  const requestedOutput = Number(counts[1].replaceAll(/[,_]/g, ""));
2709
- const input = Number(counts[2].replaceAll(/[,_]/g, ""));
2709
+ const input = Number(counts[3].replaceAll(/[,_]/g, ""));
2710
2710
  if (!Number.isSafeInteger(input) || input < 0 || !Number.isSafeInteger(requestedOutput) || requestedOutput <= 0 || !Number.isSafeInteger(input + requestedOutput) || input + requestedOutput <= maxContext) return;
2711
2711
  const available = maxContext - input;
2712
2712
  const headroom = Math.min(1024, Math.max(1, Math.ceil(available * .01)));
2713
- const cap = Math.min(requestedOutput - 1, available - headroom);
2713
+ const cap = Math.min(counts[2] === void 0 ? requestedOutput - 1 : Math.floor(requestedOutput / 2), available - headroom);
2714
2714
  if (cap < 1 || currentCap !== void 0 && (typeof currentCap !== "number" || !Number.isSafeInteger(currentCap) || cap >= currentCap)) return;
2715
2715
  return cap;
2716
2716
  }
@@ -10279,7 +10279,7 @@ function tokenFromWire(wire) {
10279
10279
  }
10280
10280
  //#endregion
10281
10281
  //#region ../../packages/oauth/src/utils.ts
10282
- function isRecord$8(value) {
10282
+ function isRecord$9(value) {
10283
10283
  return typeof value === "object" && value !== null && !Array.isArray(value);
10284
10284
  }
10285
10285
  //#endregion
@@ -10594,7 +10594,7 @@ var FileTokenStorage = class {
10594
10594
  } catch {
10595
10595
  return;
10596
10596
  }
10597
- if (!isRecord$8(parsed)) return void 0;
10597
+ if (!isRecord$9(parsed)) return void 0;
10598
10598
  return tokenFromWire(parsed);
10599
10599
  }
10600
10600
  async save(name, token) {
@@ -10699,7 +10699,7 @@ function extractApiErrorMessage(value) {
10699
10699
  }
10700
10700
  return;
10701
10701
  }
10702
- if (!isRecord$8(value)) return void 0;
10702
+ if (!isRecord$9(value)) return void 0;
10703
10703
  for (const key of DIRECT_ERROR_KEYS) {
10704
10704
  const message = stringField$1(value, key);
10705
10705
  if (message !== void 0) return message;
@@ -10707,7 +10707,7 @@ function extractApiErrorMessage(value) {
10707
10707
  const error = value["error"];
10708
10708
  const errorString = nonEmptyString$6(error);
10709
10709
  if (errorString !== void 0) return errorString;
10710
- if (isRecord$8(error)) for (const key of NESTED_ERROR_KEYS) {
10710
+ if (isRecord$9(error)) for (const key of NESTED_ERROR_KEYS) {
10711
10711
  const message = stringField$1(error, key);
10712
10712
  if (message !== void 0) return message;
10713
10713
  }
@@ -10801,7 +10801,7 @@ async function postForm(url, params, deviceHeaders, options) {
10801
10801
  let data = {};
10802
10802
  try {
10803
10803
  const parsed = await response.json();
10804
- if (isRecord$8(parsed)) data = parsed;
10804
+ if (isRecord$9(parsed)) data = parsed;
10805
10805
  } catch {}
10806
10806
  return {
10807
10807
  status,
@@ -12718,9 +12718,9 @@ function blunContextWindowsUrl(oauthHost) {
12718
12718
  return `${(oauthHost ?? process.env["BLUN_OAUTH_HOST"] ?? "https://account.blun.ai").replace(/\/+$/, "")}/api/verbrauch/tarife`;
12719
12719
  }
12720
12720
  function parseManagedContextWindow(payload, plan) {
12721
- if (!isRecord$8(payload)) return void 0;
12721
+ if (!isRecord$9(payload)) return void 0;
12722
12722
  const contextWindows = payload["kontext"];
12723
- if (!isRecord$8(contextWindows)) return void 0;
12723
+ if (!isRecord$9(contextWindows)) return void 0;
12724
12724
  const value = contextWindows[plan];
12725
12725
  return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : void 0;
12726
12726
  }
@@ -12755,7 +12755,7 @@ function parseManagedUsagePayload(payload) {
12755
12755
  const plan = typeof rec["plan"] === "string" && rec["plan"].trim().length > 0 ? rec["plan"].trim() : void 0;
12756
12756
  const contextWindowTokens = managedContextWindowFrom(rec);
12757
12757
  const unlimited = rec["unlimited"] === true;
12758
- const stand = isRecord$8(rec["stand"]) ? rec["stand"] : void 0;
12758
+ const stand = isRecord$9(rec["stand"]) ? rec["stand"] : void 0;
12759
12759
  if (stand !== void 0) for (const [sourceKey, id, label] of ACCOUNT_USAGE_WINDOWS) {
12760
12760
  const row = toAccountUsageRow(stand[sourceKey], id, label, unlimited);
12761
12761
  if (row !== null) limits.push(row);
@@ -12765,9 +12765,9 @@ function parseManagedUsagePayload(payload) {
12765
12765
  const item = rawLimits[idx];
12766
12766
  if (!item || typeof item !== "object") continue;
12767
12767
  const detailRaw = item["detail"];
12768
- const detail = isRecord$8(detailRaw) ? detailRaw : item;
12768
+ const detail = isRecord$9(detailRaw) ? detailRaw : item;
12769
12769
  const windowRaw = item["window"];
12770
- const row = toUsageRow(detail, limitLabel(item, detail, isRecord$8(windowRaw) ? windowRaw : {}, idx));
12770
+ const row = toUsageRow(detail, limitLabel(item, detail, isRecord$9(windowRaw) ? windowRaw : {}, idx));
12771
12771
  if (row !== null) limits.push(row);
12772
12772
  }
12773
12773
  return {
@@ -12808,7 +12808,7 @@ const ACCOUNT_USAGE_WINDOWS = [
12808
12808
  ]
12809
12809
  ];
12810
12810
  function toAccountUsageRow(raw, id, label, accountUnlimited) {
12811
- if (!isRecord$8(raw)) return null;
12811
+ if (!isRecord$9(raw)) return null;
12812
12812
  const used = toInt(raw["verbraucht"]);
12813
12813
  const unlimited = accountUnlimited || raw["unlimited"] === true;
12814
12814
  const fraction = raw["anteil"];
@@ -12841,7 +12841,7 @@ function toAccountUsageRow(raw, id, label, accountUnlimited) {
12841
12841
  };
12842
12842
  }
12843
12843
  function toUsageRow(raw, defaultLabel) {
12844
- if (!isRecord$8(raw)) return null;
12844
+ if (!isRecord$9(raw)) return null;
12845
12845
  const unlimited = raw["unlimited"] === true;
12846
12846
  const limit = toInt(raw["limit"]);
12847
12847
  let used = toInt(raw["used"]);
@@ -12962,7 +12962,7 @@ function isManagedQuotaErrorMessage(message) {
12962
12962
  return /you(?:'|’)?ve reached your usage limit/.test(normalized) && /billing cycle|quota will be (?:refreshed|reset)|purchase extra usage/.test(normalized);
12963
12963
  }
12964
12964
  function hasManagedUsageShape(payload) {
12965
- if (!isRecord$8(payload)) return false;
12965
+ if (!isRecord$9(payload)) return false;
12966
12966
  let recognized = false;
12967
12967
  if ("context_window_tokens" in payload || "contextWindowTokens" in payload) {
12968
12968
  recognized = true;
@@ -12978,15 +12978,15 @@ function hasManagedUsageShape(payload) {
12978
12978
  if (!Array.isArray(limits)) return false;
12979
12979
  for (let index = 0; index < limits.length; index++) {
12980
12980
  const item = limits[index];
12981
- if (!isRecord$8(item)) return false;
12982
- const detail = isRecord$8(item["detail"]) ? item["detail"] : item;
12983
- if (toUsageRow(detail, limitLabel(item, detail, isRecord$8(item["window"]) ? item["window"] : {}, index)) === null) return false;
12981
+ if (!isRecord$9(item)) return false;
12982
+ const detail = isRecord$9(item["detail"]) ? item["detail"] : item;
12983
+ if (toUsageRow(detail, limitLabel(item, detail, isRecord$9(item["window"]) ? item["window"] : {}, index)) === null) return false;
12984
12984
  }
12985
12985
  }
12986
12986
  if ("stand" in payload) {
12987
12987
  recognized = true;
12988
12988
  const stand = payload["stand"];
12989
- if (!isRecord$8(stand)) return false;
12989
+ if (!isRecord$9(stand)) return false;
12990
12990
  const windows = ACCOUNT_USAGE_WINDOWS.filter(([sourceKey]) => sourceKey in stand);
12991
12991
  if (windows.length === 0) return false;
12992
12992
  const unlimited = payload["unlimited"] === true;
@@ -13069,8 +13069,8 @@ function userExtras(existing, remoteOwnedFields) {
13069
13069
  return out;
13070
13070
  }
13071
13071
  function mergeRefreshedModelAlias(existing, remote, remoteOwnedFields) {
13072
- const current = isRecord$8(existing) ? existing : {};
13073
- const overrides = cloneOverrides(isRecord$8(current["overrides"]) ? current["overrides"] : void 0);
13072
+ const current = isRecord$9(existing) ? existing : {};
13073
+ const overrides = cloneOverrides(isRecord$9(current["overrides"]) ? current["overrides"] : void 0);
13074
13074
  return {
13075
13075
  ...userExtras(current, remoteOwnedFields),
13076
13076
  ...remote,
@@ -13272,7 +13272,7 @@ function parseModelContextLength(item, modelId) {
13272
13272
  return values[0];
13273
13273
  }
13274
13274
  function toModelInfo(item) {
13275
- if (!isRecord$8(item) || typeof item["id"] !== "string" || item["id"].length === 0) return;
13275
+ if (!isRecord$9(item) || typeof item["id"] !== "string" || item["id"].length === 0) return;
13276
13276
  const contextLength = parseModelContextLength(item, item["id"]);
13277
13277
  const displayName = item["display_name"];
13278
13278
  const normalizedDisplayName = typeof displayName === "string" && displayName.length > 0 ? displayName : void 0;
@@ -13359,7 +13359,7 @@ async function fetchManagedBlunCodeModels(options) {
13359
13359
  throw new Error(message);
13360
13360
  }
13361
13361
  const payload = await response.json();
13362
- if (!isRecord$8(payload) || !Array.isArray(payload["data"])) throw new Error(`Unexpected models response for ${baseUrl}.`);
13362
+ if (!isRecord$9(payload) || !Array.isArray(payload["data"])) throw new Error(`Unexpected models response for ${baseUrl}.`);
13363
13363
  return payload["data"].map((item) => toModelInfo(item)).filter((item) => item !== void 0);
13364
13364
  }
13365
13365
  throw new Error(`Failed to list BLUN models for ${baseUrl}.`);
@@ -13402,11 +13402,11 @@ function applyManagedBlunCodeConfig(config, options) {
13402
13402
  apiKey
13403
13403
  };
13404
13404
  const upstreamKeys = new Set(options.models.map((m) => managedModelKey(m.id)));
13405
- for (const [key, model] of Object.entries(existingModels)) if (RETIRED_BLUN_MODEL_KEYS.has(key) || isRecord$8(model) && model["provider"] === "managed:blun" && !upstreamKeys.has(key)) delete existingModels[key];
13405
+ for (const [key, model] of Object.entries(existingModels)) if (RETIRED_BLUN_MODEL_KEYS.has(key) || isRecord$9(model) && model["provider"] === "managed:blun" && !upstreamKeys.has(key)) delete existingModels[key];
13406
13406
  for (const model of options.models) {
13407
13407
  const capabilities = capabilitiesForModel(model);
13408
13408
  const key = managedModelKey(model.id);
13409
- const existing = isRecord$8(existingModels[key]) ? existingModels[key] : {};
13409
+ const existing = isRecord$9(existingModels[key]) ? existingModels[key] : {};
13410
13410
  const supportsAdaptiveThinking = capabilities?.includes("thinking") === true || capabilities?.includes("always_thinking") === true;
13411
13411
  existingModels[key] = mergeRefreshedModelAlias(existing, {
13412
13412
  provider: BLUN_PROVIDER_NAME$1,
@@ -13454,7 +13454,7 @@ function applyManagedBlunCodeLogoutConfig(config) {
13454
13454
  let removedDefaultModel = false;
13455
13455
  const existingModels = config.models ?? {};
13456
13456
  for (const [key, model] of Object.entries(existingModels)) {
13457
- if (!isRecord$8(model) || model["provider"] !== "managed:blun") continue;
13457
+ if (!isRecord$9(model) || model["provider"] !== "managed:blun") continue;
13458
13458
  delete existingModels[key];
13459
13459
  if (config.defaultModel === key) removedDefaultModel = true;
13460
13460
  }
@@ -13494,7 +13494,7 @@ function selectDefaultModel(config, models, options) {
13494
13494
  function canPreserveDefaultModel(existingModels, defaultModel, managedModels) {
13495
13495
  if (managedModels.has(defaultModel)) return true;
13496
13496
  const existing = existingModels[defaultModel];
13497
- return isRecord$8(existing) && existing["provider"] !== "managed:blun";
13497
+ return isRecord$9(existing) && existing["provider"] !== "managed:blun";
13498
13498
  }
13499
13499
  function assertPositiveContextLength(model) {
13500
13500
  if (!Number.isInteger(model.contextLength) || model.contextLength <= 0) throw new Error(`BLUN model "${model.id}" must include a positive context_length.`);
@@ -13532,13 +13532,13 @@ function blunManagedQuotaUrl(oauthHost) {
13532
13532
  return `${(oauthHost ?? process.env["BLUN_OAUTH_HOST"] ?? "https://account.blun.ai").replace(/\/+$/, "")}${MANAGED_QUOTA_PATH}`;
13533
13533
  }
13534
13534
  function parseManagedQuotaPayload(payload) {
13535
- if (!isRecord$8(payload)) return void 0;
13535
+ if (!isRecord$9(payload)) return void 0;
13536
13536
  const plan = nonEmptyString$5(payload["plan"]);
13537
13537
  const paid = payload["bezahlt"];
13538
13538
  const creditCents = payload["guthaben_cent"];
13539
13539
  const billingKind = nonEmptyString$5(payload["art"]);
13540
13540
  const globalUnlimited = payload["unlimited"];
13541
- if (plan === void 0 || typeof paid !== "boolean" || !Number.isSafeInteger(creditCents) || creditCents < 0 || billingKind === void 0 || globalUnlimited !== void 0 && typeof globalUnlimited !== "boolean" || !isRecord$8(payload["stand"])) return;
13541
+ if (plan === void 0 || typeof paid !== "boolean" || !Number.isSafeInteger(creditCents) || creditCents < 0 || billingKind === void 0 || globalUnlimited !== void 0 && typeof globalUnlimited !== "boolean" || !isRecord$9(payload["stand"])) return;
13542
13542
  if (!hasStrictQuotaStand(payload["stand"], globalUnlimited === true)) return void 0;
13543
13543
  const limits = parseManagedUsagePayload(payload).limits;
13544
13544
  if (limits.length !== REQUIRED_WINDOWS.length || REQUIRED_WINDOWS.some(([, id]) => limits.filter((row) => row.id === id).length !== 1)) return;
@@ -13608,7 +13608,7 @@ function nonEmptyString$5(value) {
13608
13608
  function hasStrictQuotaStand(stand, globalUnlimited) {
13609
13609
  return REQUIRED_WINDOWS.every(([sourceKey]) => {
13610
13610
  const row = stand[sourceKey];
13611
- if (!isRecord$8(row)) return false;
13611
+ if (!isRecord$9(row)) return false;
13612
13612
  const used = row["verbraucht"];
13613
13613
  const rowUnlimited = row["unlimited"];
13614
13614
  if (rowUnlimited !== void 0 && typeof rowUnlimited !== "boolean") return false;
@@ -20705,7 +20705,7 @@ function parseSkillText(options) {
20705
20705
  throw error;
20706
20706
  }
20707
20707
  const frontmatter = parsed.data ?? {};
20708
- if (!isRecord$7(frontmatter)) throw new SkillParseError(`Frontmatter in ${options.skillMdPath} must be a mapping at the top level`);
20708
+ if (!isRecord$8(frontmatter)) throw new SkillParseError(`Frontmatter in ${options.skillMdPath} must be a mapping at the top level`);
20709
20709
  const metadata = normalizeMetadata(frontmatter);
20710
20710
  if (!isSupportedSkillType(metadata.type)) throw new UnsupportedSkillTypeError(metadata.type ?? String(frontmatter["type"]));
20711
20711
  const name = nonEmptyString$3(metadata.name);
@@ -20818,7 +20818,7 @@ function tokenizeArgs(raw) {
20818
20818
  function nonEmptyString$3(value) {
20819
20819
  return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
20820
20820
  }
20821
- function isRecord$7(value) {
20821
+ function isRecord$8(value) {
20822
20822
  return typeof value === "object" && value !== null && !Array.isArray(value);
20823
20823
  }
20824
20824
  //#endregion
@@ -20826,7 +20826,7 @@ function isRecord$7(value) {
20826
20826
  function parseCommandText(input) {
20827
20827
  const { text, commandPath, pluginId } = input;
20828
20828
  const parsed = parseFrontmatter(text);
20829
- const frontmatter = isRecord$6(parsed.data) ? parsed.data : {};
20829
+ const frontmatter = isRecord$7(parsed.data) ? parsed.data : {};
20830
20830
  const baseName = input.fallbackName ?? path.basename(commandPath).replace(/\.md$/i, "");
20831
20831
  const name = nonEmptyString$2(frontmatter["name"]) ?? baseName;
20832
20832
  const body = parsed.body.trim();
@@ -20868,7 +20868,7 @@ function descriptionFromBody(body) {
20868
20868
  if (firstLine === void 0) return "No description provided.";
20869
20869
  return firstLine.length > 240 ? `${firstLine.slice(0, 239)}…` : firstLine;
20870
20870
  }
20871
- function isRecord$6(value) {
20871
+ function isRecord$7(value) {
20872
20872
  return typeof value === "object" && value !== null && !Array.isArray(value);
20873
20873
  }
20874
20874
  //#endregion
@@ -28376,7 +28376,7 @@ const ResumeIntentSchema = object({
28376
28376
  startedAt: timestamp$2.nullable()
28377
28377
  }).strict();
28378
28378
  const hash$2 = (value) => createHash("sha256").update(value).digest("hex");
28379
- const isRecord$5 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
28379
+ const isRecord$6 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
28380
28380
  /** Explicit resume intents, isolated from ordinary task status and output files. */
28381
28381
  var TaskResumeStore = class {
28382
28382
  home;
@@ -28432,9 +28432,9 @@ var TaskResumeStore = class {
28432
28432
  }
28433
28433
  async readOwnedTask(taskId) {
28434
28434
  const task = await this.read("tasks", taskId);
28435
- if (!isRecord$5(task)) throw new Error("Task has no persisted local record.");
28435
+ if (!isRecord$6(task)) throw new Error("Task has no persisted local record.");
28436
28436
  const owner = task["resumeOwnership"];
28437
- if (!isRecord$5(owner) || owner["version"] !== 1 || owner["scope"] !== hash$2(await this.root()) || owner["taskId"] !== taskId || task["taskId"] !== taskId || task["kind"] !== "agent" || owner["agentId"] !== task["agentId"] || owner["startedAt"] !== task["startedAt"]) throw new Error("Task ownership is unverified; historical or foreign records cannot be resumed automatically.");
28437
+ if (!isRecord$6(owner) || owner["version"] !== 1 || owner["scope"] !== hash$2(await this.root()) || owner["taskId"] !== taskId || task["taskId"] !== taskId || task["kind"] !== "agent" || owner["agentId"] !== task["agentId"] || owner["startedAt"] !== task["startedAt"]) throw new Error("Task ownership is unverified; historical or foreign records cannot be resumed automatically.");
28438
28438
  const { resumeOwnership: _ownership, ...info } = task;
28439
28439
  return info;
28440
28440
  }
@@ -28686,12 +28686,12 @@ function legacyStatusToCurrent(task) {
28686
28686
  return task.status;
28687
28687
  }
28688
28688
  function isReadablePersistedTask(obj) {
28689
- return isRecord$4(obj) && (typeof obj["taskId"] === "string" || typeof obj["task_id"] === "string");
28689
+ return isRecord$5(obj) && (typeof obj["taskId"] === "string" || typeof obj["task_id"] === "string");
28690
28690
  }
28691
28691
  function isLegacyPersistedTask(task) {
28692
28692
  return "task_id" in task;
28693
28693
  }
28694
- function isRecord$4(value) {
28694
+ function isRecord$5(value) {
28695
28695
  return typeof value === "object" && value !== null;
28696
28696
  }
28697
28697
  function optionalNonEmptyString(value) {
@@ -228018,7 +228018,7 @@ var HttpVisionReader = class {
228018
228018
  reason: "malformed"
228019
228019
  };
228020
228020
  }
228021
- if (!isRecord$3(payload) || payload["done"] !== true || typeof payload["response"] !== "string" || !isNonNegativeInteger(payload["prompt_eval_count"]) || !isNonNegativeInteger(payload["eval_count"])) return {
228021
+ if (!isRecord$4(payload) || payload["done"] !== true || typeof payload["response"] !== "string" || !isNonNegativeInteger(payload["prompt_eval_count"]) || !isNonNegativeInteger(payload["eval_count"])) return {
228022
228022
  ok: false,
228023
228023
  reason: "malformed"
228024
228024
  };
@@ -228442,7 +228442,7 @@ function escapeXml(value) {
228442
228442
  function locationKey(messageIndex, partIndex) {
228443
228443
  return `${String(messageIndex)}:${String(partIndex)}`;
228444
228444
  }
228445
- function isRecord$3(value) {
228445
+ function isRecord$4(value) {
228446
228446
  return value !== null && typeof value === "object" && !Array.isArray(value);
228447
228447
  }
228448
228448
  function isNonNegativeInteger(value) {
@@ -229582,7 +229582,7 @@ var FullCompaction = class {
229582
229582
  this.agent.log.error("failed to refresh system prompt after compaction", { error });
229583
229583
  }
229584
229584
  await this.agent.injection.injectAfterCompaction();
229585
- this.fitRetainedInput();
229585
+ this.fitRetainedInput({}, result.keptHeadUserMessageCount !== void 0);
229586
229586
  this.lastCompactedState = {
229587
229587
  tokenCount: this.tokenCountWithPending,
229588
229588
  modelAlias: this.agent.activeResponderModel ?? this.agent.config.modelAlias,
@@ -229618,17 +229618,18 @@ var FullCompaction = class {
229618
229618
  this.agent.turn.onCompactionFinished();
229619
229619
  }
229620
229620
  }
229621
- fitRetainedInput(context = {}) {
229621
+ fitRetainedInput(context = {}, freshBudgetedSelection = false) {
229622
229622
  const budget = this.getCompactionBudgetTokens();
229623
229623
  const window = this.getEffectiveMaxContextTokens();
229624
229624
  const limit = window > 0 ? window - 1 : Infinity;
229625
+ const hardContextLimit = window > 0 && !freshBudgetedSelection ? window : void 0;
229625
229626
  const estimate = (messages) => this.estimateProjectedRequestTokens({
229626
229627
  ...context,
229627
229628
  messages
229628
229629
  });
229629
229630
  const preserveLatestUser = this.agent.turn.hasActiveTurn;
229630
- if (budget === void 0) return this.agent.context.fitCompactionPrefix(limit, estimate, preserveLatestUser);
229631
- return this.agent.context.fitCompactionPrefix(Math.min(Math.floor(budget * .8), limit), estimate, preserveLatestUser) || this.agent.context.fitCompactionPrefix(Math.min(Math.floor(budget * .9) - 1, limit), estimate, preserveLatestUser);
229631
+ if (budget === void 0) return this.agent.context.fitCompactionPrefix(limit, estimate, preserveLatestUser, hardContextLimit);
229632
+ return this.agent.context.fitCompactionPrefix(Math.min(Math.floor(budget * .8), limit), estimate, preserveLatestUser, hardContextLimit) || this.agent.context.fitCompactionPrefix(Math.min(Math.floor(budget * .9) - 1, limit), estimate, preserveLatestUser, hardContextLimit);
229632
229633
  }
229633
229634
  buildInstruction(customInstruction) {
229634
229635
  return renderPrompt(compaction_instruction_default, { customInstruction: customInstruction?.trim() ?? "" }).trimEnd();
@@ -233588,7 +233589,7 @@ var ContextMemory = class {
233588
233589
  this.agent.microCompaction.reset();
233589
233590
  this.agent.emitStatusUpdated();
233590
233591
  }
233591
- fitCompactionPrefix(maxRequestTokens, estimateRequest, preserveLatestUser = false) {
233592
+ fitCompactionPrefix(maxRequestTokens, estimateRequest, preserveLatestUser = false, hardContextLimit) {
233592
233593
  const prefixLength = this._history.findIndex((message) => message.origin?.kind === "compaction_summary");
233593
233594
  if (prefixLength <= 0 || !Number.isSafeInteger(maxRequestTokens) || maxRequestTokens <= 0) return false;
233594
233595
  const suffix = this._history.slice(prefixLength);
@@ -233600,7 +233601,9 @@ var ContextMemory = class {
233600
233601
  ];
233601
233602
  return Math.max(estimateTokensForMessages(messages), estimateRequest(project(messages, { dropOrphanResults: true })));
233602
233603
  };
233603
- if (estimate(this._history.slice(0, prefixLength)) <= maxRequestTokens) return false;
233604
+ const requestTokens = estimate(this._history.slice(0, prefixLength));
233605
+ if (requestTokens <= maxRequestTokens) return false;
233606
+ if (hardContextLimit !== void 0 && requestTokens >= hardContextLimit) return false;
233604
233607
  const sourceKey = Symbol("compaction source");
233605
233608
  const sources = collectCompactableUserMessages(this._history.slice(0, prefixLength)).map((message) => ({
233606
233609
  ...message,
@@ -234239,7 +234242,7 @@ const OptionalStringSchema = preprocess((value) => {
234239
234242
  if (typeof value === "string") return value;
234240
234243
  if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value);
234241
234244
  }, string().optional());
234242
- const HookSpecificOutputSchema = preprocess((value) => isRecord$2(value) ? value : void 0, looseObject({
234245
+ const HookSpecificOutputSchema = preprocess((value) => isRecord$3(value) ? value : void 0, looseObject({
234243
234246
  message: OptionalStringSchema,
234244
234247
  additionalContext: OptionalStringSchema,
234245
234248
  permissionDecision: unknown().optional(),
@@ -234391,7 +234394,7 @@ function structuredOutput(stdout, input) {
234391
234394
  return {
234392
234395
  ...result,
234393
234396
  additionalContext: input["hook_event_name"] === "UserPromptSubmit" && hookSpecificOutput?.hookEventName === "UserPromptSubmit" ? hookSpecificOutput.additionalContext : void 0,
234394
- updatedInput: input["hook_event_name"] === "PreToolUse" && hookSpecificOutput?.hookEventName === "PreToolUse" && isRecord$2(hookSpecificOutput.updatedInput) ? hookSpecificOutput.updatedInput : void 0
234397
+ updatedInput: input["hook_event_name"] === "PreToolUse" && hookSpecificOutput?.hookEventName === "PreToolUse" && isRecord$3(hookSpecificOutput.updatedInput) ? hookSpecificOutput.updatedInput : void 0
234395
234398
  };
234396
234399
  }
234397
234400
  return {
@@ -234460,7 +234463,7 @@ function killProcessTreeWindows(child, force) {
234460
234463
  } catch {}
234461
234464
  }
234462
234465
  }
234463
- function isRecord$2(value) {
234466
+ function isRecord$3(value) {
234464
234467
  return typeof value === "object" && value !== null && !Array.isArray(value);
234465
234468
  }
234466
234469
  function errorMessage$3(error) {
@@ -234581,7 +234584,16 @@ var HookEngine = class {
234581
234584
  };
234582
234585
  }
234583
234586
  if (this.admissionClosed) return void 0;
234584
- return runHook(hook.command, inputData, {
234587
+ let hookInput;
234588
+ try {
234589
+ hookInput = this.options.runtimeInput?.(hook, inputData) ?? inputData;
234590
+ } catch {
234591
+ return {
234592
+ action: "block",
234593
+ reason: "Hook input preparation failed."
234594
+ };
234595
+ }
234596
+ return runHook(hook.command, hookInput, {
234585
234597
  timeout: hook.timeout ?? DEFAULT_HOOK_TIMEOUT_SECONDS,
234586
234598
  cwd: hook.cwd ?? (this.options.cwd === "" ? void 0 : this.options.cwd),
234587
234599
  env: hook.env,
@@ -235816,7 +235828,7 @@ function parsePersonalMemoryRecall(output) {
235816
235828
  } catch {
235817
235829
  return { kind: "invalid" };
235818
235830
  }
235819
- if (!isRecord$1(parsed) || !isRecord$1(parsed["personal_memory"])) return { kind: "invalid" };
235831
+ if (!isRecord$2(parsed) || !isRecord$2(parsed["personal_memory"])) return { kind: "invalid" };
235820
235832
  const memory = parsed["personal_memory"];
235821
235833
  const savedRaw = memory["saved"];
235822
235834
  const threadsRaw = memory["threads"];
@@ -235825,7 +235837,7 @@ function parsePersonalMemoryRecall(output) {
235825
235837
  if (savedRaw.length > MAX_SAVED_MEMORIES || threadsRaw.length > MAX_THREADS || historyRaw.length > 0) return { kind: "invalid" };
235826
235838
  const saved = [];
235827
235839
  for (const value of savedRaw) {
235828
- if (!isRecord$1(value)) return { kind: "invalid" };
235840
+ if (!isRecord$2(value)) return { kind: "invalid" };
235829
235841
  const text = boundedTrimmedString(value["text"], MAX_MEMORY_TEXT_CHARS);
235830
235842
  const confidence = value["confidence"];
235831
235843
  if (text === void 0 || typeof confidence !== "string" || !CONFIDENCE_VALUES.has(confidence)) return { kind: "invalid" };
@@ -235836,7 +235848,7 @@ function parsePersonalMemoryRecall(output) {
235836
235848
  }
235837
235849
  const threads = [];
235838
235850
  for (const value of threadsRaw) {
235839
- if (!isRecord$1(value)) return { kind: "invalid" };
235851
+ if (!isRecord$2(value)) return { kind: "invalid" };
235840
235852
  const title = boundedTrimmedString(value["title"], MAX_THREAD_TITLE_CHARS);
235841
235853
  const summary = boundedTrimmedString(value["summary"], MAX_THREAD_SUMMARY_CHARS);
235842
235854
  if (title === void 0 || summary === void 0) return { kind: "invalid" };
@@ -235890,7 +235902,7 @@ function boundedTrimmedString(value, maxChars) {
235890
235902
  const trimmed = value.trim();
235891
235903
  return trimmed.length > 0 ? trimmed : void 0;
235892
235904
  }
235893
- function isRecord$1(value) {
235905
+ function isRecord$2(value) {
235894
235906
  return typeof value === "object" && value !== null && !Array.isArray(value);
235895
235907
  }
235896
235908
  //#endregion
@@ -264047,10 +264059,70 @@ function rejectionMessage(value) {
264047
264059
  return `Response review rejected candidate: ${value.stage}/${value.reason}` + (findings?.length ? ` (${findings.join(", ")})` : "");
264048
264060
  }
264049
264061
  //#endregion
264062
+ //#region ../../packages/agent-core/src/agent/response-review-deadline.ts
264063
+ const IDLE_TIMEOUT_MS = 3e4;
264064
+ const NATIVE_FIRST_CONTENT_MS = 9e4;
264065
+ const ABSOLUTE_TIMEOUT_MS = 12e4;
264066
+ function createResponseReviewDeadline(source) {
264067
+ const absoluteExpiresAt = performance.now() + ABSOLUTE_TIMEOUT_MS;
264068
+ const absolute = createDeadlineAbortSignal(source, ABSOLUTE_TIMEOUT_MS);
264069
+ const controller = new AbortController();
264070
+ const unlink = linkAbortSignal(absolute.signal, controller);
264071
+ let stage = "setup";
264072
+ let didTimeout = false;
264073
+ let closed = false;
264074
+ let idle;
264075
+ let idleExpiresAt = 0;
264076
+ const expire = () => {
264077
+ didTimeout = true;
264078
+ controller.abort(abortError());
264079
+ };
264080
+ const expireIfDue = () => {
264081
+ if (closed || controller.signal.aborted) return;
264082
+ const now = performance.now();
264083
+ if (now >= absoluteExpiresAt || now >= idleExpiresAt) expire();
264084
+ };
264085
+ const refresh = (timeoutMs) => {
264086
+ idleExpiresAt = performance.now() + timeoutMs;
264087
+ if (idle !== void 0) clearTimeout(idle);
264088
+ idle = setTimeout(expire, timeoutMs);
264089
+ };
264090
+ refresh(IDLE_TIMEOUT_MS);
264091
+ return {
264092
+ signal: controller.signal,
264093
+ timedOut: () => didTimeout || absolute.timedOut(),
264094
+ throwIfAborted: () => {
264095
+ expireIfDue();
264096
+ controller.signal.throwIfAborted();
264097
+ },
264098
+ progress: (event) => {
264099
+ expireIfDue();
264100
+ if (closed || controller.signal.aborted) return;
264101
+ if (event === "native.start" && stage === "setup") {
264102
+ stage = "native";
264103
+ refresh(NATIVE_FIRST_CONTENT_MS);
264104
+ } else if (event === "native.text" && stage === "native") refresh(IDLE_TIMEOUT_MS);
264105
+ else if (event === "release.start" && stage === "native") {
264106
+ stage = "release";
264107
+ refresh(IDLE_TIMEOUT_MS);
264108
+ } else if (event === "verification.start" && stage === "release") {
264109
+ stage = "verification";
264110
+ refresh(IDLE_TIMEOUT_MS);
264111
+ }
264112
+ },
264113
+ clear: () => {
264114
+ closed = true;
264115
+ if (idle !== void 0) clearTimeout(idle);
264116
+ idle = void 0;
264117
+ unlink();
264118
+ absolute.clear();
264119
+ }
264120
+ };
264121
+ }
264122
+ //#endregion
264050
264123
  //#region ../../packages/agent-core/src/agent/response-review.ts
264051
264124
  const MAX_CANDIDATE_BYTES = 1048576;
264052
264125
  const MAX_CANDIDATE_PARTS = 4096;
264053
- const REVIEW_TIMEOUT_MS = 3e4;
264054
264126
  const REVIEW_CANCEL_SETTLE_MS = 1e3;
264055
264127
  const MAX_REVIEW_ATTEMPTS = 2;
264056
264128
  function createReviewedOutputDispatcher(options) {
@@ -264137,6 +264209,7 @@ function createReviewedOutputDispatcher(options) {
264137
264209
  if (pending.length === 0) return options.dispatch(event);
264138
264210
  if (event.uuid !== stepUuid) throw new Error("Response review step mismatch");
264139
264211
  const currentEpoch = epoch;
264212
+ const isCurrentCandidate = () => epoch === currentEpoch;
264140
264213
  const parts = [...pending];
264141
264214
  reviewing = true;
264142
264215
  const text = parts.map((part) => part.part.type === "text" ? part.part.text : "").join("");
@@ -264150,8 +264223,9 @@ function createReviewedOutputDispatcher(options) {
264150
264223
  for (let attempt = 1; attempt <= MAX_REVIEW_ATTEMPTS; attempt += 1) {
264151
264224
  options.signal.throwIfAborted();
264152
264225
  if (epoch !== currentEpoch) throw new Error("Response review candidate changed during verification");
264153
- const deadline = createDeadlineAbortSignal(options.signal, REVIEW_TIMEOUT_MS);
264226
+ const deadline = createResponseReviewDeadline(options.signal);
264154
264227
  let requested;
264228
+ const pendingOperations = [];
264155
264229
  let attemptRejection;
264156
264230
  let attemptNativeRejection = false;
264157
264231
  try {
@@ -264160,6 +264234,12 @@ function createReviewedOutputDispatcher(options) {
264160
264234
  turnId: event.turnId,
264161
264235
  step: event.step,
264162
264236
  signal: deadline.signal,
264237
+ onProgress: (progress) => {
264238
+ if (isCurrentCandidate()) deadline.progress(progress);
264239
+ },
264240
+ onPendingOperation: (operation) => {
264241
+ pendingOperations.push(operation.then(() => void 0, () => void 0));
264242
+ },
264163
264243
  onRejected: (decision) => {
264164
264244
  if (!deadline.signal.aborted) {
264165
264245
  const message = rejectionMessage(decision);
@@ -264169,13 +264249,14 @@ function createReviewedOutputDispatcher(options) {
264169
264249
  }
264170
264250
  });
264171
264251
  approval = await abortable(requested, deadline.signal);
264252
+ deadline.throwIfAborted();
264172
264253
  rejection = attemptRejection;
264173
264254
  nativeRejection = attemptNativeRejection;
264174
264255
  break;
264175
264256
  } catch {
264176
264257
  options.signal.throwIfAborted();
264177
264258
  if (attemptRejection !== void 0) throw new Error(attemptRejection);
264178
- if (deadline.timedOut() && attempt < MAX_REVIEW_ATTEMPTS && requested !== void 0 && epoch === currentEpoch && await waitForCancelledReview(requested)) continue;
264259
+ if (deadline.timedOut() && attempt < MAX_REVIEW_ATTEMPTS && requested !== void 0 && epoch === currentEpoch && await waitForCancelledReview(requested, pendingOperations)) continue;
264179
264260
  throw new Error(deadline.timedOut() ? "Response review timed out" : "Response review failed");
264180
264261
  } finally {
264181
264262
  deadline.clear();
@@ -264212,10 +264293,16 @@ function createReviewedOutputDispatcher(options) {
264212
264293
  if (epoch === currentEpoch) clear();
264213
264294
  }
264214
264295
  }
264215
- async function waitForCancelledReview(requested) {
264296
+ async function waitForCancelledReview(requested, pendingOperations) {
264216
264297
  const deadline = createDeadlineAbortSignal(options.signal, REVIEW_CANCEL_SETTLE_MS);
264217
264298
  try {
264218
264299
  await abortable(requested.then(() => void 0, () => void 0), deadline.signal);
264300
+ let observed = 0;
264301
+ while (observed < pendingOperations.length) {
264302
+ const batch = pendingOperations.slice(observed);
264303
+ observed = pendingOperations.length;
264304
+ await abortable(Promise.all(batch), deadline.signal);
264305
+ }
264219
264306
  options.signal.throwIfAborted();
264220
264307
  return true;
264221
264308
  } catch {
@@ -268200,12 +268287,14 @@ function readPayload(result, includeError = false) {
268200
268287
  /** Receipts never enter ordinary tool execution, hooks, replay or model history. */
268201
268288
  function createGuardResponseReviewer(host) {
268202
268289
  const { language, guardVersion } = host;
268203
- return async ({ text, turnId, step, signal, onRejected }) => {
268290
+ return async ({ text, turnId, step, signal, onRejected, onPendingOperation, onProgress }) => {
268204
268291
  const input = {
268205
268292
  text,
268206
268293
  turnId,
268207
268294
  step,
268208
- signal
268295
+ signal,
268296
+ onPendingOperation,
268297
+ onProgress
268209
268298
  };
268210
268299
  const targetSha256 = digest(text);
268211
268300
  let stage = "setup";
@@ -268232,6 +268321,10 @@ function createGuardResponseReviewer(host) {
268232
268321
  const blocked = (reason, findings) => {
268233
268322
  record(signal.aborted ? "cancelled" : reason, findings);
268234
268323
  };
268324
+ const track = (operation) => {
268325
+ onPendingOperation?.(operation.then(() => void 0, () => void 0));
268326
+ return operation;
268327
+ };
268235
268328
  try {
268236
268329
  if (!/^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/.test(language) || /^(auto|all)$/i.test(language) || guardVersion.trim().length === 0) return blocked("invalid_settings");
268237
268330
  const resolve = () => {
@@ -268244,13 +268337,14 @@ function createGuardResponseReviewer(host) {
268244
268337
  const current = () => !signal.aborted && resolve()?.client === connection.client;
268245
268338
  if (!current()) return blocked("connection_changed");
268246
268339
  stage = "native";
268247
- const native = await abortable(host.reviewNative({ ...input }), signal);
268340
+ onProgress?.("native.start");
268341
+ const native = await abortable(track(host.reviewNative({ ...input })), signal);
268248
268342
  if (!current()) return blocked("connection_changed");
268249
268343
  if (native === void 0) return blocked("native_unavailable");
268250
268344
  if (!passed(native.nativeness) || !passed(native.orthography)) return blocked("native_rejected");
268251
268345
  const authorized = async (name) => {
268252
268346
  if (!current()) return false;
268253
- return passed(await abortable(host.authorize({
268347
+ return passed(await abortable(track(host.authorize({
268254
268348
  toolName: qualifyMcpToolName(SERVER_NAME, name),
268255
268349
  targetSha256,
268256
268350
  language,
@@ -268259,11 +268353,12 @@ function createGuardResponseReviewer(host) {
268259
268353
  turnId,
268260
268354
  step,
268261
268355
  signal
268262
- }), signal)) && current();
268356
+ })), signal)) && current();
268263
268357
  };
268264
268358
  stage = "release";
268359
+ onProgress?.("release.start");
268265
268360
  if (!await authorized("release_response")) return blocked("release_denied");
268266
- const releaseResult = await abortable(connection.client.callTool("release_response", {
268361
+ const releaseResult = await abortable(track(connection.client.callTool("release_response", {
268267
268362
  target_text: text,
268268
268363
  language,
268269
268364
  content_type: "prose",
@@ -268272,7 +268367,7 @@ function createGuardResponseReviewer(host) {
268272
268367
  nativeness: true,
268273
268368
  orthography: true
268274
268369
  }
268275
- }, signal), signal);
268370
+ }, signal)), signal);
268276
268371
  if (!current()) return blocked("connection_changed");
268277
268372
  if (releaseResult.isError) return blocked("release_rejected", rejectionFindings(readPayload(releaseResult, true)));
268278
268373
  const released = readPayload(releaseResult);
@@ -268280,8 +268375,9 @@ function createGuardResponseReviewer(host) {
268280
268375
  if (!current()) return blocked("connection_changed");
268281
268376
  if (status !== "PASS" || release_allowed !== true || blocking === true || typeof release_token !== "string" || release_token.length === 0 || release_token.length > 65536) return blocked("release_rejected", rejectionFindings(released));
268282
268377
  stage = "verification";
268378
+ onProgress?.("verification.start");
268283
268379
  if (!await authorized("verify_release_token")) return blocked("verification_denied");
268284
- const { status: verificationStatus, valid, checks, payload } = readPayload(await abortable(connection.client.callTool("verify_release_token", {
268380
+ const { status: verificationStatus, valid, checks, payload } = readPayload(await abortable(track(connection.client.callTool("verify_release_token", {
268285
268381
  release_token,
268286
268382
  source_text: "",
268287
268383
  target_text: text,
@@ -268289,7 +268385,7 @@ function createGuardResponseReviewer(host) {
268289
268385
  purpose: "response",
268290
268386
  content_type: "prose",
268291
268387
  short_text_reviewed: false
268292
- }, signal), signal)) ?? {};
268388
+ }, signal)), signal)) ?? {};
268293
268389
  if (!current()) return blocked("connection_changed");
268294
268390
  if (verificationStatus !== "PASS" || valid !== true || !isPlainRecord(checks) || !CHECK_NAMES.every((name) => checks[name] === true) || !isPlainRecord(payload)) return blocked("verification_rejected");
268295
268391
  const { v, source_sha256, target_sha256, language: verifiedLanguage, purpose, content_type, short_text_reviewed, exp, iat } = payload;
@@ -268369,6 +268465,9 @@ async function reviewNativeResponse(agent, language, input) {
268369
268465
  };
268370
268466
  const options = {
268371
268467
  signal,
268468
+ onExecution: (completion) => {
268469
+ input.onPendingOperation?.(completion.then(() => void 0, () => void 0));
268470
+ },
268372
268471
  requestLogFields: {
268373
268472
  turnStep: `review:${input.turnId}.${String(input.step)}`,
268374
268473
  attempt: `${attempt}/2`
@@ -268377,13 +268476,16 @@ async function reviewNativeResponse(agent, language, input) {
268377
268476
  const prompt = attempt === 1 ? NATIVE_REVIEW_PROMPT : `${NATIVE_REVIEW_PROMPT}\nThe previous review did not satisfy the required JSON format. Return exactly one object with the two required boolean keys nativeness and orthography, no wrapper or additional keys. Use true or false, not strings or null. Set a check to false if it fails or you are uncertain.`;
268378
268477
  try {
268379
268478
  signal.throwIfAborted();
268380
- const response = await abortable(agent.generate(provider, prompt, [], [createUserMessage(candidateMessage)], { onMessagePart: (part) => {
268479
+ const requested = agent.generate(provider, prompt, [], [createUserMessage(candidateMessage)], { onMessagePart: (part) => {
268381
268480
  if (part.type === "text") receivedBytes += Buffer.byteLength(part.text);
268382
268481
  if (receivedBytes > MAX_REVIEW_RESULT_BYTES) {
268383
268482
  outputLimitExceeded = true;
268384
268483
  throw new Error("Native review output exceeds limit");
268385
268484
  }
268386
- } }, options), signal);
268485
+ if (!signal.aborted && part.type === "text" && part.text.trim().length > 0) input.onProgress?.("native.text");
268486
+ } }, options);
268487
+ input.onPendingOperation?.(requested.then(() => void 0, () => void 0));
268488
+ const response = await abortable(requested, signal);
268387
268489
  if (response.usage !== null) agent.usage.record(modelAlias, response.usage, "turn");
268388
268490
  signal.throwIfAborted();
268389
268491
  if (response.message.toolCalls.length > 0) return invalid("tool_calls");
@@ -299939,6 +300041,36 @@ function mergeCallerMcpServers(base, callerServers) {
299939
300041
  }
299940
300042
  //#endregion
299941
300043
  //#region ../../packages/agent-core/src/plugin/agent-spine-runtime.ts
300044
+ const PROMPT_EVENTS = new Set([
300045
+ "UserPromptSubmit",
300046
+ "TurnStart",
300047
+ "SubagentStart"
300048
+ ]);
300049
+ /** AgentSpine reads prompt text; binary media stays in the original model input. */
300050
+ function projectAgentSpinePrompt(input) {
300051
+ if (typeof input["hook_event_name"] !== "string" || !PROMPT_EVENTS.has(input["hook_event_name"]) || !Array.isArray(input["prompt"])) return input;
300052
+ let changed = false;
300053
+ const prompt = input["prompt"].map((part) => {
300054
+ if (!isRecord$1(part)) return part;
300055
+ const field = part["type"] === "image_url" ? "imageUrl" : part["type"] === "audio_url" ? "audioUrl" : part["type"] === "video_url" ? "videoUrl" : void 0;
300056
+ if (field === void 0 || !isRecord$1(part[field]) || typeof part[field]["url"] !== "string") return part;
300057
+ const serialized = JSON.stringify(part);
300058
+ changed = true;
300059
+ return {
300060
+ type: "media_reference",
300061
+ media_type: part["type"],
300062
+ sha256: createHash("sha256").update(serialized).digest("hex"),
300063
+ original_bytes: Buffer.byteLength(serialized)
300064
+ };
300065
+ });
300066
+ return changed ? {
300067
+ ...input,
300068
+ prompt
300069
+ } : input;
300070
+ }
300071
+ function isRecord$1(value) {
300072
+ return typeof value === "object" && value !== null && !Array.isArray(value);
300073
+ }
299942
300074
  const EMPTY_RUNTIME = Object.freeze({
299943
300075
  AGENTSPINE_KING_TIMELINE_SOURCE: "",
299944
300076
  AGENTSPINE_KING_WIRE_PROTOCOL_VERSION: "",
@@ -300485,9 +300617,11 @@ var Session$1 = class {
300485
300617
  this.experimentalFlags = options.experimentalFlags ?? new FlagResolver();
300486
300618
  this.agentSpineRuntime = this.experimentalFlags.enabled("agent_spine_timeline") ? new AgentSpineSessionRuntime(options.agentSpineRuntime) : void 0;
300487
300619
  this.visionReader = (this.experimentalFlags.enabled("vision_reader") ? createVisionReader(options.config?.services?.visionReader) : void 0) ?? createManagedMediaVisionReader(options.toolServices?.media);
300620
+ const agentSpineHooks = new Set(options.agentSpineRuntime?.hooks);
300488
300621
  this.hookEngine = new HookEngine(options.hooks, {
300489
300622
  cwd: options.kaos.getcwd(),
300490
300623
  sessionId: options.id,
300624
+ runtimeInput: (hook, input) => agentSpineHooks.has(hook) ? projectAgentSpinePrompt(input) : input,
300491
300625
  runtimeEnv: (hook) => {
300492
300626
  const hostEnv = options.hookRuntimeEnv?.(hook);
300493
300627
  const timelineEnv = this.agentSpineRuntime?.forHook(hook);
@@ -335769,7 +335903,6 @@ var BlunCore = class {
335769
335903
  };
335770
335904
  }
335771
335905
  agentSpineRuntimeRecipients(pluginHooks, mcpConfig) {
335772
- if (!this.experimentalFlags.enabled("agent_spine_timeline")) return void 0;
335773
335906
  const plugin = this.plugins.get("agent-spine");
335774
335907
  if (plugin?.state !== "ok" || !plugin.enabled) return void 0;
335775
335908
  const hooks = pluginHooks.filter((hook) => hook.env?.["BLUN_PLUGIN_ROOT"] === plugin.root);