feat(tess): wire durable interaction surfaces (#732)
Some checks failed
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/publish Pipeline was canceled

This commit was merged in pull request #732.
This commit is contained in:
2026-07-13 10:05:29 +00:00
parent 84d884b932
commit 0b621660c8
16 changed files with 782 additions and 58 deletions

View File

@@ -406,6 +406,32 @@ export async function fetchInteractionTree(
return handleResponse<unknown[]>(res, 'Failed to get interaction session tree');
}
export async function enrollInteractionSession(
gatewayUrl: string,
sessionCookie: string,
request: InteractionRequest & {
sessionId: string;
providerId: string;
runtimeSessionId: string;
},
): Promise<{ status: string; sessionId: string }> {
const res = await fetch(
`${gatewayUrl}${interactionPath(request.agentName, `/sessions/${encodeURIComponent(request.sessionId)}/enroll`)}`,
{
method: 'POST',
headers: interactionHeaders(sessionCookie, gatewayUrl, request.correlationId),
body: JSON.stringify({
providerId: request.providerId,
runtimeSessionId: request.runtimeSessionId,
}),
},
);
return handleResponse<{ status: string; sessionId: string }>(
res,
'Failed to enroll interaction session',
);
}
export async function attachInteractionSession(
gatewayUrl: string,
sessionCookie: string,
@@ -422,6 +448,49 @@ export async function attachInteractionSession(
return handleResponse<unknown>(res, 'Failed to attach interaction session');
}
export async function* streamInteractionSession(
gatewayUrl: string,
sessionCookie: string,
request: InteractionRequest & { sessionId: string; cursor?: string },
): AsyncIterable<unknown> {
const params = request.cursor?.trim()
? `?${new URLSearchParams({ cursor: request.cursor.trim() }).toString()}`
: '';
const res = await fetch(
`${gatewayUrl}${interactionPath(request.agentName, `/sessions/${encodeURIComponent(request.sessionId)}/stream${params}`)}`,
{ headers: interactionHeaders(sessionCookie, gatewayUrl, request.correlationId) },
);
if (!res.ok || !res.body) {
const body = await res.text().catch(() => '');
throw new Error(`Failed to stream interaction session (${res.status}): ${body}`);
}
const reader = res.body.getReader();
const decoder = new TextDecoder();
let pending = '';
try {
for (;;) {
const chunk = await reader.read();
if (chunk.done) break;
pending += decoder.decode(chunk.value, { stream: true });
for (;;) {
const separator = pending.indexOf('\n\n');
if (separator < 0) break;
const frame = pending.slice(0, separator);
pending = pending.slice(separator + 2);
const data = frame
.split('\n')
.find((line: string): boolean => line.startsWith('data:'))
?.slice('data:'.length)
.trim();
if (data) yield JSON.parse(data) as unknown;
}
}
} finally {
reader.releaseLock();
}
}
export async function sendInteractionMessage(
gatewayUrl: string,
sessionCookie: string,