Skip to content

Commit db867f8

Browse files
authored
Merge pull request #1118 from cacheplane/codex/langgraph-state-values
feat: expose LangGraph state through native snapshots
2 parents 192f94e + 78df2f1 commit db867f8

35 files changed

Lines changed: 1825 additions & 223 deletions

‎fixtures/react-parity/README.md‎

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,13 @@ errors, read-only reconciliation after uncertain failures and fixed function-too
1313
execution. The private session also supports explicit fixed-thread history loading
1414
when its transport can read history. The latest checkpoint replaces the transcript;
1515
equal reads preserve identity, failures preserve the prior snapshot, and loaded
16-
tools never execute. Core contracts and native binding implementations are unchanged.
16+
tools never execute. Private snapshots now include broad readonly application
17+
`values`, published atomically with messages. Full root state, history and conclusive
18+
correlated recovery replace the map; unchanged nested data retains identity, and
19+
token-only updates reuse it without traversal. `undefined` is unobserved state and
20+
`{}` an observed empty map. Child/update/custom/live-interrupt envelopes are ignored.
21+
Core contracts are unchanged; native structural signatures preserve the concrete
22+
snapshot extension and tool inference, with the same receiver and lifetime behavior.
1723
Angular and React borrow the app-owned session and observe it through their native
1824
lifecycles. The installed consumers run ten scenarios each: inert mount, explicit
1925
history load, equal refresh, empty replacement, text, weather tool
@@ -22,8 +28,9 @@ unmount followed by explicit disposal and an aborted post-disposal submission.
2228
Five live component submissions plus one tool continuation produce six exact wire
2329
requests, with one handler invocation and zero page errors or unexpected requests.
2430
The three explicit loads make three history reads and no run requests or handler
25-
calls. This is partial T10 coverage: thread switching, pagination, branching, full
26-
backend state and interrupt resume remain outside this proof.
31+
calls. This is partial T09/T10 coverage: thread switching, pagination, branching,
32+
state writes, application-schema inference and interrupt resume remain outside this
33+
proof. The factory and snapshot extension remain private.
2734

2835
A local HTTP/SSE fixture serves production-built apps and writes real held response
2936
bytes. Browser assertions observe incremental DOM text and a native response-close
@@ -33,12 +40,12 @@ exercise that subscription replay. See [runtime/README.md](./runtime/README.md)
3340
reproduction and [runtime/evidence.json](./runtime/evidence.json) for fresh commands,
3441
counts, source provenance, cleanup assertions and limitations.
3542

36-
The current inventory has **1,453 records**: the historical 1,438 plus ten private
43+
The current inventory has **1,455 records**: the historical 1,438 plus twelve private
3744
runtime production sources, three testing helpers, a runtime Vitest config and its
3845
type-test config asset. Public export occurrences remain 550 with 514 distinct local
3946
definitions. The original runtime extraction changed fourteen existing export
40-
records' declaration/import text; history loading adds two private sources and
41-
changes no legacy public export records. There are no legacy export-name additions
47+
records' declaration/import text; history loading and values observation each add
48+
two private sources and change no legacy public export records. There are no legacy export-name additions
4249
or removals. Existing task assignments
4350
are preserved; touched extraction/configuration subsets are in progress, not whole
4451
T03–T16 completion. Core and native package contracts remain outside this legacy
@@ -218,7 +225,7 @@ burst streams and repeated agent/thread disposal.
218225

219226
The foundation branch was `codex/react-support-baseline`; the runtime branch is
220227
`codex/shared-runtime-quality`, based on `bdcc22ed31aa94f420077e046e88e1481088d453`.
221-
The history-loading increment is `codex/langgraph-history-loading`; its verified
228+
The current values increment is `codex/langgraph-state-values`; its verified
222229
base and working-source fingerprint are recorded in `runtime/evidence.json`.
223230
The local maintenance
224231
branch `codex/angular-maintenance-v0.2` points to released tag `v0.2.0`
@@ -236,7 +243,8 @@ research documents. It is a scope map, not evidence that the tasks are complete.
236243
T01/T02 describe the foundation increment. The current G1 proof is deliberately limited
237244
to shared LangGraph text streaming and fixed function-tool execution with borrowed native
238245
Angular and React bindings: the runtime owns execution while each binding observes
239-
it. Explicit fixed-thread history loading now covers a further subset of T10.
246+
it. Explicit history loading and application-values observation cover further
247+
subsets of T09/T10.
240248
Renderer reuse and SSR are deferred gates, alongside the broader T01–T39 map.
241249
This bounded runtime proof does not establish complete migration parity.
242250

‎fixtures/react-parity/runtime/README.md‎

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,16 @@ or backend SDK; Angular installs no React or backend SDK. Framework/compiler
2727
versions come from the root lockfile. Contract probes compile the installed
2828
public entries with `strict` and `skipLibCheck:false`, standard DOM signals, and
2929
no workspace aliases. Negative probes check names, arguments, results and deep
30-
readonly types directly on each binding's inferred snapshot.
30+
readonly types directly on each binding's inferred snapshot, including broad
31+
backend values without application-schema inference.
3132

