@yadsh/dsh-qa-surface 0.15.0 → 0.15.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.
Files changed (86) hide show
  1. package/README.md +1 -1
  2. package/lib/attestation.js.map +1 -1
  3. package/lib/client/QaAccountsController.js +3 -1
  4. package/lib/client/QaAccountsController.js.map +1 -1
  5. package/lib/client/QaSessionController.js +66 -18
  6. package/lib/client/QaSessionController.js.map +1 -1
  7. package/lib/client/QaSurface.js +64 -2
  8. package/lib/client/QaSurface.js.map +1 -1
  9. package/lib/client/QaTranscriptAdapter.js +46 -8
  10. package/lib/client/QaTranscriptAdapter.js.map +1 -1
  11. package/lib/client/admin/pages/Users.js +2 -2
  12. package/lib/client/admin/pages/Users.js.map +1 -1
  13. package/lib/client/attestation.js +13 -5
  14. package/lib/client/attestation.js.map +1 -1
  15. package/lib/client/chat-artifacts.js +94 -0
  16. package/lib/client/chat-artifacts.js.map +1 -0
  17. package/lib/client/chat-files.js +24 -14
  18. package/lib/client/chat-files.js.map +1 -1
  19. package/lib/client/clipboard.js +86 -0
  20. package/lib/client/clipboard.js.map +1 -0
  21. package/lib/client/components/QaAuthGate.js +41 -5
  22. package/lib/client/components/QaAuthGate.js.map +1 -1
  23. package/lib/client/components/QaChangelog.js +46 -0
  24. package/lib/client/components/QaChangelog.js.map +1 -1
  25. package/lib/client/components/QaFileAttachment.js +7 -5
  26. package/lib/client/components/QaFileAttachment.js.map +1 -1
  27. package/lib/client/components/QaFilesPanel.js +7 -3
  28. package/lib/client/components/QaFilesPanel.js.map +1 -1
  29. package/lib/client/components/QaMessage.js +27 -24
  30. package/lib/client/components/QaMessage.js.map +1 -1
  31. package/lib/client/components/QaWorkspaceBrowser.js +18 -25
  32. package/lib/client/components/QaWorkspaceBrowser.js.map +1 -1
  33. package/lib/client/components/copy-hint.js +10 -0
  34. package/lib/client/components/copy-hint.js.map +1 -0
  35. package/lib/client/markdown/CodeBlock.js +6 -13
  36. package/lib/client/markdown/CodeBlock.js.map +1 -1
  37. package/lib/client/project-session-state.js +1 -0
  38. package/lib/client/project-session-state.js.map +1 -1
  39. package/lib/client/settings/styles.js +1 -1
  40. package/lib/client/styles.js +11 -4
  41. package/lib/client/styles.js.map +1 -1
  42. package/lib/client/user-settings/IntegrationTokensPage.js +4 -12
  43. package/lib/client/user-settings/IntegrationTokensPage.js.map +1 -1
  44. package/lib/client/user-settings/PasswordSettingsPage.js +6 -1
  45. package/lib/client/user-settings/PasswordSettingsPage.js.map +1 -1
  46. package/lib/client/workspace-download.js +32 -0
  47. package/lib/client/workspace-download.js.map +1 -0
  48. package/lib/client/workspace-paths.js +55 -0
  49. package/lib/client/workspace-paths.js.map +1 -0
  50. package/lib/client.js +727 -253
  51. package/lib/client.js.map +1 -1
  52. package/lib/index.js +15 -1
  53. package/lib/index.js.map +1 -1
  54. package/lib/integration/answer.js +53 -0
  55. package/lib/integration/answer.js.map +1 -1
  56. package/lib/integration/host-runner.js +85 -13
  57. package/lib/integration/host-runner.js.map +1 -1
  58. package/lib/integration/service.js +12 -0
  59. package/lib/integration/service.js.map +1 -1
  60. package/lib/secure-session.js +36 -11
  61. package/lib/secure-session.js.map +1 -1
  62. package/lib/typert.host.js +75 -75
  63. package/lib/typert.remote-client.js +75 -75
  64. package/lib/types/attestation.d.ts +1 -1
  65. package/lib/types/client/QaAccountsController.d.ts +2 -0
  66. package/lib/types/client/QaSessionController.d.ts +13 -0
  67. package/lib/types/client/QaTranscriptAdapter.d.ts +6 -0
  68. package/lib/types/client/attestation.d.ts +7 -3
  69. package/lib/types/client/chat-artifacts.d.ts +10 -0
  70. package/lib/types/client/chat-files.d.ts +13 -8
  71. package/lib/types/client/clipboard.d.ts +37 -0
  72. package/lib/types/client/components/QaAuthGate.d.ts +4 -2
  73. package/lib/types/client/components/QaFileAttachment.d.ts +13 -4
  74. package/lib/types/client/components/QaFilesPanel.d.ts +15 -2
  75. package/lib/types/client/components/QaMessage.d.ts +10 -2
  76. package/lib/types/client/components/QaWorkspaceBrowser.d.ts +7 -1
  77. package/lib/types/client/components/copy-hint.d.ts +8 -0
  78. package/lib/types/client/project-session-state.d.ts +5 -0
  79. package/lib/types/client/workspace-download.d.ts +16 -0
  80. package/lib/types/client/workspace-paths.d.ts +25 -0
  81. package/lib/types/integration/answer.d.ts +17 -0
  82. package/lib/types/integration/contract.d.ts +13 -0
  83. package/lib/types/secure-session.d.ts +4 -0
  84. package/lib/types/types.d.ts +26 -0
  85. package/locale/en.json +6 -0
  86. package/package.json +4 -2
package/lib/client.js CHANGED
@@ -8721,7 +8721,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8721
8721
  },
8722
8722
  sourceLocation: {
8723
8723
  "file": "packages/dsh-qa-surface/src/index.ts",
8724
- "line": 1014,
8724
+ "line": 1029,
8725
8725
  "column": 9
8726
8726
  }
8727
8727
  },
@@ -8757,7 +8757,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8757
8757
  },
8758
8758
  sourceLocation: {
8759
8759
  "file": "packages/dsh-qa-surface/src/index.ts",
8760
- "line": 1025,
8760
+ "line": 1040,
8761
8761
  "column": 3
8762
8762
  }
8763
8763
  },
@@ -8784,7 +8784,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8784
8784
  },
8785
8785
  sourceLocation: {
8786
8786
  "file": "packages/dsh-qa-surface/src/index.ts",
8787
- "line": 999,
8787
+ "line": 1014,
8788
8788
  "column": 3
8789
8789
  }
8790
8790
  },
@@ -8833,7 +8833,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8833
8833
  },
8834
8834
  sourceLocation: {
8835
8835
  "file": "packages/dsh-qa-surface/src/index.ts",
8836
- "line": 1039,
8836
+ "line": 1054,
8837
8837
  "column": 3
8838
8838
  }
8839
8839
  },
@@ -8869,7 +8869,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8869
8869
  },
8870
8870
  sourceLocation: {
8871
8871
  "file": "packages/dsh-qa-surface/src/index.ts",
8872
- "line": 1005,
8872
+ "line": 1020,
8873
8873
  "column": 3
8874
8874
  }
8875
8875
  },
@@ -8905,7 +8905,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8905
8905
  },
8906
8906
  sourceLocation: {
8907
8907
  "file": "packages/dsh-qa-surface/src/index.ts",
8908
- "line": 1084,
8908
+ "line": 1099,
8909
8909
  "column": 3
8910
8910
  }
8911
8911
  },
@@ -8954,7 +8954,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8954
8954
  },
8955
8955
  sourceLocation: {
8956
8956
  "file": "packages/dsh-qa-surface/src/index.ts",
8957
- "line": 1061,
8957
+ "line": 1076,
8958
8958
  "column": 3
8959
8959
  }
8960
8960
  },
@@ -8990,7 +8990,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
8990
8990
  },
8991
8991
  sourceLocation: {
8992
8992
  "file": "packages/dsh-qa-surface/src/index.ts",
8993
- "line": 1051,
8993
+ "line": 1066,
8994
8994
  "column": 3
8995
8995
  }
8996
8996
  },
@@ -9026,7 +9026,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9026
9026
  },
9027
9027
  sourceLocation: {
9028
9028
  "file": "packages/dsh-qa-surface/src/index.ts",
9029
- "line": 1073,
9029
+ "line": 1088,
9030
9030
  "column": 3
9031
9031
  }
9032
9032
  },
@@ -9075,7 +9075,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9075
9075
  },
9076
9076
  sourceLocation: {
9077
9077
  "file": "packages/dsh-qa-surface/src/index.ts",
9078
- "line": 1032,
9078
+ "line": 1047,
9079
9079
  "column": 3
9080
9080
  }
9081
9081
  },
@@ -9124,7 +9124,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9124
9124
  },
9125
9125
  sourceLocation: {
9126
9126
  "file": "packages/dsh-qa-surface/src/index.ts",
9127
- "line": 720,
9127
+ "line": 735,
9128
9128
  "column": 3
9129
9129
  }
9130
9130
  },
@@ -9160,7 +9160,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9160
9160
  },
9161
9161
  sourceLocation: {
9162
9162
  "file": "packages/dsh-qa-surface/src/index.ts",
9163
- "line": 754,
9163
+ "line": 769,
9164
9164
  "column": 3
9165
9165
  }
9166
9166
  },
@@ -9196,7 +9196,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9196
9196
  },
9197
9197
  sourceLocation: {
9198
9198
  "file": "packages/dsh-qa-surface/src/index.ts",
9199
- "line": 841,
9199
+ "line": 856,
9200
9200
  "column": 3
9201
9201
  }
9202
9202
  },
@@ -9223,7 +9223,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9223
9223
  },
9224
9224
  sourceLocation: {
9225
9225
  "file": "packages/dsh-qa-surface/src/index.ts",
9226
- "line": 775,
9226
+ "line": 790,
9227
9227
  "column": 3
9228
9228
  }
9229
9229
  },
@@ -9250,7 +9250,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9250
9250
  },
9251
9251
  sourceLocation: {
9252
9252
  "file": "packages/dsh-qa-surface/src/index.ts",
9253
- "line": 828,
9253
+ "line": 843,
9254
9254
  "column": 3
9255
9255
  }
9256
9256
  },
@@ -9286,7 +9286,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9286
9286
  },
9287
9287
  sourceLocation: {
9288
9288
  "file": "packages/dsh-qa-surface/src/index.ts",
9289
- "line": 714,
9289
+ "line": 729,
9290
9290
  "column": 3
9291
9291
  }
9292
9292
  },
@@ -9313,7 +9313,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9313
9313
  },
9314
9314
  sourceLocation: {
9315
9315
  "file": "packages/dsh-qa-surface/src/index.ts",
9316
- "line": 765,
9316
+ "line": 780,
9317
9317
  "column": 3
9318
9318
  }
9319
9319
  },
@@ -9349,7 +9349,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9349
9349
  },
9350
9350
  sourceLocation: {
9351
9351
  "file": "packages/dsh-qa-surface/src/index.ts",
9352
- "line": 709,
9352
+ "line": 724,
9353
9353
  "column": 3
9354
9354
  }
9355
9355
  },
@@ -9376,7 +9376,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9376
9376
  },
9377
9377
  sourceLocation: {
9378
9378
  "file": "packages/dsh-qa-surface/src/index.ts",
9379
- "line": 738,
9379
+ "line": 753,
9380
9380
  "column": 3
9381
9381
  }
9382
9382
  },
@@ -9412,7 +9412,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9412
9412
  },
9413
9413
  sourceLocation: {
9414
9414
  "file": "packages/dsh-qa-surface/src/index.ts",
9415
- "line": 854,
9415
+ "line": 869,
9416
9416
  "column": 3
9417
9417
  }
9418
9418
  },
@@ -9448,7 +9448,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9448
9448
  },
9449
9449
  sourceLocation: {
9450
9450
  "file": "packages/dsh-qa-surface/src/index.ts",
9451
- "line": 815,
9451
+ "line": 830,
9452
9452
  "column": 3
9453
9453
  }
9454
9454
  },
@@ -9484,7 +9484,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9484
9484
  },
9485
9485
  sourceLocation: {
9486
9486
  "file": "packages/dsh-qa-surface/src/index.ts",
9487
- "line": 788,
9487
+ "line": 803,
9488
9488
  "column": 3
9489
9489
  }
9490
9490
  },
@@ -9520,7 +9520,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9520
9520
  },
9521
9521
  sourceLocation: {
9522
9522
  "file": "packages/dsh-qa-surface/src/index.ts",
9523
- "line": 801,
9523
+ "line": 816,
9524
9524
  "column": 3
9525
9525
  }
9526
9526
  },
@@ -9547,7 +9547,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9547
9547
  },
9548
9548
  sourceLocation: {
9549
9549
  "file": "packages/dsh-qa-surface/src/index.ts",
9550
- "line": 744,
9550
+ "line": 759,
9551
9551
  "column": 3
9552
9552
  }
9553
9553
  },
@@ -9606,7 +9606,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9606
9606
  },
9607
9607
  sourceLocation: {
9608
9608
  "file": "packages/dsh-qa-surface/src/index.ts",
9609
- "line": 1456,
9609
+ "line": 1471,
9610
9610
  "column": 3
9611
9611
  }
9612
9612
  },
@@ -9642,7 +9642,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9642
9642
  },
9643
9643
  sourceLocation: {
9644
9644
  "file": "packages/dsh-qa-surface/src/index.ts",
9645
- "line": 1336,
9645
+ "line": 1351,
9646
9646
  "column": 3
9647
9647
  }
9648
9648
  },
@@ -9702,7 +9702,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9702
9702
  },
9703
9703
  sourceLocation: {
9704
9704
  "file": "packages/dsh-qa-surface/src/index.ts",
9705
- "line": 1316,
9705
+ "line": 1331,
9706
9706
  "column": 3
9707
9707
  }
9708
9708
  },
@@ -9738,7 +9738,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9738
9738
  },
9739
9739
  sourceLocation: {
9740
9740
  "file": "packages/dsh-qa-surface/src/index.ts",
9741
- "line": 1351,
9741
+ "line": 1366,
9742
9742
  "column": 3
9743
9743
  }
9744
9744
  },
@@ -9797,7 +9797,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9797
9797
  },
9798
9798
  sourceLocation: {
9799
9799
  "file": "packages/dsh-qa-surface/src/index.ts",
9800
- "line": 1361,
9800
+ "line": 1376,
9801
9801
  "column": 3
9802
9802
  }
