@agenticmail/enterprise 0.5.23 → 0.5.25

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.
@@ -0,0 +1,47 @@
1
+ import {
2
+ AgentRuntime,
3
+ EmailChannel,
4
+ FollowUpScheduler,
5
+ SessionManager,
6
+ SubAgentManager,
7
+ ToolRegistry,
8
+ callLLM,
9
+ createAgentRuntime,
10
+ createNoopHooks,
11
+ createRuntimeHooks,
12
+ estimateMessageTokens,
13
+ estimateTokens,
14
+ executeTool,
15
+ runAgentLoop,
16
+ toolsToDefinitions
17
+ } from "./chunk-ATHZCB2M.js";
18
+ import "./chunk-TYW5XTOW.js";
19
+ import "./chunk-JLSQOQ5L.js";
20
+ import {
21
+ PROVIDER_REGISTRY,
22
+ listAllProviders,
23
+ resolveApiKeyForProvider,
24
+ resolveProvider
25
+ } from "./chunk-67KZYSLU.js";
26
+ import "./chunk-KFQGP6VL.js";
27
+ export {
28
+ AgentRuntime,
29
+ EmailChannel,
30
+ FollowUpScheduler,
31
+ PROVIDER_REGISTRY,
32
+ SessionManager,
33
+ SubAgentManager,
34
+ ToolRegistry,
35
+ callLLM,
36
+ createAgentRuntime,
37
+ createNoopHooks,
38
+ createRuntimeHooks,
39
+ estimateMessageTokens,
40
+ estimateTokens,
41
+ executeTool,
42
+ listAllProviders,
43
+ resolveApiKeyForProvider,
44
+ resolveProvider,
45
+ runAgentLoop,
46
+ toolsToDefinitions
47
+ };
@@ -0,0 +1,11 @@
1
+ import {
2
+ createServer
3
+ } from "./chunk-JIZXHTCS.js";
4
+ import "./chunk-JLSQOQ5L.js";
5
+ import "./chunk-RO537U6H.js";
6
+ import "./chunk-DRXMYYKN.js";
7
+ import "./chunk-67KZYSLU.js";
8
+ import "./chunk-KFQGP6VL.js";
9
+ export {
10
+ createServer
11
+ };
@@ -0,0 +1,20 @@
1
+ import {
2
+ promptCompanyInfo,
3
+ promptDatabase,
4
+ promptDeployment,
5
+ promptDomain,
6
+ promptRegistration,
7
+ provision,
8
+ runSetupWizard
9
+ } from "./chunk-ZI6WADMQ.js";
10
+ import "./chunk-HEK7L3DT.js";
11
+ import "./chunk-KFQGP6VL.js";
12
+ export {
13
+ promptCompanyInfo,
14
+ promptDatabase,
15
+ promptDeployment,
16
+ promptDomain,
17
+ promptRegistration,
18
+ provision,
19
+ runSetupWizard
20
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/enterprise",
3
- "version": "0.5.23",
3
+ "version": "0.5.25",
4
4
  "description": "AgenticMail Enterprise — cloud-hosted AI agent identity, email, auth & compliance for organizations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -233,7 +233,7 @@ export function DeploymentProgress({ agentId, onComplete }) {
233
233
  export function CreateAgentWizard({ onClose, onCreated, toast }) {
234
234
  const [step, setStep] = useState(0);
235
235
  const steps = ['Role', 'Basics', 'Persona', 'Skills', 'Permissions', 'Deployment', 'Review'];
236
- const [form, setForm] = useState({ name: '', email: '', role: 'assistant', description: '', personality: '', skills: [], preset: null, customTools: { allowed: [], blocked: [] }, deployTarget: 'docker', knowledgeBases: [], provider: 'anthropic', model: 'claude-sonnet-4-20250514', approvalRequired: true, soulId: null, avatar: null, gender: '', dateOfBirth: '', maritalStatus: '', culturalBackground: '', language: 'en-us', autoOnboard: true, maxRiskLevel: 'medium', blockedSideEffects: ['runs-code', 'deletes-data', 'financial', 'controls-device'], approvalForRiskLevels: ['high', 'critical'], approvalForSideEffects: ['sends-email', 'sends-message'], rateLimits: { toolCallsPerMinute: 30, toolCallsPerHour: 500, toolCallsPerDay: 5000, externalActionsPerHour: 50 }, constraints: { maxConcurrentTasks: 5, maxSessionDurationMinutes: 480, sandboxMode: false }, traits: { communication: 'direct', detail: 'detail-oriented', energy: 'calm', humor: 'warm', formality: 'adaptive', empathy: 'moderate', patience: 'patient', creativity: 'creative' } });
236
+ const [form, setForm] = useState({ name: '', email: '', role: 'assistant', description: '', personality: '', skills: [], preset: null, customTools: { allowed: [], blocked: [] }, deployTarget: 'docker', knowledgeBases: [], provider: '', model: '', approvalRequired: true, soulId: null, avatar: null, gender: '', dateOfBirth: '', maritalStatus: '', culturalBackground: '', language: 'en-us', autoOnboard: true, maxRiskLevel: 'medium', blockedSideEffects: ['runs-code', 'deletes-data', 'financial', 'controls-device'], approvalForRiskLevels: ['high', 'critical'], approvalForSideEffects: ['sends-email', 'sends-message'], rateLimits: { toolCallsPerMinute: 30, toolCallsPerHour: 500, toolCallsPerDay: 5000, externalActionsPerHour: 50 }, constraints: { maxConcurrentTasks: 5, maxSessionDurationMinutes: 480, sandboxMode: false }, traits: { communication: 'direct', detail: 'detail-oriented', energy: 'calm', humor: 'warm', formality: 'adaptive', empathy: 'moderate', patience: 'patient', creativity: 'creative' } });
237
237
  const [allSkills, setAllSkills] = useState({});
238
238
  const [providers, setProviders] = useState([]);
239
239
  const [providerModels, setProviderModels] = useState([]);
@@ -254,15 +254,25 @@ export function CreateAgentWizard({ onClose, onCreated, toast }) {
254
254
  apiCall('/providers').then(function(d) {
255
255
  var provList = d.providers || [];
256
256
  setProviders(provList);
257
- var hasConfigured = provList.some(function(p) { return p.configured; });
258
- if (!hasConfigured) { setShowSetupGuide(true); }
257
+ var configuredProviders = provList.filter(function(p) { return p.configured; });
258
+ if (configuredProviders.length === 0) { setShowSetupGuide(true); }
259
+ else {
260
+ // Auto-select first configured provider if none set
261
+ setForm(function(f) {
262
+ if (!f.provider) {
263
+ return Object.assign({}, f, { provider: configuredProviders[0].id });
264
+ }
265
+ return f;
266
+ });
267
+ }
259
268
  setSetupChecked(true);
260
269
  }).catch(function() { setShowSetupGuide(true); setSetupChecked(true); });
261
270
  }, []);
262
271
 
263
272
  // Fetch models when provider changes
264
273
  useEffect(function() {
265
- var p = form.provider || 'anthropic';
274
+ var p = form.provider;
275
+ if (!p) return;
266
276
  apiCall('/providers/' + p + '/models').then(function(d) {
267
277
  var models = d.models || [];
268
278
  setProviderModels(models);
@@ -1,4 +1,4 @@
1
- import { h, useState, useEffect, Fragment, useApp, engineCall, getOrgId } from '../components/utils.js';
1
+ import { h, useState, useEffect, useCallback, Fragment, useApp, engineCall, getOrgId } from '../components/utils.js';
2
2
  import { I } from '../components/icons.js';
3
3
  import { Modal } from '../components/modal.js';
4
4
 
@@ -7,9 +7,18 @@ export function KnowledgeBasePage() {
7
7
  const [kbs, setKbs] = useState([]);
8
8
  const [creating, setCreating] = useState(false);
9
9
  const [form, setForm] = useState({ name: '', description: '' });
10
+ const [selected, setSelected] = useState(null); // full KB detail
11
+ const [docs, setDocs] = useState([]);
12
+ const [chunks, setChunks] = useState([]);
13
+ const [selectedDoc, setSelectedDoc] = useState(null);
14
+ const [editing, setEditing] = useState(false);
15
+ const [editForm, setEditForm] = useState({ name: '', description: '' });
16
+ const [loading, setLoading] = useState(false);
10
17
 
11
- const load = () => engineCall('/knowledge-bases').then(d => setKbs(d.knowledgeBases || [])).catch(() => {});
12
- useEffect(() => { load(); }, []);
18
+ const load = useCallback(() => {
19
+ engineCall('/knowledge-bases').then(d => setKbs(d.knowledgeBases || [])).catch(() => {});
20
+ }, []);
21
+ useEffect(() => { load(); }, [load]);
13
22
 
14
23
  const create = async () => {
15
24
  try {
@@ -19,26 +28,202 @@ export function KnowledgeBasePage() {
19
28
  } catch (e) { toast(e.message, 'error'); }
20
29
  };
21
30
 
31
+ const selectKb = async (kb) => {
32
+ setLoading(true);
33
+ try {
34
+ const detail = await engineCall('/knowledge-bases/' + kb.id);
35
+ const kbData = detail.knowledgeBase || detail;
36
+ setSelected(kbData);
37
+ setDocs(kbData.documents || []);
38
+ setChunks([]);
39
+ setSelectedDoc(null);
40
+ setEditForm({ name: kbData.name || '', description: kbData.description || '' });
41
+ } catch (e) {
42
+ toast('Failed to load knowledge base: ' + e.message, 'error');
43
+ }
44
+ setLoading(false);
45
+ };
46
+
47
+ const loadDocChunks = async (doc) => {
48
+ setSelectedDoc(doc);
49
+ try {
50
+ const res = await engineCall('/knowledge-bases/' + selected.id + '/documents/' + doc.id + '/chunks');
51
+ setChunks(res.chunks || []);
52
+ } catch {
53
+ // chunks endpoint might not exist, try search with empty query
54
+ setChunks([]);
55
+ }
56
+ };
57
+
58
+ const deleteKb = async (id) => {
59
+ try {
60
+ await engineCall('/knowledge-bases/' + id, { method: 'DELETE' });
61
+ toast('Knowledge base deleted', 'success');
62
+ setSelected(null); load();
63
+ } catch (e) { toast(e.message, 'error'); }
64
+ };
65
+
66
+ const deleteDoc = async (docId) => {
67
+ if (!selected) return;
68
+ try {
69
+ await engineCall('/knowledge-bases/' + selected.id + '/documents/' + docId, { method: 'DELETE' });
70
+ toast('Document deleted', 'success');
71
+ setDocs(d => d.filter(x => x.id !== docId));
72
+ if (selectedDoc && selectedDoc.id === docId) { setSelectedDoc(null); setChunks([]); }
73
+ } catch (e) { toast(e.message, 'error'); }
74
+ };
75
+
76
+ const saveEdit = async () => {
77
+ if (!selected) return;
78
+ try {
79
+ await engineCall('/knowledge-bases/' + selected.id, { method: 'PUT', body: JSON.stringify({ name: editForm.name, description: editForm.description }) });
80
+ toast('Knowledge base updated', 'success');
81
+ setSelected(s => ({ ...s, name: editForm.name, description: editForm.description }));
82
+ setEditing(false);
83
+ load();
84
+ } catch (e) { toast(e.message, 'error'); }
85
+ };
86
+
87
+ // ── Detail View ──
88
+ if (selected) {
89
+ return h(Fragment, null,
90
+ h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 } },
91
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 12 } },
92
+ h('button', { className: 'btn btn-secondary btn-sm', onClick: () => setSelected(null) }, '\u2190 Back'),
93
+ editing
94
+ ? h('input', { className: 'input', value: editForm.name, onChange: e => setEditForm(f => ({ ...f, name: e.target.value })), style: { fontSize: 18, fontWeight: 700, padding: '4px 8px' } })
95
+ : h('h1', { style: { fontSize: 20, fontWeight: 700, margin: 0 } }, selected.name)
96
+ ),
97
+ h('div', { style: { display: 'flex', gap: 8 } },
98
+ editing
99
+ ? h(Fragment, null,
100
+ h('button', { className: 'btn btn-secondary btn-sm', onClick: () => setEditing(false) }, 'Cancel'),
101
+ h('button', { className: 'btn btn-primary btn-sm', onClick: saveEdit }, 'Save')
102
+ )
103
+ : h(Fragment, null,
104
+ h('button', { className: 'btn btn-secondary btn-sm', onClick: () => setEditing(true) }, I.journal(), ' Edit'),
105
+ h('button', { className: 'btn btn-danger btn-sm', onClick: () => deleteKb(selected.id) }, I.trash(), ' Delete')
106
+ )
107
+ )
108
+ ),
109
+
110
+ // Description
111
+ h('div', { className: 'card', style: { marginBottom: 16 } },
112
+ h('div', { className: 'card-body' },
113
+ editing
114
+ ? h('textarea', { className: 'input', rows: 3, value: editForm.description, onChange: e => setEditForm(f => ({ ...f, description: e.target.value })), placeholder: 'Knowledge base description...' })
115
+ : h('p', { style: { color: 'var(--text-secondary)', fontSize: 13, margin: 0 } }, selected.description || 'No description'),
116
+ h('div', { style: { display: 'flex', gap: 12, marginTop: 12, fontSize: 12, color: 'var(--text-muted)' } },
117
+ h('span', null, 'ID: ', h('code', null, selected.id)),
118
+ selected.createdAt && h('span', null, 'Created: ', new Date(selected.createdAt).toLocaleDateString()),
119
+ h('span', null, docs.length + ' document(s)'),
120
+ selected.agentIds && h('span', null, (selected.agentIds.length || 0) + ' agent(s)')
121
+ )
122
+ )
123
+ ),
124
+
125
+ // Stats
126
+ selected.stats && h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12, marginBottom: 16 } },
127
+ [
128
+ { label: 'Documents', value: selected.stats.documentCount || docs.length },
129
+ { label: 'Chunks', value: selected.stats.chunkCount || 0 },
130
+ { label: 'Total Tokens', value: selected.stats.totalTokens || 0 },
131
+ { label: 'Queries', value: selected.stats.queryCount || 0 },
132
+ ].map(s => h('div', { key: s.label, className: 'card', style: { textAlign: 'center', padding: 12 } },
133
+ h('div', { style: { fontSize: 22, fontWeight: 700, color: 'var(--brand-color, #6366f1)' } }, typeof s.value === 'number' && s.value > 1000 ? (s.value / 1000).toFixed(1) + 'K' : s.value),
134
+ h('div', { style: { fontSize: 12, color: 'var(--text-muted)', marginTop: 2 } }, s.label)
135
+ ))
136
+ ),
137
+
138
+ // Two-panel layout: docs list + chunk preview
139
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 } },
140
+
141
+ // Documents list
142
+ h('div', { className: 'card' },
143
+ h('div', { className: 'card-header' },
144
+ h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' } },
145
+ h('h3', { style: { margin: 0 } }, 'Documents'),
146
+ h('span', { className: 'badge badge-neutral' }, docs.length)
147
+ )
148
+ ),
149
+ h('div', { className: 'card-body-flush' },
150
+ docs.length === 0
151
+ ? h('div', { style: { padding: 24, textAlign: 'center', color: 'var(--text-muted)' } }, 'No documents in this knowledge base')
152
+ : docs.map(doc =>
153
+ h('div', { key: doc.id, style: {
154
+ padding: '10px 16px', borderBottom: '1px solid var(--border)', cursor: 'pointer',
155
+ background: selectedDoc && selectedDoc.id === doc.id ? 'var(--bg-secondary)' : 'transparent',
156
+ transition: 'background 0.15s'
157
+ }, onClick: () => loadDocChunks(doc) },
158
+ h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' } },
159
+ h('div', null,
160
+ h('div', { style: { fontWeight: 600, fontSize: 13 } }, doc.name || doc.id),
161
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 2 } },
162
+ [doc.sourceType, doc.mimeType, doc.size ? (doc.size > 1024 ? (doc.size / 1024).toFixed(1) + ' KB' : doc.size + ' B') : null, doc.status].filter(Boolean).join(' \u2022 ')
163
+ )
164
+ ),
165
+ h('button', { className: 'btn btn-sm', style: { padding: '2px 6px', fontSize: 11, color: 'var(--danger)' }, onClick: (e) => { e.stopPropagation(); deleteDoc(doc.id); } }, I.trash())
166
+ )
167
+ )
168
+ )
169
+ )
170
+ ),
171
+
172
+ // Chunk preview
173
+ h('div', { className: 'card' },
174
+ h('div', { className: 'card-header' },
175
+ h('h3', { style: { margin: 0 } }, selectedDoc ? 'Chunks: ' + (selectedDoc.name || selectedDoc.id) : 'Select a document')
176
+ ),
177
+ h('div', { className: 'card-body', style: { maxHeight: 500, overflow: 'auto' } },
178
+ !selectedDoc
179
+ ? h('div', { style: { textAlign: 'center', color: 'var(--text-muted)', padding: 24 } }, 'Click a document to preview its chunks')
180
+ : chunks.length === 0
181
+ ? h('div', { style: { textAlign: 'center', color: 'var(--text-muted)', padding: 24 } }, 'No chunks found. The document may not have been processed yet.')
182
+ : chunks.map((chunk, i) =>
183
+ h('div', { key: chunk.id || i, style: { padding: '10px 0', borderBottom: '1px solid var(--border)' } },
184
+ h('div', { style: { display: 'flex', justifyContent: 'space-between', marginBottom: 4 } },
185
+ h('span', { style: { fontSize: 11, fontWeight: 600, color: 'var(--text-muted)' } }, 'Chunk #' + (chunk.position ?? i + 1)),
186
+ chunk.tokenCount && h('span', { style: { fontSize: 11, color: 'var(--text-muted)' } }, chunk.tokenCount + ' tokens')
187
+ ),
188
+ h('div', { style: { fontSize: 13, lineHeight: 1.5, whiteSpace: 'pre-wrap', color: 'var(--text-primary)' } }, chunk.content || '(empty)')
189
+ )
190
+ )
191
+ )
192
+ )
193
+ )
194
+ );
195
+ }
196
+
197
+ // ── List View ──
22
198
  return h(Fragment, null,
23
199
  h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 } },
