Skip to content

feat!: SharedOS sets no limits of its own - #118

Merged
DaXu999 merged 6 commits into
mainfrom
feat/unset-limits
Nov 8, 2026
Merged

DaXu999 merged 6 commits into
mainfrom
feat/unset-limits

Conversation

@DaXu999

@DaXu999 DaXu999 commented Nov 8, 2026

Copy link
Copy Markdown
Collaborator

Why

SharedOS decides whether an action from an external harness is allowed, denied or escalated. How long a turn runs and how much it does is the host's call. SharedOS still chose many limits on the host's behalf: step, tool-call and time defaults, ceilings on what a host could set, size caps in the contracts, and harness timeouts. One was invisible until it bit: Node's built-in fetch gives up after 300 s of silence, so a model that thought longer failed every attempt.

What

  • Turn and harness limits are unset unless the host sets them, and unset is no limit. Executor steps, tool calls and time; the standard loop's session close timeout; the model client's output ceiling and request timeout; the malformed-call and ignored-frame guards; an MCP harness's idle and per-call tool timeouts; the delegation chain length. Each is still settable.
  • Removed outright: the ceilings on a request's limits (1,000 steps, 10,000 tool calls, two hours), the 256-grant cap, the 512-tool cap, the 512-character escalation reason cap and its truncation, the MCP image caps, the file tools' caps, and the 10 s harness version probe timeout.
  • Contracts: every string length, list length and count is gone, with the matching maxLength in the published tool schemas. Shape checks are unchanged. The conformance execution record drops the same caps, so a long escalation reason, no longer cut at 512, stays in the record.
  • Model calls: a call worth retrying is retried ten times, 1.5 s apart (was once). With no fetch passed, calls on Node go through undici 8's own fetch with its header and body waits off, so a model that thinks past 300 s is no longer cut off. undici is imported on first use and only on Node, and uses HTTP/2 where the provider offers it. A fetch the host passes is used as given.
  • Long timeouts: a host timeout over about 24.8 days (2^31 ms) is waited out in steps instead of firing almost at once. This covers turn deadlines, the model request timeout, the MCP idle timeout and the kernel's audit write timeout.
  • Docs: the threat model, architecture, host integration and other docs now say each limit applies only when the host sets it. docs/api is regenerated.

78 files: 1,547 lines added, 1,117 removed. Of those, regenerated docs/api is 539 added and 579 removed, and tests are 406 added and 90 removed.

What a host has to update

The full list is in the CHANGELOG under Unreleased, Changed. The ones most likely to matter:

  • SharedOSExecutor has no default defaultMaxSteps (was 16), defaultTimeoutMs (was 120 000) or defaultMaxToolCalls, and the tool-call limit no longer falls back to the step limit. A host that relied on the defaults sets them.
  • MAX_EXECUTION_TIMEOUT_MS, MAX_EXECUTION_TOOL_CALLS, MAX_RESOLVED_GRANTS, ESCALATION_REASON_MAX_LENGTH and DEFAULT_MAX_DELEGATION_CHAIN_LENGTH are removed, and the grant_limit_exceeded code with them.
  • OpenAiCompatibleModelClient sends no max_completion_tokens unless maxOutputTokens is set, and has no request timeout unless requestTimeoutMs is set.
  • @aicoo/sharedos-adapters depends on undici 8.

Related

Builds on #116 (Node 22.19, which undici 8 needs) and #117, both merged. Follow-ups, not in this PR:

  • A harness branch: send prompts to Claude Code, Codex and DeepSeek through stdin, since Linux rejects a single argument over 128 KiB; and the harness restrictions SharedOS still imposes (--disallowedTools, --no-builtin-tools).
  • A limit-notice branch: tell the model its limits and when one is reached.

