Repository navigation
refactor: release replaced runtimes on switchModel; name service constructors - #65
Merged
K-Mistele merged 3 commits intoOct 15, 2026
Merged
Conversation
…tructors - fold-core: each root runtime gets its own child of the session scope. A successful switchModel closes the replaced runtime's scope (its provider client and layers); a failed one closes the new runtime's. Previously both stayed open until the session closed. - fold-agent: SessionTitleGenerator service, provided per session with the output store and log, builds the title model once instead of providing a LanguageModel layer for every title. - fold-agent: loadSessionSummary reads entries with readJsonlEntries instead of building a whole JSONL EventLog layer. - fold-core: drop the SkillSource service key; nothing provided or yielded it. - Name constructors: Photon, ManagedBinaries and OutputStore put make on the service key with layer = Layer.effect(this, this.make); fold-core layer files export makeX beside each live layer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> HumanLayer-Session: https://app.dev.codelayer.gg/sessions/01a14171-1b19-772e-812c-382bdc40b824
K-Mistele
commented
Oct 15, 2026
| const titleServices = yield* Effect.context<FileSystem.FileSystem | HttpClient.HttpClient>() | ||
| const titles = yield* SessionTitleGenerator | ||
| // The summary index refresh runs after later sends, which run without this service, so take it now. | ||
| const indexServices = yield* Effect.context<FileSystem.FileSystem>() |
Contributor
Author
There was a problem hiding this comment.
Please yield this instead of using context.
…ration withGeneratedTitles captured FileSystem with Effect.context only to refresh the summary index right after each title. listSessionSummaries already rebuilds any record whose log changed, and the title append changes the log, so the eager refresh only saved one rebuild (which the next turn's append would invalidate anyway). Remove it and the now-unused refreshSessionSummaryIndex; withGeneratedTitles now needs only SessionTitleGenerator. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> HumanLayer-Session: https://app.dev.codelayer.gg/sessions/01a14171-1b19-772e-812c-382bdc40b824
Follow the RcMap post's pattern: every service this PR touches declares its constructor inline as the `make` option of `Context.Service`, with `static readonly layer = Layer.effect(this, this.make)` (or a function of the make's arguments). No separate makeX effects or liveXLayer constants remain. - fold-core: the ToolRuntime, AgentRuntime, Session, HookRunner, and AgentEvents implementations move into their service modules; the *Layer.ts files are deleted. ToolEventSink, SessionControls, and Profiles inline make the same way. Callers use X.layer. - fold-agent: ManagedBinaries, OutputStore, and SessionTitleGenerator inline make (Photon already did). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> HumanLayer-Session: https://app.dev.codelayer.gg/sessions/01a14171-1b19-772e-812c-382bdc40b824
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follows a review of fold-core and fold-agent against the
make/layerpattern from the RcMap post. It fixes one bug, removes two internal provides, and gives each service constructor a name.Bug:
switchModelkept replaced runtimes open (fold-core)Root runtimes were built into the session scope, so every model switch left the old runtime's provider HTTP client and layers open until the session closed. A failed switch also left the new runtime open.
Now each root runtime gets its own child of the session scope (
OpenSession.ts,provisionRootRuntime):New test:
SwitchModel.vi.test.ts› switchModel releases the replaced runtime…. It fails onmain(expected [] to deeply equal ['first']) and passes here.Fewer internal provides (fold-agent)
SessionTitleGeneratorservice. It builds the title model once per session. Before, every title provided a freshLanguageModellayer. The service is provided per session inLaunch.ts, next to the output store and the JSONL log, andwithGeneratedTitlesyields it.Effect.contextcapture for titles.withGeneratedTitlesused to captureFileSystemonly to refresh the summary index after each title.listSessionSummariesalready rebuilds any record whose log changed, and the title append changes the log, so that refresh andrefreshSessionSummaryIndexare gone. The index now updates on the next listing, which the TUI runs after every durable change. The onlyEffect.contextleft in fold-core and fold-agent isSession.open's capture of the tools' genericR, whose services can't be known ahead of time.readJsonlEntries(path). It yieldsFileSystemand reads the log.loadSessionSummarynow uses it instead of building a whole JSONLEventLoglayer (Ref,PubSub,Semaphore) just to read one file.Cleanups
SkillSourceservice key (fold-core). Nothing provided or yielded it. TheSkillSourceServicetype stays.makeinlined into every service class, following the post. Each service this PR touches is declared asclass X extends Context.Service<X, Shape>()('id', { make: Effect.gen(...) })withstatic readonly layer = Layer.effect(this, this.make)(or(args) => Layer.effect(this, this.make(args))for services that take config). No separatemakeXeffects orliveXLayerconstants remain.ToolRuntime,AgentRuntime,Session,HookRunner, andAgentEventsimplementations move into their*Service.tsmodules, and the*Layer.tsfiles are deleted.ToolEventSink,SessionControls, andProfilesfollow the same pattern. Callers now useX.layer(for exampleliveToolRuntimeLayerbecomesToolRuntime.layerandlayerHookRunner(hooks)becomesHookRunner.layer(hooks)).Photon,ManagedBinaries,OutputStore, andSessionTitleGenerator.layerOutputStore(opts)becomesOutputStore.layer(opts).git diff -w).Left out on purpose
layerCodingToolServicesandlayerJsonlNodestay. They're documented in the README as helpers a host calls in its own startup code, which is where wiring belongs.Launch.tskeepsLayer.build+Effect.provideContext(sessionServices). That provides a built layer; it doesn't capture the environment.Effect.provide(layer)would close the session's services whenlaunchSessionreturns, but they must live as long as the session, as the README documents.Verification
bun run lint,bun run format:check, andbun run typecheckall pass.bun run testpasses everywhere except 2 live tests infold-agent/test/Codex/CodexLive.vi.test.ts. These run against the real Codex backend with local~/.fold/auth.jsoncredentials, which reject the model ('gpt-5.5' model is not supported when using Codex with a ChatGPT account). They're skipped in CI and unrelated to this change.🤖 Generated with Claude Code