@genesislcap/ai-assistant 15.15.0 → 15.15.1-alpha-793fc09.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 (48) hide show
  1. package/dist/ai-assistant.api.json +238 -0
  2. package/dist/ai-assistant.d.ts +56 -0
  3. package/dist/chat-driver.cjs +51 -0
  4. package/dist/chat-driver.cjs.map +2 -2
  5. package/dist/chat-driver.mjs +51 -0
  6. package/dist/chat-driver.mjs.map +2 -2
  7. package/dist/custom-elements.json +88 -1
  8. package/dist/dts/components/ai-driver/ai-driver.d.ts +10 -0
  9. package/dist/dts/components/ai-driver/ai-driver.d.ts.map +1 -1
  10. package/dist/dts/components/chat-driver/chat-driver.d.ts +28 -0
  11. package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
  12. package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.d.ts +10 -0
  13. package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.d.ts.map +1 -1
  14. package/dist/dts/components/orchestrating-driver/orchestrating-driver.cost.test.d.ts +2 -0
  15. package/dist/dts/components/orchestrating-driver/orchestrating-driver.cost.test.d.ts.map +1 -0
  16. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +3 -0
  17. package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
  18. package/dist/dts/main/file-attachments.test.d.ts +2 -0
  19. package/dist/dts/main/file-attachments.test.d.ts.map +1 -0
  20. package/dist/dts/main/main.d.ts +11 -0
  21. package/dist/dts/main/main.d.ts.map +1 -1
  22. package/dist/dts/main/main.template.d.ts.map +1 -1
  23. package/dist/dts/react.d.ts +1 -0
  24. package/dist/dts/state/debug-event-log.d.ts +1 -1
  25. package/dist/dts/state/debug-event-log.d.ts.map +1 -1
  26. package/dist/esm/components/chat-driver/chat-driver.js +43 -0
  27. package/dist/esm/components/chat-driver/chat-driver.test.js +66 -0
  28. package/dist/esm/components/chat-interaction-wrapper/chat-interaction-wrapper.js +18 -0
  29. package/dist/esm/components/orchestrating-driver/orchestrating-driver.cost.test.js +46 -0
  30. package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +5 -0
  31. package/dist/esm/main/file-attachments.test.js +58 -0
  32. package/dist/esm/main/main.js +42 -5
  33. package/dist/esm/main/main.template.js +1 -0
  34. package/dist/esm/state/debug-event-log.js +2 -0
  35. package/dist/react.cjs +6 -1
  36. package/dist/react.mjs +6 -1
  37. package/dist/tsconfig.tsbuildinfo +1 -1
  38. package/package.json +17 -17
  39. package/src/components/ai-driver/ai-driver.ts +10 -0
  40. package/src/components/chat-driver/chat-driver.test.ts +82 -0
  41. package/src/components/chat-driver/chat-driver.ts +45 -0
  42. package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts +24 -0
  43. package/src/components/orchestrating-driver/orchestrating-driver.cost.test.ts +73 -0
  44. package/src/components/orchestrating-driver/orchestrating-driver.ts +6 -0
  45. package/src/main/file-attachments.test.ts +70 -0
  46. package/src/main/main.template.ts +1 -0
  47. package/src/main/main.ts +41 -6
  48. package/src/state/debug-event-log.ts +3 -0
@@ -3205,6 +3205,67 @@
3205
3205
  ],
3206
3206
  "name": "primeRestoredAgentState"
3207
3207
  },
