feat(server): improve context management (#15448)

#### PR Dependency Tree


* **PR #15448** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added workspace artifact upload, browsing, removal, deduplication, and
library ownership support.
* Copilot now supports scoped document and artifact search, canvas
reading, live editor context, and frontend tools.
* Added scope and focus selectors with source-resolution receipts in
chat.
* Added embedding health, progress, synchronization, and retrieval
capabilities.
* Added BYOK policy visibility, provider restrictions, endpoint dialect
selection, and validation.
* Added delegated editor interactions and userdata document
authorization.

* **Bug Fixes**
* Improved attachment handling, cancellation, access control, retrieval
fallbacks, workspace synchronization, and configuration validation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2026-08-10 09:27:58 +08:00
committed by GitHub
parent 42322d13fe
commit ee899a267b
311 changed files with 20468 additions and 14806 deletions
@@ -1,7 +0,0 @@
mutation addContextBlob($options: AddContextBlobInput!) {
addContextBlob(options: $options) {
id
createdAt
status
}
}
@@ -1,3 +0,0 @@
mutation removeContextBlob($options: RemoveContextBlobInput!) {
removeContextBlob(options: $options)
}
@@ -1,12 +0,0 @@
mutation addContextCategory($options: AddContextCategoryInput!) {
addContextCategory(options: $options) {
id
createdAt
type
docs {
id
createdAt
status
}
}
}
@@ -1,3 +0,0 @@
mutation removeContextCategory($options: RemoveContextCategoryInput!) {
removeContextCategory(options: $options)
}
@@ -1,3 +0,0 @@
mutation createCopilotContext($workspaceId: String!, $sessionId: String!) {
createCopilotContext(workspaceId: $workspaceId, sessionId: $sessionId)
}
@@ -1,7 +0,0 @@
mutation addContextDoc($options: AddContextDocInput!) {
addContextDoc(options: $options) {
id
createdAt
status
}
}
@@ -1,3 +0,0 @@
mutation removeContextDoc($options: RemoveContextDocInput!) {
removeContextDoc(options: $options)
}
@@ -1,12 +0,0 @@
mutation addContextFile($content: Upload!, $options: AddContextFileInput!) {
addContextFile(content: $content, options: $options) {
id
createdAt
name
mimeType
chunkSize
error
status
blobId
}
}
@@ -1,3 +0,0 @@
mutation removeContextFile($options: RemoveContextFileInput!) {
removeContextFile(options: $options)
}
@@ -1,52 +0,0 @@
query listContextObject(
$workspaceId: String!
$sessionId: String!
$contextId: String!
) {
currentUser {
copilot(workspaceId: $workspaceId) {
contexts(sessionId: $sessionId, contextId: $contextId) {
blobs {
id
status
createdAt
}
docs {
id
status
createdAt
}
files {
id
name
mimeType
blobId
chunkSize
error
status
createdAt
}
tags {
type
id
docs {
id
status
createdAt
}
createdAt
}
collections {
type
id
docs {
id
status
createdAt
}
createdAt
}
}
}
}
}
@@ -1,10 +0,0 @@
query listContext($workspaceId: String!, $sessionId: String!) {
currentUser {
copilot(workspaceId: $workspaceId) {
contexts(sessionId: $sessionId) {
id
workspaceId
}
}
}
}
@@ -1,40 +0,0 @@
query matchContext(
$contextId: String
$workspaceId: String
$content: String!
$limit: SafeInt
$scopedThreshold: Float
$threshold: Float
) {
currentUser {
copilot(workspaceId: $workspaceId) {
contexts(contextId: $contextId) {
matchFiles(
content: $content
limit: $limit
scopedThreshold: $scopedThreshold
threshold: $threshold
) {
fileId
blobId
name
mimeType
chunk
content
distance
}
matchWorkspaceDocs(
content: $content
limit: $limit
scopedThreshold: $scopedThreshold
threshold: $threshold
) {
docId
chunk
content
distance
}
}
}
}
}
@@ -1,26 +0,0 @@
query matchWorkspaceDocs(
$contextId: String
$workspaceId: String
$content: String!
$limit: SafeInt
$scopedThreshold: Float
$threshold: Float
) {
currentUser {
copilot(workspaceId: $workspaceId) {
contexts(contextId: $contextId) {
matchWorkspaceDocs(
content: $content
limit: $limit
scopedThreshold: $scopedThreshold
threshold: $threshold
) {
docId
chunk
content
distance
}
}
}
}
}
@@ -1,27 +0,0 @@
query matchFiles(
$contextId: String
$workspaceId: String
$content: String!
$limit: SafeInt
$scopedThreshold: Float
$threshold: Float
) {
currentUser {
copilot(workspaceId: $workspaceId) {
contexts(contextId: $contextId) {
matchFiles(
content: $content
limit: $limit
scopedThreshold: $scopedThreshold
threshold: $threshold
) {
fileId
blobId
chunk
content
distance
}
}
}
}
}
@@ -1,3 +0,0 @@
mutation queueWorkspaceEmbedding($workspaceId: String!, $docId: [String!]!) {
queueWorkspaceEmbedding(workspaceId: $workspaceId, docId: $docId)
}
@@ -0,0 +1,9 @@
mutation addWorkspaceArtifact($workspaceId: String!, $blob: Upload!) {
addWorkspaceArtifact(workspaceId: $workspaceId, blob: $blob) {
artifactId
contentHash
mediaType
size
createdAt
}
}
@@ -1,10 +1,10 @@
query getWorkspaceEmbeddingFiles(
query getWorkspaceArtifacts(
$workspaceId: String!
$pagination: PaginationInput!
) {
workspace(id: $workspaceId) {
embedding {
files(pagination: $pagination) {
artifacts(pagination: $pagination) {
totalCount
pageInfo {
endCursor
@@ -12,10 +12,11 @@ query getWorkspaceEmbeddingFiles(
}
edges {
node {
fileId
artifactId
contentHash
fileName
blobId
mimeType
embeddingStatus
mediaType
size
createdAt
}
@@ -0,0 +1,6 @@
mutation removeWorkspaceArtifact(
$workspaceId: String!
$artifactId: String!
) {
removeWorkspaceArtifact(workspaceId: $workspaceId, artifactId: $artifactId)
}
@@ -1,10 +0,0 @@
mutation addWorkspaceEmbeddingFiles($workspaceId: String!, $blob: Upload!) {
addWorkspaceEmbeddingFiles(workspaceId: $workspaceId, blob: $blob) {
fileId
fileName
blobId
mimeType
size
createdAt
}
}
@@ -1,6 +0,0 @@
mutation removeWorkspaceEmbeddingFiles(
$workspaceId: String!
$fileId: String!
) {
removeWorkspaceEmbeddingFiles(workspaceId: $workspaceId, fileId: $fileId)
}
@@ -12,6 +12,7 @@ fragment CopilotChatHistory on CopilotHistories {
role
content
attachments
scopeSnapshot
streamObjects {
type
textDelta
+30 -288
View File
@@ -20,6 +20,7 @@ export const copilotChatHistoryFragment = `fragment CopilotChatHistory on Copilo
role
content
attachments
scopeSnapshot
streamObjects {
type
textDelta
@@ -1063,268 +1064,6 @@ export const uploadCommentAttachmentMutation = {
file: true,
};
export const addContextBlobMutation = {
id: 'addContextBlobMutation' as const,
op: 'addContextBlob',
query: `mutation addContextBlob($options: AddContextBlobInput!) {
addContextBlob(options: $options) {
id
createdAt
status
}
}`,
};
export const removeContextBlobMutation = {
id: 'removeContextBlobMutation' as const,
op: 'removeContextBlob',
query: `mutation removeContextBlob($options: RemoveContextBlobInput!) {
removeContextBlob(options: $options)
}`,
};
export const addContextCategoryMutation = {
id: 'addContextCategoryMutation' as const,
op: 'addContextCategory',
query: `mutation addContextCategory($options: AddContextCategoryInput!) {
addContextCategory(options: $options) {
id
createdAt
type
docs {
id
createdAt
status
}
}
}`,
};
export const removeContextCategoryMutation = {
id: 'removeContextCategoryMutation' as const,
op: 'removeContextCategory',
query: `mutation removeContextCategory($options: RemoveContextCategoryInput!) {
removeContextCategory(options: $options)
}`,
};
export const createCopilotContextMutation = {
id: 'createCopilotContextMutation' as const,
op: 'createCopilotContext',
query: `mutation createCopilotContext($workspaceId: String!, $sessionId: String!) {
createCopilotContext(workspaceId: $workspaceId, sessionId: $sessionId)
}`,
};
export const addContextDocMutation = {
id: 'addContextDocMutation' as const,
op: 'addContextDoc',
query: `mutation addContextDoc($options: AddContextDocInput!) {
addContextDoc(options: $options) {
id
createdAt
status
}
}`,
};
export const removeContextDocMutation = {
id: 'removeContextDocMutation' as const,
op: 'removeContextDoc',
query: `mutation removeContextDoc($options: RemoveContextDocInput!) {
removeContextDoc(options: $options)
}`,
};
export const addContextFileMutation = {
id: 'addContextFileMutation' as const,
op: 'addContextFile',
query: `mutation addContextFile($content: Upload!, $options: AddContextFileInput!) {
addContextFile(content: $content, options: $options) {
id
createdAt
name
mimeType
chunkSize
error
status
blobId
}
}`,
file: true,
};
export const removeContextFileMutation = {
id: 'removeContextFileMutation' as const,
op: 'removeContextFile',
query: `mutation removeContextFile($options: RemoveContextFileInput!) {
removeContextFile(options: $options)
}`,
};
export const listContextObjectQuery = {
id: 'listContextObjectQuery' as const,
op: 'listContextObject',
query: `query listContextObject($workspaceId: String!, $sessionId: String!, $contextId: String!) {
currentUser {
copilot(workspaceId: $workspaceId) {
contexts(sessionId: $sessionId, contextId: $contextId) {
blobs {
id
status
createdAt
}
docs {
id
status
createdAt
}
files {
id
name
mimeType
blobId
chunkSize
error
status
createdAt
}
tags {
type
id
docs {
id
status
createdAt
}
createdAt
}
collections {
type
id
docs {
id
status
createdAt
}
createdAt
}
}
}
}
}`,
};
export const listContextQuery = {
id: 'listContextQuery' as const,
op: 'listContext',
query: `query listContext($workspaceId: String!, $sessionId: String!) {
currentUser {
copilot(workspaceId: $workspaceId) {
contexts(sessionId: $sessionId) {
id
workspaceId
}
}
}
}`,
};
export const matchContextQuery = {
id: 'matchContextQuery' as const,
op: 'matchContext',
query: `query matchContext($contextId: String, $workspaceId: String, $content: String!, $limit: SafeInt, $scopedThreshold: Float, $threshold: Float) {
currentUser {
copilot(workspaceId: $workspaceId) {
contexts(contextId: $contextId) {
matchFiles(
content: $content
limit: $limit
scopedThreshold: $scopedThreshold
threshold: $threshold
) {
fileId
blobId
name
mimeType
chunk
content
distance
}
matchWorkspaceDocs(
content: $content
limit: $limit
scopedThreshold: $scopedThreshold
threshold: $threshold
) {
docId
chunk
content
distance
}
}
}
}
}`,
};
export const matchWorkspaceDocsQuery = {
id: 'matchWorkspaceDocsQuery' as const,
op: 'matchWorkspaceDocs',
query: `query matchWorkspaceDocs($contextId: String, $workspaceId: String, $content: String!, $limit: SafeInt, $scopedThreshold: Float, $threshold: Float) {
currentUser {
copilot(workspaceId: $workspaceId) {
contexts(contextId: $contextId) {
matchWorkspaceDocs(
content: $content
limit: $limit
scopedThreshold: $scopedThreshold
threshold: $threshold
) {
docId
chunk
content
distance
}
}
}
}
}`,
};
export const matchFilesQuery = {
id: 'matchFilesQuery' as const,
op: 'matchFiles',
query: `query matchFiles($contextId: String, $workspaceId: String, $content: String!, $limit: SafeInt, $scopedThreshold: Float, $threshold: Float) {
currentUser {
copilot(workspaceId: $workspaceId) {
contexts(contextId: $contextId) {
matchFiles(
content: $content
limit: $limit
scopedThreshold: $scopedThreshold
threshold: $threshold
) {
fileId
blobId
chunk
content
distance
}
}
}
}
}`,
};
export const queueWorkspaceEmbeddingMutation = {
id: 'queueWorkspaceEmbeddingMutation' as const,
op: 'queueWorkspaceEmbedding',
query: `mutation queueWorkspaceEmbedding($workspaceId: String!, $docId: [String!]!) {
queueWorkspaceEmbedding(workspaceId: $workspaceId, docId: $docId)
}`,
};
export const getCopilotHistoryIdsQuery = {
id: 'getCopilotHistoryIdsQuery' as const,
op: 'getCopilotHistoryIds',
@@ -1733,15 +1472,14 @@ export const submitTranscriptTaskMutation = {
file: true,
};
export const addWorkspaceEmbeddingFilesMutation = {
id: 'addWorkspaceEmbeddingFilesMutation' as const,
op: 'addWorkspaceEmbeddingFiles',
query: `mutation addWorkspaceEmbeddingFiles($workspaceId: String!, $blob: Upload!) {
addWorkspaceEmbeddingFiles(workspaceId: $workspaceId, blob: $blob) {
fileId
fileName
blobId
mimeType
export const addWorkspaceArtifactMutation = {
id: 'addWorkspaceArtifactMutation' as const,
op: 'addWorkspaceArtifact',
query: `mutation addWorkspaceArtifact($workspaceId: String!, $blob: Upload!) {
addWorkspaceArtifact(workspaceId: $workspaceId, blob: $blob) {
artifactId
contentHash
mediaType
size
createdAt
}
@@ -1749,13 +1487,13 @@ export const addWorkspaceEmbeddingFilesMutation = {
file: true,
};
export const getWorkspaceEmbeddingFilesQuery = {
id: 'getWorkspaceEmbeddingFilesQuery' as const,
op: 'getWorkspaceEmbeddingFiles',
query: `query getWorkspaceEmbeddingFiles($workspaceId: String!, $pagination: PaginationInput!) {
export const getWorkspaceArtifactsQuery = {
id: 'getWorkspaceArtifactsQuery' as const,
op: 'getWorkspaceArtifacts',
query: `query getWorkspaceArtifacts($workspaceId: String!, $pagination: PaginationInput!) {
workspace(id: $workspaceId) {
embedding {
files(pagination: $pagination) {
artifacts(pagination: $pagination) {
totalCount
pageInfo {
endCursor
@@ -1763,10 +1501,11 @@ export const getWorkspaceEmbeddingFilesQuery = {
}
edges {
node {
fileId
artifactId
contentHash
fileName
blobId
mimeType
embeddingStatus
mediaType
size
createdAt
}
@@ -1777,11 +1516,11 @@ export const getWorkspaceEmbeddingFilesQuery = {
}`,
};
export const removeWorkspaceEmbeddingFilesMutation = {
id: 'removeWorkspaceEmbeddingFilesMutation' as const,
op: 'removeWorkspaceEmbeddingFiles',
query: `mutation removeWorkspaceEmbeddingFiles($workspaceId: String!, $fileId: String!) {
removeWorkspaceEmbeddingFiles(workspaceId: $workspaceId, fileId: $fileId)
export const removeWorkspaceArtifactMutation = {
id: 'removeWorkspaceArtifactMutation' as const,
op: 'removeWorkspaceArtifact',
query: `mutation removeWorkspaceArtifact($workspaceId: String!, $artifactId: String!) {
removeWorkspaceArtifact(workspaceId: $workspaceId, artifactId: $artifactId)
}`,
};
@@ -3107,9 +2846,12 @@ export const workspaceByokSettingsQuery = {
entitled
serverEntitled
localEntitled
allowedProviders
customEndpointSupported
privateEndpointSupported
policy {
enabled
allowedProviders
customEndpointMode
privateEndpointSupported
}
catalog {
version
providers {
@@ -3137,10 +2879,10 @@ export const workspaceByokSettingsQuery = {
sortOrder
revision
definition {
version
endpoint {
kind
url
dialect
}
models {
modelId
@@ -6,9 +6,12 @@ query workspaceByokSettings($id: String!, $from: DateTime!, $to: DateTime!) {
entitled
serverEntitled
localEntitled
allowedProviders
customEndpointSupported
privateEndpointSupported
policy {
enabled
allowedProviders
customEndpointMode
privateEndpointSupported
}
catalog {
version
providers {
@@ -36,8 +39,7 @@ query workspaceByokSettings($id: String!, $from: DateTime!, $to: DateTime!) {
sortOrder
revision
definition {
version
endpoint { kind url }
endpoint { kind url dialect }
models {
modelId
enabled
File diff suppressed because it is too large Load Diff
+66
View File
@@ -4,6 +4,18 @@ export type RealtimeTopicName = keyof RealtimeTopicMap;
export const WORKSPACE_MEMBERS_REQUEST_TAKE_MAX = 100;
export interface RealtimeRequestMap {
'copilot.delegated.editor.upsert': {
input: DelegatedEditorLeaseInput;
output: { ok: true; expiresAt: number };
};
'copilot.delegated.editor.release': {
input: { clientId: string; editorStateId: string };
output: { ok: true };
};
'copilot.delegated.tool.respond': {
input: DelegatedToolResponse;
output: { accepted: boolean };
};
'workspace.access.get': {
input: { workspaceId: string };
output: { access: WorkspaceAccessSnapshot };
@@ -242,6 +254,10 @@ export type WorkspaceEmbeddingProgressReason =
| 'resync';
export interface RealtimeTopicMap {
'copilot.delegated.tool.requested': {
input: { clientId: string };
event: DelegatedToolRequest | DelegatedToolCancel;
};
'workspace.access.changed': {
input: { workspaceId: string };
event: { changed: true; reason: string };
@@ -320,6 +336,56 @@ export interface RealtimeTopicMap {
};
}
export type DelegatedToolName =
| 'frontend_get_editor_state'
| 'frontend_read_selection'
| 'frontend_read_nodes'
| 'frontend_snapshot_document';
export interface DelegatedEditorLeaseInput {
clientId: string;
sessionId: string;
workspaceId: string;
docId: string;
editorStateId: string;
mode: 'page' | 'edgeless';
readonly: boolean;
focused: boolean;
capabilities: DelegatedToolName[];
}
export interface DelegatedToolIdentity {
requestId: string;
runId: string;
toolCallId: string;
sessionId: string;
workspaceId: string;
docId: string;
clientId: string;
editorStateId: string;
}
export interface DelegatedToolRequest extends DelegatedToolIdentity {
type: 'request';
tool: DelegatedToolName;
args: Record<string, unknown>;
deadlineAt: number;
}
export interface DelegatedToolCancel extends DelegatedToolIdentity {
type: 'cancel';
reason: 'aborted' | 'timeout' | 'disconnect';
}
export interface DelegatedToolResponse extends DelegatedToolIdentity {
result?: unknown;
error?: {
code: string;
message: string;
retryable: boolean;
};
}
export type RealtimeRequestInputOf<Op extends RealtimeRequestName> =
RealtimeRequestMap[Op]['input'];
export type RealtimeRequestOutputOf<Op extends RealtimeRequestName> =