@taskforcehq/taskforce 0.3.303 → 0.3.305
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 +140 -247
- package/dist/Taskforce.module.css +102 -5
- package/dist/TaskforceCore.js +635 -238
- package/dist/TaskforceCore.test.js +2243 -495
- package/dist/annotatedAttachments/service.d.ts +1 -1
- package/dist/annotatedAttachments/types.d.ts +1 -1
- package/dist/compat/workspaceSyncCompat.js +6 -0
- package/dist/components/features/AgentsModule.js +1 -1
- package/dist/components/features/AgentsModule.test.js +1 -1
- package/dist/components/features/DocumentIndex.d.ts +1 -1
- package/dist/components/features/DocumentIndex.js +1 -1
- package/dist/components/features/DocumentViewer.d.ts +1 -1
- package/dist/components/features/DocumentViewer.js +32 -9
- package/dist/components/features/DocumentWorkspace.d.ts +5 -22
- package/dist/components/features/DocumentWorkspace.js +12 -155
- package/dist/components/features/DocumentWorkspace.test.js +193 -6
- package/dist/components/features/TaskSettings.js +87 -41
- package/dist/components/features/TaskSettings.test.js +180 -40
- package/dist/components/features/documentWorkspaceModel.d.ts +24 -0
- package/dist/components/features/documentWorkspaceModel.js +57 -0
- package/dist/components/features/useDocumentWorkspaceController.d.ts +34 -0
- package/dist/components/features/useDocumentWorkspaceController.js +113 -0
- package/dist/components/task/TaskActionHeader.d.ts +2 -1
- package/dist/components/task/TaskActionHeader.js +3 -3
- package/dist/components/task/TaskActionHeader.test.js +8 -0
- package/dist/components/task/TaskCard.js +16 -1
- package/dist/components/task/TaskCard.test.js +31 -0
- package/dist/components/task/TaskForm.d.ts +9 -8
- package/dist/components/task/TaskForm.js +374 -219
- package/dist/components/task/TaskForm.test.js +482 -361
- package/dist/components/task/TaskKanban.d.ts +1 -1
- package/dist/components/task/TaskKanban.js +33 -161
- package/dist/components/task/TaskStatusActions.d.ts +6 -1
- package/dist/components/task/TaskStatusActions.js +8 -3
- package/dist/components/task/TaskStatusActions.test.js +9 -0
- package/dist/components/task/taskKanbanDropRules.d.ts +58 -0
- package/dist/components/task/taskKanbanDropRules.js +187 -0
- package/dist/components/task/taskKanbanDropRules.test.d.ts +1 -0
- package/dist/components/task/taskKanbanDropRules.test.js +114 -0
- package/dist/components/ui/Modal.d.ts +1 -1
- package/dist/components/ui/Modal.js +1 -0
- package/dist/components/views/AppShellHeader.js +5 -1
- package/dist/components/views/AppShellHeader.test.js +4 -0
- package/dist/components/views/PlanComparisonPage.d.ts +3 -2
- package/dist/components/views/PlanComparisonPage.js +9 -10
- package/dist/components/views/PlanComparisonPage.test.js +55 -41
- package/dist/components/views/PlansPage.d.ts +5 -1
- package/dist/components/views/PlansPage.js +150 -81
- package/dist/components/views/PlansPage.test.js +367 -41
- package/dist/components/views/StandaloneLayout.js +135 -54
- package/dist/components/views/WidgetView.js +11 -2
- package/dist/components/views/panels/FilterToolbar.test.js +6 -4
- package/dist/components/views/standalone/modals/AccountHubModal.d.ts +2 -14
- package/dist/components/views/standalone/modals/AccountHubModal.js +12 -14
- package/dist/components/views/standalone/modals/AccountHubModal.test.js +24 -1
- package/dist/components/views/standalone/modals/SyncStatusModal.js +1 -1
- package/dist/config/envSchema.js +1 -1
- package/dist/core/AiProfileService.d.ts +6 -1
- package/dist/core/AiProfileService.js +161 -16
- package/dist/core/AiProfileService.test.js +3 -1
- package/dist/core/AiProfiles.test.js +200 -0
- package/dist/core/AuthTokenService.test.d.ts +1 -0
- package/dist/core/AuthTokenService.test.js +78 -0
- package/dist/core/DeletedTaskLifecycleService.d.ts +2 -1
- package/dist/core/EntitlementsPolicy.test.js +75 -13
- package/dist/core/PlanEntitlementService.d.ts +9 -1
- package/dist/core/PlanEntitlementService.js +144 -19
- package/dist/core/PlanVersionPolicy.test.js +19 -26
- package/dist/core/SignupMonetizationSettings.test.js +75 -15
- package/dist/core/SystemAdmin.test.js +212 -56
- package/dist/core/TaskActivityService.d.ts +4 -1
- package/dist/core/TaskActivityService.js +45 -0
- package/dist/core/TaskAuditTimeline.test.js +52 -1
- package/dist/core/TaskTaxonomyValidation.test.js +53 -0
- package/dist/core/Taskforce.d.ts +18 -11
- package/dist/core/Taskforce.js +93 -12
- package/dist/core/WorkspacePlanMode.test.js +73 -8
- package/dist/core/shared.d.ts +2 -0
- package/dist/core/shared.js +11 -0
- package/dist/documentReviews/service.d.ts +1 -1
- package/dist/documentReviews/types.d.ts +1 -1
- package/dist/hooks/auth/useTaskforceAuthBootstrap.d.ts +48 -0
- package/dist/hooks/auth/useTaskforceAuthBootstrap.js +219 -0
- package/dist/hooks/sync/auth.d.ts +37 -0
- package/dist/hooks/sync/auth.js +63 -0
- package/dist/hooks/sync/bootstrap.d.ts +34 -0
- package/dist/hooks/sync/bootstrap.js +52 -0
- package/dist/hooks/sync/orchestratorShared.d.ts +36 -0
- package/dist/hooks/sync/orchestratorShared.js +209 -0
- package/dist/hooks/sync/orchestratorShared.test.d.ts +1 -0
- package/dist/hooks/sync/orchestratorShared.test.js +49 -0
- package/dist/hooks/sync/realtime.d.ts +25 -0
- package/dist/hooks/sync/realtime.js +60 -0
- package/dist/hooks/sync/recovery.d.ts +69 -0
- package/dist/hooks/sync/recovery.js +118 -0
- package/dist/hooks/sync/transfers.d.ts +123 -0
- package/dist/hooks/sync/transfers.js +435 -0
- package/dist/hooks/sync/useDataVersionRefresh.d.ts +16 -0
- package/dist/hooks/sync/useDataVersionRefresh.js +73 -0
- package/dist/hooks/ui/useResourceExport.d.ts +3 -1
- package/dist/hooks/ui/useResourceExport.js +5 -2
- package/dist/hooks/ui/useWorkflowTemplates.js +11 -6
- package/dist/hooks/useRealtimeSync.js +2 -1
- package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +8 -0
- package/dist/hooks/useSyncOrchestrator.context-assets.test.d.ts +1 -0
- package/dist/hooks/useSyncOrchestrator.context-assets.test.js +228 -0
- package/dist/hooks/useSyncOrchestrator.d.ts +12 -8
- package/dist/hooks/useSyncOrchestrator.js +388 -879
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +641 -6
- package/dist/hooks/useTaskMutations.d.ts +2 -1
- package/dist/hooks/useTaskMutations.js +57 -7
- package/dist/hooks/useTaskMutations.test.js +176 -0
- package/dist/hooks/useTaskforce.d.ts +13 -4
- package/dist/hooks/useTaskforce.js +302 -817
- package/dist/hooks/useTaskforce.sync-behavior.test.js +78 -0
- package/dist/hooks/useWorkspaceSyncController.d.ts +4 -1
- package/dist/hooks/useWorkspaceSyncController.js +33 -11
- package/dist/hooks/useWorkspaceSyncController.test.js +1 -0
- package/dist/hooks/workspace/useTaskforceApiRouting.test.js +17 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +166 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +434 -0
- package/dist/hooks/workspace/workspaceLocalState.d.ts +19 -0
- package/dist/hooks/workspace/workspaceLocalState.js +100 -0
- package/dist/hooks/workspace/workspaceLocalState.test.d.ts +1 -0
- package/dist/hooks/workspace/workspaceLocalState.test.js +59 -0
- package/dist/localization/locales/en-US.js +2 -2
- package/dist/mcp/adminWorkspaceRegistrar.d.ts +2 -0
- package/dist/mcp/adminWorkspaceRegistrar.js +154 -0
- package/dist/mcp/canonicalAssetHelpers.d.ts +70 -0
- package/dist/mcp/canonicalAssetHelpers.js +259 -0
- package/dist/mcp/clientIntegrations.d.ts +12 -1
- package/dist/mcp/clientIntegrations.js +161 -6
- package/dist/mcp/clientIntegrations.test.js +76 -4
- package/dist/mcp/collaborationRegistrar.d.ts +2 -0
- package/dist/mcp/collaborationRegistrar.js +71 -0
- package/dist/mcp/documentAssetRegistrar.d.ts +2 -0
- package/dist/mcp/documentAssetRegistrar.js +346 -0
- package/dist/mcp/documentHelpers.d.ts +100 -0
- package/dist/mcp/documentHelpers.js +161 -0
- package/dist/mcp/runtime.js +2455 -3925
- package/dist/mcp/runtime.test.js +240 -8
- package/dist/mcp/taskAttachmentHelpers.d.ts +41 -0
- package/dist/mcp/taskAttachmentHelpers.js +60 -0
- package/dist/mcp/taskAttachmentTypes.d.ts +37 -0
- package/dist/mcp/taskAttachmentTypes.js +1 -0
- package/dist/mcp/taskPlanningRegistrar.d.ts +2 -0
- package/dist/mcp/taskPlanningRegistrar.js +418 -0
- package/dist/mcp/toolCatalog.d.ts +35 -0
- package/dist/mcp/toolCatalog.js +3 -0
- package/dist/plugins/vite.js +1 -1
- package/dist/runtime/appGateDefinitions.d.ts +1 -1
- package/dist/runtime/appGateDefinitions.js +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +46 -3
- package/dist/server/index.test.js +12 -1
- package/dist/server/routes/admin.d.ts +1 -5
- package/dist/server/routes/admin.js +151 -80
- package/dist/server/routes/annotatedAttachments.d.ts +1 -1
- package/dist/server/routes/annotatedAttachments.js +1 -1
- package/dist/server/routes/auth.d.ts +1 -4
- package/dist/server/routes/auth.js +59 -64
- package/dist/server/routes/authSupport.d.ts +30 -0
- package/dist/server/routes/authSupport.js +81 -0
- package/dist/server/routes/billing.d.ts +1 -1
- package/dist/server/routes/billing.js +122 -255
- package/dist/server/routes/billing.test.js +257 -86
- package/dist/server/routes/documentReviews.d.ts +1 -1
- package/dist/server/routes/documentReviews.js +1 -1
- package/dist/server/routes/documents.d.ts +4 -2
- package/dist/server/routes/documents.js +73 -7
- package/dist/server/routes/primitives.d.ts +11 -0
- package/dist/server/routes/primitives.js +73 -0
- package/dist/server/routes/resources.d.ts +1 -1
- package/dist/server/routes/resources.js +1 -1
- package/dist/server/routes/shared.d.ts +31 -1
- package/dist/server/routes/shared.js +26 -0
- package/dist/server/routes/sync.d.ts +1 -1
- package/dist/server/routes/sync.integration.test.js +20 -0
- package/dist/server/routes/sync.js +111 -238
- package/dist/server/routes/tasks.d.ts +1 -1
- package/dist/server/routes/tasks.js +50 -8
- package/dist/server/routes/workspaces.d.ts +1 -1
- package/dist/server/routes/workspaces.js +1 -1
- package/dist/server/routes.d.ts +2 -9
- package/dist/server/routes.js +84 -181
- package/dist/server/routes.test.js +912 -32
- package/dist/shared/taskActor.d.ts +10 -0
- package/dist/shared/taskActor.js +1 -0
- package/dist/sync/collaborationSyncPayload.js +4 -0
- package/dist/sync/collaborationSyncPayload.test.js +8 -0
- package/dist/sync/collaborationSyncState.d.ts +3 -1
- package/dist/sync/collaborationSyncState.js +19 -0
- package/dist/sync/contentSyncState.js +28 -17
- package/dist/sync/syncApplyHandlers.d.ts +87 -1
- package/dist/sync/syncApplyHandlers.js +370 -8
- package/dist/sync/syncApplyHandlers.test.d.ts +1 -0
- package/dist/sync/syncApplyHandlers.test.js +379 -0
- package/dist/sync/syncService.d.ts +9 -1
- package/dist/sync/syncService.js +44 -2
- package/dist/sync/syncService.test.js +62 -0
- package/dist/sync/workspacePullFeed.d.ts +1 -0
- package/dist/sync/workspacePullFeed.js +66 -59
- package/dist/sync/workspacePullFeed.test.js +72 -4
- package/dist/sync/workspaceSyncModel.d.ts +25 -1
- package/dist/sync/workspaceSyncModel.js +77 -3
- package/dist/sync/workspaceSyncModel.test.js +153 -0
- package/dist/sync/workspaceSyncState.d.ts +2 -0
- package/dist/sync/workspaceSyncState.js +1 -0
- package/dist/types.d.ts +2 -10
- package/dist/ui/assets/AgentsModule-N2MnQBZk.js +1 -0
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-C_TmXZwA.js → AnnotatedAttachmentWorkspace-BG6P_GVW.js} +1 -1
- package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +1 -0
- package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +252 -0
- package/dist/ui/assets/{InitiativesModule-4-Rh2K2n.js → InitiativesModule-Dhm7M8e7.js} +1 -1
- package/dist/ui/assets/{PlansPage-BlVC_lRq.css → PlansPage-C2dd2n1X.css} +1 -1
- package/dist/ui/assets/PlansPage-Cq0zXj3I.js +1 -0
- package/dist/ui/assets/TaskSettings-ln0aF7xc.js +9 -0
- package/dist/ui/assets/{WorkflowsModule-CGk9s3NJ.js → WorkflowsModule-BcS4afRn.js} +1 -1
- package/dist/ui/assets/index-C2-OXZV7.css +1 -0
- package/dist/ui/assets/index-DcSI5F86.js +6 -0
- package/dist/ui/assets/{vendor-icons-pWocEipT.js → vendor-icons-BSUaRwF8.js} +1 -1
- package/dist/ui/index.html +4 -4
- package/dist/utils/commercialLifecyclePresentation.d.ts +19 -0
- package/dist/utils/commercialLifecyclePresentation.js +196 -0
- package/dist/utils/taskActivity.js +4 -0
- package/dist/utils/taskActivity.test.js +25 -1
- package/package.json +10 -1
- package/scripts/check-cloud-mcp.mjs +83 -0
- package/scripts/playwright-auth.sh +9 -1
- package/scripts/run-billing-performance-smoke.sh +24 -0
- package/dist/ui/assets/AgentsModule-CpsTmrIW.js +0 -1
- package/dist/ui/assets/DocumentWorkspace-C3GyzrWm.js +0 -250
- package/dist/ui/assets/DocumentWorkspace-C_8T8oz-.css +0 -1
- package/dist/ui/assets/PlansPage-BP7AYOqr.js +0 -1
- package/dist/ui/assets/TaskSettings-BOC5F6Ag.js +0 -8
- package/dist/ui/assets/index-CLIMgR6g.js +0 -6
- package/dist/ui/assets/index-DneETxcu.css +0 -1
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { bindDomainTool } from './toolCatalog.js';
|
|
2
|
+
function stringProperty(description, enumValues) {
|
|
3
|
+
return enumValues ? { type: 'string', description, enum: enumValues } : { type: 'string', description };
|
|
4
|
+
}
|
|
5
|
+
function numberProperty(description) {
|
|
6
|
+
return { type: 'number', description };
|
|
7
|
+
}
|
|
8
|
+
function booleanProperty(description) {
|
|
9
|
+
return { type: 'boolean', description };
|
|
10
|
+
}
|
|
11
|
+
function arrayProperty(description, items) {
|
|
12
|
+
return { type: 'array', description, items };
|
|
13
|
+
}
|
|
14
|
+
export function registerDocumentAssetTools(registerTool, executeTool) {
|
|
15
|
+
const register = (name, buildDescriptor) => {
|
|
16
|
+
registerTool({
|
|
17
|
+
name,
|
|
18
|
+
buildDescriptor: (context) => {
|
|
19
|
+
const descriptor = buildDescriptor(context);
|
|
20
|
+
return {
|
|
21
|
+
name,
|
|
22
|
+
description: descriptor.description,
|
|
23
|
+
inputSchema: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
properties: descriptor.inputSchema.properties,
|
|
26
|
+
...(descriptor.inputSchema.required ? { required: descriptor.inputSchema.required } : {}),
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
handler: bindDomainTool(executeTool, name),
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
register('get_task_attachments', (context) => ({
|
|
34
|
+
description: context.describeTool('List attachment descriptors for one task. This returns metadata only; use read_task_attachment for file contents. For canonical documents, prefer get_document and read_document_content for the document-focused workflow.'),
|
|
35
|
+
inputSchema: {
|
|
36
|
+
properties: {
|
|
37
|
+
taskId: stringProperty('Task ID or task reference such as T-123.'),
|
|
38
|
+
},
|
|
39
|
+
required: ['taskId'],
|
|
40
|
+
},
|
|
41
|
+
}));
|
|
42
|
+
register('read_task_attachment', (context) => ({
|
|
43
|
+
description: context.describeTool('Read one task attachment by attachmentId. Prefer this tool over opening attachment path URLs directly. For a canonical document attachment, this response also points toward the document-specific workflow.'),
|
|
44
|
+
inputSchema: {
|
|
45
|
+
properties: {
|
|
46
|
+
taskId: stringProperty('Task ID or task reference such as T-123.'),
|
|
47
|
+
attachmentId: stringProperty('Attachment descriptor id from get_task_attachments.'),
|
|
48
|
+
maxChars: numberProperty('Optional max inline character count for readable text attachments.'),
|
|
49
|
+
},
|
|
50
|
+
required: ['taskId', 'attachmentId'],
|
|
51
|
+
},
|
|
52
|
+
}));
|
|
53
|
+
register('search_images', (context) => ({
|
|
54
|
+
description: context.describeTool('Search image assets by keyword, title, filename, caption, or I-123 reference. Use this before get_image when you only know a rough identifier.'),
|
|
55
|
+
inputSchema: {
|
|
56
|
+
properties: {
|
|
57
|
+
query: stringProperty('Search term for image lookup, including I-123 references.'),
|
|
58
|
+
limit: numberProperty('Max images to return.'),
|
|
59
|
+
},
|
|
60
|
+
required: ['query'],
|
|
61
|
+
},
|
|
62
|
+
}));
|
|
63
|
+
register('get_image', (context) => ({
|
|
64
|
+
description: context.describeTool('Get a single image by asset id or image reference such as I-123. Use this first when the user references an image, then use get_annotated_attachment_sessions and read_annotated_attachment_payload for annotation details.'),
|
|
65
|
+
inputSchema: {
|
|
66
|
+
properties: {
|
|
67
|
+
id: stringProperty('Image asset id or image reference such as I-123.'),
|
|
68
|
+
},
|
|
69
|
+
required: ['id'],
|
|
70
|
+
},
|
|
71
|
+
}));
|
|
72
|
+
register('search_documents', (context) => ({
|
|
73
|
+
description: context.describeTool('Search document assets by keyword, title, filename, caption, or D-123 reference. Use this before get_document when you only know a rough identifier. Search results guide the next step into get_document and read_document_content.'),
|
|
74
|
+
inputSchema: {
|
|
75
|
+
properties: {
|
|
76
|
+
query: stringProperty('Search term for document lookup, including D-123 references.'),
|
|
77
|
+
limit: numberProperty('Max documents to return.'),
|
|
78
|
+
},
|
|
79
|
+
required: ['query'],
|
|
80
|
+
},
|
|
81
|
+
}));
|
|
82
|
+
register('get_document', (context) => ({
|
|
83
|
+
description: context.describeTool('Get a single canonical document by asset id or document reference such as D-123. Use this first when the user references a document, then call read_document_content to inspect what it is reviewing.'),
|
|
84
|
+
inputSchema: {
|
|
85
|
+
properties: {
|
|
86
|
+
id: stringProperty('Document asset id or document reference such as D-123.'),
|
|
87
|
+
},
|
|
88
|
+
required: ['id'],
|
|
89
|
+
},
|
|
90
|
+
}));
|
|
91
|
+
register('read_document_content', (context) => ({
|
|
92
|
+
description: context.describeTool('Read the canonical document body for one document. Use this when an AI needs to inspect what it is reviewing before adding review comments.'),
|
|
93
|
+
inputSchema: {
|
|
94
|
+
properties: {
|
|
95
|
+
documentId: stringProperty('Document asset id or document reference such as D-123.'),
|
|
96
|
+
maxChars: numberProperty('Optional max inline character count for readable documents.'),
|
|
97
|
+
},
|
|
98
|
+
required: ['documentId'],
|
|
99
|
+
},
|
|
100
|
+
}));
|
|
101
|
+
register('get_annotated_attachment_sessions', (context) => ({
|
|
102
|
+
description: context.describeTool('List saved annotation sessions for one image. Use this after get_image when you need the saved annotation sessions before opening one payload.'),
|
|
103
|
+
inputSchema: {
|
|
104
|
+
properties: {
|
|
105
|
+
imageId: stringProperty('Image asset id or image reference such as I-123.'),
|
|
106
|
+
},
|
|
107
|
+
required: ['imageId'],
|
|
108
|
+
},
|
|
109
|
+
}));
|
|
110
|
+
register('read_annotated_attachment_payload', (context) => ({
|
|
111
|
+
description: context.describeTool('Read the structured payload for one annotated attachment session, including markers, geometry, and derived crop hints.'),
|
|
112
|
+
inputSchema: {
|
|
113
|
+
properties: {
|
|
114
|
+
sessionId: stringProperty('Annotated attachment session id.'),
|
|
115
|
+
},
|
|
116
|
+
required: ['sessionId'],
|
|
117
|
+
},
|
|
118
|
+
}));
|
|
119
|
+
register('create_annotated_attachment_session', (context) => ({
|
|
120
|
+
description: context.describeTool('Create a new annotation session for one image. Use this before adding markers when no saved session exists yet.'),
|
|
121
|
+
inputSchema: {
|
|
122
|
+
properties: {
|
|
123
|
+
imageId: stringProperty('Image asset id or image reference such as I-123.'),
|
|
124
|
+
title: stringProperty('Optional session title.'),
|
|
125
|
+
},
|
|
126
|
+
required: ['imageId'],
|
|
127
|
+
},
|
|
128
|
+
}));
|
|
129
|
+
register('update_annotated_attachment_session', (context) => ({
|
|
130
|
+
description: context.describeTool('Update an existing annotation session without changing its marker list directly.'),
|
|
131
|
+
inputSchema: {
|
|
132
|
+
properties: {
|
|
133
|
+
sessionId: stringProperty('Annotated attachment session id.'),
|
|
134
|
+
title: stringProperty('Updated session title.'),
|
|
135
|
+
},
|
|
136
|
+
required: ['sessionId'],
|
|
137
|
+
},
|
|
138
|
+
}));
|
|
139
|
+
register('delete_annotated_attachment_session', (context) => ({
|
|
140
|
+
description: context.describeTool('Delete an annotated attachment session when the whole session was created by mistake, rather than deleting markers one by one.'),
|
|
141
|
+
inputSchema: {
|
|
142
|
+
properties: {
|
|
143
|
+
sessionId: stringProperty('Annotated attachment session id.'),
|
|
144
|
+
},
|
|
145
|
+
required: ['sessionId'],
|
|
146
|
+
},
|
|
147
|
+
}));
|
|
148
|
+
register('review_image', (context) => ({
|
|
149
|
+
description: context.describeTool('Run a first-pass visual review workflow for one image and save the result into an annotation session when appropriate.'),
|
|
150
|
+
inputSchema: {
|
|
151
|
+
properties: {
|
|
152
|
+
imageId: stringProperty('Image asset id or image reference such as I-123.'),
|
|
153
|
+
prompt: stringProperty('Review instructions or focus areas for the first-pass review.'),
|
|
154
|
+
},
|
|
155
|
+
required: ['imageId', 'prompt'],
|
|
156
|
+
},
|
|
157
|
+
}));
|
|
158
|
+
register('add_annotated_attachment_marker', (context) => ({
|
|
159
|
+
description: context.describeTool('Add one marker to an annotation session using normalized 0..1 coordinates.'),
|
|
160
|
+
inputSchema: {
|
|
161
|
+
properties: {
|
|
162
|
+
sessionId: stringProperty('Annotated attachment session id.'),
|
|
163
|
+
type: stringProperty('Marker type.'),
|
|
164
|
+
x: numberProperty('Normalized x coordinate in the 0..1 range.'),
|
|
165
|
+
y: numberProperty('Normalized y coordinate in the 0..1 range.'),
|
|
166
|
+
width: numberProperty('Optional normalized width in the 0..1 range.'),
|
|
167
|
+
height: numberProperty('Optional normalized height in the 0..1 range.'),
|
|
168
|
+
text: stringProperty('Optional marker text or note.'),
|
|
169
|
+
},
|
|
170
|
+
required: ['sessionId', 'type', 'x', 'y'],
|
|
171
|
+
},
|
|
172
|
+
}));
|
|
173
|
+
register('bulk_add_annotated_attachment_markers', (context) => ({
|
|
174
|
+
description: context.describeTool('Add multiple markers to one annotation session in a single request. Useful for batch import after a first-pass review.'),
|
|
175
|
+
inputSchema: {
|
|
176
|
+
properties: {
|
|
177
|
+
sessionId: stringProperty('Annotated attachment session id.'),
|
|
178
|
+
markers: arrayProperty('Marker definitions to add.', {
|
|
179
|
+
type: 'object',
|
|
180
|
+
properties: {
|
|
181
|
+
type: { type: 'string' },
|
|
182
|
+
x: { type: 'number' },
|
|
183
|
+
y: { type: 'number' },
|
|
184
|
+
width: { type: 'number' },
|
|
185
|
+
height: { type: 'number' },
|
|
186
|
+
text: { type: 'string' },
|
|
187
|
+
},
|
|
188
|
+
required: ['type', 'x', 'y'],
|
|
189
|
+
}),
|
|
190
|
+
},
|
|
191
|
+
required: ['sessionId', 'markers'],
|
|
192
|
+
},
|
|
193
|
+
}));
|
|
194
|
+
register('update_annotated_attachment_marker', (context) => ({
|
|
195
|
+
description: context.describeTool('Update one existing annotation marker without recreating it.'),
|
|
196
|
+
inputSchema: {
|
|
197
|
+
properties: {
|
|
198
|
+
sessionId: stringProperty('Annotated attachment session id.'),
|
|
199
|
+
markerId: stringProperty('Marker id within the session.'),
|
|
200
|
+
type: stringProperty('Updated marker type.'),
|
|
201
|
+
x: numberProperty('Updated normalized x coordinate.'),
|
|
202
|
+
y: numberProperty('Updated normalized y coordinate.'),
|
|
203
|
+
width: numberProperty('Updated normalized width.'),
|
|
204
|
+
height: numberProperty('Updated normalized height.'),
|
|
205
|
+
text: stringProperty('Updated marker text.'),
|
|
206
|
+
},
|
|
207
|
+
required: ['sessionId', 'markerId'],
|
|
208
|
+
},
|
|
209
|
+
}));
|
|
210
|
+
register('reorder_annotated_attachment_marker', (context) => ({
|
|
211
|
+
description: context.describeTool('Move one marker within the session review order.'),
|
|
212
|
+
inputSchema: {
|
|
213
|
+
properties: {
|
|
214
|
+
sessionId: stringProperty('Annotated attachment session id.'),
|
|
215
|
+
markerId: stringProperty('Marker id within the session.'),
|
|
216
|
+
order: numberProperty('Target zero-based review order.'),
|
|
217
|
+
},
|
|
218
|
+
required: ['sessionId', 'markerId', 'order'],
|
|
219
|
+
},
|
|
220
|
+
}));
|
|
221
|
+
register('delete_annotated_attachment_marker', (context) => ({
|
|
222
|
+
description: context.describeTool('Delete one marker from an annotation session.'),
|
|
223
|
+
inputSchema: {
|
|
224
|
+
properties: {
|
|
225
|
+
sessionId: stringProperty('Annotated attachment session id.'),
|
|
226
|
+
markerId: stringProperty('Marker id within the session.'),
|
|
227
|
+
},
|
|
228
|
+
required: ['sessionId', 'markerId'],
|
|
229
|
+
},
|
|
230
|
+
}));
|
|
231
|
+
register('get_document_review_sessions', (context) => ({
|
|
232
|
+
description: context.describeTool('List review sessions for one document such as D-123. Use read_document_content first when you need to inspect the document body before choosing a session.'),
|
|
233
|
+
inputSchema: {
|
|
234
|
+
properties: {
|
|
235
|
+
documentId: stringProperty('Document asset id or document reference such as D-123.'),
|
|
236
|
+
},
|
|
237
|
+
required: ['documentId'],
|
|
238
|
+
},
|
|
239
|
+
}));
|
|
240
|
+
register('get_document_review_session', (context) => ({
|
|
241
|
+
description: context.describeTool('Get one document review session with its comments and summary state.'),
|
|
242
|
+
inputSchema: {
|
|
243
|
+
properties: {
|
|
244
|
+
sessionId: stringProperty('Document review session id.'),
|
|
245
|
+
},
|
|
246
|
+
required: ['sessionId'],
|
|
247
|
+
},
|
|
248
|
+
}));
|
|
249
|
+
register('create_document_review_session', (context) => ({
|
|
250
|
+
description: context.describeTool('Create a new document review session. Requires AI identity via resolve_profile before write operations.'),
|
|
251
|
+
inputSchema: {
|
|
252
|
+
properties: {
|
|
253
|
+
documentId: stringProperty('Document asset id or document reference such as D-123.'),
|
|
254
|
+
title: stringProperty('Optional review session title.'),
|
|
255
|
+
},
|
|
256
|
+
required: ['documentId'],
|
|
257
|
+
},
|
|
258
|
+
}));
|
|
259
|
+
register('update_document_review_session', (context) => ({
|
|
260
|
+
description: context.describeTool('Update a document review session title, status, or summary fields without creating a new session. Requires AI identity via resolve_profile before write operations.'),
|
|
261
|
+
inputSchema: {
|
|
262
|
+
properties: {
|
|
263
|
+
sessionId: stringProperty('Document review session id.'),
|
|
264
|
+
title: stringProperty('Updated review session title.'),
|
|
265
|
+
status: stringProperty('Updated review status.'),
|
|
266
|
+
summary: stringProperty('Updated review summary.'),
|
|
267
|
+
},
|
|
268
|
+
required: ['sessionId'],
|
|
269
|
+
},
|
|
270
|
+
}));
|
|
271
|
+
register('review_document', (context) => ({
|
|
272
|
+
description: context.describeTool('Run the lower-friction document review path for one document. This helper creates or updates the review workflow while preserving a lower-friction document review path than manual session management. Requires AI identity via resolve_profile before write operations.'),
|
|
273
|
+
inputSchema: {
|
|
274
|
+
properties: {
|
|
275
|
+
documentId: stringProperty('Document asset id or document reference such as D-123.'),
|
|
276
|
+
prompt: stringProperty('Review instructions or focus areas.'),
|
|
277
|
+
},
|
|
278
|
+
required: ['documentId', 'prompt'],
|
|
279
|
+
},
|
|
280
|
+
}));
|
|
281
|
+
register('add_document_review_comment', (context) => ({
|
|
282
|
+
description: context.describeTool('Add a comment to a document review session. Use this for review feedback that should stay attached to the document rather than the task. Requires AI identity via resolve_profile before write operations.'),
|
|
283
|
+
inputSchema: {
|
|
284
|
+
properties: {
|
|
285
|
+
sessionId: stringProperty('Document review session id.'),
|
|
286
|
+
body: stringProperty('Comment text.'),
|
|
287
|
+
anchor: { type: 'object', description: 'Optional anchor object such as a quote or line-range reference.' },
|
|
288
|
+
},
|
|
289
|
+
required: ['sessionId', 'body'],
|
|
290
|
+
},
|
|
291
|
+
}));
|
|
292
|
+
register('update_document_review_comment', (context) => ({
|
|
293
|
+
description: context.describeTool('Update an existing document review comment without creating a new comment.'),
|
|
294
|
+
inputSchema: {
|
|
295
|
+
properties: {
|
|
296
|
+
commentId: stringProperty('Document review comment id.'),
|
|
297
|
+
body: stringProperty('Updated comment text.'),
|
|
298
|
+
},
|
|
299
|
+
required: ['commentId', 'body'],
|
|
300
|
+
},
|
|
301
|
+
}));
|
|
302
|
+
register('delete_document_review_comment', (context) => ({
|
|
303
|
+
description: context.describeTool('Delete an existing document review comment when it should be removed entirely.'),
|
|
304
|
+
inputSchema: {
|
|
305
|
+
properties: {
|
|
306
|
+
commentId: stringProperty('Document review comment id.'),
|
|
307
|
+
},
|
|
308
|
+
required: ['commentId'],
|
|
309
|
+
},
|
|
310
|
+
}));
|
|
311
|
+
register('link_task_context', (context) => ({
|
|
312
|
+
description: context.describeTool('Attach an external http(s) URL as task context. link_task_context only supports an external http(s) URL; use save_task_attachment for files or generated content.'),
|
|
313
|
+
inputSchema: {
|
|
314
|
+
properties: {
|
|
315
|
+
id: stringProperty('Task ID or task reference such as T-123.'),
|
|
316
|
+
path: stringProperty('External http(s) URL to attach.'),
|
|
317
|
+
caption: stringProperty('Optional caption for the linked context entry.'),
|
|
318
|
+
},
|
|
319
|
+
required: ['id', 'path'],
|
|
320
|
+
},
|
|
321
|
+
}));
|
|
322
|
+
register('save_task_attachment', (context) => ({
|
|
323
|
+
description: context.describeTool('Save a file and attach it to a task. Use this for generated content or existing files from disk. Set overwrite=true to replace an existing canonical attachment with the same filename. For AI-authored markdown/text, use inline content for AI-authored markdown/text rather than writing an external temp file first.'),
|
|
324
|
+
inputSchema: {
|
|
325
|
+
properties: {
|
|
326
|
+
id: stringProperty('Task ID or task reference such as T-123.'),
|
|
327
|
+
filename: stringProperty('Destination filename for the attachment.'),
|
|
328
|
+
content: stringProperty('Inline file content; use this for AI-authored markdown/text attachments.'),
|
|
329
|
+
sourcePath: stringProperty('Path to an existing source file. Must be within project root, a Taskforce-managed attachment source, or an approved agent workspace root.'),
|
|
330
|
+
caption: stringProperty('Optional attachment caption.'),
|
|
331
|
+
overwrite: booleanProperty('When true, overwrite an existing canonical task attachment with the same filename instead of creating a new one.'),
|
|
332
|
+
},
|
|
333
|
+
required: ['id', 'filename'],
|
|
334
|
+
},
|
|
335
|
+
}));
|
|
336
|
+
register('delete_task_attachment', (context) => ({
|
|
337
|
+
description: context.describeTool('Delete one task attachment. Use get_task_attachments first to inspect attachment ids before removing one.'),
|
|
338
|
+
inputSchema: {
|
|
339
|
+
properties: {
|
|
340
|
+
taskId: stringProperty('Task ID or task reference such as T-123.'),
|
|
341
|
+
attachmentId: stringProperty('Attachment descriptor id from get_task_attachments.'),
|
|
342
|
+
},
|
|
343
|
+
required: ['taskId', 'attachmentId'],
|
|
344
|
+
},
|
|
345
|
+
}));
|
|
346
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { WorkspaceAssetEntry, WorkspaceAssetStore } from '../storage/workspaceAssetStore.js';
|
|
2
|
+
export interface DocumentHelpersDeps {
|
|
3
|
+
activeWorkspaceId: string;
|
|
4
|
+
workspaceAssetStore: WorkspaceAssetStore | null;
|
|
5
|
+
getDocumentReferenceLabel: (asset: {
|
|
6
|
+
assetId: string;
|
|
7
|
+
documentId?: string | null;
|
|
8
|
+
referenceNumber?: number | null;
|
|
9
|
+
}) => string | null;
|
|
10
|
+
parseDocumentReference: (value: string) => number | null;
|
|
11
|
+
isTextReadableMimeType: (mimeTypeRaw: string) => boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function createDocumentHelpers(deps: DocumentHelpersDeps): {
|
|
14
|
+
buildDocumentDescriptor: (asset: {
|
|
15
|
+
assetId: string;
|
|
16
|
+
documentId: string | null;
|
|
17
|
+
referenceNumber?: number | null;
|
|
18
|
+
kind?: string | null;
|
|
19
|
+
logicalName?: string | null;
|
|
20
|
+
originalFilename?: string | null;
|
|
21
|
+
version?: number | null;
|
|
22
|
+
storageKey?: string | null;
|
|
23
|
+
mimeType?: string | null;
|
|
24
|
+
sizeBytes?: number | null;
|
|
25
|
+
updatedAt?: string | null;
|
|
26
|
+
}) => {
|
|
27
|
+
assetId: string;
|
|
28
|
+
documentId: string | null;
|
|
29
|
+
referenceNumber: number | null;
|
|
30
|
+
referenceLabel: string | null;
|
|
31
|
+
logicalName: string | null;
|
|
32
|
+
originalFilename: string | null;
|
|
33
|
+
version: number | null;
|
|
34
|
+
storageKey: string | null;
|
|
35
|
+
mimeType: string | null;
|
|
36
|
+
sizeBytes: number | null;
|
|
37
|
+
updatedAt: string | null;
|
|
38
|
+
apiUrl: string;
|
|
39
|
+
};
|
|
40
|
+
buildDocumentReadHint: (asset: {
|
|
41
|
+
assetId: string;
|
|
42
|
+
documentId: string | null;
|
|
43
|
+
referenceNumber?: number | null;
|
|
44
|
+
mimeType?: string | null;
|
|
45
|
+
}) => {
|
|
46
|
+
available: boolean;
|
|
47
|
+
nextTool: string;
|
|
48
|
+
suggestedArgs: {
|
|
49
|
+
documentId: string;
|
|
50
|
+
};
|
|
51
|
+
guidance: string;
|
|
52
|
+
reviewNext: {
|
|
53
|
+
helperTool: string;
|
|
54
|
+
listTool: string;
|
|
55
|
+
createTool: string;
|
|
56
|
+
commentTool: string;
|
|
57
|
+
guidance: string;
|
|
58
|
+
};
|
|
59
|
+
reason?: undefined;
|
|
60
|
+
} | {
|
|
61
|
+
available: boolean;
|
|
62
|
+
nextTool: string;
|
|
63
|
+
suggestedArgs: {
|
|
64
|
+
documentId: string;
|
|
65
|
+
};
|
|
66
|
+
guidance: string;
|
|
67
|
+
reviewNext: {
|
|
68
|
+
helperTool: string;
|
|
69
|
+
listTool: string;
|
|
70
|
+
createTool: string;
|
|
71
|
+
commentTool: string;
|
|
72
|
+
guidance: string;
|
|
73
|
+
};
|
|
74
|
+
reason: string;
|
|
75
|
+
};
|
|
76
|
+
buildCanonicalDocumentAttachmentHint: (asset: {
|
|
77
|
+
assetId: string;
|
|
78
|
+
documentId: string | null;
|
|
79
|
+
referenceNumber?: number | null;
|
|
80
|
+
mimeType?: string | null;
|
|
81
|
+
}) => {
|
|
82
|
+
documentId: string | null;
|
|
83
|
+
preferredNextTool: string;
|
|
84
|
+
preferredWorkflow: string;
|
|
85
|
+
suggestedArgs: {
|
|
86
|
+
documentId: string;
|
|
87
|
+
};
|
|
88
|
+
guidance: string;
|
|
89
|
+
} | null;
|
|
90
|
+
searchDocumentAssets: (queryRaw: unknown, options?: {
|
|
91
|
+
limit?: unknown;
|
|
92
|
+
}) => Array<{
|
|
93
|
+
asset: WorkspaceAssetEntry;
|
|
94
|
+
matchedOn: string[];
|
|
95
|
+
score: number;
|
|
96
|
+
}>;
|
|
97
|
+
resolveDocumentAssetByIdentifierOrThrow: (documentIdentifier: unknown, options?: {
|
|
98
|
+
notFoundMessage?: string;
|
|
99
|
+
}) => WorkspaceAssetEntry;
|
|
100
|
+
};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
export function createDocumentHelpers(deps) {
|
|
2
|
+
const buildDocumentDescriptor = (asset) => ({
|
|
3
|
+
assetId: asset.assetId,
|
|
4
|
+
documentId: asset.documentId ?? null,
|
|
5
|
+
referenceNumber: typeof asset.referenceNumber === 'number' ? asset.referenceNumber : null,
|
|
6
|
+
referenceLabel: deps.getDocumentReferenceLabel(asset),
|
|
7
|
+
logicalName: asset.logicalName ?? null,
|
|
8
|
+
originalFilename: asset.originalFilename ?? null,
|
|
9
|
+
version: typeof asset.version === 'number' ? asset.version : null,
|
|
10
|
+
storageKey: asset.storageKey ?? null,
|
|
11
|
+
mimeType: asset.mimeType ?? null,
|
|
12
|
+
sizeBytes: typeof asset.sizeBytes === 'number' ? asset.sizeBytes : null,
|
|
13
|
+
updatedAt: asset.updatedAt ?? null,
|
|
14
|
+
apiUrl: `/api/taskforce/documents/${encodeURIComponent(String(asset.assetId || '').trim())}/content`,
|
|
15
|
+
});
|
|
16
|
+
const buildDocumentReadHint = (asset) => {
|
|
17
|
+
const preferredIdentifier = deps.getDocumentReferenceLabel(asset)
|
|
18
|
+
|| (typeof asset.documentId === 'string' && asset.documentId.trim().length > 0 ? asset.documentId.trim() : null)
|
|
19
|
+
|| String(asset.assetId || '').trim();
|
|
20
|
+
const readable = deps.isTextReadableMimeType(asset.mimeType || '');
|
|
21
|
+
if (readable) {
|
|
22
|
+
return {
|
|
23
|
+
available: true,
|
|
24
|
+
nextTool: 'read_document_content',
|
|
25
|
+
suggestedArgs: { documentId: preferredIdentifier },
|
|
26
|
+
guidance: 'To read the document body, call read_document_content with this documentId.',
|
|
27
|
+
reviewNext: {
|
|
28
|
+
helperTool: 'review_document',
|
|
29
|
+
listTool: 'get_document_review_sessions',
|
|
30
|
+
createTool: 'create_document_review_session',
|
|
31
|
+
commentTool: 'add_document_review_comment',
|
|
32
|
+
guidance: 'After reading the document body, prefer review_document for a lower-friction review flow, or use get_document_review_sessions/create_document_review_session and add_document_review_comment directly.'
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
available: false,
|
|
38
|
+
nextTool: 'read_document_content',
|
|
39
|
+
suggestedArgs: { documentId: preferredIdentifier },
|
|
40
|
+
guidance: 'This document is not inline-readable as text. Call read_document_content to get the structured download/reference response.',
|
|
41
|
+
reviewNext: {
|
|
42
|
+
helperTool: 'review_document',
|
|
43
|
+
listTool: 'get_document_review_sessions',
|
|
44
|
+
createTool: 'create_document_review_session',
|
|
45
|
+
commentTool: 'add_document_review_comment',
|
|
46
|
+
guidance: 'After inspecting the document access response, prefer review_document for a lower-friction review flow, or use get_document_review_sessions/create_document_review_session and add_document_review_comment directly.'
|
|
47
|
+
},
|
|
48
|
+
reason: 'binary_file',
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
const buildCanonicalDocumentAttachmentHint = (asset) => {
|
|
52
|
+
const preferredIdentifier = deps.getDocumentReferenceLabel(asset)
|
|
53
|
+
|| (typeof asset.documentId === 'string' && asset.documentId.trim().length > 0 ? asset.documentId.trim() : null)
|
|
54
|
+
|| String(asset.assetId || '').trim();
|
|
55
|
+
if (!preferredIdentifier) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
const readable = deps.isTextReadableMimeType(asset.mimeType || '');
|
|
59
|
+
return {
|
|
60
|
+
documentId: typeof asset.documentId === 'string' && asset.documentId.trim().length > 0 ? asset.documentId.trim() : null,
|
|
61
|
+
preferredNextTool: 'get_document',
|
|
62
|
+
preferredWorkflow: 'canonical_document',
|
|
63
|
+
suggestedArgs: { documentId: preferredIdentifier },
|
|
64
|
+
guidance: readable
|
|
65
|
+
? 'This attachment is a canonical document. Prefer get_document, then read_document_content, for ongoing document work and review.'
|
|
66
|
+
: 'This attachment is a canonical document. Prefer get_document for metadata and review context, then read_document_content for the structured content/download response.',
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
const searchDocumentAssets = (queryRaw, options) => {
|
|
70
|
+
const query = typeof queryRaw === 'string' ? queryRaw.trim().toLowerCase() : '';
|
|
71
|
+
if (!query) {
|
|
72
|
+
throw new Error('query is required');
|
|
73
|
+
}
|
|
74
|
+
if (!deps.workspaceAssetStore) {
|
|
75
|
+
throw new Error('Workspace asset store unavailable.');
|
|
76
|
+
}
|
|
77
|
+
const requestedLimit = Number(options?.limit);
|
|
78
|
+
const limit = Number.isFinite(requestedLimit)
|
|
79
|
+
? Math.max(1, Math.min(50, Math.trunc(requestedLimit)))
|
|
80
|
+
: 10;
|
|
81
|
+
const normalize = (value) => typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
82
|
+
const tokenize = (value) => normalize(value).split(/[^a-z0-9]+/i).filter(Boolean);
|
|
83
|
+
const allDocuments = deps.workspaceAssetStore.listAllByWorkspace(deps.activeWorkspaceId, { kind: 'document' });
|
|
84
|
+
const scoredMatches = allDocuments.flatMap((asset) => {
|
|
85
|
+
if (asset.deletedAt)
|
|
86
|
+
return [];
|
|
87
|
+
const fields = [
|
|
88
|
+
{ label: 'referenceLabel', value: deps.getDocumentReferenceLabel(asset) || '' },
|
|
89
|
+
{ label: 'documentId', value: asset.documentId || '' },
|
|
90
|
+
{ label: 'logicalName', value: asset.logicalName || '' },
|
|
91
|
+
{ label: 'originalFilename', value: asset.originalFilename || '' },
|
|
92
|
+
{ label: 'assetId', value: asset.assetId || '' },
|
|
93
|
+
];
|
|
94
|
+
let score = 0;
|
|
95
|
+
const matchedOn = new Set();
|
|
96
|
+
for (const field of fields) {
|
|
97
|
+
const normalized = normalize(field.value);
|
|
98
|
+
if (!normalized)
|
|
99
|
+
continue;
|
|
100
|
+
if (normalized === query) {
|
|
101
|
+
score += field.label === 'referenceLabel' || field.label === 'documentId' ? 120 : 90;
|
|
102
|
+
matchedOn.add(field.label);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (normalized.startsWith(query)) {
|
|
106
|
+
score += field.label === 'logicalName' || field.label === 'originalFilename' ? 60 : 50;
|
|
107
|
+
matchedOn.add(field.label);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (normalized.includes(query)) {
|
|
111
|
+
score += 35;
|
|
112
|
+
matchedOn.add(field.label);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (tokenize(field.value).some((token) => token.startsWith(query))) {
|
|
116
|
+
score += 20;
|
|
117
|
+
matchedOn.add(field.label);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (score <= 0)
|
|
121
|
+
return [];
|
|
122
|
+
return [{ asset, matchedOn: Array.from(matchedOn), score }];
|
|
123
|
+
});
|
|
124
|
+
return scoredMatches
|
|
125
|
+
.sort((a, b) => b.score - a.score || String(b.asset.updatedAt || '').localeCompare(String(a.asset.updatedAt || '')))
|
|
126
|
+
.slice(0, limit);
|
|
127
|
+
};
|
|
128
|
+
const resolveDocumentAssetByIdentifierOrThrow = (documentIdentifier, options) => {
|
|
129
|
+
const normalizedValue = typeof documentIdentifier === 'string' ? documentIdentifier.trim() : '';
|
|
130
|
+
if (!normalizedValue) {
|
|
131
|
+
throw new Error('documentId is required');
|
|
132
|
+
}
|
|
133
|
+
if (!deps.workspaceAssetStore) {
|
|
134
|
+
throw new Error('Workspace asset store unavailable.');
|
|
135
|
+
}
|
|
136
|
+
const matchesDocumentAsset = (asset) => Boolean(asset && asset.kind === 'document' && asset.deletedAt == null);
|
|
137
|
+
const directAsset = deps.workspaceAssetStore.get(normalizedValue, deps.activeWorkspaceId);
|
|
138
|
+
if (matchesDocumentAsset(directAsset)) {
|
|
139
|
+
return directAsset;
|
|
140
|
+
}
|
|
141
|
+
const documentAssets = deps.workspaceAssetStore.listAllByWorkspace(deps.activeWorkspaceId, { kind: 'document' });
|
|
142
|
+
const referenceNumber = deps.parseDocumentReference(normalizedValue);
|
|
143
|
+
const matchedAsset = documentAssets.find((asset) => asset.documentId === normalizedValue)
|
|
144
|
+
|| (referenceNumber
|
|
145
|
+
? documentAssets.find((asset) => asset.referenceNumber === referenceNumber && asset.isLatest)
|
|
146
|
+
|| documentAssets.find((asset) => asset.referenceNumber === referenceNumber)
|
|
147
|
+
: null);
|
|
148
|
+
if (matchesDocumentAsset(matchedAsset)) {
|
|
149
|
+
return matchedAsset;
|
|
150
|
+
}
|
|
151
|
+
throw new Error(options?.notFoundMessage
|
|
152
|
+
|| `Document ${normalizedValue} not found in workspace ${deps.activeWorkspaceId}. If you only know a keyword, filename, or rough title, call search_documents first.`);
|
|
153
|
+
};
|
|
154
|
+
return {
|
|
155
|
+
buildDocumentDescriptor,
|
|
156
|
+
buildDocumentReadHint,
|
|
157
|
+
buildCanonicalDocumentAttachmentHint,
|
|
158
|
+
searchDocumentAssets,
|
|
159
|
+
resolveDocumentAssetByIdentifierOrThrow,
|
|
160
|
+
};
|
|
161
|
+
}
|