@usecontextlayer/pi-durable-assistant-ui 1.0.0 → 2.0.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/README.md +37 -52
- package/dist/attachments.d.ts +2 -2
- package/dist/attachments.d.ts.map +1 -1
- package/dist/attachments.js +1 -1
- package/dist/client.d.ts +42 -13
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +112 -41
- package/dist/conversation.d.ts +44 -0
- package/dist/conversation.d.ts.map +1 -0
- package/dist/conversation.js +149 -0
- package/dist/index.d.ts +8 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -4
- package/dist/metadata.d.ts +1 -2
- package/dist/metadata.d.ts.map +1 -1
- package/dist/metadata.js +0 -1
- package/dist/projection.d.ts +16 -2
- package/dist/projection.d.ts.map +1 -1
- package/dist/projection.js +41 -17
- package/dist/thread-list.d.ts +6 -0
- package/dist/thread-list.d.ts.map +1 -0
- package/dist/thread-list.js +39 -0
- package/dist/use-pi-durable-runtime.d.ts +22 -0
- package/dist/use-pi-durable-runtime.d.ts.map +1 -0
- package/dist/use-pi-durable-runtime.js +95 -0
- package/package.json +7 -6
- package/dist/store.d.ts +0 -57
- package/dist/store.d.ts.map +0 -1
- package/dist/store.js +0 -261
- package/dist/use-pi-runtime.d.ts +0 -17
- package/dist/use-pi-runtime.d.ts.map +0 -1
- package/dist/use-pi-runtime.js +0 -49
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { BACKGROUND_CONTEXT, withAbortSignal } from "@earendil-works/chord/context";
|
|
2
|
+
import { ConversationTranscript } from "@earendil-works/pi-durable";
|
|
3
|
+
import { EMPTY_CONVERSATION_METADATA } from "./metadata.js";
|
|
4
|
+
import { entryIdFromMessageId, projectConversation, toPiUserInput } from "./projection.js";
|
|
5
|
+
/** Disposable observation and actions permanently bound to one native conversation. */
|
|
6
|
+
export class PiDurableConversation {
|
|
7
|
+
client;
|
|
8
|
+
id;
|
|
9
|
+
transcript;
|
|
10
|
+
#listeners = new Set();
|
|
11
|
+
#controller;
|
|
12
|
+
#state = { view: undefined, metadata: EMPTY_CONVERSATION_METADATA, connection: "idle", isLoading: true, error: undefined };
|
|
13
|
+
constructor(client, id, pageSize) {
|
|
14
|
+
this.client = client;
|
|
15
|
+
this.id = id;
|
|
16
|
+
this.transcript = new ConversationTranscript((query, limit, context) => client.entries(id, query, limit, undefined, context.abortSignal), { pageSize });
|
|
17
|
+
}
|
|
18
|
+
getSnapshot = () => this.#state;
|
|
19
|
+
subscribe = (listener) => {
|
|
20
|
+
this.#listeners.add(listener);
|
|
21
|
+
return () => this.#listeners.delete(listener);
|
|
22
|
+
};
|
|
23
|
+
#set(patch) {
|
|
24
|
+
this.#state = { ...this.#state, ...patch };
|
|
25
|
+
for (const listener of this.#listeners)
|
|
26
|
+
listener();
|
|
27
|
+
}
|
|
28
|
+
reportError = (error) => {
|
|
29
|
+
this.#set({ error: error instanceof Error ? error : new Error(String(error)), isLoading: false });
|
|
30
|
+
};
|
|
31
|
+
start = () => {
|
|
32
|
+
if (this.#controller)
|
|
33
|
+
return;
|
|
34
|
+
const controller = new AbortController();
|
|
35
|
+
this.#controller = controller;
|
|
36
|
+
this.#set({ isLoading: !this.#state.view, error: undefined });
|
|
37
|
+
const observe = async (acquire, accept) => {
|
|
38
|
+
const watch = await acquire();
|
|
39
|
+
await accept(watch.value);
|
|
40
|
+
watch.start(async (change) => {
|
|
41
|
+
if (change.kind === "value")
|
|
42
|
+
await accept(change.value);
|
|
43
|
+
else
|
|
44
|
+
this.reportError(change.error);
|
|
45
|
+
});
|
|
46
|
+
const end = await watch.closed;
|
|
47
|
+
if (!controller.signal.aborted)
|
|
48
|
+
this.#ended(end);
|
|
49
|
+
};
|
|
50
|
+
void observe(() => this.client.watch(this.id, {
|
|
51
|
+
signal: controller.signal,
|
|
52
|
+
window: () => this.transcript.resume,
|
|
53
|
+
onConnection: (connection) => { if (!controller.signal.aborted)
|
|
54
|
+
this.#set({ connection }); },
|
|
55
|
+
}), async (view) => {
|
|
56
|
+
if (controller.signal.aborted)
|
|
57
|
+
return;
|
|
58
|
+
this.#set({ view: this.transcript.merge(view), isLoading: false, error: undefined });
|
|
59
|
+
const context = withAbortSignal(controller.signal, BACKGROUND_CONTEXT);
|
|
60
|
+
let gap = this.#state.view?.items.find((item) => item.kind === "gap");
|
|
61
|
+
while (gap?.kind === "gap" && !controller.signal.aborted) {
|
|
62
|
+
this.#set({ view: await this.transcript.fillGap(gap, context) });
|
|
63
|
+
gap = this.#state.view?.items.find((item) => item.kind === "gap");
|
|
64
|
+
}
|
|
65
|
+
}).catch((error) => { if (!controller.signal.aborted)
|
|
66
|
+
this.reportError(error); });
|
|
67
|
+
if (this.client.watchMetadata) {
|
|
68
|
+
void observe(() => this.client.watchMetadata(this.id, controller.signal), (metadata) => {
|
|
69
|
+
if (!controller.signal.aborted)
|
|
70
|
+
this.#set({ metadata });
|
|
71
|
+
}).catch((error) => { if (!controller.signal.aborted)
|
|
72
|
+
this.reportError(error); });
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
#ended(end) {
|
|
76
|
+
this.#set({ connection: "closed", isLoading: false });
|
|
77
|
+
if ("error" in end)
|
|
78
|
+
this.reportError(end.error);
|
|
79
|
+
}
|
|
80
|
+
stop = () => {
|
|
81
|
+
this.#controller?.abort();
|
|
82
|
+
this.#controller = undefined;
|
|
83
|
+
};
|
|
84
|
+
reload = () => { this.stop(); this.start(); };
|
|
85
|
+
submit = (draft) => this.client.submit(this.id, draft);
|
|
86
|
+
continue = () => this.submit({ type: "continue" });
|
|
87
|
+
steer = (content) => this.submit({ type: "input", content, whenBusy: "steer" });
|
|
88
|
+
followUp = (content) => this.submit({ type: "input", content, whenBusy: "followUp" });
|
|
89
|
+
cancel = () => {
|
|
90
|
+
if (!this.client.abort)
|
|
91
|
+
throw new Error("This Pi host does not expose cancellation");
|
|
92
|
+
return this.client.abort(this.id);
|
|
93
|
+
};
|
|
94
|
+
abortSubmission = (id) => {
|
|
95
|
+
if (!this.client.abortSubmission)
|
|
96
|
+
throw new Error("This Pi host does not expose submission withdrawal");
|
|
97
|
+
return this.client.abortSubmission(id);
|
|
98
|
+
};
|
|
99
|
+
configure = (change) => {
|
|
100
|
+
if (!this.client.configure)
|
|
101
|
+
throw new Error("This Pi host does not expose configuration");
|
|
102
|
+
return this.client.configure(this.id, change);
|
|
103
|
+
};
|
|
104
|
+
reset = (handoff) => {
|
|
105
|
+
if (!this.client.reset)
|
|
106
|
+
throw new Error("This Pi host does not expose reset");
|
|
107
|
+
return this.client.reset(this.id, handoff);
|
|
108
|
+
};
|
|
109
|
+
compact = (instructions) => {
|
|
110
|
+
if (!this.client.compact)
|
|
111
|
+
throw new Error("This Pi host does not expose compaction");
|
|
112
|
+
return this.client.compact(this.id, instructions);
|
|
113
|
+
};
|
|
114
|
+
forkAt = (at) => {
|
|
115
|
+
if (!this.client.fork)
|
|
116
|
+
throw new Error("This Pi host does not expose forks");
|
|
117
|
+
return this.client.fork(this.id, at);
|
|
118
|
+
};
|
|
119
|
+
edit = async (message) => {
|
|
120
|
+
if (!message.sourceId)
|
|
121
|
+
throw new Error("An edit must identify its original message");
|
|
122
|
+
const target = entryIdFromMessageId(message.sourceId);
|
|
123
|
+
const fork = await this.forkAt(target);
|
|
124
|
+
await this.client.submit(fork.id, { type: "write", entry: { kind: "assistant-ui.context-edit", edits: [{ target, action: "replace", messages: [{ role: "user", content: toPiUserInput(message), timestamp: Date.now() }] }] } });
|
|
125
|
+
await this.client.submit(fork.id, { type: "continue" });
|
|
126
|
+
return fork;
|
|
127
|
+
};
|
|
128
|
+
regenerate = async (parentId) => {
|
|
129
|
+
const messages = this.#state.view ? projectConversation(this.#state.view, this.#state.metadata) : [];
|
|
130
|
+
const index = parentId === null ? -1 : messages.findIndex((message) => message.id === parentId);
|
|
131
|
+
const answer = messages[index + 1];
|
|
132
|
+
if (!answer?.id || answer.role !== "assistant")
|
|
133
|
+
throw new Error("No committed assistant response follows this message");
|
|
134
|
+
const target = entryIdFromMessageId(answer.id);
|
|
135
|
+
const fork = await this.forkAt(target);
|
|
136
|
+
await this.client.submit(fork.id, { type: "write", entry: { kind: "assistant-ui.context-edit", edits: [{ target, action: "omit" }] } });
|
|
137
|
+
await this.client.submit(fork.id, { type: "continue" });
|
|
138
|
+
return fork;
|
|
139
|
+
};
|
|
140
|
+
loadEarlier = async () => {
|
|
141
|
+
const context = this.#controller ? withAbortSignal(this.#controller.signal, BACKGROUND_CONTEXT) : BACKGROUND_CONTEXT;
|
|
142
|
+
this.#set({ view: await this.transcript.loadEarlier(context) });
|
|
143
|
+
};
|
|
144
|
+
setFeedback = async (messageId, rating) => {
|
|
145
|
+
if (!this.client.setFeedback)
|
|
146
|
+
throw new Error("This Pi host does not expose feedback");
|
|
147
|
+
this.#set({ metadata: await this.client.setFeedback(this.id, entryIdFromMessageId(messageId), rating) });
|
|
148
|
+
};
|
|
149
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export type {
|
|
3
|
-
export {
|
|
1
|
+
export { createHttpPiDurableClient } from "./client.js";
|
|
2
|
+
export type { PiDurableClient, HttpPiDurableWatchFrame, HttpPiDurableWatchEnvelope, HttpPiDurableWatchSubscription, PiDurableWatchOptions, PiDurableConnectionState, PiDurableUploadResult, PiDurableHumanInputDetails, HttpPiDurableClientOptions, SerializedAgentChange, SerializedConversationCreateOptions } from "./client.js";
|
|
3
|
+
export { createPiDurableAttachmentAdapter } from "./attachments.js";
|
|
4
4
|
export type { ConversationMetadata, ConversationMetadataPatch } from "./metadata.js";
|
|
5
5
|
export { conversationInbox, conversationLiveState, entryIdFromMessageId, isForegroundRunning, projectConversation, toPiUserInput } from "./projection.js";
|
|
6
|
-
export {
|
|
7
|
-
export type {
|
|
8
|
-
export {
|
|
9
|
-
export
|
|
6
|
+
export { PiDurableConversation } from "./conversation.js";
|
|
7
|
+
export type { PiDurableConversationState } from "./conversation.js";
|
|
8
|
+
export { createPiDurableThreadListAdapter } from "./thread-list.js";
|
|
9
|
+
export { usePiDurableRuntime, usePiDurable } from "./use-pi-durable-runtime.js";
|
|
10
|
+
export type { UsePiDurableRuntimeOptions, PiDurableRuntimeExtras } from "./use-pi-durable-runtime.js";
|
|
10
11
|
export type { ConversationId, ConversationRecord, ConversationView, TranscriptView, SubmissionDraft, SubmissionId, SubmissionRecord, EntryId, AgentChange, AgentState, InboxState, HarnessInspection } from "@earendil-works/pi-durable";
|
|
11
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACxD,YAAY,EAAE,eAAe,EAAE,uBAAuB,EAAE,0BAA0B,EAAE,8BAA8B,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,mCAAmC,EAAE,MAAM,aAAa,CAAC;AACpU,OAAO,EAAE,gCAAgC,EAAE,MAAM,kBAAkB,CAAC;AACpE,YAAY,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC1J,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,YAAY,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,gCAAgC,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChF,YAAY,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACtG,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,gBAAgB,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { createHttpPiDurableClient } from "./client.js";
|
|
2
|
+
export { createPiDurableAttachmentAdapter } from "./attachments.js";
|
|
3
3
|
export { conversationInbox, conversationLiveState, entryIdFromMessageId, isForegroundRunning, projectConversation, toPiUserInput } from "./projection.js";
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
4
|
+
export { PiDurableConversation } from "./conversation.js";
|
|
5
|
+
export { createPiDurableThreadListAdapter } from "./thread-list.js";
|
|
6
|
+
export { usePiDurableRuntime, usePiDurable } from "./use-pi-durable-runtime.js";
|
package/dist/metadata.d.ts
CHANGED
|
@@ -2,10 +2,9 @@ import type { EntryId } from "@earendil-works/pi-durable";
|
|
|
2
2
|
/** Presentation data persisted by the host in a native conversation document. */
|
|
3
3
|
export type ConversationMetadata = {
|
|
4
4
|
title: string | null;
|
|
5
|
-
archived: boolean;
|
|
6
5
|
feedback: Record<EntryId, "positive" | "negative">;
|
|
7
6
|
suggestions: string[];
|
|
8
7
|
};
|
|
9
|
-
export type ConversationMetadataPatch = Partial<Pick<ConversationMetadata, "title" | "
|
|
8
|
+
export type ConversationMetadataPatch = Partial<Pick<ConversationMetadata, "title" | "suggestions">>;
|
|
10
9
|
export declare const EMPTY_CONVERSATION_METADATA: ConversationMetadata;
|
|
11
10
|
//# sourceMappingURL=metadata.d.ts.map
|
package/dist/metadata.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAE1D,iFAAiF;AACjF,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAC;AAE1D,iFAAiF;AACjF,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC,OAAO,EAAE,UAAU,GAAG,UAAU,CAAC,CAAC;IACnD,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,oBAAoB,EAAE,OAAO,GAAG,aAAa,CAAC,CAAC,CAAC;AAErG,eAAO,MAAM,2BAA2B,EAAE,oBAIzC,CAAC"}
|
package/dist/metadata.js
CHANGED
package/dist/projection.d.ts
CHANGED
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
import type { AppendMessage, ThreadMessageLike } from "@assistant-ui/react";
|
|
2
2
|
import type { EntryId, InboxState, LiveState, TranscriptView, UserInput } from "@earendil-works/pi-durable";
|
|
3
3
|
import type { ConversationMetadata } from "./metadata.js";
|
|
4
|
+
type ContentPart = Exclude<ThreadMessageLike["content"], string>[number];
|
|
5
|
+
type ToolPart = Extract<ContentPart, {
|
|
6
|
+
type: "tool-call";
|
|
7
|
+
}>;
|
|
8
|
+
type MutableMessage = Omit<ThreadMessageLike, "content" | "status"> & {
|
|
9
|
+
content: ContentPart[];
|
|
10
|
+
status?: ThreadMessageLike["status"];
|
|
11
|
+
};
|
|
4
12
|
export declare function conversationLiveState(view: Pick<TranscriptView, "docs">): LiveState;
|
|
5
13
|
export declare function conversationInbox(view: Pick<TranscriptView, "docs">): InboxState;
|
|
6
14
|
export declare function entryIdFromMessageId(id: string): EntryId;
|
|
7
15
|
/** Foreground generation is distinct from a run's after-run/background work. */
|
|
8
16
|
export declare function isForegroundRunning(view: Pick<TranscriptView, "docs">): boolean;
|
|
9
|
-
|
|
10
|
-
|
|
17
|
+
export declare function projectConversationHistory(items: TranscriptView["items"], conversationMetadata?: ConversationMetadata): {
|
|
18
|
+
messages: MutableMessage[];
|
|
19
|
+
calls: Map<string, ToolPart>;
|
|
20
|
+
};
|
|
21
|
+
export declare function projectConversationLive(history: ReturnType<typeof projectConversationHistory>, live: LiveState, conversationId: TranscriptView["conversation"]["id"]): readonly ThreadMessageLike[];
|
|
22
|
+
/** Pure projection of Pi history and current execution; Pi retains the durable originals. */
|
|
23
|
+
export declare function projectConversation(view: TranscriptView, metadata?: ConversationMetadata): readonly ThreadMessageLike[];
|
|
11
24
|
/** Convert only the user's new input; never send the rendered transcript back to Pi. */
|
|
12
25
|
export declare function toPiUserInput(message: AppendMessage): UserInput;
|
|
26
|
+
export {};
|
|
13
27
|
//# sourceMappingURL=projection.d.ts.map
|
package/dist/projection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projection.d.ts","sourceRoot":"","sources":["../src/projection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"projection.d.ts","sourceRoot":"","sources":["../src/projection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAG5E,OAAO,KAAK,EAAe,OAAO,EAAe,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACtI,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAE1D,KAAK,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AACzE,KAAK,QAAQ,GAAG,OAAO,CAAC,WAAW,EAAE;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC,CAAC;AAC5D,KAAK,cAAc,GAAG,IAAI,CAAC,iBAAiB,EAAE,SAAS,GAAG,QAAQ,CAAC,GAAG;IAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAA;CAAE,CAAC;AAEvI,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG,SAAS,CAEnF;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG,UAAU,CAEhF;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAIxD;AAED,gFAAgF;AAChF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG,OAAO,CAG/E;AAuCD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,oBAAoB,CAAC,EAAE,oBAAoB;;;EA+CrH;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,0BAA0B,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,cAAc,EAAE,cAAc,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,GAAG,SAAS,iBAAiB,EAAE,CAmCnM;AAED,6FAA6F;AAC7F,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,oBAAoB,GAAG,SAAS,iBAAiB,EAAE,CAEvH;AASD,wFAAwF;AACxF,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,CAY/D"}
|
package/dist/projection.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CompactionEntry, ResetEntry, SystemEntry } from "@earendil-works/pi-durable";
|
|
1
2
|
export function conversationLiveState(view) {
|
|
2
3
|
return (view.docs["pi.live"] ?? {});
|
|
3
4
|
}
|
|
@@ -35,9 +36,11 @@ function assistantContent(message, calls) {
|
|
|
35
36
|
}
|
|
36
37
|
function applyToolResult(tool, message) {
|
|
37
38
|
Object.assign(tool, {
|
|
38
|
-
|
|
39
|
+
artifact: message.details,
|
|
40
|
+
result: message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n"),
|
|
39
41
|
isError: message.isError,
|
|
40
42
|
isPreliminary: false,
|
|
43
|
+
approval: humanInputApproval(message.details),
|
|
41
44
|
modelContent: message.content.map((part) => part.type === "text"
|
|
42
45
|
? { type: "text", text: part.text }
|
|
43
46
|
: { type: "file", mediaType: part.mimeType, data: part.data }),
|
|
@@ -52,12 +55,11 @@ function messageStatus(message) {
|
|
|
52
55
|
return { type: "incomplete", reason: "length" };
|
|
53
56
|
return { type: "complete", reason: "stop" };
|
|
54
57
|
}
|
|
55
|
-
|
|
56
|
-
export function projectConversation(view, presentation) {
|
|
58
|
+
export function projectConversationHistory(items, conversationMetadata) {
|
|
57
59
|
const messages = [];
|
|
58
60
|
const calls = new Map();
|
|
59
61
|
const edits = new Map();
|
|
60
|
-
for (const item of
|
|
62
|
+
for (const item of items) {
|
|
61
63
|
if (item.kind === "entry") {
|
|
62
64
|
for (const change of item.entry.edits ?? [])
|
|
63
65
|
edits.set(change.target, { change, by: item.entry.id });
|
|
@@ -70,7 +72,7 @@ export function projectConversation(view, presentation) {
|
|
|
70
72
|
messages.push({ id, role: "user", content: userContent(message.content), createdAt: new Date(message.timestamp), metadata });
|
|
71
73
|
}
|
|
72
74
|
else if (message.role === "assistant") {
|
|
73
|
-
const feedback =
|
|
75
|
+
const feedback = conversationMetadata?.feedback[entry.id];
|
|
74
76
|
messages.push({ id, role: "assistant", content: assistantContent(message, calls), status: messageStatus(message), createdAt: new Date(message.timestamp), metadata: { ...metadata, ...(feedback ? { submittedFeedback: { type: feedback } } : {}) } });
|
|
75
77
|
}
|
|
76
78
|
else if (message.role === "toolResult") {
|
|
@@ -83,17 +85,17 @@ export function projectConversation(view, presentation) {
|
|
|
83
85
|
applyToolResult(tool, message);
|
|
84
86
|
}
|
|
85
87
|
}
|
|
86
|
-
for (const item of
|
|
88
|
+
for (const item of items) {
|
|
87
89
|
if (item.kind !== "entry")
|
|
88
90
|
continue;
|
|
89
91
|
const entry = item.entry;
|
|
90
|
-
if (entry
|
|
92
|
+
if (SystemEntry.is(entry))
|
|
91
93
|
continue;
|
|
92
|
-
if (entry
|
|
94
|
+
if (CompactionEntry.is(entry) || ResetEntry.is(entry)) {
|
|
93
95
|
messages.push({
|
|
94
96
|
id: `entry:${entry.id}:notice`,
|
|
95
97
|
role: "assistant",
|
|
96
|
-
content: [{ type: "
|
|
98
|
+
content: [{ type: "data", name: "pi-context", data: { kind: entry.kind, data: entry.data } }],
|
|
97
99
|
metadata: { custom: { conversationId: entry.conversationId, entryId: entry.id, entryKind: entry.kind, entryData: entry.data } },
|
|
98
100
|
});
|
|
99
101
|
}
|
|
@@ -102,16 +104,20 @@ export function projectConversation(view, presentation) {
|
|
|
102
104
|
for (const [index, message] of model.entries())
|
|
103
105
|
append(entry, message, index);
|
|
104
106
|
}
|
|
105
|
-
|
|
107
|
+
return { messages, calls };
|
|
108
|
+
}
|
|
109
|
+
export function projectConversationLive(history, live, conversationId) {
|
|
110
|
+
const messages = [...history.messages];
|
|
111
|
+
const calls = new Map(history.calls);
|
|
106
112
|
if (live.generation?.message) {
|
|
107
113
|
const message = live.generation.message;
|
|
108
114
|
messages.push({
|
|
109
|
-
id: `generation:${live.run?.taskId ??
|
|
115
|
+
id: `generation:${live.run?.taskId ?? conversationId}`,
|
|
110
116
|
role: "assistant",
|
|
111
117
|
content: assistantContent(message, calls),
|
|
112
118
|
status: { type: "running" },
|
|
113
119
|
createdAt: new Date(message.timestamp),
|
|
114
|
-
metadata: { custom: { conversationId
|
|
120
|
+
metadata: { isOptimistic: true, custom: { conversationId, taskId: live.run?.taskId } },
|
|
115
121
|
});
|
|
116
122
|
}
|
|
117
123
|
for (const slot of live.tools ?? []) {
|
|
@@ -119,17 +125,35 @@ export function projectConversation(view, presentation) {
|
|
|
119
125
|
if (!tool) {
|
|
120
126
|
tool = { type: "tool-call", toolCallId: slot.callId, toolName: slot.name, args: {} };
|
|
121
127
|
calls.set(slot.callId, tool);
|
|
122
|
-
messages.push({ id: `tool:${slot.callId}`, role: "assistant", content: [tool], status: { type: "running" } });
|
|
128
|
+
messages.push({ id: `tool:${slot.callId}`, role: "assistant", content: [tool], status: { type: "running" }, metadata: { isOptimistic: true } });
|
|
123
129
|
}
|
|
124
130
|
if (slot.status !== "done") {
|
|
125
|
-
|
|
126
|
-
const owner = messages
|
|
127
|
-
|
|
128
|
-
|
|
131
|
+
const ownerIndex = messages.findIndex((message) => message.content.includes(tool));
|
|
132
|
+
const owner = messages[ownerIndex];
|
|
133
|
+
const updated = { ...tool, result: slot.output, artifact: slot.details, isPreliminary: true, approval: humanInputApproval(slot.details) };
|
|
134
|
+
calls.set(slot.callId, updated);
|
|
135
|
+
messages[ownerIndex] = { ...owner, content: owner.content.map((part) => part === tool ? updated : part), status: { type: "running" } };
|
|
129
136
|
}
|
|
130
137
|
}
|
|
138
|
+
const failure = live.runFailure;
|
|
139
|
+
const lastStatus = messages.at(-1)?.status;
|
|
140
|
+
if (failure && failure.reason !== "aborted" && failure.reason !== "reset" && failure.reason !== "nothing_to_answer" && !(lastStatus?.type === "incomplete" && lastStatus.reason === "error")) {
|
|
141
|
+
messages.push({ id: `failure:${failure.taskId}`, role: "assistant", content: [], status: { type: "incomplete", reason: "error", error: failure.detail ?? failure.reason }, metadata: { isOptimistic: true, custom: { conversationId, taskId: failure.taskId } } });
|
|
142
|
+
}
|
|
131
143
|
return messages;
|
|
132
144
|
}
|
|
145
|
+
/** Pure projection of Pi history and current execution; Pi retains the durable originals. */
|
|
146
|
+
export function projectConversation(view, metadata) {
|
|
147
|
+
return projectConversationLive(projectConversationHistory(view.items, metadata), conversationLiveState(view), view.conversation.id);
|
|
148
|
+
}
|
|
149
|
+
function humanInputApproval(value) {
|
|
150
|
+
if (value === null || typeof value !== "object")
|
|
151
|
+
return undefined;
|
|
152
|
+
const details = value;
|
|
153
|
+
if (details.type !== "human-input" || typeof details.taskId !== "number" || typeof details.prompt !== "string" || (details.status !== "pending" && details.status !== "answered"))
|
|
154
|
+
return undefined;
|
|
155
|
+
return { id: String(details.taskId), prompt: details.prompt, display: "text", ...(details.status === "answered" ? { approved: true, text: details.response } : {}) };
|
|
156
|
+
}
|
|
133
157
|
/** Convert only the user's new input; never send the rendered transcript back to Pi. */
|
|
134
158
|
export function toPiUserInput(message) {
|
|
135
159
|
const content = [];
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { RemoteThreadListAdapter } from "@assistant-ui/react";
|
|
2
|
+
import type { ConversationId } from "@earendil-works/pi-durable";
|
|
3
|
+
import type { PiDurableClient } from "./client.js";
|
|
4
|
+
export declare function nativeConversationId(value: string): ConversationId;
|
|
5
|
+
export declare function createPiDurableThreadListAdapter(client: PiDurableClient, pageSize?: number): RemoteThreadListAdapter;
|
|
6
|
+
//# sourceMappingURL=thread-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread-list.d.ts","sourceRoot":"","sources":["../src/thread-list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAU,MAAM,4BAA4B,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAKnD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,CAIlE;AAED,wBAAgB,gCAAgC,CAAC,MAAM,EAAE,eAAe,EAAE,QAAQ,SAAK,GAAG,uBAAuB,CA8BhH"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export function nativeConversationId(value) {
|
|
2
|
+
const id = Number(value);
|
|
3
|
+
if (!Number.isSafeInteger(id) || id <= 0)
|
|
4
|
+
throw new Error(`Invalid Pi conversation ID: ${value}`);
|
|
5
|
+
return id;
|
|
6
|
+
}
|
|
7
|
+
export function createPiDurableThreadListAdapter(client, pageSize = 50) {
|
|
8
|
+
const metadata = (id, details) => ({
|
|
9
|
+
remoteId: String(id), status: "regular", title: details?.title ?? `Conversation ${id}`,
|
|
10
|
+
});
|
|
11
|
+
return {
|
|
12
|
+
async list({ after } = {}) {
|
|
13
|
+
const page = await client.listConversations({}, pageSize, after ? JSON.parse(after) : undefined);
|
|
14
|
+
const details = client.listMetadata ? await client.listMetadata(page.items.map((item) => item.id)) : undefined;
|
|
15
|
+
const threads = await Promise.all(page.items.map(async ({ id }) => metadata(id, details?.[String(id)] ?? await client.getMetadata?.(id))));
|
|
16
|
+
return { threads, ...(page.next ? { nextCursor: JSON.stringify(page.next) } : {}) };
|
|
17
|
+
},
|
|
18
|
+
async initialize() {
|
|
19
|
+
return { remoteId: String((await client.createConversation()).id) };
|
|
20
|
+
},
|
|
21
|
+
async fetch(remoteId) {
|
|
22
|
+
const id = nativeConversationId(remoteId);
|
|
23
|
+
if (client.getMetadata)
|
|
24
|
+
return metadata(id, await client.getMetadata(id));
|
|
25
|
+
await client.getConversationView(id);
|
|
26
|
+
return metadata(id);
|
|
27
|
+
},
|
|
28
|
+
async rename(remoteId, title) {
|
|
29
|
+
if (!client.updateMetadata)
|
|
30
|
+
throw new Error("This Pi host does not expose conversation titles");
|
|
31
|
+
await client.updateMetadata(nativeConversationId(remoteId), { title });
|
|
32
|
+
},
|
|
33
|
+
async archive() { throw new Error("Pi Durable does not support archiving conversations."); },
|
|
34
|
+
async delete() { throw new Error("Pi Durable does not support deleting conversations."); },
|
|
35
|
+
async generateTitle() {
|
|
36
|
+
return new ReadableStream({ start(controller) { controller.close(); } });
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type AssistantRuntime, type AttachmentAdapter, type DictationAdapter, type FeedbackAdapter, type SpeechSynthesisAdapter } from "@assistant-ui/react";
|
|
2
|
+
import type { ConversationId } from "@earendil-works/pi-durable";
|
|
3
|
+
import type { PiDurableClient } from "./client.js";
|
|
4
|
+
import { PiDurableConversation, type PiDurableConversationState } from "./conversation.js";
|
|
5
|
+
export interface UsePiDurableRuntimeOptions {
|
|
6
|
+
client: PiDurableClient;
|
|
7
|
+
threadId?: ConversationId;
|
|
8
|
+
onThreadIdChange?: (id: ConversationId | undefined) => void;
|
|
9
|
+
pageSize?: number;
|
|
10
|
+
attachments?: AttachmentAdapter;
|
|
11
|
+
speech?: SpeechSynthesisAdapter;
|
|
12
|
+
dictation?: DictationAdapter;
|
|
13
|
+
feedback?: FeedbackAdapter;
|
|
14
|
+
}
|
|
15
|
+
export interface PiDurableRuntimeExtras {
|
|
16
|
+
readonly conversation: PiDurableConversation | undefined;
|
|
17
|
+
readonly state: PiDurableConversationState;
|
|
18
|
+
}
|
|
19
|
+
/** Read Pi-specific state/actions from the current Assistant UI thread scope. */
|
|
20
|
+
export declare function usePiDurable(): PiDurableRuntimeExtras;
|
|
21
|
+
export declare function usePiDurableRuntime(options: UsePiDurableRuntimeOptions): AssistantRuntime;
|
|
22
|
+
//# sourceMappingURL=use-pi-durable-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-pi-durable-runtime.d.ts","sourceRoot":"","sources":["../src/use-pi-durable-runtime.ts"],"names":[],"mappings":"AACA,OAAO,EAAgG,KAAK,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAmC,KAAK,eAAe,EAAuB,KAAK,sBAAsB,EAA0B,MAAM,qBAAqB,CAAC;AAC1U,OAAO,KAAK,EAAE,cAAc,EAAU,MAAM,4BAA4B,CAAC;AACzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,KAAK,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAK3F,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,eAAe,CAAC;IACxB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,gBAAgB,CAAC,EAAE,CAAC,EAAE,EAAE,cAAc,GAAG,SAAS,KAAK,IAAI,CAAC;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,MAAM,CAAC,EAAE,sBAAsB,CAAC;IAChC,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,YAAY,EAAE,qBAAqB,GAAG,SAAS,CAAC;IACzD,QAAQ,CAAC,KAAK,EAAE,0BAA0B,CAAC;CAC5C;AASD,iFAAiF;AACjF,wBAAgB,YAAY,IAAI,sBAAsB,CAErD;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,gBAAgB,CASzF"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
|
2
|
+
import { useAui, useAuiState, useExternalStoreRuntime, useRemoteThreadListRuntime } from "@assistant-ui/react";
|
|
3
|
+
import { PiDurableConversation } from "./conversation.js";
|
|
4
|
+
import { EMPTY_CONVERSATION_METADATA } from "./metadata.js";
|
|
5
|
+
import { conversationInbox, conversationLiveState, isForegroundRunning, projectConversationHistory, projectConversationLive, toPiUserInput } from "./projection.js";
|
|
6
|
+
import { createPiDurableThreadListAdapter, nativeConversationId } from "./thread-list.js";
|
|
7
|
+
const DRAFT_STATE = { view: undefined, metadata: EMPTY_CONVERSATION_METADATA, connection: "idle", isLoading: false, error: undefined };
|
|
8
|
+
const EMPTY_SUBSCRIPTION = () => () => { };
|
|
9
|
+
const DRAFT_SNAPSHOT = () => DRAFT_STATE;
|
|
10
|
+
const DRAFT_EXTRAS = { conversation: undefined, state: DRAFT_STATE };
|
|
11
|
+
const identity = (message) => message;
|
|
12
|
+
const EMPTY_HISTORY = projectConversationHistory([]);
|
|
13
|
+
/** Read Pi-specific state/actions from the current Assistant UI thread scope. */
|
|
14
|
+
export function usePiDurable() {
|
|
15
|
+
return useAuiState((state) => state.thread.extras ?? DRAFT_EXTRAS);
|
|
16
|
+
}
|
|
17
|
+
export function usePiDurableRuntime(options) {
|
|
18
|
+
const { client, pageSize, threadId, onThreadIdChange } = options;
|
|
19
|
+
const adapter = useMemo(() => createPiDurableThreadListAdapter(client, pageSize), [client, pageSize]);
|
|
20
|
+
return useRemoteThreadListRuntime({
|
|
21
|
+
adapter,
|
|
22
|
+
threadId: threadId === undefined ? undefined : String(threadId),
|
|
23
|
+
onThreadIdChange: (id) => onThreadIdChange?.(id === undefined ? undefined : nativeConversationId(id)),
|
|
24
|
+
runtimeHook: () => usePiDurableThreadRuntime(options),
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function usePiDurableThreadRuntime({ client, pageSize, attachments, speech, dictation, feedback }) {
|
|
28
|
+
const aui = useAui();
|
|
29
|
+
const remoteId = useAuiState((state) => state.threadListItem.remoteId);
|
|
30
|
+
const conversation = useMemo(() => remoteId === undefined ? undefined : new PiDurableConversation(client, nativeConversationId(remoteId), pageSize), [client, remoteId, pageSize]);
|
|
31
|
+
const state = useSyncExternalStore(conversation?.subscribe ?? EMPTY_SUBSCRIPTION, conversation?.getSnapshot ?? DRAFT_SNAPSHOT, conversation?.getSnapshot ?? DRAFT_SNAPSHOT);
|
|
32
|
+
const [sendError, setSendError] = useState();
|
|
33
|
+
useEffect(() => { conversation?.start(); return () => conversation?.stop(); }, [conversation]);
|
|
34
|
+
const history = useMemo(() => state.view ? projectConversationHistory(state.view.items, state.metadata) : EMPTY_HISTORY, [state.view?.items, state.metadata.feedback]);
|
|
35
|
+
const live = state.view ? conversationLiveState(state.view) : undefined;
|
|
36
|
+
const messages = useMemo(() => state.view && live ? projectConversationLive(history, live, state.view.conversation.id) : [], [history, live, state.view?.conversation.id]);
|
|
37
|
+
const isRunning = state.view ? isForegroundRunning(state.view) : false;
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!remoteId || state.metadata.title === null)
|
|
40
|
+
return;
|
|
41
|
+
if (aui.threadListItem().getState().title !== state.metadata.title) {
|
|
42
|
+
void aui.threads().reload().catch((error) => setSendError(error instanceof Error ? error : new Error(String(error))));
|
|
43
|
+
}
|
|
44
|
+
}, [aui, remoteId, state.metadata.title]);
|
|
45
|
+
const send = async (message, whenBusy) => {
|
|
46
|
+
setSendError(undefined);
|
|
47
|
+
const { remoteId: id } = await aui.threadListItem().initialize();
|
|
48
|
+
await client.submit(nativeConversationId(id), { type: "input", content: toPiUserInput(message), whenBusy });
|
|
49
|
+
};
|
|
50
|
+
const reportSendError = (error) => setSendError(error instanceof Error ? error : new Error(String(error)));
|
|
51
|
+
const queue = useMemo(() => {
|
|
52
|
+
if (!client.abortSubmission)
|
|
53
|
+
return undefined;
|
|
54
|
+
const items = state.view ? conversationInbox(state.view).items : [];
|
|
55
|
+
const convert = (mode) => items.flatMap((item) => {
|
|
56
|
+
if (item.mode !== mode || !("content" in item))
|
|
57
|
+
return [];
|
|
58
|
+
const content = typeof item.content === "string" ? [{ type: "text", text: item.content }] : item.content;
|
|
59
|
+
const parts = content.map((part) => part.type === "text" ? part : { type: "file", mimeType: part.mimeType, data: part.data });
|
|
60
|
+
return [{ id: String(item.id), prompt: content.filter((part) => part.type === "text").map((part) => part.text).join("\n"), parts }];
|
|
61
|
+
});
|
|
62
|
+
return {
|
|
63
|
+
items: convert("followUp"), steerItems: convert("steer"),
|
|
64
|
+
enqueue: (message) => { void send(message, "followUp").catch(reportSendError); },
|
|
65
|
+
steer: (message) => { void send(message, "steer").catch(reportSendError); },
|
|
66
|
+
remove: (id) => { void client.abortSubmission(Number(id)).catch(reportSendError); },
|
|
67
|
+
move: () => { throw new Error("Pi Durable does not support moving queued submissions."); },
|
|
68
|
+
edit: () => { throw new Error("Pi Durable does not support editing queued submissions."); },
|
|
69
|
+
};
|
|
70
|
+
}, [client, remoteId, state.view?.docs["pi.inbox"]]);
|
|
71
|
+
const nativeFeedback = useMemo(() => conversation && client.setFeedback ? { submit: ({ message, type }) => conversation.setFeedback(message.id, type) } : undefined, [conversation, client]);
|
|
72
|
+
const extras = useMemo(() => ({ conversation, state: sendError ? { ...state, error: sendError } : state }), [conversation, state, sendError]);
|
|
73
|
+
return useExternalStoreRuntime({
|
|
74
|
+
messages,
|
|
75
|
+
convertMessage: identity,
|
|
76
|
+
isRunning,
|
|
77
|
+
isLoading: state.isLoading,
|
|
78
|
+
hasEarlier: state.view?.items[0]?.kind === "earlier",
|
|
79
|
+
onLoadEarlier: conversation?.loadEarlier,
|
|
80
|
+
onRefetchThread: conversation ? async () => { conversation.reload(); } : undefined,
|
|
81
|
+
onNew: send,
|
|
82
|
+
onCancel: conversation && client.abort ? conversation.cancel : undefined,
|
|
83
|
+
onEdit: conversation && client.fork ? async (message) => { const fork = await conversation.edit(message); await aui.threads().switchToThread(String(fork.id)); } : undefined,
|
|
84
|
+
onReload: conversation && client.fork ? async (parentId) => { const fork = await conversation.regenerate(parentId); await aui.threads().switchToThread(String(fork.id)); } : undefined,
|
|
85
|
+
onRespondToToolApproval: client.respondToHumanInput ? async ({ approvalId, text }) => {
|
|
86
|
+
if (text === undefined)
|
|
87
|
+
throw new Error("This human-input request requires a text response");
|
|
88
|
+
await client.respondToHumanInput(Number(approvalId), text);
|
|
89
|
+
} : undefined,
|
|
90
|
+
suggestions: state.metadata.suggestions.map((prompt) => ({ prompt })),
|
|
91
|
+
queue,
|
|
92
|
+
extras,
|
|
93
|
+
adapters: { attachments, speech, dictation, feedback: feedback ?? nativeFeedback },
|
|
94
|
+
});
|
|
95
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usecontextlayer/pi-durable-assistant-ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Assistant UI runtime adapter for Pi Durable conversations",
|
|
6
6
|
"repository": {
|
|
@@ -26,21 +26,22 @@
|
|
|
26
26
|
"dist"
|
|
27
27
|
],
|
|
28
28
|
"scripts": {
|
|
29
|
-
"build": "tsc -p tsconfig.json",
|
|
29
|
+
"build": "node --input-type=module -e \"import { rmSync } from 'node:fs'; rmSync('dist', { recursive: true, force: true });\" && tsc -p tsconfig.json",
|
|
30
30
|
"check": "tsc -p tsconfig.json --noEmit",
|
|
31
31
|
"dev": "tsc -p tsconfig.json --watch --preserveWatchOutput"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@assistant-ui/react": "0.15.32",
|
|
35
|
-
"react": "^19.0.0"
|
|
34
|
+
"@assistant-ui/react": "^0.15.32",
|
|
35
|
+
"react": "^19.0.0",
|
|
36
|
+
"@earendil-works/pi-durable": "^1.5.0"
|
|
36
37
|
},
|
|
37
38
|
"dependencies": {
|
|
38
|
-
"@earendil-works/pi-durable": "1.5.0",
|
|
39
39
|
"@earendil-works/chord": "1.5.0",
|
|
40
40
|
"@earendil-works/pi-ai": "1.5.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@assistant-ui/react": "0.15.32",
|
|
44
|
-
"react": "19.3.1"
|
|
44
|
+
"react": "19.3.1",
|
|
45
|
+
"@earendil-works/pi-durable": "1.5.0"
|
|
45
46
|
}
|
|
46
47
|
}
|