synomem 0.1.1 → 0.3.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 (73) hide show
  1. package/ARCHITECTURE.md +2 -2
  2. package/CHANGELOG.md +37 -0
  3. package/README.md +26 -13
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +379 -51
  6. package/dist/cli.js.map +1 -1
  7. package/dist/client.d.ts +131 -17
  8. package/dist/client.d.ts.map +1 -1
  9. package/dist/client.js +489 -76
  10. package/dist/client.js.map +1 -1
  11. package/dist/config.d.ts +2 -2
  12. package/dist/config.js +8 -8
  13. package/dist/import.d.ts +356 -13
  14. package/dist/import.d.ts.map +1 -1
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +2 -2
  18. package/dist/index.js.map +1 -1
  19. package/dist/mcp/index.d.ts.map +1 -1
  20. package/dist/mcp/index.js +213 -31
  21. package/dist/mcp/index.js.map +1 -1
  22. package/dist/mcp-server.js +54 -8
  23. package/dist/mcp-server.js.map +1 -1
  24. package/dist/ports/repository.d.ts +21 -1
  25. package/dist/ports/repository.d.ts.map +1 -1
  26. package/dist/projections.d.ts +26 -1
  27. package/dist/projections.d.ts.map +1 -1
  28. package/dist/projections.js +208 -44
  29. package/dist/projections.js.map +1 -1
  30. package/dist/remote.d.ts +82 -9
  31. package/dist/remote.d.ts.map +1 -1
  32. package/dist/remote.js +53 -2
  33. package/dist/remote.js.map +1 -1
  34. package/dist/schemas.d.ts +476 -21
  35. package/dist/schemas.d.ts.map +1 -1
  36. package/dist/schemas.js +204 -26
  37. package/dist/schemas.js.map +1 -1
  38. package/dist/service.d.ts +82 -10
  39. package/dist/service.d.ts.map +1 -1
  40. package/dist/skill-install.d.ts +8 -2
  41. package/dist/skill-install.d.ts.map +1 -1
  42. package/dist/skill-install.js +6 -11
  43. package/dist/skill-install.js.map +1 -1
  44. package/dist/storage.d.ts +51 -1
  45. package/dist/storage.d.ts.map +1 -1
  46. package/dist/storage.js +366 -36
  47. package/dist/storage.js.map +1 -1
  48. package/dist/types.d.ts +310 -38
  49. package/dist/types.d.ts.map +1 -1
  50. package/docs/cli.md +53 -20
  51. package/docs/examples.md +4 -4
  52. package/docs/mcp.md +12 -4
  53. package/docs/skill.md +10 -7
  54. package/docs/storage-format.md +4 -4
  55. package/openapi/synomem-v1.yaml +24 -24
  56. package/package.json +1 -1
  57. package/skills/synomem/SKILL.md +24 -8
  58. package/skills/synomem/agents/openai.yaml +1 -1
  59. package/skills/synomem/references/examples.md +1 -1
  60. package/src/cli.ts +610 -95
  61. package/src/client.ts +587 -79
  62. package/src/config.ts +8 -8
  63. package/src/index.ts +11 -1
  64. package/src/mcp/index.ts +265 -30
  65. package/src/mcp-server.ts +58 -8
  66. package/src/ports/repository.ts +21 -0
  67. package/src/projections.ts +209 -44
  68. package/src/remote.ts +166 -8
  69. package/src/schemas.ts +211 -26
  70. package/src/service.ts +85 -7
  71. package/src/skill-install.ts +14 -17
  72. package/src/storage.ts +472 -34
  73. package/src/types.ts +332 -39
package/src/types.ts CHANGED
@@ -3,7 +3,7 @@ export type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue
3
3
 
4
4
  export type ActorKind = 'human' | 'agent' | 'system';
5
5
  export type Visibility = 'private' | 'workspace' | 'public';
6
- export type RecordKind = 'kudos' | 'memo' | 'note' | 'todo';
6
+ export type RecordKind = 'kudos' | 'memo' | 'note' | 'post' | 'task' | 'todo';
7
7
 
