mirror of
https://github.com/farcasclaudiu/openclaw.git
synced 2026-06-28 21:01:43 +03:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -61,7 +61,6 @@ Docs: https://docs.openclaw.ai
|
|||||||
- Heartbeat: prevent scheduler silent-death races during runner reloads, preserve retry cooldown backoff under wake bursts, and prioritize user/action wake causes over interval/retry reasons when coalescing. (#15108) Thanks @joeykrug.
|
- Heartbeat: prevent scheduler silent-death races during runner reloads, preserve retry cooldown backoff under wake bursts, and prioritize user/action wake causes over interval/retry reasons when coalescing. (#15108) Thanks @joeykrug.
|
||||||
- Heartbeat: allow explicit wake (`wake`) and hook wake (`hook:*`) reasons to run even when `HEARTBEAT.md` is effectively empty so queued system events are processed. (#14527) Thanks @arosstale.
|
- Heartbeat: allow explicit wake (`wake`) and hook wake (`hook:*`) reasons to run even when `HEARTBEAT.md` is effectively empty so queued system events are processed. (#14527) Thanks @arosstale.
|
||||||
- Auto-reply/Heartbeat: strip sentence-ending `HEARTBEAT_OK` tokens even when followed by up to 4 punctuation characters, while preserving surrounding sentence punctuation. (#15847) Thanks @Spacefish.
|
- Auto-reply/Heartbeat: strip sentence-ending `HEARTBEAT_OK` tokens even when followed by up to 4 punctuation characters, while preserving surrounding sentence punctuation. (#15847) Thanks @Spacefish.
|
||||||
- Agents/Heartbeat: stop auto-creating `HEARTBEAT.md` during workspace bootstrap so missing files continue to run heartbeat as documented. (#11766) Thanks @shadril238.
|
|
||||||
- Sessions/Agents: pass `agentId` when resolving existing transcript paths in reply runs so non-default agents and heartbeat/chat handlers no longer fail with `Session file path must be within sessions directory`. (#15141) Thanks @Goldenmonstew.
|
- Sessions/Agents: pass `agentId` when resolving existing transcript paths in reply runs so non-default agents and heartbeat/chat handlers no longer fail with `Session file path must be within sessions directory`. (#15141) Thanks @Goldenmonstew.
|
||||||
- Sessions/Agents: pass `agentId` through status and usage transcript-resolution paths (auto-reply, gateway usage APIs, and session cost/log loaders) so non-default agents can resolve absolute session files without path-validation failures. (#15103) Thanks @jalehman.
|
- Sessions/Agents: pass `agentId` through status and usage transcript-resolution paths (auto-reply, gateway usage APIs, and session cost/log loaders) so non-default agents can resolve absolute session files without path-validation failures. (#15103) Thanks @jalehman.
|
||||||
- Sessions: archive previous transcript files on `/new` and `/reset` session resets (including gateway `sessions.reset`) so stale transcripts do not accumulate on disk. (#14869) Thanks @mcaxtr.
|
- Sessions: archive previous transcript files on `/new` and `/reset` session resets (including gateway `sessions.reset`) so stale transcripts do not accumulate on disk. (#14869) Thanks @mcaxtr.
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
import fs from "node:fs/promises";
|
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { makeTempWorkspace, writeWorkspaceFile } from "../test-helpers/workspace.js";
|
import { makeTempWorkspace, writeWorkspaceFile } from "../test-helpers/workspace.js";
|
||||||
import {
|
import {
|
||||||
DEFAULT_AGENTS_FILENAME,
|
|
||||||
DEFAULT_BOOTSTRAP_FILENAME,
|
|
||||||
DEFAULT_HEARTBEAT_FILENAME,
|
|
||||||
DEFAULT_MEMORY_ALT_FILENAME,
|
DEFAULT_MEMORY_ALT_FILENAME,
|
||||||
DEFAULT_MEMORY_FILENAME,
|
DEFAULT_MEMORY_FILENAME,
|
||||||
ensureAgentWorkspace,
|
|
||||||
loadWorkspaceBootstrapFiles,
|
loadWorkspaceBootstrapFiles,
|
||||||
resolveDefaultAgentWorkspaceDir,
|
resolveDefaultAgentWorkspaceDir,
|
||||||
} from "./workspace.js";
|
} from "./workspace.js";
|
||||||
@@ -24,21 +19,6 @@ describe("resolveDefaultAgentWorkspaceDir", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("ensureAgentWorkspace", () => {
|
|
||||||
it("does not create HEARTBEAT.md during bootstrap file initialization", async () => {
|
|
||||||
const tempDir = await makeTempWorkspace("openclaw-workspace-init-");
|
|
||||||
|
|
||||||
const result = await ensureAgentWorkspace({ dir: tempDir, ensureBootstrapFiles: true });
|
|
||||||
|
|
||||||
await expect(fs.access(path.join(tempDir, DEFAULT_AGENTS_FILENAME))).resolves.toBeUndefined();
|
|
||||||
await expect(
|
|
||||||
fs.access(path.join(tempDir, DEFAULT_BOOTSTRAP_FILENAME)),
|
|
||||||
).resolves.toBeUndefined();
|
|
||||||
await expect(fs.access(path.join(tempDir, DEFAULT_HEARTBEAT_FILENAME))).rejects.toThrow();
|
|
||||||
expect("heartbeatPath" in result).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("loadWorkspaceBootstrapFiles", () => {
|
describe("loadWorkspaceBootstrapFiles", () => {
|
||||||
it("includes MEMORY.md when present", async () => {
|
it("includes MEMORY.md when present", async () => {
|
||||||
const tempDir = await makeTempWorkspace("openclaw-workspace-");
|
const tempDir = await makeTempWorkspace("openclaw-workspace-");
|
||||||
|
|||||||
@@ -173,6 +173,7 @@ export async function ensureAgentWorkspace(params?: {
|
|||||||
toolsPath?: string;
|
toolsPath?: string;
|
||||||
identityPath?: string;
|
identityPath?: string;
|
||||||
userPath?: string;
|
userPath?: string;
|
||||||
|
heartbeatPath?: string;
|
||||||
bootstrapPath?: string;
|
bootstrapPath?: string;
|
||||||
}> {
|
}> {
|
||||||
const rawDir = params?.dir?.trim() ? params.dir.trim() : DEFAULT_AGENT_WORKSPACE_DIR;
|
const rawDir = params?.dir?.trim() ? params.dir.trim() : DEFAULT_AGENT_WORKSPACE_DIR;
|
||||||
@@ -188,13 +189,11 @@ export async function ensureAgentWorkspace(params?: {
|
|||||||
const toolsPath = path.join(dir, DEFAULT_TOOLS_FILENAME);
|
const toolsPath = path.join(dir, DEFAULT_TOOLS_FILENAME);
|
||||||
const identityPath = path.join(dir, DEFAULT_IDENTITY_FILENAME);
|
const identityPath = path.join(dir, DEFAULT_IDENTITY_FILENAME);
|
||||||
const userPath = path.join(dir, DEFAULT_USER_FILENAME);
|
const userPath = path.join(dir, DEFAULT_USER_FILENAME);
|
||||||
// HEARTBEAT.md is intentionally NOT created from template.
|
const heartbeatPath = path.join(dir, DEFAULT_HEARTBEAT_FILENAME);
|
||||||
// Per docs: "If the file is missing, the heartbeat still runs and the model decides what to do."
|
|
||||||
// Creating it from template (which is effectively empty) would cause heartbeat to be skipped.
|
|
||||||
const bootstrapPath = path.join(dir, DEFAULT_BOOTSTRAP_FILENAME);
|
const bootstrapPath = path.join(dir, DEFAULT_BOOTSTRAP_FILENAME);
|
||||||
|
|
||||||
const isBrandNewWorkspace = await (async () => {
|
const isBrandNewWorkspace = await (async () => {
|
||||||
const paths = [agentsPath, soulPath, toolsPath, identityPath, userPath];
|
const paths = [agentsPath, soulPath, toolsPath, identityPath, userPath, heartbeatPath];
|
||||||
const existing = await Promise.all(
|
const existing = await Promise.all(
|
||||||
paths.map(async (p) => {
|
paths.map(async (p) => {
|
||||||
try {
|
try {
|
||||||
@@ -213,6 +212,7 @@ export async function ensureAgentWorkspace(params?: {
|
|||||||
const toolsTemplate = await loadTemplate(DEFAULT_TOOLS_FILENAME);
|
const toolsTemplate = await loadTemplate(DEFAULT_TOOLS_FILENAME);
|
||||||
const identityTemplate = await loadTemplate(DEFAULT_IDENTITY_FILENAME);
|
const identityTemplate = await loadTemplate(DEFAULT_IDENTITY_FILENAME);
|
||||||
const userTemplate = await loadTemplate(DEFAULT_USER_FILENAME);
|
const userTemplate = await loadTemplate(DEFAULT_USER_FILENAME);
|
||||||
|
const heartbeatTemplate = await loadTemplate(DEFAULT_HEARTBEAT_FILENAME);
|
||||||
const bootstrapTemplate = await loadTemplate(DEFAULT_BOOTSTRAP_FILENAME);
|
const bootstrapTemplate = await loadTemplate(DEFAULT_BOOTSTRAP_FILENAME);
|
||||||
|
|
||||||
await writeFileIfMissing(agentsPath, agentsTemplate);
|
await writeFileIfMissing(agentsPath, agentsTemplate);
|
||||||
@@ -220,6 +220,7 @@ export async function ensureAgentWorkspace(params?: {
|
|||||||
await writeFileIfMissing(toolsPath, toolsTemplate);
|
await writeFileIfMissing(toolsPath, toolsTemplate);
|
||||||
await writeFileIfMissing(identityPath, identityTemplate);
|
await writeFileIfMissing(identityPath, identityTemplate);
|
||||||
await writeFileIfMissing(userPath, userTemplate);
|
await writeFileIfMissing(userPath, userTemplate);
|
||||||
|
await writeFileIfMissing(heartbeatPath, heartbeatTemplate);
|
||||||
if (isBrandNewWorkspace) {
|
if (isBrandNewWorkspace) {
|
||||||
await writeFileIfMissing(bootstrapPath, bootstrapTemplate);
|
await writeFileIfMissing(bootstrapPath, bootstrapTemplate);
|
||||||
}
|
}
|
||||||
@@ -232,6 +233,7 @@ export async function ensureAgentWorkspace(params?: {
|
|||||||
toolsPath,
|
toolsPath,
|
||||||
identityPath,
|
identityPath,
|
||||||
userPath,
|
userPath,
|
||||||
|
heartbeatPath,
|
||||||
bootstrapPath,
|
bootstrapPath,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user