@wrongstack/tools 0.300.0 → 0.301.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.
package/dist/json.js CHANGED
@@ -15,6 +15,81 @@ var DANGEROUS_PATTERNS = [
15
15
  // Greedy quantifier inside lookahead/lookbehind — (?!.*a+)
16
16
  /[([][^)\]]*[+*][^)\]]*[)\]][^)]*\?\??/
17
17
  ];
18
+ function hasAmbiguousQuantifiedAlternation(pattern) {
19
+ for (let i = 0; i < pattern.length; i++) {
20
+ if (pattern[i] !== "(") continue;
21
+ if (i > 0 && pattern[i - 1] === "\\") continue;
22
+ let depth = 0;
23
+ let inClass = false;
24
+ let j = i;
25
+ for (; j < pattern.length; j++) {
26
+ const ch = pattern[j];
27
+ if (ch === "\\") {
28
+ j++;
29
+ continue;
30
+ }
31
+ if (inClass) {
32
+ if (ch === "]") inClass = false;
33
+ continue;
34
+ }
35
+ if (ch === "[") {
36
+ inClass = true;
37
+ continue;
38
+ }
39
+ if (ch === "(") depth++;
40
+ else if (ch === ")") {
41
+ depth--;
42
+ if (depth === 0) break;
43
+ }
44
+ }
45
+ if (j >= pattern.length) return false;
46
+ const next = pattern[j + 1];
47
+ if (next !== "+" && next !== "*" && next !== "{") continue;
48
+ let inner = pattern.slice(i + 1, j);
49
+ inner = inner.replace(/^\?(?::|<?[=!])/u, "");
50
+ const branches = [];
51
+ let current = "";
52
+ let d = 0;
53
+ let cls = false;
54
+ for (let k = 0; k < inner.length; k++) {
55
+ const ch = inner[k];
56
+ if (ch === "\\") {
57
+ current += ch + (inner[k + 1] ?? "");
58
+ k++;
59
+ continue;
60
+ }
61
+ if (cls) {
62
+ if (ch === "]") cls = false;
63
+ current += ch;
64
+ continue;
65
+ }
66
+ if (ch === "[") {
67
+ cls = true;
68
+ current += ch;
69
+ continue;
70
+ }
71
+ if (ch === "(") d++;
72
+ if (ch === ")") d--;
73
+ if (ch === "|" && d === 0) {
74
+ branches.push(current);
75
+ current = "";
76
+ continue;
77
+ }
78
+ current += ch;
79
+ }
80
+ branches.push(current);
81
+ if (branches.length < 2) continue;
82
+ for (let a = 0; a < branches.length; a++) {
83
+ for (let b = a + 1; b < branches.length; b++) {
84
+ const x = branches[a];
85
+ const y = branches[b];
86
+ if (x === "" || y === "") return true;
87
+ if (x === y || x.startsWith(y) || y.startsWith(x)) return true;
88
+ }
89
+ }
90
+ }
91
+ return false;
92
+ }
18
93
  function compileUserRegex(pattern, flags) {
19
94
  if (typeof pattern !== "string") {
20
95
  return { ok: false, reason: "pattern must be a string" };
@@ -33,6 +108,12 @@ function compileUserRegex(pattern, flags) {
33
108
  };
34
109
  }
35
110
  }
111
+ if (hasAmbiguousQuantifiedAlternation(pattern)) {
112
+ return {
113
+ ok: false,
114
+ reason: "pattern quantifies an alternation with overlapping branches \u2014 rewrite so no two branches can match the same text"
115
+ };
116
+ }
36
117
  try {
37
118
  return { ok: true, regex: new RegExp(pattern, flags) };
38
119
  } catch (err) {
package/dist/logs.js CHANGED
@@ -15,6 +15,81 @@ var DANGEROUS_PATTERNS = [
15
15
  // Greedy quantifier inside lookahead/lookbehind — (?!.*a+)
16
16
  /[([][^)\]]*[+*][^)\]]*[)\]][^)]*\?\??/
17
17
  ];
