agent-loft 0.2.29 → 0.2.31

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 (69) hide show
  1. package/README.md +5 -0
  2. package/package.json +1 -1
  3. package/src/agents/claude/driver.ts +3 -2
  4. package/src/agents/claude/session.ts +11 -4
  5. package/src/agents/codex/session.ts +9 -3
  6. package/src/channels/feishu/bot.ts +8 -4
  7. package/src/channels/feishu/turn-stream.ts +3 -2
  8. package/src/channels/web/api.ts +4 -0
  9. package/src/channels/web/server.ts +14 -2
  10. package/src/channels/web/tasks-api.ts +94 -0
  11. package/src/core/chat-mode.ts +14 -14
  12. package/src/core/hub.ts +95 -16
  13. package/src/core/memory-mcp.ts +1 -1
  14. package/src/core/personal.ts +3 -1
  15. package/src/core/store.ts +2 -0
  16. package/src/core/task-prompt.ts +38 -0
  17. package/src/core/task-runner.ts +232 -0
  18. package/src/core/task-schedule.ts +250 -0
  19. package/src/core/tasks-bridge.ts +252 -0
  20. package/src/core/tasks-mcp.ts +55 -0
  21. package/src/core/tasks.ts +281 -0
  22. package/src/core/types.ts +2 -0
  23. package/src/main.ts +8 -0
  24. package/src/mcp/tasks-server.ts +206 -0
  25. package/web/app.js +8 -5560
  26. package/web/css/base.css +291 -0
  27. package/web/css/composer.css +506 -0
  28. package/web/css/loader.css +196 -0
  29. package/web/css/messages.css +734 -0
  30. package/web/css/mobile.css +100 -0
  31. package/web/css/overlay.css +437 -0
  32. package/web/css/schedule.css +93 -0
  33. package/web/css/settings.css +335 -0
  34. package/web/css/sidebar.css +593 -0
  35. package/web/css/tasks.css +455 -0
  36. package/web/css/topbar.css +205 -0
  37. package/web/index.html +12 -1
  38. package/web/lib/api.js +26 -0
  39. package/web/lib/dom.js +101 -0
  40. package/web/lib/markdown.js +52 -0
  41. package/web/lib/overlay.js +144 -0
  42. package/web/lib/push.js +70 -0
  43. package/web/lib/time.js +60 -0
  44. package/web/lib/ws.js +236 -0
  45. package/web/settings/agents.js +210 -0
  46. package/web/settings/commands.js +411 -0
  47. package/web/settings/common.js +42 -0
  48. package/web/settings/feishu.js +181 -0
  49. package/web/settings/general.js +184 -0
  50. package/web/settings/index.js +63 -0
  51. package/web/settings/memory.js +366 -0
  52. package/web/settings/notify.js +80 -0
  53. package/web/settings/security.js +188 -0
  54. package/web/settings/users.js +189 -0
  55. package/web/state.js +46 -0
  56. package/web/views/auth.js +61 -0
  57. package/web/views/composer.js +324 -0
  58. package/web/views/conv-actions.js +412 -0
  59. package/web/views/interaction.js +140 -0
  60. package/web/views/layout.js +197 -0
  61. package/web/views/messages.js +531 -0
  62. package/web/views/quota.js +145 -0
  63. package/web/views/schedule.js +120 -0
  64. package/web/views/sidebar.js +458 -0
  65. package/web/views/slash.js +185 -0
  66. package/web/views/tasks.js +460 -0
  67. package/web/views/topbar.js +400 -0
  68. package/web/views/voice.js +189 -0
  69. package/web/style.css +0 -3500
