@yadsh/dsh-qa-surface 0.11.1 → 0.12.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.
Files changed (70) hide show
  1. package/lib/access/service.js +2 -1
  2. package/lib/access/service.js.map +1 -1
  3. package/lib/client/QaSessionController.js +24 -2
  4. package/lib/client/QaSessionController.js.map +1 -1
  5. package/lib/client/QaSurface.js +9 -2
  6. package/lib/client/QaSurface.js.map +1 -1
  7. package/lib/client/components/QaChangelog.js +46 -11
  8. package/lib/client/components/QaChangelog.js.map +1 -1
  9. package/lib/client/components/QaSidebar.js +13 -15
  10. package/lib/client/components/QaSidebar.js.map +1 -1
  11. package/lib/client/settings/card.js +2 -2
  12. package/lib/client/settings/card.js.map +1 -1
  13. package/lib/client/settings/sections/docs.js +41 -0
  14. package/lib/client/settings/sections/docs.js.map +1 -0
  15. package/lib/client/settings/sections.js +1 -0
  16. package/lib/client/settings/sections.js.map +1 -1
  17. package/lib/client/slash/SlashPalette.js +4 -13
  18. package/lib/client/slash/SlashPalette.js.map +1 -1
  19. package/lib/client/slash/palette-rows.js +45 -0
  20. package/lib/client/slash/palette-rows.js.map +1 -0
  21. package/lib/client/slash/useSlashNavigation.js +8 -2
  22. package/lib/client/slash/useSlashNavigation.js.map +1 -1
  23. package/lib/client/styles.js +1 -2
  24. package/lib/client/styles.js.map +1 -1
  25. package/lib/client/turn-sources.js +20 -0
  26. package/lib/client/turn-sources.js.map +1 -1
  27. package/lib/client.js +398 -206
  28. package/lib/client.js.map +1 -1
  29. package/lib/config-resolvers/defaults.js +7 -0
  30. package/lib/config-resolvers/defaults.js.map +1 -1
  31. package/lib/config-resolvers/integration.js +7 -1
  32. package/lib/config-resolvers/integration.js.map +1 -1
  33. package/lib/config-resolvers/shared.js +12 -0
  34. package/lib/config-resolvers/shared.js.map +1 -1
  35. package/lib/config-resolvers/tools.js +26 -0
  36. package/lib/config-resolvers/tools.js.map +1 -1
  37. package/lib/config.js +21 -0
  38. package/lib/config.js.map +1 -1
  39. package/lib/index.js +10 -2
  40. package/lib/index.js.map +1 -1
  41. package/lib/launch-token.js +22 -17
  42. package/lib/launch-token.js.map +1 -1
  43. package/lib/qa-tools/catalog.js +13 -2
  44. package/lib/qa-tools/catalog.js.map +1 -1
  45. package/lib/qa-tools/docs-tools.js +334 -32
  46. package/lib/qa-tools/docs-tools.js.map +1 -1
  47. package/lib/qa-tools/index.js +2 -0
  48. package/lib/qa-tools/index.js.map +1 -1
  49. package/lib/secure-session.js +8 -0
  50. package/lib/secure-session.js.map +1 -1
  51. package/lib/shared/docs-version.js +19 -0
  52. package/lib/shared/docs-version.js.map +1 -0
  53. package/lib/typert.host.js +71 -68
  54. package/lib/typert.remote-client.js +70 -67
  55. package/lib/types/client/QaSessionController.d.ts +14 -0
  56. package/lib/types/client/components/QaWidthHandle.d.ts +0 -1
  57. package/lib/types/client/settings/sections/docs.d.ts +15 -0
  58. package/lib/types/client/settings/sections.d.ts +1 -0
  59. package/lib/types/client/slash/palette-rows.d.ts +43 -0
  60. package/lib/types/client/turn-sources.d.ts +14 -0
  61. package/lib/types/config-resolvers/integration.d.ts +7 -1
  62. package/lib/types/config-resolvers/shared.d.ts +8 -0
  63. package/lib/types/config-resolvers/tools.d.ts +9 -0
  64. package/lib/types/launch-token.d.ts +10 -3
  65. package/lib/types/qa-tools/catalog.d.ts +11 -1
  66. package/lib/types/qa-tools/docs-tools.d.ts +38 -5
  67. package/lib/types/qa-tools/index.d.ts +7 -0
  68. package/lib/types/shared/docs-version.d.ts +15 -0
  69. package/lib/types/types.d.ts +34 -0
  70. package/package.json +5 -5