24
- h('div', null, h('h1', { style: { fontSize: 20, fontWeight: 700 } }, 'Knowledge Bases'), h('p', { style: { color: 'var(--text-muted)', fontSize: 13 } }, 'Document ingestion and RAG retrieval for agents')),
200
+ h('div', null,
201
+ h('h1', { style: { fontSize: 20, fontWeight: 700 } }, 'Knowledge Bases'),
202
+ h('p', { style: { color: 'var(--text-muted)', fontSize: 13 } }, 'Document ingestion and RAG retrieval for agents')
203
+ ),
25
204
  h('button', { className: 'btn btn-primary', onClick: () => setCreating(true) }, I.plus(), ' New Knowledge Base')
26
205
  ),
206
+
27
207
  creating && h(Modal, { title: 'Create Knowledge Base', onClose: () => setCreating(false), footer: h(Fragment, null, h('button', { className: 'btn btn-secondary', onClick: () => setCreating(false) }, 'Cancel'), h('button', { className: 'btn btn-primary', onClick: create, disabled: !form.name }, 'Create')) },
28
208
  h('div', { className: 'form-group' }, h('label', { className: 'form-label' }, 'Name'), h('input', { className: 'input', value: form.name, onChange: e => setForm(f => ({ ...f, name: e.target.value })) })),
29
209
  h('div', { className: 'form-group' }, h('label', { className: 'form-label' }, 'Description'), h('textarea', { className: 'input', value: form.description, onChange: e => setForm(f => ({ ...f, description: e.target.value })) }))
30
210
  ),
