@qilin-agent/client-ui-user-questions 0.0.0-stage → 3.1.3
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 +21 -0
- package/README.i18n.yaml +52 -0
- package/README.md +104 -2
- package/README.zh.md +105 -0
- package/lib/client.js +1935 -0
- package/lib/index.js +13 -0
- package/lib/types/client/PlanReviewPanel.d.ts +14 -0
- package/lib/types/client/QuestionComposer.d.ts +26 -0
- package/lib/types/client/QuestionReplyView.d.ts +20 -0
- package/lib/types/client/contract/slots.d.ts +276 -0
- package/lib/types/client/draft-store.d.ts +40 -0
- package/lib/types/client/index.d.ts +33 -0
- package/lib/types/client/locales.d.ts +72 -0
- package/lib/types/client/question-reply.d.ts +67 -0
- package/lib/types/index.d.ts +11 -0
- package/package.json +80 -3
package/lib/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region lib/types/index.js
|
|
2
|
+
/**
|
|
3
|
+
* Web question plugin, node half.
|
|
4
|
+
*
|
|
5
|
+
* Mounting `ask_user_question` in the tools registry's global layer expands
|
|
6
|
+
* every agent's tool list regardless of its preset. Rendering a question is
|
|
7
|
+
* a host UI capability; the model-facing tool belongs to the presets that
|
|
8
|
+
* include it and to the TUI composition, which has no presets.
|
|
9
|
+
*/
|
|
10
|
+
/** Host plugin body — the model-facing tool is composed per preset, not here. */
|
|
11
|
+
function apply() {}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { apply };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PendingQuestion, PlanReview, QuestionComposerProps } from './contract/slots.ts';
|
|
2
|
+
/** The panel's own props: the question domain face, the narrowed review, and the locale seat. */
|
|
3
|
+
export type PlanReviewPanelProps = {
|
|
4
|
+
pending: PendingQuestion;
|
|
5
|
+
review: PlanReview;
|
|
6
|
+
} & Pick<QuestionComposerProps, 't' | 'renderSlot'>;
|
|
7
|
+
/**
|
|
8
|
+
* Render plan review controls; the submitted document opens in the sidebar.
|
|
9
|
+
*
|
|
10
|
+
* @param props - the question domain face, the narrowed plan review, and `t`.
|
|
11
|
+
* @returns The plan-review takeover for this request.
|
|
12
|
+
*/
|
|
13
|
+
export declare function PlanReviewPanel({ pending, review, t, renderSlot }: PlanReviewPanelProps): import("react").JSX.Element;
|
|
14
|
+
//# sourceMappingURL=PlanReviewPanel.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type QuestionComposerProps } from './contract/slots.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Split the conventional recommendation suffix without changing the answer value.
|
|
4
|
+
* @param label - Original option label returned if selected.
|
|
5
|
+
* @returns Display label plus recommendation state.
|
|
6
|
+
*/
|
|
7
|
+
export declare function parseRecommendedLabel(label: string): {
|
|
8
|
+
label: string;
|
|
9
|
+
recommended: boolean;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Composer takeover router. Generic-question drafts live in this entry's
|
|
13
|
+
* Session-scoped Slot store, keyed by the pending carrier, so a strict Session
|
|
14
|
+
* entry remount restores the same request without exposing it to another one.
|
|
15
|
+
*
|
|
16
|
+
* One takeover, two presentations: a request that declares a presentation intent this
|
|
17
|
+
* package renders uses that presentation (a plan review is one decision over one
|
|
18
|
+
* plan, not a question set), and every other request takes the generic flow.
|
|
19
|
+
* The routing lives here, at the one entry that owns the composer seat, so
|
|
20
|
+
* neither presentation can claim a request the other is already rendering.
|
|
21
|
+
*
|
|
22
|
+
* @param props - the selector-matched pending question carrier plus the framework standard kit.
|
|
23
|
+
* @returns The question flow, or the intent's own surface, for this request.
|
|
24
|
+
*/
|
|
25
|
+
export declare function QuestionComposer(props: QuestionComposerProps): import("react").JSX.Element;
|
|
26
|
+
//# sourceMappingURL=QuestionComposer.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { PropsLocale, PropsRuntime } from '@qilin-agent/client-ui-slots';
|
|
2
|
+
import type { QuestionReplyData } from './question-reply.ts';
|
|
3
|
+
type QuestionReplyViewProps = PropsRuntime<'conversation.chat.node', 'question-reply'> & PropsLocale<'question'>;
|
|
4
|
+
/**
|
|
5
|
+
* Right-aligned late-reply bubble: a label naming the earlier pending
|
|
6
|
+
* questions, then one question and answer pair per question. A payload the
|
|
7
|
+
* Client cannot read falls back to the model-facing text.
|
|
8
|
+
*/
|
|
9
|
+
export declare const QuestionReplyView: import("react").MemoExoticComponent<({ node, t }: QuestionReplyViewProps) => import("react").JSX.Element>;
|
|
10
|
+
/**
|
|
11
|
+
* Render one settled question reply as a compact transcript bubble.
|
|
12
|
+
* @param props - Reply data and the question locale translator.
|
|
13
|
+
* @returns The expandable reply bubble.
|
|
14
|
+
*/
|
|
15
|
+
export declare function QuestionReplyBubble({ data, t }: {
|
|
16
|
+
data: QuestionReplyData;
|
|
17
|
+
t: PropsLocale<'question'>['t'];
|
|
18
|
+
}): import("react").JSX.Element;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=QuestionReplyView.d.ts.map
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/** Question composer props and one pending Remote waterfall response. */
|
|
2
|
+
import type { InjectFace, PropsLocale, PropsRenderSlots, PropsRuntime, PropsStore } from '@qilin-agent/client-ui-slots';
|
|
3
|
+
import type { ToolCallId } from '@qilin-agent/llm/brand';
|
|
4
|
+
import type { SessionId } from '@qilin-agent/session/types';
|
|
5
|
+
import type { AskUserQuestionAnswer, AskUserQuestionAnswerItem, AskUserQuestionItem, UserQuestionState } from '@qilin-agent/user-questions/types';
|
|
6
|
+
import type { createQuestionDraftStore } from '../draft-store.ts';
|
|
7
|
+
declare module '@qilin-agent/client-ui-session/client' {
|
|
8
|
+
interface SessionPendingInteractionMap {
|
|
9
|
+
/** Pending question or plan-review request. */
|
|
10
|
+
question: PendingQuestion;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
declare module '@qilin-agent/client-ui-slots' {
|
|
14
|
+
interface SlotMap {
|
|
15
|
+
/** Actions for the exact plan under review; approval remains with the question composer. */
|
|
16
|
+
'conversation.plan-review.actions': {
|
|
17
|
+
kind: 'list';
|
|
18
|
+
scope: 'session';
|
|
19
|
+
owner: {
|
|
20
|
+
review: PlanReview;
|
|
21
|
+
requestKey: PendingQuestion['key'];
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** One structured answer batch covering every question of the request. */
|
|
27
|
+
export type QuestionAnswer = AskUserQuestionAnswer;
|
|
28
|
+
/** One question of the request. */
|
|
29
|
+
type QuestionItem = AskUserQuestionItem;
|
|
30
|
+
/** One option the asker offered on a question. */
|
|
31
|
+
type QuestionOption = NonNullable<QuestionItem['options']>[number];
|
|
32
|
+
/**
|
|
33
|
+
* A request narrowed to the `plan-review` presentation intent: everything the
|
|
34
|
+
* decision card renders and answers with, so the panel never re-reads the
|
|
35
|
+
* request fields. `approve` and `decline` are the asker's own options — an
|
|
36
|
+
* answer must carry one of those labels verbatim — and `plan` is the markdown
|
|
37
|
+
* body under review.
|
|
38
|
+
*/
|
|
39
|
+
export interface PlanReview {
|
|
40
|
+
/** The reviewed question's id, echoed in the answer. */
|
|
41
|
+
id: string;
|
|
42
|
+
/** The question text, kept as the card's accessible name. */
|
|
43
|
+
question: string;
|
|
44
|
+
/** The plan markdown under review. */
|
|
45
|
+
plan: string;
|
|
46
|
+
/** Logged tool invocation used to reopen this plan. */
|
|
47
|
+
callId?: ToolCallId;
|
|
48
|
+
/** The option that approves the plan. */
|
|
49
|
+
approve: QuestionOption;
|
|
50
|
+
/** The option that declines it; absent when the asker offered no other option. */
|
|
51
|
+
decline?: QuestionOption;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Narrow a request to a renderable plan review, or return undefined to leave it
|
|
55
|
+
* to the generic question flow.
|
|
56
|
+
*
|
|
57
|
+
* The card offers approval and a return to the composer for change requests.
|
|
58
|
+
* It accepts one question carrying the plan as detail and the named approve
|
|
59
|
+
* option, with at most one alternative and no multi-select. Larger choices
|
|
60
|
+
* remain in the generic question flow.
|
|
61
|
+
*
|
|
62
|
+
* @param questions - the request's whole question batch.
|
|
63
|
+
* @returns The narrowed review, or undefined when the generic flow owns it.
|
|
64
|
+
*/
|
|
65
|
+
export declare function planReviewOf(questions: readonly QuestionItem[]): PlanReview | undefined;
|
|
66
|
+
/** Rejection codes a Client returns through the waterfall; the wire preserves `name` and `code`. */
|
|
67
|
+
export type QuestionRejectionCode = 'ASK_ABORTED' | 'ASK_CANCELLED' | 'ASK_TIMED_OUT';
|
|
68
|
+
/** One live Host waterfall attached to a card; it settles once and then detaches. */
|
|
69
|
+
export interface QuestionWaterfallChannel {
|
|
70
|
+
/** Client-clock deadline in epoch milliseconds; absent for a blocking question. */
|
|
71
|
+
readonly deadline: number | undefined;
|
|
72
|
+
/** Resolve the Host waterfall with the whole answer batch. */
|
|
73
|
+
resolve(answer: QuestionAnswer): void;
|
|
74
|
+
/** Reject the Host waterfall with a wire-preserved code. */
|
|
75
|
+
reject(code: Exclude<QuestionRejectionCode, 'ASK_ABORTED'>): void;
|
|
76
|
+
/** Hand the request to the next waterfall listener. */
|
|
77
|
+
delegate(): void;
|
|
78
|
+
}
|
|
79
|
+
/** The Remote answer path of a continued question. */
|
|
80
|
+
export interface QuestionRpcChannel {
|
|
81
|
+
/** Steer the answer into the agent; false when the question is no longer continued. Rejects when a reply is already queued. */
|
|
82
|
+
answer(answer: QuestionAnswer): Promise<boolean>;
|
|
83
|
+
}
|
|
84
|
+
/** The composer seat a card occupies while it is visible. */
|
|
85
|
+
export interface QuestionSeat {
|
|
86
|
+
/** Withdraw the card from the pending-interaction registry without ending the request. */
|
|
87
|
+
hide(): void;
|
|
88
|
+
}
|
|
89
|
+
/** One waterfall request as the Remote Event listener awaits it. */
|
|
90
|
+
export interface QuestionWaterfallRequest {
|
|
91
|
+
readonly channel: QuestionWaterfallChannel;
|
|
92
|
+
/** Outcome returned to the Host: the answer, or a wire-preserved rejection. */
|
|
93
|
+
readonly result: Promise<QuestionAnswer>;
|
|
94
|
+
/**
|
|
95
|
+
* Test whether a rejection asks the listener to call `next()`.
|
|
96
|
+
* @param reason - rejection received from {@link QuestionWaterfallRequest.result}.
|
|
97
|
+
* @returns whether {@link QuestionWaterfallChannel.delegate} produced it.
|
|
98
|
+
*/
|
|
99
|
+
isDelegation(reason: unknown): boolean;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Create the deferred one Remote Event listener settles through a card.
|
|
103
|
+
* The request signal ends the channel with `ASK_ABORTED`; the Host ignores that
|
|
104
|
+
* outcome for an event it already finished, so a cancel frame loses nothing.
|
|
105
|
+
* @param deadline - Client-clock deadline in epoch milliseconds derived from the business claim's remaining duration.
|
|
106
|
+
* @param signal - Delivery lifetime of the forwarded request.
|
|
107
|
+
* @param onSettle - Called once with the channel when it settles or aborts.
|
|
108
|
+
* @returns The channel to attach and the promise the listener awaits.
|
|
109
|
+
*/
|
|
110
|
+
export declare function createWaterfallRequest(deadline: number | undefined, signal: AbortSignal | undefined, onSettle: (channel: QuestionWaterfallChannel) => void): QuestionWaterfallRequest;
|
|
111
|
+
/** Reactive card state read by every mounted presentation of one question. */
|
|
112
|
+
export interface QuestionCardSnapshot {
|
|
113
|
+
/** Mirrors the projection row; `open` until the projection says otherwise. */
|
|
114
|
+
readonly state: UserQuestionState;
|
|
115
|
+
/** User-visible foreground wait presentation. */
|
|
116
|
+
readonly waitState: 'counting' | 'focused' | 'editing' | 'waiting' | 'continued';
|
|
117
|
+
/**
|
|
118
|
+
* Foreground countdown presentation, absent for a request that carried no
|
|
119
|
+
* deadline. The card owns the ticking, so a hidden panel keeps counting and
|
|
120
|
+
* still times out; `running` is false while a hold or an edit froze it.
|
|
121
|
+
*/
|
|
122
|
+
readonly countdown: {
|
|
123
|
+
readonly remainingMs: number;
|
|
124
|
+
readonly running: boolean;
|
|
125
|
+
} | undefined;
|
|
126
|
+
/** Channel a submission would use right now. */
|
|
127
|
+
readonly channel: 'waterfall' | 'rpc' | 'none';
|
|
128
|
+
/** Set once the card left the registry; the mounted composer clears its draft on this. */
|
|
129
|
+
readonly closed: boolean;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* One answerable Client card. A card keyed by tool call is created by whichever
|
|
133
|
+
* source arrives first, the forwarded waterfall or the Session projection, and
|
|
134
|
+
* removed only when the projection no longer lists the call.
|
|
135
|
+
*/
|
|
136
|
+
export declare class PendingQuestion {
|
|
137
|
+
#private;
|
|
138
|
+
/** Presentation discriminator used by Session pending-interaction consumers. */
|
|
139
|
+
readonly kind: 'question' | 'plan-review';
|
|
140
|
+
/** Render identity and request key for the Session-scoped draft store. */
|
|
141
|
+
readonly key: string;
|
|
142
|
+
/** Agent/Session identity owning the request. */
|
|
143
|
+
readonly sessionId: SessionId;
|
|
144
|
+
/** The request's question list. */
|
|
145
|
+
readonly questions: readonly AskUserQuestionItem[];
|
|
146
|
+
/** Tool call identity; absent for a blocking request that carried no `wait`. */
|
|
147
|
+
readonly callId: ToolCallId | undefined;
|
|
148
|
+
/**
|
|
149
|
+
* Recorded answers of a call that already settled. Present only on a
|
|
150
|
+
* read-only review card, which the tool call row builds from its own
|
|
151
|
+
* transcript so a finished question can be read back in the panel that
|
|
152
|
+
* asked it. Such a card has no answer channel and no countdown.
|
|
153
|
+
*/
|
|
154
|
+
readonly review: readonly AskUserQuestionAnswerItem[] | undefined;
|
|
155
|
+
/**
|
|
156
|
+
* What closing the panel does. A card keyed by tool call stays reachable
|
|
157
|
+
* from its tool call row, so closing only withdraws the panel (`hide`) and
|
|
158
|
+
* persists nothing. A card the Host never named has no way back, so closing
|
|
159
|
+
* it ends the request (`cancel`).
|
|
160
|
+
*/
|
|
161
|
+
readonly dismissal: 'hide' | 'cancel';
|
|
162
|
+
/**
|
|
163
|
+
* @param sessionId - Agent/Session identity owning the request.
|
|
164
|
+
* @param questions - complete question batch.
|
|
165
|
+
* @param callId - tool call identity when the Host named one.
|
|
166
|
+
* @param siblings - keys of every card currently registered for the Session, for draft pruning.
|
|
167
|
+
* @param review - recorded answers of a settled call, making this a read-only card.
|
|
168
|
+
*/
|
|
169
|
+
constructor(sessionId: SessionId, questions: readonly AskUserQuestionItem[], callId?: ToolCallId, siblings?: () => readonly string[], review?: readonly AskUserQuestionAnswerItem[]);
|
|
170
|
+
/**
|
|
171
|
+
* Card key of a tool call, shared by every source that names one. The Host
|
|
172
|
+
* request and the Session projection carry the branded `ToolCallId`; a
|
|
173
|
+
* transcript row carries the same wire value as a plain string.
|
|
174
|
+
* @param sessionId - owning Session.
|
|
175
|
+
* @param callId - tool call identity, branded or as a transcript spells it.
|
|
176
|
+
* @returns the render identity and draft key.
|
|
177
|
+
*/
|
|
178
|
+
static keyOf(sessionId: SessionId, callId: string): string;
|
|
179
|
+
/**
|
|
180
|
+
* Draft keys that are still live in this Session, this card included.
|
|
181
|
+
* @returns keys the draft store must keep; everything else is stale.
|
|
182
|
+
*/
|
|
183
|
+
liveKeys(): readonly string[];
|
|
184
|
+
/** Subscribe to card state changes. */
|
|
185
|
+
readonly subscribe: (listener: () => void) => (() => void);
|
|
186
|
+
/** Read the stable current card state. */
|
|
187
|
+
readonly snapshot: () => QuestionCardSnapshot;
|
|
188
|
+
/** Observable snapshot read by the renderer's keyed Hook. */
|
|
189
|
+
readonly getSnapshot: () => QuestionCardSnapshot;
|
|
190
|
+
private createSnapshot;
|
|
191
|
+
private publish;
|
|
192
|
+
/**
|
|
193
|
+
* Attach the live waterfall of a forwarded request.
|
|
194
|
+
* @param channel - request channel created by {@link createWaterfallRequest}.
|
|
195
|
+
*/
|
|
196
|
+
attachWaterfall(channel: QuestionWaterfallChannel): void;
|
|
197
|
+
/**
|
|
198
|
+
* Drop a waterfall channel that settled or was cancelled; the card stays.
|
|
199
|
+
* @param channel - the channel that ended.
|
|
200
|
+
*/
|
|
201
|
+
detachWaterfall(channel: QuestionWaterfallChannel): void;
|
|
202
|
+
/**
|
|
203
|
+
* Whether a live waterfall is attached.
|
|
204
|
+
* @returns whether the pending Host request still accepts settlement.
|
|
205
|
+
*/
|
|
206
|
+
hasWaterfall(): boolean;
|
|
207
|
+
/**
|
|
208
|
+
* Attach the Remote answer path used once the question is continued.
|
|
209
|
+
* @param channel - Remote calls bound to this Session and call.
|
|
210
|
+
*/
|
|
211
|
+
attachRpc(channel: QuestionRpcChannel): void;
|
|
212
|
+
/**
|
|
213
|
+
* Attach the composer seat this card is published into.
|
|
214
|
+
* @param seat - withdrawal of the published panel, owned by the card registry.
|
|
215
|
+
*/
|
|
216
|
+
attachSeat(seat: QuestionSeat): void;
|
|
217
|
+
/**
|
|
218
|
+
* Copy the projection row state.
|
|
219
|
+
* @param state - `open` or `continued`.
|
|
220
|
+
*/
|
|
221
|
+
setState(state: UserQuestionState): void;
|
|
222
|
+
/** Stop this Client's countdown indefinitely; the waterfall then waits like a blocking question. */
|
|
223
|
+
takeTime(): void;
|
|
224
|
+
/**
|
|
225
|
+
* Record focus even before the request arrives, freezing a pristine countdown once attached.
|
|
226
|
+
* @param now - current Client epoch time.
|
|
227
|
+
*/
|
|
228
|
+
holdFocus(now?: number): void;
|
|
229
|
+
/**
|
|
230
|
+
* Resume a pristine countdown after the answer surface loses focus.
|
|
231
|
+
* @param now - current Client epoch time.
|
|
232
|
+
*/
|
|
233
|
+
releaseFocus(now?: number): void;
|
|
234
|
+
/**
|
|
235
|
+
* Keep the first edited draft answerable without a foreground deadline.
|
|
236
|
+
* @param now - current Client epoch time used to preserve the remaining duration.
|
|
237
|
+
*/
|
|
238
|
+
engage(now?: number): void;
|
|
239
|
+
/** Local countdown reached zero: settle the waterfall with `ASK_TIMED_OUT`, keep the card. */
|
|
240
|
+
timeout(): void;
|
|
241
|
+
/** Hand a live waterfall to the next listener when this presentation domain unloads. */
|
|
242
|
+
delegate(): void;
|
|
243
|
+
/** Mark the card removed from the registry; the mounted composer clears its draft. */
|
|
244
|
+
close(): void;
|
|
245
|
+
/**
|
|
246
|
+
* Submit the whole answer batch through the live waterfall, or through the
|
|
247
|
+
* Remote path once the question is continued.
|
|
248
|
+
* @param answer - complete structured answer batch.
|
|
249
|
+
*/
|
|
250
|
+
answer(answer: QuestionAnswer): Promise<void>;
|
|
251
|
+
/**
|
|
252
|
+
* Close the panel. A tool-call-keyed card only leaves the composer seat: the
|
|
253
|
+
* request stands, the countdown keeps running here, and the tool call row
|
|
254
|
+
* reopens it. A card the Host never named ends its request instead, because
|
|
255
|
+
* nothing could bring it back.
|
|
256
|
+
*/
|
|
257
|
+
dismiss(): Promise<void>;
|
|
258
|
+
}
|
|
259
|
+
/** Pending value returned by the composer-chain selector. */
|
|
260
|
+
export type QuestionWait = PendingQuestion;
|
|
261
|
+
/**
|
|
262
|
+
* Full component props: the framework runtime share (chain currency +
|
|
263
|
+
* session/global standard kit) plus the chain `matched` share — the entry's
|
|
264
|
+
* selector result, already narrowed to the question carrier — plus the
|
|
265
|
+
* standard locale seat; the carrier plus the domain face above carry the
|
|
266
|
+
* whole behavior surface.
|
|
267
|
+
*/
|
|
268
|
+
export type QuestionComposerProps = PropsRuntime<'conversation.composer'> & PropsStore<ReturnType<typeof createQuestionDraftStore>> & PropsRenderSlots<'conversation.plan-review.actions'> & InjectFace<{
|
|
269
|
+
keyedHooks: {
|
|
270
|
+
questionCard: (key: string) => PendingQuestion | undefined;
|
|
271
|
+
};
|
|
272
|
+
}> & {
|
|
273
|
+
matched: QuestionWait;
|
|
274
|
+
} & PropsLocale<'question'>;
|
|
275
|
+
export {};
|
|
276
|
+
//# sourceMappingURL=slots.d.ts.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-scoped draft state for the generic question composer. The Slot
|
|
3
|
+
* registry owns store instances; this module exports only the factory so a
|
|
4
|
+
* plugin reload cannot reuse a module-global handle.
|
|
5
|
+
*/
|
|
6
|
+
import { type EngineStoreHandle } from '@qilin-agent/client-store';
|
|
7
|
+
/** One in-progress answer, including an explicit skip. */
|
|
8
|
+
export interface QuestionDraftAnswer {
|
|
9
|
+
/** Offered labels currently selected. */
|
|
10
|
+
selected: string[];
|
|
11
|
+
/** Human-authored alternative or additional answer. */
|
|
12
|
+
custom: string;
|
|
13
|
+
/** Whether the user explicitly skipped this question. */
|
|
14
|
+
skipped: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** Navigation and answer drafts for one pending request. */
|
|
17
|
+
export interface QuestionDraftProgress {
|
|
18
|
+
/** Current question index. */
|
|
19
|
+
index: number;
|
|
20
|
+
/** One draft per question, in request order. */
|
|
21
|
+
drafts: QuestionDraftAnswer[];
|
|
22
|
+
/** Local indefinite-wait choice restored with the draft. */
|
|
23
|
+
wait?: 'editing' | 'waiting';
|
|
24
|
+
}
|
|
25
|
+
interface QuestionDraftState {
|
|
26
|
+
progressByRequest: Record<string, QuestionDraftProgress>;
|
|
27
|
+
}
|
|
28
|
+
type QuestionDraftActions = {
|
|
29
|
+
replace: (draft: QuestionDraftState, requestKey: string, progress: QuestionDraftProgress) => void;
|
|
30
|
+
clear: (draft: QuestionDraftState, requestKey: string) => void;
|
|
31
|
+
prune: (draft: QuestionDraftState, keep: readonly string[]) => void;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Declare the question composer's Session store. Drafts persist per Session so
|
|
35
|
+
* leaving the Session or restarting the Client does not erase an unfinished answer.
|
|
36
|
+
* @returns a persisted store handle whose instance is owned by the Slot registry.
|
|
37
|
+
*/
|
|
38
|
+
export declare function createQuestionDraftStore(): EngineStoreHandle<QuestionDraftState, QuestionDraftActions>;
|
|
39
|
+
export {};
|
|
40
|
+
//# sourceMappingURL=draft-store.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web question plugin, browser half: QuestionComposer registered as a
|
|
3
|
+
* selector-routed entry of the conversation-declared composer chain, plus the
|
|
4
|
+
* `question` dictionaries. The selector narrows the owner's currency to the
|
|
5
|
+
* question carrier (matched prop), and the whole behavior surface rides the
|
|
6
|
+
* carrier (domain encoding in contract/slots.ts PendingQuestion); copy rides
|
|
7
|
+
* the standard locale seat. Export discipline: packages/client/AGENTS.md.
|
|
8
|
+
*
|
|
9
|
+
* One entry, two presentations: the composer renders a request with a
|
|
10
|
+
* `plan-review` intent as the plan decision card and every other request as
|
|
11
|
+
* the generic question flow. Both use the same carrier and composer seat.
|
|
12
|
+
*/
|
|
13
|
+
import type { Context as ClientContext } from '@qilin-agent/kylin';
|
|
14
|
+
import { type QuestionKey } from './locales.ts';
|
|
15
|
+
export type { PendingQuestion, PlanReview, QuestionAnswer, QuestionComposerProps, QuestionWait, } from './contract/slots.ts';
|
|
16
|
+
export type { QuestionKey } from './locales.ts';
|
|
17
|
+
declare module '@qilin-agent/client-ui-slots' {
|
|
18
|
+
interface LocaleNamespaceMap {
|
|
19
|
+
/** The question composer's copy. */
|
|
20
|
+
question: QuestionKey;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** Required services: Agent scopes, Remote Events, Session UI, Slot registry, conversation nodes, and copy. */
|
|
24
|
+
export declare const inject: string[];
|
|
25
|
+
/**
|
|
26
|
+
* Client plugin body: register the `question` dictionaries, the question
|
|
27
|
+
* composer into the composer chain, and the late-reply conversation node.
|
|
28
|
+
* Zero business face — data and verbs live on the matched carrier; t rides
|
|
29
|
+
* the standard locale seat.
|
|
30
|
+
* @param ctx - client root context.
|
|
31
|
+
*/
|
|
32
|
+
export declare function apply(ctx: ClientContext): void;
|
|
33
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/** `question` namespace dictionaries. */
|
|
2
|
+
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
3
|
+
export declare const zh: {
|
|
4
|
+
'error.incomplete': string;
|
|
5
|
+
'error.unanswered': string;
|
|
6
|
+
'error.unavailable': string;
|
|
7
|
+
'error.resubmit': string;
|
|
8
|
+
'status.sent': string;
|
|
9
|
+
'wait.takeTime': string;
|
|
10
|
+
'wait.countdown': string;
|
|
11
|
+
'wait.paused': string;
|
|
12
|
+
'wait.held': string;
|
|
13
|
+
'wait.continued': string;
|
|
14
|
+
'review.status': string;
|
|
15
|
+
'review.skipped': string;
|
|
16
|
+
'reply.label': string;
|
|
17
|
+
'reply.open': string;
|
|
18
|
+
'reply.close': string;
|
|
19
|
+
'reply.answerLabel': string;
|
|
20
|
+
'reply.skipped': string;
|
|
21
|
+
'nav.prev': string;
|
|
22
|
+
'nav.next': string;
|
|
23
|
+
'nav.minimize': string;
|
|
24
|
+
'nav.maximize': string;
|
|
25
|
+
'nav.cancel': string;
|
|
26
|
+
'nav.close': string;
|
|
27
|
+
'option.recommended': string;
|
|
28
|
+
'custom.placeholder': string;
|
|
29
|
+
'action.skip': string;
|
|
30
|
+
'action.next': string;
|
|
31
|
+
'plan.header': string;
|
|
32
|
+
'plan.approve': string;
|
|
33
|
+
'plan.decline': string;
|
|
34
|
+
'plan.discuss': string;
|
|
35
|
+
};
|
|
36
|
+
/** The question namespace key union. */
|
|
37
|
+
export type QuestionKey = keyof typeof zh;
|
|
38
|
+
/** English dictionary, checked complete against the zh key set. */
|
|
39
|
+
export declare const en: {
|
|
40
|
+
'error.incomplete': string;
|
|
41
|
+
'error.unanswered': string;
|
|
42
|
+
'error.unavailable': string;
|
|
43
|
+
'error.resubmit': string;
|
|
44
|
+
'status.sent': string;
|
|
45
|
+
'wait.takeTime': string;
|
|
46
|
+
'wait.countdown': string;
|
|
47
|
+
'wait.paused': string;
|
|
48
|
+
'wait.held': string;
|
|
49
|
+
'wait.continued': string;
|
|
50
|
+
'review.status': string;
|
|
51
|
+
'review.skipped': string;
|
|
52
|
+
'reply.label': string;
|
|
53
|
+
'reply.open': string;
|
|
54
|
+
'reply.close': string;
|
|
55
|
+
'reply.answerLabel': string;
|
|
56
|
+
'reply.skipped': string;
|
|
57
|
+
'nav.prev': string;
|
|
58
|
+
'nav.next': string;
|
|
59
|
+
'nav.minimize': string;
|
|
60
|
+
'nav.maximize': string;
|
|
61
|
+
'nav.cancel': string;
|
|
62
|
+
'nav.close': string;
|
|
63
|
+
'option.recommended': string;
|
|
64
|
+
'custom.placeholder': string;
|
|
65
|
+
'action.skip': string;
|
|
66
|
+
'action.next': string;
|
|
67
|
+
'plan.header': string;
|
|
68
|
+
'plan.approve': string;
|
|
69
|
+
'plan.decline': string;
|
|
70
|
+
'plan.discuss': string;
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=locales.d.ts.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { ConversationNodeDefinition } from '@qilin-agent/client-ui-conversation/client';
|
|
2
|
+
import type { PropsLocale } from '@qilin-agent/client-ui-slots';
|
|
3
|
+
/** One asked question as echoed in the reply payload. */
|
|
4
|
+
export interface QuestionReplyQuestion {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly question: string;
|
|
7
|
+
readonly detail?: string;
|
|
8
|
+
readonly header?: string;
|
|
9
|
+
readonly options?: readonly {
|
|
10
|
+
readonly label: string;
|
|
11
|
+
readonly description?: string;
|
|
12
|
+
}[];
|
|
13
|
+
readonly multiSelect?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/** One structured answer as echoed in the reply payload. */
|
|
16
|
+
export interface QuestionReplyAnswer {
|
|
17
|
+
readonly id: string;
|
|
18
|
+
readonly selected: readonly string[];
|
|
19
|
+
readonly custom?: string;
|
|
20
|
+
}
|
|
21
|
+
/** Presentation data of one late reply. */
|
|
22
|
+
export interface QuestionReplyData {
|
|
23
|
+
readonly callId: string;
|
|
24
|
+
/** A late answer to the pending questions. */
|
|
25
|
+
readonly outcome: 'answered';
|
|
26
|
+
readonly questions: readonly QuestionReplyQuestion[];
|
|
27
|
+
readonly answers: readonly QuestionReplyAnswer[];
|
|
28
|
+
/** Model-facing text, shown when the payload is unreadable. */
|
|
29
|
+
readonly text: string;
|
|
30
|
+
readonly time: number;
|
|
31
|
+
}
|
|
32
|
+
declare module '@qilin-agent/client-ui-chat/client' {
|
|
33
|
+
interface ChatNodeDataMap {
|
|
34
|
+
/** Late answer to a continued question. */
|
|
35
|
+
'question-reply': QuestionReplyData;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
interface QuestionReplyState extends QuestionReplyData {
|
|
39
|
+
readonly seq: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Read the question and answer pairs out of the reply text at the conversation boundary.
|
|
43
|
+
* @param text - Model-facing JSON payload of the steered message.
|
|
44
|
+
* @returns The pairs, or empty lists when the payload is unreadable.
|
|
45
|
+
*/
|
|
46
|
+
export declare function replyPairsOf(text: string): Pick<QuestionReplyData, 'questions' | 'answers'>;
|
|
47
|
+
/**
|
|
48
|
+
* Answer values of one question in display order: the selected option labels
|
|
49
|
+
* followed by a non-blank custom answer.
|
|
50
|
+
* @param data - Projected reply holding the recorded answers.
|
|
51
|
+
* @param id - Question id to read.
|
|
52
|
+
* @returns The values, empty when the user skipped that question.
|
|
53
|
+
*/
|
|
54
|
+
export declare function replyAnswerValues(data: QuestionReplyData, id: string): string[];
|
|
55
|
+
/**
|
|
56
|
+
* Clipboard text of one late reply: every question with the answer the user
|
|
57
|
+
* gave, in the layout the open bubble shows, without the options nobody chose.
|
|
58
|
+
* The text does not depend on whether the bubble is open.
|
|
59
|
+
* @param data - Projected reply to copy.
|
|
60
|
+
* @param t - Bound `question` namespace translator owning the answer labels.
|
|
61
|
+
* @returns One block per question, or the model-facing text when the payload was unreadable.
|
|
62
|
+
*/
|
|
63
|
+
export declare function replyClipboardText(data: QuestionReplyData, t: PropsLocale<'question'>['t']): string;
|
|
64
|
+
/** Late-reply projection owned by the questions UI. */
|
|
65
|
+
export declare const questionReplyDefinition: ConversationNodeDefinition<QuestionReplyState>;
|
|
66
|
+
export {};
|
|
67
|
+
//# sourceMappingURL=question-reply.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web question plugin, node half.
|
|
3
|
+
*
|
|
4
|
+
* Mounting `ask_user_question` in the tools registry's global layer expands
|
|
5
|
+
* every agent's tool list regardless of its preset. Rendering a question is
|
|
6
|
+
* a host UI capability; the model-facing tool belongs to the presets that
|
|
7
|
+
* include it and to the TUI composition, which has no presets.
|
|
8
|
+
*/
|
|
9
|
+
/** Host plugin body — the model-facing tool is composed per preset, not here. */
|
|
10
|
+
export declare function apply(): void;
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,83 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qilin-agent/client-ui-user-questions",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"description": "Web ask_user_question composer takeover and plan-review presentation UI",
|
|
4
|
+
"version": "3.1.3",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/kkutysllb/QiLin.git",
|
|
11
|
+
"directory": "packages/client/ui-user-questions"
|
|
12
|
+
},
|
|
13
|
+
"engines": {
|
|
14
|
+
"node": "^22.19.0 || >=24.0.0"
|
|
15
|
+
},
|
|
16
|
+
"type": "module",
|
|
17
|
+
"main": "lib/index.js",
|
|
18
|
+
"types": "lib/types/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./lib/types/index.d.ts",
|
|
22
|
+
"default": "./lib/index.js"
|
|
23
|
+
},
|
|
24
|
+
"./client": {
|
|
25
|
+
"types": "./lib/types/client/index.d.ts",
|
|
26
|
+
"default": "./lib/client.js"
|
|
27
|
+
},
|
|
28
|
+
"./src/*": "./src/*",
|
|
29
|
+
"./package.json": "./package.json"
|
|
30
|
+
},
|
|
31
|
+
"qilin": {
|
|
32
|
+
"client": {
|
|
33
|
+
"inject": [
|
|
34
|
+
"@qilin-agent/api-remotes",
|
|
35
|
+
"@qilin-agent/api-session-controller",
|
|
36
|
+
"@qilin-agent/client-locale",
|
|
37
|
+
"@qilin-agent/client-ui-conversation",
|
|
38
|
+
"@qilin-agent/client-ui-renderer",
|
|
39
|
+
"@qilin-agent/client-ui-session"
|
|
40
|
+
],
|
|
41
|
+
"platform": "web"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@qilin-agent/kylin": "~4.0.3"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/react": "~18.3.1",
|
|
50
|
+
"clsx": "^2.0.0",
|
|
51
|
+
"react": "^18.2.0",
|
|
52
|
+
"@qilin-agent/agent": "3.1.3",
|
|
53
|
+
"@qilin-agent/api-session-controller": "3.1.3",
|
|
54
|
+
"@qilin-agent/client-connection": "3.1.3",
|
|
55
|
+
"@qilin-agent/api-remotes": "3.1.3",
|
|
56
|
+
"@qilin-agent/client-locale": "3.1.3",
|
|
57
|
+
"@qilin-agent/client-store": "3.1.3",
|
|
58
|
+
"@qilin-agent/client-ui-chat": "3.1.3",
|
|
59
|
+
"@qilin-agent/client-ui-conversation": "3.1.3",
|
|
60
|
+
"@qilin-agent/client-ui-renderer": "3.1.3",
|
|
61
|
+
"@qilin-agent/client-ui-slots": "3.1.3",
|
|
62
|
+
"@qilin-agent/client-ui-primitives": "3.1.3",
|
|
63
|
+
"@qilin-agent/client-ui-session": "3.1.3",
|
|
64
|
+
"@qilin-agent/kylin": "~4.0.3",
|
|
65
|
+
"@qilin-agent/llm": "3.1.3",
|
|
66
|
+
"@qilin-agent/session": "3.1.3",
|
|
67
|
+
"@qilin-agent/system-prompt": "3.1.3",
|
|
68
|
+
"@qilin-agent/tools": "3.1.3",
|
|
69
|
+
"@qilin-agent/typert-protocol": "3.1.3",
|
|
70
|
+
"@qilin-agent/user-questions": "3.1.3",
|
|
71
|
+
"@qilin-agent/client-ui-tool": "3.1.3",
|
|
72
|
+
"@qilin-agent/brand": "3.1.3"
|
|
73
|
+
},
|
|
74
|
+
"files": [
|
|
75
|
+
"lib/index.js",
|
|
76
|
+
"lib/client.js",
|
|
77
|
+
"lib/types/**/*.d.ts"
|
|
78
|
+
],
|
|
79
|
+
"scripts": {
|
|
80
|
+
"bundle": "tsdown",
|
|
81
|
+
"watch": "tsdown --watch"
|
|
82
|
+
}
|
|
6
83
|
}
|