fix(agents): avoid Pi resource discovery stalls

Co-authored-by: dataCenter430 <titan032000@gmail.com>
This commit is contained in:
Peter Steinberger
2026-05-11 13:57:33 +01:00
parent ac7e1c36eb
commit a3c7fea512
5 changed files with 69 additions and 8 deletions

View File

@@ -67,6 +67,7 @@ Docs: https://docs.openclaw.ai
- Gateway: share serialized streaming event envelopes across eligible WebSocket and node subscribers while preserving per-client sequence numbers. (#80299) Thanks @samzong.
- Browser: report Chrome MCP existing-session page readiness in browser status without letting status probes exceed the client timeout. Fixes #80268. (#80280) Thanks @ai-hpc.
- WhatsApp: route opening-phase Baileys 428 connectionClosed through the WhatsApp reconnect policy and keep post-open 428 closes retryable, so transient setup socket closes retry with WhatsApp diagnostics instead of escaping as a bare `channel exited` error. Fixes #75736; mitigates #77443. Thanks @dataCenter430.
- Agents: disable Pi's default filesystem resource discovery for embedded runs while keeping OpenClaw inline extension factories active, avoiding Windows event-loop stalls during first WhatsApp-triggered agent startup. Fixes #77443. Thanks @dataCenter430.
- Providers/self-hosted: read model-scoped llama.cpp runtime context from `/props.default_generation_settings.n_ctx` while keeping top-level `n_ctx` as a fallback, so session budgeting reflects the loaded context window. Fixes #73664. (#74057) Thanks @brokemac79.
- Memory: reject symlinked directory components in configured extra memory paths before reading Markdown files. (#80331) Thanks @samzong.
- Sessions/transcripts: replace whole-file `readFile` scans with shared streaming helpers (`streamSessionTranscriptLines` and `streamSessionTranscriptLinesReverse`) for idempotency lookup, latest/tail assistant text reads, delivery-mirror dedupe, and compaction fork loading, so long-running sessions no longer materialize the full transcript in memory. Forward scans use `readline` over a bounded `createReadStream`; reverse scans read bounded chunks from the file end and decode complete JSONL lines newest-first without a fixed tail cap. Synthetic 200 MiB transcript: peak RSS delta drops from +252 MiB to +27 MiB while preserving malformed-line tolerance and idempotency-key return semantics. Fixes #54296. Thanks @jack-stormentswe.

View File

@@ -3,7 +3,6 @@ import os from "node:os";
import type { AgentMessage } from "@earendil-works/pi-agent-core";
import {
createAgentSession,
DefaultResourceLoader,
estimateTokens,
SessionManager,
} from "@earendil-works/pi-coding-agent";
@@ -141,6 +140,7 @@ import { buildEmbeddedMessageActionDiscoveryInput } from "./message-action-disco
import { readPiModelContextTokens } from "./model-context-tokens.js";
import { resolveModelAsync } from "./model.js";
import { sanitizeSessionHistory, validateReplayTurns } from "./replay-history.js";
import { createEmbeddedPiResourceLoader } from "./resource-loader.js";
import { buildEmbeddedSandboxInfo } from "./sandbox-info.js";
import { prewarmSessionFile, trackSessionManagerAccess } from "./session-manager-cache.js";
import { resolveEmbeddedRunSkillEntries } from "./skills-runtime.js";
@@ -996,7 +996,7 @@ async function compactEmbeddedPiSessionDirectOnce(
modelId,
model,
});
const resourceLoader = new DefaultResourceLoader({
const resourceLoader = createEmbeddedPiResourceLoader({
cwd: resolvedWorkspace,
agentDir,
settingsManager,

View File

@@ -0,0 +1,40 @@
import { DefaultResourceLoader } from "@earendil-works/pi-coding-agent";
import { describe, expect, it, vi } from "vitest";
import {
createEmbeddedPiResourceLoader,
EMBEDDED_PI_RESOURCE_LOADER_DISCOVERY_OPTIONS,
} from "./resource-loader.js";
vi.mock("@earendil-works/pi-coding-agent", () => ({
DefaultResourceLoader: vi.fn(function DefaultResourceLoader(
this: Record<string, unknown>,
options: unknown,
) {
Object.assign(this, {
options,
reload: vi.fn(async () => undefined),
});
}),
}));
describe("createEmbeddedPiResourceLoader", () => {
it("keeps inline extensions but disables Pi filesystem discovery", () => {
const settingsManager = {};
const extensionFactories = [vi.fn()];
createEmbeddedPiResourceLoader({
cwd: "/workspace",
agentDir: "/agent",
settingsManager: settingsManager as never,
extensionFactories: extensionFactories as never,
});
expect(DefaultResourceLoader).toHaveBeenCalledWith({
cwd: "/workspace",
agentDir: "/agent",
settingsManager,
extensionFactories,
...EMBEDDED_PI_RESOURCE_LOADER_DISCOVERY_OPTIONS,
});
});
});

View File

@@ -0,0 +1,23 @@
import { DefaultResourceLoader } from "@earendil-works/pi-coding-agent";
type DefaultResourceLoaderInit = ConstructorParameters<typeof DefaultResourceLoader>[0];
export const EMBEDDED_PI_RESOURCE_LOADER_DISCOVERY_OPTIONS = {
noExtensions: true,
noSkills: true,
noPromptTemplates: true,
noThemes: true,
noContextFiles: true,
} satisfies Partial<DefaultResourceLoaderInit>;
export function createEmbeddedPiResourceLoader(
options: Pick<
DefaultResourceLoaderInit,
"cwd" | "agentDir" | "settingsManager" | "extensionFactories"
>,
): DefaultResourceLoader {
return new DefaultResourceLoader({
...options,
...EMBEDDED_PI_RESOURCE_LOADER_DISCOVERY_OPTIONS,
});
}

View File

@@ -2,11 +2,7 @@ import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import type { AgentMessage } from "@earendil-works/pi-agent-core";
import {
createAgentSession,
DefaultResourceLoader,
SessionManager,
} from "@earendil-works/pi-coding-agent";
import { createAgentSession, SessionManager } from "@earendil-works/pi-coding-agent";
import { isAcpRuntimeSpawnAvailable } from "../../../acp/runtime/availability.js";
import { buildHierarchyReinforcementMessage } from "../../../auto-reply/handoff-summarizer.js";
import { filterHeartbeatPairs } from "../../../auto-reply/heartbeat-filter.js";
@@ -223,6 +219,7 @@ import {
validateReplayTurns,
} from "../replay-history.js";
import { observeReplayMetadata, replayMetadataFromState } from "../replay-state.js";
import { createEmbeddedPiResourceLoader } from "../resource-loader.js";
import {
clearActiveEmbeddedRun,
type EmbeddedPiQueueHandle,
@@ -1715,7 +1712,7 @@ export async function runEmbeddedAttempt(
modelId: params.modelId,
model: params.model,
});
const resourceLoader = new DefaultResourceLoader({
const resourceLoader = createEmbeddedPiResourceLoader({
cwd: resolvedWorkspace,
agentDir,
settingsManager,