@effect-agent/testing 0.0.1-beta.4 → 0.0.1-beta.6
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 +46 -1
- package/dist/index.mjs +579 -18
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -12
- package/src/certification.ts +1 -1
- package/src/chaos.ts +2 -2
- package/src/code-executor-conformance.d.ts +30 -0
- package/src/code-executor-conformance.ts +500 -0
- package/src/code-executor-substitute.ts +488 -0
- package/src/fixtures/docs-researcher/definition.ts +2 -2
- package/src/fixtures/travel-planner/definition.ts +4 -4
- package/src/fixtures/travel-planner/deterministic-layers.ts +1 -1
- package/src/fixtures/travel-planner/phase2.ts +1 -1
- package/src/fixtures/travel-planner/phase3.ts +1 -1
- package/src/fixtures/travel-planner/phase4.ts +1 -1
- package/src/fixtures/travel-planner/phase5.ts +1 -1
- package/src/fixtures/travel-planner/subagents.ts +2 -2
- package/src/fixtures/warehouse/index.ts +412 -0
- package/src/index.ts +6 -0
package/dist/index.mjs
CHANGED
|
@@ -2,9 +2,10 @@ import { McpConnectionRequest, McpConnector, McpServerIdentity, McpToolkitMismat
|
|
|
2
2
|
import { Agent, AgentId, AgentPolicy, ConversationId, IdGenerator, RunId, SubmissionId, ToolCallId, TurnId } from "@effect-agent/core";
|
|
3
3
|
import { DurableStep, DurableStepError, ToolExecutionClass } from "@effect-agent/engine";
|
|
4
4
|
import { AbortCommand, AgentBindingResolver, ApprovalDecisionCommand, BatchId, CanonicalBatch, CanonicalRecordEnvelope, CertificationReport, CertificationSweepResult, CertificationTierThreeReport, CertifiedAdapterIdentity, ConversationCheckpoint, ConversationExportRequest, ConversationProjection, ConversationStore, DEFAULT_OWNERSHIP_LEASE_DURATION, DefinitionDigests, DeploymentId, Digest, DurableAgentRuntime, DurableRuntimeConfig, DurableRuntimeFailpoint, DurableRuntimeFailpointError, DurableRuntimeFailpointLocation, DurableRuntimeFailpointTestControl, DurableWorkerBinding, IdempotencyKey, LoadCheckpointRequest, ObligationThresholds, PersistedJson, Principal, ProducerId, ReconciliationCompleted, ReconciliationSafeToRetry, ReconciliationUncertain, RecordEnvelope, RecordId, ResolutionAbortSubmission, ResolutionCompletedWithResult, ResolutionNeverHappened, ResolutionSafeToRetry, SubmissionLedger, SubmissionLookupById, ToolReconciler, ToolReconcilerError, UnknownResolutionCommand, WakeScheduler, certifyPorts, childConversationIdFor, verifyConversationInvariants } from "@effect-agent/session";
|
|
5
|
-
import { Cause, Clock, Context, DateTime, Deferred, Duration, Effect, Exit, Layer, Option, Ref, Schema, Stream } from "effect";
|
|
5
|
+
import { Cause, Clock, Context, DateTime, Deferred, Duration, Effect, Exit, Fiber, Layer, Option, Queue, Ref, Schema, Stream } from "effect";
|
|
6
6
|
import { FastCheck, TestClock } from "effect/testing";
|
|
7
7
|
import { AiError, LanguageModel, Model, Response, Tool, Toolkit } from "effect/unstable/ai";
|
|
8
|
+
import { CodeExecutionHost, CodeExecutionLimits, CodeExecutionNamespace, CodeExecutionProtocolError, CodeExecutionRequest, CodeExecutionResourceUse, CodeExecutionResult, CodeExecutionTimeoutError, CodeExecutor, CodeExecutorUnsupportedError, CodeHostCall, CodeHostCallFailure, CodeHostCallLimitError, CodeHostCallResult, CodeHostCallSuccess, CodeOutputLimitError, CodeProgramFailedError, CodeSourceError, NetworkAllowlist, NetworkDisabled, SandboxImplementation } from "@effect-agent/sandbox";
|
|
8
9
|
import * as McpSchema from "effect/unstable/ai/McpSchema";
|
|
9
10
|
//#region src/certification.ts
|
|
10
11
|
/** The six Tier-2 scenario shapes in sweep order. */
|
|
@@ -180,7 +181,7 @@ const childDefinition$1 = Agent.define("certify-child", {
|
|
|
180
181
|
toolConcurrency: 1
|
|
181
182
|
})
|
|
182
183
|
});
|
|
183
|
-
var CertifyDelegationFailed = class extends Schema.
|
|
184
|
+
var CertifyDelegationFailed = class extends Schema.TaggedError()("CertifyDelegationFailed", { childErrorTag: Schema.String }) {};
|
|
184
185
|
const researchDelegation = Subagent.define("delegate_research", {
|
|
185
186
|
description: "Research one bounded question and return findings.",
|
|
186
187
|
target: childDefinition$1,
|
|
@@ -627,7 +628,7 @@ var ChaosPlanReport = class extends Schema.Class("@effect-agent/testing/ChaosPla
|
|
|
627
628
|
openObligations: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))
|
|
628
629
|
}) {};
|
|
629
630
|
/** Typed convergence/verification failure of one chaos plan (never a bare defect). */
|
|
630
|
-
var ChaosConvergenceFailure = class extends Schema.
|
|
631
|
+
var ChaosConvergenceFailure = class extends Schema.TaggedError()("ChaosConvergenceFailure", {
|
|
631
632
|
seed: Schema.Int,
|
|
632
633
|
message: Schema.String.check(Schema.isMaxLength(16384))
|
|
633
634
|
}) {};
|
|
@@ -822,7 +823,7 @@ const childDefinition = Agent.define("chaos-child", {
|
|
|
822
823
|
toolConcurrency: 1
|
|
823
824
|
})
|
|
824
825
|
});
|
|
825
|
-
var ChaosDelegationFailed = class extends Schema.
|
|
826
|
+
var ChaosDelegationFailed = class extends Schema.TaggedError()("ChaosDelegationFailed", { childErrorTag: Schema.String }) {};
|
|
826
827
|
const chaosDelegation = Subagent.define("delegate_chaos", {
|
|
827
828
|
description: "Delegate one bounded chaos question.",
|
|
828
829
|
target: childDefinition,
|
|
@@ -1260,6 +1261,566 @@ const runChaosPlan = Effect.fn("Chaos.runChaosPlan")(function* (plan, options) {
|
|
|
1260
1261
|
});
|
|
1261
1262
|
});
|
|
1262
1263
|
//#endregion
|
|
1264
|
+
//#region src/code-executor-conformance.ts
|
|
1265
|
+
/**
|
|
1266
|
+
* Shared `CodeExecutor` conformance (TEST-015). Every adapter — the
|
|
1267
|
+
* deterministic `unisolated` substitute and each isolated adapter — runs
|
|
1268
|
+
* `codeExecutorConformanceCases` verbatim. Enforcement cases that only genuine
|
|
1269
|
+
* isolation can prove (ambient network denial, synchronous CPU runaway
|
|
1270
|
+
* termination) are NOT here; they belong to isolated adapters only
|
|
1271
|
+
* (testing spec §8.1).
|
|
1272
|
+
*
|
|
1273
|
+
* Cases assume the live `Clock` (the wall-clock case uses a short real
|
|
1274
|
+
* deadline) and take one fresh executor pass per case, so a suite may share
|
|
1275
|
+
* one executor Layer across cases.
|
|
1276
|
+
*/
|
|
1277
|
+
var CodeExecutorConformanceViolation = class extends Schema.TaggedError()("CodeExecutorConformanceViolation", {
|
|
1278
|
+
caseName: Schema.String,
|
|
1279
|
+
message: Schema.String
|
|
1280
|
+
}) {};
|
|
1281
|
+
const baseLimits = CodeExecutionLimits.make({
|
|
1282
|
+
maxSourceBytes: 64 * 1024,
|
|
1283
|
+
maxWallTime: Duration.seconds(10),
|
|
1284
|
+
maxLogBytes: 16 * 1024,
|
|
1285
|
+
maxResultBytes: 64 * 1024,
|
|
1286
|
+
maxHostCalls: 8,
|
|
1287
|
+
maxHostCallArgumentBytes: 16 * 1024,
|
|
1288
|
+
maxHostCallResultBytes: 32 * 1024
|
|
1289
|
+
});
|
|
1290
|
+
const warehouseNamespace = CodeExecutionNamespace.make({
|
|
1291
|
+
name: "warehouse",
|
|
1292
|
+
methods: ["query", "count"]
|
|
1293
|
+
});
|
|
1294
|
+
const makeRequest = (source, overrides) => CodeExecutionRequest.make({
|
|
1295
|
+
language: "javascript",
|
|
1296
|
+
source,
|
|
1297
|
+
namespaces: overrides?.namespaces ?? [],
|
|
1298
|
+
network: overrides?.network ?? NetworkDisabled.make(),
|
|
1299
|
+
limits: overrides?.limits ?? baseLimits
|
|
1300
|
+
});
|
|
1301
|
+
const unusedHost = { call: () => Effect.die(/* @__PURE__ */ new Error("this conformance case expected no host call to reach the CodeExecutionHost")) };
|
|
1302
|
+
const respondingHost = (respond) => {
|
|
1303
|
+
const calls = [];
|
|
1304
|
+
return {
|
|
1305
|
+
calls,
|
|
1306
|
+
host: { call: (call) => Effect.sync(() => {
|
|
1307
|
+
calls.push(call);
|
|
1308
|
+
return respond(call);
|
|
1309
|
+
}) }
|
|
1310
|
+
};
|
|
1311
|
+
};
|
|
1312
|
+
const runPass = (request, host) => Effect.gen(function* () {
|
|
1313
|
+
return yield* (yield* CodeExecutor).execute(request).pipe(Effect.provideService(CodeExecutionHost, CodeExecutionHost.of(host)));
|
|
1314
|
+
}).pipe(Effect.scoped);
|
|
1315
|
+
const violation = (caseName, message) => CodeExecutorConformanceViolation.make({
|
|
1316
|
+
caseName,
|
|
1317
|
+
message
|
|
1318
|
+
});
|
|
1319
|
+
const preview = (value) => {
|
|
1320
|
+
try {
|
|
1321
|
+
return JSON.stringify(value)?.slice(0, 200) ?? String(value).slice(0, 200);
|
|
1322
|
+
} catch {
|
|
1323
|
+
return String(value).slice(0, 200);
|
|
1324
|
+
}
|
|
1325
|
+
};
|
|
1326
|
+
const expectSuccess = (caseName, request, host, check) => runPass(request, host).pipe(Effect.mapError((error) => violation(caseName, `expected success, got ${error._tag}: ${preview(error)}`)), Effect.flatMap((result) => {
|
|
1327
|
+
const complaint = check(result);
|
|
1328
|
+
return complaint === void 0 ? Effect.void : Effect.fail(violation(caseName, complaint));
|
|
1329
|
+
}));
|
|
1330
|
+
const expectFailure = (caseName, request, host, tag, check) => runPass(request, host).pipe(Effect.flip, Effect.mapError((result) => violation(caseName, `expected ${tag}, but the pass succeeded with ${preview(result.value)}`)), Effect.flatMap((error) => {
|
|
1331
|
+
if (error._tag !== tag) return Effect.fail(violation(caseName, `expected ${tag}, got ${error._tag}: ${preview(error)}`));
|
|
1332
|
+
const complaint = check?.(error);
|
|
1333
|
+
return complaint === void 0 ? Effect.void : Effect.fail(violation(caseName, complaint));
|
|
1334
|
+
}));
|
|
1335
|
+
const codeExecutorConformanceCases = (options) => {
|
|
1336
|
+
const posture = options.implementation;
|
|
1337
|
+
return [
|
|
1338
|
+
{
|
|
1339
|
+
name: "TEST-015 executes bounded JSON computation and returns the program value",
|
|
1340
|
+
run: expectSuccess("TEST-015 executes bounded JSON computation and returns the program value", makeRequest("async () => { const xs = [1, 2, 3].map((n) => n * 2); return { xs, sum: xs.reduce((a, b) => a + b, 0) }; }"), unusedHost, (result) => JSON.stringify(result.value) === JSON.stringify({
|
|
1341
|
+
xs: [
|
|
1342
|
+
2,
|
|
1343
|
+
4,
|
|
1344
|
+
6
|
|
1345
|
+
],
|
|
1346
|
+
sum: 12
|
|
1347
|
+
}) ? void 0 : `unexpected program value ${preview(result.value)}`)
|
|
1348
|
+
},
|
|
1349
|
+
{
|
|
1350
|
+
name: "CAP-015 reports its isolation posture honestly in results and errors",
|
|
1351
|
+
run: Effect.gen(function* () {
|
|
1352
|
+
const caseName = "CAP-015 reports its isolation posture honestly in results and errors";
|
|
1353
|
+
const result = yield* runPass(makeRequest("async () => 1"), unusedHost).pipe(Effect.mapError((error) => violation(caseName, `expected success, got ${error._tag}`)));
|
|
1354
|
+
if (result.implementation.isolation !== posture.isolation || result.implementation.identity !== posture.identity) return yield* violation(caseName, `result posture ${preview(result.implementation)} does not match the declared ${preview(posture)}`);
|
|
1355
|
+
const error = yield* runPass(makeRequest("async () => {"), unusedHost).pipe(Effect.flip, Effect.mapError(() => violation(caseName, "expected the invalid-source pass to fail")));
|
|
1356
|
+
if (error.implementation === void 0 || error.implementation.isolation !== posture.isolation || error.implementation.identity !== posture.identity) return yield* violation(caseName, `error posture ${preview(error.implementation)} does not match the declared ${preview(posture)}`);
|
|
1357
|
+
})
|
|
1358
|
+
},
|
|
1359
|
+
{
|
|
1360
|
+
name: "TEST-015 routes host calls through the CodeExecutionHost in program order",
|
|
1361
|
+
run: Effect.gen(function* () {
|
|
1362
|
+
const caseName = "TEST-015 routes host calls through the CodeExecutionHost in program order";
|
|
1363
|
+
const { host, calls } = respondingHost((call) => call.method === "query" ? CodeHostCallSuccess.make({ value: { rows: [
|
|
1364
|
+
1,
|
|
1365
|
+
2,
|
|
1366
|
+
3
|
|
1367
|
+
] } }) : CodeHostCallSuccess.make({ value: 3 }));
|
|
1368
|
+
const result = yield* runPass(makeRequest("async () => { const q = await warehouse.query({ sql: 'select' }); const c = await warehouse.count({ table: 't' }); return { rows: q.rows, count: c }; }", { namespaces: [warehouseNamespace] }), host).pipe(Effect.mapError((error) => violation(caseName, `expected success, got ${error._tag}: ${preview(error)}`)));
|
|
1369
|
+
if (JSON.stringify(result.value) !== JSON.stringify({
|
|
1370
|
+
rows: [
|
|
1371
|
+
1,
|
|
1372
|
+
2,
|
|
1373
|
+
3
|
|
1374
|
+
],
|
|
1375
|
+
count: 3
|
|
1376
|
+
})) return yield* violation(caseName, `unexpected value ${preview(result.value)}`);
|
|
1377
|
+
const observed = calls.map((call) => `${call.namespace}.${call.method}`);
|
|
1378
|
+
if (JSON.stringify(observed) !== JSON.stringify(["warehouse.query", "warehouse.count"])) return yield* violation(caseName, `unexpected host call order ${preview(observed)}`);
|
|
1379
|
+
if (result.resourceUse.hostCalls !== 2) return yield* violation(caseName, `expected 2 accounted host calls, got ${result.resourceUse.hostCalls}`);
|
|
1380
|
+
})
|
|
1381
|
+
},
|
|
1382
|
+
{
|
|
1383
|
+
name: "TEST-015 a caught failed host call lets the program branch on the envelope",
|
|
1384
|
+
run: expectSuccess("TEST-015 a caught failed host call lets the program branch on the envelope", makeRequest("async () => { try { await warehouse.query({ sql: 'x' }); return 'unreachable'; } catch (envelope) { return { caught: envelope }; } }", { namespaces: [warehouseNamespace] }), respondingHost(() => CodeHostCallFailure.make({ error: {
|
|
1385
|
+
_tag: "ToolInputError",
|
|
1386
|
+
message: "bad input"
|
|
1387
|
+
} })).host, (result) => JSON.stringify(result.value) === JSON.stringify({ caught: {
|
|
1388
|
+
_tag: "ToolInputError",
|
|
1389
|
+
message: "bad input"
|
|
1390
|
+
} }) ? void 0 : `the envelope did not round-trip: ${preview(result.value)}`)
|
|
1391
|
+
},
|
|
1392
|
+
{
|
|
1393
|
+
name: "TEST-015 an uncaught failed host call fails the program with the envelope",
|
|
1394
|
+
run: expectFailure("TEST-015 an uncaught failed host call fails the program with the envelope", makeRequest("async () => warehouse.query({ sql: 'x' })", { namespaces: [warehouseNamespace] }), respondingHost(() => CodeHostCallFailure.make({ error: {
|
|
1395
|
+
_tag: "PolicyDenied",
|
|
1396
|
+
message: "denied"
|
|
1397
|
+
} })).host, "CodeProgramFailedError", (error) => error._tag === "CodeProgramFailedError" && error.reason === "rejected" && JSON.stringify(error.thrown) === JSON.stringify({
|
|
1398
|
+
_tag: "PolicyDenied",
|
|
1399
|
+
message: "denied"
|
|
1400
|
+
}) ? void 0 : `unexpected failure detail ${preview(error)}`)
|
|
1401
|
+
},
|
|
1402
|
+
{
|
|
1403
|
+
name: "TEST-015 fails typed on syntactically invalid source",
|
|
1404
|
+
run: expectFailure("TEST-015 fails typed on syntactically invalid source", makeRequest("async () => {"), unusedHost, "CodeSourceError", (error) => error._tag === "CodeSourceError" && error.reason === "invalid" ? void 0 : `expected reason invalid, got ${preview(error)}`)
|
|
1405
|
+
},
|
|
1406
|
+
{
|
|
1407
|
+
name: "TEST-015 fails typed when the expression is not one async function",
|
|
1408
|
+
run: expectFailure("TEST-015 fails typed when the expression is not one async function", makeRequest("1 + 1"), unusedHost, "CodeSourceError", (error) => error._tag === "CodeSourceError" && error.reason === "not-a-function" ? void 0 : `expected reason not-a-function, got ${preview(error)}`)
|
|
1409
|
+
},
|
|
1410
|
+
{
|
|
1411
|
+
name: "TEST-015 fails typed on source larger than the declared byte limit",
|
|
1412
|
+
run: expectFailure("TEST-015 fails typed on source larger than the declared byte limit", makeRequest(`async () => "${"x".repeat(2e3)}"`, { limits: CodeExecutionLimits.make({
|
|
1413
|
+
...baseLimits,
|
|
1414
|
+
maxSourceBytes: 256
|
|
1415
|
+
}) }), unusedHost, "CodeSourceError", (error) => error._tag === "CodeSourceError" && error.reason === "oversized" ? void 0 : `expected reason oversized, got ${preview(error)}`)
|
|
1416
|
+
},
|
|
1417
|
+
{
|
|
1418
|
+
name: "TEST-015 terminates a never-settling program at the wall-clock deadline",
|
|
1419
|
+
run: expectFailure("TEST-015 terminates a never-settling program at the wall-clock deadline", makeRequest("async () => { await new Promise(() => {}); return 1; }", { limits: CodeExecutionLimits.make({
|
|
1420
|
+
...baseLimits,
|
|
1421
|
+
maxWallTime: Duration.millis(250)
|
|
1422
|
+
}) }), unusedHost, "CodeExecutionTimeoutError")
|
|
1423
|
+
},
|
|
1424
|
+
{
|
|
1425
|
+
name: "TEST-015 fails typed when console output exceeds its byte budget",
|
|
1426
|
+
run: expectFailure("TEST-015 fails typed when console output exceeds its byte budget", makeRequest("async () => { for (let i = 0; i < 64; i += 1) { console.log('x'.repeat(256)); } return 1; }", { limits: CodeExecutionLimits.make({
|
|
1427
|
+
...baseLimits,
|
|
1428
|
+
maxLogBytes: 2048
|
|
1429
|
+
}) }), unusedHost, "CodeOutputLimitError", (error) => error._tag === "CodeOutputLimitError" && error.surface === "logs" ? void 0 : `expected surface logs, got ${preview(error)}`)
|
|
1430
|
+
},
|
|
1431
|
+
{
|
|
1432
|
+
name: "TEST-015 fails typed when the final result exceeds its byte budget",
|
|
1433
|
+
run: expectFailure("TEST-015 fails typed when the final result exceeds its byte budget", makeRequest("async () => 'y'.repeat(4096)", { limits: CodeExecutionLimits.make({
|
|
1434
|
+
...baseLimits,
|
|
1435
|
+
maxResultBytes: 1024
|
|
1436
|
+
}) }), unusedHost, "CodeOutputLimitError", (error) => error._tag === "CodeOutputLimitError" && error.surface === "result" ? void 0 : `expected surface result, got ${preview(error)}`)
|
|
1437
|
+
},
|
|
1438
|
+
{
|
|
1439
|
+
name: "TEST-015 fails typed when host calls exceed the executor cap",
|
|
1440
|
+
run: Effect.gen(function* () {
|
|
1441
|
+
const caseName = "TEST-015 fails typed when host calls exceed the executor cap";
|
|
1442
|
+
const { host, calls } = respondingHost(() => CodeHostCallSuccess.make({ value: null }));
|
|
1443
|
+
yield* expectFailure(caseName, makeRequest("async () => { await warehouse.query({}); await warehouse.query({}); await warehouse.query({}); return 1; }", {
|
|
1444
|
+
namespaces: [warehouseNamespace],
|
|
1445
|
+
limits: CodeExecutionLimits.make({
|
|
1446
|
+
...baseLimits,
|
|
1447
|
+
maxHostCalls: 2
|
|
1448
|
+
})
|
|
1449
|
+
}), host, "CodeHostCallLimitError");
|
|
1450
|
+
if (calls.length !== 2) return yield* violation(caseName, `expected exactly 2 dispatched host calls under a cap of 2, observed ${calls.length}`);
|
|
1451
|
+
})
|
|
1452
|
+
},
|
|
1453
|
+
{
|
|
1454
|
+
name: "TEST-015 fails typed on a host outcome outside the protocol schema",
|
|
1455
|
+
run: expectFailure("TEST-015 fails typed on a host outcome outside the protocol schema", makeRequest("async () => warehouse.query({})", { namespaces: [warehouseNamespace] }), { call: () => Effect.succeed({ bogus: true }) }, "CodeExecutionProtocolError")
|
|
1456
|
+
},
|
|
1457
|
+
{
|
|
1458
|
+
name: "TEST-015 surfaces an uncaught program throw with its bounded log capture",
|
|
1459
|
+
run: expectFailure("TEST-015 surfaces an uncaught program throw with its bounded log capture", makeRequest("async () => { console.log('before the failure'); throw new Error('deliberate'); }"), unusedHost, "CodeProgramFailedError", (error) => error._tag === "CodeProgramFailedError" && error.reason === "threw" && error.logs.some((line) => line.includes("before the failure")) ? void 0 : `expected a threw failure carrying the log capture, got ${preview(error)}`)
|
|
1460
|
+
},
|
|
1461
|
+
{
|
|
1462
|
+
name: "TEST-015 fails typed when the program returns a non-JSON value",
|
|
1463
|
+
run: expectFailure("TEST-015 fails typed when the program returns a non-JSON value", makeRequest("async () => (() => 1)"), unusedHost, "CodeProgramFailedError", (error) => error._tag === "CodeProgramFailedError" && error.reason === "non-json-result" ? void 0 : `expected reason non-json-result, got ${preview(error)}`)
|
|
1464
|
+
},
|
|
1465
|
+
{
|
|
1466
|
+
name: "CAP-015 rejects a network allowlist it cannot enforce with a typed unsupported error",
|
|
1467
|
+
run: expectFailure("CAP-015 rejects a network allowlist it cannot enforce with a typed unsupported error", makeRequest("async () => 1", { network: NetworkAllowlist.make({
|
|
1468
|
+
domains: ["example.com"],
|
|
1469
|
+
ports: [443]
|
|
1470
|
+
}) }), unusedHost, "CodeExecutorUnsupportedError", (error) => error._tag === "CodeExecutorUnsupportedError" && error.feature === "network" ? void 0 : `expected feature network, got ${preview(error)}`)
|
|
1471
|
+
},
|
|
1472
|
+
{
|
|
1473
|
+
name: "TEST-015 interruption reaches in-flight host calls and pass teardown",
|
|
1474
|
+
run: Effect.gen(function* () {
|
|
1475
|
+
const caseName = "TEST-015 interruption reaches in-flight host calls and pass teardown";
|
|
1476
|
+
const started = yield* Deferred.make();
|
|
1477
|
+
const witness = { hostCallInterrupted: false };
|
|
1478
|
+
const fiber = yield* runPass(makeRequest("async () => warehouse.query({})", { namespaces: [warehouseNamespace] }), { call: () => Deferred.succeed(started, void 0).pipe(Effect.andThen(Effect.never), Effect.ensuring(Effect.sync(() => {
|
|
1479
|
+
witness.hostCallInterrupted = true;
|
|
1480
|
+
}))) }).pipe(Effect.forkChild);
|
|
1481
|
+
if ((yield* Effect.raceFirst(Deferred.await(started).pipe(Effect.as("started")), Fiber.join(fiber).pipe(Effect.exit, Effect.as("exited")))) === "exited") return yield* violation(caseName, "the pass settled before any host call reached the CodeExecutionHost");
|
|
1482
|
+
yield* Fiber.interrupt(fiber);
|
|
1483
|
+
if (!witness.hostCallInterrupted) return yield* violation(caseName, "interrupting the pass did not interrupt the in-flight host call");
|
|
1484
|
+
})
|
|
1485
|
+
}
|
|
1486
|
+
];
|
|
1487
|
+
};
|
|
1488
|
+
//#endregion
|
|
1489
|
+
//#region src/code-executor-substitute.ts
|
|
1490
|
+
/**
|
|
1491
|
+
* The deterministic in-process executor substitute (C1 of ADR-0017). It runs
|
|
1492
|
+
* the generated program on the host JavaScript engine with best-effort global
|
|
1493
|
+
* shadowing only, so it self-identifies as `unisolated` and is never a
|
|
1494
|
+
* security boundary (CAP-010, CAP-015). It exists to prove the public
|
|
1495
|
+
* `CodeExecutor` contract and to drive deterministic capability tests.
|
|
1496
|
+
*/
|
|
1497
|
+
const inProcessCodeExecutorImplementation = SandboxImplementation.make({
|
|
1498
|
+
isolation: "unisolated",
|
|
1499
|
+
identity: "in-process-javascript"
|
|
1500
|
+
});
|
|
1501
|
+
const MAX_LOG_LINES = 4096;
|
|
1502
|
+
const MAX_LOG_LINE_CHARACTERS = 16e3;
|
|
1503
|
+
const MAX_THROWN_CHARACTERS = 4e3;
|
|
1504
|
+
const utf8ByteLength = (value) => new TextEncoder().encode(value).byteLength;
|
|
1505
|
+
/**
|
|
1506
|
+
* Ambient globals shadowed inside the harness. Shadowing blocks the obvious
|
|
1507
|
+
* identifier paths only; a determined program can still escape, which is
|
|
1508
|
+
* exactly why this executor reports `unisolated` and the isolated network and
|
|
1509
|
+
* CPU enforcement conformance cases run only against isolated adapters.
|
|
1510
|
+
*/
|
|
1511
|
+
const shadowedGlobals = [
|
|
1512
|
+
"fetch",
|
|
1513
|
+
"process",
|
|
1514
|
+
"require",
|
|
1515
|
+
"module",
|
|
1516
|
+
"exports",
|
|
1517
|
+
"global",
|
|
1518
|
+
"globalThis",
|
|
1519
|
+
"XMLHttpRequest",
|
|
1520
|
+
"WebSocket",
|
|
1521
|
+
"Deno",
|
|
1522
|
+
"Bun"
|
|
1523
|
+
];
|
|
1524
|
+
var LogLimitSignal = class {
|
|
1525
|
+
observed;
|
|
1526
|
+
constructor(observed) {
|
|
1527
|
+
this.observed = observed;
|
|
1528
|
+
}
|
|
1529
|
+
};
|
|
1530
|
+
var EvaluationThrew = class {
|
|
1531
|
+
inner;
|
|
1532
|
+
constructor(inner) {
|
|
1533
|
+
this.inner = inner;
|
|
1534
|
+
}
|
|
1535
|
+
};
|
|
1536
|
+
var NotAFunction = class {
|
|
1537
|
+
actual;
|
|
1538
|
+
constructor(actual) {
|
|
1539
|
+
this.actual = actual;
|
|
1540
|
+
}
|
|
1541
|
+
};
|
|
1542
|
+
/**
|
|
1543
|
+
* Total, defect-free rendering of untrusted values: a hostile Proxy can throw
|
|
1544
|
+
* from property access, `toString`, and `Symbol.toPrimitive`, and an expected
|
|
1545
|
+
* program failure must never escape the typed channel as a defect while its
|
|
1546
|
+
* diagnostics are being serialized.
|
|
1547
|
+
*/
|
|
1548
|
+
const formatLogValue = (value) => {
|
|
1549
|
+
try {
|
|
1550
|
+
if (typeof value === "string") return value;
|
|
1551
|
+
return JSON.stringify(value) ?? String(value);
|
|
1552
|
+
} catch {
|
|
1553
|
+
try {
|
|
1554
|
+
return String(value);
|
|
1555
|
+
} catch {
|
|
1556
|
+
return "[unprintable value]";
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
};
|
|
1560
|
+
const makeConsole = (capture, limits) => {
|
|
1561
|
+
const write = (...values) => {
|
|
1562
|
+
const joined = values.map(formatLogValue).join(" ");
|
|
1563
|
+
const line = joined.length > MAX_LOG_LINE_CHARACTERS ? `${joined.slice(0, MAX_LOG_LINE_CHARACTERS - 1)}…` : joined;
|
|
1564
|
+
const bytes = utf8ByteLength(line);
|
|
1565
|
+
if (capture.lines.length >= MAX_LOG_LINES || capture.bytes + bytes > limits.maxLogBytes) throw new LogLimitSignal(capture.bytes + bytes);
|
|
1566
|
+
capture.lines.push(line);
|
|
1567
|
+
capture.bytes += bytes;
|
|
1568
|
+
};
|
|
1569
|
+
return {
|
|
1570
|
+
debug: write,
|
|
1571
|
+
error: write,
|
|
1572
|
+
info: write,
|
|
1573
|
+
log: write,
|
|
1574
|
+
warn: write
|
|
1575
|
+
};
|
|
1576
|
+
};
|
|
1577
|
+
const buildNamespaceObject = (namespace, offer) => {
|
|
1578
|
+
const methods = {};
|
|
1579
|
+
for (const method of namespace.methods) methods[method] = (argument) => new Promise((resolve, reject) => {
|
|
1580
|
+
offer({
|
|
1581
|
+
namespace: namespace.name,
|
|
1582
|
+
method,
|
|
1583
|
+
argument,
|
|
1584
|
+
resolve,
|
|
1585
|
+
reject
|
|
1586
|
+
});
|
|
1587
|
+
});
|
|
1588
|
+
return methods;
|
|
1589
|
+
};
|
|
1590
|
+
const boundedText = (value) => {
|
|
1591
|
+
try {
|
|
1592
|
+
return (value instanceof Error ? `${value.name}: ${value.message}` : formatLogValue(value)).slice(0, MAX_THROWN_CHARACTERS);
|
|
1593
|
+
} catch {
|
|
1594
|
+
return "[unserializable thrown value]";
|
|
1595
|
+
}
|
|
1596
|
+
};
|
|
1597
|
+
/** Schema decoding of hostile values may itself throw through trap getters. */
|
|
1598
|
+
const safeDecodeJson = (value) => {
|
|
1599
|
+
try {
|
|
1600
|
+
return Schema.decodeUnknownOption(Schema.Json)(value);
|
|
1601
|
+
} catch {
|
|
1602
|
+
return Option.none();
|
|
1603
|
+
}
|
|
1604
|
+
};
|
|
1605
|
+
const boundedThrown = (value) => {
|
|
1606
|
+
const decoded = safeDecodeJson(value);
|
|
1607
|
+
if (Option.isSome(decoded)) try {
|
|
1608
|
+
const encoded = JSON.stringify(decoded.value);
|
|
1609
|
+
if (encoded !== void 0 && encoded.length <= MAX_THROWN_CHARACTERS) return decoded.value;
|
|
1610
|
+
} catch {}
|
|
1611
|
+
return boundedText(value);
|
|
1612
|
+
};
|
|
1613
|
+
const encodedJsonByteLength = (value) => {
|
|
1614
|
+
try {
|
|
1615
|
+
const encoded = JSON.stringify(value);
|
|
1616
|
+
return encoded === void 0 ? void 0 : utf8ByteLength(encoded);
|
|
1617
|
+
} catch {
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
};
|
|
1621
|
+
/** Host outcomes are protocol input; a hostile value must not defect mid-decode. */
|
|
1622
|
+
const decodeHostOutcome = (value) => {
|
|
1623
|
+
try {
|
|
1624
|
+
return Schema.decodeUnknownOption(CodeHostCallResult)(value);
|
|
1625
|
+
} catch {
|
|
1626
|
+
return Option.none();
|
|
1627
|
+
}
|
|
1628
|
+
};
|
|
1629
|
+
const validateRequest = (request) => Effect.gen(function* () {
|
|
1630
|
+
if (request.network._tag !== "NetworkDisabled") return yield* CodeExecutorUnsupportedError.make({
|
|
1631
|
+
implementation: inProcessCodeExecutorImplementation,
|
|
1632
|
+
feature: "network",
|
|
1633
|
+
message: "The unisolated in-process executor cannot enforce an egress allowlist; only NetworkDisabled is accepted, and even that is shadowed rather than enforced"
|
|
1634
|
+
});
|
|
1635
|
+
if (request.limits.cpuMillis !== void 0) return yield* CodeExecutorUnsupportedError.make({
|
|
1636
|
+
implementation: inProcessCodeExecutorImplementation,
|
|
1637
|
+
feature: "cpu-limit",
|
|
1638
|
+
message: "The unisolated in-process executor shares the host engine and cannot enforce a CPU limit"
|
|
1639
|
+
});
|
|
1640
|
+
const reservedNames = /* @__PURE__ */ new Set([...shadowedGlobals, "console"]);
|
|
1641
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1642
|
+
for (const namespace of request.namespaces) {
|
|
1643
|
+
if (reservedNames.has(namespace.name) || seen.has(namespace.name)) return yield* CodeExecutorUnsupportedError.make({
|
|
1644
|
+
implementation: inProcessCodeExecutorImplementation,
|
|
1645
|
+
feature: "namespaces",
|
|
1646
|
+
message: `Namespace ${namespace.name} collides with a harness binding or another namespace`
|
|
1647
|
+
});
|
|
1648
|
+
seen.add(namespace.name);
|
|
1649
|
+
}
|
|
1650
|
+
const sourceBytes = utf8ByteLength(request.source);
|
|
1651
|
+
if (sourceBytes > request.limits.maxSourceBytes) return yield* CodeSourceError.make({
|
|
1652
|
+
implementation: inProcessCodeExecutorImplementation,
|
|
1653
|
+
reason: "oversized",
|
|
1654
|
+
message: `Source is ${sourceBytes} bytes; the request allows ${request.limits.maxSourceBytes}`
|
|
1655
|
+
});
|
|
1656
|
+
});
|
|
1657
|
+
const serveHostCalls = (host, queue, limits, capture, counter) => Effect.gen(function* () {
|
|
1658
|
+
while (true) {
|
|
1659
|
+
const pending = yield* Queue.take(queue);
|
|
1660
|
+
counter.calls += 1;
|
|
1661
|
+
if (counter.calls > limits.maxHostCalls) return yield* CodeHostCallLimitError.make({
|
|
1662
|
+
implementation: inProcessCodeExecutorImplementation,
|
|
1663
|
+
limit: limits.maxHostCalls,
|
|
1664
|
+
logs: [...capture.lines]
|
|
1665
|
+
});
|
|
1666
|
+
const argument = safeDecodeJson(pending.argument);
|
|
1667
|
+
if (Option.isNone(argument)) {
|
|
1668
|
+
pending.reject(/* @__PURE__ */ new TypeError("host call arguments must be JSON values"));
|
|
1669
|
+
continue;
|
|
1670
|
+
}
|
|
1671
|
+
const argumentBytes = encodedJsonByteLength(argument.value);
|
|
1672
|
+
if (argumentBytes === void 0 || argumentBytes > limits.maxHostCallArgumentBytes) return yield* CodeOutputLimitError.make({
|
|
1673
|
+
implementation: inProcessCodeExecutorImplementation,
|
|
1674
|
+
surface: "host-call-argument",
|
|
1675
|
+
limit: limits.maxHostCallArgumentBytes,
|
|
1676
|
+
observed: argumentBytes ?? 0,
|
|
1677
|
+
logs: [...capture.lines]
|
|
1678
|
+
});
|
|
1679
|
+
const rawOutcome = yield* host.call(CodeHostCall.make({
|
|
1680
|
+
namespace: pending.namespace,
|
|
1681
|
+
method: pending.method,
|
|
1682
|
+
argument: argument.value
|
|
1683
|
+
}));
|
|
1684
|
+
const outcome = decodeHostOutcome(rawOutcome);
|
|
1685
|
+
if (Option.isNone(outcome)) return yield* CodeExecutionProtocolError.make({
|
|
1686
|
+
implementation: inProcessCodeExecutorImplementation,
|
|
1687
|
+
message: "The execution host returned a value outside the CodeHostCallResult schema"
|
|
1688
|
+
});
|
|
1689
|
+
if (outcome.value._tag === "CodeHostCallFailure") {
|
|
1690
|
+
pending.reject(outcome.value.error);
|
|
1691
|
+
continue;
|
|
1692
|
+
}
|
|
1693
|
+
const resultBytes = encodedJsonByteLength(outcome.value.value);
|
|
1694
|
+
if (resultBytes === void 0 || resultBytes > limits.maxHostCallResultBytes) return yield* CodeOutputLimitError.make({
|
|
1695
|
+
implementation: inProcessCodeExecutorImplementation,
|
|
1696
|
+
surface: "host-call-result",
|
|
1697
|
+
limit: limits.maxHostCallResultBytes,
|
|
1698
|
+
observed: resultBytes ?? 0,
|
|
1699
|
+
logs: [...capture.lines]
|
|
1700
|
+
});
|
|
1701
|
+
pending.resolve(outcome.value.value);
|
|
1702
|
+
}
|
|
1703
|
+
});
|
|
1704
|
+
const classifyProgramFailure = (thrown, limits, capture) => {
|
|
1705
|
+
const inner = thrown instanceof EvaluationThrew ? thrown.inner : thrown;
|
|
1706
|
+
if (inner instanceof LogLimitSignal) return CodeOutputLimitError.make({
|
|
1707
|
+
implementation: inProcessCodeExecutorImplementation,
|
|
1708
|
+
surface: "logs",
|
|
1709
|
+
limit: limits.maxLogBytes,
|
|
1710
|
+
observed: inner.observed,
|
|
1711
|
+
logs: [...capture.lines]
|
|
1712
|
+
});
|
|
1713
|
+
if (inner instanceof NotAFunction) return CodeSourceError.make({
|
|
1714
|
+
implementation: inProcessCodeExecutorImplementation,
|
|
1715
|
+
reason: "not-a-function",
|
|
1716
|
+
message: `The source expression evaluated to ${inner.actual}; it must evaluate to one async function`
|
|
1717
|
+
});
|
|
1718
|
+
const reason = thrown instanceof EvaluationThrew || inner instanceof Error ? "threw" : "rejected";
|
|
1719
|
+
return CodeProgramFailedError.make({
|
|
1720
|
+
implementation: inProcessCodeExecutorImplementation,
|
|
1721
|
+
reason,
|
|
1722
|
+
thrown: boundedThrown(inner),
|
|
1723
|
+
message: boundedText(inner),
|
|
1724
|
+
logs: [...capture.lines]
|
|
1725
|
+
});
|
|
1726
|
+
};
|
|
1727
|
+
const executeInProcess = Effect.fn("InProcessCodeExecutor.execute")(function* (request) {
|
|
1728
|
+
yield* validateRequest(request);
|
|
1729
|
+
const host = yield* CodeExecutionHost;
|
|
1730
|
+
const capture = {
|
|
1731
|
+
lines: [],
|
|
1732
|
+
bytes: 0
|
|
1733
|
+
};
|
|
1734
|
+
const counter = { calls: 0 };
|
|
1735
|
+
const queue = yield* Queue.unbounded();
|
|
1736
|
+
const factory = yield* Effect.try({
|
|
1737
|
+
try: () => new Function(...shadowedGlobals, "console", ...request.namespaces.map((namespace) => namespace.name), `"use strict";\nreturn (\n${request.source}\n);`),
|
|
1738
|
+
catch: (cause) => CodeSourceError.make({
|
|
1739
|
+
implementation: inProcessCodeExecutorImplementation,
|
|
1740
|
+
reason: "invalid",
|
|
1741
|
+
message: boundedText(cause)
|
|
1742
|
+
})
|
|
1743
|
+
});
|
|
1744
|
+
const harnessConsole = makeConsole(capture, request.limits);
|
|
1745
|
+
let issuedHostCalls = 0;
|
|
1746
|
+
const namespaceObjects = request.namespaces.map((namespace) => buildNamespaceObject(namespace, (pending) => {
|
|
1747
|
+
issuedHostCalls += 1;
|
|
1748
|
+
if (issuedHostCalls > request.limits.maxHostCalls + 1) {
|
|
1749
|
+
pending.reject(/* @__PURE__ */ new Error(`host-call limit of ${request.limits.maxHostCalls} exceeded`));
|
|
1750
|
+
return;
|
|
1751
|
+
}
|
|
1752
|
+
Queue.offerUnsafe(queue, pending);
|
|
1753
|
+
}));
|
|
1754
|
+
const server = yield* serveHostCalls(host, queue, request.limits, capture, counter).pipe(Effect.forkScoped);
|
|
1755
|
+
const program = Effect.tryPromise({
|
|
1756
|
+
try: async () => {
|
|
1757
|
+
let candidate;
|
|
1758
|
+
try {
|
|
1759
|
+
candidate = factory(...shadowedGlobals.map(() => void 0), harnessConsole, ...namespaceObjects);
|
|
1760
|
+
} catch (cause) {
|
|
1761
|
+
throw new EvaluationThrew(cause);
|
|
1762
|
+
}
|
|
1763
|
+
if (typeof candidate !== "function") throw new EvaluationThrew(new NotAFunction(typeof candidate));
|
|
1764
|
+
let outcome;
|
|
1765
|
+
try {
|
|
1766
|
+
outcome = candidate();
|
|
1767
|
+
} catch (cause) {
|
|
1768
|
+
throw new EvaluationThrew(cause);
|
|
1769
|
+
}
|
|
1770
|
+
return await Promise.resolve(outcome);
|
|
1771
|
+
},
|
|
1772
|
+
catch: (thrown) => classifyProgramFailure(thrown, request.limits, capture)
|
|
1773
|
+
});
|
|
1774
|
+
const startedAt = yield* Clock.currentTimeMillis;
|
|
1775
|
+
const returned = yield* Effect.raceFirst(program, Fiber.join(server)).pipe(Effect.timeoutOrElse({
|
|
1776
|
+
duration: request.limits.maxWallTime,
|
|
1777
|
+
orElse: () => CodeExecutionTimeoutError.make({
|
|
1778
|
+
implementation: inProcessCodeExecutorImplementation,
|
|
1779
|
+
kind: "wall-clock",
|
|
1780
|
+
maxWallTime: request.limits.maxWallTime,
|
|
1781
|
+
logs: [...capture.lines]
|
|
1782
|
+
})
|
|
1783
|
+
}), Effect.ensuring(Fiber.interrupt(server)));
|
|
1784
|
+
const finishedAt = yield* Clock.currentTimeMillis;
|
|
1785
|
+
if (issuedHostCalls > request.limits.maxHostCalls) return yield* CodeHostCallLimitError.make({
|
|
1786
|
+
implementation: inProcessCodeExecutorImplementation,
|
|
1787
|
+
limit: request.limits.maxHostCalls,
|
|
1788
|
+
logs: [...capture.lines]
|
|
1789
|
+
});
|
|
1790
|
+
const value = yield* Schema.decodeUnknownEffect(Schema.Json)(returned).pipe(Effect.mapError(() => CodeProgramFailedError.make({
|
|
1791
|
+
implementation: inProcessCodeExecutorImplementation,
|
|
1792
|
+
reason: "non-json-result",
|
|
1793
|
+
thrown: null,
|
|
1794
|
+
message: "The program must return a JSON value",
|
|
1795
|
+
logs: [...capture.lines]
|
|
1796
|
+
})));
|
|
1797
|
+
const resultBytes = encodedJsonByteLength(value);
|
|
1798
|
+
if (resultBytes === void 0 || resultBytes > request.limits.maxResultBytes) return yield* CodeOutputLimitError.make({
|
|
1799
|
+
implementation: inProcessCodeExecutorImplementation,
|
|
1800
|
+
surface: "result",
|
|
1801
|
+
limit: request.limits.maxResultBytes,
|
|
1802
|
+
observed: resultBytes ?? 0,
|
|
1803
|
+
logs: [...capture.lines]
|
|
1804
|
+
});
|
|
1805
|
+
return CodeExecutionResult.make({
|
|
1806
|
+
implementation: inProcessCodeExecutorImplementation,
|
|
1807
|
+
value,
|
|
1808
|
+
logs: [...capture.lines],
|
|
1809
|
+
resourceUse: CodeExecutionResourceUse.make({
|
|
1810
|
+
wallTime: Duration.millis(Math.max(0, finishedAt - startedAt)),
|
|
1811
|
+
hostCalls: counter.calls,
|
|
1812
|
+
logBytes: capture.bytes,
|
|
1813
|
+
resultBytes
|
|
1814
|
+
})
|
|
1815
|
+
});
|
|
1816
|
+
});
|
|
1817
|
+
/**
|
|
1818
|
+
* Layer providing the unisolated in-process `CodeExecutor` substitute. The
|
|
1819
|
+
* per-pass `CodeExecutionHost` stays in the caller's requirement channel, the
|
|
1820
|
+
* same as every real adapter.
|
|
1821
|
+
*/
|
|
1822
|
+
const inProcessCodeExecutorLayer = Layer.succeed(CodeExecutor)(CodeExecutor.of({ execute: executeInProcess }));
|
|
1823
|
+
//#endregion
|
|
1263
1824
|
//#region src/fixtures/docs-researcher/definition.ts
|
|
1264
1825
|
const ResearchDocumentId = Schema.NonEmptyString.check(Schema.isMaxLength(64)).pipe(Schema.brand("@effect-agent/testing/docs-researcher/ResearchDocumentId"));
|
|
1265
1826
|
const BoundedTitle = Schema.NonEmptyString.check(Schema.isMaxLength(120));
|
|
@@ -1273,7 +1834,7 @@ var ResearchDocument = class extends Schema.Class("ResearchDocument")({
|
|
|
1273
1834
|
title: BoundedTitle,
|
|
1274
1835
|
body: BoundedBody
|
|
1275
1836
|
}) {};
|
|
1276
|
-
var DocumentUnavailable = class extends Schema.
|
|
1837
|
+
var DocumentUnavailable = class extends Schema.TaggedError()("DocumentUnavailable", {
|
|
1277
1838
|
documentId: ResearchDocumentId,
|
|
1278
1839
|
message: Schema.String
|
|
1279
1840
|
}) {};
|
|
@@ -1372,7 +1933,7 @@ var SummaryFinding = class extends Schema.Class("SummaryFinding")({
|
|
|
1372
1933
|
documentId: ResearchDocumentId,
|
|
1373
1934
|
summary: BoundedSummary
|
|
1374
1935
|
}) {};
|
|
1375
|
-
var DocumentSummaryFailed = class extends Schema.
|
|
1936
|
+
var DocumentSummaryFailed = class extends Schema.TaggedError()("DocumentSummaryFailed", { childErrorTag: Schema.NonEmptyString }) {};
|
|
1376
1937
|
/** Finite per-invocation bounds (SUB-009): one fetch per child, two children per Run. */
|
|
1377
1938
|
const documentSummaryPolicy = SubagentPolicy.make({
|
|
1378
1939
|
maxChildren: 2,
|
|
@@ -1524,10 +2085,10 @@ const unavailableFields = {
|
|
|
1524
2085
|
query: Schema.String,
|
|
1525
2086
|
message: Schema.String
|
|
1526
2087
|
};
|
|
1527
|
-
var FlightUnavailable = class extends Schema.
|
|
1528
|
-
var LodgingUnavailable = class extends Schema.
|
|
1529
|
-
var ActivityUnavailable = class extends Schema.
|
|
1530
|
-
var GuidanceFailure = class extends Schema.
|
|
2088
|
+
var FlightUnavailable = class extends Schema.TaggedError()("FlightUnavailable", unavailableFields) {};
|
|
2089
|
+
var LodgingUnavailable = class extends Schema.TaggedError()("LodgingUnavailable", unavailableFields) {};
|
|
2090
|
+
var ActivityUnavailable = class extends Schema.TaggedError()("ActivityUnavailable", unavailableFields) {};
|
|
2091
|
+
var GuidanceFailure = class extends Schema.TaggedError()("GuidanceFailure", { message: Schema.String }) {};
|
|
1531
2092
|
var FlightCatalog = class extends Context.Service()("@effect-agent/testing/travel-planner/FlightCatalog") {};
|
|
1532
2093
|
var LodgingCatalog = class extends Context.Service()("@effect-agent/testing/travel-planner/LodgingCatalog") {};
|
|
1533
2094
|
var ActivityCatalog = class extends Context.Service()("@effect-agent/testing/travel-planner/ActivityCatalog") {};
|
|
@@ -1679,7 +2240,7 @@ var SupplierBookingRecord = class extends Schema.Class("@effect-agent/testing/tr
|
|
|
1679
2240
|
detail: Schema.NonEmptyString,
|
|
1680
2241
|
status: Schema.Literals(["confirmed", "cancelled"])
|
|
1681
2242
|
}) {};
|
|
1682
|
-
var SupplierUnavailable = class extends Schema.
|
|
2243
|
+
var SupplierUnavailable = class extends Schema.TaggedError()("SupplierUnavailable", { message: Schema.String }) {};
|
|
1683
2244
|
/** The desk-internal idempotency key of one cancellation: cancel is idempotent by bookingRef. */
|
|
1684
2245
|
const cancelBookingIdempotencyKey = (bookingRef) => `cancel-booking:${bookingRef}`;
|
|
1685
2246
|
/** The deterministic bookingRef the desk mints for one idempotency key. */
|
|
@@ -2067,7 +2628,7 @@ var ItineraryHold = class extends Schema.Class("@effect-agent/testing/travel-pla
|
|
|
2067
2628
|
quoteId: QuoteId,
|
|
2068
2629
|
status: Schema.Literal("held")
|
|
2069
2630
|
}) {};
|
|
2070
|
-
var ItineraryHoldUnavailable = class extends Schema.
|
|
2631
|
+
var ItineraryHoldUnavailable = class extends Schema.TaggedError()("ItineraryHoldUnavailable", {
|
|
2071
2632
|
quoteId: QuoteId,
|
|
2072
2633
|
message: Schema.String
|
|
2073
2634
|
}) {};
|
|
@@ -2284,7 +2845,7 @@ const phase3TravelPlannerCompletionBatch = CanonicalBatch.make({
|
|
|
2284
2845
|
const phase3TravelPlannerBatches = [phase3TravelPlannerInitialBatch, phase3TravelPlannerCompletionBatch];
|
|
2285
2846
|
/** Portable current-version fixture; it contains no passenger identity or credentials. */
|
|
2286
2847
|
const phase3TravelPlannerEncodedFixture = Schema.encodeSync(Schema.Array(CanonicalBatch))(phase3TravelPlannerBatches);
|
|
2287
|
-
var TravelPlannerProjectionError = class extends Schema.
|
|
2848
|
+
var TravelPlannerProjectionError = class extends Schema.TaggedError()("TravelPlannerProjectionError", { message: Schema.String }) {};
|
|
2288
2849
|
/** Decode the itinerary projection rebuilt from canonical model-completion records. */
|
|
2289
2850
|
const travelPlanFromProjection = (projection) => {
|
|
2290
2851
|
const output = projection.modelOutputs.at(-1);
|
|
@@ -2578,7 +3139,7 @@ const makePhase4TravelPlannerAgent = (turns = phase1HappyPathTurns) => Agent.wit
|
|
|
2578
3139
|
* so this Layer deliberately provides none.
|
|
2579
3140
|
*/
|
|
2580
3141
|
const phase4TravelPlannerWorkerLayer = Layer.mergeAll(TravelPlannerPhase4ToolkitLayer, FlightCatalogLayer, LodgingCatalogLayer, ActivityCatalogLayer, TravelGuidanceLayer).pipe(Layer.provide(CatalogLifecycle.layerNoDeps));
|
|
2581
|
-
var TravelPlannerDurableEvidenceError = class extends Schema.
|
|
3142
|
+
var TravelPlannerDurableEvidenceError = class extends Schema.TaggedError()("TravelPlannerDurableEvidenceError", { message: Schema.String }) {};
|
|
2582
3143
|
/**
|
|
2583
3144
|
* Decode the completed itinerary from the canonical `SubmissionSettled` record. Canonical history
|
|
2584
3145
|
* is the outcome authority (DUR-015): the settled result — not any ledger cache — must decode
|
|
@@ -2864,7 +3425,7 @@ const TravelPlannerPhase5 = Agent.define("travel-planner-phase-5", {
|
|
|
2864
3425
|
phase: "P5"
|
|
2865
3426
|
}
|
|
2866
3427
|
});
|
|
2867
|
-
var TravelPlannerBookingEvidenceError = class extends Schema.
|
|
3428
|
+
var TravelPlannerBookingEvidenceError = class extends Schema.TaggedError()("TravelPlannerBookingEvidenceError", { message: Schema.String }) {};
|
|
2868
3429
|
const bookingResultRefs = (result) => {
|
|
2869
3430
|
if (typeof result !== "object" || result === null) return [];
|
|
2870
3431
|
const refs = [];
|
|
@@ -2906,7 +3467,7 @@ var DestinationFacts = class extends Schema.Class("DestinationFacts")({
|
|
|
2906
3467
|
highlights: Schema.Array(Schema.String),
|
|
2907
3468
|
advisory: Schema.NonEmptyString
|
|
2908
3469
|
}) {};
|
|
2909
|
-
var DestinationGuideUnavailable = class extends Schema.
|
|
3470
|
+
var DestinationGuideUnavailable = class extends Schema.TaggedError()("DestinationGuideUnavailable", {
|
|
2910
3471
|
destination: AirportCode,
|
|
2911
3472
|
message: Schema.String
|
|
2912
3473
|
}) {};
|
|
@@ -2994,7 +3555,7 @@ var DestinationResearchFindings = class extends Schema.Class("DestinationResearc
|
|
|
2994
3555
|
destination: AirportCode,
|
|
2995
3556
|
summary: Schema.NonEmptyString
|
|
2996
3557
|
}) {};
|
|
2997
|
-
var DestinationResearchFailed = class extends Schema.
|
|
3558
|
+
var DestinationResearchFailed = class extends Schema.TaggedError()("DestinationResearchFailed", { childErrorTag: Schema.NonEmptyString }) {};
|
|
2998
3559
|
/**
|
|
2999
3560
|
* Deterministic delegation-admission choreography seam. `prepareInput` awaits
|
|
3000
3561
|
* this gate before the handler reserves budget or spawns, so tests can order
|
|
@@ -4200,6 +4761,6 @@ const PHASE7_LIVE_CREDENTIAL_ENV = "OPENAI_API_KEY";
|
|
|
4200
4761
|
*/
|
|
4201
4762
|
const phase7LiveProfileEnabled = (env) => env["EFFECT_AGENT_LIVE"] === "1" && (env["OPENAI_API_KEY"] ?? "") !== "";
|
|
4202
4763
|
//#endregion
|
|
4203
|
-
export { ActivityCatalog, ActivityCatalogLayer, ActivityQuery, ActivitySearchResult, ActivityUnavailable, AirportCode, BookFlight, BookItinerary, BookingRef, BoundedSummary, CERTIFICATION_SCENARIOS, CancelBooking, CancelBookingRequest, CancellationConfirmation, CatalogLifecycle, CatalogLifecycleCounts, ChaosApprovalDecision, ChaosConvergenceFailure, ChaosLaneReport, ChaosPlan, ChaosPlanReport, ChaosResolutionKind, ChaosScenarioKind, ChaosSubmissionSpec, DEFAULT_CHAOS_SEED, DestinationBrief, DestinationFacts, DestinationGuide, DestinationGuideLayer, DestinationGuideUnavailable, DestinationQuery, DestinationRecommendation, DestinationReport, DestinationResearchFailed, DestinationResearchFindings, DestinationResearchRequest, DestinationResearchSupportLayer, DestinationResearcher, DestinationResearcherToolkit, DestinationResearcherToolkitLayer, DestinationShortlist, DeterministicIdGeneratorLayer, DocContentToolkit, DocSummarizer, DocsMcpDiscoveryEvidence, DocsResearcher, DocsResearcherToolkit, DocumentLibrary, DocumentQuery, DocumentSummary, DocumentSummaryFailed, DocumentUnavailable, DurableSearchActivities, DurableSearchFlights, DurableSearchLodging, FetchDocument, FlightBookingRequest, FlightCatalog, FlightCatalogLayer, FlightOption, FlightQuery, FlightUnavailable, GuidanceFailure, HoldItinerary, Itinerary, ItineraryBookingRequest, ItineraryConfirmation, ItineraryHold, ItineraryHoldGateway, ItineraryHoldRequest, ItineraryHoldUnavailable, LodgingCatalog, LodgingCatalogLayer, LodgingOption, LodgingQuery, LodgingUnavailable, LookupDestination, PHASE7_LIVE_CREDENTIAL_ENV, PHASE7_LIVE_GATE_ENV, QuoteId, ResearchDigest, ResearchDispatchGate, ResearchDocument, ResearchDocumentId, ResearchMission, ResearchRequest, ReverseCompletionToolkitLayer, ScriptedGeneratePart, ScriptedGenerateTurn, ScriptedModel, ScriptedStreamPart, ScriptedStreamTermination, ScriptedStreamTurn, ScriptedTurn, SearchActivities, SearchFlights, SearchLodging, SummaryBrief, SummaryFinding, SummaryRequest, SupplierBookingConfirmation, SupplierBookingDesk, SupplierBookingRecord, SupplierOperation, SupplierUnavailable, TIER2_UNREACHED_LOCATIONS, TravelBookingReport, TravelCoordinator, TravelCoordinatorToolkit, TravelGuidance, TravelGuidanceLayer, TravelPlan, TravelPlanner, TravelPlannerBookingEvidenceError, TravelPlannerBookingProfile, TravelPlannerCloudflareProfile, TravelPlannerDurabilityProfile, TravelPlannerDurableEvidenceError, TravelPlannerPersistenceProfile, TravelPlannerPhase2, TravelPlannerPhase2Toolkit, TravelPlannerPhase2ToolkitLayer, TravelPlannerPhase4, TravelPlannerPhase4Toolkit, TravelPlannerPhase4ToolkitLayer, TravelPlannerPhase5, TravelPlannerPhase5Toolkit, TravelPlannerPhase5ToolkitLayer, TravelPlannerPhase7Profile, TravelPlannerProjectionError, TravelPlannerRuntimeLayer, TravelPlannerSubagentDurabilityProfile, TravelPlannerToolkit, TravelPlannerToolkitLayer, TravelSupplierReconcilerLayer, TravelerRef, TripRequest, assertDiscoveryMatchesAuthoredToolkit, assertSettledBookingsExistAtSupplier, bookFlightIdempotencyKey, cancelBookingIdempotencyKey, certifyDurableAdapters, chaosSeedFromEnv, coordinatorConfidentialMarker, coordinatorResearchTurn, coordinatorShortlistTurn, delegateDocumentSummary, destinationLookup, destinationReportFor, destinationResearchDelegation, destinationResearchHandlersLayer, destinationResearchPolicy, docContentToolkitLayer, docsCoordinatorConfidentialMarker, docsCoordinatorDigests, docsDocumentBodySecret, docsMcpConnectorLayer, docsMcpIdentity, docsMcpMismatchedConnectorLayer, docsMcpOversizedConnectorLayer, docsMcpRequest, docsMissionConfidentialMarker, docsResearcherDeploymentId, docsResearcherPrincipal, docsResearcherProducerId, docsResearcherSubmitAgent, docsResearcherSubmitOptions, docsSummarizerDigestStrings, docsSummarizerDigests, docsSummaryHandlersLayer, documentBodyPhrase, documentSummaryFor, documentSummaryPolicy, durableChildLookupCallId, durableDestinationResearchHandlersLayer, durableResearchAllocation, durableResearchCallId, durableResearchFinding, durableResearchShortlist, encodedDestinationFacts, encodedDestinationReport, encodedDocumentSummary, expectedDestinationShortlist, expectedResearchDigest, expectedTravelPlan, fetchCallId, generateChaosPlans, itineraryStepIdempotencyKey, makeDestinationResearcherModel, makeDocsResearcherHarness, makeDurableResearchHarness, makeInvocationCountingModel, makePhase3TravelPlannerCheckpoint, makePhase4TravelPlannerAgent, makePhase6TravelPlannerBindings, mapResearchChildFailure, mapSummaryChildFailure, missionConfidentialMarker, normalizeCrossPlatformTravelPlannerEvidence, normalizeDurableTravelPlannerEvidence, phase0HappyPathTurns, phase0Trip, phase1HappyPathTurns, phase1Trip, phase3TravelPlannerBatches, phase3TravelPlannerCompletionBatch, phase3TravelPlannerConversationId, phase3TravelPlannerDefinitionDigests, phase3TravelPlannerEncodedFixture, phase3TravelPlannerInitialBatch, phase3TravelPlannerProducerId, phase3TravelPlannerProfile, phase3TravelPlannerRunId, phase4TravelPlannerDefinitionDigests, phase4TravelPlannerDeploymentId, phase4TravelPlannerPrincipal, phase4TravelPlannerProducerId, phase4TravelPlannerProfile, phase4TravelPlannerSubmitOptions, phase4TravelPlannerWorkerLayer, phase5TravelPlannerDefinitionDigests, phase5TravelPlannerDeploymentId, phase5TravelPlannerPrincipal, phase5TravelPlannerProducerId, phase5TravelPlannerProfile, phase5TravelPlannerSubmitOptions, phase5TravelPlannerWorkerLayer, phase6ActivityCallId, phase6BookingModel, phase6BookingRef, phase6BookingToolCallId, phase6BookingTrip, phase6ChildLookupCallId, phase6CoordinatorModel, phase6FlightCallId, phase6GatedPlannerDefinitionDigests, phase6GatedPlannerModel, phase6GatedTrip, phase6GuideInvocationCount, phase6LodgingCallId, phase6PlannerModel, phase6ResearchDestination, phase6ResearchMission, phase6ResearcherModel, phase6SupplierDesk, phase6SupplierDeskLayer, phase6SupplierReconcilerLayer, phase6TravelPlannerDeploymentId, phase6TravelPlannerGoldenEvidence, phase6TravelPlannerProducerId, phase6TravelPlannerProducerPrefix, phase6TravelPlannerProfile, phase7LiveProfileEnabled, phase7TravelPlannerProfile, redactedDocumentPreview, releasePhase6PlannerGate, releasePhase6ResearcherGate, researchCorpusDocumentIds, researchDocumentFor, researchDocumentLookup, researchMission, researchMissionRequest, researcherHappyPathTurns, resetPhase6PlannerGate, resetPhase6ResearcherGate, resolveTierThree, runChaosPlan, s2CoordinatorDigests, s2CoordinatorSubmitAgent, s2ResearcherDigestStrings, s2ResearcherDigests, s2TravelPlannerDeploymentId, s2TravelPlannerPrincipal, s2TravelPlannerProducerId, s2TravelPlannerProfile, s2TravelPlannerSubmitOptions, summarizeCallId, supplierBookingRefFor, tier2NeverFiredLocations, travelPlanFromDurableSettlement, travelPlanFromProjection };
|
|
4764
|
+
export { ActivityCatalog, ActivityCatalogLayer, ActivityQuery, ActivitySearchResult, ActivityUnavailable, AirportCode, BookFlight, BookItinerary, BookingRef, BoundedSummary, CERTIFICATION_SCENARIOS, CancelBooking, CancelBookingRequest, CancellationConfirmation, CatalogLifecycle, CatalogLifecycleCounts, ChaosApprovalDecision, ChaosConvergenceFailure, ChaosLaneReport, ChaosPlan, ChaosPlanReport, ChaosResolutionKind, ChaosScenarioKind, ChaosSubmissionSpec, CodeExecutorConformanceViolation, DEFAULT_CHAOS_SEED, DestinationBrief, DestinationFacts, DestinationGuide, DestinationGuideLayer, DestinationGuideUnavailable, DestinationQuery, DestinationRecommendation, DestinationReport, DestinationResearchFailed, DestinationResearchFindings, DestinationResearchRequest, DestinationResearchSupportLayer, DestinationResearcher, DestinationResearcherToolkit, DestinationResearcherToolkitLayer, DestinationShortlist, DeterministicIdGeneratorLayer, DocContentToolkit, DocSummarizer, DocsMcpDiscoveryEvidence, DocsResearcher, DocsResearcherToolkit, DocumentLibrary, DocumentQuery, DocumentSummary, DocumentSummaryFailed, DocumentUnavailable, DurableSearchActivities, DurableSearchFlights, DurableSearchLodging, FetchDocument, FlightBookingRequest, FlightCatalog, FlightCatalogLayer, FlightOption, FlightQuery, FlightUnavailable, GuidanceFailure, HoldItinerary, Itinerary, ItineraryBookingRequest, ItineraryConfirmation, ItineraryHold, ItineraryHoldGateway, ItineraryHoldRequest, ItineraryHoldUnavailable, LodgingCatalog, LodgingCatalogLayer, LodgingOption, LodgingQuery, LodgingUnavailable, LookupDestination, PHASE7_LIVE_CREDENTIAL_ENV, PHASE7_LIVE_GATE_ENV, QuoteId, ResearchDigest, ResearchDispatchGate, ResearchDocument, ResearchDocumentId, ResearchMission, ResearchRequest, ReverseCompletionToolkitLayer, ScriptedGeneratePart, ScriptedGenerateTurn, ScriptedModel, ScriptedStreamPart, ScriptedStreamTermination, ScriptedStreamTurn, ScriptedTurn, SearchActivities, SearchFlights, SearchLodging, SummaryBrief, SummaryFinding, SummaryRequest, SupplierBookingConfirmation, SupplierBookingDesk, SupplierBookingRecord, SupplierOperation, SupplierUnavailable, TIER2_UNREACHED_LOCATIONS, TravelBookingReport, TravelCoordinator, TravelCoordinatorToolkit, TravelGuidance, TravelGuidanceLayer, TravelPlan, TravelPlanner, TravelPlannerBookingEvidenceError, TravelPlannerBookingProfile, TravelPlannerCloudflareProfile, TravelPlannerDurabilityProfile, TravelPlannerDurableEvidenceError, TravelPlannerPersistenceProfile, TravelPlannerPhase2, TravelPlannerPhase2Toolkit, TravelPlannerPhase2ToolkitLayer, TravelPlannerPhase4, TravelPlannerPhase4Toolkit, TravelPlannerPhase4ToolkitLayer, TravelPlannerPhase5, TravelPlannerPhase5Toolkit, TravelPlannerPhase5ToolkitLayer, TravelPlannerPhase7Profile, TravelPlannerProjectionError, TravelPlannerRuntimeLayer, TravelPlannerSubagentDurabilityProfile, TravelPlannerToolkit, TravelPlannerToolkitLayer, TravelSupplierReconcilerLayer, TravelerRef, TripRequest, assertDiscoveryMatchesAuthoredToolkit, assertSettledBookingsExistAtSupplier, bookFlightIdempotencyKey, cancelBookingIdempotencyKey, certifyDurableAdapters, chaosSeedFromEnv, codeExecutorConformanceCases, coordinatorConfidentialMarker, coordinatorResearchTurn, coordinatorShortlistTurn, delegateDocumentSummary, destinationLookup, destinationReportFor, destinationResearchDelegation, destinationResearchHandlersLayer, destinationResearchPolicy, docContentToolkitLayer, docsCoordinatorConfidentialMarker, docsCoordinatorDigests, docsDocumentBodySecret, docsMcpConnectorLayer, docsMcpIdentity, docsMcpMismatchedConnectorLayer, docsMcpOversizedConnectorLayer, docsMcpRequest, docsMissionConfidentialMarker, docsResearcherDeploymentId, docsResearcherPrincipal, docsResearcherProducerId, docsResearcherSubmitAgent, docsResearcherSubmitOptions, docsSummarizerDigestStrings, docsSummarizerDigests, docsSummaryHandlersLayer, documentBodyPhrase, documentSummaryFor, documentSummaryPolicy, durableChildLookupCallId, durableDestinationResearchHandlersLayer, durableResearchAllocation, durableResearchCallId, durableResearchFinding, durableResearchShortlist, encodedDestinationFacts, encodedDestinationReport, encodedDocumentSummary, expectedDestinationShortlist, expectedResearchDigest, expectedTravelPlan, fetchCallId, generateChaosPlans, inProcessCodeExecutorImplementation, inProcessCodeExecutorLayer, itineraryStepIdempotencyKey, makeDestinationResearcherModel, makeDocsResearcherHarness, makeDurableResearchHarness, makeInvocationCountingModel, makePhase3TravelPlannerCheckpoint, makePhase4TravelPlannerAgent, makePhase6TravelPlannerBindings, mapResearchChildFailure, mapSummaryChildFailure, missionConfidentialMarker, normalizeCrossPlatformTravelPlannerEvidence, normalizeDurableTravelPlannerEvidence, phase0HappyPathTurns, phase0Trip, phase1HappyPathTurns, phase1Trip, phase3TravelPlannerBatches, phase3TravelPlannerCompletionBatch, phase3TravelPlannerConversationId, phase3TravelPlannerDefinitionDigests, phase3TravelPlannerEncodedFixture, phase3TravelPlannerInitialBatch, phase3TravelPlannerProducerId, phase3TravelPlannerProfile, phase3TravelPlannerRunId, phase4TravelPlannerDefinitionDigests, phase4TravelPlannerDeploymentId, phase4TravelPlannerPrincipal, phase4TravelPlannerProducerId, phase4TravelPlannerProfile, phase4TravelPlannerSubmitOptions, phase4TravelPlannerWorkerLayer, phase5TravelPlannerDefinitionDigests, phase5TravelPlannerDeploymentId, phase5TravelPlannerPrincipal, phase5TravelPlannerProducerId, phase5TravelPlannerProfile, phase5TravelPlannerSubmitOptions, phase5TravelPlannerWorkerLayer, phase6ActivityCallId, phase6BookingModel, phase6BookingRef, phase6BookingToolCallId, phase6BookingTrip, phase6ChildLookupCallId, phase6CoordinatorModel, phase6FlightCallId, phase6GatedPlannerDefinitionDigests, phase6GatedPlannerModel, phase6GatedTrip, phase6GuideInvocationCount, phase6LodgingCallId, phase6PlannerModel, phase6ResearchDestination, phase6ResearchMission, phase6ResearcherModel, phase6SupplierDesk, phase6SupplierDeskLayer, phase6SupplierReconcilerLayer, phase6TravelPlannerDeploymentId, phase6TravelPlannerGoldenEvidence, phase6TravelPlannerProducerId, phase6TravelPlannerProducerPrefix, phase6TravelPlannerProfile, phase7LiveProfileEnabled, phase7TravelPlannerProfile, redactedDocumentPreview, releasePhase6PlannerGate, releasePhase6ResearcherGate, researchCorpusDocumentIds, researchDocumentFor, researchDocumentLookup, researchMission, researchMissionRequest, researcherHappyPathTurns, resetPhase6PlannerGate, resetPhase6ResearcherGate, resolveTierThree, runChaosPlan, s2CoordinatorDigests, s2CoordinatorSubmitAgent, s2ResearcherDigestStrings, s2ResearcherDigests, s2TravelPlannerDeploymentId, s2TravelPlannerPrincipal, s2TravelPlannerProducerId, s2TravelPlannerProfile, s2TravelPlannerSubmitOptions, summarizeCallId, supplierBookingRefFor, tier2NeverFiredLocations, travelPlanFromDurableSettlement, travelPlanFromProjection };
|
|
4204
4765
|
|
|
4205
4766
|
//# sourceMappingURL=index.mjs.map
|