@genesislcap/ai-assistant 15.6.2 → 15.7.1
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/ai-assistant.api.json +422 -6
- package/dist/ai-assistant.d.ts +654 -19
- package/dist/chat-driver.cjs +285 -26
- package/dist/chat-driver.cjs.map +3 -3
- package/dist/chat-driver.mjs +285 -26
- package/dist/chat-driver.mjs.map +3 -3
- package/dist/custom-elements.json +289 -32
- package/dist/dts/channel/ai-activity-channel.d.ts +51 -1
- package/dist/dts/channel/ai-activity-channel.d.ts.map +1 -1
- package/dist/dts/components/chat-driver/chat-driver.d.ts +99 -1
- package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
- package/dist/dts/components/chat-driver/chat-driver.test.d.ts.map +1 -1
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.budget.test.d.ts +2 -0
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.budget.test.d.ts.map +1 -0
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +14 -0
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
- package/dist/dts/components/settings-modal/settings-modal.styles.d.ts.map +1 -1
- package/dist/dts/components/settings-modal/settings-modal.template.d.ts.map +1 -1
- package/dist/dts/main/blocked-state.test.d.ts +2 -0
- package/dist/dts/main/blocked-state.test.d.ts.map +1 -0
- package/dist/dts/main/main.d.ts +435 -19
- package/dist/dts/main/main.d.ts.map +1 -1
- package/dist/dts/main/main.styles.d.ts.map +1 -1
- package/dist/dts/main/main.styles.test.d.ts +2 -0
- package/dist/dts/main/main.styles.test.d.ts.map +1 -0
- package/dist/dts/main/main.template.d.ts +53 -0
- package/dist/dts/main/main.template.d.ts.map +1 -1
- package/dist/dts/state/ai-assistant-slice.d.ts +162 -6
- package/dist/dts/state/ai-assistant-slice.d.ts.map +1 -1
- package/dist/dts/state/debug-event-log.d.ts +6 -1
- package/dist/dts/state/debug-event-log.d.ts.map +1 -1
- package/dist/dts/state/session-store.d.ts +11 -0
- package/dist/dts/state/session-store.d.ts.map +1 -1
- package/dist/dts/utils/banked-usage-baselines.d.ts +51 -0
- package/dist/dts/utils/banked-usage-baselines.d.ts.map +1 -0
- package/dist/esm/components/chat-driver/chat-driver.js +263 -21
- package/dist/esm/components/chat-driver/chat-driver.test.js +464 -1
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.budget.test.js +312 -0
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +89 -4
- package/dist/esm/components/settings-modal/settings-modal.styles.js +63 -18
- package/dist/esm/components/settings-modal/settings-modal.template.js +36 -7
- package/dist/esm/main/blocked-state.test.js +969 -0
- package/dist/esm/main/cost-session-banking.test.js +238 -8
- package/dist/esm/main/main.js +826 -48
- package/dist/esm/main/main.styles.js +72 -0
- package/dist/esm/main/main.styles.test.js +99 -0
- package/dist/esm/main/main.template.js +121 -4
- package/dist/esm/state/ai-assistant-slice.js +145 -7
- package/dist/esm/state/ai-assistant-slice.test.js +138 -1
- package/dist/esm/state/debug-event-log.js +7 -2
- package/dist/esm/state/debug-event-log.test.js +49 -1
- package/dist/esm/state/persistence/session-snapshot.test.js +18 -0
- package/dist/esm/utils/banked-usage-baselines.js +84 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/docs/migration-GENC-1464.md +562 -0
- package/docs/sub_agent.md +20 -3
- package/package.json +17 -17
- package/src/channel/ai-activity-channel.ts +56 -2
- package/src/components/chat-driver/chat-driver.test.ts +549 -0
- package/src/components/chat-driver/chat-driver.ts +324 -14
- package/src/components/orchestrating-driver/orchestrating-driver.budget.test.ts +438 -0
- package/src/components/orchestrating-driver/orchestrating-driver.ts +101 -6
- package/src/components/settings-modal/settings-modal.styles.ts +63 -18
- package/src/components/settings-modal/settings-modal.template.ts +38 -7
- package/src/main/blocked-state.test.ts +1316 -0
- package/src/main/cost-session-banking.test.ts +283 -11
- package/src/main/main.styles.test.ts +130 -0
- package/src/main/main.styles.ts +72 -0
- package/src/main/main.template.ts +131 -4
- package/src/main/main.ts +827 -42
- package/src/state/ai-assistant-slice.test.ts +215 -0
- package/src/state/ai-assistant-slice.ts +218 -8
- package/src/state/debug-event-log.test.ts +63 -0
- package/src/state/debug-event-log.ts +7 -2
- package/src/state/persistence/session-snapshot.test.ts +22 -0
- package/src/utils/banked-usage-baselines.ts +90 -0
|
@@ -274,6 +274,118 @@ const restoringTemplate = html<FoundationAiAssistant>`
|
|
|
274
274
|
</div>
|
|
275
275
|
`;
|
|
276
276
|
|
|
277
|
+
/**
|
|
278
|
+
* Class list for the banner below, joined rather than interpolated so an
|
|
279
|
+
* inapplicable modifier contributes nothing. Two interpolations directly in the
|
|
280
|
+
* attribute emitted `class="blocked-banner "` in the common (unblocked) case —
|
|
281
|
+
* harmless to the browser, but it shows up in every DOM snapshot and every
|
|
282
|
+
* innerHTML assertion a host writes against this element.
|
|
283
|
+
*
|
|
284
|
+
* The two modifiers name what they actually gate, which is why neither is
|
|
285
|
+
* `is-blocked`: `is-visible` means the banner has something to say, and that
|
|
286
|
+
* includes PARTIAL exhaustion — one vendor walled, composer still live, `blocked`
|
|
287
|
+
* false. `is-partial` then softens the treatment for exactly that case. Naming
|
|
288
|
+
* the first after `blocked` read as a contradiction beside the second, and made
|
|
289
|
+
* the styles say `.blocked-banner.is-blocked` to mean "visible".
|
|
290
|
+
*
|
|
291
|
+
* Exported for the unit test that pins the attribute; not part of the element
|
|
292
|
+
* API.
|
|
293
|
+
*
|
|
294
|
+
* @internal
|
|
295
|
+
*/
|
|
296
|
+
export const blockedBannerClasses = (x: FoundationAiAssistant): string =>
|
|
297
|
+
[
|
|
298
|
+
'blocked-banner',
|
|
299
|
+
x.bannerVisible ? 'is-visible' : '',
|
|
300
|
+
x.bannerVisible && !x.blocked ? 'is-partial' : '',
|
|
301
|
+
]
|
|
302
|
+
.filter(Boolean)
|
|
303
|
+
.join(' ');
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Id of the blocked banner, referenced by the composer controls'
|
|
307
|
+
* `aria-describedby`. Shadow-DOM-scoped, so a fixed string cannot collide with
|
|
308
|
+
* the host page — and IDREF resolution is same-root, which is exactly where both
|
|
309
|
+
* ends of this reference live.
|
|
310
|
+
*
|
|
311
|
+
* @internal
|
|
312
|
+
*/
|
|
313
|
+
export const BLOCKED_BANNER_ID = 'blocked-banner';
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* `aria-describedby` for the composer's textarea, send button and attach button:
|
|
317
|
+
* the banner's id whenever the banner has something to say, otherwise `null` (so
|
|
318
|
+
* the attribute is omitted rather than emitted empty).
|
|
319
|
+
*
|
|
320
|
+
* Keyed on `bannerVisible`, NOT on `blocked`, and that is the point. The
|
|
321
|
+
* PARTIAL state — some vendor walled, composer still live — is the state this
|
|
322
|
+
* feature exists to create, and it was the one state with no accessible
|
|
323
|
+
* explanation at all: `aria-disabled` and `aria-label` bind only on `blocked`, and
|
|
324
|
+
* a live composer keeps the host's own placeholder, so a screen-reader user
|
|
325
|
+
* arriving at the textarea heard "Type a message" with no hint that the next turn
|
|
326
|
+
* might be refused. The banner's `role="status"` announces the text when it
|
|
327
|
+
* CHANGES; this is what makes the same explanation reachable afterwards, on
|
|
328
|
+
* demand, from the control it is about.
|
|
329
|
+
*
|
|
330
|
+
* Applied in the fully blocked state too, where it is additive: the `aria-label`
|
|
331
|
+
* there states the reason as the control's name, and this restates it as its
|
|
332
|
+
* description for the send/attach buttons, which carry neither.
|
|
333
|
+
*
|
|
334
|
+
* Exported for the unit test that pins it; not part of the element API.
|
|
335
|
+
*
|
|
336
|
+
* @internal
|
|
337
|
+
*/
|
|
338
|
+
export const composerDescribedBy = (x: FoundationAiAssistant): string | null =>
|
|
339
|
+
x.bannerVisible ? BLOCKED_BANNER_ID : null;
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Persistent banner shown above the composer while a backend condition blocks
|
|
343
|
+
* the assistant — today an exhausted AI budget (GENC-1464).
|
|
344
|
+
*
|
|
345
|
+
* Deliberately NOT a transcript replacement like `compactingTemplate` /
|
|
346
|
+
* `restoringTemplate`: those cover a transient rewrite where the history is
|
|
347
|
+
* mid-flight and unsafe to show, whereas a block is indefinite. Hiding the
|
|
348
|
+
* conversation behind it would strand the user's work behind a wall they
|
|
349
|
+
* cannot clear, so the banner sits alongside a fully readable transcript.
|
|
350
|
+
*
|
|
351
|
+
* `role="status"` (not `alert`) — it is a standing condition the user can also
|
|
352
|
+
* see in the disabled composer, not an interruption.
|
|
353
|
+
*
|
|
354
|
+
* **Rendered unconditionally, empty when unblocked.** This is an accessibility
|
|
355
|
+
* requirement, not a stylistic choice: `role="status"` announces MUTATIONS to a
|
|
356
|
+
* region the assistive technology was already observing. A region INSERTED into
|
|
357
|
+
* the DOM already carrying its text is widely missed across NVDA, JAWS and
|
|
358
|
+
* VoiceOver — and it is the only announcement a screen-reader user gets, since
|
|
359
|
+
* the textarea simultaneously flips to `disabled`, which drops it out of the tab
|
|
360
|
+
* order and means the swapped "AI usage limit reached" placeholder is never
|
|
361
|
+
* voiced (`maybeAutoFocusChatInput` also no-ops against a disabled control). So
|
|
362
|
+
* the region stays mounted and only its TEXT changes.
|
|
363
|
+
*
|
|
364
|
+
* It is deliberately **not** collapsed with `display: none` when unblocked —
|
|
365
|
+
* that would drop it out of the accessibility tree and make the flip an
|
|
366
|
+
* insertion again, defeating the point. Instead the visual treatment (padding,
|
|
367
|
+
* border, background) hangs off the `is-visible` class, so the empty container
|
|
368
|
+
* occupies no space while remaining a live, registered region.
|
|
369
|
+
*
|
|
370
|
+
* It carries {@link BLOCKED_BANNER_ID} so the composer controls can point
|
|
371
|
+
* `aria-describedby` at it — see {@link composerDescribedBy}.
|
|
372
|
+
*/
|
|
373
|
+
const blockedBannerTemplate = html<FoundationAiAssistant>`
|
|
374
|
+
<div
|
|
375
|
+
id="${BLOCKED_BANNER_ID}"
|
|
376
|
+
class="${blockedBannerClasses}"
|
|
377
|
+
part="blocked-banner"
|
|
378
|
+
role="status"
|
|
379
|
+
>
|
|
380
|
+
${when(
|
|
381
|
+
(x) => x.bannerVisible,
|
|
382
|
+
html<FoundationAiAssistant>`
|
|
383
|
+
<span class="blocked-banner-text">${(x) => x.effectiveBlockedReason}</span>
|
|
384
|
+
`,
|
|
385
|
+
)}
|
|
386
|
+
</div>
|
|
387
|
+
`;
|
|
388
|
+
|
|
277
389
|
// ─── Public factory ───────────────────────────────────────────────────────────
|
|
278
390
|
|
|
279
391
|
/** @internal */
|
|
@@ -437,13 +549,18 @@ ${(tc) => (tc.foldPath?.length ? `${tc.foldPath.join(' › ')} › ` : '')}<stro
|
|
|
437
549
|
</${buttonTag}>
|
|
438
550
|
`;
|
|
439
551
|
|
|
552
|
+
// The attach button shares the textarea's disable gate. Attaching a file that
|
|
553
|
+
// can never be sent is a dead end — and `blocked`, `compacting` and `restoring`
|
|
554
|
+
// were all missing from it, so files could be attached during a compaction or
|
|
555
|
+
// against an exhausted budget and then never sent.
|
|
440
556
|
const attachButtonTemplate = html<FoundationAiAssistant>`
|
|
441
557
|
<${buttonTag}
|
|
442
558
|
class="attach-button"
|
|
443
559
|
part="attach-button"
|
|
444
560
|
appearance="stealth"
|
|
445
561
|
title=${(x) => `Attach file (${x.chatConfig.ui?.acceptedFiles})`}
|
|
446
|
-
?disabled=${(x) => x.busy}
|
|
562
|
+
?disabled=${(x) => x.busy || x.compacting || x.restoring || x.blocked}
|
|
563
|
+
aria-describedby=${composerDescribedBy}
|
|
447
564
|
@click=${(x) => x.triggerFileInput()}
|
|
448
565
|
><${iconTag} name="paperclip"></${iconTag}></${buttonTag}>
|
|
449
566
|
`;
|
|
@@ -697,7 +814,7 @@ ${(tc) => (tc.foldPath?.length ? `${tc.foldPath.join(' › ')} › ` : '')}<stro
|
|
|
697
814
|
`,
|
|
698
815
|
)}
|
|
699
816
|
${when(
|
|
700
|
-
(x) => x.chatConfig.suggestions?.behavior !== 'never',
|
|
817
|
+
(x) => x.chatConfig.suggestions?.behavior !== 'never' && !x.suggestionsBlocked,
|
|
701
818
|
html<FoundationAiAssistant>`
|
|
702
819
|
<chat-suggestions
|
|
703
820
|
:state="${(x) => x.suggestionsState}"
|
|
@@ -710,6 +827,12 @@ ${(tc) => (tc.foldPath?.length ? `${tc.foldPath.join(' › ')} › ` : '')}<stro
|
|
|
710
827
|
(x) => x.sessionMenuOpen && !x.busy,
|
|
711
828
|
sessionMenuPanelTemplate,
|
|
712
829
|
)}
|
|
830
|
+
${
|
|
831
|
+
/* Rendered above the composer and independently of it — a host that hides the
|
|
832
|
+
built-in composer (`composerHiddenByConfig`) still needs to be told why its own
|
|
833
|
+
input is being refused. */ ''
|
|
834
|
+
}
|
|
835
|
+
${blockedBannerTemplate}
|
|
713
836
|
${when(
|
|
714
837
|
(x) =>
|
|
715
838
|
!x.composerHiddenByConfig &&
|
|
@@ -743,7 +866,10 @@ ${(tc) => (tc.foldPath?.length ? `${tc.foldPath.join(' › ')} › ` : '')}<stro
|
|
|
743
866
|
style=${(x) => x.composerHeightStyle}
|
|
744
867
|
placeholder=${(x) => x.effectivePlaceholder}
|
|
745
868
|
:value=${(x) => x.inputValue}
|
|
746
|
-
?disabled=${(x) => x.busy || x.compacting || x.restoring}
|
|
869
|
+
?disabled=${(x) => x.busy || x.compacting || x.restoring || x.blocked}
|
|
870
|
+
aria-disabled=${(x) => (x.blocked ? 'true' : null)}
|
|
871
|
+
aria-label=${(x) => (x.blocked ? x.effectiveBlockedReason : null)}
|
|
872
|
+
aria-describedby=${composerDescribedBy}
|
|
747
873
|
@input=${(x, c) => (x.inputValue = (c.event.target as any).value)}
|
|
748
874
|
@keydown=${(x, c) => {
|
|
749
875
|
if (
|
|
@@ -782,7 +908,8 @@ ${(tc) => (tc.foldPath?.length ? `${tc.foldPath.join(' › ')} › ` : '')}<stro
|
|
|
782
908
|
<${buttonTag}
|
|
783
909
|
class="send-button"
|
|
784
910
|
part="send-button"
|
|
785
|
-
?disabled=${(x) => x.busy || x.compacting || x.restoring || (!x.inputValue.trim() && !x.attachments.length)}
|
|
911
|
+
?disabled=${(x) => x.busy || x.compacting || x.restoring || x.blocked || (!x.inputValue.trim() && !x.attachments.length)}
|
|
912
|
+
aria-describedby=${composerDescribedBy}
|
|
786
913
|
@click=${(x) => x.handleSendClick()}
|
|
787
914
|
>Send</${buttonTag}>
|
|
788
915
|
</div>
|