@yadsh/dsh-qa-surface 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +41 -5
  2. package/cordis.patch.yml +8 -0
  3. package/lib/accounts/credentials.js +12 -4
  4. package/lib/accounts/credentials.js.map +1 -1
  5. package/lib/accounts/store.js +16 -1
  6. package/lib/accounts/store.js.map +1 -1
  7. package/lib/approvals.js +179 -0
  8. package/lib/approvals.js.map +1 -0
  9. package/lib/cli.js +10 -0
  10. package/lib/cli.js.map +1 -1
  11. package/lib/client/QaSessionController.js +93 -0
  12. package/lib/client/QaSessionController.js.map +1 -1
  13. package/lib/client/QaSurface.js +14 -1
  14. package/lib/client/QaSurface.js.map +1 -1
  15. package/lib/client/approvals.js +104 -0
  16. package/lib/client/approvals.js.map +1 -0
  17. package/lib/client/attestation.js +3 -0
  18. package/lib/client/attestation.js.map +1 -1
  19. package/lib/client/components/QaApproval.js +23 -0
  20. package/lib/client/components/QaApproval.js.map +1 -0
  21. package/lib/client/components/QaChangelog.js +24 -0
  22. package/lib/client/components/QaChangelog.js.map +1 -1
  23. package/lib/client/components/QaQuestions.js +96 -0
  24. package/lib/client/components/QaQuestions.js.map +1 -0
  25. package/lib/client/index.js +11 -0
  26. package/lib/client/index.js.map +1 -1
  27. package/lib/client/project-session-state.js +5 -3
  28. package/lib/client/project-session-state.js.map +1 -1
  29. package/lib/client/questions.js +114 -0
  30. package/lib/client/questions.js.map +1 -0
  31. package/lib/client/settings/sections/sources.js +2 -0
  32. package/lib/client/settings/sections/sources.js.map +1 -1
  33. package/lib/client/styles.js +42 -0
  34. package/lib/client/styles.js.map +1 -1
  35. package/lib/client/types.js +2 -0
  36. package/lib/client/types.js.map +1 -1
  37. package/lib/client.js +1018 -56
  38. package/lib/client.js.map +1 -1
  39. package/lib/config-resolvers/basics.js +4 -2
  40. package/lib/config-resolvers/basics.js.map +1 -1
  41. package/lib/config-resolvers/defaults.js +4 -0
  42. package/lib/config-resolvers/defaults.js.map +1 -1
  43. package/lib/config-resolvers/lockdown.js +11 -0
  44. package/lib/config-resolvers/lockdown.js.map +1 -1
  45. package/lib/config-resolvers/session.js +2 -2
  46. package/lib/config-resolvers/session.js.map +1 -1
  47. package/lib/config-resolvers/shared.js +9 -0
  48. package/lib/config-resolvers/shared.js.map +1 -1
  49. package/lib/config-resolvers/sources.js +2 -0
  50. package/lib/config-resolvers/sources.js.map +1 -1
  51. package/lib/config.js +11 -2
  52. package/lib/config.js.map +1 -1
  53. package/lib/index.js +76 -6
  54. package/lib/index.js.map +1 -1
  55. package/lib/provenance/host-store.js +25 -9
  56. package/lib/provenance/host-store.js.map +1 -1
  57. package/lib/provenance/reported.js +37 -12
  58. package/lib/provenance/reported.js.map +1 -1
  59. package/lib/questions.js +185 -0
  60. package/lib/questions.js.map +1 -0
  61. package/lib/secure-session.js +69 -17
  62. package/lib/secure-session.js.map +1 -1
  63. package/lib/session-ownership.js +55 -0
  64. package/lib/session-ownership.js.map +1 -0
  65. package/lib/typert.host.js +357 -17
  66. package/lib/typert.remote-client.d.ts +11 -1
  67. package/lib/typert.remote-client.js +287 -14
  68. package/lib/types/accounts/credentials.d.ts +6 -0
  69. package/lib/types/accounts/store.d.ts +8 -0
  70. package/lib/types/approvals.d.ts +65 -0
  71. package/lib/types/client/QaSessionController.d.ts +38 -1
  72. package/lib/types/client/QaSurface.d.ts +8 -1
  73. package/lib/types/client/approvals.d.ts +39 -0
  74. package/lib/types/client/components/QaApproval.d.ts +13 -0
  75. package/lib/types/client/components/QaQuestions.d.ts +13 -0
  76. package/lib/types/client/project-session-state.d.ts +8 -1
  77. package/lib/types/client/questions.d.ts +45 -0
  78. package/lib/types/client/types.d.ts +48 -1
  79. package/lib/types/config-resolvers/shared.d.ts +7 -0
  80. package/lib/types/index.d.ts +29 -3
  81. package/lib/types/provenance/reported.d.ts +9 -1
  82. package/lib/types/questions.d.ts +83 -0
  83. package/lib/types/secure-session.d.ts +21 -2
  84. package/lib/types/session-ownership.d.ts +30 -0
  85. package/lib/types/types.d.ts +80 -4
  86. package/lib/types/user-workspace.d.ts +5 -1
  87. package/lib/user-workspace.js +21 -6
  88. package/lib/user-workspace.js.map +1 -1
  89. package/package.json +3 -3
package/lib/client.js CHANGED
@@ -2736,7 +2736,7 @@ window.__ModuleLoader__.load({
2736
2736
  }
2737
2737
  let handoff;
2738
2738
  const open = [];
2739
- const memo$6 = {
2739
+ const memo$8 = {
2740
2740
  alloc(_inst, payload, empty) {
2741
2741
  const bucket = handoff;
2742
2742
  if (!bucket) return empty;
@@ -2827,7 +2827,7 @@ window.__ModuleLoader__.load({
2827
2827
  };
2828
2828
  /** The memoizer that gives containers cycle support. `zod` installs it by default; `zod/mini` opts in with `config({ memoizer: memoizer() })`. */
2829
2829
  function memoizer() {
2830
- return memo$6;
2830
+ return memo$8;
2831
2831
  }
2832
2832
  /** Whether this value is a node a back-edge resolved to before it finished. */
2833
2833
  function isBackEdge(ctx, value) {
@@ -5300,6 +5300,24 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5300
5300
  }).readonly()
5301
5301
  }).readonly()
5302
5302
  })]);
5303
+ const _yadsh_dsh_qa_surface_qaSurface_answerApproval_parameter_0$schema = string();
5304
+ const _yadsh_dsh_qa_surface_qaSurface_answerApproval_parameter_1$schema = string();
5305
+ const _yadsh_dsh_qa_surface_qaSurface_answerApproval_parameter_2$schema = string();
5306
+ const _yadsh_dsh_qa_surface_qaSurface_answerApproval_parameter_3$schema = union([literal("allowed-once"), literal("rejected")]);
5307
+ const _yadsh_dsh_qa_surface_qaSurface_answerApproval_result$schema = boolean();
5308
+ const _yadsh_dsh_qa_surface_qaSurface_answerQuestion_parameter_0$schema = string();
5309
+ const _yadsh_dsh_qa_surface_qaSurface_answerQuestion_parameter_1$schema = string();
5310
+ const _yadsh_dsh_qa_surface_qaSurface_answerQuestion_parameter_2$schema = string();
5311
+ const _yadsh_dsh_qa_surface_qaSurface_answerQuestion_parameter_3$schema = array(object({
5312
+ "id": string().readonly(),
5313
+ "selected": array(string()).readonly(),
5314
+ "custom": string().readonly().optional()
5315
+ }));
5316
+ const _yadsh_dsh_qa_surface_qaSurface_answerQuestion_result$schema = boolean();
5317
+ const _yadsh_dsh_qa_surface_qaSurface_cancelQuestion_parameter_0$schema = string();
5318
+ const _yadsh_dsh_qa_surface_qaSurface_cancelQuestion_parameter_1$schema = string();
5319
+ const _yadsh_dsh_qa_surface_qaSurface_cancelQuestion_parameter_2$schema = string();
5320
+ const _yadsh_dsh_qa_surface_qaSurface_cancelQuestion_result$schema = boolean();
5303
5321
  const _yadsh_dsh_qa_surface_qaSurface_createSession_parameter_0$schema = string();
5304
5322
  const _yadsh_dsh_qa_surface_qaSurface_createSession_result$schema = string();
5305
5323
  const _yadsh_dsh_qa_surface_qaSurface_describe_result$schema = object({
@@ -5345,8 +5363,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5345
5363
  "suggestedQuestions": array(string()).readonly(),
5346
5364
  "thinkingPhrases": array(string()).readonly(),
5347
5365
  "interaction": object({
5348
- "approvals": literal("blocked").readonly(),
5349
- "questions": literal("unsupported").readonly()
5366
+ "approvals": union([literal("blocked"), literal("interactive")]).readonly(),
5367
+ "questions": union([literal("interactive"), literal("unsupported")]).readonly()
5350
5368
  }).readonly(),
5351
5369
  "lockdown": object({
5352
5370
  "enabled": boolean().readonly(),
@@ -5366,7 +5384,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5366
5384
  "toolPolicy": object({
5367
5385
  "mode": literal("allow-list").readonly(),
5368
5386
  "allow": array(string()).readonly()
5369
- }).readonly()
5387
+ }).readonly(),
5388
+ "sharedReadOnlyRoots": array(string()).readonly()
5370
5389
  }).readonly(),
5371
5390
  "embedding": object({ "frameAncestors": union([literal(null), string()]).readonly() }).readonly(),
5372
5391
  "accounts": object({
@@ -5423,7 +5442,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5423
5442
  "subagents": object({
5424
5443
  "inheritSources": boolean().readonly(),
5425
5444
  "enableReportToolFallback": boolean().readonly(),
5426
- "markIncompleteOpaqueRuns": boolean().readonly()
5445
+ "markIncompleteOpaqueRuns": boolean().readonly(),
5446
+ "validateReportedSources": boolean().readonly()
5427
5447
  }).readonly(),
5428
5448
  "legacy": object({ "parseAssistantSourcesBlock": boolean().readonly() }).readonly()
5429
5449
  }).readonly(),
