@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
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One matcher for both halves of the slash catalog.
|
|
3
|
+
*
|
|
4
|
+
* Upstream matches skills with `startsWith` and commands fuzzily, so the same
|
|
5
|
+
* query behaves differently depending on which registry answers it. QA always
|
|
6
|
+
* ranks with the ladder below, in this order:
|
|
7
|
+
*
|
|
8
|
+
* exact → prefix → name-token boundary → substring → ordered subsequence
|
|
9
|
+
*
|
|
10
|
+
* so `/tkp` finds `generate-tkp` through the token boundary and `/contract`
|
|
11
|
+
* finds both `contract-analysis` (prefix) and `review-contract` (token).
|
|
12
|
+
*
|
|
13
|
+
* Dependency-free on purpose: inlined into the browser bundle.
|
|
14
|
+
*/
|
|
15
|
+
/** How a name matched; lower is a stronger claim on the top of the list. */
|
|
16
|
+
export declare const SLASH_MATCH_EXACT = 0;
|
|
17
|
+
export declare const SLASH_MATCH_PREFIX = 1;
|
|
18
|
+
export declare const SLASH_MATCH_TOKEN = 2;
|
|
19
|
+
export declare const SLASH_MATCH_SUBSTRING = 3;
|
|
20
|
+
export declare const SLASH_MATCH_SUBSEQUENCE = 4;
|
|
21
|
+
export interface QaSlashMatch {
|
|
22
|
+
readonly rank: number;
|
|
23
|
+
/** Where the match starts; earlier wins inside one rank. */
|
|
24
|
+
readonly index: number;
|
|
25
|
+
}
|
|
26
|
+
export interface QaSlashMatchOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Whether the two weakest rungs run. Turning them off keeps the matcher
|
|
29
|
+
* predictable for deployments that read the palette as an index.
|
|
30
|
+
*/
|
|
31
|
+
readonly fuzzy?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Rank one name against a palette query. An empty query matches everything at
|
|
35
|
+
* the prefix rung, which keeps the unfiltered catalog in catalog order.
|
|
36
|
+
*/
|
|
37
|
+
export declare function matchSlashName(name: string, query: string, options?: QaSlashMatchOptions): QaSlashMatch | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Order two ranked names; the caller has already ruled out `undefined`. Ties
|
|
40
|
+
* fall back to the name, so equal-rank rows keep catalog order and the list
|
|
41
|
+
* never reshuffles between two renders of the same query.
|
|
42
|
+
*/
|
|
43
|
+
export declare function compareSlashMatches(leftName: string, left: QaSlashMatch, rightName: string, right: QaSlashMatch): number;
|
|
44
|
+
/**
|
|
45
|
+
* The matching entries in rank order. Identity of the input entries is
|
|
46
|
+
* preserved, so a caller can render them without a lookup.
|
|
47
|
+
*/
|
|
48
|
+
export declare function rankSlashEntries<T extends {
|
|
49
|
+
readonly name: string;
|
|
50
|
+
}>(entries: readonly T[], query: string, options?: QaSlashMatchOptions): readonly T[];
|
|
51
|
+
//# sourceMappingURL=matcher.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composer input classification for the slash interface.
|
|
3
|
+
*
|
|
4
|
+
* The grammars are mirrors, not inventions: a command line is what the native
|
|
5
|
+
* command runtime parses (`/name` at byte zero, name then end-of-input or
|
|
6
|
+
* whitespace, everything after kept verbatim as `rawInput`), and a skill
|
|
7
|
+
* gesture is what `dsh-tool-skill` recognises (any whitespace-bounded `/name`
|
|
8
|
+
* token with the public skill-name grammar). A line that matches neither is
|
|
9
|
+
* ordinary prose and must keep behaving exactly as it did before this feature.
|
|
10
|
+
*
|
|
11
|
+
* Deliberately dependency-free: this module is inlined into the browser
|
|
12
|
+
* bundle, where the Host's config surface must not follow it.
|
|
13
|
+
*/
|
|
14
|
+
export interface QaSlashLine {
|
|
15
|
+
readonly name: string;
|
|
16
|
+
/** Exact text after the name, including the separator whitespace. */
|
|
17
|
+
readonly args: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Parse a command-shaped line. `undefined` means the line is not one — either
|
|
21
|
+
* it is not slash-led at all, or the token after the slash is not a command
|
|
22
|
+
* name (`/usr/bin/env` stays a path, not a call).
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseSlashLine(input: string): QaSlashLine | undefined;
|
|
25
|
+
/** The palette filter of a draft, or `undefined` when the palette is closed. */
|
|
26
|
+
export declare function slashPaletteQuery(input: string): string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Every whitespace-bounded `/name` gesture in the text, first-seen order and
|
|
29
|
+
* deduplicated. The Host owns invocation; this list exists only so the QA
|
|
30
|
+
* surface can warn about a gesture its own policy withholds before the turn
|
|
31
|
+
* is submitted.
|
|
32
|
+
*/
|
|
33
|
+
export declare function skillGestures(input: string): readonly string[];
|
|
34
|
+
/** The invocation a submitted draft carries, once its route is decided. */
|
|
35
|
+
export interface QaSlashInvocation {
|
|
36
|
+
readonly name: string;
|
|
37
|
+
/** Text after the name with its separator whitespace removed. */
|
|
38
|
+
readonly args: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The invocation a draft names, independent of the catalog. Used for the
|
|
42
|
+
* routing decision and for the "unknown command" refusal, both of which must
|
|
43
|
+
* work while the catalog itself is unavailable.
|
|
44
|
+
*/
|
|
45
|
+
export declare function slashInvocation(input: string): QaSlashInvocation | undefined;
|
|
46
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admission policy for one half of the slash catalog.
|
|
3
|
+
*
|
|
4
|
+
* The policy is name-based because the persisted config is: an operator writes
|
|
5
|
+
* `generate-tkp`, never an internal id, and the same name means the same thing
|
|
6
|
+
* on every Host restart. Names that cannot be a native skill or command name
|
|
7
|
+
* are refused at config resolution rather than silently ignored at admission,
|
|
8
|
+
* so a typo surfaces while the deployment is being configured.
|
|
9
|
+
*/
|
|
10
|
+
import type { QaSlashPolicyMode } from "../types.js";
|
|
11
|
+
/** Every accepted policy mode, in the order the settings card lists them. */
|
|
12
|
+
export declare const QA_SLASH_MODES: readonly QaSlashPolicyMode[];
|
|
13
|
+
/** Longest name either registry accepts in practice; keeps host echoes bounded. */
|
|
14
|
+
export declare const QA_SLASH_NAME_MAX = 200;
|
|
15
|
+
export interface QaSlashPolicy {
|
|
16
|
+
readonly mode: QaSlashPolicyMode;
|
|
17
|
+
readonly allow: readonly string[];
|
|
18
|
+
}
|
|
19
|
+
export declare function isQaSlashPolicyMode(value: unknown): value is QaSlashPolicyMode;
|
|
20
|
+
/**
|
|
21
|
+
* Normalize one allow-list: trimmed, deduplicated, order-preserving. Refuses
|
|
22
|
+
* a name that neither registry could ever carry, and refuses `all` alongside a
|
|
23
|
+
* non-empty list, which would be two answers to one question.
|
|
24
|
+
*/
|
|
25
|
+
export declare function normalizeSlashAllow(values: readonly string[], label: string): readonly string[];
|
|
26
|
+
/** Whether the policy admits this native name. */
|
|
27
|
+
export declare function allowsSlashName(policy: QaSlashPolicy, name: string): boolean;
|
|
28
|
+
/** The admitted subset, in the order the native registry returned it. */
|
|
29
|
+
export declare function filterBySlashPolicy<T extends {
|
|
30
|
+
readonly name: string;
|
|
31
|
+
}>(entries: readonly T[], policy: QaSlashPolicy): readonly T[];
|
|
32
|
+
/**
|
|
33
|
+
* Intersect an already-admitted list with a second name set (the session's
|
|
34
|
+
* role grant). An empty set means "no second opinion" — the role system keeps
|
|
35
|
+
* its own switch, and a deployment that never enabled it grants nothing here.
|
|
36
|
+
*/
|
|
37
|
+
export declare function intersectSlashNames<T extends {
|
|
38
|
+
readonly name: string;
|
|
39
|
+
}>(entries: readonly T[], names: readonly string[] | undefined): readonly T[];
|
|
40
|
+
//# sourceMappingURL=policy.d.ts.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
import type { PluginLogger } from "@yadsh/dsh-plugin-log";
|
|
3
|
+
import type { QaSlashCatalog, QaSlashExecution, QaSlashSubmitAttachment, ResolvedQaSurfaceConfig } from "../types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Host-side bodies of the slash remotes. The `@Remote`-decorated signatures
|
|
6
|
+
* stay on the `QaSurface` service class (typert code generation reads the class
|
|
7
|
+
* shape); this context owns everything behind them.
|
|
8
|
+
*
|
|
9
|
+
* The division of labour is the whole feature in one sentence: QA decides what
|
|
10
|
+
* the palette may show and whether a line may run, and the native registries
|
|
11
|
+
* decide what a skill and a command *mean*. Nothing here reads a SKILL.md,
|
|
12
|
+
* injects instructions or re-implements a registry.
|
|
13
|
+
*/
|
|
14
|
+
export interface QaSlashRemotes {
|
|
15
|
+
/** The catalog this browser may render, already policy-filtered. */
|
|
16
|
+
catalog(token: string, sessionId: string): Promise<QaSlashCatalog>;
|
|
17
|
+
/** Run one human command line through the native command runtime. */
|
|
18
|
+
execute(token: string, sessionId: string, line: string, attachments: readonly QaSlashSubmitAttachment[]): Promise<QaSlashExecution>;
|
|
19
|
+
}
|
|
20
|
+
export interface QaSlashRemotesOptions {
|
|
21
|
+
readonly ctx: Context;
|
|
22
|
+
readonly getConfig: () => ResolvedQaSurfaceConfig;
|
|
23
|
+
readonly logger: PluginLogger;
|
|
24
|
+
/**
|
|
25
|
+
* Ownership and role check for the current QA session. Runs only while the
|
|
26
|
+
* deployment gates QA users with accounts; it throws on refusal, which is
|
|
27
|
+
* what makes a foreign session unreadable instead of merely unowned.
|
|
28
|
+
*
|
|
29
|
+
* @returns the role's user-skill grant, or `undefined` when the access
|
|
30
|
+
* system has no opinion about this session's skills.
|
|
31
|
+
*/
|
|
32
|
+
readonly sessionGrant: (token: string, sessionId: string, agent: unknown) => Promise<readonly string[] | undefined>;
|
|
33
|
+
}
|
|
34
|
+
export declare function createQaSlashRemotes(options: QaSlashRemotesOptions): QaSlashRemotes;
|
|
35
|
+
//# sourceMappingURL=remotes.d.ts.map
|
package/lib/types/types.d.ts
CHANGED
|
@@ -20,6 +20,12 @@ export type QaApprovalInteraction = "blocked" | "interactive";
|
|
|
20
20
|
* refuses the ask outright; `interactive` answers it from the QA view.
|
|
21
21
|
*/
|
|
22
22
|
export type QaQuestionInteraction = "unsupported" | "interactive";
|
|
23
|
+
/**
|
|
24
|
+
* What a deployment may write for the question seam. `enabled` is accepted as
|
|
25
|
+
* the spelling of `interactive` a feature request asked for; the resolved
|
|
26
|
+
* config carries one name only.
|
|
27
|
+
*/
|
|
28
|
+
export type QaQuestionInteractionInput = QaQuestionInteraction | "enabled";
|
|
23
29
|
/** The operator's answer to one parked tool call. */
|
|
24
30
|
export type QaApprovalDecision = "allowed-once" | "rejected";
|
|
25
31
|
/** One answered question; `custom` is free text, and may accompany `selected`. */
|
|
@@ -36,7 +42,7 @@ export interface QaQuestionAnswerItem {
|
|
|
36
42
|
*/
|
|
37
43
|
export type QaAccountRole = "user" | "reviewer" | "admin";
|
|
38
44
|
/** Administrative permissions a role grants; see `admin/permissions.ts`. */
|
|
39
|
-
export type QaPermission = "users.read" | "users.manage" | "roles.read" | "roles.manage" | "conversations.read.all" | "conversations.read.own" | "reviews.read" | "reviews.write" | "analytics.read" | "audit.read" | "settings.manage";
|
|
45
|
+
export type QaPermission = "users.read" | "users.manage" | "roles.read" | "roles.manage" | "conversations.read.all" | "conversations.read.own" | "conversations.delete" | "reviews.read" | "reviews.write" | "analytics.read" | "audit.read" | "settings.manage";
|
|
40
46
|
/**
|
|
41
47
|
* Tools split by how they become available. `always` is visible from the first
|
|
42
48
|
* model step; `skillGrantable` is a ceiling — the tool only appears once an
|
|
@@ -550,7 +556,7 @@ export interface QaSurfaceConfig {
|
|
|
550
556
|
readonly thinkingPhrases?: readonly string[];
|
|
551
557
|
readonly interaction?: {
|
|
552
558
|
readonly approvals?: QaApprovalInteraction;
|
|
553
|
-
readonly questions?:
|
|
559
|
+
readonly questions?: QaQuestionInteractionInput;
|
|
554
560
|
};
|
|
555
561
|
/**
|
|
556
562
|
* QA tool delivery. Dynamic activation keeps the QA tool schemas out of the
|
|
@@ -582,7 +588,14 @@ export interface QaSurfaceConfig {
|
|
|
582
588
|
readonly approvalPolicy?: QaApprovalPolicy;
|
|
583
589
|
readonly permissionPreset?: string;
|
|
584
590
|
readonly allowPermissionChanges?: false;
|
|
585
|
-
|
|
591
|
+
/**
|
|
592
|
+
* Master switch for the slash interface, and the only capability flag the
|
|
593
|
+
* resolver lets a deployment turn on. It opens the palette, which then
|
|
594
|
+
* offers exactly what `slashCommands` admits — human commands stay denied
|
|
595
|
+
* until the deployment names them. Nothing here widens the tool allow-list,
|
|
596
|
+
* the sandbox or the approval policy.
|
|
597
|
+
*/
|
|
598
|
+
readonly allowSlashCommands?: boolean;
|
|
586
599
|
readonly allowSettingsMutation?: false;
|
|
587
600
|
readonly allowSessionReset?: boolean;
|
|
588
601
|
readonly allowSessionRename?: false;
|
|
@@ -595,6 +608,12 @@ export interface QaSurfaceConfig {
|
|
|
595
608
|
/** Shared directories available to read-only filesystem tools. */
|
|
596
609
|
readonly sharedReadOnlyRoots?: readonly string[];
|
|
597
610
|
};
|
|
611
|
+
/**
|
|
612
|
+
* Slash interface policy. Admission only: the QA surface never implements a
|
|
613
|
+
* skill or command registry, it decides which native ones reach the palette.
|
|
614
|
+
* Inert while `lockdown.allowSlashCommands` is off.
|
|
615
|
+
*/
|
|
616
|
+
readonly slashCommands?: QaSlashCommandsConfig;
|
|
598
617
|
readonly embedding?: {
|
|
599
618
|
readonly frameAncestors?: string | null;
|
|
600
619
|
};
|
|
@@ -737,7 +756,7 @@ export interface ResolvedQaSurfaceConfig {
|
|
|
737
756
|
readonly approvalPolicy: QaApprovalPolicy;
|
|
738
757
|
readonly permissionPreset: string;
|
|
739
758
|
readonly allowPermissionChanges: false;
|
|
740
|
-
readonly allowSlashCommands:
|
|
759
|
+
readonly allowSlashCommands: boolean;
|
|
741
760
|
readonly allowSettingsMutation: false;
|
|
742
761
|
readonly allowSessionReset: boolean;
|
|
743
762
|
readonly allowSessionRename: false;
|
|
@@ -749,6 +768,7 @@ export interface ResolvedQaSurfaceConfig {
|
|
|
749
768
|
};
|
|
750
769
|
readonly sharedReadOnlyRoots: readonly string[];
|
|
751
770
|
};
|
|
771
|
+
readonly slashCommands: ResolvedQaSlashCommands;
|
|
752
772
|
readonly embedding: {
|
|
753
773
|
readonly frameAncestors: string | null;
|
|
754
774
|
};
|
|
@@ -853,6 +873,163 @@ export interface ResolvedQaSurfaceConfig {
|
|
|
853
873
|
readonly extensions: readonly string[];
|
|
854
874
|
};
|
|
855
875
|
}
|
|
876
|
+
/** Which native registry one unified entry came from. */
|
|
877
|
+
export type QaSlashEntryKind = "skill" | "command";
|
|
878
|
+
/** How one half of the catalog admits native entries. */
|
|
879
|
+
export type QaSlashPolicyMode = "deny-all" | "allow-list" | "all";
|
|
880
|
+
export interface QaSlashPolicyConfig {
|
|
881
|
+
readonly mode?: QaSlashPolicyMode;
|
|
882
|
+
readonly allow?: readonly string[];
|
|
883
|
+
}
|
|
884
|
+
export interface QaSlashPaletteConfig {
|
|
885
|
+
readonly enabled?: boolean;
|
|
886
|
+
/** Run the two weakest matcher rungs (substring, subsequence). */
|
|
887
|
+
readonly fuzzySearch?: boolean;
|
|
888
|
+
readonly maxVisible?: number;
|
|
889
|
+
readonly showDescriptions?: boolean;
|
|
890
|
+
readonly showKindBadge?: boolean;
|
|
891
|
+
}
|
|
892
|
+
export interface QaSlashCommandsConfig {
|
|
893
|
+
readonly skills?: QaSlashPolicyConfig;
|
|
894
|
+
readonly commands?: QaSlashPolicyConfig;
|
|
895
|
+
readonly palette?: QaSlashPaletteConfig;
|
|
896
|
+
}
|
|
897
|
+
export interface ResolvedQaSlashCommands {
|
|
898
|
+
/** Mirror of `lockdown.allowSlashCommands`; the one master switch. */
|
|
899
|
+
readonly enabled: boolean;
|
|
900
|
+
readonly skills: {
|
|
901
|
+
readonly mode: QaSlashPolicyMode;
|
|
902
|
+
readonly allow: readonly string[];
|
|
903
|
+
};
|
|
904
|
+
readonly commands: {
|
|
905
|
+
readonly mode: QaSlashPolicyMode;
|
|
906
|
+
readonly allow: readonly string[];
|
|
907
|
+
};
|
|
908
|
+
readonly palette: {
|
|
909
|
+
readonly enabled: boolean;
|
|
910
|
+
readonly fuzzySearch: boolean;
|
|
911
|
+
readonly maxVisible: number;
|
|
912
|
+
readonly showDescriptions: boolean;
|
|
913
|
+
readonly showKindBadge: boolean;
|
|
914
|
+
};
|
|
915
|
+
/**
|
|
916
|
+
* True when the deployment turned the master switch on without declaring a
|
|
917
|
+
* `slashCommands` section: skills then admit every user-invocable skill the
|
|
918
|
+
* session already exposes, commands stay denied. The Host logs it once, so
|
|
919
|
+
* an upgrade cannot silently widen anything.
|
|
920
|
+
*/
|
|
921
|
+
readonly legacyDefaults: boolean;
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* One palette row. `id` is the routing identity: skill and command may share a
|
|
925
|
+
* name, so identity is `skill:<name>` or `command:<name>` and never the bare
|
|
926
|
+
* name the two halves would collide on.
|
|
927
|
+
*/
|
|
928
|
+
export interface QaSlashCatalogEntry {
|
|
929
|
+
readonly id: string;
|
|
930
|
+
readonly kind: QaSlashEntryKind;
|
|
931
|
+
readonly name: string;
|
|
932
|
+
readonly description: string;
|
|
933
|
+
/** Skill-only: the native `whenToUse`, rendered as secondary copy. */
|
|
934
|
+
readonly whenToUse?: string;
|
|
935
|
+
/** Skill-only: whether the model is also offered this skill. */
|
|
936
|
+
readonly modelInvocable?: boolean;
|
|
937
|
+
/** Command-only: the advertised argument shape. */
|
|
938
|
+
readonly inputHint?: string;
|
|
939
|
+
/** Command-only: whether the executor admits composer attachments. */
|
|
940
|
+
readonly acceptsAttachments?: boolean;
|
|
941
|
+
}
|
|
942
|
+
/**
|
|
943
|
+
* Whether the command half of the catalog could be read. `inactive` is the
|
|
944
|
+
* ordinary state of a cold chat: the native command registry resolves through
|
|
945
|
+
* a live Agent, and a chat with none answers with no commands rather than an
|
|
946
|
+
* error — the palette then offers skills alone.
|
|
947
|
+
*/
|
|
948
|
+
export type QaSlashCommandSurface = "ready" | "unavailable" | "inactive";
|
|
949
|
+
export interface QaSlashCatalog {
|
|
950
|
+
/** False while `lockdown.allowSlashCommands` is off. */
|
|
951
|
+
readonly enabled: boolean;
|
|
952
|
+
readonly entries: readonly QaSlashCatalogEntry[];
|
|
953
|
+
readonly commandSurface: QaSlashCommandSurface;
|
|
954
|
+
/**
|
|
955
|
+
* User-invocable skills of this chat that the deployment or the chat's role
|
|
956
|
+
* withheld from the palette. Naming them is what lets the browser warn about
|
|
957
|
+
* a `/name` typed inside an ordinary prompt — the native gesture path never
|
|
958
|
+
* consults this list, so it is a courtesy, not an authorization.
|
|
959
|
+
*/
|
|
960
|
+
readonly deniedSkills: readonly string[];
|
|
961
|
+
}
|
|
962
|
+
/**
|
|
963
|
+
* One composer attachment offered to a human command. The same two shapes the
|
|
964
|
+
* prompt path uses — an image rides inline, a file rides its staged receipt —
|
|
965
|
+
* because the native executor admits both through one attachment store.
|
|
966
|
+
*/
|
|
967
|
+
export type QaSlashSubmitAttachment = {
|
|
968
|
+
readonly type: "image";
|
|
969
|
+
readonly mediaType: QaImageMediaType;
|
|
970
|
+
readonly data: string;
|
|
971
|
+
readonly name?: string;
|
|
972
|
+
} | {
|
|
973
|
+
readonly type: "file";
|
|
974
|
+
readonly receiptId: string;
|
|
975
|
+
};
|
|
976
|
+
/** Why a command line was refused before, or instead of, running. */
|
|
977
|
+
export type QaSlashRefusal = "slash-disabled" | "unknown-command" | "not-allowed" | "attachments-unsupported" | "inactive-session";
|
|
978
|
+
export type QaSlashCommandOutcome = {
|
|
979
|
+
readonly kind: "success";
|
|
980
|
+
readonly text?: string;
|
|
981
|
+
/** Earlier authoritative event this result is presented from. */
|
|
982
|
+
readonly sourceEventSeq?: number;
|
|
983
|
+
} | {
|
|
984
|
+
readonly kind: "error";
|
|
985
|
+
readonly text: string;
|
|
986
|
+
};
|
|
987
|
+
/**
|
|
988
|
+
* The result of one admitted command. `refused` is an admission decision and
|
|
989
|
+
* costs nothing; `executed` means the native runtime ran the handler and the
|
|
990
|
+
* lifecycle is already in the session log, which is what the transcript row
|
|
991
|
+
* projects.
|
|
992
|
+
*/
|
|
993
|
+
export type QaSlashExecution = {
|
|
994
|
+
readonly kind: "executed";
|
|
995
|
+
readonly commandId: string;
|
|
996
|
+
readonly outcome: QaSlashCommandOutcome;
|
|
997
|
+
} | {
|
|
998
|
+
readonly kind: "refused";
|
|
999
|
+
readonly reason: QaSlashRefusal;
|
|
1000
|
+
/** Host-authored detail; the browser renders its own copy for `reason`. */
|
|
1001
|
+
readonly message: string;
|
|
1002
|
+
};
|
|
1003
|
+
/** One entry of the palette the browser is filtering. */
|
|
1004
|
+
export interface QaSlashView {
|
|
1005
|
+
readonly enabled: boolean;
|
|
1006
|
+
readonly state: "idle" | "loading" | "ready" | "error";
|
|
1007
|
+
readonly entries: readonly QaSlashCatalogEntry[];
|
|
1008
|
+
readonly commandSurface: QaSlashCommandSurface;
|
|
1009
|
+
readonly deniedSkills: readonly string[];
|
|
1010
|
+
/** Set when the catalog could not be read; ordinary prompts keep working. */
|
|
1011
|
+
readonly error: string | null;
|
|
1012
|
+
/**
|
|
1013
|
+
* Bumped when the surface must re-open a palette the user had dismissed —
|
|
1014
|
+
* an ambiguous `/name` is the case that needs it. The composer watches the
|
|
1015
|
+
* counter rather than the message, so a refusal can be re-stated.
|
|
1016
|
+
*/
|
|
1017
|
+
readonly reopen: number;
|
|
1018
|
+
}
|
|
1019
|
+
/**
|
|
1020
|
+
* One human command line projected into the transcript. Commands never enter
|
|
1021
|
+
* the model conversation, so this row is their only visible trace; it is a
|
|
1022
|
+
* control row, not an assistant bubble.
|
|
1023
|
+
*/
|
|
1024
|
+
export interface QaCommandActivity {
|
|
1025
|
+
readonly commandId: string;
|
|
1026
|
+
readonly name: string;
|
|
1027
|
+
/** Verbatim text after the name, with its separator whitespace removed. */
|
|
1028
|
+
readonly args?: string;
|
|
1029
|
+
readonly state: "running" | "success" | "error";
|
|
1030
|
+
readonly resultText?: string;
|
|
1031
|
+
readonly sourceEventSeq?: number;
|
|
1032
|
+
}
|
|
856
1033
|
export interface QaSourceFilePreview {
|
|
857
1034
|
readonly path: string;
|
|
858
1035
|
readonly content: string;
|
|
@@ -927,6 +1104,11 @@ export type QaMessage = QaUserMessage | {
|
|
|
927
1104
|
readonly text: string;
|
|
928
1105
|
readonly status: "info" | "error";
|
|
929
1106
|
readonly timestamp?: number;
|
|
1107
|
+
/**
|
|
1108
|
+
* Set on a human command row. Commands stay out of the model
|
|
1109
|
+
* conversation, so this projection is the only trace the user sees.
|
|
1110
|
+
*/
|
|
1111
|
+
readonly command?: QaCommandActivity;
|
|
930
1112
|
/** Collapsible settlement row (subagent finished/stopped/failed). */
|
|
931
1113
|
readonly notice?: {
|
|
932
1114
|
readonly title: string;
|
|
@@ -1088,6 +1270,8 @@ export interface QaSessionState {
|
|
|
1088
1270
|
readonly approvals: readonly QaPendingApproval[];
|
|
1089
1271
|
/** Question requests parked for the operator's answer, oldest first. */
|
|
1090
1272
|
readonly questions: readonly QaPendingQuestion[];
|
|
1273
|
+
/** Slash palette state, keyed to the chat this snapshot describes. */
|
|
1274
|
+
readonly slash: QaSlashView;
|
|
1091
1275
|
}
|
|
1092
1276
|
/** Minimal server-trusted identity exposed to principal-scoped plugins. */
|
|
1093
1277
|
export interface QaPrincipal {
|
|
@@ -1173,7 +1357,7 @@ export interface QaReviewQueueItem {
|
|
|
1173
1357
|
readonly feedbackId?: string;
|
|
1174
1358
|
}
|
|
1175
1359
|
/** Administrative actions recorded in the audit trail. */
|
|
1176
|
-
export type QaAdminAuditAction = "user.created" | "user.updated" | "user.enabled" | "user.disabled" | "authorization.changed" | "subrole.assignment.changed" | "subrole.created" | "subrole.updated" | "subrole.deleted" | "common_capabilities.updated" | "conversation.reviewed" | "review.updated" | "review.queued" | "admin.settings.updated";
|
|
1360
|
+
export type QaAdminAuditAction = "user.created" | "user.updated" | "user.enabled" | "user.disabled" | "authorization.changed" | "subrole.assignment.changed" | "subrole.created" | "subrole.updated" | "subrole.deleted" | "common_capabilities.updated" | "conversation.reviewed" | "conversation.deleted" | "review.updated" | "review.queued" | "admin.settings.updated";
|
|
1177
1361
|
export interface QaAdminAuditEvent {
|
|
1178
1362
|
readonly id: string;
|
|
1179
1363
|
readonly timestamp: string;
|
|
@@ -1266,6 +1450,17 @@ export interface QaConversationDetail {
|
|
|
1266
1450
|
readonly reviews: readonly QaConversationReview[];
|
|
1267
1451
|
readonly queueItems: readonly QaReviewQueueItem[];
|
|
1268
1452
|
}
|
|
1453
|
+
/** What one administrative deletion removed, for the console to report. */
|
|
1454
|
+
export interface QaConversationDeletion {
|
|
1455
|
+
readonly conversationId: string;
|
|
1456
|
+
/**
|
|
1457
|
+
* Every session whose stored log is gone, the chat first: a conversation's
|
|
1458
|
+
* subagent sessions are part of it and are removed with it.
|
|
1459
|
+
*/
|
|
1460
|
+
readonly sessions: readonly string[];
|
|
1461
|
+
/** Quality rows dropped with the conversation. */
|
|
1462
|
+
readonly qualityRows: number;
|
|
1463
|
+
}
|
|
1269
1464
|
/**
|
|
1270
1465
|
* Server-side filter for the conversation list. Every field is optional and
|
|
1271
1466
|
* accepts an explicit null: the wire carries whatever the browser has, and an
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yadsh/dsh-qa-surface",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "A focused end-user QA surface backed by native DeepSeek Harness sessions",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"types": "./lib/types/index.d.ts",
|
|
17
17
|
"bin": {
|
|
18
18
|
"qa-accounts": "./lib/cli.js",
|
|
19
|
-
"qa-repair-sessions": "./scripts/repair-session-events.mjs"
|
|
19
|
+
"qa-repair-sessions": "./scripts/repair-session-events.mjs",
|
|
20
|
+
"qa-attach-sessions": "./scripts/attach-workspace-sessions.mjs"
|
|
20
21
|
},
|
|
21
22
|
"exports": {
|
|
22
23
|
".": {
|
|
@@ -76,6 +77,7 @@
|
|
|
76
77
|
"lib/**/*.js.map",
|
|
77
78
|
"lib/**/*.d.ts",
|
|
78
79
|
"scripts/repair-session-events.mjs",
|
|
80
|
+
"scripts/attach-workspace-sessions.mjs",
|
|
79
81
|
"cordis.patch.yml",
|
|
80
82
|
"compatibility.json",
|
|
81
83
|
"capability-policy.json",
|
|
@@ -131,7 +133,9 @@
|
|
|
131
133
|
"dependencies": {
|
|
132
134
|
"yaml": "^2.9.0",
|
|
133
135
|
"zod": "^4.4.3",
|
|
134
|
-
"@yadsh/dsh-
|
|
136
|
+
"@yadsh/dsh-audit-ui": "^0.1.0",
|
|
137
|
+
"@yadsh/dsh-audit-core": "^0.1.0",
|
|
138
|
+
"@yadsh/dsh-plugin-kit": "^0.3.0",
|
|
135
139
|
"@yadsh/dsh-plugin-log": "^0.4.0"
|
|
136
140
|
},
|
|
137
141
|
"devDependencies": {
|