@taskforcehq/taskforce 0.3.332 → 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.
Files changed (32) hide show
  1. package/dist/server/routes/localService.js +1 -0
  2. package/dist/server/routes/syncPushRoutes.d.ts +3 -0
  3. package/dist/server/routes/syncPushRoutes.js +8 -2
  4. package/dist/sync/coordinator/localWorkspaceSyncRunnerState.d.ts +1 -0
  5. package/dist/sync/coordinator/localWorkspaceSyncRunnerState.js +7 -6
  6. package/dist/sync/engine/workspaceSyncEngineBrowserGatewayRemoteServices.js +2 -2
  7. package/dist/sync/engine/workspaceSyncEngineLocalApply.d.ts +1 -0
  8. package/dist/sync/engine/workspaceSyncEnginePushCompletion.d.ts +6 -0
  9. package/dist/sync/engine/workspaceSyncEnginePushCompletion.js +34 -1
  10. package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.js +2 -2
  11. package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +4 -1
  12. package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.d.ts +1 -0
  13. package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.js +53 -10
  14. package/dist/sync/syncService.d.ts +1 -0
  15. package/dist/sync/syncService.js +5 -2
  16. package/dist/ui/assets/{AiIdentityRosterCard-CDMPtnbC.js → AiIdentityRosterCard-Cfvb9Nuv.js} +1 -1
  17. package/dist/ui/assets/{AiProfilesModule-Cww85C4E.js → AiProfilesModule-0oR70eFG.js} +1 -1
  18. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-Dt9TKYSH.js → AnnotatedAttachmentWorkspace-C33iH_r_.js} +1 -1
  19. package/dist/ui/assets/{AssetTraySortControl-CKgapnxu.js → AssetTraySortControl-BAqxUtke.js} +1 -1
  20. package/dist/ui/assets/{ContextAttachmentManager-T-cEVcOZ.js → ContextAttachmentManager-BudIbOR9.js} +1 -1
  21. package/dist/ui/assets/{DocumentWorkspace-HD4MUjAZ.js → DocumentWorkspace-A8dFQyId.js} +1 -1
  22. package/dist/ui/assets/{EntityActivityTimeline-_V0zfMuh.js → EntityActivityTimeline-g7vtdsx1.js} +1 -1
  23. package/dist/ui/assets/{PlanningModule-mTkBygsB.js → PlanningModule-D-DeQs4F.js} +1 -1
  24. package/dist/ui/assets/{PlansPage-Btiq06VO.js → PlansPage-BWmfGi0Z.js} +1 -1
  25. package/dist/ui/assets/{TaskContextUpload-C00NVUpn.js → TaskContextUpload-BA_lQmS9.js} +1 -1
  26. package/dist/ui/assets/{TaskSettings-Cp-L-4xh.js → TaskSettings-DAlH4YIP.js} +1 -1
  27. package/dist/ui/assets/{TaskforceAgentsModule-Coq9-PUp.js → TaskforceAgentsModule-DmeCf6pJ.js} +1 -1
  28. package/dist/ui/assets/{WorkflowManagerModule-CuqRzMmM.js → WorkflowManagerModule-p08eDOaa.js} +1 -1
  29. package/dist/ui/assets/index-GHJgcW3j.js +7 -0
  30. package/dist/ui/index.html +1 -1
  31. package/package.json +1 -1
  32. package/dist/ui/assets/index-DSQINwt9.js +0 -7
