@yadsh/dsh-qa-surface 0.9.0 → 0.11.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 +269 -2
- package/lib/access/service.js +49 -10
- package/lib/access/service.js.map +1 -1
- package/lib/account-remotes.js +41 -0
- package/lib/account-remotes.js.map +1 -1
- package/lib/accounts/database.js +140 -0
- package/lib/accounts/database.js.map +1 -1
- package/lib/accounts/errors.js.map +1 -1
- package/lib/accounts/service-token.js +103 -0
- package/lib/accounts/service-token.js.map +1 -0
- package/lib/accounts/store.js +291 -5
- package/lib/accounts/store.js.map +1 -1
- package/lib/admin/conversation-log.js +14 -8
- package/lib/admin/conversation-log.js.map +1 -1
- package/lib/admin/permissions.js +5 -0
- package/lib/admin/permissions.js.map +1 -1
- package/lib/admin/quality-store.js +4 -0
- package/lib/admin/quality-store.js.map +1 -1
- package/lib/admin/service.js +180 -7
- package/lib/admin/service.js.map +1 -1
- package/lib/admin/session-log.js +39 -8
- package/lib/admin/session-log.js.map +1 -1
- package/lib/attachment-rules.js +39 -6
- package/lib/attachment-rules.js.map +1 -1
- package/lib/cli.js +83 -1
- package/lib/cli.js.map +1 -1
- package/lib/client/QaAccountsController.js +213 -4
- package/lib/client/QaAccountsController.js.map +1 -1
- package/lib/client/QaSessionController.js +49 -21
- package/lib/client/QaSessionController.js.map +1 -1
- package/lib/client/QaSurface.js +51 -9
- package/lib/client/QaSurface.js.map +1 -1
- package/lib/client/QaSurfaceGuard.js +12 -5
- package/lib/client/QaSurfaceGuard.js.map +1 -1
- package/lib/client/admin/QaAdmin.js +14 -2
- package/lib/client/admin/QaAdmin.js.map +1 -1
- package/lib/client/admin/pages/SkillFiles.js +78 -0
- package/lib/client/admin/pages/SkillFiles.js.map +1 -0
- package/lib/client/admin/pages/Users.js +74 -3
- package/lib/client/admin/pages/Users.js.map +1 -1
- package/lib/client/admin/routes.js +8 -1
- package/lib/client/admin/routes.js.map +1 -1
- package/lib/client/admin/shared.js +5 -1
- package/lib/client/admin/shared.js.map +1 -1
- package/lib/client/admin/styles.js +2 -0
- package/lib/client/admin/styles.js.map +1 -1
- package/lib/client/attachments.js +41 -11
- package/lib/client/attachments.js.map +1 -1
- package/lib/client/base64.js +22 -1
- package/lib/client/base64.js.map +1 -1
- package/lib/client/components/QaAgentsDrawer.js +9 -6
- package/lib/client/components/QaAgentsDrawer.js.map +1 -1
- package/lib/client/components/QaApproval.js +1 -1
- package/lib/client/components/QaApproval.js.map +1 -1
- package/lib/client/components/QaAuthGate.js +19 -10
- package/lib/client/components/QaAuthGate.js.map +1 -1
- package/lib/client/components/QaChangelog.js +56 -0
- package/lib/client/components/QaChangelog.js.map +1 -1
- package/lib/client/components/QaFilesPanel.js +17 -11
- package/lib/client/components/QaFilesPanel.js.map +1 -1
- package/lib/client/components/QaHeader.js +3 -11
- package/lib/client/components/QaHeader.js.map +1 -1
- 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/QaSidebar.js +10 -2
- package/lib/client/components/QaSidebar.js.map +1 -1
- package/lib/client/components/QaWidthHandle.js +111 -5
- package/lib/client/components/QaWidthHandle.js.map +1 -1
- package/lib/client/components/QaWorkspaceBrowser.js +231 -0
- package/lib/client/components/QaWorkspaceBrowser.js.map +1 -0
- package/lib/client/index.js +122 -53
- package/lib/client/index.js.map +1 -1
- package/lib/client/kiosk.js +51 -0
- package/lib/client/kiosk.js.map +1 -0
- package/lib/client/lineage.js +64 -0
- package/lib/client/lineage.js.map +1 -1
- package/lib/client/markdown/blocks.js +122 -7
- package/lib/client/markdown/blocks.js.map +1 -1
- package/lib/client/markdown/inline.js +108 -12
- package/lib/client/markdown/inline.js.map +1 -1
- package/lib/client/markdown/katex-css.js +12 -0
- package/lib/client/markdown/katex-css.js.map +1 -0
- package/lib/client/markdown/math.js +84 -0
- package/lib/client/markdown/math.js.map +1 -0
- package/lib/client/markdown/render.js +74 -5
- package/lib/client/markdown/render.js.map +1 -1
- package/lib/client/settings/card.js +2 -2
- package/lib/client/settings/card.js.map +1 -1
- package/lib/client/settings/sections/attachments.js +6 -5
- package/lib/client/settings/sections/attachments.js.map +1 -1
- package/lib/client/settings/sections/notes.js +64 -0
- package/lib/client/settings/sections/notes.js.map +1 -0
- package/lib/client/settings/sections/sources.js +4 -1
- package/lib/client/settings/sections/sources.js.map +1 -1
- package/lib/client/settings/sections.js +1 -0
- package/lib/client/settings/sections.js.map +1 -1
- package/lib/client/settlement.js +1 -1
- package/lib/client/settlement.js.map +1 -1
- package/lib/client/source-preview.js +3 -0
- package/lib/client/source-preview.js.map +1 -1
- package/lib/client/styles.js +106 -18
- package/lib/client/styles.js.map +1 -1
- package/lib/client/types.js.map +1 -1
- package/lib/client/user-settings/IntegrationTokensPage.js +150 -0
- package/lib/client/user-settings/IntegrationTokensPage.js.map +1 -0
- package/lib/client/user-settings/PasswordSettingsPage.js +48 -0
- package/lib/client/user-settings/PasswordSettingsPage.js.map +1 -0
- package/lib/client/user-settings/SkillCatalog.js +2 -2
- package/lib/client/user-settings/SkillCatalog.js.map +1 -1
- package/lib/client/user-settings/UserSettingsDialog.js +17 -2
- package/lib/client/user-settings/UserSettingsDialog.js.map +1 -1
- package/lib/client/user-settings/copy.js +20 -0
- package/lib/client/user-settings/copy.js.map +1 -1
- package/lib/client.js +31007 -686
- package/lib/client.js.map +1 -1
- package/lib/config-resolvers/attachments.js +6 -5
- package/lib/config-resolvers/attachments.js.map +1 -1
- package/lib/config-resolvers/defaults.js +33 -2
- package/lib/config-resolvers/defaults.js.map +1 -1
- package/lib/config-resolvers/integration.js +94 -0
- package/lib/config-resolvers/integration.js.map +1 -0
- package/lib/config-resolvers/notes.js +26 -0
- package/lib/config-resolvers/notes.js.map +1 -0
- package/lib/config-resolvers/sources.js +4 -0
- package/lib/config-resolvers/sources.js.map +1 -1
- package/lib/config.js +107 -0
- package/lib/config.js.map +1 -1
- package/lib/index.js +348 -12
- package/lib/index.js.map +1 -1
- package/lib/integration/answer.js +100 -0
- package/lib/integration/answer.js.map +1 -0
- package/lib/integration/attachments.js +231 -0
- package/lib/integration/attachments.js.map +1 -0
- package/lib/integration/contract.js +53 -0
- package/lib/integration/contract.js.map +1 -0
- package/lib/integration/host-runner.js +243 -0
- package/lib/integration/host-runner.js.map +1 -0
- package/lib/integration/http.js +538 -0
- package/lib/integration/http.js.map +1 -0
- package/lib/integration/service.js +331 -0
- package/lib/integration/service.js.map +1 -0
- package/lib/integration/transcript-reader.js +202 -0
- package/lib/integration/transcript-reader.js.map +1 -0
- package/lib/integration/transcript.js +148 -0
- package/lib/integration/transcript.js.map +1 -0
- package/lib/personal-skills/index.js +2 -2
- package/lib/personal-skills/index.js.map +1 -1
- package/lib/personal-skills/paths.js +14 -8
- package/lib/personal-skills/paths.js.map +1 -1
- package/lib/personal-skills/provider.js +16 -4
- package/lib/personal-skills/provider.js.map +1 -1
- package/lib/personal-skills/service.js +240 -30
- package/lib/personal-skills/service.js.map +1 -1
- package/lib/prompt-notes.js +103 -6
- package/lib/prompt-notes.js.map +1 -1
- package/lib/provenance/document-preview.js +60 -0
- package/lib/provenance/document-preview.js.map +1 -0
- package/lib/provenance/file-preview.js +191 -19
- package/lib/provenance/file-preview.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/docs-tools.js +563 -0
- package/lib/qa-tools/docs-tools.js.map +1 -0
- package/lib/qa-tools/file-delete-gate.js +60 -0
- package/lib/qa-tools/file-delete-gate.js.map +1 -0
- package/lib/qa-tools/file-delete.js +156 -0
- package/lib/qa-tools/file-delete.js.map +1 -0
- package/lib/resolve-config.js +6 -0
- package/lib/resolve-config.js.map +1 -1
- package/lib/secure-session.js +83 -3
- package/lib/secure-session.js.map +1 -1
- package/lib/shared/documents.js +22 -0
- package/lib/shared/documents.js.map +1 -0
- package/lib/shared/integration-tokens.js +23 -0
- package/lib/shared/integration-tokens.js.map +1 -0
- package/lib/typert.host.js +1320 -134
- package/lib/typert.remote-client.d.ts +33 -1
- package/lib/typert.remote-client.js +1102 -74
- package/lib/types/access/service.d.ts +33 -2
- package/lib/types/account-remotes.d.ts +30 -1
- package/lib/types/accounts/database.d.ts +53 -0
- package/lib/types/accounts/errors.d.ts +5 -1
- package/lib/types/accounts/service-token.d.ts +70 -0
- package/lib/types/accounts/store.d.ts +131 -1
- package/lib/types/admin/conversation-log.d.ts +12 -0
- package/lib/types/admin/service.d.ts +56 -1
- package/lib/types/admin/session-log.d.ts +21 -0
- package/lib/types/attachment-rules.d.ts +26 -3
- package/lib/types/client/QaAccountsController.d.ts +64 -5
- package/lib/types/client/QaSessionController.d.ts +24 -2
- package/lib/types/client/admin/pages/SkillFiles.d.ts +17 -0
- package/lib/types/client/admin/routes.d.ts +2 -0
- package/lib/types/client/admin/shared.d.ts +2 -0
- package/lib/types/client/attachments.d.ts +21 -4
- package/lib/types/client/base64.d.ts +13 -1
- package/lib/types/client/components/QaAgentsDrawer.d.ts +2 -1
- package/lib/types/client/components/QaFilesPanel.d.ts +16 -4
- package/lib/types/client/components/QaHeader.d.ts +9 -3
- package/lib/types/client/components/QaModal.d.ts +4 -3
- package/lib/types/client/components/QaWidthHandle.d.ts +48 -2
- package/lib/types/client/components/QaWorkspaceBrowser.d.ts +15 -0
- package/lib/types/client/index.d.ts +1 -1
- package/lib/types/client/kiosk.d.ts +34 -0
- package/lib/types/client/lineage.d.ts +42 -0
- package/lib/types/client/markdown/blocks.d.ts +14 -1
- package/lib/types/client/markdown/inline.d.ts +23 -7
- package/lib/types/client/markdown/katex-css.d.ts +11 -0
- package/lib/types/client/markdown/math.d.ts +23 -0
- package/lib/types/client/markdown/render.d.ts +2 -1
- package/lib/types/client/settings/sections/notes.d.ts +12 -0
- package/lib/types/client/settings/sections.d.ts +1 -0
- package/lib/types/client/settlement.d.ts +4 -1
- package/lib/types/client/styles.d.ts +15 -2
- package/lib/types/client/types.d.ts +120 -1
- package/lib/types/client/user-settings/IntegrationTokensPage.d.ts +21 -0
- package/lib/types/client/user-settings/PasswordSettingsPage.d.ts +19 -0
- package/lib/types/client/user-settings/UserSettingsDialog.d.ts +11 -2
- package/lib/types/client/user-settings/copy.d.ts +10 -1
- package/lib/types/config-resolvers/integration.d.ts +47 -0
- package/lib/types/config-resolvers/notes.d.ts +10 -0
- package/lib/types/config.d.ts +10 -0
- package/lib/types/index.d.ts +111 -1
- package/lib/types/integration/answer.d.ts +65 -0
- package/lib/types/integration/attachments.d.ts +85 -0
- package/lib/types/integration/contract.d.ts +196 -0
- package/lib/types/integration/host-runner.d.ts +44 -0
- package/lib/types/integration/http.d.ts +79 -0
- package/lib/types/integration/service.d.ts +120 -0
- package/lib/types/integration/transcript-reader.d.ts +100 -0
- package/lib/types/integration/transcript.d.ts +59 -0
- package/lib/types/personal-skills/index.d.ts +2 -2
- package/lib/types/personal-skills/paths.d.ts +15 -3
- package/lib/types/personal-skills/provider.d.ts +14 -2
- package/lib/types/personal-skills/service.d.ts +89 -14
- package/lib/types/prompt-notes.d.ts +37 -0
- package/lib/types/provenance/document-preview.d.ts +28 -0
- package/lib/types/provenance/file-preview.d.ts +69 -2
- package/lib/types/qa-tools/catalog.d.ts +1 -1
- package/lib/types/qa-tools/docs-tools.d.ts +160 -0
- package/lib/types/qa-tools/file-delete-gate.d.ts +42 -0
- package/lib/types/qa-tools/file-delete.d.ts +32 -0
- package/lib/types/secure-session.d.ts +52 -1
- package/lib/types/shared/documents.d.ts +20 -0
- package/lib/types/shared/integration-tokens.d.ts +24 -0
- package/lib/types/types.d.ts +263 -3
- package/lib/types/ui-mode.d.ts +18 -0
- package/lib/ui-mode.js +22 -0
- package/lib/ui-mode.js.map +1 -0
- package/package.json +12 -10
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { StoredSessionEvent } from "../admin/conversation-log.js";
|
|
2
|
+
/**
|
|
3
|
+
* Reading one answered turn out of a session log.
|
|
4
|
+
*
|
|
5
|
+
* The log is the durable record of the conversation, and it is what the
|
|
6
|
+
* integration API publishes: the live assistant stream is a presentation
|
|
7
|
+
* channel that can be abandoned mid-attempt, while `assistant/message` events
|
|
8
|
+
* are what was actually committed. A turn with tools in it commits several
|
|
9
|
+
* assistant messages — the intermediate steps carry tool calls and usually no
|
|
10
|
+
* prose — so the answer is the *last one that has text*, not simply the last
|
|
11
|
+
* one written. If a later empty message follows a text one, publishing the
|
|
12
|
+
* empty message would answer the bridge with nothing.
|
|
13
|
+
*/
|
|
14
|
+
/** The answer of one settled turn, plus how the turn ended. */
|
|
15
|
+
export interface QaTurnAnswer {
|
|
16
|
+
/** The publishable text; empty when the turn committed no prose at all. */
|
|
17
|
+
readonly answer: string;
|
|
18
|
+
/** True when the last committed assistant message was interrupted. */
|
|
19
|
+
readonly interrupted: boolean;
|
|
20
|
+
/** Durable seq of the message the answer came from, when there was one. */
|
|
21
|
+
readonly seq: number | null;
|
|
22
|
+
}
|
|
23
|
+
/** The empty answer: what a turn that committed nothing readable looks like. */
|
|
24
|
+
export declare const QA_EMPTY_ANSWER: QaTurnAnswer;
|
|
25
|
+
/**
|
|
26
|
+
* Project the answer of the turn that starts after `afterSeq`.
|
|
27
|
+
*
|
|
28
|
+
* Only events after the caller's own prompt are read: a chat is continued
|
|
29
|
+
* across questions, and picking the last assistant message of the whole log
|
|
30
|
+
* would hand question #2 the answer to question #1 whenever the new turn
|
|
31
|
+
* committed nothing.
|
|
32
|
+
*
|
|
33
|
+
* @param events - durable events, in any order.
|
|
34
|
+
* @param afterSeq - the seq of the prompt whose answer is wanted.
|
|
35
|
+
* @returns the publishable answer and whether the turn was interrupted.
|
|
36
|
+
*/
|
|
37
|
+
export declare function answerAfter(events: readonly StoredSessionEvent[], afterSeq: number): QaTurnAnswer;
|
|
38
|
+
/**
|
|
39
|
+
* Cut an answer down to the size the caller publishes.
|
|
40
|
+
*
|
|
41
|
+
* The bridge posts the answer into a ticket comment, which has a size budget
|
|
42
|
+
* of its own: an over-long comment is not rejected there, it is cut — and a cut
|
|
43
|
+
* that lands in the middle of a sentence, or worse inside a code block, reads
|
|
44
|
+
* like a broken answer. The cut therefore prefers the last paragraph break,
|
|
45
|
+
* then the last line, then the last sentence, and only then an arbitrary word
|
|
46
|
+
* boundary, and it appends an ellipsis so the reader sees that text is missing
|
|
47
|
+
* rather than finished. A boundary that would leave less than half the budget
|
|
48
|
+
* used is refused: a single early paragraph break must not turn a full answer
|
|
49
|
+
* into one line.
|
|
50
|
+
* @param answer - the settled answer, already trimmed.
|
|
51
|
+
* @param maxCharacters - the publish budget; a non-positive value never cuts.
|
|
52
|
+
* @returns the answer, or its bounded head.
|
|
53
|
+
*/
|
|
54
|
+
export declare function boundAnswer(answer: string, maxCharacters: number): string;
|
|
55
|
+
/**
|
|
56
|
+
* The seq of the newest user-authored message in a log, which is the floor
|
|
57
|
+
* `answerAfter` reads from when the caller has just submitted a prompt.
|
|
58
|
+
* Synthetic context (injected notes, skill payloads) is skipped: it is model
|
|
59
|
+
* input recorded as a user message, and one arriving after the answer would
|
|
60
|
+
* otherwise hide it.
|
|
61
|
+
* @param events - durable events, in any order.
|
|
62
|
+
* @returns the seq, or 0 when the log holds no human prompt yet.
|
|
63
|
+
*/
|
|
64
|
+
export declare function lastPromptSeq(events: readonly StoredSessionEvent[]): number;
|
|
65
|
+
//# sourceMappingURL=answer.d.ts.map
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { PromptContentPart } from "@deepseek-ai/dsh-api-session-controller";
|
|
2
|
+
import type { DocumentsFace } from "@yadsh/dsh-documents";
|
|
3
|
+
import type { PluginLogger } from "@yadsh/dsh-plugin-log";
|
|
4
|
+
import { QaIntegrationAttachmentError, type QaFileAttachment } from "./contract.js";
|
|
5
|
+
/**
|
|
6
|
+
* Bringing a ticket's attachments into the prompt.
|
|
7
|
+
*
|
|
8
|
+
* An image rides the prompt inline, because that is what the harness carries.
|
|
9
|
+
* A file does not: `PromptContentPart` names one as an opaque receipt minted by
|
|
10
|
+
* the harness's own upload service, which an external caller has no session to
|
|
11
|
+
* reach — and that service is what stores the copy the model later reads with
|
|
12
|
+
* its file tools. The bridge's non-image attachments are therefore read *here*
|
|
13
|
+
* and arrive as text: a text file is decoded, a document is extracted through
|
|
14
|
+
* the deployment's own document pipeline (the same runtime, providers and
|
|
15
|
+
* limits the `document_*` tools use), and the result joins the prompt as one
|
|
16
|
+
* more text part.
|
|
17
|
+
*
|
|
18
|
+
* That is a deliberate trade. It costs the fidelity of a format the pipeline
|
|
19
|
+
* cannot read — such a request is refused with the bridge's own fallback signal
|
|
20
|
+
* rather than answered with a question the model cannot see the attachment for.
|
|
21
|
+
* It buys a path that works in every deployment shape: nothing is written into
|
|
22
|
+
* a shared workspace, the staging directory is temporary and removed after the
|
|
23
|
+
* extraction, and no second converter is grown inside this plugin.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Plain-text formats the bridge sends; their bytes *are* the content, so
|
|
27
|
+
* decoding them needs no pipeline and cannot fail on a deployment.
|
|
28
|
+
*/
|
|
29
|
+
export declare const QA_INTEGRATION_TEXT_MEDIA_TYPES: readonly string[];
|
|
30
|
+
/**
|
|
31
|
+
* Documents the deployment's pipeline is asked to extract. PDF and the OOXML
|
|
32
|
+
* family are what the bridge's own filter lets through; whether a particular
|
|
33
|
+
* one is readable is the pipeline's answer, not a list kept here.
|
|
34
|
+
*/
|
|
35
|
+
export declare const QA_INTEGRATION_DOCUMENT_MEDIA_TYPES: readonly string[];
|
|
36
|
+
/** Total inlined attachment text per question, across every attachment. */
|
|
37
|
+
export declare const QA_INTEGRATION_MAX_ATTACHMENT_CHARS = 120000;
|
|
38
|
+
/** No single attachment may claim the whole budget. */
|
|
39
|
+
export declare const QA_INTEGRATION_MAX_ATTACHMENT_CHARS_PER_FILE = 60000;
|
|
40
|
+
/** A file name is a label in the prompt, not a path. */
|
|
41
|
+
export declare const QA_INTEGRATION_MAX_ATTACHMENT_NAME = 120;
|
|
42
|
+
/** Whether the bytes are the content: decoding needs only UTF-8. */
|
|
43
|
+
export declare function isIntegrationTextMediaType(mediaType: string): boolean;
|
|
44
|
+
/** Whether the attachment has to go through the document pipeline. */
|
|
45
|
+
export declare function isIntegrationDocumentMediaType(mediaType: string): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Reduce a caller-supplied file name to the label a prompt can carry.
|
|
48
|
+
*
|
|
49
|
+
* The name arrives from another system and is used as a log field, a heading in
|
|
50
|
+
* the prompt and the file name of one temporary file — so directory parts,
|
|
51
|
+
* traversal, control characters and an unbounded length all have to go before
|
|
52
|
+
* it is any of those. An empty result is named by its media type.
|
|
53
|
+
* @param raw - the name the caller sent.
|
|
54
|
+
* @param mediaType - its media type, for the fallback name.
|
|
55
|
+
* @returns a safe, non-empty label.
|
|
56
|
+
*/
|
|
57
|
+
export declare function safeAttachmentName(raw: string, mediaType: string): string;
|
|
58
|
+
/** What the extraction needs from the Host, as a port. */
|
|
59
|
+
export interface QaAttachmentDependencies {
|
|
60
|
+
/** The document pipeline, or undefined on a deployment without it. */
|
|
61
|
+
readonly documents: DocumentsFace | undefined;
|
|
62
|
+
/** The chat the prompt belongs to; the pipeline records artifacts against it. */
|
|
63
|
+
readonly sessionId: string;
|
|
64
|
+
readonly signal: AbortSignal;
|
|
65
|
+
readonly logger: PluginLogger;
|
|
66
|
+
/** Total inlined characters for this question. */
|
|
67
|
+
readonly maxChars?: number;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Turn the request's file attachments into prompt text parts.
|
|
71
|
+
*
|
|
72
|
+
* The parts are appended after the question, in the order the caller sent them,
|
|
73
|
+
* and the question itself is untouched. A file the Host cannot read refuses the
|
|
74
|
+
* whole request: answering with the question but not its attachments would hand
|
|
75
|
+
* the model a question about material it never received, which is exactly what
|
|
76
|
+
* the bridge's 415 fallback exists to avoid.
|
|
77
|
+
*
|
|
78
|
+
* @param files - the non-image attachments of one request.
|
|
79
|
+
* @param deps - pipeline, session and budgets.
|
|
80
|
+
* @returns one text part per readable attachment, plus a note when the budget cut the list.
|
|
81
|
+
*/
|
|
82
|
+
export declare function attachmentPromptParts(files: readonly QaFileAttachment[], deps: QaAttachmentDependencies): Promise<readonly PromptContentPart[]>;
|
|
83
|
+
/** Re-exported so the runner does not reach into the contract for one name. */
|
|
84
|
+
export { QaIntegrationAttachmentError };
|
|
85
|
+
//# sourceMappingURL=attachments.d.ts.map
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The integration API's wire contract: what an external application may post to
|
|
3
|
+
* `/qa/api/ask`, what it gets back, and the refusal vocabulary the HTTP layer
|
|
4
|
+
* maps onto status codes.
|
|
5
|
+
*
|
|
6
|
+
* The shape follows the ticket bridge specification the deployment was asked to
|
|
7
|
+
* implement (issue #177): a Markdown `answer`, the `sources` that back it, a
|
|
8
|
+
* `confidence` bucket, an `escalate` flag for "the assistant could not answer,
|
|
9
|
+
* send it to a specialist", and a `reason` for the logs. `chat_id` is the DSH
|
|
10
|
+
* session identity: sending it back as `session_id` continues the same
|
|
11
|
+
* conversation, which is the whole point of the bridge keeping it.
|
|
12
|
+
*/
|
|
13
|
+
import type { QaSourceReference } from "../provenance/types.js";
|
|
14
|
+
/** Why one integration request was refused, in the bridge's own vocabulary. */
|
|
15
|
+
export type QaIntegrationReason = "unauthorized" | "forbidden" | "not-found" | "invalid-request" | "unsupported-media" | "payload-too-large" | "rate-limited" | "busy" | "unavailable" | "timeout";
|
|
16
|
+
/**
|
|
17
|
+
* HTTP status per refusal. The mapping is part of the contract, not a detail:
|
|
18
|
+
* the bridge retries on 5xx and 429, falls back to a bodyless JSON request on
|
|
19
|
+
* 415, and treats 401/403 as a configuration error it must not spin on.
|
|
20
|
+
*/
|
|
21
|
+
export declare const QA_INTEGRATION_STATUS: Readonly<Record<QaIntegrationReason, number>>;
|
|
22
|
+
/** One refusal, carrying the status the HTTP layer answers with. */
|
|
23
|
+
export declare class QaIntegrationError extends Error {
|
|
24
|
+
readonly reason: QaIntegrationReason;
|
|
25
|
+
constructor(reason: QaIntegrationReason, message: string, options?: {
|
|
26
|
+
readonly cause?: unknown;
|
|
27
|
+
});
|
|
28
|
+
get status(): number;
|
|
29
|
+
}
|
|
30
|
+
/** One image the caller attached, already bounded and decoded to base64. */
|
|
31
|
+
export interface QaInlineImageAttachment {
|
|
32
|
+
readonly kind: "image";
|
|
33
|
+
/** A raster media type; anything else is refused before it gets here. */
|
|
34
|
+
readonly mediaType: string;
|
|
35
|
+
/** base64 bytes, exactly as the harness prompt content carries them. */
|
|
36
|
+
readonly data: string;
|
|
37
|
+
readonly name?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* One non-image file the caller attached, still as bytes.
|
|
41
|
+
*
|
|
42
|
+
* Files cannot ride a prompt the way images do: the harness carries a file as
|
|
43
|
+
* an opaque receipt minted by its own upload service, which an external caller
|
|
44
|
+
* has no session to reach. What the bridge sends instead is read on the Host —
|
|
45
|
+
* text files decoded, documents extracted through the deployment's document
|
|
46
|
+
* pipeline — and the resulting text joins the prompt, which is the same
|
|
47
|
+
* material the model would have read out of the stored copy.
|
|
48
|
+
*/
|
|
49
|
+
export interface QaFileAttachment {
|
|
50
|
+
readonly kind: "file";
|
|
51
|
+
readonly mediaType: string;
|
|
52
|
+
readonly name: string;
|
|
53
|
+
/** Raw bytes, bounded by the configured per-attachment ceiling. */
|
|
54
|
+
readonly bytes: Buffer;
|
|
55
|
+
}
|
|
56
|
+
/** One attachment of a parsed request, in the order the caller sent it. */
|
|
57
|
+
export type QaIntegrationAttachment = QaInlineImageAttachment | QaFileAttachment;
|
|
58
|
+
/**
|
|
59
|
+
* Why one attachment could not become prompt content, in the vocabulary the
|
|
60
|
+
* service maps onto HTTP.
|
|
61
|
+
*
|
|
62
|
+
* `unsupported` is the bridge's own fallback signal (415): repeat the question
|
|
63
|
+
* without attachments. `unavailable` is the same answer for a document the
|
|
64
|
+
* deployment cannot read right now, and the two are deliberately distinct only
|
|
65
|
+
* in the log — a caller sees one status either way.
|
|
66
|
+
*/
|
|
67
|
+
export type QaIntegrationAttachmentReason = "unsupported" | "unavailable";
|
|
68
|
+
/** One attachment the Host could not turn into prompt content. */
|
|
69
|
+
export declare class QaIntegrationAttachmentError extends Error {
|
|
70
|
+
readonly attachment: string;
|
|
71
|
+
readonly reason: QaIntegrationAttachmentReason;
|
|
72
|
+
constructor(attachment: string, reason: QaIntegrationAttachmentReason, message: string);
|
|
73
|
+
}
|
|
74
|
+
/** A parsed `POST /qa/api/ask` body, whichever encoding carried it. */
|
|
75
|
+
export interface QaAskRequest {
|
|
76
|
+
/** The question; at least one non-whitespace character. */
|
|
77
|
+
readonly message: string;
|
|
78
|
+
/** Product version the bridge extracted from the ticket, when it knew one. */
|
|
79
|
+
readonly version: string | null;
|
|
80
|
+
/** The chat to continue, or null for a new one. */
|
|
81
|
+
readonly sessionId: string | null;
|
|
82
|
+
/** Ticket metadata the operator sees in the log line. */
|
|
83
|
+
readonly context: QaAskContext;
|
|
84
|
+
readonly attachments: readonly QaIntegrationAttachment[];
|
|
85
|
+
}
|
|
86
|
+
/** The ticket metadata the bridge sends along; all of it is optional. */
|
|
87
|
+
export interface QaAskContext {
|
|
88
|
+
readonly ticketKey: string | null;
|
|
89
|
+
readonly reporter: string | null;
|
|
90
|
+
readonly reporterName: string | null;
|
|
91
|
+
}
|
|
92
|
+
/** A successful answer, in the exact shape the bridge publishes. */
|
|
93
|
+
export interface QaAskAnswer {
|
|
94
|
+
readonly chatId: string;
|
|
95
|
+
readonly answer: string;
|
|
96
|
+
readonly sources: readonly string[];
|
|
97
|
+
readonly confidence: "high" | "medium" | "low";
|
|
98
|
+
/** True when the assistant produced nothing worth publishing. */
|
|
99
|
+
readonly escalate: boolean;
|
|
100
|
+
/** Why it escalated — for the bridge's log, never for a client. */
|
|
101
|
+
readonly reason: string;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* One message of a conversation as an external caller reads it.
|
|
105
|
+
*
|
|
106
|
+
* The text is the same flattening the answer itself uses (prose only, with
|
|
107
|
+
* attachments named), so a caller that keeps asking questions and a caller
|
|
108
|
+
* that reads back the transcript never see two versions of one message.
|
|
109
|
+
*/
|
|
110
|
+
export interface QaIntegrationMessage {
|
|
111
|
+
/** Durable position of the message; the cursor for the next read. */
|
|
112
|
+
readonly seq: number;
|
|
113
|
+
readonly role: "user" | "assistant";
|
|
114
|
+
readonly text: string;
|
|
115
|
+
/** ISO timestamp, when the log recorded one for the event. */
|
|
116
|
+
readonly at?: string;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* A conversation as the caller that owns it reads it back.
|
|
120
|
+
*
|
|
121
|
+
* `sessions:read` exists for exactly this: a bridge that escalated a question,
|
|
122
|
+
* or that wants to show the specialist what was already said, reads the chat
|
|
123
|
+
* it started without asking it again. The read is bounded from both ends —
|
|
124
|
+
* `after` is the caller's own cursor, the newest `limit` messages are the
|
|
125
|
+
* window — so a long conversation costs one page, not one transcript per call.
|
|
126
|
+
*/
|
|
127
|
+
export interface QaIntegrationTranscript {
|
|
128
|
+
readonly chatId: string;
|
|
129
|
+
/** The window, oldest first. */
|
|
130
|
+
readonly messages: readonly QaIntegrationMessage[];
|
|
131
|
+
/** Newest seq the caller has now seen; 0 when the chat holds no messages. */
|
|
132
|
+
readonly lastSeq: number;
|
|
133
|
+
/** True when older messages exist below the window. */
|
|
134
|
+
readonly truncated: boolean;
|
|
135
|
+
}
|
|
136
|
+
/** `GET /qa/api/health` payload; the bridge only needs `ok`. */
|
|
137
|
+
export interface QaHealth {
|
|
138
|
+
readonly ok: boolean;
|
|
139
|
+
readonly version: string;
|
|
140
|
+
readonly models: readonly string[];
|
|
141
|
+
readonly uptimeS: number;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* What one answered turn hands back to the service: the answer text, the
|
|
145
|
+
* sources that back it and whether the turn was cut short. The runner owns the
|
|
146
|
+
* session mechanics; nothing about sessions leaks into the wire contract.
|
|
147
|
+
*/
|
|
148
|
+
export interface QaIntegrationTurn {
|
|
149
|
+
readonly chatId: string;
|
|
150
|
+
readonly answer: string;
|
|
151
|
+
readonly sources: readonly QaSourceReference[];
|
|
152
|
+
/** True when the turn was cancelled or the log recorded an interruption. */
|
|
153
|
+
readonly interrupted: boolean;
|
|
154
|
+
}
|
|
155
|
+
/** The one host capability the integration service needs, as a port. */
|
|
156
|
+
export interface QaIntegrationRunner {
|
|
157
|
+
/**
|
|
158
|
+
* Send one question into a chat and resolve once its turn has settled.
|
|
159
|
+
*
|
|
160
|
+
* Implementations must enforce that `userId` may use `chatId` — the service
|
|
161
|
+
* has already checked the credential, and whoever owns session creation owns
|
|
162
|
+
* the ownership boundary that goes with it.
|
|
163
|
+
*/
|
|
164
|
+
run(input: {
|
|
165
|
+
readonly userId: string;
|
|
166
|
+
/** The chat to continue, or null for a new conversation. */
|
|
167
|
+
readonly chatId: string | null;
|
|
168
|
+
readonly message: string;
|
|
169
|
+
readonly attachments: readonly QaIntegrationAttachment[];
|
|
170
|
+
readonly ticketKey: string | null;
|
|
171
|
+
readonly signal: AbortSignal;
|
|
172
|
+
/**
|
|
173
|
+
* Called as soon as the chat exists, before the turn settles. The service
|
|
174
|
+
* needs the identity of a chat whose answer it may never see: a timed-out
|
|
175
|
+
* question still has to hand the bridge a chat it can continue.
|
|
176
|
+
*/
|
|
177
|
+
readonly onChat?: (chatId: string) => void;
|
|
178
|
+
}): Promise<QaIntegrationTurn>;
|
|
179
|
+
/**
|
|
180
|
+
* Read back the messages of one conversation.
|
|
181
|
+
*
|
|
182
|
+
* The service has already authenticated the credential and established that
|
|
183
|
+
* the chat belongs to that account; the implementation reads the log and
|
|
184
|
+
* projects it, and reports an unreadable log rather than an empty answer.
|
|
185
|
+
*/
|
|
186
|
+
transcript(input: {
|
|
187
|
+
readonly chatId: string;
|
|
188
|
+
/** Exclusive lower bound: only messages after this seq are returned. */
|
|
189
|
+
readonly after: number;
|
|
190
|
+
/** Newest messages to return; older ones are reported as truncated. */
|
|
191
|
+
readonly limit: number;
|
|
192
|
+
}): Promise<QaIntegrationTranscript>;
|
|
193
|
+
/** Model ids the deployment can route right now, for the health payload. */
|
|
194
|
+
models(): Promise<readonly string[]>;
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
import type { PluginLogger } from "@yadsh/dsh-plugin-log";
|
|
3
|
+
import type { QaAccessService } from "../access/service.js";
|
|
4
|
+
import type { QaAccounts } from "../accounts/store.js";
|
|
5
|
+
import type { QaSessionLogReader } from "../admin/session-log.js";
|
|
6
|
+
import type { QaProvenanceHost } from "../provenance/host-store.js";
|
|
7
|
+
import type { QaPolicyAdmission } from "../secure-session.js";
|
|
8
|
+
import type { ResolvedQaSurfaceConfig } from "../types.js";
|
|
9
|
+
import type { DocumentsFace } from "@yadsh/dsh-documents";
|
|
10
|
+
import type { QaIntegrationRunner } from "./contract.js";
|
|
11
|
+
/**
|
|
12
|
+
* The session half of the integration API: open a chat, send one question,
|
|
13
|
+
* wait for the turn, read the answer.
|
|
14
|
+
*
|
|
15
|
+
* It is deliberately not a new turn engine. Opening a chat goes through the
|
|
16
|
+
* same three steps the browser's `createSession` takes — the deployment
|
|
17
|
+
* preflight, the per-user workspace, then `secureSession` — because that last
|
|
18
|
+
* call is what pins the QA tool policy, attaches the QA catalog and records
|
|
19
|
+
* the chat as attested. A question is admitted with
|
|
20
|
+
* `sessionController.prompt`, the same call the browser makes; the wait is
|
|
21
|
+
* `agent.whenIdle()`, which is the harness's own definition of "this turn is
|
|
22
|
+
* over"; the answer is read back from the durable log rather than from the
|
|
23
|
+
* live assistant stream, so what the bridge publishes is what was committed.
|
|
24
|
+
*/
|
|
25
|
+
export interface QaIntegrationRunnerOptions {
|
|
26
|
+
ctx: Context;
|
|
27
|
+
getConfig(): ResolvedQaSurfaceConfig;
|
|
28
|
+
/** The accounts store, or undefined while accounts are disabled. */
|
|
29
|
+
accounts(): QaAccounts | undefined;
|
|
30
|
+
admission: QaPolicyAdmission;
|
|
31
|
+
access: QaAccessService;
|
|
32
|
+
sessionLog: QaSessionLogReader;
|
|
33
|
+
provenance: QaProvenanceHost;
|
|
34
|
+
/**
|
|
35
|
+
* The deployment's document pipeline, resolved per call so a provider that
|
|
36
|
+
* installs later still counts. Absent on a deployment that has none, where
|
|
37
|
+
* document attachments are refused with the caller's own fallback signal.
|
|
38
|
+
*/
|
|
39
|
+
documents(): DocumentsFace | undefined;
|
|
40
|
+
logger: PluginLogger;
|
|
41
|
+
}
|
|
42
|
+
/** Build the runner for one plugin instance. */
|
|
43
|
+
export declare function createQaIntegrationRunner(options: QaIntegrationRunnerOptions): QaIntegrationRunner;
|
|
44
|
+
//# sourceMappingURL=host-runner.d.ts.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import type { WebServer } from "@deepseek-ai/dsh-host-webserver";
|
|
3
|
+
import type { PluginLogger } from "@yadsh/dsh-plugin-log";
|
|
4
|
+
import type { ResolvedQaSurfaceConfig } from "../types.js";
|
|
5
|
+
import { type QaAskRequest } from "./contract.js";
|
|
6
|
+
import type { QaIntegrationService } from "./service.js";
|
|
7
|
+
/** The bridge's own ceiling: five attachments per question. */
|
|
8
|
+
export declare const QA_INTEGRATION_MAX_FILES = 5;
|
|
9
|
+
export interface QaIntegrationRouteOptions {
|
|
10
|
+
config: ResolvedQaSurfaceConfig;
|
|
11
|
+
service: QaIntegrationService;
|
|
12
|
+
logger: PluginLogger;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Register the integration routes on the host web server.
|
|
16
|
+
* @param webServer - the route table owner.
|
|
17
|
+
* @param options - resolved config, the service and the plugin logger.
|
|
18
|
+
* @returns the disposer removing every route this call registered.
|
|
19
|
+
*/
|
|
20
|
+
export declare function registerQaIntegrationRoutes(webServer: Pick<WebServer, "register">, options: QaIntegrationRouteOptions): () => void;
|
|
21
|
+
/** The page size a caller gets when it does not ask for one. */
|
|
22
|
+
export declare const QA_INTEGRATION_DEFAULT_TRANSCRIPT_LIMIT = 50;
|
|
23
|
+
/**
|
|
24
|
+
* Parse one `POST /ask` body out of whichever encoding carried it.
|
|
25
|
+
*
|
|
26
|
+
* JSON is the primary form. Multipart exists because the bridge sends the
|
|
27
|
+
* ticket's attachments through it, and images ride a prompt inline in this
|
|
28
|
+
* harness — so an image-only multipart request is answered like a JSON one. A
|
|
29
|
+
* non-image attachment has nowhere to go: files reach a prompt as a receipt
|
|
30
|
+
* minted by the browser upload path, which an external caller has no session
|
|
31
|
+
* to mint. That request is refused with 415, which is exactly the signal the
|
|
32
|
+
* bridge's own fallback waits for: it repeats the question without
|
|
33
|
+
* attachments rather than failing.
|
|
34
|
+
*
|
|
35
|
+
* @param body - the raw request body.
|
|
36
|
+
* @param contentType - the request's content type, parameters included.
|
|
37
|
+
* @param config - resolved config, for the attachment ceilings.
|
|
38
|
+
* @returns the normalized request.
|
|
39
|
+
*/
|
|
40
|
+
export declare function parseAskBody(body: Buffer, contentType: string | undefined, config: ResolvedQaSurfaceConfig): QaAskRequest;
|
|
41
|
+
/** One image part of a multipart body, bounded as it was read. */
|
|
42
|
+
interface MultipartFile {
|
|
43
|
+
readonly filename: string;
|
|
44
|
+
readonly mediaType: string;
|
|
45
|
+
readonly bytes: Buffer;
|
|
46
|
+
}
|
|
47
|
+
interface MultipartBody {
|
|
48
|
+
readonly fields: ReadonlyMap<string, string>;
|
|
49
|
+
readonly files: readonly MultipartFile[];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Split a multipart body into its text fields and files.
|
|
53
|
+
*
|
|
54
|
+
* Hand-written and deliberately minimal: one request shape (a flat form of
|
|
55
|
+
* scalar fields and `files` parts), one boundary, no nested multiparts. The
|
|
56
|
+
* body is already bounded by the caller, so no part can grow without limit.
|
|
57
|
+
*
|
|
58
|
+
* @param body - the raw request body.
|
|
59
|
+
* @param boundary - the boundary parameter of the content type.
|
|
60
|
+
* @returns text fields and file parts, in the order they appeared.
|
|
61
|
+
*/
|
|
62
|
+
export declare function parseMultipart(body: Buffer, boundary: string): MultipartBody;
|
|
63
|
+
/**
|
|
64
|
+
* Read the whole request body under a byte ceiling.
|
|
65
|
+
* @param request - the incoming request.
|
|
66
|
+
* @param limit - accepted body size in bytes.
|
|
67
|
+
* @returns the body.
|
|
68
|
+
*/
|
|
69
|
+
export declare function readBody(request: IncomingMessage, limit: number): Promise<Buffer>;
|
|
70
|
+
/**
|
|
71
|
+
* One refusal, mapped onto its status.
|
|
72
|
+
*
|
|
73
|
+
* An unexpected error is answered as `unavailable` rather than leaking a stack
|
|
74
|
+
* trace: the bridge retries a 5xx, and the deployment's own log holds the
|
|
75
|
+
* detail.
|
|
76
|
+
*/
|
|
77
|
+
export declare function sendRefusal(response: ServerResponse, error: unknown): void;
|
|
78
|
+
export {};
|
|
79
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { PluginLogger } from "@yadsh/dsh-plugin-log";
|
|
2
|
+
import type { QaAccounts } from "../accounts/store.js";
|
|
3
|
+
import { type QaServiceTokenScope } from "../accounts/service-token.js";
|
|
4
|
+
import type { QaSourceReference } from "../provenance/types.js";
|
|
5
|
+
import type { ResolvedQaSurfaceConfig } from "../types.js";
|
|
6
|
+
import { type QaAskAnswer, type QaAskRequest, type QaHealth, type QaIntegrationRunner, type QaIntegrationTranscript } from "./contract.js";
|
|
7
|
+
/**
|
|
8
|
+
* The integration API's brain: credential, budget, and one answered turn.
|
|
9
|
+
*
|
|
10
|
+
* The service owns everything that must be identical for every transport —
|
|
11
|
+
* who is allowed to ask, how often, how many at once, what a refusal means and
|
|
12
|
+
* how an answered turn is shaped into the contract. The HTTP layer owns bytes
|
|
13
|
+
* and status codes, the runner owns sessions and agents. That split is what
|
|
14
|
+
* lets the mechanics be tested without a web server, a model or a database.
|
|
15
|
+
*
|
|
16
|
+
* Two limits, deliberately different in kind. Concurrency (`maxConcurrent`)
|
|
17
|
+
* protects the deployment: a bridge that fires a hundred questions must not
|
|
18
|
+
* open a hundred agent turns. Rate (`requestsPerMinute`) protects the account:
|
|
19
|
+
* one token cannot spend the deployment's whole budget. Both refuse with 429,
|
|
20
|
+
* which the bridge already retries.
|
|
21
|
+
*/
|
|
22
|
+
/** The bridge publishes a handful of citations; more is noise in a Jira note. */
|
|
23
|
+
export declare const QA_INTEGRATION_MAX_CITATIONS = 5;
|
|
24
|
+
/**
|
|
25
|
+
* One citation string per source, in the bridge's own style: the document plus
|
|
26
|
+
* where in it the evidence sits, so a reader can open the right page.
|
|
27
|
+
*
|
|
28
|
+
* @param sources - the turn's evidence, best first.
|
|
29
|
+
* @returns bounded citation strings.
|
|
30
|
+
*/
|
|
31
|
+
export declare function sourceCitations(sources: readonly QaSourceReference[]): readonly string[];
|
|
32
|
+
/** Who a presented credential turned out to be. */
|
|
33
|
+
export interface QaIntegrationIdentity {
|
|
34
|
+
readonly tokenId: string;
|
|
35
|
+
readonly userId: string;
|
|
36
|
+
readonly scopes: readonly QaServiceTokenScope[];
|
|
37
|
+
}
|
|
38
|
+
export interface QaIntegrationServiceOptions {
|
|
39
|
+
getConfig(): ResolvedQaSurfaceConfig;
|
|
40
|
+
/** The accounts store, or undefined while accounts are disabled. */
|
|
41
|
+
accounts(): QaAccounts | undefined;
|
|
42
|
+
runner: QaIntegrationRunner;
|
|
43
|
+
logger: PluginLogger;
|
|
44
|
+
/** Package version the health payload reports. */
|
|
45
|
+
version: string;
|
|
46
|
+
now?(): number;
|
|
47
|
+
}
|
|
48
|
+
export declare class QaIntegrationService {
|
|
49
|
+
private readonly deps;
|
|
50
|
+
private readonly now;
|
|
51
|
+
private readonly startedAt;
|
|
52
|
+
/** Recent request timestamps per token id, pruned to the rolling minute. */
|
|
53
|
+
private readonly recent;
|
|
54
|
+
private inFlight;
|
|
55
|
+
constructor(deps: QaIntegrationServiceOptions);
|
|
56
|
+
/** Requests currently inside `ask`; the console shows this as load. */
|
|
57
|
+
get busy(): number;
|
|
58
|
+
/**
|
|
59
|
+
* Resolve a bearer credential to its token, or refuse.
|
|
60
|
+
*
|
|
61
|
+
* A malformed, unknown, expired, revoked or disabled-account token is one
|
|
62
|
+
* answer — 401 — because telling them apart would let an unauthenticated
|
|
63
|
+
* caller enumerate token ids. A valid token missing the scope is 403: the
|
|
64
|
+
* caller proved who it is, and the refusal is about rights, not identity.
|
|
65
|
+
*/
|
|
66
|
+
authenticate(authorization: string | undefined, required: QaServiceTokenScope | null): QaIntegrationIdentity;
|
|
67
|
+
/**
|
|
68
|
+
* Answer one question, or refuse before a turn is ever opened.
|
|
69
|
+
*
|
|
70
|
+
* @param authorization - the raw `Authorization` header value.
|
|
71
|
+
* @param request - the parsed body.
|
|
72
|
+
* @param signal - the request's own lifetime; a dropped connection stops the wait.
|
|
73
|
+
* @returns the answer the bridge publishes.
|
|
74
|
+
*/
|
|
75
|
+
ask(authorization: string | undefined, request: QaAskRequest, signal: AbortSignal): Promise<QaAskAnswer>;
|
|
76
|
+
/**
|
|
77
|
+
* Read one conversation back for the application that started it.
|
|
78
|
+
*
|
|
79
|
+
* This is what `sessions:read` is for. A bridge that escalated a question,
|
|
80
|
+
* or that has to show a specialist what was already said, reads the chat it
|
|
81
|
+
* owns instead of asking the same question again — which would spend a turn
|
|
82
|
+
* and produce a second answer to a question that already has one.
|
|
83
|
+
*
|
|
84
|
+
* The ownership rule is the same one `ask` applies when it continues a chat:
|
|
85
|
+
* the account the token belongs to has to own the conversation. A chat that
|
|
86
|
+
* does not exist and a chat that belongs to somebody else get one answer, so
|
|
87
|
+
* an id alone never confirms that another account's conversation exists.
|
|
88
|
+
*
|
|
89
|
+
* @param authorization - the raw `Authorization` header value.
|
|
90
|
+
* @param query - the chat, the caller's cursor and the page size.
|
|
91
|
+
* @returns the window of messages, with the caller's new cursor.
|
|
92
|
+
*/
|
|
93
|
+
session(authorization: string | undefined, query: {
|
|
94
|
+
readonly chatId: string;
|
|
95
|
+
readonly after: number;
|
|
96
|
+
readonly limit: number;
|
|
97
|
+
}): Promise<QaIntegrationTranscript>;
|
|
98
|
+
/**
|
|
99
|
+
* Liveness and routing facts for the bridge's own UI.
|
|
100
|
+
* @param authorization - the raw `Authorization` header value.
|
|
101
|
+
* @returns the health payload; `ok` is true whenever it is answered at all.
|
|
102
|
+
*/
|
|
103
|
+
health(authorization: string | undefined): Promise<QaHealth>;
|
|
104
|
+
/** One refusal shaped as an answer: empty text, escalated, no citations. */
|
|
105
|
+
private escalated;
|
|
106
|
+
/** Refuse with `rate-limited` once a token spends its minute's budget. */
|
|
107
|
+
private assertRate;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The credential in one `Authorization` value, or null.
|
|
111
|
+
*
|
|
112
|
+
* `Bearer` is matched case-insensitively, as HTTP authentication schemes are,
|
|
113
|
+
* and the token itself is never trimmed: whitespace inside a credential is
|
|
114
|
+
* part of it, and accepting a padded variant would accept a token the store
|
|
115
|
+
* never minted.
|
|
116
|
+
* @param authorization - the raw header, when the caller sent one.
|
|
117
|
+
* @returns the token, or null.
|
|
118
|
+
*/
|
|
119
|
+
export declare function bearerToken(authorization: string | undefined): string | null;
|
|
120
|
+
//# sourceMappingURL=service.d.ts.map
|