@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/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("\n");
4323
- this.buffer = lines.pop() || "";
4324
- let currentEvent = {};
4325
- for (const line of lines) {
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
- process.on("SIGINT", () => shutdown("SIGINT"));
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(governedPayload.error, null);
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(payload.error, null);
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