SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
4e23b01 verified
Raw
History Blame Contribute Delete
11.6 kB
import {
IAgentLifecycleService,
IAgentConversationUndoParticipantRegistry,
IAgentLoopService,
IAgentScopeContext,
IAgentTaskService,
IEventBus,
INTERACTION_TAG_AGENT_ID,
INTERACTION_TAG_SESSION_ID,
ISessionMetadata,
MAIN_AGENT_ID,
interactions,
toDisposable,
type AgentMeta,
type IDisposable,
type IAgentScopeHandle,
type Interaction,
type ISessionScopeHandle,
} from '@moonshot-ai/agent-core-v2';
import type { AgentDescriptor, TranscriptChangeEvent, TranscriptStore } from '@moonshot-ai/transcript';
import { legacyApprovalsOf } from '../legacyStatus/legacyActivity';
import {
AgentTranscriptProjector,
type ProjectorBusEvent,
type ProjectorInteraction,
} from './coreEventMap';
export interface TranscriptBindingLogger {
warn(obj: unknown, msg: string): void;
}
export interface TranscriptBinding extends IDisposable {
seedPendingInteractions(agentId?: string): void;
}
export function bindSessionTranscript(
store: TranscriptStore,
session: ISessionScopeHandle,
logger?: TranscriptBindingLogger,
onOps?: (event: TranscriptChangeEvent) => void,
reconcileAfterUndo?: (agentId: string) => Promise<void>,
): TranscriptBinding {
const agents = session.accessor.get(IAgentLifecycleService);
const pendingInteractions = (): readonly Interaction[] =>
interactions.findAll({
resolved: false,
tags: { [INTERACTION_TAG_SESSION_ID]: session.id },
});
const disposables: IDisposable[] = [];
const agentDisposables = new Map<string, IDisposable[]>();
const subscribedAgents = new Set<string>();
const projectors = new Map<string, AgentTranscriptProjector>();
const interactionAgents = new Map<string, string>();
const knownInteractions = new Set<string>();
const unseeded = new Map<string, Interaction>();
const earlyResolves = new Map<string, { agentId: string; response: unknown }>();
const seededAgents = new Set<string>();
let seededAll = false;
const isSeeded = (agentId: string): boolean => seededAll || seededAgents.has(agentId);
const applyOps = (agentId: string, ops: ReturnType<AgentTranscriptProjector['map']>): void => {
if (ops.length === 0) return;
const result = store.ensureAgent(agentId).apply(ops);
if (result.gap !== undefined) {
logger?.warn(
{ sessionId: store.sessionId, agentId, gap: result.gap },
'transcript: append gap — producer/consumer skew',
);
return;
}
onOps?.({ agentId, ops });
};
const projectorFor = (agentId: string): AgentTranscriptProjector => {
let projector = projectors.get(agentId);
if (projector === undefined) {
projector = new AgentTranscriptProjector(agentId, store.sessionId, {
stepFrames: (turnId, stepId) =>
store.getAgent(agentId)?.getTurn(turnId)?.steps.find((s) => s.stepId === stepId)?.frames,
toolFrame: (toolCallId) => {
const transcript = store.getAgent(agentId);
if (transcript === undefined) return undefined;
for (const item of transcript.getItems()) {
if (item.kind !== 'turn') continue;
for (const step of item.steps) {
for (const frame of step.frames) {
if (frame.kind === 'tool' && frame.toolCallId === toolCallId) {
return { turnId: item.turnId, stepId: step.stepId, frame };
}
}
}
}
return undefined;
},
stepOrdinal: (turnId) => {
const agentHandle = agents.handleOf(agentId);
if (agentHandle === undefined) return undefined;
const turn = agentHandle.accessor.get(IAgentLoopService)?.snapshot().turn;
return turn === undefined || `t${turn.turnId}` !== turnId ? undefined : turn.step;
},
activitySnapshot: () =>
agents.handleOf(agentId)?.accessor.get(IAgentLoopService)?.snapshot() ?? {},
pendingApprovals: () => {
const agentHandle = agents.handleOf(agentId);
return agentHandle === undefined ? [] : legacyApprovalsOf(agentHandle);
},
turn: (turnId) => store.getAgent(agentId)?.getTurn(turnId),
resolvePlanRevisionKey: (key) =>
agents.handleOf(agentId)?.accessor.get(IAgentScopeContext).scope(key) ?? key,
});
const agentHandle = agents.handleOf(agentId);
if (agentHandle !== undefined) {
const tasks = agentHandle.accessor.get(IAgentTaskService)?.list() ?? [];
for (const info of tasks) {
if (info.kind === 'agent' && typeof info.agentId === 'string' && info.agentId.length > 0) {
applyOps(
agentId,
projector.seedSubagentTask({
taskId: info.taskId,
agentId: info.agentId,
description: info.description,
status: info.status,
detached: info.detached ?? false,
startedAt: info.startedAt,
}),
);
}
}
}
projectors.set(agentId, projector);
}
return projector;
};
const subscribeAgent = (handle: IAgentScopeHandle): void => {
if (subscribedAgents.has(handle.id)) return;
subscribedAgents.add(handle.id);
const projector = projectorFor(handle.id);
store.ensureAgent(handle.id, { agentId: handle.id });
const bus = handle.accessor.get(IEventBus);
const busD = bus.subscribe((event) =>
applyOps(handle.id, projectorFor(handle.id).map(event as ProjectorBusEvent)),
);
const loopStatus = handle.accessor.get(IAgentLoopService)?.snapshot();
if (loopStatus?.state === 'running' && loopStatus.activeTurnId !== undefined) {
const promptId = loopStatus.activePromptId;
projector.seedActiveTurn({ turnId: loopStatus.activeTurnId, promptId });
}
const list = agentDisposables.get(handle.id) ?? [];
list.push(busD);
if (reconcileAfterUndo !== undefined) {
list.push(handle.accessor.get(IAgentConversationUndoParticipantRegistry).register({
id: 'transcript',
phase: 'after-flush',
reconcileAfterUndo: async () => {
await reconcileAfterUndo(handle.id);
projectors.delete(handle.id);
},
}));
}
agentDisposables.set(handle.id, list);
};
const interactionAgentId = (interaction: Interaction): string => {
const payloadAgent = (interaction.payload as { agentId?: unknown }).agentId;
const tag = interaction.tags[INTERACTION_TAG_AGENT_ID];
return (
(typeof tag === 'string' ? tag : undefined) ??
(typeof payloadAgent === 'string' ? payloadAgent : undefined) ??
MAIN_AGENT_ID
);
};
const announceInteraction = (interaction: Interaction): void => {
if (interaction.kind !== 'approval' && interaction.kind !== 'question') return;
const agentId = interactionAgentId(interaction);
interactionAgents.set(interaction.id, agentId);
const request: ProjectorInteraction = {
id: interaction.id,
kind: interaction.kind,
payload: interaction.payload,
createdAt: interaction.createdAt,
};
applyOps(agentId, projectorFor(agentId).mapInteractionRequested(request));
};
const refreshDescriptors = (): void => {
void session.accessor
.get(ISessionMetadata)
.read()
.then((meta) => {
for (const agentId of projectors.keys()) {
store.describeAgent(descriptorFromMeta(agentId, meta.agents?.[agentId]));
}
})
.catch(() => {
});
};
for (const agent of agents.list()) {
const handle = agents.handleOf(agent.agentId);
if (handle !== undefined) subscribeAgent(handle);
}
disposables.push(
agents.onDidCreate((context) => {
const handle = agents.handleOf(context.agentId);
if (handle !== undefined) subscribeAgent(handle);
seededAgents.add(context.agentId);
refreshDescriptors();
}),
agents.onDidClose((context) => {
const agentId = context.agentId;
for (const d of agentDisposables.get(agentId) ?? []) d.dispose();
agentDisposables.delete(agentId);
subscribedAgents.delete(agentId);
projectors.delete(agentId);
store.markDisposed(agentId, new Date().toISOString());
}),
);
for (const pending of pendingInteractions()) {
if (pending.kind !== 'approval' && pending.kind !== 'question') continue;
if (knownInteractions.has(pending.id)) continue;
knownInteractions.add(pending.id);
interactionAgents.set(pending.id, interactionAgentId(pending));
unseeded.set(pending.id, pending);
}
const seedPendingInteractions = (agentId?: string): void => {
if (agentId === undefined) seededAll = true;
else seededAgents.add(agentId);
for (const [id, interaction] of unseeded) {
if (agentId !== undefined && interactionAgents.get(id) !== agentId) continue;
unseeded.delete(id);
announceInteraction(interaction);
const early = earlyResolves.get(id);
if (early === undefined) continue;
interactionAgents.delete(id);
earlyResolves.delete(id);
const projector = projectors.get(early.agentId);
if (projector !== undefined) {
applyOps(early.agentId, projector.mapInteractionResolved(id, early.response));
}
}
for (const pending of pendingInteractions()) {
if (knownInteractions.has(pending.id)) continue;
if (agentId !== undefined && interactionAgentId(pending) !== agentId) continue;
knownInteractions.add(pending.id);
announceInteraction(pending);
}
};
disposables.push(
toDisposable(
interactions.onDidChangePending(() => {
for (const pending of pendingInteractions()) {
if (knownInteractions.has(pending.id)) continue;
const agentId = interactionAgentId(pending);
knownInteractions.add(pending.id);
if (!isSeeded(agentId)) {
interactionAgents.set(pending.id, agentId);
unseeded.set(pending.id, pending);
continue;
}
announceInteraction(pending);
}
}),
),
toDisposable(
interactions.onDidResolve(({ id, response }) => {
knownInteractions.delete(id);
const agentId = interactionAgents.get(id);
if (agentId === undefined) return;
interactionAgents.delete(id);
if (unseeded.has(id)) {
earlyResolves.set(id, { agentId, response });
return;
}
const projector = projectors.get(agentId);
if (projector === undefined) return;
applyOps(agentId, projector.mapInteractionResolved(id, response));
}),
),
);
refreshDescriptors();
return {
seedPendingInteractions,
dispose: () => {
for (const d of disposables) d.dispose();
for (const list of agentDisposables.values()) {
for (const d of list) d.dispose();
}
agentDisposables.clear();
projectors.clear();
interactionAgents.clear();
knownInteractions.clear();
unseeded.clear();
earlyResolves.clear();
},
};
}
export function descriptorFromMeta(agentId: string, meta: AgentMeta | undefined): AgentDescriptor {
const parentFromLabels = meta?.labels?.['parentAgentId'];
const swarmItem = meta?.labels?.['swarmItem'] ?? meta?.swarmItem;
return {
agentId,
type: meta?.type ?? (agentId === MAIN_AGENT_ID ? 'main' : 'sub'),
parentAgentId:
parentFromLabels !== undefined && parentFromLabels.length > 0
? parentFromLabels
: (meta?.parentAgentId ?? undefined),
label: swarmItem !== undefined && swarmItem.length > 0 ? swarmItem : undefined,
};
}