@yadsh/dsh-qa-surface 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +20 -22
  2. package/lib/attachment-access.js +147 -0
  3. package/lib/attachment-access.js.map +1 -0
  4. package/lib/chat-sharing/search.js +89 -0
  5. package/lib/chat-sharing/search.js.map +1 -0
  6. package/lib/chat-sharing/transport.js +2 -7
  7. package/lib/chat-sharing/transport.js.map +1 -1
  8. package/lib/client/QaSessionController.js +51 -9
  9. package/lib/client/QaSessionController.js.map +1 -1
  10. package/lib/client/QaSurface.js +4 -3
  11. package/lib/client/QaSurface.js.map +1 -1
  12. package/lib/client/components/QaChangelog.js +27 -0
  13. package/lib/client/components/QaChangelog.js.map +1 -1
  14. package/lib/client/components/QaRequestWait.js +15 -0
  15. package/lib/client/components/QaRequestWait.js.map +1 -0
  16. package/lib/client/index.js +3 -1
  17. package/lib/client/index.js.map +1 -1
  18. package/lib/client/project-session-state.js.map +1 -1
  19. package/lib/client/request-wait-styles.js +7 -0
  20. package/lib/client/request-wait-styles.js.map +1 -0
  21. package/lib/client/styles.js +2 -0
  22. package/lib/client/styles.js.map +1 -1
  23. package/lib/client/types.js.map +1 -1
  24. package/lib/client.js +14 -8
  25. package/lib/client.js.map +1 -1
  26. package/lib/filesystem-intent.js +39 -0
  27. package/lib/filesystem-intent.js.map +1 -0
  28. package/lib/forms/attachments.js +18 -2
  29. package/lib/forms/attachments.js.map +1 -1
  30. package/lib/index.js +46 -80
  31. package/lib/index.js.map +1 -1
  32. package/lib/qa-tools/catalog.js.map +1 -1
  33. package/lib/qa-tools/index.js.map +1 -1
  34. package/lib/qa-tools/request-user-form.js +1 -1
  35. package/lib/qa-tools/request-user-form.js.map +1 -1
  36. package/lib/request-scheduler.js +235 -0
  37. package/lib/request-scheduler.js.map +1 -0
  38. package/lib/secure-session.js +46 -52
  39. package/lib/secure-session.js.map +1 -1
  40. package/lib/session-create.js +125 -0
  41. package/lib/session-create.js.map +1 -0
  42. package/lib/typert.host.js +163 -116
  43. package/lib/typert.remote-client.d.ts +3 -3
  44. package/lib/typert.remote-client.js +159 -112
  45. package/lib/types/attachment-access.d.ts +29 -0
  46. package/lib/types/chat-sharing/search.d.ts +5 -0
  47. package/lib/types/client/QaSessionController.d.ts +12 -9
  48. package/lib/types/client/components/QaRequestWait.d.ts +6 -0
  49. package/lib/types/client/project-session-state.d.ts +2 -4
  50. package/lib/types/client/request-wait-styles.d.ts +2 -0
  51. package/lib/types/client/types.d.ts +3 -3
  52. package/lib/types/filesystem-intent.d.ts +12 -0
  53. package/lib/types/forms/attachments.d.ts +5 -2
  54. package/lib/types/index.d.ts +6 -13
  55. package/lib/types/qa-tools/catalog.d.ts +1 -1
  56. package/lib/types/qa-tools/index.d.ts +1 -1
  57. package/lib/types/qa-tools/request-user-form.d.ts +1 -1
  58. package/lib/types/request-scheduler.d.ts +28 -0
  59. package/lib/types/secure-session.d.ts +12 -26
  60. package/lib/types/session-create.d.ts +29 -0
  61. package/lib/types/types/session.d.ts +8 -0
  62. package/lib/types/user-workspace.d.ts +4 -6
  63. package/lib/user-workspace.js +13 -67
  64. package/lib/user-workspace.js.map +1 -1
  65. package/package.json +11 -9
