mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-02 02:57:51 +00:00
Merged via /review-pr -> /prepare-pr -> /merge-pr.
Prepared head SHA: a57718c09e
Co-authored-by: robbyczgw-cla <239660374+robbyczgw-cla@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
@@ -36,6 +36,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
- Agents/Process/Bootstrap: preserve unbounded `process log` offset-only pagination (default tail applies only when both `offset` and `limit` are omitted) and enforce strict `bootstrapTotalMaxChars` budgeting across injected bootstrap content (including markers), skipping additional injection when remaining budget is too small. (#16539) Thanks @CharlieGreenman.
|
- Agents/Process/Bootstrap: preserve unbounded `process log` offset-only pagination (default tail applies only when both `offset` and `limit` are omitted) and enforce strict `bootstrapTotalMaxChars` budgeting across injected bootstrap content (including markers), skipping additional injection when remaining budget is too small. (#16539) Thanks @CharlieGreenman.
|
||||||
- Sessions/Agents: harden transcript path resolution for mismatched agent context by preserving explicit store roots and adding safe absolute-path fallback to the correct agent sessions directory. (#16288) Thanks @robbyczgw-cla.
|
- Sessions/Agents: harden transcript path resolution for mismatched agent context by preserving explicit store roots and adding safe absolute-path fallback to the correct agent sessions directory. (#16288) Thanks @robbyczgw-cla.
|
||||||
- Agents: keep unresolved mutating tool failures visible until the same action retry succeeds, scope mutation-error surfacing to mutating calls (including `session_status` model changes), and dedupe duplicate failure warnings in outbound replies. (#16131) Thanks @Swader.
|
- Agents: keep unresolved mutating tool failures visible until the same action retry succeeds, scope mutation-error surfacing to mutating calls (including `session_status` model changes), and dedupe duplicate failure warnings in outbound replies. (#16131) Thanks @Swader.
|
||||||
|
- Agents/Workspace: create `BOOTSTRAP.md` when core workspace files are seeded in partially initialized workspaces, while keeping BOOTSTRAP one-shot after onboarding deletion. (#16457) Thanks @robbyczgw-cla.
|
||||||
- Agents: classify external timeout aborts during compaction the same as internal timeouts, preventing unnecessary auth-profile rotation and preserving compaction-timeout snapshot fallback behavior. (#9855) Thanks @mverrilli.
|
- Agents: classify external timeout aborts during compaction the same as internal timeouts, preventing unnecessary auth-profile rotation and preserving compaction-timeout snapshot fallback behavior. (#9855) Thanks @mverrilli.
|
||||||
- Ollama/Agents: avoid forcing `<final>` tag enforcement for Ollama models, which could suppress all output as `(no output)`. (#16191) Thanks @Glucksberg.
|
- Ollama/Agents: avoid forcing `<final>` tag enforcement for Ollama models, which could suppress all output as `(no output)`. (#16191) Thanks @Glucksberg.
|
||||||
- Plugins: suppress false duplicate plugin id warnings when the same extension is discovered via multiple paths (config/workspace/global vs bundled), while still warning on genuine duplicates. (#16222) Thanks @shadril238.
|
- Plugins: suppress false duplicate plugin id warnings when the same extension is discovered via multiple paths (config/workspace/global vs bundled), while still warning on genuine duplicates. (#16222) Thanks @shadril238.
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
|
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_MEMORY_ALT_FILENAME,
|
DEFAULT_MEMORY_ALT_FILENAME,
|
||||||
DEFAULT_MEMORY_FILENAME,
|
DEFAULT_MEMORY_FILENAME,
|
||||||
|
ensureAgentWorkspace,
|
||||||
loadWorkspaceBootstrapFiles,
|
loadWorkspaceBootstrapFiles,
|
||||||
resolveDefaultAgentWorkspaceDir,
|
resolveDefaultAgentWorkspaceDir,
|
||||||
} from "./workspace.js";
|
} from "./workspace.js";
|
||||||
@@ -19,6 +23,41 @@ describe("resolveDefaultAgentWorkspaceDir", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("ensureAgentWorkspace", () => {
|
||||||
|
it("creates BOOTSTRAP.md for a brand new workspace", async () => {
|
||||||
|
const tempDir = await makeTempWorkspace("openclaw-workspace-");
|
||||||
|
|
||||||
|
await ensureAgentWorkspace({ dir: tempDir, ensureBootstrapFiles: true });
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
fs.access(path.join(tempDir, DEFAULT_BOOTSTRAP_FILENAME)),
|
||||||
|
).resolves.toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("creates BOOTSTRAP.md even when workspace already has other bootstrap files", async () => {
|
||||||
|
const tempDir = await makeTempWorkspace("openclaw-workspace-");
|
||||||
|
await writeWorkspaceFile({ dir: tempDir, name: DEFAULT_AGENTS_FILENAME, content: "existing" });
|
||||||
|
|
||||||
|
await ensureAgentWorkspace({ dir: tempDir, ensureBootstrapFiles: true });
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
fs.access(path.join(tempDir, DEFAULT_BOOTSTRAP_FILENAME)),
|
||||||
|
).resolves.toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not recreate BOOTSTRAP.md after onboarding deletion", async () => {
|
||||||
|
const tempDir = await makeTempWorkspace("openclaw-workspace-");
|
||||||
|
await ensureAgentWorkspace({ dir: tempDir, ensureBootstrapFiles: true });
|
||||||
|
await fs.unlink(path.join(tempDir, DEFAULT_BOOTSTRAP_FILENAME));
|
||||||
|
|
||||||
|
await ensureAgentWorkspace({ dir: tempDir, ensureBootstrapFiles: true });
|
||||||
|
|
||||||
|
await expect(fs.access(path.join(tempDir, DEFAULT_BOOTSTRAP_FILENAME))).rejects.toMatchObject({
|
||||||
|
code: "ENOENT",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
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-");
|
||||||
|
|||||||
@@ -106,17 +106,19 @@ const VALID_BOOTSTRAP_NAMES: ReadonlySet<string> = new Set([
|
|||||||
DEFAULT_MEMORY_ALT_FILENAME,
|
DEFAULT_MEMORY_ALT_FILENAME,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
async function writeFileIfMissing(filePath: string, content: string) {
|
async function writeFileIfMissing(filePath: string, content: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await fs.writeFile(filePath, content, {
|
await fs.writeFile(filePath, content, {
|
||||||
encoding: "utf-8",
|
encoding: "utf-8",
|
||||||
flag: "wx",
|
flag: "wx",
|
||||||
});
|
});
|
||||||
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const anyErr = err as { code?: string };
|
const anyErr = err as { code?: string };
|
||||||
if (anyErr.code !== "EEXIST") {
|
if (anyErr.code !== "EEXIST") {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,15 +215,16 @@ export async function ensureAgentWorkspace(params?: {
|
|||||||
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 heartbeatTemplate = await loadTemplate(DEFAULT_HEARTBEAT_FILENAME);
|
||||||
const bootstrapTemplate = await loadTemplate(DEFAULT_BOOTSTRAP_FILENAME);
|
const wroteAgents = await writeFileIfMissing(agentsPath, agentsTemplate);
|
||||||
|
const wroteSoul = await writeFileIfMissing(soulPath, soulTemplate);
|
||||||
await writeFileIfMissing(agentsPath, agentsTemplate);
|
const wroteTools = await writeFileIfMissing(toolsPath, toolsTemplate);
|
||||||
await writeFileIfMissing(soulPath, soulTemplate);
|
const wroteIdentity = await writeFileIfMissing(identityPath, identityTemplate);
|
||||||
await writeFileIfMissing(toolsPath, toolsTemplate);
|
const wroteUser = await writeFileIfMissing(userPath, userTemplate);
|
||||||
await writeFileIfMissing(identityPath, identityTemplate);
|
const wroteHeartbeat = await writeFileIfMissing(heartbeatPath, heartbeatTemplate);
|
||||||
await writeFileIfMissing(userPath, userTemplate);
|
const wroteAnyCoreBootstrapFile =
|
||||||
await writeFileIfMissing(heartbeatPath, heartbeatTemplate);
|
wroteAgents || wroteSoul || wroteTools || wroteIdentity || wroteUser || wroteHeartbeat;
|
||||||
if (isBrandNewWorkspace) {
|
if (isBrandNewWorkspace || wroteAnyCoreBootstrapFile) {
|
||||||
|
const bootstrapTemplate = await loadTemplate(DEFAULT_BOOTSTRAP_FILENAME);
|
||||||
await writeFileIfMissing(bootstrapPath, bootstrapTemplate);
|
await writeFileIfMissing(bootstrapPath, bootstrapTemplate);
|
||||||
}
|
}
|
||||||
await ensureGitRepo(dir, isBrandNewWorkspace);
|
await ensureGitRepo(dir, isBrandNewWorkspace);
|
||||||
|
|||||||
Reference in New Issue
Block a user