mcp-coordinator 0.6.1 → 0.7.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/README.md +24 -0
- package/dist/src/agent-activity.d.ts +13 -9
- package/dist/src/agent-activity.js +45 -24
- package/dist/src/agent-registry.d.ts +7 -7
- package/dist/src/agent-registry.js +19 -18
- package/dist/src/announce-workflow.d.ts +1 -0
- package/dist/src/announce-workflow.js +13 -12
- package/dist/src/auth/providers/registry.d.ts +4 -0
- package/dist/src/auth/providers/registry.js +7 -0
- package/dist/src/auth/providers/types.d.ts +11 -0
- package/dist/src/auth/providers/types.js +1 -0
- package/dist/src/auth.d.ts +24 -5
- package/dist/src/auth.js +172 -23
- package/dist/src/conflict-detector.d.ts +1 -0
- package/dist/src/conflict-detector.js +4 -4
- package/dist/src/consultation.d.ts +28 -14
- package/dist/src/consultation.js +101 -68
- package/dist/src/context-provider.d.ts +2 -2
- package/dist/src/context-provider.js +3 -4
- package/dist/src/database.js +203 -4
- package/dist/src/dependency-map.d.ts +25 -4
- package/dist/src/dependency-map.js +49 -11
- package/dist/src/file-tracker.d.ts +5 -4
- package/dist/src/file-tracker.js +16 -14
- package/dist/src/git-cochange-builder.d.ts +11 -2
- package/dist/src/git-cochange-builder.js +15 -7
- package/dist/src/http/handle-health.d.ts +9 -5
- package/dist/src/http/handle-health.js +22 -8
- package/dist/src/http/handle-rest.d.ts +3 -0
- package/dist/src/http/handle-rest.js +56 -55
- package/dist/src/http/utils.d.ts +4 -0
- package/dist/src/http/utils.js +7 -1
- package/dist/src/impact-scorer.d.ts +3 -0
- package/dist/src/impact-scorer.js +65 -51
- package/dist/src/introspection.d.ts +13 -7
- package/dist/src/introspection.js +34 -11
- package/dist/src/metrics.js +2 -1
- package/dist/src/mqtt-bridge.d.ts +3 -2
- package/dist/src/mqtt-bridge.js +33 -23
- package/dist/src/mqtt-broker.d.ts +16 -7
- package/dist/src/mqtt-broker.js +57 -15
- package/dist/src/security/audit.d.ts +11 -0
- package/dist/src/security/audit.js +7 -0
- package/dist/src/security/encryption.d.ts +17 -0
- package/dist/src/security/encryption.js +5 -0
- package/dist/src/serve-http.js +136 -57
- package/dist/src/server-setup.d.ts +12 -2
- package/dist/src/server-setup.js +33 -15
- package/dist/src/sse-emitter.d.ts +7 -4
- package/dist/src/sse-emitter.js +27 -21
- package/dist/src/tools/agents-tools.d.ts +2 -1
- package/dist/src/tools/agents-tools.js +36 -12
- package/dist/src/tools/consultation-tools.d.ts +2 -1
- package/dist/src/tools/consultation-tools.js +102 -36
- package/dist/src/tools/dependencies-tools.d.ts +2 -1
- package/dist/src/tools/dependencies-tools.js +25 -7
- package/dist/src/tools/files-tools.d.ts +2 -1
- package/dist/src/tools/files-tools.js +25 -7
- package/dist/src/tools/mqtt-tools.d.ts +7 -1
- package/dist/src/tools/mqtt-tools.js +27 -4
- package/dist/src/tools/status-tools.d.ts +7 -1
- package/dist/src/tools/status-tools.js +26 -9
- package/dist/src/types.d.ts +2 -0
- package/dist/src/working-files-tracker.d.ts +21 -11
- package/dist/src/working-files-tracker.js +32 -21
- package/package.json +1 -1
package/dist/src/auth.js
CHANGED
|
@@ -3,56 +3,143 @@ import { randomUUID } from "crypto";
|
|
|
3
3
|
import { getDb } from "./database.js";
|
|
4
4
|
import { silentLogger } from "./logger.js";
|
|
5
5
|
let signingKey;
|
|
6
|
+
let prevKey = null;
|
|
6
7
|
let defaultExpiry = "24h";
|
|
7
8
|
let log = silentLogger;
|
|
8
9
|
export function setAuthLogger(logger) {
|
|
9
10
|
log = logger;
|
|
10
11
|
}
|
|
11
|
-
export function initAuth(secret, expiry) {
|
|
12
|
+
export function initAuth(secret, expiry, options = {}) {
|
|
12
13
|
signingKey = new TextEncoder().encode(secret);
|
|
14
|
+
prevKey = options.prevSecret ? new TextEncoder().encode(options.prevSecret) : null;
|
|
13
15
|
if (expiry)
|
|
14
16
|
defaultExpiry = expiry;
|
|
15
17
|
}
|
|
16
|
-
export async function createToken(agentId, role, expiry) {
|
|
17
|
-
|
|
18
|
+
export async function createToken(agentId, role, expiry, options = {}) {
|
|
19
|
+
const jti = randomUUID();
|
|
20
|
+
return new SignJWT({
|
|
21
|
+
role,
|
|
22
|
+
user_id: options.user_id ?? agentId,
|
|
23
|
+
org: options.org ?? "default",
|
|
24
|
+
})
|
|
18
25
|
.setProtectedHeader({ alg: "HS256" })
|
|
19
26
|
.setSubject(agentId)
|
|
20
|
-
.setJti(
|
|
27
|
+
.setJti(jti)
|
|
21
28
|
.setIssuedAt()
|
|
22
29
|
.setExpirationTime(expiry || defaultExpiry)
|
|
23
30
|
.sign(signingKey);
|
|
24
31
|
}
|
|
25
32
|
export async function verifyToken(token) {
|
|
26
|
-
|
|
33
|
+
let payload;
|
|
34
|
+
try {
|
|
35
|
+
({ payload } = await jwtVerify(token, signingKey, { algorithms: ["HS256"] }));
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
if (prevKey && err instanceof errors.JWSSignatureVerificationFailed) {
|
|
39
|
+
({ payload } = await jwtVerify(token, prevKey, { algorithms: ["HS256"] }));
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
27
45
|
if (!payload.sub)
|
|
28
46
|
throw new Error("Missing sub claim in token");
|
|
29
47
|
const role = payload.role;
|
|
30
|
-
if (role !== "agent" && role !== "admin")
|
|
48
|
+
if (role !== "agent" && role !== "admin" && role !== "member") {
|
|
31
49
|
throw new Error("Invalid role in token");
|
|
32
|
-
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
sub: payload.sub,
|
|
53
|
+
role,
|
|
54
|
+
user_id: typeof payload.user_id === "string" ? payload.user_id : "legacy",
|
|
55
|
+
org: typeof payload.org === "string" ? payload.org : "default",
|
|
56
|
+
jti: typeof payload.jti === "string" ? payload.jti : randomUUID(),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export async function verifyTokenStrict(token) {
|
|
60
|
+
let payload;
|
|
61
|
+
try {
|
|
62
|
+
({ payload } = await jwtVerify(token, signingKey, { algorithms: ["HS256"] }));
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
if (prevKey && err instanceof errors.JWSSignatureVerificationFailed) {
|
|
66
|
+
({ payload } = await jwtVerify(token, prevKey, { algorithms: ["HS256"] }));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!payload.sub)
|
|
73
|
+
throw new Error("Missing sub claim in token");
|
|
74
|
+
// Tolerate missing/unknown role on v0.6 tokens. Default to 'member' (LEAST PRIVILEGE).
|
|
75
|
+
const rawRole = payload.role;
|
|
76
|
+
const role = rawRole === "agent" || rawRole === "admin" || rawRole === "member"
|
|
77
|
+
? rawRole
|
|
78
|
+
: "member";
|
|
79
|
+
// v0.7 detection: BOTH user_id AND org must be present strings.
|
|
80
|
+
const hasV07 = typeof payload.user_id === "string" && typeof payload.org === "string";
|
|
81
|
+
return {
|
|
82
|
+
claims: {
|
|
83
|
+
sub: payload.sub, role,
|
|
84
|
+
user_id: typeof payload.user_id === "string" ? payload.user_id : "legacy",
|
|
85
|
+
org: typeof payload.org === "string" ? payload.org : "default",
|
|
86
|
+
jti: typeof payload.jti === "string" ? payload.jti : randomUUID(),
|
|
87
|
+
},
|
|
88
|
+
wasLegacy: !hasV07,
|
|
89
|
+
};
|
|
33
90
|
}
|
|
34
|
-
export async function refreshToken(token, gracePeriod
|
|
91
|
+
export async function refreshToken(token, options, gracePeriod) {
|
|
92
|
+
const authEnabled = options.authEnabled;
|
|
93
|
+
const grace = gracePeriod ?? "1h";
|
|
35
94
|
let claims;
|
|
36
95
|
try {
|
|
37
|
-
claims = await
|
|
96
|
+
const { claims: c, wasLegacy } = await verifyTokenStrict(token);
|
|
97
|
+
if (wasLegacy && authEnabled) {
|
|
98
|
+
throw new Error("v0.6 token rejected: upgrade required (AUTH_ENABLED=true)");
|
|
99
|
+
}
|
|
100
|
+
claims = c;
|
|
38
101
|
}
|
|
39
102
|
catch (err) {
|
|
40
103
|
if (err instanceof errors.JWTExpired) {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
104
|
+
const verifyWith = async (key) => jwtVerify(token, key, { clockTolerance: grace, algorithms: ["HS256"] });
|
|
105
|
+
let payload;
|
|
106
|
+
try {
|
|
107
|
+
({ payload } = await verifyWith(signingKey));
|
|
108
|
+
}
|
|
109
|
+
catch (err2) {
|
|
110
|
+
if (prevKey && err2 instanceof errors.JWSSignatureVerificationFailed) {
|
|
111
|
+
({ payload } = await verifyWith(prevKey));
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
throw err2;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
44
117
|
if (!payload.sub)
|
|
45
118
|
throw new Error("Missing sub claim in token");
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
119
|
+
// Tolerate missing/unknown role on v0.6 tokens. Default to 'member' (LEAST PRIVILEGE).
|
|
120
|
+
const rawRole = payload.role;
|
|
121
|
+
const role = rawRole === "agent" || rawRole === "admin" || rawRole === "member"
|
|
122
|
+
? rawRole
|
|
123
|
+
: "member";
|
|
124
|
+
const hasV07 = typeof payload.user_id === "string" && typeof payload.org === "string";
|
|
125
|
+
if (!hasV07 && authEnabled) {
|
|
126
|
+
throw new Error("v0.6 token rejected: upgrade required (AUTH_ENABLED=true)");
|
|
127
|
+
}
|
|
128
|
+
claims = {
|
|
129
|
+
sub: payload.sub, role,
|
|
130
|
+
user_id: typeof payload.user_id === "string" ? payload.user_id : "legacy",
|
|
131
|
+
org: typeof payload.org === "string" ? payload.org : "default",
|
|
132
|
+
jti: typeof payload.jti === "string" ? payload.jti : randomUUID(),
|
|
133
|
+
};
|
|
50
134
|
}
|
|
51
135
|
else {
|
|
52
136
|
throw err;
|
|
53
137
|
}
|
|
54
138
|
}
|
|
55
|
-
return createToken(claims.sub, claims.role
|
|
139
|
+
return createToken(claims.sub, claims.role, undefined, {
|
|
140
|
+
user_id: claims.user_id,
|
|
141
|
+
org: claims.org,
|
|
142
|
+
});
|
|
56
143
|
}
|
|
57
144
|
export function isRevoked(agentId) {
|
|
58
145
|
const db = getDb();
|
|
@@ -61,23 +148,85 @@ export function isRevoked(agentId) {
|
|
|
61
148
|
}
|
|
62
149
|
export function revokeAgent(agentId, revokedBy) {
|
|
63
150
|
const db = getDb();
|
|
151
|
+
// INTENTIONALLY cross-org: revoked_agents is a global blocklist by design.
|
|
152
|
+
// A revocation issued by an admin must be effective across every org where
|
|
153
|
+
// that agent_id appears — there is no per-org revocation in Phase 1.
|
|
64
154
|
db.prepare("INSERT OR IGNORE INTO revoked_agents (agent_id, revoked_by) VALUES (?, ?)").run(agentId, revokedBy);
|
|
65
155
|
}
|
|
66
156
|
const ADMIN_ONLY_ROUTES = ["/api/auth/revoke", "/api/reset"];
|
|
67
|
-
export async function authenticateRequest(req) {
|
|
157
|
+
export async function authenticateRequest(req, options = { authEnabled: true }) {
|
|
158
|
+
const { authEnabled } = options;
|
|
68
159
|
const authHeader = req.headers.authorization;
|
|
69
|
-
|
|
70
|
-
|
|
160
|
+
// EventSource-compatible token transport: allow ?token=<JWT> on GET requests.
|
|
161
|
+
// POST/PUT/PATCH are excluded (smuggling defense: POST endpoints use the body
|
|
162
|
+
// as a credential channel, so hoisting a query param to auth would let an
|
|
163
|
+
// attacker lure a victim's browser into a CSRF-authenticated POST).
|
|
164
|
+
// Authorization header always takes precedence when both are present.
|
|
165
|
+
let effectiveAuthHeader = authHeader;
|
|
166
|
+
if (!effectiveAuthHeader && req.method === "GET") {
|
|
167
|
+
try {
|
|
168
|
+
// req.url may be relative (e.g. "/api/events?token=…") — prepend a dummy
|
|
169
|
+
// base so URL can parse it. Attacker-controlled, so we wrap in try/catch.
|
|
170
|
+
const parsed = new URL(req.url ?? "", "http://localhost");
|
|
171
|
+
const qToken = parsed.searchParams.get("token");
|
|
172
|
+
if (qToken) {
|
|
173
|
+
effectiveAuthHeader = `Bearer ${qToken}`;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
// Malformed URL — no fallback, fall through to scenario (b) 401.
|
|
178
|
+
}
|
|
71
179
|
}
|
|
72
|
-
|
|
180
|
+
// Scenario (a)/(b): No Authorization header (and no ?token= fallback)
|
|
181
|
+
if (!effectiveAuthHeader || !effectiveAuthHeader.startsWith("Bearer ")) {
|
|
182
|
+
if (!authEnabled) {
|
|
183
|
+
// Scenario (a): AUTH_ENABLED=false → inject synthetic legacy claims
|
|
184
|
+
return {
|
|
185
|
+
ok: true,
|
|
186
|
+
claims: {
|
|
187
|
+
sub: "legacy",
|
|
188
|
+
user_id: "legacy",
|
|
189
|
+
org: "default",
|
|
190
|
+
role: "admin",
|
|
191
|
+
jti: randomUUID(),
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
// Scenario (b): AUTH_ENABLED=true → 401 with WWW-Authenticate
|
|
196
|
+
return {
|
|
197
|
+
ok: false,
|
|
198
|
+
status: 401,
|
|
199
|
+
error: "Missing or invalid Authorization header",
|
|
200
|
+
wwwAuthenticate: 'Bearer realm="mcp-coordinator", error="invalid_token"',
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
// Has a Bearer token — verify it
|
|
204
|
+
const token = effectiveAuthHeader.slice(7);
|
|
73
205
|
let claims;
|
|
206
|
+
let wasLegacy;
|
|
74
207
|
try {
|
|
75
|
-
claims = await
|
|
208
|
+
({ claims, wasLegacy } = await verifyTokenStrict(token));
|
|
76
209
|
}
|
|
77
210
|
catch (err) {
|
|
78
211
|
log.error({ err }, "JWT verification error");
|
|
79
|
-
|
|
212
|
+
const isExpired = err instanceof errors.JWTExpired;
|
|
213
|
+
return {
|
|
214
|
+
ok: false,
|
|
215
|
+
status: 401,
|
|
216
|
+
error: isExpired ? "Token expired" : "Invalid or expired token",
|
|
217
|
+
wwwAuthenticate: `Bearer realm="mcp-coordinator", error="${isExpired ? "expired_token" : "invalid_token"}"`,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
// Scenario (c): v0.6 token (wasLegacy=true) under AUTH_ENABLED=true → reject
|
|
221
|
+
if (wasLegacy && authEnabled) {
|
|
222
|
+
return {
|
|
223
|
+
ok: false,
|
|
224
|
+
status: 401,
|
|
225
|
+
error: "v0.6 token rejected: upgrade required (AUTH_ENABLED=true)",
|
|
226
|
+
wwwAuthenticate: 'Bearer realm="mcp-coordinator", error="invalid_token"',
|
|
227
|
+
};
|
|
80
228
|
}
|
|
229
|
+
// Scenario (c) AUTH_ENABLED=false or Scenario (d): proceed with claims
|
|
81
230
|
if (isRevoked(claims.sub)) {
|
|
82
231
|
return { ok: false, status: 403, error: "Agent has been revoked" };
|
|
83
232
|
}
|
|
@@ -10,6 +10,7 @@ export declare class ConflictDetector {
|
|
|
10
10
|
private log;
|
|
11
11
|
constructor(consultation: Consultation, depMap: DependencyMapper, fileTracker: FileTracker, logger?: Logger);
|
|
12
12
|
detect(params: {
|
|
13
|
+
org_id: string;
|
|
13
14
|
agent_id: string;
|
|
14
15
|
target_modules: string[];
|
|
15
16
|
target_files: string[];
|
|
@@ -13,7 +13,7 @@ export class ConflictDetector {
|
|
|
13
13
|
detect(params) {
|
|
14
14
|
const conflicts = [];
|
|
15
15
|
// Include open, resolving, and recently resolved (auto-quorum) threads — exclude only cancelled
|
|
16
|
-
const allThreads = this.consultation.listThreads({});
|
|
16
|
+
const allThreads = this.consultation.listThreads(params.org_id, {});
|
|
17
17
|
const activeThreads = allThreads.filter((t) => t.status !== "cancelled");
|
|
18
18
|
for (const thread of activeThreads) {
|
|
19
19
|
if (thread.initiator_id === params.agent_id)
|
|
@@ -46,7 +46,7 @@ export class ConflictDetector {
|
|
|
46
46
|
}
|
|
47
47
|
// 3. Dependency chain
|
|
48
48
|
for (const targetModule of params.target_modules) {
|
|
49
|
-
const info = this.depMap.getModuleInfo(targetModule);
|
|
49
|
+
const info = this.depMap.getModuleInfo(params.org_id, targetModule);
|
|
50
50
|
if (!info)
|
|
51
51
|
continue;
|
|
52
52
|
for (const dep of info.depends_on) {
|
|
@@ -62,7 +62,7 @@ export class ConflictDetector {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
// Reverse: someone depends on what we're modifying
|
|
65
|
-
const radius = this.depMap.getBlastRadius(targetModule);
|
|
65
|
+
const radius = this.depMap.getBlastRadius(params.org_id, targetModule);
|
|
66
66
|
this.log.debug({
|
|
67
67
|
module_id: targetModule,
|
|
68
68
|
direct_dependents: radius.direct_dependents,
|
|
@@ -84,7 +84,7 @@ export class ConflictDetector {
|
|
|
84
84
|
}
|
|
85
85
|
// 4. Hot file overlap (from actual file activity, not just declared files)
|
|
86
86
|
for (const targetFile of params.target_files) {
|
|
87
|
-
const activity = this.fileTracker.checkFileConflict(targetFile, params.agent_id, 60);
|
|
87
|
+
const activity = this.fileTracker.checkFileConflict(params.org_id, targetFile, params.agent_id, 60);
|
|
88
88
|
if (activity.conflict) {
|
|
89
89
|
for (const otherAgent of activity.agents) {
|
|
90
90
|
// Avoid duplicating with file_overlap already detected
|
|
@@ -2,6 +2,7 @@ import { type Logger } from "./logger.js";
|
|
|
2
2
|
import type { Thread, ThreadMessage, ActionSummary, MessageType, ResolutionType } from "./types.js";
|
|
3
3
|
export interface ResolutionEvent {
|
|
4
4
|
thread_id: string;
|
|
5
|
+
org_id: string;
|
|
5
6
|
resolution_type: ResolutionType;
|
|
6
7
|
resolution_summary: string | null;
|
|
7
8
|
created_at: string;
|
|
@@ -30,7 +31,7 @@ export declare class Consultation {
|
|
|
30
31
|
startTimeoutSweeper(intervalMs?: number): void;
|
|
31
32
|
stopTimeoutSweeper(): void;
|
|
32
33
|
emitResolution(threadId: string, type: ResolutionType, approvedBy?: string, approvedByName?: string): void;
|
|
33
|
-
announceWork(params: {
|
|
34
|
+
announceWork(orgId: string, params: {
|
|
34
35
|
agent_id: string;
|
|
35
36
|
subject: string;
|
|
36
37
|
plan?: string;
|
|
@@ -41,7 +42,7 @@ export declare class Consultation {
|
|
|
41
42
|
keep_open?: boolean;
|
|
42
43
|
assigned_to?: string | null;
|
|
43
44
|
}): Thread;
|
|
44
|
-
postToThread(params: {
|
|
45
|
+
postToThread(orgId: string, params: {
|
|
45
46
|
thread_id: string;
|
|
46
47
|
agent_id: string;
|
|
47
48
|
agent_name?: string;
|
|
@@ -50,19 +51,27 @@ export declare class Consultation {
|
|
|
50
51
|
context_snapshot?: string;
|
|
51
52
|
in_reply_to?: string;
|
|
52
53
|
}): ThreadMessage;
|
|
53
|
-
proposeResolution(threadId: string, agentId: string, summary: string): void;
|
|
54
|
-
approveResolution(threadId: string, agentId: string, agentName?: string): void;
|
|
55
|
-
contestResolution(threadId: string, agentId: string, reason: string): void;
|
|
56
|
-
cancelThread(threadId: string, agentId: string, reason?: string): void;
|
|
57
|
-
closeThread(threadId: string, agentId: string, summary: string): void;
|
|
54
|
+
proposeResolution(orgId: string, threadId: string, agentId: string, summary: string): void;
|
|
55
|
+
approveResolution(orgId: string, threadId: string, agentId: string, agentName?: string): void;
|
|
56
|
+
contestResolution(orgId: string, threadId: string, agentId: string, reason: string): void;
|
|
57
|
+
cancelThread(orgId: string, threadId: string, agentId: string, reason?: string): void;
|
|
58
|
+
closeThread(orgId: string, threadId: string, agentId: string, summary: string): void;
|
|
59
|
+
/**
|
|
60
|
+
* Cross-org maintenance sweep — stays at v0.6 signature per Phase 1 plan.
|
|
61
|
+
* handleAgentDeparture iterates ALL orgs (internal maintenance only).
|
|
62
|
+
*/
|
|
58
63
|
handleAgentDeparture(agentId: string): void;
|
|
64
|
+
/**
|
|
65
|
+
* Cross-org sweeper — stays at v0.6 signature per Phase 1 plan.
|
|
66
|
+
* checkTimeouts scans ALL orgs (internal maintenance only).
|
|
67
|
+
*/
|
|
59
68
|
checkTimeouts(): void;
|
|
60
|
-
getThread(threadId: string): Thread | null;
|
|
61
|
-
getThreadWithMessages(threadId: string): {
|
|
69
|
+
getThread(orgId: string, threadId: string): Thread | null;
|
|
70
|
+
getThreadWithMessages(orgId: string, threadId: string): {
|
|
62
71
|
thread: Thread;
|
|
63
72
|
messages: ThreadMessage[];
|
|
64
73
|
} | null;
|
|
65
|
-
listThreads(filters: {
|
|
74
|
+
listThreads(orgId: string, filters: {
|
|
66
75
|
status?: string;
|
|
67
76
|
agent_id?: string;
|
|
68
77
|
module?: string;
|
|
@@ -83,15 +92,20 @@ export declare class Consultation {
|
|
|
83
92
|
*/
|
|
84
93
|
since_minutes?: number;
|
|
85
94
|
}): Thread[];
|
|
86
|
-
getThreadUpdates(agentId: string, since?: string): ThreadMessage[];
|
|
87
|
-
logActionSummary(params: {
|
|
95
|
+
getThreadUpdates(orgId: string, agentId: string, since?: string): ThreadMessage[];
|
|
96
|
+
logActionSummary(orgId: string, params: {
|
|
88
97
|
session_id: string;
|
|
89
98
|
agent_id: string;
|
|
90
99
|
file_path?: string;
|
|
91
100
|
summary: string;
|
|
92
101
|
}): ActionSummary;
|
|
93
|
-
getActionSummaries(agentId: string, since?: string): ActionSummary[];
|
|
94
|
-
getActionSummariesBySession(sessionId: string): ActionSummary[];
|
|
102
|
+
getActionSummaries(orgId: string, agentId: string, since?: string): ActionSummary[];
|
|
103
|
+
getActionSummariesBySession(orgId: string, sessionId: string): ActionSummary[];
|
|
104
|
+
/**
|
|
105
|
+
* Cross-org thread lookup for internal sweepers/departure handlers.
|
|
106
|
+
* Do NOT call from public methods — use getThread(orgId, id) instead.
|
|
107
|
+
*/
|
|
108
|
+
private getThreadCrossOrg;
|
|
95
109
|
private postResolutionMessage;
|
|
96
110
|
private allRespondentsApproved;
|
|
97
111
|
}
|