@xandreed/core 0.3.0 → 0.8.0-next.0
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/README.md +1 -1
- package/dist/conformance.entity.d.ts +15 -0
- package/dist/conformance.entity.js +7 -0
- package/dist/decision-record.entity.d.ts +31 -0
- package/dist/decision-record.entity.functions.d.ts +6 -0
- package/dist/decision-record.entity.functions.js +3 -0
- package/dist/decision-record.entity.js +24 -0
- package/dist/domain/agent-failure.entity.d.ts +6 -6
- package/dist/domain/agent-failure.entity.js +12 -1
- package/dist/domain/failure.entity.d.ts +2 -2
- package/dist/domain/loop-event.entity.d.ts +2 -0
- package/dist/domain/message.entity.d.ts +143 -156
- package/dist/domain/message.entity.js +6 -6
- package/dist/domain/model-call-policy.entity.d.ts +3 -3
- package/dist/domain/model-call-policy.entity.js +1 -1
- package/dist/domain/model-catalog.entity.d.ts +4 -4
- package/dist/domain/model-catalog.entity.js +1 -1
- package/dist/domain/model-selection.entity.d.ts +6 -13
- package/dist/domain/prompt-provenance.entity.d.ts +6 -6
- package/dist/domain/prompt-provenance.entity.js +4 -4
- package/dist/domain/token-usage.entity.d.ts +4 -4
- package/dist/harness/capability.entity.d.ts +88 -41
- package/dist/harness/capability.entity.functions.d.ts +30 -1
- package/dist/harness/capability.entity.functions.js +55 -2
- package/dist/harness/capability.entity.js +33 -12
- package/dist/harness/config.entity.d.ts +28 -28
- package/dist/harness/config.entity.js +4 -4
- package/dist/harness/plugin-stack.adapter.d.ts +24 -0
- package/dist/harness/plugin-stack.adapter.js +8 -0
- package/dist/harness/plugin-stack.adapter.test.d.ts +1 -0
- package/dist/harness/plugin.adapter.d.ts +15 -4
- package/dist/harness/plugin.adapter.js +18 -11
- package/dist/harness/plugin.adapter.test.d.ts +1 -0
- package/dist/harness/plugin.entity.d.ts +26 -10
- package/dist/harness/plugin.entity.js +1 -1
- package/dist/harness/session.entity.d.ts +20 -20
- package/dist/harness/session.entity.js +1 -1
- package/dist/harness/skill-files.d.ts +17 -0
- package/dist/harness/skill-files.js +25 -0
- package/dist/index.d.ts +48 -4
- package/dist/index.js +46 -4
- package/dist/loop/cacheKey.d.ts +3 -3
- package/dist/loop/cacheKey.js +4 -2
- package/dist/loop/mapping.d.ts +4 -5
- package/dist/loop/mapping.js +8 -16
- package/dist/loop/modelPolicy.d.ts +4 -4
- package/dist/loop/modelPolicy.js +2 -2
- package/dist/loop/promptProvenance.d.ts +2 -2
- package/dist/loop/promptProvenance.js +2 -2
- package/dist/loop/stepContext.d.ts +2 -2
- package/dist/loop/stepContext.js +2 -2
- package/dist/loop/streamFold.js +3 -3
- package/dist/loop/toolSchema.d.ts +17 -0
- package/dist/loop/toolSchema.js +25 -0
- package/dist/loop/toolSchema.test.d.ts +1 -0
- package/dist/mcp/bridge.d.ts +15 -15
- package/dist/mcp/bridge.js +14 -14
- package/dist/memory/memory-log.entity.d.ts +320 -0
- package/dist/memory/memory-log.entity.functions.d.ts +89 -0
- package/dist/memory/memory-log.entity.functions.js +180 -0
- package/dist/memory/memory-log.entity.js +75 -0
- package/dist/memory/memory-log.entity.test.d.ts +1 -0
- package/dist/memory/memory-session.d.ts +51 -0
- package/dist/memory/memory-session.js +97 -0
- package/dist/memory/memory-session.test.d.ts +1 -0
- package/dist/memory/memory.conformance.d.ts +32 -0
- package/dist/memory/memory.conformance.js +102 -0
- package/dist/ports/auth-store.port.d.ts +16 -18
- package/dist/ports/auth-store.port.js +13 -9
- package/dist/ports/capability.port.d.ts +131 -7
- package/dist/ports/capability.port.js +2 -2
- package/dist/ports/conversation-store.port.d.ts +54 -177
- package/dist/ports/conversation-store.port.js +9 -9
- package/dist/ports/file-system.port.d.ts +5 -7
- package/dist/ports/file-system.port.js +1 -1
- package/dist/ports/harness.port.d.ts +19 -13
- package/dist/ports/harness.port.js +10 -10
- package/dist/ports/mcp-client.port.d.ts +15 -47
- package/dist/ports/mcp-client.port.js +3 -3
- package/dist/ports/memory.port.d.ts +116 -0
- package/dist/ports/memory.port.js +7 -0
- package/dist/ports/model-catalog.port.d.ts +1 -1
- package/dist/ports/model-catalog.port.js +1 -1
- package/dist/ports/permission.port.d.ts +10 -0
- package/dist/ports/permission.port.js +4 -0
- package/dist/ports/run-context.port.d.ts +28 -0
- package/dist/ports/run-context.port.js +4 -0
- package/dist/ports/session-log.port.d.ts +25 -0
- package/dist/ports/session-log.port.js +4 -0
- package/dist/ports/sessions.port.d.ts +141 -0
- package/dist/ports/sessions.port.js +4 -0
- package/dist/ports/settings-store.port.d.ts +18 -56
- package/dist/ports/settings-store.port.js +3 -3
- package/dist/ports/shell.port.d.ts +9 -21
- package/dist/ports/shell.port.js +1 -1
- package/dist/ports/step-loop.port.d.ts +85 -0
- package/dist/ports/step-loop.port.js +4 -0
- package/dist/ports/tasks.port.d.ts +59 -0
- package/dist/ports/tasks.port.js +10 -0
- package/dist/ports/tool-registry.port.d.ts +53 -0
- package/dist/ports/tool-registry.port.js +4 -0
- package/dist/ports/turn-admission.port.d.ts +23 -0
- package/dist/ports/turn-admission.port.js +4 -0
- package/dist/ports/turn-events.port.d.ts +55 -0
- package/dist/ports/turn-events.port.js +7 -0
- package/dist/ports/turn-scope.port.d.ts +101 -0
- package/dist/ports/turn-scope.port.js +10 -0
- package/dist/ports/turn.port.d.ts +113 -0
- package/dist/ports/turn.port.js +0 -0
- package/dist/ports/utility-llm.port.d.ts +12 -31
- package/dist/ports/utility-llm.port.js +1 -1
- package/dist/session/chassis.js +7 -7
- package/dist/session/session-event.entity.d.ts +55 -0
- package/dist/session/session-event.entity.functions.d.ts +31 -0
- package/dist/session/session-event.entity.functions.js +42 -0
- package/dist/session/session-event.entity.js +37 -0
- package/dist/session/session-event.entity.test.d.ts +1 -0
- package/dist/session/session-log.conformance.d.ts +13 -0
- package/dist/session/session-log.conformance.js +167 -0
- package/dist/session/session-log.entity.d.ts +164 -0
- package/dist/session/session-log.entity.js +82 -0
- package/dist/session/session-log.memory.adapter.d.ts +7 -0
- package/dist/session/session-log.memory.adapter.js +82 -0
- package/dist/session/session-log.memory.adapter.test.d.ts +1 -0
- package/dist/session/sessions.entity.d.ts +201 -0
- package/dist/session/sessions.entity.js +98 -0
- package/dist/session/tasks.entity.d.ts +39 -0
- package/dist/session/tasks.entity.js +26 -0
- package/dist/session/turn-admission.open.adapter.d.ts +4 -0
- package/dist/session/turn-admission.open.adapter.js +3 -0
- package/dist/spec/SpecDoc.d.ts +30 -117
- package/dist/spec/SpecDoc.js +10 -10
- package/dist/spec/codec.js +1 -1
- package/dist/turn/model-request.entity.d.ts +99 -0
- package/dist/turn/model-request.entity.functions.d.ts +108 -0
- package/dist/turn/model-request.entity.functions.js +167 -0
- package/dist/turn/model-request.entity.js +41 -0
- package/dist/turn/model-request.entity.test.d.ts +1 -0
- package/dist/turn/prompt-sections.d.ts +10 -0
- package/dist/turn/prompt-sections.js +3 -0
- package/dist/turn/step-loop.conformance.d.ts +16 -0
- package/dist/turn/step-loop.conformance.js +164 -0
- package/dist/turn/testing.d.ts +30 -0
- package/dist/turn/turn-bus.d.ts +23 -0
- package/dist/turn/turn-bus.js +54 -0
- package/dist/turn/turn-bus.test.d.ts +1 -0
- package/dist/turn/turn-event.entity.d.ts +263 -0
- package/dist/turn/turn-event.entity.functions.d.ts +35 -0
- package/dist/turn/turn-event.entity.functions.js +21 -0
- package/dist/turn/turn-event.entity.js +101 -0
- package/dist/turn/turn-lifecycle.d.ts +32 -0
- package/dist/turn/turn-lifecycle.js +48 -0
- package/dist/turn/turn-run.d.ts +26 -0
- package/dist/turn/turn-run.js +105 -0
- package/dist/turn/turn.adapter.d.ts +21 -0
- package/dist/turn/turn.adapter.js +126 -0
- package/dist/turn/turn.adapter.test.d.ts +1 -0
- package/dist/turn/turn.conformance.d.ts +20 -0
- package/dist/turn/turn.conformance.js +74 -0
- package/dist/turn/user-message.entity.d.ts +14 -0
- package/dist/turn/user-message.entity.js +10 -0
- package/dist/util/json.d.ts +2 -2
- package/dist/util/json.js +10 -10
- package/package.json +2 -3
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { Context, Effect, Option } from "effect";
|
|
2
|
+
import { LanguageModel } from "effect/ai";
|
|
3
|
+
import { toolParametersSchema } from "../loop/toolSchema.js";
|
|
4
|
+
import { HarnessError } from "../harness/plugin.entity.js";
|
|
5
|
+
import { canonicalJson, estimateTokens, fingerprintOf } from "../memory/memory-log.entity.functions.js";
|
|
6
|
+
import { StepLoop } from "../ports/step-loop.port.js";
|
|
7
|
+
import { TurnEvents, TurnTasks } from "../ports/turn-events.port.js";
|
|
8
|
+
import { TurnMemory, TurnPrompt, TurnToolbox } from "../ports/turn-scope.port.js";
|
|
9
|
+
import { CurrentModelCallPolicy } from "../loop/modelPolicy.js";
|
|
10
|
+
import {
|
|
11
|
+
checkModelRequest,
|
|
12
|
+
modelRequestDescriptorOf,
|
|
13
|
+
modelRequestHeaderOf,
|
|
14
|
+
modelRequestTools,
|
|
15
|
+
reconstructModelRequest,
|
|
16
|
+
resolveModelRequest
|
|
17
|
+
} from "./model-request.entity.functions.js";
|
|
18
|
+
const defaultLimits = { maxSteps: 50, toolConcurrency: 1, streaming: !0, requireCompletion: !1 }, defaultBudgetTokens = 64000, incomplete = { complete: !1, awaiting: [], facts: {} }, noDirective = { context: Option.none(), toolChoice: Option.none() };
|
|
19
|
+
export const schemaTokens = (tools, active) => active.reduce((sum, name) => {
|
|
20
|
+
const tool = tools.toolkit.tools[name];
|
|
21
|
+
return tool === void 0 ? sum : sum + estimateTokens(`${tool.description ?? ""}${canonicalJson(toolParametersSchema(tool))}`);
|
|
22
|
+
}, 0), cacheKeyOf = (prefix, conversation) => Option.map(Option.filter(Option.some(prefix), (value) => value.length > 0), (value) => `${value}:${conversation}`), stepRequestOf = (policy, options = {}) => Effect.gen(function* () {
|
|
23
|
+
const runServices = yield* Effect.context(), inRun = (effect) => effect.pipe(Effect.provide(runServices)), memory = yield* TurnMemory, prompt = yield* TurnPrompt, events = yield* TurnEvents, tasks = yield* TurnTasks, tools = yield* (yield* TurnToolbox).tools, session = memory.session, runId = (yield* session.renderRecipe("none")).currentRun, defaultModel = Context.getOption(runServices, LanguageModel.LanguageModel), number = yield* memory.number, limits = { ...defaultLimits, ...options.limits, ...policy.limits }, budget = policy.budgetTokens ?? options.budgetTokens ?? defaultBudgetTokens, stepContext = policy.stepContext ?? "tail";
|
|
24
|
+
yield* Option.match(Option.fromNullishOr(policy.initial), {
|
|
25
|
+
onNone: () => Effect.void,
|
|
26
|
+
onSome: (batch) => batch.skills.length === 0 ? Effect.void : tools.activate(batch.skills, "host").pipe(Effect.asVoid)
|
|
27
|
+
});
|
|
28
|
+
yield* inRun(prompt.turnSections);
|
|
29
|
+
yield* session.maintain({ phase: "turn-start", lastUsage: Option.none(), budgetTokens: budget, views: tools.views });
|
|
30
|
+
const plan = (info) => Effect.gen(function* () {
|
|
31
|
+
const choice = policy.model === void 0 ? Option.none() : yield* inRun(policy.model(info)), system = yield* inRun(prompt.system(Option.flatMap(choice, (value) => value.variant))), directive = policy.step === void 0 ? noDirective : yield* inRun(policy.step(info));
|
|
32
|
+
yield* Option.match(directive.context, {
|
|
33
|
+
onNone: () => Effect.void,
|
|
34
|
+
onSome: (text) => session.record([{ _tag: "StepContext", step: info.stepIndex, text }], info.stepIndex).pipe(Effect.asVoid)
|
|
35
|
+
});
|
|
36
|
+
const reserved = estimateTokens(system) + schemaTokens(tools, info.activeTools);
|
|
37
|
+
yield* session.maintain({ phase: "step", lastUsage: info.lastUsage, budgetTokens: Math.max(1, budget - reserved), views: tools.views });
|
|
38
|
+
const built = yield* session.build({ stepContext: stepContext === "tail" ? "tail" : "none" }), render = yield* session.renderRecipe(stepContext === "tail" ? "tail" : "none");
|
|
39
|
+
yield* events.publish({
|
|
40
|
+
_tag: "context.built",
|
|
41
|
+
step: info.stepIndex,
|
|
42
|
+
turn: number,
|
|
43
|
+
strategy: session.strategy.id,
|
|
44
|
+
strategyVersion: session.strategy.version,
|
|
45
|
+
fingerprint: built.fingerprint,
|
|
46
|
+
systemFingerprint: fingerprintOf(system),
|
|
47
|
+
estimatedTokens: built.estimatedTokens,
|
|
48
|
+
reservedTokens: reserved,
|
|
49
|
+
compactions: built.compactions.length,
|
|
50
|
+
activeTools: info.activeTools
|
|
51
|
+
});
|
|
52
|
+
const stepText = stepContext === "system" ? Option.getOrElse(directive.context, () => "") : "", finalSystem = [system, stepText].filter((part) => part.length > 0).join(`
|
|
53
|
+
|
|
54
|
+
`), model = yield* Option.match(Option.orElse(Option.map(choice, (value) => value.model), () => defaultModel), {
|
|
55
|
+
onNone: () => Effect.succeed(Option.none()),
|
|
56
|
+
onSome: (chosen) => resolveModelRequest(chosen).pipe(Effect.map(Option.some))
|
|
57
|
+
});
|
|
58
|
+
yield* memory.prepareRequest({
|
|
59
|
+
version: 1,
|
|
60
|
+
runId,
|
|
61
|
+
step: info.stepIndex,
|
|
62
|
+
strategyVersion: session.strategy.version,
|
|
63
|
+
system: finalSystem,
|
|
64
|
+
render,
|
|
65
|
+
through: built.through,
|
|
66
|
+
contextFingerprint: built.fingerprint,
|
|
67
|
+
tools: modelRequestTools(info.activeTools.flatMap((name) => tools.toolkit.tools[name] === void 0 ? [] : [tools.toolkit.tools[name]])),
|
|
68
|
+
toolChoice: Option.getOrElse(directive.toolChoice, () => "auto"),
|
|
69
|
+
model: yield* Option.match(model, { onNone: () => Effect.succeed(Option.none()), onSome: modelRequestDescriptorOf }),
|
|
70
|
+
cacheKey: options.cacheKey ?? Option.none(),
|
|
71
|
+
callPolicy: yield* Effect.service(CurrentModelCallPolicy)
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
model,
|
|
75
|
+
system: finalSystem,
|
|
76
|
+
messages: built.messages,
|
|
77
|
+
toolChoice: directive.toolChoice
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
return {
|
|
81
|
+
tools,
|
|
82
|
+
handlers: Context.merge(runServices, tools.handlers),
|
|
83
|
+
limits,
|
|
84
|
+
initial: Option.fromNullishOr(policy.initial),
|
|
85
|
+
plan,
|
|
86
|
+
dispatch: (step, actual) => Effect.gen(function* () {
|
|
87
|
+
const stored = yield* memory.requestSnapshot, header = yield* modelRequestHeaderOf(stored.requests, runId, step), expected = yield* reconstructModelRequest(header, stored.entries);
|
|
88
|
+
yield* checkModelRequest(expected, {
|
|
89
|
+
prompt: actual.prompt,
|
|
90
|
+
tools: modelRequestTools(actual.tools),
|
|
91
|
+
toolChoice: actual.toolChoice,
|
|
92
|
+
model: yield* modelRequestDescriptorOf(actual.model),
|
|
93
|
+
cacheKey: options.cacheKey ?? Option.none(),
|
|
94
|
+
callPolicy: yield* Effect.service(CurrentModelCallPolicy)
|
|
95
|
+
});
|
|
96
|
+
}),
|
|
97
|
+
record: (step, tail) => session.recordTail(tail, tools.views, step),
|
|
98
|
+
completion: (info) => policy.completion === void 0 ? Effect.succeed(incomplete) : inRun(policy.completion(info)),
|
|
99
|
+
steering: options.steering ?? Effect.succeed(Option.none()),
|
|
100
|
+
correctives: Option.fromNullishOr(policy.correctives),
|
|
101
|
+
events,
|
|
102
|
+
tasks,
|
|
103
|
+
cacheKey: options.cacheKey ?? Option.none()
|
|
104
|
+
};
|
|
105
|
+
}), runTurnLoop = (policy, options = {}) => stepRequestOf(policy, options).pipe(Effect.flatMap((request) => StepLoop.pipe(Effect.flatMap((loop) => loop.run(request)))));
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Layer } from "effect";
|
|
2
|
+
import { HarnessError } from "../harness/plugin.entity.js";
|
|
3
|
+
import { ConversationMemory } from "../ports/memory.port.js";
|
|
4
|
+
import { RunContext } from "../ports/run-context.port.js";
|
|
5
|
+
import { ToolRegistry } from "../ports/tool-registry.port.js";
|
|
6
|
+
import { TurnEvents, TurnTasks } from "../ports/turn-events.port.js";
|
|
7
|
+
import { TurnMemory, TurnPrompt, TurnToolbox } from "../ports/turn-scope.port.js";
|
|
8
|
+
import type { TurnLiveInput } from "../ports/turn-scope.port.js";
|
|
9
|
+
/**
|
|
10
|
+
* One admitted turn's services, over the turn's writer, built in this order:
|
|
11
|
+
* the event bus and the tasks, with the writer as the bus's FIRST
|
|
12
|
+
* subscriber (every stored event is queued before any reaction runs); then
|
|
13
|
+
* the memory session, opened over the session's earlier memory events where
|
|
14
|
+
* this layer is built (a digester or summarizer there is used), and
|
|
15
|
+
* RunContext. It also holds the tools slot (`TurnToolbox`) and the prompt
|
|
16
|
+
* state (`TurnPrompt`). The message was stored when the turn began; memory
|
|
17
|
+
* takes it at `TurnMemory.persistMessage`, and records nothing before it
|
|
18
|
+
* (`TurnMemory.context` waits for it). Tasks run in the layer's scope:
|
|
19
|
+
* they are interrupted when it closes.
|
|
20
|
+
*/
|
|
21
|
+
export declare const TurnLive: (input: TurnLiveInput) => Layer.Layer<RunContext | TurnEvents | TurnTasks | TurnMemory | TurnToolbox | TurnPrompt, HarnessError, ConversationMemory | ToolRegistry>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Context, Effect, Layer, Option, Ref, Schema, Scope, Semaphore } from "effect";
|
|
2
|
+
import { HarnessError } from "../harness/plugin.entity.js";
|
|
3
|
+
import { fingerprintOf } from "../memory/memory-log.entity.functions.js";
|
|
4
|
+
import { readerOf } from "../memory/memory-session.js";
|
|
5
|
+
import { Capabilities } from "../ports/capability.port.js";
|
|
6
|
+
import { ConversationMemory } from "../ports/memory.port.js";
|
|
7
|
+
import { RunContext } from "../ports/run-context.port.js";
|
|
8
|
+
import { draftOfTurnEvent, draftsOfEntries, entriesOfEvents } from "../session/session-event.entity.functions.js";
|
|
9
|
+
import { MEMORY_KINDS, RESERVED_KINDS } from "../session/session-event.entity.js";
|
|
10
|
+
import { ToolRegistry } from "../ports/tool-registry.port.js";
|
|
11
|
+
import { TurnEvents, TurnTasks } from "../ports/turn-events.port.js";
|
|
12
|
+
import { TurnMemory, TurnPrompt, TurnToolbox } from "../ports/turn-scope.port.js";
|
|
13
|
+
import { renderSections } from "./prompt-sections.js";
|
|
14
|
+
import { makeTurnEvents, makeTurnTasks } from "./turn-bus.js";
|
|
15
|
+
import { ModelRequestHeader } from "./model-request.entity.js";
|
|
16
|
+
const failure = (code, message) => new HarnessError({ code, message }), inCaller = (effect) => Effect.flatMap(Effect.context(), (context) => effect.pipe(Effect.provide(context))), storedDraft = (turn) => (event) => event._tag === "host" && RESERVED_KINDS.includes(event.name) ? Option.some(failure("events.reserved", `${event.name} is the framework's kind`)) : Option.map(draftOfTurnEvent(turn, event), (draft) => ({ kind: draft.kind, data: draft.data })), contextBody = (entry) => ({ _tag: "TurnContext", sectionId: entry.id, version: entry.version, text: entry.text });
|
|
17
|
+
export const TurnLive = (input) => Layer.effectContext(Effect.gen(function* () {
|
|
18
|
+
const scope = yield* Effect.scope, memory = yield* ConversationMemory, registry = yield* ToolRegistry, sections = Option.getOrElse(yield* Effect.serviceOption(Capabilities), () => []).flatMap((capability) => capability.promptSections), writer = input.turn, admitted = writer.admitted, events = yield* makeTurnEvents({ maxDepth: input.maxEventDepth ?? 8 }), tasks = yield* makeTurnTasks(scope);
|
|
19
|
+
yield* events.subscribe(storedDraft(admitted.turn), (draft) => draft instanceof HarnessError ? Effect.fail(draft) : writer.append([draft]));
|
|
20
|
+
const message = yield* Ref.make({ _tag: "Waiting", early: [] }), history = yield* Ref.make(Option.none()), decoded = (stored) => entriesOfEvents(stored).pipe(Effect.mapError((error) => failure("memory.log", `undecodable memory events: ${error.message}`))), readHistory = writer.history(MEMORY_KINDS).pipe(Effect.flatMap(decoded)), log = {
|
|
21
|
+
read: readHistory.pipe(Effect.tap((entries) => Ref.set(history, Option.some(entries)))),
|
|
22
|
+
append: (entries) => Effect.gen(function* () {
|
|
23
|
+
if ((yield* Ref.get(message))._tag === "Waiting" && entries.some((entry) => entry.body._tag !== "TurnStarted"))
|
|
24
|
+
return yield* Effect.fail(failure("turn.unstarted", "memory records nothing before the turn's message is taken (persistMessage); TurnMemory.context waits for it"));
|
|
25
|
+
const started = entries.filter((entry) => entry.body._tag === "TurnStarted");
|
|
26
|
+
if (started.some((entry) => entry.turn !== admitted.turn || entry.id !== writer.started.data.entry))
|
|
27
|
+
return yield* Effect.fail(failure("turn.numbering", `memory took the message as ${started.map((entry) => `${entry.id} of turn ${entry.turn}`).join()}, the session stored ${String(writer.started.data.entry)} of turn ${admitted.turn}`));
|
|
28
|
+
const drafts = yield* draftsOfEntries(entries).pipe(Effect.mapError((error) => failure("memory.log", error.message)));
|
|
29
|
+
yield* writer.append(drafts.map((draft) => ({ kind: draft.kind, data: draft.data })));
|
|
30
|
+
})
|
|
31
|
+
}, session = yield* memory.open({ conversation: admitted.session.id, runId: admitted.runId, log }), reader = readerOf(session), slot = yield* Ref.make(Option.none()), openTools = Ref.get(slot).pipe(Effect.flatMap(Option.match({
|
|
32
|
+
onNone: () => Effect.fail(failure("tools.unavailable", "Tools are not open yet")),
|
|
33
|
+
onSome: (tools) => Effect.succeed(tools)
|
|
34
|
+
}))), run = RunContext.of({
|
|
35
|
+
conversation: admitted.session.id,
|
|
36
|
+
session: admitted.session,
|
|
37
|
+
runId: admitted.runId,
|
|
38
|
+
userMessage: admitted.userMessage,
|
|
39
|
+
memory: reader,
|
|
40
|
+
events,
|
|
41
|
+
tasks,
|
|
42
|
+
activate: (skills) => openTools.pipe(Effect.flatMap((tools) => tools.activate(skills, "host"))),
|
|
43
|
+
flush: writer.flush,
|
|
44
|
+
write: writer.write
|
|
45
|
+
}), requestKinds = [...MEMORY_KINDS, "request.prepared"], snapshots = yield* Semaphore.make(1), snapshot = yield* Ref.make(Option.none()), requestSnapshot = snapshots.withPermits(1)(Effect.gen(function* () {
|
|
46
|
+
const known = yield* Option.match(yield* Ref.get(snapshot), {
|
|
47
|
+
onNone: () => Ref.get(history).pipe(Effect.flatMap(Option.match({ onNone: () => readHistory, onSome: Effect.succeed })), Effect.map((entries) => ({ entries, requests: [], cursor: writer.started.seq - 1 }))),
|
|
48
|
+
onSome: Effect.succeed
|
|
49
|
+
}), stored = yield* writer.snapshot(requestKinds, known.cursor), next = {
|
|
50
|
+
entries: [...known.entries, ...yield* decoded(stored.filter((event) => MEMORY_KINDS.includes(event.kind)))],
|
|
51
|
+
requests: [...known.requests, ...stored.filter((event) => event.kind === "request.prepared" && event.data.runId === admitted.runId)],
|
|
52
|
+
cursor: stored.reduce((last, event) => Math.max(last, event.seq), known.cursor)
|
|
53
|
+
};
|
|
54
|
+
yield* Ref.set(snapshot, Option.some(next));
|
|
55
|
+
return { entries: next.entries, requests: next.requests };
|
|
56
|
+
})), claimed = yield* Ref.make(!1), replied = yield* Ref.make(!1), taken = Ref.get(message).pipe(Effect.map((state) => state._tag === "Taken" ? Option.some(state.number) : Option.none())), turnMemory = TurnMemory.of({
|
|
57
|
+
...reader,
|
|
58
|
+
strategy: session.strategy,
|
|
59
|
+
session,
|
|
60
|
+
prepareRequest: (header) => Schema.encodeEffect(ModelRequestHeader)(header).pipe(Effect.mapError((error) => failure("request.encode", error.message)), Effect.flatMap((data) => writer.append([{ kind: "request.prepared", data }])), Effect.andThen(writer.flush)),
|
|
61
|
+
requestSnapshot,
|
|
62
|
+
number: taken.pipe(Effect.flatMap(Option.match({
|
|
63
|
+
onNone: () => Effect.fail(failure("turn.unstarted", "The user's message is not persisted yet")),
|
|
64
|
+
onSome: (number) => Effect.succeed(number)
|
|
65
|
+
}))),
|
|
66
|
+
persistMessage: Effect.gen(function* () {
|
|
67
|
+
if (yield* Ref.getAndSet(claimed, !0))
|
|
68
|
+
return yield* Effect.fail(failure("turn.persisted", "The user's message is already persisted"));
|
|
69
|
+
const number = admitted.turn;
|
|
70
|
+
yield* session.record([{ _tag: "TurnStarted", userMessage: admitted.userMessage }], 0);
|
|
71
|
+
const early = yield* Ref.modify(message, (state) => [state._tag === "Waiting" ? state.early : [], { _tag: "Taken", number }]);
|
|
72
|
+
yield* early.length === 0 ? Effect.void : session.record(early.map(contextBody), 0);
|
|
73
|
+
yield* events.publish({ _tag: "turn.started", runId: admitted.runId, turn: number, userMessage: admitted.userMessage });
|
|
74
|
+
return number;
|
|
75
|
+
}),
|
|
76
|
+
context: (entry) => Ref.modify(message, (state) => state._tag === "Waiting" ? [!1, { _tag: "Waiting", early: [...state.early, entry] }] : [!0, state]).pipe(Effect.flatMap((now) => now ? session.record([contextBody(entry)], 0).pipe(Effect.asVoid) : Effect.void)),
|
|
77
|
+
persistReply: (outcome) => Effect.gen(function* () {
|
|
78
|
+
const number = yield* taken;
|
|
79
|
+
if (Option.isNone(number) || (yield* Ref.getAndSet(replied, !0)))
|
|
80
|
+
return;
|
|
81
|
+
yield* session.record([{ _tag: "TurnEnded", outcome: outcome.outcome, reply: outcome.reply }], 0);
|
|
82
|
+
yield* events.publish({ _tag: "turn.ended", runId: admitted.runId, turn: number.value, outcome: outcome.outcome, reply: outcome.reply });
|
|
83
|
+
})
|
|
84
|
+
}), opened = yield* Ref.make(!1), toolbox = TurnToolbox.of({
|
|
85
|
+
open: Effect.gen(function* () {
|
|
86
|
+
if (yield* Ref.getAndSet(opened, !0))
|
|
87
|
+
return yield* Effect.fail(failure("tools.opened", "The turn's tools are already open"));
|
|
88
|
+
const tools = yield* registry.open(session).pipe(Effect.provideService(RunContext, run), Scope.provide(scope));
|
|
89
|
+
yield* Ref.set(slot, Option.some(tools));
|
|
90
|
+
return tools;
|
|
91
|
+
}),
|
|
92
|
+
tools: openTools
|
|
93
|
+
}), promptContext = (tools, variant) => tools.active.pipe(Effect.map((active) => ({ variant, active, skills: tools.skills }))), rendered = (tools, variant, tiers) => inCaller(promptContext(tools, variant).pipe(Effect.flatMap((context) => renderSections(sections.filter(tiers), context)))), lastSystem = yield* Ref.make(Option.fromNullishOr((yield* session.entries).flatMap((entry) => entry.body._tag === "SystemPrepared" ? [entry.body.fingerprint] : []).at(-1))), systems = yield* Ref.make(new Map), turnSections = yield* Ref.make(!1), prompt = TurnPrompt.of({
|
|
94
|
+
system: (variant) => Effect.gen(function* () {
|
|
95
|
+
const key = Option.getOrElse(variant, () => ""), known = (yield* Ref.get(systems)).get(key);
|
|
96
|
+
if (known !== void 0)
|
|
97
|
+
return known;
|
|
98
|
+
const parts = yield* rendered(yield* openTools, variant, (section) => section.tier !== "turn"), text = [input.system ?? "", ...parts.map((part) => part.text)].filter((part) => part.trim().length > 0).join(`
|
|
99
|
+
|
|
100
|
+
`), fingerprint = fingerprintOf(text);
|
|
101
|
+
if (!Option.contains(yield* Ref.get(lastSystem), fingerprint)) {
|
|
102
|
+
yield* session.record([{
|
|
103
|
+
_tag: "SystemPrepared",
|
|
104
|
+
fingerprint,
|
|
105
|
+
text,
|
|
106
|
+
sections: parts.map((part) => ({ id: part.section.id, version: part.section.version, fingerprint: fingerprintOf(part.text) }))
|
|
107
|
+
}], 0);
|
|
108
|
+
yield* Ref.set(lastSystem, Option.some(fingerprint));
|
|
109
|
+
}
|
|
110
|
+
yield* Ref.update(systems, (all) => new Map([...all, [key, text]]));
|
|
111
|
+
return text;
|
|
112
|
+
}),
|
|
113
|
+
turnSections: Effect.gen(function* () {
|
|
114
|
+
if (yield* Ref.getAndSet(turnSections, !0))
|
|
115
|
+
return;
|
|
116
|
+
const parts = yield* rendered(yield* openTools, Option.none(), (section) => section.tier === "turn");
|
|
117
|
+
yield* parts.length === 0 ? Effect.void : session.record(parts.map(({ section, text }) => ({
|
|
118
|
+
_tag: "TurnContext",
|
|
119
|
+
sectionId: section.id,
|
|
120
|
+
version: section.version,
|
|
121
|
+
text
|
|
122
|
+
})), 0);
|
|
123
|
+
})
|
|
124
|
+
});
|
|
125
|
+
return Context.make(RunContext, run).pipe(Context.add(TurnEvents, events), Context.add(TurnTasks, tasks), Context.add(TurnMemory, turnMemory), Context.add(TurnToolbox, toolbox), Context.add(TurnPrompt, prompt));
|
|
126
|
+
}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Context, Effect } from "effect";
|
|
2
|
+
import type { Scope } from "effect";
|
|
3
|
+
import type { ConformanceCheck } from "../conformance.entity.js";
|
|
4
|
+
import { HarnessError } from "../harness/plugin.entity.js";
|
|
5
|
+
import type { TurnRunner } from "../ports/turn.port.js";
|
|
6
|
+
/**
|
|
7
|
+
* The turn contract, as checks any composition of the turn must pass (an
|
|
8
|
+
* `Agent`, or a host's own composition of `TurnLive` and the lifecycle):
|
|
9
|
+
* the session's writer is the bus's first subscriber; the user's message
|
|
10
|
+
* is in memory before the matcher reads the history; the reply is recorded
|
|
11
|
+
* and the turn ended exactly once on success, failure and interrupt; tasks
|
|
12
|
+
* are joined before the reply; and the host layer sees only earlier turns.
|
|
13
|
+
*
|
|
14
|
+
* `runner` is acquired afresh for each check. `services` are merged into
|
|
15
|
+
* every turn's services (the model, the `Sessions` the turns begin with, and
|
|
16
|
+
* whatever else the runner requires); each check makes its own session, and
|
|
17
|
+
* the kit adds its own IntentMatcher, which the runner's tools must consult
|
|
18
|
+
* (plugin-tool-discovery does).
|
|
19
|
+
*/
|
|
20
|
+
export declare const turnConformance: (runner: Effect.Effect<TurnRunner, HarnessError, Scope.Scope>, services?: Context.Context<never>) => ReadonlyArray<ConformanceCheck>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Context, Deferred, Effect, Fiber, Layer, Option, Ref } from "effect";
|
|
2
|
+
import { ConformanceFailure } from "../conformance.entity.js";
|
|
3
|
+
import { HarnessError } from "../harness/plugin.entity.js";
|
|
4
|
+
import { IntentMatcher } from "../ports/capability.port.js";
|
|
5
|
+
import { RunContext } from "../ports/run-context.port.js";
|
|
6
|
+
import { Sessions } from "../ports/sessions.port.js";
|
|
7
|
+
import { UserMessage } from "./user-message.entity.js";
|
|
8
|
+
const fail = (check) => (message) => Effect.fail(new ConformanceFailure({ check, message })), expect = (check, holds, message) => holds ? Effect.void : fail(check)(message), stored = (session) => session.sessions.read(session.address).pipe(Effect.mapError((error) => new HarnessError({ code: "conformance.session", message: error._tag }))), ofRun = (all, runId) => {
|
|
9
|
+
const turn = Option.flatMap(Option.fromNullishOr(all.find((event) => event.kind === "turn.started" && event.data.runId === runId)), (event) => event.turn);
|
|
10
|
+
return Option.match(turn, { onNone: () => [], onSome: (number) => all.filter((event) => Option.contains(event.turn, number)) });
|
|
11
|
+
}, names = (session, runId) => stored(session).pipe(Effect.map((all) => ofRun(all, runId).map((event) => event.kind)));
|
|
12
|
+
export const turnConformance = (runner, services = Context.empty()) => {
|
|
13
|
+
const check = (name, id, body) => ({
|
|
14
|
+
name,
|
|
15
|
+
run: Effect.scoped(Effect.gen(function* () {
|
|
16
|
+
const history = yield* Ref.make([]), matcher = Context.make(IntentMatcher, IntentMatcher.of({
|
|
17
|
+
id: "conformance",
|
|
18
|
+
version: "1",
|
|
19
|
+
match: (request) => Ref.update(history, (all) => [...all, request.history]).pipe(Effect.as({ skills: [], probabilities: Option.none(), abstained: !0 }))
|
|
20
|
+
})), sessions = yield* Option.match(Context.getOption(services, Sessions), {
|
|
21
|
+
onNone: () => fail(id)("the turn services hold no Sessions to begin turns with"),
|
|
22
|
+
onSome: (found) => Effect.succeed(found)
|
|
23
|
+
}), view = yield* sessions.create({ owner: "conformance" }).pipe(Effect.mapError((error) => new HarnessError({ code: "conformance.session", message: error._tag }))), acquired = yield* runner;
|
|
24
|
+
yield* body({
|
|
25
|
+
turn: (input, use) => acquired.turn({ ...input, services: Context.merge(Context.merge(services, matcher), input.services) }, use)
|
|
26
|
+
}, { sessions, address: { id: view.header.id, owner: "conformance" } }, history);
|
|
27
|
+
})).pipe(Effect.catchTag("HarnessError", (error) => fail(id)(`${error.code}: ${error.message}`)))
|
|
28
|
+
}), input = (session, runId) => ({
|
|
29
|
+
turn: { session: session.address, userMessage: new UserMessage({ text: `the message of ${runId}` }), runId },
|
|
30
|
+
services: Context.empty()
|
|
31
|
+
}), reply = (text) => Effect.succeed({ outcome: "completed", reply: Option.some(text) });
|
|
32
|
+
return [
|
|
33
|
+
check("the session's writer is the bus's first subscriber", "journal-first", (turns, session) => Effect.gen(function* () {
|
|
34
|
+
const outcome = yield* turns.turn(input(session, "run-1"), (turn) => Effect.gen(function* () {
|
|
35
|
+
const journaled = yield* Ref.make(!1);
|
|
36
|
+
yield* turn.events.subscribe((event) => event._tag === "host" && event.name === "conformance.probe" ? Option.some(event) : Option.none(), () => turn.flush.pipe(Effect.andThen(names(session, "run-1")), Effect.flatMap((kinds) => Ref.set(journaled, kinds.includes("conformance.probe")))));
|
|
37
|
+
yield* turn.events.publish({ _tag: "host", name: "conformance.probe", data: {} });
|
|
38
|
+
return yield* reply((yield* Ref.get(journaled)) ? "journaled first" : "not journaled");
|
|
39
|
+
}));
|
|
40
|
+
yield* expect("journal-first", Option.contains(outcome.reply, "journaled first"), "a subscriber ran before the session stored the event it received");
|
|
41
|
+
})),
|
|
42
|
+
check("the user's message is recorded before the matcher reads the history", "message-first", (turns, session, history) => Effect.gen(function* () {
|
|
43
|
+
yield* turns.turn(input(session, "run-1"), (turn) => turn.tools.match(turn.userMessage).pipe(Effect.andThen(reply("matched"))));
|
|
44
|
+
const seen = yield* Ref.get(history), last = Option.fromNullishOr(seen.at(-1)?.at(-1));
|
|
45
|
+
yield* expect("message-first", seen.length === 1, `the matcher was consulted ${seen.length} times, not once`);
|
|
46
|
+
yield* expect("message-first", Option.exists(last, (message) => message.role === "user" && JSON.stringify(message.content).includes("the message of run-1")), "the matcher's history does not end with the turn's own message");
|
|
47
|
+
})),
|
|
48
|
+
check("the reply is recorded and the turn ended exactly once, on success, failure and interrupt", "ended-once", (turns, session) => Effect.gen(function* () {
|
|
49
|
+
yield* turns.turn(input(session, "run-1"), () => reply("done"));
|
|
50
|
+
yield* Effect.result(turns.turn(input(session, "run-2"), () => Effect.fail(new HarnessError({ code: "conformance.host", message: "the host failed" }))));
|
|
51
|
+
const started = yield* Deferred.make(), fiber = yield* Effect.forkChild(turns.turn(input(session, "run-3"), () => Deferred.succeed(started, void 0).pipe(Effect.andThen(Effect.never))));
|
|
52
|
+
yield* Deferred.await(started);
|
|
53
|
+
yield* Fiber.interrupt(fiber);
|
|
54
|
+
const all = yield* stored(session), per = (runId, kind, field) => ofRun(all, runId).filter((event) => event.kind === kind).map(field).join("+"), replies = ["run-1", "run-2", "run-3"].map((runId) => `${runId}:${per(runId, "turn.reply", (event) => event.data.body?.outcome)}`), endings = ["run-1", "run-2", "run-3"].map((runId) => `${runId}:${per(runId, "turn.ended", (event) => event.data.reason)}`);
|
|
55
|
+
yield* expect("ended-once", replies.join() === "run-1:completed,run-2:failed,run-3:failed", `the replies were ${replies.join()}`);
|
|
56
|
+
yield* expect("ended-once", endings.join() === "run-1:completed,run-2:failed,run-3:interrupted", `the turns ended ${endings.join()}`);
|
|
57
|
+
})),
|
|
58
|
+
check("tasks are joined before the reply", "tasks-joined", (turns, session) => Effect.gen(function* () {
|
|
59
|
+
yield* turns.turn(input(session, "run-1"), (turn) => turn.tasks.fork("late", Effect.sleep("10 millis").pipe(Effect.andThen(turn.events.publish({ _tag: "host", name: "conformance.task", data: {} })))).pipe(Effect.andThen(reply("forked"))));
|
|
60
|
+
const kinds = yield* names(session, "run-1");
|
|
61
|
+
yield* expect("tasks-joined", kinds.includes("conformance.task") && kinds.indexOf("conformance.task") < kinds.indexOf("turn.reply"), `the task's event came ${kinds.includes("conformance.task") ? "after the reply" : "never"}`);
|
|
62
|
+
})),
|
|
63
|
+
check("the host layer is built before the message: it sees only earlier turns", "layer-before-message", (turns, session) => Effect.gen(function* () {
|
|
64
|
+
const seen = yield* Ref.make([]), layer = Layer.effectDiscard(Effect.gen(function* () {
|
|
65
|
+
const run = yield* RunContext, own = (yield* run.memory.entries).filter((entry) => entry.runId === run.runId).length, turn = yield* run.memory.turn;
|
|
66
|
+
yield* Ref.update(seen, (all) => [...all, `${run.runId}:turn ${turn}, own entries ${own}`]);
|
|
67
|
+
}));
|
|
68
|
+
yield* turns.turn({ ...input(session, "run-1"), layer }, () => reply("first"));
|
|
69
|
+
yield* turns.turn({ ...input(session, "run-2"), layer }, () => reply("second"));
|
|
70
|
+
const built = (yield* Ref.get(seen)).join("; ");
|
|
71
|
+
yield* expect("layer-before-message", built === "run-1:turn 0, own entries 0; run-2:turn 1, own entries 0", `the host layer saw ${built}`);
|
|
72
|
+
}))
|
|
73
|
+
];
|
|
74
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
declare const UserMessage_base: Schema.Class<UserMessage, Schema.Struct<{
|
|
3
|
+
readonly text: Schema.NonEmptyString;
|
|
4
|
+
}>, {}>;
|
|
5
|
+
/**
|
|
6
|
+
* What the user said to start a turn. A prompt is text sent to a model; the
|
|
7
|
+
* user's message is a value of its own, and every field or parameter holding
|
|
8
|
+
* it is named `userMessage`. Attachments may join `text` later.
|
|
9
|
+
*/
|
|
10
|
+
export declare class UserMessage extends UserMessage_base {
|
|
11
|
+
}
|
|
12
|
+
/** A `UserMessage` stored as its plain text, for records that keep a string on the wire. */
|
|
13
|
+
export declare const UserMessageFromString: Schema.decodeTo<typeof UserMessage, Schema.String, never, never>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Schema, SchemaTransformation } from "effect";
|
|
2
|
+
|
|
3
|
+
export class UserMessage extends Schema.Class("UserMessage")({
|
|
4
|
+
text: Schema.NonEmptyString
|
|
5
|
+
}) {
|
|
6
|
+
}
|
|
7
|
+
export const UserMessageFromString = Schema.String.pipe(Schema.decodeTo(UserMessage, SchemaTransformation.transform({
|
|
8
|
+
decode: (text) => ({ text }),
|
|
9
|
+
encode: (userMessage) => userMessage.text
|
|
10
|
+
})));
|
package/dist/util/json.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect,
|
|
1
|
+
import { Effect, Result, Option } from "effect";
|
|
2
2
|
/**
|
|
3
3
|
* Parse JSON with CORRUPT ≠ ABSENT semantics: malformed text logs a warning
|
|
4
4
|
* naming the source and yields `None`. Config readers fall back to their
|
|
@@ -16,4 +16,4 @@ export declare const parseJsonOption: (text: string) => Option.Option<unknown>;
|
|
|
16
16
|
* `decode`; a corrupt or undecodable LINE is skipped (append-only files
|
|
17
17
|
* corrupt at a line boundary — one bad row must not brick the history).
|
|
18
18
|
* The shared machinery of the smith memory ledger and the social ledger. */
|
|
19
|
-
export declare const decodeJsonLines: <A, E>(text: string, decode: (value: unknown) =>
|
|
19
|
+
export declare const decodeJsonLines: <A, E>(text: string, decode: (value: unknown) => Result.Result<A, E>) => ReadonlyArray<A>;
|
package/dist/util/json.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { Effect,
|
|
2
|
-
export const parseJsonWarn = (text, where) =>
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { Effect, Result, Option } from "effect";
|
|
2
|
+
export const parseJsonWarn = (text, where) => Result.match(Result.try(() => JSON.parse(text)), {
|
|
3
|
+
onFailure: (error) => Effect.logWarning(`${where}: unreadable JSON \u2014 treating as empty: ${String(error)}`).pipe(Effect.as(Option.none())),
|
|
4
|
+
onSuccess: (value) => Effect.succeed(Option.some(value))
|
|
5
5
|
}), asJsonRecord = (value) => Option.match(value, {
|
|
6
6
|
onNone: () => ({}),
|
|
7
7
|
onSome: (parsed) => typeof parsed === "object" && parsed !== null ? parsed : {}
|
|
8
|
-
}), parseJsonOption = (text) =>
|
|
9
|
-
`).filter((line) => line.trim().length > 0).flatMap((line) =>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
}), parseJsonOption = (text) => Result.getSuccess(Result.try(() => JSON.parse(text))), decodeJsonLines = (text, decode) => text.split(`
|
|
9
|
+
`).filter((line) => line.trim().length > 0).flatMap((line) => Result.try(() => JSON.parse(line)).pipe(Result.match({
|
|
10
|
+
onFailure: () => [],
|
|
11
|
+
onSuccess: (parsed) => Result.match(decode(parsed), {
|
|
12
|
+
onFailure: () => [],
|
|
13
|
+
onSuccess: (decoded) => [decoded]
|
|
14
14
|
})
|
|
15
15
|
})));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xandreed/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0-next.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The agent kernel of the new line: entities, ports, the provider-agnostic agent loop, and the session chassis — pure Effect, no IO, no provider SDKs.",
|
|
@@ -12,8 +12,7 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"
|
|
16
|
-
"effect": "3.21.4"
|
|
15
|
+
"effect": "4.0.0-rc.118"
|
|
17
16
|
},
|
|
18
17
|
"types": "./dist/index.d.ts",
|
|
19
18
|
"files": [
|