8
8
  export interface ActorIdentity {
9
9
  kind: ActorKind;
@@ -36,6 +36,53 @@ export interface AgentProfile {
36
36
  metadata?: Record<string, JsonValue>;
37
37
  }
38
38
 
39
+ /**
40
+ * Where an agent is currently reachable, as far as Synomem has been told.
41
+ *
42
+ * A binding is a claim made at registration, not a live connection. `lastSeenAt`
43
+ * is advisory: it says when Synomem last observed this binding act, never that
44
+ * the runtime is reachable now. Callers must not treat its absence as offline.
45
+ */
46
+ export interface AgentRuntimeBinding {
47
+ id: string;
48
+ agentId: string;
49
+ /** Hosted deployment this binding belongs to; absent for local installs. */
50
+ installationId?: string;
51
+ /** Runtime family, e.g. `claude-code`, `hermes`, `openai-agents`. */
52
+ runtime: string;
53
+ /** Named configuration within a runtime, when one runtime hosts several. */
54
+ profile?: string;
55
+ capabilities: Record<string, JsonValue>;
56
+ boundAt: string;
57
+ lastSeenAt?: string;
58
+ }
59
+
60
+ /**
61
+ * One agent as the directory exposes it: visible identity plus advisory
62
+ * reachability.
63
+ *
64
+ * Roles and capabilities are deliberately absent. Authorization is decided by
65
+ * the control plane against the caller's credential, so publishing a role here
66
+ * would only invite a reader to treat the directory as a permission check.
67
+ */
68
+ export interface AgentDirectoryEntry {
69
+ profile: AgentProfile;
70
+ runtimeBindings: AgentRuntimeBinding[];
71
+ }
72
+
73
+ /**
74
+ * The outcome of resolving a name, which may legitimately name no one or
75
+ * several.
76
+ *
77
+ * `match` is set only when exactly one agent answers to the name. Anything else
78
+ * hands back `candidates` so the caller can ask rather than pick.
79
+ */
80
+ export interface AgentResolution {
81
+ query: string;
82
+ match?: AgentProfile;
83
+ candidates: AgentProfile[];
84
+ }
85
+
39
86
  export interface BaseEvent {
40
87
  schemaVersion: 1;
41
88
  id: string;
@@ -93,6 +140,54 @@ export interface MemoArchivedEvent extends BaseEvent {
93
140
  recipientAgentId: string;
94
141
  }
95
142
 
143
+ /**
144
+ * A Post is publication: an author says something to the whole workspace rather
145
+ * than to a named recipient.
146
+ *
147
+ * It has no recipient and no assignee, which is what separates it from a memo
148
+ * and a task. Everyone who can read the workspace can read it, and each of them
149
+ * can acknowledge it independently — a memo is read by one person, a post by
150
+ * many, so "who has responded" is the interesting question rather than "was it
151
+ * read".
152
+ */
153
+ export interface PostCreatedEvent extends BaseEvent {
154
+ type: 'post.created';
155
+ title: string;
156
+ body: string;
157
+ tags?: string[];
158
+ /** The post this replies to. A reply inherits its parent's workspace. */
159
+ replyTo?: string;
160
+ }
161
+ export interface PostEditedEvent extends BaseEvent {
162
+ type: 'post.edited';
163
+ postId: string;
164
+ title: string;
165
+ body: string;
166
+ tags?: string[];
167
+ }
168
+ export interface PostArchivedEvent extends BaseEvent {
169
+ type: 'post.archived';
170
+ postId: string;
171
+ reason?: string;
172
+ }
173
+ /**
174
+ * One actor saying "I have seen this", and only ever about themselves.
175
+ *
176
+ * Appended by an explicit call. Reading a post never acknowledges it: a roster
177
+ * built from read receipts would answer "whose client fetched this", which for
178
+ * an agent means "whose runtime happened to poll".
179
+ */
180
+ export interface PostAcknowledgedEvent extends BaseEvent {
181
+ type: 'post.acknowledged';
182
+ postId: string;
183
+ note?: string;
184
+ }
185
+ export interface PostAcknowledgmentWithdrawnEvent extends BaseEvent {
186
+ type: 'post.acknowledgment.withdrawn';
187
+ postId: string;
188
+ reason?: string;
189
+ }
190
+
96
191
  export interface NoteCreatedEvent extends BaseEvent {
97
192
  type: 'note.created';
98
193
  ownerAgentId: string;
@@ -115,30 +210,95 @@ export interface NoteArchivedEvent extends BaseEvent {
115
210
  noteId: string;
116
211
  }
117
212
 
118
- export type TodoPriority = 1 | 2 | 3 | 4;
119
- export type TodoDue =
213
+ export type TaskPriority = 1 | 2 | 3 | 4;
214
+ export type TaskDue =
120
215
  { kind: 'date'; date: string } | { kind: 'datetime'; datetime: string; timeZone: string };
121
- export interface TodoCreatedEvent extends BaseEvent {
122
- type: 'todo.created';
216
+ export interface TaskCreatedEvent extends BaseEvent {
217
+ type: 'task.created';
123
218
  assigneeAgentId: string;
124
219
  assigneeDisplayName: string;
125
220
  title: string;
126
221
  description?: string;
127
- priority: TodoPriority;
128
- due?: TodoDue;
222
+ priority: TaskPriority;
223
+ due?: TaskDue;
129
224
  tags?: string[];
130
225
  visibility: Visibility;
131
226
  requiresAcceptance: boolean;
132
227
  }
228
+ export interface TaskUpdatedEvent extends BaseEvent {
229
+ type: 'task.updated';
230
+ taskId: string;
231
+ title: string;
232
+ description?: string;
233
+ priority: TaskPriority;
234
+ due?: TaskDue;
235
+ tags?: string[];
236
+ visibility: Visibility;
237
+ }
238
+ export interface TaskCompletedEvent extends BaseEvent {
239
+ type: 'task.completed';
240
+ taskId: string;
241
+ note?: string;
242
+ }
243
+ export interface TaskReopenedEvent extends BaseEvent {
244
+ type: 'task.reopened';
245
+ taskId: string;
246
+ }
247
+ export interface TaskAcceptedEvent extends BaseEvent {
248
+ type: 'task.accepted';
249
+ taskId: string;
250
+ /**
251
+ * An optional note explaining conditions, timing, or partial capability.
252
+ * "Accepted; I can send email but do not have iMessage access."
253
+ */
254
+ response?: string;
255
+ }
256
+ export interface TaskRejectedEvent extends BaseEvent {
257
+ type: 'task.rejected';
258
+ taskId: string;
259
+ /**
260
+ * Required. A refusal without a reason is the least useful event the system
261
+ * can record: the assigner learns only that the work will not happen, not
262
+ * whether to reassign it, wait, or change the request.
263
+ * "Rejected; this Hermes profile has no outbound messaging connection."
264
+ */
265
+ response: string;
266
+ }
267
+ export interface TaskCanceledEvent extends BaseEvent {
268
+ type: 'task.canceled';
269
+ taskId: string;
270
+ reason?: string;
271
+ }
272
+
273
+ /**
274
+ * A Todo is a private reminder an agent creates for itself.
275
+ *
276
+ * The distinction from a Task is the whole point of having both:
277
+ *
278
+ * Task — something another actor asks an agent to do
279
+ * Todo — something an agent privately reminds itself to do
280
+ *
281
+ * So a Todo has no assignee separate from its owner, and no accept/reject
282
+ * lifecycle: there is nobody to negotiate with. It is visible only to its
283
+ * owner, and no ordinary role reads another actor's Todos.
284
+ */
285
+ export interface TodoCreatedEvent extends BaseEvent {
286
+ type: 'todo.created';
287
+ title: string;
288
+ /** Private working detail. Never surfaced to another actor. */
289
+ details?: string;
290
+ priority: TaskPriority;
291
+ due?: TaskDue;
292
+ tags?: string[];
293
+ }
133
294
  export interface TodoUpdatedEvent extends BaseEvent {
134
295
  type: 'todo.updated';
135
296
  todoId: string;
136
297
  title: string;
137
- description?: string;
138
- priority: TodoPriority;
139
- due?: TodoDue;
298
+ details?: string;
299
+ priority: TaskPriority;
300
+ due?: TaskDue;
140
301
  tags?: string[];
141
- visibility: Visibility;
142
302
  }
143
303
  export interface TodoCompletedEvent extends BaseEvent {
144
304
  type: 'todo.completed';
@@ -149,20 +309,49 @@ export interface TodoReopenedEvent extends BaseEvent {
149
309
  type: 'todo.reopened';
150
310
  todoId: string;
151
311
  }
152
- export interface TodoAcceptedEvent extends BaseEvent {
153
- type: 'todo.accepted';
312
+ export interface TodoCanceledEvent extends BaseEvent {
313
+ type: 'todo.canceled';
154
314
  todoId: string;
315
+ reason?: string;
155
316
  }
156
- export interface TodoRejectedEvent extends BaseEvent {
157
- type: 'todo.rejected';
317
+ export interface TodoArchivedEvent extends BaseEvent {
318
+ type: 'todo.archived';
158
319
  todoId: string;
159
- reason?: string;
160
320
  }
161
- export interface TodoCanceledEvent extends BaseEvent {
162
- type: 'todo.canceled';
321
+
322
+ export interface TodoRecord {
323
+ event: TodoCreatedEvent;
324
+ update?: TodoUpdatedEvent;
325
+ terminal?: TodoCompletedEvent | TodoCanceledEvent | TodoArchivedEvent;
326
+ reopened?: TodoReopenedEvent;
327
+ current: {
328
+ title: string;
329
+ details?: string;
330
+ priority: TaskPriority;
331
+ due?: TaskDue;
332
+ tags: string[];
333
+ version: number;
334
+ };
335
+ status: 'open' | 'completed' | 'canceled' | 'archived';
336
+ }
337
+
338
+ export interface CreateTodoInput extends MutationInput {
339
+ title: string;
340
+ details?: string;
341
+ priority?: TaskPriority;
342
+ due?: TaskDue;
343
+ tags?: string[];
344
+ }
345
+ export interface UpdateTodoInput extends MutationInput {
163
346
  todoId: string;
164
- reason?: string;
347
+ expectedVersion: number;
348
+ title?: string;
349
+ details?: string;
350
+ priority?: TaskPriority;
351
+ due?: TaskDue | null;
352
+ tags?: string[];
165
353
  }
354
+ export type CreateTodoResult = MutationResult<TodoRecord>;
166
355
 
167
356
  export interface AgentCreatedEvent extends BaseEvent {
168
357
  type: 'agent.created';
@@ -181,16 +370,27 @@ export type SynomemEvent =
181
370
  | MemoSentEvent
182
371
  | MemoReadEvent
183
372
  | MemoArchivedEvent
373
+ | PostCreatedEvent
374
+ | PostEditedEvent
375
+ | PostArchivedEvent
376
+ | PostAcknowledgedEvent
377
+ | PostAcknowledgmentWithdrawnEvent
184
378
  | NoteCreatedEvent
185
379
  | NoteRevisedEvent
186
380
  | NoteArchivedEvent
381
+ | TaskCreatedEvent
382
+ | TaskUpdatedEvent
383
+ | TaskCompletedEvent
384
+ | TaskReopenedEvent
385
+ | TaskAcceptedEvent
386
+ | TaskRejectedEvent
387
+ | TaskCanceledEvent
187
388
  | TodoCreatedEvent
188
389
  | TodoUpdatedEvent
189
390
  | TodoCompletedEvent
190
391
  | TodoReopenedEvent
191
- | TodoAcceptedEvent
192
- | TodoRejectedEvent
193
392
  | TodoCanceledEvent
393
+ | TodoArchivedEvent
194
394
  | AgentCreatedEvent
195
395
  | AgentUpdatedEvent;
196
396
 
@@ -209,6 +409,40 @@ export interface MemoRecord {
209
409
  archived?: MemoArchivedEvent;
210
410
  status: 'unread' | 'read' | 'archived';
211
411
  }
412
+ export interface PostAcknowledgment {
413
+ actor: ActorIdentity;
414
+ acknowledgedAt: string;
415
+ note?: string;
416
+ }
417
+ export interface PostRecord {
418
+ event: PostCreatedEvent;
419
+ edits: PostEditedEvent[];
420
+ archived?: PostArchivedEvent;
421
+ /** Everyone who has said they have seen it, in the order they said so. */
422
+ acknowledgments: PostAcknowledgment[];
423
+ status: 'active' | 'archived';
424
+ title: string;
425
+ body: string;
426
+ tags?: string[];
427
+ version: number;
428
+ }
429
+
430
+ /**
431
+ * Who has acknowledged a post, and who has not.
432
+ *
433
+ * `outstanding` is the honest denominator: actors who can read the post now AND
434
+ * could have read it when it was posted. Someone who joined afterwards is
435
+ * neither acknowledged nor outstanding — they were not there — and is reported
436
+ * as a count instead, so a roster never accuses a newcomer of ignoring
437
+ * something written before they arrived.
438
+ */
439
+ export interface PostRoster {
440
+ postId: string;
441
+ acknowledged: PostAcknowledgment[];
442
+ outstanding: Array<{ id: string; displayName: string }>;
443
+ joinedSince: number;
444
+ }
445
+
212
446
  export interface NoteRecord {
213
447
  event: NoteCreatedEvent;
214
448
  revision?: NoteRevisedEvent;
@@ -216,23 +450,41 @@ export interface NoteRecord {
216
450
  current: { title: string; body: string; tags: string[]; visibility: Visibility; version: number };
217
451
  status: 'active' | 'archived';
218
452
  }
219
- export interface TodoRecord {
220
- event: TodoCreatedEvent;
221
- update?: TodoUpdatedEvent;
222
- terminal?: TodoCompletedEvent | TodoRejectedEvent | TodoCanceledEvent;
223
- reopened?: TodoReopenedEvent;
453
+ /**
454
+ * One entry in a task's response history.
455
+ *
456
+ * The plan calls for response notes to be part of the task's durable event
457
+ * history rather than private Notes, and for reads to expose that history. It
458
+ * is a list rather than a single field because a task can be rejected, reopened
459
+ * and answered again — and because a later general `task.respond` event for
460
+ * progress updates should extend this without changing its shape.
461
+ */
462
+ export interface TaskResponse {
463
+ kind: 'accepted' | 'rejected';
464
+ response?: string;
465
+ actor: ActorIdentity;
466
+ at: string;
467
+ }
468
+
469
+ export interface TaskRecord {
470
+ event: TaskCreatedEvent;
471
+ update?: TaskUpdatedEvent;
472
+ terminal?: TaskCompletedEvent | TaskRejectedEvent | TaskCanceledEvent;
473
+ reopened?: TaskReopenedEvent;
474
+ /** Every accept/reject answer, oldest first. */
475
+ responses: TaskResponse[];
224
476
  current: {
225
477
  title: string;
226
478
  description?: string;
227
- priority: TodoPriority;
228
- due?: TodoDue;
479
+ priority: TaskPriority;
480
+ due?: TaskDue;
229
481
  tags: string[];
230
482
  visibility: Visibility;
231
483
  version: number;
232
484
  };
233
485
  status: 'assigned' | 'open' | 'completed' | 'rejected' | 'canceled';
234
486
  }
235
- export type ItemRecord = KudosRecord | MemoRecord | NoteRecord | TodoRecord;
487
+ export type ItemRecord = KudosRecord | MemoRecord | NoteRecord | TaskRecord | TodoRecord;
236
488
 
237
489
  export interface ItemSummary {
238
490
  id: string;
@@ -273,6 +525,22 @@ export interface ItemListInput extends PaginationInput {
273
525
  to?: string;
274
526
  /** Limit results to actionable inbox states; intended for the participant's own inbox. */
275
527
  pending?: boolean;
528
+ /**
529
+ * Limit results to items still waiting for somebody to answer: tasks awaiting
530
+ * acceptance, unread memos, unacknowledged kudos.
531
+ *
532
+ * Narrower than `pending`, which also counts accepted work in progress. These
533
+ * are query states derived from durable events — they do not prove an agent
534
+ * was online, saw a notification, or possessed a claimed capability.
535
+ */
536
+ awaitingResponse?: boolean;
537
+ /** With `awaitingResponse`, only items created at or before this instant. */
538
+ awaitingSince?: string;
539
+ /**
540
+ * Items whose deadline has passed at this instant and which are still open.
541
+ * A date-only deadline counts as the end of that day.
542
+ */
543
+ overdueAsOf?: string;
276
544
  }
277
545
  export interface KudosListInput extends PaginationInput {
278
546
  recipientAgentId?: string;
@@ -348,6 +616,23 @@ export interface SendMemoInput extends MutationInput {
348
616
  tags?: string[];
349
617
  visibility?: Visibility;
350
618
  }
619
+ export interface CreatePostInput extends MutationInput {
620
+ title: string;
621
+ body: string;
622
+ tags?: string[];
623
+ /** The post being replied to; a reply inherits its parent's workspace. */
624
+ replyTo?: string;
625
+ }
626
+
627
+ export interface UpdatePostInput {
628
+ postId: string;
629
+ expectedVersion: number;
630
+ title?: string;
631
+ body?: string;
632
+ tags?: string[];
633
+ idempotencyKey?: string;
634
+ }
635
+
351
636
  export interface CreateNoteInput extends MutationInput {
352
637
  ownerAgentId?: string;
353
638
  title: string;
@@ -361,22 +646,22 @@ export interface ReviseNoteInput extends MutationInput {
361
646
  body?: string;
362
647
  tags?: string[];
363
648
  }
364
- export interface CreateTodoInput extends MutationInput {
649
+ export interface CreateTaskInput extends MutationInput {
365
650
  assigneeAgentId?: string;
366
651
  title: string;
367
652
  description?: string;
368
- priority?: TodoPriority;
369
- due?: TodoDue;
653
+ priority?: TaskPriority;
654
+ due?: TaskDue;
370
655
  tags?: string[];
371
656
  visibility?: Visibility;
372
657
  }
373
- export interface UpdateTodoInput extends MutationInput {
374
- todoId: string;
658
+ export interface UpdateTaskInput extends MutationInput {
659
+ taskId: string;
375
660
  expectedVersion: number;
376
661
  title?: string;
377
662
  description?: string;
378
- priority?: TodoPriority;
379
- due?: TodoDue | null;
663
+ priority?: TaskPriority;
664
+ due?: TaskDue | null;
380
665
  tags?: string[];
381
666
  visibility?: Visibility;
382
667
  }
@@ -388,7 +673,15 @@ export interface MutationResult<T> {
388
673
  export type GiveKudosResult = MutationResult<KudosRecord>;
389
674
  export type SendMemoResult = MutationResult<MemoRecord>;
390
675
  export type CreateNoteResult = MutationResult<NoteRecord>;
391
- export type CreateTodoResult = MutationResult<TodoRecord>;
676
+ export type CreateTaskResult = MutationResult<TaskRecord>;
677
+
678
+ export interface BindRuntimeInput {
679
+ agentId: string;
680
+ runtime: string;
681
+ profile?: string;
682
+ installationId?: string;
683
+ capabilities?: Record<string, JsonValue>;
684
+ }
392
685
 
393
686
  export interface CreateAgentInput {
394
687
  id: string;
@@ -432,14 +725,14 @@ export interface SynomemConfig {
432
725
  workspaceId: string;
433
726
  defaultVisibility: Visibility;
434
727
  allowSelfAwards: boolean;
435
- allowCrossAgentTodos: boolean;
728
+ allowCrossAgentTasks: boolean;
436
729
  allowAgentCreationViaMcp: boolean;
437
730
  allowRebuildViaMcp: boolean;
438
731
  includePrivateInStats: boolean;
439
732
  projection: {
440
733
  writeWinsMarkdown: boolean;
441
734
  writeMemoryMarkdown: boolean;
442
- writeTodosMarkdown: boolean;
735
+ writeTasksMarkdown: boolean;
443
736
  writeInboxEntries: boolean;
444
737
  };
445
738
  }