@genesislcap/ai-assistant 15.59.0 → 15.61.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/dist/ai-assistant.d.ts +17 -0
- package/dist/custom-elements.json +646 -248
- package/dist/dts/components/message-feedback/index.d.ts +2 -0
- package/dist/dts/components/message-feedback/index.d.ts.map +1 -0
- package/dist/dts/components/message-feedback/message-feedback.d.ts +61 -0
- package/dist/dts/components/message-feedback/message-feedback.d.ts.map +1 -0
- package/dist/dts/components/message-feedback/message-feedback.styles.d.ts +2 -0
- package/dist/dts/components/message-feedback/message-feedback.styles.d.ts.map +1 -0
- package/dist/dts/components/message-feedback/message-feedback.template.d.ts +4 -0
- package/dist/dts/components/message-feedback/message-feedback.template.d.ts.map +1 -0
- package/dist/dts/main/main.d.ts +17 -1
- 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.template.d.ts.map +1 -1
- package/dist/dts/react.d.ts +10 -0
- package/dist/esm/components/message-feedback/index.js +1 -0
- package/dist/esm/components/message-feedback/message-feedback.js +241 -0
- package/dist/esm/components/message-feedback/message-feedback.styles.js +94 -0
- package/dist/esm/components/message-feedback/message-feedback.template.js +80 -0
- package/dist/esm/main/main.js +44 -1
- package/dist/esm/main/main.styles.js +27 -0
- package/dist/esm/main/main.template.js +13 -0
- package/dist/react.cjs +7 -0
- package/dist/react.mjs +6 -0
- package/package.json +17 -17
- package/sandbox/README.md +1 -1
- package/sandbox/sandbox.ts +35 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { html, ref, when } from '@genesislcap/web-core';
|
|
2
|
+
import { DEBUG_LOG_NOTE_DEFAULT_MAX_LENGTH } from '../../utils/resolve-debug-log-note';
|
|
3
|
+
const thumbLabel = {
|
|
4
|
+
positive: 'Good response',
|
|
5
|
+
negative: 'Bad response',
|
|
6
|
+
};
|
|
7
|
+
const panelTitle = {
|
|
8
|
+
positive: 'What went well?',
|
|
9
|
+
negative: 'What went wrong?',
|
|
10
|
+
};
|
|
11
|
+
export const MessageFeedbackTemplate = (designSystemPrefix) => {
|
|
12
|
+
const buttonTag = `${designSystemPrefix}-button`;
|
|
13
|
+
const textareaTag = `${designSystemPrefix}-text-area`;
|
|
14
|
+
const iconTag = `${designSystemPrefix}-icon`;
|
|
15
|
+
const thumb = (rating) => html `
|
|
16
|
+
<button
|
|
17
|
+
type="button"
|
|
18
|
+
class="thumb"
|
|
19
|
+
part="message-feedback-button message-feedback-${rating}"
|
|
20
|
+
aria-label="${thumbLabel[rating]}"
|
|
21
|
+
title="${(x) => x.sentRating === rating && !x.open ? 'Thanks for your feedback' : thumbLabel[rating]}"
|
|
22
|
+
aria-pressed="${(x) => String(x.activeRating === rating)}"
|
|
23
|
+
?disabled="${(x) => x.status === 'sending'}"
|
|
24
|
+
@click="${(x) => x.toggle(rating)}"
|
|
25
|
+
>
|
|
26
|
+
<${iconTag}
|
|
27
|
+
name="${rating === 'positive' ? 'thumbs-up' : 'thumbs-down'}"
|
|
28
|
+
variant="${(x) => (x.activeRating === rating ? 'solid' : 'regular')}"
|
|
29
|
+
></${iconTag}>
|
|
30
|
+
</button>
|
|
31
|
+
`;
|
|
32
|
+
return html `
|
|
33
|
+
<div class="root" @keydown="${(x, c) => x.handleKeydown(c.event)}">
|
|
34
|
+
<div class="thumbs" part="message-feedback-thumbs">
|
|
35
|
+
${thumb('positive')}${thumb('negative')}
|
|
36
|
+
</div>
|
|
37
|
+
<span class="visually-hidden" role="status" aria-live="polite">${(x) => x.announcement}</span>
|
|
38
|
+
${when((x) => x.open && x.draftRating !== null, html `
|
|
39
|
+
<div class="panel" part="message-feedback-panel" role="group" aria-label="Send feedback">
|
|
40
|
+
<p class="panel-title" part="message-feedback-title">
|
|
41
|
+
${(x) => panelTitle[x.draftRating]}
|
|
42
|
+
</p>
|
|
43
|
+
<${textareaTag}
|
|
44
|
+
${ref('noteEl')}
|
|
45
|
+
class="note"
|
|
46
|
+
part="message-feedback-note"
|
|
47
|
+
rows="3"
|
|
48
|
+
resize="vertical"
|
|
49
|
+
aria-label="Feedback note (optional)"
|
|
50
|
+
placeholder="Add a note (optional)"
|
|
51
|
+
maxlength="${() => DEBUG_LOG_NOTE_DEFAULT_MAX_LENGTH}"
|
|
52
|
+
:value="${(x) => x.note}"
|
|
53
|
+
?disabled="${(x) => x.status === 'sending'}"
|
|
54
|
+
@input="${(x, c) => x.noteInput(c.event.target.value)}"
|
|
55
|
+
></${textareaTag}>
|
|
56
|
+
<p class="hint ${(x) => (x.status === 'error' ? 'is-error' : '')}" part="message-feedback-hint">
|
|
57
|
+
${(x) => x.status === 'error' ? "Couldn't send your feedback. Try again." : x.description}
|
|
58
|
+
</p>
|
|
59
|
+
<div class="actions">
|
|
60
|
+
<${buttonTag}
|
|
61
|
+
part="message-feedback-cancel-button"
|
|
62
|
+
appearance="outline"
|
|
63
|
+
@click="${(x) => x.cancel()}"
|
|
64
|
+
>
|
|
65
|
+
Cancel
|
|
66
|
+
</${buttonTag}>
|
|
67
|
+
<${buttonTag}
|
|
68
|
+
part="message-feedback-send-button"
|
|
69
|
+
appearance="accent"
|
|
70
|
+
?disabled="${(x) => x.status === 'sending'}"
|
|
71
|
+
@click="${(x) => x.send()}"
|
|
72
|
+
>
|
|
73
|
+
${(x) => x.status === 'sending' ? 'Sending…' : x.status === 'error' ? 'Retry' : 'Send'}
|
|
74
|
+
</${buttonTag}>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
`)}
|
|
78
|
+
</div>
|
|
79
|
+
`;
|
|
80
|
+
};
|
package/dist/esm/main/main.js
CHANGED
|
@@ -36,6 +36,7 @@ import { AiChatInteractionWrapper } from '../components/chat-interaction-wrapper
|
|
|
36
36
|
import { AiChatMarkdown } from '../components/chat-markdown/chat-markdown';
|
|
37
37
|
import { AiFlowingWavesIndicator } from '../components/flowing-waves-indicator';
|
|
38
38
|
import { AiHaloOverlay } from '../components/halo-overlay';
|
|
39
|
+
import { isRateableReply, MessageFeedback, replyKey, } from '../components/message-feedback/message-feedback';
|
|
39
40
|
import { asOrchestrator, OrchestratingDriver, } from '../components/orchestrating-driver/orchestrating-driver';
|
|
40
41
|
import { AiPlasmaOrbIndicator } from '../components/plasma-orb-indicator';
|
|
41
42
|
import { SettingsModalController, } from '../components/settings-modal';
|
|
@@ -165,7 +166,7 @@ const TIMESTAMP_CLOCK_INTERVAL_MS = 30000;
|
|
|
165
166
|
*/
|
|
166
167
|
export { budgetPercentOf, formatBlockedReason } from './budget-controller';
|
|
167
168
|
// Register supporting components when the main component module is imported.
|
|
168
|
-
avoidTreeShaking(AiChatMarkdown, AiChatInteractionWrapper, AiHaloOverlay, AiWavesIndicator, AiFlowingWavesIndicator, AiPlasmaOrbIndicator, AiChatBubble, AiActivityHalo, ChatSuggestions, AgentPicker);
|
|
169
|
+
avoidTreeShaking(AiChatMarkdown, AiChatInteractionWrapper, AiHaloOverlay, AiWavesIndicator, AiFlowingWavesIndicator, AiPlasmaOrbIndicator, AiChatBubble, AiActivityHalo, ChatSuggestions, AgentPicker, MessageFeedback);
|
|
169
170
|
/**
|
|
170
171
|
* Foundation AI Assistant component.
|
|
171
172
|
*
|
|
@@ -3681,6 +3682,48 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
3681
3682
|
const stem = this.debugLogAgentName.toLowerCase().replace(/\s+/g, '-');
|
|
3682
3683
|
return `${stem}-${new Date().toISOString().replace(/:/g, '-')}.json`;
|
|
3683
3684
|
}
|
|
3685
|
+
/**
|
|
3686
|
+
* The most recent rateable reply, whose thumbs stay visible without a hover.
|
|
3687
|
+
*
|
|
3688
|
+
* @internal
|
|
3689
|
+
*/
|
|
3690
|
+
get latestReply() {
|
|
3691
|
+
for (let i = this.messages.length - 1; i >= 0; i -= 1) {
|
|
3692
|
+
if (isRateableReply(this.messages[i]))
|
|
3693
|
+
return this.messages[i];
|
|
3694
|
+
}
|
|
3695
|
+
return undefined;
|
|
3696
|
+
}
|
|
3697
|
+
/** @internal */
|
|
3698
|
+
get messageFeedbackDescription() {
|
|
3699
|
+
var _a, _b, _c;
|
|
3700
|
+
return ((_c = (_b = (_a = this.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.messageFeedback) === null || _b === void 0 ? void 0 : _b.description) !== null && _c !== void 0 ? _c : 'Sends your rating and note on this reply.');
|
|
3701
|
+
}
|
|
3702
|
+
/**
|
|
3703
|
+
* Hand a rated reply to the host's `ui.messageFeedback.send`, with a getter for the same
|
|
3704
|
+
* lifetime log the agent-log Send button submits — built only if the host asks for it.
|
|
3705
|
+
* Rejects when the host does, or does not settle in time.
|
|
3706
|
+
*
|
|
3707
|
+
* @internal
|
|
3708
|
+
*/
|
|
3709
|
+
submitMessageFeedback(reply, rating, note) {
|
|
3710
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3711
|
+
var _a, _b, _c;
|
|
3712
|
+
const config = (_a = this.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.messageFeedback;
|
|
3713
|
+
if (!config)
|
|
3714
|
+
throw new Error('ui.messageFeedback is not configured');
|
|
3715
|
+
yield settleWithin(config.send({
|
|
3716
|
+
rating,
|
|
3717
|
+
reply,
|
|
3718
|
+
replyKey: replyKey(reply),
|
|
3719
|
+
message: note,
|
|
3720
|
+
filename: this.debugLogFilename(),
|
|
3721
|
+
agentName: (_b = reply.agentName) !== null && _b !== void 0 ? _b : this.debugLogAgentName,
|
|
3722
|
+
host: window.location.host,
|
|
3723
|
+
getLogJson: () => __awaiter(this, void 0, void 0, function* () { return JSON.stringify(yield this.getLifetimeDebugLog(), null, 2); }),
|
|
3724
|
+
}), (_c = config.timeoutMs) !== null && _c !== void 0 ? _c : DEBUG_LOG_SUBMIT_TIMEOUT_MS, 'messageFeedback.send');
|
|
3725
|
+
});
|
|
3726
|
+
}
|
|
3684
3727
|
/**
|
|
3685
3728
|
* Back to idle: nothing in flight, no flash, no open compose box, no note. Called on
|
|
3686
3729
|
* disconnect and on a session switch / Clear (a note typed against one conversation
|
|
@@ -459,6 +459,33 @@ const baseStyles = css `
|
|
|
459
459
|
}
|
|
460
460
|
}
|
|
461
461
|
|
|
462
|
+
/* Revealed like the 'hover' timestamps, with space reserved so the bubble never reflows.
|
|
463
|
+
Always shown on the latest reply, once rated, and while the note panel is open. */
|
|
464
|
+
.message-feedback {
|
|
465
|
+
opacity: 0%;
|
|
466
|
+
transition: opacity 0.12s ease-out;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
.message-row:hover .message-feedback,
|
|
470
|
+
.message-row:focus-within .message-feedback,
|
|
471
|
+
.message-feedback[latest],
|
|
472
|
+
.message-feedback[open],
|
|
473
|
+
.message-feedback[rated] {
|
|
474
|
+
opacity: 100%;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
@media (hover: none) {
|
|
478
|
+
.message-feedback {
|
|
479
|
+
opacity: 100%;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
@media (prefers-reduced-motion: reduce) {
|
|
484
|
+
.message-feedback {
|
|
485
|
+
transition: none;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
462
489
|
.payload {
|
|
463
490
|
font-family: monospace;
|
|
464
491
|
white-space: pre-wrap;
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
import { isChatToolCallUnknown } from '@genesislcap/foundation-ai';
|
|
18
18
|
import { classNames, html, ref, repeat, slotted, when } from '@genesislcap/web-core';
|
|
19
19
|
import { MARKDOWN_EXPORTPARTS } from '../components/chat-markdown/chat-markdown';
|
|
20
|
+
import { isRateableReply, MESSAGE_FEEDBACK_EXPORTPARTS, } from '../components/message-feedback/message-feedback';
|
|
20
21
|
import { SettingsModalTemplate } from '../components/settings-modal';
|
|
21
22
|
import { SUGGESTIONS_EXPORTPARTS } from '../suggestions/chat-suggestions';
|
|
22
23
|
import { formatAbsoluteTimestamp, formatMessageTimestamp, messageTimestampDateTime, } from '../utils/format-message-timestamp';
|
|
@@ -651,6 +652,18 @@ ${(tc) => { var _a; return (((_a = tc.foldPath) === null || _a === void 0 ? void
|
|
|
651
652
|
`)}
|
|
652
653
|
${when((m) => m.interaction, interactionWrapperTemplate)}
|
|
653
654
|
</div>
|
|
655
|
+
${when((m, c) => { var _a; return isRateableReply(m) && ((_a = c.parent.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.messageFeedback) != null; }, html `
|
|
656
|
+
<foundation-ai-message-feedback
|
|
657
|
+
class="message-feedback"
|
|
658
|
+
part="message-feedback"
|
|
659
|
+
exportparts="${MESSAGE_FEEDBACK_EXPORTPARTS}"
|
|
660
|
+
?latest="${(m, c) => m === c.parent.latestReply}"
|
|
661
|
+
:designSystemPrefix="${(_, c) => c.parent.designSystemPrefix}"
|
|
662
|
+
:description="${(_, c) => c.parent.messageFeedbackDescription}"
|
|
663
|
+
:submit="${(_, c) => c.parent.submitMessageFeedback.bind(c.parent)}"
|
|
664
|
+
:message="${(m) => m}"
|
|
665
|
+
></foundation-ai-message-feedback>
|
|
666
|
+
`)}
|
|
654
667
|
</div>
|
|
655
668
|
</div>
|
|
656
669
|
`)}
|
package/dist/react.cjs
CHANGED
|
@@ -13,6 +13,7 @@ const { AiChatInteractionWrapper: AiChatInteractionWrapperWC } = require('./esm/
|
|
|
13
13
|
const { AiChatMarkdown: AiChatMarkdownWC } = require('./esm/components/chat-markdown/chat-markdown.js');
|
|
14
14
|
const { AiFlowingWavesIndicator: AiFlowingWavesIndicatorWC } = require('./esm/components/flowing-waves-indicator.js');
|
|
15
15
|
const { AiHaloOverlay: AiHaloOverlayWC } = require('./esm/components/halo-overlay.js');
|
|
16
|
+
const { MessageFeedback: MessageFeedbackWC } = require('./esm/components/message-feedback/message-feedback.js');
|
|
16
17
|
const { AiPlasmaOrbIndicator: AiPlasmaOrbIndicatorWC } = require('./esm/components/plasma-orb-indicator.js');
|
|
17
18
|
const { FoundationAiPopoutManager: FoundationAiPopoutManagerWC } = require('./esm/components/popout-manager/popout-manager.js');
|
|
18
19
|
const { AiWavesIndicator: AiWavesIndicatorWC } = require('./esm/components/waves-indicator.js');
|
|
@@ -140,6 +141,11 @@ const AiChatMarkdown = React.forwardRef(function AiChatMarkdown(props, ref) {
|
|
|
140
141
|
return React.createElement(customElements.getName(AiChatMarkdownWC) ?? 'ai-chat-markdown', { ...rest, ref }, children);
|
|
141
142
|
});
|
|
142
143
|
|
|
144
|
+
const MessageFeedback = React.forwardRef(function MessageFeedback(props, ref) {
|
|
145
|
+
const { children, ...rest } = props;
|
|
146
|
+
return React.createElement(customElements.getName(MessageFeedbackWC) ?? 'foundation-ai-message-feedback', { ...rest, ref }, children);
|
|
147
|
+
});
|
|
148
|
+
|
|
143
149
|
const FoundationAiPopoutManager = React.forwardRef(function FoundationAiPopoutManager(props, ref) {
|
|
144
150
|
const { children, ...rest } = props;
|
|
145
151
|
return React.createElement(customElements.getName(FoundationAiPopoutManagerWC) ?? 'foundation-ai-popout-manager', { ...rest, ref }, children);
|
|
@@ -174,6 +180,7 @@ module.exports = {
|
|
|
174
180
|
AiChatBubble,
|
|
175
181
|
AiChatInteractionWrapper,
|
|
176
182
|
AiChatMarkdown,
|
|
183
|
+
MessageFeedback,
|
|
177
184
|
FoundationAiPopoutManager,
|
|
178
185
|
GenesisWriteReview,
|
|
179
186
|
};
|
package/dist/react.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import { AiChatInteractionWrapper as AiChatInteractionWrapperWC } from './esm/co
|
|
|
11
11
|
import { AiChatMarkdown as AiChatMarkdownWC } from './esm/components/chat-markdown/chat-markdown.js';
|
|
12
12
|
import { AiFlowingWavesIndicator as AiFlowingWavesIndicatorWC } from './esm/components/flowing-waves-indicator.js';
|
|
13
13
|
import { AiHaloOverlay as AiHaloOverlayWC } from './esm/components/halo-overlay.js';
|
|
14
|
+
import { MessageFeedback as MessageFeedbackWC } from './esm/components/message-feedback/message-feedback.js';
|
|
14
15
|
import { AiPlasmaOrbIndicator as AiPlasmaOrbIndicatorWC } from './esm/components/plasma-orb-indicator.js';
|
|
15
16
|
import { FoundationAiPopoutManager as FoundationAiPopoutManagerWC } from './esm/components/popout-manager/popout-manager.js';
|
|
16
17
|
import { AiWavesIndicator as AiWavesIndicatorWC } from './esm/components/waves-indicator.js';
|
|
@@ -138,6 +139,11 @@ export const AiChatMarkdown = React.forwardRef(function AiChatMarkdown(props, re
|
|
|
138
139
|
return React.createElement(customElements.getName(AiChatMarkdownWC) ?? 'ai-chat-markdown', { ...rest, ref }, children);
|
|
139
140
|
});
|
|
140
141
|
|
|
142
|
+
export const MessageFeedback = React.forwardRef(function MessageFeedback(props, ref) {
|
|
143
|
+
const { children, ...rest } = props;
|
|
144
|
+
return React.createElement(customElements.getName(MessageFeedbackWC) ?? 'foundation-ai-message-feedback', { ...rest, ref }, children);
|
|
145
|
+
});
|
|
146
|
+
|
|
141
147
|
export const FoundationAiPopoutManager = React.forwardRef(function FoundationAiPopoutManager(props, ref) {
|
|
142
148
|
const { children, ...rest } = props;
|
|
143
149
|
return React.createElement(customElements.getName(FoundationAiPopoutManagerWC) ?? 'foundation-ai-popout-manager', { ...rest, ref }, children);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genesislcap/ai-assistant",
|
|
3
3
|
"description": "Genesis AI Assistant micro-frontend",
|
|
4
|
-
"version": "15.
|
|
4
|
+
"version": "15.61.0",
|
|
5
5
|
"license": "SEE LICENSE IN license.txt",
|
|
6
6
|
"main": "dist/esm/index.js",
|
|
7
7
|
"types": "dist/ai-assistant.d.ts",
|
|
@@ -92,31 +92,31 @@
|
|
|
92
92
|
}
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
|
-
"@genesislcap/foundation-comms": "15.
|
|
96
|
-
"@genesislcap/foundation-testing": "15.
|
|
97
|
-
"@genesislcap/genx": "15.
|
|
98
|
-
"@genesislcap/ts-builder": "15.
|
|
99
|
-
"@genesislcap/uvu-playwright-builder": "15.
|
|
100
|
-
"@genesislcap/vite-builder": "15.
|
|
101
|
-
"@genesislcap/webpack-builder": "15.
|
|
95
|
+
"@genesislcap/foundation-comms": "15.61.0",
|
|
96
|
+
"@genesislcap/foundation-testing": "15.61.0",
|
|
97
|
+
"@genesislcap/genx": "15.61.0",
|
|
98
|
+
"@genesislcap/ts-builder": "15.61.0",
|
|
99
|
+
"@genesislcap/uvu-playwright-builder": "15.61.0",
|
|
100
|
+
"@genesislcap/vite-builder": "15.61.0",
|
|
101
|
+
"@genesislcap/webpack-builder": "15.61.0",
|
|
102
102
|
"@types/dompurify": "^3.0.5",
|
|
103
103
|
"@types/marked": "^5.0.2",
|
|
104
104
|
"esbuild": "0.25.12"
|
|
105
105
|
},
|
|
106
106
|
"dependencies": {
|
|
107
|
-
"@genesislcap/foundation-ai": "15.
|
|
108
|
-
"@genesislcap/foundation-logger": "15.
|
|
109
|
-
"@genesislcap/foundation-notifications": "15.
|
|
110
|
-
"@genesislcap/foundation-redux": "15.
|
|
111
|
-
"@genesislcap/foundation-ui": "15.
|
|
112
|
-
"@genesislcap/foundation-utils": "15.
|
|
113
|
-
"@genesislcap/rapid-design-system": "15.
|
|
114
|
-
"@genesislcap/web-core": "15.
|
|
107
|
+
"@genesislcap/foundation-ai": "15.61.0",
|
|
108
|
+
"@genesislcap/foundation-logger": "15.61.0",
|
|
109
|
+
"@genesislcap/foundation-notifications": "15.61.0",
|
|
110
|
+
"@genesislcap/foundation-redux": "15.61.0",
|
|
111
|
+
"@genesislcap/foundation-ui": "15.61.0",
|
|
112
|
+
"@genesislcap/foundation-utils": "15.61.0",
|
|
113
|
+
"@genesislcap/rapid-design-system": "15.61.0",
|
|
114
|
+
"@genesislcap/web-core": "15.61.0",
|
|
115
115
|
"dompurify": "^3.3.1",
|
|
116
116
|
"marked": "^17.0.3"
|
|
117
117
|
},
|
|
118
118
|
"peerDependencies": {
|
|
119
|
-
"@genesislcap/foundation-comms": "^15.
|
|
119
|
+
"@genesislcap/foundation-comms": "^15.61.0"
|
|
120
120
|
},
|
|
121
121
|
"peerDependenciesMeta": {
|
|
122
122
|
"@genesislcap/foundation-comms": {
|
package/sandbox/README.md
CHANGED
|
@@ -31,7 +31,7 @@ The panel covers, roughly in the order you'll want them:
|
|
|
31
31
|
| **Context window** | **Jump to state** (the four gate states in one click), the simulated window and fill, `chatConfig.context`, a flow-in-progress toggle, live readouts of the gate / thresholds / compaction plan, and Compact |
|
|
32
32
|
| **Simulated state** | Forces `idle` / busy / cancelling / blocked / compacting / restoring onto the element |
|
|
33
33
|
| **Transcript** | Seeds an empty, short, tool-and-markdown-heavy, 28-turn, ~20k-token, or ~22k-token tool-loop conversation, and how far back to shift its timestamps |
|
|
34
|
-
| **Chrome** | Header title, popout mode, suggestions, the tool-call / reasoning / narration toggles, the timestamp mode, animations |
|
|
34
|
+
| **Chrome** | Header title, popout mode, suggestions, the tool-call / reasoning / narration toggles, the timestamp mode, animations, per-reply thumbs (`messageFeedback`, with a stub send that succeeds or fails) |
|
|
35
35
|
| **Stage** | Frame size (floored at 400px — see below), light/dark, remount, full reset |
|
|
36
36
|
|
|
37
37
|
Panel state persists in `localStorage`, so a reload picks up where you left off. **Reset sandbox**
|
package/sandbox/sandbox.ts
CHANGED
|
@@ -137,6 +137,7 @@ interface SandboxState {
|
|
|
137
137
|
showNarration: boolean;
|
|
138
138
|
showTimestamps: TimestampDisplay;
|
|
139
139
|
animations: boolean;
|
|
140
|
+
messageFeedback: 'off' | 'succeeds' | 'fails';
|
|
140
141
|
// Context window (GENC-1567)
|
|
141
142
|
contextPreset: ContextPresetName | typeof CUSTOM_PRESET;
|
|
142
143
|
contextWindow: number;
|
|
@@ -182,6 +183,7 @@ const DEFAULT_STATE: SandboxState = {
|
|
|
182
183
|
showNarration: true,
|
|
183
184
|
showTimestamps: 'always',
|
|
184
185
|
animations: true,
|
|
186
|
+
messageFeedback: 'succeeds',
|
|
185
187
|
// Chosen so the `huge` (~20k) and `loop` (~22k) seeds actually clear the tail
|
|
186
188
|
// budget these produce (~12.8k) and so have something to compact. Verified, not
|
|
187
189
|
// guessed — a slightly roomier pairing leaves the tail larger than either seed,
|
|
@@ -334,6 +336,9 @@ const ACCEPTED_FILES: Record<SandboxState['attachments'], string | undefined> =
|
|
|
334
336
|
images: 'image/png,image/jpeg,image/gif,image/webp,.txt,.md,.csv,.json,text/*',
|
|
335
337
|
};
|
|
336
338
|
|
|
339
|
+
/** Long enough to see the thumbs' Sending… state. */
|
|
340
|
+
const STUB_FEEDBACK_SEND_MS = 600;
|
|
341
|
+
|
|
337
342
|
const buildChatConfig = (s: SandboxState): ChatConfig => ({
|
|
338
343
|
ui: {
|
|
339
344
|
acceptedFiles: ACCEPTED_FILES[s.attachments],
|
|
@@ -348,6 +353,25 @@ const buildChatConfig = (s: SandboxState): ChatConfig => ({
|
|
|
348
353
|
// `undefined` disables the feature outright (no cog control, nothing enabled);
|
|
349
354
|
// `userConfigurable` turns the defaults on and adds the multiselect to the cog.
|
|
350
355
|
animations: s.animations ? { userConfigurable: true } : undefined,
|
|
356
|
+
messageFeedback:
|
|
357
|
+
s.messageFeedback === 'off'
|
|
358
|
+
? undefined
|
|
359
|
+
: {
|
|
360
|
+
send: async ({ rating, reply, replyKey, message, filename, getLogJson }) => {
|
|
361
|
+
await new Promise((resolve) => {
|
|
362
|
+
setTimeout(resolve, STUB_FEEDBACK_SEND_MS);
|
|
363
|
+
});
|
|
364
|
+
console.info('[sandbox] message feedback', {
|
|
365
|
+
rating,
|
|
366
|
+
reply: reply.content,
|
|
367
|
+
replyKey,
|
|
368
|
+
note: message,
|
|
369
|
+
filename,
|
|
370
|
+
logBytes: (await getLogJson()).length,
|
|
371
|
+
});
|
|
372
|
+
if (s.messageFeedback === 'fails') throw new Error('Sandbox transport refused');
|
|
373
|
+
},
|
|
374
|
+
},
|
|
351
375
|
},
|
|
352
376
|
context: {
|
|
353
377
|
enabled: s.contextGateEnabled,
|
|
@@ -969,6 +993,17 @@ const GROUPS: ReadonlyArray<ControlGroup<SandboxState>> = [
|
|
|
969
993
|
hint: 'Also settable from the cog. Whichever you moved last wins; moving other panel controls will not disturb a cog choice.',
|
|
970
994
|
},
|
|
971
995
|
{ kind: 'toggle', key: 'animations', label: 'Animations' },
|
|
996
|
+
{
|
|
997
|
+
kind: 'select',
|
|
998
|
+
key: 'messageFeedback',
|
|
999
|
+
label: 'Message feedback',
|
|
1000
|
+
options: [
|
|
1001
|
+
{ value: 'off', label: 'Off' },
|
|
1002
|
+
{ value: 'succeeds', label: 'Send succeeds' },
|
|
1003
|
+
{ value: 'fails', label: 'Send fails' },
|
|
1004
|
+
],
|
|
1005
|
+
hint: 'chatConfig.ui.messageFeedback — thumbs on each reply; the stub send logs to the console.',
|
|
1006
|
+
},
|
|
972
1007
|
],
|
|
973
1008
|
},
|
|
974
1009
|
{
|