@@ -5435,6 +5455,34 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5435
5455
  "extensions": array(string()).readonly()
5436
5456
  }).readonly()
5437
5457
  });
5458
+ const _yadsh_dsh_qa_surface_qaSurface_pendingApprovals_parameter_0$schema = string();
5459
+ const _yadsh_dsh_qa_surface_qaSurface_pendingApprovals_parameter_1$schema = string();
5460
+ const _yadsh_dsh_qa_surface_qaSurface_pendingApprovals_result$schema = array(object({
5461
+ "id": string().readonly(),
5462
+ "sessionId": string().readonly(),
5463
+ "toolName": string().readonly(),
5464
+ "reason": union([literal(null), string()]).readonly(),
5465
+ "createdAt": number().readonly(),
5466
+ "delegated": boolean().readonly()
5467
+ }));
5468
+ const _yadsh_dsh_qa_surface_qaSurface_pendingQuestions_parameter_0$schema = string();
5469
+ const _yadsh_dsh_qa_surface_qaSurface_pendingQuestions_parameter_1$schema = string();
5470
+ const _yadsh_dsh_qa_surface_qaSurface_pendingQuestions_result$schema = array(object({
5471
+ "id": string().readonly(),
5472
+ "sessionId": string().readonly(),
5473
+ "questions": array(object({
5474
+ "id": string().readonly(),
5475
+ "question": string().readonly(),
5476
+ "header": union([literal(null), string()]).readonly(),
5477
+ "detail": union([literal(null), string()]).readonly(),
5478
+ "options": array(object({
5479
+ "label": string().readonly(),
5480
+ "description": union([literal(null), string()]).readonly()
5481
+ })).readonly(),
5482
+ "multiSelect": boolean().readonly()
5483
+ })).readonly(),
5484
+ "createdAt": number().readonly()
5485
+ }));
5438
5486
  const _yadsh_dsh_qa_surface_qaSurface_readSourceFile_parameter_0$schema = string();
5439
5487
  const _yadsh_dsh_qa_surface_qaSurface_readSourceFile_parameter_1$schema = string();
5440
5488
  const _yadsh_dsh_qa_surface_qaSurface_readSourceFile_parameter_2$schema = string();
@@ -5614,7 +5662,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5614
5662
  },
5615
5663
  sourceLocation: {
5616
5664
  "file": "packages/dsh-qa-surface/src/index.ts",
5617
- "line": 230,
5665
+ "line": 268,
5618
5666
  "column": 3
5619
5667
  }
5620
5668
  },
@@ -5641,7 +5689,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5641
5689
  },
5642
5690
  sourceLocation: {
5643
5691
  "file": "packages/dsh-qa-surface/src/index.ts",
5644
- "line": 249,
5692
+ "line": 287,
5645
5693
  "column": 3
5646
5694
  }
5647
5695
  },
@@ -5677,7 +5725,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5677
5725
  },
5678
5726
  sourceLocation: {
5679
5727
  "file": "packages/dsh-qa-surface/src/index.ts",
5680
- "line": 218,
5728
+ "line": 256,
5681
5729
  "column": 3
5682
5730
  }
5683
5731
  },
@@ -5704,7 +5752,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5704
5752
  },
5705
5753
  sourceLocation: {
5706
5754
  "file": "packages/dsh-qa-surface/src/index.ts",
5707
- "line": 239,
5755
+ "line": 277,
5708
5756
  "column": 3
5709
5757
  }
5710
5758
  },
@@ -5740,7 +5788,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5740
5788
  },
5741
5789
  sourceLocation: {
5742
5790
  "file": "packages/dsh-qa-surface/src/index.ts",
5743
- "line": 213,
5791
+ "line": 251,
5744
5792
  "column": 3
5745
5793
  }
5746
5794
  },
@@ -5776,7 +5824,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5776
5824
  },
5777
5825
  sourceLocation: {
5778
5826
  "file": "packages/dsh-qa-surface/src/index.ts",
5779
- "line": 262,
5827
+ "line": 300,
5780
5828
  "column": 3
5781
5829
  }
5782
5830
  },
@@ -5803,10 +5851,177 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5803
5851
  },
5804
5852
  sourceLocation: {
5805
5853
  "file": "packages/dsh-qa-surface/src/index.ts",
5806
- "line": 224,
5854
+ "line": 262,
5807
5855
  "column": 3
5808
5856
  }
5809
5857
  },
5858
+ {
5859
+ id: "@yadsh/dsh-qa-surface#qaSurface/answerApproval",
5860
+ service: "qaSurface",
5861
+ namespace: "qaSurface",
5862
+ method: "answerApproval",
5863
+ invocation: { kind: "direct" },
5864
+ parameters: [
5865
+ {
5866
+ name: "token",
5867
+ wire: "token",
5868
+ source: "json",
5869
+ codec: {
5870
+ mode: "strict",
5871
+ typeSymbol: "@yadsh/dsh-qa-surface#qaSurface/answerApproval:token",
5872
+ schema: _yadsh_dsh_qa_surface_qaSurface_answerApproval_parameter_0$schema
5873
+ }
5874
+ },
5875
+ {
5876
+ name: "sessionId",
5877
+ wire: "sessionId",
5878
+ source: "json",
5879
+ codec: {
5880
+ mode: "strict",
5881
+ typeSymbol: "@yadsh/dsh-qa-surface#qaSurface/answerApproval:sessionId",
5882
+ schema: _yadsh_dsh_qa_surface_qaSurface_answerApproval_parameter_1$schema
5883
+ }
5884
+ },
5885
+ {
5886
+ name: "requestId",
5887
+ wire: "requestId",
5888
+ source: "json",
5889
+ codec: {
5890
+ mode: "strict",
5891
+ typeSymbol: "@yadsh/dsh-qa-surface#qaSurface/answerApproval:requestId",
5892
+ schema: _yadsh_dsh_qa_surface_qaSurface_answerApproval_parameter_2$schema
5893
+ }
5894
+ },
5895
+ {
5896
+ name: "decision",
5897
+ wire: "decision",
5898
+ source: "json",
5899
+ codec: {
5900
+ mode: "strict",
5901
+ typeSymbol: "@yadsh/dsh-qa-surface/types#QaApprovalDecision",
5902
+ schema: _yadsh_dsh_qa_surface_qaSurface_answerApproval_parameter_3$schema
5903
+ }
5904
+ }
5905
+ ],
5906
+ result: {
5907
+ mode: "strict",
5908
+ typeSymbol: "@yadsh/dsh-qa-surface#qaSurface/answerApproval:result",
5909
+ schema: _yadsh_dsh_qa_surface_qaSurface_answerApproval_result$schema
5910
+ },
5911
+ sourceLocation: {
5912
+ "file": "packages/dsh-qa-surface/src/index.ts",
5913
+ "line": 455,
5914
+ "column": 9
5915
+ }
5916
+ },
5917
+ {
5918
+ id: "@yadsh/dsh-qa-surface#qaSurface/answerQuestion",
5919
+ service: "qaSurface",
5920
+ namespace: "qaSurface",
5921
+ method: "answerQuestion",
5922
+ invocation: { kind: "direct" },
5923
+ parameters: [
5924
+ {
5925
+ name: "token",
5926
+ wire: "token",
5927
+ source: "json",
5928
+ codec: {
5929
+ mode: "strict",
5930
+ typeSymbol: "@yadsh/dsh-qa-surface#qaSurface/answerQuestion:token",
5931
+ schema: _yadsh_dsh_qa_surface_qaSurface_answerQuestion_parameter_0$schema
5932
+ }
5933
+ },
5934
+ {
5935
+ name: "sessionId",
5936
+ wire: "sessionId",
5937
+ source: "json",
5938
+ codec: {
5939
+ mode: "strict",
5940
+ typeSymbol: "@yadsh/dsh-qa-surface#qaSurface/answerQuestion:sessionId",
5941
+ schema: _yadsh_dsh_qa_surface_qaSurface_answerQuestion_parameter_1$schema
5942
+ }
5943
+ },
5944
+ {
5945
+ name: "requestId",
5946
+ wire: "requestId",
5947
+ source: "json",
5948
+ codec: {
5949
+ mode: "strict",
5950
+ typeSymbol: "@yadsh/dsh-qa-surface#qaSurface/answerQuestion:requestId",
5951
+ schema: _yadsh_dsh_qa_surface_qaSurface_answerQuestion_parameter_2$schema
5952
+ }
5953
+ },
5954
+ {
5955
+ name: "answers",
5956
+ wire: "answers",
5957
+ source: "json",
5958
+ codec: {
5959
+ mode: "strict",
5960
+ typeSymbol: "@yadsh/dsh-qa-surface#qaSurface/answerQuestion:answers",
5961
+ schema: _yadsh_dsh_qa_surface_qaSurface_answerQuestion_parameter_3$schema
5962
+ }
5963
+ }
5964
+ ],
5965
+ result: {
5966
+ mode: "strict",
5967
+ typeSymbol: "@yadsh/dsh-qa-surface#qaSurface/answerQuestion:result",
5968
+ schema: _yadsh_dsh_qa_surface_qaSurface_answerQuestion_result$schema
5969
+ },
5970
+ sourceLocation: {
5971
+ "file": "packages/dsh-qa-surface/src/index.ts",
5972
+ "line": 484,
5973
+ "column": 9
5974
+ }
5975
+ },
5976
+ {
5977
+ id: "@yadsh/dsh-qa-surface#qaSurface/cancelQuestion",
5978
+ service: "qaSurface",
5979
+ namespace: "qaSurface",
5980
+ method: "cancelQuestion",
5981
+ invocation: { kind: "direct" },
5982
+ parameters: [
5983
+ {
5984
+ name: "token",
5985
+ wire: "token",
5986
+ source: "json",
5987
+ codec: {
5988
+ mode: "strict",
5989
+ typeSymbol: "@yadsh/dsh-qa-surface#qaSurface/cancelQuestion:token",
5990
+ schema: _yadsh_dsh_qa_surface_qaSurface_cancelQuestion_parameter_0$schema
5991
+ }
5992
+ },
5993
+ {
5994
+ name: "sessionId",
5995
+ wire: "sessionId",
5996
+ source: "json",
5997
+ codec: {
5998
+ mode: "strict",
5999
+ typeSymbol: "@yadsh/dsh-qa-surface#qaSurface/cancelQuestion:sessionId",
6000
+ schema: _yadsh_dsh_qa_surface_qaSurface_cancelQuestion_parameter_1$schema
6001
+ }
6002
+ },
6003
+ {
6004
+ name: "requestId",
6005
+ wire: "requestId",
6006
+ source: "json",
6007
+ codec: {
6008
+ mode: "strict",
6009
+ typeSymbol: "@yadsh/dsh-qa-surface#qaSurface/cancelQuestion:requestId",
6010
+ schema: _yadsh_dsh_qa_surface_qaSurface_cancelQuestion_parameter_2$schema
6011
+ }
6012
+ }
6013
+ ],
6014
+ result: {
6015
+ mode: "strict",
6016
+ typeSymbol: "@yadsh/dsh-qa-surface#qaSurface/cancelQuestion:result",
6017
+ schema: _yadsh_dsh_qa_surface_qaSurface_cancelQuestion_result$schema
6018
+ },
6019
+ sourceLocation: {
6020
+ "file": "packages/dsh-qa-surface/src/index.ts",
6021
+ "line": 496,
6022
+ "column": 9
6023
+ }
6024
+ },
5810
6025
  {
5811
6026
  id: "@yadsh/dsh-qa-surface#qaSurface/createSession",
5812
6027
  service: "qaSurface",
@@ -5830,7 +6045,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5830
6045
  },
