@yadsh/dsh-qa-surface 0.9.0 → 0.11.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 +269 -2
- package/lib/access/service.js +49 -10
- package/lib/access/service.js.map +1 -1
- package/lib/account-remotes.js +41 -0
- package/lib/account-remotes.js.map +1 -1
- package/lib/accounts/database.js +140 -0
- package/lib/accounts/database.js.map +1 -1
- package/lib/accounts/errors.js.map +1 -1
- package/lib/accounts/service-token.js +103 -0
- package/lib/accounts/service-token.js.map +1 -0
- package/lib/accounts/store.js +291 -5
- package/lib/accounts/store.js.map +1 -1
- package/lib/admin/conversation-log.js +14 -8
- package/lib/admin/conversation-log.js.map +1 -1
- package/lib/admin/permissions.js +5 -0
- package/lib/admin/permissions.js.map +1 -1
- package/lib/admin/quality-store.js +4 -0
- package/lib/admin/quality-store.js.map +1 -1
- package/lib/admin/service.js +180 -7
- package/lib/admin/service.js.map +1 -1
- package/lib/admin/session-log.js +39 -8
- package/lib/admin/session-log.js.map +1 -1
- package/lib/attachment-rules.js +39 -6
- package/lib/attachment-rules.js.map +1 -1
- package/lib/cli.js +83 -1
- package/lib/cli.js.map +1 -1
- package/lib/client/QaAccountsController.js +213 -4
- package/lib/client/QaAccountsController.js.map +1 -1
- package/lib/client/QaSessionController.js +49 -21
- package/lib/client/QaSessionController.js.map +1 -1
- package/lib/client/QaSurface.js +51 -9
- package/lib/client/QaSurface.js.map +1 -1
- package/lib/client/QaSurfaceGuard.js +12 -5
- package/lib/client/QaSurfaceGuard.js.map +1 -1
- package/lib/client/admin/QaAdmin.js +14 -2
- package/lib/client/admin/QaAdmin.js.map +1 -1
- package/lib/client/admin/pages/SkillFiles.js +78 -0
- package/lib/client/admin/pages/SkillFiles.js.map +1 -0
- package/lib/client/admin/pages/Users.js +74 -3
- package/lib/client/admin/pages/Users.js.map +1 -1
- package/lib/client/admin/routes.js +8 -1
- package/lib/client/admin/routes.js.map +1 -1
- package/lib/client/admin/shared.js +5 -1
- 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/attachments.js +41 -11
- package/lib/client/attachments.js.map +1 -1
- package/lib/client/base64.js +22 -1
- package/lib/client/base64.js.map +1 -1
- package/lib/client/components/QaAgentsDrawer.js +9 -6
- package/lib/client/components/QaAgentsDrawer.js.map +1 -1
- package/lib/client/components/QaApproval.js +1 -1
- package/lib/client/components/QaApproval.js.map +1 -1
- package/lib/client/components/QaAuthGate.js +19 -10
- package/lib/client/components/QaAuthGate.js.map +1 -1
- package/lib/client/components/QaChangelog.js +56 -0
- package/lib/client/components/QaChangelog.js.map +1 -1
- package/lib/client/components/QaFilesPanel.js +17 -11
- package/lib/client/components/QaFilesPanel.js.map +1 -1
- package/lib/client/components/QaHeader.js +3 -11
- package/lib/client/components/QaHeader.js.map +1 -1
- package/lib/client/components/QaModal.js.map +1 -1
- package/lib/client/components/QaQuestions.js +4 -4
- package/lib/client/components/QaQuestions.js.map +1 -1
- package/lib/client/components/QaSidebar.js +10 -2
- package/lib/client/components/QaSidebar.js.map +1 -1
- package/lib/client/components/QaWidthHandle.js +111 -5
- package/lib/client/components/QaWidthHandle.js.map +1 -1
- package/lib/client/components/QaWorkspaceBrowser.js +231 -0
- package/lib/client/components/QaWorkspaceBrowser.js.map +1 -0
- package/lib/client/index.js +122 -53
- package/lib/client/index.js.map +1 -1
- package/lib/client/kiosk.js +51 -0
- package/lib/client/kiosk.js.map +1 -0
- package/lib/client/lineage.js +64 -0
- package/lib/client/lineage.js.map +1 -1
- package/lib/client/markdown/blocks.js +122 -7
- package/lib/client/markdown/blocks.js.map +1 -1
- package/lib/client/markdown/inline.js +108 -12
- package/lib/client/markdown/inline.js.map +1 -1
- package/lib/client/markdown/katex-css.js +12 -0
- package/lib/client/markdown/katex-css.js.map +1 -0
- package/lib/client/markdown/math.js +84 -0
- package/lib/client/markdown/math.js.map +1 -0
- package/lib/client/markdown/render.js +74 -5
- package/lib/client/markdown/render.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/attachments.js +6 -5
- package/lib/client/settings/sections/attachments.js.map +1 -1
- package/lib/client/settings/sections/notes.js +64 -0
- package/lib/client/settings/sections/notes.js.map +1 -0
- package/lib/client/settings/sections/sources.js +4 -1
- package/lib/client/settings/sections/sources.js.map +1 -1
- package/lib/client/settings/sections.js +1 -0
- package/lib/client/settings/sections.js.map +1 -1
- package/lib/client/settlement.js +1 -1
- package/lib/client/settlement.js.map +1 -1
- package/lib/client/source-preview.js +3 -0
- package/lib/client/source-preview.js.map +1 -1
- package/lib/client/styles.js +106 -18
- package/lib/client/styles.js.map +1 -1
- package/lib/client/types.js.map +1 -1
- package/lib/client/user-settings/IntegrationTokensPage.js +150 -0
- package/lib/client/user-settings/IntegrationTokensPage.js.map +1 -0
- package/lib/client/user-settings/PasswordSettingsPage.js +48 -0
- package/lib/client/user-settings/PasswordSettingsPage.js.map +1 -0
- package/lib/client/user-settings/SkillCatalog.js +2 -2
- package/lib/client/user-settings/SkillCatalog.js.map +1 -1
- package/lib/client/user-settings/UserSettingsDialog.js +17 -2
- package/lib/client/user-settings/UserSettingsDialog.js.map +1 -1
- package/lib/client/user-settings/copy.js +20 -0
- package/lib/client/user-settings/copy.js.map +1 -1
- package/lib/client.js +31007 -686
- package/lib/client.js.map +1 -1
- package/lib/config-resolvers/attachments.js +6 -5
- package/lib/config-resolvers/attachments.js.map +1 -1
- package/lib/config-resolvers/defaults.js +33 -2
- package/lib/config-resolvers/defaults.js.map +1 -1
- package/lib/config-resolvers/integration.js +94 -0
- package/lib/config-resolvers/integration.js.map +1 -0
- package/lib/config-resolvers/notes.js +26 -0
- package/lib/config-resolvers/notes.js.map +1 -0
- package/lib/config-resolvers/sources.js +4 -0
- package/lib/config-resolvers/sources.js.map +1 -1
- package/lib/config.js +107 -0
- package/lib/config.js.map +1 -1
- package/lib/index.js +348 -12
- package/lib/index.js.map +1 -1
- package/lib/integration/answer.js +100 -0
- package/lib/integration/answer.js.map +1 -0
- package/lib/integration/attachments.js +231 -0
- package/lib/integration/attachments.js.map +1 -0
- package/lib/integration/contract.js +53 -0
- package/lib/integration/contract.js.map +1 -0
- package/lib/integration/host-runner.js +243 -0
- package/lib/integration/host-runner.js.map +1 -0
- package/lib/integration/http.js +538 -0
- package/lib/integration/http.js.map +1 -0
- package/lib/integration/service.js +331 -0
- package/lib/integration/service.js.map +1 -0
- package/lib/integration/transcript-reader.js +202 -0
- package/lib/integration/transcript-reader.js.map +1 -0
- package/lib/integration/transcript.js +148 -0
- package/lib/integration/transcript.js.map +1 -0
- package/lib/personal-skills/index.js +2 -2
- package/lib/personal-skills/index.js.map +1 -1
- package/lib/personal-skills/paths.js +14 -8
- package/lib/personal-skills/paths.js.map +1 -1
- package/lib/personal-skills/provider.js +16 -4
- package/lib/personal-skills/provider.js.map +1 -1
- package/lib/personal-skills/service.js +240 -30
- package/lib/personal-skills/service.js.map +1 -1
- package/lib/prompt-notes.js +103 -6
- package/lib/prompt-notes.js.map +1 -1
- package/lib/provenance/document-preview.js +60 -0
- package/lib/provenance/document-preview.js.map +1 -0
- package/lib/provenance/file-preview.js +191 -19
- package/lib/provenance/file-preview.js.map +1 -1
- package/lib/qa-tools/catalog.js +18 -1
- package/lib/qa-tools/catalog.js.map +1 -1
- package/lib/qa-tools/docs-tools.js +563 -0
- package/lib/qa-tools/docs-tools.js.map +1 -0
- package/lib/qa-tools/file-delete-gate.js +60 -0
- package/lib/qa-tools/file-delete-gate.js.map +1 -0
- package/lib/qa-tools/file-delete.js +156 -0
- package/lib/qa-tools/file-delete.js.map +1 -0
- package/lib/resolve-config.js +6 -0
- package/lib/resolve-config.js.map +1 -1
- package/lib/secure-session.js +83 -3
- package/lib/secure-session.js.map +1 -1
- package/lib/shared/documents.js +22 -0
- package/lib/shared/documents.js.map +1 -0
- package/lib/shared/integration-tokens.js +23 -0
- package/lib/shared/integration-tokens.js.map +1 -0
- package/lib/typert.host.js +1320 -134
- package/lib/typert.remote-client.d.ts +33 -1
- package/lib/typert.remote-client.js +1102 -74
- package/lib/types/access/service.d.ts +33 -2
- package/lib/types/account-remotes.d.ts +30 -1
- package/lib/types/accounts/database.d.ts +53 -0
- package/lib/types/accounts/errors.d.ts +5 -1
- package/lib/types/accounts/service-token.d.ts +70 -0
- package/lib/types/accounts/store.d.ts +131 -1
- package/lib/types/admin/conversation-log.d.ts +12 -0
- package/lib/types/admin/service.d.ts +56 -1
- package/lib/types/admin/session-log.d.ts +21 -0
- package/lib/types/attachment-rules.d.ts +26 -3
- package/lib/types/client/QaAccountsController.d.ts +64 -5
- package/lib/types/client/QaSessionController.d.ts +24 -2
- package/lib/types/client/admin/pages/SkillFiles.d.ts +17 -0
- package/lib/types/client/admin/routes.d.ts +2 -0
- package/lib/types/client/admin/shared.d.ts +2 -0
- package/lib/types/client/attachments.d.ts +21 -4
- package/lib/types/client/base64.d.ts +13 -1
- package/lib/types/client/components/QaAgentsDrawer.d.ts +2 -1
- package/lib/types/client/components/QaFilesPanel.d.ts +16 -4
- package/lib/types/client/components/QaHeader.d.ts +9 -3
- package/lib/types/client/components/QaModal.d.ts +4 -3
- package/lib/types/client/components/QaWidthHandle.d.ts +48 -2
- package/lib/types/client/components/QaWorkspaceBrowser.d.ts +15 -0
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/kiosk.d.ts +34 -0
- package/lib/types/client/lineage.d.ts +42 -0
- package/lib/types/client/markdown/blocks.d.ts +14 -1
- package/lib/types/client/markdown/inline.d.ts +23 -7
- package/lib/types/client/markdown/katex-css.d.ts +11 -0
- package/lib/types/client/markdown/math.d.ts +23 -0
- package/lib/types/client/markdown/render.d.ts +2 -1
- package/lib/types/client/settings/sections/notes.d.ts +12 -0
- package/lib/types/client/settings/sections.d.ts +1 -0
- package/lib/types/client/settlement.d.ts +4 -1
- package/lib/types/client/styles.d.ts +15 -2
- package/lib/types/client/types.d.ts +120 -1
- package/lib/types/client/user-settings/IntegrationTokensPage.d.ts +21 -0
- package/lib/types/client/user-settings/PasswordSettingsPage.d.ts +19 -0
- package/lib/types/client/user-settings/UserSettingsDialog.d.ts +11 -2
- package/lib/types/client/user-settings/copy.d.ts +10 -1
- package/lib/types/config-resolvers/integration.d.ts +47 -0
- package/lib/types/config-resolvers/notes.d.ts +10 -0
- package/lib/types/config.d.ts +10 -0
- package/lib/types/index.d.ts +111 -1
- package/lib/types/integration/answer.d.ts +65 -0
- package/lib/types/integration/attachments.d.ts +85 -0
- package/lib/types/integration/contract.d.ts +196 -0
- package/lib/types/integration/host-runner.d.ts +44 -0
- package/lib/types/integration/http.d.ts +79 -0
- package/lib/types/integration/service.d.ts +120 -0
- package/lib/types/integration/transcript-reader.d.ts +100 -0
- package/lib/types/integration/transcript.d.ts +59 -0
- package/lib/types/personal-skills/index.d.ts +2 -2
- package/lib/types/personal-skills/paths.d.ts +15 -3
- package/lib/types/personal-skills/provider.d.ts +14 -2
- package/lib/types/personal-skills/service.d.ts +89 -14
- package/lib/types/prompt-notes.d.ts +37 -0
- package/lib/types/provenance/document-preview.d.ts +28 -0
- package/lib/types/provenance/file-preview.d.ts +69 -2
- package/lib/types/qa-tools/catalog.d.ts +1 -1
- package/lib/types/qa-tools/docs-tools.d.ts +160 -0
- package/lib/types/qa-tools/file-delete-gate.d.ts +42 -0
- package/lib/types/qa-tools/file-delete.d.ts +32 -0
- package/lib/types/secure-session.d.ts +52 -1
- package/lib/types/shared/documents.d.ts +20 -0
- package/lib/types/shared/integration-tokens.d.ts +24 -0
- package/lib/types/types.d.ts +263 -3
- package/lib/types/ui-mode.d.ts +18 -0
- package/lib/ui-mode.js +22 -0
- package/lib/ui-mode.js.map +1 -0
- package/package.json +12 -10
|
@@ -4,7 +4,7 @@ import type { ScopeKey } from "@deepseek-ai/dsh-scope";
|
|
|
4
4
|
import type { PluginLogger } from "@yadsh/dsh-plugin-log";
|
|
5
5
|
import type { QaAccounts } from "../accounts/store.js";
|
|
6
6
|
import type { QaAgentToolGrants } from "../enforcement/tool-grants.js";
|
|
7
|
-
import type { QaAccessAdminSnapshot, QaCapabilityConfig, QaCapabilityDescriptor, QaCapabilitySelection, QaClaimResult, QaCurrentAccess, QaEffectiveCapabilityPolicy, QaSessionAccess, QaSkillActivationRecord, QaSkillAssignmentOverride, QaSubrole, QaUserAccess, ResolvedQaSurfaceConfig } from "../types.js";
|
|
7
|
+
import type { QaAccessAdminSnapshot, QaAccountUserPublic, QaCapabilityConfig, QaCapabilityDescriptor, QaCapabilitySelection, QaClaimResult, QaCurrentAccess, QaEffectiveCapabilityPolicy, QaSessionAccess, QaSkillActivationRecord, QaSkillAssignmentOverride, QaSubrole, QaUserAccess, ResolvedQaSurfaceConfig } from "../types.js";
|
|
8
8
|
import type { QaSessionLogReader } from "../admin/session-log.js";
|
|
9
9
|
import { QaCapabilityCatalog, type CapabilityCatalogSnapshot } from "./capability-catalog.js";
|
|
10
10
|
import { QaRoleRepository } from "./role-repository.js";
|
|
@@ -128,7 +128,38 @@ export declare class QaAccessService {
|
|
|
128
128
|
private maybeSweepDelegatedSessions;
|
|
129
129
|
current(token: string): QaCurrentAccess;
|
|
130
130
|
session(token: string, sessionId: string): QaSessionAccess;
|
|
131
|
-
reserveSession(token: string, sessionId: string, requestedSubrole: string | null, adminPreview: boolean):
|
|
131
|
+
reserveSession(token: string, sessionId: string, requestedSubrole: string | null, adminPreview: boolean): QaAccountUserPublic;
|
|
132
|
+
/**
|
|
133
|
+
* The same reservation for a chat opened by an already-authenticated
|
|
134
|
+
* account — the integration API's path. The subrole is the account's own
|
|
135
|
+
* default: an external caller is never offered a choice of capabilities,
|
|
136
|
+
* and an administrator's preview flag stays a browser-only affordance.
|
|
137
|
+
*
|
|
138
|
+
* @param user - the account the chat belongs to.
|
|
139
|
+
* @param sessionId - the Host-generated session id to reserve.
|
|
140
|
+
* @returns the account, as the browser path returns it.
|
|
141
|
+
*/
|
|
142
|
+
reserveSessionForUser(user: QaAccountUserPublic, sessionId: string): QaAccountUserPublic;
|
|
143
|
+
private reserveForUser;
|
|
144
|
+
/**
|
|
145
|
+
* The policy of one session, addressed by its authenticated account.
|
|
146
|
+
*
|
|
147
|
+
* Split from {@link policyForSession} so the integration API — whose caller
|
|
148
|
+
* proved an account with a service token instead of a browser one — freezes
|
|
149
|
+
* exactly the same capability snapshot. What the two entry points share is
|
|
150
|
+
* everything after identity; the identity check itself is the only part that
|
|
151
|
+
* differs, and it stays with whoever holds the credential.
|
|
152
|
+
*
|
|
153
|
+
* @param owner - the account that owns the session, or undefined when the
|
|
154
|
+
* deployment has no accounts service at all.
|
|
155
|
+
* @param sessionId - the session being admitted.
|
|
156
|
+
* @param agent - its live agent, for the tool catalog the policy resolves against.
|
|
157
|
+
* @returns the policy, or undefined without an accounts service.
|
|
158
|
+
*/
|
|
159
|
+
policyForSessionOwner(owner: {
|
|
160
|
+
readonly id: string;
|
|
161
|
+
} | undefined, sessionId: string, agent: Agent): Promise<QaResolvedSessionPolicy | undefined>;
|
|
162
|
+
/** The browser's entry point: the token resolves the owning account. */
|
|
132
163
|
policyForSession(token: string, sessionId: string, agent: Agent): Promise<QaResolvedSessionPolicy | undefined>;
|
|
133
164
|
/** Activation history of one session, for review and quality analysis. */
|
|
134
165
|
skillActivations(token: string, sessionId: string): readonly QaSkillActivationRecord[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PluginLogger } from "@yadsh/dsh-plugin-log";
|
|
2
2
|
import { QaAccounts } from "./accounts/store.js";
|
|
3
|
-
import type { QaAccountProfileInput, QaAccountSession, QaAccountStartersInput, QaAccountUserPublic, QaOwnershipEntry, QaWhoamiResult, ResolvedQaSurfaceConfig } from "./types.js";
|
|
3
|
+
import type { QaAccountProfileInput, QaAccountSession, QaAccountStartersInput, QaAccountUserPublic, QaIssuedServiceToken, QaOwnershipEntry, QaServiceTokenCreateInput, QaServiceTokenSummary, QaWhoamiResult, ResolvedQaSurfaceConfig } from "./types.js";
|
|
4
4
|
/**
|
|
5
5
|
* Host-side bodies of the account remotes the `QaSurface` service exposes.
|
|
6
6
|
* The `@Remote`-decorated signatures stay on the service class (typert code
|
|
@@ -21,6 +21,18 @@ export interface QaAccountRemotes {
|
|
|
21
21
|
runAsync<T>(operation: () => Promise<T>, sessionIdForLog?: string): Promise<T>;
|
|
22
22
|
register(email: string, password: string): QaAccountSession;
|
|
23
23
|
login(email: string, password: string): QaAccountSession;
|
|
24
|
+
/**
|
|
25
|
+
* Replace the token account's own password. The token is the identity, so a
|
|
26
|
+
* browser can only ever change its own credential; the returned session
|
|
27
|
+
* carries the fresh token that keeps this browser signed in.
|
|
28
|
+
*/
|
|
29
|
+
changePassword(token: string, currentPassword: string, nextPassword: string): QaAccountSession;
|
|
30
|
+
/**
|
|
31
|
+
* File a forgotten-password request for the operator queue. The answer is
|
|
32
|
+
* deliberately the same for every address, so the sign-in screen cannot be
|
|
33
|
+
* used to learn which accounts exist.
|
|
34
|
+
*/
|
|
35
|
+
requestPasswordReset(email: string): void;
|
|
24
36
|
/** Identity probe; safe to call with an empty or expired token. */
|
|
25
37
|
whoami(token: string): QaWhoamiResult;
|
|
26
38
|
/**
|
|
@@ -45,6 +57,23 @@ export interface QaAccountRemotes {
|
|
|
45
57
|
updateProfile(token: string, input: QaAccountProfileInput): QaAccountUserPublic;
|
|
46
58
|
/** Replace the caller's own starter buttons; the token is the identity. */
|
|
47
59
|
updateStarters(token: string, input: QaAccountStartersInput): QaAccountUserPublic;
|
|
60
|
+
/**
|
|
61
|
+
* The caller's own integration tokens, newest last. Never a secret: the
|
|
62
|
+
* plaintext exists only in the answer that minted it.
|
|
63
|
+
*/
|
|
64
|
+
listServiceTokens(token: string): {
|
|
65
|
+
readonly tokens: readonly QaServiceTokenSummary[];
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Mint one integration token for the caller. Refused while the integration
|
|
69
|
+
* API is off: a credential that authenticates nothing is a credential that
|
|
70
|
+
* only leaks.
|
|
71
|
+
*/
|
|
72
|
+
createServiceToken(token: string, input: QaServiceTokenCreateInput): QaIssuedServiceToken;
|
|
73
|
+
/** Revoke one of the caller's own integration tokens, by id. */
|
|
74
|
+
revokeServiceToken(token: string, tokenId: string): {
|
|
75
|
+
readonly revoked: boolean;
|
|
76
|
+
};
|
|
48
77
|
}
|
|
49
78
|
/** Build the account-remotes context for one `QaSurface` service instance. */
|
|
50
79
|
export declare function createQaAccountRemotes(options: {
|
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
import { SqliteDatabase } from "@yadsh/dsh-plugin-kit/sqlite";
|
|
2
2
|
import type { QaSkillActivationRecord } from "../types.js";
|
|
3
3
|
import { type AccountsFile, type StoredOwnership, type StoredUser } from "./file.js";
|
|
4
|
+
interface PasswordResetRow {
|
|
5
|
+
readonly user_id: string;
|
|
6
|
+
readonly requested_at: string;
|
|
7
|
+
readonly last_requested_at: string;
|
|
8
|
+
readonly request_count: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* One integration token as it is written. The digest is the only form of the
|
|
12
|
+
* credential storage ever holds; the plaintext exists in the mint call and
|
|
13
|
+
* nowhere else.
|
|
14
|
+
*/
|
|
15
|
+
export interface StoredServiceToken {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly userId: string;
|
|
18
|
+
readonly label: string;
|
|
19
|
+
/** SHA-256 of the token's secret, hex encoded. */
|
|
20
|
+
readonly hash: string;
|
|
21
|
+
readonly scopes: readonly string[];
|
|
22
|
+
readonly createdAt: string;
|
|
23
|
+
readonly expiresAt: string;
|
|
24
|
+
readonly lastUsedAt: string | null;
|
|
25
|
+
readonly revokedAt: string | null;
|
|
26
|
+
readonly useCount: number;
|
|
27
|
+
}
|
|
4
28
|
/** One ownership record as it is written, before it becomes a row. */
|
|
5
29
|
export interface OwnershipWrite {
|
|
6
30
|
readonly sessionId: string;
|
|
@@ -39,6 +63,16 @@ export declare class QaAccountsDatabase {
|
|
|
39
63
|
insertUser(user: StoredUser): void;
|
|
40
64
|
/** Replace one account's row in place. */
|
|
41
65
|
updateUser(user: StoredUser): void;
|
|
66
|
+
/**
|
|
67
|
+
* Record one forgotten-password request. A repeat from the same account
|
|
68
|
+
* moves the row up the queue and counts up, so the console shows "asked
|
|
69
|
+
* again" rather than an identical-looking pile of rows.
|
|
70
|
+
*/
|
|
71
|
+
recordPasswordReset(userId: string, at: string): void;
|
|
72
|
+
/** Every pending request, newest first. */
|
|
73
|
+
listPasswordResets(): readonly PasswordResetRow[];
|
|
74
|
+
/** Drop an account's pending request: it was answered, or it is moot. */
|
|
75
|
+
clearPasswordReset(userId: string): void;
|
|
42
76
|
/** Record a successful sign-in without rewriting the account. */
|
|
43
77
|
touchLogin(userId: string, at: string): void;
|
|
44
78
|
/** Claim one chat for an account. */
|
|
@@ -49,6 +83,24 @@ export declare class QaAccountsDatabase {
|
|
|
49
83
|
updateOwnership(sessionId: string, owner: StoredOwnership): void;
|
|
50
84
|
/** Forget chats: a rolled-back reservation, or a chat the Harness lost. */
|
|
51
85
|
deleteOwnership(sessionIds: readonly string[]): void;
|
|
86
|
+
/** Record one freshly minted integration token (digest only, never a secret). */
|
|
87
|
+
insertServiceToken(token: StoredServiceToken): void;
|
|
88
|
+
/**
|
|
89
|
+
* One integration token by id, revoked and expired ones included.
|
|
90
|
+
*
|
|
91
|
+
* The caller needs the revoked ones: presenting a revoked token must be a
|
|
92
|
+
* refusal, not a lookup miss, and the two are only distinguishable when the
|
|
93
|
+
* row still answers.
|
|
94
|
+
*/
|
|
95
|
+
serviceToken(tokenId: string): StoredServiceToken | undefined;
|
|
96
|
+
/** Every integration token of one account, oldest first. */
|
|
97
|
+
serviceTokensOf(userId: string): readonly StoredServiceToken[];
|
|
98
|
+
/** Record that one token was used, without rewriting its row. */
|
|
99
|
+
touchServiceToken(tokenId: string, at: string): void;
|
|
100
|
+
/** Revoke one token; already-revoked tokens keep their first timestamp. */
|
|
101
|
+
revokeServiceToken(tokenId: string, at: string): boolean;
|
|
102
|
+
/** Revoke every token of one account; returns how many were still live. */
|
|
103
|
+
revokeServiceTokensOf(userId: string, at: string): number;
|
|
52
104
|
/**
|
|
53
105
|
* Append one skill activation and keep the newest `limit` of them. The list
|
|
54
106
|
* is bounded because a long session may activate skills many times, and the
|
|
@@ -81,4 +133,5 @@ export declare class QaAccountsDatabase {
|
|
|
81
133
|
private gcSnapshots;
|
|
82
134
|
private parseLegacyFile;
|
|
83
135
|
}
|
|
136
|
+
export {};
|
|
84
137
|
//# sourceMappingURL=database.d.ts.map
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
* marker pattern the attestation path established; the browser maps them to
|
|
4
4
|
* audience-safe copy and the precise cause stays in the Host logs.
|
|
5
5
|
*/
|
|
6
|
-
export type QaAccountsErrorReason = "auth-required" | "admin-required" | "forbidden" | "invalid-credentials"
|
|
6
|
+
export type QaAccountsErrorReason = "auth-required" | "admin-required" | "forbidden" | "invalid-credentials"
|
|
7
|
+
/** The signed-in user typed a current password that does not match. */
|
|
8
|
+
| "invalid-current-password" | "account-disabled" | "email-taken" | "invalid-email" | "invalid-display-name" | "invalid-profile" | "profile-disabled" | "invalid-starters" | "starters-disabled"
|
|
9
|
+
/** The endpoint this credential is for is switched off on this deployment. */
|
|
10
|
+
| "integration-disabled" | "invalid-role" | "weak-password" | "registration-disabled" | "rate-limited" | "session-owned-elsewhere"
|
|
7
11
|
/** The conversation a deletion names is not one this deployment knows. */
|
|
8
12
|
| "conversation-unknown"
|
|
9
13
|
/** The Harness still holds the conversation open, so its log would come back. */
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { QA_SERVICE_TOKEN_DEFAULT_SCOPES, QA_SERVICE_TOKEN_LABEL_MAX, QA_SERVICE_TOKEN_SCOPES, QA_SERVICE_TOKEN_TTL_DAYS_MAX, QA_SERVICE_TOKEN_TTL_DAYS_MIN } from "../shared/integration-tokens.js";
|
|
2
|
+
import type { QaServiceTokenScope } from "../types.js";
|
|
3
|
+
export { QA_SERVICE_TOKEN_DEFAULT_SCOPES, QA_SERVICE_TOKEN_LABEL_MAX, QA_SERVICE_TOKEN_SCOPES, QA_SERVICE_TOKEN_TTL_DAYS_MAX, QA_SERVICE_TOKEN_TTL_DAYS_MIN, };
|
|
4
|
+
export type { QaServiceTokenScope };
|
|
5
|
+
/**
|
|
6
|
+
* Integration tokens: the credential a non-browser application presents to the
|
|
7
|
+
* QA HTTP API.
|
|
8
|
+
*
|
|
9
|
+
* They are deliberately a second, separate credential from the browser auth
|
|
10
|
+
* token (`accounts/token.ts`). The browser token is an HMAC over a payload that
|
|
11
|
+
* carries the account's `tokenVersion`; bumping that version — what a password
|
|
12
|
+
* change does — signs every browser out. An integration token must survive
|
|
13
|
+
* that: a service that keeps a Jira bridge running cannot be logged out by a
|
|
14
|
+
* human editing their profile. What it *does* share is the accounts database,
|
|
15
|
+
* so a disabled account and the operator's "revoke every token" command still
|
|
16
|
+
* reach it.
|
|
17
|
+
*
|
|
18
|
+
* The plaintext is minted once and never stored. What rests in the database is
|
|
19
|
+
* a SHA-256 digest of the secret, so a leaked database is not a leaked
|
|
20
|
+
* credential, and the token id in front of the secret makes verification one
|
|
21
|
+
* indexed lookup instead of a scan over every account's digests.
|
|
22
|
+
*/
|
|
23
|
+
/** Marks a credential as an integration token in a log line, a paste or a grep. */
|
|
24
|
+
export declare const QA_SERVICE_TOKEN_PREFIX = "qsat";
|
|
25
|
+
/** One minted token: the plaintext to hand over, plus what storage keeps. */
|
|
26
|
+
export interface MintedQaServiceToken {
|
|
27
|
+
/** Shown once and never recoverable. */
|
|
28
|
+
readonly token: string;
|
|
29
|
+
readonly tokenId: string;
|
|
30
|
+
/** The digest the store persists. */
|
|
31
|
+
readonly hash: string;
|
|
32
|
+
}
|
|
33
|
+
/** The parts a presented token splits into, or null when it is not our shape. */
|
|
34
|
+
export interface ParsedQaServiceToken {
|
|
35
|
+
readonly tokenId: string;
|
|
36
|
+
readonly secret: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Normalize a scope list: known scopes only, duplicates dropped, declared
|
|
40
|
+
* order preserved. An unknown scope is not an error the caller can see
|
|
41
|
+
* through — it is simply not granted.
|
|
42
|
+
* @param values - scope tokens as configured or requested.
|
|
43
|
+
* @returns the accepted scopes.
|
|
44
|
+
*/
|
|
45
|
+
export declare function normalizeServiceTokenScopes(values: readonly string[]): readonly QaServiceTokenScope[];
|
|
46
|
+
/** The digest of one secret, hex encoded, in the one shape verification expects. */
|
|
47
|
+
export declare function serviceTokenHash(secret: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* Mint one token. The id is a UUID so the operator can name a token in a
|
|
50
|
+
* revocation request without ever holding its plaintext again.
|
|
51
|
+
* @returns the plaintext, its id and the digest storage keeps.
|
|
52
|
+
*/
|
|
53
|
+
export declare function mintServiceToken(): MintedQaServiceToken;
|
|
54
|
+
/**
|
|
55
|
+
* Split a presented token. Anything that is not exactly
|
|
56
|
+
* `qsat.<uuid>.<secret>` is refused here rather than looked up: a malformed
|
|
57
|
+
* credential must not reach the database at all.
|
|
58
|
+
* @param token - the raw `Authorization: Bearer` value.
|
|
59
|
+
* @returns the id and secret, or null.
|
|
60
|
+
*/
|
|
61
|
+
export declare function parseServiceToken(token: string): ParsedQaServiceToken | null;
|
|
62
|
+
/**
|
|
63
|
+
* Constant-time digest comparison. Both sides are hex digests of a fixed
|
|
64
|
+
* length, so the length check leaks nothing beyond "this is not a digest".
|
|
65
|
+
* @param secret - the presented secret.
|
|
66
|
+
* @param hash - the digest storage holds.
|
|
67
|
+
* @returns whether they are the same secret.
|
|
68
|
+
*/
|
|
69
|
+
export declare function serviceTokenMatches(secret: string, hash: string): boolean;
|
|
70
|
+
//# sourceMappingURL=service-token.d.ts.map
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import type { QaAccountIdentityField, QaAccountProfile, QaAccountProfileInput, QaAccountRole, QaAccountSession, QaAccountStartersInput, QaAccountUserPublic, QaAccessUser, QaAdminAccountRow, QaClaimResult, QaEffectiveCapabilityPolicy, QaOwnershipEntry, QaSkillActivationRecord, QaUserAccess, QaWhoamiResult } from "../types.js";
|
|
1
|
+
import type { QaAccountIdentityField, QaAccountProfile, QaAccountProfileInput, QaAccountRole, QaAccountSession, QaAccountStartersInput, QaAccountUserPublic, QaAccessUser, QaAdminAccountRow, QaClaimResult, QaEffectiveCapabilityPolicy, QaOwnershipEntry, QaIssuedServiceToken, QaPasswordResetRequest, QaServiceTokenSummary, QaSkillActivationRecord, QaUserAccess, QaWhoamiResult } from "../types.js";
|
|
2
2
|
import { type StoredOwnership, type StoredUser } from "./file.js";
|
|
3
|
+
import { type QaServiceTokenScope } from "./service-token.js";
|
|
3
4
|
export { QaAccountsError } from "./errors.js";
|
|
4
5
|
export type { QaAccountsErrorReason } from "./errors.js";
|
|
5
6
|
export { defaultAccountsFilePath } from "./file.js";
|
|
7
|
+
export type { QaIssuedServiceToken, QaServiceTokenSummary } from "../types.js";
|
|
6
8
|
export interface QaAccountsOptions {
|
|
7
9
|
readonly sessionTtlDays: number;
|
|
8
10
|
readonly allowRegistration: boolean;
|
|
@@ -35,6 +37,27 @@ export interface QaSessionFacts {
|
|
|
35
37
|
/** True when the session is a delegated child of another chat. */
|
|
36
38
|
readonly hasParent?: boolean;
|
|
37
39
|
}
|
|
40
|
+
/** What a caller asks for when it mints an integration token. */
|
|
41
|
+
export interface QaServiceTokenIssueInput {
|
|
42
|
+
/** A note for the operator's token list; empty becomes a generic label. */
|
|
43
|
+
readonly label?: string;
|
|
44
|
+
/** Requested scopes; unknown ones are dropped, empty falls back to `ask`. */
|
|
45
|
+
readonly scopes?: readonly string[];
|
|
46
|
+
/** How long the token lives; bounded, and the session TTL when omitted. */
|
|
47
|
+
readonly ttlDays?: number;
|
|
48
|
+
/**
|
|
49
|
+
* The account the token belongs to. An administrator may name somebody
|
|
50
|
+
* else; everybody else is refused, so an integration credential is always
|
|
51
|
+
* issued with an explicit owner.
|
|
52
|
+
*/
|
|
53
|
+
readonly userId?: string;
|
|
54
|
+
}
|
|
55
|
+
/** What a presented integration token resolved to. */
|
|
56
|
+
export interface QaVerifiedServiceToken {
|
|
57
|
+
readonly tokenId: string;
|
|
58
|
+
readonly userId: string;
|
|
59
|
+
readonly scopes: readonly QaServiceTokenScope[];
|
|
60
|
+
}
|
|
38
61
|
/**
|
|
39
62
|
* How long an unowned session stays claimable at access time. Mirrors the
|
|
40
63
|
* admission bootstrap window: a session older than this has either user
|
|
@@ -117,6 +140,23 @@ export declare class QaAccounts {
|
|
|
117
140
|
readonly subroleId: string;
|
|
118
141
|
readonly adminPreview?: boolean;
|
|
119
142
|
}): QaAccountUserPublic;
|
|
143
|
+
/**
|
|
144
|
+
* The same reservation for an account resolved by another credential.
|
|
145
|
+
*
|
|
146
|
+
* The caller has authenticated the account already — the integration API
|
|
147
|
+
* checks its service token before it gets here — so this entry point takes
|
|
148
|
+
* the account rather than a token. It is deliberately not reachable from a
|
|
149
|
+
* remote: nothing a browser can send may name somebody else's account.
|
|
150
|
+
*
|
|
151
|
+
* @param user - the authenticated account the chat belongs to.
|
|
152
|
+
* @param sessionId - the Host-generated session id to reserve.
|
|
153
|
+
* @param access - the pinned subrole and preview flag, when the path has them.
|
|
154
|
+
* @returns the account, as the token-shaped entry returns it.
|
|
155
|
+
*/
|
|
156
|
+
reserveSessionForUser(user: QaAccountUserPublic, sessionId: string, access?: {
|
|
157
|
+
readonly subroleId: string;
|
|
158
|
+
readonly adminPreview?: boolean;
|
|
159
|
+
}): QaAccountUserPublic;
|
|
120
160
|
/** Roll back a reservation if Host creation failed before a chat existed. */
|
|
121
161
|
releaseSessionReservation(userId: string, sessionId: string): void;
|
|
122
162
|
/** Bulk-claim a browser's local chat index; foreign ids come back as conflicts. */
|
|
@@ -278,6 +318,34 @@ export declare class QaAccounts {
|
|
|
278
318
|
* still theirs after signing in again.
|
|
279
319
|
*/
|
|
280
320
|
setPassword(email: string, password: string): QaAccountUserPublic;
|
|
321
|
+
/**
|
|
322
|
+
* Self-service password change: verify the caller's current password, then
|
|
323
|
+
* replace it. The token version bumps, so every other browser holding this
|
|
324
|
+
* account's token is signed out — that is the point of a change after a leak
|
|
325
|
+
* — but the caller's own session must not die from its own write, so a fresh
|
|
326
|
+
* token minted on the bumped version comes back with the account.
|
|
327
|
+
*/
|
|
328
|
+
changePassword(token: string, currentPassword: string, nextPassword: string): QaAccountSession;
|
|
329
|
+
/**
|
|
330
|
+
* A forgotten-password request from the sign-in screen. The caller learns
|
|
331
|
+
* nothing: a known address, an unknown one and a disabled account all take
|
|
332
|
+
* the same path out of here, and only the operator-visible queue records the
|
|
333
|
+
* one real request. The attempt still spends the authentication budget, so
|
|
334
|
+
* the screen cannot be used to flood that queue.
|
|
335
|
+
*/
|
|
336
|
+
requestPasswordReset(email: string): void;
|
|
337
|
+
/**
|
|
338
|
+
* Pending requests, newest first. A request whose account no longer exists is
|
|
339
|
+
* dropped rather than listed: the console has no account to act on.
|
|
340
|
+
*/
|
|
341
|
+
passwordResetRequests(): readonly QaPasswordResetRequest[];
|
|
342
|
+
/**
|
|
343
|
+
* Operator write: set a new password for an account addressed by id — the
|
|
344
|
+
* console works in ids, not addresses — and answer its pending request. Same
|
|
345
|
+
* token-version bump as {@link setPassword}, so every live session of that
|
|
346
|
+
* account dies and the user signs in with the new password.
|
|
347
|
+
*/
|
|
348
|
+
resetUserPassword(userId: string, password: string): QaAccountUserPublic;
|
|
281
349
|
/**
|
|
282
350
|
* Disable an account: future logins are refused and every live token is
|
|
283
351
|
* invalidated (the token version bumps). `enable` does not resurrect old
|
|
@@ -286,6 +354,60 @@ export declare class QaAccounts {
|
|
|
286
354
|
setUserDisabled(email: string, disabled: boolean): QaAccountUserPublic;
|
|
287
355
|
/** Invalidate every token of the account (password-leak response). */
|
|
288
356
|
revokeTokens(email: string): QaAccountUserPublic;
|
|
357
|
+
/**
|
|
358
|
+
* Mint one integration token for an account.
|
|
359
|
+
*
|
|
360
|
+
* The caller may mint for itself; naming somebody else is an administrative
|
|
361
|
+
* act and carries the same check as every other cross-account operation.
|
|
362
|
+
*
|
|
363
|
+
* @param token - the caller's browser token.
|
|
364
|
+
* @param input - label, scopes, lifetime and the optional target account.
|
|
365
|
+
* @returns the token with the scopes and expiry that were actually granted.
|
|
366
|
+
*/
|
|
367
|
+
mintServiceToken(token: string, input?: QaServiceTokenIssueInput): QaIssuedServiceToken;
|
|
368
|
+
/**
|
|
369
|
+
* Mint one integration token for an account the caller has established.
|
|
370
|
+
*
|
|
371
|
+
* The operator CLI takes this path: it runs in its own process, against the
|
|
372
|
+
* accounts database, with the deployment's file access as its only
|
|
373
|
+
* authority — there is no browser token to present. The browser path above
|
|
374
|
+
* keeps its own check, so nothing a page can send reaches this entry point.
|
|
375
|
+
*
|
|
376
|
+
* @param target - the account the credential belongs to.
|
|
377
|
+
* @param input - label, scopes and lifetime.
|
|
378
|
+
* @returns the token, with the plaintext shown exactly once.
|
|
379
|
+
*/
|
|
380
|
+
mintServiceTokenFor(target: QaAccountUserPublic, input?: QaServiceTokenIssueInput): QaIssuedServiceToken;
|
|
381
|
+
/**
|
|
382
|
+
* Verify a presented integration token.
|
|
383
|
+
*
|
|
384
|
+
* Every refusal is the same null: unknown id, bad secret, expired, revoked,
|
|
385
|
+
* disabled account. An unauthenticated caller must not learn which of those
|
|
386
|
+
* it hit, and a caller holding a stale token does not need to.
|
|
387
|
+
*
|
|
388
|
+
* @param presented - the raw credential from the request.
|
|
389
|
+
* @returns the token's identity, or null.
|
|
390
|
+
*/
|
|
391
|
+
verifyServiceToken(presented: string): QaVerifiedServiceToken | null;
|
|
392
|
+
/**
|
|
393
|
+
* The caller's own integration tokens, newest last.
|
|
394
|
+
* @param token - the caller's browser token.
|
|
395
|
+
* @returns summaries; the secrets are not recoverable and are not listed.
|
|
396
|
+
*/
|
|
397
|
+
listServiceTokens(token: string): readonly QaServiceTokenSummary[];
|
|
398
|
+
/** The integration tokens of an account the caller has established. */
|
|
399
|
+
listServiceTokensFor(user: QaAccountUserPublic): readonly QaServiceTokenSummary[];
|
|
400
|
+
/**
|
|
401
|
+
* Revoke one of the caller's own integration tokens.
|
|
402
|
+
* @param token - the caller's browser token.
|
|
403
|
+
* @param tokenId - the token to revoke.
|
|
404
|
+
* @returns true when a live token was revoked.
|
|
405
|
+
*/
|
|
406
|
+
revokeServiceToken(token: string, tokenId: string): boolean;
|
|
407
|
+
/** Revoke one integration token of an account the caller has established. */
|
|
408
|
+
revokeServiceTokenFor(user: QaAccountUserPublic, tokenId: string): boolean;
|
|
409
|
+
/** The caller if it administers the deployment, or a refusal. */
|
|
410
|
+
private requireAdmin;
|
|
289
411
|
/**
|
|
290
412
|
* The account that owns one session, or undefined while nobody claimed it.
|
|
291
413
|
* The prompt renderer resolves a chat's owner through this map, so a session
|
|
@@ -302,6 +424,14 @@ export declare class QaAccounts {
|
|
|
302
424
|
readonly profile: QaAccountProfile;
|
|
303
425
|
} | undefined;
|
|
304
426
|
/** One account in its public shape; undefined when the address is unknown. */
|
|
427
|
+
/**
|
|
428
|
+
* One account's public identity by id.
|
|
429
|
+
*
|
|
430
|
+
* Host paths that authenticated the account themselves — the integration
|
|
431
|
+
* API, which resolves its own service token — need the public shape the
|
|
432
|
+
* token-shaped entries return, without a browser token to hand.
|
|
433
|
+
*/
|
|
434
|
+
accountById(userId: string): QaAccountUserPublic | undefined;
|
|
305
435
|
findUser(email: string): QaAccountUserPublic | undefined;
|
|
306
436
|
/**
|
|
307
437
|
* Operator write: replace one account's profile, addressed by email. The
|
|
@@ -30,6 +30,18 @@ export interface QaProjectedTranscript {
|
|
|
30
30
|
/** Newest event time in the log, when the log carried one. */
|
|
31
31
|
readonly lastActivity?: number;
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Flatten one message's content blocks into text. Non-text blocks are named
|
|
35
|
+
* rather than dropped: a reviewer judging "the answer ignored my screenshot"
|
|
36
|
+
* needs to see that an image was attached.
|
|
37
|
+
*
|
|
38
|
+
* Exported because the integration API publishes an assistant message as the
|
|
39
|
+
* answer to an external application, and the flattening rule (skip reasoning
|
|
40
|
+
* and tool calls, keep text, name attachments) must be the same one the
|
|
41
|
+
* transcript shows. Two readers of one message that flatten it differently
|
|
42
|
+
* would be two answers to the same question.
|
|
43
|
+
*/
|
|
44
|
+
export declare function messageContentText(content: unknown): string;
|
|
33
45
|
/**
|
|
34
46
|
* Project one log into the review transcript. Unknown event types are skipped,
|
|
35
47
|
* not refused: a deployment may carry plugin events this package has never
|
|
@@ -2,7 +2,8 @@ import type { PluginLogger } from "@yadsh/dsh-plugin-log";
|
|
|
2
2
|
import type { QaAccounts } from "../accounts/store.js";
|
|
3
3
|
import type { QaAccessService } from "../access/service.js";
|
|
4
4
|
import type { QaRoleRepository } from "../access/role-repository.js";
|
|
5
|
-
import type { QaAdminAuditEvent, QaAdminOverview, QaAdminPage, QaAdminUserDetail, QaAdminUserRow, QaAdminUserUpdate, QaAuditQuery, QaConversationDetail, QaConversationDeletion, QaConversationQuery, QaConversationReview, QaConversationReviewInput, QaConversationSummary, QaFeedbackQuery, QaFeedbackRow, QaMessageFeedback, QaMessageFeedbackInput, QaQualityMetrics, QaReviewQueueItem, QaReviewQueueRow, QaUserQuery } from "../types.js";
|
|
5
|
+
import type { QaAdminAuditEvent, QaAdminOverview, QaAdminPage, QaAdminSkillScope, QaAdminSkillsView, QaAdminUserDetail, QaAdminUserRow, QaAdminUserUpdate, QaAuditQuery, QaConversationDetail, QaConversationDeletion, QaConversationQuery, QaConversationReview, QaConversationReviewInput, QaConversationSummary, QaFeedbackQuery, QaFeedbackRow, QaMessageFeedback, QaMessageFeedbackInput, QaPasswordResetRequest, QaQualityMetrics, QaReviewQueueItem, QaReviewQueueRow, QaSkillDocument, QaSkillDraftInput, QaSkillRemoval, QaSkillToolDescriptor, QaSkillValidation, QaUserQuery } from "../types.js";
|
|
6
|
+
import type { QaPersonalSkills } from "../personal-skills/index.js";
|
|
6
7
|
import type { QaQualityStore } from "./quality-store.js";
|
|
7
8
|
import type { QaAdminRedactor } from "./redaction.js";
|
|
8
9
|
import type { QaSessionLogReader, QaStoredSessionEraser } from "./session-log.js";
|
|
@@ -37,6 +38,11 @@ export interface QaAdminServiceOptions {
|
|
|
37
38
|
* pretending it did.
|
|
38
39
|
*/
|
|
39
40
|
readonly sessionFiles?: QaStoredSessionEraser;
|
|
41
|
+
/**
|
|
42
|
+
* The skill store both scopes read and write, resolved lazily so the console
|
|
43
|
+
* never constructs storage before someone opens its skills page.
|
|
44
|
+
*/
|
|
45
|
+
readonly skills: () => QaPersonalSkills;
|
|
40
46
|
/** Drops the sources a conversation collected, when the deployment keeps any. */
|
|
41
47
|
readonly dropSources?: (sessionId: string) => void;
|
|
42
48
|
readonly logger: PluginLogger;
|
|
@@ -79,6 +85,21 @@ export declare class QaAdminService {
|
|
|
79
85
|
users(token: string, query: QaUserQuery, cursor: string | undefined, limit: number | undefined): Promise<QaAdminPage<QaAdminUserRow>>;
|
|
80
86
|
user(token: string, userId: string): Promise<QaAdminUserDetail>;
|
|
81
87
|
updateUser(token: string, userId: string, update: QaAdminUserUpdate): Promise<QaAdminUserDetail>;
|
|
88
|
+
/**
|
|
89
|
+
* Accounts that asked for a password reset from the sign-in screen. Reading
|
|
90
|
+
* the list takes `users.read` — a reviewer has no business seeing who is
|
|
91
|
+
* locked out; answering one takes `users.manage`, since a reset is a write
|
|
92
|
+
* to a credential.
|
|
93
|
+
*/
|
|
94
|
+
passwordResetRequests(token: string): Promise<readonly QaPasswordResetRequest[]>;
|
|
95
|
+
/**
|
|
96
|
+
* Answer one forgotten-password request: set a new password for the account
|
|
97
|
+
* and clear its queue row. Every live session of that account dies with the
|
|
98
|
+
* token-version bump, so the user signs in with the password the operator
|
|
99
|
+
* handed over. Audited, because an operator setting somebody else's
|
|
100
|
+
* credential must leave a trace.
|
|
101
|
+
*/
|
|
102
|
+
resetUserPassword(token: string, userId: string, password: string): Promise<QaAdminUserDetail>;
|
|
82
103
|
/**
|
|
83
104
|
* Refuse a write that would leave the deployment without a usable admin.
|
|
84
105
|
* Granting the admin role carries no such risk; demoting or disabling the
|
|
@@ -124,6 +145,40 @@ export declare class QaAdminService {
|
|
|
124
145
|
queueConversation(token: string, conversationId: string, messageId: string | undefined): QaReviewQueueItem;
|
|
125
146
|
/** Save a reviewer's verdict, and take the item out of the queue when done. */
|
|
126
147
|
saveReview(token: string, input: QaConversationReviewInput): QaConversationReview;
|
|
148
|
+
/**
|
|
149
|
+
* Resolve one requested scope into what the storage service understands.
|
|
150
|
+
* The account id arrives from the browser, so an unknown one is refused
|
|
151
|
+
* here: deriving a directory for it would create storage for nobody.
|
|
152
|
+
*/
|
|
153
|
+
private skillTarget;
|
|
154
|
+
/**
|
|
155
|
+
* One audit image of a skill. The body is deliberately not part of it: the
|
|
156
|
+
* trail records who changed which skill and to what revision, and a stored
|
|
157
|
+
* transcript of a person's instructions belongs in the skill, not doubled in
|
|
158
|
+
* every audit row.
|
|
159
|
+
*/
|
|
160
|
+
private skillImage;
|
|
161
|
+
/** The catalog of one scope with the facts the console names it by. */
|
|
162
|
+
skills(token: string, scope: QaAdminSkillScope): Promise<QaAdminSkillsView>;
|
|
163
|
+
/** One skill file with its body and the revision a save must echo. */
|
|
164
|
+
skill(token: string, scope: QaAdminSkillScope, name: string): Promise<QaSkillDocument>;
|
|
165
|
+
/**
|
|
166
|
+
* Check an unsaved draft against the target scope: the file a save would
|
|
167
|
+
* write, and every diagnostic for it.
|
|
168
|
+
*/
|
|
169
|
+
validateSkill(token: string, scope: QaAdminSkillScope, name: string | null, input: QaSkillDraftInput): Promise<QaSkillValidation>;
|
|
170
|
+
/** The tool catalog the picker offers for one scope. */
|
|
171
|
+
skillTools(token: string, scope: QaAdminSkillScope): Promise<readonly QaSkillToolDescriptor[]>;
|
|
172
|
+
/**
|
|
173
|
+
* Create (`name` null) or replace one skill. The write is marked as this
|
|
174
|
+
* administrator's, so the owner reads it as an administrator's edit rather
|
|
175
|
+
* than as a silent substitution, and it is audited with both images.
|
|
176
|
+
*/
|
|
177
|
+
saveSkill(token: string, scope: QaAdminSkillScope, name: string | null, input: QaSkillDraftInput): Promise<QaSkillDocument>;
|
|
178
|
+
/** Remove one skill into the trash beside its own skills root. */
|
|
179
|
+
removeSkill(token: string, scope: QaAdminSkillScope, name: string, expectedRevision: string | null): Promise<QaSkillRemoval>;
|
|
180
|
+
/** One skill as it exists now; a missing one is the service's refusal. */
|
|
181
|
+
private existingSkill;
|
|
127
182
|
/**
|
|
128
183
|
* The audit trail as one table. Two writers keep records — the capability
|
|
129
184
|
* service, whose repository predates the console, and the quality store —
|
|
@@ -60,6 +60,27 @@ export interface QaSessionLogReader {
|
|
|
60
60
|
* next flush would put its log back.
|
|
61
61
|
*/
|
|
62
62
|
live(sessionId: string): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* The events of a session this process is holding, without touching storage.
|
|
65
|
+
*
|
|
66
|
+
* This is a *probe*, not a read: it never fails and never waits, and
|
|
67
|
+
* `undefined` means only that this process does not hold the session — not
|
|
68
|
+
* that the session has no log. What it is good for is the one question a
|
|
69
|
+
* warm reader has to ask on every page: has anything been appended since the
|
|
70
|
+
* seq I already projected? A session the Harness holds is appended in this
|
|
71
|
+
* process, so its in-memory events answer that without a stored read, and
|
|
72
|
+
* the answer is strictly *newer* than what storage has flushed.
|
|
73
|
+
*
|
|
74
|
+
* `after` is that cursor: events at or below it are not materialized, so a
|
|
75
|
+
* reader that already knows a prefix pays for the pass over the session's
|
|
76
|
+
* event references and not for the events it has already seen. The snapshot
|
|
77
|
+
* can still be partial (a session that existed in an earlier run of the
|
|
78
|
+
* process, or a long one), which is why a caller may use it to extend what it
|
|
79
|
+
* already knows and never to replace it.
|
|
80
|
+
*/
|
|
81
|
+
snapshot(sessionId: string, options?: {
|
|
82
|
+
readonly after?: number | undefined;
|
|
83
|
+
}): readonly StoredSessionEvent[] | undefined;
|
|
63
84
|
}
|
|
64
85
|
/** What one storage-removal call did, id by id. */
|
|
65
86
|
export interface QaSessionEraseOutcome {
|
|
@@ -9,6 +9,21 @@
|
|
|
9
9
|
* lowercase and without the leading dot; an entry is a suffix, not a glob.
|
|
10
10
|
*/
|
|
11
11
|
export declare const DEFAULT_QA_TEXT_EXTENSIONS: readonly string[];
|
|
12
|
+
/**
|
|
13
|
+
* Document extensions the stand's document pipeline can read, accepted on top
|
|
14
|
+
* of the text files. A visitor who has the spec in Word should be able to hand
|
|
15
|
+
* it over instead of retyping it, and a deployment that renders and converts
|
|
16
|
+
* those formats is the one that can answer for them: this list is what stops
|
|
17
|
+
* `attachments.extensions` from being text-only by construction.
|
|
18
|
+
*/
|
|
19
|
+
export declare const DEFAULT_QA_DOCUMENT_EXTENSIONS: readonly string[];
|
|
20
|
+
/**
|
|
21
|
+
* What a visitor may attach to one message out of the box: the text files plus
|
|
22
|
+
* the documents the pipeline reads. An operator names whatever their stand can
|
|
23
|
+
* actually work with — the list is a ceiling, not a guarantee that the model
|
|
24
|
+
* has a tool for every entry.
|
|
25
|
+
*/
|
|
26
|
+
export declare const DEFAULT_QA_ATTACHMENT_EXTENSIONS: readonly string[];
|
|
12
27
|
/** Bounds the resolver and the settings card agree on. */
|
|
13
28
|
export declare const QA_PASTED_TEXT_LINES_MIN = 0;
|
|
14
29
|
export declare const QA_PASTED_TEXT_LINES_MAX = 10000;
|
|
@@ -24,16 +39,24 @@ export declare const QA_MAX_PENDING_MAX = 40;
|
|
|
24
39
|
* @param values - raw extension tokens from configuration.
|
|
25
40
|
* @returns the normalized token list.
|
|
26
41
|
*/
|
|
27
|
-
export declare function
|
|
42
|
+
export declare function normalizeAcceptedExtensions(values: readonly string[]): readonly string[];
|
|
43
|
+
/**
|
|
44
|
+
* The extension one file name carries, lowercase and without the dot. A name
|
|
45
|
+
* without a dot, or with a hidden-file dot only (`.gitignore`), reports the
|
|
46
|
+
* whole leaf, which no configured extension can match.
|
|
47
|
+
* @param name - display file name.
|
|
48
|
+
* @returns the extension token, or the leaf when there is none.
|
|
49
|
+
*/
|
|
50
|
+
export declare function fileExtensionOf(name: string): string;
|
|
28
51
|
/**
|
|
29
|
-
* Whether one file name carries an accepted
|
|
52
|
+
* Whether one file name carries an accepted extension. The comparison is
|
|
30
53
|
* case-insensitive; a name without a dot, or with a hidden-file dot only
|
|
31
54
|
* (`.gitignore`), is matched against the whole leaf.
|
|
32
55
|
* @param name - display file name.
|
|
33
56
|
* @param extensions - normalized accepted extensions.
|
|
34
57
|
* @returns whether the name matches one accepted extension.
|
|
35
58
|
*/
|
|
36
|
-
export declare function
|
|
59
|
+
export declare function hasAcceptedExtension(name: string, extensions: readonly string[]): boolean;
|
|
37
60
|
/**
|
|
38
61
|
* Count the lines of a pasted blob the way a reader counts them: a single
|
|
39
62
|
* trailing newline does not open a new line, and CRLF counts once.
|