@opengeni/api-router 2.10.0-canary.0 → 2.10.0-canary.1

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.
@@ -1,3 +1,210 @@
1
+ // src/codemode.ts
2
+ import {
3
+ codemodeDispatchSubject,
4
+ decodeCodemodeDispatchAck,
5
+ encodeCodemodeDispatchRequest
6
+ } from "@opengeni/codemode";
7
+ import {
8
+ CODEMODE_DISPATCH_TIMEOUT_MS,
9
+ CodemodeCallRequest,
10
+ CodemodeCallSubmission
11
+ } from "@opengeni/contracts";
12
+ import { hasPermission, requireSessionAuthorization } from "@opengeni/core";
13
+ import {
14
+ getActiveSessionTurnForExecution,
15
+ getAttemptToolCatalog,
16
+ getCodemodeOperation,
17
+ submitCodemodeOperation
18
+ } from "@opengeni/db";
19
+ import { getSession } from "@opengeni/db";
20
+ import { resolveFirstPartyDelegationSecret } from "@opengeni/config";
21
+ import {
22
+ DEFAULT_FIRST_PARTY_MCP_PERMISSIONS,
23
+ signDelegatedAccessToken,
24
+ siteSessionPath,
25
+ OPENGENI_API_CONTRACT_HEADER,
26
+ OPENGENI_API_CONTRACT_REVISION
27
+ } from "@opengeni/contracts";
28
+ async function codemodeSessionRequest(deps, grant, request, path) {
29
+ siteSessionPath(path, grant.workspaceId, request.method);
30
+ const { authority } = await requireActiveCodemodeCatalog(deps, grant);
31
+ const session = await getSession(deps.db, authority.workspaceId, authority.sessionId);
32
+ const secret = resolveFirstPartyDelegationSecret(deps.settings);
33
+ if (!session || !secret) throw new CodemodeAuthorityError("invalid_grant");
34
+ const permissions = (session.firstPartyMcpPermissions ?? [...DEFAULT_FIRST_PARTY_MCP_PERMISSIONS]).filter(
35
+ (p) => ["workspace:read", "sessions:read", "sessions:create", "sessions:control"].includes(p)
36
+ );
37
+ const token = await signDelegatedAccessToken(secret, {
38
+ ...authority,
39
+ permissions,
40
+ principalKind: "agent_attempt",
41
+ exp: Math.floor(Date.now() / 1e3) + 60
42
+ });
43
+ const target = new URL(request.url);
44
+ const rewritten = siteSessionPath(path, authority.workspaceId, request.method);
45
+ const url = new URL(rewritten, target.origin);
46
+ const headers = new Headers({
47
+ authorization: `Bearer ${token}`,
48
+ [OPENGENI_API_CONTRACT_HEADER]: OPENGENI_API_CONTRACT_REVISION
49
+ });
50
+ for (const name of ["content-type", "accept", "last-event-id"]) {
51
+ const value = request.headers.get(name);
52
+ if (value) headers.set(name, value);
53
+ }
54
+ return new Request(url, {
55
+ method: request.method,
56
+ headers,
57
+ signal: request.signal,
58
+ ...request.body ? { body: await request.text() } : {}
59
+ });
60
+ }
61
+ var CODEMODE_AUTHORITY_FAILURE_MESSAGES = {
62
+ invalid_grant: "Codemode bearer does not carry valid execution-attempt authority",
63
+ inactive_attempt: "Codemode execution attempt is no longer active",
64
+ catalog_mismatch: "Codemode tool catalog does not match the active execution attempt"
65
+ };
66
+ var CodemodeAuthorityError = class extends Error {
67
+ constructor(reason2) {
68
+ super(CODEMODE_AUTHORITY_FAILURE_MESSAGES[reason2]);
69
+ this.reason = reason2;
70
+ this.name = "CodemodeAuthorityError";
71
+ this.code = `codemode_${reason2}`;
72
+ }
73
+ code;
74
+ };
75
+ var CodemodeCatalogNotReadyError = class extends Error {
76
+ code = "codemode_catalog_not_ready";
77
+ constructor() {
78
+ super("Codemode tool catalog is not ready for the active execution attempt");
79
+ this.name = "CodemodeCatalogNotReadyError";
80
+ }
81
+ };
82
+ var CodemodeCatalogStaleError = class extends Error {
83
+ code = "codemode_catalog_stale";
84
+ constructor() {
85
+ super("Codemode tool catalog is stale for the active execution attempt");
86
+ this.name = "CodemodeCatalogStaleError";
87
+ }
88
+ };
89
+ function codemodeAuthorityForGrant(grant) {
90
+ const metadata = grant.metadata;
91
+ if (grant.principalKind !== "agent_attempt" || metadata?.delegated !== true || typeof metadata.sessionId !== "string" || typeof metadata.turnId !== "string" || typeof metadata.attemptId !== "string" || typeof metadata.executionGeneration !== "number" || !Number.isInteger(metadata.executionGeneration) || metadata.executionGeneration < 1) {
92
+ return null;
93
+ }
94
+ return {
95
+ accountId: grant.accountId,
96
+ workspaceId: grant.workspaceId,
97
+ sessionId: metadata.sessionId,
98
+ turnId: metadata.turnId,
99
+ attemptId: metadata.attemptId,
100
+ executionGeneration: metadata.executionGeneration,
101
+ subjectId: grant.subjectId
102
+ };
103
+ }
104
+ function isCodemodeGrant(grant) {
105
+ return hasPermission(grant.permissions, "codemode:call") && codemodeAuthorityForGrant(grant) !== null;
106
+ }
107
+ function requireMatchingCodemodeCatalog(authority, catalog) {
108
+ if (!catalog) throw new CodemodeCatalogNotReadyError();
109
+ if (catalog.accountId !== authority.accountId || catalog.workspaceId !== authority.workspaceId || catalog.sessionId !== authority.sessionId || catalog.turnId !== authority.turnId || catalog.attemptId !== authority.attemptId || catalog.executionGeneration !== authority.executionGeneration) {
110
+ throw new CodemodeAuthorityError("catalog_mismatch");
111
+ }
112
+ return catalog;
113
+ }
114
+ async function requireActiveCodemodeCatalog(deps, grant) {
115
+ if (!isCodemodeGrant(grant)) throw new CodemodeAuthorityError("invalid_grant");
116
+ const authority = codemodeAuthorityForGrant(grant);
117
+ await requireSessionAuthorization(deps, grant, {
118
+ sessionId: authority.sessionId,
119
+ operation: "session.codemode.call",
120
+ surface: "codemode"
121
+ });
122
+ const active = await getActiveSessionTurnForExecution(
123
+ deps.db,
124
+ authority.workspaceId,
125
+ authority.sessionId
126
+ );
127
+ if (!active || active.status !== "running" || active.id !== authority.turnId || active.activeAttemptId !== authority.attemptId || active.executionGeneration !== authority.executionGeneration) {
128
+ throw new CodemodeAuthorityError("inactive_attempt");
129
+ }
130
+ const catalog = requireMatchingCodemodeCatalog(
131
+ authority,
132
+ await getAttemptToolCatalog(deps.db, {
133
+ accountId: authority.accountId,
134
+ workspaceId: authority.workspaceId,
135
+ attemptId: authority.attemptId
136
+ })
137
+ );
138
+ return { authority, catalog };
139
+ }
140
+ async function submitAndDispatchCodemodeCall(deps, grant, rawRequest) {
141
+ const request = CodemodeCallRequest.parse(rawRequest);
142
+ const { authority, catalog } = await requireActiveCodemodeCatalog(deps, grant);
143
+ if (request.catalogDigest !== catalog.digest) throw new CodemodeCatalogStaleError();
144
+ const submitted = await submitCodemodeOperation(deps.db, {
145
+ ...authority,
146
+ call: {
147
+ ...request,
148
+ caller: { kind: "codemode", subjectId: authority.subjectId }
149
+ }
150
+ });
151
+ let operation = submitted.operation;
152
+ let dispatch = terminal(operation) ? "terminal" : operation.state === "running" ? "already_running" : "unavailable";
153
+ if (codemodeOperationNeedsDispatch(operation)) {
154
+ try {
155
+ const reply = await deps.bus.request(
156
+ codemodeDispatchSubject(authority.workspaceId, authority.attemptId),
157
+ encodeCodemodeDispatchRequest({
158
+ version: 1,
159
+ operationId: operation.operationId,
160
+ catalogDigest: catalog.digest
161
+ }),
162
+ { timeoutMs: CODEMODE_DISPATCH_TIMEOUT_MS }
163
+ );
164
+ dispatch = decodeCodemodeDispatchAck(reply.data).status;
165
+ } catch {
166
+ dispatch = "unavailable";
167
+ }
168
+ operation = await refreshAdmittedCodemodeOperation(
169
+ operation,
170
+ () => getCodemodeOperation(deps.db, {
171
+ accountId: authority.accountId,
172
+ workspaceId: authority.workspaceId,
173
+ attemptId: authority.attemptId,
174
+ operationId: operation.operationId
175
+ })
176
+ );
177
+ if (terminal(operation)) dispatch = "terminal";
178
+ else if (operation.state === "running" && dispatch === "unavailable") {
179
+ dispatch = "already_running";
180
+ }
181
+ }
182
+ return CodemodeCallSubmission.parse({ operation, dispatch });
183
+ }
184
+ function codemodeOperationNeedsDispatch(operation) {
185
+ return operation.state === "queued" || operation.state === "running";
186
+ }
187
+ async function readCodemodeOperation(deps, grant, operationId) {
188
+ if (!isCodemodeGrant(grant)) throw new CodemodeAuthorityError("invalid_grant");
189
+ const authority = codemodeAuthorityForGrant(grant);
190
+ return await getCodemodeOperation(deps.db, {
191
+ accountId: authority.accountId,
192
+ workspaceId: authority.workspaceId,
193
+ attemptId: authority.attemptId,
194
+ operationId
195
+ });
196
+ }
197
+ function terminal(operation) {
198
+ return ["completed", "failed", "outcome_unknown", "cancelled"].includes(operation.state);
199
+ }
200
+ async function refreshAdmittedCodemodeOperation(admitted, read) {
201
+ try {
202
+ return await read() ?? admitted;
203
+ } catch {
204
+ return admitted;
205
+ }
206
+ }
207
+
1
208
  // src/app.ts
