@wrongstack/tools 1.0.0 → 1.0.1

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.
@@ -9,7 +9,7 @@
9
9
  * on every reply — which happens whenever the model's output is stable but
10
10
  * autonomy treats the next step as "still actionable" — the loop self-feeds
11
11
  * the same instruction 2–3 times in a row before the longer
12
- * `autoProceedMaxIterations` cap (default 50) trips.
12
+ * `autoProceedMaxIterations` cap (default 0 = unlimited) trips.
13
13
  *
14
14
  * The user reports the visible symptom: the same response, including the same
15
15
  * `<nextsteps>` block, repeats. Manual input from the user is the only thing
@@ -6,7 +6,7 @@ import {
6
6
  generateAdvancementPrompt,
7
7
  matchTodoIdFromPrompt,
8
8
  normalizeForRepetition
9
- } from "./chunk-37QKUQ2Z.js";
9
+ } from "./chunk-NL2S2CXD.js";
10
10
  export {
11
11
  GROUNDED_NO_PROGRESS_STEER,
12
12
  MAX_ADVANCEMENT_ATTEMPTS,
package/dist/bash.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  bashTool
3
- } from "./chunk-NN2SNP2R.js";
3
+ } from "./chunk-6H4ERJP7.js";
4
4
  import "./chunk-BTSFTGIL.js";
5
5
  import "./chunk-PVCB7RUZ.js";
6
6
  import "./chunk-SCMRM6NL.js";
package/dist/builtin.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  TIER2_TOOLS,
7
7
  TIER3_TOOLS,
8
8
  builtinTools
9
- } from "./chunk-AB2CAUOP.js";
9
+ } from "./chunk-IAYMTTR2.js";
10
10
  import "./chunk-RV7BVTDG.js";
11
11
  import "./chunk-YEISRT4S.js";
12
12
  import "./chunk-XRNN44QY.js";
@@ -40,8 +40,8 @@ import "./chunk-ASEXQRA7.js";
40
40
  import "./chunk-4ERMPDHV.js";
41
41
  import "./chunk-YJTXRH5Y.js";
42
42
  import "./chunk-ZBMANLYH.js";
43
- import "./chunk-NN2SNP2R.js";
44
- import "./chunk-KDEXUQY7.js";
43
+ import "./chunk-6H4ERJP7.js";
44
+ import "./chunk-MGPR7VG3.js";
45
45
  import "./chunk-BTSFTGIL.js";
46
46
  import "./chunk-PVCB7RUZ.js";
47
47
  import "./chunk-SCMRM6NL.js";
