@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.
- package/dist/app.js +1 -1
- package/dist/{chunk-XXH7DW34.js → chunk-BVX5ASFY.js} +1559 -798
- package/dist/chunk-BVX5ASFY.js.map +1 -0
- package/dist/codemode.d.ts +3 -0
- package/dist/index.js +1 -1
- package/dist/integrations/slack-interactions.d.ts +1 -1
- package/dist/mcp/command-read.d.ts +24 -0
- package/dist/mcp/scheduled-task-view.d.ts +3 -3
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/session-event-view.d.ts +86 -0
- package/dist/sandbox/channel-a.d.ts +2 -0
- package/dist/sandbox/rematerialize.d.ts +2 -0
- package/dist/sandbox/viewer.d.ts +2 -0
- package/dist/session-event-view-MU3HMNV3.js +264 -0
- package/dist/session-event-view-MU3HMNV3.js.map +1 -0
- package/dist/site-uploads.d.ts +41 -0
- package/dist/workspace-artifact-content.d.ts +4 -4
- package/package.json +19 -19
- package/src/app.ts +20 -0
- package/src/codemode.ts +52 -0
- package/src/integrations/oauth-client.ts +51 -7
- package/src/mcp/command-read.ts +109 -0
- package/src/mcp/server.ts +287 -153
- package/src/mcp/session-event-view.ts +336 -0
- package/src/mcp/session-wait.ts +1 -1
- package/src/routes/github.ts +124 -1
- package/src/routes/scheduled-tasks.ts +19 -1
- package/src/routes/sessions.ts +150 -7
- package/src/routes/workspace-artifacts.ts +92 -21
- package/src/sandbox/channel-a.ts +5 -0
- package/src/sandbox/rematerialize.ts +46 -3
- package/src/sandbox/viewer.ts +3 -0
- package/src/site-uploads.ts +202 -0
- package/src/workspace-artifact-content.ts +20 -39
- package/dist/chunk-XXH7DW34.js.map +0 -1
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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" || !
|
|
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
|
|
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,
|
|
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
|
|
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 (!
|
|
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
|
|
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" && !
|
|
5349
|
+
if (server.id === "docs" && !hasPermission4(grant.permissions, "documents:search")) {
|
|
4850
5350
|
return false;
|
|
4851
5351
|
}
|
|
4852
|
-
if (server.id === "files" && !
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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) :
|
|
7428
|
-
if (!grant || grant.accountId !== state.accountId || !
|
|
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
|
|
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
|
|
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" && !
|
|
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 {
|
|
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
|
|
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
|
|
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 || !
|
|
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
|
|
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 || !
|
|
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
|
|
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 (!
|
|
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:
|
|
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
|
|
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
|
|
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 || !
|
|
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: {
|
|
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) =>
|
|
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) =>
|
|
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
|
|
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}:${
|
|
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: "
|
|
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
|
|
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
|
|
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:${
|
|
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
|
-
"
|
|
23263
|
+
"artifacts_prepare_upload",
|
|
22695
23264
|
{
|
|
22696
|
-
description: "
|
|
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: "
|
|
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
|
|
23301
|
+
const downloads = await workspaceArtifactDownloads(deps.objectStorage, ref, "sandbox");
|
|
22737
23302
|
return json({
|
|
22738
23303
|
detail,
|
|
22739
23304
|
version: projectedVersion,
|
|
22740
|
-
|
|
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 ({
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
24183
|
-
|
|
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 ??
|
|
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: `
|
|
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
|
-
|
|
24338
|
-
|
|
24339
|
-
|
|
24340
|
-
|
|
24341
|
-
|
|
24342
|
-
|
|
24343
|
-
|
|
24344
|
-
|
|
24345
|
-
|
|
24346
|
-
|
|
24347
|
-
|
|
24348
|
-
|
|
24349
|
-
|
|
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
|
-
|
|
24408
|
-
|
|
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 &&
|
|
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 (!
|
|
25988
|
+
if (!hasPermission11(grant.permissions, "variable-sets:attach")) {
|
|
25376
25989
|
throw new Error("missing permission: variable-sets:attach");
|
|
25377
25990
|
}
|
|
25378
|
-
if (variableSetId !== null && !
|
|
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 (!
|
|
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 && !
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
35776
|
-
contentDigest:
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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
|
|
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 [
|
|
42891
|
-
if (!
|
|
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(
|
|
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 [
|
|
42955
|
-
if (!
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
49071
|
-
return
|
|
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
|
|
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 (
|
|
49727
|
+
resolveCommit: async (owner2, repository, ref) => {
|
|
49169
49728
|
const payload = recordValue(
|
|
49170
49729
|
await requestJson(
|
|
49171
|
-
`/repos/${encodeURIComponent(
|
|
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 (
|
|
49740
|
+
listTree: async (owner2, repository, commit) => {
|
|
49182
49741
|
const payload = recordValue(
|
|
49183
49742
|
await requestJson(
|
|
49184
|
-
`/repos/${encodeURIComponent(
|
|
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 (
|
|
49766
|
+
readBlob: async (owner2, repository, sha) => {
|
|
49208
49767
|
const payload = recordValue(
|
|
49209
49768
|
await requestJson(
|
|
49210
|
-
`/repos/${encodeURIComponent(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
49846
|
-
return
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (!
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
{
|
|
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 (!
|
|
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
|
-
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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
|
|
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:
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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" && !
|
|
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
|
|
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 =
|
|
55669
|
-
v:
|
|
55670
|
-
parentSessionId:
|
|
55671
|
-
search:
|
|
55672
|
-
cursor:
|
|
55673
|
-
orderBy:
|
|
55674
|
-
sortRevision:
|
|
55675
|
-
sortAt:
|
|
55676
|
-
id:
|
|
55677
|
-
snapshotAt:
|
|
55678
|
-
snapshotRevision:
|
|
55679
|
-
updatedAfter:
|
|
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 =
|
|
55704
|
-
v:
|
|
55705
|
-
parentSessionId:
|
|
55706
|
-
rootSessionId:
|
|
55707
|
-
query:
|
|
55708
|
-
statuses:
|
|
55709
|
-
|
|
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:
|
|
55721
|
-
recentHours:
|
|
55722
|
-
subject:
|
|
55723
|
-
namespace:
|
|
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:
|
|
56405
|
+
canonicalKey: z15.string().min(1).max(WORK_CLAIM_CANONICAL_KEY_MAX_BYTES2)
|
|
55726
56406
|
}).strict().nullable(),
|
|
55727
|
-
claimLimit:
|
|
55728
|
-
cursor:
|
|
55729
|
-
orderBy:
|
|
55730
|
-
sortRank:
|
|
55731
|
-
sortRevision:
|
|
55732
|
-
sortAt:
|
|
55733
|
-
id:
|
|
55734
|
-
snapshotAt:
|
|
55735
|
-
snapshotRevision:
|
|
55736
|
-
updatedAfter:
|
|
55737
|
-
filterHash:
|
|
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" && !
|
|
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 && !
|
|
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 =
|
|
55798
|
-
|
|
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
|
|
56066
|
-
import { requireAccessGrant as requireAccessGrant31, requireAccessGrantAuthorization as
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
57434
|
-
var WorkspaceInstructionPolicyRevisionId =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
57974
|
-
var RevisionId =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
58530
|
-
if (policyRole !== null)
|
|
58531
|
-
return
|
|
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 =
|
|
58859
|
-
attemptId:
|
|
58860
|
-
cursor:
|
|
58861
|
-
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
|
|
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 =
|
|
58879
|
-
v:
|
|
58880
|
-
s:
|
|
58881
|
-
createdAt:
|
|
58882
|
-
id:
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
59404
|
-
var ArtifactId3 =
|
|
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
|
|
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,
|
|
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
|
|
60137
|
+
return prepareWorkspaceArtifactPublication(deps, grant, input);
|
|
59452
60138
|
}
|
|
59453
60139
|
function provenance(subjectId, idempotencyKey) {
|
|
59454
60140
|
return {
|
|
59455
|
-
operationKey: `subject:${
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
59762
|
-
var Id =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
60568
|
-
if (
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
61405
|
-
var BindingId =
|
|
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
|
|
61600
|
-
var OrganizationId2 =
|
|
61601
|
-
var WorkspaceId =
|
|
61602
|
-
var MembershipId =
|
|
61603
|
-
var InvitationId =
|
|
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
|
|
62351
|
-
var OrganizationId3 =
|
|
62352
|
-
var RecoveryOperationId =
|
|
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
|
|
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
|
|
62904
|
-
var ManagedAuthSocialStartReceipt =
|
|
62905
|
-
version:
|
|
62906
|
-
requestDigest:
|
|
62907
|
-
url:
|
|
62908
|
-
stateCookie:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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,
|
|
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:
|
|
65885
|
+
sha256: sha2564
|
|
65143
65886
|
});
|
|
65144
65887
|
if (!created) {
|
|
65145
|
-
await assertVisibleSlackImage(storage, objectKey, image,
|
|
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:
|
|
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,
|
|
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 !==
|
|
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,
|
|
65935
|
+
async function assertVisibleSlackImage(storage, objectKey, image, sha2564) {
|
|
65193
65936
|
const head = await retryWhileMissing6(async () => await storage.headObject(objectKey));
|
|
65194
|
-
assertStoredSlackImage(head, image,
|
|
65937
|
+
assertStoredSlackImage(head, image, sha2564);
|
|
65195
65938
|
}
|
|
65196
|
-
function assertStoredSlackImage(head, image,
|
|
65197
|
-
if (!head || head.ContentLength !== image.bytes.byteLength || head.ContentType !== image.contentType || head.Metadata?.sha256 !==
|
|
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 =
|
|
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 =
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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(
|
|
69321
|
-
if (deps.settings.environment !== "test" && isApiContractProtectedMutation(c.req.method, new URL(c.req.url).pathname) && c.req.header(
|
|
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:
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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
|
-
|
|
71854
|
+
workflowIdForSession4 as workflowIdForSession
|
|
71094
71855
|
};
|
|
71095
|
-
//# sourceMappingURL=chunk-
|
|
71856
|
+
//# sourceMappingURL=chunk-BVX5ASFY.js.map
|