@standardagents/builder 0.37.4 → 0.37.5
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 +160 -79
- package/dist/built-in-routes.js.map +1 -1
- package/dist/client/{ApiKeysView-BhuT3pIF.js → ApiKeysView-Pk5oljVU.js} +1 -1
- package/dist/client/CenteredContentView-HGpMGAQj.js +1 -0
- package/dist/client/CompositionView-DN8dTOCO.js +32 -0
- package/dist/client/{ConfirmDialog.vue_vue_type_script_setup_true_lang-BqwwsXWJ.js → ConfirmDialog.vue_vue_type_script_setup_true_lang--j_hnMZ0.js} +1 -1
- package/dist/client/{CopyButton.vue_vue_type_script_setup_true_lang-Cqm3Vt3E.js → CopyButton.vue_vue_type_script_setup_true_lang-CpJDcbZx.js} +1 -1
- package/dist/client/{DataTable.vue_vue_type_script_setup_true_lang-CA5qgsty.js → DataTable.vue_vue_type_script_setup_true_lang-unLrY-PY.js} +1 -1
- package/dist/client/{JsonViewer-ByQKCShF.js → JsonViewer-BYj_yrGB.js} +1 -1
- package/dist/client/{LoginView-J4qtHbQK.js → LoginView-CfVqItZV.js} +1 -1
- package/dist/client/{MarketplaceView-CshdWDqp.js → MarketplaceView-Cidt0nLf.js} +1 -1
- package/dist/client/{Modal.vue_vue_type_script_setup_true_lang-DKdB9A8Z.js → Modal.vue_vue_type_script_setup_true_lang-C1dmm1HX.js} +1 -1
- package/dist/client/ModelModal.vue_vue_type_script_setup_true_lang-DIKLuPXT.js +1 -0
- package/dist/client/{ModelsView-B_n306yk.js → ModelsView-DH6wNe38.js} +1 -1
- package/dist/client/{PromptEditView-C85qRgsE.js → PromptEditView-D_VjoAa-.js} +1 -1
- package/dist/client/{PromptModal-PNpoJfqD.js → PromptModal-a9LTOWNt.js} +1 -1
- package/dist/client/{PromptsView-BKnfThIF.js → PromptsView-BCCCryKR.js} +1 -1
- package/dist/client/{ProvidersView-Df41VhHD.js → ProvidersView-CF4x5F6G.js} +2 -2
- package/dist/client/ThreadInspectorPane.vue_vue_type_script_setup_true_lang-D5VDoQiK.js +22 -0
- package/dist/client/ThreadsView-BzscU1l2.js +1 -0
- package/dist/client/{ToolsView-C0MH9x3N.js → ToolsView-CeEtxDZV.js} +1 -1
- package/dist/client/{UiCodeBlock.vue_vue_type_script_setup_true_lang-DjDQOYjB.js → UiCodeBlock.vue_vue_type_script_setup_true_lang-BdyzSxZZ.js} +1 -1
- package/dist/client/{UsersView--TQiJYP7.js → UsersView-Di5cmUYL.js} +1 -1
- package/dist/client/{VariablesView-BO3UljZu.js → VariablesView-CXUr05jD.js} +1 -1
- package/dist/client/assets/{CompositionView-nJBKoNx4.css → CompositionView-0qQUWuc1.css} +1 -1
- package/dist/client/{check-DeHZQcv4.js → check-YKi1o6p6.js} +1 -1
- package/dist/client/{chevron-right-C8Mb0B59.js → chevron-right-T0zzq_GX.js} +1 -1
- package/dist/client/{copy-BxJsl-KZ.js → copy-C_gUGAj1.js} +1 -1
- package/dist/client/{formkit-0eyyCEP_.js → formkit-CE26dtFp.js} +1 -1
- package/dist/client/index-BMqXblce.js +6 -0
- package/dist/client/index.html +3 -3
- package/dist/client/{package-B9czwyIu.js → package-B70IFSDB.js} +1 -1
- package/dist/client/{plus-DqtuqJFm.js → plus-BN9IHYdt.js} +1 -1
- package/dist/client/{timeAgo-CDt-vAhI.js → timeAgo-G1ovbWPH.js} +1 -1
- package/dist/client/{useActionFeedback-B6UfBJ7l.js → useActionFeedback-7pfPjKMi.js} +1 -1
- package/dist/client/{vue-Dw7WLX4c.js → vue-CZ-ORfS3.js} +1 -1
- package/dist/{index-DceFbLG9.d.ts → index-Lfan8QMN.d.ts} +46 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +279 -23
- package/dist/index.js.map +1 -1
- package/dist/plugin.js +6 -0
- package/dist/plugin.js.map +1 -1
- package/dist/runtime.d.ts +45 -5
- package/dist/runtime.js +273 -23
- package/dist/runtime.js.map +1 -1
- package/dist/test.d.ts +1 -1
- package/package.json +5 -5
- package/dist/client/CenteredContentView-CfgX3Ao3.js +0 -1
- package/dist/client/CompositionView-B9FfWMHd.js +0 -32
- package/dist/client/ModelModal.vue_vue_type_script_setup_true_lang-BAxaBZsx.js +0 -1
- package/dist/client/ThreadInspectorPane.vue_vue_type_script_setup_true_lang-CtIBWHUR.js +0 -22
- package/dist/client/ThreadsView-sjo5Rrcm.js +0 -1
- package/dist/client/index-BgZnQr8X.js +0 -6
|
@@ -1154,6 +1154,46 @@ declare function requireAuth(request: Request, env: any): Promise<AuthContext |
|
|
|
1154
1154
|
*/
|
|
1155
1155
|
declare function requireAdmin(request: Request, env: any): Promise<AuthContext | Response>;
|
|
1156
1156
|
|
|
1157
|
+
/**
|
|
1158
|
+
* Same-request reuse of the router's authentication and thread-authorization
|
|
1159
|
+
* work (AB-05, coordinator docs/plans/active/CLOUDFLARE_COST_FINDINGS.md).
|
|
1160
|
+
*
|
|
1161
|
+
* The generated runtime router authenticates every protected API request and,
|
|
1162
|
+
* for thread routes, loads the registry record and checks thread access —
|
|
1163
|
+
* then controllers historically repeated the exact same singleton RPCs
|
|
1164
|
+
* (validateSession/validateApiKey + getUserById, getThread), doubling
|
|
1165
|
+
* singleton traffic on every protected request.
|
|
1166
|
+
*
|
|
1167
|
+
* SECURITY CONTRACT (the AB-05 gate):
|
|
1168
|
+
* - Reuse happens ONLY within one incoming request: the router computes these
|
|
1169
|
+
* values per request and passes them as explicit controller-context data.
|
|
1170
|
+
* There is NO cross-request cache, NO module-global map, NO retained
|
|
1171
|
+
* tokens/cookies/identities — revocation is effective on the next request
|
|
1172
|
+
* because the next request re-authenticates from scratch.
|
|
1173
|
+
* - Thread authorization is bound to the EXACT thread id the router
|
|
1174
|
+
* authorized. A controller asking about any other id (including nested
|
|
1175
|
+
* route params also named `id`) gets null and falls back to its own
|
|
1176
|
+
* lookup + access check.
|
|
1177
|
+
* - Permission-specific checks stay at the operation boundary: reusing the
|
|
1178
|
+
* authenticated identity never implies a permission; helpers re-evaluate
|
|
1179
|
+
* `hasPermission`/role against the reused context on every call.
|
|
1180
|
+
* - Controllers invoked WITHOUT router context (direct tests, standalone
|
|
1181
|
+
* invocation, trusted-local public routes) execute their historical
|
|
1182
|
+
* authentication path unchanged.
|
|
1183
|
+
*/
|
|
1184
|
+
|
|
1185
|
+
/** The router's thread-authorization result, bound to one exact thread id. */
|
|
1186
|
+
interface RouterThreadAccess {
|
|
1187
|
+
/** The thread id (as matched from the route) the router authorized. */
|
|
1188
|
+
threadId: string;
|
|
1189
|
+
/**
|
|
1190
|
+
* The registry record the router loaded while authorizing. Same-request
|
|
1191
|
+
* read-only reuse only — a controller that mutates thread metadata must
|
|
1192
|
+
* re-read after its mutation rather than trusting this snapshot.
|
|
1193
|
+
*/
|
|
1194
|
+
thread: Record<string, unknown>;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1157
1197
|
/**
|
|
1158
1198
|
* Router and endpoint definition module for AgentBuilder.
|
|
1159
1199
|
*
|
|
@@ -1740,6 +1780,12 @@ interface BuilderControllerContext<Env extends ThreadEnv = ThreadEnv> extends Om
|
|
|
1740
1780
|
registry?: NamespacedRegistry;
|
|
1741
1781
|
/** Authenticated caller, populated by the generated runtime router. */
|
|
1742
1782
|
auth?: AuthContext | null;
|
|
1783
|
+
/**
|
|
1784
|
+
* The router's thread-authorization result for this request, bound to the
|
|
1785
|
+
* exact matched thread id (AB-05 same-request reuse). Populated only after
|
|
1786
|
+
* the router's own getThread + canAccessThread checks passed.
|
|
1787
|
+
*/
|
|
1788
|
+
threadAccess?: RouterThreadAccess | null;
|
|
1743
1789
|
}
|
|
1744
1790
|
/**
|
|
1745
1791
|
* Builder-specific controller type with typed env.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ModelDefinition, AgentDefinition } from './runtime.js';
|
|
2
2
|
export { AgentBuilderEnv, AgentBuilderEvent, BroadcastOptions, CodeExecutionBridge, DurableAgentBuilder, DurableThread, DurableUserHub, FlowStateSdk, FlowStateWithSdk, HubClientInfo, ImageContextConfig, InjectMessageOptions, LLMProviderInterface, NamespaceResolutionError, PromptContent, PromptDefinition, PromptIncludePart, PromptPart, PromptToolConfig, Provider, ProviderContentPart, ProviderFilePart, ProviderImagePart, ProviderRegistry, ProviderTextPart, SessionToolBinding, SessionToolConfig, SideConfig, SubagentToolConfig, SubpromptConfig, ThreadEventPayload, ThreadRegistryEntry, ThreadWatchdog, ThreadWatchdogState, ToolConfig, UpdateThreadParams, User, WatchdogCheckin, WatchdogProbeResult, buildImageDescription, canAccessThread, cat, createNamespaceContext, emitThreadEvent, enhanceFlowState, exists, find, forceTurn, generateImageDescription, getFileStats, getMessages, getMessagesToSummarize, getShortName, getThumbnail, getUnsummarizedImageAttachments, getVisibleAgentNames, getVisibleModelNames, getVisiblePromptNames, getVisibleToolNames, grep, hasImageAttachments, head, injectMessage, isAdminOnlyApiRoute, isHostedInstance, isPublicApiRoute, isQualifiedName, isTrustedLocalDevelopmentRequest, linkFile, mkdir, move, optimizeImageContext, parseQualifiedName, qualifyName, queueTool, readFile, readdir, reloadHistory, rename, replaceImagesWithDescriptions, resolveAgent, resolveHook, resolveModel, resolvePrompt, resolveTool, rmdir, stat, tail, unlink, updateThread, writeFile, writeImage } from './runtime.js';
|
|
3
3
|
export { AgentPluginOptions, agentbuilder, builder } from './plugin.js';
|
|
4
|
-
export { v as Agent, H as AttachmentRef, i as AuthContext, A as AuthUser, f as BuilderThreadEndpointHandler, C as CLIENT_STREAM_CHUNK_BYTES, t as ClientDuplex, u as ClientForwarding, p as ClientStreamRequest, q as ClientStreamResponse, n as ClientToolRequest, o as ClientToolResponse, B as Controller, a as ControllerContext, E as Env, G as FileRecord, K as FileStats, z as FlowResult, F as FlowState, J as GrepResult, O as ImageContentPart, I as ImageMetadata, L as LLMResponse, M as Message, Q as MessageContent, P as MultimodalContent, R as RequestContext, S as StorageBackend, D as TelemetryEvent, N as TextContentPart, b as ThreadEndpointContext, e as ThreadEnv, w as ThreadInstance, x as ThreadMetadata, T as ToolCall, y as ToolResult, g as authenticate, j as callClient, k as callClientStream, c as createThreadEndpointHandler, m as decodeStreamChunk, d as defineController, l as encodeStreamChunk, h as requireAdmin, r as requireAuth, s as supportsClientForwarding } from './index-
|
|
4
|
+
export { v as Agent, H as AttachmentRef, i as AuthContext, A as AuthUser, f as BuilderThreadEndpointHandler, C as CLIENT_STREAM_CHUNK_BYTES, t as ClientDuplex, u as ClientForwarding, p as ClientStreamRequest, q as ClientStreamResponse, n as ClientToolRequest, o as ClientToolResponse, B as Controller, a as ControllerContext, E as Env, G as FileRecord, K as FileStats, z as FlowResult, F as FlowState, J as GrepResult, O as ImageContentPart, I as ImageMetadata, L as LLMResponse, M as Message, Q as MessageContent, P as MultimodalContent, R as RequestContext, S as StorageBackend, D as TelemetryEvent, N as TextContentPart, b as ThreadEndpointContext, e as ThreadEnv, w as ThreadInstance, x as ThreadMetadata, T as ToolCall, y as ToolResult, g as authenticate, j as callClient, k as callClientStream, c as createThreadEndpointHandler, m as decodeStreamChunk, d as defineController, l as encodeStreamChunk, h as requireAdmin, r as requireAuth, s as supportsClientForwarding } from './index-Lfan8QMN.js';
|
|
5
5
|
export { AgentType, DefinitionLoader, GlobalNamespaceContext, HookSignatures, ImageContent, ModelCapabilities, ModelProvider, NamespaceContext, PackageSignature, PackedExports, PackedMeta, PackedMetadata, PackedNamespaceContext, PromptInput, PromptTextPart, ProviderAssistantMessage, ProviderConfigSlot, ProviderConfigSlots, ProviderConfigValueSource, ProviderDefinition, ProviderError, ProviderErrorCode, ProviderFactory, ProviderFactoryConfig, ProviderFinishReason, ProviderGeneratedImage, ProviderMessage, ProviderMessageContent, ProviderMethodOverrides, ModelCapabilities as ProviderModelCapabilities, ProviderOverride, ProviderOverrideContext, ProviderReasoningDetail, ProviderRequest, ProviderResponse, ProviderStreamChunk, ProviderSyncOverride, ProviderSystemMessage, ProviderTool, ProviderToolCallPart, ProviderToolMessage, ProviderToolResultContent, ProviderUsage, ProviderUserMessage, ReasoningConfig, StructuredPrompt, TextContent, Tool, ToolArgs, ToolArgsNode, ToolArgsRawShape, ToolContent, belongsToPackage, defineAgent, defineHook, defineModel, definePrompt, defineProvider, defineTool, isPacked, isVisibleInNamespace, mapReasoningLevel, providerConst, providerEnv, providerValue } from '@standardagents/spec';
|
|
6
6
|
export { C as ConstituentItem, D as DiscoveredPackage, N as NamespacedRegistry, P as PackingAnalysis, a as PackingOptions, b as PackingResult, c as UnpackOptions, d as UnpackResult } from './types-Bpe7IANZ.js';
|
|
7
7
|
export { D as DiscoveryConfig, P as PackageDiscoveryService, d as discoverPackages } from './discovery-DiMJWisl.js';
|
package/dist/index.js
CHANGED
|
@@ -13738,8 +13738,25 @@ var init_ThreadStateImpl = __esm({
|
|
|
13738
13738
|
const mustParkForApproval = await this.forwardedCallNeedsApproval(request);
|
|
13739
13739
|
if (mustParkForApproval && canPark && toolCallId && typeof instance.hasBridge === "function" && await instance.hasBridge()) {
|
|
13740
13740
|
await instance.dispatchClientTool(request, toolCallId, side);
|
|
13741
|
-
const
|
|
13742
|
-
if (typeof
|
|
13741
|
+
const withDelivery = instance;
|
|
13742
|
+
if (typeof withDelivery.awaitDeliveredClientToolResult === "function") {
|
|
13743
|
+
try {
|
|
13744
|
+
const delivered = await withDelivery.awaitDeliveredClientToolResult.call(
|
|
13745
|
+
instance,
|
|
13746
|
+
toolCallId,
|
|
13747
|
+
4e3
|
|
13748
|
+
);
|
|
13749
|
+
if (delivered) {
|
|
13750
|
+
return {
|
|
13751
|
+
ok: delivered.toolStatus !== "error",
|
|
13752
|
+
result: delivered.content,
|
|
13753
|
+
data: { toolCallId }
|
|
13754
|
+
};
|
|
13755
|
+
}
|
|
13756
|
+
} catch {
|
|
13757
|
+
}
|
|
13758
|
+
} else if (typeof withDelivery.getDeliveredClientToolResult === "function") {
|
|
13759
|
+
const getDelivered = withDelivery.getDeliveredClientToolResult;
|
|
13743
13760
|
const graceDeadline = Date.now() + 4e3;
|
|
13744
13761
|
while (Date.now() < graceDeadline) {
|
|
13745
13762
|
await new Promise((r) => setTimeout(r, 150));
|
|
@@ -13829,7 +13846,14 @@ var init_ThreadStateImpl = __esm({
|
|
|
13829
13846
|
if (typeof stub.proxyStreamOpen === "function") {
|
|
13830
13847
|
const opened = await stub.proxyStreamOpen(withWake(parentId));
|
|
13831
13848
|
if (opened.ok && opened.id) {
|
|
13832
|
-
return {
|
|
13849
|
+
return {
|
|
13850
|
+
ok: true,
|
|
13851
|
+
duplex: this.buildProxyDuplex(
|
|
13852
|
+
stub,
|
|
13853
|
+
opened.id,
|
|
13854
|
+
opened.parkedPull === true
|
|
13855
|
+
)
|
|
13856
|
+
};
|
|
13833
13857
|
}
|
|
13834
13858
|
if (opened.error && !opened.noClient) {
|
|
13835
13859
|
return {
|
|
@@ -13847,11 +13871,15 @@ var init_ThreadStateImpl = __esm({
|
|
|
13847
13871
|
}
|
|
13848
13872
|
/**
|
|
13849
13873
|
* Build a client duplex that bridges to a stream opened on an ancestor DO
|
|
13850
|
-
* (the bridge holder) via chunk-pull RPCs. `readable`
|
|
13851
|
-
*
|
|
13874
|
+
* (the bridge holder) via chunk-pull RPCs. `readable` issues PARKED pulls —
|
|
13875
|
+
* a current ancestor holds one `proxyStreamPull` until bytes, completion,
|
|
13876
|
+
* error, or abort, so an idle open stream performs zero repeated pulls.
|
|
13877
|
+
* Older ancestors use the bounded long-pull compatibility path. `writable`
|
|
13878
|
+
* forwards each chunk with `proxyStreamWrite`; teardown calls
|
|
13852
13879
|
* `proxyStreamAbort`.
|
|
13853
13880
|
*/
|
|
13854
|
-
|
|
13881
|
+
static PROXY_PULL_MAX_WAIT_MS = 2e4;
|
|
13882
|
+
buildProxyDuplex(stub, id, supportsParkedPull = false) {
|
|
13855
13883
|
let settleDone;
|
|
13856
13884
|
const done = new Promise((r) => settleDone = r);
|
|
13857
13885
|
let closed = false;
|
|
@@ -13864,9 +13892,13 @@ var init_ThreadStateImpl = __esm({
|
|
|
13864
13892
|
const readable = new ReadableStream({
|
|
13865
13893
|
async pull(controller) {
|
|
13866
13894
|
while (!closed) {
|
|
13895
|
+
const pullStartedAt = Date.now();
|
|
13867
13896
|
let res;
|
|
13868
13897
|
try {
|
|
13869
|
-
res = await stub.proxyStreamPull(
|
|
13898
|
+
res = await stub.proxyStreamPull(
|
|
13899
|
+
id,
|
|
13900
|
+
supportsParkedPull ? void 0 : _ThreadStateImpl.PROXY_PULL_MAX_WAIT_MS
|
|
13901
|
+
);
|
|
13870
13902
|
} catch (error) {
|
|
13871
13903
|
controller.error(error instanceof Error ? error : new Error(String(error)));
|
|
13872
13904
|
settle({ ok: false, error: "proxy stream pull failed" });
|
|
@@ -13881,7 +13913,14 @@ var init_ThreadStateImpl = __esm({
|
|
|
13881
13913
|
return;
|
|
13882
13914
|
}
|
|
13883
13915
|
if (res.chunks.length > 0) return;
|
|
13884
|
-
|
|
13916
|
+
if (supportsParkedPull) {
|
|
13917
|
+
controller.error(new Error("parked proxy pull returned without data"));
|
|
13918
|
+
settle({ ok: false, error: "parked proxy pull returned without data" });
|
|
13919
|
+
return;
|
|
13920
|
+
}
|
|
13921
|
+
if (Date.now() - pullStartedAt < 250) {
|
|
13922
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
13923
|
+
}
|
|
13885
13924
|
}
|
|
13886
13925
|
},
|
|
13887
13926
|
cancel() {
|
|
@@ -22245,6 +22284,10 @@ ${packedThreadRouteCode}
|
|
|
22245
22284
|
const isApiRoute = routePath.startsWith('/api/');
|
|
22246
22285
|
|
|
22247
22286
|
let authContext = null;
|
|
22287
|
+
// The router's thread-authorization result, bound to the exact matched
|
|
22288
|
+
// thread id, for same-request reuse by controllers (AB-05). Never cached
|
|
22289
|
+
// across requests.
|
|
22290
|
+
let threadAccess = null;
|
|
22248
22291
|
|
|
22249
22292
|
// Require authentication for all API routes except public ones
|
|
22250
22293
|
if (isApiRoute && !publicRoute) {
|
|
@@ -22277,6 +22320,7 @@ ${packedThreadRouteCode}
|
|
|
22277
22320
|
if (!canAccessThread(authContext, thread)) {
|
|
22278
22321
|
return addCorsHeaders(Response.json({ error: "Forbidden: You don't have access to this thread" }, { status: 403 }));
|
|
22279
22322
|
}
|
|
22323
|
+
threadAccess = { threadId, thread };
|
|
22280
22324
|
}
|
|
22281
22325
|
}
|
|
22282
22326
|
}
|
|
@@ -22297,6 +22341,7 @@ ${packedThreadRouteCode}
|
|
|
22297
22341
|
config,
|
|
22298
22342
|
tools,
|
|
22299
22343
|
auth: authContext, // Add auth context to controller context
|
|
22344
|
+
threadAccess, // Router's thread authorization, bound to the exact id
|
|
22300
22345
|
};
|
|
22301
22346
|
const result = await controller(context);
|
|
22302
22347
|
|
|
@@ -24400,6 +24445,9 @@ var AlarmQueue = class {
|
|
|
24400
24445
|
options.sourceLogId ?? null
|
|
24401
24446
|
);
|
|
24402
24447
|
await this.rescheduleAlarm();
|
|
24448
|
+
if (method === "runEffect") {
|
|
24449
|
+
await this.emitEffectRunChange(id);
|
|
24450
|
+
}
|
|
24403
24451
|
return id;
|
|
24404
24452
|
}
|
|
24405
24453
|
/**
|
|
@@ -24438,6 +24486,9 @@ var AlarmQueue = class {
|
|
|
24438
24486
|
`UPDATE alarm_queue SET status = 'processing', attempts = attempts + 1 WHERE id = ?`,
|
|
24439
24487
|
item.id
|
|
24440
24488
|
);
|
|
24489
|
+
if (item.method === "runEffect") {
|
|
24490
|
+
await this.emitEffectRunChange(item.id);
|
|
24491
|
+
}
|
|
24441
24492
|
try {
|
|
24442
24493
|
const args = JSON.parse(item.args);
|
|
24443
24494
|
await this.executeMethod(item.method, args, item.id);
|
|
@@ -24491,6 +24542,7 @@ var AlarmQueue = class {
|
|
|
24491
24542
|
now,
|
|
24492
24543
|
id
|
|
24493
24544
|
);
|
|
24545
|
+
await this.emitEffectRunChange(id);
|
|
24494
24546
|
}
|
|
24495
24547
|
/**
|
|
24496
24548
|
* Mark an item as failed but keep it for 24 hours for debugging
|
|
@@ -24510,6 +24562,7 @@ var AlarmQueue = class {
|
|
|
24510
24562
|
now,
|
|
24511
24563
|
id
|
|
24512
24564
|
);
|
|
24565
|
+
await this.emitEffectRunChange(id);
|
|
24513
24566
|
}
|
|
24514
24567
|
/**
|
|
24515
24568
|
* Clean up failed items older than 24 hours
|
|
@@ -24599,6 +24652,10 @@ var AlarmQueue = class {
|
|
|
24599
24652
|
*/
|
|
24600
24653
|
async recoverOrphanedProcessing(maxAttempts = 3) {
|
|
24601
24654
|
const now = Date.now() * 1e3;
|
|
24655
|
+
const orphanedEffectsCursor = await this.sql.exec(
|
|
24656
|
+
`SELECT id FROM alarm_queue WHERE status = 'processing' AND method = 'runEffect'`
|
|
24657
|
+
);
|
|
24658
|
+
const orphanedEffectIds = orphanedEffectsCursor.toArray().map((row) => row.id);
|
|
24602
24659
|
await this.sql.exec(
|
|
24603
24660
|
`UPDATE alarm_queue
|
|
24604
24661
|
SET status = 'failed',
|
|
@@ -24611,6 +24668,9 @@ var AlarmQueue = class {
|
|
|
24611
24668
|
await this.sql.exec(
|
|
24612
24669
|
`UPDATE alarm_queue SET status = 'pending' WHERE status = 'processing'`
|
|
24613
24670
|
);
|
|
24671
|
+
for (const id of orphanedEffectIds) {
|
|
24672
|
+
await this.emitEffectRunChange(id);
|
|
24673
|
+
}
|
|
24614
24674
|
}
|
|
24615
24675
|
// ============================================================
|
|
24616
24676
|
// Effect-specific Methods
|
|
@@ -24789,6 +24849,7 @@ var AlarmQueue = class {
|
|
|
24789
24849
|
const canceled = changesRows.length > 0 && changesRows[0].changes > 0;
|
|
24790
24850
|
if (canceled) {
|
|
24791
24851
|
await this.rescheduleAlarm();
|
|
24852
|
+
await this.emitEffectRunChange(id);
|
|
24792
24853
|
}
|
|
24793
24854
|
return canceled;
|
|
24794
24855
|
}
|
|
@@ -24863,6 +24924,7 @@ var AlarmQueue = class {
|
|
|
24863
24924
|
if (!changed) {
|
|
24864
24925
|
throw new Error(`Effect run ${item.id} is no longer pending`);
|
|
24865
24926
|
}
|
|
24927
|
+
await this.emitEffectRunChange(item.id);
|
|
24866
24928
|
try {
|
|
24867
24929
|
await this.executeMethod(item.method, JSON.parse(item.args), item.id);
|
|
24868
24930
|
await this.complete(item.id, item.method);
|
|
@@ -24874,6 +24936,45 @@ var AlarmQueue = class {
|
|
|
24874
24936
|
await this.rescheduleAlarm();
|
|
24875
24937
|
}
|
|
24876
24938
|
}
|
|
24939
|
+
/**
|
|
24940
|
+
* Read one effect row and hand its bounded projection (no args payload) to
|
|
24941
|
+
* the change listener. Self-filtering: operational (non-effect) rows and
|
|
24942
|
+
* already-deleted rows emit nothing, and a listener failure must never
|
|
24943
|
+
* disturb queue processing.
|
|
24944
|
+
*/
|
|
24945
|
+
async emitEffectRunChange(id) {
|
|
24946
|
+
const listener = this.options.onEffectRunChanged;
|
|
24947
|
+
if (!listener) return;
|
|
24948
|
+
try {
|
|
24949
|
+
const cursor = await this.sql.exec(
|
|
24950
|
+
`
|
|
24951
|
+
SELECT id, args, scheduled_at, created_at, status, attempts, error, completed_at, manual_run, manual_run_at, replayed_from
|
|
24952
|
+
FROM alarm_queue
|
|
24953
|
+
WHERE id = ? AND method = 'runEffect'
|
|
24954
|
+
LIMIT 1
|
|
24955
|
+
`,
|
|
24956
|
+
id
|
|
24957
|
+
);
|
|
24958
|
+
const row = cursor.toArray()[0];
|
|
24959
|
+
if (!row) return;
|
|
24960
|
+
const { effectName } = this.parseEffectPayload(row.args);
|
|
24961
|
+
listener({
|
|
24962
|
+
id: row.id,
|
|
24963
|
+
name: effectName,
|
|
24964
|
+
scheduledAt: row.scheduled_at,
|
|
24965
|
+
createdAt: row.created_at,
|
|
24966
|
+
status: row.status,
|
|
24967
|
+
attempts: row.attempts,
|
|
24968
|
+
error: row.error,
|
|
24969
|
+
completedAt: row.completed_at,
|
|
24970
|
+
manualRun: row.manual_run === 1,
|
|
24971
|
+
manualRunAt: row.manual_run_at,
|
|
24972
|
+
replayedFrom: row.replayed_from
|
|
24973
|
+
});
|
|
24974
|
+
} catch (error) {
|
|
24975
|
+
console.error(`[AlarmQueue] Failed to emit effect run change for ${id}:`, error);
|
|
24976
|
+
}
|
|
24977
|
+
}
|
|
24877
24978
|
normalizeOffset(value) {
|
|
24878
24979
|
const parsed = typeof value === "number" ? Math.trunc(value) : Number.NaN;
|
|
24879
24980
|
if (!Number.isFinite(parsed)) return 0;
|
|
@@ -29344,6 +29445,25 @@ var DurableThread = class _DurableThread extends DurableObject {
|
|
|
29344
29445
|
alarmQueueRearmed = false;
|
|
29345
29446
|
currentAbortController = null;
|
|
29346
29447
|
isExecuting = false;
|
|
29448
|
+
/**
|
|
29449
|
+
* In-memory waiters parked until the running flow finishes (AB-09): the
|
|
29450
|
+
* executeFlow busy-guard awaits this signal instead of re-checking
|
|
29451
|
+
* isExecuting every 500ms. Every isExecuting=false transition (execution
|
|
29452
|
+
* finallys and the watchdog takeover) notifies. In-memory only — a DO
|
|
29453
|
+
* restart drops waiters together with the busy caller.
|
|
29454
|
+
*/
|
|
29455
|
+
executionIdleWaiters = /* @__PURE__ */ new Set();
|
|
29456
|
+
notifyExecutionIdleWaiters() {
|
|
29457
|
+
if (!this.executionIdleWaiters || this.executionIdleWaiters.size === 0) return;
|
|
29458
|
+
const waiters = Array.from(this.executionIdleWaiters);
|
|
29459
|
+
this.executionIdleWaiters.clear();
|
|
29460
|
+
for (const wake of waiters) {
|
|
29461
|
+
try {
|
|
29462
|
+
wake();
|
|
29463
|
+
} catch {
|
|
29464
|
+
}
|
|
29465
|
+
}
|
|
29466
|
+
}
|
|
29347
29467
|
// ── Execution watchdog state (see watchdogCheck) ─────────────────────────
|
|
29348
29468
|
// In-memory heartbeat: bumped by noteExecutionProgress() on every streamed
|
|
29349
29469
|
// chunk and request attempt. Deliberately NOT persisted — after a DO restart
|
|
@@ -29368,7 +29488,11 @@ var DurableThread = class _DurableThread extends DurableObject {
|
|
|
29368
29488
|
this.alarmQueue = new AlarmQueue(ctx, {
|
|
29369
29489
|
retentionEnabled: env.AGENT_BUILDER_ALARM_RETENTION_ENABLED !== "false",
|
|
29370
29490
|
isForensicHold: async () => (await this.runtimeSafety.getStatus()).forensicHold,
|
|
29371
|
-
invocationSqlLimits: { rowsRead: 1e6, rowsWritten: 1e4 }
|
|
29491
|
+
invocationSqlLimits: { rowsRead: 1e6, rowsWritten: 1e4 },
|
|
29492
|
+
// Effect lifecycle transitions broadcast on the thread event channel so
|
|
29493
|
+
// inspectors stay live from the already-open message socket instead of
|
|
29494
|
+
// polling the effects list.
|
|
29495
|
+
onEffectRunChanged: (change) => this.broadcastEvent("effect_changed", change)
|
|
29372
29496
|
});
|
|
29373
29497
|
this.alarmQueue.setExecutor(
|
|
29374
29498
|
async (method, args, itemId) => {
|
|
@@ -29927,6 +30051,7 @@ var DurableThread = class _DurableThread extends DurableObject {
|
|
|
29927
30051
|
if (value === null || value === void 0) {
|
|
29928
30052
|
await this.ctx.storage.sql.exec(`DELETE FROM thread_values WHERE key = ?`, key);
|
|
29929
30053
|
this.broadcastLog({ type: "kv_data", key, deleted: true, updated_at: updatedAt });
|
|
30054
|
+
this.broadcastEvent("kv_changed", { key, deleted: true, updated_at: updatedAt });
|
|
29930
30055
|
return;
|
|
29931
30056
|
}
|
|
29932
30057
|
const serialized = this.serializeThreadValue(value);
|
|
@@ -29943,6 +30068,7 @@ var DurableThread = class _DurableThread extends DurableObject {
|
|
|
29943
30068
|
updatedAt
|
|
29944
30069
|
);
|
|
29945
30070
|
this.broadcastLog({ type: "kv_data", key, deleted: false, updated_at: updatedAt });
|
|
30071
|
+
this.broadcastEvent("kv_changed", { key, deleted: false, updated_at: updatedAt });
|
|
29946
30072
|
}
|
|
29947
30073
|
async listValues(options = {}) {
|
|
29948
30074
|
await this.ensureMigrated();
|
|
@@ -32963,6 +33089,7 @@ ${resultContent}${attachmentSummary}`;
|
|
|
32963
33089
|
if (isOwner) {
|
|
32964
33090
|
void this.resendPendingClientCalls(server);
|
|
32965
33091
|
}
|
|
33092
|
+
this.notifyOwnerBridgeWaiters();
|
|
32966
33093
|
return new Response(null, {
|
|
32967
33094
|
status: 101,
|
|
32968
33095
|
webSocket: client
|
|
@@ -32998,6 +33125,7 @@ ${resultContent}${attachmentSummary}`;
|
|
|
32998
33125
|
/** Persist + cache the execution-owner record (null releases ownership). */
|
|
32999
33126
|
async setExecutionOwner(owner) {
|
|
33000
33127
|
await this.setValue(EXECUTION_OWNER_KEY, owner);
|
|
33128
|
+
this.notifyOwnerBridgeWaiters();
|
|
33001
33129
|
}
|
|
33002
33130
|
/** Whether the given client id has an open bridge socket (excluding `except`).
|
|
33003
33131
|
* Idle bridge heartbeats are handled by Cloudflare without waking this object,
|
|
@@ -33355,6 +33483,55 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33355
33483
|
* permission level covers the call) otherwise race the placeholder into
|
|
33356
33484
|
* stored history, leaving the model blind to its own tool outcomes.
|
|
33357
33485
|
*/
|
|
33486
|
+
/**
|
|
33487
|
+
* In-memory waiters parked on a forwarded call's result delivery (AB-09).
|
|
33488
|
+
* `deliverClientToolResult` resolves them so the approval grace-wait reads
|
|
33489
|
+
* delivered state exactly once instead of re-polling it every 150ms.
|
|
33490
|
+
* In-memory only: a DO restart drops waiters along with the caller that
|
|
33491
|
+
* registered them, and the durable park path still resumes the turn.
|
|
33492
|
+
*/
|
|
33493
|
+
deliveredResultWaiters = /* @__PURE__ */ new Map();
|
|
33494
|
+
notifyDeliveredResultWaiters(toolCallId) {
|
|
33495
|
+
const waiters = this.deliveredResultWaiters?.get(toolCallId);
|
|
33496
|
+
if (!waiters) return;
|
|
33497
|
+
this.deliveredResultWaiters.delete(toolCallId);
|
|
33498
|
+
for (const wake of waiters) {
|
|
33499
|
+
try {
|
|
33500
|
+
wake();
|
|
33501
|
+
} catch {
|
|
33502
|
+
}
|
|
33503
|
+
}
|
|
33504
|
+
}
|
|
33505
|
+
/**
|
|
33506
|
+
* Await a parked forwarded call's delivered result: one immediate read,
|
|
33507
|
+
* then a single parked wait resolved by result delivery, bounded by one
|
|
33508
|
+
* hard deadline, then one final read. Replaces the caller-side 150ms
|
|
33509
|
+
* re-poll of {@link getDeliveredClientToolResult}.
|
|
33510
|
+
*/
|
|
33511
|
+
async awaitDeliveredClientToolResult(toolCallId, maxWaitMs) {
|
|
33512
|
+
const immediate = await this.getDeliveredClientToolResult(toolCallId);
|
|
33513
|
+
if (immediate) return immediate;
|
|
33514
|
+
if (!(maxWaitMs > 0)) return null;
|
|
33515
|
+
await new Promise((resolve3) => {
|
|
33516
|
+
let waiters = this.deliveredResultWaiters.get(toolCallId);
|
|
33517
|
+
if (!waiters) {
|
|
33518
|
+
waiters = /* @__PURE__ */ new Set();
|
|
33519
|
+
this.deliveredResultWaiters.set(toolCallId, waiters);
|
|
33520
|
+
}
|
|
33521
|
+
const registered = waiters;
|
|
33522
|
+
const deadline = setTimeout(() => {
|
|
33523
|
+
registered.delete(wake);
|
|
33524
|
+
if (registered.size === 0) this.deliveredResultWaiters.delete(toolCallId);
|
|
33525
|
+
resolve3();
|
|
33526
|
+
}, maxWaitMs);
|
|
33527
|
+
const wake = () => {
|
|
33528
|
+
clearTimeout(deadline);
|
|
33529
|
+
resolve3();
|
|
33530
|
+
};
|
|
33531
|
+
registered.add(wake);
|
|
33532
|
+
});
|
|
33533
|
+
return this.getDeliveredClientToolResult(toolCallId);
|
|
33534
|
+
}
|
|
33358
33535
|
async getDeliveredClientToolResult(toolCallId) {
|
|
33359
33536
|
await this.ensureMigrated();
|
|
33360
33537
|
await this.ensurePendingClientTable();
|
|
@@ -33490,6 +33667,7 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33490
33667
|
`UPDATE pending_client_calls SET status = 'resolved' WHERE tool_call_id = ?`,
|
|
33491
33668
|
toolCallId
|
|
33492
33669
|
);
|
|
33670
|
+
this.notifyDeliveredResultWaiters(toolCallId);
|
|
33493
33671
|
try {
|
|
33494
33672
|
const msgCursor = await this.ctx.storage.sql.exec(
|
|
33495
33673
|
`SELECT id, role, content, name, tool_call_id, tool_status, created_at, depth
|
|
@@ -33508,8 +33686,24 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33508
33686
|
// they are bound to one bridge socket and never park.
|
|
33509
33687
|
/** Default time to wait for the client to accept a stream open. */
|
|
33510
33688
|
static CLIENT_STREAM_OPEN_TIMEOUT_MS = 3e4;
|
|
33511
|
-
/**
|
|
33512
|
-
|
|
33689
|
+
/**
|
|
33690
|
+
* In-memory waiters parked on the owner's bridge socket returning (AB-09).
|
|
33691
|
+
* Resolved by bridge attach and by execution-owner changes; each waiter
|
|
33692
|
+
* re-evaluates against the CURRENT owner on wake. In-memory only: a DO
|
|
33693
|
+
* restart drops waiters with the stream-open call that registered them.
|
|
33694
|
+
*/
|
|
33695
|
+
ownerBridgeWaiters = /* @__PURE__ */ new Set();
|
|
33696
|
+
notifyOwnerBridgeWaiters() {
|
|
33697
|
+
if (!this.ownerBridgeWaiters || this.ownerBridgeWaiters.size === 0) return;
|
|
33698
|
+
const waiters = Array.from(this.ownerBridgeWaiters);
|
|
33699
|
+
this.ownerBridgeWaiters.clear();
|
|
33700
|
+
for (const wake of waiters) {
|
|
33701
|
+
try {
|
|
33702
|
+
wake();
|
|
33703
|
+
} catch {
|
|
33704
|
+
}
|
|
33705
|
+
}
|
|
33706
|
+
}
|
|
33513
33707
|
/**
|
|
33514
33708
|
* The thread HAS an elected execution owner, but its bridge socket is gone
|
|
33515
33709
|
* (daemon restart, network blip, DO handoff). Summon the owning machine
|
|
@@ -33518,6 +33712,11 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33518
33712
|
* Sama One turns with "workspace unavailable" whenever a slow tool/approval
|
|
33519
33713
|
* outlived the socket. Threads with NO recorded owner (subagent children)
|
|
33520
33714
|
* still fail fast so ancestor walk-up routing stays immediate.
|
|
33715
|
+
*
|
|
33716
|
+
* The wait parks on bridge-attach/owner-change signals under ONE hard
|
|
33717
|
+
* deadline — no re-poll cadence. Each wake re-reads the owner because the
|
|
33718
|
+
* returning daemon may reclaim with a new generation, and the socket must
|
|
33719
|
+
* match the CURRENT owner.
|
|
33521
33720
|
*/
|
|
33522
33721
|
async awaitOwnerBridgeSocket(request) {
|
|
33523
33722
|
if (request.wake?.userId) {
|
|
@@ -33525,15 +33724,25 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33525
33724
|
}
|
|
33526
33725
|
const timeoutMs = typeof request.timeoutMs === "number" && request.timeoutMs > 0 ? request.timeoutMs : _DurableThread.CLIENT_STREAM_OPEN_TIMEOUT_MS;
|
|
33527
33726
|
const deadline = Date.now() + timeoutMs;
|
|
33528
|
-
|
|
33529
|
-
|
|
33530
|
-
(resolve3) => setTimeout(resolve3, _DurableThread.OWNER_REATTACH_POLL_MS)
|
|
33531
|
-
);
|
|
33727
|
+
this.ownerBridgeWaiters ??= /* @__PURE__ */ new Set();
|
|
33728
|
+
while (true) {
|
|
33532
33729
|
const owner = await this.getExecutionOwner();
|
|
33533
33730
|
const socket = this.getOpenBridgeSocket(owner?.client_id ?? null);
|
|
33534
33731
|
if (socket) return socket;
|
|
33732
|
+
const remainingMs = deadline - Date.now();
|
|
33733
|
+
if (remainingMs <= 0) return null;
|
|
33734
|
+
await new Promise((resolve3) => {
|
|
33735
|
+
const timer = setTimeout(() => {
|
|
33736
|
+
this.ownerBridgeWaiters.delete(wake);
|
|
33737
|
+
resolve3();
|
|
33738
|
+
}, remainingMs);
|
|
33739
|
+
const wake = () => {
|
|
33740
|
+
clearTimeout(timer);
|
|
33741
|
+
resolve3();
|
|
33742
|
+
};
|
|
33743
|
+
this.ownerBridgeWaiters.add(wake);
|
|
33744
|
+
});
|
|
33535
33745
|
}
|
|
33536
|
-
return null;
|
|
33537
33746
|
}
|
|
33538
33747
|
/** Open a bidirectional byte stream to the connected owner client. */
|
|
33539
33748
|
async openClientStream(request) {
|
|
@@ -33677,27 +33886,38 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33677
33886
|
done: false,
|
|
33678
33887
|
error: void 0,
|
|
33679
33888
|
pump: Promise.resolve(),
|
|
33680
|
-
close: (error) => duplex.close(error)
|
|
33889
|
+
close: (error) => duplex.close(error),
|
|
33890
|
+
waiter: null,
|
|
33891
|
+
notify: () => {
|
|
33892
|
+
const waiter = entry.waiter;
|
|
33893
|
+
entry.waiter = null;
|
|
33894
|
+
waiter?.();
|
|
33895
|
+
}
|
|
33681
33896
|
};
|
|
33682
33897
|
entry.pump = (async () => {
|
|
33683
33898
|
try {
|
|
33684
33899
|
while (true) {
|
|
33685
33900
|
const { value, done } = await reader.read();
|
|
33686
33901
|
if (done) break;
|
|
33687
|
-
if (value)
|
|
33902
|
+
if (value) {
|
|
33903
|
+
entry.buffer.push(value);
|
|
33904
|
+
entry.notify();
|
|
33905
|
+
}
|
|
33688
33906
|
}
|
|
33689
33907
|
} catch (error) {
|
|
33690
33908
|
entry.error = error instanceof Error ? error.message : String(error);
|
|
33691
33909
|
} finally {
|
|
33692
33910
|
entry.done = true;
|
|
33911
|
+
entry.notify();
|
|
33693
33912
|
}
|
|
33694
33913
|
})();
|
|
33695
33914
|
void duplex.done.then((d) => {
|
|
33696
33915
|
entry.done = true;
|
|
33697
33916
|
if (!d.ok && !entry.error) entry.error = d.error;
|
|
33917
|
+
entry.notify();
|
|
33698
33918
|
});
|
|
33699
33919
|
this.proxyStreams.set(id, entry);
|
|
33700
|
-
return { ok: true, id };
|
|
33920
|
+
return { ok: true, id, parkedPull: true };
|
|
33701
33921
|
}
|
|
33702
33922
|
async proxyStreamWrite(id, dataB64) {
|
|
33703
33923
|
const entry = this.proxyStreams.get(id);
|
|
@@ -33707,9 +33927,22 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33707
33927
|
} catch {
|
|
33708
33928
|
}
|
|
33709
33929
|
}
|
|
33710
|
-
async proxyStreamPull(id) {
|
|
33930
|
+
async proxyStreamPull(id, maxWaitMs) {
|
|
33711
33931
|
const entry = this.proxyStreams.get(id);
|
|
33712
33932
|
if (!entry) return { chunks: [], done: true, error: "unknown proxy stream" };
|
|
33933
|
+
if (entry.buffer.length === 0 && !entry.done) {
|
|
33934
|
+
await new Promise((resolve3) => {
|
|
33935
|
+
const deadline = typeof maxWaitMs === "number" && maxWaitMs > 0 ? setTimeout(() => {
|
|
33936
|
+
if (entry.waiter === wake) entry.waiter = null;
|
|
33937
|
+
resolve3();
|
|
33938
|
+
}, maxWaitMs) : null;
|
|
33939
|
+
const wake = () => {
|
|
33940
|
+
if (deadline) clearTimeout(deadline);
|
|
33941
|
+
resolve3();
|
|
33942
|
+
};
|
|
33943
|
+
entry.waiter = wake;
|
|
33944
|
+
});
|
|
33945
|
+
}
|
|
33713
33946
|
const chunks = entry.buffer.splice(0).map((b) => encodeStreamChunk(b));
|
|
33714
33947
|
if (entry.done && entry.buffer.length === 0) {
|
|
33715
33948
|
this.proxyStreams.delete(id);
|
|
@@ -33729,6 +33962,9 @@ ${resultContent}${attachmentSummary}`;
|
|
|
33729
33962
|
const entry = this.proxyStreams.get(id);
|
|
33730
33963
|
if (!entry) return;
|
|
33731
33964
|
this.proxyStreams.delete(id);
|
|
33965
|
+
entry.done = true;
|
|
33966
|
+
if (!entry.error) entry.error = "aborted";
|
|
33967
|
+
entry.notify();
|
|
33732
33968
|
try {
|
|
33733
33969
|
entry.close("aborted");
|
|
33734
33970
|
} catch {
|
|
@@ -34084,6 +34320,7 @@ ${resultContent}${attachmentSummary}`;
|
|
|
34084
34320
|
this.broadcastEvent(type, data);
|
|
34085
34321
|
}
|
|
34086
34322
|
broadcastEvent(type, data) {
|
|
34323
|
+
if (!this.messageSockets || this.messageSockets.size === 0) return;
|
|
34087
34324
|
const payload = JSON.stringify({
|
|
34088
34325
|
type: "event",
|
|
34089
34326
|
eventType: type,
|
|
@@ -34510,6 +34747,7 @@ ${resultContent}${attachmentSummary}`;
|
|
|
34510
34747
|
} catch {
|
|
34511
34748
|
}
|
|
34512
34749
|
this.isExecuting = false;
|
|
34750
|
+
this.notifyExecutionIdleWaiters();
|
|
34513
34751
|
this.currentAbortController = null;
|
|
34514
34752
|
await this.recordRegistryActivity(threadId, false);
|
|
34515
34753
|
tookOver = true;
|
|
@@ -34702,9 +34940,22 @@ Its execution was lost (the executor restarted or hung) and automatic recovery w
|
|
|
34702
34940
|
await this.assertNotTerminated();
|
|
34703
34941
|
if (this.isExecuting) {
|
|
34704
34942
|
const busyWaitBudgetMs = 3e4;
|
|
34705
|
-
const
|
|
34706
|
-
|
|
34707
|
-
|
|
34943
|
+
const deadline = Date.now() + busyWaitBudgetMs;
|
|
34944
|
+
this.executionIdleWaiters ??= /* @__PURE__ */ new Set();
|
|
34945
|
+
while (this.isExecuting) {
|
|
34946
|
+
const remainingMs = deadline - Date.now();
|
|
34947
|
+
if (remainingMs <= 0) break;
|
|
34948
|
+
await new Promise((resolve3) => {
|
|
34949
|
+
const timer = setTimeout(() => {
|
|
34950
|
+
this.executionIdleWaiters.delete(wake);
|
|
34951
|
+
resolve3();
|
|
34952
|
+
}, remainingMs);
|
|
34953
|
+
const wake = () => {
|
|
34954
|
+
clearTimeout(timer);
|
|
34955
|
+
resolve3();
|
|
34956
|
+
};
|
|
34957
|
+
this.executionIdleWaiters.add(wake);
|
|
34958
|
+
});
|
|
34708
34959
|
}
|
|
34709
34960
|
if (this.isExecuting) {
|
|
34710
34961
|
throw new Error(
|
|
@@ -34843,6 +35094,7 @@ Its execution was lost (the executor restarted or hung) and automatic recovery w
|
|
|
34843
35094
|
return flowResult;
|
|
34844
35095
|
} finally {
|
|
34845
35096
|
this.isExecuting = false;
|
|
35097
|
+
this.notifyExecutionIdleWaiters();
|
|
34846
35098
|
this.currentAbortController = null;
|
|
34847
35099
|
await this.disarmExecutionWatchdog(threadId);
|
|
34848
35100
|
await this.recordRegistryActivity(threadId, false);
|
|
@@ -35056,6 +35308,7 @@ Its execution was lost (the executor restarted or hung) and automatic recovery w
|
|
|
35056
35308
|
}, true);
|
|
35057
35309
|
} finally {
|
|
35058
35310
|
this.isExecuting = false;
|
|
35311
|
+
this.notifyExecutionIdleWaiters();
|
|
35059
35312
|
this.currentAbortController = null;
|
|
35060
35313
|
await this.disarmExecutionWatchdog(threadId);
|
|
35061
35314
|
await this.recordRegistryActivity(threadId, false);
|
|
@@ -35182,6 +35435,7 @@ Its execution was lost (the executor restarted or hung) and automatic recovery w
|
|
|
35182
35435
|
);
|
|
35183
35436
|
} finally {
|
|
35184
35437
|
this.isExecuting = false;
|
|
35438
|
+
this.notifyExecutionIdleWaiters();
|
|
35185
35439
|
this.currentAbortController = null;
|
|
35186
35440
|
await this.disarmExecutionWatchdog(threadId);
|
|
35187
35441
|
await this.recordRegistryActivity(threadId, false);
|
|
@@ -35608,6 +35862,7 @@ Its execution was lost (the executor restarted or hung) and automatic recovery w
|
|
|
35608
35862
|
const fs19 = this.getFileStorage();
|
|
35609
35863
|
const from = normalizePath2(sourcePath);
|
|
35610
35864
|
const record = await fs19.move(sourcePath, destinationPath);
|
|
35865
|
+
this.broadcastEvent("file_moved", { from, path: record.path, file: record });
|
|
35611
35866
|
this.broadcastEvent("file_deleted", { path: from });
|
|
35612
35867
|
this.broadcastEvent("file_created", { path: record.path, file: record });
|
|
35613
35868
|
return { success: true, file: record };
|
|
@@ -35626,6 +35881,7 @@ Its execution was lost (the executor restarted or hung) and automatic recovery w
|
|
|
35626
35881
|
const fs19 = this.getFileStorage();
|
|
35627
35882
|
const from = normalizePath2(path21);
|
|
35628
35883
|
const record = await fs19.rename(path21, newName);
|
|
35884
|
+
this.broadcastEvent("file_moved", { from, path: record.path, file: record });
|
|
35629
35885
|
this.broadcastEvent("file_deleted", { path: from });
|
|
35630
35886
|
this.broadcastEvent("file_created", { path: record.path, file: record });
|
|
35631
35887
|
return { success: true, file: record };
|