@agent-native/core 0.114.11 → 0.114.13

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 (109) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +12 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/application-state/index.ts +3 -0
  5. package/corpus/core/src/application-state/script-helpers.ts +12 -1
  6. package/corpus/core/src/application-state/store.ts +216 -15
  7. package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +89 -1
  8. package/corpus/core/src/db/client.ts +44 -1
  9. package/corpus/templates/clips/changelog/2026-07-21-live-dictation-text-now-appears-above-the-desktop-recording-.md +6 -0
  10. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +14 -3
  11. package/corpus/templates/clips/desktop/src/overlays/flow-bar.tsx +25 -0
  12. package/corpus/templates/clips/desktop/src/styles.css +20 -0
  13. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +3 -4
  14. package/corpus/templates/clips/desktop/src-tauri/src/native_screen/custom_capture.rs +11 -4
  15. package/corpus/templates/design/AGENTS.md +7 -3
  16. package/corpus/templates/design/actions/begin-node-rewrite-request.ts +78 -0
  17. package/corpus/templates/design/actions/cancel-node-rewrite-request.ts +17 -7
  18. package/corpus/templates/design/actions/propose-node-rewrite.ts +26 -18
  19. package/corpus/templates/design/actions/resolve-node-rewrite.ts +67 -19
  20. package/corpus/templates/design/app/components/visual-editor/NodeRewriteProposal.tsx +1 -4
  21. package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +2 -6
  22. package/corpus/templates/design/shared/node-rewrite.ts +32 -0
  23. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +6 -3
  24. package/corpus/templates/forms/app/pages/FormBuilderPage.tsx +11 -18
  25. package/corpus/templates/forms/app/pages/FormsListPage.tsx +9 -5
  26. package/corpus/templates/slides/app/context/DeckContext.tsx +18 -0
  27. package/corpus/templates/slides/changelog/2026-07-21-decks-list-now-refreshes-when-you-switch-organizations.md +6 -0
  28. package/corpus/templates/tasks/app/components/custom-fields/FieldCreateBar.tsx +13 -19
  29. package/corpus/templates/tasks/app/components/custom-fields/FieldListRow.tsx +27 -22
  30. package/corpus/templates/tasks/app/components/custom-fields/FieldsList.tsx +26 -22
  31. package/corpus/templates/tasks/app/components/custom-fields/FieldsListSkeleton.tsx +4 -1
  32. package/corpus/templates/tasks/app/components/custom-fields/FieldsPage.tsx +4 -2
  33. package/corpus/templates/tasks/app/components/custom-fields/editor/FieldEditorSidebar.tsx +12 -3
  34. package/corpus/templates/tasks/app/components/custom-fields/editor/FieldTitleSection.tsx +6 -3
  35. package/corpus/templates/tasks/app/components/custom-fields/editor/config/CurrencyConfigControl.tsx +5 -2
  36. package/corpus/templates/tasks/app/components/custom-fields/editor/config/NumberConfigControl.tsx +5 -2
  37. package/corpus/templates/tasks/app/components/custom-fields/editor/config/PercentConfigControl.tsx +4 -1
  38. package/corpus/templates/tasks/app/components/custom-fields/editor/config/SelectConfigControl.tsx +17 -7
  39. package/corpus/templates/tasks/app/components/custom-fields/editor/config/select-colors.ts +37 -9
  40. package/corpus/templates/tasks/app/components/custom-fields/field-types.ts +23 -0
  41. package/corpus/templates/tasks/app/components/inbox/InboxList.tsx +24 -16
  42. package/corpus/templates/tasks/app/components/inbox/InboxListPage.tsx +5 -3
  43. package/corpus/templates/tasks/app/components/inbox/InboxListRow.tsx +7 -5
  44. package/corpus/templates/tasks/app/components/layout/Header.tsx +10 -10
  45. package/corpus/templates/tasks/app/components/layout/Layout.tsx +11 -7
  46. package/corpus/templates/tasks/app/components/layout/Sidebar.tsx +17 -10
  47. package/corpus/templates/tasks/app/components/shared/AddListItemInput.tsx +14 -8
  48. package/corpus/templates/tasks/app/components/shared/AlertDialog.tsx +6 -2
  49. package/corpus/templates/tasks/app/components/shared/BulkDeleteDialog.tsx +13 -6
  50. package/corpus/templates/tasks/app/components/shared/ChipSelect.tsx +12 -5
  51. package/corpus/templates/tasks/app/components/shared/DeleteItemDialog.tsx +6 -6
  52. package/corpus/templates/tasks/app/components/shared/ListViewHeader.tsx +3 -1
  53. package/corpus/templates/tasks/app/components/shared/SidePanel.tsx +5 -2
  54. package/corpus/templates/tasks/app/components/shared/list/ListRowDragHandle.tsx +3 -1
  55. package/corpus/templates/tasks/app/components/shared/selection/ListSelectionBar.tsx +46 -20
  56. package/corpus/templates/tasks/app/components/shared/selection/ListSelectionHeaderToggle.tsx +3 -1
  57. package/corpus/templates/tasks/app/components/shared/selection/ListSelectionToolbar.tsx +10 -6
  58. package/corpus/templates/tasks/app/components/tasks/TaskList.tsx +24 -13
  59. package/corpus/templates/tasks/app/components/tasks/TaskListHeaderRow.tsx +4 -1
  60. package/corpus/templates/tasks/app/components/tasks/TaskListPage.tsx +5 -3
  61. package/corpus/templates/tasks/app/components/tasks/TaskListRow.tsx +15 -6
  62. package/corpus/templates/tasks/app/components/tasks/fields/TaskFieldsSidebar.tsx +11 -6
  63. package/corpus/templates/tasks/app/components/tasks/fields/TaskTitleSection.tsx +8 -3
  64. package/corpus/templates/tasks/app/components/tasks/fields/controls/RichTextValueControl.tsx +12 -6
  65. package/corpus/templates/tasks/app/components/tasks/fields/controls/SingleSelectValueControl.tsx +6 -1
  66. package/corpus/templates/tasks/app/i18n/en-US.ts +209 -0
  67. package/corpus/templates/tasks/app/i18n/index.ts +34 -0
  68. package/corpus/templates/tasks/app/root.tsx +34 -2
  69. package/corpus/templates/tasks/app/routes/extensions.$id.tsx +2 -1
  70. package/corpus/templates/tasks/app/routes/extensions._index.tsx +2 -1
  71. package/corpus/templates/tasks/app/routes/fields.tsx +2 -1
  72. package/corpus/templates/tasks/app/routes/inbox.tsx +2 -1
  73. package/corpus/templates/tasks/app/routes/tasks.tsx +2 -1
  74. package/corpus/templates/tasks/app/routes/team.tsx +6 -3
  75. package/corpus/templates/tasks/changelog/2026-07-20-tasks-is-now-available-in-10-additional-languages.md +6 -0
  76. package/dist/application-state/index.d.ts +2 -2
  77. package/dist/application-state/index.d.ts.map +1 -1
  78. package/dist/application-state/index.js +2 -2
  79. package/dist/application-state/index.js.map +1 -1
  80. package/dist/application-state/script-helpers.d.ts +3 -1
  81. package/dist/application-state/script-helpers.d.ts.map +1 -1
  82. package/dist/application-state/script-helpers.js +7 -1
  83. package/dist/application-state/script-helpers.js.map +1 -1
  84. package/dist/application-state/store.d.ts +7 -1
  85. package/dist/application-state/store.d.ts.map +1 -1
  86. package/dist/application-state/store.js +143 -17
  87. package/dist/application-state/store.js.map +1 -1
  88. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
  89. package/dist/client/extensions/EmbeddedExtension.js +65 -1
  90. package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
  91. package/dist/collab/awareness.d.ts +2 -2
  92. package/dist/collab/awareness.d.ts.map +1 -1
  93. package/dist/collab/routes.d.ts +1 -1
  94. package/dist/db/client.d.ts +7 -0
  95. package/dist/db/client.d.ts.map +1 -1
  96. package/dist/db/client.js +39 -1
  97. package/dist/db/client.js.map +1 -1
  98. package/dist/notifications/routes.d.ts +1 -1
  99. package/dist/observability/routes.d.ts +3 -3
  100. package/dist/progress/routes.d.ts +1 -1
  101. package/dist/secrets/routes.d.ts +6 -6
  102. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  103. package/dist/server/transcribe-voice.d.ts +1 -1
  104. package/package.json +1 -1
  105. package/src/application-state/index.ts +3 -0
  106. package/src/application-state/script-helpers.ts +12 -1
  107. package/src/application-state/store.ts +216 -15
  108. package/src/client/extensions/EmbeddedExtension.tsx +89 -1
  109. package/src/db/client.ts +44 -1