@@ -15,6 +15,7 @@ export { AccessSection } from "./sections/access.js";
15
15
  export { AccountsSection } from "./sections/accounts.js";
16
16
  export { AttachmentsSection } from "./sections/attachments.js";
17
17
  export { BrandingSection } from "./sections/branding.js";
18
+ export { DocsSection } from "./sections/docs.js";
18
19
  export { EmbeddingSection } from "./sections/embedding.js";
19
20
  export { InterfaceSection } from "./sections/interface.js";
20
21
  export { LockdownSection } from "./sections/lockdown.js";
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Grouping for the palette's rows.
3
+ *
4
+ * Ranking is global: skills and commands are scored and sorted together, so
5
+ * `/tkp` can put a command between two skills and a palette that captioned
6
+ * every neighbouring run would draw «Навыки» twice over one group of rows.
7
+ *
8
+ * This module is the translation between the two: it buckets the ranked rows
9
+ * by kind, keeps each bucket in ranked order, and puts the bucket whose best
10
+ * row ranked highest first. The palette's first row is therefore still the
11
+ * best match — which is the row Enter picks when the user types nothing else.
12
+ *
13
+ * A row keeps its `index` from the ranked list, so what the keyboard moves
14
+ * through is the order the palette draws: the navigation hook publishes the
15
+ * flattened groups rather than the raw ranking.
16
+ *
17
+ * Pure on purpose: the hook and the component both need the same grouping, and
18
+ * the tests drive it with plain arrays.
19
+ */
20
+ import type { QaSlashCatalogEntry } from "../../types.js";
21
+ /** One palette row: the entry, and where it sat in the ranked list. */
22
+ export interface QaSlashPaletteRow {
23
+ readonly entry: QaSlashCatalogEntry;
24
+ /** Position in the ranked list these groups were built from. */
25
+ readonly index: number;
26
+ }
27
+ /** One titled group of the palette: exactly one per kind that was ranked. */
28
+ export interface QaSlashPaletteGroup {
29
+ readonly kind: QaSlashCatalogEntry["kind"];
30
+ readonly rows: readonly QaSlashPaletteRow[];
31
+ }
32
+ /**
33
+ * Bucket ranked rows by kind, one group per kind.
34
+ *
35
+ * A kind with no row contributes no group, and the group order follows the
36
+ * ranking (the best row's own kind leads) rather than a fixed
37
+ * skill-then-command order, so the top of the list never disagrees with the
38
+ * ranking above it.
39
+ */
40
+ export declare function groupPaletteRows(rows: readonly QaSlashCatalogEntry[]): readonly QaSlashPaletteGroup[];
41
+ /** The entries of every group, in the order the palette draws them. */
42
+ export declare function flattenPaletteGroups(groups: readonly QaSlashPaletteGroup[]): readonly QaSlashCatalogEntry[];
43
+ //# sourceMappingURL=palette-rows.d.ts.map
@@ -9,6 +9,20 @@ export declare function chatLegacyOf(snapshot: ConversationSnapshot | undefined)
9
9
  * and replay and requires no assistant-authored bibliography.
10
10
  */
11
11
  export declare function projectTurnSources(snapshot: ConversationSnapshot | undefined, sessionId?: string, workspaceRoot?: string): readonly QaTurnSources[];