5831
6046
  sourceLocation: {
5832
6047
  "file": "packages/dsh-qa-surface/src/index.ts",
5833
- "line": 287,
6048
+ "line": 325,
5834
6049
  "column": 9
5835
6050
  }
5836
6051
  },
@@ -5848,10 +6063,82 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5848
6063
  },
5849
6064
  sourceLocation: {
5850
6065
  "file": "packages/dsh-qa-surface/src/index.ts",
5851
- "line": 277,
6066
+ "line": 315,
5852
6067
  "column": 3
5853
6068
  }
5854
6069
  },
6070
+ {
6071
+ id: "@yadsh/dsh-qa-surface#qaSurface/pendingApprovals",
6072
+ service: "qaSurface",
6073
+ namespace: "qaSurface",
6074
+ method: "pendingApprovals",
6075
+ invocation: { kind: "direct" },
6076
+ parameters: [{
6077
+ name: "token",
6078
+ wire: "token",
6079
+ source: "json",
6080
+ codec: {
6081
+ mode: "strict",
6082
+ typeSymbol: "@yadsh/dsh-qa-surface#qaSurface/pendingApprovals:token",
6083
+ schema: _yadsh_dsh_qa_surface_qaSurface_pendingApprovals_parameter_0$schema
6084
+ }
6085
+ }, {
6086
+ name: "sessionId",
6087
+ wire: "sessionId",
6088
+ source: "json",
6089
+ codec: {
6090
+ mode: "strict",
6091
+ typeSymbol: "@yadsh/dsh-qa-surface#qaSurface/pendingApprovals:sessionId",
6092
+ schema: _yadsh_dsh_qa_surface_qaSurface_pendingApprovals_parameter_1$schema
6093
+ }
6094
+ }],
6095
+ result: {
6096
+ mode: "strict",
6097
+ typeSymbol: "@yadsh/dsh-qa-surface#qaSurface/pendingApprovals:result",
6098
+ schema: _yadsh_dsh_qa_surface_qaSurface_pendingApprovals_result$schema
6099
+ },
6100
+ sourceLocation: {
6101
+ "file": "packages/dsh-qa-surface/src/index.ts",
6102
+ "line": 441,
6103
+ "column": 9
6104
+ }
6105
+ },
6106
+ {
6107
+ id: "@yadsh/dsh-qa-surface#qaSurface/pendingQuestions",
6108
+ service: "qaSurface",
6109
+ namespace: "qaSurface",
6110
+ method: "pendingQuestions",
6111
+ invocation: { kind: "direct" },
6112
+ parameters: [{
6113
+ name: "token",
6114
+ wire: "token",
6115
+ source: "json",
6116
+ codec: {
6117
+ mode: "strict",
6118
+ typeSymbol: "@yadsh/dsh-qa-surface#qaSurface/pendingQuestions:token",
6119
+ schema: _yadsh_dsh_qa_surface_qaSurface_pendingQuestions_parameter_0$schema
6120
+ }
6121
+ }, {
6122
+ name: "sessionId",
6123
+ wire: "sessionId",
6124
+ source: "json",
6125
+ codec: {
6126
+ mode: "strict",
6127
+ typeSymbol: "@yadsh/dsh-qa-surface#qaSurface/pendingQuestions:sessionId",
6128
+ schema: _yadsh_dsh_qa_surface_qaSurface_pendingQuestions_parameter_1$schema
6129
+ }
6130
+ }],
6131
+ result: {
6132
+ mode: "strict",
6133
+ typeSymbol: "@yadsh/dsh-qa-surface#qaSurface/pendingQuestions:result",
6134
+ schema: _yadsh_dsh_qa_surface_qaSurface_pendingQuestions_result$schema
6135
+ },
6136
+ sourceLocation: {
6137
+ "file": "packages/dsh-qa-surface/src/index.ts",
6138
+ "line": 470,
6139
+ "column": 9
6140
+ }
6141
+ },
5855
6142
  {
5856
6143
  id: "@yadsh/dsh-qa-surface#qaSurface/readSourceFile",
5857
6144
  service: "qaSurface",
@@ -5897,7 +6184,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5897
6184
  },
5898
6185
  sourceLocation: {
5899
6186
  "file": "packages/dsh-qa-surface/src/index.ts",
5900
- "line": 394,
6187
+ "line": 507,
5901
6188
  "column": 9
5902
6189
  }
5903
6190
  },
@@ -5933,8 +6220,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5933
6220
  },
5934
6221
  sourceLocation: {
5935
6222
  "file": "packages/dsh-qa-surface/src/index.ts",
5936
- "line": 361,
5937
- "column": 3
6223
+ "line": 399,
6224
+ "column": 9
5938
6225
  }
5939
6226
  },
5940
6227
  {
@@ -5969,8 +6256,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5969
6256
  },
5970
6257
  sourceLocation: {
5971
6258
  "file": "packages/dsh-qa-surface/src/index.ts",
5972
- "line": 387,
5973
- "column": 3
6259
+ "line": 428,
6260
+ "column": 9
5974
6261
  }
5975
6262
  }
5976
6263
  ]
@@ -6459,7 +6746,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6459
6746
  toolPolicy: Object.freeze({
6460
6747
  mode: "allow-list",
6461
6748
  allow: Object.freeze([])
6462
- })
6749
+ }),
6750
+ sharedReadOnlyRoots: Object.freeze([])
6463
6751
  }),
6464
6752
  embedding: Object.freeze({ frameAncestors: null }),
6465
6753
  accounts: Object.freeze({
@@ -6513,7 +6801,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6513
6801
  subagents: Object.freeze({
6514
6802
  inheritSources: true,
6515
6803
  enableReportToolFallback: true,
6516
- markIncompleteOpaqueRuns: true
6804
+ markIncompleteOpaqueRuns: true,
6805
+ validateReportedSources: true
6517
6806
  }),
6518
6807
  legacy: Object.freeze({ parseAssistantSourcesBlock: false })
6519
6808
  }),
@@ -6533,6 +6822,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6533
6822
  return trimmed === "" ? null : trimmed;
6534
6823
  }
6535
6824
  /**
6825
+ * Whether a configured directory is absolute. The rule reads a drive-letter
6826
+ * path as absolute on every platform, like `session.cwd` does: a deployment
6827
+ * profile is authored on one machine and may be read on another, so a
6828
+ * Windows-style root is not a mistake to reject on a POSIX host.
6829
+ */
6830
+ function isAbsoluteDirectoryPath(value) {
6831
+ return /^([a-zA-Z]:[/\\]|\/)/u.test(value);
6832
+ }
6833
+ /**
6536
6834
  * Shared numeric guard for operator-tuned limits: the value has to be a safe
6537
6835
  * integer inside [min, max], and every rejection carries the same message.
6538
6836
  */
@@ -6643,8 +6941,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6643
6941
  cookieBootstrap: input.entry?.cookieBootstrap ?? DEFAULT_QA_SURFACE_CONFIG.entry.cookieBootstrap
6644
6942
  }),
6645
6943
  interaction: Object.freeze({
6646
- approvals: "blocked",
6647
- questions: "unsupported"
6944
+ approvals: input.interaction?.approvals ?? DEFAULT_QA_SURFACE_CONFIG.interaction.approvals,
6945
+ questions: input.interaction?.questions ?? DEFAULT_QA_SURFACE_CONFIG.interaction.questions
6648
6946
  })
6649
6947
  };
6650
6948
  }
@@ -6656,6 +6954,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6656
6954
  if (names.some((value) => value.length > 200)) throw new TypeError("dsh-qa-surface: lockdown.toolPolicy.allow names must be at most 200 characters");
6657
6955
  return Object.freeze(names);
