@yadsh/dsh-qa-surface 0.8.0 → 0.9.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 +113 -3
- package/compatibility.json +5 -0
- package/lib/access/capability-catalog.js +32 -3
- package/lib/access/capability-catalog.js.map +1 -1
- package/lib/access/service.js +45 -17
- package/lib/access/service.js.map +1 -1
- package/lib/accounts/errors.js.map +1 -1
- package/lib/accounts/store.js +14 -0
- package/lib/accounts/store.js.map +1 -1
- package/lib/admin/permissions.js +4 -0
- package/lib/admin/permissions.js.map +1 -1
- package/lib/admin/quality-store.js +71 -0
- package/lib/admin/quality-store.js.map +1 -1
- package/lib/admin/service.js +106 -1
- package/lib/admin/service.js.map +1 -1
- package/lib/admin/session-log.js +66 -5
- package/lib/admin/session-log.js.map +1 -1
- package/lib/client/QaSessionController.js +359 -37
- package/lib/client/QaSessionController.js.map +1 -1
- package/lib/client/QaSurface.js +26 -4
- package/lib/client/QaSurface.js.map +1 -1
- package/lib/client/QaTranscriptAdapter.js +36 -0
- package/lib/client/QaTranscriptAdapter.js.map +1 -1
- package/lib/client/admin/QaAdmin.js +9 -1
- package/lib/client/admin/QaAdmin.js.map +1 -1
- package/lib/client/admin/pages/Conversations.js +33 -7
- package/lib/client/admin/pages/Conversations.js.map +1 -1
- package/lib/client/admin/shared.js +9 -0
- package/lib/client/admin/shared.js.map +1 -1
- package/lib/client/admin/styles.js +2 -0
- package/lib/client/admin/styles.js.map +1 -1
- package/lib/client/audit/QaAuditDialog.js +74 -0
- package/lib/client/audit/QaAuditDialog.js.map +1 -0
- package/lib/client/audit/QaAuditRowBadge.js +17 -0
- package/lib/client/audit/QaAuditRowBadge.js.map +1 -0
- package/lib/client/audit/controller.js +39 -0
- package/lib/client/audit/controller.js.map +1 -0
- package/lib/client/audit/types.js +36 -0
- package/lib/client/audit/types.js.map +1 -0
- package/lib/client/audit/use-chat-audits.js +139 -0
- package/lib/client/audit/use-chat-audits.js.map +1 -0
- package/lib/client/components/QaChangelog.js +43 -0
- package/lib/client/components/QaChangelog.js.map +1 -1
- package/lib/client/components/QaComposer.js +63 -3
- package/lib/client/components/QaComposer.js.map +1 -1
- package/lib/client/components/QaMessage.js +16 -0
- package/lib/client/components/QaMessage.js.map +1 -1
- package/lib/client/components/QaQuestions.js +1 -1
- package/lib/client/components/QaQuestions.js.map +1 -1
- package/lib/client/components/QaSidebar.js +12 -1
- package/lib/client/components/QaSidebar.js.map +1 -1
- package/lib/client/index.js +22 -2
- package/lib/client/index.js.map +1 -1
- package/lib/client/project-session-state.js +15 -2
- package/lib/client/project-session-state.js.map +1 -1
- package/lib/client/prompt-content.js +26 -0
- package/lib/client/prompt-content.js.map +1 -1
- package/lib/client/settings/card.js +2 -2
- package/lib/client/settings/card.js.map +1 -1
- package/lib/client/settings/sections/lockdown.js +1 -6
- package/lib/client/settings/sections/lockdown.js.map +1 -1
- package/lib/client/settings/sections/slash.js +57 -0
- package/lib/client/settings/sections/slash.js.map +1 -0
- package/lib/client/settings/sections/status.js +9 -1
- package/lib/client/settings/sections/status.js.map +1 -1
- package/lib/client/settings/sections.js +1 -0
- package/lib/client/settings/sections.js.map +1 -1
- package/lib/client/slash/SlashPalette.js +48 -0
- package/lib/client/slash/SlashPalette.js.map +1 -0
- package/lib/client/slash/copy.js +54 -0
- package/lib/client/slash/copy.js.map +1 -0
- package/lib/client/slash/slash-router.js +49 -0
- package/lib/client/slash/slash-router.js.map +1 -0
- package/lib/client/slash/useSlashNavigation.js +95 -0
- package/lib/client/slash/useSlashNavigation.js.map +1 -0
- package/lib/client/styles.js +51 -1
- package/lib/client/styles.js.map +1 -1
- package/lib/client/types.js +11 -0
- package/lib/client/types.js.map +1 -1
- package/lib/client.js +4231 -551
- package/lib/client.js.map +1 -1
- package/lib/config-resolvers/basics.js +11 -2
- package/lib/config-resolvers/basics.js.map +1 -1
- package/lib/config-resolvers/defaults.js +22 -0
- package/lib/config-resolvers/defaults.js.map +1 -1
- package/lib/config-resolvers/lockdown.js +8 -2
- package/lib/config-resolvers/lockdown.js.map +1 -1
- package/lib/config-resolvers/slash-commands.js +60 -0
- package/lib/config-resolvers/slash-commands.js.map +1 -0
- package/lib/config.js +61 -2
- package/lib/config.js.map +1 -1
- package/lib/index.js +158 -1
- package/lib/index.js.map +1 -1
- package/lib/personal-skills/service.js +16 -3
- package/lib/personal-skills/service.js.map +1 -1
- package/lib/provenance/host-store.js +18 -1
- package/lib/provenance/host-store.js.map +1 -1
- package/lib/questions.js +90 -43
- package/lib/questions.js.map +1 -1
- package/lib/resolve-config.js +5 -0
- package/lib/resolve-config.js.map +1 -1
- package/lib/secure-session.js +24 -0
- package/lib/secure-session.js.map +1 -1
- package/lib/slash/catalog.js +77 -0
- package/lib/slash/catalog.js.map +1 -0
- package/lib/slash/matcher.js +100 -0
- package/lib/slash/matcher.js.map +1 -0
- package/lib/slash/parser.js +79 -0
- package/lib/slash/parser.js.map +1 -0
- package/lib/slash/policy.js +72 -0
- package/lib/slash/policy.js.map +1 -0
- package/lib/slash/remotes.js +272 -0
- package/lib/slash/remotes.js.map +1 -0
- package/lib/typert.host.js +318 -53
- package/lib/typert.remote-client.d.ts +7 -1
- package/lib/typert.remote-client.js +247 -51
- package/lib/types/access/capability-catalog.d.ts +28 -1
- package/lib/types/access/service.d.ts +33 -4
- package/lib/types/accounts/errors.d.ts +7 -1
- package/lib/types/accounts/store.d.ts +12 -0
- package/lib/types/admin/quality-store.d.ts +18 -0
- package/lib/types/admin/service.d.ts +34 -2
- package/lib/types/admin/session-log.d.ts +65 -1
- package/lib/types/client/QaSessionController.d.ts +84 -2
- package/lib/types/client/QaSurface.d.ts +14 -1
- package/lib/types/client/admin/pages/Conversations.d.ts +4 -0
- package/lib/types/client/audit/QaAuditDialog.d.ts +21 -0
- package/lib/types/client/audit/QaAuditRowBadge.d.ts +18 -0
- package/lib/types/client/audit/controller.d.ts +27 -0
- package/lib/types/client/audit/types.d.ts +72 -0
- package/lib/types/client/audit/use-chat-audits.d.ts +20 -0
- package/lib/types/client/components/QaComposer.d.ts +12 -2
- package/lib/types/client/components/QaQuestions.d.ts +7 -0
- package/lib/types/client/components/QaSidebar.d.ts +8 -0
- package/lib/types/client/project-session-state.d.ts +3 -1
- package/lib/types/client/prompt-content.d.ts +9 -1
- package/lib/types/client/settings/sections/slash.d.ts +16 -0
- package/lib/types/client/settings/sections.d.ts +1 -0
- package/lib/types/client/slash/SlashPalette.d.ts +24 -0
- package/lib/types/client/slash/copy.d.ts +30 -0
- package/lib/types/client/slash/slash-router.d.ts +67 -0
- package/lib/types/client/slash/useSlashNavigation.d.ts +47 -0
- package/lib/types/client/styles.d.ts +9 -0
- package/lib/types/client/types.d.ts +16 -1
- package/lib/types/config-resolvers/slash-commands.d.ts +20 -0
- package/lib/types/index.d.ts +78 -1
- package/lib/types/personal-skills/service.d.ts +5 -0
- package/lib/types/provenance/host-store.d.ts +13 -0
- package/lib/types/questions.d.ts +8 -0
- package/lib/types/slash/catalog.d.ts +56 -0
- package/lib/types/slash/matcher.d.ts +51 -0
- package/lib/types/slash/parser.d.ts +46 -0
- package/lib/types/slash/policy.d.ts +40 -0
- package/lib/types/slash/remotes.d.ts +35 -0
- package/lib/types/types.d.ts +200 -5
- package/package.json +7 -3
- package/scripts/attach-workspace-sessions.mjs +472 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-log.js","sourceRoot":"","sources":["../../src/admin/session-log.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"session-log.js","sourceRoot":"","sources":["../../src/admin/session-log.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { bytesToBase64 } from "./base64.js";
|
|
2
2
|
import { qaStorageNamespace } from "../shared/session-key.js";
|
|
3
|
+
import { resolveSlashRoute } from "./slash/slash-router.js";
|
|
4
|
+
import { SLASH_DISABLED_COPY, slashAttachmentsCopy, slashExecuteFailureCopy, slashHostRefusalCopy, slashRefusalCopy, slashWithheldCopy, } from "./slash/copy.js";
|
|
3
5
|
import { canOpenAsCompatibilityReadOnly, QaPolicyAttestationError, } from "./attestation.js";
|
|
4
6
|
import { QaChatIndex } from "./chat-index.js";
|
|
5
7
|
import { isDelegatedSession } from "./lineage.js";
|
|
6
8
|
import { SessionAssetRepository } from "./session-assets.js";
|
|
7
9
|
import { createQaSession } from "./create-session.js";
|
|
8
|
-
import { buildQaPromptContent, stageQaFiles } from "./prompt-content.js";
|
|
10
|
+
import { buildQaPromptContent, buildQaSlashAttachments, stageQaFiles, } from "./prompt-content.js";
|
|
9
11
|
import { attestQaPolicy } from "./session-admission.js";
|
|
10
12
|
import { QaHostSourceBridge } from "./session-sources.js";
|
|
11
13
|
import { QaHostApprovalBridge } from "./approvals.js";
|
|
@@ -18,6 +20,13 @@ import { readableSubagentName } from "./settlement.js";
|
|
|
18
20
|
import { projectBoundSessionState } from "./project-session-state.js";
|
|
19
21
|
import { projectTurnSources } from "./turn-sources.js";
|
|
20
22
|
const CONFIGURATION_ERROR = "Настройки помощника недоступны.";
|
|
23
|
+
/**
|
|
24
|
+
* How long a catalog answer stays fresh enough to skip a re-read when the
|
|
25
|
+
* palette opens. Short on purpose: the check is one round-trip and a stale
|
|
26
|
+
* palette shows a skill that was deleted or hides one that was just added.
|
|
27
|
+
* No polling loop — the palette opening is the only trigger.
|
|
28
|
+
*/
|
|
29
|
+
const SLASH_CATALOG_STALE_MS = 3_000;
|
|
21
30
|
/**
|
|
22
31
|
* Spacing of the parked-request poll. A request is answered by a person, so a
|
|
23
32
|
* second of latency is invisible; the poll only runs while a turn runs.
|
|
@@ -39,6 +48,8 @@ export class QaSessionController {
|
|
|
39
48
|
sourceApi;
|
|
40
49
|
chats;
|
|
41
50
|
accounts;
|
|
51
|
+
/** Slash half of the Host namespace; absent on an older Host build. */
|
|
52
|
+
slashApi;
|
|
42
53
|
/** Resolved per send: a page without the upload plugin has no receipts. */
|
|
43
54
|
fileUpload;
|
|
44
55
|
/** Per-chat attachment URL cache; blob URLs die with the chat binding. */
|
|
@@ -79,6 +90,25 @@ export class QaSessionController {
|
|
|
79
90
|
hostQuestions;
|
|
80
91
|
/** Set while a running turn is polled for parked requests. */
|
|
81
92
|
pendingTimer;
|
|
93
|
+
/**
|
|
94
|
+
* Slash catalog of the bound chat. Keyed by session: it depends on the
|
|
95
|
+
* chat's cwd, its agent composition and its role, so it is dropped with the
|
|
96
|
+
* binding instead of being carried across chats.
|
|
97
|
+
*/
|
|
98
|
+
slashSessionId = null;
|
|
99
|
+
slashEntries = [];
|
|
100
|
+
slashDenied = [];
|
|
101
|
+
slashSurface = "unavailable";
|
|
102
|
+
slashState = "idle";
|
|
103
|
+
slashError = null;
|
|
104
|
+
slashLoadedAt = 0;
|
|
105
|
+
slashLoading;
|
|
106
|
+
/** Last published view; its identity is the render signal. */
|
|
107
|
+
slashViewCache;
|
|
108
|
+
/** Bumped to re-open a palette the user dismissed; see `QaSlashView`. */
|
|
109
|
+
slashReopen = 0;
|
|
110
|
+
/** Chat, connection and admission state of the last one-shot probe. */
|
|
111
|
+
pendingProbeKey = "";
|
|
82
112
|
constructor(options) {
|
|
83
113
|
this.sessions = options.sessions;
|
|
84
114
|
this.createSessionRemote = options.createSession;
|
|
@@ -97,6 +127,7 @@ export class QaSessionController {
|
|
|
97
127
|
};
|
|
98
128
|
this.chats = new QaChatIndex(options.storage, qaStorageNamespace(options.config));
|
|
99
129
|
this.accounts = options.accounts;
|
|
130
|
+
this.slashApi = options.slashApi;
|
|
100
131
|
this.selectedSubrole = options.initialSubrole ?? null;
|
|
101
132
|
this.adminPreview = options.adminPreview === true;
|
|
102
133
|
this.fileUpload = options.fileUpload ?? (() => undefined);
|
|
@@ -139,7 +170,52 @@ export class QaSessionController {
|
|
|
139
170
|
this.adminPreview = adminPreview;
|
|
140
171
|
await this.startDraft(true);
|
|
141
172
|
}
|
|
142
|
-
|
|
173
|
+
/**
|
|
174
|
+
* The attested live session a submission may ride, or `undefined` when the
|
|
175
|
+
* caller must abort. Materializing a draft chat, attesting policy and
|
|
176
|
+
* recovering from a stale proof live here, so the prompt path and the
|
|
177
|
+
* command path cannot drift apart on what "ready to send" means.
|
|
178
|
+
*/
|
|
179
|
+
async liveTarget() {
|
|
180
|
+
if (this.session === undefined) {
|
|
181
|
+
// A draft chat materializes its session only now: nothing was created
|
|
182
|
+
// when the user pressed "New chat", so the first submission pays for it.
|
|
183
|
+
if (!this.drafting || this.materializing !== undefined)
|
|
184
|
+
return undefined;
|
|
185
|
+
if (!(await this.materializeDraft()) || this.session === undefined) {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const step = await this.attestPolicy();
|
|
190
|
+
if (step.kind === "stale") {
|
|
191
|
+
// The user moved to another chat while the proof was pending; that
|
|
192
|
+
// binding attests through its own flow and must not receive a submission
|
|
193
|
+
// meant for the old one.
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
let attestedId = step.kind === "ok" ? step.sessionId : null;
|
|
197
|
+
if (attestedId === null) {
|
|
198
|
+
attestedId = await this.recoverForSend();
|
|
199
|
+
if (attestedId === null)
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
// Nothing can interleave between the last await and here, so matching the
|
|
203
|
+
// attested id proves the submission rides exactly the session it proved.
|
|
204
|
+
const target = this.session;
|
|
205
|
+
if (this.disposed ||
|
|
206
|
+
target === undefined ||
|
|
207
|
+
String(target.sessionId) !== attestedId) {
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
return target;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Route one composer draft. Ordinary text and a skill invocation both ride
|
|
214
|
+
* the native prompt path — a skill is a gesture inside the prompt, not a
|
|
215
|
+
* call this surface makes — while a human command goes to the Host's command
|
|
216
|
+
* runtime and never becomes a model message.
|
|
217
|
+
*/
|
|
218
|
+
async send(text, attachments = [], pick = null) {
|
|
143
219
|
const prompt = text.trim();
|
|
144
220
|
// Images ride the prompt inline; files must be staged first, because the
|
|
145
221
|
// wire carries a receipt rather than bytes.
|
|
@@ -147,8 +223,37 @@ export class QaSessionController {
|
|
|
147
223
|
if ((prompt === "" && attachments.length === 0) || !this.state.canSend) {
|
|
148
224
|
return false;
|
|
149
225
|
}
|
|
150
|
-
|
|
151
|
-
|
|
226
|
+
const route = resolveSlashRoute({
|
|
227
|
+
text: prompt,
|
|
228
|
+
enabled: this.config.slashCommands.enabled,
|
|
229
|
+
catalogReady: this.slashCatalogReady(),
|
|
230
|
+
entries: this.slashEntries,
|
|
231
|
+
deniedSkills: this.slashDenied,
|
|
232
|
+
picked: pick,
|
|
233
|
+
});
|
|
234
|
+
if (route.kind === "command") {
|
|
235
|
+
return this.sendCommand(prompt, attachments, files, route.entry);
|
|
236
|
+
}
|
|
237
|
+
if (route.kind === "disabled") {
|
|
238
|
+
this.operationError = SLASH_DISABLED_COPY;
|
|
239
|
+
this.publish();
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
if (route.kind !== "prompt" && route.kind !== "skill") {
|
|
243
|
+
this.operationError = slashRefusalCopy(route);
|
|
244
|
+
if (route.kind === "unknown" || route.kind === "ambiguous") {
|
|
245
|
+
// Re-open the palette the user had to dismiss to reach this line: the
|
|
246
|
+
// refusal is only useful next to the choices it is talking about.
|
|
247
|
+
this.slashReopen += 1;
|
|
248
|
+
}
|
|
249
|
+
this.publish();
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
if (route.kind === "prompt" && (route.withheld?.length ?? 0) > 0) {
|
|
253
|
+
// The gesture is real and user-invocable here, and this deployment
|
|
254
|
+
// withholds it. Sending it would let the user believe it took effect —
|
|
255
|
+
// the native consumer refuses the injection, but only after the turn.
|
|
256
|
+
this.operationError = slashWithheldCopy(route.withheld ?? []);
|
|
152
257
|
this.publish();
|
|
153
258
|
return false;
|
|
154
259
|
}
|
|
@@ -160,36 +265,9 @@ export class QaSessionController {
|
|
|
160
265
|
let accepted = false;
|
|
161
266
|
let target;
|
|
162
267
|
try {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
// when the user pressed "New chat", so the first prompt pays for it.
|
|
166
|
-
if (!this.drafting || this.materializing !== undefined)
|
|
167
|
-
return false;
|
|
168
|
-
if (!(await this.materializeDraft()) || this.session === undefined) {
|
|
169
|
-
return false;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
const step = await this.attestPolicy();
|
|
173
|
-
if (step.kind === "stale") {
|
|
174
|
-
// The user moved to another chat while the proof was pending; that
|
|
175
|
-
// binding attests through its own flow and must not receive a prompt
|
|
176
|
-
// meant for the old one.
|
|
268
|
+
target = await this.liveTarget();
|
|
269
|
+
if (target === undefined)
|
|
177
270
|
return false;
|
|
178
|
-
}
|
|
179
|
-
let attestedId = step.kind === "ok" ? step.sessionId : null;
|
|
180
|
-
if (attestedId === null) {
|
|
181
|
-
attestedId = await this.recoverForSend();
|
|
182
|
-
if (attestedId === null)
|
|
183
|
-
return false;
|
|
184
|
-
}
|
|
185
|
-
// Nothing can interleave between the last await and here, so matching the
|
|
186
|
-
// attested id proves the prompt rides exactly the session it proved.
|
|
187
|
-
target = this.session;
|
|
188
|
-
if (this.disposed ||
|
|
189
|
-
target === undefined ||
|
|
190
|
-
String(target.sessionId) !== attestedId) {
|
|
191
|
-
return false;
|
|
192
|
-
}
|
|
193
271
|
if (submission !== undefined && this.pendingSubmission === submission) {
|
|
194
272
|
// Recovery may have replaced the original chat with a fresh session;
|
|
195
273
|
// reconcile against the actual prompt target, not the abandoned one.
|
|
@@ -247,6 +325,198 @@ export class QaSessionController {
|
|
|
247
325
|
}
|
|
248
326
|
}
|
|
249
327
|
}
|
|
328
|
+
/**
|
|
329
|
+
* Run one admitted human command. The Host answers it and writes the
|
|
330
|
+
* lifecycle into the session log; this method only decides what the composer
|
|
331
|
+
* does next, which is why nothing here creates a pending user row.
|
|
332
|
+
*
|
|
333
|
+
* A refusal keeps the draft and the attachments: the user asked for
|
|
334
|
+
* something the deployment does not allow, and retyping the line is a worse
|
|
335
|
+
* answer than leaving it in place to be edited.
|
|
336
|
+
*/
|
|
337
|
+
async sendCommand(line, attachments, files, entry) {
|
|
338
|
+
if (this.slashApi === undefined) {
|
|
339
|
+
this.operationError = SLASH_DISABLED_COPY;
|
|
340
|
+
this.publish();
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
if (attachments.length > 0 && entry.acceptsAttachments !== true) {
|
|
344
|
+
// Checked here so nothing is staged and no Host round-trip is paid for a
|
|
345
|
+
// submission that cannot be admitted; the Host checks it again anyway.
|
|
346
|
+
this.operationError = slashAttachmentsCopy(entry);
|
|
347
|
+
this.publish();
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
let target;
|
|
351
|
+
try {
|
|
352
|
+
target = await this.liveTarget();
|
|
353
|
+
if (target === undefined)
|
|
354
|
+
return false;
|
|
355
|
+
let receipts = new Map();
|
|
356
|
+
if (files.length > 0) {
|
|
357
|
+
const outcome = await stageQaFiles(this.fileUpload(), target, files);
|
|
358
|
+
if (outcome.kind !== "ok") {
|
|
359
|
+
if (this.session === target) {
|
|
360
|
+
this.operationError =
|
|
361
|
+
outcome.kind === "unavailable"
|
|
362
|
+
? "Вложения недоступны на этом сервере."
|
|
363
|
+
: "Не удалось приложить файл.";
|
|
364
|
+
this.publish();
|
|
365
|
+
}
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
receipts = outcome.receipts;
|
|
369
|
+
}
|
|
370
|
+
const result = await this.slashApi.execute(this.accounts?.token() ?? "", String(target.sessionId), line, buildQaSlashAttachments(attachments, receipts));
|
|
371
|
+
if (this.session !== target)
|
|
372
|
+
return false;
|
|
373
|
+
if (!result.ok) {
|
|
374
|
+
this.operationError = slashExecuteFailureCopy(entry);
|
|
375
|
+
this.publish();
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
if (result.value.kind === "refused") {
|
|
379
|
+
this.operationError = slashHostRefusalCopy(result.value);
|
|
380
|
+
this.publish();
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
this.operationError = null;
|
|
384
|
+
this.publish();
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
catch (error) {
|
|
388
|
+
console.error("dsh-qa-surface: slash command failed", error);
|
|
389
|
+
if (this.session === target) {
|
|
390
|
+
this.operationError = slashExecuteFailureCopy(entry);
|
|
391
|
+
this.publish();
|
|
392
|
+
}
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Refresh the catalog of the bound chat. Called when a chat binds, and again
|
|
398
|
+
* when the palette opens on a catalog that has had time to go stale: the
|
|
399
|
+
* skill registry has no browser-facing change event, so a skill installed
|
|
400
|
+
* while the page is open is only discovered by asking again.
|
|
401
|
+
*/
|
|
402
|
+
async refreshSlashCatalog(force = false) {
|
|
403
|
+
const session = this.session;
|
|
404
|
+
if (session === undefined || this.disposed)
|
|
405
|
+
return;
|
|
406
|
+
if (!this.config.slashCommands.enabled || this.compatibilityReadOnly) {
|
|
407
|
+
this.applySlashCatalog(String(session.sessionId), null);
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
if (this.slashApi === undefined) {
|
|
411
|
+
this.applySlashCatalog(String(session.sessionId), undefined);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
if (this.slashLoading !== undefined)
|
|
415
|
+
return this.slashLoading;
|
|
416
|
+
if (!force && this.slashSessionId === String(session.sessionId)) {
|
|
417
|
+
if (Date.now() - this.slashLoadedAt < SLASH_CATALOG_STALE_MS)
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const sessionId = String(session.sessionId);
|
|
421
|
+
this.slashState = "loading";
|
|
422
|
+
this.publish();
|
|
423
|
+
const operation = (async () => {
|
|
424
|
+
try {
|
|
425
|
+
const result = await this.slashApi?.catalog(this.accounts?.token() ?? "", sessionId);
|
|
426
|
+
if (result === undefined)
|
|
427
|
+
return;
|
|
428
|
+
if (!result.ok) {
|
|
429
|
+
this.applySlashCatalog(sessionId, undefined);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
this.applySlashCatalog(sessionId, result.value);
|
|
433
|
+
}
|
|
434
|
+
catch (error) {
|
|
435
|
+
console.error("dsh-qa-surface: slash catalog failed", error);
|
|
436
|
+
this.applySlashCatalog(sessionId, undefined);
|
|
437
|
+
}
|
|
438
|
+
})().finally(() => {
|
|
439
|
+
if (this.slashLoading === operation)
|
|
440
|
+
this.slashLoading = undefined;
|
|
441
|
+
});
|
|
442
|
+
this.slashLoading = operation;
|
|
443
|
+
return operation;
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Install one catalog answer. `null` means the deployment runs with the
|
|
447
|
+
* slash interface off; `undefined` means the answer never arrived. The
|
|
448
|
+
* difference matters: an unreachable catalog must not turn ordinary prompts
|
|
449
|
+
* into refusals.
|
|
450
|
+
*/
|
|
451
|
+
applySlashCatalog(sessionId, catalog) {
|
|
452
|
+
if (this.session === undefined ||
|
|
453
|
+
String(this.session.sessionId) !== sessionId) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
this.slashSessionId = sessionId;
|
|
457
|
+
this.slashLoadedAt = Date.now();
|
|
458
|
+
if (catalog === null) {
|
|
459
|
+
this.slashEntries = [];
|
|
460
|
+
this.slashDenied = [];
|
|
461
|
+
this.slashSurface = "unavailable";
|
|
462
|
+
this.slashState = "idle";
|
|
463
|
+
this.slashError = null;
|
|
464
|
+
this.publish();
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
if (catalog === undefined) {
|
|
468
|
+
this.slashEntries = [];
|
|
469
|
+
this.slashDenied = [];
|
|
470
|
+
this.slashSurface = "unavailable";
|
|
471
|
+
this.slashState = "error";
|
|
472
|
+
this.slashError = "Не удалось загрузить команды";
|
|
473
|
+
this.publish();
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
this.slashEntries = catalog.entries;
|
|
477
|
+
this.slashDenied = catalog.deniedSkills;
|
|
478
|
+
this.slashSurface = catalog.commandSurface;
|
|
479
|
+
this.slashState = "ready";
|
|
480
|
+
this.slashError = null;
|
|
481
|
+
this.publish();
|
|
482
|
+
}
|
|
483
|
+
/** Whether the catalog of the bound chat was read successfully. */
|
|
484
|
+
slashCatalogReady() {
|
|
485
|
+
return this.slashState === "ready" || this.slashState === "loading";
|
|
486
|
+
}
|
|
487
|
+
/** The slash view of the current state; assembled where the state is. */
|
|
488
|
+
slashView() {
|
|
489
|
+
const enabled = this.config.slashCommands.enabled &&
|
|
490
|
+
!this.compatibilityReadOnly &&
|
|
491
|
+
this.slashSessionId !== null &&
|
|
492
|
+
this.slashApi !== undefined;
|
|
493
|
+
// Identity is the render signal: this runs on every publish, including
|
|
494
|
+
// every stream frame of a running turn, and the composer is memoized on
|
|
495
|
+
// its props. A fresh object here would re-render the composer for each
|
|
496
|
+
// frame of an answer, which is exactly what its memo exists to prevent.
|
|
497
|
+
const cached = this.slashViewCache;
|
|
498
|
+
if (cached !== undefined &&
|
|
499
|
+
cached.enabled === enabled &&
|
|
500
|
+
cached.state === this.slashState &&
|
|
501
|
+
cached.entries === this.slashEntries &&
|
|
502
|
+
cached.commandSurface === this.slashSurface &&
|
|
503
|
+
cached.deniedSkills === this.slashDenied &&
|
|
504
|
+
cached.error === this.slashError &&
|
|
505
|
+
cached.reopen === this.slashReopen) {
|
|
506
|
+
return cached;
|
|
507
|
+
}
|
|
508
|
+
const view = {
|
|
509
|
+
enabled,
|
|
510
|
+
state: this.slashState,
|
|
511
|
+
entries: this.slashEntries,
|
|
512
|
+
commandSurface: this.slashSurface,
|
|
513
|
+
deniedSkills: this.slashDenied,
|
|
514
|
+
error: this.slashError,
|
|
515
|
+
reopen: this.slashReopen,
|
|
516
|
+
};
|
|
517
|
+
this.slashViewCache = view;
|
|
518
|
+
return view;
|
|
519
|
+
}
|
|
250
520
|
/** Publish a browser-only copy before any network or Host admission awaits. */
|
|
251
521
|
beginSubmission(text, attachments) {
|
|
252
522
|
const submission = {
|
|
@@ -803,6 +1073,9 @@ export class QaSessionController {
|
|
|
803
1073
|
this.accounts?.onSessionCreated(String(this.session.sessionId));
|
|
804
1074
|
}
|
|
805
1075
|
this.publish();
|
|
1076
|
+
// Deliberately not awaited: the palette is a convenience, and a chat must
|
|
1077
|
+
// open at once whether or not the skill and command registries answer.
|
|
1078
|
+
void this.refreshSlashCatalog(true);
|
|
806
1079
|
}
|
|
807
1080
|
unbind() {
|
|
808
1081
|
this.streamPublisher.clear();
|
|
@@ -821,9 +1094,21 @@ export class QaSessionController {
|
|
|
821
1094
|
this.hostApprovals.reset();
|
|
822
1095
|
this.hostQuestions.reset();
|
|
823
1096
|
this.stopPendingPolling();
|
|
1097
|
+
// The next binding probes the Host again even for the same chat.
|
|
1098
|
+
this.pendingProbeKey = "";
|
|
824
1099
|
this.admissionPending = false;
|
|
825
1100
|
this.policyReady = false;
|
|
826
1101
|
this.compatibilityReadOnly = false;
|
|
1102
|
+
// The catalog belongs to the chat that produced it: another chat has a
|
|
1103
|
+
// different cwd, composition and role, so carrying it across would offer
|
|
1104
|
+
// actions this chat may not have.
|
|
1105
|
+
this.slashSessionId = null;
|
|
1106
|
+
this.slashEntries = [];
|
|
1107
|
+
this.slashDenied = [];
|
|
1108
|
+
this.slashSurface = "unavailable";
|
|
1109
|
+
this.slashState = "idle";
|
|
1110
|
+
this.slashError = null;
|
|
1111
|
+
this.slashLoadedAt = 0;
|
|
827
1112
|
}
|
|
828
1113
|
waitForConnection() {
|
|
829
1114
|
return waitFor(this.connection, (description) => description !== undefined, this.timeoutMs);
|
|
@@ -874,6 +1159,7 @@ export class QaSessionController {
|
|
|
874
1159
|
if (!snapshot.running) {
|
|
875
1160
|
void this.hostSources.refresh(sessionId, () => this.publish());
|
|
876
1161
|
}
|
|
1162
|
+
this.probePending(sessionId, connected);
|
|
877
1163
|
this.syncPendingPolling(snapshot.running === true && !this.compatibilityReadOnly);
|
|
878
1164
|
const projectionInput = {
|
|
879
1165
|
connected,
|
|
@@ -892,6 +1178,7 @@ export class QaSessionController {
|
|
|
892
1178
|
admissionPending: this.admissionPending || this.pendingSubmission !== undefined,
|
|
893
1179
|
chatsRevision: this.chatsRevision,
|
|
894
1180
|
viewingSubagent: this.viewingSubagent,
|
|
1181
|
+
slash: this.slashView(),
|
|
895
1182
|
config: this.config,
|
|
896
1183
|
subagentNames: this.subagentNames(),
|
|
897
1184
|
};
|
|
@@ -1041,6 +1328,12 @@ export class QaSessionController {
|
|
|
1041
1328
|
* inside an open turn, and it is Host state: polling is how the page learns
|
|
1042
1329
|
* about it, and how it reappears after a reload. One timer serves both seams,
|
|
1043
1330
|
* and each is polled only where the deployment answers it.
|
|
1331
|
+
*
|
|
1332
|
+
* A request that is already on screen keeps the timer alive past the end of
|
|
1333
|
+
* its turn: the Host settles what the turn can no longer answer — an aborted
|
|
1334
|
+
* tool call, an agent that went idle — and this keeps reading until that
|
|
1335
|
+
* settlement reaches the page, so the form goes away instead of sitting
|
|
1336
|
+
* there answerable but dead.
|
|
1044
1337
|
*/
|
|
1045
1338
|
syncPendingPolling(running) {
|
|
1046
1339
|
const approvals = this.hostApprovals.available
|
|
@@ -1049,8 +1342,9 @@ export class QaSessionController {
|
|
|
1049
1342
|
const questions = this.hostQuestions.available
|
|
1050
1343
|
? this.config.interaction.questions === "interactive"
|
|
1051
1344
|
: false;
|
|
1052
|
-
const
|
|
1053
|
-
(
|
|
1345
|
+
const pending = this.hostApprovals.list().length > 0 ||
|
|
1346
|
+
this.hostQuestions.list().length > 0;
|
|
1347
|
+
const wanted = ((running && (approvals || questions)) || pending) &&
|
|
1054
1348
|
// A subagent watched from the panel is a read-only view of a session the
|
|
1055
1349
|
// chat owns: its own requests belong to the chat, not to this binding.
|
|
1056
1350
|
this.viewingSubagent === null &&
|
|
@@ -1064,16 +1358,44 @@ export class QaSessionController {
|
|
|
1064
1358
|
const sessionId = String(this.session.sessionId);
|
|
1065
1359
|
const token = this.hostToken();
|
|
1066
1360
|
const tick = () => {
|
|
1067
|
-
|
|
1361
|
+
// A seam still on screen is re-read even where its config gate has since
|
|
1362
|
+
// closed: the Host is the authority on what it holds, and an empty answer
|
|
1363
|
+
// is what takes a settled request off the screen.
|
|
1364
|
+
if (approvals || this.hostApprovals.list().length > 0) {
|
|
1068
1365
|
void this.hostApprovals.refresh(sessionId, token, () => this.publish());
|
|
1069
1366
|
}
|
|
1070
|
-
if (questions) {
|
|
1367
|
+
if (questions || this.hostQuestions.list().length > 0) {
|
|
1071
1368
|
void this.hostQuestions.refresh(sessionId, token, () => this.publish());
|
|
1072
1369
|
}
|
|
1073
1370
|
};
|
|
1074
1371
|
this.pendingTimer = setInterval(tick, PENDING_POLL_MS);
|
|
1075
1372
|
tick();
|
|
1076
1373
|
}
|
|
1374
|
+
/**
|
|
1375
|
+
* Read what the Host parks once per chat and connection, whether or not a
|
|
1376
|
+
* turn is running. A reload binds the chat before any turn reports itself
|
|
1377
|
+
* over the fresh stream, and a turn that is already waiting on a question
|
|
1378
|
+
* would otherwise sit behind an empty composer until the operator happened
|
|
1379
|
+
* to send something.
|
|
1380
|
+
*/
|
|
1381
|
+
probePending(sessionId, connected) {
|
|
1382
|
+
const key = `${sessionId}:${connected ? "up" : "down"}:${String(this.policyReady)}`;
|
|
1383
|
+
// Attestation is what lets the Host answer about this chat at all, so the
|
|
1384
|
+
// read waits for it: the publish that follows a successful attestation
|
|
1385
|
+
// carries a new key and probes then.
|
|
1386
|
+
if (!connected || !this.policyReady || key === this.pendingProbeKey)
|
|
1387
|
+
return;
|
|
1388
|
+
this.pendingProbeKey = key;
|
|
1389
|
+
const token = this.hostToken();
|
|
1390
|
+
if (this.hostApprovals.available &&
|
|
1391
|
+
this.config.interaction.approvals === "interactive") {
|
|
1392
|
+
void this.hostApprovals.refresh(sessionId, token, () => this.publish());
|
|
1393
|
+
}
|
|
1394
|
+
if (this.hostQuestions.available &&
|
|
1395
|
+
this.config.interaction.questions === "interactive") {
|
|
1396
|
+
void this.hostQuestions.refresh(sessionId, token, () => this.publish());
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1077
1399
|
stopPendingPolling() {
|
|
1078
1400
|
if (this.pendingTimer === undefined)
|
|
1079
1401
|
return;
|