@elizaos/app-core 2.0.0-alpha.335 → 2.0.0-alpha.337

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/apps/app-lifeops/src/routes/lifeops-routes.d.ts.map +1 -1
  2. package/apps/app-lifeops/src/routes/lifeops-routes.js +10 -1
  3. package/i18n/locales/en.json +5 -5
  4. package/package.json +5 -5
  5. package/packages/agent/src/runtime/eliza.d.ts.map +1 -1
  6. package/packages/agent/src/runtime/eliza.js +7 -2
  7. package/packages/app-core/src/api/automations-compat-routes.js +1 -1
  8. package/packages/app-core/src/api/client-n8n.d.ts +4 -1
  9. package/packages/app-core/src/api/client-n8n.d.ts.map +1 -1
  10. package/packages/app-core/src/api/client-n8n.js +18 -0
  11. package/packages/app-core/src/api/client-types-chat.d.ts +37 -0
  12. package/packages/app-core/src/api/client-types-chat.d.ts.map +1 -1
  13. package/packages/app-core/src/api/n8n-routes.d.ts +6 -0
  14. package/packages/app-core/src/api/n8n-routes.d.ts.map +1 -1
  15. package/packages/app-core/src/api/n8n-routes.js +334 -0
  16. package/packages/app-core/src/components/character/CharacterEditorPanels.d.ts.map +1 -1
  17. package/packages/app-core/src/components/character/CharacterEditorPanels.js +192 -85
  18. package/packages/app-core/src/components/character/CharacterExperienceWorkspace.d.ts.map +1 -1
  19. package/packages/app-core/src/components/character/CharacterExperienceWorkspace.js +182 -31
  20. package/packages/app-core/src/components/character/CharacterHubView.d.ts.map +1 -1
  21. package/packages/app-core/src/components/character/CharacterHubView.js +253 -23
  22. package/packages/app-core/src/components/character/CharacterOverviewSection.d.ts +14 -1
  23. package/packages/app-core/src/components/character/CharacterOverviewSection.d.ts.map +1 -1
  24. package/packages/app-core/src/components/character/CharacterOverviewSection.js +35 -8
  25. package/packages/app-core/src/components/character/character-hub-helpers.d.ts.map +1 -1
  26. package/packages/app-core/src/components/character/character-hub-helpers.js +3 -7
  27. package/packages/app-core/src/components/pages/AutomationsView.d.ts.map +1 -1
  28. package/packages/app-core/src/components/pages/AutomationsView.js +523 -274
  29. package/packages/app-core/src/components/pages/HeartbeatForm.d.ts.map +1 -1
  30. package/packages/app-core/src/components/pages/HeartbeatForm.js +11 -13
  31. package/packages/app-core/src/components/pages/KnowledgeView.d.ts.map +1 -1
  32. package/packages/app-core/src/components/pages/KnowledgeView.js +20 -6
  33. package/packages/app-core/src/components/pages/RelationshipsGraphPanel.d.ts +2 -1
  34. package/packages/app-core/src/components/pages/RelationshipsGraphPanel.d.ts.map +1 -1
  35. package/packages/app-core/src/components/pages/RelationshipsGraphPanel.js +3 -3
  36. package/packages/app-core/src/components/pages/WorkflowGraphViewer.d.ts +0 -9
  37. package/packages/app-core/src/components/pages/WorkflowGraphViewer.d.ts.map +1 -1
  38. package/packages/app-core/src/components/pages/WorkflowGraphViewer.js +52 -26
  39. package/packages/app-core/src/components/pages/knowledge-detail.d.ts.map +1 -1
  40. package/packages/app-core/src/components/pages/knowledge-detail.js +28 -2
  41. package/packages/app-core/src/components/pages/page-scoped-conversations.d.ts +1 -1
  42. package/packages/app-core/src/components/pages/page-scoped-conversations.js +4 -4
  43. package/packages/app-core/src/components/pages/relationships/RelationshipsPersonPanels.d.ts +2 -1
  44. package/packages/app-core/src/components/pages/relationships/RelationshipsPersonPanels.d.ts.map +1 -1
  45. package/packages/app-core/src/components/pages/relationships/RelationshipsPersonPanels.js +69 -32
  46. package/packages/app-core/src/components/pages/relationships/RelationshipsSidebar.d.ts +1 -6
  47. package/packages/app-core/src/components/pages/relationships/RelationshipsSidebar.d.ts.map +1 -1
  48. package/packages/app-core/src/components/pages/relationships/RelationshipsSidebar.js +4 -4
  49. package/packages/app-core/src/components/pages/relationships/RelationshipsWorkspaceView.d.ts +2 -1
  50. package/packages/app-core/src/components/pages/relationships/RelationshipsWorkspaceView.d.ts.map +1 -1
  51. package/packages/app-core/src/components/pages/relationships/RelationshipsWorkspaceView.js +25 -15
  52. package/packages/app-core/src/i18n/locales/en.json +5 -5
  53. package/packages/app-core/src/registry/generate-apps.js +2 -2
  54. package/packages/app-core/src/runtime/eliza.d.ts.map +1 -1
  55. package/packages/app-core/src/runtime/eliza.js +6 -1
  56. package/packages/app-core/src/services/n8n-dispatch.d.ts +10 -2
  57. package/packages/app-core/src/services/n8n-dispatch.d.ts.map +1 -1
  58. package/packages/app-core/src/services/n8n-dispatch.js +102 -8
  59. package/packages/shared/src/contracts/lifeops.d.ts +4 -0
  60. package/packages/shared/src/contracts/lifeops.d.ts.map +1 -1
  61. package/packages/typescript/src/features/advanced-capabilities/clipboard/services/clipboardService.d.ts.map +1 -1
  62. package/packages/typescript/src/features/advanced-capabilities/clipboard/services/clipboardService.js +6 -1
