@genesislcap/ai-assistant 14.436.0-FUI-2489.2 → 14.437.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.
Files changed (31) hide show
  1. package/api-extractor.json +9 -9
  2. package/dist/ai-assistant.api.json +73 -47
  3. package/dist/ai-assistant.d.ts +26 -21
  4. package/dist/dts/components/chat-driver/chat-driver.d.ts +6 -3
  5. package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
  6. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
  7. package/dist/dts/components/popout-manager/popout-manager.d.ts.map +1 -1
  8. package/dist/dts/main/main.d.ts +4 -4
  9. package/dist/dts/main/main.d.ts.map +1 -1
  10. package/dist/dts/state/ai-assistant-slice.d.ts +12 -10
  11. package/dist/dts/state/ai-assistant-slice.d.ts.map +1 -1
  12. package/dist/dts/state/session-store.d.ts +2 -2
  13. package/dist/dts/suggestions/chat-suggestions.d.ts.map +1 -1
  14. package/dist/dts/tags/index.d.ts +0 -1
  15. package/dist/dts/tags/index.d.ts.map +1 -1
  16. package/dist/esm/components/chat-driver/chat-driver.js +25 -9
  17. package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +12 -6
  18. package/dist/esm/components/popout-manager/popout-manager.js +0 -1
  19. package/dist/esm/main/main.js +29 -8
  20. package/dist/esm/state/ai-assistant-slice.js +5 -5
  21. package/dist/esm/suggestions/chat-suggestions.js +2 -1
  22. package/dist/esm/tags/index.js +0 -1
  23. package/package.json +16 -16
  24. package/src/components/chat-driver/chat-driver.ts +49 -14
  25. package/src/components/orchestrating-driver/orchestrating-driver.ts +16 -6
  26. package/src/components/popout-manager/popout-manager.ts +0 -1
  27. package/src/main/main.ts +29 -8
  28. package/src/state/ai-assistant-slice.ts +15 -15
  29. package/src/suggestions/chat-suggestions.ts +2 -9
  30. package/src/tags/index.ts +0 -1
  31. package/tsconfig.json +3 -1
@@ -90,6 +90,12 @@ export class OrchestratingDriver extends EventTarget {
90
90
  this.chatDriver.addEventListener('sub-agent-stop', (e) => {
91
91
  this.dispatchEvent(new CustomEvent('sub-agent-stop', { detail: e.detail }));
92
92
  });
93
+ this.chatDriver.addEventListener('interaction-start', (e) => {
94
+ this.dispatchEvent(new CustomEvent('interaction-start', { detail: e.detail }));
95
+ });
96
+ this.chatDriver.addEventListener('interaction-stop', (e) => {
97
+ this.dispatchEvent(new CustomEvent('interaction-stop', { detail: e.detail }));
98
+ });
93
99
  }
