@standardagents/builder 0.37.8 → 0.38.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/built-in-routes.js +1246 -518
- package/dist/built-in-routes.js.map +1 -1
- package/dist/client/{ApiKeysView-L85eJb6G.js → ApiKeysView-AH5i-H6j.js} +1 -1
- package/dist/client/CenteredContentView-DaBp0K-A.js +1 -0
- package/dist/client/{CompositionView-BGp0EJCk.js → CompositionView-yY1Q4M23.js} +1 -1
- package/dist/client/{ConfirmDialog.vue_vue_type_script_setup_true_lang-DgwqZrLV.js → ConfirmDialog.vue_vue_type_script_setup_true_lang-Bwo5CJzF.js} +1 -1
- package/dist/client/{CopyButton.vue_vue_type_script_setup_true_lang-B8cGgU2J.js → CopyButton.vue_vue_type_script_setup_true_lang-D0WYwWyA.js} +1 -1
- package/dist/client/{DataTable.vue_vue_type_script_setup_true_lang-CRBXym9D.js → DataTable.vue_vue_type_script_setup_true_lang-1wwPn2aY.js} +1 -1
- package/dist/client/{JsonViewer-DkHnIfq3.js → JsonViewer-Bi8Zdmir.js} +1 -1
- package/dist/client/LoginView-BIKahEU6.js +1 -0
- package/dist/client/{MarketplaceView-DEnujUBs.js → MarketplaceView-DPDPVH68.js} +1 -1
- package/dist/client/{Modal.vue_vue_type_script_setup_true_lang-keKEwm3K.js → Modal.vue_vue_type_script_setup_true_lang-D1_rfOsy.js} +1 -1
- package/dist/client/{ModelModal.vue_vue_type_script_setup_true_lang-t1n0bbJe.js → ModelModal.vue_vue_type_script_setup_true_lang-DSLeOdxr.js} +1 -1
- package/dist/client/{ModelsView-BkCLr8sp.js → ModelsView-Q2WXu1Gw.js} +1 -1
- package/dist/client/{PromptEditView-BjCfsJQE.js → PromptEditView-BzA-3n1w.js} +1 -1
- package/dist/client/{PromptModal-nxu_LIv6.js → PromptModal-CJWh_cru.js} +1 -1
- package/dist/client/{PromptsView-1yXGCmEZ.js → PromptsView-BezNl-Et.js} +1 -1
- package/dist/client/{ProvidersView-DxF6gafk.js → ProvidersView-BuzSKxuT.js} +1 -1
- package/dist/client/ThreadInspectorPane.vue_vue_type_script_setup_true_lang-DOxNp_3C.js +22 -0
- package/dist/client/{ThreadsView-CBup_w8u.js → ThreadsView-DPCpZA4J.js} +1 -1
- package/dist/client/{ToolsView-B30TfXU9.js → ToolsView-DMjFMAhH.js} +1 -1
- package/dist/client/{UiCodeBlock.vue_vue_type_script_setup_true_lang-BdyzSxZZ.js → UiCodeBlock.vue_vue_type_script_setup_true_lang-7FgZmkfs.js} +1 -1
- package/dist/client/{UsersView-kE7Xr4f3.js → UsersView-B7jXLNQs.js} +1 -1
- package/dist/client/{VariablesView-yYuynG0M.js → VariablesView-CF7x0G8g.js} +1 -1
- package/dist/client/assets/{ThreadInspectorPane-BRGO_P0_.css → ThreadInspectorPane-CZMdV0Tb.css} +1 -1
- package/dist/client/assets/{index-B-69GGhJ.css → index-DAABRRAA.css} +1 -1
- package/dist/client/{check-YKi1o6p6.js → check-CD2lvcVi.js} +1 -1
- package/dist/client/{chevron-right-T0zzq_GX.js → chevron-right-bWZrmJ5a.js} +1 -1
- package/dist/client/{copy-C_gUGAj1.js → copy-B7nifl6a.js} +1 -1
- package/dist/client/{formkit-CE26dtFp.js → formkit-DJxUzqRQ.js} +1 -1
- package/dist/client/index-eGECJFYe.js +6 -0
- package/dist/client/index.html +6 -6
- package/dist/client/{package-B70IFSDB.js → package-Dyv7fTIQ.js} +1 -1
- package/dist/client/{plus-BN9IHYdt.js → plus-XkSslfF4.js} +1 -1
- package/dist/client/{syntax-D-ZcovVx.js → syntax-D68HCEYm.js} +1 -1
- package/dist/client/{timeAgo-G1ovbWPH.js → timeAgo-DRBdu9ed.js} +1 -1
- package/dist/client/{useActionFeedback-7pfPjKMi.js → useActionFeedback-FN4t6ezB.js} +1 -1
- package/dist/client/{vendor-TPZjEwCU.js → vendor-c_uwtQ-S.js} +13 -13
- package/dist/client/{vue-CZ-ORfS3.js → vue-BuvSxRmI.js} +1 -1
- package/dist/{index-Lfan8QMN.d.ts → index-CF-lgN47.d.ts} +83 -12
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1155 -360
- package/dist/index.js.map +1 -1
- package/dist/plugin.js +131 -72
- package/dist/plugin.js.map +1 -1
- package/dist/runtime.d.ts +69 -11
- package/dist/runtime.js +1018 -280
- package/dist/runtime.js.map +1 -1
- package/dist/test.d.ts +1 -1
- package/package.json +7 -6
- package/dist/client/CenteredContentView-D9jkWoLc.js +0 -1
- package/dist/client/LoginView-BXD9Odc-.js +0 -1
- package/dist/client/ThreadInspectorPane.vue_vue_type_script_setup_true_lang-CjwWJ3uw.js +0 -22
- package/dist/client/index-AUGwDdSH.js +0 -6
package/dist/index.js
CHANGED
|
@@ -37,6 +37,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
37
37
|
};
|
|
38
38
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
39
39
|
|
|
40
|
+
// src/skills/scope.ts
|
|
41
|
+
var SHARED_SKILL_OWNER;
|
|
42
|
+
var init_scope = __esm({
|
|
43
|
+
"src/skills/scope.ts"() {
|
|
44
|
+
SHARED_SKILL_OWNER = "__instance_shared__";
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
40
48
|
// src/agents/client-forwarding.ts
|
|
41
49
|
function asForwarding(state) {
|
|
42
50
|
return state;
|
|
@@ -3159,6 +3167,104 @@ var init_sessionTools = __esm({
|
|
|
3159
3167
|
}
|
|
3160
3168
|
});
|
|
3161
3169
|
|
|
3170
|
+
// src/utils/remoteAttachment.ts
|
|
3171
|
+
function parseAllowedHosts(value) {
|
|
3172
|
+
if (typeof value !== "string") {
|
|
3173
|
+
return /* @__PURE__ */ new Set();
|
|
3174
|
+
}
|
|
3175
|
+
return new Set(
|
|
3176
|
+
value.split(",").map((host) => host.trim().toLowerCase().replace(/\.$/, "")).filter(Boolean)
|
|
3177
|
+
);
|
|
3178
|
+
}
|
|
3179
|
+
function isForbiddenHostname(hostname) {
|
|
3180
|
+
const normalized = hostname.toLowerCase().replace(/\.$/, "");
|
|
3181
|
+
if (/^\d{1,3}(?:\.\d{1,3}){3}$/.test(normalized)) {
|
|
3182
|
+
return true;
|
|
3183
|
+
}
|
|
3184
|
+
if (normalized.startsWith("[") && normalized.endsWith("]")) {
|
|
3185
|
+
return true;
|
|
3186
|
+
}
|
|
3187
|
+
if (!normalized.includes(".")) {
|
|
3188
|
+
return true;
|
|
3189
|
+
}
|
|
3190
|
+
return FORBIDDEN_HOST_SUFFIXES.some(
|
|
3191
|
+
(suffix) => normalized === suffix.slice(1) || normalized.endsWith(suffix)
|
|
3192
|
+
);
|
|
3193
|
+
}
|
|
3194
|
+
function validateRemoteAttachmentUrl(value, allowedHostsValue) {
|
|
3195
|
+
let url;
|
|
3196
|
+
try {
|
|
3197
|
+
url = new URL(value);
|
|
3198
|
+
} catch {
|
|
3199
|
+
return null;
|
|
3200
|
+
}
|
|
3201
|
+
if (url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.port !== "" && url.port !== "443") {
|
|
3202
|
+
return null;
|
|
3203
|
+
}
|
|
3204
|
+
const hostname = url.hostname.toLowerCase().replace(/\.$/, "");
|
|
3205
|
+
if (isForbiddenHostname(hostname)) {
|
|
3206
|
+
return null;
|
|
3207
|
+
}
|
|
3208
|
+
const allowedHosts = parseAllowedHosts(allowedHostsValue);
|
|
3209
|
+
if (!allowedHosts.has(hostname)) {
|
|
3210
|
+
return null;
|
|
3211
|
+
}
|
|
3212
|
+
return url;
|
|
3213
|
+
}
|
|
3214
|
+
async function fetchRemoteAttachment(location, allowedHostsValue, fetchImpl = fetch) {
|
|
3215
|
+
let url = validateRemoteAttachmentUrl(location, allowedHostsValue);
|
|
3216
|
+
if (!url) {
|
|
3217
|
+
return null;
|
|
3218
|
+
}
|
|
3219
|
+
for (let redirectCount = 0; redirectCount <= MAX_REDIRECTS; redirectCount++) {
|
|
3220
|
+
let response;
|
|
3221
|
+
try {
|
|
3222
|
+
response = await fetchImpl(url.toString(), {
|
|
3223
|
+
redirect: "manual"
|
|
3224
|
+
});
|
|
3225
|
+
} catch {
|
|
3226
|
+
return null;
|
|
3227
|
+
}
|
|
3228
|
+
if (!REDIRECT_STATUSES.has(response.status)) {
|
|
3229
|
+
return response.ok ? response : null;
|
|
3230
|
+
}
|
|
3231
|
+
await response.body?.cancel().catch(() => void 0);
|
|
3232
|
+
if (redirectCount === MAX_REDIRECTS) {
|
|
3233
|
+
return null;
|
|
3234
|
+
}
|
|
3235
|
+
const redirectLocation = response.headers.get("Location");
|
|
3236
|
+
if (!redirectLocation) {
|
|
3237
|
+
return null;
|
|
3238
|
+
}
|
|
3239
|
+
let resolvedLocation;
|
|
3240
|
+
try {
|
|
3241
|
+
resolvedLocation = new URL(redirectLocation, url).toString();
|
|
3242
|
+
} catch {
|
|
3243
|
+
return null;
|
|
3244
|
+
}
|
|
3245
|
+
url = validateRemoteAttachmentUrl(resolvedLocation, allowedHostsValue);
|
|
3246
|
+
if (!url) {
|
|
3247
|
+
return null;
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
return null;
|
|
3251
|
+
}
|
|
3252
|
+
var REDIRECT_STATUSES, MAX_REDIRECTS, FORBIDDEN_HOST_SUFFIXES;
|
|
3253
|
+
var init_remoteAttachment = __esm({
|
|
3254
|
+
"src/utils/remoteAttachment.ts"() {
|
|
3255
|
+
REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
|
|
3256
|
+
MAX_REDIRECTS = 3;
|
|
3257
|
+
FORBIDDEN_HOST_SUFFIXES = [
|
|
3258
|
+
".internal",
|
|
3259
|
+
".lan",
|
|
3260
|
+
".local",
|
|
3261
|
+
".localhost",
|
|
3262
|
+
".home",
|
|
3263
|
+
".test"
|
|
3264
|
+
];
|
|
3265
|
+
}
|
|
3266
|
+
});
|
|
3267
|
+
|
|
3162
3268
|
// src/durable-objects/files.ts
|
|
3163
3269
|
var files_exports = {};
|
|
3164
3270
|
__export(files_exports, {
|
|
@@ -3896,6 +4002,7 @@ var init_ToolExecutor = __esm({
|
|
|
3896
4002
|
init_types();
|
|
3897
4003
|
init_ThreadStateImpl();
|
|
3898
4004
|
init_sessionTools();
|
|
4005
|
+
init_remoteAttachment();
|
|
3899
4006
|
SUBAGENT_CREATE_TOOL = "subagent_create";
|
|
3900
4007
|
SUBAGENT_MESSAGE_TOOL = "subagent_message";
|
|
3901
4008
|
SUBAGENTS_LIST_TOOL = "subagents";
|
|
@@ -4944,7 +5051,8 @@ Message one with subagent_message(referenceId, message).`
|
|
|
4944
5051
|
stub,
|
|
4945
5052
|
state.thread.instance,
|
|
4946
5053
|
childResult.attachments,
|
|
4947
|
-
`from-subagent-${childThreadId}
|
|
5054
|
+
`from-subagent-${childThreadId}`,
|
|
5055
|
+
state.env.AGENT_BUILDER_ATTACHMENT_FETCH_HOSTS
|
|
4948
5056
|
);
|
|
4949
5057
|
if (resumable) {
|
|
4950
5058
|
await this.settleResumableChildAfterRun(
|
|
@@ -4977,7 +5085,8 @@ Message one with subagent_message(referenceId, message).`
|
|
|
4977
5085
|
stub,
|
|
4978
5086
|
state.thread.instance,
|
|
4979
5087
|
childResult.attachments,
|
|
4980
|
-
`from-subagent-${childThreadId}
|
|
5088
|
+
`from-subagent-${childThreadId}`,
|
|
5089
|
+
state.env.AGENT_BUILDER_ATTACHMENT_FETCH_HOSTS
|
|
4981
5090
|
);
|
|
4982
5091
|
const messageContent = this.formatSubagentTerminalMessage(
|
|
4983
5092
|
childThreadId,
|
|
@@ -5391,7 +5500,8 @@ Message one with subagent_message(referenceId, message).`
|
|
|
5391
5500
|
state.thread.instance,
|
|
5392
5501
|
childThread,
|
|
5393
5502
|
parentAttachments,
|
|
5394
|
-
`from-parent-${state.threadId}
|
|
5503
|
+
`from-parent-${state.threadId}`,
|
|
5504
|
+
state.env.AGENT_BUILDER_ATTACHMENT_FETCH_HOSTS
|
|
5395
5505
|
);
|
|
5396
5506
|
const targetRole = receivesMessages === "side_a" ? "user" : "assistant";
|
|
5397
5507
|
const baseTimestamp = Date.now() * 1e3;
|
|
@@ -5651,7 +5761,7 @@ Reference: ${referenceId}` : `Reference: ${referenceId}`;
|
|
|
5651
5761
|
const shortId = crypto.randomUUID().slice(0, 8);
|
|
5652
5762
|
return `/attachments/${normalizedStem}-${shortId}.${finalExt}`;
|
|
5653
5763
|
}
|
|
5654
|
-
static async copyAttachmentsBetweenThreads(sourceThread, targetThread, attachments, _pathPrefix) {
|
|
5764
|
+
static async copyAttachmentsBetweenThreads(sourceThread, targetThread, attachments, _pathPrefix, allowedAttachmentFetchHosts) {
|
|
5655
5765
|
if (!attachments || attachments.length === 0) {
|
|
5656
5766
|
return [];
|
|
5657
5767
|
}
|
|
@@ -5679,7 +5789,8 @@ Reference: ${referenceId}` : `Reference: ${referenceId}`;
|
|
|
5679
5789
|
const copiedData = await this.readAttachmentDataFromSource(
|
|
5680
5790
|
sourceThread,
|
|
5681
5791
|
attachment.path,
|
|
5682
|
-
sourceFile
|
|
5792
|
+
sourceFile,
|
|
5793
|
+
allowedAttachmentFetchHosts
|
|
5683
5794
|
);
|
|
5684
5795
|
if (!copiedData) {
|
|
5685
5796
|
continue;
|
|
@@ -5736,7 +5847,7 @@ Reference: ${referenceId}` : `Reference: ${referenceId}`;
|
|
|
5736
5847
|
}
|
|
5737
5848
|
return copied;
|
|
5738
5849
|
}
|
|
5739
|
-
static async readAttachmentDataFromSource(sourceThread, path21, sourceFile) {
|
|
5850
|
+
static async readAttachmentDataFromSource(sourceThread, path21, sourceFile, allowedAttachmentFetchHosts) {
|
|
5740
5851
|
const sourceData = await sourceThread.readFile(path21);
|
|
5741
5852
|
if (sourceData.success && sourceData.data) {
|
|
5742
5853
|
return sourceData.data;
|
|
@@ -5745,14 +5856,17 @@ Reference: ${referenceId}` : `Reference: ${referenceId}`;
|
|
|
5745
5856
|
return null;
|
|
5746
5857
|
}
|
|
5747
5858
|
const location = sourceFile.location;
|
|
5748
|
-
if (!location
|
|
5859
|
+
if (!location) {
|
|
5860
|
+
return null;
|
|
5861
|
+
}
|
|
5862
|
+
const response = await fetchRemoteAttachment(
|
|
5863
|
+
location,
|
|
5864
|
+
allowedAttachmentFetchHosts
|
|
5865
|
+
);
|
|
5866
|
+
if (!response) {
|
|
5749
5867
|
return null;
|
|
5750
5868
|
}
|
|
5751
5869
|
try {
|
|
5752
|
-
const response = await fetch(location);
|
|
5753
|
-
if (!response.ok) {
|
|
5754
|
-
return null;
|
|
5755
|
-
}
|
|
5756
5870
|
const buffer = await response.arrayBuffer();
|
|
5757
5871
|
return this.arrayBufferToBase64(buffer);
|
|
5758
5872
|
} catch {
|
|
@@ -6006,7 +6120,8 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
6006
6120
|
state.thread.instance,
|
|
6007
6121
|
childStub,
|
|
6008
6122
|
sourceAttachments,
|
|
6009
|
-
`from-parent-${state.threadId}
|
|
6123
|
+
`from-parent-${state.threadId}`,
|
|
6124
|
+
state.env.AGENT_BUILDER_ATTACHMENT_FETCH_HOSTS
|
|
6010
6125
|
);
|
|
6011
6126
|
await childStub.queueMessage(referenceId, {
|
|
6012
6127
|
role: receivesMessages === "side_a" ? "user" : "assistant",
|
|
@@ -12988,6 +13103,32 @@ var ThreadStateImpl_exports = {};
|
|
|
12988
13103
|
__export(ThreadStateImpl_exports, {
|
|
12989
13104
|
ThreadStateImpl: () => ThreadStateImpl
|
|
12990
13105
|
});
|
|
13106
|
+
function isRuntimeDurableNamespace(value) {
|
|
13107
|
+
return value !== null && typeof value === "object" && "idFromName" in value && typeof value.idFromName === "function" && "get" in value && typeof value.get === "function";
|
|
13108
|
+
}
|
|
13109
|
+
function isThreadStateRuntimeEnvironment(value) {
|
|
13110
|
+
return value !== null && typeof value === "object" && "AGENT_BUILDER" in value && isRuntimeDurableNamespace(value.AGENT_BUILDER) && (!("AGENT_BUILDER_USER_HUB" in value) || value.AGENT_BUILDER_USER_HUB === void 0 || isRuntimeDurableNamespace(value.AGENT_BUILDER_USER_HUB));
|
|
13111
|
+
}
|
|
13112
|
+
function confinedUserEndpointUrl(userId, endpoint) {
|
|
13113
|
+
const base = new URL(
|
|
13114
|
+
`https://user-hub/users/${encodeURIComponent(userId)}/endpoints/`
|
|
13115
|
+
);
|
|
13116
|
+
const relative4 = endpoint.replace(/^\/+/, "");
|
|
13117
|
+
let decoded;
|
|
13118
|
+
try {
|
|
13119
|
+
decoded = decodeURIComponent(relative4);
|
|
13120
|
+
} catch {
|
|
13121
|
+
throw new Error("Invalid user endpoint path: malformed percent encoding.");
|
|
13122
|
+
}
|
|
13123
|
+
if (decoded.includes("\\") || decoded.includes("?") || decoded.includes("#") || /[\u0000-\u001f\u007f]/.test(decoded) || decoded.split("/").some((segment) => segment === "." || segment === "..")) {
|
|
13124
|
+
throw new Error("Invalid user endpoint path: traversal and URL syntax are not allowed.");
|
|
13125
|
+
}
|
|
13126
|
+
const resolved = new URL(relative4, base);
|
|
13127
|
+
if (resolved.origin !== base.origin || !resolved.pathname.startsWith(base.pathname)) {
|
|
13128
|
+
throw new Error("Invalid user endpoint path: endpoint escaped the user namespace.");
|
|
13129
|
+
}
|
|
13130
|
+
return resolved.toString();
|
|
13131
|
+
}
|
|
12991
13132
|
function hasLocalCodeExecutionRuntime(threadInstance) {
|
|
12992
13133
|
const candidate = threadInstance;
|
|
12993
13134
|
return Boolean(candidate.env && candidate.ctx);
|
|
@@ -13168,6 +13309,7 @@ function runCodeViaThreadRpc(threadInstance, threadId, source, options) {
|
|
|
13168
13309
|
var ThreadStateImpl, ExecutionStateImpl;
|
|
13169
13310
|
var init_ThreadStateImpl = __esm({
|
|
13170
13311
|
"src/agents/ThreadStateImpl.ts"() {
|
|
13312
|
+
init_scope();
|
|
13171
13313
|
init_client_forwarding();
|
|
13172
13314
|
init_types();
|
|
13173
13315
|
init_utilities();
|
|
@@ -13401,6 +13543,14 @@ var init_ThreadStateImpl = __esm({
|
|
|
13401
13543
|
}
|
|
13402
13544
|
return value;
|
|
13403
13545
|
}
|
|
13546
|
+
async instanceEnv(propertyName) {
|
|
13547
|
+
const agentBuilder = this._getAgentBuilderStub();
|
|
13548
|
+
if (typeof agentBuilder.getInstanceEnv !== "function") {
|
|
13549
|
+
throw new Error("instanceEnv is not supported by this runtime");
|
|
13550
|
+
}
|
|
13551
|
+
const values = await agentBuilder.getInstanceEnv();
|
|
13552
|
+
return values[propertyName];
|
|
13553
|
+
}
|
|
13404
13554
|
async envType(propertyName) {
|
|
13405
13555
|
const agentBuilder = this._getAgentBuilderStub();
|
|
13406
13556
|
if (typeof agentBuilder.resolveThreadEnvType === "function") {
|
|
@@ -14431,12 +14581,11 @@ var init_ThreadStateImpl = __esm({
|
|
|
14431
14581
|
* dispatcher recorded in the runtime context instead.
|
|
14432
14582
|
*/
|
|
14433
14583
|
_getRuntimeEnv() {
|
|
14434
|
-
|
|
14435
|
-
|
|
14436
|
-
return contextEnv;
|
|
14584
|
+
if (isThreadStateRuntimeEnvironment(this._runtimeContext.env)) {
|
|
14585
|
+
return this._runtimeContext.env;
|
|
14437
14586
|
}
|
|
14438
14587
|
const instanceEnv = this._threadInstance.env;
|
|
14439
|
-
if (!instanceEnv
|
|
14588
|
+
if (!isThreadStateRuntimeEnvironment(instanceEnv)) {
|
|
14440
14589
|
throw new Error(
|
|
14441
14590
|
"No runtime environment available on this ThreadState. Stub-backed states must be constructed with runtimeContext.env."
|
|
14442
14591
|
);
|
|
@@ -14453,9 +14602,7 @@ var init_ThreadStateImpl = __esm({
|
|
|
14453
14602
|
return namespace.get(namespace.idFromName(userId));
|
|
14454
14603
|
}
|
|
14455
14604
|
// ─────────────────────────────────────────────────────────────────────────
|
|
14456
|
-
// Skills —
|
|
14457
|
-
// DurableAgentBuilder singleton (skills are never stored per-thread or on
|
|
14458
|
-
// a client machine).
|
|
14605
|
+
// Skills — owner-scoped library plus explicitly shared administrator skills.
|
|
14459
14606
|
// ─────────────────────────────────────────────────────────────────────────
|
|
14460
14607
|
/**
|
|
14461
14608
|
* The profile of the user this thread belongs to (identity fields only —
|
|
@@ -14478,9 +14625,8 @@ var init_ThreadStateImpl = __esm({
|
|
|
14478
14625
|
"Reentrant user-scope call: this ThreadState was raised from user-scope code; state.user().call() is unavailable here."
|
|
14479
14626
|
);
|
|
14480
14627
|
}
|
|
14481
|
-
const path21 = endpoint.replace(/^\/+/, "");
|
|
14482
14628
|
return hub().fetch(
|
|
14483
|
-
new Request(
|
|
14629
|
+
new Request(confinedUserEndpointUrl(userId, endpoint), {
|
|
14484
14630
|
method: "POST",
|
|
14485
14631
|
headers: {
|
|
14486
14632
|
"Content-Type": "application/json",
|
|
@@ -14540,18 +14686,42 @@ var init_ThreadStateImpl = __esm({
|
|
|
14540
14686
|
}
|
|
14541
14687
|
get skills() {
|
|
14542
14688
|
const stub = this._getAgentBuilderStub();
|
|
14689
|
+
const ownerId = this._metadata.user_id ?? SHARED_SKILL_OWNER;
|
|
14690
|
+
const scope = {
|
|
14691
|
+
ownerId,
|
|
14692
|
+
includeShared: ownerId !== SHARED_SKILL_OWNER
|
|
14693
|
+
};
|
|
14694
|
+
const requireMutationAdmin = async () => {
|
|
14695
|
+
const userId = this._metadata.user_id;
|
|
14696
|
+
if (!userId) {
|
|
14697
|
+
throw new Error("Skill mutations require an authenticated instance admin.");
|
|
14698
|
+
}
|
|
14699
|
+
const user = await stub.getUserById(userId);
|
|
14700
|
+
if (user?.role !== "admin") {
|
|
14701
|
+
throw new Error("Skill mutations require an instance admin.");
|
|
14702
|
+
}
|
|
14703
|
+
};
|
|
14543
14704
|
return {
|
|
14544
|
-
list: (options) => stub.listSkills(options),
|
|
14705
|
+
list: (options) => stub.listSkills(scope, options),
|
|
14545
14706
|
get: async (name) => {
|
|
14546
|
-
const metadata = await stub.getSkill(name);
|
|
14707
|
+
const metadata = await stub.getSkill(scope, name);
|
|
14547
14708
|
if (!metadata) return null;
|
|
14548
|
-
const skillMd = await stub.getSkillFile(name, "SKILL.md") ?? "";
|
|
14709
|
+
const skillMd = await stub.getSkillFile(scope, name, "SKILL.md") ?? "";
|
|
14549
14710
|
return { metadata, skillMd };
|
|
14550
14711
|
},
|
|
14551
|
-
readFile: (name, path21) => stub.getSkillFile(name, path21),
|
|
14552
|
-
install: (files) =>
|
|
14553
|
-
|
|
14554
|
-
|
|
14712
|
+
readFile: (name, path21) => stub.getSkillFile(scope, name, path21),
|
|
14713
|
+
install: async (files) => {
|
|
14714
|
+
await requireMutationAdmin();
|
|
14715
|
+
return stub.upsertSkill(scope, files);
|
|
14716
|
+
},
|
|
14717
|
+
setEnabled: async (name, enabled) => {
|
|
14718
|
+
await requireMutationAdmin();
|
|
14719
|
+
return stub.setSkillEnabled(scope, name, enabled);
|
|
14720
|
+
},
|
|
14721
|
+
remove: async (name) => {
|
|
14722
|
+
await requireMutationAdmin();
|
|
14723
|
+
return stub.deleteSkill(scope, name);
|
|
14724
|
+
}
|
|
14555
14725
|
};
|
|
14556
14726
|
}
|
|
14557
14727
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -15389,6 +15559,27 @@ declare module 'virtual:@standardagents/builder' {
|
|
|
15389
15559
|
validateSession(tokenHash: string): Promise<{ user_id: string; expires_at: number } | null>;
|
|
15390
15560
|
cleanupSessions(): Promise<number>;
|
|
15391
15561
|
deleteSession(tokenHash: string): Promise<void>;
|
|
15562
|
+
createSuperAdminSession(params: {
|
|
15563
|
+
token_hash: string;
|
|
15564
|
+
expires_at: number;
|
|
15565
|
+
}): Promise<void>;
|
|
15566
|
+
validateSuperAdminSession(tokenHash: string): Promise<boolean>;
|
|
15567
|
+
deleteSuperAdminSession(tokenHash: string): Promise<void>;
|
|
15568
|
+
createWebSocketTicket(params: {
|
|
15569
|
+
ticket_hash: string;
|
|
15570
|
+
pathname: string;
|
|
15571
|
+
auth_json: string;
|
|
15572
|
+
expires_at: number;
|
|
15573
|
+
}): Promise<void>;
|
|
15574
|
+
consumeWebSocketTicket(params: {
|
|
15575
|
+
ticket_hash: string;
|
|
15576
|
+
pathname: string;
|
|
15577
|
+
}): Promise<string | null>;
|
|
15578
|
+
consumeAuthRateLimit(params: {
|
|
15579
|
+
key: string;
|
|
15580
|
+
limit: number;
|
|
15581
|
+
window_seconds: number;
|
|
15582
|
+
}): Promise<{ allowed: boolean; retry_after: number; remaining: number }>;
|
|
15392
15583
|
|
|
15393
15584
|
// API key methods
|
|
15394
15585
|
createApiKey(params: {
|
|
@@ -15990,6 +16181,92 @@ function resolveLocalBootstrapCookie(projectRoot, routePath, cookieHeader) {
|
|
|
15990
16181
|
return findLocalBootstrapSession(projectRoot)?.session.session_cookie ?? null;
|
|
15991
16182
|
}
|
|
15992
16183
|
|
|
16184
|
+
// src/utils/auth.ts
|
|
16185
|
+
var SUPER_ADMIN_TOKEN_TTL_SECONDS = 12 * 60 * 60;
|
|
16186
|
+
async function hashToken(token) {
|
|
16187
|
+
const encoder = new TextEncoder();
|
|
16188
|
+
const data = encoder.encode(token);
|
|
16189
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
16190
|
+
const hashArray = new Uint8Array(hashBuffer);
|
|
16191
|
+
return Array.from(hashArray, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
16192
|
+
}
|
|
16193
|
+
var SESSION_COOKIE_NAME = "agtuser_session";
|
|
16194
|
+
function readSessionCookie(request) {
|
|
16195
|
+
const header = request.headers.get("Cookie");
|
|
16196
|
+
if (!header) return null;
|
|
16197
|
+
for (const part of header.split(";")) {
|
|
16198
|
+
const eq = part.indexOf("=");
|
|
16199
|
+
if (eq === -1) continue;
|
|
16200
|
+
if (part.slice(0, eq).trim() === SESSION_COOKIE_NAME) {
|
|
16201
|
+
return decodeURIComponent(part.slice(eq + 1).trim()) || null;
|
|
16202
|
+
}
|
|
16203
|
+
}
|
|
16204
|
+
return null;
|
|
16205
|
+
}
|
|
16206
|
+
function isValidUserToken(token) {
|
|
16207
|
+
return token.startsWith("agtuser_") && token.length > 10;
|
|
16208
|
+
}
|
|
16209
|
+
function isValidApiKey(key) {
|
|
16210
|
+
return key.startsWith("agtbldr_") && key.length > 10 || key.startsWith("sak_live_") && key.length > 10;
|
|
16211
|
+
}
|
|
16212
|
+
async function verifySignedToken(signedToken, encryptionKey) {
|
|
16213
|
+
try {
|
|
16214
|
+
const parts = signedToken.split(".");
|
|
16215
|
+
if (parts.length !== 2) {
|
|
16216
|
+
return null;
|
|
16217
|
+
}
|
|
16218
|
+
const [payloadB64, signatureB64] = parts;
|
|
16219
|
+
const encoder = new TextEncoder();
|
|
16220
|
+
const keyData = encoder.encode(encryptionKey);
|
|
16221
|
+
const cryptoKey = await crypto.subtle.importKey(
|
|
16222
|
+
"raw",
|
|
16223
|
+
keyData,
|
|
16224
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
16225
|
+
false,
|
|
16226
|
+
["verify"]
|
|
16227
|
+
);
|
|
16228
|
+
const payloadData = base64ToBuffer(payloadB64);
|
|
16229
|
+
const signature = base64ToBuffer(signatureB64);
|
|
16230
|
+
const isValid = await crypto.subtle.verify("HMAC", cryptoKey, signature, payloadData);
|
|
16231
|
+
if (!isValid) {
|
|
16232
|
+
return null;
|
|
16233
|
+
}
|
|
16234
|
+
const decoder = new TextDecoder();
|
|
16235
|
+
return decoder.decode(payloadData);
|
|
16236
|
+
} catch (error) {
|
|
16237
|
+
console.error("Error verifying signed token:", error);
|
|
16238
|
+
return null;
|
|
16239
|
+
}
|
|
16240
|
+
}
|
|
16241
|
+
async function verifySuperAdminToken(token, encryptionKey) {
|
|
16242
|
+
const payload = await verifySignedToken(token, encryptionKey);
|
|
16243
|
+
if (!payload) {
|
|
16244
|
+
return false;
|
|
16245
|
+
}
|
|
16246
|
+
const parts = payload.split(":");
|
|
16247
|
+
if (parts.length !== 3 || parts[0] !== "super_admin") {
|
|
16248
|
+
return false;
|
|
16249
|
+
}
|
|
16250
|
+
const timestamp = parseInt(parts[1], 10);
|
|
16251
|
+
if (!Number.isFinite(timestamp)) {
|
|
16252
|
+
return false;
|
|
16253
|
+
}
|
|
16254
|
+
const age = Date.now() - timestamp;
|
|
16255
|
+
const maxAge = SUPER_ADMIN_TOKEN_TTL_SECONDS * 1e3;
|
|
16256
|
+
if (age < 0 || age > maxAge) {
|
|
16257
|
+
return false;
|
|
16258
|
+
}
|
|
16259
|
+
return true;
|
|
16260
|
+
}
|
|
16261
|
+
function base64ToBuffer(base64) {
|
|
16262
|
+
const binary = atob(base64);
|
|
16263
|
+
const bytes = new Uint8Array(binary.length);
|
|
16264
|
+
for (let i = 0; i < binary.length; i++) {
|
|
16265
|
+
bytes[i] = binary.charCodeAt(i);
|
|
16266
|
+
}
|
|
16267
|
+
return bytes;
|
|
16268
|
+
}
|
|
16269
|
+
|
|
15993
16270
|
// src/sdk/generators/generateModelFile.ts
|
|
15994
16271
|
function generateModelFile(data, options) {
|
|
15995
16272
|
const { providerName, providerPackage, defaultImport } = options;
|
|
@@ -16055,7 +16332,7 @@ function generateModelFile(data, options) {
|
|
|
16055
16332
|
return lines.join("\n");
|
|
16056
16333
|
}
|
|
16057
16334
|
function escapeString(str) {
|
|
16058
|
-
return str.replace(
|
|
16335
|
+
return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
16059
16336
|
}
|
|
16060
16337
|
|
|
16061
16338
|
// src/sdk/generators/jsonSchemaToZod.ts
|
|
@@ -16586,7 +16863,7 @@ function formatSideConfig(config) {
|
|
|
16586
16863
|
return parts.join("\n");
|
|
16587
16864
|
}
|
|
16588
16865
|
function escapeString3(str) {
|
|
16589
|
-
return str.replace(
|
|
16866
|
+
return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
16590
16867
|
}
|
|
16591
16868
|
function formatSessionBinding(binding) {
|
|
16592
16869
|
if (typeof binding === "string") {
|
|
@@ -21408,18 +21685,13 @@ var PUBLIC_ROUTES = /* @__PURE__ */ new Set([
|
|
|
21408
21685
|
"/api/auth/oauth/github",
|
|
21409
21686
|
"/api/auth/oauth/google",
|
|
21410
21687
|
"/api/auth/oauth/github/callback",
|
|
21411
|
-
"/api/auth/oauth/google/callback"
|
|
21412
|
-
"/api/hooks"
|
|
21688
|
+
"/api/auth/oauth/google/callback"
|
|
21413
21689
|
]);
|
|
21414
21690
|
function isProviderIconRoute(routePath) {
|
|
21415
21691
|
return /^\/api\/providers\/[^/]+\/icon$/.test(routePath);
|
|
21416
21692
|
}
|
|
21417
|
-
function isPublicThreadFileRoute(routePath, method) {
|
|
21418
|
-
if (method !== "GET" && method !== "HEAD") return false;
|
|
21419
|
-
return /^\/api\/threads\/[^/]+\/public\/.+/.test(routePath);
|
|
21420
|
-
}
|
|
21421
21693
|
function isHostedInstance(env) {
|
|
21422
|
-
const value = env
|
|
21694
|
+
const value = env && typeof env === "object" ? Reflect.get(env, "STANDARD_AGENTS_HOSTED") : void 0;
|
|
21423
21695
|
if (typeof value === "string") {
|
|
21424
21696
|
const normalized = value.trim().toLowerCase();
|
|
21425
21697
|
return normalized !== "" && normalized !== "0" && normalized !== "false";
|
|
@@ -21444,13 +21716,54 @@ function isPublicApiRoute(routePath, request, env) {
|
|
|
21444
21716
|
if (isProviderIconRoute(routePath)) {
|
|
21445
21717
|
return true;
|
|
21446
21718
|
}
|
|
21447
|
-
|
|
21719
|
+
return routePath === "/api/platform-auth/bootstrap" && isTrustedLocalDevelopmentRequest(request, env);
|
|
21720
|
+
}
|
|
21721
|
+
function requestObservedOrigins(request) {
|
|
21722
|
+
const origins = /* @__PURE__ */ new Set();
|
|
21723
|
+
let url = null;
|
|
21724
|
+
try {
|
|
21725
|
+
url = new URL(request.url);
|
|
21726
|
+
origins.add(url.origin);
|
|
21727
|
+
} catch {
|
|
21728
|
+
url = null;
|
|
21729
|
+
}
|
|
21730
|
+
const forwardedHost = request.headers.get("x-forwarded-host")?.split(",")[0]?.trim();
|
|
21731
|
+
const host = forwardedHost || request.headers.get("host")?.trim();
|
|
21732
|
+
if (!host) return origins;
|
|
21733
|
+
const forwardedProto = request.headers.get("x-forwarded-proto")?.split(",")[0]?.trim().toLowerCase();
|
|
21734
|
+
const schemes = /* @__PURE__ */ new Set();
|
|
21735
|
+
if (forwardedProto === "http" || forwardedProto === "https") schemes.add(forwardedProto);
|
|
21736
|
+
if (url) schemes.add(url.protocol.replace(":", ""));
|
|
21737
|
+
if (schemes.size === 0) {
|
|
21738
|
+
schemes.add(host.endsWith(".orb.local") ? "https" : "http");
|
|
21739
|
+
}
|
|
21740
|
+
for (const scheme of schemes) {
|
|
21741
|
+
try {
|
|
21742
|
+
origins.add(new URL(`${scheme}://${host}`).origin);
|
|
21743
|
+
} catch {
|
|
21744
|
+
}
|
|
21745
|
+
}
|
|
21746
|
+
return origins;
|
|
21747
|
+
}
|
|
21748
|
+
function isSameOriginBrowserRequest(request) {
|
|
21749
|
+
const fetchSite = request.headers.get("Sec-Fetch-Site")?.trim().toLowerCase();
|
|
21750
|
+
if (fetchSite === "same-origin" || fetchSite === "none") {
|
|
21448
21751
|
return true;
|
|
21449
21752
|
}
|
|
21450
|
-
if (
|
|
21451
|
-
|
|
21753
|
+
if (fetchSite) {
|
|
21754
|
+
return false;
|
|
21755
|
+
}
|
|
21756
|
+
const origin = request.headers.get("Origin")?.trim();
|
|
21757
|
+
if (!origin) return true;
|
|
21758
|
+
if (origin === "null") return false;
|
|
21759
|
+
try {
|
|
21760
|
+
return requestObservedOrigins(request).has(new URL(origin).origin);
|
|
21761
|
+
} catch {
|
|
21762
|
+
return false;
|
|
21763
|
+
}
|
|
21452
21764
|
}
|
|
21453
21765
|
function isAdminOnlyApiRoute(routePath, method = "GET") {
|
|
21766
|
+
if (routePath === "/api/hooks") return true;
|
|
21454
21767
|
if (routePath === "/api/skills" || routePath.startsWith("/api/skills/")) return true;
|
|
21455
21768
|
if (routePath === "/api/pack" || routePath.startsWith("/api/pack/")) return true;
|
|
21456
21769
|
if (routePath === "/api/unpack" || routePath.startsWith("/api/unpack/")) return true;
|
|
@@ -21469,6 +21782,9 @@ function isAdminOnlyApiRoute(routePath, method = "GET") {
|
|
|
21469
21782
|
}
|
|
21470
21783
|
|
|
21471
21784
|
// src/plugin/dev-middleware.ts
|
|
21785
|
+
function isEncryptedSocket(socket) {
|
|
21786
|
+
return "encrypted" in socket && socket.encrypted === true;
|
|
21787
|
+
}
|
|
21472
21788
|
function readRawRequestBody(req) {
|
|
21473
21789
|
return new Promise((resolve3, reject) => {
|
|
21474
21790
|
const chunks = [];
|
|
@@ -21493,6 +21809,36 @@ function resolveContainedFile(baseDir, requestPath) {
|
|
|
21493
21809
|
return null;
|
|
21494
21810
|
}
|
|
21495
21811
|
}
|
|
21812
|
+
function firstHeader(value) {
|
|
21813
|
+
return Array.isArray(value) ? value[0] : value;
|
|
21814
|
+
}
|
|
21815
|
+
function isSameOriginDevRequest(req) {
|
|
21816
|
+
const fetchSite = firstHeader(req.headers["sec-fetch-site"])?.trim().toLowerCase();
|
|
21817
|
+
if (fetchSite && fetchSite !== "same-origin" && fetchSite !== "none") {
|
|
21818
|
+
return false;
|
|
21819
|
+
}
|
|
21820
|
+
const origin = firstHeader(req.headers.origin)?.trim();
|
|
21821
|
+
if (!origin) return true;
|
|
21822
|
+
if (origin === "null") return false;
|
|
21823
|
+
try {
|
|
21824
|
+
const forwardedProtocol = firstHeader(req.headers["x-forwarded-proto"])?.split(",")[0]?.trim().toLowerCase();
|
|
21825
|
+
const protocol = forwardedProtocol === "https" || forwardedProtocol === "http" ? forwardedProtocol : isEncryptedSocket(req.socket) ? "https" : "http";
|
|
21826
|
+
const requestOrigin = new URL(`${protocol}://${String(req.headers.host || "").trim()}`).origin;
|
|
21827
|
+
return new URL(origin).origin === requestOrigin;
|
|
21828
|
+
} catch {
|
|
21829
|
+
return false;
|
|
21830
|
+
}
|
|
21831
|
+
}
|
|
21832
|
+
function hasDevAuthoringCredential(req) {
|
|
21833
|
+
const cookie = firstHeader(req.headers.cookie) || "";
|
|
21834
|
+
const hasSession = cookie.split(";").some((part) => {
|
|
21835
|
+
const separator = part.indexOf("=");
|
|
21836
|
+
return separator !== -1 && part.slice(0, separator).trim() === SESSION_COOKIE_NAME && part.slice(separator + 1).trim() !== "";
|
|
21837
|
+
});
|
|
21838
|
+
if (hasSession) return true;
|
|
21839
|
+
const authorization = firstHeader(req.headers.authorization)?.trim() || "";
|
|
21840
|
+
return /^Bearer\s+\S+$/.test(authorization);
|
|
21841
|
+
}
|
|
21496
21842
|
function injectUiConfigIntoHtml(htmlContent) {
|
|
21497
21843
|
const configScript = `<script>window.__AGENTBUILDER_CONFIG__ = { devMode: true };</script>`;
|
|
21498
21844
|
return htmlContent.replace(/\/agents\//g, "/").replace("</head>", `${configScript}</head>`);
|
|
@@ -21547,11 +21893,21 @@ function createDevMiddleware(server, context) {
|
|
|
21547
21893
|
);
|
|
21548
21894
|
} catch {
|
|
21549
21895
|
}
|
|
21550
|
-
if (isAdminOnlyApiRoute(routePath, method)
|
|
21551
|
-
|
|
21552
|
-
|
|
21553
|
-
|
|
21554
|
-
|
|
21896
|
+
if (isAdminOnlyApiRoute(routePath, method)) {
|
|
21897
|
+
if (!trustedLocalHost || !isSameOriginDevRequest(req)) {
|
|
21898
|
+
res.statusCode = 403;
|
|
21899
|
+
res.setHeader("Content-Type", "application/json");
|
|
21900
|
+
res.setHeader("Cache-Control", "no-store");
|
|
21901
|
+
res.end(JSON.stringify({ error: "Authoring APIs require a same-origin local request" }));
|
|
21902
|
+
return;
|
|
21903
|
+
}
|
|
21904
|
+
if (!hasDevAuthoringCredential(req)) {
|
|
21905
|
+
res.statusCode = 401;
|
|
21906
|
+
res.setHeader("Content-Type", "application/json");
|
|
21907
|
+
res.setHeader("Cache-Control", "no-store");
|
|
21908
|
+
res.end(JSON.stringify({ error: "Authoring APIs require an authenticated local session" }));
|
|
21909
|
+
return;
|
|
21910
|
+
}
|
|
21555
21911
|
}
|
|
21556
21912
|
if (await handleConfigApiRequest({
|
|
21557
21913
|
req,
|
|
@@ -22200,14 +22556,21 @@ import { prompts, promptNames } from "virtual:@standardagents-prompts";
|
|
|
22200
22556
|
import { agents, agentNames } from "virtual:@standardagents-agents";
|
|
22201
22557
|
import { registry } from "virtual:@standardagents-registry";
|
|
22202
22558
|
import {
|
|
22559
|
+
authenticate,
|
|
22203
22560
|
requireAuth,
|
|
22204
22561
|
createThreadEndpointHandler,
|
|
22205
22562
|
canAccessThread,
|
|
22563
|
+
endpointCallerFromAuth,
|
|
22206
22564
|
isHostedInstance,
|
|
22565
|
+
isTrustedLocalDevelopmentRequest,
|
|
22566
|
+
isSameOriginBrowserRequest,
|
|
22207
22567
|
isPublicApiRoute,
|
|
22208
22568
|
isAdminOnlyApiRoute,
|
|
22569
|
+
applyRuntimeCors,
|
|
22570
|
+
runtimeCorsHeaders,
|
|
22571
|
+
runtimePreflightResponse,
|
|
22209
22572
|
} from "@standardagents/builder/runtime";
|
|
22210
|
-
import { isThreadEndpoint } from "@standardagents/spec";
|
|
22573
|
+
import { isThreadEndpoint, getEndpointAccess } from "@standardagents/spec";
|
|
22211
22574
|
|
|
22212
22575
|
function platformEndpoint(env) {
|
|
22213
22576
|
const configured =
|
|
@@ -22262,71 +22625,34 @@ function isHtmlNavigationRequest(request) {
|
|
|
22262
22625
|
return (accept === '' || accept === '*/*') && isUiNavigationPath(pathname);
|
|
22263
22626
|
}
|
|
22264
22627
|
|
|
22265
|
-
// CORS headers for API responses
|
|
22266
|
-
const CORS_HEADERS = {
|
|
22267
|
-
"Access-Control-Allow-Origin": "*",
|
|
22268
|
-
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
22269
|
-
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With",
|
|
22270
|
-
"Access-Control-Max-Age": "86400",
|
|
22271
|
-
};
|
|
22272
|
-
|
|
22273
22628
|
// Helper to create headers with CORS
|
|
22274
|
-
function corsHeaders(contentType) {
|
|
22629
|
+
function corsHeaders(contentType, request, env) {
|
|
22275
22630
|
return {
|
|
22276
22631
|
"Content-Type": contentType,
|
|
22277
|
-
...
|
|
22632
|
+
...runtimeCorsHeaders(request, env),
|
|
22278
22633
|
};
|
|
22279
22634
|
}
|
|
22280
22635
|
|
|
22281
|
-
// Helper to add CORS headers to any Response without touching the body
|
|
22282
|
-
function addCorsHeaders(response) {
|
|
22283
|
-
// Skip WebSocket upgrade responses - they can't be wrapped
|
|
22284
|
-
if (response.status === 101) {
|
|
22285
|
-
return response;
|
|
22286
|
-
}
|
|
22287
|
-
|
|
22288
|
-
// Skip if already has CORS headers
|
|
22289
|
-
if (response.headers.has("Access-Control-Allow-Origin")) {
|
|
22290
|
-
return response;
|
|
22291
|
-
}
|
|
22292
|
-
|
|
22293
|
-
// Prefer mutating the original Response headers. Rebuilding a Response from
|
|
22294
|
-
// response.headers can drop special headers such as Set-Cookie in Worker
|
|
22295
|
-
// runtimes, even when the header is not visible through Headers.has().
|
|
22296
|
-
try {
|
|
22297
|
-
for (const [key, value] of Object.entries(CORS_HEADERS)) {
|
|
22298
|
-
response.headers.set(key, value);
|
|
22299
|
-
}
|
|
22300
|
-
return response;
|
|
22301
|
-
} catch {
|
|
22302
|
-
// Some Responses, especially fetched upstream responses, have immutable
|
|
22303
|
-
// headers. Fall back to wrapping those responses.
|
|
22304
|
-
}
|
|
22305
|
-
|
|
22306
|
-
// Create new headers with CORS added
|
|
22307
|
-
const newHeaders = new Headers(response.headers);
|
|
22308
|
-
for (const [key, value] of Object.entries(CORS_HEADERS)) {
|
|
22309
|
-
newHeaders.set(key, value);
|
|
22310
|
-
}
|
|
22311
|
-
|
|
22312
|
-
// Return new Response with same body stream (not cloned, just transferred)
|
|
22313
|
-
return new Response(response.body, {
|
|
22314
|
-
status: response.status,
|
|
22315
|
-
statusText: response.statusText,
|
|
22316
|
-
headers: newHeaders,
|
|
22317
|
-
});
|
|
22318
|
-
}
|
|
22319
|
-
|
|
22320
22636
|
export async function router(request, env) {
|
|
22321
22637
|
const url = new URL(request.url);
|
|
22322
22638
|
const pathname = url.pathname;
|
|
22323
22639
|
|
|
22324
22640
|
// Handle CORS preflight requests
|
|
22325
22641
|
if (request.method === "OPTIONS") {
|
|
22326
|
-
return
|
|
22327
|
-
|
|
22328
|
-
|
|
22329
|
-
|
|
22642
|
+
return runtimePreflightResponse(request, env);
|
|
22643
|
+
}
|
|
22644
|
+
|
|
22645
|
+
// WebSockets do not use CORS. Reject foreign browser provenance before
|
|
22646
|
+
// route dispatch so a malicious page cannot open a local stream/bridge even
|
|
22647
|
+
// if a browser were to attach ambient cookies to the handshake.
|
|
22648
|
+
if (
|
|
22649
|
+
isTrustedLocalDevelopmentRequest(request, env) &&
|
|
22650
|
+
!isSameOriginBrowserRequest(request)
|
|
22651
|
+
) {
|
|
22652
|
+
return Response.json(
|
|
22653
|
+
{ error: "Cross-origin access is disabled for local development" },
|
|
22654
|
+
{ status: 403, headers: { "Cache-Control": "no-store" } }
|
|
22655
|
+
);
|
|
22330
22656
|
}
|
|
22331
22657
|
|
|
22332
22658
|
const routePath = pathname || "/";
|
|
@@ -22350,8 +22676,16 @@ ${packedThreadRouteCode}
|
|
|
22350
22676
|
);
|
|
22351
22677
|
|
|
22352
22678
|
if (routeMatch) {
|
|
22679
|
+
let controller = await routeMatch.data();
|
|
22680
|
+
|
|
22681
|
+
// Endpoint-declared access (spec: EndpointAccess). A 'public' declaration
|
|
22682
|
+
// opts that one endpoint out of the router's authentication gate; the
|
|
22683
|
+
// handler still receives the resolved caller identity when the request
|
|
22684
|
+
// presents verifiable credentials.
|
|
22685
|
+
const declaredPublic = getEndpointAccess(controller) === 'public';
|
|
22686
|
+
|
|
22353
22687
|
// Check if authentication is required for this route
|
|
22354
|
-
const publicRoute = isPublicApiRoute(routePath, request, env);
|
|
22688
|
+
const publicRoute = declaredPublic || isPublicApiRoute(routePath, request, env);
|
|
22355
22689
|
const isApiRoute = routePath.startsWith('/api/');
|
|
22356
22690
|
|
|
22357
22691
|
let authContext = null;
|
|
@@ -22366,13 +22700,13 @@ ${packedThreadRouteCode}
|
|
|
22366
22700
|
|
|
22367
22701
|
// If requireAuth returns a Response, it's an error (401)
|
|
22368
22702
|
if (authResult instanceof Response) {
|
|
22369
|
-
return
|
|
22703
|
+
return applyRuntimeCors(authResult, request, env);
|
|
22370
22704
|
}
|
|
22371
22705
|
|
|
22372
22706
|
authContext = authResult;
|
|
22373
22707
|
|
|
22374
22708
|
if (isAdminOnlyApiRoute(routePath, request.method) && authContext.user.role !== 'admin') {
|
|
22375
|
-
return
|
|
22709
|
+
return applyRuntimeCors(Response.json({ error: 'Forbidden: Admin access required' }, { status: 403 }), request, env);
|
|
22376
22710
|
}
|
|
22377
22711
|
|
|
22378
22712
|
if (routePath.startsWith('/api/threads/')) {
|
|
@@ -22386,18 +22720,32 @@ ${packedThreadRouteCode}
|
|
|
22386
22720
|
const agentBuilder = env.AGENT_BUILDER.get(agentBuilderId);
|
|
22387
22721
|
const thread = await agentBuilder.getThread(threadId);
|
|
22388
22722
|
if (!thread) {
|
|
22389
|
-
return
|
|
22723
|
+
return applyRuntimeCors(Response.json({ error: \`Thread not found: \${threadId}\` }, { status: 404 }), request, env);
|
|
22390
22724
|
}
|
|
22391
22725
|
if (!canAccessThread(authContext, thread)) {
|
|
22392
|
-
return
|
|
22726
|
+
return applyRuntimeCors(Response.json({ error: "Forbidden: You don't have access to this thread" }, { status: 403 }), request, env);
|
|
22393
22727
|
}
|
|
22394
22728
|
threadAccess = { threadId, thread };
|
|
22395
22729
|
}
|
|
22396
22730
|
}
|
|
22731
|
+
} else if (isApiRoute && declaredPublic) {
|
|
22732
|
+
// Optional caller resolution for endpoint-declared public routes:
|
|
22733
|
+
// verifiable credentials attribute the request, anything else stays
|
|
22734
|
+
// anonymous instead of being rejected (spec ThreadEndpointAuth).
|
|
22735
|
+
authContext = await authenticate(request, env);
|
|
22736
|
+
if (authContext && routePath.startsWith('/api/threads/')) {
|
|
22737
|
+
const threadId = routeMatch.params?.threadId || routeMatch.params?.id;
|
|
22738
|
+
if (threadId) {
|
|
22739
|
+
const agentBuilderId = env.AGENT_BUILDER.idFromName('singleton');
|
|
22740
|
+
const agentBuilder = env.AGENT_BUILDER.get(agentBuilderId);
|
|
22741
|
+
const thread = await agentBuilder.getThread(threadId);
|
|
22742
|
+
if (thread && canAccessThread(authContext, thread)) {
|
|
22743
|
+
threadAccess = { threadId, thread };
|
|
22744
|
+
}
|
|
22745
|
+
}
|
|
22746
|
+
}
|
|
22397
22747
|
}
|
|
22398
22748
|
|
|
22399
|
-
let controller = await routeMatch.data();
|
|
22400
|
-
|
|
22401
22749
|
// Check if this is a thread endpoint (marked by defineThreadEndpoint)
|
|
22402
22750
|
// If so, wrap it with createThreadEndpointHandler to provide ThreadState
|
|
22403
22751
|
if (isThreadEndpoint(controller)) {
|
|
@@ -22412,21 +22760,22 @@ ${packedThreadRouteCode}
|
|
|
22412
22760
|
config,
|
|
22413
22761
|
tools,
|
|
22414
22762
|
auth: authContext, // Add auth context to controller context
|
|
22763
|
+
caller: endpointCallerFromAuth(authContext), // Spec ControllerContext.caller
|
|
22415
22764
|
threadAccess, // Router's thread authorization, bound to the exact id
|
|
22416
22765
|
};
|
|
22417
22766
|
const result = await controller(context);
|
|
22418
22767
|
|
|
22419
22768
|
if (result instanceof Response) {
|
|
22420
|
-
return
|
|
22769
|
+
return applyRuntimeCors(result, request, env);
|
|
22421
22770
|
}
|
|
22422
22771
|
if (typeof result === "string") {
|
|
22423
22772
|
return new Response(result, {
|
|
22424
|
-
headers: corsHeaders("text/plain"),
|
|
22773
|
+
headers: corsHeaders("text/plain", request, env),
|
|
22425
22774
|
});
|
|
22426
22775
|
}
|
|
22427
22776
|
// JSON responses get CORS headers
|
|
22428
22777
|
return new Response(JSON.stringify(result), {
|
|
22429
|
-
headers: corsHeaders("application/json"),
|
|
22778
|
+
headers: corsHeaders("application/json", request, env),
|
|
22430
22779
|
});
|
|
22431
22780
|
}
|
|
22432
22781
|
|
|
@@ -23192,144 +23541,82 @@ var builder = agentbuilder;
|
|
|
23192
23541
|
|
|
23193
23542
|
// src/router/index.ts
|
|
23194
23543
|
init_ThreadStateImpl();
|
|
23195
|
-
function defineController2(controller) {
|
|
23196
|
-
return controller;
|
|
23197
|
-
}
|
|
23198
|
-
function createThreadEndpointHandler(handler) {
|
|
23199
|
-
return (async ({ req, params, env }) => {
|
|
23200
|
-
const threadId = params.threadId ?? params.id;
|
|
23201
|
-
if (!threadId) {
|
|
23202
|
-
return Response.json({ error: "Thread ID required" }, { status: 400 });
|
|
23203
|
-
}
|
|
23204
|
-
try {
|
|
23205
|
-
const agentBuilderId = env.AGENT_BUILDER.idFromName("singleton");
|
|
23206
|
-
const agentBuilder = env.AGENT_BUILDER.get(agentBuilderId);
|
|
23207
|
-
const metadata = await agentBuilder.getThread(threadId);
|
|
23208
|
-
if (!metadata) {
|
|
23209
|
-
return Response.json(
|
|
23210
|
-
{ error: `Thread not found: ${threadId}` },
|
|
23211
|
-
{ status: 404 }
|
|
23212
|
-
);
|
|
23213
|
-
}
|
|
23214
|
-
const meta = metadata;
|
|
23215
|
-
if (!meta.agent_id && meta.agent_name) {
|
|
23216
|
-
meta.agent_id = meta.agent_name;
|
|
23217
|
-
}
|
|
23218
|
-
const durableId = env.AGENT_BUILDER_THREAD.idFromName(threadId);
|
|
23219
|
-
const stub = env.AGENT_BUILDER_THREAD.get(durableId);
|
|
23220
|
-
const instance = stub;
|
|
23221
|
-
const threadState = ThreadStateImpl.fromThreadInstance(instance, metadata, {
|
|
23222
|
-
env
|
|
23223
|
-
});
|
|
23224
|
-
const endpointParams = {};
|
|
23225
|
-
for (const [key, value] of Object.entries(params)) {
|
|
23226
|
-
if (key === "threadId" || key === "id" && !params.threadId) {
|
|
23227
|
-
continue;
|
|
23228
|
-
}
|
|
23229
|
-
endpointParams[key === "_" ? "*" : key] = value;
|
|
23230
|
-
}
|
|
23231
|
-
const response = await handler(req, threadState, endpointParams);
|
|
23232
|
-
await threadState.flushPendingEventDeliveries();
|
|
23233
|
-
return response;
|
|
23234
|
-
} catch (error) {
|
|
23235
|
-
console.error(`Error in thread endpoint for ${threadId}:`, error);
|
|
23236
|
-
return Response.json(
|
|
23237
|
-
{ error: error.message || "Internal server error" },
|
|
23238
|
-
{ status: 500 }
|
|
23239
|
-
);
|
|
23240
|
-
}
|
|
23241
|
-
});
|
|
23242
|
-
}
|
|
23243
23544
|
|
|
23244
|
-
// src/
|
|
23245
|
-
|
|
23246
|
-
|
|
23247
|
-
|
|
23248
|
-
|
|
23249
|
-
|
|
23250
|
-
|
|
23251
|
-
|
|
23252
|
-
|
|
23253
|
-
|
|
23254
|
-
|
|
23255
|
-
|
|
23256
|
-
|
|
23257
|
-
|
|
23258
|
-
|
|
23259
|
-
|
|
23260
|
-
|
|
23545
|
+
// src/routing/cors.ts
|
|
23546
|
+
var RUNTIME_CORS_HEADERS = {
|
|
23547
|
+
"Access-Control-Allow-Origin": "*",
|
|
23548
|
+
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
23549
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With",
|
|
23550
|
+
"Access-Control-Max-Age": "86400"
|
|
23551
|
+
};
|
|
23552
|
+
var CORS_RESPONSE_HEADERS = [
|
|
23553
|
+
"Access-Control-Allow-Origin",
|
|
23554
|
+
"Access-Control-Allow-Methods",
|
|
23555
|
+
"Access-Control-Allow-Headers",
|
|
23556
|
+
"Access-Control-Max-Age",
|
|
23557
|
+
"Access-Control-Allow-Credentials",
|
|
23558
|
+
"Access-Control-Expose-Headers"
|
|
23559
|
+
];
|
|
23560
|
+
function mutateCorsHeaders(headers, exposeCors) {
|
|
23561
|
+
if (exposeCors) {
|
|
23562
|
+
for (const [key, value] of Object.entries(RUNTIME_CORS_HEADERS)) {
|
|
23563
|
+
if (!headers.has(key)) headers.set(key, value);
|
|
23261
23564
|
}
|
|
23565
|
+
return;
|
|
23566
|
+
}
|
|
23567
|
+
for (const key of CORS_RESPONSE_HEADERS) {
|
|
23568
|
+
headers.delete(key);
|
|
23262
23569
|
}
|
|
23263
|
-
return null;
|
|
23264
23570
|
}
|
|
23265
|
-
function
|
|
23266
|
-
return
|
|
23571
|
+
function exposesRuntimeCors(request, env) {
|
|
23572
|
+
return !isTrustedLocalDevelopmentRequest(request, env);
|
|
23267
23573
|
}
|
|
23268
|
-
function
|
|
23269
|
-
return
|
|
23574
|
+
function runtimeCorsHeaders(request, env) {
|
|
23575
|
+
return exposesRuntimeCors(request, env) ? { ...RUNTIME_CORS_HEADERS } : {};
|
|
23270
23576
|
}
|
|
23271
|
-
|
|
23577
|
+
function applyRuntimeCors(response, request, env) {
|
|
23578
|
+
if (response.status === 101) return response;
|
|
23579
|
+
const exposeCors = exposesRuntimeCors(request, env);
|
|
23272
23580
|
try {
|
|
23273
|
-
|
|
23274
|
-
|
|
23275
|
-
|
|
23276
|
-
}
|
|
23277
|
-
const [payloadB64, signatureB64] = parts;
|
|
23278
|
-
const encoder = new TextEncoder();
|
|
23279
|
-
const keyData = encoder.encode(encryptionKey);
|
|
23280
|
-
const cryptoKey = await crypto.subtle.importKey(
|
|
23281
|
-
"raw",
|
|
23282
|
-
keyData,
|
|
23283
|
-
{ name: "HMAC", hash: "SHA-256" },
|
|
23284
|
-
false,
|
|
23285
|
-
["verify"]
|
|
23286
|
-
);
|
|
23287
|
-
const payloadData = base64ToBuffer(payloadB64);
|
|
23288
|
-
const signature = base64ToBuffer(signatureB64);
|
|
23289
|
-
const isValid = await crypto.subtle.verify("HMAC", cryptoKey, signature, payloadData);
|
|
23290
|
-
if (!isValid) {
|
|
23291
|
-
return null;
|
|
23292
|
-
}
|
|
23293
|
-
const decoder = new TextDecoder();
|
|
23294
|
-
return decoder.decode(payloadData);
|
|
23295
|
-
} catch (error) {
|
|
23296
|
-
console.error("Error verifying signed token:", error);
|
|
23297
|
-
return null;
|
|
23298
|
-
}
|
|
23299
|
-
}
|
|
23300
|
-
async function verifySuperAdminToken(token, encryptionKey) {
|
|
23301
|
-
const payload = await verifySignedToken(token, encryptionKey);
|
|
23302
|
-
if (!payload) {
|
|
23303
|
-
return false;
|
|
23304
|
-
}
|
|
23305
|
-
const parts = payload.split(":");
|
|
23306
|
-
if (parts.length !== 3 || parts[0] !== "super_admin") {
|
|
23307
|
-
return false;
|
|
23308
|
-
}
|
|
23309
|
-
const timestamp = parseInt(parts[1], 10);
|
|
23310
|
-
const age = Date.now() - timestamp;
|
|
23311
|
-
const maxAge = 30 * 24 * 60 * 60 * 1e3;
|
|
23312
|
-
if (age > maxAge) {
|
|
23313
|
-
return false;
|
|
23581
|
+
mutateCorsHeaders(response.headers, exposeCors);
|
|
23582
|
+
return response;
|
|
23583
|
+
} catch {
|
|
23314
23584
|
}
|
|
23315
|
-
|
|
23585
|
+
const headers = new Headers(response.headers);
|
|
23586
|
+
mutateCorsHeaders(headers, exposeCors);
|
|
23587
|
+
return new Response(response.body, {
|
|
23588
|
+
status: response.status,
|
|
23589
|
+
statusText: response.statusText,
|
|
23590
|
+
headers
|
|
23591
|
+
});
|
|
23316
23592
|
}
|
|
23317
|
-
function
|
|
23318
|
-
|
|
23319
|
-
|
|
23320
|
-
|
|
23321
|
-
|
|
23593
|
+
function runtimePreflightResponse(request, env) {
|
|
23594
|
+
if (!exposesRuntimeCors(request, env)) {
|
|
23595
|
+
return Response.json(
|
|
23596
|
+
{ error: "Cross-origin access is disabled for local development" },
|
|
23597
|
+
{ status: 403, headers: { "Cache-Control": "no-store" } }
|
|
23598
|
+
);
|
|
23322
23599
|
}
|
|
23323
|
-
return
|
|
23600
|
+
return new Response(null, {
|
|
23601
|
+
status: 204,
|
|
23602
|
+
headers: RUNTIME_CORS_HEADERS
|
|
23603
|
+
});
|
|
23324
23604
|
}
|
|
23325
23605
|
|
|
23326
23606
|
// src/middleware/auth.ts
|
|
23327
|
-
|
|
23328
|
-
|
|
23329
|
-
|
|
23330
|
-
|
|
23331
|
-
|
|
23332
|
-
|
|
23607
|
+
function endpointCallerFromAuth(auth) {
|
|
23608
|
+
if (!auth) return null;
|
|
23609
|
+
const { id, username, role, permissions, email, display_name, avatar_url } = auth.user;
|
|
23610
|
+
return {
|
|
23611
|
+
id,
|
|
23612
|
+
username,
|
|
23613
|
+
role,
|
|
23614
|
+
permissions: permissions ?? null,
|
|
23615
|
+
email: email ?? null,
|
|
23616
|
+
display_name: display_name ?? null,
|
|
23617
|
+
avatar_url: avatar_url ?? null
|
|
23618
|
+
};
|
|
23619
|
+
}
|
|
23333
23620
|
function parsePermissions(value) {
|
|
23334
23621
|
if (Array.isArray(value)) return value.filter((p) => typeof p === "string");
|
|
23335
23622
|
if (typeof value === "string" && value.trim()) {
|
|
@@ -23350,43 +23637,88 @@ function extractBearerToken(request) {
|
|
|
23350
23637
|
if (cookieToken) {
|
|
23351
23638
|
return cookieToken;
|
|
23352
23639
|
}
|
|
23353
|
-
const isWebSocket = request.headers.get("upgrade")?.toLowerCase() === "websocket";
|
|
23354
|
-
if (isWebSocket) {
|
|
23355
|
-
try {
|
|
23356
|
-
const url = new URL(request.url);
|
|
23357
|
-
const token = url.searchParams.get("token");
|
|
23358
|
-
if (token) {
|
|
23359
|
-
return token;
|
|
23360
|
-
}
|
|
23361
|
-
} catch (err) {
|
|
23362
|
-
}
|
|
23363
|
-
}
|
|
23364
23640
|
return null;
|
|
23365
23641
|
}
|
|
23366
23642
|
function getAgentBuilder(env) {
|
|
23367
23643
|
const agentBuilderId = env.AGENT_BUILDER.idFromName("singleton");
|
|
23368
23644
|
return env.AGENT_BUILDER.get(agentBuilderId);
|
|
23369
23645
|
}
|
|
23646
|
+
function parseWebSocketTicketAuth(authJson) {
|
|
23647
|
+
let parsed;
|
|
23648
|
+
try {
|
|
23649
|
+
parsed = JSON.parse(authJson);
|
|
23650
|
+
} catch {
|
|
23651
|
+
return null;
|
|
23652
|
+
}
|
|
23653
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
23654
|
+
const record = parsed;
|
|
23655
|
+
if (!record.user || typeof record.user !== "object") return null;
|
|
23656
|
+
const user = record.user;
|
|
23657
|
+
if (typeof user.id !== "string" || typeof user.username !== "string" || typeof user.role !== "string" || record.authType !== "super_admin" && record.authType !== "session" && record.authType !== "api_key") {
|
|
23658
|
+
return null;
|
|
23659
|
+
}
|
|
23660
|
+
const permissions = user.permissions;
|
|
23661
|
+
if (permissions !== void 0 && permissions !== null && (!Array.isArray(permissions) || !permissions.every((permission) => typeof permission === "string"))) {
|
|
23662
|
+
return null;
|
|
23663
|
+
}
|
|
23664
|
+
if (record.credentialId !== void 0 && typeof record.credentialId !== "string") return null;
|
|
23665
|
+
return {
|
|
23666
|
+
user: {
|
|
23667
|
+
id: user.id,
|
|
23668
|
+
username: user.username,
|
|
23669
|
+
role: user.role,
|
|
23670
|
+
permissions,
|
|
23671
|
+
platform_user_id: typeof user.platform_user_id === "string" ? user.platform_user_id : null,
|
|
23672
|
+
email: typeof user.email === "string" ? user.email : null,
|
|
23673
|
+
display_name: typeof user.display_name === "string" ? user.display_name : null,
|
|
23674
|
+
avatar_url: typeof user.avatar_url === "string" ? user.avatar_url : null
|
|
23675
|
+
},
|
|
23676
|
+
authType: record.authType,
|
|
23677
|
+
credentialId: record.credentialId
|
|
23678
|
+
};
|
|
23679
|
+
}
|
|
23680
|
+
async function consumeWebSocketTicket(request, env) {
|
|
23681
|
+
if (request.headers.get("upgrade")?.toLowerCase() !== "websocket") return null;
|
|
23682
|
+
let url;
|
|
23683
|
+
try {
|
|
23684
|
+
url = new URL(request.url);
|
|
23685
|
+
} catch {
|
|
23686
|
+
return null;
|
|
23687
|
+
}
|
|
23688
|
+
const ticket = url.searchParams.get("ticket");
|
|
23689
|
+
if (!ticket) return null;
|
|
23690
|
+
const authJson = await getAgentBuilder(env).consumeWebSocketTicket({
|
|
23691
|
+
ticket_hash: await hashToken(ticket),
|
|
23692
|
+
pathname: url.pathname
|
|
23693
|
+
});
|
|
23694
|
+
return typeof authJson === "string" ? parseWebSocketTicketAuth(authJson) : null;
|
|
23695
|
+
}
|
|
23370
23696
|
async function authenticate(request, env) {
|
|
23697
|
+
const ticketAuth = await consumeWebSocketTicket(request, env);
|
|
23698
|
+
if (ticketAuth) return ticketAuth;
|
|
23371
23699
|
const token = extractBearerToken(request);
|
|
23372
23700
|
if (!token) {
|
|
23373
23701
|
return null;
|
|
23374
23702
|
}
|
|
23375
|
-
if (env.SUPER_ADMIN_PASSWORD && env.ENCRYPTION_KEY) {
|
|
23703
|
+
if (typeof env.SUPER_ADMIN_PASSWORD === "string" && typeof env.ENCRYPTION_KEY === "string") {
|
|
23376
23704
|
if (token.includes(".")) {
|
|
23377
23705
|
const isValidSuperAdmin = await verifySuperAdminToken(
|
|
23378
23706
|
token,
|
|
23379
23707
|
env.ENCRYPTION_KEY
|
|
23380
23708
|
);
|
|
23381
23709
|
if (isValidSuperAdmin) {
|
|
23382
|
-
|
|
23383
|
-
|
|
23384
|
-
|
|
23385
|
-
|
|
23386
|
-
|
|
23387
|
-
|
|
23388
|
-
|
|
23389
|
-
|
|
23710
|
+
const tokenHash = await hashToken(token);
|
|
23711
|
+
const agentBuilder = getAgentBuilder(env);
|
|
23712
|
+
if (await agentBuilder.validateSuperAdminSession(tokenHash)) {
|
|
23713
|
+
return {
|
|
23714
|
+
user: {
|
|
23715
|
+
id: "super_admin",
|
|
23716
|
+
username: "admin",
|
|
23717
|
+
role: "admin"
|
|
23718
|
+
},
|
|
23719
|
+
authType: "super_admin"
|
|
23720
|
+
};
|
|
23721
|
+
}
|
|
23390
23722
|
}
|
|
23391
23723
|
}
|
|
23392
23724
|
}
|
|
@@ -23431,7 +23763,8 @@ async function authenticate(request, env) {
|
|
|
23431
23763
|
display_name: user.display_name ?? null,
|
|
23432
23764
|
avatar_url: user.avatar_url ?? null
|
|
23433
23765
|
},
|
|
23434
|
-
authType: "api_key"
|
|
23766
|
+
authType: "api_key",
|
|
23767
|
+
credentialId: apiKey.id
|
|
23435
23768
|
};
|
|
23436
23769
|
}
|
|
23437
23770
|
}
|
|
@@ -23443,7 +23776,10 @@ async function requireAuth(request, env) {
|
|
|
23443
23776
|
if (!authContext) {
|
|
23444
23777
|
return new Response(JSON.stringify({ error: "Unauthorized" }), {
|
|
23445
23778
|
status: 401,
|
|
23446
|
-
headers: {
|
|
23779
|
+
headers: {
|
|
23780
|
+
"Content-Type": "application/json",
|
|
23781
|
+
...runtimeCorsHeaders(request, env)
|
|
23782
|
+
}
|
|
23447
23783
|
});
|
|
23448
23784
|
}
|
|
23449
23785
|
return authContext;
|
|
@@ -23458,12 +23794,70 @@ async function requireAdmin(request, env) {
|
|
|
23458
23794
|
JSON.stringify({ error: "Forbidden: Admin access required" }),
|
|
23459
23795
|
{
|
|
23460
23796
|
status: 403,
|
|
23461
|
-
headers: {
|
|
23797
|
+
headers: {
|
|
23798
|
+
"Content-Type": "application/json",
|
|
23799
|
+
...runtimeCorsHeaders(request, env)
|
|
23800
|
+
}
|
|
23462
23801
|
}
|
|
23463
23802
|
);
|
|
23464
23803
|
}
|
|
23465
23804
|
return result;
|
|
23466
23805
|
}
|
|
23806
|
+
function defineController2(controller) {
|
|
23807
|
+
return controller;
|
|
23808
|
+
}
|
|
23809
|
+
function createThreadEndpointHandler(handler) {
|
|
23810
|
+
return (async (context) => {
|
|
23811
|
+
const { req, params, env } = context;
|
|
23812
|
+
const threadId = params.threadId ?? params.id;
|
|
23813
|
+
if (!threadId) {
|
|
23814
|
+
return Response.json({ error: "Thread ID required" }, { status: 400 });
|
|
23815
|
+
}
|
|
23816
|
+
const endpointAuth = {
|
|
23817
|
+
caller: endpointCallerFromAuth(context.auth),
|
|
23818
|
+
threadAccess: Boolean(
|
|
23819
|
+
context.threadAccess && context.threadAccess.threadId === threadId
|
|
23820
|
+
)
|
|
23821
|
+
};
|
|
23822
|
+
try {
|
|
23823
|
+
const agentBuilderId = env.AGENT_BUILDER.idFromName("singleton");
|
|
23824
|
+
const agentBuilder = env.AGENT_BUILDER.get(agentBuilderId);
|
|
23825
|
+
const metadata = await agentBuilder.getThread(threadId);
|
|
23826
|
+
if (!metadata) {
|
|
23827
|
+
return Response.json(
|
|
23828
|
+
{ error: `Thread not found: ${threadId}` },
|
|
23829
|
+
{ status: 404 }
|
|
23830
|
+
);
|
|
23831
|
+
}
|
|
23832
|
+
const meta = metadata;
|
|
23833
|
+
if (!meta.agent_id && meta.agent_name) {
|
|
23834
|
+
meta.agent_id = meta.agent_name;
|
|
23835
|
+
}
|
|
23836
|
+
const durableId = env.AGENT_BUILDER_THREAD.idFromName(threadId);
|
|
23837
|
+
const stub = env.AGENT_BUILDER_THREAD.get(durableId);
|
|
23838
|
+
const instance = stub;
|
|
23839
|
+
const threadState = ThreadStateImpl.fromThreadInstance(instance, metadata, {
|
|
23840
|
+
env
|
|
23841
|
+
});
|
|
23842
|
+
const endpointParams = {};
|
|
23843
|
+
for (const [key, value] of Object.entries(params)) {
|
|
23844
|
+
if (key === "threadId" || key === "id" && !params.threadId) {
|
|
23845
|
+
continue;
|
|
23846
|
+
}
|
|
23847
|
+
endpointParams[key === "_" ? "*" : key] = value;
|
|
23848
|
+
}
|
|
23849
|
+
const response = await handler(req, threadState, endpointParams, endpointAuth);
|
|
23850
|
+
await threadState.flushPendingEventDeliveries();
|
|
23851
|
+
return response;
|
|
23852
|
+
} catch (error) {
|
|
23853
|
+
console.error(`Error in thread endpoint for ${threadId}:`, error);
|
|
23854
|
+
return Response.json(
|
|
23855
|
+
{ error: error.message || "Internal server error" },
|
|
23856
|
+
{ status: 500 }
|
|
23857
|
+
);
|
|
23858
|
+
}
|
|
23859
|
+
});
|
|
23860
|
+
}
|
|
23467
23861
|
|
|
23468
23862
|
// src/utils/permissions.ts
|
|
23469
23863
|
function canAccessThread(auth, thread) {
|
|
@@ -24388,8 +24782,37 @@ var migration35 = {
|
|
|
24388
24782
|
}
|
|
24389
24783
|
};
|
|
24390
24784
|
|
|
24785
|
+
// src/durable-objects/migrations/036_add_tool_delivery_tokens.ts
|
|
24786
|
+
var migration36 = {
|
|
24787
|
+
version: 36,
|
|
24788
|
+
name: "add_tool_delivery_tokens",
|
|
24789
|
+
async up(sql) {
|
|
24790
|
+
sql.exec(`
|
|
24791
|
+
CREATE TABLE IF NOT EXISTS pending_client_calls (
|
|
24792
|
+
tool_call_id TEXT PRIMARY KEY,
|
|
24793
|
+
tool TEXT NOT NULL,
|
|
24794
|
+
args TEXT,
|
|
24795
|
+
request_permission TEXT,
|
|
24796
|
+
risk INTEGER,
|
|
24797
|
+
side TEXT NOT NULL DEFAULT 'a',
|
|
24798
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
24799
|
+
created_at INTEGER NOT NULL,
|
|
24800
|
+
dispatched_client_id TEXT,
|
|
24801
|
+
dispatched_generation INTEGER NOT NULL DEFAULT 0
|
|
24802
|
+
)
|
|
24803
|
+
`);
|
|
24804
|
+
const columns = sql.exec(
|
|
24805
|
+
`PRAGMA table_info(pending_client_calls)`
|
|
24806
|
+
).toArray();
|
|
24807
|
+
if (!columns.some((column) => column.name === "delivery_token_hash")) {
|
|
24808
|
+
sql.exec(`ALTER TABLE pending_client_calls ADD COLUMN delivery_token_hash TEXT`);
|
|
24809
|
+
}
|
|
24810
|
+
sql.exec(`UPDATE _metadata SET value = '36' WHERE key = 'schema_version'`);
|
|
24811
|
+
}
|
|
24812
|
+
};
|
|
24813
|
+
|
|
24391
24814
|
// src/durable-objects/migrations/index.ts
|
|
24392
|
-
var migrations = [migration, migration2, migration3, migration4, migration5, migration6, migration7, migration8, migration9, migration10, migration11, migration12, migration13, migration14, migration15, migration16, migration17, migration18, migration19, migration20, migration21, migration22, migration23, migration24, migration25, migration26, migration27, migration28, migration29, migration30, migration31, migration32, migration33, migration34, migration35];
|
|
24815
|
+
var migrations = [migration, migration2, migration3, migration4, migration5, migration6, migration7, migration8, migration9, migration10, migration11, migration12, migration13, migration14, migration15, migration16, migration17, migration18, migration19, migration20, migration21, migration22, migration23, migration24, migration25, migration26, migration27, migration28, migration29, migration30, migration31, migration32, migration33, migration34, migration35, migration36];
|
|
24393
24816
|
var LATEST_SCHEMA_VERSION = migrations.length;
|
|
24394
24817
|
|
|
24395
24818
|
// src/durable-objects/subagent-reconcile.ts
|
|
@@ -25225,7 +25648,7 @@ function eventSocketCanReceive(attachment, ownerUserId) {
|
|
|
25225
25648
|
}
|
|
25226
25649
|
|
|
25227
25650
|
// src/durable-objects/agentbuilder-migrations/0001_initial.ts
|
|
25228
|
-
var
|
|
25651
|
+
var migration37 = {
|
|
25229
25652
|
version: 1,
|
|
25230
25653
|
async up(sql) {
|
|
25231
25654
|
sql.exec(`
|
|
@@ -25324,7 +25747,7 @@ var migration36 = {
|
|
|
25324
25747
|
};
|
|
25325
25748
|
|
|
25326
25749
|
// src/durable-objects/agentbuilder-migrations/0002_add_tenvs_properties.ts
|
|
25327
|
-
var
|
|
25750
|
+
var migration38 = {
|
|
25328
25751
|
version: 2,
|
|
25329
25752
|
async up(sql) {
|
|
25330
25753
|
sql.exec(`ALTER TABLE threads ADD COLUMN tenvs TEXT`);
|
|
@@ -25336,7 +25759,7 @@ var migration37 = {
|
|
|
25336
25759
|
};
|
|
25337
25760
|
|
|
25338
25761
|
// src/durable-objects/agentbuilder-migrations/0003_add_parent_and_terminated.ts
|
|
25339
|
-
var
|
|
25762
|
+
var migration39 = {
|
|
25340
25763
|
version: 3,
|
|
25341
25764
|
async up(sql) {
|
|
25342
25765
|
sql.exec(`ALTER TABLE threads ADD COLUMN parent TEXT REFERENCES threads(id)`);
|
|
@@ -25349,7 +25772,7 @@ var migration38 = {
|
|
|
25349
25772
|
};
|
|
25350
25773
|
|
|
25351
25774
|
// src/durable-objects/agentbuilder-migrations/0004_add_env_tables.ts
|
|
25352
|
-
var
|
|
25775
|
+
var migration40 = {
|
|
25353
25776
|
version: 4,
|
|
25354
25777
|
async up(sql) {
|
|
25355
25778
|
sql.exec(`ALTER TABLE threads ADD COLUMN env TEXT`);
|
|
@@ -25373,7 +25796,7 @@ var migration39 = {
|
|
|
25373
25796
|
};
|
|
25374
25797
|
|
|
25375
25798
|
// src/durable-objects/agentbuilder-migrations/0005_add_pending_subagent_env_requests.ts
|
|
25376
|
-
var
|
|
25799
|
+
var migration41 = {
|
|
25377
25800
|
version: 5,
|
|
25378
25801
|
async up(sql) {
|
|
25379
25802
|
sql.exec(`
|
|
@@ -25399,7 +25822,7 @@ var migration40 = {
|
|
|
25399
25822
|
};
|
|
25400
25823
|
|
|
25401
25824
|
// src/durable-objects/agentbuilder-migrations/0006_add_env_value_types.ts
|
|
25402
|
-
var
|
|
25825
|
+
var migration42 = {
|
|
25403
25826
|
version: 6,
|
|
25404
25827
|
async up(sql) {
|
|
25405
25828
|
sql.exec(
|
|
@@ -25414,7 +25837,7 @@ var migration41 = {
|
|
|
25414
25837
|
};
|
|
25415
25838
|
|
|
25416
25839
|
// src/durable-objects/agentbuilder-migrations/0007_platform_identity_replica.ts
|
|
25417
|
-
var
|
|
25840
|
+
var migration43 = {
|
|
25418
25841
|
version: 7,
|
|
25419
25842
|
async up(sql) {
|
|
25420
25843
|
sql.exec(`ALTER TABLE users ADD COLUMN platform_user_id TEXT`);
|
|
@@ -25443,7 +25866,7 @@ var migration42 = {
|
|
|
25443
25866
|
};
|
|
25444
25867
|
|
|
25445
25868
|
// src/durable-objects/agentbuilder-migrations/0008_add_skills.ts
|
|
25446
|
-
var
|
|
25869
|
+
var migration44 = {
|
|
25447
25870
|
version: 8,
|
|
25448
25871
|
async up(sql) {
|
|
25449
25872
|
sql.exec(`
|
|
@@ -25474,7 +25897,7 @@ var migration43 = {
|
|
|
25474
25897
|
};
|
|
25475
25898
|
|
|
25476
25899
|
// src/durable-objects/agentbuilder-migrations/0009_add_user_permissions.ts
|
|
25477
|
-
var
|
|
25900
|
+
var migration45 = {
|
|
25478
25901
|
version: 9,
|
|
25479
25902
|
async up(sql) {
|
|
25480
25903
|
sql.exec(`ALTER TABLE users ADD COLUMN permissions TEXT`);
|
|
@@ -25485,7 +25908,7 @@ var migration44 = {
|
|
|
25485
25908
|
};
|
|
25486
25909
|
|
|
25487
25910
|
// src/durable-objects/agentbuilder-migrations/0010_add_device_logins.ts
|
|
25488
|
-
var
|
|
25911
|
+
var migration46 = {
|
|
25489
25912
|
version: 10,
|
|
25490
25913
|
async up(sql) {
|
|
25491
25914
|
sql.exec(`
|
|
@@ -25505,7 +25928,7 @@ var migration45 = {
|
|
|
25505
25928
|
};
|
|
25506
25929
|
|
|
25507
25930
|
// src/durable-objects/agentbuilder-migrations/0011_add_user_values.ts
|
|
25508
|
-
var
|
|
25931
|
+
var migration47 = {
|
|
25509
25932
|
version: 11,
|
|
25510
25933
|
async up(sql) {
|
|
25511
25934
|
sql.exec(`
|
|
@@ -25528,7 +25951,7 @@ var migration46 = {
|
|
|
25528
25951
|
};
|
|
25529
25952
|
|
|
25530
25953
|
// src/durable-objects/agentbuilder-migrations/0012_allow_api_instance_role.ts
|
|
25531
|
-
var
|
|
25954
|
+
var migration48 = {
|
|
25532
25955
|
version: 12,
|
|
25533
25956
|
async up(sql) {
|
|
25534
25957
|
sql.exec(`CREATE TABLE oauth_accounts_0012_backup AS SELECT * FROM oauth_accounts`);
|
|
@@ -25638,7 +26061,7 @@ var migration47 = {
|
|
|
25638
26061
|
};
|
|
25639
26062
|
|
|
25640
26063
|
// src/durable-objects/agentbuilder-migrations/0013_add_thread_activity.ts
|
|
25641
|
-
var
|
|
26064
|
+
var migration49 = {
|
|
25642
26065
|
version: 13,
|
|
25643
26066
|
async up(sql) {
|
|
25644
26067
|
sql.exec(`ALTER TABLE threads ADD COLUMN last_activity_at INTEGER`);
|
|
@@ -25654,7 +26077,7 @@ var migration48 = {
|
|
|
25654
26077
|
};
|
|
25655
26078
|
|
|
25656
26079
|
// src/durable-objects/agentbuilder-migrations/0014_thread_activity_scale.ts
|
|
25657
|
-
var
|
|
26080
|
+
var migration50 = {
|
|
25658
26081
|
version: 14,
|
|
25659
26082
|
async up(sql) {
|
|
25660
26083
|
sql.exec(`ALTER TABLE threads ADD COLUMN last_message_preview TEXT`);
|
|
@@ -25672,7 +26095,7 @@ var migration49 = {
|
|
|
25672
26095
|
};
|
|
25673
26096
|
|
|
25674
26097
|
// src/durable-objects/agentbuilder-migrations/0015_clear_envelope_previews.ts
|
|
25675
|
-
var
|
|
26098
|
+
var migration51 = {
|
|
25676
26099
|
version: 15,
|
|
25677
26100
|
async up(sql) {
|
|
25678
26101
|
sql.exec(
|
|
@@ -25686,7 +26109,7 @@ var migration50 = {
|
|
|
25686
26109
|
};
|
|
25687
26110
|
|
|
25688
26111
|
// src/durable-objects/agentbuilder-migrations/0016_add_origin_agent.ts
|
|
25689
|
-
var
|
|
26112
|
+
var migration52 = {
|
|
25690
26113
|
version: 16,
|
|
25691
26114
|
async up(sql) {
|
|
25692
26115
|
sql.exec(`ALTER TABLE threads ADD COLUMN origin_agent_name TEXT`);
|
|
@@ -25701,7 +26124,7 @@ var migration51 = {
|
|
|
25701
26124
|
};
|
|
25702
26125
|
|
|
25703
26126
|
// src/durable-objects/agentbuilder-migrations/0017_add_thread_message_recency.ts
|
|
25704
|
-
var
|
|
26127
|
+
var migration53 = {
|
|
25705
26128
|
version: 17,
|
|
25706
26129
|
async up(sql) {
|
|
25707
26130
|
sql.exec(`ALTER TABLE threads ADD COLUMN last_message_at INTEGER`);
|
|
@@ -25712,10 +26135,132 @@ var migration52 = {
|
|
|
25712
26135
|
}
|
|
25713
26136
|
};
|
|
25714
26137
|
|
|
26138
|
+
// src/durable-objects/agentbuilder-migrations/0018_auth_security.ts
|
|
26139
|
+
var migration54 = {
|
|
26140
|
+
version: 18,
|
|
26141
|
+
async up(sql) {
|
|
26142
|
+
sql.exec(`
|
|
26143
|
+
CREATE TABLE IF NOT EXISTS super_admin_sessions (
|
|
26144
|
+
token_hash TEXT PRIMARY KEY,
|
|
26145
|
+
created_at INTEGER NOT NULL,
|
|
26146
|
+
expires_at INTEGER NOT NULL
|
|
26147
|
+
)
|
|
26148
|
+
`);
|
|
26149
|
+
sql.exec(`
|
|
26150
|
+
CREATE INDEX IF NOT EXISTS idx_super_admin_sessions_expires
|
|
26151
|
+
ON super_admin_sessions(expires_at)
|
|
26152
|
+
`);
|
|
26153
|
+
sql.exec(`
|
|
26154
|
+
CREATE TABLE IF NOT EXISTS auth_rate_limits (
|
|
26155
|
+
bucket_key TEXT PRIMARY KEY,
|
|
26156
|
+
attempts INTEGER NOT NULL,
|
|
26157
|
+
reset_at INTEGER NOT NULL
|
|
26158
|
+
)
|
|
26159
|
+
`);
|
|
26160
|
+
sql.exec(`
|
|
26161
|
+
CREATE INDEX IF NOT EXISTS idx_auth_rate_limits_reset
|
|
26162
|
+
ON auth_rate_limits(reset_at)
|
|
26163
|
+
`);
|
|
26164
|
+
sql.exec(`
|
|
26165
|
+
INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '18')
|
|
26166
|
+
`);
|
|
26167
|
+
}
|
|
26168
|
+
};
|
|
26169
|
+
|
|
26170
|
+
// src/durable-objects/agentbuilder-migrations/0019_scope_skills.ts
|
|
26171
|
+
var migration55 = {
|
|
26172
|
+
version: 19,
|
|
26173
|
+
async up(sql) {
|
|
26174
|
+
sql.exec(`
|
|
26175
|
+
CREATE TABLE skills_0019 (
|
|
26176
|
+
owner_id TEXT NOT NULL,
|
|
26177
|
+
name TEXT NOT NULL,
|
|
26178
|
+
description TEXT NOT NULL,
|
|
26179
|
+
version TEXT,
|
|
26180
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
26181
|
+
shared INTEGER NOT NULL DEFAULT 0,
|
|
26182
|
+
created_at INTEGER DEFAULT (strftime('%s', 'now')),
|
|
26183
|
+
updated_at INTEGER DEFAULT (strftime('%s', 'now')),
|
|
26184
|
+
PRIMARY KEY (owner_id, name)
|
|
26185
|
+
)
|
|
26186
|
+
`);
|
|
26187
|
+
sql.exec(`
|
|
26188
|
+
INSERT INTO skills_0019 (
|
|
26189
|
+
owner_id, name, description, version, enabled, shared, created_at, updated_at
|
|
26190
|
+
)
|
|
26191
|
+
SELECT '__instance_shared__', name, description, version, enabled, 1, created_at, updated_at
|
|
26192
|
+
FROM skills
|
|
26193
|
+
`);
|
|
26194
|
+
sql.exec(`
|
|
26195
|
+
CREATE TABLE skill_files_0019 (
|
|
26196
|
+
owner_id TEXT NOT NULL,
|
|
26197
|
+
skill_name TEXT NOT NULL,
|
|
26198
|
+
path TEXT NOT NULL,
|
|
26199
|
+
content TEXT NOT NULL,
|
|
26200
|
+
size INTEGER NOT NULL,
|
|
26201
|
+
PRIMARY KEY (owner_id, skill_name, path),
|
|
26202
|
+
FOREIGN KEY (owner_id, skill_name) REFERENCES skills_0019(owner_id, name) ON DELETE CASCADE
|
|
26203
|
+
)
|
|
26204
|
+
`);
|
|
26205
|
+
sql.exec(`
|
|
26206
|
+
INSERT INTO skill_files_0019 (owner_id, skill_name, path, content, size)
|
|
26207
|
+
SELECT '__instance_shared__', skill_name, path, content, size FROM skill_files
|
|
26208
|
+
`);
|
|
26209
|
+
sql.exec(`DROP TABLE skill_files`);
|
|
26210
|
+
sql.exec(`DROP TABLE skills`);
|
|
26211
|
+
sql.exec(`ALTER TABLE skills_0019 RENAME TO skills`);
|
|
26212
|
+
sql.exec(`ALTER TABLE skill_files_0019 RENAME TO skill_files`);
|
|
26213
|
+
sql.exec(`CREATE INDEX idx_skills_owner_enabled ON skills(owner_id, enabled)`);
|
|
26214
|
+
sql.exec(`CREATE INDEX idx_skills_shared_enabled ON skills(shared, enabled)`);
|
|
26215
|
+
sql.exec(`CREATE INDEX idx_skill_files_skill ON skill_files(owner_id, skill_name)`);
|
|
26216
|
+
sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '19')`);
|
|
26217
|
+
}
|
|
26218
|
+
};
|
|
26219
|
+
|
|
26220
|
+
// src/durable-objects/agentbuilder-migrations/0020_websocket_tickets.ts
|
|
26221
|
+
var migration56 = {
|
|
26222
|
+
version: 20,
|
|
26223
|
+
async up(sql) {
|
|
26224
|
+
sql.exec(`
|
|
26225
|
+
CREATE TABLE websocket_tickets (
|
|
26226
|
+
ticket_hash TEXT PRIMARY KEY,
|
|
26227
|
+
pathname TEXT NOT NULL,
|
|
26228
|
+
auth_json TEXT NOT NULL,
|
|
26229
|
+
created_at INTEGER NOT NULL,
|
|
26230
|
+
expires_at INTEGER NOT NULL
|
|
26231
|
+
)
|
|
26232
|
+
`);
|
|
26233
|
+
sql.exec(`
|
|
26234
|
+
CREATE INDEX idx_websocket_tickets_expires
|
|
26235
|
+
ON websocket_tickets(expires_at)
|
|
26236
|
+
`);
|
|
26237
|
+
sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '20')`);
|
|
26238
|
+
}
|
|
26239
|
+
};
|
|
26240
|
+
|
|
26241
|
+
// src/durable-objects/agentbuilder-migrations/0021_platform_handoff_jtis.ts
|
|
26242
|
+
var migration57 = {
|
|
26243
|
+
version: 21,
|
|
26244
|
+
async up(sql) {
|
|
26245
|
+
sql.exec(`
|
|
26246
|
+
CREATE TABLE platform_handoff_jtis (
|
|
26247
|
+
jti TEXT PRIMARY KEY,
|
|
26248
|
+
consumed_at INTEGER NOT NULL,
|
|
26249
|
+
expires_at INTEGER NOT NULL
|
|
26250
|
+
)
|
|
26251
|
+
`);
|
|
26252
|
+
sql.exec(`
|
|
26253
|
+
CREATE INDEX idx_platform_handoff_jtis_expires
|
|
26254
|
+
ON platform_handoff_jtis(expires_at)
|
|
26255
|
+
`);
|
|
26256
|
+
sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '21')`);
|
|
26257
|
+
}
|
|
26258
|
+
};
|
|
26259
|
+
|
|
25715
26260
|
// src/durable-objects/agentbuilder-migrations/index.ts
|
|
25716
|
-
var migrations2 = [
|
|
26261
|
+
var migrations2 = [migration37, migration38, migration39, migration40, migration41, migration42, migration43, migration44, migration45, migration46, migration47, migration48, migration49, migration50, migration51, migration52, migration53, migration54, migration55, migration56, migration57];
|
|
25717
26262
|
var LATEST_SCHEMA_VERSION2 = migrations2.reduce(
|
|
25718
|
-
(max,
|
|
26263
|
+
(max, migration58) => Math.max(max, migration58.version),
|
|
25719
26264
|
0
|
|
25720
26265
|
);
|
|
25721
26266
|
|
|
@@ -26220,6 +26765,7 @@ function validateSkillFiles(files) {
|
|
|
26220
26765
|
}
|
|
26221
26766
|
|
|
26222
26767
|
// src/durable-objects/DurableAgentBuilder.ts
|
|
26768
|
+
init_scope();
|
|
26223
26769
|
var ENV_TYPES_PROPERTY = "__agentbuilder_env_types";
|
|
26224
26770
|
function parseStoredPermissions(value) {
|
|
26225
26771
|
if (typeof value !== "string" || !value.trim()) return null;
|
|
@@ -26812,9 +27358,9 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
26812
27358
|
}
|
|
26813
27359
|
}
|
|
26814
27360
|
async runMigrations(fromVersion) {
|
|
26815
|
-
for (const
|
|
26816
|
-
if (
|
|
26817
|
-
await
|
|
27361
|
+
for (const migration58 of migrations2) {
|
|
27362
|
+
if (migration58.version > fromVersion) {
|
|
27363
|
+
await migration58.up(this.ctx.storage.sql);
|
|
26818
27364
|
}
|
|
26819
27365
|
}
|
|
26820
27366
|
}
|
|
@@ -28763,6 +29309,150 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
28763
29309
|
tokenHash
|
|
28764
29310
|
);
|
|
28765
29311
|
}
|
|
29312
|
+
/** Register a short-lived super-admin token so it can be revoked alone. */
|
|
29313
|
+
async createSuperAdminSession(params) {
|
|
29314
|
+
await this.ensureMigrated();
|
|
29315
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
29316
|
+
await this.ctx.storage.sql.exec(
|
|
29317
|
+
`DELETE FROM super_admin_sessions WHERE expires_at <= ?`,
|
|
29318
|
+
now
|
|
29319
|
+
);
|
|
29320
|
+
await this.ctx.storage.sql.exec(
|
|
29321
|
+
`INSERT INTO super_admin_sessions (token_hash, created_at, expires_at)
|
|
29322
|
+
VALUES (?, ?, ?)`,
|
|
29323
|
+
params.token_hash,
|
|
29324
|
+
now,
|
|
29325
|
+
params.expires_at
|
|
29326
|
+
);
|
|
29327
|
+
}
|
|
29328
|
+
/** A valid signature is insufficient: the session must still be registered. */
|
|
29329
|
+
async validateSuperAdminSession(tokenHash) {
|
|
29330
|
+
await this.ensureMigrated();
|
|
29331
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
29332
|
+
const cursor = await this.ctx.storage.sql.exec(
|
|
29333
|
+
`SELECT 1 AS found FROM super_admin_sessions
|
|
29334
|
+
WHERE token_hash = ? AND expires_at > ?`,
|
|
29335
|
+
tokenHash,
|
|
29336
|
+
now
|
|
29337
|
+
);
|
|
29338
|
+
return cursor.toArray().length > 0;
|
|
29339
|
+
}
|
|
29340
|
+
/** Revoke only the presented super-admin session; no key rotation required. */
|
|
29341
|
+
async deleteSuperAdminSession(tokenHash) {
|
|
29342
|
+
await this.ensureMigrated();
|
|
29343
|
+
await this.ctx.storage.sql.exec(
|
|
29344
|
+
`DELETE FROM super_admin_sessions WHERE token_hash = ?`,
|
|
29345
|
+
tokenHash
|
|
29346
|
+
);
|
|
29347
|
+
}
|
|
29348
|
+
/** Register a path-bound bearer ticket for one WebSocket handshake. */
|
|
29349
|
+
async createWebSocketTicket(params) {
|
|
29350
|
+
await this.ensureMigrated();
|
|
29351
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
29352
|
+
this.ctx.storage.sql.exec(
|
|
29353
|
+
`DELETE FROM websocket_tickets WHERE expires_at <= ?`,
|
|
29354
|
+
now
|
|
29355
|
+
);
|
|
29356
|
+
this.ctx.storage.sql.exec(
|
|
29357
|
+
`INSERT INTO websocket_tickets (ticket_hash, pathname, auth_json, created_at, expires_at)
|
|
29358
|
+
VALUES (?, ?, ?, ?, ?)`,
|
|
29359
|
+
params.ticket_hash,
|
|
29360
|
+
params.pathname,
|
|
29361
|
+
params.auth_json,
|
|
29362
|
+
now,
|
|
29363
|
+
params.expires_at
|
|
29364
|
+
);
|
|
29365
|
+
}
|
|
29366
|
+
/**
|
|
29367
|
+
* Atomically consume a WebSocket ticket. Deleting before validating its path
|
|
29368
|
+
* means even a mistaken or malicious first presentation burns the bearer.
|
|
29369
|
+
*/
|
|
29370
|
+
async consumeWebSocketTicket(params) {
|
|
29371
|
+
await this.ensureMigrated();
|
|
29372
|
+
const cursor = this.ctx.storage.sql.exec(
|
|
29373
|
+
`DELETE FROM websocket_tickets
|
|
29374
|
+
WHERE ticket_hash = ?
|
|
29375
|
+
RETURNING pathname, auth_json, expires_at`,
|
|
29376
|
+
params.ticket_hash
|
|
29377
|
+
);
|
|
29378
|
+
const ticket = cursor.toArray()[0];
|
|
29379
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
29380
|
+
if (!ticket || ticket.expires_at <= now || ticket.pathname !== params.pathname) {
|
|
29381
|
+
return null;
|
|
29382
|
+
}
|
|
29383
|
+
return ticket.auth_json;
|
|
29384
|
+
}
|
|
29385
|
+
/** Atomically accept an unexpired platform handoff identifier once. */
|
|
29386
|
+
async consumePlatformHandoffJti(params) {
|
|
29387
|
+
await this.ensureMigrated();
|
|
29388
|
+
const jti = params.jti.trim();
|
|
29389
|
+
const expiresAt = Math.floor(params.expires_at);
|
|
29390
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
29391
|
+
if (!jti || jti.length > 200 || !Number.isSafeInteger(expiresAt) || expiresAt <= now) {
|
|
29392
|
+
return false;
|
|
29393
|
+
}
|
|
29394
|
+
this.ctx.storage.sql.exec(
|
|
29395
|
+
`DELETE FROM platform_handoff_jtis WHERE expires_at <= ?`,
|
|
29396
|
+
now
|
|
29397
|
+
);
|
|
29398
|
+
const cursor = this.ctx.storage.sql.exec(
|
|
29399
|
+
`INSERT OR IGNORE INTO platform_handoff_jtis (jti, consumed_at, expires_at)
|
|
29400
|
+
VALUES (?, ?, ?)`,
|
|
29401
|
+
jti,
|
|
29402
|
+
now,
|
|
29403
|
+
expiresAt
|
|
29404
|
+
);
|
|
29405
|
+
return cursor.rowsWritten > 0;
|
|
29406
|
+
}
|
|
29407
|
+
/**
|
|
29408
|
+
* Atomically consume one attempt from a fixed-window authentication bucket.
|
|
29409
|
+
* The singleton Durable Object serializes calls, preventing parallel bypass.
|
|
29410
|
+
*/
|
|
29411
|
+
async consumeAuthRateLimit(params) {
|
|
29412
|
+
await this.ensureMigrated();
|
|
29413
|
+
const key = params.key.trim();
|
|
29414
|
+
const limit = Math.floor(params.limit);
|
|
29415
|
+
const windowSeconds = Math.floor(params.window_seconds);
|
|
29416
|
+
if (!key || key.length > 160 || limit < 1 || limit > 1e4 || windowSeconds < 1 || windowSeconds > 86400) {
|
|
29417
|
+
throw new Error("Invalid authentication rate-limit bucket");
|
|
29418
|
+
}
|
|
29419
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
29420
|
+
await this.ctx.storage.sql.exec(
|
|
29421
|
+
`DELETE FROM auth_rate_limits WHERE reset_at <= ?`,
|
|
29422
|
+
now
|
|
29423
|
+
);
|
|
29424
|
+
const cursor = await this.ctx.storage.sql.exec(
|
|
29425
|
+
`SELECT attempts, reset_at FROM auth_rate_limits WHERE bucket_key = ?`,
|
|
29426
|
+
key
|
|
29427
|
+
);
|
|
29428
|
+
const current = cursor.toArray()[0];
|
|
29429
|
+
if (!current || current.reset_at <= now) {
|
|
29430
|
+
const resetAt = now + windowSeconds;
|
|
29431
|
+
await this.ctx.storage.sql.exec(
|
|
29432
|
+
`INSERT INTO auth_rate_limits (bucket_key, attempts, reset_at)
|
|
29433
|
+
VALUES (?, 1, ?)
|
|
29434
|
+
ON CONFLICT(bucket_key) DO UPDATE SET attempts = 1, reset_at = excluded.reset_at`,
|
|
29435
|
+
key,
|
|
29436
|
+
resetAt
|
|
29437
|
+
);
|
|
29438
|
+
return { allowed: true, retry_after: windowSeconds, remaining: limit - 1 };
|
|
29439
|
+
}
|
|
29440
|
+
const retryAfter = Math.max(1, current.reset_at - now);
|
|
29441
|
+
if (current.attempts >= limit) {
|
|
29442
|
+
return { allowed: false, retry_after: retryAfter, remaining: 0 };
|
|
29443
|
+
}
|
|
29444
|
+
const attempts = current.attempts + 1;
|
|
29445
|
+
await this.ctx.storage.sql.exec(
|
|
29446
|
+
`UPDATE auth_rate_limits SET attempts = ? WHERE bucket_key = ?`,
|
|
29447
|
+
attempts,
|
|
29448
|
+
key
|
|
29449
|
+
);
|
|
29450
|
+
return {
|
|
29451
|
+
allowed: true,
|
|
29452
|
+
retry_after: retryAfter,
|
|
29453
|
+
remaining: Math.max(0, limit - attempts)
|
|
29454
|
+
};
|
|
29455
|
+
}
|
|
28766
29456
|
// ============================================================
|
|
28767
29457
|
// API Key Methods
|
|
28768
29458
|
// ============================================================
|
|
@@ -29080,40 +29770,61 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
29080
29770
|
}
|
|
29081
29771
|
}
|
|
29082
29772
|
// ─────────────────────────────────────────────────────────────────────────
|
|
29083
|
-
// Skills —
|
|
29084
|
-
// Installed once, visible to every thread; file contents live here so
|
|
29085
|
-
// nothing about a skill is stored on a client machine.
|
|
29773
|
+
// Skills — owner-scoped Agent Skills plus explicitly shared admin skills.
|
|
29086
29774
|
// ─────────────────────────────────────────────────────────────────────────
|
|
29087
29775
|
/** Installed skills, metadata only (no file contents). */
|
|
29088
|
-
async listSkills(options = {}) {
|
|
29776
|
+
async listSkills(scope, options = {}) {
|
|
29089
29777
|
await this.ensureMigrated();
|
|
29090
|
-
const where = options.includeDisabled ? "" : "WHERE enabled = 1";
|
|
29091
29778
|
const cursor = await this.ctx.storage.sql.exec(
|
|
29092
29779
|
`SELECT s.name, s.description, s.version, s.enabled, s.updated_at,
|
|
29093
|
-
|
|
29094
|
-
|
|
29780
|
+
s.owner_id,
|
|
29781
|
+
(SELECT json_group_array(f.path) FROM skill_files f
|
|
29782
|
+
WHERE f.owner_id = s.owner_id AND f.skill_name = s.name) AS files
|
|
29783
|
+
FROM skills s
|
|
29784
|
+
WHERE (s.owner_id = ? OR (? = 1 AND s.shared = 1))
|
|
29785
|
+
AND (? = 1 OR s.enabled = 1)
|
|
29786
|
+
ORDER BY s.name, CASE WHEN s.owner_id = ? THEN 0 ELSE 1 END`,
|
|
29787
|
+
scope.ownerId,
|
|
29788
|
+
scope.includeShared ? 1 : 0,
|
|
29789
|
+
options.includeDisabled ? 1 : 0,
|
|
29790
|
+
scope.ownerId
|
|
29095
29791
|
);
|
|
29096
|
-
|
|
29097
|
-
|
|
29098
|
-
|
|
29099
|
-
|
|
29100
|
-
|
|
29101
|
-
|
|
29102
|
-
|
|
29103
|
-
|
|
29792
|
+
const skills = /* @__PURE__ */ new Map();
|
|
29793
|
+
for (const row of cursor.toArray()) {
|
|
29794
|
+
if (skills.has(row.name)) continue;
|
|
29795
|
+
const parsedFiles = row.files ? JSON.parse(row.files) : [];
|
|
29796
|
+
const files = Array.isArray(parsedFiles) ? parsedFiles.filter((path21) => typeof path21 === "string").sort() : [];
|
|
29797
|
+
skills.set(row.name, {
|
|
29798
|
+
name: row.name,
|
|
29799
|
+
description: row.description,
|
|
29800
|
+
version: row.version ?? void 0,
|
|
29801
|
+
enabled: row.enabled === 1,
|
|
29802
|
+
files,
|
|
29803
|
+
updatedAt: row.updated_at
|
|
29804
|
+
});
|
|
29805
|
+
}
|
|
29806
|
+
return [...skills.values()];
|
|
29104
29807
|
}
|
|
29105
29808
|
/** One skill's metadata (null when not installed). */
|
|
29106
|
-
async getSkill(name) {
|
|
29107
|
-
const all = await this.listSkills({ includeDisabled: true });
|
|
29809
|
+
async getSkill(scope, name) {
|
|
29810
|
+
const all = await this.listSkills(scope, { includeDisabled: true });
|
|
29108
29811
|
return all.find((s) => s.name === name) ?? null;
|
|
29109
29812
|
}
|
|
29110
29813
|
/** One bundled file's text (null when the skill or file is missing). */
|
|
29111
|
-
async getSkillFile(name, path21) {
|
|
29814
|
+
async getSkillFile(scope, name, path21) {
|
|
29112
29815
|
await this.ensureMigrated();
|
|
29113
29816
|
const cursor = await this.ctx.storage.sql.exec(
|
|
29114
|
-
`SELECT content FROM skill_files
|
|
29817
|
+
`SELECT f.content FROM skill_files f
|
|
29818
|
+
JOIN skills s ON s.owner_id = f.owner_id AND s.name = f.skill_name
|
|
29819
|
+
WHERE f.skill_name = ? AND f.path = ?
|
|
29820
|
+
AND (f.owner_id = ? OR (? = 1 AND s.shared = 1))
|
|
29821
|
+
ORDER BY CASE WHEN f.owner_id = ? THEN 0 ELSE 1 END
|
|
29822
|
+
LIMIT 1`,
|
|
29115
29823
|
name,
|
|
29116
|
-
path21.replace(/^\.\//, "")
|
|
29824
|
+
path21.replace(/^\.\//, ""),
|
|
29825
|
+
scope.ownerId,
|
|
29826
|
+
scope.includeShared ? 1 : 0,
|
|
29827
|
+
scope.ownerId
|
|
29117
29828
|
);
|
|
29118
29829
|
const rows = cursor.toArray();
|
|
29119
29830
|
return rows.length ? rows[0].content : null;
|
|
@@ -29124,44 +29835,56 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
29124
29835
|
* that frontmatter. Throws SkillValidationError with an actionable message
|
|
29125
29836
|
* on any standard violation.
|
|
29126
29837
|
*/
|
|
29127
|
-
async upsertSkill(files) {
|
|
29838
|
+
async upsertSkill(scope, files) {
|
|
29128
29839
|
await this.ensureMigrated();
|
|
29129
29840
|
const frontmatter = validateSkillFiles(files);
|
|
29130
29841
|
const name = frontmatter.name;
|
|
29131
|
-
const
|
|
29132
|
-
await this.
|
|
29842
|
+
const ownScope = { ownerId: scope.ownerId };
|
|
29843
|
+
const existing = await this.getSkill(ownScope, name);
|
|
29844
|
+
await this.ctx.storage.sql.exec(
|
|
29845
|
+
`DELETE FROM skill_files WHERE owner_id = ? AND skill_name = ?`,
|
|
29846
|
+
scope.ownerId,
|
|
29847
|
+
name
|
|
29848
|
+
);
|
|
29133
29849
|
await this.ctx.storage.sql.exec(
|
|
29134
|
-
`INSERT INTO skills (name, description, version, enabled, updated_at)
|
|
29135
|
-
VALUES (?, ?, ?, ?, strftime('%s', 'now'))
|
|
29136
|
-
ON CONFLICT(name) DO UPDATE SET
|
|
29850
|
+
`INSERT INTO skills (owner_id, name, description, version, enabled, shared, updated_at)
|
|
29851
|
+
VALUES (?, ?, ?, ?, ?, ?, strftime('%s', 'now'))
|
|
29852
|
+
ON CONFLICT(owner_id, name) DO UPDATE SET
|
|
29137
29853
|
description = excluded.description,
|
|
29138
29854
|
version = excluded.version,
|
|
29139
29855
|
updated_at = excluded.updated_at`,
|
|
29856
|
+
scope.ownerId,
|
|
29140
29857
|
name,
|
|
29141
29858
|
frontmatter.description,
|
|
29142
29859
|
frontmatter.version ?? null,
|
|
29143
|
-
existing ? existing.enabled ? 1 : 0 : 1
|
|
29860
|
+
existing ? existing.enabled ? 1 : 0 : 1,
|
|
29861
|
+
scope.ownerId === SHARED_SKILL_OWNER ? 1 : 0
|
|
29144
29862
|
);
|
|
29145
29863
|
for (const f of files) {
|
|
29146
29864
|
const p = f.path.replace(/^\.\//, "");
|
|
29147
29865
|
await this.ctx.storage.sql.exec(
|
|
29148
|
-
`INSERT INTO skill_files (skill_name, path, content, size) VALUES (?, ?, ?, ?)`,
|
|
29866
|
+
`INSERT INTO skill_files (owner_id, skill_name, path, content, size) VALUES (?, ?, ?, ?, ?)`,
|
|
29867
|
+
scope.ownerId,
|
|
29149
29868
|
name,
|
|
29150
29869
|
p,
|
|
29151
29870
|
f.content,
|
|
29152
29871
|
f.content.length
|
|
29153
29872
|
);
|
|
29154
29873
|
}
|
|
29155
|
-
|
|
29874
|
+
const installed = await this.getSkill(ownScope, name);
|
|
29875
|
+
if (!installed) throw new Error(`Installed skill was not found: ${name}`);
|
|
29876
|
+
return installed;
|
|
29156
29877
|
}
|
|
29157
29878
|
/** Enable/disable an installed skill. Returns false when not installed. */
|
|
29158
|
-
async setSkillEnabled(name, enabled) {
|
|
29879
|
+
async setSkillEnabled(scope, name, enabled) {
|
|
29159
29880
|
await this.ensureMigrated();
|
|
29160
|
-
const existing = await this.getSkill(name);
|
|
29881
|
+
const existing = await this.getSkill({ ownerId: scope.ownerId }, name);
|
|
29161
29882
|
if (!existing) return false;
|
|
29162
29883
|
await this.ctx.storage.sql.exec(
|
|
29163
|
-
`UPDATE skills SET enabled = ?, updated_at = strftime('%s', 'now')
|
|
29884
|
+
`UPDATE skills SET enabled = ?, updated_at = strftime('%s', 'now')
|
|
29885
|
+
WHERE owner_id = ? AND name = ?`,
|
|
29164
29886
|
enabled ? 1 : 0,
|
|
29887
|
+
scope.ownerId,
|
|
29165
29888
|
name
|
|
29166
29889
|
);
|
|
29167
29890
|
return true;
|
|
@@ -29236,12 +29959,20 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
29236
29959
|
return true;
|
|
29237
29960
|
}
|
|
29238
29961
|
/** Uninstall a skill and its files. Returns false when not installed. */
|
|
29239
|
-
async deleteSkill(name) {
|
|
29962
|
+
async deleteSkill(scope, name) {
|
|
29240
29963
|
await this.ensureMigrated();
|
|
29241
|
-
const existing = await this.getSkill(name);
|
|
29964
|
+
const existing = await this.getSkill({ ownerId: scope.ownerId }, name);
|
|
29242
29965
|
if (!existing) return false;
|
|
29243
|
-
await this.ctx.storage.sql.exec(
|
|
29244
|
-
|
|
29966
|
+
await this.ctx.storage.sql.exec(
|
|
29967
|
+
`DELETE FROM skill_files WHERE owner_id = ? AND skill_name = ?`,
|
|
29968
|
+
scope.ownerId,
|
|
29969
|
+
name
|
|
29970
|
+
);
|
|
29971
|
+
await this.ctx.storage.sql.exec(
|
|
29972
|
+
`DELETE FROM skills WHERE owner_id = ? AND name = ?`,
|
|
29973
|
+
scope.ownerId,
|
|
29974
|
+
name
|
|
29975
|
+
);
|
|
29245
29976
|
return true;
|
|
29246
29977
|
}
|
|
29247
29978
|
};
|
|
@@ -29333,8 +30064,38 @@ function selectBridgeSocket(candidates, ownerClientId) {
|
|
|
29333
30064
|
|
|
29334
30065
|
// src/durable-objects/DurableThread.ts
|
|
29335
30066
|
init_code_execution();
|
|
30067
|
+
init_remoteAttachment();
|
|
29336
30068
|
var DEFAULT_CLIENT_TOOL_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
29337
30069
|
var BRIDGE_LIVENESS_TIMEOUT_MS = 20 * 1e3;
|
|
30070
|
+
var INTERNAL_TOOL_DELIVERY = Symbol("internal-tool-delivery");
|
|
30071
|
+
function generateToolDeliveryToken() {
|
|
30072
|
+
const bytes = crypto.getRandomValues(new Uint8Array(32));
|
|
30073
|
+
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
30074
|
+
}
|
|
30075
|
+
async function hashToolDeliveryToken(token) {
|
|
30076
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(token));
|
|
30077
|
+
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
30078
|
+
}
|
|
30079
|
+
async function createToolDeliveryCapability() {
|
|
30080
|
+
const token = generateToolDeliveryToken();
|
|
30081
|
+
return { token, tokenHash: await hashToolDeliveryToken(token) };
|
|
30082
|
+
}
|
|
30083
|
+
async function matchesToolDeliveryToken(expectedHash, token) {
|
|
30084
|
+
if (!expectedHash || !token || token.length > 256) return false;
|
|
30085
|
+
const actualHash = await hashToolDeliveryToken(token);
|
|
30086
|
+
if (expectedHash.length !== actualHash.length) return false;
|
|
30087
|
+
let difference = 0;
|
|
30088
|
+
for (let index = 0; index < actualHash.length; index++) {
|
|
30089
|
+
difference |= expectedHash.charCodeAt(index) ^ actualHash.charCodeAt(index);
|
|
30090
|
+
}
|
|
30091
|
+
return difference === 0;
|
|
30092
|
+
}
|
|
30093
|
+
function matchesToolDeliveryOwner(pending, proof) {
|
|
30094
|
+
return !pending.dispatched_client_id || pending.dispatched_client_id === proof.clientId && Number(pending.dispatched_generation ?? 0) === (proof.generation ?? -1);
|
|
30095
|
+
}
|
|
30096
|
+
async function authorizesToolDelivery(pending, proof) {
|
|
30097
|
+
return matchesToolDeliveryOwner(pending, proof) && await matchesToolDeliveryToken(pending.delivery_token_hash, proof.token);
|
|
30098
|
+
}
|
|
29338
30099
|
var RUNTIME_STORAGE_PREFIX = "__agentbuilder_runtime_storage__:";
|
|
29339
30100
|
function normalizeEndpointThreadPath(path21) {
|
|
29340
30101
|
if (!path21 || /^[A-Za-z][A-Za-z\d+\-.]*:/.test(path21)) {
|
|
@@ -29485,6 +30246,30 @@ function createEndpointThreadStateBridge(state) {
|
|
|
29485
30246
|
getFileThumbnail: (path21) => state.getFileThumbnail(normalizeEndpointThreadPath(path21))
|
|
29486
30247
|
};
|
|
29487
30248
|
}
|
|
30249
|
+
function routeBridgeClientFrame(socket, message, handlers) {
|
|
30250
|
+
const attachment = socket.deserializeAttachment();
|
|
30251
|
+
const attachedAsBridge = attachment !== null && typeof attachment === "object" && "type" in attachment && attachment.type === "bridge";
|
|
30252
|
+
if (!attachedAsBridge || !handlers.isTracked(socket)) return false;
|
|
30253
|
+
handlers.markSeen(socket);
|
|
30254
|
+
if (typeof message !== "string") return false;
|
|
30255
|
+
let parsed;
|
|
30256
|
+
try {
|
|
30257
|
+
const value = JSON.parse(message);
|
|
30258
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
30259
|
+
parsed = value;
|
|
30260
|
+
} catch {
|
|
30261
|
+
return false;
|
|
30262
|
+
}
|
|
30263
|
+
if (parsed.type === "tool_response") {
|
|
30264
|
+
handlers.resolveToolResponse(socket, parsed);
|
|
30265
|
+
return true;
|
|
30266
|
+
}
|
|
30267
|
+
if (typeof parsed.type === "string" && parsed.type.startsWith("stream_") && parsed.type !== "stream_ping" && parsed.type !== "stream_pong") {
|
|
30268
|
+
handlers.handleStreamFrame(socket, parsed);
|
|
30269
|
+
return true;
|
|
30270
|
+
}
|
|
30271
|
+
return false;
|
|
30272
|
+
}
|
|
29488
30273
|
var DurableThread = class _DurableThread extends DurableObject {
|
|
29489
30274
|
migratedToVersion = null;
|
|
29490
30275
|
logSockets = /* @__PURE__ */ new Set();
|
|
@@ -30879,9 +31664,9 @@ var DurableThread = class _DurableThread extends DurableObject {
|
|
|
30879
31664
|
* Each migration is run in order, starting from the current version + 1.
|
|
30880
31665
|
*/
|
|
30881
31666
|
async runMigrations(fromVersion) {
|
|
30882
|
-
for (const
|
|
30883
|
-
if (
|
|
30884
|
-
await
|
|
31667
|
+
for (const migration58 of migrations) {
|
|
31668
|
+
if (migration58.version > fromVersion) {
|
|
31669
|
+
await migration58.up(this.ctx.storage.sql);
|
|
30885
31670
|
}
|
|
30886
31671
|
}
|
|
30887
31672
|
}
|
|
@@ -31565,14 +32350,17 @@ var DurableThread = class _DurableThread extends DurableObject {
|
|
|
31565
32350
|
return null;
|
|
31566
32351
|
}
|
|
31567
32352
|
const location = sourceFile.location;
|
|
31568
|
-
if (!location
|
|
32353
|
+
if (!location) {
|
|
32354
|
+
return null;
|
|
32355
|
+
}
|
|
32356
|
+
const response = await fetchRemoteAttachment(
|
|
32357
|
+
location,
|
|
32358
|
+
this.env.AGENT_BUILDER_ATTACHMENT_FETCH_HOSTS
|
|
32359
|
+
);
|
|
32360
|
+
if (!response) {
|
|
31569
32361
|
return null;
|
|
31570
32362
|
}
|
|
31571
32363
|
try {
|
|
31572
|
-
const response = await fetch(location);
|
|
31573
|
-
if (!response.ok) {
|
|
31574
|
-
return null;
|
|
31575
|
-
}
|
|
31576
32364
|
const buffer = await response.arrayBuffer();
|
|
31577
32365
|
return this.arrayBufferToBase64(buffer);
|
|
31578
32366
|
} catch {
|
|
@@ -33269,7 +34057,9 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33269
34057
|
void 0,
|
|
33270
34058
|
"The client executing this operation was replaced before it finished. Retry if the work is still needed.",
|
|
33271
34059
|
previous.client_id,
|
|
33272
|
-
previous.generation
|
|
34060
|
+
previous.generation,
|
|
34061
|
+
void 0,
|
|
34062
|
+
INTERNAL_TOOL_DELIVERY
|
|
33273
34063
|
);
|
|
33274
34064
|
if (delivered) sides.add(delivered.side);
|
|
33275
34065
|
}
|
|
@@ -33299,18 +34089,21 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33299
34089
|
data: { noClient: true }
|
|
33300
34090
|
};
|
|
33301
34091
|
}
|
|
34092
|
+
let deliveryToken;
|
|
33302
34093
|
if (durability) {
|
|
33303
34094
|
try {
|
|
33304
34095
|
await this.ensureMigrated();
|
|
33305
34096
|
await this.ensurePendingClientTable();
|
|
34097
|
+
const capability = await createToolDeliveryCapability();
|
|
34098
|
+
deliveryToken = capability.token;
|
|
33306
34099
|
const ownerId = owner?.client_id ?? null;
|
|
33307
34100
|
const socketClientId = this.bridgeClientInfo.get(socket)?.clientId ?? null;
|
|
33308
34101
|
const fenced = owner && socketClientId === owner.client_id;
|
|
33309
34102
|
await this.ctx.storage.sql.exec(
|
|
33310
34103
|
`INSERT OR REPLACE INTO pending_client_calls
|
|
33311
34104
|
(tool_call_id, tool, args, request_permission, risk, side, status, created_at,
|
|
33312
|
-
dispatched_client_id, dispatched_generation)
|
|
33313
|
-
VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?, ?)`,
|
|
34105
|
+
dispatched_client_id, dispatched_generation, delivery_token_hash)
|
|
34106
|
+
VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?, ?, ?)`,
|
|
33314
34107
|
durability.toolCallId,
|
|
33315
34108
|
request.tool,
|
|
33316
34109
|
JSON.stringify(request.args ?? {}),
|
|
@@ -33319,7 +34112,8 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33319
34112
|
durability.side,
|
|
33320
34113
|
Date.now(),
|
|
33321
34114
|
fenced ? ownerId : null,
|
|
33322
|
-
fenced ? owner.generation : 0
|
|
34115
|
+
fenced ? owner.generation : 0,
|
|
34116
|
+
capability.tokenHash
|
|
33323
34117
|
);
|
|
33324
34118
|
} catch (error) {
|
|
33325
34119
|
console.error("[DurableThread] Failed to persist fast-path call (continuing):", error);
|
|
@@ -33355,7 +34149,8 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33355
34149
|
tool: request.tool,
|
|
33356
34150
|
args: request.args ?? {},
|
|
33357
34151
|
requestPermission: request.requestPermission ?? null,
|
|
33358
|
-
risk: typeof request.risk === "number" ? request.risk : null
|
|
34152
|
+
risk: typeof request.risk === "number" ? request.risk : null,
|
|
34153
|
+
deliveryToken: deliveryToken ?? null
|
|
33359
34154
|
});
|
|
33360
34155
|
try {
|
|
33361
34156
|
socket.send(frame);
|
|
@@ -33478,7 +34273,8 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33478
34273
|
status TEXT NOT NULL DEFAULT 'pending',
|
|
33479
34274
|
created_at INTEGER NOT NULL,
|
|
33480
34275
|
dispatched_client_id TEXT,
|
|
33481
|
-
dispatched_generation INTEGER NOT NULL DEFAULT 0
|
|
34276
|
+
dispatched_generation INTEGER NOT NULL DEFAULT 0,
|
|
34277
|
+
delivery_token_hash TEXT
|
|
33482
34278
|
)
|
|
33483
34279
|
`);
|
|
33484
34280
|
}
|
|
@@ -33519,13 +34315,15 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33519
34315
|
const socketClientId = this.bridgeClientInfo.get(socket)?.clientId ?? null;
|
|
33520
34316
|
const fenced = owner && socketClientId === owner.client_id;
|
|
33521
34317
|
const generation = fenced ? owner.generation : 0;
|
|
34318
|
+
const capability = await createToolDeliveryCapability();
|
|
33522
34319
|
try {
|
|
33523
34320
|
await this.ctx.storage.sql.exec(
|
|
33524
34321
|
`UPDATE pending_client_calls
|
|
33525
|
-
SET dispatched_client_id = ?, dispatched_generation = ?
|
|
34322
|
+
SET dispatched_client_id = ?, dispatched_generation = ?, delivery_token_hash = ?
|
|
33526
34323
|
WHERE tool_call_id = ? AND status = 'pending'`,
|
|
33527
34324
|
fenced ? owner.client_id : null,
|
|
33528
34325
|
generation,
|
|
34326
|
+
capability.tokenHash,
|
|
33529
34327
|
toolCallId
|
|
33530
34328
|
);
|
|
33531
34329
|
socket.send(
|
|
@@ -33537,7 +34335,8 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33537
34335
|
args: request.args ?? {},
|
|
33538
34336
|
requestPermission: request.requestPermission ?? null,
|
|
33539
34337
|
risk: typeof request.risk === "number" ? request.risk : null,
|
|
33540
|
-
generation
|
|
34338
|
+
generation,
|
|
34339
|
+
deliveryToken: capability.token
|
|
33541
34340
|
})
|
|
33542
34341
|
);
|
|
33543
34342
|
this.bridgeLastSeen.set(socket, Date.now());
|
|
@@ -33660,12 +34459,14 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33660
34459
|
const pending = await this.getPendingClientCalls();
|
|
33661
34460
|
for (const p of pending) {
|
|
33662
34461
|
if (socket.readyState !== WebSocket.OPEN) break;
|
|
34462
|
+
const capability = await createToolDeliveryCapability();
|
|
33663
34463
|
await this.ctx.storage.sql.exec(
|
|
33664
34464
|
`UPDATE pending_client_calls
|
|
33665
|
-
SET dispatched_client_id = ?, dispatched_generation = ?
|
|
34465
|
+
SET dispatched_client_id = ?, dispatched_generation = ?, delivery_token_hash = ?
|
|
33666
34466
|
WHERE tool_call_id = ? AND status = 'pending'`,
|
|
33667
34467
|
fenced ? owner.client_id : null,
|
|
33668
34468
|
generation,
|
|
34469
|
+
capability.tokenHash,
|
|
33669
34470
|
p.toolCallId
|
|
33670
34471
|
);
|
|
33671
34472
|
socket.send(
|
|
@@ -33677,7 +34478,8 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33677
34478
|
args: p.args,
|
|
33678
34479
|
requestPermission: p.requestPermission,
|
|
33679
34480
|
risk: p.risk,
|
|
33680
|
-
generation
|
|
34481
|
+
generation,
|
|
34482
|
+
deliveryToken: capability.token
|
|
33681
34483
|
})
|
|
33682
34484
|
);
|
|
33683
34485
|
}
|
|
@@ -33690,20 +34492,25 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33690
34492
|
* call resolved. Returns the side to continue, or null if there is no pending
|
|
33691
34493
|
* call (unknown id or already resolved — the delivery is idempotent).
|
|
33692
34494
|
*/
|
|
33693
|
-
async deliverClientToolResult(toolCallId, ok, result, error, deliveredByClientId, deliveredGeneration) {
|
|
34495
|
+
async deliverClientToolResult(toolCallId, ok, result, error, deliveredByClientId, deliveredGeneration, deliveryToken, internalAuthorization) {
|
|
33694
34496
|
await this.ensureMigrated();
|
|
33695
34497
|
await this.ensurePendingClientTable();
|
|
33696
34498
|
const pendingCursor = await this.ctx.storage.sql.exec(
|
|
33697
|
-
`SELECT side, status, dispatched_client_id, dispatched_generation
|
|
34499
|
+
`SELECT side, status, dispatched_client_id, dispatched_generation, delivery_token_hash
|
|
33698
34500
|
FROM pending_client_calls WHERE tool_call_id = ? LIMIT 1`,
|
|
33699
34501
|
toolCallId
|
|
33700
34502
|
);
|
|
33701
34503
|
const pending = pendingCursor.toArray()[0];
|
|
33702
34504
|
if (!pending || pending.status !== "pending") return null;
|
|
33703
|
-
|
|
33704
|
-
|
|
33705
|
-
|
|
33706
|
-
|
|
34505
|
+
const proof = {
|
|
34506
|
+
token: deliveryToken,
|
|
34507
|
+
clientId: deliveredByClientId,
|
|
34508
|
+
generation: deliveredGeneration
|
|
34509
|
+
};
|
|
34510
|
+
if (internalAuthorization !== INTERNAL_TOOL_DELIVERY) {
|
|
34511
|
+
if (!await authorizesToolDelivery(pending, proof)) return null;
|
|
34512
|
+
} else if (!matchesToolDeliveryOwner(pending, proof)) {
|
|
34513
|
+
return null;
|
|
33707
34514
|
}
|
|
33708
34515
|
const side = pending.side === "b" ? "b" : "a";
|
|
33709
34516
|
const content = ok ? result ?? "" : `Error: ${error ?? "tool failed"}`;
|
|
@@ -34420,29 +35227,17 @@ ${resultContent}${attachmentSummary}`;
|
|
|
34420
35227
|
*/
|
|
34421
35228
|
async webSocketMessage(ws, message) {
|
|
34422
35229
|
try {
|
|
34423
|
-
|
|
34424
|
-
this.
|
|
34425
|
-
|
|
35230
|
+
const handledBridgeFrame = routeBridgeClientFrame(ws, message, {
|
|
35231
|
+
isTracked: (socket) => this.bridgeSockets.has(socket),
|
|
35232
|
+
markSeen: (socket) => this.bridgeLastSeen.set(socket, Date.now()),
|
|
35233
|
+
resolveToolResponse: (socket, payload) => this.resolveClientCall(socket, payload),
|
|
35234
|
+
handleStreamFrame: (socket, payload) => this.handleClientStreamFrame(socket, payload)
|
|
35235
|
+
});
|
|
35236
|
+
if (handledBridgeFrame) return;
|
|
34426
35237
|
if (typeof message === "string" && message === "ping") {
|
|
34427
35238
|
ws.send("pong");
|
|
34428
35239
|
return;
|
|
34429
35240
|
}
|
|
34430
|
-
if (typeof message === "string") {
|
|
34431
|
-
let parsed = null;
|
|
34432
|
-
try {
|
|
34433
|
-
parsed = JSON.parse(message);
|
|
34434
|
-
} catch {
|
|
34435
|
-
parsed = null;
|
|
34436
|
-
}
|
|
34437
|
-
if (parsed && parsed.type === "tool_response") {
|
|
34438
|
-
this.resolveClientCall(ws, parsed);
|
|
34439
|
-
return;
|
|
34440
|
-
}
|
|
34441
|
-
if (parsed && typeof parsed.type === "string" && parsed.type.startsWith("stream_") && parsed.type !== "stream_ping" && parsed.type !== "stream_pong") {
|
|
34442
|
-
this.handleClientStreamFrame(ws, parsed);
|
|
34443
|
-
return;
|
|
34444
|
-
}
|
|
34445
|
-
}
|
|
34446
35241
|
} catch (error) {
|
|
34447
35242
|
console.error("[DurableThread] Error handling WebSocket message:", error);
|
|
34448
35243
|
}
|
|
@@ -37449,6 +38244,6 @@ init_context();
|
|
|
37449
38244
|
init_ProviderRegistry();
|
|
37450
38245
|
init_types2();
|
|
37451
38246
|
|
|
37452
|
-
export { CLIENT_STREAM_CHUNK_BYTES, CodeExecutionBridge, DurableAgentBuilder, DurableThread, DurableUserHub, FlowStateSdk, NamespaceResolutionError, PackageDiscoveryService, ProviderRegistry, ThreadWatchdog, agentbuilder, assistantMessageText, authenticate, buildImageDescription, builder, callClient, callClientStream, canAccessThread, cat, createNamespaceContext, createThreadEndpointHandler, decodeStreamChunk, defineController2 as defineController, discoverPackages, emitThreadEvent, encodeStreamChunk, enhanceFlowState, exists, find, forceTurn, generateAgentFile, generateImageDescription, generateModelFile, generatePromptFile, getFileStats, getMessages, getMessagesToSummarize, getShortName, getThumbnail, getUnsummarizedImageAttachments, getVisibleAgentNames, getVisibleModelNames, getVisiblePromptNames, getVisibleToolNames, grep, hasImageAttachments, hasJsonArrayEntries, head, injectMessage, isAdminOnlyApiRoute, isHostedInstance, isPublicApiRoute, isQualifiedName, isTrustedLocalDevelopmentRequest, isVisibleTerminalAssistantMessage, linkFile, messageTimestampSeconds, mkdir, move, optimizeImageContext, parseQualifiedName, qualifyName, queueTool, readFile, readdir, reloadHistory, rename, replaceImagesWithDescriptions, requireAdmin, requireAuth, resolveAgent, resolveHook, resolveModel, resolvePrompt, resolveTool, rmdir, stat, supportsClientForwarding, tail, unlink, updateThread, writeFile, writeImage };
|
|
38247
|
+
export { CLIENT_STREAM_CHUNK_BYTES, CodeExecutionBridge, DurableAgentBuilder, DurableThread, DurableUserHub, FlowStateSdk, NamespaceResolutionError, PackageDiscoveryService, ProviderRegistry, ThreadWatchdog, agentbuilder, applyRuntimeCors, assistantMessageText, authenticate, buildImageDescription, builder, callClient, callClientStream, canAccessThread, cat, createNamespaceContext, createThreadEndpointHandler, decodeStreamChunk, defineController2 as defineController, discoverPackages, emitThreadEvent, encodeStreamChunk, endpointCallerFromAuth, enhanceFlowState, exists, find, forceTurn, generateAgentFile, generateImageDescription, generateModelFile, generatePromptFile, getFileStats, getMessages, getMessagesToSummarize, getShortName, getThumbnail, getUnsummarizedImageAttachments, getVisibleAgentNames, getVisibleModelNames, getVisiblePromptNames, getVisibleToolNames, grep, hasImageAttachments, hasJsonArrayEntries, head, injectMessage, isAdminOnlyApiRoute, isHostedInstance, isPublicApiRoute, isQualifiedName, isSameOriginBrowserRequest, isTrustedLocalDevelopmentRequest, isVisibleTerminalAssistantMessage, linkFile, messageTimestampSeconds, mkdir, move, optimizeImageContext, parseQualifiedName, qualifyName, queueTool, readFile, readdir, reloadHistory, rename, replaceImagesWithDescriptions, requireAdmin, requireAuth, resolveAgent, resolveHook, resolveModel, resolvePrompt, resolveTool, rmdir, runtimeCorsHeaders, runtimePreflightResponse, stat, supportsClientForwarding, tail, unlink, updateThread, writeFile, writeImage };
|
|
37453
38248
|
//# sourceMappingURL=index.js.map
|
|
37454
38249
|
//# sourceMappingURL=index.js.map
|