@effect-agent/platform-cloudflare 0.1.0-beta.12 → 0.1.0-beta.13
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/index.d.mts +10 -8
- package/dist/index.mjs +159 -79
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
- package/src/code-mode-executor.ts +243 -100
package/dist/index.d.mts
CHANGED
|
@@ -594,14 +594,6 @@ declare const encodeHostResponse: (input: AbortRecorded | ApprovalRecorded | Hos
|
|
|
594
594
|
readonly createdAt: string;
|
|
595
595
|
readonly deploymentId: string;
|
|
596
596
|
readonly payload: {
|
|
597
|
-
readonly _tag: "SubagentStarted";
|
|
598
|
-
readonly runId: string;
|
|
599
|
-
readonly toolCallId: string;
|
|
600
|
-
readonly childConversationId: string;
|
|
601
|
-
readonly childSubmissionId: string;
|
|
602
|
-
readonly childReceiptId: string;
|
|
603
|
-
readonly childRunId: string;
|
|
604
|
-
} | {
|
|
605
597
|
readonly _tag: "ToolCallPrepared";
|
|
606
598
|
readonly runId: string;
|
|
607
599
|
readonly turnId: string;
|
|
@@ -637,6 +629,14 @@ declare const encodeHostResponse: (input: AbortRecorded | ApprovalRecorded | Hos
|
|
|
637
629
|
readonly submissionId: string;
|
|
638
630
|
readonly author: string;
|
|
639
631
|
readonly reason: string;
|
|
632
|
+
} | {
|
|
633
|
+
readonly _tag: "SubagentStarted";
|
|
634
|
+
readonly runId: string;
|
|
635
|
+
readonly toolCallId: string;
|
|
636
|
+
readonly childConversationId: string;
|
|
637
|
+
readonly childSubmissionId: string;
|
|
638
|
+
readonly childReceiptId: string;
|
|
639
|
+
readonly childRunId: string;
|
|
640
640
|
} | {
|
|
641
641
|
readonly _tag: "ConversationCreated";
|
|
642
642
|
readonly agentId: string;
|
|
@@ -717,6 +717,8 @@ declare const encodeHostResponse: (input: AbortRecorded | ApprovalRecorded | Hos
|
|
|
717
717
|
readonly runId?: string | undefined;
|
|
718
718
|
readonly result?: Schema.Json | undefined;
|
|
719
719
|
readonly finishReason?: "budget-exhausted" | undefined;
|
|
720
|
+
readonly exhausted?: "tokens" | "tool-calls" | "turns" | undefined;
|
|
721
|
+
readonly policyLimit?: "cost" | "duration" | "repeated-failures" | "tokens" | "tool-calls" | "turns" | "usage" | undefined;
|
|
720
722
|
} | {
|
|
721
723
|
readonly _tag: "SubagentRequested";
|
|
722
724
|
readonly runId: string;
|
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { Clock, Context, Duration, Effect, Fiber, Layer, Option, PubSub, Random, Ref, Schema, Stream } from "effect";
|
|
1
|
+
import { Cause, Clock, Context, Duration, Effect, Exit, Fiber, Layer, Option, PubSub, Random, Ref, Schema, Stream } from "effect";
|
|
2
2
|
import { AbortCommand, AbortIntent, AdmissionConflict, AgentBindingResolver, AppendConflict, ApprovalConflict, ApprovalDecisionCommand, ApprovalDecisionIntent, CanonicalRecordEnvelope, CanonicalSequence, ConversationNotMaterialized, ConversationRead, ConversationStore, ConversationStoreError, DEFAULT_OWNERSHIP_LEASE_DURATION, DefinitionDigests, DeploymentId, DigestError, DurableAgentRuntime, DurableRuntimeConfig, DurableRuntimeFailpoint, DurableRuntimeFailpointError, FenceRejected, IdempotencyKey, IntegrityReport, JoinedToHost, LedgerError, ObligationReport, ObligationThresholds, OperationAuthorizationRequest, OperationAuthorizer, OperationDenied, OwnershipLost, PersistedJson, Principal, ProducerId, Receipt, RecoveryExplanation, RecoveryReport, RetryCommand, RetryRefused, RunJournalError, Settlement, SettlementConflict, SubmissionLedger, SubmissionLookupByKey, ToolReconciler, UnknownResolutionCommand, UnknownResolutionConflict, UnknownResolutionIntent, WakeScheduler } from "@effect-agent/session";
|
|
3
3
|
import { ConversationPortTransport, DEFAULT_MAX_STORED_VALUE_BYTES, conversationStoreLayer, handleEncodedPortRequest, portTransportFailure, routedConversationStoreLayer, routedSubmissionLedgerLayer, storageConfigLayer, storageFailpointLayer, submissionLedgerLayer } from "@effect-agent/storage-cloudflare";
|
|
4
4
|
import { AgentId, AgentInputError, ConversationId, SubmissionId } from "@effect-agent/core";
|
|
5
5
|
import { BrowserCrypto } from "@effect/platform-browser";
|
|
6
6
|
import { SqliteClient } from "@effect/sql-sqlite-do";
|
|
7
7
|
import { DurableObject, DurableObjectState, WorkerEnvironment } from "effect-cf";
|
|
8
|
-
import { CodeExecutionHost, CodeExecutionProtocolError, CodeExecutionResourceUse, CodeExecutionResult, CodeExecutionTimeoutError, CodeExecutor, CodeExecutorStartError, CodeExecutorTerminatedError, CodeExecutorUnsupportedError, CodeHostCall, CodeHostCallLimitError, CodeOutputLimitError, CodeProgramFailedError, CodeSourceError, SandboxImplementation } from "@effect-agent/sandbox";
|
|
8
|
+
import { CodeExecutionHost, CodeExecutionProtocolError, CodeExecutionResourceUse, CodeExecutionResult, CodeExecutionTimeoutError, CodeExecutor, CodeExecutorStartError, CodeExecutorTerminatedError, CodeExecutorUnsupportedError, CodeHostCall, CodeHostCallLimitError, CodeHostCallResult, CodeOutputLimitError, CodeProgramFailedError, CodeSourceError, SandboxImplementation } from "@effect-agent/sandbox";
|
|
9
9
|
import { WorkerEntrypoint } from "cloudflare:workers";
|
|
10
10
|
//#region src/bindings.ts
|
|
11
11
|
/**
|
|
@@ -1348,19 +1348,25 @@ const decodeHostCall = (value) => {
|
|
|
1348
1348
|
return Option.none();
|
|
1349
1349
|
}
|
|
1350
1350
|
};
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
const
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1351
|
+
const decodeHostCallResult = (value) => {
|
|
1352
|
+
try {
|
|
1353
|
+
return Schema.decodeUnknownOption(CodeHostCallResult)(value);
|
|
1354
|
+
} catch {
|
|
1355
|
+
return Option.none();
|
|
1356
|
+
}
|
|
1357
|
+
};
|
|
1358
|
+
const encodeHostResultPayload = (outcome) => {
|
|
1359
|
+
try {
|
|
1360
|
+
const payload = outcome._tag === "CodeHostCallSuccess" ? outcome.value : outcome.error;
|
|
1361
|
+
const encodedPayload = JSON.stringify(payload);
|
|
1362
|
+
if (encodedPayload === void 0) return void 0;
|
|
1363
|
+
return {
|
|
1364
|
+
encodedPayload,
|
|
1365
|
+
resultBytes: utf8ByteLength(encodedPayload)
|
|
1366
|
+
};
|
|
1367
|
+
} catch {
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1364
1370
|
};
|
|
1365
1371
|
const utf8ByteLength = (value) => {
|
|
1366
1372
|
let total = 0;
|
|
@@ -1370,14 +1376,6 @@ const utf8ByteLength = (value) => {
|
|
|
1370
1376
|
}
|
|
1371
1377
|
return total;
|
|
1372
1378
|
};
|
|
1373
|
-
const encodedJsonByteLength = (value) => {
|
|
1374
|
-
try {
|
|
1375
|
-
const encoded = JSON.stringify(value);
|
|
1376
|
-
return encoded === void 0 ? void 0 : utf8ByteLength(encoded);
|
|
1377
|
-
} catch {
|
|
1378
|
-
return;
|
|
1379
|
-
}
|
|
1380
|
-
};
|
|
1381
1379
|
/** Reserved global names the harness owns inside the dynamic worker. */
|
|
1382
1380
|
const reservedHarnessGlobals = /* @__PURE__ */ new Set(["console"]);
|
|
1383
1381
|
const passCounterState = { next: 0 };
|
|
@@ -1401,67 +1399,149 @@ const makeExecute = (options) => Effect.fn("DynamicWorkerCodeExecutor.execute")(
|
|
|
1401
1399
|
const host = yield* CodeExecutionHost;
|
|
1402
1400
|
passCounterState.next += 1;
|
|
1403
1401
|
const passId = `code-mode-pass-${passCounterState.next}-${crypto.randomUUID()}`;
|
|
1404
|
-
const
|
|
1405
|
-
|
|
1402
|
+
const startedAt = performance.now();
|
|
1403
|
+
const passDeadline = startedAt + Duration.toMillis(request.limits.maxWallTime);
|
|
1404
|
+
const remainingPassWallTime = () => Duration.millis(Math.max(0, passDeadline - performance.now()));
|
|
1406
1405
|
let issuedHostCalls = 0;
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
resolve,
|
|
1416
|
-
reject
|
|
1417
|
-
});
|
|
1418
|
-
wake?.();
|
|
1419
|
-
});
|
|
1420
|
-
yield* Effect.acquireRelease(Effect.sync(() => {
|
|
1421
|
-
passRegistry.set(passId, { dispatch });
|
|
1422
|
-
}), () => Effect.sync(() => {
|
|
1423
|
-
passRegistry.delete(passId);
|
|
1424
|
-
}));
|
|
1425
|
-
const nextPending = Effect.suspend(() => {
|
|
1426
|
-
const item = pending.shift();
|
|
1427
|
-
if (item !== void 0) return Effect.succeed(item);
|
|
1428
|
-
return Effect.callback((resume) => {
|
|
1429
|
-
wake = () => {
|
|
1430
|
-
wake = void 0;
|
|
1431
|
-
const next = pending.shift();
|
|
1432
|
-
if (next !== void 0) resume(Effect.succeed(next));
|
|
1433
|
-
};
|
|
1434
|
-
});
|
|
1406
|
+
let passOpen = true;
|
|
1407
|
+
const queuedHostCalls = [];
|
|
1408
|
+
let activeHostCall;
|
|
1409
|
+
let hostDispatchFailure;
|
|
1410
|
+
let propagatedHostDispatchFailure;
|
|
1411
|
+
let signalHostDispatchFailure = (_failure) => void 0;
|
|
1412
|
+
const hostDispatchFailureSignal = new Promise((resolve) => {
|
|
1413
|
+
signalHostDispatchFailure = resolve;
|
|
1435
1414
|
});
|
|
1436
|
-
const
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1415
|
+
const rejectQueuedHostCalls = (reason) => {
|
|
1416
|
+
for (const queued of queuedHostCalls.splice(0)) queued.reject(reason);
|
|
1417
|
+
};
|
|
1418
|
+
const recordHostDispatchFailure = (failure) => {
|
|
1419
|
+
if (hostDispatchFailure !== void 0) return;
|
|
1420
|
+
hostDispatchFailure = failure;
|
|
1421
|
+
signalHostDispatchFailure(failure);
|
|
1422
|
+
rejectQueuedHostCalls(/* @__PURE__ */ new Error("Code Mode pass failed"));
|
|
1423
|
+
};
|
|
1424
|
+
const failHostDispatch = (failure) => {
|
|
1425
|
+
switch (failure._tag) {
|
|
1426
|
+
case "host-call-limit": return Effect.fail(CodeHostCallLimitError.make({
|
|
1442
1427
|
implementation: dynamicWorkerImplementation,
|
|
1443
1428
|
limit: request.limits.maxHostCalls,
|
|
1444
1429
|
logs: []
|
|
1445
|
-
});
|
|
1446
|
-
|
|
1430
|
+
}));
|
|
1431
|
+
case "host-call-result-limit": return Effect.fail(CodeOutputLimitError.make({
|
|
1432
|
+
implementation: dynamicWorkerImplementation,
|
|
1433
|
+
surface: "host-call-result",
|
|
1434
|
+
limit: request.limits.maxHostCallResultBytes,
|
|
1435
|
+
observed: failure.observed,
|
|
1436
|
+
logs: []
|
|
1437
|
+
}));
|
|
1438
|
+
case "host-call-protocol": return Effect.fail(CodeExecutionProtocolError.make({
|
|
1439
|
+
implementation: dynamicWorkerImplementation,
|
|
1440
|
+
message: "The execution host returned a value outside the CodeHostCallResult schema"
|
|
1441
|
+
}));
|
|
1442
|
+
case "wall-clock-timeout": return Effect.fail(CodeExecutionTimeoutError.make({
|
|
1443
|
+
implementation: dynamicWorkerImplementation,
|
|
1444
|
+
kind: "wall-clock",
|
|
1445
|
+
maxWallTime: request.limits.maxWallTime,
|
|
1446
|
+
logs: []
|
|
1447
|
+
}));
|
|
1448
|
+
case "host-call-defect": return Effect.failCause(failure.cause);
|
|
1449
|
+
}
|
|
1450
|
+
};
|
|
1451
|
+
const propagateHostDispatchFailure = (failure) => Effect.gen(function* () {
|
|
1452
|
+
propagatedHostDispatchFailure = failure;
|
|
1453
|
+
return yield* failHostDispatch(failure);
|
|
1454
|
+
});
|
|
1455
|
+
const startNextHostCall = () => {
|
|
1456
|
+
if (!passOpen || hostDispatchFailure !== void 0 || activeHostCall !== void 0) return;
|
|
1457
|
+
const queued = queuedHostCalls.shift();
|
|
1458
|
+
if (queued === void 0) return;
|
|
1459
|
+
const fiber = Effect.runFork(Effect.yieldNow.pipe(Effect.andThen(host.call(queued.call)), Effect.timeoutOrElse({
|
|
1460
|
+
duration: remainingPassWallTime(),
|
|
1461
|
+
orElse: () => Effect.sync(() => {
|
|
1462
|
+
recordHostDispatchFailure({ _tag: "wall-clock-timeout" });
|
|
1463
|
+
throw new Error("code-mode host call exceeded the pass wall-clock limit");
|
|
1464
|
+
})
|
|
1465
|
+
}), Effect.map((outcome) => {
|
|
1466
|
+
const decoded = decodeHostCallResult(outcome);
|
|
1447
1467
|
if (Option.isNone(decoded)) {
|
|
1448
|
-
|
|
1449
|
-
|
|
1468
|
+
recordHostDispatchFailure({ _tag: "host-call-protocol" });
|
|
1469
|
+
throw new Error("host-call protocol violation");
|
|
1450
1470
|
}
|
|
1451
|
-
const
|
|
1452
|
-
if (
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
surface: "host-call-result",
|
|
1457
|
-
limit: request.limits.maxHostCallResultBytes,
|
|
1458
|
-
observed: bytes ?? 0,
|
|
1459
|
-
logs: []
|
|
1471
|
+
const encoded = encodeHostResultPayload(decoded.value);
|
|
1472
|
+
if (encoded === void 0 || encoded.resultBytes > request.limits.maxHostCallResultBytes) {
|
|
1473
|
+
recordHostDispatchFailure({
|
|
1474
|
+
_tag: "host-call-result-limit",
|
|
1475
|
+
observed: encoded?.resultBytes ?? 0
|
|
1460
1476
|
});
|
|
1477
|
+
throw new Error("host-call result limit exceeded");
|
|
1461
1478
|
}
|
|
1462
|
-
|
|
1479
|
+
const normalizedPayload = JSON.parse(encoded.encodedPayload);
|
|
1480
|
+
return decoded.value._tag === "CodeHostCallSuccess" ? {
|
|
1481
|
+
_tag: "CodeHostCallSuccess",
|
|
1482
|
+
value: normalizedPayload
|
|
1483
|
+
} : {
|
|
1484
|
+
_tag: "CodeHostCallFailure",
|
|
1485
|
+
error: normalizedPayload
|
|
1486
|
+
};
|
|
1487
|
+
})));
|
|
1488
|
+
activeHostCall = {
|
|
1489
|
+
fiber,
|
|
1490
|
+
settlement: Effect.runPromise(Fiber.await(fiber)).then((exit) => {
|
|
1491
|
+
if (Exit.isSuccess(exit)) {
|
|
1492
|
+
queued.resolve(exit.value);
|
|
1493
|
+
return;
|
|
1494
|
+
}
|
|
1495
|
+
if (!Cause.hasInterruptsOnly(exit.cause)) recordHostDispatchFailure({
|
|
1496
|
+
_tag: "host-call-defect",
|
|
1497
|
+
cause: exit.cause
|
|
1498
|
+
});
|
|
1499
|
+
queued.reject(/* @__PURE__ */ new Error("Code Mode host call failed"));
|
|
1500
|
+
}).finally(() => {
|
|
1501
|
+
if (activeHostCall?.fiber === fiber) activeHostCall = void 0;
|
|
1502
|
+
startNextHostCall();
|
|
1503
|
+
})
|
|
1504
|
+
};
|
|
1505
|
+
};
|
|
1506
|
+
const dispatch = (hostCall) => {
|
|
1507
|
+
if (!passOpen) return Promise.reject(/* @__PURE__ */ new Error("Code Mode pass is closing"));
|
|
1508
|
+
issuedHostCalls += 1;
|
|
1509
|
+
if (issuedHostCalls > request.limits.maxHostCalls) {
|
|
1510
|
+
recordHostDispatchFailure({ _tag: "host-call-limit" });
|
|
1511
|
+
return Promise.reject(/* @__PURE__ */ new Error("host-call limit exceeded"));
|
|
1512
|
+
}
|
|
1513
|
+
const decoded = decodeHostCall(hostCall);
|
|
1514
|
+
if (Option.isNone(decoded)) return Promise.reject(/* @__PURE__ */ new TypeError("host calls must match the CodeHostCall schema"));
|
|
1515
|
+
return new Promise((resolve, reject) => {
|
|
1516
|
+
queuedHostCalls.push({
|
|
1517
|
+
call: decoded.value,
|
|
1518
|
+
resolve,
|
|
1519
|
+
reject
|
|
1520
|
+
});
|
|
1521
|
+
startNextHostCall();
|
|
1522
|
+
});
|
|
1523
|
+
};
|
|
1524
|
+
const closeHostDispatch = Effect.gen(function* () {
|
|
1525
|
+
passOpen = false;
|
|
1526
|
+
passRegistry.delete(passId);
|
|
1527
|
+
rejectQueuedHostCalls(/* @__PURE__ */ new Error("Code Mode pass is closing"));
|
|
1528
|
+
const active = activeHostCall;
|
|
1529
|
+
if (active !== void 0) {
|
|
1530
|
+
yield* Fiber.interrupt(active.fiber);
|
|
1531
|
+
yield* Effect.promise(() => active.settlement);
|
|
1532
|
+
if (activeHostCall === active) activeHostCall = void 0;
|
|
1463
1533
|
}
|
|
1534
|
+
return hostDispatchFailure;
|
|
1464
1535
|
});
|
|
1536
|
+
const stopHostDispatch = closeHostDispatch.pipe(Effect.flatMap((failure) => failure !== void 0 && propagatedHostDispatchFailure !== failure ? propagateHostDispatchFailure(failure) : Effect.void));
|
|
1537
|
+
const releaseHostDispatch = closeHostDispatch.pipe(Effect.flatMap((failure) => {
|
|
1538
|
+
if (failure?._tag !== "host-call-defect" || propagatedHostDispatchFailure === failure) return Effect.void;
|
|
1539
|
+
propagatedHostDispatchFailure = failure;
|
|
1540
|
+
return Effect.failCause(failure.cause);
|
|
1541
|
+
}));
|
|
1542
|
+
yield* Effect.acquireRelease(Effect.sync(() => {
|
|
1543
|
+
passRegistry.set(passId, { dispatch });
|
|
1544
|
+
}), () => releaseHostDispatch);
|
|
1465
1545
|
const workerCode = {
|
|
1466
1546
|
compatibilityDate: options.compatibilityDate ?? "2025-05-01",
|
|
1467
1547
|
allowExperimental: true,
|
|
@@ -1492,7 +1572,6 @@ const makeExecute = (options) => Effect.fn("DynamicWorkerCodeExecutor.execute")(
|
|
|
1492
1572
|
subRequests: request.limits.maxHostCalls + 8
|
|
1493
1573
|
} }
|
|
1494
1574
|
};
|
|
1495
|
-
const startedAt = yield* Clock.currentTimeMillis;
|
|
1496
1575
|
const worker = yield* Effect.acquireRelease(Effect.try({
|
|
1497
1576
|
try: () => options.loader.load(workerCode),
|
|
1498
1577
|
catch: (cause) => {
|
|
@@ -1511,23 +1590,24 @@ const makeExecute = (options) => Effect.fn("DynamicWorkerCodeExecutor.execute")(
|
|
|
1511
1590
|
}), (stub) => Effect.sync(() => {
|
|
1512
1591
|
stub[Symbol.dispose]?.();
|
|
1513
1592
|
}));
|
|
1514
|
-
const server = yield* serveHostCalls.pipe(Effect.forkScoped);
|
|
1515
1593
|
const rpc = Effect.tryPromise({
|
|
1516
1594
|
try: async () => {
|
|
1517
1595
|
return await worker.getEntrypoint().run();
|
|
1518
1596
|
},
|
|
1519
1597
|
catch: (cause) => classifyWorkerFailure(cause, request.limits.maxWallTime)
|
|
1520
1598
|
});
|
|
1521
|
-
const raw = yield* Effect.raceFirst(rpc
|
|
1522
|
-
duration:
|
|
1599
|
+
const raw = yield* Effect.raceFirst(rpc.pipe(Effect.timeoutOrElse({
|
|
1600
|
+
duration: remainingPassWallTime(),
|
|
1523
1601
|
orElse: () => CodeExecutionTimeoutError.make({
|
|
1524
1602
|
implementation: dynamicWorkerImplementation,
|
|
1525
1603
|
kind: "wall-clock",
|
|
1526
1604
|
maxWallTime: request.limits.maxWallTime,
|
|
1527
1605
|
logs: []
|
|
1528
1606
|
})
|
|
1529
|
-
}), Effect.
|
|
1530
|
-
|
|
1607
|
+
})), Effect.promise(() => hostDispatchFailureSignal).pipe(Effect.flatMap(propagateHostDispatchFailure)));
|
|
1608
|
+
yield* stopHostDispatch;
|
|
1609
|
+
const finishedAt = performance.now();
|
|
1610
|
+
if (hostDispatchFailure !== void 0) return yield* propagateHostDispatchFailure(hostDispatchFailure);
|
|
1531
1611
|
const outcome = decodeHarnessOutcome(raw);
|
|
1532
1612
|
if (Option.isNone(outcome)) return yield* CodeExecutionProtocolError.make({
|
|
1533
1613
|
implementation: dynamicWorkerImplementation,
|