9803
9803
  },
@@ -9833,7 +9833,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9833
9833
  },
9834
9834
  sourceLocation: {
9835
9835
  "file": "packages/dsh-qa-surface/src/index.ts",
9836
- "line": 1398,
9836
+ "line": 1413,
9837
9837
  "column": 3
9838
9838
  }
9839
9839
  },
@@ -9892,7 +9892,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9892
9892
  },
9893
9893
  sourceLocation: {
9894
9894
  "file": "packages/dsh-qa-surface/src/index.ts",
9895
- "line": 1210,
9895
+ "line": 1225,
9896
9896
  "column": 3
9897
9897
  }
9898
9898
  },
@@ -9941,7 +9941,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9941
9941
  },
9942
9942
  sourceLocation: {
9943
9943
  "file": "packages/dsh-qa-surface/src/index.ts",
9944
- "line": 1223,
9944
+ "line": 1238,
9945
9945
  "column": 3
9946
9946
  }
9947
9947
  },
@@ -10010,7 +10010,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10010
10010
  },
10011
10011
  sourceLocation: {
10012
10012
  "file": "packages/dsh-qa-surface/src/index.ts",
10013
- "line": 1196,
10013
+ "line": 1211,
10014
10014
  "column": 3
10015
10015
  }
10016
10016
  },
@@ -10037,7 +10037,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10037
10037
  },
10038
10038
  sourceLocation: {
10039
10039
  "file": "packages/dsh-qa-surface/src/index.ts",
10040
- "line": 1190,
10040
+ "line": 1205,
10041
10041
  "column": 3
10042
10042
  }
10043
10043
  },
@@ -10086,7 +10086,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10086
10086
  },
10087
10087
  sourceLocation: {
10088
10088
  "file": "packages/dsh-qa-surface/src/index.ts",
10089
- "line": 1241,
10089
+ "line": 1256,
10090
10090
  "column": 3
10091
10091
  }
10092
10092
  },
@@ -10114,7 +10114,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10114
10114
  },
10115
10115
  sourceLocation: {
10116
10116
  "file": "packages/dsh-qa-surface/src/index.ts",
10117
- "line": 1449,
10117
+ "line": 1464,
10118
10118
  "column": 3
10119
10119
  }
10120
10120
  },
@@ -10142,7 +10142,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10142
10142
  },
10143
10143
  sourceLocation: {
10144
10144
  "file": "packages/dsh-qa-surface/src/index.ts",
10145
- "line": 1260,
10145
+ "line": 1275,
10146
10146
  "column": 3
10147
10147
  }
10148
10148
  },
@@ -10169,7 +10169,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10169
10169
  },
10170
10170
  sourceLocation: {
10171
10171
  "file": "packages/dsh-qa-surface/src/index.ts",
10172
- "line": 1296,
10172
+ "line": 1311,
10173
10173
  "column": 3
10174
10174
  }
10175
10175
  },
@@ -10218,7 +10218,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10218
10218
  },
10219
10219
  sourceLocation: {
10220
10220
  "file": "packages/dsh-qa-surface/src/index.ts",
10221
- "line": 1426,
10221
+ "line": 1441,
10222
10222
  "column": 3
10223
10223
  }
10224
10224
  },
@@ -10277,7 +10277,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10277
10277
  },
10278
10278
  sourceLocation: {
10279
10279
  "file": "packages/dsh-qa-surface/src/index.ts",
10280
- "line": 1382,
10280
+ "line": 1397,
10281
10281
  "column": 3
10282
10282
  }
10283
10283
  },
@@ -10326,7 +10326,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10326
10326
  },
10327
10327
  sourceLocation: {
10328
10328
  "file": "packages/dsh-qa-surface/src/index.ts",
10329
- "line": 1305,
10329
+ "line": 1320,
10330
10330
  "column": 3
10331
10331
  }
10332
10332
  },
@@ -10376,7 +10376,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10376
10376
  },
10377
10377
  sourceLocation: {
10378
10378
  "file": "packages/dsh-qa-surface/src/index.ts",
10379
- "line": 1408,
10379
+ "line": 1423,
10380
10380
  "column": 3
10381
10381
  }
10382
10382
  },
@@ -10412,7 +10412,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10412
10412
  },
10413
10413
  sourceLocation: {
10414
10414
  "file": "packages/dsh-qa-surface/src/index.ts",
10415
- "line": 1441,
10415
+ "line": 1456,
10416
10416
  "column": 3
10417
10417
  }
10418
10418
  },
@@ -10461,7 +10461,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10461
10461
  },
10462
10462
  sourceLocation: {
10463
10463
  "file": "packages/dsh-qa-surface/src/index.ts",
10464
- "line": 1114,
10464
+ "line": 1129,
10465
10465
  "column": 3
10466
10466
  }
10467
10467
  },
@@ -10520,7 +10520,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10520
10520
  },
10521
10521
  sourceLocation: {
10522
10522
  "file": "packages/dsh-qa-surface/src/index.ts",
10523
- "line": 1142,
10523
+ "line": 1157,
10524
10524
  "column": 3
10525
10525
  }
10526
10526
  },
@@ -10556,7 +10556,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10556
10556
  },
10557
10557
  sourceLocation: {
10558
10558
  "file": "packages/dsh-qa-surface/src/index.ts",
10559
- "line": 1105,
10559
+ "line": 1120,
10560
10560
  "column": 3
10561
10561
  }
10562
10562
  },
@@ -10615,7 +10615,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10615
10615
  },
10616
10616
  sourceLocation: {
10617
10617
  "file": "packages/dsh-qa-surface/src/index.ts",
10618
- "line": 1129,
10618
+ "line": 1144,
10619
10619
  "column": 3
10620
10620
  }
10621
10621
  },
@@ -10651,7 +10651,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10651
10651
  },
10652
10652
  sourceLocation: {
10653
10653
  "file": "packages/dsh-qa-surface/src/index.ts",
10654
- "line": 1168,
10654
+ "line": 1183,
10655
10655
  "column": 9
10656
10656
  }
10657
10657
  },
@@ -10710,7 +10710,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10710
10710
  },
10711
10711
  sourceLocation: {
10712
10712
  "file": "packages/dsh-qa-surface/src/index.ts",
10713
- "line": 1155,
10713
+ "line": 1170,
10714
10714
  "column": 3
10715
10715
  }
10716
10716
  },
@@ -10759,7 +10759,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10759
10759
  },
10760
10760
  sourceLocation: {
10761
10761
  "file": "packages/dsh-qa-surface/src/index.ts",
10762
- "line": 1285,
10762
+ "line": 1300,
10763
10763
  "column": 3
10764
10764
  }
10765
10765
  },
@@ -10795,7 +10795,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10795
10795
  },
10796
10796
  sourceLocation: {
10797
10797
  "file": "packages/dsh-qa-surface/src/index.ts",
10798
- "line": 1279,
10798
+ "line": 1294,
10799
10799
  "column": 3
10800
10800
  }
10801
10801
  },
@@ -10854,7 +10854,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10854
10854
  },
10855
10855
  sourceLocation: {
10856
10856
  "file": "packages/dsh-qa-surface/src/index.ts",
10857
- "line": 1267,
10857
+ "line": 1282,
10858
10858
  "column": 3
10859
10859
  }
10860
10860
  },
@@ -10913,7 +10913,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10913
10913
  },
10914
10914
  sourceLocation: {
10915
10915
  "file": "packages/dsh-qa-surface/src/index.ts",
10916
- "line": 1532,
10916
+ "line": 1550,
10917
10917
  "column": 9
10918
10918
  }
10919
10919
  },
@@ -10972,7 +10972,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10972
10972
  },
10973
10973
  sourceLocation: {
10974
10974
  "file": "packages/dsh-qa-surface/src/index.ts",
10975
- "line": 1561,
10975
+ "line": 1579,
10976
10976
  "column": 9
10977
10977
  }
10978
10978
  },
@@ -11021,7 +11021,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11021
11021
  },
11022
11022
  sourceLocation: {
11023
11023
  "file": "packages/dsh-qa-surface/src/index.ts",
11024
- "line": 1573,
11024
+ "line": 1591,
11025
11025
  "column": 9
11026
11026
  }
11027
11027
  },
@@ -11070,7 +11070,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11070
11070
  },
11071
11071
  sourceLocation: {
11072
11072
  "file": "packages/dsh-qa-surface/src/index.ts",
11073
- "line": 900,
11073
+ "line": 915,
11074
11074
  "column": 9
11075
11075
  }
11076
11076
  },
@@ -11088,7 +11088,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11088
11088
  },
11089
11089
  sourceLocation: {
11090
11090
  "file": "packages/dsh-qa-surface/src/index.ts",
11091
- "line": 869,
11091
+ "line": 884,
11092
11092
  "column": 3
11093
11093
  }
11094
11094
  },
@@ -11137,7 +11137,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11137
11137
  },
11138
11138
  sourceLocation: {
11139
11139
  "file": "packages/dsh-qa-surface/src/index.ts",
11140
- "line": 1641,
11140
+ "line": 1659,
11141
11141
  "column": 9
11142
11142
  }
11143
11143
  },
@@ -11173,7 +11173,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11173
11173
  },
11174
11174
  sourceLocation: {
11175
11175
  "file": "packages/dsh-qa-surface/src/index.ts",
11176
- "line": 1518,
11176
+ "line": 1536,
11177
11177
  "column": 9
11178
11178
  }
11179
11179
  },
@@ -11209,7 +11209,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11209
11209
  },
11210
11210
  sourceLocation: {
11211
11211
  "file": "packages/dsh-qa-surface/src/index.ts",
11212
- "line": 1547,
11212
+ "line": 1565,
11213
11213
  "column": 9
11214
11214
  }
11215
11215
  },
@@ -11258,7 +11258,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11258
11258
  },
11259
11259
  sourceLocation: {
11260
11260
  "file": "packages/dsh-qa-surface/src/index.ts",
11261
- "line": 1702,
11261
+ "line": 1720,
11262
11262
  "column": 9
11263
11263
  }
11264
11264
  },
@@ -11276,7 +11276,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11276
11276
  },
11277
11277
  sourceLocation: {
11278
11278
  "file": "packages/dsh-qa-surface/src/index.ts",
11279
- "line": 887,
11279
+ "line": 902,
11280
11280
  "column": 3
11281
11281
  }
11282
11282
  },
@@ -11325,7 +11325,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11325
11325
  },
11326
11326
  sourceLocation: {
11327
11327
  "file": "packages/dsh-qa-surface/src/index.ts",
11328
- "line": 1592,
11328
+ "line": 1610,
11329
11329
  "column": 9
11330
11330
  }
11331
11331
  },
@@ -11374,7 +11374,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11374
11374
  },
11375
11375
  sourceLocation: {
11376
11376
  "file": "packages/dsh-qa-surface/src/index.ts",
11377
- "line": 1668,
11377
+ "line": 1686,
11378
11378
  "column": 9
11379
11379
  }
11380
11380
  },
@@ -11410,7 +11410,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11410
11410
  },
11411
11411
  sourceLocation: {
11412
11412
  "file": "packages/dsh-qa-surface/src/index.ts",
11413
- "line": 1469,
11413
+ "line": 1484,
11414
11414
  "column": 9
11415
11415
  }
11416
11416
  },
@@ -11446,7 +11446,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11446
11446
  },
11447
11447
  sourceLocation: {
11448
11448
  "file": "packages/dsh-qa-surface/src/index.ts",
11449
- "line": 1772,
11449
+ "line": 1790,
11450
11450
  "column": 3
11451
11451
  }
11452
11452
  },
@@ -11482,7 +11482,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11482
11482
  },
11483
11483
  sourceLocation: {
11484
11484
  "file": "packages/dsh-qa-surface/src/index.ts",
11485
- "line": 1766,
11485
+ "line": 1784,
11486
11486
  "column": 3
11487
11487
  }
11488
11488
  },
@@ -11509,7 +11509,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11509
11509
  },
11510
11510
  sourceLocation: {
11511
11511
  "file": "packages/dsh-qa-surface/src/index.ts",
11512
- "line": 1760,
11512
+ "line": 1778,
11513
11513
  "column": 3
11514
11514
  }
11515
11515
  },
@@ -11558,7 +11558,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11558
11558
  },
11559
11559
  sourceLocation: {
11560
11560
  "file": "packages/dsh-qa-surface/src/index.ts",
11561
- "line": 1791,
11561
+ "line": 1809,
11562
11562
  "column": 3
11563
11563
  }
11564
11564
  },
@@ -11585,7 +11585,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11585
11585
  },
11586
11586
  sourceLocation: {
11587
11587
  "file": "packages/dsh-qa-surface/src/index.ts",
11588
- "line": 1817,
11588
+ "line": 1835,
11589
11589
  "column": 3
11590
11590
  }
11591
11591
  },
@@ -11634,7 +11634,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11634
11634
  },
11635
11635
  sourceLocation: {
11636
11636
  "file": "packages/dsh-qa-surface/src/index.ts",
11637
- "line": 1781,
11637
+ "line": 1799,
11638
11638
  "column": 3
11639
11639
  }
11640
11640
  },
@@ -11683,7 +11683,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11683
11683
  },
11684
11684
  sourceLocation: {
11685
11685
  "file": "packages/dsh-qa-surface/src/index.ts",
11686
- "line": 1804,
11686
+ "line": 1822,
11687
11687
  "column": 3
11688
11688
  }
11689
11689
  },
@@ -11719,7 +11719,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11719
11719
  },
11720
11720
  sourceLocation: {
11721
11721
  "file": "packages/dsh-qa-surface/src/index.ts",
11722
- "line": 1834,
11722
+ "line": 1852,
11723
11723
  "column": 3
11724
11724
  }
11725
11725
  },
@@ -11778,7 +11778,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11778
11778
  },
11779
11779
  sourceLocation: {
11780
11780
  "file": "packages/dsh-qa-surface/src/index.ts",
11781
- "line": 1849,
11781
+ "line": 1867,
11782
11782
  "column": 3
11783
11783
  }
11784
11784
  },
@@ -11814,7 +11814,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11814
11814
  },
11815
11815
  sourceLocation: {
11816
11816
  "file": "packages/dsh-qa-surface/src/index.ts",
11817
- "line": 1505,
11817
+ "line": 1523,
11818
11818
  "column": 9
11819
11819
  }
11820
11820
  }
@@ -13428,7 +13428,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
13428
13428
  case "account-disabled": return "Аккаунт отключён администратором.";
13429
13429
  case "email-taken": return "Этот email уже зарегистрирован.";
