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.
- package/ARCHITECTURE.md +2 -2
- package/CHANGELOG.md +37 -0
- package/README.md +26 -13
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +379 -51
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +131 -17
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +489 -76
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/config.js +8 -8
- package/dist/import.d.ts +356 -13
- package/dist/import.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +213 -31
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp-server.js +54 -8
- package/dist/mcp-server.js.map +1 -1
- package/dist/ports/repository.d.ts +21 -1
- package/dist/ports/repository.d.ts.map +1 -1
- package/dist/projections.d.ts +26 -1
- package/dist/projections.d.ts.map +1 -1
- package/dist/projections.js +208 -44
- package/dist/projections.js.map +1 -1
- package/dist/remote.d.ts +82 -9
- package/dist/remote.d.ts.map +1 -1
- package/dist/remote.js +53 -2
- package/dist/remote.js.map +1 -1
- package/dist/schemas.d.ts +476 -21
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +204 -26
- package/dist/schemas.js.map +1 -1
- package/dist/service.d.ts +82 -10
- package/dist/service.d.ts.map +1 -1
- package/dist/skill-install.d.ts +8 -2
- package/dist/skill-install.d.ts.map +1 -1
- package/dist/skill-install.js +6 -11
- package/dist/skill-install.js.map +1 -1
- package/dist/storage.d.ts +51 -1
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +366 -36
- package/dist/storage.js.map +1 -1
- package/dist/types.d.ts +310 -38
- package/dist/types.d.ts.map +1 -1
- package/docs/cli.md +53 -20
- package/docs/examples.md +4 -4
- package/docs/mcp.md +12 -4
- package/docs/skill.md +10 -7
- package/docs/storage-format.md +4 -4
- package/openapi/synomem-v1.yaml +24 -24
- package/package.json +1 -1
- package/skills/synomem/SKILL.md +24 -8
- package/skills/synomem/agents/openai.yaml +1 -1
- package/skills/synomem/references/examples.md +1 -1
- package/src/cli.ts +610 -95
- package/src/client.ts +587 -79
- package/src/config.ts +8 -8
- package/src/index.ts +11 -1
- package/src/mcp/index.ts +265 -30
- package/src/mcp-server.ts +58 -8
- package/src/ports/repository.ts +21 -0
- package/src/projections.ts +209 -44
- package/src/remote.ts +166 -8
- package/src/schemas.ts +211 -26
- package/src/service.ts +85 -7
- package/src/skill-install.ts +14 -17
- package/src/storage.ts +472 -34
- package/src/types.ts +332 -39
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ActorIdentity, AgentProfile, ChangePage, ItemListInput, ItemSummary, KudosListInput, KudosSummary, SynomemConfig, SynomemEvent, Page, RecordKind } from '../types.js';
|
|
1
|
+
import type { ActorIdentity, AgentProfile, AgentRuntimeBinding, ChangePage, ItemListInput, JsonValue, PostAcknowledgment, PostRoster, ItemSummary, KudosListInput, KudosSummary, SynomemConfig, SynomemEvent, Page, RecordKind } from '../types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Operations required by authoritative domain behavior.
|
|
4
4
|
*
|
|
@@ -21,6 +21,26 @@ export interface SynomemRepository {
|
|
|
21
21
|
updateAgent(profile: AgentProfile, updatedAt: string): Awaitable<void>;
|
|
22
22
|
getAgent(idOrAlias: string): Awaitable<AgentProfile | undefined>;
|
|
23
23
|
listAgents(): Awaitable<AgentProfile[]>;
|
|
24
|
+
/** Resolves a name case-insensitively, reporting ambiguity instead of guessing. */
|
|
25
|
+
resolveAgent(query: string): Awaitable<{
|
|
26
|
+
match?: AgentProfile;
|
|
27
|
+
candidates: AgentProfile[];
|
|
28
|
+
}>;
|
|
29
|
+
listPostAcknowledgments(postId: string): Awaitable<PostAcknowledgment[]>;
|
|
30
|
+
/** Who has acknowledged a post and who has not; see PostRoster. */
|
|
31
|
+
postRoster(postId: string): Awaitable<PostRoster | undefined>;
|
|
32
|
+
listRuntimeBindings(agentId: string): Awaitable<AgentRuntimeBinding[]>;
|
|
33
|
+
bindRuntime(binding: {
|
|
34
|
+
id: string;
|
|
35
|
+
agentId: string;
|
|
36
|
+
installationId?: string;
|
|
37
|
+
runtime: string;
|
|
38
|
+
profile?: string;
|
|
39
|
+
capabilities?: Record<string, JsonValue>;
|
|
40
|
+
boundAt: string;
|
|
41
|
+
}): Awaitable<void>;
|
|
42
|
+
unbindRuntime(bindingId: string): Awaitable<boolean>;
|
|
43
|
+
touchRuntimeBinding(agentId: string, runtime: string, at: string): Awaitable<void>;
|
|
24
44
|
listKudosSummaries(input: Required<Pick<KudosListInput, 'limit' | 'offset'>> & KudosListInput, viewer: ActorIdentity): Awaitable<Page<KudosSummary>>;
|
|
25
45
|
listKudosChanges(after: string | undefined, limit: number, viewer: ActorIdentity): Awaitable<ChangePage>;
|
|
26
46
|
listItemSummaries(input: Required<Pick<ItemListInput, 'limit' | 'offset'>> & ItemListInput, viewer: ActorIdentity): Awaitable<Page<ItemSummary>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/ports/repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,UAAU,EACV,aAAa,EACb,WAAW,EACX,cAAc,EACd,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,IAAI,EACJ,UAAU,EACX,MAAM,aAAa,CAAC;AAErB;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAE/B,IAAI,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC;IACxB,KAAK,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC;IACzB,wBAAwB,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC;IAC5C,WAAW,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAE1D,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAClD,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAC7D,qBAAqB,CACnB,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,EAChC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,GACV,SAAS,CAAC,YAAY,GAAG,SAAS,CAAC,CAAC;IACvC,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC;IACrE,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC;IAE7D,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IACpD,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IACvE,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC,YAAY,GAAG,SAAS,CAAC,CAAC;IACjE,UAAU,IAAI,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/ports/repository.ts"],"names":[],"mappings":"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"}
|
package/dist/projections.d.ts
CHANGED
|
@@ -1,11 +1,36 @@
|
|
|
1
1
|
import type { SynomemStorage } from './storage.js';
|
|
2
2
|
import type { ProjectionWriter } from './ports/projections.js';
|
|
3
|
-
import type { SynomemEvent, KudosRecord, MemoRecord, NoteRecord, TodoRecord } from './types.js';
|
|
3
|
+
import type { SynomemEvent, KudosRecord, MemoRecord, NoteRecord, PostRecord, TaskDue, TaskRecord, TodoRecord } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* The instant a deadline has passed.
|
|
6
|
+
*
|
|
7
|
+
* A date-only deadline does not invent a time of day; it lapses at the end of
|
|
8
|
+
* that UTC day, which is the latest reading that cannot call work late while
|
|
9
|
+
* the stated day is still running. Every projection must use this one rule, or
|
|
10
|
+
* two stores would disagree about what is overdue.
|
|
11
|
+
*/
|
|
12
|
+
export declare function dueInstant(due: TaskDue | undefined): string | undefined;
|
|
4
13
|
export declare function escapeMarkdown(value: string): string;
|
|
5
14
|
export declare function recordsFromEvents(events: SynomemEvent[]): KudosRecord[];
|
|
6
15
|
export declare function memoRecordsFromEvents(events: SynomemEvent[]): MemoRecord[];
|
|
16
|
+
/**
|
|
17
|
+
* Rebuilds posts from their events, acknowledgements included.
|
|
18
|
+
*
|
|
19
|
+
* Edits are kept as a list rather than collapsed into the current text. A reader
|
|
20
|
+
* has to be able to see that a post changed after somebody acknowledged it —
|
|
21
|
+
* silently rewriting what was acknowledged is how a record becomes a lie.
|
|
22
|
+
*/
|
|
23
|
+
export declare function postRecordsFromEvents(events: SynomemEvent[]): PostRecord[];
|
|
7
24
|
export declare function noteRecordsFromEvents(events: SynomemEvent[]): NoteRecord[];
|
|
25
|
+
/**
|
|
26
|
+
* Builds private Todo records.
|
|
27
|
+
*
|
|
28
|
+
* Deliberately simpler than the Task reducer: there is no acceptance state and
|
|
29
|
+
* no response history, because there is nobody to negotiate with. A Todo goes
|
|
30
|
+
* open → completed / canceled / archived, and back to open on reopen.
|
|
31
|
+
*/
|
|
8
32
|
export declare function todoRecordsFromEvents(events: SynomemEvent[]): TodoRecord[];
|
|
33
|
+
export declare function taskRecordsFromEvents(events: SynomemEvent[]): TaskRecord[];
|
|
9
34
|
/** Render a portable human-readable export without reading generated projections. */
|
|
10
35
|
export declare function renderMarkdownExport(events: SynomemEvent[]): string;
|
|
11
36
|
export declare class ProjectionManager implements ProjectionWriter {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projections.d.ts","sourceRoot":"","sources":["../src/projections.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAGV,YAAY,EACZ,WAAW,EACX,UAAU,EACV,UAAU,EACV,UAAU,EACX,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"projections.d.ts","sourceRoot":"","sources":["../src/projections.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAGV,YAAY,EACZ,WAAW,EACX,UAAU,EACV,UAAU,EACV,UAAU,EACV,OAAO,EACP,UAAU,EACV,UAAU,EACX,MAAM,YAAY,CAAC;AAEpB;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAGvE;AAQD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOpD;AA2JD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,WAAW,EAAE,CA4BvE;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,UAAU,EAAE,CAoB1E;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,UAAU,EAAE,CA4D1E;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,UAAU,EAAE,CAqC1E;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,UAAU,EAAE,CAwD1E;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,UAAU,EAAE,CA8E1E;AAED,qFAAqF;AACrF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,CAoBnE;AAED,qBAAa,iBAAkB,YAAW,gBAAgB;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,cAAc;IAEpD,OAAO,IAAI;QAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE;IAwGrD,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG;QAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE;IA+GtE,aAAa,IAAI,MAAM,EAAE;CAqC1B"}
|
package/dist/projections.js
CHANGED
|
@@ -2,6 +2,19 @@ import { existsSync, lstatSync, unlinkSync } from 'node:fs';
|
|
|
2
2
|
import { join, relative, sep } from 'node:path';
|
|
3
3
|
import { SynomemError } from './errors.js';
|
|
4
4
|
import { assertNoSymlinkEscape, atomicWriteDerivedFile, atomicWriteFile, ensureDirectory, } from './fs-utils.js';
|
|
5
|
+
/**
|
|
6
|
+
* The instant a deadline has passed.
|
|
7
|
+
*
|
|
8
|
+
* A date-only deadline does not invent a time of day; it lapses at the end of
|
|
9
|
+
* that UTC day, which is the latest reading that cannot call work late while
|
|
10
|
+
* the stated day is still running. Every projection must use this one rule, or
|
|
11
|
+
* two stores would disagree about what is overdue.
|
|
12
|
+
*/
|
|
13
|
+
export function dueInstant(due) {
|
|
14
|
+
if (!due)
|
|
15
|
+
return undefined;
|
|
16
|
+
return due.kind === 'date' ? `${due.date}T23:59:59.999Z` : due.datetime;
|
|
17
|
+
}
|
|
5
18
|
const generatedMarker = '<!-- Generated by Synomem. Rebuild with `synomem rebuild`. -->';
|
|
6
19
|
function manifestPath(home, path) {
|
|
7
20
|
return relative(home, path).split(sep).join('/');
|
|
@@ -87,7 +100,7 @@ function memoInboxMarkdown(record) {
|
|
|
87
100
|
'',
|
|
88
101
|
].join('\n')}\n`;
|
|
89
102
|
}
|
|
90
|
-
function
|
|
103
|
+
function taskInboxMarkdown(record) {
|
|
91
104
|
const due = record.current.due?.kind === 'date'
|
|
92
105
|
? record.current.due.date
|
|
93
106
|
: record.current.due
|
|
@@ -101,7 +114,7 @@ function todoInboxMarkdown(record) {
|
|
|
101
114
|
`**Status:** ${record.status} `,
|
|
102
115
|
`**Priority:** ${record.current.priority} `,
|
|
103
116
|
`**Due:** ${escapeMarkdown(due)} `,
|
|
104
|
-
`**
|
|
117
|
+
`**Task ID:** \`${record.event.id}\``,
|
|
105
118
|
'',
|
|
106
119
|
...(record.current.description ? [escapeMarkdown(record.current.description), ''] : []),
|
|
107
120
|
].join('\n')}\n`;
|
|
@@ -123,24 +136,24 @@ function memoryMarkdown(profile, records, rebuiltAt) {
|
|
|
123
136
|
}
|
|
124
137
|
return `${lines.join('\n')}\n`;
|
|
125
138
|
}
|
|
126
|
-
function
|
|
139
|
+
function tasksMarkdown(profile, records, rebuiltAt) {
|
|
127
140
|
const lines = [
|
|
128
141
|
generatedMarker,
|
|
129
142
|
'',
|
|
130
|
-
`#
|
|
143
|
+
`# Tasks — ${escapeMarkdown(profile.displayName)}`,
|
|
131
144
|
'',
|
|
132
145
|
`_Last rebuilt: ${rebuiltAt}_`,
|
|
133
146
|
'',
|
|
134
147
|
];
|
|
135
148
|
if (!records.length)
|
|
136
|
-
lines.push('_No
|
|
149
|
+
lines.push('_No tasks yet._', '');
|
|
137
150
|
for (const record of records) {
|
|
138
151
|
const due = record.current.due?.kind === 'date'
|
|
139
152
|
? record.current.due.date
|
|
140
153
|
: record.current.due
|
|
141
154
|
? `${record.current.due.datetime} (${record.current.due.timeZone})`
|
|
142
155
|
: 'None';
|
|
143
|
-
lines.push(`## ${escapeMarkdown(record.current.title)}`, '', `**Status:** ${record.status} `, `**Priority:** ${record.current.priority} `, `**Due:** ${escapeMarkdown(due)} `, `**
|
|
156
|
+
lines.push(`## ${escapeMarkdown(record.current.title)}`, '', `**Status:** ${record.status} `, `**Priority:** ${record.current.priority} `, `**Due:** ${escapeMarkdown(due)} `, `**Task ID:** \`${record.event.id}\``, '');
|
|
144
157
|
}
|
|
145
158
|
return `${lines.join('\n')}\n`;
|
|
146
159
|
}
|
|
@@ -194,6 +207,77 @@ export function memoRecordsFromEvents(events) {
|
|
|
194
207
|
}
|
|
195
208
|
return [...records.values()].sort((a, b) => b.event.createdAt.localeCompare(a.event.createdAt));
|
|
196
209
|
}
|
|
210
|
+
/**
|
|
211
|
+
* Rebuilds posts from their events, acknowledgements included.
|
|
212
|
+
*
|
|
213
|
+
* Edits are kept as a list rather than collapsed into the current text. A reader
|
|
214
|
+
* has to be able to see that a post changed after somebody acknowledged it —
|
|
215
|
+
* silently rewriting what was acknowledged is how a record becomes a lie.
|
|
216
|
+
*/
|
|
217
|
+
export function postRecordsFromEvents(events) {
|
|
218
|
+
const records = new Map();
|
|
219
|
+
for (const event of events) {
|
|
220
|
+
if (event.type === 'post.created') {
|
|
221
|
+
records.set(event.id, {
|
|
222
|
+
event,
|
|
223
|
+
edits: [],
|
|
224
|
+
acknowledgments: [],
|
|
225
|
+
status: 'active',
|
|
226
|
+
title: event.title,
|
|
227
|
+
body: event.body,
|
|
228
|
+
...(event.tags ? { tags: event.tags } : {}),
|
|
229
|
+
// The text version, counting only changes to the text.
|
|
230
|
+
//
|
|
231
|
+
// Deliberately not the aggregate version, which also counts every
|
|
232
|
+
// acknowledgement. If they were the same number, somebody
|
|
233
|
+
// acknowledging a post would invalidate an edit the author was in the
|
|
234
|
+
// middle of making — a conflict with nothing to reconcile.
|
|
235
|
+
version: 1,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
else if (event.type === 'post.edited') {
|
|
239
|
+
const record = records.get(event.postId);
|
|
240
|
+
if (record) {
|
|
241
|
+
record.edits.push(event);
|
|
242
|
+
record.title = event.title;
|
|
243
|
+
record.body = event.body;
|
|
244
|
+
if (event.tags)
|
|
245
|
+
record.tags = event.tags;
|
|
246
|
+
record.version += 1;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
else if (event.type === 'post.archived') {
|
|
250
|
+
const record = records.get(event.postId);
|
|
251
|
+
if (record) {
|
|
252
|
+
record.archived = event;
|
|
253
|
+
record.status = 'archived';
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
else if (event.type === 'post.acknowledged') {
|
|
257
|
+
const record = records.get(event.postId);
|
|
258
|
+
if (record) {
|
|
259
|
+
// One statement per actor: a repeat replaces rather than accumulates.
|
|
260
|
+
const existing = record.acknowledgments.findIndex((entry) => entry.actor.id === event.actor.id && entry.actor.kind === event.actor.kind);
|
|
261
|
+
const entry = {
|
|
262
|
+
actor: event.actor,
|
|
263
|
+
acknowledgedAt: event.createdAt,
|
|
264
|
+
...(event.note ? { note: event.note } : {}),
|
|
265
|
+
};
|
|
266
|
+
if (existing >= 0)
|
|
267
|
+
record.acknowledgments[existing] = entry;
|
|
268
|
+
else
|
|
269
|
+
record.acknowledgments.push(entry);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
else if (event.type === 'post.acknowledgment.withdrawn') {
|
|
273
|
+
const record = records.get(event.postId);
|
|
274
|
+
if (record) {
|
|
275
|
+
record.acknowledgments = record.acknowledgments.filter((entry) => !(entry.actor.id === event.actor.id && entry.actor.kind === event.actor.kind));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return [...records.values()];
|
|
280
|
+
}
|
|
197
281
|
export function noteRecordsFromEvents(events) {
|
|
198
282
|
const records = new Map();
|
|
199
283
|
for (const event of events) {
|
|
@@ -234,6 +318,13 @@ export function noteRecordsFromEvents(events) {
|
|
|
234
318
|
}
|
|
235
319
|
return [...records.values()].sort((a, b) => b.event.createdAt.localeCompare(a.event.createdAt));
|
|
236
320
|
}
|
|
321
|
+
/**
|
|
322
|
+
* Builds private Todo records.
|
|
323
|
+
*
|
|
324
|
+
* Deliberately simpler than the Task reducer: there is no acceptance state and
|
|
325
|
+
* no response history, because there is nobody to negotiate with. A Todo goes
|
|
326
|
+
* open → completed / canceled / archived, and back to open on reopen.
|
|
327
|
+
*/
|
|
237
328
|
export function todoRecordsFromEvents(events) {
|
|
238
329
|
const records = new Map();
|
|
239
330
|
for (const event of events) {
|
|
@@ -242,14 +333,13 @@ export function todoRecordsFromEvents(events) {
|
|
|
242
333
|
event,
|
|
243
334
|
current: {
|
|
244
335
|
title: event.title,
|
|
245
|
-
...(event.
|
|
336
|
+
...(event.details !== undefined ? { details: event.details } : {}),
|
|
246
337
|
priority: event.priority,
|
|
247
338
|
...(event.due ? { due: event.due } : {}),
|
|
248
339
|
tags: event.tags ?? [],
|
|
249
|
-
visibility: event.visibility,
|
|
250
340
|
version: event.aggregateVersion,
|
|
251
341
|
},
|
|
252
|
-
status:
|
|
342
|
+
status: 'open',
|
|
253
343
|
});
|
|
254
344
|
}
|
|
255
345
|
else if (event.type === 'todo.updated') {
|
|
@@ -258,25 +348,26 @@ export function todoRecordsFromEvents(events) {
|
|
|
258
348
|
record.update = event;
|
|
259
349
|
record.current = {
|
|
260
350
|
title: event.title,
|
|
261
|
-
...(event.
|
|
351
|
+
...(event.details !== undefined ? { details: event.details } : {}),
|
|
262
352
|
priority: event.priority,
|
|
263
353
|
...(event.due ? { due: event.due } : {}),
|
|
264
354
|
tags: event.tags ?? [],
|
|
265
|
-
visibility: event.visibility,
|
|
266
355
|
version: event.aggregateVersion,
|
|
267
356
|
};
|
|
268
357
|
}
|
|
269
358
|
}
|
|
270
|
-
else if (event.type === 'todo.
|
|
359
|
+
else if (event.type === 'todo.reopened') {
|
|
271
360
|
const record = records.get(event.todoId);
|
|
272
361
|
if (record) {
|
|
362
|
+
record.reopened = event;
|
|
273
363
|
record.current.version = event.aggregateVersion;
|
|
274
364
|
record.status = 'open';
|
|
365
|
+
delete record.terminal;
|
|
275
366
|
}
|
|
276
367
|
}
|
|
277
368
|
else if (event.type === 'todo.completed' ||
|
|
278
|
-
event.type === 'todo.
|
|
279
|
-
event.type === 'todo.
|
|
369
|
+
event.type === 'todo.canceled' ||
|
|
370
|
+
event.type === 'todo.archived') {
|
|
280
371
|
const record = records.get(event.todoId);
|
|
281
372
|
if (record) {
|
|
282
373
|
record.terminal = event;
|
|
@@ -284,13 +375,86 @@ export function todoRecordsFromEvents(events) {
|
|
|
284
375
|
record.status =
|
|
285
376
|
event.type === 'todo.completed'
|
|
286
377
|
? 'completed'
|
|
287
|
-
: event.type === 'todo.
|
|
378
|
+
: event.type === 'todo.canceled'
|
|
379
|
+
? 'canceled'
|
|
380
|
+
: 'archived';
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return [...records.values()];
|
|
385
|
+
}
|
|
386
|
+
export function taskRecordsFromEvents(events) {
|
|
387
|
+
const records = new Map();
|
|
388
|
+
for (const event of events) {
|
|
389
|
+
if (event.type === 'task.created') {
|
|
390
|
+
records.set(event.id, {
|
|
391
|
+
event,
|
|
392
|
+
current: {
|
|
393
|
+
title: event.title,
|
|
394
|
+
...(event.description !== undefined ? { description: event.description } : {}),
|
|
395
|
+
priority: event.priority,
|
|
396
|
+
...(event.due ? { due: event.due } : {}),
|
|
397
|
+
tags: event.tags ?? [],
|
|
398
|
+
visibility: event.visibility,
|
|
399
|
+
version: event.aggregateVersion,
|
|
400
|
+
},
|
|
401
|
+
status: event.requiresAcceptance ? 'assigned' : 'open',
|
|
402
|
+
responses: [],
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
else if (event.type === 'task.updated') {
|
|
406
|
+
const record = records.get(event.taskId);
|
|
407
|
+
if (record) {
|
|
408
|
+
record.update = event;
|
|
409
|
+
record.current = {
|
|
410
|
+
title: event.title,
|
|
411
|
+
...(event.description !== undefined ? { description: event.description } : {}),
|
|
412
|
+
priority: event.priority,
|
|
413
|
+
...(event.due ? { due: event.due } : {}),
|
|
414
|
+
tags: event.tags ?? [],
|
|
415
|
+
visibility: event.visibility,
|
|
416
|
+
version: event.aggregateVersion,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
else if (event.type === 'task.accepted') {
|
|
421
|
+
const record = records.get(event.taskId);
|
|
422
|
+
if (record) {
|
|
423
|
+
record.current.version = event.aggregateVersion;
|
|
424
|
+
record.status = 'open';
|
|
425
|
+
record.responses.push({
|
|
426
|
+
kind: 'accepted',
|
|
427
|
+
...(event.response !== undefined ? { response: event.response } : {}),
|
|
428
|
+
actor: event.actor,
|
|
429
|
+
at: event.createdAt,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
else if (event.type === 'task.completed' ||
|
|
434
|
+
event.type === 'task.rejected' ||
|
|
435
|
+
event.type === 'task.canceled') {
|
|
436
|
+
const record = records.get(event.taskId);
|
|
437
|
+
if (record) {
|
|
438
|
+
record.terminal = event;
|
|
439
|
+
record.current.version = event.aggregateVersion;
|
|
440
|
+
if (event.type === 'task.rejected') {
|
|
441
|
+
record.responses.push({
|
|
442
|
+
kind: 'rejected',
|
|
443
|
+
response: event.response,
|
|
444
|
+
actor: event.actor,
|
|
445
|
+
at: event.createdAt,
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
record.status =
|
|
449
|
+
event.type === 'task.completed'
|
|
450
|
+
? 'completed'
|
|
451
|
+
: event.type === 'task.rejected'
|
|
288
452
|
? 'rejected'
|
|
289
453
|
: 'canceled';
|
|
290
454
|
}
|
|
291
455
|
}
|
|
292
|
-
else if (event.type === '
|
|
293
|
-
const record = records.get(event.
|
|
456
|
+
else if (event.type === 'task.reopened') {
|
|
457
|
+
const record = records.get(event.taskId);
|
|
294
458
|
if (record) {
|
|
295
459
|
record.reopened = event;
|
|
296
460
|
record.current.version = event.aggregateVersion;
|
|
@@ -306,7 +470,7 @@ export function renderMarkdownExport(events) {
|
|
|
306
470
|
...recordsFromEvents(events).map((record) => `## Kudos: ${escapeMarkdown(record.event.title)}\n\n${escapeMarkdown(record.event.reason)}\n\nStatus: ${record.revocationStatus === 'revoked' ? 'Revoked' : record.status}\n\nID: \`${record.event.id}\``),
|
|
307
471
|
...memoRecordsFromEvents(events).map((record) => `## Memo: ${escapeMarkdown(record.event.subject)}\n\n${escapeMarkdown(record.event.body)}\n\nStatus: ${record.status}\n\nID: \`${record.event.id}\``),
|
|
308
472
|
...noteRecordsFromEvents(events).map((record) => `## Note: ${escapeMarkdown(record.current.title)}\n\n${escapeMarkdown(record.current.body)}\n\nStatus: ${record.status}; version ${record.current.version}\n\nID: \`${record.event.id}\``),
|
|
309
|
-
...
|
|
473
|
+
...taskRecordsFromEvents(events).map((record) => `## Task: ${escapeMarkdown(record.current.title)}\n\n${record.current.description ? `${escapeMarkdown(record.current.description)}\n\n` : ''}Status: ${record.status}; priority ${record.current.priority}\n\nID: \`${record.event.id}\``),
|
|
310
474
|
];
|
|
311
475
|
return `${sections.join('\n\n')}\n`;
|
|
312
476
|
}
|
|
@@ -326,7 +490,7 @@ export class ProjectionManager {
|
|
|
326
490
|
const records = recordsFromEvents(events);
|
|
327
491
|
const memos = memoRecordsFromEvents(events);
|
|
328
492
|
const notes = noteRecordsFromEvents(events);
|
|
329
|
-
const
|
|
493
|
+
const tasks = taskRecordsFromEvents(events);
|
|
330
494
|
const rebuiltAt = events.at(-1)?.createdAt ?? new Date(0).toISOString();
|
|
331
495
|
const generated = [];
|
|
332
496
|
for (const profile of profiles) {
|
|
@@ -339,12 +503,12 @@ export class ProjectionManager {
|
|
|
339
503
|
const inboxDirectory = join(agentDirectory, 'inbox');
|
|
340
504
|
const kudosInboxDirectory = join(inboxDirectory, 'kudos');
|
|
341
505
|
const memoInboxDirectory = join(inboxDirectory, 'memos');
|
|
342
|
-
const
|
|
506
|
+
const taskInboxDirectory = join(inboxDirectory, 'tasks');
|
|
343
507
|
assertNoSymlinkEscape(this.storage.home, inboxDirectory);
|
|
344
508
|
ensureDirectory(inboxDirectory);
|
|
345
509
|
ensureDirectory(kudosInboxDirectory);
|
|
346
510
|
ensureDirectory(memoInboxDirectory);
|
|
347
|
-
ensureDirectory(
|
|
511
|
+
ensureDirectory(taskInboxDirectory);
|
|
348
512
|
atomicWriteDerivedFile(profilePath, `${JSON.stringify({ _generatedBy: 'synomem', ...profile }, null, 2)}\n`);
|
|
349
513
|
generated.push(manifestPath(this.storage.home, profilePath));
|
|
350
514
|
if (!existsSync(notesPath)) {
|
|
@@ -361,11 +525,11 @@ export class ProjectionManager {
|
|
|
361
525
|
atomicWriteDerivedFile(memoryPath, memoryMarkdown(profile, agentNotes, rebuiltAt));
|
|
362
526
|
generated.push(manifestPath(this.storage.home, memoryPath));
|
|
363
527
|
}
|
|
364
|
-
const
|
|
365
|
-
if (this.storage.config.projection.
|
|
366
|
-
const
|
|
367
|
-
atomicWriteDerivedFile(
|
|
368
|
-
generated.push(manifestPath(this.storage.home,
|
|
528
|
+
const agentTasks = tasks.filter((record) => record.event.assigneeAgentId === profile.id);
|
|
529
|
+
if (this.storage.config.projection.writeTasksMarkdown) {
|
|
530
|
+
const tasksPath = join(agentDirectory, 'TASKS.md');
|
|
531
|
+
atomicWriteDerivedFile(tasksPath, tasksMarkdown(profile, agentTasks, rebuiltAt));
|
|
532
|
+
generated.push(manifestPath(this.storage.home, tasksPath));
|
|
369
533
|
}
|
|
370
534
|
if (this.storage.config.projection.writeInboxEntries) {
|
|
371
535
|
for (const record of agentRecords.filter((item) => item.status === 'unacknowledged' && item.revocationStatus === 'active')) {
|
|
@@ -378,9 +542,9 @@ export class ProjectionManager {
|
|
|
378
542
|
atomicWriteDerivedFile(inboxPath, memoInboxMarkdown(record));
|
|
379
543
|
generated.push(manifestPath(this.storage.home, inboxPath));
|
|
380
544
|
}
|
|
381
|
-
for (const record of
|
|
382
|
-
const inboxPath = join(
|
|
383
|
-
atomicWriteDerivedFile(inboxPath,
|
|
545
|
+
for (const record of agentTasks.filter((item) => item.status === 'open' || item.status === 'assigned')) {
|
|
546
|
+
const inboxPath = join(taskInboxDirectory, `${record.event.id}.md`);
|
|
547
|
+
atomicWriteDerivedFile(inboxPath, taskInboxMarkdown(record));
|
|
384
548
|
generated.push(manifestPath(this.storage.home, inboxPath));
|
|
385
549
|
}
|
|
386
550
|
}
|
|
@@ -413,20 +577,20 @@ export class ProjectionManager {
|
|
|
413
577
|
const records = recordsFromEvents(events).filter((record) => record.event.recipientAgentId === profile.id);
|
|
414
578
|
const memos = memoRecordsFromEvents(events).filter((record) => record.event.recipientAgentId === profile.id);
|
|
415
579
|
const notes = noteRecordsFromEvents(events).filter((record) => record.event.ownerAgentId === profile.id);
|
|
416
|
-
const
|
|
580
|
+
const tasks = taskRecordsFromEvents(events).filter((record) => record.event.assigneeAgentId === profile.id);
|
|
417
581
|
const rebuiltAt = events.at(-1)?.createdAt ?? new Date(0).toISOString();
|
|
418
582
|
const agentDirectory = join(this.storage.home, profile.id);
|
|
419
583
|
const inboxDirectory = join(agentDirectory, 'inbox');
|
|
420
584
|
const kudosInboxDirectory = join(inboxDirectory, 'kudos');
|
|
421
585
|
const memoInboxDirectory = join(inboxDirectory, 'memos');
|
|
422
|
-
const
|
|
586
|
+
const taskInboxDirectory = join(inboxDirectory, 'tasks');
|
|
423
587
|
assertNoSymlinkEscape(this.storage.home, agentDirectory);
|
|
424
588
|
assertNoSymlinkEscape(this.storage.home, inboxDirectory);
|
|
425
589
|
ensureDirectory(agentDirectory);
|
|
426
590
|
ensureDirectory(inboxDirectory);
|
|
427
591
|
ensureDirectory(kudosInboxDirectory);
|
|
428
592
|
ensureDirectory(memoInboxDirectory);
|
|
429
|
-
ensureDirectory(
|
|
593
|
+
ensureDirectory(taskInboxDirectory);
|
|
430
594
|
const generated = [];
|
|
431
595
|
const profilePath = join(agentDirectory, 'profile.json');
|
|
432
596
|
atomicWriteDerivedFile(profilePath, `${JSON.stringify({ _generatedBy: 'synomem', ...profile }, null, 2)}\n`);
|
|
@@ -445,10 +609,10 @@ export class ProjectionManager {
|
|
|
445
609
|
atomicWriteDerivedFile(memoryPath, memoryMarkdown(profile, notes, rebuiltAt));
|
|
446
610
|
generated.push(manifestPath(this.storage.home, memoryPath));
|
|
447
611
|
}
|
|
448
|
-
if (this.storage.config.projection.
|
|
449
|
-
const
|
|
450
|
-
atomicWriteDerivedFile(
|
|
451
|
-
generated.push(manifestPath(this.storage.home,
|
|
612
|
+
if (this.storage.config.projection.writeTasksMarkdown) {
|
|
613
|
+
const tasksPath = join(agentDirectory, 'TASKS.md');
|
|
614
|
+
atomicWriteDerivedFile(tasksPath, tasksMarkdown(profile, tasks, rebuiltAt));
|
|
615
|
+
generated.push(manifestPath(this.storage.home, tasksPath));
|
|
452
616
|
}
|
|
453
617
|
if (this.storage.config.projection.writeInboxEntries) {
|
|
454
618
|
for (const record of records.filter((item) => item.status === 'unacknowledged' && item.revocationStatus === 'active')) {
|
|
@@ -465,11 +629,11 @@ export class ProjectionManager {
|
|
|
465
629
|
atomicWriteDerivedFile(inboxPath, memoInboxMarkdown(record));
|
|
466
630
|
generated.push(relativePath);
|
|
467
631
|
}
|
|
468
|
-
for (const record of
|
|
469
|
-
const inboxPath = join(
|
|
632
|
+
for (const record of tasks.filter((item) => item.status === 'open' || item.status === 'assigned')) {
|
|
633
|
+
const inboxPath = join(taskInboxDirectory, `${record.event.id}.md`);
|
|
470
634
|
const relativePath = manifestPath(this.storage.home, inboxPath);
|
|
471
635
|
if (!existsSync(inboxPath))
|
|
472
|
-
atomicWriteDerivedFile(inboxPath,
|
|
636
|
+
atomicWriteDerivedFile(inboxPath, taskInboxMarkdown(record));
|
|
473
637
|
generated.push(relativePath);
|
|
474
638
|
}
|
|
475
639
|
}
|
|
@@ -500,7 +664,7 @@ export class ProjectionManager {
|
|
|
500
664
|
const events = this.storage.getReadableEvents();
|
|
501
665
|
const records = recordsFromEvents(events);
|
|
502
666
|
const memos = memoRecordsFromEvents(events);
|
|
503
|
-
const
|
|
667
|
+
const tasks = taskRecordsFromEvents(events);
|
|
504
668
|
const paths = [];
|
|
505
669
|
for (const profile of profiles) {
|
|
506
670
|
paths.push(`${profile.id}/profile.json`);
|
|
@@ -508,8 +672,8 @@ export class ProjectionManager {
|
|
|
508
672
|
paths.push(`${profile.id}/WINS.md`);
|
|
509
673
|
if (this.storage.config.projection.writeMemoryMarkdown)
|
|
510
674
|
paths.push(`${profile.id}/MEMORY.md`);
|
|
511
|
-
if (this.storage.config.projection.
|
|
512
|
-
paths.push(`${profile.id}/
|
|
675
|
+
if (this.storage.config.projection.writeTasksMarkdown)
|
|
676
|
+
paths.push(`${profile.id}/TASKS.md`);
|
|
513
677
|
if (this.storage.config.projection.writeInboxEntries) {
|
|
514
678
|
for (const record of records.filter((item) => item.event.recipientAgentId === profile.id &&
|
|
515
679
|
item.status === 'unacknowledged' &&
|
|
@@ -519,9 +683,9 @@ export class ProjectionManager {
|
|
|
519
683
|
for (const record of memos.filter((item) => item.event.recipientAgentId === profile.id && item.status === 'unread')) {
|
|
520
684
|
paths.push(`${profile.id}/inbox/memos/${record.event.id}.md`);
|
|
521
685
|
}
|
|
522
|
-
for (const record of
|
|
686
|
+
for (const record of tasks.filter((item) => item.event.assigneeAgentId === profile.id &&
|
|
523
687
|
(item.status === 'open' || item.status === 'assigned'))) {
|
|
524
|
-
paths.push(`${profile.id}/inbox/
|
|
688
|
+
paths.push(`${profile.id}/inbox/tasks/${record.event.id}.md`);
|
|
525
689
|
}
|
|
526
690
|
}
|
|
527
691
|
}
|