twining-mcp 1.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 (171) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +165 -0
  3. package/dist/config.d.ts +8 -0
  4. package/dist/config.d.ts.map +1 -0
  5. package/dist/config.js +78 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/dashboard/api-routes.d.ts +21 -0
  8. package/dist/dashboard/api-routes.d.ts.map +1 -0
  9. package/dist/dashboard/api-routes.js +516 -0
  10. package/dist/dashboard/api-routes.js.map +1 -0
  11. package/dist/dashboard/dashboard-config.d.ts +19 -0
  12. package/dist/dashboard/dashboard-config.d.ts.map +1 -0
  13. package/dist/dashboard/dashboard-config.js +17 -0
  14. package/dist/dashboard/dashboard-config.js.map +1 -0
  15. package/dist/dashboard/http-server.d.ts +29 -0
  16. package/dist/dashboard/http-server.d.ts.map +1 -0
  17. package/dist/dashboard/http-server.js +177 -0
  18. package/dist/dashboard/http-server.js.map +1 -0
  19. package/dist/dashboard/public/app.js +2396 -0
  20. package/dist/dashboard/public/assets/favicon.svg +44 -0
  21. package/dist/dashboard/public/assets/icon-512.png +0 -0
  22. package/dist/dashboard/public/assets/logo.svg +45 -0
  23. package/dist/dashboard/public/favicon.ico +0 -0
  24. package/dist/dashboard/public/index.html +338 -0
  25. package/dist/dashboard/public/style.css +858 -0
  26. package/dist/dashboard/public/vendor/cytoscape.min.js +1 -0
  27. package/dist/dashboard/public/vendor/vis-timeline-graph2d.min.css +2 -0
  28. package/dist/dashboard/public/vendor/vis-timeline-graph2d.min.js +48 -0
  29. package/dist/embeddings/embedder.d.ts +24 -0
  30. package/dist/embeddings/embedder.d.ts.map +1 -0
  31. package/dist/embeddings/embedder.js +109 -0
  32. package/dist/embeddings/embedder.js.map +1 -0
  33. package/dist/embeddings/index-manager.d.ts +27 -0
  34. package/dist/embeddings/index-manager.d.ts.map +1 -0
  35. package/dist/embeddings/index-manager.js +115 -0
  36. package/dist/embeddings/index-manager.js.map +1 -0
  37. package/dist/embeddings/search.d.ts +52 -0
  38. package/dist/embeddings/search.d.ts.map +1 -0
  39. package/dist/embeddings/search.js +170 -0
  40. package/dist/embeddings/search.js.map +1 -0
  41. package/dist/engine/archiver.d.ts +28 -0
  42. package/dist/engine/archiver.d.ts.map +1 -0
  43. package/dist/engine/archiver.js +153 -0
  44. package/dist/engine/archiver.js.map +1 -0
  45. package/dist/engine/blackboard.d.ts +55 -0
  46. package/dist/engine/blackboard.d.ts.map +1 -0
  47. package/dist/engine/blackboard.js +90 -0
  48. package/dist/engine/blackboard.js.map +1 -0
  49. package/dist/engine/context-assembler.d.ts +50 -0
  50. package/dist/engine/context-assembler.d.ts.map +1 -0
  51. package/dist/engine/context-assembler.js +483 -0
  52. package/dist/engine/context-assembler.js.map +1 -0
  53. package/dist/engine/coordination.d.ts +53 -0
  54. package/dist/engine/coordination.d.ts.map +1 -0
  55. package/dist/engine/coordination.js +239 -0
  56. package/dist/engine/coordination.js.map +1 -0
  57. package/dist/engine/decisions.d.ts +147 -0
  58. package/dist/engine/decisions.d.ts.map +1 -0
  59. package/dist/engine/decisions.js +540 -0
  60. package/dist/engine/decisions.js.map +1 -0
  61. package/dist/engine/exporter.d.ts +30 -0
  62. package/dist/engine/exporter.d.ts.map +1 -0
  63. package/dist/engine/exporter.js +192 -0
  64. package/dist/engine/exporter.js.map +1 -0
  65. package/dist/engine/graph.d.ts +55 -0
  66. package/dist/engine/graph.d.ts.map +1 -0
  67. package/dist/engine/graph.js +119 -0
  68. package/dist/engine/graph.js.map +1 -0
  69. package/dist/engine/planning-bridge.d.ts +53 -0
  70. package/dist/engine/planning-bridge.d.ts.map +1 -0
  71. package/dist/engine/planning-bridge.js +170 -0
  72. package/dist/engine/planning-bridge.js.map +1 -0
  73. package/dist/index.d.ts +3 -0
  74. package/dist/index.d.ts.map +1 -0
  75. package/dist/index.js +32 -0
  76. package/dist/index.js.map +1 -0
  77. package/dist/server.d.ts +7 -0
  78. package/dist/server.d.ts.map +1 -0
  79. package/dist/server.js +82 -0
  80. package/dist/server.js.map +1 -0
  81. package/dist/storage/agent-store.d.ts +36 -0
  82. package/dist/storage/agent-store.d.ts.map +1 -0
  83. package/dist/storage/agent-store.js +119 -0
  84. package/dist/storage/agent-store.js.map +1 -0
  85. package/dist/storage/blackboard-store.d.ts +21 -0
  86. package/dist/storage/blackboard-store.d.ts.map +1 -0
  87. package/dist/storage/blackboard-store.js +56 -0
  88. package/dist/storage/blackboard-store.js.map +1 -0
  89. package/dist/storage/decision-store.d.ts +23 -0
  90. package/dist/storage/decision-store.d.ts.map +1 -0
  91. package/dist/storage/decision-store.js +179 -0
  92. package/dist/storage/decision-store.js.map +1 -0
  93. package/dist/storage/file-store.d.ts +20 -0
  94. package/dist/storage/file-store.d.ts.map +1 -0
  95. package/dist/storage/file-store.js +108 -0
  96. package/dist/storage/file-store.js.map +1 -0
  97. package/dist/storage/graph-store.d.ts +39 -0
  98. package/dist/storage/graph-store.d.ts.map +1 -0
  99. package/dist/storage/graph-store.js +143 -0
  100. package/dist/storage/graph-store.js.map +1 -0
  101. package/dist/storage/handoff-store.d.ts +44 -0
  102. package/dist/storage/handoff-store.d.ts.map +1 -0
  103. package/dist/storage/handoff-store.js +136 -0
  104. package/dist/storage/handoff-store.js.map +1 -0
  105. package/dist/storage/init.d.ts +10 -0
  106. package/dist/storage/init.d.ts.map +1 -0
  107. package/dist/storage/init.js +48 -0
  108. package/dist/storage/init.js.map +1 -0
  109. package/dist/tools/blackboard-tools.d.ts +4 -0
  110. package/dist/tools/blackboard-tools.d.ts.map +1 -0
  111. package/dist/tools/blackboard-tools.js +131 -0
  112. package/dist/tools/blackboard-tools.js.map +1 -0
  113. package/dist/tools/context-tools.d.ts +4 -0
  114. package/dist/tools/context-tools.d.ts.map +1 -0
  115. package/dist/tools/context-tools.js +76 -0
  116. package/dist/tools/context-tools.js.map +1 -0
  117. package/dist/tools/coordination-tools.d.ts +6 -0
  118. package/dist/tools/coordination-tools.d.ts.map +1 -0
  119. package/dist/tools/coordination-tools.js +220 -0
  120. package/dist/tools/coordination-tools.js.map +1 -0
  121. package/dist/tools/decision-tools.d.ts +4 -0
  122. package/dist/tools/decision-tools.d.ts.map +1 -0
  123. package/dist/tools/decision-tools.js +265 -0
  124. package/dist/tools/decision-tools.js.map +1 -0
  125. package/dist/tools/export-tools.d.ts +4 -0
  126. package/dist/tools/export-tools.d.ts.map +1 -0
  127. package/dist/tools/export-tools.js +30 -0
  128. package/dist/tools/export-tools.js.map +1 -0
  129. package/dist/tools/graph-tools.d.ts +4 -0
  130. package/dist/tools/graph-tools.d.ts.map +1 -0
  131. package/dist/tools/graph-tools.js +127 -0
  132. package/dist/tools/graph-tools.js.map +1 -0
  133. package/dist/tools/lifecycle-tools.d.ts +9 -0
  134. package/dist/tools/lifecycle-tools.d.ts.map +1 -0
  135. package/dist/tools/lifecycle-tools.js +142 -0
  136. package/dist/tools/lifecycle-tools.js.map +1 -0
  137. package/dist/utils/errors.d.ts +24 -0
  138. package/dist/utils/errors.d.ts.map +1 -0
  139. package/dist/utils/errors.js +31 -0
  140. package/dist/utils/errors.js.map +1 -0
  141. package/dist/utils/ids.d.ts +2 -0
  142. package/dist/utils/ids.d.ts.map +1 -0
  143. package/dist/utils/ids.js +10 -0
  144. package/dist/utils/ids.js.map +1 -0
  145. package/dist/utils/liveness.d.ts +14 -0
  146. package/dist/utils/liveness.d.ts.map +1 -0
  147. package/dist/utils/liveness.js +19 -0
  148. package/dist/utils/liveness.js.map +1 -0
  149. package/dist/utils/tags.d.ts +9 -0
  150. package/dist/utils/tags.d.ts.map +1 -0
  151. package/dist/utils/tags.js +12 -0
  152. package/dist/utils/tags.js.map +1 -0
  153. package/dist/utils/tokens.d.ts +6 -0
  154. package/dist/utils/tokens.d.ts.map +1 -0
  155. package/dist/utils/tokens.js +8 -0
  156. package/dist/utils/tokens.js.map +1 -0
  157. package/dist/utils/types.d.ts +308 -0
  158. package/dist/utils/types.d.ts.map +1 -0
  159. package/dist/utils/types.js +18 -0
  160. package/dist/utils/types.js.map +1 -0
  161. package/package.json +60 -0
  162. package/src/dashboard/public/app.js +2396 -0
  163. package/src/dashboard/public/assets/favicon.svg +44 -0
  164. package/src/dashboard/public/assets/icon-512.png +0 -0
  165. package/src/dashboard/public/assets/logo.svg +45 -0
  166. package/src/dashboard/public/favicon.ico +0 -0
  167. package/src/dashboard/public/index.html +338 -0
  168. package/src/dashboard/public/style.css +858 -0
  169. package/src/dashboard/public/vendor/cytoscape.min.js +1 -0
  170. package/src/dashboard/public/vendor/vis-timeline-graph2d.min.css +2 -0
  171. package/src/dashboard/public/vendor/vis-timeline-graph2d.min.js +48 -0