3208
+ {
3209
+ "kind": "MethodSignature",
3210
+ "canonicalReference": "@genesislcap/ai-assistant!AiDriver#recordExternalCost:member(1)",
3211
+ "docComment": "/**\n * Record external spend against an interaction that has ALREADY resolved.\n *\n * For widgets that resolve early — so the assistant stops waiting — and then keep doing paid work on the user's behalf. Reports a positive DELTA; repeated calls sum. Returns false when the message is no longer in history.\n *\n * @beta\n */\n",
3212
+ "excerptTokens": [
3213
+ {
3214
+ "kind": "Content",
3215
+ "text": "recordExternalCost?(interactionId: "
3216
+ },
3217
+ {
3218
+ "kind": "Content",
3219
+ "text": "string"
3220
+ },
3221
+ {
3222
+ "kind": "Content",
3223
+ "text": ", costUsd: "
3224
+ },
3225
+ {
3226
+ "kind": "Content",
3227
+ "text": "number"
3228
+ },
3229
+ {
3230
+ "kind": "Content",
3231
+ "text": "): "
3232
+ },
3233
+ {
3234
+ "kind": "Content",
3235
+ "text": "boolean"
3236
+ },
3237
+ {
3238
+ "kind": "Content",
3239
+ "text": ";"
3240
+ }
3241
+ ],
3242
+ "isOptional": true,
3243
+ "returnTypeTokenRange": {
3244
+ "startIndex": 5,
3245
+ "endIndex": 6
3246
+ },
3247
+ "releaseTag": "Beta",
3248
+ "overloadIndex": 1,
3249
+ "parameters": [
3250
+ {
3251
+ "parameterName": "interactionId",
3252
+ "parameterTypeTokenRange": {
3253
+ "startIndex": 1,
3254
+ "endIndex": 2
3255
+ },
3256
+ "isOptional": false
3257
+ },
3258
+ {
3259
+ "parameterName": "costUsd",
3260
+ "parameterTypeTokenRange": {
3261
+ "startIndex": 3,
3262
+ "endIndex": 4
3263
+ },
3264
+ "isOptional": false
3265
+ }
3266
+ ],
3267
+ "name": "recordExternalCost"
3268
+ },
3208
3269
  {
3209
3270
  "kind": "MethodSignature",
3210
3271
  "canonicalReference": "@genesislcap/ai-assistant!AiDriver#resolveInteraction:member(1)",
@@ -5265,6 +5326,70 @@
5265
5326
  "isAbstract": false,
5266
5327
  "name": "markAsSubAgent"
5267
5328
  },
5329
+ {
5330
+ "kind": "Method",
5331
+ "canonicalReference": "@genesislcap/ai-assistant!ChatDriver#recordExternalCost:member(1)",
5332
+ "docComment": "/**\n * Add external (non-LLM) spend to an interaction's message AFTER it has resolved.\n *\n * **Why this exists separately from `resolveInteraction`.** A widget's spend reaches the session total only through `InteractionResult.costUsd`, which is read exactly once — `resolveInteraction` does its whole job inside `if (interaction)` and then deletes the entry from `pendingInteractions`. That is correct for the resolution itself, but it assumes a widget stops costing money when it resolves, and several deliberately do not: a widget that resolves EARLY so the assistant stops waiting (options shown, a preview rendered) may stay interactive for minutes afterwards and do more paid work on the user's behalf. Re-emitting `interaction-completed` does not help — with the interaction gone from the map the call is a silent no-op — so that spend had nowhere to go and was simply missing from the figure shown to the user.\n *\n * Additive by design: the caller reports a DELTA, not a running total, so repeated calls sum. It touches only `externalCostUsd`; the recorded `interaction.resolved` payload is left exactly as the resolution wrote it, because this is not a second resolution and must not read like one.\n *\n * Deliberately does NOT require the interaction to be pending, does not resurrect it, and has no loading/turn side effects — this is bookkeeping about a turn that already happened.\n *\n * @param interactionId - the interaction whose message owns the spend.\n *\n * @param costUsd - a positive delta in USD. Non-finite, zero and negative values are ignored.\n *\n * @returns `true` when the spend was recorded; `false` when the message is no longer in history (trimmed or condensed away), which is a real outcome the caller may want to log rather than a failure to throw on.\n *\n * @beta\n */\n",
5333
+ "excerptTokens": [
5334
+ {
5335
+ "kind": "Content",
5336
+ "text": "recordExternalCost(interactionId: "
5337
+ },
5338
+ {
5339
+ "kind": "Content",
5340
+ "text": "string"
5341
+ },
5342
+ {
5343
+ "kind": "Content",
5344
+ "text": ", costUsd: "
5345
+ },
5346
+ {
5347
+ "kind": "Content",
5348
+ "text": "number"
5349
+ },
5350
+ {
5351
+ "kind": "Content",
5352
+ "text": "): "
5353
+ },
5354
+ {
5355
+ "kind": "Content",
5356
+ "text": "boolean"
5357
+ },
5358
+ {
5359
+ "kind": "Content",
5360
+ "text": ";"
5361
+ }
5362
+ ],
5363
+ "isStatic": false,
5364
+ "returnTypeTokenRange": {
5365
+ "startIndex": 5,
5366
+ "endIndex": 6
5367
+ },
5368
+ "releaseTag": "Beta",
5369
+ "isProtected": false,
5370
+ "overloadIndex": 1,
5371
+ "parameters": [
5372
+ {
5373
+ "parameterName": "interactionId",
5374
+ "parameterTypeTokenRange": {
5375
+ "startIndex": 1,
5376
+ "endIndex": 2
5377
+ },
5378
+ "isOptional": false
5379
+ },
5380
+ {
5381
+ "parameterName": "costUsd",
5382
+ "parameterTypeTokenRange": {
5383
+ "startIndex": 3,
5384
+ "endIndex": 4
5385
+ },
5386
+ "isOptional": false
5387
+ }
5388
+ ],
5389
+ "isOptional": false,
5390
+ "isAbstract": false,
5391
+ "name": "recordExternalCost"
5392
+ },
5268
5393
  {
5269
5394
  "kind": "Method",
5270
5395
  "canonicalReference": "@genesislcap/ai-assistant!ChatDriver#reportBudgetExhausted:member(1)",
@@ -9077,6 +9202,55 @@
9077
9202
  "isAbstract": false,
9078
9203
  "name": "handleInteractionCompleted"
9079
9204
  },
