@zhixuan92/multi-model-agent-mcp 2.8.0 → 2.8.1

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 (90) hide show
  1. package/README.md +11 -207
  2. package/dist/cli.js +6 -599
  3. package/package.json +8 -49
  4. package/dist/cli.d.ts +0 -79
  5. package/dist/cli.d.ts.map +0 -1
  6. package/dist/cli.js.map +0 -1
  7. package/dist/headline.d.ts +0 -25
  8. package/dist/headline.d.ts.map +0 -1
  9. package/dist/headline.js +0 -58
  10. package/dist/headline.js.map +0 -1
  11. package/dist/http/auth.d.ts +0 -8
  12. package/dist/http/auth.d.ts.map +0 -1
  13. package/dist/http/auth.js +0 -43
  14. package/dist/http/auth.js.map +0 -1
  15. package/dist/http/cwd-validator.d.ts +0 -11
  16. package/dist/http/cwd-validator.d.ts.map +0 -1
  17. package/dist/http/cwd-validator.js +0 -31
  18. package/dist/http/cwd-validator.js.map +0 -1
  19. package/dist/http/lifecycle-handlers.d.ts +0 -12
  20. package/dist/http/lifecycle-handlers.d.ts.map +0 -1
  21. package/dist/http/lifecycle-handlers.js +0 -87
  22. package/dist/http/lifecycle-handlers.js.map +0 -1
  23. package/dist/http/loopback.d.ts +0 -10
  24. package/dist/http/loopback.d.ts.map +0 -1
  25. package/dist/http/loopback.js +0 -34
  26. package/dist/http/loopback.js.map +0 -1
  27. package/dist/http/project-registry.d.ts +0 -48
  28. package/dist/http/project-registry.d.ts.map +0 -1
  29. package/dist/http/project-registry.js +0 -119
  30. package/dist/http/project-registry.js.map +0 -1
  31. package/dist/http/session-router.d.ts +0 -33
  32. package/dist/http/session-router.d.ts.map +0 -1
  33. package/dist/http/session-router.js +0 -62
  34. package/dist/http/session-router.js.map +0 -1
  35. package/dist/http/status-endpoint.d.ts +0 -20
  36. package/dist/http/status-endpoint.d.ts.map +0 -1
  37. package/dist/http/status-endpoint.js +0 -85
  38. package/dist/http/status-endpoint.js.map +0 -1
  39. package/dist/http/transport.d.ts +0 -14
  40. package/dist/http/transport.d.ts.map +0 -1
  41. package/dist/http/transport.js +0 -209
  42. package/dist/http/transport.js.map +0 -1
  43. package/dist/index.d.ts +0 -4
  44. package/dist/index.d.ts.map +0 -1
  45. package/dist/index.js +0 -3
  46. package/dist/index.js.map +0 -1
  47. package/dist/routing/render-provider-routing-matrix.d.ts +0 -7
  48. package/dist/routing/render-provider-routing-matrix.d.ts.map +0 -1
  49. package/dist/routing/render-provider-routing-matrix.js +0 -153
  50. package/dist/routing/render-provider-routing-matrix.js.map +0 -1
  51. package/dist/status-cli.d.ts +0 -2
  52. package/dist/status-cli.d.ts.map +0 -1
  53. package/dist/status-cli.js +0 -68
  54. package/dist/status-cli.js.map +0 -1
  55. package/dist/tools/audit-document.d.ts +0 -23
  56. package/dist/tools/audit-document.d.ts.map +0 -1
  57. package/dist/tools/audit-document.js +0 -123
  58. package/dist/tools/audit-document.js.map +0 -1
  59. package/dist/tools/batch-response.d.ts +0 -14
  60. package/dist/tools/batch-response.d.ts.map +0 -1
  61. package/dist/tools/batch-response.js +0 -42
  62. package/dist/tools/batch-response.js.map +0 -1
  63. package/dist/tools/confirm-clarifications.d.ts +0 -15
  64. package/dist/tools/confirm-clarifications.d.ts.map +0 -1
  65. package/dist/tools/confirm-clarifications.js +0 -95
  66. package/dist/tools/confirm-clarifications.js.map +0 -1
  67. package/dist/tools/debug-task.d.ts +0 -13
  68. package/dist/tools/debug-task.d.ts.map +0 -1
  69. package/dist/tools/debug-task.js +0 -63
  70. package/dist/tools/debug-task.js.map +0 -1
  71. package/dist/tools/execute-plan.d.ts +0 -12
  72. package/dist/tools/execute-plan.d.ts.map +0 -1
  73. package/dist/tools/execute-plan.js +0 -123
  74. package/dist/tools/execute-plan.js.map +0 -1
  75. package/dist/tools/review-code.d.ts +0 -17
  76. package/dist/tools/review-code.d.ts.map +0 -1
  77. package/dist/tools/review-code.js +0 -108
  78. package/dist/tools/review-code.js.map +0 -1
  79. package/dist/tools/shared.d.ts +0 -72
  80. package/dist/tools/shared.d.ts.map +0 -1
  81. package/dist/tools/shared.js +0 -160
  82. package/dist/tools/shared.js.map +0 -1
  83. package/dist/tools/truncation.d.ts +0 -18
  84. package/dist/tools/truncation.d.ts.map +0 -1
  85. package/dist/tools/truncation.js +0 -62
  86. package/dist/tools/truncation.js.map +0 -1
  87. package/dist/tools/verify-work.d.ts +0 -12
  88. package/dist/tools/verify-work.d.ts.map +0 -1
  89. package/dist/tools/verify-work.js +0 -85
  90. package/dist/tools/verify-work.js.map +0 -1