13430
13430
  case "invalid-email": return "Введите корректный email.";
13431
- case "weak-password": return "Пароль должен быть не короче 8 символов.";
13431
+ case "weak-password": return `Пароль должен быть не короче 8 символов.`;
13432
13432
  case "invalid-display-name": return "Слишком длинное имя.";
13433
13433
  case "invalid-profile": return "Проверьте поля профиля: значение слишком длинное или недопустимое.";
13434
13434
  case "profile-disabled": return "Профиль отключён на этом сервере.";
@@ -14517,12 +14517,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14517
14517
  }
14518
14518
  };
14519
14519
  /**
14520
- * Refusals that classify an existing chat as belonging to an older deployment
14521
- * composition. Keeping only its transcript is safe: policyReady stays false,
14522
- * so no prompt, cancel, approval or question reaches the Host.
14520
+ * Refusals that classify what the browser has on screen rather than faulting
14521
+ * the deployment: an existing chat of an older composition, one adopted from
14522
+ * outside the QA policy, or an identity that is another conversation's
14523
+ * delegated child.
14524
+ *
14525
+ * Keeping only its transcript is safe: policyReady stays false, so no prompt,
14526
+ * cancel, approval or question reaches the Host.
14523
14527
  */
14524
14528
  function canOpenAsCompatibilityReadOnly(reason) {
14525
- return reason === "composition-mismatch" || reason === "agent-unavailable" || reason === "adoption-refused";
14529
+ return reason === "composition-mismatch" || reason === "agent-unavailable" || reason === "adoption-refused" || reason === "subagent-session";
14526
14530
  }
14527
14531
  /**
14528
14532
  * Extract the coarse attestation reason code from a Host wire failure, if the
@@ -14533,7 +14537,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
14533
14537
  return reason === void 0 ? null : reason;
14534
14538
  }
14535
14539
  function attestationHint(reason) {
14536
- if (reason === "agent-unavailable") return "The Host could not put a live agent behind this chat: resuming the composition its session recorded failed (a preset that no longer mounts does this). The refusal detail is in the Host logs.";
14540
+ if (reason === "agent-unavailable") return "This chat's transcript is on the Host, but no agent stands behind it: resuming the composition its session recorded failed — a preset that no longer mounts, or a log the Host refuses to read. That is not what a restarted stand looks like; after a restart the next open simply resumes the chat. The failing session is named in the Host log under `session.agent-resolve-rejected` — re-mount the preset that session recorded and re-open the chat, while New chat gives the visitor a working one meanwhile.";
14541
+ if (reason === "subagent-session") return "The id this page asked about is a delegated subagent run, not a chat: subagent routing owns that identity, so the Host puts no sendable agent behind it and nothing can be attested here. Read the run from its parent chat's work group — its sources already reach that chat. If a chat row of this browser leads here, it points at a child: start a new chat and leave that id behind.";
14537
14542
  if (reason === "unknown-tools") return "A lockdown.toolPolicy name is not mounted in this session's tool catalog — check the deployment agent preset and the tool's server availability.";
14538
14543
  if (reason === "workspace-unavailable") return "The configured session.workspaceId does not match a registered workspace - create the workspace or fix the id (session.cwd is the no-registry alternative).";
14539
14544
  if (reason === "composition-mismatch") return "The session's agent preset, workspace or model no longer matches the deployment QA config.";
@@ -15157,6 +15162,97 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15157
15162
  });
15158
15163
  }
15159
15164
  //#endregion
15165
+ //#region src/client/chat-artifacts.ts
15166
+ /**
15167
+ * The files one turn produced, read out of the tool results that produced them.
15168
+ *
15169
+ * A document the agent made is the result of the conversation, but nothing
15170
+ * carries it into the transcript: it is a file in the chat's own workspace, and
15171
+ * the only trace it leaves is the line the producing tool rendered. Reading
15172
+ * those lines is what turns a paragraph that mentions a file into a card a
15173
+ * person can open and download, so the reader does not have to know the files
15174
+ * tab exists to receive what they asked for.
15175
+ *
15176
+ * The line shape belongs to the tools of `@yadsh/dsh-documents`, which report
15177
+ * every produced file as `<format>: <path> (<size>, sha256 …)`. The path is the
15178
+ * workspace-relative name the tool was asked to report: an absolute path would
15179
+ * both refuse at the workspace fence and print the account's own directory into
15180
+ * the answer, so a line carrying one is ignored rather than rendered.
15181
+ */
15182
+ /** The tools that hand a file to the user, and the formats each reports. */
15183
+ const PRODUCED_FORMATS = Object.freeze({
15184
+ document_create: ["docx", "pdf"],
15185
+ document_convert: [
15186
+ "docx",
15187
+ "pdf",
15188
+ "md"
15189
+ ],
15190
+ document_to_markdown: ["markdown"],
15191
+ document_from_url: ["markdown"]
15192
+ });
15193
+ /** One reported file: `<format>: <path>` with an optional `(<size>, …)`. */
15194
+ const FILE_LINE = /^([a-z][a-z0-9]*):\s+(\S+)(?:\s+\(([^)]*)\))?$/u;
15195
+ /** A path spelled from the filesystem root, which the fence would refuse. */
15196
+ function isAbsolutePath(value) {
15197
+ return value.startsWith("/") || value.startsWith("\\") || /^[A-Za-z]:[\\/]/u.test(value);
15198
+ }
15199
+ /** `12.3 KiB` back to bytes; an unparseable size reads as unknown. */
15200
+ const UNIT_SCALES = Object.freeze({
15201
+ b: 1,
15202
+ kib: 1024,
15203
+ mib: 1048576,
15204
+ gib: 1073741824
15205
+ });
15206
+ function reportedBytes(details) {
15207
+ const size = /^([\d.]+)\s*([A-Za-z]+)$/u.exec(details?.split(",")[0]?.trim() ?? "");
15208
+ if (size === null) return 0;
15209
+ const value = Number.parseFloat(size[1] ?? "");
15210
+ const scale = UNIT_SCALES[(size[2] ?? "").toLowerCase()];
15211
+ if (!Number.isFinite(value) || scale === void 0) return 0;
15212
+ return Math.round(value * scale);
15213
+ }
15214
+ /** Last segment of a reported path, spelled with forward slashes. */
15215
+ function baseName(path) {
15216
+ const segments = path.split(/[/\\]/u).filter((segment) => segment !== "");
15217
+ return segments[segments.length - 1] ?? path;
15218
+ }
15219
+ /**
15220
+ * Whether a reported name is a file. A produced document always carries its
15221
+ * extension, while a prose line that happens to start with a format word — a
15222
+ * summary the tool wrote, a translation of the status — does not. Reading the
15223
+ * one as the other would card a sentence.
15224
+ */
15225
+ function isFileName(value) {
15226
+ return /^[^./\\][^/\\]*\.[A-Za-z][A-Za-z0-9]{0,9}$/u.test(baseName(value));
15227
+ }
15228
+ /**
15229
+ * The files one tool result reports, in the order it reported them.
15230
+ *
15231
+ * `output` is the rendered text of the result, the same string the work group
15232
+ * shows. A tool that produced nothing — a read, an inspection, a refusal — is
15233
+ * not in the table and contributes no artifact.
15234
+ */
15235
+ function artifactsOfToolResult(toolName, output) {
15236
+ const formats = PRODUCED_FORMATS[toolName];
15237
+ if (formats === void 0 || output === null) return [];
15238
+ const artifacts = [];
15239
+ for (const line of output.split(/\r?\n/u)) {
15240
+ const reported = FILE_LINE.exec(line.trim());
15241
+ if (reported === null) continue;
15242
+ const format = reported[1] ?? "";
15243
+ const path = reported[2] ?? "";
15244
+ if (!formats.includes(format)) continue;
15245
+ if (path === "failed" || !isFileName(path) || isAbsolutePath(path)) continue;
15246
+ artifacts.push({
15247
+ path,
15248
+ name: baseName(path),
15249
+ format,
15250
+ bytes: reportedBytes(reported[3])
15251
+ });
15252
+ }
15253
+ return artifacts;
15254
+ }
15255
+ //#endregion
15160
15256
  //#region src/provenance/normalize.ts
15161
15257
  const TRACKING_PARAMETERS = /* @__PURE__ */ new Set([
15162
15258
  "fbclid",
@@ -15864,6 +15960,55 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
15864
15960
  return configured === void 0 || configured === "" ? void 0 : configured;
15865
15961
  }
15866
15962
  //#endregion
15963
+ //#region src/client/workspace-paths.ts
15964
+ /**
15965
+ * The answer a person reads names a file; it does not say where the deployment
15966
+ * keeps the account that owns it.
15967
+ *
15968
+ * A model quoting a path it was handed prints it verbatim, and a QA deployment
15969
+ * gives every account its own directory under one parent, so a path printed
15970
+ * with an account identifier is a sentence about the server's layout — and
15971
+ * stand dialogs below the signature line can be read by other accounts of the
15972
+ * same server. The document tools report workspace-relative names, which is
15973
+ * the fix at the source; this is the repair for what still arrives, because a
15974
+ * model can rebuild a path from the working directory it is told, and because
15975
+ * an answer written before this change is durable history that replays.
15976
+ */
15977
+ /** Characters a literal path may carry into a regular expression. */
15978
+ function escapeForRegex(value) {
15979
+ return value.replace(/[.*+?^${}()|[\]\\]/gu, String.raw`\$&`);
15980
+ }
15981
+ /** One or two separators, either convention, as a regular-expression source. */
15982
+ const SEPARATOR = String.raw`[\\\/]+`;
15983
+ const TRAILING_SEPARATOR = String.raw`[\\\/]?`;
15984
+ /**
15985
+ * The workspace root spelled so either separator convention matches it: a
15986
+ * container records forward slashes, a browser on Windows may print backslashes
15987
+ * at the same position, and a mask that only reads one of them leaks on the
15988
+ * other. A leading separator is kept, so what replaces the root reads as a name
15989
+ * inside the workspace rather than as a path leading out of nowhere.
15990
+ */
15991
+ function rootPattern(workspaceRoot) {
15992
+ const segments = workspaceRoot.split(/[/\\]/u).filter((segment) => segment !== "").map((segment) => escapeForRegex(segment));
15993
+ const leading = /^[/\\]/u.test(workspaceRoot) ? SEPARATOR : "";
15994
+ return new RegExp(`${leading}${segments.join(SEPARATOR)}${TRAILING_SEPARATOR}`, "gu");
15995
+ }
15996
+ /**
15997
+ * Rewrite an answer so a file is named, not located.
15998
+ *
15999
+ * The chat's own workspace directory is removed, leaving the spelling the
16000
+ * producing tool reports and the workspace reads take. The per-account
16001
+ * partition is removed the same way even when the root is unknown or the answer
16002
+ * quotes a different chat's file, because the identifier inside it is the part
16003
+ * that must not be printed. A path that is neither of those is left alone: a
16004
+ * reader told about `/etc/hosts` is not being shown this deployment's layout.
16005
+ */
16006
+ function toWorkspaceRelativeText(text, workspaceRoot) {
16007
+ if (!text.includes("/") && !text.includes("\\")) return text;
16008
+ const root = workspaceRoot?.trim();
16009
+ return (root === void 0 || root === "" ? text : text.replace(rootPattern(root), "")).replace(/[/\\](?:[^/\\\s]+[/\\])*?\.qa-users[/\\][^/\\\s]+[/\\]/gu, "");
16010
+ }
16011
+ //#endregion
15867
16012
  //#region src/client/subagent-codenames.ts
15868
16013
  /**
15869
16014
  * Deterministic display codenames for subagent sessions.
@@ -16246,8 +16391,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16246
16391
  return Reflect.get(block, "type") === "text" && typeof Reflect.get(block, "text") === "string" ? [Reflect.get(block, "text")] : [];
16247
16392
  }).join("");
16248
16393
  }
16249
- function visibleAssistantText(blocks) {
16250
- return blocks.filter((block) => block.kind === "text" && typeof block.text === "string").map((block) => block.text).join("");
16394
+ function visibleAssistantText(blocks, workspaceRoot) {
16395
+ return toWorkspaceRelativeText(blocks.filter((block) => block.kind === "text" && typeof block.text === "string").map((block) => block.text).join(""), workspaceRoot);
16251
16396
  }
16252
16397
  /**
16253
16398
  * Response timing for the message row, straight from the host-recorded step
@@ -16277,15 +16422,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16277
16422
  buffer = {
16278
16423
  turn,
16279
16424
  text: [],
16280
- work: []
16425
+ work: [],
16426
+ artifacts: []
16281
16427
  };
16282
16428
  turns.set(turn, buffer);
16283
16429
  }
16284
16430
  return buffer;
16285
16431
  }
16286
- function collectAssistant(node, turns, toolHeads, showReasoning) {
16432
+ function collectAssistant(node, turns, toolHeads, showReasoning, workspaceRoot) {
16287
16433
  const turn = getTurn(turns, node.turn);
16288
- const text = visibleAssistantText(node.blocks);
16434
+ const text = visibleAssistantText(node.blocks, workspaceRoot);
16289
16435
  if (text !== "") turn.text.push({
16290
16436
  id: `assistant:${node.messageId ?? node.seq}`,
16291
16437
  seq: node.seq,
@@ -16335,6 +16481,31 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16335
16481
  }
16336
16482
  return settled;
16337
16483
  }
16484
+ /**
16485
+ * The files each turn's tools handed over, keyed onto the turn that made them.
16486
+ *
16487
+ * This reads the settled tool results whatever the work-view switch says: a
16488
+ * produced document is part of the answer, not tool noise, so a stand that
16489
+ * hides the tool rows must still hand the file to the reader. The turn is
16490
+ * resolved exactly as the work rows resolve it, because the card belongs to the
16491
+ * answer the same turn wrote.
16492
+ */
16493
+ function collectTurnArtifacts(nodes, turns, toolHeads) {
16494
+ let nearestTurn;
16495
+ for (const node of nodes) {
16496
+ if (node.kind === "assistant") nearestTurn = node.turn;
16497
+ if (node.kind !== "tool-result") continue;
16498
+ const head = toolHeads.get(node.callId);
16499
+ const name = head?.name ?? node.call?.name;
16500
+ const turnNumber = head?.turn ?? nearestTurn;
16501
+ if (name === void 0 || turnNumber === void 0) continue;
16502
+ const buffer = getTurn(turns, turnNumber);
16503
+ for (const artifact of artifactsOfToolResult(name, flattenToolOutput(node))) {
16504
+ if (buffer.artifacts.some((seen) => seen.path === artifact.path)) continue;
16505
+ buffer.artifacts.push(artifact);
16506
+ }
16507
+ }
16508
+ }
16338
16509
  function collectRunningTool(call, turns, toolHeads) {
16339
16510
  const head = toolHeads.get(call.callId);
16340
16511
  getTurn(turns, call.turn).work.push({
@@ -16420,7 +16591,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16420
16591
  turn: turn.turn,
16421
16592
  ...finalText.seq === void 0 ? {} : { seq: finalText.seq },
16422
16593
  ...finalText.timestamp === void 0 ? {} : { timestamp: finalText.timestamp },
16423
- ...finalText.stats === void 0 ? {} : { stats: finalText.stats }
16594
+ ...finalText.stats === void 0 ? {} : { stats: finalText.stats },
16595
+ ...turn.artifacts.length === 0 ? {} : { artifacts: turn.artifacts }
16424
16596
  }
16425
16597
  });
16426
16598
  }
