@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/codemode.ts
CHANGED
|
@@ -31,12 +31,33 @@ export type CodemodeGrantAuthority = {
|
|
|
31
31
|
subjectId: string;
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
+
export type CodemodeAuthorityFailureReason =
|
|
35
|
+
| "invalid_grant"
|
|
36
|
+
| "inactive_attempt"
|
|
37
|
+
| "catalog_mismatch";
|
|
38
|
+
|
|
39
|
+
const CODEMODE_AUTHORITY_FAILURE_MESSAGES: Record<CodemodeAuthorityFailureReason, string> = {
|
|
40
|
+
invalid_grant: "Codemode bearer does not carry valid execution-attempt authority",
|
|
41
|
+
inactive_attempt: "Codemode execution attempt is no longer active",
|
|
42
|
+
catalog_mismatch: "Codemode tool catalog does not match the active execution attempt",
|
|
43
|
+
};
|
|
44
|
+
|
|
34
45
|
export class CodemodeAuthorityError extends Error {
|
|
35
|
-
readonly code
|
|
46
|
+
readonly code: `codemode_${CodemodeAuthorityFailureReason}`;
|
|
36
47
|
|
|
37
|
-
constructor() {
|
|
38
|
-
super(
|
|
48
|
+
constructor(readonly reason: CodemodeAuthorityFailureReason) {
|
|
49
|
+
super(CODEMODE_AUTHORITY_FAILURE_MESSAGES[reason]);
|
|
39
50
|
this.name = "CodemodeAuthorityError";
|
|
51
|
+
this.code = `codemode_${reason}`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export class CodemodeCatalogNotReadyError extends Error {
|
|
56
|
+
readonly code = "codemode_catalog_not_ready";
|
|
57
|
+
|
|
58
|
+
constructor() {
|
|
59
|
+
super("Codemode tool catalog is not ready for the active execution attempt");
|
|
60
|
+
this.name = "CodemodeCatalogNotReadyError";
|
|
40
61
|
}
|
|
41
62
|
}
|
|
42
63
|
|
|
@@ -71,11 +92,29 @@ export function isCodemodeGrant(grant: AccessGrant): boolean {
|
|
|
71
92
|
);
|
|
72
93
|
}
|
|
73
94
|
|
|
95
|
+
export function requireMatchingCodemodeCatalog(
|
|
96
|
+
authority: CodemodeGrantAuthority,
|
|
97
|
+
catalog: AttemptToolCatalog | null,
|
|
98
|
+
): AttemptToolCatalog {
|
|
99
|
+
if (!catalog) throw new CodemodeCatalogNotReadyError();
|
|
100
|
+
if (
|
|
101
|
+
catalog.accountId !== authority.accountId ||
|
|
102
|
+
catalog.workspaceId !== authority.workspaceId ||
|
|
103
|
+
catalog.sessionId !== authority.sessionId ||
|
|
104
|
+
catalog.turnId !== authority.turnId ||
|
|
105
|
+
catalog.attemptId !== authority.attemptId ||
|
|
106
|
+
catalog.executionGeneration !== authority.executionGeneration
|
|
107
|
+
) {
|
|
108
|
+
throw new CodemodeAuthorityError("catalog_mismatch");
|
|
109
|
+
}
|
|
110
|
+
return catalog;
|
|
111
|
+
}
|
|
112
|
+
|
|
74
113
|
export async function requireActiveCodemodeCatalog(
|
|
75
114
|
deps: ApiRouteDeps,
|
|
76
115
|
grant: AccessGrant,
|
|
77
116
|
): Promise<{ authority: CodemodeGrantAuthority; catalog: AttemptToolCatalog }> {
|
|
78
|
-
if (!isCodemodeGrant(grant)) throw new CodemodeAuthorityError();
|
|
117
|
+
if (!isCodemodeGrant(grant)) throw new CodemodeAuthorityError("invalid_grant");
|
|
79
118
|
const authority = codemodeAuthorityForGrant(grant)!;
|
|
80
119
|
await requireSessionAuthorization(deps, grant, {
|
|
81
120
|
sessionId: authority.sessionId,
|
|
@@ -94,24 +133,16 @@ export async function requireActiveCodemodeCatalog(
|
|
|
94
133
|
active.activeAttemptId !== authority.attemptId ||
|
|
95
134
|
active.executionGeneration !== authority.executionGeneration
|
|
96
135
|
) {
|
|
97
|
-
throw new CodemodeAuthorityError();
|
|
98
|
-
}
|
|
99
|
-
const catalog = await getAttemptToolCatalog(deps.db, {
|
|
100
|
-
accountId: authority.accountId,
|
|
101
|
-
workspaceId: authority.workspaceId,
|
|
102
|
-
attemptId: authority.attemptId,
|
|
103
|
-
});
|
|
104
|
-
if (
|
|
105
|
-
!catalog ||
|
|
106
|
-
catalog.accountId !== authority.accountId ||
|
|
107
|
-
catalog.workspaceId !== authority.workspaceId ||
|
|
108
|
-
catalog.sessionId !== authority.sessionId ||
|
|
109
|
-
catalog.turnId !== authority.turnId ||
|
|
110
|
-
catalog.attemptId !== authority.attemptId ||
|
|
111
|
-
catalog.executionGeneration !== authority.executionGeneration
|
|
112
|
-
) {
|
|
113
|
-
throw new CodemodeAuthorityError();
|
|
136
|
+
throw new CodemodeAuthorityError("inactive_attempt");
|
|
114
137
|
}
|
|
138
|
+
const catalog = requireMatchingCodemodeCatalog(
|
|
139
|
+
authority,
|
|
140
|
+
await getAttemptToolCatalog(deps.db, {
|
|
141
|
+
accountId: authority.accountId,
|
|
142
|
+
workspaceId: authority.workspaceId,
|
|
143
|
+
attemptId: authority.attemptId,
|
|
144
|
+
}),
|
|
145
|
+
);
|
|
115
146
|
return { authority, catalog };
|
|
116
147
|
}
|
|
117
148
|
|
|
@@ -128,7 +159,6 @@ export async function submitAndDispatchCodemodeCall(
|
|
|
128
159
|
...request,
|
|
129
160
|
caller: { kind: "codemode", subjectId: authority.subjectId },
|
|
130
161
|
},
|
|
131
|
-
callLimit: deps.settings.codemodeMaxCallsPerTurn,
|
|
132
162
|
});
|
|
133
163
|
let operation = submitted.operation;
|
|
134
164
|
let dispatch: CodemodeCallSubmissionValue["dispatch"] = terminal(operation)
|
|
@@ -171,7 +201,7 @@ export async function readCodemodeOperation(
|
|
|
171
201
|
grant: AccessGrant,
|
|
172
202
|
operationId: string,
|
|
173
203
|
): Promise<CodemodeOperation | null> {
|
|
174
|
-
if (!isCodemodeGrant(grant)) throw new CodemodeAuthorityError();
|
|
204
|
+
if (!isCodemodeGrant(grant)) throw new CodemodeAuthorityError("invalid_grant");
|
|
175
205
|
const authority = codemodeAuthorityForGrant(grant)!;
|
|
176
206
|
return await getCodemodeOperation(deps.db, {
|
|
177
207
|
accountId: authority.accountId,
|
package/src/codex-realtime.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
CodexRealtimeError,
|
|
6
6
|
CodexReloginRequired,
|
|
7
7
|
createCodexRealtimeCall,
|
|
8
|
+
fetchCodexRealtimeProviderConfig,
|
|
8
9
|
selectCodexCredentialId,
|
|
9
10
|
type CodexAuthHeaders,
|
|
10
11
|
type CodexFetch,
|
|
@@ -295,8 +296,13 @@ export function buildSessionCodexRealtimeBroker(
|
|
|
295
296
|
},
|
|
296
297
|
tokenResolver: (credentialId) =>
|
|
297
298
|
buildCodexTokenResolver(db, settings, workspaceId, credentialId),
|
|
298
|
-
createCall: async (auth, callInput, options) =>
|
|
299
|
-
await
|
|
299
|
+
createCall: async (auth, callInput, options) => {
|
|
300
|
+
const providerConfig = await fetchCodexRealtimeProviderConfig(auth, fetchImpl, options);
|
|
301
|
+
return await createCodexRealtimeCall(auth, callInput, fetchImpl, {
|
|
302
|
+
...options,
|
|
303
|
+
providerConfig,
|
|
304
|
+
});
|
|
305
|
+
},
|
|
300
306
|
},
|
|
301
307
|
{ ...input, sessionId },
|
|
302
308
|
);
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import {
|
|
2
|
+
COMPANY_BRAIN_GUIDANCE_MAX_CONTENT_BYTES,
|
|
3
|
+
COMPANY_BRAIN_GUIDANCE_MAX_ENTRIES,
|
|
4
|
+
CompanyBrainOkfPackage,
|
|
5
|
+
type CompanyBrainGuidanceEntry,
|
|
6
|
+
type CompanyBrainGuidanceTruncationReason,
|
|
7
|
+
type CompanyBrainOkfPackage as CompanyBrainOkfPackageType,
|
|
8
|
+
type CompanyProfileListResponse,
|
|
9
|
+
type WorkspaceInstructionPolicyListResponse,
|
|
10
|
+
type WorkspaceInstructionPolicyRevision,
|
|
11
|
+
type WorkspaceStateKnowledge,
|
|
12
|
+
} from "@opengeni/contracts";
|
|
13
|
+
import type { CompanyBrainPreferenceGuidancePage } from "@opengeni/db";
|
|
14
|
+
|
|
15
|
+
const OKF_MARKER = "<!-- opengeni-company-brain-okf-v1 -->";
|
|
16
|
+
|
|
17
|
+
function canonicalize(value: unknown): unknown {
|
|
18
|
+
if (Array.isArray(value)) return value.map(canonicalize);
|
|
19
|
+
if (value === null || typeof value !== "object") return value;
|
|
20
|
+
return Object.fromEntries(
|
|
21
|
+
Object.entries(value as Record<string, unknown>)
|
|
22
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
23
|
+
.map(([key, nested]) => [key, canonicalize(nested)]),
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function canonicalJson(value: unknown): string {
|
|
28
|
+
return `${JSON.stringify(canonicalize(value), null, 2)}\n`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function companyProfileEntries(profile: CompanyProfileListResponse): {
|
|
32
|
+
entries: CompanyBrainGuidanceEntry[];
|
|
33
|
+
truncated: boolean;
|
|
34
|
+
} {
|
|
35
|
+
const activeRevisionId = profile.current?.revisionId ?? null;
|
|
36
|
+
const byId = new Map(profile.revisions.map((revision) => [revision.id, revision] as const));
|
|
37
|
+
if (profile.activeRevision) byId.set(profile.activeRevision.id, profile.activeRevision);
|
|
38
|
+
return {
|
|
39
|
+
entries: [...byId.values()].map((revision) => {
|
|
40
|
+
const active = revision.id === activeRevisionId;
|
|
41
|
+
return {
|
|
42
|
+
id: "company-profile",
|
|
43
|
+
revisionId: revision.id,
|
|
44
|
+
path: "company/profile",
|
|
45
|
+
scope: "organization",
|
|
46
|
+
classification: "company_profile",
|
|
47
|
+
title: "Company profile",
|
|
48
|
+
description: "Company identity, mission, products, customers, goals, and constraints.",
|
|
49
|
+
// Company-profile hashes cover this exact canonical field order.
|
|
50
|
+
content: JSON.stringify(revision.profile),
|
|
51
|
+
contentHash: revision.contentHash,
|
|
52
|
+
revision: revision.revision,
|
|
53
|
+
active,
|
|
54
|
+
lifecycle: active ? "active" : revision.intent === "proposal" ? "proposal" : "historical",
|
|
55
|
+
provenance: {
|
|
56
|
+
source: revision.provenance.source,
|
|
57
|
+
sourceId: revision.provenance.sourceId,
|
|
58
|
+
trust: revision.intent === "proposal" ? "untrusted_proposal" : "organization_managed",
|
|
59
|
+
},
|
|
60
|
+
relationships:
|
|
61
|
+
revision.supersedesRevisionId && byId.has(revision.supersedesRevisionId)
|
|
62
|
+
? [{ type: "supersedes" as const, targetId: revision.supersedesRevisionId }]
|
|
63
|
+
: [],
|
|
64
|
+
createdAt: revision.createdAt,
|
|
65
|
+
} satisfies CompanyBrainGuidanceEntry;
|
|
66
|
+
}),
|
|
67
|
+
truncated: profile.nextAfterRevision !== null,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function instructionPolicyEntries(
|
|
72
|
+
policies: WorkspaceInstructionPolicyListResponse,
|
|
73
|
+
activeRevisions: WorkspaceInstructionPolicyRevision[],
|
|
74
|
+
activatedRevisionIds: string[],
|
|
75
|
+
): { entries: CompanyBrainGuidanceEntry[]; truncated: boolean } {
|
|
76
|
+
const byId = new Map(policies.revisions.map((revision) => [revision.id, revision] as const));
|
|
77
|
+
for (const revision of activeRevisions) byId.set(revision.id, revision);
|
|
78
|
+
const activeIds = new Set(policies.activeHeads.map((head) => head.revisionId));
|
|
79
|
+
const activatedIds = new Set(activatedRevisionIds);
|
|
80
|
+
return {
|
|
81
|
+
entries: [...byId.values()].map((revision) => {
|
|
82
|
+
const roleSegment = revision.roleKey ?? "all";
|
|
83
|
+
const active = activeIds.has(revision.id);
|
|
84
|
+
const lifecycle = active
|
|
85
|
+
? "active"
|
|
86
|
+
: activatedIds.has(revision.id)
|
|
87
|
+
? "historical"
|
|
88
|
+
: revision.provenance.source === "onboarding" ||
|
|
89
|
+
revision.provenance.source === "knowledge_proposal"
|
|
90
|
+
? "proposal"
|
|
91
|
+
: "inactive";
|
|
92
|
+
return {
|
|
93
|
+
id: `instruction-policy:${revision.kind}:${revision.scope}:${roleSegment}`,
|
|
94
|
+
revisionId: revision.id,
|
|
95
|
+
path: `ways-of-working/rules/${revision.kind}/${revision.scope}/${roleSegment}`,
|
|
96
|
+
scope: "workspace",
|
|
97
|
+
classification: "mandatory_rule",
|
|
98
|
+
title:
|
|
99
|
+
revision.kind === "charter"
|
|
100
|
+
? "Workspace charter"
|
|
101
|
+
: revision.scope === "role"
|
|
102
|
+
? `Role policy: ${roleSegment}`
|
|
103
|
+
: "Workspace policy",
|
|
104
|
+
description: active ? "Mandatory context included for applicable agents." : null,
|
|
105
|
+
content: revision.content,
|
|
106
|
+
contentHash: revision.contentHash,
|
|
107
|
+
revision: revision.revision,
|
|
108
|
+
active,
|
|
109
|
+
lifecycle,
|
|
110
|
+
provenance: {
|
|
111
|
+
source: revision.provenance.source,
|
|
112
|
+
sourceId: revision.provenance.sourceId,
|
|
113
|
+
trust:
|
|
114
|
+
revision.provenance.source === "human"
|
|
115
|
+
? "workspace_managed"
|
|
116
|
+
: revision.provenance.source === "legacy_import"
|
|
117
|
+
? "migrated"
|
|
118
|
+
: "untrusted_proposal",
|
|
119
|
+
},
|
|
120
|
+
relationships:
|
|
121
|
+
revision.supersedesRevisionId && byId.has(revision.supersedesRevisionId)
|
|
122
|
+
? [{ type: "supersedes" as const, targetId: revision.supersedesRevisionId }]
|
|
123
|
+
: [],
|
|
124
|
+
createdAt: revision.createdAt,
|
|
125
|
+
} satisfies CompanyBrainGuidanceEntry;
|
|
126
|
+
}),
|
|
127
|
+
truncated: policies.nextAfterRevision !== null,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function preferenceLifecycle(
|
|
132
|
+
row: CompanyBrainPreferenceGuidancePage["rows"][number],
|
|
133
|
+
generatedAt: string,
|
|
134
|
+
): CompanyBrainGuidanceEntry["lifecycle"] {
|
|
135
|
+
if (row.active && row.expiresAt && row.expiresAt.getTime() <= Date.parse(generatedAt)) {
|
|
136
|
+
return "expired";
|
|
137
|
+
}
|
|
138
|
+
if (row.active && row.status === "active") return "active";
|
|
139
|
+
if (row.status === "proposed") return "proposal";
|
|
140
|
+
if (row.status === "inactive") return "inactive";
|
|
141
|
+
if (row.status === "rejected") return "rejected";
|
|
142
|
+
if (row.status === "superseded") return "superseded";
|
|
143
|
+
return "historical";
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function preferenceEntries(
|
|
147
|
+
page: CompanyBrainPreferenceGuidancePage,
|
|
148
|
+
generatedAt: string,
|
|
149
|
+
): CompanyBrainGuidanceEntry[] {
|
|
150
|
+
const visibleRevisionIds = new Set(page.rows.map((row) => row.revisionId));
|
|
151
|
+
const visiblePreferenceIds = new Set(page.rows.map((row) => row.preferenceId));
|
|
152
|
+
return page.rows.map((row) => {
|
|
153
|
+
const scope =
|
|
154
|
+
row.scope === "organization"
|
|
155
|
+
? "organization"
|
|
156
|
+
: row.scope === "user"
|
|
157
|
+
? "personal"
|
|
158
|
+
: "workspace";
|
|
159
|
+
const lifecycle = preferenceLifecycle(row, generatedAt);
|
|
160
|
+
const relationships: CompanyBrainGuidanceEntry["relationships"] = [];
|
|
161
|
+
if (row.correctsRevisionId && visibleRevisionIds.has(row.correctsRevisionId)) {
|
|
162
|
+
relationships.push({ type: "corrects", targetId: row.correctsRevisionId });
|
|
163
|
+
}
|
|
164
|
+
if (row.supersededByPreferenceId && visiblePreferenceIds.has(row.supersededByPreferenceId)) {
|
|
165
|
+
relationships.push({
|
|
166
|
+
type: "superseded_by",
|
|
167
|
+
targetId: `preference:${row.supersededByPreferenceId}`,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
id: `preference:${row.preferenceId}`,
|
|
172
|
+
revisionId: row.revisionId,
|
|
173
|
+
path: `ways-of-working/guides/${scope}/${row.stableKey}`,
|
|
174
|
+
scope,
|
|
175
|
+
classification: "guide",
|
|
176
|
+
title: row.title,
|
|
177
|
+
description: row.description,
|
|
178
|
+
content: row.content,
|
|
179
|
+
contentHash: row.contentHash,
|
|
180
|
+
revision: row.revision,
|
|
181
|
+
active: lifecycle === "active",
|
|
182
|
+
lifecycle,
|
|
183
|
+
provenance: {
|
|
184
|
+
source: row.provenanceSource,
|
|
185
|
+
sourceId: row.provenanceSourceId,
|
|
186
|
+
trust: row.trust,
|
|
187
|
+
},
|
|
188
|
+
relationships,
|
|
189
|
+
createdAt: row.createdAt.toISOString(),
|
|
190
|
+
} satisfies CompanyBrainGuidanceEntry;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function guidanceEntryOrder(
|
|
195
|
+
left: CompanyBrainGuidanceEntry,
|
|
196
|
+
right: CompanyBrainGuidanceEntry,
|
|
197
|
+
): number {
|
|
198
|
+
return (
|
|
199
|
+
left.classification.localeCompare(right.classification) ||
|
|
200
|
+
left.path.localeCompare(right.path) ||
|
|
201
|
+
right.revision - left.revision ||
|
|
202
|
+
left.revisionId.localeCompare(right.revisionId)
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function relationshipTargetKeys(entry: CompanyBrainGuidanceEntry): string[] {
|
|
207
|
+
return [entry.id, entry.revisionId];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function boundCompanyBrainGuidanceEntries(entries: CompanyBrainGuidanceEntry[]): {
|
|
211
|
+
entries: CompanyBrainGuidanceEntry[];
|
|
212
|
+
itemCountTruncated: boolean;
|
|
213
|
+
contentBytesTruncated: boolean;
|
|
214
|
+
} {
|
|
215
|
+
const prioritized = [...entries].sort(
|
|
216
|
+
(left, right) => Number(right.active) - Number(left.active) || guidanceEntryOrder(left, right),
|
|
217
|
+
);
|
|
218
|
+
const selected: CompanyBrainGuidanceEntry[] = [];
|
|
219
|
+
let contentBytes = 0;
|
|
220
|
+
let itemCountTruncated = false;
|
|
221
|
+
let contentBytesTruncated = false;
|
|
222
|
+
for (const entry of prioritized) {
|
|
223
|
+
if (selected.length >= COMPANY_BRAIN_GUIDANCE_MAX_ENTRIES) {
|
|
224
|
+
itemCountTruncated = true;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const entryBytes = new TextEncoder().encode(entry.content).byteLength;
|
|
228
|
+
if (contentBytes + entryBytes > COMPANY_BRAIN_GUIDANCE_MAX_CONTENT_BYTES) {
|
|
229
|
+
contentBytesTruncated = true;
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
selected.push(entry);
|
|
233
|
+
contentBytes += entryBytes;
|
|
234
|
+
}
|
|
235
|
+
const visibleTargets = new Set(selected.flatMap(relationshipTargetKeys));
|
|
236
|
+
return {
|
|
237
|
+
entries: selected
|
|
238
|
+
.map((entry) => ({
|
|
239
|
+
...entry,
|
|
240
|
+
relationships: entry.relationships.filter((relationship) =>
|
|
241
|
+
visibleTargets.has(relationship.targetId),
|
|
242
|
+
),
|
|
243
|
+
}))
|
|
244
|
+
.sort(guidanceEntryOrder),
|
|
245
|
+
itemCountTruncated,
|
|
246
|
+
contentBytesTruncated,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function createCompanyBrainOkfPackage(input: {
|
|
251
|
+
workspaceId: string;
|
|
252
|
+
generatedAt: string;
|
|
253
|
+
companyProfile: CompanyProfileListResponse;
|
|
254
|
+
instructionPolicies: WorkspaceInstructionPolicyListResponse;
|
|
255
|
+
activeInstructionPolicyRevisions: WorkspaceInstructionPolicyRevision[];
|
|
256
|
+
activatedInstructionPolicyRevisionIds: string[];
|
|
257
|
+
preferences: CompanyBrainPreferenceGuidancePage;
|
|
258
|
+
knowledge: WorkspaceStateKnowledge;
|
|
259
|
+
}): CompanyBrainOkfPackageType {
|
|
260
|
+
const company = companyProfileEntries(input.companyProfile);
|
|
261
|
+
const policies = instructionPolicyEntries(
|
|
262
|
+
input.instructionPolicies,
|
|
263
|
+
input.activeInstructionPolicyRevisions,
|
|
264
|
+
input.activatedInstructionPolicyRevisionIds,
|
|
265
|
+
);
|
|
266
|
+
const preferences = preferenceEntries(input.preferences, input.generatedAt);
|
|
267
|
+
const truncationReasons: CompanyBrainGuidanceTruncationReason[] = [];
|
|
268
|
+
if (company.truncated) truncationReasons.push("company_profile_history");
|
|
269
|
+
if (policies.truncated) truncationReasons.push("instruction_policy_history");
|
|
270
|
+
if (input.preferences.preferenceCountTruncated) truncationReasons.push("preference_count");
|
|
271
|
+
if (input.preferences.revisionCountTruncated) truncationReasons.push("preference_history");
|
|
272
|
+
if (input.preferences.contentBytesTruncated) truncationReasons.push("aggregate_content_bytes");
|
|
273
|
+
|
|
274
|
+
const bounded = boundCompanyBrainGuidanceEntries([
|
|
275
|
+
...company.entries,
|
|
276
|
+
...policies.entries,
|
|
277
|
+
...preferences,
|
|
278
|
+
]);
|
|
279
|
+
if (bounded.itemCountTruncated) truncationReasons.push("aggregate_item_count");
|
|
280
|
+
if (bounded.contentBytesTruncated && !truncationReasons.includes("aggregate_content_bytes")) {
|
|
281
|
+
truncationReasons.push("aggregate_content_bytes");
|
|
282
|
+
}
|
|
283
|
+
const knowledgeAvailable = input.knowledge.availability === "available";
|
|
284
|
+
return CompanyBrainOkfPackage.parse({
|
|
285
|
+
kind: "opengeni.company_brain.okf",
|
|
286
|
+
schemaVersion: 1,
|
|
287
|
+
workspaceId: input.workspaceId,
|
|
288
|
+
generatedAt: input.generatedAt,
|
|
289
|
+
permissions: {
|
|
290
|
+
guidance: "available",
|
|
291
|
+
knowledge: knowledgeAvailable ? "available" : "unavailable",
|
|
292
|
+
},
|
|
293
|
+
guidance: {
|
|
294
|
+
entries: bounded.entries,
|
|
295
|
+
truncated: truncationReasons.length > 0,
|
|
296
|
+
truncationReasons,
|
|
297
|
+
},
|
|
298
|
+
knowledge: input.knowledge,
|
|
299
|
+
omissions: [
|
|
300
|
+
...(knowledgeAvailable ? [] : (["inaccessible_knowledge"] as const)),
|
|
301
|
+
"document_bodies_use_documents_export",
|
|
302
|
+
"memory_bodies_and_provenance",
|
|
303
|
+
"secret_values_and_credentials",
|
|
304
|
+
"session_messages_and_task_notes",
|
|
305
|
+
"policy_and_preference_actor_identifiers",
|
|
306
|
+
],
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* JSON is a strict subset of YAML 1.2. Keeping the payload in one fenced YAML
|
|
312
|
+
* block gives humans a readable Markdown package while making arbitrary
|
|
313
|
+
* guidance Markdown structurally inert and exactly round-trippable.
|
|
314
|
+
*/
|
|
315
|
+
export function serializeCompanyBrainOkf(value: CompanyBrainOkfPackageType): string {
|
|
316
|
+
const parsed = CompanyBrainOkfPackage.parse(value);
|
|
317
|
+
return [
|
|
318
|
+
OKF_MARKER,
|
|
319
|
+
"# OpenGeni Company Brain",
|
|
320
|
+
"",
|
|
321
|
+
"This permission-filtered package is portable evidence. Postgres remains canonical.",
|
|
322
|
+
"",
|
|
323
|
+
"```yaml",
|
|
324
|
+
canonicalJson(parsed).trimEnd(),
|
|
325
|
+
"```",
|
|
326
|
+
"",
|
|
327
|
+
].join("\n");
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export function parseCompanyBrainOkf(markdown: string): CompanyBrainOkfPackageType {
|
|
331
|
+
if (!markdown.startsWith(`${OKF_MARKER}\n`)) throw new Error("Invalid Company Brain OKF marker");
|
|
332
|
+
const opening = markdown.indexOf("```yaml\n");
|
|
333
|
+
if (opening < 0) throw new Error("Company Brain OKF payload is missing");
|
|
334
|
+
const payloadStart = opening + "```yaml\n".length;
|
|
335
|
+
const closing = markdown.indexOf("\n```", payloadStart);
|
|
336
|
+
if (closing < 0) throw new Error("Company Brain OKF payload is incomplete");
|
|
337
|
+
const trailing = markdown.slice(closing + "\n```".length).trim();
|
|
338
|
+
if (trailing.length > 0) throw new Error("Company Brain OKF has unexpected trailing content");
|
|
339
|
+
return CompanyBrainOkfPackage.parse(JSON.parse(markdown.slice(payloadStart, closing)));
|
|
340
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type ConnectedMachineComputerAccessState = {
|
|
2
|
+
hasDisplay: boolean;
|
|
3
|
+
desktopUnavailableReason: string | null;
|
|
4
|
+
allowScreenControl: boolean;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type ConnectedMachineComputerAccessError = {
|
|
8
|
+
status: 403 | 409;
|
|
9
|
+
message: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/** Keep passive desktop viewing separate from input consent. */
|
|
13
|
+
export function connectedMachineComputerAccessError(
|
|
14
|
+
state: ConnectedMachineComputerAccessState,
|
|
15
|
+
requiresControl: boolean,
|
|
16
|
+
): ConnectedMachineComputerAccessError | null {
|
|
17
|
+
if (!state.hasDisplay) {
|
|
18
|
+
const reason = state.desktopUnavailableReason?.trim();
|
|
19
|
+
return {
|
|
20
|
+
status: 409,
|
|
21
|
+
message: reason
|
|
22
|
+
? `Connected Machine desktop is unavailable. ${reason}`
|
|
23
|
+
: "Connected Machine desktop is unavailable because this machine has no capturable display.",
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (requiresControl && !state.allowScreenControl) {
|
|
27
|
+
return {
|
|
28
|
+
status: 403,
|
|
29
|
+
message: "Screen control is not enabled for this Connected Machine.",
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { AccessGrantAuthorization } from "@opengeni/core";
|
|
2
|
+
import type { UserResourceAuthoritySummary } from "@opengeni/db";
|
|
3
|
+
import {
|
|
4
|
+
IssueConnectionUseGrantRequest,
|
|
5
|
+
ListConnectionAuthoritiesResponse,
|
|
6
|
+
type IssueConnectionUseGrantRequest as IssueConnectionUseGrantRequestValue,
|
|
7
|
+
} from "@opengeni/contracts/connection-authority";
|
|
8
|
+
import { HTTPException } from "hono/http-exception";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Personal connection authority is available only to the exact authenticated
|
|
12
|
+
* human. API keys, delegated bearers, services, and agent attempts cannot name
|
|
13
|
+
* or substitute a personal owner.
|
|
14
|
+
*/
|
|
15
|
+
export function requireConnectionAuthorityOwner(access: AccessGrantAuthorization): string {
|
|
16
|
+
if (
|
|
17
|
+
!access.contextIntegrity ||
|
|
18
|
+
access.authenticatedSubjectId !== access.grant.subjectId ||
|
|
19
|
+
access.grant.principalKind !== "human_session" ||
|
|
20
|
+
access.grant.serviceInitiator ||
|
|
21
|
+
access.grant.serviceInitiatorContext ||
|
|
22
|
+
access.grant.subjectId.startsWith("api_key:")
|
|
23
|
+
) {
|
|
24
|
+
throw new HTTPException(403, { message: "authenticated connection owner required" });
|
|
25
|
+
}
|
|
26
|
+
return access.grant.subjectId;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Removes generic resource kind and refuses non-connection grant actions. */
|
|
30
|
+
export function projectSelfConnectionAuthorities(page: {
|
|
31
|
+
authorities: UserResourceAuthoritySummary[];
|
|
32
|
+
nextCursor: string | null;
|
|
33
|
+
}) {
|
|
34
|
+
return ListConnectionAuthoritiesResponse.parse({
|
|
35
|
+
scope: "user",
|
|
36
|
+
authorities: page.authorities
|
|
37
|
+
.filter((authority) => authority.resourceKind === "connection")
|
|
38
|
+
.map(({ resourceKind: _resourceKind, ...authority }) => authority),
|
|
39
|
+
nextCursor: page.nextCursor,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The API owns the action; callers supply neither action nor owner identity. */
|
|
44
|
+
export function connectionUseGrantLifecycleInput(input: unknown): {
|
|
45
|
+
action: "connection.use";
|
|
46
|
+
mode: IssueConnectionUseGrantRequestValue["mode"];
|
|
47
|
+
context: IssueConnectionUseGrantRequestValue["context"];
|
|
48
|
+
sessionId: string | null;
|
|
49
|
+
expectedAuthorityEpoch: number | null;
|
|
50
|
+
workspaceSharedAcknowledged: boolean;
|
|
51
|
+
} {
|
|
52
|
+
const request = IssueConnectionUseGrantRequest.parse(input);
|
|
53
|
+
return {
|
|
54
|
+
action: "connection.use",
|
|
55
|
+
mode: request.mode,
|
|
56
|
+
context: request.context,
|
|
57
|
+
sessionId: request.sessionId ?? null,
|
|
58
|
+
expectedAuthorityEpoch: request.expectedAuthorityEpoch ?? null,
|
|
59
|
+
workspaceSharedAcknowledged: request.workspaceSharedAcknowledged,
|
|
60
|
+
};
|
|
61
|
+
}
|