mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-10 18:35:17 +00:00
* fix(subscriptions): preserve older-host workflows and explicit timeline recovery Keep established app and SDK workflows available on older daemons without emulating independent directory filters. Restore live timeline membership without choosing the consumer's history recovery policy. Preserve daemon filesystem paths separately from legacy workspace IDs. * fix(client): keep legacy workspace replies scoped to the requested page
76 lines
2.3 KiB
TypeScript
76 lines
2.3 KiB
TypeScript
import type { SessionOutboundMessage } from "@getpaseo/protocol/messages";
|
|
import type { OwnedSubscription, TimelineSubscription } from "../connection/index.js";
|
|
|
|
type TimelineUpdate = Extract<
|
|
SessionOutboundMessage,
|
|
{ type: "agent_stream" | "agent.timeline.replacement" }
|
|
>;
|
|
|
|
/** Local SDK lifecycle messages; these are not additional wire RPCs. */
|
|
export type TimelineMessage =
|
|
| TimelineUpdate
|
|
| {
|
|
type: "agent.timeline.subscription_restored";
|
|
payload: { agentId: string; subscriptionId: string };
|
|
}
|
|
| { type: "agent.timeline.error"; payload: { agentId: string; error: string } };
|
|
|
|
/** Restores live delivery; the consumer owns history reads and gap recovery. */
|
|
export function subscribeTimeline(
|
|
agentId: string,
|
|
observation: OwnedSubscription<{ agentIds: string[] }>,
|
|
handler: (message: TimelineMessage) => void,
|
|
reportError: (error: unknown) => void,
|
|
): TimelineSubscription {
|
|
let established = false;
|
|
let released = false;
|
|
const notify = (message: TimelineMessage) => {
|
|
if (released) return;
|
|
try {
|
|
handler(message);
|
|
} catch (error) {
|
|
reportError(error);
|
|
}
|
|
};
|
|
const release = () => {
|
|
released = true;
|
|
return observation.release();
|
|
};
|
|
const stop = () => {
|
|
// Use the public release entry point so the owning API also forgets this handle.
|
|
void subscription.release().catch(reportError);
|
|
};
|
|
const fail = (error: unknown) => {
|
|
notify({
|
|
type: "agent.timeline.error",
|
|
payload: { agentId, error: error instanceof Error ? error.message : String(error) },
|
|
});
|
|
stop();
|
|
};
|
|
observation.subscribe({
|
|
snapshot: ({ subscriptionId }) => {
|
|
if (!established) {
|
|
established = true;
|
|
return;
|
|
}
|
|
notify({
|
|
type: "agent.timeline.subscription_restored",
|
|
payload: { agentId, subscriptionId },
|
|
});
|
|
},
|
|
update: (message) => {
|
|
if (message.type === "agent_stream" || message.type === "agent.timeline.replacement")
|
|
notify(message);
|
|
},
|
|
error: fail,
|
|
});
|
|
const subscription: TimelineSubscription = Object.assign(stop, {
|
|
ready: observation.ready.then(() => undefined),
|
|
release,
|
|
subscriptionId: null,
|
|
});
|
|
Object.defineProperty(subscription, "subscriptionId", { get: () => observation.subscriptionId });
|
|
void subscription.ready.catch(fail);
|
|
return subscription;
|
|
}
|