@@ -16428,6 +16600,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16428
16600
  function projectTranscript(snapshot, options = {}) {
16429
16601
  const legacy = chatLegacyOf(snapshot);
16430
16602
  const running = options.running === true;
16603
+ const workspaceRoot = options.workspaceRoot;
16431
16604
  const output = [];
16432
16605
  const turns = /* @__PURE__ */ new Map();
16433
16606
  const toolHeads = /* @__PURE__ */ new Map();
@@ -16450,7 +16623,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16450
16623
  ...files.length === 0 ? {} : { files }
16451
16624
  }
16452
16625
  });
16453
- } else if (node.kind === "assistant") collectAssistant(node, turns, toolHeads, options.showReasoning === true);
16626
+ } else if (node.kind === "assistant") collectAssistant(node, turns, toolHeads, options.showReasoning === true, workspaceRoot);
16454
16627
  else if (node.kind === "context") {
16455
16628
  if (!(node.producer.label ?? "").toLowerCase().startsWith("subagent")) continue;
16456
16629
  const text = visibleContentText(node.content);
@@ -16517,13 +16690,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16517
16690
  timestamp: node.time
16518
16691
  }
16519
16692
  });
16693
+ collectTurnArtifacts(legacy.nodes, turns, toolHeads);
16520
16694
  if (options.showToolActivity === true) {
16521
16695
  const settled = collectSettledTools(legacy.nodes, turns, toolHeads);
16522
16696
  for (const call of legacy.runningCalls) if (!settled.has(call.callId)) collectRunningTool(call, turns, toolHeads);
16523
16697
  }
16524
16698
  if (legacy.partial !== null) {
16525
16699
  const partialTurn = getTurn(turns, legacy.partial.turn);
16526
- const text = visibleAssistantText(legacy.partial.blocks);
16700
+ const text = visibleAssistantText(legacy.partial.blocks, workspaceRoot);
16527
16701
  if (text !== "") partialTurn.text.push({
16528
16702
  id: `assistant:partial:${legacy.partial.turn}:${legacy.partial.step}`,
16529
16703
  order: Number.MAX_SAFE_INTEGER - 1,
@@ -16687,7 +16861,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
16687
16861
  showToolActivity: config.ui.showToolActivity,
16688
16862
  showReasoning: config.ui.showReasoning,
16689
16863
  subagentNames: input.subagentNames,
16690
- subagentCodenames: config.ui.subagentCodenames
16864
+ subagentCodenames: config.ui.subagentCodenames,
16865
+ workspaceRoot: input.workspaceRoot
16691
16866
  }).map((message) => {
16692
16867
  if (message.role === "user" && input.author !== void 0) return {
16693
16868
  ...message,
@@ -17425,10 +17600,29 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17425
17600
  }
17426
17601
  if (this.disposed || this.generation !== owned) return;
17427
17602
  }
17603
+ this.enterDraft();
17604
+ }
17605
+ /**
17606
+ * Put a draft on the screen: no session, so no row in the chat history and no
17607
+ * record on the Host until the first prompt is sent into it
17608
+ * ({@link materializeDraft}). Taking the screen moves the generation the way
17609
+ * every other path that retires a binding does, so an adoption still looking
17610
+ * for its session measures itself against the new one rather than taking the
17611
+ * draft back.
17612
+ *
17613
+ * A draft that is already on screen — including one whose first send fell
17614
+ * short of a claimed session — keeps its chat identity: a new one rebuilds the
17615
+ * composer over the question the visitor is still looking at.
17616
+ */
17617
+ enterDraft() {
17428
17618
  this.generation += 1;
17429
17619
  this.materializing = void 0;
17430
17620
  this.drafting = true;
17431
- this.openChat();
17621
+ if (this.namedSession !== null) this.openChat();
17622
+ else {
17623
+ this.pendingSubmission = void 0;
17624
+ this.admissionPending = false;
17625
+ }
17432
17626
  this.unbind();
17433
17627
  this.operationError = null;
17434
17628
  this.policyReady = false;
@@ -17638,6 +17832,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17638
17832
  this.listeners.clear();
17639
17833
  }
17640
17834
  async ensureSessionNow() {
17835
+ const resumable = this.config.session.policy === "browser-persistent" && this.chats.activeId() !== null;
17836
+ if (this.config.session.policy !== "fixed" && !resumable) {
17837
+ this.enterDraft();
17838
+ return;
17839
+ }
17641
17840
  const operation = ++this.generation;
17642
17841
  this.drafting = false;
17643
17842
  this.state = {
@@ -17666,12 +17865,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17666
17865
  existing = true;
17667
17866
  } else if (stored !== null) this.chats.clearActive();
17668
17867
  }
17669
- if (id === null) id = await createQaSession({
17670
- createSession: this.createSessionRemote,
17671
- token: this.accounts?.token() ?? "",
17672
- subroleId: this.selectedSubrole,
17673
- adminPreview: this.adminPreview
17674
- });
17868
+ if (id === null) {
17869
+ this.enterDraft();
17870
+ return;
17871
+ }
17675
17872
  if (this.disposed || operation !== this.generation) return;
17676
17873
  try {
17677
17874
  if (!await this.bind(id, {
@@ -17942,7 +18139,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17942
18139
  publish() {
17943
18140
  if (this.disposed) return;
17944
18141
  const connected = this.connection.getSnapshot() !== void 0;
17945
- if (this.drafting && this.session === void 0) {
18142
+ const refusedDraft = this.drafting && this.namedSession === null && this.materializing === void 0;
18143
+ if (this.drafting && (this.session === void 0 || refusedDraft)) {
17946
18144
  const materializing = this.materializing !== void 0 || this.pendingSubmission !== void 0;
17947
18145
  this.state = {
17948
18146
  ...QA_SESSION_IDLE_STATE,
@@ -17972,7 +18170,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17972
18170
  const conversationSnapshot = this.conversationBinding?.snapshot.getSnapshot();
17973
18171
  const projectedSourceBundles = projectTurnSources(conversationSnapshot, sessionId, this.sourceAnchor(sessionId));
17974
18172
  const sourceBundles = this.hostSources.merge(projectedSourceBundles);
17975
- if (!snapshot.running) this.hostSources.refresh(sessionId, () => this.publish());
18173
+ if (!snapshot.running && this.viewingSubagent === null) this.hostSources.refresh(sessionId, () => this.publish());
17976
18174
  this.probePending(sessionId, connected);
17977
18175
  this.syncPendingPolling(snapshot.running === true && !this.compatibilityReadOnly);
17978
18176
  const queuedMessages = this.queuedMessages();
@@ -17983,6 +18181,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
17983
18181
  sessionSnapshot: snapshot,
17984
18182
  conversationSnapshot,
17985
18183
  sourceBundles,
18184
+ workspaceRoot: this.sourceAnchor(sessionId),
17986
18185
  approvals: this.hostApprovals.list(),
17987
18186
  questions: this.hostQuestions.list(),
17988
18187
  author: this.accounts?.messageAuthorOf(sessionId),
@@ -18617,6 +18816,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18617
18816
  }
18618
18817
  //#endregion
18619
18818
  //#region src/client/components/QaAuthGate.tsx
18819
+ /** The Host's address shape, mirrored so the card refuses before the round trip. */
18820
+ const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/u;
18821
+ /** Own copy for input the browser would otherwise answer with its own English bubble. */
18822
+ function refusalOf(email, password) {
18823
+ if (!EMAIL_PATTERN.test(email.trim().toLowerCase())) return accountsErrorMessage("invalid-email");
18824
+ if (password !== null && password.length < 8) return accountsErrorMessage("weak-password");
18825
+ return null;
18826
+ }
18620
18827
  function Logo({ logoUrl }) {
18621
18828
  if (logoUrl !== null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
18622
18829
  className: "dsh-qa-auth__logo",
@@ -18635,7 +18842,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18635
18842
  /**
18636
18843
  * The full-frame login/registration card shown instead of the chat while the
18637
18844
  * deployment has accounts enabled and the browser holds no valid identity.
18638
- * Errors are the Host's coarse codes rendered as audience-safe copy.
18845
+ * Both halves of the error line are the surface's own copy: the card refuses
18846
+ * mistyped input itself, and renders the Host's coarse codes in audience-safe
18847
+ * words.
18639
18848
  */
18640
18849
  function QaAuthGate(props) {
18641
18850
  const { snapshot, accounts } = props;
@@ -18645,11 +18854,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18645
18854
  const notice = gate?.notice ?? null;
18646
18855
  const [email, setEmail] = (0, react.useState)("");
18647
18856
  const [password, setPassword] = (0, react.useState)("");
18857
+ const [refusal, setRefusal] = (0, react.useState)(null);
18648
18858
  const showRegister = props.allowRegistration;
18649
18859
  const reset = mode === "reset";
18860
+ const error = refusal ?? gate?.error ?? null;
18650
18861
  const submit = (event) => {
18651
18862
  event.preventDefault();
18652
18863
  if (busy) return;
18864
+ const next = refusalOf(email, reset ? null : password);
18865
+ setRefusal(next);
18866
+ if (next !== null) return;
18653
18867
  reset ? accounts.requestPasswordReset(email) : mode === "register" ? accounts.register(email, password) : accounts.login(email, password);
18654
18868
  };
18655
18869
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("main", {
@@ -18659,6 +18873,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18659
18873
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
18660
18874
  className: "dsh-qa-auth__card",
18661
18875
  "data-testid": "qa-surface-auth-card",
18876
+ noValidate: true,
18662
18877
  onSubmit: submit,
18663
18878
  children: [
18664
18879
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -18685,7 +18900,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18685
18900
  "data-testid": "qa-surface-auth-tab-login",
18686
18901
  "aria-selected": mode === "login",
18687
18902
  className: mode === "login" ? "dsh-qa-auth__tab dsh-qa-auth__tab--active" : "dsh-qa-auth__tab",
18688
- onClick: () => accounts.setMode("login"),
18903
+ onClick: () => {
18904
+ setRefusal(null);
18905
+ accounts.setMode("login");
18906
+ },
18689
18907
  children: "Вход"
18690
18908
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
18691
18909
  type: "button",
@@ -18693,7 +18911,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18693
18911
  "data-testid": "qa-surface-auth-tab-register",
18694
18912
  "aria-selected": mode === "register",
18695
18913
  className: mode === "register" ? "dsh-qa-auth__tab dsh-qa-auth__tab--active" : "dsh-qa-auth__tab",
18696
- onClick: () => accounts.setMode("register"),
18914
+ onClick: () => {
18915
+ setRefusal(null);
18916
+ accounts.setMode("register");
18917
+ },
18697
18918
  children: "Регистрация"
18698
18919
  })]
18699
18920
  }) : null,
@@ -18708,7 +18929,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18708
18929
  required: true,
18709
18930
  disabled: busy,
18710
18931
  value: email,
18711
- onChange: (event) => setEmail(event.currentTarget.value)
18932
+ onChange: (event) => {
18933
+ setRefusal(null);
18934
+ setEmail(event.currentTarget.value);
18935
+ }
18712
18936
  })]
18713
18937
  }),
18714
18938
  reset ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
@@ -18723,15 +18947,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18723
18947
  minLength: 8,
18724
18948
  disabled: busy,
18725
18949
  value: password,
18726
- onChange: (event) => setPassword(event.currentTarget.value)
18950
+ onChange: (event) => {
18951
+ setRefusal(null);
18952
+ setPassword(event.currentTarget.value);
18953
+ }
18727
18954
  })]
18728
18955
  }),
18729
- gate !== void 0 && gate.error !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
18956
+ error === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
18730
18957
  className: "dsh-qa-auth__error",
18731
18958
  "data-testid": "qa-surface-auth-error",
18732
18959
  role: "alert",
18733
- children: gate.error
18734
- }) : null,
18960
+ children: error
18961
+ }),
18735
18962
  notice === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
18736
18963
  className: "dsh-qa-auth__notice",
18737
18964
  "data-testid": "qa-surface-auth-notice",
@@ -18750,7 +18977,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
18750
18977
  className: "dsh-qa-auth__link",
18751
18978
  "data-testid": "qa-surface-auth-forgot",
18752
18979
  disabled: busy,
18753
- onClick: () => accounts.setMode(reset ? "login" : "reset"),
18980
+ onClick: () => {
18981
+ setRefusal(null);
18982
+ accounts.setMode(reset ? "login" : "reset");
18983
+ },
18754
18984
  children: reset ? "Вернуться ко входу" : "Забыли пароль?"
18755
18985
  })
18756
18986
  ]
@@ -19560,11 +19790,24 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19560
19790
  return extension.length > 0 && extension.length <= 4 ? extension.toUpperCase() : "FILE";
19561
19791
  }
19562
19792
  /**
19563
- * One attached file: the extension badge, the name and the size. The browser
19564
- * never re-reads these bytes (the Host attachment route serves images), so
19565
- * both the composer card and the sent row show the same handle.
19793
+ * One attached file: the extension badge, the name and the size. A sent
19794
+ * attachment shows the handle the model resolved and the browser never
19795
+ * re-reads its bytes; a file the turn produced is the same chip with the two
19796
+ * controls that make it usable — open it, or take it away.
19566
19797
  */
19567
- function QaFileAttachment({ name, bytes, tone, onRemove }) {
19798
+ function QaFileAttachment({ name, bytes, tone, onRemove, onOpen, onDownload }) {
19799
+ const text = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
19800
+ className: "dsh-qa-file__text",
19801
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
19802
+ className: "dsh-qa-file__name",
19803
+ "data-testid": "qa-file-name",
19804
+ children: name
19805
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
19806
+ className: "dsh-qa-file__meta",
19807
+ "data-testid": "qa-file-size",
19808
+ children: formatFileSize(bytes)
19809
+ })]
19810
+ });
19568
19811
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
19569
19812
  className: "dsh-qa-file",
