@yadsh/dsh-qa-surface 0.3.0 → 0.5.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 +192 -15
- package/cordis.patch.yml +8 -0
- package/lib/account-remotes.js +100 -0
- package/lib/account-remotes.js.map +1 -0
- package/lib/accounts/credentials.js +35 -0
- package/lib/accounts/credentials.js.map +1 -0
- package/lib/accounts/errors.js +9 -0
- package/lib/accounts/errors.js.map +1 -0
- package/lib/accounts/file.js +101 -0
- package/lib/accounts/file.js.map +1 -0
- package/lib/accounts/store.js +159 -161
- package/lib/accounts/store.js.map +1 -1
- package/lib/accounts/token.js +61 -0
- package/lib/accounts/token.js.map +1 -0
- package/lib/approvals.js +179 -0
- package/lib/approvals.js.map +1 -0
- package/lib/attachment-rules.js +111 -0
- package/lib/attachment-rules.js.map +1 -0
- package/lib/cli.js +132 -9
- package/lib/cli.js.map +1 -1
- package/lib/client/QaAccountsController.js +32 -2
- package/lib/client/QaAccountsController.js.map +1 -1
- package/lib/client/QaSessionController.js +140 -65
- package/lib/client/QaSessionController.js.map +1 -1
- package/lib/client/QaSurface.js +134 -107
- package/lib/client/QaSurface.js.map +1 -1
- package/lib/client/QaTranscriptAdapter.js +71 -6
- package/lib/client/QaTranscriptAdapter.js.map +1 -1
- package/lib/client/approvals.js +104 -0
- package/lib/client/approvals.js.map +1 -0
- package/lib/client/attachments.js +150 -0
- package/lib/client/attachments.js.map +1 -0
- package/lib/client/attestation.js +4 -1
- package/lib/client/attestation.js.map +1 -1
- package/lib/client/base64.js +16 -0
- package/lib/client/base64.js.map +1 -0
- package/lib/client/chat-files.js +48 -0
- package/lib/client/chat-files.js.map +1 -0
- package/lib/client/components/Markdown.js +1 -1
- package/lib/client/components/Markdown.js.map +1 -1
- package/lib/client/components/QaApproval.js +23 -0
- package/lib/client/components/QaApproval.js.map +1 -0
- package/lib/client/components/QaChangelog.js +62 -19
- package/lib/client/components/QaChangelog.js.map +1 -1
- package/lib/client/components/QaComposer.js +56 -74
- package/lib/client/components/QaComposer.js.map +1 -1
- package/lib/client/components/QaFileAttachment.js +19 -0
- package/lib/client/components/QaFileAttachment.js.map +1 -0
- package/lib/client/components/QaFilesPanel.js +45 -0
- package/lib/client/components/QaFilesPanel.js.map +1 -0
- package/lib/client/components/QaHeader.js +34 -0
- package/lib/client/components/QaHeader.js.map +1 -0
- package/lib/client/components/QaMessage.js +17 -4
- package/lib/client/components/QaMessage.js.map +1 -1
- package/lib/client/components/QaModal.js +31 -0
- package/lib/client/components/QaModal.js.map +1 -0
- package/lib/client/components/QaProfile.js +64 -0
- package/lib/client/components/QaProfile.js.map +1 -0
- package/lib/client/components/QaQuestions.js +96 -0
- package/lib/client/components/QaQuestions.js.map +1 -0
- package/lib/client/components/QaRightRail.js +15 -0
- package/lib/client/components/QaRightRail.js.map +1 -0
- package/lib/client/components/QaSidebar.js +6 -1
- package/lib/client/components/QaSidebar.js.map +1 -1
- package/lib/client/components/{QaSourcesDrawer.js → QaSourcesPanel.js} +3 -3
- package/lib/client/components/QaSourcesPanel.js.map +1 -0
- package/lib/client/components/QaTurnRail.js +15 -0
- package/lib/client/components/QaTurnRail.js.map +1 -1
- package/lib/client/components/QaWidthHandle.js +25 -20
- package/lib/client/components/QaWidthHandle.js.map +1 -1
- package/lib/client/components/QaWorkGroup.js +19 -20
- package/lib/client/components/QaWorkGroup.js.map +1 -1
- package/lib/client/components/surface-utils.js +28 -0
- package/lib/client/components/surface-utils.js.map +1 -0
- package/lib/client/components/thinking-phrases.js +24 -0
- package/lib/client/components/thinking-phrases.js.map +1 -0
- package/lib/client/create-session.js +7 -32
- package/lib/client/create-session.js.map +1 -1
- package/lib/client/failure-copy.js +63 -0
- package/lib/client/failure-copy.js.map +1 -0
- package/lib/client/index.js +49 -7
- package/lib/client/index.js.map +1 -1
- package/lib/client/project-session-state.js +5 -3
- package/lib/client/project-session-state.js.map +1 -1
- package/lib/client/prompt-content.js +54 -0
- package/lib/client/prompt-content.js.map +1 -0
- package/lib/client/questions.js +114 -0
- package/lib/client/questions.js.map +1 -0
- package/lib/client/settings/card.js +143 -0
- package/lib/client/settings/card.js.map +1 -0
- package/lib/client/settings/fields.js +145 -0
- package/lib/client/settings/fields.js.map +1 -0
- package/lib/client/settings/format.js +255 -0
- package/lib/client/settings/format.js.map +1 -0
- package/lib/client/settings/sections/access.js +25 -0
- package/lib/client/settings/sections/access.js.map +1 -0
- package/lib/client/settings/sections/accounts.js +47 -0
- package/lib/client/settings/sections/accounts.js.map +1 -0
- package/lib/client/settings/sections/attachments.js +46 -0
- package/lib/client/settings/sections/attachments.js.map +1 -0
- package/lib/client/settings/sections/branding.js +28 -0
- package/lib/client/settings/sections/branding.js.map +1 -0
- package/lib/client/settings/sections/common.js +20 -0
- package/lib/client/settings/sections/common.js.map +1 -0
- package/lib/client/settings/sections/embedding.js +19 -0
- package/lib/client/settings/sections/embedding.js.map +1 -0
- package/lib/client/settings/sections/interface.js +60 -0
- package/lib/client/settings/sections/interface.js.map +1 -0
- package/lib/client/settings/sections/lockdown.js +82 -0
- package/lib/client/settings/sections/lockdown.js.map +1 -0
- package/lib/client/settings/sections/session.js +53 -0
- package/lib/client/settings/sections/session.js.map +1 -0
- package/lib/client/settings/sections/sources.js +74 -0
- package/lib/client/settings/sections/sources.js.map +1 -0
- package/lib/client/settings/sections/status.js +37 -0
- package/lib/client/settings/sections/status.js.map +1 -0
- package/lib/client/settings/sections.js +23 -0
- package/lib/client/settings/sections.js.map +1 -0
- package/lib/client/settings/styles.js +57 -0
- package/lib/client/settings/styles.js.map +1 -0
- package/lib/client/stream-publisher.js +47 -0
- package/lib/client/stream-publisher.js.map +1 -0
- package/lib/client/styles.js +127 -19
- package/lib/client/styles.js.map +1 -1
- package/lib/client/types.js +2 -0
- package/lib/client/types.js.map +1 -1
- package/lib/client/use-right-rail.js +96 -0
- package/lib/client/use-right-rail.js.map +1 -0
- package/lib/client/use-session-ui-state.js +26 -19
- package/lib/client/use-session-ui-state.js.map +1 -1
- package/lib/client.js +5694 -813
- package/lib/client.js.map +1 -1
- package/lib/config-resolvers/accounts.js +60 -0
- package/lib/config-resolvers/accounts.js.map +1 -0
- package/lib/config-resolvers/attachments.js +28 -0
- package/lib/config-resolvers/attachments.js.map +1 -0
- package/lib/config-resolvers/basics.js +61 -0
- package/lib/config-resolvers/basics.js.map +1 -0
- package/lib/config-resolvers/defaults.js +136 -0
- package/lib/config-resolvers/defaults.js.map +1 -0
- package/lib/config-resolvers/lockdown.js +90 -0
- package/lib/config-resolvers/lockdown.js.map +1 -0
- package/lib/config-resolvers/session.js +42 -0
- package/lib/config-resolvers/session.js.map +1 -0
- package/lib/config-resolvers/shared.js +51 -0
- package/lib/config-resolvers/shared.js.map +1 -0
- package/lib/config-resolvers/sources.js +83 -0
- package/lib/config-resolvers/sources.js.map +1 -0
- package/lib/config-resolvers/ui.js +20 -0
- package/lib/config-resolvers/ui.js.map +1 -0
- package/lib/config.js +76 -6
- package/lib/config.js.map +1 -1
- package/lib/entry-redirect.js +2 -1
- package/lib/entry-redirect.js.map +1 -1
- package/lib/index.js +198 -79
- package/lib/index.js.map +1 -1
- package/lib/profile.js +214 -0
- package/lib/profile.js.map +1 -0
- package/lib/prompt-notes.js +231 -0
- package/lib/prompt-notes.js.map +1 -0
- package/lib/provenance/host-store.js +25 -9
- package/lib/provenance/host-store.js.map +1 -1
- package/lib/provenance/reported.js +37 -12
- package/lib/provenance/reported.js.map +1 -1
- package/lib/questions.js +185 -0
- package/lib/questions.js.map +1 -0
- package/lib/resolve-config.js +35 -410
- 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/session-ownership.js +55 -0
- package/lib/session-ownership.js.map +1 -0
- package/lib/shared/session-key.js +18 -0
- package/lib/shared/session-key.js.map +1 -0
- package/lib/shared/settings.js +10 -0
- package/lib/shared/settings.js.map +1 -0
- package/lib/thinking-phrases.js +68 -0
- package/lib/thinking-phrases.js.map +1 -0
- package/lib/typert.host.js +506 -21
- package/lib/typert.remote-client.d.ts +15 -1
- package/lib/typert.remote-client.js +407 -15
- package/lib/types/account-remotes.d.ts +42 -0
- package/lib/types/accounts/credentials.d.ts +20 -0
- package/lib/types/accounts/errors.d.ts +11 -0
- package/lib/types/accounts/file.d.ts +82 -0
- package/lib/types/accounts/store.d.ts +74 -43
- package/lib/types/accounts/token.d.ts +31 -0
- package/lib/types/approvals.d.ts +65 -0
- package/lib/types/attachment-rules.d.ts +44 -0
- package/lib/types/cli.d.ts +2 -2
- package/lib/types/client/QaAccountsController.d.ts +8 -1
- package/lib/types/client/QaSessionController.d.ts +53 -16
- package/lib/types/client/QaSurface.d.ts +14 -1
- package/lib/types/client/approvals.d.ts +39 -0
- package/lib/types/client/attachments.d.ts +54 -0
- package/lib/types/client/base64.d.ts +10 -0
- package/lib/types/client/chat-files.d.ts +30 -0
- package/lib/types/client/components/QaApproval.d.ts +13 -0
- package/lib/types/client/components/QaChangelog.d.ts +1 -1
- package/lib/types/client/components/QaComposer.d.ts +8 -4
- package/lib/types/client/components/QaFileAttachment.d.ts +14 -0
- package/lib/types/client/components/QaFilesPanel.d.ts +15 -0
- package/lib/types/client/components/QaHeader.d.ts +41 -0
- package/lib/types/client/components/QaMessage.d.ts +3 -1
- package/lib/types/client/components/QaModal.d.ts +26 -0
- package/lib/types/client/components/QaProfile.d.ts +25 -0
- package/lib/types/client/components/QaQuestions.d.ts +13 -0
- package/lib/types/client/components/QaRightRail.d.ts +23 -0
- package/lib/types/client/components/QaSidebar.d.ts +14 -0
- package/lib/types/client/components/{QaSourcesDrawer.d.ts → QaSourcesPanel.d.ts} +7 -4
- package/lib/types/client/components/QaTurnRail.d.ts +6 -0
- package/lib/types/client/components/QaWidthHandle.d.ts +14 -7
- package/lib/types/client/components/QaWorkGroup.d.ts +5 -3
- package/lib/types/client/components/surface-utils.d.ts +11 -0
- package/lib/types/client/components/thinking-phrases.d.ts +9 -0
- package/lib/types/client/create-session.d.ts +6 -9
- package/lib/types/client/failure-copy.d.ts +19 -0
- package/lib/types/client/project-session-state.d.ts +8 -1
- package/lib/types/client/prompt-content.d.ts +30 -0
- package/lib/types/client/questions.d.ts +45 -0
- package/lib/types/client/settings/card.d.ts +23 -0
- package/lib/types/client/settings/fields.d.ts +139 -0
- package/lib/types/client/settings/format.d.ts +108 -0
- package/lib/types/client/settings/sections/access.d.ts +9 -0
- package/lib/types/client/settings/sections/accounts.d.ts +9 -0
- package/lib/types/client/settings/sections/attachments.d.ts +15 -0
- package/lib/types/client/settings/sections/branding.d.ts +8 -0
- package/lib/types/client/settings/sections/common.d.ts +28 -0
- package/lib/types/client/settings/sections/embedding.d.ts +8 -0
- package/lib/types/client/settings/sections/interface.d.ts +9 -0
- package/lib/types/client/settings/sections/lockdown.d.ts +9 -0
- package/lib/types/client/settings/sections/session.d.ts +9 -0
- package/lib/types/client/settings/sections/sources.d.ts +9 -0
- package/lib/types/client/settings/sections/status.d.ts +22 -0
- package/lib/types/client/settings/sections.d.ts +25 -0
- package/lib/types/client/settings/styles.d.ts +8 -0
- package/lib/types/client/stream-publisher.d.ts +28 -0
- package/lib/types/client/types.d.ts +76 -1
- package/lib/types/client/use-right-rail.d.ts +56 -0
- package/lib/types/client/use-session-ui-state.d.ts +13 -7
- package/lib/types/config-resolvers/accounts.d.ts +15 -0
- package/lib/types/config-resolvers/attachments.d.ts +6 -0
- package/lib/types/config-resolvers/basics.d.ts +8 -0
- package/lib/types/config-resolvers/defaults.d.ts +4 -0
- package/lib/types/config-resolvers/lockdown.d.ts +10 -0
- package/lib/types/config-resolvers/session.d.ts +6 -0
- package/lib/types/config-resolvers/shared.d.ts +23 -0
- package/lib/types/config-resolvers/sources.d.ts +6 -0
- package/lib/types/config-resolvers/ui.d.ts +6 -0
- package/lib/types/index.d.ts +48 -15
- package/lib/types/profile.d.ts +76 -0
- package/lib/types/prompt-notes.d.ts +110 -0
- package/lib/types/provenance/reported.d.ts +9 -1
- package/lib/types/questions.d.ts +83 -0
- package/lib/types/resolve-config.d.ts +8 -3
- package/lib/types/secure-session.d.ts +46 -2
- package/lib/types/session-ownership.d.ts +30 -0
- package/lib/types/shared/session-key.d.ts +23 -0
- package/lib/types/shared/settings.d.ts +10 -0
- package/lib/types/thinking-phrases.d.ts +17 -0
- package/lib/types/types.d.ts +203 -9
- package/lib/types/user-workspace.d.ts +42 -0
- package/lib/user-workspace.js +246 -0
- package/lib/user-workspace.js.map +1 -0
- package/package.json +15 -8
- package/CHANGELOG.md +0 -176
- package/SPEC-dsh-qa-surface.md +0 -2425
- package/docs/ARCHITECTURE.md +0 -66
- package/docs/COMPATIBILITY.md +0 -14
- package/docs/CONFIGURATION.md +0 -199
- package/docs/EMBEDDING.md +0 -13
- package/docs/SOURCES-MIGRATION.md +0 -28
- package/docs/SOURCES-PROVENANCE-AUDIT.md +0 -88
- package/docs/SPEC-dsh-qa-surface-accounts.md +0 -219
- package/docs/SPEC-dsh-qa-surface-sources-provenance.md +0 -1692
- package/lib/client/components/QaSourcesDrawer.js.map +0 -1
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { chmodSync, lstatSync, mkdirSync, readdirSync, realpathSync, statSync, } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
/** Stable, non-workspace child used to keep account directories out of sight. */
|
|
4
|
+
export const QA_USER_WORKSPACES_DIRECTORY = ".qa-users";
|
|
5
|
+
/** Conservative fixed budgets; there is deliberately no browser override. */
|
|
6
|
+
export const QA_USER_WORKSPACE_MAX_BYTES = 256 * 1024 * 1024;
|
|
7
|
+
export const QA_USER_WORKSPACE_MAX_WRITE_BYTES = 10 * 1024 * 1024;
|
|
8
|
+
const USER_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu;
|
|
9
|
+
const PATH_DENIAL = "QA workspace boundary: path is outside this user's directory.";
|
|
10
|
+
const QUOTA_DENIAL = "QA workspace boundary: storage quota exceeded.";
|
|
11
|
+
function samePath(left, right) {
|
|
12
|
+
return process.platform === "win32"
|
|
13
|
+
? left.toLowerCase() === right.toLowerCase()
|
|
14
|
+
: left === right;
|
|
15
|
+
}
|
|
16
|
+
export function pathIsInside(target, root) {
|
|
17
|
+
const relative = path.relative(root, target);
|
|
18
|
+
return (relative === "" ||
|
|
19
|
+
(!relative.startsWith(`..${path.sep}`) &&
|
|
20
|
+
relative !== ".." &&
|
|
21
|
+
!path.isAbsolute(relative)));
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the deepest existing ancestor, preserving the missing suffix. This
|
|
25
|
+
* catches symlink escapes for reads and for files which a write is about to
|
|
26
|
+
* create.
|
|
27
|
+
*/
|
|
28
|
+
export function canonicalCandidate(input) {
|
|
29
|
+
const resolved = path.resolve(input);
|
|
30
|
+
const suffix = [];
|
|
31
|
+
let cursor = resolved;
|
|
32
|
+
for (;;) {
|
|
33
|
+
try {
|
|
34
|
+
return path.resolve(realpathSync.native(cursor), ...suffix);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (error.code !== "ENOENT")
|
|
38
|
+
throw error;
|
|
39
|
+
const parent = path.dirname(cursor);
|
|
40
|
+
if (parent === cursor)
|
|
41
|
+
throw error;
|
|
42
|
+
suffix.unshift(path.basename(cursor));
|
|
43
|
+
cursor = parent;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function assertPlainDirectory(input) {
|
|
48
|
+
const stat = lstatSync(input);
|
|
49
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
50
|
+
throw new Error(`QA user workspace must be a real directory: ${input}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Materialize `<registered workspace>/.qa-users/<account UUID>` without
|
|
55
|
+
* registering the child as a DSH Workspace. Both directory levels reject
|
|
56
|
+
* symlinks, and their real paths must stay below the configured workspace.
|
|
57
|
+
*/
|
|
58
|
+
export function prepareQaUserWorkspace(workspaceRoot, userId) {
|
|
59
|
+
if (!USER_ID_PATTERN.test(userId)) {
|
|
60
|
+
throw new Error("QA account id is not safe for a workspace directory");
|
|
61
|
+
}
|
|
62
|
+
const canonicalWorkspace = realpathSync.native(workspaceRoot);
|
|
63
|
+
const usersRoot = path.join(canonicalWorkspace, QA_USER_WORKSPACES_DIRECTORY);
|
|
64
|
+
mkdirSync(usersRoot, { recursive: true, mode: 0o700 });
|
|
65
|
+
assertPlainDirectory(usersRoot);
|
|
66
|
+
if (process.platform !== "win32")
|
|
67
|
+
chmodSync(usersRoot, 0o700);
|
|
68
|
+
const canonicalUsersRoot = realpathSync.native(usersRoot);
|
|
69
|
+
if (!pathIsInside(canonicalUsersRoot, canonicalWorkspace)) {
|
|
70
|
+
throw new Error("QA user-workspace root escapes the configured workspace");
|
|
71
|
+
}
|
|
72
|
+
const userRoot = path.join(canonicalUsersRoot, userId);
|
|
73
|
+
mkdirSync(userRoot, { recursive: true, mode: 0o700 });
|
|
74
|
+
assertPlainDirectory(userRoot);
|
|
75
|
+
if (process.platform !== "win32")
|
|
76
|
+
chmodSync(userRoot, 0o700);
|
|
77
|
+
const canonicalUserRoot = realpathSync.native(userRoot);
|
|
78
|
+
if (!pathIsInside(canonicalUserRoot, canonicalUsersRoot) ||
|
|
79
|
+
!samePath(path.dirname(canonicalUserRoot), canonicalUsersRoot)) {
|
|
80
|
+
throw new Error("QA user workspace escapes its account directory");
|
|
81
|
+
}
|
|
82
|
+
return canonicalUserRoot;
|
|
83
|
+
}
|
|
84
|
+
/** Resolve an already provisioned account directory for admission checks. */
|
|
85
|
+
export function existingQaUserWorkspace(workspaceRoot, userId) {
|
|
86
|
+
if (!USER_ID_PATTERN.test(userId)) {
|
|
87
|
+
throw new Error("QA account id is not safe for a workspace directory");
|
|
88
|
+
}
|
|
89
|
+
const canonicalWorkspace = realpathSync.native(workspaceRoot);
|
|
90
|
+
const usersRoot = path.join(canonicalWorkspace, QA_USER_WORKSPACES_DIRECTORY);
|
|
91
|
+
const userRoot = path.join(usersRoot, userId);
|
|
92
|
+
assertPlainDirectory(usersRoot);
|
|
93
|
+
assertPlainDirectory(userRoot);
|
|
94
|
+
const canonicalUsersRoot = realpathSync.native(usersRoot);
|
|
95
|
+
const canonicalUserRoot = realpathSync.native(userRoot);
|
|
96
|
+
if (!pathIsInside(canonicalUsersRoot, canonicalWorkspace) ||
|
|
97
|
+
!pathIsInside(canonicalUserRoot, canonicalUsersRoot) ||
|
|
98
|
+
!samePath(path.dirname(canonicalUserRoot), canonicalUsersRoot)) {
|
|
99
|
+
throw new Error("QA user workspace escapes the configured workspace");
|
|
100
|
+
}
|
|
101
|
+
return canonicalUserRoot;
|
|
102
|
+
}
|
|
103
|
+
function record(value) {
|
|
104
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
105
|
+
? value
|
|
106
|
+
: undefined;
|
|
107
|
+
}
|
|
108
|
+
function requestedPaths(execution) {
|
|
109
|
+
const args = record(execution.arguments);
|
|
110
|
+
if (args === undefined)
|
|
111
|
+
return [];
|
|
112
|
+
switch (execution.name) {
|
|
113
|
+
case "read":
|
|
114
|
+
case "read_image":
|
|
115
|
+
case "write":
|
|
116
|
+
case "edit": {
|
|
117
|
+
return typeof args.file_path === "string" ? [args.file_path] : [];
|
|
118
|
+
}
|
|
119
|
+
case "glob":
|
|
120
|
+
case "grep": {
|
|
121
|
+
return [typeof args.path === "string" ? args.path : "."];
|
|
122
|
+
}
|
|
123
|
+
case "str_replace_editor": {
|
|
124
|
+
return typeof args.path === "string" ? [args.path] : [];
|
|
125
|
+
}
|
|
126
|
+
default:
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Whether the named path is only read. Directory-wide tools stay confined even
|
|
132
|
+
* for reads: the attachment store is shared by every account, and only the
|
|
133
|
+
* exact file an upload produced is meant to be reachable.
|
|
134
|
+
*/
|
|
135
|
+
function readsSharedRoot(execution) {
|
|
136
|
+
if (execution.name === "read" ||
|
|
137
|
+
execution.name === "read_image" ||
|
|
138
|
+
execution.name === "glob" ||
|
|
139
|
+
execution.name === "grep") {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
return (execution.name === "str_replace_editor" &&
|
|
143
|
+
record(execution.arguments)?.command === "view");
|
|
144
|
+
}
|
|
145
|
+
function readsSingleFile(execution) {
|
|
146
|
+
if (execution.name === "read" || execution.name === "read_image")
|
|
147
|
+
return true;
|
|
148
|
+
return (execution.name === "str_replace_editor" &&
|
|
149
|
+
record(execution.arguments)?.command === "view");
|
|
150
|
+
}
|
|
151
|
+
function directoryBytes(root, stopAfter) {
|
|
152
|
+
let total = 0;
|
|
153
|
+
const pending = [root];
|
|
154
|
+
while (pending.length > 0 && total <= stopAfter) {
|
|
155
|
+
const current = pending.pop();
|
|
156
|
+
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
157
|
+
const candidate = path.join(current, entry.name);
|
|
158
|
+
if (entry.isSymbolicLink())
|
|
159
|
+
continue;
|
|
160
|
+
if (entry.isDirectory())
|
|
161
|
+
pending.push(candidate);
|
|
162
|
+
else if (entry.isFile())
|
|
163
|
+
total += statSync(candidate).size;
|
|
164
|
+
if (total > stopAfter)
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return total;
|
|
169
|
+
}
|
|
170
|
+
function requestedWriteBytes(execution) {
|
|
171
|
+
const args = record(execution.arguments);
|
|
172
|
+
if (args === undefined)
|
|
173
|
+
return 0;
|
|
174
|
+
const values = [args.content, args.new_string, args.new_str];
|
|
175
|
+
return values.reduce((total, value) => total + (typeof value === "string" ? Buffer.byteLength(value) : 0), 0);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Monotonic guard used in addition to DSH's workspace-write sandbox. It
|
|
179
|
+
* fences model-controlled file paths for both reads and writes, rejects tools
|
|
180
|
+
* whose implementation can walk above cwd, and enforces bounded scratch use.
|
|
181
|
+
*
|
|
182
|
+
* `attachmentRoot` is the one deliberate read exemption: an uploaded file is
|
|
183
|
+
* an immutable content-addressed copy the deployment stores outside every
|
|
184
|
+
* workspace, and the model reaches it exactly as the harness intends (the
|
|
185
|
+
* prompt carries the stored path). Reads of a single such file are allowed;
|
|
186
|
+
* directory-wide tools stay confined because the store is shared by every
|
|
187
|
+
* account, and writes are never exempted.
|
|
188
|
+
*/
|
|
189
|
+
export function qaUserWorkspaceDenial(execution, root, attachmentRoot, readPolicy = {}) {
|
|
190
|
+
const args = record(execution.arguments);
|
|
191
|
+
if (args?.sandbox_permissions !== undefined ||
|
|
192
|
+
(execution.name === "edit" &&
|
|
193
|
+
(args?.replace_all === true || args?.replaceAll === true))) {
|
|
194
|
+
return PATH_DENIAL;
|
|
195
|
+
}
|
|
196
|
+
if (execution.name === "bash" ||
|
|
197
|
+
execution.name === "shell" ||
|
|
198
|
+
execution.name.startsWith("terminal_") ||
|
|
199
|
+
execution.name.startsWith("job_") ||
|
|
200
|
+
execution.name === "run_code" ||
|
|
201
|
+
execution.name === "lsp" ||
|
|
202
|
+
execution.name === "apply_patch") {
|
|
203
|
+
return PATH_DENIAL;
|
|
204
|
+
}
|
|
205
|
+
const readsShared = readsSharedRoot(execution);
|
|
206
|
+
const readsOneFile = readsSingleFile(execution);
|
|
207
|
+
try {
|
|
208
|
+
const sharedReadOnlyRoots = (readPolicy.sharedReadOnlyRoots ?? []).map(canonicalCandidate);
|
|
209
|
+
for (const requested of requestedPaths(execution)) {
|
|
210
|
+
const absolute = path.isAbsolute(requested)
|
|
211
|
+
? requested
|
|
212
|
+
: path.resolve(root, requested);
|
|
213
|
+
const canonical = canonicalCandidate(absolute);
|
|
214
|
+
if (pathIsInside(canonical, root))
|
|
215
|
+
continue;
|
|
216
|
+
if (readsShared &&
|
|
217
|
+
sharedReadOnlyRoots.some((readRoot) => pathIsInside(canonical, readRoot))) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
// Resolved only for a path the fence would otherwise refuse, so the
|
|
221
|
+
// common case pays no extra filesystem walk.
|
|
222
|
+
if (readsOneFile &&
|
|
223
|
+
attachmentRoot !== undefined &&
|
|
224
|
+
pathIsInside(canonical, canonicalCandidate(attachmentRoot))) {
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
return PATH_DENIAL;
|
|
228
|
+
}
|
|
229
|
+
if (execution.name === "write" ||
|
|
230
|
+
execution.name === "edit" ||
|
|
231
|
+
(execution.name === "str_replace_editor" &&
|
|
232
|
+
record(execution.arguments)?.command !== "view")) {
|
|
233
|
+
const requested = requestedWriteBytes(execution);
|
|
234
|
+
if (requested > QA_USER_WORKSPACE_MAX_WRITE_BYTES ||
|
|
235
|
+
directoryBytes(root, QA_USER_WORKSPACE_MAX_BYTES) + requested >
|
|
236
|
+
QA_USER_WORKSPACE_MAX_BYTES) {
|
|
237
|
+
return QUOTA_DENIAL;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
return PATH_DENIAL;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=user-workspace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-workspace.js","sourceRoot":"","sources":["../src/user-workspace.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yadsh/dsh-qa-surface",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "A focused end-user QA surface backed by native DeepSeek Harness sessions",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -53,8 +53,10 @@
|
|
|
53
53
|
"@deepseek-ai/dsh-api-session-controller",
|
|
54
54
|
"@deepseek-ai/dsh-agent-presets",
|
|
55
55
|
"@deepseek-ai/dsh-client-connection",
|
|
56
|
+
"@deepseek-ai/dsh-client-file-upload",
|
|
56
57
|
"@deepseek-ai/dsh-client-ui-layout",
|
|
57
58
|
"@deepseek-ai/dsh-client-ui-settings",
|
|
59
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins",
|
|
58
60
|
"@deepseek-ai/dsh-client-ui-theme"
|
|
59
61
|
],
|
|
60
62
|
"platform": "web"
|
|
@@ -68,9 +70,6 @@
|
|
|
68
70
|
"compatibility.json",
|
|
69
71
|
"capability-policy.json",
|
|
70
72
|
"README.md",
|
|
71
|
-
"CHANGELOG.md",
|
|
72
|
-
"docs/*.md",
|
|
73
|
-
"SPEC-dsh-qa-surface.md",
|
|
74
73
|
"LICENSE"
|
|
75
74
|
],
|
|
76
75
|
"keywords": [
|
|
@@ -78,9 +77,12 @@
|
|
|
78
77
|
"deepseek-harness",
|
|
79
78
|
"dsh",
|
|
80
79
|
"dsh-plugin",
|
|
80
|
+
"cordis",
|
|
81
81
|
"qa",
|
|
82
82
|
"chat",
|
|
83
|
-
"web-ui"
|
|
83
|
+
"web-ui",
|
|
84
|
+
"testing",
|
|
85
|
+
"quality-assurance"
|
|
84
86
|
],
|
|
85
87
|
"license": "MIT",
|
|
86
88
|
"engines": {
|
|
@@ -100,9 +102,11 @@
|
|
|
100
102
|
"@deepseek-ai/dsh-client-ui-layout": ">=0.1.5-rc.2 <0.2.0",
|
|
101
103
|
"@deepseek-ai/dsh-client-ui-renderer": ">=0.1.5-rc.2 <0.2.0",
|
|
102
104
|
"@deepseek-ai/dsh-client-ui-settings": ">=0.1.5-rc.2 <0.2.0",
|
|
105
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins": ">=0.1.5-rc.2 <0.2.0",
|
|
103
106
|
"@deepseek-ai/dsh-client-ui-slots": ">=0.1.5-rc.2 <0.2.0",
|
|
104
107
|
"@deepseek-ai/dsh-client-ui-theme": ">=0.1.5-rc.2 <0.2.0",
|
|
105
108
|
"@deepseek-ai/dsh-host-webserver": ">=0.1.5-rc.2 <0.2.0",
|
|
109
|
+
"@deepseek-ai/dsh-llm": ">=0.1.5-rc.2 <0.2.0",
|
|
106
110
|
"@deepseek-ai/dsh-permission-presets": ">=0.1.5-rc.2 <0.2.0",
|
|
107
111
|
"@deepseek-ai/dsh-session": ">=0.1.5-rc.2 <0.2.0",
|
|
108
112
|
"@deepseek-ai/dsh-settings": ">=0.1.5-rc.2 <0.2.0",
|
|
@@ -115,7 +119,7 @@
|
|
|
115
119
|
},
|
|
116
120
|
"dependencies": {
|
|
117
121
|
"zod": "^4.4.3",
|
|
118
|
-
"@yadsh/dsh-plugin-log": "^0.
|
|
122
|
+
"@yadsh/dsh-plugin-log": "^0.3.0"
|
|
119
123
|
},
|
|
120
124
|
"devDependencies": {
|
|
121
125
|
"@deepseek-ai/cordis": "4.0.2",
|
|
@@ -131,9 +135,11 @@
|
|
|
131
135
|
"@deepseek-ai/dsh-client-ui-layout": "0.1.5-rc.2",
|
|
132
136
|
"@deepseek-ai/dsh-client-ui-renderer": "0.1.5-rc.2",
|
|
133
137
|
"@deepseek-ai/dsh-client-ui-settings": "0.1.5-rc.2",
|
|
138
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins": "0.1.5-rc.2",
|
|
134
139
|
"@deepseek-ai/dsh-client-ui-slots": "0.1.5-rc.2",
|
|
135
140
|
"@deepseek-ai/dsh-client-ui-theme": "0.1.5-rc.2",
|
|
136
141
|
"@deepseek-ai/dsh-host-webserver": "0.1.5-rc.2",
|
|
142
|
+
"@deepseek-ai/dsh-llm": "0.1.5-rc.2",
|
|
137
143
|
"@deepseek-ai/dsh-permission-presets": "0.1.5-rc.2",
|
|
138
144
|
"@deepseek-ai/dsh-session": "0.1.5-rc.2",
|
|
139
145
|
"@deepseek-ai/dsh-settings": "0.1.5-rc.2",
|
|
@@ -155,8 +161,9 @@
|
|
|
155
161
|
"tsdown": "^0.22.14",
|
|
156
162
|
"typescript": "^7.0.2",
|
|
157
163
|
"vitest": "^4.1.11",
|
|
158
|
-
"@yadsh/dsh-
|
|
159
|
-
"@yadsh/dsh-
|
|
164
|
+
"@yadsh/dsh-config": "^0.0.0",
|
|
165
|
+
"@yadsh/dsh-plugin-kit": "^0.1.0",
|
|
166
|
+
"@yadsh/dsh-plugin-scripts": "^0.0.0"
|
|
160
167
|
},
|
|
161
168
|
"publishConfig": {
|
|
162
169
|
"access": "public",
|
package/CHANGELOG.md
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
## 0.3.0 (2026-09-12)
|
|
2
|
-
|
|
3
|
-
### 🚀 Features
|
|
4
|
-
|
|
5
|
-
- Optional QA accounts and entry routing. `accounts.enabled` mounts a ([dc2f582](https://github.com/xarleyn/dsh-plugins/commit/dc2f582))
|
|
6
|
-
full-frame login/registration gate (email + password, coarse audience-safe
|
|
7
|
-
refusals, per-store rate limiting, self-registration toggle) backed by
|
|
8
|
-
`$DSH_HOME/qa-accounts.json`: scrypt password hashes, a persisted HMAC secret
|
|
9
|
-
for stateful-expiry account tokens, and a session ownership map. Ownership is
|
|
10
|
-
first come, first served - attesting or bulk-claiming an unowned session binds
|
|
11
|
-
it to the caller (the migration path for existing per-browser chats on first
|
|
12
|
-
login); sessions owned by another user refuse attestation with
|
|
13
|
-
`session-owned-elsewhere` and stay hidden from the sidebar, admins are not
|
|
14
|
-
refused. The identity rides as an explicit token argument into the gated
|
|
15
|
-
`qaSurface` remotes (the typert carrier never exposes HTTP requests), the
|
|
16
|
-
policy admission checks it before any session fact is revealed
|
|
17
|
-
(`auth-required` reopens the gate on expiry), and the account chip with
|
|
18
|
-
logout lives in the sidebar footer. `entry.redirectNonLoopback` injects a
|
|
19
|
-
guarded head script through the `webserver/index-inject` event that continues
|
|
20
|
-
non-loopback hostnames into the QA route - the navigation-marker hand-off is
|
|
21
|
-
never redirected (no loops), `/?ui=admin` persists an operator bypass and
|
|
22
|
-
`/?ui=qa` clears it. Accounts are an identity layer for the QA surface, not a
|
|
23
|
-
harness boundary: QA users still hold the shared host launch-token cookie.
|
|
24
|
-
|
|
25
|
-
Follow-ups adopted from a review of the independent dsh-auth-gate plugin: a
|
|
26
|
-
proxy-side deny list for the privileged config-plane RPC methods behind the
|
|
27
|
-
deploy proxy's Host/Origin rewrite, a plugin-side launch-token bridge
|
|
28
|
-
(`entry.cookieBootstrap`) that performs the one-time host-cookie exchange on
|
|
29
|
-
the `/qa` route itself, a `qa-accounts` bin CLI (list/add/set-role/disable/
|
|
30
|
-
enable/revoke) so account administration never requires hand-editing the
|
|
31
|
-
JSON file, and per-account state — a `disabled` flag refusing logins with
|
|
32
|
-
`account-disabled` plus a `tokenVersion` burned into tokens that
|
|
33
|
-
`disable`/`revoke` bump, making logout and lockout server-side facts.
|
|
34
|
-
|
|
35
|
-
Admins get cross-user views over the same ownership map:
|
|
36
|
-
`qaSurface/accountsListOwnership` (admin-only, `admin-required` refusal
|
|
37
|
-
otherwise) returns every chat with its owner's resolved display name, the
|
|
38
|
-
admin sidebar switches to per-owner sections ordered by their freshest chat
|
|
39
|
-
(unclaimed chats trail under "Без владельца"), and user messages in foreign
|
|
40
|
-
chats carry an `author` byline naming the chat owner. Ordinary accounts and
|
|
41
|
-
deployments with accounts disabled keep the flat sidebar and unlabeled
|
|
42
|
-
messages.
|
|
43
|
-
|
|
44
|
-
- The chat-history sidebar gained a footer version button that opens an ([d9d5868](https://github.com/xarleyn/dsh-plugins/commit/d9d5868))
|
|
45
|
-
end-user changelog dialog: a curated per-version summary (new features and
|
|
46
|
-
fixes in Russian) rendered in a themed modal with Escape/backdrop close.
|
|
47
|
-
The bundled version and entries are pinned to package.json and the release
|
|
48
|
-
CHANGELOG by a unit test, so a release cannot ship a stale dialog.
|
|
49
|
-
|
|
50
|
-
- Rebuild the client on the 0.1.5 surfaces: the transcript projects from the ([458b9c2](https://github.com/xarleyn/dsh-plugins/commit/458b9c2))
|
|
51
|
-
ui-chat conversation view's legacy slice, chat/model pinning moves to the
|
|
52
|
-
wire remotes (`agentPresets.select` on the still-blank session, then
|
|
53
|
-
`session.selectModel`) with the attestation ordering preserved, and
|
|
54
|
-
history reads go through the session-v3 surface. The supported host range
|
|
55
|
-
moves to `>=0.1.5-rc.2 <0.2.0`, dropping 0.1.1-rc.2.
|
|
56
|
-
|
|
57
|
-
- Harden the gated QA experience and make cross-user history explicitly ([2ed2024](https://github.com/xarleyn/dsh-plugins/commit/2ed2024))
|
|
58
|
-
opt-in. A new `accounts.showOtherUsersChats` setting defaults to `false`, so
|
|
59
|
-
administrators only see their own chats unless the deployment enables the
|
|
60
|
-
shared ownership view. Account storage now follows external CLI updates and
|
|
61
|
-
uses process-scoped temporary writes, while session admission and client state
|
|
62
|
-
handling avoid stale async results and reset session-bound assets reliably.
|
|
63
|
-
|
|
64
|
-
The QA client now presents a dedicated test-interface disclosure, improves
|
|
65
|
-
chat search and owner matching, keeps row actions from disturbing result
|
|
66
|
-
layout, distinguishes administrator roles, and removes decorative middle-dot
|
|
67
|
-
separators from the sidebar, messages, and source details. Its curated 0.3.0
|
|
68
|
-
history entry is prepared in advance, while the current-version marker is
|
|
69
|
-
injected from package.json at build time so the release bump promotes it
|
|
70
|
-
without another source edit.
|
|
71
|
-
|
|
72
|
-
### ❤️ Thank You
|
|
73
|
-
|
|
74
|
-
- xarleyn @xarleyn
|
|
75
|
-
|
|
76
|
-
## 0.2.0 (2026-09-10)
|
|
77
|
-
|
|
78
|
-
- Added DSH-style symmetric transcript/composer width handles with adaptive
|
|
79
|
-
defaults, viewport clamping, and per-route browser persistence.
|
|
80
|
-
- Added Host-owned structured source provenance for parent and delegated turns,
|
|
81
|
-
replayable `qa/sources` snapshots, dedupe/ranking, opaque-provider reporting,
|
|
82
|
-
grouped source UI, and safe rendered/raw file previews.
|
|
83
|
-
- Updated packed Host/browser smoke coverage for browser authentication,
|
|
84
|
-
revisioned client batches, scoped Remote injection, and current Typert RPC
|
|
85
|
-
envelopes.
|
|
86
|
-
|
|
87
|
-
### 🚀 Features
|
|
88
|
-
|
|
89
|
-
- Make the QA surface a complete end-user assistant shell: image attachments ([b9af082](https://github.com/xarleyn/dsh-plugins/commit/b9af082))
|
|
90
|
-
(drag & drop, paste, and a picker with removable previews; base64 prompt
|
|
91
|
-
parts the Host promotes to durable attachments, rendered back as clickable
|
|
92
|
-
thumbnails), subagent delegation presentation (launch work items with the
|
|
93
|
-
durable child id, settlement notices collapsed into titled expandable rows,
|
|
94
|
-
an agents panel listing the chat's subagents, and live read-only subagent
|
|
95
|
-
transcripts with a one-click return), source cards with a full-output detail
|
|
96
|
-
pane, clickable links and an open action, answer regeneration with
|
|
97
|
-
ChatGPT-style variant switching, message ratings with hover response
|
|
98
|
-
metadata (duration, TTFT, tokens per second), a data-usage disclaimer
|
|
99
|
-
plate under the composer, per-browser chat history
|
|
100
|
-
ordered by host updates with search and a collapsible sidebar, lazy draft
|
|
101
|
-
chats that create nothing until the first prompt, directory/workspace
|
|
102
|
-
pinning with a dedicated `workspace-unavailable` refusal, the company
|
|
103
|
-
interaction palette as `--dsh-qa-*` tokens, and a split of the surface into
|
|
104
|
-
focused drawer, switcher, and formatting modules.
|
|
105
|
-
|
|
106
|
-
### 🩹 Fixes
|
|
107
|
-
|
|
108
|
-
- Pack the generated Typert host and remote-client entrypoints: the `files` ([0059cb4](https://github.com/xarleyn/dsh-plugins/commit/0059cb4))
|
|
109
|
-
allowlist only kept declarations under `lib/types/`, so the `./remote` and
|
|
110
|
-
`./typert` exports previously shipped without their implementation modules
|
|
111
|
-
and type definitions.
|
|
112
|
-
|
|
113
|
-
### ❤️ Thank You
|
|
114
|
-
|
|
115
|
-
- xarleyn @xarleyn
|
|
116
|
-
|
|
117
|
-
## 0.1.1 (2026-09-06)
|
|
118
|
-
|
|
119
|
-
### 🩹 Fixes
|
|
120
|
-
|
|
121
|
-
- Add the dedicated browser QA surface backed by native DeepSeek Harness ([dfdc430](https://github.com/xarleyn/dsh-plugins/commit/dfdc430))
|
|
122
|
-
sessions.
|
|
123
|
-
|
|
124
|
-
### 🧱 Updated Dependencies
|
|
125
|
-
|
|
126
|
-
- Updated @yadsh/dsh-plugin-log to 0.2.1
|
|
127
|
-
|
|
128
|
-
### ❤️ Thank You
|
|
129
|
-
|
|
130
|
-
- xarleyn @xarleyn
|
|
131
|
-
|
|
132
|
-
# Changelog
|
|
133
|
-
|
|
134
|
-
## 0.1.0 - 2026-09-05
|
|
135
|
-
|
|
136
|
-
- Added the `/qa` full-screen overlay backed by native DSH sessions.
|
|
137
|
-
- Added persistent, new-on-load and fixed session policies.
|
|
138
|
-
- Added streaming transcript, Stop, New chat, safe Markdown and responsive UI.
|
|
139
|
-
- Added Host settings registration and safety filtering for internal events.
|
|
140
|
-
- Added a narrow Host navigation redirect for DSH releases whose static
|
|
141
|
-
frontend returns 404 for direct `/qa` requests.
|
|
142
|
-
- Added fail-closed Host policy attestation, the `qa-read-only` permission pin,
|
|
143
|
-
an inherited-tool allow-list, and a monotonic execution guard.
|
|
144
|
-
- Disabled session reset by default and added capability-regression gates.
|
|
145
|
-
- Refined the QA surface with first-party-style conversation chrome, plain
|
|
146
|
-
assistant flow, user bubbles, copy actions and a floating two-row composer.
|
|
147
|
-
- Added opt-in reasoning and tool-call details grouped into a live turn work
|
|
148
|
-
disclosure that collapses to a `Worked for …` summary before the final answer.
|
|
149
|
-
- Added an optional minimal chat-history sidebar (`ui.showSessionList`) with a
|
|
150
|
-
per-browser localStorage chat index, attested switching and a gated
|
|
151
|
-
New chat control.
|
|
152
|
-
- Send-time policy attestation now survives a Host that idled the session's
|
|
153
|
-
agent out: the client re-binds the session (re-materializing the agent) and,
|
|
154
|
-
when the refused session is still blank, continues in a fresh attested
|
|
155
|
-
session instead of surfacing an error.
|
|
156
|
-
- Added GFM table, ordered-list and horizontal-rule rendering to the safe
|
|
157
|
-
Markdown output.
|
|
158
|
-
- Added a two-click chat delete control to the chat-history sidebar; it
|
|
159
|
-
removes the chat from the per-browser index only (DSH has no
|
|
160
|
-
session-deletion seam).
|
|
161
|
-
- Added attestation diagnostics: the Host folds a coarse reason code into the
|
|
162
|
-
refusal and the browser console prints one operator hint instead of a
|
|
163
|
-
duplicate stack trace.
|
|
164
|
-
- Fixed repeated policy attestation for tools contributed by an agent preset:
|
|
165
|
-
the applied restriction now retains the exact scoped allow-list instead of
|
|
166
|
-
accidentally reducing it to global-only tools. Removed the redundant native
|
|
167
|
-
presentation override so reloads cannot collide with the preset's mode.
|
|
168
|
-
- The browser re-reads the Host configuration when the connection is restored,
|
|
169
|
-
so an open page survives a Host restart with a changed deployment config.
|
|
170
|
-
- Added LAN serving support: a shipped `deploy/qa-lan.patch.yml` webserver
|
|
171
|
-
overlay, and a `qaSurface/describe` Host Remote the browser falls back to
|
|
172
|
-
when the loopback-pinned settings namespace is unavailable, so branding,
|
|
173
|
-
session pinning and lockdown UI switches keep working over the network.
|
|
174
|
-
- Localized the end-user chat interface into Russian, added rotating playful
|
|
175
|
-
thinking phrases, and moved the default quick-question chips next to the
|
|
176
|
-
composer on an empty chat.
|