@yeaft/webchat-agent 1.0.296 → 1.0.299
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/local-runtime/server/auth/login.js +2 -0
- package/local-runtime/server/db/connection.js +13 -0
- package/local-runtime/server/db/session-db.js +3 -0
- package/local-runtime/server/db/yeaft-session-db.js +9 -0
- package/local-runtime/server/handlers/agent-output.js +27 -0
- package/local-runtime/server/handlers/client-conversation.js +2 -2
- package/local-runtime/server/handlers/client-work-center.js +4 -3
- package/local-runtime/server/routes/auth-routes.js +8 -1
- package/local-runtime/server/routes/user-routes.js +1 -0
- package/local-runtime/server/session-catalog.js +4 -7
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +110 -95
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +2 -2
- package/local-runtime/web/style.bundle.css +1 -1
- package/local-runtime/web/style.bundle.css.gz +0 -0
- package/package.json +1 -1
- package/yeaft/engine.js +3 -0
- package/yeaft/sessions/session-crud.js +9 -6
- package/yeaft/sessions/session-store.js +1 -0
- package/yeaft/tools/file-edit.js +8 -6
- package/yeaft/tools/file-write.js +2 -2
- package/yeaft/tools/registry.js +28 -10
- package/yeaft/tools/types.js +4 -0
- package/yeaft/web-bridge.js +10 -2
- package/yeaft/work-center/coordinator.js +37 -10
- package/yeaft/work-center/durable-model.js +59 -3
- package/yeaft/work-center/projection.js +2 -0
- package/yeaft/work-center/runner.js +14 -5
- package/yeaft/work-center/service.js +59 -16
- package/yeaft/work-center/store.js +242 -53
|
@@ -12,12 +12,14 @@ import { generateVerificationCode, maskEmail } from './utils.js';
|
|
|
12
12
|
* Helper: complete login and return token + sessionKey + role
|
|
13
13
|
*/
|
|
14
14
|
export function completeLogin(username, sessionKey, role) {
|
|
15
|
+
const user = getUserByUsername(username);
|
|
15
16
|
const token = issueSessionToken(username);
|
|
16
17
|
activeSessions.set(token, { username, sessionKey });
|
|
17
18
|
return {
|
|
18
19
|
success: true,
|
|
19
20
|
token,
|
|
20
21
|
sessionKey: encodeKey(sessionKey),
|
|
22
|
+
userId: user?.id || null,
|
|
21
23
|
role: role === 'admin' ? 'admin' : 'pro',
|
|
22
24
|
needTotpCode: false,
|
|
23
25
|
needTotpSetup: false,
|
|
@@ -45,6 +45,7 @@ db.exec(`
|
|
|
45
45
|
title TEXT,
|
|
46
46
|
created_at INTEGER NOT NULL,
|
|
47
47
|
updated_at INTEGER NOT NULL,
|
|
48
|
+
metadata_updated_at INTEGER,
|
|
48
49
|
is_active INTEGER DEFAULT 1
|
|
49
50
|
);
|
|
50
51
|
|
|
@@ -153,6 +154,7 @@ const migrations = [
|
|
|
153
154
|
`ALTER TABLE users ADD COLUMN role TEXT DEFAULT 'user'`,
|
|
154
155
|
`ALTER TABLE messages ADD COLUMN metadata TEXT`,
|
|
155
156
|
`ALTER TABLE sessions ADD COLUMN is_pinned INTEGER DEFAULT 0`,
|
|
157
|
+
`ALTER TABLE sessions ADD COLUMN metadata_updated_at INTEGER`,
|
|
156
158
|
`ALTER TABLE users ADD COLUMN aad_oid TEXT`,
|
|
157
159
|
// fix-chat-title-sticky: persist the "user manually renamed this session"
|
|
158
160
|
// bit so it survives agent reconnect / server restart / DB rehydration.
|
|
@@ -214,6 +216,7 @@ const yeaftSessionsTable = `
|
|
|
214
216
|
announcement TEXT,
|
|
215
217
|
created_at INTEGER,
|
|
216
218
|
updated_at INTEGER NOT NULL,
|
|
219
|
+
metadata_updated_at INTEGER,
|
|
217
220
|
is_archived INTEGER DEFAULT 0,
|
|
218
221
|
is_pinned INTEGER DEFAULT 0,
|
|
219
222
|
sort_order INTEGER,
|
|
@@ -291,6 +294,7 @@ const yeaftMigrations = [
|
|
|
291
294
|
// between chat and yeaft.
|
|
292
295
|
`ALTER TABLE yeaft_sessions ADD COLUMN is_pinned INTEGER DEFAULT 0`,
|
|
293
296
|
`ALTER TABLE yeaft_sessions ADD COLUMN sort_order INTEGER`,
|
|
297
|
+
`ALTER TABLE yeaft_sessions ADD COLUMN metadata_updated_at INTEGER`,
|
|
294
298
|
];
|
|
295
299
|
for (const migration of yeaftMigrations) {
|
|
296
300
|
try { db.exec(migration); } catch (_) { /* column exists */ }
|
|
@@ -531,6 +535,10 @@ export const stmts = {
|
|
|
531
535
|
WHERE id = ?
|
|
532
536
|
`),
|
|
533
537
|
|
|
538
|
+
touchSessionMetadata: db.prepare(`
|
|
539
|
+
UPDATE sessions SET metadata_updated_at = ? WHERE id = ?
|
|
540
|
+
`),
|
|
541
|
+
|
|
534
542
|
updateSessionActive: db.prepare(`
|
|
535
543
|
UPDATE sessions SET is_active = ?, updated_at = ? WHERE id = ?
|
|
536
544
|
`),
|
|
@@ -893,6 +901,11 @@ export const stmts = {
|
|
|
893
901
|
is_archived = excluded.is_archived
|
|
894
902
|
`),
|
|
895
903
|
|
|
904
|
+
touchYeaftSessionMetadata: db.prepare(`
|
|
905
|
+
UPDATE yeaft_sessions SET metadata_updated_at = ?
|
|
906
|
+
WHERE id = ? AND user_id IS ? AND agent_id = ?
|
|
907
|
+
`),
|
|
908
|
+
|
|
896
909
|
getYeaftSession: db.prepare(`
|
|
897
910
|
SELECT * FROM yeaft_sessions WHERE id = ? ORDER BY updated_at DESC LIMIT 1
|
|
898
911
|
`),
|
|
@@ -51,6 +51,7 @@ function mapRow(row) {
|
|
|
51
51
|
announcement: row.announcement || '',
|
|
52
52
|
createdAt: row.created_at || null,
|
|
53
53
|
updatedAt: row.updated_at,
|
|
54
|
+
metadataUpdatedAt: row.metadata_updated_at || null,
|
|
54
55
|
isArchived: row.is_archived === 1,
|
|
55
56
|
// fix-yeaft-session-list-and-menu: persisted pin state. Decorated
|
|
56
57
|
// onto outgoing snapshots in server/handlers/agent-output.js so the
|
|
@@ -90,6 +91,14 @@ export const yeaftSessionDb = {
|
|
|
90
91
|
now,
|
|
91
92
|
0,
|
|
92
93
|
);
|
|
94
|
+
if (session.metadataUpdatedAt) {
|
|
95
|
+
stmts.touchYeaftSessionMetadata.run(
|
|
96
|
+
session.metadataUpdatedAt,
|
|
97
|
+
session.id,
|
|
98
|
+
userId || null,
|
|
99
|
+
agentId,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
93
102
|
},
|
|
94
103
|
|
|
95
104
|
/**
|
|
@@ -528,10 +528,34 @@ export async function handleAgentOutput(agentId, agent, msg) {
|
|
|
528
528
|
case 'session_output': {
|
|
529
529
|
const data = hydrateInlinePreviewData(msg.data);
|
|
530
530
|
const event = syncYeaftSessionMetadata(agentId, agent, msg.event);
|
|
531
|
+
let catalogChanged = false;
|
|
531
532
|
if (event?.type === 'yeaft_status') {
|
|
532
533
|
agent.yeaftStatus = event;
|
|
533
534
|
await broadcastAgentList();
|
|
534
535
|
}
|
|
536
|
+
if (event?.type === 'session_roster_changed' && agent.ownerId && event.sessionId) {
|
|
537
|
+
try {
|
|
538
|
+
const existing = yeaftSessionDb.getForAgent(agent.ownerId, agentId, event.sessionId);
|
|
539
|
+
if (existing) {
|
|
540
|
+
yeaftSessionDb.upsertFromSnapshot(agent.ownerId, agentId, {
|
|
541
|
+
id: event.sessionId,
|
|
542
|
+
name: event.name != null ? event.name : (existing.name || event.sessionId),
|
|
543
|
+
roster: Array.isArray(event.roster) ? event.roster : (existing.roster || []),
|
|
544
|
+
defaultVpId: event.defaultVpId != null ? event.defaultVpId : (existing.defaultVpId || null),
|
|
545
|
+
workDir: existing.workDir || '',
|
|
546
|
+
config: existing.config || {},
|
|
547
|
+
announcement: typeof event.announcement === 'string'
|
|
548
|
+
? event.announcement
|
|
549
|
+
: (existing.announcement || ''),
|
|
550
|
+
createdAt: existing.createdAt || Date.now(),
|
|
551
|
+
metadataUpdatedAt: event.metadataUpdatedAt || existing.metadataUpdatedAt || existing.createdAt || null,
|
|
552
|
+
});
|
|
553
|
+
catalogChanged = true;
|
|
554
|
+
}
|
|
555
|
+
} catch (e) {
|
|
556
|
+
console.warn(`[Server] yeaft roster persist failed for agent ${agentId}:`, e?.message || e);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
535
559
|
if (msg.perfTraceId) {
|
|
536
560
|
recordPerfTraceEvent({
|
|
537
561
|
traceId: msg.perfTraceId,
|
|
@@ -614,6 +638,7 @@ export async function handleAgentOutput(agentId, agent, msg) {
|
|
|
614
638
|
}
|
|
615
639
|
}
|
|
616
640
|
}
|
|
641
|
+
if (catalogChanged) await broadcastSessionCatalog(agent.ownerId);
|
|
617
642
|
break;
|
|
618
643
|
}
|
|
619
644
|
|
|
@@ -859,8 +884,10 @@ export async function handleAgentOutput(agentId, agent, msg) {
|
|
|
859
884
|
? msg.announcement
|
|
860
885
|
: (existing?.announcement || ''),
|
|
861
886
|
createdAt: existing?.createdAt || Date.now(),
|
|
887
|
+
metadataUpdatedAt: msg.metadataUpdatedAt || existing?.metadataUpdatedAt || existing?.createdAt || null,
|
|
862
888
|
};
|
|
863
889
|
yeaftSessionDb.upsertFromSnapshot(agent.ownerId, agentId, merged);
|
|
890
|
+
await broadcastSessionCatalog(agent.ownerId);
|
|
864
891
|
}
|
|
865
892
|
}
|
|
866
893
|
} catch (e) {
|
|
@@ -1017,12 +1017,12 @@ export async function handleClientConversation(clientId, client, msg, checkAgent
|
|
|
1017
1017
|
const titleAgent = agents.get(settingsAgentId);
|
|
1018
1018
|
const titleConvInfo = titleAgent?.conversations.get(settingsConvId);
|
|
1019
1019
|
if (msg.title) {
|
|
1020
|
-
sessionDb.update(settingsConvId, { title: msg.title, isCustomTitle: 1 });
|
|
1020
|
+
sessionDb.update(settingsConvId, { title: msg.title, isCustomTitle: 1, metadataChanged: true });
|
|
1021
1021
|
if (titleConvInfo) { titleConvInfo.title = msg.title; titleConvInfo.customTitle = true; }
|
|
1022
1022
|
} else {
|
|
1023
1023
|
// Clearing the custom title returns the session to auto-naming
|
|
1024
1024
|
// mode — the next user prompt repopulates the title.
|
|
1025
|
-
sessionDb.update(settingsConvId, { isCustomTitle: 0 });
|
|
1025
|
+
sessionDb.update(settingsConvId, { isCustomTitle: 0, metadataChanged: true });
|
|
1026
1026
|
if (titleConvInfo) { titleConvInfo.customTitle = false; }
|
|
1027
1027
|
}
|
|
1028
1028
|
}
|
|
@@ -227,9 +227,10 @@ export async function deliverWorkCenterResponse(agentId, msg) {
|
|
|
227
227
|
const pending = typeof msg?.requestId === 'string' ? pendingRequests.get(msg.requestId) : null;
|
|
228
228
|
if (!pending || pending.agentId !== agentId) return false;
|
|
229
229
|
pendingRequests.delete(msg.requestId);
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
230
|
+
// Keep staged Work Center bytes until the existing upload cleanup expires them.
|
|
231
|
+
// The Agent may have committed the durable clientMessageId while this response
|
|
232
|
+
// is lost before the browser receives it; a same-envelope retry must still be
|
|
233
|
+
// able to resolve the original fileId and reach the Agent receipt preflight.
|
|
233
234
|
const { agentId: _untrustedAgentId, requestId: _opaqueRequestId, _requestUserId, ...payload } = msg;
|
|
234
235
|
let response = payload;
|
|
235
236
|
if (msg.ok === true && msg.op === 'preview_attachment') {
|
|
@@ -239,7 +239,13 @@ export function registerAuthRoutes(app, { requireAuth, checkRateLimit }) {
|
|
|
239
239
|
if (!r) return res.json({ status: 'pending' });
|
|
240
240
|
if (r.kind === 'login') {
|
|
241
241
|
setSessionCookie(req, res, r.token);
|
|
242
|
-
return res.json({
|
|
242
|
+
return res.json({
|
|
243
|
+
status: 'login',
|
|
244
|
+
token: r.token,
|
|
245
|
+
sessionKey: r.sessionKey,
|
|
246
|
+
userId: r.userId,
|
|
247
|
+
role: r.role,
|
|
248
|
+
});
|
|
243
249
|
}
|
|
244
250
|
if (r.kind === 'bind') {
|
|
245
251
|
return res.json({ status: 'bind', provider: r.provider });
|
|
@@ -278,6 +284,7 @@ export function registerAuthRoutes(app, { requireAuth, checkRateLimit }) {
|
|
|
278
284
|
const params = new URLSearchParams({
|
|
279
285
|
token: result.token,
|
|
280
286
|
sessionKey: result.sessionKey,
|
|
287
|
+
userId: result.userId || '',
|
|
281
288
|
role: result.role
|
|
282
289
|
});
|
|
283
290
|
return res.redirect(`/#/sso-complete?${params.toString()}`);
|
|
@@ -56,7 +56,7 @@ export function projectSessionCatalog({
|
|
|
56
56
|
pinned: meta.pinned ?? session.is_pinned === 1,
|
|
57
57
|
sortRank: meta.sortRank ?? null,
|
|
58
58
|
createdAt: session.created_at || null,
|
|
59
|
-
|
|
59
|
+
metadataUpdatedAt: session.metadata_updated_at ?? session.created_at ?? null,
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -75,17 +75,14 @@ export function projectSessionCatalog({
|
|
|
75
75
|
pinned: meta.pinned ?? !!session.pinned,
|
|
76
76
|
sortRank: meta.sortRank ?? session.sortOrder ?? null,
|
|
77
77
|
createdAt: session.createdAt || null,
|
|
78
|
-
|
|
78
|
+
metadataUpdatedAt: session.metadataUpdatedAt ?? session.createdAt ?? null,
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
return rows.sort((left, right) => {
|
|
83
83
|
if (left.pinned !== right.pinned) return left.pinned ? -1 : 1;
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
if (leftRank !== rightRank) return leftRank - rightRank;
|
|
87
|
-
const creationDelta = timestampValue(right.createdAt) - timestampValue(left.createdAt);
|
|
88
|
-
if (creationDelta !== 0) return creationDelta;
|
|
84
|
+
const metadataDelta = timestampValue(right.metadataUpdatedAt) - timestampValue(left.metadataUpdatedAt);
|
|
85
|
+
if (metadataDelta !== 0) return metadataDelta;
|
|
89
86
|
return left.catalogKey.localeCompare(right.catalogKey);
|
|
90
87
|
});
|
|
91
88
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.0.
|
|
1
|
+
{"version":"1.0.299"}
|