@standardagents/builder 0.38.0 → 0.38.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/built-in-routes.js +1243 -418
- 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-BBu-Osn5.d.ts → index-CF-lgN47.d.ts} +74 -11
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1064 -304
- package/dist/index.js.map +1 -1
- package/dist/plugin.js +106 -69
- package/dist/plugin.js.map +1 -1
- package/dist/runtime.d.ts +68 -11
- package/dist/runtime.js +950 -225
- 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,14 +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
21693
|
function isHostedInstance(env) {
|
|
21418
|
-
const value = env
|
|
21694
|
+
const value = env && typeof env === "object" ? Reflect.get(env, "STANDARD_AGENTS_HOSTED") : void 0;
|
|
21419
21695
|
if (typeof value === "string") {
|
|
21420
21696
|
const normalized = value.trim().toLowerCase();
|
|
21421
21697
|
return normalized !== "" && normalized !== "0" && normalized !== "false";
|
|
@@ -21440,10 +21716,54 @@ function isPublicApiRoute(routePath, request, env) {
|
|
|
21440
21716
|
if (isProviderIconRoute(routePath)) {
|
|
21441
21717
|
return true;
|
|
21442
21718
|
}
|
|
21443
|
-
|
|
21444
|
-
|
|
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") {
|
|
21751
|
+
return true;
|
|
21752
|
+
}
|
|
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
|
+
}
|
|
21445
21764
|
}
|
|
21446
21765
|
function isAdminOnlyApiRoute(routePath, method = "GET") {
|
|
21766
|
+
if (routePath === "/api/hooks") return true;
|
|
21447
21767
|
if (routePath === "/api/skills" || routePath.startsWith("/api/skills/")) return true;
|
|
21448
21768
|
if (routePath === "/api/pack" || routePath.startsWith("/api/pack/")) return true;
|
|
21449
21769
|
if (routePath === "/api/unpack" || routePath.startsWith("/api/unpack/")) return true;
|
|
@@ -21462,6 +21782,9 @@ function isAdminOnlyApiRoute(routePath, method = "GET") {
|
|
|
21462
21782
|
}
|
|
21463
21783
|
|
|
21464
21784
|
// src/plugin/dev-middleware.ts
|
|
21785
|
+
function isEncryptedSocket(socket) {
|
|
21786
|
+
return "encrypted" in socket && socket.encrypted === true;
|
|
21787
|
+
}
|
|
21465
21788
|
function readRawRequestBody(req) {
|
|
21466
21789
|
return new Promise((resolve3, reject) => {
|
|
21467
21790
|
const chunks = [];
|
|
@@ -21486,6 +21809,36 @@ function resolveContainedFile(baseDir, requestPath) {
|
|
|
21486
21809
|
return null;
|
|
21487
21810
|
}
|
|
21488
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
|
+
}
|
|
21489
21842
|
function injectUiConfigIntoHtml(htmlContent) {
|
|
21490
21843
|
const configScript = `<script>window.__AGENTBUILDER_CONFIG__ = { devMode: true };</script>`;
|
|
21491
21844
|
return htmlContent.replace(/\/agents\//g, "/").replace("</head>", `${configScript}</head>`);
|
|
@@ -21540,11 +21893,21 @@ function createDevMiddleware(server, context) {
|
|
|
21540
21893
|
);
|
|
21541
21894
|
} catch {
|
|
21542
21895
|
}
|
|
21543
|
-
if (isAdminOnlyApiRoute(routePath, method)
|
|
21544
|
-
|
|
21545
|
-
|
|
21546
|
-
|
|
21547
|
-
|
|
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
|
+
}
|
|
21548
21911
|
}
|
|
21549
21912
|
if (await handleConfigApiRequest({
|
|
21550
21913
|
req,
|
|
@@ -22199,8 +22562,13 @@ import {
|
|
|
22199
22562
|
canAccessThread,
|
|
22200
22563
|
endpointCallerFromAuth,
|
|
22201
22564
|
isHostedInstance,
|
|
22565
|
+
isTrustedLocalDevelopmentRequest,
|
|
22566
|
+
isSameOriginBrowserRequest,
|
|
22202
22567
|
isPublicApiRoute,
|
|
22203
22568
|
isAdminOnlyApiRoute,
|
|
22569
|
+
applyRuntimeCors,
|
|
22570
|
+
runtimeCorsHeaders,
|
|
22571
|
+
runtimePreflightResponse,
|
|
22204
22572
|
} from "@standardagents/builder/runtime";
|
|
22205
22573
|
import { isThreadEndpoint, getEndpointAccess } from "@standardagents/spec";
|
|
22206
22574
|
|
|
@@ -22257,71 +22625,34 @@ function isHtmlNavigationRequest(request) {
|
|
|
22257
22625
|
return (accept === '' || accept === '*/*') && isUiNavigationPath(pathname);
|
|
22258
22626
|
}
|
|
22259
22627
|
|
|
22260
|
-
// CORS headers for API responses
|
|
22261
|
-
const CORS_HEADERS = {
|
|
22262
|
-
"Access-Control-Allow-Origin": "*",
|
|
22263
|
-
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
22264
|
-
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With",
|
|
22265
|
-
"Access-Control-Max-Age": "86400",
|
|
22266
|
-
};
|
|
22267
|
-
|
|
22268
22628
|
// Helper to create headers with CORS
|
|
22269
|
-
function corsHeaders(contentType) {
|
|
22629
|
+
function corsHeaders(contentType, request, env) {
|
|
22270
22630
|
return {
|
|
22271
22631
|
"Content-Type": contentType,
|
|
22272
|
-
...
|
|
22632
|
+
...runtimeCorsHeaders(request, env),
|
|
22273
22633
|
};
|
|
22274
22634
|
}
|
|
22275
22635
|
|
|
22276
|
-
// Helper to add CORS headers to any Response without touching the body
|
|
22277
|
-
function addCorsHeaders(response) {
|
|
22278
|
-
// Skip WebSocket upgrade responses - they can't be wrapped
|
|
22279
|
-
if (response.status === 101) {
|
|
22280
|
-
return response;
|
|
22281
|
-
}
|
|
22282
|
-
|
|
22283
|
-
// Skip if already has CORS headers
|
|
22284
|
-
if (response.headers.has("Access-Control-Allow-Origin")) {
|
|
22285
|
-
return response;
|
|
22286
|
-
}
|
|
22287
|
-
|
|
22288
|
-
// Prefer mutating the original Response headers. Rebuilding a Response from
|
|
22289
|
-
// response.headers can drop special headers such as Set-Cookie in Worker
|
|
22290
|
-
// runtimes, even when the header is not visible through Headers.has().
|
|
22291
|
-
try {
|
|
22292
|
-
for (const [key, value] of Object.entries(CORS_HEADERS)) {
|
|
22293
|
-
response.headers.set(key, value);
|
|
22294
|
-
}
|
|
22295
|
-
return response;
|
|
22296
|
-
} catch {
|
|
22297
|
-
// Some Responses, especially fetched upstream responses, have immutable
|
|
22298
|
-
// headers. Fall back to wrapping those responses.
|
|
22299
|
-
}
|
|
22300
|
-
|
|
22301
|
-
// Create new headers with CORS added
|
|
22302
|
-
const newHeaders = new Headers(response.headers);
|
|
22303
|
-
for (const [key, value] of Object.entries(CORS_HEADERS)) {
|
|
22304
|
-
newHeaders.set(key, value);
|
|
22305
|
-
}
|
|
22306
|
-
|
|
22307
|
-
// Return new Response with same body stream (not cloned, just transferred)
|
|
22308
|
-
return new Response(response.body, {
|
|
22309
|
-
status: response.status,
|
|
22310
|
-
statusText: response.statusText,
|
|
22311
|
-
headers: newHeaders,
|
|
22312
|
-
});
|
|
22313
|
-
}
|
|
22314
|
-
|
|
22315
22636
|
export async function router(request, env) {
|
|
22316
22637
|
const url = new URL(request.url);
|
|
22317
22638
|
const pathname = url.pathname;
|
|
22318
22639
|
|
|
22319
22640
|
// Handle CORS preflight requests
|
|
22320
22641
|
if (request.method === "OPTIONS") {
|
|
22321
|
-
return
|
|
22322
|
-
|
|
22323
|
-
|
|
22324
|
-
|
|
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
|
+
);
|
|
22325
22656
|
}
|
|
22326
22657
|
|
|
22327
22658
|
const routePath = pathname || "/";
|
|
@@ -22363,19 +22694,22 @@ ${packedThreadRouteCode}
|
|
|
22363
22694
|
// across requests.
|
|
22364
22695
|
let threadAccess = null;
|
|
22365
22696
|
|
|
22366
|
-
// Require authentication for all API routes except public ones
|
|
22697
|
+
// Require authentication for all API routes except public ones.
|
|
22698
|
+
// WebSocket tickets are single-use and are consumed here. Controllers
|
|
22699
|
+
// must reuse context.auth (resolveRequestAuth) rather than calling
|
|
22700
|
+
// requireAuth again, or a ticket-only upgrade will 401.
|
|
22367
22701
|
if (isApiRoute && !publicRoute) {
|
|
22368
22702
|
const authResult = await requireAuth(request, env);
|
|
22369
22703
|
|
|
22370
22704
|
// If requireAuth returns a Response, it's an error (401)
|
|
22371
22705
|
if (authResult instanceof Response) {
|
|
22372
|
-
return
|
|
22706
|
+
return applyRuntimeCors(authResult, request, env);
|
|
22373
22707
|
}
|
|
22374
22708
|
|
|
22375
22709
|
authContext = authResult;
|
|
22376
22710
|
|
|
22377
22711
|
if (isAdminOnlyApiRoute(routePath, request.method) && authContext.user.role !== 'admin') {
|
|
22378
|
-
return
|
|
22712
|
+
return applyRuntimeCors(Response.json({ error: 'Forbidden: Admin access required' }, { status: 403 }), request, env);
|
|
22379
22713
|
}
|
|
22380
22714
|
|
|
22381
22715
|
if (routePath.startsWith('/api/threads/')) {
|
|
@@ -22389,10 +22723,10 @@ ${packedThreadRouteCode}
|
|
|
22389
22723
|
const agentBuilder = env.AGENT_BUILDER.get(agentBuilderId);
|
|
22390
22724
|
const thread = await agentBuilder.getThread(threadId);
|
|
22391
22725
|
if (!thread) {
|
|
22392
|
-
return
|
|
22726
|
+
return applyRuntimeCors(Response.json({ error: \`Thread not found: \${threadId}\` }, { status: 404 }), request, env);
|
|
22393
22727
|
}
|
|
22394
22728
|
if (!canAccessThread(authContext, thread)) {
|
|
22395
|
-
return
|
|
22729
|
+
return applyRuntimeCors(Response.json({ error: "Forbidden: You don't have access to this thread" }, { status: 403 }), request, env);
|
|
22396
22730
|
}
|
|
22397
22731
|
threadAccess = { threadId, thread };
|
|
22398
22732
|
}
|
|
@@ -22435,16 +22769,16 @@ ${packedThreadRouteCode}
|
|
|
22435
22769
|
const result = await controller(context);
|
|
22436
22770
|
|
|
22437
22771
|
if (result instanceof Response) {
|
|
22438
|
-
return
|
|
22772
|
+
return applyRuntimeCors(result, request, env);
|
|
22439
22773
|
}
|
|
22440
22774
|
if (typeof result === "string") {
|
|
22441
22775
|
return new Response(result, {
|
|
22442
|
-
headers: corsHeaders("text/plain"),
|
|
22776
|
+
headers: corsHeaders("text/plain", request, env),
|
|
22443
22777
|
});
|
|
22444
22778
|
}
|
|
22445
22779
|
// JSON responses get CORS headers
|
|
22446
22780
|
return new Response(JSON.stringify(result), {
|
|
22447
|
-
headers: corsHeaders("application/json"),
|
|
22781
|
+
headers: corsHeaders("application/json", request, env),
|
|
22448
22782
|
});
|
|
22449
22783
|
}
|
|
22450
22784
|
|
|
@@ -23211,95 +23545,68 @@ var builder = agentbuilder;
|
|
|
23211
23545
|
// src/router/index.ts
|
|
23212
23546
|
init_ThreadStateImpl();
|
|
23213
23547
|
|
|
23214
|
-
// src/
|
|
23215
|
-
|
|
23216
|
-
|
|
23217
|
-
|
|
23218
|
-
|
|
23219
|
-
|
|
23220
|
-
|
|
23221
|
-
|
|
23222
|
-
|
|
23223
|
-
|
|
23224
|
-
|
|
23225
|
-
|
|
23226
|
-
|
|
23227
|
-
|
|
23228
|
-
|
|
23229
|
-
|
|
23230
|
-
|
|
23548
|
+
// src/routing/cors.ts
|
|
23549
|
+
var RUNTIME_CORS_HEADERS = {
|
|
23550
|
+
"Access-Control-Allow-Origin": "*",
|
|
23551
|
+
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
23552
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With",
|
|
23553
|
+
"Access-Control-Max-Age": "86400"
|
|
23554
|
+
};
|
|
23555
|
+
var CORS_RESPONSE_HEADERS = [
|
|
23556
|
+
"Access-Control-Allow-Origin",
|
|
23557
|
+
"Access-Control-Allow-Methods",
|
|
23558
|
+
"Access-Control-Allow-Headers",
|
|
23559
|
+
"Access-Control-Max-Age",
|
|
23560
|
+
"Access-Control-Allow-Credentials",
|
|
23561
|
+
"Access-Control-Expose-Headers"
|
|
23562
|
+
];
|
|
23563
|
+
function mutateCorsHeaders(headers, exposeCors) {
|
|
23564
|
+
if (exposeCors) {
|
|
23565
|
+
for (const [key, value] of Object.entries(RUNTIME_CORS_HEADERS)) {
|
|
23566
|
+
if (!headers.has(key)) headers.set(key, value);
|
|
23231
23567
|
}
|
|
23568
|
+
return;
|
|
23569
|
+
}
|
|
23570
|
+
for (const key of CORS_RESPONSE_HEADERS) {
|
|
23571
|
+
headers.delete(key);
|
|
23232
23572
|
}
|
|
23233
|
-
return null;
|
|
23234
23573
|
}
|
|
23235
|
-
function
|
|
23236
|
-
return
|
|
23574
|
+
function exposesRuntimeCors(request, env) {
|
|
23575
|
+
return !isTrustedLocalDevelopmentRequest(request, env);
|
|
23237
23576
|
}
|
|
23238
|
-
function
|
|
23239
|
-
return
|
|
23577
|
+
function runtimeCorsHeaders(request, env) {
|
|
23578
|
+
return exposesRuntimeCors(request, env) ? { ...RUNTIME_CORS_HEADERS } : {};
|
|
23240
23579
|
}
|
|
23241
|
-
|
|
23580
|
+
function applyRuntimeCors(response, request, env) {
|
|
23581
|
+
if (response.status === 101) return response;
|
|
23582
|
+
const exposeCors = exposesRuntimeCors(request, env);
|
|
23242
23583
|
try {
|
|
23243
|
-
|
|
23244
|
-
|
|
23245
|
-
|
|
23246
|
-
}
|
|
23247
|
-
const [payloadB64, signatureB64] = parts;
|
|
23248
|
-
const encoder = new TextEncoder();
|
|
23249
|
-
const keyData = encoder.encode(encryptionKey);
|
|
23250
|
-
const cryptoKey = await crypto.subtle.importKey(
|
|
23251
|
-
"raw",
|
|
23252
|
-
keyData,
|
|
23253
|
-
{ name: "HMAC", hash: "SHA-256" },
|
|
23254
|
-
false,
|
|
23255
|
-
["verify"]
|
|
23256
|
-
);
|
|
23257
|
-
const payloadData = base64ToBuffer(payloadB64);
|
|
23258
|
-
const signature = base64ToBuffer(signatureB64);
|
|
23259
|
-
const isValid = await crypto.subtle.verify("HMAC", cryptoKey, signature, payloadData);
|
|
23260
|
-
if (!isValid) {
|
|
23261
|
-
return null;
|
|
23262
|
-
}
|
|
23263
|
-
const decoder = new TextDecoder();
|
|
23264
|
-
return decoder.decode(payloadData);
|
|
23265
|
-
} catch (error) {
|
|
23266
|
-
console.error("Error verifying signed token:", error);
|
|
23267
|
-
return null;
|
|
23268
|
-
}
|
|
23269
|
-
}
|
|
23270
|
-
async function verifySuperAdminToken(token, encryptionKey) {
|
|
23271
|
-
const payload = await verifySignedToken(token, encryptionKey);
|
|
23272
|
-
if (!payload) {
|
|
23273
|
-
return false;
|
|
23274
|
-
}
|
|
23275
|
-
const parts = payload.split(":");
|
|
23276
|
-
if (parts.length !== 3 || parts[0] !== "super_admin") {
|
|
23277
|
-
return false;
|
|
23278
|
-
}
|
|
23279
|
-
const timestamp = parseInt(parts[1], 10);
|
|
23280
|
-
const age = Date.now() - timestamp;
|
|
23281
|
-
const maxAge = 30 * 24 * 60 * 60 * 1e3;
|
|
23282
|
-
if (age > maxAge) {
|
|
23283
|
-
return false;
|
|
23584
|
+
mutateCorsHeaders(response.headers, exposeCors);
|
|
23585
|
+
return response;
|
|
23586
|
+
} catch {
|
|
23284
23587
|
}
|
|
23285
|
-
|
|
23588
|
+
const headers = new Headers(response.headers);
|
|
23589
|
+
mutateCorsHeaders(headers, exposeCors);
|
|
23590
|
+
return new Response(response.body, {
|
|
23591
|
+
status: response.status,
|
|
23592
|
+
statusText: response.statusText,
|
|
23593
|
+
headers
|
|
23594
|
+
});
|
|
23286
23595
|
}
|
|
23287
|
-
function
|
|
23288
|
-
|
|
23289
|
-
|
|
23290
|
-
|
|
23291
|
-
|
|
23596
|
+
function runtimePreflightResponse(request, env) {
|
|
23597
|
+
if (!exposesRuntimeCors(request, env)) {
|
|
23598
|
+
return Response.json(
|
|
23599
|
+
{ error: "Cross-origin access is disabled for local development" },
|
|
23600
|
+
{ status: 403, headers: { "Cache-Control": "no-store" } }
|
|
23601
|
+
);
|
|
23292
23602
|
}
|
|
23293
|
-
return
|
|
23603
|
+
return new Response(null, {
|
|
23604
|
+
status: 204,
|
|
23605
|
+
headers: RUNTIME_CORS_HEADERS
|
|
23606
|
+
});
|
|
23294
23607
|
}
|
|
23295
23608
|
|
|
23296
23609
|
// src/middleware/auth.ts
|
|
23297
|
-
var CORS_HEADERS = {
|
|
23298
|
-
"Access-Control-Allow-Origin": "*",
|
|
23299
|
-
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
|
|
23300
|
-
"Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With",
|
|
23301
|
-
"Access-Control-Max-Age": "86400"
|
|
23302
|
-
};
|
|
23303
23610
|
function endpointCallerFromAuth(auth) {
|
|
23304
23611
|
if (!auth) return null;
|
|
23305
23612
|
const { id, username, role, permissions, email, display_name, avatar_url } = auth.user;
|
|
@@ -23333,43 +23640,88 @@ function extractBearerToken(request) {
|
|
|
23333
23640
|
if (cookieToken) {
|
|
23334
23641
|
return cookieToken;
|
|
23335
23642
|
}
|
|
23336
|
-
const isWebSocket = request.headers.get("upgrade")?.toLowerCase() === "websocket";
|
|
23337
|
-
if (isWebSocket) {
|
|
23338
|
-
try {
|
|
23339
|
-
const url = new URL(request.url);
|
|
23340
|
-
const token = url.searchParams.get("token");
|
|
23341
|
-
if (token) {
|
|
23342
|
-
return token;
|
|
23343
|
-
}
|
|
23344
|
-
} catch (err) {
|
|
23345
|
-
}
|
|
23346
|
-
}
|
|
23347
23643
|
return null;
|
|
23348
23644
|
}
|
|
23349
23645
|
function getAgentBuilder(env) {
|
|
23350
23646
|
const agentBuilderId = env.AGENT_BUILDER.idFromName("singleton");
|
|
23351
23647
|
return env.AGENT_BUILDER.get(agentBuilderId);
|
|
23352
23648
|
}
|
|
23649
|
+
function parseWebSocketTicketAuth(authJson) {
|
|
23650
|
+
let parsed;
|
|
23651
|
+
try {
|
|
23652
|
+
parsed = JSON.parse(authJson);
|
|
23653
|
+
} catch {
|
|
23654
|
+
return null;
|
|
23655
|
+
}
|
|
23656
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
23657
|
+
const record = parsed;
|
|
23658
|
+
if (!record.user || typeof record.user !== "object") return null;
|
|
23659
|
+
const user = record.user;
|
|
23660
|
+
if (typeof user.id !== "string" || typeof user.username !== "string" || typeof user.role !== "string" || record.authType !== "super_admin" && record.authType !== "session" && record.authType !== "api_key") {
|
|
23661
|
+
return null;
|
|
23662
|
+
}
|
|
23663
|
+
const permissions = user.permissions;
|
|
23664
|
+
if (permissions !== void 0 && permissions !== null && (!Array.isArray(permissions) || !permissions.every((permission) => typeof permission === "string"))) {
|
|
23665
|
+
return null;
|
|
23666
|
+
}
|
|
23667
|
+
if (record.credentialId !== void 0 && typeof record.credentialId !== "string") return null;
|
|
23668
|
+
return {
|
|
23669
|
+
user: {
|
|
23670
|
+
id: user.id,
|
|
23671
|
+
username: user.username,
|
|
23672
|
+
role: user.role,
|
|
23673
|
+
permissions,
|
|
23674
|
+
platform_user_id: typeof user.platform_user_id === "string" ? user.platform_user_id : null,
|
|
23675
|
+
email: typeof user.email === "string" ? user.email : null,
|
|
23676
|
+
display_name: typeof user.display_name === "string" ? user.display_name : null,
|
|
23677
|
+
avatar_url: typeof user.avatar_url === "string" ? user.avatar_url : null
|
|
23678
|
+
},
|
|
23679
|
+
authType: record.authType,
|
|
23680
|
+
credentialId: record.credentialId
|
|
23681
|
+
};
|
|
23682
|
+
}
|
|
23683
|
+
async function consumeWebSocketTicket(request, env) {
|
|
23684
|
+
if (request.headers.get("upgrade")?.toLowerCase() !== "websocket") return null;
|
|
23685
|
+
let url;
|
|
23686
|
+
try {
|
|
23687
|
+
url = new URL(request.url);
|
|
23688
|
+
} catch {
|
|
23689
|
+
return null;
|
|
23690
|
+
}
|
|
23691
|
+
const ticket = url.searchParams.get("ticket");
|
|
23692
|
+
if (!ticket) return null;
|
|
23693
|
+
const authJson = await getAgentBuilder(env).consumeWebSocketTicket({
|
|
23694
|
+
ticket_hash: await hashToken(ticket),
|
|
23695
|
+
pathname: url.pathname
|
|
23696
|
+
});
|
|
23697
|
+
return typeof authJson === "string" ? parseWebSocketTicketAuth(authJson) : null;
|
|
23698
|
+
}
|
|
23353
23699
|
async function authenticate(request, env) {
|
|
23700
|
+
const ticketAuth = await consumeWebSocketTicket(request, env);
|
|
23701
|
+
if (ticketAuth) return ticketAuth;
|
|
23354
23702
|
const token = extractBearerToken(request);
|
|
23355
23703
|
if (!token) {
|
|
23356
23704
|
return null;
|
|
23357
23705
|
}
|
|
23358
|
-
if (env.SUPER_ADMIN_PASSWORD && env.ENCRYPTION_KEY) {
|
|
23706
|
+
if (typeof env.SUPER_ADMIN_PASSWORD === "string" && typeof env.ENCRYPTION_KEY === "string") {
|
|
23359
23707
|
if (token.includes(".")) {
|
|
23360
23708
|
const isValidSuperAdmin = await verifySuperAdminToken(
|
|
23361
23709
|
token,
|
|
23362
23710
|
env.ENCRYPTION_KEY
|
|
23363
23711
|
);
|
|
23364
23712
|
if (isValidSuperAdmin) {
|
|
23365
|
-
|
|
23366
|
-
|
|
23367
|
-
|
|
23368
|
-
|
|
23369
|
-
|
|
23370
|
-
|
|
23371
|
-
|
|
23372
|
-
|
|
23713
|
+
const tokenHash = await hashToken(token);
|
|
23714
|
+
const agentBuilder = getAgentBuilder(env);
|
|
23715
|
+
if (await agentBuilder.validateSuperAdminSession(tokenHash)) {
|
|
23716
|
+
return {
|
|
23717
|
+
user: {
|
|
23718
|
+
id: "super_admin",
|
|
23719
|
+
username: "admin",
|
|
23720
|
+
role: "admin"
|
|
23721
|
+
},
|
|
23722
|
+
authType: "super_admin"
|
|
23723
|
+
};
|
|
23724
|
+
}
|
|
23373
23725
|
}
|
|
23374
23726
|
}
|
|
23375
23727
|
}
|
|
@@ -23414,7 +23766,8 @@ async function authenticate(request, env) {
|
|
|
23414
23766
|
display_name: user.display_name ?? null,
|
|
23415
23767
|
avatar_url: user.avatar_url ?? null
|
|
23416
23768
|
},
|
|
23417
|
-
authType: "api_key"
|
|
23769
|
+
authType: "api_key",
|
|
23770
|
+
credentialId: apiKey.id
|
|
23418
23771
|
};
|
|
23419
23772
|
}
|
|
23420
23773
|
}
|
|
@@ -23426,7 +23779,10 @@ async function requireAuth(request, env) {
|
|
|
23426
23779
|
if (!authContext) {
|
|
23427
23780
|
return new Response(JSON.stringify({ error: "Unauthorized" }), {
|
|
23428
23781
|
status: 401,
|
|
23429
|
-
headers: {
|
|
23782
|
+
headers: {
|
|
23783
|
+
"Content-Type": "application/json",
|
|
23784
|
+
...runtimeCorsHeaders(request, env)
|
|
23785
|
+
}
|
|
23430
23786
|
});
|
|
23431
23787
|
}
|
|
23432
23788
|
return authContext;
|
|
@@ -23441,7 +23797,10 @@ async function requireAdmin(request, env) {
|
|
|
23441
23797
|
JSON.stringify({ error: "Forbidden: Admin access required" }),
|
|
23442
23798
|
{
|
|
23443
23799
|
status: 403,
|
|
23444
|
-
headers: {
|
|
23800
|
+
headers: {
|
|
23801
|
+
"Content-Type": "application/json",
|
|
23802
|
+
...runtimeCorsHeaders(request, env)
|
|
23803
|
+
}
|
|
23445
23804
|
}
|
|
23446
23805
|
);
|
|
23447
23806
|
}
|
|
@@ -24426,8 +24785,37 @@ var migration35 = {
|
|
|
24426
24785
|
}
|
|
24427
24786
|
};
|
|
24428
24787
|
|
|
24788
|
+
// src/durable-objects/migrations/036_add_tool_delivery_tokens.ts
|
|
24789
|
+
var migration36 = {
|
|
24790
|
+
version: 36,
|
|
24791
|
+
name: "add_tool_delivery_tokens",
|
|
24792
|
+
async up(sql) {
|
|
24793
|
+
sql.exec(`
|
|
24794
|
+
CREATE TABLE IF NOT EXISTS pending_client_calls (
|
|
24795
|
+
tool_call_id TEXT PRIMARY KEY,
|
|
24796
|
+
tool TEXT NOT NULL,
|
|
24797
|
+
args TEXT,
|
|
24798
|
+
request_permission TEXT,
|
|
24799
|
+
risk INTEGER,
|
|
24800
|
+
side TEXT NOT NULL DEFAULT 'a',
|
|
24801
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
24802
|
+
created_at INTEGER NOT NULL,
|
|
24803
|
+
dispatched_client_id TEXT,
|
|
24804
|
+
dispatched_generation INTEGER NOT NULL DEFAULT 0
|
|
24805
|
+
)
|
|
24806
|
+
`);
|
|
24807
|
+
const columns = sql.exec(
|
|
24808
|
+
`PRAGMA table_info(pending_client_calls)`
|
|
24809
|
+
).toArray();
|
|
24810
|
+
if (!columns.some((column) => column.name === "delivery_token_hash")) {
|
|
24811
|
+
sql.exec(`ALTER TABLE pending_client_calls ADD COLUMN delivery_token_hash TEXT`);
|
|
24812
|
+
}
|
|
24813
|
+
sql.exec(`UPDATE _metadata SET value = '36' WHERE key = 'schema_version'`);
|
|
24814
|
+
}
|
|
24815
|
+
};
|
|
24816
|
+
|
|
24429
24817
|
// src/durable-objects/migrations/index.ts
|
|
24430
|
-
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];
|
|
24818
|
+
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];
|
|
24431
24819
|
var LATEST_SCHEMA_VERSION = migrations.length;
|
|
24432
24820
|
|
|
24433
24821
|
// src/durable-objects/subagent-reconcile.ts
|
|
@@ -25263,7 +25651,7 @@ function eventSocketCanReceive(attachment, ownerUserId) {
|
|
|
25263
25651
|
}
|
|
25264
25652
|
|
|
25265
25653
|
// src/durable-objects/agentbuilder-migrations/0001_initial.ts
|
|
25266
|
-
var
|
|
25654
|
+
var migration37 = {
|
|
25267
25655
|
version: 1,
|
|
25268
25656
|
async up(sql) {
|
|
25269
25657
|
sql.exec(`
|
|
@@ -25362,7 +25750,7 @@ var migration36 = {
|
|
|
25362
25750
|
};
|
|
25363
25751
|
|
|
25364
25752
|
// src/durable-objects/agentbuilder-migrations/0002_add_tenvs_properties.ts
|
|
25365
|
-
var
|
|
25753
|
+
var migration38 = {
|
|
25366
25754
|
version: 2,
|
|
25367
25755
|
async up(sql) {
|
|
25368
25756
|
sql.exec(`ALTER TABLE threads ADD COLUMN tenvs TEXT`);
|
|
@@ -25374,7 +25762,7 @@ var migration37 = {
|
|
|
25374
25762
|
};
|
|
25375
25763
|
|
|
25376
25764
|
// src/durable-objects/agentbuilder-migrations/0003_add_parent_and_terminated.ts
|
|
25377
|
-
var
|
|
25765
|
+
var migration39 = {
|
|
25378
25766
|
version: 3,
|
|
25379
25767
|
async up(sql) {
|
|
25380
25768
|
sql.exec(`ALTER TABLE threads ADD COLUMN parent TEXT REFERENCES threads(id)`);
|
|
@@ -25387,7 +25775,7 @@ var migration38 = {
|
|
|
25387
25775
|
};
|
|
25388
25776
|
|
|
25389
25777
|
// src/durable-objects/agentbuilder-migrations/0004_add_env_tables.ts
|
|
25390
|
-
var
|
|
25778
|
+
var migration40 = {
|
|
25391
25779
|
version: 4,
|
|
25392
25780
|
async up(sql) {
|
|
25393
25781
|
sql.exec(`ALTER TABLE threads ADD COLUMN env TEXT`);
|
|
@@ -25411,7 +25799,7 @@ var migration39 = {
|
|
|
25411
25799
|
};
|
|
25412
25800
|
|
|
25413
25801
|
// src/durable-objects/agentbuilder-migrations/0005_add_pending_subagent_env_requests.ts
|
|
25414
|
-
var
|
|
25802
|
+
var migration41 = {
|
|
25415
25803
|
version: 5,
|
|
25416
25804
|
async up(sql) {
|
|
25417
25805
|
sql.exec(`
|
|
@@ -25437,7 +25825,7 @@ var migration40 = {
|
|
|
25437
25825
|
};
|
|
25438
25826
|
|
|
25439
25827
|
// src/durable-objects/agentbuilder-migrations/0006_add_env_value_types.ts
|
|
25440
|
-
var
|
|
25828
|
+
var migration42 = {
|
|
25441
25829
|
version: 6,
|
|
25442
25830
|
async up(sql) {
|
|
25443
25831
|
sql.exec(
|
|
@@ -25452,7 +25840,7 @@ var migration41 = {
|
|
|
25452
25840
|
};
|
|
25453
25841
|
|
|
25454
25842
|
// src/durable-objects/agentbuilder-migrations/0007_platform_identity_replica.ts
|
|
25455
|
-
var
|
|
25843
|
+
var migration43 = {
|
|
25456
25844
|
version: 7,
|
|
25457
25845
|
async up(sql) {
|
|
25458
25846
|
sql.exec(`ALTER TABLE users ADD COLUMN platform_user_id TEXT`);
|
|
@@ -25481,7 +25869,7 @@ var migration42 = {
|
|
|
25481
25869
|
};
|
|
25482
25870
|
|
|
25483
25871
|
// src/durable-objects/agentbuilder-migrations/0008_add_skills.ts
|
|
25484
|
-
var
|
|
25872
|
+
var migration44 = {
|
|
25485
25873
|
version: 8,
|
|
25486
25874
|
async up(sql) {
|
|
25487
25875
|
sql.exec(`
|
|
@@ -25512,7 +25900,7 @@ var migration43 = {
|
|
|
25512
25900
|
};
|
|
25513
25901
|
|
|
25514
25902
|
// src/durable-objects/agentbuilder-migrations/0009_add_user_permissions.ts
|
|
25515
|
-
var
|
|
25903
|
+
var migration45 = {
|
|
25516
25904
|
version: 9,
|
|
25517
25905
|
async up(sql) {
|
|
25518
25906
|
sql.exec(`ALTER TABLE users ADD COLUMN permissions TEXT`);
|
|
@@ -25523,7 +25911,7 @@ var migration44 = {
|
|
|
25523
25911
|
};
|
|
25524
25912
|
|
|
25525
25913
|
// src/durable-objects/agentbuilder-migrations/0010_add_device_logins.ts
|
|
25526
|
-
var
|
|
25914
|
+
var migration46 = {
|
|
25527
25915
|
version: 10,
|
|
25528
25916
|
async up(sql) {
|
|
25529
25917
|
sql.exec(`
|
|
@@ -25543,7 +25931,7 @@ var migration45 = {
|
|
|
25543
25931
|
};
|
|
25544
25932
|
|
|
25545
25933
|
// src/durable-objects/agentbuilder-migrations/0011_add_user_values.ts
|
|
25546
|
-
var
|
|
25934
|
+
var migration47 = {
|
|
25547
25935
|
version: 11,
|
|
25548
25936
|
async up(sql) {
|
|
25549
25937
|
sql.exec(`
|
|
@@ -25566,7 +25954,7 @@ var migration46 = {
|
|
|
25566
25954
|
};
|
|
25567
25955
|
|
|
25568
25956
|
// src/durable-objects/agentbuilder-migrations/0012_allow_api_instance_role.ts
|
|
25569
|
-
var
|
|
25957
|
+
var migration48 = {
|
|
25570
25958
|
version: 12,
|
|
25571
25959
|
async up(sql) {
|
|
25572
25960
|
sql.exec(`CREATE TABLE oauth_accounts_0012_backup AS SELECT * FROM oauth_accounts`);
|
|
@@ -25676,7 +26064,7 @@ var migration47 = {
|
|
|
25676
26064
|
};
|
|
25677
26065
|
|
|
25678
26066
|
// src/durable-objects/agentbuilder-migrations/0013_add_thread_activity.ts
|
|
25679
|
-
var
|
|
26067
|
+
var migration49 = {
|
|
25680
26068
|
version: 13,
|
|
25681
26069
|
async up(sql) {
|
|
25682
26070
|
sql.exec(`ALTER TABLE threads ADD COLUMN last_activity_at INTEGER`);
|
|
@@ -25692,7 +26080,7 @@ var migration48 = {
|
|
|
25692
26080
|
};
|
|
25693
26081
|
|
|
25694
26082
|
// src/durable-objects/agentbuilder-migrations/0014_thread_activity_scale.ts
|
|
25695
|
-
var
|
|
26083
|
+
var migration50 = {
|
|
25696
26084
|
version: 14,
|
|
25697
26085
|
async up(sql) {
|
|
25698
26086
|
sql.exec(`ALTER TABLE threads ADD COLUMN last_message_preview TEXT`);
|
|
@@ -25710,7 +26098,7 @@ var migration49 = {
|
|
|
25710
26098
|
};
|
|
25711
26099
|
|
|
25712
26100
|
// src/durable-objects/agentbuilder-migrations/0015_clear_envelope_previews.ts
|
|
25713
|
-
var
|
|
26101
|
+
var migration51 = {
|
|
25714
26102
|
version: 15,
|
|
25715
26103
|
async up(sql) {
|
|
25716
26104
|
sql.exec(
|
|
@@ -25724,7 +26112,7 @@ var migration50 = {
|
|
|
25724
26112
|
};
|
|
25725
26113
|
|
|
25726
26114
|
// src/durable-objects/agentbuilder-migrations/0016_add_origin_agent.ts
|
|
25727
|
-
var
|
|
26115
|
+
var migration52 = {
|
|
25728
26116
|
version: 16,
|
|
25729
26117
|
async up(sql) {
|
|
25730
26118
|
sql.exec(`ALTER TABLE threads ADD COLUMN origin_agent_name TEXT`);
|
|
@@ -25739,7 +26127,7 @@ var migration51 = {
|
|
|
25739
26127
|
};
|
|
25740
26128
|
|
|
25741
26129
|
// src/durable-objects/agentbuilder-migrations/0017_add_thread_message_recency.ts
|
|
25742
|
-
var
|
|
26130
|
+
var migration53 = {
|
|
25743
26131
|
version: 17,
|
|
25744
26132
|
async up(sql) {
|
|
25745
26133
|
sql.exec(`ALTER TABLE threads ADD COLUMN last_message_at INTEGER`);
|
|
@@ -25750,10 +26138,132 @@ var migration52 = {
|
|
|
25750
26138
|
}
|
|
25751
26139
|
};
|
|
25752
26140
|
|
|
26141
|
+
// src/durable-objects/agentbuilder-migrations/0018_auth_security.ts
|
|
26142
|
+
var migration54 = {
|
|
26143
|
+
version: 18,
|
|
26144
|
+
async up(sql) {
|
|
26145
|
+
sql.exec(`
|
|
26146
|
+
CREATE TABLE IF NOT EXISTS super_admin_sessions (
|
|
26147
|
+
token_hash TEXT PRIMARY KEY,
|
|
26148
|
+
created_at INTEGER NOT NULL,
|
|
26149
|
+
expires_at INTEGER NOT NULL
|
|
26150
|
+
)
|
|
26151
|
+
`);
|
|
26152
|
+
sql.exec(`
|
|
26153
|
+
CREATE INDEX IF NOT EXISTS idx_super_admin_sessions_expires
|
|
26154
|
+
ON super_admin_sessions(expires_at)
|
|
26155
|
+
`);
|
|
26156
|
+
sql.exec(`
|
|
26157
|
+
CREATE TABLE IF NOT EXISTS auth_rate_limits (
|
|
26158
|
+
bucket_key TEXT PRIMARY KEY,
|
|
26159
|
+
attempts INTEGER NOT NULL,
|
|
26160
|
+
reset_at INTEGER NOT NULL
|
|
26161
|
+
)
|
|
26162
|
+
`);
|
|
26163
|
+
sql.exec(`
|
|
26164
|
+
CREATE INDEX IF NOT EXISTS idx_auth_rate_limits_reset
|
|
26165
|
+
ON auth_rate_limits(reset_at)
|
|
26166
|
+
`);
|
|
26167
|
+
sql.exec(`
|
|
26168
|
+
INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '18')
|
|
26169
|
+
`);
|
|
26170
|
+
}
|
|
26171
|
+
};
|
|
26172
|
+
|
|
26173
|
+
// src/durable-objects/agentbuilder-migrations/0019_scope_skills.ts
|
|
26174
|
+
var migration55 = {
|
|
26175
|
+
version: 19,
|
|
26176
|
+
async up(sql) {
|
|
26177
|
+
sql.exec(`
|
|
26178
|
+
CREATE TABLE skills_0019 (
|
|
26179
|
+
owner_id TEXT NOT NULL,
|
|
26180
|
+
name TEXT NOT NULL,
|
|
26181
|
+
description TEXT NOT NULL,
|
|
26182
|
+
version TEXT,
|
|
26183
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
26184
|
+
shared INTEGER NOT NULL DEFAULT 0,
|
|
26185
|
+
created_at INTEGER DEFAULT (strftime('%s', 'now')),
|
|
26186
|
+
updated_at INTEGER DEFAULT (strftime('%s', 'now')),
|
|
26187
|
+
PRIMARY KEY (owner_id, name)
|
|
26188
|
+
)
|
|
26189
|
+
`);
|
|
26190
|
+
sql.exec(`
|
|
26191
|
+
INSERT INTO skills_0019 (
|
|
26192
|
+
owner_id, name, description, version, enabled, shared, created_at, updated_at
|
|
26193
|
+
)
|
|
26194
|
+
SELECT '__instance_shared__', name, description, version, enabled, 1, created_at, updated_at
|
|
26195
|
+
FROM skills
|
|
26196
|
+
`);
|
|
26197
|
+
sql.exec(`
|
|
26198
|
+
CREATE TABLE skill_files_0019 (
|
|
26199
|
+
owner_id TEXT NOT NULL,
|
|
26200
|
+
skill_name TEXT NOT NULL,
|
|
26201
|
+
path TEXT NOT NULL,
|
|
26202
|
+
content TEXT NOT NULL,
|
|
26203
|
+
size INTEGER NOT NULL,
|
|
26204
|
+
PRIMARY KEY (owner_id, skill_name, path),
|
|
26205
|
+
FOREIGN KEY (owner_id, skill_name) REFERENCES skills_0019(owner_id, name) ON DELETE CASCADE
|
|
26206
|
+
)
|
|
26207
|
+
`);
|
|
26208
|
+
sql.exec(`
|
|
26209
|
+
INSERT INTO skill_files_0019 (owner_id, skill_name, path, content, size)
|
|
26210
|
+
SELECT '__instance_shared__', skill_name, path, content, size FROM skill_files
|
|
26211
|
+
`);
|
|
26212
|
+
sql.exec(`DROP TABLE skill_files`);
|
|
26213
|
+
sql.exec(`DROP TABLE skills`);
|
|
26214
|
+
sql.exec(`ALTER TABLE skills_0019 RENAME TO skills`);
|
|
26215
|
+
sql.exec(`ALTER TABLE skill_files_0019 RENAME TO skill_files`);
|
|
26216
|
+
sql.exec(`CREATE INDEX idx_skills_owner_enabled ON skills(owner_id, enabled)`);
|
|
26217
|
+
sql.exec(`CREATE INDEX idx_skills_shared_enabled ON skills(shared, enabled)`);
|
|
26218
|
+
sql.exec(`CREATE INDEX idx_skill_files_skill ON skill_files(owner_id, skill_name)`);
|
|
26219
|
+
sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '19')`);
|
|
26220
|
+
}
|
|
26221
|
+
};
|
|
26222
|
+
|
|
26223
|
+
// src/durable-objects/agentbuilder-migrations/0020_websocket_tickets.ts
|
|
26224
|
+
var migration56 = {
|
|
26225
|
+
version: 20,
|
|
26226
|
+
async up(sql) {
|
|
26227
|
+
sql.exec(`
|
|
26228
|
+
CREATE TABLE websocket_tickets (
|
|
26229
|
+
ticket_hash TEXT PRIMARY KEY,
|
|
26230
|
+
pathname TEXT NOT NULL,
|
|
26231
|
+
auth_json TEXT NOT NULL,
|
|
26232
|
+
created_at INTEGER NOT NULL,
|
|
26233
|
+
expires_at INTEGER NOT NULL
|
|
26234
|
+
)
|
|
26235
|
+
`);
|
|
26236
|
+
sql.exec(`
|
|
26237
|
+
CREATE INDEX idx_websocket_tickets_expires
|
|
26238
|
+
ON websocket_tickets(expires_at)
|
|
26239
|
+
`);
|
|
26240
|
+
sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '20')`);
|
|
26241
|
+
}
|
|
26242
|
+
};
|
|
26243
|
+
|
|
26244
|
+
// src/durable-objects/agentbuilder-migrations/0021_platform_handoff_jtis.ts
|
|
26245
|
+
var migration57 = {
|
|
26246
|
+
version: 21,
|
|
26247
|
+
async up(sql) {
|
|
26248
|
+
sql.exec(`
|
|
26249
|
+
CREATE TABLE platform_handoff_jtis (
|
|
26250
|
+
jti TEXT PRIMARY KEY,
|
|
26251
|
+
consumed_at INTEGER NOT NULL,
|
|
26252
|
+
expires_at INTEGER NOT NULL
|
|
26253
|
+
)
|
|
26254
|
+
`);
|
|
26255
|
+
sql.exec(`
|
|
26256
|
+
CREATE INDEX idx_platform_handoff_jtis_expires
|
|
26257
|
+
ON platform_handoff_jtis(expires_at)
|
|
26258
|
+
`);
|
|
26259
|
+
sql.exec(`INSERT OR REPLACE INTO _metadata (key, value) VALUES ('schema_version', '21')`);
|
|
26260
|
+
}
|
|
26261
|
+
};
|
|
26262
|
+
|
|
25753
26263
|
// src/durable-objects/agentbuilder-migrations/index.ts
|
|
25754
|
-
var migrations2 = [
|
|
26264
|
+
var migrations2 = [migration37, migration38, migration39, migration40, migration41, migration42, migration43, migration44, migration45, migration46, migration47, migration48, migration49, migration50, migration51, migration52, migration53, migration54, migration55, migration56, migration57];
|
|
25755
26265
|
var LATEST_SCHEMA_VERSION2 = migrations2.reduce(
|
|
25756
|
-
(max,
|
|
26266
|
+
(max, migration58) => Math.max(max, migration58.version),
|
|
25757
26267
|
0
|
|
25758
26268
|
);
|
|
25759
26269
|
|
|
@@ -26258,6 +26768,7 @@ function validateSkillFiles(files) {
|
|
|
26258
26768
|
}
|
|
26259
26769
|
|
|
26260
26770
|
// src/durable-objects/DurableAgentBuilder.ts
|
|
26771
|
+
init_scope();
|
|
26261
26772
|
var ENV_TYPES_PROPERTY = "__agentbuilder_env_types";
|
|
26262
26773
|
function parseStoredPermissions(value) {
|
|
26263
26774
|
if (typeof value !== "string" || !value.trim()) return null;
|
|
@@ -26850,9 +27361,9 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
26850
27361
|
}
|
|
26851
27362
|
}
|
|
26852
27363
|
async runMigrations(fromVersion) {
|
|
26853
|
-
for (const
|
|
26854
|
-
if (
|
|
26855
|
-
await
|
|
27364
|
+
for (const migration58 of migrations2) {
|
|
27365
|
+
if (migration58.version > fromVersion) {
|
|
27366
|
+
await migration58.up(this.ctx.storage.sql);
|
|
26856
27367
|
}
|
|
26857
27368
|
}
|
|
26858
27369
|
}
|
|
@@ -28801,6 +29312,150 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
28801
29312
|
tokenHash
|
|
28802
29313
|
);
|
|
28803
29314
|
}
|
|
29315
|
+
/** Register a short-lived super-admin token so it can be revoked alone. */
|
|
29316
|
+
async createSuperAdminSession(params) {
|
|
29317
|
+
await this.ensureMigrated();
|
|
29318
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
29319
|
+
await this.ctx.storage.sql.exec(
|
|
29320
|
+
`DELETE FROM super_admin_sessions WHERE expires_at <= ?`,
|
|
29321
|
+
now
|
|
29322
|
+
);
|
|
29323
|
+
await this.ctx.storage.sql.exec(
|
|
29324
|
+
`INSERT INTO super_admin_sessions (token_hash, created_at, expires_at)
|
|
29325
|
+
VALUES (?, ?, ?)`,
|
|
29326
|
+
params.token_hash,
|
|
29327
|
+
now,
|
|
29328
|
+
params.expires_at
|
|
29329
|
+
);
|
|
29330
|
+
}
|
|
29331
|
+
/** A valid signature is insufficient: the session must still be registered. */
|
|
29332
|
+
async validateSuperAdminSession(tokenHash) {
|
|
29333
|
+
await this.ensureMigrated();
|
|
29334
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
29335
|
+
const cursor = await this.ctx.storage.sql.exec(
|
|
29336
|
+
`SELECT 1 AS found FROM super_admin_sessions
|
|
29337
|
+
WHERE token_hash = ? AND expires_at > ?`,
|
|
29338
|
+
tokenHash,
|
|
29339
|
+
now
|
|
29340
|
+
);
|
|
29341
|
+
return cursor.toArray().length > 0;
|
|
29342
|
+
}
|
|
29343
|
+
/** Revoke only the presented super-admin session; no key rotation required. */
|
|
29344
|
+
async deleteSuperAdminSession(tokenHash) {
|
|
29345
|
+
await this.ensureMigrated();
|
|
29346
|
+
await this.ctx.storage.sql.exec(
|
|
29347
|
+
`DELETE FROM super_admin_sessions WHERE token_hash = ?`,
|
|
29348
|
+
tokenHash
|
|
29349
|
+
);
|
|
29350
|
+
}
|
|
29351
|
+
/** Register a path-bound bearer ticket for one WebSocket handshake. */
|
|
29352
|
+
async createWebSocketTicket(params) {
|
|
29353
|
+
await this.ensureMigrated();
|
|
29354
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
29355
|
+
this.ctx.storage.sql.exec(
|
|
29356
|
+
`DELETE FROM websocket_tickets WHERE expires_at <= ?`,
|
|
29357
|
+
now
|
|
29358
|
+
);
|
|
29359
|
+
this.ctx.storage.sql.exec(
|
|
29360
|
+
`INSERT INTO websocket_tickets (ticket_hash, pathname, auth_json, created_at, expires_at)
|
|
29361
|
+
VALUES (?, ?, ?, ?, ?)`,
|
|
29362
|
+
params.ticket_hash,
|
|
29363
|
+
params.pathname,
|
|
29364
|
+
params.auth_json,
|
|
29365
|
+
now,
|
|
29366
|
+
params.expires_at
|
|
29367
|
+
);
|
|
29368
|
+
}
|
|
29369
|
+
/**
|
|
29370
|
+
* Atomically consume a WebSocket ticket. Deleting before validating its path
|
|
29371
|
+
* means even a mistaken or malicious first presentation burns the bearer.
|
|
29372
|
+
*/
|
|
29373
|
+
async consumeWebSocketTicket(params) {
|
|
29374
|
+
await this.ensureMigrated();
|
|
29375
|
+
const cursor = this.ctx.storage.sql.exec(
|
|
29376
|
+
`DELETE FROM websocket_tickets
|
|
29377
|
+
WHERE ticket_hash = ?
|
|
29378
|
+
RETURNING pathname, auth_json, expires_at`,
|
|
29379
|
+
params.ticket_hash
|
|
29380
|
+
);
|
|
29381
|
+
const ticket = cursor.toArray()[0];
|
|
29382
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
29383
|
+
if (!ticket || ticket.expires_at <= now || ticket.pathname !== params.pathname) {
|
|
29384
|
+
return null;
|
|
29385
|
+
}
|
|
29386
|
+
return ticket.auth_json;
|
|
29387
|
+
}
|
|
29388
|
+
/** Atomically accept an unexpired platform handoff identifier once. */
|
|
29389
|
+
async consumePlatformHandoffJti(params) {
|
|
29390
|
+
await this.ensureMigrated();
|
|
29391
|
+
const jti = params.jti.trim();
|
|
29392
|
+
const expiresAt = Math.floor(params.expires_at);
|
|
29393
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
29394
|
+
if (!jti || jti.length > 200 || !Number.isSafeInteger(expiresAt) || expiresAt <= now) {
|
|
29395
|
+
return false;
|
|
29396
|
+
}
|
|
29397
|
+
this.ctx.storage.sql.exec(
|
|
29398
|
+
`DELETE FROM platform_handoff_jtis WHERE expires_at <= ?`,
|
|
29399
|
+
now
|
|
29400
|
+
);
|
|
29401
|
+
const cursor = this.ctx.storage.sql.exec(
|
|
29402
|
+
`INSERT OR IGNORE INTO platform_handoff_jtis (jti, consumed_at, expires_at)
|
|
29403
|
+
VALUES (?, ?, ?)`,
|
|
29404
|
+
jti,
|
|
29405
|
+
now,
|
|
29406
|
+
expiresAt
|
|
29407
|
+
);
|
|
29408
|
+
return cursor.rowsWritten > 0;
|
|
29409
|
+
}
|
|
29410
|
+
/**
|
|
29411
|
+
* Atomically consume one attempt from a fixed-window authentication bucket.
|
|
29412
|
+
* The singleton Durable Object serializes calls, preventing parallel bypass.
|
|
29413
|
+
*/
|
|
29414
|
+
async consumeAuthRateLimit(params) {
|
|
29415
|
+
await this.ensureMigrated();
|
|
29416
|
+
const key = params.key.trim();
|
|
29417
|
+
const limit = Math.floor(params.limit);
|
|
29418
|
+
const windowSeconds = Math.floor(params.window_seconds);
|
|
29419
|
+
if (!key || key.length > 160 || limit < 1 || limit > 1e4 || windowSeconds < 1 || windowSeconds > 86400) {
|
|
29420
|
+
throw new Error("Invalid authentication rate-limit bucket");
|
|
29421
|
+
}
|
|
29422
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
29423
|
+
await this.ctx.storage.sql.exec(
|
|
29424
|
+
`DELETE FROM auth_rate_limits WHERE reset_at <= ?`,
|
|
29425
|
+
now
|
|
29426
|
+
);
|
|
29427
|
+
const cursor = await this.ctx.storage.sql.exec(
|
|
29428
|
+
`SELECT attempts, reset_at FROM auth_rate_limits WHERE bucket_key = ?`,
|
|
29429
|
+
key
|
|
29430
|
+
);
|
|
29431
|
+
const current = cursor.toArray()[0];
|
|
29432
|
+
if (!current || current.reset_at <= now) {
|
|
29433
|
+
const resetAt = now + windowSeconds;
|
|
29434
|
+
await this.ctx.storage.sql.exec(
|
|
29435
|
+
`INSERT INTO auth_rate_limits (bucket_key, attempts, reset_at)
|
|
29436
|
+
VALUES (?, 1, ?)
|
|
29437
|
+
ON CONFLICT(bucket_key) DO UPDATE SET attempts = 1, reset_at = excluded.reset_at`,
|
|
29438
|
+
key,
|
|
29439
|
+
resetAt
|
|
29440
|
+
);
|
|
29441
|
+
return { allowed: true, retry_after: windowSeconds, remaining: limit - 1 };
|
|
29442
|
+
}
|
|
29443
|
+
const retryAfter = Math.max(1, current.reset_at - now);
|
|
29444
|
+
if (current.attempts >= limit) {
|
|
29445
|
+
return { allowed: false, retry_after: retryAfter, remaining: 0 };
|
|
29446
|
+
}
|
|
29447
|
+
const attempts = current.attempts + 1;
|
|
29448
|
+
await this.ctx.storage.sql.exec(
|
|
29449
|
+
`UPDATE auth_rate_limits SET attempts = ? WHERE bucket_key = ?`,
|
|
29450
|
+
attempts,
|
|
29451
|
+
key
|
|
29452
|
+
);
|
|
29453
|
+
return {
|
|
29454
|
+
allowed: true,
|
|
29455
|
+
retry_after: retryAfter,
|
|
29456
|
+
remaining: Math.max(0, limit - attempts)
|
|
29457
|
+
};
|
|
29458
|
+
}
|
|
28804
29459
|
// ============================================================
|
|
28805
29460
|
// API Key Methods
|
|
28806
29461
|
// ============================================================
|
|
@@ -29118,40 +29773,61 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
29118
29773
|
}
|
|
29119
29774
|
}
|
|
29120
29775
|
// ─────────────────────────────────────────────────────────────────────────
|
|
29121
|
-
// Skills —
|
|
29122
|
-
// Installed once, visible to every thread; file contents live here so
|
|
29123
|
-
// nothing about a skill is stored on a client machine.
|
|
29776
|
+
// Skills — owner-scoped Agent Skills plus explicitly shared admin skills.
|
|
29124
29777
|
// ─────────────────────────────────────────────────────────────────────────
|
|
29125
29778
|
/** Installed skills, metadata only (no file contents). */
|
|
29126
|
-
async listSkills(options = {}) {
|
|
29779
|
+
async listSkills(scope, options = {}) {
|
|
29127
29780
|
await this.ensureMigrated();
|
|
29128
|
-
const where = options.includeDisabled ? "" : "WHERE enabled = 1";
|
|
29129
29781
|
const cursor = await this.ctx.storage.sql.exec(
|
|
29130
29782
|
`SELECT s.name, s.description, s.version, s.enabled, s.updated_at,
|
|
29131
|
-
|
|
29132
|
-
|
|
29783
|
+
s.owner_id,
|
|
29784
|
+
(SELECT json_group_array(f.path) FROM skill_files f
|
|
29785
|
+
WHERE f.owner_id = s.owner_id AND f.skill_name = s.name) AS files
|
|
29786
|
+
FROM skills s
|
|
29787
|
+
WHERE (s.owner_id = ? OR (? = 1 AND s.shared = 1))
|
|
29788
|
+
AND (? = 1 OR s.enabled = 1)
|
|
29789
|
+
ORDER BY s.name, CASE WHEN s.owner_id = ? THEN 0 ELSE 1 END`,
|
|
29790
|
+
scope.ownerId,
|
|
29791
|
+
scope.includeShared ? 1 : 0,
|
|
29792
|
+
options.includeDisabled ? 1 : 0,
|
|
29793
|
+
scope.ownerId
|
|
29133
29794
|
);
|
|
29134
|
-
|
|
29135
|
-
|
|
29136
|
-
|
|
29137
|
-
|
|
29138
|
-
|
|
29139
|
-
|
|
29140
|
-
|
|
29141
|
-
|
|
29795
|
+
const skills = /* @__PURE__ */ new Map();
|
|
29796
|
+
for (const row of cursor.toArray()) {
|
|
29797
|
+
if (skills.has(row.name)) continue;
|
|
29798
|
+
const parsedFiles = row.files ? JSON.parse(row.files) : [];
|
|
29799
|
+
const files = Array.isArray(parsedFiles) ? parsedFiles.filter((path21) => typeof path21 === "string").sort() : [];
|
|
29800
|
+
skills.set(row.name, {
|
|
29801
|
+
name: row.name,
|
|
29802
|
+
description: row.description,
|
|
29803
|
+
version: row.version ?? void 0,
|
|
29804
|
+
enabled: row.enabled === 1,
|
|
29805
|
+
files,
|
|
29806
|
+
updatedAt: row.updated_at
|
|
29807
|
+
});
|
|
29808
|
+
}
|
|
29809
|
+
return [...skills.values()];
|
|
29142
29810
|
}
|
|
29143
29811
|
/** One skill's metadata (null when not installed). */
|
|
29144
|
-
async getSkill(name) {
|
|
29145
|
-
const all = await this.listSkills({ includeDisabled: true });
|
|
29812
|
+
async getSkill(scope, name) {
|
|
29813
|
+
const all = await this.listSkills(scope, { includeDisabled: true });
|
|
29146
29814
|
return all.find((s) => s.name === name) ?? null;
|
|
29147
29815
|
}
|
|
29148
29816
|
/** One bundled file's text (null when the skill or file is missing). */
|
|
29149
|
-
async getSkillFile(name, path21) {
|
|
29817
|
+
async getSkillFile(scope, name, path21) {
|
|
29150
29818
|
await this.ensureMigrated();
|
|
29151
29819
|
const cursor = await this.ctx.storage.sql.exec(
|
|
29152
|
-
`SELECT content FROM skill_files
|
|
29820
|
+
`SELECT f.content FROM skill_files f
|
|
29821
|
+
JOIN skills s ON s.owner_id = f.owner_id AND s.name = f.skill_name
|
|
29822
|
+
WHERE f.skill_name = ? AND f.path = ?
|
|
29823
|
+
AND (f.owner_id = ? OR (? = 1 AND s.shared = 1))
|
|
29824
|
+
ORDER BY CASE WHEN f.owner_id = ? THEN 0 ELSE 1 END
|
|
29825
|
+
LIMIT 1`,
|
|
29153
29826
|
name,
|
|
29154
|
-
path21.replace(/^\.\//, "")
|
|
29827
|
+
path21.replace(/^\.\//, ""),
|
|
29828
|
+
scope.ownerId,
|
|
29829
|
+
scope.includeShared ? 1 : 0,
|
|
29830
|
+
scope.ownerId
|
|
29155
29831
|
);
|
|
29156
29832
|
const rows = cursor.toArray();
|
|
29157
29833
|
return rows.length ? rows[0].content : null;
|
|
@@ -29162,44 +29838,56 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
29162
29838
|
* that frontmatter. Throws SkillValidationError with an actionable message
|
|
29163
29839
|
* on any standard violation.
|
|
29164
29840
|
*/
|
|
29165
|
-
async upsertSkill(files) {
|
|
29841
|
+
async upsertSkill(scope, files) {
|
|
29166
29842
|
await this.ensureMigrated();
|
|
29167
29843
|
const frontmatter = validateSkillFiles(files);
|
|
29168
29844
|
const name = frontmatter.name;
|
|
29169
|
-
const
|
|
29170
|
-
await this.
|
|
29845
|
+
const ownScope = { ownerId: scope.ownerId };
|
|
29846
|
+
const existing = await this.getSkill(ownScope, name);
|
|
29171
29847
|
await this.ctx.storage.sql.exec(
|
|
29172
|
-
`
|
|
29173
|
-
|
|
29174
|
-
|
|
29848
|
+
`DELETE FROM skill_files WHERE owner_id = ? AND skill_name = ?`,
|
|
29849
|
+
scope.ownerId,
|
|
29850
|
+
name
|
|
29851
|
+
);
|
|
29852
|
+
await this.ctx.storage.sql.exec(
|
|
29853
|
+
`INSERT INTO skills (owner_id, name, description, version, enabled, shared, updated_at)
|
|
29854
|
+
VALUES (?, ?, ?, ?, ?, ?, strftime('%s', 'now'))
|
|
29855
|
+
ON CONFLICT(owner_id, name) DO UPDATE SET
|
|
29175
29856
|
description = excluded.description,
|
|
29176
29857
|
version = excluded.version,
|
|
29177
29858
|
updated_at = excluded.updated_at`,
|
|
29859
|
+
scope.ownerId,
|
|
29178
29860
|
name,
|
|
29179
29861
|
frontmatter.description,
|
|
29180
29862
|
frontmatter.version ?? null,
|
|
29181
|
-
existing ? existing.enabled ? 1 : 0 : 1
|
|
29863
|
+
existing ? existing.enabled ? 1 : 0 : 1,
|
|
29864
|
+
scope.ownerId === SHARED_SKILL_OWNER ? 1 : 0
|
|
29182
29865
|
);
|
|
29183
29866
|
for (const f of files) {
|
|
29184
29867
|
const p = f.path.replace(/^\.\//, "");
|
|
29185
29868
|
await this.ctx.storage.sql.exec(
|
|
29186
|
-
`INSERT INTO skill_files (skill_name, path, content, size) VALUES (?, ?, ?, ?)`,
|
|
29869
|
+
`INSERT INTO skill_files (owner_id, skill_name, path, content, size) VALUES (?, ?, ?, ?, ?)`,
|
|
29870
|
+
scope.ownerId,
|
|
29187
29871
|
name,
|
|
29188
29872
|
p,
|
|
29189
29873
|
f.content,
|
|
29190
29874
|
f.content.length
|
|
29191
29875
|
);
|
|
29192
29876
|
}
|
|
29193
|
-
|
|
29877
|
+
const installed = await this.getSkill(ownScope, name);
|
|
29878
|
+
if (!installed) throw new Error(`Installed skill was not found: ${name}`);
|
|
29879
|
+
return installed;
|
|
29194
29880
|
}
|
|
29195
29881
|
/** Enable/disable an installed skill. Returns false when not installed. */
|
|
29196
|
-
async setSkillEnabled(name, enabled) {
|
|
29882
|
+
async setSkillEnabled(scope, name, enabled) {
|
|
29197
29883
|
await this.ensureMigrated();
|
|
29198
|
-
const existing = await this.getSkill(name);
|
|
29884
|
+
const existing = await this.getSkill({ ownerId: scope.ownerId }, name);
|
|
29199
29885
|
if (!existing) return false;
|
|
29200
29886
|
await this.ctx.storage.sql.exec(
|
|
29201
|
-
`UPDATE skills SET enabled = ?, updated_at = strftime('%s', 'now')
|
|
29887
|
+
`UPDATE skills SET enabled = ?, updated_at = strftime('%s', 'now')
|
|
29888
|
+
WHERE owner_id = ? AND name = ?`,
|
|
29202
29889
|
enabled ? 1 : 0,
|
|
29890
|
+
scope.ownerId,
|
|
29203
29891
|
name
|
|
29204
29892
|
);
|
|
29205
29893
|
return true;
|
|
@@ -29274,12 +29962,20 @@ ${result ?? error ?? "No result content."}${attachmentSummary}`;
|
|
|
29274
29962
|
return true;
|
|
29275
29963
|
}
|
|
29276
29964
|
/** Uninstall a skill and its files. Returns false when not installed. */
|
|
29277
|
-
async deleteSkill(name) {
|
|
29965
|
+
async deleteSkill(scope, name) {
|
|
29278
29966
|
await this.ensureMigrated();
|
|
29279
|
-
const existing = await this.getSkill(name);
|
|
29967
|
+
const existing = await this.getSkill({ ownerId: scope.ownerId }, name);
|
|
29280
29968
|
if (!existing) return false;
|
|
29281
|
-
await this.ctx.storage.sql.exec(
|
|
29282
|
-
|
|
29969
|
+
await this.ctx.storage.sql.exec(
|
|
29970
|
+
`DELETE FROM skill_files WHERE owner_id = ? AND skill_name = ?`,
|
|
29971
|
+
scope.ownerId,
|
|
29972
|
+
name
|
|
29973
|
+
);
|
|
29974
|
+
await this.ctx.storage.sql.exec(
|
|
29975
|
+
`DELETE FROM skills WHERE owner_id = ? AND name = ?`,
|
|
29976
|
+
scope.ownerId,
|
|
29977
|
+
name
|
|
29978
|
+
);
|
|
29283
29979
|
return true;
|
|
29284
29980
|
}
|
|
29285
29981
|
};
|
|
@@ -29371,8 +30067,38 @@ function selectBridgeSocket(candidates, ownerClientId) {
|
|
|
29371
30067
|
|
|
29372
30068
|
// src/durable-objects/DurableThread.ts
|
|
29373
30069
|
init_code_execution();
|
|
30070
|
+
init_remoteAttachment();
|
|
29374
30071
|
var DEFAULT_CLIENT_TOOL_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
29375
30072
|
var BRIDGE_LIVENESS_TIMEOUT_MS = 20 * 1e3;
|
|
30073
|
+
var INTERNAL_TOOL_DELIVERY = Symbol("internal-tool-delivery");
|
|
30074
|
+
function generateToolDeliveryToken() {
|
|
30075
|
+
const bytes = crypto.getRandomValues(new Uint8Array(32));
|
|
30076
|
+
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
30077
|
+
}
|
|
30078
|
+
async function hashToolDeliveryToken(token) {
|
|
30079
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(token));
|
|
30080
|
+
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
30081
|
+
}
|
|
30082
|
+
async function createToolDeliveryCapability() {
|
|
30083
|
+
const token = generateToolDeliveryToken();
|
|
30084
|
+
return { token, tokenHash: await hashToolDeliveryToken(token) };
|
|
30085
|
+
}
|
|
30086
|
+
async function matchesToolDeliveryToken(expectedHash, token) {
|
|
30087
|
+
if (!expectedHash || !token || token.length > 256) return false;
|
|
30088
|
+
const actualHash = await hashToolDeliveryToken(token);
|
|
30089
|
+
if (expectedHash.length !== actualHash.length) return false;
|
|
30090
|
+
let difference = 0;
|
|
30091
|
+
for (let index = 0; index < actualHash.length; index++) {
|
|
30092
|
+
difference |= expectedHash.charCodeAt(index) ^ actualHash.charCodeAt(index);
|
|
30093
|
+
}
|
|
30094
|
+
return difference === 0;
|
|
30095
|
+
}
|
|
30096
|
+
function matchesToolDeliveryOwner(pending, proof) {
|
|
30097
|
+
return !pending.dispatched_client_id || pending.dispatched_client_id === proof.clientId && Number(pending.dispatched_generation ?? 0) === (proof.generation ?? -1);
|
|
30098
|
+
}
|
|
30099
|
+
async function authorizesToolDelivery(pending, proof) {
|
|
30100
|
+
return matchesToolDeliveryOwner(pending, proof) && await matchesToolDeliveryToken(pending.delivery_token_hash, proof.token);
|
|
30101
|
+
}
|
|
29376
30102
|
var RUNTIME_STORAGE_PREFIX = "__agentbuilder_runtime_storage__:";
|
|
29377
30103
|
function normalizeEndpointThreadPath(path21) {
|
|
29378
30104
|
if (!path21 || /^[A-Za-z][A-Za-z\d+\-.]*:/.test(path21)) {
|
|
@@ -29523,6 +30249,30 @@ function createEndpointThreadStateBridge(state) {
|
|
|
29523
30249
|
getFileThumbnail: (path21) => state.getFileThumbnail(normalizeEndpointThreadPath(path21))
|
|
29524
30250
|
};
|
|
29525
30251
|
}
|
|
30252
|
+
function routeBridgeClientFrame(socket, message, handlers) {
|
|
30253
|
+
const attachment = socket.deserializeAttachment();
|
|
30254
|
+
const attachedAsBridge = attachment !== null && typeof attachment === "object" && "type" in attachment && attachment.type === "bridge";
|
|
30255
|
+
if (!attachedAsBridge || !handlers.isTracked(socket)) return false;
|
|
30256
|
+
handlers.markSeen(socket);
|
|
30257
|
+
if (typeof message !== "string") return false;
|
|
30258
|
+
let parsed;
|
|
30259
|
+
try {
|
|
30260
|
+
const value = JSON.parse(message);
|
|
30261
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
30262
|
+
parsed = value;
|
|
30263
|
+
} catch {
|
|
30264
|
+
return false;
|
|
30265
|
+
}
|
|
30266
|
+
if (parsed.type === "tool_response") {
|
|
30267
|
+
handlers.resolveToolResponse(socket, parsed);
|
|
30268
|
+
return true;
|
|
30269
|
+
}
|
|
30270
|
+
if (typeof parsed.type === "string" && parsed.type.startsWith("stream_") && parsed.type !== "stream_ping" && parsed.type !== "stream_pong") {
|
|
30271
|
+
handlers.handleStreamFrame(socket, parsed);
|
|
30272
|
+
return true;
|
|
30273
|
+
}
|
|
30274
|
+
return false;
|
|
30275
|
+
}
|
|
29526
30276
|
var DurableThread = class _DurableThread extends DurableObject {
|
|
29527
30277
|
migratedToVersion = null;
|
|
29528
30278
|
logSockets = /* @__PURE__ */ new Set();
|
|
@@ -30917,9 +31667,9 @@ var DurableThread = class _DurableThread extends DurableObject {
|
|
|
30917
31667
|
* Each migration is run in order, starting from the current version + 1.
|
|
30918
31668
|
*/
|
|
30919
31669
|
async runMigrations(fromVersion) {
|
|
30920
|
-
for (const
|
|
30921
|
-
if (
|
|
30922
|
-
await
|
|
31670
|
+
for (const migration58 of migrations) {
|
|
31671
|
+
if (migration58.version > fromVersion) {
|
|
31672
|
+
await migration58.up(this.ctx.storage.sql);
|
|
30923
31673
|
}
|
|
30924
31674
|
}
|
|
30925
31675
|
}
|
|
@@ -31603,14 +32353,17 @@ var DurableThread = class _DurableThread extends DurableObject {
|
|
|
31603
32353
|
return null;
|
|
31604
32354
|
}
|
|
31605
32355
|
const location = sourceFile.location;
|
|
31606
|
-
if (!location
|
|
32356
|
+
if (!location) {
|
|
32357
|
+
return null;
|
|
32358
|
+
}
|
|
32359
|
+
const response = await fetchRemoteAttachment(
|
|
32360
|
+
location,
|
|
32361
|
+
this.env.AGENT_BUILDER_ATTACHMENT_FETCH_HOSTS
|
|
32362
|
+
);
|
|
32363
|
+
if (!response) {
|
|
31607
32364
|
return null;
|
|
31608
32365
|
}
|
|
31609
32366
|
try {
|
|
31610
|
-
const response = await fetch(location);
|
|
31611
|
-
if (!response.ok) {
|
|
31612
|
-
return null;
|
|
31613
|
-
}
|
|
31614
32367
|
const buffer = await response.arrayBuffer();
|
|
31615
32368
|
return this.arrayBufferToBase64(buffer);
|
|
31616
32369
|
} catch {
|
|
@@ -33307,7 +34060,9 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33307
34060
|
void 0,
|
|
33308
34061
|
"The client executing this operation was replaced before it finished. Retry if the work is still needed.",
|
|
33309
34062
|
previous.client_id,
|
|
33310
|
-
previous.generation
|
|
34063
|
+
previous.generation,
|
|
34064
|
+
void 0,
|
|
34065
|
+
INTERNAL_TOOL_DELIVERY
|
|
33311
34066
|
);
|
|
33312
34067
|
if (delivered) sides.add(delivered.side);
|
|
33313
34068
|
}
|
|
@@ -33337,18 +34092,21 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33337
34092
|
data: { noClient: true }
|
|
33338
34093
|
};
|
|
33339
34094
|
}
|
|
34095
|
+
let deliveryToken;
|
|
33340
34096
|
if (durability) {
|
|
33341
34097
|
try {
|
|
33342
34098
|
await this.ensureMigrated();
|
|
33343
34099
|
await this.ensurePendingClientTable();
|
|
34100
|
+
const capability = await createToolDeliveryCapability();
|
|
34101
|
+
deliveryToken = capability.token;
|
|
33344
34102
|
const ownerId = owner?.client_id ?? null;
|
|
33345
34103
|
const socketClientId = this.bridgeClientInfo.get(socket)?.clientId ?? null;
|
|
33346
34104
|
const fenced = owner && socketClientId === owner.client_id;
|
|
33347
34105
|
await this.ctx.storage.sql.exec(
|
|
33348
34106
|
`INSERT OR REPLACE INTO pending_client_calls
|
|
33349
34107
|
(tool_call_id, tool, args, request_permission, risk, side, status, created_at,
|
|
33350
|
-
dispatched_client_id, dispatched_generation)
|
|
33351
|
-
VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?, ?)`,
|
|
34108
|
+
dispatched_client_id, dispatched_generation, delivery_token_hash)
|
|
34109
|
+
VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?, ?, ?)`,
|
|
33352
34110
|
durability.toolCallId,
|
|
33353
34111
|
request.tool,
|
|
33354
34112
|
JSON.stringify(request.args ?? {}),
|
|
@@ -33357,7 +34115,8 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33357
34115
|
durability.side,
|
|
33358
34116
|
Date.now(),
|
|
33359
34117
|
fenced ? ownerId : null,
|
|
33360
|
-
fenced ? owner.generation : 0
|
|
34118
|
+
fenced ? owner.generation : 0,
|
|
34119
|
+
capability.tokenHash
|
|
33361
34120
|
);
|
|
33362
34121
|
} catch (error) {
|
|
33363
34122
|
console.error("[DurableThread] Failed to persist fast-path call (continuing):", error);
|
|
@@ -33393,7 +34152,8 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33393
34152
|
tool: request.tool,
|
|
33394
34153
|
args: request.args ?? {},
|
|
33395
34154
|
requestPermission: request.requestPermission ?? null,
|
|
33396
|
-
risk: typeof request.risk === "number" ? request.risk : null
|
|
34155
|
+
risk: typeof request.risk === "number" ? request.risk : null,
|
|
34156
|
+
deliveryToken: deliveryToken ?? null
|
|
33397
34157
|
});
|
|
33398
34158
|
try {
|
|
33399
34159
|
socket.send(frame);
|
|
@@ -33516,7 +34276,8 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33516
34276
|
status TEXT NOT NULL DEFAULT 'pending',
|
|
33517
34277
|
created_at INTEGER NOT NULL,
|
|
33518
34278
|
dispatched_client_id TEXT,
|
|
33519
|
-
dispatched_generation INTEGER NOT NULL DEFAULT 0
|
|
34279
|
+
dispatched_generation INTEGER NOT NULL DEFAULT 0,
|
|
34280
|
+
delivery_token_hash TEXT
|
|
33520
34281
|
)
|
|
33521
34282
|
`);
|
|
33522
34283
|
}
|
|
@@ -33557,13 +34318,15 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33557
34318
|
const socketClientId = this.bridgeClientInfo.get(socket)?.clientId ?? null;
|
|
33558
34319
|
const fenced = owner && socketClientId === owner.client_id;
|
|
33559
34320
|
const generation = fenced ? owner.generation : 0;
|
|
34321
|
+
const capability = await createToolDeliveryCapability();
|
|
33560
34322
|
try {
|
|
33561
34323
|
await this.ctx.storage.sql.exec(
|
|
33562
34324
|
`UPDATE pending_client_calls
|
|
33563
|
-
SET dispatched_client_id = ?, dispatched_generation = ?
|
|
34325
|
+
SET dispatched_client_id = ?, dispatched_generation = ?, delivery_token_hash = ?
|
|
33564
34326
|
WHERE tool_call_id = ? AND status = 'pending'`,
|
|
33565
34327
|
fenced ? owner.client_id : null,
|
|
33566
34328
|
generation,
|
|
34329
|
+
capability.tokenHash,
|
|
33567
34330
|
toolCallId
|
|
33568
34331
|
);
|
|
33569
34332
|
socket.send(
|
|
@@ -33575,7 +34338,8 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33575
34338
|
args: request.args ?? {},
|
|
33576
34339
|
requestPermission: request.requestPermission ?? null,
|
|
33577
34340
|
risk: typeof request.risk === "number" ? request.risk : null,
|
|
33578
|
-
generation
|
|
34341
|
+
generation,
|
|
34342
|
+
deliveryToken: capability.token
|
|
33579
34343
|
})
|
|
33580
34344
|
);
|
|
33581
34345
|
this.bridgeLastSeen.set(socket, Date.now());
|
|
@@ -33698,12 +34462,14 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33698
34462
|
const pending = await this.getPendingClientCalls();
|
|
33699
34463
|
for (const p of pending) {
|
|
33700
34464
|
if (socket.readyState !== WebSocket.OPEN) break;
|
|
34465
|
+
const capability = await createToolDeliveryCapability();
|
|
33701
34466
|
await this.ctx.storage.sql.exec(
|
|
33702
34467
|
`UPDATE pending_client_calls
|
|
33703
|
-
SET dispatched_client_id = ?, dispatched_generation = ?
|
|
34468
|
+
SET dispatched_client_id = ?, dispatched_generation = ?, delivery_token_hash = ?
|
|
33704
34469
|
WHERE tool_call_id = ? AND status = 'pending'`,
|
|
33705
34470
|
fenced ? owner.client_id : null,
|
|
33706
34471
|
generation,
|
|
34472
|
+
capability.tokenHash,
|
|
33707
34473
|
p.toolCallId
|
|
33708
34474
|
);
|
|
33709
34475
|
socket.send(
|
|
@@ -33715,7 +34481,8 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33715
34481
|
args: p.args,
|
|
33716
34482
|
requestPermission: p.requestPermission,
|
|
33717
34483
|
risk: p.risk,
|
|
33718
|
-
generation
|
|
34484
|
+
generation,
|
|
34485
|
+
deliveryToken: capability.token
|
|
33719
34486
|
})
|
|
33720
34487
|
);
|
|
33721
34488
|
}
|
|
@@ -33728,20 +34495,25 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33728
34495
|
* call resolved. Returns the side to continue, or null if there is no pending
|
|
33729
34496
|
* call (unknown id or already resolved — the delivery is idempotent).
|
|
33730
34497
|
*/
|
|
33731
|
-
async deliverClientToolResult(toolCallId, ok, result, error, deliveredByClientId, deliveredGeneration) {
|
|
34498
|
+
async deliverClientToolResult(toolCallId, ok, result, error, deliveredByClientId, deliveredGeneration, deliveryToken, internalAuthorization) {
|
|
33732
34499
|
await this.ensureMigrated();
|
|
33733
34500
|
await this.ensurePendingClientTable();
|
|
33734
34501
|
const pendingCursor = await this.ctx.storage.sql.exec(
|
|
33735
|
-
`SELECT side, status, dispatched_client_id, dispatched_generation
|
|
34502
|
+
`SELECT side, status, dispatched_client_id, dispatched_generation, delivery_token_hash
|
|
33736
34503
|
FROM pending_client_calls WHERE tool_call_id = ? LIMIT 1`,
|
|
33737
34504
|
toolCallId
|
|
33738
34505
|
);
|
|
33739
34506
|
const pending = pendingCursor.toArray()[0];
|
|
33740
34507
|
if (!pending || pending.status !== "pending") return null;
|
|
33741
|
-
|
|
33742
|
-
|
|
33743
|
-
|
|
33744
|
-
|
|
34508
|
+
const proof = {
|
|
34509
|
+
token: deliveryToken,
|
|
34510
|
+
clientId: deliveredByClientId,
|
|
34511
|
+
generation: deliveredGeneration
|
|
34512
|
+
};
|
|
34513
|
+
if (internalAuthorization !== INTERNAL_TOOL_DELIVERY) {
|
|
34514
|
+
if (!await authorizesToolDelivery(pending, proof)) return null;
|
|
34515
|
+
} else if (!matchesToolDeliveryOwner(pending, proof)) {
|
|
34516
|
+
return null;
|
|
33745
34517
|
}
|
|
33746
34518
|
const side = pending.side === "b" ? "b" : "a";
|
|
33747
34519
|
const content = ok ? result ?? "" : `Error: ${error ?? "tool failed"}`;
|
|
@@ -34458,29 +35230,17 @@ ${resultContent}${attachmentSummary}`;
|
|
|
34458
35230
|
*/
|
|
34459
35231
|
async webSocketMessage(ws, message) {
|
|
34460
35232
|
try {
|
|
34461
|
-
|
|
34462
|
-
this.
|
|
34463
|
-
|
|
35233
|
+
const handledBridgeFrame = routeBridgeClientFrame(ws, message, {
|
|
35234
|
+
isTracked: (socket) => this.bridgeSockets.has(socket),
|
|
35235
|
+
markSeen: (socket) => this.bridgeLastSeen.set(socket, Date.now()),
|
|
35236
|
+
resolveToolResponse: (socket, payload) => this.resolveClientCall(socket, payload),
|
|
35237
|
+
handleStreamFrame: (socket, payload) => this.handleClientStreamFrame(socket, payload)
|
|
35238
|
+
});
|
|
35239
|
+
if (handledBridgeFrame) return;
|
|
34464
35240
|
if (typeof message === "string" && message === "ping") {
|
|
34465
35241
|
ws.send("pong");
|
|
34466
35242
|
return;
|
|
34467
35243
|
}
|
|
34468
|
-
if (typeof message === "string") {
|
|
34469
|
-
let parsed = null;
|
|
34470
|
-
try {
|
|
34471
|
-
parsed = JSON.parse(message);
|
|
34472
|
-
} catch {
|
|
34473
|
-
parsed = null;
|
|
34474
|
-
}
|
|
34475
|
-
if (parsed && parsed.type === "tool_response") {
|
|
34476
|
-
this.resolveClientCall(ws, parsed);
|
|
34477
|
-
return;
|
|
34478
|
-
}
|
|
34479
|
-
if (parsed && typeof parsed.type === "string" && parsed.type.startsWith("stream_") && parsed.type !== "stream_ping" && parsed.type !== "stream_pong") {
|
|
34480
|
-
this.handleClientStreamFrame(ws, parsed);
|
|
34481
|
-
return;
|
|
34482
|
-
}
|
|
34483
|
-
}
|
|
34484
35244
|
} catch (error) {
|
|
34485
35245
|
console.error("[DurableThread] Error handling WebSocket message:", error);
|
|
34486
35246
|
}
|
|
@@ -37487,6 +38247,6 @@ init_context();
|
|
|
37487
38247
|
init_ProviderRegistry();
|
|
37488
38248
|
init_types2();
|
|
37489
38249
|
|
|
37490
|
-
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, 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, 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 };
|
|
38250
|
+
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 };
|
|
37491
38251
|
//# sourceMappingURL=index.js.map
|
|
37492
38252
|
//# sourceMappingURL=index.js.map
|