chore: merge environment state serve (6b8db198) into environment state writes

This branch already refuses a loaded state that data/get would write past the intake limit, with the size kept incrementally, so its store is kept; the serving branch's test and docs come along.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Edgar ArakelyanandClaude Opus 5.5 committed 2026-10-20 21:10:13 -07:00
commit fc403d98d4
3 files changed
+50 -3

No files matched your search

+4 -2
View File
@@ -488,8 +488,10 @@ within 30 seconds, including reading the body; `mimeType`, when given, is
`application/json`. A `file://` URL naming another host, or a UNC path such as
`file:////server/share/state.json`, is refused before anything is opened, so a
load never reaches a network share. A loaded document is at most 32
MiB; an inline part also stays within the 4 MiB request body bound, so larger
states arrive by `uri`. Anything else, including `{"parts": []}`, is `-32602
MiB, and so is the state as `data/get` writes it back with the members a
document may omit (`types`, `state_evidence_id`, record citations), so a
reported state always loads again; an inline part also stays within the 4 MiB
request body bound, so larger states arrive by `uri`. Anything else, including `{"parts": []}`, is `-32602
invalid params`; a download that fails or answers a non-2xx status is
`-32000`. Errors never echo the uri or the content; a file or download failure
names its category in `error.data.reason`: `not-found`, `permission`,
@@ -77,7 +77,9 @@ this increment: write tools are not served from the state.
`mimeType` when given is `application/json`) and `data/get` (`{parts:
[{kind: "data", data: rea_state}]}`). A `file://` uri whose host is neither
empty nor `localhost`, or whose path is a UNC path, is `-32602` before
anything is opened. Anything else is `-32602`, including empty `parts`; a
anything is opened. A document that `data/get` would write back past 32 MiB,
once it adds the members a document may omit, is `-32602`, so a reported
state always loads again. Anything else is `-32602`, including empty `parts`; a
failed download is `-32000`; errors never echo a uri or content and name a
coarse failure category in `error.data.reason`. Calls run one at a time in
arrival order across requests; every message of a batch is queued when the
@@ -28,6 +28,7 @@ import {
loadEnvironmentState,
prepareServedState,
} from "./environmentState.js";
import { STATE_INTAKE_LIMIT_BYTES } from "./environmentStateDocument.js";
import { EnvironmentStateStore } from "./environmentStateStore.js";
const fixture = environmentStateServeFixture();
@@ -373,6 +374,48 @@ describe("EnvironmentStateStore", () => {
expect(refused.ok).toBe(false);
expect(store.snapshot()).toEqual(before);
});
it("refuses a document that data/get would write back past what data/add loads", () => {
const store = new EnvironmentStateStore(state);
const { rea_state: held } = store.snapshot();
// Without the members data/get adds back, the document fits only until they return.
const document = {
rea_state: {
version: held.version,
entities: held.entities.map(({ type, collection, id, fields }) => ({
type,
collection,
id,
fields: { ...fields },
})),
documents: held.documents,
},
};
const entity = document.rea_state.entities[0]!;
const { fields } = entity;
const idField = state.types.find(
(type) => type.collection_template === entity.type,
)?.id_field;
const text = Object.keys(fields).find(
(field) => field !== idField && typeof fields[field] === "string",
)!;
fields[text] = `${String(fields[text])}${"a".repeat(
STATE_INTAKE_LIMIT_BYTES -
Buffer.byteLength(JSON.stringify(document), "utf8"),
)}`;
expect(Buffer.byteLength(JSON.stringify(document), "utf8")).toBe(
STATE_INTAKE_LIMIT_BYTES,
);
const before = store.snapshot();
const replaced = store.replace(document);
expect(!replaced.ok && replaced.error.issues).toEqual([
expect.objectContaining({
reason: "out_of_range",
maximum: STATE_INTAKE_LIMIT_BYTES,
}),
]);
expect(store.snapshot()).toEqual(before);
});
});
describe("rea-state/v1 validation", () => {