19570
19813
  "data-testid": "qa-file",
@@ -19577,17 +19820,23 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
19577
19820
  "aria-hidden": "true",
19578
19821
  children: badgeText(name)
19579
19822
  }),
19580
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
19581
- className: "dsh-qa-file__text",
19582
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
19583
- className: "dsh-qa-file__name",
19584
- "data-testid": "qa-file-name",
19585
- children: name
19586
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
19587
- className: "dsh-qa-file__meta",
19588
- "data-testid": "qa-file-size",
19589
- children: formatFileSize(bytes)
19590
- })]
19823
+ onOpen === void 0 ? text : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
19824
+ type: "button",
19825
+ className: "dsh-qa-file__open",
19826
+ "data-testid": "qa-file-open",
19827
+ "aria-label": `Открыть ${name}`,
19828
+ title: `Открыть ${name}`,
19829
+ onClick: onOpen,
19830
+ children: text
19831
+ }),
19832
+ onDownload === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
19833
+ type: "button",
19834
+ className: "dsh-qa-file__download",
19835
+ "data-testid": "qa-file-download",
19836
+ "aria-label": `Скачать ${name}`,
19837
+ title: "Скачать",
19838
+ onClick: onDownload,
19839
+ children: "Скачать"
19591
19840
  }),
19592
19841
  onRemove === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
19593
19842
  type: "button",
@@ -21574,6 +21823,124 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
21574
21823
  };
21575
21824
  }
21576
21825
  //#endregion
21826
+ //#region src/client/markdown/use-transient-flag.ts
21827
+ /**
21828
+ * A boolean that turns on for a fixed window and drops back off — the
21829
+ * "Скопировано" flash and its siblings. The pending timer lives in a ref and
21830
+ * is cleared by the unmount cleanup, so a pulse fired right before the block
21831
+ * leaves the DOM never schedules a state update on it, and re-pulsing restarts
21832
+ * the window instead of stacking timers.
21833
+ */
21834
+ function useTransientFlag(windowMs) {
21835
+ const [on, setOn] = (0, react.useState)(false);
21836
+ const timer = (0, react.useRef)(void 0);
21837
+ (0, react.useEffect)(() => () => {
21838
+ if (timer.current !== void 0) clearTimeout(timer.current);
21839
+ }, []);
21840
+ return {
21841
+ on,
21842
+ pulse: (0, react.useCallback)(() => {
21843
+ setOn(true);
21844
+ if (timer.current !== void 0) clearTimeout(timer.current);
21845
+ timer.current = setTimeout(() => {
21846
+ timer.current = void 0;
21847
+ setOn(false);
21848
+ }, windowMs);
21849
+ }, [windowMs])
21850
+ };
21851
+ }
21852
+ //#endregion
21853
+ //#region src/client/clipboard.ts
21854
+ /** How long the confirmation that a copy landed stays on screen. */
21855
+ const COPIED_WINDOW_MS = 1200;
21856
+ /**
21857
+ * The sentence a copy affordance falls back to. Three surfaces show it, so it
21858
+ * is written once.
21859
+ */
21860
+ const COPY_MANUAL_HINT = "Скопируйте вручную";
21861
+ /** The reason behind that sentence, for the tooltip that carries it. */
21862
+ const COPY_MANUAL_REASON = "Браузер не отдаёт буфер обмена — выделите текст и скопируйте его сами";
21863
+ function hasClipboardApi() {
21864
+ return navigator.clipboard?.writeText !== void 0;
21865
+ }
21866
+ /**
21867
+ * Whether this browser can be asked to move text at all.
21868
+ *
21869
+ * The stand is served over plain HTTP on a LAN address, and there
21870
+ * `window.isSecureContext` is false, so the browser never exposes
21871
+ * `navigator.clipboard`. The older copy command still works in an insecure
21872
+ * context, which is why the absence of the API alone does not mean copying is
21873
+ * impossible — and why asking for it anyway only ends in silence.
21874
+ */
21875
+ function canCopyToClipboard() {
21876
+ return hasClipboardApi() || typeof document.execCommand === "function";
21877
+ }
21878
+ /**
21879
+ * Write `text` to the clipboard, resolving false when nothing took it — the
21880
+ * caller then says so instead of leaving the click unanswered.
21881
+ */
21882
+ async function copyTextToClipboard(text) {
21883
+ const clipboard = navigator.clipboard;
21884
+ if (clipboard?.writeText !== void 0) try {
21885
+ await clipboard.writeText(text);
21886
+ return true;
21887
+ } catch {}
21888
+ return copyThroughField(text);
21889
+ }
21890
+ /** The pre-API path: select a throwaway field and ask the browser to copy it. */
21891
+ function copyThroughField(text) {
21892
+ const field = document.createElement("textarea");
21893
+ field.value = text;
21894
+ field.readOnly = true;
21895
+ field.setAttribute("aria-hidden", "true");
21896
+ field.style.position = "fixed";
21897
+ field.style.top = "0";
21898
+ field.style.left = "-100vw";
21899
+ document.body.appendChild(field);
21900
+ field.select();
21901
+ try {
21902
+ return document.execCommand("copy");
21903
+ } catch {
21904
+ return false;
21905
+ } finally {
21906
+ field.remove();
21907
+ }
21908
+ }
21909
+ /**
21910
+ * The copy state one affordance needs: the confirmation window, the refusal
21911
+ * that replaces it with the manual hint, and whether either was ever possible.
21912
+ */
21913
+ function useCopyAction(text) {
21914
+ const [impossible] = (0, react.useState)(() => !canCopyToClipboard());
21915
+ const { on: copied, pulse } = useTransientFlag(COPIED_WINDOW_MS);
21916
+ const [refused, setRefused] = (0, react.useState)(false);
21917
+ return {
21918
+ copied,
21919
+ impossible,
21920
+ refused,
21921
+ copy: (0, react.useCallback)(() => {
21922
+ copyTextToClipboard(text).then((ok) => {
21923
+ setRefused(!ok);
21924
+ if (ok) pulse();
21925
+ });
21926
+ }, [text, pulse])
21927
+ };
21928
+ }
21929
+ //#endregion
21930
+ //#region src/client/components/copy-hint.tsx
21931
+ /**
21932
+ * What a copy affordance shows where copying cannot happen: a sentence naming
21933
+ * the dead end, in place of a button that would only look like it worked.
21934
+ */
21935
+ function CopyHint({ testId }) {
21936
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
21937
+ className: "dsh-qa-copy-hint",
21938
+ "data-testid": testId,
21939
+ title: COPY_MANUAL_REASON,
21940
+ children: COPY_MANUAL_HINT
21941
+ });
21942
+ }
21943
+ //#endregion
21577
21944
  //#region src/client/markdown/highlight.ts
21578
21945
  /** Double and single quotes that end at the line they start on. */
21579
21946
  const QUOTES = [{
@@ -22698,36 +23065,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
22698
23065
  return cursor;
22699
23066
  }
22700
23067
  //#endregion
22701
- //#region src/client/markdown/use-transient-flag.ts
22702
- /**
22703
- * A boolean that turns on for a fixed window and drops back off — the
22704
- * "Скопировано" flash and its siblings. The pending timer lives in a ref and
22705
- * is cleared by the unmount cleanup, so a pulse fired right before the block
22706
- * leaves the DOM never schedules a state update on it, and re-pulsing restarts
22707
- * the window instead of stacking timers.
22708
- */
22709
- function useTransientFlag(windowMs) {
22710
- const [on, setOn] = (0, react.useState)(false);
22711
- const timer = (0, react.useRef)(void 0);
22712
- (0, react.useEffect)(() => () => {
22713
- if (timer.current !== void 0) clearTimeout(timer.current);
22714
- }, []);
22715
- return {
22716
- on,
22717
- pulse: (0, react.useCallback)(() => {
22718
- setOn(true);
22719
- if (timer.current !== void 0) clearTimeout(timer.current);
22720
- timer.current = setTimeout(() => {
22721
- timer.current = void 0;
22722
- setOn(false);
22723
- }, windowMs);
22724
- }, [windowMs])
22725
- };
22726
- }
22727
- //#endregion
22728
23068
  //#region src/client/markdown/CodeBlock.tsx
22729
- /** Copied-label window: long enough to read, short enough to not stick. */
22730
- const COPIED_MS = 1200;
22731
23069
  /**
22732
23070
  * One fenced code block: the language banner, a copy button, and the code
22733
23071
  * itself. Highlighting is our own scanner (see `highlight.ts`) — the fence is
@@ -22736,12 +23074,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
22736
23074
  */
22737
23075
  const CodeBlock = (0, react.memo)(function CodeBlock({ code, lang }) {
22738
23076
  const lines = (0, react.useMemo)(() => highlightLines(code, lang), [code, lang]);
22739
- const { on: copied, pulse: pulseCopied } = useTransientFlag(COPIED_MS);
22740
- const onCopy = (0, react.useCallback)(() => {
22741
- const clipboard = navigator.clipboard;
22742
- if (clipboard === void 0) return;
22743
- clipboard.writeText(code).then(pulseCopied, () => void 0);
22744
- }, [code, pulseCopied]);
23077
+ const copy = useCopyAction(code);
22745
23078
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
22746
23079
  className: "dsh-qa-md-code",
22747
23080
  "data-testid": "qa-md-code",
@@ -22752,14 +23085,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
22752
23085
  className: "dsh-qa-md-code__lang",
22753
23086
  "data-testid": "qa-md-code-lang",
22754
23087
  children: lang ?? ""
22755
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
23088
+ }), copy.impossible ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopyHint, { testId: "qa-md-code-copy-hint" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
22756
23089
  type: "button",
22757
23090
  className: "dsh-qa-md-code__copy",
22758
23091
  "data-testid": "qa-md-code-copy",
22759
- onClick: onCopy,
22760
- "aria-label": copied ? "Скопировано" : "Копировать код",
22761
- children: copied ? "Скопировано" : "Копировать"
22762
- })]
23092
+ onClick: copy.copy,
23093
+ "aria-label": copy.copied ? "Скопировано" : "Копировать код",
23094
+ children: copy.copied ? "Скопировано" : "Копировать"
23095
+ }), copy.refused ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopyHint, { testId: "qa-md-code-copy-hint" }) : null] })]
22763
23096
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
22764
23097
  tabIndex: 0,
22765
23098
  "data-testid": "qa-md-code-pre",
@@ -50301,7 +50634,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
50301
50634
  if (a.role === "work" && b.role === "work") return a.turn === b.turn && a.startedAt === b.startedAt && a.endedAt === b.endedAt && sameWorkItems(a.items, b.items);
50302
50635
  if (a.role === "work" || b.role === "work") return false;
50303
50636
  if (a.text !== b.text || a.timestamp !== b.timestamp) return false;
50304
- if (a.role === "assistant" && b.role === "assistant") return sameSources(a.sources, b.sources) && a.stats === void 0 === (b.stats === void 0) && (a.stats === void 0 || b.stats === void 0 || a.stats.durationMs === b.stats.durationMs && a.stats.ttftMs === b.stats.ttftMs && a.stats.tokensPerSecond === b.stats.tokensPerSecond);
50637
+ if (a.role === "assistant" && b.role === "assistant") return sameSources(a.sources, b.sources) && sameArtifacts(a.artifacts, b.artifacts) && a.stats === void 0 === (b.stats === void 0) && (a.stats === void 0 || b.stats === void 0 || a.stats.durationMs === b.stats.durationMs && a.stats.ttftMs === b.stats.ttftMs && a.stats.tokensPerSecond === b.stats.tokensPerSecond);
50305
50638
  if (a.role === "user" && b.role === "user") return a.author === b.author && sameImages(a.images, b.images) && sameFiles(a.files, b.files);
50306
50639
  if (a.role === "system" && b.role === "system") return a.notice === void 0 === (b.notice === void 0) && (a.notice === void 0 || b.notice === void 0 || a.notice.title === b.notice.title && a.notice.body === b.notice.body && a.notice.meta === b.notice.meta);
50307
50640
  return false;
@@ -50321,6 +50654,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
50321
50654
  if (a === void 0 || b === void 0 || a.length !== b.length) return false;
50322
50655
  return a.every((file, index) => file.attachmentId === b[index].attachmentId && file.name === b[index].name && file.bytes === b[index].bytes);
50323
50656
  }
50657
+ function sameArtifacts(a, b) {
50658
+ if (a === b) return true;
50659
+ if (a === void 0 || b === void 0 || a.length !== b.length) return false;
50660
+ return a.every((artifact, index) => artifact.path === b[index].path && artifact.name === b[index].name && artifact.format === b[index].format && artifact.bytes === b[index].bytes);
50661
+ }
50324
50662
  const REASONS = [
50325
50663
  "incorrect",
50326
50664
  "instruction_not_followed",
@@ -50411,9 +50749,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
50411
50749
  }
50412
50750
  return parts;
50413
50751
  }