6658
6956
  }
6957
+ function uniqueAbsoluteRoots(values) {
6958
+ const roots = [...new Set(values.map((value) => value.trim()).filter(Boolean))];
6959
+ if (roots.some((value) => !isAbsoluteDirectoryPath(value))) throw new TypeError("dsh-qa-surface: lockdown.sharedReadOnlyRoots must contain only absolute paths");
6960
+ return Object.freeze(roots);
6961
+ }
6659
6962
  /**
6660
6963
  * Resolve the lockdown domain. The capability flags are clamped shut, and
6661
6964
  * showing the reset control needs an explicit authorization here, so the
@@ -6699,7 +7002,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6699
7002
  toolPolicy: Object.freeze({
6700
7003
  mode: "allow-list",
6701
7004
  allow: uniqueToolNames(input.lockdown?.toolPolicy?.allow ?? [])
6702
- })
7005
+ }),
7006
+ sharedReadOnlyRoots: uniqueAbsoluteRoots(input.lockdown?.sharedReadOnlyRoots ?? [])
6703
7007
  });
6704
7008
  }
6705
7009
  //#endregion
@@ -6710,7 +7014,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6710
7014
  const fixedSessionId = optionalText(input.session?.fixedSessionId);
6711
7015
  if (policy === "fixed" && fixedSessionId === null) throw new TypeError("dsh-qa-surface: session.fixedSessionId is required for fixed policy");
6712
7016
  const cwd = optionalText(input.session?.cwd);
6713
- if (cwd !== null && !/^([a-zA-Z]:[/\\]|\/)/u.test(cwd)) throw new TypeError("dsh-qa-surface: session.cwd must be an absolute directory path");
7017
+ if (cwd !== null && !isAbsoluteDirectoryPath(cwd)) throw new TypeError("dsh-qa-surface: session.cwd must be an absolute directory path");
6714
7018
  const workspaceId = optionalText(input.session?.workspaceId);
6715
7019
  if (workspaceId !== null && cwd !== null) throw new TypeError("dsh-qa-surface: session.workspaceId and session.cwd are mutually exclusive");
6716
7020
  const provider = optionalText(input.session?.provider);
@@ -6777,7 +7081,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6777
7081
  subagents: Object.freeze({
6778
7082
  inheritSources: input.sources?.subagents?.inheritSources ?? DEFAULT_QA_SURFACE_CONFIG.sources.subagents.inheritSources,
6779
7083
  enableReportToolFallback: input.sources?.subagents?.enableReportToolFallback ?? DEFAULT_QA_SURFACE_CONFIG.sources.subagents.enableReportToolFallback,
6780
- markIncompleteOpaqueRuns: input.sources?.subagents?.markIncompleteOpaqueRuns ?? DEFAULT_QA_SURFACE_CONFIG.sources.subagents.markIncompleteOpaqueRuns
7084
+ markIncompleteOpaqueRuns: input.sources?.subagents?.markIncompleteOpaqueRuns ?? DEFAULT_QA_SURFACE_CONFIG.sources.subagents.markIncompleteOpaqueRuns,
7085
+ validateReportedSources: input.sources?.subagents?.validateReportedSources ?? DEFAULT_QA_SURFACE_CONFIG.sources.subagents.validateReportedSources
6781
7086
  }),
6782
7087
  legacy: Object.freeze({ parseAssistantSourcesBlock: input.sources?.legacy?.parseAssistantSourcesBlock ?? DEFAULT_QA_SURFACE_CONFIG.sources.legacy.parseAssistantSourcesBlock })
6783
7088
  });
@@ -7493,6 +7798,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7493
7798
  return reason === void 0 ? null : reason;
7494
7799
  }
7495
7800
  function attestationHint(reason) {
7801
+ 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.";
7496
7802
  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.";
7497
7803
  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).";
7498
7804
  if (reason === "composition-mismatch") return "The session's agent preset, workspace or model no longer matches the deployment QA config.";
@@ -7726,6 +8032,194 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7726
8032
  }
7727
8033
  };
7728
8034
  //#endregion
8035
+ //#region src/client/approvals.ts
8036
+ /**
8037
+ * The operator's side of a parked tool call.
8038
+ *
8039
+ * A composed gate's `ask` is answered on the Host, so the waiting request is
8040
+ * Host state: it survives a page reload, and the surface learns about it by
8041
+ * asking rather than by receiving an event. The bridge holds the last list it
8042
+ * saw, refreshes it while a turn runs, and forwards an answer.
8043
+ */
8044
+ var QaHostApprovalBridge = class {
8045
+ approvalApi;
8046
+ approvals = [];
8047
+ signature = "";
8048
+ refreshing = false;
8049
+ answering = false;
8050
+ /** Bumped on reset so a stale in-flight response never lands. */
8051
+ generation = 0;
8052
+ constructor(approvalApi) {
8053
+ this.approvalApi = approvalApi;
8054
+ }
8055
+ /** Leave the chat: a chat switch drops the previous chat's requests. */
8056
+ reset() {
8057
+ this.generation += 1;
8058
+ this.approvals = [];
8059
+ this.signature = "";
8060
+ this.refreshing = false;
8061
+ this.answering = false;
8062
+ }
8063
+ /** The last list this bridge saw; empty when the deployment blocks them. */
8064
+ list() {
8065
+ return this.approvals;
8066
+ }
8067
+ /** Whether this page can ask the Host about approvals at all. */
8068
+ get available() {
8069
+ return this.approvalApi !== void 0;
8070
+ }
8071
+ /**
8072
+ * Re-read the chat's pending requests; a changed list republishes through the
8073
+ * callback. Overlapping calls coalesce, and a response that outlives its chat
8074
+ * is dropped.
8075
+ */
8076
+ async refresh(sessionId, token, onChanged) {
8077
+ const api = this.approvalApi;
8078
+ if (api === void 0 || this.refreshing) return;
8079
+ const generation = this.generation;
8080
+ this.refreshing = true;
8081
+ try {
8082
+ const result = await api.pendingApprovals(token, sessionId);
8083
+ if (!result.ok || generation !== this.generation) return;
8084
+ const signature = JSON.stringify(result.value);
8085
+ if (signature === this.signature) return;
8086
+ this.signature = signature;
8087
+ this.approvals = result.value;
8088
+ onChanged();
8089
+ } catch (error) {
8090
+ console.error("dsh-qa-surface: approval poll failed", error);
8091
+ } finally {
8092
+ if (generation === this.generation) this.refreshing = false;
8093
+ }
8094
+ }
8095
+ /**
8096
+ * Answer one request and re-read the list. A refusal is not an error: the
8097
+ * request was already settled (the turn was stopped, the call went away), so
8098
+ * the refresh — not the answer — is what the surface shows.
8099
+ */
8100
+ async answer(sessionId, token, requestId, decision) {
8101
+ const api = this.approvalApi;
8102
+ if (api === void 0 || this.answering) return;
8103
+ this.answering = true;
8104
+ const generation = this.generation;
8105
+ try {
8106
+ await api.answerApproval(token, sessionId, requestId, decision);
8107
+ const result = await api.pendingApprovals(token, sessionId);
8108
+ if (generation !== this.generation) return;
8109
+ if (result.ok) {
8110
+ this.signature = JSON.stringify(result.value);
8111
+ this.approvals = result.value;
8112
+ return;
8113
+ }
8114
+ this.signature = "";
8115
+ this.approvals = this.approvals.filter((approval) => approval.id !== requestId);
8116
+ } catch (error) {
8117
+ console.error("dsh-qa-surface: approval answer failed", error);
8118
+ } finally {
8119
+ this.answering = false;
8120
+ }
8121
+ }
8122
+ };
8123
+ //#endregion
8124
+ //#region src/client/questions.ts
8125
+ /**
8126
+ * The operator's side of a parked `ask_user_question` call.
8127
+ *
8128
+ * Like a parked approval, the request lives on the Host: it survives a reload,
8129
+ * and the surface learns about it by asking while a turn runs. Unlike an
8130
+ * approval, an answer is a whole form — every question of the request, with the
8131
+ * operator's selections and free text — so the bridge forwards the form and
8132
+ * re-reads the list afterwards.
8133
+ */
8134
+ var QaHostQuestionBridge = class {
8135
+ questionApi;
8136
+ questions = [];
8137
+ signature = "";
8138
+ refreshing = false;
8139
+ answering = false;
8140
+ /** Bumped on reset so a stale in-flight response never lands. */
8141
+ generation = 0;
8142
+ constructor(questionApi) {
8143
+ this.questionApi = questionApi;
8144
+ }
8145
+ /** Leave the chat: a chat switch drops the previous chat's requests. */
8146
+ reset() {
8147
+ this.generation += 1;
8148
+ this.questions = [];
8149
+ this.signature = "";
8150
+ this.refreshing = false;
8151
+ this.answering = false;
8152
+ }
8153
+ /** The last list this bridge saw; empty when the deployment refuses them. */
8154
+ list() {
8155
+ return this.questions;
8156
+ }
8157
+ /** Whether this page can ask the Host about questions at all. */
8158
+ get available() {
8159
+ return this.questionApi !== void 0;
8160
+ }
8161
+ /**
8162
+ * Re-read the chat's pending questions; a changed list republishes through
8163
+ * the callback. Overlapping calls coalesce, and a response that outlives its
8164
+ * chat is dropped.
8165
+ */
8166
+ async refresh(sessionId, token, onChanged) {
8167
+ const api = this.questionApi;
8168
+ if (api === void 0 || this.refreshing) return;
8169
+ const generation = this.generation;
8170
+ this.refreshing = true;
8171
+ try {
8172
+ const result = await api.pendingQuestions(token, sessionId);
8173
+ if (!result.ok || generation !== this.generation) return;
8174
+ const signature = JSON.stringify(result.value);
8175
+ if (signature === this.signature) return;
8176
+ this.signature = signature;
8177
+ this.questions = result.value;
8178
+ onChanged();
8179
+ } catch (error) {
8180
+ console.error("dsh-qa-surface: question poll failed", error);
8181
+ } finally {
8182
+ if (generation === this.generation) this.refreshing = false;
8183
+ }
8184
+ }
8185
+ /** Send the operator's answers to one request and re-read the list. */
8186
+ async answer(sessionId, token, requestId, answers) {
8187
+ await this.settle(sessionId, token, requestId, (api) => api.answerQuestion(token, sessionId, requestId, answers));
8188
+ }
8189
+ /** Close one request without answering it. */
8190
+ async cancel(sessionId, token, requestId) {
8191
+ await this.settle(sessionId, token, requestId, (api) => api.cancelQuestion(token, sessionId, requestId));
8192
+ }
8193
+ /**
8194
+ * Run one write against a request, then re-read the list it belonged to. A
8195
+ * refusal is not an error: the request was already settled (the turn was
8196
+ * stopped, the call went away), so the refresh — not the write — is what the
8197
+ * surface shows.
8198
+ */
8199
+ async settle(sessionId, token, requestId, write) {
8200
+ const api = this.questionApi;
8201
+ if (api === void 0 || this.answering) return;
8202
+ this.answering = true;
8203
+ const generation = this.generation;
8204
+ try {
8205
+ await write(api);
8206
+ const result = await api.pendingQuestions(token, sessionId);
8207
+ if (generation !== this.generation) return;
8208
+ if (result.ok) {
8209
+ this.signature = JSON.stringify(result.value);
8210
+ this.questions = result.value;
8211
+ return;
8212
+ }
8213
+ this.signature = "";
8214
+ this.questions = this.questions.filter((entry) => entry.id !== requestId);
8215
+ } catch (error) {
8216
+ console.error("dsh-qa-surface: question answer failed", error);
8217
+ } finally {
8218
+ this.answering = false;
8219
+ }
8220
+ }
8221
+ };
8222
+ //#endregion
7729
8223
  //#region src/client/stream-publisher.ts