31
- kbs.length === 0
211
+
212
+ loading && h('div', { style: { textAlign: 'center', padding: 40 } }, 'Loading...'),
213
+
214
+ !loading && kbs.length === 0
32
215
  ? h('div', { className: 'card' }, h('div', { className: 'card-body' }, h('div', { className: 'empty-state' }, I.knowledge(), h('h3', null, 'No knowledge bases'), h('p', null, 'Create a knowledge base to give agents access to your documents, policies, and data.'))))
33
- : h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))', gap: 16 } }, kbs.map(kb =>
34
- h('div', { key: kb.id, className: 'card' },
216
+ : !loading && h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))', gap: 16 } }, kbs.map(kb =>
217
+ h('div', { key: kb.id, className: 'card', style: { cursor: 'pointer', transition: 'border-color 0.15s' }, onClick: () => selectKb(kb) },
35
218
  h('div', { className: 'card-body' },
36
219
  h('h3', { style: { fontSize: 15, fontWeight: 600, marginBottom: 4 } }, kb.name),
37
- h('p', { style: { fontSize: 12, color: 'var(--text-muted)', marginBottom: 12 } }, kb.description || 'No description'),
38
- h('div', { style: { display: 'flex', gap: 8 } },
39
- h('span', { className: 'badge badge-info' }, (kb.documents?.length || 0) + ' documents'),
40
- h('span', { className: 'badge badge-neutral' }, (kb.agentIds?.length || 0) + ' agents')
41
- )
220
+ h('p', { style: { fontSize: 12, color: 'var(--text-muted)', marginBottom: 12, minHeight: 32 } }, kb.description || 'No description'),
221
+ h('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap' } },
222
+ h('span', { className: 'badge badge-info' }, (kb.stats?.documentCount || kb.documents?.length || 0) + ' docs'),
223
+ h('span', { className: 'badge badge-neutral' }, (kb.stats?.chunkCount || 0) + ' chunks'),
224
+ kb.agentIds && kb.agentIds.length > 0 && h('span', { className: 'badge badge-success' }, kb.agentIds.length + ' agent(s)')
225
+ ),
226
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 8 } }, 'Click to view details \u2192')
42
227
  )