18
+ function hasAmbiguousQuantifiedAlternation(pattern) {
19
+ for (let i = 0; i < pattern.length; i++) {
20
+ if (pattern[i] !== "(") continue;
21
+ if (i > 0 && pattern[i - 1] === "\\") continue;
22
+ let depth = 0;
23
+ let inClass = false;
24
+ let j = i;
25
+ for (; j < pattern.length; j++) {
26
+ const ch = pattern[j];
27
+ if (ch === "\\") {
28
+ j++;
29
+ continue;
30
+ }
31
+ if (inClass) {
32
+ if (ch === "]") inClass = false;
33
+ continue;
34
+ }
35
+ if (ch === "[") {
36
+ inClass = true;
37
+ continue;
38
+ }
39
+ if (ch === "(") depth++;
40
+ else if (ch === ")") {
41
+ depth--;
42
+ if (depth === 0) break;
43
+ }
44
+ }
45
+ if (j >= pattern.length) return false;
46
+ const next = pattern[j + 1];
47
+ if (next !== "+" && next !== "*" && next !== "{") continue;
48
+ let inner = pattern.slice(i + 1, j);
49
+ inner = inner.replace(/^\?(?::|<?[=!])/u, "");
50
+ const branches = [];
51
+ let current = "";
52
+ let d = 0;
53
+ let cls = false;
54
+ for (let k = 0; k < inner.length; k++) {
55
+ const ch = inner[k];
56
+ if (ch === "\\") {
57
+ current += ch + (inner[k + 1] ?? "");
58
+ k++;
59
+ continue;
60
+ }
61
+ if (cls) {
62
+ if (ch === "]") cls = false;
63
+ current += ch;
64
+ continue;
65
+ }
66
+ if (ch === "[") {
67
+ cls = true;
68
+ current += ch;
69
+ continue;
70
+ }
71
+ if (ch === "(") d++;
72
+ if (ch === ")") d--;
73
+ if (ch === "|" && d === 0) {
74
+ branches.push(current);
75
+ current = "";
76
+ continue;
77
+ }
78
+ current += ch;
79
+ }
80
+ branches.push(current);
81
+ if (branches.length < 2) continue;
82
+ for (let a = 0; a < branches.length; a++) {
83
+ for (let b = a + 1; b < branches.length; b++) {
84
+ const x = branches[a];
85
+ const y = branches[b];
86
+ if (x === "" || y === "") return true;
87
+ if (x === y || x.startsWith(y) || y.startsWith(x)) return true;
88
+ }
89
+ }
90
+ }
91
+ return false;
92
+ }
18
93
  function compileUserRegex(pattern, flags) {
19
94
  if (typeof pattern !== "string") {
20
95
  return { ok: false, reason: "pattern must be a string" };
@@ -33,6 +108,12 @@ function compileUserRegex(pattern, flags) {
33
108
  };
34
109
  }
35
110
  }
111
+ if (hasAmbiguousQuantifiedAlternation(pattern)) {
112
+ return {
113
+ ok: false,
114
+ reason: "pattern quantifies an alternation with overlapping branches \u2014 rewrite so no two branches can match the same text"
115
+ };
116
+ }
36
117
  try {
37
118
  return { ok: true, regex: new RegExp(pattern, flags) };
38
119
  } catch (err) {
@@ -0,0 +1,26 @@
1
+ import type { Tool } from '@wrongstack/core/types';
2
+ /**
3
+ * Structured alternative to typing a `<nextsteps>` block into the final message.
4
+ *
5
+ * Both routes are equally valid and produce the same result — the runtime folds
6
+ * whatever this tool parks into the turn-ending response (see
7
+ * `next-steps-slot.ts` in core), so every surface reads it through the same
8
+ * parser it already uses. What the tool buys is that the schema enforces the
9
+ * shape: a block cannot arrive unbalanced, `auto` cannot land on the wrong
10
+ * item, and item count cannot drift past the contract.
11
+ *
12
+ * Opt-in via `tools.nextsteps.enabled`; registered for the leader only.
13
+ */
14
+ interface NextStepsInput {
15
+ steps: Array<{
16
+ text: string;
17
+ auto?: boolean;
18
+ }>;
19
+ }
20
+ interface NextStepsOutput {
21
+ accepted: number;
22
+ auto: boolean;
23
+ }
24
+ export declare const nextStepsTool: Tool<NextStepsInput, NextStepsOutput>;
25
+ export {};
26
+ //# sourceMappingURL=next-steps-tool.d.ts.map