Skip to content

chore(release): adopt Knowledge 6.1.9 and gate edge-unsafe static imports - #650

Closed
drewstone wants to merge 1 commit into
mainfrom
fix/root-barrel-knowledge
Closed

chore(release): adopt Knowledge 6.1.9 and gate edge-unsafe static imports#650
drewstone wants to merge 1 commit into
mainfrom
fix/root-barrel-knowledge

Conversation

@drewstone

Copy link
Copy Markdown
Contributor

Every Cloudflare Worker importing this package's root barrel was undeployable

graceful-fs@4.2.11 patches Node's fs at module scope. workerd exposes node:fs's close/closeSync as getter-only accessors, so the assignment throws while Cloudflare runs startup validation on upload, and the entire Worker is rejected — no request frames, nothing the app can catch:

Uncaught TypeError: Cannot set property close of #<Object> which has only a getter
  at index.js:14662:12
 [code: 10021]

Every edge in the chain is a static top-level import:

@tangle-network/agent-runtime  dist/index.js:13   (the ROOT barrel)
  -> @tangle-network/agent-knowledge@6.1.8        (an EXACT pin, here)
    -> proper-lockfile@4.1.2 -> graceful-fs@4.2.11

The root barrel is the documented home of runToolLoop / streamToolLoop, which is what a product's chat turn imports — so any Worker on 0.108+ importing it could not deploy. It already took one product's production deploys down.

Changes

Adopt Knowledge 6.1.9 (catalog + cohort SHA). The exact pin means agent-knowledge#97 reaches nobody until this package republishes; that is the whole reason this PR exists.

verify:package now gates the class. It scans the INSTALLED @tangle-network tree and fails on any static import of a module that patches a Node builtin. This belongs here and not only in the package that tripped it — the defect was one package away, and this repo's CI had no way to see it. The matcher covers bound imports, bare side-effect imports (import 'proper-lockfile'), re-exports, and require, excludes dynamic import(), and self-tests those five forms before trusting a clean scan.

The root re-export stays — a deliberate semver call

The obvious second fix is dropping export * from './knowledge' from the root entry, since a dedicated ./knowledge subpath already exists. I measured it instead of assuming, and it does not earn a breaking change:

  • It buys nothing now. The reason the knowledge subtree survived tree-shaking was the module-scope side effect (var import_proper_lockfile = __toESM(require_proper_lockfile())) — a bundler must keep that. With 6.1.9 the side effect is gone, and a consumer importing only the tool loop tree-shakes the subsystem out entirely: zero hashKnowledgeBase / KnowledgeIndexSchema / withKnowledgeMutation / runKnowledgeImprovementJob symbols in the built Worker, bundle 782.38 kB -> 738.91 kB.
  • It would not have fixed the crash anyway. The root barrel reaches agent-knowledge by two paths — export * from './knowledge' and src/candidate-execution/knowledge.ts's hashKnowledgeBase. Removing the first leaves the second.
  • It is breaking. An org-wide code search found every reference to the nine root knowledge exports inside this repo (src/, tests/, docs/, scripts/verify-package-exports.mjs) or agent-knowledge's own docs. Zero external consumers — but it is still a public API removal, so it belongs in a deliberate major, not smuggled into a P0 deploy fix.

Proof — the guard fails on the real regression

Same tree, only the Knowledge pin differs.

With agent-knowledge@6.1.8 (the pin before this PR):

$ pnpm run verify:package
Error: an installed @tangle-network package statically imports a module that patches a Node builtin.
Cloudflare rejects the whole Worker on upload with code 10021, and a dry run cannot see it.
Fix it in the owning package with a dynamic import() inside the function that needs it.
  - agent-knowledge/dist/inspect-D5iarJc2.js -> proper-lockfile
VERIFY_EXIT_WITH_6.1.8=1

With agent-knowledge@6.1.9:

$ pnpm run verify:package
Edge module-load safety: 7 first-party packages, 206 shipped files, no builtin patching.
VERIFY_EXIT_WITH_6.1.9=0

Proof — the real Worker upload

A minimal Worker whose only import is this package's root barrel, built with @cloudflare/vite-plugin and uploaded with wrangler versions upload (same startup validation as a deploy, zero traffic).

import { runToolLoop, streamToolLoop } from '@tangle-network/agent-runtime'
export default { async fetch() { /* ... */ } }

Before, on Knowledge 6.1.8:

$ npx wrangler deploy --dry-run
Total Upload: 1380.33 KiB / gzip: 248.18 KiB
--dry-run: exiting now.
DRYRUN_EXIT=0                       <- green, and structurally blind

$ npx wrangler versions upload
Uncaught TypeError: Cannot set property close of #<Object> which has only a getter
  at index.js:14662:12
 [code: 10021]
BEFORE_FIX_UPLOAD_EXIT=1

After, on Knowledge 6.1.9:

$ npx wrangler versions upload
Total Upload: 721.59 KiB / gzip: 166.58 KiB
Worker Startup Time: 99 ms
Uploaded agent-runtime-gracefulfs-repro (0.96 sec)
Worker Version ID: 77c07e7d-1315-49f5-a4ec-4d43e2c8f24c
AFTER_FIX_UPLOAD_EXIT=0

Verification

  • pnpm lint — clean (502 files)
  • pnpm typecheck — clean (src + examples)
  • pnpm test1856 passed | 6 skipped (1862)
  • pnpm verify:package — passes, including the new gate
  • Published Knowledge 6.1.9 tarball grepped: only lockfileModule ??= import("proper-lockfile") in runtime .js, no static import

…orts

`@tangle-network/agent-knowledge` is pinned at an EXACT version here and
re-exported from the root barrel, so its 6.1.9 fix reaches nobody until
this package republishes against it.

6.1.8 statically imported `proper-lockfile`, which pulls in `graceful-fs`,
which patches Node's `fs` at module scope (`fs.close = ...`). workerd
exposes those as getter-only accessors, so the assignment threw while
Cloudflare ran startup validation on upload:

  Uncaught TypeError: Cannot set property close of #<Object>
  which has only a getter        [code: 10021]

The whole Worker is rejected. Because the root barrel is the documented
home of `runToolLoop` / `streamToolLoop`, every Cloudflare product that
imported the tool loop from this package was undeployable — confirmed by
uploading a Worker whose only import is that barrel.

`verify:package` now scans the INSTALLED `@tangle-network` tree and fails
on any static import of a module that patches a Node builtin. The defect
was one package away and this repo's CI could not see it: `wrangler
deploy --dry-run` bundles without executing module scope, so it is
structurally blind to the class. The matcher covers bound imports, bare
side-effect imports, re-exports, and `require`, excludes dynamic
`import()`, and self-tests those five forms before trusting a clean scan.

The root barrel keeps `export * from './knowledge'`. With 6.1.9 the
knowledge subtree has no module-scope side effect left, so a consumer
importing only the tool loop now tree-shakes it out entirely — measured
zero agent-knowledge symbols and a 782.38 kB -> 738.91 kB Worker bundle.
Removing the re-export is a breaking change that buys nothing measurable.
@drewstone

Copy link
Copy Markdown
Contributor Author

Superseded by the Runtime 0.109.0 release branch. The final shipped-code scan from 2fcbed9 is integrated there unchanged, while Eval moves to 0.135.1 and Knowledge to 6.1.10. Keeping this 0.108.2 cohort would publish an outdated dependency set immediately before the intentional ./kernel release.

@drewstone drewstone closed this Jul 28, 2026
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