Skip to content

Commit 77fad76

Browse files
committed
Vendor intx-types and intx-storage-isogit at upstream head
Re-syncs vendor/intx-types and vendor/intx-storage-isogit from Interchange commit 55c4431e60cc97dae2f63bfd52de56166e42b13b (both were previously at ad0f99e7). vendor/intx-inference stays at ad0f99e7 for now (tracked separately); the shared PendingOperation shape did not change in this range so the two are still compatible. Both packages remain verbatim, no local patches. storage-isogit's index now requires a runtime-bound entry point (./node or ./browser) instead of a bare createIsogitStore export; the one first-party consumer switches to @intx/storage-isogit/node, which keeps the identical signature. Adds the package's two new upstream deps (@isomorphic-git/lightning-fs, buffer, both browser-only) and a 0.2.2-pinned @intx/crypto dev dependency, mirroring the existing @intx/mime pin on intx-inference. Excludes vendor/intx-storage-isogit/src/browser-bundle.test.ts via bunfig.toml: it bundles against an export condition that resolves @intx/log/@intx/mime to source files that only exist in upstream's own monorepo, not in our published-npm installs of those two packages. Full suite: 6042 pass, 1 pre-existing failure in vendor/intx-inference's reactor.test.ts unrelated to this sync (reproduces identically before these changes). Part of CL-5684; fixes CL-6826 https://linear.app/abklabs/issue/CL-6826
1 parent f0188ca commit 77fad76

55 files changed

Lines changed: 3735 additions & 726 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bun.lock

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bunfig.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,12 @@ pathIgnorePatterns = [
44
"tests/fixtures/**",
55
"eval/tasks/**",
66
"tmp/**",
7+
# Bundles vendor/intx-storage-isogit's browser entry point with Bun.build,
8+
# under the "intx-src" export condition that resolves @intx/log and
9+
# @intx/mime to ./src/*.ts. Upstream's own monorepo vendors those two
10+
# packages too, so the condition finds real source; here they stay on
11+
# published npm (dist-only, no src/), so the condition matches an export
12+
# key whose target file doesn't exist. Not a vendored-code defect — see
13+
# docs/VENDORING.md.
14+
"vendor/intx-storage-isogit/src/browser-bundle.test.ts",
715
]

docs/VENDORING.md

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,21 @@ points straight at `./src/*.ts` files rather than a `dist/` build.
2424
| Package | Vendor path | License | Synced from upstream commit | Retrieved | Local patches |
2525
|---|---|---|---|---|---|
2626
| `@intx/inference` | `vendor/intx-inference/` | LGPL-2.1-only | `ad0f99e7977b3ad4f28d8cc8d446ac52a4a2d685` | 2026-08-10 | Yes — see `vendor/intx-inference/PATCHES.md` |
27-
| `@intx/types` | `vendor/intx-types/` | LGPL-2.1-only | `ad0f99e7977b3ad4f28d8cc8d446ac52a4a2d685` | 2026-08-10 | None — verbatim |
28-
| `@intx/storage-isogit` | `vendor/intx-storage-isogit/` | LGPL-2.1-only | `ad0f99e7977b3ad4f28d8cc8d446ac52a4a2d685` | 2026-08-10 | None — verbatim |
27+
| `@intx/types` | `vendor/intx-types/` | LGPL-2.1-only | `55c4431e60cc97dae2f63bfd52de56166e42b13b` | 2026-08-22 | None — verbatim |
28+
| `@intx/storage-isogit` | `vendor/intx-storage-isogit/` | LGPL-2.1-only | `55c4431e60cc97dae2f63bfd52de56166e42b13b` | 2026-08-22 | None — verbatim |
29+
30+
`@intx/inference` is temporarily behind `@intx/types` and
31+
`@intx/storage-isogit` as of the 2026-08-22 sync: this is stage 2 of a
32+
staged re-vendor (the two lower-coupling packages first), with
33+
`@intx/inference`'s re-sync tracked separately as stage 3. The
34+
cross-package coupling described below did not regress in this gap — the
35+
`PendingOperation` shape both packages already shared did not change
36+
between `ad0f99e7` and `55c4431e` (`vendor/intx-types/src/runtime.ts` and
37+
`src/index.ts` are byte-identical across that range), so `@intx/inference`
38+
built against the older `@intx/types` still typechecks against the newer
39+
one. A later re-sync of `@intx/inference` should still move all three
40+
together per the coupling rule below, since that check only holds for
41+
this specific gap, not in general.
2942