package/dist/index.js ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Twining MCP Server entry point.
4
+ * Connects via stdio transport — never use console.log (corrupts JSON-RPC).
5
+ */
6
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
+ import { createServer } from "./server.js";
8
+ import { startDashboard, setupDashboardShutdown } from "./dashboard/http-server.js";
9
+ async function main() {
10
+ // Parse --project argument, default to cwd
11
+ let projectRoot = process.cwd();
12
+ const projectArgIndex = process.argv.indexOf("--project");
13
+ if (projectArgIndex !== -1 && process.argv[projectArgIndex + 1]) {
14
+ projectRoot = process.argv[projectArgIndex + 1];
15
+ }
16
+ const server = createServer(projectRoot);
17
+ const transport = new StdioServerTransport();
18
+ await server.connect(transport);
19
+ // Start dashboard HTTP server (fire-and-forget — never blocks MCP)
20
+ startDashboard(projectRoot).then((result) => {
21
+ if (result) {
22
+ setupDashboardShutdown(result.server);
23
+ }
24
+ }).catch((err) => {
25
+ console.error("[twining] Dashboard failed to start (non-fatal):", err.message);
26
+ });
27
+ }
28
+ main().catch((err) => {
29
+ console.error("Fatal:", err);
30
+ process.exit(1);
31
+ });
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAEpF,KAAK,UAAU,IAAI;IACjB,2CAA2C;IAC3C,IAAI,WAAW,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAChC,MAAM,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC1D,IAAI,eAAe,KAAK,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC,EAAE,CAAC;QAChE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,eAAe,GAAG,CAAC,CAAE,CAAC;IACnD,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,mEAAmE;IACnE,cAAc,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;QAC1C,IAAI,MAAM,EAAE,CAAC;YACX,sBAAsB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACxC,CAAC;IACH,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QACf,OAAO,CAAC,KAAK,CAAC,kDAAkD,EAAG,GAAa,CAAC,OAAO,CAAC,CAAC;IAC5F,CAAC,CAAC,CAAC;AACL,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC7B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ /**
3
+ * Create and configure the Twining MCP server.
4
+ * Auto-creates .twining/ directory on first use.
5
+ */
6
+ export declare function createServer(projectRoot: string): McpServer;
7
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA8BpE;;;GAGG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CA+F3D"}
package/dist/server.js ADDED
@@ -0,0 +1,82 @@
1
+ /**
2
+ * MCP server creation with all tool registrations.
3
+ * Creates stores, engines, and registers all tools.
4
+ * Phase 3: Adds knowledge graph layer and lifecycle management.
5
+ */
6
+ import { createRequire } from "node:module";
7
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
+ import { ensureInitialized } from "./storage/init.js";
9
+ const require = createRequire(import.meta.url);
10
+ const { version: PKG_VERSION } = require("../package.json");
11
+ import { loadConfig } from "./config.js";
12
+ import { BlackboardStore } from "./storage/blackboard-store.js";
13
+ import { DecisionStore } from "./storage/decision-store.js";
14
+ import { GraphStore } from "./storage/graph-store.js";
15
+ import { BlackboardEngine } from "./engine/blackboard.js";
16
+ import { DecisionEngine } from "./engine/decisions.js";
17
+ import { GraphEngine } from "./engine/graph.js";
18
+ import { Archiver } from "./engine/archiver.js";
19
+ import { ContextAssembler } from "./engine/context-assembler.js";
20
+ import { PlanningBridge } from "./engine/planning-bridge.js";
21
+ import { Embedder } from "./embeddings/embedder.js";
22
+ import { IndexManager } from "./embeddings/index-manager.js";
23
+ import { SearchEngine } from "./embeddings/search.js";
24
+ import { registerBlackboardTools } from "./tools/blackboard-tools.js";
25
+ import { registerDecisionTools } from "./tools/decision-tools.js";
26
+ import { registerContextTools } from "./tools/context-tools.js";
27
+ import { registerLifecycleTools } from "./tools/lifecycle-tools.js";
28
+ import { registerGraphTools } from "./tools/graph-tools.js";
29
+ import { Exporter } from "./engine/exporter.js";
30
+ import { registerExportTools } from "./tools/export-tools.js";
31
+ import { AgentStore } from "./storage/agent-store.js";
32
+ import { HandoffStore } from "./storage/handoff-store.js";
33
+ import { CoordinationEngine } from "./engine/coordination.js";
34
+ import { registerCoordinationTools } from "./tools/coordination-tools.js";
35
+ /**
36
+ * Create and configure the Twining MCP server.
37
+ * Auto-creates .twining/ directory on first use.
38
+ */
39
+ export function createServer(projectRoot) {
40
+ // Ensure .twining/ directory exists
41
+ const twiningDir = ensureInitialized(projectRoot);
42
+ // Load config
43
+ const config = loadConfig(twiningDir);
44
+ // Create stores
45
+ const blackboardStore = new BlackboardStore(twiningDir);
46
+ const decisionStore = new DecisionStore(twiningDir);
47
+ const graphStore = new GraphStore(twiningDir);
48
+ // Create embedding layer (lazy-loaded — no ONNX init cost at startup)
49
+ const embedder = new Embedder(twiningDir);
50
+ const indexManager = new IndexManager(twiningDir);
51
+ const searchEngine = new SearchEngine(embedder, indexManager);
52
+ // Create engines (with embedding support)
53
+ const blackboardEngine = new BlackboardEngine(blackboardStore, embedder, indexManager, searchEngine);
54
+ const graphEngine = new GraphEngine(graphStore);
55
+ const decisionEngine = new DecisionEngine(decisionStore, blackboardEngine, embedder, indexManager, projectRoot, searchEngine, graphEngine);
56
+ const archiver = new Archiver(twiningDir, blackboardStore, blackboardEngine, indexManager);
57
+ const planningBridge = new PlanningBridge(projectRoot);
58
+ // Create coordination stores (before ContextAssembler which depends on them)
59
+ const agentStore = new AgentStore(twiningDir);
60
+ const handoffStore = new HandoffStore(twiningDir);
61
+ const contextAssembler = new ContextAssembler(blackboardStore, decisionStore, searchEngine, config, graphEngine, planningBridge, handoffStore, // for recent handoffs in assembly
62
+ agentStore);
63
+ // Create coordination engine
64
+ const coordinationEngine = new CoordinationEngine(agentStore, handoffStore, blackboardEngine, decisionStore, blackboardStore, config);
65
+ // Create exporter
66
+ const exporter = new Exporter(blackboardStore, decisionStore, graphStore);
67
+ // Create MCP server
68
+ const server = new McpServer({
69
+ name: "twining-mcp",
70
+ version: PKG_VERSION,
71
+ });
72
+ // Register all tools
73
+ registerBlackboardTools(server, blackboardEngine);
74
+ registerDecisionTools(server, decisionEngine);
75
+ registerContextTools(server, contextAssembler);
76
+ registerLifecycleTools(server, twiningDir, blackboardStore, decisionStore, graphStore, archiver, config, agentStore);
77
+ registerGraphTools(server, graphEngine);
78
+ registerExportTools(server, exporter);
79
+ registerCoordinationTools(server, agentStore, coordinationEngine, config);
80
+ return server;
81
+ }
82
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { AgentRecord } from "../utils/types.js";
2
+ export declare class AgentStore {
3
+ private readonly registryPath;
4
+ private readonly agentsDir;
5
+ constructor(twiningDir: string);
6
+ /** Read the registry, returning empty array if file doesn't exist. */
7
+ private readRegistry;
8
+ /** Write the registry, ensuring agents/ directory exists. */
9
+ private writeRegistry;
10
+ /**
11
+ * Create or update an agent record.
12
+ * - If agent_id exists: merge capabilities (union), overwrite role/description if provided
13
+ * - If agent_id doesn't exist: create new record
14
+ */
15
+ upsert(input: {
16
+ agent_id: string;
17
+ capabilities?: string[];
18
+ role?: string;
19
+ description?: string;
20
+ }): Promise<AgentRecord>;
21
+ /**
22
+ * Update last_active timestamp, creating a minimal record if agent doesn't exist.
23
+ * This is the auto-register path (REG-01).
24
+ */
25
+ touch(agentId: string): Promise<AgentRecord>;
26
+ /** Get a single agent by agent_id, or null if not found. */
27
+ get(agentId: string): Promise<AgentRecord | null>;
28
+ /** Get all agents from registry. */
29
+ getAll(): Promise<AgentRecord[]>;
30
+ /**
31
+ * Find agents with any matching capability tag (OR match).
32
+ * Input tags are normalized before matching.
33
+ */
34
+ findByCapabilities(tags: string[]): Promise<AgentRecord[]>;
35
+ }
36
+ //# sourceMappingURL=agent-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-store.d.ts","sourceRoot":"","sources":["../../src/storage/agent-store.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,UAAU,EAAE,MAAM;IAK9B,sEAAsE;YACxD,YAAY;IAS1B,6DAA6D;YAC/C,aAAa;IAK3B;;;;OAIG;IACG,MAAM,CAAC,KAAK,EAAE;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,GAAG,OAAO,CAAC,WAAW,CAAC;IAyCxB;;;OAGG;IACG,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAwBlD,4DAA4D;IACtD,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAKvD,oCAAoC;IAC9B,MAAM,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAItC;;;OAGG;IACG,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;CASjE"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Agent registry storage layer.
3
+ * Manages agent records in `.twining/agents/registry.json` as a JSON array.
4
+ * Uses file-store readJSON/writeJSON for locked file I/O.
5
+ * Upsert semantics by agent_id with capability merging.
6
+ */
7
+ import path from "node:path";
8
+ import { readJSON, writeJSON, ensureDir } from "./file-store.js";
9
+ import { normalizeTags } from "../utils/tags.js";
10
+ export class AgentStore {
11
+ registryPath;
12
+ agentsDir;
13
+ constructor(twiningDir) {
14
+ this.agentsDir = path.join(twiningDir, "agents");
15
+ this.registryPath = path.join(this.agentsDir, "registry.json");
16
+ }
17
+ /** Read the registry, returning empty array if file doesn't exist. */
18
+ async readRegistry() {
19
+ try {
20
+ return await readJSON(this.registryPath);
21
+ }
22
+ catch {
23
+ // File doesn't exist or is corrupt — treat as empty
24
+ return [];
25
+ }
26
+ }
27
+ /** Write the registry, ensuring agents/ directory exists. */
28
+ async writeRegistry(agents) {
29
+ ensureDir(this.agentsDir);
30
+ await writeJSON(this.registryPath, agents);
31
+ }
32
+ /**
33
+ * Create or update an agent record.
34
+ * - If agent_id exists: merge capabilities (union), overwrite role/description if provided
35
+ * - If agent_id doesn't exist: create new record
36
+ */
37
+ async upsert(input) {
38
+ const agents = await this.readRegistry();
39
+ const now = new Date().toISOString();
40
+ const normalizedCaps = normalizeTags(input.capabilities ?? []);
41
+ const existingIdx = agents.findIndex((a) => a.agent_id === input.agent_id);
42
+ if (existingIdx >= 0) {
43
+ const existing = agents[existingIdx];
44
+ // Merge capabilities: union of existing + new, then normalize
45
+ const mergedCaps = normalizeTags([
46
+ ...existing.capabilities,
47
+ ...normalizedCaps,
48
+ ]);
49
+ existing.capabilities = mergedCaps;
50
+ // Overwrite role/description only if explicitly provided (not undefined)
51
+ if (input.role !== undefined) {
52
+ existing.role = input.role;
53
+ }
54
+ if (input.description !== undefined) {
55
+ existing.description = input.description;
56
+ }
57
+ existing.last_active = now;
58
+ await this.writeRegistry(agents);
59
+ return existing;
60
+ }
61
+ // Create new record
62
+ const agent = {
63
+ agent_id: input.agent_id,
64
+ capabilities: normalizedCaps,
65
+ role: input.role,
66
+ description: input.description,
67
+ registered_at: now,
68
+ last_active: now,
69
+ };
70
+ agents.push(agent);
71
+ await this.writeRegistry(agents);
72
+ return agent;
73
+ }
74
+ /**
75
+ * Update last_active timestamp, creating a minimal record if agent doesn't exist.
76
+ * This is the auto-register path (REG-01).
77
+ */
78
+ async touch(agentId) {
79
+ const agents = await this.readRegistry();
80
+ const now = new Date().toISOString();
81
+ const existing = agents.find((a) => a.agent_id === agentId);
82
+ if (existing) {
83
+ existing.last_active = now;
84
+ await this.writeRegistry(agents);
85
+ return existing;
86
+ }
87
+ // Create minimal record
88
+ const agent = {
89
+ agent_id: agentId,
90
+ capabilities: [],
91
+ registered_at: now,
92
+ last_active: now,
93
+ };
94
+ agents.push(agent);
95
+ await this.writeRegistry(agents);
96
+ return agent;
97
+ }
98
+ /** Get a single agent by agent_id, or null if not found. */
99
+ async get(agentId) {
100
+ const agents = await this.readRegistry();
101
+ return agents.find((a) => a.agent_id === agentId) ?? null;
102
+ }
103
+ /** Get all agents from registry. */
104
+ async getAll() {
105
+ return this.readRegistry();
106
+ }
107
+ /**
108
+ * Find agents with any matching capability tag (OR match).
109
+ * Input tags are normalized before matching.
110
+ */
111
+ async findByCapabilities(tags) {
112
+ const normalizedInput = normalizeTags(tags);
113
+ if (normalizedInput.length === 0)
114
+ return [];
115
+ const agents = await this.readRegistry();
116
+ return agents.filter((agent) => agent.capabilities.some((cap) => normalizedInput.includes(cap)));
117
+ }
118
+ }
119
+ //# sourceMappingURL=agent-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-store.js","sourceRoot":"","sources":["../../src/storage/agent-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { BlackboardEntry } from "../utils/types.js";
2
+ export declare class BlackboardStore {
3
+ private readonly blackboardPath;
4
+ constructor(twiningDir: string);
5
+ /** Append a new entry, generating ID and timestamp. */
6
+ append(entry: Omit<BlackboardEntry, "id" | "timestamp">): Promise<BlackboardEntry>;
7
+ /** Read entries with optional filters. */
8
+ read(filters?: {
9
+ entry_types?: string[];
10
+ tags?: string[];
11
+ scope?: string;
12
+ since?: string;
13
+ limit?: number;
14
+ }): Promise<{
15
+ entries: BlackboardEntry[];
16
+ total_count: number;
17
+ }>;
18
+ /** Get the N most recent entries, optionally filtered by type. */
19
+ recent(n?: number, entry_types?: string[]): Promise<BlackboardEntry[]>;
20
+ }
21
+ //# sourceMappingURL=blackboard-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blackboard-store.d.ts","sourceRoot":"","sources":["../../src/storage/blackboard-store.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEzD,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,UAAU,EAAE,MAAM;IAI9B,uDAAuD;IACjD,MAAM,CACV,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,GAAG,WAAW,CAAC,GAC/C,OAAO,CAAC,eAAe,CAAC;IAU3B,0CAA0C;IACpC,IAAI,CAAC,OAAO,CAAC,EAAE;QACnB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,eAAe,EAAE,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAqChE,kEAAkE;IAC5D,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;CAU7E"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Blackboard CRUD operations.
3
+ * Append-only JSONL storage for shared state entries.
4
+ */
5
+ import path from "node:path";
6
+ import { appendJSONL, readJSONL } from "./file-store.js";
7
+ import { generateId } from "../utils/ids.js";
8
+ export class BlackboardStore {
9
+ blackboardPath;
10
+ constructor(twiningDir) {
11
+ this.blackboardPath = path.join(twiningDir, "blackboard.jsonl");
12
+ }
13
+ /** Append a new entry, generating ID and timestamp. */
14
+ async append(entry) {
15
+ const full = {
16
+ ...entry,
17
+ id: generateId(),
18
+ timestamp: new Date().toISOString(),
19
+ };
20
+ await appendJSONL(this.blackboardPath, full);
21
+ return full;
22
+ }
23
+ /** Read entries with optional filters. */
24
+ async read(filters) {
25
+ let entries = await readJSONL(this.blackboardPath);
26
+ if (filters?.entry_types && filters.entry_types.length > 0) {
27
+ entries = entries.filter((e) => filters.entry_types.includes(e.entry_type));
28
+ }
29
+ if (filters?.tags && filters.tags.length > 0) {
30
+ entries = entries.filter((e) => e.tags.some((t) => filters.tags.includes(t)));
31
+ }
32
+ if (filters?.scope) {
33
+ const filterScope = filters.scope;
34
+ entries = entries.filter((e) => e.scope.startsWith(filterScope) || filterScope.startsWith(e.scope));
35
+ }
36
+ if (filters?.since) {
37
+ const sinceTime = filters.since;
38
+ entries = entries.filter((e) => e.timestamp >= sinceTime);
39
+ }
40
+ const total_count = entries.length;
41
+ if (filters?.limit !== undefined && filters.limit > 0) {
42
+ entries = entries.slice(-filters.limit);
43
+ }
44
+ return { entries, total_count };
45
+ }
46
+ /** Get the N most recent entries, optionally filtered by type. */
47
+ async recent(n, entry_types) {
48
+ let entries = await readJSONL(this.blackboardPath);
49
+ if (entry_types && entry_types.length > 0) {
50
+ entries = entries.filter((e) => entry_types.includes(e.entry_type));
51
+ }
52
+ const count = n ?? 20;
53
+ return entries.slice(-count).reverse();
54
+ }
55
+ }
56
+ //# sourceMappingURL=blackboard-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blackboard-store.js","sourceRoot":"","sources":["../../src/storage/blackboard-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { Decision, DecisionIndexEntry, DecisionStatus } from "../utils/types.js";
2
+ export declare class DecisionStore {
3
+ private readonly decisionsDir;
4
+ private readonly indexPath;
5
+ constructor(twiningDir: string);
6
+ /** Create a new decision. Writes individual file and updates index atomically. */
7
+ create(input: Omit<Decision, "id" | "timestamp" | "status">): Promise<Decision>;
8
+ /** Get a single decision by ID. Returns null if not found. */
9
+ get(id: string): Promise<Decision | null>;
10
+ /** Get all decisions matching a scope (prefix match or affected files/symbols match). */
11
+ getByScope(scope: string): Promise<Decision[]>;
12
+ /** Update a decision's status (and optionally other fields). */
13
+ updateStatus(id: string, status: DecisionStatus, extra?: Partial<Decision>): Promise<void>;
14
+ /** Get the full decision index. */
15
+ getIndex(): Promise<DecisionIndexEntry[]>;
16
+ /** Link a commit hash to an existing decision. Updates both file and index. */
17
+ linkCommit(id: string, commitHash: string): Promise<void>;
18
+ /** Get decisions linked to a specific commit hash. */
19
+ getByCommitHash(commitHash: string): Promise<Decision[]>;
20
+ /** Extract index entry from a full decision. */
21
+ private toIndexEntry;
22
+ }
23
+ //# sourceMappingURL=decision-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decision-store.d.ts","sourceRoot":"","sources":["../../src/storage/decision-store.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EACV,QAAQ,EACR,kBAAkB,EAClB,cAAc,EACf,MAAM,mBAAmB,CAAC;AAO3B,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,UAAU,EAAE,MAAM;IAK9B,kFAAkF;IAC5E,MAAM,CACV,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,GAAG,WAAW,GAAG,QAAQ,CAAC,GACnD,OAAO,CAAC,QAAQ,CAAC;IA8BpB,8DAA8D;IACxD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAM/C,yFAAyF;IACnF,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IA8BpD,gEAAgE;IAC1D,YAAY,CAChB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,cAAc,EACtB,KAAK,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,GACxB,OAAO,CAAC,IAAI,CAAC;IAmChB,mCAAmC;IAC7B,QAAQ,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAI/C,+EAA+E;IACzE,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4C/D,sDAAsD;IAChD,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAsB9D,gDAAgD;IAChD,OAAO,CAAC,YAAY;CAcrB"}
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Decision CRUD operations.
3
+ * Individual JSON files per decision with a fast-lookup index.
4
+ */
5
+ import fs from "node:fs";
6
+ import path from "node:path";
7
+ import lockfile from "proper-lockfile";
8
+ import { readJSON, writeJSON } from "./file-store.js";
9
+ import { generateId } from "../utils/ids.js";
10
+ const INDEX_LOCK_OPTIONS = {
11
+ retries: { retries: 10, factor: 1.5, minTimeout: 50, maxTimeout: 1000 },
12
+ stale: 10000,
13
+ };
14
+ export class DecisionStore {
15
+ decisionsDir;
16
+ indexPath;
17
+ constructor(twiningDir) {
18
+ this.decisionsDir = path.join(twiningDir, "decisions");
19
+ this.indexPath = path.join(this.decisionsDir, "index.json");
20
+ }
21
+ /** Create a new decision. Writes individual file and updates index atomically. */
22
+ async create(input) {
23
+ const decision = {
24
+ ...input,
25
+ commit_hashes: input.commit_hashes ?? [],
26
+ id: generateId(),
27
+ timestamp: new Date().toISOString(),
28
+ status: "active",
29
+ };
30
+ const filePath = path.join(this.decisionsDir, `${decision.id}.json`);
31
+ // Lock index for atomic read-modify-write
32
+ const release = await lockfile.lock(this.indexPath, INDEX_LOCK_OPTIONS);
33
+ try {
34
+ // Write individual decision file
35
+ fs.writeFileSync(filePath, JSON.stringify(decision, null, 2));
36
+ // Update index
37
+ const index = JSON.parse(fs.readFileSync(this.indexPath, "utf-8"));
38
+ index.push(this.toIndexEntry(decision));
39
+ fs.writeFileSync(this.indexPath, JSON.stringify(index, null, 2));
40
+ }
41
+ finally {
42
+ await release();
43
+ }
44
+ return decision;
45
+ }
46
+ /** Get a single decision by ID. Returns null if not found. */
47
+ async get(id) {
48
+ const filePath = path.join(this.decisionsDir, `${id}.json`);
49
+ if (!fs.existsSync(filePath))
50
+ return null;
51
+ return readJSON(filePath);
52
+ }
53
+ /** Get all decisions matching a scope (prefix match or affected files/symbols match). */
54
+ async getByScope(scope) {
55
+ const index = await this.getIndex();
56
+ const matching = index.filter((entry) => entry.scope.startsWith(scope) ||
57
+ scope.startsWith(entry.scope) ||
58
+ entry.affected_files.some((f) => f.startsWith(scope) || scope.startsWith(f)) ||
59
+ entry.affected_symbols.some((s) => s === scope));
60
+ // Load full decision files for matches
61
+ const decisions = [];
62
+ for (const entry of matching) {
63
+ const decision = await this.get(entry.id);
64
+ if (decision)
65
+ decisions.push(decision);
66
+ }
67
+ // Sort by timestamp descending, then by ID descending (ULID is monotonic)
68
+ decisions.sort((a, b) => b.timestamp.localeCompare(a.timestamp) ||
69
+ b.id.localeCompare(a.id));
70
+ return decisions;
71
+ }
72
+ /** Update a decision's status (and optionally other fields). */
73
+ async updateStatus(id, status, extra) {
74
+ const filePath = path.join(this.decisionsDir, `${id}.json`);
75
+ if (!fs.existsSync(filePath))
76
+ return;
77
+ // Lock and update individual file
78
+ const fileRelease = await lockfile.lock(filePath, INDEX_LOCK_OPTIONS);
79
+ try {
80
+ const decision = JSON.parse(fs.readFileSync(filePath, "utf-8"));
81
+ decision.status = status;
82
+ if (extra) {
83
+ Object.assign(decision, extra);
84
+ }
85
+ fs.writeFileSync(filePath, JSON.stringify(decision, null, 2));
86
+ }
87
+ finally {
88
+ await fileRelease();
89
+ }
90
+ // Update index
91
+ const release = await lockfile.lock(this.indexPath, INDEX_LOCK_OPTIONS);
92
+ try {
93
+ const index = JSON.parse(fs.readFileSync(this.indexPath, "utf-8"));
94
+ const indexEntry = index.find((e) => e.id === id);
95
+ if (indexEntry) {
96
+ indexEntry.status = status;
97
+ }
98
+ fs.writeFileSync(this.indexPath, JSON.stringify(index, null, 2));
99
+ }
100
+ finally {
101
+ await release();
102
+ }
103
+ }
104
+ /** Get the full decision index. */
105
+ async getIndex() {
106
+ return readJSON(this.indexPath);
107
+ }
108
+ /** Link a commit hash to an existing decision. Updates both file and index. */
109
+ async linkCommit(id, commitHash) {
110
+ const filePath = path.join(this.decisionsDir, `${id}.json`);
111
+ if (!fs.existsSync(filePath)) {
112
+ throw new Error(`Decision not found: ${id}`);
113
+ }
114
+ // Lock and update individual decision file
115
+ const fileRelease = await lockfile.lock(filePath, INDEX_LOCK_OPTIONS);
116
+ try {
117
+ const decision = JSON.parse(fs.readFileSync(filePath, "utf-8"));
118
+ if (!decision.commit_hashes) {
119
+ decision.commit_hashes = [];
120
+ }
121
+ if (!decision.commit_hashes.includes(commitHash)) {
122
+ decision.commit_hashes.push(commitHash);
123
+ }
124
+ fs.writeFileSync(filePath, JSON.stringify(decision, null, 2));
125
+ }
126
+ finally {
127
+ await fileRelease();
128
+ }
129
+ // Update index
130
+ const release = await lockfile.lock(this.indexPath, INDEX_LOCK_OPTIONS);
131
+ try {
132
+ const index = JSON.parse(fs.readFileSync(this.indexPath, "utf-8"));
133
+ const indexEntry = index.find((e) => e.id === id);
134
+ if (indexEntry) {
135
+ if (!indexEntry.commit_hashes) {
136
+ indexEntry.commit_hashes = [];
137
+ }
138
+ if (!indexEntry.commit_hashes.includes(commitHash)) {
139
+ indexEntry.commit_hashes.push(commitHash);
140
+ }
141
+ }
142
+ fs.writeFileSync(this.indexPath, JSON.stringify(index, null, 2));
143
+ }
144
+ finally {
145
+ await release();
146
+ }
147
+ }
148
+ /** Get decisions linked to a specific commit hash. */
149
+ async getByCommitHash(commitHash) {
150
+ const index = await this.getIndex();
151
+ const matching = index.filter((entry) => entry.commit_hashes && entry.commit_hashes.includes(commitHash));
152
+ const decisions = [];
153
+ for (const entry of matching) {
154
+ const decision = await this.get(entry.id);
155
+ if (decision)
156
+ decisions.push(decision);
157
+ }
158
+ // Sort by timestamp descending
159
+ decisions.sort((a, b) => b.timestamp.localeCompare(a.timestamp) ||
160
+ b.id.localeCompare(a.id));
161
+ return decisions;
162
+ }
163
+ /** Extract index entry from a full decision. */
164
+ toIndexEntry(decision) {
165
+ return {
166
+ id: decision.id,
167
+ timestamp: decision.timestamp,
168
+ domain: decision.domain,
169
+ scope: decision.scope,
170
+ summary: decision.summary,
171
+ confidence: decision.confidence,
172
+ status: decision.status,
173
+ affected_files: decision.affected_files,
174
+ affected_symbols: decision.affected_symbols,
175
+ commit_hashes: decision.commit_hashes ?? [],
176
+ };
177
+ }
178
+ }
179
+ //# sourceMappingURL=decision-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decision-store.js","sourceRoot":"","sources":["../../src/storage/decision-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /** Read and parse a JSON file. Throws if file doesn't exist. */
2
+ export declare function readJSON<T>(filePath: string): Promise<T>;
3
+ /** Write JSON to file under advisory lock. */
4
+ export declare function writeJSON(filePath: string, data: unknown): Promise<void>;
5
+ /** Append a single JSON object as a line to a JSONL file under advisory lock. */
6
+ export declare function appendJSONL(filePath: string, data: unknown): Promise<void>;
7
+ /**
8
+ * Read a JSONL file and parse each line.
9
+ * Corrupt lines are skipped with a warning to stderr.
10
+ * No locking needed for reads.
11
+ */
12
+ export declare function readJSONL<T>(filePath: string): Promise<T[]>;
13
+ /**
14
+ * Overwrite a JSONL file atomically under lock.
15
+ * Used by archiver to rewrite blackboard after removing archived entries.
16
+ */
17
+ export declare function writeJSONL(filePath: string, data: unknown[]): Promise<void>;
18
+ /** Ensure a directory exists, creating it recursively if needed. */
19
+ export declare function ensureDir(dirPath: string): void;
20
+ //# sourceMappingURL=file-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-store.d.ts","sourceRoot":"","sources":["../../src/storage/file-store.ts"],"names":[],"mappings":"AAiBA,gEAAgE;AAChE,wBAAsB,QAAQ,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAG9D;AAED,8CAA8C;AAC9C,wBAAsB,SAAS,CAC7B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED,iFAAiF;AACjF,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,IAAI,CAAC,CAef;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAejE;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EAAE,GACd,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,oEAAoE;AACpE,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE/C"}