@agent-link/server 0.1.187 → 0.1.189

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 (76) hide show
  1. package/dist/auth-manager.d.ts +36 -0
  2. package/dist/auth-manager.js +96 -0
  3. package/dist/auth-manager.js.map +1 -0
  4. package/dist/http.d.ts +4 -0
  5. package/dist/http.js +85 -0
  6. package/dist/http.js.map +1 -0
  7. package/dist/index.js +5 -84
  8. package/dist/index.js.map +1 -1
  9. package/dist/message-relay.d.ts +17 -0
  10. package/dist/message-relay.js +23 -0
  11. package/dist/message-relay.js.map +1 -0
  12. package/dist/session-manager.d.ts +44 -0
  13. package/dist/session-manager.js +83 -0
  14. package/dist/session-manager.js.map +1 -0
  15. package/dist/ws-agent.js +19 -27
  16. package/dist/ws-agent.js.map +1 -1
  17. package/dist/ws-client.js +31 -37
  18. package/dist/ws-client.js.map +1 -1
  19. package/package.json +3 -3
  20. package/web/dist/assets/index-DIO7Hox0.js +320 -0
  21. package/web/dist/assets/index-DIO7Hox0.js.map +1 -0
  22. package/web/dist/assets/index-Y1FN_mFe.css +1 -0
  23. package/web/{index.html → dist/index.html} +2 -19
  24. package/dist/auth.d.ts +0 -13
  25. package/dist/auth.js +0 -65
  26. package/dist/auth.js.map +0 -1
  27. package/dist/context.d.ts +0 -52
  28. package/dist/context.js +0 -60
  29. package/dist/context.js.map +0 -1
  30. package/web/app.js +0 -2881
  31. package/web/css/ask-question.css +0 -333
  32. package/web/css/base.css +0 -270
  33. package/web/css/btw.css +0 -148
  34. package/web/css/chat.css +0 -176
  35. package/web/css/file-browser.css +0 -499
  36. package/web/css/input.css +0 -671
  37. package/web/css/loop.css +0 -674
  38. package/web/css/markdown.css +0 -169
  39. package/web/css/responsive.css +0 -314
  40. package/web/css/sidebar.css +0 -593
  41. package/web/css/team.css +0 -1277
  42. package/web/css/tools.css +0 -327
  43. package/web/encryption.js +0 -56
  44. package/web/modules/appHelpers.js +0 -100
  45. package/web/modules/askQuestion.js +0 -63
  46. package/web/modules/backgroundRouting.js +0 -269
  47. package/web/modules/connection.js +0 -731
  48. package/web/modules/fileAttachments.js +0 -125
  49. package/web/modules/fileBrowser.js +0 -398
  50. package/web/modules/filePreview.js +0 -213
  51. package/web/modules/i18n.js +0 -101
  52. package/web/modules/loop.js +0 -338
  53. package/web/modules/loopTemplates.js +0 -110
  54. package/web/modules/markdown.js +0 -83
  55. package/web/modules/messageHelpers.js +0 -206
  56. package/web/modules/sidebar.js +0 -402
  57. package/web/modules/streaming.js +0 -116
  58. package/web/modules/team.js +0 -396
  59. package/web/modules/teamTemplates.js +0 -360
  60. package/web/vendor/highlight.min.js +0 -1213
  61. package/web/vendor/marked.min.js +0 -6
  62. package/web/vendor/nacl-fast.min.js +0 -1
  63. package/web/vendor/nacl-util.min.js +0 -1
  64. package/web/vendor/pako.min.js +0 -2
  65. package/web/vendor/vue.global.prod.js +0 -13
  66. /package/web/{favicon.svg → dist/favicon.svg} +0 -0
  67. /package/web/{images → dist/images}/chat-iPad.webp +0 -0
  68. /package/web/{images → dist/images}/chat-iPhone.webp +0 -0
  69. /package/web/{images → dist/images}/loop-iPad.webp +0 -0
  70. /package/web/{images → dist/images}/team-iPad.webp +0 -0
  71. /package/web/{landing.html → dist/landing.html} +0 -0
  72. /package/web/{landing.zh.html → dist/landing.zh.html} +0 -0
  73. /package/web/{locales → dist/locales}/en.json +0 -0
  74. /package/web/{locales → dist/locales}/zh.json +0 -0
  75. /package/web/{vendor → dist/vendor}/github-dark.min.css +0 -0
  76. /package/web/{vendor → dist/vendor}/github.min.css +0 -0