3043
The license column records what each package declares in its own
3144
`package.json`; the corresponding `LICENSE` file travels with every vendored
@@ -114,6 +127,36 @@ those packages now resolves to the single root instance. As of this sync,
114127
no modifications. A diff against any later upstream checkout at the same
115128
paths will show 100% upstream-authored lines.
116129

130+
## Notable upstream shape changes carried by the 2026-08-22 sync
131+
132+
Upstream commit `5f798bea` ("Inject the runtime into iso-git storage")
133+
split `@intx/storage-isogit`'s entry point: the package root (`.`) no
134+
longer exports a bindable `createIsogitStore` free function, and callers
135+
now pick a runtime-bound entry point instead — `./node` (backed by
136+
`createNodeIsogitRuntime`) or `./browser`. `vendor/intx-storage-isogit/`'s
137+
`package.json` `exports` map picks up both new subpaths verbatim from
138+
upstream. The one first-party consumer,
139+
`src/session/optimized-context-store.ts`, now imports `createIsogitStore`
140+
from `@intx/storage-isogit/node` instead of the package root — the
141+
`./node` re-export has the identical `(dir, signer?, gcPolicy?)` signature
142+
the old root export had, so this is an import-path change, not a
143+
behavioral one. The package also picked up two new dependencies
144+
(`@isomorphic-git/lightning-fs`, `buffer`, both used only by the new
145+
`./browser` runtime, which nothing here imports) and a new
146+
`@intx/crypto` dev dependency for its own test suite, pinned to `0.2.2`
147+
like the existing `@intx/mime` dev dependency on `@intx/inference` — the
148+
same "stay on published npm for a package we don't vendor" pattern.
149+
150+
One new upstream test, `browser-bundle.test.ts`, is excluded via
151+
`bunfig.toml`'s `pathIgnorePatterns`. It bundles `browser.ts` with
152+
`Bun.build` under the `intx-src` export condition, which resolves
153+
`@intx/log` and `@intx/mime` to `./src/*.ts` — real files in upstream's
154+
own monorepo, where those two are also vendored source. Here they remain
155+
published npm installs (`dist/` only, no `src/`), so the condition
156+
matches an export key whose target does not exist and the bundle fails
157+
to resolve. This is an environment gap, not a defect in the vendored
158+
code; re-check it whenever `@intx/log` or `@intx/mime` get vendored too.
159+
117160
`@intx/inference` carries local patches — real fixes not yet present
118161
upstream, not workarounds for something upstream has since fixed. Every
119162
patched location carries a one-line comment naming its site-specific entry