9205
+ {
9206
+ "kind": "Method",
9207
+ "canonicalReference": "@genesislcap/ai-assistant!FoundationAiAssistant#handleInteractionCost:member(1)",
9208
+ "docComment": "/**\n * A widget reporting spend it incurred AFTER resolving.\n *\n * Note what this deliberately does NOT do: no `startLoadingTimer()`. This is bookkeeping about a turn that already finished, so it must be invisible — showing a spinner would tell the user the assistant is working when nothing is pending.\n */\n",
9209
+ "excerptTokens": [
9210
+ {
9211
+ "kind": "Content",
9212
+ "text": "handleInteractionCost(e: "
9213
+ },
9214
+ {
9215
+ "kind": "Reference",
9216
+ "text": "Event",
9217
+ "canonicalReference": "!Event:interface"
9218
+ },
9219
+ {
9220
+ "kind": "Content",
9221
+ "text": "): "
9222
+ },
9223
+ {
9224
+ "kind": "Content",
9225
+ "text": "void"
9226
+ },
9227
+ {
9228
+ "kind": "Content",
9229
+ "text": ";"
9230
+ }
9231
+ ],
9232
+ "isStatic": false,
9233
+ "returnTypeTokenRange": {
9234
+ "startIndex": 3,
9235
+ "endIndex": 4
9236
+ },
9237
+ "releaseTag": "Beta",
9238
+ "isProtected": false,
9239
+ "overloadIndex": 1,
9240
+ "parameters": [
9241
+ {
9242
+ "parameterName": "e",
9243
+ "parameterTypeTokenRange": {
9244
+ "startIndex": 1,
9245
+ "endIndex": 2
9246
+ },
9247
+ "isOptional": false
9248
+ }
9249
+ ],
9250
+ "isOptional": false,
9251
+ "isAbstract": false,
9252
+ "name": "handleInteractionCost"
9253
+ },
9080
9254
  {
9081
9255
  "kind": "Method",
9082
9256
  "canonicalReference": "@genesislcap/ai-assistant!FoundationAiAssistant#handlePopout:member(1)",
@@ -14889,6 +15063,70 @@
14889
15063
  "isAbstract": false,
14890
15064
  "name": "primeRestoredAgentState"
14891
15065
  },
