@assistant-ui/eve 0.0.19 → 0.0.20
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/LICENSE +1 -1
- package/dist/eveCloudSessions.d.ts +9 -0
- package/dist/eveCloudSessions.d.ts.map +1 -0
- package/dist/eveCloudSessions.js +51 -0
- package/dist/eveCloudSessions.js.map +1 -0
- package/dist/hooks.d.ts +4 -0
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +4 -0
- package/dist/hooks.js.map +1 -1
- package/dist/sdkIdentity.d.ts +3 -0
- package/dist/sdkIdentity.d.ts.map +1 -0
- package/dist/sdkIdentity.js +9 -0
- package/dist/sdkIdentity.js.map +1 -0
- package/dist/useEveAgentRuntime.d.ts +8 -1
- package/dist/useEveAgentRuntime.d.ts.map +1 -1
- package/dist/useEveAgentRuntime.js +104 -21
- package/dist/useEveAgentRuntime.js.map +1 -1
- package/package.json +8 -4
- package/src/eveCloudSessions.test.ts +78 -0
- package/src/eveCloudSessions.ts +59 -0
- package/src/hooks.ts +4 -0
- package/src/sdkIdentity.ts +9 -0
- package/src/useEveAgentRuntime.cloud.test.tsx +558 -0
- package/src/useEveAgentRuntime.eveResume.test.tsx +102 -2
- package/src/useEveAgentRuntime.fast-refresh.test.tsx +342 -0
- package/src/useEveAgentRuntime.ts +203 -45
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { createEveCloudSessions } from "./eveCloudSessions";
|
|
3
|
+
|
|
4
|
+
describe("createEveCloudSessions", () => {
|
|
5
|
+
it("hands a waiting thread the session its first turn creates", async () => {
|
|
6
|
+
const sessions = createEveCloudSessions();
|
|
7
|
+
const waiting = sessions.wait("thread-1");
|
|
8
|
+
|
|
9
|
+
sessions.resolve("thread-1", "session-1");
|
|
10
|
+
sessions.resolve("thread-1", "session-2");
|
|
11
|
+
|
|
12
|
+
await expect(waiting).resolves.toBe("session-1");
|
|
13
|
+
await expect(sessions.wait("thread-1")).resolves.toBe("session-1");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("keeps a session resolved before the thread list asks for it", async () => {
|
|
17
|
+
const sessions = createEveCloudSessions();
|
|
18
|
+
|
|
19
|
+
sessions.resolve("thread-1", "session-1");
|
|
20
|
+
|
|
21
|
+
await expect(sessions.wait("thread-1")).resolves.toBe("session-1");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("rejects the wait when the first turn fails, then waits for the next session", async () => {
|
|
25
|
+
const sessions = createEveCloudSessions();
|
|
26
|
+
const first = sessions.wait("thread-1");
|
|
27
|
+
|
|
28
|
+
sessions.reject("thread-1", new Error("no session"));
|
|
29
|
+
await expect(first).rejects.toThrow("no session");
|
|
30
|
+
|
|
31
|
+
const second = sessions.wait("thread-1");
|
|
32
|
+
sessions.resolve("thread-1", "session-2");
|
|
33
|
+
await expect(second).resolves.toBe("session-2");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("ignores a rejection once the thread has a session", async () => {
|
|
37
|
+
const sessions = createEveCloudSessions();
|
|
38
|
+
|
|
39
|
+
sessions.resolve("thread-1", "session-1");
|
|
40
|
+
sessions.reject("thread-1", new Error("staged"));
|
|
41
|
+
|
|
42
|
+
await expect(sessions.wait("thread-1")).resolves.toBe("session-1");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("does not surface a rejection nobody waits for", async () => {
|
|
46
|
+
const sessions = createEveCloudSessions();
|
|
47
|
+
void sessions.wait("thread-1");
|
|
48
|
+
|
|
49
|
+
sessions.reject("thread-1", new Error("no session"));
|
|
50
|
+
sessions.reject("thread-2", new Error("no session"));
|
|
51
|
+
sessions.resolve("thread-1", "session-1");
|
|
52
|
+
|
|
53
|
+
await expect(sessions.wait("thread-1")).resolves.toBe("session-1");
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("rejects a pending wait when the thread's runtime unmounts", async () => {
|
|
57
|
+
const sessions = createEveCloudSessions();
|
|
58
|
+
const waiting = sessions.wait("thread-1");
|
|
59
|
+
|
|
60
|
+
sessions.release("thread-1");
|
|
61
|
+
|
|
62
|
+
await expect(waiting).rejects.toThrow(
|
|
63
|
+
"The thread's eve runtime unmounted before its first turn created a session.",
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("keeps threads apart", async () => {
|
|
68
|
+
const sessions = createEveCloudSessions();
|
|
69
|
+
const first = sessions.wait("thread-1");
|
|
70
|
+
const second = sessions.wait("thread-2");
|
|
71
|
+
|
|
72
|
+
sessions.resolve("thread-2", "session-2");
|
|
73
|
+
sessions.resolve("thread-1", "session-1");
|
|
74
|
+
|
|
75
|
+
await expect(first).resolves.toBe("session-1");
|
|
76
|
+
await expect(second).resolves.toBe("session-2");
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
type PendingSession = {
|
|
2
|
+
readonly promise: Promise<string>;
|
|
3
|
+
readonly resolve: (sessionId: string) => void;
|
|
4
|
+
readonly reject: (error: unknown) => void;
|
|
5
|
+
sessionId?: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const createPendingSession = (): PendingSession => {
|
|
9
|
+
let resolve!: (sessionId: string) => void;
|
|
10
|
+
let reject!: (error: unknown) => void;
|
|
11
|
+
const promise = new Promise<string>((onResolve, onReject) => {
|
|
12
|
+
resolve = onResolve;
|
|
13
|
+
reject = onReject;
|
|
14
|
+
});
|
|
15
|
+
// A thread can fail its first turn before the thread list asks for its session.
|
|
16
|
+
promise.catch(() => {});
|
|
17
|
+
return { promise, resolve, reject };
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** Lets the cloud thread list wait for the eve session a new thread's first turn creates, because eve assigns a session id only once that turn is accepted while a cloud thread takes its external id when it is created. */
|
|
21
|
+
export const createEveCloudSessions = () => {
|
|
22
|
+
const pending = new Map<string, PendingSession>();
|
|
23
|
+
|
|
24
|
+
const get = (threadId: string) => {
|
|
25
|
+
let session = pending.get(threadId);
|
|
26
|
+
if (!session) {
|
|
27
|
+
session = createPendingSession();
|
|
28
|
+
pending.set(threadId, session);
|
|
29
|
+
}
|
|
30
|
+
return session;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
wait: (threadId: string) => get(threadId).promise,
|
|
35
|
+
resolve: (threadId: string, sessionId: string) => {
|
|
36
|
+
const session = get(threadId);
|
|
37
|
+
if (session.sessionId !== undefined) return;
|
|
38
|
+
session.sessionId = sessionId;
|
|
39
|
+
session.resolve(sessionId);
|
|
40
|
+
},
|
|
41
|
+
reject: (threadId: string, error: Error) => {
|
|
42
|
+
const session = pending.get(threadId);
|
|
43
|
+
if (!session || session.sessionId !== undefined) return;
|
|
44
|
+
pending.delete(threadId);
|
|
45
|
+
session.reject(error);
|
|
46
|
+
},
|
|
47
|
+
release: (threadId: string) => {
|
|
48
|
+
const session = pending.get(threadId);
|
|
49
|
+
pending.delete(threadId);
|
|
50
|
+
session?.reject(
|
|
51
|
+
new Error(
|
|
52
|
+
"The thread's eve runtime unmounted before its first turn created a session.",
|
|
53
|
+
),
|
|
54
|
+
);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export type EveCloudSessions = ReturnType<typeof createEveCloudSessions>;
|
package/src/hooks.ts
CHANGED
|
@@ -13,6 +13,8 @@ export const useEveError = () => eveExtras.use((e) => e.error, undefined);
|
|
|
13
13
|
* Read the current Eve session cursor from the runtime extras. Persist it to
|
|
14
14
|
* resume the session later via `initialSession`. `undefined` when no session
|
|
15
15
|
* exists yet or outside an Eve runtime.
|
|
16
|
+
*
|
|
17
|
+
* With `cloud`, each cloud thread already keeps its session, so there is nothing to persist.
|
|
16
18
|
*/
|
|
17
19
|
export const useEveSession = (): EveRuntimeExtras["session"] | undefined =>
|
|
18
20
|
eveExtras.use((e) => e.session, undefined);
|
|
@@ -29,6 +31,8 @@ export const useEveEvents = (): EveRuntimeExtras["events"] =>
|
|
|
29
31
|
* recreates the owned session, and clears events and projected data. Pending
|
|
30
32
|
* staged messages and tool execution state are discarded. Safe to render
|
|
31
33
|
* outside an Eve runtime; invoking the returned function there throws.
|
|
34
|
+
*
|
|
35
|
+
* With `cloud`, it starts a new thread instead, leaving the current thread and its session in the list.
|
|
32
36
|
*/
|
|
33
37
|
export const useEveReset = () => {
|
|
34
38
|
const aui = useAui();
|