@jbarket/loomai 0.5.0 → 0.6.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 (94) hide show
  1. package/CHANGELOG.md +114 -1
  2. package/README.md +97 -0
  3. package/dist/backends/dedup.d.ts +23 -0
  4. package/dist/backends/dedup.js +100 -0
  5. package/dist/backends/embedding-runtime.js +38 -9
  6. package/dist/backends/episodes.js +1 -1
  7. package/dist/backends/fastembed.js +30 -4
  8. package/dist/backends/migrations.js +200 -0
  9. package/dist/backends/mmr.d.ts +8 -0
  10. package/dist/backends/mmr.js +18 -4
  11. package/dist/backends/people.d.ts +192 -0
  12. package/dist/backends/people.js +824 -0
  13. package/dist/backends/proposals.js +1 -1
  14. package/dist/backends/recorder-health.d.ts +34 -0
  15. package/dist/backends/recorder-health.js +117 -0
  16. package/dist/backends/salience.d.ts +39 -0
  17. package/dist/backends/salience.js +52 -4
  18. package/dist/backends/scope.d.ts +56 -0
  19. package/dist/backends/scope.js +75 -0
  20. package/dist/backends/sqlite-knowledge.d.ts +24 -4
  21. package/dist/backends/sqlite-knowledge.js +253 -91
  22. package/dist/backends/sqlite-vec.d.ts +32 -1
  23. package/dist/backends/sqlite-vec.js +394 -75
  24. package/dist/backends/types.d.ts +203 -3
  25. package/dist/categories.d.ts +2 -0
  26. package/dist/categories.js +2 -0
  27. package/dist/cli/args.d.ts +16 -1
  28. package/dist/cli/args.js +45 -1
  29. package/dist/cli/index.d.ts +7 -0
  30. package/dist/cli/index.js +57 -3
  31. package/dist/cli/knowledge.js +4 -1
  32. package/dist/cli/memory.js +2 -2
  33. package/dist/cli/person.d.ts +2 -0
  34. package/dist/cli/person.js +116 -0
  35. package/dist/cli/recall.js +2 -1
  36. package/dist/cli/remember.js +10 -0
  37. package/dist/cli/remote.d.ts +36 -0
  38. package/dist/cli/remote.js +173 -0
  39. package/dist/cli/serve.d.ts +7 -0
  40. package/dist/cli/serve.js +94 -5
  41. package/dist/cli/subcommands.d.ts +1 -1
  42. package/dist/cli/subcommands.js +1 -1
  43. package/dist/cli/update.js +30 -11
  44. package/dist/clients.js +7 -4
  45. package/dist/config.d.ts +14 -0
  46. package/dist/config.js +25 -2
  47. package/dist/index.js +22 -7
  48. package/dist/injection/harnesses.js +9 -3
  49. package/dist/install/harnesses.js +7 -1
  50. package/dist/server.d.ts +8 -1
  51. package/dist/server.js +461 -374
  52. package/dist/tools/dossier.js +5 -1
  53. package/dist/tools/eurorack-import.js +3 -0
  54. package/dist/tools/identity.d.ts +2 -0
  55. package/dist/tools/identity.js +35 -2
  56. package/dist/tools/knowledge-history.d.ts +6 -1
  57. package/dist/tools/knowledge-history.js +26 -8
  58. package/dist/tools/knowledge-maintain.d.ts +5 -0
  59. package/dist/tools/knowledge-maintain.js +92 -5
  60. package/dist/tools/knowledge-recall.d.ts +7 -1
  61. package/dist/tools/knowledge-recall.js +53 -3
  62. package/dist/tools/knowledge-verify.d.ts +11 -1
  63. package/dist/tools/knowledge-verify.js +17 -4
  64. package/dist/tools/knowledge-write.d.ts +8 -1
  65. package/dist/tools/knowledge-write.js +27 -9
  66. package/dist/tools/memory-history.d.ts +5 -0
  67. package/dist/tools/memory-history.js +52 -0
  68. package/dist/tools/memory-list.js +2 -1
  69. package/dist/tools/memory-merge.d.ts +2 -0
  70. package/dist/tools/memory-merge.js +40 -0
  71. package/dist/tools/memory-revision-restore.d.ts +5 -0
  72. package/dist/tools/memory-revision-restore.js +33 -0
  73. package/dist/tools/person.d.ts +32 -0
  74. package/dist/tools/person.js +111 -0
  75. package/dist/tools/prune.js +1 -1
  76. package/dist/tools/recall.js +7 -1
  77. package/dist/tools/remember.js +18 -2
  78. package/dist/tools/update.js +5 -1
  79. package/dist/transport/cli-endpoint.d.ts +13 -0
  80. package/dist/transport/cli-endpoint.js +97 -0
  81. package/dist/transport/guards.d.ts +14 -0
  82. package/dist/transport/guards.js +26 -0
  83. package/dist/transport/http-server.d.ts +28 -1
  84. package/dist/transport/http-server.js +156 -18
  85. package/dist/transport/knowledge-server.d.ts +11 -0
  86. package/dist/transport/knowledge-server.js +30 -0
  87. package/dist/transport/knowledge-tools.d.ts +50 -0
  88. package/dist/transport/knowledge-tools.js +290 -0
  89. package/dist/transport/mcp-app-hello.d.ts +1 -1
  90. package/dist/widgets/display-modes.html +62 -56
  91. package/dist/widgets/hello.html +62 -56
  92. package/dist/widgets/proposals.html +62 -56
  93. package/dist/widgets/top-of-mind.html +60 -54
  94. package/package.json +7 -5
