@taskforcehq/taskforce 0.3.330 → 0.3.333
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 +7 -2
- package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.d.ts +2 -2
- package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.js +2 -2
- package/dist/components/features/planning/PlanningModule.js +15 -3
- package/dist/components/features/planning/planningMetadata.d.ts +10 -0
- package/dist/components/features/planning/planningMetadata.js +8 -0
- package/dist/components/views/StandaloneLayout.js +35 -1
- package/dist/components/views/panels/PlanningDrawer.d.ts +2 -1
- package/dist/components/views/panels/PlanningDrawer.js +12 -8
- package/dist/config/envSchema.js +7 -0
- package/dist/core/McpTokenService.d.ts +2 -0
- package/dist/core/McpTokenService.js +13 -5
- package/dist/core/Taskforce.d.ts +11 -3
- package/dist/core/Taskforce.js +33 -3
- package/dist/core/types.d.ts +1 -0
- package/dist/documentReviews/DocumentReviewCommentStore.d.ts +2 -0
- package/dist/documentReviews/DocumentReviewCommentStore.js +28 -9
- package/dist/mcp/httpProtocolRouting.d.ts +1 -0
- package/dist/mcp/httpProtocolRouting.js +8 -5
- package/dist/mcp/runtime.js +24 -5
- package/dist/mcp/taskPlanningRegistrar.js +2 -2
- package/dist/mcp/toolProfiles.js +1 -0
- package/dist/mcp/toolRegistry.js +8 -8
- package/dist/server/buildInfo.d.ts +1 -0
- package/dist/server/buildInfo.js +5 -1
- package/dist/server/cloudMcpCapacityTelemetry.d.ts +68 -0
- package/dist/server/cloudMcpCapacityTelemetry.js +221 -0
- package/dist/server/localWorkspaceSyncServerRuntime.js +13 -5
- package/dist/server/routes/admin.js +14 -2
- package/dist/server/routes/localService.js +1 -0
- package/dist/server/routes/shared.d.ts +3 -0
- package/dist/server/routes/shared.js +280 -240
- package/dist/server/routes/syncPushRoutes.d.ts +3 -0
- package/dist/server/routes/syncPushRoutes.js +8 -2
- package/dist/service/localServiceCli.d.ts +2 -2
- package/dist/service/localServiceCli.js +4 -3
- package/dist/services/executorPhaseAIssuerCore.d.ts +13 -0
- package/dist/services/executorPhaseAIssuerCore.js +107 -0
- package/dist/services/taskforceAgentMcpBridge.d.ts +1 -1
- package/dist/services/taskforceAgentMcpBridge.js +35 -5
- package/dist/shared/executorPhaseASchemaValidation.js +1 -1
- package/dist/storage/postgresAdapter.js +3 -1
- package/dist/storage/postgresRequestDiagnostics.d.ts +9 -1
- package/dist/storage/postgresRequestDiagnostics.js +15 -2
- package/dist/storage/postgresWorker.js +13 -3
- package/dist/storage/postgresWorkerProtocol.d.ts +6 -0
- package/dist/storage/workflowStore.d.ts +2 -0
- package/dist/storage/workflowStore.js +39 -2
- package/dist/sync/cloudSyncApi.js +4 -2
- package/dist/sync/collaborationSyncState.js +5 -0
- package/dist/sync/coordinator/localWorkspaceSyncRunner.d.ts +3 -1
- package/dist/sync/coordinator/localWorkspaceSyncRunner.js +18 -0
- package/dist/sync/coordinator/localWorkspaceSyncRunnerState.d.ts +1 -0
- package/dist/sync/coordinator/localWorkspaceSyncRunnerState.js +7 -6
- package/dist/sync/engine/workspaceSyncEngineBrowserGatewayRemoteServices.js +2 -2
- package/dist/sync/engine/workspaceSyncEngineLocalApply.d.ts +1 -0
- package/dist/sync/engine/workspaceSyncEnginePushCompletion.d.ts +6 -0
- package/dist/sync/engine/workspaceSyncEnginePushCompletion.js +34 -1
- package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.js +2 -2
- package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +4 -1
- package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.d.ts +1 -0
- package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.js +55 -10
- package/dist/sync/syncApplyHandlers.d.ts +1 -0
- package/dist/sync/syncApplyHandlers.js +72 -15
- package/dist/sync/syncService.d.ts +1 -0
- package/dist/sync/syncService.js +5 -2
- package/dist/sync/v3/durableWorkflowRuntimeMutationRouter.d.ts +7 -7
- package/dist/sync/v3/durableWorkflowRuntimeMutationRouter.js +50 -13
- package/dist/sync/v3/localOutboxRecoveryPreparationService.js +8 -2
- package/dist/sync/v3/localTaskCreateOutboxService.d.ts +3 -1
- package/dist/sync/v3/localTaskCreateOutboxService.js +6 -1
- package/dist/sync/v3/localTaskDeleteOutboxService.js +1 -0
- package/dist/sync/v3/localTaskLifecycleOutboxService.d.ts +2 -1
- package/dist/sync/v3/localTaskLifecycleOutboxService.js +5 -1
- package/dist/sync/v3/localTaskMetadataOutboxService.d.ts +2 -1
- package/dist/sync/v3/localTaskMetadataOutboxService.js +6 -2
- package/dist/sync/v3/localTaskStatusOutboxService.d.ts +2 -1
- package/dist/sync/v3/localTaskStatusOutboxService.js +20 -5
- package/dist/sync/v3/syncDurabilityStore.d.ts +2 -1
- package/dist/sync/v3/syncDurabilityStore.js +34 -6
- package/dist/sync/v3/workflowRuntimeMutationService.d.ts +5 -0
- package/dist/sync/v3/workflowRuntimeMutationService.js +124 -71
- package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +25 -3
- package/dist/sync/v3/workspaceSyncV3Feed.d.ts +1 -0
- package/dist/sync/v3/workspaceSyncV3Feed.js +6 -1
- package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.d.ts +2 -0
- package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.js +49 -18
- package/dist/sync/v3/workspaceSyncV3WorkflowRuntimeApply.d.ts +1 -0
- package/dist/sync/v3/workspaceSyncV3WorkflowRuntimeApply.js +50 -5
- package/dist/ui/assets/{AiIdentityRosterCard-OcSz1PkK.js → AiIdentityRosterCard-Cfvb9Nuv.js} +1 -1
- package/dist/ui/assets/{AiProfilesModule-ByhYW5Xi.js → AiProfilesModule-0oR70eFG.js} +1 -1
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-BTM6nIg_.js → AnnotatedAttachmentWorkspace-C33iH_r_.js} +1 -1
- package/dist/ui/assets/{AssetTraySortControl-DabEG8L4.js → AssetTraySortControl-BAqxUtke.js} +1 -1
- package/dist/ui/assets/{ContextAttachmentManager-Coa8yVGM.js → ContextAttachmentManager-BudIbOR9.js} +2 -2
- package/dist/ui/assets/{DocumentWorkspace-BB0qhdix.js → DocumentWorkspace-A8dFQyId.js} +1 -1
- package/dist/ui/assets/{EntityActivityTimeline-CZ8x4UJE.js → EntityActivityTimeline-g7vtdsx1.js} +1 -1
- package/dist/ui/assets/PlanningModule-D-DeQs4F.js +1 -0
- package/dist/ui/assets/{PlansPage-D86pZSSl.js → PlansPage-BWmfGi0Z.js} +1 -1
- package/dist/ui/assets/{TaskContextUpload-BVIkU9yT.js → TaskContextUpload-BA_lQmS9.js} +1 -1
- package/dist/ui/assets/{TaskSettings-Cv1mNhv_.js → TaskSettings-DAlH4YIP.js} +1 -1
- package/dist/ui/assets/{TaskforceAgentsModule-gmH-PD2q.js → TaskforceAgentsModule-DmeCf6pJ.js} +2 -2
- package/dist/ui/assets/{WorkflowManagerModule-DbfnHJ-f.js → WorkflowManagerModule-p08eDOaa.js} +1 -1
- package/dist/ui/assets/index-GHJgcW3j.js +7 -0
- package/dist/ui/index.html +1 -1
- package/package.json +3 -1
- package/dist/ui/assets/PlanningModule-pRPjR7v5.js +0 -1
- package/dist/ui/assets/index-CTsDBaef.js +0 -7
|
@@ -225,6 +225,34 @@ function projectNestedDependentRows(rows, attachmentProjection) {
|
|
|
225
225
|
return [projected];
|
|
226
226
|
});
|
|
227
227
|
}
|
|
228
|
+
function assertOperationEventWorkspace(events, workspaceId) {
|
|
229
|
+
if (events.some((event) => event.workspaceId !== workspaceId)) {
|
|
230
|
+
throw new Error('Nested task operation activity belongs to another workspace.');
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function assertTaskOperationActivities(events, taskId) {
|
|
234
|
+
let membershipStarted = false;
|
|
235
|
+
let taskActivityCount = 0;
|
|
236
|
+
for (const event of events) {
|
|
237
|
+
if (event.entityType === 'task') {
|
|
238
|
+
if (membershipStarted) {
|
|
239
|
+
throw new Error('Task operation activity cannot follow workstream membership activity.');
|
|
240
|
+
}
|
|
241
|
+
if (event.entityId !== taskId || event.taskId !== taskId) {
|
|
242
|
+
throw new Error('Nested task operation event identity does not match its parent root.');
|
|
243
|
+
}
|
|
244
|
+
taskActivityCount += 1;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
membershipStarted = true;
|
|
248
|
+
if (event.entityType !== 'workstream' || event.details?.taskId !== taskId) {
|
|
249
|
+
throw new Error('Task operation secondary activity does not describe workstream membership.');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (taskActivityCount < 1) {
|
|
253
|
+
throw new Error('Task operation must contain primary task activity before workstream membership.');
|
|
254
|
+
}
|
|
255
|
+
}
|
|
228
256
|
function isSupportedNestedOnlyOperation(rows) {
|
|
229
257
|
const first = rows[0];
|
|
230
258
|
if (!first)
|
|
@@ -247,7 +275,7 @@ function isSupportedNestedOnlyOperation(rows) {
|
|
|
247
275
|
* root. Apply still requires referenced parents to exist on the receiver; feed
|
|
248
276
|
* only needs to project the nested rows.
|
|
249
277
|
*/
|
|
250
|
-
function projectNestedOnlyOperation(rows, attachmentProjection) {
|
|
278
|
+
function projectNestedOnlyOperation(rows, attachmentProjection, workspaceId) {
|
|
251
279
|
if (!isSupportedNestedOnlyOperation(rows)) {
|
|
252
280
|
throw new Error('Nested-only task operation is not an allowed repair or reconciliation shape.');
|
|
253
281
|
}
|
|
@@ -259,6 +287,10 @@ function projectNestedOnlyOperation(rows, attachmentProjection) {
|
|
|
259
287
|
throw new Error('Nested task operation dependent identity does not match its task root.');
|
|
260
288
|
}
|
|
261
289
|
}
|
|
290
|
+
const rawEvents = rows
|
|
291
|
+
.filter((row) => row.domain === 'entity-event')
|
|
292
|
+
.map((row) => canonicalEntityEventPayload(parsePayload(row)));
|
|
293
|
+
assertOperationEventWorkspace(rawEvents, workspaceId);
|
|
262
294
|
const projected = projectNestedDependentRows(rows, attachmentProjection);
|
|
263
295
|
const events = projected
|
|
264
296
|
.filter((entry) => entry.domain === 'entity-event')
|
|
@@ -268,13 +300,7 @@ function projectNestedOnlyOperation(rows, attachmentProjection) {
|
|
|
268
300
|
}
|
|
269
301
|
if (taskScoped.length > 0 && events.length > 0) {
|
|
270
302
|
const taskId = taskScoped[0].entityId;
|
|
271
|
-
|
|
272
|
-
if (primaryEvent && (primaryEvent.entityType !== 'task' || primaryEvent.entityId !== taskId)) {
|
|
273
|
-
throw new Error('Nested task operation event identity does not match its parent root.');
|
|
274
|
-
}
|
|
275
|
-
if (secondaryEvents.some((event) => (event.entityType !== 'workstream' || event.details?.taskId !== taskId))) {
|
|
276
|
-
throw new Error('Task operation secondary activity does not describe workstream membership.');
|
|
277
|
-
}
|
|
303
|
+
assertTaskOperationActivities(events, taskId);
|
|
278
304
|
}
|
|
279
305
|
if (events.length > 0 && projected.at(-1)?.domain !== 'entity-event') {
|
|
280
306
|
throw new Error('Nested task operation must end with entity activity.');
|
|
@@ -293,7 +319,7 @@ function projectNestedTaskOperation(input, attachmentProjection) {
|
|
|
293
319
|
const [first, ...dependents] = input.rows;
|
|
294
320
|
if (first && projectionByDomain.has(first.domain)
|
|
295
321
|
&& input.rows.every((row) => projectionByDomain.has(row.domain))) {
|
|
296
|
-
return projectNestedOnlyOperation(input.rows, attachmentProjection);
|
|
322
|
+
return projectNestedOnlyOperation(input.rows, attachmentProjection, input.workspaceId);
|
|
297
323
|
}
|
|
298
324
|
if (!first || !['task', 'initiative', 'workstream'].includes(first.domain)) {
|
|
299
325
|
throw new Error('Nested task operation must begin with its v3-owned parent root.');
|
|
@@ -302,10 +328,13 @@ function projectNestedTaskOperation(input, attachmentProjection) {
|
|
|
302
328
|
throw new Error('Nested task operation contains an unsupported dependent row.');
|
|
303
329
|
}
|
|
304
330
|
assertNestedDependentOrder(dependents);
|
|
331
|
+
const rawEventRows = dependents.filter((row) => row.domain === 'entity-event');
|
|
332
|
+
const rawEvents = rawEventRows.map((row) => canonicalEntityEventPayload(parsePayload(row)));
|
|
333
|
+
assertOperationEventWorkspace(rawEvents, input.workspaceId);
|
|
305
334
|
const projectedDependents = projectNestedDependentRows(dependents, attachmentProjection);
|
|
306
335
|
const eventRows = projectedDependents.filter((entry) => entry.domain === 'entity-event');
|
|
307
|
-
const
|
|
308
|
-
|
|
336
|
+
const sentinelOnly = eventRows.length === 0 && rawEvents.length > 0
|
|
337
|
+
&& rawEvents.every((event) => event.action === 'sync-v3-repair-baseline');
|
|
309
338
|
const nestedBodiesOnly = eventRows.length === 0
|
|
310
339
|
&& projectedDependents.length > 0
|
|
311
340
|
&& projectedDependents.every((entry) => entry.domain !== 'entity-event');
|
|
@@ -324,17 +353,19 @@ function projectNestedTaskOperation(input, attachmentProjection) {
|
|
|
324
353
|
}
|
|
325
354
|
const events = eventRows.map((entry) => validateWorkspaceSyncV3EntityEventPayload(entry.payload));
|
|
326
355
|
const [primaryEvent, ...secondaryEvents] = events;
|
|
327
|
-
if (primaryEvent && (primaryEvent.entityType !== first.domain || primaryEvent.entityId !== first.entityId)) {
|
|
328
|
-
throw new Error('Nested task operation event identity does not match its parent root.');
|
|
329
|
-
}
|
|
330
356
|
if (new Set(events.map((event) => event.id)).size !== events.length) {
|
|
331
357
|
throw new Error('Nested task operation contains duplicate entity-event identities.');
|
|
332
358
|
}
|
|
333
|
-
if (first.domain
|
|
334
|
-
|
|
359
|
+
if (first.domain === 'task' && events.length > 0) {
|
|
360
|
+
assertTaskOperationActivities(events, first.entityId);
|
|
335
361
|
}
|
|
336
|
-
if (first.domain
|
|
337
|
-
|
|
362
|
+
else if (first.domain !== 'task') {
|
|
363
|
+
if (primaryEvent && (primaryEvent.entityType !== first.domain || primaryEvent.entityId !== first.entityId)) {
|
|
364
|
+
throw new Error('Nested task operation event identity does not match its parent root.');
|
|
365
|
+
}
|
|
366
|
+
if (secondaryEvents.length > 0) {
|
|
367
|
+
throw new Error('Planning-root operations cannot contain secondary entity activity.');
|
|
368
|
+
}
|
|
338
369
|
}
|
|
339
370
|
let parent;
|
|
340
371
|
if (first.domain === 'task') {
|
|
@@ -14,4 +14,5 @@ export declare function prepareWorkspaceSyncV3WorkflowRuntimeApply(input: {
|
|
|
14
14
|
store: WorkflowStore;
|
|
15
15
|
workspaceId: string;
|
|
16
16
|
entries: WorkspaceSyncV3FeedEntry[];
|
|
17
|
+
replaceAuthoritativeHistoryExecutionIds?: ReadonlySet<string>;
|
|
17
18
|
}): WorkspaceSyncV3WorkflowRuntimeApplyPlan;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { canonicalWorkspaceSyncJson } from './canonicalWorkspaceSyncJson.js';
|
|
2
|
-
import { validateWorkflowExecutionEventSyncPayload, validateWorkflowExecutionRootSyncPayload, validateWorkflowPublishedDefinitionSyncPayload, validateWorkflowRuntimeEventHistory, } from './workflowRuntimeSync.js';
|
|
2
|
+
import { serializeWorkflowPublishedDefinitionSyncPayload, validateWorkflowExecutionEventSyncPayload, validateWorkflowExecutionRootSyncPayload, validateWorkflowPublishedDefinitionSyncPayload, validateWorkflowRuntimeEventHistory, } from './workflowRuntimeSync.js';
|
|
3
3
|
import { validateWorkspaceSyncV3WorkflowRuntimeFeedEntry } from './workspaceSyncV3WorkflowRuntimeProjection.js';
|
|
4
4
|
const WORKFLOW_PROJECTIONS = new Set([
|
|
5
5
|
'workflow-published-definition/full-v2',
|
|
@@ -9,6 +9,19 @@ const WORKFLOW_PROJECTIONS = new Set([
|
|
|
9
9
|
function compareEvents(left, right) {
|
|
10
10
|
return canonicalWorkspaceSyncJson(left) === canonicalWorkspaceSyncJson(right);
|
|
11
11
|
}
|
|
12
|
+
function executionIdentity(root) {
|
|
13
|
+
const execution = root.execution;
|
|
14
|
+
return canonicalWorkspaceSyncJson({
|
|
15
|
+
id: execution.id,
|
|
16
|
+
tenantId: execution.tenantId,
|
|
17
|
+
workspaceId: execution.workspaceId,
|
|
18
|
+
taskId: execution.taskId,
|
|
19
|
+
workflowTemplateId: execution.workflowTemplateId,
|
|
20
|
+
workflowVersionId: execution.workflowVersionId,
|
|
21
|
+
createdByActorId: execution.createdByActorId || null,
|
|
22
|
+
createdAt: execution.createdAt,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
12
25
|
function mergeEventHistory(input) {
|
|
13
26
|
const local = [...input.local].sort((left, right) => left.sequenceNumber - right.sequenceNumber);
|
|
14
27
|
const incoming = [...input.incoming].sort((left, right) => left.sequenceNumber - right.sequenceNumber);
|
|
@@ -47,6 +60,19 @@ function derivedTemplate(definition, existing) {
|
|
|
47
60
|
deletedAt: null,
|
|
48
61
|
};
|
|
49
62
|
}
|
|
63
|
+
function installedPublishedDefinition(store, workspaceId, versionId) {
|
|
64
|
+
const version = store.getVersion(workspaceId, versionId);
|
|
65
|
+
if (!version || version.status !== 'published')
|
|
66
|
+
return null;
|
|
67
|
+
const template = store.getTemplate(workspaceId, version.workflowTemplateId);
|
|
68
|
+
if (!template)
|
|
69
|
+
return null;
|
|
70
|
+
return serializeWorkflowPublishedDefinitionSyncPayload({
|
|
71
|
+
template,
|
|
72
|
+
version,
|
|
73
|
+
steps: store.listSteps(workspaceId, version.id),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
50
76
|
/**
|
|
51
77
|
* Builds complete workflow aggregates before any revision is accepted. A root
|
|
52
78
|
* entry is the atomic install trigger; definition and event entries only carry
|
|
@@ -56,6 +82,7 @@ export function prepareWorkspaceSyncV3WorkflowRuntimeApply(input) {
|
|
|
56
82
|
const workflowEntries = new Set(input.entries.filter((entry) => WORKFLOW_PROJECTIONS.has(entry.projection)));
|
|
57
83
|
const definitions = new Map();
|
|
58
84
|
const roots = new Map();
|
|
85
|
+
const rootEntries = new Map();
|
|
59
86
|
const eventsByExecution = new Map();
|
|
60
87
|
for (const entry of workflowEntries) {
|
|
61
88
|
validateWorkspaceSyncV3WorkflowRuntimeFeedEntry(entry);
|
|
@@ -68,10 +95,23 @@ export function prepareWorkspaceSyncV3WorkflowRuntimeApply(input) {
|
|
|
68
95
|
}
|
|
69
96
|
else if (entry.projection === 'workflow-execution/root-v1') {
|
|
70
97
|
const root = validateWorkflowExecutionRootSyncPayload(entry.payload);
|
|
71
|
-
|
|
72
|
-
|
|
98
|
+
const existing = roots.get(root.execution.id);
|
|
99
|
+
const existingEntry = rootEntries.get(root.execution.id);
|
|
100
|
+
if (existing && existingEntry) {
|
|
101
|
+
if (executionIdentity(existing) !== executionIdentity(root)) {
|
|
102
|
+
throw new Error(`Workflow execution root identity changes in one apply batch: ${root.execution.id}.`);
|
|
103
|
+
}
|
|
104
|
+
const revision = BigInt(entry.entityRevision);
|
|
105
|
+
const existingRevision = BigInt(existingEntry.entityRevision);
|
|
106
|
+
if (revision === existingRevision
|
|
107
|
+
&& canonicalWorkspaceSyncJson(existing) !== canonicalWorkspaceSyncJson(root)) {
|
|
108
|
+
throw new Error(`Workflow execution root revision conflicts in one apply batch: ${root.execution.id}.`);
|
|
109
|
+
}
|
|
110
|
+
if (revision <= existingRevision)
|
|
111
|
+
continue;
|
|
73
112
|
}
|
|
74
113
|
roots.set(root.execution.id, root);
|
|
114
|
+
rootEntries.set(root.execution.id, entry);
|
|
75
115
|
}
|
|
76
116
|
else {
|
|
77
117
|
const event = validateWorkflowExecutionEventSyncPayload(entry.payload).event;
|
|
@@ -87,6 +127,7 @@ export function prepareWorkspaceSyncV3WorkflowRuntimeApply(input) {
|
|
|
87
127
|
}
|
|
88
128
|
const assignments = new Map();
|
|
89
129
|
for (const [executionId, root] of roots) {
|
|
130
|
+
const replaceAuthoritativeHistory = Boolean(input.replaceAuthoritativeHistoryExecutionIds?.has(executionId));
|
|
90
131
|
const local = input.store.getAssignmentForExecutionSync(input.workspaceId, executionId);
|
|
91
132
|
const definition = definitions.get(root.execution.workflowVersionId)
|
|
92
133
|
|| (local ? {
|
|
@@ -97,12 +138,13 @@ export function prepareWorkspaceSyncV3WorkflowRuntimeApply(input) {
|
|
|
97
138
|
},
|
|
98
139
|
version: local.version,
|
|
99
140
|
steps: local.steps,
|
|
100
|
-
} : null)
|
|
141
|
+
} : null)
|
|
142
|
+
|| installedPublishedDefinition(input.store, input.workspaceId, root.execution.workflowVersionId);
|
|
101
143
|
if (!definition) {
|
|
102
144
|
throw new Error(`Workflow execution ${executionId} is missing its published definition.`);
|
|
103
145
|
}
|
|
104
146
|
const merged = mergeEventHistory({
|
|
105
|
-
local: local?.events || [],
|
|
147
|
+
local: replaceAuthoritativeHistory ? [] : local?.events || [],
|
|
106
148
|
incoming: eventsByExecution.get(executionId) || [],
|
|
107
149
|
});
|
|
108
150
|
const execution = merged.localExtension && local
|
|
@@ -131,12 +173,15 @@ export function prepareWorkspaceSyncV3WorkflowRuntimeApply(input) {
|
|
|
131
173
|
return;
|
|
132
174
|
if (entry.projection !== 'workflow-execution/root-v1')
|
|
133
175
|
return;
|
|
176
|
+
if (rootEntries.get(entry.entityId) !== entry)
|
|
177
|
+
return;
|
|
134
178
|
const assignment = assignments.get(entry.entityId);
|
|
135
179
|
if (!assignment)
|
|
136
180
|
throw new Error(`Workflow execution apply plan is missing ${entry.entityId}.`);
|
|
137
181
|
input.store.installAssignmentForSync(input.workspaceId, assignment, {
|
|
138
182
|
authority: 'cloud-execution',
|
|
139
183
|
expectedTaskId: assignment.execution.taskId,
|
|
184
|
+
replaceAuthoritativeHistory: input.replaceAuthoritativeHistoryExecutionIds?.has(entry.entityId),
|
|
140
185
|
});
|
|
141
186
|
},
|
|
142
187
|
};
|
package/dist/ui/assets/{AiIdentityRosterCard-OcSz1PkK.js → AiIdentityRosterCard-Cfvb9Nuv.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as i}from"./vendor-react-CKJs5o3c.js";import{t as r}from"./index-
|
|
1
|
+
import{j as i}from"./vendor-react-CKJs5o3c.js";import{t as r}from"./index-GHJgcW3j.js";const t=[{name:"Blue",value:"#4269d0"},{name:"Orange",value:"#efb118"},{name:"Red",value:"#ff725c"},{name:"Cyan",value:"#6cc5b0"},{name:"Green",value:"#3ca951"},{name:"Pink",value:"#ff8ab7"},{name:"Purple",value:"#a463f2"},{name:"Light blue",value:"#97bbf5"},{name:"Brown",value:"#9c6b4e"},{name:"Gray",value:"#9498a0"}];function N(e){const s=String(e||"ai").trim().toLowerCase();let a=2166136261;for(let l=0;l<s.length;l+=1)a^=s.charCodeAt(l),a=Math.imul(a,16777619);return t[(a>>>0)%t.length].value}function b(e){const[s,a=""]=e.split("#"),n=s.includes("?")?"&":"?";return`${s}${n}taskforceCloud=1${a?`#${a}`:""}`}function $(e){return/^https?:\/\//i.test(e)?e:b(e)}function j({name:e,username:s,subtitle:a,avatar:n,selected:l,disabled:o=!1,onSelect:u,ariaLabel:c,avatarVariant:f="portrait",avatarClassName:m="",avatarStyle:d,className:p="",topUtility:h,bottomUtility:v,footer:P}){return i.jsxs("button",{type:"button",className:`${r.aiProfileGroup} ${l?r.aiProfileGroupSelected:""} ${p}`.trim(),onClick:u,disabled:o,"aria-label":c,"aria-pressed":l,children:[h,v,i.jsxs("span",{className:r.aiProfileGroupHeader,children:[i.jsx("span",{className:`${r.aiProfileGroupAvatar} ${f==="logo"?r.aiProfileGroupAvatarLogo:""} ${m}`.trim(),style:d,"aria-hidden":"true",children:n}),i.jsxs("span",{className:r.aiProfileGroupIdentity,children:[i.jsx("span",{className:r.aiProfileName,children:e}),s?i.jsxs("span",{className:r.aiProfileHandle,children:["@",s]}):null,i.jsx("span",{className:r.aiProfileRole,children:a}),P]})]})]})}export{j as A,N as a,t as b,$ as r,b as w};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as f,j as a}from"./vendor-react-CKJs5o3c.js";import{ad as Qe,ae as re,U as Ue,af as ea,ag as W,t as r,W as aa,ah as ra,ai as Re,X as ta,aj as ia,ak as te,al as Me,am as la,Z as oa,Y as Ee,a0 as b,a1 as ke}from"./index-CTsDBaef.js";import{A as sa,b as na,r as Ce}from"./AiIdentityRosterCard-OcSz1PkK.js";import{ay as ca,ak as da,U as fa,w as ua,Z as V,J as Le,ax as pa,aC as ma}from"./vendor-icons-Bsq-mcEn.js";import"./vendor-markdown-_lhNCyq-.js";import"./vendor-dnd-CJ-AjP-Y.js";import"./vendor-router-AqJMU8Lz.js";const va=["chat_app","ide","cli","coding_tool","taskforce_agent","agent","unclassified"];function L(l){return typeof l.avatarUrl=="string"&&l.avatarUrl.trim().length>0}function ie(l){return l?Ee(l.avatarUrl,l.avatarRevision,l.avatarUpdatedAt):""}function ga(l){return l?Ee(l.avatarSourceUrl,l.avatarRevision,l.avatarUpdatedAt):""}function ha(l){return l.find(L)||l[0]}function ya({profile:l,className:g=""}){const A=ie(l),$=W(l),{activeImageUrl:T,handleImageError:Y}=oa(A,$);return T?a.jsx("img",{src:T,alt:"",className:g,onError:Y}):a.jsx(Le,{size:22})}function K(l){const g=te(l);return g?`taskforce-agent:${g}`:`${l.seatScope||"unknown"}:${l.name.trim().toLowerCase()}`}function le(l){const g=l.providerMetadata?.taskforce;return!!(g&&typeof g=="object"&&!Array.isArray(g)&&String(g.taskforceAgentId||"").trim())}function Pa(l){return le(l)?"taskforce_agent":Me(l.surfaceType)}function Te(l){return l==="taskforce_agent"?"Taskforce Agents":la(l)}function Aa(l){return le(l)?"Taskforce Agent":l.surfaceType?Re(l.surfaceType):""}function Sa(l){const g=Math.max(0,l-1);return`${g} duplicate${g===1?"":"s"}`}function we(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 De(l,g){const A=g?.variant==="inline"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconInline}`:g?.variant==="detail"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconDetail}`:r.aiProfileSeatScopeIcon;return l==="cloud_metered"?a.jsx("span",{className:`${A} ${r.aiProfileSeatScopeIconCloud}`,title:"Cloud MCP","aria-label":"Cloud MCP",children:a.jsx(pa,{size:20})}):l==="local_unmetered"?a.jsx("span",{className:`${A} ${r.aiProfileSeatScopeIconLocal}`,title:"Local MCP","aria-label":"Local MCP",children:a.jsx(ma,{size:20})}):null}function Ca({workspaceId:l,runtimeMode:g="local",cloudAuthConfigured:A=!1,authSessionResolved:$=!1,isAuthenticated:T=!1,cloudAiProfileSeatUsage:Y=null,agentTrayOpen:Z=!1,onCloseAgentTray:$e,mcpSettingsNode:oe=null,resolveCloudAuthUrl:B,theme:Be="dark",onNotice:w}){const[N,O]=f.useState([]),[Oe,Fe]=f.useState(null),[Ge,se]=f.useState(!1),[S,F]=f.useState(null),[G,D]=f.useState(null),[ne,X]=f.useState(null),[_e,ce]=f.useState(!1),[U,_]=f.useState(null),[de,fe]=f.useState(null),[ue,z]=f.useState(!1),[ze,P]=f.useState(null),[He,x]=f.useState(null),[R,pe]=f.useState(null),[me,k]=f.useState({}),[ve,ge]=f.useState(!1),[q,he]=f.useState(!1),[ye,Pe]=f.useState(null),C=f.useCallback(async()=>{if(l){se(!0);try{const e=`/api/taskforce/workspace/assignee-options?workspaceId=${encodeURIComponent(l)}&kind=agent`,t=await fetch(e,{credentials:"include"}),o=t.ok?await t.json().catch(()=>({})):{},s=we(o?.aiProfileSeatUsage),d=Array.isArray(o?.assignees)?o.assignees.filter(i=>i.kind==="agent").map(i=>({id:String(i.value||""),name:String(i.label||i.value||"Unknown Agent"),username:String(i.username||i.value||""),icon:String(i.icon||"Bot"),color:String(i.color||"#6B7280"),colorSource:typeof i.colorSource=="string"?i.colorSource:null,colorUpdatedAt:typeof i.colorUpdatedAt=="string"?i.colorUpdatedAt:null,avatarUrl:typeof i.avatarUrl=="string"?i.avatarUrl:null,avatarSourceUrl:typeof i.avatarSourceUrl=="string"?i.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(i.avatarRevision))?Math.max(0,Math.floor(Number(i.avatarRevision))):0,avatarUpdatedAt:typeof i.avatarUpdatedAt=="string"?i.avatarUpdatedAt:null,kind:String(i.kind||"agent"),description:typeof i.description=="string"?i.description:null,role:typeof i.role=="string"?i.role:null,provider:typeof i.provider=="string"?i.provider:null,model:typeof i.model=="string"?i.model:null,surfaceType:re(i.surfaceType),seatScope:Qe(i.seatScope),providerMetadata:i.providerMetadata&&typeof i.providerMetadata=="object"&&!Array.isArray(i.providerMetadata)?i.providerMetadata:null,archivedAt:typeof i.archivedAt=="string"?i.archivedAt:null,createdAt:String(i.createdAt||""),updatedAt:String(i.updatedAt||i.createdAt||""),lastActiveAt:typeof i.lastActiveAt=="string"?i.lastActiveAt:null})):[];O(d),Fe(s),D(i=>i&&!d.some(u=>u.id===i.profileId)?null:i),X(i=>i&&!d.some(u=>u.id===i.profileId)?null:i)}finally{se(!1)}}},[l]);f.useEffect(()=>{C()},[C]),f.useEffect(()=>{const e=t=>{const o=t.detail;(String(o?.workspaceId||"").trim()||"default")===l&&o?.origin!=="ai-profiles-module"&&C()};return window.addEventListener(Ue,e),()=>window.removeEventListener(Ue,e)},[C,l]);const H=f.useMemo(()=>{const e=new Map;for(const t of N){const o=Pa(t);e.has(o)||e.set(o,new Map);const s=e.get(o),d=K(t);s.has(d)||s.set(d,[]),s.get(d).push(t)}return va.map(t=>({section:t,label:Te(t),groups:Array.from(e.get(t)?.entries()||[]).map(([o,s])=>({groupId:`${t}:${o}`,section:t,sectionLabel:Te(t),profiles:s,primaryProfile:ha(s)}))})).filter(t=>t.groups.length>0)},[N]),I=f.useMemo(()=>H.flatMap(e=>e.groups),[H]),n=f.useMemo(()=>I.find(e=>e.groupId===U)||I[0]||null,[I,U]),p=f.useMemo(()=>N.find(e=>e.id===de)||null,[N,de]),Ae=p&&me[p.id]||null,Se=n&&me[n.primaryProfile.id]||null;f.useEffect(()=>{if(!I.length){U!==null&&_(null);return}(!U||!I.some(e=>e.groupId===U))&&_(I[0].groupId)},[I,U]);const Je=async(e,t)=>{D(null);try{const o=await fetch("/api/taskforce/workspace/ai-profiles/merge",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({keepId:e,mergeId:t})}),s=await o.json().catch(()=>({}));if(!o.ok){w?.(String(s?.error||"Failed to merge AI profiles."),"error");return}F(null),w?.("AI profiles merged.","success"),await C(),b({workspaceId:l,profileId:e,reason:"merge",origin:"ai-profiles-module"})}catch{w?.("Failed to merge AI profiles.","error")}},xe=async e=>{if(window.confirm(`Remove ${e.name} from the active roster? This frees an AI profile seat and preserves task and comment history.`)){D(null);try{const o=await fetch("/api/taskforce/workspace/ai-profiles/archive",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:e.id,reason:"manual_archive"})}),s=await o.json().catch(()=>({}));if(!o.ok){D({profileId:e.id,message:String(s?.error||"Failed to remove AI profile from roster.")});return}(S?.keepId===e.id||S?.mergeId===e.id)&&F(null),w?.("AI profile removed from active roster.","success"),await C(),b({workspaceId:l,profileId:e.id,reason:"archive",origin:"ai-profiles-module"})}catch{D({profileId:e.id,message:"Failed to remove AI profile from roster."})}}},Ve=async(e,t)=>{const o=re(t),s=new Set(e.profiles.map(u=>u.surfaceType??"")),d=o??"";if(s.size===1&&s.has(d))return;ce(!0),X(null);const i=[];try{for(const c of e.profiles){const m=await fetch("/api/taskforce/workspace/ai-profiles/surface-type",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:c.id,surfaceType:o})}),v=await m.json().catch(()=>({}));if(!m.ok||!v?.profile)throw new Error(String(v?.error||"Failed to update AI profile category."));const j=v.profile;i.push({...c,surfaceType:re(j.surfaceType),updatedAt:typeof j.updatedAt=="string"?j.updatedAt:c.updatedAt})}const u=new Map(i.map(c=>[c.id,c]));O(c=>c.map(m=>u.get(m.id)||m));const h=u.get(e.primaryProfile.id)||{...e.primaryProfile,surfaceType:o},y=Me(h.surfaceType);_(`${y}:${K(h)}`),w?.("AI profile category updated.","success");for(const c of i)b({workspaceId:l,profileId:c.id,reason:"update",origin:"ai-profiles-module"})}catch(u){X({profileId:e.primaryProfile.id,message:String(u?.message||"Failed to update AI profile category.")})}finally{ce(!1)}},Ie=async(e,t,o=!1)=>{he(!0),Pe(null);try{const s=K(e.primaryProfile),d=N.filter(c=>K(c)===s),i=await fetch("/api/taskforce/workspace/ai-profiles/color",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileIds:d.map(c=>c.id),color:t,reset:o})}),u=await i.text();let h={};try{h=u?JSON.parse(u):{}}catch{h={}}if(!i.ok){const c=i.status===404?"Color updates are unavailable in the running Taskforce server. Restart Taskforce and try again.":`Failed to update AI profile color (HTTP ${i.status}).`;throw new Error(String(h.error||c))}if(!Array.isArray(h.profiles))throw new Error("Taskforce returned an invalid response while updating the AI profile color.");const y=new Map(h.profiles.map(c=>[String(c.id),c]));O(c=>c.map(m=>{const v=y.get(m.id);return v?{...m,color:String(v.color||m.color),colorSource:v.colorSource??m.colorSource,colorUpdatedAt:v.colorUpdatedAt??m.colorUpdatedAt,updatedAt:v.updatedAt??m.updatedAt}:m})),ge(!1);for(const c of d)b({workspaceId:l,profileId:c.id,reason:"update",origin:"ai-profiles-module"})}catch(s){Pe(String(s?.message||"Failed to update AI profile color."))}finally{he(!1)}},je=e=>new Promise((t,o)=>{const s=new FileReader;s.onload=()=>t(String(s.result||"")),s.onerror=()=>o(new Error("Failed to read image file.")),s.readAsDataURL(e)}),Q=e=>{const t=String(e?.id||"").trim();t&&O(o=>o.map(s=>s.id===t?{...s,avatarUrl:typeof e.avatarUrl=="string"?e.avatarUrl:null,avatarSourceUrl:typeof e.avatarSourceUrl=="string"?e.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(e.avatarRevision))?Math.max(0,Math.floor(Number(e.avatarRevision))):s.avatarRevision,avatarUpdatedAt:typeof e.avatarUpdatedAt=="string"?e.avatarUpdatedAt:s.avatarUpdatedAt,updatedAt:typeof e.updatedAt=="string"?e.updatedAt:s.updatedAt}:s))},Ke=async(e,t,o,s=!1)=>{const d=await je(t),i=o?await je(o):null,u={profileId:e,preserveExistingSource:s,displayImage:{dataUrl:d,mimeType:t.type||"application/octet-stream",originalName:t.name||"display-avatar"}};o&&i&&(u.sourceImage={dataUrl:i,mimeType:o.type||"application/octet-stream",originalName:o.name||"source-avatar"});const h=await fetch("/api/taskforce/workspace/ai-profiles/avatar/upload",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(u)}),y=await h.json().catch(()=>({}));if(!h.ok||!y?.profile)throw new Error(String(y?.error||"Failed to update AI profile avatar."));Q(y.profile),b({workspaceId:l,profileId:e,reason:"avatar",origin:"ai-profiles-module"})},We=async e=>{const t=await fetch("/api/taskforce/workspace/ai-profiles/avatar",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:e,avatarUrl:null,avatarSourceUrl:null})}),o=await t.json().catch(()=>({}));if(!t.ok||!o?.profile)throw new Error(String(o?.error||"Failed to update AI profile avatar."));Q(o.profile),b({workspaceId:l,profileId:e,reason:"avatar",origin:"ai-profiles-module"})},Ye=async e=>{if(R)return!1;const t=te(e),o=L(e);if(o&&!window.confirm(`Replace the existing avatar for ${e.name}?`))return!1;const s=t?"":window.prompt("Optional visual direction (300 characters maximum). Leave blank to use the profile details.","");if(s===null)return!1;const d=g==="local";if(d&&(!A||!$||!T||!B))return P("Sign in to Taskforce Cloud to generate profile avatars."),x(null),!1;pe(e.id),P(null),x(null),k(i=>({...i,[e.id]:{error:null,notice:null}}));try{const i=t?`/api/taskforce/agents/${encodeURIComponent(t)}/avatar/generate`:"/api/taskforce/workspace/ai-profiles/avatar/generate",u=d&&B?B(i):i,h=await fetch(u,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(t?{workspaceId:l}:{workspaceId:l,profileId:e.id,visualDirection:s.slice(0,300),replaceExisting:o})}),y=await h.text().catch(()=>"");let c={};if(y&&!/^\s*(?:<!doctype\s+html|<html\b)/i.test(y))try{c=JSON.parse(y)}catch{c={}}if(!h.ok||!c?.profile&&!c?.agent)throw new Error(String(c?.error||(h.status?`Unable to generate avatar. HTTP ${h.status}.`:"Unable to generate avatar.")));const m=c.profile||{id:e.id,avatarUrl:c.agent?.avatarUrl??null,avatarSourceUrl:c.agent?.avatarSourceUrl??null,avatarRevision:c.agent?.avatarRevision??0,avatarUpdatedAt:c.agent?.avatarUpdatedAt??null},v=d&&B?{...m,avatarUrl:m.avatarUrl?Ce(m.avatarUrl):m.avatarUrl,avatarSourceUrl:m.avatarSourceUrl?Ce(m.avatarSourceUrl):m.avatarSourceUrl}:m;return Q(v),k(d?j=>({...j,[e.id]:{error:null,notice:`Generated photo for ${e.name} was saved in Taskforce Cloud. The local copy will be retained when sync runs.`}}):j=>({...j,[e.id]:{error:null,notice:`Generated photo for ${e.name} was saved.`}})),b({workspaceId:l,profileId:e.id,reason:"avatar",origin:"ai-profiles-module",...t?{taskforceAgentAvatar:{agentId:t,avatarUrl:typeof v.avatarUrl=="string"?v.avatarUrl:null,avatarSourceUrl:typeof v.avatarSourceUrl=="string"?v.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(v.avatarRevision))?Math.max(0,Math.floor(Number(v.avatarRevision))):0,avatarUpdatedAt:typeof v.avatarUpdatedAt=="string"?v.avatarUpdatedAt:null}}:{}}),!0}catch(i){return k(u=>({...u,[e.id]:{error:String(i?.message||"Unable to generate profile avatar."),notice:null}})),!1}finally{pe(null)}},Ze=async(e,t,o)=>{if(!p)return!1;if(!e.type.startsWith("image/"))return P("AI profile photo must be an image file."),!1;z(!0),P(null),x(null),k(s=>{const d={...s};return delete d[p.id],d});try{const s=await ke(e,{maxBytes:5242880});if(s.exceededLimit)throw new Error(e.type==="image/gif"?"Animated GIF AI profile photos must be 5 MB or smaller.":"AI profile photo must be 5 MB or smaller.");const d=s.file;let i=null;if(t){const u=await ke(t,{maxBytes:5242880});if(u.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.");i=u.file}return await Ke(p.id,d,i,o?.preserveExistingSource===!0),!0}catch(s){return P(String(s?.message||"Failed to update AI profile photo.")),!1}finally{z(!1)}},Xe=async()=>{if(p){z(!0),P(null),x(null),k(e=>{const t={...e};return delete t[p.id],t});try{await We(p.id),x("Profile photo removed.")}catch(e){P(String(e?.message||"Failed to remove AI profile photo."))}finally{z(!1)}}},M=e=>{const t=String(e||"").trim();if(!t)return"Unknown";const o=Date.parse(t);return Number.isFinite(o)?new Date(o).toLocaleString(void 0,{dateStyle:"medium",timeStyle:"short"}):t},be=f.useMemo(()=>{if(!n)return{attributes:[],dates:[]};const e=n.primaryProfile,t=o=>o||"Not set";return{attributes:[{label:"Category",value:Aa(e)},{label:"Connection",value:e.seatScope?ea(e.seatScope):""},{label:"Provider",value:e.provider||""},{label:"Model",value:e.model||""}].map(o=>({...o,value:t(o.value)})),dates:[{label:"Status",value:e.archivedAt?"Retired":"Active"},{label:"Recruited",value:M(e.createdAt)},{label:"Last updated",value:M(e.updatedAt)},{label:"Last active",value:M(e.lastActiveAt)}].map(o=>({...o,value:t(o.value)}))}},[n]),qe=!!n&&n.profiles.length>1,E=!!n&&!L(n.primaryProfile)&&!!W(n.primaryProfile),Ne=A&&$&&!T,J=A?we(Y):Oe,ee=Ne?"Log into account for Cloud AI Profiles":J?`${J.used}/${J.limit===null?"Unlimited":J.limit}`:null,ae=!!oe;return a.jsxs("section",{className:`${r.agentsModuleRoot} ${ae?r.agentsModuleWithTray:""} ${ae&&Z?r.agentsModuleTrayOpen:""}`.trim(),children:[ae&&a.jsxs("aside",{className:`${r.agentTrayPanel} ${Z?r.agentTrayPanelOpen:""}`.trim(),"aria-label":"AI Profile MCP settings tray","aria-hidden":!Z,children:[a.jsxs("div",{className:r.agentTrayHeader,children:[a.jsxs("span",{className:r.agentTrayTitle,children:[a.jsx(ca,{size:14}),"MCP Settings"]}),a.jsx("button",{type:"button",className:"tf-control-icon",onClick:$e,title:"Collapse AI Profile tray","aria-label":"Collapse AI Profile tray",children:a.jsx(da,{size:16})})]}),a.jsx("div",{className:`${r.agentTrayContent} tf-scrollbar tf-scrollbar--track-transparent`,children:a.jsx("div",{className:r.agentTrayContentInner,children:oe})})]}),a.jsx("div",{className:r.agentsModuleContent,children:a.jsxs("div",{className:r.aiProfilesExplorer,children:[a.jsxs("aside",{className:r.aiProfilesRosterPanel,"aria-label":"AI profile roster",children:[a.jsx("div",{className:r.taskforceAgentRosterHeader,children:a.jsxs("span",{className:r.taskforceAgentRosterTitle,children:[a.jsx(fa,{size:14}),"AI Profiles"]})}),ee&&a.jsx("div",{className:r.aiProfilesRosterSummary,children:a.jsx("div",{className:r.aiProfileSeatSummary,children:Ne?ee:`Registered Cloud AI Profiles: ${ee}`})}),Ge?a.jsxs("div",{className:r.aiProfilesRosterState,role:"status","aria-live":"polite",children:[a.jsx(ua,{size:13,className:r.spinner})," Loading profiles…"]}):N.length===0?a.jsx("div",{className:r.aiProfilesRosterState,children:"No AI profiles registered yet."}):H.length>0?a.jsx("div",{className:`${r.aiProfilesListPane} tf-scrollbar tf-scrollbar--track-transparent tf-tray-scroll-viewport`,children:a.jsx("div",{className:r.aiProfilesList,children:H.map(e=>a.jsxs("div",{className:r.aiProfilesSection,children:[a.jsx("div",{className:r.aiProfilesSectionHeader,children:e.label}),e.groups.map(t=>{const o=n?.groupId===t.groupId,s=t.primaryProfile,d=!L(s)&&!!W(s);return a.jsx(sa,{name:s.name,username:s.username,subtitle:s.role||"Role not set",selected:o,onSelect:()=>_(t.groupId),avatarVariant:d?"logo":"portrait",avatar:a.jsx(ya,{profile:s}),avatarStyle:{color:s.color},className:t.profiles.length>1?r.aiProfileGroupDuplicate:"",topUtility:De(s.seatScope),bottomUtility:a.jsx("span",{className:r.aiProfileGroupSignatureSwatch,style:{backgroundColor:s.color},title:`Signature color ${s.color}`,"aria-hidden":"true"}),footer:t.profiles.length>1?a.jsxs("span",{className:r.aiProfileDuplicateBadge,children:[a.jsx(V,{size:11})," ",Sa(t.profiles.length)]}):null},t.groupId)})]},e.section))})}):null]}),a.jsx("main",{className:`${r.aiProfilesDetailViewport} tf-scrollbar`,"aria-label":"AI profile details",children:n&&a.jsx("div",{className:r.aiProfileDetailPane,children:a.jsxs("div",{className:r.aiProfileDetailCard,children:[De(n.primaryProfile.seatScope,{variant:"detail"}),a.jsxs("div",{className:r.aiProfileDetailHero,children:[a.jsx(aa,{label:"Edit AI profile photo",imageUrl:ie(n.primaryProfile),fallbackImageUrl:W(n.primaryProfile),fallback:a.jsx(Le,{size:38}),accentColor:E?null:n.primaryProfile.color,size:176,width:E?176:153,height:E?176:207,radius:E?0:6,editBadgeSize:28,editIconSize:14,loading:R===n.primaryProfile.id,loadingLabel:`Generating ${n.primaryProfile.name} photo`,error:!!Se?.error,errorLabel:Se?.error||"Profile photo generation failed",className:`${r.aiProfileDetailAvatar} ${E?r.aiProfileDetailAvatarLogo:""}`.trim(),onClick:()=>{P(null),x(null),fe(n.primaryProfile.id)}}),a.jsxs("div",{className:r.aiProfileDetailHeading,children:[a.jsx("div",{className:r.aiProfileDetailTitleRow,children:a.jsx("h5",{className:r.aiProfileDetailTitle,children:n.primaryProfile.name})}),a.jsxs("div",{className:r.aiProfileDetailMetaRow,children:[a.jsxs("span",{className:r.aiProfileDetailHandle,children:["@",n.primaryProfile.username]}),n.profiles.length>1&&a.jsxs("span",{className:r.aiProfileDetailLinkedCount,children:[n.profiles.length," linked"]})]}),a.jsxs("div",{className:r.aiProfileDetailRole,children:["Role: ",n.primaryProfile.role||"Not set"]}),n.primaryProfile.description&&a.jsx("div",{className:r.aiProfileDetailDescription,children:n.primaryProfile.description}),a.jsxs("div",{className:r.aiProfileSignatureColor,children:[a.jsx("span",{children:"Signature color"}),a.jsxs("button",{type:"button",className:r.aiProfileSignatureColorButton,onClick:()=>ge(e=>!e),disabled:q,"aria-expanded":ve,"aria-label":"Change signature color",children:[a.jsx("span",{className:r.aiProfileSignatureSwatch,style:{backgroundColor:n.primaryProfile.color},"aria-hidden":"true"}),a.jsx("span",{children:n.primaryProfile.color})]})]}),ve&&a.jsxs("div",{className:r.aiProfileColorPicker,"aria-label":"Signature color options",children:[na.map(({name:e,value:t})=>a.jsx("button",{type:"button",className:r.aiProfileColorOption,style:{backgroundColor:t},"aria-label":`Use ${e}`,"aria-pressed":n.primaryProfile.color.toLowerCase()===t.toLowerCase(),disabled:q,onClick:()=>{Ie(n,t)}},t)),a.jsx("button",{type:"button",className:r.secondaryHeaderBtn,disabled:q,onClick:()=>{Ie(n,void 0,!0)},children:"Reset"})]}),ye&&a.jsx("div",{className:r.aiProfileInlineError,children:ye})]})]}),a.jsxs("div",{className:r.aiProfileDetailDataList,children:[a.jsx("div",{className:r.aiProfileDetailDataGroup,children:be.attributes.map(e=>a.jsxs("div",{className:r.aiProfileDetailDataRow,children:[a.jsx("span",{className:r.aiProfileDetailDataLabel,children:e.label}),e.label==="Category"&&!le(n.primaryProfile)?a.jsx("span",{className:r.aiProfileDetailDataValue,children:a.jsxs("select",{className:r.aiProfileStatusSelect,"aria-label":"Agent category",value:n.primaryProfile.surfaceType??"",disabled:_e,onChange:t=>{Ve(n,t.target.value)},children:[a.jsx("option",{value:"",children:"Unclassified"}),ra.map(t=>a.jsx("option",{value:t,children:Re(t)},t))]})}):a.jsx("span",{className:r.aiProfileDetailDataValue,children:e.value})]},e.label))}),a.jsx("div",{className:`${r.aiProfileDetailDataGroup} ${r.aiProfileDetailDateGroup}`,children:be.dates.map(e=>a.jsxs("div",{className:r.aiProfileDetailDataRow,children:[a.jsx("span",{className:r.aiProfileDetailDataLabel,children:e.label}),e.label==="Status"?a.jsx("span",{className:r.aiProfileDetailDataValue,children:a.jsxs("select",{className:r.aiProfileStatusSelect,"aria-label":"Agent roster status",value:n.primaryProfile.archivedAt?"retired":"active",onChange:t=>{t.target.value==="retire"&&xe(n.primaryProfile)},children:[a.jsx("option",{value:"active",children:"Active"}),n.primaryProfile.archivedAt?a.jsx("option",{value:"retired",children:"Retired"}):a.jsx("option",{value:"retire",children:"Retire from roster"})]})}):a.jsx("span",{className:r.aiProfileDetailDataValue,children:e.value})]},e.label))})]}),ne?.profileId===n.primaryProfile.id&&a.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[a.jsx(V,{size:12}),a.jsx("span",{children:ne.message})]}),qe?a.jsxs("div",{className:r.aiProfileInstanceSection,children:[a.jsxs("div",{className:r.aiProfileInstanceSectionHeader,children:[a.jsx("span",{children:"Profile instances"}),a.jsx("span",{className:r.settingsHint,children:"Choose a keeper here if duplicates need to be merged."})]}),a.jsx("div",{className:r.aiProfileInstanceList,children:n.profiles.map(e=>a.jsxs("div",{className:r.aiProfileInstanceCard,children:[a.jsxs("div",{className:r.aiProfileInstanceTopRow,children:[a.jsxs("div",{children:[a.jsxs("div",{className:r.aiProfileInstanceName,children:["@",e.username]}),a.jsx("div",{className:r.aiProfileIdChip,children:e.id})]}),S?.keepId===e.id&&a.jsx("span",{className:r.aiProfileKeepBadge,children:"Keeping"})]}),a.jsxs("div",{className:r.aiProfileInstanceMeta,children:[a.jsxs("span",{children:["Created ",M(e.createdAt)]}),a.jsxs("span",{children:["Updated ",M(e.updatedAt)]})]}),S?.keepId!==e.id&&a.jsx("button",{className:r.secondaryHeaderBtn,title:"Keep this profile, merge others into it",onClick:()=>{const t=n.profiles.find(o=>o.id!==e.id)?.id;t&&F({keepId:e.id,mergeId:t})},children:"Keep this"}),G?.profileId===e.id&&a.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[a.jsx(V,{size:12}),a.jsx("span",{children:G.message})]}),a.jsx("button",{className:r.aiProfileDangerTextButton,title:"Remove this profile from the active roster",onClick:()=>{xe(e)},children:"Remove from roster"})]},e.id))}),S&&n.profiles.some(e=>e.id===S.keepId)&&a.jsxs("div",{className:r.aiProfileMergeActions,children:[a.jsx("button",{className:r.dangerBtn,onClick:()=>{Je(S.keepId,S.mergeId)},children:"Merge duplicates"}),a.jsx("button",{className:r.secondaryHeaderBtn,onClick:()=>F(null),children:"Cancel"})]})]}):a.jsxs("div",{className:r.aiProfileIdFooter,children:[a.jsx("div",{className:r.aiProfileIdFooterValue,children:n.primaryProfile.id}),G?.profileId===n.primaryProfile.id&&a.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[a.jsx(V,{size:12}),a.jsx("span",{children:G.message})]})]})]})})})]})}),a.jsx(ta,{isOpen:!!p,theme:Be,title:"Edit AI Profile Photo",currentImageUrl:ie(p),editorImageUrl:ga(p),fallbackInitial:(p?.name||"AI").charAt(0).toUpperCase(),accept:"image/png,image/jpeg,image/webp,image/gif",busy:ue,generating:R===p?.id,generateLabel:p&&L(p)?"Regenerate":"Generate",hasPendingImage:!1,canRemove:!!p?.avatarUrl,error:ze||Ae?.error||null,notice:He||Ae?.notice||null,onClose:()=>{ue||(fe(null),P(null),x(null))},onApplyImage:Ze,onGenerateImage:p&&(!R||R===p.id)&&(ia(p)||te(p))?()=>Ye(p):void 0,onRemoveImage:Xe},p?.id||"ai-profile-avatar-closed")]})}export{Ca as AiProfilesModule};
|
|
1
|
+
import{r as f,j as a}from"./vendor-react-CKJs5o3c.js";import{ad as Qe,ae as re,U as Ue,af as ea,ag as W,t as r,W as aa,ah as ra,ai as Re,X as ta,aj as ia,ak as te,al as Me,am as la,Z as oa,Y as Ee,a0 as b,a1 as ke}from"./index-GHJgcW3j.js";import{A as sa,b as na,r as Ce}from"./AiIdentityRosterCard-Cfvb9Nuv.js";import{ay as ca,ak as da,U as fa,w as ua,Z as V,J as Le,ax as pa,aC as ma}from"./vendor-icons-Bsq-mcEn.js";import"./vendor-markdown-_lhNCyq-.js";import"./vendor-dnd-CJ-AjP-Y.js";import"./vendor-router-AqJMU8Lz.js";const va=["chat_app","ide","cli","coding_tool","taskforce_agent","agent","unclassified"];function L(l){return typeof l.avatarUrl=="string"&&l.avatarUrl.trim().length>0}function ie(l){return l?Ee(l.avatarUrl,l.avatarRevision,l.avatarUpdatedAt):""}function ga(l){return l?Ee(l.avatarSourceUrl,l.avatarRevision,l.avatarUpdatedAt):""}function ha(l){return l.find(L)||l[0]}function ya({profile:l,className:g=""}){const A=ie(l),$=W(l),{activeImageUrl:T,handleImageError:Y}=oa(A,$);return T?a.jsx("img",{src:T,alt:"",className:g,onError:Y}):a.jsx(Le,{size:22})}function K(l){const g=te(l);return g?`taskforce-agent:${g}`:`${l.seatScope||"unknown"}:${l.name.trim().toLowerCase()}`}function le(l){const g=l.providerMetadata?.taskforce;return!!(g&&typeof g=="object"&&!Array.isArray(g)&&String(g.taskforceAgentId||"").trim())}function Pa(l){return le(l)?"taskforce_agent":Me(l.surfaceType)}function Te(l){return l==="taskforce_agent"?"Taskforce Agents":la(l)}function Aa(l){return le(l)?"Taskforce Agent":l.surfaceType?Re(l.surfaceType):""}function Sa(l){const g=Math.max(0,l-1);return`${g} duplicate${g===1?"":"s"}`}function we(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 De(l,g){const A=g?.variant==="inline"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconInline}`:g?.variant==="detail"?`${r.aiProfileSeatScopeIcon} ${r.aiProfileSeatScopeIconDetail}`:r.aiProfileSeatScopeIcon;return l==="cloud_metered"?a.jsx("span",{className:`${A} ${r.aiProfileSeatScopeIconCloud}`,title:"Cloud MCP","aria-label":"Cloud MCP",children:a.jsx(pa,{size:20})}):l==="local_unmetered"?a.jsx("span",{className:`${A} ${r.aiProfileSeatScopeIconLocal}`,title:"Local MCP","aria-label":"Local MCP",children:a.jsx(ma,{size:20})}):null}function Ca({workspaceId:l,runtimeMode:g="local",cloudAuthConfigured:A=!1,authSessionResolved:$=!1,isAuthenticated:T=!1,cloudAiProfileSeatUsage:Y=null,agentTrayOpen:Z=!1,onCloseAgentTray:$e,mcpSettingsNode:oe=null,resolveCloudAuthUrl:B,theme:Be="dark",onNotice:w}){const[N,O]=f.useState([]),[Oe,Fe]=f.useState(null),[Ge,se]=f.useState(!1),[S,F]=f.useState(null),[G,D]=f.useState(null),[ne,X]=f.useState(null),[_e,ce]=f.useState(!1),[U,_]=f.useState(null),[de,fe]=f.useState(null),[ue,z]=f.useState(!1),[ze,P]=f.useState(null),[He,x]=f.useState(null),[R,pe]=f.useState(null),[me,k]=f.useState({}),[ve,ge]=f.useState(!1),[q,he]=f.useState(!1),[ye,Pe]=f.useState(null),C=f.useCallback(async()=>{if(l){se(!0);try{const e=`/api/taskforce/workspace/assignee-options?workspaceId=${encodeURIComponent(l)}&kind=agent`,t=await fetch(e,{credentials:"include"}),o=t.ok?await t.json().catch(()=>({})):{},s=we(o?.aiProfileSeatUsage),d=Array.isArray(o?.assignees)?o.assignees.filter(i=>i.kind==="agent").map(i=>({id:String(i.value||""),name:String(i.label||i.value||"Unknown Agent"),username:String(i.username||i.value||""),icon:String(i.icon||"Bot"),color:String(i.color||"#6B7280"),colorSource:typeof i.colorSource=="string"?i.colorSource:null,colorUpdatedAt:typeof i.colorUpdatedAt=="string"?i.colorUpdatedAt:null,avatarUrl:typeof i.avatarUrl=="string"?i.avatarUrl:null,avatarSourceUrl:typeof i.avatarSourceUrl=="string"?i.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(i.avatarRevision))?Math.max(0,Math.floor(Number(i.avatarRevision))):0,avatarUpdatedAt:typeof i.avatarUpdatedAt=="string"?i.avatarUpdatedAt:null,kind:String(i.kind||"agent"),description:typeof i.description=="string"?i.description:null,role:typeof i.role=="string"?i.role:null,provider:typeof i.provider=="string"?i.provider:null,model:typeof i.model=="string"?i.model:null,surfaceType:re(i.surfaceType),seatScope:Qe(i.seatScope),providerMetadata:i.providerMetadata&&typeof i.providerMetadata=="object"&&!Array.isArray(i.providerMetadata)?i.providerMetadata:null,archivedAt:typeof i.archivedAt=="string"?i.archivedAt:null,createdAt:String(i.createdAt||""),updatedAt:String(i.updatedAt||i.createdAt||""),lastActiveAt:typeof i.lastActiveAt=="string"?i.lastActiveAt:null})):[];O(d),Fe(s),D(i=>i&&!d.some(u=>u.id===i.profileId)?null:i),X(i=>i&&!d.some(u=>u.id===i.profileId)?null:i)}finally{se(!1)}}},[l]);f.useEffect(()=>{C()},[C]),f.useEffect(()=>{const e=t=>{const o=t.detail;(String(o?.workspaceId||"").trim()||"default")===l&&o?.origin!=="ai-profiles-module"&&C()};return window.addEventListener(Ue,e),()=>window.removeEventListener(Ue,e)},[C,l]);const H=f.useMemo(()=>{const e=new Map;for(const t of N){const o=Pa(t);e.has(o)||e.set(o,new Map);const s=e.get(o),d=K(t);s.has(d)||s.set(d,[]),s.get(d).push(t)}return va.map(t=>({section:t,label:Te(t),groups:Array.from(e.get(t)?.entries()||[]).map(([o,s])=>({groupId:`${t}:${o}`,section:t,sectionLabel:Te(t),profiles:s,primaryProfile:ha(s)}))})).filter(t=>t.groups.length>0)},[N]),I=f.useMemo(()=>H.flatMap(e=>e.groups),[H]),n=f.useMemo(()=>I.find(e=>e.groupId===U)||I[0]||null,[I,U]),p=f.useMemo(()=>N.find(e=>e.id===de)||null,[N,de]),Ae=p&&me[p.id]||null,Se=n&&me[n.primaryProfile.id]||null;f.useEffect(()=>{if(!I.length){U!==null&&_(null);return}(!U||!I.some(e=>e.groupId===U))&&_(I[0].groupId)},[I,U]);const Je=async(e,t)=>{D(null);try{const o=await fetch("/api/taskforce/workspace/ai-profiles/merge",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({keepId:e,mergeId:t})}),s=await o.json().catch(()=>({}));if(!o.ok){w?.(String(s?.error||"Failed to merge AI profiles."),"error");return}F(null),w?.("AI profiles merged.","success"),await C(),b({workspaceId:l,profileId:e,reason:"merge",origin:"ai-profiles-module"})}catch{w?.("Failed to merge AI profiles.","error")}},xe=async e=>{if(window.confirm(`Remove ${e.name} from the active roster? This frees an AI profile seat and preserves task and comment history.`)){D(null);try{const o=await fetch("/api/taskforce/workspace/ai-profiles/archive",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:e.id,reason:"manual_archive"})}),s=await o.json().catch(()=>({}));if(!o.ok){D({profileId:e.id,message:String(s?.error||"Failed to remove AI profile from roster.")});return}(S?.keepId===e.id||S?.mergeId===e.id)&&F(null),w?.("AI profile removed from active roster.","success"),await C(),b({workspaceId:l,profileId:e.id,reason:"archive",origin:"ai-profiles-module"})}catch{D({profileId:e.id,message:"Failed to remove AI profile from roster."})}}},Ve=async(e,t)=>{const o=re(t),s=new Set(e.profiles.map(u=>u.surfaceType??"")),d=o??"";if(s.size===1&&s.has(d))return;ce(!0),X(null);const i=[];try{for(const c of e.profiles){const m=await fetch("/api/taskforce/workspace/ai-profiles/surface-type",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:c.id,surfaceType:o})}),v=await m.json().catch(()=>({}));if(!m.ok||!v?.profile)throw new Error(String(v?.error||"Failed to update AI profile category."));const j=v.profile;i.push({...c,surfaceType:re(j.surfaceType),updatedAt:typeof j.updatedAt=="string"?j.updatedAt:c.updatedAt})}const u=new Map(i.map(c=>[c.id,c]));O(c=>c.map(m=>u.get(m.id)||m));const h=u.get(e.primaryProfile.id)||{...e.primaryProfile,surfaceType:o},y=Me(h.surfaceType);_(`${y}:${K(h)}`),w?.("AI profile category updated.","success");for(const c of i)b({workspaceId:l,profileId:c.id,reason:"update",origin:"ai-profiles-module"})}catch(u){X({profileId:e.primaryProfile.id,message:String(u?.message||"Failed to update AI profile category.")})}finally{ce(!1)}},Ie=async(e,t,o=!1)=>{he(!0),Pe(null);try{const s=K(e.primaryProfile),d=N.filter(c=>K(c)===s),i=await fetch("/api/taskforce/workspace/ai-profiles/color",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileIds:d.map(c=>c.id),color:t,reset:o})}),u=await i.text();let h={};try{h=u?JSON.parse(u):{}}catch{h={}}if(!i.ok){const c=i.status===404?"Color updates are unavailable in the running Taskforce server. Restart Taskforce and try again.":`Failed to update AI profile color (HTTP ${i.status}).`;throw new Error(String(h.error||c))}if(!Array.isArray(h.profiles))throw new Error("Taskforce returned an invalid response while updating the AI profile color.");const y=new Map(h.profiles.map(c=>[String(c.id),c]));O(c=>c.map(m=>{const v=y.get(m.id);return v?{...m,color:String(v.color||m.color),colorSource:v.colorSource??m.colorSource,colorUpdatedAt:v.colorUpdatedAt??m.colorUpdatedAt,updatedAt:v.updatedAt??m.updatedAt}:m})),ge(!1);for(const c of d)b({workspaceId:l,profileId:c.id,reason:"update",origin:"ai-profiles-module"})}catch(s){Pe(String(s?.message||"Failed to update AI profile color."))}finally{he(!1)}},je=e=>new Promise((t,o)=>{const s=new FileReader;s.onload=()=>t(String(s.result||"")),s.onerror=()=>o(new Error("Failed to read image file.")),s.readAsDataURL(e)}),Q=e=>{const t=String(e?.id||"").trim();t&&O(o=>o.map(s=>s.id===t?{...s,avatarUrl:typeof e.avatarUrl=="string"?e.avatarUrl:null,avatarSourceUrl:typeof e.avatarSourceUrl=="string"?e.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(e.avatarRevision))?Math.max(0,Math.floor(Number(e.avatarRevision))):s.avatarRevision,avatarUpdatedAt:typeof e.avatarUpdatedAt=="string"?e.avatarUpdatedAt:s.avatarUpdatedAt,updatedAt:typeof e.updatedAt=="string"?e.updatedAt:s.updatedAt}:s))},Ke=async(e,t,o,s=!1)=>{const d=await je(t),i=o?await je(o):null,u={profileId:e,preserveExistingSource:s,displayImage:{dataUrl:d,mimeType:t.type||"application/octet-stream",originalName:t.name||"display-avatar"}};o&&i&&(u.sourceImage={dataUrl:i,mimeType:o.type||"application/octet-stream",originalName:o.name||"source-avatar"});const h=await fetch("/api/taskforce/workspace/ai-profiles/avatar/upload",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(u)}),y=await h.json().catch(()=>({}));if(!h.ok||!y?.profile)throw new Error(String(y?.error||"Failed to update AI profile avatar."));Q(y.profile),b({workspaceId:l,profileId:e,reason:"avatar",origin:"ai-profiles-module"})},We=async e=>{const t=await fetch("/api/taskforce/workspace/ai-profiles/avatar",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({profileId:e,avatarUrl:null,avatarSourceUrl:null})}),o=await t.json().catch(()=>({}));if(!t.ok||!o?.profile)throw new Error(String(o?.error||"Failed to update AI profile avatar."));Q(o.profile),b({workspaceId:l,profileId:e,reason:"avatar",origin:"ai-profiles-module"})},Ye=async e=>{if(R)return!1;const t=te(e),o=L(e);if(o&&!window.confirm(`Replace the existing avatar for ${e.name}?`))return!1;const s=t?"":window.prompt("Optional visual direction (300 characters maximum). Leave blank to use the profile details.","");if(s===null)return!1;const d=g==="local";if(d&&(!A||!$||!T||!B))return P("Sign in to Taskforce Cloud to generate profile avatars."),x(null),!1;pe(e.id),P(null),x(null),k(i=>({...i,[e.id]:{error:null,notice:null}}));try{const i=t?`/api/taskforce/agents/${encodeURIComponent(t)}/avatar/generate`:"/api/taskforce/workspace/ai-profiles/avatar/generate",u=d&&B?B(i):i,h=await fetch(u,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify(t?{workspaceId:l}:{workspaceId:l,profileId:e.id,visualDirection:s.slice(0,300),replaceExisting:o})}),y=await h.text().catch(()=>"");let c={};if(y&&!/^\s*(?:<!doctype\s+html|<html\b)/i.test(y))try{c=JSON.parse(y)}catch{c={}}if(!h.ok||!c?.profile&&!c?.agent)throw new Error(String(c?.error||(h.status?`Unable to generate avatar. HTTP ${h.status}.`:"Unable to generate avatar.")));const m=c.profile||{id:e.id,avatarUrl:c.agent?.avatarUrl??null,avatarSourceUrl:c.agent?.avatarSourceUrl??null,avatarRevision:c.agent?.avatarRevision??0,avatarUpdatedAt:c.agent?.avatarUpdatedAt??null},v=d&&B?{...m,avatarUrl:m.avatarUrl?Ce(m.avatarUrl):m.avatarUrl,avatarSourceUrl:m.avatarSourceUrl?Ce(m.avatarSourceUrl):m.avatarSourceUrl}:m;return Q(v),k(d?j=>({...j,[e.id]:{error:null,notice:`Generated photo for ${e.name} was saved in Taskforce Cloud. The local copy will be retained when sync runs.`}}):j=>({...j,[e.id]:{error:null,notice:`Generated photo for ${e.name} was saved.`}})),b({workspaceId:l,profileId:e.id,reason:"avatar",origin:"ai-profiles-module",...t?{taskforceAgentAvatar:{agentId:t,avatarUrl:typeof v.avatarUrl=="string"?v.avatarUrl:null,avatarSourceUrl:typeof v.avatarSourceUrl=="string"?v.avatarSourceUrl:null,avatarRevision:Number.isFinite(Number(v.avatarRevision))?Math.max(0,Math.floor(Number(v.avatarRevision))):0,avatarUpdatedAt:typeof v.avatarUpdatedAt=="string"?v.avatarUpdatedAt:null}}:{}}),!0}catch(i){return k(u=>({...u,[e.id]:{error:String(i?.message||"Unable to generate profile avatar."),notice:null}})),!1}finally{pe(null)}},Ze=async(e,t,o)=>{if(!p)return!1;if(!e.type.startsWith("image/"))return P("AI profile photo must be an image file."),!1;z(!0),P(null),x(null),k(s=>{const d={...s};return delete d[p.id],d});try{const s=await ke(e,{maxBytes:5242880});if(s.exceededLimit)throw new Error(e.type==="image/gif"?"Animated GIF AI profile photos must be 5 MB or smaller.":"AI profile photo must be 5 MB or smaller.");const d=s.file;let i=null;if(t){const u=await ke(t,{maxBytes:5242880});if(u.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.");i=u.file}return await Ke(p.id,d,i,o?.preserveExistingSource===!0),!0}catch(s){return P(String(s?.message||"Failed to update AI profile photo.")),!1}finally{z(!1)}},Xe=async()=>{if(p){z(!0),P(null),x(null),k(e=>{const t={...e};return delete t[p.id],t});try{await We(p.id),x("Profile photo removed.")}catch(e){P(String(e?.message||"Failed to remove AI profile photo."))}finally{z(!1)}}},M=e=>{const t=String(e||"").trim();if(!t)return"Unknown";const o=Date.parse(t);return Number.isFinite(o)?new Date(o).toLocaleString(void 0,{dateStyle:"medium",timeStyle:"short"}):t},be=f.useMemo(()=>{if(!n)return{attributes:[],dates:[]};const e=n.primaryProfile,t=o=>o||"Not set";return{attributes:[{label:"Category",value:Aa(e)},{label:"Connection",value:e.seatScope?ea(e.seatScope):""},{label:"Provider",value:e.provider||""},{label:"Model",value:e.model||""}].map(o=>({...o,value:t(o.value)})),dates:[{label:"Status",value:e.archivedAt?"Retired":"Active"},{label:"Recruited",value:M(e.createdAt)},{label:"Last updated",value:M(e.updatedAt)},{label:"Last active",value:M(e.lastActiveAt)}].map(o=>({...o,value:t(o.value)}))}},[n]),qe=!!n&&n.profiles.length>1,E=!!n&&!L(n.primaryProfile)&&!!W(n.primaryProfile),Ne=A&&$&&!T,J=A?we(Y):Oe,ee=Ne?"Log into account for Cloud AI Profiles":J?`${J.used}/${J.limit===null?"Unlimited":J.limit}`:null,ae=!!oe;return a.jsxs("section",{className:`${r.agentsModuleRoot} ${ae?r.agentsModuleWithTray:""} ${ae&&Z?r.agentsModuleTrayOpen:""}`.trim(),children:[ae&&a.jsxs("aside",{className:`${r.agentTrayPanel} ${Z?r.agentTrayPanelOpen:""}`.trim(),"aria-label":"AI Profile MCP settings tray","aria-hidden":!Z,children:[a.jsxs("div",{className:r.agentTrayHeader,children:[a.jsxs("span",{className:r.agentTrayTitle,children:[a.jsx(ca,{size:14}),"MCP Settings"]}),a.jsx("button",{type:"button",className:"tf-control-icon",onClick:$e,title:"Collapse AI Profile tray","aria-label":"Collapse AI Profile tray",children:a.jsx(da,{size:16})})]}),a.jsx("div",{className:`${r.agentTrayContent} tf-scrollbar tf-scrollbar--track-transparent`,children:a.jsx("div",{className:r.agentTrayContentInner,children:oe})})]}),a.jsx("div",{className:r.agentsModuleContent,children:a.jsxs("div",{className:r.aiProfilesExplorer,children:[a.jsxs("aside",{className:r.aiProfilesRosterPanel,"aria-label":"AI profile roster",children:[a.jsx("div",{className:r.taskforceAgentRosterHeader,children:a.jsxs("span",{className:r.taskforceAgentRosterTitle,children:[a.jsx(fa,{size:14}),"AI Profiles"]})}),ee&&a.jsx("div",{className:r.aiProfilesRosterSummary,children:a.jsx("div",{className:r.aiProfileSeatSummary,children:Ne?ee:`Registered Cloud AI Profiles: ${ee}`})}),Ge?a.jsxs("div",{className:r.aiProfilesRosterState,role:"status","aria-live":"polite",children:[a.jsx(ua,{size:13,className:r.spinner})," Loading profiles…"]}):N.length===0?a.jsx("div",{className:r.aiProfilesRosterState,children:"No AI profiles registered yet."}):H.length>0?a.jsx("div",{className:`${r.aiProfilesListPane} tf-scrollbar tf-scrollbar--track-transparent tf-tray-scroll-viewport`,children:a.jsx("div",{className:r.aiProfilesList,children:H.map(e=>a.jsxs("div",{className:r.aiProfilesSection,children:[a.jsx("div",{className:r.aiProfilesSectionHeader,children:e.label}),e.groups.map(t=>{const o=n?.groupId===t.groupId,s=t.primaryProfile,d=!L(s)&&!!W(s);return a.jsx(sa,{name:s.name,username:s.username,subtitle:s.role||"Role not set",selected:o,onSelect:()=>_(t.groupId),avatarVariant:d?"logo":"portrait",avatar:a.jsx(ya,{profile:s}),avatarStyle:{color:s.color},className:t.profiles.length>1?r.aiProfileGroupDuplicate:"",topUtility:De(s.seatScope),bottomUtility:a.jsx("span",{className:r.aiProfileGroupSignatureSwatch,style:{backgroundColor:s.color},title:`Signature color ${s.color}`,"aria-hidden":"true"}),footer:t.profiles.length>1?a.jsxs("span",{className:r.aiProfileDuplicateBadge,children:[a.jsx(V,{size:11})," ",Sa(t.profiles.length)]}):null},t.groupId)})]},e.section))})}):null]}),a.jsx("main",{className:`${r.aiProfilesDetailViewport} tf-scrollbar`,"aria-label":"AI profile details",children:n&&a.jsx("div",{className:r.aiProfileDetailPane,children:a.jsxs("div",{className:r.aiProfileDetailCard,children:[De(n.primaryProfile.seatScope,{variant:"detail"}),a.jsxs("div",{className:r.aiProfileDetailHero,children:[a.jsx(aa,{label:"Edit AI profile photo",imageUrl:ie(n.primaryProfile),fallbackImageUrl:W(n.primaryProfile),fallback:a.jsx(Le,{size:38}),accentColor:E?null:n.primaryProfile.color,size:176,width:E?176:153,height:E?176:207,radius:E?0:6,editBadgeSize:28,editIconSize:14,loading:R===n.primaryProfile.id,loadingLabel:`Generating ${n.primaryProfile.name} photo`,error:!!Se?.error,errorLabel:Se?.error||"Profile photo generation failed",className:`${r.aiProfileDetailAvatar} ${E?r.aiProfileDetailAvatarLogo:""}`.trim(),onClick:()=>{P(null),x(null),fe(n.primaryProfile.id)}}),a.jsxs("div",{className:r.aiProfileDetailHeading,children:[a.jsx("div",{className:r.aiProfileDetailTitleRow,children:a.jsx("h5",{className:r.aiProfileDetailTitle,children:n.primaryProfile.name})}),a.jsxs("div",{className:r.aiProfileDetailMetaRow,children:[a.jsxs("span",{className:r.aiProfileDetailHandle,children:["@",n.primaryProfile.username]}),n.profiles.length>1&&a.jsxs("span",{className:r.aiProfileDetailLinkedCount,children:[n.profiles.length," linked"]})]}),a.jsxs("div",{className:r.aiProfileDetailRole,children:["Role: ",n.primaryProfile.role||"Not set"]}),n.primaryProfile.description&&a.jsx("div",{className:r.aiProfileDetailDescription,children:n.primaryProfile.description}),a.jsxs("div",{className:r.aiProfileSignatureColor,children:[a.jsx("span",{children:"Signature color"}),a.jsxs("button",{type:"button",className:r.aiProfileSignatureColorButton,onClick:()=>ge(e=>!e),disabled:q,"aria-expanded":ve,"aria-label":"Change signature color",children:[a.jsx("span",{className:r.aiProfileSignatureSwatch,style:{backgroundColor:n.primaryProfile.color},"aria-hidden":"true"}),a.jsx("span",{children:n.primaryProfile.color})]})]}),ve&&a.jsxs("div",{className:r.aiProfileColorPicker,"aria-label":"Signature color options",children:[na.map(({name:e,value:t})=>a.jsx("button",{type:"button",className:r.aiProfileColorOption,style:{backgroundColor:t},"aria-label":`Use ${e}`,"aria-pressed":n.primaryProfile.color.toLowerCase()===t.toLowerCase(),disabled:q,onClick:()=>{Ie(n,t)}},t)),a.jsx("button",{type:"button",className:r.secondaryHeaderBtn,disabled:q,onClick:()=>{Ie(n,void 0,!0)},children:"Reset"})]}),ye&&a.jsx("div",{className:r.aiProfileInlineError,children:ye})]})]}),a.jsxs("div",{className:r.aiProfileDetailDataList,children:[a.jsx("div",{className:r.aiProfileDetailDataGroup,children:be.attributes.map(e=>a.jsxs("div",{className:r.aiProfileDetailDataRow,children:[a.jsx("span",{className:r.aiProfileDetailDataLabel,children:e.label}),e.label==="Category"&&!le(n.primaryProfile)?a.jsx("span",{className:r.aiProfileDetailDataValue,children:a.jsxs("select",{className:r.aiProfileStatusSelect,"aria-label":"Agent category",value:n.primaryProfile.surfaceType??"",disabled:_e,onChange:t=>{Ve(n,t.target.value)},children:[a.jsx("option",{value:"",children:"Unclassified"}),ra.map(t=>a.jsx("option",{value:t,children:Re(t)},t))]})}):a.jsx("span",{className:r.aiProfileDetailDataValue,children:e.value})]},e.label))}),a.jsx("div",{className:`${r.aiProfileDetailDataGroup} ${r.aiProfileDetailDateGroup}`,children:be.dates.map(e=>a.jsxs("div",{className:r.aiProfileDetailDataRow,children:[a.jsx("span",{className:r.aiProfileDetailDataLabel,children:e.label}),e.label==="Status"?a.jsx("span",{className:r.aiProfileDetailDataValue,children:a.jsxs("select",{className:r.aiProfileStatusSelect,"aria-label":"Agent roster status",value:n.primaryProfile.archivedAt?"retired":"active",onChange:t=>{t.target.value==="retire"&&xe(n.primaryProfile)},children:[a.jsx("option",{value:"active",children:"Active"}),n.primaryProfile.archivedAt?a.jsx("option",{value:"retired",children:"Retired"}):a.jsx("option",{value:"retire",children:"Retire from roster"})]})}):a.jsx("span",{className:r.aiProfileDetailDataValue,children:e.value})]},e.label))})]}),ne?.profileId===n.primaryProfile.id&&a.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[a.jsx(V,{size:12}),a.jsx("span",{children:ne.message})]}),qe?a.jsxs("div",{className:r.aiProfileInstanceSection,children:[a.jsxs("div",{className:r.aiProfileInstanceSectionHeader,children:[a.jsx("span",{children:"Profile instances"}),a.jsx("span",{className:r.settingsHint,children:"Choose a keeper here if duplicates need to be merged."})]}),a.jsx("div",{className:r.aiProfileInstanceList,children:n.profiles.map(e=>a.jsxs("div",{className:r.aiProfileInstanceCard,children:[a.jsxs("div",{className:r.aiProfileInstanceTopRow,children:[a.jsxs("div",{children:[a.jsxs("div",{className:r.aiProfileInstanceName,children:["@",e.username]}),a.jsx("div",{className:r.aiProfileIdChip,children:e.id})]}),S?.keepId===e.id&&a.jsx("span",{className:r.aiProfileKeepBadge,children:"Keeping"})]}),a.jsxs("div",{className:r.aiProfileInstanceMeta,children:[a.jsxs("span",{children:["Created ",M(e.createdAt)]}),a.jsxs("span",{children:["Updated ",M(e.updatedAt)]})]}),S?.keepId!==e.id&&a.jsx("button",{className:r.secondaryHeaderBtn,title:"Keep this profile, merge others into it",onClick:()=>{const t=n.profiles.find(o=>o.id!==e.id)?.id;t&&F({keepId:e.id,mergeId:t})},children:"Keep this"}),G?.profileId===e.id&&a.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[a.jsx(V,{size:12}),a.jsx("span",{children:G.message})]}),a.jsx("button",{className:r.aiProfileDangerTextButton,title:"Remove this profile from the active roster",onClick:()=>{xe(e)},children:"Remove from roster"})]},e.id))}),S&&n.profiles.some(e=>e.id===S.keepId)&&a.jsxs("div",{className:r.aiProfileMergeActions,children:[a.jsx("button",{className:r.dangerBtn,onClick:()=>{Je(S.keepId,S.mergeId)},children:"Merge duplicates"}),a.jsx("button",{className:r.secondaryHeaderBtn,onClick:()=>F(null),children:"Cancel"})]})]}):a.jsxs("div",{className:r.aiProfileIdFooter,children:[a.jsx("div",{className:r.aiProfileIdFooterValue,children:n.primaryProfile.id}),G?.profileId===n.primaryProfile.id&&a.jsxs("div",{className:r.aiProfileInlineError,role:"alert",children:[a.jsx(V,{size:12}),a.jsx("span",{children:G.message})]})]})]})})})]})}),a.jsx(ta,{isOpen:!!p,theme:Be,title:"Edit AI Profile Photo",currentImageUrl:ie(p),editorImageUrl:ga(p),fallbackInitial:(p?.name||"AI").charAt(0).toUpperCase(),accept:"image/png,image/jpeg,image/webp,image/gif",busy:ue,generating:R===p?.id,generateLabel:p&&L(p)?"Regenerate":"Generate",hasPendingImage:!1,canRemove:!!p?.avatarUrl,error:ze||Ae?.error||null,notice:He||Ae?.notice||null,onClose:()=>{ue||(fe(null),P(null),x(null))},onApplyImage:Ze,onGenerateImage:p&&(!R||R===p.id)&&(ia(p)||te(p))?()=>Ye(p):void 0,onRemoveImage:Xe},p?.id||"ai-profile-avatar-closed")]})}export{Ca as AiProfilesModule};
|