@yeaft/webchat-agent 0.1.530 → 0.1.532

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,49 @@
1
+ /**
2
+ * open-source-message.js — task-334f R6 §Δ24.4.
3
+ *
4
+ * Low-level random access: given a (groupId, msgId), fetch the raw message
5
+ * from the group's jsonl log. Used when a VP has an exact pointer but does
6
+ * not want to run the memory_trace wrapper (5% case: audit / debug).
7
+ */
8
+
9
+ import { defineTool } from './types.js';
10
+
11
+ export default defineTool({
12
+ name: 'open_source_message',
13
+ description: `Open a single source message by (groupId, msgId).
14
+
15
+ This is the low-level random-access primitive. Prefer memory_trace if you are
16
+ starting from a memory entry. Returns JSON: { message } or { error }.`,
17
+ parameters: {
18
+ type: 'object',
19
+ properties: {
20
+ groupId: { type: 'string', description: 'Group id' },
21
+ msgId: { type: 'string', description: 'Message id' },
22
+ },
23
+ required: ['groupId', 'msgId'],
24
+ },
25
+ isConcurrencySafe: () => true,
26
+ isReadOnly: () => true,
27
+ async execute(input, ctx) {
28
+ const { groupId, msgId } = input || {};
29
+ if (!groupId || !msgId) {
30
+ return JSON.stringify({ error: 'groupId and msgId required' });
31
+ }
32
+ const coordinator = ctx?.coordinator;
33
+ if (!coordinator || typeof coordinator.openGroup !== 'function') {
34
+ return JSON.stringify({ error: 'group coordinator not available' });
35
+ }
36
+ const group = coordinator.openGroup(groupId);
37
+ if (!group) return JSON.stringify({ error: `group not found: ${groupId}` });
38
+
39
+ const iter = typeof group.readMessageRange === 'function'
40
+ ? group.readMessageRange(msgId, msgId)
41
+ : group.streamMessages();
42
+ for (const msg of iter) {
43
+ if (msg.id === msgId) {
44
+ return JSON.stringify({ message: msg });
45
+ }
46
+ }
47
+ return JSON.stringify({ error: `message not found: ${msgId} in ${groupId}` });
48
+ },
49
+ });
@@ -0,0 +1,104 @@
1
+ /**
2
+ * user-memory.js — R6 §Δ29 user-memory WS event skeleton.
3
+ *
4
+ * PLACEHOLDER ONLY. Actual ingestion / shard write / cross-task recall
5
+ * is owned by task-334l. This file reserves three event names on the
6
+ * wire + acknowledges the request so the web client can ship its
7
+ * emitter code without a dependency-cycle on 334l's storage layer.
8
+ *
9
+ * Wire shapes (frozen by R6 §Δ31.6 table; additive fields only):
10
+ *
11
+ * inbound (web → agent): `unify_user_memory_write`
12
+ * { type, text, tags?, sourceRef?, requestId? }
13
+ *
14
+ * outbound (agent → web): `user_memory_updated`
15
+ * { type, entryId?, reason: 'accepted'|'deferred'|'noop',
16
+ * requestId?, pending?: boolean }
17
+ *
18
+ * outbound (agent → web): `user_memory_removed`
19
+ * { type, entryId, requestId? }
20
+ *
21
+ * Current behaviour: every write is replied with `user_memory_updated`
22
+ * carrying `reason: 'deferred'` and `pending: true` — the frontend
23
+ * treats this as "queued but not yet persisted" and keeps the toast in
24
+ * a muted state. 334l will flip the reason to `'accepted'` with a
25
+ * concrete `entryId` once the ingestion pipeline lands.
26
+ *
27
+ * No removal path is offered yet (would require the storage layer to
28
+ * have produced entryIds first); the handler is exported as a named
29
+ * stub so the router can wire it without a second edit when 334l ships.
30
+ */
31
+
32
+ /** @type {(event:object)=>void | null} */
33
+ let _sendUnifyEvent = null;
34
+
35
+ /**
36
+ * Install a send fn. Called once during session init from web-bridge.js.
37
+ * Exposed so tests can swap in a collector without spinning up a session.
38
+ */
39
+ export function setUserMemorySender(fn) {
40
+ _sendUnifyEvent = (typeof fn === 'function') ? fn : null;
41
+ }
42
+
43
+ /**
44
+ * WS handler: `unify_user_memory_write`.
45
+ *
46
+ * Validates the minimum shape (non-empty string `text`) and replies with
47
+ * a `user_memory_updated` ack carrying `pending: true`. Never throws.
48
+ *
49
+ * @param {any} msg
50
+ * @param {(event:object)=>void} [sendUnifyEvent] — optional override
51
+ * (falls back to the module-level sender installed via setUserMemorySender)
52
+ */
53
+ export function handleUnifyUserMemoryWrite(msg, sendUnifyEvent) {
54
+ const send = sendUnifyEvent || _sendUnifyEvent;
55
+ if (!send) return;
56
+
57
+ const requestId = msg && typeof msg.requestId === 'string' ? msg.requestId : undefined;
58
+ const text = msg && typeof msg.text === 'string' ? msg.text : '';
59
+
60
+ if (!text || text.length === 0) {
61
+ try {
62
+ send({
63
+ type: 'user_memory_updated',
64
+ reason: 'noop',
65
+ pending: false,
66
+ ...(requestId ? { requestId } : {}),
67
+ });
68
+ } catch { /* best-effort */ }
69
+ return;
70
+ }
71
+
72
+ // Placeholder — 334l replaces this with real ingestion.
73
+ try {
74
+ send({
75
+ type: 'user_memory_updated',
76
+ reason: 'deferred',
77
+ pending: true,
78
+ ...(requestId ? { requestId } : {}),
79
+ });
80
+ } catch { /* best-effort */ }
81
+ }
82
+
83
+ /**
84
+ * WS handler: `unify_user_memory_remove` (skeleton).
85
+ *
86
+ * Until 334l lands we have no entries to remove; reply with a noop
87
+ * `user_memory_updated` so the UI can clear its toast.
88
+ */
89
+ export function handleUnifyUserMemoryRemove(msg, sendUnifyEvent) {
90
+ const send = sendUnifyEvent || _sendUnifyEvent;
91
+ if (!send) return;
92
+
93
+ const requestId = msg && typeof msg.requestId === 'string' ? msg.requestId : undefined;
94
+ const entryId = msg && typeof msg.entryId === 'string' ? msg.entryId : null;
95
+
96
+ try {
97
+ send({
98
+ type: 'user_memory_removed',
99
+ entryId,
100
+ pending: true, // 334l will flip once real removal lands
101
+ ...(requestId ? { requestId } : {}),
102
+ });
103
+ } catch { /* best-effort */ }
104
+ }
@@ -28,6 +28,11 @@ import ctx from '../context.js';
28
28
  import { getThreadStore, MAIN_THREAD_ID } from './threads/store.js';
