@sonnechasser/ntrp 0.3.2 → 0.3.4
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/ai/findings-stream-smoke.js +185 -0
- package/dist/ai/findings-stream-smoke.js.map +1 -0
- package/dist/ai/guardrails-smoke.js +2143 -323
- package/dist/ai/guardrails-smoke.js.map +1 -1
- package/dist/conversation/loop-guard-smoke.js +20395 -9338
- package/dist/conversation/loop-guard-smoke.js.map +1 -1
- package/dist/demo/whimsy-smoke.js +5 -0
- package/dist/demo/whimsy-smoke.js.map +1 -1
- package/dist/index.js +8179 -7211
- package/dist/index.js.map +1 -1
- package/dist/investigation/quality-eval-cli.js +21742 -0
- package/dist/investigation/quality-eval-cli.js.map +1 -0
- package/dist/investigation/verbosity-cli.js +1977 -329
- package/dist/investigation/verbosity-cli.js.map +1 -1
- package/dist/mcp/server.js +6286 -5191
- package/dist/mcp/server.js.map +1 -1
- package/dist/services/transcript-smoke.js +1 -0
- package/dist/services/transcript-smoke.js.map +1 -1
- package/dist/strategist/strategist-smoke.js +233 -10
- package/dist/strategist/strategist-smoke.js.map +1 -1
- package/dist/whimsy/time-bank-smoke.js +4910 -3329
- package/dist/whimsy/time-bank-smoke.js.map +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
process.noDeprecation = true;
|
|
3
|
+
|
|
4
|
+
// src/ai/json-stream.ts
|
|
5
|
+
var StreamingJsonArrayParser = class {
|
|
6
|
+
buf = "";
|
|
7
|
+
pos = 0;
|
|
8
|
+
inArray = false;
|
|
9
|
+
arrayClosed = false;
|
|
10
|
+
inString = false;
|
|
11
|
+
escaped = false;
|
|
12
|
+
depth = 0;
|
|
13
|
+
elementStart = -1;
|
|
14
|
+
/** Feed a chunk; returns the objects completed by this chunk, in order. */
|
|
15
|
+
push(chunk) {
|
|
16
|
+
this.buf += chunk;
|
|
17
|
+
const out = [];
|
|
18
|
+
if (this.arrayClosed) return out;
|
|
19
|
+
while (this.pos < this.buf.length) {
|
|
20
|
+
const ch = this.buf[this.pos];
|
|
21
|
+
if (!this.inArray) {
|
|
22
|
+
if (ch === "[") this.inArray = true;
|
|
23
|
+
this.pos++;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (this.inString) {
|
|
27
|
+
if (this.escaped) this.escaped = false;
|
|
28
|
+
else if (ch === "\\") this.escaped = true;
|
|
29
|
+
else if (ch === '"') this.inString = false;
|
|
30
|
+
this.pos++;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (ch === '"') {
|
|
34
|
+
this.inString = true;
|
|
35
|
+
} else if (ch === "{") {
|
|
36
|
+
if (this.depth === 0) this.elementStart = this.pos;
|
|
37
|
+
this.depth++;
|
|
38
|
+
} else if (ch === "}") {
|
|
39
|
+
if (this.depth > 0) {
|
|
40
|
+
this.depth--;
|
|
41
|
+
if (this.depth === 0 && this.elementStart >= 0) {
|
|
42
|
+
const raw = this.buf.slice(this.elementStart, this.pos + 1);
|
|
43
|
+
this.elementStart = -1;
|
|
44
|
+
try {
|
|
45
|
+
out.push(JSON.parse(raw));
|
|
46
|
+
} catch {
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
} else if (ch === "]" && this.depth === 0) {
|
|
51
|
+
this.arrayClosed = true;
|
|
52
|
+
this.pos++;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
this.pos++;
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// src/ai/json-response.ts
|
|
62
|
+
function stripJsonFences(text) {
|
|
63
|
+
const trimmed = text.trim();
|
|
64
|
+
const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
|
|
65
|
+
if (fenced) return fenced[1].trim();
|
|
66
|
+
return trimmed.replace(/```(?:json)?\s*/gi, "").replace(/```/g, "").trim();
|
|
67
|
+
}
|
|
68
|
+
function parseJsonArrayFromText(text) {
|
|
69
|
+
const cleaned = stripJsonFences(text);
|
|
70
|
+
const start = cleaned.indexOf("[");
|
|
71
|
+
if (start === -1) return null;
|
|
72
|
+
let depth = 0;
|
|
73
|
+
let end = -1;
|
|
74
|
+
for (let i = start; i < cleaned.length; i++) {
|
|
75
|
+
if (cleaned[i] === "[") depth++;
|
|
76
|
+
else if (cleaned[i] === "]") {
|
|
77
|
+
depth--;
|
|
78
|
+
if (depth === 0) {
|
|
79
|
+
end = i;
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (end === -1) return null;
|
|
85
|
+
try {
|
|
86
|
+
const parsed = JSON.parse(cleaned.slice(start, end + 1));
|
|
87
|
+
return Array.isArray(parsed) ? parsed : null;
|
|
88
|
+
} catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/ai/findings-stream-smoke.ts
|
|
94
|
+
var failures = [];
|
|
95
|
+
function assert(cond, msg) {
|
|
96
|
+
if (!cond) failures.push(msg);
|
|
97
|
+
}
|
|
98
|
+
function feed(chunks) {
|
|
99
|
+
const scanner = new StreamingJsonArrayParser();
|
|
100
|
+
const out = [];
|
|
101
|
+
for (const chunk of chunks) out.push(...scanner.push(chunk));
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
function shred(text, size) {
|
|
105
|
+
const chunks = [];
|
|
106
|
+
for (let i = 0; i < text.length; i += size) chunks.push(text.slice(i, i + size));
|
|
107
|
+
return chunks;
|
|
108
|
+
}
|
|
109
|
+
var FINDINGS_JSON = JSON.stringify([
|
|
110
|
+
{
|
|
111
|
+
segment: "Enterprise Deals (>$100K)",
|
|
112
|
+
severity: "critical",
|
|
113
|
+
finding: "**$10.4M pipeline at risk** \u2014 61% of enterprise opps have had no touch in 45+ days. Watch {stale_days} vs [benchmark].",
|
|
114
|
+
dollar_value: 104e5,
|
|
115
|
+
recommended_plays: [{ play_id: "clean-dead-pipeline", play_name: "Clean Dead Pipeline", rationale: "freshness < 60" }]
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
segment: "Rep: Tiger Woods",
|
|
119
|
+
severity: "warning",
|
|
120
|
+
finding: 'Escaped quote test: "single-threaded" deals with a \\ backslash.',
|
|
121
|
+
dollar_value: 33e5,
|
|
122
|
+
recommended_plays: []
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
segment: "SMB Deals",
|
|
126
|
+
severity: "info",
|
|
127
|
+
finding: "No dollar impact \u2014 informational only.",
|
|
128
|
+
dollar_value: null,
|
|
129
|
+
recommended_plays: []
|
|
130
|
+
}
|
|
131
|
+
]);
|
|
132
|
+
{
|
|
133
|
+
const parsed = feed([FINDINGS_JSON]);
|
|
134
|
+
assert(parsed.length === 3, `single chunk yields all elements (got ${parsed.length})`);
|
|
135
|
+
assert(parsed[0].segment === "Enterprise Deals (>$100K)", "element order preserved");
|
|
136
|
+
}
|
|
137
|
+
for (const size of [1, 2, 3, 5, 7]) {
|
|
138
|
+
const parsed = feed(shred(FINDINGS_JSON, size));
|
|
139
|
+
assert(parsed.length === 3, `chunk size ${size} yields all elements (got ${parsed.length})`);
|
|
140
|
+
assert(
|
|
141
|
+
JSON.stringify(parsed) === JSON.stringify(JSON.parse(FINDINGS_JSON)),
|
|
142
|
+
`chunk size ${size} round-trips identically`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
{
|
|
146
|
+
const wrapped = "Here are the findings:\n```json\n" + FINDINGS_JSON + "\n```\nLet me know if you need more.";
|
|
147
|
+
const parsed = feed(shred(wrapped, 4));
|
|
148
|
+
assert(parsed.length === 3, `fenced+prose yields all elements (got ${parsed.length})`);
|
|
149
|
+
}
|
|
150
|
+
{
|
|
151
|
+
const scanner = new StreamingJsonArrayParser();
|
|
152
|
+
const objects = JSON.parse(FINDINGS_JSON);
|
|
153
|
+
const first = JSON.stringify(objects[0]);
|
|
154
|
+
const early = scanner.push(`[${first},`);
|
|
155
|
+
assert(early.length === 1, "first element yields before the array closes");
|
|
156
|
+
const rest = scanner.push(`${JSON.stringify(objects[1])},${JSON.stringify(objects[2])}]`);
|
|
157
|
+
assert(rest.length === 2, "remaining elements yield on their closing braces");
|
|
158
|
+
}
|
|
159
|
+
{
|
|
160
|
+
const wrapped = "Note:\n" + FINDINGS_JSON;
|
|
161
|
+
const streamed = feed(shred(wrapped, 3));
|
|
162
|
+
const authoritative = parseJsonArrayFromText(wrapped);
|
|
163
|
+
assert(authoritative !== null, "authoritative parse succeeds on the same text");
|
|
164
|
+
assert(
|
|
165
|
+
JSON.stringify(streamed) === JSON.stringify(authoritative),
|
|
166
|
+
"streamed elements match parseJsonArrayFromText index-for-index"
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
{
|
|
170
|
+
const parsed = feed(shred('[{"a": 1}, {"broken": ', 2));
|
|
171
|
+
assert(parsed.length === 1, "incomplete tail yields only the balanced element");
|
|
172
|
+
const garbage = feed(["no json here at all"]);
|
|
173
|
+
assert(garbage.length === 0, "prose without an array yields nothing");
|
|
174
|
+
}
|
|
175
|
+
{
|
|
176
|
+
assert(feed(["[]"]).length === 0, "empty array yields nothing");
|
|
177
|
+
assert(feed(shred("Sure: []", 1)).length === 0, "empty array with preamble yields nothing");
|
|
178
|
+
}
|
|
179
|
+
if (failures.length > 0) {
|
|
180
|
+
console.error(`FAIL findings-stream-smoke (${failures.length}):`);
|
|
181
|
+
for (const f of failures) console.error(` - ${f}`);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
console.log("PASS findings-stream-smoke (single chunk, shredded chunks, fences, incremental yield, authoritative alignment, malformed input)");
|
|
185
|
+
//# sourceMappingURL=findings-stream-smoke.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/ai/json-stream.ts","../../src/ai/json-response.ts","../../src/ai/findings-stream-smoke.ts"],"sourcesContent":["/**\n * Incremental extractor for a streaming JSON array of objects.\n *\n * Feed model text chunks as they arrive; each complete top-level object\n * inside the first `[...]` is parsed and returned the moment its closing\n * brace lands, so consumers can render items while the rest of the\n * response is still streaming. Anchors on the first `[` in the text —\n * the same anchor `parseJsonArrayFromText` uses for the authoritative\n * end-of-stream parse — so the streamed elements and the final array\n * line up index-for-index whenever the full response is valid JSON.\n */\nexport class StreamingJsonArrayParser {\n private buf = \"\";\n private pos = 0;\n private inArray = false;\n private arrayClosed = false;\n private inString = false;\n private escaped = false;\n private depth = 0;\n private elementStart = -1;\n\n /** Feed a chunk; returns the objects completed by this chunk, in order. */\n push(chunk: string): unknown[] {\n this.buf += chunk;\n const out: unknown[] = [];\n if (this.arrayClosed) return out;\n\n while (this.pos < this.buf.length) {\n const ch = this.buf[this.pos]!;\n\n if (!this.inArray) {\n if (ch === \"[\") this.inArray = true;\n this.pos++;\n continue;\n }\n\n if (this.inString) {\n if (this.escaped) this.escaped = false;\n else if (ch === \"\\\\\") this.escaped = true;\n else if (ch === '\"') this.inString = false;\n this.pos++;\n continue;\n }\n\n if (ch === '\"') {\n this.inString = true;\n } else if (ch === \"{\") {\n if (this.depth === 0) this.elementStart = this.pos;\n this.depth++;\n } else if (ch === \"}\") {\n if (this.depth > 0) {\n this.depth--;\n if (this.depth === 0 && this.elementStart >= 0) {\n const raw = this.buf.slice(this.elementStart, this.pos + 1);\n this.elementStart = -1;\n try {\n out.push(JSON.parse(raw));\n } catch {\n // Malformed element — the authoritative end-of-stream parse\n // owns error handling; skip here.\n }\n }\n }\n } else if (ch === \"]\" && this.depth === 0) {\n // First top-level array closed — ignore any trailing prose.\n this.arrayClosed = true;\n this.pos++;\n break;\n }\n\n this.pos++;\n }\n return out;\n }\n}\n","/**\n * Shared helpers for parsing JSON from LLM text responses.\n * Models often wrap arrays in markdown fences or add a short preamble —\n * always extract the outermost balanced `[...]` before parsing.\n */\n\n/** Strip ```json fences and trim. */\nexport function stripJsonFences(text: string): string {\n const trimmed = text.trim();\n const fenced = trimmed.match(/```(?:json)?\\s*([\\s\\S]*?)\\s*```/i);\n if (fenced) return fenced[1]!.trim();\n return trimmed.replace(/```(?:json)?\\s*/gi, \"\").replace(/```/g, \"\").trim();\n}\n\n/**\n * Parse a JSON array from free-form model output.\n * Returns null when no valid array can be extracted (distinct from `[]`).\n */\nexport function parseJsonArrayFromText(text: string): unknown[] | null {\n const cleaned = stripJsonFences(text);\n const start = cleaned.indexOf(\"[\");\n if (start === -1) return null;\n\n let depth = 0;\n let end = -1;\n for (let i = start; i < cleaned.length; i++) {\n if (cleaned[i] === \"[\") depth++;\n else if (cleaned[i] === \"]\") {\n depth--;\n if (depth === 0) {\n end = i;\n break;\n }\n }\n }\n if (end === -1) return null;\n\n try {\n const parsed = JSON.parse(cleaned.slice(start, end + 1));\n return Array.isArray(parsed) ? parsed : null;\n } catch {\n return null;\n }\n}\n","/**\n * Offline smoke for StreamingJsonArrayParser — the incremental scanner\n * behind streamed findings. Run via `npm run test:findings-stream`.\n *\n * Covers: single-chunk parse, chunk splits mid-string/mid-escape/mid-brace,\n * markdown fences and prose preambles, nested arrays inside elements,\n * braces and brackets inside strings, trailing prose after the array, and\n * index alignment with the authoritative parseJsonArrayFromText.\n */\n\nimport { StreamingJsonArrayParser } from \"./json-stream.js\";\nimport { parseJsonArrayFromText } from \"./json-response.js\";\n\nconst failures: string[] = [];\n\nfunction assert(cond: boolean, msg: string): void {\n if (!cond) failures.push(msg);\n}\n\nfunction feed(chunks: string[]): unknown[] {\n const scanner = new StreamingJsonArrayParser();\n const out: unknown[] = [];\n for (const chunk of chunks) out.push(...scanner.push(chunk));\n return out;\n}\n\n/** Split text into n-char chunks — simulates arbitrary token boundaries. */\nfunction shred(text: string, size: number): string[] {\n const chunks: string[] = [];\n for (let i = 0; i < text.length; i += size) chunks.push(text.slice(i, i + size));\n return chunks;\n}\n\nconst FINDINGS_JSON = JSON.stringify([\n {\n segment: \"Enterprise Deals (>$100K)\",\n severity: \"critical\",\n finding: \"**$10.4M pipeline at risk** — 61% of enterprise opps have had no touch in 45+ days. Watch {stale_days} vs [benchmark].\",\n dollar_value: 10400000,\n recommended_plays: [{ play_id: \"clean-dead-pipeline\", play_name: \"Clean Dead Pipeline\", rationale: \"freshness < 60\" }],\n },\n {\n segment: \"Rep: Tiger Woods\",\n severity: \"warning\",\n finding: \"Escaped quote test: \\\"single-threaded\\\" deals with a \\\\ backslash.\",\n dollar_value: 3300000,\n recommended_plays: [],\n },\n {\n segment: \"SMB Deals\",\n severity: \"info\",\n finding: \"No dollar impact — informational only.\",\n dollar_value: null,\n recommended_plays: [],\n },\n]);\n\n// ── 1: whole array in one chunk ──────────────────────────────────────\n{\n const parsed = feed([FINDINGS_JSON]);\n assert(parsed.length === 3, `single chunk yields all elements (got ${parsed.length})`);\n assert((parsed[0] as { segment?: string }).segment === \"Enterprise Deals (>$100K)\", \"element order preserved\");\n}\n\n// ── 2: arbitrary chunk boundaries (1..7 chars) ───────────────────────\nfor (const size of [1, 2, 3, 5, 7]) {\n const parsed = feed(shred(FINDINGS_JSON, size));\n assert(parsed.length === 3, `chunk size ${size} yields all elements (got ${parsed.length})`);\n assert(\n JSON.stringify(parsed) === JSON.stringify(JSON.parse(FINDINGS_JSON)),\n `chunk size ${size} round-trips identically`,\n );\n}\n\n// ── 3: fences + preamble + trailing prose ────────────────────────────\n{\n const wrapped = \"Here are the findings:\\n```json\\n\" + FINDINGS_JSON + \"\\n```\\nLet me know if you need more.\";\n const parsed = feed(shred(wrapped, 4));\n assert(parsed.length === 3, `fenced+prose yields all elements (got ${parsed.length})`);\n}\n\n// ── 4: elements arrive as their braces close, not at the end ─────────\n{\n const scanner = new StreamingJsonArrayParser();\n const objects = JSON.parse(FINDINGS_JSON) as unknown[];\n const first = JSON.stringify(objects[0]);\n const early = scanner.push(`[${first},`);\n assert(early.length === 1, \"first element yields before the array closes\");\n const rest = scanner.push(`${JSON.stringify(objects[1])},${JSON.stringify(objects[2])}]`);\n assert(rest.length === 2, \"remaining elements yield on their closing braces\");\n}\n\n// ── 5: alignment with the authoritative parser ───────────────────────\n{\n const wrapped = \"Note:\\n\" + FINDINGS_JSON;\n const streamed = feed(shred(wrapped, 3));\n const authoritative = parseJsonArrayFromText(wrapped);\n assert(authoritative !== null, \"authoritative parse succeeds on the same text\");\n assert(\n JSON.stringify(streamed) === JSON.stringify(authoritative),\n \"streamed elements match parseJsonArrayFromText index-for-index\",\n );\n}\n\n// ── 6: malformed input yields only what balanced, never throws ───────\n{\n const parsed = feed(shred('[{\"a\": 1}, {\"broken\": ', 2));\n assert(parsed.length === 1, \"incomplete tail yields only the balanced element\");\n const garbage = feed([\"no json here at all\"]);\n assert(garbage.length === 0, \"prose without an array yields nothing\");\n}\n\n// ── 7: empty array ───────────────────────────────────────────────────\n{\n assert(feed([\"[]\"]).length === 0, \"empty array yields nothing\");\n assert(feed(shred('Sure: []', 1)).length === 0, \"empty array with preamble yields nothing\");\n}\n\n// ── Report ───────────────────────────────────────────────────────────\nif (failures.length > 0) {\n console.error(`FAIL findings-stream-smoke (${failures.length}):`);\n for (const f of failures) console.error(` - ${f}`);\n process.exit(1);\n}\nconsole.log(\"PASS findings-stream-smoke (single chunk, shredded chunks, fences, incremental yield, authoritative alignment, malformed input)\");\n"],"mappings":"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","names":[]}
|