@taskforcehq/taskforce 0.3.320 → 0.3.322
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/TaskforceCore.js +8 -3
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +29 -5
- package/dist/mcp/runtime.js +72 -10
- package/dist/mcp/taskPlanningRegistrar.js +1 -0
- package/dist/server/routes/admin.d.ts +1 -0
- package/dist/server/routes/admin.js +17 -0
- package/dist/ui/assets/{AgentsModule-BZG0WHDN.js → AgentsModule-7jpDeuVR.js} +1 -1
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-EQEzsETm.js → AnnotatedAttachmentWorkspace-BJLe8DFj.js} +1 -1
- package/dist/ui/assets/{ContextAttachmentManager-B3hESx6y.js → ContextAttachmentManager-AmEny3eI.js} +1 -1
- package/dist/ui/assets/{DocumentWorkspace-Z7bykYtA.js → DocumentWorkspace-Cqdq31Ha.js} +1 -1
- package/dist/ui/assets/{EntityActivityTimeline-B2aXkxiE.js → EntityActivityTimeline-BlS7DhA4.js} +1 -1
- package/dist/ui/assets/{InitiativesModule-CSzXelzW.js → InitiativesModule-C1qfO1uZ.js} +1 -1
- package/dist/ui/assets/{PlansPage-2vebpTBs.js → PlansPage-DFt5w-0Y.js} +1 -1
- package/dist/ui/assets/{TaskContextUpload-Bz06La5A.js → TaskContextUpload-zuro5gDT.js} +1 -1
- package/dist/ui/assets/{TaskSettings-4PbrcOqD.js → TaskSettings-CJPC2Bft.js} +1 -1
- package/dist/ui/assets/{WorkflowsModule-AyOzrP3y.js → WorkflowsModule-CEEGuxsf.js} +1 -1
- package/dist/ui/assets/documentReferences-Ca2P--i5.js +1 -0
- package/dist/ui/assets/index-CGDQY0nN.js +5 -0
- package/dist/ui/index.html +1 -1
- package/package.json +1 -1
- package/dist/ui/assets/documentReferences-CKk8ylqA.js +0 -1
- package/dist/ui/assets/index-rB9GGUCz.js +0 -5
package/dist/TaskforceCore.js
CHANGED
|
@@ -54,6 +54,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
54
54
|
const isPricingRoute = location.pathname === '/pricing';
|
|
55
55
|
const isPlansRoute = location.pathname === '/plans';
|
|
56
56
|
const isPlansScreen = location.pathname === '/' && String(routeQuery.get('screen') || '').trim().toLowerCase() === 'plans';
|
|
57
|
+
const plansPath = buildPlansPath();
|
|
57
58
|
const planSelectionPath = buildPlansPath({ gate: 'plan_selection_required' });
|
|
58
59
|
const navigate = useNavigate();
|
|
59
60
|
const [loginEmail, setLoginEmail] = useState('');
|
|
@@ -1284,7 +1285,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1284
1285
|
|| pendingVerificationCommercialState === 'checkout_pending';
|
|
1285
1286
|
setCommercialOnboardingRedirectActive(commercialOnboardingGate);
|
|
1286
1287
|
setPendingPostAuthRedirect({
|
|
1287
|
-
path: pendingVerificationRedirectPath ||
|
|
1288
|
+
path: pendingVerificationRedirectPath || nextTarget,
|
|
1288
1289
|
workspaceSetupRequired: pendingVerificationWorkspaceSetupRequired,
|
|
1289
1290
|
commercialOnboardingGate
|
|
1290
1291
|
});
|
|
@@ -1608,6 +1609,11 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1608
1609
|
});
|
|
1609
1610
|
const result = createResult || saveResult;
|
|
1610
1611
|
if (!result?.success) {
|
|
1612
|
+
if (result?.code === 'WORKSPACE_LIMIT_REACHED') {
|
|
1613
|
+
setSetupBusy(false);
|
|
1614
|
+
navigate(plansPath, { replace: true });
|
|
1615
|
+
return;
|
|
1616
|
+
}
|
|
1611
1617
|
setSetupError(result?.error || t('setup.saveWorkspaceFailed'));
|
|
1612
1618
|
}
|
|
1613
1619
|
else {
|
|
@@ -1620,8 +1626,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1620
1626
|
setSetupBusy(false);
|
|
1621
1627
|
return;
|
|
1622
1628
|
}
|
|
1623
|
-
const shouldApplyStarterPack =
|
|
1624
|
-
&& Boolean(selectedSetupPack)
|
|
1629
|
+
const shouldApplyStarterPack = Boolean(selectedSetupPack)
|
|
1625
1630
|
&& (setupTaxonomySections.categories || setupTaxonomySections.types || setupTaxonomySections.priorities);
|
|
1626
1631
|
if (shouldApplyStarterPack && selectedSetupPack) {
|
|
1627
1632
|
const applyResult = await settingsModel.onApplySystemTaxonomyPack({
|
|
@@ -622,10 +622,34 @@ export function useTaskforceWorkspaceBootstrap(args) {
|
|
|
622
622
|
setWorkspaceBootstrapPending(true);
|
|
623
623
|
try {
|
|
624
624
|
await fetchBootstrapConfig({ ignoreAuthGuard });
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
625
|
+
const workspaceId = String(currentWorkspaceIdRef.current || 'default').trim() || 'default';
|
|
626
|
+
const deferredStartedAt = getBootstrapTimestamp();
|
|
627
|
+
void fetchTasks(tasksSilent, { ignoreAuthGuard }).then(() => {
|
|
628
|
+
logBootstrapDebug('cloud_bootstrap_tasks_loaded_deferred', {
|
|
629
|
+
reason,
|
|
630
|
+
workspaceId,
|
|
631
|
+
durationMs: Math.max(0, Math.round(getBootstrapTimestamp() - deferredStartedAt))
|
|
632
|
+
});
|
|
633
|
+
}).catch((error) => {
|
|
634
|
+
logBootstrapDebug('cloud_bootstrap_tasks_deferred_failed', {
|
|
635
|
+
reason,
|
|
636
|
+
workspaceId,
|
|
637
|
+
error: error instanceof Error ? error.message : String(error)
|
|
638
|
+
});
|
|
639
|
+
});
|
|
640
|
+
void fetchReferenceData({ ignoreAuthGuard }).then(() => {
|
|
641
|
+
logBootstrapDebug('cloud_bootstrap_reference_data_loaded_deferred', {
|
|
642
|
+
reason,
|
|
643
|
+
workspaceId,
|
|
644
|
+
durationMs: Math.max(0, Math.round(getBootstrapTimestamp() - deferredStartedAt))
|
|
645
|
+
});
|
|
646
|
+
}).catch((error) => {
|
|
647
|
+
logBootstrapDebug('cloud_bootstrap_reference_data_deferred_failed', {
|
|
648
|
+
reason,
|
|
649
|
+
workspaceId,
|
|
650
|
+
error: error instanceof Error ? error.message : String(error)
|
|
651
|
+
});
|
|
652
|
+
});
|
|
629
653
|
void fetchPlanningEntities({ ignoreAuthGuard });
|
|
630
654
|
if (includeDeferredArchive) {
|
|
631
655
|
scheduleDeferredArchiveBootstrap();
|
|
@@ -636,7 +660,7 @@ export function useTaskforceWorkspaceBootstrap(args) {
|
|
|
636
660
|
authenticated: true,
|
|
637
661
|
workspaceSetupRequired: false,
|
|
638
662
|
success: true,
|
|
639
|
-
workspaceId
|
|
663
|
+
workspaceId
|
|
640
664
|
});
|
|
641
665
|
return { success: true, authenticated: true, workspaceSetupRequired: false };
|
|
642
666
|
}
|
package/dist/mcp/runtime.js
CHANGED
|
@@ -740,6 +740,21 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
740
740
|
}))
|
|
741
741
|
.filter((actor) => Boolean(actor.id));
|
|
742
742
|
}
|
|
743
|
+
function buildTaskWriteSummary(task) {
|
|
744
|
+
return {
|
|
745
|
+
id: task?.id || null,
|
|
746
|
+
referenceLabel: getTaskReferenceLabel(task) || null,
|
|
747
|
+
title: task?.title || null,
|
|
748
|
+
status: task?.status || null,
|
|
749
|
+
assignee: task?.assignee || null,
|
|
750
|
+
workstreamId: task?.workstreamId || null,
|
|
751
|
+
scheduledDate: task?.scheduledDate || null,
|
|
752
|
+
dueDate: task?.dueDate || null,
|
|
753
|
+
scheduledWeekKey: task?.scheduledWeekKey || null,
|
|
754
|
+
orderInDay: task?.orderInDay ?? null,
|
|
755
|
+
updatedAt: task?.updatedAt || null,
|
|
756
|
+
};
|
|
757
|
+
}
|
|
743
758
|
function buildAiProfileDescriptor(profile) {
|
|
744
759
|
return {
|
|
745
760
|
id: profile.id,
|
|
@@ -2178,7 +2193,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
2178
2193
|
const assigneeHint = describeEligibleAssignees();
|
|
2179
2194
|
const mutatingProfileTokenProperty = {
|
|
2180
2195
|
type: "string",
|
|
2181
|
-
description: "Optional AI profile token returned by resolve_profile. Pass this when your MCP client does not preserve session identity between calls."
|
|
2196
|
+
description: "Optional AI profile token returned by resolve_profile. Pass this when your MCP client does not preserve session identity between calls. In stateless/cloud MCP clients without ambient AI binding, this is usually required on mutating calls after bootstrap."
|
|
2182
2197
|
};
|
|
2183
2198
|
const prefix = `[Project: ${PROJECT_NAME}] `;
|
|
2184
2199
|
const agentNote = runtimeMode === 'local'
|
|
@@ -2190,11 +2205,32 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
2190
2205
|
.filter(Boolean)
|
|
2191
2206
|
.map((part) => `${part.charAt(0).toUpperCase()}${part.slice(1)}`)
|
|
2192
2207
|
.join(' ');
|
|
2208
|
+
const nonDestructiveWriteTools = new Set([
|
|
2209
|
+
'add_annotated_attachment_marker',
|
|
2210
|
+
'add_comment',
|
|
2211
|
+
'add_document_review_comment',
|
|
2212
|
+
'bulk_add_annotated_attachment_markers',
|
|
2213
|
+
'create_annotated_attachment_session',
|
|
2214
|
+
'create_document_review_session',
|
|
2215
|
+
'create_initiative',
|
|
2216
|
+
'create_task',
|
|
2217
|
+
'create_workstream',
|
|
2218
|
+
'link_task_context',
|
|
2219
|
+
'resolve_profile',
|
|
2220
|
+
'review_document',
|
|
2221
|
+
'review_image',
|
|
2222
|
+
]);
|
|
2193
2223
|
const annotationsForTool = (name) => {
|
|
2194
2224
|
const title = titleForTool(name);
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2225
|
+
if (!isMcpWriteToolName(name)) {
|
|
2226
|
+
return { title, readOnlyHint: true, destructiveHint: false, openWorldHint: false };
|
|
2227
|
+
}
|
|
2228
|
+
return {
|
|
2229
|
+
title,
|
|
2230
|
+
readOnlyHint: false,
|
|
2231
|
+
destructiveHint: !nonDestructiveWriteTools.has(name),
|
|
2232
|
+
openWorldHint: false,
|
|
2233
|
+
};
|
|
2198
2234
|
};
|
|
2199
2235
|
const tools = registeredTools
|
|
2200
2236
|
.map((tool) => tool.buildDescriptor({
|
|
@@ -2915,7 +2951,13 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
2915
2951
|
workstreamIds: [resolvedWorkstreamId],
|
|
2916
2952
|
});
|
|
2917
2953
|
return {
|
|
2918
|
-
content: [{
|
|
2954
|
+
content: [{
|
|
2955
|
+
type: "text",
|
|
2956
|
+
text: JSON.stringify({
|
|
2957
|
+
task: buildTaskWriteSummary(updated),
|
|
2958
|
+
workstreamId: resolvedWorkstreamId,
|
|
2959
|
+
}, null, 2)
|
|
2960
|
+
}],
|
|
2919
2961
|
};
|
|
2920
2962
|
}
|
|
2921
2963
|
case "clear_task_workstream": {
|
|
@@ -2935,7 +2977,14 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
2935
2977
|
...(priorWorkstreamId ? { includeWorkstreams: true, workstreamIds: [priorWorkstreamId] } : {}),
|
|
2936
2978
|
});
|
|
2937
2979
|
return {
|
|
2938
|
-
content: [{
|
|
2980
|
+
content: [{
|
|
2981
|
+
type: "text",
|
|
2982
|
+
text: JSON.stringify({
|
|
2983
|
+
task: buildTaskWriteSummary(updated),
|
|
2984
|
+
workstreamId: null,
|
|
2985
|
+
previousWorkstreamId: priorWorkstreamId,
|
|
2986
|
+
}, null, 2)
|
|
2987
|
+
}],
|
|
2939
2988
|
};
|
|
2940
2989
|
}
|
|
2941
2990
|
case "set_workstream_initiative": {
|
|
@@ -4001,7 +4050,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
4001
4050
|
persistedOrderInDay: result.task.orderInDay ?? null
|
|
4002
4051
|
},
|
|
4003
4052
|
warnings: result.warnings,
|
|
4004
|
-
task: result.task
|
|
4053
|
+
task: buildTaskWriteSummary(result.task)
|
|
4005
4054
|
}, null, 2)
|
|
4006
4055
|
}],
|
|
4007
4056
|
};
|
|
@@ -4154,6 +4203,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
4154
4203
|
priority: resolveNumberOption('priority', createArgs.priority, getActivePriorities().map(p => ({ value: p.value, label: p.label }))),
|
|
4155
4204
|
approach: resolveStringOption('approach', createArgs.approach, getActiveApproaches().map(a => ({ value: a.value, label: a.label }))),
|
|
4156
4205
|
assignee: resolveRuntimeTaskAssigneeInput(createAssigneeInput),
|
|
4206
|
+
workstreamId: createArgs.workstreamId ? resolveWorkstreamByIdentifierOrThrow(createArgs.workstreamId).id : undefined,
|
|
4157
4207
|
status: resolveStringOption('status', createArgs.status, STATUS_OPTIONS),
|
|
4158
4208
|
complexity: resolveNumberOption('complexity', createArgs.complexity, COMPLEXITY_OPTIONS)
|
|
4159
4209
|
};
|
|
@@ -4219,7 +4269,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
4219
4269
|
content: [{
|
|
4220
4270
|
type: "text",
|
|
4221
4271
|
text: JSON.stringify({
|
|
4222
|
-
task: updated,
|
|
4272
|
+
task: buildTaskWriteSummary(updated),
|
|
4223
4273
|
started: true,
|
|
4224
4274
|
claimed: resolved.claimed && updated.assignee === activeAiProfileId,
|
|
4225
4275
|
statusChanged: resolved.previousStatus !== String(updated.status || '').trim(),
|
|
@@ -4294,7 +4344,13 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
4294
4344
|
includeStats: true,
|
|
4295
4345
|
});
|
|
4296
4346
|
return {
|
|
4297
|
-
content: [{
|
|
4347
|
+
content: [{
|
|
4348
|
+
type: "text",
|
|
4349
|
+
text: JSON.stringify({
|
|
4350
|
+
task: buildTaskWriteSummary(updated),
|
|
4351
|
+
changedFields: Object.keys(finalUpdates),
|
|
4352
|
+
}, null, 2)
|
|
4353
|
+
}],
|
|
4298
4354
|
};
|
|
4299
4355
|
}
|
|
4300
4356
|
catch (error) {
|
|
@@ -4385,7 +4441,13 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
4385
4441
|
includeStats: true,
|
|
4386
4442
|
});
|
|
4387
4443
|
return {
|
|
4388
|
-
content: [{
|
|
4444
|
+
content: [{
|
|
4445
|
+
type: "text",
|
|
4446
|
+
text: JSON.stringify({
|
|
4447
|
+
task: buildTaskWriteSummary(updated),
|
|
4448
|
+
assignee: resolvedAssignee,
|
|
4449
|
+
}, null, 2)
|
|
4450
|
+
}],
|
|
4389
4451
|
};
|
|
4390
4452
|
}
|
|
4391
4453
|
case "bulk_assign_tasks": {
|
|
@@ -353,6 +353,7 @@ export function registerTaskPlanningTools(registerTool, executeTool) {
|
|
|
353
353
|
title: stringProperty('Task title.'),
|
|
354
354
|
description: stringProperty('Optional task description.'),
|
|
355
355
|
assignee: stringProperty(`${context.assigneeHint} Call list_assignees to inspect the current eligible IDs.`),
|
|
356
|
+
workstreamId: stringProperty('Optional workstream ID or workstream reference such as WS-123. Use this to create the task directly inside a workstream without a separate set_task_workstream call.'),
|
|
356
357
|
category: stringProperty('Task category value.'),
|
|
357
358
|
type: stringProperty('Task type value.', context.typeEnum),
|
|
358
359
|
priority: numberProperty(context.priorityDesc),
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
* POST /api/taskforce/admin/workspace-repair/scan
|
|
41
41
|
* POST /api/taskforce/admin/workspace-repair/apply
|
|
42
42
|
* POST/GET/DELETE/OPTIONS /mcp (+ /mcp/)
|
|
43
|
+
* GET /.well-known/openai-apps-challenge
|
|
43
44
|
* GET /.well-known/oauth-authorization-server
|
|
44
45
|
* GET /.well-known/oauth-protected-resource
|
|
45
46
|
* POST /oauth/mcp/register
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
* POST /api/taskforce/admin/workspace-repair/scan
|
|
41
41
|
* POST /api/taskforce/admin/workspace-repair/apply
|
|
42
42
|
* POST/GET/DELETE/OPTIONS /mcp (+ /mcp/)
|
|
43
|
+
* GET /.well-known/openai-apps-challenge
|
|
43
44
|
* GET /.well-known/oauth-authorization-server
|
|
44
45
|
* GET /.well-known/oauth-protected-resource
|
|
45
46
|
* POST /oauth/mcp/register
|
|
@@ -73,6 +74,7 @@ import { buildAuthBehavior, buildSiteBehavior, getRouteEmailConfig, resolvePubli
|
|
|
73
74
|
import { applyWorkspaceRepair, scanWorkspaceRepair } from '../../sync/workspaceRepair.js';
|
|
74
75
|
import { testMintAppleClientSecret } from '../auth/providers/appleClientSecret.js';
|
|
75
76
|
const PRICING_PAGE_DESCRIPTION_MAX_LENGTH = 2000;
|
|
77
|
+
const OPENAI_APPS_CHALLENGE_TOKEN = 'o9iBeJDuKPMtnKm5-B0BROga3bWDkJS6PBIg6JT9Izs';
|
|
76
78
|
// === SECTION: Registration ===
|
|
77
79
|
export function registerAdminRoutes(deps) {
|
|
78
80
|
const { addRoute, core, context, requestWorkspaceId, requestUserId, requestRole, requestHeader, requestRuntimeMode, ensureAppAccess, ensureAdminRole, ensureCloudPlanData, requireCloudRuntime, resolveAuthenticatedUserId, resolveAdminRequestActor, auditAdminAction, resolveEffectiveObjectStorageConfig, getObjectStorageClient, taskAssetStore, normalizeDocumentPathInput, ensureWithinDir, getAssetMetadata, markAssetDeleted, clearAssetDeleted, removeAssetMetadata, getStorageTelemetry, appendStorageTelemetry, getStorageIntegrityHistory, appendStorageIntegrityHistory, getDocumentPurgeHistory, appendDocumentPurgeHistory, getCollaborationTelemetry, isCollaborationPolicyEnabled, normalizeMcpOauthScopeString, MCP_TOKEN_SCOPE_VALUES, MCP_CLOUD_TOOL_SCOPE_REQUIREMENTS, MCP_WRITE_RATE_LIMIT_ENABLED, MCP_WRITE_RATE_LIMIT_ACTOR_MAX_REQUESTS, MCP_WRITE_RATE_LIMIT_WORKSPACE_MAX_REQUESTS, MCP_WRITE_RATE_LIMIT_WINDOW_MS, } = deps;
|
|
@@ -2524,6 +2526,21 @@ export function registerAdminRoutes(deps) {
|
|
|
2524
2526
|
});
|
|
2525
2527
|
res.end();
|
|
2526
2528
|
});
|
|
2529
|
+
// === SECTION: ChatGPT Apps domain verification ===
|
|
2530
|
+
addRoute('GET', '/.well-known/openai-apps-challenge', async (_req, res) => {
|
|
2531
|
+
res.writeHead(200, {
|
|
2532
|
+
'Content-Type': 'text/plain; charset=utf-8',
|
|
2533
|
+
'Cache-Control': 'no-store'
|
|
2534
|
+
});
|
|
2535
|
+
res.end(OPENAI_APPS_CHALLENGE_TOKEN);
|
|
2536
|
+
});
|
|
2537
|
+
addRoute('HEAD', '/.well-known/openai-apps-challenge', async (_req, res) => {
|
|
2538
|
+
res.writeHead(200, {
|
|
2539
|
+
'Content-Type': 'text/plain; charset=utf-8',
|
|
2540
|
+
'Cache-Control': 'no-store'
|
|
2541
|
+
});
|
|
2542
|
+
res.end();
|
|
2543
|
+
});
|
|
2527
2544
|
// === SECTION: MCP OAuth 2.0 ===
|
|
2528
2545
|
addRoute('GET', '/.well-known/oauth-authorization-server', async (req, res) => {
|
|
2529
2546
|
const issuer = resolveMcpOauthIssuer(req);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as c,j as e}from"./vendor-react-CKJs5o3c.js";import{s as Ue,p as _,u as ie,v as De,w as se,x as le,y as we,t as r,E as Te,z as Me,B as ke,C as fe,F as b,G as oe}from"./index-rB9GGUCz.js";import{a0 as Re,x as $e,h as Ee,B as ne,m as C,b as Le,$ as Be,a5 as Ge}from"./vendor-icons-CLnehDTw.js";import"./vendor-markdown-BUxTU7dS.js";import"./vendor-dnd-DRzYolkg.js";import"./vendor-router-BbWMxlnO.js";function Fe(l){return typeof l.avatarUrl=="string"&&l.avatarUrl.trim().length>0}function K(l){return l?fe(l.avatarUrl,l.avatarRevision,l.avatarUpdatedAt):""}function ze(l){return l?fe(l.avatarSourceUrl,l.avatarRevision,l.avatarUpdatedAt):""}function He(l){return l.find(Fe)||l[0]}function ce(l){return`${l.seatScope||"unknown"}:${l.name.trim().toLowerCase()}`}function Oe(l){const m=Math.max(0,l-1);return`${m} duplicate${m===1?"":"s"}`}function de(l){return!l||typeof l!="object"?null:{used:Math.max(0,Number(l.used||0)),limit:l.limit===null||l.limit===void 0?null:Math.max(0,Number(l.limit||0)),remaining:l.remaining===null||l.remaining===void 0?null:Math.max(0,Number(l.remaining||0))}}function pe(l,m){const U=m?.variant==="inline"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconInline}`:m?.variant==="detail"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconDetail}`:r.aiProfileSeatScopeIcon;return l==="cloud_metered"?e.jsx("span",{className:`${U} ${r.aiProfileSeatScopeIconCloud}`,title:"Cloud MCP","aria-label":"Cloud MCP",children:e.jsx(Be,{size:20})}):l==="local_unmetered"?e.jsx("span",{className:`${U} ${r.aiProfileSeatScopeIconLocal}`,title:"Local MCP","aria-label":"Local MCP",children:e.jsx(Ge,{size:20})}):null}function qe({workspaceId:l,cloudAuthConfigured:m=!1,authSessionResolved:U=!1,isAuthenticated:ue=!1,cloudAiProfileSeatUsage:me=null,agentTrayOpen:E=!1,onCloseAgentTray:he,mcpSettingsNode:V=null}){const[S,L]=c.useState([]),[ve,ge]=c.useState(null),[B,J]=c.useState(!1),[h,D]=c.useState(null),[w,v]=c.useState(null),[T,x]=c.useState(null),[W,G]=c.useState(null),[ye,Y]=c.useState(!1),[P,j]=c.useState(null),[q,Q]=c.useState(null),[X,M]=c.useState(!1),[Pe,g]=c.useState(null),k=c.useCallback(async()=>{if(l){J(!0);try{const a=`/api/taskforce/workspace/assignee-options?workspaceId=${encodeURIComponent(l)}&kind=agent`,t=await fetch(a,{credentials:"include"}),i=t.ok?await t.json().catch(()=>({})):{},o=de(i?.aiProfileSeatUsage),d=Array.isArray(i?.assignees)?i.assignees.filter(s=>s.kind==="agent").map(s=>({id:String(s.value||""),name:String(s.label||s.value||"Unknown Agent"),username:String(s.username||s.value||""),icon:String(s.icon||"Bot"),color:String(s.color||"#6B7280"),avatarUrl:typeof s.avatarUrl=="string"?s.avatarUrl:null,avatarSourceUrl:typeof s.avatarSourceUrl=="string"?s.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(s.avatarRevision))?Math.max(0,Math.floor(Number(s.avatarRevision))):0,avatarUpdatedAt:typeof s.avatarUpdatedAt=="string"?s.avatarUpdatedAt:null,kind:String(s.kind||"agent"),description:typeof s.description=="string"?s.description:null,role:typeof s.role=="string"?s.role:null,provider:typeof s.provider=="string"?s.provider:null,model:typeof s.model=="string"?s.model:null,surfaceType:_(s.surfaceType),seatScope:Ue(s.seatScope),archivedAt:typeof s.archivedAt=="string"?s.archivedAt:null,createdAt:String(s.createdAt||""),updatedAt:String(s.updatedAt||s.createdAt||""),lastActiveAt:typeof s.lastActiveAt=="string"?s.lastActiveAt:null})):[];L(d),ge(o),x(s=>s&&!d.some(p=>p.id===s.profileId)?null:s),G(s=>s&&!d.some(p=>p.id===s.profileId)?null:s)}finally{J(!1)}}},[l]);c.useEffect(()=>{k()},[k]);const R=c.useMemo(()=>{const a=new Map;for(const t of S){const i=ie(t.surfaceType);a.has(i)||a.set(i,new Map);const o=a.get(i),d=ce(t);o.has(d)||o.set(d,[]),o.get(d).push(t)}return De.map(t=>({section:t,label:se(t),groups:Array.from(a.get(t)?.entries()||[]).map(([i,o])=>({groupId:`${t}:${i}`,section:t,sectionLabel:se(t),profiles:o,primaryProfile:He(o)}))})).filter(t=>t.groups.length>0)},[S]),y=c.useMemo(()=>R.flatMap(a=>a.groups),[R]),n=c.useMemo(()=>y.find(a=>a.groupId===P)||y[0]||null,[y,P]),u=c.useMemo(()=>S.find(a=>a.id===q)||null,[S,q]);c.useEffect(()=>{if(!y.length){P!==null&&j(null);return}(!P||!y.some(a=>a.groupId===P))&&j(y[0].groupId)},[y,P]);const Ae=async(a,t)=>{v(null),x(null);try{const i=await fetch("/api/taskforce/workspace/ai-profiles/merge",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({keepId:a,mergeId:t})}),o=await i.json().catch(()=>({}));if(!i.ok){v({type:"error",message:String(o?.error||"Failed to merge AI profiles.")});return}D(null),v({type:"success",message:"AI profiles merged."}),await k(),b({workspaceId:l,profileId:a,reason:"merge"})}catch{v({type:"error",message:"Failed to merge AI profiles."})}},Z=async a=>{if(window.confirm(`Remove ${a.name} from the active roster? This frees an AI profile seat and preserves task and comment history.`)){x(null),v(null);try{const i=await fetch("/api/taskforce/workspace/ai-profiles/archive",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:a.id,reason:"manual_archive"})}),o=await i.json().catch(()=>({}));if(!i.ok){x({profileId:a.id,message:String(o?.error||"Failed to remove AI profile from roster.")});return}(h?.keepId===a.id||h?.mergeId===a.id)&&D(null),v({type:"success",message:"AI profile removed from active roster."}),await k(),b({workspaceId:l,profileId:a.id,reason:"archive"})}catch{x({profileId:a.id,message:"Failed to remove AI profile from roster."})}}},Se=async(a,t)=>{const i=_(t),o=new Set(a.profiles.map(p=>p.surfaceType??"")),d=i??"";if(o.size===1&&o.has(d))return;Y(!0),G(null),v(null);const s=[];try{for(const f of a.profiles){const I=await fetch("/api/taskforce/workspace/ai-profiles/surface-type",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:f.id,surfaceType:i})}),H=await I.json().catch(()=>({}));if(!I.ok||!H?.profile)throw new Error(String(H?.error||"Failed to update AI profile category."));const O=H.profile;s.push({...f,surfaceType:_(O.surfaceType),updatedAt:typeof O.updatedAt=="string"?O.updatedAt:f.updatedAt})}const p=new Map(s.map(f=>[f.id,f]));L(f=>f.map(I=>p.get(I.id)||I));const A=p.get(a.primaryProfile.id)||{...a.primaryProfile,surfaceType:i},Ce=ie(A.surfaceType);j(`${Ce}:${ce(A)}`),v({type:"success",message:"AI profile category updated."});for(const f of s)b({workspaceId:l,profileId:f.id,reason:"update"})}catch(p){G({profileId:a.primaryProfile.id,message:String(p?.message||"Failed to update AI profile category.")})}finally{Y(!1)}},ee=a=>new Promise((t,i)=>{const o=new FileReader;o.onload=()=>t(String(o.result||"")),o.onerror=()=>i(new Error("Failed to read image file.")),o.readAsDataURL(a)}),ae=a=>{const t=String(a?.id||"").trim();t&&L(i=>i.map(o=>o.id===t?{...o,avatarUrl:typeof a.avatarUrl=="string"?a.avatarUrl:null,avatarSourceUrl:typeof a.avatarSourceUrl=="string"?a.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(a.avatarRevision))?Math.max(0,Math.floor(Number(a.avatarRevision))):o.avatarRevision,avatarUpdatedAt:typeof a.avatarUpdatedAt=="string"?a.avatarUpdatedAt:o.avatarUpdatedAt,updatedAt:typeof a.updatedAt=="string"?a.updatedAt:o.updatedAt}:o))},xe=async(a,t,i)=>{const o=await ee(t),d=i?await ee(i):null,s={profileId:a,displayImage:{dataUrl:o,mimeType:t.type||"application/octet-stream",originalName:t.name||"display-avatar"}};i&&d&&(s.sourceImage={dataUrl:d,mimeType:i.type||"application/octet-stream",originalName:i.name||"source-avatar"});const p=await fetch("/api/taskforce/workspace/ai-profiles/avatar/upload",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)}),A=await p.json().catch(()=>({}));if(!p.ok||!A?.profile)throw new Error(String(A?.error||"Failed to update AI profile avatar."));ae(A.profile),b({workspaceId:l,profileId:a,reason:"avatar"})},je=async a=>{const t=await fetch("/api/taskforce/workspace/ai-profiles/avatar",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:a,avatarUrl:null,avatarSourceUrl:null})}),i=await t.json().catch(()=>({}));if(!t.ok||!i?.profile)throw new Error(String(i?.error||"Failed to update AI profile avatar."));ae(i.profile),b({workspaceId:l,profileId:a,reason:"avatar"})},Ne=async(a,t)=>{if(!u)return!1;if(!a.type.startsWith("image/"))return g("AI profile photo must be an image file."),!1;M(!0),g(null);try{const i=await oe(a,{maxBytes:5242880});if(i.exceededLimit)throw new Error(a.type==="image/gif"?"Animated GIF AI profile photos must be 5 MB or smaller.":"AI profile photo must be 5 MB or smaller.");const o=i.file;let d=null;if(t){const s=await oe(t,{maxBytes:5242880});if(s.exceededLimit)throw new Error(t.type==="image/gif"?"Animated GIF AI profile photos must be 5 MB or smaller.":"AI profile source photo must be 5 MB or smaller.");d=s.file}return await xe(u.id,o,d),!0}catch(i){return g(String(i?.message||"Failed to update AI profile photo.")),!1}finally{M(!1)}},Ie=async()=>{if(u){M(!0),g(null);try{await je(u.id)}catch(a){g(String(a?.message||"Failed to remove AI profile photo."))}finally{M(!1)}}},N=a=>{const t=String(a||"").trim();if(!t)return"Unknown";const i=Date.parse(t);return Number.isFinite(i)?new Date(i).toLocaleString(void 0,{dateStyle:"medium",timeStyle:"short"}):t},re=c.useMemo(()=>{if(!n)return{attributes:[],dates:[]};const a=n.primaryProfile,t=i=>i||"Not set";return{attributes:[{label:"Category",value:a.surfaceType?le(a.surfaceType):""},{label:"Connection",value:a.seatScope?we(a.seatScope):""},{label:"Provider",value:a.provider||""},{label:"Model",value:a.model||""}].map(i=>({...i,value:t(i.value)})),dates:[{label:"Status",value:a.archivedAt?"Retired":"Active"},{label:"Recruited",value:N(a.createdAt)},{label:"Last updated",value:N(a.updatedAt)},{label:"Last active",value:N(a.lastActiveAt)}].map(i=>({...i,value:t(i.value)}))}},[n]),be=!!n&&n.profiles.length>1,te=m&&U&&!ue,$=m?de(me):ve,F=te?"Log into account for Cloud agents":$?`${$.used}/${$.limit===null?"Unlimited":$.limit}`:null,z=!!V;return e.jsxs("section",{className:`${r.agentsModuleRoot} ${z?r.agentsModuleWithTray:""} ${z&&E?r.agentsModuleTrayOpen:""}`.trim(),children:[z&&e.jsxs("aside",{className:`${r.agentTrayPanel} ${E?r.agentTrayPanelOpen:""}`.trim(),"aria-label":"Agent MCP settings tray","aria-hidden":!E,children:[e.jsxs("div",{className:r.agentTrayHeader,children:[e.jsxs("span",{className:r.agentTrayTitle,children:[e.jsx(Re,{size:14}),"MCP Settings"]}),e.jsx("button",{type:"button",className:"tf-control-icon",onClick:he,title:"Collapse agent tray","aria-label":"Collapse agent tray",children:e.jsx($e,{size:16})})]}),e.jsx("div",{className:`${r.agentTrayContent} tf-scrollbar tf-scrollbar--track-transparent`,children:e.jsx("div",{className:r.agentTrayContentInner,children:V})})]}),e.jsx("div",{className:r.agentsModuleContent,children:e.jsx("div",{className:`${r.settingGroup} ${r.agentsModuleGroup}`,children:e.jsxs("div",{children:[e.jsx("h4",{className:r.settingSubTitle,children:"Registered AI Profiles"}),e.jsx("p",{className:`${r.settingsHint} ${r.marginBottom12}`,children:"AI agents register profiles when connecting via MCP. Merge duplicates created when a token was lost, or remove inactive profiles from the active roster to free seats while preserving history."}),F&&e.jsx("div",{className:r.aiProfileSeatSummary,children:te?F:`Registered Cloud Agents: ${F}`}),B&&e.jsxs("div",{className:r.settingsHint,children:[e.jsx(Ee,{size:13,className:r.spinner})," Loading profiles…"]}),!B&&S.length===0&&e.jsx("div",{className:r.settingsHint,children:"No AI profiles registered yet."}),!B&&R.length>0&&e.jsxs("div",{className:r.aiProfilesExplorer,children:[e.jsx("div",{className:`${r.aiProfilesListPane} tf-scrollbar tf-scrollbar--track-transparent`,children:e.jsx("div",{className:r.aiProfilesList,children:R.map(a=>e.jsxs("div",{className:r.aiProfilesSection,children:[e.jsx("div",{className:r.aiProfilesSectionHeader,children:a.label}),a.groups.map(t=>{const i=n?.groupId===t.groupId,o=t.primaryProfile;return e.jsxs("div",{role:"button",tabIndex:0,className:`${r.aiProfileGroup} ${t.profiles.length>1?r.aiProfileGroupDuplicate:""} ${i?r.aiProfileGroupSelected:""}`,onClick:()=>j(t.groupId),onKeyDown:d=>{d.key!=="Enter"&&d.key!==" "||(d.preventDefault(),j(t.groupId))},"aria-pressed":i,children:[pe(o.seatScope),e.jsxs("div",{className:r.aiProfileGroupHeader,children:[e.jsx("span",{className:r.aiProfileGroupAvatar,style:{color:o.color},children:o.avatarUrl?e.jsx("img",{src:K(o),alt:""}):e.jsx(ne,{size:22})}),e.jsxs("span",{className:r.aiProfileGroupIdentity,children:[e.jsx("span",{className:r.aiProfileName,children:o.name}),e.jsxs("span",{className:r.aiProfileHandle,children:["@",o.username]}),e.jsx("span",{className:r.aiProfileRole,children:o.role||"Role not set"}),t.profiles.length>1&&e.jsxs("span",{className:r.aiProfileDuplicateBadge,children:[e.jsx(C,{size:11})," ",Oe(t.profiles.length)]})]})]})]},t.groupId)})]},a.section))})}),n&&e.jsx("div",{className:r.aiProfileDetailPane,children:e.jsxs("div",{className:r.aiProfileDetailCard,children:[pe(n.primaryProfile.seatScope,{variant:"detail"}),e.jsxs("div",{className:r.aiProfileDetailHero,children:[e.jsx(Te,{label:"Edit AI profile photo",imageUrl:K(n.primaryProfile),fallback:e.jsx(ne,{size:38}),accentColor:n.primaryProfile.color,size:176,width:153,height:207,radius:6,editBadgeSize:28,editIconSize:14,className:r.aiProfileDetailAvatar,onClick:()=>{g(null),Q(n.primaryProfile.id)}}),e.jsxs("div",{className:r.aiProfileDetailHeading,children:[e.jsx("div",{className:r.aiProfileDetailTitleRow,children:e.jsx("h5",{className:r.aiProfileDetailTitle,children:n.primaryProfile.name})}),e.jsxs("div",{className:r.aiProfileDetailMetaRow,children:[e.jsxs("span",{className:r.aiProfileDetailHandle,children:["@",n.primaryProfile.username]}),n.profiles.length>1&&e.jsxs("span",{className:r.aiProfileDetailLinkedCount,children:[n.profiles.length," linked"]})]}),e.jsxs("div",{className:r.aiProfileDetailRole,children:["Role: ",n.primaryProfile.role||"Not set"]}),n.primaryProfile.description&&e.jsx("div",{className:r.aiProfileDetailDescription,children:n.primaryProfile.description}),e.jsxs("div",{className:r.aiProfileSignatureColor,children:[e.jsx("span",{children:"Signature color"}),e.jsx("span",{className:r.aiProfileSignatureSwatch,style:{backgroundColor:n.primaryProfile.color},"aria-hidden":"true"}),e.jsx("span",{children:n.primaryProfile.color})]})]})]}),e.jsxs("div",{className:r.aiProfileDetailDataList,children:[e.jsx("div",{className:r.aiProfileDetailDataGroup,children:re.attributes.map(a=>e.jsxs("div",{className:r.aiProfileDetailDataRow,children:[e.jsx("span",{className:r.aiProfileDetailDataLabel,children:a.label}),a.label==="Category"?e.jsx("span",{className:r.aiProfileDetailDataValue,children:e.jsxs("select",{className:r.aiProfileStatusSelect,"aria-label":"Agent category",value:n.primaryProfile.surfaceType??"",disabled:ye,onChange:t=>{Se(n,t.target.value)},children:[e.jsx("option",{value:"",children:"Unclassified"}),Me.map(t=>e.jsx("option",{value:t,children:le(t)},t))]})}):e.jsx("span",{className:r.aiProfileDetailDataValue,children:a.value})]},a.label))}),e.jsx("div",{className:`${r.aiProfileDetailDataGroup} ${r.aiProfileDetailDateGroup}`,children:re.dates.map(a=>e.jsxs("div",{className:r.aiProfileDetailDataRow,children:[e.jsx("span",{className:r.aiProfileDetailDataLabel,children:a.label}),a.label==="Status"?e.jsx("span",{className:r.aiProfileDetailDataValue,children:e.jsxs("select",{className:r.aiProfileStatusSelect,"aria-label":"Agent roster status",value:n.primaryProfile.archivedAt?"retired":"active",onChange:t=>{t.target.value==="retire"&&Z(n.primaryProfile)},children:[e.jsx("option",{value:"active",children:"Active"}),n.primaryProfile.archivedAt?e.jsx("option",{value:"retired",children:"Retired"}):e.jsx("option",{value:"retire",children:"Retire from roster"})]})}):e.jsx("span",{className:r.aiProfileDetailDataValue,children:a.value})]},a.label))})]}),W?.profileId===n.primaryProfile.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:W.message})]}),be?e.jsxs("div",{className:r.aiProfileInstanceSection,children:[e.jsxs("div",{className:r.aiProfileInstanceSectionHeader,children:[e.jsx("span",{children:"Profile instances"}),e.jsx("span",{className:r.settingsHint,children:"Choose a keeper here if duplicates need to be merged."})]}),e.jsx("div",{className:r.aiProfileInstanceList,children:n.profiles.map(a=>e.jsxs("div",{className:r.aiProfileInstanceCard,children:[e.jsxs("div",{className:r.aiProfileInstanceTopRow,children:[e.jsxs("div",{children:[e.jsxs("div",{className:r.aiProfileInstanceName,children:["@",a.username]}),e.jsx("div",{className:r.aiProfileIdChip,children:a.id})]}),h?.keepId===a.id&&e.jsx("span",{className:r.aiProfileKeepBadge,children:"Keeping"})]}),e.jsxs("div",{className:r.aiProfileInstanceMeta,children:[e.jsxs("span",{children:["Created ",N(a.createdAt)]}),e.jsxs("span",{children:["Updated ",N(a.updatedAt)]})]}),h?.keepId!==a.id&&e.jsx("button",{className:r.secondaryHeaderBtn,title:"Keep this profile, merge others into it",onClick:()=>{const t=n.profiles.find(i=>i.id!==a.id)?.id;t&&D({keepId:a.id,mergeId:t})},children:"Keep this"}),T?.profileId===a.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:T.message})]}),e.jsx("button",{className:r.aiProfileDangerTextButton,title:"Remove this profile from the active roster",onClick:()=>{Z(a)},children:"Remove from roster"})]},a.id))}),h&&n.profiles.some(a=>a.id===h.keepId)&&e.jsxs("div",{className:r.aiProfileMergeActions,children:[e.jsx("button",{className:r.dangerBtn,onClick:()=>{Ae(h.keepId,h.mergeId)},children:"Merge duplicates"}),e.jsx("button",{className:r.secondaryHeaderBtn,onClick:()=>D(null),children:"Cancel"})]})]}):e.jsxs("div",{className:r.aiProfileIdFooter,children:[e.jsx("div",{className:r.aiProfileIdFooterValue,children:n.primaryProfile.id}),T?.profileId===n.primaryProfile.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:T.message})]})]})]})})]}),w&&e.jsxs("div",{className:`${w.type==="success"?r.successMessage:r.errorMessage} ${r.marginTop12}`,children:[w.type==="success"?e.jsx(Le,{size:14}):e.jsx(C,{size:14}),w.message]})]})})}),e.jsx(ke,{isOpen:!!u,theme:"dark",title:"Edit AI Profile Photo",currentImageUrl:K(u),editorImageUrl:ze(u),fallbackInitial:(u?.name||"AI").charAt(0).toUpperCase(),accept:"image/png,image/jpeg,image/webp,image/gif",busy:X,hasPendingImage:!1,canRemove:!!u?.avatarUrl,error:Pe,notice:null,onClose:()=>{X||(Q(null),g(null))},onApplyImage:Ne,onRemoveImage:Ie})]})}export{qe as AgentsModule};
|
|
1
|
+
import{r as c,j as e}from"./vendor-react-CKJs5o3c.js";import{s as Ue,p as _,u as ie,v as De,w as se,x as le,y as we,t as r,E as Te,z as Me,B as ke,C as fe,F as b,G as oe}from"./index-CGDQY0nN.js";import{a0 as Re,x as $e,h as Ee,B as ne,m as C,b as Le,$ as Be,a5 as Ge}from"./vendor-icons-CLnehDTw.js";import"./vendor-markdown-BUxTU7dS.js";import"./vendor-dnd-DRzYolkg.js";import"./vendor-router-BbWMxlnO.js";function Fe(l){return typeof l.avatarUrl=="string"&&l.avatarUrl.trim().length>0}function K(l){return l?fe(l.avatarUrl,l.avatarRevision,l.avatarUpdatedAt):""}function ze(l){return l?fe(l.avatarSourceUrl,l.avatarRevision,l.avatarUpdatedAt):""}function He(l){return l.find(Fe)||l[0]}function ce(l){return`${l.seatScope||"unknown"}:${l.name.trim().toLowerCase()}`}function Oe(l){const m=Math.max(0,l-1);return`${m} duplicate${m===1?"":"s"}`}function de(l){return!l||typeof l!="object"?null:{used:Math.max(0,Number(l.used||0)),limit:l.limit===null||l.limit===void 0?null:Math.max(0,Number(l.limit||0)),remaining:l.remaining===null||l.remaining===void 0?null:Math.max(0,Number(l.remaining||0))}}function pe(l,m){const U=m?.variant==="inline"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconInline}`:m?.variant==="detail"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconDetail}`:r.aiProfileSeatScopeIcon;return l==="cloud_metered"?e.jsx("span",{className:`${U} ${r.aiProfileSeatScopeIconCloud}`,title:"Cloud MCP","aria-label":"Cloud MCP",children:e.jsx(Be,{size:20})}):l==="local_unmetered"?e.jsx("span",{className:`${U} ${r.aiProfileSeatScopeIconLocal}`,title:"Local MCP","aria-label":"Local MCP",children:e.jsx(Ge,{size:20})}):null}function qe({workspaceId:l,cloudAuthConfigured:m=!1,authSessionResolved:U=!1,isAuthenticated:ue=!1,cloudAiProfileSeatUsage:me=null,agentTrayOpen:E=!1,onCloseAgentTray:he,mcpSettingsNode:V=null}){const[S,L]=c.useState([]),[ve,ge]=c.useState(null),[B,J]=c.useState(!1),[h,D]=c.useState(null),[w,v]=c.useState(null),[T,x]=c.useState(null),[W,G]=c.useState(null),[ye,Y]=c.useState(!1),[P,j]=c.useState(null),[q,Q]=c.useState(null),[X,M]=c.useState(!1),[Pe,g]=c.useState(null),k=c.useCallback(async()=>{if(l){J(!0);try{const a=`/api/taskforce/workspace/assignee-options?workspaceId=${encodeURIComponent(l)}&kind=agent`,t=await fetch(a,{credentials:"include"}),i=t.ok?await t.json().catch(()=>({})):{},o=de(i?.aiProfileSeatUsage),d=Array.isArray(i?.assignees)?i.assignees.filter(s=>s.kind==="agent").map(s=>({id:String(s.value||""),name:String(s.label||s.value||"Unknown Agent"),username:String(s.username||s.value||""),icon:String(s.icon||"Bot"),color:String(s.color||"#6B7280"),avatarUrl:typeof s.avatarUrl=="string"?s.avatarUrl:null,avatarSourceUrl:typeof s.avatarSourceUrl=="string"?s.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(s.avatarRevision))?Math.max(0,Math.floor(Number(s.avatarRevision))):0,avatarUpdatedAt:typeof s.avatarUpdatedAt=="string"?s.avatarUpdatedAt:null,kind:String(s.kind||"agent"),description:typeof s.description=="string"?s.description:null,role:typeof s.role=="string"?s.role:null,provider:typeof s.provider=="string"?s.provider:null,model:typeof s.model=="string"?s.model:null,surfaceType:_(s.surfaceType),seatScope:Ue(s.seatScope),archivedAt:typeof s.archivedAt=="string"?s.archivedAt:null,createdAt:String(s.createdAt||""),updatedAt:String(s.updatedAt||s.createdAt||""),lastActiveAt:typeof s.lastActiveAt=="string"?s.lastActiveAt:null})):[];L(d),ge(o),x(s=>s&&!d.some(p=>p.id===s.profileId)?null:s),G(s=>s&&!d.some(p=>p.id===s.profileId)?null:s)}finally{J(!1)}}},[l]);c.useEffect(()=>{k()},[k]);const R=c.useMemo(()=>{const a=new Map;for(const t of S){const i=ie(t.surfaceType);a.has(i)||a.set(i,new Map);const o=a.get(i),d=ce(t);o.has(d)||o.set(d,[]),o.get(d).push(t)}return De.map(t=>({section:t,label:se(t),groups:Array.from(a.get(t)?.entries()||[]).map(([i,o])=>({groupId:`${t}:${i}`,section:t,sectionLabel:se(t),profiles:o,primaryProfile:He(o)}))})).filter(t=>t.groups.length>0)},[S]),y=c.useMemo(()=>R.flatMap(a=>a.groups),[R]),n=c.useMemo(()=>y.find(a=>a.groupId===P)||y[0]||null,[y,P]),u=c.useMemo(()=>S.find(a=>a.id===q)||null,[S,q]);c.useEffect(()=>{if(!y.length){P!==null&&j(null);return}(!P||!y.some(a=>a.groupId===P))&&j(y[0].groupId)},[y,P]);const Ae=async(a,t)=>{v(null),x(null);try{const i=await fetch("/api/taskforce/workspace/ai-profiles/merge",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({keepId:a,mergeId:t})}),o=await i.json().catch(()=>({}));if(!i.ok){v({type:"error",message:String(o?.error||"Failed to merge AI profiles.")});return}D(null),v({type:"success",message:"AI profiles merged."}),await k(),b({workspaceId:l,profileId:a,reason:"merge"})}catch{v({type:"error",message:"Failed to merge AI profiles."})}},Z=async a=>{if(window.confirm(`Remove ${a.name} from the active roster? This frees an AI profile seat and preserves task and comment history.`)){x(null),v(null);try{const i=await fetch("/api/taskforce/workspace/ai-profiles/archive",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:a.id,reason:"manual_archive"})}),o=await i.json().catch(()=>({}));if(!i.ok){x({profileId:a.id,message:String(o?.error||"Failed to remove AI profile from roster.")});return}(h?.keepId===a.id||h?.mergeId===a.id)&&D(null),v({type:"success",message:"AI profile removed from active roster."}),await k(),b({workspaceId:l,profileId:a.id,reason:"archive"})}catch{x({profileId:a.id,message:"Failed to remove AI profile from roster."})}}},Se=async(a,t)=>{const i=_(t),o=new Set(a.profiles.map(p=>p.surfaceType??"")),d=i??"";if(o.size===1&&o.has(d))return;Y(!0),G(null),v(null);const s=[];try{for(const f of a.profiles){const I=await fetch("/api/taskforce/workspace/ai-profiles/surface-type",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:f.id,surfaceType:i})}),H=await I.json().catch(()=>({}));if(!I.ok||!H?.profile)throw new Error(String(H?.error||"Failed to update AI profile category."));const O=H.profile;s.push({...f,surfaceType:_(O.surfaceType),updatedAt:typeof O.updatedAt=="string"?O.updatedAt:f.updatedAt})}const p=new Map(s.map(f=>[f.id,f]));L(f=>f.map(I=>p.get(I.id)||I));const A=p.get(a.primaryProfile.id)||{...a.primaryProfile,surfaceType:i},Ce=ie(A.surfaceType);j(`${Ce}:${ce(A)}`),v({type:"success",message:"AI profile category updated."});for(const f of s)b({workspaceId:l,profileId:f.id,reason:"update"})}catch(p){G({profileId:a.primaryProfile.id,message:String(p?.message||"Failed to update AI profile category.")})}finally{Y(!1)}},ee=a=>new Promise((t,i)=>{const o=new FileReader;o.onload=()=>t(String(o.result||"")),o.onerror=()=>i(new Error("Failed to read image file.")),o.readAsDataURL(a)}),ae=a=>{const t=String(a?.id||"").trim();t&&L(i=>i.map(o=>o.id===t?{...o,avatarUrl:typeof a.avatarUrl=="string"?a.avatarUrl:null,avatarSourceUrl:typeof a.avatarSourceUrl=="string"?a.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(a.avatarRevision))?Math.max(0,Math.floor(Number(a.avatarRevision))):o.avatarRevision,avatarUpdatedAt:typeof a.avatarUpdatedAt=="string"?a.avatarUpdatedAt:o.avatarUpdatedAt,updatedAt:typeof a.updatedAt=="string"?a.updatedAt:o.updatedAt}:o))},xe=async(a,t,i)=>{const o=await ee(t),d=i?await ee(i):null,s={profileId:a,displayImage:{dataUrl:o,mimeType:t.type||"application/octet-stream",originalName:t.name||"display-avatar"}};i&&d&&(s.sourceImage={dataUrl:d,mimeType:i.type||"application/octet-stream",originalName:i.name||"source-avatar"});const p=await fetch("/api/taskforce/workspace/ai-profiles/avatar/upload",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(s)}),A=await p.json().catch(()=>({}));if(!p.ok||!A?.profile)throw new Error(String(A?.error||"Failed to update AI profile avatar."));ae(A.profile),b({workspaceId:l,profileId:a,reason:"avatar"})},je=async a=>{const t=await fetch("/api/taskforce/workspace/ai-profiles/avatar",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:a,avatarUrl:null,avatarSourceUrl:null})}),i=await t.json().catch(()=>({}));if(!t.ok||!i?.profile)throw new Error(String(i?.error||"Failed to update AI profile avatar."));ae(i.profile),b({workspaceId:l,profileId:a,reason:"avatar"})},Ne=async(a,t)=>{if(!u)return!1;if(!a.type.startsWith("image/"))return g("AI profile photo must be an image file."),!1;M(!0),g(null);try{const i=await oe(a,{maxBytes:5242880});if(i.exceededLimit)throw new Error(a.type==="image/gif"?"Animated GIF AI profile photos must be 5 MB or smaller.":"AI profile photo must be 5 MB or smaller.");const o=i.file;let d=null;if(t){const s=await oe(t,{maxBytes:5242880});if(s.exceededLimit)throw new Error(t.type==="image/gif"?"Animated GIF AI profile photos must be 5 MB or smaller.":"AI profile source photo must be 5 MB or smaller.");d=s.file}return await xe(u.id,o,d),!0}catch(i){return g(String(i?.message||"Failed to update AI profile photo.")),!1}finally{M(!1)}},Ie=async()=>{if(u){M(!0),g(null);try{await je(u.id)}catch(a){g(String(a?.message||"Failed to remove AI profile photo."))}finally{M(!1)}}},N=a=>{const t=String(a||"").trim();if(!t)return"Unknown";const i=Date.parse(t);return Number.isFinite(i)?new Date(i).toLocaleString(void 0,{dateStyle:"medium",timeStyle:"short"}):t},re=c.useMemo(()=>{if(!n)return{attributes:[],dates:[]};const a=n.primaryProfile,t=i=>i||"Not set";return{attributes:[{label:"Category",value:a.surfaceType?le(a.surfaceType):""},{label:"Connection",value:a.seatScope?we(a.seatScope):""},{label:"Provider",value:a.provider||""},{label:"Model",value:a.model||""}].map(i=>({...i,value:t(i.value)})),dates:[{label:"Status",value:a.archivedAt?"Retired":"Active"},{label:"Recruited",value:N(a.createdAt)},{label:"Last updated",value:N(a.updatedAt)},{label:"Last active",value:N(a.lastActiveAt)}].map(i=>({...i,value:t(i.value)}))}},[n]),be=!!n&&n.profiles.length>1,te=m&&U&&!ue,$=m?de(me):ve,F=te?"Log into account for Cloud agents":$?`${$.used}/${$.limit===null?"Unlimited":$.limit}`:null,z=!!V;return e.jsxs("section",{className:`${r.agentsModuleRoot} ${z?r.agentsModuleWithTray:""} ${z&&E?r.agentsModuleTrayOpen:""}`.trim(),children:[z&&e.jsxs("aside",{className:`${r.agentTrayPanel} ${E?r.agentTrayPanelOpen:""}`.trim(),"aria-label":"Agent MCP settings tray","aria-hidden":!E,children:[e.jsxs("div",{className:r.agentTrayHeader,children:[e.jsxs("span",{className:r.agentTrayTitle,children:[e.jsx(Re,{size:14}),"MCP Settings"]}),e.jsx("button",{type:"button",className:"tf-control-icon",onClick:he,title:"Collapse agent tray","aria-label":"Collapse agent tray",children:e.jsx($e,{size:16})})]}),e.jsx("div",{className:`${r.agentTrayContent} tf-scrollbar tf-scrollbar--track-transparent`,children:e.jsx("div",{className:r.agentTrayContentInner,children:V})})]}),e.jsx("div",{className:r.agentsModuleContent,children:e.jsx("div",{className:`${r.settingGroup} ${r.agentsModuleGroup}`,children:e.jsxs("div",{children:[e.jsx("h4",{className:r.settingSubTitle,children:"Registered AI Profiles"}),e.jsx("p",{className:`${r.settingsHint} ${r.marginBottom12}`,children:"AI agents register profiles when connecting via MCP. Merge duplicates created when a token was lost, or remove inactive profiles from the active roster to free seats while preserving history."}),F&&e.jsx("div",{className:r.aiProfileSeatSummary,children:te?F:`Registered Cloud Agents: ${F}`}),B&&e.jsxs("div",{className:r.settingsHint,children:[e.jsx(Ee,{size:13,className:r.spinner})," Loading profiles…"]}),!B&&S.length===0&&e.jsx("div",{className:r.settingsHint,children:"No AI profiles registered yet."}),!B&&R.length>0&&e.jsxs("div",{className:r.aiProfilesExplorer,children:[e.jsx("div",{className:`${r.aiProfilesListPane} tf-scrollbar tf-scrollbar--track-transparent`,children:e.jsx("div",{className:r.aiProfilesList,children:R.map(a=>e.jsxs("div",{className:r.aiProfilesSection,children:[e.jsx("div",{className:r.aiProfilesSectionHeader,children:a.label}),a.groups.map(t=>{const i=n?.groupId===t.groupId,o=t.primaryProfile;return e.jsxs("div",{role:"button",tabIndex:0,className:`${r.aiProfileGroup} ${t.profiles.length>1?r.aiProfileGroupDuplicate:""} ${i?r.aiProfileGroupSelected:""}`,onClick:()=>j(t.groupId),onKeyDown:d=>{d.key!=="Enter"&&d.key!==" "||(d.preventDefault(),j(t.groupId))},"aria-pressed":i,children:[pe(o.seatScope),e.jsxs("div",{className:r.aiProfileGroupHeader,children:[e.jsx("span",{className:r.aiProfileGroupAvatar,style:{color:o.color},children:o.avatarUrl?e.jsx("img",{src:K(o),alt:""}):e.jsx(ne,{size:22})}),e.jsxs("span",{className:r.aiProfileGroupIdentity,children:[e.jsx("span",{className:r.aiProfileName,children:o.name}),e.jsxs("span",{className:r.aiProfileHandle,children:["@",o.username]}),e.jsx("span",{className:r.aiProfileRole,children:o.role||"Role not set"}),t.profiles.length>1&&e.jsxs("span",{className:r.aiProfileDuplicateBadge,children:[e.jsx(C,{size:11})," ",Oe(t.profiles.length)]})]})]})]},t.groupId)})]},a.section))})}),n&&e.jsx("div",{className:r.aiProfileDetailPane,children:e.jsxs("div",{className:r.aiProfileDetailCard,children:[pe(n.primaryProfile.seatScope,{variant:"detail"}),e.jsxs("div",{className:r.aiProfileDetailHero,children:[e.jsx(Te,{label:"Edit AI profile photo",imageUrl:K(n.primaryProfile),fallback:e.jsx(ne,{size:38}),accentColor:n.primaryProfile.color,size:176,width:153,height:207,radius:6,editBadgeSize:28,editIconSize:14,className:r.aiProfileDetailAvatar,onClick:()=>{g(null),Q(n.primaryProfile.id)}}),e.jsxs("div",{className:r.aiProfileDetailHeading,children:[e.jsx("div",{className:r.aiProfileDetailTitleRow,children:e.jsx("h5",{className:r.aiProfileDetailTitle,children:n.primaryProfile.name})}),e.jsxs("div",{className:r.aiProfileDetailMetaRow,children:[e.jsxs("span",{className:r.aiProfileDetailHandle,children:["@",n.primaryProfile.username]}),n.profiles.length>1&&e.jsxs("span",{className:r.aiProfileDetailLinkedCount,children:[n.profiles.length," linked"]})]}),e.jsxs("div",{className:r.aiProfileDetailRole,children:["Role: ",n.primaryProfile.role||"Not set"]}),n.primaryProfile.description&&e.jsx("div",{className:r.aiProfileDetailDescription,children:n.primaryProfile.description}),e.jsxs("div",{className:r.aiProfileSignatureColor,children:[e.jsx("span",{children:"Signature color"}),e.jsx("span",{className:r.aiProfileSignatureSwatch,style:{backgroundColor:n.primaryProfile.color},"aria-hidden":"true"}),e.jsx("span",{children:n.primaryProfile.color})]})]})]}),e.jsxs("div",{className:r.aiProfileDetailDataList,children:[e.jsx("div",{className:r.aiProfileDetailDataGroup,children:re.attributes.map(a=>e.jsxs("div",{className:r.aiProfileDetailDataRow,children:[e.jsx("span",{className:r.aiProfileDetailDataLabel,children:a.label}),a.label==="Category"?e.jsx("span",{className:r.aiProfileDetailDataValue,children:e.jsxs("select",{className:r.aiProfileStatusSelect,"aria-label":"Agent category",value:n.primaryProfile.surfaceType??"",disabled:ye,onChange:t=>{Se(n,t.target.value)},children:[e.jsx("option",{value:"",children:"Unclassified"}),Me.map(t=>e.jsx("option",{value:t,children:le(t)},t))]})}):e.jsx("span",{className:r.aiProfileDetailDataValue,children:a.value})]},a.label))}),e.jsx("div",{className:`${r.aiProfileDetailDataGroup} ${r.aiProfileDetailDateGroup}`,children:re.dates.map(a=>e.jsxs("div",{className:r.aiProfileDetailDataRow,children:[e.jsx("span",{className:r.aiProfileDetailDataLabel,children:a.label}),a.label==="Status"?e.jsx("span",{className:r.aiProfileDetailDataValue,children:e.jsxs("select",{className:r.aiProfileStatusSelect,"aria-label":"Agent roster status",value:n.primaryProfile.archivedAt?"retired":"active",onChange:t=>{t.target.value==="retire"&&Z(n.primaryProfile)},children:[e.jsx("option",{value:"active",children:"Active"}),n.primaryProfile.archivedAt?e.jsx("option",{value:"retired",children:"Retired"}):e.jsx("option",{value:"retire",children:"Retire from roster"})]})}):e.jsx("span",{className:r.aiProfileDetailDataValue,children:a.value})]},a.label))})]}),W?.profileId===n.primaryProfile.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:W.message})]}),be?e.jsxs("div",{className:r.aiProfileInstanceSection,children:[e.jsxs("div",{className:r.aiProfileInstanceSectionHeader,children:[e.jsx("span",{children:"Profile instances"}),e.jsx("span",{className:r.settingsHint,children:"Choose a keeper here if duplicates need to be merged."})]}),e.jsx("div",{className:r.aiProfileInstanceList,children:n.profiles.map(a=>e.jsxs("div",{className:r.aiProfileInstanceCard,children:[e.jsxs("div",{className:r.aiProfileInstanceTopRow,children:[e.jsxs("div",{children:[e.jsxs("div",{className:r.aiProfileInstanceName,children:["@",a.username]}),e.jsx("div",{className:r.aiProfileIdChip,children:a.id})]}),h?.keepId===a.id&&e.jsx("span",{className:r.aiProfileKeepBadge,children:"Keeping"})]}),e.jsxs("div",{className:r.aiProfileInstanceMeta,children:[e.jsxs("span",{children:["Created ",N(a.createdAt)]}),e.jsxs("span",{children:["Updated ",N(a.updatedAt)]})]}),h?.keepId!==a.id&&e.jsx("button",{className:r.secondaryHeaderBtn,title:"Keep this profile, merge others into it",onClick:()=>{const t=n.profiles.find(i=>i.id!==a.id)?.id;t&&D({keepId:a.id,mergeId:t})},children:"Keep this"}),T?.profileId===a.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:T.message})]}),e.jsx("button",{className:r.aiProfileDangerTextButton,title:"Remove this profile from the active roster",onClick:()=>{Z(a)},children:"Remove from roster"})]},a.id))}),h&&n.profiles.some(a=>a.id===h.keepId)&&e.jsxs("div",{className:r.aiProfileMergeActions,children:[e.jsx("button",{className:r.dangerBtn,onClick:()=>{Ae(h.keepId,h.mergeId)},children:"Merge duplicates"}),e.jsx("button",{className:r.secondaryHeaderBtn,onClick:()=>D(null),children:"Cancel"})]})]}):e.jsxs("div",{className:r.aiProfileIdFooter,children:[e.jsx("div",{className:r.aiProfileIdFooterValue,children:n.primaryProfile.id}),T?.profileId===n.primaryProfile.id&&e.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[e.jsx(C,{size:12}),e.jsx("span",{children:T.message})]})]})]})})]}),w&&e.jsxs("div",{className:`${w.type==="success"?r.successMessage:r.errorMessage} ${r.marginTop12}`,children:[w.type==="success"?e.jsx(Le,{size:14}):e.jsx(C,{size:14}),w.message]})]})})}),e.jsx(ke,{isOpen:!!u,theme:"dark",title:"Edit AI Profile Photo",currentImageUrl:K(u),editorImageUrl:ze(u),fallbackInitial:(u?.name||"AI").charAt(0).toUpperCase(),accept:"image/png,image/jpeg,image/webp,image/gif",busy:X,hasPendingImage:!1,canRemove:!!u?.avatarUrl,error:Pe,notice:null,onClose:()=>{X||(Q(null),g(null))},onApplyImage:Ne,onRemoveImage:Ie})]})}export{qe as AgentsModule};
|