@yadsh/dsh-qa-surface 0.7.4 → 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 +169 -12
- 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/model.js +24 -4
- package/lib/access/model.js.map +1 -1
- package/lib/access/role-repository.js +199 -91
- package/lib/access/role-repository.js.map +1 -1
- package/lib/access/service.js +223 -7
- package/lib/access/service.js.map +1 -1
- package/lib/account-remotes.js +2 -4
- package/lib/account-remotes.js.map +1 -1
- package/lib/accounts/database.js +447 -0
- package/lib/accounts/database.js.map +1 -0
- package/lib/accounts/errors.js.map +1 -1
- package/lib/accounts/file.js +27 -90
- package/lib/accounts/file.js.map +1 -1
- package/lib/accounts/store.js +224 -59
- 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 +270 -54
- package/lib/admin/quality-store.js.map +1 -1
- package/lib/admin/service.js +145 -7
- 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/cli.js +2 -1
- package/lib/cli.js.map +1 -1
- package/lib/client/QaSessionController.js +379 -39
- package/lib/client/QaSessionController.js.map +1 -1
- package/lib/client/QaSurface.js +94 -34
- package/lib/client/QaSurface.js.map +1 -1
- package/lib/client/QaTranscriptAdapter.js +40 -0
- package/lib/client/QaTranscriptAdapter.js.map +1 -1
- package/lib/client/admin/QaAdmin.js +26 -8
- package/lib/client/admin/QaAdmin.js.map +1 -1
- package/lib/client/admin/{format.js → copy.js} +1 -1
- package/lib/client/admin/copy.js.map +1 -0
- package/lib/client/admin/pages/Conversations.js +34 -8
- package/lib/client/admin/pages/Conversations.js.map +1 -1
- package/lib/client/admin/pages/Overview.js +1 -1
- package/lib/client/admin/pages/Overview.js.map +1 -1
- package/lib/client/admin/pages/Quality.js +1 -1
- package/lib/client/admin/pages/Quality.js.map +1 -1
- package/lib/client/admin/pages/Review.js +1 -1
- package/lib/client/admin/pages/Review.js.map +1 -1
- package/lib/client/admin/pages/Users.js +6 -2
- package/lib/client/admin/pages/Users.js.map +1 -1
- package/lib/client/admin/shared.js +10 -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/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/Markdown.js +14 -206
- package/lib/client/components/Markdown.js.map +1 -1
- package/lib/client/components/QaChangelog.js +91 -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/QaHeader.js +1 -3
- package/lib/client/components/QaHeader.js.map +1 -1
- package/lib/client/components/QaMessage.js +25 -4
- 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 +17 -3
- package/lib/client/components/QaSidebar.js.map +1 -1
- package/lib/client/components/QaSourcesPanel.js +3 -18
- package/lib/client/components/QaSourcesPanel.js.map +1 -1
- package/lib/client/components/QaWorkGroup.js +4 -8
- package/lib/client/components/QaWorkGroup.js.map +1 -1
- package/lib/client/components/source-icon.js +26 -0
- package/lib/client/components/source-icon.js.map +1 -0
- package/lib/client/components/thinking-phrases.js +2 -11
- package/lib/client/components/thinking-phrases.js.map +1 -1
- package/lib/client/components/use-now.js +21 -0
- package/lib/client/components/use-now.js.map +1 -0
- package/lib/client/index.js +33 -5
- package/lib/client/index.js.map +1 -1
- package/lib/client/lineage.js +18 -0
- package/lib/client/lineage.js.map +1 -0
- package/lib/client/markdown/CodeBlock.js +28 -0
- package/lib/client/markdown/CodeBlock.js.map +1 -0
- package/lib/client/markdown/blocks.js +342 -0
- package/lib/client/markdown/blocks.js.map +1 -0
- package/lib/client/markdown/highlight.js +1089 -0
- package/lib/client/markdown/highlight.js.map +1 -0
- package/lib/client/markdown/inline.js +377 -0
- package/lib/client/markdown/inline.js.map +1 -0
- package/lib/client/markdown/render.js +147 -0
- package/lib/client/markdown/render.js.map +1 -0
- package/lib/client/markdown/source-chip.js +55 -0
- package/lib/client/markdown/source-chip.js.map +1 -0
- package/lib/client/markdown/use-transient-flag.js +27 -0
- package/lib/client/markdown/use-transient-flag.js.map +1 -0
- 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/role/RoleSelector.js +11 -2
- package/lib/client/role/RoleSelector.js.map +1 -1
- package/lib/client/role/preview.js +123 -0
- package/lib/client/role/preview.js.map +1 -0
- 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 +124 -20
- 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 +7615 -1567
- package/lib/client.js.map +1 -1
- package/lib/config-resolvers/accounts.js +15 -0
- package/lib/config-resolvers/accounts.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 +30 -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-resolvers/sources.js +16 -0
- package/lib/config-resolvers/sources.js.map +1 -1
- package/lib/config.js +118 -2
- package/lib/config.js.map +1 -1
- package/lib/index.js +183 -11
- package/lib/index.js.map +1 -1
- package/lib/lockdown-policy.js +19 -0
- package/lib/lockdown-policy.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 +25 -4
- package/lib/provenance/host-store.js.map +1 -1
- package/lib/provenance/retention.js +25 -0
- package/lib/provenance/retention.js.map +1 -0
- package/lib/provenance/snapshot-store.js +290 -35
- package/lib/provenance/snapshot-store.js.map +1 -1
- package/lib/questions.js +98 -38
- 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 +139 -21
- 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 +348 -56
- package/lib/typert.remote-client.d.ts +7 -1
- package/lib/typert.remote-client.js +274 -51
- package/lib/types/access/capability-catalog.d.ts +28 -1
- package/lib/types/access/role-repository.d.ts +38 -7
- package/lib/types/access/service.d.ts +106 -3
- package/lib/types/account-remotes.d.ts +8 -4
- package/lib/types/accounts/database.d.ts +84 -0
- package/lib/types/accounts/errors.d.ts +7 -1
- package/lib/types/accounts/file.d.ts +17 -39
- package/lib/types/accounts/store.d.ts +110 -19
- package/lib/types/admin/quality-store.d.ts +38 -2
- 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/{format.d.ts → copy.d.ts} +1 -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/Markdown.d.ts +7 -8
- 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 +11 -1
- package/lib/types/client/components/QaSourcesPanel.d.ts +1 -3
- package/lib/types/client/components/source-icon.d.ts +12 -0
- package/lib/types/client/components/use-now.d.ts +8 -0
- package/lib/types/client/lineage.d.ts +15 -0
- package/lib/types/client/markdown/CodeBlock.d.ts +14 -0
- package/lib/types/client/markdown/blocks.d.ts +74 -0
- package/lib/types/client/markdown/highlight.d.ts +29 -0
- package/lib/types/client/markdown/inline.d.ts +40 -0
- package/lib/types/client/markdown/render.d.ts +13 -0
- package/lib/types/client/markdown/source-chip.d.ts +32 -0
- package/lib/types/client/markdown/use-transient-flag.d.ts +12 -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/role/RoleSelector.d.ts +11 -1
- package/lib/types/client/role/preview.d.ts +109 -0
- 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 +83 -2
- package/lib/types/lockdown-policy.d.ts +16 -0
- package/lib/types/personal-skills/service.d.ts +5 -0
- package/lib/types/provenance/host-store.d.ts +20 -3
- package/lib/types/provenance/retention.d.ts +46 -0
- package/lib/types/provenance/snapshot-store.d.ts +63 -7
- package/lib/types/questions.d.ts +13 -0
- package/lib/types/secure-session.d.ts +27 -1
- 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 +254 -5
- package/package.json +9 -5
- package/scripts/attach-workspace-sessions.mjs +472 -0
- package/lib/client/admin/format.js.map +0 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Agent } from "@deepseek-ai/dsh-agent";
|
|
2
2
|
import type { Context } from "@deepseek-ai/cordis";
|
|
3
|
+
import type { ScopeKey } from "@deepseek-ai/dsh-scope";
|
|
3
4
|
import type { SkillSummary } from "@deepseek-ai/dsh-skill";
|
|
4
5
|
import type { QaCapabilityDescriptor, QaSkillDescriptor } from "../types.js";
|
|
5
6
|
export interface CapabilityCatalogSnapshot {
|
|
@@ -19,10 +20,36 @@ export declare class QaCapabilityCatalog {
|
|
|
19
20
|
private readonly additionalToolNames;
|
|
20
21
|
/** Role ids used to report unknown subrole ids in skill metadata. */
|
|
21
22
|
private readonly knownSubroles;
|
|
23
|
+
/**
|
|
24
|
+
* The viewing scope an administrator's catalog is read with, for callers
|
|
25
|
+
* that have no agent of their own. Skills and tools an agent preset mounts
|
|
26
|
+
* (a kit's skill catalog, the preset's tool family) live in that preset's
|
|
27
|
+
* scope, so a global-only read reported an almost empty catalog: the
|
|
28
|
+
* administrator could neither see nor grant what every chat mounts.
|
|
29
|
+
* Resolving the scope may compose the preset; a failure degrades to the
|
|
30
|
+
* global view instead of failing the page.
|
|
31
|
+
*/
|
|
32
|
+
private readonly presetScope;
|
|
22
33
|
constructor(ctx: Context, additionalToolNames?: () => readonly string[],
|
|
23
34
|
/** Role ids used to report unknown subrole ids in skill metadata. */
|
|
24
|
-
knownSubroles?: () => readonly string[]
|
|
35
|
+
knownSubroles?: () => readonly string[],
|
|
36
|
+
/**
|
|
37
|
+
* The viewing scope an administrator's catalog is read with, for callers
|
|
38
|
+
* that have no agent of their own. Skills and tools an agent preset mounts
|
|
39
|
+
* (a kit's skill catalog, the preset's tool family) live in that preset's
|
|
40
|
+
* scope, so a global-only read reported an almost empty catalog: the
|
|
41
|
+
* administrator could neither see nor grant what every chat mounts.
|
|
42
|
+
* Resolving the scope may compose the preset; a failure degrades to the
|
|
43
|
+
* global view instead of failing the page.
|
|
44
|
+
*/
|
|
45
|
+
presetScope?: () => Promise<ScopeKey | undefined>);
|
|
25
46
|
snapshot(agent?: Agent): Promise<CapabilityCatalogSnapshot>;
|
|
47
|
+
/**
|
|
48
|
+
* The scope an administrator's catalog is read with. A deployment that pins
|
|
49
|
+
* QA chats to an agent preset gets that preset's standing scope; without a
|
|
50
|
+
* pinned preset there is no scope to borrow, and the read stays global.
|
|
51
|
+
*/
|
|
52
|
+
private resolvePresetScope;
|
|
26
53
|
/**
|
|
27
54
|
* Read `metadata.qa-surface` of every discovered skill.
|
|
28
55
|
*
|
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
import type { QaAccessAuditEvent, QaCapabilityConfig, QaCapabilitySelection, QaSkillAssignmentOverride, QaSubrole } from "../types.js";
|
|
2
2
|
export declare function defaultCapabilityFilePath(): string;
|
|
3
|
-
/**
|
|
3
|
+
/** The pre-SQLite policy file, imported once on first use. */
|
|
4
|
+
export declare function defaultLegacyCapabilityFilePath(): string;
|
|
5
|
+
/**
|
|
6
|
+
* Atomic, externally reloadable source of truth for roles and their audit.
|
|
7
|
+
*
|
|
8
|
+
* The policy is one row and a change appends one row beside it, so an edit
|
|
9
|
+
* writes what it changed instead of rewriting a growing document — the cost of
|
|
10
|
+
* an administrative change no longer depends on how long the deployment has
|
|
11
|
+
* been administering anything.
|
|
12
|
+
*
|
|
13
|
+
* Only the configuration is cached. An audit trail carries the full policy
|
|
14
|
+
* that preceded each change, so reading it is reserved for the callers that
|
|
15
|
+
* ask for it, and dropped whenever another process writes.
|
|
16
|
+
*/
|
|
4
17
|
export declare class QaRoleRepository {
|
|
5
18
|
readonly filePath: string;
|
|
6
|
-
private
|
|
7
|
-
private
|
|
19
|
+
private readonly storage;
|
|
20
|
+
private config;
|
|
21
|
+
private auditCache;
|
|
22
|
+
private observedDataVersion;
|
|
8
23
|
constructor(filePath?: string);
|
|
24
|
+
close(): void;
|
|
9
25
|
snapshot(): QaCapabilityConfig;
|
|
10
26
|
audit(): readonly QaAccessAuditEvent[];
|
|
11
27
|
create(actorId: string, input: QaSubrole): QaSubrole;
|
|
@@ -16,7 +32,7 @@ export declare class QaRoleRepository {
|
|
|
16
32
|
* Replace one skill's administrator overlay.
|
|
17
33
|
*
|
|
18
34
|
* An overlay with nothing left to declare is removed outright, so clearing a
|
|
19
|
-
* row in the administration surface leaves the
|
|
35
|
+
* row in the administration surface leaves the store as if it was never set.
|
|
20
36
|
* @param actorId - the administrator performing the change.
|
|
21
37
|
* @param input - the overlay as edited in the browser.
|
|
22
38
|
* @returns every overlay in force after the change.
|
|
@@ -24,9 +40,24 @@ export declare class QaRoleRepository {
|
|
|
24
40
|
updateSkillOverride(actorId: string, input: QaSkillAssignmentOverride): readonly QaSkillAssignmentOverride[];
|
|
25
41
|
recordAssignment(actorId: string, targetId: string, before: unknown, after: unknown): void;
|
|
26
42
|
private replace;
|
|
43
|
+
/** Append one change and trim the trail to its newest events. */
|
|
27
44
|
private appendAudit;
|
|
28
|
-
private
|
|
29
|
-
private
|
|
30
|
-
private
|
|
45
|
+
private writeConfig;
|
|
46
|
+
private readConfig;
|
|
47
|
+
private readAudit;
|
|
48
|
+
private readDataVersion;
|
|
49
|
+
/**
|
|
50
|
+
* Re-read the policy when another process wrote it. The `qa-admin` surfaces
|
|
51
|
+
* and this store share one database, so a configuration an administrator
|
|
52
|
+
* just saved must be the one the next policy resolution sees.
|
|
53
|
+
*/
|
|
54
|
+
private refresh;
|
|
55
|
+
/**
|
|
56
|
+
* Import a pre-SQLite `qa-capability-policies.json` exactly once, then rename
|
|
57
|
+
* it aside. A configuration the database already holds wins: a leftover file
|
|
58
|
+
* must not silently revert an administrator's policy.
|
|
59
|
+
*/
|
|
60
|
+
importLegacyFile(legacyFilePath: string): void;
|
|
61
|
+
private assertImportArrived;
|
|
31
62
|
}
|
|
32
63
|
//# sourceMappingURL=role-repository.d.ts.map
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { Agent } from "@deepseek-ai/dsh-agent";
|
|
2
2
|
import type { Context } from "@deepseek-ai/cordis";
|
|
3
|
+
import type { ScopeKey } from "@deepseek-ai/dsh-scope";
|
|
3
4
|
import type { PluginLogger } from "@yadsh/dsh-plugin-log";
|
|
4
5
|
import type { QaAccounts } from "../accounts/store.js";
|
|
5
6
|
import type { QaAgentToolGrants } from "../enforcement/tool-grants.js";
|
|
6
|
-
import type { QaAccessAdminSnapshot, QaCapabilityConfig, QaCapabilityDescriptor, QaCapabilitySelection, QaCurrentAccess, QaEffectiveCapabilityPolicy, QaSessionAccess, QaSkillActivationRecord, QaSkillAssignmentOverride, QaSubrole, QaUserAccess, ResolvedQaSurfaceConfig } from "../types.js";
|
|
7
|
+
import type { QaAccessAdminSnapshot, QaCapabilityConfig, QaCapabilityDescriptor, QaCapabilitySelection, QaClaimResult, QaCurrentAccess, QaEffectiveCapabilityPolicy, QaSessionAccess, QaSkillActivationRecord, QaSkillAssignmentOverride, QaSubrole, QaUserAccess, ResolvedQaSurfaceConfig } from "../types.js";
|
|
8
|
+
import type { QaSessionLogReader } from "../admin/session-log.js";
|
|
7
9
|
import { QaCapabilityCatalog, type CapabilityCatalogSnapshot } from "./capability-catalog.js";
|
|
8
10
|
import { QaRoleRepository } from "./role-repository.js";
|
|
9
11
|
export interface QaResolvedSessionPolicy {
|
|
@@ -21,16 +23,109 @@ export interface QaResolvedSessionPolicy {
|
|
|
21
23
|
}
|
|
22
24
|
/** Server-only orchestration of roles, assignments, discovery and snapshots. */
|
|
23
25
|
export declare class QaAccessService {
|
|
26
|
+
private readonly ctx;
|
|
24
27
|
private readonly options;
|
|
25
28
|
readonly roles: QaRoleRepository;
|
|
26
29
|
readonly catalog: QaCapabilityCatalog;
|
|
30
|
+
private lastOwnershipSweepAt;
|
|
31
|
+
/** The sweep in flight, so a burst of readers runs one listing, not ten. */
|
|
32
|
+
private ownershipSweep;
|
|
33
|
+
/**
|
|
34
|
+
* Sessions the Host has listed as delegated children of another session.
|
|
35
|
+
* The live registry answers for a child that is running right now; this
|
|
36
|
+
* cache answers for the ones that finished and were dismantled, which is the
|
|
37
|
+
* state a browser meets when it opens an old subagent transcript.
|
|
38
|
+
*/
|
|
39
|
+
private readonly delegatedSessions;
|
|
40
|
+
private lastDelegatedSweepAt;
|
|
41
|
+
private delegatedSweep;
|
|
27
42
|
constructor(ctx: Context, options: {
|
|
28
43
|
readonly accounts: () => QaAccounts | undefined;
|
|
29
44
|
readonly config: () => ResolvedQaSurfaceConfig;
|
|
30
45
|
readonly logger: PluginLogger;
|
|
31
46
|
readonly repository?: QaRoleRepository;
|
|
32
47
|
readonly dynamicToolNames?: () => readonly string[];
|
|
48
|
+
/**
|
|
49
|
+
* The viewing scope an administrator's catalog is read with. Skills and
|
|
50
|
+
* tools a deployment mounts through an agent preset live in that
|
|
51
|
+
* preset's scope, so a global-only catalog left the operator unable to
|
|
52
|
+
* see — or grant — what every QA chat actually mounts.
|
|
53
|
+
*/
|
|
54
|
+
readonly presetScope?: () => Promise<ScopeKey | undefined>;
|
|
55
|
+
/**
|
|
56
|
+
* Durable session listing, used to tell a chat from a delegated child
|
|
57
|
+
* across Host runs. Without it the lineage answer degrades to the live
|
|
58
|
+
* registry, which knows nothing about a session this process has not
|
|
59
|
+
* materialized — the sweep then reclaims nothing rather than guessing.
|
|
60
|
+
*/
|
|
61
|
+
readonly sessionLog?: QaSessionLogReader;
|
|
62
|
+
/**
|
|
63
|
+
* Called with the ids of chats the Harness no longer knows, once their
|
|
64
|
+
* ownership records are reclaimed. The record is the auth boundary, not
|
|
65
|
+
* the whole of what the deployment kept about a chat: ratings, reviews
|
|
66
|
+
* and queue entries are keyed by conversation, and they outlive their
|
|
67
|
+
* chat unless the caller drops them here.
|
|
68
|
+
*/
|
|
69
|
+
readonly onVanishedSessions?: (sessionIds: readonly string[]) => void;
|
|
33
70
|
});
|
|
71
|
+
/**
|
|
72
|
+
* Whether the Host can prove this session is a delegated child: a subagent's
|
|
73
|
+
* session, which is an implementation detail of one answer and never a chat.
|
|
74
|
+
* Used to refuse one wherever conversations are listed or claimed.
|
|
75
|
+
*/
|
|
76
|
+
isDelegatedChild(sessionId: string): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* The Host-registered facts of one session, for the accounts store's
|
|
79
|
+
* bounded auto-claim. Absent facts (the session registry is unavailable,
|
|
80
|
+
* or this chat is not materialized here) keep the historical claim.
|
|
81
|
+
*/
|
|
82
|
+
private sessionFacts;
|
|
83
|
+
/**
|
|
84
|
+
* Reclaim ownership records of chats the Harness no longer knows.
|
|
85
|
+
*
|
|
86
|
+
* Nothing else removes them: the browser's "delete chat" hides a chat in one
|
|
87
|
+
* browser, and a record outlives its session forever, so a year of QA traffic
|
|
88
|
+
* accumulates one row per chat ever created. The sweep is throttled, runs
|
|
89
|
+
* off the reservation path and off the review reads, and is skipped entirely
|
|
90
|
+
* when the Harness cannot say what exists — an unanswerable question is
|
|
91
|
+
* never a licence to delete an auth boundary.
|
|
92
|
+
*
|
|
93
|
+
* What exists is both halves of the Harness's own knowledge: the sessions
|
|
94
|
+
* this process has open and the ones its storage still holds. The live store
|
|
95
|
+
* alone would make a cold chat — one nobody has opened since the last
|
|
96
|
+
* restart — look deleted, and the sweep would reclaim records of chats that
|
|
97
|
+
* are merely quiet. A listing that cannot see stored sessions therefore
|
|
98
|
+
* reclaims nothing.
|
|
99
|
+
*
|
|
100
|
+
* @returns the sweep, for callers that want to await it (tests, and the
|
|
101
|
+
* review reads that must show the console what still exists).
|
|
102
|
+
*/
|
|
103
|
+
sweepVanishedOwnership(): Promise<void>;
|
|
104
|
+
private runOwnershipSweep;
|
|
105
|
+
/**
|
|
106
|
+
* Migrate a browser's local chat index into server-side ownership.
|
|
107
|
+
*
|
|
108
|
+
* A delegated session is not a chat: claiming one would write exactly the
|
|
109
|
+
* ownership record the admission refuses to create and put a subagent's
|
|
110
|
+
* transcript in the account's chat list. Its ids are left out of the batch
|
|
111
|
+
* and reported, so a stale browser index cannot reintroduce the record.
|
|
112
|
+
*/
|
|
113
|
+
claimSessions(token: string, sessionIds: readonly string[]): QaClaimResult;
|
|
114
|
+
/**
|
|
115
|
+
* Reclaim ownership records that point at delegated sessions, and remember
|
|
116
|
+
* which sessions are children while doing it.
|
|
117
|
+
*
|
|
118
|
+
* Records like this are residue of the paths that used to claim a session
|
|
119
|
+
* without knowing its lineage: each one keeps a subagent's transcript in the
|
|
120
|
+
* chat list of whoever opened it. Only positively identified children lose
|
|
121
|
+
* their record, so a listing that cannot be read reclaims nothing, and every
|
|
122
|
+
* other record stays the auth boundary it is.
|
|
123
|
+
*
|
|
124
|
+
* @returns the sweep, for callers that want to await it (tests).
|
|
125
|
+
*/
|
|
126
|
+
reclaimDelegatedSessions(): Promise<void>;
|
|
127
|
+
/** Throttled, off the critical path, and never awaited by a request. */
|
|
128
|
+
private maybeSweepDelegatedSessions;
|
|
34
129
|
current(token: string): QaCurrentAccess;
|
|
35
130
|
session(token: string, sessionId: string): QaSessionAccess;
|
|
36
131
|
reserveSession(token: string, sessionId: string, requestedSubrole: string | null, adminPreview: boolean): import("../types.js").QaAccountUserPublic;
|
|
@@ -47,9 +142,17 @@ export declare class QaAccessService {
|
|
|
47
142
|
deleteSubrole(token: string, id: string, replacementId: string | null): void;
|
|
48
143
|
updateCommon(token: string, input: QaCapabilitySelection): QaCapabilitySelection;
|
|
49
144
|
updateAssignment(token: string, userId: string, input: QaUserAccess): QaUserAccess;
|
|
145
|
+
/** The accounts store, or the refusal this operation names. */
|
|
50
146
|
private requireAccounts;
|
|
51
|
-
/**
|
|
52
|
-
|
|
147
|
+
/**
|
|
148
|
+
* Tools every session resolves regardless of the role it runs under: the
|
|
149
|
+
* deployment's pinned allow-list plus the activation diagnostic.
|
|
150
|
+
*
|
|
151
|
+
* Readable outside the service because the administration surface reports a
|
|
152
|
+
* profile's effective capabilities next to the configured ones, and the
|
|
153
|
+
* pinned set is part of that answer whatever the profile says.
|
|
154
|
+
*/
|
|
155
|
+
systemRequiredTools(): readonly string[];
|
|
53
156
|
private requireAdmin;
|
|
54
157
|
}
|
|
55
158
|
export declare function validateCapabilityConfig(config: QaCapabilityConfig): QaCapabilityConfig;
|
|
@@ -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,
|
|
3
|
+
import type { QaAccountProfileInput, QaAccountSession, QaAccountStartersInput, QaAccountUserPublic, QaOwnershipEntry, 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
|
|
@@ -23,9 +23,13 @@ export interface QaAccountRemotes {
|
|
|
23
23
|
login(email: string, password: string): QaAccountSession;
|
|
24
24
|
/** Identity probe; safe to call with an empty or expired token. */
|
|
25
25
|
whoami(token: string): QaWhoamiResult;
|
|
26
|
-
/**
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
/**
|
|
27
|
+
* The token user's owned session ids; the sidebar list authority.
|
|
28
|
+
*
|
|
29
|
+
* Migrating a browser's local chat index is NOT here: it has to refuse a
|
|
30
|
+
* delegated session, which takes session lineage, and that lives in
|
|
31
|
+
* `QaAccessService.claimSessions` — one entry point, no path around it.
|
|
32
|
+
*/
|
|
29
33
|
ownedSessions(token: string): {
|
|
30
34
|
readonly ids: readonly string[];
|
|
31
35
|
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { SqliteDatabase } from "@yadsh/dsh-plugin-kit/sqlite";
|
|
2
|
+
import type { QaSkillActivationRecord } from "../types.js";
|
|
3
|
+
import { type AccountsFile, type StoredOwnership, type StoredUser } from "./file.js";
|
|
4
|
+
/** One ownership record as it is written, before it becomes a row. */
|
|
5
|
+
export interface OwnershipWrite {
|
|
6
|
+
readonly sessionId: string;
|
|
7
|
+
readonly owner: StoredOwnership;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The deployment's accounts, as tables.
|
|
11
|
+
*
|
|
12
|
+
* The stores this replaces were one JSON document read, parsed and rewritten
|
|
13
|
+
* whole on every mutation — so a chat being opened rewrote every account and
|
|
14
|
+
* every chat the deployment had ever seen, and the cost of that write grew with
|
|
15
|
+
* the deployment's age. Here a mutation touches the row it changes: opening a
|
|
16
|
+
* chat inserts one ownership row, and the file's size stops being a per-turn
|
|
17
|
+
* cost.
|
|
18
|
+
*
|
|
19
|
+
* Cross-process freshness survives the change. The `qa-accounts` CLI writes the
|
|
20
|
+
* same database from a second process, so the in-memory model is invalidated by
|
|
21
|
+
* `PRAGMA data_version`, which SQLite bumps exactly when *another* connection
|
|
22
|
+
* commits — the same guarantee the previous mtime-and-size stamp provided, with
|
|
23
|
+
* no file read to notice it.
|
|
24
|
+
*/
|
|
25
|
+
export declare class QaAccountsDatabase {
|
|
26
|
+
readonly filePath: string;
|
|
27
|
+
readonly storage: SqliteDatabase;
|
|
28
|
+
private observedDataVersion;
|
|
29
|
+
constructor(filePath: string);
|
|
30
|
+
/** The token secret, creating one for a database that has none yet. */
|
|
31
|
+
get secret(): string;
|
|
32
|
+
/** True while the in-memory model a caller holds is still current. */
|
|
33
|
+
get unchangedSinceLoad(): boolean;
|
|
34
|
+
/** Take the current data version as the baseline a caller's model reflects. */
|
|
35
|
+
markLoaded(): void;
|
|
36
|
+
/** Every account, ownership record and skill activation, in one read. */
|
|
37
|
+
loadAll(): AccountsFile;
|
|
38
|
+
/** Append one account, preserving the order accounts are listed in. */
|
|
39
|
+
insertUser(user: StoredUser): void;
|
|
40
|
+
/** Replace one account's row in place. */
|
|
41
|
+
updateUser(user: StoredUser): void;
|
|
42
|
+
/** Record a successful sign-in without rewriting the account. */
|
|
43
|
+
touchLogin(userId: string, at: string): void;
|
|
44
|
+
/** Claim one chat for an account. */
|
|
45
|
+
insertOwnership(sessionId: string, owner: StoredOwnership): void;
|
|
46
|
+
/** Claim many chats at once, which is how a browser's index migrates. */
|
|
47
|
+
insertOwnershipMany(rows: readonly OwnershipWrite[]): void;
|
|
48
|
+
/** Rewrite an existing chat's pinned role, preview flag or snapshot. */
|
|
49
|
+
updateOwnership(sessionId: string, owner: StoredOwnership): void;
|
|
50
|
+
/** Forget chats: a rolled-back reservation, or a chat the Harness lost. */
|
|
51
|
+
deleteOwnership(sessionIds: readonly string[]): void;
|
|
52
|
+
/**
|
|
53
|
+
* Append one skill activation and keep the newest `limit` of them. The list
|
|
54
|
+
* is bounded because a long session may activate skills many times, and the
|
|
55
|
+
* oldest entries stop being interesting long before the store does.
|
|
56
|
+
*/
|
|
57
|
+
appendSkillActivation(sessionId: string, entry: QaSkillActivationRecord, limit: number): readonly QaSkillActivationRecord[];
|
|
58
|
+
/**
|
|
59
|
+
* Import a pre-SQLite `qa-accounts.json` exactly once, then rename it away.
|
|
60
|
+
*
|
|
61
|
+
* The import is verified *inside* the transaction and before the file is
|
|
62
|
+
* retired — every account and every ownership record must have arrived, and
|
|
63
|
+
* the token secret must be the one the file carried — because a silent
|
|
64
|
+
* partial import would either lock accounts out or hand a chat to the wrong
|
|
65
|
+
* owner. A database that already holds accounts is never overwritten by a
|
|
66
|
+
* leftover file.
|
|
67
|
+
*/
|
|
68
|
+
importLegacyFile(legacyFilePath: string | undefined): AccountsFile | undefined;
|
|
69
|
+
private assertImportArrived;
|
|
70
|
+
close(): void;
|
|
71
|
+
private hasAccounts;
|
|
72
|
+
private readDataVersion;
|
|
73
|
+
private ensureSecret;
|
|
74
|
+
/** The account columns, in the order both write statements bind them. */
|
|
75
|
+
private accountColumns;
|
|
76
|
+
private writeUser;
|
|
77
|
+
private writeOwnership;
|
|
78
|
+
/** One row per distinct policy; the digest is the policy's content. */
|
|
79
|
+
private writeSnapshot;
|
|
80
|
+
/** Drop snapshots no chat refers to any more, so the table cannot drift up. */
|
|
81
|
+
private gcSnapshots;
|
|
82
|
+
private parseLegacyFile;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=database.d.ts.map
|
|
@@ -3,7 +3,13 @@
|
|
|
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" | "account-disabled" | "email-taken" | "invalid-email" | "invalid-display-name" | "invalid-profile" | "profile-disabled" | "invalid-starters" | "starters-disabled" | "invalid-role" | "weak-password" | "registration-disabled" | "rate-limited" | "session-owned-elsewhere"
|
|
6
|
+
export type QaAccountsErrorReason = "auth-required" | "admin-required" | "forbidden" | "invalid-credentials" | "account-disabled" | "email-taken" | "invalid-email" | "invalid-display-name" | "invalid-profile" | "profile-disabled" | "invalid-starters" | "starters-disabled" | "invalid-role" | "weak-password" | "registration-disabled" | "rate-limited" | "session-owned-elsewhere"
|
|
7
|
+
/** The conversation a deletion names is not one this deployment knows. */
|
|
8
|
+
| "conversation-unknown"
|
|
9
|
+
/** The Harness still holds the conversation open, so its log would come back. */
|
|
10
|
+
| "conversation-live"
|
|
11
|
+
/** This deployment cannot remove stored conversations at all. */
|
|
12
|
+
| "conversation-not-removable";
|
|
7
13
|
export declare class QaAccountsError extends Error {
|
|
8
14
|
readonly reason: QaAccountsErrorReason;
|
|
9
15
|
constructor(reason: QaAccountsErrorReason, message: string);
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import type { Stats } from "node:fs";
|
|
2
1
|
import type { QaAccountRole, QaEffectiveCapabilityPolicy, QaSkillActivationRecord, QaUserAccess } from "../types.js";
|
|
3
|
-
/** One account's self-declared profile as it rests in the accounts
|
|
2
|
+
/** One account's self-declared profile as it rests in the accounts store. */
|
|
4
3
|
export interface StoredProfile {
|
|
5
4
|
fullName?: string;
|
|
6
5
|
identities?: Record<string, string>;
|
|
7
6
|
instructions?: string;
|
|
8
7
|
updatedAt?: string;
|
|
9
8
|
}
|
|
10
|
-
/** One account's starter buttons as they rest in the accounts
|
|
9
|
+
/** One account's starter buttons as they rest in the accounts store. */
|
|
11
10
|
export interface StoredStarters {
|
|
12
11
|
items?: {
|
|
13
12
|
label?: string;
|
|
@@ -53,51 +52,30 @@ export interface StoredOwnership {
|
|
|
53
52
|
/** Skill activations in order: what the model gained, and when it gained it. */
|
|
54
53
|
readonly skillActivations?: readonly QaSkillActivationRecord[];
|
|
55
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* The accounts model as the store holds it in memory, hydrated from the
|
|
57
|
+
* database and invalidated by `PRAGMA data_version` when another process
|
|
58
|
+
* writes. Snapshots appear inline here: the storage layer is what keeps one
|
|
59
|
+
* copy per distinct policy, not the callers.
|
|
60
|
+
*/
|
|
56
61
|
export interface AccountsFile {
|
|
57
62
|
readonly version: 1;
|
|
58
|
-
/** HMAC key for account tokens;
|
|
63
|
+
/** HMAC key for account tokens; created on first use. */
|
|
59
64
|
readonly secret: string;
|
|
60
65
|
readonly users: StoredUser[];
|
|
61
66
|
readonly ownership: Record<string, StoredOwnership>;
|
|
62
67
|
}
|
|
63
|
-
/**
|
|
64
|
-
export interface FileStamp {
|
|
65
|
-
readonly mtimeMs: number;
|
|
66
|
-
readonly size: number;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Where the last-seen stamp lives. Load and persist keep it current in place,
|
|
70
|
-
* so a caller holds one mutable ref instead of threading the stamp through
|
|
71
|
-
* every read-modify-write.
|
|
72
|
-
*/
|
|
73
|
-
export interface FileStampRef {
|
|
74
|
-
current: FileStamp;
|
|
75
|
-
}
|
|
76
|
-
/** A stamp no real file can match, so the first load always reads the disk. */
|
|
77
|
-
export declare function createFileStampRef(): FileStampRef;
|
|
78
|
-
export declare function stampOf(stat: Stats): FileStamp;
|
|
79
|
-
/** The accounts file lives next to the DSH home the launcher exports. */
|
|
68
|
+
/** The accounts database lives next to the DSH home the launcher exports. */
|
|
80
69
|
export declare function defaultAccountsFilePath(): string;
|
|
81
70
|
/**
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*/
|
|
85
|
-
export declare function loadAccountsFile(filePath: string, stamp: FileStampRef): AccountsFile;
|
|
86
|
-
/**
|
|
87
|
-
* Persist the accounts file atomically (temp file + rename) and leave the
|
|
88
|
-
* stamp ref pointing at the freshly written version.
|
|
71
|
+
* Where the pre-SQLite accounts file lived. It is imported on first use and
|
|
72
|
+
* then renamed aside, so a deployment upgrades without re-registering anyone.
|
|
89
73
|
*/
|
|
90
|
-
export declare function
|
|
74
|
+
export declare function defaultLegacyAccountsFilePath(): string;
|
|
91
75
|
/**
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
* and its next save would resurrect the CLI's change from a stale snapshot.
|
|
96
|
-
* One file, one writer at a time remains the deployment's discipline; LAN
|
|
97
|
-
* scale keeps the stat per operation trivial.
|
|
98
|
-
*
|
|
99
|
-
* Returns the freshly loaded file, or undefined when the caller should keep
|
|
100
|
-
* serving its in-memory state.
|
|
76
|
+
* A stable digest of one capability policy, so two chats admitted under the
|
|
77
|
+
* same policy share a stored snapshot. Keys are sorted because object key order
|
|
78
|
+
* is an artefact of construction, not part of the policy.
|
|
101
79
|
*/
|
|
102
|
-
export declare function
|
|
80
|
+
export declare function snapshotDigest(value: unknown): string;
|
|
103
81
|
//# sourceMappingURL=file.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { QaAccountIdentityField, QaAccountProfile, QaAccountProfileInput, QaAccountRole, QaAccountSession, QaAccountStartersInput, QaAccountUserPublic, QaAccessUser, QaAdminAccountRow, QaClaimResult, QaEffectiveCapabilityPolicy, QaOwnershipEntry, QaSkillActivationRecord, QaUserAccess, QaWhoamiResult } from "../types.js";
|
|
2
|
-
import type
|
|
2
|
+
import { type StoredOwnership, type StoredUser } from "./file.js";
|
|
3
3
|
export { QaAccountsError } from "./errors.js";
|
|
4
4
|
export type { QaAccountsErrorReason } from "./errors.js";
|
|
5
5
|
export { defaultAccountsFilePath } from "./file.js";
|
|
@@ -15,18 +15,45 @@ export interface QaAccountsOptions {
|
|
|
15
15
|
* omitting the list (the operator CLI) checks the key shape alone.
|
|
16
16
|
*/
|
|
17
17
|
readonly identityFields?: readonly QaAccountIdentityField[];
|
|
18
|
+
/**
|
|
19
|
+
* Where a pre-SQLite accounts file may still live. Defaults to the `.json`
|
|
20
|
+
* sibling of the database; pass an explicit path when a deployment kept it
|
|
21
|
+
* somewhere else.
|
|
22
|
+
*/
|
|
23
|
+
readonly legacyFilePath?: string;
|
|
18
24
|
}
|
|
19
25
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
26
|
+
* What the caller knows about the live session it is gating. The store is
|
|
27
|
+
* file-backed and session-agnostic; the Host supplies these facts so the
|
|
28
|
+
* first-come auto-claim can refuse what it must not attach silently. Absent
|
|
29
|
+
* facts (the operator CLI, a session the Host has not materialized) keep the
|
|
30
|
+
* historical unbounded claim.
|
|
31
|
+
*/
|
|
32
|
+
export interface QaSessionFacts {
|
|
33
|
+
/** Epoch ms the Host recorded as the session's creation. */
|
|
34
|
+
readonly createdAt?: number;
|
|
35
|
+
/** True when the session is a delegated child of another chat. */
|
|
36
|
+
readonly hasParent?: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* How long an unowned session stays claimable at access time. Mirrors the
|
|
40
|
+
* admission bootstrap window: a session older than this has either user
|
|
41
|
+
* history or a settled composition, so silently attaching it to whoever
|
|
42
|
+
* asked first would hand over a conversation nobody assigned to them.
|
|
43
|
+
* Owners are unaffected — their claim already exists in the file.
|
|
44
|
+
*/
|
|
45
|
+
export declare const QA_SESSION_CLAIM_WINDOW_MS = 120000;
|
|
46
|
+
/**
|
|
47
|
+
* Host authority for QA accounts: the account list, the session ownership map
|
|
48
|
+
* and the HMAC account tokens. Mutations are synchronous and land as the rows
|
|
49
|
+
* they change, in one transaction each.
|
|
23
50
|
*
|
|
24
|
-
* The class is the facade: token mechanics live in token.ts, the
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* credentials.ts.
|
|
51
|
+
* The class is the facade: token mechanics live in token.ts, the tables and the
|
|
52
|
+
* legacy import in database.ts, and the credential rules shared by registration
|
|
53
|
+
* and the operator CLI in credentials.ts.
|
|
28
54
|
*/
|
|
29
55
|
export declare class QaAccounts {
|
|
56
|
+
/** The database this store reads and writes. */
|
|
30
57
|
readonly filePath: string;
|
|
31
58
|
private file;
|
|
32
59
|
private readonly authAttempts;
|
|
@@ -35,18 +62,26 @@ export declare class QaAccounts {
|
|
|
35
62
|
private readonly allowRegistration;
|
|
36
63
|
/** Character cap on a stored profile's agent guidance. */
|
|
37
64
|
private readonly instructionsMaxLength;
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
/** mtime+size of the file as of the last load; the external-change probe. */
|
|
41
|
-
private readonly fileStamp;
|
|
42
|
-
constructor(filePath: string | undefined, options: QaAccountsOptions);
|
|
65
|
+
/** Whether a deployment declares which identity handles an account may hold. */
|
|
66
|
+
readonly identityFields: readonly QaAccountIdentityField[] | undefined;
|
|
43
67
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
68
|
+
* The store's tables. Every mutation writes the rows it changes and the
|
|
69
|
+
* in-memory model is refreshed only when another process wrote — which is
|
|
70
|
+
* what keeps the cost of opening a chat independent of the deployment's age.
|
|
71
|
+
*/
|
|
72
|
+
private readonly database;
|
|
73
|
+
constructor(configuredPath: string | undefined, options: QaAccountsOptions);
|
|
74
|
+
/** Close the underlying database; the store is unusable afterwards. */
|
|
75
|
+
close(): void;
|
|
76
|
+
/**
|
|
77
|
+
* Re-read the model when another process wrote the database, before every
|
|
78
|
+
* read or read-modify-write. The `qa-accounts` CLI runs in its own process
|
|
79
|
+
* against the same file, so without this a running Host would keep honoring
|
|
80
|
+
* tokens the CLI revoked. SQLite bumps `PRAGMA data_version` exactly when a
|
|
81
|
+
* *different* connection commits, which is the probe the previous
|
|
82
|
+
* mtime-and-size stamp of the JSON file used to provide.
|
|
47
83
|
*/
|
|
48
84
|
private reloadIfChanged;
|
|
49
|
-
private save;
|
|
50
85
|
private pruneAuthAttempts;
|
|
51
86
|
private assertAuthBudget;
|
|
52
87
|
private userByEmail;
|
|
@@ -64,8 +99,15 @@ export declare class QaAccounts {
|
|
|
64
99
|
* Ownership gate for one session: claim unowned sessions for the requesting
|
|
65
100
|
* user (first come, first served — the pre-accounts migration path), refuse
|
|
66
101
|
* foreign sessions unless the caller administers the deployment.
|
|
102
|
+
*
|
|
103
|
+
* The auto-claim is bounded when the caller can describe the live session
|
|
104
|
+
* ({@link QaSessionFacts}): a delegated subagent session is never claimed,
|
|
105
|
+
* and a session past the freshness window is refused instead of silently
|
|
106
|
+
* attached to whoever opened it first. Both refusals keep the file intact —
|
|
107
|
+
* the session stays unowned, so the operator CLI or a deliberate future
|
|
108
|
+
* claim can still resolve it.
|
|
67
109
|
*/
|
|
68
|
-
ensureSessionAccess(token: string, sessionId: string): QaAccountUserPublic;
|
|
110
|
+
ensureSessionAccess(token: string, sessionId: string, facts?: QaSessionFacts): QaAccountUserPublic;
|
|
69
111
|
/**
|
|
70
112
|
* Atomically reserve a Host-generated session id for the token user before
|
|
71
113
|
* the Session exists. Browser callers therefore never get a race window in
|
|
@@ -81,6 +123,48 @@ export declare class QaAccounts {
|
|
|
81
123
|
claimSessions(token: string, sessionIds: readonly string[]): QaClaimResult;
|
|
82
124
|
/** Session ids owned by the token's user, most recently claimed first. */
|
|
83
125
|
ownedSessionIds(token: string): readonly string[];
|
|
126
|
+
/**
|
|
127
|
+
* Drop ownership records of sessions that are not chats at all.
|
|
128
|
+
*
|
|
129
|
+
* A delegated subagent session is an implementation detail of one answer:
|
|
130
|
+
* nothing legitimately owns one, so no grace period applies and every named
|
|
131
|
+
* id loses its record. Only ids the caller positively identified are
|
|
132
|
+
* touched — a record is an auth boundary, and an id that merely looks odd
|
|
133
|
+
* keeps its owner. What this reclaims is residue: records written before the
|
|
134
|
+
* claim and attestation paths learned to refuse a delegated child, each one
|
|
135
|
+
* keeping a subagent's session in some account's chat list.
|
|
136
|
+
*
|
|
137
|
+
* @param sessionIds - the ids the caller identified as delegated children.
|
|
138
|
+
* @returns the session ids whose records were dropped.
|
|
139
|
+
*/
|
|
140
|
+
pruneDelegatedOwnership(sessionIds: ReadonlySet<string>): readonly string[];
|
|
141
|
+
/**
|
|
142
|
+
* Drop the ownership records of the named sessions, whatever their lineage.
|
|
143
|
+
*
|
|
144
|
+
* This is the record half of a deletion: the caller has already established
|
|
145
|
+
* that these sessions are gone (or is removing them as part of the same
|
|
146
|
+
* act), so the auth boundary goes with them. Nothing here decides anything —
|
|
147
|
+
* the sweeps above own the judgments, and both end in this.
|
|
148
|
+
*
|
|
149
|
+
* @param sessionIds - the ids whose records are dropped.
|
|
150
|
+
* @returns the session ids whose records were actually there.
|
|
151
|
+
*/
|
|
152
|
+
forgetSessions(sessionIds: ReadonlySet<string>): readonly string[];
|
|
153
|
+
/**
|
|
154
|
+
* Drop ownership records for chats the Harness no longer knows.
|
|
155
|
+
*
|
|
156
|
+
* An ownership record is a chat's auth boundary — `ensureSessionAccess`
|
|
157
|
+
* claims an unowned session for whoever asks first — so a record is removed
|
|
158
|
+
* only when the caller reports the session gone *and* the claim is older
|
|
159
|
+
* than `graceHours`. A reservation racing its own creation therefore keeps
|
|
160
|
+
* its owner, and a live chat never becomes claimable by someone else. What
|
|
161
|
+
* this reclaims is the residue of deleted chats, which nothing else removes.
|
|
162
|
+
*
|
|
163
|
+
* @param isLive - whether the Harness still knows this session.
|
|
164
|
+
* @param graceHours - youngest claim the sweep may touch.
|
|
165
|
+
* @returns the session ids whose records were dropped.
|
|
166
|
+
*/
|
|
167
|
+
pruneVanishedSessions(isLive: (sessionId: string) => boolean, graceHours: number): readonly string[];
|
|
84
168
|
/**
|
|
85
169
|
* Every ownership entry with the owner's display name resolved at read
|
|
86
170
|
* time, oldest claim first. Admin-only: this is the cross-user view the
|
|
@@ -131,7 +215,14 @@ export declare class QaAccounts {
|
|
|
131
215
|
readonly adminPreview?: boolean;
|
|
132
216
|
readonly capabilitySnapshot?: QaEffectiveCapabilityPolicy;
|
|
133
217
|
}[];
|
|
134
|
-
/**
|
|
218
|
+
/**
|
|
219
|
+
* Replace one account's authorization role, addressed by stable id.
|
|
220
|
+
*
|
|
221
|
+
* The union is re-checked here even though the type names it: the value
|
|
222
|
+
* arrives from the browser's admin console over the wire, and a role outside
|
|
223
|
+
* the union would be stored as-is and then crash every later permission
|
|
224
|
+
* check (`PERMISSIONS[role]` lookup) for that account.
|
|
225
|
+
*/
|
|
135
226
|
setAccessRole(userId: string, role: QaAccountRole): QaAccountUserPublic;
|
|
136
227
|
/**
|
|
137
228
|
* Disable or enable one account by id. Same semantics as the operator
|