@rk0429/agentic-relay 22.0.0 → 22.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.
- package/dist/application/chat/chat-daemon-service.d.ts +10 -3
- package/dist/application/chat/chat-daemon-service.js +7 -31
- package/dist/application/chat/chat-daemon-service.js.map +1 -1
- package/dist/application/chat/chat-inbound-handler.js +21 -3
- package/dist/application/chat/chat-inbound-handler.js.map +1 -1
- package/dist/application/chat/chat-outbound-handler.d.ts +2 -1
- package/dist/application/chat/chat-outbound-handler.js +22 -5
- package/dist/application/chat/chat-outbound-handler.js.map +1 -1
- package/dist/application/chat/chat-question-service.d.ts +1 -1
- package/dist/application/chat/chat-question-service.js +1 -1
- package/dist/application/chat/chat-question-service.js.map +1 -1
- package/dist/application/chat/chat-setup-service.js +1 -1
- package/dist/application/chat/chat-setup-service.js.map +1 -1
- package/dist/application/housekeeping.js +6 -2
- package/dist/application/housekeeping.js.map +1 -1
- package/dist/bin/relay.js +23 -1
- package/dist/bin/relay.js.map +1 -1
- package/dist/core/types.d.ts +1 -0
- package/dist/core/types.js +1 -0
- package/dist/core/types.js.map +1 -1
- package/dist/domain/chat/message-cleaner.d.ts +1 -1
- package/dist/domain/chat/message-cleaner.js +10 -1
- package/dist/domain/chat/message-cleaner.js.map +1 -1
- package/dist/domain/chat/message-router.js +9 -5
- package/dist/domain/chat/message-router.js.map +1 -1
- package/dist/domain/chat/platform-connection.d.ts +1 -1
- package/dist/domain/chat/platform-connection.js +1 -1
- package/dist/domain/chat/platform-connection.js.map +1 -1
- package/dist/{core/chat-types.js → domain/chat/platform-types.js} +1 -1
- package/dist/domain/chat/platform-types.js.map +1 -0
- package/dist/domain/chat/ports.d.ts +1 -1
- package/dist/domain/chat/session-bridge.d.ts +1 -1
- package/dist/domain/chat/session-bridge.js +1 -1
- package/dist/domain/chat/session-bridge.js.map +1 -1
- package/dist/domain/chat/types.d.ts +3 -1
- package/dist/infrastructure/chat/adapters/discord-adapter.d.ts +1 -1
- package/dist/infrastructure/chat/adapters/discord-adapter.js +1 -1
- package/dist/infrastructure/chat/adapters/discord-adapter.js.map +1 -1
- package/dist/infrastructure/chat/adapters/slack-adapter.d.ts +1 -1
- package/dist/infrastructure/chat/adapters/slack-adapter.js +1 -1
- package/dist/infrastructure/chat/adapters/slack-adapter.js.map +1 -1
- package/dist/infrastructure/chat/chat-ipc-client.d.ts +1 -1
- package/dist/infrastructure/chat/chat-ipc-server.js +1 -1
- package/dist/infrastructure/chat/chat-ipc-server.js.map +1 -1
- package/dist/infrastructure/chat/session-bridge-repository.js +1 -1
- package/dist/infrastructure/chat/session-bridge-repository.js.map +1 -1
- package/dist/interfaces/mcp/chat-tools.d.ts +2 -1
- package/dist/interfaces/mcp/chat-tools.js +5 -3
- package/dist/interfaces/mcp/chat-tools.js.map +1 -1
- package/dist/interfaces/mcp/relay-mcp-server.js +1 -1
- package/package.json +3 -1
- package/scripts/check-chat-boundary.mjs +220 -0
- package/scripts/check-chat-boundary.test.ts +139 -0
- package/scripts/check-circular-deps.mjs +199 -0
- package/scripts/check-circular-deps.test.ts +62 -0
- package/dist/core/chat-types.js.map +0 -1
- /package/dist/{core/chat-types.d.ts → domain/chat/platform-types.d.ts} +0 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const PROJECT_ROOT = path.resolve(__dirname, "..");
|
|
8
|
+
const STATIC_IMPORT_RE =
|
|
9
|
+
/^[\t ]*import(?!\s+type\b)(?:[\s\S]*?\sfrom\s+|\s+)["']([^"'\n]+)["'];?/gm;
|
|
10
|
+
const DYNAMIC_IMPORT_RE = /\bimport\s*\(\s*["']([^"'\n]+)["']\s*\)/g;
|
|
11
|
+
|
|
12
|
+
export async function checkCircularDeps(options = {}) {
|
|
13
|
+
const projectRoot = options.projectRoot ?? PROJECT_ROOT;
|
|
14
|
+
const graph = await buildDependencyGraph(projectRoot);
|
|
15
|
+
const cycles = findCircularDependencies(graph);
|
|
16
|
+
return { graph, cycles };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function buildDependencyGraph(projectRoot = PROJECT_ROOT) {
|
|
20
|
+
const srcRoot = path.join(projectRoot, "src");
|
|
21
|
+
const files = await walk(srcRoot);
|
|
22
|
+
const graph = new Map();
|
|
23
|
+
|
|
24
|
+
for (const filePath of files) {
|
|
25
|
+
if (!filePath.endsWith(".ts") || filePath.endsWith(".test.ts") || filePath.endsWith(".d.ts")) {
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const source = await readFile(filePath, "utf8");
|
|
30
|
+
const dependencies = new Set();
|
|
31
|
+
for (const specifier of findImportSpecifiers(source)) {
|
|
32
|
+
if (!specifier.startsWith(".")) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const resolvedTargetPath = resolveImport(filePath, specifier);
|
|
36
|
+
if (!resolvedTargetPath?.startsWith(srcRoot)) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (resolvedTargetPath.endsWith(".test.ts")) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
dependencies.add(toPosixPath(path.relative(projectRoot, resolvedTargetPath)));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
graph.set(toPosixPath(path.relative(projectRoot, filePath)), dependencies);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return graph;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function findCircularDependencies(graph) {
|
|
52
|
+
const visiting = new Set();
|
|
53
|
+
const visited = new Set();
|
|
54
|
+
const stack = [];
|
|
55
|
+
const cycles = [];
|
|
56
|
+
const seenCycles = new Set();
|
|
57
|
+
|
|
58
|
+
const nodes = [...graph.keys()].sort();
|
|
59
|
+
|
|
60
|
+
const visit = (node) => {
|
|
61
|
+
visiting.add(node);
|
|
62
|
+
stack.push(node);
|
|
63
|
+
|
|
64
|
+
for (const dependency of [...(graph.get(node) ?? [])].sort()) {
|
|
65
|
+
if (!graph.has(dependency)) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (visiting.has(dependency)) {
|
|
70
|
+
const startIndex = stack.indexOf(dependency);
|
|
71
|
+
const cycle = stack.slice(startIndex).concat(dependency);
|
|
72
|
+
const cycleKey = canonicalizeCycle(cycle.slice(0, -1));
|
|
73
|
+
if (!seenCycles.has(cycleKey)) {
|
|
74
|
+
seenCycles.add(cycleKey);
|
|
75
|
+
cycles.push(cycle);
|
|
76
|
+
}
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!visited.has(dependency)) {
|
|
81
|
+
visit(dependency);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
stack.pop();
|
|
86
|
+
visiting.delete(node);
|
|
87
|
+
visited.add(node);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
for (const node of nodes) {
|
|
91
|
+
if (!visited.has(node)) {
|
|
92
|
+
visit(node);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return cycles;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function walk(directory) {
|
|
100
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
101
|
+
const files = [];
|
|
102
|
+
|
|
103
|
+
for (const entry of entries) {
|
|
104
|
+
if (entry.name === "dist" || entry.name === "node_modules") {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const fullPath = path.join(directory, entry.name);
|
|
109
|
+
if (entry.isDirectory()) {
|
|
110
|
+
files.push(...(await walk(fullPath)));
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (entry.isFile()) {
|
|
114
|
+
files.push(fullPath);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return files;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function findImportSpecifiers(source) {
|
|
122
|
+
const specifiers = new Set();
|
|
123
|
+
|
|
124
|
+
for (const match of source.matchAll(STATIC_IMPORT_RE)) {
|
|
125
|
+
specifiers.add(match[1]);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
for (const match of source.matchAll(DYNAMIC_IMPORT_RE)) {
|
|
129
|
+
specifiers.add(match[1]);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return [...specifiers];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function resolveImport(sourceFilePath, specifier) {
|
|
136
|
+
const candidateBasePath = path.resolve(path.dirname(sourceFilePath), specifier);
|
|
137
|
+
return buildResolutionCandidates(candidateBasePath).find(fileExists) ?? null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function buildResolutionCandidates(candidateBasePath) {
|
|
141
|
+
const extension = path.extname(candidateBasePath);
|
|
142
|
+
|
|
143
|
+
if (extension === ".ts" || extension === ".mts" || extension === ".cts") {
|
|
144
|
+
return [candidateBasePath];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (extension === ".js") {
|
|
148
|
+
return [candidateBasePath.slice(0, -3) + ".ts"];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (extension === ".mjs") {
|
|
152
|
+
return [candidateBasePath.slice(0, -4) + ".mts"];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (extension === ".cjs") {
|
|
156
|
+
return [candidateBasePath.slice(0, -4) + ".cts"];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return [
|
|
160
|
+
`${candidateBasePath}.ts`,
|
|
161
|
+
`${candidateBasePath}.mts`,
|
|
162
|
+
`${candidateBasePath}.cts`,
|
|
163
|
+
path.join(candidateBasePath, "index.ts"),
|
|
164
|
+
path.join(candidateBasePath, "index.mts"),
|
|
165
|
+
path.join(candidateBasePath, "index.cts"),
|
|
166
|
+
];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function canonicalizeCycle(cycle) {
|
|
170
|
+
const rotations = cycle.map((_, index) =>
|
|
171
|
+
[...cycle.slice(index), ...cycle.slice(0, index)].join("->"),
|
|
172
|
+
);
|
|
173
|
+
return rotations.sort()[0];
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function fileExists(filePath) {
|
|
177
|
+
return Boolean(filePath) && existsSync(filePath);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function toPosixPath(filePath) {
|
|
181
|
+
return filePath.split(path.sep).join("/");
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function main() {
|
|
185
|
+
const { cycles } = await checkCircularDeps();
|
|
186
|
+
if (cycles.length > 0) {
|
|
187
|
+
for (const cycle of cycles) {
|
|
188
|
+
console.error(`AF-2 cycle: ${cycle.join(" -> ")}`);
|
|
189
|
+
}
|
|
190
|
+
process.exitCode = 1;
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
console.error("✓ AF-2: no circular dependencies found");
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1])) {
|
|
198
|
+
await main();
|
|
199
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { mkdtemp, mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { checkCircularDeps } from "./check-circular-deps.mjs";
|
|
6
|
+
|
|
7
|
+
const workspaces: string[] = [];
|
|
8
|
+
|
|
9
|
+
afterEach(async () => {
|
|
10
|
+
const { rm } = await import("node:fs/promises");
|
|
11
|
+
await Promise.all(workspaces.map((workspace) => rm(workspace, { recursive: true, force: true })));
|
|
12
|
+
workspaces.length = 0;
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe("checkCircularDeps", () => {
|
|
16
|
+
it("passes when relative imports do not form a cycle", async () => {
|
|
17
|
+
const projectRoot = await createCircularFixture({
|
|
18
|
+
"src/a.ts": 'import "./b.js";\n',
|
|
19
|
+
"src/b.ts": 'await import("./c.js");\n',
|
|
20
|
+
"src/c.ts": "export const c = 1;\n",
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const result = await checkCircularDeps({ projectRoot });
|
|
24
|
+
|
|
25
|
+
expect(result.cycles).toEqual([]);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it("detects cycles across static and dynamic imports", async () => {
|
|
29
|
+
const projectRoot = await createCircularFixture({
|
|
30
|
+
"src/a.ts": 'await import("./b.js");\n',
|
|
31
|
+
"src/b.ts": 'import "./a.js";\n',
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const result = await checkCircularDeps({ projectRoot });
|
|
35
|
+
|
|
36
|
+
expect(result.cycles).toEqual([["src/a.ts", "src/b.ts", "src/a.ts"]]);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("ignores import type edges when detecting cycles", async () => {
|
|
40
|
+
const projectRoot = await createCircularFixture({
|
|
41
|
+
"src/a.ts": 'import type { B } from "./b.js";\nexport type A = { b: B };\n',
|
|
42
|
+
"src/b.ts": 'import type { A } from "./a.js";\nexport type B = { a: A };\n',
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const result = await checkCircularDeps({ projectRoot });
|
|
46
|
+
|
|
47
|
+
expect(result.cycles).toEqual([]);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
async function createCircularFixture(files: Record<string, string>) {
|
|
52
|
+
const projectRoot = await mkdtemp(path.join(os.tmpdir(), "agentic-relay-circular-"));
|
|
53
|
+
workspaces.push(projectRoot);
|
|
54
|
+
|
|
55
|
+
for (const [relativePath, content] of Object.entries(files)) {
|
|
56
|
+
const absolutePath = path.join(projectRoot, relativePath);
|
|
57
|
+
await mkdir(path.dirname(absolutePath), { recursive: true });
|
|
58
|
+
await writeFile(absolutePath, content, "utf8");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return projectRoot;
|
|
62
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chat-types.js","sourceRoot":"","sources":["../../src/core/chat-types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,YAGX;AAHD,WAAY,YAAY;IACtB,+BAAe,CAAA;IACf,mCAAmB,CAAA;AACrB,CAAC,EAHW,YAAY,KAAZ,YAAY,QAGvB;AAED,MAAM,CAAN,IAAY,eAMX;AAND,WAAY,eAAe;IACzB,gDAA6B,CAAA;IAC7B,4CAAyB,CAAA;IACzB,0CAAuB,CAAA;IACvB,gDAA6B,CAAA;IAC7B,oCAAiB,CAAA;AACnB,CAAC,EANW,eAAe,KAAf,eAAe,QAM1B"}
|
|
File without changes
|