7730
8224
  /**
7731
8225
  * Spacing for a running turn's stream frames. A running turn's frames arrive
@@ -7785,7 +8279,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7785
8279
  sources: Object.freeze([]),
7786
8280
  sourcesComplete: true,
7787
8281
  incompleteSourceOrigins: void 0,
7788
- viewingSubagent: null
8282
+ viewingSubagent: null,
8283
+ approvals: Object.freeze([]),
8284
+ questions: Object.freeze([])
7789
8285
  });
7790
8286
  //#endregion
7791
8287
  //#region src/client/wait-for.ts
@@ -9029,13 +9525,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9029
9525
  sources: latest === void 0 ? [] : [...latest.sources, ...config.sources.display.showDiscovered ? latest.discovered ?? [] : []],
9030
9526
  sourcesComplete: latest?.complete ?? true,
9031
9527
  incompleteSourceOrigins: latest?.incompleteOrigins,
9032
- viewingSubagent: input.viewingSubagent
9528
+ viewingSubagent: input.viewingSubagent,
9529
+ approvals: input.approvals,
9530
+ questions: input.questions
9033
9531
  };
9034
9532
  }
9035
9533
  //#endregion
9036
9534
  //#region src/client/QaSessionController.ts
9037
9535
  const CONFIGURATION_ERROR = "Настройки помощника недоступны.";
9038
9536
  /**
9537
+ * Spacing of the parked-request poll. A request is answered by a person, so a
9538
+ * second of latency is invisible; the poll only runs while a turn runs.
9539
+ */
9540
+ const PENDING_POLL_MS = 1e3;
9541
+ /**
9039
9542
  * The only module that couples QA behavior to DSH Session/client APIs. It owns
9040
9543
  * the chat lifecycle state machine; the observable wait, browser-local chat
9041
9544
  * bookkeeping and policy-attestation details live in their own modules.
@@ -9079,6 +9582,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9079
9582
  chatsRevision = 0;
9080
9583
  /** Host-side provenance of the bound chat, merged into the projection. */
9081
9584
  hostSources;
9585
+ /** Host-side approvals of the bound chat waiting for the operator. */
9586
+ hostApprovals;
9587
+ /** Host-side question requests of the bound chat waiting for the operator. */
9588
+ hostQuestions;
9589
+ /** Set while a running turn is polled for parked requests. */
9590
+ pendingTimer;
9082
9591
  constructor(options) {
9083
9592
  this.sessions = options.sessions;
9084
9593
  this.createSessionRemote = options.createSession;
@@ -9100,6 +9609,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9100
9609
  this.accounts = options.accounts;
9101
9610
  this.fileUpload = options.fileUpload ?? (() => void 0);
9102
9611
  this.hostSources = new QaHostSourceBridge(this.sourceApi, () => this.accounts?.token() ?? "");
9612
+ this.hostApprovals = new QaHostApprovalBridge(options.approvalApi);
9613
+ this.hostQuestions = new QaHostQuestionBridge(options.questionApi);
9103
9614
  this.timeoutMs = options.timeoutMs ?? 15e3;
9104
9615
  this.streamPublisher = new StreamPublisher(options.streamIntervalMs ?? 66);
9105
9616
  this.connectedOnce = this.connection.getSnapshot() !== void 0;
@@ -9557,6 +10068,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9557
10068
  if (this.session !== void 0) this.assets.release(String(this.session.sessionId));
9558
10069
  this.session = void 0;
9559
10070
  this.hostSources.reset();
10071
+ this.hostApprovals.reset();
10072
+ this.hostQuestions.reset();
10073
+ this.stopPendingPolling();
9560
10074
  this.admissionPending = false;
9561
10075
  this.policyReady = false;
9562
10076
  }
@@ -9598,12 +10112,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9598
10112
  const projectedSourceBundles = projectTurnSources(conversationSnapshot, sessionId, this.config.session.cwd ?? void 0);
9599
10113
  const sourceBundles = this.hostSources.merge(projectedSourceBundles);
9600
10114
  if (!snapshot.running) this.hostSources.refresh(sessionId, () => this.publish());
10115
+ this.syncPendingPolling(snapshot.running === true);
9601
10116
  this.state = projectBoundSessionState({
9602
10117
  connected,
9603
10118
  sessionId,
9604
10119
  sessionSnapshot: snapshot,
9605
10120
  conversationSnapshot,
9606
10121
  sourceBundles,
10122
+ approvals: this.hostApprovals.list(),
10123
+ questions: this.hostQuestions.list(),
9607
10124
  author: this.accounts?.messageAuthorOf(sessionId),
9608
10125
  operationError: this.operationError,
9609
10126
  policyReady: this.policyReady,
@@ -9674,6 +10191,59 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9674
10191
  emit() {
9675
10192
  for (const listener of this.listeners) listener();
9676
10193
  }
10194
+ /**
10195
+ * Answer a request the Host parked for this chat. The Host re-checks the
10196
+ * request against the chat, so an answer that lost its race is a no-op; the
10197
+ * refresh that follows is what the surface renders.
10198
+ */
10199
+ async answerApproval(requestId, decision) {
10200
+ if (this.session === void 0) return;
10201
+ await this.hostApprovals.answer(String(this.session.sessionId), this.hostToken(), requestId, decision);
10202
+ this.publish();
10203
+ }
10204
+ /** Send the operator's answers to one parked question request. */
10205
+ async answerQuestion(requestId, answers) {
10206
+ if (this.session === void 0) return;
10207
+ await this.hostQuestions.answer(String(this.session.sessionId), this.hostToken(), requestId, answers);
10208
+ this.publish();
10209
+ }
10210
+ /** Close a parked question request without answering it. */
10211
+ async cancelQuestion(requestId) {
10212
+ if (this.session === void 0) return;
10213
+ await this.hostQuestions.cancel(String(this.session.sessionId), this.hostToken(), requestId);
10214
+ this.publish();
10215
+ }
10216
+ /**
10217
+ * Poll the Host for parked requests while a turn runs. A request only exists
10218
+ * inside an open turn, and it is Host state: polling is how the page learns
10219
+ * about it, and how it reappears after a reload. One timer serves both seams,
10220
+ * and each is polled only where the deployment answers it.
10221
+ */
10222
+ syncPendingPolling(running) {
10223
+ const approvals = this.hostApprovals.available ? this.config.interaction.approvals === "interactive" : false;
10224
+ const questions = this.hostQuestions.available ? this.config.interaction.questions === "interactive" : false;
10225
+ if (!(running && (approvals || questions) && this.viewingSubagent === null && this.session !== void 0)) {
10226
+ this.stopPendingPolling();
10227
+ return;
10228
+ }
10229
+ if (this.pendingTimer !== void 0 || this.session === void 0) return;
10230
+ const sessionId = String(this.session.sessionId);
10231
+ const token = this.hostToken();
10232
+ const tick = () => {
10233
+ if (approvals) this.hostApprovals.refresh(sessionId, token, () => this.publish());
10234
+ if (questions) this.hostQuestions.refresh(sessionId, token, () => this.publish());
10235
+ };
10236
+ this.pendingTimer = setInterval(tick, PENDING_POLL_MS);
10237
+ tick();
10238
+ }
10239
+ stopPendingPolling() {
10240
+ if (this.pendingTimer === void 0) return;
10241
+ clearInterval(this.pendingTimer);
10242
+ this.pendingTimer = void 0;
10243
+ }
10244
+ hostToken() {
10245
+ return this.accounts?.token() ?? "";
10246
+ }
9677
10247
  };
9678
10248
  //#endregion
9679
10249
  //#region src/client/settings/format.ts
@@ -10121,6 +10691,292 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10121
10691
  });
10122
10692
  }
10123
10693
  //#endregion
