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

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) {
@@ -24434,7 +25045,9 @@ function registerWorkspaceOrchestrationTools(server, deps, grant, can, callerSes
24434
25045
  ),
24435
25046
  instructions: z43.string().min(1).max(SESSION_INSTRUCTIONS_MAX_CHARACTERS).optional(),
24436
25047
  goal: z43.unknown().optional(),
24437
- resources: z43.array(z43.unknown()).optional(),
25048
+ resources: z43.array(z43.unknown()).optional().describe(
25049
+ "Omit to inherit parent repositories only. Files are never inherited automatically; explicitly include file resources to attach them. An empty array inherits no resources."
25050
+ ),
24438
25051
  tools: z43.array(z43.unknown()).optional(),
24439
25052
  mcpServers: z43.array(z43.unknown()).optional(),
24440
25053
  variableSetId: z43.string().uuid().optional(),
@@ -25346,7 +25959,7 @@ function registerGitHubConnectTool(server, deps, grant, options, json) {
25346
25959
  const installations = await listWorkspaceGitHubInstallationBindings(deps, grant.workspaceId);
25347
25960
  const status = githubBindingStatus(true, installations);
25348
25961
  const baseUrl = githubBrowserBaseUrl(settings, options.requestOrigin);
25349
- const state = baseUrl && hasPermission10(grant.permissions, "github:manage") ? createSignedState6(deps.githubStateSecret, {
25962
+ const state = baseUrl && hasPermission11(grant.permissions, "github:manage") ? createSignedState6(deps.githubStateSecret, {
25350
25963
  accountId: grant.accountId,
25351
25964
  workspaceId: grant.workspaceId,
25352
25965
  intent: "installation_authority",
@@ -25372,10 +25985,10 @@ function registerGitHubConnectTool(server, deps, grant, options, json) {
25372
25985
  }
25373
25986
  function requireVariableSetsUseForMcpAttachment(grant, variableSetId) {
25374
25987
  if (variableSetId === void 0) return;
25375
- if (!hasPermission10(grant.permissions, "variable-sets:attach")) {
25988
+ if (!hasPermission11(grant.permissions, "variable-sets:attach")) {
25376
25989
  throw new Error("missing permission: variable-sets:attach");
25377
25990
  }
25378
- if (variableSetId !== null && !hasPermission10(grant.permissions, "variable-sets:use")) {
25991
+ if (variableSetId !== null && !hasPermission11(grant.permissions, "variable-sets:use")) {
25379
25992
  throw new Error("missing permission: variable-sets:use");
25380
25993
  }
25381
25994
  }
@@ -25383,10 +25996,10 @@ function requireVariableSetsUseForMcpAttachments(grant, selection) {
25383
25996
  const singular = selection.variableSetId ?? selection.environmentId;
25384
25997
  const variableSetIds = selection.variableSetIds ?? (singular ? [singular] : void 0);
25385
25998
  if (variableSetIds === void 0) return;
25386
- if (!hasPermission10(grant.permissions, "variable-sets:attach")) {
25999
+ if (!hasPermission11(grant.permissions, "variable-sets:attach")) {
25387
26000
  throw new HTTPException19(403, { message: "missing permission: variable-sets:attach" });
25388
26001
  }
25389
- if (variableSetIds.length > 0 && !hasPermission10(grant.permissions, "variable-sets:use")) {
26002
+ if (variableSetIds.length > 0 && !hasPermission11(grant.permissions, "variable-sets:use")) {
25390
26003
  throw new HTTPException19(403, { message: "missing permission: variable-sets:use" });
25391
26004
  }
25392
26005
  }
@@ -25432,6 +26045,7 @@ var SESSION_DISCOVERY_PREVIEW_OMISSION_REASON = "aggregatePreviewBudget";
25432
26045
  var SESSION_DISCOVERY_PAGE_MAX_BYTES = 128e3;
25433
26046
  var SESSION_DISCOVERY_PREVIEW_DRILL_DOWN_TOOL = "session_events";
25434
26047
  var SESSION_DISCOVERY_PREVIEW_DRILL_DOWN_BASE_INPUT = {
26048
+ view: "debug",
25435
26049
  direction: "before",
25436
26050
  limit: 1,
25437
26051
  mode: "monitoring",
@@ -25798,171 +26412,6 @@ async function withMcpEffectivePolicy(deps, workspaceId, subjectId, session) {
25798
26412
  return sessionWithEffectiveToolPolicy(session, workspaceServerIds, workspaceDefaultServerIds);
25799
26413
  }
25800
26414
 
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
26415
  // src/app.ts
25967
26416
  import { boundedMcpRequest, McpPayloadTooLargeError } from "@opengeni/runtime/mcp-network";
25968
26417
  import {
@@ -25971,7 +26420,7 @@ import {
25971
26420
  } from "@opengeni/runtime/sandbox";
25972
26421
 
25973
26422
  // src/http/auth.ts
25974
- import { resolveFirstPartyDelegationSecret } from "@opengeni/config";
26423
+ import { resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret2 } from "@opengeni/config";
25975
26424
  import { verifyDelegatedAccessToken } from "@opengeni/contracts";
25976
26425
 
25977
26426
  // src/routes/install.ts
@@ -26255,7 +26704,7 @@ async function isAuthorized(c, settings) {
26255
26704
  if (expected && constantTimeEqual(bearer, expected)) {
26256
26705
  return true;
26257
26706
  }
26258
- const delegationSecret = resolveFirstPartyDelegationSecret(settings);
26707
+ const delegationSecret = resolveFirstPartyDelegationSecret2(settings);
26259
26708
  return Boolean(
26260
26709
  c.req.path.startsWith("/v1/") && bearer && delegationSecret && await verifyDelegatedAccessToken(delegationSecret, bearer)
26261
26710
  );
@@ -26318,7 +26767,7 @@ import {
26318
26767
  } from "@opengeni/core";
26319
26768
 
26320
26769
  // src/http/common.ts
26321
- import { getSession as getSession3 } from "@opengeni/db";
26770
+ import { getSession as getSession4 } from "@opengeni/db";
26322
26771
  import { HTTPException as HTTPException22 } from "hono/http-exception";
26323
26772
  function boundedLimit(raw) {
26324
26773
  const limit = Number(raw ?? 100);
@@ -26328,7 +26777,7 @@ function boundedLimit(raw) {
26328
26777
  return Math.min(500, Math.max(1, Math.floor(limit)));
26329
26778
  }
26330
26779
  async function assertSessionExists(db, workspaceId, sessionId) {
26331
- if (!await getSession3(db, workspaceId, sessionId)) {
26780
+ if (!await getSession4(db, workspaceId, sessionId)) {
26332
26781
  throw new HTTPException22(404, { message: "session not found" });
26333
26782
  }
26334
26783
  }
@@ -29152,7 +29601,7 @@ function registerSuperGrokRoutes(app, deps) {
29152
29601
  }
29153
29602
 
29154
29603
  // src/routes/connections.ts
29155
- import { createHash as createHash13, createHmac as createHmac4 } from "crypto";
29604
+ import { createHash as createHash12, createHmac as createHmac4 } from "crypto";
29156
29605
  import {
29157
29606
  WORKSPACE_OPENROUTER_CONNECTION_DOMAIN,
29158
29607
  WORKSPACE_OPENROUTER_CONNECTION_ROLE,
@@ -29250,7 +29699,7 @@ import { HTTPException as HTTPException29 } from "hono/http-exception";
29250
29699
 
29251
29700
  // src/integrations/provider-oauth.ts
29252
29701
  import { Buffer as Buffer4 } from "buffer";
29253
- import { createHash as createHash11, randomBytes as randomBytes6 } from "crypto";
29702
+ import { createHash as createHash10, randomBytes as randomBytes6 } from "crypto";
29254
29703
  import {
29255
29704
  integrationDefinitionProviderDomain as integrationDefinitionProviderDomain2,
29256
29705
  integrationDefinitionById
@@ -29367,7 +29816,7 @@ async function startApiIntegrationProviderOAuth(deps, input) {
29367
29816
  authorizationUrl.searchParams.set("code_challenge_method", "S256");
29368
29817
  authorizationUrl.searchParams.set(
29369
29818
  "code_challenge",
29370
- createHash11("sha256").update(verifier).digest("base64url")
29819
+ createHash10("sha256").update(verifier).digest("base64url")
29371
29820
  );
29372
29821
  authorizationUrl.searchParams.set("prompt", "select_account");
29373
29822
  if (definition.provider.id === "google") {
@@ -29597,7 +30046,7 @@ function providerClientKeys(definition) {
29597
30046
  ]);
29598
30047
  }
29599
30048
  function providerDefinitionFingerprint(definition) {
29600
- return createHash11("sha256").update(
30049
+ return createHash10("sha256").update(
29601
30050
  JSON.stringify({
29602
30051
  id: definition.id,
29603
30052
  provider: definition.provider,
@@ -29840,7 +30289,7 @@ function numberValue4(value) {
29840
30289
  }
29841
30290
 
29842
30291
  // src/integrations/personal-github.ts
29843
- import { createHash as createHash12, randomBytes as randomBytes7, randomUUID as randomUUID3 } from "crypto";
30292
+ import { createHash as createHash11, randomBytes as randomBytes7, randomUUID as randomUUID3 } from "crypto";
29844
30293
  import { personalGitHubOAuthCallbackUrl } from "@opengeni/config";
29845
30294
  import {
29846
30295
  PERSONAL_GITHUB_AUTHORIZATION_URL,
@@ -29886,7 +30335,7 @@ var PersonalGitHubCallbackError = class extends Error {
29886
30335
  }
29887
30336
  };
29888
30337
  function personalGitHubClientMarker(clientId) {
29889
- return createHash12("sha256").update(clientId).digest("hex").slice(0, 32);
30338
+ return createHash11("sha256").update(clientId).digest("hex").slice(0, 32);
29890
30339
  }
29891
30340
  function requirePersonalGitHubOAuthSettings(settings) {
29892
30341
  if (!settings.githubPersonalOauthEnabled) {
@@ -29989,7 +30438,7 @@ async function startPersonalGitHubOAuth(deps, input) {
29989
30438
  authorizationUrl.searchParams.set("state", state);
29990
30439
  authorizationUrl.searchParams.set(
29991
30440
  "code_challenge",
29992
- createHash12("sha256").update(verifier).digest("base64url")
30441
+ createHash11("sha256").update(verifier).digest("base64url")
29993
30442
  );
29994
30443
  authorizationUrl.searchParams.set("code_challenge_method", "S256");
29995
30444
  return PersonalGitHubOAuthStartResponse.parse({
@@ -30582,7 +31031,7 @@ function registerConnectionRoutes(app, deps) {
30582
31031
  accountId: state.accountId,
30583
31032
  workspaceId: state.workspaceId,
30584
31033
  subjectId: state.subjectId,
30585
- callbackDigest: createHash13("sha256").update(state.nonce).digest("hex"),
31034
+ callbackDigest: createHash12("sha256").update(state.nonce).digest("hex"),
30586
31035
  installMode: state.connectionId ? "reinstall" : "connect",
30587
31036
  ...failure
30588
31037
  });
@@ -33308,7 +33757,7 @@ import { buildFleetContextForSession as buildFleetContextForSession2, swapActive
33308
33757
 
33309
33758
  // src/sandbox/machines.ts
33310
33759
  import {
33311
- getSession as getSession4,
33760
+ getSession as getSession5,
33312
33761
  getLiveEnrollmentConnection as getLiveEnrollmentConnection3,
33313
33762
  listEnrollments as listEnrollments3,
33314
33763
  listSandboxes,
@@ -33441,7 +33890,7 @@ async function listMachines(services, input) {
33441
33890
  let activeEpoch = 0;
33442
33891
  let session = null;
33443
33892
  if (input.sessionId) {
33444
- session = await getSession4(db, workspaceId, input.sessionId);
33893
+ session = await getSession5(db, workspaceId, input.sessionId);
33445
33894
  if (session) {
33446
33895
  const pointer = await readActiveSandbox2(db, workspaceId, input.sessionId);
33447
33896
  activeSandboxId = pointer?.activeSandboxId ?? null;
@@ -35302,7 +35751,7 @@ function browserIdentityRouteError(error) {
35302
35751
  import { randomUUID as randomUUID5 } from "crypto";
35303
35752
  import {
35304
35753
  environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes4,
35305
- resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret2,
35754
+ resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret3,
35306
35755
  resolveStreamTokenSecret as resolveStreamTokenSecret2
35307
35756
  } from "@opengeni/config";
35308
35757
  import {
@@ -35396,7 +35845,7 @@ import {
35396
35845
  getExternalAuthPreparation,
35397
35846
  getProtectedAuthFillPreparation,
35398
35847
  getSiteAuthConnection,
35399
- getSession as getSession5,
35848
+ getSession as getSession6,
35400
35849
  InteractionResourceConflictError,
35401
35850
  InteractionResourceNotFoundError,
35402
35851
  InteractionResourceStateError,
@@ -35695,7 +36144,7 @@ async function withInteractionHolderHeartbeat(deps, input, run) {
35695
36144
  }
35696
36145
 
35697
36146
  // src/browser-state-authority.ts
35698
- import { createCipheriv, createDecipheriv, createHash as createHash14, createHmac as createHmac6, randomBytes as randomBytes9 } from "crypto";
36147
+ import { createCipheriv, createDecipheriv, createHash as createHash13, createHmac as createHmac6, randomBytes as randomBytes9 } from "crypto";
35699
36148
  import { BROWSER_PROFILE_ARTIFACT_FORMAT } from "@opengeni/contracts";
35700
36149
  var ROOT_KEY_BYTES = 32;
35701
36150
  var DATA_KEY_BYTES = 32;
@@ -35759,7 +36208,7 @@ function browserStateObjectKey(workspaceIdInput, operationIdInput) {
35759
36208
  return `workspaces/${workspaceId}/browser-state/revisions/${operationId}/chromium-profile.ogbs`;
35760
36209
  }
35761
36210
  function browserStateManifestDigest(manifest) {
35762
- return createHash14("sha256").update(canonicalJson2(manifest), "utf8").digest("hex");
36211
+ return createHash13("sha256").update(canonicalJson2(manifest), "utf8").digest("hex");
35763
36212
  }
35764
36213
  function operationScope(input) {
35765
36214
  const base = baseArtifactScope(input);
@@ -35772,8 +36221,8 @@ function operationScope(input) {
35772
36221
  function artifactScope(input) {
35773
36222
  return {
35774
36223
  ...baseArtifactScope(input),
35775
- artifactDigest: sha2562(input.artifactDigest, "browser state artifact digest"),
35776
- contentDigest: sha2562(input.contentDigest, "browser state content digest")
36224
+ artifactDigest: sha256(input.artifactDigest, "browser state artifact digest"),
36225
+ contentDigest: sha256(input.contentDigest, "browser state content digest")
35777
36226
  };
35778
36227
  }
35779
36228
  function baseArtifactScope(input) {
@@ -35817,7 +36266,7 @@ function uuid(value, label) {
35817
36266
  }
35818
36267
  return value.toLowerCase();
35819
36268
  }
35820
- function sha2562(value, label) {
36269
+ function sha256(value, label) {
35821
36270
  if (typeof value !== "string" || !SHA256_PATTERN.test(value)) {
35822
36271
  throw new Error(`${label} is invalid`);
35823
36272
  }
@@ -39798,7 +40247,7 @@ function interactionHolderId(browserSessionId) {
39798
40247
  return `browser-session:${browserSessionId}`;
39799
40248
  }
39800
40249
  function browserAuthorityRoot(deps) {
39801
- const root2 = resolveFirstPartyDelegationSecret2(deps.settings);
40250
+ const root2 = resolveFirstPartyDelegationSecret3(deps.settings);
39802
40251
  if (!root2) {
39803
40252
  throw new HTTPException39(503, {
39804
40253
  message: "browser controller authority is not configured"
@@ -39875,7 +40324,7 @@ async function settleBrowserRevisionCaptureFailure(deps, input, error) {
39875
40324
  });
39876
40325
  }
39877
40326
  async function requireSourceSession(deps, workspaceId, sessionId) {
39878
- const session = await getSession5(deps.db, workspaceId, sessionId);
40327
+ const session = await getSession6(deps.db, workspaceId, sessionId);
39879
40328
  if (!session) throw new BrowserSessionNotFoundError("Associated session not found");
39880
40329
  return session;
39881
40330
  }
@@ -40400,7 +40849,7 @@ function isAbort(error) {
40400
40849
 
40401
40850
  // src/routes/computer-sessions.ts
40402
40851
  import { randomUUID as randomUUID6 } from "crypto";
40403
- import { resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret3, resolveStreamTokenSecret as resolveStreamTokenSecret3 } from "@opengeni/config";
40852
+ import { resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret4, resolveStreamTokenSecret as resolveStreamTokenSecret3 } from "@opengeni/config";
40404
40853
  import {
40405
40854
  BROWSER_CONTROL_WEBSOCKET_BEARER_PREFIX as BROWSER_CONTROL_WEBSOCKET_BEARER_PREFIX2,
40406
40855
  BROWSER_CONTROL_PORT as BROWSER_CONTROL_PORT2,
@@ -40434,7 +40883,7 @@ import {
40434
40883
  getComputerSessionControlRecord as getComputerSessionControlRecord2,
40435
40884
  getLiveEnrollmentConnection as getLiveEnrollmentConnection6,
40436
40885
  getSandbox as getSandbox3,
40437
- getSession as getSession6,
40886
+ getSession as getSession7,
40438
40887
  listComputerSessions,
40439
40888
  prepareComputerSessionCreate,
40440
40889
  prepareComputerSessionEnd,
@@ -41777,7 +42226,7 @@ function interactionHolderId2(computerSessionId) {
41777
42226
  return `computer-session:${computerSessionId}`;
41778
42227
  }
41779
42228
  function controllerAuthorityRoot(deps) {
41780
- const root2 = resolveFirstPartyDelegationSecret3(deps.settings);
42229
+ const root2 = resolveFirstPartyDelegationSecret4(deps.settings);
41781
42230
  if (!root2) {
41782
42231
  throw new HTTPException40(503, {
41783
42232
  message: "interaction controller authority is not configured"
@@ -41786,7 +42235,7 @@ function controllerAuthorityRoot(deps) {
41786
42235
  return root2;
41787
42236
  }
41788
42237
  async function requireSourceSession2(deps, workspaceId, sessionId) {
41789
- const session = await getSession6(deps.db, workspaceId, sessionId);
42238
+ const session = await getSession7(deps.db, workspaceId, sessionId);
41790
42239
  if (!session) throw new ComputerSessionNotFoundError("Associated session not found");
41791
42240
  return session;
41792
42241
  }
@@ -41966,8 +42415,12 @@ function isAbort2(error) {
41966
42415
 
41967
42416
  // src/routes/github.ts
41968
42417
  import {
41969
- GitHubAppManifestCreate
42418
+ GitHubActionPoliciesResponse,
42419
+ GitHubActionPolicyActorState,
42420
+ GitHubAppManifestCreate,
42421
+ UpdateGitHubActionPolicyRequest
41970
42422
  } from "@opengeni/contracts";
42423
+ import { PersonalGitHubConnectionMetadata as PersonalGitHubConnectionMetadata2 } from "@opengeni/contracts/personal-github";
41971
42424
  import {
41972
42425
  ListGitHubRepositoryBranchesQuery,
41973
42426
  VerifyPublicGitHubRepositoryRefRequest
@@ -41976,7 +42429,8 @@ import { parseCanonicalGitHubRepositoryUrl } from "@opengeni/contracts/github-re
41976
42429
  import {
41977
42430
  bindAuthorizedGitHubInstallationRepositories,
41978
42431
  deleteGitHubInstallationBinding,
41979
- GitHubInstallationAuthorityCommitError
42432
+ GitHubInstallationAuthorityCommitError,
42433
+ listGitHubInstallationAccessForWorkspace as listGitHubInstallationAccessForWorkspace2
41980
42434
  } from "@opengeni/db";
41981
42435
  import {
41982
42436
  authorizeGitHubInstallationBinding,
@@ -42000,7 +42454,15 @@ import {
42000
42454
  } from "@opengeni/github";
42001
42455
  import { deleteCookie, setCookie } from "hono/cookie";
42002
42456
  import { HTTPException as HTTPException41 } from "hono/http-exception";
42003
- import { hasPermission as hasPermission16, requireAccessGrant as requireAccessGrant16 } from "@opengeni/core";
42457
+ import {
42458
+ githubAppActionPolicyActor,
42459
+ hasPermission as hasPermission16,
42460
+ listGitHubActionPolicyActors,
42461
+ personalGitHubActionPolicyActor,
42462
+ requireAccessGrant as requireAccessGrant16,
42463
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization8,
42464
+ updateGitHubActionPolicyGroup
42465
+ } from "@opengeni/core";
42004
42466
  var githubStateCookie = "opengeni_github_state";
42005
42467
  var githubBindingStateMaxAgeSeconds = 10 * 60;
42006
42468
  var legacyInstallationChooserDisabledMessage = "The legacy repository-admin GitHub installation chooser is disabled; use the GitHub owner-consent connect flow";
@@ -42041,6 +42503,89 @@ function registerGitHubRoutes(app, deps) {
42041
42503
  missing: setupMode === "operator" ? missing : []
42042
42504
  });
42043
42505
  });
42506
+ app.get("/v1/workspaces/:workspaceId/github/action-policies", async (c) => {
42507
+ const workspaceId = c.req.param("workspaceId");
42508
+ const grant = await requireAccessGrant16(c, deps, workspaceId, "github:use");
42509
+ const [installations, personalConnections] = await Promise.all([
42510
+ listGitHubInstallationAccessForWorkspace2(db, grant.workspaceId),
42511
+ listPersonalGitHubConnections(deps, {
42512
+ workspaceId: grant.workspaceId,
42513
+ subjectId: grant.subjectId
42514
+ })
42515
+ ]);
42516
+ const personalConnection = canonicalPersonalGitHubPolicyConnection(personalConnections);
42517
+ const actors = [
42518
+ ...installations.map(githubAppActionPolicyActor),
42519
+ ...personalConnection ? [
42520
+ personalGitHubActionPolicyActor({
42521
+ connectionId: personalConnection.id,
42522
+ githubLogin: PersonalGitHubConnectionMetadata2.parse(personalConnection.metadata).githubLogin
42523
+ })
42524
+ ] : []
42525
+ ];
42526
+ return c.json(
42527
+ GitHubActionPoliciesResponse.parse({
42528
+ enabled: settings.githubRestMcpEnabled,
42529
+ actors: await listGitHubActionPolicyActors(db, {
42530
+ accountId: grant.accountId,
42531
+ workspaceId: grant.workspaceId,
42532
+ actors
42533
+ })
42534
+ })
42535
+ );
42536
+ });
42537
+ app.patch("/v1/workspaces/:workspaceId/github/action-policies", async (c) => {
42538
+ const workspaceId = c.req.param("workspaceId");
42539
+ const payload = UpdateGitHubActionPolicyRequest.parse(await c.req.json());
42540
+ const actor = payload.actor;
42541
+ if (actor.kind === "workspace_app") {
42542
+ const grant = await requireAccessGrant16(c, deps, workspaceId, "github:manage");
42543
+ const installations = await listGitHubInstallationAccessForWorkspace2(db, grant.workspaceId);
42544
+ const installation = installations.find(
42545
+ (candidate) => candidate.installationId === actor.installationId
42546
+ );
42547
+ if (!installation || installation.accountId !== grant.accountId) {
42548
+ throw new HTTPException41(404, { message: "GitHub installation not found" });
42549
+ }
42550
+ return c.json(
42551
+ GitHubActionPolicyActorState.parse(
42552
+ await updateGitHubActionPolicyGroup(db, {
42553
+ accountId: grant.accountId,
42554
+ workspaceId: grant.workspaceId,
42555
+ subjectId: grant.subjectId,
42556
+ actor: githubAppActionPolicyActor(installation),
42557
+ group: payload.group,
42558
+ decision: payload.decision
42559
+ })
42560
+ )
42561
+ );
42562
+ }
42563
+ const access = await requireAccessGrantAuthorization8(c, deps, workspaceId, "connections:write");
42564
+ assertPersonalConnectionOwnerPrincipal(access, "My GitHub action policy");
42565
+ const connections = await listPersonalGitHubConnections(deps, {
42566
+ workspaceId: access.grant.workspaceId,
42567
+ subjectId: access.grant.subjectId
42568
+ });
42569
+ const connection = connections.find((candidate) => candidate.id === actor.connectionId);
42570
+ if (!connection) {
42571
+ throw new HTTPException41(404, { message: "personal GitHub connection not found" });
42572
+ }
42573
+ return c.json(
42574
+ GitHubActionPolicyActorState.parse(
42575
+ await updateGitHubActionPolicyGroup(db, {
42576
+ accountId: access.grant.accountId,
42577
+ workspaceId: access.grant.workspaceId,
42578
+ subjectId: access.grant.subjectId,
42579
+ actor: personalGitHubActionPolicyActor({
42580
+ connectionId: connection.id,
42581
+ githubLogin: PersonalGitHubConnectionMetadata2.parse(connection.metadata).githubLogin
42582
+ }),
42583
+ group: payload.group,
42584
+ decision: payload.decision
42585
+ })
42586
+ )
42587
+ );
42588
+ });
42044
42589
  app.get("/v1/workspaces/:workspaceId/github/connect", async (c) => {
42045
42590
  const workspaceId = c.req.param("workspaceId");
42046
42591
  const state = c.req.query("state");
@@ -42540,6 +43085,20 @@ function githubInstallationSettingsUrl(installation) {
42540
43085
  }
42541
43086
  return new URL(`https://github.com/settings/installations/${installation.installationId}`);
42542
43087
  }
43088
+ function canonicalPersonalGitHubPolicyConnection(connections) {
43089
+ const statusRank = {
43090
+ active: 0,
43091
+ needs_reauth: 1,
43092
+ error: 2,
43093
+ revoked: 3
43094
+ };
43095
+ return [...connections].sort((left, right) => {
43096
+ const rank = (statusRank[left.status] ?? 4) - (statusRank[right.status] ?? 4);
43097
+ if (rank !== 0) return rank;
43098
+ const updated = right.updatedAt.localeCompare(left.updatedAt);
43099
+ return updated !== 0 ? updated : right.id.localeCompare(left.id);
43100
+ })[0] ?? null;
43101
+ }
42543
43102
  function redirectToExactGitHubAuthorization(c, deps, sourceState, installationId) {
42544
43103
  const payload = readSignedState11(sourceState, deps.githubStateSecret);
42545
43104
  const clientId = deps.settings.githubClientId?.trim();
@@ -42764,7 +43323,7 @@ import {
42764
43323
  } from "@opengeni/contracts/github-repository-contracts";
42765
43324
  import {
42766
43325
  requireAccessGrant as requireAccessGrant17,
42767
- requireAccessGrantAuthorization as requireAccessGrantAuthorization8
43326
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization9
42768
43327
  } from "@opengeni/core";
42769
43328
  import {
42770
43329
  getPersonalGitHubRepositorySelectionState as getPersonalGitHubRepositorySelectionState2,
@@ -42887,12 +43446,12 @@ async function listLivePersonalGitHubRepositoryBranches(deps, input, services =
42887
43446
  null,
42888
43447
  services.getSelectionState
42889
43448
  );
42890
- const [owner, repositoryName] = current.repository.fullName.split("/");
42891
- if (!owner || !repositoryName) {
43449
+ const [owner2, repositoryName] = current.repository.fullName.split("/");
43450
+ if (!owner2 || !repositoryName) {
42892
43451
  throw new PersonalGitHubRepositoryProviderError("invalid_provider_response");
42893
43452
  }
42894
43453
  const url = new URL(
42895
- `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repositoryName)}/branches`,
43454
+ `/repos/${encodeURIComponent(owner2)}/${encodeURIComponent(repositoryName)}/branches`,
42896
43455
  PERSONAL_GITHUB_API_ORIGIN
42897
43456
  );
42898
43457
  url.searchParams.set("page", String(input.query.cursor));
@@ -42951,12 +43510,12 @@ async function verifyLivePersonalGitHubRepositories(deps, input) {
42951
43510
  const connection = await requirePersonalGitHubRepositoryConnection(deps, input);
42952
43511
  const verified = [];
42953
43512
  for (const selection of input.repositories) {
42954
- const [owner, repositoryName] = selection.fullName.split("/");
42955
- if (!owner || !repositoryName) {
43513
+ const [owner2, repositoryName] = selection.fullName.split("/");
43514
+ if (!owner2 || !repositoryName) {
42956
43515
  throw new PersonalGitHubRepositoryProviderError("invalid_provider_response");
42957
43516
  }
42958
43517
  const url = new URL(
42959
- `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repositoryName)}`,
43518
+ `/repos/${encodeURIComponent(owner2)}/${encodeURIComponent(repositoryName)}`,
42960
43519
  PERSONAL_GITHUB_API_ORIGIN
42961
43520
  );
42962
43521
  const payload = await personalGitHubProviderJson(
@@ -43265,7 +43824,7 @@ function registerPersonalGitHubRoutes(app, deps) {
43265
43824
  });
43266
43825
  app.post("/v1/workspaces/:workspaceId/connections/github/oauth/start", async (c) => {
43267
43826
  const workspaceId = c.req.param("workspaceId");
43268
- const access = await requireAccessGrantAuthorization8(c, deps, workspaceId, "connections:write");
43827
+ const access = await requireAccessGrantAuthorization9(c, deps, workspaceId, "connections:write");
43269
43828
  assertPersonalConnectionOwnerPrincipal(access, "My GitHub account");
43270
43829
  const payload = PersonalGitHubOAuthStartRequest.parse(await c.req.json().catch(() => ({})));
43271
43830
  return c.json(
@@ -43279,7 +43838,7 @@ function registerPersonalGitHubRoutes(app, deps) {
43279
43838
  });
43280
43839
  app.post("/v1/workspaces/:workspaceId/connections/:connectionId/github/reconnect", async (c) => {
43281
43840
  const workspaceId = c.req.param("workspaceId");
43282
- const access = await requireAccessGrantAuthorization8(c, deps, workspaceId, "connections:write");
43841
+ const access = await requireAccessGrantAuthorization9(c, deps, workspaceId, "connections:write");
43283
43842
  assertPersonalConnectionOwnerPrincipal(access, "My GitHub account");
43284
43843
  const payload = PersonalGitHubOAuthStartRequest.omit({ connectionId: true }).parse(
43285
43844
  await c.req.json().catch(() => ({}))
@@ -43298,7 +43857,7 @@ function registerPersonalGitHubRoutes(app, deps) {
43298
43857
  async (c) => {
43299
43858
  const workspaceId = c.req.param("workspaceId");
43300
43859
  const connectionId = c.req.param("connectionId");
43301
- const access = await requireAccessGrantAuthorization8(
43860
+ const access = await requireAccessGrantAuthorization9(
43302
43861
  c,
43303
43862
  deps,
43304
43863
  workspaceId,
@@ -43349,7 +43908,7 @@ function registerPersonalGitHubRoutes(app, deps) {
43349
43908
  async (c) => {
43350
43909
  const workspaceId = c.req.param("workspaceId");
43351
43910
  const connectionId = c.req.param("connectionId");
43352
- const access = await requireAccessGrantAuthorization8(
43911
+ const access = await requireAccessGrantAuthorization9(
43353
43912
  c,
43354
43913
  deps,
43355
43914
  workspaceId,
@@ -43396,7 +43955,7 @@ function registerPersonalGitHubRoutes(app, deps) {
43396
43955
  async (c) => {
43397
43956
  const workspaceId = c.req.param("workspaceId");
43398
43957
  const connectionId = c.req.param("connectionId");
43399
- const access = await requireAccessGrantAuthorization8(
43958
+ const access = await requireAccessGrantAuthorization9(
43400
43959
  c,
43401
43960
  deps,
43402
43961
  workspaceId,
@@ -43428,7 +43987,7 @@ function registerPersonalGitHubRoutes(app, deps) {
43428
43987
  async (c) => {
43429
43988
  const workspaceId = c.req.param("workspaceId");
43430
43989
  const connectionId = c.req.param("connectionId");
43431
- const access = await requireAccessGrantAuthorization8(
43990
+ const access = await requireAccessGrantAuthorization9(
43432
43991
  c,
43433
43992
  deps,
43434
43993
  workspaceId,
@@ -43540,7 +44099,7 @@ function canonicalPersonalGitHubConnection(connections) {
43540
44099
  import {
43541
44100
  PERSONAL_GITHUB_API_ORIGIN as PERSONAL_GITHUB_API_ORIGIN2,
43542
44101
  PERSONAL_GITHUB_PROVIDER_DOMAIN as PERSONAL_GITHUB_PROVIDER_DOMAIN4,
43543
- PersonalGitHubConnectionMetadata as PersonalGitHubConnectionMetadata2,
44102
+ PersonalGitHubConnectionMetadata as PersonalGitHubConnectionMetadata3,
43544
44103
  isPersonalGitHubConnection as isPersonalGitHubConnection4
43545
44104
  } from "@opengeni/contracts/personal-github";
43546
44105
  import {
@@ -43837,7 +44396,7 @@ async function assertCurrentConnectionAndSelection(deps, claims, repository) {
43837
44396
  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
44397
  throw new Error("personal GitHub connection changed");
43839
44398
  }
43840
- const metadata = PersonalGitHubConnectionMetadata2.parse(connection.metadata);
44399
+ const metadata = PersonalGitHubConnectionMetadata3.parse(connection.metadata);
43841
44400
  if (metadata.credentialBindingId !== claims.credentialBindingId) {
43842
44401
  throw new Error("personal GitHub credential binding changed");
43843
44402
  }
@@ -44092,7 +44651,7 @@ import {
44092
44651
  import { HTTPException as HTTPException44 } from "hono/http-exception";
44093
44652
 
44094
44653
  // src/integrations/api-integrations.ts
44095
- import { createHash as createHash15 } from "crypto";
44654
+ import { createHash as createHash14 } from "crypto";
44096
44655
  import {
44097
44656
  compileGraphqlRevision,
44098
44657
  compileOpenApiRevision,
@@ -44302,7 +44861,7 @@ function resolvedPreview(input) {
44302
44861
  };
44303
44862
  }
44304
44863
  function integrationInstallationIdentity(protocol, label, locator) {
44305
- const hash = createHash15("sha256").update(`${protocol}\0${locator}`).digest("hex").slice(0, 12);
44864
+ const hash = createHash14("sha256").update(`${protocol}\0${locator}`).digest("hex").slice(0, 12);
44306
44865
  const slug = label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "integration";
44307
44866
  return {
44308
44867
  sourceDerivedDefinitionId: `${slug}-${hash}`,
@@ -44668,7 +45227,7 @@ import {
44668
45227
  import {
44669
45228
  hasPermission as hasPermission17,
44670
45229
  requireAccessGrant as requireAccessGrant19,
44671
- requireAccessGrantAuthorization as requireAccessGrantAuthorization9
45230
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization10
44672
45231
  } from "@opengeni/core";
44673
45232
  import {
44674
45233
  configureIntegrationFacet as configureIntegrationFacet2,
@@ -44713,7 +45272,7 @@ function registerIntegrationFacetRoutes(app, deps) {
44713
45272
  "/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/facets/:facetKey/source",
44714
45273
  async (c) => {
44715
45274
  const workspaceId = c.req.param("workspaceId");
44716
- const authorization = await requireAccessGrantAuthorization9(
45275
+ const authorization = await requireAccessGrantAuthorization10(
44717
45276
  c,
44718
45277
  deps,
44719
45278
  workspaceId,
@@ -44966,7 +45525,7 @@ import {
44966
45525
  requireFreshAccessGrant,
44967
45526
  requirePermission as requirePermission3,
44968
45527
  requireSessionAuthorization as requireSessionAuthorization6,
44969
- workflowIdForSession
45528
+ workflowIdForSession as workflowIdForSession2
44970
45529
  } from "@opengeni/core";
44971
45530
  import { publishDurableSessionEvents as publishDurableSessionEvents2 } from "@opengeni/events";
44972
45531
  import { HTTPException as HTTPException46 } from "hono/http-exception";
@@ -46334,7 +46893,7 @@ function registerInteractionResourceRoutes(app, deps) {
46334
46893
  workspaceId,
46335
46894
  sessionId: approvalTarget.sessionId,
46336
46895
  eventId: accepted.event.id,
46337
- workflowId: workflowIdForSession(approvalTarget.sessionId),
46896
+ workflowId: workflowIdForSession2(approvalTarget.sessionId),
46338
46897
  workflowWakeRevision: accepted.workflowWakeRevision
46339
46898
  });
46340
46899
  const response = InteractionInterventionMutationResponse.parse({
@@ -48458,7 +49017,7 @@ function positiveInteger3(value) {
48458
49017
  }
48459
49018
 
48460
49019
  // src/routes/packs.ts
48461
- import { createHash as createHash16 } from "crypto";
49020
+ import { createHash as createHash15 } from "crypto";
48462
49021
  import {
48463
49022
  EnablePackRequest,
48464
49023
  InstallPackRequest,
@@ -48644,7 +49203,7 @@ function registerPackRoutes(app, deps) {
48644
49203
  packVersion: pack.version,
48645
49204
  ...preview.variableSetId ? { variableSetId: preview.variableSetId } : {}
48646
49205
  };
48647
- const requestDigest = sha2563(
49206
+ const requestDigest = sha2562(
48648
49207
  stableJson4({
48649
49208
  packId: pack.id,
48650
49209
  manifestDigest: preview.manifestDigest,
@@ -48823,7 +49382,7 @@ function registerPackRoutes(app, deps) {
48823
49382
  const grant = await requireAccessGrant24(c, deps, workspaceId, "capabilities:manage");
48824
49383
  const pack = await requirePack(db, workspaceId, c.req.param("packId"));
48825
49384
  const payload = UninstallPackRequest.parse(await c.req.json());
48826
- const requestDigest = sha2563(
49385
+ const requestDigest = sha2562(
48827
49386
  stableJson4({
48828
49387
  packId: pack.id,
48829
49388
  expectedInstallationVersion: payload.expectedInstallationVersion
@@ -49067,8 +49626,8 @@ function packFailureCode(error) {
49067
49626
  if (error instanceof CapabilityComponentVersionConflictError) return "component_version_conflict";
49068
49627
  return "component_install_failed";
49069
49628
  }
49070
- function sha2563(value) {
49071
- return createHash16("sha256").update(value).digest("hex");
49629
+ function sha2562(value) {
49630
+ return createHash15("sha256").update(value).digest("hex");
49072
49631
  }
49073
49632
  async function requirePack(db, workspaceId, packId) {
49074
49633
  const pack = await resolveCapabilityPack(db, workspaceId, packId);
@@ -49112,7 +49671,7 @@ async function validateDocumentBaseIds(db, workspaceId, documentBaseIds) {
49112
49671
  }
49113
49672
 
49114
49673
  // src/routes/plugins.ts
49115
- import { createHash as createHash17 } from "crypto";
49674
+ import { createHash as createHash16 } from "crypto";
49116
49675
  import {
49117
49676
  createPinnedIntegrationTransport as createPinnedIntegrationTransport2,
49118
49677
  fetchIntegrationSourceDocument as fetchIntegrationSourceDocument2
@@ -49165,10 +49724,10 @@ var githubMetadataMaxBytes = 4 * 1024 * 1024;
49165
49724
  var githubBlobResponseMaxBytes = 512 * 1024;
49166
49725
  function createGitHubSkillSourceClient(settings, requestJson = (path, maxBytes, label) => githubJson(settings, path, maxBytes, label)) {
49167
49726
  return {
49168
- resolveCommit: async (owner, repository, ref) => {
49727
+ resolveCommit: async (owner2, repository, ref) => {
49169
49728
  const payload = recordValue(
49170
49729
  await requestJson(
49171
- `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/commits/${encodeURIComponent(ref)}`,
49730
+ `/repos/${encodeURIComponent(owner2)}/${encodeURIComponent(repository)}/commits/${encodeURIComponent(ref)}`,
49172
49731
  githubMetadataMaxBytes,
49173
49732
  "GitHub Skill commit"
49174
49733
  ),
@@ -49178,10 +49737,10 @@ function createGitHubSkillSourceClient(settings, requestJson = (path, maxBytes,
49178
49737
  if (!sha) throw new Error("GitHub commit response omitted sha");
49179
49738
  return sha.toLowerCase();
49180
49739
  },
49181
- listTree: async (owner, repository, commit) => {
49740
+ listTree: async (owner2, repository, commit) => {
49182
49741
  const payload = recordValue(
49183
49742
  await requestJson(
49184
- `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/git/trees/${encodeURIComponent(commit)}?recursive=1`,
49743
+ `/repos/${encodeURIComponent(owner2)}/${encodeURIComponent(repository)}/git/trees/${encodeURIComponent(commit)}?recursive=1`,
49185
49744
  githubMetadataMaxBytes,
49186
49745
  "GitHub Skill tree"
49187
49746
  ),
@@ -49204,10 +49763,10 @@ function createGitHubSkillSourceClient(settings, requestJson = (path, maxBytes,
49204
49763
  return { path, type, mode, sha, size: typeof size === "number" ? size : null };
49205
49764
  });
49206
49765
  },
49207
- readBlob: async (owner, repository, sha) => {
49766
+ readBlob: async (owner2, repository, sha) => {
49208
49767
  const payload = recordValue(
49209
49768
  await requestJson(
49210
- `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repository)}/git/blobs/${encodeURIComponent(sha)}`,
49769
+ `/repos/${encodeURIComponent(owner2)}/${encodeURIComponent(repository)}/git/blobs/${encodeURIComponent(sha)}`,
49211
49770
  githubBlobResponseMaxBytes,
49212
49771
  "GitHub Skill file"
49213
49772
  ),
@@ -49333,7 +49892,7 @@ function registerPluginRoutes(app, deps, overrides = {}) {
49333
49892
  message: "A Plugin component changed after preview. Review the updated bill of materials."
49334
49893
  });
49335
49894
  }
49336
- const requestDigest = sha2564(
49895
+ const requestDigest = sha2563(
49337
49896
  stableJson5({
49338
49897
  url: payload.url,
49339
49898
  expectedManifestDigest: payload.expectedManifestDigest,
@@ -49471,13 +50030,13 @@ async function resolvePluginPackage(input) {
49471
50030
  message: error instanceof Error ? error.message : "Plugin manifest is invalid"
49472
50031
  });
49473
50032
  }
49474
- const manifestDigest = sha2564(stableJson5(manifest));
50033
+ const manifestDigest = sha2563(stableJson5(manifest));
49475
50034
  const components = [];
49476
50035
  for (const component of manifest.components) {
49477
50036
  if (component.kind === "skill") {
49478
50037
  const resolved = await resolveSkillImport(component.url, input.github);
49479
50038
  const capabilityId = portableSkillCapabilityId(resolved.preview);
49480
- const digest3 = sha2564(
50039
+ const digest3 = sha2563(
49481
50040
  stableJson5({
49482
50041
  capabilityId,
49483
50042
  sourceCommit: resolved.preview.sourceCommit,
@@ -49627,7 +50186,7 @@ async function resolvePluginPackage(input) {
49627
50186
  continue;
49628
50187
  }
49629
50188
  const server = configuredMcpServer(input.deps.settings.mcpServers, component.serverId);
49630
- const digest2 = sha2564(stableJson5(safeMcpFacts(server)));
50189
+ const digest2 = sha2563(stableJson5(safeMcpFacts(server)));
49631
50190
  components.push({
49632
50191
  preview: {
49633
50192
  key: component.key,
@@ -49776,7 +50335,7 @@ function safeMcpFacts(server) {
49776
50335
  };
49777
50336
  }
49778
50337
  function integrationDigest(resolved) {
49779
- return sha2564(
50338
+ return sha2563(
49780
50339
  stableJson5({
49781
50340
  capabilityId: resolved.preview.capabilityId,
49782
50341
  revisionId: resolved.preview.revisionId,
@@ -49842,8 +50401,8 @@ function pluginFailureCode(error) {
49842
50401
  if (error instanceof HTTPException51) return `http_${error.status}`;
49843
50402
  return "component_install_failed";
49844
50403
  }
49845
- function sha2564(value) {
49846
- return createHash17("sha256").update(value).digest("hex");
50404
+ function sha2563(value) {
50405
+ return createHash16("sha256").update(value).digest("hex");
49847
50406
  }
49848
50407
 
49849
50408
  // src/routes/skills.ts
@@ -49873,7 +50432,7 @@ import {
49873
50432
  uninstallPortableSkill
49874
50433
  } from "@opengeni/db";
49875
50434
  import { loadSkillLibrarySkill, skillLibraryRepositoryUrl } from "@opengeni/runtime/skill-library";
49876
- import { createHash as createHash18 } from "crypto";
50435
+ import { createHash as createHash17 } from "crypto";
49877
50436
  import { HTTPException as HTTPException52 } from "hono/http-exception";
49878
50437
  function registerSkillRoutes(app, deps, overrides = {}) {
49879
50438
  const github = overrides.github ?? createGitHubSkillSourceClient(deps.settings);
@@ -49908,7 +50467,7 @@ function registerSkillRoutes(app, deps, overrides = {}) {
49908
50467
  path: file.path,
49909
50468
  content: file.content,
49910
50469
  byteSize: bytes.byteLength,
49911
- contentSha256: createHash18("sha256").update(bytes).digest("hex")
50470
+ contentSha256: createHash17("sha256").update(bytes).digest("hex")
49912
50471
  };
49913
50472
  });
49914
50473
  try {
@@ -50178,7 +50737,7 @@ import {
50178
50737
  import { HTTPException as HTTPException54 } from "hono/http-exception";
50179
50738
  import {
50180
50739
  requireAccessGrant as requireAccessGrant28,
50181
- requireAccessGrantAuthorization as requireAccessGrantAuthorization10,
50740
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization11,
50182
50741
  requirePermission as requirePermission7
50183
50742
  } from "@opengeni/core";
50184
50743
  import {
@@ -50219,7 +50778,7 @@ async function parseRigRequest(c, schema, label) {
50219
50778
  function registerRigRoutes(app, deps) {
50220
50779
  const { db, workflowClient } = deps;
50221
50780
  async function requireRigMutation(c, workspaceId, permission) {
50222
- const authorization = await requireAccessGrantAuthorization10(c, deps, workspaceId, permission);
50781
+ const authorization = await requireAccessGrantAuthorization11(c, deps, workspaceId, permission);
50223
50782
  const rigId = c.req.param("rigId");
50224
50783
  if (!rigId) {
50225
50784
  throw new HTTPException54(400, { message: "rig id is required" });
@@ -50297,7 +50856,7 @@ function registerRigRoutes(app, deps) {
50297
50856
  });
50298
50857
  app.post("/v1/workspaces/:workspaceId/rigs", async (c) => {
50299
50858
  const workspaceId = c.req.param("workspaceId");
50300
- const authorization = await requireAccessGrantAuthorization10(c, deps, workspaceId);
50859
+ const authorization = await requireAccessGrantAuthorization11(c, deps, workspaceId);
50301
50860
  const grant = authorization.grant;
50302
50861
  requirePermission7(grant, "rigs:manage");
50303
50862
  const payload = await parseRigRequest(c, CreateRigRequest, "Rig create request");
@@ -50323,7 +50882,7 @@ function registerRigRoutes(app, deps) {
50323
50882
  });
50324
50883
  app.patch("/v1/workspaces/:workspaceId/rigs/:rigId", async (c) => {
50325
50884
  const workspaceId = c.req.param("workspaceId");
50326
- const authorization = await requireAccessGrantAuthorization10(c, deps, workspaceId);
50885
+ const authorization = await requireAccessGrantAuthorization11(c, deps, workspaceId);
50327
50886
  const grant = authorization.grant;
50328
50887
  requirePermission7(grant, "rigs:manage");
50329
50888
  const rig = await requireRigForApi2(db, grant, c.req.param("rigId"));
@@ -50338,7 +50897,7 @@ function registerRigRoutes(app, deps) {
50338
50897
  });
50339
50898
  app.delete("/v1/workspaces/:workspaceId/rigs/:rigId", async (c) => {
50340
50899
  const workspaceId = c.req.param("workspaceId");
50341
- const authorization = await requireAccessGrantAuthorization10(c, deps, workspaceId);
50900
+ const authorization = await requireAccessGrantAuthorization11(c, deps, workspaceId);
50342
50901
  const grant = authorization.grant;
50343
50902
  requirePermission7(grant, "rigs:manage");
50344
50903
  const rig = await requireRigForApi2(db, grant, c.req.param("rigId"));
@@ -50439,6 +50998,7 @@ function registerRigRoutes(app, deps) {
50439
50998
  }
50440
50999
 
50441
51000
  // src/routes/scheduled-tasks.ts
51001
+ import { z as z14 } from "zod";
50442
51002
  import {
50443
51003
  CreateScheduledTaskRequest as CreateScheduledTaskRequest2,
50444
51004
  TriggerScheduledTaskRequest,
@@ -50504,7 +51064,24 @@ function registerScheduledTaskRoutes(app, deps) {
50504
51064
  app.get("/v1/workspaces/:workspaceId/scheduled-tasks", async (c) => {
50505
51065
  const workspaceId = c.req.param("workspaceId");
50506
51066
  const grant = await requireAccessGrant29(c, deps, workspaceId, "scheduled_tasks:run");
50507
- const tasks = await listScheduledTasks2(db, workspaceId, boundedLimit(c.req.query("limit")));
51067
+ const sessionId = c.req.query("sessionId");
51068
+ const offset = Number(c.req.query("offset") ?? 0);
51069
+ if (!Number.isSafeInteger(offset) || offset < 0) {
51070
+ throw new HTTPException55(400, { message: "invalid offset" });
51071
+ }
51072
+ if (sessionId !== void 0) {
51073
+ if (!z14.string().uuid().safeParse(sessionId).success) {
51074
+ throw new HTTPException55(400, { message: "invalid sessionId" });
51075
+ }
51076
+ await requireAccessGrant29(c, deps, workspaceId, "sessions:control");
51077
+ }
51078
+ const tasks = await listScheduledTasks2(
51079
+ db,
51080
+ workspaceId,
51081
+ boundedLimit(c.req.query("limit")),
51082
+ offset,
51083
+ sessionId
51084
+ );
50508
51085
  return c.json(tasks.map((task) => scheduledTaskForGrant2(task, grant)));
50509
51086
  });
50510
51087
  app.get("/v1/workspaces/:workspaceId/scheduled-tasks/:taskId", async (c) => {
@@ -50671,6 +51248,7 @@ function registerScheduledTaskRoutes(app, deps) {
50671
51248
  }
50672
51249
 
50673
51250
  // src/routes/sessions.ts
51251
+ import { scheduledSessionIds } from "@opengeni/db";
50674
51252
  import {
50675
51253
  AcknowledgeStreamRequest,
50676
51254
  ApplySessionGoalRevisionRequest,
@@ -50764,7 +51342,7 @@ import {
50764
51342
  getLiveEnrollmentConnection as getLiveEnrollmentConnection7,
50765
51343
  getRetainedProcess,
50766
51344
  getSandbox as getSandbox4,
50767
- getSession as getSession7,
51345
+ getSession as getSession8,
50768
51346
  readActiveSandbox as readActiveSandbox3,
50769
51347
  deleteSessionTreeIfQuiescent,
50770
51348
  getSessionEvent,
@@ -51586,7 +52164,7 @@ function normalizeExpiry(value) {
51586
52164
  }
51587
52165
 
51588
52166
  // src/routes/sessions.ts
51589
- import { z as z14, ZodError } from "zod";
52167
+ import { z as z15, ZodError } from "zod";
51590
52168
  import {
51591
52169
  NatsControlRpc as NatsControlRpc6,
51592
52170
  negotiateCapabilities,
@@ -51600,7 +52178,7 @@ import {
51600
52178
  getManagedAuthRequestActorEpoch as getManagedAuthRequestActorEpoch2,
51601
52179
  hasPermission as hasPermission19,
51602
52180
  requireAccessGrant as requireAccessGrant30,
51603
- requireAccessGrantAuthorization as requireAccessGrantAuthorization11,
52181
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization12,
51604
52182
  requireFreshAccessGrant as requireFreshAccessGrant2,
51605
52183
  requirePermission as requirePermission8,
51606
52184
  requireSessionAuthorization as requireSessionAuthorization7,
@@ -51635,7 +52213,7 @@ import {
51635
52213
  updateManagedHumanSessionVisibility,
51636
52214
  updateSessionToolPolicy,
51637
52215
  updateSessionTitle as updateSessionTitle2,
51638
- workflowIdForSession as workflowIdForSession2,
52216
+ workflowIdForSession as workflowIdForSession3,
51639
52217
  sessionWithEffectiveToolPolicy as sessionWithEffectiveToolPolicy2,
51640
52218
  workspaceSessionToolPolicyDefaultServerIds as workspaceSessionToolPolicyDefaultServerIds2,
51641
52219
  workspaceSessionToolPolicyServerIds as workspaceSessionToolPolicyServerIds2,
@@ -51899,6 +52477,7 @@ function registerSessionRoutes(app, deps) {
51899
52477
  db,
51900
52478
  settings,
51901
52479
  bus,
52480
+ objectStorage,
51902
52481
  observability: deps.observability
51903
52482
  };
51904
52483
  const workspaceCaptureManifestCache = new WorkspaceCaptureManifestCache();
@@ -51986,7 +52565,7 @@ function registerSessionRoutes(app, deps) {
51986
52565
  const authorizeSessionHttp = async (c, next) => {
51987
52566
  const workspaceId = c.req.param("workspaceId") ?? "";
51988
52567
  const sessionId = c.req.param("sessionId") ?? "";
51989
- if (!z14.string().uuid().safeParse(sessionId).success) {
52568
+ if (!z15.string().uuid().safeParse(sessionId).success) {
51990
52569
  throw new HTTPException57(404, { message: "session not found" });
51991
52570
  }
51992
52571
  const operation = sessionAuthorizationOperationForHttp(
@@ -52027,11 +52606,12 @@ function registerSessionRoutes(app, deps) {
52027
52606
  db,
52028
52607
  settings,
52029
52608
  bus,
52609
+ objectStorage,
52030
52610
  ...deps.establishSandboxSession ? { establishSandboxSession: deps.establishSandboxSession } : {}
52031
52611
  };
52032
52612
  app.get("/v1/workspaces/:workspaceId/session-tenancy/capabilities", async (c) => {
52033
52613
  const workspaceId = c.req.param("workspaceId");
52034
- const authorization = await requireAccessGrantAuthorization11(
52614
+ const authorization = await requireAccessGrantAuthorization12(
52035
52615
  c,
52036
52616
  deps,
52037
52617
  workspaceId,
@@ -52041,7 +52621,7 @@ function registerSessionRoutes(app, deps) {
52041
52621
  });
52042
52622
  app.post("/v1/workspaces/:workspaceId/sessions", async (c) => {
52043
52623
  const workspaceId = c.req.param("workspaceId");
52044
- const authorization = await requireAccessGrantAuthorization11(
52624
+ const authorization = await requireAccessGrantAuthorization12(
52045
52625
  c,
52046
52626
  deps,
52047
52627
  workspaceId,
@@ -52082,7 +52662,7 @@ function registerSessionRoutes(app, deps) {
52082
52662
  });
52083
52663
  app.put("/v1/workspaces/:workspaceId/new-session-draft", async (c) => {
52084
52664
  const workspaceId = c.req.param("workspaceId");
52085
- const authorization = await requireAccessGrantAuthorization11(
52665
+ const authorization = await requireAccessGrantAuthorization12(
52086
52666
  c,
52087
52667
  deps,
52088
52668
  workspaceId,
@@ -52141,7 +52721,7 @@ function registerSessionRoutes(app, deps) {
52141
52721
  });
52142
52722
  app.get("/v1/workspaces/:workspaceId/sessions", async (c) => {
52143
52723
  const workspaceId = c.req.param("workspaceId");
52144
- const authorization = await requireAccessGrantAuthorization11(
52724
+ const authorization = await requireAccessGrantAuthorization12(
52145
52725
  c,
52146
52726
  deps,
52147
52727
  workspaceId,
@@ -52167,6 +52747,12 @@ function registerSessionRoutes(app, deps) {
52167
52747
  ...query.pinsOnly ? { pinsOnly: true } : {},
52168
52748
  ...query.archivedOnly ? { archivedOnly: true } : {},
52169
52749
  ...query.parentSessionId !== void 0 ? { parentSessionId: query.parentSessionId } : {},
52750
+ ...query.channelId !== void 0 ? { channelId: query.channelId } : {},
52751
+ ...query.createdBy ? { createdBy: query.createdBy } : {},
52752
+ ...query.updatedFrom ? { updatedFrom: query.updatedFrom } : {},
52753
+ ...query.updatedBefore ? { updatedBefore: query.updatedBefore } : {},
52754
+ ...query.createdFrom ? { createdFrom: query.createdFrom } : {},
52755
+ ...query.createdBefore ? { createdBefore: query.createdBefore } : {},
52170
52756
  ...authorizationScope ? { authorizationScope } : {},
52171
52757
  // A managed human's own personal workspace has no membership row, so
52172
52758
  // the list's removal fence must fall back to the organization-membership
@@ -52192,10 +52778,19 @@ function registerSessionRoutes(app, deps) {
52192
52778
  workspaceId,
52193
52779
  sessionIds: [...page.pinned, ...page.sessions].map((session) => session.id)
52194
52780
  });
52781
+ const scheduleTargets = hasPermission19(grant.permissions, "scheduled_tasks:run") && hasPermission19(grant.permissions, "sessions:control") ? await scheduledSessionIds(
52782
+ db,
52783
+ workspaceId,
52784
+ [...page.pinned, ...page.sessions].map((session) => session.id)
52785
+ ) : /* @__PURE__ */ new Set();
52195
52786
  const decorate = (session) => {
52196
52787
  const activity = commandActivity.get(session.id);
52197
52788
  return sessionWithEffectiveToolPolicy2(
52198
- { ...session, ...activity ? { backgroundCommandActivity: activity } : {} },
52789
+ {
52790
+ ...session,
52791
+ hasSchedules: scheduleTargets.has(session.id),
52792
+ ...activity ? { backgroundCommandActivity: activity } : {}
52793
+ },
52199
52794
  policy.workspaceServerIds,
52200
52795
  policy.workspaceDefaultServerIds
52201
52796
  );
@@ -52203,6 +52798,7 @@ function registerSessionRoutes(app, deps) {
52203
52798
  if (pageView) {
52204
52799
  return c.json({
52205
52800
  ...page,
52801
+ ...query.hasPageFilters ? { filtersApplied: true } : {},
52206
52802
  pinned: page.pinned.map(decorate),
52207
52803
  sessions: page.sessions.map(decorate)
52208
52804
  });
@@ -52345,7 +52941,7 @@ function registerSessionRoutes(app, deps) {
52345
52941
  const workspaceId = c.req.param("workspaceId");
52346
52942
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:read");
52347
52943
  const sessionId = c.req.param("sessionId");
52348
- if (!z14.string().uuid().safeParse(sessionId).success) {
52944
+ if (!z15.string().uuid().safeParse(sessionId).success) {
52349
52945
  throw new HTTPException57(404, { message: "session not found" });
52350
52946
  }
52351
52947
  const session = await getSessionForSubject(
@@ -52358,13 +52954,25 @@ function registerSessionRoutes(app, deps) {
52358
52954
  if (!session) {
52359
52955
  throw new HTTPException57(404, { message: "session not found" });
52360
52956
  }
52361
- return c.json(await withEffectivePolicy(deps, workspaceId, grant.subjectId, session));
52957
+ const activity = await backgroundCommandActivityForSessions(db, {
52958
+ accountId: grant.accountId,
52959
+ workspaceId,
52960
+ sessionIds: [sessionId]
52961
+ });
52962
+ const scheduleTargets = hasPermission19(grant.permissions, "scheduled_tasks:run") && hasPermission19(grant.permissions, "sessions:control") ? await scheduledSessionIds(db, workspaceId, [sessionId]) : /* @__PURE__ */ new Set();
52963
+ return c.json(
52964
+ await withEffectivePolicy(deps, workspaceId, grant.subjectId, {
52965
+ ...session,
52966
+ hasSchedules: scheduleTargets.has(sessionId),
52967
+ ...activity.get(sessionId) ? { backgroundCommandActivity: activity.get(sessionId) } : {}
52968
+ })
52969
+ );
52362
52970
  });
52363
52971
  app.get("/v1/workspaces/:workspaceId/sessions/:sessionId/model-context", async (c) => {
52364
52972
  const workspaceId = c.req.param("workspaceId");
52365
52973
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:read");
52366
52974
  const sessionId = c.req.param("sessionId");
52367
- if (!z14.string().uuid().safeParse(sessionId).success) {
52975
+ if (!z15.string().uuid().safeParse(sessionId).success) {
52368
52976
  throw new HTTPException57(404, { message: "session not found" });
52369
52977
  }
52370
52978
  const session = await getSessionForSubject(
@@ -52392,7 +53000,8 @@ function registerSessionRoutes(app, deps) {
52392
53000
  const commands = await listSessionBackgroundCommands(db, {
52393
53001
  accountId: grant.accountId,
52394
53002
  workspaceId,
52395
- sessionId
53003
+ sessionId,
53004
+ activeOnly: true
52396
53005
  });
52397
53006
  return c.json({ commands });
52398
53007
  });
@@ -52402,7 +53011,7 @@ function registerSessionRoutes(app, deps) {
52402
53011
  const workspaceId = c.req.param("workspaceId");
52403
53012
  const sessionId = c.req.param("sessionId");
52404
53013
  const commandId = c.req.param("commandId");
52405
- if (!z14.string().uuid().safeParse(commandId).success) {
53014
+ if (!z15.string().uuid().safeParse(commandId).success) {
52406
53015
  throw new HTTPException57(404, { message: "background command not found" });
52407
53016
  }
52408
53017
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
@@ -52422,7 +53031,7 @@ function registerSessionRoutes(app, deps) {
52422
53031
  app.put("/v1/workspaces/:workspaceId/sessions/:sessionId/visibility", async (c) => {
52423
53032
  const workspaceId = c.req.param("workspaceId");
52424
53033
  const sessionId = c.req.param("sessionId");
52425
- const authorization = await requireAccessGrantAuthorization11(c, deps, workspaceId);
53034
+ const authorization = await requireAccessGrantAuthorization12(c, deps, workspaceId);
52426
53035
  const parsed = UpdateSessionVisibilityRequest.safeParse(await c.req.json().catch(() => null));
52427
53036
  if (!parsed.success) {
52428
53037
  throw new ApiHttpError(422, {
@@ -52450,7 +53059,7 @@ function registerSessionRoutes(app, deps) {
52450
53059
  app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/forks", async (c) => {
52451
53060
  const workspaceId = c.req.param("workspaceId");
52452
53061
  const sessionId = c.req.param("sessionId");
52453
- const authorization = await requireAccessGrantAuthorization11(c, deps, workspaceId);
53062
+ const authorization = await requireAccessGrantAuthorization12(c, deps, workspaceId);
52454
53063
  const parsed = ForkSessionRequest.safeParse(await c.req.json().catch(() => null));
52455
53064
  if (!parsed.success) {
52456
53065
  throw new ApiHttpError(422, {
@@ -52482,7 +53091,7 @@ function registerSessionRoutes(app, deps) {
52482
53091
  accountId,
52483
53092
  workspaceId,
52484
53093
  sessionId,
52485
- workflowId: workflowIdForSession2(sessionId),
53094
+ workflowId: workflowIdForSession3(sessionId),
52486
53095
  wakeRevision: result.workflowWakeRevision
52487
53096
  });
52488
53097
  }
@@ -52491,7 +53100,7 @@ function registerSessionRoutes(app, deps) {
52491
53100
  const workspaceId = c.req.param("workspaceId");
52492
53101
  const sessionId = c.req.param("sessionId");
52493
53102
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
52494
- if (!z14.string().uuid().safeParse(sessionId).success) {
53103
+ if (!z15.string().uuid().safeParse(sessionId).success) {
52495
53104
  throw new HTTPException57(400, { message: "invalid session id" });
52496
53105
  }
52497
53106
  const parsed = BeginSessionRealtimeRequest.safeParse(await c.req.json().catch(() => null));
@@ -52526,7 +53135,7 @@ function registerSessionRoutes(app, deps) {
52526
53135
  const sessionId = c.req.param("sessionId");
52527
53136
  const realtimeId = c.req.param("realtimeId");
52528
53137
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
52529
- if (!z14.string().uuid().safeParse(sessionId).success || !z14.string().uuid().safeParse(realtimeId).success) {
53138
+ if (!z15.string().uuid().safeParse(sessionId).success || !z15.string().uuid().safeParse(realtimeId).success) {
52530
53139
  throw new HTTPException57(400, {
52531
53140
  message: "invalid realtime lifecycle id"
52532
53141
  });
@@ -52562,7 +53171,7 @@ function registerSessionRoutes(app, deps) {
52562
53171
  const sessionId = c.req.param("sessionId");
52563
53172
  const realtimeId = c.req.param("realtimeId");
52564
53173
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
52565
- if (!z14.string().uuid().safeParse(sessionId).success || !z14.string().uuid().safeParse(realtimeId).success) {
53174
+ if (!z15.string().uuid().safeParse(sessionId).success || !z15.string().uuid().safeParse(realtimeId).success) {
52566
53175
  throw new HTTPException57(400, {
52567
53176
  message: "invalid realtime lifecycle id"
52568
53177
  });
@@ -52596,7 +53205,7 @@ function registerSessionRoutes(app, deps) {
52596
53205
  const workspaceId = c.req.param("workspaceId");
52597
53206
  const sessionId = c.req.param("sessionId");
52598
53207
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
52599
- if (!z14.string().uuid().safeParse(sessionId).success) {
53208
+ if (!z15.string().uuid().safeParse(sessionId).success) {
52600
53209
  throw new HTTPException57(404, { message: "session not found" });
52601
53210
  }
52602
53211
  const parsed = CodexRealtimeWebrtcRequest.safeParse(await c.req.json().catch(() => null));
@@ -52775,7 +53384,7 @@ function registerSessionRoutes(app, deps) {
52775
53384
  const workspaceId = c.req.param("workspaceId");
52776
53385
  const sessionId = c.req.param("sessionId");
52777
53386
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
52778
- if (!z14.string().uuid().safeParse(sessionId).success) {
53387
+ if (!z15.string().uuid().safeParse(sessionId).success) {
52779
53388
  throw new HTTPException57(404, { message: "session not found" });
52780
53389
  }
52781
53390
  const parsed = GatewayRealtimeConnectRequest.safeParse(await c.req.json().catch(() => null));
@@ -52894,7 +53503,7 @@ function registerSessionRoutes(app, deps) {
52894
53503
  const workspaceId = c.req.param("workspaceId");
52895
53504
  const sessionId = c.req.param("sessionId");
52896
53505
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
52897
- if (!z14.string().uuid().safeParse(sessionId).success) {
53506
+ if (!z15.string().uuid().safeParse(sessionId).success) {
52898
53507
  throw new HTTPException57(404, { message: "session not found" });
52899
53508
  }
52900
53509
  const parsed = GatewayRealtimeConnectRequest.safeParse(await c.req.json().catch(() => null));
@@ -53017,7 +53626,7 @@ function registerSessionRoutes(app, deps) {
53017
53626
  const realtimeId = c.req.param("realtimeId");
53018
53627
  const connectionId = c.req.param("connectionId");
53019
53628
  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) {
53629
+ if (!z15.string().uuid().safeParse(sessionId).success || !z15.string().uuid().safeParse(realtimeId).success || !z15.string().uuid().safeParse(connectionId).success) {
53021
53630
  throw new HTTPException57(400, {
53022
53631
  message: "invalid realtime connection id"
53023
53632
  });
@@ -53059,7 +53668,7 @@ function registerSessionRoutes(app, deps) {
53059
53668
  const sessionId = c.req.param("sessionId");
53060
53669
  const realtimeId = c.req.param("realtimeId");
53061
53670
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
53062
- if (!z14.string().uuid().safeParse(sessionId).success || !z14.string().uuid().safeParse(realtimeId).success) {
53671
+ if (!z15.string().uuid().safeParse(sessionId).success || !z15.string().uuid().safeParse(realtimeId).success) {
53063
53672
  throw new HTTPException57(400, { message: "invalid realtime ledger id" });
53064
53673
  }
53065
53674
  const parsed = SyncSessionRealtimeLedgerRequest.safeParse(
@@ -53093,7 +53702,7 @@ function registerSessionRoutes(app, deps) {
53093
53702
  );
53094
53703
  app.put("/v1/workspaces/:workspaceId/sessions/:sessionId/pin", async (c) => {
53095
53704
  const workspaceId = c.req.param("workspaceId");
53096
- const authorization = await requireAccessGrantAuthorization11(
53705
+ const authorization = await requireAccessGrantAuthorization12(
53097
53706
  c,
53098
53707
  deps,
53099
53708
  workspaceId,
@@ -53101,7 +53710,7 @@ function registerSessionRoutes(app, deps) {
53101
53710
  );
53102
53711
  const grant = authorization.grant;
53103
53712
  const sessionId = c.req.param("sessionId");
53104
- if (!z14.string().uuid().safeParse(sessionId).success) {
53713
+ if (!z15.string().uuid().safeParse(sessionId).success) {
53105
53714
  throw new HTTPException57(404, { message: "session not found" });
53106
53715
  }
53107
53716
  const parsed = UpdateSessionPinRequest.safeParse(await c.req.json().catch(() => null));
@@ -53146,7 +53755,7 @@ function registerSessionRoutes(app, deps) {
53146
53755
  });
53147
53756
  app.put("/v1/workspaces/:workspaceId/sessions/:sessionId/attention", async (c) => {
53148
53757
  const workspaceId = c.req.param("workspaceId");
53149
- const authorization = await requireAccessGrantAuthorization11(
53758
+ const authorization = await requireAccessGrantAuthorization12(
53150
53759
  c,
53151
53760
  deps,
53152
53761
  workspaceId,
@@ -53154,7 +53763,7 @@ function registerSessionRoutes(app, deps) {
53154
53763
  );
53155
53764
  const grant = authorization.grant;
53156
53765
  const sessionId = c.req.param("sessionId");
53157
- if (!z14.string().uuid().safeParse(sessionId).success) {
53766
+ if (!z15.string().uuid().safeParse(sessionId).success) {
53158
53767
  throw new HTTPException57(404, { message: "session not found" });
53159
53768
  }
53160
53769
  const parsed = UpdateSessionAttentionRequest.safeParse(await c.req.json().catch(() => null));
@@ -53196,7 +53805,7 @@ function registerSessionRoutes(app, deps) {
53196
53805
  });
53197
53806
  app.put("/v1/workspaces/:workspaceId/sessions/:sessionId/archive", async (c) => {
53198
53807
  const workspaceId = c.req.param("workspaceId");
53199
- const authorization = await requireAccessGrantAuthorization11(
53808
+ const authorization = await requireAccessGrantAuthorization12(
53200
53809
  c,
53201
53810
  deps,
53202
53811
  workspaceId,
@@ -53242,17 +53851,35 @@ function registerSessionRoutes(app, deps) {
53242
53851
  const workspaceId = c.req.param("workspaceId");
53243
53852
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:read");
53244
53853
  const lineage = await readSessionLineage(deps, grant, c.req.param("sessionId"));
53854
+ const sessionIds = [
53855
+ c.req.param("sessionId"),
53856
+ ...lineage.ancestors.map((session) => session.id)
53857
+ ];
53858
+ const collect = (nodes) => {
53859
+ for (const node of nodes) {
53860
+ sessionIds.push(node.session.id);
53861
+ collect(node.children);
53862
+ }
53863
+ };
53864
+ collect(lineage.children);
53865
+ const targets = hasPermission19(grant.permissions, "scheduled_tasks:run") && hasPermission19(grant.permissions, "sessions:control") ? await scheduledSessionIds(db, workspaceId, sessionIds) : /* @__PURE__ */ new Set();
53866
+ const decorateNodes = (nodes) => nodes.map((node) => ({
53867
+ ...node,
53868
+ session: { ...node.session, hasSchedules: targets.has(node.session.id) },
53869
+ children: decorateNodes(node.children)
53870
+ }));
53245
53871
  const policy = await loadEffectivePolicyContext(deps, workspaceId, grant.subjectId);
53246
53872
  return c.json({
53247
53873
  ...lineage,
53874
+ sessionHasSchedules: targets.has(c.req.param("sessionId")),
53248
53875
  ancestors: lineage.ancestors.map(
53249
53876
  (session) => sessionWithEffectiveToolPolicy2(
53250
- session,
53877
+ { ...session, hasSchedules: targets.has(session.id) },
53251
53878
  policy.workspaceServerIds,
53252
53879
  policy.workspaceDefaultServerIds
53253
53880
  )
53254
53881
  ),
53255
- children: mapLineageNodes(lineage.children, policy)
53882
+ children: decorateNodes(mapLineageNodes(lineage.children, policy))
53256
53883
  });
53257
53884
  });
53258
53885
  app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/codex-account", async (c) => {
@@ -53620,7 +54247,7 @@ function registerSessionRoutes(app, deps) {
53620
54247
  accountId: grant.accountId,
53621
54248
  workspaceId,
53622
54249
  sessionId,
53623
- workflowId: workflowIdForSession2(sessionId),
54250
+ workflowId: workflowIdForSession3(sessionId),
53624
54251
  wakeRevision: workflowWakeRevision
53625
54252
  });
53626
54253
  }
@@ -53703,7 +54330,7 @@ function registerSessionRoutes(app, deps) {
53703
54330
  accountId: grant.accountId,
53704
54331
  workspaceId,
53705
54332
  sessionId,
53706
- workflowId: workflowIdForSession2(sessionId),
54333
+ workflowId: workflowIdForSession3(sessionId),
53707
54334
  wakeRevision: workflowWakeRevision
53708
54335
  });
53709
54336
  }
@@ -53751,7 +54378,7 @@ function registerSessionRoutes(app, deps) {
53751
54378
  accountId: grant.accountId,
53752
54379
  workspaceId,
53753
54380
  sessionId,
53754
- workflowId: workflowIdForSession2(sessionId),
54381
+ workflowId: workflowIdForSession3(sessionId),
53755
54382
  wakeRevision: workflowWakeRevision2
53756
54383
  });
53757
54384
  }
@@ -53811,7 +54438,7 @@ function registerSessionRoutes(app, deps) {
53811
54438
  accountId: grant.accountId,
53812
54439
  workspaceId,
53813
54440
  sessionId,
53814
- workflowId: workflowIdForSession2(sessionId),
54441
+ workflowId: workflowIdForSession3(sessionId),
53815
54442
  wakeRevision: workflowWakeRevision
53816
54443
  });
53817
54444
  }
@@ -54275,7 +54902,7 @@ function registerSessionRoutes(app, deps) {
54275
54902
  });
54276
54903
  app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/steer", async (c) => {
54277
54904
  const workspaceId = c.req.param("workspaceId");
54278
- const authorization = await requireAccessGrantAuthorization11(
54905
+ const authorization = await requireAccessGrantAuthorization12(
54279
54906
  c,
54280
54907
  deps,
54281
54908
  workspaceId,
@@ -54312,7 +54939,7 @@ function registerSessionRoutes(app, deps) {
54312
54939
  });
54313
54940
  app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/composer-draft/submit", async (c) => {
54314
54941
  const workspaceId = c.req.param("workspaceId");
54315
- const authorization = await requireAccessGrantAuthorization11(
54942
+ const authorization = await requireAccessGrantAuthorization12(
54316
54943
  c,
54317
54944
  deps,
54318
54945
  workspaceId,
@@ -54334,7 +54961,7 @@ function registerSessionRoutes(app, deps) {
54334
54961
  });
54335
54962
  app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/events", async (c) => {
54336
54963
  const workspaceId = c.req.param("workspaceId");
54337
- const authorization = await requireAccessGrantAuthorization11(
54964
+ const authorization = await requireAccessGrantAuthorization12(
54338
54965
  c,
54339
54966
  deps,
54340
54967
  workspaceId,
@@ -54395,7 +55022,7 @@ function registerSessionRoutes(app, deps) {
54395
55022
  });
54396
55023
  }
54397
55024
  await publishDurableSessionEvents3(bus, workspaceId, sessionId, accepted.events);
54398
- const workflowId = workflowIdForSession2(sessionId);
55025
+ const workflowId = workflowIdForSession3(sessionId);
54399
55026
  await workflowClient.signalApprovalDecision({
54400
55027
  accountId: grant.accountId,
54401
55028
  workspaceId,
@@ -54439,7 +55066,7 @@ function registerSessionRoutes(app, deps) {
54439
55066
  workspaceId,
54440
55067
  sessionId,
54441
55068
  eventId: accepted.events[0]?.id ?? event.payload.requestId,
54442
- workflowId: workflowIdForSession2(sessionId),
55069
+ workflowId: workflowIdForSession3(sessionId),
54443
55070
  workflowWakeRevision: accepted.workflowWakeRevision
54444
55071
  });
54445
55072
  }
@@ -54504,7 +55131,7 @@ function registerSessionRoutes(app, deps) {
54504
55131
  const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:read");
54505
55132
  assertOwnershipEnabled();
54506
55133
  const sessionId = c.req.param("sessionId");
54507
- const session = await getSession7(db, workspaceId, sessionId);
55134
+ const session = await getSession8(db, workspaceId, sessionId);
54508
55135
  if (!session) {
54509
55136
  throw new HTTPException57(404, { message: "session not found" });
54510
55137
  }
@@ -54637,7 +55264,7 @@ function registerSessionRoutes(app, deps) {
54637
55264
  const grant = await requireAccessGrant30(c, deps, workspaceId, "stream:acknowledge");
54638
55265
  assertOwnershipEnabled();
54639
55266
  const sessionId = c.req.param("sessionId");
54640
- const session = await getSession7(db, workspaceId, sessionId);
55267
+ const session = await getSession8(db, workspaceId, sessionId);
54641
55268
  if (!session) {
54642
55269
  throw new HTTPException57(404, { message: "session not found" });
54643
55270
  }
@@ -54666,7 +55293,7 @@ function registerSessionRoutes(app, deps) {
54666
55293
  const grant = await requireAccessGrant30(c, deps, workspaceId);
54667
55294
  assertOwnershipEnabled();
54668
55295
  const sessionId = c.req.param("sessionId");
54669
- const session = await getSession7(db, workspaceId, sessionId);
55296
+ const session = await getSession8(db, workspaceId, sessionId);
54670
55297
  if (!session) {
54671
55298
  throw new HTTPException57(404, { message: "session not found" });
54672
55299
  }
@@ -54821,7 +55448,7 @@ function registerSessionRoutes(app, deps) {
54821
55448
  requireViewerLifecyclePermission(grant);
54822
55449
  assertOwnershipEnabled();
54823
55450
  const sessionId = c.req.param("sessionId");
54824
- const session = await getSession7(db, workspaceId, sessionId);
55451
+ const session = await getSession8(db, workspaceId, sessionId);
54825
55452
  if (!session) {
54826
55453
  throw new HTTPException57(404, { message: "session not found" });
54827
55454
  }
@@ -54850,7 +55477,7 @@ function registerSessionRoutes(app, deps) {
54850
55477
  requireViewerLifecyclePermission(grant);
54851
55478
  assertOwnershipEnabled();
54852
55479
  const sessionId = c.req.param("sessionId");
54853
- const session = await getSession7(db, workspaceId, sessionId);
55480
+ const session = await getSession8(db, workspaceId, sessionId);
54854
55481
  if (!session) {
54855
55482
  throw new HTTPException57(404, { message: "session not found" });
54856
55483
  }
@@ -54872,7 +55499,7 @@ function registerSessionRoutes(app, deps) {
54872
55499
  const grant = await requireAccessGrant30(c, deps, workspaceId, "stream:view");
54873
55500
  assertOwnershipEnabled();
54874
55501
  const sessionId = c.req.param("sessionId");
54875
- const session = await getSession7(db, workspaceId, sessionId);
55502
+ const session = await getSession8(db, workspaceId, sessionId);
54876
55503
  if (!session) {
54877
55504
  throw new HTTPException57(404, { message: "session not found" });
54878
55505
  }
@@ -54894,7 +55521,7 @@ function registerSessionRoutes(app, deps) {
54894
55521
  const grant = await requireAccessGrant30(c, deps, workspaceId, permission);
54895
55522
  assertOwnershipEnabled();
54896
55523
  const sessionId = c.req.param("sessionId") ?? "";
54897
- const session = await getSession7(db, workspaceId, sessionId);
55524
+ const session = await getSession8(db, workspaceId, sessionId);
54898
55525
  if (!session) {
54899
55526
  throw new HTTPException57(404, { message: "session not found" });
54900
55527
  }
@@ -55093,7 +55720,7 @@ function registerSessionRoutes(app, deps) {
55093
55720
  message: "path query parameter is required"
55094
55721
  });
55095
55722
  }
55096
- const session = await getSession7(db, workspaceId, sessionId);
55723
+ const session = await getSession8(db, workspaceId, sessionId);
55097
55724
  if (!session) {
55098
55725
  throw new HTTPException57(404, { message: "session not found" });
55099
55726
  }
@@ -55608,7 +56235,7 @@ function eventEnumList(raw, schema, name) {
55608
56235
  }
55609
56236
  function sessionListQuery(query, allowCursor = true) {
55610
56237
  const parentSessionId = query.parentSessionId;
55611
- if (parentSessionId !== void 0 && parentSessionId !== "null" && !z14.string().uuid().safeParse(parentSessionId).success) {
56238
+ if (parentSessionId !== void 0 && parentSessionId !== "null" && !z15.string().uuid().safeParse(parentSessionId).success) {
55612
56239
  throw new HTTPException57(400, {
55613
56240
  message: 'parentSessionId must be a session id or the literal "null"'
55614
56241
  });
@@ -55634,12 +56261,58 @@ function sessionListQuery(query, allowCursor = true) {
55634
56261
  throw new HTTPException57(400, { message: 'archivedOnly must be the literal "true"' });
55635
56262
  }
55636
56263
  const archivedOnly = query.archivedOnly === "true";
56264
+ const channelId = query.channelId;
56265
+ if (channelId !== void 0 && channelId !== "null" && !z15.string().uuid().safeParse(channelId).success) {
56266
+ throw new HTTPException57(400, {
56267
+ message: 'channelId must be a channel id or the literal "null"'
56268
+ });
56269
+ }
56270
+ const createdByKind = query.createdByKind;
56271
+ const createdBySubjectId = query.createdBySubjectId;
56272
+ if (createdByKind === void 0 !== (createdBySubjectId === void 0)) {
56273
+ throw new HTTPException57(400, {
56274
+ message: "createdByKind and createdBySubjectId must be supplied together"
56275
+ });
56276
+ }
56277
+ if (createdByKind !== void 0 && createdByKind !== "subject" && createdByKind !== "service") {
56278
+ throw new HTTPException57(400, {
56279
+ message: 'createdByKind must be "subject" or "service"'
56280
+ });
56281
+ }
56282
+ if (createdBySubjectId !== void 0 && (createdBySubjectId.trim().length < 1 || createdBySubjectId.length > 1024)) {
56283
+ throw new HTTPException57(400, {
56284
+ message: "createdBySubjectId must be between 1 and 1024 characters"
56285
+ });
56286
+ }
56287
+ const parseDateBound = (name) => {
56288
+ const raw = query[name];
56289
+ if (raw === void 0) return void 0;
56290
+ const parsed = z15.string().datetime({ offset: true }).safeParse(raw);
56291
+ if (!parsed.success) {
56292
+ throw new HTTPException57(400, { message: `${name} must be an ISO-8601 timestamp` });
56293
+ }
56294
+ if (/\.\d{4,}/.test(parsed.data)) {
56295
+ throw new HTTPException57(400, { message: `${name} supports at most millisecond precision` });
56296
+ }
56297
+ return new Date(parsed.data);
56298
+ };
56299
+ const updatedFrom = parseDateBound("updatedFrom");
56300
+ const updatedBefore = parseDateBound("updatedBefore");
56301
+ const createdFrom = parseDateBound("createdFrom");
56302
+ const createdBefore = parseDateBound("createdBefore");
56303
+ if (updatedFrom && updatedBefore && updatedFrom >= updatedBefore) {
56304
+ throw new HTTPException57(400, { message: "updatedFrom must be earlier than updatedBefore" });
56305
+ }
56306
+ if (createdFrom && createdBefore && createdFrom >= createdBefore) {
56307
+ throw new HTTPException57(400, { message: "createdFrom must be earlier than createdBefore" });
56308
+ }
56309
+ const hasPageFilters = channelId !== void 0 || createdByKind !== void 0 || updatedFrom !== void 0 || updatedBefore !== void 0 || createdFrom !== void 0 || createdBefore !== void 0;
55637
56310
  if (pinsOnly && !allowCursor) {
55638
56311
  throw new HTTPException57(400, { message: 'pinsOnly requires view="page"' });
55639
56312
  }
55640
- if (pinsOnly && (rawCursor || parentSessionId !== void 0 || search)) {
56313
+ if (pinsOnly && (rawCursor || parentSessionId !== void 0 || search || hasPageFilters)) {
55641
56314
  throw new HTTPException57(400, {
55642
- message: "pinsOnly cannot be combined with cursor, parentSessionId, or search"
56315
+ message: "pinsOnly cannot be combined with cursor, parentSessionId, search, or filters"
55643
56316
  });
55644
56317
  }
55645
56318
  if (pinsOnly && archivedOnly) {
@@ -55651,7 +56324,14 @@ function sessionListQuery(query, allowCursor = true) {
55651
56324
  cursor,
55652
56325
  search: search || void 0,
55653
56326
  pinsOnly,
55654
- archivedOnly
56327
+ archivedOnly,
56328
+ channelId: channelId === void 0 ? void 0 : channelId === "null" ? null : channelId,
56329
+ createdBy: createdByKind && createdBySubjectId ? { kind: createdByKind, subjectId: createdBySubjectId } : void 0,
56330
+ updatedFrom,
56331
+ updatedBefore,
56332
+ createdFrom,
56333
+ createdBefore,
56334
+ hasPageFilters
55655
56335
  };
55656
56336
  }
55657
56337
  function encodeAgentTopologyCursor(value) {
@@ -55665,18 +56345,18 @@ function decodeAgentTopologyCursor(value) {
55665
56345
  }
55666
56346
  try {
55667
56347
  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()
56348
+ const legacy = z15.object({
56349
+ v: z15.literal(1),
56350
+ parentSessionId: z15.string().uuid().nullable(),
56351
+ search: z15.string().max(WORK_DISCOVERY_QUERY_MAX_CHARS2).nullable(),
56352
+ cursor: z15.object({
56353
+ orderBy: z15.literal("updatedAt"),
56354
+ sortRevision: z15.string().max(64),
56355
+ sortAt: z15.string().max(64),
56356
+ id: z15.string().uuid(),
56357
+ snapshotAt: z15.string().max(64),
56358
+ snapshotRevision: z15.string().max(64),
56359
+ updatedAfter: z15.null()
55680
56360
  })
55681
56361
  }).safeParse(decoded2);
55682
56362
  if (legacy.success) {
@@ -55700,13 +56380,13 @@ function decodeAgentTopologyCursor(value) {
55700
56380
  claimLimit: null
55701
56381
  };
55702
56382
  }
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([
56383
+ const parsed = z15.object({
56384
+ v: z15.literal(2),
56385
+ parentSessionId: z15.union([z15.string().uuid(), z15.literal("all"), z15.null()]),
56386
+ rootSessionId: z15.string().uuid().nullable(),
56387
+ query: z15.string().max(WORK_DISCOVERY_QUERY_MAX_CHARS2).nullable(),
56388
+ statuses: z15.array(
56389
+ z15.enum([
55710
56390
  "queued",
55711
56391
  "running",
55712
56392
  "idle",
@@ -55717,24 +56397,24 @@ function decodeAgentTopologyCursor(value) {
55717
56397
  "cancelled"
55718
56398
  ])
55719
56399
  ).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),
56400
+ activeOnly: z15.boolean(),
56401
+ recentHours: z15.number().int().positive().max(WORK_DISCOVERY_RECENT_HOURS_MAX2).nullable(),
56402
+ subject: z15.object({
56403
+ namespace: z15.string().min(1).max(WORK_CLAIM_NAMESPACE_MAX_BYTES2),
55724
56404
  type: WorkClaimSubjectType2,
55725
- canonicalKey: z14.string().min(1).max(WORK_CLAIM_CANONICAL_KEY_MAX_BYTES2)
56405
+ canonicalKey: z15.string().min(1).max(WORK_CLAIM_CANONICAL_KEY_MAX_BYTES2)
55726
56406
  }).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()
56407
+ claimLimit: z15.number().int().positive().max(WORK_CLAIM_DISCOVERY_LIMIT2).nullable(),
56408
+ cursor: z15.object({
56409
+ orderBy: z15.enum(["updatedAt", "relevance"]),
56410
+ sortRank: z15.number().int().nonnegative().nullable(),
56411
+ sortRevision: z15.string().max(64),
56412
+ sortAt: z15.string().max(64),
56413
+ id: z15.string().uuid(),
56414
+ snapshotAt: z15.string().max(64),
56415
+ snapshotRevision: z15.string().max(64),
56416
+ updatedAfter: z15.null(),
56417
+ filterHash: z15.string().regex(/^[0-9a-f]{64}$/).nullable()
55738
56418
  })
55739
56419
  }).parse(decoded2);
55740
56420
  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 +56439,13 @@ function agentTopologyQuery(query) {
55759
56439
  });
55760
56440
  }
55761
56441
  const rawParent = query.parentSessionId;
55762
- if (rawParent !== void 0 && rawParent !== "null" && !z14.string().uuid().safeParse(rawParent).success) {
56442
+ if (rawParent !== void 0 && rawParent !== "null" && !z15.string().uuid().safeParse(rawParent).success) {
55763
56443
  throw new HTTPException57(400, {
55764
56444
  message: 'parentSessionId must be a session id or the literal "null"'
55765
56445
  });
55766
56446
  }
55767
56447
  const rootSessionId = query.rootSessionId?.trim();
55768
- if (rootSessionId && !z14.string().uuid().safeParse(rootSessionId).success) {
56448
+ if (rootSessionId && !z15.string().uuid().safeParse(rootSessionId).success) {
55769
56449
  throw new HTTPException57(400, { message: "rootSessionId must be a session id" });
55770
56450
  }
55771
56451
  const normalizeSearchQuery = (value) => {
@@ -55794,8 +56474,8 @@ function agentTopologyQuery(query) {
55794
56474
  query.statuses.split(",").map((status) => status.trim()).filter(Boolean)
55795
56475
  )
55796
56476
  ].sort() : [];
55797
- const parsedStatuses = z14.array(
55798
- z14.enum([
56477
+ const parsedStatuses = z15.array(
56478
+ z15.enum([
55799
56479
  "queued",
55800
56480
  "running",
55801
56481
  "idle",
@@ -55838,7 +56518,7 @@ function agentTopologyQuery(query) {
55838
56518
  throw new HTTPException57(400, { message: "query cannot be combined with an exact subject" });
55839
56519
  }
55840
56520
  const relevanceRequested = Boolean(searchQuery || subject);
55841
- const parentSessionId = rawParent === void 0 ? relevanceRequested ? void 0 : null : rawParent === "null" ? null : rawParent;
56521
+ const parentSessionId = rawParent === void 0 ? relevanceRequested || Boolean(rootSessionId) ? void 0 : null : rawParent === "null" ? null : rawParent;
55842
56522
  const claimLimit = query.claimLimit === void 0 ? void 0 : Number(query.claimLimit);
55843
56523
  if (claimLimit !== void 0 && (!Number.isSafeInteger(claimLimit) || claimLimit < 1 || claimLimit > WORK_CLAIM_DISCOVERY_LIMIT2)) {
55844
56524
  throw new HTTPException57(400, {
@@ -56062,8 +56742,8 @@ import {
56062
56742
  updateSocialConnectionCredential as updateSocialConnectionCredential2
56063
56743
  } from "@opengeni/db";
56064
56744
  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";
56745
+ import { z as z16 } from "zod";
56746
+ import { requireAccessGrant as requireAccessGrant31, requireAccessGrantAuthorization as requireAccessGrantAuthorization13 } from "@opengeni/core";
56067
56747
  function registerSocialRoutes(app, deps) {
56068
56748
  const { db, settings, observability } = deps;
56069
56749
  app.get("/v1/workspaces/:workspaceId/social/connections", async (c) => {
@@ -56133,7 +56813,7 @@ function registerSocialRoutes(app, deps) {
56133
56813
  });
56134
56814
  }
56135
56815
  const payload = parsed.data;
56136
- const access = await requireAccessGrantAuthorization12(
56816
+ const access = await requireAccessGrantAuthorization13(
56137
56817
  c,
56138
56818
  deps,
56139
56819
  workspaceId,
@@ -56227,7 +56907,7 @@ function parseConnectionIds(raw) {
56227
56907
  return void 0;
56228
56908
  }
56229
56909
  const values = raw.split(",").map((value) => value.trim()).filter(Boolean);
56230
- const parsed = z15.array(z15.string().uuid()).safeParse(values);
56910
+ const parsed = z16.array(z16.string().uuid()).safeParse(values);
56231
56911
  if (!parsed.success) {
56232
56912
  throw new HTTPException58(422, {
56233
56913
  message: "connectionIds must be a comma-separated list of UUIDs"
@@ -56248,7 +56928,7 @@ function socialHttpException(error) {
56248
56928
  }
56249
56929
 
56250
56930
  // src/routes/workspaces.ts
56251
- import { createHash as createHash19 } from "crypto";
56931
+ import { createHash as createHash18 } from "crypto";
56252
56932
  import {
56253
56933
  AddWorkspaceMemberRequest,
56254
56934
  CreateWorkspaceRequest,
@@ -56511,7 +57191,7 @@ function projectWorkspaceOpenRouterCustomModel(model) {
56511
57191
  });
56512
57192
  }
56513
57193
  function workspaceCustomModelRequestHash(value) {
56514
- return createHash19("sha256").update(stableJson6(value), "utf8").digest("hex");
57194
+ return createHash18("sha256").update(stableJson6(value), "utf8").digest("hex");
56515
57195
  }
56516
57196
  function workspaceMembersResponse(members) {
56517
57197
  return ListWorkspaceMembersResponse.parse({
@@ -57430,8 +58110,8 @@ import {
57430
58110
  WorkspaceInstructionPolicyOperationReuseError
57431
58111
  } from "@opengeni/db";
57432
58112
  import { HTTPException as HTTPException61 } from "hono/http-exception";
57433
- import { z as z16 } from "zod";
57434
- var WorkspaceInstructionPolicyRevisionId = z16.string().uuid();
58113
+ import { z as z17 } from "zod";
58114
+ var WorkspaceInstructionPolicyRevisionId = z17.string().uuid();
57435
58115
  async function parseBody(context, schema) {
57436
58116
  const parsed = schema.safeParse(await context.req.json().catch(() => null));
57437
58117
  if (!parsed.success) {
@@ -57763,7 +58443,7 @@ import {
57763
58443
  WorkspaceLearningPolicyOperationReuseError
57764
58444
  } from "@opengeni/db";
57765
58445
  import { HTTPException as HTTPException62 } from "hono/http-exception";
57766
- import { z as z17 } from "zod";
58446
+ import { z as z18 } from "zod";
57767
58447
  async function parseBody2(context, schema) {
57768
58448
  const parsed = schema.safeParse(await context.req.json().catch(() => null));
57769
58449
  if (!parsed.success) {
@@ -57859,7 +58539,7 @@ function registerWorkspaceLearningRoutes(app, deps) {
57859
58539
  });
57860
58540
  app.post(`${base}/revisions/:revisionId/activate`, async (context) => {
57861
58541
  const workspaceId = context.req.param("workspaceId");
57862
- const revisionId2 = z17.string().uuid().safeParse(context.req.param("revisionId"));
58542
+ const revisionId2 = z18.string().uuid().safeParse(context.req.param("revisionId"));
57863
58543
  if (!revisionId2.success) throw new HTTPException62(422, { message: "Invalid revision id" });
57864
58544
  const grant = await requireAccessGrant34(context, deps, workspaceId, "workspace:admin");
57865
58545
  const request = await parseBody2(context, ActivateWorkspaceLearningPolicyRevisionRequest);
@@ -57909,7 +58589,7 @@ function registerWorkspaceLearningRoutes(app, deps) {
57909
58589
  });
57910
58590
  app.post(`${base}/activations/:activationReceiptId/undo`, async (context) => {
57911
58591
  const workspaceId = context.req.param("workspaceId");
57912
- const activationReceiptId = z17.string().uuid().safeParse(context.req.param("activationReceiptId"));
58592
+ const activationReceiptId = z18.string().uuid().safeParse(context.req.param("activationReceiptId"));
57913
58593
  if (!activationReceiptId.success) {
57914
58594
  throw new HTTPException62(422, { message: "Invalid activation receipt id" });
57915
58595
  }
@@ -57952,7 +58632,7 @@ import {
57952
58632
  } from "@opengeni/contracts";
57953
58633
  import {
57954
58634
  requireAccessGrant as requireAccessGrant35,
57955
- requireAccessGrantAuthorization as requireAccessGrantAuthorization13
58635
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization14
57956
58636
  } from "@opengeni/core";
57957
58637
  import {
57958
58638
  activateCompanyProfileRevision,
@@ -57970,8 +58650,8 @@ import {
57970
58650
  updateCompanyProfileAgentPolicy
57971
58651
  } from "@opengeni/db";
57972
58652
  import { HTTPException as HTTPException63 } from "hono/http-exception";
57973
- import { z as z18 } from "zod";
57974
- var RevisionId = z18.string().uuid();
58653
+ import { z as z19 } from "zod";
58654
+ var RevisionId = z19.string().uuid();
57975
58655
  async function parseBody3(context, schema) {
57976
58656
  const parsed = schema.safeParse(await context.req.json().catch(() => null));
57977
58657
  if (!parsed.success) throw new HTTPException63(422, { message: "Invalid company-profile request" });
@@ -58032,7 +58712,7 @@ function registerCompanyProfileRoutes(app, deps) {
58032
58712
  const base = "/v1/workspaces/:workspaceId/company-profile";
58033
58713
  app.get(`${base}/agent-policy`, async (context) => {
58034
58714
  const workspaceId = context.req.param("workspaceId");
58035
- const access = await requireAccessGrantAuthorization13(
58715
+ const access = await requireAccessGrantAuthorization14(
58036
58716
  context,
58037
58717
  deps,
58038
58718
  workspaceId,
@@ -58055,7 +58735,7 @@ function registerCompanyProfileRoutes(app, deps) {
58055
58735
  });
58056
58736
  app.patch(`${base}/agent-policy`, async (context) => {
58057
58737
  const workspaceId = context.req.param("workspaceId");
58058
- const access = await requireAccessGrantAuthorization13(
58738
+ const access = await requireAccessGrantAuthorization14(
58059
58739
  context,
58060
58740
  deps,
58061
58741
  workspaceId,
@@ -58099,7 +58779,7 @@ function registerCompanyProfileRoutes(app, deps) {
58099
58779
  });
58100
58780
  app.put(base, async (context) => {
58101
58781
  const workspaceId = context.req.param("workspaceId");
58102
- const access = await requireAccessGrantAuthorization13(
58782
+ const access = await requireAccessGrantAuthorization14(
58103
58783
  context,
58104
58784
  deps,
58105
58785
  workspaceId,
@@ -58151,7 +58831,7 @@ function registerCompanyProfileRoutes(app, deps) {
58151
58831
  });
58152
58832
  app.post(`${base}/rollback`, async (context) => {
58153
58833
  const workspaceId = context.req.param("workspaceId");
58154
- const access = await requireAccessGrantAuthorization13(
58834
+ const access = await requireAccessGrantAuthorization14(
58155
58835
  context,
58156
58836
  deps,
58157
58837
  workspaceId,
@@ -58198,7 +58878,7 @@ function registerCompanyProfileRoutes(app, deps) {
58198
58878
  });
58199
58879
  app.post(`${base}/revisions/:revisionId/activate`, async (context) => {
58200
58880
  const workspaceId = context.req.param("workspaceId");
58201
- const access = await requireAccessGrantAuthorization13(
58881
+ const access = await requireAccessGrantAuthorization14(
58202
58882
  context,
58203
58883
  deps,
58204
58884
  workspaceId,
@@ -58229,7 +58909,7 @@ function registerCompanyProfileRoutes(app, deps) {
58229
58909
  }
58230
58910
 
58231
58911
  // src/routes/company-brain.ts
58232
- import { createHash as createHash21 } from "crypto";
58912
+ import { createHash as createHash20 } from "crypto";
58233
58913
  import {
58234
58914
  COMPANY_BRAIN_INSPECTOR_CURSOR_MAX_CHARS,
58235
58915
  COMPANY_BRAIN_INSPECTOR_DEFAULT_LIMIT,
@@ -58264,7 +58944,7 @@ import {
58264
58944
  searchEffectiveKnowledge as searchEffectiveKnowledge2
58265
58945
  } from "@opengeni/documents";
58266
58946
  import { HTTPException as HTTPException64 } from "hono/http-exception";
58267
- import { z as z19 } from "zod";
58947
+ import { z as z20 } from "zod";
58268
58948
 
58269
58949
  // src/company-brain-okf.ts
58270
58950
  import {
@@ -58506,7 +59186,7 @@ function serializeCompanyBrainOkf(value) {
58506
59186
  }
58507
59187
 
58508
59188
  // src/workspace-state-projection.ts
58509
- import { createHash as createHash20 } from "crypto";
59189
+ import { createHash as createHash19 } from "crypto";
58510
59190
  import {
58511
59191
  KnowledgeMemoryKind,
58512
59192
  KnowledgeMemoryStatus,
@@ -58520,15 +59200,15 @@ import {
58520
59200
  WorkspaceStateResponse
58521
59201
  } from "@opengeni/contracts";
58522
59202
  function hashIdentities(values) {
58523
- return createHash20("sha256").update(values.join("\n"), "utf8").digest("hex");
59203
+ return createHash19("sha256").update(values.join("\n"), "utf8").digest("hex");
58524
59204
  }
58525
59205
  function policyTargetKey(value) {
58526
59206
  return `${value.kind}:${value.scope}:${value.roleKey ?? ""}`;
58527
59207
  }
58528
59208
  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;
59209
+ const keys2 = /* @__PURE__ */ new Set(["charter:global:", "policy:global:"]);
59210
+ if (policyRole !== null) keys2.add(`policy:role:${policyRole}`);
59211
+ return keys2;
58532
59212
  }
58533
59213
  function policyIdentity(value) {
58534
59214
  return `${policyTargetKey(value)}:${value.revisionId}:${value.contentHash}:${value.activationVersion}`;
@@ -58855,15 +59535,15 @@ function projectWorkspaceState(input) {
58855
59535
  var BASE_LIMIT = 24;
58856
59536
  var TOPIC_LIMIT = 24;
58857
59537
  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)
59538
+ var inspectorQuery = z20.object({
59539
+ attemptId: z20.string().uuid().optional(),
59540
+ cursor: z20.string().min(1).max(COMPANY_BRAIN_INSPECTOR_CURSOR_MAX_CHARS).optional(),
59541
+ limit: z20.coerce.number().int().positive().max(COMPANY_BRAIN_INSPECTOR_MAX_LIMIT).default(COMPANY_BRAIN_INSPECTOR_DEFAULT_LIMIT)
58862
59542
  }).strict().refine((value) => !(value.attemptId && value.cursor), {
58863
59543
  message: "attemptId cannot be combined with cursor"
58864
59544
  });
58865
59545
  function receiptCursorScope(workspaceId, subjectId) {
58866
- return createHash21("sha256").update("opengeni:company-brain-context-receipts:v1\0").update(workspaceId).update("\0").update(subjectId).digest("hex");
59546
+ return createHash20("sha256").update("opengeni:company-brain-context-receipts:v1\0").update(workspaceId).update("\0").update(subjectId).digest("hex");
58867
59547
  }
58868
59548
  function encodeReceiptCursor(workspaceId, subjectId, cursor) {
58869
59549
  return Buffer.from(
@@ -58875,11 +59555,11 @@ function decodeReceiptCursor(value, workspaceId, subjectId) {
58875
59555
  try {
58876
59556
  const bytes = Buffer.from(value, "base64url");
58877
59557
  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()
59558
+ const decoded2 = z20.object({
59559
+ v: z20.literal(1),
59560
+ s: z20.string().regex(/^[0-9a-f]{64}$/u),
59561
+ createdAt: z20.string().datetime(),
59562
+ id: z20.string().uuid()
58883
59563
  }).strict().parse(JSON.parse(bytes.toString("utf8")));
58884
59564
  if (decoded2.s !== receiptCursorScope(workspaceId, subjectId)) {
58885
59565
  throw new HTTPException64(409, { message: "context receipt cursor belongs to another scope" });
@@ -59101,7 +59781,7 @@ function registerCompanyBrainRoutes(app, deps) {
59101
59781
  app.get(`${base}/knowledge-proposals`, async (context) => {
59102
59782
  const workspaceId = context.req.param("workspaceId");
59103
59783
  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"));
59784
+ 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
59785
  if (!parsed.success) throw new HTTPException64(400, { message: "invalid proposal limit" });
59106
59786
  const response = await listCompanyBrainKnowledgeProposals(deps.db, {
59107
59787
  workspaceId,
@@ -59122,7 +59802,7 @@ import {
59122
59802
  } from "@opengeni/contracts";
59123
59803
  import {
59124
59804
  requireAccessGrant as requireAccessGrant37,
59125
- requireAccessGrantAuthorization as requireAccessGrantAuthorization14
59805
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization15
59126
59806
  } from "@opengeni/core";
59127
59807
  import {
59128
59808
  listSlackTaskPolicy,
@@ -59181,7 +59861,7 @@ function registerSlackTaskPolicyRoutes(app, deps) {
59181
59861
  });
59182
59862
  app.put(base, async (context) => {
59183
59863
  const workspaceId = context.req.param("workspaceId");
59184
- const access = await requireAccessGrantAuthorization14(
59864
+ const access = await requireAccessGrantAuthorization15(
59185
59865
  context,
59186
59866
  deps,
59187
59867
  workspaceId,
@@ -59235,7 +59915,7 @@ import { getDocumentInventory as getDocumentInventory2 } from "@opengeni/documen
59235
59915
  import { HTTPException as HTTPException66 } from "hono/http-exception";
59236
59916
 
59237
59917
  // src/workspace-state-export.ts
59238
- import { createHash as createHash22 } from "crypto";
59918
+ import { createHash as createHash21 } from "crypto";
59239
59919
  import {
59240
59920
  WORKSPACE_STATE_EXPORT_SCHEMA_VERSION,
59241
59921
  WorkspaceStateExportResponse
@@ -59266,7 +59946,7 @@ function createWorkspaceStateExport(state) {
59266
59946
  kind: "opengeni.workspace_state.sanitized_export",
59267
59947
  schemaVersion: WORKSPACE_STATE_EXPORT_SCHEMA_VERSION,
59268
59948
  generatedAt: state.generatedAt,
59269
- stateSha256: createHash22("sha256").update(canonicalState, "utf8").digest("hex"),
59949
+ stateSha256: createHash21("sha256").update(canonicalState, "utf8").digest("hex"),
59270
59950
  omissions: WORKSPACE_STATE_EXPORT_OMISSIONS,
59271
59951
  state
59272
59952
  });
@@ -59366,7 +60046,7 @@ function registerWorkspaceStateRoutes(app, deps) {
59366
60046
  }
59367
60047
 
59368
60048
  // src/routes/workspace-artifacts.ts
59369
- import { createHash as createHash23 } from "crypto";
60049
+ import { createHash as createHash22 } from "crypto";
59370
60050
  import {
59371
60051
  CreateWorkspaceArtifactRequest,
59372
60052
  PublishWorkspaceArtifactVersionRequest,
@@ -59381,7 +60061,7 @@ import {
59381
60061
  } from "@opengeni/contracts";
59382
60062
  import {
59383
60063
  requireAccessGrant as requireAccessGrant39,
59384
- requireAccessGrantAuthorization as requireAccessGrantAuthorization15,
60064
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization16,
59385
60065
  requireSessionAuthorization as requireSessionAuthorization8,
59386
60066
  SessionAuthorizationDeniedError as SessionAuthorizationDeniedError9,
59387
60067
  SessionAuthorizationUnavailableError as SessionAuthorizationUnavailableError7
@@ -59397,16 +60077,22 @@ import {
59397
60077
  setWorkspaceArtifactStatus as setWorkspaceArtifactStatus2,
59398
60078
  WorkspaceArtifactConflictError,
59399
60079
  WorkspaceArtifactNotFoundError as WorkspaceArtifactNotFoundError2,
59400
- WorkspaceArtifactOperationError
60080
+ WorkspaceArtifactOperationError as WorkspaceArtifactOperationError2
59401
60081
  } from "@opengeni/db";
59402
60082
  import { HTTPException as HTTPException67 } from "hono/http-exception";
59403
- import { z as z20 } from "zod";
59404
- var ArtifactId3 = z20.string().uuid();
60083
+ import { z as z21 } from "zod";
60084
+ var ArtifactId3 = z21.string().uuid();
59405
60085
  async function body(context, schema) {
59406
60086
  const parsed = schema.safeParse(await context.req.json().catch(() => null));
59407
60087
  if (!parsed.success) throw new HTTPException67(422, { message: "Invalid artifact request" });
59408
60088
  return parsed.data;
59409
60089
  }
60090
+ function parseVersionId(value) {
60091
+ if (value !== void 0 && !ArtifactId3.safeParse(value).success) {
60092
+ throw new HTTPException67(422, { message: "Invalid artifact version id" });
60093
+ }
60094
+ return value;
60095
+ }
59410
60096
  function artifactId(context) {
59411
60097
  const parsed = ArtifactId3.safeParse(context.req.param("artifactId"));
59412
60098
  if (!parsed.success) throw new HTTPException67(422, { message: "Invalid artifact id" });
@@ -59426,7 +60112,7 @@ function workspaceArtifactErrorResponse(context, error) {
59426
60112
  409
59427
60113
  );
59428
60114
  }
59429
- if (error instanceof WorkspaceArtifactOperationError) {
60115
+ if (error instanceof WorkspaceArtifactOperationError2) {
59430
60116
  return context.json(
59431
60117
  { code: "INVALID_WORKSPACE_ARTIFACT_OPERATION", message: error.message },
59432
60118
  422
@@ -59443,16 +60129,16 @@ function workspaceArtifactErrorResponse(context, error) {
59443
60129
  }
59444
60130
  throw error;
59445
60131
  }
59446
- function prepareContent(deps, workspaceId, input) {
60132
+ function prepareContent(deps, grant, input) {
59447
60133
  if (!deps.objectStorage)
59448
60134
  throw new HTTPException67(503, {
59449
60135
  message: "Object storage is not configured"
59450
60136
  });
59451
- return prepareWorkspaceArtifactContent(deps.objectStorage, workspaceId, input);
60137
+ return prepareWorkspaceArtifactPublication(deps, grant, input);
59452
60138
  }
59453
60139
  function provenance(subjectId, idempotencyKey) {
59454
60140
  return {
59455
- operationKey: `subject:${createHash23("sha256").update(`${subjectId}:${idempotencyKey}`).digest("hex")}`,
60141
+ operationKey: `subject:${createHash22("sha256").update(`${subjectId}:${idempotencyKey}`).digest("hex")}`,
59456
60142
  actorSubjectId: subjectId,
59457
60143
  sourceSessionId: null,
59458
60144
  sourceTurnId: null,
@@ -59489,6 +60175,28 @@ async function mutationResponse(deps, grant, response) {
59489
60175
  }
59490
60176
  function registerWorkspaceArtifactRoutes(app, deps) {
59491
60177
  const base = "/v1/workspaces/:workspaceId/published-artifacts";
60178
+ app.post(`${base}/uploads`, async (context) => {
60179
+ const grant = await requireAccessGrant39(
60180
+ context,
60181
+ deps,
60182
+ context.req.param("workspaceId"),
60183
+ "artifacts:publish"
60184
+ );
60185
+ return context.json(await prepareWorkspaceArtifactUpload(deps, grant));
60186
+ });
60187
+ app.get(`${base}/:artifactId/downloads`, async (context) => {
60188
+ const workspaceId = context.req.param("workspaceId");
60189
+ await requireAccessGrant39(context, deps, workspaceId, "artifacts:read");
60190
+ if (!deps.objectStorage)
60191
+ throw new HTTPException67(503, { message: "Object storage is not configured" });
60192
+ const ref = await getWorkspaceArtifactContentRef2(
60193
+ deps.db,
60194
+ workspaceId,
60195
+ artifactId(context),
60196
+ parseVersionId(context.req.query("versionId"))
60197
+ );
60198
+ return context.json(await workspaceArtifactDownloads(deps.objectStorage, ref, "public"));
60199
+ });
59492
60200
  app.get(base, async (context) => {
59493
60201
  const workspaceId = context.req.param("workspaceId");
59494
60202
  await requireAccessGrant39(context, deps, workspaceId, "artifacts:read");
@@ -59516,7 +60224,7 @@ function registerWorkspaceArtifactRoutes(app, deps) {
59516
60224
  });
59517
60225
  app.post(base, async (context) => {
59518
60226
  const workspaceId = context.req.param("workspaceId");
59519
- const authorization = await requireAccessGrantAuthorization15(
60227
+ const authorization = await requireAccessGrantAuthorization16(
59520
60228
  context,
59521
60229
  deps,
59522
60230
  workspaceId,
@@ -59527,12 +60235,13 @@ function registerWorkspaceArtifactRoutes(app, deps) {
59527
60235
  const id = crypto.randomUUID();
59528
60236
  const slugBase = request.slug ?? (normalizeWorkspaceArtifactSlug2(request.title) || "artifact");
59529
60237
  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
60238
  try {
60239
+ const content = await prepareContent(deps, grant, {
60240
+ ...request.html !== void 0 ? { html: request.html } : {},
60241
+ ...request.uploadId ? { uploadId: request.uploadId } : {},
60242
+ ...request.source ? { source: request.source } : {},
60243
+ ...request.requestedTools ? { requestedTools: request.requestedTools } : {}
60244
+ });
59536
60245
  return context.json(
59537
60246
  await mutationResponse(
59538
60247
  deps,
@@ -59572,6 +60281,42 @@ function registerWorkspaceArtifactRoutes(app, deps) {
59572
60281
  return workspaceArtifactErrorResponse(context, error);
59573
60282
  }
59574
60283
  });
60284
+ app.get(`${base}/:artifactId/html`, async (context) => {
60285
+ const workspaceId = context.req.param("workspaceId");
60286
+ await requireAccessGrant39(context, deps, workspaceId, "artifacts:read");
60287
+ if (!deps.objectStorage)
60288
+ throw new HTTPException67(503, { message: "Object storage is not configured" });
60289
+ const ref = await getWorkspaceArtifactContentRef2(
60290
+ deps.db,
60291
+ workspaceId,
60292
+ artifactId(context),
60293
+ parseVersionId(context.req.query("versionId"))
60294
+ );
60295
+ const iterator = readArtifactObject(deps.objectStorage, ref.contentKey);
60296
+ return new Response(
60297
+ new ReadableStream({
60298
+ async pull(controller) {
60299
+ try {
60300
+ const item = await iterator.next();
60301
+ if (item.done) controller.close();
60302
+ else controller.enqueue(item.value);
60303
+ } catch (error) {
60304
+ controller.error(error);
60305
+ }
60306
+ },
60307
+ async cancel() {
60308
+ await iterator.return();
60309
+ }
60310
+ }),
60311
+ {
60312
+ headers: {
60313
+ "Content-Type": "text/html; charset=utf-8",
60314
+ "Cache-Control": "private, no-store",
60315
+ "Content-Security-Policy": "sandbox allow-scripts"
60316
+ }
60317
+ }
60318
+ );
60319
+ });
59575
60320
  app.get(`${base}/:artifactId/content`, async (context) => {
59576
60321
  const workspaceId = context.req.param("workspaceId");
59577
60322
  await requireAccessGrant39(context, deps, workspaceId, "artifacts:read");
@@ -59579,10 +60324,7 @@ function registerWorkspaceArtifactRoutes(app, deps) {
59579
60324
  throw new HTTPException67(503, {
59580
60325
  message: "Object storage is not configured"
59581
60326
  });
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
- }
60327
+ const parsedVersion = parseVersionId(context.req.query("versionId"));
59586
60328
  try {
59587
60329
  const ref = await getWorkspaceArtifactContentRef2(
59588
60330
  deps.db,
@@ -59614,7 +60356,7 @@ function registerWorkspaceArtifactRoutes(app, deps) {
59614
60356
  });
59615
60357
  app.post(`${base}/:artifactId/versions`, async (context) => {
59616
60358
  const workspaceId = context.req.param("workspaceId");
59617
- const authorization = await requireAccessGrantAuthorization15(
60359
+ const authorization = await requireAccessGrantAuthorization16(
59618
60360
  context,
59619
60361
  deps,
59620
60362
  workspaceId,
@@ -59623,12 +60365,13 @@ function registerWorkspaceArtifactRoutes(app, deps) {
59623
60365
  const grant = authorization.grant;
59624
60366
  const request = await body(context, PublishWorkspaceArtifactVersionRequest);
59625
60367
  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
60368
  try {
60369
+ const content = await prepareContent(deps, grant, {
60370
+ ...request.html !== void 0 ? { html: request.html } : {},
60371
+ ...request.uploadId ? { uploadId: request.uploadId } : {},
60372
+ ...request.source ? { source: request.source } : {},
60373
+ ...request.requestedTools ? { requestedTools: request.requestedTools } : {}
60374
+ });
59632
60375
  return context.json(
59633
60376
  await mutationResponse(
59634
60377
  deps,
@@ -59651,7 +60394,7 @@ function registerWorkspaceArtifactRoutes(app, deps) {
59651
60394
  });
59652
60395
  app.post(`${base}/:artifactId/rollback`, async (context) => {
59653
60396
  const workspaceId = context.req.param("workspaceId");
59654
- const authorization = await requireAccessGrantAuthorization15(
60397
+ const authorization = await requireAccessGrantAuthorization16(
59655
60398
  context,
59656
60399
  deps,
59657
60400
  workspaceId,
@@ -59682,7 +60425,7 @@ function registerWorkspaceArtifactRoutes(app, deps) {
59682
60425
  });
59683
60426
  app.patch(`${base}/:artifactId/status`, async (context) => {
59684
60427
  const workspaceId = context.req.param("workspaceId");
59685
- const authorization = await requireAccessGrantAuthorization15(
60428
+ const authorization = await requireAccessGrantAuthorization16(
59686
60429
  context,
59687
60430
  deps,
59688
60431
  workspaceId,
@@ -59735,7 +60478,7 @@ import {
59735
60478
  import {
59736
60479
  hasPermission as hasPermission23,
59737
60480
  requireAccessGrant as requireAccessGrant40,
59738
- requireAccessGrantAuthorization as requireAccessGrantAuthorization16
60481
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization17
59739
60482
  } from "@opengeni/core";
59740
60483
  import {
59741
60484
  activatePreferenceRegistryRevision,
@@ -59758,8 +60501,8 @@ import {
59758
60501
  supersedePreferenceRegistry
59759
60502
  } from "@opengeni/db";
59760
60503
  import { HTTPException as HTTPException68 } from "hono/http-exception";
59761
- import { z as z21 } from "zod";
59762
- var Id = z21.string().uuid();
60504
+ import { z as z22 } from "zod";
60505
+ var Id = z22.string().uuid();
59763
60506
  async function parseBody4(context, schema) {
59764
60507
  const parsed = schema.safeParse(await context.req.json().catch(() => null));
59765
60508
  if (!parsed.success)
@@ -59883,7 +60626,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
59883
60626
  });
59884
60627
  app.post(`${base}/proposals`, async (context) => {
59885
60628
  const workspaceId = context.req.param("workspaceId");
59886
- const access = await requireAccessGrantAuthorization16(
60629
+ const access = await requireAccessGrantAuthorization17(
59887
60630
  context,
59888
60631
  deps,
59889
60632
  workspaceId,
@@ -59963,7 +60706,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
59963
60706
  });
59964
60707
  app.post(`${base}/:preferenceId/activate`, async (context) => {
59965
60708
  const workspaceId = context.req.param("workspaceId");
59966
- const access = await requireAccessGrantAuthorization16(
60709
+ const access = await requireAccessGrantAuthorization17(
59967
60710
  context,
59968
60711
  deps,
59969
60712
  workspaceId,
@@ -59992,7 +60735,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
59992
60735
  });
59993
60736
  app.post(`${base}/:preferenceId/correct`, async (context) => {
59994
60737
  const workspaceId = context.req.param("workspaceId");
59995
- const access = await requireAccessGrantAuthorization16(
60738
+ const access = await requireAccessGrantAuthorization17(
59996
60739
  context,
59997
60740
  deps,
59998
60741
  workspaceId,
@@ -60021,7 +60764,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
60021
60764
  });
60022
60765
  app.post(`${base}/:preferenceId/scope`, async (context) => {
60023
60766
  const workspaceId = context.req.param("workspaceId");
60024
- const access = await requireAccessGrantAuthorization16(
60767
+ const access = await requireAccessGrantAuthorization17(
60025
60768
  context,
60026
60769
  deps,
60027
60770
  workspaceId,
@@ -60050,7 +60793,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
60050
60793
  });
60051
60794
  app.post(`${base}/:preferenceId/deactivate`, async (context) => {
60052
60795
  const workspaceId = context.req.param("workspaceId");
60053
- const access = await requireAccessGrantAuthorization16(
60796
+ const access = await requireAccessGrantAuthorization17(
60054
60797
  context,
60055
60798
  deps,
60056
60799
  workspaceId,
@@ -60079,7 +60822,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
60079
60822
  });
60080
60823
  app.post(`${base}/:preferenceId/supersede`, async (context) => {
60081
60824
  const workspaceId = context.req.param("workspaceId");
60082
- const access = await requireAccessGrantAuthorization16(
60825
+ const access = await requireAccessGrantAuthorization17(
60083
60826
  context,
60084
60827
  deps,
60085
60828
  workspaceId,
@@ -60108,7 +60851,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
60108
60851
  });
60109
60852
  app.post(`${base}/:preferenceId/reject`, async (context) => {
60110
60853
  const workspaceId = context.req.param("workspaceId");
60111
- const access = await requireAccessGrantAuthorization16(
60854
+ const access = await requireAccessGrantAuthorization17(
60112
60855
  context,
60113
60856
  deps,
60114
60857
  workspaceId,
@@ -60208,7 +60951,7 @@ import { requireAccessGrant as requireAccessGrant43, TranscriptionServiceError a
60208
60951
  import { getWorkspace as getWorkspace5 } from "@opengeni/db";
60209
60952
 
60210
60953
  // src/routes/transcription-recordings.ts
60211
- import { createHash as createHash24 } from "crypto";
60954
+ import { createHash as createHash23 } from "crypto";
60212
60955
  import {
60213
60956
  CreateTranscriptionRecordingRequest,
60214
60957
  FinalizeTranscriptionRecordingRequest,
@@ -60564,8 +61307,8 @@ function registerResumableTranscriptionRoutes(app, deps) {
60564
61307
  c.req.raw,
60565
61308
  deps.settings.voiceInputResumableMaxChunkSizeBytes
60566
61309
  );
60567
- const sha2565 = sha256Hex3(body4);
60568
- if (sha2565 !== declaredSha256) {
61310
+ const sha2564 = sha256Hex3(body4);
61311
+ if (sha2564 !== declaredSha256) {
60569
61312
  return c.json({ code: "conflict" }, 409);
60570
61313
  }
60571
61314
  const existing = await getTranscriptionRecording(deps.db, {
@@ -60587,7 +61330,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
60587
61330
  recordingId: existing.recording.id,
60588
61331
  chunkNumber,
60589
61332
  byteLength: body4.byteLength,
60590
- sha256: sha2565,
61333
+ sha256: sha2564,
60591
61334
  startMilliseconds,
60592
61335
  durationMilliseconds,
60593
61336
  maxTotalBytes: deps.settings.voiceInputResumableMaxSizeBytes,
@@ -60600,7 +61343,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
60600
61343
  key: reservation.chunk.objectKey,
60601
61344
  contentType: existing.recording.mimeType,
60602
61345
  body: body4,
60603
- sha256: sha2565
61346
+ sha256: sha2564
60604
61347
  });
60605
61348
  } catch {
60606
61349
  throw new RecordingProcessingError("Chunk upload failed", "network", true);
@@ -60639,7 +61382,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
60639
61382
  app.post(
60640
61383
  "/v1/workspaces/:workspaceId/transcription-recordings/:recordingId/finalize",
60641
61384
  async (c) => {
60642
- const owner = correlationId(c);
61385
+ const owner2 = correlationId(c);
60643
61386
  let authority = null;
60644
61387
  let generation = 0;
60645
61388
  try {
@@ -60654,7 +61397,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
60654
61397
  workspaceId: authority.workspaceId,
60655
61398
  subjectId: authority.subjectId,
60656
61399
  recordingId,
60657
- owner,
61400
+ owner: owner2,
60658
61401
  ...parsed.data,
60659
61402
  staleBefore: new Date(Date.now() - PROCESSING_LEASE_MILLISECONDS)
60660
61403
  });
@@ -60702,15 +61445,15 @@ function registerResumableTranscriptionRoutes(app, deps) {
60702
61445
  chunks: verifiedChunkBytes(deps, chunks, c.req.raw.signal),
60703
61446
  signal: c.req.raw.signal
60704
61447
  })) {
60705
- const sha2565 = sha256Hex3(segment.bytes);
61448
+ const sha2564 = sha256Hex3(segment.bytes);
60706
61449
  const reservation = await reserveTranscriptionRecordingSegment(deps.db, {
60707
61450
  ...authority,
60708
61451
  recordingId,
60709
- owner,
61452
+ owner: owner2,
60710
61453
  generation,
60711
61454
  segmentNumber: segment.segmentNumber,
60712
61455
  byteLength: segment.bytes.byteLength,
60713
- sha256: sha2565,
61456
+ sha256: sha2564,
60714
61457
  startMilliseconds: segment.startMilliseconds,
60715
61458
  durationMilliseconds: segment.durationMilliseconds
60716
61459
  });
@@ -60719,7 +61462,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
60719
61462
  key: reservation.objectKey,
60720
61463
  contentType: segment.mimeType,
60721
61464
  body: segment.bytes,
60722
- sha256: sha2565
61465
+ sha256: sha2564
60723
61466
  });
60724
61467
  } catch {
60725
61468
  throw new RecordingProcessingError("Segment upload failed", "network", true);
@@ -60728,7 +61471,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
60728
61471
  workspaceId: authority.workspaceId,
60729
61472
  subjectId: authority.subjectId,
60730
61473
  recordingId,
60731
- owner,
61474
+ owner: owner2,
60732
61475
  generation,
60733
61476
  segmentNumber: segment.segmentNumber
60734
61477
  });
@@ -60738,7 +61481,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
60738
61481
  workspaceId: authority.workspaceId,
60739
61482
  subjectId: authority.subjectId,
60740
61483
  recordingId,
60741
- owner,
61484
+ owner: owner2,
60742
61485
  generation
60743
61486
  })
60744
61487
  );
@@ -60749,7 +61492,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
60749
61492
  workspaceId: authority.workspaceId,
60750
61493
  subjectId: authority.subjectId,
60751
61494
  recordingId: uuidParam3(c, "recordingId"),
60752
- owner,
61495
+ owner: owner2,
60753
61496
  generation,
60754
61497
  errorCode: failure.code,
60755
61498
  retryable: failure.retryable
@@ -60915,13 +61658,13 @@ async function cleanupTerminalObjects(deps, authority, response) {
60915
61658
  return response;
60916
61659
  }
60917
61660
  try {
60918
- const keys = await transcriptionRecordingObjectKeys(deps.db, {
61661
+ const keys2 = await transcriptionRecordingObjectKeys(deps.db, {
60919
61662
  workspaceId: authority.workspaceId,
60920
61663
  subjectId: authority.subjectId,
60921
61664
  recordingId: response.recording.id
60922
61665
  });
60923
61666
  let current = response;
60924
- for (const key of keys) {
61667
+ for (const key of keys2) {
60925
61668
  await deps.objectStorage.deleteObject(key);
60926
61669
  current = await markTranscriptionRecordingObjectCleaned(deps.db, {
60927
61670
  workspaceId: authority.workspaceId,
@@ -61031,7 +61774,7 @@ function valueAsUuid(value) {
61031
61774
  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
61775
  return value;
61033
61776
  }
61034
- const hex2 = createHash24("sha256").update(value).digest("hex").slice(0, 32);
61777
+ const hex2 = createHash23("sha256").update(value).digest("hex").slice(0, 32);
61035
61778
  return `${hex2.slice(0, 8)}-${hex2.slice(8, 12)}-4${hex2.slice(13, 16)}-a${hex2.slice(17, 20)}-${hex2.slice(20, 32)}`;
61036
61779
  }
61037
61780
  function nonnegativeInteger(value) {
@@ -61095,7 +61838,7 @@ async function readBoundedBody(request, maxBytes) {
61095
61838
  return body4;
61096
61839
  }
61097
61840
  function sha256Hex3(bytes) {
61098
- return createHash24("sha256").update(bytes).digest("hex");
61841
+ return createHash23("sha256").update(bytes).digest("hex");
61099
61842
  }
61100
61843
 
61101
61844
  // src/routes/transcriptions.ts
@@ -61401,8 +62144,8 @@ import {
61401
62144
  getCanonicalHumanIdentityProjection as getCanonicalHumanIdentityProjection2
61402
62145
  } from "@opengeni/db/canonical-human-identities";
61403
62146
  import { HTTPException as HTTPException71 } from "hono/http-exception";
61404
- import { z as z22 } from "zod";
61405
- var BindingId = z22.string().uuid();
62147
+ import { z as z23 } from "zod";
62148
+ var BindingId = z23.string().uuid();
61406
62149
  async function requestIdentity(context, deps) {
61407
62150
  return await requireCanonicalHumanRequestIdentity(context, {
61408
62151
  db: deps.db,
@@ -61596,11 +62339,11 @@ import {
61596
62339
  updateOrganizationRetentionPolicy
61597
62340
  } from "@opengeni/db";
61598
62341
  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();
62342
+ import { z as z24 } from "zod";
62343
+ var OrganizationId2 = z24.string().uuid();
62344
+ var WorkspaceId = z24.string().uuid();
62345
+ var MembershipId = z24.string().uuid();
62346
+ var InvitationId = z24.string().uuid();
61604
62347
  async function requireManagedHuman(context, deps) {
61605
62348
  if (deps.settings.productAccessMode !== "managed" || !deps.managedAuth || !context.req.header("cookie") || context.req.header("authorization")) {
61606
62349
  throw new HTTPException72(401, { message: "managed human session required" });
@@ -62347,9 +63090,9 @@ import {
62347
63090
  startOrganizationRecoveryOperation
62348
63091
  } from "@opengeni/db";
62349
63092
  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();
63093
+ import { z as z25 } from "zod";
63094
+ var OrganizationId3 = z25.string().uuid();
63095
+ var RecoveryOperationId = z25.string().uuid();
62353
63096
  var productionServices = {
62354
63097
  requireCanonicalHumanRequestIdentity: requireCanonicalHumanRequestIdentity2,
62355
63098
  getManagedAuthRequestActorAdmissionStamp,
@@ -62897,15 +63640,15 @@ import {
62897
63640
  ManagedAuthSessionSetOperationReuseError,
62898
63641
  mutateManagedAuthSessionSet
62899
63642
  } from "@opengeni/db/managed-auth-session-sets";
62900
- import { ensureManagedAccessForUser as ensureManagedAccessForUser2, getSession as getSession8 } from "@opengeni/db";
63643
+ import { ensureManagedAccessForUser as ensureManagedAccessForUser2, getSession as getSession9 } from "@opengeni/db";
62901
63644
  import { deleteCookie as deleteCookie3, getCookie, setCookie as setCookie3 } from "hono/cookie";
62902
63645
  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)
63646
+ import { z as z26 } from "zod";
63647
+ var ManagedAuthSocialStartReceipt = z26.object({
63648
+ version: z26.literal(1),
63649
+ requestDigest: z26.string().regex(/^[0-9a-f]{64}$/u),
63650
+ url: z26.string().url().max(4096),
63651
+ stateCookie: z26.string().min(1).max(4096)
62909
63652
  }).strict();
62910
63653
  function registerManagedAuthSessionSetRoutes(app, deps) {
62911
63654
  const noStore = async (context, next) => {
@@ -63535,7 +64278,7 @@ async function resolveCanonicalDeepLink(deps, authority, path, expectedActorEpoc
63535
64278
  continue;
63536
64279
  }
63537
64280
  if (target.sessionId) {
63538
- const session = await getSession8(deps.db, grant.workspaceId, target.sessionId);
64281
+ const session = await getSession9(deps.db, grant.workspaceId, target.sessionId);
63539
64282
  if (!session) continue;
63540
64283
  try {
63541
64284
  await requireSessionAuthorization9(deps, grant, {
@@ -63834,7 +64577,7 @@ import {
63834
64577
  import {
63835
64578
  issueManagedHumanUserResourceGrant,
63836
64579
  listManagedHumanUserResourceAuthorities,
63837
- requireAccessGrantAuthorization as requireAccessGrantAuthorization17,
64580
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization18,
63838
64581
  revokeManagedHumanUserResourceGrant,
63839
64582
  SessionAuthorizationDeniedError as SessionAuthorizationDeniedError10,
63840
64583
  SessionAuthorizationUnavailableError as SessionAuthorizationUnavailableError8,
@@ -63842,7 +64585,7 @@ import {
63842
64585
  } from "@opengeni/core";
63843
64586
  import { nestedPostgresSqlState as nestedPostgresSqlState9, SessionTenancyNotActivatedError as SessionTenancyNotActivatedError2 } from "@opengeni/db";
63844
64587
  import { HTTPException as HTTPException76 } from "hono/http-exception";
63845
- import { z as z26 } from "zod";
64588
+ import { z as z27 } from "zod";
63846
64589
  async function body2(context, schema) {
63847
64590
  const parsed = schema.safeParse(await context.req.json().catch(() => null));
63848
64591
  if (!parsed.success) throw new HTTPException76(422, { message: "invalid user-resource request" });
@@ -63874,7 +64617,7 @@ function registerUserResourceAuthorityRoutes(app, deps) {
63874
64617
  });
63875
64618
  if (!query.success) throw new HTTPException76(422, { message: "explicit scope=user required" });
63876
64619
  const workspaceId = context.req.param("workspaceId");
63877
- const access = await requireAccessGrantAuthorization17(
64620
+ const access = await requireAccessGrantAuthorization18(
63878
64621
  context,
63879
64622
  deps,
63880
64623
  workspaceId,
@@ -63899,10 +64642,10 @@ function registerUserResourceAuthorityRoutes(app, deps) {
63899
64642
  });
63900
64643
  app.post(`${base}/:authorityId/grants`, async (context) => {
63901
64644
  const workspaceId = context.req.param("workspaceId");
63902
- const authorityId = z26.string().uuid().safeParse(context.req.param("authorityId"));
64645
+ const authorityId = z27.string().uuid().safeParse(context.req.param("authorityId"));
63903
64646
  if (!authorityId.success) throw new HTTPException76(422, { message: "invalid authority id" });
63904
64647
  const request = await body2(context, IssueUserResourceGrantRequest);
63905
- const access = await requireAccessGrantAuthorization17(
64648
+ const access = await requireAccessGrantAuthorization18(
63906
64649
  context,
63907
64650
  deps,
63908
64651
  workspaceId,
@@ -63929,10 +64672,10 @@ function registerUserResourceAuthorityRoutes(app, deps) {
63929
64672
  app.delete(`${base}/grants/:grantId`, async (context) => {
63930
64673
  const query = RevokeUserResourceGrantQuery.safeParse({ scope: context.req.query("scope") });
63931
64674
  if (!query.success) throw new HTTPException76(422, { message: "explicit scope=user required" });
63932
- const grantId = z26.string().uuid().safeParse(context.req.param("grantId"));
64675
+ const grantId = z27.string().uuid().safeParse(context.req.param("grantId"));
63933
64676
  if (!grantId.success) throw new HTTPException76(422, { message: "invalid grant id" });
63934
64677
  const workspaceId = context.req.param("workspaceId");
63935
- const access = await requireAccessGrantAuthorization17(
64678
+ const access = await requireAccessGrantAuthorization18(
63936
64679
  context,
63937
64680
  deps,
63938
64681
  workspaceId,
@@ -63962,7 +64705,7 @@ import {
63962
64705
  import {
63963
64706
  issueManagedHumanUserResourceGrant as issueManagedHumanUserResourceGrant2,
63964
64707
  listManagedHumanUserResourceAuthorities as listManagedHumanUserResourceAuthorities2,
63965
- requireAccessGrantAuthorization as requireAccessGrantAuthorization18,
64708
+ requireAccessGrantAuthorization as requireAccessGrantAuthorization19,
63966
64709
  revokeManagedHumanUserResourceGrant as revokeManagedHumanUserResourceGrant2,
63967
64710
  SessionAuthorizationDeniedError as SessionAuthorizationDeniedError11,
63968
64711
  SessionAuthorizationUnavailableError as SessionAuthorizationUnavailableError9,
@@ -63970,7 +64713,7 @@ import {
63970
64713
  } from "@opengeni/core";
63971
64714
  import { nestedPostgresSqlState as nestedPostgresSqlState10, SessionTenancyNotActivatedError as SessionTenancyNotActivatedError3 } from "@opengeni/db";
63972
64715
  import { HTTPException as HTTPException78 } from "hono/http-exception";
63973
- import { z as z27 } from "zod";
64716
+ import { z as z28 } from "zod";
63974
64717
 
63975
64718
  // src/connection-authority-owner.ts
63976
64719
  import {
@@ -64028,7 +64771,7 @@ function registerConnectionAuthorityRoutes(app, deps) {
64028
64771
  });
64029
64772
  if (!query.success) throw new HTTPException78(422, { message: "explicit scope=user required" });
64030
64773
  const workspaceId = context.req.param("workspaceId");
64031
- const access = await requireAccessGrantAuthorization18(
64774
+ const access = await requireAccessGrantAuthorization19(
64032
64775
  context,
64033
64776
  deps,
64034
64777
  workspaceId,
@@ -64050,10 +64793,10 @@ function registerConnectionAuthorityRoutes(app, deps) {
64050
64793
  });
64051
64794
  app.post(`${base}/:authorityId/grants`, async (context) => {
64052
64795
  const workspaceId = context.req.param("workspaceId");
64053
- const authorityId = z27.string().uuid().safeParse(context.req.param("authorityId"));
64796
+ const authorityId = z28.string().uuid().safeParse(context.req.param("authorityId"));
64054
64797
  if (!authorityId.success) throw new HTTPException78(422, { message: "invalid authority id" });
64055
64798
  const request = await body3(context, IssueConnectionUseGrantRequest2);
64056
- const access = await requireAccessGrantAuthorization18(
64799
+ const access = await requireAccessGrantAuthorization19(
64057
64800
  context,
64058
64801
  deps,
64059
64802
  workspaceId,
@@ -64081,10 +64824,10 @@ function registerConnectionAuthorityRoutes(app, deps) {
64081
64824
  app.delete(`${base}/grants/:grantId`, async (context) => {
64082
64825
  const query = RevokeConnectionUseGrantQuery.safeParse({ scope: context.req.query("scope") });
64083
64826
  if (!query.success) throw new HTTPException78(422, { message: "explicit scope=user required" });
64084
- const grantId = z27.string().uuid().safeParse(context.req.param("grantId"));
64827
+ const grantId = z28.string().uuid().safeParse(context.req.param("grantId"));
64085
64828
  if (!grantId.success) throw new HTTPException78(422, { message: "invalid grant id" });
64086
64829
  const workspaceId = context.req.param("workspaceId");
64087
- const access = await requireAccessGrantAuthorization18(
64830
+ const access = await requireAccessGrantAuthorization19(
64088
64831
  context,
64089
64832
  deps,
64090
64833
  workspaceId,
@@ -64610,7 +65353,7 @@ async function exactAvailable(providers, providerId, context) {
64610
65353
  }
64611
65354
 
64612
65355
  // src/integrations/slack-interactions.ts
64613
- import { createHash as createHash26, createHmac as createHmac9, timingSafeEqual as timingSafeEqual3 } from "crypto";
65356
+ import { createHash as createHash25, createHmac as createHmac9, timingSafeEqual as timingSafeEqual3 } from "crypto";
64614
65357
  import {
64615
65358
  approvalIdentifier,
64616
65359
  ApproveSlackUserLinkAccessRequest,
@@ -64653,7 +65396,7 @@ import {
64653
65396
  getConnectionMetadata as getConnectionMetadata12,
64654
65397
  getOrCreateSlackInteraction,
64655
65398
  getLatestSessionModelForSubject,
64656
- getSession as getSession9,
65399
+ getSession as getSession10,
64657
65400
  getSessionEvent as getSessionEvent2,
64658
65401
  getSessionHumanInputRequest as getSessionHumanInputRequest2,
64659
65402
  childRequiresActionResolutionExists,
@@ -65088,7 +65831,7 @@ function record5(value) {
65088
65831
  }
65089
65832
 
65090
65833
  // src/slack-reaction-files.ts
65091
- import { createHash as createHash25 } from "crypto";
65834
+ import { createHash as createHash24 } from "crypto";
65092
65835
  import { completeFileUpload as completeFileUpload5, prepareGeneratedWorkspaceFile as prepareGeneratedWorkspaceFile2 } from "@opengeni/db";
65093
65836
  import { retryWhileMissing as retryWhileMissing6 } from "@opengeni/storage";
65094
65837
  var IMPORT_IDENTITY_VERSION = "slack-reaction-image-v1";
@@ -65105,7 +65848,7 @@ async function importSlackReactionImage(dependencies, source, image, ordinal) {
65105
65848
  if (image.bytes.byteLength < 1 || image.bytes.byteLength > 4 * 1024 * 1024) {
65106
65849
  throw new Error("Slack reaction image size is invalid");
65107
65850
  }
65108
- const sha2565 = createHash25("sha256").update(image.bytes).digest("hex");
65851
+ const sha2564 = createHash24("sha256").update(image.bytes).digest("hex");
65109
65852
  const identity = slackReactionImageIdentity(source, image.fileId);
65110
65853
  const fileId = deterministicUuid(`${identity}:file`);
65111
65854
  const uploadId = deterministicUuid(`${identity}:upload`);
@@ -65123,7 +65866,7 @@ async function importSlackReactionImage(dependencies, source, image, ordinal) {
65123
65866
  safeFilename,
65124
65867
  contentType: image.contentType,
65125
65868
  sizeBytes: image.bytes.byteLength,
65126
- sha256: sha2565,
65869
+ sha256: sha2564,
65127
65870
  bucket: storage.bucket,
65128
65871
  objectKey,
65129
65872
  expiresAt: new Date((dependencies.now?.() ?? /* @__PURE__ */ new Date()).getTime() + UPLOAD_INTENT_TTL_MS2)
@@ -65133,23 +65876,23 @@ async function importSlackReactionImage(dependencies, source, image, ordinal) {
65133
65876
  if (file.status !== "ready") {
65134
65877
  const existing = await storage.headObject(objectKey);
65135
65878
  if (existing) {
65136
- assertStoredSlackImage(existing, image, sha2565);
65879
+ assertStoredSlackImage(existing, image, sha2564);
65137
65880
  } else if (storage.putObjectIfAbsent) {
65138
65881
  const created = await storage.putObjectIfAbsent({
65139
65882
  key: objectKey,
65140
65883
  contentType: image.contentType,
65141
65884
  body: image.bytes,
65142
- sha256: sha2565
65885
+ sha256: sha2564
65143
65886
  });
65144
65887
  if (!created) {
65145
- await assertVisibleSlackImage(storage, objectKey, image, sha2565);
65888
+ await assertVisibleSlackImage(storage, objectKey, image, sha2564);
65146
65889
  }
65147
65890
  } else {
65148
65891
  await storage.putObject({
65149
65892
  key: objectKey,
65150
65893
  contentType: image.contentType,
65151
65894
  body: image.bytes,
65152
- sha256: sha2565
65895
+ sha256: sha2564
65153
65896
  });
65154
65897
  }
65155
65898
  file = await (dependencies.completeFile ?? completeFileUpload5)(
@@ -65158,9 +65901,9 @@ async function importSlackReactionImage(dependencies, source, image, ordinal) {
65158
65901
  uploadId
65159
65902
  );
65160
65903
  } else {
65161
- await assertVisibleSlackImage(storage, objectKey, image, sha2565);
65904
+ await assertVisibleSlackImage(storage, objectKey, image, sha2564);
65162
65905
  }
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) {
65906
+ 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
65907
  throw new Error("Imported Slack image differs from its immutable workspace file");
65165
65908
  }
65166
65909
  const mountPath = `attachments/slack/${String(ordinal).padStart(2, "0")}-${safeFilename}`;
@@ -65189,17 +65932,17 @@ function safeSlackImageFilename(value, contentType) {
65189
65932
  const stem = withoutKnownExtension.replace(/^[. -]+|[. ]+$/gu, "").slice(0, 180) || "slack-image";
65190
65933
  return `${stem}${extension}`;
65191
65934
  }
65192
- async function assertVisibleSlackImage(storage, objectKey, image, sha2565) {
65935
+ async function assertVisibleSlackImage(storage, objectKey, image, sha2564) {
65193
65936
  const head = await retryWhileMissing6(async () => await storage.headObject(objectKey));
65194
- assertStoredSlackImage(head, image, sha2565);
65937
+ assertStoredSlackImage(head, image, sha2564);
65195
65938
  }
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) {
65939
+ function assertStoredSlackImage(head, image, sha2564) {
65940
+ if (!head || head.ContentLength !== image.bytes.byteLength || head.ContentType !== image.contentType || head.Metadata?.sha256 !== sha2564 || typeof head.VersionToken !== "string" || head.VersionToken.length < 1) {
65198
65941
  throw new Error("Stored Slack image differs from its immutable import");
65199
65942
  }
65200
65943
  }
65201
65944
  function deterministicUuid(seed) {
65202
- const bytes = createHash25("sha256").update(seed, "utf8").digest().subarray(0, 16);
65945
+ const bytes = createHash24("sha256").update(seed, "utf8").digest().subarray(0, 16);
65203
65946
  bytes[6] = (bytes[6] ?? 0) & 15 | 80;
65204
65947
  bytes[8] = (bytes[8] ?? 0) & 63 | 128;
65205
65948
  const hex2 = bytes.toString("hex");
@@ -65358,7 +66101,7 @@ function slackReactionInboxEntry(payload, bot, settings) {
65358
66101
  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
66102
  return null;
65360
66103
  }
65361
- const stableReactionIdentity = createHash26("sha256").update([teamId, userId, channelId, timestamp, reaction].join("\n")).digest("hex");
66104
+ const stableReactionIdentity = createHash25("sha256").update([teamId, userId, channelId, timestamp, reaction].join("\n")).digest("hex");
65362
66105
  return {
65363
66106
  providerEventId: eventId,
65364
66107
  providerMessageId: `reaction:${stableReactionIdentity}`,
@@ -65548,7 +66291,7 @@ function registerSlackInteractionRoutes(app, deps) {
65548
66291
  const prepared = await prepareSlackUserLinkAccessRequest(deps.db, {
65549
66292
  accountId: installation.accountId,
65550
66293
  workspaceId,
65551
- tokenDigest: createHash26("sha256").update(payload.linkToken).digest("hex"),
66294
+ tokenDigest: createHash25("sha256").update(payload.linkToken).digest("hex"),
65552
66295
  connectionId: link.connectionId,
65553
66296
  slackTeamId: link.slackTeamId,
65554
66297
  slackUserId: link.slackUserId,
@@ -65755,7 +66498,7 @@ function registerSlackInteractionRoutes(app, deps) {
65755
66498
  const prepared = await prepareSlackUserLinkAccessRequest(deps.db, {
65756
66499
  accountId: grant.accountId,
65757
66500
  workspaceId,
65758
- tokenDigest: createHash26("sha256").update(linkToken).digest("hex"),
66501
+ tokenDigest: createHash25("sha256").update(linkToken).digest("hex"),
65759
66502
  connectionId: link.connectionId,
65760
66503
  slackTeamId: link.slackTeamId,
65761
66504
  slackUserId: link.slackUserId,
@@ -67219,7 +67962,7 @@ function slackReactionPreparedEntry(entry2, context, prepared) {
67219
67962
  }
67220
67963
  async function remountSlackReactionTaskForSession(deps, workspaceId, sessionId, prepared) {
67221
67964
  if (prepared.attachments.length === 0) return prepared;
67222
- const session = await getSession9(deps.db, workspaceId, sessionId);
67965
+ const session = await getSession10(deps.db, workspaceId, sessionId);
67223
67966
  if (!session) throw new SlackInteractionPermanentError("session_not_found");
67224
67967
  const initialOrdinal = session.resources.reduce((maximum, resource) => {
67225
67968
  if (resource.kind !== "file" || !resource.mountPath) return maximum;
@@ -67815,7 +68558,7 @@ async function executeSlackAction(deps, grant, interaction, handle) {
67815
68558
  controlState: action === "pause" ? "paused" : "active"
67816
68559
  };
67817
68560
  }
67818
- const session = await getSession9(deps.db, grant.workspaceId, handle.sessionId);
68561
+ const session = await getSession10(deps.db, grant.workspaceId, handle.sessionId);
67819
68562
  if (!session) throw new SlackInteractionPermanentError("slack_action_session_missing");
67820
68563
  const recurring = hasPermission25(grant.permissions, "sessions:read") && hasPermission25(grant.permissions, "scheduled_tasks:manage") ? makeRecurringText(deps, grant.workspaceId, handle.sessionId) : null;
67821
68564
  return {
@@ -68636,7 +69379,7 @@ function normalizedBlockActionInteraction(payload) {
68636
69379
  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
69380
  throw new HTTPException79(400, { message: "invalid Slack block action" });
68638
69381
  }
68639
- const identity = createHash26("sha256").update([teamId, userId, channelId, messageTs, actionTs, actionId, handleId].join("\n")).digest("hex");
69382
+ const identity = createHash25("sha256").update([teamId, userId, channelId, messageTs, actionTs, actionId, handleId].join("\n")).digest("hex");
68640
69383
  return {
68641
69384
  providerEventId: `block:${identity}`,
68642
69385
  providerMessageId: `block:${identity}`,
@@ -68901,7 +69644,7 @@ function usesPrivateBotDm(interaction, entry2) {
68901
69644
  return isDirectMessageShortcut(entry2) || interaction.visibility === "private" && entry2.triggerKind !== "dm";
68902
69645
  }
68903
69646
  function deterministicUuid2(value) {
68904
- const bytes = createHash26("sha256").update(value).digest().subarray(0, 16);
69647
+ const bytes = createHash25("sha256").update(value).digest().subarray(0, 16);
68905
69648
  bytes[6] = bytes[6] & 15 | 80;
68906
69649
  bytes[8] = bytes[8] & 63 | 128;
68907
69650
  const hex2 = bytes.toString("hex");
@@ -69021,7 +69764,7 @@ import {
69021
69764
  validateToolRefs,
69022
69765
  withDefaultEnabledCapabilityMcpTools
69023
69766
  } from "@opengeni/core";
69024
- import { workflowIdForSession as workflowIdForSession3 } from "@opengeni/core";
69767
+ import { workflowIdForSession as workflowIdForSession4 } from "@opengeni/core";
69025
69768
  var API_MAX_REQUEST_BODY_BYTES = 32 * 1024 * 1024;
69026
69769
  var managedAuthReaperDatabases = /* @__PURE__ */ new WeakSet();
69027
69770
  var MANAGED_AUTH_REAPER_INTERVAL_MS = 6e4;
@@ -69317,13 +70060,13 @@ function createAppComposition(deps) {
69317
70060
  return await accessKeyBoundary(c, next);
69318
70061
  });
69319
70062
  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) {
70063
+ c.header(OPENGENI_API_CONTRACT_HEADER2, OPENGENI_API_CONTRACT_REVISION2);
70064
+ 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
70065
  return c.json(
69323
70066
  {
69324
70067
  code: "API_CONTRACT_CHANGED",
69325
70068
  message: "OpenGeni updated. Reload this client before changing state.",
69326
- apiContractRevision: OPENGENI_API_CONTRACT_REVISION
70069
+ apiContractRevision: OPENGENI_API_CONTRACT_REVISION2
69327
70070
  },
69328
70071
  409
69329
70072
  );
@@ -69522,7 +70265,7 @@ function createAppComposition(deps) {
69522
70265
  return c.json(
69523
70266
  ClientConfig.parse({
69524
70267
  deploymentRevision: deps.settings.deploymentRevision,
69525
- apiContractRevision: OPENGENI_API_CONTRACT_REVISION,
70268
+ apiContractRevision: OPENGENI_API_CONTRACT_REVISION2,
69526
70269
  ...deps.settings.serverVersion ? { serverVersion: deps.settings.serverVersion } : {},
69527
70270
  defaultModel: canonicalizeConfiguredModelId2(catalogSettings, catalogSettings.openaiModel),
69528
70271
  allowedModels: configuredAllowedModels(catalogSettings),
@@ -69561,6 +70304,7 @@ function createAppComposition(deps) {
69561
70304
  } : {}
69562
70305
  },
69563
70306
  productAccessMode: deps.settings.productAccessMode,
70307
+ billingMode: deps.settings.billingMode,
69564
70308
  managedAuthSessionSetMode: deps.settings.managedAuthSessionSetMode,
69565
70309
  auth: clientAuthConfig(deps.settings),
69566
70310
  analytics: clientAnalyticsConfig(deps.settings),
@@ -69696,7 +70440,7 @@ function createAppComposition(deps) {
69696
70440
  });
69697
70441
  app.get("/v1/workspaces/:workspaceId/tools/catalog", async (c) => {
69698
70442
  const workspaceId = c.req.param("workspaceId");
69699
- const authorization = await requireAccessGrantAuthorization19(
70443
+ const authorization = await requireAccessGrantAuthorization20(
69700
70444
  c,
69701
70445
  routeDeps,
69702
70446
  workspaceId,
@@ -69712,7 +70456,7 @@ function createAppComposition(deps) {
69712
70456
  });
69713
70457
  app.post("/v1/workspaces/:workspaceId/tools/calls", async (c) => {
69714
70458
  const workspaceId = c.req.param("workspaceId");
69715
- const authorization = await requireAccessGrantAuthorization19(
70459
+ const authorization = await requireAccessGrantAuthorization20(
69716
70460
  c,
69717
70461
  routeDeps,
69718
70462
  workspaceId,
@@ -69741,7 +70485,7 @@ function createAppComposition(deps) {
69741
70485
  });
69742
70486
  app.post("/v1/workspaces/:workspaceId/tools/approvals", async (c) => {
69743
70487
  const workspaceId = c.req.param("workspaceId");
69744
- const authorization = await requireAccessGrantAuthorization19(
70488
+ const authorization = await requireAccessGrantAuthorization20(
69745
70489
  c,
69746
70490
  routeDeps,
69747
70491
  workspaceId,
@@ -69770,7 +70514,7 @@ function createAppComposition(deps) {
69770
70514
  });
69771
70515
  app.get("/v1/workspaces/:workspaceId/tools/declarations", async (c) => {
69772
70516
  const workspaceId = c.req.param("workspaceId");
69773
- const authorization = await requireAccessGrantAuthorization19(
70517
+ const authorization = await requireAccessGrantAuthorization20(
69774
70518
  c,
69775
70519
  routeDeps,
69776
70520
  workspaceId,
@@ -69784,6 +70528,19 @@ function createAppComposition(deps) {
69784
70528
  await prepared.close();
69785
70529
  }
69786
70530
  });
70531
+ app.all("/v1/workspaces/:workspaceId/codemode/sdk/*", async (c) => {
70532
+ const workspaceId = c.req.param("workspaceId");
70533
+ const grant = await requireAccessGrant46(c, routeDeps, workspaceId);
70534
+ const url = new URL(c.req.url);
70535
+ const prefix = `/v1/workspaces/${workspaceId}/codemode/sdk`;
70536
+ const forwarded = await codemodeSessionRequest(
70537
+ routeDeps,
70538
+ grant,
70539
+ c.req.raw,
70540
+ url.pathname.slice(prefix.length) + url.search
70541
+ );
70542
+ return app.fetch(forwarded);
70543
+ });
69787
70544
  app.get("/v1/workspaces/:workspaceId/codemode/catalog", async (c) => {
69788
70545
  const workspaceId = c.req.param("workspaceId");
69789
70546
  const grant = await requireAccessGrant46(c, routeDeps, workspaceId);
@@ -69912,7 +70669,7 @@ function createAppComposition(deps) {
69912
70669
  const requestId = correlationIds.get(c.req.raw) ?? crypto.randomUUID();
69913
70670
  c.header(OPENGENI_CORRELATION_HEADER, requestId);
69914
70671
  if (new URL(c.req.url).pathname.startsWith("/v1/")) {
69915
- c.header(OPENGENI_API_CONTRACT_HEADER, OPENGENI_API_CONTRACT_REVISION);
70672
+ c.header(OPENGENI_API_CONTRACT_HEADER2, OPENGENI_API_CONTRACT_REVISION2);
69916
70673
  }
69917
70674
  const envelope = ErrorEnvelope.parse({
69918
70675
  error: {
@@ -69970,7 +70727,7 @@ async function validateCodexAccountTarget(request) {
69970
70727
  }
69971
70728
  }
69972
70729
  async function requireMcpAccessGrantAuthorization(c, deps, workspaceId) {
69973
- const authorization = await requireAccessGrantAuthorization19(c, deps, workspaceId);
70730
+ const authorization = await requireAccessGrantAuthorization20(c, deps, workspaceId);
69974
70731
  const grant = authorization.grant;
69975
70732
  if (hasPermission26(grant.permissions, "workspace:read")) {
69976
70733
  return authorization;
@@ -70684,6 +71441,10 @@ var routeLabelPatterns = [
70684
71441
  pattern: /^\/v1\/workspaces\/[^/]+\/github\/app$/,
70685
71442
  label: "/v1/workspaces/:workspaceId/github/app"
70686
71443
  },
71444
+ {
71445
+ pattern: /^\/v1\/workspaces\/[^/]+\/github\/action-policies$/,
71446
+ label: "/v1/workspaces/:workspaceId/github/action-policies"
71447
+ },
70687
71448
  {
70688
71449
  pattern: /^\/v1\/workspaces\/[^/]+\/github\/repositories$/,
70689
71450
  label: "/v1/workspaces/:workspaceId/github/repositories"
@@ -71090,6 +71851,6 @@ export {
71090
71851
  validateGitHubRepositorySelectionShapes,
71091
71852
  validateToolRefs,
71092
71853
  withDefaultEnabledCapabilityMcpTools,
71093
- workflowIdForSession3 as workflowIdForSession
71854
+ workflowIdForSession4 as workflowIdForSession
71094
71855
  };
71095
- //# sourceMappingURL=chunk-XXH7DW34.js.map
71856
+ //# sourceMappingURL=chunk-BVX5ASFY.js.map