12
+ /**
13
+ * The directory a projected source path is anchored on: the chat's own cwd.
14
+ *
15
+ * Both channels read the same durable tool results, and the Host's evidence —
16
+ * the set the source preview checks a request against — canonicalizes every
17
+ * recorded path against the session's own cwd. A projection anchored on
18
+ * anything else splits one file into two spellings the moment the two
19
+ * directories differ (an adopted chat, a chat whose cwd was pinned under an
20
+ * older configuration): the rail then shows a path the Host does not hold, and
21
+ * the detailed view refuses a source the answer just cited as no longer
22
+ * evidence until the rail is rebuilt from the Host's own bundles. The
23
+ * configured pin stays the fallback for a chat the browser cannot place yet.
24
+ */
25
+ export declare function sourceAnchorRoot(chatCwd: string | undefined, configuredCwd: string | null): string | undefined;
12
26
  /** Visible evidence sources for the latest turn, shared by the drawer/footer. */
13
27
  export declare function projectSources(snapshot: ConversationSnapshot | undefined, sessionId?: string, workspaceRoot?: string): readonly QaSource[];
14
28
  //# sourceMappingURL=turn-sources.d.ts.map
@@ -3,8 +3,14 @@ type ResolvedIntegration = ResolvedQaSurfaceConfig["integration"];
3
3
  /** Operator-tunable bounds; the values the settings card shows as hard limits. */
4
4
  export declare const QA_INTEGRATION_TTL_DAYS_MIN = 1;
5
5
  export declare const QA_INTEGRATION_TTL_DAYS_MAX = 3650;
6
+ /**
7
+ * The operator may choose any wait Node can represent faithfully. The former
8
+ * ten-minute product ceiling cut off legitimate long analyses, while values
9
+ * above the runtime ceiling wrap to an almost immediate timer.
10
+ */
6
11
  export declare const QA_INTEGRATION_TIMEOUT_MS_MIN = 5000;
7
- export declare const QA_INTEGRATION_TIMEOUT_MS_MAX = 600000;
12
+ /** Largest delay Node schedules without overflowing to an immediate timer. */
13
+ export declare const QA_INTEGRATION_TIMEOUT_MS_MAX = 2147483647;
8
14
  export declare const QA_INTEGRATION_CONCURRENCY_MIN = 1;
9
15
  export declare const QA_INTEGRATION_CONCURRENCY_MAX = 16;
10
16
  export declare const QA_INTEGRATION_RATE_MIN = 1;
@@ -7,6 +7,14 @@ export declare function optionalText(value: string | null | undefined): string |
7
7
  * Windows-style root is not a mistake to reject on a POSIX host.
8
8
  */
9
9
  export declare function isAbsoluteDirectoryPath(value: string): boolean;
10
+ /**
11
+ * Shared numeric guard for a limit with a floor but no honest ceiling: the value
12
+ * has to be a safe integer of at least `min`. A ceiling belongs on a budget the
13
+ * deployment itself spends (bytes, parallel calls); a wait it merely allows is
14
+ * the operator's call, and a ceiling there only ever truncated a legitimate long
15
+ * answer.
16
+ */
17
+ export declare function assertIntAtLeast(name: string, value: number, min: number): void;
10
18
  /**
11
19
  * Shared numeric guard for operator-tuned limits: the value has to be a safe
12
20
  * integer inside [min, max], and every rejection carries the same message.
@@ -2,5 +2,14 @@ import type { QaSurfaceConfig, ResolvedQaSurfaceConfig } from "../types.js";
2
2
  type ToolsSlice = ResolvedQaSurfaceConfig["tools"];
3
3
  /** Resolve the QA tool delivery domain: activation trigger, policy, and scope. */
4
4
  export declare function resolveTools(input: QaSurfaceConfig): ToolsSlice;
5
+ /**
6
+ * The version one search runs under, or `""` when the deployment's default is
7
+ * off, unset, or not what the call is about.
8
+ *
9
+ * The readers take a single fallback, and a switch that is off must not fall
10
+ * back at all — the value stays configured for the day it is turned on, and an
11
+ * enabled-but-empty pair is simply no default.
12
+ */
13
+ export declare function docsDefaultVersionOf(tools: ResolvedQaSurfaceConfig["tools"]): string;
5
14
  export {};
