feat: move heartbeat config to agent.heartbeat

This commit is contained in:
Peter Steinberger
2025-12-26 01:13:13 +01:00
parent 1ef888ca23
commit 9f7b1f0942
10 changed files with 138 additions and 47 deletions

View File

@@ -5,6 +5,8 @@ import path from "node:path";
import JSON5 from "json5";
import { z } from "zod";
import { parseDurationMs } from "../cli/parse-duration.js";
export type SessionScope = "per-sender" | "global";
export type SessionConfig = {
@@ -305,7 +307,6 @@ export type ClawdisConfig = {
skillsInstall?: SkillsInstallConfig;
models?: ModelsConfig;
agent?: {
/** Provider id, e.g. "anthropic" or "openai" (pi-ai catalog). */
/** Model id (provider/model), e.g. "anthropic/claude-opus-4-5". */
model?: string;
/** Agent working directory (preferred). Used as the default cwd for agent runs. */
@@ -322,8 +323,13 @@ export type ClawdisConfig = {
/** Max inbound media size in MB for agent-visible attachments (text note or future image attach). */
mediaMaxMb?: number;
typingIntervalSeconds?: number;
/** Periodic background heartbeat runs (minutes). 0 disables. */
heartbeatMinutes?: number;
/** Periodic background heartbeat runs. */
heartbeat?: {
/** Heartbeat interval (duration string, default unit: minutes). */
every?: string;
/** Heartbeat model override (provider/model). */
model?: string;
};
/** Max concurrent agent runs across all conversations. Default: 1 (sequential). */
maxConcurrent?: number;
/** Bash tool defaults. */
@@ -444,6 +450,25 @@ const MessagesSchema = z
})
.optional();
const HeartbeatSchema = z
.object({
every: z.string().optional(),
model: z.string().optional(),
})
.superRefine((val, ctx) => {
if (!val.every) return;
try {
parseDurationMs(val.every, { defaultUnit: "m" });
} catch {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["every"],
message: "invalid duration (use ms, s, m)",
});
}
})
.optional();
const RoutingSchema = z
.object({
allowFrom: z.array(z.string()).optional(),
@@ -581,7 +606,7 @@ const ClawdisSchema = z.object({
timeoutSeconds: z.number().int().positive().optional(),
mediaMaxMb: z.number().positive().optional(),
typingIntervalSeconds: z.number().int().positive().optional(),
heartbeatMinutes: z.number().nonnegative().optional(),
heartbeat: HeartbeatSchema,
maxConcurrent: z.number().int().positive().optional(),
bash: z
.object({