@@ -1,3 +1,4 @@
1
+ import { useT } from "@agent-native/core/client/i18n";
1
2
  import {
2
3
  IconBold,
3
4
  IconH1,
@@ -20,6 +21,7 @@ export function RichTextValueControl({
20
21
  disabled: boolean;
21
22
  onChange: (value: string) => void;
22
23
  }) {
24
+ const t = useT();
23
25
  const textareaRef = useRef<HTMLTextAreaElement | null>(null);
24
26
 
25
27
  function applyWrap(before: string, after = before, placeholder = "text") {
@@ -54,28 +56,32 @@ export function RichTextValueControl({
54
56
 
55
57
  const tools = [
56
58
  {
57
- label: "Bold",
59
+ label: t("taskFields.toolBold"),
58
60
  icon: IconBold,
59
61
  action: () => applyWrap("**", "**", "bold"),
60
62
  },
61
63
  {
62
- label: "Italic",
64
+ label: t("taskFields.toolItalic"),
63
65
  icon: IconItalic,
64
66
  action: () => applyWrap("_", "_", "italic"),
65
67
  },
66
- { label: "Heading", icon: IconH1, action: () => applyLinePrefix("## ") },
67
68
  {
68
- label: "Bulleted list",
69
+ label: t("taskFields.toolHeading"),
70
+ icon: IconH1,
71
+ action: () => applyLinePrefix("## "),
72
+ },
73
+ {
74
+ label: t("taskFields.toolBulletedList"),
69
75
  icon: IconList,
70
76
  action: () => applyLinePrefix("- "),
71
77
  },
72
78
  {
73
- label: "Numbered list",
79
+ label: t("taskFields.toolNumberedList"),
74
80
  icon: IconListNumbers,
75
81
  action: () => applyLinePrefix("1. "),
76
82
  },
77
83
  {
78
- label: "Link",
84
+ label: t("taskFields.toolLink"),
79
85
  icon: IconLink,
80
86
  action: () => applyWrap("[", "](https://example.com)", "link"),
81
87
  },
@@ -1,3 +1,5 @@
1
+ import { useT } from "@agent-native/core/client/i18n";
2
+
1
3
  import { selectColorClass } from "@/components/custom-fields/editor/config/select-colors";
2
4
  import { optionsFromConfig } from "@/components/custom-fields/editor/config/utils";
3
5
  import {
@@ -24,6 +26,7 @@ export function SingleSelectValueControl({
24
26
  disabled: boolean;
25
27
  onChange: (value: FieldValue | null) => void;
26
28
  }) {
29
+ const t = useT();
27
30
  const options = optionsFromConfig(field.config);
28
31
  const selected = typeof value === "string" ? value : EMPTY_SELECT_VALUE;
29
32
 
@@ -40,7 +43,9 @@ export function SingleSelectValueControl({
40
43
  </SelectTrigger>
41
44
  <SelectContent>
42
45
  <SelectGroup>
43
- <SelectItem value={EMPTY_SELECT_VALUE}>None</SelectItem>
46
+ <SelectItem value={EMPTY_SELECT_VALUE}>
47
+ {t("taskFields.noneOption")}
48
+ </SelectItem>
44
49
  {options.map((option) => (
45
50
  <SelectItem key={option.id} value={option.id}>
46
51
  <span className="flex items-center gap-2">
@@ -0,0 +1,209 @@
1
+ const messages = {
2
+ common: {
3
+ cancel: "Cancel",
4
+ delete: "Delete",
5
+ add: "Add",
6
+ noneSelected: "None selected",
7
+ closePanel: "Close panel",
8
+ editTitleAriaLabel: "Edit title",
9
+ rowActionsAriaLabel: "Actions for {{title}}",
10
+ reorderAriaLabel: "Reorder {{title}}",
11
+ removeAriaLabel: "Remove {{label}}",
12
+ showAll: "Show all",
13
+ markReady: "Mark ready",
14
+ },
15
+ sidebar: {
16
+ navInbox: "Inbox",
17
+ navTasks: "Tasks",
18
+ navFields: "Fields",
19
+ collapseSidebar: "Collapse sidebar",
20
+ expandSidebar: "Expand sidebar",
21
+ navigationTitle: "Navigation",
22
+ navigationDescription: "App navigation links",
23
+ },
24
+ header: {
25
+ openNavigation: "Open navigation",
26
+ pageTasks: "Tasks",
27
+ pageSettings: "Settings",
28
+ pageTeam: "Team",
29
+ pageExtension: "Extension",
30
+ pageExtensions: "Extensions",
31
+ },
32
+ agent: {
33
+ emptyState: "Ask the agent to inspect or change this app.",
34
+ suggestionCalendar: "Check my calendar and mails - any tasks for today?",
35
+ suggestionPrioritize: "Prioritise my tasks",
36
+ suggestionCleanHouse:
37
+ "I need to clean my house, create a list of tasks for it",
38
+ },
39
+ team: {
40
+ createOrgDescription:
41
+ "Set up a team to share this app with your colleagues.",
42
+ },
43
+ tasks: {
44
+ pageTitle: "Tasks",
45
+ pageDescription:
46
+ "Manage your task list, drag to reorder, or ask chat to add reminders.",
47
+ loadError: "Failed to load tasks.",
48
+ allCompleteHeading: "All tasks complete",
49
+ allCompleteDescription: "Toggle Show all to review completed tasks.",
50
+ emptyHeading: "No tasks yet",
51
+ emptyDescription: "Add one above or ask chat to create a task for you.",
52
+ listAriaLabel: "Tasks list",
53
+ bulkDeleteError: "Could not delete selected tasks.",
54
+ entitySingular: "task",
55
+ entityPlural: "tasks",
56
+ taskColumnHeader: "Task",
57
+ visibleTaskFieldsAriaLabel: "Visible task fields",
58
+ markCompleteAriaLabel: "Mark {{title}} complete",
59
+ markIncompleteAriaLabel: "Mark {{title}} incomplete",
60
+ addPlaceholder: "Add a task...",
61
+ addButtonLabel: "Add task",
62
+ addInputAriaLabel: "New task title",
63
+ addErrorMessage: "Failed to create task. Please try again.",
64
+ },
65
+ inbox: {
66
+ pageTitle: "Inbox",
67
+ pageDescription:
68
+ "Capture rough ideas here, then mark ready when they become tasks.",
69
+ loadError: "Failed to load inbox items.",
70
+ emptyHeading: "Inbox is empty",
71
+ emptyDescription:
72
+ "Add an item above or ask chat to capture something for triage.",
73
+ listAriaLabel: "Inbox list",
74
+ bulkDeleteError: "Could not delete selected inbox items.",
75
+ entitySingular: "inbox item",
76
+ entityPlural: "inbox items",
77
+ addPlaceholder: "Add to inbox...",
78
+ addButtonLabel: "Add item",
79
+ addInputAriaLabel: "New inbox item title",
80
+ addErrorMessage: "Failed to add inbox item. Please try again.",
81
+ },
82
+ fields: {
83
+ pageTitle: "Fields",
84
+ pageDescription: "Define the reusable fields that every task can fill in.",
85
+ loadError: "Failed to load fields.",
86
+ emptyHeading: "No fields yet",
87
+ emptyDescription: "Create a field above to start adding task structure.",
88
+ listAriaLabel: "Fields list",
89
+ createdToast: "Field created.",
90
+ createError: "Could not create field.",
91
+ deletedToast: "Deleted {{title}}.",
92
+ deletedWithValuesToast: "Deleted {{title}} and {{count}} task values.",
93
+ deleteError: "Could not delete field.",
94
+ taskCardFieldsLabel: "Task card fields",
95
+ addFieldButtonLabel: "Add field",
96
+ noFieldsSelectedLabel: "No fields selected",
97
+ entitySingular: "field",
98
+ deleteFieldDescription:
99
+ "This deletes the field and removes its values from every task.",
100
+ deleteFieldDescriptionWithTitle:
101
+ 'This deletes "{{title}}" and removes its values from every task.',
102
+ deleteFieldAriaLabel: "Delete {{title}}",
103
+ createNewFieldHeading: "Create new field",
104
+ fieldTitleLabel: "Field title",
105
+ newFieldTitlePlaceholder: "New field title",
106
+ fieldTypeAriaLabel: "Field type",
107
+ createButton: "Create",
108
+ currencyDescription: "Currency {{symbol}}",
109
+ decimalsDescription: "{{count}} decimals",
110
+ positiveOnlySuffix: "positive only",
111
+ optionsCountDescription: "{{count}} options",
112
+ types: {
113
+ text: "Text",
114
+ richText: "Rich text",
115
+ number: "Number",
116
+ percent: "Percent",
117
+ currency: "Currency",
118
+ singleSelect: "Single-select",
119
+ multiSelect: "Multi-select",
120
+ date: "Date",
121
+ },
122
+ },
123
+ fieldEditor: {
124
+ panelTitle: "Field",
125
+ closeLabel: "Close field editor",
126
+ updateError: "Could not update field.",
127
+ titleLabel: "Title",
128
+ requiredLabel: "Required",
129
+ editFieldTitleAriaLabel: "Edit field title",
130
+ symbolLabel: "Symbol",
131
+ precisionLabel: "Precision",
132
+ positiveOnlyLabel: "Positive numbers only",
133
+ optionsLabel: "Options",
134
+ optionNameAriaLabel: "Option {{index}} name",
135
+ optionColorAriaLabel: "Option {{name}} color",
136
+ removeOptionAriaLabel: "Remove {{name}}",
137
+ newOptionName: "Option {{index}}",
138
+ addOptionButton: "Add option",
139
+ colors: {
140
+ red: "Red",
141
+ orange: "Orange",
142
+ yellow: "Yellow",
143
+ green: "Green",
144
+ blue: "Blue",
145
+ purple: "Purple",
146
+ pink: "Pink",
147
+ gray: "Gray",
148
+ },
149
+ },
150
+ taskFields: {
151
+ panelTitle: "Fields",
152
+ panelSubtitle: "Task details",
153
+ closeLabel: "Close fields panel",
154
+ updateError: "Could not update task.",
155
+ noFieldsDefined: "No fields defined.",
156
+ titleLabel: "Title",
157
+ taskBadge: "Task",
158
+ editTaskTitleAriaLabel: "Edit task title",
159
+ noneOption: "None",
160
+ toolBold: "Bold",
161
+ toolItalic: "Italic",
162
+ toolHeading: "Heading",
163
+ toolBulletedList: "Bulleted list",
164
+ toolNumberedList: "Numbered list",
165
+ toolLink: "Link",
166
+ },
167
+ selection: {
168
+ select: "Select",
169
+ doneSelecting: "Done selecting",
170
+ selectAll: "Select all",
171
+ clearAll: "Clear all",
172
+ exitSelectionMode: "Exit selection mode",
173
+ markComplete: "Mark complete",
174
+ markIncomplete: "Mark incomplete",
175
+ completeLabel: "Complete",
176
+ incompleteLabel: "Incomplete",
177
+ selectedCount: "{{count}} selected",
178
+ selectedCountReorder: "{{count}} selected · drag to reorder",
179
+ tapToSelectTasks: "Tap tasks to select them.",
180
+ tapToSelectItems: "Tap items to select them.",
181
+ taskSelectionActionsAriaLabel: "Task selection actions",
182
+ inboxSelectionActionsAriaLabel: "Inbox selection actions",
183
+ couldNotMarkReady: "Could not mark selected items ready.",
184
+ couldNotUpdateTasks: "Could not update selected tasks.",
185
+ markedReadyOne: "Marked {{count}} item ready",
186
+ markedReadyOther: "Marked {{count}} items ready",
187
+ allTasksAlreadyComplete: "All selected tasks are already complete.",
188
+ allTasksAlreadyIncomplete: "All selected tasks are already incomplete.",
189
+ markedDone: "Marked {{count}} {{unit}} complete",
190
+ markedDoneWithSkipped:
191
+ "Marked {{count}} {{unit}} complete ({{skipped}} already complete)",
192
+ markedNotDone: "Marked {{count}} {{unit}} incomplete",
193
+ markedNotDoneWithSkipped:
194
+ "Marked {{count}} {{unit}} incomplete ({{skipped}} already incomplete)",
195
+ },
196
+ dialogs: {
197
+ deleteEntityTitle: "Delete {{entity}}?",
198
+ deleteItemDescription: "This removes the {{entity}} permanently.",
199
+ deleteItemDescriptionWithTitle: 'This removes "{{title}}" permanently.',
200
+ bulkDeleteDescriptionOne:
201
+ "This permanently removes the selected {{entity}}.",
202
+ bulkDeleteDescriptionOther:
203
+ "This permanently removes all {{count}} selected {{entity}}.",
204
+ andMore: "and {{count}} more",
205
+ deletedCount: "Deleted {{count}} {{entity}}",
206
+ },
207
+ };
208
+
209
+ export default messages;
@@ -0,0 +1,34 @@
1
+ import { type AgentNativeI18nCatalog } from "@agent-native/core/client/i18n";
2
+
3
+ import enUS from "./en-US";
4
+
5
+ export const i18nCatalog = {
6
+ sourceLocale: "en-US",
7
+ messages: enUS,
8
+ loadMessages: async (locale) => {
9
+ switch (locale) {
10
+ case "zh-CN":
11
+ return (await import("./zh-CN")).default;
12
+ case "zh-TW":
13
+ return (await import("./zh-TW")).default;
14
+ case "es-ES":
15
+ return (await import("./es-ES")).default;
16
+ case "fr-FR":
17
+ return (await import("./fr-FR")).default;
18
+ case "de-DE":
19
+ return (await import("./de-DE")).default;
20
+ case "ja-JP":
21
+ return (await import("./ja-JP")).default;
22
+ case "ko-KR":
23
+ return (await import("./ko-KR")).default;
24
+ case "pt-BR":
25
+ return (await import("./pt-BR")).default;
26
+ case "hi-IN":
27
+ return (await import("./hi-IN")).default;
28
+ case "ar-SA":
29
+ return (await import("./ar-SA")).default;
30
+ default:
31
+ return null;
32
+ }
33
+ },
34
+ } satisfies AgentNativeI18nCatalog;
@@ -5,6 +5,7 @@ import {
5
5
  AppProviders,
6
6
  createAgentNativeQueryClient,
7
7
  } from "@agent-native/core/client/hooks";
8
+ import { getLocaleInitScript } from "@agent-native/core/client/i18n";
8
9
  import {
9
10
  CommandMenu,
10
11
  useCommandMenuShortcut,
@@ -20,6 +21,7 @@ import type { LinksFunction } from "react-router";
20
21
  import { Layout as AppLayout } from "@/components/layout/Layout";
21
22
  import { Toaster } from "@/components/ui/sonner";
22
23
  import { useNavigationState } from "@/hooks/use-navigation-state";
24
+ import { i18nCatalog } from "@/i18n";
23
25
  import { APP_TITLE } from "@/lib/app-config";
24
26
  import { TAB_ID } from "@/lib/tab-id";
25
27
 
@@ -37,7 +39,31 @@ export const links: LinksFunction = () => [
37
39
  { rel: "stylesheet", href: stylesheet },
38
40
  ];
39
41
 
40
- const THEME_INIT_SCRIPT = getThemeInitScript();
42
+ const THEME_INIT_SCRIPT_SELECTOR = "script[data-agent-native-theme-init]";
43
+ const LOCALE_INIT_SCRIPT_SELECTOR = "script[data-agent-native-locale-init]";
44
+
45
+ function getHydrationStableThemeInitScript() {
46
+ if (typeof document !== "undefined") {
47
+ const existing = document.querySelector<HTMLScriptElement>(
48
+ THEME_INIT_SCRIPT_SELECTOR,
49
+ );
50
+ if (existing?.innerHTML) return existing.innerHTML;
51
+ }
52
+ return getThemeInitScript();
53
+ }
54
+
55
+ function getHydrationStableLocaleInitScript() {
56
+ if (typeof document !== "undefined") {
57
+ const existing = document.querySelector<HTMLScriptElement>(
58
+ LOCALE_INIT_SCRIPT_SELECTOR,
59
+ );
60
+ if (existing?.innerHTML) return existing.innerHTML;
61
+ }
62
+ return getLocaleInitScript();
63
+ }
64
+
65
+ const THEME_INIT_SCRIPT = getHydrationStableThemeInitScript();
66
+ const LOCALE_INIT_SCRIPT = getHydrationStableLocaleInitScript();
41
67
 
42
68
  export function Layout({ children }: { children: React.ReactNode }) {
43
69
  return (
@@ -49,9 +75,15 @@ export function Layout({ children }: { children: React.ReactNode }) {
49
75
  content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"
50
76
  />
51
77
  <script
78
+ data-agent-native-theme-init
52
79
  suppressHydrationWarning
53
80
  dangerouslySetInnerHTML={{ __html: THEME_INIT_SCRIPT }}
54
81
  />
82
+ <script
83
+ data-agent-native-locale-init
84
+ suppressHydrationWarning
85
+ dangerouslySetInnerHTML={{ __html: LOCALE_INIT_SCRIPT }}
86
+ />
55
87
  <link rel="manifest" href={appPath("/manifest.json")} />
56
88
  <meta name="theme-color" content="#18181B" />
57
89
  <meta name="mobile-web-app-capable" content="yes" />
@@ -122,7 +154,7 @@ function AppContent() {
122
154
  export default function Root() {
123
155
  const [queryClient] = useState(() => createAgentNativeQueryClient());
124
156
  return (
125
- <AppProviders queryClient={queryClient}>
157
+ <AppProviders queryClient={queryClient} i18n={{ catalog: i18nCatalog }}>
126
158
  <DbSyncSetup />
127
159
  <AppContent />
128
160
  </AppProviders>
@@ -1,9 +1,10 @@
1
1
  import { ExtensionViewerPage } from "@agent-native/core/client/extensions";
2
2
 
3
+ import messages from "@/i18n/en-US";
3
4
  import { APP_TITLE } from "@/lib/app-config";
4
5
 
5
6
  export function meta() {
6
- return [{ title: `Extension — ${APP_TITLE}` }];
7
+ return [{ title: `${messages.header.pageExtension} — ${APP_TITLE}` }];
7
8
  }
8
9
 
9
10
  export default function ExtensionViewerRoute() {
@@ -1,9 +1,10 @@
1
1
  import { ExtensionsListPage } from "@agent-native/core/client/extensions";
2
2
 
3
+ import messages from "@/i18n/en-US";
3
4
  import { APP_TITLE } from "@/lib/app-config";
4
5
 
5
6
  export function meta() {
6
- return [{ title: `Extensions — ${APP_TITLE}` }];
7
+ return [{ title: `${messages.header.pageExtensions} — ${APP_TITLE}` }];
7
8
  }
8
9
 
9
10
  export default function ExtensionsRoute() {
@@ -1,9 +1,10 @@
1
1
  import { FieldsPage } from "@/components/custom-fields/FieldsPage";
2
+ import messages from "@/i18n/en-US";
2
3
  import { APP_TITLE } from "@/lib/app-config";
3
4
 
4
5
  export function meta() {
5
6
  return [
6
- { title: `Fields · ${APP_TITLE}` },
7
+ { title: `${messages.fields.pageTitle} · ${APP_TITLE}` },
7
8
  {
8
9
  name: "description",
9
10
  content: "Define reusable custom fields for tasks.",
@@ -1,9 +1,10 @@
1
1
  import { InboxListPage } from "@/components/inbox/InboxListPage";
2
+ import messages from "@/i18n/en-US";
2
3
  import { APP_TITLE } from "@/lib/app-config";
3
4
 
4
5
  export function meta() {
5
6
  return [
6
- { title: `Inbox · ${APP_TITLE}` },
7
+ { title: `${messages.inbox.pageTitle} · ${APP_TITLE}` },
7
8
  {
8
9
  name: "description",
9
10
  content: "Capture rough ideas in the inbox before they become tasks.",
@@ -1,9 +1,10 @@
1
1
  import { TaskListPage } from "@/components/tasks/TaskListPage";
2
+ import messages from "@/i18n/en-US";
2
3
  import { APP_TITLE } from "@/lib/app-config";
3
4
 
4
5
  export function meta() {
5
6
  return [
6
- { title: `Tasks · ${APP_TITLE}` },
7
+ { title: `${messages.tasks.pageTitle} · ${APP_TITLE}` },
7
8
  {
8
9
  name: "description",
9
10
  content:
@@ -1,17 +1,20 @@
1
+ import { useT } from "@agent-native/core/client/i18n";
1
2
  import { TeamPage } from "@agent-native/core/client/org";
2
3
 
3
4
  import { useSetPageTitle } from "@/components/layout/HeaderActions";
5
+ import messages from "@/i18n/en-US";
4
6
  import { APP_TITLE } from "@/lib/app-config";
5
7
 
6
8
  export function meta() {
7
- return [{ title: `Team — ${APP_TITLE}` }];
9
+ return [{ title: `${messages.header.pageTeam} — ${APP_TITLE}` }];
8
10
  }
9
11
 
10
12
  export default function TeamRoute() {
11
- useSetPageTitle("Team");
13
+ const t = useT();
14
+ useSetPageTitle(t("header.pageTeam"));
12
15
  return (
13
16
  <main className="mx-auto w-full max-w-3xl px-4 py-6 sm:px-6 sm:py-10">
14
- <TeamPage createOrgDescription="Set up a team to share this app with your colleagues." />
17
+ <TeamPage createOrgDescription={t("team.createOrgDescription")} />
15
18
  </main>
16
19
  );
17
20
  }
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-07-20
4
+ ---
5
+
6
+ Tasks is now available in 10 additional languages
@@ -1,5 +1,5 @@
1
- export { appStateGet, appStateGetMany, appStatePut, appStateDelete, appStateCompareAndSet, appStateList, appStateDeleteByPrefix, } from "./store.js";
1
+ export { appStateGet, appStateGetMany, appStatePut, appStateDelete, appStateCompareAndSet, appStateCompareAndSetMany, appStateList, appStateDeleteByPrefix, type AppStateCompareAndSetOperation, } from "./store.js";
2
2
  export { getAppStateEmitter, emitAppStateChange, emitAppStateDelete, type AppStateEvent, } from "./emitter.js";
3
3
  export { getState, putState, deleteState, listComposeDrafts, getComposeDraft, putComposeDraft, deleteComposeDraft, deleteAllComposeDrafts, } from "./handlers.js";
4
- export { readAppState, writeAppState, deleteAppState, compareAndSetAppState, listAppState, deleteAppStateByPrefix, readAppStateForCurrentTab, writeAppStateForCurrentTab, appStateKeyForBrowserTab, getCurrentRequestBrowserTabId, } from "./script-helpers.js";
4
+ export { readAppState, writeAppState, deleteAppState, compareAndSetAppState, compareAndSetManyAppState, listAppState, deleteAppStateByPrefix, readAppStateForCurrentTab, writeAppStateForCurrentTab, appStateKeyForBrowserTab, getCurrentRequestBrowserTabId, } from "./script-helpers.js";
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/application-state/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,WAAW,EACX,eAAe,EACf,WAAW,EACX,cAAc,EACd,qBAAqB,EACrB,YAAY,EACZ,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,KAAK,aAAa,GACnB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,cAAc,EACd,qBAAqB,EACrB,YAAY,EACZ,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,EACxB,6BAA6B,GAC9B,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/application-state/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,WAAW,EACX,eAAe,EACf,WAAW,EACX,cAAc,EACd,qBAAqB,EACrB,yBAAyB,EACzB,YAAY,EACZ,sBAAsB,EACtB,KAAK,8BAA8B,GACpC,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,KAAK,aAAa,GACnB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,cAAc,EACd,qBAAqB,EACrB,yBAAyB,EACzB,YAAY,EACZ,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,EACxB,6BAA6B,GAC9B,MAAM,qBAAqB,CAAC"}
@@ -1,9 +1,9 @@
1
1
  // Store
2
- export { appStateGet, appStateGetMany, appStatePut, appStateDelete, appStateCompareAndSet, appStateList, appStateDeleteByPrefix, } from "./store.js";
2
+ export { appStateGet, appStateGetMany, appStatePut, appStateDelete, appStateCompareAndSet, appStateCompareAndSetMany, appStateList, appStateDeleteByPrefix, } from "./store.js";
3
3
  // Emitter (for SSE wiring)
4
4
  export { getAppStateEmitter, emitAppStateChange, emitAppStateDelete, } from "./emitter.js";
5
5
  // H3 route handlers (for templates)
6
6
  export { getState, putState, deleteState, listComposeDrafts, getComposeDraft, putComposeDraft, deleteComposeDraft, deleteAllComposeDrafts, } from "./handlers.js";
7
7
  // Script helpers
8
- export { readAppState, writeAppState, deleteAppState, compareAndSetAppState, listAppState, deleteAppStateByPrefix, readAppStateForCurrentTab, writeAppStateForCurrentTab, appStateKeyForBrowserTab, getCurrentRequestBrowserTabId, } from "./script-helpers.js";
8
+ export { readAppState, writeAppState, deleteAppState, compareAndSetAppState, compareAndSetManyAppState, listAppState, deleteAppStateByPrefix, readAppStateForCurrentTab, writeAppStateForCurrentTab, appStateKeyForBrowserTab, getCurrentRequestBrowserTabId, } from "./script-helpers.js";
9
9
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/application-state/index.ts"],"names":[],"mappings":"AAAA,QAAQ;AACR,OAAO,EACL,WAAW,EACX,eAAe,EACf,WAAW,EACX,cAAc,EACd,qBAAqB,EACrB,YAAY,EACZ,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAEpB,2BAA2B;AAC3B,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,GAEnB,MAAM,cAAc,CAAC;AAEtB,oCAAoC;AACpC,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAEvB,iBAAiB;AACjB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,cAAc,EACd,qBAAqB,EACrB,YAAY,EACZ,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,EACxB,6BAA6B,GAC9B,MAAM,qBAAqB,CAAC","sourcesContent":["// Store\nexport {\n appStateGet,\n appStateGetMany,\n appStatePut,\n appStateDelete,\n appStateCompareAndSet,\n appStateList,\n appStateDeleteByPrefix,\n} from \"./store.js\";\n\n// Emitter (for SSE wiring)\nexport {\n getAppStateEmitter,\n emitAppStateChange,\n emitAppStateDelete,\n type AppStateEvent,\n} from \"./emitter.js\";\n\n// H3 route handlers (for templates)\nexport {\n getState,\n putState,\n deleteState,\n listComposeDrafts,\n getComposeDraft,\n putComposeDraft,\n deleteComposeDraft,\n deleteAllComposeDrafts,\n} from \"./handlers.js\";\n\n// Script helpers\nexport {\n readAppState,\n writeAppState,\n deleteAppState,\n compareAndSetAppState,\n listAppState,\n deleteAppStateByPrefix,\n readAppStateForCurrentTab,\n writeAppStateForCurrentTab,\n appStateKeyForBrowserTab,\n getCurrentRequestBrowserTabId,\n} from \"./script-helpers.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/application-state/index.ts"],"names":[],"mappings":"AAAA,QAAQ;AACR,OAAO,EACL,WAAW,EACX,eAAe,EACf,WAAW,EACX,cAAc,EACd,qBAAqB,EACrB,yBAAyB,EACzB,YAAY,EACZ,sBAAsB,GAEvB,MAAM,YAAY,CAAC;AAEpB,2BAA2B;AAC3B,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,GAEnB,MAAM,cAAc,CAAC;AAEtB,oCAAoC;AACpC,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAEvB,iBAAiB;AACjB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,cAAc,EACd,qBAAqB,EACrB,yBAAyB,EACzB,YAAY,EACZ,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,EACxB,6BAA6B,GAC9B,MAAM,qBAAqB,CAAC","sourcesContent":["// Store\nexport {\n appStateGet,\n appStateGetMany,\n appStatePut,\n appStateDelete,\n appStateCompareAndSet,\n appStateCompareAndSetMany,\n appStateList,\n appStateDeleteByPrefix,\n type AppStateCompareAndSetOperation,\n} from \"./store.js\";\n\n// Emitter (for SSE wiring)\nexport {\n getAppStateEmitter,\n emitAppStateChange,\n emitAppStateDelete,\n type AppStateEvent,\n} from \"./emitter.js\";\n\n// H3 route handlers (for templates)\nexport {\n getState,\n putState,\n deleteState,\n listComposeDrafts,\n getComposeDraft,\n putComposeDraft,\n deleteComposeDraft,\n deleteAllComposeDrafts,\n} from \"./handlers.js\";\n\n// Script helpers\nexport {\n readAppState,\n writeAppState,\n deleteAppState,\n compareAndSetAppState,\n compareAndSetManyAppState,\n listAppState,\n deleteAppStateByPrefix,\n readAppStateForCurrentTab,\n writeAppStateForCurrentTab,\n appStateKeyForBrowserTab,\n getCurrentRequestBrowserTabId,\n} from \"./script-helpers.js\";\n"]}
@@ -11,10 +11,12 @@
11
11
  * reliably identify the caller. Only CLI scripts (single-user, no HTTP
12
12
  * context) should fall through to the env var.
13
13
  */
14
+ import { type AppStateCompareAndSetOperation } from "./store.js";
14
15
  export declare function readAppState(key: string): Promise<Record<string, unknown> | null>;
15
16
  export declare function writeAppState(key: string, value: Record<string, unknown>): Promise<void>;
16
17
  export declare function deleteAppState(key: string): Promise<boolean>;
17
- export declare function compareAndSetAppState(key: string, expectedValue: Record<string, unknown>, nextValue: Record<string, unknown> | null): Promise<boolean>;
18
+ export declare function compareAndSetAppState(key: string, expectedValue: Record<string, unknown> | null, nextValue: Record<string, unknown> | null): Promise<boolean>;
19
+ export declare function compareAndSetManyAppState(operations: readonly AppStateCompareAndSetOperation[]): Promise<boolean>;
18
20
  export declare function listAppState(prefix: string): Promise<Array<{
19
21
  key: string;
20
22
  value: Record<string, unknown>;
@@ -1 +1 @@
1
- {"version":3,"file":"script-helpers.d.ts","sourceRoot":"","sources":["../../src/application-state/script-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAsCH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAGzC;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAKlE;AAED,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GACxC,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAAC,CAGjE;AAED,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAK5E;AAUD;;;;GAIG;AACH,wBAAgB,6BAA6B,IAAI,MAAM,GAAG,IAAI,CAM7D;AAED,uEAAuE;AACvE,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,OAAO,GACpB,MAAM,CAGR;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;IAAE,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAE,GACvC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAQzC;AAED,oEAAoE;AACpE,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,IAAI,CAAC,CAGf"}
1
+ {"version":3,"file":"script-helpers.d.ts","sourceRoot":"","sources":["../../src/application-state/script-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAQL,KAAK,8BAA8B,EACpC,MAAM,YAAY,CAAC;AA4BpB,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAGzC;AAED,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAKlE;AAED,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EAC7C,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GACxC,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,SAAS,8BAA8B,EAAE,GACpD,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAAC,CAGjE;AAED,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAK5E;AAUD;;;;GAIG;AACH,wBAAgB,6BAA6B,IAAI,MAAM,GAAG,IAAI,CAM7D;AAED,uEAAuE;AACvE,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,OAAO,GACpB,MAAM,CAGR;AAED;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE;IAAE,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAE,GACvC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAQzC;AAED,oEAAoE;AACpE,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,IAAI,CAAC,CAGf"}
@@ -12,7 +12,7 @@
12
12
  * context) should fall through to the env var.
13
13
  */
14
14
  import { getRequestRunContext } from "../server/request-context.js";
15
- import { appStateGet, appStatePut, appStateDelete, appStateCompareAndSet, appStateList, appStateDeleteByPrefix, } from "./store.js";
15
+ import { appStateGet, appStatePut, appStateDelete, appStateCompareAndSet, appStateCompareAndSetMany, appStateList, appStateDeleteByPrefix, } from "./store.js";
16
16
  /**
17
17
  * Resolve session ID for the current caller.
18
18
  *
@@ -58,6 +58,12 @@ export async function compareAndSetAppState(key, expectedValue, nextValue) {
58
58
  requestSource: "agent",
59
59
  });
60
60
  }
61
+ export async function compareAndSetManyAppState(operations) {
62
+ const sessionId = await resolveSessionId();
63
+ return appStateCompareAndSetMany(sessionId, operations, {
64
+ requestSource: "agent",
65
+ });
66
+ }
61
67
  export async function listAppState(prefix) {
62
68
  const sessionId = await resolveSessionId();
63
69
  return appStateList(sessionId, prefix);
@@ -1 +1 @@
1
- {"version":3,"file":"script-helpers.js","sourceRoot":"","sources":["../../src/application-state/script-helpers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Application state helpers for use in scripts and actions.\n *\n * The session ID determines which user's application state is read/written.\n * Resolution order:\n * 1. Per-request context (AsyncLocalStorage) — set by the HTTP handler\n * 2. AGENT_USER_EMAIL env var — CLI scripts only\n *\n * The per-request context is critical in multi-user deployments: the env var\n * is process-global and gets overwritten by concurrent requests, so it cannot\n * reliably identify the caller. Only CLI scripts (single-user, no HTTP\n * context) should fall through to the env var.\n */\n\nimport { getRequestRunContext } from \"../server/request-context.js\";\nimport {\n appStateGet,\n appStatePut,\n appStateDelete,\n appStateCompareAndSet,\n appStateList,\n appStateDeleteByPrefix,\n} from \"./store.js\";\n\n/**\n * Resolve session ID for the current caller.\n *\n * In an HTTP/action context, uses the per-request user email from\n * AsyncLocalStorage so concurrent users don't collide. In a CLI context\n * (no request), falls back to AGENT_USER_EMAIL. Throws when neither is\n * present — application state must be scoped to a real identity.\n */\nasync function resolveSessionId(): Promise<string> {\n try {\n const { getRequestUserEmail } =\n await import(\"../server/request-context.js\");\n const ctxEmail = getRequestUserEmail();\n if (ctxEmail) return ctxEmail;\n } catch {\n // request-context not available — fall through to env var\n }\n\n const email = process.env.AGENT_USER_EMAIL;\n if (email) return email;\n\n throw new Error(\n \"Application state access requires an authenticated request context or AGENT_USER_EMAIL env var\",\n );\n}\n\nexport async function readAppState(\n key: string,\n): Promise<Record<string, unknown> | null> {\n const sessionId = await resolveSessionId();\n return appStateGet(sessionId, key);\n}\n\nexport async function writeAppState(\n key: string,\n value: Record<string, unknown>,\n): Promise<void> {\n const sessionId = await resolveSessionId();\n return appStatePut(sessionId, key, value, {\n requestSource: \"agent\",\n });\n}\n\nexport async function deleteAppState(key: string): Promise<boolean> {\n const sessionId = await resolveSessionId();\n return appStateDelete(sessionId, key, {\n requestSource: \"agent\",\n });\n}\n\nexport async function compareAndSetAppState(\n key: string,\n expectedValue: Record<string, unknown>,\n nextValue: Record<string, unknown> | null,\n): Promise<boolean> {\n const sessionId = await resolveSessionId();\n return appStateCompareAndSet(sessionId, key, expectedValue, nextValue, {\n requestSource: \"agent\",\n });\n}\n\nexport async function listAppState(\n prefix: string,\n): Promise<Array<{ key: string; value: Record<string, unknown> }>> {\n const sessionId = await resolveSessionId();\n return appStateList(sessionId, prefix);\n}\n\nexport async function deleteAppStateByPrefix(prefix: string): Promise<number> {\n const sessionId = await resolveSessionId();\n return appStateDeleteByPrefix(sessionId, prefix, {\n requestSource: \"agent\",\n });\n}\n\nconst SAFE_TAB_ID_RE = /^[A-Za-z0-9_-]{1,96}$/;\n\nfunction normalizeBrowserTabId(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n const trimmed = value.trim();\n return SAFE_TAB_ID_RE.test(trimmed) ? trimmed : null;\n}\n\n/**\n * Browser tab id for the current request, if the client sent one. Used to\n * scope ambient UI state (navigation, selection, etc.) so a chat from one tab\n * reads that tab's state instead of whichever tab wrote the global key last.\n */\nexport function getCurrentRequestBrowserTabId(): string | null {\n try {\n return normalizeBrowserTabId(getRequestRunContext()?.browserTabId);\n } catch {\n return null;\n }\n}\n\n/** `key:<tabId>` when a browser tab id is present, otherwise `key`. */\nexport function appStateKeyForBrowserTab(\n key: string,\n browserTabId: unknown,\n): string {\n const normalized = normalizeBrowserTabId(browserTabId);\n return normalized ? `${key}:${normalized}` : key;\n}\n\n/**\n * Read application state scoped to the requesting browser tab. Reads the\n * tab-scoped key first and (by default) falls back to the global key so\n * CLI/external agents and pre-scoping clients keep working.\n */\nexport async function readAppStateForCurrentTab(\n key: string,\n options?: { fallbackToGlobal?: boolean },\n): Promise<Record<string, unknown> | null> {\n const tabKey = appStateKeyForBrowserTab(key, getCurrentRequestBrowserTabId());\n if (tabKey !== key) {\n const scoped = await readAppState(tabKey).catch(() => null);\n if (scoped) return scoped;\n if (options?.fallbackToGlobal === false) return null;\n }\n return readAppState(key);\n}\n\n/** Write application state scoped to the requesting browser tab. */\nexport async function writeAppStateForCurrentTab(\n key: string,\n value: Record<string, unknown>,\n): Promise<void> {\n const tabKey = appStateKeyForBrowserTab(key, getCurrentRequestBrowserTabId());\n return writeAppState(tabKey, value);\n}\n"]}
1
+ {"version":3,"file":"script-helpers.js","sourceRoot":"","sources":["../../src/application-state/script-helpers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Application state helpers for use in scripts and actions.\n *\n * The session ID determines which user's application state is read/written.\n * Resolution order:\n * 1. Per-request context (AsyncLocalStorage) — set by the HTTP handler\n * 2. AGENT_USER_EMAIL env var — CLI scripts only\n *\n * The per-request context is critical in multi-user deployments: the env var\n * is process-global and gets overwritten by concurrent requests, so it cannot\n * reliably identify the caller. Only CLI scripts (single-user, no HTTP\n * context) should fall through to the env var.\n */\n\nimport { getRequestRunContext } from \"../server/request-context.js\";\nimport {\n appStateGet,\n appStatePut,\n appStateDelete,\n appStateCompareAndSet,\n appStateCompareAndSetMany,\n appStateList,\n appStateDeleteByPrefix,\n type AppStateCompareAndSetOperation,\n} from \"./store.js\";\n\n/**\n * Resolve session ID for the current caller.\n *\n * In an HTTP/action context, uses the per-request user email from\n * AsyncLocalStorage so concurrent users don't collide. In a CLI context\n * (no request), falls back to AGENT_USER_EMAIL. Throws when neither is\n * present — application state must be scoped to a real identity.\n */\nasync function resolveSessionId(): Promise<string> {\n try {\n const { getRequestUserEmail } =\n await import(\"../server/request-context.js\");\n const ctxEmail = getRequestUserEmail();\n if (ctxEmail) return ctxEmail;\n } catch {\n // request-context not available — fall through to env var\n }\n\n const email = process.env.AGENT_USER_EMAIL;\n if (email) return email;\n\n throw new Error(\n \"Application state access requires an authenticated request context or AGENT_USER_EMAIL env var\",\n );\n}\n\nexport async function readAppState(\n key: string,\n): Promise<Record<string, unknown> | null> {\n const sessionId = await resolveSessionId();\n return appStateGet(sessionId, key);\n}\n\nexport async function writeAppState(\n key: string,\n value: Record<string, unknown>,\n): Promise<void> {\n const sessionId = await resolveSessionId();\n return appStatePut(sessionId, key, value, {\n requestSource: \"agent\",\n });\n}\n\nexport async function deleteAppState(key: string): Promise<boolean> {\n const sessionId = await resolveSessionId();\n return appStateDelete(sessionId, key, {\n requestSource: \"agent\",\n });\n}\n\nexport async function compareAndSetAppState(\n key: string,\n expectedValue: Record<string, unknown> | null,\n nextValue: Record<string, unknown> | null,\n): Promise<boolean> {\n const sessionId = await resolveSessionId();\n return appStateCompareAndSet(sessionId, key, expectedValue, nextValue, {\n requestSource: \"agent\",\n });\n}\n\nexport async function compareAndSetManyAppState(\n operations: readonly AppStateCompareAndSetOperation[],\n): Promise<boolean> {\n const sessionId = await resolveSessionId();\n return appStateCompareAndSetMany(sessionId, operations, {\n requestSource: \"agent\",\n });\n}\n\nexport async function listAppState(\n prefix: string,\n): Promise<Array<{ key: string; value: Record<string, unknown> }>> {\n const sessionId = await resolveSessionId();\n return appStateList(sessionId, prefix);\n}\n\nexport async function deleteAppStateByPrefix(prefix: string): Promise<number> {\n const sessionId = await resolveSessionId();\n return appStateDeleteByPrefix(sessionId, prefix, {\n requestSource: \"agent\",\n });\n}\n\nconst SAFE_TAB_ID_RE = /^[A-Za-z0-9_-]{1,96}$/;\n\nfunction normalizeBrowserTabId(value: unknown): string | null {\n if (typeof value !== \"string\") return null;\n const trimmed = value.trim();\n return SAFE_TAB_ID_RE.test(trimmed) ? trimmed : null;\n}\n\n/**\n * Browser tab id for the current request, if the client sent one. Used to\n * scope ambient UI state (navigation, selection, etc.) so a chat from one tab\n * reads that tab's state instead of whichever tab wrote the global key last.\n */\nexport function getCurrentRequestBrowserTabId(): string | null {\n try {\n return normalizeBrowserTabId(getRequestRunContext()?.browserTabId);\n } catch {\n return null;\n }\n}\n\n/** `key:<tabId>` when a browser tab id is present, otherwise `key`. */\nexport function appStateKeyForBrowserTab(\n key: string,\n browserTabId: unknown,\n): string {\n const normalized = normalizeBrowserTabId(browserTabId);\n return normalized ? `${key}:${normalized}` : key;\n}\n\n/**\n * Read application state scoped to the requesting browser tab. Reads the\n * tab-scoped key first and (by default) falls back to the global key so\n * CLI/external agents and pre-scoping clients keep working.\n */\nexport async function readAppStateForCurrentTab(\n key: string,\n options?: { fallbackToGlobal?: boolean },\n): Promise<Record<string, unknown> | null> {\n const tabKey = appStateKeyForBrowserTab(key, getCurrentRequestBrowserTabId());\n if (tabKey !== key) {\n const scoped = await readAppState(tabKey).catch(() => null);\n if (scoped) return scoped;\n if (options?.fallbackToGlobal === false) return null;\n }\n return readAppState(key);\n}\n\n/** Write application state scoped to the requesting browser tab. */\nexport async function writeAppStateForCurrentTab(\n key: string,\n value: Record<string, unknown>,\n): Promise<void> {\n const tabKey = appStateKeyForBrowserTab(key, getCurrentRequestBrowserTabId());\n return writeAppState(tabKey, value);\n}\n"]}
@@ -8,7 +8,13 @@ export declare function appStateGet(sessionId: string, key: string): Promise<Rec
8
8
  export declare function appStateGetMany(sessionId: string, keys: readonly string[]): Promise<Record<string, Record<string, unknown> | null>>;
9
9
  export declare function appStatePut(sessionId: string, key: string, value: Record<string, unknown>, options?: StoreWriteOptions): Promise<void>;
10
10
  export declare function appStateDelete(sessionId: string, key: string, options?: StoreWriteOptions): Promise<boolean>;
11
- export declare function appStateCompareAndSet(sessionId: string, key: string, expectedValue: Record<string, unknown>, nextValue: Record<string, unknown> | null, options?: StoreWriteOptions): Promise<boolean>;
11
+ export declare function appStateCompareAndSet(sessionId: string, key: string, expectedValue: Record<string, unknown> | null, nextValue: Record<string, unknown> | null, options?: StoreWriteOptions): Promise<boolean>;
12
+ export interface AppStateCompareAndSetOperation {
13
+ key: string;
14
+ expectedValue: Record<string, unknown> | null;
15
+ nextValue: Record<string, unknown> | null;
16
+ }
17
+ export declare function appStateCompareAndSetMany(sessionId: string, operations: readonly AppStateCompareAndSetOperation[], options?: StoreWriteOptions): Promise<boolean>;
12
18
  export declare function appStateList(sessionId: string, keyPrefix: string): Promise<Array<{
13
19
  key: string;
14
20
  value: Record<string, unknown>;