@@ -1,402 +0,0 @@
1
- // ── Sidebar: session management, folder picker, grouped sessions ─────────────
2
- const { computed } = Vue;
3
-
4
- /**
5
- * Creates sidebar functionality bound to reactive state.
6
- * @param {object} deps
7
- * @param {Function} deps.wsSend
8
- * @param {import('vue').Ref} deps.messages
9
- * @param {import('vue').Ref} deps.isProcessing
10
- * @param {import('vue').Ref} deps.sidebarOpen
11
- * @param {import('vue').Ref} deps.historySessions
12
- * @param {import('vue').Ref} deps.currentClaudeSessionId
13
- * @param {import('vue').Ref} deps.needsResume
14
- * @param {import('vue').Ref} deps.loadingSessions
15
- * @param {import('vue').Ref} deps.loadingHistory
16
- * @param {import('vue').Ref} deps.workDir
17
- * @param {import('vue').Ref} deps.visibleLimit
18
- * @param {import('vue').Ref} deps.folderPickerOpen
19
- * @param {import('vue').Ref} deps.folderPickerPath
20
- * @param {import('vue').Ref} deps.folderPickerEntries
21
- * @param {import('vue').Ref} deps.folderPickerLoading
22
- * @param {import('vue').Ref} deps.folderPickerSelected
23
- * @param {object} deps.streaming - streaming controller
24
- * @param {import('vue').Ref} deps.hostname
25
- * @param {import('vue').Ref} deps.workdirHistory
26
- */
27
- export function createSidebar(deps) {
28
- const {
29
- wsSend, messages, isProcessing, sidebarOpen,
30
- historySessions, currentClaudeSessionId, needsResume,
31
- loadingSessions, loadingHistory, workDir, visibleLimit,
32
- folderPickerOpen, folderPickerPath, folderPickerEntries,
33
- folderPickerLoading, folderPickerSelected, streaming,
34
- hostname, workdirHistory, workdirSwitching,
35
- // Multi-session parallel
36
- currentConversationId, conversationCache, processingConversations,
37
- switchConversation,
38
- // i18n
39
- t,
40
- } = deps;
41
-
42
- // Late-binding callback: called when user switches to a normal chat session
43
- let _onSwitchToChat = null;
44
- function setOnSwitchToChat(fn) { _onSwitchToChat = fn; }
45
-
46
- // ── Workdir switching timeout ──
47
- let _workdirSwitchTimer = null;
48
- function setWorkdirSwitching() {
49
- workdirSwitching.value = true;
50
- clearTimeout(_workdirSwitchTimer);
51
- _workdirSwitchTimer = setTimeout(() => { workdirSwitching.value = false; }, 10000);
52
- }
53
-
54
- // ── Session management ──
55
-
56
- let _sessionListTimer = null;
57
-
58
- function requestSessionList() {
59
- // Debounce: coalesce rapid calls (e.g. session_started + turn_completed)
60
- // into a single request. First call fires immediately, subsequent calls
61
- // within 2s are deferred.
62
- if (_sessionListTimer) {
63
- clearTimeout(_sessionListTimer);
64
- _sessionListTimer = setTimeout(() => {
65
- _sessionListTimer = null;
66
- loadingSessions.value = true;
67
- wsSend({ type: 'list_sessions' });
68
- }, 2000);
69
- return;
70
- }
71
- loadingSessions.value = true;
72
- wsSend({ type: 'list_sessions' });
73
- _sessionListTimer = setTimeout(() => { _sessionListTimer = null; }, 2000);
74
- }
75
-
76
- function resumeSession(session) {
77
- if (window.innerWidth <= 768) sidebarOpen.value = false;
78
- if (_onSwitchToChat) _onSwitchToChat();
79
-
80
- // Multi-session: check if we already have a conversation loaded for this claudeSessionId
81
- if (switchConversation && conversationCache) {
82
- // Check cache for existing conversation with this claudeSessionId
83
- for (const [convId, cached] of Object.entries(conversationCache.value)) {
84
- if (cached.claudeSessionId === session.sessionId) {
85
- switchConversation(convId);
86
- return;
87
- }
88
- }
89
- // Check if current foreground already shows this session
90
- if (currentClaudeSessionId.value === session.sessionId) {
91
- return;
92
- }
93
- // Create new conversationId, switch to it, then send resume
94
- const newConvId = crypto.randomUUID();
95
- switchConversation(newConvId);
96
- currentClaudeSessionId.value = session.sessionId;
97
- needsResume.value = true;
98
- loadingHistory.value = true;
99
- wsSend({
100
- type: 'resume_conversation',
101
- conversationId: newConvId,
102
- claudeSessionId: session.sessionId,
103
- });
104
- return;
105
- }
106
-
107
- // Legacy fallback (no multi-session)
108
- if (isProcessing.value) return;
109
- messages.value = [];
110
- visibleLimit.value = 50;
111
- streaming.setMessageIdCounter(0);
112
- streaming.setStreamingMessageId(null);
113
- streaming.reset();
114
-
115
- currentClaudeSessionId.value = session.sessionId;
116
- needsResume.value = true;
117
- loadingHistory.value = true;
118
-
119
- wsSend({
120
- type: 'resume_conversation',
121
- claudeSessionId: session.sessionId,
122
- });
123
- }
124
-
125
- function newConversation() {
126
- if (window.innerWidth <= 768) sidebarOpen.value = false;
127
- if (_onSwitchToChat) _onSwitchToChat();
128
-
129
- // Multi-session: just switch to a new blank conversation
130
- if (switchConversation) {
131
- const newConvId = crypto.randomUUID();
132
- switchConversation(newConvId);
133
- messages.value.push({
134
- id: streaming.nextId(), role: 'system',
135
- content: t('system.newConversation'),
136
- timestamp: new Date(),
137
- });
138
- return;
139
- }
140
-
141
- // Legacy fallback (no multi-session)
142
- if (isProcessing.value) return;
143
- messages.value = [];
144
- visibleLimit.value = 50;
145
- streaming.setMessageIdCounter(0);
146
- streaming.setStreamingMessageId(null);
147
- streaming.reset();
148
- currentClaudeSessionId.value = null;
149
- needsResume.value = false;
150
-
151
- // Tell the agent to clear its lastClaudeSessionId so the next message
152
- // starts a fresh session instead of auto-resuming the previous one.
153
- wsSend({ type: 'new_conversation' });
154
-
155
- messages.value.push({
156
- id: streaming.nextId(), role: 'system',
157
- content: 'New conversation started.',
158
- timestamp: new Date(),
159
- });
160
- }
161
-
162
- function toggleSidebar() {
163
- sidebarOpen.value = !sidebarOpen.value;
164
- }
165
-
166
- // ── Delete session ──
167
-
168
- /** Session pending delete confirmation (null = dialog closed) */
169
- let pendingDeleteSession = null;
170
- const deleteConfirmOpen = deps.deleteConfirmOpen;
171
- const deleteConfirmTitle = deps.deleteConfirmTitle;
172
-
173
- function deleteSession(session) {
174
- if (currentClaudeSessionId.value === session.sessionId) return; // guard: foreground
175
- // Guard: check background conversations that are actively processing
176
- if (conversationCache) {
177
- for (const [, cached] of Object.entries(conversationCache.value)) {
178
- if (cached.claudeSessionId === session.sessionId && cached.isProcessing) return;
179
- }
180
- }
181
- pendingDeleteSession = session;
182
- deleteConfirmTitle.value = session.title || session.sessionId.slice(0, 8);
183
- deleteConfirmOpen.value = true;
184
- }
185
-
186
- function confirmDeleteSession() {
187
- if (!pendingDeleteSession) return;
188
- wsSend({ type: 'delete_session', sessionId: pendingDeleteSession.sessionId });
189
- deleteConfirmOpen.value = false;
190
- pendingDeleteSession = null;
191
- }
192
-
193
- function cancelDeleteSession() {
194
- deleteConfirmOpen.value = false;
195
- pendingDeleteSession = null;
196
- }
197
-
198
- // ── Rename session ──
199
-
200
- const renamingSessionId = deps.renamingSessionId;
201
- const renameText = deps.renameText;
202
-
203
- function startRename(session) {
204
- renamingSessionId.value = session.sessionId;
205
- renameText.value = session.title || '';
206
- }
207
-
208
- function confirmRename() {
209
- const sid = renamingSessionId.value;
210
- const title = renameText.value.trim();
211
- if (!sid || !title) {
212
- cancelRename();
213
- return;
214
- }
215
- wsSend({ type: 'rename_session', sessionId: sid, newTitle: title });
216
- renamingSessionId.value = null;
217
- renameText.value = '';
218
- }
219
-
220
- function cancelRename() {
221
- renamingSessionId.value = null;
222
- renameText.value = '';
223
- }
224
-
225
- // ── Folder picker ──
226
-
227
- function openFolderPicker() {
228
- folderPickerOpen.value = true;
229
- folderPickerSelected.value = '';
230
- folderPickerLoading.value = true;
231
- folderPickerPath.value = workDir.value || '';
232
- folderPickerEntries.value = [];
233
- wsSend({ type: 'list_directory', dirPath: workDir.value || '' });
234
- }
235
-
236
- function loadFolderPickerDir(dirPath) {
237
- folderPickerLoading.value = true;
238
- folderPickerSelected.value = '';
239
- folderPickerEntries.value = [];
240
- wsSend({ type: 'list_directory', dirPath });
241
- }
242
-
243
- function folderPickerNavigateUp() {
244
- if (!folderPickerPath.value) return;
245
- const isWin = folderPickerPath.value.includes('\\');
246
- const parts = folderPickerPath.value.replace(/[/\\]$/, '').split(/[/\\]/);
247
- parts.pop();
248
- if (parts.length === 0) {
249
- folderPickerPath.value = '';
250
- loadFolderPickerDir('');
251
- } else if (isWin && parts.length === 1 && /^[A-Za-z]:$/.test(parts[0])) {
252
- folderPickerPath.value = parts[0] + '\\';
253
- loadFolderPickerDir(parts[0] + '\\');
254
- } else {
255
- const sep = isWin ? '\\' : '/';
256
- const parent = parts.join(sep);
257
- folderPickerPath.value = parent;
258
- loadFolderPickerDir(parent);
259
- }
260
- }
261
-
262
- function folderPickerSelectItem(entry) {
263
- folderPickerSelected.value = entry.name;
264
- }
265
-
266
- function folderPickerEnter(entry) {
267
- const sep = folderPickerPath.value.includes('\\') || /^[A-Z]:/.test(entry.name) ? '\\' : '/';
268
- let newPath;
269
- if (!folderPickerPath.value) {
270
- newPath = entry.name + (entry.name.endsWith('\\') ? '' : '\\');
271
- } else {
272
- newPath = folderPickerPath.value.replace(/[/\\]$/, '') + sep + entry.name;
273
- }
274
- folderPickerPath.value = newPath;
275
- folderPickerSelected.value = '';
276
- loadFolderPickerDir(newPath);
277
- }
278
-
279
- function folderPickerGoToPath() {
280
- const path = folderPickerPath.value.trim();
281
- if (!path) {
282
- loadFolderPickerDir('');
283
- return;
284
- }
285
- folderPickerSelected.value = '';
286
- loadFolderPickerDir(path);
287
- }
288
-
289
- function confirmFolderPicker() {
290
- let path = folderPickerPath.value;
291
- if (!path) return;
292
- if (folderPickerSelected.value) {
293
- const sep = path.includes('\\') ? '\\' : '/';
294
- path = path.replace(/[/\\]$/, '') + sep + folderPickerSelected.value;
295
- }
296
- folderPickerOpen.value = false;
297
- setWorkdirSwitching();
298
- wsSend({ type: 'change_workdir', workDir: path });
299
- }
300
-
301
- // ── Working directory history ──
302
-
303
- const WORKDIR_HISTORY_MAX = 10;
304
-
305
- function getWorkdirHistoryKey() {
306
- return `agentlink-workdir-history-${hostname.value}`;
307
- }
308
-
309
- function loadWorkdirHistory() {
310
- try {
311
- const stored = localStorage.getItem(getWorkdirHistoryKey());
312
- workdirHistory.value = stored ? JSON.parse(stored) : [];
313
- } catch {
314
- workdirHistory.value = [];
315
- }
316
- }
317
-
318
- function saveWorkdirHistory() {
319
- localStorage.setItem(getWorkdirHistoryKey(), JSON.stringify(workdirHistory.value));
320
- }
321
-
322
- function addToWorkdirHistory(path) {
323
- if (!path) return;
324
- const filtered = workdirHistory.value.filter(p => p !== path);
325
- filtered.unshift(path);
326
- workdirHistory.value = filtered.slice(0, WORKDIR_HISTORY_MAX);
327
- saveWorkdirHistory();
328
- }
329
-
330
- function removeFromWorkdirHistory(path) {
331
- workdirHistory.value = workdirHistory.value.filter(p => p !== path);
332
- saveWorkdirHistory();
333
- }
334
-
335
- function switchToWorkdir(path) {
336
- setWorkdirSwitching();
337
- wsSend({ type: 'change_workdir', workDir: path });
338
- }
339
-
340
- const filteredWorkdirHistory = computed(() => {
341
- return workdirHistory.value.filter(p => p !== workDir.value);
342
- });
343
-
344
- // ── isSessionProcessing ──
345
- // Used by sidebar template to show processing indicator on session items
346
- function isSessionProcessing(claudeSessionId) {
347
- if (!conversationCache || !processingConversations) return false;
348
- // Check cached background conversations
349
- for (const [convId, cached] of Object.entries(conversationCache.value)) {
350
- if (cached.claudeSessionId === claudeSessionId && cached.isProcessing) {
351
- return true;
352
- }
353
- }
354
- // Check current foreground conversation
355
- if (currentClaudeSessionId.value === claudeSessionId && isProcessing.value) {
356
- return true;
357
- }
358
- return false;
359
- }
360
-
361
- // ── Grouped sessions ──
362
-
363
- const groupedSessions = computed(() => {
364
- if (!historySessions.value.length) return [];
365
- const now = new Date();
366
- const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
367
- const yesterdayStart = todayStart - 86400000;
368
- const weekStart = todayStart - 6 * 86400000;
369
-
370
- const GROUP_KEYS = {
371
- today: 'session.today',
372
- yesterday: 'session.yesterday',
373
- thisWeek: 'session.thisWeek',
374
- earlier: 'session.earlier',
375
- };
376
-
377
- const groups = {};
378
- for (const s of historySessions.value) {
379
- let key;
380
- if (s.lastModified >= todayStart) key = 'today';
381
- else if (s.lastModified >= yesterdayStart) key = 'yesterday';
382
- else if (s.lastModified >= weekStart) key = 'thisWeek';
383
- else key = 'earlier';
384
- if (!groups[key]) groups[key] = [];
385
- groups[key].push(s);
386
- }
387
- const order = ['today', 'yesterday', 'thisWeek', 'earlier'];
388
- return order.filter(k => groups[k]).map(k => ({ label: t(GROUP_KEYS[k]), sessions: groups[k] }));
389
- });
390
-
391
- return {
392
- requestSessionList, resumeSession, newConversation, toggleSidebar,
393
- setOnSwitchToChat,
394
- deleteSession, confirmDeleteSession, cancelDeleteSession,
395
- startRename, confirmRename, cancelRename,
396
- openFolderPicker, folderPickerNavigateUp, folderPickerSelectItem,
397
- folderPickerEnter, folderPickerGoToPath, confirmFolderPicker,
398
- groupedSessions, isSessionProcessing,
399
- loadWorkdirHistory, addToWorkdirHistory, removeFromWorkdirHistory,
400
- switchToWorkdir, filteredWorkdirHistory,
401
- };
402
- }
@@ -1,116 +0,0 @@
1
- // ── Progressive text streaming / reveal animation ────────────────────────────
2
-
3
- const CHARS_PER_TICK = 20;
4
- const TICK_MS = 50;
5
-
6
- /**
7
- * Creates a streaming text reveal controller.
8
- * @param {object} deps
9
- * @param {import('vue').Ref} deps.messages - messages ref array
10
- * @param {() => void} deps.scrollToBottom - scroll callback
11
- */
12
- export function createStreaming({ messages, scrollToBottom }) {
13
- let pendingText = '';
14
- let revealTimer = null;
15
- let streamingMessageId = null;
16
- let messageIdCounter = 0;
17
-
18
- function getMessageIdCounter() { return messageIdCounter; }
19
- function setMessageIdCounter(v) { messageIdCounter = v; }
20
- function getStreamingMessageId() { return streamingMessageId; }
21
- function setStreamingMessageId(v) { streamingMessageId = v; }
22
- function nextId() { return ++messageIdCounter; }
23
-
24
- function startReveal() {
25
- if (revealTimer !== null) return;
26
- revealTimer = setTimeout(revealTick, TICK_MS);
27
- }
28
-
29
- function revealTick() {
30
- revealTimer = null;
31
- if (!pendingText) return;
32
-
33
- const streamMsg = streamingMessageId !== null
34
- ? messages.value.find(m => m.id === streamingMessageId)
35
- : null;
36
-
37
- const chunk = pendingText.slice(0, CHARS_PER_TICK);
38
- pendingText = pendingText.slice(CHARS_PER_TICK);
39
-
40
- if (!streamMsg) {
41
- const id = ++messageIdCounter;
42
- const newMsg = {
43
- id, role: 'assistant', content: chunk,
44
- isStreaming: true, timestamp: new Date(),
45
- _chunks: [chunk],
46
- };
47
- messages.value.push(newMsg);
48
- streamingMessageId = id;
49
- } else {
50
- streamMsg._chunks.push(chunk);
51
- streamMsg.content = streamMsg._chunks.join('');
52
- }
53
- scrollToBottom();
54
- if (pendingText) revealTimer = setTimeout(revealTick, TICK_MS);
55
- }
56
-
57
- function flushReveal() {
58
- if (revealTimer !== null) { clearTimeout(revealTimer); revealTimer = null; }
59
- if (!pendingText) return;
60
- const streamMsg = streamingMessageId !== null
61
- ? messages.value.find(m => m.id === streamingMessageId) : null;
62
- if (streamMsg) {
63
- if (!streamMsg._chunks) streamMsg._chunks = [streamMsg.content];
64
- streamMsg._chunks.push(pendingText);
65
- streamMsg.content = streamMsg._chunks.join('');
66
- } else {
67
- const id = ++messageIdCounter;
68
- const newMsg = {
69
- id, role: 'assistant', content: pendingText,
70
- isStreaming: true, timestamp: new Date(),
71
- _chunks: [pendingText],
72
- };
73
- messages.value.push(newMsg);
74
- streamingMessageId = id;
75
- }
76
- pendingText = '';
77
- scrollToBottom();
78
- }
79
-
80
- function appendPending(text) {
81
- pendingText += text;
82
- }
83
-
84
- function reset() {
85
- pendingText = '';
86
- if (revealTimer !== null) { clearTimeout(revealTimer); revealTimer = null; }
87
- }
88
-
89
- function cleanup() {
90
- if (revealTimer !== null) { clearTimeout(revealTimer); revealTimer = null; }
91
- }
92
-
93
- function saveState() {
94
- flushReveal(); // flush pending text into the message before saving
95
- return {
96
- pendingText: '',
97
- streamingMessageId,
98
- messageIdCounter,
99
- };
100
- }
101
-
102
- function restoreState(saved) {
103
- flushReveal(); // clear any current pending
104
- pendingText = saved.pendingText || '';
105
- streamingMessageId = saved.streamingMessageId ?? null;
106
- messageIdCounter = saved.messageIdCounter || 0;
107
- if (pendingText) startReveal();
108
- }
109
-
110
- return {
111
- startReveal, flushReveal, appendPending, reset, cleanup,
112
- getMessageIdCounter, setMessageIdCounter,
113
- getStreamingMessageId, setStreamingMessageId,
114
- nextId, saveState, restoreState,
115
- };
116
- }