chore: fork @llblab/[email protected].0 as @evandrodevbr/oh-my-pi-telegram
Fork of upstream llblab/[email protected].0 (SHA afe09c5) repackaged as a drop-in extension for can1357/oh-my-pi (omp >= 16.5.2). Changes vs upstream: - Rename package: @llblab/pi-telegram -> @evandrodevbr/oh-my-pi-telegram - Bump version: 0.22.0 -> 0.22.0-evandro.1 - Update repository/homepage/bugs URLs to evandrodevbr - Add omp field in package.json (displayName + compatibleWith) - README: prepend 'Oh My Pi (omp) Compatibility' section with install commands, verified-components table, upstream lineage, and rationale - CHANGELOG: add '0.22.0-evandro.1' entry under Unreleased - docs/: rename @llblab/pi-telegram imports to @evandrodevbr/oh-my-pi-telegram - tests/public-api.test.ts: rename package import paths Gates re-run on the fork: - npm run typecheck -> tsc --noEmit (success) - npm test -> 1229 pass / 0 fail / 1 platform-only skip - npm run audit -> found 0 vulnerabilities - npm run pack:check -> evandrodevbr-oh-my-pi-telegram-0.22.0-evandro.1.tgz Verified: - omp install --link ./pi-telegram -> Linked OK - omp plugin list -> @evandrodevbr/[email protected].0-evandro.1 Upstream lineage: badlogic/pi-telegram -> llblab/pi-telegram (0.22.0) -> evandrodevbr/oh-my-pi-telegram (this fork)
This commit is contained in:
commit
d1d04001d0
155 files changed
+107848
No files matched your search
+631
@@ -0,0 +1,631 @@
|
||||
/**
|
||||
* Telegram bridge runtime-state helpers
|
||||
* Zones: pi agent runtime state, telegram session, shared coordination
|
||||
* Owns small session-local runtime primitives that are shared by orchestration but are not specific to queueing, rendering, polling, or Telegram transport
|
||||
*/
|
||||
|
||||
const TELEGRAM_TYPING_ACTION_INTERVAL_MS = 2500;
|
||||
const TELEGRAM_TYPING_IDLE_DRAIN_MAX_MS = 250;
|
||||
|
||||
export interface TelegramRuntimeQueueCounters {
|
||||
nextQueuedTelegramItemOrder: number;
|
||||
nextQueuedTelegramControlOrder: number;
|
||||
nextPriorityReactionOrder: number;
|
||||
}
|
||||
|
||||
export interface TelegramRuntimeLifecycleFlags {
|
||||
activeTelegramToolExecutions: number;
|
||||
telegramTurnDispatchPending: boolean;
|
||||
compactionInProgress: boolean;
|
||||
foldQueuedPromptsIntoHistory: boolean;
|
||||
setupInProgress: boolean;
|
||||
}
|
||||
|
||||
export interface TelegramBridgeRuntimeState
|
||||
extends TelegramRuntimeQueueCounters, TelegramRuntimeLifecycleFlags {
|
||||
abortHandler?: () => void;
|
||||
typingInterval?: ReturnType<typeof setInterval>;
|
||||
typingInFlight?: Promise<void>;
|
||||
typingLoopDeps?: TelegramTypingLoopDeps;
|
||||
typingLoopKey?: string;
|
||||
}
|
||||
|
||||
export interface TelegramRuntimeQueuePort {
|
||||
syncCounters: (counters: Partial<TelegramRuntimeQueueCounters>) => void;
|
||||
allocateItemOrder: () => number;
|
||||
allocateControlOrder: () => number;
|
||||
getNextPriorityReactionOrder: () => number;
|
||||
incrementNextPriorityReactionOrder: () => void;
|
||||
}
|
||||
|
||||
export interface TelegramRuntimeLifecyclePort {
|
||||
syncFlags: (flags: Partial<TelegramRuntimeLifecycleFlags>) => void;
|
||||
getActiveToolExecutions: () => number;
|
||||
setActiveToolExecutions: (count: number) => void;
|
||||
resetActiveToolExecutions: () => void;
|
||||
hasDispatchPending: () => boolean;
|
||||
setDispatchPending: (pending: boolean) => void;
|
||||
clearDispatchPending: () => void;
|
||||
isCompactionInProgress: () => boolean;
|
||||
setCompactionInProgress: (inProgress: boolean) => void;
|
||||
shouldFoldQueuedPromptsIntoHistory: () => boolean;
|
||||
setFoldQueuedPromptsIntoHistory: (fold: boolean) => void;
|
||||
}
|
||||
|
||||
export interface TelegramRuntimeSetupPort {
|
||||
isInProgress: () => boolean;
|
||||
start: () => boolean;
|
||||
finish: () => void;
|
||||
}
|
||||
|
||||
export interface TelegramRuntimeAbortPort {
|
||||
hasHandler: () => boolean;
|
||||
setHandler: (abortHandler: () => void) => void;
|
||||
clearHandler: () => void;
|
||||
getHandler: () => (() => void) | undefined;
|
||||
abortTurn: () => boolean;
|
||||
}
|
||||
|
||||
export interface TelegramRuntimeTypingPort {
|
||||
start: (deps: TelegramTypingLoopDeps) => boolean;
|
||||
stop: () => boolean;
|
||||
waitForIdle: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface TelegramBridgeRuntime {
|
||||
state: TelegramBridgeRuntimeState;
|
||||
queue: TelegramRuntimeQueuePort;
|
||||
lifecycle: TelegramRuntimeLifecyclePort;
|
||||
setup: TelegramRuntimeSetupPort;
|
||||
abort: TelegramRuntimeAbortPort;
|
||||
typing: TelegramRuntimeTypingPort;
|
||||
}
|
||||
|
||||
export function createTelegramBridgeRuntimeState(): TelegramBridgeRuntimeState {
|
||||
return {
|
||||
nextQueuedTelegramItemOrder: 0,
|
||||
nextQueuedTelegramControlOrder: 0,
|
||||
nextPriorityReactionOrder: 0,
|
||||
activeTelegramToolExecutions: 0,
|
||||
telegramTurnDispatchPending: false,
|
||||
compactionInProgress: false,
|
||||
foldQueuedPromptsIntoHistory: false,
|
||||
setupInProgress: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function createTelegramBridgeRuntime(
|
||||
state = createTelegramBridgeRuntimeState(),
|
||||
): TelegramBridgeRuntime {
|
||||
return {
|
||||
state,
|
||||
queue: {
|
||||
syncCounters: (counters) =>
|
||||
syncTelegramQueueRuntimeCounters(state, counters),
|
||||
allocateItemOrder: () => allocateTelegramQueueItemOrder(state),
|
||||
allocateControlOrder: () => allocateTelegramQueueControlOrder(state),
|
||||
getNextPriorityReactionOrder: () =>
|
||||
getNextTelegramPriorityReactionOrder(state),
|
||||
incrementNextPriorityReactionOrder: () =>
|
||||
incrementNextTelegramPriorityReactionOrder(state),
|
||||
},
|
||||
lifecycle: {
|
||||
syncFlags: (flags) => syncTelegramLifecycleRuntimeFlags(state, flags),
|
||||
getActiveToolExecutions: () => getActiveTelegramToolExecutions(state),
|
||||
setActiveToolExecutions: (count) =>
|
||||
setActiveTelegramToolExecutions(state, count),
|
||||
resetActiveToolExecutions: () => resetActiveTelegramToolExecutions(state),
|
||||
hasDispatchPending: () => hasTelegramDispatchPending(state),
|
||||
setDispatchPending: (pending) =>
|
||||
setTelegramDispatchPending(state, pending),
|
||||
clearDispatchPending: () => clearTelegramDispatchPending(state),
|
||||
isCompactionInProgress: () => isTelegramCompactionInProgress(state),
|
||||
setCompactionInProgress: (inProgress) =>
|
||||
setTelegramCompactionInProgress(state, inProgress),
|
||||
shouldFoldQueuedPromptsIntoHistory: () =>
|
||||
shouldFoldQueuedPromptsIntoHistory(state),
|
||||
setFoldQueuedPromptsIntoHistory: (fold) =>
|
||||
setFoldQueuedPromptsIntoHistory(state, fold),
|
||||
},
|
||||
setup: {
|
||||
isInProgress: () => isTelegramSetupInProgress(state),
|
||||
start: () => startTelegramSetup(state),
|
||||
finish: () => finishTelegramSetup(state),
|
||||
},
|
||||
abort: {
|
||||
hasHandler: () => hasTelegramAbortHandler(state),
|
||||
setHandler: (abortHandler) =>
|
||||
setTelegramAbortHandler(state, abortHandler),
|
||||
clearHandler: () => clearTelegramAbortHandler(state),
|
||||
getHandler: () => getTelegramAbortHandler(state),
|
||||
abortTurn: () => abortTelegramTurn(state),
|
||||
},
|
||||
typing: {
|
||||
start: (deps) => startTelegramTypingLoop(state, deps),
|
||||
stop: () => stopTelegramTypingLoop(state),
|
||||
waitForIdle: () => waitForTelegramTypingLoopIdle(state),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function syncTelegramQueueRuntimeCounters(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
counters: Partial<TelegramRuntimeQueueCounters>,
|
||||
): void {
|
||||
if (counters.nextQueuedTelegramItemOrder !== undefined) {
|
||||
state.nextQueuedTelegramItemOrder = counters.nextQueuedTelegramItemOrder;
|
||||
}
|
||||
if (counters.nextQueuedTelegramControlOrder !== undefined) {
|
||||
state.nextQueuedTelegramControlOrder =
|
||||
counters.nextQueuedTelegramControlOrder;
|
||||
}
|
||||
if (counters.nextPriorityReactionOrder !== undefined) {
|
||||
state.nextPriorityReactionOrder = counters.nextPriorityReactionOrder;
|
||||
}
|
||||
}
|
||||
|
||||
export function allocateTelegramQueueItemOrder(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
): number {
|
||||
return state.nextQueuedTelegramItemOrder++;
|
||||
}
|
||||
|
||||
export function allocateTelegramQueueControlOrder(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
): number {
|
||||
return state.nextQueuedTelegramControlOrder++;
|
||||
}
|
||||
|
||||
export function getNextTelegramPriorityReactionOrder(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
): number {
|
||||
return state.nextPriorityReactionOrder;
|
||||
}
|
||||
|
||||
export function incrementNextTelegramPriorityReactionOrder(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
): void {
|
||||
state.nextPriorityReactionOrder += 1;
|
||||
}
|
||||
|
||||
export function syncTelegramLifecycleRuntimeFlags(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
flags: Partial<TelegramRuntimeLifecycleFlags>,
|
||||
): void {
|
||||
if (flags.activeTelegramToolExecutions !== undefined) {
|
||||
state.activeTelegramToolExecutions = flags.activeTelegramToolExecutions;
|
||||
}
|
||||
if (flags.telegramTurnDispatchPending !== undefined) {
|
||||
state.telegramTurnDispatchPending = flags.telegramTurnDispatchPending;
|
||||
}
|
||||
if (flags.compactionInProgress !== undefined) {
|
||||
state.compactionInProgress = flags.compactionInProgress;
|
||||
}
|
||||
if (flags.foldQueuedPromptsIntoHistory !== undefined) {
|
||||
state.foldQueuedPromptsIntoHistory = flags.foldQueuedPromptsIntoHistory;
|
||||
}
|
||||
if (flags.setupInProgress !== undefined) {
|
||||
state.setupInProgress = flags.setupInProgress;
|
||||
}
|
||||
}
|
||||
|
||||
export function getActiveTelegramToolExecutions(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
): number {
|
||||
return state.activeTelegramToolExecutions;
|
||||
}
|
||||
|
||||
export function setActiveTelegramToolExecutions(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
count: number,
|
||||
): void {
|
||||
state.activeTelegramToolExecutions = count;
|
||||
}
|
||||
|
||||
export function resetActiveTelegramToolExecutions(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
): void {
|
||||
state.activeTelegramToolExecutions = 0;
|
||||
}
|
||||
|
||||
export function hasTelegramDispatchPending(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
): boolean {
|
||||
return state.telegramTurnDispatchPending;
|
||||
}
|
||||
|
||||
function setTelegramDispatchPending(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
pending: boolean,
|
||||
): void {
|
||||
state.telegramTurnDispatchPending = pending;
|
||||
}
|
||||
|
||||
export function clearTelegramDispatchPending(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
): void {
|
||||
state.telegramTurnDispatchPending = false;
|
||||
}
|
||||
|
||||
export function isTelegramCompactionInProgress(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
): boolean {
|
||||
return state.compactionInProgress;
|
||||
}
|
||||
|
||||
export function setTelegramCompactionInProgress(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
inProgress: boolean,
|
||||
): void {
|
||||
state.compactionInProgress = inProgress;
|
||||
}
|
||||
|
||||
export function shouldFoldQueuedPromptsIntoHistory(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
): boolean {
|
||||
return state.foldQueuedPromptsIntoHistory;
|
||||
}
|
||||
|
||||
export function setFoldQueuedPromptsIntoHistory(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
fold: boolean,
|
||||
): void {
|
||||
state.foldQueuedPromptsIntoHistory = fold;
|
||||
}
|
||||
|
||||
export function isTelegramSetupInProgress(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
): boolean {
|
||||
return state.setupInProgress;
|
||||
}
|
||||
|
||||
export function startTelegramSetup(state: TelegramBridgeRuntimeState): boolean {
|
||||
if (state.setupInProgress) return false;
|
||||
state.setupInProgress = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
export function finishTelegramSetup(state: TelegramBridgeRuntimeState): void {
|
||||
state.setupInProgress = false;
|
||||
}
|
||||
|
||||
export function hasTelegramAbortHandler(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
): boolean {
|
||||
return !!state.abortHandler;
|
||||
}
|
||||
|
||||
export function setTelegramAbortHandler(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
abortHandler: () => void,
|
||||
): void {
|
||||
state.abortHandler = abortHandler;
|
||||
}
|
||||
|
||||
export function clearTelegramAbortHandler(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
): void {
|
||||
state.abortHandler = undefined;
|
||||
}
|
||||
|
||||
export function getTelegramAbortHandler(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
): (() => void) | undefined {
|
||||
return state.abortHandler;
|
||||
}
|
||||
|
||||
export function abortTelegramTurn(state: TelegramBridgeRuntimeState): boolean {
|
||||
if (!state.abortHandler) return false;
|
||||
state.abortHandler();
|
||||
return true;
|
||||
}
|
||||
|
||||
export interface TelegramTypingLoopTarget {
|
||||
chatId: number;
|
||||
threadId?: number;
|
||||
}
|
||||
|
||||
function getTelegramTypingLoopThreadParams(
|
||||
target: TelegramTypingLoopTarget | undefined,
|
||||
): { message_thread_id?: number } | undefined {
|
||||
const threadId = target?.threadId;
|
||||
return Number.isInteger(threadId)
|
||||
? { message_thread_id: threadId }
|
||||
: undefined;
|
||||
}
|
||||
|
||||
export interface TelegramTypingLoopDeps {
|
||||
chatId: number | undefined;
|
||||
target?: TelegramTypingLoopTarget;
|
||||
intervalMs: number;
|
||||
sendTypingAction: (
|
||||
chatId: number,
|
||||
options?: { message_thread_id?: number },
|
||||
) => Promise<unknown>;
|
||||
sendAggregateTypingAction?: (chatId: number) => Promise<unknown>;
|
||||
}
|
||||
|
||||
export interface TelegramRuntimeEventRecorderPort {
|
||||
recordRuntimeEvent?: (
|
||||
category: string,
|
||||
error: unknown,
|
||||
details?: Record<string, unknown>,
|
||||
) => void;
|
||||
}
|
||||
|
||||
function updateTelegramRuntimeStatusSafely<TContext>(
|
||||
updateStatus: (ctx: TContext, error?: string) => void,
|
||||
ctx: TContext,
|
||||
options: {
|
||||
error?: string;
|
||||
category: string;
|
||||
phase: string;
|
||||
recordRuntimeEvent?: TelegramRuntimeEventRecorderPort["recordRuntimeEvent"];
|
||||
},
|
||||
): void {
|
||||
try {
|
||||
updateStatus(ctx, options.error);
|
||||
} catch (statusError) {
|
||||
options.recordRuntimeEvent?.(options.category, statusError, {
|
||||
phase: options.phase,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export interface TelegramTypingLoopStarterDeps<
|
||||
TContext,
|
||||
> extends TelegramRuntimeEventRecorderPort {
|
||||
typing: TelegramRuntimeTypingPort;
|
||||
getDefaultChatId: () => number | undefined;
|
||||
sendTypingAction: (
|
||||
chatId: number,
|
||||
options?: { message_thread_id?: number },
|
||||
) => Promise<unknown>;
|
||||
sendAggregateTypingAction?: (chatId: number) => Promise<unknown>;
|
||||
updateStatus: (ctx: TContext, error?: string) => void;
|
||||
intervalMs?: number;
|
||||
}
|
||||
|
||||
export function createTelegramTypingLoopStarter<TContext>(
|
||||
deps: TelegramTypingLoopStarterDeps<TContext>,
|
||||
): (
|
||||
ctx: TContext,
|
||||
chatId?: number,
|
||||
options?: { target?: TelegramTypingLoopTarget },
|
||||
) => void {
|
||||
return (ctx, chatId, options) => {
|
||||
deps.typing.start({
|
||||
chatId: chatId ?? deps.getDefaultChatId(),
|
||||
target: options?.target,
|
||||
intervalMs: deps.intervalMs ?? TELEGRAM_TYPING_ACTION_INTERVAL_MS,
|
||||
sendTypingAction: async (targetChatId, actionOptions) => {
|
||||
try {
|
||||
await deps.sendTypingAction(targetChatId, actionOptions);
|
||||
} catch (error) {
|
||||
const message =
|
||||
error instanceof Error ? error.message : String(error);
|
||||
updateTelegramRuntimeStatusSafely(deps.updateStatus, ctx, {
|
||||
error: message,
|
||||
category: "typing",
|
||||
phase: "status-update",
|
||||
recordRuntimeEvent: deps.recordRuntimeEvent,
|
||||
});
|
||||
deps.recordRuntimeEvent?.("typing", error, {
|
||||
chatId: targetChatId,
|
||||
});
|
||||
}
|
||||
},
|
||||
sendAggregateTypingAction: deps.sendAggregateTypingAction
|
||||
? async (targetChatId) => {
|
||||
try {
|
||||
await deps.sendAggregateTypingAction?.(targetChatId);
|
||||
} catch (error) {
|
||||
const message =
|
||||
error instanceof Error ? error.message : String(error);
|
||||
updateTelegramRuntimeStatusSafely(deps.updateStatus, ctx, {
|
||||
error: message,
|
||||
category: "typing",
|
||||
phase: "status-update",
|
||||
recordRuntimeEvent: deps.recordRuntimeEvent,
|
||||
});
|
||||
deps.recordRuntimeEvent?.("typing", error, {
|
||||
chatId: targetChatId,
|
||||
aggregate: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function getTelegramTypingLoopKey(deps: TelegramTypingLoopDeps): string {
|
||||
const threadId = deps.target?.threadId;
|
||||
return `${deps.chatId ?? 0}:${Number.isInteger(threadId) ? threadId : "all"}`;
|
||||
}
|
||||
|
||||
export function startTelegramTypingLoop(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
deps: TelegramTypingLoopDeps,
|
||||
): boolean {
|
||||
if (deps.chatId === undefined || deps.chatId === 0) return false;
|
||||
const previousKey = state.typingLoopKey;
|
||||
const nextKey = getTelegramTypingLoopKey(deps);
|
||||
state.typingLoopDeps = deps;
|
||||
state.typingLoopKey = nextKey;
|
||||
const sendTyping = (): void => {
|
||||
const activeDeps = state.typingLoopDeps;
|
||||
if (!activeDeps || activeDeps.chatId === undefined || activeDeps.chatId === 0)
|
||||
return;
|
||||
const targetChatId = activeDeps.chatId;
|
||||
const threadParams = getTelegramTypingLoopThreadParams(activeDeps.target);
|
||||
const typing = Promise.resolve()
|
||||
.then(async () => {
|
||||
await activeDeps.sendTypingAction(targetChatId, threadParams);
|
||||
if (threadParams?.message_thread_id !== undefined) {
|
||||
await activeDeps.sendAggregateTypingAction?.(targetChatId);
|
||||
}
|
||||
})
|
||||
.then(() => undefined)
|
||||
.catch(() => undefined);
|
||||
state.typingInFlight = typing;
|
||||
void typing.finally(() => {
|
||||
if (state.typingInFlight === typing) state.typingInFlight = undefined;
|
||||
});
|
||||
};
|
||||
if (state.typingInterval) {
|
||||
if (previousKey === nextKey) return false;
|
||||
sendTyping();
|
||||
return true;
|
||||
}
|
||||
sendTyping();
|
||||
state.typingInterval = setInterval(sendTyping, deps.intervalMs);
|
||||
state.typingInterval.unref?.();
|
||||
return true;
|
||||
}
|
||||
|
||||
export function stopTelegramTypingLoop(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
): boolean {
|
||||
if (!state.typingInterval) return false;
|
||||
clearInterval(state.typingInterval);
|
||||
state.typingInterval = undefined;
|
||||
state.typingLoopDeps = undefined;
|
||||
state.typingLoopKey = undefined;
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function waitForTelegramTypingLoopIdle(
|
||||
state: TelegramBridgeRuntimeState,
|
||||
timeoutMs = TELEGRAM_TYPING_IDLE_DRAIN_MAX_MS,
|
||||
): Promise<void> {
|
||||
const inFlight = state.typingInFlight;
|
||||
if (!inFlight) return;
|
||||
if (timeoutMs <= 0) {
|
||||
await Promise.race([inFlight, Promise.resolve()]);
|
||||
return;
|
||||
}
|
||||
await Promise.race([
|
||||
inFlight,
|
||||
new Promise<void>((resolve) => {
|
||||
const timer = setTimeout(resolve, timeoutMs);
|
||||
timer.unref?.();
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
export function createTelegramContextAbortHandlerSetter<
|
||||
TContext extends { abort: () => void },
|
||||
>(
|
||||
abort: Pick<TelegramRuntimeAbortPort, "setHandler">,
|
||||
): (ctx: TContext) => void {
|
||||
return (ctx) => {
|
||||
abort.setHandler(() => ctx.abort());
|
||||
};
|
||||
}
|
||||
|
||||
export interface TelegramAgentEndResetDeps {
|
||||
abort: Pick<TelegramRuntimeAbortPort, "clearHandler">;
|
||||
typing: Pick<TelegramRuntimeTypingPort, "stop">;
|
||||
clearActiveTurn: () => void;
|
||||
resetToolExecutions: () => void;
|
||||
clearPendingModelSwitch: () => void;
|
||||
clearDispatchPending: () => void;
|
||||
}
|
||||
|
||||
export function createTelegramAgentEndResetter(
|
||||
deps: TelegramAgentEndResetDeps,
|
||||
): () => void {
|
||||
return () => {
|
||||
deps.abort.clearHandler();
|
||||
deps.typing.stop();
|
||||
deps.clearActiveTurn();
|
||||
deps.resetToolExecutions();
|
||||
deps.clearPendingModelSwitch();
|
||||
deps.clearDispatchPending();
|
||||
};
|
||||
}
|
||||
|
||||
export interface TelegramPromptDispatchLifecycleDeps<
|
||||
TContext,
|
||||
> extends TelegramRuntimeEventRecorderPort {
|
||||
lifecycle: Pick<
|
||||
TelegramRuntimeLifecyclePort,
|
||||
"setDispatchPending" | "clearDispatchPending"
|
||||
>;
|
||||
typing: Pick<TelegramRuntimeTypingPort, "stop">;
|
||||
startTypingLoop: (
|
||||
ctx: TContext,
|
||||
chatId?: number,
|
||||
options?: { target?: TelegramTypingLoopTarget },
|
||||
) => void;
|
||||
updateStatus: (ctx: TContext, error?: string) => void;
|
||||
}
|
||||
|
||||
export interface TelegramPromptDispatchRuntimeDeps<
|
||||
TContext,
|
||||
> extends TelegramRuntimeEventRecorderPort {
|
||||
lifecycle: TelegramPromptDispatchLifecycleDeps<TContext>["lifecycle"];
|
||||
typing: TelegramRuntimeTypingPort;
|
||||
getDefaultChatId: () => number | undefined;
|
||||
sendTypingAction: (
|
||||
chatId: number,
|
||||
options?: { message_thread_id?: number },
|
||||
) => Promise<unknown>;
|
||||
sendAggregateTypingAction?: (chatId: number) => Promise<unknown>;
|
||||
updateStatus: (ctx: TContext, error?: string) => void;
|
||||
intervalMs?: number;
|
||||
}
|
||||
|
||||
export interface TelegramPromptDispatchRuntime<TContext> {
|
||||
startTypingLoop: (
|
||||
ctx: TContext,
|
||||
chatId?: number,
|
||||
options?: { target?: TelegramTypingLoopTarget },
|
||||
) => void;
|
||||
onPromptDispatchStart: (ctx: TContext, chatId?: number) => void;
|
||||
onPromptDispatchFailure: (ctx: TContext, message: string) => void;
|
||||
}
|
||||
|
||||
export function createTelegramPromptDispatchRuntime<TContext>(
|
||||
deps: TelegramPromptDispatchRuntimeDeps<TContext>,
|
||||
): TelegramPromptDispatchRuntime<TContext> {
|
||||
const startTypingLoop = createTelegramTypingLoopStarter(deps);
|
||||
return {
|
||||
startTypingLoop,
|
||||
...createTelegramPromptDispatchLifecycle({
|
||||
lifecycle: deps.lifecycle,
|
||||
typing: deps.typing,
|
||||
startTypingLoop,
|
||||
updateStatus: deps.updateStatus,
|
||||
recordRuntimeEvent: deps.recordRuntimeEvent,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export function createTelegramPromptDispatchLifecycle<TContext>(
|
||||
deps: TelegramPromptDispatchLifecycleDeps<TContext>,
|
||||
) {
|
||||
return {
|
||||
onPromptDispatchStart: (ctx: TContext, chatId?: number): void => {
|
||||
deps.lifecycle.setDispatchPending(true);
|
||||
deps.startTypingLoop(ctx, chatId);
|
||||
updateTelegramRuntimeStatusSafely(deps.updateStatus, ctx, {
|
||||
category: "dispatch",
|
||||
phase: "status-update",
|
||||
recordRuntimeEvent: deps.recordRuntimeEvent,
|
||||
});
|
||||
},
|
||||
onPromptDispatchFailure: (ctx: TContext, message: string): void => {
|
||||
deps.lifecycle.clearDispatchPending();
|
||||
deps.typing.stop();
|
||||
deps.recordRuntimeEvent?.("dispatch", new Error(message));
|
||||
updateTelegramRuntimeStatusSafely(deps.updateStatus, ctx, {
|
||||
error: `dispatch failed: ${message}`,
|
||||
category: "dispatch",
|
||||
phase: "status-update",
|
||||
recordRuntimeEvent: deps.recordRuntimeEvent,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user