@@ -32,40 +32,191 @@ function outcomeAccent(outcome) {
32
32
  return "text-status-info border-status-info/30 bg-status-info-bg";
33
33
  }
34
34
  }
35
- export function CharacterExperienceWorkspace({ experiences, selectedExperienceId, onSelectExperience, onSaveExperience, onDeleteExperience, savingExperienceId, deletingExperienceId, }) {
36
- const selectedExperience = useMemo(() => experiences.find((experience) => experience.id === selectedExperienceId) ??
35
+ function clampScore(value) {
36
+ if (typeof value !== "number" || Number.isNaN(value))
37
+ return 0;
38
+ return Math.max(0, Math.min(1, value));
39
+ }
40
+ function formatPercent(value) {
41
+ return `${Math.round(clampScore(value) * 100)}%`;
42
+ }
43
+ function getTimestampMs(value) {
44
+ if (value === null || value === undefined)
45
+ return 0;
46
+ const timestamp = new Date(value).getTime();
47
+ return Number.isNaN(timestamp) ? 0 : timestamp;
48
+ }
49
+ function getPriorityScore(experience) {
50
+ const importance = clampScore(experience.importance);
51
+ const confidence = clampScore(experience.confidence);
52
+ const correctionBoost = experience.previousBelief || experience.correctedBelief ? 0.18 : 0;
53
+ const supersessionBoost = experience.supersedes ? 0.08 : 0;
54
+ return (importance * 0.64 +
55
+ (1 - confidence) * 0.28 +
56
+ correctionBoost +
57
+ supersessionBoost);
58
+ }
59
+ function needsReview(experience) {
60
+ return (clampScore(experience.confidence) < 0.65 ||
61
+ clampScore(experience.importance) >= 0.75 ||
62
+ Boolean(experience.previousBelief || experience.correctedBelief));
63
+ }
64
+ function compactText(...values) {
65
+ return values.filter(Boolean).join(" ").toLowerCase();
66
+ }
67
+ function uniqueSorted(values) {
68
+ return Array.from(new Set(values.map((value) => value?.trim()).filter(Boolean))).sort((left, right) => left.localeCompare(right));
69
+ }
70
+ function selectedOrFirst(experiences, selectedExperienceId) {
71
+ return (experiences.find((experience) => experience.id === selectedExperienceId) ??
37
72
  experiences[0] ??
38
- null, [experiences, selectedExperienceId]);
73
+ null);
74
+ }
75
+ function StatTile({ label, value, detail, }) {
76
+ return (_jsxs("div", { className: "min-w-0 rounded-xl border border-border/30 bg-bg-muted/15 px-3 py-2", children: [_jsx("div", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: label }), _jsx("div", { className: "mt-1 text-lg font-semibold leading-tight text-txt", children: value }), _jsx("div", { className: "mt-0.5 truncate text-xs text-muted", children: detail })] }));
77
+ }
78
+ function ScoreBar({ label, value, explanation, }) {
79
+ const percent = formatPercent(value);
80
+ return (_jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "flex items-center justify-between gap-3 text-xs", children: [_jsx("span", { className: "font-semibold text-muted-strong", children: label }), _jsx("span", { className: "font-mono text-muted", children: percent })] }), _jsx("div", { className: "mt-1 h-2 overflow-hidden rounded-full bg-bg-muted", children: _jsx("div", { className: "h-full rounded-full bg-accent", style: { width: percent } }) }), _jsx("p", { className: "mt-1 text-xs leading-relaxed text-muted", children: explanation })] }));
81
+ }
82
+ function EvidencePanel({ title, body, }) {
83
+ return (_jsxs("div", { className: "min-w-0 rounded-xl border border-border/30 bg-bg-muted/15 p-3", children: [_jsx("div", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: title }), _jsx("p", { className: "mt-2 whitespace-pre-wrap text-sm leading-relaxed text-muted-strong", children: body || "Not recorded." })] }));
84
+ }
85
+ export function CharacterExperienceWorkspace({ experiences, selectedExperienceId, onSelectExperience, onSaveExperience, onDeleteExperience, savingExperienceId, deletingExperienceId, }) {
86
+ const [searchQuery, setSearchQuery] = useState("");
87
+ const [outcomeFilter, setOutcomeFilter] = useState("all");
88
+ const [domainFilter, setDomainFilter] = useState("all");
89
+ const [tagFilter, setTagFilter] = useState("all");
90
+ const [reviewFilter, setReviewFilter] = useState("all");
91
+ const [sortMode, setSortMode] = useState("priority");
92
+ const selectedExperience = useMemo(() => selectedOrFirst(experiences, selectedExperienceId), [experiences, selectedExperienceId]);
39
93
  const [draft, setDraft] = useState(normalizeDraft(selectedExperience));
94
+ const filters = useMemo(() => {
95
+ const outcomes = uniqueSorted(experiences.map((experience) => experience.outcome));
96
+ const domains = uniqueSorted(experiences.map((experience) => experience.domain));
97
+ const tags = uniqueSorted(experiences.flatMap((experience) => experience.tags));
98
+ return { outcomes, domains, tags };
99
+ }, [experiences]);
100
+ const stats = useMemo(() => {
101
+ const total = experiences.length;
102
+ const reviewCount = experiences.filter(needsReview).length;
103
+ const averageImportance = total === 0
104
+ ? 0
105
+ : experiences.reduce((sum, experience) => sum + clampScore(experience.importance), 0) / total;
106
+ const averageConfidence = total === 0
107
+ ? 0
108
+ : experiences.reduce((sum, experience) => sum + clampScore(experience.confidence), 0) / total;
109
+ const corrections = experiences.filter((experience) => experience.previousBelief || experience.correctedBelief).length;
110
+ return {
111
+ averageConfidence,
112
+ averageImportance,
113
+ corrections,
114
+ reviewCount,
115
+ total,
116
+ };
117
+ }, [experiences]);
118
+ const filteredExperiences = useMemo(() => {
119
+ const query = searchQuery.trim().toLowerCase();
120
+ const filtered = experiences.filter((experience) => {
121
+ if (outcomeFilter !== "all" && experience.outcome !== outcomeFilter) {
122
+ return false;
123
+ }
124
+ if (domainFilter !== "all" && experience.domain !== domainFilter) {
125
+ return false;
126
+ }
127
+ if (tagFilter !== "all" && !experience.tags.includes(tagFilter)) {
128
+ return false;
129
+ }
130
+ if (reviewFilter === "needs-review" && !needsReview(experience)) {
131
+ return false;
132
+ }
133
+ if (reviewFilter === "corrected" &&
134
+ !experience.previousBelief &&
135
+ !experience.correctedBelief) {
136
+ return false;
137
+ }
138
+ if (reviewFilter === "superseded" && !experience.supersedes) {
139
+ return false;
140
+ }
141
+ if (!query)
142
+ return true;
143
+ const haystack = compactText(experience.type, experience.outcome, experience.domain, experience.context, experience.action, experience.result, experience.learning, experience.previousBelief, experience.correctedBelief, experience.supersedes, ...(experience.relatedExperienceIds ?? []), ...experience.tags);
144
+ return haystack.includes(query);
145
+ });
146
+ return [...filtered].sort((left, right) => {
147
+ switch (sortMode) {
148
+ case "confidence":
149
+ return clampScore(left.confidence) - clampScore(right.confidence);
150
+ case "importance":
151
+ return clampScore(right.importance) - clampScore(left.importance);
152
+ case "newest":
153
+ return (getTimestampMs(right.createdAt) - getTimestampMs(left.createdAt));
154
+ }
155
+ return getPriorityScore(right) - getPriorityScore(left);
156
+ });
157
+ }, [
158
+ domainFilter,
159
+ experiences,
160
+ outcomeFilter,
161
+ reviewFilter,
162
+ searchQuery,
163
+ sortMode,
164
+ tagFilter,
165
+ ]);
166
+ const visibleSelectedExperience = useMemo(() => selectedOrFirst(filteredExperiences, selectedExperience?.id ?? null), [filteredExperiences, selectedExperience?.id]);
40
167
  useEffect(() => {
41
- setDraft(normalizeDraft(selectedExperience));
42
- }, [selectedExperience]);
168
+ setDraft(normalizeDraft(visibleSelectedExperience));
169
+ }, [visibleSelectedExperience]);
170
+ const selectedRelatedExperiences = useMemo(() => {
171
+ const ids = new Set(visibleSelectedExperience?.relatedExperienceIds ?? []);
172
+ return experiences.filter((experience) => ids.has(experience.id));
173
+ }, [experiences, visibleSelectedExperience?.relatedExperienceIds]);
174
+ const supersededExperience = useMemo(() => visibleSelectedExperience?.supersedes
175
+ ? experiences.find((experience) => experience.id === visibleSelectedExperience.supersedes)
176
+ : null, [experiences, visibleSelectedExperience]);
43
177
  if (experiences.length === 0) {
44
- return (_jsx("section", { className: "rounded-2xl border border-dashed border-border/40 bg-bg-muted/20 px-5 py-8 text-sm text-muted", children: "No experiences recorded yet." }));
178
+ return (_jsxs("section", { className: "rounded-2xl border border-dashed border-border/40 bg-bg-muted/20 px-5 py-8 text-sm text-muted", children: [_jsx("div", { className: "text-base font-semibold text-txt", children: "No experiences recorded yet." }), _jsx("p", { className: "mt-1 max-w-xl", children: "Learned outcomes will appear here with confidence, importance, source context, and review controls once the agent captures experience." })] }));
45
179
  }
