@tutti-os/agent-gui 0.0.147 → 0.0.149

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 CHANGED
@@ -220,8 +220,9 @@ targets from presentation metadata.
220
220
 
221
221
  Agent names, primary icons, optional conversation-mask icons, and optional
222
222
  home-carousel artwork come from `agents[].name`, `agents[].iconUrl`,
223
- `agents[].maskIconUrl`, and `agents[].heroImageUrl`. All identity surfaces use
224
- `iconUrl`; conversation rows use `maskIconUrl` where present.
223
+ `agents[].maskIconUrl`, and `agents[].heroImageUrl`. Hosts must pass fully
224
+ resolved presentation URLs from their authoritative directory. All identity
225
+ surfaces use `iconUrl`; conversation rows use `maskIconUrl` where present.
225
226
  `owner.avatarUrl` is rendered separately as an ownership badge. Invalid entries
226
227
  and duplicate `agentTargetId` values are discarded by
227
228
  `normalizeAgentGUIAgents`, with the first occurrence preserving host order.
@@ -234,12 +235,10 @@ directly. With multiple agents, it shows `All` plus the host-ordered agent rail
234
235
  and empty-home carousel. Hosts may customize the aggregate icon with
235
236
  `allAgentsPresentation.iconUrl`.
236
237
 
237
- Hosts adapting daemon-owned agent targets must resolve the target's descriptor
238
- `iconKey` instead of assuming it equals the provider ID. The narrow
239
- `@tutti-os/agent-gui/provider-icons` subpath exports
240
- `resolveProviderIconAsset(iconKey, variant)` for that adapter seam. Unknown
241
- keys return `null`; hosts should render a neutral icon rather than silently
242
- substituting another provider's icon.
238
+ Daemon-owned system targets seed and refresh their `iconUrl` and `maskIconUrl`
239
+ from the target descriptor `iconKey`. AgentGUI consumers must not synthesize
240
+ built-in target icons from provider IDs or `iconKey`; stale or missing directory
241
+ presentation should be fixed in the source directory.
243
242
 
244
243
  Hosts that need provider identity presentation may call
245
244
  `resolveAgentGUIProviderIdentity(value)` from the narrow