15066
+ {
15067
+ "kind": "Method",
15068
+ "canonicalReference": "@genesislcap/ai-assistant!OrchestratingDriver#recordExternalCost:member(1)",
15069
+ "docComment": "/**\n * Delegated for the same reason as `resolveInteraction`: the history that owns the interaction — and therefore the cost — lives on the wrapped driver, not here.\n */\n",
15070
+ "excerptTokens": [
15071
+ {
15072
+ "kind": "Content",
15073
+ "text": "recordExternalCost(interactionId: "
15074
+ },
15075
+ {
15076
+ "kind": "Content",
15077
+ "text": "string"
15078
+ },
15079
+ {
15080
+ "kind": "Content",
15081
+ "text": ", costUsd: "
15082
+ },
15083
+ {
15084
+ "kind": "Content",
15085
+ "text": "number"
15086
+ },
15087
+ {
15088
+ "kind": "Content",
15089
+ "text": "): "
15090
+ },
15091
+ {
15092
+ "kind": "Content",
15093
+ "text": "boolean"
15094
+ },
15095
+ {
15096
+ "kind": "Content",
15097
+ "text": ";"
15098
+ }
15099
+ ],
15100
+ "isStatic": false,
15101
+ "returnTypeTokenRange": {
15102
+ "startIndex": 5,
15103
+ "endIndex": 6
15104
+ },
15105
+ "releaseTag": "Beta",
15106
+ "isProtected": false,
15107
+ "overloadIndex": 1,
15108
+ "parameters": [
15109
+ {
15110
+ "parameterName": "interactionId",
15111
+ "parameterTypeTokenRange": {
15112
+ "startIndex": 1,
15113
+ "endIndex": 2
15114
+ },
15115
+ "isOptional": false
15116
+ },
15117
+ {
15118
+ "parameterName": "costUsd",
15119
+ "parameterTypeTokenRange": {
15120
+ "startIndex": 3,
15121
+ "endIndex": 4
15122
+ },
15123
+ "isOptional": false
15124
+ }
15125
+ ],
15126
+ "isOptional": false,
15127
+ "isAbstract": false,
15128
+ "name": "recordExternalCost"
15129
+ },
14892
15130
  {
14893
15131
  "kind": "Method",
14894
15132
  "canonicalReference": "@genesislcap/ai-assistant!OrchestratingDriver#resolveInteraction:member(1)",
@@ -597,6 +597,16 @@ export declare interface AiDriver extends EventTarget {
597
597
  * Resolve a pending blocking interaction with the given result.
598
598
  */
599
599
  resolveInteraction(interactionId: string, result: unknown): void;
600
+ /**
601
+ * Record external spend against an interaction that has ALREADY resolved.
602
+ *
603
+ * For widgets that resolve early — so the assistant stops waiting — and then keep doing paid work
604
+ * on the user's behalf. Reports a positive DELTA; repeated calls sum. Returns false when the
605
+ * message is no longer in history.
606
+ *
607
+ * @beta
608
+ */
609
+ recordExternalCost?(interactionId: string, costUsd: number): boolean;
600
610
  /**
601
611
  * The live {@link InteractionContext} for a pending interaction (GENC-1390), or
602
612
  * `undefined` if the interaction is resolved/evicted or the driver owns no
@@ -1858,6 +1868,34 @@ export declare class ChatDriver extends EventTarget implements AiDriver {
1858
1868
  * bubble and/or shows the "Assistant" label.
1859
1869
  */
1860
1870
  requestInteraction<T>(componentName: string, data: any, options?: InteractionRequestOptions): Promise<T>;
1871
+ /**
1872
+ * Add external (non-LLM) spend to an interaction's message AFTER it has resolved.
1873
+ *
1874
+ * **Why this exists separately from `resolveInteraction`.** A widget's spend reaches the session
1875
+ * total only through `InteractionResult.costUsd`, which is read exactly once — `resolveInteraction`
1876
+ * does its whole job inside `if (interaction)` and then deletes the entry from
1877
+ * `pendingInteractions`. That is correct for the resolution itself, but it assumes a widget stops
1878
+ * costing money when it resolves, and several deliberately do not: a widget that resolves EARLY so
1879
+ * the assistant stops waiting (options shown, a preview rendered) may stay interactive for minutes
1880
+ * afterwards and do more paid work on the user's behalf. Re-emitting `interaction-completed` does
1881
+ * not help — with the interaction gone from the map the call is a silent no-op — so that spend had
1882
+ * nowhere to go and was simply missing from the figure shown to the user.
1883
+ *
1884
+ * Additive by design: the caller reports a DELTA, not a running total, so repeated calls sum. It
1885
+ * touches only `externalCostUsd`; the recorded `interaction.resolved` payload is left exactly as
1886
+ * the resolution wrote it, because this is not a second resolution and must not read like one.
1887
+ *
1888
+ * Deliberately does NOT require the interaction to be pending, does not resurrect it, and has no
1889
+ * loading/turn side effects — this is bookkeeping about a turn that already happened.
1890
+ *
1891
+ * @param interactionId the interaction whose message owns the spend.
1892
+ * @param costUsd a positive delta in USD. Non-finite, zero and negative values are ignored.
1893
+ * @returns `true` when the spend was recorded; `false` when the message is no longer in history
1894
+ * (trimmed or condensed away), which is a real outcome the caller may want to log rather than a
1895
+ * failure to throw on.
1896
+ * @beta
1897
+ */
1898
+ recordExternalCost(interactionId: string, costUsd: number): boolean;
1861
1899
  /**
1862
1900
  * Resolve a pending interaction. The wrapper component calls this on completion.
1863
1901
  * Marks the interaction message as resolved so it renders read-only on re-render.
@@ -2057,6 +2095,10 @@ declare type ChatInteractionEventsMap = {
2057
2095
  'interaction-completed': InteractionResult<unknown> & {
2058
2096
  interactionId: string;
2059
2097
  };
2098
+ 'interaction-cost': {
2099
+ interactionId: string;
2100
+ costUsd: number;
2101
+ };
2060
2102
  };
2061
2103
 
2062
2104
  export { ChatThinkingPolicy }
@@ -3914,6 +3956,9 @@ export declare class FoundationAiAssistant extends GenesisElement {
3914
3956
  removeAttachmentError(message: string): void;
3915
3957
  private isAcceptedFile;
3916
3958
  private readFileAsText;
3959
+ /** The base64 payload of a file, without any `data:` prefix — the raw form
3960
+ * `ChatImageAttachment.data` specifies (transports add their own framing). */
3961
+ private readFileAsBase64;
3917
3962
  private processFiles;
3918
3963
  private loadSelectedFiles;
3919
3964
  handleSendClick(): void;
@@ -3960,6 +4005,14 @@ export declare class FoundationAiAssistant extends GenesisElement {
3960
4005
  private send;
3961
4006
  onChatHeaderMouseDown(e: MouseEvent): void;
3962
4007
  handleInteractionCompleted(e: Event): void;
4008
+ /**
4009
+ * A widget reporting spend it incurred AFTER resolving.
4010
+ *
4011
+ * Note what this deliberately does NOT do: no `startLoadingTimer()`. This is bookkeeping about a
4012
+ * turn that already finished, so it must be invisible — showing a spinner would tell the user the
4013
+ * assistant is working when nothing is pending.
4014
+ */
4015
+ handleInteractionCost(e: Event): void;
3963
4016
  /**
3964
4017
  * The live {@link InteractionContext} for a pending interaction (GENC-1390).
3965
4018
  * `AiChatInteractionWrapper` pierces this across the shadow boundary to build the
@@ -4294,6 +4347,9 @@ export declare class OrchestratingDriver extends EventTarget implements AiDriver
4294
4347
  budgetExhaustedMessage?: string;
4295
4348
  });
4296
4349
  resolveInteraction(interactionId: string, result: unknown): void;
4350
+ /** Delegated for the same reason as `resolveInteraction`: the history that owns the
4351
+ * interaction — and therefore the cost — lives on the wrapped driver, not here. */
4352
+ recordExternalCost(interactionId: string, costUsd: number): boolean;
4297
4353
  getInteractionContext(interactionId: string): InteractionContext | undefined;
4298
4354
  isBusy(): boolean;
4299
4355
  /** Currently active provider name from the underlying ChatDriver. */
@@ -3582,6 +3582,8 @@ var META_EVENT_IMPORTANCE = {
3582
3582
  "provider.selected": "normal",
3583
3583
  "interaction.requested": "normal",
3584
3584
  "interaction.resolved": "normal",
3585
+ // Bookkeeping about an already-finished turn; interesting when reconciling a session total.
3586
+ "interaction.external-cost": "normal",
3585
3587
  "external-diagnostics.folded": "normal",
3586
3588
  "session.cleared": "normal",
3587
3589
  "session.switched": "low",
@@ -5067,6 +5069,50 @@ Output format (strict):
5067
5069
  });
5068
5070
  });
