@genesislcap/ai-assistant 15.23.1-agent-rules.1 → 15.23.1-agent-log-submit.2

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.
@@ -63,6 +63,7 @@ import { logger } from '../utils/logger';
63
63
  import { filterVisibleMessages, trailingInteractionRow } from '../utils/message-partition';
64
64
  import { CONTEXT_ABORT_MARGIN_TOKENS, resolveContextBudget, } from '../utils/resolve-context-budget';
65
65
  import { resolveCostHistoryConfig, } from '../utils/resolve-cost-history-config';
66
+ import { normalizeDebugLogNote, resolveDebugLogNoteConfig, } from '../utils/resolve-debug-log-note';
66
67
  import { resolvePreferenceBaseline } from '../utils/resolve-preference-baseline';
67
68
  import { stripAgentHandlers } from '../utils/strip-agent-handlers';
68
69
  import { addUsage, emptyUsage, sumUsage, totalTokens } from '../utils/sum-usage';
@@ -112,6 +113,11 @@ const COMPOSER_MENU_MIN_HEIGHT = 140;
112
113
  * assistant is running — the Usage tab renders the in-memory list — so the delay is invisible.
113
114
  */
114
115
  const LEDGER_FLUSH_DEBOUNCE_MS = 2000;
116
+ /**
117
+ * How long the agent-log Send button shows its 'Sent'/'Failed' outcome before
118
+ * returning to idle — long enough to read, short enough that a retry is never blocked.
119
+ */
120
+ const DEBUG_LOG_SUBMIT_RESULT_FLASH_MS = 4000;
115
121
  /**
116
122
  * The image types BOTH vendors accept, and therefore the only ones sent as image blocks.
117
123
  *
@@ -384,6 +390,25 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
384
390
  * element has its own settings modal.
385
391
  */
386
392
  this.downloadingDebugLog = false;
393
+ /**
394
+ * True while `submitDebugLog` is building and handing off the log. Same role as
395
+ * `downloadingDebugLog` for the Send button: the lifetime-stitch and the host's
396
+ * `send` callback are async, so the button needs a visible in-flight state.
397
+ */
398
+ this.submittingDebugLog = false;
399
+ /**
400
+ * Outcome of the last log submission, shown briefly on the button ('Sent' /
401
+ * 'Failed — retry') then cleared by `setDebugLogSubmitResult`'s timer. `null` when idle.
402
+ */
403
+ this.debugLogSubmitResult = null;
404
+ /**
405
+ * Whether the agent-log compose box (note + Send/Cancel) is open. Opened by the row's
406
+ * Send button when `debugLogSubmit.note` is not `false`; closed by Cancel or a
407
+ * successful send. Stays open after a failure so the typed note is not lost.
408
+ */
409
+ this.debugLogComposeOpen = false;
410
+ /** The note typed in the compose box, raw (trimmed only at submit time). */
411
+ this.debugLogNote = '';
387
412
  /** Active tab inside the settings modal. */
388
413
  this.settingsModalTab = 'settings';
