mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 20:44:32 +00:00
Co-authored-by: Onur Solmaz <2453968+osolmaz@users.noreply.github.com>
This commit is contained in:
@@ -32,12 +32,19 @@ import {
|
||||
} from "../infra/outbound/session-binding-service.js";
|
||||
import { normalizeAgentId } from "../routing/session-key.js";
|
||||
import { normalizeDeliveryContext } from "../utils/delivery-context.js";
|
||||
import {
|
||||
type AcpSpawnParentRelayHandle,
|
||||
resolveAcpSpawnStreamLogPath,
|
||||
startAcpSpawnParentStreamRelay,
|
||||
} from "./acp-spawn-parent-stream.js";
|
||||
import { resolveSandboxRuntimeStatus } from "./sandbox/runtime-status.js";
|
||||
|
||||
export const ACP_SPAWN_MODES = ["run", "session"] as const;
|
||||
export type SpawnAcpMode = (typeof ACP_SPAWN_MODES)[number];
|
||||
export const ACP_SPAWN_SANDBOX_MODES = ["inherit", "require"] as const;
|
||||
export type SpawnAcpSandboxMode = (typeof ACP_SPAWN_SANDBOX_MODES)[number];
|
||||
export const ACP_SPAWN_STREAM_TARGETS = ["parent"] as const;
|
||||
export type SpawnAcpStreamTarget = (typeof ACP_SPAWN_STREAM_TARGETS)[number];
|
||||
|
||||
export type SpawnAcpParams = {
|
||||
task: string;
|
||||
@@ -47,6 +54,7 @@ export type SpawnAcpParams = {
|
||||
mode?: SpawnAcpMode;
|
||||
thread?: boolean;
|
||||
sandbox?: SpawnAcpSandboxMode;
|
||||
streamTo?: SpawnAcpStreamTarget;
|
||||
};
|
||||
|
||||
export type SpawnAcpContext = {
|
||||
@@ -63,6 +71,7 @@ export type SpawnAcpResult = {
|
||||
childSessionKey?: string;
|
||||
runId?: string;
|
||||
mode?: SpawnAcpMode;
|
||||
streamLogPath?: string;
|
||||
note?: string;
|
||||
error?: string;
|
||||
};
|
||||
@@ -234,6 +243,14 @@ export async function spawnAcpDirect(
|
||||
};
|
||||
}
|
||||
const sandboxMode = params.sandbox === "require" ? "require" : "inherit";
|
||||
const streamToParentRequested = params.streamTo === "parent";
|
||||
const parentSessionKey = ctx.agentSessionKey?.trim();
|
||||
if (streamToParentRequested && !parentSessionKey) {
|
||||
return {
|
||||
status: "error",
|
||||
error: 'sessions_spawn streamTo="parent" requires an active requester session context.',
|
||||
};
|
||||
}
|
||||
const requesterRuntime = resolveSandboxRuntimeStatus({
|
||||
cfg,
|
||||
sessionKey: ctx.agentSessionKey,
|
||||
@@ -410,8 +427,27 @@ export async function spawnAcpDirect(
|
||||
? `channel:${boundThreadId}`
|
||||
: requesterOrigin?.to?.trim() || (deliveryThreadId ? `channel:${deliveryThreadId}` : undefined);
|
||||
const hasDeliveryTarget = Boolean(requesterOrigin?.channel && inferredDeliveryTo);
|
||||
const deliverToBoundTarget = hasDeliveryTarget && !streamToParentRequested;
|
||||
const childIdem = crypto.randomUUID();
|
||||
let childRunId: string = childIdem;
|
||||
const streamLogPath =
|
||||
streamToParentRequested && parentSessionKey
|
||||
? resolveAcpSpawnStreamLogPath({
|
||||
childSessionKey: sessionKey,
|
||||
})
|
||||
: undefined;
|
||||
let parentRelay: AcpSpawnParentRelayHandle | undefined;
|
||||
if (streamToParentRequested && parentSessionKey) {
|
||||
// Register relay before dispatch so fast lifecycle failures are not missed.
|
||||
parentRelay = startAcpSpawnParentStreamRelay({
|
||||
runId: childIdem,
|
||||
parentSessionKey,
|
||||
childSessionKey: sessionKey,
|
||||
agentId: targetAgentId,
|
||||
logPath: streamLogPath,
|
||||
emitStartNotice: false,
|
||||
});
|
||||
}
|
||||
try {
|
||||
const response = await callGateway<{ runId?: string }>({
|
||||
method: "agent",
|
||||
@@ -423,7 +459,7 @@ export async function spawnAcpDirect(
|
||||
accountId: hasDeliveryTarget ? (requesterOrigin?.accountId ?? undefined) : undefined,
|
||||
threadId: hasDeliveryTarget ? deliveryThreadId : undefined,
|
||||
idempotencyKey: childIdem,
|
||||
deliver: hasDeliveryTarget,
|
||||
deliver: deliverToBoundTarget,
|
||||
label: params.label || undefined,
|
||||
},
|
||||
timeoutMs: 10_000,
|
||||
@@ -432,6 +468,7 @@ export async function spawnAcpDirect(
|
||||
childRunId = response.runId.trim();
|
||||
}
|
||||
} catch (err) {
|
||||
parentRelay?.dispose();
|
||||
await cleanupFailedAcpSpawn({
|
||||
cfg,
|
||||
sessionKey,
|
||||
@@ -445,6 +482,30 @@ export async function spawnAcpDirect(
|
||||
};
|
||||
}
|
||||
|
||||
if (streamToParentRequested && parentSessionKey) {
|
||||
if (parentRelay && childRunId !== childIdem) {
|
||||
parentRelay.dispose();
|
||||
// Defensive fallback if gateway returns a runId that differs from idempotency key.
|
||||
parentRelay = startAcpSpawnParentStreamRelay({
|
||||
runId: childRunId,
|
||||
parentSessionKey,
|
||||
childSessionKey: sessionKey,
|
||||
agentId: targetAgentId,
|
||||
logPath: streamLogPath,
|
||||
emitStartNotice: false,
|
||||
});
|
||||
}
|
||||
parentRelay?.notifyStarted();
|
||||
return {
|
||||
status: "accepted",
|
||||
childSessionKey: sessionKey,
|
||||
runId: childRunId,
|
||||
mode: spawnMode,
|
||||
...(streamLogPath ? { streamLogPath } : {}),
|
||||
note: spawnMode === "session" ? ACP_SPAWN_SESSION_ACCEPTED_NOTE : ACP_SPAWN_ACCEPTED_NOTE,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: "accepted",
|
||||
childSessionKey: sessionKey,
|
||||
|
||||
Reference in New Issue
Block a user