@yadsh/dsh-qa-surface 0.13.0 → 0.15.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 +148 -22
- package/compatibility.json +7 -3
- package/lib/access/capability-catalog.js +22 -6
- package/lib/access/capability-catalog.js.map +1 -1
- package/lib/access/model.js +36 -2
- package/lib/access/model.js.map +1 -1
- package/lib/access/service.js +19 -4
- package/lib/access/service.js.map +1 -1
- package/lib/account-remotes.js +4 -0
- package/lib/account-remotes.js.map +1 -1
- package/lib/accounts/database.js +49 -9
- package/lib/accounts/database.js.map +1 -1
- package/lib/accounts/errors.js.map +1 -1
- package/lib/accounts/file.js.map +1 -1
- package/lib/accounts/store.js +53 -8
- package/lib/accounts/store.js.map +1 -1
- package/lib/admin/permissions.js +6 -0
- package/lib/admin/permissions.js.map +1 -1
- package/lib/admin/quality-store.js +95 -19
- package/lib/admin/quality-store.js.map +1 -1
- package/lib/admin/service.js +178 -0
- package/lib/admin/service.js.map +1 -1
- package/lib/cli.js +24 -4
- package/lib/cli.js.map +1 -1
- package/lib/client/QaAccountsController.js +52 -5
- package/lib/client/QaAccountsController.js.map +1 -1
- package/lib/client/QaConfigController.js.map +1 -1
- package/lib/client/QaSessionController.js +626 -63
- package/lib/client/QaSessionController.js.map +1 -1
- package/lib/client/QaSurface.js +293 -43
- package/lib/client/QaSurface.js.map +1 -1
- package/lib/client/QaSurfaceGuard.js +2 -2
- package/lib/client/QaSurfaceGuard.js.map +1 -1
- package/lib/client/QaTranscriptAdapter.js +4 -2
- package/lib/client/QaTranscriptAdapter.js.map +1 -1
- package/lib/client/admin/QaAdmin.js +59 -34
- package/lib/client/admin/QaAdmin.js.map +1 -1
- package/lib/client/admin/pages/Conversations.js +12 -12
- package/lib/client/admin/pages/Conversations.js.map +1 -1
- package/lib/client/admin/pages/ExpertMemory.js +210 -0
- package/lib/client/admin/pages/ExpertMemory.js.map +1 -0
- package/lib/client/admin/pages/Overview.js +4 -4
- package/lib/client/admin/pages/Overview.js.map +1 -1
- package/lib/client/admin/pages/Quality.js +5 -5
- package/lib/client/admin/pages/Quality.js.map +1 -1
- package/lib/client/admin/pages/Review.js +5 -5
- package/lib/client/admin/pages/Review.js.map +1 -1
- package/lib/client/admin/pages/SkillFiles.js +3 -3
- package/lib/client/admin/pages/SkillFiles.js.map +1 -1
- package/lib/client/admin/pages/Users.js +12 -12
- package/lib/client/admin/pages/Users.js.map +1 -1
- package/lib/client/admin/routes.js +4 -0
- package/lib/client/admin/routes.js.map +1 -1
- package/lib/client/admin/shared.js +15 -6
- package/lib/client/admin/shared.js.map +1 -1
- package/lib/client/admin/styles.js +3 -0
- package/lib/client/admin/styles.js.map +1 -1
- package/lib/client/attestation.js +11 -2
- package/lib/client/attestation.js.map +1 -1
- package/lib/client/audit/QaAuditDialog.js +2 -2
- package/lib/client/audit/QaAuditDialog.js.map +1 -1
- package/lib/client/audit/QaAuditRowBadge.js +1 -1
- package/lib/client/audit/QaAuditRowBadge.js.map +1 -1
- package/lib/client/components/Markdown.js +7 -2
- package/lib/client/components/Markdown.js.map +1 -1
- package/lib/client/components/QaAgentsDrawer.js +7 -5
- package/lib/client/components/QaAgentsDrawer.js.map +1 -1
- package/lib/client/components/QaApproval.js +2 -2
- package/lib/client/components/QaApproval.js.map +1 -1
- package/lib/client/components/QaAuthGate.js +6 -6
- package/lib/client/components/QaAuthGate.js.map +1 -1
- package/lib/client/components/QaChangelog.js +77 -0
- package/lib/client/components/QaChangelog.js.map +1 -1
- package/lib/client/components/QaComposer.js +9 -6
- package/lib/client/components/QaComposer.js.map +1 -1
- package/lib/client/components/QaFileAttachment.js +1 -1
- package/lib/client/components/QaFileAttachment.js.map +1 -1
- package/lib/client/components/QaFilesPanel.js +7 -7
- package/lib/client/components/QaFilesPanel.js.map +1 -1
- package/lib/client/components/QaHeader.js +4 -4
- package/lib/client/components/QaHeader.js.map +1 -1
- package/lib/client/components/QaMessage.js +12 -12
- package/lib/client/components/QaMessage.js.map +1 -1
- package/lib/client/components/QaModal.js +11 -2
- package/lib/client/components/QaModal.js.map +1 -1
- package/lib/client/components/QaQuestions.js +4 -4
- package/lib/client/components/QaQuestions.js.map +1 -1
- package/lib/client/components/QaQueueDock.js +137 -0
- package/lib/client/components/QaQueueDock.js.map +1 -0
- package/lib/client/components/QaRequestQueue.js +22 -0
- package/lib/client/components/QaRequestQueue.js.map +1 -0
- package/lib/client/components/QaRightRail.js +2 -2
- package/lib/client/components/QaRightRail.js.map +1 -1
- package/lib/client/components/QaSidebar.js +98 -7
- package/lib/client/components/QaSidebar.js.map +1 -1
- package/lib/client/components/QaSourcesPanel.js +10 -8
- package/lib/client/components/QaSourcesPanel.js.map +1 -1
- package/lib/client/components/QaThemeSwitcher.js +19 -0
- package/lib/client/components/QaThemeSwitcher.js.map +1 -0
- package/lib/client/components/QaTurnNotice.js +40 -0
- package/lib/client/components/QaTurnNotice.js.map +1 -0
- package/lib/client/components/QaTurnRail.js +3 -3
- package/lib/client/components/QaTurnRail.js.map +1 -1
- package/lib/client/components/QaWelcomeNotice.js +2 -6
- package/lib/client/components/QaWelcomeNotice.js.map +1 -1
- package/lib/client/components/QaWidthHandle.js +2 -2
- package/lib/client/components/QaWidthHandle.js.map +1 -1
- package/lib/client/components/QaWorkGroup.js +5 -5
- package/lib/client/components/QaWorkGroup.js.map +1 -1
- package/lib/client/components/QaWorkspaceBrowser.js +10 -10
- package/lib/client/components/QaWorkspaceBrowser.js.map +1 -1
- package/lib/client/components/VariantSwitcher.js +1 -1
- package/lib/client/components/VariantSwitcher.js.map +1 -1
- package/lib/client/failure-copy.js +26 -8
- package/lib/client/failure-copy.js.map +1 -1
- package/lib/client/focus-ring.js +193 -0
- package/lib/client/focus-ring.js.map +1 -0
- package/lib/client/index.js +40 -20
- package/lib/client/index.js.map +1 -1
- package/lib/client/lineage.js +3 -16
- package/lib/client/lineage.js.map +1 -1
- package/lib/client/markdown/CodeBlock.js +2 -2
- package/lib/client/markdown/CodeBlock.js.map +1 -1
- package/lib/client/markdown/blocks.js +118 -31
- package/lib/client/markdown/blocks.js.map +1 -1
- package/lib/client/markdown/math.js +1 -1
- package/lib/client/markdown/math.js.map +1 -1
- package/lib/client/markdown/render.js +32 -16
- package/lib/client/markdown/render.js.map +1 -1
- package/lib/client/markdown/source-chip.js +3 -3
- package/lib/client/markdown/source-chip.js.map +1 -1
- package/lib/client/notifications/notification-dispatcher.js +90 -0
- package/lib/client/notifications/notification-dispatcher.js.map +1 -0
- package/lib/client/notifications/preferences.js +61 -0
- package/lib/client/notifications/preferences.js.map +1 -0
- package/lib/client/notifications/turn-completion-source.js +82 -0
- package/lib/client/notifications/turn-completion-source.js.map +1 -0
- package/lib/client/notifications/use-turn-notifications.js +108 -0
- package/lib/client/notifications/use-turn-notifications.js.map +1 -0
- package/lib/client/panels/PanelHost.js +5 -5
- package/lib/client/panels/PanelHost.js.map +1 -1
- package/lib/client/panels/PanelLauncher.js +2 -2
- package/lib/client/panels/PanelLauncher.js.map +1 -1
- package/lib/client/project-session-state.js +156 -4
- package/lib/client/project-session-state.js.map +1 -1
- package/lib/client/role/RoleSelector.js +9 -4
- package/lib/client/role/RoleSelector.js.map +1 -1
- package/lib/client/settings/card.js +58 -17
- package/lib/client/settings/card.js.map +1 -1
- package/lib/client/settings/fields.js +11 -10
- package/lib/client/settings/fields.js.map +1 -1
- package/lib/client/settings/format.js +0 -9
- package/lib/client/settings/format.js.map +1 -1
- package/lib/client/settings/sections/access.js +5 -5
- package/lib/client/settings/sections/access.js.map +1 -1
- package/lib/client/settings/sections/accounts.js +10 -10
- package/lib/client/settings/sections/accounts.js.map +1 -1
- package/lib/client/settings/sections/attachments.js +6 -6
- package/lib/client/settings/sections/attachments.js.map +1 -1
- package/lib/client/settings/sections/branding.js +7 -7
- package/lib/client/settings/sections/branding.js.map +1 -1
- package/lib/client/settings/sections/common.js +4 -2
- package/lib/client/settings/sections/common.js.map +1 -1
- package/lib/client/settings/sections/docs.js +4 -3
- package/lib/client/settings/sections/docs.js.map +1 -1
- package/lib/client/settings/sections/embedding.js +2 -2
- package/lib/client/settings/sections/embedding.js.map +1 -1
- package/lib/client/settings/sections/interface.js +12 -12
- package/lib/client/settings/sections/interface.js.map +1 -1
- package/lib/client/settings/sections/lockdown.js +9 -9
- package/lib/client/settings/sections/lockdown.js.map +1 -1
- package/lib/client/settings/sections/notes.js +12 -12
- package/lib/client/settings/sections/notes.js.map +1 -1
- package/lib/client/settings/sections/session.js +15 -12
- package/lib/client/settings/sections/session.js.map +1 -1
- package/lib/client/settings/sections/slash.js +9 -9
- package/lib/client/settings/sections/slash.js.map +1 -1
- package/lib/client/settings/sections/sources.js +26 -26
- package/lib/client/settings/sections/sources.js.map +1 -1
- package/lib/client/settings/sections/status.js +2 -2
- package/lib/client/settings/sections/status.js.map +1 -1
- package/lib/client/settings/styles.js +15 -9
- package/lib/client/settings/styles.js.map +1 -1
- package/lib/client/slash/SlashPalette.js +4 -4
- package/lib/client/slash/SlashPalette.js.map +1 -1
- package/lib/client/styles.js +145 -83
- package/lib/client/styles.js.map +1 -1
- package/lib/client/theme-preference.js +162 -0
- package/lib/client/theme-preference.js.map +1 -0
- package/lib/client/types.js +8 -0
- package/lib/client/types.js.map +1 -1
- package/lib/client/use-session-ui-state.js +7 -5
- package/lib/client/use-session-ui-state.js.map +1 -1
- package/lib/client/user-settings/GeneralSettingsPage.js +6 -6
- package/lib/client/user-settings/GeneralSettingsPage.js.map +1 -1
- package/lib/client/user-settings/IntegrationTokensPage.js +6 -6
- package/lib/client/user-settings/IntegrationTokensPage.js.map +1 -1
- package/lib/client/user-settings/NotificationSettingsPage.js +107 -0
- package/lib/client/user-settings/NotificationSettingsPage.js.map +1 -0
- package/lib/client/user-settings/PasswordSettingsPage.js +1 -1
- package/lib/client/user-settings/PasswordSettingsPage.js.map +1 -1
- package/lib/client/user-settings/ProfileSettingsPage.js +2 -2
- package/lib/client/user-settings/ProfileSettingsPage.js.map +1 -1
- package/lib/client/user-settings/SkillCatalog.js +3 -3
- package/lib/client/user-settings/SkillCatalog.js.map +1 -1
- package/lib/client/user-settings/SkillEditor.js +26 -8
- package/lib/client/user-settings/SkillEditor.js.map +1 -1
- package/lib/client/user-settings/SkillToolPicker.js +3 -3
- package/lib/client/user-settings/SkillToolPicker.js.map +1 -1
- package/lib/client/user-settings/SkillsSettingsPage.js +1 -1
- package/lib/client/user-settings/SkillsSettingsPage.js.map +1 -1
- package/lib/client/user-settings/StartersSettingsPage.js +6 -6
- package/lib/client/user-settings/StartersSettingsPage.js.map +1 -1
- package/lib/client/user-settings/UserSettingsDialog.js +7 -2
- package/lib/client/user-settings/UserSettingsDialog.js.map +1 -1
- package/lib/client/user-settings/copy.js +2 -0
- package/lib/client/user-settings/copy.js.map +1 -1
- package/lib/client/user-settings/draft.js +4 -1
- package/lib/client/user-settings/draft.js.map +1 -1
- package/lib/client/user-settings/fields.js +5 -5
- package/lib/client/user-settings/fields.js.map +1 -1
- package/lib/client.js +7307 -4010
- package/lib/client.js.map +1 -1
- package/lib/config-resolvers/defaults.js +7 -0
- package/lib/config-resolvers/defaults.js.map +1 -1
- package/lib/config-resolvers/lockdown.js +7 -0
- package/lib/config-resolvers/lockdown.js.map +1 -1
- package/lib/config-resolvers/notifications.js +15 -0
- package/lib/config-resolvers/notifications.js.map +1 -0
- package/lib/config-resolvers/session.js +11 -1
- package/lib/config-resolvers/session.js.map +1 -1
- package/lib/config.js +67 -18
- package/lib/config.js.map +1 -1
- package/lib/enforcement/tool-grants.js +6 -6
- package/lib/enforcement/tool-grants.js.map +1 -1
- package/lib/index.js +145 -30
- package/lib/index.js.map +1 -1
- package/lib/integration/answer.js +74 -8
- package/lib/integration/answer.js.map +1 -1
- package/lib/integration/expert-memory.js +39 -0
- package/lib/integration/expert-memory.js.map +1 -0
- package/lib/integration/host-runner.js +26 -14
- package/lib/integration/host-runner.js.map +1 -1
- package/lib/notifications.js +66 -0
- package/lib/notifications.js.map +1 -0
- package/lib/personal-skills/errors.js.map +1 -1
- package/lib/personal-skills/service.js +81 -7
- package/lib/personal-skills/service.js.map +1 -1
- package/lib/prompt-notes.js +2 -10
- package/lib/prompt-notes.js.map +1 -1
- package/lib/provenance/host-store.js +8 -5
- package/lib/provenance/host-store.js.map +1 -1
- package/lib/qa-tools/docs-tools.js +1 -1
- package/lib/qa-tools/docs-tools.js.map +1 -1
- package/lib/qa-tools/lifecycle.js +6 -3
- package/lib/qa-tools/lifecycle.js.map +1 -1
- package/lib/request-queue.js +32 -0
- package/lib/request-queue.js.map +1 -0
- package/lib/resolve-config.js +2 -0
- package/lib/resolve-config.js.map +1 -1
- package/lib/secure-session.js +7 -2
- package/lib/secure-session.js.map +1 -1
- package/lib/shared/settings.js +19 -2
- package/lib/shared/settings.js.map +1 -1
- package/lib/turn-failure-log.js +67 -0
- package/lib/turn-failure-log.js.map +1 -0
- package/lib/typert.host.js +1398 -651
- package/lib/typert.remote-client.d.ts +15 -1
- package/lib/typert.remote-client.js +1315 -645
- package/lib/types/access/capability-catalog.d.ts +20 -4
- package/lib/types/access/model.d.ts +23 -0
- package/lib/types/access/service.d.ts +2 -3
- package/lib/types/account-remotes.d.ts +8 -1
- package/lib/types/accounts/database.d.ts +13 -3
- package/lib/types/accounts/errors.d.ts +9 -1
- package/lib/types/accounts/file.d.ts +12 -0
- package/lib/types/accounts/store.d.ts +30 -2
- package/lib/types/admin/quality-store.d.ts +33 -0
- package/lib/types/admin/service.d.ts +60 -1
- package/lib/types/client/QaAccountsController.d.ts +25 -2
- package/lib/types/client/QaConfigController.d.ts +2 -2
- package/lib/types/client/QaSessionController.d.ts +199 -6
- package/lib/types/client/QaSurface.d.ts +6 -1
- package/lib/types/client/admin/pages/ExpertMemory.d.ts +19 -0
- package/lib/types/client/admin/routes.d.ts +3 -1
- package/lib/types/client/admin/shared.d.ts +4 -0
- package/lib/types/client/components/Markdown.d.ts +8 -1
- package/lib/types/client/components/QaHeader.d.ts +18 -4
- package/lib/types/client/components/QaModal.d.ts +7 -0
- package/lib/types/client/components/QaQueueDock.d.ts +27 -0
- package/lib/types/client/components/QaRequestQueue.d.ts +20 -0
- package/lib/types/client/components/QaSidebar.d.ts +14 -0
- package/lib/types/client/components/QaThemeSwitcher.d.ts +14 -0
- package/lib/types/client/components/QaTurnNotice.d.ts +55 -0
- package/lib/types/client/focus-ring.d.ts +65 -0
- package/lib/types/client/lineage.d.ts +3 -13
- package/lib/types/client/markdown/blocks.d.ts +30 -2
- package/lib/types/client/markdown/render.d.ts +4 -1
- package/lib/types/client/notifications/notification-dispatcher.d.ts +68 -0
- package/lib/types/client/notifications/preferences.d.ts +44 -0
- package/lib/types/client/notifications/turn-completion-source.d.ts +114 -0
- package/lib/types/client/notifications/use-turn-notifications.d.ts +51 -0
- package/lib/types/client/project-session-state.d.ts +35 -1
- package/lib/types/client/role/RoleSelector.d.ts +7 -1
- package/lib/types/client/settings/card.d.ts +36 -5
- package/lib/types/client/settings/fields.d.ts +24 -0
- package/lib/types/client/settings/format.d.ts +0 -5
- package/lib/types/client/settings/sections/common.d.ts +3 -1
- package/lib/types/client/settings/sections/session.d.ts +2 -1
- package/lib/types/client/settings/styles.d.ts +9 -4
- package/lib/types/client/settings-extensions/user-session.d.ts +1 -1
- package/lib/types/client/theme-preference.d.ts +69 -0
- package/lib/types/client/types.d.ts +25 -1
- package/lib/types/client/use-session-ui-state.d.ts +6 -4
- package/lib/types/client/user-settings/GeneralSettingsPage.d.ts +5 -5
- package/lib/types/client/user-settings/NotificationSettingsPage.d.ts +40 -0
- package/lib/types/client/user-settings/UserSettingsDialog.d.ts +12 -2
- package/lib/types/client/user-settings/draft.d.ts +4 -1
- package/lib/types/client/user-settings/fields.d.ts +7 -0
- package/lib/types/config-resolvers/notifications.d.ts +10 -0
- package/lib/types/config-resolvers/session.d.ts +6 -0
- package/lib/types/config.d.ts +20 -1
- package/lib/types/index.d.ts +53 -5
- package/lib/types/integration/answer.d.ts +27 -0
- package/lib/types/integration/expert-memory.d.ts +49 -0
- package/lib/types/notifications.d.ts +32 -0
- package/lib/types/personal-skills/errors.d.ts +1 -1
- package/lib/types/personal-skills/service.d.ts +6 -0
- package/lib/types/prompt-notes.d.ts +17 -4
- package/lib/types/request-queue.d.ts +32 -0
- package/lib/types/shared/settings.d.ts +19 -2
- package/lib/types/turn-failure-log.d.ts +18 -0
- package/lib/types/types.d.ts +180 -4
- package/package.json +64 -63
- package/scripts/attach-workspace-sessions.mjs +24 -5
- package/scripts/repair-session-events.mjs +25 -5
- package/lib/client/markdown/MermaidBlock.js +0 -80
- package/lib/client/markdown/MermaidBlock.js.map +0 -1
- package/lib/types/client/markdown/MermaidBlock.d.ts +0 -6
|
@@ -24,11 +24,67 @@ function record(value) {
|
|
|
24
24
|
*/
|
|
25
25
|
export function answerAfter(events, afterSeq) {
|
|
26
26
|
const ordered = [...events].sort((left, right) => left.seq - right.seq);
|
|
27
|
+
return projectTurn(ordered, (event) => event.seq > afterSeq);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Project the answer of the turn one request's own prompt opened.
|
|
31
|
+
*
|
|
32
|
+
* The harness stamps the prompt's rpc id onto the durable user row it claims,
|
|
33
|
+
* so the row is found by id rather than guessed from the newest human message:
|
|
34
|
+
* two questions in flight on one chat open two turns, and a selection by
|
|
35
|
+
* "latest" answers each caller with the other's turn. The turn that answers a
|
|
36
|
+
* request is the one its row was claimed into — `turn/start` opens a turn and
|
|
37
|
+
* the claimed rows follow it — so the answer is read out of that turn's
|
|
38
|
+
* assistant messages only, and its number travels with it for the citation.
|
|
39
|
+
*
|
|
40
|
+
* @param events - durable events, in any order.
|
|
41
|
+
* @param requestId - the rpc id the prompt was submitted with.
|
|
42
|
+
* @returns the answer of this request's turn, or `undefined` when the log
|
|
43
|
+
* holds no row for the request (an unflushed prompt, or a harness that does not
|
|
44
|
+
* echo the id), which leaves the choice of cursor to the caller.
|
|
45
|
+
*/
|
|
46
|
+
export function answerForRequest(events, requestId) {
|
|
47
|
+
const ordered = [...events].sort((left, right) => left.seq - right.seq);
|
|
48
|
+
let promptSeq;
|
|
49
|
+
let turn;
|
|
50
|
+
for (const event of ordered) {
|
|
51
|
+
if (promptSeq !== undefined)
|
|
52
|
+
break;
|
|
53
|
+
if (event.type === "turn/start") {
|
|
54
|
+
const data = record(event.data);
|
|
55
|
+
if (typeof data?.turn === "number")
|
|
56
|
+
turn = data.turn;
|
|
57
|
+
}
|
|
58
|
+
else if (isPromptOf(event, requestId)) {
|
|
59
|
+
promptSeq = event.seq;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (promptSeq === undefined)
|
|
63
|
+
return undefined;
|
|
64
|
+
const ownSeq = promptSeq;
|
|
65
|
+
if (turn === undefined) {
|
|
66
|
+
// A row with no `turn/start` before it: the answer is whatever this prompt
|
|
67
|
+
// and the next human one bracket — the window the turn would have been.
|
|
68
|
+
let next = Number.POSITIVE_INFINITY;
|
|
69
|
+
for (const event of ordered) {
|
|
70
|
+
if (event.seq > ownSeq && isHumanPrompt(event) && event.seq < next) {
|
|
71
|
+
next = event.seq;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const bounded = projectTurn(ordered, (event) => event.seq > ownSeq && event.seq < next);
|
|
75
|
+
return Object.freeze({ ...bounded, turn: null });
|
|
76
|
+
}
|
|
77
|
+
const numbered = turn;
|
|
78
|
+
const projected = projectTurn(ordered, (event) => event.seq > ownSeq && record(event.data)?.turn === numbered);
|
|
79
|
+
return Object.freeze({ ...projected, turn: numbered });
|
|
80
|
+
}
|
|
81
|
+
/** Project one turn's answer out of the assistant messages it committed. */
|
|
82
|
+
function projectTurn(ordered, belongs) {
|
|
27
83
|
let answer = "";
|
|
28
84
|
let seq = null;
|
|
29
85
|
let lastWasInterrupted = false;
|
|
30
86
|
for (const event of ordered) {
|
|
31
|
-
if (event.type !== "assistant/message" || event
|
|
87
|
+
if (event.type !== "assistant/message" || !belongs(event))
|
|
32
88
|
continue;
|
|
33
89
|
const data = record(event.data);
|
|
34
90
|
const message = record(data?.message);
|
|
@@ -41,6 +97,22 @@ export function answerAfter(events, afterSeq) {
|
|
|
41
97
|
}
|
|
42
98
|
return Object.freeze({ answer, interrupted: lastWasInterrupted, seq });
|
|
43
99
|
}
|
|
100
|
+
/** The producing source of one durable message row, when it has one. */
|
|
101
|
+
function sourceOf(event) {
|
|
102
|
+
return record(record(event.data)?.source);
|
|
103
|
+
}
|
|
104
|
+
/** Whether one row is a human prompt, as opposed to injected context. */
|
|
105
|
+
function isHumanPrompt(event) {
|
|
106
|
+
return event.type === "user/message" && sourceOf(event)?.kind === "user";
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Whether one row is the prompt submitted under `requestId`. The harness copies
|
|
110
|
+
* the prompt's rpc id onto its source, which is what ties a durable row back to
|
|
111
|
+
* the caller that wrote it.
|
|
112
|
+
*/
|
|
113
|
+
function isPromptOf(event, requestId) {
|
|
114
|
+
return isHumanPrompt(event) && sourceOf(event)?.rpcId === requestId;
|
|
115
|
+
}
|
|
44
116
|
/**
|
|
45
117
|
* Cut an answer down to the size the caller publishes.
|
|
46
118
|
*
|
|
@@ -86,13 +158,7 @@ export function boundAnswer(answer, maxCharacters) {
|
|
|
86
158
|
export function lastPromptSeq(events) {
|
|
87
159
|
let seq = 0;
|
|
88
160
|
for (const event of events) {
|
|
89
|
-
if (event.
|
|
90
|
-
continue;
|
|
91
|
-
const data = record(event.data);
|
|
92
|
-
const source = record(data?.source);
|
|
93
|
-
if (source?.kind !== "user")
|
|
94
|
-
continue;
|
|
95
|
-
if (event.seq > seq)
|
|
161
|
+
if (isHumanPrompt(event) && event.seq > seq)
|
|
96
162
|
seq = event.seq;
|
|
97
163
|
}
|
|
98
164
|
return seq;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"answer.js","sourceRoot":"","sources":["../../src/integration/answer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"answer.js","sourceRoot":"","sources":["../../src/integration/answer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why a memory call failed, in the coarse vocabulary the console's copy table
|
|
3
|
+
* speaks.
|
|
4
|
+
*
|
|
5
|
+
* The refusal is read off the plugin's stable `code` field rather than its
|
|
6
|
+
* message: a message is written for a log and gets reworded, and matching one
|
|
7
|
+
* here would mean a rename in another package silently turns a refusal into an
|
|
8
|
+
* unhandled fault. `cause` is checked too, because the storage seam wraps what
|
|
9
|
+
* the backend threw.
|
|
10
|
+
*/
|
|
11
|
+
export function expertMemoryReasonOf(error) {
|
|
12
|
+
const code = errorCodeOf(error);
|
|
13
|
+
if (code === undefined)
|
|
14
|
+
return undefined;
|
|
15
|
+
switch (code) {
|
|
16
|
+
case "MEMORY_SCOPE_DENIED":
|
|
17
|
+
return "forbidden";
|
|
18
|
+
case "MEMORY_RECORD_MISSING":
|
|
19
|
+
return "memory-record-unknown";
|
|
20
|
+
case "STORAGE_UNAVAILABLE":
|
|
21
|
+
case "MEMORY_PROVIDER_MISSING":
|
|
22
|
+
return "memory-unavailable";
|
|
23
|
+
case "TASK_REJECTED":
|
|
24
|
+
return "invalid-memory";
|
|
25
|
+
default:
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function errorCodeOf(error) {
|
|
30
|
+
for (let current = error; current !== undefined && current !== null; current = current instanceof Error ? current.cause : undefined) {
|
|
31
|
+
const code = current.code;
|
|
32
|
+
if (typeof code === "string" && code !== "")
|
|
33
|
+
return code;
|
|
34
|
+
if (!(current instanceof Error))
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=expert-memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expert-memory.js","sourceRoot":"","sources":["../../src/integration/expert-memory.ts"],"names":[],"mappings":"AA8CA;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAAc;IAOd,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAChC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACzC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,qBAAqB;YACxB,OAAO,WAAW,CAAC;QACrB,KAAK,uBAAuB;YAC1B,OAAO,uBAAuB,CAAC;QACjC,KAAK,qBAAqB,CAAC;QAC3B,KAAK,yBAAyB;YAC5B,OAAO,oBAAoB,CAAC;QAC9B,KAAK,eAAe;YAClB,OAAO,gBAAgB,CAAC;QAC1B;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,KACE,IAAI,OAAO,GAAY,KAAK,EAC5B,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,IAAI,EACzC,OAAO,GAAG,OAAO,YAAY,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,EAC9D,CAAC;QACD,MAAM,IAAI,GAAI,OAAuC,CAAC,IAAI,CAAC;QAC3D,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,EAAE;YAAE,OAAO,IAAI,CAAC;QACzD,IAAI,CAAC,CAAC,OAAO,YAAY,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;IACpD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { SessionId } from "@deepseek-ai/dsh-session/types";
|
|
3
3
|
import { WorkspaceId } from "@deepseek-ai/dsh-workspace";
|
|
4
4
|
import { prepareQaUserWorkspace } from "../user-workspace.js";
|
|
5
|
-
import { answerAfter, lastPromptSeq } from "./answer.js";
|
|
5
|
+
import { answerAfter, answerForRequest, lastPromptSeq } from "./answer.js";
|
|
6
6
|
import { attachmentPromptParts } from "./attachments.js";
|
|
7
7
|
import { QaIntegrationTranscriptReader } from "./transcript-reader.js";
|
|
8
8
|
/** Build the runner for one plugin instance. */
|
|
@@ -170,16 +170,22 @@ export function createQaIntegrationRunner(options) {
|
|
|
170
170
|
if (!read.ok) {
|
|
171
171
|
throw new Error(`the session log is unavailable (${read.reason})`);
|
|
172
172
|
}
|
|
173
|
-
//
|
|
174
|
-
//
|
|
175
|
-
// newest
|
|
176
|
-
//
|
|
177
|
-
//
|
|
178
|
-
|
|
173
|
+
// This request's own turn answers it. The harness echoes the prompt's rpc
|
|
174
|
+
// id onto the durable user row, so the turn is found by that id instead of
|
|
175
|
+
// by "the newest prompt in the log": two questions in flight on one chat
|
|
176
|
+
// commit two turns, and reading the newest of them answers each caller with
|
|
177
|
+
// the other's turn. The cursor fallback is for the one case correlation
|
|
178
|
+
// cannot serve — a prompt whose row the read did not reach yet — and it is
|
|
179
|
+
// the later of the cursor taken before the question and the newest human
|
|
180
|
+
// prompt, so no earlier turn can be read as this one's answer.
|
|
181
|
+
const projected = answerForRequest(read.events, requestId) ?? {
|
|
182
|
+
...answerAfter(read.events, Math.max(floor, lastPromptSeq(read.events))),
|
|
183
|
+
turn: null,
|
|
184
|
+
};
|
|
179
185
|
return Object.freeze({
|
|
180
186
|
chatId,
|
|
181
187
|
answer: projected.answer,
|
|
182
|
-
sources: sourcesOf(chatId),
|
|
188
|
+
sources: sourcesOf(chatId, projected.turn),
|
|
183
189
|
interrupted: projected.interrupted,
|
|
184
190
|
});
|
|
185
191
|
},
|
|
@@ -195,15 +201,21 @@ export function createQaIntegrationRunner(options) {
|
|
|
195
201
|
},
|
|
196
202
|
};
|
|
197
203
|
/**
|
|
198
|
-
* The evidence of
|
|
199
|
-
*
|
|
200
|
-
* that belongs to the answer just written
|
|
201
|
-
*
|
|
204
|
+
* The evidence of one turn, for the bridge's citations. The provenance store
|
|
205
|
+
* materializes one bundle per turn, so the bundle named by the turn that
|
|
206
|
+
* answered is the one that belongs to the answer just written — the newest
|
|
207
|
+
* bundle is only the same thing while one turn is the last one in the chat.
|
|
208
|
+
* `turn` is null when the answer came from a log that named no turn for it,
|
|
209
|
+
* and the newest bundle is then the best the citation has. A deployment that
|
|
210
|
+
* switched source collection off answers with an empty list rather than no
|
|
211
|
+
* answer.
|
|
202
212
|
*/
|
|
203
|
-
function sourcesOf(sessionId) {
|
|
213
|
+
function sourcesOf(sessionId, turn) {
|
|
204
214
|
try {
|
|
205
215
|
const bundles = options.provenance.bundles(sessionId);
|
|
206
|
-
|
|
216
|
+
if (turn === null)
|
|
217
|
+
return bundles.at(-1)?.sources ?? [];
|
|
218
|
+
return bundles.find((bundle) => bundle.turn === turn)?.sources ?? [];
|
|
207
219
|
}
|
|
208
220
|
catch (error) {
|
|
209
221
|
logger.warn("integration.sources-unavailable", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-runner.js","sourceRoot":"","sources":["../../src/integration/host-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAOzC,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AASzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"host-runner.js","sourceRoot":"","sources":["../../src/integration/host-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-account choices about the notices a finished turn may raise. Shape,
|
|
3
|
+
* defaults and the normalization both halves of the plugin agree on — the
|
|
4
|
+
* accounts store reads a record it cannot trust, and the settings form sends
|
|
5
|
+
* one back. Free of Node built-ins for the same reason starters.ts is: the
|
|
6
|
+
* browser bundle imports it too.
|
|
7
|
+
*/
|
|
8
|
+
/** The channels a reader can choose between, in the order the form lists them. */
|
|
9
|
+
const NOTIFICATION_CHANNELS = ["inApp", "desktop"];
|
|
10
|
+
/**
|
|
11
|
+
* What an account that never opened the notifications form gets.
|
|
12
|
+
*
|
|
13
|
+
* The in-page line is the shipped default of the feature itself, so an
|
|
14
|
+
* untouched account keeps behaving as it did before the form existed. The
|
|
15
|
+
* desktop channel stays off until asked for: raising an operating-system notice
|
|
16
|
+
* is the one choice that leaves the page, and a stand where the browser was
|
|
17
|
+
* never asked must not make it on the account's behalf.
|
|
18
|
+
*/
|
|
19
|
+
export function emptyNotifications() {
|
|
20
|
+
return { inApp: true, desktop: false };
|
|
21
|
+
}
|
|
22
|
+
function asRecord(value) {
|
|
23
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
24
|
+
? value
|
|
25
|
+
: undefined;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Read one stored (or wire-delivered) notifications record into its public
|
|
29
|
+
* shape. Unknown or hand-edited input degrades to the readable subset rather
|
|
30
|
+
* than throwing, mirroring the starters read path: a malformed record must
|
|
31
|
+
* never cost the account its session.
|
|
32
|
+
*/
|
|
33
|
+
export function normalizeNotifications(value) {
|
|
34
|
+
const raw = asRecord(value);
|
|
35
|
+
if (raw === undefined)
|
|
36
|
+
return emptyNotifications();
|
|
37
|
+
const defaults = emptyNotifications();
|
|
38
|
+
return {
|
|
39
|
+
inApp: typeof raw.inApp === "boolean" ? raw.inApp : defaults.inApp,
|
|
40
|
+
desktop: typeof raw.desktop === "boolean" ? raw.desktop : defaults.desktop,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Validate one full-replace notifications write. Both channels are required: a
|
|
45
|
+
* payload that names only one would silently reset the other, and the form
|
|
46
|
+
* always sends the pair it was rendered with.
|
|
47
|
+
*/
|
|
48
|
+
export function validateNotificationsWrite(input) {
|
|
49
|
+
const raw = asRecord(input);
|
|
50
|
+
if (raw === undefined) {
|
|
51
|
+
return {
|
|
52
|
+
ok: false,
|
|
53
|
+
message: "the notifications payload is not an object",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
for (const channel of NOTIFICATION_CHANNELS) {
|
|
57
|
+
if (typeof raw[channel] !== "boolean") {
|
|
58
|
+
return { ok: false, message: `${channel} must be a boolean` };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
ok: true,
|
|
63
|
+
value: { inApp: raw.inApp === true, desktop: raw.desktop === true },
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=notifications.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notifications.js","sourceRoot":"","sources":["../src/notifications.ts"],"names":[],"mappings":"AAKA;;;;;;GAMG;AAEH,kFAAkF;AAClF,MAAM,qBAAqB,GAAG,CAAC,OAAO,EAAE,SAAS,CAAU,CAAC;AAE5D;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB;IAChC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AACzC,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACzE,CAAC,CAAE,KAAiC;QACpC,CAAC,CAAC,SAAS,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAc;IACnD,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5B,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,kBAAkB,EAAE,CAAC;IACnD,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAC;IACtC,OAAO;QACL,KAAK,EAAE,OAAO,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK;QAClE,OAAO,EAAE,OAAO,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO;KAC3E,CAAC;AACJ,CAAC;AAMD;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CACxC,KAAc;IAEd,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE,4CAA4C;SACtD,CAAC;IACJ,CAAC;IACD,KAAK,MAAM,OAAO,IAAI,qBAAqB,EAAE,CAAC;QAC5C,IAAI,OAAO,GAAG,CAAC,OAAO,CAAC,KAAK,SAAS,EAAE,CAAC;YACtC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,oBAAoB,EAAE,CAAC;QAChE,CAAC;IACH,CAAC;IACD,OAAO;QACL,EAAE,EAAE,IAAI;QACR,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,KAAK,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,KAAK,IAAI,EAAE;KACpE,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/personal-skills/errors.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/personal-skills/errors.ts"],"names":[],"mappings":"AAgBA,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAElC,MAAM;IADjB,YACW,MAAkC,EAC3C,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;sBAHN,MAAM;QAIf,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF"}
|
|
@@ -197,6 +197,12 @@ export class QaPersonalSkills {
|
|
|
197
197
|
throw new QaPersonalSkillError("skill-not-found", `no skill directory named ${name}`);
|
|
198
198
|
}
|
|
199
199
|
this.assertRevision(name, input.expectedRevision, current.revision);
|
|
200
|
+
if (current.truncated && input.confirmPartialOverwrite !== true) {
|
|
201
|
+
// The editor holds a head it could read, not the document. A save writes
|
|
202
|
+
// what it holds, so the tail nobody loaded would vanish with no trace:
|
|
203
|
+
// the client has to say it knows before that becomes a write.
|
|
204
|
+
throw new QaPersonalSkillError("skill-truncated", `skill ${name} is larger than the read ceiling and was loaded only partly`);
|
|
205
|
+
}
|
|
200
206
|
const text = serializeSkillFile({
|
|
201
207
|
...prepared.draft,
|
|
202
208
|
// Foreign frontmatter is read from the file, not from the browser: the
|
|
@@ -402,6 +408,7 @@ export class QaPersonalSkills {
|
|
|
402
408
|
contents: undefined,
|
|
403
409
|
diagnostics: [],
|
|
404
410
|
resourceCount: 0,
|
|
411
|
+
truncated: false,
|
|
405
412
|
};
|
|
406
413
|
let directory;
|
|
407
414
|
try {
|
|
@@ -431,6 +438,7 @@ export class QaPersonalSkills {
|
|
|
431
438
|
revision: "",
|
|
432
439
|
updatedAt: null,
|
|
433
440
|
contents: undefined,
|
|
441
|
+
truncated: false,
|
|
434
442
|
diagnostics: [
|
|
435
443
|
...resources.diagnostics,
|
|
436
444
|
{
|
|
@@ -445,6 +453,18 @@ export class QaPersonalSkills {
|
|
|
445
453
|
const parsed = parseSkillFile(read.text);
|
|
446
454
|
const diagnostics = [
|
|
447
455
|
...resources.diagnostics,
|
|
456
|
+
// Ahead of the parse findings: a body that stops mid-document explains
|
|
457
|
+
// why what follows it reads as broken.
|
|
458
|
+
...(read.truncated
|
|
459
|
+
? [
|
|
460
|
+
{
|
|
461
|
+
code: "skill-file-truncated",
|
|
462
|
+
severity: "error",
|
|
463
|
+
field: null,
|
|
464
|
+
detail: String(read.sizeBytes),
|
|
465
|
+
},
|
|
466
|
+
]
|
|
467
|
+
: []),
|
|
448
468
|
...(parsed.ok ? parsed.value.warnings : []),
|
|
449
469
|
];
|
|
450
470
|
if (!parsed.ok) {
|
|
@@ -463,7 +483,7 @@ export class QaPersonalSkills {
|
|
|
463
483
|
modelInvocable: parsed.value.modelInvocable,
|
|
464
484
|
userInvocable: parsed.value.userInvocable,
|
|
465
485
|
allowedTools: parsed.value.allowedTools,
|
|
466
|
-
sizeBytes:
|
|
486
|
+
sizeBytes: read.sizeBytes,
|
|
467
487
|
maxBytes,
|
|
468
488
|
availableTools: [...this.availableTools()],
|
|
469
489
|
}));
|
|
@@ -479,10 +499,13 @@ export class QaPersonalSkills {
|
|
|
479
499
|
return {
|
|
480
500
|
...base,
|
|
481
501
|
raw: read.text,
|
|
482
|
-
|
|
502
|
+
// The whole file, not the loaded prefix: an append behind the ceiling has
|
|
503
|
+
// to move the revision, or an update would overwrite bytes it never saw.
|
|
504
|
+
revision: hashFileBytes(filePath) ?? hashText(read.text),
|
|
483
505
|
updatedAt: read.updatedAt,
|
|
484
506
|
contents: parsed.ok ? parsed.value : undefined,
|
|
485
507
|
diagnostics,
|
|
508
|
+
truncated: read.truncated,
|
|
486
509
|
};
|
|
487
510
|
}
|
|
488
511
|
resources(directory) {
|
|
@@ -677,6 +700,7 @@ export class QaPersonalSkills {
|
|
|
677
700
|
body: contents?.body ?? skillFileBody(stored.raw),
|
|
678
701
|
extraFrontmatter: contents?.extraFrontmatter ?? {},
|
|
679
702
|
sourcePath: stored.filePath,
|
|
703
|
+
truncated: stored.truncated,
|
|
680
704
|
preview: contents === undefined ? stored.raw : serializeSkillFile(contents),
|
|
681
705
|
};
|
|
682
706
|
}
|
|
@@ -726,12 +750,17 @@ export class QaPersonalSkills {
|
|
|
726
750
|
* since changed reports as absent rather than as a claim about the present.
|
|
727
751
|
*/
|
|
728
752
|
adminEdits(roots) {
|
|
729
|
-
const
|
|
730
|
-
if (
|
|
753
|
+
const sidecar = readSkillText(path.join(roots.skills, QA_SKILL_ADMIN_EDITS_FILE), ADMIN_EDITS_MAX_BYTES);
|
|
754
|
+
if (sidecar === undefined)
|
|
731
755
|
return new Map();
|
|
756
|
+
// Only the text is taken, and the truncation flag is left unread on
|
|
757
|
+
// purpose: a half-read sidecar fails to parse and answers "no marks",
|
|
758
|
+
// which is the right degradation for a badge — and, unlike a skill file,
|
|
759
|
+
// this read has no write behind it that could lose the rest.
|
|
760
|
+
const { text } = sidecar;
|
|
732
761
|
let parsed;
|
|
733
762
|
try {
|
|
734
|
-
parsed = JSON.parse(
|
|
763
|
+
parsed = JSON.parse(text);
|
|
735
764
|
}
|
|
736
765
|
catch {
|
|
737
766
|
return new Map();
|
|
@@ -861,6 +890,38 @@ function qaUserWorkspaceFromCwd(cwd) {
|
|
|
861
890
|
function hashText(text) {
|
|
862
891
|
return createHash("sha256").update(Buffer.from(text, "utf8")).digest("hex");
|
|
863
892
|
}
|
|
893
|
+
/** One chunk of the streaming hash below. */
|
|
894
|
+
const HASH_CHUNK_BYTES = 64 * 1024;
|
|
895
|
+
/**
|
|
896
|
+
* sha256 of a whole file, read through a bounded buffer.
|
|
897
|
+
*
|
|
898
|
+
* The revision has to describe every byte a skill file holds, including the
|
|
899
|
+
* ones a size-capped read never loaded, and hashing must not put a huge file
|
|
900
|
+
* back into memory. Returns undefined when the file cannot be read: the caller
|
|
901
|
+
* falls back to the bytes it did see.
|
|
902
|
+
*/
|
|
903
|
+
function hashFileBytes(filePath) {
|
|
904
|
+
const hash = createHash("sha256");
|
|
905
|
+
const buffer = Buffer.alloc(HASH_CHUNK_BYTES);
|
|
906
|
+
try {
|
|
907
|
+
const handle = openSync(filePath, "r");
|
|
908
|
+
try {
|
|
909
|
+
for (;;) {
|
|
910
|
+
const read = readSync(handle, buffer, 0, buffer.length, null);
|
|
911
|
+
if (read === 0)
|
|
912
|
+
break;
|
|
913
|
+
hash.update(buffer.subarray(0, read));
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
finally {
|
|
917
|
+
closeSync(handle);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
catch {
|
|
921
|
+
return undefined;
|
|
922
|
+
}
|
|
923
|
+
return hash.digest("hex");
|
|
924
|
+
}
|
|
864
925
|
function pathExists(input) {
|
|
865
926
|
try {
|
|
866
927
|
statSync(input);
|
|
@@ -874,6 +935,9 @@ function pathExists(input) {
|
|
|
874
935
|
* Read one skill file, refusing to hold an unbounded file in memory: an
|
|
875
936
|
* oversized `SKILL.md` is read only up to the ceiling and reported by the
|
|
876
937
|
* validator, which keeps the editor usable instead of the Host unresponsive.
|
|
938
|
+
* The prefix is never presented as the document: the answer carries the real
|
|
939
|
+
* size and says plainly that the read stopped early, so a save can refuse to
|
|
940
|
+
* overwrite bytes nobody saw.
|
|
877
941
|
* The file is lstat-ed, so a symlinked `SKILL.md` is treated as absent rather
|
|
878
942
|
* than followed out of the account's directory.
|
|
879
943
|
*/
|
|
@@ -892,7 +956,12 @@ function readSkillText(filePath, maxBytes) {
|
|
|
892
956
|
}
|
|
893
957
|
if (size <= maxBytes) {
|
|
894
958
|
try {
|
|
895
|
-
return {
|
|
959
|
+
return {
|
|
960
|
+
text: readFileSync(filePath, "utf8"),
|
|
961
|
+
updatedAt,
|
|
962
|
+
sizeBytes: size,
|
|
963
|
+
truncated: false,
|
|
964
|
+
};
|
|
896
965
|
}
|
|
897
966
|
catch {
|
|
898
967
|
return undefined;
|
|
@@ -903,7 +972,12 @@ function readSkillText(filePath, maxBytes) {
|
|
|
903
972
|
try {
|
|
904
973
|
const buffer = Buffer.alloc(maxBytes);
|
|
905
974
|
const read = readSync(handle, buffer, 0, maxBytes, 0);
|
|
906
|
-
return {
|
|
975
|
+
return {
|
|
976
|
+
text: buffer.subarray(0, read).toString("utf8"),
|
|
977
|
+
updatedAt,
|
|
978
|
+
sizeBytes: size,
|
|
979
|
+
truncated: true,
|
|
980
|
+
};
|
|
907
981
|
}
|
|
908
982
|
finally {
|
|
909
983
|
closeSync(handle);
|