@copilotkit/runtime
Version:
<img src="https://github.com/user-attachments/assets/0a6b64d9-e193-4940-a3f6-60334ac34084" alt="banner" style="border-radius: 12px; border: 2px solid #d6d4fa;" />
295 lines (293 loc) • 10.8 kB
JavaScript
require("reflect-metadata");
const require_runtime = require('../../../_virtual/_rolldown/runtime.cjs');
const require_agent_runner = require('./agent-runner.cjs');
let _copilotkit_shared = require("@copilotkit/shared");
let rxjs = require("rxjs");
let _ag_ui_client = require("@ag-ui/client");
//#region src/v2/runtime/runner/in-memory.ts
var InMemoryEventStore = class {
constructor(threadId) {
this.threadId = threadId;
this.subject = null;
this.isRunning = false;
this.currentRunId = null;
this.historicRuns = [];
this.agent = null;
this.runSubject = null;
this.stopRequested = false;
this.currentEvents = null;
}
};
const GLOBAL_STORE = /* @__PURE__ */ new Map();
var InMemoryAgentRunner = class extends require_agent_runner.AgentRunner {
run(request) {
let existingStore = GLOBAL_STORE.get(request.threadId);
if (!existingStore) {
existingStore = new InMemoryEventStore(request.threadId);
GLOBAL_STORE.set(request.threadId, existingStore);
}
const store = existingStore;
if (store.isRunning) throw new Error("Thread already running");
store.isRunning = true;
store.currentRunId = request.input.runId;
store.agent = request.agent;
store.stopRequested = false;
const seenMessageIds = /* @__PURE__ */ new Set();
const currentRunEvents = [];
store.currentEvents = currentRunEvents;
const historicMessageIds = /* @__PURE__ */ new Set();
for (const run of store.historicRuns) for (const event of run.events) {
if ("messageId" in event && typeof event.messageId === "string") historicMessageIds.add(event.messageId);
if (event.type === _ag_ui_client.EventType.RUN_STARTED) {
const messages = event.input?.messages ?? [];
for (const message of messages) historicMessageIds.add(message.id);
}
}
const nextSubject = new rxjs.ReplaySubject(Infinity);
const prevSubject = store.subject;
store.subject = nextSubject;
const runSubject = new rxjs.ReplaySubject(Infinity);
store.runSubject = runSubject;
const runAgent = async () => {
const parentRunId = store.historicRuns[store.historicRuns.length - 1]?.runId ?? null;
try {
await request.agent.runAgent(request.input, {
onEvent: ({ event }) => {
let processedEvent = event;
if (event.type === _ag_ui_client.EventType.RUN_STARTED) {
const runStartedEvent = event;
if (!runStartedEvent.input) {
const sanitizedMessages = request.input.messages ? request.input.messages.filter((message) => !historicMessageIds.has(message.id)) : void 0;
const updatedInput = {
...request.input,
...sanitizedMessages !== void 0 ? { messages: sanitizedMessages } : {}
};
processedEvent = {
...runStartedEvent,
input: updatedInput
};
}
}
runSubject.next(processedEvent);
nextSubject.next(processedEvent);
currentRunEvents.push(processedEvent);
},
onNewMessage: ({ message }) => {
if (!seenMessageIds.has(message.id)) seenMessageIds.add(message.id);
},
onRunStartedEvent: () => {
if (request.input.messages) {
for (const message of request.input.messages) if (!seenMessageIds.has(message.id)) seenMessageIds.add(message.id);
}
}
});
const appendedEvents = (0, _copilotkit_shared.finalizeRunEvents)(currentRunEvents, { stopRequested: store.stopRequested });
for (const event of appendedEvents) {
runSubject.next(event);
nextSubject.next(event);
}
if (store.currentRunId) {
const compactedEvents = (0, _ag_ui_client.compactEvents)(currentRunEvents);
store.historicRuns.push({
threadId: request.threadId,
runId: store.currentRunId,
agentId: request.agent.agentId ?? "default",
parentRunId,
events: compactedEvents,
messages: Array.isArray(request.agent.messages) ? [...request.agent.messages] : [],
createdAt: Date.now()
});
}
store.currentEvents = null;
store.currentRunId = null;
store.agent = null;
store.runSubject = null;
store.stopRequested = false;
store.isRunning = false;
runSubject.complete();
nextSubject.complete();
} catch (error) {
const interruptionMessage = error instanceof Error ? error.message : String(error);
const appendedEvents = (0, _copilotkit_shared.finalizeRunEvents)(currentRunEvents, {
stopRequested: store.stopRequested,
interruptionMessage
});
for (const event of appendedEvents) {
runSubject.next(event);
nextSubject.next(event);
}
if (store.currentRunId && currentRunEvents.length > 0) {
const compactedEvents = (0, _ag_ui_client.compactEvents)(currentRunEvents);
store.historicRuns.push({
threadId: request.threadId,
runId: store.currentRunId,
agentId: request.agent.agentId ?? "default",
parentRunId,
events: compactedEvents,
messages: Array.isArray(request.agent.messages) ? [...request.agent.messages] : [],
createdAt: Date.now()
});
}
store.currentEvents = null;
store.currentRunId = null;
store.agent = null;
store.runSubject = null;
store.stopRequested = false;
store.isRunning = false;
runSubject.complete();
nextSubject.complete();
}
};
if (prevSubject) prevSubject.subscribe({
next: (e) => nextSubject.next(e),
error: (err) => nextSubject.error(err),
complete: () => {}
});
runAgent();
return runSubject.asObservable();
}
connect(request) {
const store = GLOBAL_STORE.get(request.threadId);
const connectionSubject = new rxjs.ReplaySubject(Infinity);
if (!store) {
connectionSubject.complete();
return connectionSubject.asObservable();
}
const allHistoricEvents = [];
for (const run of store.historicRuns) allHistoricEvents.push(...run.events);
const compactedEvents = (0, _ag_ui_client.compactEvents)(allHistoricEvents);
const emittedMessageIds = /* @__PURE__ */ new Set();
for (const event of compactedEvents) {
connectionSubject.next(event);
if ("messageId" in event && typeof event.messageId === "string") emittedMessageIds.add(event.messageId);
}
if (store.subject && (store.isRunning || store.stopRequested)) store.subject.subscribe({
next: (event) => {
if ("messageId" in event && typeof event.messageId === "string" && emittedMessageIds.has(event.messageId)) return;
connectionSubject.next(event);
},
complete: () => connectionSubject.complete(),
error: (err) => connectionSubject.error(err)
});
else connectionSubject.complete();
return connectionSubject.asObservable();
}
isRunning(request) {
const store = GLOBAL_STORE.get(request.threadId);
return Promise.resolve(store?.isRunning ?? false);
}
stop(request) {
const store = GLOBAL_STORE.get(request.threadId);
if (!store || !store.isRunning) return Promise.resolve(false);
if (store.stopRequested) return Promise.resolve(false);
store.stopRequested = true;
store.isRunning = false;
const agent = store.agent;
if (!agent) {
store.stopRequested = false;
store.isRunning = false;
return Promise.resolve(false);
}
try {
agent.abortRun();
return Promise.resolve(true);
} catch (error) {
console.error("Failed to abort agent run", error);
store.stopRequested = false;
store.isRunning = true;
return Promise.resolve(false);
}
}
/**
* Returns a summary of every thread that has been run through this runner.
*
* This powers the local-dev fallback for `GET /threads` when the Intelligence
* platform is not configured. Each entry mirrors the shape of a platform
* `ThreadRecord` so the HTTP handler can use the same response envelope.
*/
listThreads() {
const threads = [];
for (const [threadId, store] of GLOBAL_STORE) {
if (store.historicRuns.length === 0) continue;
const firstRun = store.historicRuns[0];
const lastRun = store.historicRuns[store.historicRuns.length - 1];
threads.push({
id: threadId,
name: null,
agentId: lastRun.agentId,
organizationId: "",
createdById: "",
archived: false,
createdAt: new Date(firstRun.createdAt).toISOString(),
updatedAt: new Date(lastRun.createdAt).toISOString()
});
}
return threads.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime());
}
/**
* Returns all messages for a thread, using the snapshot captured at the end
* of the most recent run.
*
* This powers the local-dev fallback for `GET /threads/:threadId/messages`
* when the Intelligence platform is not configured. The returned `Message[]`
* objects come directly from the ag-ui agent, so their shape is compatible
* with the Intelligence platform's `ThreadMessage` type.
*/
getThreadMessages(threadId) {
const store = GLOBAL_STORE.get(threadId);
if (!store || store.historicRuns.length === 0) return [];
return store.historicRuns[store.historicRuns.length - 1].messages;
}
/**
* Returns all AG-UI events for a thread, compacted across historic runs.
*
* Powers the local-dev fallback for `GET /threads/:threadId/events` when the
* Intelligence platform is not configured. The compaction logic matches
* the connection-replay path in {@link connect}, so the stream a
* late-joining inspector sees matches what this method returns.
*/
getThreadEvents(threadId) {
const store = GLOBAL_STORE.get(threadId);
if (!store || store.historicRuns.length === 0) return [];
const all = [];
for (const run of store.historicRuns) all.push(...run.events);
return (0, _ag_ui_client.compactEvents)(all);
}
/**
* Returns the agent state snapshot for a thread.
*
* Derived from the last `STATE_SNAPSHOT` in the compacted event stream. The
* AG-UI `compactEvents` helper consolidates STATE_DELTA events and produces
* a single trailing STATE_SNAPSHOT when state changes exist, so this is a
* faithful view of state at the end of the most recent run.
*
* Returns `null` when the thread has never emitted a STATE_SNAPSHOT.
*/
getThreadState(threadId) {
const events = this.getThreadEvents(threadId);
for (let i = events.length - 1; i >= 0; i--) {
const event = events[i];
if (event.type === _ag_ui_client.EventType.STATE_SNAPSHOT) {
const snapshot = event.snapshot;
if (snapshot && typeof snapshot === "object") return snapshot;
return null;
}
}
return null;
}
/**
* Clears all in-memory thread history.
*
* Powers the local-dev fallback for `POST /threads/clear`, letting consumers
* (e.g. the demo's Clear button) reset to an empty thread list without
* restarting the runtime. Intentionally not exposed on the Intelligence
* platform path: there, thread history lives in a real database and must
* not be wiped this way.
*/
clearThreads() {
GLOBAL_STORE.clear();
}
};
//#endregion
exports.InMemoryAgentRunner = InMemoryAgentRunner;
//# sourceMappingURL=in-memory.cjs.map