@@ -0,0 +1,29 @@
1
+ import type { Context } from "@deepseek-ai/cordis";
2
+ interface SessionSource {
3
+ readonly id: unknown;
4
+ snapshotEvents?(): readonly unknown[];
5
+ }
6
+ export interface QaAttachmentRecovery {
7
+ readonly readEvents?: (sessionId: string) => Promise<readonly unknown[]>;
8
+ readonly restoreFiles?: (sessionId: string) => Promise<void>;
9
+ }
10
+ /** Exact attachment capabilities, owned by one admitted conversation. */
11
+ export declare class QaAttachmentAccess {
12
+ private readonly ctx;
13
+ private readonly recovery;
14
+ private readonly bindings;
15
+ constructor(ctx: Context, recovery?: QaAttachmentRecovery);
16
+ root(): string | undefined;
17
+ admit(session: SessionSource): Promise<void>;
18
+ inherit(session: SessionSource, parentId: string): void;
19
+ /** Only Host-admitted file blocks are authority; text and tool output are not. */
20
+ observe(sessionId: string, events: readonly unknown[]): void;
21
+ /** Called only after a durable form's bytes and session ownership are verified. */
22
+ grantFile(sessionId: string, file: string): void;
23
+ files(sessionId: string): readonly string[];
24
+ private publish;
25
+ revoke(sessionId: string): void;
26
+ dispose(): void;
27
+ }
28
+ export {};
29
+ //# sourceMappingURL=attachment-access.d.ts.map
@@ -0,0 +1,5 @@
1
+ import type { Context } from "@deepseek-ai/cordis";
2
+ import type { SessionSearchValue } from "@deepseek-ai/dsh-api-session-controller/types";
3
+ /** Apply account scope before the provider's ranking/limit, then recheck each read. */
4
+ export declare function searchQaChats(ctx: Context, query: string, visible: () => Promise<ReadonlySet<string>>, signal: AbortSignal): Promise<SessionSearchValue>;
5
+ //# sourceMappingURL=search.d.ts.map
@@ -54,13 +54,13 @@ export interface QaSessionControllerOptions {
54
54
  */
55
55
  readonly slashApi?: QaSlashApi;
56
56
  /**
57
- * The Host's live read of the request ceiling. Absent on a Host build that
58
- * predates it — and unanswered, or answered by a deployment that sets no
59
- * ceiling — the surface sends the way it always did: the ceiling is an
60
- * arrangement between browsers about how much this stand can take, never a
61
- * reason a question cannot be asked.
57
+ * Global admission snapshot. Managed queues accept sends even while full;
58
+ * legacy Hosts retain the courtesy refusal. A failed read still submits the
59
+ * prompt through the server's own admission boundary.
62
60
  */
63
61
  readonly queueStatus?: QaQueueStatusRemote;
62
+ /** Authorized per-chat queue status, polled while the Host driver is live. */
63
+ readonly requestStatus?: QaQueueStatusRemote;
64
64
  readonly storage?: StorageLike;
65
65
  /**
66
66
  * Browser file-upload service, resolved lazily: the page may not serve the
@@ -187,6 +187,10 @@ export declare class QaSessionController {
187
187
  /** Host-side question requests of the bound chat waiting for the operator. */
188
188
  private readonly hostQuestions;
189
189
  private readonly queueRemote;
190
+ private readonly requestRemote;
191
+ private requestWaitStatus;
192
+ private requestReadRevision;
193
+ private requestReading;
190
194
  /** Last send the stand had no room for; drives the request-ceiling dialog. */
191
195
  private requestQueueNotice;
192
196
  /** Set while a running turn is polled for parked requests. */
@@ -244,12 +248,11 @@ export declare class QaSessionController {
244
248
  /**
245
249
  * What the stand answers about its load, or null when it does not answer.
246
250
  *
247
- * Every failure here sends the question anyway. The ceiling exists to keep a
248
- * weak model from drowning, not to be a door a browser cannot open: an
249
- * unreadable count says nothing about the load, and refusing a visitor on
250
- * that would be a worse answer than the one they would have got.
251
+ * A failed status read does not bypass the server queue. Submit normally;
252
+ * the Host owns admission even when the browser cannot read its load.
251
253
  */
252
254
  private readQueueStatus;
255
+ private refreshRequestStatus;
253
256
  /**
254
257
  * Close the request-ceiling dialog. Nothing is retried and nothing is
255
258
  * dropped: the held-back question is still the composer's own text, so asking
@@ -0,0 +1,6 @@
1
+ import type { QaQueueStatus } from "../../types.js";
2
+ /** A queued turn belongs to the Host and starts without resending the question. */
3
+ export declare function QaRequestWait({ status, }: {
4
+ readonly status: QaQueueStatus | null;
5
+ }): import("react/jsx-runtime").JSX.Element | null;
6
+ //# sourceMappingURL=QaRequestWait.d.ts.map
@@ -40,10 +40,8 @@ export interface QaBoundProjectionInput {
40
40
  readonly questions: readonly QaPendingQuestion[];
41
41
  readonly operationError: string | null;
42
42
  /**
43
- * A send the stand had no room for. The dialog is the whole of what the
44
- * visitor learns: the question never reached the Host, so the transcript has
45
- * no row to carry the refusal on. Distinct from `queue`, the waiting rows of
46
- * this chat.
43
+ * Server admission status for this waiting chat, or a legacy Host's
44
+ * pre-send refusal. Distinct from `queue`, the next messages of this chat.
47
45
  */
48
46
  readonly requestQueue: QaQueueStatus | null;
49
47
  readonly policyReady: boolean;
@@ -0,0 +1,2 @@
1
+ export declare const requestWaitStyles = "\n.dsh-qa-request-wait{display:flex;align-items:center;gap:8px;align-self:flex-start;width:fit-content;max-width:100%;box-sizing:border-box;margin:0 0 10px;padding:6px 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.45;overflow-wrap:anywhere}\n.dsh-qa-request-wait__spinner{width:12px;height:12px;flex:none;border:1.5px solid var(--dsw-alias-border-l2);border-top-color:var(--dsw-alias-label-secondary);border-radius:50%;animation:dsh-qa-request-spin 1s linear infinite}\n@keyframes dsh-qa-request-spin{to{transform:rotate(360deg)}}\n@media(prefers-reduced-motion:reduce){.dsh-qa-request-wait__spinner{animation:none}}\n";
2
+ //# sourceMappingURL=request-wait-styles.d.ts.map
@@ -37,10 +37,10 @@ export type QaCreateSession = (token: string, subroleId: string | null, adminPre
37
37
  export type QaForkSession = (token: string, sourceId: string, answerSeq: number) => Promise<RemoteResult<string>>;
38
38
  /**
39
39
  * The Host's live read of the request ceiling (`qaSurface/queueStatus`), asked
40
- * per send: how many questions the stand is answering and whether one more
41
- * fits.
40
+ * per send and for the current waiting chat. Supplying a session id requires
41
+ * current account read access; global reads expose counts only.
42
42
  */
43
- export type QaQueueStatusRemote = () => Promise<RemoteResult<QaQueueStatus>>;
43
+ export type QaQueueStatusRemote = (token?: string, sessionId?: string) => Promise<RemoteResult<QaQueueStatus>>;
44
44
  /** Role selector plus administrator mutation channel. */
45
45
  export interface QaAccessApi {
46
46
  current(token: string): Promise<RemoteResult<QaCurrentAccess>>;
@@ -0,0 +1,12 @@
1
+ /** The Host tool contract, normalized once for path and storage policy. */
2
+ export type QaFilesystemIntent = {
3
+ readonly kind: "read";
4
+ readonly path: string;
5
+ readonly fileOnlyGrant: boolean;
6
+ } | {
7
+ readonly kind: "write";
8
+ readonly path: string;
9
+ readonly bytes: number;
10
+ };
11
+ export declare function qaFilesystemIntent(name: string, args: Record<string, unknown> | undefined): QaFilesystemIntent | undefined;
12
+ //# sourceMappingURL=filesystem-intent.d.ts.map
@@ -27,7 +27,8 @@ export declare function validateFormFile(name: string, data: Buffer): string;
27
27
  export declare class QaFormAttachmentAdapter {
28
28
  private readonly service;
29
29
  private readonly host;
30
- constructor(service: () => QaFormService, host: () => QaFormHostAttachments | undefined);
30
+ private readonly grantFile?;
31
+ constructor(service: () => QaFormService, host: () => QaFormHostAttachments | undefined, grantFile?: ((sessionId: string, file: string) => void) | undefined);
31
32
  available(): boolean;
32
33
  check(session: string, formId: string, uploadId: string): void;
33
34
  claim(session: string, uploadId: string): {
@@ -37,12 +38,14 @@ export declare class QaFormAttachmentAdapter {
37
38
  complete(session: string, formId: string, uploadId: string, raw: unknown, signal: AbortSignal, authorize: () => void): Promise<QaFormRecord>;
38
39
  fail(session: string, formId: string, uploadId: string): void;
39
40
  /** Exact saved bytes are re-projected through the Host on each successful resume. */
40
- readable(submission: QaFormSubmission): Promise<QaFormSubmission & {
41
+ readable(submission: QaFormSubmission, sessionId?: string): Promise<QaFormSubmission & {
41
42
  resources: {
42
43
  resource_id: string;
43
44
  read_path: string;
44
45
  }[];
45
46
  }>;
47
+ /** Rebuild exact grants from this chat's persisted, verified submissions. */
48
+ restore(sessionId: string): Promise<void>;
46
49
  }
47
50
  export {};
48
51
  //# sourceMappingURL=attachments.d.ts.map
@@ -79,6 +79,7 @@ export declare class QaSurface extends TypertRemoteService {
79
79
  private chatSharingStore;
80
80
  private readonly chatTransport;
81
81
  private readonly chatTurnGuard;
82
+ private readonly requestScheduler;
82
83
  private sharing;
83
84
  constructor(ctx: Context, entry?: Partial<QaSurfaceConfigRefs>);
84
85
  /** The quality store, opening its file the first time anyone needs it. */
@@ -237,19 +238,11 @@ export declare class QaSurface extends TypertRemoteService {
237
238
  */
238
239
  describe(): ResolvedQaSurfaceConfig;
239
240
  /**
240
- * Report how much of the deployment's request ceiling the stand is using, so
241
- * a browser holds its question back instead of opening another turn on a
242
- * model that is busy answering the ones it allows.
243
- *
244
- * The count is the Host's, not the browser's: a visitor cannot see another
245
- * account's chats, and the HTTP API's questions are invisible to every chat
246
- * view. Read-only, and a ceiling rather than a lock — a prompt rides the
247
- * native session RPC this plugin does not own, so the boundary is the
248
- * browser's own courtesy, and two questions sent in the same instant can
249
- * still overshoot by one. What the ceiling buys is that steady state, not a
250
- * strict bound.
251
- */
252
- queueStatus(): QaQueueStatus;
241
+ * Report server admission. Global reads expose counts only; a chat's FIFO
242
+ * position requires that account's current read access to the chat. The
243
+ * scheduler gates every root turn, including native and integration sends.
244
+ */
245
+ queueStatus(token?: string, sessionId?: string): QaQueueStatus;
253
246
  /**
254
247
  * Create a QA session from Host-owned inputs. With account workspaces on,
255
248
  * cwd is `<configured workspace>/.qa-users/<account UUID>` and the child is
@@ -13,7 +13,7 @@ export declare const QA_TOOL_CATALOG_VERSION = "4";
13
13
  /** What the catalog's definitions read, beyond the execution record. */
14
14
  export interface QaToolsSelfcheckDeps {
15
15
  readonly forms?: QaFormGate;
16
- readonly formProjection?: (submission: import("../forms/types.js").QaFormSubmission) => Promise<unknown>;
16
+ readonly formProjection?: (submission: import("../forms/types.js").QaFormSubmission, sessionId: string) => Promise<unknown>;
17
17
  readonly catalogVersion: string;
18
18
  readonly activationMode: string;
19
19
  /** Names the catalog contributes, in catalog order. */
@@ -5,7 +5,7 @@ import type { QaToolActivation } from "./types.js";
5
5
  export interface QaToolsOptions {
6
6
  readonly forms?: import("../forms/gate.js").QaFormGate;
7
7
  readonly formsEnabled?: () => boolean;
8
- readonly formProjection?: (submission: import("../forms/types.js").QaFormSubmission) => Promise<unknown>;
8
+ readonly formProjection?: (submission: import("../forms/types.js").QaFormSubmission, sessionId: string) => Promise<unknown>;
9
9
  readonly logger: PluginLogger;
10
10
  readonly dynamicActivation: boolean;
11
11
  readonly activationSkill: string;
@@ -1,5 +1,5 @@
1
1
  import type { ToolDefinition } from "@deepseek-ai/dsh-tools";
2
2
  import type { QaFormGate } from "../forms/gate.js";
3
3
  /** Build only; the QA activation manager owns registration and role admission. */
4
- export declare function createRequestUserFormTool(gate: QaFormGate, project?: (submission: import("../forms/types.js").QaFormSubmission) => Promise<unknown>): ToolDefinition;
4
+ export declare function createRequestUserFormTool(gate: QaFormGate, project?: (submission: import("../forms/types.js").QaFormSubmission, sessionId: string) => Promise<unknown>): ToolDefinition;
5
5
  //# sourceMappingURL=request-user-form.d.ts.map
@@ -0,0 +1,28 @@
1
+ import type { Context } from "@deepseek-ai/cordis";
2
+ import type { Agent } from "@deepseek-ai/dsh-agent";
3
+ import type { QaQueueStatus } from "./types.js";
4
+ /** A FIFO admission lease covers a complete root turn, including its experts. */
5
+ export declare class QaRequestScheduler {
6
+ private readonly limit;
7
+ private readonly roots;
8
+ private readonly now;
9
+ private readonly familyBusy;
10
+ private readonly active;
11
+ private readonly waiting;
12
+ private readonly completed;
13
+ private readonly retiring;
14
+ private closed;
15
+ constructor(limit: () => number, roots: () => readonly Agent[], now?: () => number, familyBusy?: (agent: Agent) => boolean);
16
+ install(ctx: Context): () => void;
17
+ acquire(agent: Agent, signal: AbortSignal): Promise<void>;
18
+ release(agent: Agent, completed?: boolean): void;
19
+ private pump;
20
+ status(sessionId?: string): QaQueueStatus;
21
+ private estimate;
22
+ dispose(): void;
23
+ }
24
+ /** Include background descendants whose root has already ended its own turn. */
25
+ export declare function qaRequestFamilyBusy(ctx: Context, root: Agent): boolean;
26
+ /** Older optional compositions may lack the registry, but cannot enforce a cap. */
27
+ export declare function qaRequestRoots(ctx: Context, required: boolean): readonly Agent[];
28
+ //# sourceMappingURL=request-scheduler.d.ts.map
@@ -1,5 +1,6 @@
1
1
  import type { Agent } from "@deepseek-ai/dsh-agent";
2
2
  import type { Context } from "@deepseek-ai/cordis";
3
+ import { type QaAttachmentRecovery } from "./attachment-access.js";
3
4
  import type { PluginLogger } from "@yadsh/dsh-plugin-log";
4
5
  import { type QaSessionFacts } from "./accounts/store.js";
5
6
  import type { QaIntegrationPrincipalBindings } from "./integration-principals.js";
@@ -71,13 +72,10 @@ export declare class QaPolicyAdmission {
71
72
  * one set answers for the whole chat.
72
73
  */
73
74
  private readonly attested;
75
+ private readonly rejected;
74
76
  private readonly questionWarnings;
75
77
  private readonly workspaceAccess;
76
- /**
77
- * Removers of the document input roots granted per session, so a grant lives
78
- * exactly as long as the workspace access that justified it.
79
- */
80
- private readonly documentInputRoots;
78
+ private readonly attachmentAccess;
81
79
  /**
82
80
  * Every name reachable anywhere in one QA conversation, keyed by session id.
83
81
  *
@@ -116,7 +114,7 @@ export declare class QaPolicyAdmission {
116
114
  * deliberately not a delegation); {@link secureSessionForUser} has no token
117
115
  * to compare, so it fills it here, against the ownership record it checked.
118
116
  */
119
- principals?: QaIntegrationPrincipalBindings | undefined);
117
+ principals?: QaIntegrationPrincipalBindings | undefined, attachmentRecovery?: QaAttachmentRecovery);
120
118
  /**
121
119
  * Add tools whose own executor resolves the QA principal server-side.
122
120
  * Enabling the contributing plugin is the operator decision; model arguments
@@ -130,29 +128,17 @@ export declare class QaPolicyAdmission {
130
128
  * newly-created, permanently unusable chat behind.
131
129
  */
132
130
  preflightDeployment(): void;
131
+ /** Keep a failed creation fenced even when its Host teardown cannot finish. */
132
+ rejectSession(sessionId: string): void;
133
+ /** Called only after failed creation confirms resource and ownership cleanup. */
134
+ forgetRejectedSession(sessionId: string): void;
133
135
  /**
134
- * The mounted attachment store's verbatim root, when the backend exposes
135
- * one. It sits outside every workspace, so the per-user fence would
136
- * otherwise deny the model the exact file an upload produced and the prompt
137
- * points it at. A backend without a resolvable root simply keeps the fence
138
- * closed — attachments then only work where reads are not fenced per user.
139
- * Source preview asks for the same root, so one upload is readable by both.
136
+ * The backend location used to validate exact file capabilities. The root
137
+ * itself grants no read access; only this conversation's Host-admitted
138
+ * user-message references and verified submitted form resources do.
140
139
  */
141
140
  attachmentRoot(): string | undefined;
142
- /**
143
- * Carry the fence's attachment exemption into the document pipeline.
144
- *
145
- * The guard above lets a single-file read reach the mounted store: an upload
146
- * is content-addressed, immutable, outside every workspace, and the prompt
147
- * hands the model exactly that path. The document pipeline keeps its own
148
- * read scope and knew nothing about the store, so `document_*` refused the
149
- * very file the model was allowed to read — the panel's own Word preview hit
150
- * the same wall. The grant is the missing hand-over, made where the access it
151
- * mirrors is made: per admitted session, and revoked with it.
152
- */
153
- private grantDocumentInputRoots;
154
- /** Drop one session's document grant, if the pipeline still holds it. */
155
- private revokeDocumentInputRoots;
141
+ grantAttachmentFile(sessionId: string, file: string): void;
156
142
  /**
157
143
  * The live agent for one session, resuming a Session the Host has not
158
144
  * materialized in this process.
@@ -0,0 +1,29 @@
1
+ import type { Context } from "@deepseek-ai/cordis";
2
+ import type { PluginLogger } from "@yadsh/dsh-plugin-log";
3
+ import type { QaStoredSessionEraser } from "./admin/session-log.js";
4
+ import type { QaSessionModelPolicy } from "./access/model-policy.js";
5
+ import type { ResolvedQaSurfaceConfig } from "./types.js";
6
+ export interface QaSessionCreateOptions {
7
+ readonly ctx: Context;
8
+ readonly config: ResolvedQaSurfaceConfig;
9
+ readonly logger: PluginLogger;
10
+ readonly sessionFiles: QaStoredSessionEraser;
11
+ reserve(id: string): {
12
+ readonly id: string;
13
+ } | undefined;
14
+ release(owner: string, id: string): void;
15
+ modelPolicy(id: string): QaSessionModelPolicy;
16
+ preflight(): void;
17
+ admit(id: string): Promise<unknown>;
18
+ attest(id: string, owner: string | undefined): void;
19
+ reject(id: string): void;
20
+ /** Drop failure-only metadata after resource and ownership cleanup succeeds. */
21
+ forget(id: string): void;
22
+ }
23
+ /**
24
+ * Admit inside the native factory's serial publication callback. A failure is
25
+ * rolled back by the Host's lifecycle owner before we erase this UUID's log;
26
+ * native preset composition and dynamic default-model hooks stay intact.
27
+ */
28
+ export declare function createQaSession(options: QaSessionCreateOptions): Promise<string>;
29
+ //# sourceMappingURL=session-create.d.ts.map
@@ -331,6 +331,14 @@ export interface QaQueueStatus {
331
331
  readonly active: number;
332
332
  /** Whether one more turn fits; false while no ceiling is set. */
333
333
  readonly full: boolean;
334
+ /** The Host enforces admission and starts queued turns automatically. */
335
+ readonly managed?: boolean;
336
+ readonly queued?: number;
337
+ /** Present only for the authorized chat whose status was requested. */
338
+ readonly waiting?: boolean;
339
+ readonly position?: number | null;
340
+ /** Approximate delay derived from completed turns, or null without a sample. */
341
+ readonly estimatedWaitMs?: number | null;
334
342
  }
335
343
  export interface QaSessionState {
336
344
  readonly chatAccess?: import("../chat-sharing/types.js").QaChatAccess;
@@ -10,6 +10,8 @@ export interface QaToolExecutionLike {
10
10
  export interface QaUserWorkspaceReadPolicy {
11
11
  /** Shared directories exposed only to read-only filesystem tools. */
12
12
  readonly sharedReadOnlyRoots?: readonly string[];
13
+ /** Exact canonical files derived from this conversation's trusted references. */
14
+ readonly attachmentFiles?: readonly string[];
13
15
  }
14
16
  export declare function pathIsInside(target: string, root: string): boolean;
15
17
  /**
@@ -31,12 +33,8 @@ export declare function existingQaUserWorkspace(workspaceRoot: string, userId: s
31
33
  * fences model-controlled file paths for both reads and writes, rejects tools
32
34
  * whose implementation can walk above cwd, and enforces bounded scratch use.
33
35
  *
34
- * `attachmentRoot` is the one deliberate read exemption: an uploaded file is
35
- * an immutable content-addressed copy the deployment stores outside every
36
- * workspace, and the model reaches it exactly as the harness intends (the
37
- * prompt carries the stored path). Reads of a single such file are allowed;
38
- * directory-wide tools stay confined because the store is shared by every
39
- * account, and writes are never exempted.
36
+ * `attachmentRoot` identifies the storage backend, never authority to read it.
37
+ * Only regular files in `attachmentFiles` may cross the workspace boundary.
40
38
  */
41
39
  export declare function qaUserWorkspaceDenial(execution: QaToolExecutionLike, root: string, attachmentRoot?: string, readPolicy?: QaUserWorkspaceReadPolicy): string | undefined;
42
40
  //# sourceMappingURL=user-workspace.d.ts.map
@@ -1,5 +1,6 @@
1
1
  import { chmodSync, lstatSync, mkdirSync, readdirSync, realpathSync, statSync, } from "node:fs";
2
2
  import path from "node:path";
3
+ import { qaFilesystemIntent } from "./filesystem-intent.js";
3
4
  /** Stable, non-workspace child used to keep account directories out of sight. */
4
5
  export const QA_USER_WORKSPACES_DIRECTORY = ".qa-users";
5
6
  /** Conservative fixed budgets; there is deliberately no browser override. */
@@ -105,49 +106,6 @@ function record(value) {
105
106
  ? value
106
107
  : undefined;
107
108
  }
108
- function requestedPaths(execution) {
109
- const args = record(execution.arguments);
110
- if (args === undefined)
111
- return [];
112
- switch (execution.name) {
113
- case "read":
114
- case "read_image":
115
- case "write":
116
- case "edit": {
117
- return typeof args.file_path === "string" ? [args.file_path] : [];
118
- }
119
- case "glob":
120
- case "grep": {
121
- return [typeof args.path === "string" ? args.path : "."];
122
- }
123
- case "str_replace_editor": {
124
- return typeof args.path === "string" ? [args.path] : [];
125
- }
126
- default:
127
- return [];
128
- }
129
- }
130
- /**
131
- * Whether the named path is only read. Directory-wide tools stay confined even
132
- * for reads: the attachment store is shared by every account, and only the
133
- * exact file an upload produced is meant to be reachable.
134
- */
135
- function readsSharedRoot(execution) {
136
- if (execution.name === "read" ||
137
- execution.name === "read_image" ||
138
- execution.name === "glob" ||
139
- execution.name === "grep") {
140
- return true;
141
- }
142
- return (execution.name === "str_replace_editor" &&
143
- record(execution.arguments)?.command === "view");
144
- }
145
- function readsSingleFile(execution) {
146
- if (execution.name === "read" || execution.name === "read_image")
147
- return true;
148
- return (execution.name === "str_replace_editor" &&
149
- record(execution.arguments)?.command === "view");
150
- }
151
109
  function directoryBytes(root, stopAfter) {
152
110
  let total = 0;
153
111
  const pending = [root];
@@ -167,24 +125,13 @@ function directoryBytes(root, stopAfter) {
167
125
  }
168
126
  return total;
169
127
  }
170
- function requestedWriteBytes(execution) {
171
- const args = record(execution.arguments);
172
- if (args === undefined)
173
- return 0;
174
- const values = [args.content, args.new_string, args.new_str];
175
- return values.reduce((total, value) => total + (typeof value === "string" ? Buffer.byteLength(value) : 0), 0);
176
- }
177
128
  /**
178
129
  * Monotonic guard used in addition to DSH's workspace-write sandbox. It
179
130
  * fences model-controlled file paths for both reads and writes, rejects tools
180
131
  * whose implementation can walk above cwd, and enforces bounded scratch use.
181
132
  *
182
- * `attachmentRoot` is the one deliberate read exemption: an uploaded file is
183
- * an immutable content-addressed copy the deployment stores outside every
184
- * workspace, and the model reaches it exactly as the harness intends (the
185
- * prompt carries the stored path). Reads of a single such file are allowed;
186
- * directory-wide tools stay confined because the store is shared by every
187
- * account, and writes are never exempted.
133
+ * `attachmentRoot` identifies the storage backend, never authority to read it.
134
+ * Only regular files in `attachmentFiles` may cross the workspace boundary.
188
135
  */
189
136
  export function qaUserWorkspaceDenial(execution, root, attachmentRoot, readPolicy = {}) {
190
137
  const args = record(execution.arguments);
@@ -202,35 +149,34 @@ export function qaUserWorkspaceDenial(execution, root, attachmentRoot, readPolic
202
149
  execution.name === "apply_patch") {
203
150
  return PATH_DENIAL;
204
151
  }
205
- const readsShared = readsSharedRoot(execution);
206
- const readsOneFile = readsSingleFile(execution);
152
+ const intent = qaFilesystemIntent(execution.name, args);
207
153
  try {
208
154
  const sharedReadOnlyRoots = (readPolicy.sharedReadOnlyRoots ?? []).map(canonicalCandidate);
209
- for (const requested of requestedPaths(execution)) {
155
+ for (const requested of intent === undefined ? [] : [intent.path]) {
210
156
  const absolute = path.isAbsolute(requested)
211
157
  ? requested
212
158
  : path.resolve(root, requested);
213
159
  const canonical = canonicalCandidate(absolute);
214
160
  if (pathIsInside(canonical, root))
215
161
  continue;
216
- if (readsShared &&
162
+ if (intent?.kind === "read" &&
217
163
  sharedReadOnlyRoots.some((readRoot) => pathIsInside(canonical, readRoot))) {
218
164
  continue;
219
165
  }
220
166
  // Resolved only for a path the fence would otherwise refuse, so the
221
167
  // common case pays no extra filesystem walk.
222
- if (readsOneFile &&
168
+ if (intent?.kind === "read" &&
169
+ intent.fileOnlyGrant &&
223
170
  attachmentRoot !== undefined &&
224
- pathIsInside(canonical, canonicalCandidate(attachmentRoot))) {
171
+ pathIsInside(canonical, canonicalCandidate(attachmentRoot)) &&
172
+ (readPolicy.attachmentFiles ?? []).some((file) => samePath(canonical, file)) &&
173
+ statSync(canonical).isFile()) {
225
174
  continue;
226
175
  }
227
176
  return PATH_DENIAL;
228
177
  }
229
- if (execution.name === "write" ||
230
- execution.name === "edit" ||
231
- (execution.name === "str_replace_editor" &&
232
- record(execution.arguments)?.command !== "view")) {
233
- const requested = requestedWriteBytes(execution);
178
+ if (intent?.kind === "write") {
179
+ const requested = intent.bytes;
234
180
  if (requested > QA_USER_WORKSPACE_MAX_WRITE_BYTES ||
235
181
  directoryBytes(root, QA_USER_WORKSPACE_MAX_BYTES) + requested >
236
182
  QA_USER_WORKSPACE_MAX_BYTES) {
@@ -1 +1 @@
1
- {"version":3,"file":"user-workspace.js","sourceRoot":"","sources":["../src/user-workspace.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"user-workspace.js","sourceRoot":"","sources":["../src/user-workspace.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yadsh/dsh-qa-surface",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "A focused end-user QA surface backed by native DeepSeek Harness sessions",
5
5
  "repository": {
6
6
  "type": "git",
@@ -103,7 +103,7 @@
103
103
  "node": "^22.19.0 || >=24.0.0"
104
104
  },
105
105
  "peerDependencies": {
106
- "@deepseek-ai/cordis": "^4.0.4",
106
+ "@deepseek-ai/cordis": "~4.0.4",
107
107
  "@deepseek-ai/dsh-agent": ">=0.1.7-rc.2 <0.3.0-0",
108
108
  "@deepseek-ai/dsh-agent-preset-registry": ">=0.1.7-rc.2 <0.3.0-0",
109
109
  "@deepseek-ai/dsh-api-gateway": ">=0.1.7-rc.2 <0.3.0-0",
@@ -137,15 +137,16 @@
137
137
  "openid-client": "6.8.8",
138
138
  "yaml": "^2.9.0",
139
139
  "zod": "^4.4.3",
140
- "@yadsh/dsh-audit-core": "^0.1.2",
141
- "@yadsh/dsh-audit-ui": "^0.1.3",
142
- "@yadsh/dsh-documents": "^0.8.1",
143
- "@yadsh/dsh-plugin-kit": "^0.6.0",
144
- "@yadsh/dsh-plugin-log": "^0.4.2"
140
+ "@yadsh/dsh-audit-core": "^0.1.3",
141
+ "@yadsh/dsh-plugin-kit": "^0.7.0",
142
+ "@yadsh/dsh-audit-ui": "^0.1.4",
143
+ "@yadsh/dsh-plugin-log": "^0.4.3",
144
+ "@yadsh/dsh-documents": "^0.8.2"
145
145
  },
146
146
  "devDependencies": {
147
147
  "@deepseek-ai/cordis": "4.0.5",
148
148
  "@deepseek-ai/dsh-agent": "0.2.1-rc.3",
149
+ "@deepseek-ai/dsh-agent-loop": "0.2.1-rc.3",
149
150
  "@deepseek-ai/dsh-agent-preset-registry": "0.2.1-rc.3",
150
151
  "@deepseek-ai/dsh-api-gateway": "0.2.1-rc.3",
151
152
  "@deepseek-ai/dsh-api-session-controller": "0.2.1-rc.3",
@@ -164,6 +165,7 @@
164
165
  "@deepseek-ai/dsh-permission-presets": "0.2.1-rc.3",
165
166
  "@deepseek-ai/dsh-scope": "0.2.1-rc.3",
166
167
  "@deepseek-ai/dsh-session": "0.2.1-rc.3",
168
+ "@deepseek-ai/dsh-session-projection": "0.2.1-rc.3",
167
169
  "@deepseek-ai/dsh-settings": "0.2.1-rc.3",
168
170
  "@deepseek-ai/dsh-skill": "0.2.1-rc.3",
169
171
  "@deepseek-ai/dsh-system-prompt": "0.2.1-rc.3",
@@ -177,7 +179,7 @@
177
179
  "@types/node": "^26.4.0",
178
180
  "@types/react": "18.3.28",
179
181
  "@types/react-dom": "18.3.7",
180
- "@vitest/coverage-v8": "^4.1.8",
182
+ "@vitest/coverage-v8": "^4.1.12",
181
183
  "eslint": "10.9.1",
182
184
  "jsdom": "^26.1.0",
183
185
  "katex": "^0.18.2",
@@ -186,7 +188,7 @@
186
188
  "react-dom": "18.3.1",
187
189
  "tsdown": "^0.22.14",
188
190
  "typescript": "^7.0.2",
189
- "vitest": "^4.1.11",
191
+ "vitest": "^4.1.12",
190
192
  "@yadsh/dsh-config": "^0.0.0",
191
193
  "@yadsh/dsh-plugin-scripts": "^0.0.0"
192
194
  },