@sesamespace/hivemind 0.8.11 → 0.8.13

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.
@@ -1,3 +1,36 @@
1
+ import {
2
+ __require
3
+ } from "./chunk-DGUM43GV.js";
4
+
5
+ // packages/runtime/src/logger.ts
6
+ var LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
7
+ var currentLevel = process.env.HIVEMIND_LOG_LEVEL || "info";
8
+ function setLogLevel(level) {
9
+ currentLevel = level;
10
+ }
11
+ function createLogger(component) {
12
+ const log4 = (level, msg, data) => {
13
+ if (LEVELS[level] < LEVELS[currentLevel]) return;
14
+ const entry = {
15
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
16
+ level,
17
+ component,
18
+ msg,
19
+ ...data
20
+ };
21
+ const line = JSON.stringify(entry);
22
+ if (level === "error") console.error(line);
23
+ else if (level === "warn") console.warn(line);
24
+ else console.log(line);
25
+ };
26
+ return {
27
+ debug: (msg, data) => log4("debug", msg, data),
28
+ info: (msg, data) => log4("info", msg, data),
29
+ warn: (msg, data) => log4("warn", msg, data),
30
+ error: (msg, data) => log4("error", msg, data)
31
+ };
32
+ }
33
+
1
34
  // packages/runtime/src/llm-client.ts
2
35
  import { execSync } from "child_process";
