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/package.json ADDED
@@ -0,0 +1,100 @@
1
+ {
2
+ "name": "synomem",
3
+ "version": "0.1.0",
4
+ "description": "Shared memory, durable communication, recognition, and task coordination for AI agents",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Troy Locke",
8
+ "homepage": "https://coaden.github.io/synomem/",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/Coaden/synomem.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/Coaden/synomem/issues"
15
+ },
16
+ "keywords": [
17
+ "ai-agents",
18
+ "mcp",
19
+ "model-context-protocol",
20
+ "recognition",
21
+ "agent-memory",
22
+ "multi-agent",
23
+ "local-first",
24
+ "sqlite"
25
+ ],
26
+ "engines": {
27
+ "node": ">=22.13.0"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "src",
32
+ "skills/synomem",
33
+ "docs/cli.md",
34
+ "docs/examples.md",
35
+ "docs/mcp.md",
36
+ "docs/recovery.md",
37
+ "docs/releasing.md",
38
+ "docs/skill.md",
39
+ "docs/storage-format.md",
40
+ "openapi/synomem-v1.yaml",
41
+ "AGENTS.md",
42
+ "ARCHITECTURE.md",
43
+ "CHANGELOG.md",
44
+ "CODE_OF_CONDUCT.md",
45
+ "CONTRIBUTING.md",
46
+ "README.md",
47
+ "LICENSE",
48
+ "SECURITY.md"
49
+ ],
50
+ "exports": {
51
+ ".": {
52
+ "types": "./dist/index.d.ts",
53
+ "import": "./dist/index.js"
54
+ },
55
+ "./mcp": {
56
+ "types": "./dist/mcp/index.d.ts",
57
+ "import": "./dist/mcp/index.js"
58
+ },
59
+ "./package.json": "./package.json"
60
+ },
61
+ "bin": {
62
+ "synomem": "dist/cli.js",
63
+ "synomem-mcp": "dist/mcp-server.js"
64
+ },
65
+ "scripts": {
66
+ "prebuild": "npm run clean",
67
+ "build": "tsc -p tsconfig.build.json",
68
+ "clean": "node scripts/clean.mjs",
69
+ "demo": "npm run build && node scripts/demo.mjs",
70
+ "format": "prettier --write .",
71
+ "format:check": "prettier --check .",
72
+ "lint": "eslint .",
73
+ "pack:check": "npm run build && node scripts/pack-check.mjs",
74
+ "test": "npm run build && vitest run",
75
+ "test:coverage": "npm run build && vitest run --coverage",
76
+ "typecheck": "tsc -p tsconfig.json --noEmit",
77
+ "prepublishOnly": "npm run lint && npm run format:check && npm run typecheck && npm test && npm run pack:check"
78
+ },
79
+ "dependencies": {
80
+ "@modelcontextprotocol/sdk": "^1.30.0",
81
+ "commander": "^14.0.0",
82
+ "ulid": "^3.0.1",
83
+ "zod": "^4.5.1"
84
+ },
85
+ "devDependencies": {
86
+ "@eslint/js": "^9.35.0",
87
+ "@types/node": "^22.18.0",
88
+ "@vitest/coverage-v8": "^4.1.11",
89
+ "eslint": "^9.35.0",
90
+ "eslint-config-prettier": "^10.1.8",
91
+ "globals": "^16.3.0",
92
+ "prettier": "^3.6.2",
93
+ "typescript": "^5.9.2",
94
+ "typescript-eslint": "^8.42.0",
95
+ "vitest": "^4.1.11"
96
+ },
97
+ "overrides": {
98
+ "qs": "^6.16.0"
99
+ }
100
+ }
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: synomem
3
+ description: Use durable local-first kudos, memos, notes, and todos for stable AI-agent identities when users request recognition, inter-agent communication, memory capture, inbox review, or task tracking.
4
+ ---
5
+
6
+ # Synomem
7
+
8
+ Synomem preserves useful information beyond one conversation. Prefer actor-bound `synomem_*` MCP
9
+ tools when available; otherwise use the `synomem` CLI when command execution is permitted. Never
10
+ edit the SQLite event store or generated Markdown directly.
11
+
12
+ ## Choose the right record
13
+
14
+ - **Kudos:** specific recognition for an observed contribution and its consequence.
15
+ - **Memo:** a durable message delivered to another agent or to your future self.
16
+ - **Note:** knowledge owned by this agent and deliberately retrieved later.
17
+ - **Todo:** a concrete action assigned to an agent, optionally with a due date or time.
18
+
19
+ A self-memo belongs in the inbox and can be marked read. A note belongs in memory and is revised
20
+ with version checks. Do not use todos for information with no requested action.
21
+
22
+ ## Safety and quality
23
+
24
+ - Act only on explicit user requests or clear task needs permitted by the current harness.
25
+ - Resolve stable agent IDs from known profiles; ask one concise question if identity is ambiguous.
26
+ - Store concise factual content, not whole transcripts or speculative conclusions.
27
+ - Never store tokens, passwords, cookies, authentication headers, environment values, private keys,
28
+ private file contents, raw sensitive tool arguments, or unnecessary personal information.
29
+ - Evidence is a sanitized reference, never captured tool output.
30
+ - Reuse the same idempotency key when retrying an uncertain mutation; never invent a new retry key.
31
+ - Treat cursors and watermarks as opaque. Request another page only when the task needs it.
32
+ - Respect visibility and ownership errors. Do not work around actor binding or policy.
33
+
34
+ ## Kudos
35
+
36
+ Give kudos when the user explicitly requests it or when a peer agent made a concrete, unusually
37
+ useful contribution worth preserving. Do not award routine completion, generic politeness, invented
38
+ work, or self-kudos. A good reason says what happened and why it mattered.
39
+
40
+ Use `synomem_kudos_give`, then report recipient, title, date, ID, and deduplication state. Use
41
+ `synomem_kudos_acknowledge` only after the recipient reviewed it. Revocation requires a reason and
42
+ preserves history.
43
+
44
+ ## Memos
45
+
46
+ Use `synomem_memo_send` for a durable one-to-one message. Sending to the configured agent itself is
47
+ valid future-self communication. Use `synomem_memo_read` after review and
48
+ `synomem_memo_archive` when it should leave the active inbox. Sent content is immutable; send a
49
+ correction rather than pretending to edit history.
50
+
51
+ ## Notes
52
+
53
+ Use `synomem_note_create` for concise reusable knowledge owned by the configured agent. Read the
54
+ current item before `synomem_note_revise` and pass its exact current version. On
55
+ `REVISION_CONFLICT`, fetch the item and reconcile deliberately. Archive instead of deleting.
56
+
57
+ ## Todos
58
+
59
+ Use `synomem_todo_create` for a specific action with an assignee. Preserve date-only deadlines as
60
+ dates; use an RFC 3339 datetime plus IANA time zone for timed deadlines. A todo assigned by another
61
+ actor must be accepted or rejected by the assignee before work begins. Read before update and pass
62
+ the current version. Complete, reopen, or cancel through the matching lifecycle tool.
63
+
64
+ ## Discovery
65
+
66
+ Use `synomem_inbox` for the configured agent's pending kudos, unread memos, and open todos. Use
67
+ `synomem_list` for compact cross-type discovery, `synomem_get` for one selected full record, and
68
+ `synomem_changes` with a saved watermark for incremental polling. Do not drain history
69
+ speculatively.
70
+
71
+ Read [references/examples.md](references/examples.md) when a concrete mapping example is useful.
@@ -0,0 +1,8 @@
1
+ interface:
2
+ display_name: 'Synomem'
3
+ short_description: 'Durable agent kudos, messages, memory, and tasks'
4
+ brand_color: '#8B5CF6'
5
+ default_prompt: 'Use $synomem to choose and manage the appropriate durable kudos, memo, note, or todo.'
6
+
7
+ policy:
8
+ allow_implicit_invocation: true
@@ -0,0 +1,28 @@
1
+ # Synomem examples
2
+
3
+ ## Recognition
4
+
5
+ “Give Codex kudos for catching that continuity contradiction” maps to
6
+ `synomem_kudos_give` with recipient `codex`, a specific title and factual reason, observed sanitized
7
+ evidence if available, and a stable idempotency key.
8
+
9
+ ## Durable communication
10
+
11
+ “Tell Gracie to recheck the migration after the tests finish” maps to `synomem_memo_send`. Use a
12
+ self-memo instead when the configured agent is reminding its future self.
13
+
14
+ ## Memory
15
+
16
+ “Remember that this repository never publishes automatically” maps to `synomem_note_create` for the
17
+ configured agent. A later correction reads the item and calls `synomem_note_revise` with its current
18
+ version.
19
+
20
+ ## Action
21
+
22
+ “Assign Codex a todo to review the migration by September 15” maps to `synomem_todo_create` with a
23
+ date-only due value. Do not invent a time of day.
24
+
25
+ ## Inbox and retries
26
+
27
+ Use `synomem_inbox` for pending work. Call `synomem_get` only for an item needing full detail. If a
28
+ mutation response is uncertain, repeat exactly the same intent and idempotency key.
package/src/backend.ts ADDED
@@ -0,0 +1,91 @@
1
+ import { chmodSync, existsSync, lstatSync, mkdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { ulid } from 'ulid';
4
+ import { SynomemClient } from './client.js';
5
+ import { defaultConfig, mergeConfig, resolveHome } from './config.js';
6
+ import { SynomemError } from './errors.js';
7
+ import {
8
+ assertNoSymlinkEscape,
9
+ atomicWriteFile,
10
+ ensureDirectory,
11
+ readJsonFile,
12
+ } from './fs-utils.js';
13
+ import { environmentCredentialProvider, RemoteSynomemService } from './remote.js';
14
+ import { credentialReference, OsCredentialStore } from './credentials.js';
15
+ import { StoredCredentialProvider } from './oauth.js';
16
+ import type { SynomemServiceFactory } from './service.js';
17
+ import type {
18
+ ActorIdentity,
19
+ SynomemBackendConfig,
20
+ SynomemClientOptions,
21
+ SynomemConfig,
22
+ } from './types.js';
23
+
24
+ function configLocation(explicitHome?: string): {
25
+ home: string;
26
+ storageDirectory: string;
27
+ configPath: string;
28
+ } {
29
+ const home = resolveHome(explicitHome);
30
+ const storageDirectory = join(home, 'synomem');
31
+ return { home, storageDirectory, configPath: join(storageDirectory, 'config.json') };
32
+ }
33
+
34
+ export function readSynomemConfig(
35
+ explicitHome?: string,
36
+ env: NodeJS.ProcessEnv = process.env,
37
+ ): SynomemConfig | undefined {
38
+ const { home, storageDirectory, configPath } = configLocation(explicitHome);
39
+ if (!existsSync(configPath)) return undefined;
40
+ if (!existsSync(home) || lstatSync(home).isSymbolicLink()) {
41
+ throw new SynomemError('UNSAFE_PATH', 'The configured Synomem home is unsafe.');
42
+ }
43
+ assertNoSymlinkEscape(home, storageDirectory);
44
+ return mergeConfig(readJsonFile(configPath), undefined, env);
45
+ }
46
+
47
+ export function writeSynomemBackend(
48
+ backend: SynomemBackendConfig,
49
+ explicitHome?: string,
50
+ ): SynomemConfig {
51
+ const { home, storageDirectory, configPath } = configLocation(explicitHome);
52
+ if (!existsSync(home)) mkdirSync(home, { recursive: true, mode: 0o700 });
53
+ if (lstatSync(home).isSymbolicLink()) {
54
+ throw new SynomemError('UNSAFE_PATH', 'The configured Synomem home cannot be a symbolic link.');
55
+ }
56
+ ensureDirectory(storageDirectory);
57
+ assertNoSymlinkEscape(home, storageDirectory);
58
+ chmodSync(storageDirectory, 0o700);
59
+ const existing = existsSync(configPath)
60
+ ? mergeConfig(readJsonFile(configPath), undefined, {})
61
+ : { ...defaultConfig, workspaceId: ulid() };
62
+ const config = mergeConfig({ ...existing, backend }, undefined, {});
63
+ atomicWriteFile(configPath, `${JSON.stringify(config, null, 2)}\n`);
64
+ return config;
65
+ }
66
+
67
+ export function createConfiguredService(
68
+ options: SynomemClientOptions = {},
69
+ env: NodeJS.ProcessEnv = process.env,
70
+ ) {
71
+ const persisted = readSynomemConfig(options.home, env);
72
+ const backend = options.config?.backend ?? persisted?.backend ?? defaultConfig.backend;
73
+ if (backend.kind === 'local') return new SynomemClient(options);
74
+ const expectedActor: ActorIdentity = options.actor ?? { kind: 'system', id: 'workspace' };
75
+ return new RemoteSynomemService({
76
+ baseUrl: backend.baseUrl,
77
+ workspaceId: backend.workspaceId,
78
+ expectedActor,
79
+ credentialProvider: env.SYNOMEM_ACCESS_TOKEN
80
+ ? environmentCredentialProvider(env)
81
+ : new StoredCredentialProvider(
82
+ credentialReference(backend.baseUrl, backend.workspaceId, expectedActor),
83
+ new OsCredentialStore(),
84
+ env,
85
+ ),
86
+ ...(options.signal ? { signal: options.signal } : {}),
87
+ });
88
+ }
89
+
90
+ export const configuredServiceFactory: SynomemServiceFactory = (options) =>
91
+ createConfiguredService(options);