@genesislcap/ai-assistant 15.26.0 → 15.27.0-agent-log-submit.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.
Files changed (52) hide show
  1. package/dist/ai-assistant.api.json +1014 -110
  2. package/dist/ai-assistant.d.ts +236 -99
  3. package/dist/chat-driver.cjs +460 -373
  4. package/dist/chat-driver.cjs.map +4 -4
  5. package/dist/chat-driver.mjs +460 -373
  6. package/dist/chat-driver.mjs.map +4 -4
  7. package/dist/custom-elements.json +1171 -248
  8. package/dist/dts/components/chat-driver/chat-driver.d.ts +28 -200
  9. package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
  10. package/dist/dts/components/chat-driver/interaction-controller.d.ts +99 -0
  11. package/dist/dts/components/chat-driver/interaction-controller.d.ts.map +1 -0
  12. package/dist/dts/components/chat-driver/turn-recorder.d.ts +191 -0
  13. package/dist/dts/components/chat-driver/turn-recorder.d.ts.map +1 -0
  14. package/dist/dts/components/settings-modal/settings-modal.d.ts.map +1 -1
  15. package/dist/dts/components/settings-modal/settings-modal.styles.d.ts.map +1 -1
  16. package/dist/dts/components/settings-modal/settings-modal.template.d.ts +28 -0
  17. package/dist/dts/components/settings-modal/settings-modal.template.d.ts.map +1 -1
  18. package/dist/dts/index.d.ts +1 -0
  19. package/dist/dts/index.d.ts.map +1 -1
  20. package/dist/dts/main/main.d.ts +180 -1
  21. package/dist/dts/main/main.d.ts.map +1 -1
  22. package/dist/dts/main/main.styles.d.ts.map +1 -1
  23. package/dist/dts/main/main.template.d.ts +19 -0
  24. package/dist/dts/main/main.template.d.ts.map +1 -1
  25. package/dist/dts/state/ai-assistant-slice.d.ts +7 -1
  26. package/dist/dts/state/ai-assistant-slice.d.ts.map +1 -1
  27. package/dist/dts/state/persistence/session-persistence-provider.d.ts +5 -0
  28. package/dist/dts/state/persistence/session-persistence-provider.d.ts.map +1 -1
  29. package/dist/dts/state/session-store.d.ts +1 -0
  30. package/dist/dts/state/session-store.d.ts.map +1 -1
  31. package/dist/dts/utils/format-message-timestamp.d.ts +77 -0
  32. package/dist/dts/utils/format-message-timestamp.d.ts.map +1 -0
  33. package/dist/dts/utils/resolve-debug-log-note.d.ts +25 -0
  34. package/dist/dts/utils/resolve-debug-log-note.d.ts.map +1 -0
  35. package/dist/dts/utils/resolve-preference-baseline.d.ts +18 -1
  36. package/dist/dts/utils/resolve-preference-baseline.d.ts.map +1 -1
  37. package/dist/esm/components/chat-driver/chat-driver.js +68 -404
  38. package/dist/esm/components/chat-driver/interaction-controller.js +235 -0
  39. package/dist/esm/components/chat-driver/turn-recorder.js +206 -0
  40. package/dist/esm/components/settings-modal/settings-modal.js +1 -0
  41. package/dist/esm/components/settings-modal/settings-modal.styles.js +85 -5
  42. package/dist/esm/components/settings-modal/settings-modal.template.js +220 -25
  43. package/dist/esm/main/main.js +510 -23
  44. package/dist/esm/main/main.styles.js +75 -0
  45. package/dist/esm/main/main.template.js +43 -2
  46. package/dist/esm/state/ai-assistant-slice.js +5 -0
  47. package/dist/esm/utils/format-message-timestamp.js +126 -0
  48. package/dist/esm/utils/resolve-debug-log-note.js +37 -0
  49. package/dist/esm/utils/resolve-preference-baseline.js +54 -2
  50. package/package.json +16 -16
  51. package/sandbox/README.md +45 -2
  52. package/sandbox/sandbox.ts +95 -3