package/dist/config.d.ts CHANGED
@@ -25,6 +25,20 @@ export declare function assertContextBootable(contextDir: string, defaultPath?:
25
25
  export declare function resolveRepoRoot(): string;
26
26
  export declare function resolveSqliteDbPath(contextDir: string): string;
27
27
  export declare function resolveKnowledgeDbPath(contextDir: string): string;
28
+ /**
29
+ * Resolve the acting identity's name for actor attribution (knowledge
30
+ * pages.author / page_revisions.actor / verifications.verifier). This is
31
+ * deliberately NOT a tool argument — an LLM-supplied free-text identity is
32
+ * spoofable (any caller could claim to be any identity), so it is derived
33
+ * server-side from the connection instead.
34
+ *
35
+ * LOOM_IDENTITY overrides explicitly (useful for the multi-identity
36
+ * knowledge service, where each bearer token maps to a known identity).
37
+ * Otherwise derived from the context dir's real path basename — this
38
+ * resolves symlink aliases (e.g. `~/.config/loom/default` -> the real
39
+ * `.../loom/art` dir) to the actual identity name rather than the alias.
40
+ */
41
+ export declare function resolveIdentityName(contextDir: string): string;
28
42
  /**
29
43
  * Local-only observation logs (e.g. `telemetry/recall.jsonl`). Lives inside
30
44
  * the context dir so it travels with the agent and never leaves the machine.
package/dist/config.js CHANGED
@@ -14,10 +14,10 @@
14
14
  * 2. --context-dir CLI argument
15
15
  * 3. ~/.config/loom/default (fallback)
16
16
  */
17
- import { resolve, dirname } from 'node:path';
17
+ import { resolve, dirname, basename } from 'node:path';
18
18
  import { homedir } from 'node:os';
19
19
  import { fileURLToPath } from 'node:url';
20
- import { existsSync, readFileSync, writeFileSync, statSync } from 'node:fs';
20
+ import { existsSync, readFileSync, writeFileSync, statSync, realpathSync } from 'node:fs';
21
21
  /**
22
22
  * The single canonical helper for the default context path. Every call
23
23
  * site that needs `~/.config/loom/default` must go through this —
@@ -70,6 +70,29 @@ export function resolveSqliteDbPath(contextDir) {
70
70
  export function resolveKnowledgeDbPath(contextDir) {
71
71
  return process.env.LOOM_KNOWLEDGE_DB_PATH ?? resolve(contextDir, 'knowledge.db');
72
72
  }
73
+ /**
74
+ * Resolve the acting identity's name for actor attribution (knowledge
75
+ * pages.author / page_revisions.actor / verifications.verifier). This is
76
+ * deliberately NOT a tool argument — an LLM-supplied free-text identity is
77
+ * spoofable (any caller could claim to be any identity), so it is derived
78
+ * server-side from the connection instead.
79
+ *
80
+ * LOOM_IDENTITY overrides explicitly (useful for the multi-identity
81
+ * knowledge service, where each bearer token maps to a known identity).
82
+ * Otherwise derived from the context dir's real path basename — this
83
+ * resolves symlink aliases (e.g. `~/.config/loom/default` -> the real
84
+ * `.../loom/art` dir) to the actual identity name rather than the alias.
85
+ */
86
+ export function resolveIdentityName(contextDir) {
87
+ if (process.env.LOOM_IDENTITY)
88
+ return process.env.LOOM_IDENTITY;
89
+ try {
90
+ return basename(realpathSync(contextDir));
91
+ }
92
+ catch {
93
+ return basename(contextDir);
94
+ }
95
+ }
73
96
  /**
74
97
  * Local-only observation logs (e.g. `telemetry/recall.jsonl`). Lives inside
75
98
  * the context dir so it travels with the agent and never leaves the machine.
package/dist/index.js CHANGED
@@ -2,10 +2,16 @@
2
2
  /**
3
3
  * Loom — CLI + stdio MCP entry point.
4
4
  *
5
- * When argv[2] is a known CLI subcommand or --help/--version, routes to
6
- * src/cli/index.ts. Otherwise (or if argv is empty / only flags), falls
7
- * through to the MCP stdio server so existing .mcp.json configs keep
8
- * working.
5
+ * When the first non-global token is a known CLI subcommand or
6
+ * --help/--version, routes to src/cli/index.ts. Otherwise (or if argv is
7
+ * empty / only flags), falls through to the MCP stdio server so existing
8
+ * .mcp.json configs keep working.
9
+ *
10
+ * "First non-global token", not argv[2]: --context-dir and friends are
11
+ * documented as global flags, so `loom --context-dir DIR knowledge write`
12
+ * has to reach the CLI. Routing on argv[2] alone sent it to the stdio
13
+ * server instead, which read an empty stdin and exited 0 — a write verb
14
+ * reporting success having never entered the write path (t-665).
9
15
  *
10
16
  * Configure via environment variables:
11
17
  * LOOM_CONTEXT_DIR — path to identity/memory directory (required)
@@ -16,13 +22,15 @@
16
22
  */
17
23
  import { realpathSync } from 'node:fs';
18
24
  import { fileURLToPath } from 'node:url';
19
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
25
+ import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
20
26
  import { createLoomServer } from './server.js';
21
27
  import { resolveContextDir } from './config.js';
22
28
  import { SUBCOMMANDS } from './cli/subcommands.js';
29
+ import { firstNonGlobalIndex } from './cli/args.js';
23
30
  const CLI_KEYWORDS = new Set(SUBCOMMANDS);
24
31
  function isCliInvocation(argv) {
25
- const first = argv[2];
32
+ const args = argv.slice(2);
33
+ const first = args[firstNonGlobalIndex(args)];
26
34
  if (first === undefined)
27
35
  return false;
28
36
  if (first === '--help' || first === '-h')
@@ -35,7 +43,14 @@ export { isCliInvocation };
35
43
  async function main() {
36
44
  if (isCliInvocation(process.argv)) {
37
45
  const { runCli } = await import('./cli/index.js');
38
- process.exit(await runCli(process.argv.slice(2)));
46
+ const code = await runCli(process.argv.slice(2));
47
+ // Drain stdout before exiting — without this, large outputs (e.g.
48
+ // `loom memory list --json` on a big store) truncate at 64 KiB when
49
+ // piped because process.exit() tears down the stream before the OS
50
+ // buffer drains (t-198). process.stdout.write('', cb) fires after all
51
+ // prior writes have been handed to the OS.
52
+ process.stdout.write('', () => process.exit(code));
53
+ return;
39
54
  }
40
55
  const contextDir = resolveContextDir();
41
56
  const { server } = createLoomServer({ contextDir });
@@ -2,9 +2,15 @@
2
2
  * Harness preset table for `loom inject`. Each entry names a target
3
3
  * harness, its canonical default path, and the MCP tool prefix to emit
4
4
  * in the injected instruction block.
5
+ *
6
+ * toolPrefix is NOT redeclared here — it's read from
7
+ * `src/install/harnesses.ts`'s INSTALL_TARGETS, which is the single
8
+ * source of truth for the prefix per harness (t-397: the two tables used
9
+ * to carry independent, disagreeing hardcoded values for codex).
5
10
  */
6
11
  import { homedir } from 'node:os';
7
12
  import { join } from 'node:path';
13
+ import { INSTALL_TARGETS } from '../install/harnesses.js';
8
14
  export const HARNESS_KEYS = [
9
15
  'claude-code',
10
16
  'codex',
@@ -15,19 +21,19 @@ export const HARNESSES = {
15
21
  key: 'claude-code',
16
22
  display: 'Claude Code',
17
23
  defaultPath: join(homedir(), '.claude', 'CLAUDE.md'),
18
- toolPrefix: 'mcp__loom__',
24
+ toolPrefix: INSTALL_TARGETS['claude-code'].toolPrefix,
19
25
  },
20
26
  'codex': {
21
27
  key: 'codex',
22
28
  display: 'Codex',
23
29
  defaultPath: join(homedir(), '.codex', 'AGENTS.md'),
24
- toolPrefix: 'mcp__loom__',
30
+ toolPrefix: INSTALL_TARGETS['codex'].toolPrefix,
25
31
  },
26
32
  'gemini-cli': {
27
33
  key: 'gemini-cli',
28
34
  display: 'Gemini CLI',
29
35
  defaultPath: join(homedir(), '.gemini', 'GEMINI.md'),
30
- toolPrefix: 'mcp__loom__',
36
+ toolPrefix: INSTALL_TARGETS['gemini-cli'].toolPrefix,
31
37
  },
32
38
  };
33
39
  export function isHarnessKey(s) {
@@ -27,7 +27,9 @@ export const INSTALL_TARGETS = {
27
27
  mcpConfigHint: '~/.codex/config.toml',
28
28
  invoke: 'use the loom-setup skill',
29
29
  restart: 'restart the Codex session',
30
- toolPrefix: 'mcp_loom_',
30
+ // mcp__loom__, not mcp_loom_: a live Codex session's own tool catalog
31
+ // names it mcp__loom__identity (t-397, verified 2026-09-05 by mark).
32
+ toolPrefix: 'mcp__loom__',
31
33
  },
32
34
  'gemini-cli': {
33
35
  key: 'gemini-cli',
@@ -36,6 +38,10 @@ export const INSTALL_TARGETS = {
36
38
  mcpConfigHint: '~/.gemini/settings.json',
37
39
  invoke: 'use the loom-setup skill',
38
40
  restart: 'exit and restart Gemini CLI',
41
+ // mcp_loom_, single underscores: gemini-cli joins server + '_' + tool
42
+ // and prepends 'mcp_' (MCP_QUALIFIED_NAME_SEPARATOR / generateValidName in
43
+ // packages/core/src/tools/mcp-tool.ts @ b25669d), so the model sees
44
+ // mcp_loom_identity (t-397, caught by mark in review of #122).
39
45
  toolPrefix: 'mcp_loom_',
40
46
  },
41
47
  'opencode': {
package/dist/server.d.ts CHANGED
@@ -1,6 +1,13 @@
1
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1
+ import { McpServer } from '@modelcontextprotocol/server';
2
2
  export interface LoomServerConfig {
3
3
  contextDir: string;
4
+ /**
5
+ * Optional path to the knowledge-gaps directory (e.g. `~/Art/.knowledge-gaps`).
6
+ * When set, zero-result knowledge_recall calls append a miss entry to
7
+ * `${gapsDir}/recall-miss.jsonl`, feeding the expansion engine's second
8
+ * signal channel. Set via LOOM_GAPS_DIR env var in production.
9
+ */
10
+ gapsDir?: string;
4
11
  }
5
12
  export interface LoomServerInstance {
6
13
  server: McpServer;