@burdenoff/microfe-subscriberbot 2026.706.1 → 2026.706.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/subscriberbot/components/Modal.js +51 -0
- package/dist/subscriberbot/components/Modal.js.map +1 -0
- package/dist/subscriberbot/hooks/useSubscriberBotApi.js +358 -163
- package/dist/subscriberbot/hooks/useSubscriberBotApi.js.map +1 -1
- package/dist/subscriberbot/pages/InboxPage.js +339 -66
- package/dist/subscriberbot/pages/InboxPage.js.map +1 -1
- package/dist/subscriberbot/pages/InvoicesPage.js +496 -73
- package/dist/subscriberbot/pages/InvoicesPage.js.map +1 -1
- package/dist/subscriberbot/pages/WorkflowsPage.js +136 -150
- package/dist/subscriberbot/pages/WorkflowsPage.js.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js +197 -58
- package/dist/subscriberbot/pages/agents/AgentDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/agents/AgentsListPage.js +2 -0
- package/dist/subscriberbot/pages/agents/AgentsListPage.js.map +1 -1
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.js +538 -71
- package/dist/subscriberbot/pages/providers/ProviderDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/providers/ProvidersListPage.js +2 -0
- package/dist/subscriberbot/pages/providers/ProvidersListPage.js.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js +664 -126
- package/dist/subscriberbot/pages/relationships/RelationshipDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js +18 -16
- package/dist/subscriberbot/pages/relationships/RelationshipsListPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -4,141 +4,276 @@ import { formatEnumLabel as n, formatRelativeTime as r } from "../../utils/forma
|
|
|
4
4
|
import { StatusBadge as i } from "../../components/StatusBadge.js";
|
|
5
5
|
import { EmptyState as a } from "../../components/EmptyState.js";
|
|
6
6
|
import { ErrorView as o, LoadingView as s, Section as c } from "../../components/StateViews.js";
|
|
7
|
-
import { useAgent as l,
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
7
|
+
import { useAgent as l, useCreatePolicy as u, useSetAgentStatus as d } from "../../hooks/useSubscriberBotApi.js";
|
|
8
|
+
import { Modal as f } from "../../components/Modal.js";
|
|
9
|
+
import { useState as p } from "react";
|
|
10
|
+
import { ArrowLeft as m, Bot as h, Plus as g, ShieldCheck as _ } from "lucide-react";
|
|
11
|
+
import { useParams as v } from "react-router-dom";
|
|
12
|
+
import { jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
13
|
+
import { GlassCard as x } from "@burdenoff/fe-libs/ui";
|
|
13
14
|
//#region src/subscriberbot/pages/agents/AgentDetailPage.tsx
|
|
14
|
-
var
|
|
15
|
+
var S = [
|
|
15
16
|
"DISABLED",
|
|
16
17
|
"SUGGEST_ONLY",
|
|
17
18
|
"AUTONOMOUS"
|
|
18
|
-
]
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
], C = [
|
|
20
|
+
"ALERT",
|
|
21
|
+
"CREATE_TASK",
|
|
22
|
+
"RENEW",
|
|
23
|
+
"CANCEL",
|
|
24
|
+
"PAUSE",
|
|
25
|
+
"UPGRADE",
|
|
26
|
+
"DOWNGRADE"
|
|
27
|
+
], w = "w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]", T = "mb-1 block text-xs font-medium text-text-secondary";
|
|
28
|
+
function E({ agentId: e, onClose: t }) {
|
|
29
|
+
let n = u(), [r, i] = p(""), [a, o] = p("ALERT"), [s, c] = p(""), [l, d] = p(""), [m, h] = p(!0), [g, _] = p(null);
|
|
30
|
+
return /* @__PURE__ */ y(f, {
|
|
31
|
+
title: "Add policy",
|
|
32
|
+
onClose: t,
|
|
33
|
+
maxWidthClassName: "max-w-xl",
|
|
34
|
+
children: /* @__PURE__ */ b("form", {
|
|
35
|
+
onSubmit: (i) => {
|
|
36
|
+
if (i.preventDefault(), _(null), !r.trim()) {
|
|
37
|
+
_("A policy name is required.");
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
let o;
|
|
41
|
+
if (l.trim()) try {
|
|
42
|
+
o = JSON.parse(l);
|
|
43
|
+
} catch {
|
|
44
|
+
_("Condition must be valid JSON (e.g. {\"amountOver\": 100}).");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
n.mutate({
|
|
48
|
+
agentId: e,
|
|
49
|
+
name: r.trim(),
|
|
50
|
+
action: a,
|
|
51
|
+
isEnabled: m,
|
|
52
|
+
...s.trim() ? { description: s.trim() } : {},
|
|
53
|
+
...o ? { condition: o } : {}
|
|
54
|
+
}, {
|
|
55
|
+
onSuccess: () => t(),
|
|
56
|
+
onError: (e) => _(e instanceof Error ? e.message : "Could not add policy.")
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
className: "space-y-4",
|
|
60
|
+
children: [
|
|
61
|
+
g && /* @__PURE__ */ y("div", {
|
|
62
|
+
className: "rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text",
|
|
63
|
+
children: g
|
|
64
|
+
}),
|
|
65
|
+
/* @__PURE__ */ b("div", { children: [/* @__PURE__ */ b("label", {
|
|
66
|
+
htmlFor: "policy-name",
|
|
67
|
+
className: T,
|
|
68
|
+
children: ["Name ", /* @__PURE__ */ y("span", {
|
|
69
|
+
className: "text-status-error-text",
|
|
70
|
+
children: "*"
|
|
71
|
+
})]
|
|
72
|
+
}), /* @__PURE__ */ y("input", {
|
|
73
|
+
id: "policy-name",
|
|
74
|
+
type: "text",
|
|
75
|
+
value: r,
|
|
76
|
+
onChange: (e) => i(e.target.value),
|
|
77
|
+
placeholder: "e.g. Alert on price hike over 20%",
|
|
78
|
+
className: w,
|
|
79
|
+
required: !0
|
|
80
|
+
})] }),
|
|
81
|
+
/* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("label", {
|
|
82
|
+
htmlFor: "policy-action",
|
|
83
|
+
className: T,
|
|
84
|
+
children: "Action"
|
|
85
|
+
}), /* @__PURE__ */ y("select", {
|
|
86
|
+
id: "policy-action",
|
|
87
|
+
value: a,
|
|
88
|
+
onChange: (e) => o(e.target.value),
|
|
89
|
+
className: w,
|
|
90
|
+
children: C.map((e) => /* @__PURE__ */ y("option", {
|
|
91
|
+
value: e,
|
|
92
|
+
children: e.replace(/_/g, " ")
|
|
93
|
+
}, e))
|
|
94
|
+
})] }),
|
|
95
|
+
/* @__PURE__ */ b("div", { children: [/* @__PURE__ */ b("label", {
|
|
96
|
+
htmlFor: "policy-condition",
|
|
97
|
+
className: T,
|
|
98
|
+
children: ["Condition ", /* @__PURE__ */ y("span", {
|
|
99
|
+
className: "text-text-secondary",
|
|
100
|
+
children: "(optional JSON)"
|
|
101
|
+
})]
|
|
102
|
+
}), /* @__PURE__ */ y("textarea", {
|
|
103
|
+
id: "policy-condition",
|
|
104
|
+
value: l,
|
|
105
|
+
onChange: (e) => d(e.target.value),
|
|
106
|
+
rows: 3,
|
|
107
|
+
placeholder: "{ \"priceIncreasePct\": 20 }",
|
|
108
|
+
className: `${w} font-mono`
|
|
109
|
+
})] }),
|
|
110
|
+
/* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("label", {
|
|
111
|
+
htmlFor: "policy-desc",
|
|
112
|
+
className: T,
|
|
113
|
+
children: "Description"
|
|
114
|
+
}), /* @__PURE__ */ y("textarea", {
|
|
115
|
+
id: "policy-desc",
|
|
116
|
+
value: s,
|
|
117
|
+
onChange: (e) => c(e.target.value),
|
|
118
|
+
rows: 2,
|
|
119
|
+
className: w
|
|
120
|
+
})] }),
|
|
121
|
+
/* @__PURE__ */ b("label", {
|
|
122
|
+
className: "flex items-center gap-2 text-sm text-text-primary",
|
|
123
|
+
children: [/* @__PURE__ */ y("input", {
|
|
124
|
+
type: "checkbox",
|
|
125
|
+
checked: m,
|
|
126
|
+
onChange: (e) => h(e.target.checked),
|
|
127
|
+
className: "size-4 rounded border-border-subtle"
|
|
128
|
+
}), "Enabled"]
|
|
129
|
+
}),
|
|
130
|
+
/* @__PURE__ */ b("div", {
|
|
131
|
+
className: "flex justify-end gap-3",
|
|
132
|
+
children: [/* @__PURE__ */ y("button", {
|
|
133
|
+
type: "button",
|
|
134
|
+
onClick: t,
|
|
135
|
+
className: "rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
|
|
136
|
+
children: "Cancel"
|
|
137
|
+
}), /* @__PURE__ */ y("button", {
|
|
138
|
+
type: "submit",
|
|
139
|
+
disabled: n.isPending,
|
|
140
|
+
className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50",
|
|
141
|
+
children: n.isPending ? "Adding…" : "Add policy"
|
|
142
|
+
})]
|
|
143
|
+
})
|
|
144
|
+
]
|
|
145
|
+
})
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
function D() {
|
|
149
|
+
let { id: u } = v(), { navigateTo: f } = e(), { data: C, isLoading: w, isError: T, refetch: D } = l(u), O = d(), [k, A] = p(null), [j, M] = p(!1);
|
|
150
|
+
if (w) return /* @__PURE__ */ y(t, {
|
|
22
151
|
title: "Agent",
|
|
23
|
-
children: /* @__PURE__ */
|
|
152
|
+
children: /* @__PURE__ */ y(s, { label: "Loading agent…" })
|
|
24
153
|
});
|
|
25
|
-
if (
|
|
154
|
+
if (T) return /* @__PURE__ */ y(t, {
|
|
26
155
|
title: "Agent",
|
|
27
|
-
children: /* @__PURE__ */
|
|
156
|
+
children: /* @__PURE__ */ y(o, {
|
|
28
157
|
message: "Could not load this agent.",
|
|
29
|
-
onRetry: () => void
|
|
158
|
+
onRetry: () => void D()
|
|
30
159
|
})
|
|
31
160
|
});
|
|
32
|
-
if (!
|
|
161
|
+
if (!C) return /* @__PURE__ */ y(t, {
|
|
33
162
|
title: "Agent",
|
|
34
|
-
children: /* @__PURE__ */
|
|
163
|
+
children: /* @__PURE__ */ y(a, {
|
|
35
164
|
illustration: "empty-data",
|
|
36
165
|
title: "Agent not found",
|
|
37
166
|
description: "It may have been removed, or you may not have access to it.",
|
|
38
|
-
action: /* @__PURE__ */
|
|
167
|
+
action: /* @__PURE__ */ y("button", {
|
|
39
168
|
type: "button",
|
|
40
|
-
onClick: () =>
|
|
169
|
+
onClick: () => f("/agents"),
|
|
41
170
|
className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90",
|
|
42
171
|
children: "Back to agents"
|
|
43
172
|
})
|
|
44
173
|
})
|
|
45
174
|
});
|
|
46
|
-
let
|
|
47
|
-
return /* @__PURE__ */
|
|
48
|
-
title:
|
|
49
|
-
description: `${n(
|
|
50
|
-
actions: /* @__PURE__ */
|
|
175
|
+
let N = C;
|
|
176
|
+
return /* @__PURE__ */ b(t, {
|
|
177
|
+
title: N.name,
|
|
178
|
+
description: `${n(N.kind)} agent`,
|
|
179
|
+
actions: /* @__PURE__ */ b("button", {
|
|
51
180
|
type: "button",
|
|
52
|
-
onClick: () =>
|
|
181
|
+
onClick: () => f("/agents"),
|
|
53
182
|
className: "inline-flex items-center gap-2 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken",
|
|
54
|
-
children: [/* @__PURE__ */
|
|
183
|
+
children: [/* @__PURE__ */ y(m, { className: "h-4 w-4" }), " Back"]
|
|
55
184
|
}),
|
|
56
185
|
children: [
|
|
57
|
-
/* @__PURE__ */
|
|
186
|
+
/* @__PURE__ */ b("div", {
|
|
58
187
|
className: "flex flex-wrap items-center gap-3",
|
|
59
188
|
children: [
|
|
60
|
-
/* @__PURE__ */
|
|
189
|
+
/* @__PURE__ */ y("span", {
|
|
61
190
|
className: "flex size-10 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary",
|
|
62
|
-
children: /* @__PURE__ */
|
|
191
|
+
children: /* @__PURE__ */ y(h, { className: "h-5 w-5" })
|
|
63
192
|
}),
|
|
64
|
-
/* @__PURE__ */
|
|
65
|
-
/* @__PURE__ */
|
|
193
|
+
/* @__PURE__ */ y(i, { status: N.status }),
|
|
194
|
+
/* @__PURE__ */ y("span", {
|
|
66
195
|
className: "text-sm text-text-secondary",
|
|
67
|
-
children:
|
|
196
|
+
children: N.lastRunAt ? `Last run ${r(N.lastRunAt)}` : "Never run"
|
|
68
197
|
})
|
|
69
198
|
]
|
|
70
199
|
}),
|
|
71
|
-
|
|
200
|
+
N.description && /* @__PURE__ */ y("p", {
|
|
72
201
|
className: "mt-4 max-w-3xl text-sm text-text-secondary",
|
|
73
|
-
children:
|
|
202
|
+
children: N.description
|
|
74
203
|
}),
|
|
75
|
-
|
|
204
|
+
k && /* @__PURE__ */ y("div", {
|
|
76
205
|
className: "mt-4 rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text",
|
|
77
|
-
children:
|
|
206
|
+
children: k
|
|
78
207
|
}),
|
|
79
|
-
/* @__PURE__ */
|
|
208
|
+
/* @__PURE__ */ b("div", {
|
|
80
209
|
className: "mt-6 max-w-sm",
|
|
81
210
|
children: [
|
|
82
|
-
/* @__PURE__ */
|
|
211
|
+
/* @__PURE__ */ y("label", {
|
|
83
212
|
htmlFor: "agent-posture",
|
|
84
213
|
className: "mb-1 block text-xs font-medium text-text-secondary",
|
|
85
214
|
children: "Operating posture"
|
|
86
215
|
}),
|
|
87
|
-
/* @__PURE__ */
|
|
216
|
+
/* @__PURE__ */ y("select", {
|
|
88
217
|
id: "agent-posture",
|
|
89
|
-
value:
|
|
90
|
-
disabled:
|
|
218
|
+
value: N.status,
|
|
219
|
+
disabled: O.isPending,
|
|
91
220
|
onChange: (e) => {
|
|
92
|
-
|
|
93
|
-
id:
|
|
221
|
+
A(null), O.mutate({
|
|
222
|
+
id: N.id,
|
|
94
223
|
status: e.target.value
|
|
95
|
-
}, { onError: (e) =>
|
|
224
|
+
}, { onError: (e) => A(e instanceof Error ? e.message : "Could not change posture.") });
|
|
96
225
|
},
|
|
97
226
|
className: "w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] disabled:opacity-50",
|
|
98
|
-
children:
|
|
227
|
+
children: S.map((e) => /* @__PURE__ */ y("option", {
|
|
99
228
|
value: e,
|
|
100
229
|
children: n(e)
|
|
101
230
|
}, e))
|
|
102
231
|
}),
|
|
103
|
-
/* @__PURE__ */
|
|
232
|
+
/* @__PURE__ */ y("p", {
|
|
104
233
|
className: "mt-1.5 text-xs text-text-secondary",
|
|
105
234
|
children: "Disabled never acts; Suggest-only proposes; Autonomous acts within its policies."
|
|
106
235
|
})
|
|
107
236
|
]
|
|
108
237
|
}),
|
|
109
|
-
/* @__PURE__ */
|
|
238
|
+
/* @__PURE__ */ y(c, {
|
|
110
239
|
title: "Policies",
|
|
111
240
|
description: "The autonomous rules that bound this agent",
|
|
112
|
-
|
|
241
|
+
action: /* @__PURE__ */ b("button", {
|
|
242
|
+
type: "button",
|
|
243
|
+
onClick: () => M(!0),
|
|
244
|
+
className: "inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken",
|
|
245
|
+
children: [/* @__PURE__ */ y(g, { className: "h-3.5 w-3.5" }), " Add policy"]
|
|
246
|
+
}),
|
|
247
|
+
children: !N.policies || N.policies.length === 0 ? /* @__PURE__ */ y(a, {
|
|
113
248
|
illustration: "empty-data",
|
|
114
249
|
title: "No policies",
|
|
115
250
|
description: "Policies define what this agent may do autonomously, and what it must alert on."
|
|
116
|
-
}) : /* @__PURE__ */
|
|
251
|
+
}) : /* @__PURE__ */ y("div", {
|
|
117
252
|
className: "grid grid-cols-1 gap-3 sm:grid-cols-2",
|
|
118
|
-
children:
|
|
253
|
+
children: N.policies.map((e) => /* @__PURE__ */ b(x, {
|
|
119
254
|
treatment: "metric",
|
|
120
255
|
className: "p-4",
|
|
121
256
|
children: [
|
|
122
|
-
/* @__PURE__ */
|
|
257
|
+
/* @__PURE__ */ b("div", {
|
|
123
258
|
className: "flex items-start justify-between gap-2",
|
|
124
|
-
children: [/* @__PURE__ */
|
|
259
|
+
children: [/* @__PURE__ */ y("p", {
|
|
125
260
|
className: "text-sm font-medium text-text-primary",
|
|
126
261
|
children: e.name
|
|
127
|
-
}), /* @__PURE__ */
|
|
262
|
+
}), /* @__PURE__ */ y(i, { status: e.isEnabled ? "ACTIVE" : "DISABLED" })]
|
|
128
263
|
}),
|
|
129
|
-
/* @__PURE__ */
|
|
264
|
+
/* @__PURE__ */ b("p", {
|
|
130
265
|
className: "mt-1 flex items-center gap-1.5 text-xs text-text-secondary",
|
|
131
266
|
children: [
|
|
132
|
-
/* @__PURE__ */
|
|
267
|
+
/* @__PURE__ */ y(_, { className: "h-3.5 w-3.5" }),
|
|
133
268
|
" Action: ",
|
|
134
269
|
n(e.action)
|
|
135
270
|
]
|
|
136
271
|
}),
|
|
137
|
-
e.description && /* @__PURE__ */
|
|
272
|
+
e.description && /* @__PURE__ */ y("p", {
|
|
138
273
|
className: "mt-2 text-xs text-text-secondary",
|
|
139
274
|
children: e.description
|
|
140
275
|
}),
|
|
141
|
-
/* @__PURE__ */
|
|
276
|
+
/* @__PURE__ */ b("p", {
|
|
142
277
|
className: "mt-2 text-xs text-text-secondary",
|
|
143
278
|
children: [
|
|
144
279
|
"Triggered ",
|
|
@@ -150,11 +285,15 @@ function b() {
|
|
|
150
285
|
]
|
|
151
286
|
}, e.id))
|
|
152
287
|
})
|
|
288
|
+
}),
|
|
289
|
+
j && /* @__PURE__ */ y(E, {
|
|
290
|
+
agentId: N.id,
|
|
291
|
+
onClose: () => M(!1)
|
|
153
292
|
})
|
|
154
293
|
]
|
|
155
294
|
});
|
|
156
295
|
}
|
|
157
296
|
//#endregion
|
|
158
|
-
export {
|
|
297
|
+
export { D as AgentDetailPage };
|
|
159
298
|
|
|
160
299
|
//# sourceMappingURL=AgentDetailPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentDetailPage.js","names":[],"sources":["../../../../src/subscriberbot/pages/agents/AgentDetailPage.tsx"],"sourcesContent":["/**\n * Agent detail\n * @module subscriberbot/pages/agents\n *\n * A single AI agent: its kind, posture, last run, and the autonomous policies\n * that bound it. Reads `subAgent(id)`; changes posture via `subSetAgentStatus`.\n */\n\nimport { useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft, Bot, ShieldCheck } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView, Section } from '../../components/StateViews';\nimport { useSubscriberBot } from '../../context/SubscriberBotContext';\nimport { useAgent, useSetAgentStatus } from '../../hooks/useSubscriberBotApi';\nimport { formatEnumLabel, formatRelativeTime } from '../../utils/formatters';\nimport type { SubAgentStatus } from '../../types';\n\nconst STATUS_OPTIONS: SubAgentStatus[] = ['DISABLED', 'SUGGEST_ONLY', 'AUTONOMOUS'];\n\nexport function AgentDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useSubscriberBot();\n const { data: agent, isLoading, isError, refetch } = useAgent(id);\n const setAgentStatus = useSetAgentStatus();\n const [actionError, setActionError] = useState<string | null>(null);\n\n if (isLoading) {\n return (\n <PageLayout title=\"Agent\">\n <LoadingView label=\"Loading agent…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout title=\"Agent\">\n <ErrorView message=\"Could not load this agent.\" onRetry={() => void refetch()} />\n </PageLayout>\n );\n }\n\n if (!agent) {\n return (\n <PageLayout title=\"Agent\">\n <EmptyState\n illustration=\"empty-data\"\n title=\"Agent not found\"\n description=\"It may have been removed, or you may not have access to it.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/agents')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n Back to agents\n </button>\n }\n />\n </PageLayout>\n );\n }\n\n const a = agent;\n\n return (\n <PageLayout\n title={a.name}\n description={`${formatEnumLabel(a.kind)} agent`}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/agents')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back\n </button>\n }\n >\n <div className=\"flex flex-wrap items-center gap-3\">\n <span className=\"flex size-10 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Bot className=\"h-5 w-5\" />\n </span>\n <StatusBadge status={a.status} />\n <span className=\"text-sm text-text-secondary\">\n {a.lastRunAt ? `Last run ${formatRelativeTime(a.lastRunAt)}` : 'Never run'}\n </span>\n </div>\n\n {a.description && (\n <p className=\"mt-4 max-w-3xl text-sm text-text-secondary\">{a.description}</p>\n )}\n\n {actionError && (\n <div className=\"mt-4 rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {actionError}\n </div>\n )}\n\n {/* Posture control */}\n <div className=\"mt-6 max-w-sm\">\n <label\n htmlFor=\"agent-posture\"\n className=\"mb-1 block text-xs font-medium text-text-secondary\"\n >\n Operating posture\n </label>\n <select\n id=\"agent-posture\"\n value={a.status}\n disabled={setAgentStatus.isPending}\n onChange={(e) => {\n setActionError(null);\n setAgentStatus.mutate(\n { id: a.id, status: e.target.value as SubAgentStatus },\n {\n onError: (err) =>\n setActionError(err instanceof Error ? err.message : 'Could not change posture.'),\n }\n );\n }}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] disabled:opacity-50\"\n >\n {STATUS_OPTIONS.map((s) => (\n <option key={s} value={s}>\n {formatEnumLabel(s)}\n </option>\n ))}\n </select>\n <p className=\"mt-1.5 text-xs text-text-secondary\">\n Disabled never acts; Suggest-only proposes; Autonomous acts within its policies.\n </p>\n </div>\n\n {/* Policies */}\n <Section title=\"Policies\" description=\"The autonomous rules that bound this agent\">\n {!a.policies || a.policies.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No policies\"\n description=\"Policies define what this agent may do autonomously, and what it must alert on.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {a.policies.map((policy) => (\n <GlassCard key={policy.id} treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"text-sm font-medium text-text-primary\">{policy.name}</p>\n <StatusBadge status={policy.isEnabled ? 'ACTIVE' : 'DISABLED'} />\n </div>\n <p className=\"mt-1 flex items-center gap-1.5 text-xs text-text-secondary\">\n <ShieldCheck className=\"h-3.5 w-3.5\" /> Action: {formatEnumLabel(policy.action)}\n </p>\n {policy.description && (\n <p className=\"mt-2 text-xs text-text-secondary\">{policy.description}</p>\n )}\n <p className=\"mt-2 text-xs text-text-secondary\">\n Triggered {policy.triggerCount}×\n {policy.lastTriggeredAt\n ? ` · last ${formatRelativeTime(policy.lastTriggeredAt)}`\n : ''}\n </p>\n </GlassCard>\n ))}\n </div>\n )}\n </Section>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgentDetailPage.js","names":[],"sources":["../../../../src/subscriberbot/pages/agents/AgentDetailPage.tsx"],"sourcesContent":["/**\n * Agent detail\n * @module subscriberbot/pages/agents\n *\n * A single AI agent: its kind, posture, last run, and the autonomous policies\n * that bound it. Reads `subAgent(id)`; changes posture via `subSetAgentStatus`.\n */\n\nimport { useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft, Bot, ShieldCheck, Plus } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { Modal } from '../../components/Modal';\nimport { LoadingView, ErrorView, Section } from '../../components/StateViews';\nimport { useSubscriberBot } from '../../context/SubscriberBotContext';\nimport { useAgent, useSetAgentStatus, useCreatePolicy } from '../../hooks/useSubscriberBotApi';\nimport { formatEnumLabel, formatRelativeTime } from '../../utils/formatters';\nimport type { SubAgentStatus, SubPolicyAction } from '../../types';\n\nconst STATUS_OPTIONS: SubAgentStatus[] = ['DISABLED', 'SUGGEST_ONLY', 'AUTONOMOUS'];\n\nconst POLICY_ACTIONS: SubPolicyAction[] = [\n 'ALERT',\n 'CREATE_TASK',\n 'RENEW',\n 'CANCEL',\n 'PAUSE',\n 'UPGRADE',\n 'DOWNGRADE',\n];\n\nconst inputCls =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\nconst labelCls = 'mb-1 block text-xs font-medium text-text-secondary';\n\n// ---------------------------------------------------------------------------\n// Add policy modal → subCreatePolicy\n// ---------------------------------------------------------------------------\n\nfunction AddPolicyModal({ agentId, onClose }: { agentId: string; onClose: () => void }) {\n const create = useCreatePolicy();\n const [name, setName] = useState('');\n const [action, setAction] = useState<SubPolicyAction>('ALERT');\n const [description, setDescription] = useState('');\n const [condition, setCondition] = useState('');\n const [isEnabled, setIsEnabled] = useState(true);\n const [formError, setFormError] = useState<string | null>(null);\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n setFormError(null);\n if (!name.trim()) {\n setFormError('A policy name is required.');\n return;\n }\n let parsedCondition: Record<string, unknown> | undefined;\n if (condition.trim()) {\n try {\n parsedCondition = JSON.parse(condition) as Record<string, unknown>;\n } catch {\n setFormError('Condition must be valid JSON (e.g. {\"amountOver\": 100}).');\n return;\n }\n }\n create.mutate(\n {\n agentId,\n name: name.trim(),\n action,\n isEnabled,\n ...(description.trim() ? { description: description.trim() } : {}),\n ...(parsedCondition ? { condition: parsedCondition } : {}),\n },\n {\n onSuccess: () => onClose(),\n onError: (err) =>\n setFormError(err instanceof Error ? err.message : 'Could not add policy.'),\n }\n );\n };\n\n return (\n <Modal title=\"Add policy\" onClose={onClose} maxWidthClassName=\"max-w-xl\">\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n {formError && (\n <div className=\"rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {formError}\n </div>\n )}\n <div>\n <label htmlFor=\"policy-name\" className={labelCls}>\n Name <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"policy-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Alert on price hike over 20%\"\n className={inputCls}\n required\n />\n </div>\n <div>\n <label htmlFor=\"policy-action\" className={labelCls}>\n Action\n </label>\n <select\n id=\"policy-action\"\n value={action}\n onChange={(e) => setAction(e.target.value as SubPolicyAction)}\n className={inputCls}\n >\n {POLICY_ACTIONS.map((a) => (\n <option key={a} value={a}>\n {a.replace(/_/g, ' ')}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"policy-condition\" className={labelCls}>\n Condition <span className=\"text-text-secondary\">(optional JSON)</span>\n </label>\n <textarea\n id=\"policy-condition\"\n value={condition}\n onChange={(e) => setCondition(e.target.value)}\n rows={3}\n placeholder={'{ \"priceIncreasePct\": 20 }'}\n className={`${inputCls} font-mono`}\n />\n </div>\n <div>\n <label htmlFor=\"policy-desc\" className={labelCls}>\n Description\n </label>\n <textarea\n id=\"policy-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n className={inputCls}\n />\n </div>\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={isEnabled}\n onChange={(e) => setIsEnabled(e.target.checked)}\n className=\"size-4 rounded border-border-subtle\"\n />\n Enabled\n </label>\n <div className=\"flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"rounded-lg border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={create.isPending}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90 disabled:opacity-50\"\n >\n {create.isPending ? 'Adding…' : 'Add policy'}\n </button>\n </div>\n </form>\n </Modal>\n );\n}\n\nexport function AgentDetailPage() {\n const { id } = useParams<{ id: string }>();\n const { navigateTo } = useSubscriberBot();\n const { data: agent, isLoading, isError, refetch } = useAgent(id);\n const setAgentStatus = useSetAgentStatus();\n const [actionError, setActionError] = useState<string | null>(null);\n const [showAddPolicy, setShowAddPolicy] = useState(false);\n\n if (isLoading) {\n return (\n <PageLayout title=\"Agent\">\n <LoadingView label=\"Loading agent…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout title=\"Agent\">\n <ErrorView message=\"Could not load this agent.\" onRetry={() => void refetch()} />\n </PageLayout>\n );\n }\n\n if (!agent) {\n return (\n <PageLayout title=\"Agent\">\n <EmptyState\n illustration=\"empty-data\"\n title=\"Agent not found\"\n description=\"It may have been removed, or you may not have access to it.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/agents')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n Back to agents\n </button>\n }\n />\n </PageLayout>\n );\n }\n\n const a = agent;\n\n return (\n <PageLayout\n title={a.name}\n description={`${formatEnumLabel(a.kind)} agent`}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/agents')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back\n </button>\n }\n >\n <div className=\"flex flex-wrap items-center gap-3\">\n <span className=\"flex size-10 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Bot className=\"h-5 w-5\" />\n </span>\n <StatusBadge status={a.status} />\n <span className=\"text-sm text-text-secondary\">\n {a.lastRunAt ? `Last run ${formatRelativeTime(a.lastRunAt)}` : 'Never run'}\n </span>\n </div>\n\n {a.description && (\n <p className=\"mt-4 max-w-3xl text-sm text-text-secondary\">{a.description}</p>\n )}\n\n {actionError && (\n <div className=\"mt-4 rounded-lg border border-status-error-text/30 bg-status-error-bg-subtle px-4 py-2 text-sm text-status-error-text\">\n {actionError}\n </div>\n )}\n\n {/* Posture control */}\n <div className=\"mt-6 max-w-sm\">\n <label\n htmlFor=\"agent-posture\"\n className=\"mb-1 block text-xs font-medium text-text-secondary\"\n >\n Operating posture\n </label>\n <select\n id=\"agent-posture\"\n value={a.status}\n disabled={setAgentStatus.isPending}\n onChange={(e) => {\n setActionError(null);\n setAgentStatus.mutate(\n { id: a.id, status: e.target.value as SubAgentStatus },\n {\n onError: (err) =>\n setActionError(err instanceof Error ? err.message : 'Could not change posture.'),\n }\n );\n }}\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] disabled:opacity-50\"\n >\n {STATUS_OPTIONS.map((s) => (\n <option key={s} value={s}>\n {formatEnumLabel(s)}\n </option>\n ))}\n </select>\n <p className=\"mt-1.5 text-xs text-text-secondary\">\n Disabled never acts; Suggest-only proposes; Autonomous acts within its policies.\n </p>\n </div>\n\n {/* Policies */}\n <Section\n title=\"Policies\"\n description=\"The autonomous rules that bound this agent\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowAddPolicy(true)}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-text-primary hover:bg-bg-sunken\"\n >\n <Plus className=\"h-3.5 w-3.5\" /> Add policy\n </button>\n }\n >\n {!a.policies || a.policies.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No policies\"\n description=\"Policies define what this agent may do autonomously, and what it must alert on.\"\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {a.policies.map((policy) => (\n <GlassCard key={policy.id} treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <p className=\"text-sm font-medium text-text-primary\">{policy.name}</p>\n <StatusBadge status={policy.isEnabled ? 'ACTIVE' : 'DISABLED'} />\n </div>\n <p className=\"mt-1 flex items-center gap-1.5 text-xs text-text-secondary\">\n <ShieldCheck className=\"h-3.5 w-3.5\" /> Action: {formatEnumLabel(policy.action)}\n </p>\n {policy.description && (\n <p className=\"mt-2 text-xs text-text-secondary\">{policy.description}</p>\n )}\n <p className=\"mt-2 text-xs text-text-secondary\">\n Triggered {policy.triggerCount}×\n {policy.lastTriggeredAt\n ? ` · last ${formatRelativeTime(policy.lastTriggeredAt)}`\n : ''}\n </p>\n </GlassCard>\n ))}\n </div>\n )}\n </Section>\n\n {showAddPolicy && <AddPolicyModal agentId={a.id} onClose={() => setShowAddPolicy(false)} />}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -78,6 +78,7 @@ function v() {
|
|
|
78
78
|
/* @__PURE__ */ m("div", {
|
|
79
79
|
className: "flex flex-wrap items-center gap-3",
|
|
80
80
|
children: [/* @__PURE__ */ p("select", {
|
|
81
|
+
"aria-label": "Filter by kind",
|
|
81
82
|
value: y,
|
|
82
83
|
onChange: (e) => b(e.target.value),
|
|
83
84
|
className: "rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
@@ -86,6 +87,7 @@ function v() {
|
|
|
86
87
|
children: e.label
|
|
87
88
|
}, e.value))
|
|
88
89
|
}), /* @__PURE__ */ m("select", {
|
|
90
|
+
"aria-label": "Filter by posture",
|
|
89
91
|
value: x,
|
|
90
92
|
onChange: (e) => S(e.target.value),
|
|
91
93
|
className: "rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentsListPage.js","names":[],"sources":["../../../../src/subscriberbot/pages/agents/AgentsListPage.tsx"],"sourcesContent":["/**\n * AI Agents list\n * @module subscriberbot/pages/agents\n *\n * The AI Agents Framework — specialized agents (Discovery, Renewal, Cost,\n * Newsletter, Vendor, Compliance, Negotiation, Migration, Concierge) that each\n * own a slice of the recurring-relationship problem. Filter by kind / posture.\n * Reads `subListAgents`; sets posture inline via `subSetAgentStatus`.\n */\n\nimport { useState } from 'react';\nimport { Plus, Bot } from 'lucide-react';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { useSubscriberBot } from '../../context/SubscriberBotContext';\nimport { useAgents, useSetAgentStatus } from '../../hooks/useSubscriberBotApi';\nimport { formatEnumLabel, formatRelativeTime } from '../../utils/formatters';\nimport type { SubAgentKind, SubAgentStatus } from '../../types';\n\nconst KIND_OPTIONS: Array<{ value: '' | SubAgentKind; label: string }> = [\n { value: '', label: 'All kinds' },\n { value: 'DISCOVERY', label: 'Discovery' },\n { value: 'RENEWAL', label: 'Renewal' },\n { value: 'COST', label: 'Cost' },\n { value: 'NEWSLETTER', label: 'Newsletter' },\n { value: 'VENDOR', label: 'Vendor' },\n { value: 'COMPLIANCE', label: 'Compliance' },\n { value: 'NEGOTIATION', label: 'Negotiation' },\n { value: 'MIGRATION', label: 'Migration' },\n { value: 'CONCIERGE', label: 'Concierge' },\n];\n\nconst STATUS_OPTIONS: SubAgentStatus[] = ['DISABLED', 'SUGGEST_ONLY', 'AUTONOMOUS'];\n\nexport function AgentsListPage() {\n const { navigateTo } = useSubscriberBot();\n const [kind, setKind] = useState<'' | SubAgentKind>('');\n const [status, setStatus] = useState<'' | SubAgentStatus>('');\n\n const { data, isLoading, isError, refetch } = useAgents({\n kind: kind || undefined,\n status: status || undefined,\n });\n const setAgentStatus = useSetAgentStatus();\n\n const agents = data ?? [];\n\n return (\n <PageLayout\n title=\"AI Agents\"\n description=\"Specialized agents that automate your recurring portfolio\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/agents/new')}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n <Plus className=\"h-4 w-4\" /> New agent\n </button>\n }\n >\n <PagePurpose className=\"mb-4\">\n Agents propose; your policies decide what they may do autonomously. Set each agent's posture\n — Disabled, Suggest-only, or Autonomous — and open one to review its policies.\n </PagePurpose>\n\n <div className=\"flex flex-wrap items-center gap-3\">\n <select\n value={kind}\n onChange={(e) => setKind(e.target.value as '' | SubAgentKind)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n {KIND_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n <select\n value={status}\n onChange={(e) => setStatus(e.target.value as '' | SubAgentStatus)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">Any posture</option>\n {STATUS_OPTIONS.map((s) => (\n <option key={s} value={s}>\n {formatEnumLabel(s)}\n </option>\n ))}\n </select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading agents…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load agents.\" onRetry={() => void refetch()} />\n ) : agents.length === 0 ? (\n <div className=\"mt-6\">\n <EmptyState\n illustration=\"empty-data\"\n icon={<Bot className=\"h-12 w-12\" />}\n title=\"No agents yet\"\n description=\"Create your first AI agent to start automating discovery, renewals, or cost optimization.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/agents/new')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n New agent\n </button>\n }\n />\n </div>\n ) : (\n <div className=\"mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {agents.map((a) => (\n <div\n key={a.id}\n className=\"flex flex-col rounded-lg border border-border-subtle bg-bg-surface p-4\"\n >\n <button\n type=\"button\"\n onClick={() => navigateTo(`/agents/${a.id}`)}\n className=\"text-left\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <span className=\"flex size-9 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Bot className=\"h-4 w-4\" />\n </span>\n <StatusBadge status={a.status} />\n </div>\n <p className=\"mt-3 truncate text-sm font-semibold text-text-primary\">{a.name}</p>\n <p className=\"text-xs text-text-secondary\">{formatEnumLabel(a.kind)} agent</p>\n {a.description && (\n <p className=\"mt-2 line-clamp-2 text-xs text-text-secondary\">{a.description}</p>\n )}\n <p className=\"mt-2 text-xs text-text-secondary\">\n {a.lastRunAt ? `Last run ${formatRelativeTime(a.lastRunAt)}` : 'Never run'}\n </p>\n </button>\n <div className=\"mt-3 border-t border-border-subtle pt-3\">\n <label className=\"mb-1 block text-[10px] font-medium uppercase tracking-wide text-text-secondary\">\n Posture\n </label>\n <select\n value={a.status}\n disabled={setAgentStatus.isPending}\n onChange={(e) =>\n setAgentStatus.mutate({ id: a.id, status: e.target.value as SubAgentStatus })\n }\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface px-2 py-1.5 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] disabled:opacity-50\"\n >\n {STATUS_OPTIONS.map((s) => (\n <option key={s} value={s}>\n {formatEnumLabel(s)}\n </option>\n ))}\n </select>\n </div>\n </div>\n ))}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AgentsListPage.js","names":[],"sources":["../../../../src/subscriberbot/pages/agents/AgentsListPage.tsx"],"sourcesContent":["/**\n * AI Agents list\n * @module subscriberbot/pages/agents\n *\n * The AI Agents Framework — specialized agents (Discovery, Renewal, Cost,\n * Newsletter, Vendor, Compliance, Negotiation, Migration, Concierge) that each\n * own a slice of the recurring-relationship problem. Filter by kind / posture.\n * Reads `subListAgents`; sets posture inline via `subSetAgentStatus`.\n */\n\nimport { useState } from 'react';\nimport { Plus, Bot } from 'lucide-react';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { useSubscriberBot } from '../../context/SubscriberBotContext';\nimport { useAgents, useSetAgentStatus } from '../../hooks/useSubscriberBotApi';\nimport { formatEnumLabel, formatRelativeTime } from '../../utils/formatters';\nimport type { SubAgentKind, SubAgentStatus } from '../../types';\n\nconst KIND_OPTIONS: Array<{ value: '' | SubAgentKind; label: string }> = [\n { value: '', label: 'All kinds' },\n { value: 'DISCOVERY', label: 'Discovery' },\n { value: 'RENEWAL', label: 'Renewal' },\n { value: 'COST', label: 'Cost' },\n { value: 'NEWSLETTER', label: 'Newsletter' },\n { value: 'VENDOR', label: 'Vendor' },\n { value: 'COMPLIANCE', label: 'Compliance' },\n { value: 'NEGOTIATION', label: 'Negotiation' },\n { value: 'MIGRATION', label: 'Migration' },\n { value: 'CONCIERGE', label: 'Concierge' },\n];\n\nconst STATUS_OPTIONS: SubAgentStatus[] = ['DISABLED', 'SUGGEST_ONLY', 'AUTONOMOUS'];\n\nexport function AgentsListPage() {\n const { navigateTo } = useSubscriberBot();\n const [kind, setKind] = useState<'' | SubAgentKind>('');\n const [status, setStatus] = useState<'' | SubAgentStatus>('');\n\n const { data, isLoading, isError, refetch } = useAgents({\n kind: kind || undefined,\n status: status || undefined,\n });\n const setAgentStatus = useSetAgentStatus();\n\n const agents = data ?? [];\n\n return (\n <PageLayout\n title=\"AI Agents\"\n description=\"Specialized agents that automate your recurring portfolio\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/agents/new')}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n <Plus className=\"h-4 w-4\" /> New agent\n </button>\n }\n >\n <PagePurpose className=\"mb-4\">\n Agents propose; your policies decide what they may do autonomously. Set each agent's posture\n — Disabled, Suggest-only, or Autonomous — and open one to review its policies.\n </PagePurpose>\n\n <div className=\"flex flex-wrap items-center gap-3\">\n <select\n aria-label=\"Filter by kind\"\n value={kind}\n onChange={(e) => setKind(e.target.value as '' | SubAgentKind)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n {KIND_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>\n {o.label}\n </option>\n ))}\n </select>\n <select\n aria-label=\"Filter by posture\"\n value={status}\n onChange={(e) => setStatus(e.target.value as '' | SubAgentStatus)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">Any posture</option>\n {STATUS_OPTIONS.map((s) => (\n <option key={s} value={s}>\n {formatEnumLabel(s)}\n </option>\n ))}\n </select>\n </div>\n\n {isLoading ? (\n <LoadingView label=\"Loading agents…\" />\n ) : isError ? (\n <ErrorView message=\"Could not load agents.\" onRetry={() => void refetch()} />\n ) : agents.length === 0 ? (\n <div className=\"mt-6\">\n <EmptyState\n illustration=\"empty-data\"\n icon={<Bot className=\"h-12 w-12\" />}\n title=\"No agents yet\"\n description=\"Create your first AI agent to start automating discovery, renewals, or cost optimization.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/agents/new')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:opacity-90\"\n >\n New agent\n </button>\n }\n />\n </div>\n ) : (\n <div className=\"mt-6 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {agents.map((a) => (\n <div\n key={a.id}\n className=\"flex flex-col rounded-lg border border-border-subtle bg-bg-surface p-4\"\n >\n <button\n type=\"button\"\n onClick={() => navigateTo(`/agents/${a.id}`)}\n className=\"text-left\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <span className=\"flex size-9 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Bot className=\"h-4 w-4\" />\n </span>\n <StatusBadge status={a.status} />\n </div>\n <p className=\"mt-3 truncate text-sm font-semibold text-text-primary\">{a.name}</p>\n <p className=\"text-xs text-text-secondary\">{formatEnumLabel(a.kind)} agent</p>\n {a.description && (\n <p className=\"mt-2 line-clamp-2 text-xs text-text-secondary\">{a.description}</p>\n )}\n <p className=\"mt-2 text-xs text-text-secondary\">\n {a.lastRunAt ? `Last run ${formatRelativeTime(a.lastRunAt)}` : 'Never run'}\n </p>\n </button>\n <div className=\"mt-3 border-t border-border-subtle pt-3\">\n <label className=\"mb-1 block text-[10px] font-medium uppercase tracking-wide text-text-secondary\">\n Posture\n </label>\n <select\n value={a.status}\n disabled={setAgentStatus.isPending}\n onChange={(e) =>\n setAgentStatus.mutate({ id: a.id, status: e.target.value as SubAgentStatus })\n }\n className=\"w-full rounded-lg border border-border-subtle bg-bg-surface px-2 py-1.5 text-xs text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] disabled:opacity-50\"\n >\n {STATUS_OPTIONS.map((s) => (\n <option key={s} value={s}>\n {formatEnumLabel(s)}\n </option>\n ))}\n </select>\n </div>\n </div>\n ))}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|