@yadsh/dsh-qa-surface 0.17.0 → 0.19.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.
- package/README.md +35 -25
- package/compatibility.json +1 -1
- package/lib/account-remotes.js +4 -0
- package/lib/account-remotes.js.map +1 -1
- package/lib/accounts/database.js +86 -1
- package/lib/accounts/database.js.map +1 -1
- package/lib/accounts/file.js.map +1 -1
- package/lib/accounts/store.js +111 -5
- package/lib/accounts/store.js.map +1 -1
- package/lib/accounts/token.js +8 -2
- package/lib/accounts/token.js.map +1 -1
- package/lib/admin/metrics.js +15 -2
- package/lib/admin/metrics.js.map +1 -1
- package/lib/admin/read-budget.js +28 -0
- package/lib/admin/read-budget.js.map +1 -0
- package/lib/admin/service.js +135 -40
- package/lib/admin/service.js.map +1 -1
- package/lib/admin/session-log.js +63 -4
- package/lib/admin/session-log.js.map +1 -1
- package/lib/attachment-access.js +147 -0
- package/lib/attachment-access.js.map +1 -0
- package/lib/chat-sharing/native-fence.js +20 -0
- package/lib/chat-sharing/native-fence.js.map +1 -1
- package/lib/chat-sharing/search.js +89 -0
- package/lib/chat-sharing/search.js.map +1 -0
- package/lib/chat-sharing/transport.js +10 -9
- package/lib/chat-sharing/transport.js.map +1 -1
- package/lib/chat-sharing/uploads.js +79 -6
- package/lib/chat-sharing/uploads.js.map +1 -1
- package/lib/cli.js +13 -0
- package/lib/cli.js.map +1 -1
- package/lib/client/QaAccountsController.js +61 -0
- package/lib/client/QaAccountsController.js.map +1 -1
- package/lib/client/QaRouteController.js +3 -1
- package/lib/client/QaRouteController.js.map +1 -1
- package/lib/client/QaSessionController.js +141 -35
- package/lib/client/QaSessionController.js.map +1 -1
- package/lib/client/QaSurface.js +43 -13
- package/lib/client/QaSurface.js.map +1 -1
- package/lib/client/account-sessions.js +74 -8
- package/lib/client/account-sessions.js.map +1 -1
- package/lib/client/admin/CapabilityPicker.js +50 -0
- package/lib/client/admin/CapabilityPicker.js.map +1 -0
- package/lib/client/admin/CoverageNotice.js +9 -0
- package/lib/client/admin/CoverageNotice.js.map +1 -0
- package/lib/client/admin/QaAdmin.js +3 -150
- package/lib/client/admin/QaAdmin.js.map +1 -1
- package/lib/client/admin/RoleEditor.js +46 -0
- package/lib/client/admin/RoleEditor.js.map +1 -0
- package/lib/client/admin/capability-selection.js +63 -0
- package/lib/client/admin/capability-selection.js.map +1 -0
- package/lib/client/admin/pages/Conversations.js +35 -17
- package/lib/client/admin/pages/Conversations.js.map +1 -1
- package/lib/client/admin/pages/Overview.js +3 -2
- package/lib/client/admin/pages/Overview.js.map +1 -1
- package/lib/client/admin/pages/Quality.js +77 -20
- package/lib/client/admin/pages/Quality.js.map +1 -1
- package/lib/client/admin/pages/SkillFiles.js +14 -3
- package/lib/client/admin/pages/SkillFiles.js.map +1 -1
- package/lib/client/admin/shared.js +68 -3
- package/lib/client/admin/shared.js.map +1 -1
- package/lib/client/components/QaAuthGate.js +31 -28
- package/lib/client/components/QaAuthGate.js.map +1 -1
- package/lib/client/components/QaChangelog.js +71 -0
- package/lib/client/components/QaChangelog.js.map +1 -1
- package/lib/client/components/QaComposer.js +27 -4
- package/lib/client/components/QaComposer.js.map +1 -1
- package/lib/client/components/QaFormItem.js +63 -0
- package/lib/client/components/QaFormItem.js.map +1 -0
- package/lib/client/components/QaInputForm.js +281 -0
- package/lib/client/components/QaInputForm.js.map +1 -0
- package/lib/client/components/QaModal.js +17 -4
- package/lib/client/components/QaModal.js.map +1 -1
- package/lib/client/components/QaRequestWait.js +15 -0
- package/lib/client/components/QaRequestWait.js.map +1 -0
- package/lib/client/forms-styles.js +14 -0
- package/lib/client/forms-styles.js.map +1 -0
- package/lib/client/forms.js +40 -0
- package/lib/client/forms.js.map +1 -0
- package/lib/client/index.js +18 -2
- package/lib/client/index.js.map +1 -1
- package/lib/client/oidc-browser.js +39 -0
- package/lib/client/oidc-browser.js.map +1 -0
- package/lib/client/panels/PanelHost.js +23 -1
- package/lib/client/panels/PanelHost.js.map +1 -1
- package/lib/client/project-session-state.js.map +1 -1
- package/lib/client/request-wait-styles.js +7 -0
- package/lib/client/request-wait-styles.js.map +1 -0
- package/lib/client/settings/sections/interface.js +2 -1
- package/lib/client/settings/sections/interface.js.map +1 -1
- package/lib/client/settings/sections/status.js +3 -1
- package/lib/client/settings/sections/status.js.map +1 -1
- package/lib/client/styles.js +5 -3
- package/lib/client/styles.js.map +1 -1
- package/lib/client/types.js.map +1 -1
- package/lib/client.js +471 -72918
- package/lib/client.js.map +1 -1
- package/lib/config-resolvers/accounts.js +5 -4
- package/lib/config-resolvers/accounts.js.map +1 -1
- package/lib/config-resolvers/defaults.js +16 -0
- package/lib/config-resolvers/defaults.js.map +1 -1
- package/lib/config-resolvers/oidc.js +53 -0
- package/lib/config-resolvers/oidc.js.map +1 -0
- package/lib/config.js +71 -0
- package/lib/config.js.map +1 -1
- package/lib/filesystem-intent.js +39 -0
- package/lib/filesystem-intent.js.map +1 -0
- package/lib/forms/attachments.js +230 -0
- package/lib/forms/attachments.js.map +1 -0
- package/lib/forms/errors.js +37 -0
- package/lib/forms/errors.js.map +1 -0
- package/lib/forms/fingerprint.js +21 -0
- package/lib/forms/fingerprint.js.map +1 -0
- package/lib/forms/gate.js +125 -0
- package/lib/forms/gate.js.map +1 -0
- package/lib/forms/schema.js +182 -0
- package/lib/forms/schema.js.map +1 -0
- package/lib/forms/service.js +220 -0
- package/lib/forms/service.js.map +1 -0
- package/lib/forms/store.js +194 -0
- package/lib/forms/store.js.map +1 -0
- package/lib/forms/types.js +2 -0
- package/lib/forms/types.js.map +1 -0
- package/lib/forms/validation.js +113 -0
- package/lib/forms/validation.js.map +1 -0
- package/lib/index.js +271 -81
- package/lib/index.js.map +1 -1
- package/lib/oidc/http.js +30 -0
- package/lib/oidc/http.js.map +1 -0
- package/lib/oidc/service.js +250 -0
- package/lib/oidc/service.js.map +1 -0
- package/lib/qa-tools/activation-manager.js +24 -2
- package/lib/qa-tools/activation-manager.js.map +1 -1
- package/lib/qa-tools/catalog.js +11 -1
- package/lib/qa-tools/catalog.js.map +1 -1
- package/lib/qa-tools/index.js +27 -0
- package/lib/qa-tools/index.js.map +1 -1
- package/lib/qa-tools/request-user-form.js +121 -0
- package/lib/qa-tools/request-user-form.js.map +1 -0
- package/lib/request-scheduler.js +235 -0
- package/lib/request-scheduler.js.map +1 -0
- package/lib/resolve-config.js +2 -0
- package/lib/resolve-config.js.map +1 -1
- package/lib/secure-session.js +60 -60
- package/lib/secure-session.js.map +1 -1
- package/lib/session-create.js +125 -0
- package/lib/session-create.js.map +1 -0
- package/lib/typert.host.js +4636 -1103
- package/lib/typert.remote-client.d.ts +135 -5
- package/lib/typert.remote-client.js +4502 -1095
- package/lib/types/accounts/database.d.ts +8 -0
- package/lib/types/accounts/file.d.ts +2 -0
- package/lib/types/accounts/store.d.ts +17 -0
- package/lib/types/accounts/token.d.ts +3 -1
- package/lib/types/admin/metrics.d.ts +5 -1
- package/lib/types/admin/read-budget.d.ts +8 -0
- package/lib/types/admin/service.d.ts +17 -0
- package/lib/types/admin/session-log.d.ts +30 -1
- package/lib/types/attachment-access.d.ts +29 -0
- package/lib/types/chat-sharing/search.d.ts +5 -0
- package/lib/types/chat-sharing/uploads.d.ts +2 -1
- package/lib/types/client/QaAccountsController.d.ts +4 -0
- package/lib/types/client/QaRouteController.d.ts +1 -0
- package/lib/types/client/QaSessionController.d.ts +24 -12
- package/lib/types/client/QaSurface.d.ts +2 -0
- package/lib/types/client/admin/CapabilityPicker.d.ts +14 -0
- package/lib/types/client/admin/CoverageNotice.d.ts +6 -0
- package/lib/types/client/admin/RoleEditor.d.ts +15 -0
- package/lib/types/client/admin/capability-selection.d.ts +27 -0
- package/lib/types/client/admin/shared.d.ts +39 -1
- package/lib/types/client/components/QaAuthGate.d.ts +2 -0
- package/lib/types/client/components/QaFormItem.d.ts +20 -0
- package/lib/types/client/components/QaInputForm.d.ts +18 -0
- package/lib/types/client/components/QaRequestWait.d.ts +6 -0
- package/lib/types/client/forms-styles.d.ts +2 -0
- package/lib/types/client/forms.d.ts +23 -0
- package/lib/types/client/oidc-browser.d.ts +12 -0
- package/lib/types/client/project-session-state.d.ts +2 -4
- package/lib/types/client/request-wait-styles.d.ts +2 -0
- package/lib/types/client/types.d.ts +10 -4
- package/lib/types/config-resolvers/oidc.d.ts +3 -0
- package/lib/types/filesystem-intent.d.ts +12 -0
- package/lib/types/forms/attachments.d.ts +51 -0
- package/lib/types/forms/errors.d.ts +11 -0
- package/lib/types/forms/fingerprint.d.ts +9 -0
- package/lib/types/forms/gate.d.ts +33 -0
- package/lib/types/forms/schema.d.ts +72 -0
- package/lib/types/forms/service.d.ts +15 -0
- package/lib/types/forms/store.d.ts +25 -0
- package/lib/types/forms/types.d.ts +215 -0
- package/lib/types/forms/validation.d.ts +20 -0
- package/lib/types/index.d.ts +33 -12
- package/lib/types/oidc/http.d.ts +5 -0
- package/lib/types/oidc/service.d.ts +30 -0
- package/lib/types/oidc.js +2 -0
- package/lib/types/oidc.js.map +1 -0
- package/lib/types/qa-tools/activation-manager.d.ts +5 -0
- package/lib/types/qa-tools/catalog.d.ts +4 -1
- package/lib/types/qa-tools/index.d.ts +5 -1
- package/lib/types/qa-tools/request-user-form.d.ts +5 -0
- package/lib/types/request-scheduler.d.ts +28 -0
- package/lib/types/secure-session.d.ts +13 -26
- package/lib/types/session-create.d.ts +29 -0
- package/lib/types/types/accounts.d.ts +2 -0
- package/lib/types/types/admin.d.ts +6 -0
- package/lib/types/types/config.d.ts +6 -0
- package/lib/types/types/oidc.d.ts +36 -0
- package/lib/types/types/session.d.ts +8 -0
- package/lib/types/types.d.ts +2 -0
- package/lib/types/user-workspace.d.ts +4 -6
- package/lib/user-workspace.js +13 -67
- package/lib/user-workspace.js.map +1 -1
- package/package.json +66 -62
|
@@ -36,15 +36,17 @@ const CONFIGURATION_ERROR = "Настройки помощника недост
|
|
|
36
36
|
const SLASH_CATALOG_STALE_MS = 3_000;
|
|
37
37
|
/**
|
|
38
38
|
* How long a refused retain waits for its row to come into this browser's
|
|
39
|
-
* Session catalog.
|
|
40
|
-
* row arrives
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
39
|
+
* Session catalog. Shorter than the adoption timeout, but wide enough for the
|
|
40
|
+
* way the row actually arrives on a QA deployment: this surface has no
|
|
41
|
+
* forwarded-event push of its own — the row rides on the account's chat
|
|
42
|
+
* listing — and a listing that lands right after the create pays for the
|
|
43
|
+
* Host's shared catalog scan the create itself invalidated, which on a loaded
|
|
44
|
+
* stand takes longer than a round trip. The interval keeps asking for the
|
|
45
|
+
* listing while this waits, so the window only has to outlast one slow scan.
|
|
46
|
+
* A row that misses it loses the chat, and the plate's «Повторить» re-runs
|
|
47
|
+
* the adoption on a catalog that has had the pull.
|
|
46
48
|
*/
|
|
47
|
-
const CATALOG_ROW_PATIENCE_MS =
|
|
49
|
+
const CATALOG_ROW_PATIENCE_MS = 15_000;
|
|
48
50
|
/**
|
|
49
51
|
* Spacing of the parked-request poll. A request is answered by a person, so a
|
|
50
52
|
* second of latency is invisible; the poll only runs while a turn runs.
|
|
@@ -129,7 +131,10 @@ export class QaSessionController {
|
|
|
129
131
|
unsubscribeConnection;
|
|
130
132
|
ensuring;
|
|
131
133
|
materializing;
|
|
134
|
+
/** A successful create survives a failed adoption; retry must not create again. */
|
|
135
|
+
draftSessionId;
|
|
132
136
|
operationError = null;
|
|
137
|
+
operationDiagnostic = null;
|
|
133
138
|
admissionPending = false;
|
|
134
139
|
pendingSubmission;
|
|
135
140
|
pendingSequence = 0;
|
|
@@ -170,9 +175,9 @@ export class QaSessionController {
|
|
|
170
175
|
* both a failed adoption and {@link unbind} take it back. The converse does
|
|
171
176
|
* not hold: a held session may name nothing, which is the transcript of an
|
|
172
177
|
* attestation this chat could not pass and whose caller still reads it. So a
|
|
173
|
-
* chat whose first send fell short keeps its identity while naming no session
|
|
174
|
-
*
|
|
175
|
-
* was typed into
|
|
178
|
+
* chat whose first send fell short keeps its identity while naming no session;
|
|
179
|
+
* a retry adopts the successful create it already holds and preserves the
|
|
180
|
+
* composer the question was typed into.
|
|
176
181
|
*/
|
|
177
182
|
namedSession = null;
|
|
178
183
|
/**
|
|
@@ -193,6 +198,10 @@ export class QaSessionController {
|
|
|
193
198
|
/** Host-side question requests of the bound chat waiting for the operator. */
|
|
194
199
|
hostQuestions;
|
|
195
200
|
queueRemote;
|
|
201
|
+
requestRemote;
|
|
202
|
+
requestWaitStatus = null;
|
|
203
|
+
requestReadRevision = 0;
|
|
204
|
+
requestReading = false;
|
|
196
205
|
/** Last send the stand had no room for; drives the request-ceiling dialog. */
|
|
197
206
|
requestQueueNotice = null;
|
|
198
207
|
/** Set while a running turn is polled for parked requests. */
|
|
@@ -261,6 +270,7 @@ export class QaSessionController {
|
|
|
261
270
|
this.hostApprovals = new QaHostApprovalBridge(options.approvalApi);
|
|
262
271
|
this.hostQuestions = new QaHostQuestionBridge(options.questionApi);
|
|
263
272
|
this.queueRemote = options.queueStatus;
|
|
273
|
+
this.requestRemote = options.requestStatus;
|
|
264
274
|
// Cold account catalogs can outlive one short network round trip.
|
|
265
275
|
// Keep bootstrap bounded without rejecting a valid slow restoration.
|
|
266
276
|
this.timeoutMs = options.timeoutMs ?? 60_000;
|
|
@@ -287,6 +297,16 @@ export class QaSessionController {
|
|
|
287
297
|
this.ensuring = operation;
|
|
288
298
|
return operation;
|
|
289
299
|
}
|
|
300
|
+
/** Retry opening a failed draft without abandoning its successful create. */
|
|
301
|
+
async retrySession() {
|
|
302
|
+
if (this.drafting)
|
|
303
|
+
await this.materializeDraft();
|
|
304
|
+
else
|
|
305
|
+
await this.ensureSession();
|
|
306
|
+
}
|
|
307
|
+
failureDiagnostic() {
|
|
308
|
+
return this.operationError === null ? null : this.operationDiagnostic;
|
|
309
|
+
}
|
|
290
310
|
/**
|
|
291
311
|
* Select a different capability profile for the next agent. The current
|
|
292
312
|
* agent is never mutated: even a blank materialized chat is abandoned in
|
|
@@ -415,7 +435,7 @@ export class QaSessionController {
|
|
|
415
435
|
// the submission rides exactly the way it always did.
|
|
416
436
|
if (this.disposed || (bound !== undefined && this.session !== bound))
|
|
417
437
|
return false;
|
|
418
|
-
if (ceiling !== null && ceiling.full) {
|
|
438
|
+
if (ceiling !== null && ceiling.full && ceiling.managed !== true) {
|
|
419
439
|
this.requestQueueNotice = ceiling;
|
|
420
440
|
this.publish();
|
|
421
441
|
return false;
|
|
@@ -538,10 +558,8 @@ export class QaSessionController {
|
|
|
538
558
|
/**
|
|
539
559
|
* What the stand answers about its load, or null when it does not answer.
|
|
540
560
|
*
|
|
541
|
-
*
|
|
542
|
-
*
|
|
543
|
-
* unreadable count says nothing about the load, and refusing a visitor on
|
|
544
|
-
* that would be a worse answer than the one they would have got.
|
|
561
|
+
* A failed status read does not bypass the server queue. Submit normally;
|
|
562
|
+
* the Host owns admission even when the browser cannot read its load.
|
|
545
563
|
*/
|
|
546
564
|
async readQueueStatus() {
|
|
547
565
|
if (this.queueRemote === undefined)
|
|
@@ -555,6 +573,35 @@ export class QaSessionController {
|
|
|
555
573
|
return null;
|
|
556
574
|
}
|
|
557
575
|
}
|
|
576
|
+
async refreshRequestStatus(sessionId, token) {
|
|
577
|
+
if (this.requestRemote === undefined || this.requestReading)
|
|
578
|
+
return;
|
|
579
|
+
this.requestReading = true;
|
|
580
|
+
const revision = this.requestReadRevision;
|
|
581
|
+
try {
|
|
582
|
+
const result = await this.requestRemote(token, sessionId);
|
|
583
|
+
if (this.disposed ||
|
|
584
|
+
revision !== this.requestReadRevision ||
|
|
585
|
+
String(this.session?.sessionId) !== sessionId)
|
|
586
|
+
return;
|
|
587
|
+
if (!result.ok)
|
|
588
|
+
return;
|
|
589
|
+
const status = result.value.managed === true && result.value.waiting === true
|
|
590
|
+
? result.value
|
|
591
|
+
: null;
|
|
592
|
+
if (JSON.stringify(status) === JSON.stringify(this.requestWaitStatus))
|
|
593
|
+
return;
|
|
594
|
+
this.requestWaitStatus = status;
|
|
595
|
+
this.publish();
|
|
596
|
+
}
|
|
597
|
+
catch {
|
|
598
|
+
// Keep the last known position during a transient transport failure.
|
|
599
|
+
}
|
|
600
|
+
finally {
|
|
601
|
+
if (revision === this.requestReadRevision)
|
|
602
|
+
this.requestReading = false;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
558
605
|
/**
|
|
559
606
|
* Close the request-ceiling dialog. Nothing is retried and nothing is
|
|
560
607
|
* dropped: the held-back question is still the composer's own text, so asking
|
|
@@ -948,6 +995,7 @@ export class QaSessionController {
|
|
|
948
995
|
enterDraft() {
|
|
949
996
|
this.generation += 1;
|
|
950
997
|
this.materializing = undefined;
|
|
998
|
+
this.draftSessionId = undefined;
|
|
951
999
|
this.drafting = true;
|
|
952
1000
|
if (this.namedSession !== null) {
|
|
953
1001
|
this.openChat();
|
|
@@ -985,14 +1033,17 @@ export class QaSessionController {
|
|
|
985
1033
|
const attempt = (async () => {
|
|
986
1034
|
try {
|
|
987
1035
|
await this.waitForConnection();
|
|
988
|
-
const id =
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1036
|
+
const id = this.draftSessionId ??
|
|
1037
|
+
(await createQaSession({
|
|
1038
|
+
createSession: this.createSessionRemote,
|
|
1039
|
+
token: this.accounts?.token() ?? "",
|
|
1040
|
+
subroleId: this.selectedSubrole,
|
|
1041
|
+
adminPreview: this.adminPreview,
|
|
1042
|
+
}));
|
|
994
1043
|
if (this.disposed || operation !== this.generation)
|
|
995
1044
|
return false;
|
|
1045
|
+
this.draftSessionId = id;
|
|
1046
|
+
this.sessions.rememberAdmitted?.(id);
|
|
996
1047
|
// A step back is not a materialized draft: this adoption never ended up
|
|
997
1048
|
// holding a session, so the draft has to stay a draft. Calling it done
|
|
998
1049
|
// leaves a chat with neither a session nor a draft behind, and every
|
|
@@ -1002,6 +1053,7 @@ export class QaSessionController {
|
|
|
1002
1053
|
if (this.disposed || operation !== this.generation)
|
|
1003
1054
|
return false;
|
|
1004
1055
|
this.drafting = false;
|
|
1056
|
+
this.draftSessionId = undefined;
|
|
1005
1057
|
if (this.config.session.policy === "browser-persistent") {
|
|
1006
1058
|
this.chats.saveActive(id);
|
|
1007
1059
|
}
|
|
@@ -1314,7 +1366,36 @@ export class QaSessionController {
|
|
|
1314
1366
|
this.emit();
|
|
1315
1367
|
try {
|
|
1316
1368
|
await this.waitForConnection();
|
|
1317
|
-
const
|
|
1369
|
+
const saved = this.config.session.policy === "browser-persistent"
|
|
1370
|
+
? this.chats.activeId()
|
|
1371
|
+
: null;
|
|
1372
|
+
let admitted = null;
|
|
1373
|
+
const savedSummary = saved === null
|
|
1374
|
+
? undefined
|
|
1375
|
+
: this.sessions.list.getSnapshot().byId[saved];
|
|
1376
|
+
if (saved !== null &&
|
|
1377
|
+
this.accounts !== undefined &&
|
|
1378
|
+
this.sharingApi !== undefined &&
|
|
1379
|
+
this.sessions.rememberAdmitted !== undefined &&
|
|
1380
|
+
(savedSummary === undefined || !isDelegatedSession(savedSummary))) {
|
|
1381
|
+
const identity = this.accounts.token();
|
|
1382
|
+
if (identity !== null) {
|
|
1383
|
+
const access = await this.sharingApi.access(identity, saved);
|
|
1384
|
+
if (this.disposed ||
|
|
1385
|
+
operation !== this.generation ||
|
|
1386
|
+
identity !== this.accounts.token())
|
|
1387
|
+
return;
|
|
1388
|
+
if (access.ok && access.value.sessionId === saved) {
|
|
1389
|
+
// A direct Host read admission is sufficient to reopen this exact
|
|
1390
|
+
// chat. A cold deployment-wide catalog scan must not gate restore.
|
|
1391
|
+
this.sessions.rememberAdmitted(saved);
|
|
1392
|
+
admitted = saved;
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
const list = admitted === null
|
|
1397
|
+
? await waitFor(this.sessions.list, (snapshot) => snapshot.phase === "ready", this.timeoutMs)
|
|
1398
|
+
: this.sessions.list.getSnapshot();
|
|
1318
1399
|
let id = null;
|
|
1319
1400
|
let restored = false;
|
|
1320
1401
|
let existing = false;
|
|
@@ -1335,8 +1416,8 @@ export class QaSessionController {
|
|
|
1335
1416
|
// falls through to a draft instead of restoring a transcript nobody can
|
|
1336
1417
|
// send into.
|
|
1337
1418
|
if (stored !== null &&
|
|
1338
|
-
|
|
1339
|
-
|
|
1419
|
+
(stored === admitted ||
|
|
1420
|
+
(summary !== undefined && !isDelegatedSession(summary)))) {
|
|
1340
1421
|
id = stored;
|
|
1341
1422
|
restored = true;
|
|
1342
1423
|
existing = true;
|
|
@@ -1603,7 +1684,7 @@ export class QaSessionController {
|
|
|
1603
1684
|
// chat: it takes its own identity, so nothing the previous chat was holding
|
|
1604
1685
|
// — an unsent question, staged attachments, an open drawer — walks into it.
|
|
1605
1686
|
// A chat that names no session adopts under the identity it already has,
|
|
1606
|
-
// which is what keeps a retried first send
|
|
1687
|
+
// which is what keeps a retried first send in the
|
|
1607
1688
|
// composer the question was typed into.
|
|
1608
1689
|
if (this.namedSession !== null && this.namedSession !== id)
|
|
1609
1690
|
this.openChat();
|
|
@@ -1841,6 +1922,9 @@ export class QaSessionController {
|
|
|
1841
1922
|
// answers for its own sends.
|
|
1842
1923
|
this.requestQueueNotice = null;
|
|
1843
1924
|
this.policyReady = false;
|
|
1925
|
+
this.requestWaitStatus = null;
|
|
1926
|
+
this.requestReadRevision += 1;
|
|
1927
|
+
this.requestReading = false;
|
|
1844
1928
|
this.inheritedThroughSeq = undefined;
|
|
1845
1929
|
this.compatibilityReadOnly = false;
|
|
1846
1930
|
// The catalog belongs to the chat that produced it: another chat has a
|
|
@@ -1897,7 +1981,7 @@ export class QaSessionController {
|
|
|
1897
1981
|
? "reconnecting"
|
|
1898
1982
|
: "idle",
|
|
1899
1983
|
error: this.operationError,
|
|
1900
|
-
requestQueue: this.requestQueueNotice,
|
|
1984
|
+
requestQueue: this.requestWaitStatus ?? this.requestQueueNotice,
|
|
1901
1985
|
canSend: connected && !materializing,
|
|
1902
1986
|
pendingMessage: this.pendingSubmission?.message ?? null,
|
|
1903
1987
|
chatsRevision: this.chatsRevision,
|
|
@@ -1911,7 +1995,7 @@ export class QaSessionController {
|
|
|
1911
1995
|
...this.state,
|
|
1912
1996
|
phase: !connected && this.connectedOnce ? "reconnecting" : this.state.phase,
|
|
1913
1997
|
error: this.operationError ?? this.state.error,
|
|
1914
|
-
requestQueue: this.requestQueueNotice,
|
|
1998
|
+
requestQueue: this.requestWaitStatus ?? this.requestQueueNotice,
|
|
1915
1999
|
};
|
|
1916
2000
|
this.emit();
|
|
1917
2001
|
return;
|
|
@@ -1949,7 +2033,7 @@ export class QaSessionController {
|
|
|
1949
2033
|
// carries its owner's name when an admin reads it.
|
|
1950
2034
|
author: this.accounts?.messageAuthorOf(sessionId),
|
|
1951
2035
|
operationError: this.operationError,
|
|
1952
|
-
requestQueue: this.requestQueueNotice,
|
|
2036
|
+
requestQueue: this.requestWaitStatus ?? this.requestQueueNotice,
|
|
1953
2037
|
policyReady: this.policyReady,
|
|
1954
2038
|
compatibilityReadOnly: this.compatibilityReadOnly,
|
|
1955
2039
|
admissionPending: this.admissionPending || this.pendingSubmission !== undefined,
|
|
@@ -2124,13 +2208,28 @@ export class QaSessionController {
|
|
|
2124
2208
|
this.publish();
|
|
2125
2209
|
}
|
|
2126
2210
|
fail(message, error) {
|
|
2127
|
-
// Policy attestation refusals already logged their precise reason; a
|
|
2128
|
-
// second stack trace for the wrapper error is only console noise.
|
|
2129
|
-
if (!(error instanceof QaPolicyAttestationError)) {
|
|
2130
|
-
console.error("dsh-qa-surface: session operation failed", error);
|
|
2131
|
-
}
|
|
2132
2211
|
if (this.disposed)
|
|
2133
2212
|
return;
|
|
2213
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
2214
|
+
const reason = /transport|network|fetch|HTTP|connection|timed? ?out|timeout/iu.test(detail)
|
|
2215
|
+
? "Не удалось связаться со стендом или дождаться его ответа."
|
|
2216
|
+
: /sessions\.retain|unknown session|catalog/iu.test(detail)
|
|
2217
|
+
? "Стенд создал чат, но его запись ещё недоступна в истории."
|
|
2218
|
+
: error instanceof QaPolicyAttestationError
|
|
2219
|
+
? "Настройки чата не прошли проверку политики стенда."
|
|
2220
|
+
: "Стенд отклонил операцию. Подробности доступны оператору по коду обращения.";
|
|
2221
|
+
// A diagnostic label, not an authority token. This also works on a LAN
|
|
2222
|
+
// HTTP origin where crypto.randomUUID is unavailable.
|
|
2223
|
+
const id = `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
2224
|
+
this.operationDiagnostic = { reason, id };
|
|
2225
|
+
if (error instanceof QaPolicyAttestationError) {
|
|
2226
|
+
// The policy probe already logged the precise refusal without a wrapper
|
|
2227
|
+
// stack. Add only the reference the user sees on the plate.
|
|
2228
|
+
console.info("dsh-qa-surface: policy failure reference", { id });
|
|
2229
|
+
}
|
|
2230
|
+
else {
|
|
2231
|
+
console.error("dsh-qa-surface: session operation failed", error, { id });
|
|
2232
|
+
}
|
|
2134
2233
|
this.operationError = message;
|
|
2135
2234
|
this.state = {
|
|
2136
2235
|
...QA_SESSION_IDLE_STATE,
|
|
@@ -2281,13 +2380,18 @@ export class QaSessionController {
|
|
|
2281
2380
|
: false;
|
|
2282
2381
|
const pending = this.hostApprovals.list().length > 0 ||
|
|
2283
2382
|
this.hostQuestions.list().length > 0;
|
|
2284
|
-
const
|
|
2383
|
+
const queue = this.requestRemote !== undefined &&
|
|
2384
|
+
this.config.session.maxActiveRequests > 0;
|
|
2385
|
+
const wanted = ((running && (approvals || questions || queue)) || pending) &&
|
|
2285
2386
|
// A subagent watched from the panel is a read-only view of a session the
|
|
2286
2387
|
// chat owns: its own requests belong to the chat, not to this binding.
|
|
2287
2388
|
this.viewingSubagent === null &&
|
|
2288
2389
|
this.session !== undefined;
|
|
2289
2390
|
if (!wanted) {
|
|
2290
2391
|
this.stopPendingPolling();
|
|
2392
|
+
this.requestWaitStatus = null;
|
|
2393
|
+
this.requestReadRevision += 1;
|
|
2394
|
+
this.requestReading = false;
|
|
2291
2395
|
return;
|
|
2292
2396
|
}
|
|
2293
2397
|
if (this.pendingTimer !== undefined || this.session === undefined)
|
|
@@ -2295,6 +2399,8 @@ export class QaSessionController {
|
|
|
2295
2399
|
const sessionId = String(this.session.sessionId);
|
|
2296
2400
|
const token = this.hostToken();
|
|
2297
2401
|
const tick = () => {
|
|
2402
|
+
if (queue)
|
|
2403
|
+
void this.refreshRequestStatus(sessionId, token);
|
|
2298
2404
|
// A seam still on screen is re-read even where its config gate has since
|
|
2299
2405
|
// closed: the Host is the authority on what it holds, and an empty answer
|
|
2300
2406
|
// is what takes a settled request off the screen.
|