@@ -31,6 +31,54 @@ const toggleAnimationEnabled = (host, value, enabled) => {
31
31
  host.setEnabledAnimations(next);
32
32
  };
33
33
  const readSwitchChecked = (event) => event.target.checked;
34
+ /**
35
+ * The timestamp modes, in the order they read as an increasing amount of chrome.
36
+ *
37
+ * Keyed by {@link TimestampDisplay} rather than listed as an array of `{ value, label }`,
38
+ * so adding a mode to the union is a compile error here instead of a picker that silently
39
+ * cannot reach it.
40
+ */
41
+ const TIMESTAMP_MODE_LABELS = {
42
+ off: 'Off',
43
+ hover: 'On hover',
44
+ always: 'Always',
45
+ };
46
+ const TIMESTAMP_MODE_OPTIONS = Object.keys(TIMESTAMP_MODE_LABELS).map((value) => ({ value, label: TIMESTAMP_MODE_LABELS[value] }));
47
+ /**
48
+ * Three-way picker for {@link TimestampDisplay} (GENC-1257).
49
+ *
50
+ * The design system's segmented control rather than the `switch` every other row uses,
51
+ * because the setting genuinely has three states — and rather than hand-rolled buttons
52
+ * carrying `role="radio"`, because ARIA roles supply semantics but never behaviour:
53
+ * promising a radio group means owning roving `tabindex` and arrow-key navigation.
54
+ *
55
+ * `SegmentedControl extends FASTRadioGroup` (and `SegmentedItem extends FASTRadio`), so
56
+ * this IS the DS radio group — it inherits the whole keyboard pattern — just skinned as
57
+ * segments rather than dots, which is the right affordance for three short mode labels.
58
+ *
59
+ * The usual reason to avoid a DS control here does not apply. `RadioGroup.valueChanged`
60
+ * emits `change` whenever `value` is written — including the write this template's own
61
+ * binding performs on render, exactly the phantom-change footgun the DS switch has
62
+ * (GENC-1351 #1). It is harmless because the `timestampDisplay` setter guards on
63
+ * `preferenceUnchanged`, as every preference setter does, so a render-time echo returns early
64
+ * and never reaches the store or marks preferences user-changed.
65
+ */
66
+ export const timestampModePickerTemplate = (segmentedControlTag, segmentedItemTag) => html `
67
+ <${segmentedControlTag}
68
+ part="toggle-timestamps"
69
+ aria-label="Show timestamps"
70
+ :value=${(x) => x.timestampDisplay}
71
+ @change=${(x, c) => {
72
+ x.timestampDisplay = c.event.target.value;
73
+ }}
74
+ >
75
+ ${repeat(() => TIMESTAMP_MODE_OPTIONS, html `
76
+ <${segmentedItemTag} part="timestamp-mode-option" value="${(option) => option.value}"
77
+ >${(option) => option.label}</${segmentedItemTag}
78
+ >
79
+ `)}
80
+ </${segmentedControlTag}>
81
+ `;
34
82
  /** Inline animation picker — categories and options always visible (no multiselect popup). */
35
83
  const animationsPickerTemplate = (switchTag) => html `
36
84
  <div class="settings-animation-list" part="toggle-animations">
@@ -198,7 +246,7 @@ export const settingsUsageMetersTemplate = (progressTag, buttonTag, iconTag) =>
198
246
  </div>
199
247
  `)}