43
228
  )
44
229
  ))
@@ -59,6 +59,49 @@ export function createKnowledgeRoutes(knowledgeBase: KnowledgeBaseEngine) {
59
59
  return c.json({ context });
60
60
  });
61
61
 
62
+ // Update knowledge base name/description
63
+ router.put('/knowledge-bases/:id', async (c) => {
64
+ const id = c.req.param('id');
65
+ const kb = knowledgeBase.getKnowledgeBase(id);
66
+ if (!kb) return c.json({ error: 'Knowledge base not found' }, 404);
67
+ const body = await c.req.json();
68
+ if (body.name !== undefined) (kb as any).name = body.name;
69
+ if (body.description !== undefined) (kb as any).description = body.description;
70
+ (kb as any).updatedAt = new Date().toISOString();
71
+ // Persist to DB if engine supports it
72
+ if ((knowledgeBase as any).db) {
73
+ try {
74
+ await (knowledgeBase as any).db.execute(
75
+ 'UPDATE knowledge_bases SET name = $1, description = $2, updated_at = $3 WHERE id = $4',
76
+ [kb.name, kb.description, (kb as any).updatedAt, id]
77
+ );
78
+ } catch { /* in-memory only fallback */ }
79
+ }
80
+ return c.json({ knowledgeBase: kb });
81
+ });
82
+
83
+ // Get chunks for a specific document
84
+ router.get('/knowledge-bases/:kbId/documents/:docId/chunks', (c) => {
85
+ const kbId = c.req.param('kbId');
86
+ const docId = c.req.param('docId');
87
+ const kb = knowledgeBase.getKnowledgeBase(kbId);
88
+ if (!kb) return c.json({ error: 'Knowledge base not found' }, 404);
89
+ // Get chunks from the internal chunks map
90
+ const allChunks = (knowledgeBase as any).chunks || new Map();
91
+ const docChunks: any[] = [];
92
+ if (allChunks instanceof Map) {
93
+ allChunks.forEach((chunk: any) => {
94
+ if (chunk.documentId === docId) docChunks.push(chunk);
95
+ });
96
+ } else if (typeof allChunks === 'object') {
97
+ for (const [, chunk] of Object.entries(allChunks)) {
98
+ if ((chunk as any).documentId === docId) docChunks.push(chunk);
99
+ }
100
+ }
101
+ docChunks.sort((a: any, b: any) => (a.position || 0) - (b.position || 0));
102
+ return c.json({ chunks: docChunks, total: docChunks.length });
103
+ });
104
+
62
105
  router.delete('/knowledge-bases/:id', (c) => {
63
106
  const ok = knowledgeBase.deleteKnowledgeBase(c.req.param('id'));
64
107
  return ok ? c.json({ success: true }) : c.json({ error: 'Not found' }, 404);