@@ -176,6 +176,8 @@ interface WorkspaceAgentMessageCenterCardProps {
176
176
  interactive?: boolean;
177
177
  isSubmitting: boolean;
178
178
  lazySummary?: boolean;
179
+ promptVariant?: "compact" | "full";
180
+ showSummaryWithPrompt?: boolean;
179
181
  summaryAccessory?: ReactNode;
180
182
  onLinkAction?: (action: WorkspaceLinkAction) => void;
181
183
  onOpenChat: (input: {
@@ -471,14 +473,42 @@ declare function useEngineSelector<TState, TSelected>(engine: EngineStateStore<T
471
473
  declare function buildWorkspaceAgentMessageCenterModelFromEngine(presentation: WorkspaceAgentMessageCenterPresentation, snapshot: Pick<AgentActivitySnapshot, "sessionMessagesById" | "workspaceId">, options?: BuildWorkspaceAgentMessageCenterOptions): WorkspaceAgentMessageCenterModel;
472
474
  interface WorkspaceAgentMessageCenterPresentation {
473
475
  consumers: readonly WorkspaceAgentConsumerSession[];
476
+ dismissedPlanTurnKeys: Readonly<Record<string, true>>;
474
477
  promptStatusByKey: Readonly<Record<string, WorkspaceAgentMessageCenterPromptStatus>>;
475
478
  }
476
479
  declare function selectWorkspaceAgentMessageCenterPresentation(state: AgentSessionEngineState): WorkspaceAgentMessageCenterPresentation;
477
480
  declare function workspaceAgentMessageCenterPresentationEqual(left: WorkspaceAgentMessageCenterPresentation, right: WorkspaceAgentMessageCenterPresentation): boolean;
481
+ type WorkspaceAgentAttentionTarget = {
482
+ kind: "interaction";
483
+ workspaceId: string;
484
+ agentSessionId: string;
485
+ turnId: string;
486
+ requestId: string;
487
+ } | {
488
+ kind: "plan-implementation";
489
+ workspaceId: string;
490
+ agentSessionId: string;
491
+ turnId: string;
492
+ requestId: string;
493
+ };
494
+ interface WorkspaceAgentAttentionItem {
495
+ item: WorkspaceAgentMessageCenterItem;
496
+ status: WorkspaceAgentMessageCenterPromptStatus;
497
+ target: WorkspaceAgentAttentionTarget;
498
+ }
499
+ /**
500
+ * Projects every canonical actionable target instead of collapsing a root
501
+ * conversation to its latest interaction. The display item keeps the root
502
+ * conversation identity for navigation while target keeps the exact child
503
+ * session/turn/request identity used by the command.
504
+ */
505
+ declare function selectWorkspaceAgentAttentionItems(presentation: WorkspaceAgentMessageCenterPresentation, snapshot: Pick<AgentActivitySnapshot, "sessionMessagesById" | "workspaceId">, options?: BuildWorkspaceAgentMessageCenterOptions): WorkspaceAgentAttentionItem[];
506
+ declare function workspaceAgentAttentionItemsEqual(left: readonly WorkspaceAgentAttentionItem[], right: readonly WorkspaceAgentAttentionItem[]): boolean;
478
507
  type WorkspaceAgentMessageCenterPromptStatus = "idle" | "responding" | "unknown" | "failed";
479
508
  declare function workspaceAgentMessageCenterPromptStatus(presentation: WorkspaceAgentMessageCenterPresentation, item: Pick<WorkspaceAgentMessageCenterItem, "agentSessionId" | "pendingInteractionTarget" | "pendingPrompt">): WorkspaceAgentMessageCenterPromptStatus;
480
509
 
481
510
  declare function stabilizeWorkspaceAgentMessageCenterModel(previous: WorkspaceAgentMessageCenterModel | null, next: WorkspaceAgentMessageCenterModel): WorkspaceAgentMessageCenterModel;
511
+ declare function workspaceAgentMessageCenterItemEqual(left: WorkspaceAgentMessageCenterItem, right: WorkspaceAgentMessageCenterItem): boolean;
482
512
 
483
513
  declare const messageCenterFiltersStorageKey = "tutti.agent-message-center.filters";
484
514
  interface MessageCenterFilterPreferences {
@@ -489,4 +519,4 @@ interface MessageCenterFilterPreferences {
489
519
  declare function readMessageCenterFilterPreferences(): MessageCenterFilterPreferences;
490
520
  declare function writeMessageCenterFilterPreferences(preferences: MessageCenterFilterPreferences): void;
491
521
 
492
- export { AgentInteractivePromptSurface, AgentVerticalScrollArea, type BuildWorkspaceAgentMessageCenterOptions, type MessageCenterAgentUserStack, type MessageCenterFilterPreferences, type MessageCenterGroup, type MessageCenterGroupBy, MessageCenterGroupHeading, MessageCenterIdentityAvatarMark, MessageCenterIdentityAvatarStack, MessageCenterIdentityLabel, MessageCenterOpenChatButton, type MessageCenterProviderOption, type MessageCenterStatusFilter, type MessageCenterStatusOption, type MessageCenterStatusTone, MessageCenterSummary, type MessageCenterTranslate, PLAN_IMPLEMENTATION_ACTION_IMPLEMENT, PLAN_IMPLEMENTATION_PROMPT, type PromptToolDetail, type WorkspaceAgentMessageCenterAgentPresentation, WorkspaceAgentMessageCenterAttentionDeck, type WorkspaceAgentMessageCenterAttentionDeckProps, type WorkspaceAgentMessageCenterAttentionDeckRenderCardInput, WorkspaceAgentMessageCenterCard, type WorkspaceAgentMessageCenterCardProps, type WorkspaceAgentMessageCenterCounts, type WorkspaceAgentMessageCenterDigest, type WorkspaceAgentMessageCenterDigestPrimary, type WorkspaceAgentMessageCenterDigestPrimaryKind, type WorkspaceAgentMessageCenterIdentity, type WorkspaceAgentMessageCenterInteractionTarget, type WorkspaceAgentMessageCenterItem, type WorkspaceAgentMessageCenterModel, WorkspaceAgentMessageCenterPanel, type WorkspaceAgentMessageCenterPanelPresentation, type WorkspaceAgentMessageCenterPanelProps, type WorkspaceAgentMessageCenterPresentation, type WorkspaceAgentMessageCenterPromptStatus, WorkspaceAgentMessageCenterStack, MessageCenterViewMenu as WorkspaceAgentMessageCenterViewMenu, approvalOptionDisplayLabel, buildMessageCenterProviderOptions, buildMessageCenterStatusOptions, buildWorkspaceAgentInteractivePromptLabels, buildWorkspaceAgentMessageCenterModelFromEngine, dispatchAgentPlanPromptAction, getPromptToolDetails, groupMessageCenterItems, isInteractiveMessageCenterItem, isPromptRequestIdTitle, isRecentlyCompletedMessageCenterItem, isWaitingMessageCenterItem, itemMatchesViewFilters, managedAgentRoundedIconUrl, messageCenterAgentUserStackId, messageCenterFiltersStorageKey, messageCenterGroupLabel, messageCenterStackPreviewNodes, messageCenterStackPreviewText, messageCenterStackRenderId, messageCenterStackScrollSyncSegment, messageCenterStatusFilterValue, messageCenterStatusTone, messageCenterStatusToneClass, partitionMessageCenterItemsByAgentUser, readMessageCenterFilterPreferences, resolveMessageCenterNotificationAction, selectMessageCenterAttentionDeckItems, selectWorkspaceAgentMessageCenterPresentation, stabilizeWorkspaceAgentMessageCenterModel, statusFilterSummary, useEngineSelector, userAvatarPlaceholderUrl, workspaceAgentActivityStatusLabel, workspaceAgentMessageCenterPresentationEqual, workspaceAgentMessageCenterPromptStatus, writeMessageCenterFilterPreferences };
522
+ export { AgentInteractivePromptSurface, AgentVerticalScrollArea, type BuildWorkspaceAgentMessageCenterOptions, type MessageCenterAgentUserStack, type MessageCenterFilterPreferences, type MessageCenterGroup, type MessageCenterGroupBy, MessageCenterGroupHeading, MessageCenterIdentityAvatarMark, MessageCenterIdentityAvatarStack, MessageCenterIdentityLabel, MessageCenterOpenChatButton, type MessageCenterProviderOption, type MessageCenterStatusFilter, type MessageCenterStatusOption, type MessageCenterStatusTone, MessageCenterSummary, type MessageCenterTranslate, PLAN_IMPLEMENTATION_ACTION_IMPLEMENT, PLAN_IMPLEMENTATION_PROMPT, type PromptToolDetail, type WorkspaceAgentAttentionItem, type WorkspaceAgentAttentionTarget, type WorkspaceAgentMessageCenterAgentPresentation, WorkspaceAgentMessageCenterAttentionDeck, type WorkspaceAgentMessageCenterAttentionDeckProps, type WorkspaceAgentMessageCenterAttentionDeckRenderCardInput, WorkspaceAgentMessageCenterCard, type WorkspaceAgentMessageCenterCardProps, type WorkspaceAgentMessageCenterCounts, type WorkspaceAgentMessageCenterDigest, type WorkspaceAgentMessageCenterDigestPrimary, type WorkspaceAgentMessageCenterDigestPrimaryKind, type WorkspaceAgentMessageCenterIdentity, type WorkspaceAgentMessageCenterInteractionTarget, type WorkspaceAgentMessageCenterItem, type WorkspaceAgentMessageCenterModel, WorkspaceAgentMessageCenterPanel, type WorkspaceAgentMessageCenterPanelPresentation, type WorkspaceAgentMessageCenterPanelProps, type WorkspaceAgentMessageCenterPresentation, type WorkspaceAgentMessageCenterPromptStatus, WorkspaceAgentMessageCenterStack, MessageCenterViewMenu as WorkspaceAgentMessageCenterViewMenu, approvalOptionDisplayLabel, buildMessageCenterProviderOptions, buildMessageCenterStatusOptions, buildWorkspaceAgentInteractivePromptLabels, buildWorkspaceAgentMessageCenterModelFromEngine, dispatchAgentPlanPromptAction, getPromptToolDetails, groupMessageCenterItems, isInteractiveMessageCenterItem, isPromptRequestIdTitle, isRecentlyCompletedMessageCenterItem, isWaitingMessageCenterItem, itemMatchesViewFilters, managedAgentRoundedIconUrl, messageCenterAgentUserStackId, messageCenterFiltersStorageKey, messageCenterGroupLabel, messageCenterStackPreviewNodes, messageCenterStackPreviewText, messageCenterStackRenderId, messageCenterStackScrollSyncSegment, messageCenterStatusFilterValue, messageCenterStatusTone, messageCenterStatusToneClass, partitionMessageCenterItemsByAgentUser, readMessageCenterFilterPreferences, resolveMessageCenterNotificationAction, selectMessageCenterAttentionDeckItems, selectWorkspaceAgentAttentionItems, selectWorkspaceAgentMessageCenterPresentation, stabilizeWorkspaceAgentMessageCenterModel, statusFilterSummary, useEngineSelector, userAvatarPlaceholderUrl, workspaceAgentActivityStatusLabel, workspaceAgentAttentionItemsEqual, workspaceAgentMessageCenterItemEqual, workspaceAgentMessageCenterPresentationEqual, workspaceAgentMessageCenterPromptStatus, writeMessageCenterFilterPreferences };
@@ -5,7 +5,9 @@ import {
5
5
  approvalOptionDisplayLabel,
6
6
  dispatchAgentPlanPromptAction,
7
7
  getPromptToolDetails,
8
- isPromptRequestIdTitle
8
+ isPromptRequestIdTitle,
9
+ latestPlanTurnId,
10
+ planImplementationPromptFromPlanTurn
9
11
  } from "../chunk-OEC2IIWG.js";
10
12
  import {
11
13
  useEngineSelector
@@ -1517,6 +1519,8 @@ var WorkspaceAgentMessageCenterCard = memo(
1517
1519
  item,
1518
1520
  isSubmitting,
1519
1521
  lazySummary = false,
1522
+ promptVariant = "compact",
1523
+ showSummaryWithPrompt = true,
1520
1524
  summaryAccessory,
1521
1525
  onLinkAction,
1522
1526
  onOpenChat,
@@ -1594,7 +1598,7 @@ var WorkspaceAgentMessageCenterCard = memo(
1594
1598
  )
1595
1599
  ] })
1596
1600
  ] }),
1597
- summary ? /* @__PURE__ */ jsx4(
1601
+ summary && (showSummaryWithPrompt || !prompt || !interactive) ? /* @__PURE__ */ jsx4(
1598
1602
  MessageCenterSummary,
1599
1603
  {
1600
1604
  item,
@@ -1609,7 +1613,7 @@ var WorkspaceAgentMessageCenterCard = memo(
1609
1613
  AgentInteractivePromptSurface,
1610
1614
  {
1611
1615
  embedded: true,
1612
- variant: "compact",
1616
+ variant: promptVariant,
1613
1617
  keyboardShortcuts: false,
1614
1618
  prompt,
1615
1619
  isSubmitting,
@@ -3450,8 +3454,155 @@ import {
3450
3454
  selectEngineInteractionResponse,
3451
3455
  selectPendingSubmitsForSession,
3452
3456
  selectPlanDecisionForTurn,
3457
+ selectPlanTurnDismissed,
3453
3458
  selectWorkspaceAgentRootConversationSessions
3454
3459
  } from "@tutti-os/agent-activity-core";
3460
+
3461
+ // agent-message-center/workspaceAgentMessageCenterModelStability.ts
3462
+ function stabilizeWorkspaceAgentMessageCenterModel(previous, next) {
3463
+ if (!previous) {
3464
+ return next;
3465
+ }
3466
+ const previousItemsById = new Map(
3467
+ previous.items.map((item) => [item.id, item])
3468
+ );
3469
+ let itemsChanged = previous.items.length !== next.items.length;
3470
+ const items = next.items.map((nextItem, index) => {
3471
+ const previousItem = previousItemsById.get(nextItem.id);
3472
+ const stableItem = previousItem && workspaceAgentMessageCenterItemEqual(previousItem, nextItem) ? previousItem : nextItem;
3473
+ if (stableItem !== previous.items[index]) {
3474
+ itemsChanged = true;
3475
+ }
3476
+ return stableItem;
3477
+ });
3478
+ const stableItems = itemsChanged ? items : previous.items;
3479
+ const stableCounts = messageCenterCountsEqual(previous.counts, next.counts) ? previous.counts : next.counts;
3480
+ if (previous.waitingCount === next.waitingCount && stableItems === previous.items && stableCounts === previous.counts) {
3481
+ return previous;
3482
+ }
3483
+ return {
3484
+ ...next,
3485
+ counts: stableCounts,
3486
+ items: stableItems
3487
+ };
3488
+ }
3489
+ function workspaceAgentMessageCenterItemEqual(left, right) {
3490
+ return left.id === right.id && left.agentSessionId === right.agentSessionId && left.agentTargetId === right.agentTargetId && left.agentName === right.agentName && left.agentAvatarUrl === right.agentAvatarUrl && left.provider === right.provider && left.userId === right.userId && left.title === right.title && left.imported === right.imported && left.cwd === right.cwd && left.status === right.status && left.lastAgentMessageSummary === right.lastAgentMessageSummary && left.lastAgentMessageAtUnixMs === right.lastAgentMessageAtUnixMs && messageCenterInteractionTargetEqual(
3491
+ left.pendingInteractionTarget,
3492
+ right.pendingInteractionTarget
3493
+ ) && left.needsAttentionKind === right.needsAttentionKind && left.needsAttentionSummary === right.needsAttentionSummary && left.sortTimeUnixMs === right.sortTimeUnixMs && messageCenterIdentityEqual(left.identity, right.identity) && messageCenterDigestEqual(left.digest, right.digest) && messageCenterPromptEqual(left.pendingPrompt, right.pendingPrompt) && messageCenterTurnOutcomeEqual(
3494
+ left.latestTurnOutcome ?? null,
3495
+ right.latestTurnOutcome ?? null
3496
+ );
3497
+ }
3498
+ function messageCenterInteractionTargetEqual(left, right) {
3499
+ return left === right || left !== null && right !== null && left.agentSessionId === right.agentSessionId && left.turnId === right.turnId && left.requestId === right.requestId;
3500
+ }
3501
+ function messageCenterCountsEqual(left, right) {
3502
+ return left.all === right.all && left.working === right.working && left.waiting === right.waiting && left.completed === right.completed && left.failed === right.failed;
3503
+ }
3504
+ function messageCenterIdentityEqual(left, right) {
3505
+ if (left === right) {
3506
+ return true;
3507
+ }
3508
+ if (!left || !right) {
3509
+ return false;
3510
+ }
3511
+ return left.userName === right.userName && left.userAvatarUrl === right.userAvatarUrl && left.agentName === right.agentName && left.agentAvatarUrl === right.agentAvatarUrl;
3512
+ }
3513
+ function messageCenterDigestEqual(left, right) {
3514
+ return left.primary.kind === right.primary.kind && left.primary.summary === right.primary.summary && left.primary.occurredAtUnixMs === right.primary.occurredAtUnixMs;
3515
+ }
3516
+ function messageCenterTurnOutcomeEqual(left, right) {
3517
+ if (left === right) {
3518
+ return true;
3519
+ }
3520
+ if (!left || !right) {
3521
+ return false;
3522
+ }
3523
+ return left.notificationKey === right.notificationKey && left.status === right.status && left.turnId === right.turnId;
3524
+ }
3525
+ function messageCenterPromptEqual(left, right) {
3526
+ if (left === right) {
3527
+ return true;
3528
+ }
3529
+ if (!left || !right || left.kind !== right.kind) {
3530
+ return false;
3531
+ }
3532
+ switch (left.kind) {
3533
+ case "approval":
3534
+ return right.kind === "approval" && left.id === right.id && left.turnId === right.turnId && left.requestId === right.requestId && left.callId === right.callId && left.title === right.title && left.toolName === right.toolName && left.status === right.status && left.occurredAtUnixMs === right.occurredAtUnixMs && messageCenterJsonValueEqual(left.input, right.input) && messageCenterJsonValueEqual(
3535
+ left.output ?? null,
3536
+ right.output ?? null
3537
+ ) && messageCenterApprovalOptionsEqual(left.options, right.options);
3538
+ case "ask-user":
3539
+ return right.kind === "ask-user" && left.requestId === right.requestId && left.title === right.title && messageCenterAskUserQuestionsEqual(left.questions, right.questions);
3540
+ case "exit-plan":
3541
+ return right.kind === "exit-plan" && left.requestId === right.requestId && left.title === right.title && left.keepPlanningOptionId === right.keepPlanningOptionId && messageCenterApprovalOptionsEqual(left.options, right.options);
3542
+ case "plan-implementation":
3543
+ return right.kind === "plan-implementation" && left.requestId === right.requestId && left.title === right.title;
3544
+ }
3545
+ }
3546
+ function messageCenterApprovalOptionsEqual(left, right) {
3547
+ if (left.length !== right.length) {
3548
+ return false;
3549
+ }
3550
+ return left.every((option, index) => {
3551
+ const rightOption = right[index];
3552
+ return rightOption && option.id === rightOption.id && option.label === rightOption.label && option.kind === rightOption.kind && option.description === rightOption.description;
3553
+ });
3554
+ }
3555
+ function messageCenterAskUserQuestionsEqual(left, right) {
3556
+ if (left.length !== right.length) {
3557
+ return false;
3558
+ }
3559
+ return left.every((question, index) => {
3560
+ const rightQuestion = right[index];
3561
+ return rightQuestion && question.id === rightQuestion.id && question.header === rightQuestion.header && question.question === rightQuestion.question && question.multiSelect === rightQuestion.multiSelect && messageCenterAnswerEqual(
3562
+ question.answer ?? null,
3563
+ rightQuestion.answer ?? null
3564
+ ) && question.options.length === rightQuestion.options.length && question.options.every((option, optionIndex) => {
3565
+ const rightOption = rightQuestion.options[optionIndex];
3566
+ return rightOption && option.label === rightOption.label && option.description === rightOption.description;
3567
+ });
3568
+ });
3569
+ }
3570
+ function messageCenterAnswerEqual(left, right) {
3571
+ if (left === right) {
3572
+ return true;
3573
+ }
3574
+ if (!Array.isArray(left) || !Array.isArray(right)) {
3575
+ return false;
3576
+ }
3577
+ return left.length === right.length && left.every((value, index) => value === right[index]);
3578
+ }
3579
+ function messageCenterJsonValueEqual(left, right) {
3580
+ if (left === right) {
3581
+ return true;
3582
+ }
3583
+ if (!left || !right || typeof left !== "object" || typeof right !== "object") {
3584
+ return false;
3585
+ }
3586
+ if (Array.isArray(left) || Array.isArray(right)) {
3587
+ if (!Array.isArray(left) || !Array.isArray(right)) {
3588
+ return false;
3589
+ }
3590
+ return left.length === right.length && left.every(
3591
+ (value, index) => messageCenterJsonValueEqual(value, right[index])
3592
+ );
3593
+ }
3594
+ const leftRecord = left;
3595
+ const rightRecord = right;
3596
+ const leftKeys = Object.keys(leftRecord);
3597
+ if (leftKeys.length !== Object.keys(rightRecord).length) {
3598
+ return false;
3599
+ }
3600
+ return leftKeys.every(
3601
+ (key) => messageCenterJsonValueEqual(leftRecord[key], rightRecord[key])
3602
+ );
3603
+ }
3604
+
3605
+ // agent-message-center/workspaceAgentMessageCenterEngineModel.ts
3455
3606
  function buildWorkspaceAgentMessageCenterModelFromEngine(presentation, snapshot, options = {}) {
3456
3607
  const items = presentation.consumers.filter((consumer) => consumer.session.visible !== false).map((consumer) => {
3457
3608
  const interaction = latestPendingInteraction(consumer);
@@ -3476,6 +3627,7 @@ function buildWorkspaceAgentMessageCenterModelFromEngine(presentation, snapshot,
3476
3627
  function selectWorkspaceAgentMessageCenterPresentation(state) {
3477
3628
  const consumers = selectWorkspaceAgentRootConversationSessions(state);
3478
3629
  const promptStatusByKey = {};
3630
+ const dismissedPlanTurnKeys = {};
3479
3631
  for (const consumer of consumers) {
3480
3632
  const sessionId = consumer.session.agentSessionId;
3481
3633
  for (const interaction of consumer.pendingInteractions) {
@@ -3495,6 +3647,9 @@ function selectWorkspaceAgentMessageCenterPresentation(state) {
3495
3647
  }
3496
3648
  const turnId = consumer.latestTurn?.turnId ?? "";
3497
3649
  if (!turnId) continue;
3650
+ if (selectPlanTurnDismissed(state, sessionId, turnId)) {
3651
+ dismissedPlanTurnKeys[promptStatusKey(sessionId, turnId, turnId)] = true;
3652
+ }
3498
3653
  const decision = selectPlanDecisionForTurn(state, sessionId, turnId);
3499
3654
  if (decision) {
3500
3655
  promptStatusByKey[promptStatusKey(sessionId, turnId, turnId)] = decision.status === "requested" ? "responding" : decision.status;
@@ -3516,17 +3671,128 @@ function selectWorkspaceAgentMessageCenterPresentation(state) {
3516
3671
  }
3517
3672
  return {
3518
3673
  consumers,
3674
+ dismissedPlanTurnKeys,
3519
3675
  promptStatusByKey
3520
3676
  };
3521
3677
  }
3522
3678
  function workspaceAgentMessageCenterPresentationEqual(left, right) {
3523
- return promptStatusMapsEqual(left.promptStatusByKey, right.promptStatusByKey) && left.consumers.length === right.consumers.length && left.consumers.every((item, index) => {
3679
+ return booleanMapsEqual(left.dismissedPlanTurnKeys, right.dismissedPlanTurnKeys) && promptStatusMapsEqual(left.promptStatusByKey, right.promptStatusByKey) && left.consumers.length === right.consumers.length && left.consumers.every((item, index) => {
3524
3680
  const candidate = right.consumers[index];
3525
3681
  return candidate !== void 0 && item.session === candidate.session && item.activeTurn === candidate.activeTurn && item.latestTurn === candidate.latestTurn && item.displayStatus === candidate.displayStatus && item.pendingInteractions.length === candidate.pendingInteractions.length && item.pendingInteractions.every(
3526
3682
  (interaction, interactionIndex) => interaction === candidate.pendingInteractions[interactionIndex]
3527
3683
  );
3528
3684
  });
3529
3685
  }
3686
+ function selectWorkspaceAgentAttentionItems(presentation, snapshot, options = {}) {
3687
+ const result = [];
3688
+ for (const consumer of presentation.consumers) {
3689
+ if (consumer.session.visible === false || consumer.session.workspaceId !== snapshot.workspaceId) {
3690
+ continue;
3691
+ }
3692
+ const messages = sessionMessages(snapshot.sessionMessagesById, consumer);
3693
+ for (const interaction of consumer.pendingInteractions) {
3694
+ const prompt2 = promptFromInteraction(interaction);
3695
+ if (!prompt2) continue;
3696
+ const target2 = {
3697
+ kind: "interaction",
3698
+ workspaceId: consumer.session.workspaceId,
3699
+ agentSessionId: interaction.agentSessionId,
3700
+ turnId: interaction.turnId,
3701
+ requestId: interaction.requestId
3702
+ };
3703
+ const item2 = buildWorkspaceAgentMessageCenterItem({
3704
+ session: consumer.session,
3705
+ latestTurn: consumer.latestTurn,
3706
+ messages,
3707
+ status: "waiting",
3708
+ needsAttention: needsAttentionFromInteraction(consumer, interaction),
3709
+ pendingInteractionTarget: interactionTarget(interaction),
3710
+ pendingPrompt: prompt2,
3711
+ latestTurnOutcome: null,
3712
+ options
3713
+ });
3714
+ item2.id = workspaceAgentAttentionKey(target2);
3715
+ item2.sortTimeUnixMs = interaction.createdAtUnixMs;
3716
+ result.push({
3717
+ item: item2,
3718
+ status: presentation.promptStatusByKey[promptStatusKey(
3719
+ interaction.agentSessionId,
3720
+ interaction.turnId,
3721
+ interaction.requestId
3722
+ )] ?? "idle",
3723
+ target: target2
3724
+ });
3725
+ }
3726
+ const latestTurn = consumer.latestTurn;
3727
+ if (latestTurn?.phase !== "settled" || latestTurn.outcome !== "completed" || consumer.session.capabilities?.planImplementation !== true || consumer.session.capabilities.planMode !== true || latestPlanTurnId(messages) !== latestTurn.turnId) {
3728
+ continue;
3729
+ }
3730
+ const target = {
3731
+ kind: "plan-implementation",
3732
+ workspaceId: consumer.session.workspaceId,
3733
+ agentSessionId: consumer.session.agentSessionId,
3734
+ turnId: latestTurn.turnId,
3735
+ requestId: latestTurn.turnId
3736
+ };
3737
+ const statusKey = promptStatusKey(
3738
+ target.agentSessionId,
3739
+ target.turnId,
3740
+ target.requestId
3741
+ );
3742
+ if (presentation.dismissedPlanTurnKeys[statusKey]) continue;
3743
+ const prompt = planImplementationPromptFromPlanTurn(
3744
+ latestTurn.turnId,
3745
+ consumer.session.title
3746
+ );
3747
+ const needsAttention = {
3748
+ id: `plan-implementation:${latestTurn.turnId}`,
3749
+ workspaceId: consumer.session.workspaceId,
3750
+ agentSessionId: consumer.session.agentSessionId,
3751
+ provider: consumer.session.provider,
3752
+ title: prompt.title,
3753
+ cwd: consumer.session.cwd,
3754
+ kind: "constraint",
3755
+ summary: prompt.title,
3756
+ occurredAtUnixMs: latestTurn.settledAtUnixMs ?? latestTurn.updatedAtUnixMs
3757
+ };
3758
+ const item = buildWorkspaceAgentMessageCenterItem({
3759
+ session: consumer.session,
3760
+ latestTurn,
3761
+ messages,
3762
+ status: "waiting",
3763
+ needsAttention,
3764
+ pendingInteractionTarget: null,
3765
+ pendingPrompt: prompt,
3766
+ latestTurnOutcome: null,
3767
+ options
3768
+ });
3769
+ item.id = workspaceAgentAttentionKey(target);
3770
+ item.sortTimeUnixMs = needsAttention.occurredAtUnixMs;
3771
+ result.push({
3772
+ item,
3773
+ status: presentation.promptStatusByKey[statusKey] ?? "idle",
3774
+ target
3775
+ });
3776
+ }
3777
+ return result;
3778
+ }
3779
+ function workspaceAgentAttentionItemsEqual(left, right) {
3780
+ return left.length === right.length && left.every((entry, index) => {
3781
+ const candidate = right[index];
3782
+ return candidate !== void 0 && entry.status === candidate.status && workspaceAgentAttentionTargetEqual(entry.target, candidate.target) && workspaceAgentMessageCenterItemEqual(entry.item, candidate.item);
3783
+ });
3784
+ }
3785
+ function workspaceAgentAttentionKey(target) {
3786
+ return [
3787
+ target.workspaceId,
3788
+ target.agentSessionId,
3789
+ target.turnId,
3790
+ target.requestId
3791
+ ].join("\n");
3792
+ }
3793
+ function workspaceAgentAttentionTargetEqual(left, right) {
3794
+ return left.kind === right.kind && left.workspaceId === right.workspaceId && left.agentSessionId === right.agentSessionId && left.turnId === right.turnId && left.requestId === right.requestId;
3795
+ }
3530
3796
  function workspaceAgentMessageCenterPromptStatus(presentation, item) {
3531
3797
  const prompt = item.pendingPrompt;
3532
3798
  if (!prompt) return "idle";
@@ -3548,6 +3814,10 @@ function promptStatusMapsEqual(left, right) {
3548
3814
  const keys = Object.keys(left);
3549
3815
  return keys.length === Object.keys(right).length && keys.every((key) => left[key] === right[key]);
3550
3816
  }
3817
+ function booleanMapsEqual(left, right) {
3818
+ const keys = Object.keys(left);
3819
+ return keys.length === Object.keys(right).length && keys.every((key) => right[key] === true);
3820
+ }
3551
3821
  function sessionMessages(sessionMessagesById, consumer) {
3552
3822
  for (const id of [
3553
3823
  consumer.session.agentSessionId,
@@ -3586,6 +3856,17 @@ function needsAttentionFromInteraction(consumer, interaction) {
3586
3856
  }
3587
3857
  function promptFromInteraction(interaction) {
3588
3858
  const input = interaction.input ?? {};
3859
+ const normalizedToolName = (interaction.toolName ?? "").replace(/[_\s-]+/g, "").trim().toLowerCase();
3860
+ if (interaction.kind === "plan" || normalizedToolName === "exitplanmode" || isExitPlanSwitchModeInput(input)) {
3861
+ const keepPlanningOptionId = extractExitPlanKeepPlanningOptionId(input);
3862
+ return {
3863
+ kind: "exit-plan",
3864
+ requestId: interaction.requestId,
3865
+ title: interactionSummary(interaction),
3866
+ options: extractExitPlanModeOptions(input),
3867
+ ...keepPlanningOptionId ? { keepPlanningOptionId } : {}
3868
+ };
3869
+ }
3589
3870
  if (interaction.kind === "question") {
3590
3871
  const questions = normalizeAskUserQuestions(input.questions);
3591
3872
  return {
@@ -3614,7 +3895,8 @@ function promptFromInteraction(interaction) {
3614
3895
  {
3615
3896
  id,
3616
3897
  label: textValue(option.label) ?? textValue(option.name) ?? id,
3617
- kind: textValue(option.kind) ?? id
3898
+ kind: textValue(option.kind) ?? id,
3899
+ ...textValue(option.description) ? { description: textValue(option.description) } : {}
3618
3900
  }
3619
3901
  ] : [];
3620
3902
  });
@@ -3664,150 +3946,6 @@ function arrayValue2(value) {
3664
3946
  return Array.isArray(value) ? value : [];
3665
3947
  }
3666
3948
 
3667
- // agent-message-center/workspaceAgentMessageCenterModelStability.ts
3668
- function stabilizeWorkspaceAgentMessageCenterModel(previous, next) {
3669
- if (!previous) {
3670
- return next;
3671
- }
3672
- const previousItemsById = new Map(
3673
- previous.items.map((item) => [item.id, item])
3674
- );
3675
- let itemsChanged = previous.items.length !== next.items.length;
3676
- const items = next.items.map((nextItem, index) => {
3677
- const previousItem = previousItemsById.get(nextItem.id);
3678
- const stableItem = previousItem && messageCenterItemsEqual(previousItem, nextItem) ? previousItem : nextItem;
3679
- if (stableItem !== previous.items[index]) {
3680
- itemsChanged = true;
3681
- }
3682
- return stableItem;
3683
- });
3684
- const stableItems = itemsChanged ? items : previous.items;
3685
- const stableCounts = messageCenterCountsEqual(previous.counts, next.counts) ? previous.counts : next.counts;
3686
- if (previous.waitingCount === next.waitingCount && stableItems === previous.items && stableCounts === previous.counts) {
3687
- return previous;
3688
- }
3689
- return {
3690
- ...next,
3691
- counts: stableCounts,
3692
- items: stableItems
3693
- };
3694
- }
3695
- function messageCenterItemsEqual(left, right) {
3696
- return left.id === right.id && left.agentSessionId === right.agentSessionId && left.agentTargetId === right.agentTargetId && left.agentName === right.agentName && left.agentAvatarUrl === right.agentAvatarUrl && left.provider === right.provider && left.userId === right.userId && left.title === right.title && left.imported === right.imported && left.cwd === right.cwd && left.status === right.status && left.lastAgentMessageSummary === right.lastAgentMessageSummary && left.lastAgentMessageAtUnixMs === right.lastAgentMessageAtUnixMs && messageCenterInteractionTargetEqual(
3697
- left.pendingInteractionTarget,
3698
- right.pendingInteractionTarget
3699
- ) && left.needsAttentionKind === right.needsAttentionKind && left.needsAttentionSummary === right.needsAttentionSummary && left.sortTimeUnixMs === right.sortTimeUnixMs && messageCenterIdentityEqual(left.identity, right.identity) && messageCenterDigestEqual(left.digest, right.digest) && messageCenterPromptEqual(left.pendingPrompt, right.pendingPrompt) && messageCenterTurnOutcomeEqual(
3700
- left.latestTurnOutcome ?? null,
3701
- right.latestTurnOutcome ?? null
3702
- );
3703
- }
3704
- function messageCenterInteractionTargetEqual(left, right) {
3705
- return left === right || left !== null && right !== null && left.agentSessionId === right.agentSessionId && left.turnId === right.turnId && left.requestId === right.requestId;
3706
- }
3707
- function messageCenterCountsEqual(left, right) {
3708
- return left.all === right.all && left.working === right.working && left.waiting === right.waiting && left.completed === right.completed && left.failed === right.failed;
3709
- }
3710
- function messageCenterIdentityEqual(left, right) {
3711
- if (left === right) {
3712
- return true;
3713
- }
3714
- if (!left || !right) {
3715
- return false;
3716
- }
3717
- return left.userName === right.userName && left.userAvatarUrl === right.userAvatarUrl && left.agentName === right.agentName && left.agentAvatarUrl === right.agentAvatarUrl;
3718
- }
3719
- function messageCenterDigestEqual(left, right) {
3720
- return left.primary.kind === right.primary.kind && left.primary.summary === right.primary.summary && left.primary.occurredAtUnixMs === right.primary.occurredAtUnixMs;
3721
- }
3722
- function messageCenterTurnOutcomeEqual(left, right) {
3723
- if (left === right) {
3724
- return true;
3725
- }
3726
- if (!left || !right) {
3727
- return false;
3728
- }
3729
- return left.notificationKey === right.notificationKey && left.status === right.status && left.turnId === right.turnId;
3730
- }
3731
- function messageCenterPromptEqual(left, right) {
3732
- if (left === right) {
3733
- return true;
3734
- }
3735
- if (!left || !right || left.kind !== right.kind) {
3736
- return false;
3737
- }
3738
- switch (left.kind) {
3739
- case "approval":
3740
- return right.kind === "approval" && left.id === right.id && left.turnId === right.turnId && left.requestId === right.requestId && left.callId === right.callId && left.title === right.title && left.toolName === right.toolName && left.status === right.status && left.occurredAtUnixMs === right.occurredAtUnixMs && messageCenterJsonValueEqual(left.input, right.input) && messageCenterJsonValueEqual(
3741
- left.output ?? null,
3742
- right.output ?? null
3743
- ) && messageCenterApprovalOptionsEqual(left.options, right.options);
3744
- case "ask-user":
3745
- return right.kind === "ask-user" && left.requestId === right.requestId && left.title === right.title && messageCenterAskUserQuestionsEqual(left.questions, right.questions);
3746
- case "exit-plan":
3747
- return right.kind === "exit-plan" && left.requestId === right.requestId && left.title === right.title && left.keepPlanningOptionId === right.keepPlanningOptionId && messageCenterApprovalOptionsEqual(left.options, right.options);
3748
- case "plan-implementation":
3749
- return right.kind === "plan-implementation" && left.requestId === right.requestId && left.title === right.title;
3750
- }
3751
- }
3752
- function messageCenterApprovalOptionsEqual(left, right) {
3753
- if (left.length !== right.length) {
3754
- return false;
3755
- }
3756
- return left.every((option, index) => {
3757
- const rightOption = right[index];
3758
- return rightOption && option.id === rightOption.id && option.label === rightOption.label && option.kind === rightOption.kind && option.description === rightOption.description;
3759
- });
3760
- }
3761
- function messageCenterAskUserQuestionsEqual(left, right) {
3762
- if (left.length !== right.length) {
3763
- return false;
3764
- }
3765
- return left.every((question, index) => {
3766
- const rightQuestion = right[index];
3767
- return rightQuestion && question.id === rightQuestion.id && question.header === rightQuestion.header && question.question === rightQuestion.question && question.multiSelect === rightQuestion.multiSelect && messageCenterAnswerEqual(
3768
- question.answer ?? null,
3769
- rightQuestion.answer ?? null
3770
- ) && question.options.length === rightQuestion.options.length && question.options.every((option, optionIndex) => {
3771
- const rightOption = rightQuestion.options[optionIndex];
3772
- return rightOption && option.label === rightOption.label && option.description === rightOption.description;
3773
- });
3774
- });
3775
- }
3776
- function messageCenterAnswerEqual(left, right) {
3777
- if (left === right) {
3778
- return true;
3779
- }
3780
- if (!Array.isArray(left) || !Array.isArray(right)) {
3781
- return false;
3782
- }
3783
- return left.length === right.length && left.every((value, index) => value === right[index]);
3784
- }
3785
- function messageCenterJsonValueEqual(left, right) {
3786
- if (left === right) {
3787
- return true;
3788
- }
3789
- if (!left || !right || typeof left !== "object" || typeof right !== "object") {
3790
- return false;
3791
- }
3792
- if (Array.isArray(left) || Array.isArray(right)) {
3793
- if (!Array.isArray(left) || !Array.isArray(right)) {
3794
- return false;
3795
- }
3796
- return left.length === right.length && left.every(
3797
- (value, index) => messageCenterJsonValueEqual(value, right[index])
3798
- );
3799
- }
3800
- const leftRecord = left;
3801
- const rightRecord = right;
3802
- const leftKeys = Object.keys(leftRecord);
3803
- if (leftKeys.length !== Object.keys(rightRecord).length) {
3804
- return false;
3805
- }
3806
- return leftKeys.every(
3807
- (key) => messageCenterJsonValueEqual(leftRecord[key], rightRecord[key])
3808
- );
3809
- }
3810
-
3811
3949
  // agent-message-center/workspaceAgentConsumerSelectors.ts
3812
3950
  import {
3813
3951
  selectWorkspaceAgentConsumerCounts,
@@ -3858,6 +3996,7 @@ export {
3858
3996
  readMessageCenterFilterPreferences,
3859
3997
  resolveMessageCenterNotificationAction,
3860
3998
  selectMessageCenterAttentionDeckItems,
3999
+ selectWorkspaceAgentAttentionItems,
3861
4000
  selectWorkspaceAgentConsumerCounts,
3862
4001
  selectWorkspaceAgentConsumerSession,
3863
4002
  selectWorkspaceAgentConsumerSessions,
@@ -3867,6 +4006,8 @@ export {
3867
4006
  useEngineSelector,
3868
4007
  userAvatarPlaceholderUrl,
3869
4008
  workspaceAgentActivityStatusLabel,
4009
+ workspaceAgentAttentionItemsEqual,
4010
+ workspaceAgentMessageCenterItemEqual,
3870
4011
  workspaceAgentMessageCenterPresentationEqual,
3871
4012
  workspaceAgentMessageCenterPromptStatus,
3872
4013
  writeMessageCenterFilterPreferences