@effect-agent/testing 0.1.0-beta.9 → 0.1.0-beta.90
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/Certification.d.mts +106 -0
- package/dist/Certification.mjs +633 -0
- package/dist/Certification.mjs.map +1 -0
- package/dist/Chaos.d.mts +126 -0
- package/dist/Chaos.mjs +755 -0
- package/dist/Chaos.mjs.map +1 -0
- package/dist/CodeExecutorConformance.d.mts +33 -0
- package/dist/CodeExecutorConformance.mjs +231 -0
- package/dist/CodeExecutorConformance.mjs.map +1 -0
- package/dist/CodeExecutorSubstitute.d.mts +21 -0
- package/dist/CodeExecutorSubstitute.mjs +368 -0
- package/dist/CodeExecutorSubstitute.mjs.map +1 -0
- package/dist/DocsResearcher.d.mts +276 -0
- package/dist/DocsResearcher.mjs +490 -0
- package/dist/DocsResearcher.mjs.map +1 -0
- package/dist/ScriptedModel-DAvxIiud.d.mts +220 -0
- package/dist/ScriptedModel.d.mts +2 -0
- package/dist/ScriptedModel.mjs +155 -0
- package/dist/ScriptedModel.mjs.map +1 -0
- package/dist/TravelPlanner.d.mts +1680 -0
- package/dist/TravelPlanner.mjs +1963 -0
- package/dist/TravelPlanner.mjs.map +1 -0
- package/dist/deterministic-layers-D5owIoke.mjs +358 -0
- package/dist/deterministic-layers-D5owIoke.mjs.map +1 -0
- package/dist/index.d.mts +2 -3408
- package/dist/index.mjs +2 -4771
- package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
- package/package.json +1 -48
- package/src/{certification.ts → Certification.ts} +366 -171
- package/src/{chaos.ts → Chaos.ts} +241 -127
- package/src/{code-executor-conformance.ts → CodeExecutorConformance.ts} +30 -7
- package/src/{code-executor-substitute.ts → CodeExecutorSubstitute.ts} +113 -46
- package/src/{fixtures/docs-researcher/index.ts → DocsResearcher.ts} +68 -5
- package/src/{scripted-model.ts → ScriptedModel.ts} +25 -29
- package/src/TravelPlanner.ts +232 -0
- package/src/fixtures/docs-researcher/definition.ts +20 -11
- package/src/fixtures/docs-researcher/harness.ts +41 -34
- package/src/fixtures/docs-researcher/mcp.ts +49 -3
- package/src/fixtures/travel-planner/definition.ts +15 -2
- package/src/fixtures/travel-planner/deterministic-layers.ts +47 -4
- package/src/fixtures/travel-planner/phase2.ts +4 -3
- package/src/fixtures/travel-planner/phase3.ts +19 -42
- package/src/fixtures/travel-planner/phase4.ts +24 -37
- package/src/fixtures/travel-planner/phase5.ts +35 -42
- package/src/fixtures/travel-planner/phase6.ts +191 -88
- package/src/fixtures/travel-planner/phase7.ts +4 -102
- package/src/fixtures/travel-planner/scenarios.ts +3 -4
- package/src/fixtures/travel-planner/subagents-durable.ts +35 -61
- package/src/fixtures/travel-planner/subagents.ts +36 -14
- package/src/index.ts +1 -11
- package/src/internal/certification-report.ts +25 -0
- package/dist/index.mjs.map +0 -1
- package/src/code-executor-conformance.d.ts +0 -30
- package/src/fixtures/travel-planner/index.ts +0 -11
- package/src/fixtures/warehouse/index.ts +0 -412
|
@@ -1,33 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
delegationAllocationFromPolicy,
|
|
3
|
-
SubagentReservationsMemoryLive,
|
|
4
|
-
SubagentRuntime,
|
|
5
|
-
} from "@effect-agent/capabilities";
|
|
6
|
-
import { Agent, type ConversationId } from "@effect-agent/core";
|
|
7
|
-
import type { RuntimeBinding } from "@effect-agent/engine";
|
|
8
|
-
import {
|
|
9
|
-
DefinitionDigests,
|
|
10
|
-
DeploymentId,
|
|
11
|
-
Digest,
|
|
12
|
-
DurableWorkerBinding,
|
|
13
|
-
Principal,
|
|
14
|
-
ProducerId,
|
|
15
|
-
type DurableSubmitOptions,
|
|
16
|
-
type IdempotencyKey,
|
|
17
|
-
type ResolvedBinding,
|
|
18
|
-
} from "@effect-agent/session";
|
|
19
1
|
import { Effect, Layer, Ref, Schema, Stream } from "effect";
|
|
2
|
+
import * as Agent from "effect-agent/agent";
|
|
3
|
+
import { DurableWorkerBinding, type ResolvedBinding } from "effect-agent/agent-registration";
|
|
4
|
+
import { type RuntimeBinding } from "effect-agent/agent-runtime";
|
|
5
|
+
import { type DurableSubmitOptions } from "effect-agent/durable-agent-runtime";
|
|
6
|
+
import { type ThreadId } from "effect-agent/identifiers";
|
|
7
|
+
import { DefinitionDigests, DeploymentId, Digest, ProducerId } from "effect-agent/records";
|
|
8
|
+
import * as Subagent from "effect-agent/subagent";
|
|
9
|
+
import { delegationAllocationFromPolicy } from "effect-agent/subagent";
|
|
10
|
+
import { SubagentReservationsMemoryLive } from "effect-agent/subagent-reservations";
|
|
11
|
+
import { Principal, type IdempotencyKey } from "effect-agent/submission-ledger";
|
|
20
12
|
import { LanguageModel, Model, type Response, type Toolkit } from "effect/unstable/ai";
|
|
21
13
|
|
|
22
14
|
import { DeterministicIdGeneratorLayer } from "./deterministic-layers.ts";
|
|
23
|
-
import {
|
|
15
|
+
import type {
|
|
24
16
|
DestinationBrief,
|
|
17
|
+
DestinationReport,
|
|
18
|
+
DestinationResearcherToolkit,
|
|
19
|
+
} from "./subagents.ts";
|
|
20
|
+
import {
|
|
25
21
|
DestinationFacts,
|
|
26
22
|
DestinationGuide,
|
|
27
23
|
DestinationRecommendation,
|
|
28
|
-
DestinationReport,
|
|
29
24
|
DestinationResearcher,
|
|
30
|
-
DestinationResearcherToolkit,
|
|
31
25
|
DestinationResearcherToolkitLayer,
|
|
32
26
|
destinationLookup,
|
|
33
27
|
destinationReportFor,
|
|
@@ -40,46 +34,15 @@ import {
|
|
|
40
34
|
TravelCoordinator,
|
|
41
35
|
} from "./subagents.ts";
|
|
42
36
|
|
|
43
|
-
|
|
44
|
-
// S2 durability profile (spec/subagents.md §17): the S1 coordinator →
|
|
45
|
-
// destination-researcher delegation re-run as ACCEPTED WORK on the Node/SQLite
|
|
46
|
-
// runtime. The claim is `DN` durable attached Subagents only: establishment
|
|
47
|
-
// and join are replay-safe by construction (SUB-016/SUB-019), a completed
|
|
48
|
-
// child is never re-executed on a lost join acknowledgment, and NO claim of
|
|
49
|
-
// exactly-once child external effects is made (rule 8; an unresolved ordinary
|
|
50
|
-
// child Tool blocks as an Unknown Outcome instead, SUB-021). Cloudflare
|
|
51
|
-
// equivalence is P6 scope and explicitly not claimed here.
|
|
52
|
-
// ---------------------------------------------------------------------------
|
|
53
|
-
|
|
54
|
-
export class TravelPlannerSubagentDurabilityProfile extends Schema.Class<TravelPlannerSubagentDurabilityProfile>(
|
|
55
|
-
"@effect-agent/testing/travel-planner/TravelPlannerSubagentDurabilityProfile",
|
|
56
|
-
)({
|
|
57
|
-
deploymentClass: Schema.Literal("DN"),
|
|
58
|
-
durableAttachedSubagents: Schema.Literal(true),
|
|
59
|
-
canonicalSchemaVersion: Schema.Literal(1),
|
|
60
|
-
/** Establishment/join replay converges on one child Receipt, Conversation, and join batch. */
|
|
61
|
-
subagentReplaySafe: Schema.Literal(true),
|
|
62
|
-
/** Never claimed (rule 8): child ordinary Tools stop at Unknown Outcomes, they do not replay. */
|
|
63
|
-
childExternalEffectsExactlyOnce: Schema.Literal(false),
|
|
64
|
-
/** The same conformance suite under DO eviction/alarms is P6 scope (spec §17 `DC`). */
|
|
65
|
-
cloudflareEquivalence: Schema.Literal(false),
|
|
66
|
-
}) {}
|
|
67
|
-
|
|
68
|
-
export const s2TravelPlannerProfile = TravelPlannerSubagentDurabilityProfile.make({
|
|
69
|
-
deploymentClass: "DN",
|
|
70
|
-
durableAttachedSubagents: true,
|
|
71
|
-
canonicalSchemaVersion: 1,
|
|
72
|
-
subagentReplaySafe: true,
|
|
73
|
-
childExternalEffectsExactlyOnce: false,
|
|
74
|
-
cloudflareEquivalence: false,
|
|
75
|
-
});
|
|
76
|
-
|
|
37
|
+
/** Registered coordinator and researcher bindings for durable delegation tests. */
|
|
77
38
|
export const s2TravelPlannerDeploymentId = Schema.decodeSync(DeploymentId)(
|
|
78
39
|
"travel-planner-s2-deployment",
|
|
79
40
|
);
|
|
41
|
+
|
|
80
42
|
export const s2TravelPlannerProducerId = Schema.decodeSync(ProducerId)(
|
|
81
43
|
"travel-planner-s2-producer",
|
|
82
44
|
);
|
|
45
|
+
|
|
83
46
|
export const s2TravelPlannerPrincipal = Schema.decodeSync(Principal)("travel-planner-s2-principal");
|
|
84
47
|
|
|
85
48
|
const digestOf = (character: string) => Schema.decodeSync(Digest)(character.repeat(64));
|
|
@@ -94,7 +57,7 @@ export const s2CoordinatorDigests = DefinitionDigests.make({
|
|
|
94
57
|
/**
|
|
95
58
|
* The exact child Binding digest strings the application declares on
|
|
96
59
|
* `SubagentRuntimeOptions.durable.targetDigests` AND the host registers with
|
|
97
|
-
* the
|
|
60
|
+
* the host's binding array for the researcher Binding. The coordinator
|
|
98
61
|
* stores and verifies them byte-for-byte (SUB-023); a host registration under
|
|
99
62
|
* different strings is a `ChildCompatibilityFailure`, never a substitution.
|
|
100
63
|
*/
|
|
@@ -112,10 +75,10 @@ export const s2ResearcherDigests = DefinitionDigests.make({
|
|
|
112
75
|
|
|
113
76
|
/** Durable admission options for one coordinator Submission on one mission lane. */
|
|
114
77
|
export const s2TravelPlannerSubmitOptions = (
|
|
115
|
-
|
|
78
|
+
threadId: ThreadId,
|
|
116
79
|
idempotencyKey: IdempotencyKey,
|
|
117
80
|
): DurableSubmitOptions => ({
|
|
118
|
-
|
|
81
|
+
threadId,
|
|
119
82
|
principal: s2TravelPlannerPrincipal,
|
|
120
83
|
idempotencyKey,
|
|
121
84
|
definitions: s2CoordinatorDigests,
|
|
@@ -166,6 +129,7 @@ export const durableResearchShortlist = (destination: string): DestinationShortl
|
|
|
166
129
|
*/
|
|
167
130
|
export const encodedDestinationFacts = (destination: string): unknown => {
|
|
168
131
|
const report = destinationReportFor(destination);
|
|
132
|
+
|
|
169
133
|
return Schema.encodeSync(DestinationFacts)(
|
|
170
134
|
DestinationFacts.make({
|
|
171
135
|
destination: report.destination,
|
|
@@ -193,6 +157,7 @@ export const makeInvocationCountingModel = (
|
|
|
193
157
|
Effect.gen(function* () {
|
|
194
158
|
const calls = yield* Ref.make(0);
|
|
195
159
|
const prompts = yield* Ref.make<ReadonlyArray<string>>([]);
|
|
160
|
+
|
|
196
161
|
const model = Model.make(
|
|
197
162
|
"scripted",
|
|
198
163
|
name,
|
|
@@ -204,16 +169,19 @@ export const makeInvocationCountingModel = (
|
|
|
204
169
|
Stream.unwrap(
|
|
205
170
|
Effect.gen(function* () {
|
|
206
171
|
const call = yield* Ref.getAndUpdate(calls, (value) => value + 1);
|
|
172
|
+
|
|
207
173
|
yield* Ref.update(prompts, (previous) => [
|
|
208
174
|
...previous,
|
|
209
175
|
JSON.stringify(request.prompt.content),
|
|
210
176
|
]);
|
|
177
|
+
|
|
211
178
|
return Stream.fromIterable(script(call));
|
|
212
179
|
}),
|
|
213
180
|
),
|
|
214
181
|
}),
|
|
215
182
|
),
|
|
216
183
|
);
|
|
184
|
+
|
|
217
185
|
return { model, calls: Ref.get(calls), prompts: Ref.get(prompts) };
|
|
218
186
|
});
|
|
219
187
|
|
|
@@ -283,7 +251,7 @@ export const durableDestinationResearchHandlersLayer = <Provider, ModelProvides,
|
|
|
283
251
|
ModelRequires
|
|
284
252
|
>,
|
|
285
253
|
) =>
|
|
286
|
-
|
|
254
|
+
Subagent.layer(destinationResearchDelegation, childBinding, {
|
|
287
255
|
mapChildFailure: mapResearchChildFailure,
|
|
288
256
|
durable: { targetDigests: s2ResearcherDigestStrings },
|
|
289
257
|
});
|
|
@@ -304,7 +272,7 @@ export interface DurableResearchHarnessOptions {
|
|
|
304
272
|
|
|
305
273
|
/** One durable coordinator/researcher pair with observable invocation counters. */
|
|
306
274
|
export interface DurableResearchHarness {
|
|
307
|
-
/** Host registrations for `
|
|
275
|
+
/** Host registrations for `NodeDurableAgentRuntimeOptions.bindings` (parent + child). */
|
|
308
276
|
readonly bindings: ReadonlyArray<ResolvedBinding>;
|
|
309
277
|
/** Total coordinator model invocations across every Attempt and runtime handle. */
|
|
310
278
|
readonly parentModelCalls: Effect.Effect<number>;
|
|
@@ -325,7 +293,7 @@ export interface DurableResearchHarness {
|
|
|
325
293
|
* guide, Turn 2 writes the report), and both worker Bindings captured with
|
|
326
294
|
* their requirement Contexts via `DurableWorkerBinding.make` under the exact
|
|
327
295
|
* fixture digests. The returned `bindings` are plain values: they can be
|
|
328
|
-
* registered with several `
|
|
296
|
+
* registered with several `NodeDurableAgentRuntime` stacks over the same SQLite
|
|
329
297
|
* file while the counters keep counting across all of them.
|
|
330
298
|
*/
|
|
331
299
|
export const makeDurableResearchHarness = (options?: DurableResearchHarnessOptions) =>
|
|
@@ -334,6 +302,7 @@ export const makeDurableResearchHarness = (options?: DurableResearchHarnessOptio
|
|
|
334
302
|
const focus = options?.focus ?? "museums";
|
|
335
303
|
|
|
336
304
|
const guideInvocations = yield* Ref.make(0);
|
|
305
|
+
|
|
337
306
|
const guideLayer = Layer.succeed(
|
|
338
307
|
DestinationGuide,
|
|
339
308
|
DestinationGuide.of({
|
|
@@ -343,6 +312,7 @@ export const makeDurableResearchHarness = (options?: DurableResearchHarnessOptio
|
|
|
343
312
|
),
|
|
344
313
|
}),
|
|
345
314
|
);
|
|
315
|
+
|
|
346
316
|
const childToolkitLayer = DestinationResearcherToolkitLayer.pipe(
|
|
347
317
|
Layer.provideMerge(guideLayer),
|
|
348
318
|
);
|
|
@@ -350,6 +320,7 @@ export const makeDurableResearchHarness = (options?: DurableResearchHarnessOptio
|
|
|
350
320
|
const childModel = yield* makeInvocationCountingModel("destination-researcher-s2", (call) =>
|
|
351
321
|
call === 0 ? researcherLookupParts(destination) : researcherReportParts(destination),
|
|
352
322
|
);
|
|
323
|
+
|
|
353
324
|
const childBinding = Agent.withModel(DestinationResearcher, childModel.model);
|
|
354
325
|
|
|
355
326
|
const parentModel = yield* makeInvocationCountingModel("travel-coordinator-s2", (call) =>
|
|
@@ -357,6 +328,7 @@ export const makeDurableResearchHarness = (options?: DurableResearchHarnessOptio
|
|
|
357
328
|
? delegationTurnParts(durableResearchCallId, destination, focus)
|
|
358
329
|
: shortlistParts(durableResearchShortlist(destination)),
|
|
359
330
|
);
|
|
331
|
+
|
|
360
332
|
const parentBinding = Agent.withModel(TravelCoordinator, parentModel.model);
|
|
361
333
|
|
|
362
334
|
const delegationLayer = durableDestinationResearchHandlersLayer(childBinding).pipe(
|
|
@@ -374,6 +346,7 @@ export const makeDurableResearchHarness = (options?: DurableResearchHarnessOptio
|
|
|
374
346
|
parentBinding,
|
|
375
347
|
s2CoordinatorDigests,
|
|
376
348
|
).pipe(Effect.provide(delegationLayer));
|
|
349
|
+
|
|
377
350
|
const childResolved: ResolvedBinding = yield* DurableWorkerBinding.make(
|
|
378
351
|
childBinding,
|
|
379
352
|
options?.childRegistrationDigests ?? s2ResearcherDigests,
|
|
@@ -387,5 +360,6 @@ export const makeDurableResearchHarness = (options?: DurableResearchHarnessOptio
|
|
|
387
360
|
childPrompts: childModel.prompts,
|
|
388
361
|
guideInvocations: Ref.get(guideInvocations),
|
|
389
362
|
};
|
|
363
|
+
|
|
390
364
|
return harness;
|
|
391
365
|
});
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import { Subagent, SubagentPolicy, SubagentRuntime } from "@effect-agent/capabilities";
|
|
2
|
-
import { Agent, AgentPolicy } from "@effect-agent/core";
|
|
3
|
-
import type { RuntimeBinding } from "@effect-agent/engine";
|
|
4
1
|
import { Context, Deferred, Effect, Layer, Ref, Schema, Stream } from "effect";
|
|
2
|
+
import * as Agent from "effect-agent/agent";
|
|
3
|
+
import { AgentPolicy } from "effect-agent/agent-policy";
|
|
4
|
+
import { type RuntimeBinding } from "effect-agent/agent-runtime";
|
|
5
|
+
import * as Subagent from "effect-agent/subagent";
|
|
6
|
+
import { SubagentPolicy } from "effect-agent/subagent";
|
|
5
7
|
import { LanguageModel, Model, type Response, Tool, Toolkit } from "effect/unstable/ai";
|
|
6
8
|
|
|
7
|
-
import type { ScriptedTurnInput } from "../../
|
|
9
|
+
import type { ScriptedTurnInput } from "../../ScriptedModel.ts";
|
|
8
10
|
import { AirportCode } from "./definition.ts";
|
|
9
11
|
import { CatalogLifecycle } from "./deterministic-layers.ts";
|
|
10
12
|
|
|
11
13
|
// ---------------------------------------------------------------------------
|
|
12
|
-
// Destination Researcher: the S1 specialist child Agent
|
|
13
|
-
//
|
|
14
|
-
// delegation"). The researcher is a normal Agent with one deterministic
|
|
14
|
+
// Destination Researcher: the S1 specialist child Agent.
|
|
15
|
+
// The researcher is a normal Agent with one deterministic
|
|
15
16
|
// travel-service Tool; it never becomes a second child loop (SUB-001/003).
|
|
16
17
|
// ---------------------------------------------------------------------------
|
|
17
18
|
|
|
@@ -51,6 +52,7 @@ export const LookupDestination = Tool.make("lookup_destination", {
|
|
|
51
52
|
});
|
|
52
53
|
|
|
53
54
|
export const DestinationResearcherToolkit = Toolkit.make(LookupDestination);
|
|
55
|
+
|
|
54
56
|
export const DestinationResearcherToolkitLayer = DestinationResearcherToolkit.toLayer({
|
|
55
57
|
lookup_destination: (query) => Effect.flatMap(DestinationGuide, (guide) => guide.lookup(query)),
|
|
56
58
|
});
|
|
@@ -66,7 +68,7 @@ export class DestinationReport extends Schema.Class<DestinationReport>("Destinat
|
|
|
66
68
|
advisory: Schema.NonEmptyString,
|
|
67
69
|
}) {}
|
|
68
70
|
|
|
69
|
-
export const DestinationResearcher = Agent.
|
|
71
|
+
export const DestinationResearcher = Agent.make("destination-researcher", {
|
|
70
72
|
input: DestinationBrief,
|
|
71
73
|
output: DestinationReport,
|
|
72
74
|
instructions:
|
|
@@ -112,6 +114,7 @@ export const destinationLookup = (
|
|
|
112
114
|
query: DestinationQuery,
|
|
113
115
|
): Effect.Effect<DestinationFacts, DestinationGuideUnavailable> => {
|
|
114
116
|
const facts = guideFacts.get(query.destination);
|
|
117
|
+
|
|
115
118
|
return facts === undefined
|
|
116
119
|
? Effect.fail(
|
|
117
120
|
DestinationGuideUnavailable.make({
|
|
@@ -124,15 +127,18 @@ export const destinationLookup = (
|
|
|
124
127
|
|
|
125
128
|
const requireDestinationFacts = (destination: string): DestinationFacts => {
|
|
126
129
|
const facts = guideFacts.get(destination);
|
|
130
|
+
|
|
127
131
|
if (facts === undefined) {
|
|
128
132
|
throw new Error(`No deterministic guide entry exists for destination ${destination}`);
|
|
129
133
|
}
|
|
134
|
+
|
|
130
135
|
return facts;
|
|
131
136
|
};
|
|
132
137
|
|
|
133
138
|
/** The report the scripted researcher writes after consulting the guide. */
|
|
134
139
|
export const destinationReportFor = (destination: string): DestinationReport => {
|
|
135
140
|
const facts = requireDestinationFacts(destination);
|
|
141
|
+
|
|
136
142
|
return DestinationReport.make({
|
|
137
143
|
destination: facts.destination,
|
|
138
144
|
highlights: facts.highlights,
|
|
@@ -147,7 +153,9 @@ export const DestinationGuideLayer = Layer.effect(
|
|
|
147
153
|
DestinationGuide,
|
|
148
154
|
Effect.gen(function* () {
|
|
149
155
|
const lifecycle = yield* CatalogLifecycle;
|
|
156
|
+
|
|
150
157
|
yield* Effect.acquireRelease(lifecycle.markAcquired, () => lifecycle.markFinalized);
|
|
158
|
+
|
|
151
159
|
return DestinationGuide.of({ lookup: destinationLookup });
|
|
152
160
|
}),
|
|
153
161
|
);
|
|
@@ -159,7 +167,7 @@ export const DestinationResearchSupportLayer = Layer.mergeAll(
|
|
|
159
167
|
);
|
|
160
168
|
|
|
161
169
|
// ---------------------------------------------------------------------------
|
|
162
|
-
// Delegation Definition
|
|
170
|
+
// Delegation Definition: the coordinator sees exactly
|
|
163
171
|
// one Effect AI Tool with explicit input/result projections and finite bounds.
|
|
164
172
|
// ---------------------------------------------------------------------------
|
|
165
173
|
|
|
@@ -189,7 +197,7 @@ export class DestinationResearchFailed extends Schema.TaggedError<DestinationRes
|
|
|
189
197
|
* this gate before the handler reserves budget or spawns, so tests can order
|
|
190
198
|
* concurrent delegation preflights without sleeps. It also keeps the
|
|
191
199
|
* projection's construction requirements honestly visible in the handler
|
|
192
|
-
* Layer's `R
|
|
200
|
+
* Layer's `R`. The open Layer never waits.
|
|
193
201
|
*/
|
|
194
202
|
export class ResearchDispatchGate extends Context.Service<
|
|
195
203
|
ResearchDispatchGate,
|
|
@@ -224,14 +232,16 @@ export const destinationResearchDelegation = Subagent.define("delegate_destinati
|
|
|
224
232
|
prepareInput: (request) =>
|
|
225
233
|
Effect.gen(function* () {
|
|
226
234
|
const gate = yield* ResearchDispatchGate;
|
|
235
|
+
|
|
227
236
|
yield* gate.awaitDispatch(request.destination);
|
|
237
|
+
|
|
228
238
|
return DestinationBrief.make({
|
|
229
239
|
destination: request.destination,
|
|
230
240
|
focus: `research:${request.focus}`,
|
|
231
241
|
});
|
|
232
242
|
}),
|
|
233
243
|
// The explicit declassification boundary (SUB-015): only the advisory
|
|
234
|
-
// crosses to the parent; guide highlights stay in the child
|
|
244
|
+
// crosses to the parent; guide highlights stay in the child Thread.
|
|
235
245
|
projectResult: (report) =>
|
|
236
246
|
Effect.succeed(
|
|
237
247
|
DestinationResearchFindings.make({
|
|
@@ -259,7 +269,7 @@ export const destinationResearchHandlersLayer = <Provider, ModelProvides, ModelR
|
|
|
259
269
|
ModelRequires
|
|
260
270
|
>,
|
|
261
271
|
) =>
|
|
262
|
-
|
|
272
|
+
Subagent.layer(destinationResearchDelegation, childBinding, {
|
|
263
273
|
mapChildFailure: mapResearchChildFailure,
|
|
264
274
|
});
|
|
265
275
|
|
|
@@ -292,7 +302,7 @@ export const missionConfidentialMarker = "traveler-dossier-19f";
|
|
|
292
302
|
|
|
293
303
|
export const TravelCoordinatorToolkit = Toolkit.make(destinationResearchDelegation.tool);
|
|
294
304
|
|
|
295
|
-
export const TravelCoordinator = Agent.
|
|
305
|
+
export const TravelCoordinator = Agent.make("travel-coordinator", {
|
|
296
306
|
input: ResearchMission,
|
|
297
307
|
output: DestinationShortlist,
|
|
298
308
|
instructions: [
|
|
@@ -314,7 +324,7 @@ export const TravelCoordinator = Agent.define("travel-coordinator", {
|
|
|
314
324
|
});
|
|
315
325
|
|
|
316
326
|
export const researchMission = Schema.decodeSync(ResearchMission)({
|
|
317
|
-
request: `Shortlist one September culture city; keep ${missionConfidentialMarker} inside the coordinator
|
|
327
|
+
request: `Shortlist one September culture city; keep ${missionConfidentialMarker} inside the coordinator thread.`,
|
|
318
328
|
candidates: ["LHR", "CDG"],
|
|
319
329
|
});
|
|
320
330
|
|
|
@@ -461,19 +471,23 @@ export const makeDestinationResearcherModel = (destinations: ReadonlyArray<strin
|
|
|
461
471
|
const lifecycle = yield* CatalogLifecycle;
|
|
462
472
|
const prompts = yield* Ref.make<ReadonlyArray<string>>([]);
|
|
463
473
|
const gates = new Map<string, ResearcherGates>();
|
|
474
|
+
|
|
464
475
|
for (const destination of destinations) {
|
|
465
476
|
gates.set(destination, {
|
|
466
477
|
started: yield* Deferred.make<void>(),
|
|
467
478
|
release: yield* Deferred.make<void>(),
|
|
468
479
|
});
|
|
469
480
|
}
|
|
481
|
+
|
|
470
482
|
const gatesFor = (destination: string): Effect.Effect<ResearcherGates> =>
|
|
471
483
|
Effect.suspend(() => {
|
|
472
484
|
const entry = gates.get(destination);
|
|
485
|
+
|
|
473
486
|
return entry === undefined
|
|
474
487
|
? Effect.die(new Error(`No researcher gates exist for destination ${destination}`))
|
|
475
488
|
: Effect.succeed(entry);
|
|
476
489
|
});
|
|
490
|
+
|
|
477
491
|
const controls: DestinationResearcherControls = {
|
|
478
492
|
awaitStarted: (destination) =>
|
|
479
493
|
gatesFor(destination).pipe(Effect.flatMap((entry) => Deferred.await(entry.started))),
|
|
@@ -484,6 +498,7 @@ export const makeDestinationResearcherModel = (destinations: ReadonlyArray<strin
|
|
|
484
498
|
),
|
|
485
499
|
prompts: Ref.get(prompts),
|
|
486
500
|
};
|
|
501
|
+
|
|
487
502
|
const model = Model.make(
|
|
488
503
|
"scripted",
|
|
489
504
|
"destination-researcher-scripted",
|
|
@@ -492,15 +507,18 @@ export const makeDestinationResearcherModel = (destinations: ReadonlyArray<strin
|
|
|
492
507
|
Effect.gen(function* () {
|
|
493
508
|
yield* Effect.acquireRelease(lifecycle.markAcquired, () => lifecycle.markFinalized);
|
|
494
509
|
const turn = yield* Ref.make(0);
|
|
510
|
+
|
|
495
511
|
return yield* LanguageModel.make({
|
|
496
512
|
generateText: () => Effect.succeed([]),
|
|
497
513
|
streamText: (options) =>
|
|
498
514
|
Stream.unwrap(
|
|
499
515
|
Effect.gen(function* () {
|
|
500
516
|
const promptJson = JSON.stringify(options.prompt.content);
|
|
517
|
+
|
|
501
518
|
const destination = destinations.find((candidate) =>
|
|
502
519
|
promptJson.includes(candidate),
|
|
503
520
|
);
|
|
521
|
+
|
|
504
522
|
if (destination === undefined) {
|
|
505
523
|
return yield* Effect.die(
|
|
506
524
|
new Error("The researcher prompt names no scripted destination"),
|
|
@@ -508,12 +526,15 @@ export const makeDestinationResearcherModel = (destinations: ReadonlyArray<strin
|
|
|
508
526
|
}
|
|
509
527
|
const entry = yield* gatesFor(destination);
|
|
510
528
|
const index = yield* Ref.getAndUpdate(turn, (value) => value + 1);
|
|
529
|
+
|
|
511
530
|
if (index === 0) {
|
|
512
531
|
yield* Ref.update(prompts, (previous) => [...previous, promptJson]);
|
|
513
532
|
yield* Deferred.succeed(entry.started, undefined);
|
|
533
|
+
|
|
514
534
|
return Stream.fromIterable(researcherLookupParts(destination));
|
|
515
535
|
}
|
|
516
536
|
yield* Deferred.await(entry.release);
|
|
537
|
+
|
|
517
538
|
return Stream.fromIterable(researcherReportParts(destination));
|
|
518
539
|
}),
|
|
519
540
|
),
|
|
@@ -521,5 +542,6 @@ export const makeDestinationResearcherModel = (destinations: ReadonlyArray<strin
|
|
|
521
542
|
}),
|
|
522
543
|
),
|
|
523
544
|
);
|
|
545
|
+
|
|
524
546
|
return { controls, model };
|
|
525
547
|
});
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1 @@
|
|
|
1
|
-
export * from "./
|
|
2
|
-
export * from "./chaos.ts";
|
|
3
|
-
export * from "./code-executor-conformance.ts";
|
|
4
|
-
export * from "./code-executor-substitute.ts";
|
|
5
|
-
export * from "./fixtures/docs-researcher/index.ts";
|
|
6
|
-
export * from "./fixtures/travel-planner/index.ts";
|
|
7
|
-
// The warehouse fixture is NOT re-exported here: it depends on
|
|
8
|
-
// `@effect/sql-sqlite-node` (`node:sqlite`), which workerd consumers of this
|
|
9
|
-
// barrel (the Cloudflare certification suites) cannot load. Import it
|
|
10
|
-
// directly from `@effect-agent/testing/src/fixtures/warehouse/index.ts`.
|
|
11
|
-
export * from "./scripted-model.ts";
|
|
1
|
+
export * as ScriptedModel from "./ScriptedModel.ts";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { CertificationReport } from "effect-agent/testing/certification";
|
|
2
|
+
|
|
3
|
+
/** Fold executed results without acquiring adapters or claiming coverage from citations. */
|
|
4
|
+
export const makeCertificationReport = (
|
|
5
|
+
results: Pick<CertificationReport, "adapter" | "generatedAt" | "tier1" | "tier2" | "tier3">,
|
|
6
|
+
): CertificationReport => {
|
|
7
|
+
const { adapter, tier1, tier2, tier3 } = results;
|
|
8
|
+
|
|
9
|
+
const ok =
|
|
10
|
+
tier1.every((result) => result.status === "passed") &&
|
|
11
|
+
tier2.every((result) => result.status !== "failed") &&
|
|
12
|
+
tier3.cases.every((result) => result.status === "passed");
|
|
13
|
+
|
|
14
|
+
return CertificationReport.make({
|
|
15
|
+
format: "effect-agent/certification@2",
|
|
16
|
+
...results,
|
|
17
|
+
ok,
|
|
18
|
+
fullyCertified:
|
|
19
|
+
ok &&
|
|
20
|
+
adapter.durability !== "non-durable" &&
|
|
21
|
+
tier3.status === "exercised" &&
|
|
22
|
+
tier3.cases.length > 0 &&
|
|
23
|
+
tier3.cases.every((result) => result.suite === "real-loss"),
|
|
24
|
+
});
|
|
25
|
+
};
|