@opengeni/api-router 0.30.3 → 2.2.0-canary.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/api-websocket.d.ts +18 -0
- package/dist/app.js +1 -1
- package/dist/auth/managed-auth.d.ts +6 -0
- package/dist/{chunk-WCXHI3FF.js → chunk-3TP54PPX.js} +26692 -15389
- package/dist/chunk-3TP54PPX.js.map +1 -0
- package/dist/codemode.d.ts +8 -1
- package/dist/company-brain-okf.d.ts +24 -0
- package/dist/connected-machine-computer-access.d.ts +11 -0
- package/dist/connection-authority-owner.d.ts +59 -0
- package/dist/connection-ownership.d.ts +47 -0
- package/dist/controller-data-plane.d.ts +16 -0
- package/dist/editable-artifact-websocket.d.ts +5 -15
- package/dist/editable-artifact-workspace-files.d.ts +1 -1
- package/dist/http/api-error.d.ts +10 -0
- package/dist/http/cors.d.ts +5 -0
- package/dist/http/interaction-control-error.d.ts +13 -0
- package/dist/http/sse.d.ts +2 -3
- package/dist/index.d.ts +5 -2
- package/dist/index.js +326 -82
- package/dist/index.js.map +1 -1
- package/dist/integrations/atlassian.d.ts +17 -0
- package/dist/integrations/fiken.d.ts +7 -2
- package/dist/integrations/google-drive.d.ts +18 -0
- package/dist/integrations/oauth-client.d.ts +22 -3
- package/dist/integrations/oauth-profiles.d.ts +195 -0
- package/dist/integrations/personal-github-repositories.d.ts +45 -0
- package/dist/integrations/personal-github.d.ts +40 -0
- package/dist/integrations/pr-review-provider.d.ts +22 -0
- package/dist/integrations/provider-oauth.d.ts +8 -0
- package/dist/integrations/slack-app-home.d.ts +24 -0
- package/dist/integrations/slack-bot.d.ts +157 -9
- package/dist/integrations/slack-interactions.d.ts +39 -5
- package/dist/integrations/slack-routing.d.ts +120 -0
- package/dist/integrations/social-oauth.d.ts +5 -0
- package/dist/interaction-frame-proxy.d.ts +68 -0
- package/dist/mcp/company-brain-governed-writes.d.ts +26 -0
- package/dist/mcp/company-profile-agent-admin.d.ts +44 -0
- package/dist/mcp/documents.d.ts +1 -0
- package/dist/mcp/remember.d.ts +28 -0
- package/dist/mcp/request-abort.d.ts +19 -0
- package/dist/mcp/scheduled-task-view.d.ts +4 -4
- package/dist/mcp/server.d.ts +15 -2
- package/dist/mcp/session-view.d.ts +4 -0
- package/dist/mcp/session-wait.d.ts +137 -0
- package/dist/routes/automations.d.ts +4 -0
- package/dist/routes/billing.d.ts +5 -0
- package/dist/routes/browser-sessions.d.ts +4 -1
- package/dist/routes/company-brain.d.ts +3 -0
- package/dist/routes/company-profile.d.ts +2 -1
- package/dist/routes/connection-authorities.d.ts +3 -0
- package/dist/routes/personal-github-git-broker.d.ts +29 -0
- package/dist/routes/personal-github.d.ts +3 -0
- package/dist/routes/pr-review.d.ts +3 -0
- package/dist/routes/sessions.d.ts +1 -0
- package/dist/routes/user-resource-authorities.d.ts +3 -0
- package/dist/routes/workspace-capture.d.ts +10 -2
- package/dist/routes/workspace-learning.d.ts +3 -0
- package/dist/routes/workspaces.d.ts +19 -0
- package/dist/sandbox/channel-a.d.ts +11 -1
- package/dist/sandbox/connection-authority.d.ts +3 -0
- package/dist/sandbox/enrollment.d.ts +2 -0
- package/dist/sandbox/machines.d.ts +2 -2
- package/dist/sandbox/metrics-ingestion.d.ts +10 -20
- package/dist/sandbox/viewer.d.ts +24 -1
- package/dist/sandbox-file-artifacts.d.ts +11 -0
- package/dist/scheduled-task-deletion.d.ts +14 -0
- package/dist/slack-reaction-files.d.ts +1 -1
- package/dist/temporal-schedule-cleanup.d.ts +1 -0
- package/package.json +20 -18
- package/src/api-websocket.ts +23 -0
- package/src/app.ts +161 -23
- package/src/auth/managed-auth.ts +25 -3
- package/src/codemode.ts +53 -23
- package/src/codex-realtime.ts +8 -2
- package/src/company-brain-okf.ts +340 -0
- package/src/connected-machine-computer-access.ts +33 -0
- package/src/connection-authority-owner.ts +61 -0
- package/src/connection-ownership.ts +180 -0
- package/src/controller-data-plane.ts +47 -0
- package/src/editable-artifact-native-kernel.ts +26 -15
- package/src/editable-artifact-office-import.ts +28 -3
- package/src/editable-artifact-production.ts +13 -7
- package/src/editable-artifact-websocket.ts +11 -18
- package/src/editable-artifact-workspace-files.ts +31 -6
- package/src/http/api-error.ts +28 -0
- package/src/http/auth.ts +4 -0
- package/src/http/cors.ts +35 -0
- package/src/http/interaction-control-error.ts +164 -0
- package/src/http/sse.ts +112 -66
- package/src/index.ts +46 -6
- package/src/integrations/atlassian.ts +146 -35
- package/src/integrations/fiken.ts +102 -22
- package/src/integrations/google-drive.ts +301 -92
- package/src/integrations/oauth-client.ts +272 -143
- package/src/integrations/oauth-profiles.ts +477 -0
- package/src/integrations/personal-github-repositories.ts +445 -0
- package/src/integrations/personal-github.ts +705 -0
- package/src/integrations/pr-review-provider.ts +246 -0
- package/src/integrations/provider-oauth.ts +121 -5
- package/src/integrations/slack-app-home.ts +300 -0
- package/src/integrations/slack-bot.ts +342 -57
- package/src/integrations/slack-interactions.ts +1898 -259
- package/src/integrations/slack-routing.ts +337 -0
- package/src/integrations/social-oauth.ts +25 -0
- package/src/interaction-frame-proxy.ts +409 -0
- package/src/mcp/company-brain-governed-writes.ts +262 -0
- package/src/mcp/company-profile-agent-admin.ts +205 -0
- package/src/mcp/documents.ts +37 -6
- package/src/mcp/files.ts +16 -1
- package/src/mcp/remember.ts +181 -0
- package/src/mcp/request-abort.ts +44 -0
- package/src/mcp/scheduled-task-view.ts +1 -0
- package/src/mcp/server.ts +719 -369
- package/src/mcp/session-view.ts +54 -0
- package/src/mcp/session-wait.ts +554 -0
- package/src/model-catalog.ts +20 -12
- package/src/routes/api-integrations.ts +4 -0
- package/src/routes/api-keys.ts +1 -0
- package/src/routes/automations.ts +534 -0
- package/src/routes/billing.ts +57 -1
- package/src/routes/browser-sessions.ts +329 -79
- package/src/routes/channels.ts +17 -1
- package/src/routes/codex.ts +50 -0
- package/src/routes/company-brain.ts +367 -0
- package/src/routes/company-profile.ts +1 -1
- package/src/routes/computer-sessions.ts +320 -93
- package/src/routes/connection-authorities.ts +139 -0
- package/src/routes/connections.ts +144 -34
- package/src/routes/documents.ts +353 -4
- package/src/routes/editable-artifacts.ts +11 -3
- package/src/routes/enrollments.ts +116 -26
- package/src/routes/environments.ts +127 -55
- package/src/routes/files.ts +72 -11
- package/src/routes/install.ts +82 -28
- package/src/routes/integration-facets.ts +29 -0
- package/src/routes/interaction-resources.ts +20 -2
- package/src/routes/machines.ts +275 -13
- package/src/routes/organization-memberships.ts +717 -28
- package/src/routes/packs.ts +4 -3
- package/src/routes/personal-github-git-broker.ts +785 -0
- package/src/routes/personal-github.ts +319 -0
- package/src/routes/pr-review.ts +531 -0
- package/src/routes/rigs.ts +104 -39
- package/src/routes/scheduled-tasks.ts +19 -21
- package/src/routes/sessions.ts +775 -38
- package/src/routes/social.ts +14 -2
- package/src/routes/supergrok.ts +23 -4
- package/src/routes/transcription-recordings.ts +8 -2
- package/src/routes/user-resource-authorities.ts +138 -0
- package/src/routes/workspace-artifacts.ts +4 -1
- package/src/routes/workspace-capture.ts +21 -0
- package/src/routes/workspace-learning.ts +228 -0
- package/src/routes/workspaces.ts +59 -6
- package/src/sandbox/auth-callout.ts +38 -12
- package/src/sandbox/channel-a.ts +242 -19
- package/src/sandbox/connection-authority.ts +3 -0
- package/src/sandbox/enrollment.ts +15 -3
- package/src/sandbox/machines.ts +186 -64
- package/src/sandbox/metrics-ingestion.ts +220 -58
- package/src/sandbox/viewer.ts +243 -19
- package/src/sandbox-file-artifacts.ts +329 -0
- package/src/scheduled-task-deletion.ts +127 -0
- package/src/slack-reaction-files.ts +16 -5
- package/src/temporal-schedule-cleanup.ts +13 -0
- package/dist/chunk-WCXHI3FF.js.map +0 -1
package/src/routes/channels.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
CreateChannelRequest,
|
|
4
|
+
ReorderChannelsRequest,
|
|
5
|
+
UpdateChannelRequest,
|
|
6
|
+
} from "@opengeni/contracts";
|
|
3
7
|
import {
|
|
4
8
|
ChannelNameConflictError,
|
|
5
9
|
createChannel,
|
|
6
10
|
deleteChannel,
|
|
7
11
|
listChannels,
|
|
12
|
+
reorderChannels,
|
|
8
13
|
updateChannel,
|
|
9
14
|
} from "@opengeni/db";
|
|
10
15
|
import type { Hono } from "hono";
|
|
@@ -78,6 +83,17 @@ export function registerChannelRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
78
83
|
}
|
|
79
84
|
});
|
|
80
85
|
|
|
86
|
+
app.put("/v1/workspaces/:workspaceId/channels/order", async (c) => {
|
|
87
|
+
const workspaceId = c.req.param("workspaceId");
|
|
88
|
+
await requireAccessGrant(c, deps, workspaceId, "sessions:create");
|
|
89
|
+
const payload = ReorderChannelsRequest.parse(await c.req.json());
|
|
90
|
+
const channels = await reorderChannels(db, workspaceId, payload.channelIds);
|
|
91
|
+
if (!channels) {
|
|
92
|
+
throw new HTTPException(409, { message: "projects changed; refresh and try again" });
|
|
93
|
+
}
|
|
94
|
+
return c.json(channels);
|
|
95
|
+
});
|
|
96
|
+
|
|
81
97
|
app.delete("/v1/workspaces/:workspaceId/channels/:channelId", async (c) => {
|
|
82
98
|
const workspaceId = c.req.param("workspaceId");
|
|
83
99
|
await requireAccessGrant(c, deps, workspaceId, "sessions:create");
|
package/src/routes/codex.ts
CHANGED
|
@@ -386,10 +386,46 @@ function createProviderCallLimiter(limit: number): CodexProviderCall {
|
|
|
386
386
|
};
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
+
type CodexRedemptionAccess = {
|
|
390
|
+
ownership: "current_human" | "unowned" | "different_human" | "managed_human_unavailable";
|
|
391
|
+
canClaimUnownedViaReconnect: boolean;
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
function codexRedemptionAccess(input: {
|
|
395
|
+
connectedBySubjectId: string | null;
|
|
396
|
+
grantSubjectId: string;
|
|
397
|
+
managedHumanSubjectId: string | null;
|
|
398
|
+
canManage: boolean;
|
|
399
|
+
}): CodexRedemptionAccess {
|
|
400
|
+
if (
|
|
401
|
+
input.managedHumanSubjectId === null ||
|
|
402
|
+
input.managedHumanSubjectId !== input.grantSubjectId
|
|
403
|
+
) {
|
|
404
|
+
return {
|
|
405
|
+
ownership: "managed_human_unavailable",
|
|
406
|
+
canClaimUnownedViaReconnect: false,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
if (input.connectedBySubjectId === null) {
|
|
410
|
+
return {
|
|
411
|
+
ownership: "unowned",
|
|
412
|
+
canClaimUnownedViaReconnect: input.canManage,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
return {
|
|
416
|
+
ownership:
|
|
417
|
+
input.connectedBySubjectId === input.managedHumanSubjectId
|
|
418
|
+
? "current_human"
|
|
419
|
+
: "different_human",
|
|
420
|
+
canClaimUnownedViaReconnect: false,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
389
424
|
async function fetchCodexAccountOverview(
|
|
390
425
|
deps: ApiRouteDeps,
|
|
391
426
|
workspaceId: string,
|
|
392
427
|
row: CodexAccountStatus,
|
|
428
|
+
redemptionAccess: CodexRedemptionAccess,
|
|
393
429
|
canRedeem: boolean,
|
|
394
430
|
canResumeRedemption: boolean,
|
|
395
431
|
redemptions: Awaited<ReturnType<typeof listCodexResetRedemptionRecoveries>> = [],
|
|
@@ -496,6 +532,7 @@ async function fetchCodexAccountOverview(
|
|
|
496
532
|
})),
|
|
497
533
|
},
|
|
498
534
|
canRedeem,
|
|
535
|
+
redemptionAccess,
|
|
499
536
|
canResumeRedemption,
|
|
500
537
|
redemptions: redemptions.map((redemption) => ({
|
|
501
538
|
attemptId: redemption.attemptId,
|
|
@@ -1166,10 +1203,17 @@ export function registerCodexRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
1166
1203
|
hasPermission(grant.permissions, "workspace:admin"),
|
|
1167
1204
|
);
|
|
1168
1205
|
const canRedeem = canResumeRedemption && account.status === "active";
|
|
1206
|
+
const redemptionAccess = codexRedemptionAccess({
|
|
1207
|
+
connectedBySubjectId: account.connectedBySubjectId,
|
|
1208
|
+
grantSubjectId: grant.subjectId,
|
|
1209
|
+
managedHumanSubjectId: human?.subjectId ?? null,
|
|
1210
|
+
canManage: hasPermission(grant.permissions, "workspace:admin"),
|
|
1211
|
+
});
|
|
1169
1212
|
overview[account.id] = await fetchCodexAccountOverview(
|
|
1170
1213
|
deps,
|
|
1171
1214
|
workspaceId,
|
|
1172
1215
|
account,
|
|
1216
|
+
redemptionAccess,
|
|
1173
1217
|
canRedeem,
|
|
1174
1218
|
canResumeRedemption,
|
|
1175
1219
|
canResumeRedemption
|
|
@@ -1216,6 +1260,12 @@ export function registerCodexRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
1216
1260
|
deps,
|
|
1217
1261
|
workspaceId,
|
|
1218
1262
|
account,
|
|
1263
|
+
codexRedemptionAccess({
|
|
1264
|
+
connectedBySubjectId: account.connectedBySubjectId,
|
|
1265
|
+
grantSubjectId: grant.subjectId,
|
|
1266
|
+
managedHumanSubjectId: human?.subjectId ?? null,
|
|
1267
|
+
canManage: hasPermission(grant.permissions, "workspace:admin"),
|
|
1268
|
+
}),
|
|
1219
1269
|
false,
|
|
1220
1270
|
canResumeRedemption,
|
|
1221
1271
|
canResumeRedemption
|
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
COMPANY_BRAIN_INSPECTOR_CURSOR_MAX_CHARS,
|
|
4
|
+
COMPANY_BRAIN_INSPECTOR_DEFAULT_LIMIT,
|
|
5
|
+
COMPANY_BRAIN_INSPECTOR_MAX_LIMIT,
|
|
6
|
+
CompanyBrainContextReceiptPage,
|
|
7
|
+
CompanyBrainKnowledgeProposalPage,
|
|
8
|
+
CompanyBrainOkfPackage,
|
|
9
|
+
KnowledgeBrowseRequest,
|
|
10
|
+
KnowledgeBrowseResponse,
|
|
11
|
+
KnowledgeGetResponse,
|
|
12
|
+
KnowledgeRecordId,
|
|
13
|
+
KnowledgeSearchRequest,
|
|
14
|
+
KnowledgeSearchResponse,
|
|
15
|
+
type WorkspaceInstructionPolicyRevision,
|
|
16
|
+
} from "@opengeni/contracts";
|
|
17
|
+
import { hasPermission, requireAccessGrant, type ApiRouteDeps } from "@opengeni/core";
|
|
18
|
+
import {
|
|
19
|
+
getCurrentPreferenceRegistryGovernanceMetadata,
|
|
20
|
+
getWorkspace,
|
|
21
|
+
getWorkspaceInstructionPolicyRevision,
|
|
22
|
+
inspectCompanyBrainContextReceipts,
|
|
23
|
+
listActivatedCompanyBrainPolicyRevisionIds,
|
|
24
|
+
listCompanyBrainKnowledgeProposals,
|
|
25
|
+
listCompanyBrainPreferenceGuidance,
|
|
26
|
+
listCompanyProfile,
|
|
27
|
+
listWorkspaceInstructionPolicyRevisions,
|
|
28
|
+
listWorkspaceStateMemoryRecords,
|
|
29
|
+
} from "@opengeni/db";
|
|
30
|
+
import {
|
|
31
|
+
browseEffectiveKnowledge,
|
|
32
|
+
getDocumentInventory,
|
|
33
|
+
getEffectiveKnowledgeRecord,
|
|
34
|
+
searchEffectiveKnowledge,
|
|
35
|
+
} from "@opengeni/documents";
|
|
36
|
+
import type { Hono } from "hono";
|
|
37
|
+
import { HTTPException } from "hono/http-exception";
|
|
38
|
+
import { z } from "zod";
|
|
39
|
+
|
|
40
|
+
import { createCompanyBrainOkfPackage, serializeCompanyBrainOkf } from "../company-brain-okf";
|
|
41
|
+
import { projectWorkspaceState } from "../workspace-state-projection";
|
|
42
|
+
|
|
43
|
+
const BASE_LIMIT = 24;
|
|
44
|
+
const TOPIC_LIMIT = 24;
|
|
45
|
+
const TOPIC_MAX_CHARS = 96;
|
|
46
|
+
|
|
47
|
+
const inspectorQuery = z
|
|
48
|
+
.object({
|
|
49
|
+
attemptId: z.string().uuid().optional(),
|
|
50
|
+
cursor: z.string().min(1).max(COMPANY_BRAIN_INSPECTOR_CURSOR_MAX_CHARS).optional(),
|
|
51
|
+
limit: z.coerce
|
|
52
|
+
.number()
|
|
53
|
+
.int()
|
|
54
|
+
.positive()
|
|
55
|
+
.max(COMPANY_BRAIN_INSPECTOR_MAX_LIMIT)
|
|
56
|
+
.default(COMPANY_BRAIN_INSPECTOR_DEFAULT_LIMIT),
|
|
57
|
+
})
|
|
58
|
+
.strict()
|
|
59
|
+
.refine((value) => !(value.attemptId && value.cursor), {
|
|
60
|
+
message: "attemptId cannot be combined with cursor",
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
type ReceiptCursor = { createdAt: string; id: string };
|
|
64
|
+
|
|
65
|
+
function receiptCursorScope(workspaceId: string, subjectId: string): string {
|
|
66
|
+
return createHash("sha256")
|
|
67
|
+
.update("opengeni:company-brain-context-receipts:v1\0")
|
|
68
|
+
.update(workspaceId)
|
|
69
|
+
.update("\0")
|
|
70
|
+
.update(subjectId)
|
|
71
|
+
.digest("hex");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function encodeReceiptCursor(
|
|
75
|
+
workspaceId: string,
|
|
76
|
+
subjectId: string,
|
|
77
|
+
cursor: ReceiptCursor,
|
|
78
|
+
): string {
|
|
79
|
+
return Buffer.from(
|
|
80
|
+
JSON.stringify({ v: 1, s: receiptCursorScope(workspaceId, subjectId), ...cursor }),
|
|
81
|
+
"utf8",
|
|
82
|
+
).toString("base64url");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function decodeReceiptCursor(value: string, workspaceId: string, subjectId: string): ReceiptCursor {
|
|
86
|
+
try {
|
|
87
|
+
const bytes = Buffer.from(value, "base64url");
|
|
88
|
+
if (bytes.toString("base64url") !== value) throw new Error("encoding");
|
|
89
|
+
const decoded = z
|
|
90
|
+
.object({
|
|
91
|
+
v: z.literal(1),
|
|
92
|
+
s: z.string().regex(/^[0-9a-f]{64}$/u),
|
|
93
|
+
createdAt: z.string().datetime(),
|
|
94
|
+
id: z.string().uuid(),
|
|
95
|
+
})
|
|
96
|
+
.strict()
|
|
97
|
+
.parse(JSON.parse(bytes.toString("utf8")));
|
|
98
|
+
if (decoded.s !== receiptCursorScope(workspaceId, subjectId)) {
|
|
99
|
+
throw new HTTPException(409, { message: "context receipt cursor belongs to another scope" });
|
|
100
|
+
}
|
|
101
|
+
return { createdAt: decoded.createdAt, id: decoded.id };
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if (error instanceof HTTPException) throw error;
|
|
104
|
+
throw new HTTPException(400, { message: "context receipt cursor is invalid" });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function parsedJson<T>(
|
|
109
|
+
context: { req: { json: () => Promise<unknown> } },
|
|
110
|
+
schema: z.ZodType<T>,
|
|
111
|
+
) {
|
|
112
|
+
const result = schema.safeParse(await context.req.json().catch(() => null));
|
|
113
|
+
if (!result.success) throw new HTTPException(400, { message: "invalid Company Brain request" });
|
|
114
|
+
return result.data;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function knowledgeHttpError(error: unknown): never {
|
|
118
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
119
|
+
if (message.includes("belongs to a different scope")) {
|
|
120
|
+
throw new HTTPException(409, { message });
|
|
121
|
+
}
|
|
122
|
+
if (message.includes("invalid knowledge") || message.includes("knowledge browse")) {
|
|
123
|
+
throw new HTTPException(400, { message });
|
|
124
|
+
}
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function readCompanyBrainPackage(
|
|
129
|
+
deps: ApiRouteDeps,
|
|
130
|
+
input: {
|
|
131
|
+
workspaceId: string;
|
|
132
|
+
accountId: string;
|
|
133
|
+
subjectId: string;
|
|
134
|
+
canInspectKnowledge: boolean;
|
|
135
|
+
},
|
|
136
|
+
) {
|
|
137
|
+
const generatedAt = new Date().toISOString();
|
|
138
|
+
const [
|
|
139
|
+
workspace,
|
|
140
|
+
instructionPolicies,
|
|
141
|
+
companyProfile,
|
|
142
|
+
preferences,
|
|
143
|
+
currentPreferences,
|
|
144
|
+
knowledge,
|
|
145
|
+
] = await Promise.all([
|
|
146
|
+
getWorkspace(deps.db, input.workspaceId),
|
|
147
|
+
listWorkspaceInstructionPolicyRevisions(deps.db, input.workspaceId, { limit: 100 }),
|
|
148
|
+
listCompanyProfile(deps.db, {
|
|
149
|
+
accountId: input.accountId,
|
|
150
|
+
workspaceId: input.workspaceId,
|
|
151
|
+
limit: 100,
|
|
152
|
+
}),
|
|
153
|
+
listCompanyBrainPreferenceGuidance(deps.db, {
|
|
154
|
+
workspaceId: input.workspaceId,
|
|
155
|
+
subjectId: input.subjectId,
|
|
156
|
+
}),
|
|
157
|
+
getCurrentPreferenceRegistryGovernanceMetadata(deps.db, {
|
|
158
|
+
workspaceId: input.workspaceId,
|
|
159
|
+
subjectId: input.subjectId,
|
|
160
|
+
}),
|
|
161
|
+
input.canInspectKnowledge
|
|
162
|
+
? Promise.all([
|
|
163
|
+
getDocumentInventory(deps.db, input.workspaceId, {
|
|
164
|
+
baseLimit: BASE_LIMIT,
|
|
165
|
+
topicLimit: TOPIC_LIMIT,
|
|
166
|
+
topicMaxChars: TOPIC_MAX_CHARS,
|
|
167
|
+
access: { viewerSubjectId: input.subjectId },
|
|
168
|
+
}),
|
|
169
|
+
listWorkspaceStateMemoryRecords(deps.db, input.workspaceId),
|
|
170
|
+
]).then(([documents, memories]) => ({ documents, memories }))
|
|
171
|
+
: Promise.resolve(null),
|
|
172
|
+
]);
|
|
173
|
+
if (!workspace) throw new HTTPException(404, { message: "workspace not found" });
|
|
174
|
+
|
|
175
|
+
const listedRevisionIds = new Set(instructionPolicies.revisions.map((revision) => revision.id));
|
|
176
|
+
const missingActiveIds = instructionPolicies.activeHeads
|
|
177
|
+
.map((head) => head.revisionId)
|
|
178
|
+
.filter((revisionId) => !listedRevisionIds.has(revisionId));
|
|
179
|
+
const activeInstructionPolicyRevisions: WorkspaceInstructionPolicyRevision[] = [
|
|
180
|
+
...instructionPolicies.revisions.filter((revision) =>
|
|
181
|
+
instructionPolicies.activeHeads.some((head) => head.revisionId === revision.id),
|
|
182
|
+
),
|
|
183
|
+
...(await Promise.all(
|
|
184
|
+
missingActiveIds.map((revisionId) =>
|
|
185
|
+
getWorkspaceInstructionPolicyRevision(deps.db, input.workspaceId, revisionId),
|
|
186
|
+
),
|
|
187
|
+
)),
|
|
188
|
+
];
|
|
189
|
+
const activatedInstructionPolicyRevisionIds = await listActivatedCompanyBrainPolicyRevisionIds(
|
|
190
|
+
deps.db,
|
|
191
|
+
{
|
|
192
|
+
workspaceId: input.workspaceId,
|
|
193
|
+
subjectId: input.subjectId,
|
|
194
|
+
revisionIds: [...instructionPolicies.revisions, ...activeInstructionPolicyRevisions].map(
|
|
195
|
+
(revision) => revision.id,
|
|
196
|
+
),
|
|
197
|
+
},
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
const state = projectWorkspaceState({
|
|
201
|
+
workspaceId: input.workspaceId,
|
|
202
|
+
generatedAt,
|
|
203
|
+
workspaceAgentInstructions: workspace.agentInstructions,
|
|
204
|
+
policies: instructionPolicies,
|
|
205
|
+
preferences: currentPreferences,
|
|
206
|
+
knowledge,
|
|
207
|
+
attemptGovernance: null,
|
|
208
|
+
});
|
|
209
|
+
return createCompanyBrainOkfPackage({
|
|
210
|
+
workspaceId: input.workspaceId,
|
|
211
|
+
generatedAt,
|
|
212
|
+
companyProfile,
|
|
213
|
+
instructionPolicies,
|
|
214
|
+
activeInstructionPolicyRevisions,
|
|
215
|
+
activatedInstructionPolicyRevisionIds,
|
|
216
|
+
preferences,
|
|
217
|
+
knowledge: state.knowledge,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function registerCompanyBrainRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
222
|
+
const base = "/v1/workspaces/:workspaceId/company-brain";
|
|
223
|
+
|
|
224
|
+
app.get(base, async (context) => {
|
|
225
|
+
const workspaceId = context.req.param("workspaceId");
|
|
226
|
+
const grant = await requireAccessGrant(context, deps, workspaceId, "workspace:read");
|
|
227
|
+
const result = await readCompanyBrainPackage(deps, {
|
|
228
|
+
workspaceId,
|
|
229
|
+
accountId: grant.accountId,
|
|
230
|
+
subjectId: grant.subjectId,
|
|
231
|
+
canInspectKnowledge: hasPermission(grant.permissions, "documents:search"),
|
|
232
|
+
});
|
|
233
|
+
context.header("cache-control", "private, no-store");
|
|
234
|
+
return context.json(CompanyBrainOkfPackage.parse(result));
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
app.get(`${base}/export`, async (context) => {
|
|
238
|
+
const workspaceId = context.req.param("workspaceId");
|
|
239
|
+
const grant = await requireAccessGrant(context, deps, workspaceId, "workspace:read");
|
|
240
|
+
const result = await readCompanyBrainPackage(deps, {
|
|
241
|
+
workspaceId,
|
|
242
|
+
accountId: grant.accountId,
|
|
243
|
+
subjectId: grant.subjectId,
|
|
244
|
+
canInspectKnowledge: hasPermission(grant.permissions, "documents:search"),
|
|
245
|
+
});
|
|
246
|
+
context.header("cache-control", "private, no-store");
|
|
247
|
+
context.header("content-type", "text/markdown; charset=utf-8");
|
|
248
|
+
context.header(
|
|
249
|
+
"content-disposition",
|
|
250
|
+
`attachment; filename="company-brain-${workspaceId}.okf.md"`,
|
|
251
|
+
);
|
|
252
|
+
return context.body(serializeCompanyBrainOkf(result));
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
app.post(`${base}/knowledge/search`, async (context) => {
|
|
256
|
+
const workspaceId = context.req.param("workspaceId");
|
|
257
|
+
const grant = await requireAccessGrant(context, deps, workspaceId, "documents:search");
|
|
258
|
+
const request = await parsedJson(context, KnowledgeSearchRequest);
|
|
259
|
+
try {
|
|
260
|
+
const response = await searchEffectiveKnowledge(
|
|
261
|
+
deps.db,
|
|
262
|
+
{
|
|
263
|
+
accountId: grant.accountId,
|
|
264
|
+
workspaceId,
|
|
265
|
+
initiatingSubjectId: grant.subjectId,
|
|
266
|
+
surface: "human",
|
|
267
|
+
...request,
|
|
268
|
+
},
|
|
269
|
+
deps.getDocumentServices(),
|
|
270
|
+
);
|
|
271
|
+
context.header("cache-control", "private, no-store");
|
|
272
|
+
return context.json(KnowledgeSearchResponse.parse(response));
|
|
273
|
+
} catch (error) {
|
|
274
|
+
return knowledgeHttpError(error);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
app.get(`${base}/knowledge/record`, async (context) => {
|
|
279
|
+
const workspaceId = context.req.param("workspaceId");
|
|
280
|
+
const grant = await requireAccessGrant(context, deps, workspaceId, "documents:search");
|
|
281
|
+
const parsedId = KnowledgeRecordId.safeParse(context.req.query("id"));
|
|
282
|
+
if (!parsedId.success)
|
|
283
|
+
throw new HTTPException(400, { message: "knowledge record id is invalid" });
|
|
284
|
+
const record = await getEffectiveKnowledgeRecord(deps.db, {
|
|
285
|
+
accountId: grant.accountId,
|
|
286
|
+
workspaceId,
|
|
287
|
+
initiatingSubjectId: grant.subjectId,
|
|
288
|
+
surface: "human",
|
|
289
|
+
id: parsedId.data,
|
|
290
|
+
});
|
|
291
|
+
if (!record) throw new HTTPException(404, { message: "knowledge record not found" });
|
|
292
|
+
context.header("cache-control", "private, no-store");
|
|
293
|
+
return context.json(KnowledgeGetResponse.parse({ record }));
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
app.post(`${base}/knowledge/browse`, async (context) => {
|
|
297
|
+
const workspaceId = context.req.param("workspaceId");
|
|
298
|
+
const grant = await requireAccessGrant(context, deps, workspaceId, "documents:search");
|
|
299
|
+
const request = await parsedJson(context, KnowledgeBrowseRequest);
|
|
300
|
+
try {
|
|
301
|
+
const response = await browseEffectiveKnowledge(deps.db, {
|
|
302
|
+
accountId: grant.accountId,
|
|
303
|
+
workspaceId,
|
|
304
|
+
initiatingSubjectId: grant.subjectId,
|
|
305
|
+
surface: "human",
|
|
306
|
+
...request,
|
|
307
|
+
});
|
|
308
|
+
context.header("cache-control", "private, no-store");
|
|
309
|
+
return context.json(KnowledgeBrowseResponse.parse(response));
|
|
310
|
+
} catch (error) {
|
|
311
|
+
return knowledgeHttpError(error);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
app.get(`${base}/context-receipts`, async (context) => {
|
|
316
|
+
const workspaceId = context.req.param("workspaceId");
|
|
317
|
+
const grant = await requireAccessGrant(context, deps, workspaceId, "workspace:read");
|
|
318
|
+
const parsed = inspectorQuery.safeParse(context.req.query());
|
|
319
|
+
if (!parsed.success) throw new HTTPException(400, { message: "invalid context receipt query" });
|
|
320
|
+
const before = parsed.data.cursor
|
|
321
|
+
? decodeReceiptCursor(parsed.data.cursor, workspaceId, grant.subjectId)
|
|
322
|
+
: undefined;
|
|
323
|
+
const rows = await inspectCompanyBrainContextReceipts(deps.db, {
|
|
324
|
+
workspaceId,
|
|
325
|
+
subjectId: grant.subjectId,
|
|
326
|
+
...(parsed.data.attemptId ? { attemptId: parsed.data.attemptId } : {}),
|
|
327
|
+
...(before ? { before } : {}),
|
|
328
|
+
limit: parsed.data.attemptId ? 1 : parsed.data.limit + 1,
|
|
329
|
+
});
|
|
330
|
+
const hasMore = !parsed.data.attemptId && rows.length > parsed.data.limit;
|
|
331
|
+
const receipts = rows.slice(0, parsed.data.limit);
|
|
332
|
+
const tail = receipts.at(-1);
|
|
333
|
+
const response = CompanyBrainContextReceiptPage.parse({
|
|
334
|
+
receipts,
|
|
335
|
+
hasMore,
|
|
336
|
+
nextCursor:
|
|
337
|
+
hasMore && tail
|
|
338
|
+
? encodeReceiptCursor(workspaceId, grant.subjectId, {
|
|
339
|
+
createdAt: tail.createdAt,
|
|
340
|
+
id: tail.id,
|
|
341
|
+
})
|
|
342
|
+
: null,
|
|
343
|
+
});
|
|
344
|
+
context.header("cache-control", "private, no-store");
|
|
345
|
+
return context.json(response);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
app.get(`${base}/knowledge-proposals`, async (context) => {
|
|
349
|
+
const workspaceId = context.req.param("workspaceId");
|
|
350
|
+
const grant = await requireAccessGrant(context, deps, workspaceId, "documents:search");
|
|
351
|
+
const parsed = z.coerce
|
|
352
|
+
.number()
|
|
353
|
+
.int()
|
|
354
|
+
.positive()
|
|
355
|
+
.max(COMPANY_BRAIN_INSPECTOR_MAX_LIMIT)
|
|
356
|
+
.default(COMPANY_BRAIN_INSPECTOR_DEFAULT_LIMIT)
|
|
357
|
+
.safeParse(context.req.query("limit"));
|
|
358
|
+
if (!parsed.success) throw new HTTPException(400, { message: "invalid proposal limit" });
|
|
359
|
+
const response = await listCompanyBrainKnowledgeProposals(deps.db, {
|
|
360
|
+
workspaceId,
|
|
361
|
+
subjectId: grant.subjectId,
|
|
362
|
+
limit: parsed.data,
|
|
363
|
+
});
|
|
364
|
+
context.header("cache-control", "private, no-store");
|
|
365
|
+
return context.json(CompanyBrainKnowledgeProposalPage.parse(response));
|
|
366
|
+
});
|
|
367
|
+
}
|
|
@@ -42,7 +42,7 @@ async function parseBody<S extends z.ZodType>(context: Context, schema: S): Prom
|
|
|
42
42
|
return parsed.data;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
function requireDirectAccountAdmin(access: AccessGrantAuthorization): void {
|
|
45
|
+
export function requireDirectAccountAdmin(access: AccessGrantAuthorization): void {
|
|
46
46
|
const { grant } = access;
|
|
47
47
|
if (
|
|
48
48
|
!access.contextIntegrity ||
|