29
29
  import { handleVpSubscribe } from './vp/vp-bridge.js';
30
30
  import { createVp, updateVp, deleteVp, readVp, VpCrudError } from './vp/vp-crud.js';
31
+ import { handleUnifyTaskMessage as _handleUnifyTaskMessage } from './task-message.js';
32
+ import {
33
+ handleUnifyUserMemoryWrite as _handleUnifyUserMemoryWrite,
34
+ handleUnifyUserMemoryRemove as _handleUnifyUserMemoryRemove,
35
+ } from './user-memory.js';
31
36
 
32
37
  /** @type {import('./session.js').Session | null} */
33
38
  let session = null;
@@ -198,6 +203,40 @@ export function handleUnifyVpDelete(msg) {
198
203
  }
199
204
  }
200
205
 
206
+ /**
207
+ * task-334h (R6 §Δ28 / §Δ31.6): task-scoped direct message echo.
208
+ *
209
+ * Replaces the withdrawn R3 `unify_task_private_chat`. The agent acts as a
210
+ * relay: validate → stamp msgId + ts → broadcast `task_message`. Real
211
+ * persistence + task ACL lands in 334l.
212
+ *
213
+ * @param {any} msg
214
+ */
215
+ export function handleUnifyTaskMessage(msg) {
216
+ _handleUnifyTaskMessage(msg, sendUnifyEvent);
217
+ }
218
+
219
+ /**
220
+ * task-334h (R6 §Δ29): user-memory write skeleton. Replies with a
221
+ * `user_memory_updated` ack carrying `pending: true`; 334l replaces the
222
+ * stub with real ingestion + entryId.
223
+ *
224
+ * @param {any} msg
225
+ */
226
+ export function handleUnifyUserMemoryWrite(msg) {
227
+ _handleUnifyUserMemoryWrite(msg, sendUnifyEvent);
228
+ }
229
+
230
+ /**
231
+ * task-334h (R6 §Δ29): user-memory remove skeleton. Replies with
232
+ * `user_memory_removed` ack; 334l replaces the stub.
233
+ *
234
+ * @param {any} msg
235
+ */
236
+ export function handleUnifyUserMemoryRemove(msg) {
237
+ _handleUnifyUserMemoryRemove(msg, sendUnifyEvent);
238
+ }
239
+
201
240
  export function handleUnifyVpRead(msg) {
202
241
  const requestId = msg && msg.requestId;
203
242
  const vpId = msg && msg.vpId;