Repository navigation
feat(global): a shell performs a run's jobs one at a time, and six seams a shell asks for - #321
Merged
rami-hatoum merged 7 commits intoOct 25, 2026
Conversation
…he facts The jobs a run's steps perform, PerformJob, ModelCallJob and ToolCallJob, and performedInProcess, which performs them in this process, were not exported. A shell that performs one model call or one tool call per invocation could not hand a job out, and could not decide the next job of a run without performing the whole attempt. The main entry now exports them, with the step functions that drive a run from its facts: - stepRecordingOf(services, run, tools), what the steps of a run record through, without a performer; - stateOfTheAttempt(recording, prompt, attempt), the state folded from the attempt's calls and the content they kept, as a resumed run folds it; - jobsOf(recording, state), which decides what follows and records the start of each job it hands out, or answers the ending; - performedJob(perform, job), which performs one job and answers its JobAnswer; - answerRecorded(recording, state, answer), which records its end. It also exports what the adapter does around the steps, which such a shell does itself: beforeTheSteps, promptOfTheAttempt, requestFor, admitted and withTools before them, stepsAnswerOf and finishedOf, the ending and its record, and resumesAfterMsOf, the quiet period of a run. definitionRun now ends a run through finishedOf. @on.auto/definitions exports attemptIsLive(quietMs, resumesAfterMs), the liveness rule its resume applies. The loop of this process now makes each job's beginning and end with the same functions around its own performer, so the server is unchanged. A run driven one job at a time, its state folded from the facts before each step, records the same facts and model requests as the loop, and ends the same way (src/steps/step-jobs.test.ts). A run whose playbook keeps no content goes on across the jobs of one invocation, its state kept in memory, and ends unreadable once a later invocation folds it from its facts. A start refused because the run's cancel was recorded ends it cancelled. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A dispatched call got the effect alone, and an MCP endpoint handed the SDK its caller through a schema, so a shell could bind an invocation's fetch and identity to a request only through the request's span. ApiOptions now takes callContext(request), an Effect Context of the services a shell binds to the invocation that answers the request. Every route that dispatches provides it: - an operation route provides it to the call it dispatches; - an MCP endpoint hands it to the SDK beside the caller, and each tool provides it to the call it dispatches. The dispatcher keeps those services, so the operation reads them as it reads any service. A server that gives none provides an empty context, so the server is unchanged. src/operations/call-context.test.ts holds that an operation reads the service a call context made of a request header, over HTTP and through an MCP tool. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… option the server passes The model access imported one provider's identity library, for that provider without a key, a package the runtime does not depend on, from a module its main entry reaches. A bundle of the core elsewhere could not resolve it, and had to alias it to a module that is not found. That import, and installedPackages, which resolves the package, now live in an entry of their own, @on.auto/reasoning/installed-packages. The server passes its loadEntraIdentity to makeModelAccess. Without a loader the identity library is absent, and that provider without a key is not configured. src/providers/credential-libraries.test.ts holds that no module the main and testing entries reach imports the package, and that the new entry does. The test of model access with its defaults holds that the provider without a key is configured only when the access is given the loader. The loader answers null for no identity, where it answered undefined, so an absent identity is a value rather than a missing one. The adapter folder, past thirty modules with the new entry's, splits by concept: src/providers makes the providers' models and the model access, and src/model-calls makes one call to a model. The resolution of a model and the language model it resolves to join src/model, the check of a gateway's options src/settings, the JSON output src/schema and the unclassified error src/failure. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A ledger over streams a shell lends, rather than over a store of this package, restated the load of a stream and the composition of decisionLoop with eventAppenderOf, since the ledger made both inside makeLedger alone. streamPortsOf(streams) now answers them over any Streams, its read, its append and the most events it takes in one append: load(stream, fold), and execute(stream, decider, command, lineage?), the decision loop over the append. makeLedger is made of it. Streams is exported beside it. src/decision-loop.test.ts holds the ports over lent streams. A command is executed and the state it left is loaded. When another writer appends between the load and the append, the command is decided again on the stream it left. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…l's requests through its fetch A shell that sends the requests of each call through the invocation that performs it had to compose the tool access and the consent client again for every invocation, since each took one fetch when it was made and kept it for its whole life. CallFetch of @on.auto/operations is the fetch of one call. A shell puts it in the context it makes of a request, callContext of @on.auto/api, and composes both once: - makeToolAccess answers each of its ways through an access over the call's fetch, made the first time that fetch is lent and kept while it lives (overFetches, over a WeakMap). Its links, their token sources and the token sources of a consent it asks of the consent client, serving.fetch, send through it. A stdio link sends nothing through a fetch, so the access over a call's fetch borrows the stdio links of the access's own and starts no second process. - makeConsentClient answers hadSources, start, finish and revoke over the call's fetch, Auto's consent service and its listing among them, and a token source over the fetch serving names. The single flight of its dynamic registrations, and the registrations the store could not keep yet, are made once with the client and shared by the requests over every fetch, so one issuer still sees one registration flight within the process. Where the context holds no CallFetch, as on the server, both send through the fetch they were made with. makeConsentClient moves to src/consent/consent-making.ts, which keeps consent-client.ts within its dependencies. src/call-fetch/access-of-the-call.test.ts calls a tool through a lent fetch and through the access's own, two calls through one lent fetch that take one access and mint one token and hold no session after, and a server on stdio started once through both. consent-making.test.ts starts a consent through a lent fetch and registers once for two starts under two fetches, and src/consent-tokens/consent-calls.test.ts renews the tokens of a connection connected by consent through one. The README names what a shell pays once for each fetch it lends. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…heir loopback server @on.auto/tool-servers/testing reached the loopback server of the fakes, node:http and node:stream, and through @on.auto/secrets/testing the fake key service and parameter store, so a shell whose tests check under web-standard types built fakes of its own. The fakes' answers are now handlers of the web's Request and Response, exported from the testing entry: fakeMcpAnswers(options) with handlerAt(origin), fakeApiAnswers(options) with handler and, for a client, issuing.handlerAt(origin), and fakeConsentServiceAnswers(options) with handler, each with the records and controls of its server, beside FetchHandler and LoopbackServer. The servers that serve them on loopback, servedForConsent, servedThroughAuto, openFakeToolRun, the behaviour suite and fakeStdioServerPath move to a new entry, @on.auto/tool-servers/served-fakes, which also exports the types of what each serves. @on.auto/secrets/served-fakes takes serveFakeKms and serveFakeParameterStore the same way. Every importer takes them from there. src/testing/testing-entries.test.ts loads each entry in a process of its own and lists the modules of Node it loaded through a resolve hook, so an import of the runtime's modules and of the packages they take counts alike: the testing entry loads neither node:http nor node:stream, and the entry of the served fakes loads both. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…adline ends it A shell that hands each call to a queue loses a call with the consumer that took it. The host only cancelled such a call when its call_deadline timer fired, so the shell kept a due time of its own beside each call to dispatch it again, and bounded its lease by the longest any run takes, since StartCall.longestMs did not reach the run port. PartsOptions.dispatchedAgainAfterMs turns the dispatch again on, and the server gives none. A call's row keeps the moment it was last dispatched, dispatched_at, written as it moves to waiting. callsDueAgainOf is due work of the host that hands out, among the work that calls out, each waiting call whose moment is that long past. It asks first whether the run the call waits for is live, by attemptIsLive of @on.auto/definitions over the age of that run's last fact: while the run shows a fact within the interval the dispatch waits, its moment moved to that fact; once the run is quiet longer, or has no fact, the host performs the call again through perform, told the attempt before died, under the permits of the calls (HostExecutor.dispatchedAgain). A call that still waits moves its moment to the dispatch and is due again an interval later. Any other answer answers it. The deadline still ends it, since a cancelled row is waiting no more. The table is made with the column and its index. The README gives the rule for the interval: at least the longest gap between two facts of a live attempt, the resumesAfterMs of the capability whose runs the calls start. DefinitionRunRequest of @on.auto/coordination carries the call's longestMs. The server's nestedRuns runs in process, bounded by its capability, and takes none. The start of a call moves to src/calls/call-starts.ts with CallAttempt, which keeps host-executor.ts within its lines. The suites both stores run move from src/testing to src/suite-testing, which keeps each folder under fifteen modules, and src/suite-testing/dispatch-suite.ts dispatches calls again on SQLite and on PostgreSQL, a call whose run is still live among them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
rami-hatoum
force-pushed
the
refactor/the-core-performs-calls-outside-the-server
branch
from
October 25, 2026 09:12
0dc5cfe to
ec56df4
Compare
rami-hatoum
deleted the
refactor/the-core-performs-calls-outside-the-server
branch
October 25, 2026 10:06
rami-hatoum
pushed a commit
that referenced
this pull request
Oct 25, 2026
🤖 I have created a release *beep* *boop* --- ## [0.21.0](v0.20.0...v0.21.0) (2026-10-25) ### Features * **api:** a call reaches the context a shell makes of its request ([#321](#321)) ([8a60a21](ht tps://github.com/BeOnAuto/runtime/commit/8a60a21a57aa2ac3d9af5c465b3e5 14ee20f298a)) * **ledger:** the stream reader and writer over any streams are exported ([#321](#321)) ([8a60a21](ht tps://github.com/BeOnAuto/runtime/commit/8a60a21a57aa2ac3d9af5c465b3e5 14ee20f298a)) * **reasoning:** a shell hands out a run's jobs one at a time, from the facts ([#321](#321)) ([8a60a 21](8a60a21a57aa2ac3d9af5c4 65b3e514ee20f298a)) * **tool-servers:** the tool access and the consent client send a call's requests through its fetch ([#321](#321)) ([8a60a21](ht tps://github.com/BeOnAuto/runtime/commit/8a60a21a57aa2ac3d9af5c465b3e5 14ee20f298a)) * **workflow-host:** a waiting call is dispatched again before its deadline ends it ([#321](#321)) ([8a60a21](ht tps://github.com/BeOnAuto/runtime/commit/8a60a21a57aa2ac3d9af5c465b3e5 14ee20f298a)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
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.
What this changes
Seven seams a program that performs a run's calls outside the server asked for, each on its own terms.
stepRecordingOfandstateOfTheAttemptbuild the state from the run's facts and kept content,jobsOfanswers the jobs the state asks for or the ending,performedJobperforms one throughPerformJob, andanswerRecordedrecords its answer and folds the next state. A shell can perform one model call or one tool call per invocation and never hold an attempt in memory; the Node loop is built from the same steps and behaves as before.ApiOptions.callContextgives every operation route and MCP endpoint a context of the request, and the tool access and the consent client send a call's requests through the fetch that context carries, one access per fetch, kept while the fetch lives.served-fakesentries of the tool-servers and secrets packages.dispatchedAgainAfterMssince it was last dispatched, the host performs it again, telling the attempt before died, until it is answered or its deadline cancels it; the due time is kept in the calls table.Tests
node:httpnornode:stream; a call dispatched again and answered once.pnpm checkpasses on each commit, with the PostgreSQL suites running, and the npm package's self-check imports every subpath.BEGIN_COMMIT_OVERRIDE
feat(reasoning): a shell hands out a run's jobs one at a time, from the facts (#321)
feat(api): a call reaches the context a shell makes of its request (#321)
refactor(reasoning): one provider's identity library loads through an option the server passes (#321)
feat(ledger): the stream reader and writer over any streams are exported (#321)
feat(tool-servers): the tool access and the consent client send a call's requests through its fetch (#321)
refactor(tool-servers): the fakes' handlers are exported apart from their loopback server (#321)
feat(workflow-host): a waiting call is dispatched again before its deadline ends it (#321)
END_COMMIT_OVERRIDE