46
- return (_jsxs("section", { className: "grid min-w-0 gap-4 xl:grid-cols-[minmax(18rem,22rem)_minmax(0,1fr)]", children: [_jsxs("div", { className: "flex min-h-[24rem] min-w-0 flex-col overflow-hidden rounded-2xl border border-border/40 bg-bg/70", children: [_jsxs("div", { className: "border-b border-border/30 px-4 py-3", children: [_jsx("h3", { className: "text-base font-semibold text-txt", children: "Experience" }), _jsx("p", { className: "text-sm text-muted", children: "Review what the agent learned and edit the takeaway when needed." })] }), _jsx("div", { className: "custom-scrollbar flex min-w-0 flex-1 flex-col overflow-y-auto", children: experiences.map((experience) => {
47
- const isSelected = experience.id === selectedExperience?.id;
48
- return (_jsxs("button", { type: "button", className: `flex min-w-0 flex-col items-start gap-2 border-b border-border/20 px-4 py-4 text-left transition-colors hover:bg-bg-muted/20 ${isSelected ? "bg-bg-muted/25" : ""}`, onClick: () => onSelectExperience(experience.id), children: [_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [_jsx("span", { className: "rounded-full border border-border/40 px-2 py-0.5 text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: experience.type }), _jsx("span", { className: `rounded-full border px-2 py-0.5 text-[0.65rem] font-semibold uppercase tracking-[0.08em] ${outcomeAccent(experience.outcome)}`, children: experience.outcome })] }), _jsx("h4", { className: "line-clamp-2 text-sm font-semibold text-txt", children: experience.learning ||
49
- experience.result ||
50
- experience.context }), _jsx("p", { className: "line-clamp-2 text-sm text-muted-strong", children: experience.context }), _jsx("span", { className: "text-xs text-muted", children: formatTimestamp(experience.createdAt) })] }, experience.id));
51
- }) })] }), selectedExperience ? (_jsxs("div", { className: "flex min-w-0 flex-col gap-4 rounded-2xl border border-border/40 bg-bg/70 px-4 py-4", children: [_jsxs("div", { className: "flex min-w-0 flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [_jsx("span", { className: "rounded-full border border-border/40 px-2 py-0.5 text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: selectedExperience.type }), _jsx("span", { className: `rounded-full border px-2 py-0.5 text-[0.65rem] font-semibold uppercase tracking-[0.08em] ${outcomeAccent(selectedExperience.outcome)}`, children: selectedExperience.outcome }), selectedExperience.domain ? (_jsx("span", { className: "rounded-full border border-border/40 px-2 py-0.5 text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: selectedExperience.domain })) : null] }), _jsxs("p", { className: "mt-2 text-xs text-muted", children: ["Created ", formatTimestamp(selectedExperience.createdAt), selectedExperience.updatedAt
52
- ? ` · Updated ${formatTimestamp(selectedExperience.updatedAt)}`
53
- : ""] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [onDeleteExperience ? (_jsx(Button, { type: "button", variant: "outline", size: "sm", className: "rounded-lg", disabled: deletingExperienceId === selectedExperience.id, onClick: () => onDeleteExperience(selectedExperience), children: deletingExperienceId === selectedExperience.id
54
- ? "Deleting..."
55
- : "Delete" })) : null, onSaveExperience ? (_jsx(Button, { type: "button", size: "sm", className: "rounded-lg", disabled: savingExperienceId === selectedExperience.id, onClick: () => onSaveExperience(selectedExperience, draft), children: savingExperienceId === selectedExperience.id
56
- ? "Saving..."
57
- : "Save" })) : null] })] }), _jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [_jsxs("div", { className: "rounded-xl border border-border/30 bg-bg-muted/20 p-3", children: [_jsx("div", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: "Context" }), _jsx("p", { className: "mt-2 whitespace-pre-wrap text-sm leading-relaxed text-muted-strong", children: selectedExperience.context })] }), _jsxs("div", { className: "rounded-xl border border-border/30 bg-bg-muted/20 p-3", children: [_jsx("div", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: "Action" }), _jsx("p", { className: "mt-2 whitespace-pre-wrap text-sm leading-relaxed text-muted-strong", children: selectedExperience.action })] }), _jsxs("div", { className: "rounded-xl border border-border/30 bg-bg-muted/20 p-3 lg:col-span-2", children: [_jsx("div", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: "Result" }), _jsx("p", { className: "mt-2 whitespace-pre-wrap text-sm leading-relaxed text-muted-strong", children: selectedExperience.result })] })] }), _jsxs("label", { htmlFor: `experience-learning-${selectedExperience.id}`, className: "flex min-w-0 flex-col gap-2", children: [_jsx("span", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: "Learning" }), _jsx(Textarea, { id: `experience-learning-${selectedExperience.id}`, value: draft.learning, rows: 8, onChange: (event) => setDraft((current) => ({
58
- ...current,
59
- learning: event.target.value,
60
- })), className: "min-h-[10rem] resize-y rounded-xl border-border/40 bg-bg-muted/15 font-mono text-sm leading-relaxed" })] }), _jsxs("div", { className: "grid gap-4 md:grid-cols-3", children: [_jsxs("label", { htmlFor: `experience-importance-${selectedExperience.id}`, className: "flex min-w-0 flex-col gap-2", children: [_jsx("span", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: "Importance" }), _jsx(Input, { id: `experience-importance-${selectedExperience.id}`, type: "number", min: "0", max: "1", step: "0.05", value: String(draft.importance), onChange: (event) => setDraft((current) => ({
61
- ...current,
62
- importance: Number(event.target.value || 0),
63
- })), className: "rounded-xl border-border/40 bg-bg-muted/15" })] }), _jsxs("label", { htmlFor: `experience-confidence-${selectedExperience.id}`, className: "flex min-w-0 flex-col gap-2", children: [_jsx("span", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: "Confidence" }), _jsx(Input, { id: `experience-confidence-${selectedExperience.id}`, type: "number", min: "0", max: "1", step: "0.05", value: String(draft.confidence), onChange: (event) => setDraft((current) => ({
64
- ...current,
65
- confidence: Number(event.target.value || 0),
66
- })), className: "rounded-xl border-border/40 bg-bg-muted/15" })] }), _jsxs("label", { htmlFor: `experience-tags-${selectedExperience.id}`, className: "flex min-w-0 flex-col gap-2", children: [_jsx("span", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: "Tags" }), _jsx(Input, { id: `experience-tags-${selectedExperience.id}`, type: "text", value: draft.tags, onChange: (event) => setDraft((current) => ({
67
- ...current,
68
- tags: event.target.value,
69
- })), className: "rounded-xl border-border/40 bg-bg-muted/15" })] })] }), selectedExperience.previousBelief ||
70
- selectedExperience.correctedBelief ? (_jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [_jsxs("div", { className: "rounded-xl border border-border/30 bg-bg-muted/20 p-3", children: [_jsx("div", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: "Previous belief" }), _jsx("p", { className: "mt-2 whitespace-pre-wrap text-sm leading-relaxed text-muted-strong", children: selectedExperience.previousBelief || "Not recorded." })] }), _jsxs("div", { className: "rounded-xl border border-border/30 bg-bg-muted/20 p-3", children: [_jsx("div", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: "Corrected belief" }), _jsx("p", { className: "mt-2 whitespace-pre-wrap text-sm leading-relaxed text-muted-strong", children: selectedExperience.correctedBelief || "Not recorded." })] })] })) : null] })) : null] }));
180
+ return (_jsxs("section", { className: "flex min-w-0 flex-col gap-4", children: [_jsxs("div", { className: "rounded-2xl border border-border/40 bg-bg/70 p-4", children: [_jsxs("div", { className: "flex min-w-0 flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h3", { className: "text-base font-semibold text-txt", children: "Experience" }), _jsx("p", { className: "text-sm text-muted", children: "Triage learned outcomes by priority, confidence, evidence, and correction history." })] }), _jsxs("div", { className: "rounded-full border border-border/40 px-3 py-1 text-xs font-semibold text-muted", children: [filteredExperiences.length, " of ", experiences.length, " shown"] })] }), _jsxs("div", { className: "mt-4 grid gap-3 md:grid-cols-4", children: [_jsx(StatTile, { label: "Captured", value: String(stats.total), detail: `${stats.reviewCount} need review` }), _jsx(StatTile, { label: "Avg importance", value: formatPercent(stats.averageImportance), detail: "Ranking weight" }), _jsx(StatTile, { label: "Avg confidence", value: formatPercent(stats.averageConfidence), detail: "Evidence strength" }), _jsx(StatTile, { label: "Corrections", value: String(stats.corrections), detail: "Beliefs revised" })] }), _jsxs("div", { className: "mt-4 grid gap-3 lg:grid-cols-[minmax(14rem,1.4fr)_repeat(5,minmax(8rem,1fr))]", children: [_jsxs("label", { htmlFor: "experience-search", className: "min-w-0", children: [_jsx("span", { className: "sr-only", children: "Search experiences" }), _jsx(Input, { id: "experience-search", type: "search", value: searchQuery, onChange: (event) => setSearchQuery(event.target.value), placeholder: "Search learning, evidence, tags...", className: "h-9 rounded-xl border-border/40 bg-bg-muted/15" })] }), _jsxs("label", { className: "min-w-0", children: [_jsx("span", { className: "sr-only", children: "Outcome filter" }), _jsxs("select", { "aria-label": "Outcome filter", value: outcomeFilter, onChange: (event) => setOutcomeFilter(event.target.value), className: "h-9 w-full rounded-xl border border-border/40 bg-bg-muted/15 px-3 text-sm text-txt", children: [_jsx("option", { value: "all", children: "All outcomes" }), filters.outcomes.map((outcome) => (_jsx("option", { value: outcome, children: outcome }, outcome)))] })] }), _jsxs("label", { className: "min-w-0", children: [_jsx("span", { className: "sr-only", children: "Domain filter" }), _jsxs("select", { "aria-label": "Domain filter", value: domainFilter, onChange: (event) => setDomainFilter(event.target.value), className: "h-9 w-full rounded-xl border border-border/40 bg-bg-muted/15 px-3 text-sm text-txt", children: [_jsx("option", { value: "all", children: "All domains" }), filters.domains.map((domain) => (_jsx("option", { value: domain, children: domain }, domain)))] })] }), _jsxs("label", { className: "min-w-0", children: [_jsx("span", { className: "sr-only", children: "Tag filter" }), _jsxs("select", { "aria-label": "Tag filter", value: tagFilter, onChange: (event) => setTagFilter(event.target.value), className: "h-9 w-full rounded-xl border border-border/40 bg-bg-muted/15 px-3 text-sm text-txt", children: [_jsx("option", { value: "all", children: "All tags" }), filters.tags.map((tag) => (_jsx("option", { value: tag, children: tag }, tag)))] })] }), _jsxs("label", { className: "min-w-0", children: [_jsx("span", { className: "sr-only", children: "Review filter" }), _jsxs("select", { "aria-label": "Review filter", value: reviewFilter, onChange: (event) => setReviewFilter(event.target.value), className: "h-9 w-full rounded-xl border border-border/40 bg-bg-muted/15 px-3 text-sm text-txt", children: [_jsx("option", { value: "all", children: "All review states" }), _jsx("option", { value: "needs-review", children: "Needs review" }), _jsx("option", { value: "corrected", children: "Corrected belief" }), _jsx("option", { value: "superseded", children: "Supersedes prior" })] })] }), _jsxs("label", { className: "min-w-0", children: [_jsx("span", { className: "sr-only", children: "Sort experiences" }), _jsxs("select", { "aria-label": "Sort experiences", value: sortMode, onChange: (event) => setSortMode(event.target.value), className: "h-9 w-full rounded-xl border border-border/40 bg-bg-muted/15 px-3 text-sm text-txt", children: [_jsx("option", { value: "priority", children: "Priority" }), _jsx("option", { value: "newest", children: "Newest" }), _jsx("option", { value: "importance", children: "Importance" }), _jsx("option", { value: "confidence", children: "Lowest confidence" })] })] })] })] }), _jsxs("div", { className: "grid min-w-0 gap-4 xl:grid-cols-[minmax(19rem,25rem)_minmax(0,1fr)]", children: [_jsxs("div", { className: "flex min-h-[28rem] min-w-0 flex-col overflow-hidden rounded-2xl border border-border/40 bg-bg/70", children: [_jsxs("div", { className: "border-b border-border/30 px-4 py-3", children: [_jsx("div", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: "Review queue" }), _jsx("p", { className: "mt-1 text-sm text-muted", children: "Priority favors high importance, low confidence, and corrected beliefs." })] }), _jsx("div", { className: "custom-scrollbar flex min-w-0 flex-1 flex-col overflow-y-auto", children: filteredExperiences.length === 0 ? (_jsx("div", { className: "px-4 py-8 text-sm text-muted", children: "No experiences match the current filters." })) : (filteredExperiences.map((experience) => {
181
+ const isSelected = experience.id === visibleSelectedExperience?.id;
182
+ const reviewReasons = [
183
+ clampScore(experience.importance) >= 0.75
184
+ ? "high importance"
185
+ : null,
186
+ clampScore(experience.confidence) < 0.65
187
+ ? "low confidence"
188
+ : null,
189
+ experience.previousBelief || experience.correctedBelief
190
+ ? "belief changed"
191
+ : null,
192
+ ].filter(Boolean);
193
+ return (_jsxs("button", { type: "button", "data-testid": `experience-row-${experience.id}`, className: `flex min-w-0 flex-col items-start gap-2 border-b border-border/20 px-4 py-4 text-left transition-colors hover:bg-bg-muted/20 ${isSelected ? "bg-bg-muted/25" : ""}`, onClick: () => onSelectExperience(experience.id), children: [_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [_jsx("span", { className: "rounded-full border border-border/40 px-2 py-0.5 text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: experience.type }), _jsx("span", { className: `rounded-full border px-2 py-0.5 text-[0.65rem] font-semibold uppercase tracking-[0.08em] ${outcomeAccent(experience.outcome)}`, children: experience.outcome }), needsReview(experience) ? (_jsx("span", { className: "rounded-full border border-status-warning/30 bg-status-warning-bg px-2 py-0.5 text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-status-warning", children: "Review" })) : null] }), _jsx("h4", { className: "line-clamp-2 text-sm font-semibold text-txt", children: experience.learning ||
194
+ experience.result ||
195
+ experience.context }), _jsxs("div", { className: "grid w-full grid-cols-2 gap-2 text-xs text-muted", children: [_jsxs("span", { children: ["Importance ", formatPercent(experience.importance)] }), _jsxs("span", { children: ["Confidence ", formatPercent(experience.confidence)] })] }), _jsx("p", { className: "line-clamp-2 text-sm text-muted-strong", children: experience.context }), _jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2 text-xs text-muted", children: [_jsx("span", { children: formatTimestamp(experience.createdAt) }), experience.domain ? (_jsx("span", { children: experience.domain })) : null, reviewReasons.length > 0 ? (_jsx("span", { children: reviewReasons.join(", ") })) : null] })] }, experience.id));
196
+ })) })] }), visibleSelectedExperience ? (_jsxs("div", { className: "flex min-w-0 flex-col gap-4 rounded-2xl border border-border/40 bg-bg/70 px-4 py-4", children: [_jsxs("div", { className: "flex min-w-0 flex-wrap items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [_jsx("span", { className: "rounded-full border border-border/40 px-2 py-0.5 text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: visibleSelectedExperience.type }), _jsx("span", { className: `rounded-full border px-2 py-0.5 text-[0.65rem] font-semibold uppercase tracking-[0.08em] ${outcomeAccent(visibleSelectedExperience.outcome)}`, children: visibleSelectedExperience.outcome }), visibleSelectedExperience.domain ? (_jsx("span", { className: "rounded-full border border-border/40 px-2 py-0.5 text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: visibleSelectedExperience.domain })) : null, needsReview(visibleSelectedExperience) ? (_jsx("span", { className: "rounded-full border border-status-warning/30 bg-status-warning-bg px-2 py-0.5 text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-status-warning", children: "Needs review" })) : null] }), _jsx("h4", { className: "mt-2 text-lg font-semibold leading-snug text-txt", children: visibleSelectedExperience.learning ||
197
+ visibleSelectedExperience.result ||
198
+ visibleSelectedExperience.context }), _jsxs("p", { className: "mt-1 text-xs text-muted", children: ["Created ", formatTimestamp(visibleSelectedExperience.createdAt), visibleSelectedExperience.updatedAt
199
+ ? ` · Updated ${formatTimestamp(visibleSelectedExperience.updatedAt)}`
200
+ : ""] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [onDeleteExperience ? (_jsx(Button, { type: "button", variant: "outline", size: "sm", className: "rounded-lg", disabled: deletingExperienceId === visibleSelectedExperience.id, onClick: () => onDeleteExperience(visibleSelectedExperience), children: deletingExperienceId === visibleSelectedExperience.id
201
+ ? "Deleting..."
202
+ : "Delete" })) : null, onSaveExperience ? (_jsx(Button, { type: "button", size: "sm", className: "rounded-lg", disabled: savingExperienceId === visibleSelectedExperience.id, onClick: () => onSaveExperience(visibleSelectedExperience, draft), children: savingExperienceId === visibleSelectedExperience.id
203
+ ? "Saving..."
204
+ : "Save review" })) : null] })] }), _jsxs("div", { className: "grid gap-4 lg:grid-cols-[minmax(0,1.2fr)_minmax(16rem,0.8fr)]", children: [_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "rounded-xl border border-border/30 bg-bg-muted/15 p-3", children: [_jsx("div", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: "Learned takeaway" }), _jsx("p", { className: "mt-2 whitespace-pre-wrap text-sm leading-relaxed text-muted-strong", children: visibleSelectedExperience.learning || "Not recorded." })] }), _jsxs("div", { className: "grid gap-3 md:grid-cols-3", children: [_jsx(EvidencePanel, { title: "Context", body: visibleSelectedExperience.context }), _jsx(EvidencePanel, { title: "Action", body: visibleSelectedExperience.action }), _jsx(EvidencePanel, { title: "Result", body: visibleSelectedExperience.result })] })] }), _jsxs("div", { className: "space-y-4 rounded-xl border border-border/30 bg-bg-muted/10 p-3", children: [_jsx(ScoreBar, { label: "Importance", value: visibleSelectedExperience.importance, explanation: "Higher values rank this learning as more likely to affect future behavior." }), _jsx(ScoreBar, { label: "Confidence", value: visibleSelectedExperience.confidence, explanation: "Lower confidence keeps this item in the review queue until evidence improves." }), _jsxs("div", { children: [_jsx("div", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: "Tags" }), _jsx("div", { className: "mt-2 flex flex-wrap gap-2", children: visibleSelectedExperience.tags.length > 0 ? (visibleSelectedExperience.tags.map((tag) => (_jsx("span", { className: "rounded-full border border-border/40 px-2 py-0.5 text-xs text-muted-strong", children: tag }, tag)))) : (_jsx("span", { className: "text-sm text-muted", children: "No tags recorded." })) })] })] })] }), _jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [_jsx(EvidencePanel, { title: "Previous belief", body: visibleSelectedExperience.previousBelief }), _jsx(EvidencePanel, { title: "Corrected belief", body: visibleSelectedExperience.correctedBelief })] }), visibleSelectedExperience.supersedes ||
205
+ selectedRelatedExperiences.length > 0 ? (_jsxs("div", { className: "rounded-xl border border-border/30 bg-bg-muted/15 p-3", children: [_jsx("div", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: "Related experience trail" }), _jsxs("div", { className: "mt-2 space-y-2 text-sm text-muted-strong", children: [visibleSelectedExperience.supersedes ? (_jsxs("p", { children: ["Supersedes", " ", _jsx("span", { className: "font-mono text-txt", children: visibleSelectedExperience.supersedes }), supersededExperience
206
+ ? `: ${supersededExperience.learning || supersededExperience.result}`
207
+ : ""] })) : null, selectedRelatedExperiences.map((experience) => (_jsxs("button", { type: "button", className: "block w-full rounded-lg border border-border/30 px-3 py-2 text-left hover:bg-bg-muted/20", onClick: () => onSelectExperience(experience.id), children: [_jsx("span", { className: "font-mono text-xs text-muted", children: experience.id }), _jsx("span", { className: "ml-2", children: experience.learning ||
208
+ experience.result ||
209
+ experience.context })] }, experience.id)))] })] })) : null, _jsxs("div", { className: "rounded-xl border border-border/30 bg-bg-muted/10 p-3", children: [_jsx("div", { className: "mb-3 flex items-center justify-between gap-3", children: _jsxs("div", { children: [_jsx("div", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: "Review edit" }), _jsx("p", { className: "text-xs text-muted", children: "Update the takeaway, ranking, and tags after checking evidence." })] }) }), _jsxs("label", { htmlFor: `experience-learning-${visibleSelectedExperience.id}`, className: "flex min-w-0 flex-col gap-2", children: [_jsx("span", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: "Learning" }), _jsx(Textarea, { id: `experience-learning-${visibleSelectedExperience.id}`, value: draft.learning, rows: 6, onChange: (event) => setDraft((current) => ({
210
+ ...current,
211
+ learning: event.target.value,
212
+ })), className: "min-h-[8rem] resize-y rounded-xl border-border/40 bg-bg-muted/15 font-mono text-sm leading-relaxed" })] }), _jsxs("div", { className: "mt-4 grid gap-4 md:grid-cols-3", children: [_jsxs("label", { htmlFor: `experience-importance-${visibleSelectedExperience.id}`, className: "flex min-w-0 flex-col gap-2", children: [_jsx("span", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: "Importance" }), _jsx(Input, { id: `experience-importance-${visibleSelectedExperience.id}`, type: "number", min: "0", max: "1", step: "0.05", value: String(draft.importance), onChange: (event) => setDraft((current) => ({
213
+ ...current,
214
+ importance: Number(event.target.value || 0),
215
+ })), className: "rounded-xl border-border/40 bg-bg-muted/15" })] }), _jsxs("label", { htmlFor: `experience-confidence-${visibleSelectedExperience.id}`, className: "flex min-w-0 flex-col gap-2", children: [_jsx("span", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: "Confidence" }), _jsx(Input, { id: `experience-confidence-${visibleSelectedExperience.id}`, type: "number", min: "0", max: "1", step: "0.05", value: String(draft.confidence), onChange: (event) => setDraft((current) => ({
216
+ ...current,
217
+ confidence: Number(event.target.value || 0),
218
+ })), className: "rounded-xl border-border/40 bg-bg-muted/15" })] }), _jsxs("label", { htmlFor: `experience-tags-${visibleSelectedExperience.id}`, className: "flex min-w-0 flex-col gap-2", children: [_jsx("span", { className: "text-[0.65rem] font-semibold uppercase tracking-[0.08em] text-muted", children: "Tags" }), _jsx(Input, { id: `experience-tags-${visibleSelectedExperience.id}`, type: "text", value: draft.tags, onChange: (event) => setDraft((current) => ({
219
+ ...current,
220
+ tags: event.target.value,
221
+ })), className: "rounded-xl border-border/40 bg-bg-muted/15" })] })] })] })] })) : null] })] }));
71
222
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CharacterHubView.d.ts","sourceRoot":"","sources":["../../../../../../src/components/character/CharacterHubView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAiBzD,OAAO,KAAK,EACV,aAAa,EAKd,MAAM,wBAAwB,CAAC;AAuBhC,KAAK,qBAAqB,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAarD,wBAAgB,gBAAgB,CAAC,EAC/B,CAAC,EACD,OAAO,EACP,yBAAyB,EACzB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,6BAA6B,EAC7B,cAAc,EACd,2BAA2B,EAC3B,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,MAAM,EACN,OAAO,EACP,QAAQ,GACT,EAAE;IACD,CAAC,EAAE,aAAa,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,yBAAyB,EAAE,mBAAmB,EAAE,CAAC;IACjD,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3C,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACzD,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACxD,6BAA6B,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACpE,cAAc,EAAE,CAAC,GAAG,EAAE,qBAAqB,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACpE,2BAA2B,EAAE,CAC3B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,KACV,IAAI,CAAC;IACV,eAAe,EAAE,OAAO,CAAC;IACzB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;CACnB,2CA2wBA"}
1
+ {"version":3,"file":"CharacterHubView.d.ts","sourceRoot":"","sources":["../../../../../../src/components/character/CharacterHubView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAyBzD,OAAO,KAAK,EACV,aAAa,EAKd,MAAM,wBAAwB,CAAC;AA0BhC,KAAK,qBAAqB,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AA0GrD,wBAAgB,gBAAgB,CAAC,EAC/B,CAAC,EACD,OAAO,EACP,yBAAyB,EACzB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,6BAA6B,EAC7B,cAAc,EACd,2BAA2B,EAC3B,eAAe,EACf,oBAAoB,EACpB,kBAAkB,EAClB,iBAAiB,EACjB,MAAM,EACN,OAAO,EACP,QAAQ,GACT,EAAE;IACD,CAAC,EAAE,aAAa,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,yBAAyB,EAAE,mBAAmB,EAAE,CAAC;IACjD,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3C,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACzD,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACxD,6BAA6B,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACpE,cAAc,EAAE,CAAC,GAAG,EAAE,qBAAqB,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACpE,2BAA2B,EAAE,CAC3B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,KACV,IAAI,CAAC;IACV,eAAe,EAAE,OAAO,CAAC;IACzB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;CACnB,2CAk7BA"}