3233
`runtime-entry.ts` is development-only composition around private `createSession`,
3334
the production `FetchStreamTransport`, and the real LangGraph SDK. A focused
3435
TypeScript check resolves its public core imports against the installed tarball
35-
declarations, then emits its narrow annotated `AgentSession<FixtureTools>` return
36-
type with an optional fixture `load` capability. Vite bundles the private backend
36+
declarations, then emits its narrow annotated fixture return type. It replaces
37+
the core getter with `Omit<AgentSession<FixtureTools>, 'getSnapshot'>` and a concrete
38+
snapshot getter, avoiding an intersected overload that would hide `values` from
39+
inference; `load` remains optional. Vite bundles the private backend
3740
and SDK into temporary ESM, externalizing
3841
`@threadplane/core` and `@threadplane/core/tools`. Only that JavaScript bundle and
3942
entry declaration are copied into each installed consumer. No private TypeScript,
@@ -48,6 +51,27 @@ transport owns its retry policy. Canonical updates may replace or remove pending
4851
tool calls for the same assistant message while retaining other messages' calls
4952
and completed results.
5053

54+
The private `LangGraphSnapshot.values` is a broad
55+
`Readonly<Record<string, PlainValue>> | undefined`. It is observed application data,
56+
not a validated application schema. `undefined` means no current values map is
57+
observed; `{}` means a root record was observed with no application fields. Root
58+
`values` and `checkpoints` records, explicit history, and conclusive recovery
59+
correlated to the attempted run replace the whole map, including deleted fields.
60+
`messages` and `__interrupt__` are excluded. Missing history values clear the map
61+
to `undefined`. Child streams, node updates, custom events and live interrupt
62+
envelopes do not replace it.
63+
64+
Messages and values publish together as one owned immutable snapshot. Equal maps
65+
retain identity, changed maps share unchanged nested branches, and token-only
66+
updates reuse the owned map without traversing it. This adds no I/O. Narrow SDK
67+
normalization prevents raw data fields from overriding protocol type/namespace;
68+
`messageMetadata` selects delta text semantics only for actual message events.
69+
The application field remains observable as data. The native bindings infer
70+
the concrete snapshot through structural getter/subscription signatures while
71+
retaining typed tool results, method receivers and borrowed lifetime semantics.
72+
The factory and snapshot extension remain private; state writes, application
73+
schema inference, SSR and package-root cutover remain outside this slice.
74+
5175
The private `LangGraphSession` offers `load({ signal })` only when its transport
5276
supports history reads. Loading is explicit: construction, mount and subscription
5377
perform no I/O. The latest checkpoint authoritatively replaces the transcript,
@@ -63,12 +87,12 @@ History is observation only: loading never executes pending tools. Execution
6387
deduplication survives a load, while locally authored result provenance is cleared.
6488
Persisted ToolMessage strings remain transcript text rather than becoming typed
6589
handler results, including on later stream replay. This is a fixed-thread history
66-
subset of T10, not thread switching, pagination, branching, full backend state,
90+
subset of T10, not thread switching, pagination, branching, state writes,
6791
interrupt resume, SSR, or a public LangGraph package cutover. Core public contracts
68-
and the native binding implementations are unchanged.
92+
are unchanged; the native signatures now retain the concrete snapshot extension.
6993

7094
The native fixtures expose Load, Send, Tool, Error, Hold and Stop buttons plus text,
71-
transcript, load completion/error, status, tool result, delivery, submission and
95+
transcript, values, load completion/error, status, tool result, delivery, submission and
7296
handler count outputs. A single app-owned
7397
session is created outside component lifetime and outside React's StrictMode
7498
tree; owner buttons perform framework unmount and explicit session disposal.
@@ -89,6 +113,10 @@ and no run requests or handler calls. Every completed load must leave its visibl
89113
error output empty, so retained text cannot conceal a failed equal refresh. Both
90114
registered handlers increment the same counter if executed. Request bodies check
91115
the catalog and actual serialized ToolMessage payload.
116+
Values assertions distinguish unobserved from empty state, show loaded application
117+
fields, and verify replacement/deletion across root, tool, held and reused runs.
118+
Separate native component tests make four history reads to cover a values-only
119+
refresh with unchanged messages; installed browser scenarios still make three.
92120

93121
A small in-process HTTP fixture serves only built artifacts and the expected
94122
LangGraph run/history routes on dynamic port 0. The held response writes an actual SSE

‎fixtures/react-parity/runtime/angular-app.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const submit = (input: string) => { submissions += 1; return session.submit(inpu
2323
<output aria-label="Status" data-testid="status">{{ snapshot().status }}</output>
2424
<output aria-label="Text" data-testid="text">{{ view().text }}</output>
2525
<output aria-label="Transcript" data-testid="transcript">{{ view().transcript }}</output>
26+
<output aria-label="Application values" data-testid="values">{{ view().values }}</output>
2627
<output aria-label="Loads finished" data-testid="loads-finished">{{ loadsFinished() }}</output>
2728
<output aria-label="Load error" data-testid="load-error">{{ loadError() }}</output>
2829
<output aria-label="Error" data-testid="error">{{ view().error }}</output>

0 commit comments

Comments
 (0)