Repository navigation
Align main with CM 0.17.1 and Membrane 0.6, preserving deferred-write recovery #643
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d6dd2c6
67d63dd
f62ac15
9c21933
b94da58
cbda885
44334f0
d9b3d16
f8c57ca
c16727d
b703ffd
dd378fe
76bc135
9aa67cb
8697f4d
3107edc
fc64381
ad3d535
f0a7c43
73477a8
e096285
aa2d19b
72134b8
2e7526a
35eae12
26e66c0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,7 +15,7 @@ jobs: | |
| runs-on: ${{ matrix.os }} | ||
| # Without a limit, a step that hangs leaves the check pending until | ||
| # GitHub's six-hour limit. Passing runs take under seven minutes. A | ||
| # test file stuck under Node 20, or a test stuck under Node 24, fails | ||
| # test file stuck under Node 22.12, or a test stuck under Node 24, fails | ||
| # sooner and by name, through npm test's --test-timeout. | ||
| timeout-minutes: 20 | ||
| strategy: | ||
|
|
@@ -24,11 +24,17 @@ jobs: | |
| # node_modules tree records only that machine's native binaries, so a | ||
| # lock made on macOS breaks Linux installs and vice versa. Each runner | ||
| # catches the lock the other's platform produced. Tests run on ubuntu. | ||
| # Node: the engines floor and current LTS — stdio flushing semantics | ||
| # around process exit differ between versions and platforms. | ||
| # Node: the engines floor, pinned exactly; the newest Node the range | ||
| # admits; and, between them, every maintained major line (24), as | ||
| # membrane and context-manager test theirs. stdio flushing semantics | ||
| # around process exit differ between versions and platforms. macOS runs | ||
| # the two ends only. | ||
| matrix: | ||
| os: [ubuntu-latest, macos-latest] | ||
| node: [20, 24] | ||
| node: ['22.12.0', 24, latest] | ||
| exclude: | ||
| - os: macos-latest | ||
| node: 24 | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | ||
|
|
@@ -61,7 +67,7 @@ jobs: | |
| # leaves running, such as a late arrival after stop() ("Queue is | ||
| # closed"). These files run unforced so that class fails here, not in a | ||
| # later PR's qualification. They get npm test's --test-timeout too: | ||
| # under Node 20 it fails a file whose leftover work keeps it alive, by | ||
| # under Node 22.12 it fails a file whose leftover work keeps it alive, by | ||
| # name, and under Node 24 the job's limit ends such a file. | ||
| - name: Test unforced (work in flight at stop) | ||
| run: node --test --test-timeout=600000 dist/test/stop-with-work-in-flight.test.js dist/test/stop-with-host-operations.test.js dist/test/conversation-routing.test.js | ||
|
|
@@ -78,9 +84,17 @@ jobs: | |
| # hours: above the check's own limits (10 minutes each for npm pack and npm | ||
| # install, a minute for each load), so the check names what hung. | ||
| pack-check: | ||
| name: Packed release installs on its own | ||
| name: Packed release installs on its own (node ${{ matrix.node }}) | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 40 | ||
| # The engines floor and current LTS: the check's load hook goes in through | ||
| # register below Node 22.15 and through registerHooks from there, so each | ||
| # branch a host takes runs before a merge, as publish.yml runs both before | ||
| # a release. | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| node: ['22.12.0', 24] | ||
|
Comment on lines
+87
to
+97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Medium severity, maintainability. Renamed and re-matrixed CI jobs change the required status-check contexts. The pack-check job is renamed from 'Packed release installs on its own' to '… (node ${{ matrix.node }})'. The Build & Test matrix drops node 20, which removes the 'Build & Test (…, node 20)' contexts and adds new ones. If branch protection requires the old names, every PR waits forever on checks that never report. The new 22.12.0/latest contexts are also not enforced until a maintainer edits the protection settings. The PR only adds a CONTRIBUTING note and leaves the mismatch for after merge. Either keep the old job names, for example a stable name for one matrix leg, or coordinate the protection change with the merge. |
||
|
|
||
| steps: | ||
| - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0 | ||
|
|
@@ -90,7 +104,7 @@ jobs: | |
| - name: Setup Node.js | ||
| uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0 | ||
| with: | ||
| node-version: 24 | ||
| node-version: ${{ matrix.node }} | ||
|
|
||
| # As publish installs, so a build that came to need a dependency's | ||
| # install script fails here, before a tag, rather than in publish. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| - A maintenance call agent-framework withdraws, under a surgery's hold or | ||
| once `stop()` has begun, no longer counts against a chunk's compression | ||
| (#549). `MaintenanceWithdrawnError` now extends context-manager's | ||
| `CompressionWithdrawn`, so context-manager rethrows it unchanged from every | ||
| rung of its compression fallback ladder and its merge path, recording no | ||
| outcome, attempt or quarantine event. Before, a call withdrawn after a | ||
| chunk's canonical call was refused was recorded as that rung's provider | ||
| error, and once every rung had failed, the chunk's request family was | ||
| quarantined, with its alert. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| - **Hosts:** agent-framework now depends on membrane `^0.6.0` and | ||
| context-manager `^0.18.0`, where it depended on `^0.5.87` and `^0.14.1`. | ||
| context-manager 0.18.0 requires membrane `^0.6.0` itself, so the two | ||
| install one membrane between them. What reaches a host through | ||
| agent-framework, from those releases' notes: | ||
| - **Your own membrane:** a host that builds its own `Membrane` and hands it | ||
| to agent-framework raises its own `@animalabs/membrane` range to | ||
| `^0.6.0`. context-manager counts a compression attempt's prompt through | ||
| that instance, and with an older membrane it counts Gemini's cached input | ||
| twice (below 0.5.81, OpenAI's too, and OpenRouter's on routes that report | ||
| OpenAI's `cached_tokens`), so a compression fallback that fits can be | ||
| rejected at admission and its chunk quarantined (context-manager 0.16.0). | ||
| - **Your own context-manager:** a host that constructs strategies from its | ||
| own `@animalabs/context-manager` must also raise that range to `^0.18.0`, | ||
| matching agent-framework's dependency, and verify that installation has | ||
| one compatible copy. Leaving the host on 0.14.x installs two copies and | ||
| hands an older strategy to a newer manager; strategy interfaces and | ||
| compression-pause/quarantine behavior differ between those versions. | ||
| - **The kv-unified hysteresis certificate is on by default** for a | ||
| strategy built from a recipe: a `kvUnified` block without | ||
| `hysteresisCertificate` takes the certified exit, and | ||
| `hysteresisCertificate: false` restores the full solver on every compile | ||
| (context-manager 0.15.0). | ||
| - **Content context-manager won't store** is refused where agent-framework | ||
| writes it, as the entry on malformed content says. | ||
| - **Stored history:** content an older context-manager stored that doesn't | ||
| match `ContentBlock` is now read mended, or left out, with one line per | ||
| message naming each place, where some such content made every compile | ||
| throw. A `tool_use` stored with `input: null`, as membrane 0.5.87's | ||
| OpenAI-family adapters gave it for the arguments `null`, reads as `{}` | ||
| with `null` in `unparseableInput`. A request compiled from such a message | ||
| can change there, so a provider's prompt cache misses from that point, | ||
| once (context-manager 0.16.0). | ||
| - **Tools given `null` arguments:** an OpenAI-family tool call whose | ||
| arguments are the text `null` reaches the tool as `{}`, where it reached | ||
| it as `null`. membrane keeps the arguments' text on the `tool_use` block | ||
| as `unparseableInput` (membrane 0.6.0). | ||
| - **Anthropic streams:** after a stream's first event, SSE `ping` | ||
| keepalives now count as activity for its idle deadline, so a stream that | ||
| keeps sending only pings no longer ends at it (membrane 0.6.0). | ||
| - **Media notes:** two of the notes membrane writes in place of media it | ||
| doesn't send now say the model isn't seeing it. The first request after | ||
| upgrading carries the new text wherever it renders such a note, so a | ||
| provider's prompt cache misses once, from the first such note on | ||
| (membrane 0.6.0). | ||
| - **Caller-supplied blob references:** context-manager 0.16.0 and later refuse | ||
| `blob_ref` writes with `MalformedContentError`; these references are | ||
| store-internal. Read with blobs resolved (the default) before copying a | ||
| body into a new write. An unresolved read is not a write-ready payload. | ||
| - **Compression quarantine status:** callers that inspect or compare | ||
| `getCompressionQuarantineStatus()` must account for its `spans` field, | ||
| including an empty array when no spans are quarantined. | ||
| - **kv-unified policy:** context-manager 0.17 adds age bands, joint latent | ||
| demand and the subtree engine. Explicit recipe overrides remain important: | ||
| `policy.ageBandSteps: 0` requests exact ages, `latentDemandMode: 'individual'` | ||
| chooses independent ranking, and `engine: 'dag'` selects the DAG engine. | ||
| - **Merges across an unowned message:** the floor is 0.18.0, not 0.17.x, | ||
| because before it a merge could group the summaries on either side of a | ||
| message no chunk owns (a raw pin, a message an unpin freed, or a head | ||
| window `resetHeadWindow` left mid-stream), and under kv-unified without | ||
| `treeifyNonContiguousSummaries` or `preserveGapBearingSummaries` every | ||
| compile after it threw `CanonicalForestError`. A store already holding | ||
| such a summary loads as before (context-manager 0.18.0, #287). | ||
| - **`close()` is final:** after a context manager's `close()`, every | ||
| operation that writes or calls into its strategy throws, with an | ||
| app-owned store too; open a new manager on the store to go on with it | ||
| (context-manager 0.18.0). | ||
| - **Compression debt:** `getCompressionDebt()` answers `unknown` when it | ||
| can't read what decides its state, with `null` for each count it couldn't | ||
| compute and an operator-facing `error`. A host that shows the report | ||
| treats `unknown` as needing attention and never shows a `null` count as | ||
| 0 (context-manager 0.18.0). | ||
| - **Strategy subclasses:** `AutobiographicalStrategy`'s protected | ||
| `pruneToolEntries` is removed; override `toolPruning` or `prunedForSend` | ||
| instead (context-manager 0.18.0). | ||
| - The rest is in membrane 0.6.0's notes and context-manager 0.15.0's, | ||
| 0.16.0's, 0.17.x's and 0.18.0's. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| - Content context-manager won't store is refused where it's written, and a deferred write that context-manager refuses no longer holds its agent's later writes. From context-manager 0.16.0, a write whose content doesn't match `ContentBlock` is refused with a `MalformedContentError` (context-manager #184). Before it, such content was stored as given, and a compile that read it could throw. | ||
| - **Writes and edits through the framework:** a module's write, and its edit of a write still held for a turn's end, throw `MalformedContentError`, naming each place, as the store's own write and edit do. They throw whether the write would be stored at once or deferred while a turn runs, where a deferred one used to be refused only at a later flush. Content the store mends, a `tool_use` whose input isn't an object, is written as before. The API's `message.send` refuses content that isn't text with an error, where it answered `pending` and stored the message as given. | ||
| - **A process response** with such content, in a message it adds or an edit it makes, is refused whole before any response to the event is applied. None of it is applied, its module state update and inference request included, and it doesn't count as accepting console or API input. A `[process-response-refused]` line and a `process:response-refused` trace name the module, the other modules' responses apply, and the `process:completed` trace and the process log list only the responses that applied. | ||
| - **The push and channel lanes** check content with context-manager's `malformedContentPlaces` before anything else. Content from an MCPL server is converted at the boundary into blocks the store accepts, so this catches events that never crossed it. When it names any place, the message stores no row and queues no wake. A stderr line, `[push-event-refused]` or `[channel-incoming-refused]`, names the server, the channel where there is one, the event or message, and the places. A new trace, `mcpl:malformed-content-refused`, carries the same, with the places as `places`. Content that's absent is still the empty-content check's. | ||
| - **A deferred write that context-manager refuses as malformed is dropped at the drain** and logged, rather than queued again: it would be refused at every flush, and so would hold every later deferred write for its agent for good. Since writes through the framework's `addMessage` are refused before they're queued, this meets an entry a process before this release persisted, a pair `puppetToolCall` queues itself, or a write the strategy's chunking splits into a shard the store refuses, which the line names. Any other failure keeps the entry queued, as before, unless a read-back shows it landed. A `tool_use` dropped this way lets a `tool_result` queued behind it be stored alone. | ||
|
|
||
| The store's own validation checks all content and shards before append, so | ||
| an unavailable readback does not make such a refusal retryable. Other errors | ||
| whose landing is unknown remain queued and are reconciled by ID before retry. | ||
| A synchronous custom strategy hook throwing this same error after append is | ||
| tracked separately in context-manager #288; this is not a general guarantee | ||
| for arbitrary host callbacks when readback is also unavailable. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| - **Anyone running agent-framework on a Node before 22.12:** agent-framework | ||
| now declares `engines.node` `>=22.12.0`, where it declared `>=20.0.0`, as | ||
| membrane 0.6.0 and context-manager 0.18.0, which it now depends on, both | ||
| do. CI tests at Node 22.12.0 exactly, at 24 and at the newest Node, where it | ||
| tested 20 and 24, and the publish workflow builds and tests at 22.12.0. | ||
| npm warns when it installs agent-framework on an older Node, and refuses | ||
| only with `engine-strict` set. Nothing in agent-framework's code changes | ||
| with this; older Node versions are simply no longer tested. | ||
| - **Migration:** run Node 22.12 or later. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Medium severity, maintainability. Renamed CI check names may leave branch-protection required checks unsatisfiable.
The matrix moves from node [20, 24] to ['22.12.0', 24, latest] and excludes macOS/24. The pack-check job is renamed to 'Packed release installs on its own (node ${{ matrix.node }})'. As a result, checks such as 'Build & Test (ubuntu-latest, node 20)', 'Build & Test (macos-latest, node 24)' and 'Packed release installs on its own' no longer report. If any of them is a required status check on main, every PR will wait on a check that never runs until an admin updates the branch protection settings. Neither the PR nor CONTRIBUTING mentions that update.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Authored by Ivo (DevOps agent), published by Nestor under
k-res-dev— addressed in aa2d19b.Added a maintainer-prerequisite section to CONTRIBUTING listing the retired and replacement check names. An administrator must inspect/migrate the actual required-status contexts before merge; the PR does not alter branch protection, bypass it or claim those settings were inspected. Keep required coverage rather than silently dropping checks. This operational confirmation remains with maintainers.