@@ -437,6 +437,7 @@ export function registerLocalServiceRoutes(deps) {
437
437
  workspaceId: session.workspaceId,
438
438
  core: deps.core,
439
439
  serverName: 'taskforce-local-service',
440
+ version: context.appVersion,
440
441
  logStartup: false,
441
442
  runtimeMode: 'local',
442
443
  operationIdentityScope: {
@@ -23,6 +23,8 @@ interface RegisterWorkspacePushSyncRoutesArgs {
23
23
  coexistenceDirection?: 'cloud-push';
24
24
  actorUserId?: string;
25
25
  assertedActiveV3TaskMetadataIds?: ReadonlySet<string>;
26
+ assertedActiveV3TaskDeleteIds?: ReadonlySet<string>;
27
+ candidateTerminalV3TaskDeleteIds?: ReadonlySet<string>;
26
28
  assertedActiveV3TaskLifecycleIds?: ReadonlySet<string>;
27
29
  assertedActiveV3TaskReopenAssigneeIds?: ReadonlySet<string>;
28
30
  assertedActiveV3InitiativeIds?: ReadonlySet<string>;
@@ -31,6 +33,7 @@ interface RegisterWorkspacePushSyncRoutesArgs {
31
33
  result: any;
32
34
  deleteCount: number;
33
35
  emittedEventIds: string[];
36
+ settledTaskDeleteIds?: string[];
34
37
  }>;
35
38
  }
36
39
  export interface WorkspaceSyncPushAppliedTaskUpsert {
@@ -211,6 +211,9 @@ export function registerWorkspacePushSyncRoutes({ addRoute, core, context, audit
211
211
  code: 'SYNC_V2_PROJECTION_BOUNDARY_MISMATCH', statusCode: 409,
212
212
  });
213
213
  }
214
+ const candidateTerminalV3TaskDeleteIds = v2ExcludedProjections.includes('task/root-v1')
215
+ ? incomingTaskUpsertIdSet
216
+ : new Set();
214
217
  if (typeof process !== 'undefined' && process.env?.TASKFORCE_SYNC_KEY) {
215
218
  const syncKey = process.env.TASKFORCE_SYNC_KEY;
216
219
  const decryptedChanges = [];
@@ -267,11 +270,12 @@ export function registerWorkspacePushSyncRoutes({ addRoute, core, context, audit
267
270
  code: 'WORKSPACE_SYNC_V3_INITIATIVE_LEGACY_PUSH_DISABLED', statusCode: 409,
268
271
  });
269
272
  }
270
- const { result, deleteCount, emittedEventIds } = await dispatchWorkspaceSyncChanges(targetWorkspaceId, incomingChanges, {
273
+ const { result, deleteCount, emittedEventIds, settledTaskDeleteIds = [], } = await dispatchWorkspaceSyncChanges(targetWorkspaceId, incomingChanges, {
271
274
  allowReferenceNumberReassignment,
272
275
  coexistenceDirection: 'cloud-push',
273
276
  actorUserId: String(access.userId || '').trim(),
274
277
  assertedActiveV3TaskMetadataIds,
278
+ candidateTerminalV3TaskDeleteIds,
275
279
  assertedActiveV3TaskLifecycleIds,
276
280
  assertedActiveV3TaskReopenAssigneeIds,
277
281
  assertedActiveV3InitiativeIds,
@@ -281,8 +285,9 @@ export function registerWorkspacePushSyncRoutes({ addRoute, core, context, audit
281
285
  let appliedTaskUpserts;
282
286
  try {
283
287
  const durabilityStore = new WorkspaceSyncDurabilityStore(core.getDatabaseAdapter());
288
+ const settledTaskDeleteIdSet = new Set(settledTaskDeleteIds);
284
289
  appliedTaskUpserts = readWorkspaceSyncPushAppliedTaskUpserts({
285
- taskIds: incomingTaskUpsertIds,
290
+ taskIds: incomingTaskUpsertIds.filter((taskId) => !settledTaskDeleteIdSet.has(taskId)),
286
291
  getTask: (taskId) => core.getTask(taskId, targetWorkspaceId),
287
292
  listTaskRevisionProjections: (taskIds) => (durabilityStore.listTaskRevisionProjections({
288
293
  tenantId: core.getTenantId(),
@@ -306,6 +311,7 @@ export function registerWorkspacePushSyncRoutes({ addRoute, core, context, audit
306
311
  idempotencyKey: idempotencyKey || null,
307
312
  emittedEventIds,
308
313
  appliedTaskUpserts,
314
+ ...(settledTaskDeleteIds.length > 0 ? { settledTaskDeleteIds } : {}),
309
315
  result,
310
316
  suppressedV2ProjectionCounts,
311
317
  durationMs
@@ -82,6 +82,7 @@ export interface LocalWorkspaceSyncSettledV2PushBatch {
82
82
  task: WorkspaceSyncTaskSnapshot;
83
83
  durableRevision?: WorkspaceSyncDurableTaskRevision;
84
84
  }>;
85
+ settledTaskDeleteIds?: string[];
85
86
  }
86
87
  export interface LocalWorkspaceSyncPendingV2PushBatchProgress {
87
88
  totalBatches: number;
@@ -528,11 +528,7 @@ function isPendingV2Push(value) {
528
528
  && settledBatchOperationIdentities.every((identity) => /^[a-f0-9]{64}$/.test(identity))
529
529
  && settledBatchContributions !== null
530
530
  && settledBatchContributions.every((entry) => (isRecord(entry)
531
- && hasExactKeys(entry, [
532
- 'operationIdentity',
533
- 'settlementIdentityFingerprint',
534
- 'appliedTaskUpserts',
535
- ])
531
+ && hasRequiredAndAllowedKeys(entry, ['operationIdentity', 'settlementIdentityFingerprint', 'appliedTaskUpserts'], ['settledTaskDeleteIds'])
536
532
  && typeof entry.operationIdentity === 'string'
537
533
  && /^[a-f0-9]{64}$/.test(entry.operationIdentity)
538
534
  && typeof entry.settlementIdentityFingerprint === 'string'
@@ -545,7 +541,11 @@ function isPendingV2Push(value) {
545
541
  && isNonEmptyText(upsert.task.id)
546
542
  && isJson(upsert.task)
547
543
  && (upsert.durableRevision === undefined
548
- || (isRecord(upsert.durableRevision) && isJson(upsert.durableRevision)))))))
544
+ || (isRecord(upsert.durableRevision) && isJson(upsert.durableRevision)))))
545
+ && (entry.settledTaskDeleteIds === undefined
546
+ || (isStringArray(entry.settledTaskDeleteIds)
547
+ && new Set(entry.settledTaskDeleteIds).size
548
+ === entry.settledTaskDeleteIds.length))))
549
549
  && new Set(settledBatchContributions.map((entry) => entry.operationIdentity)).size === settledBatchContributions.length
550
550
  && settledBatchContributions.every((entry) => (settledBatchOperationIdentities.includes(entry.operationIdentity))));
551
551
  const settlementContributionsMatchPlan = hasLegacyShape || (preparedPlan !== null
@@ -563,6 +563,7 @@ function isPendingV2Push(value) {
563
563
  operationIdentity: entry.operationIdentity,
564
564
  changes,
565
565
  appliedTaskUpserts: entry.appliedTaskUpserts,
566
+ settledTaskDeleteIds: entry.settledTaskDeleteIds,
566
567
  excludedProjections: preparedPlan.ownership.combinedExcludedProjections,
567
568
  })) === entry.settlementIdentityFingerprint;
568
569
  }
@@ -2,7 +2,7 @@ import { workspaceSyncGatewayOperationClassFor, workspaceSyncGatewayTimeoutMs, }
2
2
  import { canonicalWorkspaceSyncJson } from '../v3/canonicalWorkspaceSyncJson.js';
3
3
  import { WORKSPACE_SYNC_V3_COMBINED_COVERAGE_SELECTOR, resolveWorkspaceSyncV3CombinedCoverageForExclusions, } from '../v3/workspaceSyncV3CombinedWireContract.js';
4
4
  import { WORKSPACE_SYNC_ENGINE_TRANSFER_COMPLETION_VERSION, } from './workspaceSyncEngineTransferCompletion.js';
5
- import { parseWorkspaceSyncEngineAppliedTaskUpserts, parseWorkspaceSyncEngineV2PushBoundary, workspaceSyncEngineExpectedTaskEchoIds, } from './workspaceSyncEnginePushCompletion.js';
5
+ import { parseWorkspaceSyncEnginePushTaskSettlement, parseWorkspaceSyncEngineV2PushBoundary, workspaceSyncEngineExpectedTaskEchoIds, } from './workspaceSyncEnginePushCompletion.js';
6
6
  const PERMIT_DEADLINE_SLACK_MS = 5_000;
7
7
  function isPlainObject(value) {
8
8
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
@@ -388,7 +388,7 @@ export function createBrowserGatewayWorkspaceSyncEngineRemoteServices(options) {
388
388
  requireAcceptedJsonObject: true,
389
389
  validateAcceptedData: (value) => {
390
390
  try {
391
- parseWorkspaceSyncEngineAppliedTaskUpserts(value, expectedTaskEchoIds);
391
+ parseWorkspaceSyncEnginePushTaskSettlement(value, expectedTaskEchoIds);
392
392
  return true;
393
393
  }
394
394
  catch {
@@ -11,6 +11,7 @@ export interface WorkspaceSyncEngineLocalApplyResult {
11
11
  result: unknown;
12
12
  deleteCount: number;
13
13
  emittedEventIds: string[];
14
+ settledTaskDeleteIds?: string[];
14
15
  }
15
16
  export interface WorkspaceSyncEngineLocalContentReader {
16
17
  loadDocument(workspaceId: string, path: string): Promise<{
@@ -19,10 +19,15 @@ export interface WorkspaceSyncEngineAppliedTaskUpsert {
19
19
  }
20
20
  export declare function workspaceSyncEngineExpectedTaskEchoIds(changes: readonly WorkspaceSyncChange[]): string[];
21
21
  export declare function parseWorkspaceSyncEngineAppliedTaskUpserts(value: unknown, expectedTaskIds: readonly string[]): WorkspaceSyncEngineAppliedTaskUpsert[];
22
+ export declare function parseWorkspaceSyncEnginePushTaskSettlement(value: unknown, expectedTaskIds: readonly string[]): {
23
+ appliedTaskUpserts: WorkspaceSyncEngineAppliedTaskUpsert[];
24
+ settledTaskDeleteIds: string[];
25
+ };
22
26
  export declare function createWorkspaceSyncEngineV2SettlementIdentityProjection(input: {
23
27
  operationIdentity: string;
24
28
  changes: readonly WorkspaceSyncChange[];
25
29
  appliedTaskUpserts: readonly WorkspaceSyncEngineAppliedTaskUpsert[];
30
+ settledTaskDeleteIds?: readonly string[];
26
31
  excludedProjections?: readonly WorkspaceSyncV2ProjectionExclusion[];
27
32
  }): {
28
33
  version: 1;
@@ -31,4 +36,5 @@ export declare function createWorkspaceSyncEngineV2SettlementIdentityProjection(
31
36
  archived: boolean;
32
37
  task: Record<string, unknown>;
33
38
  }>;
39
+ settledTaskDeleteIds?: string[];
34
40
  };
@@ -86,6 +86,28 @@ export function parseWorkspaceSyncEngineAppliedTaskUpserts(value, expectedTaskId
86
86
  }
87
87
  return normalized;
88
88
  }
89
+ export function parseWorkspaceSyncEnginePushTaskSettlement(value, expectedTaskIds) {
90
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
91
+ throw new TypeError('The V2 push authoritative echo response is invalid.');
92
+ }
93
+ const source = value;
94
+ const settledTaskDeleteIds = source.settledTaskDeleteIds === undefined
95
+ ? []
96
+ : source.settledTaskDeleteIds;
97
+ if (!Array.isArray(settledTaskDeleteIds)
98
+ || settledTaskDeleteIds.some((taskId) => (typeof taskId !== 'string'
99
+ || taskId.trim().length === 0
100
+ || taskId !== taskId.trim()))
101
+ || new Set(settledTaskDeleteIds).size !== settledTaskDeleteIds.length
102
+ || settledTaskDeleteIds.some((taskId) => !expectedTaskIds.includes(taskId))) {
103
+ throw new TypeError('The V2 push terminal task settlement identities are invalid.');
104
+ }
105
+ const settledIds = new Set(settledTaskDeleteIds);
106
+ return {
107
+ appliedTaskUpserts: parseWorkspaceSyncEngineAppliedTaskUpserts(value, expectedTaskIds.filter((taskId) => !settledIds.has(taskId))),
108
+ settledTaskDeleteIds: [...settledTaskDeleteIds],
109
+ };
110
+ }
89
111
  function taskSettlementProjection(task, preserveReferenceNumber) {
90
112
  const { updatedAt: _updatedAt, ...stableTask } = structuredClone(task);
91
113
  if (!preserveReferenceNumber)
@@ -136,7 +158,17 @@ export function createWorkspaceSyncEngineV2SettlementIdentityProjection(input) {
136
158
  if (!operationIdentity || operationIdentity !== input.operationIdentity) {
137
159
  throw new TypeError('The V2 push settlement operation identity is invalid.');
138
160
  }
139
- const expectedTaskUpserts = input.changes.filter((change) => change.op === 'upsert');
161
+ const allExpectedTaskUpserts = input.changes.filter((change) => change.op === 'upsert');
162
+ const expectedTaskIds = allExpectedTaskUpserts.map((change) => String(change.task.id || '').trim());
163
+ const settledTaskDeleteIds = [...(input.settledTaskDeleteIds || [])];
164
+ if ((settledTaskDeleteIds.length > 0
165
+ && !input.excludedProjections?.includes('task/root-v1'))
166
+ || settledTaskDeleteIds.some((taskId) => !expectedTaskIds.includes(taskId))
167
+ || new Set(settledTaskDeleteIds).size !== settledTaskDeleteIds.length) {
168
+ throw new TypeError('The V2 push terminal task settlement identities are invalid.');
169
+ }
170
+ const settledIds = new Set(settledTaskDeleteIds);
171
+ const expectedTaskUpserts = allExpectedTaskUpserts.filter((change) => !settledIds.has(String(change.task.id || '').trim()));
140
172
  if (expectedTaskUpserts.length !== input.appliedTaskUpserts.length) {
141
173
  throw new TypeError('The V2 push authoritative settlement projection is incomplete.');
142
174
  }
@@ -167,5 +199,6 @@ export function createWorkspaceSyncEngineV2SettlementIdentityProjection(input) {
167
199
  version: 1,
168
200
  operationIdentity,
169
201
  taskUpserts: expectedProjection,
202
+ ...(settledTaskDeleteIds.length > 0 ? { settledTaskDeleteIds } : {}),
170
203
  };
171
204
  }
@@ -2,7 +2,7 @@ import { workspaceSyncGatewayOperationClassFor, workspaceSyncGatewayTimeoutMs, }
2
2
  import { canonicalWorkspaceSyncJson } from '../v3/canonicalWorkspaceSyncJson.js';
3
3
  import { WORKSPACE_SYNC_V3_COMBINED_COVERAGE_SELECTOR, resolveWorkspaceSyncV3CombinedCoverageForExclusions, } from '../v3/workspaceSyncV3CombinedWireContract.js';
4
4
  import { WORKSPACE_SYNC_ENGINE_TRANSFER_COMPLETION_VERSION, } from './workspaceSyncEngineTransferCompletion.js';
5
- import { parseWorkspaceSyncEngineAppliedTaskUpserts, parseWorkspaceSyncEngineV2PushBoundary, workspaceSyncEngineExpectedTaskEchoIds, } from './workspaceSyncEnginePushCompletion.js';
5
+ import { parseWorkspaceSyncEnginePushTaskSettlement, parseWorkspaceSyncEngineV2PushBoundary, workspaceSyncEngineExpectedTaskEchoIds, } from './workspaceSyncEnginePushCompletion.js';
6
6
  const PERMIT_DEADLINE_SLACK_MS = 5_000;
7
7
  function isPlainObject(value) {
8
8
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
@@ -311,7 +311,7 @@ export function createServerWorkspaceSyncEngineRemoteServices(options) {
311
311
  requireAcceptedJsonObject: true,
312
312
  validateAcceptedData: (value) => {
313
313
  try {
314
- parseWorkspaceSyncEngineAppliedTaskUpserts(value, expectedTaskEchoIds);
314
+ parseWorkspaceSyncEnginePushTaskSettlement(value, expectedTaskEchoIds);
315
315
  return true;
316
316
  }
317
317
  catch {
@@ -623,7 +623,7 @@ export function createWorkspaceSyncEngineServerRunnerOperations(options) {
623
623
  acknowledgedOperationIdentities.length;
624
624
  }
625
625
  },
626
- onAcceptedBatchSettled: async ({ operationIdentity, settlementIdentityFingerprint, appliedTaskUpserts, transferReceipt, pushBoundary, excludedProjections, }) => {
626
+ onAcceptedBatchSettled: async ({ operationIdentity, settlementIdentityFingerprint, appliedTaskUpserts, settledTaskDeleteIds, transferReceipt, pushBoundary, excludedProjections, }) => {
627
627
  const pendingV2Push = checkpoint.pendingV2Push;
628
628
  if (!pendingV2Push
629
629
  || pendingV2Push.settlementVersion !== 1
@@ -661,6 +661,9 @@ export function createWorkspaceSyncEngineServerRunnerOperations(options) {
661
661
  operationIdentity,
662
662
  settlementIdentityFingerprint,
663
663
  appliedTaskUpserts: structuredClone(appliedTaskUpserts),
664
+ ...(settledTaskDeleteIds.length > 0 ? {
665
+ settledTaskDeleteIds: [...settledTaskDeleteIds],
666
+ } : {}),
664
667
  },
665
668
  ],
666
669
  },
@@ -17,6 +17,7 @@ export interface WorkspaceSyncV2DispatchOptions {
17
17
  coexistenceDirection?: TaskMetadataCoexistenceDirection;
18
18
  assertedActiveV3TaskMetadataIds?: ReadonlySet<string>;
19
19
  assertedActiveV3TaskDeleteIds?: ReadonlySet<string>;
20
+ candidateTerminalV3TaskDeleteIds?: ReadonlySet<string>;
20
21
  assertedActiveV3TaskLifecycleIds?: ReadonlySet<string>;
21
22
  assertedActiveV3TaskReopenAssigneeIds?: ReadonlySet<string>;
22
23
  assertedActiveV3Taxonomy?: boolean;
@@ -156,11 +156,40 @@ export function createWorkspaceSyncV2ChangeDispatcher(dependencies) {
156
156
  throw createSyncRouteError('Cloud pull contained content from an excluded v2 projection.', { excludedProjections: Array.from(v2ExcludedProjections) }, { code: 'SYNC_V2_EXCLUDED_PROJECTION_PRESENT', statusCode: 409 });
157
157
  }
158
158
  const taskMutationsForApply = prepareWorkspaceSyncV2TaskCarriers(applyModel.taskMutations, v2ExcludedProjections, (taskId) => core.getTask(taskId, workspaceId));
159
- if (v2ExcludedProjections.has('task/root-v1')) {
159
+ if (v2ExcludedProjections.has('task/root-v1')
160
+ && !(options.coexistenceDirection === 'cloud-push'
161
+ && options.candidateTerminalV3TaskDeleteIds?.size)) {
160
162
  assertWorkspaceSyncV2TaskCarrierRootsExist(taskMutationsForApply, (taskId) => Boolean(core.getTask(taskId, workspaceId)));
161
163
  }
162
164
  return { applyModel, taskMutationsForApply, v2ExcludedProjections };
163
165
  };
166
+ const resolveTaskCarrierSettlement = (workspaceId, taskMutations, ownership, options) => {
167
+ const candidates = options.coexistenceDirection === 'cloud-push'
168
+ ? options.candidateTerminalV3TaskDeleteIds
169
+ : undefined;
170
+ if (!candidates?.size) {
171
+ return { taskMutationsForSettlement: taskMutations, settledTaskDeleteIds: [] };
172
+ }
173
+ const terminalOwnership = new Set(ownership
174
+ .filter((entry) => entry.activeDeleteOperation === true || entry.deleteOwned === true)
175
+ .map((entry) => entry.taskId));
176
+ const settledTaskDeleteIds = candidates
177
+ ? taskMutations.upserts
178
+ .map((mutation) => mutation.task.id)
179
+ .filter((taskId) => (candidates.has(taskId)
180
+ && terminalOwnership.has(taskId)
181
+ && !core.getTask(taskId, workspaceId)))
182
+ : [];
183
+ const settledIds = new Set(settledTaskDeleteIds);
184
+ const taskMutationsForSettlement = settledIds.size > 0
185
+ ? {
186
+ ...taskMutations,
187
+ upserts: taskMutations.upserts.filter((mutation) => !settledIds.has(mutation.task.id)),
188
+ }
189
+ : taskMutations;
190
+ assertWorkspaceSyncV2TaskCarrierRootsExist(taskMutationsForSettlement, (taskId) => Boolean(core.getTask(taskId, workspaceId)));
191
+ return { taskMutationsForSettlement, settledTaskDeleteIds };
192
+ };
164
193
  const prepareTaskSnapshotForDispatch = (workspaceId, taskMutationsForApply, v2ExcludedProjections, options) => {
165
194
  const prepared = prepareTaskSyncSnapshotApply({
166
195
  core,
@@ -335,14 +364,15 @@ export function createWorkspaceSyncV2ChangeDispatcher(dependencies) {
335
364
  if (options.coexistenceDirection === 'cloud-push'
336
365
  && taskMutationsForApply.upserts.some((mutation) => mutation.durableRevision))
337
366
  throw new Error('Task durable revisions are only accepted from the cloud pull feed.');
367
+ const { taskMutationsForSettlement, settledTaskDeleteIds, } = resolveTaskCarrierSettlement(workspaceId, taskMutationsForApply, ownership, options);
338
368
  const durablePull = options.coexistenceDirection === 'local-pull'
339
369
  ? prepareDurableTaskPullMutations({
340
370
  store: durabilityStore,
341
371
  tenantId: core.getTenantId(),
342
372
  workspaceId,
343
- taskMutations: taskMutationsForApply,
373
+ taskMutations: taskMutationsForSettlement,
344
374
  })
345
- : { taskMutations: taskMutationsForApply, pendingRevisions: [] };
375
+ : { taskMutations: taskMutationsForSettlement, pendingRevisions: [] };
346
376
  const prepared = prepareTaskSyncSnapshotApply({
347
377
  core,
348
378
  workspaceId,
@@ -361,7 +391,7 @@ export function createWorkspaceSyncV2ChangeDispatcher(dependencies) {
361
391
  ownership: durabilityStore.listTaskTaxonomyProjectionOwnership({
362
392
  tenantId: core.getTenantId(),
363
393
  workspaceId,
364
- taskIds: taskMutationsForApply.upserts.map((mutation) => mutation.task.id),
394
+ taskIds: taskMutationsForSettlement.upserts.map((mutation) => mutation.task.id),
365
395
  }),
366
396
  }),
367
397
  v3OwnedTaskDeleteIds: resolveV3OwnedTaskDeleteIds({
@@ -396,7 +426,7 @@ export function createWorkspaceSyncV2ChangeDispatcher(dependencies) {
396
426
  revisions: durablePull.pendingRevisions,
397
427
  now: revisionNow,
398
428
  });
399
- return { prepared, result };
429
+ return { prepared, result, settledTaskDeleteIds };
400
430
  },
401
431
  });
402
432
  })()
@@ -407,6 +437,7 @@ export function createWorkspaceSyncV2ChangeDispatcher(dependencies) {
407
437
  workspaceId,
408
438
  prepared: preparedTaskSnapshot,
409
439
  }),
440
+ settledTaskDeleteIds: [],
410
441
  };
411
442
  const result = finalPreparedTaskSnapshot.result;
412
443
  const committedTaskIds = finalPreparedTaskSnapshot.result.changedTaskIds || [];
@@ -480,7 +511,14 @@ export function createWorkspaceSyncV2ChangeDispatcher(dependencies) {
480
511
  recordSyncDiagnostic,
481
512
  createSyncRouteError,
482
513
  });
483
- return { result, deleteCount, emittedEventIds: Array.from(emittedEventIds) };
514
+ return {
515
+ result,
516
+ deleteCount,
517
+ emittedEventIds: Array.from(emittedEventIds),
518
+ ...(finalPreparedTaskSnapshot.settledTaskDeleteIds.length > 0 ? {
519
+ settledTaskDeleteIds: finalPreparedTaskSnapshot.settledTaskDeleteIds,
520
+ } : {}),
521
+ };
484
522
  }
485
523
  finally {
486
524
  unsubscribeRealtime();
@@ -796,14 +834,15 @@ export function createWorkspaceSyncV2ChangeDispatcher(dependencies) {
796
834
  if (options.coexistenceDirection === 'cloud-push'
797
835
  && taskMutationsForApply.upserts.some((mutation) => mutation.durableRevision))
798
836
  throw new Error('Task durable revisions are only accepted from the cloud pull feed.');
837
+ const { taskMutationsForSettlement, settledTaskDeleteIds, } = resolveTaskCarrierSettlement(workspaceId, taskMutationsForApply, ownership, options);
799
838
  const durablePull = options.coexistenceDirection === 'local-pull'
800
839
  ? prepareDurableTaskPullMutations({
801
840
  store: durabilityStore,
802
841
  tenantId: core.getTenantId(),
803
842
  workspaceId,
804
- taskMutations: taskMutationsForApply,
843
+ taskMutations: taskMutationsForSettlement,
805
844
  })
806
- : { taskMutations: taskMutationsForApply, pendingRevisions: [] };
845
+ : { taskMutations: taskMutationsForSettlement, pendingRevisions: [] };
807
846
  const prepared = prepareTaskSyncSnapshotApply({
808
847
  core,
809
848
  workspaceId,
@@ -822,7 +861,7 @@ export function createWorkspaceSyncV2ChangeDispatcher(dependencies) {
822
861
  ownership: durabilityStore.listTaskTaxonomyProjectionOwnership({
823
862
  tenantId: core.getTenantId(),
824
863
  workspaceId,
825
- taskIds: taskMutationsForApply.upserts.map((mutation) => mutation.task.id),
864
+ taskIds: taskMutationsForSettlement.upserts.map((mutation) => mutation.task.id),
826
865
  }),
827
866
  }),
828
867
  v3OwnedTaskDeleteIds: resolveV3OwnedTaskDeleteIds({
@@ -857,7 +896,7 @@ export function createWorkspaceSyncV2ChangeDispatcher(dependencies) {
857
896
  revisions: durablePull.pendingRevisions,
858
897
  now: revisionNow,
859
898
  });
860
- return { prepared, result };
899
+ return { prepared, result, settledTaskDeleteIds };
861
900
  },
862
901
  });
863
902
  })()
@@ -869,6 +908,7 @@ export function createWorkspaceSyncV2ChangeDispatcher(dependencies) {
869
908
  prepared: preparedTaskSnapshot,
870
909
  deferSideEffects: true,
871
910
  }),
911
+ settledTaskDeleteIds: [],
872
912
  };
873
913
  const protectedTaskCount = finalPreparedTaskSnapshot.prepared.metadataPreservedTaskIds.length;
874
914
  const protectedLifecycleTaskCount = finalPreparedTaskSnapshot.prepared.lifecyclePreservedTaskIds.length;
@@ -934,6 +974,9 @@ export function createWorkspaceSyncV2ChangeDispatcher(dependencies) {
934
974
  result: finalPreparedTaskSnapshot.result,
935
975
  deleteCount,
936
976
  emittedEventIds: [],
977
+ ...(finalPreparedTaskSnapshot.settledTaskDeleteIds.length > 0 ? {
978
+ settledTaskDeleteIds: finalPreparedTaskSnapshot.settledTaskDeleteIds,
979
+ } : {}),
937
980
  };
938
981
  const beforeCommitResult = options.transactionHooks?.beforeCommit?.(result);
939
982
  assertSynchronousTransactionResult(beforeCommitResult);
@@ -72,6 +72,7 @@ export interface WorkspacePushSyncInput {
72
72
  operationIdentity: string;
73
73
  settlementIdentityFingerprint: string;
74
74
  appliedTaskUpserts: WorkspacePushSyncResult['appliedTaskUpserts'];
75
+ settledTaskDeleteIds: string[];
75
76
  transferReceipt: WorkspaceSyncEngineRemoteTransferReceipt;
76
77
  pushBoundary: WorkspaceSyncEngineV2PushBoundary;
77
78
  excludedProjections?: readonly WorkspaceSyncV2ProjectionExclusion[];
@@ -1,7 +1,7 @@
1
1
  import { applyLocalWorkspaceChanges, getLocalWorkspaceContentPullObligations, recordLocalWorkspaceContentPullObligationFailures, getLocalWorkspaceContentManifest, pullUserSettings, pushUserSettings, prepareWorkspacePushChanges, } from './cloudSyncApi.js';
2
2
  import { workspaceSyncContentPullObligationId, } from './engine/workspaceSyncContentPullObligationStore.js';
3
3
  import { createBrowserWorkspaceSyncEngineRemoteServices, } from './engine/workspaceSyncEngineRemoteServices.js';
4
- import { createWorkspaceSyncEngineV2SettlementIdentityProjection, createWorkspaceSyncEngineV2PushBoundary, parseWorkspaceSyncEngineAppliedTaskUpserts, workspaceSyncEngineExpectedTaskEchoIds, } from './engine/workspaceSyncEnginePushCompletion.js';
4
+ import { createWorkspaceSyncEngineV2SettlementIdentityProjection, createWorkspaceSyncEngineV2PushBoundary, parseWorkspaceSyncEnginePushTaskSettlement, workspaceSyncEngineExpectedTaskEchoIds, } from './engine/workspaceSyncEnginePushCompletion.js';
5
5
  import { encryptDocument } from './encryptionService.js';
6
6
  import { decryptWorkspaceSyncContentChangeInPlace } from './workspaceSyncContentDecryption.js';
7
7
  import { decideWorkspaceContentFetch, fingerprintWorkspaceContentBody, } from './workspaceContentManifest.js';
@@ -835,7 +835,8 @@ export async function runWorkspacePushSyncService(input) {
835
835
  emittedEventIds.add(eventId);
836
836
  }
837
837
  }
838
- const normalizedTaskUpserts = parseWorkspaceSyncEngineAppliedTaskUpserts(pushData, expectedTaskEchoIds);
838
+ const taskSettlement = parseWorkspaceSyncEnginePushTaskSettlement(pushData, expectedTaskEchoIds);
839
+ const normalizedTaskUpserts = taskSettlement.appliedTaskUpserts;
839
840
  appliedTaskUpserts.push(...normalizedTaskUpserts);
840
841
  if (input.onAcceptedBatchSettled) {
841
842
  if (!pushResult.transferReceipt) {
@@ -847,9 +848,11 @@ export async function runWorkspacePushSyncService(input) {
847
848
  operationIdentity: idempotencyKey,
848
849
  changes: plainBatchChanges,
849
850
  appliedTaskUpserts: normalizedTaskUpserts,
851
+ settledTaskDeleteIds: taskSettlement.settledTaskDeleteIds,
850
852
  excludedProjections: effectiveV2ExcludedProjections,
851
853
  })),
852
854
  appliedTaskUpserts: normalizedTaskUpserts,
855
+ settledTaskDeleteIds: taskSettlement.settledTaskDeleteIds,
853
856
  transferReceipt: pushResult.transferReceipt,
854
857
  pushBoundary,
855
858
  excludedProjections: effectiveV2ExcludedProjections,
@@ -1 +1 @@
1
- import{j as i}from"./vendor-react-CKJs5o3c.js";import{t as r}from"./index-DSQINwt9.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
+ 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-DSQINwt9.js";import{A as sa,b as na,r as Ce}from"./AiIdentityRosterCard-CDMPtnbC.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};