389
414
  /**
@@ -2986,6 +3011,9 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
2986
3011
  super.disconnectedCallback();
2987
3012
  this._sessionActive = false;
2988
3013
  this.stopLoadingTimer();
3014
+ // The 'Sent'/'Failed' flash timer would otherwise fire against a torn-down element.
3015
+ clearTimeout(this.debugLogSubmitResultTimer);
3016
+ this.debugLogSubmitResultTimer = undefined;
2989
3017
  this.state = 'idle';
2990
3018
  // Note: `compacting`/`restoring` are NOT cleared here. They're written through the
2991
3019
  // in-flight op's captured session store (see `initPersistenceForNewStore` /
@@ -4348,6 +4376,42 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
4348
4376
  void this.chatConfig;
4349
4377
  return this.resolvedCostHistory.showTokens;
4350
4378
  }
4379
+ /**
4380
+ * Whether the settings "Agent log" section renders — shown when either action is
4381
+ * enabled: the download button (`allowDebugDownload`) or the host-transport Send
4382
+ * button (`debugLogSubmit`). Each button is additionally gated on its own flag
4383
+ * inside the section, so a host can enable one without the other.
4384
+ */
4385
+ get settingsAgentLogSectionVisible() {
4386
+ void this.chatConfig;
4387
+ const ui = this.chatConfig.ui;
4388
+ return (ui === null || ui === void 0 ? void 0 : ui.allowDebugDownload) === true || (ui === null || ui === void 0 ? void 0 : ui.debugLogSubmit) != null;
4389
+ }
4390
+ /**
4391
+ * The resolved compose-box settings for the agent-log Send button, or `null` when the
4392
+ * host opted out (`debugLogSubmit.note: false`) and the button sends on first click.
4393
+ */
4394
+ get debugLogNoteConfig() {
4395
+ var _a;
4396
+ void this.chatConfig;
4397
+ return resolveDebugLogNoteConfig((_a = this.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.debugLogSubmit);
4398
+ }
4399
+ /**
4400
+ * Whether the compose box's Send button is inert: a send is already in flight, or the
4401
+ * host made the note required and it is still blank.
4402
+ */
4403
+ get debugLogSendDisabled() {
4404
+ if (this.submittingDebugLog)
4405
+ return true;
4406
+ const note = this.debugLogNoteConfig;
4407
+ return (note === null || note === void 0 ? void 0 : note.required) === true && normalizeDebugLogNote(this.debugLogNote) === undefined;
4408
+ }
4409
+ /** Live "typed / max" counter for the compose box. */
4410
+ get debugLogNoteCounter() {
4411
+ var _a;
4412
+ const max = (_a = this.debugLogNoteConfig) === null || _a === void 0 ? void 0 : _a.maxLength;
4413
+ return max ? `${this.debugLogNote.length}/${max}` : '';
4414
+ }
4351
4415
  /** Whether the chat header (and settings cog) should render. */
4352
4416
  get settingsCogVisible() {
4353
4417
  void this.chatConfig;
@@ -4360,6 +4424,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
4360
4424
  (ui === null || ui === void 0 ? void 0 : ui.showNarration) != null ||
4361
4425
  (ui === null || ui === void 0 ? void 0 : ui.showAgentSwitchIndicator) != null ||
4362
4426
  (ui === null || ui === void 0 ? void 0 : ui.allowDebugDownload) === true ||
4427
+ (ui === null || ui === void 0 ? void 0 : ui.debugLogSubmit) != null ||
4363
4428
  (ui === null || ui === void 0 ? void 0 : ui.animations) != null ||
4364
4429
  cost.showCost ||
4365
4430
  cost.showTokens ||
@@ -4742,6 +4807,96 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
4742
4807
  }
4743
4808
  });
4744
4809
  }
