Skip to content

Commit a1aca29

Browse files
committed
Switch passive extractor to direct pi-ai model calls
- Replace embedded OpenClaw runner with @mariozechner/pi-ai The embedded extractor — even isolated in a worker — pulls in the full agent runtime for a single JSON-only completion. Direct pi-ai calls give proper abort/timeout semantics natively. Embedded extraction is retained only for tests; production fails closed if no direct adapter is available. - Identify extractor turns by run id / provenance, not just sentinel Recursive passive jobs were possible whenever OpenClaw surfaced the extractor's run through runId or inputProvenance instead of sessionKey. Consolidate detection into isPassiveExtractorEvent() and apply it across the bridge handler and plugin lifecycle hooks. - Add PassiveExtractorProviderUnavailableError so the bridge log distinguishes pi-ai load failures, unsupported providers, and missing keys from the generic "provider_unavailable". - Fall back to the configured primary model when no active model ref was observed on the turn (e.g. plugin instances that never saw model_call_started).
1 parent 68319b0 commit a1aca29

7 files changed

Lines changed: 633 additions & 42 deletions

File tree

package-lock.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
}
5959
},
6060
"dependencies": {
61+
"@mariozechner/pi-ai": "^0.52.12",
6162
"zod": "^3.23.0"
6263
},
6364
"devDependencies": {

src/features/bridge/handler.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ import { redactBridgeTraceError } from "../../internal/clawdeploy-bridge-traces.
1818
import { isLowSignal, sanitizeConversationText } from "../capture/filter.js";
1919
import {
2020
PassiveExtractorTimeoutError,
21+
isPassiveExtractorEvent,
22+
isPassiveExtractorProviderUnavailableError,
2123
isPassiveExtractorSessionPathError,
2224
isPassiveExtractorTimeoutError,
23-
PASSIVE_EXTRACTOR_SESSION_KEY,
2425
} from "./openclaw-extractor.js";
2526
import {
2627
buildPassiveExtractorInput,
@@ -83,6 +84,8 @@ interface AgentEndEvent {
8384
aborted?: boolean;
8485
sessionKey?: string;
8586
sessionId?: string;
87+
runId?: string;
88+
inputProvenance?: unknown;
8689
}
8790

8891
interface BridgePromptEvent {
@@ -91,6 +94,8 @@ interface BridgePromptEvent {
9194
messages?: unknown[];
9295
sessionKey?: string;
9396
sessionId?: string;
97+
runId?: string;
98+
inputProvenance?: unknown;
9499
}
95100

96101
interface BridgeSessionState {
@@ -1121,6 +1126,8 @@ export function createBridgeHandler(
11211126
logger.warn(`Cortex bridge: passive_job_dropped reason=session_path_error sessionId=${job.sessionKey} durationMs=${durationMs}`);
11221127
} else if (err instanceof SyntaxError) {
11231128
logger.warn(`Cortex bridge: passive_extractor_invalid_json sessionId=${job.sessionKey} durationMs=${durationMs} reason=json_parse_failed`);
1129+
} else if (isPassiveExtractorProviderUnavailableError(err)) {
1130+
logger.warn(`Cortex bridge: passive_job_dropped reason=${err.reason} provider=${err.provider} sessionId=${job.sessionKey} durationMs=${durationMs}`);
11241131
} else {
11251132
logger.warn(`Cortex bridge: passive_job_dropped reason=provider_unavailable sessionId=${job.sessionKey} durationMs=${durationMs}`);
11261133
}
@@ -1207,6 +1214,7 @@ export function createBridgeHandler(
12071214
}
12081215

12091216
async function shouldInjectPrompt(event: BridgePromptEvent): Promise<BridgePromptMode> {
1217+
if (isPassiveExtractorEvent(event)) return false;
12101218
const sessionKey = resolveBridgeSessionKey(event);
12111219
const promptText = promptCandidateFromEvent(event);
12121220
if (isHeartbeatTurn(promptText)) {
@@ -1380,7 +1388,7 @@ export function createBridgeHandler(
13801388
async function handleAgentEnd(event: AgentEndEvent): Promise<boolean> {
13811389
if (event.aborted) return false;
13821390
if (!Array.isArray(event.messages) || event.messages.length === 0) return false;
1383-
if (event.sessionKey === PASSIVE_EXTRACTOR_SESSION_KEY || event.sessionId === PASSIVE_EXTRACTOR_SESSION_KEY) return false;
1391+
if (isPassiveExtractorEvent(event)) return false;
13841392
if (userIdReady) await userIdReady;
13851393

13861394
const agentUserId = getUserId();

0 commit comments

Comments
 (0)