@respan/cli 0.6.9 → 0.7.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.
@@ -340,7 +340,7 @@ function toOtlpPayload(spans) {
340
340
  })
341
341
  },
342
342
  scopeSpans: [{
343
- scope: { name: "respan-cli-hooks", version: "0.5.3" },
343
+ scope: { name: "respan-cli-hooks", version: "0.7.0" },
344
344
  spans: otlpSpans
345
345
  }]
346
346
  }]
@@ -339,7 +339,7 @@ function toOtlpPayload(spans) {
339
339
  })
340
340
  },
341
341
  scopeSpans: [{
342
- scope: { name: "respan-cli-hooks", version: "0.5.3" },
342
+ scope: { name: "respan-cli-hooks", version: "0.7.0" },
343
343
  spans: otlpSpans
344
344
  }]
345
345
  }]
@@ -318,7 +318,7 @@ function toOtlpPayload(spans) {
318
318
  })
319
319
  },
320
320
  scopeSpans: [{
321
- scope: { name: "respan-cli-hooks", version: "0.5.3" },
321
+ scope: { name: "respan-cli-hooks", version: "0.7.0" },
322
322
  spans: otlpSpans
323
323
  }]
324
324
  }]
@@ -716,7 +716,6 @@ function processBeforeTool(hookData) {
716
716
  pending.push({ name: toolName, input: toolInput, start_time: nowISO() });
717
717
  state.pending_tools = pending;
718
718
  state.send_version = (state.send_version ?? 0) + 1;
719
- state.tool_turns = (state.tool_turns ?? 0) + 1;
720
719
  saveStreamState(sessionId, state);
721
720
  }
