@genesislcap/ai-assistant 15.2.1 → 15.3.1
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/chat-driver.cjs.map +2 -2
- package/dist/chat-driver.mjs.map +2 -2
- package/dist/custom-elements.json +191 -191
- package/dist/dts/react.d.ts +11 -11
- package/dist/dts/state/persistence/diagnostics.d.ts +9 -1
- package/dist/dts/state/persistence/diagnostics.d.ts.map +1 -1
- package/dist/dts/state/persistence/session-persister.d.ts +12 -0
- package/dist/dts/state/persistence/session-persister.d.ts.map +1 -1
- package/dist/esm/state/persistence/diagnostics.js +11 -3
- package/dist/esm/state/persistence/diagnostics.test.js +36 -1
- package/dist/esm/state/persistence/session-persister.js +12 -0
- package/dist/esm/state/persistence/session-persister.test.js +25 -0
- package/dist/react.cjs +11 -11
- package/dist/react.mjs +10 -10
- package/package.json +17 -17
- package/src/state/persistence/diagnostics.test.ts +45 -1
- package/src/state/persistence/diagnostics.ts +10 -2
- package/src/state/persistence/session-persister.test.ts +44 -0
- package/src/state/persistence/session-persister.ts +12 -0
|
@@ -8305,197 +8305,6 @@
|
|
|
8305
8305
|
}
|
|
8306
8306
|
]
|
|
8307
8307
|
},
|
|
8308
|
-
{
|
|
8309
|
-
"kind": "javascript-module",
|
|
8310
|
-
"path": "src/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.ts",
|
|
8311
|
-
"declarations": [
|
|
8312
|
-
{
|
|
8313
|
-
"kind": "variable",
|
|
8314
|
-
"name": "AiChatInteractionWrapperStyles",
|
|
8315
|
-
"default": "css`\n :host {\n display: block;\n }\n`"
|
|
8316
|
-
}
|
|
8317
|
-
],
|
|
8318
|
-
"exports": [
|
|
8319
|
-
{
|
|
8320
|
-
"kind": "js",
|
|
8321
|
-
"name": "AiChatInteractionWrapperStyles",
|
|
8322
|
-
"declaration": {
|
|
8323
|
-
"name": "AiChatInteractionWrapperStyles",
|
|
8324
|
-
"module": "src/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.ts"
|
|
8325
|
-
}
|
|
8326
|
-
}
|
|
8327
|
-
]
|
|
8328
|
-
},
|
|
8329
|
-
{
|
|
8330
|
-
"kind": "javascript-module",
|
|
8331
|
-
"path": "src/components/chat-interaction-wrapper/chat-interaction-wrapper.template.ts",
|
|
8332
|
-
"declarations": [
|
|
8333
|
-
{
|
|
8334
|
-
"kind": "variable",
|
|
8335
|
-
"name": "AiChatInteractionWrapperTemplate",
|
|
8336
|
-
"default": "html<AiChatInteractionWrapper>`\n <div ${ref('container')} class=\"container\" part=\"container\"></div>\n`"
|
|
8337
|
-
}
|
|
8338
|
-
],
|
|
8339
|
-
"exports": [
|
|
8340
|
-
{
|
|
8341
|
-
"kind": "js",
|
|
8342
|
-
"name": "AiChatInteractionWrapperTemplate",
|
|
8343
|
-
"declaration": {
|
|
8344
|
-
"name": "AiChatInteractionWrapperTemplate",
|
|
8345
|
-
"module": "src/components/chat-interaction-wrapper/chat-interaction-wrapper.template.ts"
|
|
8346
|
-
}
|
|
8347
|
-
}
|
|
8348
|
-
]
|
|
8349
|
-
},
|
|
8350
|
-
{
|
|
8351
|
-
"kind": "javascript-module",
|
|
8352
|
-
"path": "src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts",
|
|
8353
|
-
"declarations": [
|
|
8354
|
-
{
|
|
8355
|
-
"kind": "class",
|
|
8356
|
-
"description": "Hosts an interactive AI chat sub-component by tag name.",
|
|
8357
|
-
"name": "AiChatInteractionWrapper",
|
|
8358
|
-
"members": [
|
|
8359
|
-
{
|
|
8360
|
-
"kind": "field",
|
|
8361
|
-
"name": "resolved",
|
|
8362
|
-
"type": {
|
|
8363
|
-
"text": "InteractionResult<unknown> | undefined"
|
|
8364
|
-
},
|
|
8365
|
-
"default": "undefined",
|
|
8366
|
-
"description": "The resolved result once the interaction has completed. Forwarded to the rendered component. @internal"
|
|
8367
|
-
},
|
|
8368
|
-
{
|
|
8369
|
-
"kind": "field",
|
|
8370
|
-
"name": "_resizeObserver",
|
|
8371
|
-
"type": {
|
|
8372
|
-
"text": "ResizeObserver | undefined"
|
|
8373
|
-
},
|
|
8374
|
-
"privacy": "private",
|
|
8375
|
-
"description": "Observes the rendered widget for mid-lifecycle size changes — see observeWidgetResize."
|
|
8376
|
-
},
|
|
8377
|
-
{
|
|
8378
|
-
"kind": "method",
|
|
8379
|
-
"name": "componentNameChanged"
|
|
8380
|
-
},
|
|
8381
|
-
{
|
|
8382
|
-
"kind": "method",
|
|
8383
|
-
"name": "interactionIdChanged",
|
|
8384
|
-
"description": "Re-render the hosted widget when the interaction identity changes. This\nensures widgets don't go stale with them being rendered with `recycle: true`\nwhen messages are dynamically filtered, showing/hiding thinking/tool calls\n\n`interactionId` is a per-interaction UUID, so it changes exactly when the\nhosted interaction does. Property bindings apply in template order\n(componentName, data, interactionId), so `data` is already rebound by the\ntime this fires and the fresh widget is built with the correct data."
|
|
8385
|
-
},
|
|
8386
|
-
{
|
|
8387
|
-
"kind": "method",
|
|
8388
|
-
"name": "resolvedChanged"
|
|
8389
|
-
},
|
|
8390
|
-
{
|
|
8391
|
-
"kind": "method",
|
|
8392
|
-
"name": "observeWidgetResize",
|
|
8393
|
-
"privacy": "private",
|
|
8394
|
-
"parameters": [
|
|
8395
|
-
{
|
|
8396
|
-
"name": "element",
|
|
8397
|
-
"type": {
|
|
8398
|
-
"text": "Element"
|
|
8399
|
-
}
|
|
8400
|
-
}
|
|
8401
|
-
],
|
|
8402
|
-
"description": "Watch the rendered widget element for box-size changes. When the widget\ngrows or shrinks mid-lifecycle (e.g. expanding a \"More info\" panel,\nappending an SSE status row, revealing generated code), bubble a\n`request-scroll-to-bottom` event so `FoundationAiAssistant` re-snaps to\nthe bottom. `scrollToBottom` respects `_userScrolledAway`, so a user\nreading old messages is not yanked back.\n\nThe first ResizeObserver callback fires synchronously with the initial\nsize when `observe()` is called; we skip it so freshly-rendered widgets\ndon't double up with the existing `handleMessagesUpdate` auto-scroll for\nnew messages."
|
|
8403
|
-
},
|
|
8404
|
-
{
|
|
8405
|
-
"kind": "method",
|
|
8406
|
-
"name": "renderComponent",
|
|
8407
|
-
"privacy": "private"
|
|
8408
|
-
},
|
|
8409
|
-
{
|
|
8410
|
-
"kind": "method",
|
|
8411
|
-
"name": "buildInteraction",
|
|
8412
|
-
"privacy": "private",
|
|
8413
|
-
"return": {
|
|
8414
|
-
"type": {
|
|
8415
|
-
"text": "AiChatInteraction"
|
|
8416
|
-
}
|
|
8417
|
-
},
|
|
8418
|
-
"parameters": [
|
|
8419
|
-
{
|
|
8420
|
-
"name": "host",
|
|
8421
|
-
"type": {
|
|
8422
|
-
"text": "InteractionHost | null"
|
|
8423
|
-
}
|
|
8424
|
-
}
|
|
8425
|
-
],
|
|
8426
|
-
"description": "Project the wrapper's current `data`/`resolved`/`interactionId` into the\ndiscriminated AiChatInteraction the widget consumes (GENC-1390)."
|
|
8427
|
-
}
|
|
8428
|
-
],
|
|
8429
|
-
"events": [
|
|
8430
|
-
{
|
|
8431
|
-
"name": "request-scroll-to-bottom",
|
|
8432
|
-
"type": {
|
|
8433
|
-
"text": "CustomEvent"
|
|
8434
|
-
}
|
|
8435
|
-
},
|
|
8436
|
-
{
|
|
8437
|
-
"description": "Fired when the interaction finishes. detail: `InteractionCompletedEventDetail`",
|
|
8438
|
-
"name": "interaction-completed"
|
|
8439
|
-
}
|
|
8440
|
-
],
|
|
8441
|
-
"superclass": {
|
|
8442
|
-
"name": "GenesisElement",
|
|
8443
|
-
"package": "@genesislcap/web-core"
|
|
8444
|
-
},
|
|
8445
|
-
"tagName": "ai-chat-interaction-wrapper",
|
|
8446
|
-
"customElement": true
|
|
8447
|
-
}
|
|
8448
|
-
],
|
|
8449
|
-
"exports": [
|
|
8450
|
-
{
|
|
8451
|
-
"kind": "js",
|
|
8452
|
-
"name": "AiChatInteractionWrapper",
|
|
8453
|
-
"declaration": {
|
|
8454
|
-
"name": "AiChatInteractionWrapper",
|
|
8455
|
-
"module": "src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts"
|
|
8456
|
-
}
|
|
8457
|
-
},
|
|
8458
|
-
{
|
|
8459
|
-
"kind": "custom-element-definition",
|
|
8460
|
-
"name": "ai-chat-interaction-wrapper",
|
|
8461
|
-
"declaration": {
|
|
8462
|
-
"name": "AiChatInteractionWrapper",
|
|
8463
|
-
"module": "src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts"
|
|
8464
|
-
}
|
|
8465
|
-
}
|
|
8466
|
-
]
|
|
8467
|
-
},
|
|
8468
|
-
{
|
|
8469
|
-
"kind": "javascript-module",
|
|
8470
|
-
"path": "src/components/chat-interaction-wrapper/index.ts",
|
|
8471
|
-
"declarations": [],
|
|
8472
|
-
"exports": [
|
|
8473
|
-
{
|
|
8474
|
-
"kind": "js",
|
|
8475
|
-
"name": "*",
|
|
8476
|
-
"declaration": {
|
|
8477
|
-
"name": "*",
|
|
8478
|
-
"package": "./chat-interaction-wrapper"
|
|
8479
|
-
}
|
|
8480
|
-
},
|
|
8481
|
-
{
|
|
8482
|
-
"kind": "js",
|
|
8483
|
-
"name": "*",
|
|
8484
|
-
"declaration": {
|
|
8485
|
-
"name": "*",
|
|
8486
|
-
"package": "./chat-interaction-wrapper.styles"
|
|
8487
|
-
}
|
|
8488
|
-
},
|
|
8489
|
-
{
|
|
8490
|
-
"kind": "js",
|
|
8491
|
-
"name": "*",
|
|
8492
|
-
"declaration": {
|
|
8493
|
-
"name": "*",
|
|
8494
|
-
"package": "./chat-interaction-wrapper.template"
|
|
8495
|
-
}
|
|
8496
|
-
}
|
|
8497
|
-
]
|
|
8498
|
-
},
|
|
8499
8308
|
{
|
|
8500
8309
|
"kind": "javascript-module",
|
|
8501
8310
|
"path": "src/components/chat-markdown/chat-markdown.ts",
|
|
@@ -9991,6 +9800,197 @@
|
|
|
9991
9800
|
}
|
|
9992
9801
|
}
|
|
9993
9802
|
]
|
|
9803
|
+
},
|
|
9804
|
+
{
|
|
9805
|
+
"kind": "javascript-module",
|
|
9806
|
+
"path": "src/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.ts",
|
|
9807
|
+
"declarations": [
|
|
9808
|
+
{
|
|
9809
|
+
"kind": "variable",
|
|
9810
|
+
"name": "AiChatInteractionWrapperStyles",
|
|
9811
|
+
"default": "css`\n :host {\n display: block;\n }\n`"
|
|
9812
|
+
}
|
|
9813
|
+
],
|
|
9814
|
+
"exports": [
|
|
9815
|
+
{
|
|
9816
|
+
"kind": "js",
|
|
9817
|
+
"name": "AiChatInteractionWrapperStyles",
|
|
9818
|
+
"declaration": {
|
|
9819
|
+
"name": "AiChatInteractionWrapperStyles",
|
|
9820
|
+
"module": "src/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.ts"
|
|
9821
|
+
}
|
|
9822
|
+
}
|
|
9823
|
+
]
|
|
9824
|
+
},
|
|
9825
|
+
{
|
|
9826
|
+
"kind": "javascript-module",
|
|
9827
|
+
"path": "src/components/chat-interaction-wrapper/chat-interaction-wrapper.template.ts",
|
|
9828
|
+
"declarations": [
|
|
9829
|
+
{
|
|
9830
|
+
"kind": "variable",
|
|
9831
|
+
"name": "AiChatInteractionWrapperTemplate",
|
|
9832
|
+
"default": "html<AiChatInteractionWrapper>`\n <div ${ref('container')} class=\"container\" part=\"container\"></div>\n`"
|
|
9833
|
+
}
|
|
9834
|
+
],
|
|
9835
|
+
"exports": [
|
|
9836
|
+
{
|
|
9837
|
+
"kind": "js",
|
|
9838
|
+
"name": "AiChatInteractionWrapperTemplate",
|
|
9839
|
+
"declaration": {
|
|
9840
|
+
"name": "AiChatInteractionWrapperTemplate",
|
|
9841
|
+
"module": "src/components/chat-interaction-wrapper/chat-interaction-wrapper.template.ts"
|
|
9842
|
+
}
|
|
9843
|
+
}
|
|
9844
|
+
]
|
|
9845
|
+
},
|
|
9846
|
+
{
|
|
9847
|
+
"kind": "javascript-module",
|
|
9848
|
+
"path": "src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts",
|
|
9849
|
+
"declarations": [
|
|
9850
|
+
{
|
|
9851
|
+
"kind": "class",
|
|
9852
|
+
"description": "Hosts an interactive AI chat sub-component by tag name.",
|
|
9853
|
+
"name": "AiChatInteractionWrapper",
|
|
9854
|
+
"members": [
|
|
9855
|
+
{
|
|
9856
|
+
"kind": "field",
|
|
9857
|
+
"name": "resolved",
|
|
9858
|
+
"type": {
|
|
9859
|
+
"text": "InteractionResult<unknown> | undefined"
|
|
9860
|
+
},
|
|
9861
|
+
"default": "undefined",
|
|
9862
|
+
"description": "The resolved result once the interaction has completed. Forwarded to the rendered component. @internal"
|
|
9863
|
+
},
|
|
9864
|
+
{
|
|
9865
|
+
"kind": "field",
|
|
9866
|
+
"name": "_resizeObserver",
|
|
9867
|
+
"type": {
|
|
9868
|
+
"text": "ResizeObserver | undefined"
|
|
9869
|
+
},
|
|
9870
|
+
"privacy": "private",
|
|
9871
|
+
"description": "Observes the rendered widget for mid-lifecycle size changes — see observeWidgetResize."
|
|
9872
|
+
},
|
|
9873
|
+
{
|
|
9874
|
+
"kind": "method",
|
|
9875
|
+
"name": "componentNameChanged"
|
|
9876
|
+
},
|
|
9877
|
+
{
|
|
9878
|
+
"kind": "method",
|
|
9879
|
+
"name": "interactionIdChanged",
|
|
9880
|
+
"description": "Re-render the hosted widget when the interaction identity changes. This\nensures widgets don't go stale with them being rendered with `recycle: true`\nwhen messages are dynamically filtered, showing/hiding thinking/tool calls\n\n`interactionId` is a per-interaction UUID, so it changes exactly when the\nhosted interaction does. Property bindings apply in template order\n(componentName, data, interactionId), so `data` is already rebound by the\ntime this fires and the fresh widget is built with the correct data."
|
|
9881
|
+
},
|
|
9882
|
+
{
|
|
9883
|
+
"kind": "method",
|
|
9884
|
+
"name": "resolvedChanged"
|
|
9885
|
+
},
|
|
9886
|
+
{
|
|
9887
|
+
"kind": "method",
|
|
9888
|
+
"name": "observeWidgetResize",
|
|
9889
|
+
"privacy": "private",
|
|
9890
|
+
"parameters": [
|
|
9891
|
+
{
|
|
9892
|
+
"name": "element",
|
|
9893
|
+
"type": {
|
|
9894
|
+
"text": "Element"
|
|
9895
|
+
}
|
|
9896
|
+
}
|
|
9897
|
+
],
|
|
9898
|
+
"description": "Watch the rendered widget element for box-size changes. When the widget\ngrows or shrinks mid-lifecycle (e.g. expanding a \"More info\" panel,\nappending an SSE status row, revealing generated code), bubble a\n`request-scroll-to-bottom` event so `FoundationAiAssistant` re-snaps to\nthe bottom. `scrollToBottom` respects `_userScrolledAway`, so a user\nreading old messages is not yanked back.\n\nThe first ResizeObserver callback fires synchronously with the initial\nsize when `observe()` is called; we skip it so freshly-rendered widgets\ndon't double up with the existing `handleMessagesUpdate` auto-scroll for\nnew messages."
|
|
9899
|
+
},
|
|
9900
|
+
{
|
|
9901
|
+
"kind": "method",
|
|
9902
|
+
"name": "renderComponent",
|
|
9903
|
+
"privacy": "private"
|
|
9904
|
+
},
|
|
9905
|
+
{
|
|
9906
|
+
"kind": "method",
|
|
9907
|
+
"name": "buildInteraction",
|
|
9908
|
+
"privacy": "private",
|
|
9909
|
+
"return": {
|
|
9910
|
+
"type": {
|
|
9911
|
+
"text": "AiChatInteraction"
|
|
9912
|
+
}
|
|
9913
|
+
},
|
|
9914
|
+
"parameters": [
|
|
9915
|
+
{
|
|
9916
|
+
"name": "host",
|
|
9917
|
+
"type": {
|
|
9918
|
+
"text": "InteractionHost | null"
|
|
9919
|
+
}
|
|
9920
|
+
}
|
|
9921
|
+
],
|
|
9922
|
+
"description": "Project the wrapper's current `data`/`resolved`/`interactionId` into the\ndiscriminated AiChatInteraction the widget consumes (GENC-1390)."
|
|
9923
|
+
}
|
|
9924
|
+
],
|
|
9925
|
+
"events": [
|
|
9926
|
+
{
|
|
9927
|
+
"name": "request-scroll-to-bottom",
|
|
9928
|
+
"type": {
|
|
9929
|
+
"text": "CustomEvent"
|
|
9930
|
+
}
|
|
9931
|
+
},
|
|
9932
|
+
{
|
|
9933
|
+
"description": "Fired when the interaction finishes. detail: `InteractionCompletedEventDetail`",
|
|
9934
|
+
"name": "interaction-completed"
|
|
9935
|
+
}
|
|
9936
|
+
],
|
|
9937
|
+
"superclass": {
|
|
9938
|
+
"name": "GenesisElement",
|
|
9939
|
+
"package": "@genesislcap/web-core"
|
|
9940
|
+
},
|
|
9941
|
+
"tagName": "ai-chat-interaction-wrapper",
|
|
9942
|
+
"customElement": true
|
|
9943
|
+
}
|
|
9944
|
+
],
|
|
9945
|
+
"exports": [
|
|
9946
|
+
{
|
|
9947
|
+
"kind": "js",
|
|
9948
|
+
"name": "AiChatInteractionWrapper",
|
|
9949
|
+
"declaration": {
|
|
9950
|
+
"name": "AiChatInteractionWrapper",
|
|
9951
|
+
"module": "src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts"
|
|
9952
|
+
}
|
|
9953
|
+
},
|
|
9954
|
+
{
|
|
9955
|
+
"kind": "custom-element-definition",
|
|
9956
|
+
"name": "ai-chat-interaction-wrapper",
|
|
9957
|
+
"declaration": {
|
|
9958
|
+
"name": "AiChatInteractionWrapper",
|
|
9959
|
+
"module": "src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts"
|
|
9960
|
+
}
|
|
9961
|
+
}
|
|
9962
|
+
]
|
|
9963
|
+
},
|
|
9964
|
+
{
|
|
9965
|
+
"kind": "javascript-module",
|
|
9966
|
+
"path": "src/components/chat-interaction-wrapper/index.ts",
|
|
9967
|
+
"declarations": [],
|
|
9968
|
+
"exports": [
|
|
9969
|
+
{
|
|
9970
|
+
"kind": "js",
|
|
9971
|
+
"name": "*",
|
|
9972
|
+
"declaration": {
|
|
9973
|
+
"name": "*",
|
|
9974
|
+
"package": "./chat-interaction-wrapper"
|
|
9975
|
+
}
|
|
9976
|
+
},
|
|
9977
|
+
{
|
|
9978
|
+
"kind": "js",
|
|
9979
|
+
"name": "*",
|
|
9980
|
+
"declaration": {
|
|
9981
|
+
"name": "*",
|
|
9982
|
+
"package": "./chat-interaction-wrapper.styles"
|
|
9983
|
+
}
|
|
9984
|
+
},
|
|
9985
|
+
{
|
|
9986
|
+
"kind": "js",
|
|
9987
|
+
"name": "*",
|
|
9988
|
+
"declaration": {
|
|
9989
|
+
"name": "*",
|
|
9990
|
+
"package": "./chat-interaction-wrapper.template"
|
|
9991
|
+
}
|
|
9992
|
+
}
|
|
9993
|
+
]
|
|
9994
9994
|
}
|
|
9995
9995
|
]
|
|
9996
9996
|
}
|
package/dist/dts/react.d.ts
CHANGED
|
@@ -131,17 +131,6 @@ export declare const AiChatBubble: React.ForwardRefExoticComponent<
|
|
|
131
131
|
>;
|
|
132
132
|
export type AiChatBubbleRef = AiChatBubbleWC;
|
|
133
133
|
|
|
134
|
-
export declare const AiChatInteractionWrapper: React.ForwardRefExoticComponent<
|
|
135
|
-
React.PropsWithChildren<
|
|
136
|
-
Omit<PublicOf<AiChatInteractionWrapperWC>, 'children' | 'style'> &
|
|
137
|
-
HTMLWCProps & {
|
|
138
|
-
onRequestScrollToBottom?: (event: CustomEvent<unknown>) => void;
|
|
139
|
-
onInteractionCompleted?: (event: CustomEvent<InteractionCompletedEventDetail>) => void;
|
|
140
|
-
}
|
|
141
|
-
> & React.RefAttributes<AiChatInteractionWrapperWC>
|
|
142
|
-
>;
|
|
143
|
-
export type AiChatInteractionWrapperRef = AiChatInteractionWrapperWC;
|
|
144
|
-
|
|
145
134
|
export declare const AiChatMarkdown: React.ForwardRefExoticComponent<
|
|
146
135
|
React.PropsWithChildren<
|
|
147
136
|
Omit<PublicOf<AiChatMarkdownWC>, 'children' | 'style'> &
|
|
@@ -160,4 +149,15 @@ export declare const FoundationAiPopoutManager: React.ForwardRefExoticComponent<
|
|
|
160
149
|
>;
|
|
161
150
|
export type FoundationAiPopoutManagerRef = FoundationAiPopoutManagerWC;
|
|
162
151
|
|
|
152
|
+
export declare const AiChatInteractionWrapper: React.ForwardRefExoticComponent<
|
|
153
|
+
React.PropsWithChildren<
|
|
154
|
+
Omit<PublicOf<AiChatInteractionWrapperWC>, 'children' | 'style'> &
|
|
155
|
+
HTMLWCProps & {
|
|
156
|
+
onRequestScrollToBottom?: (event: CustomEvent<unknown>) => void;
|
|
157
|
+
onInteractionCompleted?: (event: CustomEvent<InteractionCompletedEventDetail>) => void;
|
|
158
|
+
}
|
|
159
|
+
> & React.RefAttributes<AiChatInteractionWrapperWC>
|
|
160
|
+
>;
|
|
161
|
+
export type AiChatInteractionWrapperRef = AiChatInteractionWrapperWC;
|
|
162
|
+
|
|
163
163
|
export {};
|
|
@@ -39,13 +39,21 @@ export interface DebugLog {
|
|
|
39
39
|
meta: unknown;
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
|
-
* Stable identity for a message diagnostic entry — `timestamp::role::subAgentOf`.
|
|
42
|
+
* Stable identity for a message diagnostic entry — `timestamp::role::subAgentOf::category`.
|
|
43
43
|
* Used by the forward-capture delta (skip already-emitted messages) and by the
|
|
44
44
|
* restore watermark (skip messages already present in a prior load's stream).
|
|
45
45
|
* `null` when the entry has no timestamp → treat as always-new. Shared so the
|
|
46
46
|
* element's diagnostics capture and the `SessionPersister`'s restore watermark
|
|
47
47
|
* compute identity identically (GENC-1351 §5.8/§5.10).
|
|
48
48
|
*
|
|
49
|
+
* `category` is part of the key because the reasoning/narration/answer split (GENC-1411) emits
|
|
50
|
+
* SEVERAL messages from one model response — the thought summary, any interstitial narration, and
|
|
51
|
+
* the answer/tool-call message — which share `timestamp` + `role` + `subAgentOf`. Keying on those
|
|
52
|
+
* alone collapsed them to one, so the forward-capture dropped all but the first: a server
|
|
53
|
+
* generation's answer message vanished from the persisted debug log while its reasoning survived
|
|
54
|
+
* (GENC-1461). `category` is stable across a message's in-place annotation (only `toolCalls` change,
|
|
55
|
+
* which is deliberately excluded), so a genuine re-append of the same message still dedups.
|
|
56
|
+
*
|
|
49
57
|
* @internal
|
|
50
58
|
*/
|
|
51
59
|
export declare function diagnosticsMessageKey(entry: DiagnosticEntry): string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../../../src/state/persistence/diagnostics.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;;;;;GASG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,eAAe,CAAC;IACrD,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,mFAAmF;AACnF,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,IAAI,EAAE,OAAO,CAAC;CACf;AAED
|
|
1
|
+
{"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../../../src/state/persistence/diagnostics.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;;;;;GASG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,eAAe,CAAC;IACrD,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,wEAAwE;IACxE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,mFAAmF;AACnF,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,GAAG,IAAI,CAI3E;AASD;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,QAAQ,CA+BhG"}
|
|
@@ -165,6 +165,18 @@ export declare class SessionPersister {
|
|
|
165
165
|
* (§5.8). Skips messages present at restore or already appended; emits each turn
|
|
166
166
|
* (by turn-index) and meta-event (by index) once; appends a `meta-snapshot` only
|
|
167
167
|
* when the block changed. Advances the shared per-session cursors as it goes.
|
|
168
|
+
*
|
|
169
|
+
* TODO(GENC-1461, longer-term): this whole delta step exists only because the element *pulls* the
|
|
170
|
+
* ENTIRE debug log on every throttled flush (`getDiagnosticEntries()` returns the full timeline),
|
|
171
|
+
* so we must diff it against per-session cursors to append only the new tail — and messages, unlike
|
|
172
|
+
* events (monotonic `index`) and turns (`turnIndex`), have no stable positional cursor (compaction
|
|
173
|
+
* rewrites ranges, tool-call messages are annotated in place, synthetic messages are spliced in,
|
|
174
|
+
* restore reloads them), forcing a content-derived identity (`diagnosticsMessageKey`). That
|
|
175
|
+
* identity is inherently fragile: it was too coarse for the reasoning/answer/tool-call split and
|
|
176
|
+
* silently dropped co-timestamped messages (GENC-1461; fixed for now by adding `category`). The
|
|
177
|
+
* robust fix is to invert the flow — have the driver *push* each diagnostic entry exactly once at
|
|
178
|
+
* creation (an append-only event stream) instead of the element pulling-and-diffing — which removes
|
|
179
|
+
* the message-identity key and this dedup entirely. Bigger change; deferred.
|
|
168
180
|
*/
|
|
169
181
|
private collectDiagnosticsDelta;
|
|
170
182
|
/** Cancel any pending timers. Called on explicit session teardown. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-persister.d.ts","sourceRoot":"","sources":["../../../../src/state/persistence/session-persister.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAWrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAE5E,OAAO,KAAK,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAsCnG;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB;IACnC,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,cAAc,EAAE,MAAM,wBAAwB,CAAC;IAC/C,+EAA+E;IAC/E,aAAa,EAAE,MAAM,MAAM,EAAE,CAAC;IAC9B,oEAAoE;IACpE,SAAS,EAAE,MAAM,QAAQ,GAAG,SAAS,CAAC;IACtC,mEAAmE;IACnE,QAAQ,EAAE,MAAM,kBAAkB,GAAG,SAAS,CAAC;IAC/C;;;;OAIG;IACH,oBAAoB,EAAE,MAAM,eAAe,EAAE,CAAC;CAC/C;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gBAAgB;IA0Bf,OAAO,CAAC,IAAI;IAzBxB,OAAO,CAAC,UAAU,CAAC,CAAgC;IACnD,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,iBAAiB,CAAC,CAAgC;IAC1D,OAAO,CAAC,kBAAkB,CAAK;IAC/B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAA+B;IAC7C;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB,CAAmC;IAC7D;;;;;OAKG;IACH,OAAO,CAAC,uBAAuB,CAAS;gBAEpB,IAAI,EAAE,oBAAoB;IAE9C;;;;;;;;;;OAUG;IACH,UAAU,CAAC,IAAI,EAAE,oBAAoB,GAAG,IAAI;IAI5C,0FAA0F;IAC1F,IAAI,iBAAiB,IAAI,kBAAkB,GAAG,IAAI,CAEjD;IAED,uFAAuF;IACvF,IAAI,sBAAsB,IAAI,OAAO,CAEpC;IAED,OAAO,KAAK,QAAQ,GAEnB;IAED,OAAO,CAAC,OAAO;IAIf;;;;;OAKG;IACH,YAAY,IAAI,IAAI;IAapB;;;;;OAKG;IACH,iBAAiB,IAAI,IAAI;IAOzB;;;;OAIG;YACW,cAAc;IAsJ5B;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAoK9B;;;;;;;OAOG;IACH,OAAO,CAAC,uBAAuB;IAU/B;;;;OAIG;IACG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBvC;;;;OAIG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAatC;;;;;OAKG;IACH,eAAe,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI;IAchD;;;;;;OAMG;IACH,mBAAmB,IAAI,IAAI;IAiB3B;;;;;;OAMG;IACG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAYvC
|
|
1
|
+
{"version":3,"file":"session-persister.d.ts","sourceRoot":"","sources":["../../../../src/state/persistence/session-persister.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AAWrE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAE5E,OAAO,KAAK,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAsCnG;;;;;;;GAOG;AACH,MAAM,WAAW,oBAAoB;IACnC,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,cAAc,EAAE,MAAM,wBAAwB,CAAC;IAC/C,+EAA+E;IAC/E,aAAa,EAAE,MAAM,MAAM,EAAE,CAAC;IAC9B,oEAAoE;IACpE,SAAS,EAAE,MAAM,QAAQ,GAAG,SAAS,CAAC;IACtC,mEAAmE;IACnE,QAAQ,EAAE,MAAM,kBAAkB,GAAG,SAAS,CAAC;IAC/C;;;;OAIG;IACH,oBAAoB,EAAE,MAAM,eAAe,EAAE,CAAC;CAC/C;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gBAAgB;IA0Bf,OAAO,CAAC,IAAI;IAzBxB,OAAO,CAAC,UAAU,CAAC,CAAgC;IACnD,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,iBAAiB,CAAC,CAAgC;IAC1D,OAAO,CAAC,kBAAkB,CAAK;IAC/B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAA+B;IAC7C;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB,CAAmC;IAC7D;;;;;OAKG;IACH,OAAO,CAAC,uBAAuB,CAAS;gBAEpB,IAAI,EAAE,oBAAoB;IAE9C;;;;;;;;;;OAUG;IACH,UAAU,CAAC,IAAI,EAAE,oBAAoB,GAAG,IAAI;IAI5C,0FAA0F;IAC1F,IAAI,iBAAiB,IAAI,kBAAkB,GAAG,IAAI,CAEjD;IAED,uFAAuF;IACvF,IAAI,sBAAsB,IAAI,OAAO,CAEpC;IAED,OAAO,KAAK,QAAQ,GAEnB;IAED,OAAO,CAAC,OAAO;IAIf;;;;;OAKG;IACH,YAAY,IAAI,IAAI;IAapB;;;;;OAKG;IACH,iBAAiB,IAAI,IAAI;IAOzB;;;;OAIG;YACW,cAAc;IAsJ5B;;;;;;OAMG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAoK9B;;;;;;;OAOG;IACH,OAAO,CAAC,uBAAuB;IAU/B;;;;OAIG;IACG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBvC;;;;OAIG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAatC;;;;;OAKG;IACH,eAAe,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI;IAchD;;;;;;OAMG;IACH,mBAAmB,IAAI,IAAI;IAiB3B;;;;;;OAMG;IACG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAYvC;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,uBAAuB;IAwC/B,sEAAsE;IACtE,OAAO,IAAI,IAAI;CAMhB"}
|
|
@@ -8,19 +8,27 @@
|
|
|
8
8
|
* @packageDocumentation
|
|
9
9
|
*/
|
|
10
10
|
/**
|
|
11
|
-
* Stable identity for a message diagnostic entry — `timestamp::role::subAgentOf`.
|
|
11
|
+
* Stable identity for a message diagnostic entry — `timestamp::role::subAgentOf::category`.
|
|
12
12
|
* Used by the forward-capture delta (skip already-emitted messages) and by the
|
|
13
13
|
* restore watermark (skip messages already present in a prior load's stream).
|
|
14
14
|
* `null` when the entry has no timestamp → treat as always-new. Shared so the
|
|
15
15
|
* element's diagnostics capture and the `SessionPersister`'s restore watermark
|
|
16
16
|
* compute identity identically (GENC-1351 §5.8/§5.10).
|
|
17
17
|
*
|
|
18
|
+
* `category` is part of the key because the reasoning/narration/answer split (GENC-1411) emits
|
|
19
|
+
* SEVERAL messages from one model response — the thought summary, any interstitial narration, and
|
|
20
|
+
* the answer/tool-call message — which share `timestamp` + `role` + `subAgentOf`. Keying on those
|
|
21
|
+
* alone collapsed them to one, so the forward-capture dropped all but the first: a server
|
|
22
|
+
* generation's answer message vanished from the persisted debug log while its reasoning survived
|
|
23
|
+
* (GENC-1461). `category` is stable across a message's in-place annotation (only `toolCalls` change,
|
|
24
|
+
* which is deliberately excluded), so a genuine re-append of the same message still dedups.
|
|
25
|
+
*
|
|
18
26
|
* @internal
|
|
19
27
|
*/
|
|
20
28
|
export function diagnosticsMessageKey(entry) {
|
|
21
|
-
var _a, _b;
|
|
29
|
+
var _a, _b, _c;
|
|
22
30
|
return typeof entry.timestamp === 'string'
|
|
23
|
-
? `${entry.timestamp}::${(_a = entry.role) !== null && _a !== void 0 ? _a : ''}::${(_b = entry.subAgentOf) !== null && _b !== void 0 ? _b : ''}`
|
|
31
|
+
? `${entry.timestamp}::${(_a = entry.role) !== null && _a !== void 0 ? _a : ''}::${(_b = entry.subAgentOf) !== null && _b !== void 0 ? _b : ''}::${(_c = entry.category) !== null && _c !== void 0 ? _c : ''}`
|
|
24
32
|
: null;
|
|
25
33
|
}
|
|
26
34
|
// Tie-break co-timestamped entries by cause → call → output (event, turn, message),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
|
|
2
|
-
import { assembleDebugLog } from './diagnostics';
|
|
2
|
+
import { assembleDebugLog, diagnosticsMessageKey } from './diagnostics';
|
|
3
3
|
// GENC-1351 §5.8: `assembleDebugLog` reassembles the forward-only diagnostics
|
|
4
4
|
// stream back into the `{ readme, timeline, meta }` debug-log shape — timeline
|
|
5
5
|
// sorted by timestamp, meta-snapshots pulled out with the latest winning.
|
|
@@ -70,3 +70,38 @@ Suite('collapses exact-duplicate entries but keeps genuinely-distinct ones', ()
|
|
|
70
70
|
assert.is(timeline.filter((e) => e.type === 'assistant.connected').length, 2, 'two distinct connects (different index) kept; the repeated connA collapsed');
|
|
71
71
|
});
|
|
72
72
|
Suite.run();
|
|
73
|
+
// GENC-1461: the reasoning/narration/answer split (GENC-1411) emits several messages from ONE model
|
|
74
|
+
// response, so they share `timestamp` + `role` + `subAgentOf`. The message identity key must still
|
|
75
|
+
// tell them apart — otherwise the forward-capture dedup treats them as one and drops all but the
|
|
76
|
+
// first (a server generation's answer message vanished from the persisted download while its
|
|
77
|
+
// reasoning survived).
|
|
78
|
+
const KeySuite = createLogicSuite('diagnosticsMessageKey');
|
|
79
|
+
KeySuite('gives co-timestamped split messages (reasoning vs answer) distinct keys', () => {
|
|
80
|
+
const base = {
|
|
81
|
+
kind: 'message',
|
|
82
|
+
role: 'assistant',
|
|
83
|
+
timestamp: '2026-01-01T00:00:05.000Z',
|
|
84
|
+
};
|
|
85
|
+
const reasoning = Object.assign(Object.assign({}, base), { category: 'reasoning', content: 'thinking…' });
|
|
86
|
+
const answer = Object.assign(Object.assign({}, base), { content: 'the generated code' }); // no category = the answer
|
|
87
|
+
const kReasoning = diagnosticsMessageKey(reasoning);
|
|
88
|
+
const kAnswer = diagnosticsMessageKey(answer);
|
|
89
|
+
assert.ok(kReasoning, 'reasoning message has a key');
|
|
90
|
+
assert.ok(kAnswer, 'answer message has a key');
|
|
91
|
+
assert.not.equal(kReasoning, kAnswer, 'a reasoning and an answer split from one response must not collide');
|
|
92
|
+
});
|
|
93
|
+
KeySuite('still collapses a genuine re-append of the same message (same key)', () => {
|
|
94
|
+
const m = {
|
|
95
|
+
kind: 'message',
|
|
96
|
+
role: 'assistant',
|
|
97
|
+
timestamp: '2026-01-01T00:00:05.000Z',
|
|
98
|
+
category: 'reasoning',
|
|
99
|
+
content: 'thinking…',
|
|
100
|
+
};
|
|
101
|
+
// Two instances / a reload re-appending the SAME message must still share one key.
|
|
102
|
+
assert.equal(diagnosticsMessageKey(m), diagnosticsMessageKey(Object.assign({}, m)));
|
|
103
|
+
});
|
|
104
|
+
KeySuite('returns null when the entry has no timestamp (treated as always-new)', () => {
|
|
105
|
+
assert.is(diagnosticsMessageKey({ kind: 'message', role: 'assistant' }), null);
|
|
106
|
+
});
|
|
107
|
+
KeySuite.run();
|
|
@@ -585,6 +585,18 @@ export class SessionPersister {
|
|
|
585
585
|
* (§5.8). Skips messages present at restore or already appended; emits each turn
|
|
586
586
|
* (by turn-index) and meta-event (by index) once; appends a `meta-snapshot` only
|
|
587
587
|
* when the block changed. Advances the shared per-session cursors as it goes.
|
|
588
|
+
*
|
|
589
|
+
* TODO(GENC-1461, longer-term): this whole delta step exists only because the element *pulls* the
|
|
590
|
+
* ENTIRE debug log on every throttled flush (`getDiagnosticEntries()` returns the full timeline),
|
|
591
|
+
* so we must diff it against per-session cursors to append only the new tail — and messages, unlike
|
|
592
|
+
* events (monotonic `index`) and turns (`turnIndex`), have no stable positional cursor (compaction
|
|
593
|
+
* rewrites ranges, tool-call messages are annotated in place, synthetic messages are spliced in,
|
|
594
|
+
* restore reloads them), forcing a content-derived identity (`diagnosticsMessageKey`). That
|
|
595
|
+
* identity is inherently fragile: it was too coarse for the reasoning/answer/tool-call split and
|
|
596
|
+
* silently dropped co-timestamped messages (GENC-1461; fixed for now by adding `category`). The
|
|
597
|
+
* robust fix is to invert the flow — have the driver *push* each diagnostic entry exactly once at
|
|
598
|
+
* creation (an append-only event stream) instead of the element pulling-and-diffing — which removes
|
|
599
|
+
* the message-identity key and this dedup entirely. Bigger change; deferred.
|
|
588
600
|
*/
|
|
589
601
|
collectDiagnosticsDelta() {
|
|
590
602
|
var _a;
|
|
@@ -263,6 +263,9 @@ const metaSnap = (dedupSignature, meta) => ({
|
|
|
263
263
|
meta,
|
|
264
264
|
dedupSignature,
|
|
265
265
|
});
|
|
266
|
+
// A diagnostic MESSAGE entry as the reasoning/narration/answer split (GENC-1411) produces them —
|
|
267
|
+
// several messages from ONE response, sharing timestamp + role, differing by `category` / content.
|
|
268
|
+
const diagMsg = (over = {}) => (Object.assign({ kind: 'message', role: 'assistant', timestamp: '2026-01-01T00:00:05.000Z', content: '' }, over));
|
|
266
269
|
Suite('scheduleDiagnostics() appends the collected delta once per window', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
267
270
|
const { p, f } = makePersister({ diagnosticEntries: [evt(0), evt(1)] });
|
|
268
271
|
p.scheduleDiagnostics();
|
|
@@ -311,4 +314,26 @@ Suite('flushDiagnostics() dedups meta-snapshots by dedupSignature, not the volat
|
|
|
311
314
|
yield p.flushDiagnostics();
|
|
312
315
|
assert.is(f.appended.length, 2, 'changed config → re-appended');
|
|
313
316
|
}));
|
|
317
|
+
Suite('flushDiagnostics() keeps co-timestamped split messages (reasoning + answer), not deduped as one', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
318
|
+
// GENC-1461: the reasoning/narration/answer split emits several messages from ONE response, so
|
|
319
|
+
// they share timestamp + role + subAgentOf. The forward-capture keyed only on those, so it
|
|
320
|
+
// emitted the reasoning message and dropped the answer as a "duplicate" — a server generation's
|
|
321
|
+
// answer message vanished from the persisted download while its reasoning survived. Both must be
|
|
322
|
+
// captured.
|
|
323
|
+
const reasoning = diagMsg({ category: 'reasoning', content: 'thinking…' });
|
|
324
|
+
const answer = diagMsg({ content: 'the generated code' }); // no category = the answer/tool-call slot
|
|
325
|
+
const { p, f } = makePersister({ diagnosticEntries: [reasoning, answer] });
|
|
326
|
+
yield p.flushDiagnostics();
|
|
327
|
+
assert.is(f.appended.length, 1, 'one flush');
|
|
328
|
+
assert.is(f.appended[0].length, 2, 'both the reasoning and the answer message are captured, not collapsed to one');
|
|
329
|
+
}));
|
|
330
|
+
Suite('flushDiagnostics() still dedups a genuine re-append of the same message across flushes', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
331
|
+
// The dedup must still stop the SAME message being re-appended on every throttled flush.
|
|
332
|
+
const m = diagMsg({ category: 'reasoning', content: 'thinking…' });
|
|
333
|
+
const { p, f } = makePersister({ diagnosticEntries: [m] });
|
|
334
|
+
yield p.flushDiagnostics();
|
|
335
|
+
assert.is(f.appended.length, 1, 'first flush appends the message');
|
|
336
|
+
yield p.flushDiagnostics();
|
|
337
|
+
assert.is(f.appended.length, 1, 'unchanged message → no re-append');
|
|
338
|
+
}));
|
|
314
339
|
Suite.run();
|