@opengeni/api-router 0.16.5 → 0.20.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.
@@ -3,6 +3,7 @@ import { OPENGENI_SLACK_BOT_CREDENTIAL_LABEL, OPENGENI_SLACK_BOT_CREDENTIAL_ROLE
3
3
  import { type Database } from "@opengeni/db";
4
4
  import { type FetchLike } from "@opengeni/network";
5
5
  import { HTTPException } from "hono/http-exception";
6
+ declare const SLACK_REACTION_CONTEXT_CHECKPOINT_VERSION = 1;
6
7
  type SlackPayload = Record<string, unknown> & {
7
8
  ok?: unknown;
8
9
  error?: unknown;
@@ -42,6 +43,44 @@ type SlackBotContext = {
42
43
  sessionId?: string | null;
43
44
  scheduledTaskId?: string | null;
44
45
  };
46
+ export type SlackReactionContextCheckpointBinding = {
47
+ inboxId: string;
48
+ accountId: string;
49
+ workspaceId: string;
50
+ connectionId: string;
51
+ providerEventId: string;
52
+ providerMessageId: string;
53
+ slackTeamId: string;
54
+ slackChannelId: string;
55
+ slackMessageTs: string;
56
+ };
57
+ type SlackReactionCheckpointMessage = {
58
+ timestamp: string;
59
+ userId: string;
60
+ botId: string;
61
+ threadTimestamp: string;
62
+ text: string;
63
+ files: Array<{
64
+ id: string;
65
+ label: string;
66
+ }>;
67
+ };
68
+ type SlackReactionContextCheckpointUnsigned = {
69
+ version: typeof SLACK_REACTION_CONTEXT_CHECKPOINT_VERSION;
70
+ binding: SlackReactionContextCheckpointBinding;
71
+ state: {
72
+ createdAtMs: number;
73
+ pageCount: number;
74
+ nextCursor: string;
75
+ seenCursors: string[];
76
+ seenMessageTimestamps: string[];
77
+ threadTimestamp: string | null;
78
+ messages: SlackReactionCheckpointMessage[];
79
+ };
80
+ };
81
+ export type SlackReactionContextCheckpoint = SlackReactionContextCheckpointUnsigned & {
82
+ signature: string;
83
+ };
45
84
  export declare class SlackBotProviderError extends Error {
46
85
  readonly code: string;
47
86
  readonly retryAfterMs: number | null;
@@ -89,6 +128,9 @@ export declare class OpenGeniSlackBotClient {
89
128
  isMember: boolean;
90
129
  isDirectMessage: boolean;
91
130
  isArchived: boolean;
131
+ isShared: boolean;
132
+ isExternallyShared: boolean;
133
+ isOrgShared: boolean;
92
134
  topic: string;
93
135
  purpose: string;
94
136
  numMembers: number | null;
@@ -104,6 +146,9 @@ export declare class OpenGeniSlackBotClient {
104
146
  isMember: boolean;
105
147
  isDirectMessage: boolean;
106
148
  isArchived: boolean;
149
+ isShared: boolean;
150
+ isExternallyShared: boolean;
151
+ isOrgShared: boolean;
107
152
  topic: string;
108
153
  purpose: string;
109
154
  numMembers: number | null;
@@ -120,6 +165,9 @@ export declare class OpenGeniSlackBotClient {
120
165
  isMember: boolean;
121
166
  isDirectMessage: boolean;
122
167
  isArchived: boolean;
168
+ isShared: boolean;
169
+ isExternallyShared: boolean;
170
+ isOrgShared: boolean;
123
171
  topic: string;
124
172
  purpose: string;
125
173
  numMembers: number | null;
@@ -159,6 +207,9 @@ export declare class OpenGeniSlackBotClient {
159
207
  isMember: boolean;
160
208
  isDirectMessage: boolean;
161
209
  isArchived: boolean;
210
+ isShared: boolean;
211
+ isExternallyShared: boolean;
212
+ isOrgShared: boolean;
162
213
  topic: string;
163
214
  purpose: string;
164
215
  numMembers: number | null;
@@ -186,6 +237,68 @@ export declare class OpenGeniSlackBotClient {
186
237
  } & {
187
238
  receipt: SlackBotReceipt;
188
239
  }>;
240
+ reactionMessageContext(input: {
241
+ channelId: string;
242
+ messageTimestamp: string;
243
+ checkpoint: unknown | null;
244
+ checkpointBinding: SlackReactionContextCheckpointBinding;
245
+ saveCheckpoint: (checkpoint: SlackReactionContextCheckpoint) => Promise<void>;
246
+ }): Promise<{
247
+ channel: {
248
+ id: string;
249
+ name: string;
250
+ isPrivate: boolean;
251
+ isMember: boolean;
252
+ isDirectMessage: boolean;
253
+ isArchived: boolean;
254
+ isShared: boolean;
255
+ isExternallyShared: boolean;
256
+ isOrgShared: boolean;
257
+ topic: string;
258
+ purpose: string;
259
+ numMembers: number | null;
260
+ };
261
+ threadTimestamp: string;
262
+ reactedMessage: {
263
+ timestamp: string;
264
+ userId: string;
265
+ botId: string;
266
+ threadTimestamp: string;
267
+ text: string;
268
+ files: {
269
+ id: string;
270
+ name: string;
271
+ title: string;
272
+ mimetype: string;
273
+ filetype: string;
274
+ mode: string;
275
+ size: number | null;
276
+ originatingHuddleId: string;
277
+ huddleTranscriptFileId: string;
278
+ }[];
279
+ };
280
+ messages: {
281
+ timestamp: string;
282
+ userId: string;
283
+ botId: string;
284
+ threadTimestamp: string;
285
+ text: string;
286
+ files: {
287
+ id: string;
288
+ name: string;
289
+ title: string;
290
+ mimetype: string;
291
+ filetype: string;
292
+ mode: string;
293
+ size: number | null;
294
+ originatingHuddleId: string;
295
+ huddleTranscriptFileId: string;
296
+ }[];
297
+ }[];
298
+ truncated: boolean;
299
+ } & {
300
+ receipt: SlackBotReceipt;
301
+ }>;
189
302
  listUsers(input?: {
190
303
  limit?: number;
191
304
  cursor?: string;
@@ -214,6 +327,9 @@ export declare class OpenGeniSlackBotClient {
214
327
  isMember: boolean;
215
328
  isDirectMessage: boolean;
216
329
  isArchived: boolean;
330
+ isShared: boolean;
331
+ isExternallyShared: boolean;
332
+ isOrgShared: boolean;
217
333
  topic: string;
218
334
  purpose: string;
219
335
  numMembers: number | null;
@@ -245,6 +361,9 @@ export declare class OpenGeniSlackBotClient {
245
361
  isMember: boolean;
246
362
  isDirectMessage: boolean;
247
363
  isArchived: boolean;
364
+ isShared: boolean;
365
+ isExternallyShared: boolean;
366
+ isOrgShared: boolean;
248
367
  topic: string;
249
368
  purpose: string;
250
369
  numMembers: number | null;
@@ -276,6 +395,9 @@ export declare class OpenGeniSlackBotClient {
276
395
  isMember: boolean;
277
396
  isDirectMessage: boolean;
278
397
  isArchived: boolean;
398
+ isShared: boolean;
399
+ isExternallyShared: boolean;
400
+ isOrgShared: boolean;
279
401
  topic: string;
280
402
  purpose: string;
281
403
  numMembers: number | null;
@@ -1,6 +1,8 @@
1
+ import { type WorkspaceSlackReactionSummonSettings } from "@opengeni/contracts";
1
2
  import { type SlackInstallationRoute, type SlackInteractionInboxEntry, type SlackInteractionTriggerKind } from "@opengeni/db";
2
3
  import { type ApiRouteDeps } from "@opengeni/core";
3
4
  import type { Hono } from "hono";
5
+ import { type OpenGeniSlackBotClient } from "./slack-bot";
4
6
  export declare const SLACK_INTERACTION_MAX_BODY_BYTES: number;
5
7
  export declare const SLACK_SIGNATURE_REPLAY_WINDOW_SECONDS = 300;
6
8
  export declare const SLACK_DELIVERY_EVENT_TYPES: readonly ["agent.message.completed", "session.humanInput.requested", "turn.completed", "turn.failed", "turn.cancelled", "session.status.changed"];
@@ -29,12 +31,15 @@ export declare function verifySlackRequestSignature(input: {
29
31
  rawBody: string;
30
32
  }, signingSecret: string, nowMs?: number): boolean;
31
33
  export declare function slackEventInboxEntry(payload: unknown, bot: Pick<SlackInstallationRoute, "botId" | "botUserId">): NormalizedSlackInteraction | null;
34
+ export declare function slackReactionInboxEntry(payload: unknown, bot: Pick<SlackInstallationRoute, "botUserId">, settings: WorkspaceSlackReactionSummonSettings): NormalizedSlackInteraction | null;
32
35
  export declare function registerSlackInteractionRoutes(app: Hono, deps: ApiRouteDeps): void;
33
36
  export declare function drainSlackInteractionsOnce(deps: ApiRouteDeps): Promise<boolean>;
34
37
  export declare function startSlackInteractionPump(deps: ApiRouteDeps, options?: {
35
38
  intervalMs?: number;
36
39
  maxPerTick?: number;
37
40
  }): () => void;
41
+ type SlackReactionMessageContext = Awaited<ReturnType<OpenGeniSlackBotClient["reactionMessageContext"]>>;
42
+ export declare function slackReactionTaskText(context: SlackReactionMessageContext): string;
38
43
  type SlackUserLinkToken = {
39
44
  workspaceId: string;
40
45
  connectionId: string;
@@ -1,8 +1,8 @@
1
1
  import { type DocumentServices } from "@opengeni/documents";
2
2
  import { type Database } from "@opengeni/db";
3
3
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
- export declare function buildDocumentsMcpServer(db: Database, accountId: string, workspaceId: string, documentServices: DocumentServices, options?: {
4
+ export declare function buildDocumentsMcpServer(db: Database, accountId: string, workspaceId: string, documentServices: DocumentServices, options: {
5
5
  createdBySessionId?: string | undefined;
6
- /** The human subject whose agent is making this retrieval request. */
7
- viewerSubjectId?: string | undefined;
6
+ /** Immutable human subject whose agent is making this retrieval request. */
7
+ initiatingSubjectId: string;
8
8
  }): McpServer;
@@ -70,7 +70,7 @@ export declare function capSessionDiscoveryPage(page: Awaited<ReturnType<typeof
70
70
  truncated: boolean;
71
71
  };
72
72
  latestMessage?: {
73
- type: "agent.message.completed" | "agent.message.delta" | "agent.model.request" | "agent.model.usage" | "agent.reasoning.delta" | "agent.toolCall.created" | "agent.toolCall.output" | "agent.updated" | "artifact.created" | "codex.account.switched" | "codex.capacity.resumed" | "codex.capacity.superseded" | "codex.capacity.waiting" | "codex.credential.selected" | "codex.fleet.decision" | "credential.auth_needed" | "fs.changed" | "git.changed" | "goal.cleared" | "goal.completed" | "goal.continuation" | "goal.paused" | "goal.resumed" | "goal.set" | "goal.updated" | "machine.link.lost" | "machine.link.restored" | "machine.op.failed" | "machine.op.recovered" | "machine.runner.restarted" | "memory.corrected" | "memory.saved" | "recording.available" | "recording.failed" | "recording.started" | "rig.setup.completed" | "rig.setup.failed" | "rig.setup.skipped" | "rig.setup.started" | "sandbox.box.created" | "sandbox.box.lost" | "sandbox.box.snapshot" | "sandbox.box.terminated" | "sandbox.command.output.delta" | "sandbox.env.drift" | "sandbox.operation.completed" | "sandbox.operation.failed" | "sandbox.operation.started" | "session.context.cleared" | "session.context.compacted" | "session.context.compaction.requested" | "session.context.compaction.skipped" | "session.context.compaction.started" | "session.control.paused" | "session.control.resumed" | "session.control.steer_requested" | "session.created" | "session.event.envelope_omitted" | "session.humanInput.requested" | "session.mcp.approval_policy.updated" | "session.queue.changed" | "session.queue.history" | "session.queue.prompt.cancelled" | "session.requiresAction" | "session.route.reconciled" | "session.status.changed" | "session.title_set" | "session.tool_policy.updated" | "stream.closed" | "stream.opened" | "stream.revoked" | "stream.url.rotated" | "system.update.cancelled" | "system.update.delivered" | "system.update.pending" | "system.update.settled" | "system.update.superseded" | "terminal.pty.exited" | "terminal.pty.output.delta" | "terminal.pty.started" | "tool.auth_needed" | "turn.cancelled" | "turn.capacity_waiting" | "turn.completed" | "turn.event.rejected_late" | "turn.failed" | "turn.queued" | "turn.recovery.requested" | "turn.started" | "turn.superseded" | "user.approvalDecision" | "user.humanInputResponse" | "user.message" | "user.pause" | "workspace.inference.paused" | "workspace.inference.resumed" | "workspace.revision.captured" | "workspace.revision.degraded";
73
+ type: "agent.message.completed" | "agent.message.delta" | "agent.model.request" | "agent.model.usage" | "agent.reasoning.delta" | "agent.toolCall.created" | "agent.toolCall.output" | "agent.updated" | "artifact.created" | "codex.account.switched" | "codex.capacity.resumed" | "codex.capacity.superseded" | "codex.capacity.waiting" | "codex.credential.selected" | "codex.fleet.decision" | "credential.auth_needed" | "fs.changed" | "git.changed" | "goal.cleared" | "goal.completed" | "goal.continuation" | "goal.paused" | "goal.resumed" | "goal.set" | "goal.updated" | "machine.link.lost" | "machine.link.restored" | "machine.op.failed" | "machine.op.recovered" | "machine.runner.restarted" | "memory.corrected" | "memory.saved" | "recording.available" | "recording.failed" | "recording.started" | "rig.setup.completed" | "rig.setup.failed" | "rig.setup.skipped" | "rig.setup.started" | "sandbox.box.created" | "sandbox.box.lost" | "sandbox.box.snapshot" | "sandbox.box.terminated" | "sandbox.command.output.delta" | "sandbox.env.drift" | "sandbox.operation.completed" | "sandbox.operation.failed" | "sandbox.operation.started" | "session.context.cleared" | "session.context.compacted" | "session.context.compaction.requested" | "session.context.compaction.skipped" | "session.context.compaction.started" | "session.control.paused" | "session.control.resumed" | "session.control.steer_requested" | "session.created" | "session.event.envelope_omitted" | "session.humanInput.requested" | "session.mcp.approval_policy.updated" | "session.queue.changed" | "session.queue.history" | "session.queue.prompt.cancelled" | "session.realtime.ended" | "session.realtime.started" | "session.requiresAction" | "session.route.reconciled" | "session.status.changed" | "session.title_set" | "session.tool_policy.updated" | "stream.closed" | "stream.opened" | "stream.revoked" | "stream.url.rotated" | "system.update.cancelled" | "system.update.delivered" | "system.update.pending" | "system.update.settled" | "system.update.superseded" | "terminal.pty.exited" | "terminal.pty.output.delta" | "terminal.pty.started" | "tool.auth_needed" | "turn.cancelled" | "turn.capacity_waiting" | "turn.completed" | "turn.event.rejected_late" | "turn.failed" | "turn.queued" | "turn.recovery.requested" | "turn.started" | "turn.superseded" | "user.approvalDecision" | "user.humanInputResponse" | "user.message" | "user.pause" | "workspace.inference.paused" | "workspace.inference.resumed" | "workspace.revision.captured" | "workspace.revision.degraded";
74
74
  preview: string | null;
75
75
  previewTruncated: boolean;
76
76
  } | null;
@@ -0,0 +1,19 @@
1
+ import { type CodexRealtimeInitialItem } from "@opengeni/codex";
2
+ export type SessionRealtimeHistoryRow = {
3
+ position: number;
4
+ item: Record<string, unknown>;
5
+ };
6
+ export type SessionRealtimeContinuityEntry = {
7
+ role: "user" | "assistant";
8
+ text: string;
9
+ };
10
+ /**
11
+ * Project ordinary model-facing conversation truth into Frameless V3 startup
12
+ * items. Only complete role-bearing messages are legal V3 initial items;
13
+ * reasoning, tool protocol records, images, and raw provider metadata are never
14
+ * copied into the browser-owned call bootstrap.
15
+ *
16
+ * The newest complete tail wins deterministically. This mirrors Codex's exact
17
+ * byte/4 token estimate and hard 128-item/8,192-token limits.
18
+ */
19
+ export declare function projectSessionRealtimeInitialItems(rows: readonly SessionRealtimeHistoryRow[], continuityEntries?: readonly SessionRealtimeContinuityEntry[]): CodexRealtimeInitialItem[];
@@ -1,16 +1,44 @@
1
- import { type WorkspaceInstructionPolicyListResponse, type WorkspaceStateResponse as WorkspaceStateResponseType } from "@opengeni/contracts";
1
+ import { type WorkspaceInstructionPolicyListResponse, type WorkspaceInstructionPolicySnapshot, type WorkspaceStateResponse as WorkspaceStateResponseType } from "@opengeni/contracts";
2
2
  import type { WorkspaceStateMemoryRecord } from "@opengeni/db";
3
3
  import type { DocumentInventory } from "@opengeni/documents";
4
4
  type KnowledgeProjectionInput = {
5
5
  documents: DocumentInventory;
6
6
  memories: WorkspaceStateMemoryRecord[];
7
7
  };
8
+ type PreferenceGovernanceIdentity = {
9
+ id: string;
10
+ revisionId: string;
11
+ contentHash: string;
12
+ activeVersion: number;
13
+ scope: "organization" | "workspace" | "user";
14
+ };
15
+ type AttemptGovernanceProjectionInput = {
16
+ status: "unavailable";
17
+ } | {
18
+ status: "available";
19
+ attemptId: string;
20
+ executionGeneration: number;
21
+ acceptedAt: string;
22
+ policySnapshot: WorkspaceInstructionPolicySnapshot | null;
23
+ preferenceSnapshot: {
24
+ id: string;
25
+ descriptorHash: string;
26
+ descriptors: PreferenceGovernanceIdentity[];
27
+ truncated: boolean;
28
+ createdAt: string;
29
+ } | null;
30
+ currentPreferences: {
31
+ descriptors: PreferenceGovernanceIdentity[];
32
+ truncated: boolean;
33
+ };
34
+ };
8
35
  export type WorkspaceStateProjectionInput = {
9
36
  workspaceId: string;
10
37
  generatedAt: string;
11
38
  workspaceAgentInstructions: string | null;
12
39
  policies: WorkspaceInstructionPolicyListResponse;
13
40
  knowledge: KnowledgeProjectionInput | null;
41
+ attemptGovernance?: AttemptGovernanceProjectionInput | null;
14
42
  };
15
43
  export declare function projectWorkspaceState(input: WorkspaceStateProjectionInput): WorkspaceStateResponseType;
16
44
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/api-router",
3
- "version": "0.16.5",
3
+ "version": "0.20.0",
4
4
  "description": "OpenGeni HTTP surface: the Hono adapter/router (createApp), routes, MCP HTTP transport, and HTTP access adapters over @opengeni/core. An engine-distribution surface — its runtime closure includes engine-internal packages.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -43,18 +43,18 @@
43
43
  "@hono/zod-validator": "^0.7.6",
44
44
  "@modelcontextprotocol/sdk": "^1.29.0",
45
45
  "@opengeni/agent-proto": "^0.3.0",
46
- "@opengeni/codex": "^0.2.9",
47
- "@opengeni/config": "^0.10.1",
48
- "@opengeni/contracts": "^0.31.1",
49
- "@opengeni/core": "^0.18.1",
50
- "@opengeni/db": "^0.22.2",
51
- "@opengeni/documents": "^0.2.70",
52
- "@opengeni/events": "^0.3.60",
53
- "@opengeni/github": "^0.4.17",
46
+ "@opengeni/codex": "^0.2.10",
47
+ "@opengeni/config": "^0.10.6",
48
+ "@opengeni/contracts": "^0.35.0",
49
+ "@opengeni/core": "^0.20.2",
50
+ "@opengeni/db": "^0.26.0",
51
+ "@opengeni/documents": "^0.3.2",
52
+ "@opengeni/events": "^0.3.65",
53
+ "@opengeni/github": "^0.4.22",
54
54
  "@opengeni/network": "^0.1.1",
55
- "@opengeni/observability": "^0.4.4",
56
- "@opengeni/runtime": "^0.17.1",
57
- "@opengeni/storage": "^0.2.54",
55
+ "@opengeni/observability": "^0.4.9",
56
+ "@opengeni/runtime": "^0.18.3",
57
+ "@opengeni/storage": "^0.2.59",
58
58
  "@temporalio/client": "^1.17.0",
59
59
  "better-auth": "^1.6.14",
60
60
  "hono": "^4.12.18",
package/src/app.ts CHANGED
@@ -18,10 +18,11 @@ import {
18
18
  } from "@opengeni/contracts";
19
19
  import {
20
20
  createDocumentServices,
21
+ getDocument,
21
22
  indexDocumentNow,
22
23
  type DocumentServices,
23
24
  } from "@opengeni/documents";
24
- import { dbSql, getWorkspace } from "@opengeni/db";
25
+ import { dbSql, getWorkspace, rlsContextForWorkspace } from "@opengeni/db";
25
26
  import { createObservability } from "@opengeni/observability";
26
27
  import { createObjectStorage } from "@opengeni/storage";
27
28
  import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
@@ -127,17 +128,38 @@ export function createAppComposition(deps: AppDependencies): {
127
128
  accountId,
128
129
  workspaceId,
129
130
  documentId,
131
+ authorityKind,
132
+ authorityWorkspaceId,
133
+ authoritySubjectId,
130
134
  }: {
131
135
  accountId: string;
132
136
  workspaceId: string;
133
137
  documentId: string;
138
+ authorityKind: import("@opengeni/contracts").DocumentAuthorityKind;
139
+ authorityWorkspaceId: string | null;
140
+ authoritySubjectId: string | null;
134
141
  }) => {
135
142
  if (!objectStorage) {
136
143
  throw new HTTPException(503, {
137
144
  message: "object storage is not configured",
138
145
  });
139
146
  }
140
- return await indexDocumentNow(
147
+ const context = await rlsContextForWorkspace(deps.db, workspaceId);
148
+ if (context.accountId !== accountId) {
149
+ throw new Error("document account/workspace authority mismatch");
150
+ }
151
+ const claimedDocument = await getDocument(deps.db, workspaceId, documentId, {
152
+ viewerSubjectId: authoritySubjectId,
153
+ });
154
+ if (
155
+ !claimedDocument ||
156
+ claimedDocument.authorityKind !== authorityKind ||
157
+ claimedDocument.authorityWorkspaceId !== authorityWorkspaceId ||
158
+ claimedDocument.authoritySubjectId !== authoritySubjectId
159
+ ) {
160
+ throw new Error("document authority changed before indexing");
161
+ }
162
+ const document = await indexDocumentNow(
141
163
  deps.db,
142
164
  objectStorage,
143
165
  workspaceId,
@@ -153,7 +175,16 @@ export function createAppComposition(deps: AppDependencies): {
153
175
  });
154
176
  },
155
177
  },
178
+ { viewerSubjectId: authoritySubjectId },
156
179
  );
180
+ if (
181
+ document.authorityKind !== authorityKind ||
182
+ document.authorityWorkspaceId !== authorityWorkspaceId ||
183
+ document.authoritySubjectId !== authoritySubjectId
184
+ ) {
185
+ throw new Error("document authority changed before indexing");
186
+ }
187
+ return document;
157
188
  },
158
189
  };
159
190
  // The API process's own agent-loop-free sandbox client — the API-direct