@@ -1,48 +0,0 @@
1
- import { type ProjectContext } from '@zhixuan92/multi-model-agent-core';
2
- export type ReserveError = 'project_cap' | 'invalid_cwd' | 'missing_cwd' | 'cwd_not_dir';
3
- export type ReserveResult = {
4
- ok: true;
5
- projectContext: ProjectContext;
6
- created: boolean;
7
- } | {
8
- ok: false;
9
- error: ReserveError;
10
- message: string;
11
- };
12
- export interface ProjectRegistryOptions {
13
- cap: number;
14
- idleEvictionMs: number;
15
- evictionIntervalMs: number;
16
- onProjectCreated?: (cwd: string) => void;
17
- onProjectEvicted?: (cwd: string, idleMs: number) => void;
18
- }
19
- export declare class ProjectRegistry {
20
- private readonly map;
21
- private readonly cap;
22
- private readonly idleEvictionMs;
23
- private readonly evictionIntervalMs;
24
- private evictionTimer;
25
- private readonly onProjectCreated?;
26
- private readonly onProjectEvicted?;
27
- constructor(options: ProjectRegistryOptions);
28
- startEvictionTimer(): void;
29
- stopEvictionTimer(): void;
30
- /** Synchronous lookup-or-create with cap enforcement. Increments pendingReservations on success. */
31
- reserveProject(cwd: string): ReserveResult;
32
- /**
33
- * Called from onsessioninitialized. Decrements pendingReservations and registers the session.
34
- * `canonicalCwd` MUST be the `.cwd` value returned from a prior `reserveProject` call.
35
- */
36
- attachSession(canonicalCwd: string, sessionId: string): void;
37
- /** Detach a session. `canonicalCwd` must match `projectContext.cwd`. No-op if unknown. */
38
- detachSession(canonicalCwd: string, sessionId: string): void;
39
- /** Called if attachSession never fires. `canonicalCwd` must match `projectContext.cwd`. */
40
- cancelReservation(canonicalCwd: string): void;
41
- /** Look up a project by its canonical cwd. */
42
- get(canonicalCwd: string): ProjectContext | undefined;
43
- get size(): number;
44
- entries(): IterableIterator<[string, ProjectContext]>;
45
- evictIdle(): void;
46
- clear(): void;
47
- }
48
- //# sourceMappingURL=project-registry.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"project-registry.d.ts","sourceRoot":"","sources":["../../src/http/project-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAG9F,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,aAAa,GAAG,aAAa,GAAG,aAAa,CAAC;AAEzF,MAAM,MAAM,aAAa,GACrB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,cAAc,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC9D;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAExD,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;IACvB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1D;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAqC;IACzD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAwB;IAC1D,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAwC;gBAE9D,OAAO,EAAE,sBAAsB;IAQ3C,kBAAkB,IAAI,IAAI;IAM1B,iBAAiB,IAAI,IAAI;IAKzB,oGAAoG;IACpG,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa;IAoB1C;;;OAGG;IACH,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAQ5D,0FAA0F;IAC1F,aAAa,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAO5D,2FAA2F;IAC3F,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAO7C,8CAA8C;IAC9C,GAAG,CAAC,YAAY,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAIrD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAEA,OAAO,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAItD,SAAS,IAAI,IAAI;IAuBjB,KAAK,IAAI,IAAI;CAQd"}
@@ -1,119 +0,0 @@
1
- import { createProjectContext } from '@zhixuan92/multi-model-agent-core';
2
- import { validateCwd } from './cwd-validator.js';
3
- export class ProjectRegistry {
4
- map = new Map();
5
- cap;
6
- idleEvictionMs;
7
- evictionIntervalMs;
8
- evictionTimer = null;
9
- onProjectCreated;
10
- onProjectEvicted;
11
- constructor(options) {
12
- this.cap = options.cap;
13
- this.idleEvictionMs = options.idleEvictionMs;
14
- this.evictionIntervalMs = options.evictionIntervalMs;
15
- this.onProjectCreated = options.onProjectCreated;
16
- this.onProjectEvicted = options.onProjectEvicted;
17
- }
18
- startEvictionTimer() {
19
- if (this.evictionTimer)
20
- return;
21
- this.evictionTimer = setInterval(() => this.evictIdle(), this.evictionIntervalMs);
22
- this.evictionTimer.unref?.();
23
- }
24
- stopEvictionTimer() {
25
- if (this.evictionTimer)
26
- clearInterval(this.evictionTimer);
27
- this.evictionTimer = null;
28
- }
29
- /** Synchronous lookup-or-create with cap enforcement. Increments pendingReservations on success. */
30
- reserveProject(cwd) {
31
- const v = validateCwd(cwd);
32
- if (!v.ok)
33
- return { ok: false, error: v.error, message: v.message };
34
- const key = v.canonicalCwd;
35
- const existing = this.map.get(key);
36
- if (existing) {
37
- existing.pendingReservations += 1;
38
- existing.lastSeenAt = Date.now();
39
- return { ok: true, projectContext: existing, created: false };
40
- }
41
- if (this.map.size >= this.cap) {
42
- return { ok: false, error: 'project_cap', message: `server at ${this.cap} projects; close some connections and retry` };
43
- }
44
- const pc = createProjectContext(key);
45
- pc.pendingReservations = 1;
46
- this.map.set(key, pc);
47
- this.onProjectCreated?.(key);
48
- return { ok: true, projectContext: pc, created: true };
49
- }
50
- /**
51
- * Called from onsessioninitialized. Decrements pendingReservations and registers the session.
52
- * `canonicalCwd` MUST be the `.cwd` value returned from a prior `reserveProject` call.
53
- */
54
- attachSession(canonicalCwd, sessionId) {
55
- const pc = this.map.get(canonicalCwd);
56
- if (!pc)
57
- throw new Error(`attachSession: no project for ${canonicalCwd} (did you pass the raw cwd instead of projectContext.cwd?)`);
58
- pc.activeSessions.add(sessionId);
59
- if (pc.pendingReservations > 0)
60
- pc.pendingReservations -= 1;
61
- pc.lastSeenAt = Date.now();
62
- }
63
- /** Detach a session. `canonicalCwd` must match `projectContext.cwd`. No-op if unknown. */
64
- detachSession(canonicalCwd, sessionId) {
65
- const pc = this.map.get(canonicalCwd);
66
- if (!pc)
67
- return;
68
- pc.activeSessions.delete(sessionId);
69
- pc.lastSeenAt = Date.now();
70
- }
71
- /** Called if attachSession never fires. `canonicalCwd` must match `projectContext.cwd`. */
72
- cancelReservation(canonicalCwd) {
73
- const pc = this.map.get(canonicalCwd);
74
- if (!pc)
75
- return;
76
- if (pc.pendingReservations > 0)
77
- pc.pendingReservations -= 1;
78
- pc.lastSeenAt = Date.now();
79
- }
80
- /** Look up a project by its canonical cwd. */
81
- get(canonicalCwd) {
82
- return this.map.get(canonicalCwd);
83
- }
84
- get size() {
85
- return this.map.size;
86
- }
87
- *entries() {
88
- yield* this.map.entries();
89
- }
90
- evictIdle() {
91
- const now = Date.now();
92
- const victims = [];
93
- for (const [cwd, pc] of this.map.entries()) {
94
- if (pc.activeSessions.size === 0 &&
95
- pc.activeRequests === 0 &&
96
- pc.pendingReservations === 0 &&
97
- now - pc.lastSeenAt > this.idleEvictionMs) {
98
- victims.push(cwd);
99
- }
100
- }
101
- for (const cwd of victims) {
102
- const pc = this.map.get(cwd);
103
- pc.contextBlocks.clear();
104
- pc.batchCache.clear();
105
- pc.clarifications.clear();
106
- this.map.delete(cwd);
107
- this.onProjectEvicted?.(cwd, now - pc.lastSeenAt);
108
- }
109
- }
110
- clear() {
111
- for (const pc of this.map.values()) {
112
- pc.contextBlocks.clear();
113
- pc.batchCache.clear();
114
- pc.clarifications.clear();
115
- }
116
- this.map.clear();
117
- }
118
- }
119
- //# sourceMappingURL=project-registry.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"project-registry.js","sourceRoot":"","sources":["../../src/http/project-registry.ts"],"names":[],"mappings":"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"}
@@ -1,33 +0,0 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import type { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
3
- import type { ProjectContext } from '@zhixuan92/multi-model-agent-core';
4
- export interface SessionEntry {
5
- transport: StreamableHTTPServerTransport;
6
- server: McpServer;
7
- projectContext: ProjectContext;
8
- openedAt: number;
9
- lastRequestAt: number;
10
- }
11
- export declare class SessionRouter {
12
- private readonly map;
13
- set(sessionId: string, entry: SessionEntry): void;
14
- get(sessionId: string): SessionEntry | undefined;
15
- /**
16
- * Remove the entry from the map without calling transport.close() / server.close().
17
- * Use this from inside transport.onclose to avoid reentrant close calls.
18
- */
19
- delete(sessionId: string): void;
20
- /**
21
- * Remove the entry AND dispose its transport + server. Use from external shutdown paths
22
- * where the transport is still live. Idempotent; safe to call when the entry is already gone.
23
- */
24
- remove(sessionId: string): Promise<void>;
25
- touchSession(sessionId: string): void;
26
- evictIdleSessions(timeoutMs: number, options?: {
27
- onEvict?: (sessionId: string, entry: SessionEntry) => void;
28
- }): Promise<void>;
29
- closeAll(): Promise<void>;
30
- entries(): IterableIterator<[string, SessionEntry]>;
31
- get size(): number;
32
- }
33
- //# sourceMappingURL=session-router.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"session-router.d.ts","sourceRoot":"","sources":["../../src/http/session-router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACxG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAExE,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,6BAA6B,CAAC;IACzC,MAAM,EAAE,SAAS,CAAC;IAClB,cAAc,EAAE,cAAc,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAmC;IAEvD,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI;IAIjD,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIhD;;;OAGG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI/B;;;OAGG;IACG,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ9C,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAK/B,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QACnD,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;KAC5D,GAAG,OAAO,CAAC,IAAI,CAAC;IAYX,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAK9B,OAAO,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAIpD,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
@@ -1,62 +0,0 @@
1
- export class SessionRouter {
2
- map = new Map();
3
- set(sessionId, entry) {
4
- this.map.set(sessionId, entry);
5
- }
6
- get(sessionId) {
7
- return this.map.get(sessionId);
8
- }
9
- /**
10
- * Remove the entry from the map without calling transport.close() / server.close().
11
- * Use this from inside transport.onclose to avoid reentrant close calls.
12
- */
13
- delete(sessionId) {
14
- this.map.delete(sessionId);
15
- }
16
- /**
17
- * Remove the entry AND dispose its transport + server. Use from external shutdown paths
18
- * where the transport is still live. Idempotent; safe to call when the entry is already gone.
19
- */
20
- async remove(sessionId) {
21
- const entry = this.map.get(sessionId);
22
- if (!entry)
23
- return;
24
- this.map.delete(sessionId);
25
- try {
26
- await entry.transport.close();
27
- }
28
- catch { /* best-effort */ }
29
- try {
30
- await entry.server.close();
31
- }
32
- catch { /* best-effort */ }
33
- }
34
- touchSession(sessionId) {
35
- const entry = this.map.get(sessionId);
36
- if (entry)
37
- entry.lastRequestAt = Date.now();
38
- }
39
- async evictIdleSessions(timeoutMs, options) {
40
- const now = Date.now();
41
- const victims = [];
42
- for (const [id, entry] of this.map.entries()) {
43
- if (now - entry.lastRequestAt > timeoutMs)
44
- victims.push({ id, entry });
45
- }
46
- for (const { id, entry } of victims) {
47
- options?.onEvict?.(id, entry);
48
- await this.remove(id);
49
- }
50
- }
51
- async closeAll() {
52
- const ids = Array.from(this.map.keys());
53
- await Promise.all(ids.map(id => this.remove(id)));
54
- }
55
- *entries() {
56
- yield* this.map.entries();
57
- }
58
- get size() {
59
- return this.map.size;
60
- }
61
- }
62
- //# sourceMappingURL=session-router.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"session-router.js","sourceRoot":"","sources":["../../src/http/session-router.ts"],"names":[],"mappings":"AAYA,MAAM,OAAO,aAAa;IACP,GAAG,GAAG,IAAI,GAAG,EAAwB,CAAC;IAEvD,GAAG,CAAC,SAAiB,EAAE,KAAmB;QACxC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACjC,CAAC;IAED,GAAG,CAAC,SAAiB;QACnB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACjC,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,SAAiB;QACtB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC7B,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,MAAM,CAAC,SAAiB;QAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACtC,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC3B,IAAI,CAAC;YAAC,MAAM,KAAK,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;QAClE,IAAI,CAAC;YAAC,MAAM,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;IACjE,CAAC;IAED,YAAY,CAAC,SAAiB;QAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACtC,IAAI,KAAK;YAAE,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,SAAiB,EAAE,OAE1C;QACC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,OAAO,GAA+C,EAAE,CAAC;QAC/D,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC;YAC7C,IAAI,GAAG,GAAG,KAAK,CAAC,aAAa,GAAG,SAAS;gBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QACzE,CAAC;QACD,KAAK,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,OAAO,EAAE,CAAC;YACpC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;YAC9B,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QACxC,MAAM,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,CAAC,OAAO;QACN,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAC5B,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;IACvB,CAAC;CACF"}
@@ -1,20 +0,0 @@
1
- import type * as http from 'node:http';
2
- import type { ProjectRegistry } from './project-registry.js';
3
- import type { SessionRouter } from './session-router.js';
4
- import type { MultiModelConfig, DiagnosticLogger } from '@zhixuan92/multi-model-agent-core';
5
- export interface StatusHandlerOptions {
6
- registry: ProjectRegistry;
7
- router: SessionRouter;
8
- config: MultiModelConfig;
9
- logger: DiagnosticLogger;
10
- token: string;
11
- }
12
- export interface StatusHandler {
13
- (req: http.IncomingMessage, res: http.ServerResponse): void;
14
- updateBinding(host: string, port: number): void;
15
- trackRequestStart(sessionId: string, cwd: string, tool: string): string;
16
- trackRequestEnd(reqTrackId: string, status: 'ok' | 'error', durationMs: number): void;
17
- trackHeadline(reqTrackId: string, headline: string): void;
18
- }
19
- export declare function buildStatusHandler(options: StatusHandlerOptions): StatusHandler;
20
- //# sourceMappingURL=status-endpoint.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"status-endpoint.d.ts","sourceRoot":"","sources":["../../src/http/status-endpoint.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,IAAI,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAK5F,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,eAAe,CAAC;IAC1B,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,gBAAgB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;IAC5D,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACxE,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAG,OAAO,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACtF,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3D;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAuG/E"}
@@ -1,85 +0,0 @@
1
- import { SERVER_VERSION } from '../cli.js';
2
- import { isLoopbackAddress } from './loopback.js';
3
- import { validateAuthHeader } from './auth.js';
4
- export function buildStatusHandler(options) {
5
- const startedAtMs = Date.now();
6
- let boundHost = options.config.transport.http.bind;
7
- let boundPort = options.config.transport.http.port;
8
- const active = new Map();
9
- const recent = [];
10
- const RECENT_MAX = 100;
11
- const handler = ((req, res) => {
12
- const peer = req.socket.remoteAddress;
13
- if (!isLoopbackAddress(peer)) {
14
- res.writeHead(403, { 'Content-Type': 'application/json' });
15
- res.end(JSON.stringify({ error: 'forbidden', message: '/status is loopback-only' }));
16
- return;
17
- }
18
- if (options.config.transport.http.auth.enabled) {
19
- const auth = validateAuthHeader(Array.isArray(req.headers.authorization) ? req.headers.authorization[0] : req.headers.authorization, options.token);
20
- if (!auth.ok) {
21
- res.writeHead(401, { 'Content-Type': 'application/json' });
22
- res.end(JSON.stringify({ error: 'unauthorized' }));
23
- return;
24
- }
25
- }
26
- const projects = [];
27
- for (const [cwd, pc] of options.registry.entries()) {
28
- projects.push({
29
- cwd,
30
- createdAt: new Date(pc.createdAt).toISOString(),
31
- lastSeenAt: new Date(pc.lastSeenAt).toISOString(),
32
- activeSessions: pc.activeSessions.size,
33
- batchCacheSize: pc.batchCache.size,
34
- contextBlocksSize: pc.contextBlocks.size,
35
- clarificationsSize: pc.clarifications.size,
36
- });
37
- }
38
- const displayHost = boundHost.includes(':') ? `[${boundHost}]` : boundHost;
39
- const body = {
40
- version: SERVER_VERSION,
41
- pid: process.pid,
42
- transport: 'http',
43
- bind: `${displayHost}:${boundPort}`,
44
- uptimeMs: Date.now() - startedAtMs,
45
- auth: { enabled: options.config.transport.http.auth.enabled },
46
- projects,
47
- activeRequests: Array.from(active.values()).map(r => ({
48
- sessionId: r.sessionId,
49
- cwd: r.cwd,
50
- tool: r.tool,
51
- startedAt: new Date(r.startedAt).toISOString(),
52
- lastHeadline: r.lastHeadline,
53
- })),
54
- recent: recent.slice(-10),
55
- };
56
- res.writeHead(200, { 'Content-Type': 'application/json' });
57
- res.end(JSON.stringify(body));
58
- });
59
- handler.updateBinding = (host, port) => {
60
- boundHost = host;
61
- boundPort = port;
62
- };
63
- handler.trackRequestStart = (sessionId, cwd, tool) => {
64
- const id = `${sessionId}:${Date.now()}:${Math.random().toString(36).slice(2, 8)}`;
65
- active.set(id, { sessionId, cwd, tool, startedAt: Date.now() });
66
- return id;
67
- };
68
- handler.trackRequestEnd = (id, status, durationMs) => {
69
- const entry = active.get(id);
70
- if (!entry)
71
- return;
72
- active.delete(id);
73
- recent.push({ sessionId: entry.sessionId, cwd: entry.cwd, tool: entry.tool, status, durationMs });
74
- while (recent.length > RECENT_MAX)
75
- recent.shift();
76
- };
77
- handler.trackHeadline = (id, headline) => {
78
- const entry = active.get(id);
79
- if (!entry)
80
- return;
81
- entry.lastHeadline = headline;
82
- };
83
- return handler;
84
- }
85
- //# sourceMappingURL=status-endpoint.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"status-endpoint.js","sourceRoot":"","sources":["../../src/http/status-endpoint.ts"],"names":[],"mappings":"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"}
@@ -1,14 +0,0 @@
1
- import { type DiagnosticLogger, type MultiModelConfig } from '@zhixuan92/multi-model-agent-core';
2
- import { ProjectRegistry } from './project-registry.js';
3
- import { SessionRouter } from './session-router.js';
4
- export interface DaemonHandle {
5
- url: string;
6
- logger: DiagnosticLogger;
7
- registry: ProjectRegistry;
8
- router: SessionRouter;
9
- stop: () => Promise<void>;
10
- }
11
- export declare function startHttpDaemon(config: MultiModelConfig, options?: {
12
- testMode?: boolean;
13
- }): Promise<DaemonHandle>;
14
- //# sourceMappingURL=transport.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/http/transport.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACtB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAqB,MAAM,qBAAqB,CAAC;AAQvE,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,gBAAgB,CAAC;IACzB,QAAQ,EAAE,eAAe,CAAC;IAC1B,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,gBAAgB,EACxB,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/B,OAAO,CAAC,YAAY,CAAC,CA2NvB"}
@@ -1,209 +0,0 @@
1
- import * as http from 'node:http';
2
- import { randomUUID } from 'node:crypto';
3
- import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
4
- import { createDiagnosticLogger, } from '@zhixuan92/multi-model-agent-core';
5
- import { buildMcpServer, SERVER_VERSION } from '../cli.js';
6
- import { ProjectRegistry } from './project-registry.js';
7
- import { SessionRouter } from './session-router.js';
8
- import { installHttpLifecycleHandlers } from './lifecycle-handlers.js';
9
- import { isLoopbackAddress } from './loopback.js';
10
- import { loadToken, validateAuthHeader } from './auth.js';
11
- import { buildStatusHandler } from './status-endpoint.js';
12
- const RESERVATION_TIMEOUT_MS = 5000;
13
- export async function startHttpDaemon(config, options) {
14
- const transportConfig = config.transport;
15
- if (transportConfig.mode !== 'http') {
16
- throw new Error(`startHttpDaemon called with transport.mode=${transportConfig.mode}`);
17
- }
18
- const httpConfig = transportConfig.http;
19
- if (!isLoopbackAddress(httpConfig.bind) && !httpConfig.auth.enabled) {
20
- throw new Error(`transport.http.bind='${httpConfig.bind}' is not loopback; transport.http.auth.enabled must be true to bind non-loopback`);
21
- }
22
- const enabled = config.diagnostics?.log ?? false;
23
- const logger = createDiagnosticLogger({ enabled, logDir: config.diagnostics?.logDir });
24
- logger.startup(SERVER_VERSION, { transport: 'http' });
25
- const token = httpConfig.auth.enabled ? loadToken(httpConfig.auth.tokenPath) : '';
26
- const registry = new ProjectRegistry({
27
- cap: httpConfig.projectCap,
28
- idleEvictionMs: httpConfig.projectIdleEvictionMs,
29
- evictionIntervalMs: 5 * 60 * 1000,
30
- onProjectCreated: (cwd) => logger.projectCreated({ cwd }),
31
- onProjectEvicted: (cwd, idleMs) => logger.projectEvicted({ cwd, idleMs }),
32
- });
33
- registry.startEvictionTimer();
34
- const router = new SessionRouter();
35
- const SESSION_EVICTION_INTERVAL_MS = 60_000; // check every 1 min
36
- const sessionEvictionTimer = setInterval(() => {
37
- void router.evictIdleSessions(httpConfig.sessionIdleTimeoutMs, {
38
- onEvict: (sessionId, entry) => {
39
- const durationMs = Date.now() - entry.openedAt;
40
- registry.detachSession(entry.projectContext.cwd, sessionId);
41
- logger.sessionClose({ sessionId, cwd: entry.projectContext.cwd, reason: 'session_expired', durationMs });
42
- },
43
- });
44
- }, SESSION_EVICTION_INTERVAL_MS);
45
- sessionEvictionTimer.unref?.();
46
- const statusHandler = buildStatusHandler({ registry, router, config, logger, token });
47
- const handleMcp = async (req, res, body) => {
48
- const u = new URL(req.url ?? '/', 'http://localhost');
49
- if (httpConfig.auth.enabled) {
50
- if (u.searchParams.has('token')) {
51
- logger.connectionRejected({ reason: 'unauthorized', httpStatus: 401, cwd: u.searchParams.get('cwd') ?? undefined });
52
- res.writeHead(401, { 'Content-Type': 'application/json' });
53
- res.end(JSON.stringify({ error: 'unauthorized', message: 'token must be sent as Authorization header, not query param' }));
54
- return;
55
- }
56
- const auth = validateAuthHeader(Array.isArray(req.headers.authorization) ? req.headers.authorization[0] : req.headers.authorization, token);
57
- if (!auth.ok) {
58
- logger.connectionRejected({ reason: 'unauthorized', httpStatus: 401 });
59
- res.writeHead(401, { 'Content-Type': 'application/json' });
60
- res.end(JSON.stringify({ error: 'unauthorized' }));
61
- return;
62
- }
63
- }
64
- const sessionIdHeader = req.headers['mcp-session-id'];
65
- const existingSessionId = Array.isArray(sessionIdHeader) ? sessionIdHeader[0] : sessionIdHeader;
66
- let parsedBody;
67
- try {
68
- parsedBody = body.length > 0 ? JSON.parse(body.toString('utf8')) : undefined;
69
- }
70
- catch {
71
- res.writeHead(400, { 'Content-Type': 'application/json' });
72
- res.end(JSON.stringify({ error: 'invalid_json' }));
73
- return;
74
- }
75
- if (existingSessionId) {
76
- const entry = router.get(existingSessionId);
77
- if (!entry) {
78
- logger.requestRejected({ reason: 'unknown_session', httpStatus: 404, sessionIdAttempted: existingSessionId });
79
- res.writeHead(404, { 'Content-Type': 'application/json' });
80
- res.end(JSON.stringify({ error: 'unknown_session' }));
81
- return;
82
- }
83
- entry.projectContext.lastSeenAt = Date.now();
84
- router.touchSession(existingSessionId);
85
- try {
86
- await entry.transport.handleRequest(req, res, parsedBody);
87
- }
88
- catch (err) {
89
- logger.error('transport_handle_request', err);
90
- if (!res.headersSent) {
91
- res.writeHead(500);
92
- res.end();
93
- }
94
- }
95
- return;
96
- }
97
- const isInitialize = parsedBody !== null
98
- && typeof parsedBody === 'object'
99
- && parsedBody.method === 'initialize';
100
- if (!isInitialize) {
101
- logger.requestRejected({ reason: 'missing_session_or_initialize', httpStatus: 400 });
102
- res.writeHead(400, { 'Content-Type': 'application/json' });
103
- res.end(JSON.stringify({ error: 'missing_session_or_initialize', message: 'first request must be JSON-RPC initialize; follow-up requests must carry Mcp-Session-Id' }));
104
- return;
105
- }
106
- const cwdParam = u.searchParams.get('cwd') ?? undefined;
107
- const reserveResult = registry.reserveProject(cwdParam ?? '');
108
- if (!reserveResult.ok) {
109
- const status = reserveResult.error === 'project_cap' ? 503 : 400;
110
- logger.connectionRejected({ reason: reserveResult.error, httpStatus: status, cwd: cwdParam });
111
- res.writeHead(status, { 'Content-Type': 'application/json' });
112
- res.end(JSON.stringify({ error: reserveResult.error, message: reserveResult.message }));
113
- return;
114
- }
115
- const { projectContext } = reserveResult;
116
- const canonicalCwd = projectContext.cwd;
117
- const reservationTimeout = setTimeout(() => {
118
- registry.cancelReservation(canonicalCwd);
119
- }, RESERVATION_TIMEOUT_MS);
120
- reservationTimeout.unref?.();
121
- let sessionIdCaptured;
122
- const mcpServer = buildMcpServer(config, logger, { projectContext });
123
- const transport = new StreamableHTTPServerTransport({
124
- sessionIdGenerator: () => randomUUID(),
125
- onsessioninitialized: (sessionId) => {
126
- clearTimeout(reservationTimeout);
127
- sessionIdCaptured = sessionId;
128
- registry.attachSession(canonicalCwd, sessionId);
129
- const openedAt = Date.now();
130
- const entry = { transport, server: mcpServer, projectContext, openedAt, lastRequestAt: openedAt };
131
- router.set(sessionId, entry);
132
- logger.sessionOpen({ sessionId, cwd: canonicalCwd, remoteAddr: req.socket.remoteAddress });
133
- },
134
- });
135
- transport.onclose = () => {
136
- if (!sessionIdCaptured)
137
- return;
138
- const entry = router.get(sessionIdCaptured);
139
- if (!entry)
140
- return;
141
- const durationMs = Date.now() - entry.openedAt;
142
- registry.detachSession(canonicalCwd, sessionIdCaptured);
143
- logger.sessionClose({ sessionId: sessionIdCaptured, cwd: canonicalCwd, reason: 'client_closed', durationMs });
144
- router.delete(sessionIdCaptured);
145
- };
146
- try {
147
- await mcpServer.connect(transport);
148
- await transport.handleRequest(req, res, parsedBody);
149
- }
150
- catch (err) {
151
- clearTimeout(reservationTimeout);
152
- registry.cancelReservation(canonicalCwd);
153
- logger.error('initialize_failed', err);
154
- if (!res.headersSent) {
155
- res.writeHead(500, { 'Content-Type': 'application/json' });
156
- res.end(JSON.stringify({ error: 'initialize_failed' }));
157
- }
158
- }
159
- };
160
- const httpServer = http.createServer((req, res) => {
161
- const chunks = [];
162
- req.on('data', (c) => chunks.push(c));
163
- req.on('end', () => {
164
- const body = Buffer.concat(chunks);
165
- const u = new URL(req.url ?? '/', 'http://localhost');
166
- if (u.pathname === '/status') {
167
- statusHandler(req, res);
168
- return;
169
- }
170
- if (u.pathname === '/') {
171
- void handleMcp(req, res, body);
172
- return;
173
- }
174
- res.writeHead(404);
175
- res.end();
176
- });
177
- });
178
- await new Promise((resolve, reject) => {
179
- httpServer.once('error', reject);
180
- httpServer.listen(httpConfig.port, httpConfig.bind, () => {
181
- httpServer.removeListener('error', reject);
182
- resolve();
183
- });
184
- });
185
- const addr = httpServer.address();
186
- const boundPort = typeof addr === 'object' && addr !== null ? addr.port : httpConfig.port;
187
- const boundHost = typeof addr === 'object' && addr !== null ? addr.address : httpConfig.bind;
188
- const urlHost = boundHost.includes(':') ? `[${boundHost}]` : boundHost;
189
- const url = `http://${urlHost}:${boundPort}`;
190
- statusHandler.updateBinding?.(boundHost, boundPort);
191
- if (!options?.testMode) {
192
- process.stderr.write(`[multi-model-agent] HTTP: ${url} (auth: ${httpConfig.auth.enabled ? 'on' : 'off'})\n`);
193
- const logPath = logger.expectedPath();
194
- if (logPath)
195
- process.stderr.write(`[multi-model-agent] diagnostic log: ${logPath}\n`);
196
- installHttpLifecycleHandlers(logger, httpServer, registry, router, {
197
- shutdownDrainMs: httpConfig.shutdownDrainMs,
198
- });
199
- }
200
- const stop = async () => {
201
- clearInterval(sessionEvictionTimer);
202
- registry.stopEvictionTimer();
203
- await router.closeAll();
204
- await new Promise((resolve) => httpServer.close(() => resolve()));
205
- registry.clear();
206
- };
207
- return { url, logger, registry, router, stop };
208
- }
209
- //# sourceMappingURL=transport.js.map