diff --git a/backend/app/api/board_groups.py b/backend/app/api/board_groups.py index 3c0eaeca..1d9808d3 100644 --- a/backend/app/api/board_groups.py +++ b/backend/app/api/board_groups.py @@ -235,10 +235,7 @@ def _update_agent_heartbeat( if isinstance(raw, dict): heartbeat.update(raw) heartbeat["every"] = payload.every - if payload.target is not None: - heartbeat["target"] = payload.target - elif "target" not in heartbeat: - heartbeat["target"] = DEFAULT_HEARTBEAT_CONFIG.get("target", "none") + heartbeat["target"] = DEFAULT_HEARTBEAT_CONFIG.get("target", "last") agent.heartbeat_config = heartbeat agent.updated_at = utcnow() diff --git a/backend/app/schemas/board_group_heartbeat.py b/backend/app/schemas/board_group_heartbeat.py index 40e786f3..7a3ffae2 100644 --- a/backend/app/schemas/board_group_heartbeat.py +++ b/backend/app/schemas/board_group_heartbeat.py @@ -16,8 +16,6 @@ class BoardGroupHeartbeatApply(SQLModel): # Heartbeat cadence string understood by the OpenClaw gateway # (e.g. "2m", "10m", "30m"). every: str - # Optional heartbeat target (most deployments use "none"). - target: str | None = None include_board_leads: bool = False diff --git a/backend/app/services/openclaw/constants.py b/backend/app/services/openclaw/constants.py index a310d10e..5e1b48fb 100644 --- a/backend/app/services/openclaw/constants.py +++ b/backend/app/services/openclaw/constants.py @@ -13,7 +13,7 @@ _GATEWAY_AGENT_SUFFIX = ":main" DEFAULT_HEARTBEAT_CONFIG: dict[str, Any] = { "every": "10m", - "target": "none", + "target": "last", "includeReasoning": False, } diff --git a/backend/tests/test_agent_provisioning_utils.py b/backend/tests/test_agent_provisioning_utils.py index f538d1ff..fb593357 100644 --- a/backend/tests/test_agent_provisioning_utils.py +++ b/backend/tests/test_agent_provisioning_utils.py @@ -307,7 +307,7 @@ async def test_control_plane_upsert_agent_create_then_update(monkeypatch): agent_id="board-agent-a", name="Board Agent A", workspace_path="/tmp/workspace-board-agent-a", - heartbeat={"every": "10m", "target": "none", "includeReasoning": False}, + heartbeat={"every": "10m", "target": "last", "includeReasoning": False}, ), ) @@ -341,7 +341,7 @@ async def test_control_plane_upsert_agent_handles_already_exists(monkeypatch): agent_id="board-agent-a", name="Board Agent A", workspace_path="/tmp/workspace-board-agent-a", - heartbeat={"every": "10m", "target": "none", "includeReasoning": False}, + heartbeat={"every": "10m", "target": "last", "includeReasoning": False}, ), ) diff --git a/frontend/src/app/agents/[agentId]/edit/page.tsx b/frontend/src/app/agents/[agentId]/edit/page.tsx index a2e608a5..2c1adfef 100644 --- a/frontend/src/app/agents/[agentId]/edit/page.tsx +++ b/frontend/src/app/agents/[agentId]/edit/page.tsx @@ -52,11 +52,6 @@ const EMOJI_OPTIONS = [ { value: ":brain:", label: "Brain", glyph: "🧠" }, ]; -const HEARTBEAT_TARGET_OPTIONS: SearchableSelectOption[] = [ - { value: "none", label: "None (no outbound message)" }, - { value: "last", label: "Last channel" }, -]; - const getBoardOptions = (boards: BoardRead[]): SearchableSelectOption[] => boards.map((board) => ({ value: board.id, @@ -111,9 +106,6 @@ export default function EditAgentPage() { const [heartbeatEvery, setHeartbeatEvery] = useState( undefined, ); - const [heartbeatTarget, setHeartbeatTarget] = useState( - undefined, - ); const [identityProfile, setIdentityProfile] = useState< IdentityProfile | undefined >(undefined); @@ -166,13 +158,11 @@ export default function EditAgentPage() { if (heartbeat && typeof heartbeat === "object") { const record = heartbeat as Record; const every = record.every; - const target = record.target; return { every: typeof every === "string" && every.trim() ? every : "10m", - target: typeof target === "string" && target.trim() ? target : "none", }; } - return { every: "10m", target: "none" }; + return { every: "10m" }; }, [loadedAgent?.heartbeat_config]); const loadedIdentityProfile = useMemo(() => { @@ -200,7 +190,6 @@ export default function EditAgentPage() { const resolvedIsGatewayMain = isGatewayMain ?? Boolean(loadedAgent?.is_gateway_main); const resolvedHeartbeatEvery = heartbeatEvery ?? loadedHeartbeat.every; - const resolvedHeartbeatTarget = heartbeatTarget ?? loadedHeartbeat.target; const resolvedIdentityProfile = identityProfile ?? loadedIdentityProfile; const resolvedBoardId = useMemo(() => { @@ -244,7 +233,7 @@ export default function EditAgentPage() { heartbeat_config: { ...existingHeartbeat, every: resolvedHeartbeatEvery.trim() || "10m", - target: resolvedHeartbeatTarget, + target: "last", includeReasoning: typeof existingHeartbeat.includeReasoning === "boolean" ? existingHeartbeat.includeReasoning @@ -449,7 +438,7 @@ export default function EditAgentPage() {

Schedule & notifications

-
+
-
- - -
diff --git a/frontend/src/app/agents/new/page.tsx b/frontend/src/app/agents/new/page.tsx index a8253223..271a8910 100644 --- a/frontend/src/app/agents/new/page.tsx +++ b/frontend/src/app/agents/new/page.tsx @@ -49,11 +49,6 @@ const EMOJI_OPTIONS = [ { value: ":brain:", label: "Brain", glyph: "🧠" }, ]; -const HEARTBEAT_TARGET_OPTIONS: SearchableSelectOption[] = [ - { value: "none", label: "None (no outbound message)" }, - { value: "last", label: "Last channel" }, -]; - const getBoardOptions = (boards: BoardRead[]): SearchableSelectOption[] => boards.map((board) => ({ value: board.id, @@ -81,7 +76,6 @@ export default function NewAgentPage() { const [name, setName] = useState(""); const [boardId, setBoardId] = useState(""); const [heartbeatEvery, setHeartbeatEvery] = useState("10m"); - const [heartbeatTarget, setHeartbeatTarget] = useState("none"); const [identityProfile, setIdentityProfile] = useState({ ...DEFAULT_IDENTITY_PROFILE, }); @@ -136,7 +130,7 @@ export default function NewAgentPage() { board_id: resolvedBoardId, heartbeat_config: { every: heartbeatEvery.trim() || "10m", - target: heartbeatTarget, + target: "last", includeReasoning: false, }, identity_profile: normalizeIdentityProfile( @@ -277,7 +271,7 @@ export default function NewAgentPage() {

Schedule & notifications

-
+
-
- - -