@opengeni/api-router 2.3.2-canary.2 → 2.5.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/app.js +1 -1
- package/dist/auth/managed-auth-attempt-context.d.ts +4 -0
- package/dist/auth/managed-auth-session-adapter.d.ts +4 -0
- package/dist/{chunk-IBV7Z6F4.js → chunk-QESX7HDK.js} +3645 -470
- package/dist/chunk-QESX7HDK.js.map +1 -0
- package/dist/fatal-process-boundary.d.ts +25 -0
- package/dist/http/sse.d.ts +2 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +168 -6
- package/dist/index.js.map +1 -1
- package/dist/integrations/slack-interactions.d.ts +1 -1
- package/dist/mcp/receipts.d.ts +9 -0
- package/dist/mcp/server.d.ts +33 -0
- package/dist/organization-recovery-notifications.d.ts +41 -0
- package/dist/routes/managed-auth-session-sets.d.ts +19 -0
- package/dist/routes/organization-recovery.d.ts +19 -0
- package/dist/routes/sessions.d.ts +17 -5
- package/dist/routes/workspaces.d.ts +1 -0
- package/dist/work-discovery-observability.d.ts +33 -0
- package/package.json +18 -18
- package/src/app.ts +133 -1
- package/src/auth/managed-auth-attempt-context.ts +24 -0
- package/src/auth/managed-auth-session-adapter.ts +205 -0
- package/src/auth/managed-auth.ts +52 -2
- package/src/fatal-process-boundary.ts +231 -0
- package/src/http/sse.ts +7 -0
- package/src/index.ts +25 -5
- package/src/integrations/slack-interactions.ts +30 -17
- package/src/mcp/receipts.ts +34 -0
- package/src/mcp/server.ts +349 -68
- package/src/organization-recovery-notifications.ts +103 -0
- package/src/routes/canonical-human-identities.ts +29 -14
- package/src/routes/codex.ts +5 -1
- package/src/routes/environments.ts +23 -0
- package/src/routes/interaction-resources.ts +3 -0
- package/src/routes/managed-auth-session-sets.ts +994 -0
- package/src/routes/managed-onboarding.ts +2 -0
- package/src/routes/organization-memberships.ts +2 -0
- package/src/routes/organization-recovery.ts +325 -0
- package/src/routes/sessions.ts +401 -120
- package/src/routes/supergrok.ts +5 -1
- package/src/routes/workspaces.ts +23 -1
- package/src/work-discovery-observability.ts +121 -0
- package/dist/chunk-IBV7Z6F4.js.map +0 -1
package/src/routes/supergrok.ts
CHANGED
|
@@ -80,7 +80,11 @@ async function managedCookieHuman(
|
|
|
80
80
|
) {
|
|
81
81
|
return null;
|
|
82
82
|
}
|
|
83
|
-
const session = await getManagedSession(c, deps.managedAuth
|
|
83
|
+
const session = await getManagedSession(c, deps.managedAuth, {
|
|
84
|
+
db: deps.db,
|
|
85
|
+
sessionAdapter: deps.managedAuthSessionAdapter,
|
|
86
|
+
sessionSetMode: deps.settings.managedAuthSessionSetMode,
|
|
87
|
+
});
|
|
84
88
|
return session?.user?.id ? { subjectId: `user:${session.user.id}` } : null;
|
|
85
89
|
}
|
|
86
90
|
|
package/src/routes/workspaces.ts
CHANGED
|
@@ -45,6 +45,7 @@ import { boundWorkspaceControlHttpPage } from "@opengeni/events";
|
|
|
45
45
|
import type { Hono } from "hono";
|
|
46
46
|
import { HTTPException } from "hono/http-exception";
|
|
47
47
|
import {
|
|
48
|
+
getManagedAuthRequestActorEpoch,
|
|
48
49
|
hasPermission,
|
|
49
50
|
requireAccessContext,
|
|
50
51
|
requireAccessGrant,
|
|
@@ -58,6 +59,7 @@ import {
|
|
|
58
59
|
resolveMemberSubjectId,
|
|
59
60
|
} from "@opengeni/core";
|
|
60
61
|
import { boundedLimit } from "../http/common";
|
|
62
|
+
import { ApiHttpError } from "../http/api-error";
|
|
61
63
|
import { sseWorkspaceControlStream } from "../http/sse";
|
|
62
64
|
import { buildWorkspaceModelCatalog } from "../model-catalog";
|
|
63
65
|
import { processTemporalScheduleCleanupClaims } from "../temporal-schedule-cleanup";
|
|
@@ -97,6 +99,14 @@ export function workspaceMembersResponse(members: readonly WorkspaceMemberProjec
|
|
|
97
99
|
});
|
|
98
100
|
}
|
|
99
101
|
|
|
102
|
+
export function workspaceUpdateRequestsAccountTransfer(value: unknown): boolean {
|
|
103
|
+
return (
|
|
104
|
+
typeof value === "object" &&
|
|
105
|
+
value !== null &&
|
|
106
|
+
Object.prototype.hasOwnProperty.call(value, "accountId")
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
100
110
|
export function registerWorkspaceRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
101
111
|
app.get("/v1/access/me", async (c) => {
|
|
102
112
|
return c.json(await requireAccessContext(c, deps));
|
|
@@ -163,7 +173,18 @@ export function registerWorkspaceRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
163
173
|
app.patch("/v1/workspaces/:workspaceId", async (c) => {
|
|
164
174
|
const workspaceId = c.req.param("workspaceId");
|
|
165
175
|
await requireAccessGrant(c, deps, workspaceId, "workspace:admin");
|
|
166
|
-
const
|
|
176
|
+
const body = await c.req.json();
|
|
177
|
+
if (workspaceUpdateRequestsAccountTransfer(body)) {
|
|
178
|
+
throw new ApiHttpError(409, {
|
|
179
|
+
code: "conflict",
|
|
180
|
+
message:
|
|
181
|
+
"A workspace is permanently owned by one organization; use workspace grants for same-organization access handoff.",
|
|
182
|
+
retryable: false,
|
|
183
|
+
outcomeUnknown: false,
|
|
184
|
+
details: { code: "workspace_transfer_unsupported" },
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
const payload = UpdateWorkspaceRequest.parse(body);
|
|
167
188
|
const workspace = await updateWorkspace(deps.db, workspaceId, {
|
|
168
189
|
...(payload.name !== undefined ? { name: payload.name.trim() } : {}),
|
|
169
190
|
...(payload.slug !== undefined ? { slug: payload.slug?.trim() || null } : {}),
|
|
@@ -363,6 +384,7 @@ export function registerWorkspaceRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
363
384
|
c.req.raw.signal,
|
|
364
385
|
{
|
|
365
386
|
observability: deps.observability,
|
|
387
|
+
actorEpoch: getManagedAuthRequestActorEpoch(c.req.raw) ?? undefined,
|
|
366
388
|
reauthorize: async () => {
|
|
367
389
|
await requireFreshAccessGrant(c, deps, workspaceId, "workspace:read");
|
|
368
390
|
},
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { WorkDiscoveryMatchClass } from "@opengeni/contracts";
|
|
2
|
+
import type { Observability } from "@opengeni/observability";
|
|
3
|
+
|
|
4
|
+
export type WorkDiscoverySurface = "first_party_mcp" | "agent_topology";
|
|
5
|
+
export type WorkDiscoveryMode = "browse" | "query" | "subject";
|
|
6
|
+
export type WorkDiscoveryOutcome = "ok" | "empty" | "disabled" | "error";
|
|
7
|
+
export type WorkDiscoveryAuthorizationScope = "workspace" | "scoped";
|
|
8
|
+
|
|
9
|
+
export type WorkDiscoveryObservation = {
|
|
10
|
+
surface: WorkDiscoverySurface;
|
|
11
|
+
mode: WorkDiscoveryMode;
|
|
12
|
+
outcome: WorkDiscoveryOutcome;
|
|
13
|
+
authorizationScope: WorkDiscoveryAuthorizationScope;
|
|
14
|
+
durationMs: number;
|
|
15
|
+
responseBytes: number;
|
|
16
|
+
resultCount: number;
|
|
17
|
+
overlapCount: number;
|
|
18
|
+
matchCounts: Partial<Record<WorkDiscoveryMatchClass, number>>;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type WorkDiscoveryObservedRow = {
|
|
22
|
+
relatedWork: {
|
|
23
|
+
match: { class: WorkDiscoveryMatchClass } | null;
|
|
24
|
+
possibleOverlap: boolean;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const RESULT_BUCKETS = [0, 1, 2, 4, 8, 16, 32, 64, 100];
|
|
29
|
+
const RESPONSE_BYTE_BUCKETS = [512, 1_024, 4_096, 16_384, 65_536, 128_000, 262_144];
|
|
30
|
+
const DURATION_BUCKETS = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5];
|
|
31
|
+
|
|
32
|
+
export function summarizeWorkDiscoveryRows(
|
|
33
|
+
rows: readonly WorkDiscoveryObservedRow[],
|
|
34
|
+
): Pick<WorkDiscoveryObservation, "resultCount" | "overlapCount" | "matchCounts"> {
|
|
35
|
+
const matchCounts: WorkDiscoveryObservation["matchCounts"] = {};
|
|
36
|
+
let overlapCount = 0;
|
|
37
|
+
for (const row of rows) {
|
|
38
|
+
if (row.relatedWork.possibleOverlap) overlapCount += 1;
|
|
39
|
+
const matchClass = row.relatedWork.match?.class;
|
|
40
|
+
if (matchClass) matchCounts[matchClass] = (matchCounts[matchClass] ?? 0) + 1;
|
|
41
|
+
}
|
|
42
|
+
return { resultCount: rows.length, overlapCount, matchCounts };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Low-cardinality work-discovery telemetry. Identifiers, search text, subject
|
|
47
|
+
* keys, titles, goals, claim labels, versions, and provenance are excluded by
|
|
48
|
+
* construction. An observability failure never changes product behavior.
|
|
49
|
+
*/
|
|
50
|
+
export function observeWorkDiscovery(
|
|
51
|
+
observability: Observability | null | undefined,
|
|
52
|
+
observation: WorkDiscoveryObservation,
|
|
53
|
+
): void {
|
|
54
|
+
if (!observability) return;
|
|
55
|
+
const labels = {
|
|
56
|
+
surface: observation.surface,
|
|
57
|
+
mode: observation.mode,
|
|
58
|
+
outcome: observation.outcome,
|
|
59
|
+
authorization_scope: observation.authorizationScope,
|
|
60
|
+
};
|
|
61
|
+
try {
|
|
62
|
+
observability.incrementCounter({
|
|
63
|
+
name: "opengeni_work_discovery_requests_total",
|
|
64
|
+
help: "Advisory work-discovery requests by bounded surface, mode, outcome, and authorization scope.",
|
|
65
|
+
labels,
|
|
66
|
+
});
|
|
67
|
+
observability.observeHistogram({
|
|
68
|
+
name: "opengeni_work_discovery_duration_seconds",
|
|
69
|
+
help: "Advisory work-discovery request duration in seconds.",
|
|
70
|
+
labels,
|
|
71
|
+
buckets: DURATION_BUCKETS,
|
|
72
|
+
value: Math.max(0, observation.durationMs) / 1_000,
|
|
73
|
+
});
|
|
74
|
+
observability.observeHistogram({
|
|
75
|
+
name: "opengeni_work_discovery_results",
|
|
76
|
+
help: "Bounded advisory work-discovery result rows per request.",
|
|
77
|
+
labels: { surface: observation.surface, mode: observation.mode },
|
|
78
|
+
buckets: RESULT_BUCKETS,
|
|
79
|
+
value: Math.max(0, Math.floor(observation.resultCount)),
|
|
80
|
+
});
|
|
81
|
+
observability.observeHistogram({
|
|
82
|
+
name: "opengeni_work_discovery_response_bytes",
|
|
83
|
+
help: "Serialized advisory work-discovery response bytes.",
|
|
84
|
+
labels: { surface: observation.surface, mode: observation.mode },
|
|
85
|
+
buckets: RESPONSE_BYTE_BUCKETS,
|
|
86
|
+
value: Math.max(0, Math.floor(observation.responseBytes)),
|
|
87
|
+
});
|
|
88
|
+
if (observation.overlapCount > 0) {
|
|
89
|
+
observability.incrementCounter({
|
|
90
|
+
name: "opengeni_work_discovery_overlap_results_total",
|
|
91
|
+
help: "Advisory discovery rows carrying a possible-overlap explanation.",
|
|
92
|
+
labels: { surface: observation.surface, mode: observation.mode },
|
|
93
|
+
amount: Math.floor(observation.overlapCount),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
for (const matchClass of ["exact_subject", "title", "goal", "fuzzy"] as const) {
|
|
97
|
+
const amount = Math.floor(observation.matchCounts[matchClass] ?? 0);
|
|
98
|
+
if (amount < 1) continue;
|
|
99
|
+
observability.incrementCounter({
|
|
100
|
+
name: "opengeni_work_discovery_matches_total",
|
|
101
|
+
help: "Advisory discovery matches by stable explanation class.",
|
|
102
|
+
labels: {
|
|
103
|
+
surface: observation.surface,
|
|
104
|
+
mode: observation.mode,
|
|
105
|
+
match_class: matchClass,
|
|
106
|
+
},
|
|
107
|
+
amount,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
try {
|
|
112
|
+
observability.incrementCounter({
|
|
113
|
+
name: "opengeni_observability_observer_errors_total",
|
|
114
|
+
help: "Observability observer failures isolated from product execution.",
|
|
115
|
+
labels: { observer: "work_discovery" },
|
|
116
|
+
});
|
|
117
|
+
} catch {
|
|
118
|
+
// The metrics registry itself is unhealthy. Discovery remains authoritative.
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|