@sema-agent/client-core 0.95.0 → 0.97.0

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 (233) hide show
  1. package/CHANGELOG.md +269 -0
  2. package/README.md +61 -31
  3. package/dist/adapt/arms.js +7 -1
  4. package/dist/adapt/toolCards.d.ts +1 -0
  5. package/dist/adapt/toolCards.js +16 -3
  6. package/dist/adapt/wireShapes.js +2 -16
  7. package/dist/adapter/activeRunSelfHeal.d.ts +4 -2
  8. package/dist/adapter/activeRunSelfHeal.js +32 -6
  9. package/dist/adapter/downstream/eventToSdkMessage.js +11 -3
  10. package/dist/adapter/downstream/terminalToSdkResult.js +14 -11
  11. package/dist/adapter/runStream.js +30 -47
  12. package/dist/agentSession/backgroundView.js +2 -15
  13. package/dist/agentSession/engineAgentRegistryRead.js +5 -4
  14. package/dist/agentSession/engineSessionBackgroundStop.js +22 -15
  15. package/dist/agentsWireCaps.d.ts +1 -1
  16. package/dist/agentsWireCaps.js +29 -12
  17. package/dist/approvalDecidedByCapability.d.ts +2 -0
  18. package/dist/approvalDecidedByCapability.js +0 -1
  19. package/dist/approvalsStreamLiveCapability.d.ts +2 -0
  20. package/dist/approvalsStreamLiveCapability.js +3 -2
  21. package/dist/attachmentSeatCapability.d.ts +2 -0
  22. package/dist/attachmentSeatCapability.js +0 -1
  23. package/dist/callerCwdCapability.d.ts +2 -4
  24. package/dist/callerCwdCapability.js +0 -7
  25. package/dist/centerWiringCapability.d.ts +2 -4
  26. package/dist/centerWiringCapability.js +4 -9
  27. package/dist/classifierVerdictWire.js +4 -16
  28. package/dist/clientSlice.js +1 -1
  29. package/dist/cloudConfigWireCaps.d.ts +4 -4
  30. package/dist/cloudConfigWireCaps.js +241 -85
  31. package/dist/coreValuePorts.d.ts +11 -7
  32. package/dist/coreValuePorts.js +5 -8
  33. package/dist/credentialProvenance.d.ts +28 -0
  34. package/dist/credentialProvenance.js +76 -0
  35. package/dist/decideFailureNote.js +3 -1
  36. package/dist/decideReceipt.js +7 -10
  37. package/dist/declared/supportFloorDeclared.js +3 -3
  38. package/dist/deviceExecutorManagementCapability.d.ts +2 -0
  39. package/dist/deviceExecutorManagementCapability.js +3 -2
  40. package/dist/displayUntrusted.d.ts +10 -3
  41. package/dist/displayUntrusted.js +371 -60
  42. package/dist/engineCapReader.d.ts +8 -2
  43. package/dist/engineCapReader.js +15 -7
  44. package/dist/engineCapsCache.d.ts +17 -3
  45. package/dist/engineCapsCache.js +268 -43
  46. package/dist/engineDirectory.d.ts +79 -0
  47. package/dist/engineDirectory.js +186 -0
  48. package/dist/engineErrorCodes.d.ts +17 -1
  49. package/dist/engineErrorCodes.js +46 -16
  50. package/dist/engineNoticeCodes.d.ts +17 -0
  51. package/dist/engineNoticeCodes.js +54 -1
  52. package/dist/executionLaneCapability.d.ts +2 -0
  53. package/dist/executionLaneCapability.js +3 -2
  54. package/dist/fileHistoryCaptureCapability.d.ts +16 -0
  55. package/dist/fileHistoryCaptureCapability.js +98 -2
  56. package/dist/fileHistoryLegNotice.js +4 -0
  57. package/dist/fleet/bgSubagentRow.d.ts +15 -0
  58. package/dist/fleet/bgSubagentRow.js +67 -0
  59. package/dist/fleet/fleetLedger.d.ts +1 -0
  60. package/dist/fleet/fleetLedger.js +164 -126
  61. package/dist/fleet/fleetProjection.d.ts +1 -0
  62. package/dist/fleet/fleetProjection.js +10 -3
  63. package/dist/fleetTaskDesc.js +4 -17
  64. package/dist/gateOutcome.d.ts +12 -1
  65. package/dist/gateOutcome.js +56 -1
  66. package/dist/gateVocabulary.d.ts +2 -2
  67. package/dist/gateVocabulary.js +4 -3
  68. package/dist/generated/engineAskFaceTables.js +1 -1
  69. package/dist/generated/engineFactTables.d.ts +5 -3
  70. package/dist/generated/engineFactTables.js +17 -4
  71. package/dist/generated/engineNoticeTables.js +12 -3
  72. package/dist/generated/sdkFactTables.d.ts +2 -0
  73. package/dist/generated/sdkFactTables.js +7 -0
  74. package/dist/generated/settingsSchemaFns.d.ts +3 -0
  75. package/dist/generated/settingsSchemaFns.js +53 -0
  76. package/dist/generated/toolNameTables.d.ts +1 -1
  77. package/dist/generated/toolNameTables.js +1 -42
  78. package/dist/hitl/approvalDecisionNoteAudit.js +2 -2
  79. package/dist/hitl/approvalEndingCause.d.ts +2 -0
  80. package/dist/hitl/approvalEndingCause.js +26 -21
  81. package/dist/hitl/approvalHolds.d.ts +16 -1
  82. package/dist/hitl/approvalHolds.js +52 -12
  83. package/dist/hitl/askGateWire.js +1 -0
  84. package/dist/hitl/askParkRowRouting.js +1 -1
  85. package/dist/hitl/automaticAnswerVerdict.d.ts +1 -1
  86. package/dist/hitl/automaticAnswerVerdict.js +13 -10
  87. package/dist/hitl/decisionConflict.js +4 -5
  88. package/dist/hitl/decisionRefusal.d.ts +8 -0
  89. package/dist/hitl/decisionRefusal.js +55 -14
  90. package/dist/hitl/frameRouter.d.ts +3 -1
  91. package/dist/hitl/frameRouter.js +58 -39
  92. package/dist/hitl/gateLedger.d.ts +0 -5
  93. package/dist/hitl/gateLedger.js +0 -21
  94. package/dist/hitl/hitlBridge.d.ts +6 -2
  95. package/dist/hitl/hitlBridge.js +46 -11
  96. package/dist/hitl/localAllowRule.d.ts +1 -0
  97. package/dist/hitl/localAllowRule.js +2 -2
  98. package/dist/hitl/noAllowCause.d.ts +2 -1
  99. package/dist/hitl/noAllowCause.js +3 -2
  100. package/dist/hitl/parkResolver.d.ts +1 -0
  101. package/dist/hitl/parkResolver.js +14 -56
  102. package/dist/hitl/parkRowBirthWait.d.ts +1 -0
  103. package/dist/hitl/parkRowBirthWait.js +2 -2
  104. package/dist/hitl/planReviewWire.d.ts +1 -0
  105. package/dist/hitl/planReviewWire.js +98 -20
  106. package/dist/hitl/replayedApproval.d.ts +3 -3
  107. package/dist/hitl/replayedApproval.js +21 -16
  108. package/dist/hitl/sessionPolicyDeliverable.d.ts +0 -1
  109. package/dist/hitl/sessionPolicyDeliverable.js +17 -17
  110. package/dist/hitl/sessionPolicyWire.d.ts +2 -0
  111. package/dist/hitl/sessionPolicyWire.js +15 -3
  112. package/dist/hitl/standingRows.d.ts +19 -0
  113. package/dist/hitl/standingRows.js +148 -0
  114. package/dist/hitl/suspendedReopen.js +2 -1
  115. package/dist/hitl/toolApprovalWire.d.ts +22 -10
  116. package/dist/hitl/toolApprovalWire.js +286 -47
  117. package/dist/hookMatcherVerdict.d.ts +1 -9
  118. package/dist/hookMatcherVerdict.js +21 -61
  119. package/dist/hooksWireCaps.js +2 -2
  120. package/dist/hostCallbackGuard.d.ts +1 -0
  121. package/dist/hostCallbackGuard.js +6 -0
  122. package/dist/index.d.ts +26 -12
  123. package/dist/index.js +20 -10
  124. package/dist/liveModelCatalog.d.ts +13 -0
  125. package/dist/liveModelCatalog.js +56 -0
  126. package/dist/mcpProbeCapability.d.ts +2 -4
  127. package/dist/mcpProbeCapability.js +3 -8
  128. package/dist/mcpWireCaps.d.ts +1 -0
  129. package/dist/mcpWireCaps.js +4 -1
  130. package/dist/memoryComplianceCapability.d.ts +2 -4
  131. package/dist/memoryComplianceCapability.js +3 -8
  132. package/dist/memoryOriginCapability.d.ts +2 -4
  133. package/dist/memoryOriginCapability.js +3 -8
  134. package/dist/memorySpecWire.js +2 -2
  135. package/dist/modelIdentityResolvability.d.ts +2 -2
  136. package/dist/modelIdentityResolvability.js +14 -5
  137. package/dist/noticePresentation.d.ts +68 -0
  138. package/dist/noticePresentation.js +247 -0
  139. package/dist/outputStyleSeatCapability.d.ts +2 -1
  140. package/dist/outputStyleSeatCapability.js +0 -4
  141. package/dist/ownKey.d.ts +2 -0
  142. package/dist/ownKey.js +18 -0
  143. package/dist/peerFrames.js +13 -9
  144. package/dist/peerLaneCapability.d.ts +2 -0
  145. package/dist/peerLaneCapability.js +3 -2
  146. package/dist/permissionModesCapability.d.ts +2 -4
  147. package/dist/permissionModesCapability.js +0 -7
  148. package/dist/permissionRuleIssue.js +0 -3
  149. package/dist/permissionRulesWriteCapability.d.ts +2 -0
  150. package/dist/permissionRulesWriteCapability.js +3 -2
  151. package/dist/permissionsAllowGrantCapability.d.ts +2 -0
  152. package/dist/permissionsAllowGrantCapability.js +4 -2
  153. package/dist/permissionsDeclaration.d.ts +2 -0
  154. package/dist/permissionsDeclaration.js +72 -59
  155. package/dist/printPermissionOutcome.d.ts +12 -0
  156. package/dist/printPermissionOutcome.js +50 -0
  157. package/dist/printToolResultFrame.d.ts +1 -0
  158. package/dist/printToolResultFrame.js +5 -9
  159. package/dist/promptBlockedCopy.d.ts +5 -0
  160. package/dist/promptBlockedCopy.js +35 -0
  161. package/dist/protocolSegment.d.ts +9 -6
  162. package/dist/protocolSegment.js +31 -21
  163. package/dist/providerRejection.d.ts +4 -0
  164. package/dist/providerRejection.js +75 -14
  165. package/dist/replayBody.d.ts +12 -0
  166. package/dist/replayBody.js +25 -0
  167. package/dist/request/taskRequest.d.ts +1 -4
  168. package/dist/request/taskRequest.js +74 -19
  169. package/dist/resumeRefusalCopy.js +9 -25
  170. package/dist/retryStatus.d.ts +9 -1
  171. package/dist/retryStatus.js +9 -2
  172. package/dist/rewindArchiveCapability.d.ts +3 -13
  173. package/dist/rewindArchiveCapability.js +12 -58
  174. package/dist/rewindArchiveReading.d.ts +17 -0
  175. package/dist/rewindArchiveReading.js +55 -0
  176. package/dist/ruleCompileVerdict.d.ts +9 -1
  177. package/dist/ruleCompileVerdict.js +29 -4
  178. package/dist/runTerminal.d.ts +48 -5
  179. package/dist/runTerminal.js +215 -12
  180. package/dist/sandboxWire.js +13 -0
  181. package/dist/sdkWireTransit.d.ts +2 -1
  182. package/dist/sdkWireTransit.js +1 -0
  183. package/dist/sealedKeyCapability.d.ts +2 -4
  184. package/dist/sealedKeyCapability.js +0 -7
  185. package/dist/seam.js +4 -2
  186. package/dist/seatContract.d.ts +76 -11
  187. package/dist/seatContract.js +222 -27
  188. package/dist/sessionPolicyCapability.d.ts +2 -0
  189. package/dist/sessionPolicyCapability.js +3 -2
  190. package/dist/sessionReadRedactionCapability.d.ts +2 -4
  191. package/dist/sessionReadRedactionCapability.js +0 -7
  192. package/dist/sessionSyncFileHistory.d.ts +4 -0
  193. package/dist/sessionSyncFileHistory.js +66 -0
  194. package/dist/sessionSyncFileHistoryCapability.d.ts +2 -1
  195. package/dist/sessionSyncFileHistoryCapability.js +3 -5
  196. package/dist/skillToolListsCapability.d.ts +2 -1
  197. package/dist/skillToolListsCapability.js +3 -5
  198. package/dist/sqlEngineCapability.d.ts +2 -0
  199. package/dist/sqlEngineCapability.js +4 -3
  200. package/dist/steerPriorityCapability.d.ts +2 -1
  201. package/dist/steerPriorityCapability.js +3 -5
  202. package/dist/storePostureCapability.d.ts +2 -4
  203. package/dist/storePostureCapability.js +4 -9
  204. package/dist/subagent/engineSubagentSteer.js +7 -5
  205. package/dist/subagentContentStore.js +4 -15
  206. package/dist/supportFloor.d.ts +1 -0
  207. package/dist/supportFloor.js +13 -9
  208. package/dist/systemReminderTag.d.ts +1 -0
  209. package/dist/systemReminderTag.js +8 -0
  210. package/dist/toolHistoryBadTurn.js +3 -2
  211. package/dist/toolHistoryMismatch.d.ts +0 -3
  212. package/dist/toolHistoryMismatch.js +3 -7
  213. package/dist/toolResult.d.ts +12 -1
  214. package/dist/toolResult.js +55 -1
  215. package/dist/toolResultContent.d.ts +2 -0
  216. package/dist/toolResultContent.js +63 -0
  217. package/dist/usageAnalytics.js +40 -16
  218. package/dist/userInvokedSkillsCapability.d.ts +2 -1
  219. package/dist/userInvokedSkillsCapability.js +3 -5
  220. package/dist/webSearchBackendCapability.d.ts +2 -0
  221. package/dist/webSearchBackendCapability.js +3 -2
  222. package/dist/wireErrorTriage.d.ts +2 -0
  223. package/dist/wireErrorTriage.js +91 -38
  224. package/dist/wireFailureShape.d.ts +20 -3
  225. package/dist/wireFailureShape.js +128 -72
  226. package/dist/workflowsJournalCapability.d.ts +2 -0
  227. package/dist/workflowsJournalCapability.js +3 -2
  228. package/dist/writeProtectionCapability.d.ts +2 -0
  229. package/dist/writeProtectionCapability.js +3 -2
  230. package/docs/INTEGRATION-CLIENTS.md +1784 -20
  231. package/package.json +5 -5
  232. package/dist/engineRefusedToolName.d.ts +0 -1
  233. package/dist/engineRefusedToolName.js +0 -8