6
15
  //# sourceMappingURL=tools.d.ts.map
@@ -12,9 +12,16 @@ interface AuthenticatedUrlFace {
12
12
  authenticatedUrl?(baseUrl: string): string;
13
13
  }
14
14
  /**
15
- * Resolve the launch token once per process (it is stable for the process
16
- * lifetime); a missing connection service or an unusable answer warns once
17
- * and permanently disables the bridge instead of retrying per request.
15
+ * Resolve the launch token lazily, on the navigations that need it.
16
+ *
17
+ * A token that resolved is cached — it is stable for the process lifetime, so
18
+ * the host is asked once. A failure is **not** cached: the first `/qa` request
19
+ * can arrive while the plugin tree is still starting, when `connection` is not
20
+ * answerable yet, and treating that single early answer as final used to leave
21
+ * every later cookie-less browser on the marker hand-off, which dead-ends at
22
+ * the host's token screen until the next restart. The failure is reported once
23
+ * per reason and retried quietly afterwards, so the retry costs a service
24
+ * lookup and never a log flood.
18
25
  */
19
26
  export declare function makeLaunchTokenSource(getConnection: () => AuthenticatedUrlFace | undefined, warn: (message: string) => void): LaunchTokenSource;
20
27
  export {};
@@ -9,7 +9,7 @@ import type { QaToolDescriptor } from "./types.js";
9
9
  * Bump it whenever the catalog's tool set or their schemas change.
10
10
  */
11
11
  export declare const QA_TOOL_CATALOG_VERSION = "3";