@@ -627,17 +627,51 @@ var bashTool = {
627
627
  const detached = !isWin2;
628
628
  const startedAt = Date.now();
629
629
  if (input.background) {
630
- const child2 = spawn(shell, args, {
631
- cwd: spawnCwd,
632
- env,
633
- stdio: ["ignore", "ignore", "ignore"],
634
- detached: !isWin2,
635
- windowsHide: true
636
- });
630
+ let child2;
631
+ try {
632
+ child2 = spawn(shell, args, {
633
+ cwd: spawnCwd,
634
+ env,
635
+ stdio: ["ignore", "ignore", "ignore"],
636
+ detached: !isWin2,
637
+ windowsHide: true
638
+ });
639
+ } catch (err) {
640
+ registry.afterCall(Date.now() - startedAt, true, bypassBreaker);
641
+ emitProcessStarted({
642
+ parentPid: process.pid,
643
+ command: redactCommand(`${shell} ${args.join(" ")}`),
644
+ args: redactCommand(args.join(" ")).split(" ").filter(Boolean),
645
+ cwd: spawnCwd,
646
+ background: true,
647
+ startedAt: new Date(startedAt).toISOString()
648
+ });
649
+ emitProcessCompleted({
650
+ exitCode: 1,
651
+ durationMs: Date.now() - startedAt,
652
+ stdoutBytes: 0,
653
+ stderrBytes: 0,
654
+ timedOut: false,
655
+ endedAt: (/* @__PURE__ */ new Date()).toISOString()
656
+ });
657
+ yield {
658
+ type: "final",
659
+ output: {
660
+ output: "",
661
+ exit_code: 1,
662
+ timed_out: false,
663
+ pid: null,
664
+ error: `spawn failed: ${err instanceof Error ? err.message : String(err)}`
665
+ }
666
+ };
667
+ return;
668
+ }
637
669
  const pid2 = child2.pid;
638
670
  const stdoutBytes2 = 0;
639
671
  const stderrBytes2 = 0;
640
672
  let telemetryCompleted2 = false;
673
+ let bgErrorOccurred = false;
674
+ let bgSettled = false;
641
675
  emitProcessStarted({
642
676
  ...pid2 !== void 0 ? { pid: pid2 } : {},
643
677
  parentPid: process.pid,
@@ -675,15 +709,33 @@ var bashTool = {
675
709
  child2.on("close", () => registry.unregister(pid2));
676
710
  }
677
711
  child2.on("error", () => {
678
- if (typeof pid2 === "number") registry.unregister(pid2);
712
+ if (bgSettled) return;
713
+ bgErrorOccurred = true;
679
714
  registry.afterCall(Date.now() - startedAt, true, bypassBreaker);
680
715
  completeBackground(1);
681
716
  });
682
717
  child2.on("close", (code, signal) => {
683
- registry.afterCall(Date.now() - startedAt, false, bypassBreaker);
718
+ bgSettled = true;
719
+ if (bgErrorOccurred) return;
720
+ const failed = code !== 0 && code !== null || !!signal;
721
+ registry.afterCall(Date.now() - startedAt, failed, bypassBreaker);
684
722
  completeBackground(code ?? (signal ? 1 : 0), signal ?? void 0);
685
723
  });
686
- if (typeof pid2 === "number") child2.unref();
724
+ if (typeof pid2 !== "number") {
725
+ completeBackground(1);
726
+ yield {
727
+ type: "final",
728
+ output: {
729
+ output: "",
730
+ exit_code: 1,
731
+ timed_out: false,
732
+ pid: null,
733
+ error: "Failed to launch background process: invalid PID"
734
+ }
735
+ };
736
+ return;
737
+ }
738
+ child2.unref();
687
739
  yield {
688
740
  type: "final",
689
741
  output: {
@@ -705,14 +757,46 @@ var bashTool = {
705
757
  });
706
758
  return;
707
759
  }
708
- const child = spawn(shell, args, {
709
- cwd: spawnCwd,
710
- env,
711
- stdio: ["ignore", "pipe", "pipe"],
712
- detached,
713
- windowsHide: true,
714
- ...isWin2 ? {} : { signal: callerSignal }
715
- });
760
+ let child;
761
+ try {
762
+ child = spawn(shell, args, {
763
+ cwd: spawnCwd,
764
+ env,
765
+ stdio: ["ignore", "pipe", "pipe"],
766
+ detached,
767
+ windowsHide: true,
768
+ ...isWin2 ? {} : { signal: callerSignal }
769
+ });
770
+ } catch (err) {
771
+ registry.afterCall(Date.now() - startedAt, true, bypassBreaker);
772
+ emitProcessStarted({
773
+ parentPid: process.pid,
774
+ command: redactCommand(`${shell} ${args.join(" ")}`),
775
+ args: redactCommand(args.join(" ")).split(" ").filter(Boolean),
776
+ cwd: spawnCwd,
777
+ background: false,
778
+ startedAt: new Date(startedAt).toISOString()
779
+ });
780
+ emitProcessCompleted({
781
+ exitCode: 1,
782
+ durationMs: Date.now() - startedAt,
783
+ stdoutBytes: 0,
784
+ stderrBytes: 0,
785
+ timedOut: false,
786
+ endedAt: (/* @__PURE__ */ new Date()).toISOString()
787
+ });
788
+ yield {
789
+ type: "final",
790
+ output: {
791
+ output: "",
792
+ exit_code: 1,
793
+ timed_out: false,
794
+ pid: null,
795
+ error: `spawn failed: ${err instanceof Error ? err.message : String(err)}`
796
+ }
797
+ };
798
+ return;
799
+ }
716
800
  const pid = child.pid;
717
801
  let stdoutBytes = 0;
718
802
  let stderrBytes = 0;
@@ -862,15 +946,21 @@ var bashTool = {
862
946
  const onStderrData = (chunk) => onData(chunk, "stderr");
863
947
  child.stdout?.on("data", onStdoutData);
864
948
  child.stderr?.on("data", onStderrData);
949
+ let fgErrorOccurred = false;
950
+ let sawClose = false;
865
951
  child.on("error", (err) => {
952
+ if (sawClose) return;
953
+ fgErrorOccurred = true;
866
954
  for (const t of timers) clearTimeout(t);
867
955
  registry.afterCall(Date.now() - startedAt, true);
868
956
  completeForeground(1);
869
957
  push({ kind: "error", err });
870
958
  });
871
959
  child.on("close", (code, signal) => {
960
+ sawClose = true;
872
961
  for (const t of timers) clearTimeout(t);
873
962
  if (typeof pid === "number") registry.unregister(pid);
963
+ if (fgErrorOccurred) return;
874
964
  registry.afterCall(Date.now() - startedAt, code !== 0 && code !== null);
875
965
  completeForeground(timedOut ? 124 : code ?? (signal ? 1 : 0), signal ?? void 0);
876
966
  const tail = stdoutDecoder.end() + stderrDecoder.end();
@@ -885,7 +975,33 @@ var bashTool = {
885
975
  while (true) {
886
976
  const c = await next();
887
977
  resumeIfDrained();
888
- if (c.kind === "error") throw c.err;
978
+ if (c.kind === "error") {
979
+ const isAbort = c.err?.code === "ABORT_ERR" || callerSignal.aborted;
980
+ const remainder = flush();
981
+ if (remainder !== null) {
982
+ yield { type: "partial_output", text: remainder };
983
+ }
984
+ const spooled = spool.finalize();
985
+ yield {
986
+ type: "final",
987
+ output: {
988
+ output: normalizeCommandOutput(buf) + (spooled ? spoolNote(spooled) : "") + pipeToShellNote,
989
+ exit_code: isAbort ? 124 : 1,
990
+ timed_out: isAbort,
991
+ pid: pid ?? null,
992
+ error: isAbort ? "Command aborted by user or signal" : c.err.message
993
+ }
994
+ };
995
+ ctx.recordSideEffect?.({
996
+ toolUseId: `bash-${Date.now()}`,
997
+ toolName: "bash",
998
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
999
+ input: { command: redactCommand(input.command) },
1000
+ outcome: isAbort ? "aborted" : `error (${c.err.message})`,
1001
+ risk: "shell"
1002
+ });
1003
+ return;
1004
+ }
889
1005
  if (c.kind === "end") {
890
1006
  const remainder = flush();
891
1007
  if (remainder !== null) {
@@ -930,7 +1046,7 @@ ${hint}` : "") + pipeToShellNote,
930
1046
  child.stderr?.off("data", onStderrData);
931
1047
  child.stdout?.destroy();
932
1048
  child.stderr?.destroy();
933
- if (child.exitCode === null && !child.killed) {
1049
+ if (!sawClose && child.exitCode === null && !child.killed) {
934
1050
  if (typeof pid === "number") registry.kill(pid, { force: true });
935
1051
  else killWithTimeout(child, 2e3);
936
1052
  }
@@ -977,4 +1093,4 @@ export {
977
1093
  checkAndBlockKillCommand,
978
1094
  bashTool
979
1095
  };
980
- //# sourceMappingURL=chunk-NN2SNP2R.js.map
1096
+ //# sourceMappingURL=chunk-6H4ERJP7.js.map
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  builtinToolsPack
3
- } from "./chunk-M6UV67BH.js";
3
+ } from "./chunk-E7JE3UEX.js";
4
4
  import {
5
5
  TIER1_TOOLS,
6
6
  TIER2_TOOLS
7
- } from "./chunk-AB2CAUOP.js";
7
+ } from "./chunk-IAYMTTR2.js";
8
8
 
9
9
  // src/tool-tier.ts
10
10
  function toolNameSet(tools) {
@@ -54,4 +54,4 @@ export {
54
54
  selectBuiltinToolsForTier,
55
55
  registerBuiltinToolTier
56
56
  };
57
- //# sourceMappingURL=chunk-YULVUTK5.js.map
57
+ //# sourceMappingURL=chunk-7LQCT3LT.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  builtinTools
3
- } from "./chunk-AB2CAUOP.js";
3
+ } from "./chunk-IAYMTTR2.js";
4
4
 
5
5
  // src/pack.ts
6
6
  var builtinToolsPack = {
@@ -12,4 +12,4 @@ var builtinToolsPack = {
12
12
  export {
13
13
  builtinToolsPack
14
14
  };
15
- //# sourceMappingURL=chunk-M6UV67BH.js.map
15
+ //# sourceMappingURL=chunk-E7JE3UEX.js.map
@@ -100,11 +100,11 @@ import {
100
100
  checkAndBlockKillCommand,
101
101
  diagnoseBashism,
102
102
  shellArgs
103
- } from "./chunk-NN2SNP2R.js";
103
+ } from "./chunk-6H4ERJP7.js";
104
104
  import {
105
105
  detectDanger,
106
106
  execTool
107
- } from "./chunk-KDEXUQY7.js";
107
+ } from "./chunk-MGPR7VG3.js";
108
108
  import {
109
109
  buildChildEnv
110
110
  } from "./chunk-BTSFTGIL.js";
@@ -474,14 +474,72 @@ var pwshTool = {
474
474
  const startedAt = Date.now();
475
475
  const detached = !isWin;
476
476
  if (isBackground) {
477
- const child2 = spawn(bin, args, {
477
+ let child2;
478
+ try {
479
+ child2 = spawn(bin, args, {
480
+ cwd: targetCwd,
481
+ env,
482
+ detached,
483
+ stdio: ["ignore", "ignore", "ignore"],
484
+ windowsHide: true
485
+ });
486
+ } catch (err) {
487
+ registry.afterCall(Date.now() - startedAt, true, isBackground);
488
+ emitProcessStarted({
489
+ parentPid: process.pid,
490
+ command: redactCommand(`${bin} ${args.join(" ")}`),
491
+ args: redactCommand(args.join(" ")).split(" ").filter(Boolean),
492
+ cwd: targetCwd,
493
+ background: true,
494
+ startedAt: new Date(startedAt).toISOString()
495
+ });
496
+ emitProcessCompleted({
497
+ exitCode: 1,
498
+ durationMs: Date.now() - startedAt,
499
+ stdoutBytes: 0,
500
+ stderrBytes: 0,
501
+ timedOut: false,
502
+ endedAt: (/* @__PURE__ */ new Date()).toISOString()
503
+ });
504
+ yield {
505
+ type: "final",
506
+ output: {
507
+ output: "",
508
+ exit_code: 1,
509
+ timed_out: false,
510
+ pid: null,
511
+ error: `pwsh: spawn failed: ${err instanceof Error ? err.message : String(err)}`
512
+ }
513
+ };
514
+ return;
515
+ }
516
+ const pid2 = child2.pid;
517
+ let bgTelemetryCompleted = false;
518
+ let bgErrorOccurred = false;
519
+ let bgSettled = false;
520
+ emitProcessStarted({
521
+ ...pid2 !== void 0 ? { pid: pid2 } : {},
522
+ parentPid: process.pid,
523
+ command: redactCommand(`${bin} ${args.join(" ")}`),
524
+ args: redactCommand(args.join(" ")).split(" ").filter(Boolean),
478
525
  cwd: targetCwd,
479
- env,
480
- detached,
481
- stdio: ["ignore", "ignore", "ignore"],
482
- windowsHide: true
526
+ background: true,
527
+ startedAt: new Date(startedAt).toISOString()
483
528
  });
484
- const pid2 = child2.pid;
529
+ const completeBackground = (exitCode, sig) => {
530
+ if (bgTelemetryCompleted) return;
531
+ bgTelemetryCompleted = true;
532
+ emitProcessCompleted({
533
+ ...pid2 !== void 0 ? { pid: pid2 } : {},
534
+ exitCode,
535
+ ...sig ? { signal: sig } : {},
536
+ durationMs: Date.now() - startedAt,
537
+ stdoutBytes: 0,
538
+ stderrBytes: 0,
539
+ timedOut: false,
540
+ endedAt: (/* @__PURE__ */ new Date()).toISOString()
541
+ });
542
+ };
485
543
  if (typeof pid2 === "number") {
486
544
  registry.register({
487
545
  pid: pid2,
@@ -493,10 +551,19 @@ var pwshTool = {
493
551
  processGroupLeader: detached && child2.pid === pid2,
494
552
  background: true
495
553
  });
496
- child2.on("close", () => registry.unregister(pid2));
497
- child2.on("error", () => {
554
+ child2.on("close", (code, signal) => {
555
+ bgSettled = true;
498
556
  registry.unregister(pid2);
557
+ if (bgErrorOccurred) return;
558
+ const failed = code !== 0 && code !== null || !!signal;
559
+ registry.afterCall(Date.now() - startedAt, failed, isBackground);
560
+ completeBackground(code ?? (signal ? 1 : 0), signal ?? void 0);
561
+ });
562
+ child2.on("error", () => {
563
+ if (bgSettled) return;
564
+ bgErrorOccurred = true;
499
565
  registry.afterCall(Date.now() - startedAt, true, isBackground);
566
+ completeBackground(1);
500
567
  });
501
568
  child2.unref();
502
569
  const jobId = `job_${pid2}`;
@@ -521,6 +588,7 @@ Working directory: ${targetCwd}`,
521
588
  };
522
589
  return;
523
590
  }
591
+ completeBackground(1);
524
592
  yield {
525
593
  type: "final",
526
594
  output: {
@@ -534,13 +602,46 @@ Working directory: ${targetCwd}`,
534
602
  return;
535
603
  }
536
604
  const spool = createOutputSpool({ tool: "pwsh", thresholdBytes: MAX_OUTPUT });
537
- const child = spawn(bin, args, {
538
- cwd: targetCwd,
539
- env,
540
- detached,
541
- stdio: ["ignore", "pipe", "pipe"],
542
- windowsHide: true
543
- });
605
+ let child;
606
+ try {
607
+ child = spawn(bin, args, {
608
+ cwd: targetCwd,
609
+ env,
610
+ detached,
611
+ stdio: ["ignore", "pipe", "pipe"],
612
+ windowsHide: true
613
+ });
614
+ } catch (err) {
615
+ registry.afterCall(Date.now() - startedAt, true, isBackground);
616
+ spool.finalize();
617
+ emitProcessStarted({
618
+ parentPid: process.pid,
619
+ command: redactCommand(`${bin} ${args.join(" ")}`),
620
+ args: redactCommand(args.join(" ")).split(" ").filter(Boolean),
621
+ cwd: targetCwd,
622
+ background: false,
623
+ startedAt: new Date(startedAt).toISOString()
624
+ });
625
+ emitProcessCompleted({
626
+ exitCode: 1,
627
+ durationMs: Date.now() - startedAt,
628
+ stdoutBytes: 0,
629
+ stderrBytes: 0,
630
+ timedOut: false,
631
+ endedAt: (/* @__PURE__ */ new Date()).toISOString()
632
+ });
633
+ yield {
634
+ type: "final",
635
+ output: {
636
+ output: "",
637
+ exit_code: 1,
638
+ timed_out: false,
639
+ pid: null,
640
+ error: `pwsh: spawn failed: ${err instanceof Error ? err.message : String(err)}`
641
+ }
642
+ };
643
+ return;
644
+ }
544
645
  const pid = child.pid;
545
646
  let stdoutBytes = 0;
546
647
  let stderrBytes = 0;
@@ -688,17 +789,25 @@ Working directory: ${targetCwd}`,
688
789
  push({ kind: "data", text });
689
790
  pauseIfFlooded();
690
791
  };
691
- child.stdout?.on("data", (chunk) => onData(chunk, "stdout"));
692
- child.stderr?.on("data", (chunk) => onData(chunk, "stderr"));
792
+ const onStdoutData = (chunk) => onData(chunk, "stdout");
793
+ const onStderrData = (chunk) => onData(chunk, "stderr");
794
+ child.stdout?.on("data", onStdoutData);
795
+ child.stderr?.on("data", onStderrData);
796
+ let fgErrorOccurred = false;
797
+ let sawClose = false;
693
798
  child.on("error", (err) => {
799
+ if (sawClose) return;
800
+ fgErrorOccurred = true;
694
801
  for (const t of timers) clearTimeout(t);
695
802
  registry.afterCall(Date.now() - startedAt, true);
696
803
  completeForeground(1);
697
804
  push({ kind: "error", err });
698
805
  });
699
806
  child.on("close", (code, signal) => {
807
+ sawClose = true;
700
808
  for (const t of timers) clearTimeout(t);
701
809
  if (typeof pid === "number") registry.unregister(pid);
810
+ if (fgErrorOccurred) return;
702
811
  registry.afterCall(Date.now() - startedAt, code !== 0 && code !== null);
703
812
  completeForeground(timedOut ? 124 : code ?? (signal ? 1 : 0), signal ?? void 0);
704
813
  const tail = stdoutDecoder.end() + stderrDecoder.end();
@@ -713,7 +822,33 @@ Working directory: ${targetCwd}`,
713
822
  while (true) {
714
823
  const c = await next();
715
824
  resumeIfDrained();
716
- if (c.kind === "error") throw c.err;
825
+ if (c.kind === "error") {
826
+ const isAbort = c.err?.code === "ABORT_ERR" || callerSignal.aborted;
827
+ const remainder = flush();
828
+ if (remainder !== null) {
829
+ yield { type: "partial_output", text: remainder };
830
+ }
831
+ const spooled = spool.finalize();
832
+ yield {
833
+ type: "final",
834
+ output: {
835
+ output: normalizeCommandOutput(buf) + (spooled ? spoolNote(spooled) : ""),
836
+ exit_code: isAbort ? 124 : 1,
837
+ timed_out: isAbort,
838
+ pid: pid ?? null,
839
+ error: isAbort ? "Command aborted by user or signal" : c.err.message
840
+ }
841
+ };
842
+ ctx.recordSideEffect?.({
843
+ toolUseId: `pwsh-${Date.now()}`,
844
+ toolName: "pwsh",
845
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
846
+ input: { command: redactCommand(input.command) },
847
+ outcome: isAbort ? "aborted" : `error (${c.err.message})`,
848
+ risk: "shell"
849
+ });
850
+ return;
851
+ }
717
852
  if (c.kind === "end") {
718
853
  const remainder = flush();
719
854
  if (remainder !== null) {
@@ -739,14 +874,16 @@ Working directory: ${targetCwd}`,
739
874
  const finalResultText = formattedOutput + (spooled ? spoolNote(spooled) : "") + (hint ? `
740
875
 
741
876
  ${hint}` : "") + cautionText;
742
- ctx.recordSideEffect?.({
743
- toolUseId: `pwsh-${Date.now()}`,
744
- toolName: "pwsh",
745
- ts: (/* @__PURE__ */ new Date()).toISOString(),
746
- input: { command: redactCommand(input.command) },
747
- outcome: timedOut ? `timed out (exit ${c.code})` : `exit ${c.code}`,
748
- risk: "shell"
749
- });
877
+ if (!fgErrorOccurred) {
878
+ ctx.recordSideEffect?.({
879
+ toolUseId: `pwsh-${Date.now()}`,
880
+ toolName: "pwsh",
881
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
882
+ input: { command: redactCommand(input.command) },
883
+ outcome: timedOut ? `timed out (exit ${c.code})` : `exit ${c.code}`,
884
+ risk: "shell"
885
+ });
886
+ }
750
887
  const isAborted = callerSignal.aborted;
751
888
  yield {
752
889
  type: "final",
@@ -768,7 +905,16 @@ ${hint}` : "") + cautionText;
768
905
  }
769
906
  } finally {
770
907
  for (const t of timers) clearTimeout(t);
908
+ spool.finalize();
771
909
  callerSignal.removeEventListener("abort", onAbort);
910
+ child.stdout?.off("data", onStdoutData);
911
+ child.stderr?.off("data", onStderrData);
912
+ child.stdout?.destroy();
913
+ child.stderr?.destroy();
914
+ if (!sawClose && child.exitCode === null && !child.killed) {
915
+ if (typeof pid === "number") registry.kill(pid, { force: true });
916
+ else killWithTimeout(2e3);
917
+ }
772
918
  }
773
919
  }
774
920
  };
@@ -1288,4 +1434,4 @@ export {
1288
1434
  TIER3_TOOLS,
1289
1435
  builtinTools
1290
1436
  };
1291
- //# sourceMappingURL=chunk-AB2CAUOP.js.map
1437
+ //# sourceMappingURL=chunk-IAYMTTR2.js.map
@@ -471,245 +471,6 @@ function levelRank(level) {
471
471
  }
472
472
  }
473
473
 
474
- // src/exec-kill-guard.ts
475
- import * as os from "node:os";
476
- import * as path from "node:path";
477
- var isWin = os.platform() === "win32";
478
- async function checkExecKillCommand(cmd, args) {
479
- if (!cmd) return { blocked: false };
480
- const cmdLower = cmd.toLowerCase().trim();
481
- const fullCommand = [cmdLower, ...args].join(" ").replace(/\s+/g, " ").trim();
482
- if (isWin) {
483
- if (cmdLower === "taskkill" || cmdLower === "taskkill.exe") {
484
- const hasForce = args.some((a) => a.toUpperCase() === "/F" || a.toUpperCase() === "-F");
485
- const signal = hasForce ? "FORCE" : "TERM";
486
- for (let i = 0; i < args.length; i++) {
487
- const a = args[i];
488
- if (a.toUpperCase() === "/IM" || a.toUpperCase() === "-IM") {
489
- const nameArg = args[i + 1];
490
- if (nameArg) {
491
- const result = await checkKillTarget({ name: nameArg, signal, cmd: fullCommand });
492
- if (result.blocked) return result;
493
- }
494
- }
495
- }
496
- for (let i = 0; i < args.length; i++) {
497
- const a = args[i];
498
- if (a.toUpperCase() === "/PID" || a.toUpperCase() === "-PID") {
499
- const pidArg = args[i + 1];
500
- if (pidArg && /^\d+$/.test(pidArg)) {
501
- const result = await checkKillTarget({
502
- pid: parseInt(pidArg, 10),
503
- signal,
504
- cmd: fullCommand
505
- });
506
- if (result.blocked) return result;
507
- }
508
- }
509
- }
510
- for (let i = 0; i < args.length; i++) {
511
- const a = args[i];
512
- if (a.toUpperCase() === "/FI" || a.toUpperCase() === "-FI") {
513
- const filterArg = args[i + 1];
514
- if (filterArg) {
515
- const nameMatch = filterArg.match(/IMAGENAME\s+eq\s+"?([^"\s]+)/i);
516
- if (nameMatch?.[1]) {
517
- const result = await checkKillTarget({
518
- name: nameMatch[1],
519
- signal,
520
- cmd: fullCommand
521
- });
522
- if (result.blocked) return result;
523
- }
524
- }
525
- }
526
- }
527
- return { blocked: false };
528
- }
529
- if (cmdLower === "powershell" || cmdLower === "powershell.exe" || cmdLower === "pwsh" || cmdLower === "pwsh.exe" || cmdLower === "cmd" || cmdLower === "cmd.exe") {
530
- const shellFlagIndex = args.findIndex((arg) => {
531
- const lower = arg.toLowerCase();
532
- return lower === "-c" || lower === "-command" || lower === "/c";
533
- });
534
- if (shellFlagIndex >= 0) {
535
- const innerTokens = tokenizeShellCommand(args.slice(shellFlagIndex + 1).join(" "));
536
- const innerCommand = innerTokens[0];
537
- if (innerCommand) {
538
- const result = await checkExecKillCommand(innerCommand, innerTokens.slice(1));
539
- if (result.blocked) return result;
540
- }
541
- }
542
- }
543
- if (cmdLower === "stop-process" || cmdLower === "kill") {
544
- for (let i = 0; i < args.length; i++) {
545
- const a = args[i];
546
- if (a === "-Name" || a === "-n") {
547
- const nameArg = args[i + 1]?.replace(/^['"]|['"]$/g, "");
548
- if (nameArg) {
549
- const result = await checkKillTarget({
550
- name: nameArg,
551
- signal: "FORCE",
552
- cmd: fullCommand
553
- });
554
- if (result.blocked) return result;
555
- }
556
- }
557
- if (a === "-Id" || a === "-PID" || a === "-pid") {
558
- const pidArg = args[i + 1];
559
- if (pidArg && /^\d+$/.test(pidArg)) {
560
- const result = await checkKillTarget({
561
- pid: parseInt(pidArg, 10),
562
- signal: "FORCE",
563
- cmd: fullCommand
564
- });
565
- if (result.blocked) return result;
566
- }
567
- }
568
- }
569
- const firstNonFlag = args.find((a) => !a.startsWith("-"));
570
- if (firstNonFlag) {
571
- const name = firstNonFlag.replace(/^['"]|['"]$/g, "");
572
- const result = await checkKillTarget({ name, signal: "TERM", cmd: fullCommand });
573
- if (result.blocked) return result;
574
- }
575
- }
576
- if (cmdLower === "wmic" || cmdLower === "wmic.exe") {
577
- const joined = args.join(" ").toLowerCase();
578
- if (/\bprocess\b/.test(joined) && /\bdelete\b/.test(joined)) {
579
- const nameMatch = joined.match(/name\s*=\s*['"]?([^'"]+)/);
580
- if (nameMatch?.[1]) {
581
- const result = await checkKillTarget({
582
- name: nameMatch[1].trim(),
583
- signal: "FORCE",
584
- cmd: fullCommand
585
- });
586
- if (result.blocked) return result;
587
- }
588
- return {
589
- blocked: true,
590
- reason: "Blocked: wmic process delete targets all matched processes \u2014 would include protected WrongStack processes."
591
- };
592
- }
593
- }
594
- if (cmdLower === "node" || cmdLower === "node.exe") {
595
- if (args.includes("-e") || args.includes("--eval")) {
596
- const evalIdx = args.indexOf("-e") !== -1 ? args.indexOf("-e") : args.indexOf("--eval");
597
- const evalCode = args[evalIdx + 1] ?? "";
598
- if (/\bprocess\.kill\s*\(/.test(evalCode)) {
599
- const pidMatch = evalCode.match(/process\.kill\s*\(\s*(\d+)/);
600
- if (pidMatch?.[1]) {
601
- const pid = parseInt(pidMatch[1], 10);
602
- const result = await checkKillTarget({ pid, signal: "SIGTERM", cmd: fullCommand });
603
- if (result.blocked) return result;
604
- }
605
- return {
606
- blocked: true,
607
- reason: "Blocked: node -e with process.kill() \u2014 would target protected WrongStack process(es)."
608
- };
609
- }
610
- }
611
- }
612
- } else {
613
- if (cmdLower === "kill") {
614
- for (const a of args) {
615
- const num = a.replace(/^-/, "");
616
- if (/^\d+$/.test(num)) {
617
- const pid = parseInt(num, 10);
618
- const result = await checkKillTarget({ pid, signal: "SIGTERM", cmd: fullCommand });
619
- if (result.blocked) return result;
620
- }
621
- }
622
- }
623
- if (cmdLower === "pkill" || cmdLower === "killall") {
624
- const firstNonFlag = args.find((a) => !a.startsWith("-"));
625
- if (firstNonFlag) {
626
- const result = await checkKillTarget({
627
- name: firstNonFlag,
628
- signal: "SIGTERM",
629
- cmd: fullCommand
630
- });
631
- if (result.blocked) return result;
632
- }
633
- }
634
- }
635
- return { blocked: false };
636
- }
637
- function tokenizeShellCommand(command) {
638
- const tokens = [];
639
- let current = "";
640
- let quote = null;
641
- for (const char of command.trim()) {
642
- if (quote) {
643
- if (char === quote) quote = null;
644
- else current += char;
645
- continue;
646
- }
647
- if (char === '"' || char === "'") {
648
- quote = char;
649
- } else if (/\s/.test(char)) {
650
- if (current) {
651
- tokens.push(current);
652
- current = "";
653
- }
654
- } else {
655
- current += char;
656
- }
657
- }
658
- if (current) tokens.push(current);
659
- return tokens;
660
- }
661
- async function checkKillTarget(target) {
662
- const registry = getPersistentProcessRegistry();
663
- if (target.pid !== void 0) {
664
- const blocked = await registry.shouldBlockKill(target.pid);
665
- if (blocked) {
666
- return {
667
- blocked: true,
668
- reason: `Blocked: kill ${target.signal} PID ${target.pid} targets a protected WrongStack process (${target.cmd.slice(0, 80)}).`
669
- };
670
- }
671
- if (target.pid === process.pid) {
672
- return {
673
- blocked: true,
674
- reason: "Blocked: cannot kill the current WrongStack process."
675
- };
676
- }
677
- if (target.pid === process.ppid) {
678
- return {
679
- blocked: true,
680
- reason: "Blocked: cannot kill the parent terminal hosting WrongStack."
681
- };
682
- }
683
- return { blocked: false };
684
- }
685
- if (target.name) {
686
- const nameLower = target.name.toLowerCase().replace(/\.exe$/, "");
687
- if (nameLower.includes("wrongstack")) {
688
- return {
689
- blocked: true,
690
- reason: `Blocked: kill ${target.signal} '${target.name}' targets a WrongStack process name.`
691
- };
692
- }
693
- const currentImage = path.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
694
- const targetsNodeRuntime = nameLower === "node" || nameLower.startsWith("node");
695
- if (targetsNodeRuntime && currentImage === "node") {
696
- return {
697
- blocked: true,
698
- reason: `Blocked: kill ${target.signal} '${target.name}' would kill the active WrongStack node.exe runtime.`
699
- };
700
- }
701
- const protectedPids = await registry.getAllProtectedPids();
702
- if (protectedPids.length > 0 && targetsNodeRuntime) {
703
- return {
704
- blocked: true,
705
- reason: `Blocked: kill ${target.signal} '${target.name}' would kill all node.exe processes including active WrongStack instance(s).`
706
- };
707
- }
708
- return { blocked: false };
709
- }
710
- return { blocked: false };
711
- }
712
-
713
474
  // src/exec-allowlist.ts
714
475
  var DEFAULT_ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
715
476
  // JS / TS toolchain
@@ -1300,6 +1061,245 @@ var DEFAULT_ALLOWED_COMMANDS = /* @__PURE__ */ new Set([
1300
1061
  "chaos"
1301
1062
  ]);
1302
1063
 
1064
+ // src/exec-kill-guard.ts
1065
+ import * as os from "node:os";
1066
+ import * as path from "node:path";
1067
+ var isWin = os.platform() === "win32";
1068
+ async function checkExecKillCommand(cmd, args) {
1069
+ if (!cmd) return { blocked: false };
1070
+ const cmdLower = cmd.toLowerCase().trim();
1071
+ const fullCommand = [cmdLower, ...args].join(" ").replace(/\s+/g, " ").trim();
1072
+ if (isWin) {
1073
+ if (cmdLower === "taskkill" || cmdLower === "taskkill.exe") {
1074
+ const hasForce = args.some((a) => a.toUpperCase() === "/F" || a.toUpperCase() === "-F");
1075
+ const signal = hasForce ? "FORCE" : "TERM";
1076
+ for (let i = 0; i < args.length; i++) {
1077
+ const a = args[i];
1078
+ if (a.toUpperCase() === "/IM" || a.toUpperCase() === "-IM") {
1079
+ const nameArg = args[i + 1];
1080
+ if (nameArg) {
1081
+ const result = await checkKillTarget({ name: nameArg, signal, cmd: fullCommand });
1082
+ if (result.blocked) return result;
1083
+ }
1084
+ }
1085
+ }
1086
+ for (let i = 0; i < args.length; i++) {
1087
+ const a = args[i];
1088
+ if (a.toUpperCase() === "/PID" || a.toUpperCase() === "-PID") {
1089
+ const pidArg = args[i + 1];
1090
+ if (pidArg && /^\d+$/.test(pidArg)) {
1091
+ const result = await checkKillTarget({
1092
+ pid: parseInt(pidArg, 10),
1093
+ signal,
1094
+ cmd: fullCommand
1095
+ });
1096
+ if (result.blocked) return result;
1097
+ }
1098
+ }
1099
+ }
1100
+ for (let i = 0; i < args.length; i++) {
1101
+ const a = args[i];
1102
+ if (a.toUpperCase() === "/FI" || a.toUpperCase() === "-FI") {
1103
+ const filterArg = args[i + 1];
1104
+ if (filterArg) {
1105
+ const nameMatch = filterArg.match(/IMAGENAME\s+eq\s+"?([^"\s]+)/i);
1106
+ if (nameMatch?.[1]) {
1107
+ const result = await checkKillTarget({
1108
+ name: nameMatch[1],
1109
+ signal,
1110
+ cmd: fullCommand
1111
+ });
1112
+ if (result.blocked) return result;
1113
+ }
1114
+ }
1115
+ }
1116
+ }
1117
+ return { blocked: false };
1118
+ }
1119
+ if (cmdLower === "powershell" || cmdLower === "powershell.exe" || cmdLower === "pwsh" || cmdLower === "pwsh.exe" || cmdLower === "cmd" || cmdLower === "cmd.exe") {
1120
+ const shellFlagIndex = args.findIndex((arg) => {
1121
+ const lower = arg.toLowerCase();
1122
+ return lower === "-c" || lower === "-command" || lower === "/c";
1123
+ });
1124
+ if (shellFlagIndex >= 0) {
1125
+ const innerTokens = tokenizeShellCommand(args.slice(shellFlagIndex + 1).join(" "));
1126
+ const innerCommand = innerTokens[0];
1127
+ if (innerCommand) {
1128
+ const result = await checkExecKillCommand(innerCommand, innerTokens.slice(1));
1129
+ if (result.blocked) return result;
1130
+ }
1131
+ }
1132
+ }
1133
+ if (cmdLower === "stop-process" || cmdLower === "kill") {
1134
+ for (let i = 0; i < args.length; i++) {
1135
+ const a = args[i];
1136
+ if (a === "-Name" || a === "-n") {
1137
+ const nameArg = args[i + 1]?.replace(/^['"]|['"]$/g, "");
1138
+ if (nameArg) {
1139
+ const result = await checkKillTarget({
1140
+ name: nameArg,
1141
+ signal: "FORCE",
1142
+ cmd: fullCommand
1143
+ });
1144
+ if (result.blocked) return result;
1145
+ }
1146
+ }
1147
+ if (a === "-Id" || a === "-PID" || a === "-pid") {
1148
+ const pidArg = args[i + 1];
1149
+ if (pidArg && /^\d+$/.test(pidArg)) {
1150
+ const result = await checkKillTarget({
1151
+ pid: parseInt(pidArg, 10),
1152
+ signal: "FORCE",
1153
+ cmd: fullCommand
1154
+ });
1155
+ if (result.blocked) return result;
1156
+ }
1157
+ }
1158
+ }
1159
+ const firstNonFlag = args.find((a) => !a.startsWith("-"));
1160
+ if (firstNonFlag) {
1161
+ const name = firstNonFlag.replace(/^['"]|['"]$/g, "");
1162
+ const result = await checkKillTarget({ name, signal: "TERM", cmd: fullCommand });
1163
+ if (result.blocked) return result;
1164
+ }
1165
+ }
1166
+ if (cmdLower === "wmic" || cmdLower === "wmic.exe") {
1167
+ const joined = args.join(" ").toLowerCase();
1168
+ if (/\bprocess\b/.test(joined) && /\bdelete\b/.test(joined)) {
1169
+ const nameMatch = joined.match(/name\s*=\s*['"]?([^'"]+)/);
1170
+ if (nameMatch?.[1]) {
1171
+ const result = await checkKillTarget({
1172
+ name: nameMatch[1].trim(),
1173
+ signal: "FORCE",
1174
+ cmd: fullCommand
1175
+ });
1176
+ if (result.blocked) return result;
1177
+ }
1178
+ return {
1179
+ blocked: true,
1180
+ reason: "Blocked: wmic process delete targets all matched processes \u2014 would include protected WrongStack processes."
1181
+ };
1182
+ }
1183
+ }
1184
+ if (cmdLower === "node" || cmdLower === "node.exe") {
1185
+ if (args.includes("-e") || args.includes("--eval")) {
1186
+ const evalIdx = args.indexOf("-e") !== -1 ? args.indexOf("-e") : args.indexOf("--eval");
1187
+ const evalCode = args[evalIdx + 1] ?? "";
1188
+ if (/\bprocess\.kill\s*\(/.test(evalCode)) {
1189
+ const pidMatch = evalCode.match(/process\.kill\s*\(\s*(\d+)/);
1190
+ if (pidMatch?.[1]) {
1191
+ const pid = parseInt(pidMatch[1], 10);
1192
+ const result = await checkKillTarget({ pid, signal: "SIGTERM", cmd: fullCommand });
1193
+ if (result.blocked) return result;
1194
+ }
1195
+ return {
1196
+ blocked: true,
1197
+ reason: "Blocked: node -e with process.kill() \u2014 would target protected WrongStack process(es)."
1198
+ };
1199
+ }
1200
+ }
1201
+ }
1202
+ } else {
1203
+ if (cmdLower === "kill") {
1204
+ for (const a of args) {
1205
+ const num = a.replace(/^-/, "");
1206
+ if (/^\d+$/.test(num)) {
1207
+ const pid = parseInt(num, 10);
1208
+ const result = await checkKillTarget({ pid, signal: "SIGTERM", cmd: fullCommand });
1209
+ if (result.blocked) return result;
1210
+ }
1211
+ }
1212
+ }
1213
+ if (cmdLower === "pkill" || cmdLower === "killall") {
1214
+ const firstNonFlag = args.find((a) => !a.startsWith("-"));
1215
+ if (firstNonFlag) {
1216
+ const result = await checkKillTarget({
1217
+ name: firstNonFlag,
1218
+ signal: "SIGTERM",
1219
+ cmd: fullCommand
1220
+ });
1221
+ if (result.blocked) return result;
1222
+ }
1223
+ }
1224
+ }
1225
+ return { blocked: false };
1226
+ }
1227
+ function tokenizeShellCommand(command) {
1228
+ const tokens = [];
1229
+ let current = "";
1230
+ let quote = null;
1231
+ for (const char of command.trim()) {
1232
+ if (quote) {
1233
+ if (char === quote) quote = null;
1234
+ else current += char;
1235
+ continue;
1236
+ }
1237
+ if (char === '"' || char === "'") {
1238
+ quote = char;
1239
+ } else if (/\s/.test(char)) {
1240
+ if (current) {
1241
+ tokens.push(current);
1242
+ current = "";
1243
+ }
1244
+ } else {
1245
+ current += char;
1246
+ }
1247
+ }
1248
+ if (current) tokens.push(current);
1249
+ return tokens;
1250
+ }
1251
+ async function checkKillTarget(target) {
1252
+ const registry = getPersistentProcessRegistry();
1253
+ if (target.pid !== void 0) {
1254
+ const blocked = await registry.shouldBlockKill(target.pid);
1255
+ if (blocked) {
1256
+ return {
1257
+ blocked: true,
1258
+ reason: `Blocked: kill ${target.signal} PID ${target.pid} targets a protected WrongStack process (${target.cmd.slice(0, 80)}).`
1259
+ };
1260
+ }
1261
+ if (target.pid === process.pid) {
1262
+ return {
1263
+ blocked: true,
1264
+ reason: "Blocked: cannot kill the current WrongStack process."
1265
+ };
1266
+ }
1267
+ if (target.pid === process.ppid) {
1268
+ return {
1269
+ blocked: true,
1270
+ reason: "Blocked: cannot kill the parent terminal hosting WrongStack."
1271
+ };
1272
+ }
1273
+ return { blocked: false };
1274
+ }
1275
+ if (target.name) {
1276
+ const nameLower = target.name.toLowerCase().replace(/\.exe$/, "");
1277
+ if (nameLower.includes("wrongstack")) {
1278
+ return {
1279
+ blocked: true,
1280
+ reason: `Blocked: kill ${target.signal} '${target.name}' targets a WrongStack process name.`
1281
+ };
1282
+ }
1283
+ const currentImage = path.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
1284
+ const targetsNodeRuntime = nameLower === "node" || nameLower.startsWith("node");
1285
+ if (targetsNodeRuntime && currentImage === "node") {
1286
+ return {
1287
+ blocked: true,
1288
+ reason: `Blocked: kill ${target.signal} '${target.name}' would kill the active WrongStack node.exe runtime.`
1289
+ };
1290
+ }
1291
+ const protectedPids = await registry.getAllProtectedPids();
1292
+ if (protectedPids.length > 0 && targetsNodeRuntime) {
1293
+ return {
1294
+ blocked: true,
1295
+ reason: `Blocked: kill ${target.signal} '${target.name}' would kill all node.exe processes including active WrongStack instance(s).`
1296
+ };
1297
+ }
1298
+ return { blocked: false };
1299
+ }
1300
+ return { blocked: false };
1301
+ }
1302
+
1303
1303
  // src/exec.ts
1304
1304
  var isWin2 = process.platform === "win32";
1305
1305
  var allowedCommands = new Set(DEFAULT_ALLOWED_COMMANDS);
@@ -1351,10 +1351,9 @@ var BLOCKED_ARG_PATTERNS = {
1351
1351
  /^--exec=/,
1352
1352
  /^--upload-pack=/,
1353
1353
  /^--receive-pack=/,
1354
- /^-C$/,
1355
- /^-c$/,
1354
+ /^-C/,
1355
+ /^-c/,
1356
1356
  /^--config$/,
1357
- /^-c=/,
1358
1357
  /^--config=/,
1359
1358
  /^--config-env=/
1360
1359
  ],
@@ -1454,6 +1453,7 @@ function validateArgs(cmd, args) {
1454
1453
  const blocked = BLOCKED_ARG_PATTERNS[cmd];
1455
1454
  if (!blocked) return null;
1456
1455
  for (const arg of args) {
1456
+ if (arg === "--") break;
1457
1457
  for (const pattern of blocked) {
1458
1458
  if (pattern.test(arg)) {
1459
1459
  return `Blocked argument "${arg}" for command "${cmd}" (matches security pattern ${pattern})`;
@@ -1657,11 +1657,6 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
1657
1657
  });
1658
1658
  return;
1659
1659
  }
1660
- const resolved = resolveWin32Command(cmd);
1661
- const needsShell = isWin2 && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
1662
- const shim = needsShell ? buildWin32CmdShimInvocation(resolved, args) : null;
1663
- const spawnCmd = shim?.command ?? resolved;
1664
- const spawnArgs = shim?.args ?? args;
1665
1660
  const emitCompletedOnce = (exitCode, pid2, signal2) => {
1666
1661
  if (telemetryCompleted) return;
1667
1662
  telemetryCompleted = true;
@@ -1678,6 +1673,11 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
1678
1673
  };
1679
1674
  let child;
1680
1675
  try {
1676
+ const resolved = resolveWin32Command(cmd);
1677
+ const needsShell = isWin2 && (resolved.endsWith(".cmd") || resolved.endsWith(".bat"));
1678
+ const shim = needsShell ? buildWin32CmdShimInvocation(resolved, args) : null;
1679
+ const spawnCmd = shim?.command ?? resolved;
1680
+ const spawnArgs = shim?.args ?? args;
1681
1681
  child = spawn(spawnCmd, spawnArgs, {
1682
1682
  cwd,
1683
1683
  env: buildChildEnv(sessionId),
@@ -1687,6 +1687,7 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
1687
1687
  ...shim ? { windowsVerbatimArguments: shim.windowsVerbatimArguments } : {}
1688
1688
  });
1689
1689
  } catch (err) {
1690
+ getProcessRegistry().afterCall(Date.now() - startedAt, true);
1690
1691
  spool.finalize();
1691
1692
  emitProcessStarted({
1692
1693
  parentPid: process.pid,
@@ -1718,10 +1719,18 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
1718
1719
  background: false,
1719
1720
  startedAt: new Date(startedAt).toISOString()
1720
1721
  });
1722
+ const registry = getProcessRegistry();
1723
+ const pid = child.pid;
1724
+ let timer;
1725
+ const onAbort = () => {
1726
+ killed = true;
1727
+ if (typeof pid === "number") registry.kill(pid, { force: true });
1728
+ else child.kill("SIGTERM");
1729
+ };
1721
1730
  child.on("error", (err) => {
1722
1731
  const isAbort = err && err.code === "ABORT_ERR";
1723
1732
  const stderrText = isAbort ? `Aborted: ${err.message}` : err.message;
1724
- clearTimeout(timer);
1733
+ if (timer !== void 0) clearTimeout(timer);
1725
1734
  if (isWin2) signal.removeEventListener("abort", onAbort);
1726
1735
  if (typeof pid === "number") registry.unregister(pid);
1727
1736
  registry.afterCall(Date.now() - startedAt, true);
@@ -1738,8 +1747,6 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
1738
1747
  danger
1739
1748
  });
1740
1749
  });
1741
- const registry = getProcessRegistry();
1742
- const pid = child.pid;
1743
1750
  if (typeof pid === "number") {
1744
1751
  const fullCommand = `${cmd} ${args.join(" ")}`;
1745
1752
  registry.register({
@@ -1751,17 +1758,12 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
1751
1758
  child
1752
1759
  });
1753
1760
  }
1754
- const timer = setTimeout(() => {
1761
+ timer = setTimeout(() => {
1755
1762
  killed = true;
1756
1763
  timedOut = true;
1757
1764
  if (typeof pid === "number") registry.kill(pid);
1758
1765
  else child.kill("SIGTERM");
1759
1766
  }, timeout);
1760
- const onAbort = () => {
1761
- killed = true;
1762
- if (typeof pid === "number") registry.kill(pid, { force: true });
1763
- else child.kill("SIGTERM");
1764
- };
1765
1767
  if (isWin2) {
1766
1768
  if (signal.aborted) onAbort();
1767
1769
  else signal.addEventListener("abort", onAbort, { once: true });
@@ -1787,9 +1789,10 @@ function runCommand(cmd, args, cwd, timeout, signal, sessionId, danger) {
1787
1789
  }
1788
1790
  });
1789
1791
  child.on("close", (code) => {
1790
- clearTimeout(timer);
1792
+ if (timer !== void 0) clearTimeout(timer);
1791
1793
  if (isWin2) signal.removeEventListener("abort", onAbort);
1792
1794
  if (typeof pid === "number") registry.unregister(pid);
1795
+ if (resolvedOnce.value) return;
1793
1796
  const durationMs = Date.now() - startedAt;
1794
1797
  const exitCode = killed ? 124 : code ?? 1;
1795
1798
  emitCompletedOnce(exitCode, pid);
@@ -1832,4 +1835,4 @@ export {
1832
1835
  getExecAllowlist,
1833
1836
  execTool
1834
1837
  };
1835
- //# sourceMappingURL=chunk-KDEXUQY7.js.map
1838
+ //# sourceMappingURL=chunk-MGPR7VG3.js.map
@@ -168,4 +168,4 @@ export {
168
168
  MAX_ADVANCEMENT_ATTEMPTS,
169
169
  createAutoProceedLoopGuard
170
170
  };
171
- //# sourceMappingURL=chunk-37QKUQ2Z.js.map
171
+ //# sourceMappingURL=chunk-NL2S2CXD.js.map
package/dist/exec.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  isExecCommandAllowed,
8
8
  resetDangerBypass,
9
9
  resetExecPolicy
10
- } from "./chunk-KDEXUQY7.js";
10
+ } from "./chunk-MGPR7VG3.js";
11
11
  import "./chunk-BTSFTGIL.js";
12
12
  import "./chunk-PVCB7RUZ.js";
13
13
  import "./chunk-SCMRM6NL.js";
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ import {
16
16
  GROUNDED_NO_PROGRESS_STEER,
17
17
  createAutoProceedLoopGuard,
18
18
  normalizeForRepetition
19
- } from "./chunk-37QKUQ2Z.js";
19
+ } from "./chunk-NL2S2CXD.js";
20
20
  import {
21
21
  createGlobalPsSlashCommand,
22
22
  formatGlobalStatus,
@@ -41,10 +41,10 @@ import "./chunk-Z6DAKQ3U.js";
41
41
  import {
42
42
  registerBuiltinToolTier,
43
43
  selectBuiltinToolsForTier
44
- } from "./chunk-YULVUTK5.js";
44
+ } from "./chunk-7LQCT3LT.js";
45
45
  import {
46
46
  builtinToolsPack
47
- } from "./chunk-M6UV67BH.js";
47
+ } from "./chunk-E7JE3UEX.js";
48
48
  import {
49
49
  OFF_ONLY_TOOLS,
50
50
  OPTIONAL_TOOLS,
@@ -59,7 +59,7 @@ import {
59
59
  pwshTool,
60
60
  securityAstScanTool,
61
61
  setWorkingDirTool
62
- } from "./chunk-AB2CAUOP.js";
62
+ } from "./chunk-IAYMTTR2.js";
63
63
  import {
64
64
  discoverE2EProjects,
65
65
  e2ePlanTool
@@ -193,7 +193,7 @@ import {
193
193
  import {
194
194
  bashTool,
195
195
  checkAndBlockKillCommand
196
- } from "./chunk-NN2SNP2R.js";
196
+ } from "./chunk-6H4ERJP7.js";
197
197
  import {
198
198
  checkExecKillCommand,
199
199
  configureDangerBypass,
@@ -205,7 +205,7 @@ import {
205
205
  isExecCommandAllowed,
206
206
  resetDangerBypass,
207
207
  resetExecPolicy
208
- } from "./chunk-KDEXUQY7.js";
208
+ } from "./chunk-MGPR7VG3.js";
209
209
  import "./chunk-BTSFTGIL.js";
210
210
  import {
211
211
  getPersistentProcessRegistry,
package/dist/pack.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  builtinToolsPack
3
- } from "./chunk-M6UV67BH.js";
4
- import "./chunk-AB2CAUOP.js";
3
+ } from "./chunk-E7JE3UEX.js";
4
+ import "./chunk-IAYMTTR2.js";
5
5
  import "./chunk-RV7BVTDG.js";
6
6
  import "./chunk-YEISRT4S.js";
7
7
  import "./chunk-XRNN44QY.js";
@@ -35,8 +35,8 @@ import "./chunk-ASEXQRA7.js";
35
35
  import "./chunk-4ERMPDHV.js";
36
36
  import "./chunk-YJTXRH5Y.js";
37
37
  import "./chunk-ZBMANLYH.js";
38
- import "./chunk-NN2SNP2R.js";
39
- import "./chunk-KDEXUQY7.js";
38
+ import "./chunk-6H4ERJP7.js";
39
+ import "./chunk-MGPR7VG3.js";
40
40
  import "./chunk-BTSFTGIL.js";
41
41
  import "./chunk-PVCB7RUZ.js";
42
42
  import "./chunk-SCMRM6NL.js";
package/dist/tool-tier.js CHANGED
@@ -2,9 +2,9 @@ import {
2
2
  BUILTIN_TIER_COUNTS,
3
3
  registerBuiltinToolTier,
4
4
  selectBuiltinToolsForTier
5
- } from "./chunk-YULVUTK5.js";
6
- import "./chunk-M6UV67BH.js";
7
- import "./chunk-AB2CAUOP.js";
5
+ } from "./chunk-7LQCT3LT.js";
6
+ import "./chunk-E7JE3UEX.js";
7
+ import "./chunk-IAYMTTR2.js";
8
8
  import "./chunk-RV7BVTDG.js";
9
9
  import "./chunk-YEISRT4S.js";
10
10
  import "./chunk-XRNN44QY.js";
@@ -38,8 +38,8 @@ import "./chunk-ASEXQRA7.js";
38
38
  import "./chunk-4ERMPDHV.js";
39
39
  import "./chunk-YJTXRH5Y.js";
40
40
  import "./chunk-ZBMANLYH.js";
41
- import "./chunk-NN2SNP2R.js";
42
- import "./chunk-KDEXUQY7.js";
41
+ import "./chunk-6H4ERJP7.js";
42
+ import "./chunk-MGPR7VG3.js";
43
43
  import "./chunk-BTSFTGIL.js";
44
44
  import "./chunk-PVCB7RUZ.js";
45
45
  import "./chunk-SCMRM6NL.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/tools",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "license": "MIT",
5
5
  "description": "WrongStack built-in tools: read/write/edit, bash/exec, grep/glob, git, fetch, test, lint, and more.",
6
6
  "repository": {
@@ -249,10 +249,10 @@
249
249
  "@msgpack/msgpack": "^3.1.3",
250
250
  "@playwright/test": "^1.62.1",
251
251
  "@typescript/typescript6": "^6.0.2",
252
- "@wrongstack/core": "1.0.0",
253
- "@wrongstack/kanban": "1.0.0",
254
- "@wrongstack/persistence": "1.0.0",
255
- "@wrongstack/primitives": "1.0.0",
252
+ "@wrongstack/core": "1.0.1",
253
+ "@wrongstack/kanban": "1.0.1",
254
+ "@wrongstack/persistence": "1.0.1",
255
+ "@wrongstack/primitives": "1.0.1",
256
256
  "tree-sitter-wasm": "1.1.4",
257
257
  "turndown": "^7.2.4",
258
258
  "undici": "^8.10.0",