synomem 0.1.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.
Files changed (130) hide show
  1. package/AGENTS.md +42 -0
  2. package/ARCHITECTURE.md +126 -0
  3. package/CHANGELOG.md +35 -0
  4. package/CODE_OF_CONDUCT.md +29 -0
  5. package/CONTRIBUTING.md +48 -0
  6. package/LICENSE +21 -0
  7. package/README.md +247 -0
  8. package/SECURITY.md +117 -0
  9. package/dist/backend.d.ts +9 -0
  10. package/dist/backend.d.ts.map +1 -0
  11. package/dist/backend.js +60 -0
  12. package/dist/backend.js.map +1 -0
  13. package/dist/cli.d.ts +34 -0
  14. package/dist/cli.d.ts.map +1 -0
  15. package/dist/cli.js +1040 -0
  16. package/dist/cli.js.map +1 -0
  17. package/dist/client.d.ts +168 -0
  18. package/dist/client.d.ts.map +1 -0
  19. package/dist/client.js +1031 -0
  20. package/dist/client.js.map +1 -0
  21. package/dist/config.d.ts +33 -0
  22. package/dist/config.d.ts.map +1 -0
  23. package/dist/config.js +153 -0
  24. package/dist/config.js.map +1 -0
  25. package/dist/credentials.d.ts +36 -0
  26. package/dist/credentials.d.ts.map +1 -0
  27. package/dist/credentials.js +127 -0
  28. package/dist/credentials.js.map +1 -0
  29. package/dist/errors.d.ts +9 -0
  30. package/dist/errors.d.ts.map +1 -0
  31. package/dist/errors.js +60 -0
  32. package/dist/errors.js.map +1 -0
  33. package/dist/fs-utils.d.ts +7 -0
  34. package/dist/fs-utils.d.ts.map +1 -0
  35. package/dist/fs-utils.js +68 -0
  36. package/dist/fs-utils.js.map +1 -0
  37. package/dist/import.d.ts +655 -0
  38. package/dist/import.d.ts.map +1 -0
  39. package/dist/import.js +226 -0
  40. package/dist/import.js.map +1 -0
  41. package/dist/index.d.ts +20 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +11 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/mcp/index.d.ts +14 -0
  46. package/dist/mcp/index.d.ts.map +1 -0
  47. package/dist/mcp/index.js +698 -0
  48. package/dist/mcp/index.js.map +1 -0
  49. package/dist/mcp-server.d.ts +3 -0
  50. package/dist/mcp-server.d.ts.map +1 -0
  51. package/dist/mcp-server.js +45 -0
  52. package/dist/mcp-server.js.map +1 -0
  53. package/dist/oauth.d.ts +27 -0
  54. package/dist/oauth.d.ts.map +1 -0
  55. package/dist/oauth.js +201 -0
  56. package/dist/oauth.js.map +1 -0
  57. package/dist/ports/projections.d.ts +6 -0
  58. package/dist/ports/projections.d.ts.map +1 -0
  59. package/dist/ports/projections.js +2 -0
  60. package/dist/ports/projections.js.map +1 -0
  61. package/dist/ports/repository.d.ts +31 -0
  62. package/dist/ports/repository.d.ts.map +1 -0
  63. package/dist/ports/repository.js +2 -0
  64. package/dist/ports/repository.js.map +1 -0
  65. package/dist/projections.d.ts +24 -0
  66. package/dist/projections.d.ts.map +1 -0
  67. package/dist/projections.js +531 -0
  68. package/dist/projections.js.map +1 -0
  69. package/dist/remote.d.ts +120 -0
  70. package/dist/remote.d.ts.map +1 -0
  71. package/dist/remote.js +286 -0
  72. package/dist/remote.js.map +1 -0
  73. package/dist/schemas.d.ts +901 -0
  74. package/dist/schemas.d.ts.map +1 -0
  75. package/dist/schemas.js +424 -0
  76. package/dist/schemas.js.map +1 -0
  77. package/dist/service.d.ts +126 -0
  78. package/dist/service.d.ts.map +1 -0
  79. package/dist/service.js +2 -0
  80. package/dist/service.js.map +1 -0
  81. package/dist/skill-install.d.ts +34 -0
  82. package/dist/skill-install.d.ts.map +1 -0
  83. package/dist/skill-install.js +256 -0
  84. package/dist/skill-install.js.map +1 -0
  85. package/dist/storage.d.ts +94 -0
  86. package/dist/storage.d.ts.map +1 -0
  87. package/dist/storage.js +1229 -0
  88. package/dist/storage.js.map +1 -0
  89. package/dist/types.d.ts +442 -0
  90. package/dist/types.d.ts.map +1 -0
  91. package/dist/types.js +2 -0
  92. package/dist/types.js.map +1 -0
  93. package/dist/version.d.ts +3 -0
  94. package/dist/version.d.ts.map +1 -0
  95. package/dist/version.js +14 -0
  96. package/dist/version.js.map +1 -0
  97. package/docs/cli.md +180 -0
  98. package/docs/examples.md +70 -0
  99. package/docs/mcp.md +96 -0
  100. package/docs/recovery.md +45 -0
  101. package/docs/releasing.md +61 -0
  102. package/docs/skill.md +93 -0
  103. package/docs/storage-format.md +83 -0
  104. package/openapi/synomem-v1.yaml +674 -0
  105. package/package.json +100 -0
  106. package/skills/synomem/SKILL.md +71 -0
  107. package/skills/synomem/agents/openai.yaml +8 -0
  108. package/skills/synomem/references/examples.md +28 -0
  109. package/src/backend.ts +91 -0
  110. package/src/cli.ts +1623 -0
  111. package/src/client.ts +1261 -0
  112. package/src/config.ts +170 -0
  113. package/src/credentials.ts +194 -0
  114. package/src/errors.ts +64 -0
  115. package/src/fs-utils.ts +85 -0
  116. package/src/import.ts +284 -0
  117. package/src/index.ts +61 -0
  118. package/src/mcp/index.ts +973 -0
  119. package/src/mcp-server.ts +46 -0
  120. package/src/oauth.ts +252 -0
  121. package/src/ports/projections.ts +6 -0
  122. package/src/ports/repository.ts +67 -0
  123. package/src/projections.ts +624 -0
  124. package/src/remote.ts +516 -0
  125. package/src/schemas.ts +457 -0
  126. package/src/service.ts +140 -0
  127. package/src/skill-install.ts +332 -0
  128. package/src/storage.ts +1603 -0
  129. package/src/types.ts +458 -0
  130. package/src/version.ts +16 -0
