@emseepea/feedback 0.5.3 → 0.7.0

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,161 @@
1
+ import { publishMcpEvent } from "@emseepea/server";
2
+ import { z } from "zod";
3
+ import { feedbackEventSchema, feedbackSubmissionRecordedEventSchema, } from "./index.js";
4
+ export const feedbackReplyEventName = "feedback.reply.ready";
5
+ export const feedbackSubmittedEventName = "feedback.submitted";
6
+ const identifier = z.string().min(1).max(240);
7
+ const threadArguments = z.strictObject({
8
+ threadId: identifier.describe("Exact feedback thread to watch for a team reply."),
9
+ });
10
+ const replyReference = z.strictObject({
11
+ scope: identifier,
12
+ threadId: identifier,
13
+ messageId: identifier,
14
+ sourceEventId: identifier,
15
+ });
16
+ export function createFeedbackReplyEventsOptions(options) {
17
+ if (typeof options?.canReadThread !== "function")
18
+ throw new TypeError("feedback reply events require canReadThread");
19
+ return {
20
+ definitions: [{
21
+ name: feedbackReplyEventName,
22
+ description: "A team reply is ready. Read the exact thread with get-feedback-thread and present the reply to the user.",
23
+ inputSchema: threadArguments,
24
+ payloadSchema: replyReference,
25
+ matches: (arguments_, data, ownerKey) => arguments_.threadId === data.threadId && ownerKey === data.scope,
26
+ }],
27
+ ownerKey: options.ownerKey,
28
+ async authorize({ ownerKey, name, arguments: arguments_, phase }) {
29
+ if (name !== feedbackReplyEventName)
30
+ return false;
31
+ if (phase === "list")
32
+ return true;
33
+ const parsed = threadArguments.safeParse(arguments_);
34
+ return parsed.success && await options.canReadThread(ownerKey, parsed.data.threadId);
35
+ },
36
+ health: options.health,
37
+ store: options.store,
38
+ ...(options.callbackTimeoutMs === undefined ? {} : { callbackTimeoutMs: options.callbackTimeoutMs }),
39
+ };
40
+ }
41
+ /** Publish a durable team reply reference after the feedback backend commits it. */
42
+ export async function publishFeedbackTeamReplyEvent(app, event) {
43
+ const checked = feedbackEventSchema.safeParse(event);
44
+ if (!checked.success)
45
+ throw new TypeError("Invalid feedback event");
46
+ if (checked.data.type !== "feedback.message.added" || checked.data.author !== "team")
47
+ return false;
48
+ if (!checked.data.messageId)
49
+ throw new TypeError("Team reply event requires messageId");
50
+ await publishMcpEvent(app, feedbackReplyEventName, replyReference.parse({
51
+ scope: checked.data.scope,
52
+ threadId: checked.data.threadId,
53
+ messageId: checked.data.messageId,
54
+ sourceEventId: checked.data.id,
55
+ }));
56
+ return true;
57
+ }
58
+ const maximumMonitoredCollections = 32;
59
+ const submittedReference = z.strictObject({
60
+ collection: identifier.describe("Feedback collection containing the recorded submission."),
61
+ submissionId: identifier.describe("Stable identifier within the feedback collection."),
62
+ sourceEventId: identifier.describe("Identifier of the durable feedback submission event."),
63
+ });
64
+ export function createFeedbackSubmittedEventsOptions(options) {
65
+ const collections = options.definition.monitors.map(({ collection }) => collection);
66
+ if (collections.length === 0)
67
+ return undefined;
68
+ if (collections.length > maximumMonitoredCollections) {
69
+ throw new TypeError(`Configure no more than ${maximumMonitoredCollections} monitored feedback collections.`);
70
+ }
71
+ if (typeof options.canMonitorCollection !== "function") {
72
+ throw new TypeError("Provide canMonitorCollection to authorize feedback submission monitoring.");
73
+ }
74
+ const collectionSchema = z.enum(collections)
75
+ .describe("Feedback collection containing the recorded submission.");
76
+ const subscriptionArguments = submittedSubscriptionArgumentsSchema(collectionSchema, collections.length);
77
+ const payload = submittedReference.extend({ collection: collectionSchema });
78
+ const authorizedCollections = async (ownerKey, phase) => {
79
+ const allowed = [];
80
+ for (const collection of collections) {
81
+ if (await options.canMonitorCollection({ ownerKey, collection, phase })) {
82
+ allowed.push(collection);
83
+ }
84
+ }
85
+ return allowed.sort();
86
+ };
87
+ return {
88
+ definitions: [{
89
+ name: feedbackSubmittedEventName,
90
+ description: "Feedback was recorded in a subscribed collection. Read the exact record with get-feedback-submission before acting on its contents.",
91
+ inputSchema: subscriptionArguments,
92
+ payloadSchema: payload,
93
+ async listWire(ownerKey, wire) {
94
+ const visible = await authorizedCollections(ownerKey, "list");
95
+ if (visible.length === 0)
96
+ return undefined;
97
+ const visibleCollectionSchema = z.enum(visible)
98
+ .describe("Feedback collection containing the recorded submission.");
99
+ return Object.freeze({
100
+ ...wire,
101
+ inputSchema: jsonObjectSchema(submittedSubscriptionArgumentsSchema(visibleCollectionSchema, visible.length)),
102
+ payloadSchema: jsonObjectSchema(submittedReference.extend({ collection: visibleCollectionSchema })),
103
+ });
104
+ },
105
+ matches: (arguments_, data) => Array.isArray(arguments_.collections) && arguments_.collections.includes(data.collection),
106
+ }],
107
+ ownerKey: options.ownerKey,
108
+ async authorize({ ownerKey, name, arguments: arguments_, phase }) {
109
+ if (name !== feedbackSubmittedEventName)
110
+ return false;
111
+ if (phase === "list")
112
+ return (await authorizedCollections(ownerKey, phase)).length > 0;
113
+ const parsed = subscriptionArguments.safeParse(arguments_);
114
+ if (!parsed.success)
115
+ return false;
116
+ for (const collection of parsed.data.collections) {
117
+ if (!await options.canMonitorCollection({ ownerKey, collection, phase }))
118
+ return false;
119
+ }
120
+ return true;
121
+ },
122
+ health: options.health,
123
+ store: options.store,
124
+ ...(options.callbackTimeoutMs === undefined ? {} : { callbackTimeoutMs: options.callbackTimeoutMs }),
125
+ };
126
+ }
127
+ function submittedSubscriptionArgumentsSchema(collectionSchema, maximum) {
128
+ return z.strictObject({
129
+ collections: z.array(collectionSchema)
130
+ .describe("One or more authorized feedback collections to monitor.")
131
+ .min(1)
132
+ .max(maximum)
133
+ .superRefine((selected, context) => {
134
+ if (new Set(selected).size !== selected.length) {
135
+ context.addIssue({ code: "custom", message: "List each collection once, then try again." });
136
+ }
137
+ })
138
+ .overwrite((selected) => [...selected].sort()),
139
+ });
140
+ }
141
+ function jsonObjectSchema(schema) {
142
+ const json = z.toJSONSchema(schema);
143
+ if (!json || typeof json !== "object" || Array.isArray(json) || json.type !== "object") {
144
+ throw new TypeError("feedback submitted event schemas must describe objects");
145
+ }
146
+ return json;
147
+ }
148
+ /**
149
+ * Publish a body-free submission reference from a validated durable-record event.
150
+ * `context.scope` selects the event owner and must equal the stable key returned by `ownerKey`.
151
+ */
152
+ export async function publishFeedbackSubmittedEvent(app, event, context) {
153
+ const checked = feedbackSubmissionRecordedEventSchema.parse(event);
154
+ await publishMcpEvent(app, feedbackSubmittedEventName, submittedReference.parse({
155
+ collection: checked.record.collection,
156
+ submissionId: checked.record.submissionId,
157
+ sourceEventId: checked.id,
158
+ }), { ownerKey: context.scope });
159
+ return true;
160
+ }
161
+ //# sourceMappingURL=mcp-events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-events.js","sourceRoot":"","sources":["../src/mcp-events.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emseepea/feedback",
3
- "version": "0.5.3",
3
+ "version": "0.7.0",
4
4
  "description": "Optional detailed feedback and support conversations for Em See Pea servers",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -44,6 +44,10 @@
