@astralform/js 4.3.0 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +92 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -21
- package/dist/index.d.ts +7 -21
- package/dist/index.js +92 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -420,6 +420,12 @@ type ChatEvent = {
|
|
|
420
420
|
type: "user_message";
|
|
421
421
|
content: string;
|
|
422
422
|
createdAt?: number;
|
|
423
|
+
id?: string;
|
|
424
|
+
/** This message was steered into a run already in flight — it started
|
|
425
|
+
* no turn of its own. Consumers that badge a steer (a "waiting to be
|
|
426
|
+
* read" notice) key off this; without it a replayed steer is
|
|
427
|
+
* indistinguishable from an ordinary prompt. */
|
|
428
|
+
steer?: boolean;
|
|
423
429
|
} | {
|
|
424
430
|
type: "title_generated";
|
|
425
431
|
title: string;
|
|
@@ -1139,7 +1145,7 @@ declare class ChatSession {
|
|
|
1139
1145
|
* ``message_start`` in the stream (e.g. ``memory_recall`` from prompt prep),
|
|
1140
1146
|
* so leading with the prompt keeps the turn in order.
|
|
1141
1147
|
*/
|
|
1142
|
-
replayTurn(id: string, events: ConversationEvent[], userMessageContent?: string): void;
|
|
1148
|
+
replayTurn(id: string, events: ConversationEvent[], userMessageContent?: string, userMessageId?: string, isSteer?: boolean): void;
|
|
1143
1149
|
/**
|
|
1144
1150
|
* Load a conversation's messages and replay its persisted history.
|
|
1145
1151
|
*
|
|
@@ -1236,26 +1242,6 @@ declare function generateId(): string;
|
|
|
1236
1242
|
*/
|
|
1237
1243
|
declare function streamJobSSE(options: StreamJobSSEOptions): AsyncGenerator<ChatStreamEvent>;
|
|
1238
1244
|
|
|
1239
|
-
/**
|
|
1240
|
-
* StreamManager — high-level conversation lifecycle coordinator.
|
|
1241
|
-
*
|
|
1242
|
-
* Sits on top of ChatSession and manages the state machine for
|
|
1243
|
-
* multi-conversation SSE streaming. Framework-agnostic: emits
|
|
1244
|
-
* typed events to registered handlers. Block construction is NOT
|
|
1245
|
-
* the SDK's concern — consumers build their own block tree from
|
|
1246
|
-
* the forwarded ``ChatEvent`` instances.
|
|
1247
|
-
*
|
|
1248
|
-
* import { ChatSession, StreamManager } from "@astralform/js";
|
|
1249
|
-
* const session = new ChatSession({ ... });
|
|
1250
|
-
* const manager = new StreamManager(session);
|
|
1251
|
-
* manager.on((event) => {
|
|
1252
|
-
* if (event.type === "event") {
|
|
1253
|
-
* // event.event is a typed ChatEvent — dispatch to your reducer
|
|
1254
|
-
* }
|
|
1255
|
-
* });
|
|
1256
|
-
* await manager.send("Hello");
|
|
1257
|
-
*/
|
|
1258
|
-
|
|
1259
1245
|
type StreamState = "idle" | "streaming" | "restoring" | "detached";
|
|
1260
1246
|
interface SendOptions extends ModelChoiceOptions {
|
|
1261
1247
|
agentName?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -420,6 +420,12 @@ type ChatEvent = {
|
|
|
420
420
|
type: "user_message";
|
|
421
421
|
content: string;
|
|
422
422
|
createdAt?: number;
|
|
423
|
+
id?: string;
|
|
424
|
+
/** This message was steered into a run already in flight — it started
|
|
425
|
+
* no turn of its own. Consumers that badge a steer (a "waiting to be
|
|
426
|
+
* read" notice) key off this; without it a replayed steer is
|
|
427
|
+
* indistinguishable from an ordinary prompt. */
|
|
428
|
+
steer?: boolean;
|
|
423
429
|
} | {
|
|
424
430
|
type: "title_generated";
|
|
425
431
|
title: string;
|
|
@@ -1139,7 +1145,7 @@ declare class ChatSession {
|
|
|
1139
1145
|
* ``message_start`` in the stream (e.g. ``memory_recall`` from prompt prep),
|
|
1140
1146
|
* so leading with the prompt keeps the turn in order.
|
|
1141
1147
|
*/
|
|
1142
|
-
replayTurn(id: string, events: ConversationEvent[], userMessageContent?: string): void;
|
|
1148
|
+
replayTurn(id: string, events: ConversationEvent[], userMessageContent?: string, userMessageId?: string, isSteer?: boolean): void;
|
|
1143
1149
|
/**
|
|
1144
1150
|
* Load a conversation's messages and replay its persisted history.
|
|
1145
1151
|
*
|
|
@@ -1236,26 +1242,6 @@ declare function generateId(): string;
|
|
|
1236
1242
|
*/
|
|
1237
1243
|
declare function streamJobSSE(options: StreamJobSSEOptions): AsyncGenerator<ChatStreamEvent>;
|
|
1238
1244
|
|
|
1239
|
-
/**
|
|
1240
|
-
* StreamManager — high-level conversation lifecycle coordinator.
|
|
1241
|
-
*
|
|
1242
|
-
* Sits on top of ChatSession and manages the state machine for
|
|
1243
|
-
* multi-conversation SSE streaming. Framework-agnostic: emits
|
|
1244
|
-
* typed events to registered handlers. Block construction is NOT
|
|
1245
|
-
* the SDK's concern — consumers build their own block tree from
|
|
1246
|
-
* the forwarded ``ChatEvent`` instances.
|
|
1247
|
-
*
|
|
1248
|
-
* import { ChatSession, StreamManager } from "@astralform/js";
|
|
1249
|
-
* const session = new ChatSession({ ... });
|
|
1250
|
-
* const manager = new StreamManager(session);
|
|
1251
|
-
* manager.on((event) => {
|
|
1252
|
-
* if (event.type === "event") {
|
|
1253
|
-
* // event.event is a typed ChatEvent — dispatch to your reducer
|
|
1254
|
-
* }
|
|
1255
|
-
* });
|
|
1256
|
-
* await manager.send("Hello");
|
|
1257
|
-
*/
|
|
1258
|
-
|
|
1259
1245
|
type StreamState = "idle" | "streaming" | "restoring" | "detached";
|
|
1260
1246
|
interface SendOptions extends ModelChoiceOptions {
|
|
1261
1247
|
agentName?: string;
|
package/dist/index.js
CHANGED
|
@@ -1773,11 +1773,16 @@ var ChatSession = class {
|
|
|
1773
1773
|
* ``message_start`` in the stream (e.g. ``memory_recall`` from prompt prep),
|
|
1774
1774
|
* so leading with the prompt keeps the turn in order.
|
|
1775
1775
|
*/
|
|
1776
|
-
replayTurn(id, events, userMessageContent) {
|
|
1776
|
+
replayTurn(id, events, userMessageContent, userMessageId, isSteer = false) {
|
|
1777
1777
|
this.conversationId = id;
|
|
1778
1778
|
this.resetStreamingState();
|
|
1779
1779
|
if (userMessageContent) {
|
|
1780
|
-
this.emit({
|
|
1780
|
+
this.emit({
|
|
1781
|
+
type: "user_message",
|
|
1782
|
+
content: userMessageContent,
|
|
1783
|
+
...userMessageId ? { id: userMessageId } : {},
|
|
1784
|
+
...isSteer ? { steer: true } : {}
|
|
1785
|
+
});
|
|
1781
1786
|
}
|
|
1782
1787
|
for (const ev of events) {
|
|
1783
1788
|
const type = ev.data.type || ev.event;
|
|
@@ -1890,6 +1895,64 @@ var ChatSession = class {
|
|
|
1890
1895
|
}
|
|
1891
1896
|
};
|
|
1892
1897
|
|
|
1898
|
+
// src/restore-plan.ts
|
|
1899
|
+
function planRestore(args) {
|
|
1900
|
+
const { completedJobs, userMessages } = args;
|
|
1901
|
+
const byId = /* @__PURE__ */ new Map();
|
|
1902
|
+
userMessages.forEach((m, i) => {
|
|
1903
|
+
if (m.id) byId.set(m.id, i);
|
|
1904
|
+
});
|
|
1905
|
+
const linkOf = (j) => j.message_id ? byId.get(j.message_id) : void 0;
|
|
1906
|
+
const positional = (jobs, msgs) => jobs.map((job, i) => ({
|
|
1907
|
+
kind: "turn",
|
|
1908
|
+
jobId: job.job_id,
|
|
1909
|
+
content: msgs[i]?.content,
|
|
1910
|
+
messageId: msgs[i]?.id
|
|
1911
|
+
}));
|
|
1912
|
+
const firstLinked = completedJobs.findIndex((j) => linkOf(j) !== void 0);
|
|
1913
|
+
if (firstLinked === -1) {
|
|
1914
|
+
return positional(completedJobs, userMessages);
|
|
1915
|
+
}
|
|
1916
|
+
const cutover = linkOf(completedJobs[firstLinked]);
|
|
1917
|
+
const steps = positional(
|
|
1918
|
+
completedJobs.slice(0, firstLinked),
|
|
1919
|
+
userMessages.slice(0, cutover)
|
|
1920
|
+
);
|
|
1921
|
+
let cursor = cutover;
|
|
1922
|
+
const isSteer = (m) => !!m?.id && !completedJobs.some((j) => j.message_id === m.id);
|
|
1923
|
+
const drainTo = (stopAt) => {
|
|
1924
|
+
while (cursor < stopAt) {
|
|
1925
|
+
const m = userMessages[cursor++];
|
|
1926
|
+
if (isSteer(m)) {
|
|
1927
|
+
steps.push({ kind: "steer", content: m.content, messageId: m.id });
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
cursor = stopAt + 1;
|
|
1931
|
+
};
|
|
1932
|
+
for (const job of completedJobs.slice(firstLinked)) {
|
|
1933
|
+
const at = linkOf(job);
|
|
1934
|
+
if (at !== void 0) {
|
|
1935
|
+
drainTo(at);
|
|
1936
|
+
const prompt = userMessages[at];
|
|
1937
|
+
steps.push({
|
|
1938
|
+
kind: "turn",
|
|
1939
|
+
jobId: job.job_id,
|
|
1940
|
+
content: prompt.content,
|
|
1941
|
+
messageId: prompt.id
|
|
1942
|
+
});
|
|
1943
|
+
continue;
|
|
1944
|
+
}
|
|
1945
|
+
steps.push({ kind: "turn", jobId: job.job_id });
|
|
1946
|
+
}
|
|
1947
|
+
for (let i = cursor; i < userMessages.length; i++) {
|
|
1948
|
+
const m = userMessages[i];
|
|
1949
|
+
if (isSteer(m)) {
|
|
1950
|
+
steps.push({ kind: "steer", content: m.content, messageId: m.id });
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
return steps;
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1893
1956
|
// src/types.ts
|
|
1894
1957
|
var ChatEventType = {
|
|
1895
1958
|
// Connection lifecycle (SDK-local, not wire)
|
|
@@ -2158,16 +2221,40 @@ var StreamManager = class {
|
|
|
2158
2221
|
const userMessages = this.session.messages.filter(
|
|
2159
2222
|
(m) => m.role === "user"
|
|
2160
2223
|
);
|
|
2224
|
+
const plan = planRestore({
|
|
2225
|
+
completedJobs: completedJobs.map((j) => ({
|
|
2226
|
+
job_id: j.job_id,
|
|
2227
|
+
message_id: j.message_id
|
|
2228
|
+
})),
|
|
2229
|
+
userMessages: userMessages.map((m) => ({
|
|
2230
|
+
id: m.id,
|
|
2231
|
+
content: m.content
|
|
2232
|
+
}))
|
|
2233
|
+
});
|
|
2161
2234
|
const eventLists = await Promise.all(
|
|
2162
2235
|
completedJobs.map(
|
|
2163
2236
|
(job) => this.session.client.getConversationEvents(conversationId, job.job_id).catch(() => [])
|
|
2164
2237
|
)
|
|
2165
2238
|
);
|
|
2166
|
-
|
|
2239
|
+
const eventsByJobId = new Map(
|
|
2240
|
+
completedJobs.map((job, i) => [job.job_id, eventLists[i] ?? []])
|
|
2241
|
+
);
|
|
2242
|
+
for (const step of plan) {
|
|
2243
|
+
if (step.kind === "steer") {
|
|
2244
|
+
this.session.replayTurn(
|
|
2245
|
+
conversationId,
|
|
2246
|
+
[],
|
|
2247
|
+
step.content,
|
|
2248
|
+
step.messageId,
|
|
2249
|
+
true
|
|
2250
|
+
);
|
|
2251
|
+
continue;
|
|
2252
|
+
}
|
|
2167
2253
|
this.session.replayTurn(
|
|
2168
2254
|
conversationId,
|
|
2169
|
-
|
|
2170
|
-
|
|
2255
|
+
eventsByJobId.get(step.jobId) ?? [],
|
|
2256
|
+
step.content,
|
|
2257
|
+
step.messageId
|
|
2171
2258
|
);
|
|
2172
2259
|
}
|
|
2173
2260
|
if (completedJobs.length > 0) {
|