@yeaft/webchat-agent 0.1.727 → 0.1.730
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/connection/message-router.js +1 -8
- package/package.json +1 -1
- package/unify/web-bridge.js +0 -5
- package/unify/feature-message.js +0 -144
|
@@ -36,7 +36,7 @@ import { sendToServer, flushMessageBuffer } from './buffer.js';
|
|
|
36
36
|
import { handleRestartAgent, handleUpgradeAgent } from './upgrade.js';
|
|
37
37
|
import { loadMcpServers, updateMcpConfig } from '../mcp.js';
|
|
38
38
|
import { getLlmConfig, updateLlmConfig, getUnifySettings, updateUnifySettings, getSearchSettings, updateSearchSettings, fetchTavilyUsage } from '../unify/config-api.js';
|
|
39
|
-
import { handleUnifyGroupChat, handleUnifyModeSwitch, handleUnifyModelSwitch, resetUnifySession, handleUnifyLoadHistory, handleUnifyAbortThread, handleUnifyAbortAll, handleUnifyAbortTurn, handleUnifyVpSubscribe, handleUnifyVpCreate, handleUnifyVpUpdate, handleUnifyVpDelete, handleUnifyVpRead,
|
|
39
|
+
import { handleUnifyGroupChat, handleUnifyModeSwitch, handleUnifyModelSwitch, resetUnifySession, handleUnifyLoadHistory, handleUnifyAbortThread, handleUnifyAbortAll, handleUnifyAbortTurn, handleUnifyVpSubscribe, handleUnifyVpCreate, handleUnifyVpUpdate, handleUnifyVpDelete, handleUnifyVpRead, handleUnifyFetchSummaryHistory, handleUnifyFeatureCrud, handleUnifyListGroups, handleUnifyCreateGroup, handleUnifyRenameGroup, handleUnifyUpdateGroup, handleUnifyArchiveGroup, handleUnifyDeleteGroup, handleUnifyAddMember, handleUnifyRemoveMember, handleUnifySetDefaultVp, handleUnifyDreamTrigger, broadcastLanguageChange } from '../unify/web-bridge.js';
|
|
40
40
|
|
|
41
41
|
export async function handleMessage(msg) {
|
|
42
42
|
switch (msg.type) {
|
|
@@ -456,13 +456,6 @@ export async function handleMessage(msg) {
|
|
|
456
456
|
handleUnifyVpRead(msg);
|
|
457
457
|
break;
|
|
458
458
|
|
|
459
|
-
// task-334h (R6 §Δ28 / §Δ31.6): feature-scoped direct message echo.
|
|
460
|
-
// Replaces the withdrawn R3 `unify_task_private_chat`. Agent validates,
|
|
461
|
-
// stamps msgId + ts, and broadcasts the `feature_message` mirror back.
|
|
462
|
-
case 'unify_feature_message':
|
|
463
|
-
handleUnifyFeatureMessage(msg);
|
|
464
|
-
break;
|
|
465
|
-
|
|
466
459
|
// R6 G1a — feature summary history + feature affiliation CRUD.
|
|
467
460
|
case 'unify_fetch_summary_history':
|
|
468
461
|
await handleUnifyFetchSummaryHistory(msg);
|
package/package.json
CHANGED
package/unify/web-bridge.js
CHANGED
|
@@ -29,7 +29,6 @@ import { handleVpSubscribe } from './vp/vp-bridge.js';
|
|
|
29
29
|
import { createVp, updateVp, deleteVp, readVp, VpCrudError } from './vp/vp-crud.js';
|
|
30
30
|
import { scanVpLibrary } from './vp/vp-store.js';
|
|
31
31
|
import { createRouter } from './routing/router.js';
|
|
32
|
-
import { handleUnifyFeatureMessage as _handleUnifyFeatureMessage } from './feature-message.js';
|
|
33
32
|
import {
|
|
34
33
|
GroupCrudError,
|
|
35
34
|
createGroupFromSpec,
|
|
@@ -637,10 +636,6 @@ export function handleUnifyVpDelete(msg) {
|
|
|
637
636
|
}
|
|
638
637
|
}
|
|
639
638
|
|
|
640
|
-
export function handleUnifyFeatureMessage(msg) {
|
|
641
|
-
_handleUnifyFeatureMessage(msg, sendUnifyEvent);
|
|
642
|
-
}
|
|
643
|
-
|
|
644
639
|
export function handleUnifyVpRead(msg) {
|
|
645
640
|
const requestId = msg && msg.requestId;
|
|
646
641
|
const vpId = msg && msg.vpId;
|
package/unify/feature-message.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* feature-message.js — R6 §Δ28 / §Δ31.6 feature-scoped direct messaging.
|
|
3
|
-
*
|
|
4
|
-
* Replaces the withdrawn R3 `unify_feature_private_chat` event with a simple
|
|
5
|
-
* echo-able `feature_message` pair:
|
|
6
|
-
*
|
|
7
|
-
* inbound (web → agent): `unify_feature_message`
|
|
8
|
-
* { type, groupId, featureId, vpId, text, mentions?, replyTo?, requestId? }
|
|
9
|
-
* outbound (agent → web): `feature_message`
|
|
10
|
-
* { type, groupId, featureId, vpId, msgId, text, mentions, replyTo,
|
|
11
|
-
* ts, requestId? }
|
|
12
|
-
*
|
|
13
|
-
* This module owns only the *wire adapter* — the payload is validated,
|
|
14
|
-
* stamped with msgId + ts, and broadcast back so the sender's UI and any
|
|
15
|
-
* other connected views converge on the same record. Persistence + feature
|
|
16
|
-
* ACL enforcement are deliberately deferred to task-334l (per PM dispatch:
|
|
17
|
-
* "user_memory_* 实际 ingestion 归 334l"); the parallel feature-private
|
|
18
|
-
* storage hook follows the same phasing.
|
|
19
|
-
*
|
|
20
|
-
* Invariants:
|
|
21
|
-
* • Never throws on the WS hot path — bad payloads reply with a
|
|
22
|
-
* `feature_message_rejected` event carrying a stable `code` string for
|
|
23
|
-
* UI i18n (mirrors the vp_crud_result contract from 334-ui-g).
|
|
24
|
-
* • The outbound event field order and keys are considered wire-frozen
|
|
25
|
-
* per R6 §Δ31.6 table; additive fields only in future slices.
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
import { nextMsgId, isValidVpId } from './groups/ids.js';
|
|
29
|
-
|
|
30
|
-
/** Known `reject` codes — kept stable so 334-ui-* can key i18n on them. */
|
|
31
|
-
export const FEATURE_MESSAGE_REJECT_CODES = Object.freeze({
|
|
32
|
-
MISSING_GROUP_ID: 'missing_group_id',
|
|
33
|
-
MISSING_FEATURE_ID: 'missing_feature_id',
|
|
34
|
-
MISSING_VP_ID: 'missing_vp_id',
|
|
35
|
-
INVALID_VP_ID: 'invalid_vp_id',
|
|
36
|
-
EMPTY_TEXT: 'empty_text',
|
|
37
|
-
TEXT_TOO_LONG: 'text_too_long',
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
/** Soft body cap — matches the shard-entry cap used elsewhere in R6 (§Δ26.3). */
|
|
41
|
-
export const MAX_TEXT_LENGTH = 16_384;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Pure validator. Returns `{ ok: true, payload }` or `{ ok: false, code }`.
|
|
45
|
-
* No IO, no clock reads — safe to unit-test in isolation.
|
|
46
|
-
*
|
|
47
|
-
* @param {any} msg — raw WS message from the web client
|
|
48
|
-
*/
|
|
49
|
-
export function validateFeatureMessage(msg) {
|
|
50
|
-
if (!msg || typeof msg !== 'object') {
|
|
51
|
-
return { ok: false, code: FEATURE_MESSAGE_REJECT_CODES.MISSING_GROUP_ID };
|
|
52
|
-
}
|
|
53
|
-
const { groupId, featureId, vpId, text } = msg;
|
|
54
|
-
if (!groupId || typeof groupId !== 'string') {
|
|
55
|
-
return { ok: false, code: FEATURE_MESSAGE_REJECT_CODES.MISSING_GROUP_ID };
|
|
56
|
-
}
|
|
57
|
-
if (!featureId || typeof featureId !== 'string') {
|
|
58
|
-
return { ok: false, code: FEATURE_MESSAGE_REJECT_CODES.MISSING_FEATURE_ID };
|
|
59
|
-
}
|
|
60
|
-
if (!vpId || typeof vpId !== 'string') {
|
|
61
|
-
return { ok: false, code: FEATURE_MESSAGE_REJECT_CODES.MISSING_VP_ID };
|
|
62
|
-
}
|
|
63
|
-
// Allow the reserved `user` sentinel as a speaker here — features can have
|
|
64
|
-
// human-user messages alongside VP messages. Any other vpId must pass
|
|
65
|
-
// the full shape check (rejects `all`, `system`, pure digits, etc.).
|
|
66
|
-
if (vpId !== 'user' && !isValidVpId(vpId)) {
|
|
67
|
-
return { ok: false, code: FEATURE_MESSAGE_REJECT_CODES.INVALID_VP_ID };
|
|
68
|
-
}
|
|
69
|
-
if (typeof text !== 'string' || text.length === 0) {
|
|
70
|
-
return { ok: false, code: FEATURE_MESSAGE_REJECT_CODES.EMPTY_TEXT };
|
|
71
|
-
}
|
|
72
|
-
if (text.length > MAX_TEXT_LENGTH) {
|
|
73
|
-
return { ok: false, code: FEATURE_MESSAGE_REJECT_CODES.TEXT_TOO_LONG };
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const mentions = Array.isArray(msg.mentions)
|
|
77
|
-
? msg.mentions.filter(m => typeof m === 'string' && m.length > 0).slice(0, 32)
|
|
78
|
-
: [];
|
|
79
|
-
const replyTo = typeof msg.replyTo === 'string' && msg.replyTo.length > 0
|
|
80
|
-
? msg.replyTo
|
|
81
|
-
: null;
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
ok: true,
|
|
85
|
-
payload: { groupId, featureId, vpId, text, mentions, replyTo },
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Build the outbound `feature_message` event from a validated payload.
|
|
91
|
-
* Exposed separately so tests can snapshot the wire shape without
|
|
92
|
-
* needing a live send fn.
|
|
93
|
-
*
|
|
94
|
-
* @param {{groupId:string,featureId:string,vpId:string,text:string,mentions:string[],replyTo:?string}} payload
|
|
95
|
-
* @param {{now?:()=>number, msgId?:()=>string, requestId?:string}} [opts]
|
|
96
|
-
*/
|
|
97
|
-
export function buildFeatureMessageEvent(payload, opts = {}) {
|
|
98
|
-
const now = typeof opts.now === 'function' ? opts.now : Date.now;
|
|
99
|
-
const mkId = typeof opts.msgId === 'function' ? opts.msgId : nextMsgId;
|
|
100
|
-
const evt = {
|
|
101
|
-
type: 'feature_message',
|
|
102
|
-
groupId: payload.groupId,
|
|
103
|
-
featureId: payload.featureId,
|
|
104
|
-
vpId: payload.vpId,
|
|
105
|
-
msgId: mkId(),
|
|
106
|
-
text: payload.text,
|
|
107
|
-
mentions: payload.mentions,
|
|
108
|
-
replyTo: payload.replyTo,
|
|
109
|
-
ts: now(),
|
|
110
|
-
};
|
|
111
|
-
if (opts.requestId) evt.requestId = opts.requestId;
|
|
112
|
-
return evt;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Build the outbound `feature_message_rejected` event.
|
|
117
|
-
* @param {string} code — one of FEATURE_MESSAGE_REJECT_CODES
|
|
118
|
-
* @param {any} msg — original inbound msg (for requestId echo)
|
|
119
|
-
*/
|
|
120
|
-
export function buildFeatureMessageRejected(code, msg) {
|
|
121
|
-
const evt = { type: 'feature_message_rejected', code };
|
|
122
|
-
if (msg && typeof msg.requestId === 'string') evt.requestId = msg.requestId;
|
|
123
|
-
if (msg && typeof msg.groupId === 'string') evt.groupId = msg.groupId;
|
|
124
|
-
if (msg && typeof msg.featureId === 'string') evt.featureId = msg.featureId;
|
|
125
|
-
return evt;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* WS handler entry point. Validates, echoes, never throws.
|
|
130
|
-
*
|
|
131
|
-
* @param {any} msg
|
|
132
|
-
* @param {(event:object)=>void} sendUnifyEvent
|
|
133
|
-
* @param {{now?:()=>number, msgId?:()=>string}} [opts] — test seams
|
|
134
|
-
*/
|
|
135
|
-
export function handleUnifyFeatureMessage(msg, sendUnifyEvent, opts = {}) {
|
|
136
|
-
const result = validateFeatureMessage(msg);
|
|
137
|
-
if (!result.ok) {
|
|
138
|
-
try { sendUnifyEvent(buildFeatureMessageRejected(result.code, msg)); } catch { /* best-effort */ }
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
const requestId = msg && typeof msg.requestId === 'string' ? msg.requestId : undefined;
|
|
142
|
-
const evt = buildFeatureMessageEvent(result.payload, { ...opts, requestId });
|
|
143
|
-
try { sendUnifyEvent(evt); } catch { /* never crash WS pipeline */ }
|
|
144
|
-
}
|