722
721
  function processAfterTool(hookData) {
@@ -873,35 +872,30 @@ function processChunk(hookData) {
873
872
  debug(`Delayed send (version=${state.send_version}, delay=${SEND_DELAY}s), ${state.accumulated_text.length} chars`);
874
873
  launchDelayedSend(sessionId, state.send_version, spans, creds.apiKey, creds.baseUrl);
875
874
  }
876
- function mainWorker(raw) {
875
+ function processChunkInWorker(dataFile) {
877
876
  try {
877
+ const raw = fs2.readFileSync(dataFile, "utf-8");
878
+ fs2.unlinkSync(dataFile);
878
879
  if (!raw.trim()) return;
879
880
  const hookData = JSON.parse(raw);
880
- const event = String(hookData.hook_event_name ?? "");
881
881
  const unlock = acquireLock(LOCK_PATH);
882
882
  try {
883
- if (event === "BeforeTool") {
884
- processBeforeTool(hookData);
885
- } else if (event === "AfterTool") {
886
- processAfterTool(hookData);
887
- } else {
888
- processChunk(hookData);
889
- }
883
+ processChunk(hookData);
890
884
  } finally {
891
885
  unlock?.();
892
886
  }
893
887
  } catch (e) {
894
- if (e instanceof SyntaxError) {
895
- log("ERROR", `Invalid JSON from stdin: ${e}`);
896
- } else {
897
- log("ERROR", `Hook error: ${e}`);
888
+ log("ERROR", `Worker error: ${e}`);
889
+ try {
890
+ fs2.unlinkSync(dataFile);
891
+ } catch {
898
892
  }
899
893
  }
900
894
  }
901
895
  function main() {
902
896
  if (process.env._RESPAN_GEM_WORKER === "1") {
903
- const raw2 = process.env._RESPAN_GEM_DATA ?? "";
904
- mainWorker(raw2);
897
+ const dataFile = process.env._RESPAN_GEM_FILE ?? "";
898
+ if (dataFile) processChunkInWorker(dataFile);
905
899
  return;
906
900
  }
907
901
  let raw = "";
@@ -914,15 +908,34 @@ function main() {
914
908
  process.exit(0);
915
909
  }
916
910
  try {
917
- const scriptPath = __filename || process.argv[1];
918
- const child = (0, import_node_child_process.execFile)("node", [scriptPath], {
919
- env: { ...process.env, _RESPAN_GEM_WORKER: "1", _RESPAN_GEM_DATA: raw },
920
- stdio: "ignore",
921
- detached: true
922
- });
923
- child.unref();
911
+ const hookData = JSON.parse(raw);
912
+ const event = String(hookData.hook_event_name ?? "");
913
+ if (event === "BeforeTool" || event === "AfterTool") {
914
+ const unlock = acquireLock(LOCK_PATH);
915
+ try {
916
+ if (event === "BeforeTool") processBeforeTool(hookData);
917
+ else processAfterTool(hookData);
918
+ } finally {
919
+ unlock?.();
920
+ }
921
+ } else {
922
+ const dataFile = path2.join(STATE_DIR, `respan_chunk_${process.pid}.json`);
923
+ fs2.mkdirSync(STATE_DIR, { recursive: true });
924
+ fs2.writeFileSync(dataFile, raw);
925
+ try {
926
+ const scriptPath = __filename || process.argv[1];
927
+ const child = (0, import_node_child_process.execFile)("node", [scriptPath], {
928
+ env: { ...process.env, _RESPAN_GEM_WORKER: "1", _RESPAN_GEM_FILE: dataFile },
929
+ stdio: "ignore",
930
+ detached: true
931
+ });
932
+ child.unref();
933
+ } catch (e) {
934
+ processChunkInWorker(dataFile);
935
+ }
936
+ }
924
937
  } catch (e) {
925
- mainWorker(raw);
938
+ log("ERROR", `Hook error: ${e}`);
926
939
  }
927
940
  process.exit(0);
928
941
  }
@@ -448,7 +448,6 @@ function processBeforeTool(hookData) {
448
448
  // Increment send_version to cancel any pending delayed sends —
449
449
  // the turn isn't done yet, a tool is about to execute.
450
450
  state.send_version = (state.send_version ?? 0) + 1;
451
- state.tool_turns = (state.tool_turns ?? 0) + 1;
452
451
  saveStreamState(sessionId, state);
453
452
  }
454
453
  function processAfterTool(hookData) {
@@ -624,66 +623,87 @@ function processChunk(hookData) {
624
623
  launchDelayedSend(sessionId, state.send_version, spans, creds.apiKey, creds.baseUrl);
625
624
  }
626
625
  // ── Main ──────────────────────────────────────────────────────────
627
- function mainWorker(raw) {
626
+ function processChunkInWorker(dataFile) {
628
627
  try {
628
+ const raw = fs.readFileSync(dataFile, 'utf-8');
629
+ fs.unlinkSync(dataFile);
629
630
  if (!raw.trim())
630
631
  return;
631
632
  const hookData = JSON.parse(raw);
632
- const event = String(hookData.hook_event_name ?? '');
633
633
  const unlock = acquireLock(LOCK_PATH);
634
634
  try {
635
- if (event === 'BeforeTool') {
636
- processBeforeTool(hookData);
637
- }
638
- else if (event === 'AfterTool') {
639
- processAfterTool(hookData);
640
- }
641
- else {
642
- processChunk(hookData);
643
- }
635
+ processChunk(hookData);
644
636
  }
645
637
  finally {
646
638
  unlock?.();
647
639
  }
648
640
  }
649
641
  catch (e) {
650
- if (e instanceof SyntaxError) {
651
- log('ERROR', `Invalid JSON from stdin: ${e}`);
652
- }
653
- else {
654
- log('ERROR', `Hook error: ${e}`);
642
+ log('ERROR', `Worker error: ${e}`);
643
+ try {
644
+ fs.unlinkSync(dataFile);
655
645
  }
646
+ catch { }
656
647
  }
657
648
  }
658
649
  function main() {
659
- // Worker mode: re-invoked as detached subprocess
650
+ // Worker mode: process chunk from temp file
660
651
  if (process.env._RESPAN_GEM_WORKER === '1') {
661
- const raw = process.env._RESPAN_GEM_DATA ?? '';
662
- mainWorker(raw);
652
+ const dataFile = process.env._RESPAN_GEM_FILE ?? '';
653
+ if (dataFile)
654
+ processChunkInWorker(dataFile);
663
655
  return;
664
656
  }
665
- // Read stdin synchronously, respond immediately, fork worker, exit
666
657
  let raw = '';
667
658
  try {
668
659
  raw = fs.readFileSync(0, 'utf-8');
669
660
  }
670
661
  catch { }
662
+ // Respond immediately so Gemini CLI doesn't block
671
663
  process.stdout.write('{}\n');
672
664
  if (!raw.trim()) {
673
665
  process.exit(0);
674
666
  }
675
667
  try {
676
- const scriptPath = __filename || process.argv[1];
677
- const child = execFile('node', [scriptPath], {
678
- env: { ...process.env, _RESPAN_GEM_WORKER: '1', _RESPAN_GEM_DATA: raw },
679
- stdio: 'ignore',
680
- detached: true,
681
- });
682
- child.unref();
668
+ const hookData = JSON.parse(raw);
669
+ const event = String(hookData.hook_event_name ?? '');
670
+ if (event === 'BeforeTool' || event === 'AfterTool') {
671
+ // Tool events are fast (just state updates) and must run in order.
672
+ // Process inline, don't fork.
673
+ const unlock = acquireLock(LOCK_PATH);
674
+ try {
675
+ if (event === 'BeforeTool')
676
+ processBeforeTool(hookData);
677
+ else
678
+ processAfterTool(hookData);
679
+ }
680
+ finally {
681
+ unlock?.();
682
+ }
683
+ }
684
+ else {
685
+ // AfterModel chunks: fork to background so Gemini CLI doesn't block.
686
+ // Write data to temp file (avoids env var size limits).
687
+ const dataFile = path.join(STATE_DIR, `respan_chunk_${process.pid}.json`);
688
+ fs.mkdirSync(STATE_DIR, { recursive: true });
689
+ fs.writeFileSync(dataFile, raw);
690
+ try {
691
+ const scriptPath = __filename || process.argv[1];
692
+ const child = execFile('node', [scriptPath], {
693
+ env: { ...process.env, _RESPAN_GEM_WORKER: '1', _RESPAN_GEM_FILE: dataFile },
694
+ stdio: 'ignore',
695
+ detached: true,
696
+ });
697
+ child.unref();
698
+ }
699
+ catch (e) {
700
+ // Fallback: run inline
701
+ processChunkInWorker(dataFile);
702
+ }
703
+ }
683
704
  }
684
705
  catch (e) {
685
- // Fallback: run inline
686
- mainWorker(raw);
706
+ log('ERROR', `Hook error: ${e}`);
687
707
  }
688
708
  process.exit(0);
689
709
  }
@@ -395,7 +395,7 @@ export function toOtlpPayload(spans) {
395
395
  }),
396
396
  },
397
397
  scopeSpans: [{
398
- scope: { name: 'respan-cli-hooks', version: '0.5.3' },
398
+ scope: { name: 'respan-cli-hooks', version: '0.7.0' },
399
399
  spans: otlpSpans,
400
400
  }],
401
401
  }],