@@ -0,0 +1,100 @@
1
+ /* ---------- 手机 ---------- */
2
+
3
+ @media (max-width: 768px) {
4
+ /* 侧栏和遮罩用 absolute 而不是 fixed:iOS PWA 会拿贴着顶部的 fixed 元素给状态栏上色,
5
+ 遮罩一出来状态栏就变暗,关掉后还不恢复 */
6
+ .sidebar {
7
+ position: absolute;
8
+ inset: 0 auto 0 0;
9
+ z-index: 30;
10
+ transform: translateX(-100%);
11
+ transition: transform 0.2s ease;
12
+ width: min(84vw, 320px);
13
+ box-shadow: var(--shadow);
14
+ }
15
+
16
+ .layout.side-open .sidebar {
17
+ transform: none;
18
+ }
19
+
20
+ .layout.side-open .scrim {
21
+ position: absolute;
22
+ inset: 0;
23
+ background: rgba(0, 0, 0, 0.35);
24
+ z-index: 25;
25
+ }
26
+
27
+ .topbar .menu-btn {
28
+ display: inline-grid;
29
+ }
30
+
31
+ .messages-inner {
32
+ padding: 12px 14px 36px;
33
+ }
34
+
35
+ .msg-user {
36
+ max-width: 90%;
37
+ }
38
+
39
+ .md th,
40
+ .md td {
41
+ white-space: nowrap;
42
+ }
43
+
44
+ .composer-wrap {
45
+ padding: 0 10px calc(8px + env(safe-area-inset-bottom));
46
+ }
47
+
48
+ .composer-hint {
49
+ display: none;
50
+ }
51
+
52
+ .ctx-bar {
53
+ margin: 0 10px -22px;
54
+ font-size: 13px;
55
+ }
56
+
57
+ .hero-logo svg {
58
+ width: 52px;
59
+ height: 52px;
60
+ }
61
+
62
+ .overlay {
63
+ padding: 0;
64
+ align-items: flex-end;
65
+ }
66
+
67
+ /* 重命名这类小弹窗在手机上也居中,不贴底 */
68
+ .overlay.dialog-overlay {
69
+ padding: 16px;
70
+ align-items: center;
71
+ }
72
+
73
+ .modal.tall {
74
+ height: 92vh;
75
+ }
76
+
77
+ .modal,
78
+ .modal.wide {
79
+ max-width: none;
80
+ max-height: 92vh;
81
+ border-radius: 16px 16px 0 0;
82
+ padding-bottom: env(safe-area-inset-bottom);
83
+ }
84
+
85
+ .settings {
86
+ flex-direction: column;
87
+ }
88
+
89
+ .settings-nav {
90
+ width: auto;
91
+ flex-direction: row;
92
+ overflow-x: auto;
93
+ border-right: none;
94
+ border-bottom: 1px solid var(--border);
95
+ }
96
+
97
+ .settings-nav button {
98
+ white-space: nowrap;
99
+ }
100
+ }
@@ -0,0 +1,437 @@
1
+ /* ---------- 弹层 ---------- */
2
+
3
+ .overlay {
4
+ position: fixed;
5
+ inset: 0;
6
+ background: rgba(0, 0, 0, 0.4);
7
+ z-index: 40;
8
+ display: flex;
9
+ align-items: center;
10
+ justify-content: center;
11
+ padding: 16px;
12
+ }
13
+
14
+ /* ---- 小弹窗(重命名) ---- */
15
+ .dialog-overlay {
16
+ background: rgba(0, 0, 0, 0.32);
17
+ animation: fade-in 0.12s ease-out;
18
+ }
19
+
20
+ .dialog-overlay.leaving {
21
+ opacity: 0;
22
+ transition: opacity 0.12s;
23
+ }
24
+
25
+ .dialog {
26
+ width: min(420px, 100%);
27
+ background: var(--bg);
28
+ border-radius: 18px;
29
+ padding: 20px 20px 16px;
30
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2), 0 2px 8px rgba(0, 0, 0, 0.06);
31
+ display: flex;
32
+ flex-direction: column;
33
+ animation: dialog-in 0.16s cubic-bezier(0.2, 0.9, 0.3, 1.2);
34
+ }
35
+
36
+ .dialog h3 {
37
+ margin: 0;
38
+ font-size: 17px;
39
+ font-weight: 600;
40
+ }
41
+
42
+ .dialog-sub {
43
+ display: flex;
44
+ align-items: center;
45
+ gap: 6px;
46
+ margin: 6px 0 16px;
47
+ font-size: 13px;
48
+ color: var(--fg-faint);
49
+ }
50
+
51
+ .dialog-sub .agent-icon {
52
+ display: inline-grid;
53
+ }
54
+
55
+ .dialog-sub .agent-icon svg {
56
+ width: 14px;
57
+ height: 14px;
58
+ }
59
+
60
+ .rename-input {
61
+ width: 100%;
62
+ height: 44px;
63
+ padding: 0 14px;
64
+ border: 1px solid var(--border);
65
+ border-radius: 12px;
66
+ background: var(--bg-input);
67
+ font-size: 15px;
68
+ outline: none;
69
+ transition: border-color 0.12s, box-shadow 0.12s;
70
+ }
71
+
72
+ .rename-input:focus {
73
+ border-color: var(--accent);
74
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent);
75
+ }
76
+
77
+ .dialog-foot {
78
+ display: flex;
79
+ justify-content: flex-end;
80
+ gap: 8px;
81
+ margin-top: 18px;
82
+ }
83
+
84
+ .dialog-foot .btn {
85
+ min-width: 72px;
86
+ border-radius: 999px;
87
+ padding: 8px 18px;
88
+ }
89
+
90
+ .dialog-foot .btn.ghost {
91
+ border-color: transparent;
92
+ background: var(--bg-soft);
93
+ }
94
+
95
+ .dialog-foot .btn.ghost:hover {
96
+ background: var(--bg-hover);
97
+ }
98
+
99
+ .confirm-dialog .dialog-sub {
100
+ margin-bottom: 0;
101
+ min-width: 0;
102
+ }
103
+
104
+ .confirm-dialog .dialog-sub > span:last-child {
105
+ overflow: hidden;
106
+ text-overflow: ellipsis;
107
+ white-space: nowrap;
108
+ }
109
+
110
+ .dialog-msg {
111
+ margin: 12px 0 0;
112
+ font-size: 14px;
113
+ line-height: 1.6;
114
+ color: var(--fg-soft);
115
+ white-space: pre-line;
116
+ }
117
+
118
+ .dialog-foot .btn.danger-fill {
119
+ background: var(--danger);
120
+ border-color: var(--danger);
121
+ color: #fff;
122
+ }
123
+
124
+ .dialog-foot .btn.danger-fill:hover {
125
+ background: color-mix(in srgb, var(--danger) 88%, #000);
126
+ }
127
+
128
+ .dialog-foot .btn.primary {
129
+ background: var(--accent);
130
+ border-color: var(--accent);
131
+ color: var(--accent-fg);
132
+ }
133
+
134
+ @keyframes fade-in {
135
+ from {
136
+ opacity: 0;
137
+ }
138
+ }
139
+
140
+ @keyframes dialog-in {
141
+ from {
142
+ opacity: 0;
143
+ transform: scale(0.96) translateY(6px);
144
+ }
145
+ }
146
+
147
+ .modal {
148
+ background: var(--bg);
149
+ border-radius: 16px;
150
+ width: 100%;
151
+ max-width: 560px;
152
+ max-height: 86vh;
153
+ display: flex;
154
+ flex-direction: column;
155
+ box-shadow: var(--shadow);
156
+ overflow: hidden;
157
+ }
158
+
159
+ .modal.wide {
160
+ max-width: 860px;
161
+ }
162
+
163
+ /* 设置弹窗:固定高度 */
164
+ .modal.tall {
165
+ height: 86vh;
166
+ }
167
+
168
+ .modal-head {
169
+ display: flex;
170
+ align-items: center;
171
+ padding: 14px 16px;
172
+ border-bottom: 1px solid var(--border);
173
+ gap: 8px;
174
+ }
175
+
176
+ .modal-head h3 {
177
+ margin: 0;
178
+ font-size: 17px;
179
+ flex: 1;
180
+ }
181
+
182
+ .modal-body {
183
+ padding: 14px 16px;
184
+ overflow-y: auto;
185
+ display: flex;
186
+ flex-direction: column;
187
+ gap: 12px;
188
+ }
189
+
190
+ .modal-foot {
191
+ padding: 12px 16px;
192
+ border-top: 1px solid var(--border);
193
+ display: flex;
194
+ justify-content: flex-end;
195
+ gap: 8px;
196
+ }
197
+
198
+ .list-item {
199
+ display: flex;
200
+ align-items: center;
201
+ gap: 10px;
202
+ padding: 10px 12px;
203
+ border: 1px solid var(--border);
204
+ border-radius: 10px;
205
+ text-align: left;
206
+ width: 100%;
207
+ }
208
+
209
+ button.list-item:hover {
210
+ background: var(--bg-hover);
211
+ }
212
+
213
+ .list-item .grow {
214
+ flex: 1;
215
+ min-width: 0;
216
+ }
217
+
218
+ .list-item .t {
219
+ overflow: hidden;
220
+ text-overflow: ellipsis;
221
+ white-space: nowrap;
222
+ }
223
+
224
+ .list-item .s {
225
+ font-size: 12px;
226
+ color: var(--fg-faint);
227
+ overflow: hidden;
228
+ text-overflow: ellipsis;
229
+ white-space: nowrap;
230
+ }
231
+
232
+ .list-item.on {
233
+ border-color: var(--accent);
234
+ }
235
+
236
+ /* ----- 目录选择器 ----- */
237
+ .dir-crumbs {
238
+ display: flex;
239
+ flex-wrap: wrap;
240
+ align-items: center;
241
+ gap: 2px;
242
+ font-size: 14px;
243
+ }
244
+
245
+ .dir-crumbs .crumb {
246
+ padding: 2px 6px;
247
+ border-radius: 6px;
248
+ color: var(--accent);
249
+ }
250
+
251
+ .dir-crumbs .crumb:last-child {
252
+ color: inherit;
253
+ font-weight: 600;
254
+ }
255
+
256
+ .dir-crumbs .crumb:hover {
257
+ background: var(--bg-hover);
258
+ }
259
+
260
+ .dir-crumbs .crumb-sep {
261
+ color: var(--fg-faint);
262
+ }
263
+
264
+ .dir-list {
265
+ display: flex;
266
+ flex-direction: column;
267
+ gap: 6px;
268
+ min-height: 120px;
269
+ max-height: 50vh;
270
+ overflow-y: auto;
271
+ }
272
+
273
+ .dir-item {
274
+ padding: 8px 12px;
275
+ }
276
+
277
+ .dir-item .dir-icon {
278
+ width: 20px;
279
+ text-align: center;
280
+ }
281
+
282
+ .dir-item .dir-chev {
283
+ color: var(--fg-faint);
284
+ font-size: 18px;
285
+ }
286
+
287
+ .dir-manual {
288
+ flex: 1;
289
+ min-width: 160px;
290
+ padding: 8px 12px;
291
+ border: 1px solid var(--border);
292
+ border-radius: 10px;
293
+ background: var(--bg-input);
294
+ outline: none;
295
+ }
296
+
297
+ .dir-manual:focus {
298
+ border-color: var(--accent);
299
+ }
300
+
301
+ .modal-foot .grow {
302
+ flex: 1;
303
+ }
304
+
305
+ .menu {
306
+ position: fixed;
307
+ z-index: 50;
308
+ background: var(--bg);
309
+ border: 1px solid var(--border);
310
+ border-radius: 12px;
311
+ box-shadow: var(--shadow);
312
+ padding: 6px;
313
+ min-width: 180px;
314
+ display: flex;
315
+ flex-direction: column;
316
+ }
317
+
318
+ .menu button {
319
+ text-align: left;
320
+ padding: 8px 12px;
321
+ border-radius: 8px;
322
+ font-size: 14px;
323
+ display: flex;
324
+ align-items: center;
325
+ gap: 8px;
326
+ }
327
+
328
+ .menu button:hover {
329
+ background: var(--bg-hover);
330
+ }
331
+
332
+ .menu button.danger {
333
+ color: var(--danger);
334
+ }
335
+
336
+ /* 贴着按钮弹出的选择面板(模型 / agent) */
337
+ .popover {
338
+ min-width: 220px;
339
+ max-width: min(340px, calc(100vw - 16px));
340
+ max-height: min(70vh, 520px);
341
+ overflow-y: auto;
342
+ border-radius: 14px;
343
+ padding: 6px;
344
+ }
345
+
346
+ .popover .pop-head {
347
+ font-size: 12px;
348
+ color: var(--fg-faint);
349
+ padding: 6px 12px 4px;
350
+ }
351
+
352
+ .popover .pop-item {
353
+ width: 100%;
354
+ gap: 10px;
355
+ }
356
+
357
+ .popover .pop-item .grow {
358
+ flex: 1;
359
+ min-width: 0;
360
+ display: flex;
361
+ flex-direction: column;
362
+ gap: 2px;
363
+ }
364
+
365
+ .popover .pop-item .s {
366
+ font-size: 12px;
367
+ color: var(--fg-faint);
368
+ white-space: normal;
369
+ }
370
+
371
+ .popover .pop-item .check {
372
+ color: var(--accent);
373
+ font-weight: 600;
374
+ }
375
+
376
+ .popover .agent-icon {
377
+ display: inline-grid;
378
+ color: var(--fg-soft);
379
+ }
380
+
381
+ .popover .agent-icon svg {
382
+ width: 18px;
383
+ height: 18px;
384
+ }
385
+
386
+ .popover .effort-seg {
387
+ display: flex;
388
+ gap: 2px;
389
+ margin: 2px 6px 6px;
390
+ padding: 3px;
391
+ background: var(--bg-soft);
392
+ border-radius: 999px;
393
+ }
394
+
395
+ .popover .effort-seg button {
396
+ flex: 1;
397
+ justify-content: center;
398
+ padding: 5px 10px;
399
+ border-radius: 999px;
400
+ font-size: 13px;
401
+ color: var(--fg-soft);
402
+ }
403
+
404
+ .popover .effort-seg button.on {
405
+ background: var(--bg);
406
+ color: var(--fg);
407
+ font-weight: 500;
408
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
409
+ }
410
+
411
+ .popover .pop-loading,
412
+ .popover .pop-error {
413
+ padding: 12px;
414
+ font-size: 13px;
415
+ color: var(--fg-soft);
416
+ }
417
+
418
+ .menu .sep {
419
+ height: 1px;
420
+ background: var(--border);
421
+ margin: 4px 0;
422
+ }
423
+
424
+ .toast {
425
+ position: fixed;
426
+ left: 50%;
427
+ bottom: calc(110px + env(safe-area-inset-bottom));
428
+ transform: translateX(-50%);
429
+ background: var(--fg);
430
+ color: var(--bg);
431
+ padding: 8px 16px;
432
+ border-radius: 10px;
433
+ font-size: 14px;
434
+ z-index: 60;
435
+ max-width: 90vw;
436
+ white-space: pre-wrap;
437
+ }
@@ -0,0 +1,93 @@
1
+ /* ---------- 定时发送 ---------- */
2
+
3
+ .sched-bar {
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: 4px;
7
+ margin: 0 14px 6px;
8
+ }
9
+
10
+ .sched-bar[hidden] {
11
+ display: none;
12
+ }
13
+
14
+ .sched-item {
15
+ display: flex;
16
+ align-items: center;
17
+ gap: 8px;
18
+ min-width: 0;
19
+ padding: 6px 6px 6px 10px;
20
+ border: 1px solid var(--border);
21
+ border-radius: 12px;
22
+ background: var(--bg-soft);
23
+ font-size: 13px;
24
+ color: var(--fg-soft);
25
+ }
26
+
27
+ .sched-item svg {
28
+ width: 15px;
29
+ height: 15px;
30
+ flex-shrink: 0;
31
+ color: var(--accent);
32
+ }
33
+
34
+ .sched-when {
35
+ flex-shrink: 0;
36
+ color: var(--fg);
37
+ font-weight: 500;
38
+ }
39
+
40
+ .sched-text {
41
+ flex: 1;
42
+ min-width: 0;
43
+ overflow: hidden;
44
+ text-overflow: ellipsis;
45
+ white-space: nowrap;
46
+ }
47
+
48
+ .sched-act {
49
+ flex-shrink: 0;
50
+ padding: 3px 8px;
51
+ border-radius: 8px;
52
+ font-size: 12px;
53
+ color: var(--fg-soft);
54
+ }
55
+
56
+ .sched-act:hover {
57
+ background: var(--bg-hover);
58
+ color: var(--fg);
59
+ }
60
+
61
+ .sched-input {
62
+ width: 100%;
63
+ box-sizing: border-box;
64
+ resize: vertical;
65
+ padding: 10px 12px;
66
+ border: 1px solid var(--border);
67
+ border-radius: 10px;
68
+ background: var(--bg-input);
69
+ color: var(--fg);
70
+ font: inherit;
71
+ }
72
+
73
+ .sched-hint {
74
+ font-size: 12px;
75
+ line-height: 1.6;
76
+ }
77
+
78
+ .sched-row {
79
+ display: flex;
80
+ align-items: center;
81
+ gap: 10px;
82
+ }
83
+
84
+ .sched-time {
85
+ flex: 1;
86
+ padding: 7px 10px;
87
+ border: 1px solid var(--border);
88
+ border-radius: 10px;
89
+ background: var(--bg-input);
90
+ color: var(--fg);
91
+ font: inherit;
92
+ color-scheme: light dark;
93
+ }