12
- /** One QA tool reports the activation state of whichever agent calls it. */
12
+ /** What the catalog's definitions read, beyond the execution record. */
13
13
  export interface QaToolsSelfcheckDeps {
14
14
  readonly catalogVersion: string;
15
15
  readonly activationMode: string;
@@ -19,6 +19,16 @@ export interface QaToolsSelfcheckDeps {
19
19
  readonly activeTools: (agent: Agent) => readonly string[];
20
20
  /** Whether the calling agent's session shows the activation skill was loaded. */
21
21
  readonly skillLoaded: (agent: Agent) => boolean;
22
+ /**
23
+ * Documentation root the two readers use, or `""` for the `docs/` directory
24
+ * inside each chat's own workspace.
25
+ */
26
+ readonly docsRoot?: string | (() => string);
27
+ /**
28
+ * Version a search without `version` and without `path` stays inside, or `""`
29
+ * for a search across every edition the corpus carries.
30
+ */
31
+ readonly docsDefaultVersion?: string | (() => string);
22
32
  }
23
33
  /**
24
34
  * Build the shipped QA tool catalog.
@@ -19,6 +19,14 @@ import type { ToolDefinition } from "@deepseek-ai/dsh-tools";
19
19
  * names are accepted as filters. A path the convention does not describe is
20
20
  * still searched and still read — it simply carries no identity.
21
21
  *
22
+ * A real corpus also carries its own working material: the stand publishes an
23
+ * asset tree, inventories and triage notes beside the documents, under leading
24
+ * underscores. None of that is what a question is asked about, and an inventory
25
+ * names every module and version there is, so a walk that met it first spent the
26
+ * whole answer on it. The walk therefore visits the documented modules first,
27
+ * loose files next, and the underscored service material last — searched, but
28
+ * never at the expense of the documents.
29
+ *
22
30
  * Nothing here is destructive and building it registers nothing: like the rest
23
31
  * of the catalog these are side-effect-free definitions, and the workspace root
24
32
  * is read from `exec.agent` at call time.
@@ -54,11 +62,32 @@ interface QaDocsExecution {
54
62
  };
55
63
  };
56
64
  }
65
+ /**
66
+ * Where this deployment publishes its documentation.
67
+ *
68
+ * A deployment that hands every chat its own workspace publishes documentation
69
+ * inside it, and an empty `root` keeps that layout. One that publishes the
70
+ * corpus once — outside every per-user directory — names it here, and the tools
71
+ * read it exactly as they read a per-chat tree: same fence, same reported paths
72
+ * (`docs/<module>/<version>/…`), only the directory that contains the tree
73
+ * comes from configuration instead of from the calling chat.
74
+ */
75
+ export interface QaDocsOptions {
76
+ /** Absolute documentation root, or `""` for `<chat workspace>/docs`. */
77
+ readonly root?: string;
78
+ /**
79
+ * Documentation version a search stays inside when the caller named neither
80
+ * `version` nor `path`; `""` (the default) leaves every edition in scope. The
81
+ * deployment sets it so a chat's questions do not have to carry the stand's
82
+ * edition, and an explicit `version` or `path` still decides for itself.
83
+ */
84
+ readonly defaultVersion?: string;
85
+ }
57
86
  /** The resolved documentation root of one call. */
58
87
  export interface QaDocsRoot {
59
- /** Canonical workspace root of the calling chat. */
88
+ /** Canonical directory the tree sits in: the chat's workspace, or the configured root's parent. */
60
89
  readonly workspace: string;
61
- /** Canonical `<workspace>/docs`. */
90
+ /** Canonical directory documentation is read from. */
62
91
  readonly docs: string;
63
92
  }
64
93
  /**
@@ -116,8 +145,12 @@ export declare function docsIdentityOf(pathFromDocsRoot: string): QaDocsIdentity
116
145
  * The root is a real directory inside the canonical workspace: a `docs` entry
117
146
  * that is a link or a file is refused rather than followed, because the fence
118
147
  * is the point of the tool and a link is how a fence is left.
148
+ *
149
+ * A deployment that names its corpus once (`QaDocsOptions.root`) skips the
150
+ * per-chat lookup entirely — that layout is exactly why it configures the root —
151
+ * and is held to the same rules: absolute, a real directory, no links.
119
152
  */
120
- export declare function docsRootOf(exec: QaDocsExecution): Promise<QaDocsRoot>;
153
+ export declare function docsRootOf(exec: QaDocsExecution, options?: QaDocsOptions): Promise<QaDocsRoot>;
121
154
  export interface QaDocsSearchRequest {
122
155
  readonly query: string;
123
156
  readonly version?: string | undefined;
@@ -153,8 +186,8 @@ export declare function readDocumentation(root: QaDocsRoot, request: QaDocsReadR
153
186
  * version/module facets exist so a model that has been told "3.8" stops
154
187
  * sweeping the whole tree.
155
188
  */
156
- export declare function createDocsSearchTool(): ToolDefinition;
189
+ export declare function createDocsSearchTool(options?: QaDocsOptions): ToolDefinition;
157
190
  /** `docs_read` — open one documentation file at a bounded window of lines. */
158
- export declare function createDocsReadTool(): ToolDefinition;
191
+ export declare function createDocsReadTool(options?: QaDocsOptions): ToolDefinition;
159
192
  export {};
160
193
  //# sourceMappingURL=docs-tools.d.ts.map
@@ -8,6 +8,13 @@ export interface QaToolsOptions {
8
8
  readonly activationSkill: string;
9
9
  readonly activationMode: string;
10
10
  readonly activationPresets: readonly string[];
11
+ /** Documentation root the readers use; `""` reads each chat's own `docs/`. */
12
+ readonly docsRoot: string | (() => string);
13
+ /**
14
+ * Version a search falls back to when the model named neither `version` nor
15
+ * `path`; `""` searches every edition the corpus carries.
16
+ */
17
+ readonly docsDefaultVersion: string | (() => string);
11
18
  }
12
19
  /**
13
20
  * The QA tool subsystem: one catalog, one per-agent activation manager, and the
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The documentation version grammar, shared by the Host and the browser half.
3
+ *
4
+ * This module must stay dependency-free: the settings card bundle inlines the
5
+ * resolver that validates a configured default version, and a browser build
6
+ * must not pull the QA tool runtime — or the Node builtins it reads files with
7
+ * — into the page just to learn what a version looks like.
8
+ */
9
+ /**
10
+ * Whether a path segment — or a configured default version — is a documentation
11
+ * version. A default the corpus can never match is refused where it is written
12
+ * rather than answered with an empty search.
13
+ */
14
+ export declare function isDocumentationVersion(value: string): boolean;
15
+ //# sourceMappingURL=docs-version.d.ts.map
@@ -714,6 +714,28 @@ export interface QaSurfaceConfig {
714
714
  * name the QA preset in any deployment that composes more than one.
715
715
  */
716
716
  readonly activationPresets?: readonly string[];
717
+ /**
718
+ * Absolute path of the documentation tree `docs_search`/`docs_read` read.
719
+ * Leave it empty when the deployment publishes `docs/` inside each chat's
720
+ * own workspace; name it when the corpus is published once, outside every
721
+ * per-user directory, which is otherwise unreadable to those tools.
722
+ */
723
+ readonly docsRoot?: string;
724
+ /**
725
+ * Documentation version `docs_search` stays inside when the model named
726
+ * neither `version` nor `path`. A corpus carries several editions of the
727
+ * same module, and a chat asked about "the product" answers out of whichever
728
+ * edition it happened to read first; naming the stand's edition here lets a
729
+ * search default to it, and an explicit `version` or `path` still decides
730
+ * for itself.
731
+ */
732
+ readonly docsDefaultVersion?: string;
733
+ /**
734
+ * Whether `docsDefaultVersion` is applied. The value stays configured while
735
+ * the switch is off, so a stand can turn the default back on without
736
+ * retyping it.
737
+ */
738
+ readonly docsDefaultVersionEnabled?: boolean;
717
739
  };
718
740
  readonly lockdown?: {
719
741
  readonly enabled?: boolean;
@@ -990,6 +1012,18 @@ export interface ResolvedQaSurfaceConfig {
990
1012
  readonly activationSkill: string;
991
1013
  readonly activationMode: QaToolActivationMode;
992
1014
  readonly activationPresets: readonly string[];
1015
+ /**
1016
+ * Absolute documentation root the QA catalogue reads, or `""` for the
1017
+ * `docs/` directory inside each chat's own workspace.
1018
+ */
1019
+ readonly docsRoot: string;
1020
+ /**
1021
+ * Version a search without `version` and without `path` stays inside, or
1022
+ * `""` for a search across every edition the corpus carries.
1023
+ */
1024
+ readonly docsDefaultVersion: string;
1025
+ /** Whether the deployment's default version is in force. */
1026
+ readonly docsDefaultVersionEnabled: boolean;
993
1027
  };
994
1028
  readonly sources: {
995
1029
  readonly enabled: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yadsh/dsh-qa-surface",
3
- "version": "0.11.1",
3
+ "version": "0.12.0",
4
4
  "description": "A focused end-user QA surface backed by native DeepSeek Harness sessions",
5
5
  "repository": {
6
6
  "type": "git",
@@ -133,11 +133,11 @@
133
133
  "dependencies": {
134
134
  "yaml": "^2.9.0",
135
135
  "zod": "^4.4.3",
136
+ "@yadsh/dsh-documents": "^0.5.1",
136
137
  "@yadsh/dsh-audit-core": "^0.1.0",
137
- "@yadsh/dsh-audit-ui": "^0.1.0",
138
- "@yadsh/dsh-documents": "^0.5.0",
139
- "@yadsh/dsh-plugin-kit": "^0.3.0",
140
- "@yadsh/dsh-plugin-log": "^0.4.0"
138
+ "@yadsh/dsh-audit-ui": "^0.1.1",
139
+ "@yadsh/dsh-plugin-log": "^0.4.0",
140
+ "@yadsh/dsh-plugin-kit": "^0.3.0"
141
141
  },
142
142
  "devDependencies": {
143
143
  "@deepseek-ai/cordis": "4.0.2",