@peterwangze/claude-trigger-router 1.10.0 → 1.11.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/README.md +103 -460
- package/dist/cli.js +146 -17
- package/dist/cli.js.map +2 -2
- package/docs/release-notes-v1.11.0.md +36 -0
- package/docs/releasing.md +5 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4298,6 +4298,7 @@ var init_SSEParser_transform = __esm({
|
|
|
4298
4298
|
"use strict";
|
|
4299
4299
|
SSEParserTransform = class {
|
|
4300
4300
|
buffer = "";
|
|
4301
|
+
currentEvent = {};
|
|
4301
4302
|
constructor() {
|
|
4302
4303
|
const transformStream = new TransformStream({
|
|
4303
4304
|
start: (controller) => {
|
|
@@ -4309,7 +4310,9 @@ var init_SSEParser_transform = __esm({
|
|
|
4309
4310
|
},
|
|
4310
4311
|
flush: (controller) => {
|
|
4311
4312
|
if (this.buffer.trim()) {
|
|
4312
|
-
this.parseBuffer(controller);
|
|
4313
|
+
this.parseBuffer(controller, true);
|
|
4314
|
+
} else if (Object.keys(this.currentEvent).length > 0) {
|
|
4315
|
+
this.parseBuffer(controller, true);
|
|
4313
4316
|
}
|
|
4314
4317
|
}
|
|
4315
4318
|
});
|
|
@@ -4318,25 +4321,29 @@ var init_SSEParser_transform = __esm({
|
|
|
4318
4321
|
}
|
|
4319
4322
|
readable;
|
|
4320
4323
|
writable;
|
|
4321
|
-
parseBuffer(controller) {
|
|
4322
|
-
const lines = this.buffer.split(
|
|
4323
|
-
this.buffer = lines.pop() || "";
|
|
4324
|
-
|
|
4325
|
-
|
|
4324
|
+
parseBuffer(controller, flush = false) {
|
|
4325
|
+
const lines = this.buffer.split(/\r?\n/);
|
|
4326
|
+
this.buffer = flush ? "" : lines.pop() || "";
|
|
4327
|
+
for (const rawLine of lines) {
|
|
4328
|
+
const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
|
|
4326
4329
|
if (line.startsWith("event:")) {
|
|
4327
|
-
currentEvent.event = line.slice(6).trim();
|
|
4330
|
+
this.currentEvent.event = line.slice(6).trim();
|
|
4328
4331
|
} else if (line.startsWith("data:")) {
|
|
4329
4332
|
const dataStr = line.slice(5).trim();
|
|
4330
4333
|
try {
|
|
4331
|
-
currentEvent.data = JSON.parse(dataStr);
|
|
4334
|
+
this.currentEvent.data = JSON.parse(dataStr);
|
|
4332
4335
|
} catch {
|
|
4333
|
-
currentEvent.data = dataStr;
|
|
4336
|
+
this.currentEvent.data = dataStr;
|
|
4334
4337
|
}
|
|
4335
|
-
} else if (line === "" && Object.keys(currentEvent).length > 0) {
|
|
4336
|
-
controller.enqueue(currentEvent);
|
|
4337
|
-
currentEvent = {};
|
|
4338
|
+
} else if (line === "" && Object.keys(this.currentEvent).length > 0) {
|
|
4339
|
+
controller.enqueue(this.currentEvent);
|
|
4340
|
+
this.currentEvent = {};
|
|
4338
4341
|
}
|
|
4339
4342
|
}
|
|
4343
|
+
if (flush && Object.keys(this.currentEvent).length > 0) {
|
|
4344
|
+
controller.enqueue(this.currentEvent);
|
|
4345
|
+
this.currentEvent = {};
|
|
4346
|
+
}
|
|
4340
4347
|
}
|
|
4341
4348
|
};
|
|
4342
4349
|
}
|
|
@@ -4356,6 +4363,102 @@ function serializeEvent(event2) {
|
|
|
4356
4363
|
output3 += "\n";
|
|
4357
4364
|
return new TextEncoder().encode(output3);
|
|
4358
4365
|
}
|
|
4366
|
+
function parseSSEBlock(block) {
|
|
4367
|
+
const event2 = {};
|
|
4368
|
+
const dataLines = [];
|
|
4369
|
+
for (const rawLine of block.split(/\r?\n/)) {
|
|
4370
|
+
const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
|
|
4371
|
+
if (line.startsWith("event:")) {
|
|
4372
|
+
event2.event = line.slice(6).trim();
|
|
4373
|
+
} else if (line.startsWith("data:")) {
|
|
4374
|
+
dataLines.push(line.slice(5).trim());
|
|
4375
|
+
}
|
|
4376
|
+
}
|
|
4377
|
+
if (dataLines.length > 0) {
|
|
4378
|
+
const dataStr = dataLines.join("\n");
|
|
4379
|
+
try {
|
|
4380
|
+
event2.data = JSON.parse(dataStr);
|
|
4381
|
+
} catch {
|
|
4382
|
+
event2.data = dataStr;
|
|
4383
|
+
}
|
|
4384
|
+
}
|
|
4385
|
+
return Object.keys(event2).length > 0 ? event2 : null;
|
|
4386
|
+
}
|
|
4387
|
+
function collectEventObservation(event2, observation) {
|
|
4388
|
+
const deltaText = event2?.data?.delta?.text;
|
|
4389
|
+
if (typeof deltaText === "string") {
|
|
4390
|
+
observation.sawText = true;
|
|
4391
|
+
observation.text += deltaText;
|
|
4392
|
+
}
|
|
4393
|
+
if (event2?.event === "message_delta" && event2?.data?.usage) {
|
|
4394
|
+
observation.usage = event2.data.usage;
|
|
4395
|
+
}
|
|
4396
|
+
}
|
|
4397
|
+
function observeSSEChunk(buffer, chunkText, observation) {
|
|
4398
|
+
let nextBuffer = buffer + chunkText;
|
|
4399
|
+
while (true) {
|
|
4400
|
+
const match = /\r?\n\r?\n/.exec(nextBuffer);
|
|
4401
|
+
if (!match || match.index < 0) {
|
|
4402
|
+
break;
|
|
4403
|
+
}
|
|
4404
|
+
const block = nextBuffer.slice(0, match.index);
|
|
4405
|
+
nextBuffer = nextBuffer.slice(match.index + match[0].length);
|
|
4406
|
+
const event2 = parseSSEBlock(block);
|
|
4407
|
+
if (event2) {
|
|
4408
|
+
collectEventObservation(event2, observation);
|
|
4409
|
+
}
|
|
4410
|
+
}
|
|
4411
|
+
return nextBuffer;
|
|
4412
|
+
}
|
|
4413
|
+
function finalizeStreamingTrace(req, observation) {
|
|
4414
|
+
if (req.sessionId && observation.usage) {
|
|
4415
|
+
sessionUsageCache.put(req.sessionId, observation.usage);
|
|
4416
|
+
}
|
|
4417
|
+
if (!req.governanceTrace) {
|
|
4418
|
+
return;
|
|
4419
|
+
}
|
|
4420
|
+
const outputGuardrail = inspectOutputGuardrail(observation.text);
|
|
4421
|
+
req.governanceTrace.outputGuardrail = outputGuardrail;
|
|
4422
|
+
for (const finding of outputGuardrail.findings) {
|
|
4423
|
+
appendTraceReason(req.governanceTrace, `output_guardrail:${finding.code}`);
|
|
4424
|
+
}
|
|
4425
|
+
req.governanceTrace.handoffSummary = summarizeRouteHandoffTrace(
|
|
4426
|
+
req.governanceTrace,
|
|
4427
|
+
getRuntimePipeline(req)
|
|
4428
|
+
);
|
|
4429
|
+
req.governanceTrace.spans = buildTraceSpansFromPipeline(
|
|
4430
|
+
req.governanceTrace,
|
|
4431
|
+
getRuntimePipeline(req)
|
|
4432
|
+
);
|
|
4433
|
+
req.governanceTrace = finalizeTrace(req.governanceTrace, {
|
|
4434
|
+
finalModel: req.body?.model ?? req.governanceTrace.finalModel
|
|
4435
|
+
});
|
|
4436
|
+
recordGovernanceTrace(req.governanceTrace);
|
|
4437
|
+
}
|
|
4438
|
+
function passThroughStreamingResponse(stream, req) {
|
|
4439
|
+
const decoder = new TextDecoder();
|
|
4440
|
+
const observation = { text: "", sawText: false };
|
|
4441
|
+
let buffer = "";
|
|
4442
|
+
return stream.pipeThrough(new TransformStream({
|
|
4443
|
+
transform(chunk, controller) {
|
|
4444
|
+
controller.enqueue(chunk);
|
|
4445
|
+
buffer = observeSSEChunk(buffer, decoder.decode(chunk, { stream: true }), observation);
|
|
4446
|
+
},
|
|
4447
|
+
flush() {
|
|
4448
|
+
const remaining = decoder.decode();
|
|
4449
|
+
if (remaining) {
|
|
4450
|
+
buffer = observeSSEChunk(buffer, remaining, observation);
|
|
4451
|
+
}
|
|
4452
|
+
if (buffer.trim()) {
|
|
4453
|
+
const event2 = parseSSEBlock(buffer);
|
|
4454
|
+
if (event2) {
|
|
4455
|
+
collectEventObservation(event2, observation);
|
|
4456
|
+
}
|
|
4457
|
+
}
|
|
4458
|
+
finalizeStreamingTrace(req, observation);
|
|
4459
|
+
}
|
|
4460
|
+
}));
|
|
4461
|
+
}
|
|
4359
4462
|
async function collectSSE(stream) {
|
|
4360
4463
|
const parser = new SSEParserTransform();
|
|
4361
4464
|
const parsedStream = stream.pipeThrough(parser);
|
|
@@ -4395,6 +4498,12 @@ function governStreamingResponse(stream, req, config, servicePort, deps) {
|
|
|
4395
4498
|
to: resolveModelReference(config, level.to) ?? level.to
|
|
4396
4499
|
}))
|
|
4397
4500
|
} : void 0;
|
|
4501
|
+
const shouldBufferForStreamGuard = Boolean(
|
|
4502
|
+
config.Governance?.enabled && resolvedCascadeConfig?.enabled && resolvedCascadeConfig.stream_guard && req.governanceTrace
|
|
4503
|
+
);
|
|
4504
|
+
if (!shouldBufferForStreamGuard) {
|
|
4505
|
+
return passThroughStreamingResponse(stream, req);
|
|
4506
|
+
}
|
|
4398
4507
|
return new ReadableStream({
|
|
4399
4508
|
async start(controller) {
|
|
4400
4509
|
try {
|
|
@@ -11552,6 +11661,25 @@ var init_protocols = __esm({
|
|
|
11552
11661
|
});
|
|
11553
11662
|
|
|
11554
11663
|
// src/index.ts
|
|
11664
|
+
function getShutdownSignalHandlers() {
|
|
11665
|
+
const store = globalThis;
|
|
11666
|
+
if (!store[SHUTDOWN_SIGNAL_HANDLERS_KEY]) {
|
|
11667
|
+
store[SHUTDOWN_SIGNAL_HANDLERS_KEY] = [];
|
|
11668
|
+
}
|
|
11669
|
+
return store[SHUTDOWN_SIGNAL_HANDLERS_KEY];
|
|
11670
|
+
}
|
|
11671
|
+
function registerShutdownSignalHandlers(shutdown) {
|
|
11672
|
+
const handlers = getShutdownSignalHandlers();
|
|
11673
|
+
for (const { signal, handler } of handlers) {
|
|
11674
|
+
process.off(signal, handler);
|
|
11675
|
+
}
|
|
11676
|
+
handlers.length = 0;
|
|
11677
|
+
for (const signal of SHUTDOWN_SIGNALS) {
|
|
11678
|
+
const handler = () => shutdown(signal);
|
|
11679
|
+
process.on(signal, handler);
|
|
11680
|
+
handlers.push({ signal, handler });
|
|
11681
|
+
}
|
|
11682
|
+
}
|
|
11555
11683
|
function cloneRequestBody(value) {
|
|
11556
11684
|
if (typeof structuredClone === "function") {
|
|
11557
11685
|
return structuredClone(value);
|
|
@@ -11722,8 +11850,7 @@ async function run(options = {}) {
|
|
|
11722
11850
|
process.exit(0);
|
|
11723
11851
|
});
|
|
11724
11852
|
};
|
|
11725
|
-
|
|
11726
|
-
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
11853
|
+
registerShutdownSignalHandlers(shutdown);
|
|
11727
11854
|
const servicePort = process.env.SERVICE_PORT ? parseInt(process.env.SERVICE_PORT) : port;
|
|
11728
11855
|
config.PORT = servicePort;
|
|
11729
11856
|
const pad = (num) => (num > 9 ? "" : "0") + num;
|
|
@@ -12176,7 +12303,7 @@ async function run(options = {}) {
|
|
|
12176
12303
|
}).then((governedPayload) => {
|
|
12177
12304
|
req.responseGovernanceApplied = true;
|
|
12178
12305
|
if (governedPayload && typeof governedPayload === "object" && governedPayload.error) {
|
|
12179
|
-
return done(
|
|
12306
|
+
return done(null, governedPayload);
|
|
12180
12307
|
}
|
|
12181
12308
|
if (req.sessionId && governedPayload?.usage) {
|
|
12182
12309
|
sessionUsageCache.put(req.sessionId, governedPayload.usage);
|
|
@@ -12185,7 +12312,7 @@ async function run(options = {}) {
|
|
|
12185
12312
|
}).catch((error) => done(error, null));
|
|
12186
12313
|
return;
|
|
12187
12314
|
}
|
|
12188
|
-
return done(
|
|
12315
|
+
return done(null, payload);
|
|
12189
12316
|
}
|
|
12190
12317
|
done(null, payload);
|
|
12191
12318
|
});
|
|
@@ -12215,7 +12342,7 @@ async function run(options = {}) {
|
|
|
12215
12342
|
});
|
|
12216
12343
|
await server.start();
|
|
12217
12344
|
}
|
|
12218
|
-
var import_fs8, import_promises5, import_os2, import_path8, import_json5, import_node_events, import_rotating_file_stream, event;
|
|
12345
|
+
var import_fs8, import_promises5, import_os2, import_path8, import_json5, import_node_events, import_rotating_file_stream, event, SHUTDOWN_SIGNAL_HANDLERS_KEY, SHUTDOWN_SIGNALS;
|
|
12219
12346
|
var init_index = __esm({
|
|
12220
12347
|
"src/index.ts"() {
|
|
12221
12348
|
"use strict";
|
|
@@ -12249,6 +12376,8 @@ var init_index = __esm({
|
|
|
12249
12376
|
init_protocols();
|
|
12250
12377
|
init_pipeline();
|
|
12251
12378
|
event = new import_node_events.EventEmitter();
|
|
12379
|
+
SHUTDOWN_SIGNAL_HANDLERS_KEY = Symbol.for("claude-trigger-router.shutdownSignalHandlers");
|
|
12380
|
+
SHUTDOWN_SIGNALS = ["SIGINT", "SIGTERM"];
|
|
12252
12381
|
}
|
|
12253
12382
|
});
|
|
12254
12383
|
|