@agent-native/dispatch 0.38.6 → 0.38.7-nightly-20260924034232

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.
@@ -50,6 +50,7 @@ declare const _default: import("node_modules/@agent-native/core/dist").ActionDef
50
50
  id?: undefined;
51
51
  deleted?: undefined;
52
52
  provider?: undefined;
53
+ found?: undefined;
53
54
  providers: {
54
55
  id: string;
55
56
  label: string;
@@ -60,7 +61,6 @@ declare const _default: import("node_modules/@agent-native/core/dist").ActionDef
60
61
  updatedAt: number;
61
62
  }[];
62
63
  count: number;
63
- found?: undefined;
64
64
  registered?: undefined;
65
65
  label?: undefined;
66
66
  message?: undefined;
@@ -86,9 +86,9 @@ declare const _default: import("node_modules/@agent-native/core/dist").ActionDef
86
86
  message?: undefined;
87
87
  } | {
88
88
  provider?: undefined;
89
+ found?: undefined;
89
90
  providers?: undefined;
90
91
  count?: undefined;
91
- found?: undefined;
92
92
  deleted: boolean;
93
93
  id: string;
94
94
  registered?: undefined;
@@ -97,9 +97,9 @@ declare const _default: import("node_modules/@agent-native/core/dist").ActionDef
97
97
  } | {
98
98
  deleted?: undefined;
99
99
  provider?: undefined;
100
+ found?: undefined;
100
101
  providers?: undefined;
101
102
  count?: undefined;
102
- found?: undefined;
103
103
  registered: boolean;
104
104
  id: string;
105
105
  label: string;
@@ -1 +1 @@
1
- {"version":3,"file":"usage-alerts-panel.d.ts","sourceRoot":"","sources":["../../src/components/usage-alerts-panel.tsx"],"names":[],"mappings":"AAuBA,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,qBAAqB;IAC7B,KAAK,EAAE,MAAM,GAAG,WAAW,CAAC;IAC5B,UAAU,CAAC,EAAE,mBAAmB,EAAE,CAAC;CACpC;AA2LD,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,UAAe,GAChB,EAAE,qBAAqB,mDA+NvB"}
1
+ {"version":3,"file":"usage-alerts-panel.d.ts","sourceRoot":"","sources":["../../src/components/usage-alerts-panel.tsx"],"names":[],"mappings":"AAwBA,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,qBAAqB;IAC7B,KAAK,EAAE,MAAM,GAAG,WAAW,CAAC;IAC5B,UAAU,CAAC,EAAE,mBAAmB,EAAE,CAAC;CACpC;AA6LD,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,UAAe,GAChB,EAAE,qBAAqB,mDAqOvB"}
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useActionMutation, useActionQuery, } from "@agent-native/core/client/hooks";
3
+ import { useT } from "@agent-native/core/client/i18n";
3
4
  import { IconBell, IconMail, IconPencil, IconX } from "@tabler/icons-react";
4
5
  import { useMemo, useState } from "react";
5
6
  import { toast } from "sonner";
@@ -56,9 +57,11 @@ function defaultDraft() {
56
57
  };
57
58
  }
58
59
  function AlertEditor({ draft, appOptions, isExisting, onChange, onSave, onCancel, isPending, }) {
59
- return (_jsxs("div", { className: "grid gap-3 rounded-md border bg-background/60 p-3 sm:grid-cols-2 lg:grid-cols-5", children: [_jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: "App" }), _jsxs(Select, { value: draft.appId || "all", onValueChange: (value) => onChange({ appId: value === "all" ? "" : value }), disabled: isExisting, children: [_jsx(SelectTrigger, { className: "h-9 text-sm", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "All apps" }), appOptions.map((app) => (_jsx(SelectItem, { value: app.id, children: app.label }, app.id)))] })] })] }), _jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: "Unit" }), _jsxs(Select, { value: draft.unit, onValueChange: (value) => onChange({ unit: value }), children: [_jsx(SelectTrigger, { className: "h-9 text-sm", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "usd", children: "Dollars" }), _jsx(SelectItem, { value: "builder-credits", children: "Credits" }), _jsx(SelectItem, { value: "tokens", children: "Tokens" })] })] })] }), _jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: "Limit" }), _jsx(Input, { type: "number", min: "0.01", step: draft.unit === "tokens" ? "1" : "0.01", value: draft.limit, onChange: (event) => onChange({ limit: event.target.value }), className: "h-9 text-sm" })] }), _jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: "Period" }), _jsxs(Select, { value: draft.period, onValueChange: (value) => onChange({ period: value }), children: [_jsx(SelectTrigger, { className: "h-9 text-sm", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "day", children: "Per day" }), _jsx(SelectItem, { value: "month", children: "Per month" })] })] })] }), _jsxs("div", { className: "flex flex-wrap items-end gap-3 pb-1 text-xs text-muted-foreground", children: [_jsxs("label", { className: "inline-flex items-center gap-2", children: [_jsx(Checkbox, { checked: draft.inApp, onCheckedChange: (checked) => onChange({ inApp: checked === true }) }), "In app"] }), _jsxs("label", { className: "inline-flex items-center gap-2", children: [_jsx(Checkbox, { checked: draft.email, onCheckedChange: (checked) => onChange({ email: checked === true }) }), "Email"] }), _jsx(Button, { type: "button", size: "sm", className: "ms-auto h-8", onClick: onSave, disabled: isPending, children: "Save" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "h-8", onClick: onCancel, children: "Cancel" })] })] }));
60
+ const t = useT();
61
+ return (_jsxs("div", { className: "grid gap-3 rounded-md border bg-background/60 p-3 sm:grid-cols-2 lg:grid-cols-5", children: [_jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: "App" }), _jsxs(Select, { value: draft.appId || "all", onValueChange: (value) => onChange({ appId: value === "all" ? "" : value }), disabled: isExisting, children: [_jsx(SelectTrigger, { className: "h-9 text-sm", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "all", children: "All apps" }), appOptions.map((app) => (_jsx(SelectItem, { value: app.id, children: app.label }, app.id)))] })] })] }), _jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: "Unit" }), _jsxs(Select, { value: draft.unit, onValueChange: (value) => onChange({ unit: value }), children: [_jsx(SelectTrigger, { className: "h-9 text-sm", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "usd", children: "Dollars" }), _jsx(SelectItem, { value: "builder-credits", children: "Credits" }), _jsx(SelectItem, { value: "tokens", children: "Tokens" })] })] })] }), _jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: t("dispatch.pages.usageAlertThreshold") }), _jsx(Input, { type: "number", min: "0.01", step: draft.unit === "tokens" ? "1" : "0.01", value: draft.limit, onChange: (event) => onChange({ limit: event.target.value }), className: "h-9 text-sm" })] }), _jsxs("label", { className: "space-y-1.5 text-xs text-muted-foreground", children: [_jsx("span", { children: "Period" }), _jsxs(Select, { value: draft.period, onValueChange: (value) => onChange({ period: value }), children: [_jsx(SelectTrigger, { className: "h-9 text-sm", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "day", children: "Per day" }), _jsx(SelectItem, { value: "month", children: "Per month" })] })] })] }), _jsxs("div", { className: "flex flex-wrap items-end gap-3 pb-1 text-xs text-muted-foreground", children: [_jsxs("label", { className: "inline-flex items-center gap-2", children: [_jsx(Checkbox, { checked: draft.inApp, onCheckedChange: (checked) => onChange({ inApp: checked === true }) }), "In app"] }), _jsxs("label", { className: "inline-flex items-center gap-2", children: [_jsx(Checkbox, { checked: draft.email, onCheckedChange: (checked) => onChange({ email: checked === true }) }), "Email"] }), _jsx(Button, { type: "button", size: "sm", className: "ms-auto h-8", onClick: onSave, disabled: isPending, children: "Save" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "h-8", onClick: onCancel, children: "Cancel" })] })] }));
60
62
  }