10694
+ //#region src/client/components/QaApproval.tsx
10695
+ /**
10696
+ * The tool calls a composed gate parked for a person. One row per request, the
10697
+ * gate's own reason on top, and the two answers stock DSH offers: refuse, or
10698
+ * let this one call through. Nothing here decides on its own — an unanswered
10699
+ * request simply keeps the turn waiting.
10700
+ */
10701
+ const QaApproval = (0, react.memo)(function QaApproval(props) {
10702
+ const [answering, setAnswering] = (0, react.useState)(null);
10703
+ const approvals = props.approvals;
10704
+ if (approvals.length === 0) return null;
10705
+ const answer = (requestId, decision) => {
10706
+ setAnswering(requestId);
10707
+ props.onAnswer(requestId, decision).finally(() => {
10708
+ setAnswering((current) => current === requestId ? null : current);
10709
+ });
10710
+ };
10711
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10712
+ className: "dsh-qa-approvals",
10713
+ "aria-label": "Запросы на одобрение",
10714
+ children: approvals.map((approval) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
10715
+ className: "dsh-qa-approval",
10716
+ "aria-live": "polite",
10717
+ children: [
10718
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
10719
+ className: "dsh-qa-approval__strip",
10720
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10721
+ className: "dsh-qa-approval__dot",
10722
+ "aria-hidden": "true"
10723
+ }), "Ожидает одобрения"]
10724
+ }),
10725
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10726
+ className: "dsh-qa-approval__reason",
10727
+ children: approval.reason ?? `Инструмент «${approval.toolName}» запрашивает разрешение.`
10728
+ }),
10729
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
10730
+ className: "dsh-qa-approval__tool",
10731
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: approval.toolName }), approval.delegated ? " · запросил субагент" : ""]
10732
+ }),
10733
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10734
+ className: "dsh-qa-approval__actions",
10735
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
10736
+ type: "button",
10737
+ className: "dsh-qa-approval__button",
10738
+ disabled: answering === approval.id,
10739
+ onClick: () => answer(approval.id, "rejected"),
10740
+ children: "Отклонить"
10741
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
10742
+ type: "button",
10743
+ className: "dsh-qa-approval__button dsh-qa-approval__button--primary",
10744
+ disabled: answering === approval.id,
10745
+ onClick: () => answer(approval.id, "allowed-once"),
10746
+ children: "Разрешить один раз"
10747
+ })]
10748
+ })
10749
+ ]
10750
+ }, approval.id))
10751
+ });
10752
+ });
10753
+ //#endregion
10754
+ //#region src/client/components/QaQuestions.tsx
10755
+ const NO_DRAFT = {
10756
+ selected: [],
10757
+ custom: "",
10758
+ skipped: false
10759
+ };
10760
+ const isAnswered = (draft) => draft.skipped || draft.selected.length > 0 || draft.custom.trim() !== "";
10761
+ /**
10762
+ * Encode one question's draft the way the harness decodes it (its
10763
+ * user-questions README): a skipped question carries no selection, free text
10764
+ * overrides the choice in a single-select, and accompanies it in a
10765
+ * multi-select.
10766
+ */
10767
+ function encodeAnswer(question, draft) {
10768
+ if (draft.skipped) return {
10769
+ id: question.id,
10770
+ selected: []
10771
+ };
10772
+ const custom = draft.custom.trim();
10773
+ if (question.multiSelect) return {
10774
+ id: question.id,
10775
+ selected: [...draft.selected],
10776
+ ...custom === "" ? {} : { custom }
10777
+ };
10778
+ if (custom !== "") return {
10779
+ id: question.id,
10780
+ selected: [],
10781
+ custom
10782
+ };
10783
+ return {
10784
+ id: question.id,
10785
+ selected: [...draft.selected]
10786
+ };
10787
+ }
10788
+ /**
10789
+ * One parked `ask_user_question` request: the operator's answers, a question at
10790
+ * a time, with a pager when the model asked more than one thing at once.
10791
+ */
10792
+ function QaQuestionForm({ request, onAnswer, onCancel }) {
10793
+ const [index, setIndex] = (0, react.useState)(0);
10794
+ const [drafts, setDrafts] = (0, react.useState)({});
10795
+ const [busy, setBusy] = (0, react.useState)(null);
10796
+ const [failure, setFailure] = (0, react.useState)(null);
10797
+ const questions = request.questions;
10798
+ const question = questions[Math.min(index, questions.length - 1)];
10799
+ if (question === void 0) return null;
10800
+ const draft = drafts[question.id] ?? NO_DRAFT;
10801
+ const last = index === questions.length - 1;
10802
+ const complete = questions.every((item) => isAnswered(drafts[item.id] ?? NO_DRAFT));
10803
+ const edit = (next) => {
10804
+ setDrafts((current) => ({
10805
+ ...current,
10806
+ [question.id]: next
10807
+ }));
10808
+ };
10809
+ const advance = () => {
10810
+ setIndex((current) => Math.min(current + 1, questions.length - 1));
10811
+ };
10812
+ const select = (label) => {
10813
+ if (question.multiSelect) {
10814
+ const selected = draft.selected.includes(label) ? draft.selected.filter((item) => item !== label) : [...draft.selected, label];
10815
+ edit({
10816
+ ...draft,
10817
+ selected
10818
+ });
10819
+ return;
10820
+ }
10821
+ edit({
10822
+ ...draft,
10823
+ selected: [label]
10824
+ });
10825
+ if (!last) advance();
10826
+ };
10827
+ const skip = () => {
10828
+ edit({
10829
+ ...draft,
10830
+ selected: [],
10831
+ custom: "",
10832
+ skipped: true
10833
+ });
10834
+ if (!last) advance();
10835
+ };
10836
+ const submit = () => {
10837
+ setBusy("answer");
10838
+ setFailure(null);
10839
+ onAnswer(request.id, questions.map((item) => encodeAnswer(item, drafts[item.id] ?? NO_DRAFT))).catch(() => setFailure("Не удалось отправить ответ.")).finally(() => setBusy(null));
10840
+ };
10841
+ const cancel = () => {
10842
+ setBusy("cancel");
10843
+ setFailure(null);
10844
+ onCancel(request.id).catch(() => setFailure("Не удалось закрыть вопрос.")).finally(() => setBusy(null));
10845
+ };
10846
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
10847
+ className: "dsh-qa-question",
10848
+ "aria-live": "polite",
10849
+ children: [
10850
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
10851
+ className: "dsh-qa-question__strip",
10852
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10853
+ className: "dsh-qa-question__dot",
10854
+ "aria-hidden": "true"
10855
+ }), questions.length > 1 ? `Требуется ответ · вопрос ${index + 1} из ${questions.length}` : "Требуется ответ"]
10856
+ }),
10857
+ question.header === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10858
+ className: "dsh-qa-question__header",
10859
+ children: question.header
10860
+ }),
10861
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
10862
+ className: "dsh-qa-question__fieldset",
10863
+ disabled: busy !== null,
10864
+ children: [
10865
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("legend", {
10866
+ className: "dsh-qa-question__text",
10867
+ children: question.question
10868
+ }),
10869
+ question.detail === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10870
+ className: "dsh-qa-question__detail",
10871
+ children: question.detail
10872
+ }),
10873
+ question.options.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10874
+ className: "dsh-qa-question__options",
10875
+ role: question.multiSelect ? "group" : "radiogroup",
10876
+ "aria-label": question.question,
10877
+ children: question.options.map((option) => {
10878
+ const checked = draft.selected.includes(option.label);
10879
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
10880
+ className: "dsh-qa-question__option",
10881
+ "data-checked": checked ? "true" : "false",
10882
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
10883
+ type: question.multiSelect ? "checkbox" : "radio",
10884
+ name: `qa-question-${request.id}-${question.id}`,
10885
+ checked,
10886
+ onChange: () => select(option.label)
10887
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
10888
+ className: "dsh-qa-question__option-text",
10889
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10890
+ className: "dsh-qa-question__option-label",
10891
+ children: option.label
10892
+ }), option.description === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10893
+ className: "dsh-qa-question__option-description",
10894
+ children: option.description
10895
+ })]
10896
+ })]
10897
+ }, option.label);
10898
+ })
10899
+ }),
10900
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
10901
+ className: "dsh-qa-question__custom",
10902
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "Свой ответ" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
10903
+ rows: 2,
10904
+ value: draft.custom,
10905
+ placeholder: "Напечатайте ответ…",
10906
+ onChange: (event) => edit({
10907
+ ...draft,
10908
+ custom: event.target.value,
10909
+ skipped: false
10910
+ })
10911
+ })]
10912
+ })
10913
+ ]
10914
+ }),
10915
+ failure === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10916
+ className: "dsh-qa-question__error",
10917
+ role: "alert",
10918
+ children: failure
10919
+ }),
10920
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10921
+ className: "dsh-qa-question__actions",
10922
+ children: [
10923
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
10924
+ type: "button",
10925
+ className: "dsh-qa-question__button",
10926
+ disabled: busy !== null,
10927
+ onClick: cancel,
10928
+ children: "Отмена"
10929
+ }),
10930
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
10931
+ type: "button",
10932
+ className: "dsh-qa-question__button",
10933
+ disabled: busy !== null || draft.skipped,
10934
+ onClick: skip,
10935
+ children: "Пропустить"
10936
+ }),
10937
+ index > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
10938
+ type: "button",
10939
+ className: "dsh-qa-question__button",
10940
+ disabled: busy !== null,
10941
+ onClick: () => setIndex((current) => Math.max(current - 1, 0)),
10942
+ children: "Назад"
10943
+ }) : null,
10944
+ last ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
10945
+ type: "button",
10946
+ className: "dsh-qa-question__button dsh-qa-question__button--primary",
10947
+ disabled: busy !== null || !complete,
10948
+ onClick: submit,
10949
+ children: "Отправить"
10950
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
10951
+ type: "button",
10952
+ className: "dsh-qa-question__button dsh-qa-question__button--primary",
10953
+ disabled: busy !== null || !isAnswered(draft),
10954
+ onClick: advance,
10955
+ children: "Далее"
10956
+ })
10957
+ ]
10958
+ })
10959
+ ]
10960
+ });
10961
+ }
10962
+ /**
10963
+ * The questions a running turn waits on. Answers are the operator's own: the
10964
+ * form refuses to submit a question left blank, and a question explicitly
10965
+ * skipped is reported as skipped rather than answered by guesswork.
10966
+ */
10967
+ const QaQuestions = (0, react.memo)(function QaQuestions(props) {
10968
+ if (props.questions.length === 0) return null;
10969
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10970
+ className: "dsh-qa-questions",
10971
+ "aria-label": "Вопросы помощника",
10972
+ children: props.questions.map((request) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QaQuestionForm, {
10973
+ request,
10974
+ onAnswer: props.onAnswer,
10975
+ onCancel: props.onCancel
10976
+ }, request.id))
10977
+ });
10978
+ });
10979
+ //#endregion
10124
10980
  //#region src/client/components/QaFileAttachment.tsx