50414
- const QaMessage = (0, react.memo)(function QaMessage({ message, renderMarkdown, showTimestamp, stateKey, onRegenerate, resolveImage, onOpenSources, onSourceDetail, thinkingPhrases, onRateFeedback }) {
50415
- const [copied, setCopied] = (0, react.useState)(false);
50416
- const copiedTimer = (0, react.useRef)(void 0);
50752
+ const QaMessage = (0, react.memo)(function QaMessage({ message, renderMarkdown, showTimestamp, stateKey, onRegenerate, resolveImage, onOpenSources, onSourceDetail, onArtifactOpen, onArtifactDownload, thinkingPhrases, onRateFeedback }) {
50753
+ const { copied, impossible: copyImpossible, refused: copyRefused, copy } = useCopyAction(message.role === "work" ? "" : message.text);
50417
50754
  const [rating, setRating] = (0, react.useState)(null);
50418
50755
  const [askingWhy, setAskingWhy] = (0, react.useState)(false);
50419
50756
  const refsCache = (0, react.useRef)({
@@ -50431,9 +50768,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
50431
50768
  (0, react.useEffect)(() => {
50432
50769
  setRating(readRatings(stateKey)[message.id] ?? null);
50433
50770
  }, [stateKey, message.id]);
50434
- (0, react.useEffect)(() => () => {
50435
- if (copiedTimer.current !== void 0) clearTimeout(copiedTimer.current);
50436
- }, []);
50437
50771
  if (message.role === "system" && message.command !== void 0) {
50438
50772
  const activity = message.command;
50439
50773
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
@@ -50515,15 +50849,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
50515
50849
  })
50516
50850
  });
50517
50851
  const label = message.role === "assistant" ? "Помощник" : message.role === "user" ? message.author ?? "Вы" : "Статус";
50518
- const copy = async () => {
50519
- if (copied || navigator.clipboard?.writeText === void 0) return;
50520
- try {
50521
- await navigator.clipboard.writeText(message.text);
50522
- setCopied(true);
50523
- if (copiedTimer.current !== void 0) clearTimeout(copiedTimer.current);
50524
- copiedTimer.current = setTimeout(() => setCopied(false), 1200);
50525
- } catch {}
50526
- };
50527
50852
  const persist = (next, detail) => {
50528
50853
  if (next === null || onRateFeedback === void 0) return;
50529
50854
  if (message.role !== "assistant" || message.seq === void 0) {
@@ -50600,6 +50925,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
50600
50925
  sourceRefs,
50601
50926
  onSourceOpen: onSourceDetail
50602
50927
  }) : message.text,
50928
+ message.role === "assistant" && message.artifacts !== void 0 && message.artifacts.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
50929
+ className: "dsh-qa-message__artifacts",
50930
+ "data-testid": "qa-message-artifacts",
50931
+ "aria-label": "Созданные файлы",
50932
+ children: message.artifacts.map((artifact) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QaFileAttachment, {
50933
+ name: artifact.name,
50934
+ bytes: artifact.bytes,
50935
+ tone: "sent",
50936
+ onOpen: onArtifactOpen === void 0 ? void 0 : () => onArtifactOpen(artifact),
50937
+ onDownload: onArtifactDownload === void 0 ? void 0 : () => onArtifactDownload(artifact)
50938
+ }, artifact.path))
50939
+ }) : null,
50603
50940
  message.status === "streaming" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
50604
50941
  className: "dsh-qa-message__cursor",
50605
50942
  "aria-hidden": "true"
@@ -50632,12 +50969,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
50632
50969
  "data-persistent": persistentMeta || void 0,
50633
50970
  children: [
50634
50971
  message.role === "user" ? meta : null,
50635
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
50972
+ copyImpossible ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopyHint, { testId: "qa-message-copy-hint" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
50636
50973
  type: "button",
50637
50974
  "data-testid": "qa-message-copy",
50638
50975
  "aria-label": copied ? "Скопировано" : "Скопировать сообщение",
50639
50976
  title: copied ? "Скопировано" : "Копировать",
50640
- onClick: () => void copy(),
50977
+ onClick: copy,
50641
50978
  children: copied ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
50642
50979
  viewBox: "0 0 18 18",
50643
50980
  "aria-hidden": "true",
@@ -50653,7 +50990,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
50653
50990
  rx: "2"
50654
50991
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M11.75 11.75v.5a2.5 2.5 0 0 1-2.5 2.5h-3.5a2.5 2.5 0 0 1-2.5-2.5v-3.5a2.5 2.5 0 0 1 2.5-2.5h.5" })]
50655
50992
  })
50656
- }),
50993
+ }), copyRefused ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopyHint, { testId: "qa-message-copy-hint" }) : null] }),
50657
50994
  message.role === "user" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
50658
50995
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
50659
50996
  type: "button",
@@ -50714,7 +51051,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
50714
51051
  }) : null
50715
51052
  ]
50716
51053
  });
50717
- }, (prev, next) => prev.renderMarkdown === next.renderMarkdown && prev.showTimestamp === next.showTimestamp && prev.stateKey === next.stateKey && prev.onRegenerate === next.onRegenerate && prev.resolveImage === next.resolveImage && prev.onOpenSources === next.onOpenSources && prev.onSourceDetail === next.onSourceDetail && prev.thinkingPhrases === next.thinkingPhrases && sameMessage(prev.message, next.message));
51054
+ }, (prev, next) => prev.renderMarkdown === next.renderMarkdown && prev.showTimestamp === next.showTimestamp && prev.stateKey === next.stateKey && prev.onRegenerate === next.onRegenerate && prev.resolveImage === next.resolveImage && prev.onOpenSources === next.onOpenSources && prev.onSourceDetail === next.onSourceDetail && prev.onArtifactOpen === next.onArtifactOpen && prev.onArtifactDownload === next.onArtifactDownload && prev.thinkingPhrases === next.thinkingPhrases && sameMessage(prev.message, next.message));
50718
51055
  //#endregion
50719
51056
  //#region ../../packages/audit-core/lib/types.js
50720
51057
  /**
@@ -52624,8 +52961,42 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
52624
52961
  }
52625
52962
  //#endregion
52626
52963
  //#region src/client/components/QaChangelog.tsx
52627
- const QA_VERSION = "0.15.0";
52964
+ const QA_VERSION = "0.15.2";
52628
52965
  const QA_CHANGELOG = [
52966
+ {
52967
+ version: "0.15.2",
52968
+ date: "2026-10-07",
52969
+ sections: [
52970
+ {
52971
+ title: "Исправления",
52972
+ items: ["Кнопка «Копировать» работает на стенде, открытом по http по адресу в локальной сети. Браузер доверяет буфер обмена только защищённым страницам, поэтому на таком стенде современного интерфейса буфера нет вовсе — и нажатие молчало: ни ошибки, ни отметки «Скопировано», ни текста в буфере, а проверка вставлялась пустой. Теперь поверхность сначала пробует буфер, а когда его нет, копирует через временное поле — этот путь доступен и на незащищённой странице. Так копируются код-блок в ответе, ответ целиком и значение интеграционного токена. Если браузер не отдал буфер ни одним путём, на месте кнопки появляется строка «Скопируйте вручную», а там, где кнопка осталась рабочей, та же строка встаёт рядом с ней после отказа: молчание больше не выглядит удачным копированием."]
52973
+ },
52974
+ {
52975
+ title: "Изменения",
52976
+ items: [
52977
+ "Ряд плагина на странице Plugins называется словами, а не спецификатором пакета. Хост берёт имя и описание ряда из `locale/en.json` — этот файл читается без активации плагина, — и без него подписывает ряд полным именем пакета: вместо «QA Surface» оператор читал `@yadsh/dsh-qa-surface`. То же предложение, что описывает ряд, карточка отвечает и как однострочник на случай, когда описания у ряда нет, — проверка сверяет пару с самим файлом, а не с копией строки в тесте, поэтому ряд не сможет описать одну страницу, открыв другую. Так названы ряды всех плагинов набора.",
52978
+ "Страница настроек Prompt Firewall переехала на панель Plugins: её ряд открывает секцию конфигурации, и карточка больше не рисует свою рамку, заголовок и стрелку — их рисует страница. Раздел, который недоступен этому браузеру, отвечает предложением, а не пустой секцией. Страница Domain Experts села на ряд самого пакета и сняла собственный заголовок: панель уже подписывает пакет его именем и описанием.",
52979
+ "Блоки отказа и опасные действия снова окрашены. Стили ссылались на имена токенов, которых у темы хоста нет (`--dsw-alias-bg-error`, `--dsw-alias-label-error`), а неизвестный `var(--dsw-…)` отбрасывает всю декларацию молча: фон становился прозрачным, цвет — унаследованным, и отказ на вкладке выглядел обычным мелким текстом. Теперь цвет берётся из `--dsw-alias-state-error-primary` с фолбэком, а каждая подстановка `--dsw-*` сверяется с самой темой (`pnpm verify:tokens`), поэтому список несуществующих имён не вырастет."
52980
+ ]
52981
+ },
52982
+ {
52983
+ title: "Исправления",
52984
+ items: ["Отказ открыть чат называет причину. Дочерняя сессия субагента — кусок работы, делегированный из другого разговора, — раньше отвергалась теми же тремя словами, что и чат, за которым стенд не смог собрать агента: в консоли читалась подсказка про пресет, который никто не менял, а в журнале стенда на каждое обращение к чату падали две строки уровня ERROR, из-за чего день одного стенда выглядел как двенадцать одинаковых аварий. У этих двух случаев теперь разные лица: дочерняя сессия называется дочерней, и её строка идёт предупреждением — стенд ответил правильно про чужой разговор, и чинить нечего; чат же без агента остаётся ошибкой и в подсказке называет, куда смотреть и что делать (перемонтировать записанный пресет и открыть чат заново), а читателю тем временем остаётся переписка только для чтения и «Новый чат». Панель субагента перестала спрашивать у стенда источники дочерней сессии, которые тот по правилам не выдаёт: доказательства делегированного хода и так приходят в родительский чат, а экран читает их из самого транскрипта."]
52985
+ },
52986
+ {
52987
+ title: "Исправления",
52988
+ items: ["История чатов больше не наполняется пустыми. Вход на поверхность создавал разговор раньше, чем в него что-нибудь написали, и тот оставался в сайдбаре строкой «Новый чат»: шесть заходов одним и тем же аккаунтом — шесть одинаковых строк, а поиск по истории выдавал их вместо разговоров. Теперь разговор появляется вместе с первым отправленным сообщением: зашли, посмотрели, ушли — в истории ничего, и на стенде не осталось сессии, в которую никто не написал. Кнопка «Новый чат» пустой разговор не создавала и раньше; теперь так же ведёт себя и загрузка страницы.", "Созданный по просьбе документ приходит файлом, а не строкой пути. Ответ на «сделай docx» был текстом: карточки под ответом не было, вкладка «Файлы» стояла с «нет вложений», а в тексте печатался путь контейнера вместе с идентификатором аккаунта — раскладка сервера в сообщении, которое на этом стенде может быть видно другим аккаунтом. Теперь под ответом лежит карточка созданного файла с предпросмотром и скачиванием, она же появляется в списке вложений чата, и карточка видна независимо от того, скрыта ли в настройках активность инструментов: файл, который попросили, — это не шум инструментов. Плагины документов сообщают путь файла внутри рабочего каталога чата (тем же написанием, которым этот файл потом снова открывается), а если абсолютный путь всё же попадает в текст ответа — из старого, уже сохранённого хода или собранный моделью из известного ей рабочего каталога, — интерфейс вычитает из него каталог аккаунта, и остаётся имя файла."]
52989
+ }
52990
+ ]
52991
+ },
52992
+ {
52993
+ version: "0.15.1",
52994
+ date: "2026-10-05",
52995
+ sections: [{
52996
+ title: "Исправления",
52997
+ items: ["Интеграционный API описывает бюджет ответа как настройку развёртывания, а не как фиксированные 90 секунд. Пример в README показывал `requestTimeoutMs: 90000` без объяснения, что это число решает: за бюджетом эндпоинт отвечает `200` с `escalate: true` и `chat_id` — так тикетный мост передаёт вопрос человеку, а не долбит повторами ход, который ещё идёт. Развёртывание, которое поднимает бюджет, меняет и момент этой передачи, поэтому мост, прочитавший пример как контракт, ждал бы не столько, сколько надо. Теперь ссылка ведёт на раздел, объясняющий эскалацию, и назван путь опроса `GET {basePath}/session` по возвращённому `chat_id` — для моста, который не может держать один запрос открытым весь бюджет.", "Вопрос, от которого ушёл тот, кто его задал, больше не продолжает отвечаться. Раньше обрыв клиента только прекращал ожидание: ход оставался запущенным, модель и инструменты доделывали то, чего уже никто не ждал, а следующий вопрос в этот же чат ставился в очередь за ним и сам упирался в полный бюджет. Теперь чужой отказ остановлен: как только звонящий отключился и журнал говорит, что именно этот запрос ещё идёт, ход этого чата отменяется — переписка и поставленные в очередь вопросы других не теряются. Истёкший бюджет по-прежнему означает эскалацию, а не отмену: мост заберёт ответ опросом по `chat_id`."]
52998
+ }]
52999
+ },
52629
53000
  {
52630
53001
  version: "0.15.0",
52631
53002
  date: "2026-10-05",
@@ -53142,7 +53513,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
53142
53513
  className: "dsh-qa-changelog__date",
53143
53514
  children: entry.date
53144
53515
  }),
53145
- entry.version === "0.15.0" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
53516
+ entry.version === "0.15.2" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
53146
53517
  className: "dsh-qa-changelog__current",
53147
53518
  children: "текущая"
53148
53519
  }) : null
@@ -53977,14 +54348,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
53977
54348
  /** Total attachment count across groups; the header badge's value. */
53978
54349
  function countChatAttachments(groups) {
53979
54350
  let total = 0;
53980
- for (const group of groups) total += group.files.length + group.images.length;
54351
+ for (const group of groups) total += group.files.length + group.images.length + (group.artifacts?.length ?? 0);
53981
54352
  return total;
53982
54353
  }
53983
54354
  /**
53984
- * Group the attachments of a chat by their sending message. Only user
53985
- * messages can carry attachments; messages without any are dropped. The
53986
- * groups come out newest message first, matching a roster that grows at the
53987
- * bottom of the transcript.
54355
+ * Group the attachments of a chat by their message. A user message carries what
54356
+ * was sent; an assistant message carries what its turn produced, so a document
54357
+ * the stand made is listed beside the answer that made it instead of only on
54358
+ * disk. Messages with neither are dropped. The groups come out newest first,
54359
+ * matching a roster that grows at the bottom of the transcript.
53988
54360
  * @param messages - the chat's projected transcript.
53989
54361
  * @returns attachment groups, newest message first.
53990
54362
  */
@@ -53992,20 +54364,53 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
53992
54364
  const groups = [];
53993
54365
  for (let index = messages.length - 1; index >= 0; index -= 1) {
53994
54366
  const message = messages[index];
53995
- if (message === void 0 || message.role !== "user") continue;
53996
- const files = message.files ?? [];
53997
- const images = message.images ?? [];
53998
- if (files.length === 0 && images.length === 0) continue;
54367
+ if (message === void 0) continue;
54368
+ const files = message.role === "user" ? message.files ?? [] : [];
54369
+ const images = message.role === "user" ? message.images ?? [] : [];
54370
+ const artifacts = message.role === "assistant" ? message.artifacts ?? [] : [];
54371
+ if (files.length === 0 && images.length === 0 && artifacts.length === 0) continue;
54372
+ const timestamp = message.role === "work" ? void 0 : message.timestamp;
53999
54373
  groups.push({
54000
54374
  messageId: message.id,
54001
- ...message.timestamp === void 0 ? {} : { timestamp: message.timestamp },
54375
+ ...timestamp === void 0 ? {} : { timestamp },
54002
54376
  files,
54003
- images
54377
+ images,
54378
+ ...artifacts.length === 0 ? {} : { artifacts }
54004
54379
  });
54005
54380
  }