200
248
  `;
201
- const chatBotSettingsTemplate = (switchTag, buttonTag) => html `
249
+ const chatBotSettingsTemplate = (switchTag, buttonTag, segmentedControlTag, segmentedItemTag) => html `
202
250
  ${when((x) => { var _a; return ((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.showToolCalls) != null; }, chatBotToggleRow('Show tool calls', "Display each tool the chatbot invokes — file reads, searches, edits and commands — inline as it works, so you can follow exactly what it's doing behind the scenes.", switchTag, 'toggle-tool-calls', (x) => x.showToolCalls, (x, c) => {
203
251
  x.showToolCalls = readSwitchChecked(c.event);
204
252
  }))}
@@ -211,6 +259,7 @@ const chatBotSettingsTemplate = (switchTag, buttonTag) => html `
211
259
  ${when((x) => { var _a; return ((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.showAgentSwitchIndicator) != null; }, chatBotToggleRow('Show delegation', 'Show in the chat window when tasks are delegated to different agents.', switchTag, 'toggle-agent-switch', (x) => x.showAgentSwitchIndicator, (x, c) => {
212
260
  x.showAgentSwitchIndicator = readSwitchChecked(c.event);
213
261
  }))}
262
+ ${settingsToggleRow('Show timestamps', 'Show when each message was sent, next to the sender name. Screen readers announce the full date in either visible mode.', 'toggle-timestamps', timestampModePickerTemplate(segmentedControlTag, segmentedItemTag))}
214
263
  ${when((x) => x.persistenceToggleable, chatBotToggleRow('Remember chat', 'Save this conversation so it can be restored after a page refresh. Turning this off deletes the saved copy.', switchTag, 'toggle-remember-chat', (x) => x.persistenceActive, (x, c) => {
215
264
  x.setPersistenceEnabled(readSwitchChecked(c.event));
216
265
  }))}
@@ -513,6 +562,171 @@ const costSessionHistoryTemplate = (iconTag, buttonTag) => html `
513
562
  </div>
514
563
  `)}