61
63
  export function UsageAlertsPanel({ scope, appOptions = [], }) {
64
+ const t = useT();
62
65
  const query = useActionQuery("get-usage-alerts", { scope });
63
66
  const mutation = useActionMutation("manage-usage-alert", {
64
67
  onSuccess: () => {
@@ -99,16 +102,22 @@ export function UsageAlertsPanel({ scope, appOptions = [], }) {
99
102
  enabled: true,
100
103
  });
101
104
  }
102
- return (_jsxs("section", { className: "rounded-lg bg-card", children: [_jsxs("div", { className: "flex items-start justify-between gap-3 border-b px-4 py-3", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-sm font-semibold text-foreground", children: "Usage alerts" }), _jsx("p", { className: "mt-1 max-w-2xl text-xs leading-5 text-muted-foreground", children: "Get a quiet in-app and email notice when usage crosses a daily or monthly limit." })] }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setEditing(defaultDraft()), disabled: mutation.isPending, children: "Add alert" })] }), _jsxs("div", { className: "divide-y", children: [query.isLoading && rules.length === 0 ? (_jsx("div", { className: "space-y-3 px-4 py-5", children: Array.from({ length: 3 }).map((_, index) => (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-4 w-2/3 max-w-sm" }), _jsx(Skeleton, { className: "h-3 w-1/2 max-w-xs" })] }, index))) })) : null, query.isError ? (_jsxs("div", { className: "px-4 py-6 text-sm text-destructive", children: ["Usage alerts are unavailable for this scope.", " ", query.error instanceof Error ? query.error.message : "Try again."] })) : null, !query.isLoading && !query.isError && rules.length === 0 ? (_jsx("div", { className: "px-4 py-6 text-sm text-muted-foreground", children: "No alerts yet. The recommended default is $100 per day across all apps." })) : null, rules.map((rule) => {
105
+ return (_jsxs("section", { className: "rounded-lg bg-card", children: [_jsxs("div", { className: "flex items-start justify-between gap-3 border-b px-4 py-3", children: [_jsxs("div", { children: [_jsx("h2", { className: "text-sm font-semibold text-foreground", children: "Usage alerts" }), _jsx("p", { className: "mt-1 max-w-2xl text-xs leading-5 text-muted-foreground", children: t("dispatch.pages.usageAlertDescription") })] }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setEditing(defaultDraft()), disabled: mutation.isPending, children: "Add alert" })] }), _jsxs("div", { className: "divide-y", children: [query.isLoading && rules.length === 0 ? (_jsx("div", { className: "space-y-3 px-4 py-5", children: Array.from({ length: 3 }).map((_, index) => (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-4 w-2/3 max-w-sm" }), _jsx(Skeleton, { className: "h-3 w-1/2 max-w-xs" })] }, index))) })) : null, query.isError ? (_jsxs("div", { className: "px-4 py-6 text-sm text-destructive", children: ["Usage alerts are unavailable for this scope.", " ", query.error instanceof Error ? query.error.message : "Try again."] })) : null, !query.isLoading && !query.isError && rules.length === 0 ? (_jsx("div", { className: "px-4 py-6 text-sm text-muted-foreground", children: t("dispatch.pages.usageAlertEmpty") })) : null, rules.map((rule) => {
103
106
  const isEditing = editing?.ruleId === rule.id;
104
107
  const target = rule.appId
105
108
  ? appLabels.get(rule.appId) || rule.appId
106
109
  : "All apps";
107
110
  return (_jsxs("div", { className: "px-4 py-4", children: [_jsxs("div", { className: "flex flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "text-sm font-medium text-foreground", children: target }), rule.isDefault ? (_jsx(Badge, { variant: "secondary", children: "Default" })) : null, _jsx(Badge, { variant: rule.status === "triggered" ? "destructive" : "outline", children: rule.status === "triggered"
108
- ? "Over limit"
111
+ ? t("dispatch.pages.usageAlertReached")
109
112
  : rule.status === "dismissed"
110
113
  ? "Dismissed"
111
- : "On track" })] }), _jsxs("div", { className: "mt-1 text-xs text-muted-foreground", children: [formatAlertValue(rule, rule.current), " of", " ", formatAlertValue(rule, rule.limit), " \u00B7 per ", rule.period, rule.percent > 0 ? " · " + rule.percent + "%" : ""] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: rule.enabled, onCheckedChange: (checked) => mutation.mutate({
114
+ : "On track" })] }), _jsxs("div", { className: "mt-1 text-xs text-muted-foreground", children: [t("dispatch.pages.usageAlertCurrentAndThreshold", {
115
+ current: formatAlertValue(rule, rule.current),
116
+ threshold: formatAlertValue(rule, rule.limit),
117
+ period: t(rule.period === "day"
118
+ ? "dispatch.pages.usageAlertDay"
119
+ : "dispatch.pages.usageAlertMonth"),
120
+ }), rule.percent > 0 ? " · " + rule.percent + "%" : ""] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: rule.enabled, onCheckedChange: (checked) => mutation.mutate({
112
121
  operation: "set-enabled",
113
122
  scope,
114
123
  ruleId: rule.id,
@@ -1 +1 @@
1
- {"version":3,"file":"usage-alerts-panel.js","sourceRoot":"","sources":["../../src/components/usage-alerts-panel.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { UsageAlertRule } from \"@agent-native/core\";\nimport {\n useActionMutation,\n useActionQuery,\n} from \"@agent-native/core/client/hooks\";\nimport { IconBell, IconMail, IconPencil, IconX } from \"@tabler/icons-react\";\nimport { useMemo, useState } from \"react\";\nimport { toast } from \"sonner\";\n\nimport { Badge } from \"./ui/badge\";\nimport { Button } from \"./ui/button\";\nimport { Checkbox } from \"./ui/checkbox\";\nimport { Input } from \"./ui/input\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"./ui/select\";\nimport { Skeleton } from \"./ui/skeleton\";\nimport { Switch } from \"./ui/switch\";\n\nexport interface UsageAlertAppOption {\n id: string;\n label: string;\n}\n\ninterface UsageAlertsPanelProps {\n scope: \"user\" | \"workspace\";\n appOptions?: UsageAlertAppOption[];\n}\n\ntype AlertUnit = \"usd\" | \"builder-credits\" | \"tokens\";\ntype AlertChannel = \"in-app\" | \"email\";\n\ninterface AlertDraft {\n ruleId?: string;\n appId: string;\n unit: AlertUnit;\n period: \"day\" | \"month\";\n limit: string;\n inApp: boolean;\n email: boolean;\n}\n\nfunction asRules(value: unknown): UsageAlertRule[] {\n if (Array.isArray(value)) return value as UsageAlertRule[];\n if (value && typeof value === \"object\") {\n const rules = (value as { rules?: unknown }).rules;\n if (Array.isArray(rules)) return rules as UsageAlertRule[];\n }\n return [];\n}\n\nfunction formatAlertValue(rule: UsageAlertRule, value: number): string {\n if (rule.unit === \"usd\") {\n return value.toLocaleString(undefined, {\n style: \"currency\",\n currency: \"USD\",\n maximumFractionDigits: 2,\n });\n }\n return (\n value.toLocaleString(undefined, {\n maximumFractionDigits: rule.unit === \"builder-credits\" ? 2 : 0,\n }) +\n \" \" +\n (rule.unit === \"builder-credits\" ? \"credits\" : \"tokens\")\n );\n}\n\nfunction draftFromRule(rule: UsageAlertRule): AlertDraft {\n return {\n ruleId: rule.id,\n appId: rule.appId ?? \"\",\n unit: rule.unit,\n period: rule.period,\n limit: String(rule.limit),\n inApp: rule.channels.includes(\"in-app\"),\n email: rule.channels.includes(\"email\"),\n };\n}\n\nfunction defaultDraft(): AlertDraft {\n return {\n appId: \"\",\n unit: \"usd\",\n period: \"day\",\n limit: \"100\",\n inApp: true,\n email: true,\n };\n}\n\nfunction AlertEditor({\n draft,\n appOptions,\n isExisting,\n onChange,\n onSave,\n onCancel,\n isPending,\n}: {\n draft: AlertDraft;\n appOptions: UsageAlertAppOption[];\n isExisting: boolean;\n onChange: (patch: Partial<AlertDraft>) => void;\n onSave: () => void;\n onCancel: () => void;\n isPending: boolean;\n}) {\n return (\n <div className=\"grid gap-3 rounded-md border bg-background/60 p-3 sm:grid-cols-2 lg:grid-cols-5\">\n <label className=\"space-y-1.5 text-xs text-muted-foreground\">\n <span>App</span>\n <Select\n value={draft.appId || \"all\"}\n onValueChange={(value) =>\n onChange({ appId: value === \"all\" ? \"\" : value })\n }\n disabled={isExisting}\n >\n <SelectTrigger className=\"h-9 text-sm\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">All apps</SelectItem>\n {appOptions.map((app) => (\n <SelectItem key={app.id} value={app.id}>\n {app.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1.5 text-xs text-muted-foreground\">\n <span>Unit</span>\n <Select\n value={draft.unit}\n onValueChange={(value) => onChange({ unit: value as AlertUnit })}\n >\n <SelectTrigger className=\"h-9 text-sm\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"usd\">Dollars</SelectItem>\n <SelectItem value=\"builder-credits\">Credits</SelectItem>\n <SelectItem value=\"tokens\">Tokens</SelectItem>\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1.5 text-xs text-muted-foreground\">\n <span>Limit</span>\n <Input\n type=\"number\"\n min=\"0.01\"\n step={draft.unit === \"tokens\" ? \"1\" : \"0.01\"}\n value={draft.limit}\n onChange={(event) => onChange({ limit: event.target.value })}\n className=\"h-9 text-sm\"\n />\n </label>\n <label className=\"space-y-1.5 text-xs text-muted-foreground\">\n <span>Period</span>\n <Select\n value={draft.period}\n onValueChange={(value) =>\n onChange({ period: value as \"day\" | \"month\" })\n }\n >\n <SelectTrigger className=\"h-9 text-sm\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"day\">Per day</SelectItem>\n <SelectItem value=\"month\">Per month</SelectItem>\n </SelectContent>\n </Select>\n </label>\n <div className=\"flex flex-wrap items-end gap-3 pb-1 text-xs text-muted-foreground\">\n <label className=\"inline-flex items-center gap-2\">\n <Checkbox\n checked={draft.inApp}\n onCheckedChange={(checked) => onChange({ inApp: checked === true })}\n />\n In app\n </label>\n <label className=\"inline-flex items-center gap-2\">\n <Checkbox\n checked={draft.email}\n onCheckedChange={(checked) => onChange({ email: checked === true })}\n />\n Email\n </label>\n <Button\n type=\"button\"\n size=\"sm\"\n className=\"ms-auto h-8\"\n onClick={onSave}\n disabled={isPending}\n >\n Save\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8\"\n onClick={onCancel}\n >\n Cancel\n </Button>\n </div>\n </div>\n );\n}\n\nexport function UsageAlertsPanel({\n scope,\n appOptions = [],\n}: UsageAlertsPanelProps) {\n const query = useActionQuery(\"get-usage-alerts\", { scope });\n const mutation = useActionMutation(\"manage-usage-alert\", {\n onSuccess: () => {\n setEditing(null);\n toast.success(\"Usage alert updated\");\n },\n onError: (error) => toast.error(String(error)),\n });\n const [editing, setEditing] = useState<AlertDraft | null>(null);\n const rules = asRules(query.data);\n const appLabels = useMemo(\n () => new Map(appOptions.map((app) => [app.id, app.label])),\n [appOptions],\n );\n\n function saveDraft() {\n if (!editing) return;\n const limit = Number(editing.limit);\n const channels: AlertChannel[] = [];\n if (editing.inApp) channels.push(\"in-app\");\n if (editing.email) channels.push(\"email\");\n if (!Number.isFinite(limit) || limit <= 0) {\n toast.error(\"Enter a positive threshold.\");\n return;\n }\n if (channels.length === 0) {\n toast.error(\"Choose at least one delivery channel.\");\n return;\n }\n mutation.mutate({\n operation: \"save\",\n scope,\n ruleId: editing.ruleId,\n appId: editing.appId || null,\n unit: editing.unit,\n period: editing.period,\n limit,\n channels,\n enabled: true,\n });\n }\n\n return (\n <section className=\"rounded-lg bg-card\">\n <div className=\"flex items-start justify-between gap-3 border-b px-4 py-3\">\n <div>\n <h2 className=\"text-sm font-semibold text-foreground\">\n Usage alerts\n </h2>\n <p className=\"mt-1 max-w-2xl text-xs leading-5 text-muted-foreground\">\n Get a quiet in-app and email notice when usage crosses a daily or\n monthly limit.\n </p>\n </div>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setEditing(defaultDraft())}\n disabled={mutation.isPending}\n >\n Add alert\n </Button>\n </div>\n\n <div className=\"divide-y\">\n {query.isLoading && rules.length === 0 ? (\n <div className=\"space-y-3 px-4 py-5\">\n {Array.from({ length: 3 }).map((_, index) => (\n <div key={index} className=\"space-y-2\">\n <Skeleton className=\"h-4 w-2/3 max-w-sm\" />\n <Skeleton className=\"h-3 w-1/2 max-w-xs\" />\n </div>\n ))}\n </div>\n ) : null}\n {query.isError ? (\n <div className=\"px-4 py-6 text-sm text-destructive\">\n Usage alerts are unavailable for this scope.{\" \"}\n {query.error instanceof Error ? query.error.message : \"Try again.\"}\n </div>\n ) : null}\n {!query.isLoading && !query.isError && rules.length === 0 ? (\n <div className=\"px-4 py-6 text-sm text-muted-foreground\">\n No alerts yet. The recommended default is $100 per day across all\n apps.\n </div>\n ) : null}\n {rules.map((rule) => {\n const isEditing = editing?.ruleId === rule.id;\n const target = rule.appId\n ? appLabels.get(rule.appId) || rule.appId\n : \"All apps\";\n return (\n <div key={rule.id} className=\"px-4 py-4\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-medium text-foreground\">\n {target}\n </span>\n {rule.isDefault ? (\n <Badge variant=\"secondary\">Default</Badge>\n ) : null}\n <Badge\n variant={\n rule.status === \"triggered\" ? \"destructive\" : \"outline\"\n }\n >\n {rule.status === \"triggered\"\n ? \"Over limit\"\n : rule.status === \"dismissed\"\n ? \"Dismissed\"\n : \"On track\"}\n </Badge>\n </div>\n <div className=\"mt-1 text-xs text-muted-foreground\">\n {formatAlertValue(rule, rule.current)} of{\" \"}\n {formatAlertValue(rule, rule.limit)} · per {rule.period}\n {rule.percent > 0 ? \" · \" + rule.percent + \"%\" : \"\"}\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <Switch\n checked={rule.enabled}\n onCheckedChange={(checked) =>\n mutation.mutate({\n operation: \"set-enabled\",\n scope,\n ruleId: rule.id,\n enabled: checked,\n })\n }\n aria-label={\n (rule.enabled ? \"Disable \" : \"Enable \") +\n target +\n \" usage alert\"\n }\n disabled={mutation.isPending}\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setEditing(draftFromRule(rule))}\n aria-label={\"Edit \" + target + \" usage alert\"}\n disabled={mutation.isPending}\n >\n <IconPencil className=\"size-4\" />\n </Button>\n {rule.status === \"triggered\" ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() =>\n mutation.mutate({\n operation: \"dismiss\",\n scope,\n ruleId: rule.id,\n })\n }\n aria-label={\"Dismiss \" + target + \" usage alert\"}\n disabled={mutation.isPending}\n >\n <IconX className=\"size-4\" />\n </Button>\n ) : null}\n </div>\n </div>\n <div className=\"mt-2 flex flex-wrap items-center gap-3 text-xs text-muted-foreground\">\n {rule.channels.includes(\"in-app\") ? (\n <span className=\"inline-flex items-center gap-1\">\n <IconBell className=\"size-3.5\" /> In app\n </span>\n ) : null}\n {rule.channels.includes(\"email\") ? (\n <span className=\"inline-flex items-center gap-1\">\n <IconMail className=\"size-3.5\" /> Email\n </span>\n ) : null}\n </div>\n {isEditing && editing ? (\n <div className=\"mt-4\">\n <AlertEditor\n draft={editing}\n appOptions={appOptions}\n isExisting\n onChange={(patch) =>\n setEditing((current) =>\n current ? { ...current, ...patch } : current,\n )\n }\n onSave={saveDraft}\n onCancel={() => setEditing(null)}\n isPending={mutation.isPending}\n />\n </div>\n ) : null}\n </div>\n );\n })}\n {editing && !editing.ruleId ? (\n <div className=\"px-4 py-4\">\n <AlertEditor\n draft={editing}\n appOptions={appOptions}\n isExisting={false}\n onChange={(patch) =>\n setEditing((current) =>\n current ? { ...current, ...patch } : current,\n )\n }\n onSave={saveDraft}\n onCancel={() => setEditing(null)}\n isPending={mutation.isPending}\n />\n </div>\n ) : null}\n </div>\n </section>\n );\n}\n"]}
1
+ {"version":3,"file":"usage-alerts-panel.js","sourceRoot":"","sources":["../../src/components/usage-alerts-panel.tsx"],"names":[],"mappings":"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","sourcesContent":["import type { UsageAlertRule } from \"@agent-native/core\";\nimport {\n useActionMutation,\n useActionQuery,\n} from \"@agent-native/core/client/hooks\";\nimport { useT } from \"@agent-native/core/client/i18n\";\nimport { IconBell, IconMail, IconPencil, IconX } from \"@tabler/icons-react\";\nimport { useMemo, useState } from \"react\";\nimport { toast } from \"sonner\";\n\nimport { Badge } from \"./ui/badge\";\nimport { Button } from \"./ui/button\";\nimport { Checkbox } from \"./ui/checkbox\";\nimport { Input } from \"./ui/input\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"./ui/select\";\nimport { Skeleton } from \"./ui/skeleton\";\nimport { Switch } from \"./ui/switch\";\n\nexport interface UsageAlertAppOption {\n id: string;\n label: string;\n}\n\ninterface UsageAlertsPanelProps {\n scope: \"user\" | \"workspace\";\n appOptions?: UsageAlertAppOption[];\n}\n\ntype AlertUnit = \"usd\" | \"builder-credits\" | \"tokens\";\ntype AlertChannel = \"in-app\" | \"email\";\n\ninterface AlertDraft {\n ruleId?: string;\n appId: string;\n unit: AlertUnit;\n period: \"day\" | \"month\";\n limit: string;\n inApp: boolean;\n email: boolean;\n}\n\nfunction asRules(value: unknown): UsageAlertRule[] {\n if (Array.isArray(value)) return value as UsageAlertRule[];\n if (value && typeof value === \"object\") {\n const rules = (value as { rules?: unknown }).rules;\n if (Array.isArray(rules)) return rules as UsageAlertRule[];\n }\n return [];\n}\n\nfunction formatAlertValue(rule: UsageAlertRule, value: number): string {\n if (rule.unit === \"usd\") {\n return value.toLocaleString(undefined, {\n style: \"currency\",\n currency: \"USD\",\n maximumFractionDigits: 2,\n });\n }\n return (\n value.toLocaleString(undefined, {\n maximumFractionDigits: rule.unit === \"builder-credits\" ? 2 : 0,\n }) +\n \" \" +\n (rule.unit === \"builder-credits\" ? \"credits\" : \"tokens\")\n );\n}\n\nfunction draftFromRule(rule: UsageAlertRule): AlertDraft {\n return {\n ruleId: rule.id,\n appId: rule.appId ?? \"\",\n unit: rule.unit,\n period: rule.period,\n limit: String(rule.limit),\n inApp: rule.channels.includes(\"in-app\"),\n email: rule.channels.includes(\"email\"),\n };\n}\n\nfunction defaultDraft(): AlertDraft {\n return {\n appId: \"\",\n unit: \"usd\",\n period: \"day\",\n limit: \"100\",\n inApp: true,\n email: true,\n };\n}\n\nfunction AlertEditor({\n draft,\n appOptions,\n isExisting,\n onChange,\n onSave,\n onCancel,\n isPending,\n}: {\n draft: AlertDraft;\n appOptions: UsageAlertAppOption[];\n isExisting: boolean;\n onChange: (patch: Partial<AlertDraft>) => void;\n onSave: () => void;\n onCancel: () => void;\n isPending: boolean;\n}) {\n const t = useT();\n\n return (\n <div className=\"grid gap-3 rounded-md border bg-background/60 p-3 sm:grid-cols-2 lg:grid-cols-5\">\n <label className=\"space-y-1.5 text-xs text-muted-foreground\">\n <span>App</span>\n <Select\n value={draft.appId || \"all\"}\n onValueChange={(value) =>\n onChange({ appId: value === \"all\" ? \"\" : value })\n }\n disabled={isExisting}\n >\n <SelectTrigger className=\"h-9 text-sm\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"all\">All apps</SelectItem>\n {appOptions.map((app) => (\n <SelectItem key={app.id} value={app.id}>\n {app.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1.5 text-xs text-muted-foreground\">\n <span>Unit</span>\n <Select\n value={draft.unit}\n onValueChange={(value) => onChange({ unit: value as AlertUnit })}\n >\n <SelectTrigger className=\"h-9 text-sm\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"usd\">Dollars</SelectItem>\n <SelectItem value=\"builder-credits\">Credits</SelectItem>\n <SelectItem value=\"tokens\">Tokens</SelectItem>\n </SelectContent>\n </Select>\n </label>\n <label className=\"space-y-1.5 text-xs text-muted-foreground\">\n <span>{t(\"dispatch.pages.usageAlertThreshold\")}</span>\n <Input\n type=\"number\"\n min=\"0.01\"\n step={draft.unit === \"tokens\" ? \"1\" : \"0.01\"}\n value={draft.limit}\n onChange={(event) => onChange({ limit: event.target.value })}\n className=\"h-9 text-sm\"\n />\n </label>\n <label className=\"space-y-1.5 text-xs text-muted-foreground\">\n <span>Period</span>\n <Select\n value={draft.period}\n onValueChange={(value) =>\n onChange({ period: value as \"day\" | \"month\" })\n }\n >\n <SelectTrigger className=\"h-9 text-sm\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"day\">Per day</SelectItem>\n <SelectItem value=\"month\">Per month</SelectItem>\n </SelectContent>\n </Select>\n </label>\n <div className=\"flex flex-wrap items-end gap-3 pb-1 text-xs text-muted-foreground\">\n <label className=\"inline-flex items-center gap-2\">\n <Checkbox\n checked={draft.inApp}\n onCheckedChange={(checked) => onChange({ inApp: checked === true })}\n />\n In app\n </label>\n <label className=\"inline-flex items-center gap-2\">\n <Checkbox\n checked={draft.email}\n onCheckedChange={(checked) => onChange({ email: checked === true })}\n />\n Email\n </label>\n <Button\n type=\"button\"\n size=\"sm\"\n className=\"ms-auto h-8\"\n onClick={onSave}\n disabled={isPending}\n >\n Save\n </Button>\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-8\"\n onClick={onCancel}\n >\n Cancel\n </Button>\n </div>\n </div>\n );\n}\n\nexport function UsageAlertsPanel({\n scope,\n appOptions = [],\n}: UsageAlertsPanelProps) {\n const t = useT();\n const query = useActionQuery(\"get-usage-alerts\", { scope });\n const mutation = useActionMutation(\"manage-usage-alert\", {\n onSuccess: () => {\n setEditing(null);\n toast.success(\"Usage alert updated\");\n },\n onError: (error) => toast.error(String(error)),\n });\n const [editing, setEditing] = useState<AlertDraft | null>(null);\n const rules = asRules(query.data);\n const appLabels = useMemo(\n () => new Map(appOptions.map((app) => [app.id, app.label])),\n [appOptions],\n );\n\n function saveDraft() {\n if (!editing) return;\n const limit = Number(editing.limit);\n const channels: AlertChannel[] = [];\n if (editing.inApp) channels.push(\"in-app\");\n if (editing.email) channels.push(\"email\");\n if (!Number.isFinite(limit) || limit <= 0) {\n toast.error(\"Enter a positive threshold.\");\n return;\n }\n if (channels.length === 0) {\n toast.error(\"Choose at least one delivery channel.\");\n return;\n }\n mutation.mutate({\n operation: \"save\",\n scope,\n ruleId: editing.ruleId,\n appId: editing.appId || null,\n unit: editing.unit,\n period: editing.period,\n limit,\n channels,\n enabled: true,\n });\n }\n\n return (\n <section className=\"rounded-lg bg-card\">\n <div className=\"flex items-start justify-between gap-3 border-b px-4 py-3\">\n <div>\n <h2 className=\"text-sm font-semibold text-foreground\">\n Usage alerts\n </h2>\n <p className=\"mt-1 max-w-2xl text-xs leading-5 text-muted-foreground\">\n {t(\"dispatch.pages.usageAlertDescription\")}\n </p>\n </div>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setEditing(defaultDraft())}\n disabled={mutation.isPending}\n >\n Add alert\n </Button>\n </div>\n\n <div className=\"divide-y\">\n {query.isLoading && rules.length === 0 ? (\n <div className=\"space-y-3 px-4 py-5\">\n {Array.from({ length: 3 }).map((_, index) => (\n <div key={index} className=\"space-y-2\">\n <Skeleton className=\"h-4 w-2/3 max-w-sm\" />\n <Skeleton className=\"h-3 w-1/2 max-w-xs\" />\n </div>\n ))}\n </div>\n ) : null}\n {query.isError ? (\n <div className=\"px-4 py-6 text-sm text-destructive\">\n Usage alerts are unavailable for this scope.{\" \"}\n {query.error instanceof Error ? query.error.message : \"Try again.\"}\n </div>\n ) : null}\n {!query.isLoading && !query.isError && rules.length === 0 ? (\n <div className=\"px-4 py-6 text-sm text-muted-foreground\">\n {t(\"dispatch.pages.usageAlertEmpty\")}\n </div>\n ) : null}\n {rules.map((rule) => {\n const isEditing = editing?.ruleId === rule.id;\n const target = rule.appId\n ? appLabels.get(rule.appId) || rule.appId\n : \"All apps\";\n return (\n <div key={rule.id} className=\"px-4 py-4\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"text-sm font-medium text-foreground\">\n {target}\n </span>\n {rule.isDefault ? (\n <Badge variant=\"secondary\">Default</Badge>\n ) : null}\n <Badge\n variant={\n rule.status === \"triggered\" ? \"destructive\" : \"outline\"\n }\n >\n {rule.status === \"triggered\"\n ? t(\"dispatch.pages.usageAlertReached\")\n : rule.status === \"dismissed\"\n ? \"Dismissed\"\n : \"On track\"}\n </Badge>\n </div>\n <div className=\"mt-1 text-xs text-muted-foreground\">\n {t(\"dispatch.pages.usageAlertCurrentAndThreshold\", {\n current: formatAlertValue(rule, rule.current),\n threshold: formatAlertValue(rule, rule.limit),\n period: t(\n rule.period === \"day\"\n ? \"dispatch.pages.usageAlertDay\"\n : \"dispatch.pages.usageAlertMonth\",\n ),\n })}\n {rule.percent > 0 ? \" · \" + rule.percent + \"%\" : \"\"}\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <Switch\n checked={rule.enabled}\n onCheckedChange={(checked) =>\n mutation.mutate({\n operation: \"set-enabled\",\n scope,\n ruleId: rule.id,\n enabled: checked,\n })\n }\n aria-label={\n (rule.enabled ? \"Disable \" : \"Enable \") +\n target +\n \" usage alert\"\n }\n disabled={mutation.isPending}\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() => setEditing(draftFromRule(rule))}\n aria-label={\"Edit \" + target + \" usage alert\"}\n disabled={mutation.isPending}\n >\n <IconPencil className=\"size-4\" />\n </Button>\n {rule.status === \"triggered\" ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n onClick={() =>\n mutation.mutate({\n operation: \"dismiss\",\n scope,\n ruleId: rule.id,\n })\n }\n aria-label={\"Dismiss \" + target + \" usage alert\"}\n disabled={mutation.isPending}\n >\n <IconX className=\"size-4\" />\n </Button>\n ) : null}\n </div>\n </div>\n <div className=\"mt-2 flex flex-wrap items-center gap-3 text-xs text-muted-foreground\">\n {rule.channels.includes(\"in-app\") ? (\n <span className=\"inline-flex items-center gap-1\">\n <IconBell className=\"size-3.5\" /> In app\n </span>\n ) : null}\n {rule.channels.includes(\"email\") ? (\n <span className=\"inline-flex items-center gap-1\">\n <IconMail className=\"size-3.5\" /> Email\n </span>\n ) : null}\n </div>\n {isEditing && editing ? (\n <div className=\"mt-4\">\n <AlertEditor\n draft={editing}\n appOptions={appOptions}\n isExisting\n onChange={(patch) =>\n setEditing((current) =>\n current ? { ...current, ...patch } : current,\n )\n }\n onSave={saveDraft}\n onCancel={() => setEditing(null)}\n isPending={mutation.isPending}\n />\n </div>\n ) : null}\n </div>\n );\n })}\n {editing && !editing.ruleId ? (\n <div className=\"px-4 py-4\">\n <AlertEditor\n draft={editing}\n appOptions={appOptions}\n isExisting={false}\n onChange={(patch) =>\n setEditing((current) =>\n current ? { ...current, ...patch } : current,\n )\n }\n onSave={saveDraft}\n onCancel={() => setEditing(null)}\n isPending={mutation.isPending}\n />\n </div>\n ) : null}\n </div>\n </section>\n );\n}\n"]}
@@ -24,7 +24,7 @@ export function filterOtherApps(connectedApps, workspaceApps) {
24
24
  workspaceAppIds.has(id) ||
25
25
  seen.has(id))
26
26
  return false;
27
- if (app.source === "workspace")
27
+ if (app.source !== "custom")
28
28
  return false;
29
29
  if (!isHttpUrl(app.url))
30
30
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"other-apps.js","sourceRoot":"","sources":["../../src/lib/other-apps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAC;AAkBjE,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC;AAE1D,SAAS,SAAS,CAAC,KAAa;IAC9B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3B,OAAO,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,aAAoC,EACpC,aAA+B;IAE/B,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;QAC9B,UAAU;QACV,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;KAC3D,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,OAAO,aAAa;SACjB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;QACd,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACvC,IACE,CAAC,EAAE;YACH,6BAA6B,CAAC,EAAE,CAAC;YACjC,oBAAoB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAEZ,OAAO,KAAK,CAAC;QACf,IAAI,GAAG,CAAC,MAAM,KAAK,WAAW;YAAE,OAAO,KAAK,CAAC;QAC7C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QACtC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACb,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;SACD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAClD,CAAC","sourcesContent":["import { isDefaultWorkspaceAppHiddenId } from \"./workspace-apps\";\n\nexport interface ConnectedAppSummary {\n id: string;\n name: string;\n description?: string;\n url: string;\n /** Canonical app-home URL used for launchers; `url` remains the A2A endpoint. */\n homeUrl?: string;\n color?: string;\n source?: \"builtin\" | \"custom\" | \"workspace\";\n}\n\nexport interface WorkspaceAppId {\n id: string;\n isDispatch?: boolean;\n}\n\nconst HIDDEN_OTHER_APP_IDS = new Set([\"crm\", \"research\"]);\n\nfunction isHttpUrl(value: string): boolean {\n try {\n const url = new URL(value);\n return url.protocol === \"http:\" || url.protocol === \"https:\";\n } catch {\n return false;\n }\n}\n\nexport function filterOtherApps(\n connectedApps: ConnectedAppSummary[],\n workspaceApps: WorkspaceAppId[],\n): ConnectedAppSummary[] {\n const workspaceAppIds = new Set([\n \"dispatch\",\n ...workspaceApps.map((app) => app.id.trim().toLowerCase()),\n ]);\n const seen = new Set<string>();\n\n return connectedApps\n .filter((app) => {\n const id = app.id.trim().toLowerCase();\n if (\n !id ||\n isDefaultWorkspaceAppHiddenId(id) ||\n HIDDEN_OTHER_APP_IDS.has(id) ||\n workspaceAppIds.has(id) ||\n seen.has(id)\n )\n return false;\n if (app.source === \"workspace\") return false;\n if (!isHttpUrl(app.url)) return false;\n seen.add(id);\n return true;\n })\n .sort((a, b) => a.name.localeCompare(b.name));\n}\n"]}
1
+ {"version":3,"file":"other-apps.js","sourceRoot":"","sources":["../../src/lib/other-apps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAC;AAkBjE,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC;AAE1D,SAAS,SAAS,CAAC,KAAa;IAC9B,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3B,OAAO,GAAG,CAAC,QAAQ,KAAK,OAAO,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,aAAoC,EACpC,aAA+B;IAE/B,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;QAC9B,UAAU;QACV,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;KAC3D,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,OAAO,aAAa;SACjB,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;QACd,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACvC,IACE,CAAC,EAAE;YACH,6BAA6B,CAAC,EAAE,CAAC;YACjC,oBAAoB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAEZ,OAAO,KAAK,CAAC;QACf,IAAI,GAAG,CAAC,MAAM,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QAC1C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QACtC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACb,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;SACD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAClD,CAAC","sourcesContent":["import { isDefaultWorkspaceAppHiddenId } from \"./workspace-apps\";\n\nexport interface ConnectedAppSummary {\n id: string;\n name: string;\n description?: string;\n url: string;\n /** Canonical app-home URL used for launchers; `url` remains the A2A endpoint. */\n homeUrl?: string;\n color?: string;\n source?: \"builtin\" | \"custom\" | \"workspace\";\n}\n\nexport interface WorkspaceAppId {\n id: string;\n isDispatch?: boolean;\n}\n\nconst HIDDEN_OTHER_APP_IDS = new Set([\"crm\", \"research\"]);\n\nfunction isHttpUrl(value: string): boolean {\n try {\n const url = new URL(value);\n return url.protocol === \"http:\" || url.protocol === \"https:\";\n } catch {\n return false;\n }\n}\n\nexport function filterOtherApps(\n connectedApps: ConnectedAppSummary[],\n workspaceApps: WorkspaceAppId[],\n): ConnectedAppSummary[] {\n const workspaceAppIds = new Set([\n \"dispatch\",\n ...workspaceApps.map((app) => app.id.trim().toLowerCase()),\n ]);\n const seen = new Set<string>();\n\n return connectedApps\n .filter((app) => {\n const id = app.id.trim().toLowerCase();\n if (\n !id ||\n isDefaultWorkspaceAppHiddenId(id) ||\n HIDDEN_OTHER_APP_IDS.has(id) ||\n workspaceAppIds.has(id) ||\n seen.has(id)\n )\n return false;\n if (app.source !== \"custom\") return false;\n if (!isHttpUrl(app.url)) return false;\n seen.add(id);\n return true;\n })\n .sort((a, b) => a.name.localeCompare(b.name));\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/dispatch",
3
- "version": "0.38.6",
3
+ "version": "0.38.7-nightly-20260924034232",
4
4
  "description": "Dispatch — workspace control plane for agent-native apps. Vault, integrations, destinations, scheduled jobs, and cross-app delegation, shipped as a single drop-in package.",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -102,10 +102,10 @@
102
102
  "typescript-7": "npm:typescript@^7.0.2",
103
103
  "vite": "8.1.0",
104
104
  "vitest": "^4.1.5",
105
- "@agent-native/core": "0.186.0"
105
+ "@agent-native/core": "0.187.0-nightly-20260924034232"
106
106
  },
107
107
  "peerDependencies": {
108
- "@agent-native/core": ">=0.8.0",
108
+ "@agent-native/core": "0.187.0-nightly-20260924034232",
109
109
  "react": ">=19.2.7",
110
110
  "react-dom": ">=19.2.7",
111
111
  "react-router": ">=8"
@@ -324,6 +324,7 @@ describe("DispatchControlPlane", () => {
324
324
  name: "Mail",
325
325
  description: "Email client",
326
326
  url: "https://mail.agent-native.com",
327
+ source: "builtin",
327
328
  },
328
329
  {
329
330
  id: "clips",
@@ -337,6 +338,14 @@ describe("DispatchControlPlane", () => {
337
338
  id: "onboarding",
338
339
  name: "Duplicate onboarding",
339
340
  url: "https://duplicate.example.com",
341
+ source: "custom",
342
+ },
343
+ {
344
+ id: "custom-app",
345
+ name: "Custom app",
346
+ description: "A workspace-connected app",
347
+ url: "https://custom.agent-native.com",
348
+ source: "custom",
340
349
  },
341
350
  ];
342
351
  clientState.curatedTemplates = [
@@ -370,7 +379,8 @@ describe("DispatchControlPlane", () => {
370
379
 
371
380
  expect(container.textContent).toContain("Onboarding");
372
381
  expect(container.textContent).toContain("Mail");
373
- expect(container.textContent).toContain("Clips");
382
+ expect(container.textContent).toContain("Custom app");
383
+ expect(container.textContent).not.toContain("Clips");
374
384
  expect(container.textContent).toContain("Analytics");
375
385
  expect(container.textContent).toContain("Apps");
376
386
  expect(container.textContent).toContain("New");
@@ -386,6 +396,12 @@ describe("DispatchControlPlane", () => {
386
396
  expect(container.textContent).not.toContain("Archived app");
387
397
  expect(container.textContent).not.toContain("Duplicate onboarding");
388
398
  expect(container.textContent).not.toContain("CRM");
399
+ expect(
400
+ Array.from(container.querySelectorAll("a")).some(
401
+ (anchor) =>
402
+ anchor.getAttribute("href") === "https://custom.agent-native.com",
403
+ ),
404
+ ).toBe(true);
389
405
  expect(
390
406
  container.querySelectorAll(
391
407
  'button[aria-label="Open options for Onboarding"]',
@@ -404,11 +420,6 @@ describe("DispatchControlPlane", () => {
404
420
  'a[href="/onboarding/home"][target="_blank"]',
405
421
  );
406
422
  expect(onboardingNewTabLink).not.toBeNull();
407
- const clipsHref = Array.from(container.querySelectorAll("a"))
408
- .map((anchor) => anchor.getAttribute("href"))
409
- .find((href) => href?.includes("clips.agent-native.com"));
410
- expect(clipsHref).toContain("https://clips.agent-native.com");
411
- expect(clipsHref).not.toContain("/share/");
412
423
  });
413
424
 
414
425
  it("searches available apps case-insensitively before showing the empty state", async () => {
@@ -25,6 +25,7 @@ describe("other app search", () => {
25
25
  name: "Slides",
26
26
  description: "Create presentations",
27
27
  url: "https://slides.agent-native.com",
28
+ source: "custom",
28
29
  },
29
30
  ],
30
31
  workspaceApps: [],
@@ -3,6 +3,7 @@ import {
3
3
  useActionMutation,
4
4
  useActionQuery,
5
5
  } from "@agent-native/core/client/hooks";
6
+ import { useT } from "@agent-native/core/client/i18n";
6
7
  import { IconBell, IconMail, IconPencil, IconX } from "@tabler/icons-react";
7
8
  import { useMemo, useState } from "react";
8
9
  import { toast } from "sonner";
@@ -110,6 +111,8 @@ function AlertEditor({
110
111
  onCancel: () => void;
111
112
  isPending: boolean;
112
113
  }) {
114
+ const t = useT();
115
+
113
116
  return (
114
117
  <div className="grid gap-3 rounded-md border bg-background/60 p-3 sm:grid-cols-2 lg:grid-cols-5">
115
118
  <label className="space-y-1.5 text-xs text-muted-foreground">
@@ -151,7 +154,7 @@ function AlertEditor({
151
154
  </Select>
152
155
  </label>
153
156
  <label className="space-y-1.5 text-xs text-muted-foreground">
154
- <span>Limit</span>
157
+ <span>{t("dispatch.pages.usageAlertThreshold")}</span>
155
158
  <Input
156
159
  type="number"
157
160
  min="0.01"
@@ -220,6 +223,7 @@ export function UsageAlertsPanel({
220
223
  scope,
221
224
  appOptions = [],
222
225
  }: UsageAlertsPanelProps) {
226
+ const t = useT();
223
227
  const query = useActionQuery("get-usage-alerts", { scope });
224
228
  const mutation = useActionMutation("manage-usage-alert", {
225
229
  onSuccess: () => {
@@ -270,8 +274,7 @@ export function UsageAlertsPanel({
270
274
  Usage alerts
271
275
  </h2>
272
276
  <p className="mt-1 max-w-2xl text-xs leading-5 text-muted-foreground">
273
- Get a quiet in-app and email notice when usage crosses a daily or
274
- monthly limit.
277
+ {t("dispatch.pages.usageAlertDescription")}
275
278
  </p>
276
279
  </div>
277
280
  <Button
@@ -304,8 +307,7 @@ export function UsageAlertsPanel({
304
307
  ) : null}
305
308
  {!query.isLoading && !query.isError && rules.length === 0 ? (
306
309
  <div className="px-4 py-6 text-sm text-muted-foreground">
307
- No alerts yet. The recommended default is $100 per day across all
308
- apps.
310
+ {t("dispatch.pages.usageAlertEmpty")}
309
311
  </div>
310
312
  ) : null}
311
313
  {rules.map((rule) => {
@@ -330,15 +332,22 @@ export function UsageAlertsPanel({
330
332
  }
331
333
  >
332
334
  {rule.status === "triggered"
333
- ? "Over limit"
335
+ ? t("dispatch.pages.usageAlertReached")
334
336
  : rule.status === "dismissed"
335
337
  ? "Dismissed"
336
338
  : "On track"}
337
339
  </Badge>
338
340
  </div>
339
341
  <div className="mt-1 text-xs text-muted-foreground">
340
- {formatAlertValue(rule, rule.current)} of{" "}
341
- {formatAlertValue(rule, rule.limit)} · per {rule.period}
342
+ {t("dispatch.pages.usageAlertCurrentAndThreshold", {
343
+ current: formatAlertValue(rule, rule.current),
344
+ threshold: formatAlertValue(rule, rule.limit),
345
+ period: t(
346
+ rule.period === "day"
347
+ ? "dispatch.pages.usageAlertDay"
348
+ : "dispatch.pages.usageAlertMonth",
349
+ ),
350
+ })}
342
351
  {rule.percent > 0 ? " · " + rule.percent + "%" : ""}
343
352
  </div>
344
353
  </div>
@@ -11,12 +11,14 @@ describe("filterOtherApps", () => {
11
11
  id: "slides",
12
12
  name: "Slides",
13
13
  url: "https://slides.agent-native.com",
14
+ source: "custom",
14
15
  },
15
16
  {
16
17
  id: "analytics",
17
18
  name: "Analytics",
18
19
  description: "Explore data",
19
20
  url: "https://analytics.agent-native.com",
21
+ source: "custom",
20
22
  },
21
23
  {
22
24
  id: "coach",
@@ -38,11 +40,13 @@ describe("filterOtherApps", () => {
38
40
  name: "Analytics",
39
41
  description: "Explore data",
40
42
  url: "https://analytics.agent-native.com",
43
+ source: "custom",
41
44
  },
42
45
  {
43
46
  id: "slides",
44
47
  name: "Slides",
45
48
  url: "https://slides.agent-native.com",
49
+ source: "custom",
46
50
  },
47
51
  ]);
48
52
  });
@@ -55,13 +59,20 @@ describe("filterOtherApps", () => {
55
59
  id: "analytics",
56
60
  name: "Analytics",
57
61
  url: "https://analytics.agent-native.com",
62
+ source: "custom",
58
63
  },
59
64
  {
60
65
  id: "Analytics",
61
66
  name: "Analytics duplicate",
62
67
  url: "https://duplicate.example.com",
68
+ source: "custom",
69
+ },
70
+ {
71
+ id: "relative",
72
+ name: "Relative",
73
+ url: "/relative",
74
+ source: "custom",
63
75
  },
64
- { id: "relative", name: "Relative", url: "/relative" },
65
76
  ],
66
77
  [],
67
78
  ),
@@ -70,6 +81,7 @@ describe("filterOtherApps", () => {
70
81
  id: "analytics",
71
82
  name: "Analytics",
72
83
  url: "https://analytics.agent-native.com",
84
+ source: "custom",
73
85
  },
74
86
  ]);
75
87
  });
@@ -92,6 +104,7 @@ describe("filterOtherApps", () => {
92
104
  id: "mail",
93
105
  name: "Mail",
94
106
  url: "https://mail.agent-native.com",
107
+ source: "custom",
95
108
  },
96
109
  ],
97
110
  [],
@@ -101,6 +114,7 @@ describe("filterOtherApps", () => {
101
114
  id: "mail",
102
115
  name: "Mail",
103
116
  url: "https://mail.agent-native.com",
117
+ source: "custom",
104
118
  },
105
119
  ]);
106
120
  });
@@ -113,11 +127,13 @@ describe("filterOtherApps", () => {
113
127
  id: "chat",
114
128
  name: "Chat",
115
129
  url: "https://chat.agent-native.com",
130
+ source: "builtin",
116
131
  },
117
132
  {
118
133
  id: "mail",
119
134
  name: "Mail",
120
135
  url: "https://mail.agent-native.com",
136
+ source: "custom",
121
137
  },
122
138
  ],
123
139
  [],
@@ -127,6 +143,47 @@ describe("filterOtherApps", () => {
127
143
  id: "mail",
128
144
  name: "Mail",
129
145
  url: "https://mail.agent-native.com",
146
+ source: "custom",
147
+ },
148
+ ]);
149
+ });
150
+
151
+ it("hides builtin, workspace, and unknown sources from external app launchers", () => {
152
+ expect(
153
+ filterOtherApps(
154
+ [
155
+ {
156
+ id: "account-tiering",
157
+ name: "Account Tiering",
158
+ url: "https://account-tiering.agent-native.com",
159
+ source: "builtin",
160
+ },
161
+ {
162
+ id: "workspace-app",
163
+ name: "Workspace app",
164
+ url: "https://workspace.example.com/app",
165
+ source: "workspace",
166
+ },
167
+ {
168
+ id: "unknown-source",
169
+ name: "Unknown source",
170
+ url: "https://unknown.example.com",
171
+ },
172
+ {
173
+ id: "connected-agent",
174
+ name: "Connected agent",
175
+ url: "https://agent.example.com",
176
+ source: "custom",
177
+ },
178
+ ],
179
+ [],
180
+ ),
181
+ ).toEqual([
182
+ {
183
+ id: "connected-agent",
184
+ name: "Connected agent",
185
+ url: "https://agent.example.com",
186
+ source: "custom",
130
187
  },
131
188
  ]);
132
189
  });
@@ -48,7 +48,7 @@ export function filterOtherApps(
48
48
  seen.has(id)
49
49
  )
50
50
  return false;
51
- if (app.source === "workspace") return false;
51
+ if (app.source !== "custom") return false;
52
52
  if (!isHttpUrl(app.url)) return false;
53
53
  seen.add(id);
54
54
  return true;