@opengeni/api-router 2.10.0-canary.0 → 2.10.0-canary.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +1 -1
- package/dist/{chunk-XXH7DW34.js → chunk-3VUEFGWS.js} +1555 -797
- package/dist/chunk-3VUEFGWS.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 +19 -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 +281 -152
- 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) {
|
|
@@ -25346,7 +25957,7 @@ function registerGitHubConnectTool(server, deps, grant, options, json) {
|
|
|
25346
25957
|
const installations = await listWorkspaceGitHubInstallationBindings(deps, grant.workspaceId);
|
|
25347
25958
|
const status = githubBindingStatus(true, installations);
|
|
25348
25959
|
const baseUrl = githubBrowserBaseUrl(settings, options.requestOrigin);
|
|
25349
|
-
const state = baseUrl &&
|
|
25960
|
+
const state = baseUrl && hasPermission11(grant.permissions, "github:manage") ? createSignedState6(deps.githubStateSecret, {
|
|
25350
25961
|
accountId: grant.accountId,
|
|
25351
25962
|
workspaceId: grant.workspaceId,
|
|
25352
25963
|
intent: "installation_authority",
|
|
@@ -25372,10 +25983,10 @@ function registerGitHubConnectTool(server, deps, grant, options, json) {
|
|
|
25372
25983
|
}
|
|
25373
25984
|
function requireVariableSetsUseForMcpAttachment(grant, variableSetId) {
|
|
25374
25985
|
if (variableSetId === void 0) return;
|
|
25375
|
-
if (!
|
|
25986
|
+
if (!hasPermission11(grant.permissions, "variable-sets:attach")) {
|
|
25376
25987
|
throw new Error("missing permission: variable-sets:attach");
|
|
25377
25988
|
}
|
|
25378
|
-
if (variableSetId !== null && !
|
|
25989
|
+
if (variableSetId !== null && !hasPermission11(grant.permissions, "variable-sets:use")) {
|
|
25379
25990
|
throw new Error("missing permission: variable-sets:use");
|
|
25380
25991
|
}
|
|
25381
25992
|
}
|
|
@@ -25383,10 +25994,10 @@ function requireVariableSetsUseForMcpAttachments(grant, selection) {
|
|
|
25383
25994
|
const singular = selection.variableSetId ?? selection.environmentId;
|
|
25384
25995
|
const variableSetIds = selection.variableSetIds ?? (singular ? [singular] : void 0);
|
|
25385
25996
|
if (variableSetIds === void 0) return;
|
|
25386
|
-
if (!
|
|
25997
|
+
if (!hasPermission11(grant.permissions, "variable-sets:attach")) {
|
|
25387
25998
|
throw new HTTPException19(403, { message: "missing permission: variable-sets:attach" });
|
|
25388
25999
|
}
|
|
25389
|
-
if (variableSetIds.length > 0 && !
|
|
26000
|
+
if (variableSetIds.length > 0 && !hasPermission11(grant.permissions, "variable-sets:use")) {
|
|
25390
26001
|
throw new HTTPException19(403, { message: "missing permission: variable-sets:use" });
|
|
25391
26002
|
}
|
|
25392
26003
|
}
|
|
@@ -25432,6 +26043,7 @@ var SESSION_DISCOVERY_PREVIEW_OMISSION_REASON = "aggregatePreviewBudget";
|
|
|
25432
26043
|
var SESSION_DISCOVERY_PAGE_MAX_BYTES = 128e3;
|
|
25433
26044
|
var SESSION_DISCOVERY_PREVIEW_DRILL_DOWN_TOOL = "session_events";
|
|
25434
26045
|
var SESSION_DISCOVERY_PREVIEW_DRILL_DOWN_BASE_INPUT = {
|
|
26046
|
+
view: "debug",
|
|
25435
26047
|
direction: "before",
|
|
25436
26048
|
limit: 1,
|
|
25437
26049
|
mode: "monitoring",
|
|
@@ -25798,171 +26410,6 @@ async function withMcpEffectivePolicy(deps, workspaceId, subjectId, session) {
|
|
|
25798
26410
|
return sessionWithEffectiveToolPolicy(session, workspaceServerIds, workspaceDefaultServerIds);
|
|
25799
26411
|
}
|
|
25800
26412
|
|
|
25801
|
-
// src/codemode.ts
|
|
25802
|
-
import {
|
|
25803
|
-
codemodeDispatchSubject,
|
|
25804
|
-
decodeCodemodeDispatchAck,
|
|
25805
|
-
encodeCodemodeDispatchRequest
|
|
25806
|
-
} from "@opengeni/codemode";
|
|
25807
|
-
import {
|
|
25808
|
-
CODEMODE_DISPATCH_TIMEOUT_MS,
|
|
25809
|
-
CodemodeCallRequest,
|
|
25810
|
-
CodemodeCallSubmission
|
|
25811
|
-
} from "@opengeni/contracts";
|
|
25812
|
-
import { hasPermission as hasPermission11, requireSessionAuthorization as requireSessionAuthorization3 } from "@opengeni/core";
|
|
25813
|
-
import {
|
|
25814
|
-
getActiveSessionTurnForExecution,
|
|
25815
|
-
getAttemptToolCatalog,
|
|
25816
|
-
getCodemodeOperation,
|
|
25817
|
-
submitCodemodeOperation
|
|
25818
|
-
} from "@opengeni/db";
|
|
25819
|
-
var CODEMODE_AUTHORITY_FAILURE_MESSAGES = {
|
|
25820
|
-
invalid_grant: "Codemode bearer does not carry valid execution-attempt authority",
|
|
25821
|
-
inactive_attempt: "Codemode execution attempt is no longer active",
|
|
25822
|
-
catalog_mismatch: "Codemode tool catalog does not match the active execution attempt"
|
|
25823
|
-
};
|
|
25824
|
-
var CodemodeAuthorityError = class extends Error {
|
|
25825
|
-
constructor(reason2) {
|
|
25826
|
-
super(CODEMODE_AUTHORITY_FAILURE_MESSAGES[reason2]);
|
|
25827
|
-
this.reason = reason2;
|
|
25828
|
-
this.name = "CodemodeAuthorityError";
|
|
25829
|
-
this.code = `codemode_${reason2}`;
|
|
25830
|
-
}
|
|
25831
|
-
code;
|
|
25832
|
-
};
|
|
25833
|
-
var CodemodeCatalogNotReadyError = class extends Error {
|
|
25834
|
-
code = "codemode_catalog_not_ready";
|
|
25835
|
-
constructor() {
|
|
25836
|
-
super("Codemode tool catalog is not ready for the active execution attempt");
|
|
25837
|
-
this.name = "CodemodeCatalogNotReadyError";
|
|
25838
|
-
}
|
|
25839
|
-
};
|
|
25840
|
-
var CodemodeCatalogStaleError = class extends Error {
|
|
25841
|
-
code = "codemode_catalog_stale";
|
|
25842
|
-
constructor() {
|
|
25843
|
-
super("Codemode tool catalog is stale for the active execution attempt");
|
|
25844
|
-
this.name = "CodemodeCatalogStaleError";
|
|
25845
|
-
}
|
|
25846
|
-
};
|
|
25847
|
-
function codemodeAuthorityForGrant(grant) {
|
|
25848
|
-
const metadata = grant.metadata;
|
|
25849
|
-
if (grant.principalKind !== "agent_attempt" || metadata?.delegated !== true || typeof metadata.sessionId !== "string" || typeof metadata.turnId !== "string" || typeof metadata.attemptId !== "string" || typeof metadata.executionGeneration !== "number" || !Number.isInteger(metadata.executionGeneration) || metadata.executionGeneration < 1) {
|
|
25850
|
-
return null;
|
|
25851
|
-
}
|
|
25852
|
-
return {
|
|
25853
|
-
accountId: grant.accountId,
|
|
25854
|
-
workspaceId: grant.workspaceId,
|
|
25855
|
-
sessionId: metadata.sessionId,
|
|
25856
|
-
turnId: metadata.turnId,
|
|
25857
|
-
attemptId: metadata.attemptId,
|
|
25858
|
-
executionGeneration: metadata.executionGeneration,
|
|
25859
|
-
subjectId: grant.subjectId
|
|
25860
|
-
};
|
|
25861
|
-
}
|
|
25862
|
-
function isCodemodeGrant(grant) {
|
|
25863
|
-
return hasPermission11(grant.permissions, "codemode:call") && codemodeAuthorityForGrant(grant) !== null;
|
|
25864
|
-
}
|
|
25865
|
-
function requireMatchingCodemodeCatalog(authority, catalog) {
|
|
25866
|
-
if (!catalog) throw new CodemodeCatalogNotReadyError();
|
|
25867
|
-
if (catalog.accountId !== authority.accountId || catalog.workspaceId !== authority.workspaceId || catalog.sessionId !== authority.sessionId || catalog.turnId !== authority.turnId || catalog.attemptId !== authority.attemptId || catalog.executionGeneration !== authority.executionGeneration) {
|
|
25868
|
-
throw new CodemodeAuthorityError("catalog_mismatch");
|
|
25869
|
-
}
|
|
25870
|
-
return catalog;
|
|
25871
|
-
}
|
|
25872
|
-
async function requireActiveCodemodeCatalog(deps, grant) {
|
|
25873
|
-
if (!isCodemodeGrant(grant)) throw new CodemodeAuthorityError("invalid_grant");
|
|
25874
|
-
const authority = codemodeAuthorityForGrant(grant);
|
|
25875
|
-
await requireSessionAuthorization3(deps, grant, {
|
|
25876
|
-
sessionId: authority.sessionId,
|
|
25877
|
-
operation: "session.codemode.call",
|
|
25878
|
-
surface: "codemode"
|
|
25879
|
-
});
|
|
25880
|
-
const active = await getActiveSessionTurnForExecution(
|
|
25881
|
-
deps.db,
|
|
25882
|
-
authority.workspaceId,
|
|
25883
|
-
authority.sessionId
|
|
25884
|
-
);
|
|
25885
|
-
if (!active || active.status !== "running" || active.id !== authority.turnId || active.activeAttemptId !== authority.attemptId || active.executionGeneration !== authority.executionGeneration) {
|
|
25886
|
-
throw new CodemodeAuthorityError("inactive_attempt");
|
|
25887
|
-
}
|
|
25888
|
-
const catalog = requireMatchingCodemodeCatalog(
|
|
25889
|
-
authority,
|
|
25890
|
-
await getAttemptToolCatalog(deps.db, {
|
|
25891
|
-
accountId: authority.accountId,
|
|
25892
|
-
workspaceId: authority.workspaceId,
|
|
25893
|
-
attemptId: authority.attemptId
|
|
25894
|
-
})
|
|
25895
|
-
);
|
|
25896
|
-
return { authority, catalog };
|
|
25897
|
-
}
|
|
25898
|
-
async function submitAndDispatchCodemodeCall(deps, grant, rawRequest) {
|
|
25899
|
-
const request = CodemodeCallRequest.parse(rawRequest);
|
|
25900
|
-
const { authority, catalog } = await requireActiveCodemodeCatalog(deps, grant);
|
|
25901
|
-
if (request.catalogDigest !== catalog.digest) throw new CodemodeCatalogStaleError();
|
|
25902
|
-
const submitted = await submitCodemodeOperation(deps.db, {
|
|
25903
|
-
...authority,
|
|
25904
|
-
call: {
|
|
25905
|
-
...request,
|
|
25906
|
-
caller: { kind: "codemode", subjectId: authority.subjectId }
|
|
25907
|
-
}
|
|
25908
|
-
});
|
|
25909
|
-
let operation = submitted.operation;
|
|
25910
|
-
let dispatch = terminal(operation) ? "terminal" : operation.state === "running" ? "already_running" : "unavailable";
|
|
25911
|
-
if (codemodeOperationNeedsDispatch(operation)) {
|
|
25912
|
-
try {
|
|
25913
|
-
const reply = await deps.bus.request(
|
|
25914
|
-
codemodeDispatchSubject(authority.workspaceId, authority.attemptId),
|
|
25915
|
-
encodeCodemodeDispatchRequest({
|
|
25916
|
-
version: 1,
|
|
25917
|
-
operationId: operation.operationId,
|
|
25918
|
-
catalogDigest: catalog.digest
|
|
25919
|
-
}),
|
|
25920
|
-
{ timeoutMs: CODEMODE_DISPATCH_TIMEOUT_MS }
|
|
25921
|
-
);
|
|
25922
|
-
dispatch = decodeCodemodeDispatchAck(reply.data).status;
|
|
25923
|
-
} catch {
|
|
25924
|
-
dispatch = "unavailable";
|
|
25925
|
-
}
|
|
25926
|
-
operation = await refreshAdmittedCodemodeOperation(
|
|
25927
|
-
operation,
|
|
25928
|
-
() => getCodemodeOperation(deps.db, {
|
|
25929
|
-
accountId: authority.accountId,
|
|
25930
|
-
workspaceId: authority.workspaceId,
|
|
25931
|
-
attemptId: authority.attemptId,
|
|
25932
|
-
operationId: operation.operationId
|
|
25933
|
-
})
|
|
25934
|
-
);
|
|
25935
|
-
if (terminal(operation)) dispatch = "terminal";
|
|
25936
|
-
else if (operation.state === "running" && dispatch === "unavailable") {
|
|
25937
|
-
dispatch = "already_running";
|
|
25938
|
-
}
|
|
25939
|
-
}
|
|
25940
|
-
return CodemodeCallSubmission.parse({ operation, dispatch });
|
|
25941
|
-
}
|
|
25942
|
-
function codemodeOperationNeedsDispatch(operation) {
|
|
25943
|
-
return operation.state === "queued" || operation.state === "running";
|
|
25944
|
-
}
|
|
25945
|
-
async function readCodemodeOperation(deps, grant, operationId) {
|
|
25946
|
-
if (!isCodemodeGrant(grant)) throw new CodemodeAuthorityError("invalid_grant");
|
|
25947
|
-
const authority = codemodeAuthorityForGrant(grant);
|
|
25948
|
-
return await getCodemodeOperation(deps.db, {
|
|
25949
|
-
accountId: authority.accountId,
|
|
25950
|
-
workspaceId: authority.workspaceId,
|
|
25951
|
-
attemptId: authority.attemptId,
|
|
25952
|
-
operationId
|
|
25953
|
-
});
|
|
25954
|
-
}
|
|
25955
|
-
function terminal(operation) {
|
|
25956
|
-
return ["completed", "failed", "outcome_unknown", "cancelled"].includes(operation.state);
|
|
25957
|
-
}
|
|
25958
|
-
async function refreshAdmittedCodemodeOperation(admitted, read) {
|
|
25959
|
-
try {
|
|
25960
|
-
return await read() ?? admitted;
|
|
25961
|
-
} catch {
|
|
25962
|
-
return admitted;
|
|
25963
|
-
}
|
|
25964
|
-
}
|
|
25965
|
-
|
|
25966
26413
|
// src/app.ts
|
|
25967
26414
|
import { boundedMcpRequest, McpPayloadTooLargeError } from "@opengeni/runtime/mcp-network";
|
|
25968
26415
|
import {
|
|
@@ -25971,7 +26418,7 @@ import {
|
|
|
25971
26418
|
} from "@opengeni/runtime/sandbox";
|
|
25972
26419
|
|
|
25973
26420
|
// src/http/auth.ts
|
|
25974
|
-
import { resolveFirstPartyDelegationSecret } from "@opengeni/config";
|
|
26421
|
+
import { resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret2 } from "@opengeni/config";
|
|
25975
26422
|
import { verifyDelegatedAccessToken } from "@opengeni/contracts";
|
|
25976
26423
|
|
|
25977
26424
|
// src/routes/install.ts
|
|
@@ -26255,7 +26702,7 @@ async function isAuthorized(c, settings) {
|
|
|
26255
26702
|
if (expected && constantTimeEqual(bearer, expected)) {
|
|
26256
26703
|
return true;
|
|
26257
26704
|
}
|
|
26258
|
-
const delegationSecret =
|
|
26705
|
+
const delegationSecret = resolveFirstPartyDelegationSecret2(settings);
|
|
26259
26706
|
return Boolean(
|
|
26260
26707
|
c.req.path.startsWith("/v1/") && bearer && delegationSecret && await verifyDelegatedAccessToken(delegationSecret, bearer)
|
|
26261
26708
|
);
|
|
@@ -26318,7 +26765,7 @@ import {
|
|
|
26318
26765
|
} from "@opengeni/core";
|
|
26319
26766
|
|
|
26320
26767
|
// src/http/common.ts
|
|
26321
|
-
import { getSession as
|
|
26768
|
+
import { getSession as getSession4 } from "@opengeni/db";
|
|
26322
26769
|
import { HTTPException as HTTPException22 } from "hono/http-exception";
|
|
26323
26770
|
function boundedLimit(raw) {
|
|
26324
26771
|
const limit = Number(raw ?? 100);
|
|
@@ -26328,7 +26775,7 @@ function boundedLimit(raw) {
|
|
|
26328
26775
|
return Math.min(500, Math.max(1, Math.floor(limit)));
|
|
26329
26776
|
}
|
|
26330
26777
|
async function assertSessionExists(db, workspaceId, sessionId) {
|
|
26331
|
-
if (!await
|
|
26778
|
+
if (!await getSession4(db, workspaceId, sessionId)) {
|
|
26332
26779
|
throw new HTTPException22(404, { message: "session not found" });
|
|
26333
26780
|
}
|
|
26334
26781
|
}
|
|
@@ -29152,7 +29599,7 @@ function registerSuperGrokRoutes(app, deps) {
|
|
|
29152
29599
|
}
|
|
29153
29600
|
|
|
29154
29601
|
// src/routes/connections.ts
|
|
29155
|
-
import { createHash as
|
|
29602
|
+
import { createHash as createHash12, createHmac as createHmac4 } from "crypto";
|
|
29156
29603
|
import {
|
|
29157
29604
|
WORKSPACE_OPENROUTER_CONNECTION_DOMAIN,
|
|
29158
29605
|
WORKSPACE_OPENROUTER_CONNECTION_ROLE,
|
|
@@ -29250,7 +29697,7 @@ import { HTTPException as HTTPException29 } from "hono/http-exception";
|
|
|
29250
29697
|
|
|
29251
29698
|
// src/integrations/provider-oauth.ts
|
|
29252
29699
|
import { Buffer as Buffer4 } from "buffer";
|
|
29253
|
-
import { createHash as
|
|
29700
|
+
import { createHash as createHash10, randomBytes as randomBytes6 } from "crypto";
|
|
29254
29701
|
import {
|
|
29255
29702
|
integrationDefinitionProviderDomain as integrationDefinitionProviderDomain2,
|
|
29256
29703
|
integrationDefinitionById
|
|
@@ -29367,7 +29814,7 @@ async function startApiIntegrationProviderOAuth(deps, input) {
|
|
|
29367
29814
|
authorizationUrl.searchParams.set("code_challenge_method", "S256");
|
|
29368
29815
|
authorizationUrl.searchParams.set(
|
|
29369
29816
|
"code_challenge",
|
|
29370
|
-
|
|
29817
|
+
createHash10("sha256").update(verifier).digest("base64url")
|
|
29371
29818
|
);
|
|
29372
29819
|
authorizationUrl.searchParams.set("prompt", "select_account");
|
|
29373
29820
|
if (definition.provider.id === "google") {
|
|
@@ -29597,7 +30044,7 @@ function providerClientKeys(definition) {
|
|
|
29597
30044
|
]);
|
|
29598
30045
|
}
|
|
29599
30046
|
function providerDefinitionFingerprint(definition) {
|
|
29600
|
-
return
|
|
30047
|
+
return createHash10("sha256").update(
|
|
29601
30048
|
JSON.stringify({
|
|
29602
30049
|
id: definition.id,
|
|
29603
30050
|
provider: definition.provider,
|
|
@@ -29840,7 +30287,7 @@ function numberValue4(value) {
|
|
|
29840
30287
|
}
|
|
29841
30288
|
|
|
29842
30289
|
// src/integrations/personal-github.ts
|
|
29843
|
-
import { createHash as
|
|
30290
|
+
import { createHash as createHash11, randomBytes as randomBytes7, randomUUID as randomUUID3 } from "crypto";
|
|
29844
30291
|
import { personalGitHubOAuthCallbackUrl } from "@opengeni/config";
|
|
29845
30292
|
import {
|
|
29846
30293
|
PERSONAL_GITHUB_AUTHORIZATION_URL,
|
|
@@ -29886,7 +30333,7 @@ var PersonalGitHubCallbackError = class extends Error {
|
|
|
29886
30333
|
}
|
|
29887
30334
|
};
|
|
29888
30335
|
function personalGitHubClientMarker(clientId) {
|
|
29889
|
-
return
|
|
30336
|
+
return createHash11("sha256").update(clientId).digest("hex").slice(0, 32);
|
|
29890
30337
|
}
|
|
29891
30338
|
function requirePersonalGitHubOAuthSettings(settings) {
|
|
29892
30339
|
if (!settings.githubPersonalOauthEnabled) {
|
|
@@ -29989,7 +30436,7 @@ async function startPersonalGitHubOAuth(deps, input) {
|
|
|
29989
30436
|
authorizationUrl.searchParams.set("state", state);
|
|
29990
30437
|
authorizationUrl.searchParams.set(
|
|
29991
30438
|
"code_challenge",
|
|
29992
|
-
|
|
30439
|
+
createHash11("sha256").update(verifier).digest("base64url")
|
|
29993
30440
|
);
|
|
29994
30441
|
authorizationUrl.searchParams.set("code_challenge_method", "S256");
|
|
29995
30442
|
return PersonalGitHubOAuthStartResponse.parse({
|
|
@@ -30582,7 +31029,7 @@ function registerConnectionRoutes(app, deps) {
|
|
|
30582
31029
|
accountId: state.accountId,
|
|
30583
31030
|
workspaceId: state.workspaceId,
|
|
30584
31031
|
subjectId: state.subjectId,
|
|
30585
|
-
callbackDigest:
|
|
31032
|
+
callbackDigest: createHash12("sha256").update(state.nonce).digest("hex"),
|
|
30586
31033
|
installMode: state.connectionId ? "reinstall" : "connect",
|
|
30587
31034
|
...failure
|
|
30588
31035
|
});
|
|
@@ -33308,7 +33755,7 @@ import { buildFleetContextForSession as buildFleetContextForSession2, swapActive
|
|
|
33308
33755
|
|
|
33309
33756
|
// src/sandbox/machines.ts
|
|
33310
33757
|
import {
|
|
33311
|
-
getSession as
|
|
33758
|
+
getSession as getSession5,
|
|
33312
33759
|
getLiveEnrollmentConnection as getLiveEnrollmentConnection3,
|
|
33313
33760
|
listEnrollments as listEnrollments3,
|
|
33314
33761
|
listSandboxes,
|
|
@@ -33441,7 +33888,7 @@ async function listMachines(services, input) {
|
|
|
33441
33888
|
let activeEpoch = 0;
|
|
33442
33889
|
let session = null;
|
|
33443
33890
|
if (input.sessionId) {
|
|
33444
|
-
session = await
|
|
33891
|
+
session = await getSession5(db, workspaceId, input.sessionId);
|
|
33445
33892
|
if (session) {
|
|
33446
33893
|
const pointer = await readActiveSandbox2(db, workspaceId, input.sessionId);
|
|
33447
33894
|
activeSandboxId = pointer?.activeSandboxId ?? null;
|
|
@@ -35302,7 +35749,7 @@ function browserIdentityRouteError(error) {
|
|
|
35302
35749
|
import { randomUUID as randomUUID5 } from "crypto";
|
|
35303
35750
|
import {
|
|
35304
35751
|
environmentsEncryptionKeyBytes as environmentsEncryptionKeyBytes4,
|
|
35305
|
-
resolveFirstPartyDelegationSecret as
|
|
35752
|
+
resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret3,
|
|
35306
35753
|
resolveStreamTokenSecret as resolveStreamTokenSecret2
|
|
35307
35754
|
} from "@opengeni/config";
|
|
35308
35755
|
import {
|
|
@@ -35396,7 +35843,7 @@ import {
|
|
|
35396
35843
|
getExternalAuthPreparation,
|
|
35397
35844
|
getProtectedAuthFillPreparation,
|
|
35398
35845
|
getSiteAuthConnection,
|
|
35399
|
-
getSession as
|
|
35846
|
+
getSession as getSession6,
|
|
35400
35847
|
InteractionResourceConflictError,
|
|
35401
35848
|
InteractionResourceNotFoundError,
|
|
35402
35849
|
InteractionResourceStateError,
|
|
@@ -35695,7 +36142,7 @@ async function withInteractionHolderHeartbeat(deps, input, run) {
|
|
|
35695
36142
|
}
|
|
35696
36143
|
|
|
35697
36144
|
// src/browser-state-authority.ts
|
|
35698
|
-
import { createCipheriv, createDecipheriv, createHash as
|
|
36145
|
+
import { createCipheriv, createDecipheriv, createHash as createHash13, createHmac as createHmac6, randomBytes as randomBytes9 } from "crypto";
|
|
35699
36146
|
import { BROWSER_PROFILE_ARTIFACT_FORMAT } from "@opengeni/contracts";
|
|
35700
36147
|
var ROOT_KEY_BYTES = 32;
|
|
35701
36148
|
var DATA_KEY_BYTES = 32;
|
|
@@ -35759,7 +36206,7 @@ function browserStateObjectKey(workspaceIdInput, operationIdInput) {
|
|
|
35759
36206
|
return `workspaces/${workspaceId}/browser-state/revisions/${operationId}/chromium-profile.ogbs`;
|
|
35760
36207
|
}
|
|
35761
36208
|
function browserStateManifestDigest(manifest) {
|
|
35762
|
-
return
|
|
36209
|
+
return createHash13("sha256").update(canonicalJson2(manifest), "utf8").digest("hex");
|
|
35763
36210
|
}
|
|
35764
36211
|
function operationScope(input) {
|
|
35765
36212
|
const base = baseArtifactScope(input);
|
|
@@ -35772,8 +36219,8 @@ function operationScope(input) {
|
|
|
35772
36219
|
function artifactScope(input) {
|
|
35773
36220
|
return {
|
|
35774
36221
|
...baseArtifactScope(input),
|
|
35775
|
-
artifactDigest:
|
|
35776
|
-
contentDigest:
|
|
36222
|
+
artifactDigest: sha256(input.artifactDigest, "browser state artifact digest"),
|
|
36223
|
+
contentDigest: sha256(input.contentDigest, "browser state content digest")
|
|
35777
36224
|
};
|
|
35778
36225
|
}
|
|
35779
36226
|
function baseArtifactScope(input) {
|
|
@@ -35817,7 +36264,7 @@ function uuid(value, label) {
|
|
|
35817
36264
|
}
|
|
35818
36265
|
return value.toLowerCase();
|
|
35819
36266
|
}
|
|
35820
|
-
function
|
|
36267
|
+
function sha256(value, label) {
|
|
35821
36268
|
if (typeof value !== "string" || !SHA256_PATTERN.test(value)) {
|
|
35822
36269
|
throw new Error(`${label} is invalid`);
|
|
35823
36270
|
}
|
|
@@ -39798,7 +40245,7 @@ function interactionHolderId(browserSessionId) {
|
|
|
39798
40245
|
return `browser-session:${browserSessionId}`;
|
|
39799
40246
|
}
|
|
39800
40247
|
function browserAuthorityRoot(deps) {
|
|
39801
|
-
const root2 =
|
|
40248
|
+
const root2 = resolveFirstPartyDelegationSecret3(deps.settings);
|
|
39802
40249
|
if (!root2) {
|
|
39803
40250
|
throw new HTTPException39(503, {
|
|
39804
40251
|
message: "browser controller authority is not configured"
|
|
@@ -39875,7 +40322,7 @@ async function settleBrowserRevisionCaptureFailure(deps, input, error) {
|
|
|
39875
40322
|
});
|
|
39876
40323
|
}
|
|
39877
40324
|
async function requireSourceSession(deps, workspaceId, sessionId) {
|
|
39878
|
-
const session = await
|
|
40325
|
+
const session = await getSession6(deps.db, workspaceId, sessionId);
|
|
39879
40326
|
if (!session) throw new BrowserSessionNotFoundError("Associated session not found");
|
|
39880
40327
|
return session;
|
|
39881
40328
|
}
|
|
@@ -40400,7 +40847,7 @@ function isAbort(error) {
|
|
|
40400
40847
|
|
|
40401
40848
|
// src/routes/computer-sessions.ts
|
|
40402
40849
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
40403
|
-
import { resolveFirstPartyDelegationSecret as
|
|
40850
|
+
import { resolveFirstPartyDelegationSecret as resolveFirstPartyDelegationSecret4, resolveStreamTokenSecret as resolveStreamTokenSecret3 } from "@opengeni/config";
|
|
40404
40851
|
import {
|
|
40405
40852
|
BROWSER_CONTROL_WEBSOCKET_BEARER_PREFIX as BROWSER_CONTROL_WEBSOCKET_BEARER_PREFIX2,
|
|
40406
40853
|
BROWSER_CONTROL_PORT as BROWSER_CONTROL_PORT2,
|
|
@@ -40434,7 +40881,7 @@ import {
|
|
|
40434
40881
|
getComputerSessionControlRecord as getComputerSessionControlRecord2,
|
|
40435
40882
|
getLiveEnrollmentConnection as getLiveEnrollmentConnection6,
|
|
40436
40883
|
getSandbox as getSandbox3,
|
|
40437
|
-
getSession as
|
|
40884
|
+
getSession as getSession7,
|
|
40438
40885
|
listComputerSessions,
|
|
40439
40886
|
prepareComputerSessionCreate,
|
|
40440
40887
|
prepareComputerSessionEnd,
|
|
@@ -41777,7 +42224,7 @@ function interactionHolderId2(computerSessionId) {
|
|
|
41777
42224
|
return `computer-session:${computerSessionId}`;
|
|
41778
42225
|
}
|
|
41779
42226
|
function controllerAuthorityRoot(deps) {
|
|
41780
|
-
const root2 =
|
|
42227
|
+
const root2 = resolveFirstPartyDelegationSecret4(deps.settings);
|
|
41781
42228
|
if (!root2) {
|
|
41782
42229
|
throw new HTTPException40(503, {
|
|
41783
42230
|
message: "interaction controller authority is not configured"
|
|
@@ -41786,7 +42233,7 @@ function controllerAuthorityRoot(deps) {
|
|
|
41786
42233
|
return root2;
|
|
41787
42234
|
}
|
|
41788
42235
|
async function requireSourceSession2(deps, workspaceId, sessionId) {
|
|
41789
|
-
const session = await
|
|
42236
|
+
const session = await getSession7(deps.db, workspaceId, sessionId);
|
|
41790
42237
|
if (!session) throw new ComputerSessionNotFoundError("Associated session not found");
|
|
41791
42238
|
return session;
|
|
41792
42239
|
}
|
|
@@ -41966,8 +42413,12 @@ function isAbort2(error) {
|
|
|
41966
42413
|
|
|
41967
42414
|
// src/routes/github.ts
|
|
41968
42415
|
import {
|
|
41969
|
-
|
|
42416
|
+
GitHubActionPoliciesResponse,
|
|
42417
|
+
GitHubActionPolicyActorState,
|
|
42418
|
+
GitHubAppManifestCreate,
|
|
42419
|
+
UpdateGitHubActionPolicyRequest
|
|
41970
42420
|
} from "@opengeni/contracts";
|
|
42421
|
+
import { PersonalGitHubConnectionMetadata as PersonalGitHubConnectionMetadata2 } from "@opengeni/contracts/personal-github";
|
|
41971
42422
|
import {
|
|
41972
42423
|
ListGitHubRepositoryBranchesQuery,
|
|
41973
42424
|
VerifyPublicGitHubRepositoryRefRequest
|
|
@@ -41976,7 +42427,8 @@ import { parseCanonicalGitHubRepositoryUrl } from "@opengeni/contracts/github-re
|
|
|
41976
42427
|
import {
|
|
41977
42428
|
bindAuthorizedGitHubInstallationRepositories,
|
|
41978
42429
|
deleteGitHubInstallationBinding,
|
|
41979
|
-
GitHubInstallationAuthorityCommitError
|
|
42430
|
+
GitHubInstallationAuthorityCommitError,
|
|
42431
|
+
listGitHubInstallationAccessForWorkspace as listGitHubInstallationAccessForWorkspace2
|
|
41980
42432
|
} from "@opengeni/db";
|
|
41981
42433
|
import {
|
|
41982
42434
|
authorizeGitHubInstallationBinding,
|
|
@@ -42000,7 +42452,15 @@ import {
|
|
|
42000
42452
|
} from "@opengeni/github";
|
|
42001
42453
|
import { deleteCookie, setCookie } from "hono/cookie";
|
|
42002
42454
|
import { HTTPException as HTTPException41 } from "hono/http-exception";
|
|
42003
|
-
import {
|
|
42455
|
+
import {
|
|
42456
|
+
githubAppActionPolicyActor,
|
|
42457
|
+
hasPermission as hasPermission16,
|
|
42458
|
+
listGitHubActionPolicyActors,
|
|
42459
|
+
personalGitHubActionPolicyActor,
|
|
42460
|
+
requireAccessGrant as requireAccessGrant16,
|
|
42461
|
+
requireAccessGrantAuthorization as requireAccessGrantAuthorization8,
|
|
42462
|
+
updateGitHubActionPolicyGroup
|
|
42463
|
+
} from "@opengeni/core";
|
|
42004
42464
|
var githubStateCookie = "opengeni_github_state";
|
|
42005
42465
|
var githubBindingStateMaxAgeSeconds = 10 * 60;
|
|
42006
42466
|
var legacyInstallationChooserDisabledMessage = "The legacy repository-admin GitHub installation chooser is disabled; use the GitHub owner-consent connect flow";
|
|
@@ -42041,6 +42501,89 @@ function registerGitHubRoutes(app, deps) {
|
|
|
42041
42501
|
missing: setupMode === "operator" ? missing : []
|
|
42042
42502
|
});
|
|
42043
42503
|
});
|
|
42504
|
+
app.get("/v1/workspaces/:workspaceId/github/action-policies", async (c) => {
|
|
42505
|
+
const workspaceId = c.req.param("workspaceId");
|
|
42506
|
+
const grant = await requireAccessGrant16(c, deps, workspaceId, "github:use");
|
|
42507
|
+
const [installations, personalConnections] = await Promise.all([
|
|
42508
|
+
listGitHubInstallationAccessForWorkspace2(db, grant.workspaceId),
|
|
42509
|
+
listPersonalGitHubConnections(deps, {
|
|
42510
|
+
workspaceId: grant.workspaceId,
|
|
42511
|
+
subjectId: grant.subjectId
|
|
42512
|
+
})
|
|
42513
|
+
]);
|
|
42514
|
+
const personalConnection = canonicalPersonalGitHubPolicyConnection(personalConnections);
|
|
42515
|
+
const actors = [
|
|
42516
|
+
...installations.map(githubAppActionPolicyActor),
|
|
42517
|
+
...personalConnection ? [
|
|
42518
|
+
personalGitHubActionPolicyActor({
|
|
42519
|
+
connectionId: personalConnection.id,
|
|
42520
|
+
githubLogin: PersonalGitHubConnectionMetadata2.parse(personalConnection.metadata).githubLogin
|
|
42521
|
+
})
|
|
42522
|
+
] : []
|
|
42523
|
+
];
|
|
42524
|
+
return c.json(
|
|
42525
|
+
GitHubActionPoliciesResponse.parse({
|
|
42526
|
+
enabled: settings.githubRestMcpEnabled,
|
|
42527
|
+
actors: await listGitHubActionPolicyActors(db, {
|
|
42528
|
+
accountId: grant.accountId,
|
|
42529
|
+
workspaceId: grant.workspaceId,
|
|
42530
|
+
actors
|
|
42531
|
+
})
|
|
42532
|
+
})
|
|
42533
|
+
);
|
|
42534
|
+
});
|
|
42535
|
+
app.patch("/v1/workspaces/:workspaceId/github/action-policies", async (c) => {
|
|
42536
|
+
const workspaceId = c.req.param("workspaceId");
|
|
42537
|
+
const payload = UpdateGitHubActionPolicyRequest.parse(await c.req.json());
|
|
42538
|
+
const actor = payload.actor;
|
|
42539
|
+
if (actor.kind === "workspace_app") {
|
|
42540
|
+
const grant = await requireAccessGrant16(c, deps, workspaceId, "github:manage");
|
|
42541
|
+
const installations = await listGitHubInstallationAccessForWorkspace2(db, grant.workspaceId);
|
|
42542
|
+
const installation = installations.find(
|
|
42543
|
+
(candidate) => candidate.installationId === actor.installationId
|
|
42544
|
+
);
|
|
42545
|
+
if (!installation || installation.accountId !== grant.accountId) {
|
|
42546
|
+
throw new HTTPException41(404, { message: "GitHub installation not found" });
|
|
42547
|
+
}
|
|
42548
|
+
return c.json(
|
|
42549
|
+
GitHubActionPolicyActorState.parse(
|
|
42550
|
+
await updateGitHubActionPolicyGroup(db, {
|
|
42551
|
+
accountId: grant.accountId,
|
|
42552
|
+
workspaceId: grant.workspaceId,
|
|
42553
|
+
subjectId: grant.subjectId,
|
|
42554
|
+
actor: githubAppActionPolicyActor(installation),
|
|
42555
|
+
group: payload.group,
|
|
42556
|
+
decision: payload.decision
|
|
42557
|
+
})
|
|
42558
|
+
)
|
|
42559
|
+
);
|
|
42560
|
+
}
|
|
42561
|
+
const access = await requireAccessGrantAuthorization8(c, deps, workspaceId, "connections:write");
|
|
42562
|
+
assertPersonalConnectionOwnerPrincipal(access, "My GitHub action policy");
|
|
42563
|
+
const connections = await listPersonalGitHubConnections(deps, {
|
|
42564
|
+
workspaceId: access.grant.workspaceId,
|
|
42565
|
+
subjectId: access.grant.subjectId
|
|
42566
|
+
});
|
|
42567
|
+
const connection = connections.find((candidate) => candidate.id === actor.connectionId);
|
|
42568
|
+
if (!connection) {
|
|
42569
|
+
throw new HTTPException41(404, { message: "personal GitHub connection not found" });
|
|
42570
|
+
}
|
|
42571
|
+
return c.json(
|
|
42572
|
+
GitHubActionPolicyActorState.parse(
|
|
42573
|
+
await updateGitHubActionPolicyGroup(db, {
|
|
42574
|
+
accountId: access.grant.accountId,
|
|
42575
|
+
workspaceId: access.grant.workspaceId,
|
|
42576
|
+
subjectId: access.grant.subjectId,
|
|
42577
|
+
actor: personalGitHubActionPolicyActor({
|
|
42578
|
+
connectionId: connection.id,
|
|
42579
|
+
githubLogin: PersonalGitHubConnectionMetadata2.parse(connection.metadata).githubLogin
|
|
42580
|
+
}),
|
|
42581
|
+
group: payload.group,
|
|
42582
|
+
decision: payload.decision
|
|
42583
|
+
})
|
|
42584
|
+
)
|
|
42585
|
+
);
|
|
42586
|
+
});
|
|
42044
42587
|
app.get("/v1/workspaces/:workspaceId/github/connect", async (c) => {
|
|
42045
42588
|
const workspaceId = c.req.param("workspaceId");
|
|
42046
42589
|
const state = c.req.query("state");
|
|
@@ -42540,6 +43083,20 @@ function githubInstallationSettingsUrl(installation) {
|
|
|
42540
43083
|
}
|
|
42541
43084
|
return new URL(`https://github.com/settings/installations/${installation.installationId}`);
|
|
42542
43085
|
}
|
|
43086
|
+
function canonicalPersonalGitHubPolicyConnection(connections) {
|
|
43087
|
+
const statusRank = {
|
|
43088
|
+
active: 0,
|
|
43089
|
+
needs_reauth: 1,
|
|
43090
|
+
error: 2,
|
|
43091
|
+
revoked: 3
|
|
43092
|
+
};
|
|
43093
|
+
return [...connections].sort((left, right) => {
|
|
43094
|
+
const rank = (statusRank[left.status] ?? 4) - (statusRank[right.status] ?? 4);
|
|
43095
|
+
if (rank !== 0) return rank;
|
|
43096
|
+
const updated = right.updatedAt.localeCompare(left.updatedAt);
|
|
43097
|
+
return updated !== 0 ? updated : right.id.localeCompare(left.id);
|
|
43098
|
+
})[0] ?? null;
|
|
43099
|
+
}
|
|
42543
43100
|
function redirectToExactGitHubAuthorization(c, deps, sourceState, installationId) {
|
|
42544
43101
|
const payload = readSignedState11(sourceState, deps.githubStateSecret);
|
|
42545
43102
|
const clientId = deps.settings.githubClientId?.trim();
|
|
@@ -42764,7 +43321,7 @@ import {
|
|
|
42764
43321
|
} from "@opengeni/contracts/github-repository-contracts";
|
|
42765
43322
|
import {
|
|
42766
43323
|
requireAccessGrant as requireAccessGrant17,
|
|
42767
|
-
requireAccessGrantAuthorization as
|
|
43324
|
+
requireAccessGrantAuthorization as requireAccessGrantAuthorization9
|
|
42768
43325
|
} from "@opengeni/core";
|
|
42769
43326
|
import {
|
|
42770
43327
|
getPersonalGitHubRepositorySelectionState as getPersonalGitHubRepositorySelectionState2,
|
|
@@ -42887,12 +43444,12 @@ async function listLivePersonalGitHubRepositoryBranches(deps, input, services =
|
|
|
42887
43444
|
null,
|
|
42888
43445
|
services.getSelectionState
|
|
42889
43446
|
);
|
|
42890
|
-
const [
|
|
42891
|
-
if (!
|
|
43447
|
+
const [owner2, repositoryName] = current.repository.fullName.split("/");
|
|
43448
|
+
if (!owner2 || !repositoryName) {
|
|
42892
43449
|
throw new PersonalGitHubRepositoryProviderError("invalid_provider_response");
|
|
42893
43450
|
}
|
|
42894
43451
|
const url = new URL(
|
|
42895
|
-
`/repos/${encodeURIComponent(
|
|
43452
|
+
`/repos/${encodeURIComponent(owner2)}/${encodeURIComponent(repositoryName)}/branches`,
|
|
42896
43453
|
PERSONAL_GITHUB_API_ORIGIN
|
|
42897
43454
|
);
|
|
42898
43455
|
url.searchParams.set("page", String(input.query.cursor));
|
|
@@ -42951,12 +43508,12 @@ async function verifyLivePersonalGitHubRepositories(deps, input) {
|
|
|
42951
43508
|
const connection = await requirePersonalGitHubRepositoryConnection(deps, input);
|
|
42952
43509
|
const verified = [];
|
|
42953
43510
|
for (const selection of input.repositories) {
|
|
42954
|
-
const [
|
|
42955
|
-
if (!
|
|
43511
|
+
const [owner2, repositoryName] = selection.fullName.split("/");
|
|
43512
|
+
if (!owner2 || !repositoryName) {
|
|
42956
43513
|
throw new PersonalGitHubRepositoryProviderError("invalid_provider_response");
|
|
42957
43514
|
}
|
|
42958
43515
|
const url = new URL(
|
|
42959
|
-
`/repos/${encodeURIComponent(
|
|
43516
|
+
`/repos/${encodeURIComponent(owner2)}/${encodeURIComponent(repositoryName)}`,
|
|
42960
43517
|
PERSONAL_GITHUB_API_ORIGIN
|
|
42961
43518
|
);
|
|
42962
43519
|
const payload = await personalGitHubProviderJson(
|
|
@@ -43265,7 +43822,7 @@ function registerPersonalGitHubRoutes(app, deps) {
|
|
|
43265
43822
|
});
|
|
43266
43823
|
app.post("/v1/workspaces/:workspaceId/connections/github/oauth/start", async (c) => {
|
|
43267
43824
|
const workspaceId = c.req.param("workspaceId");
|
|
43268
|
-
const access = await
|
|
43825
|
+
const access = await requireAccessGrantAuthorization9(c, deps, workspaceId, "connections:write");
|
|
43269
43826
|
assertPersonalConnectionOwnerPrincipal(access, "My GitHub account");
|
|
43270
43827
|
const payload = PersonalGitHubOAuthStartRequest.parse(await c.req.json().catch(() => ({})));
|
|
43271
43828
|
return c.json(
|
|
@@ -43279,7 +43836,7 @@ function registerPersonalGitHubRoutes(app, deps) {
|
|
|
43279
43836
|
});
|
|
43280
43837
|
app.post("/v1/workspaces/:workspaceId/connections/:connectionId/github/reconnect", async (c) => {
|
|
43281
43838
|
const workspaceId = c.req.param("workspaceId");
|
|
43282
|
-
const access = await
|
|
43839
|
+
const access = await requireAccessGrantAuthorization9(c, deps, workspaceId, "connections:write");
|
|
43283
43840
|
assertPersonalConnectionOwnerPrincipal(access, "My GitHub account");
|
|
43284
43841
|
const payload = PersonalGitHubOAuthStartRequest.omit({ connectionId: true }).parse(
|
|
43285
43842
|
await c.req.json().catch(() => ({}))
|
|
@@ -43298,7 +43855,7 @@ function registerPersonalGitHubRoutes(app, deps) {
|
|
|
43298
43855
|
async (c) => {
|
|
43299
43856
|
const workspaceId = c.req.param("workspaceId");
|
|
43300
43857
|
const connectionId = c.req.param("connectionId");
|
|
43301
|
-
const access = await
|
|
43858
|
+
const access = await requireAccessGrantAuthorization9(
|
|
43302
43859
|
c,
|
|
43303
43860
|
deps,
|
|
43304
43861
|
workspaceId,
|
|
@@ -43349,7 +43906,7 @@ function registerPersonalGitHubRoutes(app, deps) {
|
|
|
43349
43906
|
async (c) => {
|
|
43350
43907
|
const workspaceId = c.req.param("workspaceId");
|
|
43351
43908
|
const connectionId = c.req.param("connectionId");
|
|
43352
|
-
const access = await
|
|
43909
|
+
const access = await requireAccessGrantAuthorization9(
|
|
43353
43910
|
c,
|
|
43354
43911
|
deps,
|
|
43355
43912
|
workspaceId,
|
|
@@ -43396,7 +43953,7 @@ function registerPersonalGitHubRoutes(app, deps) {
|
|
|
43396
43953
|
async (c) => {
|
|
43397
43954
|
const workspaceId = c.req.param("workspaceId");
|
|
43398
43955
|
const connectionId = c.req.param("connectionId");
|
|
43399
|
-
const access = await
|
|
43956
|
+
const access = await requireAccessGrantAuthorization9(
|
|
43400
43957
|
c,
|
|
43401
43958
|
deps,
|
|
43402
43959
|
workspaceId,
|
|
@@ -43428,7 +43985,7 @@ function registerPersonalGitHubRoutes(app, deps) {
|
|
|
43428
43985
|
async (c) => {
|
|
43429
43986
|
const workspaceId = c.req.param("workspaceId");
|
|
43430
43987
|
const connectionId = c.req.param("connectionId");
|
|
43431
|
-
const access = await
|
|
43988
|
+
const access = await requireAccessGrantAuthorization9(
|
|
43432
43989
|
c,
|
|
43433
43990
|
deps,
|
|
43434
43991
|
workspaceId,
|
|
@@ -43540,7 +44097,7 @@ function canonicalPersonalGitHubConnection(connections) {
|
|
|
43540
44097
|
import {
|
|
43541
44098
|
PERSONAL_GITHUB_API_ORIGIN as PERSONAL_GITHUB_API_ORIGIN2,
|
|
43542
44099
|
PERSONAL_GITHUB_PROVIDER_DOMAIN as PERSONAL_GITHUB_PROVIDER_DOMAIN4,
|
|
43543
|
-
PersonalGitHubConnectionMetadata as
|
|
44100
|
+
PersonalGitHubConnectionMetadata as PersonalGitHubConnectionMetadata3,
|
|
43544
44101
|
isPersonalGitHubConnection as isPersonalGitHubConnection4
|
|
43545
44102
|
} from "@opengeni/contracts/personal-github";
|
|
43546
44103
|
import {
|
|
@@ -43837,7 +44394,7 @@ async function assertCurrentConnectionAndSelection(deps, claims, repository) {
|
|
|
43837
44394
|
if (!connection || connection.accountId !== claims.accountId || connection.workspaceId !== claims.originWorkspaceId || connection.subjectId !== claims.ownerSubjectId || connection.status !== "active" || connection.grantedScopes.length !== 1 || connection.grantedScopes[0] !== "repo" || !isPersonalGitHubConnection4(connection)) {
|
|
43838
44395
|
throw new Error("personal GitHub connection changed");
|
|
43839
44396
|
}
|
|
43840
|
-
const metadata =
|
|
44397
|
+
const metadata = PersonalGitHubConnectionMetadata3.parse(connection.metadata);
|
|
43841
44398
|
if (metadata.credentialBindingId !== claims.credentialBindingId) {
|
|
43842
44399
|
throw new Error("personal GitHub credential binding changed");
|
|
43843
44400
|
}
|
|
@@ -44092,7 +44649,7 @@ import {
|
|
|
44092
44649
|
import { HTTPException as HTTPException44 } from "hono/http-exception";
|
|
44093
44650
|
|
|
44094
44651
|
// src/integrations/api-integrations.ts
|
|
44095
|
-
import { createHash as
|
|
44652
|
+
import { createHash as createHash14 } from "crypto";
|
|
44096
44653
|
import {
|
|
44097
44654
|
compileGraphqlRevision,
|
|
44098
44655
|
compileOpenApiRevision,
|
|
@@ -44302,7 +44859,7 @@ function resolvedPreview(input) {
|
|
|
44302
44859
|
};
|
|
44303
44860
|
}
|
|
44304
44861
|
function integrationInstallationIdentity(protocol, label, locator) {
|
|
44305
|
-
const hash =
|
|
44862
|
+
const hash = createHash14("sha256").update(`${protocol}\0${locator}`).digest("hex").slice(0, 12);
|
|
44306
44863
|
const slug = label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "integration";
|
|
44307
44864
|
return {
|
|
44308
44865
|
sourceDerivedDefinitionId: `${slug}-${hash}`,
|
|
@@ -44668,7 +45225,7 @@ import {
|
|
|
44668
45225
|
import {
|
|
44669
45226
|
hasPermission as hasPermission17,
|
|
44670
45227
|
requireAccessGrant as requireAccessGrant19,
|
|
44671
|
-
requireAccessGrantAuthorization as
|
|
45228
|
+
requireAccessGrantAuthorization as requireAccessGrantAuthorization10
|
|
44672
45229
|
} from "@opengeni/core";
|
|
44673
45230
|
import {
|
|
44674
45231
|
configureIntegrationFacet as configureIntegrationFacet2,
|
|
@@ -44713,7 +45270,7 @@ function registerIntegrationFacetRoutes(app, deps) {
|
|
|
44713
45270
|
"/v1/workspaces/:workspaceId/integrations/:capabilityId/instances/:instanceKey/facets/:facetKey/source",
|
|
44714
45271
|
async (c) => {
|
|
44715
45272
|
const workspaceId = c.req.param("workspaceId");
|
|
44716
|
-
const authorization = await
|
|
45273
|
+
const authorization = await requireAccessGrantAuthorization10(
|
|
44717
45274
|
c,
|
|
44718
45275
|
deps,
|
|
44719
45276
|
workspaceId,
|
|
@@ -44966,7 +45523,7 @@ import {
|
|
|
44966
45523
|
requireFreshAccessGrant,
|
|
44967
45524
|
requirePermission as requirePermission3,
|
|
44968
45525
|
requireSessionAuthorization as requireSessionAuthorization6,
|
|
44969
|
-
workflowIdForSession
|
|
45526
|
+
workflowIdForSession as workflowIdForSession2
|
|
44970
45527
|
} from "@opengeni/core";
|
|
44971
45528
|
import { publishDurableSessionEvents as publishDurableSessionEvents2 } from "@opengeni/events";
|
|
44972
45529
|
import { HTTPException as HTTPException46 } from "hono/http-exception";
|
|
@@ -46334,7 +46891,7 @@ function registerInteractionResourceRoutes(app, deps) {
|
|
|
46334
46891
|
workspaceId,
|
|
46335
46892
|
sessionId: approvalTarget.sessionId,
|
|
46336
46893
|
eventId: accepted.event.id,
|
|
46337
|
-
workflowId:
|
|
46894
|
+
workflowId: workflowIdForSession2(approvalTarget.sessionId),
|
|
46338
46895
|
workflowWakeRevision: accepted.workflowWakeRevision
|
|
46339
46896
|
});
|
|
46340
46897
|
const response = InteractionInterventionMutationResponse.parse({
|
|
@@ -48458,7 +49015,7 @@ function positiveInteger3(value) {
|
|
|
48458
49015
|
}
|
|
48459
49016
|
|
|
48460
49017
|
// src/routes/packs.ts
|
|
48461
|
-
import { createHash as
|
|
49018
|
+
import { createHash as createHash15 } from "crypto";
|
|
48462
49019
|
import {
|
|
48463
49020
|
EnablePackRequest,
|
|
48464
49021
|
InstallPackRequest,
|
|
@@ -48644,7 +49201,7 @@ function registerPackRoutes(app, deps) {
|
|
|
48644
49201
|
packVersion: pack.version,
|
|
48645
49202
|
...preview.variableSetId ? { variableSetId: preview.variableSetId } : {}
|
|
48646
49203
|
};
|
|
48647
|
-
const requestDigest =
|
|
49204
|
+
const requestDigest = sha2562(
|
|
48648
49205
|
stableJson4({
|
|
48649
49206
|
packId: pack.id,
|
|
48650
49207
|
manifestDigest: preview.manifestDigest,
|
|
@@ -48823,7 +49380,7 @@ function registerPackRoutes(app, deps) {
|
|
|
48823
49380
|
const grant = await requireAccessGrant24(c, deps, workspaceId, "capabilities:manage");
|
|
48824
49381
|
const pack = await requirePack(db, workspaceId, c.req.param("packId"));
|
|
48825
49382
|
const payload = UninstallPackRequest.parse(await c.req.json());
|
|
48826
|
-
const requestDigest =
|
|
49383
|
+
const requestDigest = sha2562(
|
|
48827
49384
|
stableJson4({
|
|
48828
49385
|
packId: pack.id,
|
|
48829
49386
|
expectedInstallationVersion: payload.expectedInstallationVersion
|
|
@@ -49067,8 +49624,8 @@ function packFailureCode(error) {
|
|
|
49067
49624
|
if (error instanceof CapabilityComponentVersionConflictError) return "component_version_conflict";
|
|
49068
49625
|
return "component_install_failed";
|
|
49069
49626
|
}
|
|
49070
|
-
function
|
|
49071
|
-
return
|
|
49627
|
+
function sha2562(value) {
|
|
49628
|
+
return createHash15("sha256").update(value).digest("hex");
|
|
49072
49629
|
}
|
|
49073
49630
|
async function requirePack(db, workspaceId, packId) {
|
|
49074
49631
|
const pack = await resolveCapabilityPack(db, workspaceId, packId);
|
|
@@ -49112,7 +49669,7 @@ async function validateDocumentBaseIds(db, workspaceId, documentBaseIds) {
|
|
|
49112
49669
|
}
|
|
49113
49670
|
|
|
49114
49671
|
// src/routes/plugins.ts
|
|
49115
|
-
import { createHash as
|
|
49672
|
+
import { createHash as createHash16 } from "crypto";
|
|
49116
49673
|
import {
|
|
49117
49674
|
createPinnedIntegrationTransport as createPinnedIntegrationTransport2,
|
|
49118
49675
|
fetchIntegrationSourceDocument as fetchIntegrationSourceDocument2
|
|
@@ -49165,10 +49722,10 @@ var githubMetadataMaxBytes = 4 * 1024 * 1024;
|
|
|
49165
49722
|
var githubBlobResponseMaxBytes = 512 * 1024;
|
|
49166
49723
|
function createGitHubSkillSourceClient(settings, requestJson = (path, maxBytes, label) => githubJson(settings, path, maxBytes, label)) {
|
|
49167
49724
|
return {
|
|
49168
|
-
resolveCommit: async (
|
|
49725
|
+
resolveCommit: async (owner2, repository, ref) => {
|
|
49169
49726
|
const payload = recordValue(
|
|
49170
49727
|
await requestJson(
|
|
49171
|
-
`/repos/${encodeURIComponent(
|
|
49728
|
+
`/repos/${encodeURIComponent(owner2)}/${encodeURIComponent(repository)}/commits/${encodeURIComponent(ref)}`,
|
|
49172
49729
|
githubMetadataMaxBytes,
|
|
49173
49730
|
"GitHub Skill commit"
|
|
49174
49731
|
),
|
|
@@ -49178,10 +49735,10 @@ function createGitHubSkillSourceClient(settings, requestJson = (path, maxBytes,
|
|
|
49178
49735
|
if (!sha) throw new Error("GitHub commit response omitted sha");
|
|
49179
49736
|
return sha.toLowerCase();
|
|
49180
49737
|
},
|
|
49181
|
-
listTree: async (
|
|
49738
|
+
listTree: async (owner2, repository, commit) => {
|
|
49182
49739
|
const payload = recordValue(
|
|
49183
49740
|
await requestJson(
|
|
49184
|
-
`/repos/${encodeURIComponent(
|
|
49741
|
+
`/repos/${encodeURIComponent(owner2)}/${encodeURIComponent(repository)}/git/trees/${encodeURIComponent(commit)}?recursive=1`,
|
|
49185
49742
|
githubMetadataMaxBytes,
|
|
49186
49743
|
"GitHub Skill tree"
|
|
49187
49744
|
),
|
|
@@ -49204,10 +49761,10 @@ function createGitHubSkillSourceClient(settings, requestJson = (path, maxBytes,
|
|
|
49204
49761
|
return { path, type, mode, sha, size: typeof size === "number" ? size : null };
|
|
49205
49762
|
});
|
|
49206
49763
|
},
|
|
49207
|
-
readBlob: async (
|
|
49764
|
+
readBlob: async (owner2, repository, sha) => {
|
|
49208
49765
|
const payload = recordValue(
|
|
49209
49766
|
await requestJson(
|
|
49210
|
-
`/repos/${encodeURIComponent(
|
|
49767
|
+
`/repos/${encodeURIComponent(owner2)}/${encodeURIComponent(repository)}/git/blobs/${encodeURIComponent(sha)}`,
|
|
49211
49768
|
githubBlobResponseMaxBytes,
|
|
49212
49769
|
"GitHub Skill file"
|
|
49213
49770
|
),
|
|
@@ -49333,7 +49890,7 @@ function registerPluginRoutes(app, deps, overrides = {}) {
|
|
|
49333
49890
|
message: "A Plugin component changed after preview. Review the updated bill of materials."
|
|
49334
49891
|
});
|
|
49335
49892
|
}
|
|
49336
|
-
const requestDigest =
|
|
49893
|
+
const requestDigest = sha2563(
|
|
49337
49894
|
stableJson5({
|
|
49338
49895
|
url: payload.url,
|
|
49339
49896
|
expectedManifestDigest: payload.expectedManifestDigest,
|
|
@@ -49471,13 +50028,13 @@ async function resolvePluginPackage(input) {
|
|
|
49471
50028
|
message: error instanceof Error ? error.message : "Plugin manifest is invalid"
|
|
49472
50029
|
});
|
|
49473
50030
|
}
|
|
49474
|
-
const manifestDigest =
|
|
50031
|
+
const manifestDigest = sha2563(stableJson5(manifest));
|
|
49475
50032
|
const components = [];
|
|
49476
50033
|
for (const component of manifest.components) {
|
|
49477
50034
|
if (component.kind === "skill") {
|
|
49478
50035
|
const resolved = await resolveSkillImport(component.url, input.github);
|
|
49479
50036
|
const capabilityId = portableSkillCapabilityId(resolved.preview);
|
|
49480
|
-
const digest3 =
|
|
50037
|
+
const digest3 = sha2563(
|
|
49481
50038
|
stableJson5({
|
|
49482
50039
|
capabilityId,
|
|
49483
50040
|
sourceCommit: resolved.preview.sourceCommit,
|
|
@@ -49627,7 +50184,7 @@ async function resolvePluginPackage(input) {
|
|
|
49627
50184
|
continue;
|
|
49628
50185
|
}
|
|
49629
50186
|
const server = configuredMcpServer(input.deps.settings.mcpServers, component.serverId);
|
|
49630
|
-
const digest2 =
|
|
50187
|
+
const digest2 = sha2563(stableJson5(safeMcpFacts(server)));
|
|
49631
50188
|
components.push({
|
|
49632
50189
|
preview: {
|
|
49633
50190
|
key: component.key,
|
|
@@ -49776,7 +50333,7 @@ function safeMcpFacts(server) {
|
|
|
49776
50333
|
};
|
|
49777
50334
|
}
|
|
49778
50335
|
function integrationDigest(resolved) {
|
|
49779
|
-
return
|
|
50336
|
+
return sha2563(
|
|
49780
50337
|
stableJson5({
|
|
49781
50338
|
capabilityId: resolved.preview.capabilityId,
|
|
49782
50339
|
revisionId: resolved.preview.revisionId,
|
|
@@ -49842,8 +50399,8 @@ function pluginFailureCode(error) {
|
|
|
49842
50399
|
if (error instanceof HTTPException51) return `http_${error.status}`;
|
|
49843
50400
|
return "component_install_failed";
|
|
49844
50401
|
}
|
|
49845
|
-
function
|
|
49846
|
-
return
|
|
50402
|
+
function sha2563(value) {
|
|
50403
|
+
return createHash16("sha256").update(value).digest("hex");
|
|
49847
50404
|
}
|
|
49848
50405
|
|
|
49849
50406
|
// src/routes/skills.ts
|
|
@@ -49873,7 +50430,7 @@ import {
|
|
|
49873
50430
|
uninstallPortableSkill
|
|
49874
50431
|
} from "@opengeni/db";
|
|
49875
50432
|
import { loadSkillLibrarySkill, skillLibraryRepositoryUrl } from "@opengeni/runtime/skill-library";
|
|
49876
|
-
import { createHash as
|
|
50433
|
+
import { createHash as createHash17 } from "crypto";
|
|
49877
50434
|
import { HTTPException as HTTPException52 } from "hono/http-exception";
|
|
49878
50435
|
function registerSkillRoutes(app, deps, overrides = {}) {
|
|
49879
50436
|
const github = overrides.github ?? createGitHubSkillSourceClient(deps.settings);
|
|
@@ -49908,7 +50465,7 @@ function registerSkillRoutes(app, deps, overrides = {}) {
|
|
|
49908
50465
|
path: file.path,
|
|
49909
50466
|
content: file.content,
|
|
49910
50467
|
byteSize: bytes.byteLength,
|
|
49911
|
-
contentSha256:
|
|
50468
|
+
contentSha256: createHash17("sha256").update(bytes).digest("hex")
|
|
49912
50469
|
};
|
|
49913
50470
|
});
|
|
49914
50471
|
try {
|
|
@@ -50178,7 +50735,7 @@ import {
|
|
|
50178
50735
|
import { HTTPException as HTTPException54 } from "hono/http-exception";
|
|
50179
50736
|
import {
|
|
50180
50737
|
requireAccessGrant as requireAccessGrant28,
|
|
50181
|
-
requireAccessGrantAuthorization as
|
|
50738
|
+
requireAccessGrantAuthorization as requireAccessGrantAuthorization11,
|
|
50182
50739
|
requirePermission as requirePermission7
|
|
50183
50740
|
} from "@opengeni/core";
|
|
50184
50741
|
import {
|
|
@@ -50219,7 +50776,7 @@ async function parseRigRequest(c, schema, label) {
|
|
|
50219
50776
|
function registerRigRoutes(app, deps) {
|
|
50220
50777
|
const { db, workflowClient } = deps;
|
|
50221
50778
|
async function requireRigMutation(c, workspaceId, permission) {
|
|
50222
|
-
const authorization = await
|
|
50779
|
+
const authorization = await requireAccessGrantAuthorization11(c, deps, workspaceId, permission);
|
|
50223
50780
|
const rigId = c.req.param("rigId");
|
|
50224
50781
|
if (!rigId) {
|
|
50225
50782
|
throw new HTTPException54(400, { message: "rig id is required" });
|
|
@@ -50297,7 +50854,7 @@ function registerRigRoutes(app, deps) {
|
|
|
50297
50854
|
});
|
|
50298
50855
|
app.post("/v1/workspaces/:workspaceId/rigs", async (c) => {
|
|
50299
50856
|
const workspaceId = c.req.param("workspaceId");
|
|
50300
|
-
const authorization = await
|
|
50857
|
+
const authorization = await requireAccessGrantAuthorization11(c, deps, workspaceId);
|
|
50301
50858
|
const grant = authorization.grant;
|
|
50302
50859
|
requirePermission7(grant, "rigs:manage");
|
|
50303
50860
|
const payload = await parseRigRequest(c, CreateRigRequest, "Rig create request");
|
|
@@ -50323,7 +50880,7 @@ function registerRigRoutes(app, deps) {
|
|
|
50323
50880
|
});
|
|
50324
50881
|
app.patch("/v1/workspaces/:workspaceId/rigs/:rigId", async (c) => {
|
|
50325
50882
|
const workspaceId = c.req.param("workspaceId");
|
|
50326
|
-
const authorization = await
|
|
50883
|
+
const authorization = await requireAccessGrantAuthorization11(c, deps, workspaceId);
|
|
50327
50884
|
const grant = authorization.grant;
|
|
50328
50885
|
requirePermission7(grant, "rigs:manage");
|
|
50329
50886
|
const rig = await requireRigForApi2(db, grant, c.req.param("rigId"));
|
|
@@ -50338,7 +50895,7 @@ function registerRigRoutes(app, deps) {
|
|
|
50338
50895
|
});
|
|
50339
50896
|
app.delete("/v1/workspaces/:workspaceId/rigs/:rigId", async (c) => {
|
|
50340
50897
|
const workspaceId = c.req.param("workspaceId");
|
|
50341
|
-
const authorization = await
|
|
50898
|
+
const authorization = await requireAccessGrantAuthorization11(c, deps, workspaceId);
|
|
50342
50899
|
const grant = authorization.grant;
|
|
50343
50900
|
requirePermission7(grant, "rigs:manage");
|
|
50344
50901
|
const rig = await requireRigForApi2(db, grant, c.req.param("rigId"));
|
|
@@ -50439,6 +50996,7 @@ function registerRigRoutes(app, deps) {
|
|
|
50439
50996
|
}
|
|
50440
50997
|
|
|
50441
50998
|
// src/routes/scheduled-tasks.ts
|
|
50999
|
+
import { z as z14 } from "zod";
|
|
50442
51000
|
import {
|
|
50443
51001
|
CreateScheduledTaskRequest as CreateScheduledTaskRequest2,
|
|
50444
51002
|
TriggerScheduledTaskRequest,
|
|
@@ -50504,7 +51062,24 @@ function registerScheduledTaskRoutes(app, deps) {
|
|
|
50504
51062
|
app.get("/v1/workspaces/:workspaceId/scheduled-tasks", async (c) => {
|
|
50505
51063
|
const workspaceId = c.req.param("workspaceId");
|
|
50506
51064
|
const grant = await requireAccessGrant29(c, deps, workspaceId, "scheduled_tasks:run");
|
|
50507
|
-
const
|
|
51065
|
+
const sessionId = c.req.query("sessionId");
|
|
51066
|
+
const offset = Number(c.req.query("offset") ?? 0);
|
|
51067
|
+
if (!Number.isSafeInteger(offset) || offset < 0) {
|
|
51068
|
+
throw new HTTPException55(400, { message: "invalid offset" });
|
|
51069
|
+
}
|
|
51070
|
+
if (sessionId !== void 0) {
|
|
51071
|
+
if (!z14.string().uuid().safeParse(sessionId).success) {
|
|
51072
|
+
throw new HTTPException55(400, { message: "invalid sessionId" });
|
|
51073
|
+
}
|
|
51074
|
+
await requireAccessGrant29(c, deps, workspaceId, "sessions:control");
|
|
51075
|
+
}
|
|
51076
|
+
const tasks = await listScheduledTasks2(
|
|
51077
|
+
db,
|
|
51078
|
+
workspaceId,
|
|
51079
|
+
boundedLimit(c.req.query("limit")),
|
|
51080
|
+
offset,
|
|
51081
|
+
sessionId
|
|
51082
|
+
);
|
|
50508
51083
|
return c.json(tasks.map((task) => scheduledTaskForGrant2(task, grant)));
|
|
50509
51084
|
});
|
|
50510
51085
|
app.get("/v1/workspaces/:workspaceId/scheduled-tasks/:taskId", async (c) => {
|
|
@@ -50671,6 +51246,7 @@ function registerScheduledTaskRoutes(app, deps) {
|
|
|
50671
51246
|
}
|
|
50672
51247
|
|
|
50673
51248
|
// src/routes/sessions.ts
|
|
51249
|
+
import { scheduledSessionIds } from "@opengeni/db";
|
|
50674
51250
|
import {
|
|
50675
51251
|
AcknowledgeStreamRequest,
|
|
50676
51252
|
ApplySessionGoalRevisionRequest,
|
|
@@ -50764,7 +51340,7 @@ import {
|
|
|
50764
51340
|
getLiveEnrollmentConnection as getLiveEnrollmentConnection7,
|
|
50765
51341
|
getRetainedProcess,
|
|
50766
51342
|
getSandbox as getSandbox4,
|
|
50767
|
-
getSession as
|
|
51343
|
+
getSession as getSession8,
|
|
50768
51344
|
readActiveSandbox as readActiveSandbox3,
|
|
50769
51345
|
deleteSessionTreeIfQuiescent,
|
|
50770
51346
|
getSessionEvent,
|
|
@@ -51586,7 +52162,7 @@ function normalizeExpiry(value) {
|
|
|
51586
52162
|
}
|
|
51587
52163
|
|
|
51588
52164
|
// src/routes/sessions.ts
|
|
51589
|
-
import { z as
|
|
52165
|
+
import { z as z15, ZodError } from "zod";
|
|
51590
52166
|
import {
|
|
51591
52167
|
NatsControlRpc as NatsControlRpc6,
|
|
51592
52168
|
negotiateCapabilities,
|
|
@@ -51600,7 +52176,7 @@ import {
|
|
|
51600
52176
|
getManagedAuthRequestActorEpoch as getManagedAuthRequestActorEpoch2,
|
|
51601
52177
|
hasPermission as hasPermission19,
|
|
51602
52178
|
requireAccessGrant as requireAccessGrant30,
|
|
51603
|
-
requireAccessGrantAuthorization as
|
|
52179
|
+
requireAccessGrantAuthorization as requireAccessGrantAuthorization12,
|
|
51604
52180
|
requireFreshAccessGrant as requireFreshAccessGrant2,
|
|
51605
52181
|
requirePermission as requirePermission8,
|
|
51606
52182
|
requireSessionAuthorization as requireSessionAuthorization7,
|
|
@@ -51635,7 +52211,7 @@ import {
|
|
|
51635
52211
|
updateManagedHumanSessionVisibility,
|
|
51636
52212
|
updateSessionToolPolicy,
|
|
51637
52213
|
updateSessionTitle as updateSessionTitle2,
|
|
51638
|
-
workflowIdForSession as
|
|
52214
|
+
workflowIdForSession as workflowIdForSession3,
|
|
51639
52215
|
sessionWithEffectiveToolPolicy as sessionWithEffectiveToolPolicy2,
|
|
51640
52216
|
workspaceSessionToolPolicyDefaultServerIds as workspaceSessionToolPolicyDefaultServerIds2,
|
|
51641
52217
|
workspaceSessionToolPolicyServerIds as workspaceSessionToolPolicyServerIds2,
|
|
@@ -51899,6 +52475,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
51899
52475
|
db,
|
|
51900
52476
|
settings,
|
|
51901
52477
|
bus,
|
|
52478
|
+
objectStorage,
|
|
51902
52479
|
observability: deps.observability
|
|
51903
52480
|
};
|
|
51904
52481
|
const workspaceCaptureManifestCache = new WorkspaceCaptureManifestCache();
|
|
@@ -51986,7 +52563,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
51986
52563
|
const authorizeSessionHttp = async (c, next) => {
|
|
51987
52564
|
const workspaceId = c.req.param("workspaceId") ?? "";
|
|
51988
52565
|
const sessionId = c.req.param("sessionId") ?? "";
|
|
51989
|
-
if (!
|
|
52566
|
+
if (!z15.string().uuid().safeParse(sessionId).success) {
|
|
51990
52567
|
throw new HTTPException57(404, { message: "session not found" });
|
|
51991
52568
|
}
|
|
51992
52569
|
const operation = sessionAuthorizationOperationForHttp(
|
|
@@ -52027,11 +52604,12 @@ function registerSessionRoutes(app, deps) {
|
|
|
52027
52604
|
db,
|
|
52028
52605
|
settings,
|
|
52029
52606
|
bus,
|
|
52607
|
+
objectStorage,
|
|
52030
52608
|
...deps.establishSandboxSession ? { establishSandboxSession: deps.establishSandboxSession } : {}
|
|
52031
52609
|
};
|
|
52032
52610
|
app.get("/v1/workspaces/:workspaceId/session-tenancy/capabilities", async (c) => {
|
|
52033
52611
|
const workspaceId = c.req.param("workspaceId");
|
|
52034
|
-
const authorization = await
|
|
52612
|
+
const authorization = await requireAccessGrantAuthorization12(
|
|
52035
52613
|
c,
|
|
52036
52614
|
deps,
|
|
52037
52615
|
workspaceId,
|
|
@@ -52041,7 +52619,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
52041
52619
|
});
|
|
52042
52620
|
app.post("/v1/workspaces/:workspaceId/sessions", async (c) => {
|
|
52043
52621
|
const workspaceId = c.req.param("workspaceId");
|
|
52044
|
-
const authorization = await
|
|
52622
|
+
const authorization = await requireAccessGrantAuthorization12(
|
|
52045
52623
|
c,
|
|
52046
52624
|
deps,
|
|
52047
52625
|
workspaceId,
|
|
@@ -52082,7 +52660,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
52082
52660
|
});
|
|
52083
52661
|
app.put("/v1/workspaces/:workspaceId/new-session-draft", async (c) => {
|
|
52084
52662
|
const workspaceId = c.req.param("workspaceId");
|
|
52085
|
-
const authorization = await
|
|
52663
|
+
const authorization = await requireAccessGrantAuthorization12(
|
|
52086
52664
|
c,
|
|
52087
52665
|
deps,
|
|
52088
52666
|
workspaceId,
|
|
@@ -52141,7 +52719,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
52141
52719
|
});
|
|
52142
52720
|
app.get("/v1/workspaces/:workspaceId/sessions", async (c) => {
|
|
52143
52721
|
const workspaceId = c.req.param("workspaceId");
|
|
52144
|
-
const authorization = await
|
|
52722
|
+
const authorization = await requireAccessGrantAuthorization12(
|
|
52145
52723
|
c,
|
|
52146
52724
|
deps,
|
|
52147
52725
|
workspaceId,
|
|
@@ -52167,6 +52745,12 @@ function registerSessionRoutes(app, deps) {
|
|
|
52167
52745
|
...query.pinsOnly ? { pinsOnly: true } : {},
|
|
52168
52746
|
...query.archivedOnly ? { archivedOnly: true } : {},
|
|
52169
52747
|
...query.parentSessionId !== void 0 ? { parentSessionId: query.parentSessionId } : {},
|
|
52748
|
+
...query.channelId !== void 0 ? { channelId: query.channelId } : {},
|
|
52749
|
+
...query.createdBy ? { createdBy: query.createdBy } : {},
|
|
52750
|
+
...query.updatedFrom ? { updatedFrom: query.updatedFrom } : {},
|
|
52751
|
+
...query.updatedBefore ? { updatedBefore: query.updatedBefore } : {},
|
|
52752
|
+
...query.createdFrom ? { createdFrom: query.createdFrom } : {},
|
|
52753
|
+
...query.createdBefore ? { createdBefore: query.createdBefore } : {},
|
|
52170
52754
|
...authorizationScope ? { authorizationScope } : {},
|
|
52171
52755
|
// A managed human's own personal workspace has no membership row, so
|
|
52172
52756
|
// the list's removal fence must fall back to the organization-membership
|
|
@@ -52192,10 +52776,19 @@ function registerSessionRoutes(app, deps) {
|
|
|
52192
52776
|
workspaceId,
|
|
52193
52777
|
sessionIds: [...page.pinned, ...page.sessions].map((session) => session.id)
|
|
52194
52778
|
});
|
|
52779
|
+
const scheduleTargets = hasPermission19(grant.permissions, "scheduled_tasks:run") && hasPermission19(grant.permissions, "sessions:control") ? await scheduledSessionIds(
|
|
52780
|
+
db,
|
|
52781
|
+
workspaceId,
|
|
52782
|
+
[...page.pinned, ...page.sessions].map((session) => session.id)
|
|
52783
|
+
) : /* @__PURE__ */ new Set();
|
|
52195
52784
|
const decorate = (session) => {
|
|
52196
52785
|
const activity = commandActivity.get(session.id);
|
|
52197
52786
|
return sessionWithEffectiveToolPolicy2(
|
|
52198
|
-
{
|
|
52787
|
+
{
|
|
52788
|
+
...session,
|
|
52789
|
+
hasSchedules: scheduleTargets.has(session.id),
|
|
52790
|
+
...activity ? { backgroundCommandActivity: activity } : {}
|
|
52791
|
+
},
|
|
52199
52792
|
policy.workspaceServerIds,
|
|
52200
52793
|
policy.workspaceDefaultServerIds
|
|
52201
52794
|
);
|
|
@@ -52203,6 +52796,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
52203
52796
|
if (pageView) {
|
|
52204
52797
|
return c.json({
|
|
52205
52798
|
...page,
|
|
52799
|
+
...query.hasPageFilters ? { filtersApplied: true } : {},
|
|
52206
52800
|
pinned: page.pinned.map(decorate),
|
|
52207
52801
|
sessions: page.sessions.map(decorate)
|
|
52208
52802
|
});
|
|
@@ -52345,7 +52939,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
52345
52939
|
const workspaceId = c.req.param("workspaceId");
|
|
52346
52940
|
const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:read");
|
|
52347
52941
|
const sessionId = c.req.param("sessionId");
|
|
52348
|
-
if (!
|
|
52942
|
+
if (!z15.string().uuid().safeParse(sessionId).success) {
|
|
52349
52943
|
throw new HTTPException57(404, { message: "session not found" });
|
|
52350
52944
|
}
|
|
52351
52945
|
const session = await getSessionForSubject(
|
|
@@ -52358,13 +52952,25 @@ function registerSessionRoutes(app, deps) {
|
|
|
52358
52952
|
if (!session) {
|
|
52359
52953
|
throw new HTTPException57(404, { message: "session not found" });
|
|
52360
52954
|
}
|
|
52361
|
-
|
|
52955
|
+
const activity = await backgroundCommandActivityForSessions(db, {
|
|
52956
|
+
accountId: grant.accountId,
|
|
52957
|
+
workspaceId,
|
|
52958
|
+
sessionIds: [sessionId]
|
|
52959
|
+
});
|
|
52960
|
+
const scheduleTargets = hasPermission19(grant.permissions, "scheduled_tasks:run") && hasPermission19(grant.permissions, "sessions:control") ? await scheduledSessionIds(db, workspaceId, [sessionId]) : /* @__PURE__ */ new Set();
|
|
52961
|
+
return c.json(
|
|
52962
|
+
await withEffectivePolicy(deps, workspaceId, grant.subjectId, {
|
|
52963
|
+
...session,
|
|
52964
|
+
hasSchedules: scheduleTargets.has(sessionId),
|
|
52965
|
+
...activity.get(sessionId) ? { backgroundCommandActivity: activity.get(sessionId) } : {}
|
|
52966
|
+
})
|
|
52967
|
+
);
|
|
52362
52968
|
});
|
|
52363
52969
|
app.get("/v1/workspaces/:workspaceId/sessions/:sessionId/model-context", async (c) => {
|
|
52364
52970
|
const workspaceId = c.req.param("workspaceId");
|
|
52365
52971
|
const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:read");
|
|
52366
52972
|
const sessionId = c.req.param("sessionId");
|
|
52367
|
-
if (!
|
|
52973
|
+
if (!z15.string().uuid().safeParse(sessionId).success) {
|
|
52368
52974
|
throw new HTTPException57(404, { message: "session not found" });
|
|
52369
52975
|
}
|
|
52370
52976
|
const session = await getSessionForSubject(
|
|
@@ -52392,7 +52998,8 @@ function registerSessionRoutes(app, deps) {
|
|
|
52392
52998
|
const commands = await listSessionBackgroundCommands(db, {
|
|
52393
52999
|
accountId: grant.accountId,
|
|
52394
53000
|
workspaceId,
|
|
52395
|
-
sessionId
|
|
53001
|
+
sessionId,
|
|
53002
|
+
activeOnly: true
|
|
52396
53003
|
});
|
|
52397
53004
|
return c.json({ commands });
|
|
52398
53005
|
});
|
|
@@ -52402,7 +53009,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
52402
53009
|
const workspaceId = c.req.param("workspaceId");
|
|
52403
53010
|
const sessionId = c.req.param("sessionId");
|
|
52404
53011
|
const commandId = c.req.param("commandId");
|
|
52405
|
-
if (!
|
|
53012
|
+
if (!z15.string().uuid().safeParse(commandId).success) {
|
|
52406
53013
|
throw new HTTPException57(404, { message: "background command not found" });
|
|
52407
53014
|
}
|
|
52408
53015
|
const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
|
|
@@ -52422,7 +53029,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
52422
53029
|
app.put("/v1/workspaces/:workspaceId/sessions/:sessionId/visibility", async (c) => {
|
|
52423
53030
|
const workspaceId = c.req.param("workspaceId");
|
|
52424
53031
|
const sessionId = c.req.param("sessionId");
|
|
52425
|
-
const authorization = await
|
|
53032
|
+
const authorization = await requireAccessGrantAuthorization12(c, deps, workspaceId);
|
|
52426
53033
|
const parsed = UpdateSessionVisibilityRequest.safeParse(await c.req.json().catch(() => null));
|
|
52427
53034
|
if (!parsed.success) {
|
|
52428
53035
|
throw new ApiHttpError(422, {
|
|
@@ -52450,7 +53057,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
52450
53057
|
app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/forks", async (c) => {
|
|
52451
53058
|
const workspaceId = c.req.param("workspaceId");
|
|
52452
53059
|
const sessionId = c.req.param("sessionId");
|
|
52453
|
-
const authorization = await
|
|
53060
|
+
const authorization = await requireAccessGrantAuthorization12(c, deps, workspaceId);
|
|
52454
53061
|
const parsed = ForkSessionRequest.safeParse(await c.req.json().catch(() => null));
|
|
52455
53062
|
if (!parsed.success) {
|
|
52456
53063
|
throw new ApiHttpError(422, {
|
|
@@ -52482,7 +53089,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
52482
53089
|
accountId,
|
|
52483
53090
|
workspaceId,
|
|
52484
53091
|
sessionId,
|
|
52485
|
-
workflowId:
|
|
53092
|
+
workflowId: workflowIdForSession3(sessionId),
|
|
52486
53093
|
wakeRevision: result.workflowWakeRevision
|
|
52487
53094
|
});
|
|
52488
53095
|
}
|
|
@@ -52491,7 +53098,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
52491
53098
|
const workspaceId = c.req.param("workspaceId");
|
|
52492
53099
|
const sessionId = c.req.param("sessionId");
|
|
52493
53100
|
const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
|
|
52494
|
-
if (!
|
|
53101
|
+
if (!z15.string().uuid().safeParse(sessionId).success) {
|
|
52495
53102
|
throw new HTTPException57(400, { message: "invalid session id" });
|
|
52496
53103
|
}
|
|
52497
53104
|
const parsed = BeginSessionRealtimeRequest.safeParse(await c.req.json().catch(() => null));
|
|
@@ -52526,7 +53133,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
52526
53133
|
const sessionId = c.req.param("sessionId");
|
|
52527
53134
|
const realtimeId = c.req.param("realtimeId");
|
|
52528
53135
|
const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
|
|
52529
|
-
if (!
|
|
53136
|
+
if (!z15.string().uuid().safeParse(sessionId).success || !z15.string().uuid().safeParse(realtimeId).success) {
|
|
52530
53137
|
throw new HTTPException57(400, {
|
|
52531
53138
|
message: "invalid realtime lifecycle id"
|
|
52532
53139
|
});
|
|
@@ -52562,7 +53169,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
52562
53169
|
const sessionId = c.req.param("sessionId");
|
|
52563
53170
|
const realtimeId = c.req.param("realtimeId");
|
|
52564
53171
|
const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
|
|
52565
|
-
if (!
|
|
53172
|
+
if (!z15.string().uuid().safeParse(sessionId).success || !z15.string().uuid().safeParse(realtimeId).success) {
|
|
52566
53173
|
throw new HTTPException57(400, {
|
|
52567
53174
|
message: "invalid realtime lifecycle id"
|
|
52568
53175
|
});
|
|
@@ -52596,7 +53203,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
52596
53203
|
const workspaceId = c.req.param("workspaceId");
|
|
52597
53204
|
const sessionId = c.req.param("sessionId");
|
|
52598
53205
|
const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
|
|
52599
|
-
if (!
|
|
53206
|
+
if (!z15.string().uuid().safeParse(sessionId).success) {
|
|
52600
53207
|
throw new HTTPException57(404, { message: "session not found" });
|
|
52601
53208
|
}
|
|
52602
53209
|
const parsed = CodexRealtimeWebrtcRequest.safeParse(await c.req.json().catch(() => null));
|
|
@@ -52775,7 +53382,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
52775
53382
|
const workspaceId = c.req.param("workspaceId");
|
|
52776
53383
|
const sessionId = c.req.param("sessionId");
|
|
52777
53384
|
const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
|
|
52778
|
-
if (!
|
|
53385
|
+
if (!z15.string().uuid().safeParse(sessionId).success) {
|
|
52779
53386
|
throw new HTTPException57(404, { message: "session not found" });
|
|
52780
53387
|
}
|
|
52781
53388
|
const parsed = GatewayRealtimeConnectRequest.safeParse(await c.req.json().catch(() => null));
|
|
@@ -52894,7 +53501,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
52894
53501
|
const workspaceId = c.req.param("workspaceId");
|
|
52895
53502
|
const sessionId = c.req.param("sessionId");
|
|
52896
53503
|
const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
|
|
52897
|
-
if (!
|
|
53504
|
+
if (!z15.string().uuid().safeParse(sessionId).success) {
|
|
52898
53505
|
throw new HTTPException57(404, { message: "session not found" });
|
|
52899
53506
|
}
|
|
52900
53507
|
const parsed = GatewayRealtimeConnectRequest.safeParse(await c.req.json().catch(() => null));
|
|
@@ -53017,7 +53624,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
53017
53624
|
const realtimeId = c.req.param("realtimeId");
|
|
53018
53625
|
const connectionId = c.req.param("connectionId");
|
|
53019
53626
|
const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
|
|
53020
|
-
if (!
|
|
53627
|
+
if (!z15.string().uuid().safeParse(sessionId).success || !z15.string().uuid().safeParse(realtimeId).success || !z15.string().uuid().safeParse(connectionId).success) {
|
|
53021
53628
|
throw new HTTPException57(400, {
|
|
53022
53629
|
message: "invalid realtime connection id"
|
|
53023
53630
|
});
|
|
@@ -53059,7 +53666,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
53059
53666
|
const sessionId = c.req.param("sessionId");
|
|
53060
53667
|
const realtimeId = c.req.param("realtimeId");
|
|
53061
53668
|
const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:control");
|
|
53062
|
-
if (!
|
|
53669
|
+
if (!z15.string().uuid().safeParse(sessionId).success || !z15.string().uuid().safeParse(realtimeId).success) {
|
|
53063
53670
|
throw new HTTPException57(400, { message: "invalid realtime ledger id" });
|
|
53064
53671
|
}
|
|
53065
53672
|
const parsed = SyncSessionRealtimeLedgerRequest.safeParse(
|
|
@@ -53093,7 +53700,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
53093
53700
|
);
|
|
53094
53701
|
app.put("/v1/workspaces/:workspaceId/sessions/:sessionId/pin", async (c) => {
|
|
53095
53702
|
const workspaceId = c.req.param("workspaceId");
|
|
53096
|
-
const authorization = await
|
|
53703
|
+
const authorization = await requireAccessGrantAuthorization12(
|
|
53097
53704
|
c,
|
|
53098
53705
|
deps,
|
|
53099
53706
|
workspaceId,
|
|
@@ -53101,7 +53708,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
53101
53708
|
);
|
|
53102
53709
|
const grant = authorization.grant;
|
|
53103
53710
|
const sessionId = c.req.param("sessionId");
|
|
53104
|
-
if (!
|
|
53711
|
+
if (!z15.string().uuid().safeParse(sessionId).success) {
|
|
53105
53712
|
throw new HTTPException57(404, { message: "session not found" });
|
|
53106
53713
|
}
|
|
53107
53714
|
const parsed = UpdateSessionPinRequest.safeParse(await c.req.json().catch(() => null));
|
|
@@ -53146,7 +53753,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
53146
53753
|
});
|
|
53147
53754
|
app.put("/v1/workspaces/:workspaceId/sessions/:sessionId/attention", async (c) => {
|
|
53148
53755
|
const workspaceId = c.req.param("workspaceId");
|
|
53149
|
-
const authorization = await
|
|
53756
|
+
const authorization = await requireAccessGrantAuthorization12(
|
|
53150
53757
|
c,
|
|
53151
53758
|
deps,
|
|
53152
53759
|
workspaceId,
|
|
@@ -53154,7 +53761,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
53154
53761
|
);
|
|
53155
53762
|
const grant = authorization.grant;
|
|
53156
53763
|
const sessionId = c.req.param("sessionId");
|
|
53157
|
-
if (!
|
|
53764
|
+
if (!z15.string().uuid().safeParse(sessionId).success) {
|
|
53158
53765
|
throw new HTTPException57(404, { message: "session not found" });
|
|
53159
53766
|
}
|
|
53160
53767
|
const parsed = UpdateSessionAttentionRequest.safeParse(await c.req.json().catch(() => null));
|
|
@@ -53196,7 +53803,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
53196
53803
|
});
|
|
53197
53804
|
app.put("/v1/workspaces/:workspaceId/sessions/:sessionId/archive", async (c) => {
|
|
53198
53805
|
const workspaceId = c.req.param("workspaceId");
|
|
53199
|
-
const authorization = await
|
|
53806
|
+
const authorization = await requireAccessGrantAuthorization12(
|
|
53200
53807
|
c,
|
|
53201
53808
|
deps,
|
|
53202
53809
|
workspaceId,
|
|
@@ -53242,17 +53849,35 @@ function registerSessionRoutes(app, deps) {
|
|
|
53242
53849
|
const workspaceId = c.req.param("workspaceId");
|
|
53243
53850
|
const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:read");
|
|
53244
53851
|
const lineage = await readSessionLineage(deps, grant, c.req.param("sessionId"));
|
|
53852
|
+
const sessionIds = [
|
|
53853
|
+
c.req.param("sessionId"),
|
|
53854
|
+
...lineage.ancestors.map((session) => session.id)
|
|
53855
|
+
];
|
|
53856
|
+
const collect = (nodes) => {
|
|
53857
|
+
for (const node of nodes) {
|
|
53858
|
+
sessionIds.push(node.session.id);
|
|
53859
|
+
collect(node.children);
|
|
53860
|
+
}
|
|
53861
|
+
};
|
|
53862
|
+
collect(lineage.children);
|
|
53863
|
+
const targets = hasPermission19(grant.permissions, "scheduled_tasks:run") && hasPermission19(grant.permissions, "sessions:control") ? await scheduledSessionIds(db, workspaceId, sessionIds) : /* @__PURE__ */ new Set();
|
|
53864
|
+
const decorateNodes = (nodes) => nodes.map((node) => ({
|
|
53865
|
+
...node,
|
|
53866
|
+
session: { ...node.session, hasSchedules: targets.has(node.session.id) },
|
|
53867
|
+
children: decorateNodes(node.children)
|
|
53868
|
+
}));
|
|
53245
53869
|
const policy = await loadEffectivePolicyContext(deps, workspaceId, grant.subjectId);
|
|
53246
53870
|
return c.json({
|
|
53247
53871
|
...lineage,
|
|
53872
|
+
sessionHasSchedules: targets.has(c.req.param("sessionId")),
|
|
53248
53873
|
ancestors: lineage.ancestors.map(
|
|
53249
53874
|
(session) => sessionWithEffectiveToolPolicy2(
|
|
53250
|
-
session,
|
|
53875
|
+
{ ...session, hasSchedules: targets.has(session.id) },
|
|
53251
53876
|
policy.workspaceServerIds,
|
|
53252
53877
|
policy.workspaceDefaultServerIds
|
|
53253
53878
|
)
|
|
53254
53879
|
),
|
|
53255
|
-
children: mapLineageNodes(lineage.children, policy)
|
|
53880
|
+
children: decorateNodes(mapLineageNodes(lineage.children, policy))
|
|
53256
53881
|
});
|
|
53257
53882
|
});
|
|
53258
53883
|
app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/codex-account", async (c) => {
|
|
@@ -53620,7 +54245,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
53620
54245
|
accountId: grant.accountId,
|
|
53621
54246
|
workspaceId,
|
|
53622
54247
|
sessionId,
|
|
53623
|
-
workflowId:
|
|
54248
|
+
workflowId: workflowIdForSession3(sessionId),
|
|
53624
54249
|
wakeRevision: workflowWakeRevision
|
|
53625
54250
|
});
|
|
53626
54251
|
}
|
|
@@ -53703,7 +54328,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
53703
54328
|
accountId: grant.accountId,
|
|
53704
54329
|
workspaceId,
|
|
53705
54330
|
sessionId,
|
|
53706
|
-
workflowId:
|
|
54331
|
+
workflowId: workflowIdForSession3(sessionId),
|
|
53707
54332
|
wakeRevision: workflowWakeRevision
|
|
53708
54333
|
});
|
|
53709
54334
|
}
|
|
@@ -53751,7 +54376,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
53751
54376
|
accountId: grant.accountId,
|
|
53752
54377
|
workspaceId,
|
|
53753
54378
|
sessionId,
|
|
53754
|
-
workflowId:
|
|
54379
|
+
workflowId: workflowIdForSession3(sessionId),
|
|
53755
54380
|
wakeRevision: workflowWakeRevision2
|
|
53756
54381
|
});
|
|
53757
54382
|
}
|
|
@@ -53811,7 +54436,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
53811
54436
|
accountId: grant.accountId,
|
|
53812
54437
|
workspaceId,
|
|
53813
54438
|
sessionId,
|
|
53814
|
-
workflowId:
|
|
54439
|
+
workflowId: workflowIdForSession3(sessionId),
|
|
53815
54440
|
wakeRevision: workflowWakeRevision
|
|
53816
54441
|
});
|
|
53817
54442
|
}
|
|
@@ -54275,7 +54900,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
54275
54900
|
});
|
|
54276
54901
|
app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/steer", async (c) => {
|
|
54277
54902
|
const workspaceId = c.req.param("workspaceId");
|
|
54278
|
-
const authorization = await
|
|
54903
|
+
const authorization = await requireAccessGrantAuthorization12(
|
|
54279
54904
|
c,
|
|
54280
54905
|
deps,
|
|
54281
54906
|
workspaceId,
|
|
@@ -54312,7 +54937,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
54312
54937
|
});
|
|
54313
54938
|
app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/composer-draft/submit", async (c) => {
|
|
54314
54939
|
const workspaceId = c.req.param("workspaceId");
|
|
54315
|
-
const authorization = await
|
|
54940
|
+
const authorization = await requireAccessGrantAuthorization12(
|
|
54316
54941
|
c,
|
|
54317
54942
|
deps,
|
|
54318
54943
|
workspaceId,
|
|
@@ -54334,7 +54959,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
54334
54959
|
});
|
|
54335
54960
|
app.post("/v1/workspaces/:workspaceId/sessions/:sessionId/events", async (c) => {
|
|
54336
54961
|
const workspaceId = c.req.param("workspaceId");
|
|
54337
|
-
const authorization = await
|
|
54962
|
+
const authorization = await requireAccessGrantAuthorization12(
|
|
54338
54963
|
c,
|
|
54339
54964
|
deps,
|
|
54340
54965
|
workspaceId,
|
|
@@ -54395,7 +55020,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
54395
55020
|
});
|
|
54396
55021
|
}
|
|
54397
55022
|
await publishDurableSessionEvents3(bus, workspaceId, sessionId, accepted.events);
|
|
54398
|
-
const workflowId =
|
|
55023
|
+
const workflowId = workflowIdForSession3(sessionId);
|
|
54399
55024
|
await workflowClient.signalApprovalDecision({
|
|
54400
55025
|
accountId: grant.accountId,
|
|
54401
55026
|
workspaceId,
|
|
@@ -54439,7 +55064,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
54439
55064
|
workspaceId,
|
|
54440
55065
|
sessionId,
|
|
54441
55066
|
eventId: accepted.events[0]?.id ?? event.payload.requestId,
|
|
54442
|
-
workflowId:
|
|
55067
|
+
workflowId: workflowIdForSession3(sessionId),
|
|
54443
55068
|
workflowWakeRevision: accepted.workflowWakeRevision
|
|
54444
55069
|
});
|
|
54445
55070
|
}
|
|
@@ -54504,7 +55129,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
54504
55129
|
const grant = await requireAccessGrant30(c, deps, workspaceId, "sessions:read");
|
|
54505
55130
|
assertOwnershipEnabled();
|
|
54506
55131
|
const sessionId = c.req.param("sessionId");
|
|
54507
|
-
const session = await
|
|
55132
|
+
const session = await getSession8(db, workspaceId, sessionId);
|
|
54508
55133
|
if (!session) {
|
|
54509
55134
|
throw new HTTPException57(404, { message: "session not found" });
|
|
54510
55135
|
}
|
|
@@ -54637,7 +55262,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
54637
55262
|
const grant = await requireAccessGrant30(c, deps, workspaceId, "stream:acknowledge");
|
|
54638
55263
|
assertOwnershipEnabled();
|
|
54639
55264
|
const sessionId = c.req.param("sessionId");
|
|
54640
|
-
const session = await
|
|
55265
|
+
const session = await getSession8(db, workspaceId, sessionId);
|
|
54641
55266
|
if (!session) {
|
|
54642
55267
|
throw new HTTPException57(404, { message: "session not found" });
|
|
54643
55268
|
}
|
|
@@ -54666,7 +55291,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
54666
55291
|
const grant = await requireAccessGrant30(c, deps, workspaceId);
|
|
54667
55292
|
assertOwnershipEnabled();
|
|
54668
55293
|
const sessionId = c.req.param("sessionId");
|
|
54669
|
-
const session = await
|
|
55294
|
+
const session = await getSession8(db, workspaceId, sessionId);
|
|
54670
55295
|
if (!session) {
|
|
54671
55296
|
throw new HTTPException57(404, { message: "session not found" });
|
|
54672
55297
|
}
|
|
@@ -54821,7 +55446,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
54821
55446
|
requireViewerLifecyclePermission(grant);
|
|
54822
55447
|
assertOwnershipEnabled();
|
|
54823
55448
|
const sessionId = c.req.param("sessionId");
|
|
54824
|
-
const session = await
|
|
55449
|
+
const session = await getSession8(db, workspaceId, sessionId);
|
|
54825
55450
|
if (!session) {
|
|
54826
55451
|
throw new HTTPException57(404, { message: "session not found" });
|
|
54827
55452
|
}
|
|
@@ -54850,7 +55475,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
54850
55475
|
requireViewerLifecyclePermission(grant);
|
|
54851
55476
|
assertOwnershipEnabled();
|
|
54852
55477
|
const sessionId = c.req.param("sessionId");
|
|
54853
|
-
const session = await
|
|
55478
|
+
const session = await getSession8(db, workspaceId, sessionId);
|
|
54854
55479
|
if (!session) {
|
|
54855
55480
|
throw new HTTPException57(404, { message: "session not found" });
|
|
54856
55481
|
}
|
|
@@ -54872,7 +55497,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
54872
55497
|
const grant = await requireAccessGrant30(c, deps, workspaceId, "stream:view");
|
|
54873
55498
|
assertOwnershipEnabled();
|
|
54874
55499
|
const sessionId = c.req.param("sessionId");
|
|
54875
|
-
const session = await
|
|
55500
|
+
const session = await getSession8(db, workspaceId, sessionId);
|
|
54876
55501
|
if (!session) {
|
|
54877
55502
|
throw new HTTPException57(404, { message: "session not found" });
|
|
54878
55503
|
}
|
|
@@ -54894,7 +55519,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
54894
55519
|
const grant = await requireAccessGrant30(c, deps, workspaceId, permission);
|
|
54895
55520
|
assertOwnershipEnabled();
|
|
54896
55521
|
const sessionId = c.req.param("sessionId") ?? "";
|
|
54897
|
-
const session = await
|
|
55522
|
+
const session = await getSession8(db, workspaceId, sessionId);
|
|
54898
55523
|
if (!session) {
|
|
54899
55524
|
throw new HTTPException57(404, { message: "session not found" });
|
|
54900
55525
|
}
|
|
@@ -55093,7 +55718,7 @@ function registerSessionRoutes(app, deps) {
|
|
|
55093
55718
|
message: "path query parameter is required"
|
|
55094
55719
|
});
|
|
55095
55720
|
}
|
|
55096
|
-
const session = await
|
|
55721
|
+
const session = await getSession8(db, workspaceId, sessionId);
|
|
55097
55722
|
if (!session) {
|
|
55098
55723
|
throw new HTTPException57(404, { message: "session not found" });
|
|
55099
55724
|
}
|
|
@@ -55608,7 +56233,7 @@ function eventEnumList(raw, schema, name) {
|
|
|
55608
56233
|
}
|
|
55609
56234
|
function sessionListQuery(query, allowCursor = true) {
|
|
55610
56235
|
const parentSessionId = query.parentSessionId;
|
|
55611
|
-
if (parentSessionId !== void 0 && parentSessionId !== "null" && !
|
|
56236
|
+
if (parentSessionId !== void 0 && parentSessionId !== "null" && !z15.string().uuid().safeParse(parentSessionId).success) {
|
|
55612
56237
|
throw new HTTPException57(400, {
|
|
55613
56238
|
message: 'parentSessionId must be a session id or the literal "null"'
|
|
55614
56239
|
});
|
|
@@ -55634,12 +56259,58 @@ function sessionListQuery(query, allowCursor = true) {
|
|
|
55634
56259
|
throw new HTTPException57(400, { message: 'archivedOnly must be the literal "true"' });
|
|
55635
56260
|
}
|
|
55636
56261
|
const archivedOnly = query.archivedOnly === "true";
|
|
56262
|
+
const channelId = query.channelId;
|
|
56263
|
+
if (channelId !== void 0 && channelId !== "null" && !z15.string().uuid().safeParse(channelId).success) {
|
|
56264
|
+
throw new HTTPException57(400, {
|
|
56265
|
+
message: 'channelId must be a channel id or the literal "null"'
|
|
56266
|
+
});
|
|
56267
|
+
}
|
|
56268
|
+
const createdByKind = query.createdByKind;
|
|
56269
|
+
const createdBySubjectId = query.createdBySubjectId;
|
|
56270
|
+
if (createdByKind === void 0 !== (createdBySubjectId === void 0)) {
|
|
56271
|
+
throw new HTTPException57(400, {
|
|
56272
|
+
message: "createdByKind and createdBySubjectId must be supplied together"
|
|
56273
|
+
});
|
|
56274
|
+
}
|
|
56275
|
+
if (createdByKind !== void 0 && createdByKind !== "subject" && createdByKind !== "service") {
|
|
56276
|
+
throw new HTTPException57(400, {
|
|
56277
|
+
message: 'createdByKind must be "subject" or "service"'
|
|
56278
|
+
});
|
|
56279
|
+
}
|
|
56280
|
+
if (createdBySubjectId !== void 0 && (createdBySubjectId.trim().length < 1 || createdBySubjectId.length > 1024)) {
|
|
56281
|
+
throw new HTTPException57(400, {
|
|
56282
|
+
message: "createdBySubjectId must be between 1 and 1024 characters"
|
|
56283
|
+
});
|
|
56284
|
+
}
|
|
56285
|
+
const parseDateBound = (name) => {
|
|
56286
|
+
const raw = query[name];
|
|
56287
|
+
if (raw === void 0) return void 0;
|
|
56288
|
+
const parsed = z15.string().datetime({ offset: true }).safeParse(raw);
|
|
56289
|
+
if (!parsed.success) {
|
|
56290
|
+
throw new HTTPException57(400, { message: `${name} must be an ISO-8601 timestamp` });
|
|
56291
|
+
}
|
|
56292
|
+
if (/\.\d{4,}/.test(parsed.data)) {
|
|
56293
|
+
throw new HTTPException57(400, { message: `${name} supports at most millisecond precision` });
|
|
56294
|
+
}
|
|
56295
|
+
return new Date(parsed.data);
|
|
56296
|
+
};
|
|
56297
|
+
const updatedFrom = parseDateBound("updatedFrom");
|
|
56298
|
+
const updatedBefore = parseDateBound("updatedBefore");
|
|
56299
|
+
const createdFrom = parseDateBound("createdFrom");
|
|
56300
|
+
const createdBefore = parseDateBound("createdBefore");
|
|
56301
|
+
if (updatedFrom && updatedBefore && updatedFrom >= updatedBefore) {
|
|
56302
|
+
throw new HTTPException57(400, { message: "updatedFrom must be earlier than updatedBefore" });
|
|
56303
|
+
}
|
|
56304
|
+
if (createdFrom && createdBefore && createdFrom >= createdBefore) {
|
|
56305
|
+
throw new HTTPException57(400, { message: "createdFrom must be earlier than createdBefore" });
|
|
56306
|
+
}
|
|
56307
|
+
const hasPageFilters = channelId !== void 0 || createdByKind !== void 0 || updatedFrom !== void 0 || updatedBefore !== void 0 || createdFrom !== void 0 || createdBefore !== void 0;
|
|
55637
56308
|
if (pinsOnly && !allowCursor) {
|
|
55638
56309
|
throw new HTTPException57(400, { message: 'pinsOnly requires view="page"' });
|
|
55639
56310
|
}
|
|
55640
|
-
if (pinsOnly && (rawCursor || parentSessionId !== void 0 || search)) {
|
|
56311
|
+
if (pinsOnly && (rawCursor || parentSessionId !== void 0 || search || hasPageFilters)) {
|
|
55641
56312
|
throw new HTTPException57(400, {
|
|
55642
|
-
message: "pinsOnly cannot be combined with cursor, parentSessionId, or
|
|
56313
|
+
message: "pinsOnly cannot be combined with cursor, parentSessionId, search, or filters"
|
|
55643
56314
|
});
|
|
55644
56315
|
}
|
|
55645
56316
|
if (pinsOnly && archivedOnly) {
|
|
@@ -55651,7 +56322,14 @@ function sessionListQuery(query, allowCursor = true) {
|
|
|
55651
56322
|
cursor,
|
|
55652
56323
|
search: search || void 0,
|
|
55653
56324
|
pinsOnly,
|
|
55654
|
-
archivedOnly
|
|
56325
|
+
archivedOnly,
|
|
56326
|
+
channelId: channelId === void 0 ? void 0 : channelId === "null" ? null : channelId,
|
|
56327
|
+
createdBy: createdByKind && createdBySubjectId ? { kind: createdByKind, subjectId: createdBySubjectId } : void 0,
|
|
56328
|
+
updatedFrom,
|
|
56329
|
+
updatedBefore,
|
|
56330
|
+
createdFrom,
|
|
56331
|
+
createdBefore,
|
|
56332
|
+
hasPageFilters
|
|
55655
56333
|
};
|
|
55656
56334
|
}
|
|
55657
56335
|
function encodeAgentTopologyCursor(value) {
|
|
@@ -55665,18 +56343,18 @@ function decodeAgentTopologyCursor(value) {
|
|
|
55665
56343
|
}
|
|
55666
56344
|
try {
|
|
55667
56345
|
const decoded2 = JSON.parse(Buffer.from(value, "base64url").toString("utf8"));
|
|
55668
|
-
const legacy =
|
|
55669
|
-
v:
|
|
55670
|
-
parentSessionId:
|
|
55671
|
-
search:
|
|
55672
|
-
cursor:
|
|
55673
|
-
orderBy:
|
|
55674
|
-
sortRevision:
|
|
55675
|
-
sortAt:
|
|
55676
|
-
id:
|
|
55677
|
-
snapshotAt:
|
|
55678
|
-
snapshotRevision:
|
|
55679
|
-
updatedAfter:
|
|
56346
|
+
const legacy = z15.object({
|
|
56347
|
+
v: z15.literal(1),
|
|
56348
|
+
parentSessionId: z15.string().uuid().nullable(),
|
|
56349
|
+
search: z15.string().max(WORK_DISCOVERY_QUERY_MAX_CHARS2).nullable(),
|
|
56350
|
+
cursor: z15.object({
|
|
56351
|
+
orderBy: z15.literal("updatedAt"),
|
|
56352
|
+
sortRevision: z15.string().max(64),
|
|
56353
|
+
sortAt: z15.string().max(64),
|
|
56354
|
+
id: z15.string().uuid(),
|
|
56355
|
+
snapshotAt: z15.string().max(64),
|
|
56356
|
+
snapshotRevision: z15.string().max(64),
|
|
56357
|
+
updatedAfter: z15.null()
|
|
55680
56358
|
})
|
|
55681
56359
|
}).safeParse(decoded2);
|
|
55682
56360
|
if (legacy.success) {
|
|
@@ -55700,13 +56378,13 @@ function decodeAgentTopologyCursor(value) {
|
|
|
55700
56378
|
claimLimit: null
|
|
55701
56379
|
};
|
|
55702
56380
|
}
|
|
55703
|
-
const parsed =
|
|
55704
|
-
v:
|
|
55705
|
-
parentSessionId:
|
|
55706
|
-
rootSessionId:
|
|
55707
|
-
query:
|
|
55708
|
-
statuses:
|
|
55709
|
-
|
|
56381
|
+
const parsed = z15.object({
|
|
56382
|
+
v: z15.literal(2),
|
|
56383
|
+
parentSessionId: z15.union([z15.string().uuid(), z15.literal("all"), z15.null()]),
|
|
56384
|
+
rootSessionId: z15.string().uuid().nullable(),
|
|
56385
|
+
query: z15.string().max(WORK_DISCOVERY_QUERY_MAX_CHARS2).nullable(),
|
|
56386
|
+
statuses: z15.array(
|
|
56387
|
+
z15.enum([
|
|
55710
56388
|
"queued",
|
|
55711
56389
|
"running",
|
|
55712
56390
|
"idle",
|
|
@@ -55717,24 +56395,24 @@ function decodeAgentTopologyCursor(value) {
|
|
|
55717
56395
|
"cancelled"
|
|
55718
56396
|
])
|
|
55719
56397
|
).max(8),
|
|
55720
|
-
activeOnly:
|
|
55721
|
-
recentHours:
|
|
55722
|
-
subject:
|
|
55723
|
-
namespace:
|
|
56398
|
+
activeOnly: z15.boolean(),
|
|
56399
|
+
recentHours: z15.number().int().positive().max(WORK_DISCOVERY_RECENT_HOURS_MAX2).nullable(),
|
|
56400
|
+
subject: z15.object({
|
|
56401
|
+
namespace: z15.string().min(1).max(WORK_CLAIM_NAMESPACE_MAX_BYTES2),
|
|
55724
56402
|
type: WorkClaimSubjectType2,
|
|
55725
|
-
canonicalKey:
|
|
56403
|
+
canonicalKey: z15.string().min(1).max(WORK_CLAIM_CANONICAL_KEY_MAX_BYTES2)
|
|
55726
56404
|
}).strict().nullable(),
|
|
55727
|
-
claimLimit:
|
|
55728
|
-
cursor:
|
|
55729
|
-
orderBy:
|
|
55730
|
-
sortRank:
|
|
55731
|
-
sortRevision:
|
|
55732
|
-
sortAt:
|
|
55733
|
-
id:
|
|
55734
|
-
snapshotAt:
|
|
55735
|
-
snapshotRevision:
|
|
55736
|
-
updatedAfter:
|
|
55737
|
-
filterHash:
|
|
56405
|
+
claimLimit: z15.number().int().positive().max(WORK_CLAIM_DISCOVERY_LIMIT2).nullable(),
|
|
56406
|
+
cursor: z15.object({
|
|
56407
|
+
orderBy: z15.enum(["updatedAt", "relevance"]),
|
|
56408
|
+
sortRank: z15.number().int().nonnegative().nullable(),
|
|
56409
|
+
sortRevision: z15.string().max(64),
|
|
56410
|
+
sortAt: z15.string().max(64),
|
|
56411
|
+
id: z15.string().uuid(),
|
|
56412
|
+
snapshotAt: z15.string().max(64),
|
|
56413
|
+
snapshotRevision: z15.string().max(64),
|
|
56414
|
+
updatedAfter: z15.null(),
|
|
56415
|
+
filterHash: z15.string().regex(/^[0-9a-f]{64}$/).nullable()
|
|
55738
56416
|
})
|
|
55739
56417
|
}).parse(decoded2);
|
|
55740
56418
|
if (!/^(?:0|[1-9]\d*)$/.test(parsed.cursor.sortRevision) || !/^(?:0|[1-9]\d*)$/.test(parsed.cursor.snapshotRevision) || BigInt(parsed.cursor.sortRevision) > 9223372036854775807n || BigInt(parsed.cursor.snapshotRevision) > 9223372036854775807n || Number.isNaN(Date.parse(parsed.cursor.sortAt)) || Number.isNaN(Date.parse(parsed.cursor.snapshotAt))) {
|
|
@@ -55759,13 +56437,13 @@ function agentTopologyQuery(query) {
|
|
|
55759
56437
|
});
|
|
55760
56438
|
}
|
|
55761
56439
|
const rawParent = query.parentSessionId;
|
|
55762
|
-
if (rawParent !== void 0 && rawParent !== "null" && !
|
|
56440
|
+
if (rawParent !== void 0 && rawParent !== "null" && !z15.string().uuid().safeParse(rawParent).success) {
|
|
55763
56441
|
throw new HTTPException57(400, {
|
|
55764
56442
|
message: 'parentSessionId must be a session id or the literal "null"'
|
|
55765
56443
|
});
|
|
55766
56444
|
}
|
|
55767
56445
|
const rootSessionId = query.rootSessionId?.trim();
|
|
55768
|
-
if (rootSessionId && !
|
|
56446
|
+
if (rootSessionId && !z15.string().uuid().safeParse(rootSessionId).success) {
|
|
55769
56447
|
throw new HTTPException57(400, { message: "rootSessionId must be a session id" });
|
|
55770
56448
|
}
|
|
55771
56449
|
const normalizeSearchQuery = (value) => {
|
|
@@ -55794,8 +56472,8 @@ function agentTopologyQuery(query) {
|
|
|
55794
56472
|
query.statuses.split(",").map((status) => status.trim()).filter(Boolean)
|
|
55795
56473
|
)
|
|
55796
56474
|
].sort() : [];
|
|
55797
|
-
const parsedStatuses =
|
|
55798
|
-
|
|
56475
|
+
const parsedStatuses = z15.array(
|
|
56476
|
+
z15.enum([
|
|
55799
56477
|
"queued",
|
|
55800
56478
|
"running",
|
|
55801
56479
|
"idle",
|
|
@@ -55838,7 +56516,7 @@ function agentTopologyQuery(query) {
|
|
|
55838
56516
|
throw new HTTPException57(400, { message: "query cannot be combined with an exact subject" });
|
|
55839
56517
|
}
|
|
55840
56518
|
const relevanceRequested = Boolean(searchQuery || subject);
|
|
55841
|
-
const parentSessionId = rawParent === void 0 ? relevanceRequested ? void 0 : null : rawParent === "null" ? null : rawParent;
|
|
56519
|
+
const parentSessionId = rawParent === void 0 ? relevanceRequested || Boolean(rootSessionId) ? void 0 : null : rawParent === "null" ? null : rawParent;
|
|
55842
56520
|
const claimLimit = query.claimLimit === void 0 ? void 0 : Number(query.claimLimit);
|
|
55843
56521
|
if (claimLimit !== void 0 && (!Number.isSafeInteger(claimLimit) || claimLimit < 1 || claimLimit > WORK_CLAIM_DISCOVERY_LIMIT2)) {
|
|
55844
56522
|
throw new HTTPException57(400, {
|
|
@@ -56062,8 +56740,8 @@ import {
|
|
|
56062
56740
|
updateSocialConnectionCredential as updateSocialConnectionCredential2
|
|
56063
56741
|
} from "@opengeni/db";
|
|
56064
56742
|
import { HTTPException as HTTPException58 } from "hono/http-exception";
|
|
56065
|
-
import { z as
|
|
56066
|
-
import { requireAccessGrant as requireAccessGrant31, requireAccessGrantAuthorization as
|
|
56743
|
+
import { z as z16 } from "zod";
|
|
56744
|
+
import { requireAccessGrant as requireAccessGrant31, requireAccessGrantAuthorization as requireAccessGrantAuthorization13 } from "@opengeni/core";
|
|
56067
56745
|
function registerSocialRoutes(app, deps) {
|
|
56068
56746
|
const { db, settings, observability } = deps;
|
|
56069
56747
|
app.get("/v1/workspaces/:workspaceId/social/connections", async (c) => {
|
|
@@ -56133,7 +56811,7 @@ function registerSocialRoutes(app, deps) {
|
|
|
56133
56811
|
});
|
|
56134
56812
|
}
|
|
56135
56813
|
const payload = parsed.data;
|
|
56136
|
-
const access = await
|
|
56814
|
+
const access = await requireAccessGrantAuthorization13(
|
|
56137
56815
|
c,
|
|
56138
56816
|
deps,
|
|
56139
56817
|
workspaceId,
|
|
@@ -56227,7 +56905,7 @@ function parseConnectionIds(raw) {
|
|
|
56227
56905
|
return void 0;
|
|
56228
56906
|
}
|
|
56229
56907
|
const values = raw.split(",").map((value) => value.trim()).filter(Boolean);
|
|
56230
|
-
const parsed =
|
|
56908
|
+
const parsed = z16.array(z16.string().uuid()).safeParse(values);
|
|
56231
56909
|
if (!parsed.success) {
|
|
56232
56910
|
throw new HTTPException58(422, {
|
|
56233
56911
|
message: "connectionIds must be a comma-separated list of UUIDs"
|
|
@@ -56248,7 +56926,7 @@ function socialHttpException(error) {
|
|
|
56248
56926
|
}
|
|
56249
56927
|
|
|
56250
56928
|
// src/routes/workspaces.ts
|
|
56251
|
-
import { createHash as
|
|
56929
|
+
import { createHash as createHash18 } from "crypto";
|
|
56252
56930
|
import {
|
|
56253
56931
|
AddWorkspaceMemberRequest,
|
|
56254
56932
|
CreateWorkspaceRequest,
|
|
@@ -56511,7 +57189,7 @@ function projectWorkspaceOpenRouterCustomModel(model) {
|
|
|
56511
57189
|
});
|
|
56512
57190
|
}
|
|
56513
57191
|
function workspaceCustomModelRequestHash(value) {
|
|
56514
|
-
return
|
|
57192
|
+
return createHash18("sha256").update(stableJson6(value), "utf8").digest("hex");
|
|
56515
57193
|
}
|
|
56516
57194
|
function workspaceMembersResponse(members) {
|
|
56517
57195
|
return ListWorkspaceMembersResponse.parse({
|
|
@@ -57430,8 +58108,8 @@ import {
|
|
|
57430
58108
|
WorkspaceInstructionPolicyOperationReuseError
|
|
57431
58109
|
} from "@opengeni/db";
|
|
57432
58110
|
import { HTTPException as HTTPException61 } from "hono/http-exception";
|
|
57433
|
-
import { z as
|
|
57434
|
-
var WorkspaceInstructionPolicyRevisionId =
|
|
58111
|
+
import { z as z17 } from "zod";
|
|
58112
|
+
var WorkspaceInstructionPolicyRevisionId = z17.string().uuid();
|
|
57435
58113
|
async function parseBody(context, schema) {
|
|
57436
58114
|
const parsed = schema.safeParse(await context.req.json().catch(() => null));
|
|
57437
58115
|
if (!parsed.success) {
|
|
@@ -57763,7 +58441,7 @@ import {
|
|
|
57763
58441
|
WorkspaceLearningPolicyOperationReuseError
|
|
57764
58442
|
} from "@opengeni/db";
|
|
57765
58443
|
import { HTTPException as HTTPException62 } from "hono/http-exception";
|
|
57766
|
-
import { z as
|
|
58444
|
+
import { z as z18 } from "zod";
|
|
57767
58445
|
async function parseBody2(context, schema) {
|
|
57768
58446
|
const parsed = schema.safeParse(await context.req.json().catch(() => null));
|
|
57769
58447
|
if (!parsed.success) {
|
|
@@ -57859,7 +58537,7 @@ function registerWorkspaceLearningRoutes(app, deps) {
|
|
|
57859
58537
|
});
|
|
57860
58538
|
app.post(`${base}/revisions/:revisionId/activate`, async (context) => {
|
|
57861
58539
|
const workspaceId = context.req.param("workspaceId");
|
|
57862
|
-
const revisionId2 =
|
|
58540
|
+
const revisionId2 = z18.string().uuid().safeParse(context.req.param("revisionId"));
|
|
57863
58541
|
if (!revisionId2.success) throw new HTTPException62(422, { message: "Invalid revision id" });
|
|
57864
58542
|
const grant = await requireAccessGrant34(context, deps, workspaceId, "workspace:admin");
|
|
57865
58543
|
const request = await parseBody2(context, ActivateWorkspaceLearningPolicyRevisionRequest);
|
|
@@ -57909,7 +58587,7 @@ function registerWorkspaceLearningRoutes(app, deps) {
|
|
|
57909
58587
|
});
|
|
57910
58588
|
app.post(`${base}/activations/:activationReceiptId/undo`, async (context) => {
|
|
57911
58589
|
const workspaceId = context.req.param("workspaceId");
|
|
57912
|
-
const activationReceiptId =
|
|
58590
|
+
const activationReceiptId = z18.string().uuid().safeParse(context.req.param("activationReceiptId"));
|
|
57913
58591
|
if (!activationReceiptId.success) {
|
|
57914
58592
|
throw new HTTPException62(422, { message: "Invalid activation receipt id" });
|
|
57915
58593
|
}
|
|
@@ -57952,7 +58630,7 @@ import {
|
|
|
57952
58630
|
} from "@opengeni/contracts";
|
|
57953
58631
|
import {
|
|
57954
58632
|
requireAccessGrant as requireAccessGrant35,
|
|
57955
|
-
requireAccessGrantAuthorization as
|
|
58633
|
+
requireAccessGrantAuthorization as requireAccessGrantAuthorization14
|
|
57956
58634
|
} from "@opengeni/core";
|
|
57957
58635
|
import {
|
|
57958
58636
|
activateCompanyProfileRevision,
|
|
@@ -57970,8 +58648,8 @@ import {
|
|
|
57970
58648
|
updateCompanyProfileAgentPolicy
|
|
57971
58649
|
} from "@opengeni/db";
|
|
57972
58650
|
import { HTTPException as HTTPException63 } from "hono/http-exception";
|
|
57973
|
-
import { z as
|
|
57974
|
-
var RevisionId =
|
|
58651
|
+
import { z as z19 } from "zod";
|
|
58652
|
+
var RevisionId = z19.string().uuid();
|
|
57975
58653
|
async function parseBody3(context, schema) {
|
|
57976
58654
|
const parsed = schema.safeParse(await context.req.json().catch(() => null));
|
|
57977
58655
|
if (!parsed.success) throw new HTTPException63(422, { message: "Invalid company-profile request" });
|
|
@@ -58032,7 +58710,7 @@ function registerCompanyProfileRoutes(app, deps) {
|
|
|
58032
58710
|
const base = "/v1/workspaces/:workspaceId/company-profile";
|
|
58033
58711
|
app.get(`${base}/agent-policy`, async (context) => {
|
|
58034
58712
|
const workspaceId = context.req.param("workspaceId");
|
|
58035
|
-
const access = await
|
|
58713
|
+
const access = await requireAccessGrantAuthorization14(
|
|
58036
58714
|
context,
|
|
58037
58715
|
deps,
|
|
58038
58716
|
workspaceId,
|
|
@@ -58055,7 +58733,7 @@ function registerCompanyProfileRoutes(app, deps) {
|
|
|
58055
58733
|
});
|
|
58056
58734
|
app.patch(`${base}/agent-policy`, async (context) => {
|
|
58057
58735
|
const workspaceId = context.req.param("workspaceId");
|
|
58058
|
-
const access = await
|
|
58736
|
+
const access = await requireAccessGrantAuthorization14(
|
|
58059
58737
|
context,
|
|
58060
58738
|
deps,
|
|
58061
58739
|
workspaceId,
|
|
@@ -58099,7 +58777,7 @@ function registerCompanyProfileRoutes(app, deps) {
|
|
|
58099
58777
|
});
|
|
58100
58778
|
app.put(base, async (context) => {
|
|
58101
58779
|
const workspaceId = context.req.param("workspaceId");
|
|
58102
|
-
const access = await
|
|
58780
|
+
const access = await requireAccessGrantAuthorization14(
|
|
58103
58781
|
context,
|
|
58104
58782
|
deps,
|
|
58105
58783
|
workspaceId,
|
|
@@ -58151,7 +58829,7 @@ function registerCompanyProfileRoutes(app, deps) {
|
|
|
58151
58829
|
});
|
|
58152
58830
|
app.post(`${base}/rollback`, async (context) => {
|
|
58153
58831
|
const workspaceId = context.req.param("workspaceId");
|
|
58154
|
-
const access = await
|
|
58832
|
+
const access = await requireAccessGrantAuthorization14(
|
|
58155
58833
|
context,
|
|
58156
58834
|
deps,
|
|
58157
58835
|
workspaceId,
|
|
@@ -58198,7 +58876,7 @@ function registerCompanyProfileRoutes(app, deps) {
|
|
|
58198
58876
|
});
|
|
58199
58877
|
app.post(`${base}/revisions/:revisionId/activate`, async (context) => {
|
|
58200
58878
|
const workspaceId = context.req.param("workspaceId");
|
|
58201
|
-
const access = await
|
|
58879
|
+
const access = await requireAccessGrantAuthorization14(
|
|
58202
58880
|
context,
|
|
58203
58881
|
deps,
|
|
58204
58882
|
workspaceId,
|
|
@@ -58229,7 +58907,7 @@ function registerCompanyProfileRoutes(app, deps) {
|
|
|
58229
58907
|
}
|
|
58230
58908
|
|
|
58231
58909
|
// src/routes/company-brain.ts
|
|
58232
|
-
import { createHash as
|
|
58910
|
+
import { createHash as createHash20 } from "crypto";
|
|
58233
58911
|
import {
|
|
58234
58912
|
COMPANY_BRAIN_INSPECTOR_CURSOR_MAX_CHARS,
|
|
58235
58913
|
COMPANY_BRAIN_INSPECTOR_DEFAULT_LIMIT,
|
|
@@ -58264,7 +58942,7 @@ import {
|
|
|
58264
58942
|
searchEffectiveKnowledge as searchEffectiveKnowledge2
|
|
58265
58943
|
} from "@opengeni/documents";
|
|
58266
58944
|
import { HTTPException as HTTPException64 } from "hono/http-exception";
|
|
58267
|
-
import { z as
|
|
58945
|
+
import { z as z20 } from "zod";
|
|
58268
58946
|
|
|
58269
58947
|
// src/company-brain-okf.ts
|
|
58270
58948
|
import {
|
|
@@ -58506,7 +59184,7 @@ function serializeCompanyBrainOkf(value) {
|
|
|
58506
59184
|
}
|
|
58507
59185
|
|
|
58508
59186
|
// src/workspace-state-projection.ts
|
|
58509
|
-
import { createHash as
|
|
59187
|
+
import { createHash as createHash19 } from "crypto";
|
|
58510
59188
|
import {
|
|
58511
59189
|
KnowledgeMemoryKind,
|
|
58512
59190
|
KnowledgeMemoryStatus,
|
|
@@ -58520,15 +59198,15 @@ import {
|
|
|
58520
59198
|
WorkspaceStateResponse
|
|
58521
59199
|
} from "@opengeni/contracts";
|
|
58522
59200
|
function hashIdentities(values) {
|
|
58523
|
-
return
|
|
59201
|
+
return createHash19("sha256").update(values.join("\n"), "utf8").digest("hex");
|
|
58524
59202
|
}
|
|
58525
59203
|
function policyTargetKey(value) {
|
|
58526
59204
|
return `${value.kind}:${value.scope}:${value.roleKey ?? ""}`;
|
|
58527
59205
|
}
|
|
58528
59206
|
function policyTargetKeysForRole(policyRole) {
|
|
58529
|
-
const
|
|
58530
|
-
if (policyRole !== null)
|
|
58531
|
-
return
|
|
59207
|
+
const keys2 = /* @__PURE__ */ new Set(["charter:global:", "policy:global:"]);
|
|
59208
|
+
if (policyRole !== null) keys2.add(`policy:role:${policyRole}`);
|
|
59209
|
+
return keys2;
|
|
58532
59210
|
}
|
|
58533
59211
|
function policyIdentity(value) {
|
|
58534
59212
|
return `${policyTargetKey(value)}:${value.revisionId}:${value.contentHash}:${value.activationVersion}`;
|
|
@@ -58855,15 +59533,15 @@ function projectWorkspaceState(input) {
|
|
|
58855
59533
|
var BASE_LIMIT = 24;
|
|
58856
59534
|
var TOPIC_LIMIT = 24;
|
|
58857
59535
|
var TOPIC_MAX_CHARS = 96;
|
|
58858
|
-
var inspectorQuery =
|
|
58859
|
-
attemptId:
|
|
58860
|
-
cursor:
|
|
58861
|
-
limit:
|
|
59536
|
+
var inspectorQuery = z20.object({
|
|
59537
|
+
attemptId: z20.string().uuid().optional(),
|
|
59538
|
+
cursor: z20.string().min(1).max(COMPANY_BRAIN_INSPECTOR_CURSOR_MAX_CHARS).optional(),
|
|
59539
|
+
limit: z20.coerce.number().int().positive().max(COMPANY_BRAIN_INSPECTOR_MAX_LIMIT).default(COMPANY_BRAIN_INSPECTOR_DEFAULT_LIMIT)
|
|
58862
59540
|
}).strict().refine((value) => !(value.attemptId && value.cursor), {
|
|
58863
59541
|
message: "attemptId cannot be combined with cursor"
|
|
58864
59542
|
});
|
|
58865
59543
|
function receiptCursorScope(workspaceId, subjectId) {
|
|
58866
|
-
return
|
|
59544
|
+
return createHash20("sha256").update("opengeni:company-brain-context-receipts:v1\0").update(workspaceId).update("\0").update(subjectId).digest("hex");
|
|
58867
59545
|
}
|
|
58868
59546
|
function encodeReceiptCursor(workspaceId, subjectId, cursor) {
|
|
58869
59547
|
return Buffer.from(
|
|
@@ -58875,11 +59553,11 @@ function decodeReceiptCursor(value, workspaceId, subjectId) {
|
|
|
58875
59553
|
try {
|
|
58876
59554
|
const bytes = Buffer.from(value, "base64url");
|
|
58877
59555
|
if (bytes.toString("base64url") !== value) throw new Error("encoding");
|
|
58878
|
-
const decoded2 =
|
|
58879
|
-
v:
|
|
58880
|
-
s:
|
|
58881
|
-
createdAt:
|
|
58882
|
-
id:
|
|
59556
|
+
const decoded2 = z20.object({
|
|
59557
|
+
v: z20.literal(1),
|
|
59558
|
+
s: z20.string().regex(/^[0-9a-f]{64}$/u),
|
|
59559
|
+
createdAt: z20.string().datetime(),
|
|
59560
|
+
id: z20.string().uuid()
|
|
58883
59561
|
}).strict().parse(JSON.parse(bytes.toString("utf8")));
|
|
58884
59562
|
if (decoded2.s !== receiptCursorScope(workspaceId, subjectId)) {
|
|
58885
59563
|
throw new HTTPException64(409, { message: "context receipt cursor belongs to another scope" });
|
|
@@ -59101,7 +59779,7 @@ function registerCompanyBrainRoutes(app, deps) {
|
|
|
59101
59779
|
app.get(`${base}/knowledge-proposals`, async (context) => {
|
|
59102
59780
|
const workspaceId = context.req.param("workspaceId");
|
|
59103
59781
|
const grant = await requireAccessGrant36(context, deps, workspaceId, "documents:search");
|
|
59104
|
-
const parsed =
|
|
59782
|
+
const parsed = z20.coerce.number().int().positive().max(COMPANY_BRAIN_INSPECTOR_MAX_LIMIT).default(COMPANY_BRAIN_INSPECTOR_DEFAULT_LIMIT).safeParse(context.req.query("limit"));
|
|
59105
59783
|
if (!parsed.success) throw new HTTPException64(400, { message: "invalid proposal limit" });
|
|
59106
59784
|
const response = await listCompanyBrainKnowledgeProposals(deps.db, {
|
|
59107
59785
|
workspaceId,
|
|
@@ -59122,7 +59800,7 @@ import {
|
|
|
59122
59800
|
} from "@opengeni/contracts";
|
|
59123
59801
|
import {
|
|
59124
59802
|
requireAccessGrant as requireAccessGrant37,
|
|
59125
|
-
requireAccessGrantAuthorization as
|
|
59803
|
+
requireAccessGrantAuthorization as requireAccessGrantAuthorization15
|
|
59126
59804
|
} from "@opengeni/core";
|
|
59127
59805
|
import {
|
|
59128
59806
|
listSlackTaskPolicy,
|
|
@@ -59181,7 +59859,7 @@ function registerSlackTaskPolicyRoutes(app, deps) {
|
|
|
59181
59859
|
});
|
|
59182
59860
|
app.put(base, async (context) => {
|
|
59183
59861
|
const workspaceId = context.req.param("workspaceId");
|
|
59184
|
-
const access = await
|
|
59862
|
+
const access = await requireAccessGrantAuthorization15(
|
|
59185
59863
|
context,
|
|
59186
59864
|
deps,
|
|
59187
59865
|
workspaceId,
|
|
@@ -59235,7 +59913,7 @@ import { getDocumentInventory as getDocumentInventory2 } from "@opengeni/documen
|
|
|
59235
59913
|
import { HTTPException as HTTPException66 } from "hono/http-exception";
|
|
59236
59914
|
|
|
59237
59915
|
// src/workspace-state-export.ts
|
|
59238
|
-
import { createHash as
|
|
59916
|
+
import { createHash as createHash21 } from "crypto";
|
|
59239
59917
|
import {
|
|
59240
59918
|
WORKSPACE_STATE_EXPORT_SCHEMA_VERSION,
|
|
59241
59919
|
WorkspaceStateExportResponse
|
|
@@ -59266,7 +59944,7 @@ function createWorkspaceStateExport(state) {
|
|
|
59266
59944
|
kind: "opengeni.workspace_state.sanitized_export",
|
|
59267
59945
|
schemaVersion: WORKSPACE_STATE_EXPORT_SCHEMA_VERSION,
|
|
59268
59946
|
generatedAt: state.generatedAt,
|
|
59269
|
-
stateSha256:
|
|
59947
|
+
stateSha256: createHash21("sha256").update(canonicalState, "utf8").digest("hex"),
|
|
59270
59948
|
omissions: WORKSPACE_STATE_EXPORT_OMISSIONS,
|
|
59271
59949
|
state
|
|
59272
59950
|
});
|
|
@@ -59366,7 +60044,7 @@ function registerWorkspaceStateRoutes(app, deps) {
|
|
|
59366
60044
|
}
|
|
59367
60045
|
|
|
59368
60046
|
// src/routes/workspace-artifacts.ts
|
|
59369
|
-
import { createHash as
|
|
60047
|
+
import { createHash as createHash22 } from "crypto";
|
|
59370
60048
|
import {
|
|
59371
60049
|
CreateWorkspaceArtifactRequest,
|
|
59372
60050
|
PublishWorkspaceArtifactVersionRequest,
|
|
@@ -59381,7 +60059,7 @@ import {
|
|
|
59381
60059
|
} from "@opengeni/contracts";
|
|
59382
60060
|
import {
|
|
59383
60061
|
requireAccessGrant as requireAccessGrant39,
|
|
59384
|
-
requireAccessGrantAuthorization as
|
|
60062
|
+
requireAccessGrantAuthorization as requireAccessGrantAuthorization16,
|
|
59385
60063
|
requireSessionAuthorization as requireSessionAuthorization8,
|
|
59386
60064
|
SessionAuthorizationDeniedError as SessionAuthorizationDeniedError9,
|
|
59387
60065
|
SessionAuthorizationUnavailableError as SessionAuthorizationUnavailableError7
|
|
@@ -59397,16 +60075,22 @@ import {
|
|
|
59397
60075
|
setWorkspaceArtifactStatus as setWorkspaceArtifactStatus2,
|
|
59398
60076
|
WorkspaceArtifactConflictError,
|
|
59399
60077
|
WorkspaceArtifactNotFoundError as WorkspaceArtifactNotFoundError2,
|
|
59400
|
-
WorkspaceArtifactOperationError
|
|
60078
|
+
WorkspaceArtifactOperationError as WorkspaceArtifactOperationError2
|
|
59401
60079
|
} from "@opengeni/db";
|
|
59402
60080
|
import { HTTPException as HTTPException67 } from "hono/http-exception";
|
|
59403
|
-
import { z as
|
|
59404
|
-
var ArtifactId3 =
|
|
60081
|
+
import { z as z21 } from "zod";
|
|
60082
|
+
var ArtifactId3 = z21.string().uuid();
|
|
59405
60083
|
async function body(context, schema) {
|
|
59406
60084
|
const parsed = schema.safeParse(await context.req.json().catch(() => null));
|
|
59407
60085
|
if (!parsed.success) throw new HTTPException67(422, { message: "Invalid artifact request" });
|
|
59408
60086
|
return parsed.data;
|
|
59409
60087
|
}
|
|
60088
|
+
function parseVersionId(value) {
|
|
60089
|
+
if (value !== void 0 && !ArtifactId3.safeParse(value).success) {
|
|
60090
|
+
throw new HTTPException67(422, { message: "Invalid artifact version id" });
|
|
60091
|
+
}
|
|
60092
|
+
return value;
|
|
60093
|
+
}
|
|
59410
60094
|
function artifactId(context) {
|
|
59411
60095
|
const parsed = ArtifactId3.safeParse(context.req.param("artifactId"));
|
|
59412
60096
|
if (!parsed.success) throw new HTTPException67(422, { message: "Invalid artifact id" });
|
|
@@ -59426,7 +60110,7 @@ function workspaceArtifactErrorResponse(context, error) {
|
|
|
59426
60110
|
409
|
|
59427
60111
|
);
|
|
59428
60112
|
}
|
|
59429
|
-
if (error instanceof
|
|
60113
|
+
if (error instanceof WorkspaceArtifactOperationError2) {
|
|
59430
60114
|
return context.json(
|
|
59431
60115
|
{ code: "INVALID_WORKSPACE_ARTIFACT_OPERATION", message: error.message },
|
|
59432
60116
|
422
|
|
@@ -59443,16 +60127,16 @@ function workspaceArtifactErrorResponse(context, error) {
|
|
|
59443
60127
|
}
|
|
59444
60128
|
throw error;
|
|
59445
60129
|
}
|
|
59446
|
-
function prepareContent(deps,
|
|
60130
|
+
function prepareContent(deps, grant, input) {
|
|
59447
60131
|
if (!deps.objectStorage)
|
|
59448
60132
|
throw new HTTPException67(503, {
|
|
59449
60133
|
message: "Object storage is not configured"
|
|
59450
60134
|
});
|
|
59451
|
-
return
|
|
60135
|
+
return prepareWorkspaceArtifactPublication(deps, grant, input);
|
|
59452
60136
|
}
|
|
59453
60137
|
function provenance(subjectId, idempotencyKey) {
|
|
59454
60138
|
return {
|
|
59455
|
-
operationKey: `subject:${
|
|
60139
|
+
operationKey: `subject:${createHash22("sha256").update(`${subjectId}:${idempotencyKey}`).digest("hex")}`,
|
|
59456
60140
|
actorSubjectId: subjectId,
|
|
59457
60141
|
sourceSessionId: null,
|
|
59458
60142
|
sourceTurnId: null,
|
|
@@ -59489,6 +60173,28 @@ async function mutationResponse(deps, grant, response) {
|
|
|
59489
60173
|
}
|
|
59490
60174
|
function registerWorkspaceArtifactRoutes(app, deps) {
|
|
59491
60175
|
const base = "/v1/workspaces/:workspaceId/published-artifacts";
|
|
60176
|
+
app.post(`${base}/uploads`, async (context) => {
|
|
60177
|
+
const grant = await requireAccessGrant39(
|
|
60178
|
+
context,
|
|
60179
|
+
deps,
|
|
60180
|
+
context.req.param("workspaceId"),
|
|
60181
|
+
"artifacts:publish"
|
|
60182
|
+
);
|
|
60183
|
+
return context.json(await prepareWorkspaceArtifactUpload(deps, grant));
|
|
60184
|
+
});
|
|
60185
|
+
app.get(`${base}/:artifactId/downloads`, async (context) => {
|
|
60186
|
+
const workspaceId = context.req.param("workspaceId");
|
|
60187
|
+
await requireAccessGrant39(context, deps, workspaceId, "artifacts:read");
|
|
60188
|
+
if (!deps.objectStorage)
|
|
60189
|
+
throw new HTTPException67(503, { message: "Object storage is not configured" });
|
|
60190
|
+
const ref = await getWorkspaceArtifactContentRef2(
|
|
60191
|
+
deps.db,
|
|
60192
|
+
workspaceId,
|
|
60193
|
+
artifactId(context),
|
|
60194
|
+
parseVersionId(context.req.query("versionId"))
|
|
60195
|
+
);
|
|
60196
|
+
return context.json(await workspaceArtifactDownloads(deps.objectStorage, ref, "public"));
|
|
60197
|
+
});
|
|
59492
60198
|
app.get(base, async (context) => {
|
|
59493
60199
|
const workspaceId = context.req.param("workspaceId");
|
|
59494
60200
|
await requireAccessGrant39(context, deps, workspaceId, "artifacts:read");
|
|
@@ -59516,7 +60222,7 @@ function registerWorkspaceArtifactRoutes(app, deps) {
|
|
|
59516
60222
|
});
|
|
59517
60223
|
app.post(base, async (context) => {
|
|
59518
60224
|
const workspaceId = context.req.param("workspaceId");
|
|
59519
|
-
const authorization = await
|
|
60225
|
+
const authorization = await requireAccessGrantAuthorization16(
|
|
59520
60226
|
context,
|
|
59521
60227
|
deps,
|
|
59522
60228
|
workspaceId,
|
|
@@ -59527,12 +60233,13 @@ function registerWorkspaceArtifactRoutes(app, deps) {
|
|
|
59527
60233
|
const id = crypto.randomUUID();
|
|
59528
60234
|
const slugBase = request.slug ?? (normalizeWorkspaceArtifactSlug2(request.title) || "artifact");
|
|
59529
60235
|
const slug = request.slug ?? `${slugBase.slice(0, 87)}-${id.slice(0, 8)}`;
|
|
59530
|
-
const content = prepareContent(deps, workspaceId, {
|
|
59531
|
-
html: request.html,
|
|
59532
|
-
...request.source ? { source: request.source } : {},
|
|
59533
|
-
...request.requestedTools ? { requestedTools: request.requestedTools } : {}
|
|
59534
|
-
});
|
|
59535
60236
|
try {
|
|
60237
|
+
const content = await prepareContent(deps, grant, {
|
|
60238
|
+
...request.html !== void 0 ? { html: request.html } : {},
|
|
60239
|
+
...request.uploadId ? { uploadId: request.uploadId } : {},
|
|
60240
|
+
...request.source ? { source: request.source } : {},
|
|
60241
|
+
...request.requestedTools ? { requestedTools: request.requestedTools } : {}
|
|
60242
|
+
});
|
|
59536
60243
|
return context.json(
|
|
59537
60244
|
await mutationResponse(
|
|
59538
60245
|
deps,
|
|
@@ -59572,6 +60279,42 @@ function registerWorkspaceArtifactRoutes(app, deps) {
|
|
|
59572
60279
|
return workspaceArtifactErrorResponse(context, error);
|
|
59573
60280
|
}
|
|
59574
60281
|
});
|
|
60282
|
+
app.get(`${base}/:artifactId/html`, async (context) => {
|
|
60283
|
+
const workspaceId = context.req.param("workspaceId");
|
|
60284
|
+
await requireAccessGrant39(context, deps, workspaceId, "artifacts:read");
|
|
60285
|
+
if (!deps.objectStorage)
|
|
60286
|
+
throw new HTTPException67(503, { message: "Object storage is not configured" });
|
|
60287
|
+
const ref = await getWorkspaceArtifactContentRef2(
|
|
60288
|
+
deps.db,
|
|
60289
|
+
workspaceId,
|
|
60290
|
+
artifactId(context),
|
|
60291
|
+
parseVersionId(context.req.query("versionId"))
|
|
60292
|
+
);
|
|
60293
|
+
const iterator = readArtifactObject(deps.objectStorage, ref.contentKey);
|
|
60294
|
+
return new Response(
|
|
60295
|
+
new ReadableStream({
|
|
60296
|
+
async pull(controller) {
|
|
60297
|
+
try {
|
|
60298
|
+
const item = await iterator.next();
|
|
60299
|
+
if (item.done) controller.close();
|
|
60300
|
+
else controller.enqueue(item.value);
|
|
60301
|
+
} catch (error) {
|
|
60302
|
+
controller.error(error);
|
|
60303
|
+
}
|
|
60304
|
+
},
|
|
60305
|
+
async cancel() {
|
|
60306
|
+
await iterator.return();
|
|
60307
|
+
}
|
|
60308
|
+
}),
|
|
60309
|
+
{
|
|
60310
|
+
headers: {
|
|
60311
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
60312
|
+
"Cache-Control": "private, no-store",
|
|
60313
|
+
"Content-Security-Policy": "sandbox allow-scripts"
|
|
60314
|
+
}
|
|
60315
|
+
}
|
|
60316
|
+
);
|
|
60317
|
+
});
|
|
59575
60318
|
app.get(`${base}/:artifactId/content`, async (context) => {
|
|
59576
60319
|
const workspaceId = context.req.param("workspaceId");
|
|
59577
60320
|
await requireAccessGrant39(context, deps, workspaceId, "artifacts:read");
|
|
@@ -59579,10 +60322,7 @@ function registerWorkspaceArtifactRoutes(app, deps) {
|
|
|
59579
60322
|
throw new HTTPException67(503, {
|
|
59580
60323
|
message: "Object storage is not configured"
|
|
59581
60324
|
});
|
|
59582
|
-
const parsedVersion = context.req.query("versionId");
|
|
59583
|
-
if (parsedVersion && !ArtifactId3.safeParse(parsedVersion).success) {
|
|
59584
|
-
throw new HTTPException67(422, { message: "Invalid artifact version id" });
|
|
59585
|
-
}
|
|
60325
|
+
const parsedVersion = parseVersionId(context.req.query("versionId"));
|
|
59586
60326
|
try {
|
|
59587
60327
|
const ref = await getWorkspaceArtifactContentRef2(
|
|
59588
60328
|
deps.db,
|
|
@@ -59614,7 +60354,7 @@ function registerWorkspaceArtifactRoutes(app, deps) {
|
|
|
59614
60354
|
});
|
|
59615
60355
|
app.post(`${base}/:artifactId/versions`, async (context) => {
|
|
59616
60356
|
const workspaceId = context.req.param("workspaceId");
|
|
59617
|
-
const authorization = await
|
|
60357
|
+
const authorization = await requireAccessGrantAuthorization16(
|
|
59618
60358
|
context,
|
|
59619
60359
|
deps,
|
|
59620
60360
|
workspaceId,
|
|
@@ -59623,12 +60363,13 @@ function registerWorkspaceArtifactRoutes(app, deps) {
|
|
|
59623
60363
|
const grant = authorization.grant;
|
|
59624
60364
|
const request = await body(context, PublishWorkspaceArtifactVersionRequest);
|
|
59625
60365
|
const id = artifactId(context);
|
|
59626
|
-
const content = prepareContent(deps, workspaceId, {
|
|
59627
|
-
html: request.html,
|
|
59628
|
-
...request.source ? { source: request.source } : {},
|
|
59629
|
-
...request.requestedTools ? { requestedTools: request.requestedTools } : {}
|
|
59630
|
-
});
|
|
59631
60366
|
try {
|
|
60367
|
+
const content = await prepareContent(deps, grant, {
|
|
60368
|
+
...request.html !== void 0 ? { html: request.html } : {},
|
|
60369
|
+
...request.uploadId ? { uploadId: request.uploadId } : {},
|
|
60370
|
+
...request.source ? { source: request.source } : {},
|
|
60371
|
+
...request.requestedTools ? { requestedTools: request.requestedTools } : {}
|
|
60372
|
+
});
|
|
59632
60373
|
return context.json(
|
|
59633
60374
|
await mutationResponse(
|
|
59634
60375
|
deps,
|
|
@@ -59651,7 +60392,7 @@ function registerWorkspaceArtifactRoutes(app, deps) {
|
|
|
59651
60392
|
});
|
|
59652
60393
|
app.post(`${base}/:artifactId/rollback`, async (context) => {
|
|
59653
60394
|
const workspaceId = context.req.param("workspaceId");
|
|
59654
|
-
const authorization = await
|
|
60395
|
+
const authorization = await requireAccessGrantAuthorization16(
|
|
59655
60396
|
context,
|
|
59656
60397
|
deps,
|
|
59657
60398
|
workspaceId,
|
|
@@ -59682,7 +60423,7 @@ function registerWorkspaceArtifactRoutes(app, deps) {
|
|
|
59682
60423
|
});
|
|
59683
60424
|
app.patch(`${base}/:artifactId/status`, async (context) => {
|
|
59684
60425
|
const workspaceId = context.req.param("workspaceId");
|
|
59685
|
-
const authorization = await
|
|
60426
|
+
const authorization = await requireAccessGrantAuthorization16(
|
|
59686
60427
|
context,
|
|
59687
60428
|
deps,
|
|
59688
60429
|
workspaceId,
|
|
@@ -59735,7 +60476,7 @@ import {
|
|
|
59735
60476
|
import {
|
|
59736
60477
|
hasPermission as hasPermission23,
|
|
59737
60478
|
requireAccessGrant as requireAccessGrant40,
|
|
59738
|
-
requireAccessGrantAuthorization as
|
|
60479
|
+
requireAccessGrantAuthorization as requireAccessGrantAuthorization17
|
|
59739
60480
|
} from "@opengeni/core";
|
|
59740
60481
|
import {
|
|
59741
60482
|
activatePreferenceRegistryRevision,
|
|
@@ -59758,8 +60499,8 @@ import {
|
|
|
59758
60499
|
supersedePreferenceRegistry
|
|
59759
60500
|
} from "@opengeni/db";
|
|
59760
60501
|
import { HTTPException as HTTPException68 } from "hono/http-exception";
|
|
59761
|
-
import { z as
|
|
59762
|
-
var Id =
|
|
60502
|
+
import { z as z22 } from "zod";
|
|
60503
|
+
var Id = z22.string().uuid();
|
|
59763
60504
|
async function parseBody4(context, schema) {
|
|
59764
60505
|
const parsed = schema.safeParse(await context.req.json().catch(() => null));
|
|
59765
60506
|
if (!parsed.success)
|
|
@@ -59883,7 +60624,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
|
|
|
59883
60624
|
});
|
|
59884
60625
|
app.post(`${base}/proposals`, async (context) => {
|
|
59885
60626
|
const workspaceId = context.req.param("workspaceId");
|
|
59886
|
-
const access = await
|
|
60627
|
+
const access = await requireAccessGrantAuthorization17(
|
|
59887
60628
|
context,
|
|
59888
60629
|
deps,
|
|
59889
60630
|
workspaceId,
|
|
@@ -59963,7 +60704,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
|
|
|
59963
60704
|
});
|
|
59964
60705
|
app.post(`${base}/:preferenceId/activate`, async (context) => {
|
|
59965
60706
|
const workspaceId = context.req.param("workspaceId");
|
|
59966
|
-
const access = await
|
|
60707
|
+
const access = await requireAccessGrantAuthorization17(
|
|
59967
60708
|
context,
|
|
59968
60709
|
deps,
|
|
59969
60710
|
workspaceId,
|
|
@@ -59992,7 +60733,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
|
|
|
59992
60733
|
});
|
|
59993
60734
|
app.post(`${base}/:preferenceId/correct`, async (context) => {
|
|
59994
60735
|
const workspaceId = context.req.param("workspaceId");
|
|
59995
|
-
const access = await
|
|
60736
|
+
const access = await requireAccessGrantAuthorization17(
|
|
59996
60737
|
context,
|
|
59997
60738
|
deps,
|
|
59998
60739
|
workspaceId,
|
|
@@ -60021,7 +60762,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
|
|
|
60021
60762
|
});
|
|
60022
60763
|
app.post(`${base}/:preferenceId/scope`, async (context) => {
|
|
60023
60764
|
const workspaceId = context.req.param("workspaceId");
|
|
60024
|
-
const access = await
|
|
60765
|
+
const access = await requireAccessGrantAuthorization17(
|
|
60025
60766
|
context,
|
|
60026
60767
|
deps,
|
|
60027
60768
|
workspaceId,
|
|
@@ -60050,7 +60791,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
|
|
|
60050
60791
|
});
|
|
60051
60792
|
app.post(`${base}/:preferenceId/deactivate`, async (context) => {
|
|
60052
60793
|
const workspaceId = context.req.param("workspaceId");
|
|
60053
|
-
const access = await
|
|
60794
|
+
const access = await requireAccessGrantAuthorization17(
|
|
60054
60795
|
context,
|
|
60055
60796
|
deps,
|
|
60056
60797
|
workspaceId,
|
|
@@ -60079,7 +60820,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
|
|
|
60079
60820
|
});
|
|
60080
60821
|
app.post(`${base}/:preferenceId/supersede`, async (context) => {
|
|
60081
60822
|
const workspaceId = context.req.param("workspaceId");
|
|
60082
|
-
const access = await
|
|
60823
|
+
const access = await requireAccessGrantAuthorization17(
|
|
60083
60824
|
context,
|
|
60084
60825
|
deps,
|
|
60085
60826
|
workspaceId,
|
|
@@ -60108,7 +60849,7 @@ function registerPreferenceRegistryRoutes(app, deps) {
|
|
|
60108
60849
|
});
|
|
60109
60850
|
app.post(`${base}/:preferenceId/reject`, async (context) => {
|
|
60110
60851
|
const workspaceId = context.req.param("workspaceId");
|
|
60111
|
-
const access = await
|
|
60852
|
+
const access = await requireAccessGrantAuthorization17(
|
|
60112
60853
|
context,
|
|
60113
60854
|
deps,
|
|
60114
60855
|
workspaceId,
|
|
@@ -60208,7 +60949,7 @@ import { requireAccessGrant as requireAccessGrant43, TranscriptionServiceError a
|
|
|
60208
60949
|
import { getWorkspace as getWorkspace5 } from "@opengeni/db";
|
|
60209
60950
|
|
|
60210
60951
|
// src/routes/transcription-recordings.ts
|
|
60211
|
-
import { createHash as
|
|
60952
|
+
import { createHash as createHash23 } from "crypto";
|
|
60212
60953
|
import {
|
|
60213
60954
|
CreateTranscriptionRecordingRequest,
|
|
60214
60955
|
FinalizeTranscriptionRecordingRequest,
|
|
@@ -60564,8 +61305,8 @@ function registerResumableTranscriptionRoutes(app, deps) {
|
|
|
60564
61305
|
c.req.raw,
|
|
60565
61306
|
deps.settings.voiceInputResumableMaxChunkSizeBytes
|
|
60566
61307
|
);
|
|
60567
|
-
const
|
|
60568
|
-
if (
|
|
61308
|
+
const sha2564 = sha256Hex3(body4);
|
|
61309
|
+
if (sha2564 !== declaredSha256) {
|
|
60569
61310
|
return c.json({ code: "conflict" }, 409);
|
|
60570
61311
|
}
|
|
60571
61312
|
const existing = await getTranscriptionRecording(deps.db, {
|
|
@@ -60587,7 +61328,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
|
|
|
60587
61328
|
recordingId: existing.recording.id,
|
|
60588
61329
|
chunkNumber,
|
|
60589
61330
|
byteLength: body4.byteLength,
|
|
60590
|
-
sha256:
|
|
61331
|
+
sha256: sha2564,
|
|
60591
61332
|
startMilliseconds,
|
|
60592
61333
|
durationMilliseconds,
|
|
60593
61334
|
maxTotalBytes: deps.settings.voiceInputResumableMaxSizeBytes,
|
|
@@ -60600,7 +61341,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
|
|
|
60600
61341
|
key: reservation.chunk.objectKey,
|
|
60601
61342
|
contentType: existing.recording.mimeType,
|
|
60602
61343
|
body: body4,
|
|
60603
|
-
sha256:
|
|
61344
|
+
sha256: sha2564
|
|
60604
61345
|
});
|
|
60605
61346
|
} catch {
|
|
60606
61347
|
throw new RecordingProcessingError("Chunk upload failed", "network", true);
|
|
@@ -60639,7 +61380,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
|
|
|
60639
61380
|
app.post(
|
|
60640
61381
|
"/v1/workspaces/:workspaceId/transcription-recordings/:recordingId/finalize",
|
|
60641
61382
|
async (c) => {
|
|
60642
|
-
const
|
|
61383
|
+
const owner2 = correlationId(c);
|
|
60643
61384
|
let authority = null;
|
|
60644
61385
|
let generation = 0;
|
|
60645
61386
|
try {
|
|
@@ -60654,7 +61395,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
|
|
|
60654
61395
|
workspaceId: authority.workspaceId,
|
|
60655
61396
|
subjectId: authority.subjectId,
|
|
60656
61397
|
recordingId,
|
|
60657
|
-
owner,
|
|
61398
|
+
owner: owner2,
|
|
60658
61399
|
...parsed.data,
|
|
60659
61400
|
staleBefore: new Date(Date.now() - PROCESSING_LEASE_MILLISECONDS)
|
|
60660
61401
|
});
|
|
@@ -60702,15 +61443,15 @@ function registerResumableTranscriptionRoutes(app, deps) {
|
|
|
60702
61443
|
chunks: verifiedChunkBytes(deps, chunks, c.req.raw.signal),
|
|
60703
61444
|
signal: c.req.raw.signal
|
|
60704
61445
|
})) {
|
|
60705
|
-
const
|
|
61446
|
+
const sha2564 = sha256Hex3(segment.bytes);
|
|
60706
61447
|
const reservation = await reserveTranscriptionRecordingSegment(deps.db, {
|
|
60707
61448
|
...authority,
|
|
60708
61449
|
recordingId,
|
|
60709
|
-
owner,
|
|
61450
|
+
owner: owner2,
|
|
60710
61451
|
generation,
|
|
60711
61452
|
segmentNumber: segment.segmentNumber,
|
|
60712
61453
|
byteLength: segment.bytes.byteLength,
|
|
60713
|
-
sha256:
|
|
61454
|
+
sha256: sha2564,
|
|
60714
61455
|
startMilliseconds: segment.startMilliseconds,
|
|
60715
61456
|
durationMilliseconds: segment.durationMilliseconds
|
|
60716
61457
|
});
|
|
@@ -60719,7 +61460,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
|
|
|
60719
61460
|
key: reservation.objectKey,
|
|
60720
61461
|
contentType: segment.mimeType,
|
|
60721
61462
|
body: segment.bytes,
|
|
60722
|
-
sha256:
|
|
61463
|
+
sha256: sha2564
|
|
60723
61464
|
});
|
|
60724
61465
|
} catch {
|
|
60725
61466
|
throw new RecordingProcessingError("Segment upload failed", "network", true);
|
|
@@ -60728,7 +61469,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
|
|
|
60728
61469
|
workspaceId: authority.workspaceId,
|
|
60729
61470
|
subjectId: authority.subjectId,
|
|
60730
61471
|
recordingId,
|
|
60731
|
-
owner,
|
|
61472
|
+
owner: owner2,
|
|
60732
61473
|
generation,
|
|
60733
61474
|
segmentNumber: segment.segmentNumber
|
|
60734
61475
|
});
|
|
@@ -60738,7 +61479,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
|
|
|
60738
61479
|
workspaceId: authority.workspaceId,
|
|
60739
61480
|
subjectId: authority.subjectId,
|
|
60740
61481
|
recordingId,
|
|
60741
|
-
owner,
|
|
61482
|
+
owner: owner2,
|
|
60742
61483
|
generation
|
|
60743
61484
|
})
|
|
60744
61485
|
);
|
|
@@ -60749,7 +61490,7 @@ function registerResumableTranscriptionRoutes(app, deps) {
|
|
|
60749
61490
|
workspaceId: authority.workspaceId,
|
|
60750
61491
|
subjectId: authority.subjectId,
|
|
60751
61492
|
recordingId: uuidParam3(c, "recordingId"),
|
|
60752
|
-
owner,
|
|
61493
|
+
owner: owner2,
|
|
60753
61494
|
generation,
|
|
60754
61495
|
errorCode: failure.code,
|
|
60755
61496
|
retryable: failure.retryable
|
|
@@ -60915,13 +61656,13 @@ async function cleanupTerminalObjects(deps, authority, response) {
|
|
|
60915
61656
|
return response;
|
|
60916
61657
|
}
|
|
60917
61658
|
try {
|
|
60918
|
-
const
|
|
61659
|
+
const keys2 = await transcriptionRecordingObjectKeys(deps.db, {
|
|
60919
61660
|
workspaceId: authority.workspaceId,
|
|
60920
61661
|
subjectId: authority.subjectId,
|
|
60921
61662
|
recordingId: response.recording.id
|
|
60922
61663
|
});
|
|
60923
61664
|
let current = response;
|
|
60924
|
-
for (const key of
|
|
61665
|
+
for (const key of keys2) {
|
|
60925
61666
|
await deps.objectStorage.deleteObject(key);
|
|
60926
61667
|
current = await markTranscriptionRecordingObjectCleaned(deps.db, {
|
|
60927
61668
|
workspaceId: authority.workspaceId,
|
|
@@ -61031,7 +61772,7 @@ function valueAsUuid(value) {
|
|
|
61031
61772
|
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value)) {
|
|
61032
61773
|
return value;
|
|
61033
61774
|
}
|
|
61034
|
-
const hex2 =
|
|
61775
|
+
const hex2 = createHash23("sha256").update(value).digest("hex").slice(0, 32);
|
|
61035
61776
|
return `${hex2.slice(0, 8)}-${hex2.slice(8, 12)}-4${hex2.slice(13, 16)}-a${hex2.slice(17, 20)}-${hex2.slice(20, 32)}`;
|
|
61036
61777
|
}
|
|
61037
61778
|
function nonnegativeInteger(value) {
|
|
@@ -61095,7 +61836,7 @@ async function readBoundedBody(request, maxBytes) {
|
|
|
61095
61836
|
return body4;
|
|
61096
61837
|
}
|
|
61097
61838
|
function sha256Hex3(bytes) {
|
|
61098
|
-
return
|
|
61839
|
+
return createHash23("sha256").update(bytes).digest("hex");
|
|
61099
61840
|
}
|
|
61100
61841
|
|
|
61101
61842
|
// src/routes/transcriptions.ts
|
|
@@ -61401,8 +62142,8 @@ import {
|
|
|
61401
62142
|
getCanonicalHumanIdentityProjection as getCanonicalHumanIdentityProjection2
|
|
61402
62143
|
} from "@opengeni/db/canonical-human-identities";
|
|
61403
62144
|
import { HTTPException as HTTPException71 } from "hono/http-exception";
|
|
61404
|
-
import { z as
|
|
61405
|
-
var BindingId =
|
|
62145
|
+
import { z as z23 } from "zod";
|
|
62146
|
+
var BindingId = z23.string().uuid();
|
|
61406
62147
|
async function requestIdentity(context, deps) {
|
|
61407
62148
|
return await requireCanonicalHumanRequestIdentity(context, {
|
|
61408
62149
|
db: deps.db,
|
|
@@ -61596,11 +62337,11 @@ import {
|
|
|
61596
62337
|
updateOrganizationRetentionPolicy
|
|
61597
62338
|
} from "@opengeni/db";
|
|
61598
62339
|
import { HTTPException as HTTPException72 } from "hono/http-exception";
|
|
61599
|
-
import { z as
|
|
61600
|
-
var OrganizationId2 =
|
|
61601
|
-
var WorkspaceId =
|
|
61602
|
-
var MembershipId =
|
|
61603
|
-
var InvitationId =
|
|
62340
|
+
import { z as z24 } from "zod";
|
|
62341
|
+
var OrganizationId2 = z24.string().uuid();
|
|
62342
|
+
var WorkspaceId = z24.string().uuid();
|
|
62343
|
+
var MembershipId = z24.string().uuid();
|
|
62344
|
+
var InvitationId = z24.string().uuid();
|
|
61604
62345
|
async function requireManagedHuman(context, deps) {
|
|
61605
62346
|
if (deps.settings.productAccessMode !== "managed" || !deps.managedAuth || !context.req.header("cookie") || context.req.header("authorization")) {
|
|
61606
62347
|
throw new HTTPException72(401, { message: "managed human session required" });
|
|
@@ -62347,9 +63088,9 @@ import {
|
|
|
62347
63088
|
startOrganizationRecoveryOperation
|
|
62348
63089
|
} from "@opengeni/db";
|
|
62349
63090
|
import { HTTPException as HTTPException73 } from "hono/http-exception";
|
|
62350
|
-
import { z as
|
|
62351
|
-
var OrganizationId3 =
|
|
62352
|
-
var RecoveryOperationId =
|
|
63091
|
+
import { z as z25 } from "zod";
|
|
63092
|
+
var OrganizationId3 = z25.string().uuid();
|
|
63093
|
+
var RecoveryOperationId = z25.string().uuid();
|
|
62353
63094
|
var productionServices = {
|
|
62354
63095
|
requireCanonicalHumanRequestIdentity: requireCanonicalHumanRequestIdentity2,
|
|
62355
63096
|
getManagedAuthRequestActorAdmissionStamp,
|
|
@@ -62897,15 +63638,15 @@ import {
|
|
|
62897
63638
|
ManagedAuthSessionSetOperationReuseError,
|
|
62898
63639
|
mutateManagedAuthSessionSet
|
|
62899
63640
|
} from "@opengeni/db/managed-auth-session-sets";
|
|
62900
|
-
import { ensureManagedAccessForUser as ensureManagedAccessForUser2, getSession as
|
|
63641
|
+
import { ensureManagedAccessForUser as ensureManagedAccessForUser2, getSession as getSession9 } from "@opengeni/db";
|
|
62901
63642
|
import { deleteCookie as deleteCookie3, getCookie, setCookie as setCookie3 } from "hono/cookie";
|
|
62902
63643
|
import { HTTPException as HTTPException75 } from "hono/http-exception";
|
|
62903
|
-
import { z as
|
|
62904
|
-
var ManagedAuthSocialStartReceipt =
|
|
62905
|
-
version:
|
|
62906
|
-
requestDigest:
|
|
62907
|
-
url:
|
|
62908
|
-
stateCookie:
|
|
63644
|
+
import { z as z26 } from "zod";
|
|
63645
|
+
var ManagedAuthSocialStartReceipt = z26.object({
|
|
63646
|
+
version: z26.literal(1),
|
|
63647
|
+
requestDigest: z26.string().regex(/^[0-9a-f]{64}$/u),
|
|
63648
|
+
url: z26.string().url().max(4096),
|
|
63649
|
+
stateCookie: z26.string().min(1).max(4096)
|
|
62909
63650
|
}).strict();
|
|
62910
63651
|
function registerManagedAuthSessionSetRoutes(app, deps) {
|
|
62911
63652
|
const noStore = async (context, next) => {
|
|
@@ -63535,7 +64276,7 @@ async function resolveCanonicalDeepLink(deps, authority, path, expectedActorEpoc
|
|
|
63535
64276
|
continue;
|
|
63536
64277
|
}
|
|
63537
64278
|
if (target.sessionId) {
|
|
63538
|
-
const session = await
|
|
64279
|
+
const session = await getSession9(deps.db, grant.workspaceId, target.sessionId);
|
|
63539
64280
|
if (!session) continue;
|
|
63540
64281
|
try {
|
|
63541
64282
|
await requireSessionAuthorization9(deps, grant, {
|
|
@@ -63834,7 +64575,7 @@ import {
|
|
|
63834
64575
|
import {
|
|
63835
64576
|
issueManagedHumanUserResourceGrant,
|
|
63836
64577
|
listManagedHumanUserResourceAuthorities,
|
|
63837
|
-
requireAccessGrantAuthorization as
|
|
64578
|
+
requireAccessGrantAuthorization as requireAccessGrantAuthorization18,
|
|
63838
64579
|
revokeManagedHumanUserResourceGrant,
|
|
63839
64580
|
SessionAuthorizationDeniedError as SessionAuthorizationDeniedError10,
|
|
63840
64581
|
SessionAuthorizationUnavailableError as SessionAuthorizationUnavailableError8,
|
|
@@ -63842,7 +64583,7 @@ import {
|
|
|
63842
64583
|
} from "@opengeni/core";
|
|
63843
64584
|
import { nestedPostgresSqlState as nestedPostgresSqlState9, SessionTenancyNotActivatedError as SessionTenancyNotActivatedError2 } from "@opengeni/db";
|
|
63844
64585
|
import { HTTPException as HTTPException76 } from "hono/http-exception";
|
|
63845
|
-
import { z as
|
|
64586
|
+
import { z as z27 } from "zod";
|
|
63846
64587
|
async function body2(context, schema) {
|
|
63847
64588
|
const parsed = schema.safeParse(await context.req.json().catch(() => null));
|
|
63848
64589
|
if (!parsed.success) throw new HTTPException76(422, { message: "invalid user-resource request" });
|
|
@@ -63874,7 +64615,7 @@ function registerUserResourceAuthorityRoutes(app, deps) {
|
|
|
63874
64615
|
});
|
|
63875
64616
|
if (!query.success) throw new HTTPException76(422, { message: "explicit scope=user required" });
|
|
63876
64617
|
const workspaceId = context.req.param("workspaceId");
|
|
63877
|
-
const access = await
|
|
64618
|
+
const access = await requireAccessGrantAuthorization18(
|
|
63878
64619
|
context,
|
|
63879
64620
|
deps,
|
|
63880
64621
|
workspaceId,
|
|
@@ -63899,10 +64640,10 @@ function registerUserResourceAuthorityRoutes(app, deps) {
|
|
|
63899
64640
|
});
|
|
63900
64641
|
app.post(`${base}/:authorityId/grants`, async (context) => {
|
|
63901
64642
|
const workspaceId = context.req.param("workspaceId");
|
|
63902
|
-
const authorityId =
|
|
64643
|
+
const authorityId = z27.string().uuid().safeParse(context.req.param("authorityId"));
|
|
63903
64644
|
if (!authorityId.success) throw new HTTPException76(422, { message: "invalid authority id" });
|
|
63904
64645
|
const request = await body2(context, IssueUserResourceGrantRequest);
|
|
63905
|
-
const access = await
|
|
64646
|
+
const access = await requireAccessGrantAuthorization18(
|
|
63906
64647
|
context,
|
|
63907
64648
|
deps,
|
|
63908
64649
|
workspaceId,
|
|
@@ -63929,10 +64670,10 @@ function registerUserResourceAuthorityRoutes(app, deps) {
|
|
|
63929
64670
|
app.delete(`${base}/grants/:grantId`, async (context) => {
|
|
63930
64671
|
const query = RevokeUserResourceGrantQuery.safeParse({ scope: context.req.query("scope") });
|
|
63931
64672
|
if (!query.success) throw new HTTPException76(422, { message: "explicit scope=user required" });
|
|
63932
|
-
const grantId =
|
|
64673
|
+
const grantId = z27.string().uuid().safeParse(context.req.param("grantId"));
|
|
63933
64674
|
if (!grantId.success) throw new HTTPException76(422, { message: "invalid grant id" });
|
|
63934
64675
|
const workspaceId = context.req.param("workspaceId");
|
|
63935
|
-
const access = await
|
|
64676
|
+
const access = await requireAccessGrantAuthorization18(
|
|
63936
64677
|
context,
|
|
63937
64678
|
deps,
|
|
63938
64679
|
workspaceId,
|
|
@@ -63962,7 +64703,7 @@ import {
|
|
|
63962
64703
|
import {
|
|
63963
64704
|
issueManagedHumanUserResourceGrant as issueManagedHumanUserResourceGrant2,
|
|
63964
64705
|
listManagedHumanUserResourceAuthorities as listManagedHumanUserResourceAuthorities2,
|
|
63965
|
-
requireAccessGrantAuthorization as
|
|
64706
|
+
requireAccessGrantAuthorization as requireAccessGrantAuthorization19,
|
|
63966
64707
|
revokeManagedHumanUserResourceGrant as revokeManagedHumanUserResourceGrant2,
|
|
63967
64708
|
SessionAuthorizationDeniedError as SessionAuthorizationDeniedError11,
|
|
63968
64709
|
SessionAuthorizationUnavailableError as SessionAuthorizationUnavailableError9,
|
|
@@ -63970,7 +64711,7 @@ import {
|
|
|
63970
64711
|
} from "@opengeni/core";
|
|
63971
64712
|
import { nestedPostgresSqlState as nestedPostgresSqlState10, SessionTenancyNotActivatedError as SessionTenancyNotActivatedError3 } from "@opengeni/db";
|
|
63972
64713
|
import { HTTPException as HTTPException78 } from "hono/http-exception";
|
|
63973
|
-
import { z as
|
|
64714
|
+
import { z as z28 } from "zod";
|
|
63974
64715
|
|
|
63975
64716
|
// src/connection-authority-owner.ts
|
|
63976
64717
|
import {
|
|
@@ -64028,7 +64769,7 @@ function registerConnectionAuthorityRoutes(app, deps) {
|
|
|
64028
64769
|
});
|
|
64029
64770
|
if (!query.success) throw new HTTPException78(422, { message: "explicit scope=user required" });
|
|
64030
64771
|
const workspaceId = context.req.param("workspaceId");
|
|
64031
|
-
const access = await
|
|
64772
|
+
const access = await requireAccessGrantAuthorization19(
|
|
64032
64773
|
context,
|
|
64033
64774
|
deps,
|
|
64034
64775
|
workspaceId,
|
|
@@ -64050,10 +64791,10 @@ function registerConnectionAuthorityRoutes(app, deps) {
|
|
|
64050
64791
|
});
|
|
64051
64792
|
app.post(`${base}/:authorityId/grants`, async (context) => {
|
|
64052
64793
|
const workspaceId = context.req.param("workspaceId");
|
|
64053
|
-
const authorityId =
|
|
64794
|
+
const authorityId = z28.string().uuid().safeParse(context.req.param("authorityId"));
|
|
64054
64795
|
if (!authorityId.success) throw new HTTPException78(422, { message: "invalid authority id" });
|
|
64055
64796
|
const request = await body3(context, IssueConnectionUseGrantRequest2);
|
|
64056
|
-
const access = await
|
|
64797
|
+
const access = await requireAccessGrantAuthorization19(
|
|
64057
64798
|
context,
|
|
64058
64799
|
deps,
|
|
64059
64800
|
workspaceId,
|
|
@@ -64081,10 +64822,10 @@ function registerConnectionAuthorityRoutes(app, deps) {
|
|
|
64081
64822
|
app.delete(`${base}/grants/:grantId`, async (context) => {
|
|
64082
64823
|
const query = RevokeConnectionUseGrantQuery.safeParse({ scope: context.req.query("scope") });
|
|
64083
64824
|
if (!query.success) throw new HTTPException78(422, { message: "explicit scope=user required" });
|
|
64084
|
-
const grantId =
|
|
64825
|
+
const grantId = z28.string().uuid().safeParse(context.req.param("grantId"));
|
|
64085
64826
|
if (!grantId.success) throw new HTTPException78(422, { message: "invalid grant id" });
|
|
64086
64827
|
const workspaceId = context.req.param("workspaceId");
|
|
64087
|
-
const access = await
|
|
64828
|
+
const access = await requireAccessGrantAuthorization19(
|
|
64088
64829
|
context,
|
|
64089
64830
|
deps,
|
|
64090
64831
|
workspaceId,
|
|
@@ -64610,7 +65351,7 @@ async function exactAvailable(providers, providerId, context) {
|
|
|
64610
65351
|
}
|
|
64611
65352
|
|
|
64612
65353
|
// src/integrations/slack-interactions.ts
|
|
64613
|
-
import { createHash as
|
|
65354
|
+
import { createHash as createHash25, createHmac as createHmac9, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
64614
65355
|
import {
|
|
64615
65356
|
approvalIdentifier,
|
|
64616
65357
|
ApproveSlackUserLinkAccessRequest,
|
|
@@ -64653,7 +65394,7 @@ import {
|
|
|
64653
65394
|
getConnectionMetadata as getConnectionMetadata12,
|
|
64654
65395
|
getOrCreateSlackInteraction,
|
|
64655
65396
|
getLatestSessionModelForSubject,
|
|
64656
|
-
getSession as
|
|
65397
|
+
getSession as getSession10,
|
|
64657
65398
|
getSessionEvent as getSessionEvent2,
|
|
64658
65399
|
getSessionHumanInputRequest as getSessionHumanInputRequest2,
|
|
64659
65400
|
childRequiresActionResolutionExists,
|
|
@@ -65088,7 +65829,7 @@ function record5(value) {
|
|
|
65088
65829
|
}
|
|
65089
65830
|
|
|
65090
65831
|
// src/slack-reaction-files.ts
|
|
65091
|
-
import { createHash as
|
|
65832
|
+
import { createHash as createHash24 } from "crypto";
|
|
65092
65833
|
import { completeFileUpload as completeFileUpload5, prepareGeneratedWorkspaceFile as prepareGeneratedWorkspaceFile2 } from "@opengeni/db";
|
|
65093
65834
|
import { retryWhileMissing as retryWhileMissing6 } from "@opengeni/storage";
|
|
65094
65835
|
var IMPORT_IDENTITY_VERSION = "slack-reaction-image-v1";
|
|
@@ -65105,7 +65846,7 @@ async function importSlackReactionImage(dependencies, source, image, ordinal) {
|
|
|
65105
65846
|
if (image.bytes.byteLength < 1 || image.bytes.byteLength > 4 * 1024 * 1024) {
|
|
65106
65847
|
throw new Error("Slack reaction image size is invalid");
|
|
65107
65848
|
}
|
|
65108
|
-
const
|
|
65849
|
+
const sha2564 = createHash24("sha256").update(image.bytes).digest("hex");
|
|
65109
65850
|
const identity = slackReactionImageIdentity(source, image.fileId);
|
|
65110
65851
|
const fileId = deterministicUuid(`${identity}:file`);
|
|
65111
65852
|
const uploadId = deterministicUuid(`${identity}:upload`);
|
|
@@ -65123,7 +65864,7 @@ async function importSlackReactionImage(dependencies, source, image, ordinal) {
|
|
|
65123
65864
|
safeFilename,
|
|
65124
65865
|
contentType: image.contentType,
|
|
65125
65866
|
sizeBytes: image.bytes.byteLength,
|
|
65126
|
-
sha256:
|
|
65867
|
+
sha256: sha2564,
|
|
65127
65868
|
bucket: storage.bucket,
|
|
65128
65869
|
objectKey,
|
|
65129
65870
|
expiresAt: new Date((dependencies.now?.() ?? /* @__PURE__ */ new Date()).getTime() + UPLOAD_INTENT_TTL_MS2)
|
|
@@ -65133,23 +65874,23 @@ async function importSlackReactionImage(dependencies, source, image, ordinal) {
|
|
|
65133
65874
|
if (file.status !== "ready") {
|
|
65134
65875
|
const existing = await storage.headObject(objectKey);
|
|
65135
65876
|
if (existing) {
|
|
65136
|
-
assertStoredSlackImage(existing, image,
|
|
65877
|
+
assertStoredSlackImage(existing, image, sha2564);
|
|
65137
65878
|
} else if (storage.putObjectIfAbsent) {
|
|
65138
65879
|
const created = await storage.putObjectIfAbsent({
|
|
65139
65880
|
key: objectKey,
|
|
65140
65881
|
contentType: image.contentType,
|
|
65141
65882
|
body: image.bytes,
|
|
65142
|
-
sha256:
|
|
65883
|
+
sha256: sha2564
|
|
65143
65884
|
});
|
|
65144
65885
|
if (!created) {
|
|
65145
|
-
await assertVisibleSlackImage(storage, objectKey, image,
|
|
65886
|
+
await assertVisibleSlackImage(storage, objectKey, image, sha2564);
|
|
65146
65887
|
}
|
|
65147
65888
|
} else {
|
|
65148
65889
|
await storage.putObject({
|
|
65149
65890
|
key: objectKey,
|
|
65150
65891
|
contentType: image.contentType,
|
|
65151
65892
|
body: image.bytes,
|
|
65152
|
-
sha256:
|
|
65893
|
+
sha256: sha2564
|
|
65153
65894
|
});
|
|
65154
65895
|
}
|
|
65155
65896
|
file = await (dependencies.completeFile ?? completeFileUpload5)(
|
|
@@ -65158,9 +65899,9 @@ async function importSlackReactionImage(dependencies, source, image, ordinal) {
|
|
|
65158
65899
|
uploadId
|
|
65159
65900
|
);
|
|
65160
65901
|
} else {
|
|
65161
|
-
await assertVisibleSlackImage(storage, objectKey, image,
|
|
65902
|
+
await assertVisibleSlackImage(storage, objectKey, image, sha2564);
|
|
65162
65903
|
}
|
|
65163
|
-
if (file.id !== fileId || file.status !== "ready" || file.filename !== filename || file.safeFilename !== safeFilename || file.contentType !== image.contentType || file.sizeBytes !== image.bytes.byteLength || file.sha256 !==
|
|
65904
|
+
if (file.id !== fileId || file.status !== "ready" || file.filename !== filename || file.safeFilename !== safeFilename || file.contentType !== image.contentType || file.sizeBytes !== image.bytes.byteLength || file.sha256 !== sha2564) {
|
|
65164
65905
|
throw new Error("Imported Slack image differs from its immutable workspace file");
|
|
65165
65906
|
}
|
|
65166
65907
|
const mountPath = `attachments/slack/${String(ordinal).padStart(2, "0")}-${safeFilename}`;
|
|
@@ -65189,17 +65930,17 @@ function safeSlackImageFilename(value, contentType) {
|
|
|
65189
65930
|
const stem = withoutKnownExtension.replace(/^[. -]+|[. ]+$/gu, "").slice(0, 180) || "slack-image";
|
|
65190
65931
|
return `${stem}${extension}`;
|
|
65191
65932
|
}
|
|
65192
|
-
async function assertVisibleSlackImage(storage, objectKey, image,
|
|
65933
|
+
async function assertVisibleSlackImage(storage, objectKey, image, sha2564) {
|
|
65193
65934
|
const head = await retryWhileMissing6(async () => await storage.headObject(objectKey));
|
|
65194
|
-
assertStoredSlackImage(head, image,
|
|
65935
|
+
assertStoredSlackImage(head, image, sha2564);
|
|
65195
65936
|
}
|
|
65196
|
-
function assertStoredSlackImage(head, image,
|
|
65197
|
-
if (!head || head.ContentLength !== image.bytes.byteLength || head.ContentType !== image.contentType || head.Metadata?.sha256 !==
|
|
65937
|
+
function assertStoredSlackImage(head, image, sha2564) {
|
|
65938
|
+
if (!head || head.ContentLength !== image.bytes.byteLength || head.ContentType !== image.contentType || head.Metadata?.sha256 !== sha2564 || typeof head.VersionToken !== "string" || head.VersionToken.length < 1) {
|
|
65198
65939
|
throw new Error("Stored Slack image differs from its immutable import");
|
|
65199
65940
|
}
|
|
65200
65941
|
}
|
|
65201
65942
|
function deterministicUuid(seed) {
|
|
65202
|
-
const bytes =
|
|
65943
|
+
const bytes = createHash24("sha256").update(seed, "utf8").digest().subarray(0, 16);
|
|
65203
65944
|
bytes[6] = (bytes[6] ?? 0) & 15 | 80;
|
|
65204
65945
|
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
65205
65946
|
const hex2 = bytes.toString("hex");
|
|
@@ -65358,7 +66099,7 @@ function slackReactionInboxEntry(payload, bot, settings) {
|
|
|
65358
66099
|
if (!settings.enabled || !event || event.type !== "reaction_added" || item?.type !== "message" || !teamId || !eventId || !userId || userId === bot.botUserId || !reaction || reaction !== settings.emoji || !channelId || !workspaceSlackReactionChannelAllowed(settings, channelId) || !timestamp) {
|
|
65359
66100
|
return null;
|
|
65360
66101
|
}
|
|
65361
|
-
const stableReactionIdentity =
|
|
66102
|
+
const stableReactionIdentity = createHash25("sha256").update([teamId, userId, channelId, timestamp, reaction].join("\n")).digest("hex");
|
|
65362
66103
|
return {
|
|
65363
66104
|
providerEventId: eventId,
|
|
65364
66105
|
providerMessageId: `reaction:${stableReactionIdentity}`,
|
|
@@ -65548,7 +66289,7 @@ function registerSlackInteractionRoutes(app, deps) {
|
|
|
65548
66289
|
const prepared = await prepareSlackUserLinkAccessRequest(deps.db, {
|
|
65549
66290
|
accountId: installation.accountId,
|
|
65550
66291
|
workspaceId,
|
|
65551
|
-
tokenDigest:
|
|
66292
|
+
tokenDigest: createHash25("sha256").update(payload.linkToken).digest("hex"),
|
|
65552
66293
|
connectionId: link.connectionId,
|
|
65553
66294
|
slackTeamId: link.slackTeamId,
|
|
65554
66295
|
slackUserId: link.slackUserId,
|
|
@@ -65755,7 +66496,7 @@ function registerSlackInteractionRoutes(app, deps) {
|
|
|
65755
66496
|
const prepared = await prepareSlackUserLinkAccessRequest(deps.db, {
|
|
65756
66497
|
accountId: grant.accountId,
|
|
65757
66498
|
workspaceId,
|
|
65758
|
-
tokenDigest:
|
|
66499
|
+
tokenDigest: createHash25("sha256").update(linkToken).digest("hex"),
|
|
65759
66500
|
connectionId: link.connectionId,
|
|
65760
66501
|
slackTeamId: link.slackTeamId,
|
|
65761
66502
|
slackUserId: link.slackUserId,
|
|
@@ -67219,7 +67960,7 @@ function slackReactionPreparedEntry(entry2, context, prepared) {
|
|
|
67219
67960
|
}
|
|
67220
67961
|
async function remountSlackReactionTaskForSession(deps, workspaceId, sessionId, prepared) {
|
|
67221
67962
|
if (prepared.attachments.length === 0) return prepared;
|
|
67222
|
-
const session = await
|
|
67963
|
+
const session = await getSession10(deps.db, workspaceId, sessionId);
|
|
67223
67964
|
if (!session) throw new SlackInteractionPermanentError("session_not_found");
|
|
67224
67965
|
const initialOrdinal = session.resources.reduce((maximum, resource) => {
|
|
67225
67966
|
if (resource.kind !== "file" || !resource.mountPath) return maximum;
|
|
@@ -67815,7 +68556,7 @@ async function executeSlackAction(deps, grant, interaction, handle) {
|
|
|
67815
68556
|
controlState: action === "pause" ? "paused" : "active"
|
|
67816
68557
|
};
|
|
67817
68558
|
}
|
|
67818
|
-
const session = await
|
|
68559
|
+
const session = await getSession10(deps.db, grant.workspaceId, handle.sessionId);
|
|
67819
68560
|
if (!session) throw new SlackInteractionPermanentError("slack_action_session_missing");
|
|
67820
68561
|
const recurring = hasPermission25(grant.permissions, "sessions:read") && hasPermission25(grant.permissions, "scheduled_tasks:manage") ? makeRecurringText(deps, grant.workspaceId, handle.sessionId) : null;
|
|
67821
68562
|
return {
|
|
@@ -68636,7 +69377,7 @@ function normalizedBlockActionInteraction(payload) {
|
|
|
68636
69377
|
if (!teamId || !userId || !channelId || !messageTs || !actionTs || !actionId || !SLACK_BLOCK_ACTION_IDS.has(actionId) || !handleId || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(handleId) || !Array.isArray(payload.actions) || payload.actions.length !== 1) {
|
|
68637
69378
|
throw new HTTPException79(400, { message: "invalid Slack block action" });
|
|
68638
69379
|
}
|
|
68639
|
-
const identity =
|
|
69380
|
+
const identity = createHash25("sha256").update([teamId, userId, channelId, messageTs, actionTs, actionId, handleId].join("\n")).digest("hex");
|
|
68640
69381
|
return {
|
|
68641
69382
|
providerEventId: `block:${identity}`,
|
|
68642
69383
|
providerMessageId: `block:${identity}`,
|
|
@@ -68901,7 +69642,7 @@ function usesPrivateBotDm(interaction, entry2) {
|
|
|
68901
69642
|
return isDirectMessageShortcut(entry2) || interaction.visibility === "private" && entry2.triggerKind !== "dm";
|
|
68902
69643
|
}
|
|
68903
69644
|
function deterministicUuid2(value) {
|
|
68904
|
-
const bytes =
|
|
69645
|
+
const bytes = createHash25("sha256").update(value).digest().subarray(0, 16);
|
|
68905
69646
|
bytes[6] = bytes[6] & 15 | 80;
|
|
68906
69647
|
bytes[8] = bytes[8] & 63 | 128;
|
|
68907
69648
|
const hex2 = bytes.toString("hex");
|
|
@@ -69021,7 +69762,7 @@ import {
|
|
|
69021
69762
|
validateToolRefs,
|
|
69022
69763
|
withDefaultEnabledCapabilityMcpTools
|
|
69023
69764
|
} from "@opengeni/core";
|
|
69024
|
-
import { workflowIdForSession as
|
|
69765
|
+
import { workflowIdForSession as workflowIdForSession4 } from "@opengeni/core";
|
|
69025
69766
|
var API_MAX_REQUEST_BODY_BYTES = 32 * 1024 * 1024;
|
|
69026
69767
|
var managedAuthReaperDatabases = /* @__PURE__ */ new WeakSet();
|
|
69027
69768
|
var MANAGED_AUTH_REAPER_INTERVAL_MS = 6e4;
|
|
@@ -69317,13 +70058,13 @@ function createAppComposition(deps) {
|
|
|
69317
70058
|
return await accessKeyBoundary(c, next);
|
|
69318
70059
|
});
|
|
69319
70060
|
app.use("/v1/*", async (c, next) => {
|
|
69320
|
-
c.header(
|
|
69321
|
-
if (deps.settings.environment !== "test" && isApiContractProtectedMutation(c.req.method, new URL(c.req.url).pathname) && c.req.header(
|
|
70061
|
+
c.header(OPENGENI_API_CONTRACT_HEADER2, OPENGENI_API_CONTRACT_REVISION2);
|
|
70062
|
+
if (deps.settings.environment !== "test" && isApiContractProtectedMutation(c.req.method, new URL(c.req.url).pathname) && c.req.header(OPENGENI_API_CONTRACT_HEADER2) !== OPENGENI_API_CONTRACT_REVISION2) {
|
|
69322
70063
|
return c.json(
|
|
69323
70064
|
{
|
|
69324
70065
|
code: "API_CONTRACT_CHANGED",
|
|
69325
70066
|
message: "OpenGeni updated. Reload this client before changing state.",
|
|
69326
|
-
apiContractRevision:
|
|
70067
|
+
apiContractRevision: OPENGENI_API_CONTRACT_REVISION2
|
|
69327
70068
|
},
|
|
69328
70069
|
409
|
|
69329
70070
|
);
|
|
@@ -69522,7 +70263,7 @@ function createAppComposition(deps) {
|
|
|
69522
70263
|
return c.json(
|
|
69523
70264
|
ClientConfig.parse({
|
|
69524
70265
|
deploymentRevision: deps.settings.deploymentRevision,
|
|
69525
|
-
apiContractRevision:
|
|
70266
|
+
apiContractRevision: OPENGENI_API_CONTRACT_REVISION2,
|
|
69526
70267
|
...deps.settings.serverVersion ? { serverVersion: deps.settings.serverVersion } : {},
|
|
69527
70268
|
defaultModel: canonicalizeConfiguredModelId2(catalogSettings, catalogSettings.openaiModel),
|
|
69528
70269
|
allowedModels: configuredAllowedModels(catalogSettings),
|
|
@@ -69696,7 +70437,7 @@ function createAppComposition(deps) {
|
|
|
69696
70437
|
});
|
|
69697
70438
|
app.get("/v1/workspaces/:workspaceId/tools/catalog", async (c) => {
|
|
69698
70439
|
const workspaceId = c.req.param("workspaceId");
|
|
69699
|
-
const authorization = await
|
|
70440
|
+
const authorization = await requireAccessGrantAuthorization20(
|
|
69700
70441
|
c,
|
|
69701
70442
|
routeDeps,
|
|
69702
70443
|
workspaceId,
|
|
@@ -69712,7 +70453,7 @@ function createAppComposition(deps) {
|
|
|
69712
70453
|
});
|
|
69713
70454
|
app.post("/v1/workspaces/:workspaceId/tools/calls", async (c) => {
|
|
69714
70455
|
const workspaceId = c.req.param("workspaceId");
|
|
69715
|
-
const authorization = await
|
|
70456
|
+
const authorization = await requireAccessGrantAuthorization20(
|
|
69716
70457
|
c,
|
|
69717
70458
|
routeDeps,
|
|
69718
70459
|
workspaceId,
|
|
@@ -69741,7 +70482,7 @@ function createAppComposition(deps) {
|
|
|
69741
70482
|
});
|
|
69742
70483
|
app.post("/v1/workspaces/:workspaceId/tools/approvals", async (c) => {
|
|
69743
70484
|
const workspaceId = c.req.param("workspaceId");
|
|
69744
|
-
const authorization = await
|
|
70485
|
+
const authorization = await requireAccessGrantAuthorization20(
|
|
69745
70486
|
c,
|
|
69746
70487
|
routeDeps,
|
|
69747
70488
|
workspaceId,
|
|
@@ -69770,7 +70511,7 @@ function createAppComposition(deps) {
|
|
|
69770
70511
|
});
|
|
69771
70512
|
app.get("/v1/workspaces/:workspaceId/tools/declarations", async (c) => {
|
|
69772
70513
|
const workspaceId = c.req.param("workspaceId");
|
|
69773
|
-
const authorization = await
|
|
70514
|
+
const authorization = await requireAccessGrantAuthorization20(
|
|
69774
70515
|
c,
|
|
69775
70516
|
routeDeps,
|
|
69776
70517
|
workspaceId,
|
|
@@ -69784,6 +70525,19 @@ function createAppComposition(deps) {
|
|
|
69784
70525
|
await prepared.close();
|
|
69785
70526
|
}
|
|
69786
70527
|
});
|
|
70528
|
+
app.all("/v1/workspaces/:workspaceId/codemode/sdk/*", async (c) => {
|
|
70529
|
+
const workspaceId = c.req.param("workspaceId");
|
|
70530
|
+
const grant = await requireAccessGrant46(c, routeDeps, workspaceId);
|
|
70531
|
+
const url = new URL(c.req.url);
|
|
70532
|
+
const prefix = `/v1/workspaces/${workspaceId}/codemode/sdk`;
|
|
70533
|
+
const forwarded = await codemodeSessionRequest(
|
|
70534
|
+
routeDeps,
|
|
70535
|
+
grant,
|
|
70536
|
+
c.req.raw,
|
|
70537
|
+
url.pathname.slice(prefix.length) + url.search
|
|
70538
|
+
);
|
|
70539
|
+
return app.fetch(forwarded);
|
|
70540
|
+
});
|
|
69787
70541
|
app.get("/v1/workspaces/:workspaceId/codemode/catalog", async (c) => {
|
|
69788
70542
|
const workspaceId = c.req.param("workspaceId");
|
|
69789
70543
|
const grant = await requireAccessGrant46(c, routeDeps, workspaceId);
|
|
@@ -69912,7 +70666,7 @@ function createAppComposition(deps) {
|
|
|
69912
70666
|
const requestId = correlationIds.get(c.req.raw) ?? crypto.randomUUID();
|
|
69913
70667
|
c.header(OPENGENI_CORRELATION_HEADER, requestId);
|
|
69914
70668
|
if (new URL(c.req.url).pathname.startsWith("/v1/")) {
|
|
69915
|
-
c.header(
|
|
70669
|
+
c.header(OPENGENI_API_CONTRACT_HEADER2, OPENGENI_API_CONTRACT_REVISION2);
|
|
69916
70670
|
}
|
|
69917
70671
|
const envelope = ErrorEnvelope.parse({
|
|
69918
70672
|
error: {
|
|
@@ -69970,7 +70724,7 @@ async function validateCodexAccountTarget(request) {
|
|
|
69970
70724
|
}
|
|
69971
70725
|
}
|
|
69972
70726
|
async function requireMcpAccessGrantAuthorization(c, deps, workspaceId) {
|
|
69973
|
-
const authorization = await
|
|
70727
|
+
const authorization = await requireAccessGrantAuthorization20(c, deps, workspaceId);
|
|
69974
70728
|
const grant = authorization.grant;
|
|
69975
70729
|
if (hasPermission26(grant.permissions, "workspace:read")) {
|
|
69976
70730
|
return authorization;
|
|
@@ -70684,6 +71438,10 @@ var routeLabelPatterns = [
|
|
|
70684
71438
|
pattern: /^\/v1\/workspaces\/[^/]+\/github\/app$/,
|
|
70685
71439
|
label: "/v1/workspaces/:workspaceId/github/app"
|
|
70686
71440
|
},
|
|
71441
|
+
{
|
|
71442
|
+
pattern: /^\/v1\/workspaces\/[^/]+\/github\/action-policies$/,
|
|
71443
|
+
label: "/v1/workspaces/:workspaceId/github/action-policies"
|
|
71444
|
+
},
|
|
70687
71445
|
{
|
|
70688
71446
|
pattern: /^\/v1\/workspaces\/[^/]+\/github\/repositories$/,
|
|
70689
71447
|
label: "/v1/workspaces/:workspaceId/github/repositories"
|
|
@@ -71090,6 +71848,6 @@ export {
|
|
|
71090
71848
|
validateGitHubRepositorySelectionShapes,
|
|
71091
71849
|
validateToolRefs,
|
|
71092
71850
|
withDefaultEnabledCapabilityMcpTools,
|
|
71093
|
-
|
|
71851
|
+
workflowIdForSession4 as workflowIdForSession
|
|
71094
71852
|
};
|
|
71095
|
-
//# sourceMappingURL=chunk-
|
|
71853
|
+
//# sourceMappingURL=chunk-3VUEFGWS.js.map
|