Checks

  • pnpm check is green in a plain clone on Node 22.19 and on Node 24: format, typecheck, vitest 1170/1170, test:release 7/7, docs:api:check and conformance:check. pnpm pack:check and pnpm pack:lint pass.
  • Verification agents pushed values 100 to 1,000 times past the old caps through contracts, runtime, adapters, MCP and core, then ran the same tests against the old code, where they failed.
  • 310 s of silence on Node 22.19 with undici 8.12.4: the built-in fetch failed at about 302 s. The model client's default answered at 310 s, both before headers and mid-body, over HTTP/1.1 and HTTP/2. Aborting the turn cancels an in-flight call within about 1 ms.
  • Each fix has a test that fails on the old code. Repeat runs on both Node versions all passed: the fetch test 20 of 20, and the authority, fetch and client tests together 15 of 15.

Every turn and harness limit SharedOS chose on a host's behalf is now
unset unless the host sets it, and an unset limit is no limit. SharedOS
decides whether each action is allowed, denied or escalated; how long a
turn runs and how much it does is the host's call.

Unset by default, and still settable by a host: the executor's step,
tool-call and time limits (the tool-call limit no longer falls back to
the step limit); the standard loop's session close timeout; the model
client's output ceiling and request timeout; the malformed-call and
ignored-frame guards; an MCP harness's idle timeout and per-call tool
timeout; the delegation chain length.

Removed outright: the ceilings on a request's limits (1,000 steps,
10,000 tool calls, two hours), the 256-grant cap, the 512-tool cap,
the 512-character escalation reason cap and its truncation, the image
count and size caps on MCP results, the file tools' caps on results,
grep context, path segments and commit messages, and the 10 s timeout
on a harness version probe. A turn deadline longer than one timer can
hold is waited out rather than firing early.
Every string length, list length and count in the contracts is gone:
identifiers and path segments, resource paths, capabilities, actions and
purposes per grant, tool names and descriptions, error messages, reach
lists, namespace catalogues, tool policy lists, message parent ids, the
MCP execution token's claims and the file tools' remaining fields. The
JSON schemas published for the message and file tools drop their
matching maxLength. Shape checks are unchanged. Whatever carries a value
(a filesystem, a vendor's tool name rules, an HTTP server) still applies
its own limit, and SharedOS no longer adds a stricter one.

The OpenAI-compatible client now retries a failed call that is worth
retrying ten times, 1.5 s apart, where it retried once.
The record schema still carried the old contract caps: purpose 512,
grant ids and snapshots 256, exposed and requested tools 512,
decisions and operations 4,096, events 8,192, and an escalation
reason of 512.

Since the runtime stopped cutting reasons at 512, a longer ask failed
EscalationAskedSchema, which assemble reads with safeParse, so the
ask vanished from the record and the judge graded the turn as never
having asked. The other caps made assembleExecutionRecord throw on
turns the kernel now accepts.

All are dropped. A test lifts a 100,000-character reason whole; it
fails on the old schema.
The threat model, architecture, release readiness, host integration,
MCP toolshare, docs README, errors table and the adapters and mcp
READMEs still described step, tool-call, time, size and cleanup
limits as always present. They now say each applies only when the
host sets it.
Node's built-in fetch gives up after 300 s without response headers
and after 300 s without body data. A model that thought longer than
that failed the call, and every retry failed the same way.

With no fetch passed, the model client now calls through undici's
own fetch with an agent whose headers and body waits are switched
off, so only the host's request timeout or the turn's deadline ends
a call. undici is imported on first use and only on Node, so the
package's main entry still loads elsewhere. A fetch the host passes
is used as given.

undici 8, on the Node 22.19 floor. It uses HTTP/2 where the provider
offers it, and the same options switch the waits off there.
One timer holds at most 2^31 - 1 ms (about 24.8 days). Asked for more,
the platform fires it almost at once, or refuses it. A turn's deadline
already waited out a longer delay in steps; three host-set timeouts did
not:

- the model client's requestTimeoutMs failed every model call, retries
  included;
- an MCP harness launch's idleTimeoutMs closed a session harness's
  input almost as soon as it opened;
- the kernel's auditWriteTimeoutMs treated any audit write after an
  effect that took more than a millisecond as timed out.

The step-wise wait moves from the runtime into core's internal helpers,
with an option to leave its timers unreferenced, and all three use it.
The model client's timeout still aborts with a TimeoutError and still
does not keep the process running on its own; it is now also cleared
once the request ends.
@DaXu999
DaXu999 merged commit f490809 into main Nov 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant