@alpaca-editor/core 1.0.4135 → 1.0.4141

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 (149) hide show
  1. package/dist/components/index.d.ts +2 -0
  2. package/dist/components/index.js +1 -0
  3. package/dist/components/index.js.map +1 -1
  4. package/dist/components/ui/select.d.ts +2 -1
  5. package/dist/components/ui/select.js +2 -2
  6. package/dist/components/ui/select.js.map +1 -1
  7. package/dist/components/ui/tabs.d.ts +17 -0
  8. package/dist/components/ui/tabs.js +27 -0
  9. package/dist/components/ui/tabs.js.map +1 -0
  10. package/dist/config/config.js +7 -1
  11. package/dist/config/config.js.map +1 -1
  12. package/dist/editor/FieldListField.js +3 -4
  13. package/dist/editor/FieldListField.js.map +1 -1
  14. package/dist/editor/ImageEditButton.d.ts +2 -1
  15. package/dist/editor/ImageEditButton.js +4 -4
  16. package/dist/editor/ImageEditButton.js.map +1 -1
  17. package/dist/editor/PictureEditor.js +42 -1
  18. package/dist/editor/PictureEditor.js.map +1 -1
  19. package/dist/editor/Terminal.js +1 -1
  20. package/dist/editor/Terminal.js.map +1 -1
  21. package/dist/editor/Titlebar.js +0 -1
  22. package/dist/editor/Titlebar.js.map +1 -1
  23. package/dist/editor/ai/AgentCostDisplay.d.ts +3 -1
  24. package/dist/editor/ai/AgentCostDisplay.js +26 -2
  25. package/dist/editor/ai/AgentCostDisplay.js.map +1 -1
  26. package/dist/editor/ai/AgentStatusBadge.d.ts +26 -0
  27. package/dist/editor/ai/AgentStatusBadge.js +110 -0
  28. package/dist/editor/ai/AgentStatusBadge.js.map +1 -0
  29. package/dist/editor/ai/AgentTerminal.js +289 -198
  30. package/dist/editor/ai/AgentTerminal.js.map +1 -1
  31. package/dist/editor/ai/Agents.d.ts +2 -2
  32. package/dist/editor/ai/Agents.js +115 -19
  33. package/dist/editor/ai/Agents.js.map +1 -1
  34. package/dist/editor/ai/AiResponseMessage.js +259 -45
  35. package/dist/editor/ai/AiResponseMessage.js.map +1 -1
  36. package/dist/editor/ai/ContextInfoBar.js +124 -113
  37. package/dist/editor/ai/ContextInfoBar.js.map +1 -1
  38. package/dist/editor/ai/ToolCallDisplay.d.ts +1 -0
  39. package/dist/editor/ai/ToolCallDisplay.js +70 -58
  40. package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
  41. package/dist/editor/ai/useAgentStatus.d.ts +13 -0
  42. package/dist/editor/ai/useAgentStatus.js +101 -0
  43. package/dist/editor/ai/useAgentStatus.js.map +1 -0
  44. package/dist/editor/client/EditorShell.js +23 -8
  45. package/dist/editor/client/EditorShell.js.map +1 -1
  46. package/dist/editor/client/itemsRepository.js.map +1 -1
  47. package/dist/editor/commands/localizeItem/LocalizeItemDialog.js +5 -5
  48. package/dist/editor/commands/localizeItem/LocalizeItemDialog.js.map +1 -1
  49. package/dist/editor/control-center/About.js +1 -1
  50. package/dist/editor/control-center/About.js.map +1 -1
  51. package/dist/editor/control-center/AllAgentsPanel.d.ts +5 -0
  52. package/dist/editor/control-center/AllAgentsPanel.js +126 -0
  53. package/dist/editor/control-center/AllAgentsPanel.js.map +1 -0
  54. package/dist/editor/control-center/WebSocketMessages.js +1 -0
  55. package/dist/editor/control-center/WebSocketMessages.js.map +1 -1
  56. package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.js +42 -7
  57. package/dist/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.js.map +1 -1
  58. package/dist/editor/media-selector/AiImageSearch.d.ts +1 -1
  59. package/dist/editor/media-selector/AiImageSearch.js +162 -103
  60. package/dist/editor/media-selector/AiImageSearch.js.map +1 -1
  61. package/dist/editor/media-selector/TreeSelector.js +20 -4
  62. package/dist/editor/media-selector/TreeSelector.js.map +1 -1
  63. package/dist/editor/menubar/toolbar-sections/UtilityControls.js +5 -2
  64. package/dist/editor/menubar/toolbar-sections/UtilityControls.js.map +1 -1
  65. package/dist/editor/page-editor-chrome/PlaceholderDropZone.d.ts +1 -1
  66. package/dist/editor/page-editor-chrome/PlaceholderDropZone.js +7 -5
  67. package/dist/editor/page-editor-chrome/PlaceholderDropZone.js.map +1 -1
  68. package/dist/editor/page-viewer/DeviceToolbar.js +2 -2
  69. package/dist/editor/page-viewer/DeviceToolbar.js.map +1 -1
  70. package/dist/editor/page-viewer/PageViewerFrame.js +18 -11
  71. package/dist/editor/page-viewer/PageViewerFrame.js.map +1 -1
  72. package/dist/editor/services/agentService.d.ts +53 -48
  73. package/dist/editor/services/agentService.js +137 -79
  74. package/dist/editor/services/agentService.js.map +1 -1
  75. package/dist/editor/services/aiService.d.ts +1 -1
  76. package/dist/editor/services/editService.js +1 -0
  77. package/dist/editor/services/editService.js.map +1 -1
  78. package/dist/editor/sidebar/GraphQL.js +20 -7
  79. package/dist/editor/sidebar/GraphQL.js.map +1 -1
  80. package/dist/editor/sidebar/SEOInfo.js +1 -2
  81. package/dist/editor/sidebar/SEOInfo.js.map +1 -1
  82. package/dist/editor/sidebar/Translations.js +10 -7
  83. package/dist/editor/sidebar/Translations.js.map +1 -1
  84. package/dist/editor/ui/ItemNameDialogNew.js +1 -1
  85. package/dist/editor/ui/ItemSearch.js +10 -4
  86. package/dist/editor/ui/ItemSearch.js.map +1 -1
  87. package/dist/index.d.ts +5 -1
  88. package/dist/index.js +4 -1
  89. package/dist/index.js.map +1 -1
  90. package/dist/page-wizard/steps/CollectStep.js +2 -2
  91. package/dist/page-wizard/steps/CollectStep.js.map +1 -1
  92. package/dist/page-wizard/steps/FieldEditor.js +2 -2
  93. package/dist/page-wizard/steps/FieldEditor.js.map +1 -1
  94. package/dist/revision.d.ts +2 -2
  95. package/dist/revision.js +2 -2
  96. package/dist/splash-screen/NewPage.js +2 -2
  97. package/dist/splash-screen/NewPage.js.map +1 -1
  98. package/dist/splash-screen/RecentPages.js +1 -1
  99. package/dist/splash-screen/RecentPages.js.map +1 -1
  100. package/dist/styles.css +167 -15
  101. package/dist/tour/Tour.js +15 -11
  102. package/dist/tour/Tour.js.map +1 -1
  103. package/package.json +1 -1
  104. package/src/components/index.ts +2 -0
  105. package/src/components/ui/select.tsx +3 -0
  106. package/src/components/ui/tabs.tsx +87 -0
  107. package/src/config/config.tsx +7 -1
  108. package/src/editor/FieldListField.tsx +13 -13
  109. package/src/editor/ImageEditButton.tsx +5 -3
  110. package/src/editor/PictureEditor.tsx +48 -1
  111. package/src/editor/Terminal.tsx +1 -1
  112. package/src/editor/Titlebar.tsx +0 -1
  113. package/src/editor/ai/AgentCostDisplay.tsx +57 -1
  114. package/src/editor/ai/AgentStatusBadge.tsx +144 -0
  115. package/src/editor/ai/AgentTerminal.tsx +345 -219
  116. package/src/editor/ai/Agents.tsx +179 -30
  117. package/src/editor/ai/AiResponseMessage.tsx +411 -114
  118. package/src/editor/ai/ContextInfoBar.tsx +134 -131
  119. package/src/editor/ai/ToolCallDisplay.tsx +217 -176
  120. package/src/editor/ai/useAgentStatus.ts +123 -0
  121. package/src/editor/client/EditorShell.tsx +34 -8
  122. package/src/editor/client/itemsRepository.ts +1 -2
  123. package/src/editor/commands/localizeItem/LocalizeItemDialog.tsx +5 -5
  124. package/src/editor/control-center/About.tsx +0 -14
  125. package/src/editor/control-center/AllAgentsPanel.tsx +300 -0
  126. package/src/editor/control-center/WebSocketMessages.tsx +1 -0
  127. package/src/editor/control-center/setup-steps/AiSetupStep/tools/GenerateToolsSection.tsx +49 -8
  128. package/src/editor/media-selector/AiImageSearch.tsx +162 -172
  129. package/src/editor/media-selector/TreeSelector.tsx +137 -116
  130. package/src/editor/menubar/toolbar-sections/UtilityControls.tsx +9 -1
  131. package/src/editor/page-editor-chrome/PlaceholderDropZone.tsx +7 -4
  132. package/src/editor/page-viewer/DeviceToolbar.tsx +15 -11
  133. package/src/editor/page-viewer/PageViewerFrame.tsx +20 -14
  134. package/src/editor/services/agentService.ts +217 -129
  135. package/src/editor/services/aiService.ts +2 -2
  136. package/src/editor/services/editService.ts +1 -0
  137. package/src/editor/sidebar/GraphQL.tsx +143 -117
  138. package/src/editor/sidebar/SEOInfo.tsx +1 -2
  139. package/src/editor/sidebar/Translations.tsx +14 -12
  140. package/src/editor/ui/ItemNameDialogNew.tsx +1 -1
  141. package/src/editor/ui/ItemSearch.tsx +11 -4
  142. package/src/editor/ui/SimpleTabs.tsx +1 -1
  143. package/src/index.ts +6 -0
  144. package/src/page-wizard/steps/CollectStep.tsx +2 -2
  145. package/src/page-wizard/steps/FieldEditor.tsx +13 -15
  146. package/src/revision.ts +2 -2
  147. package/src/splash-screen/NewPage.tsx +2 -2
  148. package/src/splash-screen/RecentPages.tsx +1 -1
  149. package/src/tour/Tour.tsx +61 -48
