@taskforcehq/taskforce 0.3.323 → 0.3.326
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/dist/Taskforce.module.css +56 -0
- package/dist/components/features/TaskSettings.js +26 -9
- package/dist/components/task/TaskKanban.d.ts +2 -2
- package/dist/components/views/AppShellHeader.js +47 -2
- package/dist/components/views/StandaloneLayout.js +22 -22
- package/dist/components/views/panels/PlanningDrawer.d.ts +2 -2
- package/dist/components/views/panels/PlanningDrawer.js +26 -12
- package/dist/components/views/planningScope.d.ts +4 -0
- package/dist/components/views/planningScope.js +5 -0
- package/dist/core/AiProfileService.js +6 -1
- package/dist/core/Taskforce.d.ts +3 -0
- package/dist/core/Taskforce.js +21 -0
- package/dist/hooks/useTaskforce.js +74 -17
- package/dist/hooks/workspace/workspaceLocalState.d.ts +25 -0
- package/dist/hooks/workspace/workspaceLocalState.js +38 -0
- package/dist/mcp/adminWorkspaceRegistrar.js +0 -10
- package/dist/mcp/clientIntegrations.d.ts +1 -1
- package/dist/mcp/clientIntegrations.js +50 -0
- package/dist/mcp/clientProfileDefaults.js +4 -0
- package/dist/mcp/collaborationRegistrar.js +3 -3
- package/dist/mcp/documentAssetRegistrar.js +24 -72
- package/dist/mcp/documentHelpers.d.ts +11 -4
- package/dist/mcp/documentHelpers.js +21 -17
- package/dist/mcp/runtime.d.ts +4 -0
- package/dist/mcp/runtime.js +466 -534
- package/dist/mcp/taskPlanningRegistrar.js +45 -81
- package/dist/mcp/toolProfiles.d.ts +7 -0
- package/dist/mcp/toolProfiles.js +36 -0
- package/dist/mcp/toolRegistry.d.ts +87 -609
- package/dist/mcp/toolRegistry.js +93 -88
- package/dist/resources/templates/workflow-sources/workflowDocs.mjs +10 -10
- package/dist/resources/templates/workflows/collaborate.yaml +2 -3
- package/dist/resources/templates/workflows/do.yaml +2 -3
- package/dist/resources/templates/workflows/evaluate.yaml +2 -3
- package/dist/resources/templates/workflows/execute.yaml +12 -15
- package/dist/resources/templates/workflows/plan.yaml +3 -4
- package/dist/resources/templates/workflows/review.yaml +2 -3
- package/dist/server/routes/admin.d.ts +1 -0
- package/dist/server/routes/admin.js +29 -1
- package/dist/shared/taxonomyLibrary.js +1 -1
- package/dist/sync/cloudSyncApi.js +22 -3
- package/dist/types.js +1 -1
- package/dist/ui/.well-known/openai-apps-challenge +1 -1
- package/dist/ui/assets/{AgentsModule-7jpDeuVR.js → AgentsModule-C4LcqhcZ.js} +1 -1
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-BJLe8DFj.js → AnnotatedAttachmentWorkspace-B-Dq22d2.js} +1 -1
- package/dist/ui/assets/{ContextAttachmentManager-AmEny3eI.js → ContextAttachmentManager-DsXPTvi-.js} +1 -1
- package/dist/ui/assets/{DocumentWorkspace-Cqdq31Ha.js → DocumentWorkspace-CxA1wO3U.js} +1 -1
- package/dist/ui/assets/{EntityActivityTimeline-BlS7DhA4.js → EntityActivityTimeline-B8nSe8l3.js} +1 -1
- package/dist/ui/assets/{InitiativesModule-C1qfO1uZ.js → InitiativesModule-VY95v38E.js} +1 -1
- package/dist/ui/assets/{PlansPage-DFt5w-0Y.js → PlansPage-CQbKDv1a.js} +1 -1
- package/dist/ui/assets/{TaskContextUpload-zuro5gDT.js → TaskContextUpload-DvDlNpHC.js} +1 -1
- package/dist/ui/assets/TaskSettings-CUWbYYtP.js +12 -0
- package/dist/ui/assets/{WorkflowsModule-CEEGuxsf.js → WorkflowsModule-CgM3Jako.js} +1 -1
- package/dist/ui/assets/documentReferences-Do9YjrS3.js +1 -0
- package/dist/ui/assets/index--mh3pktj.js +5 -0
- package/dist/ui/assets/index-BawaaEXS.css +1 -0
- package/dist/ui/index.html +2 -2
- package/dist/utils/theme.js +1 -1
- package/package.json +1 -1
- package/src/resources/templates/workflow-sources/workflowDocs.mjs +10 -10
- package/src/resources/templates/workflows/collaborate.yaml +2 -3
- package/src/resources/templates/workflows/do.yaml +2 -3
- package/src/resources/templates/workflows/evaluate.yaml +2 -3
- package/src/resources/templates/workflows/execute.yaml +12 -15
- package/src/resources/templates/workflows/plan.yaml +3 -4
- package/src/resources/templates/workflows/review.yaml +2 -3
- package/dist/ui/assets/TaskSettings-CJPC2Bft.js +0 -9
- package/dist/ui/assets/documentReferences-Ca2P--i5.js +0 -1
- package/dist/ui/assets/index-CGDQY0nN.js +0 -5
- package/dist/ui/assets/index-DcK_8tuE.css +0 -1
package/dist/core/Taskforce.js
CHANGED
|
@@ -5422,6 +5422,27 @@ export class TaskforceCore {
|
|
|
5422
5422
|
`).run(enabled ? 1 : 0, new Date().toISOString(), this.tenantId, normalizedUserId);
|
|
5423
5423
|
return Number(result?.changes || 0) > 0;
|
|
5424
5424
|
}
|
|
5425
|
+
markUserEmailVerified(userId) {
|
|
5426
|
+
const normalizedUserId = String(userId || '').trim();
|
|
5427
|
+
if (!normalizedUserId)
|
|
5428
|
+
return null;
|
|
5429
|
+
const now = new Date().toISOString();
|
|
5430
|
+
const row = this.db.prepare(`
|
|
5431
|
+
SELECT id, email_verified_at
|
|
5432
|
+
FROM users
|
|
5433
|
+
WHERE tenant_id = ? AND id = ?
|
|
5434
|
+
LIMIT 1
|
|
5435
|
+
`).get(this.tenantId, normalizedUserId);
|
|
5436
|
+
if (!row?.id)
|
|
5437
|
+
return null;
|
|
5438
|
+
const emailVerifiedAt = row.email_verified_at || now;
|
|
5439
|
+
this.db.prepare(`
|
|
5440
|
+
UPDATE users
|
|
5441
|
+
SET email_verified_at = COALESCE(email_verified_at, ?), updated_at = ?
|
|
5442
|
+
WHERE tenant_id = ? AND id = ?
|
|
5443
|
+
`).run(now, now, this.tenantId, normalizedUserId);
|
|
5444
|
+
return { emailVerifiedAt };
|
|
5445
|
+
}
|
|
5425
5446
|
purgeSystemUser(input) {
|
|
5426
5447
|
const targetUserId = String(input.targetUserId || '').trim();
|
|
5427
5448
|
const actorUserId = String(input.actorUserId || '').trim();
|
|
@@ -62,7 +62,7 @@ import { useTaskFilters } from './tasks/useTaskFilters';
|
|
|
62
62
|
import { useTaskFormActions } from './tasks/useTaskFormActions';
|
|
63
63
|
import { useTaskRelationships } from './tasks/useTaskRelationships';
|
|
64
64
|
import { useTaskActions } from './tasks/useTaskActions';
|
|
65
|
-
import { clearPersistedWorkspaceId, logBootstrapDebug, persistRemoteUiState, readPersistedAppUiState, readPersistedWorkspaceId, writePersistedAppUiState, writePersistedWorkspaceId } from './workspace/workspaceLocalState';
|
|
65
|
+
import { areFilterTaxonomySignaturesEqual, buildFilterTaxonomySignature, clearPersistedWorkspaceId, logBootstrapDebug, persistRemoteUiState, readPersistedAppUiState, readPersistedWorkspaceId, writePersistedAppUiState, writePersistedWorkspaceId } from './workspace/workspaceLocalState';
|
|
66
66
|
import { useTaskforceApiRouting } from './workspace/useTaskforceApiRouting';
|
|
67
67
|
import { useTaskforceAuthBootstrap } from './auth/useTaskforceAuthBootstrap';
|
|
68
68
|
import { deriveAuthGuardPolicy } from './auth/useAuthGuardPolicy';
|
|
@@ -442,6 +442,7 @@ export function useTaskforce({ config = {}, initialTaskId, onTaskCountChange, on
|
|
|
442
442
|
const authSessionLastCheckedAtRef = useRef(0);
|
|
443
443
|
const authSessionLastResultRef = useRef(hasOptimisticLocalAuth);
|
|
444
444
|
const currentWorkspaceIdRef = useRef(currentWorkspaceId);
|
|
445
|
+
const pendingPersistedTaxonomyUiStateRef = useRef(null);
|
|
445
446
|
const explicitWorkspaceSelectionRef = useRef(null);
|
|
446
447
|
const workspaceTransitionEpochRef = useRef(0);
|
|
447
448
|
const bootstrapTraceSeedRef = useRef((() => {
|
|
@@ -1238,9 +1239,26 @@ export function useTaskforce({ config = {}, initialTaskId, onTaskCountChange, on
|
|
|
1238
1239
|
authRequiredForApiRef.current = authRequiredForApi;
|
|
1239
1240
|
isAuthenticatedRef.current = isAuthenticated;
|
|
1240
1241
|
}, [authSessionResolved, authRequiredForApi, isAuthenticated]);
|
|
1242
|
+
const currentFilterTaxonomySignature = useMemo(() => (referenceDataLoaded
|
|
1243
|
+
? buildFilterTaxonomySignature({
|
|
1244
|
+
categories: activeCategories,
|
|
1245
|
+
types: activeTypes,
|
|
1246
|
+
priorities,
|
|
1247
|
+
taxonomies
|
|
1248
|
+
})
|
|
1249
|
+
: null), [activeCategories, activeTypes, priorities, referenceDataLoaded, taxonomies]);
|
|
1241
1250
|
const applyPersistedUiState = useCallback((state, options) => {
|
|
1242
|
-
if (!state)
|
|
1251
|
+
if (!state) {
|
|
1252
|
+
pendingPersistedTaxonomyUiStateRef.current = null;
|
|
1243
1253
|
return;
|
|
1254
|
+
}
|
|
1255
|
+
const hasPersistedTaxonomyFilterState = Array.isArray(state.filterCategories)
|
|
1256
|
+
|| Array.isArray(state.filterPriorities)
|
|
1257
|
+
|| Array.isArray(state.filterTypes)
|
|
1258
|
+
|| (state.filterTaxonomies && typeof state.filterTaxonomies === 'object');
|
|
1259
|
+
if (!hasPersistedTaxonomyFilterState) {
|
|
1260
|
+
pendingPersistedTaxonomyUiStateRef.current = null;
|
|
1261
|
+
}
|
|
1244
1262
|
const hasPersistedFilterState = Array.isArray(state.filterCategories)
|
|
1245
1263
|
|| Array.isArray(state.filterPriorities)
|
|
1246
1264
|
|| Array.isArray(state.filterTypes)
|
|
@@ -1248,6 +1266,20 @@ export function useTaskforce({ config = {}, initialTaskId, onTaskCountChange, on
|
|
|
1248
1266
|
|| Array.isArray(state.filterAssignees)
|
|
1249
1267
|
|| typeof state.filterAssigneesAllSelected === 'boolean'
|
|
1250
1268
|
|| (state.filterTaxonomies && typeof state.filterTaxonomies === 'object');
|
|
1269
|
+
const canCompareTaxonomySignature = currentFilterTaxonomySignature !== null;
|
|
1270
|
+
if (hasPersistedTaxonomyFilterState && !canCompareTaxonomySignature) {
|
|
1271
|
+
pendingPersistedTaxonomyUiStateRef.current = {
|
|
1272
|
+
state,
|
|
1273
|
+
skipActiveWorkspaceModule: options?.skipActiveWorkspaceModule
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
const canApplyTaxonomyFilters = hasPersistedTaxonomyFilterState
|
|
1277
|
+
&& canCompareTaxonomySignature
|
|
1278
|
+
&& areFilterTaxonomySignaturesEqual(state.filterTaxonomySignature, currentFilterTaxonomySignature);
|
|
1279
|
+
const shouldResetTaxonomyFilters = hasPersistedTaxonomyFilterState
|
|
1280
|
+
&& canCompareTaxonomySignature
|
|
1281
|
+
&& !canApplyTaxonomyFilters;
|
|
1282
|
+
const taxonomyFiltersAreReady = !hasPersistedTaxonomyFilterState || canApplyTaxonomyFilters || shouldResetTaxonomyFilters;
|
|
1251
1283
|
if (!options?.skipActiveWorkspaceModule) {
|
|
1252
1284
|
if (typeof state.activeWorkspaceModule === 'string') {
|
|
1253
1285
|
setActiveWorkspaceModuleState(state.activeWorkspaceModule);
|
|
@@ -1264,28 +1296,39 @@ export function useTaskforce({ config = {}, initialTaskId, onTaskCountChange, on
|
|
|
1264
1296
|
setZenModeState(state.zenMode);
|
|
1265
1297
|
if (typeof state.searchQuery === 'string')
|
|
1266
1298
|
setSearchQuery(state.searchQuery);
|
|
1267
|
-
if (
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1299
|
+
if (canApplyTaxonomyFilters) {
|
|
1300
|
+
if (Array.isArray(state.filterCategories))
|
|
1301
|
+
setFilterCategories(state.filterCategories);
|
|
1302
|
+
if (Array.isArray(state.filterPriorities))
|
|
1303
|
+
setFilterPriorities(state.filterPriorities);
|
|
1304
|
+
if (Array.isArray(state.filterTypes))
|
|
1305
|
+
setFilterTypes(state.filterTypes);
|
|
1306
|
+
if (state.filterTaxonomies && typeof state.filterTaxonomies === 'object')
|
|
1307
|
+
setFilterTaxonomies(state.filterTaxonomies);
|
|
1308
|
+
}
|
|
1309
|
+
else if (shouldResetTaxonomyFilters) {
|
|
1310
|
+
setFilterCategories(activeCategories.map((category) => category.value));
|
|
1311
|
+
setFilterPriorities(priorities.map((priority) => priority.value));
|
|
1312
|
+
setFilterTypes(activeTypes.map((type) => type.value));
|
|
1313
|
+
setFilterTaxonomies({});
|
|
1314
|
+
}
|
|
1273
1315
|
if (Array.isArray(state.filterStatus))
|
|
1274
1316
|
setFilterStatus(state.filterStatus);
|
|
1275
1317
|
if (Array.isArray(state.filterAssignees))
|
|
1276
1318
|
setFilterAssignees(state.filterAssignees);
|
|
1277
1319
|
if (typeof state.filterAssigneesAllSelected === 'boolean')
|
|
1278
1320
|
setFilterAssigneesAllSelected(state.filterAssigneesAllSelected);
|
|
1279
|
-
if (state.filterTaxonomies && typeof state.filterTaxonomies === 'object')
|
|
1280
|
-
setFilterTaxonomies(state.filterTaxonomies);
|
|
1281
1321
|
if (state.sortBy === 'created' || state.sortBy === 'priority' || state.sortBy === 'updated' || state.sortBy === 'complexity')
|
|
1282
1322
|
setSortBy(state.sortBy);
|
|
1283
1323
|
if (state.sortOrder === 'asc' || state.sortOrder === 'desc')
|
|
1284
1324
|
setSortOrder(state.sortOrder);
|
|
1285
|
-
if (
|
|
1325
|
+
if (shouldResetTaxonomyFilters) {
|
|
1326
|
+
setHasInitedFilters(true);
|
|
1327
|
+
}
|
|
1328
|
+
else if (typeof state.hasInitedFilters === 'boolean' && taxonomyFiltersAreReady) {
|
|
1286
1329
|
setHasInitedFilters(state.hasInitedFilters);
|
|
1287
1330
|
}
|
|
1288
|
-
else if (hasPersistedFilterState) {
|
|
1331
|
+
else if (hasPersistedFilterState && taxonomyFiltersAreReady) {
|
|
1289
1332
|
setHasInitedFilters(true);
|
|
1290
1333
|
}
|
|
1291
1334
|
if (typeof state.showChecklist === 'boolean')
|
|
@@ -1295,7 +1338,16 @@ export function useTaskforce({ config = {}, initialTaskId, onTaskCountChange, on
|
|
|
1295
1338
|
if (typeof state.exportEnvironment === 'string' && state.exportEnvironment.trim().length > 0) {
|
|
1296
1339
|
setExportEnvironment(state.exportEnvironment.trim());
|
|
1297
1340
|
}
|
|
1298
|
-
}, []);
|
|
1341
|
+
}, [activeCategories, activeTypes, currentFilterTaxonomySignature, priorities]);
|
|
1342
|
+
useEffect(() => {
|
|
1343
|
+
if (!currentFilterTaxonomySignature || !pendingPersistedTaxonomyUiStateRef.current)
|
|
1344
|
+
return;
|
|
1345
|
+
const pending = pendingPersistedTaxonomyUiStateRef.current;
|
|
1346
|
+
pendingPersistedTaxonomyUiStateRef.current = null;
|
|
1347
|
+
applyPersistedUiState(pending.state, {
|
|
1348
|
+
skipActiveWorkspaceModule: pending.skipActiveWorkspaceModule
|
|
1349
|
+
});
|
|
1350
|
+
}, [applyPersistedUiState, currentFilterTaxonomySignature]);
|
|
1299
1351
|
const loadPersistedUiState = useCallback(async (workspaceIdOverride) => {
|
|
1300
1352
|
const targetWorkspaceId = String(workspaceIdOverride || currentWorkspaceIdRef.current || currentWorkspaceId || 'default').trim() || 'default';
|
|
1301
1353
|
const uiStateLoadKey = `${runtimeMode}:${workspaceUiStateScope}:${targetWorkspaceId}`;
|
|
@@ -1369,6 +1421,7 @@ export function useTaskforce({ config = {}, initialTaskId, onTaskCountChange, on
|
|
|
1369
1421
|
filterAssignees,
|
|
1370
1422
|
filterAssigneesAllSelected,
|
|
1371
1423
|
filterTaxonomies,
|
|
1424
|
+
...(currentFilterTaxonomySignature ? { filterTaxonomySignature: currentFilterTaxonomySignature } : {}),
|
|
1372
1425
|
sortBy,
|
|
1373
1426
|
sortOrder,
|
|
1374
1427
|
hasInitedFilters,
|
|
@@ -1404,6 +1457,7 @@ export function useTaskforce({ config = {}, initialTaskId, onTaskCountChange, on
|
|
|
1404
1457
|
filterAssignees,
|
|
1405
1458
|
filterAssigneesAllSelected,
|
|
1406
1459
|
filterTaxonomies,
|
|
1460
|
+
currentFilterTaxonomySignature,
|
|
1407
1461
|
sortBy,
|
|
1408
1462
|
sortOrder,
|
|
1409
1463
|
hasInitedFilters,
|
|
@@ -2557,6 +2611,9 @@ export function useTaskforce({ config = {}, initialTaskId, onTaskCountChange, on
|
|
|
2557
2611
|
setInitiatives((current) => upsertEntityById(current, data));
|
|
2558
2612
|
return data;
|
|
2559
2613
|
}, [fetchPlanningEntities]);
|
|
2614
|
+
const refreshPlanningEntitiesInBackground = useCallback(() => {
|
|
2615
|
+
void fetchPlanningEntities();
|
|
2616
|
+
}, [fetchPlanningEntities]);
|
|
2560
2617
|
const archiveInitiative = useCallback(async (id) => {
|
|
2561
2618
|
const res = await fetch(`/api/taskforce/initiative/${encodeURIComponent(id)}/archive`, {
|
|
2562
2619
|
method: 'POST',
|
|
@@ -2565,10 +2622,10 @@ export function useTaskforce({ config = {}, initialTaskId, onTaskCountChange, on
|
|
|
2565
2622
|
if (!res.ok) {
|
|
2566
2623
|
throw new Error(data?.error || `Failed to archive initiative (${res.status})`);
|
|
2567
2624
|
}
|
|
2568
|
-
await fetchPlanningEntities();
|
|
2569
2625
|
setInitiatives((current) => upsertEntityById(current, data));
|
|
2626
|
+
refreshPlanningEntitiesInBackground();
|
|
2570
2627
|
return data;
|
|
2571
|
-
}, [
|
|
2628
|
+
}, [refreshPlanningEntitiesInBackground]);
|
|
2572
2629
|
const unarchiveInitiative = useCallback(async (id) => {
|
|
2573
2630
|
const res = await fetch(`/api/taskforce/initiative/${encodeURIComponent(id)}/unarchive`, {
|
|
2574
2631
|
method: 'POST',
|
|
@@ -2577,10 +2634,10 @@ export function useTaskforce({ config = {}, initialTaskId, onTaskCountChange, on
|
|
|
2577
2634
|
if (!res.ok) {
|
|
2578
2635
|
throw new Error(data?.error || `Failed to unarchive initiative (${res.status})`);
|
|
2579
2636
|
}
|
|
2580
|
-
await fetchPlanningEntities();
|
|
2581
2637
|
setInitiatives((current) => upsertEntityById(current, data));
|
|
2638
|
+
refreshPlanningEntitiesInBackground();
|
|
2582
2639
|
return data;
|
|
2583
|
-
}, [
|
|
2640
|
+
}, [refreshPlanningEntitiesInBackground]);
|
|
2584
2641
|
const createWorkstream = useCallback(async (payload) => {
|
|
2585
2642
|
const res = await fetch('/api/taskforce/workstream', {
|
|
2586
2643
|
method: 'POST',
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { TaskAssignee } from '../../types';
|
|
2
2
|
import type { TaskSortKey } from '../../utils/taxonomySorting.js';
|
|
3
|
+
export interface FilterTaxonomySignature {
|
|
4
|
+
categories: string[];
|
|
5
|
+
types: string[];
|
|
6
|
+
priorities: string[];
|
|
7
|
+
taxonomies: Record<string, string[]>;
|
|
8
|
+
}
|
|
3
9
|
export interface PersistedUiState {
|
|
4
10
|
groupBy?: 'category' | 'type' | 'priority' | 'approach' | 'assignee' | 'status' | 'complexity' | 'schedule' | 'docs';
|
|
5
11
|
activeWorkspaceModule?: 'tasks' | 'docs' | 'annotate' | 'workflows' | 'agents';
|
|
@@ -13,6 +19,7 @@ export interface PersistedUiState {
|
|
|
13
19
|
filterAssignees?: TaskAssignee[];
|
|
14
20
|
filterAssigneesAllSelected?: boolean;
|
|
15
21
|
filterTaxonomies?: Record<string, (string | number)[]>;
|
|
22
|
+
filterTaxonomySignature?: FilterTaxonomySignature;
|
|
16
23
|
sortBy?: TaskSortKey;
|
|
17
24
|
sortOrder?: 'asc' | 'desc';
|
|
18
25
|
hasInitedFilters?: boolean;
|
|
@@ -28,6 +35,24 @@ type RemoteUiStatePersistResult = {
|
|
|
28
35
|
response?: Response;
|
|
29
36
|
payload?: any;
|
|
30
37
|
};
|
|
38
|
+
export declare function buildFilterTaxonomySignature(input: {
|
|
39
|
+
categories: Array<{
|
|
40
|
+
value?: unknown;
|
|
41
|
+
}>;
|
|
42
|
+
types: Array<{
|
|
43
|
+
value?: unknown;
|
|
44
|
+
}>;
|
|
45
|
+
priorities: Array<{
|
|
46
|
+
value?: unknown;
|
|
47
|
+
}>;
|
|
48
|
+
taxonomies?: Array<{
|
|
49
|
+
id?: unknown;
|
|
50
|
+
options?: Array<{
|
|
51
|
+
value?: unknown;
|
|
52
|
+
}>;
|
|
53
|
+
}>;
|
|
54
|
+
}): FilterTaxonomySignature | null;
|
|
55
|
+
export declare function areFilterTaxonomySignaturesEqual(left: FilterTaxonomySignature | null | undefined, right: FilterTaxonomySignature | null | undefined): boolean;
|
|
31
56
|
export declare function generateClientUuid(): string;
|
|
32
57
|
export declare function logBootstrapDebug(event: string, details?: Record<string, unknown>): void;
|
|
33
58
|
export declare function resetRemoteUiStatePersistCache(): void;
|
|
@@ -4,6 +4,44 @@ const APP_UI_STATE_STORAGE_KEY_PREFIX = 'taskforce.uiState.app.v1';
|
|
|
4
4
|
const BOOTSTRAP_DEBUG_LOG_KEY = 'taskforce.bootstrapDebug.v1';
|
|
5
5
|
const REMOTE_UI_STATE_FAILURE_RETRY_MS = 5000;
|
|
6
6
|
const remoteUiStatePersistCache = new Map();
|
|
7
|
+
function normalizeSignatureValues(values) {
|
|
8
|
+
return Array.from(new Set(values
|
|
9
|
+
.map((value) => String(value ?? '').trim())
|
|
10
|
+
.filter((value) => value.length > 0))).sort();
|
|
11
|
+
}
|
|
12
|
+
export function buildFilterTaxonomySignature(input) {
|
|
13
|
+
const categories = normalizeSignatureValues(input.categories.map((category) => category.value));
|
|
14
|
+
const types = normalizeSignatureValues(input.types.map((type) => type.value));
|
|
15
|
+
const priorities = normalizeSignatureValues(input.priorities.map((priority) => priority.value));
|
|
16
|
+
if (categories.length === 0 || types.length === 0 || priorities.length === 0)
|
|
17
|
+
return null;
|
|
18
|
+
const taxonomies = {};
|
|
19
|
+
(input.taxonomies || []).forEach((taxonomy) => {
|
|
20
|
+
const id = String(taxonomy.id ?? '').trim();
|
|
21
|
+
if (!id)
|
|
22
|
+
return;
|
|
23
|
+
taxonomies[id] = normalizeSignatureValues((taxonomy.options || []).map((option) => option.value));
|
|
24
|
+
});
|
|
25
|
+
return { categories, types, priorities, taxonomies };
|
|
26
|
+
}
|
|
27
|
+
function haveSameStringValues(left, right) {
|
|
28
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
29
|
+
}
|
|
30
|
+
export function areFilterTaxonomySignaturesEqual(left, right) {
|
|
31
|
+
if (!left || !right)
|
|
32
|
+
return false;
|
|
33
|
+
if (!haveSameStringValues(left.categories || [], right.categories || []))
|
|
34
|
+
return false;
|
|
35
|
+
if (!haveSameStringValues(left.types || [], right.types || []))
|
|
36
|
+
return false;
|
|
37
|
+
if (!haveSameStringValues(left.priorities || [], right.priorities || []))
|
|
38
|
+
return false;
|
|
39
|
+
const leftTaxonomyKeys = Object.keys(left.taxonomies || {}).sort();
|
|
40
|
+
const rightTaxonomyKeys = Object.keys(right.taxonomies || {}).sort();
|
|
41
|
+
if (!haveSameStringValues(leftTaxonomyKeys, rightTaxonomyKeys))
|
|
42
|
+
return false;
|
|
43
|
+
return leftTaxonomyKeys.every((key) => (haveSameStringValues(left.taxonomies[key] || [], right.taxonomies[key] || [])));
|
|
44
|
+
}
|
|
7
45
|
export function generateClientUuid() {
|
|
8
46
|
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
|
9
47
|
return crypto.randomUUID();
|
|
@@ -66,16 +66,6 @@ export function registerAdminWorkspaceTools(registerTool, executeTool) {
|
|
|
66
66
|
required: ['types'],
|
|
67
67
|
},
|
|
68
68
|
}));
|
|
69
|
-
register('bulk_update_type', (context) => ({
|
|
70
|
-
description: context.describeTool('Bulk-update tasks from one task type to another. Use get_config first to confirm the current valid type values.'),
|
|
71
|
-
inputSchema: {
|
|
72
|
-
properties: {
|
|
73
|
-
fromType: stringProperty('Current task type value to replace.', context.typeEnum),
|
|
74
|
-
toType: stringProperty('Replacement task type value.', context.typeEnum),
|
|
75
|
-
},
|
|
76
|
-
required: ['fromType', 'toType'],
|
|
77
|
-
},
|
|
78
|
-
}));
|
|
79
69
|
register('bulk_update_fields', (context) => ({
|
|
80
70
|
description: context.describeTool('Apply field updates to multiple tasks in one request. Use get_config first to confirm valid taxonomy values before calling this tool.'),
|
|
81
71
|
inputSchema: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AiProfileBootstrapConfig } from './aiProfileBootstrap.js';
|
|
2
|
-
export type McpClientId = 'general' | 'cursor' | 'cline' | 'vscode' | 'windsurf' | 'antigravity' | 'openclaw' | 'codex' | 'claude-code' | 'gemini-cli' | 'claude-desktop' | 'chatgpt-desktop';
|
|
2
|
+
export type McpClientId = 'general' | 'cursor' | 'cline' | 'vscode' | 'kiro' | 'windsurf' | 'antigravity' | 'openclaw' | 'codex' | 'grok' | 'claude-code' | 'gemini-cli' | 'claude-desktop' | 'chatgpt-desktop';
|
|
3
3
|
export interface McpClientOption {
|
|
4
4
|
id: McpClientId;
|
|
5
5
|
label: string;
|
|
@@ -4,10 +4,12 @@ const LOCAL_AI_PROFILE_DEFAULTS = {
|
|
|
4
4
|
cursor: { name: 'Cursor', icon: 'Bot', role: 'IDE coding assistant', provider: 'Cursor', model: 'Cursor', surfaceType: 'ide' },
|
|
5
5
|
cline: { name: 'Cline', icon: 'Bot', role: 'IDE coding assistant', provider: 'Cline', model: 'Cline', surfaceType: 'ide' },
|
|
6
6
|
vscode: { name: 'VS Code Copilot', icon: 'Bot', role: 'IDE coding assistant', provider: 'Microsoft', model: 'Copilot', surfaceType: 'ide' },
|
|
7
|
+
kiro: { name: 'Kiro', icon: 'Bot', role: 'IDE coding assistant', provider: 'Amazon', model: 'Kiro', surfaceType: 'ide' },
|
|
7
8
|
windsurf: { name: 'Windsurf', icon: 'Bot', role: 'IDE coding assistant', provider: 'Windsurf', model: 'Windsurf', surfaceType: 'ide' },
|
|
8
9
|
antigravity: { name: 'Antigravity', icon: 'Bot', role: 'Coding agent', provider: 'Google', model: 'Antigravity', surfaceType: 'coding_tool' },
|
|
9
10
|
openclaw: { name: 'OpenClaw', icon: 'Bot', role: 'AI agent', provider: 'OpenClaw', model: 'OpenClaw', surfaceType: 'agent' },
|
|
10
11
|
codex: { name: 'Codex', icon: 'Bot', role: 'Coding agent', provider: 'OpenAI', model: 'Codex', surfaceType: 'cli' },
|
|
12
|
+
grok: { name: 'Grok CLI', icon: 'Bot', role: 'Coding agent', provider: 'xAI', model: 'Grok CLI', surfaceType: 'cli' },
|
|
11
13
|
'claude-code': { name: 'Claude Code', icon: 'Bot', role: 'Coding agent', provider: 'Anthropic', model: 'Claude Code', surfaceType: 'cli' },
|
|
12
14
|
'gemini-cli': { name: 'Gemini CLI', icon: 'Bot', role: 'Coding agent', provider: 'Google', model: 'Gemini CLI', surfaceType: 'cli' },
|
|
13
15
|
'claude-desktop': { name: 'Claude Desktop', icon: 'Bot', role: 'Desktop AI collaborator', provider: 'Anthropic', model: 'Claude Desktop', surfaceType: 'chat_app' },
|
|
@@ -32,6 +34,8 @@ export const MCP_CLIENT_OPTIONS = [
|
|
|
32
34
|
{ id: 'codex', label: 'Codex' },
|
|
33
35
|
{ id: 'cursor', label: 'Cursor' },
|
|
34
36
|
{ id: 'gemini-cli', label: 'Gemini CLI' },
|
|
37
|
+
{ id: 'grok', label: 'Grok CLI' },
|
|
38
|
+
{ id: 'kiro', label: 'Kiro' },
|
|
35
39
|
{ id: 'openclaw', label: 'OpenClaw' },
|
|
36
40
|
{ id: 'vscode', label: 'VS Code / Copilot' },
|
|
37
41
|
{ id: 'windsurf', label: 'Windsurf' },
|
|
@@ -149,6 +153,18 @@ export function buildCloudMcpClientIntegration(input) {
|
|
|
149
153
|
instructionLabel: 'VS Code Configuration Instructions',
|
|
150
154
|
instructionText: buildConfigurationInstruction('Open .vscode/mcp.json or MCP: Open User Configuration and add this server under the servers object.')
|
|
151
155
|
};
|
|
156
|
+
case 'kiro':
|
|
157
|
+
return {
|
|
158
|
+
clientId,
|
|
159
|
+
kind: 'json',
|
|
160
|
+
rootKey: 'mcpServers',
|
|
161
|
+
serverConfig: {
|
|
162
|
+
url: endpoint,
|
|
163
|
+
headers
|
|
164
|
+
},
|
|
165
|
+
instructionLabel: 'Kiro Configuration Instructions',
|
|
166
|
+
instructionText: buildConfigurationInstruction('Open .kiro/settings/mcp.json in your workspace (or ~/.kiro/settings/mcp.json for user-level config) and add this server under mcpServers.')
|
|
167
|
+
};
|
|
152
168
|
case 'windsurf':
|
|
153
169
|
return {
|
|
154
170
|
clientId,
|
|
@@ -199,6 +215,18 @@ export function buildCloudMcpClientIntegration(input) {
|
|
|
199
215
|
instructionLabel: 'Codex Configuration Instructions',
|
|
200
216
|
instructionText: buildConfigurationInstruction('Open ~/.codex/config.toml and add this Codex MCP entry.')
|
|
201
217
|
};
|
|
218
|
+
case 'grok':
|
|
219
|
+
return {
|
|
220
|
+
clientId,
|
|
221
|
+
kind: 'text',
|
|
222
|
+
renderedText: [
|
|
223
|
+
`[mcp_servers.${serverId}]`,
|
|
224
|
+
`url = "${endpoint}"`,
|
|
225
|
+
`headers = { ${Object.entries(headers).map(([key, value]) => `${key} = "${value}"`).join(', ')} }`
|
|
226
|
+
].join('\n'),
|
|
227
|
+
instructionLabel: 'Grok CLI Configuration Instructions',
|
|
228
|
+
instructionText: buildConfigurationInstruction('Open ~/.grok/config.toml (or .grok/config.toml in your project for project-scoped config) and add this Grok CLI MCP entry.')
|
|
229
|
+
};
|
|
202
230
|
case 'claude-code':
|
|
203
231
|
return {
|
|
204
232
|
clientId,
|
|
@@ -288,6 +316,15 @@ export function buildLocalMcpClientIntegration(input) {
|
|
|
288
316
|
instructionLabel: 'VS Code Configuration Instructions',
|
|
289
317
|
instructionText: buildLocalConfigurationInstruction('Open .vscode/mcp.json or MCP: Open User Configuration and add this server under the servers object.')
|
|
290
318
|
};
|
|
319
|
+
case 'kiro':
|
|
320
|
+
return {
|
|
321
|
+
clientId,
|
|
322
|
+
kind: 'json',
|
|
323
|
+
rootKey: 'mcpServers',
|
|
324
|
+
serverConfig: baseStdioConfig,
|
|
325
|
+
instructionLabel: 'Kiro Configuration Instructions',
|
|
326
|
+
instructionText: buildLocalConfigurationInstruction('Open .kiro/settings/mcp.json in your workspace (or ~/.kiro/settings/mcp.json for user-level config) and add this server under mcpServers.')
|
|
327
|
+
};
|
|
291
328
|
case 'windsurf':
|
|
292
329
|
return {
|
|
293
330
|
clientId,
|
|
@@ -332,6 +369,19 @@ export function buildLocalMcpClientIntegration(input) {
|
|
|
332
369
|
instructionLabel: 'Codex Configuration Instructions',
|
|
333
370
|
instructionText: buildLocalConfigurationInstruction('Open ~/.codex/config.toml and add this local MCP entry.')
|
|
334
371
|
};
|
|
372
|
+
case 'grok':
|
|
373
|
+
return {
|
|
374
|
+
clientId,
|
|
375
|
+
kind: 'text',
|
|
376
|
+
renderedText: [
|
|
377
|
+
`[mcp_servers.${serverId}]`,
|
|
378
|
+
`command = ${formatTomlString(serverConfig.command)}`,
|
|
379
|
+
`args = ${formatTomlArray(serverConfig.args)}`,
|
|
380
|
+
...buildTomlEnvAssignment(serverConfig.env)
|
|
381
|
+
].join('\n'),
|
|
382
|
+
instructionLabel: 'Grok CLI Configuration Instructions',
|
|
383
|
+
instructionText: buildLocalConfigurationInstruction('Open ~/.grok/config.toml (or .grok/config.toml in your project for project-scoped config) and add this local Grok CLI MCP entry.')
|
|
384
|
+
};
|
|
335
385
|
case 'claude-code':
|
|
336
386
|
return {
|
|
337
387
|
clientId,
|
|
@@ -16,8 +16,12 @@ export function getDefaultAiProfileMetadataForClient(clientId) {
|
|
|
16
16
|
return { icon: 'MessageSquareMore', color: '#d97706', description: 'Claude Desktop connected through the Taskforce MCP connector.', role: 'AI Assistant', provider: 'Anthropic', model: 'Claude', surfaceType: 'chat_app' };
|
|
17
17
|
case 'gemini-cli':
|
|
18
18
|
return { icon: 'Gem', color: '#2563eb', description: 'Gemini CLI coding agent connected through MCP.', role: 'CLI Coding Agent', provider: 'Google', model: 'Gemini', surfaceType: 'cli' };
|
|
19
|
+
case 'grok':
|
|
20
|
+
return { icon: 'Sparkles', color: '#111827', description: 'Grok CLI coding agent connected through MCP.', role: 'CLI Coding Agent', provider: 'xAI', model: 'Grok CLI', surfaceType: 'cli' };
|
|
19
21
|
case 'vscode':
|
|
20
22
|
return { icon: 'Code2', color: '#2563eb', description: 'VS Code MCP-connected coding agent.', role: 'Coding Agent', provider: null, model: null, surfaceType: 'ide' };
|
|
23
|
+
case 'kiro':
|
|
24
|
+
return { icon: 'Bot', color: '#7c3aed', description: 'Kiro IDE-connected AI assistant.', role: 'IDE coding assistant', provider: 'Amazon', model: null, surfaceType: 'ide' };
|
|
21
25
|
case 'cline':
|
|
22
26
|
case 'windsurf':
|
|
23
27
|
case 'antigravity':
|
|
@@ -23,11 +23,11 @@ export function registerCollaborationTools(registerTool, executeTool) {
|
|
|
23
23
|
});
|
|
24
24
|
};
|
|
25
25
|
register('list_assignees', (context) => ({
|
|
26
|
-
description: context.describeTool('List eligible assignees for the active workspace. Returns human workspace members and AI profiles that can own tasks. Use this before create_task, update_task,
|
|
26
|
+
description: context.describeTool('List eligible assignees for the active workspace. Returns human workspace members and AI profiles that can own tasks. Use this before create_task, update_task, or bulk_update_fields when you need a valid assignee ID.'),
|
|
27
27
|
inputSchema: { properties: {} },
|
|
28
28
|
}));
|
|
29
29
|
register('get_current_profile', (context) => ({
|
|
30
|
-
description: context.describeTool('Inspect
|
|
30
|
+
description: context.describeTool('Inspect current AI identity for this call (ambient session, connection bootstrap, or explicit profileToken). Returns identityBound (true if this invocation has a resolved identity), bindingSource, profile, taskFilter, suggestedArgs, and guidance. Stateless/scheduled agents: call with profileToken if no ambient bind; use the returned suggestedArgs (with assignee) for list_tasks and pass profileToken on writes. Call before mutating or to decide on resolve_profile.'),
|
|
31
31
|
inputSchema: {
|
|
32
32
|
properties: {
|
|
33
33
|
profileToken: stringProperty('Optional AI profile token to inspect when no profile is currently bound in session state. Pass only a token previously issued to this AI identity.'),
|
|
@@ -35,7 +35,7 @@ export function registerCollaborationTools(registerTool, executeTool) {
|
|
|
35
35
|
},
|
|
36
36
|
}));
|
|
37
37
|
register('resolve_profile', (context) => ({
|
|
38
|
-
description: context.describeTool('Resolve or create
|
|
38
|
+
description: context.describeTool('Resolve or create this AI collaborator profile. For stateless agents (no ambient binding): call with name + surfaceType (and profileToken to re-use); the response includes created, profileToken, profile, *plus* identityBound, bindingSource, taskFilter, suggestedArgs, nextTool, guidance. Store profileToken privately only. Use suggestedArgs for list_tasks; pass profileToken on mutating calls. Never write profileToken to repos, tasks, logs, or docs.'),
|
|
39
39
|
inputSchema: {
|
|
40
40
|
properties: {
|
|
41
41
|
name: stringProperty('Display name for the AI collaborator.'),
|