@yadsh/dsh-qa-surface 0.15.7 → 0.16.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 +8 -0
- package/lib/access/capability-catalog.js +11 -4
- package/lib/access/capability-catalog.js.map +1 -1
- package/lib/access/service.js +46 -5
- package/lib/access/service.js.map +1 -1
- package/lib/accounts/store.js +2 -2
- package/lib/accounts/store.js.map +1 -1
- package/lib/admin/service.js +4 -2
- package/lib/admin/service.js.map +1 -1
- package/lib/admin/session-log.js +3 -0
- package/lib/admin/session-log.js.map +1 -1
- package/lib/agent-scope.js +9 -0
- package/lib/agent-scope.js.map +1 -0
- package/lib/chat-sharing/native-fence.js +111 -0
- package/lib/chat-sharing/native-fence.js.map +1 -0
- package/lib/chat-sharing/resources.js +57 -0
- package/lib/chat-sharing/resources.js.map +1 -0
- package/lib/chat-sharing/store.js +193 -0
- package/lib/chat-sharing/store.js.map +1 -0
- package/lib/chat-sharing/transport.js +274 -0
- package/lib/chat-sharing/transport.js.map +1 -0
- package/lib/chat-sharing/turn-guard.js +154 -0
- package/lib/chat-sharing/turn-guard.js.map +1 -0
- package/lib/chat-sharing/types.js +2 -0
- package/lib/chat-sharing/types.js.map +1 -0
- package/lib/chat-sharing/uploads.js +55 -0
- package/lib/chat-sharing/uploads.js.map +1 -0
- package/lib/client/QaAccountsController.js +29 -2
- package/lib/client/QaAccountsController.js.map +1 -1
- package/lib/client/QaConfigController.js +7 -12
- package/lib/client/QaConfigController.js.map +1 -1
- package/lib/client/QaSessionController.js +186 -19
- package/lib/client/QaSessionController.js.map +1 -1
- package/lib/client/QaSurface.js +110 -9
- package/lib/client/QaSurface.js.map +1 -1
- package/lib/client/QaSurfaceGuard.js +6 -1
- package/lib/client/QaSurfaceGuard.js.map +1 -1
- package/lib/client/QaTranscriptAdapter.js +13 -3
- package/lib/client/QaTranscriptAdapter.js.map +1 -1
- package/lib/client/account-conversation.js +33 -0
- package/lib/client/account-conversation.js.map +1 -0
- package/lib/client/account-sessions.js +106 -0
- package/lib/client/account-sessions.js.map +1 -0
- package/lib/client/account-upload.js +30 -0
- package/lib/client/account-upload.js.map +1 -0
- package/lib/client/chat-artifacts.js +17 -3
- package/lib/client/chat-artifacts.js.map +1 -1
- package/lib/client/chat-sharing-api.js +2 -0
- package/lib/client/chat-sharing-api.js.map +1 -0
- package/lib/client/components/QaChangelog.js +57 -3
- package/lib/client/components/QaChangelog.js.map +1 -1
- package/lib/client/components/QaChatSharing.js +92 -0
- package/lib/client/components/QaChatSharing.js.map +1 -0
- package/lib/client/components/QaComposer.js +8 -4
- package/lib/client/components/QaComposer.js.map +1 -1
- package/lib/client/components/QaFileAttachment.js +12 -3
- package/lib/client/components/QaFileAttachment.js.map +1 -1
- package/lib/client/components/QaFilesPanel.js +11 -4
- package/lib/client/components/QaFilesPanel.js.map +1 -1
- package/lib/client/components/QaHeader.js +3 -3
- package/lib/client/components/QaHeader.js.map +1 -1
- package/lib/client/components/QaMessage.js +34 -9
- package/lib/client/components/QaMessage.js.map +1 -1
- package/lib/client/components/QaSidebar.js +1 -1
- package/lib/client/components/QaSidebar.js.map +1 -1
- package/lib/client/components/QaSourcesPanel.js +12 -4
- package/lib/client/components/QaSourcesPanel.js.map +1 -1
- package/lib/client/components/QaWorkspaceBrowser.js +28 -7
- package/lib/client/components/QaWorkspaceBrowser.js.map +1 -1
- package/lib/client/components/source-refs.js +42 -23
- package/lib/client/components/source-refs.js.map +1 -1
- package/lib/client/index.js +60 -6
- package/lib/client/index.js.map +1 -1
- package/lib/client/lineage.js +4 -3
- package/lib/client/lineage.js.map +1 -1
- package/lib/client/markdown/katex-css.js +1 -1
- package/lib/client/markdown/render.js +3 -4
- package/lib/client/markdown/render.js.map +1 -1
- package/lib/client/markdown/source-chip.js +4 -1
- package/lib/client/markdown/source-chip.js.map +1 -1
- package/lib/client/notifications/notification-dispatcher.js +50 -11
- package/lib/client/notifications/notification-dispatcher.js.map +1 -1
- package/lib/client/notifications/use-turn-notifications.js +107 -18
- package/lib/client/notifications/use-turn-notifications.js.map +1 -1
- package/lib/client/project-session-state.js +28 -13
- package/lib/client/project-session-state.js.map +1 -1
- package/lib/client/session-admission.js +9 -1
- package/lib/client/session-admission.js.map +1 -1
- package/lib/client/session-sources.js +44 -3
- package/lib/client/session-sources.js.map +1 -1
- package/lib/client/slash/useSlashNavigation.js +8 -5
- package/lib/client/slash/useSlashNavigation.js.map +1 -1
- package/lib/client/source-preview.js +1 -1
- package/lib/client/source-preview.js.map +1 -1
- package/lib/client/styles.js +9 -2
- package/lib/client/styles.js.map +1 -1
- package/lib/client/turn-sources.js +2 -1
- package/lib/client/turn-sources.js.map +1 -1
- package/lib/client/types.js.map +1 -1
- package/lib/client/workspace-download.js +42 -2
- package/lib/client/workspace-download.js.map +1 -1
- package/lib/client.js +3329 -947
- package/lib/client.js.map +1 -1
- package/lib/enforcement/skill-policy.js +29 -4
- package/lib/enforcement/skill-policy.js.map +1 -1
- package/lib/enforcement/skill-resource-paths.js +32 -0
- package/lib/enforcement/skill-resource-paths.js.map +1 -0
- package/lib/enforcement/structured-result.js +38 -0
- package/lib/enforcement/structured-result.js.map +1 -0
- package/lib/enforcement/tool-grants.js +3 -1
- package/lib/enforcement/tool-grants.js.map +1 -1
- package/lib/index.js +397 -14
- package/lib/index.js.map +1 -1
- package/lib/prompt-notes.js +18 -1
- package/lib/prompt-notes.js.map +1 -1
- package/lib/provenance/collector.js +13 -1
- package/lib/provenance/collector.js.map +1 -1
- package/lib/provenance/connectors.js +32 -8
- package/lib/provenance/connectors.js.map +1 -1
- package/lib/provenance/dedupe.js +8 -1
- package/lib/provenance/dedupe.js.map +1 -1
- package/lib/provenance/document-preview.js +19 -1
- package/lib/provenance/document-preview.js.map +1 -1
- package/lib/provenance/extractors.js +67 -1
- package/lib/provenance/extractors.js.map +1 -1
- package/lib/provenance/file-download.js +56 -0
- package/lib/provenance/file-download.js.map +1 -0
- package/lib/provenance/file-preview.js +12 -1
- package/lib/provenance/file-preview.js.map +1 -1
- package/lib/provenance/host-store.js +59 -10
- package/lib/provenance/host-store.js.map +1 -1
- package/lib/provenance/integration-extractor.js +63 -0
- package/lib/provenance/integration-extractor.js.map +1 -0
- package/lib/qa-tools/docs-tools.js +14 -0
- package/lib/qa-tools/docs-tools.js.map +1 -1
- package/lib/secure-session.js +9 -3
- package/lib/secure-session.js.map +1 -1
- package/lib/session-fork-host.js +157 -0
- package/lib/session-fork-host.js.map +1 -0
- package/lib/session-fork.js +34 -0
- package/lib/session-fork.js.map +1 -0
- package/lib/session-ownership.js +5 -1
- package/lib/session-ownership.js.map +1 -1
- package/lib/shared/session-lineage.js +6 -0
- package/lib/shared/session-lineage.js.map +1 -0
- package/lib/slash/draft-target.js +21 -0
- package/lib/slash/draft-target.js.map +1 -0
- package/lib/slash/remotes.js +39 -4
- package/lib/slash/remotes.js.map +1 -1
- package/lib/typert.host.js +1633 -236
- package/lib/typert.remote-client.d.ts +48 -1
- package/lib/typert.remote-client.js +1477 -234
- package/lib/types/access/capability-catalog.d.ts +1 -1
- package/lib/types/access/service.d.ts +7 -0
- package/lib/types/access.js +2 -0
- package/lib/types/access.js.map +1 -0
- package/lib/types/accounts/store.d.ts +1 -1
- package/lib/types/accounts.js +2 -0
- package/lib/types/accounts.js.map +1 -0
- package/lib/types/admin/session-log.d.ts +2 -0
- package/lib/types/admin.js +2 -0
- package/lib/types/admin.js.map +1 -0
- package/lib/types/agent-scope.d.ts +4 -0
- package/lib/types/capability.js +2 -0
- package/lib/types/capability.js.map +1 -0
- package/lib/types/chat-sharing/native-fence.d.ts +11 -0
- package/lib/types/chat-sharing/resources.d.ts +5 -0
- package/lib/types/chat-sharing/store.d.ts +28 -0
- package/lib/types/chat-sharing/transport.d.ts +31 -0
- package/lib/types/chat-sharing/turn-guard.d.ts +21 -0
- package/lib/types/chat-sharing/types.d.ts +25 -0
- package/lib/types/chat-sharing/uploads.d.ts +5 -0
- package/lib/types/client/QaAccountsController.d.ts +3 -0
- package/lib/types/client/QaConfigController.d.ts +3 -2
- package/lib/types/client/QaSessionController.d.ts +17 -3
- package/lib/types/client/QaSurface.d.ts +6 -1
- package/lib/types/client/account-conversation.d.ts +6 -0
- package/lib/types/client/account-sessions.d.ts +7 -0
- package/lib/types/client/account-upload.d.ts +15 -0
- package/lib/types/client/chat-sharing-api.d.ts +35 -0
- package/lib/types/client/components/QaChatSharing.d.ts +10 -0
- package/lib/types/client/components/QaComposer.d.ts +2 -0
- package/lib/types/client/components/QaFileAttachment.d.ts +3 -2
- package/lib/types/client/components/QaFilesPanel.d.ts +4 -1
- package/lib/types/client/components/QaMessage.d.ts +5 -2
- package/lib/types/client/components/QaWorkspaceBrowser.d.ts +8 -1
- package/lib/types/client/components/source-refs.d.ts +1 -0
- package/lib/types/client/lineage.d.ts +2 -2
- package/lib/types/client/notifications/notification-dispatcher.d.ts +10 -6
- package/lib/types/client/notifications/use-turn-notifications.d.ts +4 -0
- package/lib/types/client/project-session-state.d.ts +1 -0
- package/lib/types/client/session-admission.d.ts +2 -0
- package/lib/types/client/session-sources.d.ts +1 -0
- package/lib/types/client/turn-sources.d.ts +1 -1
- package/lib/types/client/types.d.ts +8 -1
- package/lib/types/client/workspace-download.d.ts +9 -1
- package/lib/types/config.js +2 -0
- package/lib/types/config.js.map +1 -0
- package/lib/types/documents.js +2 -0
- package/lib/types/documents.js.map +1 -0
- package/lib/types/enforcement/skill-resource-paths.d.ts +5 -0
- package/lib/types/enforcement/structured-result.d.ts +11 -0
- package/lib/types/expert-memory.js +2 -0
- package/lib/types/expert-memory.js.map +1 -0
- package/lib/types/index.d.ts +35 -1
- package/lib/types/policy.js +2 -0
- package/lib/types/policy.js.map +1 -0
- package/lib/types/prompt-notes.d.ts +2 -0
- package/lib/types/provenance/connectors.d.ts +4 -1
- package/lib/types/provenance/document-preview.d.ts +2 -0
- package/lib/types/provenance/extractors.d.ts +2 -0
- package/lib/types/provenance/file-download.d.ts +10 -0
- package/lib/types/provenance/host-store.d.ts +2 -0
- package/lib/types/provenance/integration-extractor.d.ts +5 -0
- package/lib/types/provenance/types.d.ts +2 -0
- package/lib/types/qa-tools/docs-tools.d.ts +2 -0
- package/lib/types/quality.js +7 -0
- package/lib/types/quality.js.map +1 -0
- package/lib/types/session-fork-host.d.ts +27 -0
- package/lib/types/session-fork.d.ts +7 -0
- package/lib/types/session.js +2 -0
- package/lib/types/session.js.map +1 -0
- package/lib/types/shared/session-lineage.d.ts +7 -0
- package/lib/types/skills.js +2 -0
- package/lib/types/skills.js.map +1 -0
- package/lib/types/slash/draft-target.d.ts +7 -0
- package/lib/types/slash/remotes.d.ts +7 -0
- package/lib/types/slash.js +2 -0
- package/lib/types/slash.js.map +1 -0
- package/lib/types/types/access.d.ts +43 -0
- package/lib/types/types/accounts.d.ts +173 -0
- package/lib/types/types/admin.d.ts +286 -0
- package/lib/types/types/capability.d.ts +230 -0
- package/lib/types/types/config.d.ts +594 -0
- package/lib/types/types/documents.d.ts +34 -0
- package/lib/types/types/expert-memory.d.ts +35 -0
- package/lib/types/types/policy.d.ts +54 -0
- package/lib/types/types/quality.d.ts +100 -0
- package/lib/types/types/session.d.ts +382 -0
- package/lib/types/types/skills.d.ts +143 -0
- package/lib/types/types/slash.d.ts +159 -0
- package/lib/types/types.d.ts +13 -2180
- package/package.json +8 -7
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { bytesToBase64 } from "./base64.js";
|
|
2
2
|
import { qaStorageNamespace } from "../shared/session-key.js";
|
|
3
|
+
import { slashDraftTarget } from "../slash/draft-target.js";
|
|
3
4
|
import { resolveSlashRoute } from "./slash/slash-router.js";
|
|
4
5
|
import { SLASH_DISABLED_COPY, slashAttachmentsCopy, slashExecuteFailureCopy, slashHostRefusalCopy, slashRefusalCopy, slashWithheldCopy, } from "./slash/copy.js";
|
|
5
6
|
import { canOpenAsCompatibilityReadOnly, QaPolicyAttestationError, } from "./attestation.js";
|
|
@@ -98,6 +99,9 @@ export class QaSessionController {
|
|
|
98
99
|
listeners = new Set();
|
|
99
100
|
sessions;
|
|
100
101
|
createSessionRemote;
|
|
102
|
+
forkSessionRemote;
|
|
103
|
+
forking = false;
|
|
104
|
+
inheritedThroughSeq;
|
|
101
105
|
conversation;
|
|
102
106
|
connection;
|
|
103
107
|
config;
|
|
@@ -150,6 +154,9 @@ export class QaSessionController {
|
|
|
150
154
|
viewingSubagent = null;
|
|
151
155
|
connectedOnce;
|
|
152
156
|
disposed = false;
|
|
157
|
+
chatAccess;
|
|
158
|
+
sharingApi;
|
|
159
|
+
accessPoll;
|
|
153
160
|
generation = 0;
|
|
154
161
|
/**
|
|
155
162
|
* The session the current chat identity names, or null while the chat it
|
|
@@ -203,6 +210,7 @@ export class QaSessionController {
|
|
|
203
210
|
slashError = null;
|
|
204
211
|
slashLoadedAt = 0;
|
|
205
212
|
slashLoading;
|
|
213
|
+
slashReadRevision = 0;
|
|
206
214
|
/** Last published view; its identity is the render signal. */
|
|
207
215
|
slashViewCache;
|
|
208
216
|
/** Bumped to re-open a palette the user dismissed; see `QaSlashView`. */
|
|
@@ -212,6 +220,7 @@ export class QaSessionController {
|
|
|
212
220
|
constructor(options) {
|
|
213
221
|
this.sessions = options.sessions;
|
|
214
222
|
this.createSessionRemote = options.createSession;
|
|
223
|
+
this.forkSessionRemote = options.forkSession;
|
|
215
224
|
this.conversation = options.conversation;
|
|
216
225
|
this.connection = options.connection;
|
|
217
226
|
this.config = options.config;
|
|
@@ -243,11 +252,18 @@ export class QaSessionController {
|
|
|
243
252
|
this.selectedSubrole = options.initialSubrole ?? null;
|
|
244
253
|
this.adminPreview = options.adminPreview === true;
|
|
245
254
|
this.fileUpload = options.fileUpload ?? (() => undefined);
|
|
255
|
+
this.sharingApi = options.sharingApi;
|
|
256
|
+
if (this.sharingApi !== undefined && options.accounts !== undefined)
|
|
257
|
+
this.accessPoll = setInterval(() => {
|
|
258
|
+
void this.refreshChatAccess();
|
|
259
|
+
}, 1000);
|
|
246
260
|
this.hostSources = new QaHostSourceBridge(this.sourceApi, () => this.accounts?.token() ?? "");
|
|
247
261
|
this.hostApprovals = new QaHostApprovalBridge(options.approvalApi);
|
|
248
262
|
this.hostQuestions = new QaHostQuestionBridge(options.questionApi);
|
|
249
263
|
this.queueRemote = options.queueStatus;
|
|
250
|
-
|
|
264
|
+
// Cold account catalogs can outlive one short network round trip.
|
|
265
|
+
// Keep bootstrap bounded without rejecting a valid slow restoration.
|
|
266
|
+
this.timeoutMs = options.timeoutMs ?? 60_000;
|
|
251
267
|
this.streamPublisher = new StreamPublisher(options.streamIntervalMs ?? 66);
|
|
252
268
|
this.connectedOnce = this.connection.getSnapshot() !== undefined;
|
|
253
269
|
this.unsubscribeConnection = this.connection.subscribe(() => {
|
|
@@ -339,7 +355,7 @@ export class QaSessionController {
|
|
|
339
355
|
* call this surface makes — while a human command goes to the Host's command
|
|
340
356
|
* runtime and never becomes a model message.
|
|
341
357
|
*/
|
|
342
|
-
async send(text, attachments = [], pick = null) {
|
|
358
|
+
async send(text, attachments = [], pick = null, fastReview = false) {
|
|
343
359
|
const prompt = text.trim();
|
|
344
360
|
// Images ride the prompt inline; files must be staged first, because the
|
|
345
361
|
// wire carries a receipt rather than bytes.
|
|
@@ -405,6 +421,23 @@ export class QaSessionController {
|
|
|
405
421
|
return false;
|
|
406
422
|
}
|
|
407
423
|
}
|
|
424
|
+
if (fastReview) {
|
|
425
|
+
this.requestQueueNotice = null;
|
|
426
|
+
const target = await this.liveTarget();
|
|
427
|
+
if (target === undefined)
|
|
428
|
+
return false;
|
|
429
|
+
await this.refreshSlashCatalog(true);
|
|
430
|
+
if (this.disposed || this.session !== target)
|
|
431
|
+
return false;
|
|
432
|
+
const command = this.slashEntries.find((entry) => entry.kind === "command" && entry.name === "no-review");
|
|
433
|
+
if (command === undefined) {
|
|
434
|
+
this.operationError =
|
|
435
|
+
"Fast недоступен в этом чате. Выключите Fast, чтобы отправить запрос с автоматическим ревью.";
|
|
436
|
+
this.publish();
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
return this.sendCommand(`/no-review${prompt === "" ? "" : ` ${prompt}`}`, attachments, files, command, true);
|
|
440
|
+
}
|
|
408
441
|
this.operationError = null;
|
|
409
442
|
this.requestQueueNotice = null;
|
|
410
443
|
// A running turn has nowhere for a new question to land but its queue, and
|
|
@@ -542,7 +575,7 @@ export class QaSessionController {
|
|
|
542
575
|
* something the deployment does not allow, and retyping the line is a worse
|
|
543
576
|
* answer than leaving it in place to be edited.
|
|
544
577
|
*/
|
|
545
|
-
async sendCommand(line, attachments, files, entry) {
|
|
578
|
+
async sendCommand(line, attachments, files, entry, requireSuccess = false) {
|
|
546
579
|
if (this.slashApi === undefined) {
|
|
547
580
|
this.operationError = SLASH_DISABLED_COPY;
|
|
548
581
|
this.publish();
|
|
@@ -594,6 +627,11 @@ export class QaSessionController {
|
|
|
594
627
|
this.publish();
|
|
595
628
|
return false;
|
|
596
629
|
}
|
|
630
|
+
if (requireSuccess && result.value.outcome.kind === "error") {
|
|
631
|
+
this.operationError = result.value.outcome.text;
|
|
632
|
+
this.publish();
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
597
635
|
this.operationError = null;
|
|
598
636
|
this.publish();
|
|
599
637
|
return true;
|
|
@@ -613,31 +651,40 @@ export class QaSessionController {
|
|
|
613
651
|
* skill registry has no browser-facing change event, so a skill installed
|
|
614
652
|
* while the page is open is only discovered by asking again.
|
|
615
653
|
*/
|
|
654
|
+
slashReadTarget() {
|
|
655
|
+
if (this.session !== undefined)
|
|
656
|
+
return String(this.session.sessionId);
|
|
657
|
+
return this.drafting
|
|
658
|
+
? slashDraftTarget(this.selectedSubrole, this.adminPreview)
|
|
659
|
+
: undefined;
|
|
660
|
+
}
|
|
616
661
|
async refreshSlashCatalog(force = false) {
|
|
617
|
-
const
|
|
618
|
-
if (
|
|
662
|
+
const sessionId = this.slashReadTarget();
|
|
663
|
+
if (sessionId === undefined || this.disposed)
|
|
619
664
|
return;
|
|
620
665
|
if (!this.config.slashCommands.enabled || this.compatibilityReadOnly) {
|
|
621
|
-
this.applySlashCatalog(
|
|
666
|
+
this.applySlashCatalog(sessionId, null);
|
|
622
667
|
return;
|
|
623
668
|
}
|
|
624
669
|
if (this.slashApi === undefined) {
|
|
625
|
-
this.applySlashCatalog(
|
|
670
|
+
this.applySlashCatalog(sessionId, undefined);
|
|
626
671
|
return;
|
|
627
672
|
}
|
|
628
673
|
if (this.slashLoading !== undefined)
|
|
629
674
|
return this.slashLoading;
|
|
630
|
-
if (!force && this.slashSessionId ===
|
|
675
|
+
if (!force && this.slashSessionId === sessionId) {
|
|
631
676
|
if (Date.now() - this.slashLoadedAt < SLASH_CATALOG_STALE_MS)
|
|
632
677
|
return;
|
|
633
678
|
}
|
|
634
|
-
const
|
|
679
|
+
const revision = this.slashReadRevision;
|
|
635
680
|
this.slashState = "loading";
|
|
636
681
|
this.publish();
|
|
637
682
|
const operation = (async () => {
|
|
638
683
|
try {
|
|
639
684
|
const result = await this.slashApi?.catalog(this.accounts?.token() ?? "", sessionId);
|
|
640
|
-
if (result === undefined
|
|
685
|
+
if (result === undefined ||
|
|
686
|
+
revision !== this.slashReadRevision ||
|
|
687
|
+
this.disposed)
|
|
641
688
|
return;
|
|
642
689
|
if (!result.ok) {
|
|
643
690
|
this.applySlashCatalog(sessionId, undefined);
|
|
@@ -647,6 +694,8 @@ export class QaSessionController {
|
|
|
647
694
|
}
|
|
648
695
|
catch (error) {
|
|
649
696
|
console.error("dsh-qa-surface: slash catalog failed", error);
|
|
697
|
+
if (revision !== this.slashReadRevision || this.disposed)
|
|
698
|
+
return;
|
|
650
699
|
this.applySlashCatalog(sessionId, undefined);
|
|
651
700
|
}
|
|
652
701
|
})().finally(() => {
|
|
@@ -663,10 +712,8 @@ export class QaSessionController {
|
|
|
663
712
|
* into refusals.
|
|
664
713
|
*/
|
|
665
714
|
applySlashCatalog(sessionId, catalog) {
|
|
666
|
-
if (this.
|
|
667
|
-
String(this.session.sessionId) !== sessionId) {
|
|
715
|
+
if (this.disposed || this.slashReadTarget() !== sessionId)
|
|
668
716
|
return;
|
|
669
|
-
}
|
|
670
717
|
this.slashSessionId = sessionId;
|
|
671
718
|
this.slashLoadedAt = Date.now();
|
|
672
719
|
if (catalog === null) {
|
|
@@ -696,13 +743,14 @@ export class QaSessionController {
|
|
|
696
743
|
}
|
|
697
744
|
/** Whether the catalog of the bound chat was read successfully. */
|
|
698
745
|
slashCatalogReady() {
|
|
699
|
-
return this.slashState === "ready" || this.slashState === "loading"
|
|
746
|
+
return ((this.slashState === "ready" || this.slashState === "loading") &&
|
|
747
|
+
this.slashSessionId === this.slashReadTarget());
|
|
700
748
|
}
|
|
701
749
|
/** The slash view of the current state; assembled where the state is. */
|
|
702
750
|
slashView() {
|
|
703
751
|
const enabled = this.config.slashCommands.enabled &&
|
|
704
752
|
!this.compatibilityReadOnly &&
|
|
705
|
-
this.
|
|
753
|
+
this.slashReadTarget() !== undefined &&
|
|
706
754
|
this.slashApi !== undefined;
|
|
707
755
|
// Identity is the render signal: this runs on every publish, including
|
|
708
756
|
// every stream frame of a running turn, and the composer is memoized on
|
|
@@ -786,8 +834,8 @@ export class QaSessionController {
|
|
|
786
834
|
* hides the marker message, and the consecutive turns read as variants of
|
|
787
835
|
* the original question (switched in the surface).
|
|
788
836
|
*/
|
|
789
|
-
async regenerate() {
|
|
790
|
-
return this.send(QA_REGENERATE_MARKER);
|
|
837
|
+
async regenerate(fastReview = false) {
|
|
838
|
+
return this.send(QA_REGENERATE_MARKER, [], null, fastReview);
|
|
791
839
|
}
|
|
792
840
|
async stop() {
|
|
793
841
|
const target = this.session;
|
|
@@ -915,12 +963,14 @@ export class QaSessionController {
|
|
|
915
963
|
this.unbind();
|
|
916
964
|
this.operationError = null;
|
|
917
965
|
this.policyReady = false;
|
|
966
|
+
this.inheritedThroughSeq = undefined;
|
|
918
967
|
this.state = {
|
|
919
968
|
...QA_SESSION_IDLE_STATE,
|
|
920
969
|
chatKey: this.chatKey,
|
|
921
970
|
chatsRevision: this.chatsRevision,
|
|
922
971
|
};
|
|
923
972
|
this.publish();
|
|
973
|
+
void this.refreshSlashCatalog(true);
|
|
924
974
|
}
|
|
925
975
|
/**
|
|
926
976
|
* Turn the current draft into a real attested session. Runs inside the
|
|
@@ -975,6 +1025,59 @@ export class QaSessionController {
|
|
|
975
1025
|
});
|
|
976
1026
|
return attempt;
|
|
977
1027
|
}
|
|
1028
|
+
/** Branch a completed answer without submitting or cancelling a turn. */
|
|
1029
|
+
async forkFromAnswer(answerSeq) {
|
|
1030
|
+
const parent = this.session;
|
|
1031
|
+
const remote = this.forkSessionRemote;
|
|
1032
|
+
if (this.disposed ||
|
|
1033
|
+
this.forking ||
|
|
1034
|
+
remote === undefined ||
|
|
1035
|
+
parent === undefined ||
|
|
1036
|
+
this.viewingSubagent !== null ||
|
|
1037
|
+
this.compatibilityReadOnly ||
|
|
1038
|
+
!this.policyReady ||
|
|
1039
|
+
this.config.session.policy === "fixed")
|
|
1040
|
+
return;
|
|
1041
|
+
const operation = this.generation;
|
|
1042
|
+
this.forking = true;
|
|
1043
|
+
try {
|
|
1044
|
+
const result = await remote(this.accounts?.token() ?? "", String(parent.sessionId), answerSeq);
|
|
1045
|
+
if (!result.ok)
|
|
1046
|
+
throw new Error("The Host refused to create the branch.");
|
|
1047
|
+
this.accounts?.onSessionCreated(result.value);
|
|
1048
|
+
this.chats.addChat(result.value);
|
|
1049
|
+
if (this.disposed ||
|
|
1050
|
+
operation !== this.generation ||
|
|
1051
|
+
parent !== this.session)
|
|
1052
|
+
return;
|
|
1053
|
+
++this.generation;
|
|
1054
|
+
this.drafting = false;
|
|
1055
|
+
this.openChat();
|
|
1056
|
+
this.unbind();
|
|
1057
|
+
this.operationError = null;
|
|
1058
|
+
this.state = {
|
|
1059
|
+
...QA_SESSION_IDLE_STATE,
|
|
1060
|
+
chatKey: this.chatKey,
|
|
1061
|
+
chatsRevision: this.chatsRevision,
|
|
1062
|
+
phase: "creating",
|
|
1063
|
+
};
|
|
1064
|
+
this.publish();
|
|
1065
|
+
const adoption = this.generation;
|
|
1066
|
+
if (await this.bind(result.value, { operation: adoption }))
|
|
1067
|
+
this.chats.saveActive(result.value);
|
|
1068
|
+
}
|
|
1069
|
+
catch (error) {
|
|
1070
|
+
if (!this.disposed &&
|
|
1071
|
+
(parent === this.session || this.session === undefined)) {
|
|
1072
|
+
this.operationError = "Не удалось создать ветку чата.";
|
|
1073
|
+
console.error("dsh-qa-surface: session operation failed (fork)", error);
|
|
1074
|
+
this.publish();
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
finally {
|
|
1078
|
+
this.forking = false;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
978
1081
|
/**
|
|
979
1082
|
* Open one of this browser's indexed chats. The id must be present in the
|
|
980
1083
|
* host session list and pass policy attestation; an unknown id is forgotten
|
|
@@ -993,6 +1096,7 @@ export class QaSessionController {
|
|
|
993
1096
|
this.unbind();
|
|
994
1097
|
this.operationError = null;
|
|
995
1098
|
this.policyReady = false;
|
|
1099
|
+
this.inheritedThroughSeq = undefined;
|
|
996
1100
|
this.state = {
|
|
997
1101
|
...QA_SESSION_IDLE_STATE,
|
|
998
1102
|
chatKey: this.chatKey,
|
|
@@ -1060,6 +1164,7 @@ export class QaSessionController {
|
|
|
1060
1164
|
this.unbind();
|
|
1061
1165
|
this.operationError = null;
|
|
1062
1166
|
this.policyReady = false;
|
|
1167
|
+
this.inheritedThroughSeq = undefined;
|
|
1063
1168
|
this.state = {
|
|
1064
1169
|
...QA_SESSION_IDLE_STATE,
|
|
1065
1170
|
chatKey: this.chatKey,
|
|
@@ -1176,6 +1281,7 @@ export class QaSessionController {
|
|
|
1176
1281
|
if (this.disposed)
|
|
1177
1282
|
return;
|
|
1178
1283
|
this.disposed = true;
|
|
1284
|
+
clearInterval(this.accessPoll);
|
|
1179
1285
|
this.generation += 1;
|
|
1180
1286
|
this.streamPublisher.clear();
|
|
1181
1287
|
this.unsubscribeConnection();
|
|
@@ -1334,6 +1440,7 @@ export class QaSessionController {
|
|
|
1334
1440
|
const operation = ++this.generation;
|
|
1335
1441
|
this.unbind();
|
|
1336
1442
|
this.policyReady = false;
|
|
1443
|
+
this.inheritedThroughSeq = undefined;
|
|
1337
1444
|
this.state = { ...this.state, phase: "creating" };
|
|
1338
1445
|
this.emit();
|
|
1339
1446
|
try {
|
|
@@ -1505,6 +1612,7 @@ export class QaSessionController {
|
|
|
1505
1612
|
this.session = binding.session;
|
|
1506
1613
|
this.conversationBinding = this.conversation.binding(id);
|
|
1507
1614
|
this.policyReady = false;
|
|
1615
|
+
this.inheritedThroughSeq = undefined;
|
|
1508
1616
|
this.compatibilityReadOnly = false;
|
|
1509
1617
|
this.unsubscribeSession = binding.session.subscribe(() => {
|
|
1510
1618
|
this.admissionPending = false;
|
|
@@ -1733,10 +1841,13 @@ export class QaSessionController {
|
|
|
1733
1841
|
// answers for its own sends.
|
|
1734
1842
|
this.requestQueueNotice = null;
|
|
1735
1843
|
this.policyReady = false;
|
|
1844
|
+
this.inheritedThroughSeq = undefined;
|
|
1736
1845
|
this.compatibilityReadOnly = false;
|
|
1737
1846
|
// The catalog belongs to the chat that produced it: another chat has a
|
|
1738
1847
|
// different cwd, composition and role, so carrying it across would offer
|
|
1739
1848
|
// actions this chat may not have.
|
|
1849
|
+
this.slashLoading = undefined;
|
|
1850
|
+
this.slashReadRevision += 1;
|
|
1740
1851
|
this.slashSessionId = null;
|
|
1741
1852
|
this.slashEntries = [];
|
|
1742
1853
|
this.slashDenied = [];
|
|
@@ -1790,6 +1901,7 @@ export class QaSessionController {
|
|
|
1790
1901
|
canSend: connected && !materializing,
|
|
1791
1902
|
pendingMessage: this.pendingSubmission?.message ?? null,
|
|
1792
1903
|
chatsRevision: this.chatsRevision,
|
|
1904
|
+
slash: this.slashView(),
|
|
1793
1905
|
};
|
|
1794
1906
|
this.emit();
|
|
1795
1907
|
return;
|
|
@@ -1807,7 +1919,7 @@ export class QaSessionController {
|
|
|
1807
1919
|
const snapshot = this.session.getSnapshot();
|
|
1808
1920
|
const sessionId = String(this.session.sessionId);
|
|
1809
1921
|
const conversationSnapshot = this.conversationBinding?.snapshot.getSnapshot();
|
|
1810
|
-
const projectedSourceBundles = projectTurnSources(conversationSnapshot, sessionId, this.sourceAnchor(sessionId));
|
|
1922
|
+
const projectedSourceBundles = projectTurnSources(conversationSnapshot, sessionId, this.sourceAnchor(sessionId), this.config.tools.docsRoot);
|
|
1811
1923
|
const sourceBundles = this.hostSources.merge(projectedSourceBundles);
|
|
1812
1924
|
// A delegated child is not attested, so every Host RPC that admits the
|
|
1813
1925
|
// session refuses it — and its evidence reaches the chat through the
|
|
@@ -1826,6 +1938,7 @@ export class QaSessionController {
|
|
|
1826
1938
|
chatKey: this.chatKey,
|
|
1827
1939
|
sessionSnapshot: snapshot,
|
|
1828
1940
|
conversationSnapshot,
|
|
1941
|
+
inheritedThroughSeq: this.inheritedThroughSeq,
|
|
1829
1942
|
sourceBundles,
|
|
1830
1943
|
// The same directory the sources are anchored on: an answer that quotes a
|
|
1831
1944
|
// file the turn made is masked to the name inside this workspace.
|
|
@@ -1875,6 +1988,26 @@ export class QaSessionController {
|
|
|
1875
1988
|
}
|
|
1876
1989
|
this.state = {
|
|
1877
1990
|
...projected,
|
|
1991
|
+
chatAccess: this.chatAccess?.sessionId === sessionId ? this.chatAccess : undefined,
|
|
1992
|
+
...(this.chatAccess?.sessionId === sessionId &&
|
|
1993
|
+
this.chatAccess.mode !== "owner"
|
|
1994
|
+
? {
|
|
1995
|
+
canSend: projected.canSend && this.chatAccess.mode === "collaborate",
|
|
1996
|
+
canStop: false,
|
|
1997
|
+
canEditQueue: false,
|
|
1998
|
+
approvals: [],
|
|
1999
|
+
questions: [],
|
|
2000
|
+
messages: projected.messages.map((message) => message.role === "assistant"
|
|
2001
|
+
? {
|
|
2002
|
+
...message,
|
|
2003
|
+
artifacts: message.artifacts?.map((artifact) => {
|
|
2004
|
+
const { workflow: _workflow, ...file } = artifact;
|
|
2005
|
+
return file;
|
|
2006
|
+
}),
|
|
2007
|
+
}
|
|
2008
|
+
: message),
|
|
2009
|
+
}
|
|
2010
|
+
: {}),
|
|
1878
2011
|
pendingMessage: this.pendingSubmission?.message ?? null,
|
|
1879
2012
|
};
|
|
1880
2013
|
this.emit();
|
|
@@ -2019,7 +2152,9 @@ export class QaSessionController {
|
|
|
2019
2152
|
if (session === undefined)
|
|
2020
2153
|
return { kind: "refused", reason: null };
|
|
2021
2154
|
const sessionId = String(session.sessionId);
|
|
2022
|
-
if (!this.config.lockdown.enabled &&
|
|
2155
|
+
if (!this.config.lockdown.enabled &&
|
|
2156
|
+
this.accounts === undefined &&
|
|
2157
|
+
this.forkSessionRemote === undefined) {
|
|
2023
2158
|
this.policyReady = true;
|
|
2024
2159
|
return { kind: "ok", sessionId };
|
|
2025
2160
|
}
|
|
@@ -2028,6 +2163,7 @@ export class QaSessionController {
|
|
|
2028
2163
|
// lockdown, and skipping the call is what let a browser that restored
|
|
2029
2164
|
// another account's chat write into it without ever being asked who spoke.
|
|
2030
2165
|
this.policyReady = false;
|
|
2166
|
+
this.inheritedThroughSeq = undefined;
|
|
2031
2167
|
this.publish();
|
|
2032
2168
|
const outcome = await attestQaPolicy({
|
|
2033
2169
|
secureSession: this.secureSessionRemote,
|
|
@@ -2039,6 +2175,8 @@ export class QaSessionController {
|
|
|
2039
2175
|
if (this.session !== session)
|
|
2040
2176
|
return { kind: "stale" };
|
|
2041
2177
|
if (outcome.ok) {
|
|
2178
|
+
this.chatAccess = outcome.chatAccess;
|
|
2179
|
+
this.inheritedThroughSeq = outcome.inheritedThroughSeq;
|
|
2042
2180
|
this.policyReady = true;
|
|
2043
2181
|
this.operationError = null;
|
|
2044
2182
|
this.publish();
|
|
@@ -2051,6 +2189,7 @@ export class QaSessionController {
|
|
|
2051
2189
|
return { kind: "refused", reason: outcome.reason };
|
|
2052
2190
|
}
|
|
2053
2191
|
this.policyReady = false;
|
|
2192
|
+
this.inheritedThroughSeq = undefined;
|
|
2054
2193
|
this.operationError = CONFIGURATION_ERROR;
|
|
2055
2194
|
this.publish();
|
|
2056
2195
|
return { kind: "refused", reason: outcome.reason };
|
|
@@ -2059,6 +2198,34 @@ export class QaSessionController {
|
|
|
2059
2198
|
for (const listener of this.listeners)
|
|
2060
2199
|
listener();
|
|
2061
2200
|
}
|
|
2201
|
+
async refreshChatAccess() {
|
|
2202
|
+
const session = this.session;
|
|
2203
|
+
const token = this.accounts?.token();
|
|
2204
|
+
if (session === undefined ||
|
|
2205
|
+
token == null ||
|
|
2206
|
+
this.sharingApi === undefined ||
|
|
2207
|
+
this.disposed)
|
|
2208
|
+
return;
|
|
2209
|
+
const id = String(session.sessionId);
|
|
2210
|
+
try {
|
|
2211
|
+
const result = await this.sharingApi.access(token, id);
|
|
2212
|
+
if (this.session !== session || this.disposed)
|
|
2213
|
+
return;
|
|
2214
|
+
if (!result.ok) {
|
|
2215
|
+
this.unbind();
|
|
2216
|
+
this.chatAccess = undefined;
|
|
2217
|
+
this.fail("Доступ к чату отозван или аккаунт недоступен.", new QaPolicyAttestationError("session-owned-elsewhere"));
|
|
2218
|
+
return;
|
|
2219
|
+
}
|
|
2220
|
+
if (JSON.stringify(this.chatAccess) !== JSON.stringify(result.value)) {
|
|
2221
|
+
this.chatAccess = result.value;
|
|
2222
|
+
this.publish();
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
catch {
|
|
2226
|
+
/* Transport failures do not fabricate a revocation. Server writes still recheck access. */
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2062
2229
|
/**
|
|
2063
2230
|
* Answer a request the Host parked for this chat. The Host re-checks the
|
|
2064
2231
|
* request against the chat, so an answer that lost its race is a no-op; the
|