94
100
  resolveInteraction(interactionId, result) {
95
101
  this.chatDriver.resolveInteraction(interactionId, result);
@@ -150,18 +156,18 @@ export class OrchestratingDriver extends EventTarget {
150
156
  let handoffSummary = '';
151
157
  let remainingTask = '';
152
158
  while (true) {
153
- // oxlint-disable-next-line no-await-in-loop
159
+ // eslint-disable-next-line no-await-in-loop
154
160
  yield this.applyAgent(currentAgent);
155
161
  let result;
156
162
  if (isHandoff) {
157
163
  const contextPrimer = handoffSummary
158
164
  ? [{ role: 'user', content: `[Context from previous agent]: ${handoffSummary}` }]
159
165
  : [];
160
- // oxlint-disable-next-line no-await-in-loop
166
+ // eslint-disable-next-line no-await-in-loop
161
167
  result = yield this.chatDriver.continueFromHistory(contextPrimer);
162
168
  }
163
169
  else {
164
- // oxlint-disable-next-line no-await-in-loop
170
+ // eslint-disable-next-line no-await-in-loop
165
171
  result = yield this.chatDriver.sendMessage(input, attachments);
166
172
  }
167
173
  // Release check: a stateful agent called `releaseAgent` from a terminal
@@ -169,7 +175,7 @@ export class OrchestratingDriver extends EventTarget {
169
175
  // classifier-mode. The LLM has already emitted its final wrap-up message
170
176
  // by the time we get here — release is purely a teardown.
171
177
  if (this.chatDriver.getAgentReleaseRequested()) {
172
- // oxlint-disable-next-line no-await-in-loop
178
+ // eslint-disable-next-line no-await-in-loop
173
179
  yield this.releaseActiveAgent();
174
180
  break;
175
181
  }
@@ -188,7 +194,7 @@ export class OrchestratingDriver extends EventTarget {
188
194
  isHandoff = true;
189
195
  const updatedHistory = this.chatDriver.getHistory();
190
196
  this.dispatchEvent(new CustomEvent('orchestrating-start'));
191
- // oxlint-disable-next-line no-await-in-loop
197
+ // eslint-disable-next-line no-await-in-loop
192
198
  currentAgent = yield this.classify(remainingTask, updatedHistory);
193
199
  }
194
200
  return { reason: 'done' };
@@ -386,7 +392,7 @@ export class OrchestratingDriver extends EventTarget {
386
392
  systemPrompt: classifierPrompt,
387
393
  tools: [routingTool],
388
394
  };
389
- // oxlint-disable-next-line no-await-in-loop
395
+ // eslint-disable-next-line no-await-in-loop
390
396
  const response = yield this.aiProvider.chat([], input, options);
391
397
  const tc = (_b = response.toolCalls) === null || _b === void 0 ? void 0 : _b[0];
392
398
  const index = (tc === null || tc === void 0 ? void 0 : tc.name) === 'select_agent' ? tc.args.agent_index : -1;
@@ -35,7 +35,6 @@ let FoundationAiPopoutManager = class FoundationAiPopoutManager extends GenesisE
35
35
  }
36
36
  connectedCallback() {
37
37
  super.connectedCallback();
38
- // oxlint-disable-next-line typescript-eslint(no-this-alias) -- module-level singleton, not a local alias
39
38
  _aiPopoutManager = this;
40
39
  const bubble = this.querySelector('foundation-ai-chat-bubble');
41
40
  const assistantEl = bubble === null || bubble === void 0 ? void 0 : bubble.querySelector('[slot="dialog-content"]');
@@ -300,22 +300,22 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
300
300
  (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.setLiveSubAgentName(value);
301
301
  }
302
302
  /**
303
- * In-flight per-call chat-input overrides pushed by `requestSubAgent`
304
- * invocations. Empty means no override is active.
303
+ * In-flight per-call chat-input overrides pushed by `requestSubAgent` or
304
+ * `requestInteraction` calls. Empty means no override is active.
305
305
  */
306
- get subAgentInputOverrides() {
306
+ get inputOverrides() {
307
307
  var _a, _b;
308
- return (_b = (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.store.aiAssistant.subAgentInputOverrides) !== null && _b !== void 0 ? _b : [];
308
+ return (_b = (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.store.aiAssistant.inputOverrides) !== null && _b !== void 0 ? _b : [];
309
309
  }
310
310
  /**
311
311
  * Resolves the effective chat-input behaviour while the assistant is
312
- * executing. Sub-agent overrides take precedence over the agent-level
312
+ * executing. Per-call overrides take precedence over the agent-level
313
313
  * config; among overrides the most restrictive wins (`'hidden'` >
314
314
  * `'disabled'`). Falls back to the active agent's config, then `'disabled'`.
315
315
  */
316
316
  get effectiveChatInputDuringExecution() {
317
317
  var _a, _b;
318
- const overrides = this.subAgentInputOverrides;
318
+ const overrides = this.inputOverrides;
319
319
  if (overrides.some((o) => o.mode === 'hidden'))
320
320
  return 'hidden';
321
321
  if (overrides.some((o) => o.mode === 'disabled'))
@@ -570,7 +570,7 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
570
570
  const { invocationId, chatInputDuringExecution } = e.detail;
571
571
  if (!chatInputDuringExecution)
572
572
  return;
573
- (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.addSubAgentInputOverride({
573
+ (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.addInputOverride({
574
574
  id: invocationId,
575
575
  mode: chatInputDuringExecution,
576
576
  });
@@ -581,17 +581,38 @@ let FoundationAiAssistant = FoundationAiAssistant_1 = class FoundationAiAssistan
581
581
  this.liveSubAgentName = null;
582
582
  const { invocationId } = e.detail;
583
583
  if (invocationId) {
584
- (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.removeSubAgentInputOverride({ id: invocationId });
584
+ (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.removeInputOverride({ id: invocationId });
585
+ }
586
+ };
587
+ const onInteractionStart = (e) => {
588
+ var _a;
589
+ const { interactionId, chatInputDuringExecution } = e.detail;
590
+ if (!chatInputDuringExecution)
591
+ return;
592
+ (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.addInputOverride({
593
+ id: interactionId,
594
+ mode: chatInputDuringExecution,
595
+ });
596
+ };
597
+ const onInteractionStop = (e) => {
598
+ var _a;
599
+ const { interactionId } = e.detail;
600
+ if (interactionId) {
601
+ (_a = this._sessionRef) === null || _a === void 0 ? void 0 : _a.actions.aiAssistant.removeInputOverride({ id: interactionId });
585
602
  }
586
603
  };
587
604
  driver.addEventListener('sub-agent-history-updated', onSubAgentHistoryUpdated);
588
605
  driver.addEventListener('sub-agent-start', onSubAgentStart);
589
606
  driver.addEventListener('sub-agent-stop', onSubAgentStop);
607
+ driver.addEventListener('interaction-start', onInteractionStart);
608
+ driver.addEventListener('interaction-stop', onInteractionStop);
590
609
  const cleanups = [
591
610
  () => driver.removeEventListener('history-updated', onHistoryUpdated),
592
611
  () => driver.removeEventListener('sub-agent-history-updated', onSubAgentHistoryUpdated),
593
612
  () => driver.removeEventListener('sub-agent-start', onSubAgentStart),
594
613
  () => driver.removeEventListener('sub-agent-stop', onSubAgentStop),
614
+ () => driver.removeEventListener('interaction-start', onInteractionStart),
615
+ () => driver.removeEventListener('interaction-stop', onInteractionStop),
595
616
  ];
596
617
  if (driver instanceof OrchestratingDriver) {
597
618
  // Restore any pinned agent from the session store onto the freshly built
@@ -15,7 +15,7 @@ export const defaultSessionState = {
15
15
  inputValue: '',
16
16
  liveSubAgentTrace: [],
17
17
  liveSubAgentName: null,
18
- subAgentInputOverrides: [],
18
+ inputOverrides: [],
19
19
  };
20
20
  export const aiAssistantSlice = createSlice({
21
21
  name: 'aiAssistant',
@@ -66,11 +66,11 @@ export const aiAssistantSlice = createSlice({
66
66
  setLiveSubAgentName(state, action) {
67
67
  state.liveSubAgentName = action.payload;
68
68
  },
69
- addSubAgentInputOverride(state, action) {
70
- state.subAgentInputOverrides.push(action.payload);
69
+ addInputOverride(state, action) {
70
+ state.inputOverrides.push(action.payload);
71
71
  },
72
- removeSubAgentInputOverride(state, action) {
73
- state.subAgentInputOverrides = state.subAgentInputOverrides.filter((o) => o.id !== action.payload.id);
72
+ removeInputOverride(state, action) {
73
+ state.inputOverrides = state.inputOverrides.filter((o) => o.id !== action.payload.id);
74
74
  },
75
75
  },
76
76
  selectors: {},
@@ -1,5 +1,6 @@
1
1
  import { __decorate } from "tslib";
2
- import { customElement, GenesisElement, observable, html, css, repeat, when, } from '@genesislcap/web-core';
2
+ import { customElement, observable, html, css, repeat, when } from '@genesislcap/web-core';
3
+ import { GenesisElement } from '@genesislcap/web-core';
3
4
  const PILL_ANIMATION_STAGGER_MS = 70;
4
5
  const template = html `
5
6
  ${when((x) => x.state.status === 'loading', html `
@@ -1 +0,0 @@
1
- export {};
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": "14.436.0-FUI-2489.2",
4
+ "version": "14.437.0",
5
5
  "license": "SEE LICENSE IN license.txt",
6
6
  "main": "dist/esm/index.js",
7
7
  "types": "dist/ai-assistant.d.ts",
@@ -64,24 +64,24 @@
64
64
  }
65
65
  },
66
66
  "devDependencies": {
67
- "@genesislcap/foundation-testing": "14.436.0-FUI-2489.2",
68
- "@genesislcap/genx": "14.436.0-FUI-2489.2",
69
- "@genesislcap/rollup-builder": "14.436.0-FUI-2489.2",
70
- "@genesislcap/ts-builder": "14.436.0-FUI-2489.2",
71
- "@genesislcap/uvu-playwright-builder": "14.436.0-FUI-2489.2",
72
- "@genesislcap/vite-builder": "14.436.0-FUI-2489.2",
73
- "@genesislcap/webpack-builder": "14.436.0-FUI-2489.2",
67
+ "@genesislcap/foundation-testing": "14.437.0",
68
+ "@genesislcap/genx": "14.437.0",
69
+ "@genesislcap/rollup-builder": "14.437.0",
70
+ "@genesislcap/ts-builder": "14.437.0",
71
+ "@genesislcap/uvu-playwright-builder": "14.437.0",
72
+ "@genesislcap/vite-builder": "14.437.0",
73
+ "@genesislcap/webpack-builder": "14.437.0",
74
74
  "@types/dompurify": "^3.0.5",
75
75
  "@types/marked": "^5.0.2"
76
76
  },
77
77
  "dependencies": {
78
- "@genesislcap/foundation-ai": "14.436.0-FUI-2489.2",
79
- "@genesislcap/foundation-logger": "14.436.0-FUI-2489.2",
80
- "@genesislcap/foundation-redux": "14.436.0-FUI-2489.2",
81
- "@genesislcap/foundation-ui": "14.436.0-FUI-2489.2",
82
- "@genesislcap/foundation-utils": "14.436.0-FUI-2489.2",
83
- "@genesislcap/rapid-design-system": "14.436.0-FUI-2489.2",
84
- "@genesislcap/web-core": "14.436.0-FUI-2489.2",
78
+ "@genesislcap/foundation-ai": "14.437.0",
79
+ "@genesislcap/foundation-logger": "14.437.0",
80
+ "@genesislcap/foundation-redux": "14.437.0",
81
+ "@genesislcap/foundation-ui": "14.437.0",
82
+ "@genesislcap/foundation-utils": "14.437.0",
83
+ "@genesislcap/rapid-design-system": "14.437.0",
84
+ "@genesislcap/web-core": "14.437.0",
85
85
  "dompurify": "^3.3.1",
86
86
  "marked": "^17.0.3"
87
87
  },
@@ -93,5 +93,5 @@
93
93
  "publishConfig": {
94
94
  "access": "public"
95
95
  },
96
- "gitHead": "16309f873a08dfa541589cd172482677ff5dfd20"
96
+ "gitHead": "c30c777f0ab5e45f4e931a9cc1200ac85bcace35"
97
97
  }
@@ -7,6 +7,7 @@ import type {
7
7
  ChatToolCall,
8
8
  ChatToolDefinition,
9
9
  ChatToolHandlers,
10
+ InteractionRequestOptions,
10
11
  SubAgentRequestOptions,
11
12
  } from '@genesislcap/foundation-ai';
12
13
  import { MalformedFunctionCallError } from '@genesislcap/foundation-ai';
@@ -89,7 +90,12 @@ export class ChatDriver extends EventTarget implements AiDriver {
89
90
  private busy = false;
90
91
  private pendingInteractions = new Map<
91
92
  string,
92
- { resolve: (value: any) => void; reject: (reason?: any) => void }
93
+ {
94
+ resolve: (value: any) => void;
95
+ reject: (reason?: any) => void;
96
+ /** Present when the call requested a chat-input override. */
97
+ overrideId?: string;
98
+ }
93
99
  >();
94
100
 
95
101
  private systemPrompt?: SystemPromptInput;
@@ -117,7 +123,11 @@ export class ChatDriver extends EventTarget implements AiDriver {
117
123
  * widget renders in — and resolves through — the parent (ultimately the
118
124
  * root) driver, where the main UI is listening.
119
125
  */
120
- private hostInteractionRequester?: <T>(componentName: string, data: any) => Promise<T>;
126
+ private hostInteractionRequester?: <T>(
127
+ componentName: string,
128
+ data: any,
129
+ options?: InteractionRequestOptions,
130
+ ) => Promise<T>;
121
131
  /**
122
132
  * When set (e.g. by OrchestratingDriver), applied only to the conversation slice
123
133
  * sent to the model — stored `history` stays unchanged for UI and logging.
@@ -385,7 +395,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
385
395
  * naturally: a grandchild → child → root.
386
396
  */
387
397
  public setHostInteractionRequester(
388
- fn: <T>(componentName: string, data: any) => Promise<T>,
398
+ fn: <T>(componentName: string, data: any, options?: InteractionRequestOptions) => Promise<T>,
389
399
  ): void {
390
400
  this.hostInteractionRequester = fn;
391
401
  }
@@ -403,10 +413,17 @@ export class ChatDriver extends EventTarget implements AiDriver {
403
413
  *
404
414
  * @param componentName - The custom element name to render.
405
415
  * @param data - Data to pass to the component.
416
+ * @param options - Optional per-call overrides, including
417
+ * `chatInputDuringExecution` to hide or disable the main chat input while
418
+ * the widget is awaiting user input. Reverts when the interaction resolves.
406
419
  */
407
- public async requestInteraction<T>(componentName: string, data: any): Promise<T> {
420
+ public async requestInteraction<T>(
421
+ componentName: string,
422
+ data: any,
423
+ options?: InteractionRequestOptions,
424
+ ): Promise<T> {
408
425
  if (this.hostInteractionRequester) {
409
- return this.hostInteractionRequester<T>(componentName, data);
426
+ return this.hostInteractionRequester<T>(componentName, data, options);
410
427
  }
411
428
  if (this.pendingInteractions.size > 0) {
412
429
  throw new Error(
@@ -416,8 +433,20 @@ export class ChatDriver extends EventTarget implements AiDriver {
416
433
  );
417
434
  }
418
435
  const interactionId = crypto.randomUUID();
436
+ const chatInputDuringExecution = options?.chatInputDuringExecution;
419
437
  return new Promise((resolve, reject) => {
420
- this.pendingInteractions.set(interactionId, { resolve, reject });
438
+ this.pendingInteractions.set(interactionId, {
439
+ resolve,
440
+ reject,
441
+ overrideId: chatInputDuringExecution ? interactionId : undefined,
442
+ });
443
+ if (chatInputDuringExecution) {
444
+ this.dispatchEvent(
445
+ new CustomEvent('interaction-start', {
446
+ detail: { interactionId, chatInputDuringExecution },
447
+ }),
448
+ );
449
+ }
421
450
  this.appendToHistory({
422
451
  role: 'assistant',
423
452
  content: '',
@@ -445,6 +474,9 @@ export class ChatDriver extends EventTarget implements AiDriver {
445
474
  }),
446
475
  );
447
476
  }
477
+ if (interaction.overrideId) {
478
+ this.dispatchEvent(new CustomEvent('interaction-stop', { detail: { interactionId } }));
479
+ }
448
480
  interaction.resolve(result);
449
481
  this.pendingInteractions.delete(interactionId);
450
482
  } else {
@@ -499,8 +531,11 @@ export class ChatDriver extends EventTarget implements AiDriver {
499
531
  */
500
532
  private buildHandlerContext(traceCapture?: { trace?: ChatMessage[] }) {
501
533
  return {
502
- requestInteraction: <T>(componentName: string, data: any): Promise<T> =>
503
- this.requestInteraction(componentName, data),
534
+ requestInteraction: <T>(
535
+ componentName: string,
536
+ data: any,
537
+ options?: InteractionRequestOptions,
538
+ ): Promise<T> => this.requestInteraction(componentName, data, options),
504
539
  ...(this.subAgentsMap.size > 0 && {
505
540
  requestSubAgent: <T = never>(
506
541
  name: string,
@@ -581,8 +616,8 @@ export class ChatDriver extends EventTarget implements AiDriver {
581
616
  // pending map the main UI is wired to. Recurses naturally for nested
582
617
  // sub-agents.
583
618
  child.setHostInteractionRequester(
584
- <R>(componentName: string, data: any): Promise<R> =>
585
- this.requestInteraction<R>(componentName, data),
619
+ <R>(componentName: string, data: any, opts?: InteractionRequestOptions): Promise<R> =>
620
+ this.requestInteraction<R>(componentName, data, opts),
586
621
  );
587
622
 
588
623
  const forwardTrace = (e: Event) => {
@@ -839,13 +874,13 @@ export class ChatDriver extends EventTarget implements AiDriver {
839
874
  // Sequential await is required — each iteration must see fresh values
840
875
  // before constructing the LLM request.
841
876
  if (this.toolDefinitionsFactory) {
842
- // oxlint-disable-next-line no-await-in-loop
877
+ // eslint-disable-next-line no-await-in-loop
843
878
  this.toolDefinitions = await this.toolDefinitionsFactory(promptCtx);
844
879
  }
845
880
 
846
881
  const resolvedSystemPrompt =
847
882
  typeof this.systemPrompt === 'function'
848
- ? // oxlint-disable-next-line no-await-in-loop
883
+ ? // eslint-disable-next-line no-await-in-loop
849
884
  await this.systemPrompt(promptCtx)
850
885
  : this.systemPrompt;
851
886
 
@@ -891,7 +926,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
891
926
 
892
927
  let response: ChatMessage;
893
928
  try {
894
- // oxlint-disable-next-line no-await-in-loop
929
+ // eslint-disable-next-line no-await-in-loop
895
930
  response = await this.aiProvider.chat!(historyForCall, userInputForCall, options);
896
931
  } catch (e) {
897
932
  if (e instanceof MalformedFunctionCallError) {
@@ -961,7 +996,7 @@ export class ChatDriver extends EventTarget implements AiDriver {
961
996
  let hitUnknownToolLimit = false;
962
997
 
963
998
  if (toolCalls.length > 0) {
964
- // oxlint-disable-next-line no-await-in-loop -- Promise.all is correct parallelism, not sequential loop awaiting
999
+ // eslint-disable-next-line no-await-in-loop
965
1000
  await Promise.all(
966
1001
  toolCalls.map(async (tc) => {
967
1002
  // Check for fold facade
@@ -156,6 +156,16 @@ export class OrchestratingDriver extends EventTarget implements AiDriver {
156
156
  this.chatDriver.addEventListener('sub-agent-stop', (e: Event) => {
157
157
  this.dispatchEvent(new CustomEvent('sub-agent-stop', { detail: (e as CustomEvent).detail }));
158
158
  });
159
+ this.chatDriver.addEventListener('interaction-start', (e: Event) => {
160
+ this.dispatchEvent(
161
+ new CustomEvent('interaction-start', { detail: (e as CustomEvent).detail }),
162
+ );
163
+ });
164
+ this.chatDriver.addEventListener('interaction-stop', (e: Event) => {
165
+ this.dispatchEvent(
166
+ new CustomEvent('interaction-stop', { detail: (e as CustomEvent).detail }),
167
+ );
168
+ });
159
169
  }
160
170
 
161
171
  resolveInteraction(interactionId: string, result: unknown): void {
@@ -230,7 +240,7 @@ export class OrchestratingDriver extends EventTarget implements AiDriver {
230
240
  let remainingTask = '';
231
241
 
232
242
  while (true) {
233
- // oxlint-disable-next-line no-await-in-loop
243
+ // eslint-disable-next-line no-await-in-loop
234
244
  await this.applyAgent(currentAgent);
235
245
 
236
246
  let result: ChatDriverResult;
@@ -238,10 +248,10 @@ export class OrchestratingDriver extends EventTarget implements AiDriver {
238
248
  const contextPrimer: ChatMessage[] = handoffSummary
239
249
  ? [{ role: 'user', content: `[Context from previous agent]: ${handoffSummary}` }]
240
250
  : [];
241
- // oxlint-disable-next-line no-await-in-loop
251
+ // eslint-disable-next-line no-await-in-loop
242
252
  result = await this.chatDriver.continueFromHistory(contextPrimer);
243
253
  } else {
244
- // oxlint-disable-next-line no-await-in-loop
254
+ // eslint-disable-next-line no-await-in-loop
245
255
  result = await this.chatDriver.sendMessage(input, attachments);
246
256
  }
247
257
 
@@ -250,7 +260,7 @@ export class OrchestratingDriver extends EventTarget implements AiDriver {
250
260
  // classifier-mode. The LLM has already emitted its final wrap-up message
251
261
  // by the time we get here — release is purely a teardown.
252
262
  if (this.chatDriver.getAgentReleaseRequested()) {
253
- // oxlint-disable-next-line no-await-in-loop
263
+ // eslint-disable-next-line no-await-in-loop
254
264
  await this.releaseActiveAgent();
255
265
  break;
256
266
  }
@@ -275,7 +285,7 @@ export class OrchestratingDriver extends EventTarget implements AiDriver {
275
285
 
276
286
  const updatedHistory = this.chatDriver.getHistory() as ChatMessage[];
277
287
  this.dispatchEvent(new CustomEvent('orchestrating-start'));
278
- // oxlint-disable-next-line no-await-in-loop
288
+ // eslint-disable-next-line no-await-in-loop
279
289
  currentAgent = await this.classify(remainingTask, updatedHistory);
280
290
  }
281
291
 
@@ -483,7 +493,7 @@ export class OrchestratingDriver extends EventTarget implements AiDriver {
483
493
  systemPrompt: classifierPrompt,
484
494
  tools: [routingTool],
485
495
  };
486
- // oxlint-disable-next-line no-await-in-loop
496
+ // eslint-disable-next-line no-await-in-loop
487
497
  const response = await this.aiProvider.chat!([], input, options);
488
498
  const tc = response.toolCalls?.[0];
489
499
  const index = tc?.name === 'select_agent' ? (tc.args.agent_index as number) : -1;
@@ -64,7 +64,6 @@ export class FoundationAiPopoutManager extends GenesisElement {
64
64
 
65
65
  connectedCallback() {
66
66
  super.connectedCallback();
67
- // oxlint-disable-next-line typescript-eslint(no-this-alias) -- module-level singleton, not a local alias
68
67
  _aiPopoutManager = this;
69
68
 
70
69
  const bubble = this.querySelector('foundation-ai-chat-bubble');
package/src/main/main.ts CHANGED
@@ -333,22 +333,22 @@ export class FoundationAiAssistant extends GenesisElement {
333
333
  }
334
334
 
335
335
  /**
336
- * In-flight per-call chat-input overrides pushed by `requestSubAgent`
337
- * invocations. Empty means no override is active.
336
+ * In-flight per-call chat-input overrides pushed by `requestSubAgent` or
337
+ * `requestInteraction` calls. Empty means no override is active.
338
338
  */
339
- get subAgentInputOverrides() {
340
- return this._sessionRef?.store.aiAssistant.subAgentInputOverrides ?? [];
339
+ get inputOverrides() {
340
+ return this._sessionRef?.store.aiAssistant.inputOverrides ?? [];
341
341
  }
342
342
 
343
343
  /**
344
344
  * Resolves the effective chat-input behaviour while the assistant is
345
- * executing. Sub-agent overrides take precedence over the agent-level
345
+ * executing. Per-call overrides take precedence over the agent-level
346
346
  * config; among overrides the most restrictive wins (`'hidden'` >
347
347
  * `'disabled'`). Falls back to the active agent's config, then `'disabled'`.
348
348
  */
349
349
  @volatile
350
350
  get effectiveChatInputDuringExecution(): ChatInputDuringExecutionMode {
351
- const overrides = this.subAgentInputOverrides;
351
+ const overrides = this.inputOverrides;
352
352
  if (overrides.some((o) => o.mode === 'hidden')) return 'hidden';
353
353
  if (overrides.some((o) => o.mode === 'disabled')) return 'disabled';
354
354
  return this.activeAgent?.chatInputDuringExecution ?? 'disabled';
@@ -656,7 +656,7 @@ export class FoundationAiAssistant extends GenesisElement {
656
656
  chatInputDuringExecution?: ChatInputDuringExecutionMode;
657
657
  };
658
658
  if (!chatInputDuringExecution) return;
659
- this._sessionRef?.actions.aiAssistant.addSubAgentInputOverride({
659
+ this._sessionRef?.actions.aiAssistant.addInputOverride({
660
660
  id: invocationId,
661
661
  mode: chatInputDuringExecution,
662
662
  });
@@ -666,18 +666,39 @@ export class FoundationAiAssistant extends GenesisElement {
666
666
  this.liveSubAgentName = null;
667
667
  const { invocationId } = (e as CustomEvent).detail as { invocationId?: string };
668
668
  if (invocationId) {
669
- this._sessionRef?.actions.aiAssistant.removeSubAgentInputOverride({ id: invocationId });
669
+ this._sessionRef?.actions.aiAssistant.removeInputOverride({ id: invocationId });
670
+ }
671
+ };
672
+ const onInteractionStart = (e: Event) => {
673
+ const { interactionId, chatInputDuringExecution } = (e as CustomEvent).detail as {
674
+ interactionId: string;
675
+ chatInputDuringExecution?: ChatInputDuringExecutionMode;
676
+ };
677
+ if (!chatInputDuringExecution) return;
678
+ this._sessionRef?.actions.aiAssistant.addInputOverride({
679
+ id: interactionId,
680
+ mode: chatInputDuringExecution,
681
+ });
682
+ };
683
+ const onInteractionStop = (e: Event) => {
684
+ const { interactionId } = (e as CustomEvent).detail as { interactionId?: string };
685
+ if (interactionId) {
686
+ this._sessionRef?.actions.aiAssistant.removeInputOverride({ id: interactionId });
670
687
  }
671
688
  };
672
689
  driver.addEventListener('sub-agent-history-updated', onSubAgentHistoryUpdated);
673
690
  driver.addEventListener('sub-agent-start', onSubAgentStart);
674
691
  driver.addEventListener('sub-agent-stop', onSubAgentStop);
692
+ driver.addEventListener('interaction-start', onInteractionStart);
693
+ driver.addEventListener('interaction-stop', onInteractionStop);
675
694
 
676
695
  const cleanups: (() => void)[] = [
677
696
  () => driver.removeEventListener('history-updated', onHistoryUpdated),
678
697
  () => driver.removeEventListener('sub-agent-history-updated', onSubAgentHistoryUpdated),
679
698
  () => driver.removeEventListener('sub-agent-start', onSubAgentStart),
680
699
  () => driver.removeEventListener('sub-agent-stop', onSubAgentStop),
700
+ () => driver.removeEventListener('interaction-start', onInteractionStart),
701
+ () => driver.removeEventListener('interaction-stop', onInteractionStop),
681
702
  ];
682
703
 
683
704
  if (driver instanceof OrchestratingDriver) {
@@ -6,13 +6,14 @@ import type { AiAssistantAnimation, AiAssistantState, SuggestionsState } from '.
6
6
 
7
7
  /**
8
8
  * A single in-flight per-call chat-input override pushed by a `requestSubAgent`
9
- * invocation. Tracked as an array (not a counter) so the slice can survive
10
- * pop-in/pop-out and so a listener that connects mid-execution can compute the
11
- * effective mode without having seen the start event.
9
+ * or `requestInteraction` call. Tracked as an array (not a counter) so the
10
+ * slice can survive pop-in/pop-out and so a listener that connects
11
+ * mid-execution can compute the effective mode without having seen the start
12
+ * event.
12
13
  *
13
14
  * @internal
14
15
  */
15
- export interface SubAgentInputOverride {
16
+ export interface InputOverride {
16
17
  /** Unique per-invocation id, paired with the start/stop events. */
17
18
  id: string;
18
19
  /** The mode requested for this invocation. */
@@ -54,11 +55,12 @@ export interface AiAssistantSessionState {
54
55
  /** Name of the currently-executing sub-agent, or null when idle. */
55
56
  liveSubAgentName: string | null;
56
57
  /**
57
- * In-flight per-call chat-input overrides pushed by `requestSubAgent` calls.
58
- * The most restrictive entry (`'hidden'` > `'disabled'`) wins; an empty list
59
- * means the agent-level `chatInputDuringExecution` applies.
58
+ * In-flight per-call chat-input overrides pushed by `requestSubAgent` or
59
+ * `requestInteraction` calls. The most restrictive entry (`'hidden'` >
60
+ * `'disabled'`) wins; an empty list means the agent-level
61
+ * `chatInputDuringExecution` applies.
60
62
  */
61
- subAgentInputOverrides: SubAgentInputOverride[];
63
+ inputOverrides: InputOverride[];
62
64
  }
63
65
 
64
66
  export const defaultSessionState: AiAssistantSessionState = {
@@ -77,7 +79,7 @@ export const defaultSessionState: AiAssistantSessionState = {
77
79
  inputValue: '',
78
80
  liveSubAgentTrace: [],
79
81
  liveSubAgentName: null,
80
- subAgentInputOverrides: [],
82
+ inputOverrides: [],
81
83
  };
82
84
 
83
85
  export const aiAssistantSlice = createSlice({
@@ -129,13 +131,11 @@ export const aiAssistantSlice = createSlice({
129
131
  setLiveSubAgentName(state, action: PayloadAction<string | null>) {
130
132
  state.liveSubAgentName = action.payload;
131
133
  },
132
- addSubAgentInputOverride(state, action: PayloadAction<SubAgentInputOverride>) {
133
- state.subAgentInputOverrides.push(action.payload);
134
+ addInputOverride(state, action: PayloadAction<InputOverride>) {
135
+ state.inputOverrides.push(action.payload);
134
136
  },
135
- removeSubAgentInputOverride(state, action: PayloadAction<{ id: string }>) {
136
- state.subAgentInputOverrides = state.subAgentInputOverrides.filter(
137
- (o) => o.id !== action.payload.id,
138
- );
137
+ removeInputOverride(state, action: PayloadAction<{ id: string }>) {
138
+ state.inputOverrides = state.inputOverrides.filter((o) => o.id !== action.payload.id);
139
139
  },
140
140
  },
141
141
  selectors: {},
@@ -1,12 +1,5 @@
1
- import {
2
- customElement,
3
- GenesisElement,
4
- observable,
5
- html,
6
- css,
7
- repeat,
8
- when,
9
- } from '@genesislcap/web-core';
1
+ import { customElement, observable, html, css, repeat, when } from '@genesislcap/web-core';
2
+ import { GenesisElement } from '@genesislcap/web-core';
10
3
  import type { SuggestionsState } from '../main/main.types';
11
4
 
12
5
  type LoadedState = { status: 'loaded'; suggestions: string[] };
package/src/tags/index.ts CHANGED
@@ -1 +0,0 @@
1
- export {};
package/tsconfig.json CHANGED
@@ -5,5 +5,7 @@
5
5
  "outDir": "./dist/esm",
6
6
  "rootDir": "./src"
7
7
  },
8
- "include": ["src/**/*"]
8
+ "include": [
9
+ "src/**/*"
10
+ ]
9
11
  }