10125
10981
  /** Uppercase extension badge; a name without one falls back to a paper glyph. */
10126
10982
  function badgeText(name) {
@@ -11842,8 +12698,29 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11842
12698
  }
11843
12699
  //#endregion
11844
12700
  //#region src/client/components/QaChangelog.tsx
11845
- const QA_VERSION = "0.4.0";
12701
+ const QA_VERSION = "0.5.0";
11846
12702
  const QA_CHANGELOG = [
12703
+ {
12704
+ version: "0.5.0",
12705
+ date: "2026-09-14",
12706
+ sections: [{
12707
+ title: "Новое",
12708
+ items: [
12709
+ "Одобрения можно давать прямо в QA-интерфейсе: если композитная проверка (классификатор безопасности, правило-хук) помечает вызов инструмента как требующий подтверждения, а настройка interaction.approvals стоит в режиме interactive, вызов встаёт на паузу и появляется карточкой над полем ввода — с причиной проверки, именем инструмента и двумя ответами, «Отклонить» и «Разрешить один раз». Запрос хранится на сервере, поэтому переживает перезагрузку страницы, а неотвеченный закрывается вместе с остановкой хода. По умолчанию (blocked) вызов отклоняется с причиной, и ни один режим не одобряет ничего без человека.",
12710
+ "Вопросы модели отвечаются там же: interaction.questions в режиме interactive показывает ask_user_question формой над полем ввода — вопросы листаются по одному, с вариантами одиночного и множественного выбора, свободным ответом и явными «Пропустить» и «Отменить». Пропущенный вопрос передаётся модели как пропущенный, а не выдуманный; без этой настройки запрос отклоняется с причиной, которую модель может отработать.",
12711
+ "Модели разрешено сообщать источники, которые не сводятся к документу: настройка sources.subagents.validateReportedSources снимает сразу две проверки — отчёт принимается и от самого QA-агента, а не только от делегированного прогона, а запись без адреса сохраняет свой тип, заголовок и сниппет вместо того, чтобы отбрасываться. Ссылка, которую не удалось разобрать, сохраняется как есть, а отсутствующий заголовок заменяется последним сегментом пути. Тумблер живёт в карточке настроек, раздел «Источники», подраздел «Субагенты».",
12712
+ "Пароль аккаунта сбрасывается из командной строки: qa-accounts set-password <почта> --password-stdin. Аккаунт сохраняет свой идентификатор, профиль и все свои чаты, а прежний пароль и выданные под ним токены перестают работать, поэтому сброс закрывает и подозрение на утечку. Пароль читается из стандартного ввода одной строкой и не остаётся в истории команд.",
12713
+ "Общие каталоги только для чтения: lockdown.sharedReadOnlyRoots перечисляет абсолютные пути, доступные читающим инструментам (read, read_image, glob, grep и просмотр в str_replace_editor) поверх личного каталога аккаунта. Записи при этом остаются внутри каталога пользователя, и доступа к инструменту, которого нет в белом списке, это не даёт."
12714
+ ]
12715
+ }, {
12716
+ title: "Исправления",
12717
+ items: [
12718
+ "Чат, начатый до перезапуска хоста, снова открывается: поверхность сама будит агента сессии, если хост его ещё не собрал. Раньше такой чат отвечал «Не удалось открыть этот чат.», а восстановление при загрузке бросало его и поднимало новый сеанс.",
12719
+ "Инструменты чтения Git больше не отклоняются сторожем путей по имени: границу репозитория держит отдельно настроенный Git-плагин.",
12720
+ "Заблокированное одобрение больше не выдаётся за отказ пользователя: вызов отклоняется с собственной причиной поверхности, а не с формулировкой «пользователь отклонил»."
12721
+ ]
12722
+ }]
12723
+ },
11847
12724
  {
11848
12725
  version: "0.4.0",
11849
12726
  date: "2026-09-13",
@@ -11963,7 +12840,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
11963
12840
  className: "dsh-qa-changelog__date",
11964
12841
  children: entry.date
11965
12842
  }),
11966
- entry.version === "0.4.0" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
12843
+ entry.version === "0.5.0" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
11967
12844
  className: "dsh-qa-changelog__current",
11968
12845
  children: "текущая"
11969
12846
  }) : null
@@ -13644,6 +14521,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
13644
14521
  secureSession: props.secureSession,
13645
14522
  createSession: props.createSession,
13646
14523
  sourceApi: props.sourceApi,
14524
+ ...props.approvalApi === void 0 ? {} : { approvalApi: props.approvalApi },
14525
+ ...props.questionApi === void 0 ? {} : { questionApi: props.questionApi },
13647
14526
  config,
13648
14527
  storage: window.localStorage,
13649
14528
  accounts: facade,
@@ -13661,6 +14540,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
13661
14540
  props.connection,
13662
14541
  props.createSession,
13663
14542
  props.fileUpload,
14543
+ props.approvalApi,
14544
+ props.questionApi,
13664
14545
  props.secureSession,
13665
14546
  props.sourceApi,
13666
14547
  props.sessions,
@@ -13711,6 +14592,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
13711
14592
  }, [controller]);
13712
14593
  const handleSend = (0, react.useCallback)((text, attachments) => controller?.send(text, attachments) ?? Promise.resolve(false), [controller]);
13713
14594
  const handleStop = (0, react.useCallback)(() => controller?.stop() ?? Promise.resolve(), [controller]);
14595
+ const handleAnswerApproval = (0, react.useCallback)((requestId, decision) => controller?.answerApproval(requestId, decision) ?? Promise.resolve(), [controller]);
14596
+ const handleAnswerQuestion = (0, react.useCallback)((requestId, answers) => controller?.answerQuestion(requestId, answers) ?? Promise.resolve(), [controller]);
14597
+ const handleCancelQuestion = (0, react.useCallback)((requestId) => controller?.cancelQuestion(requestId) ?? Promise.resolve(), [controller]);
13714
14598
  const handleCloseSubagent = (0, react.useCallback)(() => {
13715
14599
  controller?.closeSubagent();
13716
14600
  }, [controller]);
@@ -13972,31 +14856,43 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
13972
14856
  className: "dsh-qa-footer",
13973
14857
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
13974
14858
  className: "dsh-qa-footer__inner",
13975
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(QaComposer, {
13976
- placeholder: config.branding.placeholder,
13977
- quickQuestions: empty ? config.suggestedQuestions : NO_QUESTIONS,
13978
- canSend: state.canSend,
13979
- canStop: state.canStop,
13980
- running: state.phase === "running",
13981
- showStop: config.ui.showStop,
13982
- status,
13983
- attachments: pendingAttachments,
13984
- limits,
13985
- onAttachmentsChange: setPendingAttachments,
13986
- onSend: handleSend,
13987
- onStop: handleStop
13988
- }, state.sessionId ?? "draft"), config.branding.disclaimer === "" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
13989
- className: "dsh-qa-footer__disclaimer",
13990
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
13991
- viewBox: "0 0 16 16",
13992
- "aria-hidden": "true",
13993
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
13994
- cx: "8",
13995
- cy: "8",
13996
- r: "5.75"
13997
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 7.25v3.5m0-5.25v.5" })]
13998
- }), config.branding.disclaimer]
13999
- })]
14859
+ children: [
14860
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QaApproval, {
14861
+ approvals: state.approvals,
14862
+ onAnswer: handleAnswerApproval
14863
+ }),
14864
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QaQuestions, {
14865
+ questions: state.questions,
14866
+ onAnswer: handleAnswerQuestion,
14867
+ onCancel: handleCancelQuestion
14868
+ }),
14869
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QaComposer, {
14870
+ placeholder: config.branding.placeholder,
14871
+ quickQuestions: empty ? config.suggestedQuestions : NO_QUESTIONS,
14872
+ canSend: state.canSend,
14873
+ canStop: state.canStop,
14874
+ running: state.phase === "running",
14875
+ showStop: config.ui.showStop,
14876
+ status,
14877
+ attachments: pendingAttachments,
14878
+ limits,
14879
+ onAttachmentsChange: setPendingAttachments,
14880
+ onSend: handleSend,
14881
+ onStop: handleStop
14882
+ }, state.sessionId ?? "draft"),
14883
+ config.branding.disclaimer === "" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
14884
+ className: "dsh-qa-footer__disclaimer",
14885
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
14886
+ viewBox: "0 0 16 16",
14887
+ "aria-hidden": "true",
14888
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
14889
+ cx: "8",
14890
+ cy: "8",
14891
+ r: "5.75"
14892
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 7.25v3.5m0-5.25v.5" })]
14893
+ }), config.branding.disclaimer]
14894
+ })
14895
+ ]
14000
14896
  })
