@genesislcap/ai-assistant 15.29.0 → 15.30.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.api.json +771 -10
- package/dist/ai-assistant.d.ts +154 -0
- package/dist/custom-elements.json +443 -1
- package/dist/dts/components/settings-modal/settings-modal.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 +1 -0
- package/dist/dts/components/settings-modal/settings-modal.template.d.ts.map +1 -1
- package/dist/dts/index.d.ts +1 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/main/main.d.ts +148 -0
- package/dist/dts/main/main.d.ts.map +1 -1
- package/dist/dts/utils/resolve-debug-log-note.d.ts +25 -0
- package/dist/dts/utils/resolve-debug-log-note.d.ts.map +1 -0
- package/dist/esm/components/settings-modal/settings-modal.js +1 -0
- package/dist/esm/components/settings-modal/settings-modal.styles.js +85 -5
- package/dist/esm/components/settings-modal/settings-modal.template.js +130 -23
- package/dist/esm/main/main.js +430 -6
- package/dist/esm/utils/resolve-debug-log-note.js +37 -0
- package/package.json +16 -16
package/dist/esm/main/main.js
CHANGED
|
@@ -58,6 +58,7 @@ import { deleteBankedBaseline } from '../utils/banked-usage-baselines';
|
|
|
58
58
|
import { estimateContextTokens, isContextMeasurable, } from '../utils/context-tokens';
|
|
59
59
|
import { logger } from '../utils/logger';
|
|
60
60
|
import { filterVisibleMessages, trailingInteractionRow } from '../utils/message-partition';
|
|
61
|
+
import { normalizeDebugLogNote, resolveDebugLogNoteConfig, } from '../utils/resolve-debug-log-note';
|
|
61
62
|
import { DEFAULT_TIMESTAMP_DISPLAY, preferenceUnchanged, resolvePreferenceBaseline, } from '../utils/resolve-preference-baseline';
|
|
62
63
|
import { stripAgentHandlers } from '../utils/strip-agent-handlers';
|
|
63
64
|
import { emptyUsage, sumUsage, totalTokens } from '../utils/sum-usage';
|
|
@@ -92,6 +93,33 @@ export const genesisIconSvg = `<svg viewBox="-4 0 30 26" fill="none" xmlns="http
|
|
|
92
93
|
function sanitizeIconMarkup(markup) {
|
|
93
94
|
return DOMPurify.sanitize(markup, { USE_PROFILES: { svg: true, svgFilters: true } });
|
|
94
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Await `outcome` (a promise, or a sync return) but give up after `ms`, rejecting with a
|
|
98
|
+
* named error. Used to bound a host callback we cannot otherwise trust to settle.
|
|
99
|
+
*/
|
|
100
|
+
function settleWithin(outcome, ms, what) {
|
|
101
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
102
|
+
let timer;
|
|
103
|
+
const timeout = new Promise((_, reject) => {
|
|
104
|
+
timer = setTimeout(() => reject(new Error(`${what} did not settle within ${ms}ms`)), ms);
|
|
105
|
+
});
|
|
106
|
+
try {
|
|
107
|
+
yield Promise.race([Promise.resolve(outcome), timeout]);
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
clearTimeout(timer);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* How long the agent-log Send button shows its 'Sent'/'Failed' outcome before
|
|
116
|
+
* returning to idle — long enough to read, short enough that a retry is never blocked.
|
|
117
|
+
*/
|
|
118
|
+
const DEBUG_LOG_SUBMIT_RESULT_FLASH_MS = 4000;
|
|
119
|
+
/** Default ceiling on the host's `debugLogSubmit.send` before the submission is failed. */
|
|
120
|
+
const DEBUG_LOG_SUBMIT_TIMEOUT_MS = 30000;
|
|
121
|
+
/** Announce the remaining note length once this few characters are left. */
|
|
122
|
+
const DEBUG_LOG_NOTE_REMAINING_WARN = 20;
|
|
95
123
|
/**
|
|
96
124
|
* Reporter id for this element's OWN session persister in `brokenSources` (GENC-1511).
|
|
97
125
|
*
|
|
@@ -306,6 +334,34 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
306
334
|
* element has its own settings modal.
|
|
307
335
|
*/
|
|
308
336
|
this.downloadingDebugLog = false;
|
|
337
|
+
/**
|
|
338
|
+
* True while `submitDebugLog` is building and handing off the log. Same role as
|
|
339
|
+
* `downloadingDebugLog` for the Send button: the lifetime-stitch and the host's
|
|
340
|
+
* `send` callback are async, so the button needs a visible in-flight state.
|
|
341
|
+
*/
|
|
342
|
+
this.submittingDebugLog = false;
|
|
343
|
+
/**
|
|
344
|
+
* Outcome of the last log submission, shown briefly on the button ('Sent' /
|
|
345
|
+
* 'Failed — retry') then cleared by `setDebugLogSubmitResult`'s timer. `null` when idle.
|
|
346
|
+
*/
|
|
347
|
+
this.debugLogSubmitResult = null;
|
|
348
|
+
/**
|
|
349
|
+
* Whether the agent-log compose box (note + Send/Cancel) is open. Opened by the row's
|
|
350
|
+
* Send button when `debugLogSubmit.note` is not `false`; closed by Cancel or a
|
|
351
|
+
* successful send. Stays open after a failure so the typed note is not lost.
|
|
352
|
+
*/
|
|
353
|
+
this.debugLogComposeOpen = false;
|
|
354
|
+
/** The note typed in the compose box, raw (trimmed only at submit time). */
|
|
355
|
+
this.debugLogNote = '';
|
|
356
|
+
/** How long 'Sent' shows before idle. Overridable so tests need not wait 4 seconds. */
|
|
357
|
+
this.debugLogSubmitResultFlashMs = DEBUG_LOG_SUBMIT_RESULT_FLASH_MS;
|
|
358
|
+
/**
|
|
359
|
+
* Bumped by `resetDebugLogSubmitState`. An in-flight `submitDebugLog` captures it and
|
|
360
|
+
* drops its continuation when a reset (disconnect, session switch, Clear) happened
|
|
361
|
+
* while it awaited — otherwise the stale submission would write state, arm a timer on
|
|
362
|
+
* a torn-down element, or report 'Sent' into a session it never belonged to.
|
|
363
|
+
*/
|
|
364
|
+
this.debugLogSubmitGeneration = 0;
|
|
309
365
|
/**
|
|
310
366
|
* GENC-1511 — persistence has failed repeatedly and it is no longer saving. Drives a BLOCKING
|
|
311
367
|
* modal with a single Reload action: the whole failure class this ticket exists for is one the
|
|
@@ -2148,6 +2204,9 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
2148
2204
|
return;
|
|
2149
2205
|
}
|
|
2150
2206
|
this._deferredSwitchToKey = undefined;
|
|
2207
|
+
// A note typed against the outgoing conversation must not caption the incoming one's
|
|
2208
|
+
// log, and an outcome flash belongs to the session that produced it.
|
|
2209
|
+
this.resetDebugLogSubmitState();
|
|
2151
2210
|
// Cancel the outgoing session's debounced autosave FIRST, so it can't land in
|
|
2152
2211
|
// the incoming session's file once `_sessionRef` swaps below.
|
|
2153
2212
|
(_b = getPersister(oldKey)) === null || _b === void 0 ? void 0 : _b.cancelPendingSave();
|
|
@@ -2210,6 +2269,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
2210
2269
|
// After `_sessionActive` is cleared, so the sync sees "disconnected" and stops.
|
|
2211
2270
|
this.syncTimestampClock();
|
|
2212
2271
|
this.stopLoadingTimer();
|
|
2272
|
+
this.resetDebugLogSubmitState();
|
|
2213
2273
|
this.state = 'idle';
|
|
2214
2274
|
// Note: `compacting`/`restoring` are NOT cleared here. They're written through the
|
|
2215
2275
|
// in-flight op's captured session store (see `initPersistenceForNewStore` /
|
|
@@ -2968,6 +3028,9 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
2968
3028
|
if (this.driver)
|
|
2969
3029
|
this.teardownDriver(key);
|
|
2970
3030
|
(_f = this._sessionRef) === null || _f === void 0 ? void 0 : _f.actions.aiAssistant.resetSession();
|
|
3031
|
+
// Same reason as in `switchSession`: the compose box and its note were about the
|
|
3032
|
+
// conversation just discarded.
|
|
3033
|
+
this.resetDebugLogSubmitState();
|
|
2971
3034
|
// `resetSession` zeroes the store-backed `contextTokens`; the gate's held copy
|
|
2972
3035
|
// is ours to drop. Leaving it would carry a block into a conversation that no
|
|
2973
3036
|
// longer exists — the one thing `contextGate` must never do (GENC-1582).
|
|
@@ -3140,6 +3203,145 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
3140
3203
|
get settingsSessionTokensVisible() {
|
|
3141
3204
|
return this.settings.sessionTokensVisible;
|
|
3142
3205
|
}
|
|
3206
|
+
/**
|
|
3207
|
+
* Whether the settings "Agent log" section renders — shown when either action is
|
|
3208
|
+
* enabled: the download button (`allowDebugDownload`) or the host-transport Send
|
|
3209
|
+
* button (`debugLogSubmit`). Each button is additionally gated on its own flag
|
|
3210
|
+
* inside the section, so a host can enable one without the other.
|
|
3211
|
+
*/
|
|
3212
|
+
get settingsAgentLogSectionVisible() {
|
|
3213
|
+
const ui = this.chatConfig.ui;
|
|
3214
|
+
return (ui === null || ui === void 0 ? void 0 : ui.allowDebugDownload) === true || (ui === null || ui === void 0 ? void 0 : ui.debugLogSubmit) != null;
|
|
3215
|
+
}
|
|
3216
|
+
/**
|
|
3217
|
+
* The resolved compose-box settings for the agent-log Send button, or `null` when the
|
|
3218
|
+
* host opted out (`debugLogSubmit.note: false`) and the button sends on first click.
|
|
3219
|
+
*/
|
|
3220
|
+
get debugLogNoteConfig() {
|
|
3221
|
+
var _a;
|
|
3222
|
+
return resolveDebugLogNoteConfig((_a = this.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.debugLogSubmit);
|
|
3223
|
+
}
|
|
3224
|
+
/**
|
|
3225
|
+
* Whether the typed note counts as empty — the ONE emptiness rule, shared by the
|
|
3226
|
+
* compose box's disabled state and its "required" hint so the two cannot drift apart.
|
|
3227
|
+
*/
|
|
3228
|
+
get debugLogNoteMissing() {
|
|
3229
|
+
return normalizeDebugLogNote(this.debugLogNote) === undefined;
|
|
3230
|
+
}
|
|
3231
|
+
/** The Send button's idle label, as the host configured it. */
|
|
3232
|
+
get debugLogSubmitLabel() {
|
|
3233
|
+
var _a, _b, _c;
|
|
3234
|
+
return (_c = (_b = (_a = this.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.debugLogSubmit) === null || _b === void 0 ? void 0 : _b.label) !== null && _c !== void 0 ? _c : 'Send';
|
|
3235
|
+
}
|
|
3236
|
+
/**
|
|
3237
|
+
* Label on the agent-log row's Send button. While the compose box is open the row
|
|
3238
|
+
* button is just the (disabled) trigger, so it keeps its idle label; otherwise it
|
|
3239
|
+
* reflects the in-flight / flashed outcome of the last submission.
|
|
3240
|
+
*/
|
|
3241
|
+
get debugLogSubmitButtonLabel() {
|
|
3242
|
+
if (this.debugLogComposeOpen)
|
|
3243
|
+
return this.debugLogSubmitLabel;
|
|
3244
|
+
if (this.submittingDebugLog)
|
|
3245
|
+
return 'Sending…';
|
|
3246
|
+
if (this.debugLogSubmitResult === 'sent')
|
|
3247
|
+
return 'Sent';
|
|
3248
|
+
if (this.debugLogSubmitResult === 'error')
|
|
3249
|
+
return 'Failed — retry';
|
|
3250
|
+
return this.debugLogSubmitLabel;
|
|
3251
|
+
}
|
|
3252
|
+
/**
|
|
3253
|
+
* The compose box's footer hint: a failure first, then the "required" nudge, else the
|
|
3254
|
+
* live counter. Visual only — the same information reaches assistive technology via
|
|
3255
|
+
* `debugLogNoteAriaLabel` (counter, required) and `debugLogSubmitAnnouncement` (outcome),
|
|
3256
|
+
* because IDREF attributes such as `aria-describedby` cannot point across the
|
|
3257
|
+
* design-system text-area's shadow boundary.
|
|
3258
|
+
*/
|
|
3259
|
+
get debugLogComposeHint() {
|
|
3260
|
+
var _a;
|
|
3261
|
+
if (this.debugLogSubmitResult === 'error')
|
|
3262
|
+
return 'Sending failed — try again.';
|
|
3263
|
+
if (((_a = this.debugLogNoteConfig) === null || _a === void 0 ? void 0 : _a.required) && this.debugLogNoteMissing) {
|
|
3264
|
+
return 'A short note is required.';
|
|
3265
|
+
}
|
|
3266
|
+
return this.debugLogNoteCounter;
|
|
3267
|
+
}
|
|
3268
|
+
/**
|
|
3269
|
+
* Accessible name for the note. Deliberately STATIC apart from "(required)": a name
|
|
3270
|
+
* that changed per keystroke would be re-read in full by NVDA on every key and never
|
|
3271
|
+
* updated by JAWS. The running count reaches AT through `debugLogSubmitAnnouncement`
|
|
3272
|
+
* at the thresholds where it matters.
|
|
3273
|
+
*/
|
|
3274
|
+
get debugLogNoteAriaLabel() {
|
|
3275
|
+
const note = this.debugLogNoteConfig;
|
|
3276
|
+
const required = (note === null || note === void 0 ? void 0 : note.required) ? ' (required)' : '';
|
|
3277
|
+
const max = note ? `, maximum ${note.maxLength} characters` : '';
|
|
3278
|
+
return `Note to send with the agent log${required}${max}.`;
|
|
3279
|
+
}
|
|
3280
|
+
/**
|
|
3281
|
+
* Text for the always-present live region beside the agent-log row, so the outcome of
|
|
3282
|
+
* a submission is announced even when the compose box is closed or opted out
|
|
3283
|
+
* (`note: false`), where the only visible change is the button label.
|
|
3284
|
+
*/
|
|
3285
|
+
get debugLogSubmitAnnouncement() {
|
|
3286
|
+
var _a;
|
|
3287
|
+
if (this.submittingDebugLog)
|
|
3288
|
+
return 'Sending the agent log.';
|
|
3289
|
+
if (this.debugLogSubmitResult === 'sent')
|
|
3290
|
+
return 'Agent log sent.';
|
|
3291
|
+
if (this.debugLogSubmitResult === 'error')
|
|
3292
|
+
return 'Sending the agent log failed. Try again.';
|
|
3293
|
+
// The note's length, only as it nears the cap: `maxlength` is invisible to AT, so at
|
|
3294
|
+
// the limit keystrokes would be silently dropped with nothing changing anywhere.
|
|
3295
|
+
const max = (_a = this.debugLogNoteConfig) === null || _a === void 0 ? void 0 : _a.maxLength;
|
|
3296
|
+
if (this.debugLogComposeOpen && max) {
|
|
3297
|
+
const remaining = max - this.debugLogNote.length;
|
|
3298
|
+
if (remaining <= 0) {
|
|
3299
|
+
return `Note is at its ${max} character limit; further typing is dropped.`;
|
|
3300
|
+
}
|
|
3301
|
+
if (remaining <= DEBUG_LOG_NOTE_REMAINING_WARN)
|
|
3302
|
+
return `${remaining} characters remaining.`;
|
|
3303
|
+
}
|
|
3304
|
+
return '';
|
|
3305
|
+
}
|
|
3306
|
+
/**
|
|
3307
|
+
* The agent-log row's description, phrased for whichever actions the host enabled.
|
|
3308
|
+
* Send is called out as transmitting: Download keeps the log on the user's machine,
|
|
3309
|
+
* Send hands the full conversation to wherever the host's transport delivers it.
|
|
3310
|
+
*/
|
|
3311
|
+
get debugLogRowDescription() {
|
|
3312
|
+
var _a;
|
|
3313
|
+
const ui = this.chatConfig.ui;
|
|
3314
|
+
const download = (ui === null || ui === void 0 ? void 0 : ui.allowDebugDownload) === true;
|
|
3315
|
+
const send = (ui === null || ui === void 0 ? void 0 : ui.debugLogSubmit) != null;
|
|
3316
|
+
if (send && ((_a = ui === null || ui === void 0 ? void 0 : ui.debugLogSubmit) === null || _a === void 0 ? void 0 : _a.description))
|
|
3317
|
+
return ui.debugLogSubmit.description;
|
|
3318
|
+
const timeline = "a structured debug timeline of this assistant's activity";
|
|
3319
|
+
// What actually travels (see `ChatDebugLogSubmission.logJson`): the lifetime stream,
|
|
3320
|
+
// not just this conversation — say so where the user commits to it.
|
|
3321
|
+
const transmits = 'transmits the full lifetime log — every conversation held here, including cleared ' +
|
|
3322
|
+
'ones, with attachments, system prompts and agent configuration.';
|
|
3323
|
+
if (download && send)
|
|
3324
|
+
return `Export ${timeline}, or send it to support. Sending ${transmits}`;
|
|
3325
|
+
if (send)
|
|
3326
|
+
return `Send ${timeline} to support. This ${transmits}`;
|
|
3327
|
+
return "Export a structured debug timeline of this session's agent activity.";
|
|
3328
|
+
}
|
|
3329
|
+
/**
|
|
3330
|
+
* Whether the compose box's Send button is inert: a send is already in flight, or the
|
|
3331
|
+
* host made the note required and it is still blank.
|
|
3332
|
+
*/
|
|
3333
|
+
get debugLogSendDisabled() {
|
|
3334
|
+
var _a;
|
|
3335
|
+
if (this.submittingDebugLog)
|
|
3336
|
+
return true;
|
|
3337
|
+
return ((_a = this.debugLogNoteConfig) === null || _a === void 0 ? void 0 : _a.required) === true && this.debugLogNoteMissing;
|
|
3338
|
+
}
|
|
3339
|
+
/** Live "typed / max" counter for the compose box. */
|
|
3340
|
+
get debugLogNoteCounter() {
|
|
3341
|
+
var _a;
|
|
3342
|
+
const max = (_a = this.debugLogNoteConfig) === null || _a === void 0 ? void 0 : _a.maxLength;
|
|
3343
|
+
return max ? `${this.debugLogNote.length}/${max}` : '';
|
|
3344
|
+
}
|
|
3143
3345
|
/** Whether the chat header (and settings cog) should render. */
|
|
3144
3346
|
get settingsCogVisible() {
|
|
3145
3347
|
return this.settings.cogVisible;
|
|
@@ -3323,15 +3525,11 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
3323
3525
|
}
|
|
3324
3526
|
downloadDebugLog() {
|
|
3325
3527
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3326
|
-
var _a, _b, _c;
|
|
3327
3528
|
// Guard re-entry: the button is disabled while this runs, but a programmatic
|
|
3328
3529
|
// or double-fire can't kick off a second stitch either.
|
|
3329
3530
|
if (this.downloadingDebugLog)
|
|
3330
3531
|
return;
|
|
3331
|
-
const
|
|
3332
|
-
const agentName = ((_c = (_b = (_a = this.activeAgent) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : this.headerTitle) !== null && _c !== void 0 ? _c : 'chat')
|
|
3333
|
-
.toLowerCase()
|
|
3334
|
-
.replace(/\s+/g, '-');
|
|
3532
|
+
const filename = this.debugLogFilename();
|
|
3335
3533
|
this.downloadingDebugLog = true;
|
|
3336
3534
|
try {
|
|
3337
3535
|
const payload = yield this.buildDownloadLog();
|
|
@@ -3339,7 +3537,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
3339
3537
|
const url = URL.createObjectURL(blob);
|
|
3340
3538
|
const a = document.createElement('a');
|
|
3341
3539
|
a.href = url;
|
|
3342
|
-
a.download =
|
|
3540
|
+
a.download = filename;
|
|
3343
3541
|
a.click();
|
|
3344
3542
|
URL.revokeObjectURL(url);
|
|
3345
3543
|
}
|
|
@@ -3348,6 +3546,187 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
|
|
|
3348
3546
|
}
|
|
3349
3547
|
});
|
|
3350
3548
|
}
|
|
3549
|
+
/**
|
|
3550
|
+
* Hand the agent log to the host's `ui.debugLogSubmit.send` callback — the Send
|
|
3551
|
+
* button next to Download. Reuses `buildDownloadLog`, so the submitted log is
|
|
3552
|
+
* byte-identical to what Download saves (lifetime-stitched when the provider
|
|
3553
|
+
* persists diagnostics). The assistant is vendor-agnostic: the callback owns the
|
|
3554
|
+
* transport (Sentry feedback, a support API, an upload endpoint). Outcome lands in
|
|
3555
|
+
* `debugLogSubmitResult` so the button can flash 'Sent'/'Failed'; failures also go
|
|
3556
|
+
* to the logger with the cause.
|
|
3557
|
+
*/
|
|
3558
|
+
submitDebugLog() {
|
|
3559
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3560
|
+
var _a, _b;
|
|
3561
|
+
if (this.submittingDebugLog || this.debugLogSendDisabled)
|
|
3562
|
+
return;
|
|
3563
|
+
const config = (_a = this.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.debugLogSubmit;
|
|
3564
|
+
if (!config)
|
|
3565
|
+
return;
|
|
3566
|
+
const generation = this.debugLogSubmitGeneration;
|
|
3567
|
+
const agentName = this.debugLogAgentName;
|
|
3568
|
+
const filename = this.debugLogFilename();
|
|
3569
|
+
// Read at click time, BEFORE the async stitch: the user pressed Send on this note.
|
|
3570
|
+
// Only a note the user could actually type travels with the log: with the compose box
|
|
3571
|
+
// opted out there is no note, whatever a stale `debugLogNote` might hold.
|
|
3572
|
+
const message = this.debugLogNoteConfig === null ? undefined : normalizeDebugLogNote(this.debugLogNote);
|
|
3573
|
+
this.submittingDebugLog = true;
|
|
3574
|
+
try {
|
|
3575
|
+
const payload = yield this.buildDownloadLog();
|
|
3576
|
+
if (generation !== this.debugLogSubmitGeneration)
|
|
3577
|
+
return;
|
|
3578
|
+
yield settleWithin(config.send({
|
|
3579
|
+
logJson: JSON.stringify(payload, null, 2),
|
|
3580
|
+
filename,
|
|
3581
|
+
agentName,
|
|
3582
|
+
host: window.location.host,
|
|
3583
|
+
message,
|
|
3584
|
+
}), (_b = config.timeoutMs) !== null && _b !== void 0 ? _b : DEBUG_LOG_SUBMIT_TIMEOUT_MS, 'debugLogSubmit.send');
|
|
3585
|
+
if (generation !== this.debugLogSubmitGeneration)
|
|
3586
|
+
return;
|
|
3587
|
+
// Success consumes the note; a failure (below) keeps the box open with it intact.
|
|
3588
|
+
this.debugLogComposeOpen = false;
|
|
3589
|
+
this.debugLogNote = '';
|
|
3590
|
+
this.setDebugLogSubmitResult('sent');
|
|
3591
|
+
this.restoreDebugLogTriggerFocus();
|
|
3592
|
+
}
|
|
3593
|
+
catch (e) {
|
|
3594
|
+
if (generation !== this.debugLogSubmitGeneration)
|
|
3595
|
+
return;
|
|
3596
|
+
logger.error('Submitting agent log failed:', e);
|
|
3597
|
+
// Persists (no flash timer) until the user acts — see `setDebugLogSubmitResult`.
|
|
3598
|
+
this.setDebugLogSubmitResult('error');
|
|
3599
|
+
this.restoreDebugLogRetryFocus();
|
|
3600
|
+
}
|
|
3601
|
+
finally {
|
|
3602
|
+
if (generation === this.debugLogSubmitGeneration)
|
|
3603
|
+
this.submittingDebugLog = false;
|
|
3604
|
+
}
|
|
3605
|
+
});
|
|
3606
|
+
}
|
|
3607
|
+
/** The agent name stamped on the log: the active agent, else the header title. */
|
|
3608
|
+
get debugLogAgentName() {
|
|
3609
|
+
var _a, _b, _c;
|
|
3610
|
+
return (_c = (_b = (_a = this.activeAgent) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : this.headerTitle) !== null && _c !== void 0 ? _c : 'chat';
|
|
3611
|
+
}
|
|
3612
|
+
/**
|
|
3613
|
+
* Filename for the agent log — the ONE derivation Download saves under and Send hands
|
|
3614
|
+
* to the host, so `ChatDebugLogSubmission.filename`'s "same stem as the download"
|
|
3615
|
+
* promise is enforced rather than relying on two copies agreeing.
|
|
3616
|
+
*/
|
|
3617
|
+
debugLogFilename() {
|
|
3618
|
+
const stem = this.debugLogAgentName.toLowerCase().replace(/\s+/g, '-');
|
|
3619
|
+
return `${stem}-${new Date().toISOString().replace(/:/g, '-')}.json`;
|
|
3620
|
+
}
|
|
3621
|
+
/**
|
|
3622
|
+
* Back to idle: nothing in flight, no flash, no open compose box, no note. Called on
|
|
3623
|
+
* disconnect and on a session switch / Clear (a note typed against one conversation
|
|
3624
|
+
* must not caption the next one's log). On disconnect — the
|
|
3625
|
+
* flash timer would otherwise fire against a torn-down element, and clearing ONLY the
|
|
3626
|
+
* timer would strand `debugLogSubmitResult` at 'sent'/'error' for good on a pop-out /
|
|
3627
|
+
* pop-in (nothing else resets it), with the box remounting open on a stale note.
|
|
3628
|
+
*/
|
|
3629
|
+
resetDebugLogSubmitState() {
|
|
3630
|
+
this.debugLogSubmitGeneration += 1;
|
|
3631
|
+
clearTimeout(this.debugLogSubmitResultTimer);
|
|
3632
|
+
this.debugLogSubmitResultTimer = undefined;
|
|
3633
|
+
// Including the in-flight flag: every escape route (Send, Cancel, the note, the row
|
|
3634
|
+
// button) is gated on it, so a host transport that never settles would otherwise
|
|
3635
|
+
// wedge the feature until reload.
|
|
3636
|
+
this.submittingDebugLog = false;
|
|
3637
|
+
this.debugLogSubmitResult = null;
|
|
3638
|
+
this.debugLogComposeOpen = false;
|
|
3639
|
+
this.debugLogNote = '';
|
|
3640
|
+
}
|
|
3641
|
+
/**
|
|
3642
|
+
* The row's Send button. With a compose box configured (the default) it opens the box
|
|
3643
|
+
* and focuses the note so the user can caption the log; with `note: false` it sends
|
|
3644
|
+
* straight away, as before.
|
|
3645
|
+
*/
|
|
3646
|
+
openDebugLogCompose() {
|
|
3647
|
+
if (this.submittingDebugLog)
|
|
3648
|
+
return;
|
|
3649
|
+
if (this.debugLogNoteConfig === null) {
|
|
3650
|
+
void this.submitDebugLog();
|
|
3651
|
+
return;
|
|
3652
|
+
}
|
|
3653
|
+
// Opening again resets a lingering 'Failed' flash so the box starts clean.
|
|
3654
|
+
clearTimeout(this.debugLogSubmitResultTimer);
|
|
3655
|
+
this.debugLogSubmitResultTimer = undefined;
|
|
3656
|
+
this.debugLogSubmitResult = null;
|
|
3657
|
+
this.debugLogComposeOpen = true;
|
|
3658
|
+
// Next frame so the `when` block has rendered the text-area. The design-system
|
|
3659
|
+
// text-area does not delegate focus (see `maybeAutoFocusChatInput`), so drill in.
|
|
3660
|
+
requestAnimationFrame(() => {
|
|
3661
|
+
var _a, _b, _c;
|
|
3662
|
+
if (!this.debugLogComposeOpen || !this.isConnected)
|
|
3663
|
+
return;
|
|
3664
|
+
const el = this.debugLogNoteEl;
|
|
3665
|
+
(_c = ((_b = (_a = el === null || el === void 0 ? void 0 : el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('textarea')) !== null && _b !== void 0 ? _b : el)) === null || _c === void 0 ? void 0 : _c.focus();
|
|
3666
|
+
});
|
|
3667
|
+
}
|
|
3668
|
+
/** The note's `@input`: track the text, and let typing dismiss a standing failure. */
|
|
3669
|
+
noteDebugLogInput(value) {
|
|
3670
|
+
this.debugLogNote = value;
|
|
3671
|
+
if (this.debugLogSubmitResult === 'error')
|
|
3672
|
+
this.debugLogSubmitResult = null;
|
|
3673
|
+
}
|
|
3674
|
+
/** Close the compose box and discard the typed note. */
|
|
3675
|
+
cancelDebugLogCompose() {
|
|
3676
|
+
if (this.submittingDebugLog)
|
|
3677
|
+
return;
|
|
3678
|
+
this.debugLogComposeOpen = false;
|
|
3679
|
+
this.debugLogNote = '';
|
|
3680
|
+
this.restoreDebugLogTriggerFocus();
|
|
3681
|
+
}
|
|
3682
|
+
/**
|
|
3683
|
+
* Closing the compose box unmounts the focused text-area, which drops focus to the
|
|
3684
|
+
* document body and strands keyboard and screen-reader users. Hand it back to the row's
|
|
3685
|
+
* Send button — next frame, once the `when` block has torn down and the button, disabled
|
|
3686
|
+
* while the box was open, is interactive again.
|
|
3687
|
+
*/
|
|
3688
|
+
restoreDebugLogTriggerFocus() {
|
|
3689
|
+
requestAnimationFrame(() => {
|
|
3690
|
+
var _a, _b;
|
|
3691
|
+
if (!this.isConnected || this.debugLogComposeOpen)
|
|
3692
|
+
return;
|
|
3693
|
+
(_b = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('[part="submit-log-button"]')) === null || _b === void 0 ? void 0 : _b.focus();
|
|
3694
|
+
});
|
|
3695
|
+
}
|
|
3696
|
+
/**
|
|
3697
|
+
* After a FAILED send the controls come back but the element that started the send
|
|
3698
|
+
* was disabled meanwhile, so focus has already dropped to the document body. Put it
|
|
3699
|
+
* where the retry happens: the note when the box is open, else the row trigger.
|
|
3700
|
+
*/
|
|
3701
|
+
restoreDebugLogRetryFocus() {
|
|
3702
|
+
if (!this.debugLogComposeOpen) {
|
|
3703
|
+
this.restoreDebugLogTriggerFocus();
|
|
3704
|
+
return;
|
|
3705
|
+
}
|
|
3706
|
+
requestAnimationFrame(() => {
|
|
3707
|
+
var _a, _b, _c;
|
|
3708
|
+
if (!this.isConnected || !this.debugLogComposeOpen)
|
|
3709
|
+
return;
|
|
3710
|
+
const el = this.debugLogNoteEl;
|
|
3711
|
+
(_c = ((_b = (_a = el === null || el === void 0 ? void 0 : el.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('textarea')) !== null && _b !== void 0 ? _b : el)) === null || _c === void 0 ? void 0 : _c.focus();
|
|
3712
|
+
});
|
|
3713
|
+
}
|
|
3714
|
+
/**
|
|
3715
|
+
* Record the submit outcome. 'sent' flashes and falls back to idle; 'error' PERSISTS
|
|
3716
|
+
* until the user acts (types, reopens the box, cancels, or retries) — a failure that
|
|
3717
|
+
* silently reverted to the counter after four seconds left no trace it had happened.
|
|
3718
|
+
*/
|
|
3719
|
+
setDebugLogSubmitResult(result) {
|
|
3720
|
+
clearTimeout(this.debugLogSubmitResultTimer);
|
|
3721
|
+
this.debugLogSubmitResultTimer = undefined;
|
|
3722
|
+
this.debugLogSubmitResult = result;
|
|
3723
|
+
if (result !== 'sent')
|
|
3724
|
+
return;
|
|
3725
|
+
this.debugLogSubmitResultTimer = setTimeout(() => {
|
|
3726
|
+
this.debugLogSubmitResult = null;
|
|
3727
|
+
this.debugLogSubmitResultTimer = undefined;
|
|
3728
|
+
}, this.debugLogSubmitResultFlashMs);
|
|
3729
|
+
}
|
|
3351
3730
|
/**
|
|
3352
3731
|
* The log to download (GENC-1351 §5.8). When the provider persists diagnostics,
|
|
3353
3732
|
* stitch the **whole lifetime** stream (every page load) rather than just the
|
|
@@ -3968,6 +4347,18 @@ __decorate([
|
|
|
3968
4347
|
__decorate([
|
|
3969
4348
|
observable
|
|
3970
4349
|
], FoundationAiAssistant.prototype, "downloadingDebugLog", void 0);
|
|
4350
|
+
__decorate([
|
|
4351
|
+
observable
|
|
4352
|
+
], FoundationAiAssistant.prototype, "submittingDebugLog", void 0);
|
|
4353
|
+
__decorate([
|
|
4354
|
+
observable
|
|
4355
|
+
], FoundationAiAssistant.prototype, "debugLogSubmitResult", void 0);
|
|
4356
|
+
__decorate([
|
|
4357
|
+
observable
|
|
4358
|
+
], FoundationAiAssistant.prototype, "debugLogComposeOpen", void 0);
|
|
4359
|
+
__decorate([
|
|
4360
|
+
observable
|
|
4361
|
+
], FoundationAiAssistant.prototype, "debugLogNote", void 0);
|
|
3971
4362
|
__decorate([
|
|
3972
4363
|
observable
|
|
3973
4364
|
], FoundationAiAssistant.prototype, "persistenceBroken", void 0);
|
|
@@ -4016,6 +4407,39 @@ __decorate([
|
|
|
4016
4407
|
__decorate([
|
|
4017
4408
|
volatile
|
|
4018
4409
|
], FoundationAiAssistant.prototype, "agentPickerEnabled", null);
|
|
4410
|
+
__decorate([
|
|
4411
|
+
volatile
|
|
4412
|
+
], FoundationAiAssistant.prototype, "settingsAgentLogSectionVisible", null);
|
|
4413
|
+
__decorate([
|
|
4414
|
+
volatile
|
|
4415
|
+
], FoundationAiAssistant.prototype, "debugLogNoteConfig", null);
|
|
4416
|
+
__decorate([
|
|
4417
|
+
volatile
|
|
4418
|
+
], FoundationAiAssistant.prototype, "debugLogNoteMissing", null);
|
|
4419
|
+
__decorate([
|
|
4420
|
+
volatile
|
|
4421
|
+
], FoundationAiAssistant.prototype, "debugLogSubmitLabel", null);
|
|
4422
|
+
__decorate([
|
|
4423
|
+
volatile
|
|
4424
|
+
], FoundationAiAssistant.prototype, "debugLogSubmitButtonLabel", null);
|
|
4425
|
+
__decorate([
|
|
4426
|
+
volatile
|
|
4427
|
+
], FoundationAiAssistant.prototype, "debugLogComposeHint", null);
|
|
4428
|
+
__decorate([
|
|
4429
|
+
volatile
|
|
4430
|
+
], FoundationAiAssistant.prototype, "debugLogNoteAriaLabel", null);
|
|
4431
|
+
__decorate([
|
|
4432
|
+
volatile
|
|
4433
|
+
], FoundationAiAssistant.prototype, "debugLogSubmitAnnouncement", null);
|
|
4434
|
+
__decorate([
|
|
4435
|
+
volatile
|
|
4436
|
+
], FoundationAiAssistant.prototype, "debugLogRowDescription", null);
|
|
4437
|
+
__decorate([
|
|
4438
|
+
volatile
|
|
4439
|
+
], FoundationAiAssistant.prototype, "debugLogSendDisabled", null);
|
|
4440
|
+
__decorate([
|
|
4441
|
+
volatile
|
|
4442
|
+
], FoundationAiAssistant.prototype, "debugLogNoteCounter", null);
|
|
4019
4443
|
__decorate([
|
|
4020
4444
|
volatile
|
|
4021
4445
|
], FoundationAiAssistant.prototype, "pinnedAgentHint", null);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** Default cap on the user's note. Short by design — it is a caption for the log, not a report. */
|
|
2
|
+
export const DEBUG_LOG_NOTE_DEFAULT_MAX_LENGTH = 500;
|
|
3
|
+
/** Default prompt in the empty note. */
|
|
4
|
+
export const DEBUG_LOG_NOTE_DEFAULT_PLACEHOLDER = 'What were you doing, or what went wrong? (optional)';
|
|
5
|
+
/** The same prompt for a `required` note — "(optional)" would contradict the disabled Send. */
|
|
6
|
+
export const DEBUG_LOG_NOTE_REQUIRED_PLACEHOLDER = 'What were you doing, or what went wrong?';
|
|
7
|
+
/**
|
|
8
|
+
* Resolve `debugLogSubmit.note` — `undefined`/`true` mean "compose box with defaults",
|
|
9
|
+
* `false` means "no compose box, send on first click" (→ `null`), and an object customises
|
|
10
|
+
* the box. Keeps the template free of the boolean-or-object dance.
|
|
11
|
+
*/
|
|
12
|
+
export function resolveDebugLogNoteConfig(config) {
|
|
13
|
+
var _a;
|
|
14
|
+
if (!config || config.note === false)
|
|
15
|
+
return null;
|
|
16
|
+
// `typeof null === 'object'`: a host building its config dynamically can hand us `null`
|
|
17
|
+
// even though the type forbids it, and that must read as "defaults", not crash.
|
|
18
|
+
const note = typeof config.note === 'object' && config.note !== null ? config.note : {};
|
|
19
|
+
// `>= 1`, not `> 0`: a fractional cap under 1 would floor to 0 and lock the textarea.
|
|
20
|
+
const maxLength = typeof note.maxLength === 'number' && note.maxLength >= 1
|
|
21
|
+
? Math.floor(note.maxLength)
|
|
22
|
+
: DEBUG_LOG_NOTE_DEFAULT_MAX_LENGTH;
|
|
23
|
+
const required = note.required === true;
|
|
24
|
+
return {
|
|
25
|
+
placeholder: (_a = note.placeholder) !== null && _a !== void 0 ? _a : (required ? DEBUG_LOG_NOTE_REQUIRED_PLACEHOLDER : DEBUG_LOG_NOTE_DEFAULT_PLACEHOLDER),
|
|
26
|
+
required,
|
|
27
|
+
maxLength,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Normalise the typed note for the submission: trimmed, and `undefined` when empty so a
|
|
32
|
+
* host can `message ?? fallback` without checking for whitespace.
|
|
33
|
+
*/
|
|
34
|
+
export function normalizeDebugLogNote(note) {
|
|
35
|
+
const trimmed = note.trim();
|
|
36
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
37
|
+
}
|
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.30.0",
|
|
5
5
|
"license": "SEE LICENSE IN license.txt",
|
|
6
6
|
"main": "dist/esm/index.js",
|
|
7
7
|
"types": "dist/ai-assistant.d.ts",
|
|
@@ -74,26 +74,26 @@
|
|
|
74
74
|
}
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@genesislcap/foundation-testing": "15.
|
|
78
|
-
"@genesislcap/genx": "15.
|
|
79
|
-
"@genesislcap/rollup-builder": "15.
|
|
80
|
-
"@genesislcap/ts-builder": "15.
|
|
81
|
-
"@genesislcap/uvu-playwright-builder": "15.
|
|
82
|
-
"@genesislcap/vite-builder": "15.
|
|
83
|
-
"@genesislcap/webpack-builder": "15.
|
|
77
|
+
"@genesislcap/foundation-testing": "15.30.0",
|
|
78
|
+
"@genesislcap/genx": "15.30.0",
|
|
79
|
+
"@genesislcap/rollup-builder": "15.30.0",
|
|
80
|
+
"@genesislcap/ts-builder": "15.30.0",
|
|
81
|
+
"@genesislcap/uvu-playwright-builder": "15.30.0",
|
|
82
|
+
"@genesislcap/vite-builder": "15.30.0",
|
|
83
|
+
"@genesislcap/webpack-builder": "15.30.0",
|
|
84
84
|
"@types/dompurify": "^3.0.5",
|
|
85
85
|
"@types/marked": "^5.0.2",
|
|
86
86
|
"esbuild": "0.25.12"
|
|
87
87
|
},
|
|
88
88
|
"dependencies": {
|
|
89
|
-
"@genesislcap/foundation-ai": "15.
|
|
90
|
-
"@genesislcap/foundation-logger": "15.
|
|
91
|
-
"@genesislcap/foundation-notifications": "15.
|
|
92
|
-
"@genesislcap/foundation-redux": "15.
|
|
93
|
-
"@genesislcap/foundation-ui": "15.
|
|
94
|
-
"@genesislcap/foundation-utils": "15.
|
|
95
|
-
"@genesislcap/rapid-design-system": "15.
|
|
96
|
-
"@genesislcap/web-core": "15.
|
|
89
|
+
"@genesislcap/foundation-ai": "15.30.0",
|
|
90
|
+
"@genesislcap/foundation-logger": "15.30.0",
|
|
91
|
+
"@genesislcap/foundation-notifications": "15.30.0",
|
|
92
|
+
"@genesislcap/foundation-redux": "15.30.0",
|
|
93
|
+
"@genesislcap/foundation-ui": "15.30.0",
|
|
94
|
+
"@genesislcap/foundation-utils": "15.30.0",
|
|
95
|
+
"@genesislcap/rapid-design-system": "15.30.0",
|
|
96
|
+
"@genesislcap/web-core": "15.30.0",
|
|
97
97
|
"dompurify": "^3.3.1",
|
|
98
98
|
"marked": "^17.0.3"
|
|
99
99
|
},
|