package/src/schemas.ts ADDED
@@ -0,0 +1,457 @@
1
+ import { isAbsolute, normalize } from 'node:path';
2
+ import { z } from 'zod';
3
+ import type { JsonValue } from './types.js';
4
+
5
+ const reservedIds = new Set([
6
+ '.',
7
+ '..',
8
+ 'kudos',
9
+ 'synomem',
10
+ 'exports',
11
+ 'inbox',
12
+ 'con',
13
+ 'prn',
14
+ 'aux',
15
+ 'nul',
16
+ 'com1',
17
+ 'lpt1',
18
+ ]);
19
+
20
+ export const agentIdSchema = z
21
+ .string()
22
+ .min(1)
23
+ .max(63)
24
+ .regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, 'Use lowercase ASCII letters, digits, and hyphens')
25
+ .refine((id) => !reservedIds.has(id), 'Reserved agent ID');
26
+
27
+ export const actorSchema = z.object({
28
+ kind: z.enum(['human', 'agent', 'system']),
29
+ id: agentIdSchema,
30
+ displayName: z.string().trim().min(1).max(200).optional(),
31
+ });
32
+
33
+ export const jsonValueSchema: z.ZodType<JsonValue> = z.lazy(() =>
34
+ z.union([
35
+ z.string(),
36
+ z.number().finite(),
37
+ z.boolean(),
38
+ z.null(),
39
+ z.array(jsonValueSchema),
40
+ z.record(z.string(), jsonValueSchema),
41
+ ]),
42
+ );
43
+
44
+ export const metadataSchema = z.record(z.string().max(200), jsonValueSchema);
45
+
46
+ export const sourceSchema = z.object({
47
+ runtime: z.string().trim().min(1).max(200).optional(),
48
+ model: z.string().trim().min(1).max(200).optional(),
49
+ sessionId: z.string().trim().min(1).max(500).optional(),
50
+ repository: z.string().trim().min(1).max(1000).optional(),
51
+ commit: z.string().trim().min(1).max(200).optional(),
52
+ workingDirectory: z.string().trim().min(1).max(2000).optional(),
53
+ });
54
+
55
+ function isSafeFileEvidence(value: string): boolean {
56
+ const normalized = normalize(value);
57
+ return (
58
+ !isAbsolute(value) &&
59
+ normalized !== '..' &&
60
+ !normalized.startsWith(`..${process.platform === 'win32' ? '\\' : '/'}`)
61
+ );
62
+ }
63
+
64
+ function isHttpUrl(value: string): boolean {
65
+ try {
66
+ const url = new URL(value);
67
+ return url.protocol === 'https:' || url.protocol === 'http:';
68
+ } catch {
69
+ return false;
70
+ }
71
+ }
72
+
73
+ export const evidenceSchema = z
74
+ .object({
75
+ kind: z.enum(['tool-call', 'commit', 'file', 'url', 'task', 'note']),
76
+ label: z.string().trim().min(1).max(200).optional(),
77
+ value: z.string().trim().min(1).max(2000),
78
+ })
79
+ .superRefine((evidence, context) => {
80
+ if (evidence.kind === 'url' && !isHttpUrl(evidence.value)) {
81
+ context.addIssue({ code: 'custom', message: 'URL evidence must use http or https' });
82
+ }
83
+ if (evidence.kind === 'file' && !isSafeFileEvidence(evidence.value)) {
84
+ context.addIssue({ code: 'custom', message: 'File evidence must be a safe relative path' });
85
+ }
86
+ });
87
+
88
+ export const profileSchema = z.object({
89
+ id: agentIdSchema,
90
+ displayName: z.string().trim().min(1).max(200),
91
+ aliases: z.array(agentIdSchema).max(50).optional(),
92
+ description: z.string().trim().max(2000).optional(),
93
+ createdAt: z.string().datetime({ offset: true }),
94
+ metadata: metadataSchema.optional(),
95
+ });
96
+
97
+ export const createAgentSchema = profileSchema.omit({ createdAt: true });
98
+ export const updateAgentSchema = createAgentSchema.omit({ id: true }).partial();
99
+
100
+ const baseEventSchema = z.object({
101
+ schemaVersion: z.literal(1),
102
+ id: z.string().regex(/^[0-9A-HJKMNP-TV-Z]{26}$/),
103
+ workspaceId: z.string().trim().min(1).max(100),
104
+ aggregateId: z.string().regex(/^[0-9A-HJKMNP-TV-Z]{26}$|^[a-z0-9]+(?:-[a-z0-9]+)*$/),
105
+ aggregateVersion: z.number().int().min(1),
106
+ createdAt: z.string().datetime({ offset: true }),
107
+ actor: actorSchema,
108
+ idempotencyKey: z.string().trim().min(1).max(200).optional(),
109
+ source: sourceSchema.optional(),
110
+ metadata: metadataSchema.optional(),
111
+ });
112
+
113
+ export const kudosTitleSchema = z
114
+ .string()
115
+ .trim()
116
+ .min(1)
117
+ .max(200)
118
+ .regex(/^[^\r\n]+$/, 'Kudos titles must be a single line');
119
+
120
+ export const kudosTagSchema = z
121
+ .string()
122
+ .trim()
123
+ .min(1)
124
+ .max(64)
125
+ .regex(/^[\p{L}\p{N}][\p{L}\p{N}._-]*$/u);
126
+
127
+ const kudosGivenSchema = baseEventSchema.extend({
128
+ type: z.literal('kudos.given'),
129
+ recipientAgentId: agentIdSchema,
130
+ recipientDisplayName: z.string().trim().min(1).max(200),
131
+ title: z.string().trim().min(1).max(200),
132
+ reason: z.string().trim().min(1).max(5000),
133
+ evidence: z.array(evidenceSchema).max(50).optional(),
134
+ tags: z.array(kudosTagSchema).max(50).optional(),
135
+ visibility: z.enum(['private', 'workspace', 'public']),
136
+ });
137
+
138
+ const acknowledgedSchema = baseEventSchema.extend({
139
+ type: z.literal('kudos.acknowledged'),
140
+ kudosId: z.string().regex(/^[0-9A-HJKMNP-TV-Z]{26}$/),
141
+ recipientAgentId: agentIdSchema,
142
+ note: z.string().trim().min(1).max(2000).optional(),
143
+ });
144
+
145
+ const revokedSchema = baseEventSchema.extend({
146
+ type: z.literal('kudos.revoked'),
147
+ kudosId: z.string().regex(/^[0-9A-HJKMNP-TV-Z]{26}$/),
148
+ reason: z.string().trim().min(1).max(2000),
149
+ mode: z.enum(['actor-requested', 'administrative']),
150
+ });
151
+
152
+ const agentCreatedSchema = baseEventSchema.extend({
153
+ type: z.literal('agent.created'),
154
+ agent: profileSchema,
155
+ });
156
+
157
+ const agentUpdatedSchema = baseEventSchema.extend({
158
+ type: z.literal('agent.updated'),
159
+ agentId: agentIdSchema,
160
+ changes: updateAgentSchema,
161
+ });
162
+
163
+ const memoSentSchema = baseEventSchema.extend({
164
+ type: z.literal('memo.sent'),
165
+ recipientAgentId: agentIdSchema,
166
+ recipientDisplayName: z.string().trim().min(1).max(200),
167
+ subject: z
168
+ .string()
169
+ .trim()
170
+ .min(1)
171
+ .max(200)
172
+ .regex(/^[^\r\n]+$/),
173
+ body: z.string().trim().min(1).max(16_000),
174
+ tags: z.array(kudosTagSchema).max(20).optional(),
175
+ visibility: z.enum(['private', 'workspace', 'public']),
176
+ });
177
+
178
+ const memoReadSchema = baseEventSchema.extend({
179
+ type: z.literal('memo.read'),
180
+ memoId: z.string().length(26),
181
+ recipientAgentId: agentIdSchema,
182
+ });
183
+ const memoArchivedSchema = baseEventSchema.extend({
184
+ type: z.literal('memo.archived'),
185
+ memoId: z.string().length(26),
186
+ recipientAgentId: agentIdSchema,
187
+ });
188
+
189
+ const noteCreatedSchema = baseEventSchema.extend({
190
+ type: z.literal('note.created'),
191
+ ownerAgentId: agentIdSchema,
192
+ ownerDisplayName: z.string().trim().min(1).max(200),
193
+ title: z
194
+ .string()
195
+ .trim()
196
+ .min(1)
197
+ .max(200)
198
+ .regex(/^[^\r\n]+$/),
199
+ body: z.string().trim().min(1).max(32_000),
200
+ tags: z.array(kudosTagSchema).max(20).optional(),
201
+ visibility: z.literal('private'),
202
+ });
203
+ const noteRevisedSchema = baseEventSchema.extend({
204
+ type: z.literal('note.revised'),
205
+ noteId: z.string().length(26),
206
+ title: z
207
+ .string()
208
+ .trim()
209
+ .min(1)
210
+ .max(200)
211
+ .regex(/^[^\r\n]+$/),
212
+ body: z.string().trim().min(1).max(32_000),
213
+ tags: z.array(kudosTagSchema).max(20).optional(),
214
+ visibility: z.literal('private'),
215
+ });
216
+ const noteArchivedSchema = baseEventSchema.extend({
217
+ type: z.literal('note.archived'),
218
+ noteId: z.string().length(26),
219
+ });
220
+
221
+ const todoDueSchema = z.discriminatedUnion('kind', [
222
+ z.object({
223
+ kind: z.literal('date'),
224
+ date: z
225
+ .string()
226
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
227
+ .refine((value) => {
228
+ const date = new Date(`${value}T00:00:00Z`);
229
+ return !Number.isNaN(date.valueOf()) && date.toISOString().slice(0, 10) === value;
230
+ }, 'Use a valid calendar date'),
231
+ }),
232
+ z.object({
233
+ kind: z.literal('datetime'),
234
+ datetime: z.string().datetime({ offset: true }),
235
+ timeZone: z
236
+ .string()
237
+ .trim()
238
+ .min(1)
239
+ .max(100)
240
+ .refine((value) => {
241
+ try {
242
+ new Intl.DateTimeFormat('en-US', { timeZone: value });
243
+ return true;
244
+ } catch {
245
+ return false;
246
+ }
247
+ }, 'Use a valid IANA time-zone identifier'),
248
+ }),
249
+ ]);
250
+ const todoFields = {
251
+ title: z
252
+ .string()
253
+ .trim()
254
+ .min(1)
255
+ .max(200)
256
+ .regex(/^[^\r\n]+$/),
257
+ description: z.string().trim().max(16_000).optional(),
258
+ priority: z.union([z.literal(1), z.literal(2), z.literal(3), z.literal(4)]),
259
+ due: todoDueSchema.optional(),
260
+ tags: z.array(kudosTagSchema).max(20).optional(),
261
+ visibility: z.enum(['private', 'workspace', 'public']),
262
+ };
263
+ const todoCreatedSchema = baseEventSchema.extend({
264
+ type: z.literal('todo.created'),
265
+ assigneeAgentId: agentIdSchema,
266
+ assigneeDisplayName: z.string().trim().min(1).max(200),
267
+ requiresAcceptance: z.boolean(),
268
+ ...todoFields,
269
+ });
270
+ const todoUpdatedSchema = baseEventSchema.extend({
271
+ type: z.literal('todo.updated'),
272
+ todoId: z.string().length(26),
273
+ ...todoFields,
274
+ });
275
+ const todoCompletedSchema = baseEventSchema.extend({
276
+ type: z.literal('todo.completed'),
277
+ todoId: z.string().length(26),
278
+ note: z.string().trim().min(1).max(2000).optional(),
279
+ });
280
+ const todoReopenedSchema = baseEventSchema.extend({
281
+ type: z.literal('todo.reopened'),
282
+ todoId: z.string().length(26),
283
+ });
284
+ const todoAcceptedSchema = baseEventSchema.extend({
285
+ type: z.literal('todo.accepted'),
286
+ todoId: z.string().length(26),
287
+ });
288
+ const todoRejectedSchema = baseEventSchema.extend({
289
+ type: z.literal('todo.rejected'),
290
+ todoId: z.string().length(26),
291
+ reason: z.string().trim().min(1).max(2000).optional(),
292
+ });
293
+ const todoCanceledSchema = baseEventSchema.extend({
294
+ type: z.literal('todo.canceled'),
295
+ todoId: z.string().length(26),
296
+ reason: z.string().trim().min(1).max(2000).optional(),
297
+ });
298
+
299
+ export const eventSchema = z.discriminatedUnion('type', [
300
+ kudosGivenSchema,
301
+ acknowledgedSchema,
302
+ revokedSchema,
303
+ agentCreatedSchema,
304
+ agentUpdatedSchema,
305
+ memoSentSchema,
306
+ memoReadSchema,
307
+ memoArchivedSchema,
308
+ noteCreatedSchema,
309
+ noteRevisedSchema,
310
+ noteArchivedSchema,
311
+ todoCreatedSchema,
312
+ todoUpdatedSchema,
313
+ todoCompletedSchema,
314
+ todoReopenedSchema,
315
+ todoAcceptedSchema,
316
+ todoRejectedSchema,
317
+ todoCanceledSchema,
318
+ ]);
319
+
320
+ const giveKudosInputSchema = kudosGivenSchema
321
+ .omit({
322
+ schemaVersion: true,
323
+ id: true,
324
+ type: true,
325
+ workspaceId: true,
326
+ aggregateId: true,
327
+ aggregateVersion: true,
328
+ createdAt: true,
329
+ actor: true,
330
+ recipientDisplayName: true,
331
+ })
332
+ .extend({
333
+ title: kudosTitleSchema,
334
+ evidence: z.array(evidenceSchema).max(10).optional(),
335
+ tags: z.array(kudosTagSchema).max(20).optional(),
336
+ });
337
+
338
+ function enforceKudosPayloadSize(value: unknown, context: z.RefinementCtx): void {
339
+ if (Buffer.byteLength(JSON.stringify(value), 'utf8') > 32_768) {
340
+ context.addIssue({ code: 'custom', message: 'Kudos payload must not exceed 32 KiB' });
341
+ }
342
+ }
343
+
344
+ export const giveKudosSchema = giveKudosInputSchema.superRefine(enforceKudosPayloadSize);
345
+
346
+ export const giveKudosMcpSchema = giveKudosInputSchema
347
+ .extend({ visibility: z.enum(['private', 'workspace', 'public']).optional() })
348
+ .superRefine(enforceKudosPayloadSize);
349
+
350
+ export const listInputSchema = z
351
+ .object({
352
+ recipientAgentId: agentIdSchema.optional(),
353
+ actorId: agentIdSchema.optional(),
354
+ actorKind: z.enum(['human', 'agent', 'system']).optional(),
355
+ tag: z.string().trim().min(1).max(64).optional(),
356
+ status: z.enum(['acknowledged', 'unacknowledged']).optional(),
357
+ visibility: z.enum(['private', 'workspace', 'public']).optional(),
358
+ revoked: z.boolean().optional(),
359
+ from: z.string().datetime({ offset: true }).optional(),
360
+ to: z.string().datetime({ offset: true }).optional(),
361
+ cursor: z.string().min(1).max(500).optional(),
362
+ limit: z.number().int().min(1).max(50).default(10),
363
+ offset: z.number().int().min(0).default(0),
364
+ })
365
+ .superRefine((value, context) => {
366
+ if (value.cursor && value.offset > 0) {
367
+ context.addIssue({ code: 'custom', message: 'Use either cursor or offset pagination' });
368
+ }
369
+ });
370
+
371
+ export const changesInputSchema = z.object({
372
+ after: z.string().min(1).max(500).optional(),
373
+ limit: z.number().int().min(1).max(100).default(20),
374
+ });
375
+
376
+ const mutationMetadata = {
377
+ idempotencyKey: z.string().trim().min(1).max(200).optional(),
378
+ source: sourceSchema.optional(),
379
+ metadata: metadataSchema.optional(),
380
+ };
381
+ export const sendMemoSchema = z
382
+ .object({
383
+ recipientAgentId: agentIdSchema,
384
+ subject: memoSentSchema.shape.subject,
385
+ body: memoSentSchema.shape.body,
386
+ tags: z.array(kudosTagSchema).max(20).optional(),
387
+ visibility: z.enum(['private', 'workspace', 'public']).optional(),
388
+ ...mutationMetadata,
389
+ })
390
+ .strict();
391
+ export const createNoteSchema = z
392
+ .object({
393
+ ownerAgentId: agentIdSchema.optional(),
394
+ title: noteCreatedSchema.shape.title,
395
+ body: noteCreatedSchema.shape.body,
396
+ tags: z.array(kudosTagSchema).max(20).optional(),
397
+ ...mutationMetadata,
398
+ })
399
+ .strict();
400
+ export const reviseNoteSchema = z
401
+ .object({
402
+ noteId: z.string().length(26),
403
+ expectedVersion: z.number().int().min(1),
404
+ title: noteCreatedSchema.shape.title.optional(),
405
+ body: noteCreatedSchema.shape.body.optional(),
406
+ tags: z.array(kudosTagSchema).max(20).optional(),
407
+ ...mutationMetadata,
408
+ })
409
+ .strict();
410
+ export const createTodoSchema = z
411
+ .object({
412
+ assigneeAgentId: agentIdSchema.optional(),
413
+ title: todoCreatedSchema.shape.title,
414
+ description: todoCreatedSchema.shape.description,
415
+ priority: todoCreatedSchema.shape.priority.optional(),
416
+ due: todoDueSchema.optional(),
417
+ tags: z.array(kudosTagSchema).max(20).optional(),
418
+ visibility: z.enum(['private', 'workspace', 'public']).optional(),
419
+ ...mutationMetadata,
420
+ })
421
+ .strict();
422
+ export const updateTodoSchema = z
423
+ .object({
424
+ todoId: z.string().length(26),
425
+ expectedVersion: z.number().int().min(1),
426
+ title: todoCreatedSchema.shape.title.optional(),
427
+ description: todoCreatedSchema.shape.description,
428
+ priority: todoCreatedSchema.shape.priority.optional(),
429
+ due: todoDueSchema.nullable().optional(),
430
+ tags: z.array(kudosTagSchema).max(20).optional(),
431
+ visibility: z.enum(['private', 'workspace', 'public']).optional(),
432
+ ...mutationMetadata,
433
+ })
434
+ .strict();
435
+ export const itemListInputSchema = z
436
+ .object({
437
+ kinds: z
438
+ .array(z.enum(['kudos', 'memo', 'note', 'todo']))
439
+ .max(4)
440
+ .optional(),
441
+ participantAgentId: agentIdSchema.optional(),
442
+ actorId: agentIdSchema.optional(),
443
+ actorKind: z.enum(['human', 'agent', 'system']).optional(),
444
+ tag: kudosTagSchema.optional(),
445
+ status: z.string().trim().min(1).max(50).optional(),
446
+ pending: z.boolean().optional(),
447
+ visibility: z.enum(['private', 'workspace', 'public']).optional(),
448
+ from: z.string().datetime({ offset: true }).optional(),
449
+ to: z.string().datetime({ offset: true }).optional(),
450
+ cursor: z.string().min(1).max(500).optional(),
451
+ limit: z.number().int().min(1).max(50).default(10),
452
+ offset: z.number().int().min(0).default(0),
453
+ })
454
+ .superRefine((value, context) => {
455
+ if (value.cursor && value.offset > 0)
456
+ context.addIssue({ code: 'custom', message: 'Use either cursor or offset pagination' });
457
+ });
package/src/service.ts ADDED
@@ -0,0 +1,140 @@
1
+ import type {
2
+ ActorIdentity,
3
+ AgentProfile,
4
+ ChangePage,
5
+ ChangesInput,
6
+ CreateAgentInput,
7
+ CreateNoteInput,
8
+ CreateNoteResult,
9
+ CreateTodoInput,
10
+ CreateTodoResult,
11
+ DoctorResult,
12
+ GiveKudosInput,
13
+ GiveKudosResult,
14
+ ItemListInput,
15
+ ItemRecord,
16
+ ItemSummary,
17
+ KudosChangesInput,
18
+ KudosListInput,
19
+ KudosRecord,
20
+ KudosStats,
21
+ KudosSummary,
22
+ MemoRecord,
23
+ SynomemClientOptions,
24
+ SynomemEvent,
25
+ NoteRecord,
26
+ Page,
27
+ ReviseNoteInput,
28
+ SendMemoInput,
29
+ SendMemoResult,
30
+ TodoRecord,
31
+ UpdateAgentInput,
32
+ UpdateTodoInput,
33
+ } from './types.js';
34
+
35
+ export interface SynomemServiceCapabilities {
36
+ backend: 'local' | 'remote';
37
+ binding: {
38
+ workspaceId: string;
39
+ actor: ActorIdentity;
40
+ };
41
+ administration: {
42
+ agentCreationViaMcp: boolean;
43
+ rebuildViaMcp: boolean;
44
+ };
45
+ projections: {
46
+ writeWinsMarkdown: boolean;
47
+ writeMemoryMarkdown: boolean;
48
+ writeTodosMarkdown: boolean;
49
+ writeInboxEntries: boolean;
50
+ };
51
+ }
52
+
53
+ export type SynomemServiceInfo =
54
+ | { backend: 'local'; home: string; databasePath: string }
55
+ | { backend: 'remote'; baseUrl: string; workspaceId: string };
56
+
57
+ export interface ProjectionRebuildResult {
58
+ generated: string[];
59
+ removed: string[];
60
+ }
61
+
62
+ export interface SynomemDomainService {
63
+ readonly actor: ActorIdentity;
64
+ readonly agents: {
65
+ create(input: CreateAgentInput): Promise<AgentProfile>;
66
+ update(id: string, changes: UpdateAgentInput): Promise<AgentProfile>;
67
+ get(idOrAlias: string): Promise<AgentProfile>;
68
+ list(): Promise<AgentProfile[]>;
69
+ };
70
+ readonly kudos: {
71
+ give(input: GiveKudosInput): Promise<GiveKudosResult>;
72
+ list(input?: KudosListInput): Promise<Page<KudosSummary>>;
73
+ changes(input?: KudosChangesInput): Promise<ChangePage>;
74
+ get(id: string): Promise<KudosRecord>;
75
+ acknowledge(input: { kudosId: string; note?: string }): Promise<KudosRecord>;
76
+ revoke(input: {
77
+ kudosId: string;
78
+ reason: string;
79
+ administrative?: boolean;
80
+ }): Promise<KudosRecord>;
81
+ };
82
+ readonly memos: {
83
+ send(input: SendMemoInput): Promise<SendMemoResult>;
84
+ list(input?: Omit<ItemListInput, 'kinds'>): Promise<Page<ItemSummary>>;
85
+ get(id: string): Promise<MemoRecord>;
86
+ read(input: { memoId: string; idempotencyKey?: string }): Promise<MemoRecord>;
87
+ archive(input: { memoId: string; idempotencyKey?: string }): Promise<MemoRecord>;
88
+ };
89
+ readonly notes: {
90
+ create(input: CreateNoteInput): Promise<CreateNoteResult>;
91
+ list(input?: Omit<ItemListInput, 'kinds'>): Promise<Page<ItemSummary>>;
92
+ get(id: string): Promise<NoteRecord>;
93
+ revise(input: ReviseNoteInput): Promise<NoteRecord>;
94
+ archive(input: { noteId: string; idempotencyKey?: string }): Promise<NoteRecord>;
95
+ };
96
+ readonly todos: {
97
+ create(input: CreateTodoInput): Promise<CreateTodoResult>;
98
+ list(input?: Omit<ItemListInput, 'kinds'>): Promise<Page<ItemSummary>>;
99
+ get(id: string): Promise<TodoRecord>;
100
+ update(input: UpdateTodoInput): Promise<TodoRecord>;
101
+ accept(input: { todoId: string; idempotencyKey?: string }): Promise<TodoRecord>;
102
+ reject(input: {
103
+ todoId: string;
104
+ reason?: string;
105
+ idempotencyKey?: string;
106
+ }): Promise<TodoRecord>;
107
+ complete(input: {
108
+ todoId: string;
109
+ note?: string;
110
+ idempotencyKey?: string;
111
+ }): Promise<TodoRecord>;
112
+ reopen(input: { todoId: string; idempotencyKey?: string }): Promise<TodoRecord>;
113
+ cancel(input: {
114
+ todoId: string;
115
+ reason?: string;
116
+ idempotencyKey?: string;
117
+ }): Promise<TodoRecord>;
118
+ };
119
+ readonly items: {
120
+ list(input?: ItemListInput): Promise<Page<ItemSummary>>;
121
+ get(id: string): Promise<ItemRecord>;
122
+ changes(input?: ChangesInput): Promise<ChangePage>;
123
+ };
124
+
125
+ init(): Promise<void>;
126
+ close(): Promise<void>;
127
+ stats(input?: KudosListInput): Promise<KudosStats>;
128
+ }
129
+
130
+ export interface SynomemService extends SynomemDomainService {
131
+ doctor(): Promise<DoctorResult>;
132
+ export(format: 'json' | 'jsonl' | 'markdown'): Promise<string>;
133
+ backup?(destination: string): Promise<string>;
134
+ rebuild(): Promise<ProjectionRebuildResult>;
135
+ capabilities(): Promise<SynomemServiceCapabilities>;
136
+ info(): Promise<SynomemServiceInfo>;
137
+ getCanonicalEvent(id: string): Promise<SynomemEvent | undefined>;
138
+ }
139
+
140
+ export type SynomemServiceFactory = (options: SynomemClientOptions) => SynomemService;