refactor(src): split oversized modules

This commit is contained in:
Peter Steinberger
2026-01-14 01:08:15 +00:00
parent b2179de839
commit bcbfb357be
675 changed files with 91476 additions and 73453 deletions

View File

@@ -0,0 +1,134 @@
import {
getChannelPlugin,
normalizeChannelId,
} from "../../channels/plugins/index.js";
import type { ChannelOutboundTargetMode } from "../../channels/plugins/types.js";
import { DEFAULT_CHAT_CHANNEL } from "../../channels/registry.js";
import type { CliDeps } from "../../cli/deps.js";
import { createOutboundSendDeps } from "../../cli/deps.js";
import type { ClawdbotConfig } from "../../config/config.js";
import type { SessionEntry } from "../../config/sessions.js";
import { deliverOutboundPayloads } from "../../infra/outbound/deliver.js";
import { buildOutboundResultEnvelope } from "../../infra/outbound/envelope.js";
import {
formatOutboundPayloadLog,
type NormalizedOutboundPayload,
normalizeOutboundPayloads,
normalizeOutboundPayloadsForJson,
} from "../../infra/outbound/payloads.js";
import { resolveOutboundTarget } from "../../infra/outbound/targets.js";
import type { RuntimeEnv } from "../../runtime.js";
import {
isInternalMessageChannel,
resolveGatewayMessageChannel,
} from "../../utils/message-channel.js";
import type { AgentCommandOpts } from "./types.js";
type RunResult = Awaited<
ReturnType<typeof import("../../agents/pi-embedded.js")["runEmbeddedPiAgent"]>
>;
export async function deliverAgentCommandResult(params: {
cfg: ClawdbotConfig;
deps: CliDeps;
runtime: RuntimeEnv;
opts: AgentCommandOpts;
sessionEntry: SessionEntry | undefined;
result: RunResult;
payloads: RunResult["payloads"];
}) {
const { cfg, deps, runtime, opts, sessionEntry, payloads, result } = params;
const deliver = opts.deliver === true;
const bestEffortDeliver = opts.bestEffortDeliver === true;
const deliveryChannel =
resolveGatewayMessageChannel(opts.channel) ?? DEFAULT_CHAT_CHANNEL;
// Channel docking: delivery channels are resolved via plugin registry.
const deliveryPlugin = !isInternalMessageChannel(deliveryChannel)
? getChannelPlugin(normalizeChannelId(deliveryChannel) ?? deliveryChannel)
: undefined;
const isDeliveryChannelKnown =
isInternalMessageChannel(deliveryChannel) || Boolean(deliveryPlugin);
const targetMode: ChannelOutboundTargetMode =
opts.deliveryTargetMode ?? (opts.to ? "explicit" : "implicit");
const resolvedTarget =
deliver && isDeliveryChannelKnown && deliveryChannel
? resolveOutboundTarget({
channel: deliveryChannel,
to: opts.to,
cfg,
accountId:
targetMode === "implicit" ? sessionEntry?.lastAccountId : undefined,
mode: targetMode,
})
: null;
const deliveryTarget = resolvedTarget?.ok ? resolvedTarget.to : undefined;
const logDeliveryError = (err: unknown) => {
const message = `Delivery failed (${deliveryChannel}${deliveryTarget ? ` to ${deliveryTarget}` : ""}): ${String(err)}`;
runtime.error?.(message);
if (!runtime.error) runtime.log(message);
};
if (deliver) {
if (!isDeliveryChannelKnown) {
const err = new Error(`Unknown channel: ${deliveryChannel}`);
if (!bestEffortDeliver) throw err;
logDeliveryError(err);
} else if (resolvedTarget && !resolvedTarget.ok) {
if (!bestEffortDeliver) throw resolvedTarget.error;
logDeliveryError(resolvedTarget.error);
}
}
const normalizedPayloads = normalizeOutboundPayloadsForJson(payloads ?? []);
if (opts.json) {
runtime.log(
JSON.stringify(
buildOutboundResultEnvelope({
payloads: normalizedPayloads,
meta: result.meta,
}),
null,
2,
),
);
if (!deliver) return { payloads: normalizedPayloads, meta: result.meta };
}
if (!payloads || payloads.length === 0) {
runtime.log("No reply from agent.");
return { payloads: [], meta: result.meta };
}
const deliveryPayloads = normalizeOutboundPayloads(payloads);
const logPayload = (payload: NormalizedOutboundPayload) => {
if (opts.json) return;
const output = formatOutboundPayloadLog(payload);
if (output) runtime.log(output);
};
if (!deliver) {
for (const payload of deliveryPayloads) logPayload(payload);
}
if (
deliver &&
deliveryChannel &&
!isInternalMessageChannel(deliveryChannel)
) {
if (deliveryTarget) {
await deliverOutboundPayloads({
cfg,
channel: deliveryChannel,
to: deliveryTarget,
payloads: deliveryPayloads,
bestEffort: bestEffortDeliver,
onError: (err) => logDeliveryError(err),
onPayload: logPayload,
deps: createOutboundSendDeps(deps, cfg),
});
}
}
return { payloads: normalizedPayloads, meta: result.meta };
}