src/session/optimized-context-store.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import fs from "node:fs";
22
import path from "node:path";
33
import { type } from "arktype";
4-
import { createIsogitStore } from "@intx/storage-isogit";
4+
import { createIsogitStore } from "@intx/storage-isogit/node";
55
import {
66
ContentBlock,
77
type ConnectorThreadState,

vendor/intx-storage-isogit/README.md

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# @intx/storage-isogit
22

33
Isomorphic-git backed implementation of `ContextStore` and
4-
`AuditStore`. Each agent gets its own git repository on disk;
4+
`AuditStore`. Each agent gets its own git repository in the host filesystem;
55
inference state lives on a working branch, the tool-authorization
66
audit log lives on its own branch, and mail history lives in a
77
dedicated audit store that commits each inbound and outbound
@@ -12,17 +12,81 @@ Consumed by `@intx/agent` for in-process persistence, and by
1212
repositories that move between the hub and the sidecar as packs.
1313

1414
```ts
15-
import { createIsogitStore } from "@intx/storage-isogit";
15+
import { createIsogitStore } from "@intx/storage-isogit/node";
1616

1717
const store = await createIsogitStore("./tmp/agent-repo", signer);
1818

1919
// store implements both ContextStore and AuditStore -- hand it to
2020
// the inference and tool layers as appropriate.
2121
```
2222

23+
Node and Bun consumers use the `/node` entry point above. The package root is
24+
runtime-neutral and exports `createIsogitStorage(runtime)` for hosts that
25+
provide their own filesystem:
26+
27+
```ts
28+
import type { FsClient } from "isomorphic-git";
29+
import { createIsogitStorage } from "@intx/storage-isogit";
30+
31+
type IsogitRuntime = {
32+
fs: FsClient;
33+
rename(oldPath: string, newPath: string): Promise<void>;
34+
path: {
35+
join(...parts: string[]): string;
36+
relative(from: string, to: string): string;
37+
resolve(filepath: string): string;
38+
};
39+
flush?: () => Promise<void>;
40+
};
41+
```
42+
43+
The storage layer derives its other filesystem operations from `FsClient`,
44+
including recursive directory creation and removal, existence checks, and
45+
UTF-8 text reads. `rename` remains explicit because completed packs require an
46+
atomic publish primitive. Persistent backends can implement `flush` to make
47+
completed mutations durable before the API call resolves.
48+
49+
Audit and error record paths are append-only. Repeating a byte-identical
50+
record is an idempotent retry, including after an uncertain persistence
51+
failure; reusing the same path for different content is rejected. Mail commits
52+
reconcile an uncertain ref publication before the same store accepts another
53+
message, so a committed message cannot have its ordinal reused.
54+
55+
Browsers can use the included LightningFS IndexedDB adapter:
56+
57+
```ts
58+
import { createBrowserIsogitStorage } from "@intx/storage-isogit/browser";
59+
60+
const storage = createBrowserIsogitStorage("agent-storage");
61+
const store = await storage.createIsogitStore("/agents/example");
62+
```
63+
64+
The browser adapter supplies isomorphic-git's Buffer compatibility, atomic
65+
rename, POSIX path operations, and IndexedDB-backed file storage. Each call to
66+
`createBrowserIsogitStorage(name)` claims a fresh volume and clears any prior
67+
contents for that name. Call it exactly once per name during a page or worker
68+
lifetime, then pass the returned storage object to every consumer. Reusing the
69+
same name from another factory call, bundle, tab, or worker is unsupported and
70+
may clear active data.
71+
72+
Browser storage is intentionally disposable: a page reload, tab close, or
73+
worker restart ends the logical session, and the next owner starts from an
74+
empty volume instead of attempting recovery. `flush()` writes the current
75+
filesystem state to IndexedDB, but does not promise that state will survive a
76+
new owner. IndexedDB is the storage medium, not a restart-persistence
77+
guarantee. Use absolute repository paths; relative browser paths resolve from
78+
`/`. Applications that require restart persistence must inject a durable
79+
runtime through the package root and own that runtime's recovery guarantees.
80+
2381
The pack-send and pack-receive helpers (`createDeployPack`,
2482
`createNegotiatedPack`, `applyPack`, `receivePackObjects`) produce
2583
and consume the wire bytes that `@intx/pack-transport` chunks
2684
across the WebSocket. A `CommitSigner` is optional but required
2785
when the consumer needs every commit to carry a verifiable
2886
signature.
87+
88+
Pack receivers flush an accepted pack before checkout or ref promotion. If a
89+
later ref or persistence operation fails, the call rejects but retains the
90+
pack because the ref update may already be observable. Callers must treat that
91+
error as an uncertain promotion outcome rather than assuming the ref is
92+
unchanged; whichever ref value is visible remains backed by durable objects.

vendor/intx-storage-isogit/package.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,26 @@
77
".": {
88
"types": "./src/index.ts",
99
"default": "./src/index.ts"
10+
},
11+
"./node": {
12+
"types": "./src/node.ts",
13+
"default": "./src/node.ts"
14+
},
15+
"./browser": {
16+
"types": "./src/browser.ts",
17+
"default": "./src/browser.ts"
1018
}
1119
},
1220
"dependencies": {
1321
"@intx/log": "0.2.2",
1422
"@intx/mime": "0.2.2",
1523
"@intx/types": "workspace:*",
24+
"@isomorphic-git/lightning-fs": "4.7.0",
1625
"arktype": "catalog:",
26+
"buffer": "^6.0.3",
1727
"isomorphic-git": "catalog:"
28+
},
29+
"devDependencies": {
30+
"@intx/crypto": "0.2.2"
1831
}
1932
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { expect, test } from "bun:test";
2+
import path from "node:path";
3+
4+
test("browser entry bundles without Node filesystem or path modules", async () => {
5+
const result = await Bun.build({
6+
entrypoints: [path.join(import.meta.dir, "browser.ts")],
7+
target: "browser",
8+
format: "esm",
9+
conditions: ["intx-src"],
10+
});
11+
if (!result.success) {
12+
throw new Error(
13+
result.logs
14+
.map((log) => (log instanceof Error ? log.message : String(log)))
15+
.join("\n"),
16+
);
17+
}
18+
const output = result.outputs[0];
19+
if (output === undefined) {
20+
throw new Error("Browser storage bundle produced no output");
21+
}
22+
const bundle = await output.text();
23+
expect(bundle).not.toContain('from "node:fs"');
24+
expect(bundle).not.toContain('from "node:path"');
25+
});
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { describe, expect, test } from "bun:test";
2+
import path from "node:path";
3+
4+
import { browserPath } from "./browser-path";
5+
6+
describe("browserPath", () => {
7+
test.each([
8+
[[], "."],
9+
[["/repo", ".git", "objects"], "/repo/.git/objects"],
10+
[["repo", "state", "..", "audit"], "repo/audit"],
11+
[["/", "repo", "//state"], "/repo/state"],
12+
] as const)("join(%j)", (parts, expected) => {
13+
expect(browserPath.join(...parts)).toBe(expected);
14+
expect(browserPath.join(...parts)).toBe(path.posix.join(...parts));
15+
});
16+
17+
test.each([
18+
["/repo", "/repo/.git/objects", ".git/objects"],
19+
["/repo/state", "/repo/audit", "../audit"],
20+
["/repo", "/repo", ""],
21+
["repo", "other/file", "../other/file"],
22+
])("relative(%s, %s)", (from, to, expected) => {
23+
expect(browserPath.relative(from, to)).toBe(expected);
24+
});
25+
26+
test.each([
27+
["/repo/../state", "/state"],
28+
["repo/./state", "/repo/state"],
29+
["/../../repo", "/repo"],
30+
["", "/"],
31+
])("resolve(%s)", (filepath, expected) => {
32+
expect(browserPath.resolve(filepath)).toBe(expected);
33+
});
34+
});
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import type { IsogitPath } from "./runtime";
2+
3+
function normalize(filepath: string, absolute: boolean): string {
4+
const segments: string[] = [];
5+
for (const segment of filepath.split("/")) {
6+
if (segment === "" || segment === ".") {
7+
continue;
8+
}
9+
if (segment === "..") {
10+
if (segments.length > 0 && segments.at(-1) !== "..") {
11+
segments.pop();
12+
} else if (!absolute) {
13+
segments.push(segment);
14+
}
15+
continue;
16+
}
17+
segments.push(segment);
18+
}
19+
20+
if (absolute) {
21+
return segments.length === 0 ? "/" : `/${segments.join("/")}`;
22+
}
23+
return segments.length === 0 ? "." : segments.join("/");
24+
}
25+
26+
function resolve(filepath: string): string {
27+
return normalize(filepath.startsWith("/") ? filepath : `/${filepath}`, true);
28+
}
29+
30+
function join(...parts: string[]): string {
31+
if (parts.length === 0) {
32+
return ".";
33+
}
34+
const combined = parts.join("/");
35+
return normalize(combined, combined.startsWith("/"));
36+
}
37+
38+
function relative(from: string, to: string): string {
39+
const fromSegments = resolve(from).split("/").filter(Boolean);
40+
const toSegments = resolve(to).split("/").filter(Boolean);
41+
let shared = 0;
42+
while (
43+
shared < fromSegments.length &&
44+
shared < toSegments.length &&
45+
fromSegments[shared] === toSegments[shared]
46+
) {
47+
shared += 1;
48+
}
49+
return [
50+
...fromSegments.slice(shared).map(() => ".."),
51+
...toSegments.slice(shared),
52+
].join("/");
53+
}
54+
55+
export const browserPath: IsogitPath = { join, relative, resolve };

0 commit comments

Comments
 (0)