@opengeni/api-router 2.13.0-canary.0 → 2.13.0-canary.2
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/app.js +1 -1
- package/dist/{chunk-BJWHVMOL.js → chunk-K2NU4KRK.js} +3165 -3662
- package/dist/{chunk-BJWHVMOL.js.map → chunk-K2NU4KRK.js.map} +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/integrations/github-lens-connect.d.ts +2 -2
- package/dist/integrations/slack-interactions.d.ts +1 -1
- package/dist/mcp/documents.d.ts +5 -13
- package/dist/mcp/files.d.ts +2 -2
- package/dist/mcp/first-party-tool-permissions.d.ts +36 -0
- package/dist/mcp/knowledge-entries.d.ts +5 -0
- package/dist/mcp/server.d.ts +4 -4
- package/dist/routes/host-mcp-resolvers.d.ts +3 -0
- package/dist/routes/knowledge.d.ts +3 -0
- package/dist/sandbox-file-artifacts.d.ts +6 -2
- package/dist/workspace-state-projection.d.ts +3 -5
- package/dist/workspace-tool-gateway.d.ts +2 -1
- package/package.json +19 -19
- package/src/access-grant-rls.ts +13 -1
- package/src/app.ts +4 -11
- package/src/codemode.ts +4 -8
- package/src/index.ts +4 -3
- package/src/integrations/atlassian.ts +31 -28
- package/src/integrations/google-drive.ts +33 -30
- package/src/integrations/marketplace-plugin.ts +1 -1
- package/src/integrations/provider-oauth.ts +11 -2
- package/src/mcp/documents.ts +57 -428
- package/src/mcp/files.ts +76 -58
- package/src/mcp/first-party-tool-permissions.ts +9 -0
- package/src/mcp/knowledge-entries.ts +276 -0
- package/src/mcp/server.ts +7 -361
- package/src/routes/company-brain.ts +58 -107
- package/src/routes/documents.ts +78 -294
- package/src/routes/files.ts +68 -3
- package/src/routes/host-mcp-resolvers.ts +41 -0
- package/src/routes/knowledge.ts +386 -0
- package/src/routes/organization-memberships.ts +33 -0
- package/src/routes/scheduled-tasks.ts +9 -1
- package/src/routes/sessions.ts +20 -3
- package/src/routes/skill-content.ts +8 -2
- package/src/routes/video-generation.ts +7 -5
- package/src/routes/workspace-learning.ts +12 -82
- package/src/routes/workspace-state.ts +34 -25
- package/src/routes/workspaces.ts +20 -10
- package/src/sandbox-file-artifacts.ts +54 -2
- package/src/scheduled-task-deletion.ts +3 -2
- package/src/workspace-state-projection.ts +18 -148
- package/src/workspace-tool-gateway.ts +14 -25
- package/dist/mcp/company-brain-governed-writes.d.ts +0 -26
- package/dist/mcp/remember.d.ts +0 -28
- package/src/mcp/company-brain-governed-writes.ts +0 -271
- package/src/mcp/remember.ts +0 -191
|
@@ -1,21 +1,24 @@
|
|
|
1
|
+
import { withAccessGrantSessionRlsContext } from "../access-grant-rls";
|
|
1
2
|
import {
|
|
2
|
-
|
|
3
|
-
WORKSPACE_STATE_MAX_TOPICS,
|
|
4
|
-
WORKSPACE_STATE_TOPIC_MAX_CHARS,
|
|
3
|
+
WORKSPACE_STATE_KNOWLEDGE_SAMPLE_LIMIT,
|
|
5
4
|
WorkspaceStateQuery,
|
|
6
5
|
WorkspaceStateResponse,
|
|
7
|
-
type AccessGrant,
|
|
8
6
|
type WorkspaceStateQuery as WorkspaceStateQueryType,
|
|
9
7
|
} from "@opengeni/contracts";
|
|
10
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
hasPermission,
|
|
10
|
+
requireAccessGrantAuthorization,
|
|
11
|
+
knowledgeContextForAccess,
|
|
12
|
+
type AccessGrantAuthorization,
|
|
13
|
+
type ApiRouteDeps,
|
|
14
|
+
} from "@opengeni/core";
|
|
11
15
|
import {
|
|
12
16
|
getWorkspace,
|
|
13
17
|
getCurrentPreferenceRegistryGovernanceMetadata,
|
|
14
18
|
getWorkspaceStateAcceptedAttemptGovernance,
|
|
15
|
-
|
|
19
|
+
listKnowledgeEntries,
|
|
16
20
|
listWorkspaceInstructionPolicyRevisions,
|
|
17
21
|
} from "@opengeni/db";
|
|
18
|
-
import { getDocumentInventory } from "@opengeni/documents";
|
|
19
22
|
import type { Hono } from "hono";
|
|
20
23
|
import { HTTPException } from "hono/http-exception";
|
|
21
24
|
|
|
@@ -24,9 +27,10 @@ import { projectWorkspaceState } from "../workspace-state-projection";
|
|
|
24
27
|
|
|
25
28
|
async function readWorkspaceState(
|
|
26
29
|
deps: ApiRouteDeps,
|
|
27
|
-
input: { workspaceId: string; query: WorkspaceStateQueryType;
|
|
30
|
+
input: { workspaceId: string; query: WorkspaceStateQueryType; access: AccessGrantAuthorization },
|
|
28
31
|
) {
|
|
29
|
-
const { workspaceId, query,
|
|
32
|
+
const { workspaceId, query, access } = input;
|
|
33
|
+
const { grant } = access;
|
|
30
34
|
const generatedAt = new Date().toISOString();
|
|
31
35
|
const canInspectKnowledge = hasPermission(grant.permissions, "documents:search");
|
|
32
36
|
|
|
@@ -34,18 +38,13 @@ async function readWorkspaceState(
|
|
|
34
38
|
getWorkspace(deps.db, workspaceId),
|
|
35
39
|
listWorkspaceInstructionPolicyRevisions(deps.db, workspaceId, { limit: 1 }),
|
|
36
40
|
canInspectKnowledge
|
|
37
|
-
? (async () =>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}),
|
|
45
|
-
listWorkspaceStateMemoryRecords(deps.db, workspaceId),
|
|
46
|
-
]);
|
|
47
|
-
return { documents, memories };
|
|
48
|
-
})()
|
|
41
|
+
? withAccessGrantSessionRlsContext(deps, grant, async () =>
|
|
42
|
+
listKnowledgeEntries(
|
|
43
|
+
deps.db,
|
|
44
|
+
await knowledgeContextForAccess(deps, access, "documents:search"),
|
|
45
|
+
{ limit: WORKSPACE_STATE_KNOWLEDGE_SAMPLE_LIMIT, view: "published" },
|
|
46
|
+
),
|
|
47
|
+
)
|
|
49
48
|
: Promise.resolve(null),
|
|
50
49
|
getCurrentPreferenceRegistryGovernanceMetadata(deps.db, {
|
|
51
50
|
workspaceId,
|
|
@@ -109,8 +108,13 @@ export function registerWorkspaceStateRoutes(app: Hono, deps: ApiRouteDeps): voi
|
|
|
109
108
|
app.get(base, async (context) => {
|
|
110
109
|
const workspaceId = context.req.param("workspaceId");
|
|
111
110
|
const query = WorkspaceStateQuery.parse(context.req.query());
|
|
112
|
-
const
|
|
113
|
-
|
|
111
|
+
const access = await requireAccessGrantAuthorization(
|
|
112
|
+
context,
|
|
113
|
+
deps,
|
|
114
|
+
workspaceId,
|
|
115
|
+
"workspace:read",
|
|
116
|
+
);
|
|
117
|
+
const state = await readWorkspaceState(deps, { workspaceId, query, access });
|
|
114
118
|
context.header("cache-control", "private, no-store");
|
|
115
119
|
return context.json(state);
|
|
116
120
|
});
|
|
@@ -118,8 +122,13 @@ export function registerWorkspaceStateRoutes(app: Hono, deps: ApiRouteDeps): voi
|
|
|
118
122
|
app.get(`${base}/export`, async (context) => {
|
|
119
123
|
const workspaceId = context.req.param("workspaceId");
|
|
120
124
|
const query = WorkspaceStateQuery.parse(context.req.query());
|
|
121
|
-
const
|
|
122
|
-
|
|
125
|
+
const access = await requireAccessGrantAuthorization(
|
|
126
|
+
context,
|
|
127
|
+
deps,
|
|
128
|
+
workspaceId,
|
|
129
|
+
"workspace:read",
|
|
130
|
+
);
|
|
131
|
+
const state = await readWorkspaceState(deps, { workspaceId, query, access });
|
|
123
132
|
context.header("cache-control", "private, no-store");
|
|
124
133
|
context.header("content-type", "application/json; charset=utf-8");
|
|
125
134
|
context.header(
|
package/src/routes/workspaces.ts
CHANGED
|
@@ -398,16 +398,26 @@ export function registerWorkspaceRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
398
398
|
}
|
|
399
399
|
// Request-scoped: bound the exclusive control-prefix wait so a busy
|
|
400
400
|
// workspace yields the retryable 503 instead of parking this request.
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
401
|
+
try {
|
|
402
|
+
const workspace = await updateWorkspaceSettingsWithToolDefaults(
|
|
403
|
+
deps.db,
|
|
404
|
+
workspaceId,
|
|
405
|
+
parsed.data,
|
|
406
|
+
{ requireWorkspace, updateWorkspaceSettings },
|
|
407
|
+
{
|
|
408
|
+
controlLockTimeoutMs: workspaceControlRequestLockTimeoutMs(),
|
|
409
|
+
},
|
|
410
|
+
);
|
|
411
|
+
return c.json(Workspace.parse(workspace));
|
|
412
|
+
} catch (error) {
|
|
413
|
+
if (nestedPostgresSqlState(error) === "0A000") {
|
|
414
|
+
throw new HTTPException(409, {
|
|
415
|
+
message:
|
|
416
|
+
"Memory settings moved to Settings > Agent learning. Refresh the app to change them.",
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
throw error;
|
|
420
|
+
}
|
|
411
421
|
});
|
|
412
422
|
|
|
413
423
|
// Per-workspace model/provider availability policy (the HARD blocker over
|
|
@@ -8,8 +8,22 @@ import {
|
|
|
8
8
|
type AccessGrant,
|
|
9
9
|
type Session,
|
|
10
10
|
} from "@opengeni/contracts";
|
|
11
|
-
import {
|
|
12
|
-
|
|
11
|
+
import {
|
|
12
|
+
fileOwnerContextForAccess,
|
|
13
|
+
fileOwnerContextForAgent,
|
|
14
|
+
recordWorkspaceUsage,
|
|
15
|
+
requireLimit,
|
|
16
|
+
type ApiRouteDeps,
|
|
17
|
+
type AccessGrantAuthorization,
|
|
18
|
+
} from "@opengeni/core";
|
|
19
|
+
import {
|
|
20
|
+
completeFileUpload,
|
|
21
|
+
getFile,
|
|
22
|
+
prepareGeneratedWorkspaceFile,
|
|
23
|
+
getSessionAuthorityProjection,
|
|
24
|
+
requireWorkspace,
|
|
25
|
+
withSessionRlsActorContext,
|
|
26
|
+
} from "@opengeni/db";
|
|
13
27
|
import { retryWhileMissing, type ObjectHead, type ObjectStorage } from "@opengeni/storage";
|
|
14
28
|
import { HTTPException } from "hono/http-exception";
|
|
15
29
|
|
|
@@ -21,10 +35,48 @@ const UPLOAD_INTENT_TTL_MS = 60 * 60_000;
|
|
|
21
35
|
const SANDBOX_ARTIFACT_ABSOLUTE_PATH_MAX_CHARS = 4_096;
|
|
22
36
|
const SANDBOX_ARTIFACT_SAFE_FILENAME_MAX_CHARS = 200;
|
|
23
37
|
|
|
38
|
+
/** Original bytes inherit the already-authorized source session's ownership. */
|
|
24
39
|
export async function publishSandboxFileArtifact(
|
|
40
|
+
deps: ApiRouteDeps,
|
|
41
|
+
input: Parameters<typeof publishSandboxFileArtifactInScope>[1],
|
|
42
|
+
): Promise<SandboxFileArtifactReceipt> {
|
|
43
|
+
const actor = input.authorization
|
|
44
|
+
? await fileOwnerContextForAccess(deps, input.authorization, "files:upload")
|
|
45
|
+
: input.grant.principalKind === "agent_attempt"
|
|
46
|
+
? await fileOwnerContextForAgent(deps, input.grant, "files:upload")
|
|
47
|
+
: { subjectId: input.grant.subjectId, privateFileOwnerSubjectId: null };
|
|
48
|
+
return withSessionRlsActorContext(actor, async () => {
|
|
49
|
+
const [authority, workspace] = await Promise.all([
|
|
50
|
+
getSessionAuthorityProjection(deps.db, input.grant.workspaceId, input.session.id),
|
|
51
|
+
requireWorkspace(deps.db, input.grant.workspaceId),
|
|
52
|
+
]);
|
|
53
|
+
if (!authority) throw new HTTPException(404, { message: "Source session is unavailable" });
|
|
54
|
+
const personal =
|
|
55
|
+
authority.visibility === "user_private" ||
|
|
56
|
+
authority.memoryScope === "user" ||
|
|
57
|
+
workspace.kind === "personal";
|
|
58
|
+
const expectedOwner =
|
|
59
|
+
authority.visibility === "user_private"
|
|
60
|
+
? authority.ownerSubjectId
|
|
61
|
+
: authority.memoryScope === "user"
|
|
62
|
+
? authority.scopeSubjectId
|
|
63
|
+
: actor.privateFileOwnerSubjectId;
|
|
64
|
+
if (personal && (!expectedOwner || actor.privateFileOwnerSubjectId !== expectedOwner))
|
|
65
|
+
throw new HTTPException(403, {
|
|
66
|
+
message: "Personal file publication requires the session owner's authority",
|
|
67
|
+
});
|
|
68
|
+
return withSessionRlsActorContext(
|
|
69
|
+
{ ...actor, privateFileOwnerSubjectId: personal ? (expectedOwner ?? null) : null },
|
|
70
|
+
() => publishSandboxFileArtifactInScope(deps, input),
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function publishSandboxFileArtifactInScope(
|
|
25
76
|
deps: ApiRouteDeps,
|
|
26
77
|
input: {
|
|
27
78
|
grant: AccessGrant;
|
|
79
|
+
authorization?: AccessGrantAuthorization;
|
|
28
80
|
session: Session;
|
|
29
81
|
path: string;
|
|
30
82
|
signal?: AbortSignal | undefined;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { scheduledTaskKnowledgeSource } from "@opengeni/contracts";
|
|
1
2
|
import type { ScheduledTask as ScheduledTaskValue } from "@opengeni/contracts";
|
|
2
3
|
import { deleteScheduledTaskLifecycle, type ApiRouteDeps } from "@opengeni/core";
|
|
3
4
|
import type { TemporalScheduleCleanupClaim } from "@opengeni/db";
|
|
@@ -35,7 +36,7 @@ async function preflightConnectorAuthorization(
|
|
|
35
36
|
task: ScheduledTaskValue,
|
|
36
37
|
subjectId: string,
|
|
37
38
|
): Promise<void> {
|
|
38
|
-
if (task
|
|
39
|
+
if (!scheduledTaskKnowledgeSource(task)) return;
|
|
39
40
|
if (task.metadata.connectorKind === "atlassian") {
|
|
40
41
|
await preflightAtlassianScheduleAuthorization(deps, { task, subjectId });
|
|
41
42
|
return;
|
|
@@ -54,7 +55,7 @@ async function revokeConnectorAuthorization(
|
|
|
54
55
|
task: ScheduledTaskValue,
|
|
55
56
|
subjectId: string,
|
|
56
57
|
): Promise<void> {
|
|
57
|
-
if (task
|
|
58
|
+
if (!scheduledTaskKnowledgeSource(task)) return;
|
|
58
59
|
if (task.metadata.connectorKind === "atlassian") {
|
|
59
60
|
await revokeAtlassianScheduleAuthorization(deps, { task, subjectId });
|
|
60
61
|
return;
|
|
@@ -1,29 +1,17 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import {
|
|
3
|
-
KnowledgeMemoryKind,
|
|
4
|
-
KnowledgeMemoryStatus,
|
|
5
|
-
WORKSPACE_STATE_BASE_NAME_MAX_CHARS,
|
|
6
3
|
WORKSPACE_STATE_MAX_ACTIVE_POLICY_HEADS,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
WORKSPACE_STATE_MAX_TOPICS,
|
|
10
|
-
WORKSPACE_STATE_MEMORY_SAMPLE_LIMIT,
|
|
11
|
-
WORKSPACE_STATE_TOPIC_MAX_CHARS,
|
|
4
|
+
WORKSPACE_STATE_KNOWLEDGE_SAMPLE_LIMIT,
|
|
5
|
+
type KnowledgeEntrySummary,
|
|
12
6
|
WorkspaceStateResponse,
|
|
13
7
|
type WorkspaceInstructionPolicyListResponse,
|
|
14
8
|
type WorkspaceInstructionPolicySnapshot,
|
|
15
9
|
type WorkspaceStateGovernanceDriftStatus,
|
|
16
|
-
type WorkspaceStateGap,
|
|
17
|
-
type WorkspaceStateMemoryKindCounts,
|
|
18
|
-
type WorkspaceStateMemoryStatusCounts,
|
|
19
10
|
type WorkspaceStateResponse as WorkspaceStateResponseType,
|
|
20
11
|
} from "@opengeni/contracts";
|
|
21
|
-
import type { WorkspaceStateMemoryRecord } from "@opengeni/db";
|
|
22
|
-
import type { DocumentInventory } from "@opengeni/documents";
|
|
23
|
-
|
|
24
12
|
type KnowledgeProjectionInput = {
|
|
25
|
-
|
|
26
|
-
|
|
13
|
+
entries: Pick<KnowledgeEntrySummary, "id" | "scope" | "revision" | "updatedAt">[];
|
|
14
|
+
nextCursor: string | null;
|
|
27
15
|
};
|
|
28
16
|
|
|
29
17
|
type PreferenceGovernanceIdentity = {
|
|
@@ -239,30 +227,6 @@ function attemptGovernanceProjection(input: WorkspaceStateProjectionInput) {
|
|
|
239
227
|
};
|
|
240
228
|
}
|
|
241
229
|
|
|
242
|
-
function emptyMemoryStatusCounts(): WorkspaceStateMemoryStatusCounts {
|
|
243
|
-
return Object.fromEntries(
|
|
244
|
-
KnowledgeMemoryStatus.options.map((status) => [status, 0]),
|
|
245
|
-
) as WorkspaceStateMemoryStatusCounts;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function emptyMemoryKindCounts(): WorkspaceStateMemoryKindCounts {
|
|
249
|
-
return Object.fromEntries(
|
|
250
|
-
KnowledgeMemoryKind.options.map((kind) => [kind, 0]),
|
|
251
|
-
) as WorkspaceStateMemoryKindCounts;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
function boundedLabel(value: string, maxChars: number, fallback: string): string {
|
|
255
|
-
const normalized = value.normalize("NFKC").trim().replace(/\s+/gu, " ");
|
|
256
|
-
return (normalized || fallback).slice(0, maxChars);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
function newestTimestamp(values: Array<string | null>): string | null {
|
|
260
|
-
return values.reduce<string | null>((latest, value) => {
|
|
261
|
-
if (!value) return latest;
|
|
262
|
-
return latest === null || value > latest ? value : latest;
|
|
263
|
-
}, null);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
230
|
function comparePolicyTargets(
|
|
267
231
|
left: WorkspaceInstructionPolicyListResponse["activeHeads"][number],
|
|
268
232
|
right: WorkspaceInstructionPolicyListResponse["activeHeads"][number],
|
|
@@ -330,116 +294,22 @@ function preferenceProjection(input: CurrentPreferenceProjectionInput) {
|
|
|
330
294
|
}
|
|
331
295
|
|
|
332
296
|
function availableKnowledgeProjection(knowledge: KnowledgeProjectionInput) {
|
|
333
|
-
const inventory = knowledge.documents;
|
|
334
|
-
const selectedBases = inventory.bases.slice(0, WORKSPACE_STATE_MAX_BASES);
|
|
335
|
-
const aggregateStatuses = { ...inventory.statusCounts };
|
|
336
|
-
const aggregateSources = { ...inventory.sourceKindCounts };
|
|
337
|
-
const topicCounts = new Map<string, number>();
|
|
338
|
-
const projectedBases = selectedBases.map((base) => {
|
|
339
|
-
return {
|
|
340
|
-
id: base.id,
|
|
341
|
-
name: boundedLabel(base.name, WORKSPACE_STATE_BASE_NAME_MAX_CHARS, "Untitled base"),
|
|
342
|
-
visibleDocumentCount: base.visibleDocumentCount,
|
|
343
|
-
statusCounts: base.statusCounts,
|
|
344
|
-
latestUpdatedAt: base.latestUpdatedAt,
|
|
345
|
-
};
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
for (const topic of inventory.topics) {
|
|
349
|
-
const name = boundedLabel(topic.name, WORKSPACE_STATE_TOPIC_MAX_CHARS, "Unlabeled topic");
|
|
350
|
-
topicCounts.set(name, (topicCounts.get(name) ?? 0) + topic.documentCount);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
const sortedTopics = [...topicCounts.entries()]
|
|
354
|
-
.sort(
|
|
355
|
-
([leftName, leftCount], [rightName, rightCount]) =>
|
|
356
|
-
rightCount - leftCount || leftName.localeCompare(rightName),
|
|
357
|
-
)
|
|
358
|
-
.map(([name, documentCount]) => ({ name, documentCount }));
|
|
359
|
-
const topics = sortedTopics.slice(0, WORKSPACE_STATE_MAX_TOPICS);
|
|
360
|
-
const memories = knowledge.memories.slice(0, WORKSPACE_STATE_MEMORY_SAMPLE_LIMIT);
|
|
361
|
-
const memoryStatuses = emptyMemoryStatusCounts();
|
|
362
|
-
const memoryKinds = emptyMemoryKindCounts();
|
|
363
|
-
for (const memory of memories) {
|
|
364
|
-
memoryStatuses[memory.status] += 1;
|
|
365
|
-
memoryKinds[memory.kind] += 1;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
const inspectedVisibleDocumentCount = inventory.visibleDocumentCount;
|
|
369
|
-
const basesTruncated = inventory.baseCount > projectedBases.length;
|
|
370
|
-
const memoryLimitReached = memories.length === WORKSPACE_STATE_MEMORY_SAMPLE_LIMIT;
|
|
371
|
-
const inventoryPartial = basesTruncated || inventory.topicsTruncated || memoryLimitReached;
|
|
372
|
-
const gaps: WorkspaceStateGap[] = [];
|
|
373
|
-
const addGap = (gap: WorkspaceStateGap): void => {
|
|
374
|
-
if (gaps.length < WORKSPACE_STATE_MAX_GAPS) gaps.push(gap);
|
|
375
|
-
};
|
|
376
|
-
if (inventory.baseCount === 0) {
|
|
377
|
-
addGap({ code: "no_document_bases", severity: "info", relatedCount: 0 });
|
|
378
|
-
} else if (inspectedVisibleDocumentCount === 0) {
|
|
379
|
-
addGap({ code: "no_visible_documents", severity: "info", relatedCount: 0 });
|
|
380
|
-
}
|
|
381
|
-
if (aggregateStatuses.failed > 0) {
|
|
382
|
-
addGap({
|
|
383
|
-
code: "failed_documents",
|
|
384
|
-
severity: "warning",
|
|
385
|
-
relatedCount: aggregateStatuses.failed,
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
const processingCount = aggregateStatuses.queued + aggregateStatuses.indexing;
|
|
389
|
-
if (processingCount > 0) {
|
|
390
|
-
addGap({
|
|
391
|
-
code: "processing_documents",
|
|
392
|
-
severity: "info",
|
|
393
|
-
relatedCount: processingCount,
|
|
394
|
-
});
|
|
395
|
-
}
|
|
396
|
-
if (aggregateStatuses.ready > 0 && sortedTopics.length === 0) {
|
|
397
|
-
addGap({
|
|
398
|
-
code: "missing_topic_coverage",
|
|
399
|
-
severity: "info",
|
|
400
|
-
relatedCount: aggregateStatuses.ready,
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
if (memories.length === 0) {
|
|
404
|
-
addGap({ code: "no_memory_records", severity: "info", relatedCount: 0 });
|
|
405
|
-
}
|
|
406
|
-
if (memoryStatuses.proposed > 0) {
|
|
407
|
-
addGap({
|
|
408
|
-
code: "pending_memory_review",
|
|
409
|
-
severity: "info",
|
|
410
|
-
relatedCount: memoryStatuses.proposed,
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
if (inventoryPartial) {
|
|
414
|
-
addGap({ code: "partial_inventory", severity: "info", relatedCount: null });
|
|
415
|
-
}
|
|
416
|
-
|
|
417
297
|
return {
|
|
418
298
|
availability: "available" as const,
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
limitReached: memoryLimitReached,
|
|
434
|
-
statusCounts: memoryStatuses,
|
|
435
|
-
kindCounts: memoryKinds,
|
|
436
|
-
preferenceAuthority: {
|
|
437
|
-
kindCountSource: "knowledge_memories_legacy_observations" as const,
|
|
438
|
-
activeAuthority: "structured_preference_registry" as const,
|
|
439
|
-
},
|
|
440
|
-
latestUpdatedAt: newestTimestamp(memories.map((memory) => memory.updatedAt)),
|
|
441
|
-
},
|
|
442
|
-
gaps,
|
|
299
|
+
authority: "knowledge_entries" as const,
|
|
300
|
+
coverage:
|
|
301
|
+
knowledge.nextCursor || knowledge.entries.length > WORKSPACE_STATE_KNOWLEDGE_SAMPLE_LIMIT
|
|
302
|
+
? ("partial" as const)
|
|
303
|
+
: ("complete" as const),
|
|
304
|
+
sampleLimit: WORKSPACE_STATE_KNOWLEDGE_SAMPLE_LIMIT,
|
|
305
|
+
entries: knowledge.entries.slice(0, WORKSPACE_STATE_KNOWLEDGE_SAMPLE_LIMIT).map((entry) => ({
|
|
306
|
+
id: entry.id,
|
|
307
|
+
revisionId: entry.revision.id,
|
|
308
|
+
title: entry.revision.title,
|
|
309
|
+
kind: entry.revision.kind,
|
|
310
|
+
scope: entry.scope,
|
|
311
|
+
updatedAt: entry.updatedAt,
|
|
312
|
+
})),
|
|
443
313
|
};
|
|
444
314
|
}
|
|
445
315
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { knowledgeContextForGateway } from "@opengeni/core";
|
|
1
2
|
import { createHash, randomBytes } from "node:crypto";
|
|
2
3
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
3
4
|
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
@@ -48,12 +49,11 @@ import {
|
|
|
48
49
|
requireWorkspace,
|
|
49
50
|
withCodexAppsRequestAuthorization,
|
|
50
51
|
consumeToolGatewayApproval,
|
|
51
|
-
|
|
52
|
+
getWorkspaceArtifactContentRef,
|
|
52
53
|
issueToolGatewayApproval,
|
|
53
54
|
ToolGatewayApprovalOperationStartedError,
|
|
54
55
|
ToolGatewayApprovalRateLimitError,
|
|
55
56
|
WorkspaceArtifactNotFoundError,
|
|
56
|
-
resolveSessionMemoryAgentScope,
|
|
57
57
|
type ApiIntegrationRuntime,
|
|
58
58
|
} from "@opengeni/db";
|
|
59
59
|
import {
|
|
@@ -288,7 +288,7 @@ async function prepareWorkspaceToolGatewayForGrantInternal(
|
|
|
288
288
|
...(gatewayServerIds.has("files")
|
|
289
289
|
? [inMemoryMcpRegistration("files", buildFilesMcpServer(deps, grant))]
|
|
290
290
|
: []),
|
|
291
|
-
...(gatewayServerIds.has("docs")
|
|
291
|
+
...(gatewayServerIds.has("docs") && hasPermission(grant.permissions, "documents:search")
|
|
292
292
|
? [
|
|
293
293
|
inMemoryMcpRegistration(
|
|
294
294
|
"docs",
|
|
@@ -298,19 +298,7 @@ async function prepareWorkspaceToolGatewayForGrantInternal(
|
|
|
298
298
|
grant.workspaceId,
|
|
299
299
|
routeDeps.getDocumentServices(),
|
|
300
300
|
{
|
|
301
|
-
|
|
302
|
-
// A session-bound gateway caller reads Memory through that
|
|
303
|
-
// session's frozen selector; a sessionless caller keeps the
|
|
304
|
-
// workspace layer.
|
|
305
|
-
memory:
|
|
306
|
-
typeof grant.metadata?.["sessionId"] === "string"
|
|
307
|
-
? ((await resolveSessionMemoryAgentScope(
|
|
308
|
-
routeDeps.db,
|
|
309
|
-
grant.workspaceId,
|
|
310
|
-
grant.metadata["sessionId"],
|
|
311
|
-
grant.metadata,
|
|
312
|
-
)) ?? { mode: "off" as const, userSubjectId: null, rootSessionId: null })
|
|
313
|
-
: null,
|
|
301
|
+
knowledge: await knowledgeContextForGateway(routeDeps, grant),
|
|
314
302
|
},
|
|
315
303
|
),
|
|
316
304
|
),
|
|
@@ -719,24 +707,25 @@ function throwWorkspaceToolGatewayHttpError(error: unknown): never {
|
|
|
719
707
|
throw error;
|
|
720
708
|
}
|
|
721
709
|
|
|
722
|
-
async function requireWorkspaceSiteToolAuthorization(
|
|
710
|
+
export async function requireWorkspaceSiteToolAuthorization(
|
|
723
711
|
db: ApiRouteDeps["db"],
|
|
724
712
|
grant: AccessGrant,
|
|
725
713
|
context: WorkspaceSiteToolContext,
|
|
714
|
+
resolveVersion = getWorkspaceArtifactContentRef,
|
|
726
715
|
): Promise<void> {
|
|
727
716
|
try {
|
|
728
|
-
const
|
|
729
|
-
|
|
730
|
-
|
|
717
|
+
const { status, version } = await resolveVersion(
|
|
718
|
+
db,
|
|
719
|
+
grant.workspaceId,
|
|
720
|
+
context.siteArtifactId,
|
|
721
|
+
context.siteVersionId,
|
|
722
|
+
);
|
|
723
|
+
const requested = version.requestedTools.some(
|
|
731
724
|
(identity) =>
|
|
732
725
|
identity.serverId === context.identity.serverId &&
|
|
733
726
|
identity.toolName === context.identity.toolName,
|
|
734
727
|
);
|
|
735
|
-
if (
|
|
736
|
-
detail.artifact.status !== "active" ||
|
|
737
|
-
currentVersion?.id !== context.siteVersionId ||
|
|
738
|
-
!requested
|
|
739
|
-
) {
|
|
728
|
+
if (status !== "active" || version.id !== context.siteVersionId || !requested) {
|
|
740
729
|
throw new HTTPException(403, { message: "site_tool_not_authorized" });
|
|
741
730
|
}
|
|
742
731
|
} catch (error) {
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { type CompanyBrainGovernedWriteAttempt } from "@opengeni/contracts";
|
|
2
|
-
import { createCompanyBrainLearningPolicyRouter } from "@opengeni/core";
|
|
3
|
-
import { type Database } from "@opengeni/db";
|
|
4
|
-
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
-
type JsonResult = (value: unknown) => {
|
|
6
|
-
content: {
|
|
7
|
-
type: "text";
|
|
8
|
-
text: string;
|
|
9
|
-
}[];
|
|
10
|
-
};
|
|
11
|
-
export type RegisterCompanyBrainGovernedWriteToolsInput = {
|
|
12
|
-
server: McpServer;
|
|
13
|
-
db: Database;
|
|
14
|
-
attempt: CompanyBrainGovernedWriteAttempt;
|
|
15
|
-
authorize: () => Promise<void>;
|
|
16
|
-
json: JsonResult;
|
|
17
|
-
router?: Pick<ReturnType<typeof createCompanyBrainLearningPolicyRouter>, "write">;
|
|
18
|
-
};
|
|
19
|
-
/**
|
|
20
|
-
* Register explicit proposal-only Company Brain tools. The signed host attempt
|
|
21
|
-
* owns tenant/session authority and the exact evidence id owns the learning
|
|
22
|
-
* policy source override. Tool input cannot select scope, active authority, or
|
|
23
|
-
* another policy source.
|
|
24
|
-
*/
|
|
25
|
-
export declare function registerCompanyBrainGovernedWriteTools(input: RegisterCompanyBrainGovernedWriteToolsInput): void;
|
|
26
|
-
export {};
|
package/dist/mcp/remember.d.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { type CompanyBrainGovernedWriteAttempt } from "@opengeni/contracts";
|
|
2
|
-
import { createRememberRouter } from "@opengeni/core";
|
|
3
|
-
import { type Database } from "@opengeni/db";
|
|
4
|
-
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
-
type JsonResult = (value: unknown) => {
|
|
6
|
-
content: {
|
|
7
|
-
type: "text";
|
|
8
|
-
text: string;
|
|
9
|
-
}[];
|
|
10
|
-
};
|
|
11
|
-
export type RegisterRememberToolsInput = {
|
|
12
|
-
server: McpServer;
|
|
13
|
-
db: Database;
|
|
14
|
-
attempt: CompanyBrainGovernedWriteAttempt;
|
|
15
|
-
authorize: () => Promise<void>;
|
|
16
|
-
json: JsonResult;
|
|
17
|
-
router?: Pick<ReturnType<typeof createRememberRouter>, "remember" | "confirm">;
|
|
18
|
-
};
|
|
19
|
-
/**
|
|
20
|
-
* Explicit user-directed remember. Content becomes exact task-note evidence,
|
|
21
|
-
* promotion runs through the frozen workspace learning policy, and activation
|
|
22
|
-
* is either automatic (instruction under Automatic mode) or requires exactly one
|
|
23
|
-
* bound human confirmation through the built-in `request_human_input` tool.
|
|
24
|
-
* Tool input cannot select scope beyond the workspace, active authority, or
|
|
25
|
-
* another learning-policy source.
|
|
26
|
-
*/
|
|
27
|
-
export declare function registerRememberTools(input: RegisterRememberToolsInput): void;
|
|
28
|
-
export {};
|