View File

@@ -0,0 +1,77 @@
import { setCliSessionId } from "../../agents/cli-session.js";
import { lookupContextTokens } from "../../agents/context.js";
import { DEFAULT_CONTEXT_TOKENS } from "../../agents/defaults.js";
import { isCliProvider } from "../../agents/model-selection.js";
import { hasNonzeroUsage } from "../../agents/usage.js";
import type { ClawdbotConfig } from "../../config/config.js";
import { type SessionEntry, saveSessionStore } from "../../config/sessions.js";
type RunResult = Awaited<
ReturnType<typeof import("../../agents/pi-embedded.js")["runEmbeddedPiAgent"]>
>;
export async function updateSessionStoreAfterAgentRun(params: {
cfg: ClawdbotConfig;
contextTokensOverride?: number;
sessionId: string;
sessionKey: string;
storePath: string;
sessionStore: Record<string, SessionEntry>;
defaultProvider: string;
defaultModel: string;
fallbackProvider?: string;
fallbackModel?: string;
result: RunResult;
}) {
const {
cfg,
sessionId,
sessionKey,
storePath,
sessionStore,
defaultProvider,
defaultModel,
fallbackProvider,
fallbackModel,
result,
} = params;
const usage = result.meta.agentMeta?.usage;
const modelUsed =
result.meta.agentMeta?.model ?? fallbackModel ?? defaultModel;
const providerUsed =
result.meta.agentMeta?.provider ?? fallbackProvider ?? defaultProvider;
const contextTokens =
params.contextTokensOverride ??
lookupContextTokens(modelUsed) ??
DEFAULT_CONTEXT_TOKENS;
const entry = sessionStore[sessionKey] ?? {
sessionId,
updatedAt: Date.now(),
};
const next: SessionEntry = {
...entry,
sessionId,
updatedAt: Date.now(),
modelProvider: providerUsed,
model: modelUsed,
contextTokens,
};
if (isCliProvider(providerUsed, cfg)) {
const cliSessionId = result.meta.agentMeta?.sessionId?.trim();
if (cliSessionId) setCliSessionId(next, providerUsed, cliSessionId);
}
next.abortedLastRun = result.meta.aborted ?? false;
if (hasNonzeroUsage(usage)) {
const input = usage.input ?? 0;
const output = usage.output ?? 0;
const promptTokens =
input + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0);
next.inputTokens = input;
next.outputTokens = output;
next.totalTokens = promptTokens > 0 ? promptTokens : (usage.total ?? input);
}
sessionStore[sessionKey] = next;
await saveSessionStore(storePath, sessionStore);
}

View File