515
564
  `;
565
+ /**
566
+ * The settings "Agent log" section: the Download / Send row, the always-present status
567
+ * live region, and the Send button's compose box. Exported (like
568
+ * `settingsUsageMetersTemplate`) so `agent-log-section.template.test.ts` can render it on
569
+ * an unconnected element and pin what the getters alone cannot show — that the `when`s,
570
+ * `?disabled`s and part tokens are actually wired to them.
571
+ */
572
+ export const agentLogSectionTemplate = (buttonTag, textareaTag) => html `
573
+ ${when((x) => x.settingsAgentLogSectionVisible, html `
574
+ <section class="settings-modal-section" part="settings-section-agent-log">
575
+ <div class="settings-modal-section-body">
576
+ <!--
577
+ Row and compose box share a wrapper so the box can drop straight under the
578
+ row (full width, no divider between them) while the row keeps its layout.
579
+ -->
580
+ <div class="agent-log-block" part="agent-log-block">
581
+ ${settingsToggleRow('Agent log', (x) => x.debugLogRowDescription,
582
+ // The helper appends "-row", so this renders as
583
+ // part="download-agent-log-row agent-log-row": the original token stays
584
+ // for hosts already styling it, and `agent-log-row` names what the row
585
+ // now is — the container of both actions. Pinned in
586
+ // agent-log-section.template.test.ts.
587
+ 'download-agent-log-row agent-log', html `
588
+ <div class="agent-log-actions" part="agent-log-actions">
589
+ ${when((x) => { var _a; return ((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.allowDebugDownload) === true; }, html `
590
+ <${buttonTag}
591
+ part="download-button"
592
+ appearance="outline"
593
+ ?disabled=${(x) => x.downloadingDebugLog}
594
+ @click=${(x) => x.downloadDebugLog()}
595
+ >
596
+ ${when((x) => x.downloadingDebugLog, html `
597
+ <span
598
+ class="download-spinner"
599
+ part="download-spinner"
600
+ aria-hidden="true"
601
+ ></span>
602
+ `)}
603
+ ${(x) => (x.downloadingDebugLog ? 'Downloading…' : 'Download')}
604
+ </${buttonTag}>
605
+ `)}
606
+ ${when((x) => { var _a; return ((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.debugLogSubmit) != null; }, html `
607
+ <${buttonTag}
608
+ part="submit-log-button"
609
+ appearance="outline"
610
+ ?disabled=${(x) => x.submittingDebugLog || x.debugLogComposeOpen}
611
+ aria-expanded=${(x) => x.debugLogNoteConfig ? String(x.debugLogComposeOpen) : null}
612
+ @click=${(x) => x.openDebugLogCompose()}
613
+ >
614
+ ${when((x) => x.submittingDebugLog && !x.debugLogComposeOpen, html `
615
+ <span
616
+ class="download-spinner"
617
+ part="download-spinner"
618
+ aria-hidden="true"
619
+ ></span>
620
+ `)}
621
+ ${(x) => x.debugLogSubmitButtonLabel}
622
+ </${buttonTag}>
623
+ `)}
624
+ </div>
625
+ `, 'row-centered')}
626
+ <!--
627
+ Present whenever Send is configured (not gated on the compose box) so a live region exists
628
+ BEFORE an outcome lands in it — one that appears together with its text is
629
+ not announced — and so a note-less (note: false) host, whose only visible change is
630
+ the button label, is announced too.
631
+ -->
632
+ ${when((x) => { var _a; return ((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.debugLogSubmit) != null; }, html `
633
+ <span
634
+ class="visually-hidden"
635
+ part="agent-log-status"
636
+ role="status"
637
+ aria-live="polite"
638
+ >
639
+ ${(x) => x.debugLogSubmitAnnouncement}
640
+ </span>
641
+ `)}
642
+ ${when((x) => x.debugLogComposeOpen && x.debugLogNoteConfig !== null, html `
643
+ <!--
644
+ Keys are handled on the wrapper so they mean the same from every control:
645
+ Escape cancels the box (and does not reach the dialog, which would close
646
+ the whole modal and lose the note) whether focus is on the note, Cancel or
647
+ Send; Enter submits only from the note, and only when a submit would
648
+ actually go — otherwise the key keeps its default so it is not simply dead.
649
+ -->
650
+ <div
651
+ class="agent-log-compose"
652
+ part="agent-log-compose"
653
+ @keydown=${(x, c) => {
654
+ const e = c.event;
655
+ if (e.key === 'Escape') {
656
+ x.cancelDebugLogCompose();
657
+ return false;
658
+ }
659
+ if (e.key === 'Enter' &&
660
+ !e.shiftKey &&
661
+ e.target === x.debugLogNoteEl &&
662
+ !x.debugLogSendDisabled) {
663
+ void x.submitDebugLog();
664
+ return false;
665
+ }
666
+ return true;
667
+ }}
668
+ >
669
+ <${textareaTag}
670
+ ${ref('debugLogNoteEl')}
671
+ class="agent-log-note"
672
+ part="agent-log-note"
673
+ rows="3"
674
+ resize="vertical"
675
+ aria-label=${(x) => x.debugLogNoteAriaLabel}
676
+ placeholder=${(x) => { var _a; return (_a = x.debugLogNoteConfig) === null || _a === void 0 ? void 0 : _a.placeholder; }}
677
+ maxlength=${(x) => { var _a; return (_a = x.debugLogNoteConfig) === null || _a === void 0 ? void 0 : _a.maxLength; }}
678
+ :value=${(x) => x.debugLogNote}
679
+ ?disabled=${(x) => x.submittingDebugLog}
680
+ @input=${(x, c) => x.noteDebugLogInput(c.event.target.value)}
681
+ ></${textareaTag}>
682
+ <div class="agent-log-compose-footer">
683
+ <!--
684
+ Visual hint only (aria-hidden): a live counter would be announced on
685
+ every keystroke, and an IDREF from the text-area cannot reach across
686
+ its shadow boundary. AT gets the cap from the note's static aria-label,
687
+ the remaining count near the cap and every outcome from the row's
688
+ status region.
689
+ -->
690
+ <span
691
+ class="agent-log-compose-hint ${(x) => x.debugLogSubmitResult === 'error' ? 'is-error' : ''}"
692
+ part="agent-log-compose-hint"
693
+ aria-hidden="true"
694
+ >
695
+ ${(x) => x.debugLogComposeHint}
696
+ </span>
697
+ <div class="agent-log-actions">
698
+ <${buttonTag}
699
+ part="agent-log-cancel-button"
700
+ appearance="outline"
701
+ ?disabled=${(x) => x.submittingDebugLog}
702
+ @click=${(x) => x.cancelDebugLogCompose()}
703
+ >
704
+ Cancel
705
+ </${buttonTag}>
706
+ <${buttonTag}
707
+ part="agent-log-send-button"
708
+ appearance="accent"
709
+ ?disabled=${(x) => x.debugLogSendDisabled}
710
+ @click=${(x) => x.submitDebugLog()}
711
+ >
712
+ ${when((x) => x.submittingDebugLog, html `
713
+ <span
714
+ class="download-spinner"
715
+ part="download-spinner"
716
+ aria-hidden="true"
717
+ ></span>
718
+ `)}
719
+ ${(x) => (x.submittingDebugLog ? 'Sending…' : x.debugLogSubmitLabel)}
720
+ </${buttonTag}>
721
+ </div>
722
+ </div>
723
+ </div>
724
+ `)}
725
+ </div>
726
+ </div>
727
+ </section>
728
+ `)}
729
+ `;
516
730
  /**
517
731
  * Modal shell for assistant settings — Configuration and Usage tabs, section slots for
518
732
  * app-specific UI, and built-in chat-bot toggles.
@@ -523,9 +737,12 @@ export const SettingsModalTemplate = (designSystemPrefix) => {
523
737
  const switchTag = `${designSystemPrefix}-switch`;
524
738
  const iconTag = `${designSystemPrefix}-icon`;
525
739
  const progressTag = `${designSystemPrefix}-progress`;
740
+ const textareaTag = `${designSystemPrefix}-text-area`;
526
741
  const tabsTag = `${designSystemPrefix}-tabs`;
527
742
  const tabTag = `${designSystemPrefix}-tab`;
528
743
  const tabPanelTag = `${designSystemPrefix}-tab-panel`;
744
+ const segmentedControlTag = `${designSystemPrefix}-segmented-control`;
745
+ const segmentedItemTag = `${designSystemPrefix}-segmented-item`;
529
746
  const settingsTabPanel = html `
530
747
  <${tabPanelTag}
531
748
  slot="tabpanel"
@@ -551,29 +768,7 @@ export const SettingsModalTemplate = (designSystemPrefix) => {
551
768
  with the assistant's other sections rather than trailing the host's — where, now that the
552
769
  host section carries no heading of its own, it read as one more of the host's settings.
553
770
  -->
554
- ${when((x) => { var _a; return ((_a = x.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.allowDebugDownload) === true; }, html `
555
- <section class="settings-modal-section" part="settings-section-agent-log">
556
- <div class="settings-modal-section-body">
557
- ${settingsToggleRow('Download agent log', "Export a structured debug timeline of this session's agent activity.", 'download-agent-log', html `
558
- <${buttonTag}
559
- part="download-button"
560
- appearance="outline"
561
- ?disabled=${(x) => x.downloadingDebugLog}
562
- @click=${(x) => x.downloadDebugLog()}
563
- >
564
- ${when((x) => x.downloadingDebugLog, html `
565
- <span
566
- class="download-spinner"
567
- part="download-spinner"
568
- aria-hidden="true"
569
- ></span>
570
- `)}
571
- ${(x) => (x.downloadingDebugLog ? 'Downloading…' : 'Download')}
572
- </${buttonTag}>
573
- `, 'row-centered')}
574
- </div>
575
- </section>
576
- `)}
771
+ ${agentLogSectionTemplate(buttonTag, textareaTag)}
577
772
  <!--
578
773
  No heading here, by design. This section belongs to the host: only it knows what its
579
774
  settings are called, so the assistant supplies neither text nor icon and the host titles
@@ -595,7 +790,7 @@ export const SettingsModalTemplate = (designSystemPrefix) => {
595
790
  </h3>
596
791
  <p class="settings-modal-section-note">These options only control what you see in the chat window and do not control model behaviour.</p>
597
792
  <div class="settings-modal-section-body">
598
- ${chatBotSettingsTemplate(switchTag, buttonTag)}
793
+ ${chatBotSettingsTemplate(switchTag, buttonTag, segmentedControlTag, segmentedItemTag)}
599
794
  </div>
600
795
  </section>
601
796
  </div>