44
44
  "types": "./dist/webhooks.d.ts",
45
45
  "import": "./dist/webhooks.js"
46
46
  },
47
+ "./mcp-events": {
48
+ "types": "./dist/mcp-events.d.ts",
49
+ "import": "./dist/mcp-events.js"
50
+ },
47
51
  "./testing-server": "./dist/testing-server.js"
48
52
  },
49
53
  "files": [
@@ -55,18 +59,18 @@
55
59
  "build": "tsc -p tsconfig.json && tsc -p tsconfig.type-tests.json",
56
60
  "prepack": "npm run build",
57
61
  "test": "npm run build && npm run test:built",
58
- "test:built": "node --test test/*.test.mjs",
62
+ "test:built": "node --test test/*.test.mjs src/*.test.js",
59
63
  "test:postgres": "cd ../../examples/multi-instance-postgres-server && node test/with-postgres.mjs node --test ../../packages/feedback/test/postgres.integration.mjs",
60
64
  "test:llm": "npm run build && npm run test:llm:built",
61
65
  "test:llm:built": "emseepea-test eval",
62
66
  "lint": "oxlint src test eval"
63
67
  },
64
68
  "dependencies": {
65
- "@emseepea/server": "0.21.0",
69
+ "@emseepea/server": "0.22.0",
66
70
  "zod": "4.4.3"
67
71
  },
68
72
  "devDependencies": {
69
- "@emseepea/testing": "0.20.2",
73
+ "@emseepea/testing": "0.20.4",
70
74
  "pg": "8.23.0"
71
75
  },
72
76
  "engines": {