2
209
  import {
3
210
  canonicalizeConfiguredModelId as canonicalizeConfiguredModelId2,
@@ -12,8 +219,8 @@ import {
12
219
  ClientConfig,
13
220
  CodemodeCallRequest as CodemodeCallRequest2,
14
221
  ErrorEnvelope,
15
- OPENGENI_API_CONTRACT_HEADER,
16
- OPENGENI_API_CONTRACT_REVISION,
222
+ OPENGENI_API_CONTRACT_HEADER as OPENGENI_API_CONTRACT_HEADER2,
223
+ OPENGENI_API_CONTRACT_REVISION as OPENGENI_API_CONTRACT_REVISION2,
17
224
  OPENGENI_CORRELATION_HEADER,
18
225
  resolveWorkspaceMemoryEnabled,
19
226
  resolveWorkspaceMemoryPromptMode,
@@ -114,7 +321,7 @@ import {
114
321
  ManagedAuthActorLeaseOutcomeUnknownError,
115
322
  hasPermission as hasPermission26,
116
323
  requireAccessGrant as requireAccessGrant46,
117
- requireAccessGrantAuthorization as requireAccessGrantAuthorization19,
324
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization20,
118
325
  requirePermission as requirePermission9,
119
326
  releaseManagedAuthRequestActorLease,
120
327
  resolveCatalogSettings,
@@ -2290,7 +2497,271 @@ function makeResumeBoxById(client) {
2290
2497
  import { requireLimit as requireLimit10 } from "@opengeni/core";
2291
2498
 
2292
2499
  // src/mcp/server.ts
2293
- import { createHash as createHash10, randomUUID as randomUUID2 } from "crypto";
2500
+ import { createHash as createHash9, randomUUID as randomUUID2 } from "crypto";
2501
+
2502
+ // src/site-uploads.ts
2503
+ import { WorkspaceArtifactSourceBundle as WorkspaceArtifactSourceBundle2 } from "@opengeni/contracts";
2504
+ import {
2505
+ createWorkspaceArtifactUpload,
2506
+ getWorkspaceArtifactUpload,
2507
+ expireWorkspaceArtifactUploads,
2508
+ WorkspaceArtifactOperationError
2509
+ } from "@opengeni/db";
2510
+
2511
+ // src/workspace-artifact-content.ts
2512
+ import {
2513
+ WorkspaceArtifactHtml,
2514
+ WorkspaceArtifactRequestedTools,
2515
+ WorkspaceArtifactSourceBundle
2516
+ } from "@opengeni/contracts";
2517
+ import { retryWhileMissing } from "@opengeni/storage";
2518
+ var encoder2 = new TextEncoder();
2519
+ var decoder = new TextDecoder("utf-8", { fatal: true });
2520
+ function prepareWorkspaceArtifactContent(objectStorage, workspaceId, input) {
2521
+ const html = WorkspaceArtifactHtml.parse(input.html);
2522
+ const source = input.source ? WorkspaceArtifactSourceBundle.parse(input.source) : null;
2523
+ const requestedTools = input.requestedTools === void 0 ? void 0 : WorkspaceArtifactRequestedTools.parse(input.requestedTools);
2524
+ const contentBytes = encoder2.encode(html);
2525
+ const sourceBytes = source ? encoder2.encode(JSON.stringify(source)) : null;
2526
+ const contentSha256 = null;
2527
+ const sourceSha256 = null;
2528
+ const storageGroupId = crypto.randomUUID();
2529
+ const contentKey = `workspaces/${workspaceId}/workspace-artifacts/blobs/${storageGroupId}.html`;
2530
+ const sourceKey = `workspaces/${workspaceId}/workspace-artifacts/sources/${storageGroupId}.json`;
2531
+ const discardContent = async () => {
2532
+ await Promise.allSettled([
2533
+ objectStorage.deleteObject(contentKey),
2534
+ ...source ? [objectStorage.deleteObject(sourceKey)] : []
2535
+ ]);
2536
+ };
2537
+ return {
2538
+ contentKey,
2539
+ contentSha256,
2540
+ sizeBytes: contentBytes.byteLength,
2541
+ sourceKey: source ? sourceKey : null,
2542
+ sourceSha256,
2543
+ sourceSizeBytes: sourceBytes?.byteLength ?? null,
2544
+ ...requestedTools === void 0 ? {} : { requestedTools },
2545
+ discardContent,
2546
+ persistContent: async () => {
2547
+ const writes = await Promise.allSettled([
2548
+ objectStorage.putObject({
2549
+ key: contentKey,
2550
+ contentType: "text/html; charset=utf-8",
2551
+ body: contentBytes
2552
+ }),
2553
+ sourceBytes ? objectStorage.putObject({
2554
+ key: sourceKey,
2555
+ contentType: "application/json; charset=utf-8",
2556
+ body: sourceBytes
2557
+ }) : Promise.resolve()
2558
+ ]);
2559
+ const failed = writes.find((write) => write.status === "rejected");
2560
+ if (!failed) return;
2561
+ await Promise.allSettled(
2562
+ writes.flatMap(
2563
+ (write, index) => write.status === "fulfilled" ? [objectStorage.deleteObject(index === 0 ? contentKey : sourceKey)] : []
2564
+ )
2565
+ );
2566
+ throw failed.reason;
2567
+ }
2568
+ };
2569
+ }
2570
+ async function readWorkspaceArtifactContent(objectStorage, input) {
2571
+ const [contentObject, sourceObject] = await Promise.all([
2572
+ retryWhileMissing(async () => await objectStorage.getObjectBytes(input.contentKey)),
2573
+ input.sourceKey ? retryWhileMissing(async () => await objectStorage.getObjectBytes(input.sourceKey)) : Promise.resolve(null)
2574
+ ]);
2575
+ if (!contentObject) throw new Error("Artifact content is unavailable");
2576
+ const html = decode(contentObject.bytes, "Artifact content is not valid UTF-8");
2577
+ let source = sourceBundleFromHtml(html);
2578
+ if (input.sourceKey) {
2579
+ if (!sourceObject) throw new Error("Artifact source is unavailable");
2580
+ try {
2581
+ source = WorkspaceArtifactSourceBundle.parse(
2582
+ JSON.parse(decode(sourceObject.bytes, "Artifact source is not valid UTF-8"))
2583
+ );
2584
+ } catch (error) {
2585
+ if (error instanceof Error && error.message === "Artifact source is not valid UTF-8") {
2586
+ throw error;
2587
+ }
2588
+ throw new Error("Artifact source is invalid", { cause: error });
2589
+ }
2590
+ }
2591
+ return {
2592
+ html,
2593
+ source,
2594
+ requestedTools: WorkspaceArtifactRequestedTools.parse(input.version.requestedTools)
2595
+ };
2596
+ }
2597
+ function sourceBundleFromHtml(html) {
2598
+ return { entrypoint: "index.html", files: [{ path: "index.html", content: html }] };
2599
+ }
2600
+ function decode(bytes, message) {
2601
+ try {
2602
+ return decoder.decode(bytes);
2603
+ } catch (error) {
2604
+ throw new Error(message, { cause: error });
2605
+ }
2606
+ }
2607
+
2608
+ // src/site-uploads.ts
2609
+ var MAX_SITE_SOURCE_BYTES = 64 * 1024 * 1024;
2610
+ async function validateSiteSource(storage, key, sizeBytes) {
2611
+ if (sizeBytes > MAX_SITE_SOURCE_BYTES) {
2612
+ throw new WorkspaceArtifactOperationError(
2613
+ "Editable source JSON must be at most 64 MiB. Exclude dependencies and build output; HTML has a separate storage limit."
2614
+ );
2615
+ }
2616
+ const content = await storage.getObjectBytes(key);
2617
+ try {
2618
+ WorkspaceArtifactSourceBundle2.parse(
2619
+ JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(content.bytes))
2620
+ );
2621
+ } catch {
2622
+ throw new WorkspaceArtifactOperationError(
2623
+ "Source must be JSON {entrypoint, files: [{path, content}]}. Correct it and prepare a new upload."
2624
+ );
2625
+ }
2626
+ }
2627
+ function keys(workspaceId, id) {
2628
+ const base = `workspaces/${workspaceId}/workspace-artifacts`;
2629
+ return {
2630
+ html: `${base}/uploads/${id}/index.html`,
2631
+ source: `${base}/uploads/${id}/source.json`,
2632
+ frozenHtml: `${base}/blobs/${id}.html`,
2633
+ frozenSource: `${base}/sources/${id}.json`
2634
+ };
2635
+ }
2636
+ function owner(grant) {
2637
+ return typeof grant.metadata?.sessionId === "string" ? `session:${grant.metadata.sessionId}` : `subject:${grant.subjectId}`;
2638
+ }
2639
+ async function prepareWorkspaceArtifactUpload(deps, grant) {
2640
+ const storage = deps.objectStorage;
2641
+ if (!storage) throw new Error("Object storage is not configured");
2642
+ const expired = await expireWorkspaceArtifactUploads(deps.db, grant.workspaceId);
2643
+ await Promise.allSettled(
2644
+ expired.flatMap((row2) => {
2645
+ const paths2 = keys(row2.workspaceId, row2.id);
2646
+ return (row2.status === "published" ? [paths2.html, paths2.source] : Object.values(paths2)).map(
2647
+ (key) => storage.deleteObject(key)
2648
+ );
2649
+ })
2650
+ );
2651
+ const row = await createWorkspaceArtifactUpload(deps.db, {
2652
+ accountId: grant.accountId,
2653
+ workspaceId: grant.workspaceId,
2654
+ ownerId: owner(grant)
2655
+ });
2656
+ const paths = keys(grant.workspaceId, row.id);
2657
+ const audience = grant.principalKind === "agent_attempt" ? "sandbox" : "public";
2658
+ const [html, source] = await Promise.all([
2659
+ storage.createPutUrl({ key: paths.html, contentType: "text/html", audience }),
2660
+ storage.createPutUrl({ key: paths.source, contentType: "application/json", audience })
2661
+ ]);
2662
+ return {
2663
+ uploadId: row.id,
2664
+ html: { putUrl: html.url, requiredHeaders: html.requiredHeaders },
2665
+ source: { putUrl: source.url, requiredHeaders: source.requiredHeaders },
2666
+ expiresAt: new Date(
2667
+ Math.min(html.expiresAt.getTime(), source.expiresAt.getTime())
2668
+ ).toISOString()
2669
+ };
2670
+ }
2671
+ async function* readArtifactObject(storage, key, expectedHead) {
2672
+ if (!storage.headObject || !storage.getObjectRange)
2673
+ throw new Error("Object storage cannot stream Site content");
2674
+ const head = expectedHead ?? await storage.headObject(key);
2675
+ if (!head || !head.VersionToken || head.ContentLength === void 0)
2676
+ throw new Error("Site content is unavailable");
2677
+ for (let start = 0; start < head.ContentLength; start += 1024 * 1024) {
2678
+ const endInclusive = Math.min(head.ContentLength - 1, start + 1024 * 1024 - 1);
2679
+ const part = await storage.getObjectRange({
2680
+ key,
2681
+ start,
2682
+ endInclusive,
2683
+ expectedVersionToken: head.VersionToken
2684
+ });
2685
+ if (!part || part.versionToken !== head.VersionToken || part.bytes.length !== endInclusive - start + 1)
2686
+ throw new Error("Site object changed during transfer");
2687
+ yield part.bytes;
2688
+ }
2689
+ }
2690
+ async function freeze(storage, from, to, contentType, optional = false) {
2691
+ if (!storage.headObject || !storage.putObjectStreamIfAbsent)
2692
+ throw new Error("Object storage cannot freeze Site uploads");
2693
+ const existing = await storage.headObject(to);
2694
+ if (existing) return existing.ContentLength;
2695
+ const head = await storage.headObject(from);
2696
+ if (!head && optional) return null;
2697
+ if (!head || !head.ContentLength)
2698
+ throw new WorkspaceArtifactOperationError("Upload HTML before publishing the Site");
2699
+ if (head.ContentLength > storage.maxSinglePutSizeBytes)
2700
+ throw new Error("Site exceeds the storage upload limit");
2701
+ const decoder2 = new TextDecoder("utf-8", { fatal: true });
2702
+ async function* chunks() {
2703
+ for await (const part of readArtifactObject(storage, from, head)) {
2704
+ decoder2.decode(part, { stream: true });
2705
+ yield part;
2706
+ }
2707
+ decoder2.decode();
2708
+ }
2709
+ await storage.putObjectStreamIfAbsent({
2710
+ key: to,
2711
+ contentType,
2712
+ byteSize: head.ContentLength,
2713
+ chunks: chunks()
2714
+ });
2715
+ return (await storage.headObject(to)).ContentLength;
2716
+ }
2717
+ async function prepareWorkspaceArtifactPublication(deps, grant, input) {
2718
+ const storage = deps.objectStorage;
2719
+ if (!storage) throw new Error("Object storage is not configured");
2720
+ if (Boolean(input.uploadId) === (input.html !== void 0))
2721
+ throw new Error("Provide either uploadId or inline html, not both");
2722
+ if (!input.uploadId) {
2723
+ return prepareWorkspaceArtifactContent(storage, grant.workspaceId, {
2724
+ ...input,
2725
+ html: input.html
2726
+ });
2727
+ }
2728
+ if (input.source) throw new Error("Upload source JSON using the source upload URL");
2729
+ const row = await getWorkspaceArtifactUpload(deps.db, grant.workspaceId, input.uploadId);
2730
+ if (!row || row.ownerId !== owner(grant) || row.status === "expired" || row.status === "pending" && row.expiresAt.getTime() <= Date.now())
2731
+ throw new WorkspaceArtifactOperationError("Site upload expired or unavailable");
2732
+ const paths = keys(grant.workspaceId, row.id);
2733
+ const sizeBytes = await freeze(storage, paths.html, paths.frozenHtml, "text/html; charset=utf-8");
2734
+ const sourceSizeBytes = row.status === "published" && !await storage.headObject(paths.frozenSource) ? null : await freeze(storage, paths.source, paths.frozenSource, "application/json", true);
2735
+ if (sourceSizeBytes !== null) {
2736
+ await validateSiteSource(storage, paths.frozenSource, sourceSizeBytes);
2737
+ }
2738
+ return {
2739
+ uploadId: row.id,
2740
+ contentKey: paths.frozenHtml,
2741
+ contentSha256: null,
2742
+ sizeBytes,
2743
+ sourceKey: sourceSizeBytes === null ? null : paths.frozenSource,
2744
+ sourceSha256: null,
2745
+ sourceSizeBytes,
2746
+ ...input.requestedTools === void 0 ? {} : { requestedTools: input.requestedTools },
2747
+ persistContent: async () => {
2748
+ },
2749
+ discardContent: async () => {
2750
+ }
2751
+ };
2752
+ }
2753
+ async function workspaceArtifactDownloads(storage, ref, audience) {
2754
+ const [html, source] = await Promise.all([
2755
+ storage.createGetUrl({ key: ref.contentKey, audience }),
2756
+ ref.sourceKey ? storage.createGetUrl({ key: ref.sourceKey, audience }) : null
2757
+ ]);
2758
+ return {
2759
+ html: { url: html.url, expiresAt: html.expiresAt },
2760
+ source: source ? { url: source.url, expiresAt: source.expiresAt, format: "source-bundle-json" } : null
2761
+ };
2762
+ }
2763
+
2764
+ // src/mcp/server.ts
2294
2765
  import {
2295
2766
  CreateScheduledTaskRequest,
2296
2767
  boundSessionMcpText as capSessionDiscoveryText,
@@ -2348,7 +2819,7 @@ import {
2348
2819
  createVariableSet,
2349
2820
  decryptVariableSetValue,
2350
2821
  encryptVariableSetValue,
2351
- getSession as getSession2,
2822
+ getSession as getSession3,
2352
2823
  getSessionGoal,
2353
2824
  getSessionMcpMonitoringSummary,
2354
2825
  getSessionQueueSnapshot,
@@ -2424,14 +2895,14 @@ import { HTTPException as HTTPException19 } from "hono/http-exception";
2424
2895
  import * as z43 from "zod/v4";
2425
2896
  import {
2426
2897
  hasLiteralPermission,
2427
- hasPermission as hasPermission10,
2898
+ hasPermission as hasPermission11,
2428
2899
  authorizedSocialConnectionsForGrant,
2429
2900
  authorizedAtlassianConnectionsForGrant,
2430
2901
  buildCapabilityCatalog,
2431
2902
  correctWorkspaceMemoryWithSlackPublication,
2432
2903
  nativeConnectionCapabilityRecommendations,
2433
2904
  requireLiveAgentAttemptAuthorization as requireLiveAgentAttemptAuthorization3,
2434
- requireSessionAuthorization as requireSessionAuthorization2,
2905
+ requireSessionAuthorization as requireSessionAuthorization3,
2435
2906
  requireSessionAuthorizationListScope,
2436
2907
  resolveWorkspaceCatalogSettings as resolveWorkspaceCatalogSettings2,
2437
2908
  SessionAuthorizationDeniedError as SessionAuthorizationDeniedError4,
@@ -2439,7 +2910,7 @@ import {
2439
2910
  saveWorkspaceMemoryWithSlackPublication,
2440
2911
  searchCapabilityCatalogItems
2441
2912
  } from "@opengeni/core";
2442
- import { recordWorkspaceUsage as recordWorkspaceUsage3, requireLimit as requireLimit3 } from "@opengeni/core";
2913
+ import { recordWorkspaceUsage as recordWorkspaceUsage3, requireLimit as requireLimit3, workflowIdForSession } from "@opengeni/core";
2443
2914
 
2444
2915
  // src/github-access.ts
2445
2916
  import {
@@ -2622,10 +3093,10 @@ async function listWorkspaceGitHubRepositoryBranches(deps, input, services = wor
2622
3093
  }
2623
3094
 
2624
3095
  // src/github-browser-flow.ts
2625
- import { hasPermission } from "@opengeni/core";
3096
+ import { hasPermission as hasPermission2 } from "@opengeni/core";
2626
3097
  var githubBrowserGrantMaxAgeSeconds = 10 * 60;
2627
3098
  function githubBrowserGrantClaims(settings, grant, nowSeconds = Math.floor(Date.now() / 1e3)) {
2628
- if (settings.productAccessMode !== "configured" || !hasPermission(grant.permissions, "github:manage")) {
3099
+ if (settings.productAccessMode !== "configured" || !hasPermission2(grant.permissions, "github:manage")) {
2629
3100
  return {};
2630
3101
  }
2631
3102
  return {
@@ -2682,7 +3153,7 @@ import {
2682
3153
  } from "@opengeni/contracts";
2683
3154
  import { recordWorkspaceUsage as recordWorkspaceUsage2, requireLimit as requireLimit2 } from "@opengeni/core";
2684
3155
  import { completeFileUpload as completeFileUpload2, getFile, prepareGeneratedWorkspaceFile } from "@opengeni/db";
2685
- import { retryWhileMissing as retryWhileMissing2 } from "@opengeni/storage";
3156
+ import { retryWhileMissing as retryWhileMissing3 } from "@opengeni/storage";
2686
3157
  import { HTTPException as HTTPException7 } from "hono/http-exception";
2687
3158
 
2688
3159
  // src/sandbox/channel-a.ts
@@ -2756,6 +3227,7 @@ import {
2756
3227
  } from "@opengeni/core";
2757
3228
 
2758
3229
  // src/sandbox/rematerialize.ts
3230
+ import { parseWorkspaceArchiveObjectRef } from "@opengeni/contracts";
2759
3231
  import {
2760
3232
  adoptLegacyModalCheckpointArtifact,
2761
3233
  beginSandboxRematerialization,
@@ -2768,6 +3240,7 @@ import {
2768
3240
  SandboxLeaseSupersededError
2769
3241
  } from "@opengeni/db";
2770
3242
  import {
3243
+ inlineWorkspaceArchiveForRestore,
2771
3244
  describeLegacyNativeSnapshotArchive,
2772
3245
  establishSandboxSessionFromEnvelope,
2773
3246
  isProviderSandboxNotFoundError,
@@ -2784,8 +3257,33 @@ import {
2784
3257
  WorkspaceArchiveIntegrityError
2785
3258
  } from "@opengeni/runtime/sandbox";
2786
3259
  function hasWorkspaceArchive(envelope) {
2787
- const sessionState = envelope?.sessionState && typeof envelope.sessionState === "object" ? envelope.sessionState : null;
2788
- return typeof sessionState?.workspaceArchive === "string" && sessionState.workspaceArchive.length > 0;
3260
+ const sessionState = envelope?.sessionState && typeof envelope.sessionState === "object" && !Array.isArray(envelope.sessionState) ? envelope.sessionState : null;
3261
+ return typeof sessionState?.workspaceArchive === "string" && sessionState.workspaceArchive.length > 0 || parseWorkspaceArchiveObjectRef(sessionState?.workspaceArchiveRef) !== null;
3262
+ }
3263
+ async function materializeArchiveObjectRef(envelope, objectStorage) {
3264
+ if (!envelope) return null;
3265
+ const sessionState = envelope.sessionState && typeof envelope.sessionState === "object" && !Array.isArray(envelope.sessionState) ? envelope.sessionState : null;
3266
+ if (!sessionState) return envelope;
3267
+ if (sessionState.workspaceArchiveRef == null) return envelope;
3268
+ if (!parseWorkspaceArchiveObjectRef(sessionState.workspaceArchiveRef)) {
3269
+ throw new WorkspaceArchiveIntegrityError(
3270
+ "archive_metadata_invalid",
3271
+ "workspace archive object reference is malformed"
3272
+ );
3273
+ }
3274
+ if (!objectStorage) {
3275
+ throw new WorkspaceArchiveIntegrityError(
3276
+ "archive_base64_invalid",
3277
+ "workspace archive object storage is not configured"
3278
+ );
3279
+ }
3280
+ return {
3281
+ ...envelope,
3282
+ sessionState: await inlineWorkspaceArchiveForRestore(
3283
+ sessionState,
3284
+ (key) => objectStorage.getObjectBytes(key)
3285
+ )
3286
+ };
2789
3287
  }
2790
3288
  function legacyNativeArchiveFromEnvelope(envelope) {
2791
3289
  const sessionState = envelope?.sessionState && typeof envelope.sessionState === "object" ? envelope.sessionState : null;
@@ -2864,8 +3362,9 @@ async function establishApiSandboxSpawner(input) {
2864
3362
  input.acquiredLease.recovery
2865
3363
  );
2866
3364
  }
3365
+ const hydrateEnvelope = await materializeArchiveObjectRef(spawnEnvelope, input.objectStorage);
2867
3366
  const providerCreateStartedAt = /* @__PURE__ */ new Date();
2868
- established = await establishSandboxSessionFromEnvelope(input.settings, spawnEnvelope, {
3367
+ established = await establishSandboxSessionFromEnvelope(input.settings, hydrateEnvelope, {
2869
3368
  sessionId: input.sessionId,
2870
3369
  recovery: "create-or-restore",
2871
3370
  backendOverride: input.backend,
@@ -3618,7 +4117,8 @@ async function withChannelAOperation(services, ctx, readOnly, fn) {
3618
4117
  expectedEpoch,
3619
4118
  acquiredLease: acquired.lease,
3620
4119
  fallbackEnvelope: envelope,
3621
- dataPlaneUrl: acquired.lease.dataPlaneUrl
4120
+ dataPlaneUrl: acquired.lease.dataPlaneUrl,
4121
+ ...services.objectStorage !== void 0 ? { objectStorage: services.objectStorage } : {}
3622
4122
  });
3623
4123
  established = result.established;
3624
4124
  leaseSnapshot = result.lease;
@@ -3970,12 +4470,12 @@ import {
3970
4470
  SessionAuthorizationDeniedError as SessionAuthorizationDeniedError2
3971
4471
  } from "@opengeni/core";
3972
4472
  import { recordWorkspaceUsage, requireLimit } from "@opengeni/core";
3973
- import { retryWhileMissing } from "@opengeni/storage";
4473
+ import { retryWhileMissing as retryWhileMissing2 } from "@opengeni/storage";
3974
4474
  import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
3975
4475
 
3976
4476
  // src/mcp/files.ts
3977
4477
  import { recordAuditEvent, requireFileForSubject } from "@opengeni/db";
3978
- import { hasPermission as hasPermission2 } from "@opengeni/core";
4478
+ import { hasPermission as hasPermission3 } from "@opengeni/core";
3979
4479
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3980
4480
  import * as z from "zod/v4";
3981
4481
  function buildFilesMcpServer(deps, grant) {
@@ -3983,7 +4483,7 @@ function buildFilesMcpServer(deps, grant) {
3983
4483
  name: "opengeni-files",
3984
4484
  version: "1.0.0"
3985
4485
  });
3986
- if (!hasPermission2(grant.permissions, "files:read")) {
4486
+ if (!hasPermission3(grant.permissions, "files:read")) {
3987
4487
  server.registerTool(
3988
4488
  "__opengeni_empty_files_surface__",
3989
4489
  {
@@ -4131,7 +4631,7 @@ import {
4131
4631
  } from "@opengeni/contracts";
4132
4632
  import {
4133
4633
  buildApiIntegrationMcpServers,
4134
- hasPermission as hasPermission3,
4634
+ hasPermission as hasPermission4,
4135
4635
  requireResolvedAccessGrantAuthorization,
4136
4636
  resolveCodexAppsCredentialIdForRun,
4137
4637
  resolveWorkspaceCatalogSettings,
@@ -4846,10 +5346,10 @@ function workspaceToolGatewaySettingsForGrant(settings, grant, allowedIdentities
4846
5346
  ...settings,
4847
5347
  mcpServers: settings.mcpServers.filter((server) => {
4848
5348
  if (allowedServerIds && !allowedServerIds.has(server.id)) return false;
4849
- if (server.id === "docs" && !hasPermission3(grant.permissions, "documents:search")) {
5349
+ if (server.id === "docs" && !hasPermission4(grant.permissions, "documents:search")) {
4850
5350
  return false;
4851
5351
  }
4852
- if (server.id === "files" && !hasPermission3(grant.permissions, "files:read")) return false;
5352
+ if (server.id === "files" && !hasPermission4(grant.permissions, "files:read")) return false;
4853
5353
  return true;
4854
5354
  })
4855
5355
  };
@@ -5821,7 +6321,7 @@ function registerFileRoutes(app, deps) {
5821
6321
  const file2 = await rejectAndCleanObject(409, "file upload has expired", "expired");
5822
6322
  return c.json(CompleteFileUploadResponse.parse({ file: file2 }));
5823
6323
  }
5824
- const head = await retryWhileMissing(async () => {
6324
+ const head = await retryWhileMissing2(async () => {
5825
6325
  if (!await objectStorage.fileExists(upload.file)) return null;
5826
6326
  return await objectStorage.headFile(upload.file);
5827
6327
  });
@@ -5920,7 +6420,7 @@ function registerFileRoutes(app, deps) {
5920
6420
  throw new HTTPException6(409, { message: `generated video is ${retained.file.status}` });
5921
6421
  }
5922
6422
  const file = generatedVideoFileAsset(retained.file);
5923
- const videoPresent = await retryWhileMissing(
6423
+ const videoPresent = await retryWhileMissing2(
5924
6424
  async () => await objectStorage.fileExists(file) ? true : null
5925
6425
  );
5926
6426
  if (!videoPresent) {
@@ -6012,7 +6512,7 @@ function registerFileRoutes(app, deps) {
6012
6512
  if (file.status !== "ready") {
6013
6513
  throw new HTTPException6(409, { message: `file is ${file.status}` });
6014
6514
  }
6015
- const present = await retryWhileMissing(
6515
+ const present = await retryWhileMissing2(
6016
6516
  async () => await objectStorage.fileExists(file) ? true : null
6017
6517
  );
6018
6518
  if (!present) {
@@ -6086,7 +6586,7 @@ async function serveRetainedArtifactContent(c, objectStorage, artifactId2, file,
6086
6586
  ...range.contentRange ? { "Content-Range": range.contentRange } : {}
6087
6587
  };
6088
6588
  if (range.kind === "empty") {
6089
- const present = await retryWhileMissing(
6589
+ const present = await retryWhileMissing2(
6090
6590
  async () => await objectStorage.fileExists(file) ? true : null
6091
6591
  );
6092
6592
  if (!present) {
@@ -6094,7 +6594,7 @@ async function serveRetainedArtifactContent(c, objectStorage, artifactId2, file,
6094
6594
  }
6095
6595
  return c.body(null, 200, headers);
6096
6596
  }
6097
- const bytes = await retryWhileMissing(
6597
+ const bytes = await retryWhileMissing2(
6098
6598
  async () => objectStorage.getFileRange(file, {
6099
6599
  start: range.start,
6100
6600
  end: range.end
@@ -6331,12 +6831,12 @@ async function publishSandboxFileArtifact(deps, input) {
6331
6831
  }
6332
6832
  const safeFilename = sandboxArtifactSafeFilename(filename);
6333
6833
  const contentType = sandboxFileContentType(filename);
6334
- const sha2565 = createHash3("sha256").update(bytes).digest("hex");
6834
+ const sha2564 = createHash3("sha256").update(bytes).digest("hex");
6335
6835
  const identity = sandboxArtifactIdentity({
6336
6836
  workspaceId: input.grant.workspaceId,
6337
6837
  sessionId: input.session.id,
6338
6838
  path,
6339
- sha256: sha2565
6839
+ sha256: sha2564
6340
6840
  });
6341
6841
  const objectKey = `workspaces/${input.grant.workspaceId}/files/${identity.fileId}/sandbox/${safeFilename}`;
6342
6842
  const existing = await getFile(deps.db, input.grant.workspaceId, identity.fileId);
@@ -6357,7 +6857,7 @@ async function publishSandboxFileArtifact(deps, input) {
6357
6857
  safeFilename,
6358
6858
  contentType,
6359
6859
  sizeBytes: bytes.byteLength,
6360
- sha256: sha2565,
6860
+ sha256: sha2564,
6361
6861
  bucket: storage.bucket,
6362
6862
  objectKey,
6363
6863
  expiresAt: new Date(Date.now() + UPLOAD_INTENT_TTL_MS)
@@ -6369,7 +6869,7 @@ async function publishSandboxFileArtifact(deps, input) {
6369
6869
  safeFilename,
6370
6870
  contentType,
6371
6871
  sizeBytes: bytes.byteLength,
6372
- sha256: sha2565,
6872
+ sha256: sha2564,
6373
6873
  bucket: storage.bucket,
6374
6874
  objectKey
6375
6875
  });
@@ -6382,7 +6882,7 @@ async function publishSandboxFileArtifact(deps, input) {
6382
6882
  key: file.objectKey,
6383
6883
  contentType: file.contentType,
6384
6884
  body: bytes,
6385
- sha256: sha2565
6885
+ sha256: sha2564
6386
6886
  };
6387
6887
  if (storage.putObjectIfAbsent) {
6388
6888
  const created = await storage.putObjectIfAbsent(put);
@@ -6494,7 +6994,7 @@ function assertSandboxArtifactFile(file, expected) {
6494
6994
  }
6495
6995
  }
6496
6996
  async function assertVisibleSandboxArtifact(storage, file) {
6497
- const head = await retryWhileMissing2(async () => await storage.headObject(file.objectKey));
6997
+ const head = await retryWhileMissing3(async () => await storage.headObject(file.objectKey));
6498
6998
  assertStoredSandboxArtifact(head, file);
6499
6999
  }
6500
7000
  function assertStoredSandboxArtifact(head, file) {
@@ -6582,7 +7082,7 @@ import { parseSocialOauthClientsJson } from "@opengeni/config";
6582
7082
  import {
6583
7083
  OAuthStartResponse as OAuthStartResponse2
6584
7084
  } from "@opengeni/contracts";
6585
- import { hasPermission as hasPermission5, requireEnvironmentEncryption as requireEnvironmentEncryption2 } from "@opengeni/core";
7085
+ import { hasPermission as hasPermission6, requireEnvironmentEncryption as requireEnvironmentEncryption2 } from "@opengeni/core";
6586
7086
  import {
6587
7087
  consumeIntegrationOAuthStateNonce as consumeIntegrationOAuthStateNonce2,
6588
7088
  decryptEnvironmentValue as decryptEnvironmentValue2,
@@ -6672,12 +7172,13 @@ import {
6672
7172
  OAuthStartResponse,
6673
7173
  selectCanonicalPersonalSlackConnection
6674
7174
  } from "@opengeni/contracts";
6675
- import { hasPermission as hasPermission4, requireEnvironmentEncryption } from "@opengeni/core";
7175
+ import { hasPermission as hasPermission5, requireEnvironmentEncryption } from "@opengeni/core";
6676
7176
  import {
6677
7177
  consumeIntegrationOAuthStateNonce,
6678
7178
  createConnection,
6679
7179
  decryptEnvironmentValue,
6680
7180
  encryptEnvironmentValue,
7181
+ findActiveWorkspaceApiKeyById,
6681
7182
  getConnectionMetadata,
6682
7183
  getGlobalCatalogOAuthProfile,
6683
7184
  getWorkspaceGrant,
@@ -6973,6 +7474,13 @@ var OAuthStartStageError = class extends Error {
6973
7474
  this.name = "OAuthStartStageError";
6974
7475
  }
6975
7476
  };
7477
+ var OAuthMetadataUpstreamError = class extends Error {
7478
+ constructor(upstreamStatus) {
7479
+ super(`OAuth metadata endpoint returned HTTP ${upstreamStatus}`);
7480
+ this.upstreamStatus = upstreamStatus;
7481
+ this.name = "OAuthMetadataUpstreamError";
7482
+ }
7483
+ };
6976
7484
  var OAuthStartDeadline = class {
6977
7485
  signal;
6978
7486
  controller = new AbortController();
@@ -7424,13 +7932,28 @@ function requireIntegrationsStateSecret(settings) {
7424
7932
  }
7425
7933
  async function requireOAuthCallbackGrant(db, state) {
7426
7934
  const membershipGrant = await getWorkspaceGrant(db, state.subjectId, state.workspaceId);
7427
- const grant = membershipGrant?.accountId === state.accountId ? membershipGrant : state.personalOwnerVerified ? await resolveNamedManagedPersonalWorkspaceGrant(db, state) : null;
7428
- if (!grant || grant.accountId !== state.accountId || !hasPermission4(grant.permissions, "connections:write")) {
7935
+ const grant = membershipGrant?.accountId === state.accountId ? membershipGrant : state.personalOwnerVerified ? await resolveNamedManagedPersonalWorkspaceGrant(db, state) : await apiKeyCallbackGrant(db, state);
7936
+ if (!grant || grant.accountId !== state.accountId || !hasPermission5(grant.permissions, "connections:write")) {
7429
7937
  throw new HTTPException11(403, {
7430
7938
  message: "OAuth subject no longer has permission to write this workspace connection"
7431
7939
  });
7432
7940
  }
7433
7941
  }
7942
+ async function apiKeyCallbackGrant(db, state) {
7943
+ const prefix = "api_key:";
7944
+ if (!state.subjectId.startsWith(prefix)) {
7945
+ return null;
7946
+ }
7947
+ const apiKey = await findActiveWorkspaceApiKeyById(db, {
7948
+ accountId: state.accountId,
7949
+ workspaceId: state.workspaceId,
7950
+ apiKeyId: state.subjectId.slice(prefix.length)
7951
+ });
7952
+ if (!apiKey) {
7953
+ return null;
7954
+ }
7955
+ return { accountId: apiKey.accountId, permissions: apiKey.permissions };
7956
+ }
7434
7957
  async function discoverMcpOAuth(resource, settings, deadline) {
7435
7958
  const challenge = await deadline.run(
7436
7959
  "mcp_challenge",
@@ -7493,7 +8016,7 @@ async function fetchOAuthMetadata(url, settings, signal) {
7493
8016
  }
7494
8017
  if (!response.ok) {
7495
8018
  await cancelResponseBody(response);
7496
- throw new Error(`OAuth metadata endpoint returned HTTP ${response.status}`);
8019
+ throw new OAuthMetadataUpstreamError(response.status);
7497
8020
  }
7498
8021
  const payload = await readResponseJsonBounded(
7499
8022
  response,
@@ -8072,6 +8595,9 @@ function logOAuthStartFailure(observability, error) {
8072
8595
  function oauthStartFailureReason(error) {
8073
8596
  if (error instanceof RequestDeadlineError) return "timeout";
8074
8597
  if (error instanceof DestinationPolicyError) return error.reason;
8598
+ if (error instanceof OAuthMetadataUpstreamError) {
8599
+ return `upstream_http_${error.upstreamStatus}`;
8600
+ }
8075
8601
  if (error instanceof HTTPException11) return `http_${error.status}`;
8076
8602
  if (error instanceof SyntaxError) return "invalid_response";
8077
8603
  return "request_failed";
@@ -8104,11 +8630,12 @@ function isDatabaseStatementTimeout(error) {
8104
8630
  }
8105
8631
  function oauthStartApiError(error) {
8106
8632
  const timeout = error.reason === "timeout";
8107
- const status = timeout ? 408 : error.cause instanceof HTTPException11 ? error.cause.status : 422;
8633
+ const metadataUpstream = error.cause instanceof OAuthMetadataUpstreamError ? error.cause : null;
8634
+ const status = timeout ? 408 : metadataUpstream ? 502 : error.cause instanceof HTTPException11 ? error.cause.status : 422;
8108
8635
  return new ApiHttpError(status, {
8109
8636
  code: timeout || status >= 500 ? "upstream_unavailable" : "validation_failed",
8110
- retryable: timeout || status === 429 || status >= 500,
8111
- message: timeout ? oauthStartTimeoutMessage(error.stage) : error.cause instanceof HTTPException11 ? error.cause.message : `Connection setup failed during ${oauthStartStageLabel(error.stage)}.`,
8637
+ retryable: timeout || status === 429 || (metadataUpstream ? metadataUpstream.upstreamStatus === 429 || metadataUpstream.upstreamStatus >= 500 : status >= 500),
8638
+ message: timeout ? oauthStartTimeoutMessage(error.stage) : metadataUpstream ? `OAuth provider returned HTTP ${metadataUpstream.upstreamStatus} during ${oauthStartStageLabel(error.stage)}.` : error.cause instanceof HTTPException11 ? error.cause.message : `Connection setup failed during ${oauthStartStageLabel(error.stage)}.`,
8112
8639
  details: {
8113
8640
  oauthStage: error.stage,
8114
8641
  oauthReason: error.reason
@@ -8602,7 +9129,7 @@ async function completeSocialOAuthCallback(deps, input) {
8602
9129
  throw new HTTPException12(422, { message: PERSONAL_CONNECTION_PRINCIPAL_MESSAGE });
8603
9130
  }
8604
9131
  const grant = await getWorkspaceGrant2(db, state.subjectId, state.workspaceId);
8605
- if (!grant || grant.accountId !== state.accountId || state.ownership === "workspace" && !hasPermission5(grant.permissions, "workspace:admin")) {
9132
+ if (!grant || grant.accountId !== state.accountId || state.ownership === "workspace" && !hasPermission6(grant.permissions, "workspace:admin")) {
8606
9133
  throw new HTTPException12(403, {
8607
9134
  message: "OAuth subject no longer has permission to connect social accounts"
8608
9135
  });
@@ -9416,7 +9943,82 @@ import {
9416
9943
  runOnSandbox,
9417
9944
  swapActiveSandbox
9418
9945
  } from "@opengeni/core";
9419
- import { getSessionBackgroundCommand } from "@opengeni/db/session-background-commands";
9946
+ import {
9947
+ readSessionBackgroundCommandOutput,
9948
+ COMMAND_OUTPUT_MAX_BYTES as COMMAND_OUTPUT_MAX_BYTES2
9949
+ } from "@opengeni/db/session-background-commands";
9950
+
9951
+ // src/mcp/command-read.ts
9952
+ import { COMMAND_OUTPUT_MAX_BYTES } from "@opengeni/db/session-background-commands";
9953
+ var COMMAND_READ_MAX_WAIT_SECONDS = 50;
9954
+ var COMMAND_WAIT_DEFAULT_SECONDS = 45;
9955
+ async function readCommandWithWait(input) {
9956
+ const seconds = input.waitSeconds ?? 0;
9957
+ if (!Number.isInteger(seconds) || seconds < 0 || seconds > COMMAND_READ_MAX_WAIT_SECONDS) {
9958
+ throw new Error("waitSeconds must be between 0 and 50");
9959
+ }
9960
+ if (input.maxOutputBytes !== void 0 && (!Number.isInteger(input.maxOutputBytes) || input.maxOutputBytes < 4 || input.maxOutputBytes > COMMAND_OUTPUT_MAX_BYTES)) {
9961
+ throw new Error("maxOutputBytes must be between 4 and 65536");
9962
+ }
9963
+ const started = Date.now();
9964
+ const deadline = started + seconds * 1e3;
9965
+ let unsubscribe;
9966
+ let wake;
9967
+ let changed = false;
9968
+ let liveFanout = true;
9969
+ const notify = () => {
9970
+ changed = true;
9971
+ wake?.();
9972
+ };
9973
+ input.signal?.addEventListener("abort", notify);
9974
+ try {
9975
+ if (seconds > 0) {
9976
+ try {
9977
+ unsubscribe = await input.subscribe((events) => {
9978
+ if (events.some(
9979
+ (event) => (event.type === "session.command.finished" || event.type === "sandbox.command.output.delta") && event.payload !== null && typeof event.payload === "object" && event.payload.commandId === input.commandId
9980
+ ))
9981
+ notify();
9982
+ });
9983
+ } catch {
9984
+ liveFanout = false;
9985
+ }
9986
+ }
9987
+ while (true) {
9988
+ changed = false;
9989
+ const result = await input.read();
9990
+ const expired = Date.now() >= deadline;
9991
+ if (result.terminal || result.chunks.length > 0 || result.hasMore || expired || input.signal?.aborted) {
9992
+ return {
9993
+ ...result,
9994
+ waitedMs: Date.now() - started,
9995
+ timedOut: seconds > 0 && expired && !result.terminal && result.chunks.length === 0 && !result.hasMore,
9996
+ aborted: input.signal?.aborted ?? false,
9997
+ liveFanout
9998
+ };
9999
+ }
10000
+ if (changed) continue;
10001
+ await new Promise((resolve) => {
10002
+ const timer = setTimeout(
10003
+ () => {
10004
+ wake = void 0;
10005
+ resolve();
10006
+ },
10007
+ Math.max(0, Math.min(1e3, deadline - Date.now()))
10008
+ );
10009
+ wake = () => {
10010
+ clearTimeout(timer);
10011
+ wake = void 0;
10012
+ resolve();
10013
+ };
10014
+ if (changed || input.signal?.aborted) wake();
10015
+ });
10016
+ }
10017
+ } finally {
10018
+ input.signal?.removeEventListener("abort", notify);
10019
+ unsubscribe?.();
10020
+ }
10021
+ }
9420
10022
 
9421
10023
  // src/mcp/session-view.ts
9422
10024
  import {
@@ -10308,7 +10910,7 @@ var SUMMARY_TEXT_TIERS = [2e3, 800, 200];
10308
10910
  var SUMMARY_FAILURE_CHARS = 500;
10309
10911
  var SUMMARY_RESULT_CHARS = 1e3;
10310
10912
  function truncationMarker2(droppedChars) {
10311
- return `\u2026[${droppedChars} chars omitted from this session_wait summary; use session_events for the exact event]`;
10913
+ return `\u2026[${droppedChars} chars omitted from this session_wait summary; use session_events view=debug payloadMode=full for the exact event]`;
10312
10914
  }
10313
10915
  function clampSummaryString(value, maxChars) {
10314
10916
  if (value.length <= maxChars) return value;
@@ -11111,7 +11713,8 @@ async function attachViewer(services, input) {
11111
11713
  expectedEpoch,
11112
11714
  acquiredLease: acquired.lease,
11113
11715
  fallbackEnvelope: envelope,
11114
- dataPlaneUrl: null
11716
+ dataPlaneUrl: null,
11717
+ ...services.objectStorage !== void 0 ? { objectStorage: services.objectStorage } : {}
11115
11718
  });
11116
11719
  established = result.established;
11117
11720
  return {
@@ -11768,7 +12371,7 @@ import {
11768
12371
  completeSlackBotDeleteOperation,
11769
12372
  completeSlackBotPostOperation,
11770
12373
  completeSlackBotUpdateOperation,
11771
- getSession,
12374
+ getSession as getSession2,
11772
12375
  listConnectionsMetadata as listConnectionsMetadata2,
11773
12376
  markSlackBotDeleteOperationProviderStarted,
11774
12377
  markSlackBotPostOperationProviderStarted,
@@ -11948,7 +12551,7 @@ async function verifyOpenGeniSlackBotCredential(token, expected, fetchImpl = fet
11948
12551
  };
11949
12552
  }
11950
12553
  async function resolveSlackBotConnectionForTool(input) {
11951
- const session = input.sessionId ? await getSession(input.db, input.grant.workspaceId, input.sessionId) : null;
12554
+ const session = input.sessionId ? await getSession2(input.db, input.grant.workspaceId, input.sessionId) : null;
11952
12555
  if (input.sessionId && !session) {
11953
12556
  throw new Error("signed Slack bot session was not found");
11954
12557
  }
@@ -13986,7 +14589,7 @@ import {
13986
14589
  } from "@opengeni/contracts";
13987
14590
  import {
13988
14591
  fikenConnectionMetadata,
13989
- hasPermission as hasPermission6,
14592
+ hasPermission as hasPermission7,
13990
14593
  isFikenConnection,
13991
14594
  preferredFikenConnection,
13992
14595
  requireEnvironmentEncryption as requireEnvironmentEncryption3,
@@ -14902,7 +15505,7 @@ function readFikenOAuthState(raw, settings) {
14902
15505
  }
14903
15506
  async function requireFikenCallbackGrant(db, state) {
14904
15507
  const grant = await getWorkspaceGrant3(db, state.subjectId, state.workspaceId);
14905
- if (!grant || grant.accountId !== state.accountId || !hasPermission6(grant.permissions, "connections:write")) {
15508
+ if (!grant || grant.accountId !== state.accountId || !hasPermission7(grant.permissions, "connections:write")) {
14906
15509
  throw new FikenOAuthCallbackError("permission_lost");
14907
15510
  }
14908
15511
  }
@@ -14936,7 +15539,7 @@ import {
14936
15539
  import {
14937
15540
  captureScheduledTaskRestoreState,
14938
15541
  createValidatedScheduledTask,
14939
- hasPermission as hasPermission7,
15542
+ hasPermission as hasPermission8,
14940
15543
  manualScheduledTaskTriggerWorkflowId,
14941
15544
  requireEnvironmentEncryption as requireEnvironmentEncryption4,
14942
15545
  syncCreatedScheduledTask,
@@ -16172,7 +16775,7 @@ async function requireCallbackGrant(deps, state) {
16172
16775
  });
16173
16776
  }
16174
16777
  const grant = await getWorkspaceGrant4(deps.db, state.subjectId, state.workspaceId);
16175
- if (!grant || grant.accountId !== state.accountId || !hasPermission7(grant.permissions, "connections:write")) {
16778
+ if (!grant || grant.accountId !== state.accountId || !hasPermission8(grant.permissions, "connections:write")) {
16176
16779
  throw new HTTPException16(403, { message: "Atlassian OAuth subject no longer has access" });
16177
16780
  }
16178
16781
  }
@@ -16347,9 +16950,9 @@ import {
16347
16950
  editableArtifactId,
16348
16951
  editableArtifactReplicaId,
16349
16952
  editableArtifactScope,
16350
- hasPermission as hasPermission8,
16953
+ hasPermission as hasPermission9,
16351
16954
  requireAccessGrant as requireAccessGrant2,
16352
- requireSessionAuthorization,
16955
+ requireSessionAuthorization as requireSessionAuthorization2,
16353
16956
  SessionAuthorizationDeniedError as SessionAuthorizationDeniedError3,
16354
16957
  SessionAuthorizationUnavailableError
16355
16958
  } from "@opengeni/core";
@@ -16580,7 +17183,7 @@ function registerEditableArtifactRoutes(app, deps) {
16580
17183
  app.post(`${collection}/imports`, async (context) => {
16581
17184
  const workspaceId = context.req.param("workspaceId");
16582
17185
  const grant = await requireAccessGrant2(context, deps, workspaceId, "artifacts:publish");
16583
- if (!hasPermission8(grant.permissions, "files:read")) {
17186
+ if (!hasPermission9(grant.permissions, "files:read")) {
16584
17187
  throw applicationHttpError("forbidden");
16585
17188
  }
16586
17189
  const body4 = await parseBoundedImportRequest(context);
@@ -16661,7 +17264,7 @@ function registerEditableArtifactRoutes(app, deps) {
16661
17264
  snapshotVersion: body4.snapshotVersion,
16662
17265
  commandProtocolVersion: body4.commandProtocolVersion,
16663
17266
  committedTransactionProtocolVersion: body4.committedTransactionProtocolVersion,
16664
- allowEdit: hasPermission8(grant.permissions, "artifacts:publish")
17267
+ allowEdit: hasPermission9(grant.permissions, "artifacts:publish")
16665
17268
  });
16666
17269
  } catch (error) {
16667
17270
  throw editableArtifactHttpError(error);
@@ -16779,7 +17382,7 @@ function registerEditableArtifactRoutes(app, deps) {
16779
17382
  }
16780
17383
  async function authorizeSourceSession(deps, grant, sessionId) {
16781
17384
  try {
16782
- await requireSessionAuthorization(deps, grant, {
17385
+ await requireSessionAuthorization2(deps, grant, {
16783
17386
  sessionId,
16784
17387
  operation: "session.read",
16785
17388
  surface: "http"
@@ -18198,7 +18801,7 @@ import {
18198
18801
  import {
18199
18802
  captureScheduledTaskRestoreState as captureScheduledTaskRestoreState2,
18200
18803
  createValidatedScheduledTask as createValidatedScheduledTask2,
18201
- hasPermission as hasPermission9,
18804
+ hasPermission as hasPermission10,
18202
18805
  manualScheduledTaskTriggerWorkflowId as manualScheduledTaskTriggerWorkflowId2,
18203
18806
  requireEnvironmentEncryption as requireEnvironmentEncryption5,
18204
18807
  syncCreatedScheduledTask as syncCreatedScheduledTask2,
@@ -19660,7 +20263,7 @@ async function requireGoogleDriveCallbackGrant(deps, state) {
19660
20263
  });
19661
20264
  }
19662
20265
  const grant = await getWorkspaceGrant5(deps.db, state.subjectId, state.workspaceId);
19663
- if (!grant || grant.accountId !== state.accountId || !hasPermission9(grant.permissions, "connections:write")) {
20266
+ if (!grant || grant.accountId !== state.accountId || !hasPermission10(grant.permissions, "connections:write")) {
19664
20267
  throw new HTTPException17(403, {
19665
20268
  message: "Google Drive OAuth subject no longer has permission for this workspace"
19666
20269
  });
@@ -20323,115 +20926,6 @@ function observeWorkDiscovery(observability, observation) {
20323
20926
  }
20324
20927
  }
20325
20928
 
20326
- // src/workspace-artifact-content.ts
20327
- import { createHash as createHash9 } from "crypto";
20328
- import {
20329
- WorkspaceArtifactHtml,
20330
- WorkspaceArtifactRequestedTools,
20331
- WorkspaceArtifactSourceBundle
20332
- } from "@opengeni/contracts";
20333
- import { retryWhileMissing as retryWhileMissing3 } from "@opengeni/storage";
20334
- var encoder2 = new TextEncoder();
20335
- var decoder = new TextDecoder("utf-8", { fatal: true });
20336
- function prepareWorkspaceArtifactContent(objectStorage, workspaceId, input) {
20337
- const html = WorkspaceArtifactHtml.parse(input.html);
20338
- const source = WorkspaceArtifactSourceBundle.parse(input.source ?? sourceBundleFromHtml(html));
20339
- const requestedTools = input.requestedTools === void 0 ? void 0 : WorkspaceArtifactRequestedTools.parse(input.requestedTools);
20340
- const contentBytes = encoder2.encode(html);
20341
- const sourceBytes = encoder2.encode(JSON.stringify(source));
20342
- const contentSha256 = sha256(contentBytes);
20343
- const sourceSha256 = sha256(sourceBytes);
20344
- const storageGroupId = crypto.randomUUID();
20345
- const contentKey = `workspaces/${workspaceId}/workspace-artifacts/blobs/${storageGroupId}-${contentSha256}.html`;
20346
- const sourceKey = `workspaces/${workspaceId}/workspace-artifacts/sources/${storageGroupId}-${sourceSha256}.json`;
20347
- const discardContent = async () => {
20348
- await Promise.allSettled([
20349
- objectStorage.deleteObject(contentKey),
20350
- objectStorage.deleteObject(sourceKey)
20351
- ]);
20352
- };
20353
- return {
20354
- contentKey,
20355
- contentSha256,
20356
- sizeBytes: contentBytes.byteLength,
20357
- sourceKey,
20358
- sourceSha256,
20359
- sourceSizeBytes: sourceBytes.byteLength,
20360
- ...requestedTools === void 0 ? {} : { requestedTools },
20361
- discardContent,
20362
- persistContent: async () => {
20363
- const writes = await Promise.allSettled([
20364
- objectStorage.putObject({
20365
- key: contentKey,
20366
- contentType: "text/html; charset=utf-8",
20367
- body: contentBytes,
20368
- sha256: contentSha256
20369
- }),
20370
- objectStorage.putObject({
20371
- key: sourceKey,
20372
- contentType: "application/json; charset=utf-8",
20373
- body: sourceBytes,
20374
- sha256: sourceSha256
20375
- })
20376
- ]);
20377
- const failed = writes.find((write) => write.status === "rejected");
20378
- if (!failed) return;
20379
- await Promise.allSettled(
20380
- writes.flatMap(
20381
- (write, index) => write.status === "fulfilled" ? [objectStorage.deleteObject(index === 0 ? contentKey : sourceKey)] : []
20382
- )
20383
- );
20384
- throw failed.reason;
20385
- }
20386
- };
20387
- }
20388
- async function readWorkspaceArtifactContent(objectStorage, input) {
20389
- const [contentObject, sourceObject] = await Promise.all([
20390
- retryWhileMissing3(async () => await objectStorage.getObjectBytes(input.contentKey)),
20391
- input.sourceKey ? retryWhileMissing3(async () => await objectStorage.getObjectBytes(input.sourceKey)) : Promise.resolve(null)
20392
- ]);
20393
- if (!contentObject) throw new Error("Artifact content is unavailable");
20394
- if (sha256(contentObject.bytes) !== input.version.contentSha256 || contentObject.bytes.byteLength !== input.version.sizeBytes) {
20395
- throw new Error("Artifact content failed integrity verification");
20396
- }
20397
- const html = decode(contentObject.bytes, "Artifact content is not valid UTF-8");
20398
- let source = sourceBundleFromHtml(html);
20399
- if (input.sourceKey) {
20400
- if (!sourceObject) throw new Error("Artifact source is unavailable");
20401
- if (!input.version.sourceSha256 || input.version.sourceSizeBytes === null || sha256(sourceObject.bytes) !== input.version.sourceSha256 || sourceObject.bytes.byteLength !== input.version.sourceSizeBytes) {
20402
- throw new Error("Artifact source failed integrity verification");
20403
- }
20404
- try {
20405
- source = WorkspaceArtifactSourceBundle.parse(
20406
- JSON.parse(decode(sourceObject.bytes, "Artifact source is not valid UTF-8"))
20407
- );
20408
- } catch (error) {
20409
- if (error instanceof Error && error.message === "Artifact source is not valid UTF-8") {
20410
- throw error;
20411
- }
20412
- throw new Error("Artifact source is invalid", { cause: error });
20413
- }
20414
- }
20415
- return {
20416
- html,
20417
- source,
20418
- requestedTools: WorkspaceArtifactRequestedTools.parse(input.version.requestedTools)
20419
- };
20420
- }
20421
- function sourceBundleFromHtml(html) {
20422
- return { entrypoint: "index.html", files: [{ path: "index.html", content: html }] };
20423
- }
20424
- function sha256(bytes) {
20425
- return createHash9("sha256").update(bytes).digest("hex");
20426
- }
20427
- function decode(bytes, message) {
20428
- try {
20429
- return decoder.decode(bytes);
20430
- } catch (error) {
20431
- throw new Error(message, { cause: error });
20432
- }
20433
- }
20434
-
20435
20929
  // src/mcp/server.ts
20436
20930
  var ORCHESTRATION_FAILURE_CODE_MAX_LENGTH = 128;
20437
20931
  var ORCHESTRATION_FAILURE_MESSAGE_MAX_UTF8_BYTES = 1024;
@@ -20519,6 +21013,7 @@ var FIRST_PARTY_TOOL_AUTHORIZATION = {
20519
21013
  wait_for_input: { sessionRequired: true, allOf: ["sessions:control"] },
20520
21014
  goal_complete: { sessionRequired: true, allOf: ["goals:manage"] },
20521
21015
  goal_pause: { sessionRequired: true, allOf: ["goals:manage"] },
21016
+ goal_resume: { sessionRequired: true, allOf: ["goals:manage"] },
20522
21017
  memory_search: { sessionRequired: true, allOf: ["documents:search"] },
20523
21018
  memory_save: { sessionRequired: true, allOf: ["documents:search"] },
20524
21019
  memory_correct: { sessionRequired: true, allOf: ["documents:search"] },
@@ -20591,6 +21086,7 @@ var FIRST_PARTY_TOOL_AUTHORIZATION = {
20591
21086
  // self target, so the tool exists only for session-scoped grants.
20592
21087
  session_wait: { sessionRequired: true, allOf: ["sessions:read"] },
20593
21088
  command_wait: { sessionRequired: true, allOf: ["sessions:read"] },
21089
+ command_read: { sessionRequired: true, allOf: ["sessions:read"] },
20594
21090
  session_create: { allOf: ["sessions:create"] },
20595
21091
  session_send_message: { allOf: ["sessions:control"] },
20596
21092
  session_pause: { allOf: ["sessions:control"] },
@@ -20705,8 +21201,9 @@ var FIRST_PARTY_TOOL_AUTHORIZATION = {
20705
21201
  atlassian_sources_list: { allOf: ["connections:read"] },
20706
21202
  atlassian_search: { allOf: ["connections:read"] },
20707
21203
  atlassian_get: { allOf: ["connections:read"] },
20708
- artifacts_list: { sessionRequired: true, allOf: ["artifacts:read"] },
21204
+ artifacts_list: { allOf: ["artifacts:read"] },
20709
21205
  artifacts_get_source: { sessionRequired: true, allOf: ["artifacts:read"] },
21206
+ artifacts_prepare_upload: { sessionRequired: true, allOf: ["artifacts:publish"] },
20710
21207
  artifacts_create: { sessionRequired: true, allOf: ["artifacts:publish"] },
20711
21208
  artifacts_publish: { sessionRequired: true, allOf: ["artifacts:publish"] },
20712
21209
  artifacts_rollback: { sessionRequired: true, allOf: ["artifacts:publish"] },
@@ -20758,7 +21255,7 @@ var PolicyMcpServer = class extends McpServer3 {
20758
21255
  if (catalogued) {
20759
21256
  const toolName = name;
20760
21257
  const policy = FIRST_PARTY_TOOL_AUTHORIZATION[toolName];
20761
- const authorized = (!policy.sessionRequired || this.sessionId !== null) && (policy.allOf?.every((permission) => hasPermission10(this.grant.permissions, permission)) ?? true) && (policy.anyOf?.some((permission) => hasPermission10(this.grant.permissions, permission)) ?? true);
21258
+ const authorized = (!policy.sessionRequired || this.sessionId !== null) && (policy.allOf?.every((permission) => hasPermission11(this.grant.permissions, permission)) ?? true) && (policy.anyOf?.some((permission) => hasPermission11(this.grant.permissions, permission)) ?? true);
20762
21259
  const selected = this.selectedTools === null || this.selectedTools.has(toolName);
20763
21260
  admitted = authorized && selected;
20764
21261
  }
@@ -20801,7 +21298,7 @@ function buildOpenGeniMcpServer(deps, grant, options = {}) {
20801
21298
  const json = (value) => ({
20802
21299
  content: [{ type: "text", text: JSON.stringify(value, null, 2) }]
20803
21300
  });
20804
- const can = (permission) => hasPermission10(grant.permissions, permission);
21301
+ const can = (permission) => hasPermission11(grant.permissions, permission);
20805
21302
  let socialConnectionsPromise;
20806
21303
  const authorizedSocialConnections = () => socialConnectionsPromise ??= authorizedSocialConnectionsForGrant({
20807
21304
  db: deps.db,
@@ -20862,6 +21359,23 @@ function buildOpenGeniMcpServer(deps, grant, options = {}) {
20862
21359
  options.workspaceMemoryPromptMode ?? "retrieval_only"
20863
21360
  );
20864
21361
  }
21362
+ server.registerTool(
21363
+ "artifacts_list",
21364
+ {
21365
+ description: "List the generic published artifacts in this workspace and their current versions.",
21366
+ inputSchema: {}
21367
+ },
21368
+ async () => {
21369
+ if (sessionId !== null && exactAgentAttemptClaims(grant) !== null) {
21370
+ await authorizeFirstPartySession(deps, grant, sessionId, "session.first_party_mcp.call");
21371
+ }
21372
+ return json(
21373
+ redactWorkspaceArtifactListProvenance(
21374
+ await listWorkspaceArtifacts(deps.db, grant.workspaceId)
21375
+ )
21376
+ );
21377
+ }
21378
+ );
20865
21379
  if (sessionId !== null && exactAgentAttemptClaims(grant) !== null) {
20866
21380
  registerPreferenceRegistryTools(server, deps, grant, json);
20867
21381
  registerTaskNoteTools(server, deps, grant, sessionId, json);
@@ -22507,7 +23021,7 @@ function registerGoalTools(server, deps, grant, sessionId, json) {
22507
23021
  server.registerTool(
22508
23022
  "wait_for_input",
22509
23023
  {
22510
- description: "End the current turn and wait out of turn for relevant session input. This is self-only and does not require a goal. Use it for long or uncertain waits instead of sleeping or repeatedly calling session_wait/command_wait. timeoutSeconds is a relative safety-wake duration; OpenGeni persists its absolute deadline, and timeout never cancels a background command. A human/API prompt, agent message or Steer, child terminal result, scheduled input, terminal background-command result, or the deadline wakes the session. Always end your turn immediately after the tool succeeds. Use goal_pause instead when the active goal itself should stop pending a human decision.",
23024
+ description: "End the current turn and wait out of turn for relevant session input. This is self-only and does not require a goal. After success, the production runtime ends the turn at the tool-batch boundary without another model step or final message. Use it for long or uncertain waits instead of sleeping or repeatedly calling session_wait/command_wait. timeoutSeconds is a relative safety-wake duration; OpenGeni persists the first absolute deadline for the turn, and repeated calls do not extend it. Timeout never cancels a background command. A human/API prompt, agent message or Steer, child terminal result, scheduled input, terminal background-command result, or the deadline wakes the session. Use goal_pause instead when the active goal itself should stop pending a human decision.",
22511
23025
  inputSchema: {
22512
23026
  reason: inputWaitReasonSchema,
22513
23027
  timeoutSeconds: z43.number().int().min(SESSION_INPUT_WAIT_MIN_SECONDS).max(SESSION_INPUT_WAIT_MAX_SECONDS),
@@ -22517,7 +23031,7 @@ function registerGoalTools(server, deps, grant, sessionId, json) {
22517
23031
  async ({ reason: reason2, timeoutSeconds, idempotencyKey }) => {
22518
23032
  await authorizeFirstPartySession(deps, grant, sessionId, "session.control");
22519
23033
  const context = exactAgentCommandContext(grant, sessionId);
22520
- const operationKey = idempotencyKey ?? `wait-for-input:${context.callerTurnId}:${createHash10("sha256").update(JSON.stringify({ reason: reason2, timeoutSeconds })).digest("hex").slice(0, 32)}`;
23034
+ const operationKey = idempotencyKey ?? `wait-for-input:${context.callerTurnId}:${createHash9("sha256").update(JSON.stringify({ reason: reason2, timeoutSeconds })).digest("hex").slice(0, 32)}`;
22521
23035
  const { events, operationId, replay, deadlineAt } = await waitForSessionInputWithEvent(
22522
23036
  deps.db,
22523
23037
  grant.workspaceId,
@@ -22544,7 +23058,7 @@ function registerGoalTools(server, deps, grant, sessionId, json) {
22544
23058
  deadlineAt,
22545
23059
  operationId,
22546
23060
  replay,
22547
- nextAction: "End your turn now. Relevant input or the timeout deadline will start a new turn; the timeout does not cancel commands."
23061
+ nextAction: "The runtime yields this turn after the tool batch settles. Relevant input or the timeout deadline will start a new turn; the timeout does not cancel commands."
22548
23062
  });
22549
23063
  }
22550
23064
  );
@@ -22646,9 +23160,63 @@ function registerGoalTools(server, deps, grant, sessionId, json) {
22646
23160
  );
22647
23161
  }
22648
23162
  );
23163
+ server.registerTool(
23164
+ "goal_resume",
23165
+ {
23166
+ description: "Resume this session's paused goal regardless of who paused it or why. Already active is a successful no-op. Preserves the objective and resets continuation counters.",
23167
+ inputSchema: {}
23168
+ },
23169
+ async () => {
23170
+ await authorizeFirstPartySession(deps, grant, sessionId, "session.goal.write");
23171
+ await requireSession(deps.db, grant.workspaceId, sessionId);
23172
+ const existing = await getSessionGoal(deps.db, grant.workspaceId, sessionId);
23173
+ if (!existing) {
23174
+ throw new Error("this session has no goal; use goal_set first");
23175
+ }
23176
+ const { goal, events, workflowWakeRevision } = await setSessionGoalStatusWithEvent(
23177
+ deps.db,
23178
+ grant.workspaceId,
23179
+ sessionId,
23180
+ {
23181
+ status: "active",
23182
+ event: { type: "goal.resumed", actor: "agent" }
23183
+ }
23184
+ );
23185
+ const changed = events.length > 0;
23186
+ if (events.length > 0) {
23187
+ await deps.bus.publish(grant.workspaceId, sessionId, events);
23188
+ }
23189
+ if (workflowWakeRevision !== null) {
23190
+ await deps.workflowClient.wakeSessionWorkflow({
23191
+ accountId: grant.accountId,
23192
+ workspaceId: grant.workspaceId,
23193
+ sessionId,
23194
+ workflowId: workflowIdForSession(sessionId),
23195
+ wakeRevision: workflowWakeRevision
23196
+ });
23197
+ }
23198
+ return json(
23199
+ mcpMutationReceipt({
23200
+ operation: "goal_resume",
23201
+ committed: true,
23202
+ outcome: changed ? "updated" : "unchanged",
23203
+ changed,
23204
+ resource: {
23205
+ type: "session_goal",
23206
+ id: goal.id,
23207
+ version: goal.version,
23208
+ state: goal.status
23209
+ },
23210
+ timestamp: goal.updatedAt,
23211
+ idempotency: { status: "not_supported" },
23212
+ nextAction: { tool: "session_get", arguments: { sessionId } }
23213
+ })
23214
+ );
23215
+ }
23216
+ );
22649
23217
  }
22650
23218
  async function authorizeFirstPartySession(deps, grant, sessionId, operation) {
22651
- return await requireSessionAuthorization2(deps, grant, {
23219
+ return await requireSessionAuthorization3(deps, grant, {
22652
23220
  sessionId,
22653
23221
  operation,
22654
23222
  surface: "first_party_mcp"
@@ -22668,10 +23236,11 @@ function registerWorkspaceArtifactTools(server, deps, grant, sessionId, json) {
22668
23236
  throw error;
22669
23237
  });
22670
23238
  const mutation = async (response) => await projectWorkspaceArtifactMutationProvenance(response, canReadProvenanceSession2);
22671
- const prepare = (html, source, requestedTools) => {
23239
+ const prepare = (html, source, requestedTools, uploadId) => {
22672
23240
  if (!deps.objectStorage) throw new Error("Object storage is not configured");
22673
- return prepareWorkspaceArtifactContent(deps.objectStorage, grant.workspaceId, {
22674
- html,
23241
+ return prepareWorkspaceArtifactPublication(deps, grant, {
23242
+ ...html === void 0 ? {} : { html },
23243
+ ...uploadId ? { uploadId } : {},
22675
23244
  ...source ? { source } : {},
22676
23245
  ...requestedTools ? { requestedTools } : {}
22677
23246
  });
@@ -22681,7 +23250,7 @@ function registerWorkspaceArtifactTools(server, deps, grant, sessionId, json) {
22681
23250
  return {
22682
23251
  accountId: grant.accountId,
22683
23252
  workspaceId: grant.workspaceId,
22684
- operationKey: `attempt:${createHash10("sha256").update(`${claims.sessionId}:${claims.turnId}:${idempotencyKey}`).digest("hex")}`,
23253
+ operationKey: `attempt:${createHash9("sha256").update(`${claims.sessionId}:${claims.turnId}:${idempotencyKey}`).digest("hex")}`,
22685
23254
  actorSubjectId: grant.subjectId,
22686
23255
  sourceSessionId: claims.sessionId,
22687
23256
  sourceTurnId: claims.turnId,
@@ -22691,24 +23260,20 @@ function registerWorkspaceArtifactTools(server, deps, grant, sessionId, json) {
22691
23260
  };
22692
23261
  };
22693
23262
  server.registerTool(
22694
- "artifacts_list",
23263
+ "artifacts_prepare_upload",
22695
23264
  {
22696
- description: "List the generic published artifacts in this workspace and their current versions.",
23265
+ description: "Get signed upload URLs for a Site's HTML and optional source JSON. Upload with curl or fetch, then pass uploadId to artifacts_create/artifacts_publish. No hashes or sizes needed. Source upload may be omitted for HTML-only Sites.",
22697
23266
  inputSchema: {}
22698
23267
  },
22699
23268
  async () => {
22700
23269
  await authorize();
22701
- return json(
22702
- redactWorkspaceArtifactListProvenance(
22703
- await listWorkspaceArtifacts(deps.db, grant.workspaceId)
22704
- )
22705
- );
23270
+ return json(await prepareWorkspaceArtifactUpload(deps, grant));
22706
23271
  }
22707
23272
  );
22708
23273
  server.registerTool(
22709
23274
  "artifacts_get_source",
22710
23275
  {
22711
- description: "Read an artifact's metadata and exact HTML source. Omit versionId for the current version.",
23276
+ description: "Get artifact metadata, version id, and signed download URLs for HTML and optional source JSON. Download with curl or Bun. If source is null, edit the HTML instead. Omit versionId for the current version.",
22712
23277
  inputSchema: {
22713
23278
  artifactId: z43.string().uuid(),
22714
23279
  versionId: z43.string().uuid().optional()
@@ -22733,11 +23298,11 @@ function registerWorkspaceArtifactTools(server, deps, grant, sessionId, json) {
22733
23298
  projectWorkspaceArtifactDetailProvenance(rawDetail, canReadSourceSession),
22734
23299
  projectWorkspaceArtifactVersionProvenance(ref.version, canReadSourceSession)
22735
23300
  ]);
22736
- const content = await readWorkspaceArtifactContent(deps.objectStorage, ref);
23301
+ const downloads = await workspaceArtifactDownloads(deps.objectStorage, ref, "sandbox");
22737
23302
  return json({
22738
23303
  detail,
22739
23304
  version: projectedVersion,
22740
- ...content
23305
+ downloads
22741
23306
  });
22742
23307
  }
22743
23308
  );
@@ -22749,7 +23314,8 @@ function registerWorkspaceArtifactTools(server, deps, grant, sessionId, json) {
22749
23314
  title: z43.string().min(1).max(120),
22750
23315
  description: z43.string().max(2e3).nullable().optional(),
22751
23316
  slug: z43.string().regex(/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/).max(96).optional(),
22752
- html: z43.string().min(1).max(WORKSPACE_ARTIFACT_HTML_MAX_UTF8_BYTES),
23317
+ html: z43.string().min(1).max(WORKSPACE_ARTIFACT_HTML_MAX_UTF8_BYTES).optional(),
23318
+ uploadId: z43.string().uuid().optional(),
22753
23319
  source: z43.object({
22754
23320
  entrypoint: z43.string().min(1).max(256),
22755
23321
  files: z43.array(
@@ -22768,7 +23334,16 @@ function registerWorkspaceArtifactTools(server, deps, grant, sessionId, json) {
22768
23334
  idempotencyKey: z43.string().min(1).max(200)
22769
23335
  }
22770
23336
  },
22771
- async ({ title, description, slug, html, source, requestedTools, idempotencyKey }) => {
23337
+ async ({
23338
+ title,
23339
+ description,
23340
+ slug,
23341
+ html,
23342
+ source,
23343
+ requestedTools,
23344
+ idempotencyKey,
23345
+ uploadId
23346
+ }) => {
22772
23347
  await authorize();
22773
23348
  const artifactId2 = crypto.randomUUID();
22774
23349
  const slugBase = slug ?? (normalizeWorkspaceArtifactSlug(title) || "artifact");
@@ -22781,7 +23356,7 @@ function registerWorkspaceArtifactTools(server, deps, grant, sessionId, json) {
22781
23356
  requestedSlug: slug ?? null,
22782
23357
  title,
22783
23358
  description: description ?? null,
22784
- ...prepare(html, source, requestedTools),
23359
+ ...await prepare(html, source, requestedTools, uploadId),
22785
23360
  ...provenance2(idempotencyKey, "artifacts_create")
22786
23361
  })
22787
23362
  )
@@ -22797,7 +23372,8 @@ function registerWorkspaceArtifactTools(server, deps, grant, sessionId, json) {
22797
23372
  expectedCurrentVersionId: z43.string().uuid(),
22798
23373
  title: z43.string().min(1).max(120).optional(),
22799
23374
  description: z43.string().max(2e3).nullable().optional(),
22800
- html: z43.string().min(1).max(WORKSPACE_ARTIFACT_HTML_MAX_UTF8_BYTES),
23375
+ html: z43.string().min(1).max(WORKSPACE_ARTIFACT_HTML_MAX_UTF8_BYTES).optional(),
23376
+ uploadId: z43.string().uuid().optional(),
22801
23377
  source: z43.object({
22802
23378
  entrypoint: z43.string().min(1).max(256),
22803
23379
  files: z43.array(
@@ -22822,6 +23398,7 @@ function registerWorkspaceArtifactTools(server, deps, grant, sessionId, json) {
22822
23398
  title,
22823
23399
  description,
22824
23400
  html,
23401
+ uploadId,
22825
23402
  source,
22826
23403
  requestedTools,
22827
23404
  idempotencyKey
@@ -22834,7 +23411,7 @@ function registerWorkspaceArtifactTools(server, deps, grant, sessionId, json) {
22834
23411
  expectedCurrentVersionId,
22835
23412
  ...title !== void 0 ? { title } : {},
22836
23413
  ...description !== void 0 ? { description } : {},
22837
- ...prepare(html, source, requestedTools),
23414
+ ...await prepare(html, source, requestedTools, uploadId),
22838
23415
  ...provenance2(idempotencyKey, "artifacts_publish")
22839
23416
  })
22840
23417
  )
@@ -24125,20 +24702,28 @@ function registerWorkspaceOrchestrationTools(server, deps, grant, can, callerSes
24125
24702
  server.registerTool(
24126
24703
  "session_get",
24127
24704
  {
24128
- description: "Get another session you are managing. Default detail=compact returns status, goal (including completion evidence or pause rationale), latest recorded goal progress, meaningful pause/wait state, queue counts, active turn and lastSequence for session_wait. Goal completion is not a terminal child result: join with session_wait waitFor=completion. Use detail=full for the legacy bounded configuration including resources, persisted tool refs, effectiveToolPolicy and variableSet ids (never variable values). Full mode is configuration, not a substitute for compact goal/progress facts. Do not use your own session id to reconstruct context; conversation history and persistent settings are supplied directly. Text loss is explicit; REST/UI defaults are unchanged.",
24705
+ description: "Get an authorized session. Omit sessionId to read only the authenticated current agent session (a child reads itself, never its parent or root); sessionless/operator callers must provide an explicit ID. Both forms retain live-attempt and target authorization checks. Default detail=compact returns status, goal (including completion evidence or pause rationale), latest recorded goal progress, meaningful pause/wait state, queue counts, active turn and snapshot lastSequence. Goal completion is not a terminal child result: join with session_wait waitFor=completion from the last consumed event cursor (0 if none), not this snapshot lastSequence, which may already include an unread completion. For an already-settled child, retrieve its result-bearing completion with session_events or join from the last consumed cursor. Use detail=full for the legacy bounded configuration including resources, persisted tool refs, effectiveToolPolicy and variableSet ids (never variable values). Full mode is configuration, not a substitute for compact goal/progress facts. Self reads inspect session state, not conversation history, which is supplied directly. Text loss is explicit; REST/UI defaults are unchanged.",
24129
24706
  inputSchema: {
24130
- sessionId: z43.string().uuid(),
24707
+ sessionId: z43.string().uuid().optional().describe(
24708
+ "Omit only to read the authenticated current agent session, never its parent or root. Sessionless callers must provide an explicit ID."
24709
+ ),
24131
24710
  detail: z43.enum(["compact", "full"]).optional()
24132
24711
  }
24133
24712
  },
24134
- async ({ sessionId, detail }) => {
24713
+ async ({ sessionId: requestedSessionId, detail }) => {
24714
+ const sessionId = requestedSessionId ?? exactAgentAttemptClaims(grant)?.sessionId;
24715
+ if (sessionId === void 0) {
24716
+ throw new Error(
24717
+ "session_get requires an explicit sessionId without authenticated current agent session context"
24718
+ );
24719
+ }
24135
24720
  const authorization = await authorizeFirstPartySession(
24136
24721
  deps,
24137
24722
  grant,
24138
24723
  sessionId,
24139
24724
  "session.read"
24140
24725
  );
24141
- const session = await getSession2(deps.db, grant.workspaceId, sessionId);
24726
+ const session = await getSession3(deps.db, grant.workspaceId, sessionId);
24142
24727
  if (!session) {
24143
24728
  throw new Error("session not found");
24144
24729
  }
@@ -24169,9 +24754,14 @@ function registerWorkspaceOrchestrationTools(server, deps, grant, can, callerSes
24169
24754
  server.registerTool(
24170
24755
  "session_events",
24171
24756
  {
24172
- description: "Read a compact semantic tail only when session_get status is insufficient. With no cursor, this returns the newest matching events and excludes raw message/reasoning/command/PTY deltas. Monitoring mode also omits a human/API user.message whose turn was never claimed (still queued, or deleted/edited/cancelled before any claim; waiting work is represented by sessions_list queuedPromptCount); the row appears at its own sequence once the turn is claimed. Use `latest` as an exclusive lookup for the authoritative newest durable sequence in exactly one semantic class; `receipt` is the concise alias for `tool_receipt`, and latest cannot be combined with type or class filters. Add `resultMode=compact` to latest for one bounded result-bearing completion/checkpoint/receipt without another inference. Use nextBefore to page older or explicit after/nextAfter to page forward. Type/class filters run in the RLS-scoped database query. payloadMode none|summary|full controls retained audit payload projection, but every model result is independently byte-capped with explicit truncation and exact covered sequence bounds. Exact retained forensic payloads, including unclaimed prompt rows, require mode=forensic or the access-controlled REST/SDK events API with mode=forensic&payloadMode=full; generic source bytes never retained by the audit boundary remain unavailable.",
24757
+ description: "Read session history. Default view=conversation returns roughly ten complete user/assistant messages, including completed commentary, in a 16 KiB envelope; no token deltas or execution records. Prefer fewer complete messages; a single oversized message has fragment offsets and a lossless nextCursor continuation over retained source text, including large legacy rows. Fragment unit is codepoint for plain text or utf16 for codec text; pass the opaque v2 cursor unchanged (v1 cursors must restart). Pass cursor=nextCursor with sessionId, omitting other selectors; it binds the view, detail and direction. after/nextAfter and before/nextBefore only change position, never view or detail; use nextCursor when present to avoid skipping a message fragment. view=results returns final turn answers and actionable outcomes without duplicate message-completion text. view=tools returns compact call/result identities; includeArguments/includeOutput opt into one text or JSON-encoded value, and callId selects an exact call (sparse scans can return an empty advancing page). sourceExact=false and sourceOmitted identify oversized structured values that were omitted, never partial JSON presented as complete; scalar text remains resumable. Conversation/results/tools omit never-claimed human/API prompts and stale duplicate events. view=debug exposes the existing authorized audit query with explicit type/class filters, mode=monitoring|forensic and payloadMode=none|summary|full; raw deltas and never-claimed prompts require mode=forensic. Explicit legacy audit selectors remain supported without view. latest is an exclusive semantic-class lookup; resultMode=compact requires latest. No read observes commands or changes append-only history. REST behavior is unchanged.",
24173
24758
  inputSchema: {
24174
24759
  sessionId: z43.string().uuid(),
24760
+ view: z43.enum(["conversation", "results", "tools", "debug"]).optional(),
24761
+ cursor: z43.string().max(4096).optional(),
24762
+ callId: z43.string().max(512).optional(),
24763
+ includeArguments: z43.boolean().optional(),
24764
+ includeOutput: z43.boolean().optional(),
24175
24765
  after: z43.number().int().nonnegative().optional(),
24176
24766
  before: z43.number().int().positive().optional(),
24177
24767
  limit: z43.number().int().positive().optional(),
@@ -24179,8 +24769,22 @@ function registerWorkspaceOrchestrationTools(server, deps, grant, can, callerSes
24179
24769
  mode: z43.enum(SessionEventReadMode.options).optional(),
24180
24770
  payloadMode: z43.enum(SessionEventPayloadMode.options).optional(),
24181
24771
  resultMode: z43.enum(SessionEventResultMode.options).optional(),
24182
- includeTypes: z43.array(z43.enum(SessionEventType.options)).max(100).optional(),
24183
- excludeTypes: z43.array(z43.enum(SessionEventType.options)).max(100).optional(),
24772
+ includeTypes: z43.array(
24773
+ z43.string().refine(
24774
+ (value) => SessionEventType.safeParse(value).success,
24775
+ "Unknown session event type"
24776
+ )
24777
+ ).max(100).describe(
24778
+ "Debug audit event types, e.g. turn.completed, user.message, agent.message.completed, agent.toolCall.output. Validated against the canonical event-type registry."
24779
+ ).optional(),
24780
+ excludeTypes: z43.array(
24781
+ z43.string().refine(
24782
+ (value) => SessionEventType.safeParse(value).success,
24783
+ "Unknown session event type"
24784
+ )
24785
+ ).max(100).describe(
24786
+ "Debug audit event types to exclude; validated against the canonical event-type registry."
24787
+ ).optional(),
24184
24788
  includeClasses: z43.array(z43.enum(SessionEventSemanticClass.options)).max(SessionEventSemanticClass.options.length).optional(),
24185
24789
  excludeClasses: z43.array(z43.enum(SessionEventSemanticClass.options)).max(SessionEventSemanticClass.options.length).optional(),
24186
24790
  latest: z43.enum(SessionEventLatestClass.options).optional()
@@ -24188,6 +24792,11 @@ function registerWorkspaceOrchestrationTools(server, deps, grant, can, callerSes
24188
24792
  },
24189
24793
  async ({
24190
24794
  sessionId,
24795
+ view,
24796
+ cursor,
24797
+ callId,
24798
+ includeArguments,
24799
+ includeOutput,
24191
24800
  after,
24192
24801
  before,
24193
24802
  limit,
@@ -24195,24 +24804,71 @@ function registerWorkspaceOrchestrationTools(server, deps, grant, can, callerSes
24195
24804
  mode: requestedMode,
24196
24805
  payloadMode: requestedPayloadMode,
24197
24806
  resultMode: requestedResultMode,
24198
- includeTypes,
24199
- excludeTypes,
24807
+ includeTypes: requestedIncludeTypes,
24808
+ excludeTypes: requestedExcludeTypes,
24200
24809
  includeClasses,
24201
24810
  excludeClasses,
24202
24811
  latest
24203
24812
  }) => {
24204
24813
  await authorizeFirstPartySession(deps, grant, sessionId, "session.events.read");
24814
+ const includeTypes = requestedIncludeTypes?.map((type) => SessionEventType.parse(type));
24815
+ const excludeTypes = requestedExcludeTypes?.map((type) => SessionEventType.parse(type));
24205
24816
  const latestClass = latest === void 0 ? void 0 : sessionEventLatestClassToSemanticClass(latest);
24206
24817
  if (requestedResultMode === "compact" && latestClass === void 0) {
24207
24818
  throw new Error("resultMode=compact requires latest");
24208
24819
  }
24209
24820
  await requireSession(deps.db, grant.workspaceId, sessionId);
24821
+ const auditRequested = [
24822
+ requestedMode,
24823
+ requestedPayloadMode,
24824
+ requestedResultMode,
24825
+ includeTypes,
24826
+ excludeTypes,
24827
+ includeClasses,
24828
+ excludeClasses,
24829
+ latest
24830
+ ].some((value) => value !== void 0);
24831
+ if ((view !== void 0 && view !== "debug" || cursor !== void 0) && auditRequested) {
24832
+ throw new Error(
24833
+ "Audit selectors require view=debug and cannot change a conversation cursor"
24834
+ );
24835
+ }
24836
+ if (view !== "debug" && !auditRequested) {
24837
+ const { readSessionEventView } = await import("./session-event-view-MU3HMNV3.js");
24838
+ const { listSessionEventSlices } = await import("@opengeni/db/session-event-slices");
24839
+ return json(
24840
+ await readSessionEventView(
24841
+ {
24842
+ sessionId,
24843
+ view,
24844
+ cursor,
24845
+ callId,
24846
+ includeArguments,
24847
+ includeOutput,
24848
+ after,
24849
+ before,
24850
+ direction: requestedDirection,
24851
+ limit
24852
+ },
24853
+ (options) => listSessionEventSlices(
24854
+ deps.db,
24855
+ grant.workspaceId,
24856
+ sessionId,
24857
+ options,
24858
+ (legacyOptions) => listSessionEventPage(deps.db, grant.workspaceId, sessionId, legacyOptions)
24859
+ )
24860
+ )
24861
+ );
24862
+ }
24863
+ if (cursor !== void 0 || callId !== void 0 || includeArguments !== void 0 || includeOutput !== void 0) {
24864
+ throw new Error("cursor/callId/includeArguments/includeOutput require a non-debug view");
24865
+ }
24210
24866
  if (latest && [includeTypes, excludeTypes, includeClasses, excludeClasses].some(
24211
24867
  (filter) => filter !== void 0
24212
24868
  )) {
24213
24869
  throw new Error("latest cannot be combined with event filters");
24214
24870
  }
24215
- const mode = requestedMode ?? (after !== void 0 ? "forensic" : "monitoring");
24871
+ const mode = requestedMode ?? "monitoring";
24216
24872
  const direction = latestClass ? "before" : requestedDirection ?? (before !== void 0 ? "before" : after !== void 0 ? "after" : "before");
24217
24873
  const payloadMode = requestedResultMode === "compact" ? "full" : requestedPayloadMode ?? (mode === "monitoring" ? "summary" : "full");
24218
24874
  const dbPage = await listSessionEventPage(deps.db, grant.workspaceId, sessionId, {
@@ -24262,7 +24918,7 @@ function registerWorkspaceOrchestrationTools(server, deps, grant, can, callerSes
24262
24918
  server.registerTool(
24263
24919
  "session_wait",
24264
24920
  {
24265
- description: `Block until a watched session has new durable events after your cursor, until your own session has pending machine input (a child result, an agent message, a steer, or a background-command result), or until maxWaitSeconds (default ${SESSION_WAIT_DEFAULT_SECONDS}, max ${SESSION_WAIT_MAX_SECONDS}) elapses. Use this for one short wait inside the current turn instead of sleeping and polling session_events/session_get/sessions_list while a child or peer session works. Do not immediately repeat it after timedOut=true unless new evidence suggests completion within one more window; for long or uncertain waits call wait_for_input once and end the turn. Pass each target's sessionId and afterSequence (its last seen sequence, 0 for a new session). waitFor=change is the default and returns on turn lifecycle, agent.message.completed, terminal background commands, blocking failures, goal facts, or session status/control changes. waitFor=completion is the child-result join: it ignores progress, completed commentary messages, goal facts, background commands, maintenance turns, and continuation segment settlements and returns only for a result-bearing final turn or a blocking state. A goal.completed event records goal state but is not a terminal child result. Raw deltas, tool receipts, sandbox diagnostics, and unrelated progress never wake either mode. Each changed target returns a bounded compact summary of up to ${SESSION_WAIT_EVENTS_PER_TARGET} exact durable events plus latestSequence (pass it back as the next afterSequence) and hasMore (drill down with session_events after=latestSequence). ownPendingUpdates > 0 means your own session has machine input that is delivered only when your next turn is claimed: finish this turn to receive it, or pass includeOwnPendingUpdates=false to keep waiting on the targets. timedOut=true means nothing changed; liveFanout=false means the live bus was unavailable and the wait relied on the deadline re-check. The whole result is byte-bounded: summaries are shortened first, then newest rows dropped, so a changed target may come back with events=[] and hasMore=true; read those rows with session_events after=latestSequence. The wait cannot exceed ${SESSION_WAIT_MAX_SECONDS} seconds because the MCP client request timeout is 60 seconds.`,
24921
+ description: `Wait once for durable session changes, your pending machine input, or maxWaitSeconds (default ${SESSION_WAIT_DEFAULT_SECONDS}, max ${SESSION_WAIT_MAX_SECONDS}). Pass targets with sessionId and afterSequence: the last consumed cursor, or 0. Never substitute session_get.lastSequence, which may include an unread completion. waitFor=change returns on turn lifecycle, completed messages, terminal commands, blockers, goal facts, or session control. waitFor=completion joins a child result: only a result-bearing final turn or blocker qualifies, not commentary, goal.completed, background commands, maintenance turns, or continuation segments. Neither mode wakes on raw deltas or tool receipts. Each target contains up to ${SESSION_WAIT_EVENTS_PER_TARGET} bounded summaries, latestSequence (the next afterSequence), and hasMore. For omitted rows use session_events view=results after=latestSequence for final outcomes, or view=debug with explicit filters for diagnostics; the default conversation view does not contain execution records. Byte limits can leave events=[] with hasMore=true. ownPendingUpdates > 0 means input will arrive when your next turn is claimed: finish this turn, or use includeOwnPendingUpdates=false to keep waiting. timedOut=true means no matching change; liveFanout=false means the deadline re-check supplied durable truth without the live bus. Do not immediately repeat a timeout without new evidence. For long or uncertain waits call wait_for_input once and end the turn.`,
24266
24922
  inputSchema: {
24267
24923
  targets: z43.array(
24268
24924
  z43.object({
@@ -24334,96 +24990,51 @@ function registerWorkspaceOrchestrationTools(server, deps, grant, can, callerSes
24334
24990
  }
24335
24991
  );
24336
24992
  if (callerSessionId !== null) {
24337
- server.registerTool(
24338
- "command_wait",
24339
- {
24340
- description: `Wait briefly for one background command owned by this session to settle, without provider-specific polling or write_stdin loops. The durable command row is authoritative. This returns immediately when the command is already terminal or otherwise waits up to maxWaitSeconds (default ${SESSION_WAIT_DEFAULT_SECONDS}, max ${SESSION_WAIT_MAX_SECONDS}) for its session.command.finished event or other pending input for this session. timedOut=true never cancels the command. Do not immediately repeat command_wait after a timeout unless new evidence suggests completion within one more window; for long or uncertain work call wait_for_input once and end the turn.`,
24341
- inputSchema: {
24342
- commandId: z43.string().uuid(),
24343
- maxWaitSeconds: z43.number().int().min(1).max(SESSION_WAIT_MAX_SECONDS).optional()
24344
- }
24345
- },
24346
- async ({ commandId, maxWaitSeconds }, extra) => {
24347
- await authorizeFirstPartySession(deps, grant, callerSessionId, "session.events.read");
24348
- const initialSession = await requireSession(deps.db, grant.workspaceId, callerSessionId);
24349
- let command = await getSessionBackgroundCommand(deps.db, {
24350
- accountId: grant.accountId,
24351
- workspaceId: grant.workspaceId,
24352
- sessionId: callerSessionId,
24353
- commandId
24354
- });
24355
- if (!command) throw new Error("Background command not found in this session");
24356
- const terminal2 = () => command?.state === "exited" || command?.state === "lost";
24357
- let waitResult = null;
24358
- if (!terminal2()) {
24359
- waitResult = await waitForSessionChanges({
24360
- targets: [{ sessionId: callerSessionId, afterSequence: initialSession.lastSequence }],
24361
- ownSessionId: callerSessionId,
24362
- maxWaitMs: (maxWaitSeconds ?? SESSION_WAIT_DEFAULT_SECONDS) * 1e3,
24363
- targetEventTypes: ["session.command.finished"],
24364
- targetEventMatches: (event) => {
24365
- const payload = event.payload;
24366
- return payload !== null && typeof payload === "object" && !Array.isArray(payload) && payload.commandId === commandId;
24367
- },
24368
- signal: extra?.signal,
24369
- source: {
24370
- readTargetEvents: async (target) => {
24371
- const page = await listSessionEventPage(
24372
- deps.db,
24373
- grant.workspaceId,
24374
- target.sessionId,
24375
- {
24376
- after: target.afterSequence,
24377
- direction: "after",
24378
- limit: SESSION_WAIT_EVENTS_PER_TARGET,
24379
- payloadMode: "full",
24380
- includeTypes: ["session.command.finished"],
24381
- maxBytes: SESSION_EVENT_MCP_MAX_BYTES * 4
24382
- }
24383
- );
24384
- return { events: page.events, hasMore: page.hasMore };
24385
- },
24386
- readOwnPendingUpdateKinds: async () => (await listOutstandingSessionSystemUpdates(
24387
- deps.db,
24388
- grant.workspaceId,
24389
- callerSessionId
24390
- )).map((update) => update.kind),
24391
- subscribe: (targetSessionId, onEvents) => deps.bus.subscribe(grant.workspaceId, targetSessionId, onEvents)
24392
- }
24393
- });
24394
- }
24395
- command = await getSessionBackgroundCommand(deps.db, {
24396
- accountId: grant.accountId,
24397
- workspaceId: grant.workspaceId,
24398
- sessionId: callerSessionId,
24399
- commandId
24400
- }) ?? command;
24401
- const isTerminal = terminal2();
24402
- const ownPendingUpdateKinds = (await listOutstandingSessionSystemUpdates(deps.db, grant.workspaceId, callerSessionId)).map((update) => update.kind);
24403
- const ownPendingUpdates = ownPendingUpdateKinds.length;
24404
- if (waitResult) {
24993
+ for (const toolName of ["command_read", "command_wait"]) {
24994
+ server.registerTool(
24995
+ toolName,
24996
+ {
24997
+ description: `${toolName === "command_read" ? "Read retained output immediately, or briefly wait" : "Briefly wait using the same read operation as command_read"} for one command owned by the current session, across sandbox and Connected Machine providers. Returns bounded stdout/stderr chunks, durable state and exitCode, nextCursor and hasMore, plus explicit retention limitations. Pass nextCursor to continue; output remains readable after the process exits. A terminal read observes completion and suppresses only its still-pending inbox notification; running reads and already claimed history are unchanged. waitSeconds is at most 50; command_wait defaults to 45, command_read to 0. For long waits use wait_for_input. Neither tool sends stdin or cancels the command.`,
24998
+ inputSchema: {
24999
+ commandId: z43.string().uuid(),
25000
+ cursor: z43.string().max(128).optional(),
25001
+ waitSeconds: z43.number().int().min(0).max(COMMAND_READ_MAX_WAIT_SECONDS).optional(),
25002
+ maxOutputBytes: z43.number().int().min(4).max(COMMAND_OUTPUT_MAX_BYTES2).optional()
25003
+ }
25004
+ },
25005
+ async ({ commandId, cursor, waitSeconds, maxOutputBytes }, extra) => {
24405
25006
  await authorizeFirstPartySession(deps, grant, callerSessionId, "session.events.read");
25007
+ let initialRead = true;
25008
+ return json(
25009
+ await readCommandWithWait({
25010
+ commandId,
25011
+ waitSeconds: waitSeconds ?? (toolName === "command_wait" ? COMMAND_WAIT_DEFAULT_SECONDS : 0),
25012
+ ...maxOutputBytes === void 0 ? {} : { maxOutputBytes },
25013
+ ...extra?.signal === void 0 ? {} : { signal: extra.signal },
25014
+ read: async () => {
25015
+ if (!initialRead)
25016
+ await authorizeFirstPartySession(
25017
+ deps,
25018
+ grant,
25019
+ callerSessionId,
25020
+ "session.events.read"
25021
+ );
25022
+ initialRead = false;
25023
+ return await readSessionBackgroundCommandOutput(deps.db, {
25024
+ accountId: grant.accountId,
25025
+ workspaceId: grant.workspaceId,
25026
+ sessionId: callerSessionId,
25027
+ commandId,
25028
+ ...cursor === void 0 ? {} : { cursor },
25029
+ ...maxOutputBytes === void 0 ? {} : { maxOutputBytes }
25030
+ });
25031
+ },
25032
+ subscribe: (onEvents) => deps.bus.subscribe(grant.workspaceId, callerSessionId, onEvents)
25033
+ })
25034
+ );
24406
25035
  }
24407
- const timedOut = (waitResult?.timedOut ?? false) && !isTerminal && ownPendingUpdates === 0;
24408
- return json({
24409
- command,
24410
- terminal: isTerminal,
24411
- waitedMs: waitResult?.waitedMs ?? 0,
24412
- timedOut,
24413
- aborted: waitResult?.aborted ?? false,
24414
- liveFanout: waitResult?.liveFanout ?? true,
24415
- ownPendingUpdates,
24416
- ownPendingUpdateKinds,
24417
- ...isTerminal ? {
24418
- outputLocator: {
24419
- eventType: "sandbox.command.output.delta",
24420
- commandId
24421
- }
24422
- } : {},
24423
- nextAction: isTerminal ? ownPendingUpdates > 0 ? "End this turn to receive the queued command result or other machine input." : "The command is terminal; inspect output events only if more detail is needed." : ownPendingUpdates > 0 ? "End this turn to receive pending machine input." : waitResult?.timedOut ? "Do not immediately repeat this short wait without new completion evidence; use wait_for_input for a long or uncertain wait." : "The wait was aborted; follow the newer session control or input."
24424
- });
24425
- }
24426
- );
25036
+ );
25037
+ }
24427
25038
  }
24428
25039
  }
24429
25040
  if (can("sessions:create") && sessionCreateVisible) {
@@ -25346,7 +25957,7 @@ function registerGitHubConnectTool(server, deps, grant, options, json) {
25346
25957
  const installations = await listWorkspaceGitHubInstallationBindings(deps, grant.workspaceId);
25347
25958
  const status = githubBindingStatus(true, installations);
25348
25959
  const baseUrl = githubBrowserBaseUrl(settings, options.requestOrigin);
25349
- const state = baseUrl && hasPermission10(grant.permissions, "github:manage") ? createSignedState6(deps.githubStateSecret, {
25960
+ const state = baseUrl && hasPermission11(grant.permissions, "github:manage") ? createSignedState6(deps.githubStateSecret, {
25350
25961
  accountId: grant.accountId,
25351
25962
  workspaceId: grant.workspaceId,
25352
25963
  intent: "installation_authority",
@@ -25372,10 +25983,10 @@ function registerGitHubConnectTool(server, deps, grant, options, json) {
25372
25983
  }
25373
25984
  function requireVariableSetsUseForMcpAttachment(grant, variableSetId) {
25374
25985
  if (variableSetId === void 0) return;
25375
- if (!hasPermission10(grant.permissions, "variable-sets:attach")) {
25986
+ if (!hasPermission11(grant.permissions, "variable-sets:attach")) {
25376
25987
  throw new Error("missing permission: variable-sets:attach");
25377
25988
  }
25378
- if (variableSetId !== null && !hasPermission10(grant.permissions, "variable-sets:use")) {
25989
+ if (variableSetId !== null && !hasPermission11(grant.permissions, "variable-sets:use")) {
25379
25990
  throw new Error("missing permission: variable-sets:use");
25380
25991
  }
25381
25992
  }
@@ -25383,10 +25994,10 @@ function requireVariableSetsUseForMcpAttachments(grant, selection) {
25383
25994
  const singular = selection.variableSetId ?? selection.environmentId;
25384
25995
  const variableSetIds = selection.variableSetIds ?? (singular ? [singular] : void 0);
25385
25996
  if (variableSetIds === void 0) return;
25386
- if (!hasPermission10(grant.permissions, "variable-sets:attach")) {
25997
+ if (!hasPermission11(grant.permissions, "variable-sets:attach")) {
25387
25998
  throw new HTTPException19(403, { message: "missing permission: variable-sets:attach" });
25388
25999
  }
25389
- if (variableSetIds.length > 0 && !hasPermission10(grant.permissions, "variable-sets:use")) {
26000
+ if (variableSetIds.length > 0 && !hasPermission11(grant.permissions, "variable-sets:use")) {
25390
26001
  throw new HTTPException19(403, { message: "missing permission: variable-sets:use" });
25391
26002
  }
25392
26003
  }
@@ -25432,6 +26043,7 @@ var SESSION_DISCOVERY_PREVIEW_OMISSION_REASON = "aggregatePreviewBudget";
25432
26043
  var SESSION_DISCOVERY_PAGE_MAX_BYTES = 128e3;
25433
26044
  var SESSION_DISCOVERY_PREVIEW_DRILL_DOWN_TOOL = "session_events";
25434
26045
  var SESSION_DISCOVERY_PREVIEW_DRILL_DOWN_BASE_INPUT = {
26046
+ view: "debug",
25435
26047
  direction: "before",
25436
26048
  limit: 1,
25437
26049
  mode: "monitoring",
@@ -25798,171 +26410,6 @@ async function withMcpEffectivePolicy(deps, workspaceId, subjectId, session) {
25798
26410
  return sessionWithEffectiveToolPolicy(session, workspaceServerIds, workspaceDefaultServerIds);
25799
26411
  }
25800
26412
 
25801
- // src/codemode.ts
25802
- import {
25803
- codemodeDispatchSubject,
25804
- decodeCodemodeDispatchAck,
25805
- encodeCodemodeDispatchRequest
25806
- } from "@opengeni/codemode";
25807
- import {
25808
- CODEMODE_DISPATCH_TIMEOUT_MS,
25809
- CodemodeCallRequest,
25810
- CodemodeCallSubmission
25811
- } from "@opengeni/contracts";
25812
- import { hasPermission as hasPermission11, requireSessionAuthorization as requireSessionAuthorization3 } from "@opengeni/core";
25813
- import {
25814
- getActiveSessionTurnForExecution,
25815
- getAttemptToolCatalog,
25816
- getCodemodeOperation,
25817
- submitCodemodeOperation
25818
- } from "@opengeni/db";
25819
- var CODEMODE_AUTHORITY_FAILURE_MESSAGES = {
25820
- invalid_grant: "Codemode bearer does not carry valid execution-attempt authority",
25821
- inactive_attempt: "Codemode execution attempt is no longer active",
25822
- catalog_mismatch: "Codemode tool catalog does not match the active execution attempt"
25823
- };
25824
- var CodemodeAuthorityError = class extends Error {
25825
- constructor(reason2) {
25826
- super(CODEMODE_AUTHORITY_FAILURE_MESSAGES[reason2]);
25827
- this.reason = reason2;
25828
- this.name = "CodemodeAuthorityError";
25829
- this.code = `codemode_${reason2}`;
25830
- }
25831
- code;
25832
- };
25833
- var CodemodeCatalogNotReadyError = class extends Error {
25834
- code = "codemode_catalog_not_ready";
25835
- constructor() {
25836
- super("Codemode tool catalog is not ready for the active execution attempt");
25837
- this.name = "CodemodeCatalogNotReadyError";
25838
- }
25839
- };
25840
- var CodemodeCatalogStaleError = class extends Error {
25841
- code = "codemode_catalog_stale";
25842
- constructor() {
25843
- super("Codemode tool catalog is stale for the active execution attempt");
25844
- this.name = "CodemodeCatalogStaleError";
25845
- }
25846
- };
25847
- function codemodeAuthorityForGrant(grant) {
25848
- const metadata = grant.metadata;
25849
- if (grant.principalKind !== "agent_attempt" || metadata?.delegated !== true || typeof metadata.sessionId !== "string" || typeof metadata.turnId !== "string" || typeof metadata.attemptId !== "string" || typeof metadata.executionGeneration !== "number" || !Number.isInteger(metadata.executionGeneration) || metadata.executionGeneration < 1) {
25850
- return null;
25851
- }
25852
- return {
25853
- accountId: grant.accountId,
25854
- workspaceId: grant.workspaceId,
25855
- sessionId: metadata.sessionId,
25856
- turnId: metadata.turnId,
25857
- attemptId: metadata.attemptId,
25858
- executionGeneration: metadata.executionGeneration,
25859
- subjectId: grant.subjectId
25860
- };
25861
- }
25862
- function isCodemodeGrant(grant) {
25863
- return hasPermission11(grant.permissions, "codemode:call") && codemodeAuthorityForGrant(grant) !== null;
25864
- }
25865
- function requireMatchingCodemodeCatalog(authority, catalog) {
25866
- if (!catalog) throw new CodemodeCatalogNotReadyError();
25867
- if (catalog.accountId !== authority.accountId || catalog.workspaceId !== authority.workspaceId || catalog.sessionId !== authority.sessionId || catalog.turnId !== authority.turnId || catalog.attemptId !== authority.attemptId || catalog.executionGeneration !== authority.executionGeneration) {
25868
- throw new CodemodeAuthorityError("catalog_mismatch");
25869
- }
25870
- return catalog;
25871
- }
25872
- async function requireActiveCodemodeCatalog(deps, grant) {
25873
- if (!isCodemodeGrant(grant)) throw new CodemodeAuthorityError("invalid_grant");
25874
- const authority = codemodeAuthorityForGrant(grant);
25875
- await requireSessionAuthorization3(deps, grant, {
25876
- sessionId: authority.sessionId,
25877
- operation: "session.codemode.call",
25878
- surface: "codemode"
25879
- });
25880
- const active = await getActiveSessionTurnForExecution(
25881
- deps.db,
25882
- authority.workspaceId,
25883
- authority.sessionId
25884
- );
25885
- if (!active || active.status !== "running" || active.id !== authority.turnId || active.activeAttemptId !== authority.attemptId || active.executionGeneration !== authority.executionGeneration) {
25886
- throw new CodemodeAuthorityError("inactive_attempt");
25887
- }
25888
- const catalog = requireMatchingCodemodeCatalog(
25889
- authority,
25890
- await getAttemptToolCatalog(deps.db, {
25891
- accountId: authority.accountId,
25892
- workspaceId: authority.workspaceId,
25893
- attemptId: authority.attemptId
25894
- })
25895
- );
25896
- return { authority, catalog };
25897
- }
25898
- async function submitAndDispatchCodemodeCall(deps, grant, rawRequest) {
25899
- const request = CodemodeCallRequest.parse(rawRequest);
25900
- const { authority, catalog } = await requireActiveCodemodeCatalog(deps, grant);
25901
- if (request.catalogDigest !== catalog.digest) throw new CodemodeCatalogStaleError();
25902
- const submitted = await submitCodemodeOperation(deps.db, {
25903
- ...authority,
25904
- call: {
25905
- ...request,
25906
- caller: { kind: "codemode", subjectId: authority.subjectId }
25907
- }
25908
- });
25909
- let operation = submitted.operation;
25910
- let dispatch = terminal(operation) ? "terminal" : operation.state === "running" ? "already_running" : "unavailable";
25911
- if (codemodeOperationNeedsDispatch(operation)) {
25912
- try {
25913
- const reply = await deps.bus.request(
25914
- codemodeDispatchSubject(authority.workspaceId, authority.attemptId),
25915
- encodeCodemodeDispatchRequest({
25916
- version: 1,
25917
- operationId: operation.operationId,
25918
- catalogDigest: catalog.digest
25919
- }),
25920
- { timeoutMs: CODEMODE_DISPATCH_TIMEOUT_MS }
25921
- );
25922
- dispatch = decodeCodemodeDispatchAck(reply.data).status;
25923
- } catch {
25924
- dispatch = "unavailable";
25925
- }
25926
- operation = await refreshAdmittedCodemodeOperation(
25927
- operation,
25928
- () => getCodemodeOperation(deps.db, {
25929
- accountId: authority.accountId,
25930
- workspaceId: authority.workspaceId,
25931
- attemptId: authority.attemptId,
25932
- operationId: operation.operationId
25933
- })
25934
- );
25935
- if (terminal(operation)) dispatch = "terminal";
25936
- else if (operation.state === "running" && dispatch === "unavailable") {
25937
- dispatch = "already_running";
25938
- }
25939
- }
25940
- return CodemodeCallSubmission.parse({ operation, dispatch });
25941
- }
25942
- function codemodeOperationNeedsDispatch(operation) {
25943
- return operation.state === "queued" || operation.state === "running";
25944
- }
25945
- async function readCodemodeOperation(deps, grant, operationId) {
25946
- if (!isCodemodeGrant(grant)) throw new CodemodeAuthorityError("invalid_grant");
25947
- const authority = codemodeAuthorityForGrant(grant);
25948
- return await getCodemodeOperation(deps.db, {
25949
- accountId: authority.accountId,
25950
- workspaceId: authority.workspaceId,
25951
- attemptId: authority.attemptId,
25952
- operationId
25953
- });
25954
- }
25955
- function terminal(operation) {
25956
- return ["completed", "failed", "outcome_unknown", "cancelled"].includes(operation.state);
25957
- }
25958
- async function refreshAdmittedCodemodeOperation(admitted, read) {
25959
- try {
25960
- return await read() ?? admitted;
25961
- } catch {
25962
- return admitted;
25963
- }
25964
- }
25965
-
25966
26413
  // src/app.ts
25967
26414
  import { boundedMcpRequest, McpPayloadTooLargeError } from "@opengeni/runtime/mcp-network";
25968
26415
  import {
@@ -25971,7 +26418,7 @@ import {
25971
26418
  } from "@opengeni/runtime/sandbox";
25972
26419
 
25973
26420
  // src/http/auth.ts
25974
- import { resolveFirstPartyDelegationSecret } from "@opengeni/config";
26421
+ import { resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret2 } from "@opengeni/config";
25975
26422
  import { verifyDelegatedAccessToken } from "@opengeni/contracts";
25976
26423
 
25977
26424
  // src/routes/install.ts
@@ -26255,7 +26702,7 @@ async function isAuthorized(c, settings) {
26255
26702
  if (expected && constantTimeEqual(bearer, expected)) {
26256
26703
  return true;
26257
26704
  }
26258
- const delegationSecret = resolveFirstPartyDelegationSecret(settings);
26705
+ const delegationSecret = resolveFirstPartyDelegationSecret2(settings);
26259
26706
  return Boolean(
26260
26707
  c.req.path.startsWith("/v1/") && bearer && delegationSecret && await verifyDelegatedAccessToken(delegationSecret, bearer)
26261
26708
  );
@@ -26318,7 +26765,7 @@ import {
26318
26765
  } from "@opengeni/core";
26319
26766
 
26320
26767
  // src/http/common.ts
26321
- import { getSession as getSession3 } from "@opengeni/db";
26768
+ import { getSession as getSession4 } from "@opengeni/db";
26322
26769
  import { HTTPException as HTTPException22 } from "hono/http-exception";
26323
26770
  function boundedLimit(raw) {
26324
26771
  const limit = Number(raw ?? 100);
@@ -26328,7 +26775,7 @@ function boundedLimit(raw) {
26328
26775
  return Math.min(500, Math.max(1, Math.floor(limit)));
26329
26776
  }
26330
26777
  async function assertSessionExists(db, workspaceId, sessionId) {
26331
- if (!await getSession3(db, workspaceId, sessionId)) {
26778
+ if (!await getSession4(db, workspaceId, sessionId)) {
26332
26779
  throw new HTTPException22(404, { message: "session not found" });
26333
26780
  }
26334
26781
  }
@@ -29152,7 +29599,7 @@ function registerSuperGrokRoutes(app, deps) {
29152
29599
  }
29153
29600
 
29154
29601
  // src/routes/connections.ts
29155
- import { createHash as createHash13, createHmac as createHmac4 } from "crypto";
29602
+ import { createHash as createHash12, createHmac as createHmac4 } from "crypto";
29156
29603
  import {
29157
29604
  WORKSPACE_OPENROUTER_CONNECTION_DOMAIN,
29158
29605
  WORKSPACE_OPENROUTER_CONNECTION_ROLE,
@@ -29250,7 +29697,7 @@ import { HTTPException as HTTPException29 } from "hono/http-exception";
29250
29697
 
29251
29698
  // src/integrations/provider-oauth.ts
29252
29699
  import { Buffer as Buffer4 } from "buffer";
29253
- import { createHash as createHash11, randomBytes as randomBytes6 } from "crypto";
29700
+ import { createHash as createHash10, randomBytes as randomBytes6 } from "crypto";
29254
29701
  import {
29255
29702
  integrationDefinitionProviderDomain as integrationDefinitionProviderDomain2,
29256
29703
  integrationDefinitionById
@@ -29367,7 +29814,7 @@ async function startApiIntegrationProviderOAuth(deps, input) {
29367
29814
  authorizationUrl.searchParams.set("code_challenge_method", "S256");
29368
29815
  authorizationUrl.searchParams.set(
29369
29816
  "code_challenge",
29370
- createHash11("sha256").update(verifier).digest("base64url")
29817
+ createHash10("sha256").update(verifier).digest("base64url")
29371
29818
  );
29372
29819
  authorizationUrl.searchParams.set("prompt", "select_account");
29373
29820
  if (definition.provider.id === "google") {
@@ -29597,7 +30044,7 @@ function providerClientKeys(definition) {
29597
30044
  ]);
29598
30045
  }
29599
30046
  function providerDefinitionFingerprint(definition) {
29600
- return createHash11("sha256").update(
30047
+ return createHash10("sha256").update(
29601
30048
  JSON.stringify({
29602
30049
  id: definition.id,
29603
30050
  provider: definition.provider,
@@ -29840,7 +30287,7 @@ function numberValue4(value) {
29840
30287
  }
29841
30288
 
29842
30289
  // src/integrations/personal-github.ts
29843
- import { createHash as createHash12, randomBytes as randomBytes7, randomUUID as randomUUID3 } from "crypto";
30290
+ import { createHash as createHash11, randomBytes as randomBytes7, randomUUID as randomUUID3 } from "crypto";
29844
30291
  import { personalGitHubOAuthCallbackUrl } from "@opengeni/config";
29845
30292
  import {
29846
30293
  PERSONAL_GITHUB_AUTHORIZATION_URL,
@@ -29886,7 +30333,7 @@ var PersonalGitHubCallbackError = class extends Error {
29886
30333
  }
29887
30334
  };
29888
30335
  function personalGitHubClientMarker(clientId) {
29889
- return createHash12("sha256").update(clientId).digest("hex").slice(0, 32);
30336
+ return createHash11("sha256").update(clientId).digest("hex").slice(0, 32);
29890
30337
  }
29891
30338
  function requirePersonalGitHubOAuthSettings(settings) {
29892
30339
  if (!settings.githubPersonalOauthEnabled) {
@@ -29989,7 +30436,7 @@ async function startPersonalGitHubOAuth(deps, input) {
29989
30436
  authorizationUrl.searchParams.set("state", state);
29990
30437
  authorizationUrl.searchParams.set(
29991
30438
  "code_challenge",
29992
- createHash12("sha256").update(verifier).digest("base64url")
30439
+ createHash11("sha256").update(verifier).digest("base64url")
29993
30440
  );
29994
30441
  authorizationUrl.searchParams.set("code_challenge_method", "S256");
29995
30442
  return PersonalGitHubOAuthStartResponse.parse({
@@ -30582,7 +31029,7 @@ function registerConnectionRoutes(app, deps) {
30582
31029
  accountId: state.accountId,
30583
31030
  workspaceId: state.workspaceId,
30584
31031
  subjectId: state.subjectId,
30585
- callbackDigest: createHash13("sha256").update(state.nonce).digest("hex"),
31032
+ callbackDigest: createHash12("sha256").update(state.nonce).digest("hex"),
30586
31033
  installMode: state.connectionId ? "reinstall" : "connect",
30587
31034
  ...failure
30588
31035
  });
@@ -33308,7 +33755,7 @@ import { buildFleetContextForSession as buildFleetContextForSession2, swapActive
33308
33755
 
33309
33756
  // src/sandbox/machines.ts
33310
33757
  import {
33311
- getSession as getSession4,
33758
+ getSession as getSession5,
33312
33759
  getLiveEnrollmentConnection as getLiveEnrollmentConnection3,
33313
33760
  listEnrollments as listEnrollments3,
33314
33761
  listSandboxes,
@@ -33441,7 +33888,7 @@ async function listMachines(services, input) {
33441
33888
  let activeEpoch = 0;
33442
33889
  let session = null;
33443
33890
  if (input.sessionId) {
33444
- session = await getSession4(db, workspaceId, input.sessionId);
33891
+ session = await getSession5(db, workspaceId, input.sessionId);
33445
33892
  if (session) {
33446
33893
  const pointer = await readActiveSandbox2(db, workspaceId, input.sessionId);
33447
33894
  activeSandboxId = pointer?.activeSandboxId ?? null;
@@ -35302,7 +35749,7 @@ function browserIdentityRouteError(error) {
35302
35749
  import { randomUUID as randomUUID5 } from "crypto";
35303
35750
  import {
35304
35751
  environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes4,
35305
- resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret2,
35752
+ resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret3,
35306
35753
  resolveStreamTokenSecret as resolveStreamTokenSecret2
35307
35754
  } from "@opengeni/config";
35308
35755
  import {
@@ -35396,7 +35843,7 @@ import {
35396
35843
  getExternalAuthPreparation,
35397
35844
  getProtectedAuthFillPreparation,
35398
35845
  getSiteAuthConnection,
35399
- getSession as getSession5,
35846
+ getSession as getSession6,
35400
35847
  InteractionResourceConflictError,
35401
35848
  InteractionResourceNotFoundError,
35402
35849
  InteractionResourceStateError,
@@ -35695,7 +36142,7 @@ async function withInteractionHolderHeartbeat(deps, input, run) {
35695
36142
  }
35696
36143
 
35697
36144
  // src/browser-state-authority.ts
35698
- import { createCipheriv, createDecipheriv, createHash as createHash14, createHmac as createHmac6, randomBytes as randomBytes9 } from "crypto";
36145
+ import { createCipheriv, createDecipheriv, createHash as createHash13, createHmac as createHmac6, randomBytes as randomBytes9 } from "crypto";
35699
36146
  import { BROWSER_PROFILE_ARTIFACT_FORMAT } from "@opengeni/contracts";
35700
36147
  var ROOT_KEY_BYTES = 32;
35701
36148
  var DATA_KEY_BYTES = 32;
@@ -35759,7 +36206,7 @@ function browserStateObjectKey(workspaceIdInput, operationIdInput) {
35759
36206
  return `workspaces/${workspaceId}/browser-state/revisions/${operationId}/chromium-profile.ogbs`;
35760
36207
  }
35761
36208
  function browserStateManifestDigest(manifest) {
35762
- return createHash14("sha256").update(canonicalJson2(manifest), "utf8").digest("hex");
36209
+ return createHash13("sha256").update(canonicalJson2(manifest), "utf8").digest("hex");
35763
36210
  }
35764
36211
  function operationScope(input) {
35765
36212
  const base = baseArtifactScope(input);
@@ -35772,8 +36219,8 @@ function operationScope(input) {
35772
36219
  function artifactScope(input) {
35773
36220
  return {
35774
36221
  ...baseArtifactScope(input),
35775
- artifactDigest: sha2562(input.artifactDigest, "browser state artifact digest"),
35776
- contentDigest: sha2562(input.contentDigest, "browser state content digest")
36222
+ artifactDigest: sha256(input.artifactDigest, "browser state artifact digest"),
36223
+ contentDigest: sha256(input.contentDigest, "browser state content digest")
35777
36224
  };
35778
36225
  }
35779
36226
  function baseArtifactScope(input) {
@@ -35817,7 +36264,7 @@ function uuid(value, label) {
35817
36264
  }
35818
36265
  return value.toLowerCase();
35819
36266
  }
35820
- function sha2562(value, label) {
36267
+ function sha256(value, label) {
35821
36268
  if (typeof value !== "string" || !SHA256_PATTERN.test(value)) {
35822
36269
  throw new Error(`${label} is invalid`);
35823
36270
  }
@@ -39798,7 +40245,7 @@ function interactionHolderId(browserSessionId) {
39798
40245
  return `browser-session:${browserSessionId}`;
39799
40246
  }
39800
40247
  function browserAuthorityRoot(deps) {
39801
- const root2 = resolveFirstPartyDelegationSecret2(deps.settings);
40248
+ const root2 = resolveFirstPartyDelegationSecret3(deps.settings);
39802
40249
  if (!root2) {
39803
40250
  throw new HTTPException39(503, {
39804
40251
  message: "browser controller authority is not configured"
@@ -39875,7 +40322,7 @@ async function settleBrowserRevisionCaptureFailure(deps, input, error) {
39875
40322
  });
39876
40323
  }
39877
40324
  async function requireSourceSession(deps, workspaceId, sessionId) {
39878
- const session = await getSession5(deps.db, workspaceId, sessionId);
40325
+ const session = await getSession6(deps.db, workspaceId, sessionId);
39879
40326
  if (!session) throw new BrowserSessionNotFoundError("Associated session not found");
39880
40327
  return session;
39881
40328
  }
@@ -40400,7 +40847,7 @@ function isAbort(error) {
40400
40847
 
40401
40848
  // src/routes/computer-sessions.ts
40402
40849
  import { randomUUID as randomUUID6 } from "crypto";
40403
- import { resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret3, resolveStreamTokenSecret as resolveStreamTokenSecret3 } from "@opengeni/config";
40850
+ import { resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret4, resolveStreamTokenSecret as resolveStreamTokenSecret3 } from "@opengeni/config";
40404
40851
  import {
40405
40852
  BROWSER_CONTROL_WEBSOCKET_BEARER_PREFIX as BROWSER_CONTROL_WEBSOCKET_BEARER_PREFIX2,
40406
40853
  BROWSER_CONTROL_PORT as BROWSER_CONTROL_PORT2,
@@ -40434,7 +40881,7 @@ import {
40434
40881
  getComputerSessionControlRecord as getComputerSessionControlRecord2,
40435
40882
  getLiveEnrollmentConnection as getLiveEnrollmentConnection6,
40436
40883
  getSandbox as getSandbox3,
40437
- getSession as getSession6,
40884
+ getSession as getSession7,
40438
40885
  listComputerSessions,
40439
40886
  prepareComputerSessionCreate,
40440
40887
  prepareComputerSessionEnd,
@@ -41777,7 +42224,7 @@ function interactionHolderId2(computerSessionId) {
41777
42224
  return `computer-session:${computerSessionId}`;
41778
42225
  }
41779
42226
  function controllerAuthorityRoot(deps) {
41780
- const root2 = resolveFirstPartyDelegationSecret3(deps.settings);
42227
+ const root2 = resolveFirstPartyDelegationSecret4(deps.settings);
41781
42228
  if (!root2) {
41782
42229
  throw new HTTPException40(503, {
41783
42230
  message: "interaction controller authority is not configured"
@@ -41786,7 +42233,7 @@ function controllerAuthorityRoot(deps) {
41786
42233
  return root2;
41787
42234
  }
41788
42235
  async function requireSourceSession2(deps, workspaceId, sessionId) {
41789
- const session = await getSession6(deps.db, workspaceId, sessionId);
42236
+ const session = await getSession7(deps.db, workspaceId, sessionId);
41790
42237
  if (!session) throw new ComputerSessionNotFoundError("Associated session not found");
41791
42238
  return session;
41792
42239
  }
@@ -41966,8 +42413,12 @@ function isAbort2(error) {
41966
42413
 
41967
42414
  // src/routes/github.ts
41968
42415
  import {
41969
- GitHubAppManifestCreate
42416
+ GitHubActionPoliciesResponse,
42417
+ GitHubActionPolicyActorState,
42418
+ GitHubAppManifestCreate,
42419
+ UpdateGitHubActionPolicyRequest
41970
42420
  } from "@opengeni/contracts";
42421
+ import { PersonalGitHubConnectionMetadata as PersonalGitHubConnectionMetadata2 } from "@opengeni/contracts/personal-github";
41971
42422
  import {
41972
42423
  ListGitHubRepositoryBranchesQuery,
41973
42424
  VerifyPublicGitHubRepositoryRefRequest
@@ -41976,7 +42427,8 @@ import { parseCanonicalGitHubRepositoryUrl } from "@opengeni/contracts/github-re
41976
42427
  import {
41977
42428
  bindAuthorizedGitHubInstallationRepositories,
41978
42429
  deleteGitHubInstallationBinding,
41979
- GitHubInstallationAuthorityCommitError
42430
+ GitHubInstallationAuthorityCommitError,
42431
+ listGitHubInstallationAccessForWorkspace as listGitHubInstallationAccessForWorkspace2
41980
42432
  } from "@opengeni/db";
41981
42433
  import {
41982
42434
  authorizeGitHubInstallationBinding,
@@ -42000,7 +42452,15 @@ import {
42000
42452
  } from "@opengeni/github";
42001
42453
  import { deleteCookie, setCookie } from "hono/cookie";
42002
42454
  import { HTTPException as HTTPException41 } from "hono/http-exception";
42003
- import { hasPermission as hasPermission16, requireAccessGrant as requireAccessGrant16 } from "@opengeni/core";
42455
+ import {
42456
+ githubAppActionPolicyActor,
42457
+ hasPermission as hasPermission16,
42458
+ listGitHubActionPolicyActors,
42459
+ personalGitHubActionPolicyActor,
42460
+ requireAccessGrant as requireAccessGrant16,
42461
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization8,
42462
+ updateGitHubActionPolicyGroup
42463
+ } from "@opengeni/core";
42004
42464
  var githubStateCookie = "opengeni_github_state";
42005
42465
  var githubBindingStateMaxAgeSeconds = 10 * 60;
42006
42466
  var legacyInstallationChooserDisabledMessage = "The legacy repository-admin GitHub installation chooser is disabled; use the GitHub owner-consent connect flow";
@@ -42041,6 +42501,89 @@ function registerGitHubRoutes(app, deps) {
42041
42501
  missing: setupMode === "operator" ? missing : []
42042
42502
  });
42043
42503
  });
42504
+ app.get("/v1/workspaces/:workspaceId/github/action-policies", async (c) => {
42505
+ const workspaceId = c.req.param("workspaceId");
42506
+ const grant = await requireAccessGrant16(c, deps, workspaceId, "github:use");
42507
+ const [installations, personalConnections] = await Promise.all([
42508
+ listGitHubInstallationAccessForWorkspace2(db, grant.workspaceId),
42509
+ listPersonalGitHubConnections(deps, {
42510
+ workspaceId: grant.workspaceId,
42511
+ subjectId: grant.subjectId
42512
+ })
42513
+ ]);
42514
+ const personalConnection = canonicalPersonalGitHubPolicyConnection(personalConnections);
42515
+ const actors = [
42516
+ ...installations.map(githubAppActionPolicyActor),
42517
+ ...personalConnection ? [
42518
+ personalGitHubActionPolicyActor({
42519
+ connectionId: personalConnection.id,
42520
+ githubLogin: PersonalGitHubConnectionMetadata2.parse(personalConnection.metadata).githubLogin
42521
+ })
42522
+ ] : []
42523
+ ];
42524
+ return c.json(
42525
+ GitHubActionPoliciesResponse.parse({
42526
+ enabled: settings.githubRestMcpEnabled,
42527
+ actors: await listGitHubActionPolicyActors(db, {
42528
+ accountId: grant.accountId,
42529
+ workspaceId: grant.workspaceId,
42530
+ actors
42531
+ })
42532
+ })
42533
+ );
42534
+ });
42535
+ app.patch("/v1/workspaces/:workspaceId/github/action-policies", async (c) => {
42536
+ const workspaceId = c.req.param("workspaceId");
42537
+ const payload = UpdateGitHubActionPolicyRequest.parse(await c.req.json());
42538
+ const actor = payload.actor;
42539
+ if (actor.kind === "workspace_app") {
42540
+ const grant = await requireAccessGrant16(c, deps, workspaceId, "github:manage");
42541
+ const installations = await listGitHubInstallationAccessForWorkspace2(db, grant.workspaceId);
42542
+ const installation = installations.find(
42543
+ (candidate) => candidate.installationId === actor.installationId
42544
+ );
42545
+ if (!installation || installation.accountId !== grant.accountId) {
42546
+ throw new HTTPException41(404, { message: "GitHub installation not found" });
42547
+ }
42548
+ return c.json(
42549
+ GitHubActionPolicyActorState.parse(
42550
+ await updateGitHubActionPolicyGroup(db, {
42551
+ accountId: grant.accountId,
42552
+ workspaceId: grant.workspaceId,
42553
+ subjectId: grant.subjectId,
42554
+ actor: githubAppActionPolicyActor(installation),
42555
+ group: payload.group,
42556
+ decision: payload.decision
42557
+ })
42558
+ )
42559
+ );
42560
+ }
42561
+ const access = await requireAccessGrantAuthorization8(c, deps, workspaceId, "connections:write");
42562
+ assertPersonalConnectionOwnerPrincipal(access, "My GitHub action policy");
42563
+ const connections = await listPersonalGitHubConnections(deps, {
42564
+ workspaceId: access.grant.workspaceId,
42565
+ subjectId: access.grant.subjectId
42566
+ });
42567
+ const connection = connections.find((candidate) => candidate.id === actor.connectionId);
42568
+ if (!connection) {
42569
+ throw new HTTPException41(404, { message: "personal GitHub connection not found" });
42570
+ }
42571
+ return c.json(
42572
+ GitHubActionPolicyActorState.parse(
42573
+ await updateGitHubActionPolicyGroup(db, {
42574
+ accountId: access.grant.accountId,
42575
+ workspaceId: access.grant.workspaceId,
42576
+ subjectId: access.grant.subjectId,
42577
+ actor: personalGitHubActionPolicyActor({
42578
+ connectionId: connection.id,
42579
+ githubLogin: PersonalGitHubConnectionMetadata2.parse(connection.metadata).githubLogin
42580
+ }),
42581
+ group: payload.group,
42582
+ decision: payload.decision
42583
+ })
42584
+ )
42585
+ );
42586
+ });
42044
42587
  app.get("/v1/workspaces/:workspaceId/github/connect", async (c) => {
42045
42588
  const workspaceId = c.req.param("workspaceId");
42046
42589
  const state = c.req.query("state");
@@ -42540,6 +43083,20 @@ function githubInstallationSettingsUrl(installation) {
42540
43083
  }
42541
43084
  return new URL(`https://github.com/settings/installations/${installation.installationId}`);
42542
43085
  }
43086
+ function canonicalPersonalGitHubPolicyConnection(connections) {
43087
+ const statusRank = {
43088
+ active: 0,
43089
+ needs_reauth: 1,
43090
+ error: 2,
43091
+ revoked: 3
43092
+ };
43093
+ return [...connections].sort((left, right) => {
43094
+ const rank = (statusRank[left.status] ?? 4) - (statusRank[right.status] ?? 4);
43095
+ if (rank !== 0) return rank;
43096
+ const updated = right.updatedAt.localeCompare(left.updatedAt);
43097
+ return updated !== 0 ? updated : right.id.localeCompare(left.id);
43098
+ })[0] ?? null;
43099
+ }
42543
43100
  function redirectToExactGitHubAuthorization(c, deps, sourceState, installationId) {
42544
43101
  const payload = readSignedState11(sourceState, deps.githubStateSecret);
42545
43102
  const clientId = deps.settings.githubClientId?.trim();
@@ -42764,7 +43321,7 @@ import {
42764
43321
  } from "@opengeni/contracts/github-repository-contracts";
42765
43322
  import {
42766
43323
  requireAccessGrant as requireAccessGrant17,
42767
- requireAccessGrantAuthorization as requireAccessGrantAuthorization8
43324
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization9
42768
43325
  } from "@opengeni/core";
42769
43326
  import {
42770
43327
  getPersonalGitHubRepositorySelectionState as getPersonalGitHubRepositorySelectionState2,
@@ -42887,12 +43444,12 @@ async function listLivePersonalGitHubRepositoryBranches(deps, input, services =
42887
43444
  null,
42888
43445
  services.getSelectionState
42889
43446
  );
42890
- const [owner, repositoryName] = current.repository.fullName.split("/");
42891
- if (!owner || !repositoryName) {
43447
+ const [owner2, repositoryName] = current.repository.fullName.split("/");
43448
+ if (!owner2 || !repositoryName) {
42892
43449
  throw new PersonalGitHubRepositoryProviderError("invalid_provider_response");
42893
43450
  }
42894
43451
  const url = new URL(
42895
- `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repositoryName)}/branches`,
43452
+ `/repos/${encodeURIComponent(owner2)}/${encodeURIComponent(repositoryName)}/branches`,
42896
43453
  PERSONAL_GITHUB_API_ORIGIN
42897
43454
  );
42898
43455
  url.searchParams.set("page", String(input.query.cursor));
@@ -42951,12 +43508,12 @@ async function verifyLivePersonalGitHubRepositories(deps, input) {
42951
43508
  const connection = await requirePersonalGitHubRepositoryConnection(deps, input);
42952
43509
  const verified = [];
42953
43510
  for (const selection of input.repositories) {
42954
- const [owner, repositoryName] = selection.fullName.split("/");
42955
- if (!owner || !repositoryName) {
43511
+ const [owner2, repositoryName] = selection.fullName.split("/");
43512
+ if (!owner2 || !repositoryName) {
42956
43513
  throw new PersonalGitHubRepositoryProviderError("invalid_provider_response");
42957
43514
  }
42958
43515
  const url = new URL(
42959
- `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repositoryName)}`,
43516
+ `/repos/${encodeURIComponent(owner2)}/${encodeURIComponent(repositoryName)}`,
42960
43517
  PERSONAL_GITHUB_API_ORIGIN
42961
43518
  );
42962
43519
  const payload = await personalGitHubProviderJson(
@@ -43265,7 +43822,7 @@ function registerPersonalGitHubRoutes(app, deps) {
43265
43822
  });
43266
43823
  app.post("/v1/workspaces/:workspaceId/connections/github/oauth/start", async (c) => {
43267
43824
  const workspaceId = c.req.param("workspaceId");
43268
- const access = await requireAccessGrantAuthorization8(c, deps, workspaceId, "connections:write");
43825
+ const access = await requireAccessGrantAuthorization9(c, deps, workspaceId, "connections:write");
43269
43826
  assertPersonalConnectionOwnerPrincipal(access, "My GitHub account");
43270
43827
  const payload = PersonalGitHubOAuthStartRequest.parse(await c.req.json().catch(() => ({})));
43271
43828
  return c.json(
@@ -43279,7 +43836,7 @@ function registerPersonalGitHubRoutes(app, deps) {
43279
43836
  });
43280
43837
  app.post("/v1/workspaces/:workspaceId/connections/:connectionId/github/reconnect", async (c) => {
43281
43838
  const workspaceId = c.req.param("workspaceId");
43282
- const access = await requireAccessGrantAuthorization8(c, deps, workspaceId, "connections:write");
43839
+ const access = await requireAccessGrantAuthorization9(c, deps, workspaceId, "connections:write");
43283
43840
  assertPersonalConnectionOwnerPrincipal(access, "My GitHub account");
43284
43841
  const payload = PersonalGitHubOAuthStartRequest.omit({ connectionId: true }).parse(
43285
43842
  await c.req.json().catch(() => ({}))
@@ -43298,7 +43855,7 @@ function registerPersonalGitHubRoutes(app, deps) {
43298
43855
  async (c) => {
43299
43856
  const workspaceId = c.req.param("workspaceId");
43300
43857
  const connectionId = c.req.param("connectionId");
43301
- const access = await requireAccessGrantAuthorization8(
43858
+ const access = await requireAccessGrantAuthorization9(
43302
43859
  c,
43303
43860
  deps,
43304
43861
  workspaceId,
@@ -43349,7 +43906,7 @@ function registerPersonalGitHubRoutes(app, deps) {
43349
43906
  async (c) => {
43350
43907
  const workspaceId = c.req.param("workspaceId");
43351
43908
  const connectionId = c.req.param("connectionId");
43352
- const access = await requireAccessGrantAuthorization8(
43909
+ const access = await requireAccessGrantAuthorization9(
43353
43910
  c,
43354
43911
  deps,
43355
43912
  workspaceId,
@@ -43396,7 +43953,7 @@ function registerPersonalGitHubRoutes(app, deps) {
43396
43953
  async (c) => {
43397
43954
  const workspaceId = c.req.param("workspaceId");
43398
43955
  const connectionId = c.req.param("connectionId");
43399
- const access = await requireAccessGrantAuthorization8(
43956
+ const access = await requireAccessGrantAuthorization9(
43400
43957
  c,
43401
43958
  deps,
43402
43959
  workspaceId,
@@ -43428,7 +43985,7 @@ function registerPersonalGitHubRoutes(app, deps) {
43428
43985
  async (c) => {
43429
43986
  const workspaceId = c.req.param("workspaceId");
43430
43987
  const connectionId = c.req.param("connectionId");
43431
- const access = await requireAccessGrantAuthorization8(
43988
+ const access = await requireAccessGrantAuthorization9(
43432
43989
  c,
43433
43990
  deps,
43434
43991
  workspaceId,
@@ -43540,7 +44097,7 @@ function canonicalPersonalGitHubConnection(connections) {
43540
44097
  import {
43541
44098
  PERSONAL_GITHUB_API_ORIGIN as PERSONAL_GITHUB_API_ORIGIN2,
43542
44099
  PERSONAL_GITHUB_PROVIDER_DOMAIN as PERSONAL_GITHUB_PROVIDER_DOMAIN4,
43543
- PersonalGitHubConnectionMetadata as PersonalGitHubConnectionMetadata2,
44100
+ PersonalGitHubConnectionMetadata as PersonalGitHubConnectionMetadata3,
43544
44101
  isPersonalGitHubConnection as isPersonalGitHubConnection4
43545
44102
  } from "@opengeni/contracts/personal-github";
43546
44103
  import {
@@ -43837,7 +44394,7 @@ async function assertCurrentConnectionAndSelection(deps, claims, repository) {
43837
44394
  if (!connection || connection.accountId !== claims.accountId || connection.workspaceId !== claims.originWorkspaceId || connection.subjectId !== claims.ownerSubjectId || connection.status !== "active" || connection.grantedScopes.length !== 1 || connection.grantedScopes[0] !== "repo" || !isPersonalGitHubConnection4(connection)) {
43838
44395
  throw new Error("personal GitHub connection changed");
43839
44396
  }
43840
- const metadata = PersonalGitHubConnectionMetadata2.parse(connection.metadata);
44397
+ const metadata = PersonalGitHubConnectionMetadata3.parse(connection.metadata);
43841
44398
  if (metadata.credentialBindingId !== claims.credentialBindingId) {
43842
44399
  throw new Error("personal GitHub credential binding changed");
43843
44400
  }
@@ -44092,7 +44649,7 @@ import {
44092
44649
  import { HTTPException as HTTPException44 } from "hono/http-exception";
44093
44650
 
44094
44651
  // src/integrations/api-integrations.ts
44095
- import { createHash as createHash15 } from "crypto";
44652
+ import { createHash as createHash14 } from "crypto";
44096
44653
  import {
44097
44654
  compileGraphqlRevision,
44098
44655
  compileOpenApiRevision,
@@ -44302,7 +44859,7 @@ function resolvedPreview(input) {
44302
44859
  };
44303
44860
  }
44304
44861
  function integrationInstallationIdentity(protocol, label, locator) {
44305
- const hash = createHash15("sha256").update(`${protocol}\0${locator}`).digest("hex").slice(0, 12);
44862
+ const hash = createHash14("sha256").update(`${protocol}\0${locator}`).digest("hex").slice(0, 12);
44306
44863
  const slug = label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "integration";
44307
44864
  return {
44308
44865
  sourceDerivedDefinitionId: `${slug}-${hash}`,
@@ -44668,7 +45225,7 @@ import {
44668
45225
  import {
44669
45226
  hasPermission as hasPermission17,
44670
45227
  requireAccessGrant as requireAccessGrant19,
44671
- requireAccessGrantAuthorization as requireAccessGrantAuthorization9
45228
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization10
44672
45229
  } from "@opengeni/core";
44673
45230
  import {
44674
45231
  configureIntegrationFacet as configureIntegrationFacet2,
@@ -44713,7 +45270,7 @@ function registerIntegrationFacetRoutes(app, deps) {
44713
45270
  "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/facets/:facetKey/source",
44714
45271
  async (c) => {
44715
45272
  const workspaceId = c.req.param("workspaceId");
44716
- const authorization = await requireAccessGrantAuthorization9(
45273
+ const authorization = await requireAccessGrantAuthorization10(
44717
45274
  c,
44718
45275
  deps,
44719
45276
  workspaceId,
@@ -44966,7 +45523,7 @@ import {
44966
45523
  requireFreshAccessGrant,
44967
45524
  requirePermission as requirePermission3,
44968
45525
  requireSessionAuthorization as requireSessionAuthorization6,
44969
- workflowIdForSession
45526
+ workflowIdForSession as workflowIdForSession2
44970
45527
  } from "@opengeni/core";
44971
45528
  import { publishDurableSessionEvents as publishDurableSessionEvents2 } from "@opengeni/events";
44972
45529
  import { HTTPException as HTTPException46 } from "hono/http-exception";
@@ -46334,7 +46891,7 @@ function registerInteractionResourceRoutes(app, deps) {
46334
46891
  workspaceId,
46335
46892
  sessionId: approvalTarget.sessionId,
46336
46893
  eventId: accepted.event.id,
46337
- workflowId: workflowIdForSession(approvalTarget.sessionId),
46894
+ workflowId: workflowIdForSession2(approvalTarget.sessionId),
46338
46895
  workflowWakeRevision: accepted.workflowWakeRevision
46339
46896
  });
46340
46897
  const response = InteractionInterventionMutationResponse.parse({
@@ -48458,7 +49015,7 @@ function positiveInteger3(value) {
48458
49015
  }
48459
49016
 
48460
49017
  // src/routes/packs.ts
48461
- import { createHash as createHash16 } from "crypto";
49018
+ import { createHash as createHash15 } from "crypto";
48462
49019
  import {
48463
49020
  EnablePackRequest,
48464
49021
  InstallPackRequest,
@@ -48644,7 +49201,7 @@ function registerPackRoutes(app, deps) {
48644
49201
  packVersion: pack.version,
48645
49202
  ...preview.variableSetId ? { variableSetId: preview.variableSetId } : {}
48646
49203
  };
48647
- const requestDigest = sha2563(
49204
+ const requestDigest = sha2562(
48648
49205
  stableJson4({
48649
49206
  packId: pack.id,
48650
49207
  manifestDigest: preview.manifestDigest,
@@ -48823,7 +49380,7 @@ function registerPackRoutes(app, deps) {
48823
49380
  const grant = await requireAccessGrant24(c, deps, workspaceId, "capabilities:manage");
48824
49381
  const pack = await requirePack(db, workspaceId, c.req.param("packId"));
48825
49382
  const payload = UninstallPackRequest.parse(await c.req.json());
48826
- const requestDigest = sha2563(
49383
+ const requestDigest = sha2562(
48827
49384
  stableJson4({
48828
49385
  packId: pack.id,
48829
49386
  expectedInstallationVersion: payload.expectedInstallationVersion
@@ -49067,8 +49624,8 @@ function packFailureCode(error) {
49067
49624
  if (error instanceof CapabilityComponentVersionConflictError) return "component_version_conflict";
49068
49625
  return "component_install_failed";
49069
49626
  }
49070
- function sha2563(value) {
49071
- return createHash16("sha256").update(value).digest("hex");
49627
+ function sha2562(value) {
49628
+ return createHash15("sha256").update(value).digest("hex");
49072
49629
  }
49073
49630
  async function requirePack(db, workspaceId, packId) {
49074
49631
  const pack = await resolveCapabilityPack(db, workspaceId, packId);
@@ -49112,7 +49669,7 @@ async function validateDocumentBaseIds(db, workspaceId, documentBaseIds) {
49112
49669
  }
49113
49670
 
49114
49671
  // src/routes/plugins.ts
49115
- import { createHash as createHash17 } from "crypto";
49672
+ import { createHash as createHash16 } from "crypto";
49116
49673
  import {
49117
49674
  createPinnedIntegrationTransport as createPinnedIntegrationTransport2,
49118
49675
  fetchIntegrationSourceDocument as fetchIntegrationSourceDocument2
@@ -49165,10 +49722,10 @@ var githubMetadataMaxBytes = 4 * 1024 * 1024;
49165
49722
  var githubBlobResponseMaxBytes = 512 * 1024;
49166
49723
  function createGitHubSkillSourceClient(settings, requestJson = (path, maxBytes, label) => githubJson(settings, path, maxBytes, label)) {
49167
49724
  return {
49168
- resolveCommit: async (owner, repository, ref) => {
49725
+ resolveCommit: async (owner2, repository, ref) => {
49169
49726
  const payload = recordValue(
49170
49727
  await requestJson(
49171
- `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/commits/${encodeURIComponent(ref)}`,
49728
+ `/repos/${encodeURIComponent(owner2)}/${encodeURIComponent(repository)}/commits/${encodeURIComponent(ref)}`,
49172
49729
  githubMetadataMaxBytes,
49173
49730
  "GitHub Skill commit"
49174
49731
  ),
@@ -49178,10 +49735,10 @@ function createGitHubSkillSourceClient(settings, requestJson = (path, maxBytes,
49178
49735
  if (!sha) throw new Error("GitHub commit response omitted sha");
49179
49736
  return sha.toLowerCase();
49180
49737
  },
49181
- listTree: async (owner, repository, commit) => {
49738
+ listTree: async (owner2, repository, commit) => {
49182
49739
  const payload = recordValue(
49183
49740
  await requestJson(
49184
- `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/git/trees/${encodeURIComponent(commit)}?recursive=1`,
49741
+ `/repos/${encodeURIComponent(owner2)}/${encodeURIComponent(repository)}/git/trees/${encodeURIComponent(commit)}?recursive=1`,
49185
49742
  githubMetadataMaxBytes,
49186
49743
  "GitHub Skill tree"
49187
49744
  ),
@@ -49204,10 +49761,10 @@ function createGitHubSkillSourceClient(settings, requestJson = (path, maxBytes,
49204
49761
  return { path, type, mode, sha, size: typeof size === "number" ? size : null };
49205
49762
  });
49206
49763
  },
49207
- readBlob: async (owner, repository, sha) => {
49764
+ readBlob: async (owner2, repository, sha) => {
49208
49765
  const payload = recordValue(
49209
49766
  await requestJson(
49210
- `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/git/blobs/${encodeURIComponent(sha)}`,
49767
+ `/repos/${encodeURIComponent(owner2)}/${encodeURIComponent(repository)}/git/blobs/${encodeURIComponent(sha)}`,
49211
49768
  githubBlobResponseMaxBytes,
49212
49769
  "GitHub Skill file"
49213
49770
  ),
@@ -49333,7 +49890,7 @@ function registerPluginRoutes(app, deps, overrides = {}) {
49333
49890
  message: "A Plugin component changed after preview. Review the updated bill of materials."
49334
49891
  });
49335
49892
  }
49336
- const requestDigest = sha2564(
49893
+ const requestDigest = sha2563(
49337
49894
  stableJson5({
49338
49895
  url: payload.url,
49339
49896
  expectedManifestDigest: payload.expectedManifestDigest,
@@ -49471,13 +50028,13 @@ async function resolvePluginPackage(input) {
49471
50028
  message: error instanceof Error ? error.message : "Plugin manifest is invalid"
49472
50029
  });
49473
50030
  }
49474
- const manifestDigest = sha2564(stableJson5(manifest));
50031
+ const manifestDigest = sha2563(stableJson5(manifest));
49475
50032
  const components = [];
49476
50033
  for (const component of manifest.components) {
49477
50034
  if (component.kind === "skill") {
49478
50035
  const resolved = await resolveSkillImport(component.url, input.github);
49479
50036
  const capabilityId = portableSkillCapabilityId(resolved.preview);
49480
- const digest3 = sha2564(
50037
+ const digest3 = sha2563(
49481
50038
  stableJson5({
49482
50039
  capabilityId,
49483
50040
  sourceCommit: resolved.preview.sourceCommit,
@@ -49627,7 +50184,7 @@ async function resolvePluginPackage(input) {
49627
50184
  continue;
49628
50185
  }
49629
50186
  const server = configuredMcpServer(input.deps.settings.mcpServers, component.serverId);
49630
- const digest2 = sha2564(stableJson5(safeMcpFacts(server)));
50187
+ const digest2 = sha2563(stableJson5(safeMcpFacts(server)));
49631
50188
  components.push({
49632
50189
  preview: {
49633
50190
  key: component.key,
@@ -49776,7 +50333,7 @@ function safeMcpFacts(server) {
49776
50333
  };
49777
50334
  }
49778
50335
  function integrationDigest(resolved) {
49779
- return sha2564(
50336
+ return sha2563(
49780
50337
  stableJson5({
49781
50338
  capabilityId: resolved.preview.capabilityId,
49782
50339
  revisionId: resolved.preview.revisionId,
@@ -49842,8 +50399,8 @@ function pluginFailureCode(error) {
49842
50399
  if (error instanceof HTTPException51) return `http_${error.status}`;
49843
50400
  return "component_install_failed";
49844
50401
  }
49845
- function sha2564(value) {
49846
- return createHash17("sha256").update(value).digest("hex");
50402
+ function sha2563(value) {
50403
+ return createHash16("sha256").update(value).digest("hex");
49847
50404
  }
49848
50405
 
49849
50406
  // src/routes/skills.ts
@@ -49873,7 +50430,7 @@ import {
49873
50430
  uninstallPortableSkill
49874
50431
  } from "@opengeni/db";
49875
50432
  import { loadSkillLibrarySkill, skillLibraryRepositoryUrl } from "@opengeni/runtime/skill-library";
49876
- import { createHash as createHash18 } from "crypto";
50433
+ import { createHash as createHash17 } from "crypto";
49877
50434
  import { HTTPException as HTTPException52 } from "hono/http-exception";
49878
50435
  function registerSkillRoutes(app, deps, overrides = {}) {
49879
50436
  const github = overrides.github ?? createGitHubSkillSourceClient(deps.settings);
@@ -49908,7 +50465,7 @@ function registerSkillRoutes(app, deps, overrides = {}) {
49908
50465
  path: file.path,
49909
50466
  content: file.content,
49910
50467
  byteSize: bytes.byteLength,
49911
- contentSha256: createHash18("sha256").update(bytes).digest("hex")
50468
+ contentSha256: createHash17("sha256").update(bytes).digest("hex")
49912
50469
  };
49913
50470
  });
49914
50471
  try {
@@ -50178,7 +50735,7 @@ import {
50178
50735
  import { HTTPException as HTTPException54 } from "hono/http-exception";
50179
50736
  import {
50180
50737
  requireAccessGrant as requireAccessGrant28,
50181
- requireAccessGrantAuthorization as requireAccessGrantAuthorization10,
50738
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization11,
50182
50739
  requirePermission as requirePermission7
50183
50740
  } from "@opengeni/core";
50184
50741
  import {
@@ -50219,7 +50776,7 @@ async function parseRigRequest(c, schema, label) {
50219
50776
  function registerRigRoutes(app, deps) {
50220
50777
  const { db, workflowClient } = deps;
50221
50778
  async function requireRigMutation(c, workspaceId, permission) {
50222
- const authorization = await requireAccessGrantAuthorization10(c, deps, workspaceId, permission);
50779
+ const authorization = await requireAccessGrantAuthorization11(c, deps, workspaceId, permission);
50223
50780
  const rigId = c.req.param("rigId");
50224
50781
  if (!rigId) {
50225
50782
  throw new HTTPException54(400, { message: "rig id is required" });
@@ -50297,7 +50854,7 @@ function registerRigRoutes(app, deps) {
50297
50854
  });
50298
50855
  app.post("/v1/workspaces/:workspaceId/rigs", async (c) => {
50299
50856
  const workspaceId = c.req.param("workspaceId");
50300
- const authorization = await requireAccessGrantAuthorization10(c, deps, workspaceId);
50857
+ const authorization = await requireAccessGrantAuthorization11(c, deps, workspaceId);
50301
50858
  const grant = authorization.grant;
50302
50859
  requirePermission7(grant, "rigs:manage");
50303
50860
  const payload = await parseRigRequest(c, CreateRigRequest, "Rig create request");
@@ -50323,7 +50880,7 @@ function registerRigRoutes(app, deps) {
50323
50880
  });
50324
50881
  app.patch("/v1/workspaces/:workspaceId/rigs/:rigId", async (c) => {
50325
50882
  const workspaceId = c.req.param("workspaceId");
50326
- const authorization = await requireAccessGrantAuthorization10(c, deps, workspaceId);
50883
+ const authorization = await requireAccessGrantAuthorization11(c, deps, workspaceId);
50327
50884
  const grant = authorization.grant;
50328
50885
  requirePermission7(grant, "rigs:manage");
50329
50886
  const rig = await requireRigForApi2(db, grant, c.req.param("rigId"));
@@ -50338,7 +50895,7 @@ function registerRigRoutes(app, deps) {
50338
50895
  });
50339
50896
  app.delete("/v1/workspaces/:workspaceId/rigs/:rigId", async (c) => {
50340
50897
  const workspaceId = c.req.param("workspaceId");
50341
- const authorization = await requireAccessGrantAuthorization10(c, deps, workspaceId);
50898
+ const authorization = await requireAccessGrantAuthorization11(c, deps, workspaceId);
50342
50899
  const grant = authorization.grant;
50343
50900
  requirePermission7(grant, "rigs:manage");
50344
50901
  const rig = await requireRigForApi2(db, grant, c.req.param("rigId"));
@@ -50439,6 +50996,7 @@ function registerRigRoutes(app, deps) {
50439
50996
  }
50440
50997
 
50441
50998
  // src/routes/scheduled-tasks.ts
50999
+ import { z as z14 } from "zod";
50442
51000
  import {
50443
51001
  CreateScheduledTaskRequest as CreateScheduledTaskRequest2,
50444
51002
  TriggerScheduledTaskRequest,
@@ -50504,7 +51062,24 @@ function registerScheduledTaskRoutes(app, deps) {
50504
51062
  app.get("/v1/workspaces/:workspaceId/scheduled-tasks", async (c) => {
50505
51063
  const workspaceId = c.req.param("workspaceId");
50506
51064
  const grant = await requireAccessGrant29(c, deps, workspaceId, "scheduled_tasks:run");
50507
- const tasks = await listScheduledTasks2(db, workspaceId, boundedLimit(c.req.query("limit")));
51065
+ const sessionId = c.req.query("sessionId");
51066
+ const offset = Number(c.req.query("offset") ?? 0);
51067
+ if (!Number.isSafeInteger(offset) || offset < 0) {
51068
+ throw new HTTPException55(400, { message: "invalid offset" });
51069
+ }
51070
+ if (sessionId !== void 0) {
51071
+ if (!z14.string().uuid().safeParse(sessionId).success) {
51072
+ throw new HTTPException55(400, { message: "invalid sessionId" });
51073
+ }
51074
+ await requireAccessGrant29(c, deps, workspaceId, "sessions:control");
51075
+ }
51076
+ const tasks = await listScheduledTasks2(
51077
+ db,
51078
+ workspaceId,
51079
+ boundedLimit(c.req.query("limit")),
51080
+ offset,
51081
+ sessionId
51082
+ );
50508
51083
  return c.json(tasks.map((task) => scheduledTaskForGrant2(task, grant)));
50509
51084
  });
50510
51085
  app.get("/v1/workspaces/:workspaceId/scheduled-tasks/:taskId", async (c) => {
@@ -50671,6 +51246,7 @@ function registerScheduledTaskRoutes(app, deps) {
50671
51246
  }
50672
51247
 
50673
51248
  // src/routes/sessions.ts
51249
+ import { scheduledSessionIds } from "@opengeni/db";
50674
51250
  import {
50675
51251
  AcknowledgeStreamRequest,
50676
51252
  ApplySessionGoalRevisionRequest,
@@ -50764,7 +51340,7 @@ import {
50764
51340
  getLiveEnrollmentConnection as getLiveEnrollmentConnection7,
50765
51341
  getRetainedProcess,
50766
51342
  getSandbox as getSandbox4,
50767
- getSession as getSession7,
51343
+ getSession as getSession8,
50768
51344
  readActiveSandbox as readActiveSandbox3,
50769
51345
  deleteSessionTreeIfQuiescent,
50770
51346
  getSessionEvent,
@@ -51586,7 +52162,7 @@ function normalizeExpiry(value) {
51586
52162
  }
51587
52163
 
51588
52164
  // src/routes/sessions.ts
51589
- import { z as z14, ZodError } from "zod";
52165
+ import { z as z15, ZodError } from "zod";
51590
52166
  import {
51591
52167
  NatsControlRpc as NatsControlRpc6,
51592
52168
  negotiateCapabilities,
@@ -51600,7 +52176,7 @@ import {
51600
52176
  getManagedAuthRequestActorEpoch as getManagedAuthRequestActorEpoch2,
51601
52177
  hasPermission as hasPermission19,
51602
52178
  requireAccessGrant as requireAccessGrant30,
51603
- requireAccessGrantAuthorization as requireAccessGrantAuthorization11,
52179
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization12,
51604
52180
  requireFreshAccessGrant as requireFreshAccessGrant2,
51605
52181
  requirePermission as requirePermission8,
51606
52182
  requireSessionAuthorization as requireSessionAuthorization7,
@@ -51635,7 +52211,7 @@ import {
51635
52211
  updateManagedHumanSessionVisibility,
51636
52212
  updateSessionToolPolicy,
51637
52213
  updateSessionTitle as updateSessionTitle2,
51638
- workflowIdForSession as workflowIdForSession2,
52214
+ workflowIdForSession as workflowIdForSession3,
51639
52215
  sessionWithEffectiveToolPolicy as sessionWithEffectiveToolPolicy2,
51640
52216
  workspaceSessionToolPolicyDefaultServerIds as workspaceSessionToolPolicyDefaultServerIds2,
51641
52217
  workspaceSessionToolPolicyServerIds as workspaceSessionToolPolicyServerIds2,
@@ -51899,6 +52475,7 @@ function registerSessionRoutes(app, deps) {
51899
52475
  db,
51900
52476
  settings,
51901
52477
  bus,
52478
+ objectStorage,
51902
52479
  observability: deps.observability
51903
52480
  };
51904
52481
  const workspaceCaptureManifestCache = new WorkspaceCaptureManifestCache();
@@ -51986,7 +52563,7 @@ function registerSessionRoutes(app, deps) {
51986
52563
  const authorizeSessionHttp = async (c, next) => {
51987
52564
  const workspaceId = c.req.param("workspaceId") ?? "";
51988
52565
  const sessionId = c.req.param("sessionId") ?? "";
51989
- if (!z14.string().uuid().safeParse(sessionId).success) {
52566
+ if (!z15.string().uuid().safeParse(sessionId).success) {
51990
52567
  throw new HTTPException57(404, { message: "session not found" });
51991
52568
  }
51992
52569
  const operation = sessionAuthorizationOperationForHttp(
@@ -52027,11 +52604,12 @@ function registerSessionRoutes(app, deps) {
52027
52604
  db,
52028
52605
  settings,
52029
52606
  bus,
52607
+ objectStorage,
52030
52608
  ...deps.establishSandboxSession ? { establishSandboxSession: deps.establishSandboxSession } : {}
52031
52609
  };
52032
52610
  app.get("/v1/workspaces/:workspaceId/session-tenancy/capabilities", async (c) => {
52033
52611
  const workspaceId = c.req.param("workspaceId");
52034
- const authorization = await requireAccessGrantAuthorization11(
52612
+ const authorization = await requireAccessGrantAuthorization12(
52035
52613
  c,
52036
52614
  deps,
52037
52615
  workspaceId,
@@ -52041,7 +52619,7 @@ function registerSessionRoutes(app, deps) {
52041
52619
  });
52042
52620
  app.post("/v1/workspaces/:workspaceId/sessions", async (c) => {
52043
52621
  const workspaceId = c.req.param("workspaceId");
52044
- const authorization = await requireAccessGrantAuthorization11(
52622
+ const authorization = await requireAccessGrantAuthorization12(
52045
52623
  c,
52046
52624
  deps,
52047
52625
  workspaceId,
@@ -52082,7 +52660,7 @@ function registerSessionRoutes(app, deps) {
52082
52660
  });
52083
52661
  app.put("/v1/workspaces/:workspaceId/new-session-draft", async (c) => {
52084
52662
  const workspaceId = c.req.param("workspaceId");
52085
- const authorization = await requireAccessGrantAuthorization11(
52663
+ const authorization = await requireAccessGrantAuthorization12(
52086
52664
  c,
52087
52665
  deps,
52088
52666
  workspaceId,
@@ -52141,7 +52719,7 @@ function registerSessionRoutes(app, deps) {
52141
52719
  });
52142
52720
  app.get("/v1/workspaces/:workspaceId/sessions", async (c) => {
52143
52721
  const workspaceId = c.req.param("workspaceId");
52144
- const authorization = await requireAccessGrantAuthorization11(
52722
+ const authorization = await requireAccessGrantAuthorization12(
52145
52723
  c,
52146
52724
  deps,
52147
52725
  workspaceId,
@@ -52167,6 +52745,12 @@ function registerSessionRoutes(app, deps) {
52167
52745
  ...query.pinsOnly ? { pinsOnly: true } : {},
52168
52746
  ...query.archivedOnly ? { archivedOnly: true } : {},
52169
52747
  ...query.parentSessionId !== void 0 ? { parentSessionId: query.parentSessionId } : {},
52748
+ ...query.channelId !== void 0 ? { channelId: query.channelId } : {},
52749
+ ...query.createdBy ? { createdBy: query.createdBy } : {},
52750
+ ...query.updatedFrom ? { updatedFrom: query.updatedFrom } : {},
52751
+ ...query.updatedBefore ? { updatedBefore: query.updatedBefore } : {},
52752
+ ...query.createdFrom ? { createdFrom: query.createdFrom } : {},
52753
+ ...query.createdBefore ? { createdBefore: query.createdBefore } : {},
52170
52754
  ...authorizationScope ? { authorizationScope } : {},
52171
52755
  // A managed human's own personal workspace has no membership row, so
52172
52756
  // the list's removal fence must fall back to the organization-membership
@@ -52192,10 +52776,19 @@ function registerSessionRoutes(app, deps) {
52192
52776
  workspaceId,
52193
52777
  sessionIds: [...page.pinned, ...page.sessions].map((session) => session.id)
52194
52778
  });
52779
+ const scheduleTargets = hasPermission19(grant.permissions, "scheduled_tasks:run") && hasPermission19(grant.permissions, "sessions:control") ? await scheduledSessionIds(
52780
+ db,
52781
+ workspaceId,
52782
+ [...page.pinned, ...page.sessions].map((session) => session.id)
52783
+ ) : /* @__PURE__ */ new Set();
52195
52784
  const decorate = (session) => {
52196
52785
  const activity = commandActivity.get(session.id);
52197
52786
  return sessionWithEffectiveToolPolicy2(
52198
- { ...session, ...activity ? { backgroundCommandActivity: activity } : {} },
52787
+ {
52788
+ ...session,
52789
+ hasSchedules: scheduleTargets.has(session.id),
52790
+ ...activity ? { backgroundCommandActivity: activity } : {}
52791
+ },
52199
52792
  policy.workspaceServerIds,
52200
52793
  policy.workspaceDefaultServerIds
52201
52794
  );
@@ -52203,6 +52796,7 @@ function registerSessionRoutes(app, deps) {
52203
52796
  if (pageView) {
52204
52797
  return c.json({
52205
52798
  ...page,
52799
+ ...query.hasPageFilters ? { filtersApplied: true } : {},
52206
52800
  pinned: page.pinned.map(decorate),
52207
52801
  sessions: page.sessions.map(decorate)
52208
52802
  });
@@ -52345,7 +52939,7 @@ function registerSessionRoutes(app, deps) {
52345
52939
  const workspaceId = c.req.param("workspaceId");
52346
52940
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:read");
52347
52941
  const sessionId = c.req.param("sessionId");
52348
- if (!z14.string().uuid().safeParse(sessionId).success) {
52942
+ if (!z15.string().uuid().safeParse(sessionId).success) {
52349
52943
  throw new HTTPException57(404, { message: "session not found" });
52350
52944
  }
52351
52945
  const session = await getSessionForSubject(
@@ -52358,13 +52952,25 @@ function registerSessionRoutes(app, deps) {
52358
52952
  if (!session) {
52359
52953
  throw new HTTPException57(404, { message: "session not found" });
52360
52954
  }
52361
- return c.json(await withEffectivePolicy(deps, workspaceId, grant.subjectId, session));
52955
+ const activity = await backgroundCommandActivityForSessions(db, {
52956
+ accountId: grant.accountId,
52957
+ workspaceId,
52958
+ sessionIds: [sessionId]
52959
+ });
52960
+ const scheduleTargets = hasPermission19(grant.permissions, "scheduled_tasks:run") && hasPermission19(grant.permissions, "sessions:control") ? await scheduledSessionIds(db, workspaceId, [sessionId]) : /* @__PURE__ */ new Set();
52961
+ return c.json(
52962
+ await withEffectivePolicy(deps, workspaceId, grant.subjectId, {
52963
+ ...session,
52964
+ hasSchedules: scheduleTargets.has(sessionId),
52965
+ ...activity.get(sessionId) ? { backgroundCommandActivity: activity.get(sessionId) } : {}
52966
+ })
52967
+ );
52362
52968
  });
52363
52969
  app.get("/v1/workspaces/:workspaceId/sessions/:sessionId/model-context", async (c) => {
52364
52970
  const workspaceId = c.req.param("workspaceId");
52365
52971
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:read");
52366
52972
  const sessionId = c.req.param("sessionId");
52367
- if (!z14.string().uuid().safeParse(sessionId).success) {
52973
+ if (!z15.string().uuid().safeParse(sessionId).success) {
52368
52974
  throw new HTTPException57(404, { message: "session not found" });
52369
52975
  }
52370
52976
  const session = await getSessionForSubject(
@@ -52392,7 +52998,8 @@ function registerSessionRoutes(app, deps) {
52392
52998
  const commands = await listSessionBackgroundCommands(db, {
52393
52999
  accountId: grant.accountId,
52394
53000
  workspaceId,
52395
- sessionId
53001
+ sessionId,
53002
+ activeOnly: true
52396
53003
  });
52397
53004
  return c.json({ commands });
52398
53005
  });
@@ -52402,7 +53009,7 @@ function registerSessionRoutes(app, deps) {
52402
53009
  const workspaceId = c.req.param("workspaceId");
52403
53010
  const sessionId = c.req.param("sessionId");
52404
53011
  const commandId = c.req.param("commandId");
52405
- if (!z14.string().uuid().safeParse(commandId).success) {
53012
+ if (!z15.string().uuid().safeParse(commandId).success) {
52406
53013
  throw new HTTPException57(404, { message: "background command not found" });
52407
53014
  }
52408
53015
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
@@ -52422,7 +53029,7 @@ function registerSessionRoutes(app, deps) {
52422
53029
  app.put("/v1/workspaces/:workspaceId/sessions/:sessionId/visibility", async (c) => {
52423
53030
  const workspaceId = c.req.param("workspaceId");
52424
53031
  const sessionId = c.req.param("sessionId");
52425
- const authorization = await requireAccessGrantAuthorization11(c, deps, workspaceId);
53032
+ const authorization = await requireAccessGrantAuthorization12(c, deps, workspaceId);
52426
53033
  const parsed = UpdateSessionVisibilityRequest.safeParse(await c.req.json().catch(() => null));
52427
53034
  if (!parsed.success) {
52428
53035
  throw new ApiHttpError(422, {
@@ -52450,7 +53057,7 @@ function registerSessionRoutes(app, deps) {
52450
53057
  app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/forks", async (c) => {
52451
53058
  const workspaceId = c.req.param("workspaceId");
52452
53059
  const sessionId = c.req.param("sessionId");
52453
- const authorization = await requireAccessGrantAuthorization11(c, deps, workspaceId);
53060
+ const authorization = await requireAccessGrantAuthorization12(c, deps, workspaceId);
52454
53061
  const parsed = ForkSessionRequest.safeParse(await c.req.json().catch(() => null));
52455
53062
  if (!parsed.success) {
52456
53063
  throw new ApiHttpError(422, {
@@ -52482,7 +53089,7 @@ function registerSessionRoutes(app, deps) {
52482
53089
  accountId,
52483
53090
  workspaceId,
52484
53091
  sessionId,
52485
- workflowId: workflowIdForSession2(sessionId),
53092
+ workflowId: workflowIdForSession3(sessionId),
52486
53093
  wakeRevision: result.workflowWakeRevision
52487
53094
  });
52488
53095
  }
@@ -52491,7 +53098,7 @@ function registerSessionRoutes(app, deps) {
52491
53098
  const workspaceId = c.req.param("workspaceId");
52492
53099
  const sessionId = c.req.param("sessionId");
52493
53100
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
52494
- if (!z14.string().uuid().safeParse(sessionId).success) {
53101
+ if (!z15.string().uuid().safeParse(sessionId).success) {
52495
53102
  throw new HTTPException57(400, { message: "invalid session id" });
52496
53103
  }
52497
53104
  const parsed = BeginSessionRealtimeRequest.safeParse(await c.req.json().catch(() => null));
@@ -52526,7 +53133,7 @@ function registerSessionRoutes(app, deps) {
52526
53133
  const sessionId = c.req.param("sessionId");
52527
53134
  const realtimeId = c.req.param("realtimeId");
52528
53135
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
52529
- if (!z14.string().uuid().safeParse(sessionId).success || !z14.string().uuid().safeParse(realtimeId).success) {
53136
+ if (!z15.string().uuid().safeParse(sessionId).success || !z15.string().uuid().safeParse(realtimeId).success) {
52530
53137
  throw new HTTPException57(400, {
52531
53138
  message: "invalid realtime lifecycle id"
52532
53139
  });
@@ -52562,7 +53169,7 @@ function registerSessionRoutes(app, deps) {
52562
53169
  const sessionId = c.req.param("sessionId");
52563
53170
  const realtimeId = c.req.param("realtimeId");
52564
53171
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
52565
- if (!z14.string().uuid().safeParse(sessionId).success || !z14.string().uuid().safeParse(realtimeId).success) {
53172
+ if (!z15.string().uuid().safeParse(sessionId).success || !z15.string().uuid().safeParse(realtimeId).success) {
52566
53173
  throw new HTTPException57(400, {
52567
53174
  message: "invalid realtime lifecycle id"
52568
53175
  });
@@ -52596,7 +53203,7 @@ function registerSessionRoutes(app, deps) {
52596
53203
  const workspaceId = c.req.param("workspaceId");
52597
53204
  const sessionId = c.req.param("sessionId");
52598
53205
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
52599
- if (!z14.string().uuid().safeParse(sessionId).success) {
53206
+ if (!z15.string().uuid().safeParse(sessionId).success) {
52600
53207
  throw new HTTPException57(404, { message: "session not found" });
52601
53208
  }
52602
53209
  const parsed = CodexRealtimeWebrtcRequest.safeParse(await c.req.json().catch(() => null));
@@ -52775,7 +53382,7 @@ function registerSessionRoutes(app, deps) {
52775
53382
  const workspaceId = c.req.param("workspaceId");
52776
53383
  const sessionId = c.req.param("sessionId");
52777
53384
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
52778
- if (!z14.string().uuid().safeParse(sessionId).success) {
53385
+ if (!z15.string().uuid().safeParse(sessionId).success) {
52779
53386
  throw new HTTPException57(404, { message: "session not found" });
52780
53387
  }
52781
53388
  const parsed = GatewayRealtimeConnectRequest.safeParse(await c.req.json().catch(() => null));
@@ -52894,7 +53501,7 @@ function registerSessionRoutes(app, deps) {
52894
53501
  const workspaceId = c.req.param("workspaceId");
52895
53502
  const sessionId = c.req.param("sessionId");
52896
53503
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
52897
- if (!z14.string().uuid().safeParse(sessionId).success) {
53504
+ if (!z15.string().uuid().safeParse(sessionId).success) {
52898
53505
  throw new HTTPException57(404, { message: "session not found" });
52899
53506
  }
52900
53507
  const parsed = GatewayRealtimeConnectRequest.safeParse(await c.req.json().catch(() => null));
@@ -53017,7 +53624,7 @@ function registerSessionRoutes(app, deps) {
53017
53624
  const realtimeId = c.req.param("realtimeId");
53018
53625
  const connectionId = c.req.param("connectionId");
53019
53626
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
53020
- if (!z14.string().uuid().safeParse(sessionId).success || !z14.string().uuid().safeParse(realtimeId).success || !z14.string().uuid().safeParse(connectionId).success) {
53627
+ if (!z15.string().uuid().safeParse(sessionId).success || !z15.string().uuid().safeParse(realtimeId).success || !z15.string().uuid().safeParse(connectionId).success) {
53021
53628
  throw new HTTPException57(400, {
53022
53629
  message: "invalid realtime connection id"
53023
53630
  });
@@ -53059,7 +53666,7 @@ function registerSessionRoutes(app, deps) {
53059
53666
  const sessionId = c.req.param("sessionId");
53060
53667
  const realtimeId = c.req.param("realtimeId");
53061
53668
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
53062
- if (!z14.string().uuid().safeParse(sessionId).success || !z14.string().uuid().safeParse(realtimeId).success) {
53669
+ if (!z15.string().uuid().safeParse(sessionId).success || !z15.string().uuid().safeParse(realtimeId).success) {
53063
53670
  throw new HTTPException57(400, { message: "invalid realtime ledger id" });
53064
53671
  }
53065
53672
  const parsed = SyncSessionRealtimeLedgerRequest.safeParse(
@@ -53093,7 +53700,7 @@ function registerSessionRoutes(app, deps) {
53093
53700
  );
53094
53701
  app.put("/v1/workspaces/:workspaceId/sessions/:sessionId/pin", async (c) => {
53095
53702
  const workspaceId = c.req.param("workspaceId");
53096
- const authorization = await requireAccessGrantAuthorization11(
53703
+ const authorization = await requireAccessGrantAuthorization12(
53097
53704
  c,
53098
53705
  deps,
53099
53706
  workspaceId,
@@ -53101,7 +53708,7 @@ function registerSessionRoutes(app, deps) {
53101
53708
  );
53102
53709
  const grant = authorization.grant;
53103
53710
  const sessionId = c.req.param("sessionId");
53104
- if (!z14.string().uuid().safeParse(sessionId).success) {
53711
+ if (!z15.string().uuid().safeParse(sessionId).success) {
53105
53712
  throw new HTTPException57(404, { message: "session not found" });
53106
53713
  }
53107
53714
  const parsed = UpdateSessionPinRequest.safeParse(await c.req.json().catch(() => null));
@@ -53146,7 +53753,7 @@ function registerSessionRoutes(app, deps) {
53146
53753
  });
53147
53754
  app.put("/v1/workspaces/:workspaceId/sessions/:sessionId/attention", async (c) => {
53148
53755
  const workspaceId = c.req.param("workspaceId");
53149
- const authorization = await requireAccessGrantAuthorization11(
53756
+ const authorization = await requireAccessGrantAuthorization12(
53150
53757
  c,
53151
53758
  deps,
53152
53759
  workspaceId,
@@ -53154,7 +53761,7 @@ function registerSessionRoutes(app, deps) {
53154
53761
  );
53155
53762
  const grant = authorization.grant;
53156
53763
  const sessionId = c.req.param("sessionId");
53157
- if (!z14.string().uuid().safeParse(sessionId).success) {
53764
+ if (!z15.string().uuid().safeParse(sessionId).success) {
53158
53765
  throw new HTTPException57(404, { message: "session not found" });
53159
53766
  }
53160
53767
  const parsed = UpdateSessionAttentionRequest.safeParse(await c.req.json().catch(() => null));
@@ -53196,7 +53803,7 @@ function registerSessionRoutes(app, deps) {
53196
53803
  });
53197
53804
  app.put("/v1/workspaces/:workspaceId/sessions/:sessionId/archive", async (c) => {
53198
53805
  const workspaceId = c.req.param("workspaceId");
53199
- const authorization = await requireAccessGrantAuthorization11(
53806
+ const authorization = await requireAccessGrantAuthorization12(
53200
53807
  c,
53201
53808
  deps,
53202
53809
  workspaceId,
@@ -53242,17 +53849,35 @@ function registerSessionRoutes(app, deps) {
53242
53849
  const workspaceId = c.req.param("workspaceId");
53243
53850
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:read");
53244
53851
  const lineage = await readSessionLineage(deps, grant, c.req.param("sessionId"));
53852
+ const sessionIds = [
53853
+ c.req.param("sessionId"),
53854
+ ...lineage.ancestors.map((session) => session.id)
53855
+ ];
53856
+ const collect = (nodes) => {
53857
+ for (const node of nodes) {
53858
+ sessionIds.push(node.session.id);
53859
+ collect(node.children);
53860
+ }
53861
+ };
53862
+ collect(lineage.children);
53863
+ const targets = hasPermission19(grant.permissions, "scheduled_tasks:run") && hasPermission19(grant.permissions, "sessions:control") ? await scheduledSessionIds(db, workspaceId, sessionIds) : /* @__PURE__ */ new Set();
53864
+ const decorateNodes = (nodes) => nodes.map((node) => ({
53865
+ ...node,
53866
+ session: { ...node.session, hasSchedules: targets.has(node.session.id) },
53867
+ children: decorateNodes(node.children)
53868
+ }));
53245
53869
  const policy = await loadEffectivePolicyContext(deps, workspaceId, grant.subjectId);
53246
53870
  return c.json({
53247
53871
  ...lineage,
53872
+ sessionHasSchedules: targets.has(c.req.param("sessionId")),
53248
53873
  ancestors: lineage.ancestors.map(
53249
53874
  (session) => sessionWithEffectiveToolPolicy2(
53250
- session,
53875
+ { ...session, hasSchedules: targets.has(session.id) },
53251
53876
  policy.workspaceServerIds,
53252
53877
  policy.workspaceDefaultServerIds
53253
53878
  )
53254
53879
  ),
53255
- children: mapLineageNodes(lineage.children, policy)
53880
+ children: decorateNodes(mapLineageNodes(lineage.children, policy))
53256
53881
  });
53257
53882
  });
53258
53883
  app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/codex-account", async (c) => {
@@ -53620,7 +54245,7 @@ function registerSessionRoutes(app, deps) {
53620
54245
  accountId: grant.accountId,
53621
54246
  workspaceId,
53622
54247
  sessionId,
53623
- workflowId: workflowIdForSession2(sessionId),
54248
+ workflowId: workflowIdForSession3(sessionId),
53624
54249
  wakeRevision: workflowWakeRevision
53625
54250
  });
53626
54251
  }
@@ -53703,7 +54328,7 @@ function registerSessionRoutes(app, deps) {
53703
54328
  accountId: grant.accountId,
53704
54329
  workspaceId,
53705
54330
  sessionId,
53706
- workflowId: workflowIdForSession2(sessionId),
54331
+ workflowId: workflowIdForSession3(sessionId),
53707
54332
  wakeRevision: workflowWakeRevision
53708
54333
  });
53709
54334
  }
@@ -53751,7 +54376,7 @@ function registerSessionRoutes(app, deps) {
53751
54376
  accountId: grant.accountId,
53752
54377
  workspaceId,
53753
54378
  sessionId,
53754
- workflowId: workflowIdForSession2(sessionId),
54379
+ workflowId: workflowIdForSession3(sessionId),
53755
54380
  wakeRevision: workflowWakeRevision2
53756
54381
  });
53757
54382
  }
@@ -53811,7 +54436,7 @@ function registerSessionRoutes(app, deps) {
53811
54436
  accountId: grant.accountId,
53812
54437
  workspaceId,
53813
54438
  sessionId,
53814
- workflowId: workflowIdForSession2(sessionId),
54439
+ workflowId: workflowIdForSession3(sessionId),
53815
54440
  wakeRevision: workflowWakeRevision
53816
54441
  });
53817
54442
  }
@@ -54275,7 +54900,7 @@ function registerSessionRoutes(app, deps) {
54275
54900
  });
54276
54901
  app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/steer", async (c) => {
54277
54902
  const workspaceId = c.req.param("workspaceId");
54278
- const authorization = await requireAccessGrantAuthorization11(
54903
+ const authorization = await requireAccessGrantAuthorization12(
54279
54904
  c,
54280
54905
  deps,
54281
54906
  workspaceId,
@@ -54312,7 +54937,7 @@ function registerSessionRoutes(app, deps) {
54312
54937
  });
54313
54938
  app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/composer-draft/submit", async (c) => {
54314
54939
  const workspaceId = c.req.param("workspaceId");
54315
- const authorization = await requireAccessGrantAuthorization11(
54940
+ const authorization = await requireAccessGrantAuthorization12(
54316
54941
  c,
54317
54942
  deps,
54318
54943
  workspaceId,
@@ -54334,7 +54959,7 @@ function registerSessionRoutes(app, deps) {
54334
54959
  });
54335
54960
  app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/events", async (c) => {
54336
54961
  const workspaceId = c.req.param("workspaceId");
54337
- const authorization = await requireAccessGrantAuthorization11(
54962
+ const authorization = await requireAccessGrantAuthorization12(
54338
54963
  c,
54339
54964
  deps,
54340
54965
  workspaceId,
@@ -54395,7 +55020,7 @@ function registerSessionRoutes(app, deps) {
54395
55020
  });
54396
55021
  }
54397
55022
  await publishDurableSessionEvents3(bus, workspaceId, sessionId, accepted.events);
54398
- const workflowId = workflowIdForSession2(sessionId);
55023
+ const workflowId = workflowIdForSession3(sessionId);
54399
55024
  await workflowClient.signalApprovalDecision({
54400
55025
  accountId: grant.accountId,
54401
55026
  workspaceId,
@@ -54439,7 +55064,7 @@ function registerSessionRoutes(app, deps) {
54439
55064
  workspaceId,
54440
55065
  sessionId,
54441
55066
  eventId: accepted.events[0]?.id ?? event.payload.requestId,
54442
- workflowId: workflowIdForSession2(sessionId),
55067
+ workflowId: workflowIdForSession3(sessionId),
54443
55068
  workflowWakeRevision: accepted.workflowWakeRevision
54444
55069
  });
54445
55070
  }
@@ -54504,7 +55129,7 @@ function registerSessionRoutes(app, deps) {
54504
55129
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:read");
54505
55130
  assertOwnershipEnabled();
54506
55131
  const sessionId = c.req.param("sessionId");
54507
- const session = await getSession7(db, workspaceId, sessionId);
55132
+ const session = await getSession8(db, workspaceId, sessionId);
54508
55133
  if (!session) {
54509
55134
  throw new HTTPException57(404, { message: "session not found" });
54510
55135
  }
@@ -54637,7 +55262,7 @@ function registerSessionRoutes(app, deps) {
54637
55262
  const grant = await requireAccessGrant30(c, deps, workspaceId, "stream:acknowledge");
54638
55263
  assertOwnershipEnabled();
54639
55264
  const sessionId = c.req.param("sessionId");
54640
- const session = await getSession7(db, workspaceId, sessionId);
55265
+ const session = await getSession8(db, workspaceId, sessionId);
54641
55266
  if (!session) {
54642
55267
  throw new HTTPException57(404, { message: "session not found" });
54643
55268
  }
@@ -54666,7 +55291,7 @@ function registerSessionRoutes(app, deps) {
54666
55291
  const grant = await requireAccessGrant30(c, deps, workspaceId);
54667
55292
  assertOwnershipEnabled();
54668
55293
  const sessionId = c.req.param("sessionId");
54669
- const session = await getSession7(db, workspaceId, sessionId);
55294
+ const session = await getSession8(db, workspaceId, sessionId);
54670
55295
  if (!session) {
54671
55296
  throw new HTTPException57(404, { message: "session not found" });
54672
55297
  }
@@ -54821,7 +55446,7 @@ function registerSessionRoutes(app, deps) {
54821
55446
  requireViewerLifecyclePermission(grant);
54822
55447
  assertOwnershipEnabled();
54823
55448
  const sessionId = c.req.param("sessionId");
54824
- const session = await getSession7(db, workspaceId, sessionId);
55449
+ const session = await getSession8(db, workspaceId, sessionId);
54825
55450
  if (!session) {
54826
55451
  throw new HTTPException57(404, { message: "session not found" });
54827
55452
  }
@@ -54850,7 +55475,7 @@ function registerSessionRoutes(app, deps) {
54850
55475
  requireViewerLifecyclePermission(grant);
54851
55476
  assertOwnershipEnabled();
54852
55477
  const sessionId = c.req.param("sessionId");
54853
- const session = await getSession7(db, workspaceId, sessionId);
55478
+ const session = await getSession8(db, workspaceId, sessionId);
54854
55479
  if (!session) {
54855
55480
  throw new HTTPException57(404, { message: "session not found" });
54856
55481
  }
@@ -54872,7 +55497,7 @@ function registerSessionRoutes(app, deps) {
54872
55497
  const grant = await requireAccessGrant30(c, deps, workspaceId, "stream:view");
54873
55498
  assertOwnershipEnabled();
54874
55499
  const sessionId = c.req.param("sessionId");
54875
- const session = await getSession7(db, workspaceId, sessionId);
55500
+ const session = await getSession8(db, workspaceId, sessionId);
54876
55501
  if (!session) {
54877
55502
  throw new HTTPException57(404, { message: "session not found" });
54878
55503
  }
@@ -54894,7 +55519,7 @@ function registerSessionRoutes(app, deps) {
54894
55519
  const grant = await requireAccessGrant30(c, deps, workspaceId, permission);
54895
55520
  assertOwnershipEnabled();
54896
55521
  const sessionId = c.req.param("sessionId") ?? "";
54897
- const session = await getSession7(db, workspaceId, sessionId);
55522
+ const session = await getSession8(db, workspaceId, sessionId);
54898
55523
  if (!session) {
54899
55524
  throw new HTTPException57(404, { message: "session not found" });
54900
55525
  }
@@ -55093,7 +55718,7 @@ function registerSessionRoutes(app, deps) {
55093
55718
  message: "path query parameter is required"
55094
55719
  });
55095
55720
  }
55096
- const session = await getSession7(db, workspaceId, sessionId);
55721
+ const session = await getSession8(db, workspaceId, sessionId);
55097
55722
  if (!session) {
55098
55723
  throw new HTTPException57(404, { message: "session not found" });
55099
55724
  }
@@ -55608,7 +56233,7 @@ function eventEnumList(raw, schema, name) {
55608
56233
  }
55609
56234
  function sessionListQuery(query, allowCursor = true) {
55610
56235
  const parentSessionId = query.parentSessionId;
55611
- if (parentSessionId !== void 0 && parentSessionId !== "null" && !z14.string().uuid().safeParse(parentSessionId).success) {
56236
+ if (parentSessionId !== void 0 && parentSessionId !== "null" && !z15.string().uuid().safeParse(parentSessionId).success) {
55612
56237
  throw new HTTPException57(400, {
55613
56238
  message: 'parentSessionId must be a session id or the literal "null"'
55614
56239
  });
@@ -55634,12 +56259,58 @@ function sessionListQuery(query, allowCursor = true) {
55634
56259
  throw new HTTPException57(400, { message: 'archivedOnly must be the literal "true"' });
55635
56260
  }
55636
56261
  const archivedOnly = query.archivedOnly === "true";
56262
+ const channelId = query.channelId;
56263
+ if (channelId !== void 0 && channelId !== "null" && !z15.string().uuid().safeParse(channelId).success) {
56264
+ throw new HTTPException57(400, {
56265
+ message: 'channelId must be a channel id or the literal "null"'
56266
+ });
56267
+ }
56268
+ const createdByKind = query.createdByKind;
56269
+ const createdBySubjectId = query.createdBySubjectId;
56270
+ if (createdByKind === void 0 !== (createdBySubjectId === void 0)) {
56271
+ throw new HTTPException57(400, {
56272
+ message: "createdByKind and createdBySubjectId must be supplied together"
56273
+ });
56274
+ }
56275
+ if (createdByKind !== void 0 && createdByKind !== "subject" && createdByKind !== "service") {
56276
+ throw new HTTPException57(400, {
56277
+ message: 'createdByKind must be "subject" or "service"'
56278
+ });
56279
+ }
56280
+ if (createdBySubjectId !== void 0 && (createdBySubjectId.trim().length < 1 || createdBySubjectId.length > 1024)) {
56281
+ throw new HTTPException57(400, {
56282
+ message: "createdBySubjectId must be between 1 and 1024 characters"
56283
+ });
56284
+ }
56285
+ const parseDateBound = (name) => {
56286
+ const raw = query[name];
56287
+ if (raw === void 0) return void 0;
56288
+ const parsed = z15.string().datetime({ offset: true }).safeParse(raw);
56289
+ if (!parsed.success) {
56290
+ throw new HTTPException57(400, { message: `${name} must be an ISO-8601 timestamp` });
56291
+ }
56292
+ if (/\.\d{4,}/.test(parsed.data)) {
56293
+ throw new HTTPException57(400, { message: `${name} supports at most millisecond precision` });
56294
+ }
56295
+ return new Date(parsed.data);
56296
+ };
56297
+ const updatedFrom = parseDateBound("updatedFrom");
56298
+ const updatedBefore = parseDateBound("updatedBefore");
56299
+ const createdFrom = parseDateBound("createdFrom");
56300
+ const createdBefore = parseDateBound("createdBefore");
56301
+ if (updatedFrom && updatedBefore && updatedFrom >= updatedBefore) {
56302
+ throw new HTTPException57(400, { message: "updatedFrom must be earlier than updatedBefore" });
56303
+ }
56304
+ if (createdFrom && createdBefore && createdFrom >= createdBefore) {
56305
+ throw new HTTPException57(400, { message: "createdFrom must be earlier than createdBefore" });
56306
+ }
56307
+ const hasPageFilters = channelId !== void 0 || createdByKind !== void 0 || updatedFrom !== void 0 || updatedBefore !== void 0 || createdFrom !== void 0 || createdBefore !== void 0;
55637
56308
  if (pinsOnly && !allowCursor) {
55638
56309
  throw new HTTPException57(400, { message: 'pinsOnly requires view="page"' });
55639
56310
  }
55640
- if (pinsOnly && (rawCursor || parentSessionId !== void 0 || search)) {
56311
+ if (pinsOnly && (rawCursor || parentSessionId !== void 0 || search || hasPageFilters)) {
55641
56312
  throw new HTTPException57(400, {
55642
- message: "pinsOnly cannot be combined with cursor, parentSessionId, or search"
56313
+ message: "pinsOnly cannot be combined with cursor, parentSessionId, search, or filters"
55643
56314
  });
55644
56315
  }
55645
56316
  if (pinsOnly && archivedOnly) {
@@ -55651,7 +56322,14 @@ function sessionListQuery(query, allowCursor = true) {
55651
56322
  cursor,
55652
56323
  search: search || void 0,
55653
56324
  pinsOnly,
55654
- archivedOnly
56325
+ archivedOnly,
56326
+ channelId: channelId === void 0 ? void 0 : channelId === "null" ? null : channelId,
56327
+ createdBy: createdByKind && createdBySubjectId ? { kind: createdByKind, subjectId: createdBySubjectId } : void 0,
56328
+ updatedFrom,
56329
+ updatedBefore,
56330
+ createdFrom,
56331
+ createdBefore,
56332
+ hasPageFilters
55655
56333
  };
55656
56334
  }
55657
56335
  function encodeAgentTopologyCursor(value) {
@@ -55665,18 +56343,18 @@ function decodeAgentTopologyCursor(value) {
55665
56343
  }
55666
56344
  try {
55667
56345
  const decoded2 = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
55668
- const legacy = z14.object({
55669
- v: z14.literal(1),
55670
- parentSessionId: z14.string().uuid().nullable(),
55671
- search: z14.string().max(WORK_DISCOVERY_QUERY_MAX_CHARS2).nullable(),
55672
- cursor: z14.object({
55673
- orderBy: z14.literal("updatedAt"),
55674
- sortRevision: z14.string().max(64),
55675
- sortAt: z14.string().max(64),
55676
- id: z14.string().uuid(),
55677
- snapshotAt: z14.string().max(64),
55678
- snapshotRevision: z14.string().max(64),
55679
- updatedAfter: z14.null()
56346
+ const legacy = z15.object({
56347
+ v: z15.literal(1),
56348
+ parentSessionId: z15.string().uuid().nullable(),
56349
+ search: z15.string().max(WORK_DISCOVERY_QUERY_MAX_CHARS2).nullable(),
56350
+ cursor: z15.object({
56351
+ orderBy: z15.literal("updatedAt"),
56352
+ sortRevision: z15.string().max(64),
56353
+ sortAt: z15.string().max(64),
56354
+ id: z15.string().uuid(),
56355
+ snapshotAt: z15.string().max(64),
56356
+ snapshotRevision: z15.string().max(64),
56357
+ updatedAfter: z15.null()
55680
56358
  })
55681
56359
  }).safeParse(decoded2);
55682
56360
  if (legacy.success) {
@@ -55700,13 +56378,13 @@ function decodeAgentTopologyCursor(value) {
55700
56378
  claimLimit: null
55701
56379
  };
55702
56380
  }
55703
- const parsed = z14.object({
55704
- v: z14.literal(2),
55705
- parentSessionId: z14.union([z14.string().uuid(), z14.literal("all"), z14.null()]),
55706
- rootSessionId: z14.string().uuid().nullable(),
55707
- query: z14.string().max(WORK_DISCOVERY_QUERY_MAX_CHARS2).nullable(),
55708
- statuses: z14.array(
55709
- z14.enum([
56381
+ const parsed = z15.object({
56382
+ v: z15.literal(2),
56383
+ parentSessionId: z15.union([z15.string().uuid(), z15.literal("all"), z15.null()]),
56384
+ rootSessionId: z15.string().uuid().nullable(),
56385
+ query: z15.string().max(WORK_DISCOVERY_QUERY_MAX_CHARS2).nullable(),
56386
+ statuses: z15.array(
56387
+ z15.enum([
55710
56388
  "queued",
55711
56389
  "running",
55712
56390
  "idle",
@@ -55717,24 +56395,24 @@ function decodeAgentTopologyCursor(value) {
55717
56395
  "cancelled"
55718
56396
  ])
55719
56397
  ).max(8),
55720
- activeOnly: z14.boolean(),
55721
- recentHours: z14.number().int().positive().max(WORK_DISCOVERY_RECENT_HOURS_MAX2).nullable(),
55722
- subject: z14.object({
55723
- namespace: z14.string().min(1).max(WORK_CLAIM_NAMESPACE_MAX_BYTES2),
56398
+ activeOnly: z15.boolean(),
56399
+ recentHours: z15.number().int().positive().max(WORK_DISCOVERY_RECENT_HOURS_MAX2).nullable(),
56400
+ subject: z15.object({
56401
+ namespace: z15.string().min(1).max(WORK_CLAIM_NAMESPACE_MAX_BYTES2),
55724
56402
  type: WorkClaimSubjectType2,
55725
- canonicalKey: z14.string().min(1).max(WORK_CLAIM_CANONICAL_KEY_MAX_BYTES2)
56403
+ canonicalKey: z15.string().min(1).max(WORK_CLAIM_CANONICAL_KEY_MAX_BYTES2)
55726
56404
  }).strict().nullable(),
55727
- claimLimit: z14.number().int().positive().max(WORK_CLAIM_DISCOVERY_LIMIT2).nullable(),
55728
- cursor: z14.object({
55729
- orderBy: z14.enum(["updatedAt", "relevance"]),
55730
- sortRank: z14.number().int().nonnegative().nullable(),
55731
- sortRevision: z14.string().max(64),
55732
- sortAt: z14.string().max(64),
55733
- id: z14.string().uuid(),
55734
- snapshotAt: z14.string().max(64),
55735
- snapshotRevision: z14.string().max(64),
55736
- updatedAfter: z14.null(),
55737
- filterHash: z14.string().regex(/^[0-9a-f]{64}$/).nullable()
56405
+ claimLimit: z15.number().int().positive().max(WORK_CLAIM_DISCOVERY_LIMIT2).nullable(),
56406
+ cursor: z15.object({
56407
+ orderBy: z15.enum(["updatedAt", "relevance"]),
56408
+ sortRank: z15.number().int().nonnegative().nullable(),
56409
+ sortRevision: z15.string().max(64),
56410
+ sortAt: z15.string().max(64),
56411
+ id: z15.string().uuid(),
56412
+ snapshotAt: z15.string().max(64),
56413
+ snapshotRevision: z15.string().max(64),
56414
+ updatedAfter: z15.null(),
56415
+ filterHash: z15.string().regex(/^[0-9a-f]{64}$/).nullable()
55738
56416
  })
55739
56417
  }).parse(decoded2);
55740
56418
  if (!/^(?:0|[1-9]\d*)$/.test(parsed.cursor.sortRevision) || !/^(?:0|[1-9]\d*)$/.test(parsed.cursor.snapshotRevision) || BigInt(parsed.cursor.sortRevision) > 9223372036854775807n || BigInt(parsed.cursor.snapshotRevision) > 9223372036854775807n || Number.isNaN(Date.parse(parsed.cursor.sortAt)) || Number.isNaN(Date.parse(parsed.cursor.snapshotAt))) {
@@ -55759,13 +56437,13 @@ function agentTopologyQuery(query) {
55759
56437
  });
55760
56438
  }
55761
56439
  const rawParent = query.parentSessionId;
55762
- if (rawParent !== void 0 && rawParent !== "null" && !z14.string().uuid().safeParse(rawParent).success) {
56440
+ if (rawParent !== void 0 && rawParent !== "null" && !z15.string().uuid().safeParse(rawParent).success) {
55763
56441
  throw new HTTPException57(400, {
55764
56442
  message: 'parentSessionId must be a session id or the literal "null"'
55765
56443
  });
55766
56444
  }
55767
56445
  const rootSessionId = query.rootSessionId?.trim();
55768
- if (rootSessionId && !z14.string().uuid().safeParse(rootSessionId).success) {
56446
+ if (rootSessionId && !z15.string().uuid().safeParse(rootSessionId).success) {
55769
56447
  throw new HTTPException57(400, { message: "rootSessionId must be a session id" });
55770
56448
  }
55771
56449
  const normalizeSearchQuery = (value) => {
@@ -55794,8 +56472,8 @@ function agentTopologyQuery(query) {
55794
56472
  query.statuses.split(",").map((status) => status.trim()).filter(Boolean)
55795
56473
  )
55796
56474
  ].sort() : [];
55797
- const parsedStatuses = z14.array(
55798
- z14.enum([
56475
+ const parsedStatuses = z15.array(
56476
+ z15.enum([
55799
56477
  "queued",
55800
56478
  "running",
55801
56479
  "idle",
@@ -55838,7 +56516,7 @@ function agentTopologyQuery(query) {
55838
56516
  throw new HTTPException57(400, { message: "query cannot be combined with an exact subject" });
55839
56517
  }
55840
56518
  const relevanceRequested = Boolean(searchQuery || subject);
55841
- const parentSessionId = rawParent === void 0 ? relevanceRequested ? void 0 : null : rawParent === "null" ? null : rawParent;
56519
+ const parentSessionId = rawParent === void 0 ? relevanceRequested || Boolean(rootSessionId) ? void 0 : null : rawParent === "null" ? null : rawParent;
55842
56520
  const claimLimit = query.claimLimit === void 0 ? void 0 : Number(query.claimLimit);
55843
56521
  if (claimLimit !== void 0 && (!Number.isSafeInteger(claimLimit) || claimLimit < 1 || claimLimit > WORK_CLAIM_DISCOVERY_LIMIT2)) {
55844
56522
  throw new HTTPException57(400, {
@@ -56062,8 +56740,8 @@ import {
56062
56740
  updateSocialConnectionCredential as updateSocialConnectionCredential2
56063
56741
  } from "@opengeni/db";
56064
56742
  import { HTTPException as HTTPException58 } from "hono/http-exception";
56065
- import { z as z15 } from "zod";
56066
- import { requireAccessGrant as requireAccessGrant31, requireAccessGrantAuthorization as requireAccessGrantAuthorization12 } from "@opengeni/core";
56743
+ import { z as z16 } from "zod";
56744
+ import { requireAccessGrant as requireAccessGrant31, requireAccessGrantAuthorization as requireAccessGrantAuthorization13 } from "@opengeni/core";
56067
56745
  function registerSocialRoutes(app, deps) {
56068
56746
  const { db, settings, observability } = deps;
56069
56747
  app.get("/v1/workspaces/:workspaceId/social/connections", async (c) => {
@@ -56133,7 +56811,7 @@ function registerSocialRoutes(app, deps) {
56133
56811
  });
56134
56812
  }
56135
56813
  const payload = parsed.data;
56136
- const access = await requireAccessGrantAuthorization12(
56814
+ const access = await requireAccessGrantAuthorization13(
56137
56815
  c,
56138
56816
  deps,
56139
56817
  workspaceId,
@@ -56227,7 +56905,7 @@ function parseConnectionIds(raw) {
56227
56905
  return void 0;
56228
56906
  }
56229
56907
  const values = raw.split(",").map((value) => value.trim()).filter(Boolean);
56230
- const parsed = z15.array(z15.string().uuid()).safeParse(values);
56908
+ const parsed = z16.array(z16.string().uuid()).safeParse(values);
56231
56909
  if (!parsed.success) {
56232
56910
  throw new HTTPException58(422, {
56233
56911
  message: "connectionIds must be a comma-separated list of UUIDs"
@@ -56248,7 +56926,7 @@ function socialHttpException(error) {
56248
56926
  }
56249
56927
 
56250
56928
  // src/routes/workspaces.ts
56251
- import { createHash as createHash19 } from "crypto";
56929
+ import { createHash as createHash18 } from "crypto";
56252
56930
  import {
56253
56931
  AddWorkspaceMemberRequest,
56254
56932
  CreateWorkspaceRequest,
@@ -56511,7 +57189,7 @@ function projectWorkspaceOpenRouterCustomModel(model) {
56511
57189
  });
56512
57190
  }
56513
57191
  function workspaceCustomModelRequestHash(value) {
56514
- return createHash19("sha256").update(stableJson6(value), "utf8").digest("hex");
57192
+ return createHash18("sha256").update(stableJson6(value), "utf8").digest("hex");
56515
57193
  }
56516
57194
  function workspaceMembersResponse(members) {
56517
57195
  return ListWorkspaceMembersResponse.parse({
@@ -57430,8 +58108,8 @@ import {
57430
58108
  WorkspaceInstructionPolicyOperationReuseError
57431
58109
  } from "@opengeni/db";
57432
58110
  import { HTTPException as HTTPException61 } from "hono/http-exception";
57433
- import { z as z16 } from "zod";
57434
- var WorkspaceInstructionPolicyRevisionId = z16.string().uuid();
58111
+ import { z as z17 } from "zod";
58112
+ var WorkspaceInstructionPolicyRevisionId = z17.string().uuid();
57435
58113
  async function parseBody(context, schema) {
57436
58114
  const parsed = schema.safeParse(await context.req.json().catch(() => null));
57437
58115
  if (!parsed.success) {
@@ -57763,7 +58441,7 @@ import {
57763
58441
  WorkspaceLearningPolicyOperationReuseError
57764
58442
  } from "@opengeni/db";
57765
58443
  import { HTTPException as HTTPException62 } from "hono/http-exception";
57766
- import { z as z17 } from "zod";
58444
+ import { z as z18 } from "zod";
57767
58445
  async function parseBody2(context, schema) {
57768
58446
  const parsed = schema.safeParse(await context.req.json().catch(() => null));
57769
58447
  if (!parsed.success) {
@@ -57859,7 +58537,7 @@ function registerWorkspaceLearningRoutes(app, deps) {
57859
58537
  });
57860
58538
  app.post(`${base}/revisions/:revisionId/activate`, async (context) => {
57861
58539
  const workspaceId = context.req.param("workspaceId");
57862
- const revisionId2 = z17.string().uuid().safeParse(context.req.param("revisionId"));
58540
+ const revisionId2 = z18.string().uuid().safeParse(context.req.param("revisionId"));
57863
58541
  if (!revisionId2.success) throw new HTTPException62(422, { message: "Invalid revision id" });
57864
58542
  const grant = await requireAccessGrant34(context, deps, workspaceId, "workspace:admin");
57865
58543
  const request = await parseBody2(context, ActivateWorkspaceLearningPolicyRevisionRequest);
@@ -57909,7 +58587,7 @@ function registerWorkspaceLearningRoutes(app, deps) {
57909
58587
  });
57910
58588
  app.post(`${base}/activations/:activationReceiptId/undo`, async (context) => {
57911
58589
  const workspaceId = context.req.param("workspaceId");
57912
- const activationReceiptId = z17.string().uuid().safeParse(context.req.param("activationReceiptId"));
58590
+ const activationReceiptId = z18.string().uuid().safeParse(context.req.param("activationReceiptId"));
57913
58591
  if (!activationReceiptId.success) {
57914
58592
  throw new HTTPException62(422, { message: "Invalid activation receipt id" });
57915
58593
  }
@@ -57952,7 +58630,7 @@ import {
57952
58630
  } from "@opengeni/contracts";
57953
58631
  import {
57954
58632
  requireAccessGrant as requireAccessGrant35,
57955
- requireAccessGrantAuthorization as requireAccessGrantAuthorization13
58633
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization14
57956
58634
  } from "@opengeni/core";
57957
58635
  import {
57958
58636
  activateCompanyProfileRevision,
@@ -57970,8 +58648,8 @@ import {
57970
58648
  updateCompanyProfileAgentPolicy
57971
58649
  } from "@opengeni/db";
57972
58650
  import { HTTPException as HTTPException63 } from "hono/http-exception";
57973
- import { z as z18 } from "zod";
57974
- var RevisionId = z18.string().uuid();
58651
+ import { z as z19 } from "zod";
58652
+ var RevisionId = z19.string().uuid();
57975
58653
  async function parseBody3(context, schema) {
57976
58654
  const parsed = schema.safeParse(await context.req.json().catch(() => null));
57977
58655
  if (!parsed.success) throw new HTTPException63(422, { message: "Invalid company-profile request" });
@@ -58032,7 +58710,7 @@ function registerCompanyProfileRoutes(app, deps) {
58032
58710
  const base = "/v1/workspaces/:workspaceId/company-profile";
58033
58711
  app.get(`${base}/agent-policy`, async (context) => {
58034
58712
  const workspaceId = context.req.param("workspaceId");
58035
- const access = await requireAccessGrantAuthorization13(
58713
+ const access = await requireAccessGrantAuthorization14(
58036
58714
  context,
58037
58715
  deps,
58038
58716
  workspaceId,
@@ -58055,7 +58733,7 @@ function registerCompanyProfileRoutes(app, deps) {
58055
58733
  });
58056
58734
  app.patch(`${base}/agent-policy`, async (context) => {
58057
58735
  const workspaceId = context.req.param("workspaceId");
58058
- const access = await requireAccessGrantAuthorization13(
58736
+ const access = await requireAccessGrantAuthorization14(
58059
58737
  context,
58060
58738
  deps,
58061
58739
  workspaceId,
@@ -58099,7 +58777,7 @@ function registerCompanyProfileRoutes(app, deps) {
58099
58777
  });
58100
58778
  app.put(base, async (context) => {
58101
58779
  const workspaceId = context.req.param("workspaceId");
58102
- const access = await requireAccessGrantAuthorization13(
58780
+ const access = await requireAccessGrantAuthorization14(
58103
58781
  context,
58104
58782
  deps,
58105
58783
  workspaceId,
@@ -58151,7 +58829,7 @@ function registerCompanyProfileRoutes(app, deps) {
58151
58829
  });
58152
58830
  app.post(`${base}/rollback`, async (context) => {
58153
58831
  const workspaceId = context.req.param("workspaceId");
58154
- const access = await requireAccessGrantAuthorization13(
58832
+ const access = await requireAccessGrantAuthorization14(
58155
58833
  context,
58156
58834
  deps,
58157
58835
  workspaceId,
@@ -58198,7 +58876,7 @@ function registerCompanyProfileRoutes(app, deps) {
58198
58876
  });
58199
58877
  app.post(`${base}/revisions/:revisionId/activate`, async (context) => {
58200
58878
  const workspaceId = context.req.param("workspaceId");
58201
- const access = await requireAccessGrantAuthorization13(
58879
+ const access = await requireAccessGrantAuthorization14(
58202
58880
  context,
58203
58881
  deps,
58204
58882
  workspaceId,
@@ -58229,7 +58907,7 @@ function registerCompanyProfileRoutes(app, deps) {
58229
58907
  }
58230
58908
 
58231
58909
  // src/routes/company-brain.ts
58232
- import { createHash as createHash21 } from "crypto";
58910
+ import { createHash as createHash20 } from "crypto";
58233
58911
  import {
58234
58912
  COMPANY_BRAIN_INSPECTOR_CURSOR_MAX_CHARS,
58235
58913
  COMPANY_BRAIN_INSPECTOR_DEFAULT_LIMIT,
@@ -58264,7 +58942,7 @@ import {
58264
58942
  searchEffectiveKnowledge as searchEffectiveKnowledge2
58265
58943
  } from "@opengeni/documents";
58266
58944
  import { HTTPException as HTTPException64 } from "hono/http-exception";
58267
- import { z as z19 } from "zod";
58945
+ import { z as z20 } from "zod";
58268
58946
 
58269
58947
  // src/company-brain-okf.ts
58270
58948
  import {
@@ -58506,7 +59184,7 @@ function serializeCompanyBrainOkf(value) {
58506
59184
  }
58507
59185
 
58508
59186
  // src/workspace-state-projection.ts
58509
- import { createHash as createHash20 } from "crypto";
59187
+ import { createHash as createHash19 } from "crypto";
58510
59188
  import {
58511
59189
  KnowledgeMemoryKind,
58512
59190
  KnowledgeMemoryStatus,
@@ -58520,15 +59198,15 @@ import {
58520
59198
  WorkspaceStateResponse
58521
59199
  } from "@opengeni/contracts";
58522
59200
  function hashIdentities(values) {
58523
- return createHash20("sha256").update(values.join("\n"), "utf8").digest("hex");
59201
+ return createHash19("sha256").update(values.join("\n"), "utf8").digest("hex");
58524
59202
  }
58525
59203
  function policyTargetKey(value) {
58526
59204
  return `${value.kind}:${value.scope}:${value.roleKey ?? ""}`;
58527
59205
  }
58528
59206
  function policyTargetKeysForRole(policyRole) {
58529
- const keys = /* @__PURE__ */ new Set(["charter:global:", "policy:global:"]);
58530
- if (policyRole !== null) keys.add(`policy:role:${policyRole}`);
58531
- return keys;
59207
+ const keys2 = /* @__PURE__ */ new Set(["charter:global:", "policy:global:"]);
59208
+ if (policyRole !== null) keys2.add(`policy:role:${policyRole}`);
59209
+ return keys2;
58532
59210
  }
58533
59211
  function policyIdentity(value) {
58534
59212
  return `${policyTargetKey(value)}:${value.revisionId}:${value.contentHash}:${value.activationVersion}`;
@@ -58855,15 +59533,15 @@ function projectWorkspaceState(input) {
58855
59533
  var BASE_LIMIT = 24;
58856
59534
  var TOPIC_LIMIT = 24;
58857
59535
  var TOPIC_MAX_CHARS = 96;
58858
- var inspectorQuery = z19.object({
58859
- attemptId: z19.string().uuid().optional(),
58860
- cursor: z19.string().min(1).max(COMPANY_BRAIN_INSPECTOR_CURSOR_MAX_CHARS).optional(),
58861
- limit: z19.coerce.number().int().positive().max(COMPANY_BRAIN_INSPECTOR_MAX_LIMIT).default(COMPANY_BRAIN_INSPECTOR_DEFAULT_LIMIT)
59536
+ var inspectorQuery = z20.object({
59537
+ attemptId: z20.string().uuid().optional(),
59538
+ cursor: z20.string().min(1).max(COMPANY_BRAIN_INSPECTOR_CURSOR_MAX_CHARS).optional(),
59539
+ limit: z20.coerce.number().int().positive().max(COMPANY_BRAIN_INSPECTOR_MAX_LIMIT).default(COMPANY_BRAIN_INSPECTOR_DEFAULT_LIMIT)
58862
59540
  }).strict().refine((value) => !(value.attemptId && value.cursor), {
58863
59541
  message: "attemptId cannot be combined with cursor"
58864
59542
  });
58865
59543
  function receiptCursorScope(workspaceId, subjectId) {
58866
- return createHash21("sha256").update("opengeni:company-brain-context-receipts:v1\0").update(workspaceId).update("\0").update(subjectId).digest("hex");
59544
+ return createHash20("sha256").update("opengeni:company-brain-context-receipts:v1\0").update(workspaceId).update("\0").update(subjectId).digest("hex");
58867
59545
  }
58868
59546
  function encodeReceiptCursor(workspaceId, subjectId, cursor) {
58869
59547
  return Buffer.from(
@@ -58875,11 +59553,11 @@ function decodeReceiptCursor(value, workspaceId, subjectId) {
58875
59553
  try {
58876
59554
  const bytes = Buffer.from(value, "base64url");
58877
59555
  if (bytes.toString("base64url") !== value) throw new Error("encoding");
58878
- const decoded2 = z19.object({
58879
- v: z19.literal(1),
58880
- s: z19.string().regex(/^[0-9a-f]{64}$/u),
58881
- createdAt: z19.string().datetime(),
58882
- id: z19.string().uuid()
59556
+ const decoded2 = z20.object({
59557
+ v: z20.literal(1),
59558
+ s: z20.string().regex(/^[0-9a-f]{64}$/u),
59559
+ createdAt: z20.string().datetime(),
59560
+ id: z20.string().uuid()
58883
59561
  }).strict().parse(JSON.parse(bytes.toString("utf8")));
58884
59562
  if (decoded2.s !== receiptCursorScope(workspaceId, subjectId)) {
58885
59563
  throw new HTTPException64(409, { message: "context receipt cursor belongs to another scope" });
@@ -59101,7 +59779,7 @@ function registerCompanyBrainRoutes(app, deps) {
59101
59779
  app.get(`${base}/knowledge-proposals`, async (context) => {
59102
59780
  const workspaceId = context.req.param("workspaceId");
59103
59781
  const grant = await requireAccessGrant36(context, deps, workspaceId, "documents:search");
59104
- const parsed = z19.coerce.number().int().positive().max(COMPANY_BRAIN_INSPECTOR_MAX_LIMIT).default(COMPANY_BRAIN_INSPECTOR_DEFAULT_LIMIT).safeParse(context.req.query("limit"));
59782
+ const parsed = z20.coerce.number().int().positive().max(COMPANY_BRAIN_INSPECTOR_MAX_LIMIT).default(COMPANY_BRAIN_INSPECTOR_DEFAULT_LIMIT).safeParse(context.req.query("limit"));
59105
59783
  if (!parsed.success) throw new HTTPException64(400, { message: "invalid proposal limit" });
59106
59784
  const response = await listCompanyBrainKnowledgeProposals(deps.db, {
59107
59785
  workspaceId,
@@ -59122,7 +59800,7 @@ import {
59122
59800
  } from "@opengeni/contracts";
59123
59801
  import {
59124
59802
  requireAccessGrant as requireAccessGrant37,
59125
- requireAccessGrantAuthorization as requireAccessGrantAuthorization14
59803
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization15
59126
59804
  } from "@opengeni/core";
59127
59805
  import {
59128
59806
  listSlackTaskPolicy,
@@ -59181,7 +59859,7 @@ function registerSlackTaskPolicyRoutes(app, deps) {
59181
59859
  });
59182
59860
  app.put(base, async (context) => {
59183
59861
  const workspaceId = context.req.param("workspaceId");
59184
- const access = await requireAccessGrantAuthorization14(
59862
+ const access = await requireAccessGrantAuthorization15(
59185
59863
  context,
59186
59864
  deps,
59187
59865
  workspaceId,
@@ -59235,7 +59913,7 @@ import { getDocumentInventory as getDocumentInventory2 } from "@opengeni/documen
59235
59913
  import { HTTPException as HTTPException66 } from "hono/http-exception";
59236
59914
 
59237
59915
  // src/workspace-state-export.ts
59238
- import { createHash as createHash22 } from "crypto";
59916
+ import { createHash as createHash21 } from "crypto";
59239
59917
  import {
59240
59918
  WORKSPACE_STATE_EXPORT_SCHEMA_VERSION,
59241
59919
  WorkspaceStateExportResponse
@@ -59266,7 +59944,7 @@ function createWorkspaceStateExport(state) {
59266
59944
  kind: "opengeni.workspace_state.sanitized_export",
59267
59945
  schemaVersion: WORKSPACE_STATE_EXPORT_SCHEMA_VERSION,
59268
59946
  generatedAt: state.generatedAt,
59269
- stateSha256: createHash22("sha256").update(canonicalState, "utf8").digest("hex"),
59947
+ stateSha256: createHash21("sha256").update(canonicalState, "utf8").digest("hex"),
59270
59948
  omissions: WORKSPACE_STATE_EXPORT_OMISSIONS,
59271
59949
  state
59272
59950
  });
@@ -59366,7 +60044,7 @@ function registerWorkspaceStateRoutes(app, deps) {
59366
60044
  }
59367
60045
 
59368
60046
  // src/routes/workspace-artifacts.ts
59369
- import { createHash as createHash23 } from "crypto";
60047
+ import { createHash as createHash22 } from "crypto";
59370
60048
  import {
59371
60049
  CreateWorkspaceArtifactRequest,
59372
60050
  PublishWorkspaceArtifactVersionRequest,
@@ -59381,7 +60059,7 @@ import {
59381
60059
  } from "@opengeni/contracts";
59382
60060
  import {
59383
60061
  requireAccessGrant as requireAccessGrant39,
59384
- requireAccessGrantAuthorization as requireAccessGrantAuthorization15,
60062
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization16,
59385
60063
  requireSessionAuthorization as requireSessionAuthorization8,
59386
60064
  SessionAuthorizationDeniedError as SessionAuthorizationDeniedError9,
59387
60065
  SessionAuthorizationUnavailableError as SessionAuthorizationUnavailableError7
@@ -59397,16 +60075,22 @@ import {
59397
60075
  setWorkspaceArtifactStatus as setWorkspaceArtifactStatus2,
59398
60076
  WorkspaceArtifactConflictError,
59399
60077
  WorkspaceArtifactNotFoundError as WorkspaceArtifactNotFoundError2,
59400
- WorkspaceArtifactOperationError
60078
+ WorkspaceArtifactOperationError as WorkspaceArtifactOperationError2
59401
60079
  } from "@opengeni/db";
59402
60080
  import { HTTPException as HTTPException67 } from "hono/http-exception";
59403
- import { z as z20 } from "zod";
59404
- var ArtifactId3 = z20.string().uuid();
60081
+ import { z as z21 } from "zod";
60082
+ var ArtifactId3 = z21.string().uuid();
59405
60083
  async function body(context, schema) {
59406
60084
  const parsed = schema.safeParse(await context.req.json().catch(() => null));
59407
60085
  if (!parsed.success) throw new HTTPException67(422, { message: "Invalid artifact request" });
59408
60086
  return parsed.data;
59409
60087
  }
60088
+ function parseVersionId(value) {
60089
+ if (value !== void 0 && !ArtifactId3.safeParse(value).success) {
60090
+ throw new HTTPException67(422, { message: "Invalid artifact version id" });
60091
+ }
60092
+ return value;
60093
+ }
59410
60094
  function artifactId(context) {
59411
60095
  const parsed = ArtifactId3.safeParse(context.req.param("artifactId"));
59412
60096
  if (!parsed.success) throw new HTTPException67(422, { message: "Invalid artifact id" });
@@ -59426,7 +60110,7 @@ function workspaceArtifactErrorResponse(context, error) {
59426
60110
  409
59427
60111
  );
59428
60112
  }
59429
- if (error instanceof WorkspaceArtifactOperationError) {
60113
+ if (error instanceof WorkspaceArtifactOperationError2) {
59430
60114
  return context.json(
59431
60115
  { code: "INVALID_WORKSPACE_ARTIFACT_OPERATION", message: error.message },
59432
60116
  422
@@ -59443,16 +60127,16 @@ function workspaceArtifactErrorResponse(context, error) {
59443
60127
  }
59444
60128
  throw error;
59445
60129
  }
59446
- function prepareContent(deps, workspaceId, input) {
60130
+ function prepareContent(deps, grant, input) {
59447
60131
  if (!deps.objectStorage)
59448
60132
  throw new HTTPException67(503, {
59449
60133
  message: "Object storage is not configured"
59450
60134
  });
59451
- return prepareWorkspaceArtifactContent(deps.objectStorage, workspaceId, input);
60135
+ return prepareWorkspaceArtifactPublication(deps, grant, input);
59452
60136
  }
59453
60137
  function provenance(subjectId, idempotencyKey) {
59454
60138
  return {
59455
- operationKey: `subject:${createHash23("sha256").update(`${subjectId}:${idempotencyKey}`).digest("hex")}`,
60139
+ operationKey: `subject:${createHash22("sha256").update(`${subjectId}:${idempotencyKey}`).digest("hex")}`,
59456
60140
  actorSubjectId: subjectId,
59457
60141
  sourceSessionId: null,
59458
60142
  sourceTurnId: null,
@@ -59489,6 +60173,28 @@ async function mutationResponse(deps, grant, response) {
59489
60173
  }
59490
60174
  function registerWorkspaceArtifactRoutes(app, deps) {
59491
60175
  const base = "/v1/workspaces/:workspaceId/published-artifacts";
60176
+ app.post(`${base}/uploads`, async (context) => {
60177
+ const grant = await requireAccessGrant39(
60178
+ context,
60179
+ deps,
60180
+ context.req.param("workspaceId"),
60181
+ "artifacts:publish"
60182
+ );
60183
+ return context.json(await prepareWorkspaceArtifactUpload(deps, grant));
60184
+ });
60185
+ app.get(`${base}/:artifactId/downloads`, async (context) => {
60186
+ const workspaceId = context.req.param("workspaceId");
60187
+ await requireAccessGrant39(context, deps, workspaceId, "artifacts:read");
60188
+ if (!deps.objectStorage)
60189
+ throw new HTTPException67(503, { message: "Object storage is not configured" });
60190
+ const ref = await getWorkspaceArtifactContentRef2(
60191
+ deps.db,
60192
+ workspaceId,
60193
+ artifactId(context),
60194
+ parseVersionId(context.req.query("versionId"))
60195
+ );
60196
+ return context.json(await workspaceArtifactDownloads(deps.objectStorage, ref, "public"));
60197
+ });
59492
60198
  app.get(base, async (context) => {
59493
60199
  const workspaceId = context.req.param("workspaceId");
59494
60200
  await requireAccessGrant39(context, deps, workspaceId, "artifacts:read");
@@ -59516,7 +60222,7 @@ function registerWorkspaceArtifactRoutes(app, deps) {
59516
60222
  });
59517
60223
  app.post(base, async (context) => {
59518
60224
  const workspaceId = context.req.param("workspaceId");
59519
- const authorization = await requireAccessGrantAuthorization15(
60225
+ const authorization = await requireAccessGrantAuthorization16(
59520
60226
  context,
59521
60227
  deps,
59522
60228
  workspaceId,
@@ -59527,12 +60233,13 @@ function registerWorkspaceArtifactRoutes(app, deps) {
59527
60233
  const id = crypto.randomUUID();
59528
60234
  const slugBase = request.slug ?? (normalizeWorkspaceArtifactSlug2(request.title) || "artifact");
59529
60235
  const slug = request.slug ?? `${slugBase.slice(0, 87)}-${id.slice(0, 8)}`;
59530
- const content = prepareContent(deps, workspaceId, {
59531
- html: request.html,
59532
- ...request.source ? { source: request.source } : {},
59533
- ...request.requestedTools ? { requestedTools: request.requestedTools } : {}
59534
- });
59535
60236
  try {
60237
+ const content = await prepareContent(deps, grant, {
60238
+ ...request.html !== void 0 ? { html: request.html } : {},
60239
+ ...request.uploadId ? { uploadId: request.uploadId } : {},
60240
+ ...request.source ? { source: request.source } : {},
60241
+ ...request.requestedTools ? { requestedTools: request.requestedTools } : {}
60242
+ });
59536
60243
  return context.json(
59537
60244
  await mutationResponse(
59538
60245
  deps,
@@ -59572,6 +60279,42 @@ function registerWorkspaceArtifactRoutes(app, deps) {
59572
60279
  return workspaceArtifactErrorResponse(context, error);
59573
60280
  }
59574
60281
  });
60282
+ app.get(`${base}/:artifactId/html`, async (context) => {
60283
+ const workspaceId = context.req.param("workspaceId");
60284
+ await requireAccessGrant39(context, deps, workspaceId, "artifacts:read");
60285
+ if (!deps.objectStorage)
60286
+ throw new HTTPException67(503, { message: "Object storage is not configured" });
60287
+ const ref = await getWorkspaceArtifactContentRef2(
60288
+ deps.db,
60289
+ workspaceId,
60290
+ artifactId(context),
60291
+ parseVersionId(context.req.query("versionId"))
60292
+ );
60293
+ const iterator = readArtifactObject(deps.objectStorage, ref.contentKey);
60294
+ return new Response(
60295
+ new ReadableStream({
60296
+ async pull(controller) {
60297
+ try {
60298
+ const item = await iterator.next();
60299
+ if (item.done) controller.close();
60300
+ else controller.enqueue(item.value);
60301
+ } catch (error) {
60302
+ controller.error(error);
60303
+ }
60304
+ },
60305
+ async cancel() {
60306
+ await iterator.return();
60307
+ }
60308
+ }),
60309
+ {
60310
+ headers: {
60311
+ "Content-Type": "text/html; charset=utf-8",
60312
+ "Cache-Control": "private, no-store",
60313
+ "Content-Security-Policy": "sandbox allow-scripts"
60314
+ }
60315
+ }
60316
+ );
60317
+ });
59575
60318
  app.get(`${base}/:artifactId/content`, async (context) => {
59576
60319
  const workspaceId = context.req.param("workspaceId");
59577
60320
  await requireAccessGrant39(context, deps, workspaceId, "artifacts:read");
@@ -59579,10 +60322,7 @@ function registerWorkspaceArtifactRoutes(app, deps) {
59579
60322
  throw new HTTPException67(503, {
59580
60323
  message: "Object storage is not configured"
59581
60324
  });
59582
- const parsedVersion = context.req.query("versionId");
59583
- if (parsedVersion && !ArtifactId3.safeParse(parsedVersion).success) {
59584
- throw new HTTPException67(422, { message: "Invalid artifact version id" });
59585
- }
60325
+ const parsedVersion = parseVersionId(context.req.query("versionId"));
59586
60326
  try {
59587
60327
  const ref = await getWorkspaceArtifactContentRef2(
59588
60328
  deps.db,
@@ -59614,7 +60354,7 @@ function registerWorkspaceArtifactRoutes(app, deps) {
59614
60354
  });
59615
60355
  app.post(`${base}/:artifactId/versions`, async (context) => {
59616
60356
  const workspaceId = context.req.param("workspaceId");
59617
- const authorization = await requireAccessGrantAuthorization15(
60357
+ const authorization = await requireAccessGrantAuthorization16(
59618
60358
  context,
59619
60359
  deps,
59620
60360
  workspaceId,
@@ -59623,12 +60363,13 @@ function registerWorkspaceArtifactRoutes(app, deps) {
59623
60363
  const grant = authorization.grant;
59624
60364
  const request = await body(context, PublishWorkspaceArtifactVersionRequest);
59625
60365
  const id = artifactId(context);
59626
- const content = prepareContent(deps, workspaceId, {
59627
- html: request.html,
59628
- ...request.source ? { source: request.source } : {},
59629
- ...request.requestedTools ? { requestedTools: request.requestedTools } : {}
59630
- });
59631
60366
  try {
60367
+ const content = await prepareContent(deps, grant, {
60368
+ ...request.html !== void 0 ? { html: request.html } : {},
60369
+ ...request.uploadId ? { uploadId: request.uploadId } : {},
60370
+ ...request.source ? { source: request.source } : {},
60371
+ ...request.requestedTools ? { requestedTools: request.requestedTools } : {}
60372
+ });
59632
60373
  return context.json(
59633
60374
  await mutationResponse(
59634
60375
  deps,
@@ -59651,7 +60392,7 @@ function registerWorkspaceArtifactRoutes(app, deps) {
59651
60392
  });
59652
60393
  app.post(`${base}/:artifactId/rollback`, async (context) => {
59653
60394
  const workspaceId = context.req.param("workspaceId");
59654
- const authorization = await requireAccessGrantAuthorization15(
60395
+ const authorization = await requireAccessGrantAuthorization16(
59655
60396
  context,
59656
60397
  deps,
59657
60398
  workspaceId,
@@ -59682,7 +60423,7 @@ function registerWorkspaceArtifactRoutes(app, deps) {
59682
60423
  });
59683
60424
  app.patch(`${base}/:artifactId/status`, async (context) => {
59684
60425
  const workspaceId = context.req.param("workspaceId");
59685
- const authorization = await requireAccessGrantAuthorization15(
60426
+ const authorization = await requireAccessGrantAuthorization16(
59686
60427
  context,
59687
60428
  deps,
59688
60429
  workspaceId,
@@ -59735,7 +60476,7 @@ import {
59735
60476
  import {
59736
60477
  hasPermission as hasPermission23,
59737
60478
  requireAccessGrant as requireAccessGrant40,
59738
- requireAccessGrantAuthorization as requireAccessGrantAuthorization16
60479
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization17
59739
60480
  } from "@opengeni/core";
59740
60481
  import {
59741
60482
  activatePreferenceRegistryRevision,
@@ -59758,8 +60499,8 @@ import {
59758
60499
  supersedePreferenceRegistry
59759
60500
  } from "@opengeni/db";
59760
60501
  import { HTTPException as HTTPException68 } from "hono/http-exception";
59761
- import { z as z21 } from "zod";
59762
- var Id = z21.string().uuid();
60502
+ import { z as z22 } from "zod";
60503
+ var Id = z22.string().uuid();
59763
60504
  async function parseBody4(context, schema) {
59764
60505
  const parsed = schema.safeParse(await context.req.json().catch(() => null));
59765
60506
  if (!parsed.success)
@@ -59883,7 +60624,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
59883
60624
  });
59884
60625
  app.post(`${base}/proposals`, async (context) => {
59885
60626
  const workspaceId = context.req.param("workspaceId");
59886
- const access = await requireAccessGrantAuthorization16(
60627
+ const access = await requireAccessGrantAuthorization17(
59887
60628
  context,
59888
60629
  deps,
59889
60630
  workspaceId,
@@ -59963,7 +60704,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
59963
60704
  });
59964
60705
  app.post(`${base}/:preferenceId/activate`, async (context) => {
59965
60706
  const workspaceId = context.req.param("workspaceId");
59966
- const access = await requireAccessGrantAuthorization16(
60707
+ const access = await requireAccessGrantAuthorization17(
59967
60708
  context,
59968
60709
  deps,
59969
60710
  workspaceId,
@@ -59992,7 +60733,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
59992
60733
  });
59993
60734
  app.post(`${base}/:preferenceId/correct`, async (context) => {
59994
60735
  const workspaceId = context.req.param("workspaceId");
59995
- const access = await requireAccessGrantAuthorization16(
60736
+ const access = await requireAccessGrantAuthorization17(
59996
60737
  context,
59997
60738
  deps,
59998
60739
  workspaceId,
@@ -60021,7 +60762,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
60021
60762
  });
60022
60763
  app.post(`${base}/:preferenceId/scope`, async (context) => {
60023
60764
  const workspaceId = context.req.param("workspaceId");
60024
- const access = await requireAccessGrantAuthorization16(
60765
+ const access = await requireAccessGrantAuthorization17(
60025
60766
  context,
60026
60767
  deps,
60027
60768
  workspaceId,
@@ -60050,7 +60791,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
60050
60791
  });
60051
60792
  app.post(`${base}/:preferenceId/deactivate`, async (context) => {
60052
60793
  const workspaceId = context.req.param("workspaceId");
60053
- const access = await requireAccessGrantAuthorization16(
60794
+ const access = await requireAccessGrantAuthorization17(
60054
60795
  context,
60055
60796
  deps,
60056
60797
  workspaceId,
@@ -60079,7 +60820,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
60079
60820
  });
60080
60821
  app.post(`${base}/:preferenceId/supersede`, async (context) => {
60081
60822
  const workspaceId = context.req.param("workspaceId");
60082
- const access = await requireAccessGrantAuthorization16(
60823
+ const access = await requireAccessGrantAuthorization17(
60083
60824
  context,
60084
60825
  deps,
60085
60826
  workspaceId,
@@ -60108,7 +60849,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
60108
60849
  });
60109
60850
  app.post(`${base}/:preferenceId/reject`, async (context) => {
60110
60851
  const workspaceId = context.req.param("workspaceId");
60111
- const access = await requireAccessGrantAuthorization16(
60852
+ const access = await requireAccessGrantAuthorization17(
60112
60853
  context,
60113
60854
  deps,
60114
60855
  workspaceId,
@@ -60208,7 +60949,7 @@ import { requireAccessGrant as requireAccessGrant43, TranscriptionServiceError a
60208
60949
  import { getWorkspace as getWorkspace5 } from "@opengeni/db";
60209
60950
 
60210
60951
  // src/routes/transcription-recordings.ts
60211
- import { createHash as createHash24 } from "crypto";
60952
+ import { createHash as createHash23 } from "crypto";
60212
60953
  import {
60213
60954
  CreateTranscriptionRecordingRequest,
60214
60955
  FinalizeTranscriptionRecordingRequest,
@@ -60564,8 +61305,8 @@ function registerResumableTranscriptionRoutes(app, deps) {
60564
61305
  c.req.raw,
60565
61306
  deps.settings.voiceInputResumableMaxChunkSizeBytes
60566
61307
  );
60567
- const sha2565 = sha256Hex3(body4);
60568
- if (sha2565 !== declaredSha256) {
61308
+ const sha2564 = sha256Hex3(body4);
61309
+ if (sha2564 !== declaredSha256) {
60569
61310
  return c.json({ code: "conflict" }, 409);
60570
61311
  }
60571
61312
  const existing = await getTranscriptionRecording(deps.db, {
@@ -60587,7 +61328,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
60587
61328
  recordingId: existing.recording.id,
60588
61329
  chunkNumber,
60589
61330
  byteLength: body4.byteLength,
60590
- sha256: sha2565,
61331
+ sha256: sha2564,
60591
61332
  startMilliseconds,
60592
61333
  durationMilliseconds,
60593
61334
  maxTotalBytes: deps.settings.voiceInputResumableMaxSizeBytes,
@@ -60600,7 +61341,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
60600
61341
  key: reservation.chunk.objectKey,
60601
61342
  contentType: existing.recording.mimeType,
60602
61343
  body: body4,
60603
- sha256: sha2565
61344
+ sha256: sha2564
60604
61345
  });
60605
61346
  } catch {
60606
61347
  throw new RecordingProcessingError("Chunk upload failed", "network", true);
@@ -60639,7 +61380,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
60639
61380
  app.post(
60640
61381
  "/v1/workspaces/:workspaceId/transcription-recordings/:recordingId/finalize",
60641
61382
  async (c) => {
60642
- const owner = correlationId(c);
61383
+ const owner2 = correlationId(c);
60643
61384
  let authority = null;
60644
61385
  let generation = 0;
60645
61386
  try {
@@ -60654,7 +61395,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
60654
61395
  workspaceId: authority.workspaceId,
60655
61396
  subjectId: authority.subjectId,
60656
61397
  recordingId,
60657
- owner,
61398
+ owner: owner2,
60658
61399
  ...parsed.data,
60659
61400
  staleBefore: new Date(Date.now() - PROCESSING_LEASE_MILLISECONDS)
60660
61401
  });
@@ -60702,15 +61443,15 @@ function registerResumableTranscriptionRoutes(app, deps) {
60702
61443
  chunks: verifiedChunkBytes(deps, chunks, c.req.raw.signal),
60703
61444
  signal: c.req.raw.signal
60704
61445
  })) {
60705
- const sha2565 = sha256Hex3(segment.bytes);
61446
+ const sha2564 = sha256Hex3(segment.bytes);
60706
61447
  const reservation = await reserveTranscriptionRecordingSegment(deps.db, {
60707
61448
  ...authority,
60708
61449
  recordingId,
60709
- owner,
61450
+ owner: owner2,
60710
61451
  generation,
60711
61452
  segmentNumber: segment.segmentNumber,
60712
61453
  byteLength: segment.bytes.byteLength,
60713
- sha256: sha2565,
61454
+ sha256: sha2564,
60714
61455
  startMilliseconds: segment.startMilliseconds,
60715
61456
  durationMilliseconds: segment.durationMilliseconds
60716
61457
  });
@@ -60719,7 +61460,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
60719
61460
  key: reservation.objectKey,
60720
61461
  contentType: segment.mimeType,
60721
61462
  body: segment.bytes,
60722
- sha256: sha2565
61463
+ sha256: sha2564
60723
61464
  });
60724
61465
  } catch {
60725
61466
  throw new RecordingProcessingError("Segment upload failed", "network", true);
@@ -60728,7 +61469,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
60728
61469
  workspaceId: authority.workspaceId,
60729
61470
  subjectId: authority.subjectId,
60730
61471
  recordingId,
60731
- owner,
61472
+ owner: owner2,
60732
61473
  generation,
60733
61474
  segmentNumber: segment.segmentNumber
60734
61475
  });
@@ -60738,7 +61479,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
60738
61479
  workspaceId: authority.workspaceId,
60739
61480
  subjectId: authority.subjectId,
60740
61481
  recordingId,
60741
- owner,
61482
+ owner: owner2,
60742
61483
  generation
60743
61484
  })
60744
61485
  );
@@ -60749,7 +61490,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
60749
61490
  workspaceId: authority.workspaceId,
60750
61491
  subjectId: authority.subjectId,
60751
61492
  recordingId: uuidParam3(c, "recordingId"),
60752
- owner,
61493
+ owner: owner2,
60753
61494
  generation,
60754
61495
  errorCode: failure.code,
60755
61496
  retryable: failure.retryable
@@ -60915,13 +61656,13 @@ async function cleanupTerminalObjects(deps, authority, response) {
60915
61656
  return response;
60916
61657
  }
60917
61658
  try {
60918
- const keys = await transcriptionRecordingObjectKeys(deps.db, {
61659
+ const keys2 = await transcriptionRecordingObjectKeys(deps.db, {
60919
61660
  workspaceId: authority.workspaceId,
60920
61661
  subjectId: authority.subjectId,
60921
61662
  recordingId: response.recording.id
60922
61663
  });
60923
61664
  let current = response;
60924
- for (const key of keys) {
61665
+ for (const key of keys2) {
60925
61666
  await deps.objectStorage.deleteObject(key);
60926
61667
  current = await markTranscriptionRecordingObjectCleaned(deps.db, {
60927
61668
  workspaceId: authority.workspaceId,
@@ -61031,7 +61772,7 @@ function valueAsUuid(value) {
61031
61772
  if (/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value)) {
61032
61773
  return value;
61033
61774
  }
61034
- const hex2 = createHash24("sha256").update(value).digest("hex").slice(0, 32);
61775
+ const hex2 = createHash23("sha256").update(value).digest("hex").slice(0, 32);
61035
61776
  return `${hex2.slice(0, 8)}-${hex2.slice(8, 12)}-4${hex2.slice(13, 16)}-a${hex2.slice(17, 20)}-${hex2.slice(20, 32)}`;
61036
61777
  }
61037
61778
  function nonnegativeInteger(value) {
@@ -61095,7 +61836,7 @@ async function readBoundedBody(request, maxBytes) {
61095
61836
  return body4;
61096
61837
  }
61097
61838
  function sha256Hex3(bytes) {
61098
- return createHash24("sha256").update(bytes).digest("hex");
61839
+ return createHash23("sha256").update(bytes).digest("hex");
61099
61840
  }
61100
61841
 
61101
61842
  // src/routes/transcriptions.ts
@@ -61401,8 +62142,8 @@ import {
61401
62142
  getCanonicalHumanIdentityProjection as getCanonicalHumanIdentityProjection2
61402
62143
  } from "@opengeni/db/canonical-human-identities";
61403
62144
  import { HTTPException as HTTPException71 } from "hono/http-exception";
61404
- import { z as z22 } from "zod";
61405
- var BindingId = z22.string().uuid();
62145
+ import { z as z23 } from "zod";
62146
+ var BindingId = z23.string().uuid();
61406
62147
  async function requestIdentity(context, deps) {
61407
62148
  return await requireCanonicalHumanRequestIdentity(context, {
61408
62149
  db: deps.db,
@@ -61596,11 +62337,11 @@ import {
61596
62337
  updateOrganizationRetentionPolicy
61597
62338
  } from "@opengeni/db";
61598
62339
  import { HTTPException as HTTPException72 } from "hono/http-exception";
61599
- import { z as z23 } from "zod";
61600
- var OrganizationId2 = z23.string().uuid();
61601
- var WorkspaceId = z23.string().uuid();
61602
- var MembershipId = z23.string().uuid();
61603
- var InvitationId = z23.string().uuid();
62340
+ import { z as z24 } from "zod";
62341
+ var OrganizationId2 = z24.string().uuid();
62342
+ var WorkspaceId = z24.string().uuid();
62343
+ var MembershipId = z24.string().uuid();
62344
+ var InvitationId = z24.string().uuid();
61604
62345
  async function requireManagedHuman(context, deps) {
61605
62346
  if (deps.settings.productAccessMode !== "managed" || !deps.managedAuth || !context.req.header("cookie") || context.req.header("authorization")) {
61606
62347
  throw new HTTPException72(401, { message: "managed human session required" });
@@ -62347,9 +63088,9 @@ import {
62347
63088
  startOrganizationRecoveryOperation
62348
63089
  } from "@opengeni/db";
62349
63090
  import { HTTPException as HTTPException73 } from "hono/http-exception";
62350
- import { z as z24 } from "zod";
62351
- var OrganizationId3 = z24.string().uuid();
62352
- var RecoveryOperationId = z24.string().uuid();
63091
+ import { z as z25 } from "zod";
63092
+ var OrganizationId3 = z25.string().uuid();
63093
+ var RecoveryOperationId = z25.string().uuid();
62353
63094
  var productionServices = {
62354
63095
  requireCanonicalHumanRequestIdentity: requireCanonicalHumanRequestIdentity2,
62355
63096
  getManagedAuthRequestActorAdmissionStamp,
@@ -62897,15 +63638,15 @@ import {
62897
63638
  ManagedAuthSessionSetOperationReuseError,
62898
63639
  mutateManagedAuthSessionSet
62899
63640
  } from "@opengeni/db/managed-auth-session-sets";
62900
- import { ensureManagedAccessForUser as ensureManagedAccessForUser2, getSession as getSession8 } from "@opengeni/db";
63641
+ import { ensureManagedAccessForUser as ensureManagedAccessForUser2, getSession as getSession9 } from "@opengeni/db";
62901
63642
  import { deleteCookie as deleteCookie3, getCookie, setCookie as setCookie3 } from "hono/cookie";
62902
63643
  import { HTTPException as HTTPException75 } from "hono/http-exception";
62903
- import { z as z25 } from "zod";
62904
- var ManagedAuthSocialStartReceipt = z25.object({
62905
- version: z25.literal(1),
62906
- requestDigest: z25.string().regex(/^[0-9a-f]{64}$/u),
62907
- url: z25.string().url().max(4096),
62908
- stateCookie: z25.string().min(1).max(4096)
63644
+ import { z as z26 } from "zod";
63645
+ var ManagedAuthSocialStartReceipt = z26.object({
63646
+ version: z26.literal(1),
63647
+ requestDigest: z26.string().regex(/^[0-9a-f]{64}$/u),
63648
+ url: z26.string().url().max(4096),
63649
+ stateCookie: z26.string().min(1).max(4096)
62909
63650
  }).strict();
62910
63651
  function registerManagedAuthSessionSetRoutes(app, deps) {
62911
63652
  const noStore = async (context, next) => {
@@ -63535,7 +64276,7 @@ async function resolveCanonicalDeepLink(deps, authority, path, expectedActorEpoc
63535
64276
  continue;
63536
64277
  }
63537
64278
  if (target.sessionId) {
63538
- const session = await getSession8(deps.db, grant.workspaceId, target.sessionId);
64279
+ const session = await getSession9(deps.db, grant.workspaceId, target.sessionId);
63539
64280
  if (!session) continue;
63540
64281
  try {
63541
64282
  await requireSessionAuthorization9(deps, grant, {
@@ -63834,7 +64575,7 @@ import {
63834
64575
  import {
63835
64576
  issueManagedHumanUserResourceGrant,
63836
64577
  listManagedHumanUserResourceAuthorities,
63837
- requireAccessGrantAuthorization as requireAccessGrantAuthorization17,
64578
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization18,
63838
64579
  revokeManagedHumanUserResourceGrant,
63839
64580
  SessionAuthorizationDeniedError as SessionAuthorizationDeniedError10,
63840
64581
  SessionAuthorizationUnavailableError as SessionAuthorizationUnavailableError8,
@@ -63842,7 +64583,7 @@ import {
63842
64583
  } from "@opengeni/core";
63843
64584
  import { nestedPostgresSqlState as nestedPostgresSqlState9, SessionTenancyNotActivatedError as SessionTenancyNotActivatedError2 } from "@opengeni/db";
63844
64585
  import { HTTPException as HTTPException76 } from "hono/http-exception";
63845
- import { z as z26 } from "zod";
64586
+ import { z as z27 } from "zod";
63846
64587
  async function body2(context, schema) {
63847
64588
  const parsed = schema.safeParse(await context.req.json().catch(() => null));
63848
64589
  if (!parsed.success) throw new HTTPException76(422, { message: "invalid user-resource request" });
@@ -63874,7 +64615,7 @@ function registerUserResourceAuthorityRoutes(app, deps) {
63874
64615
  });
63875
64616
  if (!query.success) throw new HTTPException76(422, { message: "explicit scope=user required" });
63876
64617
  const workspaceId = context.req.param("workspaceId");
63877
- const access = await requireAccessGrantAuthorization17(
64618
+ const access = await requireAccessGrantAuthorization18(
63878
64619
  context,
63879
64620
  deps,
63880
64621
  workspaceId,
@@ -63899,10 +64640,10 @@ function registerUserResourceAuthorityRoutes(app, deps) {
63899
64640
  });
63900
64641
  app.post(`${base}/:authorityId/grants`, async (context) => {
63901
64642
  const workspaceId = context.req.param("workspaceId");
63902
- const authorityId = z26.string().uuid().safeParse(context.req.param("authorityId"));
64643
+ const authorityId = z27.string().uuid().safeParse(context.req.param("authorityId"));
63903
64644
  if (!authorityId.success) throw new HTTPException76(422, { message: "invalid authority id" });
63904
64645
  const request = await body2(context, IssueUserResourceGrantRequest);
63905
- const access = await requireAccessGrantAuthorization17(
64646
+ const access = await requireAccessGrantAuthorization18(
63906
64647
  context,
63907
64648
  deps,
63908
64649
  workspaceId,
@@ -63929,10 +64670,10 @@ function registerUserResourceAuthorityRoutes(app, deps) {
63929
64670
  app.delete(`${base}/grants/:grantId`, async (context) => {
63930
64671
  const query = RevokeUserResourceGrantQuery.safeParse({ scope: context.req.query("scope") });
63931
64672
  if (!query.success) throw new HTTPException76(422, { message: "explicit scope=user required" });
63932
- const grantId = z26.string().uuid().safeParse(context.req.param("grantId"));
64673
+ const grantId = z27.string().uuid().safeParse(context.req.param("grantId"));
63933
64674
  if (!grantId.success) throw new HTTPException76(422, { message: "invalid grant id" });
63934
64675
  const workspaceId = context.req.param("workspaceId");
63935
- const access = await requireAccessGrantAuthorization17(
64676
+ const access = await requireAccessGrantAuthorization18(
63936
64677
  context,
63937
64678
  deps,
63938
64679
  workspaceId,
@@ -63962,7 +64703,7 @@ import {
63962
64703
  import {
63963
64704
  issueManagedHumanUserResourceGrant as issueManagedHumanUserResourceGrant2,
63964
64705
  listManagedHumanUserResourceAuthorities as listManagedHumanUserResourceAuthorities2,
63965
- requireAccessGrantAuthorization as requireAccessGrantAuthorization18,
64706
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization19,
63966
64707
  revokeManagedHumanUserResourceGrant as revokeManagedHumanUserResourceGrant2,
63967
64708
  SessionAuthorizationDeniedError as SessionAuthorizationDeniedError11,
63968
64709
  SessionAuthorizationUnavailableError as SessionAuthorizationUnavailableError9,
@@ -63970,7 +64711,7 @@ import {
63970
64711
  } from "@opengeni/core";
63971
64712
  import { nestedPostgresSqlState as nestedPostgresSqlState10, SessionTenancyNotActivatedError as SessionTenancyNotActivatedError3 } from "@opengeni/db";
63972
64713
  import { HTTPException as HTTPException78 } from "hono/http-exception";
63973
- import { z as z27 } from "zod";
64714
+ import { z as z28 } from "zod";
63974
64715
 
63975
64716
  // src/connection-authority-owner.ts
63976
64717
  import {
@@ -64028,7 +64769,7 @@ function registerConnectionAuthorityRoutes(app, deps) {
64028
64769
  });
64029
64770
  if (!query.success) throw new HTTPException78(422, { message: "explicit scope=user required" });
64030
64771
  const workspaceId = context.req.param("workspaceId");
64031
- const access = await requireAccessGrantAuthorization18(
64772
+ const access = await requireAccessGrantAuthorization19(
64032
64773
  context,
64033
64774
  deps,
64034
64775
  workspaceId,
@@ -64050,10 +64791,10 @@ function registerConnectionAuthorityRoutes(app, deps) {
64050
64791
  });
64051
64792
  app.post(`${base}/:authorityId/grants`, async (context) => {
64052
64793
  const workspaceId = context.req.param("workspaceId");
64053
- const authorityId = z27.string().uuid().safeParse(context.req.param("authorityId"));
64794
+ const authorityId = z28.string().uuid().safeParse(context.req.param("authorityId"));
64054
64795
  if (!authorityId.success) throw new HTTPException78(422, { message: "invalid authority id" });
64055
64796
  const request = await body3(context, IssueConnectionUseGrantRequest2);
64056
- const access = await requireAccessGrantAuthorization18(
64797
+ const access = await requireAccessGrantAuthorization19(
64057
64798
  context,
64058
64799
  deps,
64059
64800
  workspaceId,
@@ -64081,10 +64822,10 @@ function registerConnectionAuthorityRoutes(app, deps) {
64081
64822
  app.delete(`${base}/grants/:grantId`, async (context) => {
64082
64823
  const query = RevokeConnectionUseGrantQuery.safeParse({ scope: context.req.query("scope") });
64083
64824
  if (!query.success) throw new HTTPException78(422, { message: "explicit scope=user required" });
64084
- const grantId = z27.string().uuid().safeParse(context.req.param("grantId"));
64825
+ const grantId = z28.string().uuid().safeParse(context.req.param("grantId"));
64085
64826
  if (!grantId.success) throw new HTTPException78(422, { message: "invalid grant id" });
64086
64827
  const workspaceId = context.req.param("workspaceId");
64087
- const access = await requireAccessGrantAuthorization18(
64828
+ const access = await requireAccessGrantAuthorization19(
64088
64829
  context,
64089
64830
  deps,
64090
64831
  workspaceId,
@@ -64610,7 +65351,7 @@ async function exactAvailable(providers, providerId, context) {
64610
65351
  }
64611
65352
 
64612
65353
  // src/integrations/slack-interactions.ts
64613
- import { createHash as createHash26, createHmac as createHmac9, timingSafeEqual as timingSafeEqual3 } from "crypto";
65354
+ import { createHash as createHash25, createHmac as createHmac9, timingSafeEqual as timingSafeEqual3 } from "crypto";
64614
65355
  import {
64615
65356
  approvalIdentifier,
64616
65357
  ApproveSlackUserLinkAccessRequest,
@@ -64653,7 +65394,7 @@ import {
64653
65394
  getConnectionMetadata as getConnectionMetadata12,
64654
65395
  getOrCreateSlackInteraction,
64655
65396
  getLatestSessionModelForSubject,
64656
- getSession as getSession9,
65397
+ getSession as getSession10,
64657
65398
  getSessionEvent as getSessionEvent2,
64658
65399
  getSessionHumanInputRequest as getSessionHumanInputRequest2,
64659
65400
  childRequiresActionResolutionExists,
@@ -65088,7 +65829,7 @@ function record5(value) {
65088
65829
  }
65089
65830
 
65090
65831
  // src/slack-reaction-files.ts
65091
- import { createHash as createHash25 } from "crypto";
65832
+ import { createHash as createHash24 } from "crypto";
65092
65833
  import { completeFileUpload as completeFileUpload5, prepareGeneratedWorkspaceFile as prepareGeneratedWorkspaceFile2 } from "@opengeni/db";
65093
65834
  import { retryWhileMissing as retryWhileMissing6 } from "@opengeni/storage";
65094
65835
  var IMPORT_IDENTITY_VERSION = "slack-reaction-image-v1";
@@ -65105,7 +65846,7 @@ async function importSlackReactionImage(dependencies, source, image, ordinal) {
65105
65846
  if (image.bytes.byteLength < 1 || image.bytes.byteLength > 4 * 1024 * 1024) {
65106
65847
  throw new Error("Slack reaction image size is invalid");
65107
65848
  }
65108
- const sha2565 = createHash25("sha256").update(image.bytes).digest("hex");
65849
+ const sha2564 = createHash24("sha256").update(image.bytes).digest("hex");
65109
65850
  const identity = slackReactionImageIdentity(source, image.fileId);
65110
65851
  const fileId = deterministicUuid(`${identity}:file`);
65111
65852
  const uploadId = deterministicUuid(`${identity}:upload`);
@@ -65123,7 +65864,7 @@ async function importSlackReactionImage(dependencies, source, image, ordinal) {
65123
65864
  safeFilename,
65124
65865
  contentType: image.contentType,
65125
65866
  sizeBytes: image.bytes.byteLength,
65126
- sha256: sha2565,
65867
+ sha256: sha2564,
65127
65868
  bucket: storage.bucket,
65128
65869
  objectKey,
65129
65870
  expiresAt: new Date((dependencies.now?.() ?? /* @__PURE__ */ new Date()).getTime() + UPLOAD_INTENT_TTL_MS2)
@@ -65133,23 +65874,23 @@ async function importSlackReactionImage(dependencies, source, image, ordinal) {
65133
65874
  if (file.status !== "ready") {
65134
65875
  const existing = await storage.headObject(objectKey);
65135
65876
  if (existing) {
65136
- assertStoredSlackImage(existing, image, sha2565);
65877
+ assertStoredSlackImage(existing, image, sha2564);
65137
65878
  } else if (storage.putObjectIfAbsent) {
65138
65879
  const created = await storage.putObjectIfAbsent({
65139
65880
  key: objectKey,
65140
65881
  contentType: image.contentType,
65141
65882
  body: image.bytes,
65142
- sha256: sha2565
65883
+ sha256: sha2564
65143
65884
  });
65144
65885
  if (!created) {
65145
- await assertVisibleSlackImage(storage, objectKey, image, sha2565);
65886
+ await assertVisibleSlackImage(storage, objectKey, image, sha2564);
65146
65887
  }
65147
65888
  } else {
65148
65889
  await storage.putObject({
65149
65890
  key: objectKey,
65150
65891
  contentType: image.contentType,
65151
65892
  body: image.bytes,
65152
- sha256: sha2565
65893
+ sha256: sha2564
65153
65894
  });
65154
65895
  }
65155
65896
  file = await (dependencies.completeFile ?? completeFileUpload5)(
@@ -65158,9 +65899,9 @@ async function importSlackReactionImage(dependencies, source, image, ordinal) {
65158
65899
  uploadId
65159
65900
  );
65160
65901
  } else {
65161
- await assertVisibleSlackImage(storage, objectKey, image, sha2565);
65902
+ await assertVisibleSlackImage(storage, objectKey, image, sha2564);
65162
65903
  }
65163
- if (file.id !== fileId || file.status !== "ready" || file.filename !== filename || file.safeFilename !== safeFilename || file.contentType !== image.contentType || file.sizeBytes !== image.bytes.byteLength || file.sha256 !== sha2565) {
65904
+ if (file.id !== fileId || file.status !== "ready" || file.filename !== filename || file.safeFilename !== safeFilename || file.contentType !== image.contentType || file.sizeBytes !== image.bytes.byteLength || file.sha256 !== sha2564) {
65164
65905
  throw new Error("Imported Slack image differs from its immutable workspace file");
65165
65906
  }
65166
65907
  const mountPath = `attachments/slack/${String(ordinal).padStart(2, "0")}-${safeFilename}`;
@@ -65189,17 +65930,17 @@ function safeSlackImageFilename(value, contentType) {
65189
65930
  const stem = withoutKnownExtension.replace(/^[. -]+|[. ]+$/gu, "").slice(0, 180) || "slack-image";
65190
65931
  return `${stem}${extension}`;
65191
65932
  }
65192
- async function assertVisibleSlackImage(storage, objectKey, image, sha2565) {
65933
+ async function assertVisibleSlackImage(storage, objectKey, image, sha2564) {
65193
65934
  const head = await retryWhileMissing6(async () => await storage.headObject(objectKey));
65194
- assertStoredSlackImage(head, image, sha2565);
65935
+ assertStoredSlackImage(head, image, sha2564);
65195
65936
  }
65196
- function assertStoredSlackImage(head, image, sha2565) {
65197
- if (!head || head.ContentLength !== image.bytes.byteLength || head.ContentType !== image.contentType || head.Metadata?.sha256 !== sha2565 || typeof head.VersionToken !== "string" || head.VersionToken.length < 1) {
65937
+ function assertStoredSlackImage(head, image, sha2564) {
65938
+ if (!head || head.ContentLength !== image.bytes.byteLength || head.ContentType !== image.contentType || head.Metadata?.sha256 !== sha2564 || typeof head.VersionToken !== "string" || head.VersionToken.length < 1) {
65198
65939
  throw new Error("Stored Slack image differs from its immutable import");
65199
65940
  }
65200
65941
  }
65201
65942
  function deterministicUuid(seed) {
65202
- const bytes = createHash25("sha256").update(seed, "utf8").digest().subarray(0, 16);
65943
+ const bytes = createHash24("sha256").update(seed, "utf8").digest().subarray(0, 16);
65203
65944
  bytes[6] = (bytes[6] ?? 0) & 15 | 80;
65204
65945
  bytes[8] = (bytes[8] ?? 0) & 63 | 128;
65205
65946
  const hex2 = bytes.toString("hex");
@@ -65358,7 +66099,7 @@ function slackReactionInboxEntry(payload, bot, settings) {
65358
66099
  if (!settings.enabled || !event || event.type !== "reaction_added" || item?.type !== "message" || !teamId || !eventId || !userId || userId === bot.botUserId || !reaction || reaction !== settings.emoji || !channelId || !workspaceSlackReactionChannelAllowed(settings, channelId) || !timestamp) {
65359
66100
  return null;
65360
66101
  }
65361
- const stableReactionIdentity = createHash26("sha256").update([teamId, userId, channelId, timestamp, reaction].join("\n")).digest("hex");
66102
+ const stableReactionIdentity = createHash25("sha256").update([teamId, userId, channelId, timestamp, reaction].join("\n")).digest("hex");
65362
66103
  return {
65363
66104
  providerEventId: eventId,
65364
66105
  providerMessageId: `reaction:${stableReactionIdentity}`,
@@ -65548,7 +66289,7 @@ function registerSlackInteractionRoutes(app, deps) {
65548
66289
  const prepared = await prepareSlackUserLinkAccessRequest(deps.db, {
65549
66290
  accountId: installation.accountId,
65550
66291
  workspaceId,
65551
- tokenDigest: createHash26("sha256").update(payload.linkToken).digest("hex"),
66292
+ tokenDigest: createHash25("sha256").update(payload.linkToken).digest("hex"),
65552
66293
  connectionId: link.connectionId,
65553
66294
  slackTeamId: link.slackTeamId,
65554
66295
  slackUserId: link.slackUserId,
@@ -65755,7 +66496,7 @@ function registerSlackInteractionRoutes(app, deps) {
65755
66496
  const prepared = await prepareSlackUserLinkAccessRequest(deps.db, {
65756
66497
  accountId: grant.accountId,
65757
66498
  workspaceId,
65758
- tokenDigest: createHash26("sha256").update(linkToken).digest("hex"),
66499
+ tokenDigest: createHash25("sha256").update(linkToken).digest("hex"),
65759
66500
  connectionId: link.connectionId,
65760
66501
  slackTeamId: link.slackTeamId,
65761
66502
  slackUserId: link.slackUserId,
@@ -67219,7 +67960,7 @@ function slackReactionPreparedEntry(entry2, context, prepared) {
67219
67960
  }
67220
67961
  async function remountSlackReactionTaskForSession(deps, workspaceId, sessionId, prepared) {
67221
67962
  if (prepared.attachments.length === 0) return prepared;
67222
- const session = await getSession9(deps.db, workspaceId, sessionId);
67963
+ const session = await getSession10(deps.db, workspaceId, sessionId);
67223
67964
  if (!session) throw new SlackInteractionPermanentError("session_not_found");
67224
67965
  const initialOrdinal = session.resources.reduce((maximum, resource) => {
67225
67966
  if (resource.kind !== "file" || !resource.mountPath) return maximum;
@@ -67815,7 +68556,7 @@ async function executeSlackAction(deps, grant, interaction, handle) {
67815
68556
  controlState: action === "pause" ? "paused" : "active"
67816
68557
  };
67817
68558
  }
67818
- const session = await getSession9(deps.db, grant.workspaceId, handle.sessionId);
68559
+ const session = await getSession10(deps.db, grant.workspaceId, handle.sessionId);
67819
68560
  if (!session) throw new SlackInteractionPermanentError("slack_action_session_missing");
67820
68561
  const recurring = hasPermission25(grant.permissions, "sessions:read") && hasPermission25(grant.permissions, "scheduled_tasks:manage") ? makeRecurringText(deps, grant.workspaceId, handle.sessionId) : null;
67821
68562
  return {
@@ -68636,7 +69377,7 @@ function normalizedBlockActionInteraction(payload) {
68636
69377
  if (!teamId || !userId || !channelId || !messageTs || !actionTs || !actionId || !SLACK_BLOCK_ACTION_IDS.has(actionId) || !handleId || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(handleId) || !Array.isArray(payload.actions) || payload.actions.length !== 1) {
68637
69378
  throw new HTTPException79(400, { message: "invalid Slack block action" });
68638
69379
  }
68639
- const identity = createHash26("sha256").update([teamId, userId, channelId, messageTs, actionTs, actionId, handleId].join("\n")).digest("hex");
69380
+ const identity = createHash25("sha256").update([teamId, userId, channelId, messageTs, actionTs, actionId, handleId].join("\n")).digest("hex");
68640
69381
  return {
68641
69382
  providerEventId: `block:${identity}`,
68642
69383
  providerMessageId: `block:${identity}`,
@@ -68901,7 +69642,7 @@ function usesPrivateBotDm(interaction, entry2) {
68901
69642
  return isDirectMessageShortcut(entry2) || interaction.visibility === "private" && entry2.triggerKind !== "dm";
68902
69643
  }
68903
69644
  function deterministicUuid2(value) {
68904
- const bytes = createHash26("sha256").update(value).digest().subarray(0, 16);
69645
+ const bytes = createHash25("sha256").update(value).digest().subarray(0, 16);
68905
69646
  bytes[6] = bytes[6] & 15 | 80;
68906
69647
  bytes[8] = bytes[8] & 63 | 128;
68907
69648
  const hex2 = bytes.toString("hex");
@@ -69021,7 +69762,7 @@ import {
69021
69762
  validateToolRefs,
69022
69763
  withDefaultEnabledCapabilityMcpTools
69023
69764
  } from "@opengeni/core";
69024
- import { workflowIdForSession as workflowIdForSession3 } from "@opengeni/core";
69765
+ import { workflowIdForSession as workflowIdForSession4 } from "@opengeni/core";
69025
69766
  var API_MAX_REQUEST_BODY_BYTES = 32 * 1024 * 1024;
69026
69767
  var managedAuthReaperDatabases = /* @__PURE__ */ new WeakSet();
69027
69768
  var MANAGED_AUTH_REAPER_INTERVAL_MS = 6e4;
@@ -69317,13 +70058,13 @@ function createAppComposition(deps) {
69317
70058
  return await accessKeyBoundary(c, next);
69318
70059
  });
69319
70060
  app.use("/v1/*", async (c, next) => {
69320
- c.header(OPENGENI_API_CONTRACT_HEADER, OPENGENI_API_CONTRACT_REVISION);
69321
- if (deps.settings.environment !== "test" && isApiContractProtectedMutation(c.req.method, new URL(c.req.url).pathname) && c.req.header(OPENGENI_API_CONTRACT_HEADER) !== OPENGENI_API_CONTRACT_REVISION) {
70061
+ c.header(OPENGENI_API_CONTRACT_HEADER2, OPENGENI_API_CONTRACT_REVISION2);
70062
+ if (deps.settings.environment !== "test" && isApiContractProtectedMutation(c.req.method, new URL(c.req.url).pathname) && c.req.header(OPENGENI_API_CONTRACT_HEADER2) !== OPENGENI_API_CONTRACT_REVISION2) {
69322
70063
  return c.json(
69323
70064
  {
69324
70065
  code: "API_CONTRACT_CHANGED",
69325
70066
  message: "OpenGeni updated. Reload this client before changing state.",
69326
- apiContractRevision: OPENGENI_API_CONTRACT_REVISION
70067
+ apiContractRevision: OPENGENI_API_CONTRACT_REVISION2
69327
70068
  },
69328
70069
  409
69329
70070
  );
@@ -69522,7 +70263,7 @@ function createAppComposition(deps) {
69522
70263
  return c.json(
69523
70264
  ClientConfig.parse({
69524
70265
  deploymentRevision: deps.settings.deploymentRevision,
69525
- apiContractRevision: OPENGENI_API_CONTRACT_REVISION,
70266
+ apiContractRevision: OPENGENI_API_CONTRACT_REVISION2,
69526
70267
  ...deps.settings.serverVersion ? { serverVersion: deps.settings.serverVersion } : {},
69527
70268
  defaultModel: canonicalizeConfiguredModelId2(catalogSettings, catalogSettings.openaiModel),
69528
70269
  allowedModels: configuredAllowedModels(catalogSettings),
@@ -69696,7 +70437,7 @@ function createAppComposition(deps) {
69696
70437
  });
69697
70438
  app.get("/v1/workspaces/:workspaceId/tools/catalog", async (c) => {
69698
70439
  const workspaceId = c.req.param("workspaceId");
69699
- const authorization = await requireAccessGrantAuthorization19(
70440
+ const authorization = await requireAccessGrantAuthorization20(
69700
70441
  c,
69701
70442
  routeDeps,
69702
70443
  workspaceId,
@@ -69712,7 +70453,7 @@ function createAppComposition(deps) {
69712
70453
  });
69713
70454
  app.post("/v1/workspaces/:workspaceId/tools/calls", async (c) => {
69714
70455
  const workspaceId = c.req.param("workspaceId");
69715
- const authorization = await requireAccessGrantAuthorization19(
70456
+ const authorization = await requireAccessGrantAuthorization20(
69716
70457
  c,
69717
70458
  routeDeps,
69718
70459
  workspaceId,
@@ -69741,7 +70482,7 @@ function createAppComposition(deps) {
69741
70482
  });
69742
70483
  app.post("/v1/workspaces/:workspaceId/tools/approvals", async (c) => {
69743
70484
  const workspaceId = c.req.param("workspaceId");
69744
- const authorization = await requireAccessGrantAuthorization19(
70485
+ const authorization = await requireAccessGrantAuthorization20(
69745
70486
  c,
69746
70487
  routeDeps,
69747
70488
  workspaceId,
@@ -69770,7 +70511,7 @@ function createAppComposition(deps) {
69770
70511
  });
69771
70512
  app.get("/v1/workspaces/:workspaceId/tools/declarations", async (c) => {
69772
70513
  const workspaceId = c.req.param("workspaceId");
69773
- const authorization = await requireAccessGrantAuthorization19(
70514
+ const authorization = await requireAccessGrantAuthorization20(
69774
70515
  c,
69775
70516
  routeDeps,
69776
70517
  workspaceId,
@@ -69784,6 +70525,19 @@ function createAppComposition(deps) {
69784
70525
  await prepared.close();
69785
70526
  }
69786
70527
  });
70528
+ app.all("/v1/workspaces/:workspaceId/codemode/sdk/*", async (c) => {
70529
+ const workspaceId = c.req.param("workspaceId");
70530
+ const grant = await requireAccessGrant46(c, routeDeps, workspaceId);
70531
+ const url = new URL(c.req.url);
70532
+ const prefix = `/v1/workspaces/${workspaceId}/codemode/sdk`;
70533
+ const forwarded = await codemodeSessionRequest(
70534
+ routeDeps,
70535
+ grant,
70536
+ c.req.raw,
70537
+ url.pathname.slice(prefix.length) + url.search
70538
+ );
70539
+ return app.fetch(forwarded);
70540
+ });
69787
70541
  app.get("/v1/workspaces/:workspaceId/codemode/catalog", async (c) => {
69788
70542
  const workspaceId = c.req.param("workspaceId");
69789
70543
  const grant = await requireAccessGrant46(c, routeDeps, workspaceId);
@@ -69912,7 +70666,7 @@ function createAppComposition(deps) {
69912
70666
  const requestId = correlationIds.get(c.req.raw) ?? crypto.randomUUID();
69913
70667
  c.header(OPENGENI_CORRELATION_HEADER, requestId);
69914
70668
  if (new URL(c.req.url).pathname.startsWith("/v1/")) {
69915
- c.header(OPENGENI_API_CONTRACT_HEADER, OPENGENI_API_CONTRACT_REVISION);
70669
+ c.header(OPENGENI_API_CONTRACT_HEADER2, OPENGENI_API_CONTRACT_REVISION2);
69916
70670
  }
69917
70671
  const envelope = ErrorEnvelope.parse({
69918
70672
  error: {
@@ -69970,7 +70724,7 @@ async function validateCodexAccountTarget(request) {
69970
70724
  }
69971
70725
  }
69972
70726
  async function requireMcpAccessGrantAuthorization(c, deps, workspaceId) {
69973
- const authorization = await requireAccessGrantAuthorization19(c, deps, workspaceId);
70727
+ const authorization = await requireAccessGrantAuthorization20(c, deps, workspaceId);
69974
70728
  const grant = authorization.grant;
69975
70729
  if (hasPermission26(grant.permissions, "workspace:read")) {
69976
70730
  return authorization;
@@ -70684,6 +71438,10 @@ var routeLabelPatterns = [
70684
71438
  pattern: /^\/v1\/workspaces\/[^/]+\/github\/app$/,
70685
71439
  label: "/v1/workspaces/:workspaceId/github/app"
70686
71440
  },
71441
+ {
71442
+ pattern: /^\/v1\/workspaces\/[^/]+\/github\/action-policies$/,
71443
+ label: "/v1/workspaces/:workspaceId/github/action-policies"
71444
+ },
70687
71445
  {
70688
71446
  pattern: /^\/v1\/workspaces\/[^/]+\/github\/repositories$/,
70689
71447
  label: "/v1/workspaces/:workspaceId/github/repositories"
@@ -71090,6 +71848,6 @@ export {
71090
71848
  validateGitHubRepositorySelectionShapes,
71091
71849
  validateToolRefs,
71092
71850
  withDefaultEnabledCapabilityMcpTools,
71093
- workflowIdForSession3 as workflowIdForSession
71851
+ workflowIdForSession4 as workflowIdForSession
71094
71852
  };
71095
- //# sourceMappingURL=chunk-XXH7DW34.js.map
71853
+ //# sourceMappingURL=chunk-3VUEFGWS.js.map