@@ -38,18 +38,12 @@ function scanForSecretLiteral(s) {
38
38
  return true;
39
39
  return s.split(/[^A-Za-z0-9_-]+/).some(tok => tok !== '' && SECRET_LITERAL_RE.test(tok));
40
40
  }
41
- function decodeAsciiEscapes(s) {
42
- return s.replace(/%[0-9A-Fa-f]{2}/g, esc => {
43
- const code = Number.parseInt(esc.slice(1), 16);
44
- return code < 0x80 ? String.fromCharCode(code) : esc;
45
- });
46
- }
47
41
  export function looksLikeKnownSecret(s) {
48
42
  let cur = s;
49
43
  for (let layer = 0; layer < 4; layer++) {
50
44
  if (scanForSecretLiteral(cur))
51
45
  return true;
52
- const next = decodeAsciiEscapes(cur);
46
+ const next = percentDecodeForMatching(cur).decoded;
53
47
  if (next === cur)
54
48
  break;
55
49
  cur = next;
@@ -261,21 +255,23 @@ function isKnownSecretSegment(value) {
261
255
  const core = valueCoreForJudging(value);
262
256
  return core.length > 0 && looksLikeKnownSecret(core);
263
257
  }
264
- function queryCandidates(src, base, query, edits, soft) {
258
+ function queryCandidates(src, base, query, edits, soft, byShape = false) {
259
+ const relax = byShape === 'relaxed';
265
260
  let pos = base;
266
261
  for (const part of query.split('&')) {
267
262
  if (part.length > 0) {
268
263
  const eq = part.indexOf('=');
269
264
  if (eq === -1) {
270
265
  const at = pos;
271
- if (!slotIsWholeMarker(part, soft && ((i) => soft(at + i))))
266
+ if (!slotIsWholeMarker(part, soft && ((i) => soft(at + i))) && (!byShape || queryPartCarriesCredential(null, part, relax)))
272
267
  pushEdit(edits, src, pos, pos + part.length, REDACTED_QUERY);
273
268
  }
274
269
  else {
275
270
  const value = part.slice(eq + 1);
276
271
  const at = pos + eq + 1;
277
- if (value.length > 0 && !slotIsWholeMarker(value, soft && ((i) => soft(at + i))))
272
+ if (value.length > 0 && !slotIsWholeMarker(value, soft && ((i) => soft(at + i))) && (!byShape || queryPartCarriesCredential(part.slice(0, eq), value, relax))) {
278
273
  pushEdit(edits, src, pos + eq + 1, pos + part.length, REDACTED_QUERY);
274
+ }
279
275
  }
280
276
  }
281
277
  pos += part.length + 1;
@@ -319,7 +315,7 @@ function pathCandidates(src, base, path, opaqueSegments, edits, soft) {
319
315
  pos += segment.length + 1;
320
316
  }
321
317
  }
322
- function urlTokenCandidatesAt(src, start, end, edits, soft) {
318
+ function urlTokenCandidatesAt(src, start, end, edits, soft, byShape = false) {
323
319
  const token = src.slice(start, end);
324
320
  const schemeEnd = token.indexOf('://') + '://'.length;
325
321
  const scheme = token.slice(0, schemeEnd);
@@ -337,7 +333,7 @@ function urlTokenCandidatesAt(src, start, end, edits, soft) {
337
333
  const path = question === -1 ? beforeHash : beforeHash.slice(0, question);
338
334
  pathCandidates(src, start + tailStart, path, !/^file:/i.test(scheme), edits, soft);
339
335
  if (question !== -1)
340
- queryCandidates(src, start + tailStart + question + 1, beforeHash.slice(question + 1), edits, soft);
336
+ queryCandidates(src, start + tailStart + question + 1, beforeHash.slice(question + 1), edits, soft, byShape);
341
337
  if (hash !== -1) {
342
338
  const rawFragment = tail.slice(hash + 1);
343
339
  const at = start + tailStart + hash + 1;
@@ -345,7 +341,7 @@ function urlTokenCandidatesAt(src, start, end, edits, soft) {
345
341
  pushEdit(edits, src, start + tailStart + hash + 1, end, REDACTED_FRAGMENT);
346
342
  }
347
343
  }
348
- function urlCandidates(text, out, regions, unitPos = () => false, soft) {
344
+ function urlCandidates(text, out, regions, unitPos = () => false, soft, byShape = false) {
349
345
  let mark = text.indexOf('://');
350
346
  while (mark !== -1) {
351
347
  let start = mark;
@@ -410,7 +406,7 @@ function urlCandidates(text, out, regions, unitPos = () => false, soft) {
410
406
  end = runStart;
411
407
  end = trailingCloserEnd(text, start, mark + 3, end, unitPos);
412
408
  const tokenEdits = [];
413
- urlTokenCandidatesAt(text, start, end, tokenEdits, soft);
409
+ urlTokenCandidatesAt(text, start, end, tokenEdits, soft, byShape);
414
410
  editsToCandidates(tokenEdits, 'url', out, end);
415
411
  const token = text.slice(start, end);
416
412
  const schemeEnd = token.indexOf('://') + 3;
@@ -813,8 +809,16 @@ const isNameBlank = (cp) => NAME_BLANK_RANGES.some(([a, b]) => cp >= a && cp <=
813
809
  const isFoldedBlank = (c) => isNameBlank(c) && isRegexSpace(c);
814
810
  const isUnicodeBlank = (c) => (c > 0x7f && isRegexSpace(c) && c !== 0xfeff) || isNameBlank(c);
815
811
  const SECRET_SCHEME_WORD = /(?:\b|(?<=\\[A-Za-z"']))(?!(?<=(?<!\\)\\)(?:bearer|basic|token)\\t)(bearer|basic|token)(?:[\s:=\uFF1A\uFF1D\u115F\u1160\u3164\uFFA0\u2800]|\\t)+/gi;
812
+ const SCHEME_LEFT_STRICT = String.raw `(?:\b|(?<=\\[A-Za-z"']))`;
813
+ const SCHEME_WORD_OUTER_SOURCE = widenLeft(SECRET_SCHEME_WORD.source, SCHEME_LEFT_STRICT, String.raw `(?=bearer|basic)`);
816
814
  const schemeWordIsInner = (word, tokenInner) => tokenInner || word.toLowerCase() !== 'token';
817
815
  const SECRET_LABELLED_WORD = /(?:\b|(?<=_)|(?<=\\[A-Za-z"']))((?:access[-_ ]?|refresh[-_ ]?|id[-_ ]?|client[-_ ]?|api[-_ ]?|x[-_]api[-_ ]?|session[-_ ]?|auth[-_ ]?)?(?:token|key|secret|password|authorization|credential)s?)(?:\\*["'])?(?:[\s\u115F\u1160\u3164\uFFA0\u2800]|\\t)*[:=\uFF1A\uFF1D](?:[\s\u115F\u1160\u3164\uFFA0\u2800]|\\t)*/gi;
816
+ function widenLeft(source, strictLeft, extraAlt) {
817
+ return source.startsWith(strictLeft) ? `${strictLeft.slice(0, -1)}|${extraAlt})${source.slice(strictLeft.length)}` : source;
818
+ }
819
+ const LABEL_LEFT_STRICT = String.raw `(?:\b|(?<=_)|(?<=\\[A-Za-z"']))`;
820
+ const LABEL_LEFT_GLUED_ALT = String.raw `(?<=[A-Za-z0-9])(?:(?=(?:token|secret|password|authorization|credential)(?!s))|(?<!mon|tur|don|hoc|whis|joc|lac)(?=key(?!s)))`;
821
+ const LABEL_WORD_OUTER_SOURCE = widenLeft(SECRET_LABELLED_WORD.source, LABEL_LEFT_STRICT, LABEL_LEFT_GLUED_ALT);
818
822
  const DIAGNOSTIC_VALUE_WORDS = new Set([
819
823
  'missing', 'required', 'invalid', 'expired', 'unauthorized', 'unauthenticated', 'authentication',
820
824
  'authorization', 'credential', 'credentials', 'unsupported', 'malformed', 'rejected', 'absent',
@@ -1266,6 +1270,35 @@ function percentDecodeForMatching(text) {
1266
1270
  offsets.push(text.length);
1267
1271
  return { decoded: chars.join(''), offsets };
1268
1272
  }
1273
+ const PERCENT_ESCAPE_ASCII = /%[0-7][0-9A-Fa-f]/;
1274
+ const MAX_PERCENT_VIEW_LAYERS = 3;
1275
+ function percentLayers(text) {
1276
+ const out = [];
1277
+ if (!PERCENT_ESCAPE_ASCII.test(text))
1278
+ return out;
1279
+ let cur = text;
1280
+ let ps = null;
1281
+ let pe = null;
1282
+ for (let layer = 0; layer < MAX_PERCENT_VIEW_LAYERS; layer++) {
1283
+ const { decoded, offsets } = percentDecodeForMatching(cur);
1284
+ if (decoded === cur)
1285
+ break;
1286
+ const n = decoded.length;
1287
+ const nps = new Int32Array(n);
1288
+ const npe = new Int32Array(n);
1289
+ for (let k = 0; k < n; k++) {
1290
+ const a = offsets[k] ?? 0;
1291
+ const b = offsets[k + 1] ?? cur.length;
1292
+ nps[k] = ps === null ? a : (ps[a] ?? 0);
1293
+ npe[k] = pe === null ? b : (pe[b - 1] ?? 0);
1294
+ }
1295
+ out.push({ text: decoded, ps: nps, pe: npe });
1296
+ cur = decoded;
1297
+ ps = nps;
1298
+ pe = npe;
1299
+ }
1300
+ return out;
1301
+ }
1269
1302
  function firstLabelCutInPath(path) {
1270
1303
  let cut = -1;
1271
1304
  for (const src of [SECRET_SCHEME_WORD, SECRET_LABELLED_WORD]) {
@@ -1520,7 +1553,7 @@ function quoteSwallowedBefore(S, at) {
1520
1553
  return '';
1521
1554
  return dq === -1 ? "'" : sq === -1 ? '"' : dq < sq ? '"' : "'";
1522
1555
  }
1523
- function labelEndsAt(text, end, atLeast = false) {
1556
+ function labelEndsAt(text, end, atLeast = false, outer = false) {
1524
1557
  let k = end;
1525
1558
  while (k > 0 && isRegexSpace(text.charCodeAt(k - 1)))
1526
1559
  k--;
@@ -1539,7 +1572,7 @@ function labelEndsAt(text, end, atLeast = false) {
1539
1572
  let w = k;
1540
1573
  while (w > 0 && isIdentOrDash(text.charAt(w - 1)))
1541
1574
  w--;
1542
- const re = new RegExp(SECRET_LABELLED_WORD.source, 'iy');
1575
+ const re = new RegExp(outer ? LABEL_WORD_OUTER_SOURCE : SECRET_LABELLED_WORD.source, 'iy');
1543
1576
  for (let s = w; s < k; s++) {
1544
1577
  re.lastIndex = s;
1545
1578
  const m = re.exec(text);
@@ -2097,10 +2130,12 @@ function blockSeqEntries(scan, rawFrom, rawLabel) {
2097
2130
  }
2098
2131
  return out;
2099
2132
  }
2100
- function labelCandidates(S, pass, regions, out, blocks) {
2133
+ function labelCandidates(S, pass, regions, out, blocks, outer = false) {
2101
2134
  const text = S.text;
2102
2135
  const unitAt = (k) => S.unitAt(k);
2103
- const re = pass === 'label' ? SECRET_LABELLED_WORD : SECRET_SCHEME_WORD;
2136
+ const strictSource = pass === 'label' ? SECRET_LABELLED_WORD.source : SECRET_SCHEME_WORD.source;
2137
+ const re = new RegExp(outer ? (pass === 'label' ? LABEL_WORD_OUTER_SOURCE : SCHEME_WORD_OUTER_SOURCE) : strictSource, 'gi');
2138
+ const strictAt = outer ? new RegExp(strictSource, 'iy') : null;
2104
2139
  const schemePass = pass !== 'label';
2105
2140
  const tokenPass = pass === 'token';
2106
2141
  const rawUnitMap = blocks?.view.rawUnit ?? null;
@@ -2346,6 +2381,11 @@ function labelCandidates(S, pass, regions, out, blocks) {
2346
2381
  re.lastIndex = 0;
2347
2382
  hits: for (let hit = re.exec(text); hit !== null; hit = re.exec(text)) {
2348
2383
  const label = hit[1] ?? '';
2384
+ if (strictAt !== null) {
2385
+ strictAt.lastIndex = hit.index;
2386
+ if (strictAt.test(text))
2387
+ continue;
2388
+ }
2349
2389
  if (hit.index >= skipFrom && hit.index < skipTo)
2350
2390
  continue;
2351
2391
  if (schemePass && (label.toLowerCase() === 'token') !== tokenPass)
@@ -2665,6 +2705,19 @@ function isAuthScheme(value) {
2665
2705
  const isKeyWordChar = (c) => (c >= 0x30 && c <= 0x39) || (c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a) || c === 0x5f || c === 0x2d;
2666
2706
  const isAlnumCode = (c) => (c >= 0x30 && c <= 0x39) || (c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a);
2667
2707
  const PROSE_LITERAL_MIN_RUN = 16;
2708
+ const PROSE_SHORT_LITERAL_MIN_TAIL = 6;
2709
+ function shortLiteralTail(word) {
2710
+ const lit = SECRET_LITERAL_RE.exec(word);
2711
+ if (lit === null)
2712
+ return false;
2713
+ const tail = word.slice(lit[0].length);
2714
+ if (tail.length < PROSE_SHORT_LITERAL_MIN_TAIL)
2715
+ return false;
2716
+ for (let k = 0; k < tail.length; k++)
2717
+ if (!isAlnumCode(tail.charCodeAt(k)))
2718
+ return false;
2719
+ return true;
2720
+ }
2668
2721
  function longestAlnumRun(word) {
2669
2722
  let best = 0;
2670
2723
  let run = 0;
@@ -3009,7 +3062,7 @@ function authParamEdits(text) {
3009
3062
  const LITERAL_PROBE_WINDOW = 64;
3010
3063
  function gluedLiteralStart(word) {
3011
3064
  const n = word.length;
3012
- if (n < PROSE_LITERAL_MIN_RUN + 2)
3065
+ if (n < PROSE_SHORT_LITERAL_MIN_TAIL + 4)
3013
3066
  return 0;
3014
3067
  const runFrom = new Int32Array(n + 1);
3015
3068
  let best = 0;
@@ -3028,9 +3081,10 @@ function gluedLiteralStart(word) {
3028
3081
  const left = word.charCodeAt(k - 1);
3029
3082
  if ((left !== 0x5f && left !== 0x2d) || !isAlnumCode(word.charCodeAt(k)))
3030
3083
  continue;
3031
- if ((runFrom[k] ?? 0) < PROSE_LITERAL_MIN_RUN)
3084
+ const run = runFrom[k] ?? 0;
3085
+ if (run < PROSE_SHORT_LITERAL_MIN_TAIL)
3032
3086
  return 0;
3033
- if (SECRET_LITERAL_RE.test(word.slice(k, k + LITERAL_PROBE_WINDOW)))
3087
+ if (SECRET_LITERAL_RE.test(word.slice(k, k + LITERAL_PROBE_WINDOW)) && (run >= PROSE_LITERAL_MIN_RUN || shortLiteralTail(word.slice(k))))
3034
3088
  return k;
3035
3089
  }
3036
3090
  return 0;
@@ -3090,8 +3144,27 @@ function isOpaqueWord(text, i, end) {
3090
3144
  }
3091
3145
  return segments > 0 && segmentChars / segments <= OPAQUE_MAX_AVG_SEGMENT;
3092
3146
  }
3147
+ const FILE_URL_OPENERS = new Set(['(', '[', '<', "'", ',', ';']);
3148
+ function fileUrlPositions(text) {
3149
+ let marks = null;
3150
+ const re = /file:/gi;
3151
+ for (let hit = re.exec(text); hit !== null; hit = re.exec(text)) {
3152
+ const s = hit.index;
3153
+ if (s > 0 && !endsUrlFragmentAt(text, s - 1) && !FILE_URL_OPENERS.has(text.charAt(s - 1)))
3154
+ continue;
3155
+ let e = s + hit[0].length;
3156
+ while (e < text.length && !endsUrlFragmentAt(text, e))
3157
+ e++;
3158
+ marks ??= new Uint8Array(text.length);
3159
+ for (let k = s; k < e; k++)
3160
+ marks[k] = 1;
3161
+ re.lastIndex = e;
3162
+ }
3163
+ return marks;
3164
+ }
3093
3165
  function proseLiteralEdits(text) {
3094
3166
  const edits = [];
3167
+ let fileAt;
3095
3168
  let i = 0;
3096
3169
  while (i < text.length) {
3097
3170
  const c = text.charCodeAt(i);
@@ -3102,6 +3175,7 @@ function proseLiteralEdits(text) {
3102
3175
  let end = i;
3103
3176
  while (end < text.length && isKeyWordChar(text.charCodeAt(end)))
3104
3177
  end++;
3178
+ const escHead = i > 0 && text.charCodeAt(i - 1) === 0x25 && PERCENT_ESCAPE_ASCII.test(text.slice(i - 1, i + 2)) && i + 2 < end ? 2 : 0;
3105
3179
  const word = text.slice(i, end);
3106
3180
  if (word.startsWith('eyJ')) {
3107
3181
  let segEnd = end;
@@ -3120,22 +3194,33 @@ function proseLiteralEdits(text) {
3120
3194
  }
3121
3195
  const prev = i > 0 ? text.charAt(i - 1) : '';
3122
3196
  const hostSlot = prev === '/' && i > 1 && text.charAt(i - 2) === '/';
3123
- const pathish = (prev === '/' && !hostSlot) || prev === '\\' || prev === '.';
3124
- if (!pathish && SECRET_LITERAL_RE.test(word) && longestAlnumRun(word) >= PROSE_LITERAL_MIN_RUN) {
3125
- pushEdit(edits, text, i, end, REDACTED_SECRET);
3126
- }
3127
- else if (!pathish) {
3197
+ const slashSegment = prev === '/' && !hostSlot;
3198
+ if (slashSegment && fileAt === undefined)
3199
+ fileAt = fileUrlPositions(text);
3200
+ const localPath = prev === '\\' || prev === '.' || (slashSegment && fileAt != null && fileAt[i] === 1);
3201
+ const pathish = slashSegment || localPath;
3202
+ if (!localPath && SECRET_LITERAL_RE.test(word) && (longestAlnumRun(word) >= PROSE_LITERAL_MIN_RUN || shortLiteralTail(word))) {
3203
+ pushEdit(edits, text, i + escHead, end, REDACTED_SECRET);
3204
+ }
3205
+ else if (!localPath) {
3128
3206
  const glued = gluedLiteralStart(word);
3129
3207
  if (glued > 0)
3130
- pushEdit(edits, text, i + glued, end, REDACTED_SECRET);
3131
- else if (!hostSlot && isOpaqueWord(text, i, end))
3132
- pushEdit(edits, text, i, end, REDACTED_SECRET);
3208
+ pushEdit(edits, text, i + Math.max(glued, escHead), end, REDACTED_SECRET);
3209
+ else if (!pathish && !hostSlot && isOpaqueWord(text, i, end))
3210
+ pushEdit(edits, text, i + escHead, end, REDACTED_SECRET);
3133
3211
  }
3134
3212
  i = end;
3135
3213
  }
3136
3214
  return edits;
3137
3215
  }
3216
+ const C0_ALL = '\\u0000-\\u001F';
3217
+ const C0_KEEP_LAYOUT = '\\u0000-\\u0008\\u000B-\\u001F';
3218
+ const DEL_C1 = '\\u007F-\\u009F';
3219
+ const LONE_SURROGATE = '\\uD800-\\uDFFF';
3220
+ const LINE_SEPARATORS = '\\u2028\\u2029';
3138
3221
  const FORMAT_CHAR = /^\p{Cf}$/u;
3222
+ const FORMAT_CLASS = FORMAT_CHAR.source.slice(1, -1);
3223
+ const FACE_MARKED_CHAR = new RegExp(`^(?:[${C0_ALL}${DEL_C1}${LONE_SURROGATE}${LINE_SEPARATORS}]|${FORMAT_CLASS})$`, 'u');
3139
3224
  const isStrippableControl = (c) => (c <= 0x1f && (c < 0x09 || c > 0x0d)) || (c >= 0x7f && c <= 0x9f);
3140
3225
  class SequenceReader {
3141
3226
  text;
@@ -3263,13 +3348,7 @@ function c1SequenceAfter(text, c1, at, seq) {
3263
3348
  return null;
3264
3349
  }
3265
3350
  function isFaceMarkedCodePoint(cp) {
3266
- if (cp <= 0x1f || (cp >= 0x7f && cp <= 0x9f))
3267
- return true;
3268
- if (cp >= 0xd800 && cp <= 0xdfff)
3269
- return true;
3270
- if (cp === 0x2028 || cp === 0x2029)
3271
- return true;
3272
- return cp >= 0xad && cp <= 0x10ffff && FORMAT_CHAR.test(String.fromCodePoint(cp));
3351
+ return cp >= 0 && cp <= 0x10ffff && FACE_MARKED_CHAR.test(String.fromCodePoint(cp));
3273
3352
  }
3274
3353
  function escapeTokenAt(text, at) {
3275
3354
  const four = /^\\u([0-9A-Fa-f]{4})/.exec(text.slice(at, at + 6));
@@ -3585,8 +3664,39 @@ function screenSurfaces(text, o) {
3585
3664
  if (joinedSpaced !== null)
3586
3665
  out.push(joinedSpaced);
3587
3666
  const contents = V.contents.map(([a, b]) => ({ sub: screen.slice(a, b), rawAt: (k) => M.ps[a + k] ?? 0, rawEndAt: (k) => M.pe[a + k] ?? 0 }));
3667
+ const decoded = percentSurfaces(text, screen, M, o);
3668
+ out.push(...decoded.surfaces);
3669
+ contents.push(...decoded.contents);
3588
3670
  return { surfaces: out, rawSoft, rawPlain, contents };
3589
3671
  }
3672
+ function percentSurfaces(raw, text, base, o) {
3673
+ const surfaces = [];
3674
+ const contents = [];
3675
+ for (const layer of percentLayers(text)) {
3676
+ const n = layer.text.length;
3677
+ const ps = new Int32Array(n);
3678
+ const pe = new Int32Array(n);
3679
+ const pdot = new Uint8Array(n);
3680
+ for (let k = 0; k < n; k++) {
3681
+ const a = layer.ps[k] ?? 0;
3682
+ const b = layer.pe[k] ?? 0;
3683
+ ps[k] = base === null ? a : (base.ps[a] ?? 0);
3684
+ pe[k] = base === null ? b : (base.pe[b - 1] ?? 0);
3685
+ if (base !== null && b - a === 1)
3686
+ pdot[k] = base.pdot[a] ?? 0;
3687
+ }
3688
+ const M = { ps, pe, pdot, rawDot: base === null ? new Uint8Array(raw.length) : base.rawDot };
3689
+ const VD = buildScanView(layer.text, o);
3690
+ const sep = separatedSurface(raw, VD, M);
3691
+ surfaces.push(base === null ? { ...sep, rawDot: null } : sep);
3692
+ const joined = joinedSurface(raw, VD, false, M);
3693
+ if (joined !== null)
3694
+ surfaces.push(base === null ? { ...joined, rawDot: null } : joined);
3695
+ for (const [a, b] of VD.contents)
3696
+ contents.push({ sub: layer.text.slice(a, b), rawAt: (k) => ps[a + k] ?? 0, rawEndAt: (k) => pe[a + k] ?? 0 });
3697
+ }
3698
+ return { surfaces, contents };
3699
+ }
3590
3700
  function finalIsStructural(text, at) {
3591
3701
  if (text.charCodeAt(at) !== 0x5c)
3592
3702
  return false;
@@ -3855,6 +3965,9 @@ function collectCandidates(text, o, nested) {
3855
3965
  surfaces.push(joinedSpaced);
3856
3966
  const screen = screenSurfaces(text, o);
3857
3967
  surfaces.push(...screen.surfaces);
3968
+ const decoded = percentSurfaces(text, text, null, o);
3969
+ surfaces.push(...decoded.surfaces);
3970
+ const decodedContents = decoded.contents;
3858
3971
  const raw = [];
3859
3972
  const blocks = { raw: text, view: V, out: raw, lines: [null, null], marks: null };
3860
3973
  const regionSurface = screen.surfaces[0] ?? separated;
@@ -3864,7 +3977,7 @@ function collectCandidates(text, o, nested) {
3864
3977
  const regions = [];
3865
3978
  if (o.urls) {
3866
3979
  const unitPos = (k) => S.dropped(k) || V.rawUnit[S.rawStart(k)] === 1;
3867
- urlCandidates(S.text, cands, regions, unitPos, (k) => unitPos(k) || S.painted(k));
3980
+ urlCandidates(S.text, cands, regions, unitPos, (k) => unitPos(k) || S.painted(k), o.queryByShape ?? false);
3868
3981
  userinfoCandidates(S, cands, o.words);
3869
3982
  }
3870
3983
  if (o.words) {
@@ -3873,6 +3986,8 @@ function collectCandidates(text, o, nested) {
3873
3986
  labelCandidates(S, 'scheme', regions, cands, blocks);
3874
3987
  labelCandidates(S, 'token', regions, cands, blocks);
3875
3988
  labelCandidates(S, 'label', regions, cands, blocks);
3989
+ labelCandidates(S, 'scheme', regions, cands, blocks, true);
3990
+ labelCandidates(S, 'label', regions, cands, blocks, true);
3876
3991
  editsToCandidates(authParamEdits(S.text), 'param', cands);
3877
3992
  editsToCandidates(proseLiteralEdits(S.text), 'prose', cands);
3878
3993
  }
@@ -3890,7 +4005,7 @@ function collectCandidates(text, o, nested) {
3890
4005
  raw.push({ start: a + iv.start, end: a + iv.end, kind: iv.kind, cls: 'nested', tokenEnd: a + iv.end, pretrimmed: true });
3891
4006
  }
3892
4007
  }
3893
- for (const c of screen.contents) {
4008
+ for (const c of [...screen.contents, ...decodedContents]) {
3894
4009
  const inner = collectCandidates(c.sub, o, true);
3895
4010
  for (const iv of resolveCandidates(c.sub, inner.cands, inner.rawUnit, inner.rawDot, o.words ? pemRegions(inner.pemBlocksSeen) : [], inner.rawSoft, inner.rawPlain)) {
3896
4011
  const start = c.rawAt(iv.start);
@@ -3959,11 +4074,6 @@ export function washCredentials(text) {
3959
4074
  return text;
3960
4075
  return scanAndMask(text, WASH_OPTIONS, false).text;
3961
4076
  }
3962
- const C0_ALL = '\\u0000-\\u001F';
3963
- const C0_KEEP_LAYOUT = '\\u0000-\\u0008\\u000B-\\u001F';
3964
- const DEL_C1 = '\\u007F-\\u009F';
3965
- const LONE_SURROGATE = '\\uD800-\\uDFFF';
3966
- const LINE_SEPARATORS = '\\u2028\\u2029';
3967
4077
  const NAME_BLANKS = NAME_BLANK_RANGES.map(([a, b]) => (a === b ? classEscape(a) : `${classEscape(a)}-${classEscape(b)}`)).join('');
3968
4078
  function classEscape(cp) {
3969
4079
  return `\\u${cp.toString(16).toUpperCase().padStart(4, '0')}`;
@@ -4068,9 +4178,7 @@ function faceHitKept(ch, at, text) {
4068
4178
  const c = ch.charCodeAt(0);
4069
4179
  return ch.length === 1 && (c === 0x200d || c === 0x200c) && joinerKept(text, at, c === 0x200d);
4070
4180
  }
4071
- const FORMAT_CLASS = '\\p{Cf}';
4072
4181
  const RGI_TAG_FLAG = '\\u{1F3F4}\\u{E0067}\\u{E0062}(?:\\u{E0065}\\u{E006E}\\u{E0067}|\\u{E0073}\\u{E0063}\\u{E0074}|\\u{E0077}\\u{E006C}\\u{E0073})\\u{E007F}';
4073
- const BIDI_REORDER = '\\u061C\\u200E\\u200F\\u2028\\u2029\\u202A-\\u202E\\u2066-\\u2069';
4074
4182
  function unsafeClass(members, withFormatClass) {
4075
4183
  if (members === '' && !withFormatClass)
4076
4184
  return null;
@@ -4081,17 +4189,6 @@ function unsafeClass(members, withFormatClass) {
4081
4189
  ];
4082
4190
  return new RegExp(parts.join('|'), 'gu');
4083
4191
  }
4084
- export const LEGACY_ESCAPE_FACE = Object.freeze({
4085
- unsafe: unsafeClass(`${C0_ALL}${DEL_C1}${BIDI_REORDER}${LONE_SURROGATE}`, false),
4086
- fold: false,
4087
- mark: 'escape',
4088
- });
4089
- export const LEGACY_LABEL_FACE = Object.freeze({ ...LEGACY_ESCAPE_FACE, fold: true });
4090
- export const LEGACY_NAME_FACE = Object.freeze({
4091
- unsafe: unsafeClass(`${C0_ALL}\\u007F${LINE_SEPARATORS}`, false),
4092
- fold: true,
4093
- mark: 'space',
4094
- });
4095
4192
  function markOf(ch, mark) {
4096
4193
  if (mark === 'dot')
4097
4194
  return '.';
@@ -4229,6 +4326,7 @@ function capPaired(t, max) {
4229
4326
  if (id >= 0)
4230
4327
  while (cut > 0 && t.ids[cut - 1] === id)
4231
4328
  cut--;
4329
+ cut = markerSafeCut(t.text, cut);
4232
4330
  if (cut > 0) {
4233
4331
  const code = t.text.charCodeAt(cut - 1);
4234
4332
  if (code >= 0xd800 && code <= 0xdbff)
@@ -4269,8 +4367,24 @@ function publicFace(opts) {
4269
4367
  const index = (opts.controls !== false ? 8 : 0) + (opts.bidi !== false ? 4 : 0) + (keep ? 2 : 0) + (blanks ? 1 : 0);
4270
4368
  return { unsafe: PUBLIC_UNSAFE_BY_NETS[index] ?? null, fold, mark: opts.mark ?? 'escape', keepBlanks: blanks };
4271
4369
  }
4370
+ export const ESCAPE_FACE = Object.freeze({ ...publicFace({ foldLines: false }) });
4371
+ export const LABEL_FACE = Object.freeze({ ...publicFace({}) });
4372
+ export const PEER_NAME_FACE = Object.freeze({ ...publicFace({ mark: 'space' }) });
4272
4373
  const ESCAPE_TOKEN_AT = /^\\u(?:[0-9A-F]{4}|\{[0-9A-F]{1,6}\})/;
4273
- function capDisplay(text, max, mark) {
4374
+ export const REDACTION_MARKER_MAX_LENGTH = REDACTION_MARKER_PREFIX.length + 2 * (1 + 16) + 1;
4375
+ export const DISPLAY_CAP_BACKOFF_MAX = REDACTION_MARKER_MAX_LENGTH;
4376
+ export function markerSafeCut(text, cut) {
4377
+ if (cut <= 0 || cut >= text.length)
4378
+ return cut;
4379
+ const open = text.lastIndexOf('\u00ab', cut - 1);
4380
+ if (open < 0 || cut - open >= REDACTION_MARKER_MAX_LENGTH)
4381
+ return cut;
4382
+ const close = text.indexOf('\u00bb', open);
4383
+ if (close < cut || close - open + 1 > REDACTION_MARKER_MAX_LENGTH)
4384
+ return cut;
4385
+ return MARKER_RE.test(text.slice(open, close + 1)) ? open : cut;
4386
+ }
4387
+ export function capDisplay(text, max, mark) {
4274
4388
  if (typeof max !== 'number' || !Number.isFinite(max))
4275
4389
  return text;
4276
4390
  const limit = Math.max(0, Math.floor(max));
@@ -4287,6 +4401,7 @@ function capDisplay(text, max, mark) {
4287
4401
  break;
4288
4402
  }
4289
4403
  }
4404
+ cut = markerSafeCut(text, cut);
4290
4405
  if (cut > 0) {
4291
4406
  const code = text.charCodeAt(cut - 1);
4292
4407
  if (code >= 0xd800 && code <= 0xdbff)
@@ -4309,6 +4424,14 @@ function runDisplaySteps(start, steps) {
4309
4424
  }
4310
4425
  return out;
4311
4426
  }
4427
+ export function paintAndCap(text, face, max) {
4428
+ return runDisplaySteps(text, {
4429
+ wash: (s) => s,
4430
+ paint: (s) => applyDisplayFace(s, face),
4431
+ cap: (s) => capDisplay(s, max, face.mark),
4432
+ text: (s) => s,
4433
+ });
4434
+ }
4312
4435
  function escapeSpacesFor(o, face) {
4313
4436
  if (face.mark !== 'escape' || face.fold)
4314
4437
  return null;
@@ -4327,9 +4450,10 @@ function escapeSpacesFor(o, face) {
4327
4450
  }
4328
4451
  return set.size === 0 ? null : set;
4329
4452
  }
4330
- function scanOptionsOf(o, face) {
4453
+ function scanOptionsOf(o, face, queryByShape = false) {
4331
4454
  return {
4332
4455
  urls: o.credentialUrls !== false,
4456
+ queryByShape,
4333
4457
  words: o.credentialWords !== false,
4334
4458
  blanks: face.keepBlanks === true,
4335
4459
  fold: face.fold,
@@ -4344,11 +4468,11 @@ export function credentialWashPasses(text, opts = {}) {
4344
4468
  scanAndMask(input, scan, false, stats);
4345
4469
  return { passes: stats.passes, capped: stats.capped };
4346
4470
  }
4347
- function renderUntrusted(text, opts, ledger) {
4471
+ function renderUntrusted(text, opts, ledger, queryByShape = false) {
4348
4472
  const input = typeof text === 'string' ? text : String(text ?? '');
4349
4473
  const o = opts ?? {};
4350
4474
  const face = publicFace(o);
4351
- const scan = scanOptionsOf(o, face);
4475
+ const scan = scanOptionsOf(o, face, queryByShape);
4352
4476
  const pairBackslashes = o.escapeBackslashes === true && face.mark !== 'dot' && face.mark !== 'space';
4353
4477
  const book = ledger ?? (pairBackslashes ? { cps: [], lens: [] } : null);
4354
4478
  if (book === null) {
@@ -4379,6 +4503,193 @@ export function displayUntrusted(text, opts = {}) {
4379
4503
  const out = renderUntrusted(text, opts, null);
4380
4504
  return typeof out === 'string' ? out : out.text;
4381
4505
  }
4506
+ const NON_SECRET_FIELD_NAMES = new Set([
4507
+ 'deployment', 'deploymentname', 'deploymentid', 'name', 'tenant', 'tenantname', 'tenantid', 'region', 'location', 'zone',
4508
+ 'project', 'projectname', 'projectid', 'workspace', 'workspacename', 'workspaceid', 'resource', 'resourcename', 'resourcegroup',
4509
+ 'model', 'modelname', 'modelid', 'apiversion', 'version', 'env', 'environment', 'stage', 'cluster', 'namespace', 'instance',
4510
+ 'service', 'servicename', 'app', 'appname', 'application', 'team', 'site', 'org', 'organization',
4511
+ ]);
4512
+ function isNonSecretFieldName(key) {
4513
+ const view = percentDecodeForMatching(key).decoded;
4514
+ return NON_SECRET_FIELD_NAMES.has(view.toLowerCase().replace(/[-_]/g, ''));
4515
+ }
4516
+ function isCredentialLabelName(name) {
4517
+ if (name.length === 0)
4518
+ return false;
4519
+ const decoded = percentDecodeForMatching(name).decoded;
4520
+ for (const view of decoded === name ? [name] : [name, decoded]) {
4521
+ const probe = `${view}=`;
4522
+ if (labelEndsAt(probe, probe.length, false, true))
4523
+ return true;
4524
+ }
4525
+ return false;
4526
+ }
4527
+ const WORD_CHAIN_MAX_SUFFIX_DIGITS = 4;
4528
+ const WORD_CHAIN_MAX_DIGIT_RUN = 8;
4529
+ function isReadableWordChain(value) {
4530
+ const core = valueCoreForJudging(value);
4531
+ if (core.length === 0 || !core.includes('-'))
4532
+ return false;
4533
+ const segments = core.split('-');
4534
+ if (segments.length < 2)
4535
+ return false;
4536
+ if (!segments.some((seg) => seg.length >= 3 && [...seg].every(isAlpha)))
4537
+ return false;
4538
+ if ([...core].every((ch) => ch === '-' || isHexDigit(ch)))
4539
+ return false;
4540
+ for (const seg of segments) {
4541
+ const letters = seg.replace(/[0-9]+$/, '').toLowerCase();
4542
+ if (/[bcdfghjklmnpqrstvwxz]{4,}/.test(letters))
4543
+ return false;
4544
+ if (letters.length >= 5 && !/[aeiouy]/.test(letters))
4545
+ return false;
4546
+ }
4547
+ for (const seg of segments) {
4548
+ if (seg.length === 0)
4549
+ return false;
4550
+ let i = 0;
4551
+ while (i < seg.length && isAlpha(seg.charAt(i)))
4552
+ i++;
4553
+ const letters = seg.slice(0, i);
4554
+ const digits = seg.slice(i);
4555
+ for (const ch of digits)
4556
+ if (ch < '0' || ch > '9')
4557
+ return false;
4558
+ if (letters.length === 0) {
4559
+ if (digits.length === 0 || digits.length > WORD_CHAIN_MAX_DIGIT_RUN)
4560
+ return false;
4561
+ continue;
4562
+ }
4563
+ const caseOk = letters === letters.toLowerCase() || letters === letters.toUpperCase() ||
4564
+ (letters.charAt(0) === letters.charAt(0).toUpperCase() && letters.slice(1) === letters.slice(1).toLowerCase());
4565
+ if (!caseOk)
4566
+ return false;
4567
+ if (digits.length > 0 && (letters.length < 2 || digits.length > WORD_CHAIN_MAX_SUFFIX_DIGITS))
4568
+ return false;
4569
+ }
4570
+ return true;
4571
+ }
4572
+ const OPAQUE_TOKEN_PUNCTUATION = new Set(['-', '_', '.', '~', '+', '/', '=']);
4573
+ const HIGH_ENTROPY_MIN_ALNUM = 20;
4574
+ function isHighEntropyQueryValue(value) {
4575
+ const { decoded } = percentDecodeForMatching(value);
4576
+ let alnum = 0;
4577
+ let mixed = 0;
4578
+ let run = 0;
4579
+ let runHasLetter = false;
4580
+ let runHasDigit = false;
4581
+ let anyLetter = false;
4582
+ let anyDigit = false;
4583
+ let allHex = true;
4584
+ const flush = () => {
4585
+ alnum += run;
4586
+ if (runHasLetter && runHasDigit)
4587
+ mixed += run;
4588
+ run = 0;
4589
+ runHasLetter = false;
4590
+ runHasDigit = false;
4591
+ };
4592
+ for (const ch of decoded) {
4593
+ if (isAlpha(ch)) {
4594
+ run += 1;
4595
+ runHasLetter = true;
4596
+ anyLetter = true;
4597
+ if (!isHexDigit(ch))
4598
+ allHex = false;
4599
+ continue;
4600
+ }
4601
+ if (ch >= '0' && ch <= '9') {
4602
+ run += 1;
4603
+ runHasDigit = true;
4604
+ anyDigit = true;
4605
+ continue;
4606
+ }
4607
+ if (!OPAQUE_TOKEN_PUNCTUATION.has(ch))
4608
+ return false;
4609
+ flush();
4610
+ }
4611
+ flush();
4612
+ if (alnum < HIGH_ENTROPY_MIN_ALNUM)
4613
+ return false;
4614
+ return mixed * 2 >= alnum || (allHex && anyLetter && anyDigit);
4615
+ }
4616
+ function isOpaqueSecretValue(value) {
4617
+ const core = valueCoreForJudging(value);
4618
+ return core.length > 0 && (looksLikeKnownSecret(core) || isHighEntropyQueryValue(core));
4619
+ }
4620
+ const VALUE_PIECE_SEPARATORS = new Set(['/', '\\', '?', '#', '&', '=', ';', ':', '@', ',', '+']);
4621
+ function hasOpaquePiece(text) {
4622
+ let start = 0;
4623
+ for (let i = 0; i <= text.length; i++) {
4624
+ const ch = text.charAt(i);
4625
+ if (i === text.length || ch <= ' ' || VALUE_PIECE_SEPARATORS.has(ch)) {
4626
+ if (i > start && isOpaqueSecretValue(text.slice(start, i)))
4627
+ return true;
4628
+ start = i + 1;
4629
+ }
4630
+ }
4631
+ return false;
4632
+ }
4633
+ function carriesCredentialStructure(value) {
4634
+ const netChanges = (v) => {
4635
+ if (washCredentials(v) !== v || hasOpaquePiece(v))
4636
+ return true;
4637
+ if (!v.includes('+'))
4638
+ return false;
4639
+ const spaced = v.split('+').join(' ');
4640
+ return washCredentials(spaced) !== spaced;
4641
+ };
4642
+ let view = value;
4643
+ for (let layer = 0; layer < MAX_PERCENT_VIEW_LAYERS; layer++) {
4644
+ if (netChanges(view))
4645
+ return true;
4646
+ const next = percentDecodeForMatching(view).decoded;
4647
+ if (next === view)
4648
+ return false;
4649
+ view = next;
4650
+ }
4651
+ if (netChanges(view))
4652
+ return true;
4653
+ return percentDecodeForMatching(view).decoded !== view;
4654
+ }
4655
+ function queryPartCarriesCredential(key, value, relax) {
4656
+ if (value.length === 0 || isMarker(value))
4657
+ return false;
4658
+ if (key !== null) {
4659
+ if (isCredentialLabelName(key))
4660
+ return true;
4661
+ if (relax && isNonSecretFieldName(key) && isReadableWordChain(value))
4662
+ return isKnownSecretSegment(value);
4663
+ }
4664
+ return isOpaqueSecretValue(value) || carriesCredentialStructure(value);
4665
+ }
4666
+ const UPLOAD_PROBE_PREFIX = 'https://q.invalid/?';
4667
+ const UPLOAD_SCAN_OPTIONS = Object.freeze({ urls: true, queryByShape: 'strict', words: true, blanks: false, fold: false, face: null, escapeSpaces: null });
4668
+ export function queryCarriesCredential(query) {
4669
+ if (typeof query !== 'string' || query.length === 0)
4670
+ return false;
4671
+ const probe = `${UPLOAD_PROBE_PREFIX}${query}`;
4672
+ return scanOnce(probe, UPLOAD_SCAN_OPTIONS, false).text !== probe;
4673
+ }
4674
+ export function displayConfiguredUrl(url, opts = {}) {
4675
+ const out = renderUntrusted(typeof url === 'string' ? configuredUrlScannable(url) : url, opts, null, 'relaxed');
4676
+ return typeof out === 'string' ? out : out.text;
4677
+ }
4678
+ function configuredUrlScannable(url) {
4679
+ if (/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(url) || !/^[A-Za-z][A-Za-z0-9+.-]*:/.test(url))
4680
+ return url;
4681
+ let u;
4682
+ try {
4683
+ u = new URL(url);
4684
+ }
4685
+ catch {
4686
+ return url;
4687
+ }
4688
+ if (u.host === '')
4689
+ return url;
4690
+ const userinfo = u.username !== '' || u.password !== '' ? `${u.username}${u.password !== '' ? `:${u.password}` : ''}@` : '';
4691
+ return `${u.protocol}//${userinfo}${u.host}${u.pathname}${u.search}${u.hash}`;
4692
+ }
4382
4693
  export function quoteUntrustedForSentence(text, max) {
4383
4694
  const shown = displayUntrusted(text, { max, blanks: true, escapeBackslashes: true });
4384
4695
  return shown.trim() === '' ? undefined : `"${shown.replace(/"/g, '\\"')}"`;