54006
54381
  return groups;
54007
54382
  }
54008
54383
  //#endregion
54384
+ //#region src/client/workspace-download.ts
54385
+ /**
54386
+ * Handing one file of the chat's workspace to the person the turn produced it
54387
+ * for.
54388
+ *
54389
+ * A produced document has no attachment-store handle, so the download is the
54390
+ * same read the files panel does: the Host returns the bytes under the chat's
54391
+ * own fence and the browser's download machinery takes it from there. Shared by
54392
+ * the panel's own viewer and the card under an answer, so the two can never
54393
+ * disagree on what "download this file" means.
54394
+ */
54395
+ /** Last segment of a workspace path, spelled with either separator. */
54396
+ function fileNameOf(path) {
54397
+ const segments = path.split(/[/\\]/u).filter((segment) => segment !== "");
54398
+ return segments[segments.length - 1] ?? path;
54399
+ }
54400
+ /** Save one workspace file under its own name. */
54401
+ function downloadWorkspaceFile(file) {
54402
+ const bytes = file.base64 === void 0 ? new TextEncoder().encode(file.text ?? "") : base64ToBytes(file.base64);
54403
+ const url = URL.createObjectURL(new Blob([bytes], { type: file.mime }));
54404
+ const anchor = document.createElement("a");
54405
+ anchor.href = url;
54406
+ anchor.download = fileNameOf(file.path);
54407
+ anchor.rel = "noreferrer";
54408
+ document.body.append(anchor);
54409
+ anchor.click();
54410
+ anchor.remove();
54411
+ URL.revokeObjectURL(url);
54412
+ }
54413
+ //#endregion
54009
54414
  //#region src/shared/documents.ts
54010
54415
  /**
54011
54416
  * Formats the files panel previews by conversion instead of by bytes.
@@ -54049,11 +54454,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
54049
54454
  }
54050
54455
  //#endregion
54051
54456
  //#region src/client/components/QaWorkspaceBrowser.tsx
54052
- /** Name of one entry: the last path segment, root spelled as the directory. */
54053
- function entryName(path) {
54054
- const parts = path.split("/");
54055
- return parts[parts.length - 1] ?? path;
54056
- }
54057
54457
  /** Path join for the panel's own navigation, never leaving the chat root. */
54058
54458
  function joinPath(directory, name) {
54059
54459
  return directory === "" ? name : `${directory}/${name}`;
@@ -54075,19 +54475,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
54075
54475
  }
54076
54476
  return crumbs;
54077
54477
  }
54078
- /** Hand one file to the browser's download machinery. */
54079
- function downloadFile(file) {
54080
- const bytes = file.base64 === void 0 ? new TextEncoder().encode(file.text ?? "") : base64ToBytes(file.base64);
54081
- const url = URL.createObjectURL(new Blob([bytes], { type: file.mime }));
54082
- const anchor = document.createElement("a");
54083
- anchor.href = url;
54084
- anchor.download = entryName(file.path);
54085
- anchor.rel = "noreferrer";
54086
- document.body.append(anchor);
54087
- anchor.click();
54088
- anchor.remove();
54089
- URL.revokeObjectURL(url);
54090
- }
54091
54478
  /** One row of the listing: a directory to descend into or a file to open. */
54092
54479
  function EntryRow({ entry, directory, onOpenFile, onOpenDirectory }) {
54093
54480
  const path = joinPath(directory, entry.name);
@@ -54152,13 +54539,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
54152
54539
  if (file.mime.startsWith("image/") && file.base64 !== void 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
54153
54540
  className: "dsh-qa-ws__image",
54154
54541
  "data-testid": "qa-surface-workspace-image",
54155
- alt: entryName(file.path),
54542
+ alt: fileNameOf(file.path),
54156
54543
  src: `data:${file.mime};base64,${file.base64}`
54157
54544
  });
54158
54545
  if (pdf !== null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("iframe", {
54159
54546
  className: "dsh-qa-ws__pdf",
54160
54547
  "data-testid": "qa-surface-workspace-pdf",
54161
- title: entryName(file.path),
54548
+ title: fileNameOf(file.path),
54162
54549
  src: pdf
54163
54550
  });
54164
54551
  if (converting) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
@@ -54214,7 +54601,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
54214
54601
  className: "dsh-qa-ws__preview-name",
54215
54602
  "data-testid": "qa-surface-workspace-preview-name",
54216
54603
  title: file.path,
54217
- children: entryName(file.path)
54604
+ children: fileNameOf(file.path)
54218
54605
  }),
54219
54606
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
54220
54607
  type: "button",
@@ -54233,7 +54620,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
54233
54620
  type: "button",
54234
54621
  className: "dsh-qa-ws__download",
54235
54622
  "data-testid": "qa-surface-workspace-download",
54236
- onClick: () => downloadFile(file),
54623
+ onClick: () => downloadWorkspaceFile(file),
54237
54624
  children: "Скачать"
54238
54625
  })
54239
54626
  ]
@@ -54269,7 +54656,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
54269
54656
  * the visitor out of the chat's tree. Opening a file reads exactly that file —
54270
54657
  * text inline, anything else as a downloadable handle.
54271
54658
  */
54272
- function QaWorkspaceBrowser({ sessionId, api }) {
54659
+ function QaWorkspaceBrowser({ sessionId, api, initialFile }) {
54273
54660
  const [directory, setDirectory] = (0, react.useState)("");
54274
54661
  const [entries, setEntries] = (0, react.useState)([]);
54275
54662
  const [truncated, setTruncated] = (0, react.useState)(false);
@@ -54365,6 +54752,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
54365
54752
  convert,
54366
54753
  sessionId
54367
54754
  ]);
54755
+ const arrivalOpened = (0, react.useRef)(false);
54756
+ (0, react.useEffect)(() => {
54757
+ if (arrivalOpened.current) return;
54758
+ if (initialFile === void 0 || initialFile === "") return;
54759
+ arrivalOpened.current = true;
54760
+ openFile(initialFile);
54761
+ }, [initialFile, openFile]);
54368
54762
  if (opened !== null) {
54369
54763
  const closeFile = () => {
54370
54764
  setOpened(null);
@@ -54387,7 +54781,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
54387
54781
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QaModal, {
54388
54782
  open: expanded && file !== void 0,
54389
54783
  size: "document",
54390
- title: file === void 0 ? "" : entryName(file.path),
54784
+ title: file === void 0 ? "" : fileNameOf(file.path),
54391
54785
  closeLabel: "Закрыть файл",
54392
54786
  onClose: () => setExpanded(false),
54393
54787
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -54507,14 +54901,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
54507
54901
  * back, and the images are the same object URLs the message showed — while the
54508
54902
  * workspace section reads through the Host's fenced listing and file reads.
54509
54903
  */
54510
- function QaFilesPanel({ groups, resolveImage, onJumpToMessage, sessionId, api }) {
54904
+ function QaFilesPanel({ groups, resolveImage, onJumpToMessage, onArtifactOpen, onArtifactDownload, openArtifact, sessionId, api }) {
54511
54905
  const workspace = sessionId === void 0 || api === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
54512
54906
  className: "dsh-qa-files__workspace",
54513
54907
  "data-testid": "qa-files-workspace",
54514
54908
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Рабочий каталог" }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QaWorkspaceBrowser, {
54515
54909
  sessionId,
54516
- api
54517
- })]
54910
+ api,
54911
+ initialFile: openArtifact?.path
54912
+ }, openArtifact?.stamp ?? "browse")]
54518
54913
  });
54519
54914
  if (groups.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
54520
54915
  className: "dsh-qa-files",
@@ -54551,14 +54946,24 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
54551
54946
  })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
54552
54947
  className: "dsh-qa-files__items",
54553
54948
  "data-testid": "qa-files-group-items",
54554
- children: [group.files.map((file) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QaFileAttachment, {
54555
- name: file.name,
54556
- bytes: file.bytes,
54557
- tone: "sent"
54558
- }, file.attachmentId)), group.images.map((image) => resolveImage === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QaFileImage, {
54559
- image,
54560
- resolve: resolveImage
54561
- }, image.attachmentId))]
54949
+ children: [
54950
+ group.files.map((file) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QaFileAttachment, {
54951
+ name: file.name,
54952
+ bytes: file.bytes,
54953
+ tone: "sent"
54954
+ }, file.attachmentId)),
54955
+ group.images.map((image) => resolveImage === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QaFileImage, {
54956
+ image,
54957
+ resolve: resolveImage
54958
+ }, image.attachmentId)),
54959
+ (group.artifacts ?? []).map((artifact) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QaFileAttachment, {
54960
+ name: artifact.name,
54961
+ bytes: artifact.bytes,
54962
+ tone: "sent",
54963
+ onOpen: onArtifactOpen === void 0 ? void 0 : () => onArtifactOpen(artifact),
54964
+ onDownload: onArtifactDownload === void 0 ? void 0 : () => onArtifactDownload(artifact)
54965
+ }, artifact.path))
54966
+ ]
54562
54967
  })]
54563
54968
  }, group.messageId);
54564
54969
  })]
@@ -56164,7 +56569,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
56164
56569
  const [scopes, setScopes] = (0, react.useState)(QA_SERVICE_TOKEN_DEFAULT_SCOPES);
56165
56570
  const [busy, setBusy] = (0, react.useState)(false);
56166
56571
  const [issued, setIssued] = (0, react.useState)(null);
56167
- const [copied, setCopied] = (0, react.useState)(false);
56572
+ const { copied, impossible: copyImpossible, refused: copyRefused, copy: copySecret } = useCopyAction(issued?.token ?? "");
56168
56573
  const [pendingRevoke, setPendingRevoke] = (0, react.useState)(null);
56169
56574
  const [revoking, setRevoking] = (0, react.useState)(null);
56170
56575
  (0, react.useEffect)(() => {
@@ -56196,7 +56601,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
56196
56601
  if (busy) return;
56197
56602
  setBusy(true);
56198
56603
  setError(null);
56199
- setCopied(false);
56200
56604
  props.api.create({
56201
56605
  label: label.trim(),
56202
56606
  scopes: [...scopes],
@@ -56229,11 +56633,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
56229
56633
  await reload();
56230
56634
  });
56231
56635
  };
56232
- const copySecret = (secret) => {
56233
- const clipboard = navigator.clipboard;
56234
- if (clipboard === void 0) return;
56235
- clipboard.writeText(secret).then(() => setCopied(true), () => void 0);
56236
- };
56237
56636
  const now = Date.now();
56238
56637
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
56239
56638
  className: "dsh-qa-settings__page",
@@ -56269,18 +56668,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
56269
56668
  onFocus: (event) => event.currentTarget.select()
56270
56669
  })
56271
56670
  }),
56272
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(QaSettingsActions, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(QaSettingsButton, {
56671
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(QaSettingsActions, { children: [copyImpossible ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopyHint, { testId: "qa-settings-tokens-copy-hint" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(QaSettingsButton, {
56273
56672
  testId: "qa-settings-tokens-copy",
56274
56673
  label: copied ? "Скопировано" : "Скопировать",
56275
- onClick: () => copySecret(issued.token)
56276
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QaSettingsButton, {
56674
+ onClick: copySecret
56675
+ }), copyRefused ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CopyHint, { testId: "qa-settings-tokens-copy-hint" }) : null] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QaSettingsButton, {
56277
56676
  testId: "qa-settings-tokens-ack",
56278
56677
  tone: "primary",
56279
56678
  label: "Я сохранил токен",
56280
- onClick: () => {
56281
- setIssued(null);
56282
- setCopied(false);
56283
- }
56679
+ onClick: () => setIssued(null)
56284
56680
  })] })
56285
56681
  ]
56286
56682
  }),
@@ -56609,11 +57005,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
56609
57005
  const [busy, setBusy] = (0, react.useState)(false);
56610
57006
  const [error, setError] = (0, react.useState)(null);
56611
57007
  const [saved, setSaved] = (0, react.useState)(false);
57008
+ const [attempted, setAttempted] = (0, react.useState)(false);
56612
57009
  const tooShort = next !== "" && next.length < MIN_PASSWORD_LENGTH$1;
56613
57010
  const mismatch = repeat !== "" && repeat !== next;
56614
57011
  const incomplete = current === "" || next === "" || repeat === "";
56615
57012
  const submit = (event) => {
56616
57013
  event.preventDefault();
57014
+ setAttempted(true);
56617
57015
  if (busy || incomplete || mismatch || tooShort) return;
56618
57016
  setBusy(true);
56619
57017
  setError(null);
@@ -56627,12 +57025,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
56627
57025
  setCurrent("");
56628
57026
  setNext("");
56629
57027
  setRepeat("");
57028
+ setAttempted(false);
56630
57029
  setSaved(true);
56631
57030
  });
56632
57031
  };
56633
57032
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
56634
57033
  className: "dsh-qa-settings__page",
56635
57034
  "data-testid": "qa-settings-password",
57035
+ noValidate: true,
56636
57036
  onSubmit: submit,
56637
57037
  children: [
56638
57038
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
@@ -56686,6 +57086,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
56686
57086
  onChange: (event) => setRepeat(event.currentTarget.value)
56687
57087
  })
56688
57088
  }),
57089
+ attempted && incomplete ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
57090
+ className: "dsh-qa-settings__field-hint",
57091
+ "data-testid": "qa-settings-password-incomplete",
57092
+ children: "Заполните все поля."
57093
+ }) : null,
56689
57094
  tooShort ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
56690
57095
  className: "dsh-qa-settings__field-hint",
56691
57096
  "data-testid": "qa-settings-password-too-short",
@@ -61307,7 +61712,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
61307
61712
  //#endregion
61308
61713
  //#region src/client/admin/pages/Users.tsx
61309
61714
  const PAGE_SIZE = 25;
61310
- /** The Host's floor; the form refuses a shorter password before the round trip. */
61715
+ /** The Host's floor; the queue answers a shorter password before the round trip. */
61311
61716
  const MIN_PASSWORD_LENGTH = 8;