5069
5071
  }
5072
+ /**
5073
+ * Add external (non-LLM) spend to an interaction's message AFTER it has resolved.
5074
+ *
5075
+ * **Why this exists separately from `resolveInteraction`.** A widget's spend reaches the session
5076
+ * total only through `InteractionResult.costUsd`, which is read exactly once — `resolveInteraction`
5077
+ * does its whole job inside `if (interaction)` and then deletes the entry from
5078
+ * `pendingInteractions`. That is correct for the resolution itself, but it assumes a widget stops
5079
+ * costing money when it resolves, and several deliberately do not: a widget that resolves EARLY so
5080
+ * the assistant stops waiting (options shown, a preview rendered) may stay interactive for minutes
5081
+ * afterwards and do more paid work on the user's behalf. Re-emitting `interaction-completed` does
5082
+ * not help — with the interaction gone from the map the call is a silent no-op — so that spend had
5083
+ * nowhere to go and was simply missing from the figure shown to the user.
5084
+ *
5085
+ * Additive by design: the caller reports a DELTA, not a running total, so repeated calls sum. It
5086
+ * touches only `externalCostUsd`; the recorded `interaction.resolved` payload is left exactly as
5087
+ * the resolution wrote it, because this is not a second resolution and must not read like one.
5088
+ *
5089
+ * Deliberately does NOT require the interaction to be pending, does not resurrect it, and has no
5090
+ * loading/turn side effects — this is bookkeeping about a turn that already happened.
5091
+ *
5092
+ * @param interactionId the interaction whose message owns the spend.
5093
+ * @param costUsd a positive delta in USD. Non-finite, zero and negative values are ignored.
5094
+ * @returns `true` when the spend was recorded; `false` when the message is no longer in history
5095
+ * (trimmed or condensed away), which is a real outcome the caller may want to log rather than a
5096
+ * failure to throw on.
5097
+ * @beta
5098
+ */
5099
+ recordExternalCost(interactionId, costUsd) {
5100
+ if (!interactionId) return false;
5101
+ if (typeof costUsd !== "number" || !Number.isFinite(costUsd) || costUsd <= 0) return false;
5102
+ const idx = this.history.findIndex((m) => m.interaction?.interactionId === interactionId);
5103
+ if (idx === -1) return false;
5104
+ this.history[idx] = {
5105
+ ...this.history[idx],
5106
+ externalCostUsd: (this.history[idx].externalCostUsd ?? 0) + costUsd
5107
+ };
5108
+ recordMetaEvent(this.sessionKey, "interaction.external-cost", { interactionId, costUsd });
5109
+ this.dispatchEvent(
5110
+ new CustomEvent("history-updated", {
5111
+ detail: this.history
5112
+ })
5113
+ );
5114
+ return true;
5115
+ }
5070
5116
  /**
5071
5117
  * Resolve a pending interaction. The wrapper component calls this on completion.
5072
5118
  * Marks the interaction message as resolved so it renders read-only on re-render.
@@ -6510,6 +6556,11 @@ var OrchestratingDriver = class extends EventTarget {
6510
6556
  resolveInteraction(interactionId, result) {
6511
6557
  this.chatDriver.resolveInteraction(interactionId, result);
6512
6558
  }
6559
+ /** Delegated for the same reason as `resolveInteraction`: the history that owns the
6560
+ * interaction — and therefore the cost — lives on the wrapped driver, not here. */
6561
+ recordExternalCost(interactionId, costUsd) {
6562
+ return this.chatDriver.recordExternalCost(interactionId, costUsd);
6563
+ }
6513
6564
  getInteractionContext(interactionId) {
6514
6565
  return this.chatDriver.getInteractionContext(interactionId);
6515
6566
  }