@deepseek-ai/dsh-message-feedback 0.1.2-alpha.5 → 0.1.3-alpha.2
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/README.i18n.yaml +2 -2
- package/README.md +32 -91
- package/README.zh.md +32 -91
- package/lib/index.js +138 -231
- package/lib/typert.host.js +36 -14
- package/lib/typert.remote-client.js +3 -3
- package/lib/types/index.d.ts +28 -43
- package/lib/types/index.js +139 -194
- package/lib/types/types.d.ts +24 -2
- package/package.json +15 -19
- package/lib/types/spec.d.ts +0 -48
- package/lib/types/spec.js +0 -74
package/lib/index.js
CHANGED
|
@@ -1,82 +1,15 @@
|
|
|
1
1
|
import { Buffer } from "node:buffer";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { isDeepStrictEqual } from "node:util";
|
|
3
4
|
import { Service } from "@deepseek-ai/cordis";
|
|
4
5
|
import s from "@deepseek-ai/schemastery";
|
|
5
|
-
import {
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { SessionSeq } from "@deepseek-ai/dsh-session/types";
|
|
6
8
|
import { deriveEventMessage, isAppendSurfaceEvent } from "@deepseek-ai/dsh-session/surface";
|
|
7
9
|
import { Remote, TypertRemoteService } from "@deepseek-ai/dsh-typert-protocol";
|
|
8
|
-
import { z } from "zod";
|
|
9
|
-
import { defineDomain, domainTable } from "@deepseek-ai/dsh-storage-domain";
|
|
10
|
-
//#region lib/types/spec.js
|
|
11
|
-
/**
|
|
12
|
-
* Durable storage-domain declaration for lifecycle-bound message feedback.
|
|
13
|
-
* @module @deepseek-ai/dsh-message-feedback/src/spec
|
|
14
|
-
*/
|
|
15
|
-
const nonNegativeSafeInteger = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
|
|
16
|
-
/** Runtime schema for the closed rating vocabulary. */
|
|
17
|
-
const messageFeedbackRatingSchema = z.union([z.literal("positive"), z.literal("negative")]);
|
|
18
|
-
/** Runtime schema for one opaque item version stored on disk. */
|
|
19
|
-
const messageFeedbackVersionSchema = z.uuid().transform((value) => value);
|
|
20
|
-
/** Runtime schema for one current feedback item. */
|
|
21
|
-
const messageFeedbackItemSchema = z.object({
|
|
22
|
-
messageId: z.string().min(1).transform((value) => value),
|
|
23
|
-
rating: messageFeedbackRatingSchema,
|
|
24
|
-
note: z.string().refine((note) => note.trim().length > 0, { message: "message feedback note must contain a non-whitespace character" }).optional(),
|
|
25
|
-
version: messageFeedbackVersionSchema,
|
|
26
|
-
createdAt: nonNegativeSafeInteger,
|
|
27
|
-
updatedAt: nonNegativeSafeInteger
|
|
28
|
-
}).refine((item) => item.updatedAt >= item.createdAt, {
|
|
29
|
-
path: ["updatedAt"],
|
|
30
|
-
message: "message feedback updatedAt must not precede createdAt"
|
|
31
|
-
});
|
|
32
|
-
/** Persisted Session fields that fence a sidecar row to one log lifecycle. */
|
|
33
|
-
const messageFeedbackSessionIdentitySchema = z.object({
|
|
34
|
-
createdAt: nonNegativeSafeInteger,
|
|
35
|
-
cwd: z.string().optional()
|
|
36
|
-
});
|
|
37
|
-
/**
|
|
38
|
-
* One whole-Session sidecar. Duplicate message ids would make item lookup
|
|
39
|
-
* ambiguous; duplicate versions would break their independent identity.
|
|
40
|
-
*/
|
|
41
|
-
const messageFeedbackRowSchema = z.object({
|
|
42
|
-
session: messageFeedbackSessionIdentitySchema,
|
|
43
|
-
items: z.array(messageFeedbackItemSchema)
|
|
44
|
-
}).superRefine((row, ctx) => {
|
|
45
|
-
const messageIds = /* @__PURE__ */ new Set();
|
|
46
|
-
const versions = /* @__PURE__ */ new Set();
|
|
47
|
-
row.items.forEach((item, index) => {
|
|
48
|
-
if (messageIds.has(item.messageId)) ctx.addIssue({
|
|
49
|
-
code: "custom",
|
|
50
|
-
path: [
|
|
51
|
-
"items",
|
|
52
|
-
index,
|
|
53
|
-
"messageId"
|
|
54
|
-
],
|
|
55
|
-
message: `duplicate message feedback id '${item.messageId}'`
|
|
56
|
-
});
|
|
57
|
-
messageIds.add(item.messageId);
|
|
58
|
-
if (versions.has(item.version)) ctx.addIssue({
|
|
59
|
-
code: "custom",
|
|
60
|
-
path: [
|
|
61
|
-
"items",
|
|
62
|
-
index,
|
|
63
|
-
"version"
|
|
64
|
-
],
|
|
65
|
-
message: `duplicate message feedback version '${item.version}'`
|
|
66
|
-
});
|
|
67
|
-
versions.add(item.version);
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
/** One lifecycle-bound sidecar record per Session id. */
|
|
71
|
-
const messageFeedbackDomainSpec = defineDomain({
|
|
72
|
-
name: "message_feedback",
|
|
73
|
-
version: 0,
|
|
74
|
-
tables: { sessions: domainTable(messageFeedbackRowSchema) }
|
|
75
|
-
});
|
|
76
|
-
//#endregion
|
|
77
10
|
//#region lib/types/index.js
|
|
78
11
|
/**
|
|
79
|
-
*
|
|
12
|
+
* Canonical Session-log feedback for finalized assistant messages.
|
|
80
13
|
* @module @deepseek-ai/dsh-message-feedback
|
|
81
14
|
*/
|
|
82
15
|
var __runInitializers = function(thisArg, initializers, value) {
|
|
@@ -117,74 +50,56 @@ var __esDecorate = function(ctor, descriptorIn, decorators, contextIn, initializ
|
|
|
117
50
|
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
118
51
|
done = true;
|
|
119
52
|
};
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
53
|
+
const timestamp = z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
|
|
54
|
+
const itemSchema = z.object({
|
|
55
|
+
messageId: z.string().min(1),
|
|
56
|
+
rating: z.enum(["positive", "negative"]),
|
|
57
|
+
note: z.string().refine((note) => note.trim().length > 0).optional(),
|
|
58
|
+
version: z.uuid(),
|
|
59
|
+
createdAt: timestamp,
|
|
60
|
+
updatedAt: timestamp
|
|
61
|
+
}).refine((item) => item.updatedAt >= item.createdAt);
|
|
62
|
+
const putSchema = z.object({
|
|
63
|
+
sessionId: z.string().min(1),
|
|
64
|
+
item: itemSchema
|
|
65
|
+
});
|
|
66
|
+
const deleteSchema = z.object({
|
|
67
|
+
sessionId: z.string().min(1),
|
|
68
|
+
messageId: z.string().min(1)
|
|
69
|
+
});
|
|
70
|
+
/** Return a caller-owned immutable value, detached from the log. */
|
|
128
71
|
function snapshotItem(item) {
|
|
129
|
-
return Object.freeze({
|
|
130
|
-
messageId: item.messageId,
|
|
131
|
-
rating: item.rating,
|
|
132
|
-
...item.note === void 0 ? {} : { note: item.note },
|
|
133
|
-
version: item.version,
|
|
134
|
-
createdAt: item.createdAt,
|
|
135
|
-
updatedAt: item.updatedAt
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
/** Copy and freeze a list response. */
|
|
139
|
-
function snapshotList(items) {
|
|
140
|
-
return Object.freeze({ items: Object.freeze(items.map(snapshotItem)) });
|
|
72
|
+
return Object.freeze({ ...item });
|
|
141
73
|
}
|
|
142
|
-
/** Build a frozen success branch. */
|
|
143
74
|
function success(value) {
|
|
144
75
|
return Object.freeze({
|
|
145
76
|
ok: true,
|
|
146
77
|
value
|
|
147
78
|
});
|
|
148
79
|
}
|
|
149
|
-
/** Build a frozen business-failure branch. */
|
|
150
80
|
function rejected(error) {
|
|
151
81
|
return Object.freeze({
|
|
152
82
|
ok: false,
|
|
153
83
|
error: Object.freeze(error)
|
|
154
84
|
});
|
|
155
85
|
}
|
|
156
|
-
/**
|
|
157
|
-
function
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
/** Freeze the replacement row so storage-domain never exposes mutable aliases. */
|
|
172
|
-
function rowSnapshot(session, items) {
|
|
173
|
-
const copiedItems = items.map(snapshotItem);
|
|
174
|
-
Object.freeze(copiedItems);
|
|
175
|
-
return Object.freeze({
|
|
176
|
-
session,
|
|
177
|
-
items: copiedItems
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
/** Generate an opaque equality token for one material mutation. */
|
|
181
|
-
function nextVersion() {
|
|
182
|
-
return randomUUID();
|
|
86
|
+
/** Validate persisted payloads before deriving current, Session-owned feedback. */
|
|
87
|
+
function currentItems(sessionId, events) {
|
|
88
|
+
const items = /* @__PURE__ */ new Map();
|
|
89
|
+
for (const event of events) switch (event.type) {
|
|
90
|
+
case "feedback/message-put":
|
|
91
|
+
putSchema.parse(event.data);
|
|
92
|
+
if (event.data.sessionId === sessionId) items.set(event.data.item.messageId, event.data.item);
|
|
93
|
+
break;
|
|
94
|
+
case "feedback/message-delete":
|
|
95
|
+
deleteSchema.parse(event.data);
|
|
96
|
+
if (event.data.sessionId === sessionId) items.delete(event.data.messageId);
|
|
97
|
+
break;
|
|
98
|
+
default: break;
|
|
99
|
+
}
|
|
100
|
+
return [...items.values()];
|
|
183
101
|
}
|
|
184
|
-
/**
|
|
185
|
-
* Storage-domain sidecar service. It inspects persisted Session history and
|
|
186
|
-
* never creates or resumes an Agent or Session.
|
|
187
|
-
*/
|
|
102
|
+
/** Session-log service; cold operations never construct a Session or Agent. */
|
|
188
103
|
let MessageFeedbackService = (() => {
|
|
189
104
|
let _classSuper = TypertRemoteService;
|
|
190
105
|
let _instanceExtraInitializers = [];
|
|
@@ -237,151 +152,149 @@ let MessageFeedbackService = (() => {
|
|
|
237
152
|
value: _metadata
|
|
238
153
|
});
|
|
239
154
|
}
|
|
240
|
-
static inject = [
|
|
241
|
-
"storageDomain",
|
|
242
|
-
"sessionPersistence",
|
|
243
|
-
"sessions"
|
|
244
|
-
];
|
|
155
|
+
static inject = ["sessionPersistence", "sessions"];
|
|
245
156
|
/** Loader validation for the required note-size policy. */
|
|
246
157
|
static Config = s.object({ maxNoteBytes: s.number().step(1).min(1).required() });
|
|
247
158
|
maxNoteBytes = __runInitializers(this, _instanceExtraInitializers);
|
|
248
|
-
table;
|
|
249
159
|
operationTails = /* @__PURE__ */ new Map();
|
|
250
160
|
mutationAdmissionOpen = true;
|
|
251
161
|
/**
|
|
252
|
-
* @param ctx - Host context carrying persistence and
|
|
162
|
+
* @param ctx - Host context carrying Session persistence and live owners.
|
|
253
163
|
* @param config - Required note-size policy.
|
|
254
164
|
*/
|
|
255
165
|
constructor(ctx, config) {
|
|
256
166
|
super(ctx, "messageFeedback");
|
|
257
|
-
|
|
167
|
+
if (!Number.isSafeInteger(config.maxNoteBytes) || config.maxNoteBytes < 1) throw new TypeError("message-feedback: maxNoteBytes must be a positive safe integer");
|
|
168
|
+
this.maxNoteBytes = config.maxNoteBytes;
|
|
258
169
|
}
|
|
259
|
-
|
|
260
|
-
async [Service.init]() {
|
|
261
|
-
const domain = await this.ctx.storageDomain.open(messageFeedbackDomainSpec);
|
|
170
|
+
[Service.init]() {
|
|
262
171
|
this.ctx.effect(() => async () => {
|
|
263
172
|
this.mutationAdmissionOpen = false;
|
|
264
173
|
await Promise.all(this.operationTails.values());
|
|
265
|
-
|
|
266
|
-
}, "message-feedback.domainClose");
|
|
267
|
-
this.table = domain.table("sessions");
|
|
174
|
+
}, "message-feedback.drain");
|
|
268
175
|
}
|
|
269
176
|
/**
|
|
270
|
-
* Read feedback
|
|
271
|
-
*
|
|
272
|
-
* @
|
|
273
|
-
* @returns current immutable items or `session-not-found`.
|
|
177
|
+
* Read current feedback from the canonical log.
|
|
178
|
+
* @param request - Session to inspect.
|
|
179
|
+
* @returns immutable items or a definite persistence miss.
|
|
274
180
|
*/
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (!known.ok) return known;
|
|
278
|
-
const row = this.requireTable().get(request.sessionId);
|
|
279
|
-
return success(snapshotList(row !== void 0 && sameIdentity(row, known.value.meta) ? row.items : EMPTY_ITEMS));
|
|
181
|
+
list(request) {
|
|
182
|
+
return this.enqueue(request.sessionId, () => this.withSession(request.sessionId, false, (events) => success(Object.freeze({ items: Object.freeze(currentItems(request.sessionId, events).map(snapshotItem)) }))));
|
|
280
183
|
}
|
|
281
184
|
/**
|
|
282
|
-
* Create or replace feedback
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
* @
|
|
286
|
-
* @returns the committed item or an explicit business failure.
|
|
185
|
+
* Create or replace feedback after checking its current version.
|
|
186
|
+
* Matching no-ops retain the version and append no event.
|
|
187
|
+
* @param request - Target, desired value, and observed item version.
|
|
188
|
+
* @returns the durable item or an explicit business failure.
|
|
287
189
|
*/
|
|
288
190
|
put(request) {
|
|
289
191
|
const note = this.resolveNote(request.note);
|
|
290
192
|
if (!note.ok) return Promise.resolve(note);
|
|
291
|
-
return this.enqueue(request.sessionId, async () => {
|
|
292
|
-
const
|
|
293
|
-
if (!
|
|
294
|
-
if (!this.hasFeedbackTarget(known.value, request.messageId)) return rejected({
|
|
295
|
-
code: "target-not-found",
|
|
296
|
-
sessionId: request.sessionId,
|
|
297
|
-
messageId: request.messageId
|
|
298
|
-
});
|
|
299
|
-
const durable = await this.ensureTargetDurable(known.value);
|
|
300
|
-
if (!sameHeaderIdentity(durable.meta, known.value.meta) || !this.hasFeedbackTarget(durable, request.messageId)) return rejected({
|
|
193
|
+
return this.enqueue(request.sessionId, () => this.withSession(request.sessionId, true, async (events, append) => {
|
|
194
|
+
const items = currentItems(request.sessionId, events);
|
|
195
|
+
if (!events.some((event) => event.type === "assistant/message" && isAppendSurfaceEvent(event) && deriveEventMessage(event)?.id === request.messageId)) return rejected({
|
|
301
196
|
code: "target-not-found",
|
|
302
197
|
sessionId: request.sessionId,
|
|
303
198
|
messageId: request.messageId
|
|
304
199
|
});
|
|
305
|
-
const
|
|
306
|
-
const stored = table.get(request.sessionId);
|
|
307
|
-
const items = (stored !== void 0 && sameIdentity(stored, durable.meta) ? stored : void 0)?.items ?? EMPTY_ITEMS;
|
|
308
|
-
const index = items.findIndex((item) => item.messageId === request.messageId);
|
|
309
|
-
const existing = items[index];
|
|
200
|
+
const existing = items.find((item) => item.messageId === request.messageId);
|
|
310
201
|
if (request.ifVersion !== (existing?.version ?? null)) return rejected(this.versionConflict(existing ?? null));
|
|
311
|
-
if (existing !== void 0 && existing.rating === request.rating && existing.note === note.value)
|
|
202
|
+
if (existing !== void 0 && existing.rating === request.rating && existing.note === note.value) {
|
|
203
|
+
await append();
|
|
204
|
+
return success(snapshotItem(existing));
|
|
205
|
+
}
|
|
312
206
|
const now = Date.now();
|
|
313
|
-
const item =
|
|
207
|
+
const item = {
|
|
314
208
|
messageId: request.messageId,
|
|
315
209
|
rating: request.rating,
|
|
316
210
|
...note.value === void 0 ? {} : { note: note.value },
|
|
317
|
-
version:
|
|
211
|
+
version: randomUUID(),
|
|
318
212
|
createdAt: existing?.createdAt ?? now,
|
|
319
213
|
updatedAt: existing === void 0 ? now : Math.max(now, existing.updatedAt)
|
|
214
|
+
};
|
|
215
|
+
await append({
|
|
216
|
+
type: "feedback/message-put",
|
|
217
|
+
data: {
|
|
218
|
+
sessionId: request.sessionId,
|
|
219
|
+
item
|
|
220
|
+
}
|
|
320
221
|
});
|
|
321
|
-
const nextItems = [...items];
|
|
322
|
-
if (index === -1) nextItems.push(item);
|
|
323
|
-
else nextItems[index] = item;
|
|
324
|
-
await table.put(request.sessionId, rowSnapshot(identityOf(durable.meta), nextItems));
|
|
325
222
|
return success(snapshotItem(item));
|
|
326
|
-
});
|
|
223
|
+
}));
|
|
327
224
|
}
|
|
328
225
|
/**
|
|
329
|
-
* Delete one
|
|
330
|
-
* supplied version; an existing item requires an exact version match.
|
|
226
|
+
* Delete one item after checking its version; absence succeeds without an event.
|
|
331
227
|
* @param request - Session, message, and observed item version.
|
|
332
|
-
* @returns the stable absent postcondition
|
|
228
|
+
* @returns the stable absent postcondition or an explicit failure.
|
|
333
229
|
*/
|
|
334
230
|
delete(request) {
|
|
335
|
-
return this.enqueue(request.sessionId, async () => {
|
|
336
|
-
const
|
|
337
|
-
if (
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
231
|
+
return this.enqueue(request.sessionId, () => this.withSession(request.sessionId, true, async (events, append) => {
|
|
232
|
+
const existing = currentItems(request.sessionId, events).find((item) => item.messageId === request.messageId);
|
|
233
|
+
if (existing !== void 0) {
|
|
234
|
+
if (request.ifVersion !== existing.version) return rejected(this.versionConflict(existing));
|
|
235
|
+
await append({
|
|
236
|
+
type: "feedback/message-delete",
|
|
237
|
+
data: {
|
|
238
|
+
sessionId: request.sessionId,
|
|
239
|
+
messageId: request.messageId
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
} else await append();
|
|
345
243
|
return success(Object.freeze({ absent: true }));
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
/**
|
|
349
|
-
* Resolve a live owner directly; otherwise use the storage catalog as the
|
|
350
|
-
* existence authority before inspecting the log. Inspection failures for a
|
|
351
|
-
* catalogued Session remain infrastructure failures rather than being
|
|
352
|
-
* guessed into the business `session-not-found` branch.
|
|
353
|
-
*/
|
|
354
|
-
async inspectSession(sessionId) {
|
|
355
|
-
if (this.ctx.sessions.get(sessionId) === void 0) {
|
|
356
|
-
if (!(await this.ctx.sessionPersistence.listSnapshots()).some((snapshot) => snapshot.header.id === sessionId) && this.ctx.sessions.get(sessionId) === void 0) return rejected({
|
|
357
|
-
code: "session-not-found",
|
|
358
|
-
sessionId
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
return success(await this.ctx.sessionPersistence.inspect(sessionId));
|
|
244
|
+
}));
|
|
362
245
|
}
|
|
363
|
-
/**
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
return message?.role === "assistant" && message.id === messageId;
|
|
246
|
+
/** Hold cold write ownership across read/compare/append; use live owners directly. */
|
|
247
|
+
async withSession(sessionId, write, operation) {
|
|
248
|
+
if (this.ctx.sessions.get(sessionId) === void 0 && await this.ctx.sessionPersistence.stat(sessionId) === void 0 && this.ctx.sessions.get(sessionId) === void 0) return rejected({
|
|
249
|
+
code: "session-not-found",
|
|
250
|
+
sessionId
|
|
369
251
|
});
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
252
|
+
const live = this.ctx.sessions.get(sessionId);
|
|
253
|
+
if (live !== void 0) return operation(live.snapshotEvents(), async (event) => {
|
|
254
|
+
if (event !== void 0) live.append(event.type, event.data);
|
|
255
|
+
const last = live.snapshotEvents().at(-1);
|
|
256
|
+
if (!await this.ctx.sessions.flush(live)) throw new Error(`message-feedback: no durability listener participated for live session '${sessionId}'`);
|
|
257
|
+
const handle = await this.ctx.sessionPersistence.open(sessionId, "read");
|
|
258
|
+
try {
|
|
259
|
+
const { events: stored } = await handle.read(last?.seq ?? 0, 1);
|
|
260
|
+
if (!isDeepStrictEqual([
|
|
261
|
+
handle.header.id,
|
|
262
|
+
handle.header.createdAt,
|
|
263
|
+
handle.header.cwd
|
|
264
|
+
], [
|
|
265
|
+
live.header.id,
|
|
266
|
+
live.header.createdAt,
|
|
267
|
+
live.header.cwd
|
|
268
|
+
]) || last !== void 0 && !isDeepStrictEqual(stored[0], last)) throw new Error(`message-feedback: feedback prefix is not durable for live session '${sessionId}'`);
|
|
269
|
+
} finally {
|
|
270
|
+
await handle.close();
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
const handle = await this.ctx.sessionPersistence.open(sessionId, write ? "write" : "read");
|
|
274
|
+
try {
|
|
275
|
+
const { events } = await handle.read();
|
|
276
|
+
return await operation(events, async (event) => {
|
|
277
|
+
const entry = event === void 0 ? void 0 : {
|
|
278
|
+
...event,
|
|
279
|
+
seq: SessionSeq(events.length),
|
|
280
|
+
time: Date.now()
|
|
281
|
+
};
|
|
282
|
+
if (entry !== void 0) await handle.append([entry]);
|
|
283
|
+
await handle.flush();
|
|
284
|
+
if (entry !== void 0) try {
|
|
285
|
+
await this.ctx.parallel("feedback/committed", {
|
|
286
|
+
meta: handle.header,
|
|
287
|
+
inheritedEventCount: handle.inheritedEventCount,
|
|
288
|
+
events: [...events, entry]
|
|
289
|
+
});
|
|
290
|
+
} catch (error) {
|
|
291
|
+
this.ctx.logger.warn("message-feedback: committed feedback observer failed", error);
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
} finally {
|
|
295
|
+
await handle.close();
|
|
381
296
|
}
|
|
382
|
-
return await this.ctx.sessionPersistence.readFrom(inspection.meta.id, SessionLogOffset(0));
|
|
383
297
|
}
|
|
384
|
-
/** Validate optional-note semantics and the configured complete UTF-8 byte bound. */
|
|
385
298
|
resolveNote(note) {
|
|
386
299
|
if (note === void 0) return success(void 0);
|
|
387
300
|
if (note.trim().length === 0) return rejected({ code: "note-blank" });
|
|
@@ -393,14 +306,13 @@ let MessageFeedbackService = (() => {
|
|
|
393
306
|
});
|
|
394
307
|
return success(note);
|
|
395
308
|
}
|
|
396
|
-
/** Return the authoritative item needed to reconcile one failed comparison. */
|
|
397
309
|
versionConflict(current) {
|
|
398
310
|
return {
|
|
399
311
|
code: "version-conflict",
|
|
400
312
|
current: current === null ? null : snapshotItem(current)
|
|
401
313
|
};
|
|
402
314
|
}
|
|
403
|
-
/**
|
|
315
|
+
/** Serialize complete operations and drain their handles before disposal. */
|
|
404
316
|
enqueue(sessionId, operation) {
|
|
405
317
|
if (!this.mutationAdmissionOpen) return Promise.reject(/* @__PURE__ */ new Error("message-feedback: service is disposing"));
|
|
406
318
|
const result = (this.operationTails.get(sessionId) ?? Promise.resolve()).then(operation);
|
|
@@ -410,12 +322,7 @@ let MessageFeedbackService = (() => {
|
|
|
410
322
|
if (this.operationTails.get(sessionId) === tail) this.operationTails.delete(sessionId);
|
|
411
323
|
});
|
|
412
324
|
}
|
|
413
|
-
/** Resolve the initialized durable table or fail a broken service lifecycle. */
|
|
414
|
-
requireTable() {
|
|
415
|
-
if (this.table === void 0) throw new Error("message-feedback: durable domain is not initialized");
|
|
416
|
-
return this.table;
|
|
417
|
-
}
|
|
418
325
|
};
|
|
419
326
|
})();
|
|
420
327
|
//#endregion
|
|
421
|
-
export { MessageFeedbackService, MessageFeedbackService as default
|
|
328
|
+
export { MessageFeedbackService, MessageFeedbackService as default };
|
package/lib/typert.host.js
CHANGED
|
@@ -124,7 +124,7 @@ export const TYPERT = {
|
|
|
124
124
|
typeSymbol: '@deepseek-ai/dsh-message-feedback/types#MessageFeedbackDeleteResult',
|
|
125
125
|
schema: _deepseek_ai_dsh_message_feedback_messageFeedback_delete_result$schema,
|
|
126
126
|
},
|
|
127
|
-
sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":
|
|
127
|
+
sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":203,"column":3},
|
|
128
128
|
},
|
|
129
129
|
{
|
|
130
130
|
id: '@deepseek-ai/dsh-message-feedback#messageFeedback/list',
|
|
@@ -149,7 +149,7 @@ export const TYPERT = {
|
|
|
149
149
|
typeSymbol: '@deepseek-ai/dsh-message-feedback/types#MessageFeedbackListResult',
|
|
150
150
|
schema: _deepseek_ai_dsh_message_feedback_messageFeedback_list_result$schema,
|
|
151
151
|
},
|
|
152
|
-
sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":
|
|
152
|
+
sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":153,"column":3},
|
|
153
153
|
},
|
|
154
154
|
{
|
|
155
155
|
id: '@deepseek-ai/dsh-message-feedback#messageFeedback/put',
|
|
@@ -174,39 +174,39 @@ export const TYPERT = {
|
|
|
174
174
|
typeSymbol: '@deepseek-ai/dsh-message-feedback/types#MessageFeedbackPutResult',
|
|
175
175
|
schema: _deepseek_ai_dsh_message_feedback_messageFeedback_put_result$schema,
|
|
176
176
|
},
|
|
177
|
-
sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":
|
|
177
|
+
sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":165,"column":3},
|
|
178
178
|
},
|
|
179
179
|
],
|
|
180
180
|
model: {
|
|
181
181
|
"services": [
|
|
182
182
|
{
|
|
183
|
-
"description": "
|
|
184
|
-
"summary": "
|
|
183
|
+
"description": "Session-log service; cold operations never construct a Session or Agent.",
|
|
184
|
+
"summary": "Session-log service; cold operations never construct a Session or Agent.",
|
|
185
185
|
"tags": [],
|
|
186
|
-
"jsDoc": "
|
|
186
|
+
"jsDoc": "/** Session-log service; cold operations never construct a Session or Agent. */",
|
|
187
187
|
"key": "messageFeedback",
|
|
188
188
|
"exportName": "MessageFeedbackService",
|
|
189
189
|
"members": [
|
|
190
190
|
{
|
|
191
191
|
"kind": "method",
|
|
192
192
|
"name": "list",
|
|
193
|
-
"signature": "@Remote('list')
|
|
194
|
-
"summary": "Read feedback
|
|
195
|
-
"jsDoc": "/**\n * Read
|
|
193
|
+
"signature": "@Remote('list') list(request: MessageFeedbackListRequest): Promise<MessageFeedbackListResult>",
|
|
194
|
+
"summary": "Read current feedback from the canonical log.",
|
|
195
|
+
"jsDoc": "/**\n * Read current feedback from the canonical log.\n * @param request - Session to inspect.\n * @returns immutable items or a definite persistence miss.\n */"
|
|
196
196
|
},
|
|
197
197
|
{
|
|
198
198
|
"kind": "method",
|
|
199
199
|
"name": "put",
|
|
200
200
|
"signature": "@Remote('put') put(request: MessageFeedbackPutRequest): Promise<MessageFeedbackPutResult>",
|
|
201
|
-
"summary": "Create or replace feedback
|
|
202
|
-
"jsDoc": "/**\n * Create or replace feedback
|
|
201
|
+
"summary": "Create or replace feedback after checking its current version.",
|
|
202
|
+
"jsDoc": "/**\n * Create or replace feedback after checking its current version.\n * Matching no-ops retain the version and append no event.\n * @param request - Target, desired value, and observed item version.\n * @returns the durable item or an explicit business failure.\n */"
|
|
203
203
|
},
|
|
204
204
|
{
|
|
205
205
|
"kind": "method",
|
|
206
206
|
"name": "delete",
|
|
207
207
|
"signature": "@Remote('delete') delete(request: MessageFeedbackDeleteRequest): Promise<MessageFeedbackDeleteResult>",
|
|
208
|
-
"summary": "Delete one
|
|
209
|
-
"jsDoc": "/**\n * Delete one
|
|
208
|
+
"summary": "Delete one item after checking its version; absence succeeds without an event.",
|
|
209
|
+
"jsDoc": "/**\n * Delete one item after checking its version; absence succeeds without an event.\n * @param request - Session, message, and observed item version.\n * @returns the stable absent postcondition or an explicit failure.\n */"
|
|
210
210
|
}
|
|
211
211
|
],
|
|
212
212
|
"types": [
|
|
@@ -301,7 +301,29 @@ export const TYPERT = {
|
|
|
301
301
|
]
|
|
302
302
|
}
|
|
303
303
|
],
|
|
304
|
-
"events": [
|
|
304
|
+
"events": [
|
|
305
|
+
{
|
|
306
|
+
"description": "Observe a durable cold feedback mutation without publishing a live Session. Observers run before write ownership is released and must not await another message-feedback operation for this Session. The payload is borrowed read-only; deep-clone it before transferring ownership (for example, to Session.fromRestore).",
|
|
307
|
+
"summary": "Observe a durable cold feedback mutation without publishing a live Session.",
|
|
308
|
+
"tags": [
|
|
309
|
+
{
|
|
310
|
+
"name": "param",
|
|
311
|
+
"argument": "inspection",
|
|
312
|
+
"comment": "- committed canonical prefix, including the feedback as its last event.",
|
|
313
|
+
"text": "@param inspection - committed canonical prefix, including the feedback as its last event.\n *"
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
"name": "mode",
|
|
317
|
+
"comment": "parallel",
|
|
318
|
+
"text": "@mode parallel"
|
|
319
|
+
}
|
|
320
|
+
],
|
|
321
|
+
"jsDoc": "/**\n * Observe a durable cold feedback mutation without publishing a live Session.\n * Observers run before write ownership is released and must not await\n * another message-feedback operation for this Session. The payload is borrowed\n * read-only; deep-clone it before transferring ownership (for example, to Session.fromRestore).\n * @param inspection - committed canonical prefix, including the feedback as its last event.\n * @mode parallel\n */",
|
|
322
|
+
"name": "feedback/committed",
|
|
323
|
+
"mode": "parallel",
|
|
324
|
+
"signature": "'feedback/committed'(inspection: SessionInspection): void"
|
|
325
|
+
}
|
|
326
|
+
],
|
|
305
327
|
"objects": []
|
|
306
328
|
},
|
|
307
329
|
}
|
|
@@ -121,7 +121,7 @@ export const TYPERT_REMOTE = {
|
|
|
121
121
|
typeSymbol: '@deepseek-ai/dsh-message-feedback/types#MessageFeedbackDeleteResult',
|
|
122
122
|
schema: _deepseek_ai_dsh_message_feedback_messageFeedback_delete_result$schema,
|
|
123
123
|
},
|
|
124
|
-
sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":
|
|
124
|
+
sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":203,"column":3},
|
|
125
125
|
},
|
|
126
126
|
{
|
|
127
127
|
id: '@deepseek-ai/dsh-message-feedback#messageFeedback/list',
|
|
@@ -146,7 +146,7 @@ export const TYPERT_REMOTE = {
|
|
|
146
146
|
typeSymbol: '@deepseek-ai/dsh-message-feedback/types#MessageFeedbackListResult',
|
|
147
147
|
schema: _deepseek_ai_dsh_message_feedback_messageFeedback_list_result$schema,
|
|
148
148
|
},
|
|
149
|
-
sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":
|
|
149
|
+
sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":153,"column":3},
|
|
150
150
|
},
|
|
151
151
|
{
|
|
152
152
|
id: '@deepseek-ai/dsh-message-feedback#messageFeedback/put',
|
|
@@ -171,7 +171,7 @@ export const TYPERT_REMOTE = {
|
|
|
171
171
|
typeSymbol: '@deepseek-ai/dsh-message-feedback/types#MessageFeedbackPutResult',
|
|
172
172
|
schema: _deepseek_ai_dsh_message_feedback_messageFeedback_put_result$schema,
|
|
173
173
|
},
|
|
174
|
-
sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":
|
|
174
|
+
sourceLocation: {"file":"packages/feedback/message-feedback/src/index.ts","line":165,"column":3},
|
|
175
175
|
},
|
|
176
176
|
],
|
|
177
177
|
}
|