61312
61717
  /**
61313
61718
  * The forgotten-password queue: an account asked for a reset from the sign-in
@@ -61421,7 +61826,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
61421
61826
  type: "password",
61422
61827
  "data-testid": "qa-admin-password-reset-input",
61423
61828
  autoComplete: "new-password",
61424
- minLength: MIN_PASSWORD_LENGTH,
61425
61829
  placeholder: "не короче 8 символов",
61426
61830
  disabled: busy === request.userId,
61427
61831
  value: drafts[request.userId] ?? "",
@@ -63763,7 +64167,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
63763
64167
  const listState = (0, react.useSyncExternalStore)(props.sessions.list.subscribe, props.sessions.list.getSnapshot, props.sessions.list.getSnapshot);
63764
64168
  const ui = useSessionUiState(state.chatKey);
63765
64169
  const rail = useRightRail(ui);
63766
- const { activeTurn, setActiveTurn, variantOffsets, setVariantOffsets, agentsOpen, setAgentsOpen, pendingAttachments, setPendingAttachments } = ui;
64170
+ const { activeTurn, setActiveTurn, variantOffsets, setVariantOffsets, agentsOpen, setAgentsOpen, setRailOpen, setRailTab, pendingAttachments, setPendingAttachments } = ui;
64171
+ /**
64172
+ * The file a card asked to open, with the chat it was asked in and the stamp
64173
+ * of the request. The chat is part of the arrival because the rail survives
64174
+ * switching chats: an arrival from the previous chat must not open its file
64175
+ * in this one. The stamp makes a second click on the same card a fresh
64176
+ * arrival rather than a path that never changed.
64177
+ */
64178
+ const [artifactArrival, setArtifactArrival] = (0, react.useState)(null);
64179
+ /** Counts the open requests, so the same file twice is two arrivals. */
64180
+ const artifactStamp = (0, react.useRef)(0);
63767
64181
  (0, react.useEffect)(() => {
63768
64182
  if (!route.active) return;
63769
64183
  const frame = requestAnimationFrame(() => {
@@ -63845,6 +64259,50 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
63845
64259
  if (element === null || !scrollToTranscriptAnchor(element, messageId)) return;
63846
64260
  nearBottom.current = isNearBottom(element);
63847
64261
  }, []);
64262
+ /**
64263
+ * A produced file the reader wants to see: open the rail on its workspace
64264
+ * viewer at exactly that file. The viewer is the panel's own — it reads under
64265
+ * the chat's fence and converts a Word document into a preview — so a card
64266
+ * under an answer points at that work instead of carrying a second viewer
64267
+ * that could disagree with it.
64268
+ */
64269
+ const handleArtifactOpen = (0, react.useCallback)((artifact) => {
64270
+ artifactStamp.current += 1;
64271
+ setArtifactArrival({
64272
+ path: artifact.path,
64273
+ chatKey: state.chatKey,
64274
+ stamp: artifactStamp.current
64275
+ });
64276
+ setAgentsOpen(false);
64277
+ setRailOpen(true);
64278
+ setRailTab("files");
64279
+ }, [
64280
+ setAgentsOpen,
64281
+ setRailOpen,
64282
+ setRailTab,
64283
+ state.chatKey
64284
+ ]);
64285
+ /**
64286
+ * A produced file the reader wants to keep: the same fenced read the panel
64287
+ * uses, handed to the browser's download machinery. A read that fails opens
64288
+ * the viewer instead, which reports the refusal rather than swallowing the
64289
+ * click.
64290
+ */
64291
+ const handleArtifactDownload = (0, react.useCallback)((artifact) => {
64292
+ const sessionId = state.sessionId;
64293
+ if (sessionId === null) return;
64294
+ boundSourceApi.readWorkspaceFile(sessionId, artifact.path).then((result) => {
64295
+ if (result.ok) {
64296
+ downloadWorkspaceFile(result.value);
64297
+ return;
64298
+ }
64299
+ handleArtifactOpen(artifact);
64300
+ }, () => handleArtifactOpen(artifact));
64301
+ }, [
64302
+ boundSourceApi,
64303
+ handleArtifactOpen,
64304
+ state.sessionId
64305
+ ]);
63848
64306
  const view = useTranscriptView(state.messages, variantOffsets);
63849
64307
  const railItems = view.railItems;
63850
64308
  (0, react.useLayoutEffect)(() => {
@@ -63877,6 +64335,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
63877
64335
  const fileGroups = (0, react.useMemo)(() => collectChatFiles(state.messages), [state.messages]);
63878
64336
  const attachmentCount = (0, react.useMemo)(() => countChatAttachments(fileGroups), [fileGroups]);
63879
64337
  const filesEnabled = attachmentCount > 0 || state.sessionId !== null && config.sources.filePreview.enabled;
64338
+ const workspaceReadable = state.sessionId !== null && config.sources.filePreview.enabled;
63880
64339
  const quickQuestions = (0, react.useMemo)(() => effectiveQuickQuestions(config.accounts.starters.enabled && accountsSnapshot.stage === "authed" ? accountsSnapshot.user.starters : void 0, config.suggestedQuestions), [config, accountsSnapshot]);
63881
64340
  const ownerNames = (0, react.useMemo)(() => config.accounts.enabled && config.accounts.showOtherUsersChats && accounts !== void 0 && accountsSnapshot.stage === "authed" && accountsSnapshot.user.role === "admin" ? accounts.ownerNames() : void 0, [
63882
64341
  config,
@@ -64034,6 +64493,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
64034
64493
  groups: fileGroups,
64035
64494
  resolveImage,
64036
64495
  onJumpToMessage: handleJumpToMessage,
64496
+ onArtifactOpen: workspaceReadable ? handleArtifactOpen : void 0,
64497
+ onArtifactDownload: workspaceReadable ? handleArtifactDownload : void 0,
64498
+ openArtifact: artifactArrival !== null && artifactArrival.chatKey === state.chatKey ? {
64499
+ path: artifactArrival.path,
64500
+ stamp: artifactArrival.stamp
64501
+ } : void 0,
64037
64502
  sessionId: state.sessionId ?? void 0,
64038
64503
  api: boundSourceApi
64039
64504
  })
@@ -64279,6 +64744,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
64279
64744
  onRegenerate: isLast && message.role === "assistant" && message.status === "committed" && controller !== void 0 ? handleRegenerate : void 0,
64280
64745
  onOpenSources: config.sources.enabled && config.sources.display.footer ? rail.openSources : void 0,
64281
64746
  onSourceDetail: config.sources.enabled ? rail.openSourceDetail : void 0,
64747
+ onArtifactOpen: workspaceReadable ? handleArtifactOpen : void 0,
64748
+ onArtifactDownload: workspaceReadable ? handleArtifactDownload : void 0,
64282
64749
  onRateFeedback: rateFeedback
64283
64750
  }), group !== void 0 && group.turns.length > 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(VariantSwitcher, {
64284
64751
  count: group.turns.length,
@@ -64938,8 +65405,8 @@ body[data-dsh-qa-surface="active"] div:has(>[data-shell-overlay])>:not([data-she
64938
65405
  .dsh-qa-settings__button:disabled{opacity:.6;cursor:default}
64939
65406
  .dsh-qa-settings__button--primary{border-color:transparent;background:var(--dsh-qa-brand);color:var(--dsh-qa-accent-contrast);font-weight:600}
64940
65407
  .dsh-qa-settings__button--primary:hover:not(:disabled){background:var(--dsh-qa-accent-hover);color:var(--dsh-qa-accent-contrast)}
64941
- .dsh-qa-settings__button--danger{border-color:var(--dsw-alias-label-error);color:var(--dsw-alias-label-error)}
64942
- .dsh-qa-settings__button--danger:hover:not(:disabled){background:var(--dsw-alias-bg-error);color:var(--dsw-alias-label-error)}
65408
+ .dsh-qa-settings__button--danger{border-color:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-state-error-primary)}
65409
+ .dsh-qa-settings__button--danger:hover:not(:disabled){background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 8%, transparent);color:var(--dsw-alias-state-error-primary)}
64943
65410
  .dsh-qa-settings__empty{display:flex;flex-direction:column;align-items:flex-start;gap:10px;padding:22px 18px;border:1px dashed var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-3)}
64944
65411
  .dsh-qa-settings__empty-title{margin:0;color:var(--dsw-alias-label-primary);font-size:14px;font-weight:600;line-height:20px}
64945
65412
  .dsh-qa-settings__search{appearance:none;width:100%;padding:8px 11px;border:1px solid var(--dsw-alias-border-l2);border-radius:9px;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:19px}
@@ -65381,11 +65848,12 @@ a.dsh-qa-srcref:hover,button.dsh-qa-srcref:hover{border-color:var(--dsw-alias-la
65381
65848
  .dsh-qa-md-image{display:block;max-width:100%;height:auto;margin:0;border-radius:8px}
65382
65849
  .dsh-qa-md-image-alt{color:var(--dsw-alias-label-tertiary);font-style:italic}
65383
65850
  .dsh-qa-md-code{margin:16px 0;border-radius:12px;background:var(--dsw-alias-markdown-code-block,var(--dsw-alias-bg-layer-2));color:var(--dsw-alias-label-primary)}
65384
- .dsh-qa-md-code__banner{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:9px 14px;border-radius:12px 12px 0 0;background:var(--dsw-alias-markdown-code-block-banner,var(--dsw-alias-bg-layer-3))}
65385
- .dsh-qa-md-code__lang{min-width:0;overflow:hidden;color:var(--dsw-alias-label-primary);font-family:var(--ds-font-family-code,ui-monospace,SFMono-Regular,Consolas,monospace);font-size:11px;line-height:18px;text-overflow:ellipsis;white-space:nowrap}
65851
+ .dsh-qa-md-code__banner{display:flex;align-items:center;justify-content:flex-end;gap:12px;padding:9px 14px;border-radius:12px 12px 0 0;background:var(--dsw-alias-markdown-code-block-banner,var(--dsw-alias-bg-layer-3))}
65852
+ .dsh-qa-md-code__lang{flex:none;min-width:0;margin-right:auto;overflow:hidden;color:var(--dsw-alias-label-primary);font-family:var(--ds-font-family-code,ui-monospace,SFMono-Regular,Consolas,monospace);font-size:11px;line-height:18px;text-overflow:ellipsis;white-space:nowrap}
65386
65853
  .dsh-qa-md-code__copy{appearance:none;flex:none;padding:0;border:0;background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-size:11px;line-height:18px;cursor:pointer}
65387
65854
  .dsh-qa-md-code__copy:hover{color:var(--dsw-alias-label-primary)}
65388
65855
  .dsh-qa-md-code__copy:focus-visible{outline:var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color, var(--dsh-qa-accent));outline-offset:2px}
65856
+ .dsh-qa-copy-hint{flex:none;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:18px;white-space:nowrap}
65389
65857
  .dsh-qa-md-code pre{overflow-x:auto;margin:0;padding:16px;border:0;border-radius:0 0 12px 12px;background:transparent;font-family:var(--ds-font-family-code,ui-monospace,SFMono-Regular,Consolas,monospace);font-size:var(--dsw-font-markdown-code-block-font-size,11px);line-height:var(--dsw-font-markdown-code-block-line-height,19px);white-space:pre-wrap;word-break:break-word}
65390
65858
  .dsh-qa-md-code pre code{display:block;padding:0;border:0;background:transparent;font:inherit}
65391
65859
  .dsh-qa-md-tok[data-tok="comment"]{color:var(--shiki-token-comment)}
@@ -65565,6 +66033,12 @@ a.dsh-qa-srcref:hover,button.dsh-qa-srcref:hover{border-color:var(--dsw-alias-la
65565
66033
  .dsh-qa-file button:hover{color:var(--dsh-qa-error)}
65566
66034
  .dsh-qa-file button:focus-visible{outline:var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color, var(--dsh-qa-accent));outline-offset:1px}
65567
66035
  .dsh-qa-file button svg{width:11px;height:11px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round}
66036
+ .dsh-qa-file .dsh-qa-file__open{display:flex;align-items:center;gap:10px;flex:1;min-width:0;width:auto;height:auto;padding:0;border-radius:6px;background:0 0;color:inherit;text-align:left;place-items:stretch;cursor:pointer}
66037
+ .dsh-qa-file .dsh-qa-file__open:hover{background:0 0;color:inherit}
66038
+ .dsh-qa-file .dsh-qa-file__open:hover .dsh-qa-file__name{color:var(--dsw-alias-brand-primary)}
66039
+ .dsh-qa-file .dsh-qa-file__download{width:auto;height:auto;padding:2px 8px;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-secondary);font-size:11px}
66040
+ .dsh-qa-file .dsh-qa-file__download:hover{background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}
66041
+ .dsh-qa-message__artifacts{display:flex;flex-wrap:wrap;gap:8px;margin:10px 0 0}
65568
66042
  .dsh-qa-composer__image{position:relative;display:block;width:64px;height:64px;flex:none}
65569
66043
  .dsh-qa-composer__image img{display:block;width:100%;height:100%;object-fit:cover;border-radius:10px;border:1px solid var(--dsw-alias-border-l2)}
65570
66044
  .dsh-qa-composer__image button{position:absolute;top:-6px;right:-6px;display:grid;place-items:center;width:18px;height:18px;padding:0;border:0;border-radius:999px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);cursor:pointer;box-shadow:var(--dsw-shadow-lv2)}
@@ -68451,7 +68925,7 @@ textarea.qa-card-control{height:auto;padding:8px 12px;line-height:1.5;resize:ver
68451
68925
  .qa-card-chip.off{color:var(--dsw-alias-label-tertiary)}
68452
68926
  .qa-card-notice{padding:9px 11px;border-radius:8px;background:var(--dsw-alias-bg-module-platform);border:1px solid var(--dsw-alias-border-l2);font-size:11px;line-height:1.5;color:var(--dsw-alias-label-secondary)}
68453
68927
  .qa-card-notice.warn{border-color:var(--dsw-alias-label-dimmed);color:var(--dsw-alias-label-primary)}
68454
- .qa-card-error{padding:9px 11px;border-radius:8px;background:var(--dsw-alias-bg-error);color:var(--dsw-alias-label-error);font-size:11px;line-height:1.5}
68928
+ .qa-card-error{padding:9px 11px;border-radius:8px;background:color-mix(in srgb, var(--dsw-alias-state-error-primary, #b3261e) 8%, transparent);color:var(--dsw-alias-state-error-primary, #b3261e);font-size:11px;line-height:1.5}
68455
68929
  .qa-card-advanced{border:1px solid var(--dsw-alias-border-l2);border-radius:10px;padding:0 12px}
68456
68930
  .qa-card-advanced summary{cursor:pointer;padding:11px 0;font-size:12px;font-weight:600}
68457
68931
  .qa-card-advanced summary:focus-visible{outline:var(--dsw-focus-ring-width, 2px) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));outline-offset:-1px}