4810
+ /**
4811
+ * Hand the agent log to the host's `ui.debugLogSubmit.send` callback — the Send
4812
+ * button next to Download. Reuses `buildDownloadLog`, so the submitted log is
4813
+ * byte-identical to what Download saves (lifetime-stitched when the provider
4814
+ * persists diagnostics). The assistant is vendor-agnostic: the callback owns the
4815
+ * transport (Sentry feedback, a support API, an upload endpoint). Outcome lands in
4816
+ * `debugLogSubmitResult` so the button can flash 'Sent'/'Failed'; failures also go
4817
+ * to the logger with the cause.
4818
+ */
4819
+ submitDebugLog() {
4820
+ return __awaiter(this, void 0, void 0, function* () {
4821
+ var _a, _b, _c, _d;
4822
+ if (this.submittingDebugLog || this.debugLogSendDisabled)
4823
+ return;
4824
+ const config = (_a = this.chatConfig.ui) === null || _a === void 0 ? void 0 : _a.debugLogSubmit;
4825
+ if (!config)
4826
+ return;
4827
+ const agentName = (_d = (_c = (_b = this.activeAgent) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : this.headerTitle) !== null && _d !== void 0 ? _d : 'chat';
4828
+ const timestamp = new Date().toISOString().replace(/:/g, '-');
4829
+ const fileStem = agentName.toLowerCase().replace(/\s+/g, '-');
4830
+ // Only a note the user could actually type travels with the log: with the compose box
4831
+ // opted out there is no note, whatever a stale `debugLogNote` might hold.
4832
+ const message = this.debugLogNoteConfig === null ? undefined : normalizeDebugLogNote(this.debugLogNote);
4833
+ this.submittingDebugLog = true;
4834
+ try {
4835
+ const payload = yield this.buildDownloadLog();
4836
+ yield config.send({
4837
+ logJson: JSON.stringify(payload, null, 2),
4838
+ filename: `${fileStem}-${timestamp}.json`,
4839
+ agentName,
4840
+ host: window.location.host,
4841
+ message,
4842
+ });
4843
+ // Success consumes the note; a failure (below) keeps the box open with it intact.
4844
+ this.debugLogComposeOpen = false;
4845
+ this.debugLogNote = '';
4846
+ this.setDebugLogSubmitResult('sent');
4847
+ }
4848
+ catch (e) {
4849
+ logger.error('Submitting agent log failed:', e);
4850
+ this.setDebugLogSubmitResult('error');
4851
+ }
4852
+ finally {
4853
+ this.submittingDebugLog = false;
4854
+ }
4855
+ });
4856
+ }
4857
+ /**
4858
+ * The row's Send button. With a compose box configured (the default) it opens the box
4859
+ * and focuses the note so the user can caption the log; with `note: false` it sends
4860
+ * straight away, as before.
4861
+ */
4862
+ openDebugLogCompose() {
4863
+ if (this.submittingDebugLog)
4864
+ return;
4865
+ if (this.debugLogNoteConfig === null) {
4866
+ void this.submitDebugLog();
4867
+ return;
4868
+ }
4869
+ // Opening again resets a lingering 'Failed' flash so the box starts clean.
4870
+ clearTimeout(this.debugLogSubmitResultTimer);
4871
+ this.debugLogSubmitResultTimer = undefined;
4872
+ this.debugLogSubmitResult = null;
4873
+ this.debugLogComposeOpen = true;
4874
+ // Next frame so the `when` block has rendered the text-area. The design-system
4875
+ // text-area does not delegate focus (see `maybeAutoFocusChatInput`), so drill in.
4876
+ requestAnimationFrame(() => {
4877
+ var _a, _b, _c;
4878
+ if (!this.debugLogComposeOpen || !this.isConnected)
4879
+ return;
4880
+ const el = this.debugLogNoteEl;
4881
+ (_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();
4882
+ });
4883
+ }
4884
+ /** Close the compose box and discard the typed note. */
4885
+ cancelDebugLogCompose() {
4886
+ if (this.submittingDebugLog)
4887
+ return;
4888
+ this.debugLogComposeOpen = false;
4889
+ this.debugLogNote = '';
4890
+ }
4891
+ /** Flash the submit outcome on the Send button, then fall back to idle. */
4892
+ setDebugLogSubmitResult(result) {
4893
+ clearTimeout(this.debugLogSubmitResultTimer);
4894
+ this.debugLogSubmitResult = result;
4895
+ this.debugLogSubmitResultTimer = setTimeout(() => {
4896
+ this.debugLogSubmitResult = null;
4897
+ this.debugLogSubmitResultTimer = undefined;
4898
+ }, DEBUG_LOG_SUBMIT_RESULT_FLASH_MS);
4899
+ }
4745
4900
  /**
4746
4901
  * The log to download (GENC-1351 §5.8). When the provider persists diagnostics,
4747
4902
  * stitch the **whole lifetime** stream (every page load) rather than just the
@@ -5597,6 +5752,18 @@ __decorate([
5597
5752
  __decorate([
5598
5753
  observable
5599
5754
  ], FoundationAiAssistant.prototype, "downloadingDebugLog", void 0);
5755
+ __decorate([
5756
+ observable
5757
+ ], FoundationAiAssistant.prototype, "submittingDebugLog", void 0);
5758
+ __decorate([
5759
+ observable
5760
+ ], FoundationAiAssistant.prototype, "debugLogSubmitResult", void 0);
5761
+ __decorate([
5762
+ observable
5763
+ ], FoundationAiAssistant.prototype, "debugLogComposeOpen", void 0);
5764
+ __decorate([
5765
+ observable
5766
+ ], FoundationAiAssistant.prototype, "debugLogNote", void 0);
5600
5767
  __decorate([
5601
5768
  observable
5602
5769
  ], FoundationAiAssistant.prototype, "settingsModalTab", void 0);
@@ -5744,6 +5911,18 @@ __decorate([
5744
5911
  __decorate([
5745
5912
  volatile
5746
5913
  ], FoundationAiAssistant.prototype, "settingsSessionTokensVisible", null);
5914
+ __decorate([
5915
+ volatile
5916
+ ], FoundationAiAssistant.prototype, "settingsAgentLogSectionVisible", null);
5917
+ __decorate([
5918
+ volatile
5919
+ ], FoundationAiAssistant.prototype, "debugLogNoteConfig", null);
5920
+ __decorate([
5921
+ volatile
5922
+ ], FoundationAiAssistant.prototype, "debugLogSendDisabled", null);
5923
+ __decorate([
5924
+ volatile
5925
+ ], FoundationAiAssistant.prototype, "debugLogNoteCounter", null);
5747
5926
  __decorate([
5748
5927
  volatile
5749
5928
  ], FoundationAiAssistant.prototype, "settingsCogVisible", null);
@@ -0,0 +1,31 @@
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
+ /**
6
+ * Resolve `debugLogSubmit.note` — `undefined`/`true` mean "compose box with defaults",
7
+ * `false` means "no compose box, send on first click" (→ `null`), and an object customises
8
+ * the box. Keeps the template free of the boolean-or-object dance.
9
+ */
10
+ export function resolveDebugLogNoteConfig(config) {
11
+ var _a;
12
+ if (!config || config.note === false)
13
+ return null;
14
+ const note = typeof config.note === 'object' ? config.note : {};
15
+ const maxLength = typeof note.maxLength === 'number' && note.maxLength > 0
16
+ ? Math.floor(note.maxLength)
17
+ : DEBUG_LOG_NOTE_DEFAULT_MAX_LENGTH;
18
+ return {
19
+ placeholder: (_a = note.placeholder) !== null && _a !== void 0 ? _a : DEBUG_LOG_NOTE_DEFAULT_PLACEHOLDER,
20
+ required: note.required === true,
21
+ maxLength,
22
+ };
23
+ }
24
+ /**
25
+ * Normalise the typed note for the submission: trimmed, and `undefined` when empty so a
26
+ * host can `message ?? fallback` without checking for whitespace.
27
+ */
28
+ export function normalizeDebugLogNote(note) {
29
+ const trimmed = note.trim();
30
+ return trimmed.length > 0 ? trimmed : undefined;
31
+ }
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.23.1-agent-rules.1",
4
+ "version": "15.23.1-agent-log-submit.2",
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.23.1-agent-rules.1",
78
- "@genesislcap/genx": "15.23.1-agent-rules.1",
79
- "@genesislcap/rollup-builder": "15.23.1-agent-rules.1",
80
- "@genesislcap/ts-builder": "15.23.1-agent-rules.1",
81
- "@genesislcap/uvu-playwright-builder": "15.23.1-agent-rules.1",
82
- "@genesislcap/vite-builder": "15.23.1-agent-rules.1",
83
- "@genesislcap/webpack-builder": "15.23.1-agent-rules.1",
77
+ "@genesislcap/foundation-testing": "15.23.1-agent-log-submit.2",
78
+ "@genesislcap/genx": "15.23.1-agent-log-submit.2",
79
+ "@genesislcap/rollup-builder": "15.23.1-agent-log-submit.2",
80
+ "@genesislcap/ts-builder": "15.23.1-agent-log-submit.2",
81
+ "@genesislcap/uvu-playwright-builder": "15.23.1-agent-log-submit.2",
82
+ "@genesislcap/vite-builder": "15.23.1-agent-log-submit.2",
83
+ "@genesislcap/webpack-builder": "15.23.1-agent-log-submit.2",
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.23.1-agent-rules.1",
90
- "@genesislcap/foundation-logger": "15.23.1-agent-rules.1",
91
- "@genesislcap/foundation-notifications": "15.23.1-agent-rules.1",
92
- "@genesislcap/foundation-redux": "15.23.1-agent-rules.1",
93
- "@genesislcap/foundation-ui": "15.23.1-agent-rules.1",
94
- "@genesislcap/foundation-utils": "15.23.1-agent-rules.1",
95
- "@genesislcap/rapid-design-system": "15.23.1-agent-rules.1",
96
- "@genesislcap/web-core": "15.23.1-agent-rules.1",
89
+ "@genesislcap/foundation-ai": "15.23.1-agent-log-submit.2",
90
+ "@genesislcap/foundation-logger": "15.23.1-agent-log-submit.2",
91
+ "@genesislcap/foundation-notifications": "15.23.1-agent-log-submit.2",
92
+ "@genesislcap/foundation-redux": "15.23.1-agent-log-submit.2",
93
+ "@genesislcap/foundation-ui": "15.23.1-agent-log-submit.2",
94
+ "@genesislcap/foundation-utils": "15.23.1-agent-log-submit.2",
95
+ "@genesislcap/rapid-design-system": "15.23.1-agent-log-submit.2",
96
+ "@genesislcap/web-core": "15.23.1-agent-log-submit.2",
97
97
  "dompurify": "^3.3.1",
98
98
  "marked": "^17.0.3"
99
99
  },