botholomew 0.7.5 → 0.7.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/package.json +1 -1
- package/src/daemon/index.ts +14 -2
- package/src/daemon/tick.ts +20 -7
- package/src/utils/logger.ts +19 -6
package/README.md
CHANGED
|
@@ -169,6 +169,9 @@ Topics worth understanding in detail:
|
|
|
169
169
|
|
|
170
170
|
- **[Architecture](docs/architecture.md)** — daemon, chat, watchdog, and how
|
|
171
171
|
they share a database.
|
|
172
|
+
- **[The TUI](docs/tui.md)** — the `botholomew chat` Ink/React terminal UI:
|
|
173
|
+
seven tabs, slash-command autocomplete, message queue, and tool-call
|
|
174
|
+
visualization.
|
|
172
175
|
- **[The virtual filesystem](docs/virtual-filesystem.md)** — why the agent's
|
|
173
176
|
"files" are actually DuckDB rows, and how `context_read`/`context_write` work.
|
|
174
177
|
- **[Context & hybrid search](docs/context-and-search.md)** — LLM-driven
|
package/package.json
CHANGED
package/src/daemon/index.ts
CHANGED
|
@@ -75,18 +75,30 @@ export async function startDaemon(
|
|
|
75
75
|
? buildForegroundCallbacks()
|
|
76
76
|
: undefined;
|
|
77
77
|
|
|
78
|
-
logger.info(
|
|
78
|
+
logger.info(
|
|
79
|
+
`Daemon started ${new Date().toISOString()} for ${projectDir} (PID ${process.pid})`,
|
|
80
|
+
);
|
|
79
81
|
logger.info(`Tick interval: ${config.tick_interval_seconds}s`);
|
|
80
82
|
|
|
83
|
+
let tickNum = 0;
|
|
81
84
|
while (true) {
|
|
85
|
+
tickNum++;
|
|
82
86
|
let didWork = false;
|
|
83
87
|
try {
|
|
84
|
-
didWork = await tick(
|
|
88
|
+
didWork = await tick(
|
|
89
|
+
projectDir,
|
|
90
|
+
conn,
|
|
91
|
+
config,
|
|
92
|
+
mcpxClient,
|
|
93
|
+
callbacks,
|
|
94
|
+
tickNum,
|
|
95
|
+
);
|
|
85
96
|
} catch (err) {
|
|
86
97
|
logger.error(`Tick failed: ${err}`);
|
|
87
98
|
}
|
|
88
99
|
|
|
89
100
|
if (!didWork) {
|
|
101
|
+
logger.phase("sleeping", `${config.tick_interval_seconds}s`);
|
|
90
102
|
await Bun.sleep(config.tick_interval_seconds * 1000);
|
|
91
103
|
}
|
|
92
104
|
}
|
package/src/daemon/tick.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { McpxClient } from "@evantahler/mcpx";
|
|
2
2
|
import type { BotholomewConfig } from "../config/schemas.ts";
|
|
3
3
|
import type { DbConnection } from "../db/connection.ts";
|
|
4
|
+
import { listSchedules } from "../db/schedules.ts";
|
|
4
5
|
import {
|
|
5
6
|
claimNextTask,
|
|
6
7
|
resetStaleTasks,
|
|
@@ -20,8 +21,10 @@ export async function tick(
|
|
|
20
21
|
config: Required<BotholomewConfig>,
|
|
21
22
|
mcpxClient?: McpxClient | null,
|
|
22
23
|
callbacks?: DaemonStreamCallbacks,
|
|
24
|
+
tickNum = 1,
|
|
23
25
|
): Promise<boolean> {
|
|
24
|
-
|
|
26
|
+
const tickStart = Date.now();
|
|
27
|
+
logger.phase("tick-start", `#${tickNum}`);
|
|
25
28
|
|
|
26
29
|
// Reset stale tasks stuck in in_progress
|
|
27
30
|
const resetIds = await resetStaleTasks(
|
|
@@ -35,20 +38,27 @@ export async function tick(
|
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
// Process schedules (may create new tasks)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
const enabledSchedules = await listSchedules(conn, { enabled: true });
|
|
42
|
+
if (enabledSchedules.length > 0) {
|
|
43
|
+
logger.phase("evaluating-schedules", `${enabledSchedules.length} enabled`);
|
|
44
|
+
try {
|
|
45
|
+
await processSchedules(conn, config);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
logger.error(`Schedule processing failed: ${err}`);
|
|
48
|
+
}
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
// Claim a task
|
|
52
|
+
logger.phase("claiming-task");
|
|
45
53
|
const task = await claimNextTask(conn);
|
|
46
54
|
if (!task) {
|
|
47
|
-
logger.
|
|
55
|
+
logger.info("No task claimed (queue empty or all blocked)");
|
|
56
|
+
const elapsed = ((Date.now() - tickStart) / 1000).toFixed(1);
|
|
57
|
+
logger.phase("tick-end", `#${tickNum} ${elapsed}s didWork=false`);
|
|
48
58
|
return false;
|
|
49
59
|
}
|
|
50
60
|
|
|
51
|
-
logger.info(`
|
|
61
|
+
logger.info(`Claimed task: ${task.name} (${task.id})`);
|
|
52
62
|
callbacks?.onTaskStart(task);
|
|
53
63
|
|
|
54
64
|
// Create a thread for this tick
|
|
@@ -115,5 +125,8 @@ export async function tick(
|
|
|
115
125
|
await endThread(conn, threadId);
|
|
116
126
|
}
|
|
117
127
|
|
|
128
|
+
const elapsed = ((Date.now() - tickStart) / 1000).toFixed(1);
|
|
129
|
+
logger.phase("tick-end", `#${tickNum} ${elapsed}s didWork=true`);
|
|
130
|
+
|
|
118
131
|
return true;
|
|
119
132
|
}
|
package/src/utils/logger.ts
CHANGED
|
@@ -1,29 +1,42 @@
|
|
|
1
1
|
import ansis from "ansis";
|
|
2
2
|
|
|
3
|
+
function ts(): string {
|
|
4
|
+
return ansis.gray(new Date().toTimeString().slice(0, 8));
|
|
5
|
+
}
|
|
6
|
+
|
|
3
7
|
export const logger = {
|
|
4
8
|
info(msg: string) {
|
|
5
|
-
console.log(ansis.blue("ℹ"), msg);
|
|
9
|
+
console.log(ts(), ansis.blue("ℹ"), msg);
|
|
6
10
|
},
|
|
7
11
|
|
|
8
12
|
success(msg: string) {
|
|
9
|
-
console.log(ansis.green("✓"), msg);
|
|
13
|
+
console.log(ts(), ansis.green("✓"), msg);
|
|
10
14
|
},
|
|
11
15
|
|
|
12
16
|
warn(msg: string) {
|
|
13
|
-
console.log(ansis.yellow("⚠"), msg);
|
|
17
|
+
console.log(ts(), ansis.yellow("⚠"), msg);
|
|
14
18
|
},
|
|
15
19
|
|
|
16
20
|
error(msg: string) {
|
|
17
|
-
console.error(ansis.red("✗"), msg);
|
|
21
|
+
console.error(ts(), ansis.red("✗"), msg);
|
|
18
22
|
},
|
|
19
23
|
|
|
20
24
|
debug(msg: string) {
|
|
21
25
|
if (process.env.BOTHOLOMEW_DEBUG) {
|
|
22
|
-
console.log(ansis.gray("·"), ansis.gray(msg));
|
|
26
|
+
console.log(ts(), ansis.gray("·"), ansis.gray(msg));
|
|
23
27
|
}
|
|
24
28
|
},
|
|
25
29
|
|
|
26
30
|
dim(msg: string) {
|
|
27
|
-
console.log(ansis.dim(msg));
|
|
31
|
+
console.log(ts(), ansis.dim(msg));
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
phase(name: string, detail?: string) {
|
|
35
|
+
const tag = ansis.magenta.bold(`[[${name}]]`);
|
|
36
|
+
if (detail) {
|
|
37
|
+
console.log(ts(), tag, ansis.dim(detail));
|
|
38
|
+
} else {
|
|
39
|
+
console.log(ts(), tag);
|
|
40
|
+
}
|
|
28
41
|
},
|
|
29
42
|
};
|