@yadsh/dsh-qa-surface 0.18.0 → 0.20.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 +201 -22
- package/lib/access/model-choices.js +103 -0
- package/lib/access/model-choices.js.map +1 -0
- package/lib/access/model-policy.js +79 -7
- package/lib/access/model-policy.js.map +1 -1
- package/lib/access/model.js +29 -1
- package/lib/access/model.js.map +1 -1
- package/lib/access/queue-policy.js +36 -0
- package/lib/access/queue-policy.js.map +1 -0
- package/lib/access/service.js +124 -7
- package/lib/access/service.js.map +1 -1
- package/lib/account-remotes.js +10 -1
- package/lib/account-remotes.js.map +1 -1
- package/lib/accounts/database.js +44 -3
- package/lib/accounts/database.js.map +1 -1
- package/lib/accounts/store.js +28 -0
- package/lib/accounts/store.js.map +1 -1
- package/lib/admin/service.js +6 -1
- package/lib/admin/service.js.map +1 -1
- package/lib/attachment-access.js +147 -0
- package/lib/attachment-access.js.map +1 -0
- package/lib/chat-sharing/search.js +89 -0
- package/lib/chat-sharing/search.js.map +1 -0
- package/lib/chat-sharing/transport.js +8 -9
- package/lib/chat-sharing/transport.js.map +1 -1
- package/lib/client/QaAccountsController.js +43 -8
- package/lib/client/QaAccountsController.js.map +1 -1
- package/lib/client/QaSessionController.js +106 -32
- package/lib/client/QaSessionController.js.map +1 -1
- package/lib/client/QaSurface.js +64 -5
- package/lib/client/QaSurface.js.map +1 -1
- package/lib/client/admin/ModelPolicyEditor.js +28 -2
- package/lib/client/admin/ModelPolicyEditor.js.map +1 -1
- package/lib/client/admin/RoleEditor.js +18 -1
- package/lib/client/admin/RoleEditor.js.map +1 -1
- package/lib/client/admin/pages/SkillFiles.js +1 -1
- package/lib/client/admin/pages/SkillFiles.js.map +1 -1
- package/lib/client/admin/pages/Users.js +36 -16
- package/lib/client/admin/pages/Users.js.map +1 -1
- package/lib/client/admin/styles.js +3 -0
- package/lib/client/admin/styles.js.map +1 -1
- package/lib/client/code-source-styles.js +12 -0
- package/lib/client/code-source-styles.js.map +1 -0
- package/lib/client/components/QaChangelog.js +96 -0
- package/lib/client/components/QaChangelog.js.map +1 -1
- package/lib/client/components/QaChatActions.js +36 -14
- package/lib/client/components/QaChatActions.js.map +1 -1
- package/lib/client/components/QaCodeSourcePicker.js +18 -0
- package/lib/client/components/QaCodeSourcePicker.js.map +1 -0
- package/lib/client/components/QaComposer.js +11 -12
- package/lib/client/components/QaComposer.js.map +1 -1
- package/lib/client/components/QaComposerSettings.js +202 -0
- package/lib/client/components/QaComposerSettings.js.map +1 -0
- package/lib/client/components/QaFormItem.js +74 -38
- package/lib/client/components/QaFormItem.js.map +1 -1
- package/lib/client/components/QaHistoryCatalog.js +261 -0
- package/lib/client/components/QaHistoryCatalog.js.map +1 -0
- package/lib/client/components/QaInputForm.js +115 -32
- package/lib/client/components/QaInputForm.js.map +1 -1
- package/lib/client/components/QaModelSelector.js +21 -0
- package/lib/client/components/QaModelSelector.js.map +1 -0
- package/lib/client/components/QaRequestWait.js +15 -0
- package/lib/client/components/QaRequestWait.js.map +1 -0
- package/lib/client/components/QaSidebar.js +25 -24
- package/lib/client/components/QaSidebar.js.map +1 -1
- package/lib/client/composer-settings-styles.js +21 -0
- package/lib/client/composer-settings-styles.js.map +1 -0
- package/lib/client/forms-styles.js +35 -11
- package/lib/client/forms-styles.js.map +1 -1
- package/lib/client/history-organization-styles.js +31 -0
- package/lib/client/history-organization-styles.js.map +1 -0
- package/lib/client/history-organization.js +155 -0
- package/lib/client/history-organization.js.map +1 -0
- package/lib/client/index.js +13 -2
- package/lib/client/index.js.map +1 -1
- package/lib/client/project-session-state.js.map +1 -1
- package/lib/client/request-wait-styles.js +7 -0
- package/lib/client/request-wait-styles.js.map +1 -0
- package/lib/client/settings/sections/attachments.js +7 -1
- package/lib/client/settings/sections/attachments.js.map +1 -1
- package/lib/client/settings/sections/session.js +1 -1
- package/lib/client/settings/sections/session.js.map +1 -1
- package/lib/client/styles.js +20 -1
- package/lib/client/styles.js.map +1 -1
- package/lib/client/types.js.map +1 -1
- package/lib/client/use-chat-code-source.js +156 -0
- package/lib/client/use-chat-code-source.js.map +1 -0
- package/lib/client/use-history-organization.js +48 -0
- package/lib/client/use-history-organization.js.map +1 -0
- package/lib/client/use-model-selection.js +96 -0
- package/lib/client/use-model-selection.js.map +1 -0
- package/lib/client/use-qa-file-drop.js +81 -0
- package/lib/client/use-qa-file-drop.js.map +1 -0
- package/lib/client/user-settings/IntegrationTokensPage.js +42 -6
- package/lib/client/user-settings/IntegrationTokensPage.js.map +1 -1
- package/lib/client/user-settings/UserSettingsDialog.js +1 -1
- package/lib/client/user-settings/UserSettingsDialog.js.map +1 -1
- package/lib/client.js +131 -27
- package/lib/client.js.map +1 -1
- package/lib/code-source-selection.js +102 -0
- package/lib/code-source-selection.js.map +1 -0
- package/lib/config-resolvers/accounts.js +26 -1
- package/lib/config-resolvers/accounts.js.map +1 -1
- package/lib/config-resolvers/defaults.js +5 -0
- package/lib/config-resolvers/defaults.js.map +1 -1
- package/lib/config-resolvers/integration.js +8 -1
- package/lib/config-resolvers/integration.js.map +1 -1
- package/lib/config-resolvers/session.js +2 -0
- package/lib/config-resolvers/session.js.map +1 -1
- package/lib/config.js +51 -3
- package/lib/config.js.map +1 -1
- package/lib/enforcement/skill-policy.js +37 -2
- package/lib/enforcement/skill-policy.js.map +1 -1
- package/lib/filesystem-intent.js +39 -0
- package/lib/filesystem-intent.js.map +1 -0
- package/lib/forms/attachments.js +18 -2
- package/lib/forms/attachments.js.map +1 -1
- package/lib/forms/errors.js +6 -1
- package/lib/forms/errors.js.map +1 -1
- package/lib/forms/gate.js +28 -2
- package/lib/forms/gate.js.map +1 -1
- package/lib/forms/schema-errors.js +47 -0
- package/lib/forms/schema-errors.js.map +1 -0
- package/lib/forms/schema.js +29 -20
- package/lib/forms/schema.js.map +1 -1
- package/lib/index.js +189 -84
- package/lib/index.js.map +1 -1
- package/lib/integration/http.js +9 -10
- package/lib/integration/http.js.map +1 -1
- package/lib/integration/service.js +9 -0
- package/lib/integration/service.js.map +1 -1
- package/lib/personal-skills/provider.js +9 -1
- package/lib/personal-skills/provider.js.map +1 -1
- package/lib/personal-skills/service.js +4 -0
- package/lib/personal-skills/service.js.map +1 -1
- package/lib/personal-skills/shared-seed.js +65 -0
- package/lib/personal-skills/shared-seed.js.map +1 -0
- package/lib/prompt-notes.js +19 -1
- package/lib/prompt-notes.js.map +1 -1
- package/lib/provenance/collector.js +1 -0
- package/lib/provenance/collector.js.map +1 -1
- package/lib/provenance/extractors.js +6 -1
- package/lib/provenance/extractors.js.map +1 -1
- package/lib/qa-tools/catalog.js +18 -1
- package/lib/qa-tools/catalog.js.map +1 -1
- package/lib/qa-tools/code-files.js +207 -0
- package/lib/qa-tools/code-files.js.map +1 -0
- package/lib/qa-tools/code-tools.js +158 -0
- package/lib/qa-tools/code-tools.js.map +1 -0
- package/lib/qa-tools/docs-tools.js +49 -12
- package/lib/qa-tools/docs-tools.js.map +1 -1
- package/lib/qa-tools/index.js +4 -0
- package/lib/qa-tools/index.js.map +1 -1
- package/lib/qa-tools/request-user-form.js +27 -7
- package/lib/qa-tools/request-user-form.js.map +1 -1
- package/lib/request-scheduler.js +262 -0
- package/lib/request-scheduler.js.map +1 -0
- package/lib/resolve-config.js +9 -1
- package/lib/resolve-config.js.map +1 -1
- package/lib/secure-session.js +62 -56
- package/lib/secure-session.js.map +1 -1
- package/lib/session-create.js +125 -0
- package/lib/session-create.js.map +1 -0
- package/lib/session-model.js +43 -1
- package/lib/session-model.js.map +1 -1
- package/lib/shared/history-folder.js +18 -0
- package/lib/shared/history-folder.js.map +1 -0
- package/lib/typert.host.js +793 -159
- package/lib/typert.remote-client.d.ts +19 -7
- package/lib/typert.remote-client.js +726 -148
- package/lib/types/access/model-choices.d.ts +16 -0
- package/lib/types/access/model-policy.d.ts +13 -7
- package/lib/types/access/model.d.ts +9 -0
- package/lib/types/access/queue-policy.d.ts +30 -0
- package/lib/types/access/service.d.ts +27 -0
- package/lib/types/account-remotes.d.ts +5 -4
- package/lib/types/accounts/database.d.ts +5 -0
- package/lib/types/accounts/store.d.ts +6 -0
- package/lib/types/attachment-access.d.ts +29 -0
- package/lib/types/chat-sharing/search.d.ts +5 -0
- package/lib/types/chat-sharing/transport.d.ts +1 -1
- package/lib/types/client/QaAccountsController.d.ts +3 -1
- package/lib/types/client/QaSessionController.d.ts +31 -14
- package/lib/types/client/QaSurface.d.ts +2 -0
- package/lib/types/client/admin/ModelPolicyEditor.d.ts +2 -0
- package/lib/types/client/code-source-styles.d.ts +2 -0
- package/lib/types/client/components/QaChatActions.d.ts +3 -1
- package/lib/types/client/components/QaCodeSourcePicker.d.ts +9 -0
- package/lib/types/client/components/QaComposer.d.ts +2 -0
- package/lib/types/client/components/QaComposerSettings.d.ts +16 -0
- package/lib/types/client/components/QaFormItem.d.ts +4 -2
- package/lib/types/client/components/QaHistoryCatalog.d.ts +19 -0
- package/lib/types/client/components/QaInputForm.d.ts +3 -2
- package/lib/types/client/components/QaModelSelector.d.ts +4 -0
- package/lib/types/client/components/QaRequestWait.d.ts +6 -0
- package/lib/types/client/components/QaSidebar.d.ts +3 -0
- package/lib/types/client/composer-settings-styles.d.ts +2 -0
- package/lib/types/client/forms-styles.d.ts +1 -1
- package/lib/types/client/history-organization-styles.d.ts +3 -0
- package/lib/types/client/history-organization.d.ts +38 -0
- package/lib/types/client/project-session-state.d.ts +2 -4
- package/lib/types/client/request-wait-styles.d.ts +2 -0
- package/lib/types/client/types.d.ts +15 -7
- package/lib/types/client/use-chat-code-source.d.ts +20 -0
- package/lib/types/client/use-history-organization.d.ts +8 -0
- package/lib/types/client/use-model-selection.d.ts +24 -0
- package/lib/types/client/use-qa-file-drop.d.ts +12 -0
- package/lib/types/code-source-selection.d.ts +28 -0
- package/lib/types/code-sources.js +2 -0
- package/lib/types/code-sources.js.map +1 -0
- package/lib/types/config-resolvers/integration.d.ts +7 -1
- package/lib/types/filesystem-intent.d.ts +12 -0
- package/lib/types/forms/attachments.d.ts +5 -2
- package/lib/types/forms/errors.d.ts +3 -2
- package/lib/types/forms/gate.d.ts +4 -2
- package/lib/types/forms/schema-errors.d.ts +7 -0
- package/lib/types/forms/types.d.ts +14 -0
- package/lib/types/index.d.ts +41 -15
- package/lib/types/integration/http.d.ts +1 -3
- package/lib/types/integration/service.d.ts +2 -0
- package/lib/types/personal-skills/shared-seed.d.ts +12 -0
- package/lib/types/prompt-notes.d.ts +5 -1
- package/lib/types/qa-tools/catalog.d.ts +4 -3
- package/lib/types/qa-tools/code-files.d.ts +59 -0
- package/lib/types/qa-tools/code-tools.d.ts +7 -0
- package/lib/types/qa-tools/docs-tools.d.ts +4 -0
- package/lib/types/qa-tools/index.d.ts +3 -1
- package/lib/types/qa-tools/request-user-form.d.ts +1 -1
- package/lib/types/request-scheduler.d.ts +47 -0
- package/lib/types/secure-session.d.ts +12 -26
- package/lib/types/session-create.d.ts +29 -0
- package/lib/types/session-model.d.ts +11 -2
- package/lib/types/shared/history-folder.d.ts +4 -0
- package/lib/types/types/accounts.d.ts +11 -0
- package/lib/types/types/capability.d.ts +31 -4
- package/lib/types/types/code-sources.d.ts +12 -0
- package/lib/types/types/config.d.ts +20 -0
- package/lib/types/types/session.d.ts +8 -0
- package/lib/types/types.d.ts +1 -0
- package/lib/types/user-workspace.d.ts +4 -6
- package/lib/user-workspace.js +20 -71
- package/lib/user-workspace.js.map +1 -1
- package/package.json +13 -10
package/README.md
CHANGED
|
@@ -23,10 +23,74 @@ Session and Agent Loop.
|
|
|
23
23
|
|
|
24
24
|
## What it does
|
|
25
25
|
|
|
26
|
+
Chat history supports virtual folders and a separate pinned section. Create,
|
|
27
|
+
rename, collapse and delete folders; drag chats into folders, out to the root,
|
|
28
|
+
or into the pinned section. Drag rows or folder headers to arrange them, or use
|
|
29
|
+
the chat menu and folder settings with a keyboard. Unpinning returns a chat to
|
|
30
|
+
its previous folder. Moving a pinned chat into a folder unpins it. Deleting a
|
|
31
|
+
folder leaves its chats in history, preserving any pins. Search includes folder
|
|
32
|
+
names and reveals matching chats inside collapsed folders.
|
|
33
|
+
|
|
34
|
+
This organization is browser-local and scoped to the QA route and account ID.
|
|
35
|
+
It survives reloads and updates other tabs on the same origin through storage
|
|
36
|
+
events; it does not synchronize between devices. Folder membership and pins
|
|
37
|
+
change only presentation metadata: session IDs, workspace roots, physical files,
|
|
38
|
+
ownership and access remain as they were.
|
|
39
|
+
|
|
40
|
+
Under **Integration tokens**, choose **Folder for new chats** when creating a
|
|
41
|
+
credential, or change the destination on an existing active token without
|
|
42
|
+
reissuing it. This default is stored on the server as a virtual folder ID and
|
|
43
|
+
display name. Each newly created API conversation snapshots it before its first
|
|
44
|
+
answer, even when no browser is open. Authenticated history imports that folder
|
|
45
|
+
and assignment once; manual moves and folder deletions are preserved. Changing
|
|
46
|
+
or clearing a token's default affects future conversations, not continuations
|
|
47
|
+
of an existing `session_id`. The default does not change workspace paths or
|
|
48
|
+
grant access to another account's chats. The folder picker lists folders from
|
|
49
|
+
this browser's history and retains an existing server selection from another
|
|
50
|
+
browser. New API chats can recreate a selected folder that was deleted locally;
|
|
51
|
+
clear or change the token default to stop using that destination.
|
|
52
|
+
|
|
26
53
|
Corporate sign-in through Keycloak or another OpenID Connect provider is opt-in.
|
|
27
54
|
The source tree includes `plugins/dsh-qa-surface/docs/oidc.md` with client settings,
|
|
28
55
|
account linking, role admission, session limits and deployment acceptance.
|
|
29
56
|
|
|
57
|
+
The composer has one compact **Chat settings** menu beside the attachment
|
|
58
|
+
button. Its summary shows the current model and code version. The **Model**
|
|
59
|
+
and **Code version** entries open nested choice lists with a checkmark for the
|
|
60
|
+
current value; Back, Escape and arrow keys navigate the levels. The menu closes
|
|
61
|
+
after a successful choice and stays open with an error if saving fails. Model
|
|
62
|
+
choices appear when the role or account permits alternatives; code choices
|
|
63
|
+
appear when a code-source catalog is configured.
|
|
64
|
+
|
|
65
|
+
Choosing a model applies to the current chat and
|
|
66
|
+
survives reopening it; new chats keep the configured default. Switching waits
|
|
67
|
+
until the chat has finished its response and has no queued request. Readers,
|
|
68
|
+
participants and delegated-agent views cannot change the owner's model.
|
|
69
|
+
|
|
70
|
+
Administrators set the default and **Alternative models for the user** in the
|
|
71
|
+
role's Model tab or the account's model settings. `model` keeps its existing
|
|
72
|
+
priority: account, role, deployment. The optional `allowedModels` array adds
|
|
73
|
+
provider/model pairs to that default. An omitted account array inherits its
|
|
74
|
+
role's alternatives; `[]` explicitly restricts that account to its default.
|
|
75
|
+
An omitted role array grants no alternatives. Saved policies keep their
|
|
76
|
+
previous single-model behavior until an administrator grants alternatives.
|
|
77
|
+
The server filters the catalog and validates every selection against the
|
|
78
|
+
owner's current policy, including model replacement rules.
|
|
79
|
+
|
|
80
|
+
Chat-local selection uses the Session controller's guarded selection commit
|
|
81
|
+
capability, which records the native `model/selection` event and updates the
|
|
82
|
+
next-request selection without saving the Host operator's global default.
|
|
83
|
+
A Host without that capability rejects the switch rather than changing a
|
|
84
|
+
deployment-wide preference. The rest of the QA surface remains available.
|
|
85
|
+
|
|
86
|
+
The focused packed browser check is `pnpm --filter @yadsh/dsh-qa-surface
|
|
87
|
+
e2e:browser --model-selection`. Set `DSH_QA_SMOKE_VERSION` to the Host version
|
|
88
|
+
being checked, and optionally `DSH_QA_HISTORY_EVIDENCE_DIR` to a local evidence
|
|
89
|
+
directory. This pass exercises synthetic model routes without real provider
|
|
90
|
+
credentials, including access filtering, switching, reload persistence,
|
|
91
|
+
new-chat defaults, and desktop/phone layout. It does not run the separate LAN
|
|
92
|
+
deployment-overlay acceptance pass.
|
|
93
|
+
|
|
30
94
|
- contributes one root-scoped entry to the additive `shell.overlay` slot;
|
|
31
95
|
- stays invisible outside the configured route;
|
|
32
96
|
- creates or restores one real DSH Session;
|
|
@@ -423,6 +487,83 @@ dynamically attached tool gets the same scrutiny as an allow-listed one. Tool
|
|
|
423
487
|
visibility is not an authorization boundary — a QA tool that writes must still
|
|
424
488
|
enforce its own permissions.
|
|
425
489
|
|
|
490
|
+
### Multiple code sources
|
|
491
|
+
|
|
492
|
+
`codeSources` defines named code trees independently of folder and branch
|
|
493
|
+
names. Configure it on the QA Surface row; preset-mounted Git tools read
|
|
494
|
+
this same catalogue unless their own non-empty catalogue overrides it.
|
|
495
|
+
|
|
496
|
+
```yaml
|
|
497
|
+
codeSources:
|
|
498
|
+
defaultSource: stable
|
|
499
|
+
entries:
|
|
500
|
+
- id: stable
|
|
501
|
+
label: "Stable release"
|
|
502
|
+
path: /srv/code/release-a
|
|
503
|
+
docsVersion: "2.0"
|
|
504
|
+
- id: preview
|
|
505
|
+
label: "Development"
|
|
506
|
+
path: /srv/code/work-in-progress
|
|
507
|
+
docsVersion: "2.0"
|
|
508
|
+
lockdown:
|
|
509
|
+
sharedReadOnlyRoots:
|
|
510
|
+
- /srv/code
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
Grant Git access separately with `repositoryRoots: [/srv/code]` on the
|
|
514
|
+
Git plugin row mounted in the agent preset. Adding a catalogue entry does
|
|
515
|
+
not grant either filesystem or Git access. The chat's writable scratch
|
|
516
|
+
directory stays separate from the read-only code trees.
|
|
517
|
+
|
|
518
|
+
After QA tool activation, `code_sources {}` reports the catalogue, its default
|
|
519
|
+
and each tree's read availability. `code_read` and `code_search` accept
|
|
520
|
+
`source: stable` or `source: preview` and source-relative `path` values.
|
|
521
|
+
Omitting `source` uses `defaultSource`, or the only entry when there is one;
|
|
522
|
+
several entries without a default require an explicit choice. A missing,
|
|
523
|
+
unreadable or unknown source fails without falling back to another tree.
|
|
524
|
+
Use the same id in `dsh_git_*` calls. Generic `read`, `glob` and `grep` still
|
|
525
|
+
use explicit paths; use the source-aware tools for version-scoped work.
|
|
526
|
+
|
|
527
|
+
Search is literal and case-insensitive, bounded to 1,000 attempted text
|
|
528
|
+
files, 10,000 visited entries, 8 MiB of input, three seconds and 32 KiB of
|
|
529
|
+
results. It skips links, binary/oversized files and common generated
|
|
530
|
+
directories (`.git`, `node_modules`, `.nx`, `.cache`, `bin`, `obj`, `dist`,
|
|
531
|
+
`lib`). `truncated` means incomplete: narrow `path` or query before
|
|
532
|
+
concluding code is absent. Reading accepts UTF-8 files up to 1 MiB and
|
|
533
|
+
returns at most 400 lines and 32 KiB. Both tools refuse traversal and nested
|
|
534
|
+
symbolic links. Results carry the source id, label and root; file provenance
|
|
535
|
+
preserves the source id and actual file address.
|
|
536
|
+
|
|
537
|
+
`docs_search` accepts `source` and uses its optional `docsVersion` mapping.
|
|
538
|
+
When neither `source`, `version` nor `path` is given, the default source's
|
|
539
|
+
mapping takes precedence over `tools.docsDefaultVersion`. Without a mapping,
|
|
540
|
+
the existing documentation default stays in force. An explicit `version`
|
|
541
|
+
or `path` without `source` retains its override behaviour. An explicit
|
|
542
|
+
`source` without a mapping is refused, as is a conflicting explicit edition.
|
|
543
|
+
`docs_read` validates the file's edition against the chat's selected source
|
|
544
|
+
unless a different source is explicitly named. Release and development sources
|
|
545
|
+
may share a documentation edition.
|
|
546
|
+
|
|
547
|
+
The **Версия кода** entry in the composer's shared settings menu saves an explicit source for
|
|
548
|
+
the chat on the server. Reopening the chat or using another browser restores
|
|
549
|
+
that choice. **По умолчанию** clears the override and follows the deployment
|
|
550
|
+
default. Draft selection creates no session: it is saved before the first
|
|
551
|
+
prompt, and a failed save prevents the prompt and can be retried.
|
|
552
|
+
|
|
553
|
+
Only the chat owner can change the source. Shared readers and collaborators
|
|
554
|
+
see the same choice, and changes are refused while the chat or its subagents
|
|
555
|
+
are running, while input is queued, or while the chat waits for a server slot.
|
|
556
|
+
The plugin stores preferences in `qa-code-sources.db` beside the accounts DB;
|
|
557
|
+
include that database in deployment backups. New delegated children and forks
|
|
558
|
+
snapshot the parent's effective source. Descendants keep that snapshot if the
|
|
559
|
+
parent later changes its choice; an ordinary fork can change its own source.
|
|
560
|
+
|
|
561
|
+
An omitted `source` in Git, code or documentation tools uses the persisted
|
|
562
|
+
session choice. Explicit tool parameters still allow comparisons with another
|
|
563
|
+
configured source and do not change the saved choice. Removing a selected
|
|
564
|
+
source from configuration makes it unavailable rather than selecting another
|
|
565
|
+
version. No directory is created, moved or updated by these tools.
|
|
566
|
+
|
|
426
567
|
### Writable per-user research space
|
|
427
568
|
|
|
428
569
|
Set `accounts.perUserWorkspace: true` only together with accounts, a registered
|
|
@@ -519,6 +660,34 @@ then collapses to `Worked for ...` before the final answer. Tool capability is
|
|
|
519
660
|
still controlled exclusively by `lockdown.toolPolicy.allow`; the display flags
|
|
520
661
|
do not grant tools.
|
|
521
662
|
|
|
663
|
+
When a provider retires a model identifier, `session.modelReplacements` defines
|
|
664
|
+
exact central redirects after account → role → deployment precedence:
|
|
665
|
+
|
|
666
|
+
```yaml
|
|
667
|
+
session:
|
|
668
|
+
provider: local
|
|
669
|
+
model: model-v2
|
|
670
|
+
modelReplacements:
|
|
671
|
+
- from:
|
|
672
|
+
provider: local
|
|
673
|
+
model: model-v1
|
|
674
|
+
to:
|
|
675
|
+
provider: local
|
|
676
|
+
model: model-v2
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
The rule covers saved account and role pins as well as the deployment default;
|
|
680
|
+
unrelated pairs keep their own selection. The terminal target must exist in the
|
|
681
|
+
Host catalog. Cycles and duplicate sources are configuration errors. Chained
|
|
682
|
+
redirects let a later transition cover older pins without editing every account;
|
|
683
|
+
retain a rule while stored policies still refer to its source. The stored pins
|
|
684
|
+
remain intact. New chats use the replacement immediately. On access attestation,
|
|
685
|
+
an idle owned QA chat can move along an exact redirect to its current policy;
|
|
686
|
+
an active turn is left unchanged. Ownership, workspace and adoption checks still
|
|
687
|
+
precede reconciliation. A missing target fails explicitly instead of selecting
|
|
688
|
+
another model from catalog order. The journal records the original and applied
|
|
689
|
+
pairs, and delegated experts resolve the owning chat's policy through Host lineage.
|
|
690
|
+
|
|
522
691
|
### User profile
|
|
523
692
|
|
|
524
693
|
Clicking the account name in the sidebar footer opens the signed-in user's
|
|
@@ -669,28 +838,37 @@ stays quiet until a message is actually sent, and the old session stays intact
|
|
|
669
838
|
for operator inspection. The sidebar orders chats by the host's last update,
|
|
670
839
|
so merely opening a chat never moves it.
|
|
671
840
|
|
|
672
|
-
A
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
841
|
+
A deployment can bound concurrent chats with `session.maxActiveRequests`; the
|
|
842
|
+
default 0 leaves concurrency unbounded. Admission is enforced on the server
|
|
843
|
+
before a root agent takes its first step, covering browser, integration and
|
|
844
|
+
native prompts. Simultaneous sends reserve places atomically; waiting chats
|
|
845
|
+
start in FIFO order without resending their questions. A complete turn keeps
|
|
846
|
+
its place through tool calls and review. Delegated experts run within their
|
|
847
|
+
chat's place, including background descendants still running after the parent
|
|
848
|
+
turn ends. A subsequent turn rejoins the queue behind earlier waiters.
|
|
849
|
+
|
|
850
|
+
An inline status above the composer shows the waiting chat's real queue
|
|
851
|
+
position. After at least three completed turns the server can also provide an
|
|
852
|
+
approximate delay based on recent durations. Before that, no duration is shown.
|
|
853
|
+
The status survives closing and reopening the page while Host stays running;
|
|
854
|
+
the stop button cancels a waiting request. Queue status reveals global counts
|
|
855
|
+
only, and a chat's position requires current read access. An unavailable status
|
|
856
|
+
read does not bypass server admission.
|
|
857
|
+
|
|
858
|
+
Changing the limit does not interrupt already active turns: lowering it admits
|
|
859
|
+
no new chat until the active count fits, and raising it releases more waiters.
|
|
860
|
+
Commands that do not run a turn remain usable while the stand is full.
|
|
861
|
+
|
|
862
|
+
The number is the chain's last layer, and the two above it override it per turn:
|
|
863
|
+
a subrole may carry `maxActiveRequests` (edited on the «Саброли» page's model
|
|
864
|
+
tab), and an account may carry one (the user card's «Слоты очереди этого
|
|
865
|
+
аккаунта» select). The account beats the role, the role beats the deployment,
|
|
866
|
+
and 0 is a real value that lifts the ceiling for that role's or account's turns
|
|
867
|
+
outright. A turn whose principal raises the number fits while a lower ceiling
|
|
868
|
+
would refuse it, and one full principal does not starve the queue behind it.
|
|
869
|
+
The status read answers the caller's own chain: a chat's read uses the chat's
|
|
870
|
+
account, the pre-send read uses the visitor's, and a stand without accounts
|
|
871
|
+
keeps the one deployment number.
|
|
694
872
|
|
|
695
873
|
Regeneration: the last committed answer offers a retry action. The session log
|
|
696
874
|
is append-only, so "regenerate" sends a hidden instruction as an ordinary
|
|
@@ -1268,6 +1446,7 @@ integration:
|
|
|
1268
1446
|
requestTimeoutMs: 3600000 # one-hour answer budget; see docs/INTEGRATION-API.md §2.5
|
|
1269
1447
|
maxConcurrent: 4
|
|
1270
1448
|
requestsPerMinute: 60
|
|
1449
|
+
maxAttachments: 0 # 0 inherits attachments.maxPending; positive values set an API limit (1-40)
|
|
1271
1450
|
maxAnswerCharacters: 4096 # the answer the ticket comment can hold
|
|
1272
1451
|
```
|
|
1273
1452
|
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { SessionId } from "@deepseek-ai/dsh-session/types";
|
|
2
|
+
import { ReasoningEffortId } from "@deepseek-ai/dsh-llm";
|
|
3
|
+
import { sessionModelSelection } from "../session-model.js";
|
|
4
|
+
import { modelPolicySatisfied, projectModelCatalog, } from "./model-policy.js";
|
|
5
|
+
/** Only explicit policy grants are exposed to an ordinary account. */
|
|
6
|
+
export async function userModelOptions(ctx, access, token, sessionId, subroleId, preview) {
|
|
7
|
+
const resolve = () => {
|
|
8
|
+
if (sessionId === null)
|
|
9
|
+
return access.draftModelPolicy(token, subroleId, preview);
|
|
10
|
+
return access.userSessionModelPolicy(token, sessionId);
|
|
11
|
+
};
|
|
12
|
+
resolve(); // Authenticate before reading the Host catalog.
|
|
13
|
+
const catalog = projectModelCatalog(await ctx.sessionController.modelCatalog());
|
|
14
|
+
const policy = resolve(); // Recheck the token and grants after the async read.
|
|
15
|
+
const granted = [
|
|
16
|
+
...(policy.pair === undefined ? [] : [policy.pair]),
|
|
17
|
+
...(policy.allowedModels ?? []),
|
|
18
|
+
];
|
|
19
|
+
const models = catalog.filter((entry) => granted.some((pair) => pair.provider === entry.provider && pair.model === entry.model));
|
|
20
|
+
const agent = sessionId === null ? undefined : ctx.agents.get(SessionId(sessionId));
|
|
21
|
+
const facts = agent === undefined ? policy.pair : sessionModelSelection(ctx, agent);
|
|
22
|
+
const selected = facts?.provider === undefined || facts.model === undefined
|
|
23
|
+
? undefined
|
|
24
|
+
: {
|
|
25
|
+
provider: facts.provider,
|
|
26
|
+
model: facts.model,
|
|
27
|
+
...(facts.reasoningEffort === undefined
|
|
28
|
+
? {}
|
|
29
|
+
: { reasoningEffort: facts.reasoningEffort }),
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
models,
|
|
33
|
+
choices: granted,
|
|
34
|
+
...(policy.pair === undefined ? {} : { defaultModel: policy.pair }),
|
|
35
|
+
...(selected === undefined ? {} : { selected }),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Validate both policy membership and live availability before a user mutation. */
|
|
39
|
+
export async function assertUserModelSelection(ctx, policy, request) {
|
|
40
|
+
if (typeof request.provider !== "string" || typeof request.model !== "string")
|
|
41
|
+
throw new Error("Choose a complete provider and model pair.");
|
|
42
|
+
const pair = {
|
|
43
|
+
provider: request.provider,
|
|
44
|
+
model: request.model,
|
|
45
|
+
...(request.reasoningEffort == null
|
|
46
|
+
? {}
|
|
47
|
+
: { reasoningEffort: request.reasoningEffort }),
|
|
48
|
+
};
|
|
49
|
+
const catalog = projectModelCatalog(await ctx.sessionController.modelCatalog());
|
|
50
|
+
const entry = catalog.find((item) => item.provider === pair.provider && item.model === pair.model);
|
|
51
|
+
if (entry === undefined)
|
|
52
|
+
throw new Error("This model is currently unavailable.");
|
|
53
|
+
if (pair.reasoningEffort !== undefined &&
|
|
54
|
+
!entry.reasoningEfforts.includes(pair.reasoningEffort))
|
|
55
|
+
throw new Error("This reasoning effort is unavailable for the selected model.");
|
|
56
|
+
const effective = policy();
|
|
57
|
+
if ((effective.pair === undefined && effective.allowedModels === undefined) ||
|
|
58
|
+
!modelPolicySatisfied(effective, pair))
|
|
59
|
+
throw new Error("This model is not allowed by the chat policy.");
|
|
60
|
+
}
|
|
61
|
+
/** Native durable per-session selection, without the operator-wide default save. */
|
|
62
|
+
export async function selectUserModel(ctx, access, token, request) {
|
|
63
|
+
const id = String(request.sessionId);
|
|
64
|
+
const policy = () => access.userSessionModelPolicy(token, id);
|
|
65
|
+
policy();
|
|
66
|
+
const agent = ctx.agents.get(request.sessionId);
|
|
67
|
+
if (agent === undefined)
|
|
68
|
+
throw new Error("The chat is unavailable.");
|
|
69
|
+
// DSH's public selectModel also saves the operator-wide default. Its owned
|
|
70
|
+
// agent controller has the narrower commit primitive. Keep this compatibility
|
|
71
|
+
// bridge guarded and fail closed when the Host no longer exposes it; never
|
|
72
|
+
// fall back to changing a deployment-wide preference from a QA account.
|
|
73
|
+
const controller = ctx.sessionController.agents;
|
|
74
|
+
if (typeof controller?.serializeImageAdmission !== "function" ||
|
|
75
|
+
typeof controller.selectForNextRequest !== "function")
|
|
76
|
+
throw new Error("This Host does not support chat-local model selection.");
|
|
77
|
+
return controller.serializeImageAdmission(agent, async () => {
|
|
78
|
+
if (agent.status !== "idle")
|
|
79
|
+
throw new Error("Wait for the current response before changing the model.");
|
|
80
|
+
await assertUserModelSelection(ctx, policy, request);
|
|
81
|
+
const selected = await ctx.llm.resolveCallConfig({
|
|
82
|
+
provider: request.provider,
|
|
83
|
+
model: request.model,
|
|
84
|
+
...(request.reasoningEffort === undefined
|
|
85
|
+
? {}
|
|
86
|
+
: { reasoningEffort: ReasoningEffortId(request.reasoningEffort) }),
|
|
87
|
+
});
|
|
88
|
+
if (!modelPolicySatisfied(policy(), selected))
|
|
89
|
+
throw new Error("This model is not allowed by the chat policy.");
|
|
90
|
+
if (agent.status !== "idle")
|
|
91
|
+
throw new Error("Wait for the current response before changing the model.");
|
|
92
|
+
const pair = {
|
|
93
|
+
provider: selected.provider,
|
|
94
|
+
model: selected.model,
|
|
95
|
+
...(selected.reasoningEffort === undefined
|
|
96
|
+
? {}
|
|
97
|
+
: { reasoningEffort: selected.reasoningEffort }),
|
|
98
|
+
};
|
|
99
|
+
controller.selectForNextRequest(agent, pair);
|
|
100
|
+
return { selected: pair };
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=model-choices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-choices.js","sourceRoot":"","sources":["../../src/access/model-choices.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
/** Preserve an explicit empty override and reject malformed or excessive grants. */
|
|
2
|
+
export function normalizeAllowedModels(value, label) {
|
|
3
|
+
if (value === undefined)
|
|
4
|
+
return undefined;
|
|
5
|
+
if (!Array.isArray(value) || value.length > 100)
|
|
6
|
+
throw new TypeError(`${label} must be an array of at most 100 model pairs`);
|
|
7
|
+
const pairs = value.map((entry, index) => {
|
|
8
|
+
const pair = normalizeModelPair(entry, `${label}[${index}]`);
|
|
9
|
+
if (pair === undefined)
|
|
10
|
+
throw new TypeError(`${label} needs complete pairs`);
|
|
11
|
+
return pair;
|
|
12
|
+
});
|
|
13
|
+
return Object.freeze([
|
|
14
|
+
...new Map(pairs.map((pair) => [JSON.stringify(pair), pair])).values(),
|
|
15
|
+
]);
|
|
16
|
+
}
|
|
17
|
+
/** Add explicit grants without changing which model a new chat starts on. */
|
|
18
|
+
export function withAllowedModels(policy, models, replacements) {
|
|
19
|
+
if (models === undefined)
|
|
20
|
+
return policy;
|
|
21
|
+
return Object.freeze({
|
|
22
|
+
...policy,
|
|
23
|
+
allowedModels: Object.freeze(models.map((pair) => replaceModelPair(pair, replacements))),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
1
26
|
function text(value) {
|
|
2
27
|
return typeof value === "string" ? value.trim() : "";
|
|
3
28
|
}
|
|
@@ -47,18 +72,62 @@ export function deploymentModelPair(session) {
|
|
|
47
72
|
* @param layers - the layers in priority order, `undefined` where a layer keeps
|
|
48
73
|
* no opinion.
|
|
49
74
|
*/
|
|
50
|
-
export function resolveModelPolicy(layers) {
|
|
75
|
+
export function resolveModelPolicy(layers, replacements = []) {
|
|
51
76
|
const order = [
|
|
52
77
|
"account",
|
|
53
78
|
"subrole",
|
|
54
79
|
"deployment",
|
|
55
80
|
];
|
|
56
81
|
const layer = order.find((candidate) => layers[candidate] !== undefined);
|
|
82
|
+
const original = layer === undefined ? undefined : layers[layer];
|
|
83
|
+
const pair = replaceModelPair(original, replacements);
|
|
57
84
|
return Object.freeze({
|
|
58
|
-
pair
|
|
85
|
+
pair,
|
|
59
86
|
layer,
|
|
87
|
+
...(original !== undefined && pair !== original
|
|
88
|
+
? { redirectedFrom: original }
|
|
89
|
+
: {}),
|
|
60
90
|
});
|
|
61
91
|
}
|
|
92
|
+
/** Resolve only explicitly configured exact redirects; never guess from catalog order. */
|
|
93
|
+
export function replaceModelPair(pair, replacements) {
|
|
94
|
+
if (pair === undefined)
|
|
95
|
+
return undefined;
|
|
96
|
+
let current = pair;
|
|
97
|
+
const visited = new Set();
|
|
98
|
+
for (;;) {
|
|
99
|
+
const key = JSON.stringify([current.provider, current.model]);
|
|
100
|
+
if (visited.has(key))
|
|
101
|
+
throw new TypeError("session.modelReplacements contains a cycle");
|
|
102
|
+
visited.add(key);
|
|
103
|
+
const next = replacements.find(({ from }) => from.provider === current.provider && from.model === current.model);
|
|
104
|
+
if (next === undefined)
|
|
105
|
+
return current;
|
|
106
|
+
current = next.to;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
export function normalizeModelReplacements(input) {
|
|
110
|
+
if (input === undefined)
|
|
111
|
+
return Object.freeze([]);
|
|
112
|
+
if (!Array.isArray(input) || input.length > 100)
|
|
113
|
+
throw new TypeError("session.modelReplacements must be an array of at most 100 exact replacements");
|
|
114
|
+
const keys = new Set();
|
|
115
|
+
const replacements = input.map((entry, index) => {
|
|
116
|
+
const label = `session.modelReplacements[${index}]`;
|
|
117
|
+
const from = normalizeModelPair(entry?.from, `${label}.from`);
|
|
118
|
+
const to = normalizeModelPair(entry?.to, `${label}.to`);
|
|
119
|
+
if (from === undefined || to === undefined)
|
|
120
|
+
throw new TypeError(`${label} needs complete from/to pairs`);
|
|
121
|
+
const key = JSON.stringify([from.provider, from.model]);
|
|
122
|
+
if (keys.has(key))
|
|
123
|
+
throw new TypeError(`${label} duplicates a source pair`);
|
|
124
|
+
keys.add(key);
|
|
125
|
+
return Object.freeze({ from, to });
|
|
126
|
+
});
|
|
127
|
+
for (const { from } of replacements)
|
|
128
|
+
replaceModelPair(from, replacements);
|
|
129
|
+
return Object.freeze(replacements);
|
|
130
|
+
}
|
|
62
131
|
/**
|
|
63
132
|
* Whether one agent's selection sits inside the session's model policy.
|
|
64
133
|
*
|
|
@@ -70,12 +139,15 @@ export function resolveModelPolicy(layers) {
|
|
|
70
139
|
*/
|
|
71
140
|
export function modelPolicySatisfied(policy, selection) {
|
|
72
141
|
const pair = policy.pair;
|
|
73
|
-
if (pair === undefined)
|
|
142
|
+
if (pair === undefined && policy.allowedModels === undefined)
|
|
74
143
|
return true;
|
|
75
|
-
return
|
|
76
|
-
pair
|
|
77
|
-
(
|
|
78
|
-
|
|
144
|
+
return [
|
|
145
|
+
...(pair === undefined ? [] : [pair]),
|
|
146
|
+
...(policy.allowedModels ?? []),
|
|
147
|
+
].some((candidate) => candidate.provider === selection.provider &&
|
|
148
|
+
candidate.model === selection.model &&
|
|
149
|
+
(candidate.reasoningEffort === undefined ||
|
|
150
|
+
candidate.reasoningEffort === selection.reasoningEffort));
|
|
79
151
|
}
|
|
80
152
|
/**
|
|
81
153
|
* Project the harness catalog into the pairs the administration surface lists.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-policy.js","sourceRoot":"","sources":["../../src/access/model-policy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"model-policy.js","sourceRoot":"","sources":["../../src/access/model-policy.ts"],"names":[],"mappings":"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"}
|
package/lib/access/model.js
CHANGED
|
@@ -1,7 +1,27 @@
|
|
|
1
|
-
import { normalizeModelPair } from "./model-policy.js";
|
|
1
|
+
import { normalizeModelPair, normalizeAllowedModels } from "./model-policy.js";
|
|
2
|
+
import { QA_MAX_ACTIVE_REQUESTS_MAX } from "../config-resolvers/session.js";
|
|
2
3
|
import { normalizeSkillOverride, resolveSkillVisibility, skillHealth, } from "./skill-metadata.js";
|
|
3
4
|
const CAPABILITY_ID = /^[a-zA-Z0-9][a-zA-Z0-9._:/-]{0,199}$/u;
|
|
4
5
|
const SUBROLE_ID = /^[a-z0-9][a-z0-9-]{0,63}$/u;
|
|
6
|
+
/**
|
|
7
|
+
* Normalize one queue-slots override.
|
|
8
|
+
*
|
|
9
|
+
* An absent field keeps no opinion and the layer below it speaks. 0 is a real
|
|
10
|
+
* value — it lifts the ceiling for this role or account — while anything that
|
|
11
|
+
* is not an integer from 0 to {@link QA_MAX_ACTIVE_REQUESTS_MAX} is refused,
|
|
12
|
+
* the same range the deployment's own field accepts.
|
|
13
|
+
*/
|
|
14
|
+
export function normalizeQueueSlots(value, label) {
|
|
15
|
+
if (value === undefined || value === null)
|
|
16
|
+
return undefined;
|
|
17
|
+
if (typeof value !== "number" ||
|
|
18
|
+
!Number.isSafeInteger(value) ||
|
|
19
|
+
value < 0 ||
|
|
20
|
+
value > QA_MAX_ACTIVE_REQUESTS_MAX) {
|
|
21
|
+
throw new TypeError(`${label} must be an integer from 0 to ${String(QA_MAX_ACTIVE_REQUESTS_MAX)}`);
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
5
25
|
function uniqueIds(values, label) {
|
|
6
26
|
const result = [
|
|
7
27
|
...new Set(values.map((value) => {
|
|
@@ -78,6 +98,8 @@ export function normalizeSubrole(value) {
|
|
|
78
98
|
const icon = value.ui?.icon?.trim();
|
|
79
99
|
const accent = value.ui?.accent?.trim();
|
|
80
100
|
const model = normalizeModelPair(value.model, `subrole ${id}.model`);
|
|
101
|
+
const allowedModels = normalizeAllowedModels(value.allowedModels, `subrole ${id}.allowedModels`);
|
|
102
|
+
const maxActiveRequests = normalizeQueueSlots(value.maxActiveRequests, `subrole ${id}.maxActiveRequests`);
|
|
81
103
|
return Object.freeze({
|
|
82
104
|
id,
|
|
83
105
|
name,
|
|
@@ -85,6 +107,8 @@ export function normalizeSubrole(value) {
|
|
|
85
107
|
enabled: value.enabled === true,
|
|
86
108
|
capabilities: normalizeCapabilitySelection(value.capabilities),
|
|
87
109
|
...(model === undefined ? {} : { model }),
|
|
110
|
+
...(allowedModels === undefined ? {} : { allowedModels }),
|
|
111
|
+
...(maxActiveRequests === undefined ? {} : { maxActiveRequests }),
|
|
88
112
|
...(icon === undefined && accent === undefined
|
|
89
113
|
? {}
|
|
90
114
|
: {
|
|
@@ -196,10 +220,14 @@ export function normalizeUserAccess(value, config) {
|
|
|
196
220
|
? requestedDefault
|
|
197
221
|
: allowed[0];
|
|
198
222
|
const model = normalizeModelPair(value?.model, "account model");
|
|
223
|
+
const allowedModels = normalizeAllowedModels(value?.allowedModels, "account allowedModels");
|
|
224
|
+
const maxActiveRequests = normalizeQueueSlots(value?.maxActiveRequests, "account maxActiveRequests");
|
|
199
225
|
return Object.freeze({
|
|
200
226
|
allowedSubroles: Object.freeze(allowed),
|
|
201
227
|
defaultSubrole,
|
|
202
228
|
...(model === undefined ? {} : { model }),
|
|
229
|
+
...(allowedModels === undefined ? {} : { allowedModels }),
|
|
230
|
+
...(maxActiveRequests === undefined ? {} : { maxActiveRequests }),
|
|
203
231
|
});
|
|
204
232
|
}
|
|
205
233
|
function available(values, installed) {
|