14001
14897
  }),
14002
14898
  !empty ? ["left", "right"].map((side) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QaWidthHandle, {
@@ -14482,6 +15378,48 @@ a.dsh-qa-srcref:hover,button.dsh-qa-srcref:hover{border-color:var(--dsw-alias-la
14482
15378
  .dsh-qa-work-tool__body pre{max-height:260px;overflow:auto;margin:0;padding:7px 10px 10px;color:var(--dsw-alias-label-secondary);background:transparent;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;line-height:19px;white-space:pre-wrap;overflow-wrap:anywhere}
14483
15379
  .dsh-qa-error{display:flex;align-items:center;justify-content:center;gap:10px;margin:16px auto;padding:10px 12px;border:1px solid var(--dsh-qa-error);border-radius:10px;color:var(--dsw-alias-label-primary);font-size:13px}
14484
15380
  .dsh-qa-error button{border:0;background:transparent;color:var(--dsh-qa-accent);font:inherit;font-weight:600;cursor:pointer}
15381
+ .dsh-qa-approvals{display:flex;flex-direction:column;gap:8px;width:100%;margin-bottom:10px}
15382
+ .dsh-qa-approval{padding:12px 14px;border:1px solid var(--dsh-qa-warning30);border-radius:12px;background:var(--dsh-qa-warning10)}
15383
+ .dsh-qa-approval__strip{display:flex;align-items:center;gap:7px;margin:0 0 8px;color:var(--dsh-qa-warning-hover);font-size:12px;font-weight:600;line-height:16px}
15384
+ .dsh-qa-approval__dot{width:7px;height:7px;flex:none;border-radius:999px;background:var(--dsh-qa-warning)}
15385
+ .dsh-qa-approval__reason{margin:0;color:var(--dsw-alias-label-primary);font-size:13px;line-height:19px;overflow-wrap:anywhere}
15386
+ .dsh-qa-approval__tool{margin:6px 0 0;color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
15387
+ .dsh-qa-approval__tool code{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11px}
15388
+ .dsh-qa-approval__actions{display:flex;justify-content:flex-end;gap:8px;margin-top:10px}
15389
+ .dsh-qa-approval__button{appearance:none;padding:7px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;font-weight:600;line-height:18px;cursor:pointer}
15390
+ .dsh-qa-approval__button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
15391
+ .dsh-qa-approval__button:disabled{opacity:.5;cursor:default}
15392
+ .dsh-qa-approval__button:focus-visible{outline:2px solid var(--dsh-qa-accent);outline-offset:1px}
15393
+ .dsh-qa-approval__button--primary{border-color:transparent;background:var(--dsh-qa-accent);color:var(--dsh-qa-accent-contrast)}
15394
+ .dsh-qa-approval__button--primary:hover:not(:disabled){background:var(--dsh-qa-accent-hover)}
15395
+ .dsh-qa-questions{display:flex;flex-direction:column;gap:8px;width:100%;margin-bottom:10px}
15396
+ .dsh-qa-question{padding:12px 14px;border:1px solid var(--dsh-qa-info30);border-radius:12px;background:var(--dsh-qa-info10)}
15397
+ .dsh-qa-question__strip{display:flex;align-items:center;gap:7px;margin:0 0 8px;color:var(--dsh-qa-info);font-size:12px;font-weight:600;line-height:16px}
15398
+ .dsh-qa-question__dot{width:7px;height:7px;flex:none;border-radius:999px;background:var(--dsh-qa-info)}
15399
+ .dsh-qa-question__header{margin:0 0 4px;color:var(--dsw-alias-label-tertiary);font-size:11px;font-weight:600;line-height:16px;text-transform:uppercase;letter-spacing:.04em}
15400
+ .dsh-qa-question__fieldset{display:flex;flex-direction:column;gap:8px;min-width:0;margin:0;padding:0;border:0}
15401
+ .dsh-qa-question__text{margin:0;padding:0;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600;line-height:19px;overflow-wrap:anywhere}
15402
+ .dsh-qa-question__detail{margin:0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;white-space:pre-wrap;overflow-wrap:anywhere;max-height:220px;overflow:auto}
15403
+ .dsh-qa-question__options{display:flex;flex-direction:column;gap:6px}
15404
+ .dsh-qa-question__option{display:flex;align-items:flex-start;gap:8px;padding:7px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-2);cursor:pointer}
15405
+ .dsh-qa-question__option[data-checked="true"]{border-color:var(--dsh-qa-info);background:color-mix(in srgb,var(--dsh-qa-info) 8%,var(--dsw-alias-bg-layer-2))}
15406
+ .dsh-qa-question__option input{margin:2px 0 0;accent-color:var(--dsh-qa-info)}
15407
+ .dsh-qa-question__option-text{display:flex;flex-direction:column;gap:2px;min-width:0}
15408
+ .dsh-qa-question__option-label{color:var(--dsw-alias-label-primary);font-size:13px;line-height:18px;overflow-wrap:anywhere}
15409
+ .dsh-qa-question__option-description{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:17px;overflow-wrap:anywhere}
15410
+ .dsh-qa-question__custom{display:flex;flex-direction:column;gap:4px}
15411
+ .dsh-qa-question__custom span{color:var(--dsw-alias-label-tertiary);font-size:11px;line-height:16px}
15412
+ .dsh-qa-question__custom textarea{width:100%;resize:vertical;padding:8px 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:19px}
15413
+ .dsh-qa-question__custom textarea:focus-visible{outline:2px solid var(--dsh-qa-info);outline-offset:1px}
15414
+ .dsh-qa-question__fieldset:disabled{opacity:.6}
15415
+ .dsh-qa-question__error{margin:0;color:var(--dsh-qa-error);font-size:12px;line-height:17px}
15416
+ .dsh-qa-question__actions{display:flex;justify-content:flex-end;gap:8px;margin-top:10px}
15417
+ .dsh-qa-question__button{appearance:none;padding:7px 14px;border:1px solid var(--dsw-alias-border-l2);border-radius:999px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;font-weight:600;line-height:18px;cursor:pointer}
15418
+ .dsh-qa-question__button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
15419
+ .dsh-qa-question__button:disabled{opacity:.5;cursor:default}
15420
+ .dsh-qa-question__button:focus-visible{outline:2px solid var(--dsh-qa-info);outline-offset:1px}
15421
+ .dsh-qa-question__button--primary{border-color:transparent;background:var(--dsh-qa-accent);color:var(--dsh-qa-accent-contrast)}
15422
+ .dsh-qa-question__button--primary:hover:not(:disabled){background:var(--dsh-qa-accent-hover)}
14485
15423
  .dsh-qa-footer{position:relative;flex:none;padding:28px 16px max(12px,env(safe-area-inset-bottom));background:linear-gradient(180deg,color-mix(in srgb,var(--dsw-alias-bg-base) 0%,transparent) 0,var(--dsw-alias-bg-base) 28px)}
14486
15424
  .dsh-qa-footer__inner{width:100%;max-width:var(--dsh-qa-content-width);margin:0 auto}
14487
15425
  .dsh-qa-composer-wrap{display:flex;flex-direction:column;gap:10px;width:100%}
@@ -16159,6 +17097,19 @@ a.dsh-qa-srcref:hover,button.dsh-qa-srcref:hover{border-color:var(--dsw-alias-la
16159
17097
  "markIncompleteOpaqueRuns"
16160
17098
  ], value);
16161
17099
  }
17100
+ }),
17101
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Toggle, {
17102
+ checked: sources?.subagents?.validateReportedSources ?? true,
17103
+ disabled: blocked,
17104
+ label: "Проверять источники из отчёта",
17105
+ hint: "С проверкой принимается только источник с путём или адресом из делегированного прогона. Без неё записывается и «факт» без адреса, и отчёт самого помощника.",
17106
+ onChange: (value) => {
17107
+ props.write([
17108
+ "sources",
17109
+ "subagents",
17110
+ "validateReportedSources"
17111
+ ], value);
17112
+ }
16162
17113
  })
16163
17114
  ]
16164
17115
  })]
@@ -16548,6 +17499,15 @@ textarea.qa-card-control{height:auto;padding:8px 12px;line-height:1.5;resize:ver
16548
17499
  sources: (token, sessionId) => policyRemote.sources(token, sessionId),
16549
17500
  readSourceFile: (token, sessionId, sourcePath) => policyRemote.readSourceFile(token, sessionId, sourcePath)
16550
17501
  };
17502
+ const questionApi = {
17503
+ pendingQuestions: (token, sessionId) => policyRemote.pendingQuestions(token, sessionId),
17504
+ answerQuestion: (token, sessionId, requestId, answers) => policyRemote.answerQuestion(token, sessionId, requestId, answers),
17505
+ cancelQuestion: (token, sessionId, requestId) => policyRemote.cancelQuestion(token, sessionId, requestId)
17506
+ };
17507
+ const approvalApi = {
17508
+ pendingApprovals: (token, sessionId) => policyRemote.pendingApprovals(token, sessionId),
17509
+ answerApproval: (token, sessionId, requestId, decision) => policyRemote.answerApproval(token, sessionId, requestId, decision)
17510
+ };
16551
17511
  const accounts = new QaAccountsController({
16552
17512
  remote: policyRemote,
16553
17513
  storage: window.localStorage,
@@ -16660,6 +17620,8 @@ textarea.qa-card-control{height:auto;padding:8px 12px;line-height:1.5;resize:ver
16660
17620
  secureSession,
16661
17621
  createSession: (token) => policyRemote.createSession(token),
16662
17622
  sourceApi,
17623
+ approvalApi,
17624
+ questionApi,
16663
17625
  accounts,
16664
17626
  fileUpload: () => ctx.get("fileUpload")
16665
17627
  })