@@ -0,0 +1,103 @@
import crypto from "node:crypto";
import type { MsgContext } from "../../auto-reply/templating.js";
import {
normalizeThinkLevel,
normalizeVerboseLevel,
type ThinkLevel,
type VerboseLevel,
} from "../../auto-reply/thinking.js";
import type { ClawdbotConfig } from "../../config/config.js";
import {
DEFAULT_IDLE_MINUTES,
loadSessionStore,
resolveAgentIdFromSessionKey,
resolveSessionKey,
resolveStorePath,
type SessionEntry,
} from "../../config/sessions.js";
import { normalizeMainKey } from "../../routing/session-key.js";
export type SessionResolution = {
sessionId: string;
sessionKey?: string;
sessionEntry?: SessionEntry;
sessionStore?: Record<string, SessionEntry>;
storePath: string;
isNewSession: boolean;
persistedThinking?: ThinkLevel;
persistedVerbose?: VerboseLevel;
};
export function resolveSession(opts: {
cfg: ClawdbotConfig;
to?: string;
sessionId?: string;
sessionKey?: string;
}): SessionResolution {
const sessionCfg = opts.cfg.session;
const scope = sessionCfg?.scope ?? "per-sender";
const mainKey = normalizeMainKey(sessionCfg?.mainKey);
const idleMinutes = Math.max(
sessionCfg?.idleMinutes ?? DEFAULT_IDLE_MINUTES,
1,
);
const idleMs = idleMinutes * 60_000;
const explicitSessionKey = opts.sessionKey?.trim();
const storeAgentId = resolveAgentIdFromSessionKey(explicitSessionKey);
const storePath = resolveStorePath(sessionCfg?.store, {
agentId: storeAgentId,
});
const sessionStore = loadSessionStore(storePath);
const now = Date.now();
const ctx: MsgContext | undefined = opts.to?.trim()
? { From: opts.to }
: undefined;
let sessionKey: string | undefined =
explicitSessionKey ??
(ctx ? resolveSessionKey(scope, ctx, mainKey) : undefined);
let sessionEntry = sessionKey ? sessionStore[sessionKey] : undefined;
// If a session id was provided, prefer to re-use its entry (by id) even when no key was derived.
if (
!explicitSessionKey &&
opts.sessionId &&
(!sessionEntry || sessionEntry.sessionId !== opts.sessionId)
) {
const foundKey = Object.keys(sessionStore).find(
(key) => sessionStore[key]?.sessionId === opts.sessionId,
);
if (foundKey) {
sessionKey = sessionKey ?? foundKey;
sessionEntry = sessionStore[foundKey];
}
}
const fresh = sessionEntry && sessionEntry.updatedAt >= now - idleMs;
const sessionId =
opts.sessionId?.trim() ||
(fresh ? sessionEntry?.sessionId : undefined) ||
crypto.randomUUID();
const isNewSession = !fresh && !opts.sessionId;
const persistedThinking =
fresh && sessionEntry?.thinkingLevel
? normalizeThinkLevel(sessionEntry.thinkingLevel)
: undefined;
const persistedVerbose =
fresh && sessionEntry?.verboseLevel
? normalizeVerboseLevel(sessionEntry.verboseLevel)
: undefined;
return {
sessionId,
sessionKey,
sessionEntry,
sessionStore,
storePath,
isNewSession,
persistedThinking,
persistedVerbose,
};
}

View File

@@ -0,0 +1,32 @@
import type { ChannelOutboundTargetMode } from "../../channels/plugins/types.js";
/** Image content block for Claude API multimodal messages. */
export type ImageContent = {
type: "image";
data: string;
mimeType: string;
};
export type AgentCommandOpts = {
message: string;
/** Optional image attachments for multimodal messages. */
images?: ImageContent[];
to?: string;
sessionId?: string;
sessionKey?: string;
thinking?: string;
thinkingOnce?: string;
verbose?: string;
json?: boolean;
timeout?: string;
deliver?: boolean;
/** Message channel context (webchat|voicewake|whatsapp|...). */
messageChannel?: string;
channel?: string; // delivery channel (whatsapp|telegram|...)
deliveryTargetMode?: ChannelOutboundTargetMode;
bestEffortDeliver?: boolean;
abortSignal?: AbortSignal;
lane?: string;
runId?: string;
extraSystemPrompt?: string;
};