3
36
  function convertMessagesForAnthropic(messages) {
@@ -3012,7 +3045,7 @@ var SesameClient = class {
3012
3045
  * Connect to the real-time WebSocket gateway.
3013
3046
  */
3014
3047
  connect() {
3015
- return new Promise((resolve20, reject) => {
3048
+ return new Promise((resolve21, reject) => {
3016
3049
  try {
3017
3050
  this.ws = new WebSocket(`${this.config.wsUrl}/v1/connect`);
3018
3051
  this.ws.on("open", () => {
@@ -3026,7 +3059,7 @@ var SesameClient = class {
3026
3059
  this.authenticated = true;
3027
3060
  this.startHeartbeat(event.heartbeatIntervalMs ?? 3e4);
3028
3061
  this.sendReplay();
3029
- resolve20();
3062
+ resolve21();
3030
3063
  return;
3031
3064
  }
3032
3065
  if (event.type === "pong")
@@ -3585,6 +3618,977 @@ var SkillsEngine = class {
3585
3618
  }
3586
3619
  };
3587
3620
 
3621
+ // packages/runtime/src/processors/error-registry.ts
3622
+ import { readFileSync as readFileSync7, writeFileSync, mkdirSync as mkdirSync4, existsSync as existsSync6 } from "fs";
3623
+ import { createHash } from "crypto";
3624
+ import { dirname as dirname4 } from "path";
3625
+ var log = createLogger("error-registry");
3626
+ function normalizeForHash(msg, stack) {
3627
+ let text = msg;
3628
+ if (stack) {
3629
+ text += "\n" + stack;
3630
+ }
3631
+ text = text.replace(/:\d+:\d+/g, ":X:X");
3632
+ text = text.replace(/\d{4}-\d{2}-\d{2}T[\d:.Z]+/g, "T");
3633
+ text = text.replace(/\b\d{4,}\b/g, "N");
3634
+ return text;
3635
+ }
3636
+ function hashError(msg, stack) {
3637
+ const normalized = normalizeForHash(msg, stack);
3638
+ return createHash("sha256").update(normalized).digest("hex").slice(0, 16);
3639
+ }
3640
+ function scoreSeverity(entry) {
3641
+ let score = 0;
3642
+ if (entry.level === "crash") score += 5;
3643
+ const firstMs = new Date(entry.firstSeen).getTime();
3644
+ const lastMs = new Date(entry.lastSeen).getTime();
3645
+ const spanMinutes = (lastMs - firstMs) / 6e4;
3646
+ if (entry.totalOccurrences >= 3 && spanMinutes < 5) score += 3;
3647
+ const spanHours = Math.max(spanMinutes / 60, 1 / 60);
3648
+ if (entry.totalOccurrences / spanHours > 10) score += 2;
3649
+ if (/sesame/i.test(entry.message) || /sesame/i.test(entry.stackTrace ?? "")) {
3650
+ score += 2;
3651
+ }
3652
+ if (/memory/i.test(entry.message) || /memory/i.test(entry.stackTrace ?? "")) {
3653
+ score += 1;
3654
+ }
3655
+ if (entry.status === "fix-submitted" || entry.status === "wont-fix") {
3656
+ score -= 10;
3657
+ }
3658
+ return score;
3659
+ }
3660
+ var ErrorRegistry = class {
3661
+ filePath;
3662
+ entries = /* @__PURE__ */ new Map();
3663
+ constructor(dataDir) {
3664
+ this.filePath = `${dataDir}/error-registry.json`;
3665
+ this.load();
3666
+ }
3667
+ // ---- persistence -------------------------------------------------------
3668
+ load() {
3669
+ try {
3670
+ if (!existsSync6(this.filePath)) return;
3671
+ const raw = readFileSync7(this.filePath, "utf-8");
3672
+ const data = JSON.parse(raw);
3673
+ if (data.version === 1 && data.errors) {
3674
+ for (const [id, entry] of Object.entries(data.errors)) {
3675
+ this.entries.set(id, entry);
3676
+ }
3677
+ }
3678
+ log.debug("loaded error registry", { count: this.entries.size });
3679
+ } catch (err) {
3680
+ log.warn("failed to load error registry, starting fresh", {
3681
+ error: err.message
3682
+ });
3683
+ }
3684
+ }
3685
+ save() {
3686
+ try {
3687
+ const dir = dirname4(this.filePath);
3688
+ if (!existsSync6(dir)) mkdirSync4(dir, { recursive: true });
3689
+ const data = {
3690
+ version: 1,
3691
+ errors: Object.fromEntries(this.entries)
3692
+ };
3693
+ writeFileSync(this.filePath, JSON.stringify(data, null, 2));
3694
+ } catch (err) {
3695
+ log.error("failed to save error registry", {
3696
+ error: err.message
3697
+ });
3698
+ }
3699
+ }
3700
+ // ---- public API --------------------------------------------------------
3701
+ /** Record an error event, creating or updating the registry entry. */
3702
+ recordError(event) {
3703
+ const existing = this.entries.get(event.id);
3704
+ if (existing) {
3705
+ existing.lastSeen = event.timestamp.toISOString();
3706
+ existing.totalOccurrences += event.occurrences;
3707
+ if (event.level === "crash" && existing.level !== "crash") {
3708
+ existing.level = "crash";
3709
+ }
3710
+ existing.severity = scoreSeverity(existing);
3711
+ this.save();
3712
+ log.debug("updated error entry", {
3713
+ id: event.id,
3714
+ total: existing.totalOccurrences,
3715
+ severity: existing.severity
3716
+ });
3717
+ return existing;
3718
+ }
3719
+ const entry = {
3720
+ id: event.id,
3721
+ message: event.message,
3722
+ source: event.source,
3723
+ level: event.level,
3724
+ stackTrace: event.stackTrace,
3725
+ firstSeen: event.timestamp.toISOString(),
3726
+ lastSeen: event.timestamp.toISOString(),
3727
+ totalOccurrences: event.occurrences,
3728
+ status: "new",
3729
+ severity: 0
3730
+ };
3731
+ entry.severity = scoreSeverity(entry);
3732
+ this.entries.set(event.id, entry);
3733
+ this.save();
3734
+ log.info("new error registered", {
3735
+ id: event.id,
3736
+ message: event.message.slice(0, 120),
3737
+ severity: entry.severity
3738
+ });
3739
+ return entry;
3740
+ }
3741
+ /** Return errors above the severity threshold that are actionable (new or investigating). */
3742
+ getActionable(threshold) {
3743
+ const results = [];
3744
+ for (const entry of this.entries.values()) {
3745
+ entry.severity = scoreSeverity(entry);
3746
+ if (entry.severity >= threshold && (entry.status === "new" || entry.status === "investigating")) {
3747
+ results.push(entry);
3748
+ }
3749
+ }
3750
+ results.sort((a, b) => b.severity - a.severity);
3751
+ return results;
3752
+ }
3753
+ /** Update the status (and optional PR url) for an error. */
3754
+ updateStatus(id, status, prUrl) {
3755
+ const entry = this.entries.get(id);
3756
+ if (!entry) return;
3757
+ entry.status = status;
3758
+ if (prUrl) entry.prUrl = prUrl;
3759
+ entry.severity = scoreSeverity(entry);
3760
+ this.save();
3761
+ log.info("error status updated", { id, status, prUrl });
3762
+ }
3763
+ /** Remove entries older than the given date. */
3764
+ cleanup(olderThan) {
3765
+ let removed = 0;
3766
+ for (const [id, entry] of this.entries) {
3767
+ if (new Date(entry.lastSeen) < olderThan) {
3768
+ this.entries.delete(id);
3769
+ removed++;
3770
+ }
3771
+ }
3772
+ if (removed > 0) {
3773
+ this.save();
3774
+ log.info("cleaned up old errors", { removed });
3775
+ }
3776
+ return removed;
3777
+ }
3778
+ /** Get a single entry by id. */
3779
+ get(id) {
3780
+ return this.entries.get(id);
3781
+ }
3782
+ /** Total number of tracked errors. */
3783
+ get size() {
3784
+ return this.entries.size;
3785
+ }
3786
+ };
3787
+
3788
+ // packages/runtime/src/processors/log-watcher.ts
3789
+ import { readFileSync as readFileSync8, statSync as statSync2, existsSync as existsSync7 } from "fs";
3790
+
3791
+ // packages/runtime/src/memory/background-processor.ts
3792
+ import { EventEmitter } from "events";
3793
+ var BackgroundProcess = class extends EventEmitter {
3794
+ name;
3795
+ interval;
3796
+ enabled = true;
3797
+ timer;
3798
+ running = false;
3799
+ lastRun;
3800
+ lastResult;
3801
+ constructor(name, interval) {
3802
+ super();
3803
+ this.name = name;
3804
+ this.interval = interval;
3805
+ }
3806
+ /**
3807
+ * Determine if this process should run now
3808
+ */
3809
+ async shouldRun(context) {
3810
+ if (!this.enabled || this.running) return false;
3811
+ return true;
3812
+ }
3813
+ /**
3814
+ * Start the background process
3815
+ */
3816
+ start(context) {
3817
+ if (this.timer) return;
3818
+ this.runOnce(context);
3819
+ this.timer = setInterval(() => {
3820
+ this.runOnce(context);
3821
+ }, this.interval);
3822
+ }
3823
+ /**
3824
+ * Stop the background process
3825
+ */
3826
+ stop() {
3827
+ if (this.timer) {
3828
+ clearInterval(this.timer);
3829
+ this.timer = void 0;
3830
+ }
3831
+ }
3832
+ /**
3833
+ * Run the process once
3834
+ */
3835
+ async runOnce(context) {
3836
+ if (!await this.shouldRun(context)) return;
3837
+ this.running = true;
3838
+ const startTime2 = Date.now();
3839
+ try {
3840
+ this.emit("start", { name: this.name, time: /* @__PURE__ */ new Date() });
3841
+ const result = await this.process(context);
3842
+ this.lastResult = result;
3843
+ this.lastRun = /* @__PURE__ */ new Date();
3844
+ if (result.nextRunIn && result.nextRunIn !== this.interval) {
3845
+ this.stop();
3846
+ this.interval = result.nextRunIn;
3847
+ this.start(context);
3848
+ }
3849
+ this.emit("complete", {
3850
+ name: this.name,
3851
+ duration: Date.now() - startTime2,
3852
+ result
3853
+ });
3854
+ if (result.errors.length > 0) {
3855
+ this.emit("error", {
3856
+ name: this.name,
3857
+ errors: result.errors
3858
+ });
3859
+ }
3860
+ } catch (error) {
3861
+ this.emit("error", {
3862
+ name: this.name,
3863
+ error: error instanceof Error ? error.message : String(error)
3864
+ });
3865
+ } finally {
3866
+ this.running = false;
3867
+ }
3868
+ }
3869
+ /**
3870
+ * Get process status
3871
+ */
3872
+ getStatus() {
3873
+ return {
3874
+ name: this.name,
3875
+ enabled: this.enabled,
3876
+ running: this.running,
3877
+ interval: this.interval,
3878
+ lastRun: this.lastRun,
3879
+ lastResult: this.lastResult
3880
+ };
3881
+ }
3882
+ };
3883
+ var ProcessManager = class extends EventEmitter {
3884
+ processes = /* @__PURE__ */ new Map();
3885
+ context;
3886
+ started = false;
3887
+ constructor(context) {
3888
+ super();
3889
+ this.context = context;
3890
+ }
3891
+ /**
3892
+ * Register a background process
3893
+ */
3894
+ register(process2) {
3895
+ if (this.processes.has(process2.name)) {
3896
+ throw new Error(`Process ${process2.name} already registered`);
3897
+ }
3898
+ process2.on("start", (data) => this.emit("process:start", data));
3899
+ process2.on("complete", (data) => this.emit("process:complete", data));
3900
+ process2.on("error", (data) => this.emit("process:error", data));
3901
+ this.processes.set(process2.name, process2);
3902
+ if (this.started) {
3903
+ process2.start(this.context);
3904
+ }
3905
+ }
3906
+ /**
3907
+ * Start all registered processes
3908
+ */
3909
+ start() {
3910
+ if (this.started) return;
3911
+ for (const process2 of this.processes.values()) {
3912
+ process2.start(this.context);
3913
+ }
3914
+ this.started = true;
3915
+ this.emit("started");
3916
+ }
3917
+ /**
3918
+ * Stop all processes
3919
+ */
3920
+ stop() {
3921
+ if (!this.started) return;
3922
+ for (const process2 of this.processes.values()) {
3923
+ process2.stop();
3924
+ }
3925
+ this.started = false;
3926
+ this.emit("stopped");
3927
+ }
3928
+ /**
3929
+ * Get status of all processes
3930
+ */
3931
+ getStatus() {
3932
+ const statuses = {};
3933
+ for (const [name, process2] of this.processes) {
3934
+ statuses[name] = process2.getStatus();
3935
+ }
3936
+ return {
3937
+ started: this.started,
3938
+ processCount: this.processes.size,
3939
+ processes: statuses
3940
+ };
3941
+ }
3942
+ /**
3943
+ * Enable/disable a specific process
3944
+ */
3945
+ setProcessEnabled(name, enabled) {
3946
+ const process2 = this.processes.get(name);
3947
+ if (!process2) {
3948
+ throw new Error(`Process ${name} not found`);
3949
+ }
3950
+ process2.enabled = enabled;
3951
+ if (!enabled) {
3952
+ process2.stop();
3953
+ } else if (this.started) {
3954
+ process2.start(this.context);
3955
+ }
3956
+ }
3957
+ /**
3958
+ * Manually trigger a process run
3959
+ */
3960
+ async runProcess(name) {
3961
+ const process2 = this.processes.get(name);
3962
+ if (!process2) {
3963
+ throw new Error(`Process ${name} not found`);
3964
+ }
3965
+ return process2.process(this.context);
3966
+ }
3967
+ };
3968
+
3969
+ // packages/runtime/src/processors/log-watcher.ts
3970
+ var log2 = createLogger("log-watcher");
3971
+ var STACK_TRACE_START = /^(?:Error|TypeError|RangeError|ReferenceError|SyntaxError|URIError|EvalError|AggregateError|UnhandledPromiseRejectionWarning|Uncaught):/;
3972
+ var STACK_FRAME = /^\s+at\s+/;
3973
+ var CRASH_INDICATOR = /(?:exit\s+code\s+[1-9]\d*|SIGKILL|SIGTERM|SIGSEGV|SIGABRT|uncaughtException|unhandledRejection)/i;
3974
+ var WARNING_DEDUP_WINDOW_MS = 5 * 60 * 1e3;
3975
+ var WARNING_THRESHOLD = 3;
3976
+ var LogWatcher = class extends BackgroundProcess {
3977
+ config;
3978
+ registry;
3979
+ tailPositions = /* @__PURE__ */ new Map();
3980
+ warningAccumulators = /* @__PURE__ */ new Map();
3981
+ positionsFile;
3982
+ constructor(config, dataDir) {
3983
+ super("log-watcher", 1e4);
3984
+ this.config = config;
3985
+ this.registry = new ErrorRegistry(dataDir);
3986
+ this.positionsFile = `${dataDir}/log-watcher-positions.json`;
3987
+ this.loadPositions();
3988
+ }
3989
+ /** Expose the registry so the auto-debugger can consume it. */
3990
+ getRegistry() {
3991
+ return this.registry;
3992
+ }
3993
+ // ---- BackgroundProcess implementation ------------------------------------
3994
+ async process(context) {
3995
+ const errors = [];
3996
+ let itemsProcessed = 0;
3997
+ for (const logFile of this.config.log_files) {
3998
+ try {
3999
+ const result = this.processLogFile(logFile, context);
4000
+ itemsProcessed += result.linesRead;
4001
+ } catch (err) {
4002
+ errors.push(`${logFile}: ${err.message}`);
4003
+ }
4004
+ }
4005
+ this.pruneWarnings();
4006
+ this.savePositions();
4007
+ this.registry.cleanup(new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3));
4008
+ return { itemsProcessed, errors };
4009
+ }
4010
+ // ---- log tailing ---------------------------------------------------------
4011
+ processLogFile(logFile, context) {
4012
+ if (!existsSync7(logFile)) return { linesRead: 0 };
4013
+ let fileSize;
4014
+ try {
4015
+ fileSize = statSync2(logFile).size;
4016
+ } catch {
4017
+ return { linesRead: 0 };
4018
+ }
4019
+ const lastPos = this.tailPositions.get(logFile) ?? 0;
4020
+ if (fileSize < lastPos) {
4021
+ this.tailPositions.set(logFile, 0);
4022
+ return this.processLogFile(logFile, context);
4023
+ }
4024
+ if (fileSize === lastPos) return { linesRead: 0 };
4025
+ let chunk;
4026
+ try {
4027
+ const buf = Buffer.alloc(fileSize - lastPos);
4028
+ const fd = __require("fs").openSync(logFile, "r");
4029
+ try {
4030
+ __require("fs").readSync(fd, buf, 0, buf.length, lastPos);
4031
+ } finally {
4032
+ __require("fs").closeSync(fd);
4033
+ }
4034
+ chunk = buf.toString("utf-8");
4035
+ } catch {
4036
+ return { linesRead: 0 };
4037
+ }
4038
+ this.tailPositions.set(logFile, fileSize);
4039
+ const lines = chunk.split("\n").filter((l) => l.length > 0);
4040
+ if (lines.length === 0) return { linesRead: 0 };
4041
+ let baseLineNumber;
4042
+ try {
4043
+ const fullContent = readFileSync8(logFile, "utf-8");
4044
+ const preContent = fullContent.substring(0, lastPos);
4045
+ baseLineNumber = preContent.split("\n").length;
4046
+ } catch {
4047
+ baseLineNumber = 1;
4048
+ }
4049
+ this.detectErrors(lines, logFile, baseLineNumber, context);
4050
+ return { linesRead: lines.length };
4051
+ }
4052
+ // ---- error detection -----------------------------------------------------
4053
+ detectErrors(lines, logFile, baseLineNumber, context) {
4054
+ const source = this.inferSource(logFile);
4055
+ for (let i = 0; i < lines.length; i++) {
4056
+ const line = lines[i];
4057
+ const lineNumber = baseLineNumber + i;
4058
+ const jsonError = this.tryParseJsonError(line);
4059
+ if (jsonError) {
4060
+ this.emitError({
4061
+ message: jsonError.msg,
4062
+ stackTrace: jsonError.stack,
4063
+ level: jsonError.level === "crash" ? "crash" : "error",
4064
+ source,
4065
+ logFile,
4066
+ lineNumber,
4067
+ context: this.getSurroundingLines(lines, i)
4068
+ });
4069
+ continue;
4070
+ }
4071
+ if (STACK_TRACE_START.test(line)) {
4072
+ const { message, stack } = this.collectStackTrace(lines, i);
4073
+ const level = /uncaught|unhandled/i.test(line) ? "crash" : "error";
4074
+ this.emitError({
4075
+ message,
4076
+ stackTrace: stack,
4077
+ level,
4078
+ source,
4079
+ logFile,
4080
+ lineNumber,
4081
+ context: this.getSurroundingLines(lines, i)
4082
+ });
4083
+ continue;
4084
+ }
4085
+ if (CRASH_INDICATOR.test(line)) {
4086
+ this.emitError({
4087
+ message: line.trim(),
4088
+ level: "crash",
4089
+ source,
4090
+ logFile,
4091
+ lineNumber,
4092
+ context: this.getSurroundingLines(lines, i)
4093
+ });
4094
+ continue;
4095
+ }
4096
+ this.accumulateWarning(line, logFile, lineNumber);
4097
+ }
4098
+ }
4099
+ tryParseJsonError(line) {
4100
+ if (!line.startsWith("{")) return null;
4101
+ try {
4102
+ const entry = JSON.parse(line);
4103
+ if (entry.level === "error" || entry.level === "crash") {
4104
+ return {
4105
+ msg: entry.msg || entry.message || "unknown error",
4106
+ stack: entry.stack || entry.stackTrace,
4107
+ level: entry.level
4108
+ };
4109
+ }
4110
+ } catch {
4111
+ }
4112
+ return null;
4113
+ }
4114
+ collectStackTrace(lines, startIdx) {
4115
+ const message = lines[startIdx].trim();
4116
+ const stackLines = [message];
4117
+ for (let j = startIdx + 1; j < lines.length; j++) {
4118
+ if (STACK_FRAME.test(lines[j])) {
4119
+ stackLines.push(lines[j]);
4120
+ } else {
4121
+ break;
4122
+ }
4123
+ }
4124
+ return { message, stack: stackLines.join("\n") };
4125
+ }
4126
+ getSurroundingLines(lines, idx) {
4127
+ const start = Math.max(0, idx - 10);
4128
+ const end = Math.min(lines.length, idx + 11);
4129
+ return lines.slice(start, end);
4130
+ }
4131
+ emitError(params) {
4132
+ const id = hashError(params.message, params.stackTrace);
4133
+ const event = {
4134
+ id,
4135
+ timestamp: /* @__PURE__ */ new Date(),
4136
+ source: params.source,
4137
+ level: params.level,
4138
+ message: params.message,
4139
+ stackTrace: params.stackTrace,
4140
+ logFile: params.logFile,
4141
+ lineNumber: params.lineNumber,
4142
+ occurrences: 1,
4143
+ context: params.context
4144
+ };
4145
+ const entry = this.registry.recordError(event);
4146
+ log2.info("error detected", {
4147
+ id,
4148
+ severity: entry.severity,
4149
+ message: params.message.slice(0, 120),
4150
+ logFile: params.logFile
4151
+ });
4152
+ }
4153
+ // ---- repeated warning detection ------------------------------------------
4154
+ accumulateWarning(line, logFile, lineNumber) {
4155
+ const isWarning = /\bwarn\b/i.test(line) || line.startsWith("{") && line.includes('"warn"');
4156
+ if (!isWarning) return;
4157
+ const normalized = line.replace(/\d{4}-\d{2}-\d{2}T[\d:.Z]+/g, "T").replace(/\b\d{4,}\b/g, "N").trim();
4158
+ const key = `${logFile}::${normalized}`;
4159
+ const existing = this.warningAccumulators.get(key);
4160
+ const now = Date.now();
4161
+ if (existing) {
4162
+ existing.count++;
4163
+ existing.lastLine = lineNumber;
4164
+ if (existing.count >= WARNING_THRESHOLD && now - existing.firstSeen <= WARNING_DEDUP_WINDOW_MS) {
4165
+ const id = hashError(existing.message);
4166
+ const event = {
4167
+ id,
4168
+ timestamp: /* @__PURE__ */ new Date(),
4169
+ source: this.inferSource(logFile),
4170
+ level: "repeated-warning",
4171
+ message: `Repeated warning (${existing.count}x in 5 min): ${existing.message}`,
4172
+ logFile,
4173
+ lineNumber: existing.lastLine,
4174
+ occurrences: existing.count,
4175
+ context: []
4176
+ };
4177
+ this.registry.recordError(event);
4178
+ log2.info("repeated warning detected", {
4179
+ id,
4180
+ count: existing.count,
4181
+ message: existing.message.slice(0, 100)
4182
+ });
4183
+ this.warningAccumulators.delete(key);
4184
+ }
4185
+ } else {
4186
+ this.warningAccumulators.set(key, {
4187
+ message: normalized,
4188
+ count: 1,
4189
+ firstSeen: now,
4190
+ logFile,
4191
+ lastLine: lineNumber
4192
+ });
4193
+ }
4194
+ }
4195
+ pruneWarnings() {
4196
+ const now = Date.now();
4197
+ for (const [key, acc] of this.warningAccumulators) {
4198
+ if (now - acc.firstSeen > WARNING_DEDUP_WINDOW_MS) {
4199
+ this.warningAccumulators.delete(key);
4200
+ }
4201
+ }
4202
+ }
4203
+ // ---- helpers -------------------------------------------------------------
4204
+ inferSource(logFile) {
4205
+ if (logFile.includes("watchdog")) return "watchdog";
4206
+ if (logFile.includes("memory")) return "memory";
4207
+ return "agent";
4208
+ }
4209
+ // ---- position persistence ------------------------------------------------
4210
+ loadPositions() {
4211
+ try {
4212
+ if (!existsSync7(this.positionsFile)) return;
4213
+ const raw = readFileSync8(this.positionsFile, "utf-8");
4214
+ const data = JSON.parse(raw);
4215
+ for (const [file, pos] of Object.entries(data)) {
4216
+ this.tailPositions.set(file, pos);
4217
+ }
4218
+ log2.debug("loaded tail positions", { count: this.tailPositions.size });
4219
+ } catch {
4220
+ }
4221
+ }
4222
+ savePositions() {
4223
+ try {
4224
+ const { mkdirSync: mkdirSync15, writeFileSync: writeFileSync9 } = __require("fs");
4225
+ const { dirname: dirname9 } = __require("path");
4226
+ const dir = dirname9(this.positionsFile);
4227
+ if (!existsSync7(dir)) mkdirSync15(dir, { recursive: true });
4228
+ const data = Object.fromEntries(this.tailPositions);
4229
+ writeFileSync9(this.positionsFile, JSON.stringify(data));
4230
+ } catch (err) {
4231
+ log2.warn("failed to save tail positions", {
4232
+ error: err.message
4233
+ });
4234
+ }
4235
+ }
4236
+ };
4237
+
4238
+ // packages/runtime/src/processors/auto-debugger.ts
4239
+ import { execSync as execSync3 } from "child_process";
4240
+ import { readFileSync as readFileSync9, existsSync as existsSync8 } from "fs";
4241
+ import { resolve as resolve6 } from "path";
4242
+ var log3 = createLogger("auto-debugger");
4243
+ var SECRET_PATTERNS = [
4244
+ /(?:api[_-]?key|token|secret|password|credential|auth)[\s=:]+\S+/gi,
4245
+ /sk-[a-zA-Z0-9]{20,}/g,
4246
+ /ghp_[a-zA-Z0-9]{36}/g,
4247
+ /Bearer\s+\S+/g
4248
+ ];
4249
+ function sanitize(text) {
4250
+ let clean = text;
4251
+ for (const pat of SECRET_PATTERNS) {
4252
+ clean = clean.replace(pat, "[REDACTED]");
4253
+ }
4254
+ return clean;
4255
+ }
4256
+ function extractSourceFiles(stackTrace, repoRoot) {
4257
+ const files = /* @__PURE__ */ new Set();
4258
+ const pathPattern = /(?:[(]|\s)((?:\/[^\s:()]+|[a-zA-Z][a-zA-Z0-9/_.-]+)\.[a-zA-Z]+)(?::\d+){0,2}/g;
4259
+ let m;
4260
+ while ((m = pathPattern.exec(stackTrace)) !== null) {
4261
+ let filePath = m[1];
4262
+ if (filePath.includes("node_modules")) continue;
4263
+ if (!filePath.startsWith("/")) {
4264
+ filePath = resolve6(repoRoot, filePath);
4265
+ }
4266
+ if (filePath.startsWith(repoRoot) && existsSync8(filePath)) {
4267
+ files.add(filePath);
4268
+ }
4269
+ }
4270
+ return [...files].slice(0, 5);
4271
+ }
4272
+ function ghAvailable() {
4273
+ try {
4274
+ execSync3("gh auth status", { stdio: "ignore", timeout: 5e3 });
4275
+ return true;
4276
+ } catch {
4277
+ return false;
4278
+ }
4279
+ }
4280
+ function run(cmd, cwd) {
4281
+ try {
4282
+ return execSync3(cmd, { cwd, encoding: "utf-8", timeout: 6e4 }).trim();
4283
+ } catch {
4284
+ return null;
4285
+ }
4286
+ }
4287
+ var AutoDebugger = class extends BackgroundProcess {
4288
+ config;
4289
+ registry;
4290
+ llm;
4291
+ repoRoot;
4292
+ activeFixes = /* @__PURE__ */ new Set();
4293
+ // error ids currently being fixed
4294
+ cooldowns = /* @__PURE__ */ new Map();
4295
+ // error id → epoch ms of last fix attempt
4296
+ constructor(config, registry, llmConfig, repoRoot) {
4297
+ super("auto-debugger", 6e4);
4298
+ this.config = config;
4299
+ this.registry = registry;
4300
+ this.repoRoot = repoRoot;
4301
+ if (llmConfig && llmConfig.api_key) {
4302
+ try {
4303
+ this.llm = new LLMClient(llmConfig);
4304
+ } catch {
4305
+ this.llm = null;
4306
+ }
4307
+ } else {
4308
+ this.llm = null;
4309
+ }
4310
+ }
4311
+ // ---- BackgroundProcess implementation ------------------------------------
4312
+ async process(_context) {
4313
+ const errors = [];
4314
+ let itemsProcessed = 0;
4315
+ const actionable = this.registry.getActionable(this.config.severity_threshold);
4316
+ if (actionable.length === 0) return { itemsProcessed: 0, errors: [] };
4317
+ log3.info("actionable errors found", { count: actionable.length });
4318
+ for (const entry of actionable) {
4319
+ if (this.activeFixes.size >= this.config.max_concurrent_fixes) {
4320
+ log3.debug("max concurrent fixes reached, skipping remaining");
4321
+ break;
4322
+ }
4323
+ const lastAttempt = this.cooldowns.get(entry.id);
4324
+ if (lastAttempt && Date.now() - lastAttempt < this.config.cooldown_minutes * 6e4) {
4325
+ continue;
4326
+ }
4327
+ try {
4328
+ await this.investigate(entry);
4329
+ itemsProcessed++;
4330
+ } catch (err) {
4331
+ errors.push(`${entry.id}: ${err.message}`);
4332
+ log3.error("investigation failed", {
4333
+ id: entry.id,
4334
+ error: err.message
4335
+ });
4336
+ }
4337
+ }
4338
+ return { itemsProcessed, errors };
4339
+ }
4340
+ // ---- investigation pipeline ----------------------------------------------
4341
+ async investigate(entry) {
4342
+ this.registry.updateStatus(entry.id, "investigating");
4343
+ this.activeFixes.add(entry.id);
4344
+ this.cooldowns.set(entry.id, Date.now());
4345
+ try {
4346
+ const sourceFiles = entry.stackTrace ? extractSourceFiles(entry.stackTrace, this.repoRoot) : [];
4347
+ const fileContents = {};
4348
+ for (const f of sourceFiles) {
4349
+ try {
4350
+ const content = readFileSync9(f, "utf-8");
4351
+ fileContents[f] = content.split("\n").slice(0, 200).join("\n");
4352
+ } catch {
4353
+ }
4354
+ }
4355
+ if (this.llm) {
4356
+ const diagnosis = await this.diagnose(entry, fileContents);
4357
+ log3.info("diagnosis complete", {
4358
+ id: entry.id,
4359
+ diagnosis: diagnosis.slice(0, 200)
4360
+ });
4361
+ if (this.config.auto_pr && ghAvailable()) {
4362
+ await this.attemptFix(entry, diagnosis, fileContents);
4363
+ }
4364
+ } else {
4365
+ log3.info("error detected (no LLM configured for diagnosis)", {
4366
+ id: entry.id,
4367
+ message: entry.message.slice(0, 120),
4368
+ severity: entry.severity,
4369
+ sourceFiles
4370
+ });
4371
+ }
4372
+ } finally {
4373
+ this.activeFixes.delete(entry.id);
4374
+ }
4375
+ }
4376
+ // ---- LLM diagnosis -------------------------------------------------------
4377
+ async diagnose(entry, fileContents) {
4378
+ const fileSection = Object.entries(fileContents).map(([path, content]) => `--- ${path} ---
4379
+ ${content}`).join("\n\n");
4380
+ const messages = [
4381
+ {
4382
+ role: "system",
4383
+ content: [
4384
+ "You are a senior software engineer diagnosing a bug in the Hivemind agent framework (TypeScript/Node.js).",
4385
+ "Analyze the error, identify the root cause, and suggest a concise fix.",
4386
+ "Be specific: reference exact lines, variables, and logic flows.",
4387
+ "If you cannot determine the cause from the given context, say so."
4388
+ ].join("\n")
4389
+ },
4390
+ {
4391
+ role: "user",
4392
+ content: [
4393
+ `## Error`,
4394
+ `Message: ${entry.message}`,
4395
+ `Level: ${entry.level}`,
4396
+ `Source: ${entry.source}`,
4397
+ `Occurrences: ${entry.totalOccurrences}`,
4398
+ `First seen: ${entry.firstSeen}`,
4399
+ `Last seen: ${entry.lastSeen}`,
4400
+ entry.stackTrace ? `
4401
+ Stack trace:
4402
+ ${entry.stackTrace}` : "",
4403
+ fileSection ? `
4404
+ ## Source files
4405
+ ${fileSection}` : "",
4406
+ "\n## Task",
4407
+ "1. What is the root cause?",
4408
+ "2. What is the minimal fix?",
4409
+ "3. Are there any risks with the fix?"
4410
+ ].join("\n")
4411
+ }
4412
+ ];
4413
+ try {
4414
+ const response = await this.llm.chat(messages);
4415
+ return response.content;
4416
+ } catch (err) {
4417
+ log3.error("LLM diagnosis call failed", {
4418
+ error: err.message
4419
+ });
4420
+ return `Diagnosis failed: ${err.message}`;
4421
+ }
4422
+ }
4423
+ // ---- auto PR -------------------------------------------------------------
4424
+ async attemptFix(entry, diagnosis, fileContents) {
4425
+ const shortId = entry.id.slice(0, 8);
4426
+ const branchName = `${this.config.branch_prefix}/${shortId}`;
4427
+ if (run(`git rev-parse --verify ${branchName}`, this.repoRoot) !== null) {
4428
+ log3.info("fix branch already exists, skipping", { branch: branchName });
4429
+ return;
4430
+ }
4431
+ const fix = await this.generateFix(entry, diagnosis, fileContents);
4432
+ if (!fix) {
4433
+ log3.info("LLM could not generate a fix", { id: entry.id });
4434
+ return;
4435
+ }
4436
+ const baseBranch = run("git rev-parse --abbrev-ref HEAD", this.repoRoot) || "main";
4437
+ if (run(`git checkout -b ${branchName}`, this.repoRoot) === null) {
4438
+ log3.error("failed to create fix branch", { branch: branchName });
4439
+ return;
4440
+ }
4441
+ try {
4442
+ let anyApplied = false;
4443
+ for (const [filePath, newContent] of Object.entries(fix.files)) {
4444
+ try {
4445
+ __require("fs").writeFileSync(filePath, newContent);
4446
+ run(`git add "${filePath}"`, this.repoRoot);
4447
+ anyApplied = true;
4448
+ } catch (err) {
4449
+ log3.warn("failed to write fix file", {
4450
+ file: filePath,
4451
+ error: err.message
4452
+ });
4453
+ }
4454
+ }
4455
+ if (!anyApplied) {
4456
+ log3.info("no files were patched, aborting PR", { id: entry.id });
4457
+ return;
4458
+ }
4459
+ const buildResult = run("npx tsup", this.repoRoot);
4460
+ if (buildResult === null) {
4461
+ log3.warn("build failed after applying fix, aborting", { id: entry.id });
4462
+ return;
4463
+ }
4464
+ const commitMsg = [
4465
+ `fix(auto-debug): ${fix.summary}`,
4466
+ "",
4467
+ `Error: ${entry.message}`,
4468
+ `Occurrences: ${entry.totalOccurrences} over ${this.formatTimespan(entry.firstSeen, entry.lastSeen)}`,
4469
+ `Source: ${entry.source}`,
4470
+ "",
4471
+ `Root cause: ${fix.rootCause}`,
4472
+ "",
4473
+ "Auto-generated by hivemind auto-debug processor"
4474
+ ].join("\n");
4475
+ if (run(`git commit -m "${commitMsg.replace(/"/g, '\\"')}"`, this.repoRoot) === null) {
4476
+ log3.error("git commit failed", { id: entry.id });
4477
+ return;
4478
+ }
4479
+ if (run(`git push -u origin ${branchName}`, this.repoRoot) === null) {
4480
+ log3.error("git push failed", { id: entry.id });
4481
+ return;
4482
+ }
4483
+ const prBody = this.buildPrBody(entry, diagnosis, fix);
4484
+ const prUrl = run(
4485
+ `gh pr create --base ${baseBranch} --head ${branchName} --title "fix(auto-debug): ${fix.summary}" --body "${prBody.replace(/"/g, '\\"')}"`,
4486
+ this.repoRoot
4487
+ );
4488
+ if (prUrl) {
4489
+ this.registry.updateStatus(entry.id, "fix-submitted", prUrl);
4490
+ log3.info("PR created", { id: entry.id, prUrl });
4491
+ } else {
4492
+ log3.error("gh pr create failed", { id: entry.id });
4493
+ }
4494
+ } finally {
4495
+ run(`git checkout ${baseBranch}`, this.repoRoot);
4496
+ }
4497
+ }
4498
+ async generateFix(entry, diagnosis, fileContents) {
4499
+ if (!this.llm) return null;
4500
+ const fileSection = Object.entries(fileContents).map(([path, content]) => `--- ${path} ---
4501
+ ${content}`).join("\n\n");
4502
+ const messages = [
4503
+ {
4504
+ role: "system",
4505
+ content: [
4506
+ "You are a senior TypeScript engineer. Generate a minimal, safe code fix.",
4507
+ "Output ONLY valid JSON with this shape:",
4508
+ '{ "summary": "short commit subject", "rootCause": "one sentence", "files": { "/absolute/path.ts": "full file content" } }',
4509
+ "Do NOT include explanations outside the JSON. Do NOT modify files that don't need changing.",
4510
+ "If you cannot produce a reliable fix, return null."
4511
+ ].join("\n")
4512
+ },
4513
+ {
4514
+ role: "user",
4515
+ content: [
4516
+ `## Error: ${entry.message}`,
4517
+ entry.stackTrace ? `
4518
+ Stack trace:
4519
+ ${entry.stackTrace}` : "",
4520
+ `
4521
+ ## Diagnosis
4522
+ ${diagnosis}`,
4523
+ `
4524
+ ## Current source files
4525
+ ${fileSection}`,
4526
+ "\nGenerate the fix JSON."
4527
+ ].join("\n")
4528
+ }
4529
+ ];
4530
+ try {
4531
+ const response = await this.llm.chat(messages);
4532
+ const content = response.content.trim();
4533
+ if (content === "null" || content === "null.") return null;
4534
+ const jsonMatch = content.match(/```(?:json)?\s*([\s\S]*?)```/) || content.match(/(\{[\s\S]*\})/);
4535
+ if (!jsonMatch) return null;
4536
+ const parsed = JSON.parse(jsonMatch[1]);
4537
+ if (!parsed.summary || !parsed.files || Object.keys(parsed.files).length === 0) {
4538
+ return null;
4539
+ }
4540
+ for (const filePath of Object.keys(parsed.files)) {
4541
+ if (!filePath.startsWith(this.repoRoot)) {
4542
+ log3.warn("fix targets file outside repo, rejecting", { file: filePath });
4543
+ return null;
4544
+ }
4545
+ }
4546
+ return parsed;
4547
+ } catch (err) {
4548
+ log3.error("failed to generate fix", { error: err.message });
4549
+ return null;
4550
+ }
4551
+ }
4552
+ // ---- PR body builder -----------------------------------------------------
4553
+ buildPrBody(entry, diagnosis, fix) {
4554
+ const logSample = entry.stackTrace ? sanitize(entry.stackTrace.split("\n").slice(0, 15).join("\n")) : sanitize(entry.message);
4555
+ return [
4556
+ "## Auto-Debug Fix",
4557
+ "",
4558
+ `**Error:** \`${sanitize(entry.message).slice(0, 200)}\``,
4559
+ `**Source:** ${entry.source}`,
4560
+ `**Frequency:** ${entry.totalOccurrences} occurrences over ${this.formatTimespan(entry.firstSeen, entry.lastSeen)}`,
4561
+ `**Severity:** ${entry.severity}/10`,
4562
+ "",
4563
+ "### Diagnosis",
4564
+ sanitize(diagnosis),
4565
+ "",
4566
+ "### Fix",
4567
+ sanitize(fix.rootCause),
4568
+ "",
4569
+ "### Log Sample",
4570
+ "```",
4571
+ logSample,
4572
+ "```",
4573
+ "",
4574
+ "### Verification",
4575
+ "- [ ] Build passes (`tsup`)",
4576
+ "- [ ] Error no longer reproduces (monitored for 30 min post-deploy)",
4577
+ "",
4578
+ "---",
4579
+ "*Auto-generated by hivemind auto-debug processor. Review before merging.*"
4580
+ ].join("\n");
4581
+ }
4582
+ // ---- utils ---------------------------------------------------------------
4583
+ formatTimespan(firstSeen, lastSeen) {
4584
+ const ms = new Date(lastSeen).getTime() - new Date(firstSeen).getTime();
4585
+ if (ms < 6e4) return `${Math.round(ms / 1e3)}s`;
4586
+ if (ms < 36e5) return `${Math.round(ms / 6e4)}m`;
4587
+ if (ms < 864e5) return `${Math.round(ms / 36e5)}h`;
4588
+ return `${Math.round(ms / 864e5)}d`;
4589
+ }
4590
+ };
4591
+
3588
4592
  // packages/runtime/src/pipeline.ts
3589
4593
  import { createServer as createServer3 } from "http";
3590
4594
 
@@ -3594,23 +4598,23 @@ var HEALTH_PATH = "/health";
3594
4598
 
3595
4599
  // packages/runtime/src/request-logger.ts
3596
4600
  import { randomUUID as randomUUID2 } from "crypto";
3597
- import { mkdirSync as mkdirSync4, existsSync as existsSync6, appendFileSync as appendFileSync2, readFileSync as readFileSync7, writeFileSync } from "fs";
3598
- import { dirname as dirname4 } from "path";
4601
+ import { mkdirSync as mkdirSync5, existsSync as existsSync9, appendFileSync as appendFileSync2, readFileSync as readFileSync10, writeFileSync as writeFileSync2 } from "fs";
4602
+ import { dirname as dirname5 } from "path";
3599
4603
  var RequestLogger = class {
3600
4604
  logPath;
3601
4605
  maxAgeDays = 7;
3602
4606
  constructor(dbPath) {
3603
4607
  this.logPath = dbPath.replace(/\.db$/, ".jsonl");
3604
4608
  if (this.logPath === dbPath) this.logPath = dbPath + ".jsonl";
3605
- const dir = dirname4(this.logPath);
3606
- if (!existsSync6(dir)) mkdirSync4(dir, { recursive: true });
4609
+ const dir = dirname5(this.logPath);
4610
+ if (!existsSync9(dir)) mkdirSync5(dir, { recursive: true });
3607
4611
  this.prune();
3608
4612
  }
3609
4613
  prune() {
3610
- if (!existsSync6(this.logPath)) return;
4614
+ if (!existsSync9(this.logPath)) return;
3611
4615
  const cutoff = new Date(Date.now() - this.maxAgeDays * 24 * 60 * 60 * 1e3).toISOString();
3612
4616
  try {
3613
- const lines = readFileSync7(this.logPath, "utf-8").split("\n").filter(Boolean);
4617
+ const lines = readFileSync10(this.logPath, "utf-8").split("\n").filter(Boolean);
3614
4618
  const kept = [];
3615
4619
  let pruned = 0;
3616
4620
  for (const line of lines) {
@@ -3625,7 +4629,7 @@ var RequestLogger = class {
3625
4629
  }
3626
4630
  }
3627
4631
  if (pruned > 0) {
3628
- writeFileSync(this.logPath, kept.join("\n") + (kept.length > 0 ? "\n" : ""));
4632
+ writeFileSync2(this.logPath, kept.join("\n") + (kept.length > 0 ? "\n" : ""));
3629
4633
  console.log(`[dashboard] Pruned ${pruned} old request logs`);
3630
4634
  }
3631
4635
  } catch {
@@ -3686,9 +4690,9 @@ var RequestLogger = class {
3686
4690
  close() {
3687
4691
  }
3688
4692
  readAll() {
3689
- if (!existsSync6(this.logPath)) return [];
4693
+ if (!existsSync9(this.logPath)) return [];
3690
4694
  try {
3691
- const lines = readFileSync7(this.logPath, "utf-8").split("\n").filter(Boolean);
4695
+ const lines = readFileSync10(this.logPath, "utf-8").split("\n").filter(Boolean);
3692
4696
  const entries = [];
3693
4697
  for (const line of lines) {
3694
4698
  try {
@@ -3705,17 +4709,17 @@ var RequestLogger = class {
3705
4709
 
3706
4710
  // packages/runtime/src/dashboard.ts
3707
4711
  import { createServer } from "http";
3708
- import { readFileSync as readFileSync8 } from "fs";
3709
- import { resolve as resolve6, dirname as dirname5 } from "path";
4712
+ import { readFileSync as readFileSync11 } from "fs";
4713
+ import { resolve as resolve7, dirname as dirname6 } from "path";
3710
4714
  import { fileURLToPath as fileURLToPath2 } from "url";
3711
- var __dirname = dirname5(fileURLToPath2(import.meta.url));
4715
+ var __dirname = dirname6(fileURLToPath2(import.meta.url));
3712
4716
  var DASHBOARD_PORT = 9485;
3713
4717
  var spaHtml = null;
3714
4718
  function getSpaHtml() {
3715
4719
  if (!spaHtml) {
3716
- for (const dir of [__dirname, resolve6(__dirname, "../src")]) {
4720
+ for (const dir of [__dirname, resolve7(__dirname, "../src")]) {
3717
4721
  try {
3718
- spaHtml = readFileSync8(resolve6(dir, "dashboard.html"), "utf-8");
4722
+ spaHtml = readFileSync11(resolve7(dir, "dashboard.html"), "utf-8");
3719
4723
  break;
3720
4724
  } catch {
3721
4725
  }
@@ -3890,8 +4894,8 @@ var ToolRegistry = class {
3890
4894
  };
3891
4895
 
3892
4896
  // packages/runtime/src/tools/shell.ts
3893
- import { execSync as execSync3 } from "child_process";
3894
- import { resolve as resolve7 } from "path";
4897
+ import { execSync as execSync4 } from "child_process";
4898
+ import { resolve as resolve8 } from "path";
3895
4899
  var MAX_OUTPUT = 5e4;
3896
4900
  function registerShellTool(registry, workspaceDir) {
3897
4901
  registry.register(
@@ -3918,9 +4922,9 @@ function registerShellTool(registry, workspaceDir) {
3918
4922
  async (params) => {
3919
4923
  const command = params.command;
3920
4924
  const timeout = (params.timeout || 30) * 1e3;
3921
- const cwd = params.workdir ? resolve7(workspaceDir, params.workdir) : workspaceDir;
4925
+ const cwd = params.workdir ? resolve8(workspaceDir, params.workdir) : workspaceDir;
3922
4926
  try {
3923
- const output = execSync3(command, {
4927
+ const output = execSync4(command, {
3924
4928
  cwd,
3925
4929
  timeout,
3926
4930
  encoding: "utf-8",
@@ -3945,8 +4949,8 @@ ${output || err.message}`;
3945
4949
  }
3946
4950
 
3947
4951
  // packages/runtime/src/tools/files.ts
3948
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync2, existsSync as existsSync7, mkdirSync as mkdirSync5 } from "fs";
3949
- import { resolve as resolve8, dirname as dirname6 } from "path";
4952
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync3, existsSync as existsSync10, mkdirSync as mkdirSync6 } from "fs";
4953
+ import { resolve as resolve9, dirname as dirname7 } from "path";
3950
4954
  var MAX_READ = 1e5;
3951
4955
  function registerFileTools(registry, workspaceDir) {
3952
4956
  registry.register(
@@ -3972,11 +4976,11 @@ function registerFileTools(registry, workspaceDir) {
3972
4976
  },
3973
4977
  async (params) => {
3974
4978
  const filePath = resolvePath(workspaceDir, params.path);
3975
- if (!existsSync7(filePath)) {
4979
+ if (!existsSync10(filePath)) {
3976
4980
  return `Error: File not found: ${filePath}`;
3977
4981
  }
3978
4982
  try {
3979
- let content = readFileSync9(filePath, "utf-8");
4983
+ let content = readFileSync12(filePath, "utf-8");
3980
4984
  if (params.offset || params.limit) {
3981
4985
  const lines = content.split("\n");
3982
4986
  const start = (params.offset || 1) - 1;
@@ -4013,9 +5017,9 @@ function registerFileTools(registry, workspaceDir) {
4013
5017
  async (params) => {
4014
5018
  const filePath = resolvePath(workspaceDir, params.path);
4015
5019
  try {
4016
- const dir = dirname6(filePath);
4017
- if (!existsSync7(dir)) mkdirSync5(dir, { recursive: true });
4018
- writeFileSync2(filePath, params.content);
5020
+ const dir = dirname7(filePath);
5021
+ if (!existsSync10(dir)) mkdirSync6(dir, { recursive: true });
5022
+ writeFileSync3(filePath, params.content);
4019
5023
  return `Written ${params.content.length} bytes to ${filePath}`;
4020
5024
  } catch (err) {
4021
5025
  return `Error writing file: ${err.message}`;
@@ -4045,18 +5049,18 @@ function registerFileTools(registry, workspaceDir) {
4045
5049
  },
4046
5050
  async (params) => {
4047
5051
  const filePath = resolvePath(workspaceDir, params.path);
4048
- if (!existsSync7(filePath)) {
5052
+ if (!existsSync10(filePath)) {
4049
5053
  return `Error: File not found: ${filePath}`;
4050
5054
  }
4051
5055
  try {
4052
- const content = readFileSync9(filePath, "utf-8");
5056
+ const content = readFileSync12(filePath, "utf-8");
4053
5057
  const oldText = params.old_text;
4054
5058
  const newText = params.new_text;
4055
5059
  if (!content.includes(oldText)) {
4056
5060
  return `Error: Could not find the exact text to replace in ${filePath}`;
4057
5061
  }
4058
5062
  const updated = content.replace(oldText, newText);
4059
- writeFileSync2(filePath, updated);
5063
+ writeFileSync3(filePath, updated);
4060
5064
  return `Edited ${filePath}: replaced ${oldText.length} chars with ${newText.length} chars`;
4061
5065
  } catch (err) {
4062
5066
  return `Error editing file: ${err.message}`;
@@ -4077,9 +5081,9 @@ function registerFileTools(registry, workspaceDir) {
4077
5081
  required: []
4078
5082
  },
4079
5083
  async (params) => {
4080
- const { readdirSync: readdirSync5, statSync: statSync3 } = await import("fs");
5084
+ const { readdirSync: readdirSync5, statSync: statSync4 } = await import("fs");
4081
5085
  const dirPath = params.path ? resolvePath(workspaceDir, params.path) : workspaceDir;
4082
- if (!existsSync7(dirPath)) {
5086
+ if (!existsSync10(dirPath)) {
4083
5087
  return `Error: Directory not found: ${dirPath}`;
4084
5088
  }
4085
5089
  try {
@@ -4088,7 +5092,7 @@ function registerFileTools(registry, workspaceDir) {
4088
5092
  for (const entry of entries) {
4089
5093
  if (entry.startsWith(".")) continue;
4090
5094
  try {
4091
- const stat = statSync3(resolve8(dirPath, entry));
5095
+ const stat = statSync4(resolve9(dirPath, entry));
4092
5096
  results.push(stat.isDirectory() ? `${entry}/` : entry);
4093
5097
  } catch {
4094
5098
  results.push(entry);
@@ -4105,7 +5109,7 @@ function resolvePath(workspace, path) {
4105
5109
  if (path.startsWith("/") || path.startsWith("~")) {
4106
5110
  return path.replace(/^~/, process.env.HOME || "/root");
4107
5111
  }
4108
- return resolve8(workspace, path);
5112
+ return resolve9(workspace, path);
4109
5113
  }
4110
5114
 
4111
5115
  // packages/runtime/src/tools/web.ts
@@ -4358,13 +5362,13 @@ Contexts:
4358
5362
  }
4359
5363
 
4360
5364
  // packages/runtime/src/tools/events.ts
4361
- import { existsSync as existsSync8, mkdirSync as mkdirSync6, readdirSync as readdirSync4, readFileSync as readFileSync10, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "fs";
5365
+ import { existsSync as existsSync11, mkdirSync as mkdirSync7, readdirSync as readdirSync4, readFileSync as readFileSync13, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
4362
5366
  import { join as join4 } from "path";
4363
5367
  import { randomUUID as randomUUID3 } from "crypto";
4364
5368
  function registerEventTools(registry, dataDir) {
4365
5369
  const eventsDir = join4(dataDir, "events");
4366
- if (!existsSync8(eventsDir)) {
4367
- mkdirSync6(eventsDir, { recursive: true });
5370
+ if (!existsSync11(eventsDir)) {
5371
+ mkdirSync7(eventsDir, { recursive: true });
4368
5372
  }
4369
5373
  registry.register(
4370
5374
  "create_event",
@@ -4433,7 +5437,7 @@ function registerEventTools(registry, dataDir) {
4433
5437
  const filename = `${randomUUID3()}.json`;
4434
5438
  const filePath = join4(eventsDir, filename);
4435
5439
  try {
4436
- writeFileSync3(filePath, JSON.stringify(event, null, 2));
5440
+ writeFileSync4(filePath, JSON.stringify(event, null, 2));
4437
5441
  return `Event created: ${filename} (type: ${type})`;
4438
5442
  } catch (err) {
4439
5443
  return `Error creating event: ${err.message}`;
@@ -4450,7 +5454,7 @@ function registerEventTools(registry, dataDir) {
4450
5454
  },
4451
5455
  async () => {
4452
5456
  try {
4453
- if (!existsSync8(eventsDir)) {
5457
+ if (!existsSync11(eventsDir)) {
4454
5458
  return "No events directory found.";
4455
5459
  }
4456
5460
  const files = readdirSync4(eventsDir).filter((f) => f.endsWith(".json"));
@@ -4460,7 +5464,7 @@ function registerEventTools(registry, dataDir) {
4460
5464
  const lines = [];
4461
5465
  for (const file of files) {
4462
5466
  try {
4463
- const content = readFileSync10(join4(eventsDir, file), "utf-8");
5467
+ const content = readFileSync13(join4(eventsDir, file), "utf-8");
4464
5468
  const event = JSON.parse(content);
4465
5469
  let detail = `${file} \u2014 type: ${event.type}, channel: ${event.channelId}`;
4466
5470
  if (event.type === "one-shot") {
@@ -4500,7 +5504,7 @@ ${lines.join("\n")}`;
4500
5504
  if (filename.includes("/") || filename.includes("\\") || filename.includes("..")) {
4501
5505
  return "Error: Invalid filename.";
4502
5506
  }
4503
- if (!existsSync8(filePath)) {
5507
+ if (!existsSync11(filePath)) {
4504
5508
  return `Event not found: ${filename}`;
4505
5509
  }
4506
5510
  try {
@@ -4515,16 +5519,16 @@ ${lines.join("\n")}`;
4515
5519
 
4516
5520
  // packages/runtime/src/tools/spawn.ts
4517
5521
  import { spawn } from "child_process";
4518
- import { existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync4 } from "fs";
4519
- import { join as join5, resolve as resolve9 } from "path";
5522
+ import { existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync5 } from "fs";
5523
+ import { join as join5, resolve as resolve10 } from "path";
4520
5524
  import { randomUUID as randomUUID4 } from "crypto";
4521
5525
  var spawnedAgents = /* @__PURE__ */ new Map();
4522
5526
  function registerSpawnTools(registry, hivemindHome, dataDir, configPath) {
4523
5527
  const spawnDir = join5(dataDir, "spawn");
4524
- if (!existsSync9(spawnDir)) {
4525
- mkdirSync7(spawnDir, { recursive: true });
5528
+ if (!existsSync12(spawnDir)) {
5529
+ mkdirSync8(spawnDir, { recursive: true });
4526
5530
  }
4527
- const hivemindBin = resolve9(hivemindHome, "node_modules", ".bin", "hivemind");
5531
+ const hivemindBin = resolve10(hivemindHome, "node_modules", ".bin", "hivemind");
4528
5532
  registry.register(
4529
5533
  "spawn_agent",
4530
5534
  "Fork a new hivemind process to run an isolated task. The sub-agent gets its own context, processes the task, and exits. Use for parallel work, long-running tasks, or tasks that need isolation.",
@@ -4557,8 +5561,8 @@ function registerSpawnTools(registry, hivemindHome, dataDir, configPath) {
4557
5561
  const channelId = params.channelId;
4558
5562
  const timeoutSeconds = params.timeoutSeconds || 300;
4559
5563
  const spawnWorkDir = join5(spawnDir, spawnId);
4560
- mkdirSync7(spawnWorkDir, { recursive: true });
4561
- writeFileSync4(join5(spawnWorkDir, "task.md"), task);
5564
+ mkdirSync8(spawnWorkDir, { recursive: true });
5565
+ writeFileSync5(join5(spawnWorkDir, "task.md"), task);
4562
5566
  const childEnv = {
4563
5567
  ...process.env,
4564
5568
  SPAWN_TASK: task,
@@ -4600,7 +5604,7 @@ function registerSpawnTools(registry, hivemindHome, dataDir, configPath) {
4600
5604
  agent.exitCode = code;
4601
5605
  agent.exited = true;
4602
5606
  try {
4603
- writeFileSync4(logPath, Buffer.concat(logChunks).toString("utf-8"));
5607
+ writeFileSync5(logPath, Buffer.concat(logChunks).toString("utf-8"));
4604
5608
  } catch {
4605
5609
  }
4606
5610
  });
@@ -4613,7 +5617,7 @@ function registerSpawnTools(registry, hivemindHome, dataDir, configPath) {
4613
5617
  agent.exited = true;
4614
5618
  agent.exitCode = -1;
4615
5619
  try {
4616
- writeFileSync4(
5620
+ writeFileSync5(
4617
5621
  join5(spawnWorkDir, "result.txt"),
4618
5622
  `[TIMEOUT] Sub-agent killed after ${timeoutSeconds}s`
4619
5623
  );
@@ -4668,9 +5672,9 @@ function registerSpawnTools(registry, hivemindHome, dataDir, configPath) {
4668
5672
  task: "${taskPreview}"`;
4669
5673
  if (agent.exited) {
4670
5674
  const resultPath = join5(spawnDir, id, "result.txt");
4671
- if (existsSync9(resultPath)) {
5675
+ if (existsSync12(resultPath)) {
4672
5676
  try {
4673
- const result = readFileSync11(resultPath, "utf-8");
5677
+ const result = readFileSync14(resultPath, "utf-8");
4674
5678
  const preview = result.length > 200 ? result.slice(0, 200) + "..." : result;
4675
5679
  detail += `
4676
5680
  result: "${preview}"`;
@@ -4710,7 +5714,7 @@ ${lines.join("\n\n")}`;
4710
5714
  agent.process.kill("SIGTERM");
4711
5715
  agent.exited = true;
4712
5716
  agent.exitCode = -2;
4713
- writeFileSync4(
5717
+ writeFileSync5(
4714
5718
  join5(spawnDir, id, "result.txt"),
4715
5719
  `[KILLED] Sub-agent killed by parent agent`
4716
5720
  );
@@ -4793,11 +5797,11 @@ function registerVisionTools(registry) {
4793
5797
  }
4794
5798
 
4795
5799
  // packages/runtime/src/tools/git.ts
4796
- import { execSync as execSync4 } from "child_process";
4797
- import { resolve as resolve10, normalize } from "path";
5800
+ import { execSync as execSync5 } from "child_process";
5801
+ import { resolve as resolve11, normalize } from "path";
4798
5802
  function resolveRepoPath(workspaceDir, repoPath) {
4799
5803
  if (!repoPath) return workspaceDir;
4800
- const resolved = resolve10(workspaceDir, repoPath);
5804
+ const resolved = resolve11(workspaceDir, repoPath);
4801
5805
  const normalizedResolved = normalize(resolved);
4802
5806
  const normalizedWorkspace = normalize(workspaceDir);
4803
5807
  if (!normalizedResolved.startsWith(normalizedWorkspace)) {
@@ -4806,7 +5810,7 @@ function resolveRepoPath(workspaceDir, repoPath) {
4806
5810
  return resolved;
4807
5811
  }
4808
5812
  function runGit(args, cwd) {
4809
- const output = execSync4(`git ${args}`, {
5813
+ const output = execSync5(`git ${args}`, {
4810
5814
  cwd,
4811
5815
  timeout: 3e4,
4812
5816
  encoding: "utf-8",
@@ -5030,8 +6034,8 @@ ${combined}`;
5030
6034
  }
5031
6035
 
5032
6036
  // packages/runtime/src/tools/browser.ts
5033
- import { resolve as resolve11 } from "path";
5034
- import { mkdirSync as mkdirSync8, existsSync as existsSync10 } from "fs";
6037
+ import { resolve as resolve12 } from "path";
6038
+ import { mkdirSync as mkdirSync9, existsSync as existsSync13 } from "fs";
5035
6039
  import { randomUUID as randomUUID5 } from "crypto";
5036
6040
  var MAX_OUTPUT2 = 5e4;
5037
6041
  var browserInstance = null;
@@ -5074,7 +6078,7 @@ async function closeBrowser() {
5074
6078
  }
5075
6079
  }
5076
6080
  function registerBrowserTools(registry, workspaceDir) {
5077
- const screenshotDir = resolve11(workspaceDir, "screenshots");
6081
+ const screenshotDir = resolve12(workspaceDir, "screenshots");
5078
6082
  registry.register(
5079
6083
  "browse",
5080
6084
  [
@@ -5158,11 +6162,11 @@ function registerBrowserTools(registry, workspaceDir) {
5158
6162
  return trimmed;
5159
6163
  }
5160
6164
  case "screenshot": {
5161
- if (!existsSync10(screenshotDir)) {
5162
- mkdirSync8(screenshotDir, { recursive: true });
6165
+ if (!existsSync13(screenshotDir)) {
6166
+ mkdirSync9(screenshotDir, { recursive: true });
5163
6167
  }
5164
6168
  const filename = `${randomUUID5()}.png`;
5165
- const filepath = resolve11(screenshotDir, filename);
6169
+ const filepath = resolve12(screenshotDir, filename);
5166
6170
  await page.screenshot({ path: filepath, fullPage: true });
5167
6171
  return `Screenshot saved: ${filepath}`;
5168
6172
  }
@@ -5213,7 +6217,7 @@ function registerBrowserTools(registry, workspaceDir) {
5213
6217
  }
5214
6218
 
5215
6219
  // packages/runtime/src/tools/system.ts
5216
- import { execSync as execSync5 } from "child_process";
6220
+ import { execSync as execSync6 } from "child_process";
5217
6221
  import * as os from "os";
5218
6222
  var MAX_OUTPUT3 = 5e4;
5219
6223
  function truncate(output) {
@@ -5224,7 +6228,7 @@ function truncate(output) {
5224
6228
  return output;
5225
6229
  }
5226
6230
  function exec(cmd, timeoutS = 10) {
5227
- return execSync5(cmd, {
6231
+ return execSync6(cmd, {
5228
6232
  encoding: "utf-8",
5229
6233
  timeout: timeoutS * 1e3,
5230
6234
  maxBuffer: 10 * 1024 * 1024,
@@ -5471,8 +6475,8 @@ function fmt(bytes) {
5471
6475
 
5472
6476
  // packages/runtime/src/tools/http-server.ts
5473
6477
  import { createServer as createServer2 } from "http";
5474
- import { createReadStream, existsSync as existsSync11, statSync as statSync2 } from "fs";
5475
- import { join as join6, extname, resolve as resolve12 } from "path";
6478
+ import { createReadStream, existsSync as existsSync14, statSync as statSync3 } from "fs";
6479
+ import { join as join6, extname, resolve as resolve13 } from "path";
5476
6480
  var MAX_BODY = 5e4;
5477
6481
  var activeServers = /* @__PURE__ */ new Map();
5478
6482
  var MIME_TYPES = {
@@ -5489,16 +6493,16 @@ var MIME_TYPES = {
5489
6493
  function serveStatic(baseDir, req, res) {
5490
6494
  const urlPath = (req.url || "/").split("?")[0];
5491
6495
  let filePath = join6(baseDir, urlPath === "/" ? "index.html" : urlPath);
5492
- if (!resolve12(filePath).startsWith(resolve12(baseDir))) {
6496
+ if (!resolve13(filePath).startsWith(resolve13(baseDir))) {
5493
6497
  res.writeHead(403);
5494
6498
  res.end("Forbidden");
5495
6499
  return true;
5496
6500
  }
5497
- if (!existsSync11(filePath)) return false;
5498
- const stat = statSync2(filePath);
6501
+ if (!existsSync14(filePath)) return false;
6502
+ const stat = statSync3(filePath);
5499
6503
  if (stat.isDirectory()) {
5500
6504
  filePath = join6(filePath, "index.html");
5501
- if (!existsSync11(filePath)) return false;
6505
+ if (!existsSync14(filePath)) return false;
5502
6506
  }
5503
6507
  const ext = extname(filePath);
5504
6508
  const mime = MIME_TYPES[ext] || "application/octet-stream";
@@ -5551,8 +6555,8 @@ function registerHttpTools(registry, workspaceDir) {
5551
6555
  if (activeServers.has(port)) {
5552
6556
  return `Error: A server is already running on port ${port}. Stop it first with http_stop.`;
5553
6557
  }
5554
- const staticDir = directory ? resolve12(workspaceDir, directory) : void 0;
5555
- if (staticDir && !existsSync11(staticDir)) {
6558
+ const staticDir = directory ? resolve13(workspaceDir, directory) : void 0;
6559
+ if (staticDir && !existsSync14(staticDir)) {
5556
6560
  return `Error: Directory not found: ${directory}`;
5557
6561
  }
5558
6562
  return new Promise((resolvePromise) => {
@@ -5688,8 +6692,8 @@ function registerHttpTools(registry, workspaceDir) {
5688
6692
  }
5689
6693
 
5690
6694
  // packages/runtime/src/tools/watch.ts
5691
- import { watch as watch3, existsSync as existsSync12, mkdirSync as mkdirSync9, writeFileSync as writeFileSync5 } from "fs";
5692
- import { join as join7, resolve as resolve13 } from "path";
6695
+ import { watch as watch3, existsSync as existsSync15, mkdirSync as mkdirSync10, writeFileSync as writeFileSync6 } from "fs";
6696
+ import { join as join7, resolve as resolve14 } from "path";
5693
6697
  import { randomUUID as randomUUID6 } from "crypto";
5694
6698
  var activeWatchers = /* @__PURE__ */ new Map();
5695
6699
  function registerWatchTools(registry, workspaceDir, dataDir) {
@@ -5722,15 +6726,15 @@ function registerWatchTools(registry, workspaceDir, dataDir) {
5722
6726
  if (activeWatchers.has(id)) {
5723
6727
  return `Error: A watcher with id '${id}' already exists. Stop it first or use a different id.`;
5724
6728
  }
5725
- const absolutePath = resolve13(workspaceDir, relPath);
5726
- if (!absolutePath.startsWith(resolve13(workspaceDir))) {
6729
+ const absolutePath = resolve14(workspaceDir, relPath);
6730
+ if (!absolutePath.startsWith(resolve14(workspaceDir))) {
5727
6731
  return "Error: Path must be within the workspace directory.";
5728
6732
  }
5729
- if (!existsSync12(absolutePath)) {
6733
+ if (!existsSync15(absolutePath)) {
5730
6734
  return `Error: Path not found: ${relPath}`;
5731
6735
  }
5732
- if (!existsSync12(eventsDir)) {
5733
- mkdirSync9(eventsDir, { recursive: true });
6736
+ if (!existsSync15(eventsDir)) {
6737
+ mkdirSync10(eventsDir, { recursive: true });
5734
6738
  }
5735
6739
  try {
5736
6740
  let debounceTimer = null;
@@ -5746,7 +6750,7 @@ function registerWatchTools(registry, workspaceDir, dataDir) {
5746
6750
  };
5747
6751
  const eventFile = join7(eventsDir, `watch-${id}-${Date.now()}.json`);
5748
6752
  try {
5749
- writeFileSync5(eventFile, JSON.stringify(event, null, 2));
6753
+ writeFileSync6(eventFile, JSON.stringify(event, null, 2));
5750
6754
  } catch {
5751
6755
  }
5752
6756
  });
@@ -5815,13 +6819,13 @@ ${lines.join("\n")}`;
5815
6819
  }
5816
6820
 
5817
6821
  // packages/runtime/src/tools/macos.ts
5818
- import { execSync as execSync6 } from "child_process";
5819
- import { resolve as resolve14, normalize as normalize2 } from "path";
5820
- import { mkdirSync as mkdirSync10, existsSync as existsSync13 } from "fs";
6822
+ import { execSync as execSync7 } from "child_process";
6823
+ import { resolve as resolve15, normalize as normalize2 } from "path";
6824
+ import { mkdirSync as mkdirSync11, existsSync as existsSync16 } from "fs";
5821
6825
  import { randomUUID as randomUUID7 } from "crypto";
5822
6826
  var MAX_OUTPUT4 = 5e4;
5823
6827
  function shellExec(command, timeoutMs) {
5824
- return execSync6(command, {
6828
+ return execSync7(command, {
5825
6829
  timeout: timeoutMs,
5826
6830
  encoding: "utf-8",
5827
6831
  maxBuffer: 10 * 1024 * 1024,
@@ -5932,7 +6936,7 @@ ${output || err.message}`;
5932
6936
  async (params) => {
5933
6937
  const content = params.content;
5934
6938
  try {
5935
- execSync6("pbcopy", {
6939
+ execSync7("pbcopy", {
5936
6940
  input: content,
5937
6941
  timeout: 5e3,
5938
6942
  encoding: "utf-8",
@@ -5995,26 +6999,26 @@ ${output || err.message}`;
5995
6999
  try {
5996
7000
  let savePath;
5997
7001
  if (params.path) {
5998
- savePath = resolve14(workspaceDir, params.path);
7002
+ savePath = resolve15(workspaceDir, params.path);
5999
7003
  const normalizedSave = normalize2(savePath);
6000
7004
  const normalizedWorkspace = normalize2(workspaceDir);
6001
7005
  if (!normalizedSave.startsWith(normalizedWorkspace)) {
6002
7006
  return "Error: path must be within the workspace.";
6003
7007
  }
6004
7008
  } else {
6005
- const screenshotDir = resolve14(workspaceDir, "screenshots");
6006
- if (!existsSync13(screenshotDir)) {
6007
- mkdirSync10(screenshotDir, { recursive: true });
7009
+ const screenshotDir = resolve15(workspaceDir, "screenshots");
7010
+ if (!existsSync16(screenshotDir)) {
7011
+ mkdirSync11(screenshotDir, { recursive: true });
6008
7012
  }
6009
- savePath = resolve14(screenshotDir, `${randomUUID7()}.png`);
7013
+ savePath = resolve15(screenshotDir, `${randomUUID7()}.png`);
6010
7014
  }
6011
- const parentDir = resolve14(savePath, "..");
6012
- if (!existsSync13(parentDir)) {
6013
- mkdirSync10(parentDir, { recursive: true });
7015
+ const parentDir = resolve15(savePath, "..");
7016
+ if (!existsSync16(parentDir)) {
7017
+ mkdirSync11(parentDir, { recursive: true });
6014
7018
  }
6015
7019
  const args = params.region ? `screencapture -i ${escapeShellArg(savePath)}` : `screencapture -x ${escapeShellArg(savePath)}`;
6016
7020
  shellExec(args, 15e3);
6017
- if (!existsSync13(savePath)) {
7021
+ if (!existsSync16(savePath)) {
6018
7022
  return "Screenshot cancelled or failed \u2014 no file was created.";
6019
7023
  }
6020
7024
  return `Screenshot saved: ${savePath}`;
@@ -6032,12 +7036,12 @@ function escapeAppleString(s) {
6032
7036
  }
6033
7037
 
6034
7038
  // packages/runtime/src/tools/data.ts
6035
- import { execSync as execSync7 } from "child_process";
6036
- import { resolve as resolve15, normalize as normalize3, extname as extname2 } from "path";
6037
- import { mkdirSync as mkdirSync11, existsSync as existsSync14 } from "fs";
7039
+ import { execSync as execSync8 } from "child_process";
7040
+ import { resolve as resolve16, normalize as normalize3, extname as extname2 } from "path";
7041
+ import { mkdirSync as mkdirSync12, existsSync as existsSync17 } from "fs";
6038
7042
  var MAX_OUTPUT5 = 5e4;
6039
7043
  function shellExec2(command, cwd, timeoutMs) {
6040
- return execSync7(command, {
7044
+ return execSync8(command, {
6041
7045
  cwd,
6042
7046
  timeout: timeoutMs,
6043
7047
  encoding: "utf-8",
@@ -6047,7 +7051,7 @@ function shellExec2(command, cwd, timeoutMs) {
6047
7051
  });
6048
7052
  }
6049
7053
  function safePath(workspaceDir, filePath) {
6050
- const resolved = resolve15(workspaceDir, filePath);
7054
+ const resolved = resolve16(workspaceDir, filePath);
6051
7055
  const normalizedResolved = normalize3(resolved);
6052
7056
  const normalizedWorkspace = normalize3(workspaceDir);
6053
7057
  if (!normalizedResolved.startsWith(normalizedWorkspace)) {
@@ -6087,7 +7091,7 @@ function registerDataTools(registry, workspaceDir) {
6087
7091
  const queryParams = params.params;
6088
7092
  try {
6089
7093
  const dbPath = safePath(workspaceDir, dbRelative);
6090
- if (!existsSync14(dbPath) && !query.trim().toUpperCase().startsWith("CREATE")) {
7094
+ if (!existsSync17(dbPath) && !query.trim().toUpperCase().startsWith("CREATE")) {
6091
7095
  return `Error: database file not found: ${dbRelative}`;
6092
7096
  }
6093
7097
  let fullQuery = query;
@@ -6148,16 +7152,16 @@ function registerDataTools(registry, workspaceDir) {
6148
7152
  try {
6149
7153
  const sourcePath = safePath(workspaceDir, source);
6150
7154
  const outputPath = safePath(workspaceDir, output);
6151
- if (!existsSync14(sourcePath)) {
7155
+ if (!existsSync17(sourcePath)) {
6152
7156
  return `Error: source not found: ${source}`;
6153
7157
  }
6154
- const outputParent = resolve15(outputPath, "..");
6155
- if (!existsSync14(outputParent)) {
6156
- mkdirSync11(outputParent, { recursive: true });
7158
+ const outputParent = resolve16(outputPath, "..");
7159
+ if (!existsSync17(outputParent)) {
7160
+ mkdirSync12(outputParent, { recursive: true });
6157
7161
  }
6158
7162
  let command;
6159
7163
  if (format === "tar.gz") {
6160
- command = `tar czf ${escapeShellArg2(outputPath)} -C ${escapeShellArg2(resolve15(sourcePath, ".."))} ${escapeShellArg2(sourcePath.split("/").pop())}`;
7164
+ command = `tar czf ${escapeShellArg2(outputPath)} -C ${escapeShellArg2(resolve16(sourcePath, ".."))} ${escapeShellArg2(sourcePath.split("/").pop())}`;
6161
7165
  } else {
6162
7166
  command = `zip -r ${escapeShellArg2(outputPath)} ${escapeShellArg2(source)}`;
6163
7167
  }
@@ -6192,11 +7196,11 @@ function registerDataTools(registry, workspaceDir) {
6192
7196
  try {
6193
7197
  const archivePath = safePath(workspaceDir, archive);
6194
7198
  const destPath = destination ? safePath(workspaceDir, destination) : workspaceDir;
6195
- if (!existsSync14(archivePath)) {
7199
+ if (!existsSync17(archivePath)) {
6196
7200
  return `Error: archive not found: ${archive}`;
6197
7201
  }
6198
- if (!existsSync14(destPath)) {
6199
- mkdirSync11(destPath, { recursive: true });
7202
+ if (!existsSync17(destPath)) {
7203
+ mkdirSync12(destPath, { recursive: true });
6200
7204
  }
6201
7205
  const ext = extname2(archivePath).toLowerCase();
6202
7206
  const isGz = archivePath.endsWith(".tar.gz") || archivePath.endsWith(".tgz");
@@ -6237,7 +7241,7 @@ ${trimmed}`.trim();
6237
7241
  const filePath = params.path;
6238
7242
  try {
6239
7243
  const pdfPath = safePath(workspaceDir, filePath);
6240
- if (!existsSync14(pdfPath)) {
7244
+ if (!existsSync17(pdfPath)) {
6241
7245
  return `Error: PDF not found: ${filePath}`;
6242
7246
  }
6243
7247
  try {
@@ -6278,8 +7282,8 @@ function truncate2(text) {
6278
7282
  }
6279
7283
 
6280
7284
  // packages/runtime/src/tools/coding-agent.ts
6281
- import { execSync as execSync8 } from "child_process";
6282
- import { resolve as resolve16 } from "path";
7285
+ import { execSync as execSync9 } from "child_process";
7286
+ import { resolve as resolve17 } from "path";
6283
7287
  var MAX_OUTPUT6 = 5e4;
6284
7288
  function registerCodingAgentTools(registry, workspaceDir) {
6285
7289
  registry.register(
@@ -6306,18 +7310,18 @@ function registerCodingAgentTools(registry, workspaceDir) {
6306
7310
  async (params) => {
6307
7311
  const task = params.task;
6308
7312
  const timeoutSeconds = params.timeout || 300;
6309
- const cwd = params.workdir ? resolve16(workspaceDir, params.workdir) : workspaceDir;
7313
+ const cwd = params.workdir ? resolve17(workspaceDir, params.workdir) : workspaceDir;
6310
7314
  const homedir = process.env.HOME || "/root";
6311
7315
  const extendedPath = `${homedir}/.local/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:${process.env.PATH}`;
6312
7316
  try {
6313
- execSync8("which claude", { stdio: "ignore", env: { ...process.env, PATH: extendedPath } });
7317
+ execSync9("which claude", { stdio: "ignore", env: { ...process.env, PATH: extendedPath } });
6314
7318
  } catch {
6315
7319
  return "Error: 'claude' CLI not found. Install Claude Code first: https://docs.anthropic.com/en/docs/claude-code";
6316
7320
  }
6317
7321
  const escapedTask = task.replace(/'/g, "'\\''");
6318
7322
  const command = `claude --dangerously-skip-permissions -p '${escapedTask}'`;
6319
7323
  try {
6320
- const output = execSync8(command, {
7324
+ const output = execSync9(command, {
6321
7325
  cwd,
6322
7326
  timeout: timeoutSeconds * 1e3,
6323
7327
  encoding: "utf-8",
@@ -6348,22 +7352,22 @@ ${output || err.message}`;
6348
7352
  }
6349
7353
 
6350
7354
  // packages/runtime/src/tools/register.ts
6351
- import { resolve as resolve17 } from "path";
6352
- import { mkdirSync as mkdirSync12, existsSync as existsSync15 } from "fs";
7355
+ import { resolve as resolve18 } from "path";
7356
+ import { mkdirSync as mkdirSync13, existsSync as existsSync18 } from "fs";
6353
7357
  function registerAllTools(hivemindHome, config) {
6354
7358
  const registry = new ToolRegistry();
6355
7359
  if (config?.enabled === false) {
6356
7360
  return registry;
6357
7361
  }
6358
- const workspaceDir = resolve17(hivemindHome, config?.workspace || "workspace");
6359
- if (!existsSync15(workspaceDir)) {
6360
- mkdirSync12(workspaceDir, { recursive: true });
7362
+ const workspaceDir = resolve18(hivemindHome, config?.workspace || "workspace");
7363
+ if (!existsSync18(workspaceDir)) {
7364
+ mkdirSync13(workspaceDir, { recursive: true });
6361
7365
  }
6362
7366
  registerShellTool(registry, workspaceDir);
6363
7367
  registerFileTools(registry, workspaceDir);
6364
7368
  registerWebTools(registry, { braveApiKey: config?.braveApiKey });
6365
7369
  registerMemoryTools(registry, config?.memoryDaemonUrl || "http://localhost:3434");
6366
- const dataDir = resolve17(hivemindHome, "data");
7370
+ const dataDir = resolve18(hivemindHome, "data");
6367
7371
  registerEventTools(registry, dataDir);
6368
7372
  if (config?.configPath && !process.env.SPAWN_TASK) {
6369
7373
  registerSpawnTools(registry, hivemindHome, dataDir, config.configPath);
@@ -6413,10 +7417,10 @@ function registerMessagingTools(registry, sesame) {
6413
7417
  }
6414
7418
 
6415
7419
  // packages/runtime/src/tools/skills-tools.ts
6416
- import { existsSync as existsSync16, mkdirSync as mkdirSync13, writeFileSync as writeFileSync6, rmSync } from "fs";
6417
- import { resolve as resolve18 } from "path";
7420
+ import { existsSync as existsSync19, mkdirSync as mkdirSync14, writeFileSync as writeFileSync7, rmSync } from "fs";
7421
+ import { resolve as resolve19 } from "path";
6418
7422
  function registerSkillsTools(registry, skillsEngine, workspaceDir) {
6419
- const skillsDir = resolve18(workspaceDir, "skills");
7423
+ const skillsDir = resolve19(workspaceDir, "skills");
6420
7424
  registry.register(
6421
7425
  "skill_list",
6422
7426
  "List all loaded skills with their registered tools and status.",
@@ -6483,11 +7487,11 @@ ${lines.join("\n\n")}`;
6483
7487
  const name = params.name;
6484
7488
  const description = params.description;
6485
7489
  const tools = params.tools;
6486
- const skillDir = resolve18(skillsDir, name);
6487
- if (existsSync16(skillDir)) {
7490
+ const skillDir = resolve19(skillsDir, name);
7491
+ if (existsSync19(skillDir)) {
6488
7492
  return `Error: Skill directory "${name}" already exists. Use skill_reload to update it.`;
6489
7493
  }
6490
- mkdirSync13(skillDir, { recursive: true });
7494
+ mkdirSync14(skillDir, { recursive: true });
6491
7495
  const skillMd = `---
6492
7496
  name: "${name}"
6493
7497
  description: "${description}"
@@ -6497,7 +7501,7 @@ description: "${description}"
6497
7501
 
6498
7502
  ${description}
6499
7503
  `;
6500
- writeFileSync6(resolve18(skillDir, "SKILL.md"), skillMd);
7504
+ writeFileSync7(resolve19(skillDir, "SKILL.md"), skillMd);
6501
7505
  if (tools && tools.length > 0) {
6502
7506
  const toolsDef = {
6503
7507
  tools: tools.map((t) => ({
@@ -6507,7 +7511,7 @@ ${description}
6507
7511
  command: t.command
6508
7512
  }))
6509
7513
  };
6510
- writeFileSync6(resolve18(skillDir, "tools.json"), JSON.stringify(toolsDef, null, 2) + "\n");
7514
+ writeFileSync7(resolve19(skillDir, "tools.json"), JSON.stringify(toolsDef, null, 2) + "\n");
6511
7515
  }
6512
7516
  try {
6513
7517
  await skillsEngine.loadSkill(name);
@@ -6560,8 +7564,8 @@ Path: ${skillDir}`;
6560
7564
  },
6561
7565
  async (params) => {
6562
7566
  const name = params.name;
6563
- const skillDir = resolve18(skillsDir, name);
6564
- if (!existsSync16(skillDir)) {
7567
+ const skillDir = resolve19(skillsDir, name);
7568
+ if (!existsSync19(skillDir)) {
6565
7569
  return `Error: Skill directory "${name}" does not exist.`;
6566
7570
  }
6567
7571
  try {
@@ -6580,13 +7584,13 @@ Path: ${skillDir}`;
6580
7584
  }
6581
7585
 
6582
7586
  // packages/runtime/src/pipeline.ts
6583
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, unlinkSync as unlinkSync3 } from "fs";
6584
- import { resolve as resolve19, dirname as dirname7 } from "path";
7587
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync8, unlinkSync as unlinkSync3 } from "fs";
7588
+ import { resolve as resolve20, dirname as dirname8 } from "path";
6585
7589
  import { fileURLToPath as fileURLToPath3 } from "url";
6586
7590
  var PACKAGE_VERSION = "unknown";
6587
7591
  try {
6588
- const __dirname2 = dirname7(fileURLToPath3(import.meta.url));
6589
- const pkg = JSON.parse(readFileSync12(resolve19(__dirname2, "../package.json"), "utf-8"));
7592
+ const __dirname2 = dirname8(fileURLToPath3(import.meta.url));
7593
+ const pkg = JSON.parse(readFileSync15(resolve20(__dirname2, "../package.json"), "utf-8"));
6590
7594
  PACKAGE_VERSION = pkg.version ?? "unknown";
6591
7595
  } catch {
6592
7596
  }
@@ -6628,7 +7632,7 @@ function startHealthServer(port) {
6628
7632
  return server;
6629
7633
  }
6630
7634
  function writePidFile(path) {
6631
- writeFileSync7(path, String(process.pid));
7635
+ writeFileSync8(path, String(process.pid));
6632
7636
  console.log(`[hivemind] PID file written: ${path}`);
6633
7637
  }
6634
7638
  function cleanupPidFile(path) {
@@ -6669,11 +7673,11 @@ async function startPipeline(configPath) {
6669
7673
  console.log("[hivemind] Global context already exists in memory daemon");
6670
7674
  }
6671
7675
  }
6672
- const requestLogger = new RequestLogger(resolve19(dirname7(configPath), "data", "dashboard.db"));
7676
+ const requestLogger = new RequestLogger(resolve20(dirname8(configPath), "data", "dashboard.db"));
6673
7677
  startDashboardServer(requestLogger, config.memory);
6674
7678
  const agent = new Agent(config);
6675
7679
  agent.setRequestLogger(requestLogger);
6676
- const hivemindHome = process.env.HIVEMIND_HOME || resolve19(process.env.HOME || "/root", "hivemind");
7680
+ const hivemindHome = process.env.HIVEMIND_HOME || resolve20(process.env.HOME || "/root", "hivemind");
6677
7681
  const toolRegistry = registerAllTools(hivemindHome, {
6678
7682
  enabled: true,
6679
7683
  workspace: config.agent.workspace || "workspace",
@@ -6681,7 +7685,7 @@ async function startPipeline(configPath) {
6681
7685
  memoryDaemonUrl: config.memory.daemon_url,
6682
7686
  configPath
6683
7687
  });
6684
- const workspaceDir = resolve19(hivemindHome, config.agent.workspace || "workspace");
7688
+ const workspaceDir = resolve20(hivemindHome, config.agent.workspace || "workspace");
6685
7689
  const skillsEngine = new SkillsEngine(workspaceDir, toolRegistry);
6686
7690
  await skillsEngine.loadAll();
6687
7691
  registerSkillsTools(toolRegistry, skillsEngine, workspaceDir);
@@ -6689,7 +7693,50 @@ async function startPipeline(configPath) {
6689
7693
  process.on("exit", () => skillsEngine.stopWatching());
6690
7694
  agent.setToolRegistry(toolRegistry);
6691
7695
  console.log(`[hivemind] Context manager initialized (active: ${agent.getActiveContext()})`);
6692
- const dataDir = resolve19(hivemindHome, "data");
7696
+ if (config.auto_debug?.enabled) {
7697
+ const dataDir2 = resolve20(hivemindHome, "data");
7698
+ const autoDebugConfig = config.auto_debug;
7699
+ const logWatcher = new LogWatcher(
7700
+ {
7701
+ log_files: autoDebugConfig.log_files,
7702
+ severity_threshold: autoDebugConfig.severity_threshold,
7703
+ auto_pr: autoDebugConfig.auto_pr,
7704
+ repo: autoDebugConfig.repo,
7705
+ branch_prefix: autoDebugConfig.branch_prefix,
7706
+ max_concurrent_fixes: autoDebugConfig.max_concurrent_fixes,
7707
+ cooldown_minutes: autoDebugConfig.cooldown_minutes,
7708
+ notify_channel: autoDebugConfig.notify_channel
7709
+ },
7710
+ dataDir2
7711
+ );
7712
+ const autoDebugger = new AutoDebugger(
7713
+ {
7714
+ severity_threshold: autoDebugConfig.severity_threshold,
7715
+ auto_pr: autoDebugConfig.auto_pr,
7716
+ repo: autoDebugConfig.repo,
7717
+ branch_prefix: autoDebugConfig.branch_prefix,
7718
+ max_concurrent_fixes: autoDebugConfig.max_concurrent_fixes,
7719
+ cooldown_minutes: autoDebugConfig.cooldown_minutes
7720
+ },
7721
+ logWatcher.getRegistry(),
7722
+ config.llm,
7723
+ hivemindHome
7724
+ );
7725
+ const bgContext = {
7726
+ memoryClient: memory,
7727
+ workspacePath: hivemindHome,
7728
+ currentContext: "auto-debug",
7729
+ agentId: config.agent.name,
7730
+ sharedState: /* @__PURE__ */ new Map()
7731
+ };
7732
+ const processManager = new ProcessManager(bgContext);
7733
+ processManager.register(logWatcher);
7734
+ processManager.register(autoDebugger);
7735
+ processManager.start();
7736
+ process.on("exit", () => processManager.stop());
7737
+ console.log("[hivemind] Auto-debug processors started");
7738
+ }
7739
+ const dataDir = resolve20(hivemindHome, "data");
6693
7740
  if (config.sesame.api_key) {
6694
7741
  await startSesameLoop(config, agent, toolRegistry, dataDir);
6695
7742
  } else {
@@ -6863,8 +7910,8 @@ ${response.content}
6863
7910
  console.error("Error:", err.message);
6864
7911
  }
6865
7912
  });
6866
- return new Promise((resolve20) => {
6867
- rl.on("close", resolve20);
7913
+ return new Promise((resolve21) => {
7914
+ rl.on("close", resolve21);
6868
7915
  });
6869
7916
  }
6870
7917
  async function runSpawnTask(config, configPath) {
@@ -6875,7 +7922,7 @@ async function runSpawnTask(config, configPath) {
6875
7922
  const spawnDir = process.env.SPAWN_DIR;
6876
7923
  console.log(`[spawn] Sub-agent starting (id: ${spawnId}, context: ${context})`);
6877
7924
  const agent = new Agent(config, context);
6878
- const hivemindHome = process.env.HIVEMIND_HOME || resolve19(process.env.HOME || "/root", "hivemind");
7925
+ const hivemindHome = process.env.HIVEMIND_HOME || resolve20(process.env.HOME || "/root", "hivemind");
6879
7926
  const toolRegistry = registerAllTools(hivemindHome, {
6880
7927
  enabled: true,
6881
7928
  workspace: config.agent.workspace || "workspace",
@@ -6888,7 +7935,7 @@ async function runSpawnTask(config, configPath) {
6888
7935
  const result = response.content;
6889
7936
  console.log(`[spawn] Task completed (context: ${response.context})`);
6890
7937
  if (spawnDir) {
6891
- writeFileSync7(resolve19(spawnDir, "result.txt"), result);
7938
+ writeFileSync8(resolve20(spawnDir, "result.txt"), result);
6892
7939
  }
6893
7940
  if (channelId && config.sesame.api_key) {
6894
7941
  try {
@@ -6905,7 +7952,7 @@ async function runSpawnTask(config, configPath) {
6905
7952
  const errorMsg = `[SPAWN ERROR] ${err.message}`;
6906
7953
  console.error(`[spawn] ${errorMsg}`);
6907
7954
  if (spawnDir) {
6908
- writeFileSync7(resolve19(spawnDir, "result.txt"), errorMsg);
7955
+ writeFileSync8(resolve20(spawnDir, "result.txt"), errorMsg);
6909
7956
  }
6910
7957
  process.exitCode = 1;
6911
7958
  }
@@ -6936,20 +7983,20 @@ var WorkerServer = class {
6936
7983
  }
6937
7984
  /** Start listening. */
6938
7985
  async start() {
6939
- return new Promise((resolve20, reject) => {
7986
+ return new Promise((resolve21, reject) => {
6940
7987
  this.server = createServer4((req, res) => this.handleRequest(req, res));
6941
7988
  this.server.on("error", reject);
6942
- this.server.listen(this.port, () => resolve20());
7989
+ this.server.listen(this.port, () => resolve21());
6943
7990
  });
6944
7991
  }
6945
7992
  /** Stop the server. */
6946
7993
  async stop() {
6947
- return new Promise((resolve20) => {
7994
+ return new Promise((resolve21) => {
6948
7995
  if (!this.server) {
6949
- resolve20();
7996
+ resolve21();
6950
7997
  return;
6951
7998
  }
6952
- this.server.close(() => resolve20());
7999
+ this.server.close(() => resolve21());
6953
8000
  });
6954
8001
  }
6955
8002
  getPort() {
@@ -7072,10 +8119,10 @@ var WorkerServer = class {
7072
8119
  }
7073
8120
  };
7074
8121
  function readBody(req) {
7075
- return new Promise((resolve20, reject) => {
8122
+ return new Promise((resolve21, reject) => {
7076
8123
  const chunks = [];
7077
8124
  req.on("data", (chunk) => chunks.push(chunk));
7078
- req.on("end", () => resolve20(Buffer.concat(chunks).toString("utf-8")));
8125
+ req.on("end", () => resolve21(Buffer.concat(chunks).toString("utf-8")));
7079
8126
  req.on("error", reject);
7080
8127
  });
7081
8128
  }
@@ -7342,6 +8389,8 @@ async function startWorker(config) {
7342
8389
  }
7343
8390
 
7344
8391
  export {
8392
+ setLogLevel,
8393
+ createLogger,
7345
8394
  getClaudeCodeOAuthToken,
7346
8395
  LLMClient,
7347
8396
  MemoryClient,
@@ -7362,6 +8411,10 @@ export {
7362
8411
  SesameClient2,
7363
8412
  HEALTH_PATH,
7364
8413
  SkillsEngine,
8414
+ hashError,
8415
+ ErrorRegistry,
8416
+ LogWatcher,
8417
+ AutoDebugger,
7365
8418
  startPipeline,
7366
8419
  PRIMARY_ROUTES,
7367
8420
  WORKER_ROUTES,
@@ -7411,4 +8464,4 @@ smol-toml/dist/index.js:
7411
8464
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
7412
8465
  *)
7413
8466
  */
7414
- //# sourceMappingURL=chunk-OG6GPQDK.js.map
8467
+ //# sourceMappingURL=chunk-ZM7RK5YV.js.map