@@ -7,19 +7,11 @@ export interface StartAgentRequest {
7
7
  agentId: string;
8
8
  message: string;
9
9
  sessionId: string;
10
- profileId: string;
11
- // Execution mode: 'agent' enables all tools as per profile; 'ask' restricts to askModeTools
12
- mode?: "agent" | "ask" | "restricted";
10
+ profileId: string; // GUID of the AI profile
11
+ // Execution mode: 'autonomous' enables all tools as per profile; 'read-only' restricts to askModeTools; 'supervised' requires approval for actions outside current context
12
+ mode?: "autonomous" | "read-only" | "supervised";
13
13
  model?: string;
14
- itemid: string;
15
- language: string;
16
- version: number;
17
- selection?: string[];
18
- selectedText?: string;
19
- allowedFunctions?: string[];
20
- addContextContent?: boolean;
21
- addAllContent?: boolean;
22
- addSelectedComponents?: boolean;
14
+ /** @deprecated Use profileId instead. Kept for backward compatibility. */
23
15
  profile?: string;
24
16
  deterministic?: boolean;
25
17
  seed?: number;
@@ -48,9 +40,19 @@ export type AgentStreamMessageType =
48
40
  | "toolResult"
49
41
  | "editOperations"
50
42
  | "completed"
51
- | "error";
52
-
53
- export type AgentStatus = "new" | "running" | "closed" | "cancelled";
43
+ | "error"
44
+ | "contextUpdate";
45
+
46
+ export type AgentStatus =
47
+ | "new"
48
+ | "running"
49
+ | "waitingForApproval"
50
+ | "closed"
51
+ | "cancelled"
52
+ | "completed"
53
+ | "error"
54
+ | "idle"
55
+ | number;
54
56
 
55
57
  export type Agent = {
56
58
  id: string;
@@ -61,10 +63,6 @@ export type Agent = {
61
63
  };
62
64
 
63
65
  export type AgentDetails = Agent & {
64
- itemId: string;
65
- itemPath: string;
66
- language: string;
67
- version: number;
68
66
  profileId?: string;
69
67
  profileName: string;
70
68
  model: string;
@@ -82,7 +80,10 @@ export type AgentDetails = Agent & {
82
80
  totalCost: number;
83
81
  currency: string;
84
82
  messageCount: number;
85
- metadata?: string;
83
+ /** Cost limit for this agent (in USD) */
84
+ costLimit?: number;
85
+ /** Agent context data (JSON string) - contains pages, componentIds, field, comment, etc. */
86
+ agentContext?: string;
86
87
  messages?: AgentChatMessage[];
87
88
  };
88
89
 
@@ -126,44 +127,55 @@ export async function rejectToolCall(params: {
126
127
  return result.data ?? { success: true };
127
128
  }
128
129
 
129
- // Metadata shape stored on the agent. Server accepts a flexible JSON object; keep it permissive.
130
- export type AgentMetadata = {
130
+ // Simplified context data for agent chats - matches AgentContextData on server
131
+ export type AgentContextData = {
131
132
  selection?: string[];
132
133
  selectedText?: string;
133
- allowedFunctions?: string[];
134
- mode?: "agent" | "ask" | "restricted";
135
- addContextContent?: boolean;
136
- addAllContent?: boolean;
137
- addSelectedComponents?: boolean;
138
- profile?: string;
139
- additionalData?: Record<string, any>;
140
- context?: {
141
- pages?: Array<{
134
+ pages?: Array<{
135
+ id: string;
136
+ language: string;
137
+ version: number;
138
+ path?: string;
139
+ name?: string;
140
+ }>;
141
+ /** Component context with page information per component */
142
+ components?: Array<{
143
+ componentId: string;
144
+ pageItem: {
142
145
  id: string;
143
146
  language: string;
144
147
  version: number;
145
148
  path?: string;
146
149
  name?: string;
147
- }>;
148
- componentIds?: string[];
149
- field?: {
150
- fieldId: string;
151
- itemId: string;
152
- name?: string;
153
- };
154
- comment?: {
155
- id: string;
156
- text?: string;
157
- fieldName?: string;
158
- itemName?: string;
159
- author?: string;
160
- selectedText?: string;
161
- rangeStart?: number;
162
- rangeEnd?: number;
163
150
  };
151
+ }>;
152
+ /** @deprecated Use components instead to get page information per component */
153
+ componentIds?: string[];
154
+ field?: {
155
+ fieldId: string;
156
+ itemId: string;
157
+ name?: string;
164
158
  };
159
+ comment?: {
160
+ id: string;
161
+ text?: string;
162
+ fieldName?: string;
163
+ itemName?: string;
164
+ author?: string;
165
+ selectedText?: string;
166
+ rangeStart?: number;
167
+ rangeEnd?: number;
168
+ };
169
+ mode?: "autonomous" | "read-only" | "supervised";
170
+ profile?: string;
171
+ costLimit?: number;
172
+ initialCostLimit?: number;
173
+ additionalData?: Record<string, any>;
165
174
  };
166
175
 
176
+ // Legacy alias for backward compatibility
177
+ export type AgentMetadata = AgentContextData;
178
+
167
179
  export interface AgentChatMessage {
168
180
  id: string;
169
181
  agentId: string;
@@ -359,6 +371,9 @@ async function processEventStream(
359
371
  case 6:
360
372
  message.type = "error";
361
373
  break;
374
+ case 7:
375
+ message.type = "contextUpdate";
376
+ break;
362
377
  default:
363
378
  message.type = `Unknown_${message.type}`;
364
379
  }
@@ -484,6 +499,28 @@ export async function getClosedAgents(limit?: number): Promise<Agent[]> {
484
499
  return result.data || [];
485
500
  }
486
501
 
502
+ /**
503
+ * Gets all non-closed agents across all users (admin only)
504
+ */
505
+ export async function getAllAgents(limit?: number): Promise<AgentDetails[]> {
506
+ const params = new URLSearchParams();
507
+ if (limit) params.append("limit", limit.toString());
508
+
509
+ const queryString = params.toString();
510
+ const url =
511
+ AGENT_BASE_URL + "/getAllAgents" + (queryString ? `?${queryString}` : "");
512
+
513
+ const result = await get<AgentDetails[]>(url);
514
+
515
+ if (result.type !== "success") {
516
+ throw new Error(
517
+ `Failed to get all agents: ${result.summary || "Unknown error"} ${result.details || ""}`,
518
+ );
519
+ }
520
+
521
+ return result.data || [];
522
+ }
523
+
487
524
  /**
488
525
  * Cancels a running agent execution
489
526
  */
@@ -541,7 +578,7 @@ export async function updateAgentSettings(
541
578
  agentId: string,
542
579
  settings: {
543
580
  model?: string | null;
544
- mode?: "agent" | "ask" | "restricted" | string | null;
581
+ mode?: "autonomous" | "read-only" | "supervised" | string | null;
545
582
  },
546
583
  ): Promise<{ success: boolean; updates?: { model: boolean; mode: boolean } }> {
547
584
  const result = await post<{
@@ -565,7 +602,7 @@ export async function updateAgentSettings(
565
602
  /**
566
603
  * Updates the metadata (context) for an agent chat
567
604
  */
568
- export async function updateAgentMetadata(
605
+ export async function updateAgentContext(
569
606
  agentId: string,
570
607
  metadata: AgentMetadata,
571
608
  ): Promise<{ success: boolean }> {
@@ -575,8 +612,8 @@ export async function updateAgentMetadata(
575
612
  const { context: _drop, ...cleanNoTop } = clean as any;
576
613
 
577
614
  const result = await post<{ success: boolean }>(
578
- AGENT_BASE_URL + "/updateMetadata",
579
- { agentId, metadata: cleanNoTop },
615
+ AGENT_BASE_URL + "/updateContext",
616
+ { agentId, ...cleanNoTop },
580
617
  );
581
618
 
582
619
  if (result.type !== "success") {
@@ -590,13 +627,18 @@ export async function updateAgentMetadata(
590
627
 
591
628
  export async function updateAgentCostLimit(
592
629
  agentId: string,
593
- action: "extend" | "remove" | "set",
630
+ action: "extend" | "set",
594
631
  amount?: number,
595
- ): Promise<{ success: boolean; metadata?: any }> {
596
- const result = await post<{ success: boolean; metadata?: any }>(
597
- AGENT_BASE_URL + "/updateCostLimit",
598
- { agentId, action, amount },
599
- );
632
+ ): Promise<{
633
+ success: boolean;
634
+ costLimit?: number;
635
+ initialCostLimit?: number;
636
+ }> {
637
+ const result = await post<{
638
+ success: boolean;
639
+ costLimit?: number;
640
+ initialCostLimit?: number;
641
+ }>(AGENT_BASE_URL + "/updateCostLimit", { agentId, action, amount });
600
642
 
601
643
  if (result.type !== "success") {
602
644
  throw new Error(
@@ -704,19 +746,13 @@ export function convertAgentMessagesToTerminalFormat(
704
746
  }
705
747
 
706
748
  // Canonicalize metadata to a stable, collision-free shape for both client use and POST payloads
707
- export function canonicalizeAgentMetadata(meta: AgentMetadata | any): AgentMetadata {
749
+ export function canonicalizeAgentMetadata(
750
+ meta: AgentMetadata | any,
751
+ ): AgentContextData {
708
752
  const m: any = { ...(meta || {}) };
709
- // Drop accidental top-level context copies
710
- if (Object.prototype.hasOwnProperty.call(m, "context")) {
711
- delete m.context;
712
- }
713
753
 
714
754
  // Normalize known PascalCase top-level metadata keys to camelCase
715
- const normalizeTop = (
716
- source: any,
717
- from: string,
718
- to: string,
719
- ) => {
755
+ const normalizeTop = (source: any, from: string, to: string) => {
720
756
  if (Object.prototype.hasOwnProperty.call(source, from)) {
721
757
  if (!Object.prototype.hasOwnProperty.call(source, to)) {
722
758
  source[to] = source[from];
@@ -726,47 +762,50 @@ export function canonicalizeAgentMetadata(meta: AgentMetadata | any): AgentMetad
726
762
  };
727
763
  normalizeTop(m, "Selection", "selection");
728
764
  normalizeTop(m, "SelectedText", "selectedText");
729
- normalizeTop(m, "AllowedFunctions", "allowedFunctions");
730
765
  normalizeTop(m, "Mode", "mode");
731
- normalizeTop(m, "AddContextContent", "addContextContent");
732
- normalizeTop(m, "AddAllContent", "addAllContent");
733
- normalizeTop(m, "AddSelectedComponents", "addSelectedComponents");
734
766
  normalizeTop(m, "Profile", "profile");
735
767
  normalizeTop(m, "CostLimit", "costLimit");
736
768
  normalizeTop(m, "InitialCostLimit", "initialCostLimit");
737
- normalizeTop(m, "CostLimitDisabled", "costLimitDisabled");
769
+ normalizeTop(m, "Pages", "pages");
770
+ normalizeTop(m, "ComponentIds", "componentIds");
771
+ normalizeTop(m, "Field", "field");
772
+ normalizeTop(m, "Comment", "comment");
773
+
774
+ // Handle legacy context nested structure - lift to top level
775
+ const contextSources: any[] = [];
776
+ if (m.context && typeof m.context === "object") {
777
+ contextSources.push(m.context);
778
+ delete m.context;
779
+ }
738
780
 
739
- // Merge both additionalData and AdditionalData (case variants)
740
- const sources: any[] = [];
741
- if (m.additionalData && typeof m.additionalData === "object") sources.push(m.additionalData);
742
- if (m.AdditionalData && typeof m.AdditionalData === "object") sources.push(m.AdditionalData);
743
- const additional: any = {};
744
- for (const src of sources) {
745
- for (const k of Object.keys(src)) {
746
- // Shallow merge; context handled below
747
- if (k.toLowerCase() !== "context") {
748
- if (additional[k] === undefined) additional[k] = src[k];
749
- }
750
- }
781
+ // Merge additionalData context entries
782
+ const additionalDataSources: any[] = [];
783
+ if (m.additionalData && typeof m.additionalData === "object") {
784
+ additionalDataSources.push(m.additionalData);
785
+ }
786
+ if (m.AdditionalData && typeof m.AdditionalData === "object") {
787
+ additionalDataSources.push(m.AdditionalData);
751
788
  }
752
789
 
753
- // Merge any case-variant "context" entries into one canonical object
754
- const contextCandidates: any[] = [];
755
- for (const src of sources) {
790
+ for (const src of additionalDataSources) {
756
791
  for (const key of Object.keys(src)) {
757
- if (key.toLowerCase() === "context") {
758
- if (src[key] != null) contextCandidates.push(src[key]);
792
+ if (key.toLowerCase() === "context" && src[key]) {
793
+ contextSources.push(src[key]);
759
794
  }
760
795
  }
761
796
  }
762
797
 
763
- const ctx: any = {};
764
- const pushPages = (arr: any[]) => {
765
- const existing: any[] = Array.isArray(ctx.pages) ? ctx.pages : [];
766
- const combined = [...existing, ...arr];
798
+ // Process pages
799
+ const allPages: any[] = [];
800
+ if (Array.isArray(m.pages)) allPages.push(...m.pages);
801
+ for (const ctx of contextSources) {
802
+ if (Array.isArray(ctx.pages)) allPages.push(...ctx.pages);
803
+ if (Array.isArray(ctx.items)) allPages.push(...ctx.items); // legacy
804
+ }
805
+ if (allPages.length > 0) {
767
806
  const dedup: any[] = [];
768
807
  const seen = new Set<string>();
769
- for (const p of combined) {
808
+ for (const p of allPages) {
770
809
  const id = p?.id ?? p?.Id;
771
810
  if (!id) continue;
772
811
  const lang = (p?.language ?? p?.Language ?? "").toString();
@@ -782,52 +821,101 @@ export function canonicalizeAgentMetadata(meta: AgentMetadata | any): AgentMetad
782
821
  path: p?.path ?? p?.Path,
783
822
  });
784
823
  }
785
- if (dedup.length) ctx.pages = dedup;
786
- };
787
- const pushComponentIds = (ids: any[]) => {
788
- const existing: string[] = Array.isArray(ctx.componentIds) ? ctx.componentIds : [];
789
- const all = [...existing, ...ids]
790
- .map((x) => (typeof x === "string" ? x : x?.id ?? x?.Id))
824
+ m.pages = dedup;
825
+ }
826
+
827
+ // Process components with page info (new structure)
828
+ const allComponents: any[] = [];
829
+ if (Array.isArray(m.components)) allComponents.push(...m.components);
830
+ if (Array.isArray(m.Components)) allComponents.push(...m.Components);
831
+ for (const ctx of contextSources) {
832
+ if (Array.isArray(ctx.components)) allComponents.push(...ctx.components);
833
+ if (Array.isArray(ctx.Components)) allComponents.push(...ctx.Components);
834
+ }
835
+ if (allComponents.length > 0) {
836
+ const dedup: any[] = [];
837
+ const seen = new Set<string>();
838
+ for (const c of allComponents) {
839
+ if (!c) continue;
840
+ const componentId = c.componentId ?? c.ComponentId ?? c.id ?? c.Id;
841
+ if (!componentId || typeof componentId !== "string") continue;
842
+ const key = componentId.toLowerCase();
843
+ if (seen.has(key)) continue;
844
+ seen.add(key);
845
+
846
+ const pageItem = c.pageItem ?? c.PageItem;
847
+ dedup.push({
848
+ componentId: String(componentId),
849
+ pageItem: pageItem
850
+ ? {
851
+ id: String(pageItem.id ?? pageItem.Id ?? ""),
852
+ language: pageItem.language ?? pageItem.Language,
853
+ version: pageItem.version ?? pageItem.Version,
854
+ name: pageItem.name ?? pageItem.Name,
855
+ path: pageItem.path ?? pageItem.Path,
856
+ }
857
+ : undefined,
858
+ });
859
+ }
860
+ m.components = dedup;
861
+ }
862
+
863
+ // Process componentIds (legacy - for backward compatibility)
864
+ const allComponentIds: any[] = [];
865
+ if (Array.isArray(m.componentIds)) allComponentIds.push(...m.componentIds);
866
+ for (const ctx of contextSources) {
867
+ if (Array.isArray(ctx.componentIds))
868
+ allComponentIds.push(...ctx.componentIds);
869
+ }
870
+ if (allComponentIds.length > 0) {
871
+ const ids = allComponentIds
872
+ .map((x) => (typeof x === "string" ? x : (x?.id ?? x?.Id)))
791
873
  .filter((x): x is string => !!x && typeof x === "string");
792
- if (all.length) ctx.componentIds = Array.from(new Set(all));
793
- };
874
+ m.componentIds = Array.from(new Set(ids));
875
+ }
794
876
 
795
- for (const candidate of contextCandidates) {
796
- if (!candidate || typeof candidate !== "object") continue;
797
- for (const k of Object.keys(candidate)) {
798
- const lk = k.toLowerCase();
799
- const val = (candidate as any)[k];
800
- if (lk === "pages" && Array.isArray(val)) {
801
- pushPages(val);
802
- } else if (lk === "items" && Array.isArray(val)) {
803
- // Support legacy key
804
- pushPages(val);
805
- } else if (lk === "componentids" && Array.isArray(val)) {
806
- pushComponentIds(val);
807
- } else if (lk === "components" && Array.isArray(val)) {
808
- pushComponentIds(val);
809
- } else if (lk === "field" && !ctx.field && val) {
810
- ctx.field = {
811
- fieldId: val.fieldId ?? val.FieldId ?? val.id ?? val.Id,
812
- itemId: val.itemId ?? val.ItemId,
813
- name: val.name ?? val.Name,
877
+ // Process field
878
+ if (!m.field) {
879
+ for (const ctx of contextSources) {
880
+ if (ctx.field) {
881
+ m.field = {
882
+ fieldId:
883
+ ctx.field.fieldId ??
884
+ ctx.field.FieldId ??
885
+ ctx.field.id ??
886
+ ctx.field.Id,
887
+ itemId: ctx.field.itemId ?? ctx.field.ItemId,
888
+ name: ctx.field.name ?? ctx.field.Name,
814
889
  };
815
- } else if (lk === "comment" && !ctx.comment && val) {
816
- ctx.comment = val;
817
- } else if (lk === "todolist" && !ctx.todoList && val) {
818
- ctx.todoList = val;
890
+ break;
819
891
  }
820
892
  }
821
893
  }
822
894
 
823
- if (Object.keys(ctx).length > 0) {
824
- additional.context = ctx;
895
+ // Process comment
896
+ if (!m.comment) {
897
+ for (const ctx of contextSources) {
898
+ if (ctx.comment) {
899
+ m.comment = ctx.comment;
900
+ break;
901
+ }
902
+ }
903
+ }
904
+
905
+ // Merge additionalData (excluding context which we've lifted)
906
+ const additional: any = {};
907
+ for (const src of additionalDataSources) {
908
+ for (const k of Object.keys(src)) {
909
+ if (k.toLowerCase() !== "context") {
910
+ if (additional[k] === undefined) additional[k] = src[k];
911
+ }
912
+ }
825
913
  }
826
914
 
827
- // Assign canonical, remove other case variants
828
915
  m.additionalData = Object.keys(additional).length ? additional : undefined;
829
916
  if (Object.prototype.hasOwnProperty.call(m, "AdditionalData")) {
830
917
  delete m.AdditionalData;
831
918
  }
832
- return m as AgentMetadata;
919
+
920
+ return m as AgentContextData;
833
921
  }
@@ -23,8 +23,8 @@ export type AiProfile = {
23
23
  includeEditorContextOnCreate?: boolean;
24
24
  // When true, the agent should run in managed TODO list mode.
25
25
  managedTodoMode?: boolean;
26
- // Tools that are allowed when the user switches to "Ask" mode
27
- askModeTools?: string[];
26
+ // Tools that are allowed when the user switches to "Read-Only" mode
27
+ readOnlyModeTools?: string[];
28
28
  // Optional cost limit in USD, sourced from profile
29
29
  costLimit?: number | null;
30
30
  };
@@ -133,6 +133,7 @@ export async function lockField(
133
133
  fieldId: string,
134
134
  sessionId: string,
135
135
  ): Promise<ExecutionResult<any>> {
136
+ console.trace("[editService] locking field:", fieldId, "for item:", item.id);
136
137
  const response = await post("/alpaca/editor/lockField", {
137
138
  item,
138
139
  fieldId: fieldId,