mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 21:00:44 +00:00
refactor(channels): add shared turn kernel
This commit is contained in:
@@ -175,6 +175,7 @@ function createFeishuBotRuntime(overrides: DeepPartial<PluginRuntime> = {}): Plu
|
||||
session: {
|
||||
readSessionUpdatedAt: readSessionUpdatedAtMock,
|
||||
resolveStorePath: resolveStorePathMock,
|
||||
recordInboundSession: vi.fn(async () => undefined),
|
||||
},
|
||||
reply: {
|
||||
resolveEnvelopeFormatOptions:
|
||||
@@ -196,6 +197,11 @@ function createFeishuBotRuntime(overrides: DeepPartial<PluginRuntime> = {}): Plu
|
||||
upsertPairingRequest: vi.fn(),
|
||||
buildPairingReply: vi.fn(),
|
||||
},
|
||||
turn: {
|
||||
runPrepared: vi.fn(async (params) => ({
|
||||
dispatchResult: await params.runDispatch(),
|
||||
})),
|
||||
},
|
||||
...overrides.channel,
|
||||
},
|
||||
...(overrides.system ? { system: overrides.system as PluginRuntime["system"] } : {}),
|
||||
|
||||
@@ -1268,8 +1268,18 @@ export async function handleFeishuMessage(params: {
|
||||
}
|
||||
|
||||
const agentSessionKey = buildBroadcastSessionKey(route.sessionKey, route.agentId, agentId);
|
||||
const agentStorePath = core.channel.session.resolveStorePath(cfg.session?.store, {
|
||||
agentId,
|
||||
});
|
||||
const agentRecord = {
|
||||
onRecordError: (err: unknown) => {
|
||||
log(
|
||||
`feishu[${account.accountId}]: failed to record broadcast inbound session ${agentSessionKey}: ${String(err)}`,
|
||||
);
|
||||
},
|
||||
};
|
||||
const allowReasoningPreview = resolveFeishuReasoningPreviewEnabled({
|
||||
storePath: core.channel.session.resolveStorePath(cfg.session?.store, { agentId }),
|
||||
storePath: agentStorePath,
|
||||
sessionKey: agentSessionKey,
|
||||
});
|
||||
const agentCtx = await buildCtxPayloadForAgent(
|
||||
@@ -1302,15 +1312,30 @@ export async function handleFeishuMessage(params: {
|
||||
log(
|
||||
`feishu[${account.accountId}]: broadcast active dispatch agent=${agentId} (session=${agentSessionKey})`,
|
||||
);
|
||||
await core.channel.reply.withReplyDispatcher({
|
||||
dispatcher,
|
||||
onSettled: () => markDispatchIdle(),
|
||||
run: () =>
|
||||
core.channel.reply.dispatchReplyFromConfig({
|
||||
ctx: agentCtx,
|
||||
cfg,
|
||||
await core.channel.turn.runPrepared({
|
||||
channel: "feishu",
|
||||
accountId: route.accountId,
|
||||
routeSessionKey: agentSessionKey,
|
||||
storePath: agentStorePath,
|
||||
ctxPayload: agentCtx,
|
||||
recordInboundSession: core.channel.session.recordInboundSession,
|
||||
record: agentRecord,
|
||||
onPreDispatchFailure: () =>
|
||||
core.channel.reply.settleReplyDispatcher({
|
||||
dispatcher,
|
||||
replyOptions,
|
||||
onSettled: () => markDispatchIdle(),
|
||||
}),
|
||||
runDispatch: () =>
|
||||
core.channel.reply.withReplyDispatcher({
|
||||
dispatcher,
|
||||
onSettled: () => markDispatchIdle(),
|
||||
run: () =>
|
||||
core.channel.reply.dispatchReplyFromConfig({
|
||||
ctx: agentCtx,
|
||||
cfg,
|
||||
dispatcher,
|
||||
replyOptions,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
} else {
|
||||
@@ -1331,13 +1356,23 @@ export async function handleFeishuMessage(params: {
|
||||
log(
|
||||
`feishu[${account.accountId}]: broadcast observer dispatch agent=${agentId} (session=${agentSessionKey})`,
|
||||
);
|
||||
await core.channel.reply.withReplyDispatcher({
|
||||
dispatcher: noopDispatcher,
|
||||
run: () =>
|
||||
core.channel.reply.dispatchReplyFromConfig({
|
||||
ctx: agentCtx,
|
||||
cfg,
|
||||
await core.channel.turn.runPrepared({
|
||||
channel: "feishu",
|
||||
accountId: route.accountId,
|
||||
routeSessionKey: agentSessionKey,
|
||||
storePath: agentStorePath,
|
||||
ctxPayload: agentCtx,
|
||||
recordInboundSession: core.channel.session.recordInboundSession,
|
||||
record: agentRecord,
|
||||
runDispatch: () =>
|
||||
core.channel.reply.withReplyDispatcher({
|
||||
dispatcher: noopDispatcher,
|
||||
run: () =>
|
||||
core.channel.reply.dispatchReplyFromConfig({
|
||||
ctx: agentCtx,
|
||||
cfg,
|
||||
dispatcher: noopDispatcher,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
}
|
||||
@@ -1385,10 +1420,11 @@ export async function handleFeishuMessage(params: {
|
||||
);
|
||||
|
||||
const identity = resolveAgentOutboundIdentity(cfg, route.agentId);
|
||||
const storePath = core.channel.session.resolveStorePath(cfg.session?.store, {
|
||||
agentId: route.agentId,
|
||||
});
|
||||
const allowReasoningPreview = resolveFeishuReasoningPreviewEnabled({
|
||||
storePath: core.channel.session.resolveStorePath(cfg.session?.store, {
|
||||
agentId: route.agentId,
|
||||
}),
|
||||
storePath,
|
||||
sessionKey: route.sessionKey,
|
||||
});
|
||||
const { dispatcher, replyOptions, markDispatchIdle } = createFeishuReplyDispatcher({
|
||||
@@ -1409,19 +1445,41 @@ export async function handleFeishuMessage(params: {
|
||||
});
|
||||
|
||||
log(`feishu[${account.accountId}]: dispatching to agent (session=${route.sessionKey})`);
|
||||
const { queuedFinal, counts } = await core.channel.reply.withReplyDispatcher({
|
||||
dispatcher,
|
||||
onSettled: () => {
|
||||
markDispatchIdle();
|
||||
const { dispatchResult } = await core.channel.turn.runPrepared({
|
||||
channel: "feishu",
|
||||
accountId: route.accountId,
|
||||
routeSessionKey: route.sessionKey,
|
||||
storePath,
|
||||
ctxPayload,
|
||||
recordInboundSession: core.channel.session.recordInboundSession,
|
||||
record: {
|
||||
onRecordError: (err) => {
|
||||
log(
|
||||
`feishu[${account.accountId}]: failed to record inbound session ${route.sessionKey}: ${String(err)}`,
|
||||
);
|
||||
},
|
||||
},
|
||||
run: () =>
|
||||
core.channel.reply.dispatchReplyFromConfig({
|
||||
ctx: ctxPayload,
|
||||
cfg,
|
||||
onPreDispatchFailure: () =>
|
||||
core.channel.reply.settleReplyDispatcher({
|
||||
dispatcher,
|
||||
replyOptions,
|
||||
onSettled: () => markDispatchIdle(),
|
||||
}),
|
||||
runDispatch: () =>
|
||||
core.channel.reply.withReplyDispatcher({
|
||||
dispatcher,
|
||||
onSettled: () => {
|
||||
markDispatchIdle();
|
||||
},
|
||||
run: () =>
|
||||
core.channel.reply.dispatchReplyFromConfig({
|
||||
ctx: ctxPayload,
|
||||
cfg,
|
||||
dispatcher,
|
||||
replyOptions,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
const { queuedFinal, counts } = dispatchResult;
|
||||
|
||||
if (isGroup && historyKey && chatHistories) {
|
||||
clearHistoryEntriesIfEnabled({
|
||||
|
||||
@@ -86,6 +86,27 @@ function createTestRuntime(overrides?: {
|
||||
},
|
||||
);
|
||||
const recordInboundSession = vi.fn(async () => {});
|
||||
const runPrepared = vi.fn(
|
||||
async (turn: Parameters<PluginRuntime["channel"]["turn"]["runPrepared"]>[0]) => {
|
||||
await turn.recordInboundSession({
|
||||
storePath: turn.storePath,
|
||||
sessionKey: turn.ctxPayload.SessionKey ?? turn.routeSessionKey,
|
||||
ctx: turn.ctxPayload,
|
||||
groupResolution: turn.record?.groupResolution,
|
||||
createIfMissing: turn.record?.createIfMissing,
|
||||
updateLastRoute: turn.record?.updateLastRoute,
|
||||
onRecordError: turn.record?.onRecordError ?? (() => undefined),
|
||||
});
|
||||
const dispatchResult = await turn.runDispatch();
|
||||
return {
|
||||
admission: { kind: "dispatch" as const },
|
||||
dispatched: true,
|
||||
ctxPayload: turn.ctxPayload,
|
||||
routeSessionKey: turn.routeSessionKey,
|
||||
dispatchResult,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
channel: {
|
||||
@@ -112,6 +133,11 @@ function createTestRuntime(overrides?: {
|
||||
resolveStorePath: vi.fn(() => "/tmp/feishu-session-store.json"),
|
||||
recordInboundSession,
|
||||
},
|
||||
turn: {
|
||||
runPrepared: runPrepared as unknown as PluginRuntime["channel"]["turn"]["runPrepared"],
|
||||
dispatchAssembled:
|
||||
vi.fn() as unknown as PluginRuntime["channel"]["turn"]["dispatchAssembled"],
|
||||
},
|
||||
pairing: {
|
||||
readAllowFromStore: vi.fn(overrides?.readAllowFromStore ?? (async () => [])),
|
||||
upsertPairingRequest: vi.fn(
|
||||
|
||||
@@ -221,16 +221,6 @@ export async function handleFeishuCommentEvent(
|
||||
const storePath = core.channel.session.resolveStorePath(effectiveCfg.session?.store, {
|
||||
agentId: route.agentId,
|
||||
});
|
||||
await core.channel.session.recordInboundSession({
|
||||
storePath,
|
||||
sessionKey: commentSessionKey,
|
||||
ctx: ctxPayload,
|
||||
onRecordError: (err) => {
|
||||
error(
|
||||
`feishu[${account.accountId}]: failed to record comment inbound session ${commentSessionKey}: ${String(err)}`,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const { dispatcher, replyOptions, markDispatchIdle, markRunComplete, cleanupTypingReaction } =
|
||||
createFeishuCommentReplyDispatcher({
|
||||
@@ -245,28 +235,59 @@ export async function handleFeishuCommentEvent(
|
||||
isWholeComment: turn.isWholeComment,
|
||||
});
|
||||
|
||||
let dispatchSettledBeforeStart = false;
|
||||
try {
|
||||
log(
|
||||
`feishu[${account.accountId}]: dispatching drive comment to agent ` +
|
||||
`(session=${commentSessionKey} comment=${turn.commentId} type=${turn.noticeType})`,
|
||||
);
|
||||
const { queuedFinal, counts } = await core.channel.reply.withReplyDispatcher({
|
||||
dispatcher,
|
||||
run: () =>
|
||||
core.channel.reply.dispatchReplyFromConfig({
|
||||
ctx: ctxPayload,
|
||||
cfg: effectiveCfg,
|
||||
const { dispatchResult } = await core.channel.turn.runPrepared({
|
||||
channel: "feishu",
|
||||
accountId: route.accountId,
|
||||
routeSessionKey: commentSessionKey,
|
||||
storePath,
|
||||
ctxPayload,
|
||||
recordInboundSession: core.channel.session.recordInboundSession,
|
||||
record: {
|
||||
onRecordError: (err) => {
|
||||
error(
|
||||
`feishu[${account.accountId}]: failed to record comment inbound session ${commentSessionKey}: ${String(err)}`,
|
||||
);
|
||||
},
|
||||
},
|
||||
onPreDispatchFailure: async () => {
|
||||
dispatchSettledBeforeStart = true;
|
||||
await core.channel.reply.settleReplyDispatcher({
|
||||
dispatcher,
|
||||
replyOptions,
|
||||
onSettled: () => {
|
||||
markRunComplete();
|
||||
markDispatchIdle();
|
||||
},
|
||||
});
|
||||
},
|
||||
runDispatch: () =>
|
||||
core.channel.reply.withReplyDispatcher({
|
||||
dispatcher,
|
||||
run: () =>
|
||||
core.channel.reply.dispatchReplyFromConfig({
|
||||
ctx: ctxPayload,
|
||||
cfg: effectiveCfg,
|
||||
dispatcher,
|
||||
replyOptions,
|
||||
}),
|
||||
}),
|
||||
});
|
||||
const queuedFinal = dispatchResult?.queuedFinal ?? false;
|
||||
const counts = dispatchResult?.counts ?? { tool: 0, block: 0, final: 0 };
|
||||
log(
|
||||
`feishu[${account.accountId}]: drive comment dispatch complete ` +
|
||||
`(queuedFinal=${queuedFinal}, replies=${counts.final}, session=${commentSessionKey})`,
|
||||
);
|
||||
} finally {
|
||||
markRunComplete();
|
||||
markDispatchIdle();
|
||||
if (!dispatchSettledBeforeStart) {
|
||||
markRunComplete();
|
||||
markDispatchIdle();
|
||||
}
|
||||
void cleanupTypingReaction();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user