@takboo/dsh-session-bin 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -13
- package/dist/client-model.js +61 -2
- package/dist/client.js +223 -137
- package/dist/client.js.map +1 -1
- package/dist/host/native-retirement-sdk.d.ts +42 -39
- package/dist/index.js +1475 -1400
- package/dist/index.js.map +4 -4
- package/dist/types/client/locales.d.ts +11 -4
- package/dist/types/client/model.d.ts +15 -1
- package/docs/client-interface.md +6 -4
- package/docs/host-lifecycle.md +4 -2
- package/package.json +48 -47
package/dist/index.js
CHANGED
|
@@ -1799,22 +1799,90 @@ import { readFile } from "node:fs/promises";
|
|
|
1799
1799
|
import { dirname, join as join2 } from "node:path";
|
|
1800
1800
|
import { createHash as createHash3 } from "node:crypto";
|
|
1801
1801
|
var nativeRetirementSdkSources = {
|
|
1802
|
-
cordis
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
"dsh-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1802
|
+
"cordis": {
|
|
1803
|
+
"version": "4.0.5",
|
|
1804
|
+
"files": {
|
|
1805
|
+
"lib/index.js": "6a9394c0877ff45218818c6e815edd038f8057e1a1deb390a8d43ec81c57691e"
|
|
1806
|
+
}
|
|
1807
|
+
},
|
|
1808
|
+
"dsh-session": {
|
|
1809
|
+
"version": "0.2.1-rc.1",
|
|
1810
|
+
"files": {
|
|
1811
|
+
"lib/index.js": "2f5dd07cb5c744a29abbb8003a8a4cefd9e582c6e2538074ca299a0ddd02a3bf",
|
|
1812
|
+
"lib/types/index.js": "59cced30c7ee65813c62f4858c2fc15c8c636df96507b60c15543468178b72f2"
|
|
1813
|
+
}
|
|
1814
|
+
},
|
|
1815
|
+
"dsh-session-persistence": {
|
|
1816
|
+
"version": "0.2.1-rc.1",
|
|
1817
|
+
"files": {
|
|
1818
|
+
"lib/index.js": "cc0b6d3a224133af611b428d5a49020e300f86c3b4ba28037aeb219029bde3eb"
|
|
1819
|
+
}
|
|
1820
|
+
},
|
|
1821
|
+
"dsh-session-persistence-jsonl": {
|
|
1822
|
+
"version": "0.2.1-rc.1",
|
|
1823
|
+
"files": {
|
|
1824
|
+
"lib/index.js": "973dd78679b2661d6f492343bc22b124ae958aa597d3b36c698a2c650f909984"
|
|
1825
|
+
}
|
|
1826
|
+
},
|
|
1827
|
+
"dsh-session-format-catalog": {
|
|
1828
|
+
"version": "0.2.1-rc.1",
|
|
1829
|
+
"files": {
|
|
1830
|
+
"lib/index.js": "836bbb772ab505c299f1a4a246164c50c7c08b67ab0d17f1251d7ea2cd8c1818",
|
|
1831
|
+
"lib/types/index.js": "c7d797760bf2b9bc654751f2f8cb0b8a6b98998c8d9c1e41323c71a4d5f316f2"
|
|
1832
|
+
}
|
|
1833
|
+
},
|
|
1834
|
+
"dsh-storage-domain": {
|
|
1835
|
+
"version": "0.2.1-rc.1",
|
|
1836
|
+
"files": {
|
|
1837
|
+
"lib/index.js": "a54f7664d2f64fe04a08c7058ca5bff4fc69ed6a0d30c2bc11561b620698ef16"
|
|
1838
|
+
}
|
|
1839
|
+
},
|
|
1840
|
+
"dsh-storage-json": {
|
|
1841
|
+
"version": "0.2.1-rc.1",
|
|
1842
|
+
"files": {
|
|
1843
|
+
"lib/index.js": "0a6b75d5569db3379edd93e863570170e1f4121b6c9fabd856f5b476cffe8df4"
|
|
1844
|
+
}
|
|
1845
|
+
},
|
|
1846
|
+
"dsh-workspace": {
|
|
1847
|
+
"version": "0.2.1-rc.1",
|
|
1848
|
+
"files": {
|
|
1849
|
+
"lib/index.js": "931ca0cb10c5102066c7723690a887f0e6e8e7ab0ffdee1a782686745b0b934f",
|
|
1850
|
+
"lib/types/index.js": "ed69989d8286598fddb3a40030723810a1238968e91ccfec05fc84657bb5ec74"
|
|
1851
|
+
}
|
|
1852
|
+
},
|
|
1853
|
+
"dsh-session-query": {
|
|
1854
|
+
"version": "0.2.1-rc.1",
|
|
1855
|
+
"files": {
|
|
1856
|
+
"lib/index.js": "76ce2944e561c12f48b5e240d68cafe54ce6d667b1d20722e6939ed834489939"
|
|
1857
|
+
}
|
|
1858
|
+
},
|
|
1859
|
+
"dsh-session-query-sqlite": {
|
|
1860
|
+
"version": "0.2.1-rc.1",
|
|
1861
|
+
"files": {
|
|
1862
|
+
"lib/index.js": "dbecf83320e10d93d735b3d01384ab817a0b90102ccecb0a22c013c71dfe70a0"
|
|
1863
|
+
}
|
|
1864
|
+
},
|
|
1865
|
+
"dsh-session-projection-cache": {
|
|
1866
|
+
"version": "0.2.1-rc.1",
|
|
1867
|
+
"files": {
|
|
1868
|
+
"lib/index.js": "faf307dce5db2fb7ff58b4c7d9e544fc7cad5de2f270a89326a8d7bb2bda9c80"
|
|
1869
|
+
}
|
|
1870
|
+
},
|
|
1871
|
+
"dsh-api-session-controller": {
|
|
1872
|
+
"version": "0.2.1-rc.1",
|
|
1873
|
+
"files": {
|
|
1874
|
+
"lib/index.js": "a9801acb577b03053c5d5a3191ff555c5f1d791d0acddec113bea4511e33b386",
|
|
1875
|
+
"lib/types/index.js": "616ced16623b7448afa908ff65093eb6ef2a478d32e5ef9bc22634bfa9f80472"
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1814
1878
|
};
|
|
1879
|
+
var legacyDurableHostContract = "0.2.0-rc.2";
|
|
1880
|
+
var legacyDurableContractFingerprint = "2618a33decce99fe8ae893245236273d10ac5536bb9dd582dddd115ef27069f3";
|
|
1815
1881
|
async function nativeRetirementSdkFingerprint() {
|
|
1816
1882
|
const root = createRequire(import.meta.url);
|
|
1817
|
-
const
|
|
1883
|
+
const hostPath = root.resolve("@deepseek-ai/dsh/package.json");
|
|
1884
|
+
if (JSON.parse(await readFile(hostPath, "utf8")).version !== "0.2.1-rc.1") return null;
|
|
1885
|
+
const base = createRequire(hostPath);
|
|
1818
1886
|
for (const [name2, expected] of Object.entries(nativeRetirementSdkSources)) {
|
|
1819
1887
|
let path;
|
|
1820
1888
|
try {
|
|
@@ -1836,8 +1904,8 @@ async function nativeRetirementSdkFingerprint() {
|
|
|
1836
1904
|
}
|
|
1837
1905
|
const runtime = createRequire(base.resolve("@deepseek-ai/dsh-base/package.json"));
|
|
1838
1906
|
for (const [name2, expected] of Object.entries({
|
|
1839
|
-
"dsh-agent": "
|
|
1840
|
-
"dsh-agent-loop": "
|
|
1907
|
+
"dsh-agent": "7b59cf82c2784c25bdce4e3e24e5e5303b02b9f1003efacf50255492d17baf8b",
|
|
1908
|
+
"dsh-agent-loop": "cf57e3ecd487ac5c2553455aaa5cef2c6fb41e6bd61be5aa455675d1d7193033"
|
|
1841
1909
|
})) {
|
|
1842
1910
|
let path;
|
|
1843
1911
|
try {
|
|
@@ -1846,9 +1914,9 @@ async function nativeRetirementSdkFingerprint() {
|
|
|
1846
1914
|
if (error.code === "MODULE_NOT_FOUND") return null;
|
|
1847
1915
|
throw error;
|
|
1848
1916
|
}
|
|
1849
|
-
if (JSON.parse(await readFile(path, "utf8")).version !== "0.2.
|
|
1917
|
+
if (JSON.parse(await readFile(path, "utf8")).version !== "0.2.1-rc.1" || createHash3("sha256").update(await readFile(join2(dirname(path), "lib/index.js"))).digest("hex") !== expected) return null;
|
|
1850
1918
|
}
|
|
1851
|
-
return
|
|
1919
|
+
return legacyDurableContractFingerprint;
|
|
1852
1920
|
}
|
|
1853
1921
|
|
|
1854
1922
|
// src/host/native-jsonl-files.ts
|
|
@@ -1857,1511 +1925,1518 @@ import { isAbsolute as isAbsolute2, join as join3, relative, resolve as resolve2
|
|
|
1857
1925
|
import { createHash as createHash4 } from "node:crypto";
|
|
1858
1926
|
import { createRequire as createRequire2 } from "node:module";
|
|
1859
1927
|
import { zstdDecompressSync } from "node:zlib";
|
|
1860
|
-
import
|
|
1928
|
+
import { SessionPersistence } from "@deepseek-ai/dsh-session-persistence";
|
|
1861
1929
|
import { SESSION_FORMAT_VERSION } from "@deepseek-ai/dsh-session";
|
|
1862
1930
|
import { sessionFormatCatalog } from "@deepseek-ai/dsh-session-format-catalog";
|
|
1863
1931
|
import { tryLockExclusive as tryLockExclusive2 } from "@deepseek-ai/node-addon-system/flock";
|
|
1864
1932
|
import { z as z4 } from "zod";
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
var
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
digest: hash,
|
|
1890
|
-
header: headerIdentitySchema,
|
|
1891
|
-
revision: hash
|
|
1892
|
-
}).strict().superRefine((value, ctx) => {
|
|
1893
|
-
try {
|
|
1894
|
-
const kind = classify(value.resourceId);
|
|
1895
|
-
if (kind.kind !== value.kind || kind.version !== value.formatVersion || kind.compression !== value.compression || value.revision !== sha(JSON.stringify({ identity: value.identity, digest: value.digest, header: value.header }))) {
|
|
1896
|
-
ctx.addIssue({ code: "custom", message: "Resource classification and identity digest must agree." });
|
|
1897
|
-
}
|
|
1898
|
-
} catch {
|
|
1899
|
-
ctx.addIssue({ code: "custom", message: "Unknown physical resource name." });
|
|
1933
|
+
|
|
1934
|
+
// src/host/native-retirement-persistence.ts
|
|
1935
|
+
import { SessionId as SessionId2 } from "@deepseek-ai/dsh-session";
|
|
1936
|
+
|
|
1937
|
+
// src/host/native-retirement-admission.ts
|
|
1938
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
1939
|
+
var NativeAdmissionError = class extends Error {
|
|
1940
|
+
code = "native/session-busy";
|
|
1941
|
+
};
|
|
1942
|
+
var NativeAdmission = class {
|
|
1943
|
+
maintenance = new AsyncLocalStorage();
|
|
1944
|
+
acceptedFrames = new AsyncLocalStorage();
|
|
1945
|
+
activeAcceptedFrames = /* @__PURE__ */ new Set();
|
|
1946
|
+
held = /* @__PURE__ */ new Set();
|
|
1947
|
+
released = /* @__PURE__ */ new Map();
|
|
1948
|
+
pending = /* @__PURE__ */ new Map();
|
|
1949
|
+
globalReads = /* @__PURE__ */ new Set();
|
|
1950
|
+
retired = /* @__PURE__ */ new Set();
|
|
1951
|
+
candidates = /* @__PURE__ */ new Set();
|
|
1952
|
+
closing = false;
|
|
1953
|
+
failed = false;
|
|
1954
|
+
ready = false;
|
|
1955
|
+
requiresRecovery() {
|
|
1956
|
+
return this.failed;
|
|
1900
1957
|
}
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
var nativeJsonlInventorySchema = z4.object({
|
|
1904
|
-
schemaVersion: z4.literal(1),
|
|
1905
|
-
root: z4.string(),
|
|
1906
|
-
rootIdentity: nodeIdentitySchema,
|
|
1907
|
-
directory: z4.string(),
|
|
1908
|
-
directoryIdentity: nodeIdentitySchema,
|
|
1909
|
-
sessionId: z4.string().min(1),
|
|
1910
|
-
compression: z4.enum(["none", "zstd"]),
|
|
1911
|
-
header: headerIdentitySchema,
|
|
1912
|
-
anchor: z4.object({ resourceId: z4.string(), identity: nodeIdentitySchema, header: headerIdentitySchema }).strict(),
|
|
1913
|
-
lock: nativeLockSchema,
|
|
1914
|
-
files: z4.array(fileRowSchema).max(4096),
|
|
1915
|
-
revision: hash
|
|
1916
|
-
}).strict().superRefine((value, ctx) => {
|
|
1917
|
-
try {
|
|
1918
|
-
const anchor = generation(value.anchor.resourceId);
|
|
1919
|
-
if (!isAbsolute2(value.root) || value.directory !== join3(project(value.header.cwd), segment(value.sessionId)) || value.header.id !== value.sessionId || !same(value.anchor.header, value.header) || !anchor || anchor.compression !== value.compression || new Set(value.files.map((row) => row.resourceId)).size !== value.files.length || value.files.some((row) => row.compression !== value.compression || !same(row.header, value.header)) || value.revision !== sha(JSON.stringify(value.files))) {
|
|
1920
|
-
ctx.addIssue({ code: "custom", message: "Frozen directory, header identities, file scope and digest must agree." });
|
|
1921
|
-
}
|
|
1922
|
-
} catch {
|
|
1923
|
-
ctx.addIssue({ code: "custom", message: "Invalid frozen directory or anchor." });
|
|
1958
|
+
activate() {
|
|
1959
|
+
this.ready = true;
|
|
1924
1960
|
}
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
constructor(code, message) {
|
|
1928
|
-
super(message);
|
|
1929
|
-
this.code = code;
|
|
1930
|
-
this.name = "NativeJsonlRefusal";
|
|
1961
|
+
suspend() {
|
|
1962
|
+
this.failed = true;
|
|
1931
1963
|
}
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
function lockIdentity(stat) {
|
|
1954
|
-
const identity3 = fileIdentity(stat);
|
|
1955
|
-
if (identity3.size !== "0") refuse("jsonl/lock-invalid", "The existing stable lock must contain no session content.");
|
|
1956
|
-
return lockIdentitySchema.parse({ ...nodeIdentity(stat), size: "0", nlink: "1" });
|
|
1957
|
-
}
|
|
1958
|
-
function headerIdentity(header) {
|
|
1959
|
-
return headerIdentitySchema.parse({
|
|
1960
|
-
id: header.id,
|
|
1961
|
-
createdAt: header.createdAt,
|
|
1962
|
-
cwd: header.cwd ?? null,
|
|
1963
|
-
parentSession: header.parentSession ?? null,
|
|
1964
|
-
origin: header.origin ?? null,
|
|
1965
|
-
isSeeded: header.isSeeded,
|
|
1966
|
-
delegationDepth: header.delegationDepth ?? 0,
|
|
1967
|
-
agentPreset: header.agentPreset ?? null
|
|
1968
|
-
});
|
|
1969
|
-
}
|
|
1970
|
-
function segment(value) {
|
|
1971
|
-
if (!value) refuse("jsonl/identity-invalid", "A path identity cannot be empty.");
|
|
1972
|
-
if (value === "." || value === "..") return value.replace(/\./g, "~002E");
|
|
1973
|
-
return value.replace(/[^A-Za-z0-9._-]/g, (unit) => `~${unit.charCodeAt(0).toString(16).toUpperCase().padStart(4, "0")}`);
|
|
1974
|
-
}
|
|
1975
|
-
function project(cwd) {
|
|
1976
|
-
if (cwd === null) return "_no-cwd";
|
|
1977
|
-
if (!isAbsolute2(cwd)) refuse("jsonl/identity-invalid", "The stored cwd must be absolute.");
|
|
1978
|
-
const readable = segment(cwd.replace(/[/\\:]+/g, "-")).replace(/^-+/, "") || "root";
|
|
1979
|
-
return `--${readable.slice(0, 251)}--`;
|
|
1980
|
-
}
|
|
1981
|
-
function generation(name2) {
|
|
1982
|
-
const match = /^session(?:\.v([1-9]\d*))?\.jsonl(\.zstd)?$/.exec(name2);
|
|
1983
|
-
if (!match) return;
|
|
1984
|
-
const version = match[1] === void 0 ? 0 : Number(match[1]);
|
|
1985
|
-
if (!Number.isSafeInteger(version) || version > SESSION_FORMAT_VERSION) refuse("jsonl/unsupported-generation", "Unknown future physical generations cannot be retired.");
|
|
1986
|
-
return { version, compression: match[2] ? "zstd" : "none" };
|
|
1987
|
-
}
|
|
1988
|
-
function classify(name2) {
|
|
1989
|
-
const committed = generation(name2);
|
|
1990
|
-
if (committed) return { ...committed, kind: "generation" };
|
|
1991
|
-
const materializing = /^(session(?:\.v[1-9]\d*)?\.jsonl(?:\.zstd)?)\.[a-f0-9]{12}\.tmp$/.exec(name2);
|
|
1992
|
-
if (materializing) return { ...generation(materializing[1]), kind: "staging" };
|
|
1993
|
-
const migrating = /^session\.migration\.[a-f0-9]{16}\.jsonl(\.zstd)?\.tmp$/.exec(name2);
|
|
1994
|
-
if (migrating) return { version: SESSION_FORMAT_VERSION, compression: migrating[1] ? "zstd" : "none", kind: "staging" };
|
|
1995
|
-
refuse("jsonl/unknown-resource", `Unclassified session resource: ${name2}`);
|
|
1996
|
-
}
|
|
1997
|
-
var HEADER_LIMIT = 1024 * 1024;
|
|
1998
|
-
function firstFrameEnd(bytes) {
|
|
1999
|
-
if (bytes.length < 5) return;
|
|
2000
|
-
if (bytes.readUInt32LE(0) !== 4247762216) refuse("jsonl/header-invalid", "The first Zstandard frame is not a standard frame.");
|
|
2001
|
-
const descriptor2 = bytes[4];
|
|
2002
|
-
if (descriptor2 & 24) refuse("jsonl/header-invalid", "Reserved Zstandard frame flags are unsupported.");
|
|
2003
|
-
const single = Boolean(descriptor2 & 32);
|
|
2004
|
-
const sizeFlag = descriptor2 >>> 6;
|
|
2005
|
-
const dictionaryBytes = [0, 1, 2, 4][descriptor2 & 3];
|
|
2006
|
-
const sizeBytes = sizeFlag === 0 ? single ? 1 : 0 : [0, 2, 4, 8][sizeFlag];
|
|
2007
|
-
let cursor = 5 + (single ? 0 : 1) + dictionaryBytes + sizeBytes;
|
|
2008
|
-
if (cursor > bytes.length) return;
|
|
2009
|
-
for (; ; ) {
|
|
2010
|
-
if (cursor + 3 > bytes.length) return;
|
|
2011
|
-
const block = bytes.readUIntLE(cursor, 3);
|
|
2012
|
-
const type = block >>> 1 & 3;
|
|
2013
|
-
if (type === 3) refuse("jsonl/header-invalid", "Reserved Zstandard block type.");
|
|
2014
|
-
cursor += 3 + (type === 1 ? 1 : block >>> 3);
|
|
2015
|
-
if (cursor > bytes.length) return;
|
|
2016
|
-
if (block & 1) {
|
|
2017
|
-
cursor += descriptor2 & 4 ? 4 : 0;
|
|
2018
|
-
return cursor <= bytes.length ? cursor : void 0;
|
|
1964
|
+
beginClose() {
|
|
1965
|
+
this.closing = true;
|
|
1966
|
+
}
|
|
1967
|
+
isRetired(id) {
|
|
1968
|
+
return this.retired.has(id) || this.failed && this.candidates.has(id);
|
|
1969
|
+
}
|
|
1970
|
+
hasTombstones() {
|
|
1971
|
+
return this.retired.size > 0 || this.failed && this.candidates.size > 0;
|
|
1972
|
+
}
|
|
1973
|
+
markRetired(id) {
|
|
1974
|
+
this.retired.add(id);
|
|
1975
|
+
}
|
|
1976
|
+
isAcceptedFrame() {
|
|
1977
|
+
const frame = this.acceptedFrames.getStore();
|
|
1978
|
+
return frame !== void 0 && this.activeAcceptedFrames.has(frame);
|
|
1979
|
+
}
|
|
1980
|
+
assertAllowed(id) {
|
|
1981
|
+
const maintenance = this.maintenance.getStore();
|
|
1982
|
+
if (maintenance?.active && maintenance.id === id && !(this.failed && this.candidates.has(id))) return;
|
|
1983
|
+
if (!this.ready || this.closing && !this.isAcceptedFrame() || this.failed && this.candidates.has(id) || this.retired.has(id) || this.held.has(id) && !this.isAcceptedFrame()) {
|
|
1984
|
+
throw new NativeAdmissionError("The session is reserved for retirement or awaiting recovery.");
|
|
2019
1985
|
}
|
|
2020
1986
|
}
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
let line;
|
|
2024
|
-
if (compression === "none") {
|
|
2025
|
-
const newline = bytes.indexOf(10);
|
|
2026
|
-
if (newline < 0) refuse("jsonl/header-invalid", "A complete physical header is required, including for staging.");
|
|
2027
|
-
line = bytes.subarray(0, newline);
|
|
2028
|
-
} else {
|
|
2029
|
-
const end = firstFrameEnd(bytes);
|
|
2030
|
-
if (end === void 0) refuse("jsonl/header-invalid", "A complete Zstandard header frame is required.");
|
|
2031
|
-
let decoded;
|
|
1987
|
+
bypass(id, work) {
|
|
1988
|
+
const token = { id, active: true };
|
|
2032
1989
|
try {
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
1990
|
+
const value = this.maintenance.run(token, work);
|
|
1991
|
+
if (value && typeof value.then === "function") {
|
|
1992
|
+
return Promise.resolve(value).finally(() => {
|
|
1993
|
+
token.active = false;
|
|
1994
|
+
});
|
|
1995
|
+
}
|
|
1996
|
+
token.active = false;
|
|
1997
|
+
return value;
|
|
1998
|
+
} catch (error) {
|
|
1999
|
+
token.active = false;
|
|
2000
|
+
throw error;
|
|
2036
2001
|
}
|
|
2037
|
-
if (!decoded.length || decoded.indexOf(10) !== decoded.length - 1) refuse("jsonl/header-invalid", "The header frame must contain exactly one newline-terminated record.");
|
|
2038
|
-
line = decoded.subarray(0, -1);
|
|
2039
2002
|
}
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
const result = sessionFormatCatalog.readHeader(raw);
|
|
2043
|
-
if (result.status !== "current" && result.status !== "migration-required") refuse("jsonl/header-invalid", "Malformed or unsupported physical Session header.");
|
|
2044
|
-
if (result.storedVersion !== version) refuse("jsonl/header-invalid", "Physical filename and header versions differ.");
|
|
2045
|
-
return headerIdentity(result.header);
|
|
2046
|
-
} catch (error) {
|
|
2047
|
-
if (error instanceof NativeJsonlRefusal) throw error;
|
|
2048
|
-
refuse("jsonl/header-invalid", "The physical Session header cannot be decoded and identified.");
|
|
2003
|
+
withoutBypass(work) {
|
|
2004
|
+
return this.maintenance.run(void 0, work);
|
|
2049
2005
|
}
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
});
|
|
2066
|
-
}
|
|
2067
|
-
var NativeJsonlFiles = class _NativeJsonlFiles {
|
|
2068
|
-
constructor(provider, root, compression, rootIdentity) {
|
|
2069
|
-
this.provider = provider;
|
|
2070
|
-
this.root = root;
|
|
2071
|
-
this.compression = compression;
|
|
2072
|
-
this.rootIdentity = rootIdentity;
|
|
2006
|
+
track(id, work) {
|
|
2007
|
+
this.assertAllowed(id);
|
|
2008
|
+
const tasks = this.pending.get(id) ?? /* @__PURE__ */ new Set();
|
|
2009
|
+
this.pending.set(id, tasks);
|
|
2010
|
+
const frame = Symbol("native-accepted-operation");
|
|
2011
|
+
this.activeAcceptedFrames.add(frame);
|
|
2012
|
+
const task = this.acceptedFrames.run(frame, () => Promise.resolve().then(work));
|
|
2013
|
+
tasks.add(task);
|
|
2014
|
+
void task.finally(() => {
|
|
2015
|
+
this.activeAcceptedFrames.delete(frame);
|
|
2016
|
+
tasks.delete(task);
|
|
2017
|
+
if (!tasks.size) this.pending.delete(id);
|
|
2018
|
+
}).catch(() => {
|
|
2019
|
+
});
|
|
2020
|
+
return task;
|
|
2073
2021
|
}
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
if (!(provider instanceof Jsonl) || provider.name !== "session-persistence-jsonl" || SESSION_FORMAT_VERSION !== 4) return;
|
|
2078
|
-
const internals = provider;
|
|
2079
|
-
if (typeof internals.root !== "string" || !isAbsolute2(internals.root) || internals.compression !== "none" && internals.compression !== "zstd" || typeof provider.config.root !== "string" || resolve2(provider.config.root) !== internals.root || (provider.config.compression ?? "zstd") !== internals.compression) return;
|
|
2080
|
-
const require2 = createRequire2(import.meta.url);
|
|
2081
|
-
const metadata = require2("@deepseek-ai/dsh-session-persistence-jsonl/package.json");
|
|
2082
|
-
if (metadata.version !== "0.2.0-rc.2") return;
|
|
2083
|
-
let root;
|
|
2084
|
-
try {
|
|
2085
|
-
root = await realpath2(internals.root);
|
|
2086
|
-
} catch (error) {
|
|
2087
|
-
if (missing(error)) return;
|
|
2088
|
-
throw error;
|
|
2022
|
+
async trackGlobal(work) {
|
|
2023
|
+
if (this.closing && !this.isAcceptedFrame() && !this.maintenance.getStore()?.active) {
|
|
2024
|
+
throw new NativeAdmissionError("Native admission is closing.");
|
|
2089
2025
|
}
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
if (
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
const
|
|
2099
|
-
|
|
2100
|
-
const directory = join3(project(identity3.cwd), segment(sessionId));
|
|
2101
|
-
const base = await this.directory(directory, sessionId, identity3);
|
|
2102
|
-
const files = await this.rows(base.path, identity3);
|
|
2103
|
-
const canonical3 = files.filter((row) => row.kind === "generation").sort((a, b) => a.formatVersion - b.formatVersion)[0];
|
|
2104
|
-
if (!canonical3) refuse("jsonl/session-not-found", "No identified committed generation exists.");
|
|
2105
|
-
const inventory = {
|
|
2106
|
-
schemaVersion: 1,
|
|
2107
|
-
root: this.root,
|
|
2108
|
-
rootIdentity: this.rootIdentity,
|
|
2109
|
-
directory,
|
|
2110
|
-
directoryIdentity: base.identity,
|
|
2111
|
-
sessionId,
|
|
2112
|
-
compression: this.compression,
|
|
2113
|
-
header: identity3,
|
|
2114
|
-
anchor: {
|
|
2115
|
-
resourceId: canonical3.resourceId,
|
|
2116
|
-
identity: { dev: canonical3.identity.dev, ino: canonical3.identity.ino, birthtimeNs: canonical3.identity.birthtimeNs },
|
|
2117
|
-
header: identity3
|
|
2118
|
-
},
|
|
2119
|
-
lock: await this.lockIdentity(base.path),
|
|
2120
|
-
files,
|
|
2121
|
-
revision: sha(JSON.stringify(files))
|
|
2122
|
-
};
|
|
2123
|
-
await this.directory(directory, sessionId, identity3);
|
|
2124
|
-
return nativeJsonlInventorySchema.parse(inventory);
|
|
2125
|
-
}
|
|
2126
|
-
/** Re-enumerate the entire scope without changing its original durable anchor. */
|
|
2127
|
-
async current(input) {
|
|
2128
|
-
const inventory = this.validate(input);
|
|
2129
|
-
const base = await this.assertDirectory(inventory);
|
|
2130
|
-
const files = await this.rows(base.path, inventory.header, inventory);
|
|
2131
|
-
const lock = await this.lockIdentity(base.path);
|
|
2132
|
-
if (!same(lock, inventory.lock)) refuse("jsonl/lock-changed", "The stable coordination file changed.");
|
|
2133
|
-
return nativeJsonlInventorySchema.parse({ ...inventory, files, revision: sha(JSON.stringify(files)) });
|
|
2134
|
-
}
|
|
2135
|
-
async acquire(input, options = {}) {
|
|
2136
|
-
const inventory = this.validate(input);
|
|
2137
|
-
const base = await this.assertDirectory(inventory);
|
|
2138
|
-
let fd;
|
|
2139
|
-
let unlock;
|
|
2140
|
-
const record = {
|
|
2141
|
-
stamp: leaseStamp(inventory),
|
|
2142
|
-
closing: false,
|
|
2143
|
-
tail: Promise.resolve(),
|
|
2144
|
-
pending: /* @__PURE__ */ new Set(),
|
|
2145
|
-
recovering: options.recovering === true
|
|
2146
|
-
};
|
|
2147
|
-
let held = false;
|
|
2026
|
+
while (this.held.size && !this.maintenance.getStore()?.active && !this.isAcceptedFrame()) {
|
|
2027
|
+
await Promise.all([...this.released.values()].map((signal) => signal.promise));
|
|
2028
|
+
}
|
|
2029
|
+
if (!this.ready || this.closing && !this.isAcceptedFrame() && !this.maintenance.getStore()?.active) {
|
|
2030
|
+
throw new NativeAdmissionError("Native admission is not accepting another request.");
|
|
2031
|
+
}
|
|
2032
|
+
const frame = Symbol("native-header-read");
|
|
2033
|
+
this.activeAcceptedFrames.add(frame);
|
|
2034
|
+
const task = this.acceptedFrames.run(frame, () => Promise.resolve().then(work));
|
|
2035
|
+
this.globalReads.add(task);
|
|
2148
2036
|
try {
|
|
2149
|
-
|
|
2150
|
-
try {
|
|
2151
|
-
unlock = await acquireWindowsSemaphore(join3(base.path, "session.lock"), "session");
|
|
2152
|
-
} catch (error) {
|
|
2153
|
-
if (error.code === "EBUSY") refuse("jsonl/writer-active", "Another writer holds the session kernel lease.");
|
|
2154
|
-
throw error;
|
|
2155
|
-
}
|
|
2156
|
-
} else {
|
|
2157
|
-
fd = await this.openFile(join3(base.path, "session.lock"), "write");
|
|
2158
|
-
if (!same(lockIdentity(await fd.stat({ bigint: true })), inventory.lock)) refuse("jsonl/lock-changed", "The frozen stable lock changed.");
|
|
2159
|
-
try {
|
|
2160
|
-
await tryLockExclusive2(fd.fd);
|
|
2161
|
-
} catch (error) {
|
|
2162
|
-
if (["EAGAIN", "EWOULDBLOCK"].includes(error.code ?? "")) refuse("jsonl/writer-active", "Another writer holds the session lock.");
|
|
2163
|
-
throw error;
|
|
2164
|
-
}
|
|
2165
|
-
}
|
|
2166
|
-
await this.assertDirectory(inventory);
|
|
2167
|
-
if (!same(await this.lockIdentity(base.path), inventory.lock)) refuse("jsonl/lock-changed", "The held lock no longer has its frozen namespace identity.");
|
|
2168
|
-
this.leases.set(inventory.directory, record);
|
|
2169
|
-
held = true;
|
|
2037
|
+
return await task;
|
|
2170
2038
|
} finally {
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
await unlock?.();
|
|
2174
|
-
} finally {
|
|
2175
|
-
await fd?.close();
|
|
2176
|
-
}
|
|
2177
|
-
}
|
|
2039
|
+
this.activeAcceptedFrames.delete(frame);
|
|
2040
|
+
this.globalReads.delete(task);
|
|
2178
2041
|
}
|
|
2179
|
-
let releasing;
|
|
2180
|
-
return { release: () => releasing ??= (async () => {
|
|
2181
|
-
record.closing = true;
|
|
2182
|
-
await Promise.allSettled([...record.pending]);
|
|
2183
|
-
try {
|
|
2184
|
-
try {
|
|
2185
|
-
await unlock?.();
|
|
2186
|
-
} finally {
|
|
2187
|
-
await fd?.close();
|
|
2188
|
-
}
|
|
2189
|
-
} finally {
|
|
2190
|
-
if (this.leases.get(inventory.directory) === record) this.leases.delete(inventory.directory);
|
|
2191
|
-
}
|
|
2192
|
-
})() };
|
|
2193
2042
|
}
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
if (!inventory.files.some((saved) => same(saved, row))) refuse("jsonl/scope-changed", "The resource is outside the frozen inventory.");
|
|
2201
|
-
const operation = lease.tail.then(() => this.eraseInner(inventory, row));
|
|
2202
|
-
lease.tail = operation.then(() => void 0, () => void 0);
|
|
2203
|
-
lease.pending.add(operation);
|
|
2204
|
-
try {
|
|
2205
|
-
await operation;
|
|
2206
|
-
} finally {
|
|
2207
|
-
lease.pending.delete(operation);
|
|
2043
|
+
async drain(id) {
|
|
2044
|
+
for (; ; ) {
|
|
2045
|
+
const tasks = id === void 0 ? [...this.pending.values()].flatMap((set) => [...set]) : [...this.pending.get(id) ?? []];
|
|
2046
|
+
tasks.push(...this.globalReads);
|
|
2047
|
+
if (!tasks.length) return;
|
|
2048
|
+
await Promise.allSettled(tasks);
|
|
2208
2049
|
}
|
|
2209
2050
|
}
|
|
2210
|
-
async
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
const
|
|
2217
|
-
|
|
2218
|
-
|
|
2051
|
+
async acquire(id, recovery = false) {
|
|
2052
|
+
while (this.held.has(id)) await this.released.get(id).promise;
|
|
2053
|
+
if (this.failed || this.closing || !this.ready || !recovery && this.retired.has(id)) {
|
|
2054
|
+
throw new NativeAdmissionError("The session cannot enter another retirement scope.");
|
|
2055
|
+
}
|
|
2056
|
+
let resolve4;
|
|
2057
|
+
const signal = { promise: new Promise((done2) => {
|
|
2058
|
+
resolve4 = done2;
|
|
2059
|
+
}), resolve: () => resolve4() };
|
|
2060
|
+
this.held.add(id);
|
|
2061
|
+
this.released.set(id, signal);
|
|
2062
|
+
this.candidates.add(id);
|
|
2063
|
+
const release = () => {
|
|
2064
|
+
this.held.delete(id);
|
|
2065
|
+
this.released.delete(id);
|
|
2066
|
+
signal.resolve();
|
|
2067
|
+
};
|
|
2219
2068
|
try {
|
|
2220
|
-
|
|
2221
|
-
const observed = await this.readRow(base.path, row.resourceId, inventory.header);
|
|
2222
|
-
if (!same(row, observed)) refuse("jsonl/resource-changed", "The physical resource identity or bytes changed after confirmation.");
|
|
2223
|
-
}
|
|
2069
|
+
await this.drain(id);
|
|
2224
2070
|
} catch (error) {
|
|
2225
|
-
|
|
2226
|
-
await this.syncDirectory(base.path);
|
|
2227
|
-
return;
|
|
2228
|
-
}
|
|
2071
|
+
release();
|
|
2229
2072
|
throw error;
|
|
2230
2073
|
}
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
try {
|
|
2237
|
-
const opened = fileIdentity(await handle.stat({ bigint: true }));
|
|
2238
|
-
if (remainder ? opened.size !== "0" || !same(nodeIdentity(await handle.stat({ bigint: true })), nodeIdentityFromRow(row)) : !same(opened, row.identity)) refuse("jsonl/resource-changed", "The opened erasure object differs from the frozen file.");
|
|
2239
|
-
await handle.truncate(0);
|
|
2240
|
-
await handle.sync();
|
|
2241
|
-
} finally {
|
|
2242
|
-
await handle.close();
|
|
2074
|
+
let done = false;
|
|
2075
|
+
return () => {
|
|
2076
|
+
if (!done) {
|
|
2077
|
+
done = true;
|
|
2078
|
+
release();
|
|
2243
2079
|
}
|
|
2244
|
-
|
|
2245
|
-
if (!cleared.isFile() || cleared.nlink !== 1n || cleared.size !== 0n || !same(nodeIdentity(cleared), nodeIdentityFromRow(row))) refuse("jsonl/resource-changed", "The cleared erasure object changed before unlink.");
|
|
2246
|
-
}
|
|
2247
|
-
await unlink(absolute);
|
|
2248
|
-
await this.syncDirectory(base.path);
|
|
2080
|
+
};
|
|
2249
2081
|
}
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
const internals = this.provider;
|
|
2266
|
-
if (await realpath2(internals.root) !== this.root || internals.compression !== this.compression || process.platform === "win32" && resolve2(internals.root).toLowerCase() !== this.root.toLowerCase() || resolve2(this.provider.config.root) !== internals.root || (this.provider.config.compression ?? "zstd") !== this.compression) refuse("jsonl/provider-changed", "The provider root or encoding changed.");
|
|
2267
|
-
const root = await lstat3(this.root, { bigint: true });
|
|
2268
|
-
if (!root.isDirectory() || !same(nodeIdentity(root), this.rootIdentity)) refuse("jsonl/root-changed", "The configured root identity changed.");
|
|
2269
|
-
if (directory !== join3(project(header.cwd), segment(sessionId))) refuse("jsonl/path-invalid", "Directory does not match the stored identity.");
|
|
2270
|
-
const matches = [];
|
|
2271
|
-
for (const entry of await readdir(this.root, { withFileTypes: true })) {
|
|
2272
|
-
const projectPath = join3(this.root, entry.name);
|
|
2273
|
-
if ([`${segment(sessionId)}.jsonl`, `${segment(sessionId)}.jsonl.zstd`].includes(entry.name)) refuse("jsonl/legacy-layout", "Root-level flat session artifacts are not admitted.");
|
|
2274
|
-
if (entry.isSymbolicLink()) refuse("jsonl/unsafe-directory", "Symlink project directories cannot certify a unique target.");
|
|
2275
|
-
if (!entry.isDirectory()) continue;
|
|
2276
|
-
for (const suffix of [".jsonl", ".jsonl.zstd"]) {
|
|
2277
|
-
try {
|
|
2278
|
-
await lstat3(join3(projectPath, segment(sessionId) + suffix));
|
|
2279
|
-
refuse("jsonl/legacy-layout", "Flat session artifacts are not admitted.");
|
|
2280
|
-
} catch (error) {
|
|
2281
|
-
if (!missing(error)) throw error;
|
|
2282
|
-
}
|
|
2283
|
-
}
|
|
2284
|
-
const candidate = join3(projectPath, segment(sessionId));
|
|
2285
|
-
try {
|
|
2286
|
-
const stat = await lstat3(candidate, { bigint: true });
|
|
2287
|
-
if (!stat.isDirectory()) refuse("jsonl/unsafe-directory", "Session directories must be direct regular directories.");
|
|
2288
|
-
matches.push(candidate);
|
|
2289
|
-
} catch (error) {
|
|
2290
|
-
if (!missing(error)) throw error;
|
|
2291
|
-
}
|
|
2292
|
-
}
|
|
2293
|
-
const path = join3(this.root, directory);
|
|
2294
|
-
if (matches.length !== 1 || matches[0] !== path) refuse("jsonl/identity-ambiguous", "The session directory is missing, misplaced, or duplicated across projects.");
|
|
2295
|
-
const actual = await realpath2(path);
|
|
2296
|
-
const rel = relative(this.root, actual);
|
|
2297
|
-
if (actual !== path || rel !== directory || rel.startsWith(`..${sep}`) || isAbsolute2(rel)) refuse("jsonl/unsafe-directory", "The session directory leaves the configured root.");
|
|
2298
|
-
return { path, identity: nodeIdentity(await lstat3(path, { bigint: true })) };
|
|
2082
|
+
};
|
|
2083
|
+
|
|
2084
|
+
// src/host/native-retirement-persistence.ts
|
|
2085
|
+
function nativeServiceInstance(value) {
|
|
2086
|
+
return Reflect.get(value, Symbol.for("cordis.original")) ?? value;
|
|
2087
|
+
}
|
|
2088
|
+
var NativePersistenceAdapter = class {
|
|
2089
|
+
constructor(provider, admission) {
|
|
2090
|
+
this.provider = provider;
|
|
2091
|
+
this.admission = admission;
|
|
2092
|
+
const raw = nativeServiceInstance(provider);
|
|
2093
|
+
if (!(raw.tracker?.openHandles instanceof Set) || !(raw.tracker.writers instanceof Map) || !(raw.tracker.pending instanceof Map) || !(raw.migrationPreparations instanceof Map) || !(raw.coldLogMemo instanceof Map)) throw new Error("JSONL retirement runtime shape does not match the pinned provider.");
|
|
2094
|
+
this.raw = raw;
|
|
2095
|
+
this.originalStat = provider.stat.bind(provider);
|
|
2096
|
+
this.originalList = provider.list.bind(provider);
|
|
2299
2097
|
}
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2098
|
+
raw;
|
|
2099
|
+
originalStat;
|
|
2100
|
+
originalList;
|
|
2101
|
+
restore = [];
|
|
2102
|
+
detached = false;
|
|
2103
|
+
active = true;
|
|
2104
|
+
managedHandles = /* @__PURE__ */ new WeakSet();
|
|
2105
|
+
stat(id) {
|
|
2106
|
+
return this.originalStat(SessionId2(id));
|
|
2304
2107
|
}
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
return lockIdentity(await lstat3(join3(directory, "session.lock"), { bigint: true }));
|
|
2108
|
+
list() {
|
|
2109
|
+
return this.originalList();
|
|
2308
2110
|
}
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
if (process.platform !== "win32" || !lease?.recovering || lease.stamp !== leaseStamp(inventory)) return false;
|
|
2312
|
-
try {
|
|
2313
|
-
const handle = await this.openFile(join3(this.root, inventory.directory, row.resourceId), "read");
|
|
2314
|
-
try {
|
|
2315
|
-
const stat = await handle.stat({ bigint: true });
|
|
2316
|
-
return stat.size === 0n && same(nodeIdentity(stat), nodeIdentityFromRow(row));
|
|
2317
|
-
} finally {
|
|
2318
|
-
await handle.close();
|
|
2319
|
-
}
|
|
2320
|
-
} catch (error) {
|
|
2321
|
-
if (missing(error)) return false;
|
|
2322
|
-
throw error;
|
|
2323
|
-
}
|
|
2111
|
+
busy(id) {
|
|
2112
|
+
return this.raw.tracker.pending.has(id) || this.raw.tracker.writers.has(id) || [...this.raw.tracker.openHandles].some((handle) => handle.id === id) || this.raw.migrationPreparations.size !== 0;
|
|
2324
2113
|
}
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
const posix = process.platform !== "win32";
|
|
2329
|
-
if (posix && !names.includes("session.lock")) refuse("jsonl/lock-missing", "The existing stable writer lock is required.");
|
|
2330
|
-
const rows = [];
|
|
2331
|
-
for (const name2 of names) {
|
|
2332
|
-
if (posix && name2 === "session.lock") continue;
|
|
2333
|
-
const original = frozen?.files.find((row) => row.resourceId === name2);
|
|
2334
|
-
if (frozen && original && await this.isWindowsErasureRemainder(frozen, original)) rows.push(original);
|
|
2335
|
-
else rows.push(await this.readRow(directory, name2, header));
|
|
2336
|
-
}
|
|
2337
|
-
if (!same(names, (await readdir(directory)).sort())) refuse("jsonl/scope-changed", "The directory changed while enumerating resources.");
|
|
2338
|
-
return rows;
|
|
2114
|
+
forget(id) {
|
|
2115
|
+
if (this.busy(id)) throw new NativeAdmissionError("A persistence reference still owns this session.");
|
|
2116
|
+
this.raw.coldLogMemo.delete(id);
|
|
2339
2117
|
}
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
const
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
if (
|
|
2348
|
-
const
|
|
2349
|
-
const
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
digest2.update(buffer.subarray(0, bytesRead));
|
|
2358
|
-
if (headerBytes < HEADER_LIMIT) {
|
|
2359
|
-
const chunk = Buffer.from(buffer.subarray(0, Math.min(bytesRead, HEADER_LIMIT - headerBytes)));
|
|
2360
|
-
chunks.push(chunk);
|
|
2361
|
-
headerBytes += chunk.length;
|
|
2118
|
+
install() {
|
|
2119
|
+
this.originalStat = this.provider.stat.bind(this.provider);
|
|
2120
|
+
this.originalList = this.provider.list.bind(this.provider);
|
|
2121
|
+
const route = (target, name2, idFor, transform, globalRead = false) => {
|
|
2122
|
+
target = nativeServiceInstance(target);
|
|
2123
|
+
const record = target;
|
|
2124
|
+
const original = record[name2];
|
|
2125
|
+
if (typeof original !== "function") throw new Error(`Missing pinned JSONL route ${name2}.`);
|
|
2126
|
+
const own = Object.getOwnPropertyDescriptor(target, name2);
|
|
2127
|
+
const adapter2 = this;
|
|
2128
|
+
const wrapper = function(...args) {
|
|
2129
|
+
const receiver = this;
|
|
2130
|
+
if (adapter2.detached) return Promise.reject(new NativeAdmissionError("This native guard belongs to a replaced retirement owner."));
|
|
2131
|
+
const id = idFor(args);
|
|
2132
|
+
if (!adapter2.active) {
|
|
2133
|
+
if (adapter2.admission.isRetired(id)) return Promise.reject(new NativeAdmissionError("This session has a retirement tombstone."));
|
|
2134
|
+
return Reflect.apply(original, receiver, args);
|
|
2362
2135
|
}
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
resourceId: name2,
|
|
2369
|
-
kind: classified.kind,
|
|
2370
|
-
formatVersion: classified.version,
|
|
2371
|
-
compression: classified.compression,
|
|
2372
|
-
identity: before,
|
|
2373
|
-
digest: digest2.digest("hex"),
|
|
2374
|
-
header
|
|
2136
|
+
const run = () => adapter2.admission.track(id, async () => {
|
|
2137
|
+
const value = await Reflect.apply(original, receiver, args);
|
|
2138
|
+
return transform ? transform(value) : value;
|
|
2139
|
+
});
|
|
2140
|
+
return globalRead ? adapter2.admission.trackGlobal(run) : run();
|
|
2375
2141
|
};
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2142
|
+
Object.defineProperty(target, name2, { configurable: true, writable: true, value: wrapper });
|
|
2143
|
+
this.restore.push(() => {
|
|
2144
|
+
if (record[name2] !== wrapper) throw new Error(`JSONL route ${name2} changed while retirement was installed.`);
|
|
2145
|
+
if (own) Object.defineProperty(target, name2, own);
|
|
2146
|
+
else delete record[name2];
|
|
2147
|
+
});
|
|
2148
|
+
};
|
|
2149
|
+
const headerId = (args) => String(args[0].id);
|
|
2150
|
+
route(this.provider, "create", headerId, (value) => this.manage(value));
|
|
2151
|
+
route(this.provider, "open", (args) => String(args[0]), (value) => this.manage(value), true);
|
|
2152
|
+
for (const method of ["persistBatch", "persistHeader", "truncateTornTail", "acquireWriteLease"]) {
|
|
2153
|
+
route(this.provider, method, headerId);
|
|
2379
2154
|
}
|
|
2155
|
+
route(this.provider, "readStoredLog", (args) => String(args[1]), void 0, true);
|
|
2156
|
+
route(this.provider, "resolveCurrentLog", (args) => String(args[0]));
|
|
2157
|
+
const instance = nativeServiceInstance(this.provider);
|
|
2158
|
+
const rawStat = instance.stat;
|
|
2159
|
+
const rawList = instance.list;
|
|
2160
|
+
const adapter = this;
|
|
2161
|
+
instance.stat = async function(id, options) {
|
|
2162
|
+
if (adapter.detached) throw new NativeAdmissionError("This native observer belongs to a replaced retirement owner.");
|
|
2163
|
+
if (adapter.admission.isRetired(id)) return void 0;
|
|
2164
|
+
const value = await adapter.admission.trackGlobal(() => adapter.admission.track(id, () => rawStat.call(this, id, options)));
|
|
2165
|
+
return adapter.admission.isRetired(id) ? void 0 : value;
|
|
2166
|
+
};
|
|
2167
|
+
instance.list = async function(options) {
|
|
2168
|
+
if (adapter.detached) throw new NativeAdmissionError("This native observer belongs to a replaced retirement owner.");
|
|
2169
|
+
return (await adapter.admission.trackGlobal(() => rawList.call(this, options))).filter((item) => !adapter.admission.isRetired(item.header.id));
|
|
2170
|
+
};
|
|
2171
|
+
this.restore.push(() => {
|
|
2172
|
+
instance.stat = rawStat;
|
|
2173
|
+
instance.list = rawList;
|
|
2174
|
+
});
|
|
2175
|
+
return async () => {
|
|
2176
|
+
await this.admission.drain();
|
|
2177
|
+
this.active = false;
|
|
2178
|
+
if (this.originalRetired()) return;
|
|
2179
|
+
for (const undo of [...this.restore].reverse()) undo();
|
|
2180
|
+
this.restore.length = 0;
|
|
2181
|
+
};
|
|
2380
2182
|
}
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
throw error;
|
|
2387
|
-
}
|
|
2183
|
+
forceRestore() {
|
|
2184
|
+
this.detached = true;
|
|
2185
|
+
this.active = false;
|
|
2186
|
+
for (const undo of [...this.restore].reverse()) undo();
|
|
2187
|
+
this.restore.length = 0;
|
|
2388
2188
|
}
|
|
2389
|
-
|
|
2390
|
-
|
|
2189
|
+
originalRetired() {
|
|
2190
|
+
return this.admission.hasTombstones();
|
|
2391
2191
|
}
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
this.name = "NativeMetadataRefusal";
|
|
2192
|
+
manage(handle) {
|
|
2193
|
+
if (this.managedHandles.has(handle)) return handle;
|
|
2194
|
+
this.managedHandles.add(handle);
|
|
2195
|
+
const methods2 = ["read", "append", "flush"];
|
|
2196
|
+
for (const name2 of methods2) {
|
|
2197
|
+
const original = handle[name2];
|
|
2198
|
+
Object.defineProperty(handle, name2, {
|
|
2199
|
+
configurable: true,
|
|
2200
|
+
writable: true,
|
|
2201
|
+
value: (...args) => !this.active && !this.admission.isRetired(handle.id) ? Reflect.apply(original, handle, args) : this.admission.track(handle.id, async () => Reflect.apply(original, handle, args))
|
|
2202
|
+
});
|
|
2203
|
+
}
|
|
2204
|
+
return handle;
|
|
2406
2205
|
}
|
|
2407
2206
|
};
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
var
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
queryPath: z5.string().nullable()
|
|
2207
|
+
|
|
2208
|
+
// src/host/native-jsonl-files.ts
|
|
2209
|
+
var integer = z4.string().regex(/^\d+$/);
|
|
2210
|
+
var hash = z4.string().regex(/^[a-f0-9]{64}$/);
|
|
2211
|
+
var positiveInteger = z4.string().regex(/^[1-9]\d*$/);
|
|
2212
|
+
var nodeIdentitySchema = z4.object({ dev: integer, ino: positiveInteger, birthtimeNs: positiveInteger }).strict();
|
|
2213
|
+
var fileIdentitySchema = nodeIdentitySchema.extend({ size: integer, mtimeNs: integer, ctimeNs: integer, nlink: z4.literal("1") }).strict();
|
|
2214
|
+
var lockIdentitySchema = nodeIdentitySchema.extend({ size: z4.literal("0"), nlink: z4.literal("1") }).strict();
|
|
2215
|
+
var windowsLockSchema = z4.object({ kind: z4.literal("win32-semaphore"), name: z4.string().min(1) }).strict();
|
|
2216
|
+
var nativeLockSchema = z4.union([lockIdentitySchema, windowsLockSchema]);
|
|
2217
|
+
var headerIdentitySchema = z4.object({
|
|
2218
|
+
id: z4.string().min(1),
|
|
2219
|
+
createdAt: z4.number().int().nonnegative(),
|
|
2220
|
+
cwd: z4.string().nullable(),
|
|
2221
|
+
parentSession: z4.string().nullable(),
|
|
2222
|
+
origin: z4.literal("subagent").nullable(),
|
|
2223
|
+
isSeeded: z4.boolean(),
|
|
2224
|
+
delegationDepth: z4.number().int().nonnegative(),
|
|
2225
|
+
agentPreset: z4.string().nullable()
|
|
2428
2226
|
}).strict();
|
|
2429
|
-
|
|
2430
|
-
|
|
2227
|
+
var fileRowSchema = z4.object({
|
|
2228
|
+
resourceId: z4.string().min(1),
|
|
2229
|
+
kind: z4.enum(["generation", "staging"]),
|
|
2230
|
+
formatVersion: z4.number().int().min(0).max(4),
|
|
2231
|
+
compression: z4.enum(["none", "zstd"]),
|
|
2232
|
+
identity: fileIdentitySchema,
|
|
2233
|
+
digest: hash,
|
|
2234
|
+
header: headerIdentitySchema,
|
|
2235
|
+
revision: hash
|
|
2236
|
+
}).strict().superRefine((value, ctx) => {
|
|
2237
|
+
try {
|
|
2238
|
+
const kind = classify(value.resourceId);
|
|
2239
|
+
if (kind.kind !== value.kind || kind.version !== value.formatVersion || kind.compression !== value.compression || value.revision !== sha(JSON.stringify({ identity: value.identity, digest: value.digest, header: value.header }))) {
|
|
2240
|
+
ctx.addIssue({ code: "custom", message: "Resource classification and identity digest must agree." });
|
|
2241
|
+
}
|
|
2242
|
+
} catch {
|
|
2243
|
+
ctx.addIssue({ code: "custom", message: "Unknown physical resource name." });
|
|
2244
|
+
}
|
|
2245
|
+
});
|
|
2246
|
+
var nativeJsonlFileSchema = fileRowSchema;
|
|
2247
|
+
var nativeJsonlInventorySchema = z4.object({
|
|
2248
|
+
schemaVersion: z4.literal(1),
|
|
2249
|
+
root: z4.string(),
|
|
2250
|
+
rootIdentity: nodeIdentitySchema,
|
|
2251
|
+
directory: z4.string(),
|
|
2252
|
+
directoryIdentity: nodeIdentitySchema,
|
|
2253
|
+
sessionId: z4.string().min(1),
|
|
2254
|
+
compression: z4.enum(["none", "zstd"]),
|
|
2255
|
+
header: headerIdentitySchema,
|
|
2256
|
+
anchor: z4.object({ resourceId: z4.string(), identity: nodeIdentitySchema, header: headerIdentitySchema }).strict(),
|
|
2257
|
+
lock: nativeLockSchema,
|
|
2258
|
+
files: z4.array(fileRowSchema).max(4096),
|
|
2259
|
+
revision: hash
|
|
2260
|
+
}).strict().superRefine((value, ctx) => {
|
|
2261
|
+
try {
|
|
2262
|
+
const anchor = generation(value.anchor.resourceId);
|
|
2263
|
+
if (!isAbsolute2(value.root) || value.directory !== join3(project(value.header.cwd), segment(value.sessionId)) || value.header.id !== value.sessionId || !same(value.anchor.header, value.header) || !anchor || anchor.compression !== value.compression || new Set(value.files.map((row) => row.resourceId)).size !== value.files.length || value.files.some((row) => row.compression !== value.compression || !same(row.header, value.header)) || value.revision !== sha(JSON.stringify(value.files))) {
|
|
2264
|
+
ctx.addIssue({ code: "custom", message: "Frozen directory, header identities, file scope and digest must agree." });
|
|
2265
|
+
}
|
|
2266
|
+
} catch {
|
|
2267
|
+
ctx.addIssue({ code: "custom", message: "Invalid frozen directory or anchor." });
|
|
2268
|
+
}
|
|
2269
|
+
});
|
|
2270
|
+
var NativeJsonlRefusal = class extends Error {
|
|
2271
|
+
constructor(code, message) {
|
|
2272
|
+
super(message);
|
|
2273
|
+
this.code = code;
|
|
2274
|
+
this.name = "NativeJsonlRefusal";
|
|
2275
|
+
}
|
|
2276
|
+
};
|
|
2277
|
+
function refuse(code, message) {
|
|
2278
|
+
throw new NativeJsonlRefusal(code, message);
|
|
2431
2279
|
}
|
|
2432
|
-
function
|
|
2433
|
-
|
|
2434
|
-
if (value !== null && typeof value === "object") return `{${Object.keys(value).sort().map((key2) => `${JSON.stringify(key2)}:${canonical2(value[key2])}`).join(",")}}`;
|
|
2435
|
-
return JSON.stringify(value);
|
|
2280
|
+
function sha(value) {
|
|
2281
|
+
return createHash4("sha256").update(value).digest("hex");
|
|
2436
2282
|
}
|
|
2437
|
-
function
|
|
2438
|
-
return
|
|
2283
|
+
function same(a, b) {
|
|
2284
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
2439
2285
|
}
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
function physicalIdentity(stat) {
|
|
2286
|
+
function nodeIdentity(stat) {
|
|
2287
|
+
if (stat.ino <= 0n || stat.birthtimeNs <= 0n) refuse("jsonl/identity-unavailable", "The filesystem does not provide a stable inode and creation identity.");
|
|
2443
2288
|
return { dev: String(stat.dev), ino: String(stat.ino), birthtimeNs: String(stat.birthtimeNs) };
|
|
2444
2289
|
}
|
|
2445
|
-
function
|
|
2446
|
-
return
|
|
2290
|
+
function nodeIdentityFromRow(row) {
|
|
2291
|
+
return { dev: row.identity.dev, ino: row.identity.ino, birthtimeNs: row.identity.birthtimeNs };
|
|
2447
2292
|
}
|
|
2448
|
-
function
|
|
2449
|
-
|
|
2450
|
-
return
|
|
2293
|
+
function fileIdentity(stat) {
|
|
2294
|
+
if (!stat.isFile() || stat.nlink !== 1n) refuse("jsonl/unsafe-file", "Resources must be exclusive regular files without additional hard links.");
|
|
2295
|
+
return fileIdentitySchema.parse({ ...nodeIdentity(stat), size: String(stat.size), mtimeNs: String(stat.mtimeNs), ctimeNs: String(stat.ctimeNs), nlink: "1" });
|
|
2451
2296
|
}
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
};
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
}
|
|
2519
|
-
assert(id) {
|
|
2520
|
-
this.targets.add(id);
|
|
2521
|
-
if (this.disposed) {
|
|
2522
|
-
if (this.fallbackRetired.has(id)) throw new NativeMetadataRefusal("native/retired", "This retired session remains fenced after adapter withdrawal.");
|
|
2523
|
-
return;
|
|
2297
|
+
function lockIdentity(stat) {
|
|
2298
|
+
const identity3 = fileIdentity(stat);
|
|
2299
|
+
if (identity3.size !== "0") refuse("jsonl/lock-invalid", "The existing stable lock must contain no session content.");
|
|
2300
|
+
return lockIdentitySchema.parse({ ...nodeIdentity(stat), size: "0", nlink: "1" });
|
|
2301
|
+
}
|
|
2302
|
+
function headerIdentity(header) {
|
|
2303
|
+
return headerIdentitySchema.parse({
|
|
2304
|
+
id: header.id,
|
|
2305
|
+
createdAt: header.createdAt,
|
|
2306
|
+
cwd: header.cwd ?? null,
|
|
2307
|
+
parentSession: header.parentSession ?? null,
|
|
2308
|
+
origin: header.origin ?? null,
|
|
2309
|
+
isSeeded: header.isSeeded,
|
|
2310
|
+
delegationDepth: header.delegationDepth ?? 0,
|
|
2311
|
+
agentPreset: header.agentPreset ?? null
|
|
2312
|
+
});
|
|
2313
|
+
}
|
|
2314
|
+
function segment(value) {
|
|
2315
|
+
if (!value) refuse("jsonl/identity-invalid", "A path identity cannot be empty.");
|
|
2316
|
+
if (value === "." || value === "..") return value.replace(/\./g, "~002E");
|
|
2317
|
+
return value.replace(/[^A-Za-z0-9._-]/g, (unit) => `~${unit.charCodeAt(0).toString(16).toUpperCase().padStart(4, "0")}`);
|
|
2318
|
+
}
|
|
2319
|
+
function project(cwd) {
|
|
2320
|
+
if (cwd === null) return "_no-cwd";
|
|
2321
|
+
if (!isAbsolute2(cwd)) refuse("jsonl/identity-invalid", "The stored cwd must be absolute.");
|
|
2322
|
+
const readable = segment(cwd.replace(/[/\\:]+/g, "-")).replace(/^-+/, "") || "root";
|
|
2323
|
+
return `--${readable.slice(0, 251)}--`;
|
|
2324
|
+
}
|
|
2325
|
+
function generation(name2) {
|
|
2326
|
+
const match = /^session(?:\.v([1-9]\d*))?\.jsonl(\.zstd)?$/.exec(name2);
|
|
2327
|
+
if (!match) return;
|
|
2328
|
+
const version = match[1] === void 0 ? 0 : Number(match[1]);
|
|
2329
|
+
if (!Number.isSafeInteger(version) || version > SESSION_FORMAT_VERSION) refuse("jsonl/unsupported-generation", "Unknown future physical generations cannot be retired.");
|
|
2330
|
+
return { version, compression: match[2] ? "zstd" : "none" };
|
|
2331
|
+
}
|
|
2332
|
+
function classify(name2) {
|
|
2333
|
+
const committed = generation(name2);
|
|
2334
|
+
if (committed) return { ...committed, kind: "generation" };
|
|
2335
|
+
const materializing = /^(session(?:\.v[1-9]\d*)?\.jsonl(?:\.zstd)?)\.[a-f0-9]{12}\.tmp$/.exec(name2);
|
|
2336
|
+
if (materializing) return { ...generation(materializing[1]), kind: "staging" };
|
|
2337
|
+
const migrating = /^session\.migration\.[a-f0-9]{16}\.jsonl(\.zstd)?\.tmp$/.exec(name2);
|
|
2338
|
+
if (migrating) return { version: SESSION_FORMAT_VERSION, compression: migrating[1] ? "zstd" : "none", kind: "staging" };
|
|
2339
|
+
refuse("jsonl/unknown-resource", `Unclassified session resource: ${name2}`);
|
|
2340
|
+
}
|
|
2341
|
+
var HEADER_LIMIT = 1024 * 1024;
|
|
2342
|
+
function firstFrameEnd(bytes) {
|
|
2343
|
+
if (bytes.length < 5) return;
|
|
2344
|
+
if (bytes.readUInt32LE(0) !== 4247762216) refuse("jsonl/header-invalid", "The first Zstandard frame is not a standard frame.");
|
|
2345
|
+
const descriptor2 = bytes[4];
|
|
2346
|
+
if (descriptor2 & 24) refuse("jsonl/header-invalid", "Reserved Zstandard frame flags are unsupported.");
|
|
2347
|
+
const single = Boolean(descriptor2 & 32);
|
|
2348
|
+
const sizeFlag = descriptor2 >>> 6;
|
|
2349
|
+
const dictionaryBytes = [0, 1, 2, 4][descriptor2 & 3];
|
|
2350
|
+
const sizeBytes = sizeFlag === 0 ? single ? 1 : 0 : [0, 2, 4, 8][sizeFlag];
|
|
2351
|
+
let cursor = 5 + (single ? 0 : 1) + dictionaryBytes + sizeBytes;
|
|
2352
|
+
if (cursor > bytes.length) return;
|
|
2353
|
+
for (; ; ) {
|
|
2354
|
+
if (cursor + 3 > bytes.length) return;
|
|
2355
|
+
const block = bytes.readUIntLE(cursor, 3);
|
|
2356
|
+
const type = block >>> 1 & 3;
|
|
2357
|
+
if (type === 3) refuse("jsonl/header-invalid", "Reserved Zstandard block type.");
|
|
2358
|
+
cursor += 3 + (type === 1 ? 1 : block >>> 3);
|
|
2359
|
+
if (cursor > bytes.length) return;
|
|
2360
|
+
if (block & 1) {
|
|
2361
|
+
cursor += descriptor2 & 4 ? 4 : 0;
|
|
2362
|
+
return cursor <= bytes.length ? cursor : void 0;
|
|
2524
2363
|
}
|
|
2525
|
-
this.admission.assertAllowed(id);
|
|
2526
2364
|
}
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
}
|
|
2543
|
-
increment(map, id, delta) {
|
|
2544
|
-
const next = (map.get(id) ?? 0) + delta;
|
|
2545
|
-
if (next === 0) map.delete(id);
|
|
2546
|
-
else map.set(id, next);
|
|
2547
|
-
}
|
|
2548
|
-
refuse(code, message) {
|
|
2549
|
-
throw new NativeMetadataRefusal(code, message);
|
|
2550
|
-
}
|
|
2551
|
-
assertInstances() {
|
|
2552
|
-
if (this.guardedMethods.some(({ port, name: name2, replacement }) => port[name2] !== replacement)) {
|
|
2553
|
-
this.refuse("native/guard-replaced", "A native metadata admission guard was replaced.");
|
|
2554
|
-
}
|
|
2555
|
-
if (_NativeMetadataAdapter.get(this.ctx, "workspaceRegistry") !== this.workspace || _NativeMetadataAdapter.get(this.ctx, "sessions") !== this.sessions || _NativeMetadataAdapter.get(this.ctx, "sessionProjectionCache") !== this.cache || _NativeMetadataAdapter.get(this.ctx, "sessionQuery") !== this.query || _NativeMetadataAdapter.get(this.ctx, "sessionController") !== this.controller || this.controller && _NativeMetadataAdapter.get(this.ctx, "agents") !== this.agents || this.cache && (this.cache.requireTable() !== this.cacheTable || this.cacheTable.host.unit.dir !== this.cacheDirectory) || this.query && (this.query.config.path !== this.queryConfiguration.path || this.query.config.openAt !== this.queryConfiguration.openAt) || this.queryProvider === "sqlite-memory-disabled-0.2.0-rc.2" && (this.query._db !== void 0 || this.query._ready !== void 0)) {
|
|
2556
|
-
this.refuse("native/composition-changed", "Native metadata service composition changed.");
|
|
2365
|
+
}
|
|
2366
|
+
function decodeHeader(bytes, compression, version) {
|
|
2367
|
+
let line;
|
|
2368
|
+
if (compression === "none") {
|
|
2369
|
+
const newline = bytes.indexOf(10);
|
|
2370
|
+
if (newline < 0) refuse("jsonl/header-invalid", "A complete physical header is required, including for staging.");
|
|
2371
|
+
line = bytes.subarray(0, newline);
|
|
2372
|
+
} else {
|
|
2373
|
+
const end = firstFrameEnd(bytes);
|
|
2374
|
+
if (end === void 0) refuse("jsonl/header-invalid", "A complete Zstandard header frame is required.");
|
|
2375
|
+
let decoded;
|
|
2376
|
+
try {
|
|
2377
|
+
decoded = zstdDecompressSync(bytes.subarray(0, end), { maxOutputLength: HEADER_LIMIT });
|
|
2378
|
+
} catch {
|
|
2379
|
+
refuse("jsonl/header-invalid", "The bounded first Zstandard frame cannot be decoded.");
|
|
2557
2380
|
}
|
|
2381
|
+
if (!decoded.length || decoded.indexOf(10) !== decoded.length - 1) refuse("jsonl/header-invalid", "The header frame must contain exactly one newline-terminated record.");
|
|
2382
|
+
line = decoded.subarray(0, -1);
|
|
2558
2383
|
}
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
if (this.cache && [...this.cache.dirty.keys()].some((session) => session.id === id)) this.refuse("native/cache-dirty", "Target cache write-behind is not quiescent.");
|
|
2384
|
+
try {
|
|
2385
|
+
const raw = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(line));
|
|
2386
|
+
const result = sessionFormatCatalog.readHeader(raw);
|
|
2387
|
+
if (result.status !== "current" && result.status !== "migration-required") refuse("jsonl/header-invalid", "Malformed or unsupported physical Session header.");
|
|
2388
|
+
if (result.storedVersion !== version) refuse("jsonl/header-invalid", "Physical filename and header versions differ.");
|
|
2389
|
+
return headerIdentity(result.header);
|
|
2390
|
+
} catch (error) {
|
|
2391
|
+
if (error instanceof NativeJsonlRefusal) throw error;
|
|
2392
|
+
refuse("jsonl/header-invalid", "The physical Session header cannot be decoded and identified.");
|
|
2569
2393
|
}
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
const summary = this.controller.listState.summarizeCold(agent.session.header);
|
|
2594
|
-
this.admission.withoutBypass(() => this.ctx.emit("api-session/added", summary));
|
|
2595
|
-
});
|
|
2596
|
-
this.apiHandles.delete(id);
|
|
2394
|
+
}
|
|
2395
|
+
function missing(error) {
|
|
2396
|
+
return error?.code === "ENOENT";
|
|
2397
|
+
}
|
|
2398
|
+
function leaseStamp(inventory) {
|
|
2399
|
+
return JSON.stringify({
|
|
2400
|
+
root: inventory.root,
|
|
2401
|
+
rootIdentity: inventory.rootIdentity,
|
|
2402
|
+
directory: inventory.directory,
|
|
2403
|
+
directoryIdentity: inventory.directoryIdentity,
|
|
2404
|
+
sessionId: inventory.sessionId,
|
|
2405
|
+
compression: inventory.compression,
|
|
2406
|
+
header: inventory.header,
|
|
2407
|
+
anchor: inventory.anchor,
|
|
2408
|
+
lock: inventory.lock
|
|
2409
|
+
});
|
|
2410
|
+
}
|
|
2411
|
+
var NativeJsonlFiles = class _NativeJsonlFiles {
|
|
2412
|
+
constructor(provider, root, compression, rootIdentity) {
|
|
2413
|
+
this.provider = provider;
|
|
2414
|
+
this.root = root;
|
|
2415
|
+
this.compression = compression;
|
|
2416
|
+
this.rootIdentity = rootIdentity;
|
|
2597
2417
|
}
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
if (
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
let
|
|
2610
|
-
try {
|
|
2611
|
-
location = await realpath3(directory);
|
|
2612
|
-
} catch (error) {
|
|
2613
|
-
if (error.code !== "ENOENT") throw error;
|
|
2614
|
-
location = join4(await realpath3(dirname2(directory)), "session_projcache");
|
|
2615
|
-
}
|
|
2616
|
-
const tableDirectory = table.host.unit.tableDir("sessions");
|
|
2617
|
-
let names;
|
|
2418
|
+
leases = /* @__PURE__ */ new Map();
|
|
2419
|
+
static async open(provider) {
|
|
2420
|
+
if (!nativePlatformCandidate()) return;
|
|
2421
|
+
if (!(provider instanceof SessionPersistence) || provider.constructor.name !== "JsonlSessionPersistence" || provider.name !== "session-persistence-jsonl" || SESSION_FORMAT_VERSION !== 4) return;
|
|
2422
|
+
if (createHash4("sha256").update(Function.prototype.toString.call(nativeServiceInstance(provider).constructor)).digest("hex") !== "bb0e12b14bd21adec58c51934f00014e482380102f09aa5e935536c57c0d6e1e") return;
|
|
2423
|
+
const internals = provider;
|
|
2424
|
+
const config = internals.config;
|
|
2425
|
+
if (typeof internals.root !== "string" || !isAbsolute2(internals.root) || internals.compression !== "none" && internals.compression !== "zstd" || typeof config?.root !== "string" || resolve2(config.root) !== internals.root || (config.compression ?? "zstd") !== internals.compression) return;
|
|
2426
|
+
const require2 = createRequire2(import.meta.url);
|
|
2427
|
+
const metadata = require2("@deepseek-ai/dsh-session-persistence-jsonl/package.json");
|
|
2428
|
+
if (metadata.version !== "0.2.1-rc.1") return;
|
|
2429
|
+
let root;
|
|
2618
2430
|
try {
|
|
2619
|
-
|
|
2431
|
+
root = await realpath2(internals.root);
|
|
2620
2432
|
} catch (error) {
|
|
2621
|
-
if (error
|
|
2622
|
-
|
|
2623
|
-
}
|
|
2624
|
-
if (names.some((name2) => name2.startsWith(`${id}.json.bak.`) || /^\.[a-f0-9-]+\.tmp$/.test(name2) || name2.startsWith(windowsCacheStagePrefix) && name2 !== acceptedStage)) {
|
|
2625
|
-
this.refuse("native/cache-unclassified-resource", "A cache backup or unclassified staging document prevents complete target erasure.");
|
|
2433
|
+
if (missing(error)) return;
|
|
2434
|
+
throw error;
|
|
2626
2435
|
}
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2436
|
+
if (process.platform === "win32" && resolve2(internals.root).toLowerCase() !== root.toLowerCase()) return;
|
|
2437
|
+
const identity3 = await lstat3(root, { bigint: true });
|
|
2438
|
+
if (!identity3.isDirectory()) refuse("jsonl/root-invalid", "The configured root must be an existing directory.");
|
|
2439
|
+
if (identity3.ino <= 0n || identity3.birthtimeNs <= 0n) return;
|
|
2440
|
+
return new _NativeJsonlFiles(provider, root, internals.compression, nodeIdentity(identity3));
|
|
2441
|
+
}
|
|
2442
|
+
async inspect(sessionId, header) {
|
|
2443
|
+
if (header.version !== SESSION_FORMAT_VERSION) refuse("jsonl/identity-invalid", "Inspection requires the current logical Session header.");
|
|
2444
|
+
const identity3 = headerIdentity(header);
|
|
2445
|
+
if (sessionId !== identity3.id) refuse("jsonl/identity-invalid", "Requested session and header identities differ.");
|
|
2446
|
+
const directory = join3(project(identity3.cwd), segment(sessionId));
|
|
2447
|
+
const base = await this.directory(directory, sessionId, identity3);
|
|
2448
|
+
const files = await this.rows(base.path, identity3);
|
|
2449
|
+
const canonical3 = files.filter((row) => row.kind === "generation").sort((a, b) => a.formatVersion - b.formatVersion)[0];
|
|
2450
|
+
if (!canonical3) refuse("jsonl/session-not-found", "No identified committed generation exists.");
|
|
2451
|
+
const inventory = {
|
|
2452
|
+
schemaVersion: 1,
|
|
2453
|
+
root: this.root,
|
|
2454
|
+
rootIdentity: this.rootIdentity,
|
|
2455
|
+
directory,
|
|
2456
|
+
directoryIdentity: base.identity,
|
|
2457
|
+
sessionId,
|
|
2458
|
+
compression: this.compression,
|
|
2459
|
+
header: identity3,
|
|
2460
|
+
anchor: {
|
|
2461
|
+
resourceId: canonical3.resourceId,
|
|
2462
|
+
identity: { dev: canonical3.identity.dev, ino: canonical3.identity.ino, birthtimeNs: canonical3.identity.birthtimeNs },
|
|
2463
|
+
header: identity3
|
|
2464
|
+
},
|
|
2465
|
+
lock: await this.lockIdentity(base.path),
|
|
2466
|
+
files,
|
|
2467
|
+
revision: sha(JSON.stringify(files))
|
|
2468
|
+
};
|
|
2469
|
+
await this.directory(directory, sessionId, identity3);
|
|
2470
|
+
return nativeJsonlInventorySchema.parse(inventory);
|
|
2471
|
+
}
|
|
2472
|
+
/** Re-enumerate the entire scope without changing its original durable anchor. */
|
|
2473
|
+
async current(input) {
|
|
2474
|
+
const inventory = this.validate(input);
|
|
2475
|
+
const base = await this.assertDirectory(inventory);
|
|
2476
|
+
const files = await this.rows(base.path, inventory.header, inventory);
|
|
2477
|
+
const lock = await this.lockIdentity(base.path);
|
|
2478
|
+
if (!same(lock, inventory.lock)) refuse("jsonl/lock-changed", "The stable coordination file changed.");
|
|
2479
|
+
return nativeJsonlInventorySchema.parse({ ...inventory, files, revision: sha(JSON.stringify(files)) });
|
|
2480
|
+
}
|
|
2481
|
+
async acquire(input, options = {}) {
|
|
2482
|
+
const inventory = this.validate(input);
|
|
2483
|
+
const base = await this.assertDirectory(inventory);
|
|
2484
|
+
let fd;
|
|
2485
|
+
let unlock;
|
|
2486
|
+
const record = {
|
|
2487
|
+
stamp: leaseStamp(inventory),
|
|
2488
|
+
closing: false,
|
|
2489
|
+
tail: Promise.resolve(),
|
|
2490
|
+
pending: /* @__PURE__ */ new Set(),
|
|
2491
|
+
recovering: options.recovering === true
|
|
2492
|
+
};
|
|
2493
|
+
let held = false;
|
|
2630
2494
|
try {
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2495
|
+
if (process.platform === "win32") {
|
|
2496
|
+
try {
|
|
2497
|
+
unlock = await acquireWindowsSemaphore(join3(base.path, "session.lock"), "session");
|
|
2498
|
+
} catch (error) {
|
|
2499
|
+
if (error.code === "EBUSY") refuse("jsonl/writer-active", "Another writer holds the session kernel lease.");
|
|
2500
|
+
throw error;
|
|
2501
|
+
}
|
|
2502
|
+
} else {
|
|
2503
|
+
fd = await this.openFile(join3(base.path, "session.lock"), "write");
|
|
2504
|
+
if (!same(lockIdentity(await fd.stat({ bigint: true })), inventory.lock)) refuse("jsonl/lock-changed", "The frozen stable lock changed.");
|
|
2505
|
+
try {
|
|
2506
|
+
await tryLockExclusive2(fd.fd);
|
|
2507
|
+
} catch (error) {
|
|
2508
|
+
if (["EAGAIN", "EWOULDBLOCK"].includes(error.code ?? "")) refuse("jsonl/writer-active", "Another writer holds the session lock.");
|
|
2509
|
+
throw error;
|
|
2510
|
+
}
|
|
2637
2511
|
}
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2512
|
+
await this.assertDirectory(inventory);
|
|
2513
|
+
if (!same(await this.lockIdentity(base.path), inventory.lock)) refuse("jsonl/lock-changed", "The held lock no longer has its frozen namespace identity.");
|
|
2514
|
+
this.leases.set(inventory.directory, record);
|
|
2515
|
+
held = true;
|
|
2516
|
+
} finally {
|
|
2517
|
+
if (!held) {
|
|
2518
|
+
try {
|
|
2519
|
+
await unlock?.();
|
|
2520
|
+
} finally {
|
|
2521
|
+
await fd?.close();
|
|
2645
2522
|
}
|
|
2646
|
-
documentDigest = createHash5("sha256").update(bytes).digest("hex");
|
|
2647
|
-
if (process.platform === "win32") documentIdentity = physicalIdentity(after);
|
|
2648
|
-
} finally {
|
|
2649
|
-
await handle.close();
|
|
2650
2523
|
}
|
|
2651
|
-
} catch (error) {
|
|
2652
|
-
if (error.code !== "ENOENT") throw error;
|
|
2653
2524
|
}
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2525
|
+
let releasing;
|
|
2526
|
+
return { release: () => releasing ??= (async () => {
|
|
2527
|
+
record.closing = true;
|
|
2528
|
+
await Promise.allSettled([...record.pending]);
|
|
2529
|
+
try {
|
|
2530
|
+
try {
|
|
2531
|
+
await unlock?.();
|
|
2532
|
+
} finally {
|
|
2533
|
+
await fd?.close();
|
|
2534
|
+
}
|
|
2535
|
+
} finally {
|
|
2536
|
+
if (this.leases.get(inventory.directory) === record) this.leases.delete(inventory.directory);
|
|
2537
|
+
}
|
|
2538
|
+
})() };
|
|
2665
2539
|
}
|
|
2666
|
-
|
|
2540
|
+
/** Caller must have persisted the exact retirement intent before this method. */
|
|
2541
|
+
async erase(input, inputRow) {
|
|
2542
|
+
const inventory = this.validate(input);
|
|
2543
|
+
const row = fileRowSchema.parse(inputRow);
|
|
2544
|
+
const lease = this.leases.get(inventory.directory);
|
|
2545
|
+
if (!lease || lease.closing || lease.stamp !== leaseStamp(inventory)) refuse("jsonl/lease-required", "Erasure requires the same stable namespace kernel lease.");
|
|
2546
|
+
if (!inventory.files.some((saved) => same(saved, row))) refuse("jsonl/scope-changed", "The resource is outside the frozen inventory.");
|
|
2547
|
+
const operation = lease.tail.then(() => this.eraseInner(inventory, row));
|
|
2548
|
+
lease.tail = operation.then(() => void 0, () => void 0);
|
|
2549
|
+
lease.pending.add(operation);
|
|
2667
2550
|
try {
|
|
2668
|
-
|
|
2669
|
-
}
|
|
2670
|
-
|
|
2671
|
-
throw error;
|
|
2551
|
+
await operation;
|
|
2552
|
+
} finally {
|
|
2553
|
+
lease.pending.delete(operation);
|
|
2672
2554
|
}
|
|
2673
2555
|
}
|
|
2674
|
-
async
|
|
2556
|
+
async eraseInner(inventory, row) {
|
|
2557
|
+
const base = await this.assertDirectory(inventory);
|
|
2558
|
+
const names = await readdir(base.path);
|
|
2559
|
+
if (names.some((name2) => name2 !== "session.lock" && !inventory.files.some((saved) => saved.resourceId === name2))) refuse("jsonl/scope-changed", "A new or unknown file entered the frozen scope.");
|
|
2560
|
+
if (!same(await this.lockIdentity(base.path), inventory.lock)) refuse("jsonl/lock-changed", "The held lock changed.");
|
|
2561
|
+
const absolute = join3(base.path, row.resourceId);
|
|
2562
|
+
const target = resolve2(absolute);
|
|
2563
|
+
if (target !== absolute || relative(base.path, target) !== row.resourceId) refuse("jsonl/path-invalid", "The exact resource must be a direct child of its bound directory.");
|
|
2564
|
+
const remainder = await this.isWindowsErasureRemainder(inventory, row);
|
|
2675
2565
|
try {
|
|
2676
|
-
|
|
2677
|
-
|
|
2566
|
+
if (!remainder) {
|
|
2567
|
+
const observed = await this.readRow(base.path, row.resourceId, inventory.header);
|
|
2568
|
+
if (!same(row, observed)) refuse("jsonl/resource-changed", "The physical resource identity or bytes changed after confirmation.");
|
|
2569
|
+
}
|
|
2678
2570
|
} catch (error) {
|
|
2679
|
-
if (error
|
|
2571
|
+
if (missing(error)) {
|
|
2572
|
+
await this.syncDirectory(base.path);
|
|
2573
|
+
return;
|
|
2574
|
+
}
|
|
2680
2575
|
throw error;
|
|
2681
2576
|
}
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
if (
|
|
2686
|
-
|
|
2687
|
-
const after = await handle.stat({ bigint: true });
|
|
2688
|
-
if (before.dev !== after.dev || before.ino !== after.ino || before.birthtimeNs !== after.birthtimeNs || before.size !== after.size || before.mtimeNs !== after.mtimeNs || before.ctimeNs !== after.ctimeNs || after.nlink !== 1n) return false;
|
|
2689
|
-
return allowCleared && bytes.length === 0 || snapshot.documentDigest !== null && createHash5("sha256").update(bytes).digest("hex") === snapshot.documentDigest;
|
|
2690
|
-
}
|
|
2691
|
-
async eraseWindowsCache(snapshot, id, expected, request, table) {
|
|
2692
|
-
if (snapshot.documentDigest !== null && snapshot.physicalIdentity === void 0) return false;
|
|
2693
|
-
if (snapshot.documentDigest === null && snapshot.physicalIdentity !== void 0) return false;
|
|
2694
|
-
const tableDirectory = table.host.unit.tableDir("sessions");
|
|
2695
|
-
const originalPath = join4(tableDirectory, `${id}.json`);
|
|
2696
|
-
const stageName = cacheStageBasename(snapshot, expected, request);
|
|
2697
|
-
const stagePath = join4(tableDirectory, stageName);
|
|
2698
|
-
const current = await this.cacheView(table, id, stageName);
|
|
2699
|
-
if (current.location !== snapshot.location || current.documentDigest !== null && current.documentDigest !== snapshot.documentDigest || current.memoryDigest !== null && current.memoryDigest !== snapshot.memoryDigest || !snapshot.present && current.present) return false;
|
|
2700
|
-
let originalAbsent = await this.pathAbsent(originalPath);
|
|
2701
|
-
let stageAbsent = await this.pathAbsent(stagePath);
|
|
2702
|
-
if (!originalAbsent && !stageAbsent) return false;
|
|
2703
|
-
if (!originalAbsent) {
|
|
2704
|
-
if (snapshot.physicalIdentity === void 0 || current.documentDigest !== snapshot.documentDigest || !samePhysicalIdentity(current.physicalIdentity, snapshot.physicalIdentity)) return false;
|
|
2705
|
-
let handle;
|
|
2577
|
+
await this.assertDirectory(inventory);
|
|
2578
|
+
const latest = fileIdentity(await lstat3(absolute, { bigint: true }));
|
|
2579
|
+
if (remainder ? latest.size !== "0" || !same(nodeIdentity(await lstat3(absolute, { bigint: true })), nodeIdentityFromRow(row)) : !same(latest, row.identity)) refuse("jsonl/resource-changed", "The exact resource changed before erasure.");
|
|
2580
|
+
if (process.platform === "win32") {
|
|
2581
|
+
const handle = await this.openFile(absolute, "write");
|
|
2706
2582
|
try {
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2583
|
+
const opened = fileIdentity(await handle.stat({ bigint: true }));
|
|
2584
|
+
if (remainder ? opened.size !== "0" || !same(nodeIdentity(await handle.stat({ bigint: true })), nodeIdentityFromRow(row)) : !same(opened, row.identity)) refuse("jsonl/resource-changed", "The opened erasure object differs from the frozen file.");
|
|
2585
|
+
await handle.truncate(0);
|
|
2586
|
+
await handle.sync();
|
|
2587
|
+
} finally {
|
|
2588
|
+
await handle.close();
|
|
2711
2589
|
}
|
|
2712
|
-
|
|
2713
|
-
|
|
2590
|
+
const cleared = await lstat3(absolute, { bigint: true });
|
|
2591
|
+
if (!cleared.isFile() || cleared.nlink !== 1n || cleared.size !== 0n || !same(nodeIdentity(cleared), nodeIdentityFromRow(row))) refuse("jsonl/resource-changed", "The cleared erasure object changed before unlink.");
|
|
2592
|
+
}
|
|
2593
|
+
await unlink(absolute);
|
|
2594
|
+
await this.syncDirectory(base.path);
|
|
2595
|
+
}
|
|
2596
|
+
validate(input) {
|
|
2597
|
+
const value = nativeJsonlInventorySchema.parse(input);
|
|
2598
|
+
if (value.root !== this.root || value.compression !== this.compression || !same(value.rootIdentity, this.rootIdentity) || value.sessionId !== value.header.id || value.directory !== join3(project(value.header.cwd), segment(value.sessionId)) || value.files.some((row) => row.resourceId.includes(sep) || row.resourceId.includes("/") || row.resourceId.includes("\\") || row.resourceId === "session.lock" || !same(row.header, value.header))) refuse("jsonl/binding-invalid", "The inventory is not bound to this adapter and exact directory.");
|
|
2599
|
+
const windowsLock = "kind" in value.lock;
|
|
2600
|
+
if (process.platform === "win32" !== windowsLock || "kind" in value.lock && value.lock.name !== windowsSemaphoreName(join3(this.root, value.directory, "session.lock"), "session")) {
|
|
2601
|
+
refuse("jsonl/binding-invalid", "The inventory kernel lease belongs to another platform or namespace.");
|
|
2602
|
+
}
|
|
2603
|
+
if (new Set(value.files.map((row) => row.resourceId)).size !== value.files.length || value.revision !== sha(JSON.stringify(value.files))) refuse("jsonl/binding-invalid", "The frozen resource inventory digest is invalid.");
|
|
2604
|
+
for (const row of value.files) {
|
|
2605
|
+
const classified = classify(row.resourceId);
|
|
2606
|
+
if (classified.kind !== row.kind || classified.version !== row.formatVersion || classified.compression !== row.compression || row.revision !== sha(JSON.stringify({ identity: row.identity, digest: row.digest, header: row.header }))) refuse("jsonl/binding-invalid", "Frozen resource classification or digest changed.");
|
|
2607
|
+
}
|
|
2608
|
+
return value;
|
|
2609
|
+
}
|
|
2610
|
+
async directory(directory, sessionId, header) {
|
|
2611
|
+
const internals = this.provider;
|
|
2612
|
+
if (await realpath2(internals.root) !== this.root || internals.compression !== this.compression || process.platform === "win32" && resolve2(internals.root).toLowerCase() !== this.root.toLowerCase() || resolve2(this.provider.config.root) !== internals.root || (this.provider.config.compression ?? "zstd") !== this.compression) refuse("jsonl/provider-changed", "The provider root or encoding changed.");
|
|
2613
|
+
const root = await lstat3(this.root, { bigint: true });
|
|
2614
|
+
if (!root.isDirectory() || !same(nodeIdentity(root), this.rootIdentity)) refuse("jsonl/root-changed", "The configured root identity changed.");
|
|
2615
|
+
if (directory !== join3(project(header.cwd), segment(sessionId))) refuse("jsonl/path-invalid", "Directory does not match the stored identity.");
|
|
2616
|
+
const matches = [];
|
|
2617
|
+
for (const entry of await readdir(this.root, { withFileTypes: true })) {
|
|
2618
|
+
const projectPath = join3(this.root, entry.name);
|
|
2619
|
+
if ([`${segment(sessionId)}.jsonl`, `${segment(sessionId)}.jsonl.zstd`].includes(entry.name)) refuse("jsonl/legacy-layout", "Root-level flat session artifacts are not admitted.");
|
|
2620
|
+
if (entry.isSymbolicLink()) refuse("jsonl/unsafe-directory", "Symlink project directories cannot certify a unique target.");
|
|
2621
|
+
if (!entry.isDirectory()) continue;
|
|
2622
|
+
for (const suffix of [".jsonl", ".jsonl.zstd"]) {
|
|
2714
2623
|
try {
|
|
2715
|
-
await
|
|
2624
|
+
await lstat3(join3(projectPath, segment(sessionId) + suffix));
|
|
2625
|
+
refuse("jsonl/legacy-layout", "Flat session artifacts are not admitted.");
|
|
2716
2626
|
} catch (error) {
|
|
2717
|
-
if (
|
|
2718
|
-
throw error;
|
|
2627
|
+
if (!missing(error)) throw error;
|
|
2719
2628
|
}
|
|
2720
|
-
} finally {
|
|
2721
|
-
await handle.close();
|
|
2722
2629
|
}
|
|
2723
|
-
|
|
2724
|
-
stageAbsent = await this.pathAbsent(stagePath);
|
|
2725
|
-
if (!originalAbsent || stageAbsent) return false;
|
|
2726
|
-
}
|
|
2727
|
-
if (!stageAbsent) {
|
|
2728
|
-
let handle;
|
|
2630
|
+
const candidate = join3(projectPath, segment(sessionId));
|
|
2729
2631
|
try {
|
|
2730
|
-
|
|
2632
|
+
const stat = await lstat3(candidate, { bigint: true });
|
|
2633
|
+
if (!stat.isDirectory()) refuse("jsonl/unsafe-directory", "Session directories must be direct regular directories.");
|
|
2634
|
+
matches.push(candidate);
|
|
2731
2635
|
} catch (error) {
|
|
2732
|
-
if (error
|
|
2733
|
-
throw error;
|
|
2734
|
-
}
|
|
2735
|
-
try {
|
|
2736
|
-
if (!await this.frozenCacheBytes(handle, snapshot, true)) return false;
|
|
2737
|
-
await this.clearCacheStage(handle);
|
|
2738
|
-
} finally {
|
|
2739
|
-
await handle.close();
|
|
2636
|
+
if (!missing(error)) throw error;
|
|
2740
2637
|
}
|
|
2741
|
-
await this.removeCacheStage(stagePath);
|
|
2742
|
-
if (!await this.pathAbsent(stagePath)) return false;
|
|
2743
2638
|
}
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
const
|
|
2748
|
-
|
|
2639
|
+
const path = join3(this.root, directory);
|
|
2640
|
+
if (matches.length !== 1 || matches[0] !== path) refuse("jsonl/identity-ambiguous", "The session directory is missing, misplaced, or duplicated across projects.");
|
|
2641
|
+
const actual = await realpath2(path);
|
|
2642
|
+
const rel = relative(this.root, actual);
|
|
2643
|
+
if (actual !== path || rel !== directory || rel.startsWith(`..${sep}`) || isAbsolute2(rel)) refuse("jsonl/unsafe-directory", "The session directory leaves the configured root.");
|
|
2644
|
+
return { path, identity: nodeIdentity(await lstat3(path, { bigint: true })) };
|
|
2749
2645
|
}
|
|
2750
|
-
async
|
|
2751
|
-
const
|
|
2752
|
-
if (!
|
|
2753
|
-
return
|
|
2754
|
-
if (process.platform === "win32") return this.eraseWindowsCache(snapshot, id, expected, request, table);
|
|
2755
|
-
const current = await this.cacheView(table, id);
|
|
2756
|
-
if (current.location !== snapshot.location || current.documentDigest !== null && current.documentDigest !== snapshot.documentDigest || current.memoryDigest !== null && current.memoryDigest !== snapshot.memoryDigest || !snapshot.present && current.present) return false;
|
|
2757
|
-
await table.host.unit.deleteRecord("sessions", id);
|
|
2758
|
-
const tableDirectory = table.host.unit.tableDir("sessions");
|
|
2759
|
-
try {
|
|
2760
|
-
await syncDirectory(tableDirectory);
|
|
2761
|
-
} catch (error) {
|
|
2762
|
-
if (error.code !== "ENOENT") throw error;
|
|
2763
|
-
}
|
|
2764
|
-
table.records.delete(id);
|
|
2765
|
-
this.admission.withoutBypass(() => table.host.emitChanged({ domain: table.host.domainName, table: "sessions", key: id, operation: "deleted" }));
|
|
2766
|
-
const final = await this.cacheView(table, id);
|
|
2767
|
-
return final.location === snapshot.location && !final.present;
|
|
2768
|
-
});
|
|
2646
|
+
async assertDirectory(inventory) {
|
|
2647
|
+
const base = await this.directory(inventory.directory, inventory.sessionId, inventory.header);
|
|
2648
|
+
if (!same(base.identity, inventory.directoryIdentity)) refuse("jsonl/directory-changed", "The bound directory incarnation changed.");
|
|
2649
|
+
return base;
|
|
2769
2650
|
}
|
|
2770
|
-
async
|
|
2771
|
-
return
|
|
2651
|
+
async lockIdentity(directory) {
|
|
2652
|
+
if (process.platform === "win32") return { kind: "win32-semaphore", name: windowsSemaphoreName(join3(directory, "session.lock"), "session") };
|
|
2653
|
+
return lockIdentity(await lstat3(join3(directory, "session.lock"), { bigint: true }));
|
|
2772
2654
|
}
|
|
2773
|
-
async
|
|
2774
|
-
this.
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
this.assertCold(sessionId);
|
|
2784
|
-
const domain = this.ctx.storageDomain.get("workspace");
|
|
2785
|
-
if (!domain) throw new Error("Native workspace domain is unavailable.");
|
|
2786
|
-
const global = workspaceDomainState2.parse(domain.global.get());
|
|
2787
|
-
if (!global.archivedSessionIds.some((id) => id === sessionId)) this.refuse("native/not-archived", "Native metadata preparation requires an archived session.");
|
|
2788
|
-
const workspaces = [];
|
|
2789
|
-
for (const [workspaceId, raw] of this.workspace.requireTable().entries()) {
|
|
2790
|
-
const record = workspaceRecord.parse(raw);
|
|
2791
|
-
if (!record.sessionIds.some((id) => id === sessionId)) continue;
|
|
2792
|
-
if (record.sessionIds.some((id) => id !== sessionId && this.workspace.sessionPaths.get(id) !== record.path)) {
|
|
2793
|
-
this.refuse("native/workspace-pruning-required", "Target removal would prune an unrelated Workspace candidate.");
|
|
2655
|
+
async isWindowsErasureRemainder(inventory, row) {
|
|
2656
|
+
const lease = this.leases.get(inventory.directory);
|
|
2657
|
+
if (process.platform !== "win32" || !lease?.recovering || lease.stamp !== leaseStamp(inventory)) return false;
|
|
2658
|
+
try {
|
|
2659
|
+
const handle = await this.openFile(join3(this.root, inventory.directory, row.resourceId), "read");
|
|
2660
|
+
try {
|
|
2661
|
+
const stat = await handle.stat({ bigint: true });
|
|
2662
|
+
return stat.size === 0n && same(nodeIdentity(stat), nodeIdentityFromRow(row));
|
|
2663
|
+
} finally {
|
|
2664
|
+
await handle.close();
|
|
2794
2665
|
}
|
|
2795
|
-
|
|
2666
|
+
} catch (error) {
|
|
2667
|
+
if (missing(error)) return false;
|
|
2668
|
+
throw error;
|
|
2796
2669
|
}
|
|
2797
|
-
const cache = this.cacheTable ? await this.cacheTable.host.enqueue(() => this.cacheView(this.cacheTable, sessionId)) : { present: false, digest: null, location: null, documentDigest: null, memoryDigest: null };
|
|
2798
|
-
return nativeMetadataSnapshotSchema.parse({
|
|
2799
|
-
schemaVersion: 1,
|
|
2800
|
-
sessionId,
|
|
2801
|
-
global: { archived: global.archivedSessionIds.includes(sessionId), pinned: global.pinnedSessionIds.includes(sessionId) },
|
|
2802
|
-
workspaces,
|
|
2803
|
-
cache,
|
|
2804
|
-
queryProvider: this.queryProvider,
|
|
2805
|
-
queryPath: this.queryConfiguration?.path ?? null
|
|
2806
|
-
});
|
|
2807
2670
|
}
|
|
2808
|
-
|
|
2809
|
-
const
|
|
2810
|
-
|
|
2811
|
-
const
|
|
2812
|
-
|
|
2813
|
-
const
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
else delete port[name2];
|
|
2823
|
-
});
|
|
2671
|
+
async rows(directory, header, frozen) {
|
|
2672
|
+
const names = (await readdir(directory)).sort();
|
|
2673
|
+
if (names.length > 4097) refuse("jsonl/scope-too-large", "Session scope exceeds the bounded inventory.");
|
|
2674
|
+
const posix = process.platform !== "win32";
|
|
2675
|
+
if (posix && !names.includes("session.lock")) refuse("jsonl/lock-missing", "The existing stable writer lock is required.");
|
|
2676
|
+
const rows = [];
|
|
2677
|
+
for (const name2 of names) {
|
|
2678
|
+
if (posix && name2 === "session.lock") continue;
|
|
2679
|
+
const original = frozen?.files.find((row) => row.resourceId === name2);
|
|
2680
|
+
if (frozen && original && await this.isWindowsErasureRemainder(frozen, original)) rows.push(original);
|
|
2681
|
+
else rows.push(await this.readRow(directory, name2, header));
|
|
2682
|
+
}
|
|
2683
|
+
if (!same(names, (await readdir(directory)).sort())) refuse("jsonl/scope-changed", "The directory changed while enumerating resources.");
|
|
2684
|
+
return rows;
|
|
2824
2685
|
}
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
this.
|
|
2828
|
-
|
|
2829
|
-
|
|
2686
|
+
async readRow(directory, name2, expected) {
|
|
2687
|
+
const classified = classify(name2);
|
|
2688
|
+
if (classified.compression !== this.compression) refuse("jsonl/encoding-mismatch", "Mixed raw and Zstandard session generations are not admitted.");
|
|
2689
|
+
const path = join3(directory, name2);
|
|
2690
|
+
const before = fileIdentity(await lstat3(path, { bigint: true }));
|
|
2691
|
+
const fd = await this.openFile(path, "read");
|
|
2692
|
+
try {
|
|
2693
|
+
if (!same(before, fileIdentity(await fd.stat({ bigint: true })))) refuse("jsonl/resource-changed", "Resource was replaced before reading.");
|
|
2694
|
+
const digest2 = createHash4("sha256");
|
|
2695
|
+
const chunks = [];
|
|
2696
|
+
let headerBytes = 0;
|
|
2697
|
+
const buffer = Buffer.alloc(64 * 1024);
|
|
2698
|
+
let total = 0n;
|
|
2699
|
+
for (; ; ) {
|
|
2700
|
+
const { bytesRead } = await fd.read(buffer, 0, buffer.length, null);
|
|
2701
|
+
if (!bytesRead) break;
|
|
2702
|
+
total += BigInt(bytesRead);
|
|
2703
|
+
digest2.update(buffer.subarray(0, bytesRead));
|
|
2704
|
+
if (headerBytes < HEADER_LIMIT) {
|
|
2705
|
+
const chunk = Buffer.from(buffer.subarray(0, Math.min(bytesRead, HEADER_LIMIT - headerBytes)));
|
|
2706
|
+
chunks.push(chunk);
|
|
2707
|
+
headerBytes += chunk.length;
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
if (String(total) !== before.size || !same(before, fileIdentity(await fd.stat({ bigint: true }))) || !same(before, fileIdentity(await lstat3(path, { bigint: true })))) refuse("jsonl/resource-changed", "The resource changed while hashing its exact bytes.");
|
|
2711
|
+
const header = decodeHeader(Buffer.concat(chunks), classified.compression, classified.version);
|
|
2712
|
+
if (!same(header, expected)) refuse("jsonl/identity-changed", "A physical generation or staging header belongs to another lifecycle.");
|
|
2713
|
+
const value = {
|
|
2714
|
+
resourceId: name2,
|
|
2715
|
+
kind: classified.kind,
|
|
2716
|
+
formatVersion: classified.version,
|
|
2717
|
+
compression: classified.compression,
|
|
2718
|
+
identity: before,
|
|
2719
|
+
digest: digest2.digest("hex"),
|
|
2720
|
+
header
|
|
2721
|
+
};
|
|
2722
|
+
return fileRowSchema.parse({ ...value, revision: sha(JSON.stringify({ identity: value.identity, digest: value.digest, header })) });
|
|
2723
|
+
} finally {
|
|
2724
|
+
await fd.close();
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
async openFile(path, access) {
|
|
2728
|
+
try {
|
|
2729
|
+
return await openRegularFile(path, access);
|
|
2730
|
+
} catch (error) {
|
|
2731
|
+
if (error instanceof RegularFileRefusal) refuse(error.code === "platform/unsafe-file" ? "jsonl/unsafe-file" : error.code === "platform/identity-unavailable" ? "jsonl/identity-unavailable" : "jsonl/resource-changed", error.message);
|
|
2732
|
+
throw error;
|
|
2733
|
+
}
|
|
2734
|
+
}
|
|
2735
|
+
async syncDirectory(directory) {
|
|
2736
|
+
await syncDirectory(directory);
|
|
2737
|
+
}
|
|
2738
|
+
};
|
|
2739
|
+
|
|
2740
|
+
// src/host/native-retirement-metadata.ts
|
|
2741
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
2742
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
2743
|
+
import { readdir as readdir2, realpath as realpath3, lstat as lstat4, unlink as unlink2 } from "node:fs/promises";
|
|
2744
|
+
import { dirname as dirname2, join as join4, resolve as resolve3 } from "node:path";
|
|
2745
|
+
import { workspaceDomainState as workspaceDomainState2, workspaceRecord } from "@deepseek-ai/dsh-workspace";
|
|
2746
|
+
import { z as z5 } from "zod";
|
|
2747
|
+
var NativeMetadataRefusal = class extends Error {
|
|
2748
|
+
constructor(code, message) {
|
|
2749
|
+
super(message);
|
|
2750
|
+
this.code = code;
|
|
2751
|
+
this.name = "NativeMetadataRefusal";
|
|
2752
|
+
}
|
|
2753
|
+
};
|
|
2754
|
+
var cachePhysicalIdentitySchema = z5.object({
|
|
2755
|
+
dev: z5.string().regex(/^\d+$/),
|
|
2756
|
+
ino: z5.string().regex(/^[1-9]\d*$/),
|
|
2757
|
+
birthtimeNs: z5.string().regex(/^[1-9]\d*$/)
|
|
2758
|
+
}).strict();
|
|
2759
|
+
var nativeMetadataSnapshotSchema = z5.object({
|
|
2760
|
+
schemaVersion: z5.literal(1),
|
|
2761
|
+
sessionId: z5.string().min(1),
|
|
2762
|
+
global: z5.object({ archived: z5.boolean(), pinned: z5.boolean() }).strict(),
|
|
2763
|
+
workspaces: z5.array(z5.object({ workspaceId: z5.string(), record: workspaceRecord }).strict()),
|
|
2764
|
+
cache: z5.object({
|
|
2765
|
+
present: z5.boolean(),
|
|
2766
|
+
digest: z5.string().nullable(),
|
|
2767
|
+
location: z5.string().nullable(),
|
|
2768
|
+
documentDigest: z5.string().nullable(),
|
|
2769
|
+
memoryDigest: z5.string().nullable(),
|
|
2770
|
+
physicalIdentity: cachePhysicalIdentitySchema.optional()
|
|
2771
|
+
}).strict(),
|
|
2772
|
+
queryProvider: z5.enum(["none", "sqlite-0.2.0-rc.2", "sqlite-memory-disabled-0.2.0-rc.2"]),
|
|
2773
|
+
queryPath: z5.string().nullable()
|
|
2774
|
+
}).strict();
|
|
2775
|
+
function object(value) {
|
|
2776
|
+
return value !== null && typeof value === "object" ? value : void 0;
|
|
2777
|
+
}
|
|
2778
|
+
function canonical2(value) {
|
|
2779
|
+
if (Array.isArray(value)) return `[${value.map(canonical2).join(",")}]`;
|
|
2780
|
+
if (value !== null && typeof value === "object") return `{${Object.keys(value).sort().map((key2) => `${JSON.stringify(key2)}:${canonical2(value[key2])}`).join(",")}}`;
|
|
2781
|
+
return JSON.stringify(value);
|
|
2782
|
+
}
|
|
2783
|
+
function digest(value) {
|
|
2784
|
+
return createHash5("sha256").update(canonical2(value)).digest("hex");
|
|
2785
|
+
}
|
|
2786
|
+
var methods = (port, names) => names.every((name2) => typeof port[name2] === "function");
|
|
2787
|
+
var windowsCacheStagePrefix = ".dsh-session-bin-cache-";
|
|
2788
|
+
function physicalIdentity(stat) {
|
|
2789
|
+
return { dev: String(stat.dev), ino: String(stat.ino), birthtimeNs: String(stat.birthtimeNs) };
|
|
2790
|
+
}
|
|
2791
|
+
function samePhysicalIdentity(left, right) {
|
|
2792
|
+
return left !== void 0 && right !== void 0 && left.dev === right.dev && left.ino === right.ino && left.birthtimeNs === right.birthtimeNs;
|
|
2793
|
+
}
|
|
2794
|
+
function cacheStageBasename(snapshot, expected, request) {
|
|
2795
|
+
const value = digest({ schemaVersion: 1, expected, request, cache: snapshot });
|
|
2796
|
+
return `${windowsCacheStagePrefix}${value}.stage`;
|
|
2797
|
+
}
|
|
2798
|
+
var NativeMetadataAdapter = class _NativeMetadataAdapter {
|
|
2799
|
+
constructor(ctx, admission, workspace, sessions, cache, query, controller) {
|
|
2800
|
+
this.ctx = ctx;
|
|
2801
|
+
this.admission = admission;
|
|
2802
|
+
this.workspace = workspace;
|
|
2803
|
+
this.sessions = sessions;
|
|
2804
|
+
this.cache = cache;
|
|
2805
|
+
this.query = query;
|
|
2806
|
+
this.controller = controller;
|
|
2807
|
+
this.queryConfiguration = query ? { path: query.config.path, openAt: query.config.openAt } : void 0;
|
|
2808
|
+
this.queryProvider = !query ? "none" : query.config.openAt === "never" ? "sqlite-memory-disabled-0.2.0-rc.2" : "sqlite-0.2.0-rc.2";
|
|
2809
|
+
this.cacheTable = cache?.requireTable();
|
|
2810
|
+
this.cacheDirectory = this.cacheTable?.host.unit.dir;
|
|
2811
|
+
this.agents = controller ? _NativeMetadataAdapter.get(ctx, "agents") : void 0;
|
|
2812
|
+
}
|
|
2813
|
+
targets = /* @__PURE__ */ new Set();
|
|
2814
|
+
retained = /* @__PURE__ */ new Map();
|
|
2815
|
+
followers = /* @__PURE__ */ new Map();
|
|
2816
|
+
followerStops = /* @__PURE__ */ new Map();
|
|
2817
|
+
activationTarget = new AsyncLocalStorage2();
|
|
2818
|
+
apiHandles = /* @__PURE__ */ new Map();
|
|
2819
|
+
operations = /* @__PURE__ */ new Map();
|
|
2820
|
+
queueTarget = new AsyncLocalStorage2();
|
|
2821
|
+
receivers = new AsyncLocalStorage2();
|
|
2822
|
+
restores = [];
|
|
2823
|
+
guardedMethods = [];
|
|
2824
|
+
guardedEntities = /* @__PURE__ */ new WeakSet();
|
|
2825
|
+
installed = false;
|
|
2826
|
+
disposed = false;
|
|
2827
|
+
detached = false;
|
|
2828
|
+
fallbackRetired = /* @__PURE__ */ new Set();
|
|
2829
|
+
queryConfiguration;
|
|
2830
|
+
queryProvider;
|
|
2831
|
+
cacheTable;
|
|
2832
|
+
cacheDirectory;
|
|
2833
|
+
agents;
|
|
2834
|
+
moveCacheDocument = renameWindowsWriteThrough;
|
|
2835
|
+
clearCacheStage = async (handle) => {
|
|
2836
|
+
await handle.truncate(0);
|
|
2837
|
+
await handle.sync();
|
|
2838
|
+
};
|
|
2839
|
+
removeCacheStage = unlink2;
|
|
2840
|
+
static get(ctx, name2) {
|
|
2841
|
+
const value = object(ctx.get(name2));
|
|
2842
|
+
return object(value?.[Symbol.for("cordis.original")] ?? value);
|
|
2843
|
+
}
|
|
2844
|
+
static async open(ctx, admission) {
|
|
2845
|
+
const workspace = this.get(ctx, "workspaceRegistry");
|
|
2846
|
+
const sessions = this.get(ctx, "sessions");
|
|
2847
|
+
const cache = this.get(ctx, "sessionProjectionCache");
|
|
2848
|
+
const query = this.get(ctx, "sessionQuery");
|
|
2849
|
+
const controller = this.get(ctx, "sessionController");
|
|
2850
|
+
if (!workspace || !sessions || workspace.constructor.name !== "WorkspaceRegistry" || sessions.constructor.name !== "SessionStore" || !methods(workspace, ["list", "requireTable", "requireState", "setState", "enqueueOperation", "indexHeader", "readSessionHeader"]) || ![workspace.headers, workspace.sessionPaths, workspace.invalidSessionPaths].every((map) => map instanceof Map) || !(workspace.missingSessionPaths instanceof Set) || !methods(sessions, ["get", "prepare", "enter", "announce"])) return void 0;
|
|
2851
|
+
if (cache && (cache.constructor.name !== "SessionProjectionCache" || !(cache.dirty instanceof Map) || !methods(cache, ["put", "requireTable", "write", "coldSnapshot", "hydratePrepared", "cachedSnapshot", "cachedPredecessorTitle"]))) return void 0;
|
|
2852
|
+
if (cache) {
|
|
2853
|
+
const table = cache.requireTable();
|
|
2854
|
+
const domain = ctx.storageDomain.get("session_projcache");
|
|
2855
|
+
if (table?.constructor.name !== "KvTableImpl" || table.tableName !== "sessions" || !(table.records instanceof Map) || table.host?.domainName !== "session_projcache" || table.host.unit?.constructor.name !== "PerRecordJsonUnit" || table.host.unit.descriptor?.name !== "session_projcache" || table.host.unit.descriptor.version !== 7 || table.host.unit.descriptor.layout !== "per-record" || typeof table.host.unit.dir !== "string" || !methods(table.host, ["enqueue", "emitChanged", "assertReadable"]) || !methods(table.host.unit, ["loadAll", "deleteRecord", "tableDir"]) || domain?.unit !== table.host.unit) return void 0;
|
|
2856
|
+
}
|
|
2857
|
+
if (query) {
|
|
2858
|
+
const enabled = ["startup", "first-search"].includes(query.config?.openAt);
|
|
2859
|
+
const disabledMemory = query.config?.openAt === "never" && query.config.path === ":memory:" && query._db === void 0 && query._ready === void 0;
|
|
2860
|
+
if (query.constructor.name !== "SqliteSessionQueryEngine" || !enabled && !disabledMemory || typeof query.config?.path !== "string" || !methods(query, ["searchSessions", "listSessions", "observeSession", "_serialized"]) || !(query._observations?.cache instanceof Map) || !methods(query._observations, ["read", "store"])) return void 0;
|
|
2861
|
+
}
|
|
2862
|
+
if (controller && (controller.constructor.name !== "SessionController" || !(controller.history?.closeFollowers instanceof Set) || !methods(controller.history, ["page", "follow", "sourceFor"]) || controller.agents?.constructor.name !== "ApiSessionAgentController" || !methods(controller.agents, ["resumeObserved", "claim", "createAgent"]) || !methods(controller.listState ?? {}, ["summarizeCold"]) || !methods(this.get(ctx, "agents") ?? {}, ["create", "resume", "get"]))) return void 0;
|
|
2863
|
+
return new _NativeMetadataAdapter(ctx, admission, workspace, sessions, cache, query, controller);
|
|
2864
|
+
}
|
|
2865
|
+
assert(id) {
|
|
2866
|
+
this.targets.add(id);
|
|
2867
|
+
if (this.disposed) {
|
|
2868
|
+
if (this.fallbackRetired.has(id)) throw new NativeMetadataRefusal("native/retired", "This retired session remains fenced after adapter withdrawal.");
|
|
2869
|
+
return;
|
|
2870
|
+
}
|
|
2871
|
+
this.admission.assertAllowed(id);
|
|
2872
|
+
}
|
|
2873
|
+
tracked(id, work) {
|
|
2874
|
+
this.targets.add(id);
|
|
2875
|
+
if (this.disposed) {
|
|
2830
2876
|
this.assert(id);
|
|
2831
|
-
return
|
|
2877
|
+
return work();
|
|
2878
|
+
}
|
|
2879
|
+
return this.admission.trackGlobal(() => this.admission.track(id, async () => {
|
|
2880
|
+
this.assert(id);
|
|
2881
|
+
this.increment(this.operations, id, 1);
|
|
2882
|
+
try {
|
|
2883
|
+
return await work();
|
|
2884
|
+
} finally {
|
|
2885
|
+
this.increment(this.operations, id, -1);
|
|
2886
|
+
}
|
|
2832
2887
|
}));
|
|
2833
2888
|
}
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
if (property === Symbol.dispose) return () => {
|
|
2839
|
-
if (disposed) return;
|
|
2840
|
-
disposed = true;
|
|
2841
|
-
try {
|
|
2842
|
-
target[Symbol.dispose]();
|
|
2843
|
-
} finally {
|
|
2844
|
-
this.increment(this.retained, id, -1);
|
|
2845
|
-
}
|
|
2846
|
-
};
|
|
2847
|
-
if (property === "retain") return () => {
|
|
2848
|
-
this.assert(id);
|
|
2849
|
-
return this.guardedObservation(id, target.retain());
|
|
2850
|
-
};
|
|
2851
|
-
if (property === "events" || property === "projections") this.assert(id);
|
|
2852
|
-
return Reflect.get(target, property, target);
|
|
2853
|
-
} });
|
|
2889
|
+
increment(map, id, delta) {
|
|
2890
|
+
const next = (map.get(id) ?? 0) + delta;
|
|
2891
|
+
if (next === 0) map.delete(id);
|
|
2892
|
+
else map.set(id, next);
|
|
2854
2893
|
}
|
|
2855
|
-
|
|
2856
|
-
|
|
2894
|
+
refuse(code, message) {
|
|
2895
|
+
throw new NativeMetadataRefusal(code, message);
|
|
2896
|
+
}
|
|
2897
|
+
assertInstances() {
|
|
2898
|
+
if (this.guardedMethods.some(({ port, name: name2, replacement }) => port[name2] !== replacement)) {
|
|
2899
|
+
this.refuse("native/guard-replaced", "A native metadata admission guard was replaced.");
|
|
2900
|
+
}
|
|
2901
|
+
if (_NativeMetadataAdapter.get(this.ctx, "workspaceRegistry") !== this.workspace || _NativeMetadataAdapter.get(this.ctx, "sessions") !== this.sessions || _NativeMetadataAdapter.get(this.ctx, "sessionProjectionCache") !== this.cache || _NativeMetadataAdapter.get(this.ctx, "sessionQuery") !== this.query || _NativeMetadataAdapter.get(this.ctx, "sessionController") !== this.controller || this.controller && _NativeMetadataAdapter.get(this.ctx, "agents") !== this.agents || this.cache && (this.cache.requireTable() !== this.cacheTable || this.cacheTable.host.unit.dir !== this.cacheDirectory) || this.query && (this.query.config.path !== this.queryConfiguration.path || this.query.config.openAt !== this.queryConfiguration.openAt) || this.queryProvider === "sqlite-memory-disabled-0.2.0-rc.2" && (this.query._db !== void 0 || this.query._ready !== void 0)) {
|
|
2902
|
+
this.refuse("native/composition-changed", "Native metadata service composition changed.");
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2905
|
+
assertCold(id) {
|
|
2906
|
+
if (this.sessions.get(id) !== void 0 || _NativeMetadataAdapter.get(this.ctx, "agents")?.get(id) !== void 0) {
|
|
2907
|
+
this.refuse("native/session-live", "Native deletion currently accepts only cold sessions.");
|
|
2908
|
+
}
|
|
2909
|
+
const cached = this.query?._observations.cache.get(id);
|
|
2910
|
+
if (cached?.refs > 0 || (this.retained.get(id) ?? 0) > 0) this.refuse("native/query-retained", "A retained query observation still owns this session.");
|
|
2911
|
+
if ((this.followers.get(id) ?? 0) > 0) this.refuse("native/follow-retained", "A history follower still owns this session.");
|
|
2912
|
+
const knownFollowers = [...this.followers.values()].reduce((sum, count) => sum + count, 0);
|
|
2913
|
+
if ((this.controller?.history.closeFollowers.size ?? 0) > knownFollowers) this.refuse("native/unknown-follow", "A history follower predates native admission tracking.");
|
|
2914
|
+
if (this.cache && [...this.cache.dirty.keys()].some((session) => session.id === id)) this.refuse("native/cache-dirty", "Target cache write-behind is not quiescent.");
|
|
2915
|
+
}
|
|
2916
|
+
/** Explicit deletion preparation may unload an idle API-owned lifecycle.
|
|
2917
|
+
* No bare registry Agent, foreign handle, or unknown reader is disposed. */
|
|
2918
|
+
async releaseIdle(id) {
|
|
2857
2919
|
this.assertInstances();
|
|
2858
|
-
|
|
2920
|
+
const handle = this.apiHandles.get(id);
|
|
2921
|
+
if (!handle) return;
|
|
2922
|
+
const agent = handle.agent;
|
|
2923
|
+
const registry = this.agents;
|
|
2924
|
+
if (registry.get(id) !== agent || this.sessions.get(id) !== agent.session) return;
|
|
2925
|
+
const activity = await this.admission.withoutBypass(() => this.ctx.waterfall(
|
|
2926
|
+
"workspace/session-activity",
|
|
2927
|
+
{ sessionId: id },
|
|
2928
|
+
async () => []
|
|
2929
|
+
));
|
|
2930
|
+
if (activity.length || agent.constructor.name !== "ReactLoopAgent" || agent.phase?.kind !== "idle" || agent.status !== "idle" || !Array.isArray(agent.inbox?.nextTurn) || !Array.isArray(agent.inbox?.nextStep) || agent.inbox.nextTurn.length || agent.inbox.nextStep.length) {
|
|
2931
|
+
this.refuse("native/session-active", "Native deletion refuses active or queued work.");
|
|
2932
|
+
}
|
|
2933
|
+
if (!this.workspace.archivedSessionIds.includes(id)) this.refuse("native/not-archived", "Only an archived target may be unloaded.");
|
|
2934
|
+
if (registry.get(id) !== agent || this.sessions.get(id) !== agent.session) this.refuse("native/session-live", "The API lifecycle changed.");
|
|
2935
|
+
await this.admission.bypass(id, async () => {
|
|
2936
|
+
await handle.dispose();
|
|
2937
|
+
for (const stop of [...this.followerStops.get(id) ?? []]) await stop();
|
|
2938
|
+
if (this.cache) await this.cache.requireTable().host.enqueue(async () => void 0);
|
|
2939
|
+
const summary = this.controller.listState.summarizeCold({ header: agent.session.header });
|
|
2940
|
+
this.admission.withoutBypass(() => this.ctx.emit("api-session/added", summary));
|
|
2941
|
+
});
|
|
2942
|
+
this.apiHandles.delete(id);
|
|
2943
|
+
}
|
|
2944
|
+
async cacheView(table, id, acceptedStage) {
|
|
2945
|
+
table.host.assertReadable();
|
|
2946
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(id)) this.refuse("native/cache-key-unsupported", "The pinned cache backend cannot own this record key.");
|
|
2947
|
+
const directory = resolve3(table.host.unit.dir);
|
|
2948
|
+
const legacyPath = join4(dirname2(directory), `${table.host.unit.descriptor.name}.json`);
|
|
2859
2949
|
try {
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
this.wrap(this.workspace, name2, (original) => (id, ...args) => this.tracked(
|
|
2869
|
-
id,
|
|
2870
|
-
() => this.queueTarget.run(id, async () => original(id, ...args))
|
|
2871
|
-
));
|
|
2872
|
-
}
|
|
2873
|
-
this.wrap(this.workspace, "enqueueOperation", (original) => (operation) => {
|
|
2874
|
-
const id = this.queueTarget.getStore();
|
|
2875
|
-
return original(async () => {
|
|
2876
|
-
if (id !== void 0) this.assert(id);
|
|
2877
|
-
return operation();
|
|
2878
|
-
});
|
|
2879
|
-
});
|
|
2880
|
-
this.wrap(this.workspace, "setState", (original) => async (next) => {
|
|
2881
|
-
const id = this.queueTarget.getStore();
|
|
2882
|
-
if (id !== void 0) this.assert(id);
|
|
2883
|
-
return original(next);
|
|
2884
|
-
});
|
|
2885
|
-
this.wrap(this.workspace, "indexHeader", (original) => (header) => this.tracked(header.id, async () => {
|
|
2886
|
-
const result = await original(header);
|
|
2887
|
-
this.assert(header.id);
|
|
2888
|
-
return result;
|
|
2889
|
-
}));
|
|
2890
|
-
this.wrap(this.workspace, "readSessionHeader", (original) => (id) => this.tracked(id, async () => {
|
|
2891
|
-
const result = await original(id);
|
|
2892
|
-
this.assert(id);
|
|
2893
|
-
return result;
|
|
2894
|
-
}));
|
|
2895
|
-
for (const entity of this.workspace.list()) this.guardEntity(entity);
|
|
2896
|
-
for (const name2 of ["create", "initializeDefault"]) this.wrap(this.workspace, name2, (original) => async (...args) => {
|
|
2897
|
-
const entity = await original(...args);
|
|
2898
|
-
if (entity) this.guardEntity(entity);
|
|
2899
|
-
return entity;
|
|
2900
|
-
});
|
|
2901
|
-
this.wrap(this.sessions, "prepare", (original) => (id, ...args) => {
|
|
2902
|
-
if (id !== void 0) this.assert(id);
|
|
2903
|
-
const session = original(id, ...args);
|
|
2904
|
-
this.assert(session.id);
|
|
2905
|
-
return session;
|
|
2906
|
-
});
|
|
2907
|
-
for (const name2 of ["enter", "announce"]) this.wrap(this.sessions, name2, (original) => (session) => {
|
|
2908
|
-
this.assert(session.id);
|
|
2909
|
-
return original(session);
|
|
2910
|
-
});
|
|
2911
|
-
if (this.cache) {
|
|
2912
|
-
this.wrap(this.cache, "put", (original) => (id, ...args) => this.tracked(id, async () => original(id, ...args)));
|
|
2913
|
-
for (const name2 of ["cachedSnapshot", "cachedPredecessorTitle", "coldSnapshot"]) this.wrap(
|
|
2914
|
-
this.cache,
|
|
2915
|
-
name2,
|
|
2916
|
-
(original) => (header, ...args) => {
|
|
2917
|
-
this.assert(header.id);
|
|
2918
|
-
return original(header, ...args);
|
|
2919
|
-
}
|
|
2920
|
-
);
|
|
2921
|
-
this.wrap(this.cache, "hydratePrepared", (original) => (session, ...args) => {
|
|
2922
|
-
this.assert(session.id);
|
|
2923
|
-
return original(session, ...args);
|
|
2924
|
-
});
|
|
2925
|
-
}
|
|
2926
|
-
if (this.query) {
|
|
2927
|
-
this.wrap(this.query, "_serialized", (original) => (signal, operation) => this.admission.trackGlobal(async () => original(signal, operation)));
|
|
2928
|
-
this.wrap(this.query._observations, "read", (original) => (id, ...args) => this.tracked(id, async () => {
|
|
2929
|
-
const observation = await original(id, ...args);
|
|
2930
|
-
try {
|
|
2931
|
-
this.assert(id);
|
|
2932
|
-
return this.guardedObservation(id, observation);
|
|
2933
|
-
} catch (error) {
|
|
2934
|
-
observation[Symbol.dispose]();
|
|
2935
|
-
throw error;
|
|
2936
|
-
}
|
|
2937
|
-
}));
|
|
2938
|
-
this.wrap(this.query._observations, "store", (original) => (id, ...args) => {
|
|
2939
|
-
this.assert(id);
|
|
2940
|
-
return original(id, ...args);
|
|
2941
|
-
});
|
|
2942
|
-
for (const name2 of ["readSession", "readTitle", "readTitleSnapshot", "listEvents", "filterEvents", "readSurface", "traceSession"]) {
|
|
2943
|
-
if (typeof this.query[name2] === "function") this.wrap(this.query, name2, (original) => (id, ...args) => this.tracked(id, async () => {
|
|
2944
|
-
const result = await original(id, ...args);
|
|
2945
|
-
this.assert(id);
|
|
2946
|
-
return result;
|
|
2947
|
-
}));
|
|
2948
|
-
}
|
|
2949
|
-
for (const name2 of ["searchEvents", "readEvent", "traceEvent"]) if (typeof this.query[name2] === "function") {
|
|
2950
|
-
this.wrap(this.query, name2, (original) => (request, ...args) => this.tracked(
|
|
2951
|
-
request.sessionId,
|
|
2952
|
-
async () => {
|
|
2953
|
-
const result = await original(request, ...args);
|
|
2954
|
-
this.assert(request.sessionId);
|
|
2955
|
-
return result;
|
|
2956
|
-
}
|
|
2957
|
-
));
|
|
2958
|
-
}
|
|
2959
|
-
}
|
|
2960
|
-
if (this.controller) {
|
|
2961
|
-
const adapter = this;
|
|
2962
|
-
for (const name2 of ["resumeObserved", "createOrAdopt"]) {
|
|
2963
|
-
this.wrap(this.controller.agents, name2, (original) => (id, ...args) => this.tracked(id, () => this.activationTarget.run(id, async () => original(id, ...args))));
|
|
2964
|
-
}
|
|
2965
|
-
const agents = this.agents;
|
|
2966
|
-
for (const name2 of ["create", "resume"]) {
|
|
2967
|
-
this.wrap(agents, name2, (original) => (options, ...args) => {
|
|
2968
|
-
const id = String(name2 === "create" ? options.sessionId : options.resumeSessionId);
|
|
2969
|
-
return this.tracked(id, async () => {
|
|
2970
|
-
const handle = await original(options, ...args);
|
|
2971
|
-
if (this.activationTarget.getStore() === id && options.parentAgent === void 0 && handle.agent.id === id && handle.agent.session.header.origin !== "subagent" && typeof handle.dispose === "function") this.apiHandles.set(id, handle);
|
|
2972
|
-
return handle;
|
|
2973
|
-
});
|
|
2974
|
-
});
|
|
2975
|
-
}
|
|
2976
|
-
this.restores.push(this.ctx.on("agent/disposed", ({ agent }) => {
|
|
2977
|
-
if (this.apiHandles.get(agent.id)?.agent === agent) this.apiHandles.delete(agent.id);
|
|
2978
|
-
}, { global: true }));
|
|
2979
|
-
this.wrap(this.controller.history, "follow", (original) => function(request, signal) {
|
|
2980
|
-
const id = String(request.address.sessionId);
|
|
2981
|
-
adapter.assert(id);
|
|
2982
|
-
return (async function* () {
|
|
2983
|
-
adapter.assert(id);
|
|
2984
|
-
adapter.increment(adapter.followers, id, 1);
|
|
2985
|
-
const cancellation = new AbortController();
|
|
2986
|
-
const iterator = original(request, AbortSignal.any([signal, cancellation.signal]));
|
|
2987
|
-
const stops = adapter.followerStops.get(id) ?? /* @__PURE__ */ new Set();
|
|
2988
|
-
adapter.followerStops.set(id, stops);
|
|
2989
|
-
let finished = false;
|
|
2990
|
-
const finish = () => {
|
|
2991
|
-
if (finished) return;
|
|
2992
|
-
finished = true;
|
|
2993
|
-
adapter.increment(adapter.followers, id, -1);
|
|
2994
|
-
stops.delete(stop);
|
|
2995
|
-
if (!stops.size) adapter.followerStops.delete(id);
|
|
2996
|
-
};
|
|
2997
|
-
const stop = async () => {
|
|
2998
|
-
cancellation.abort();
|
|
2999
|
-
await iterator.return();
|
|
3000
|
-
finish();
|
|
3001
|
-
};
|
|
3002
|
-
stops.add(stop);
|
|
3003
|
-
try {
|
|
3004
|
-
for await (const frame of iterator) {
|
|
3005
|
-
adapter.assert(id);
|
|
3006
|
-
yield frame;
|
|
3007
|
-
}
|
|
3008
|
-
} finally {
|
|
3009
|
-
finish();
|
|
3010
|
-
}
|
|
3011
|
-
})();
|
|
3012
|
-
});
|
|
3013
|
-
}
|
|
2950
|
+
await lstat4(legacyPath);
|
|
2951
|
+
this.refuse("native/cache-unclassified-resource", "A legacy whole-unit cache could republish the retired target document.");
|
|
2952
|
+
} catch (error) {
|
|
2953
|
+
if (error.code !== "ENOENT") throw error;
|
|
2954
|
+
}
|
|
2955
|
+
let location;
|
|
2956
|
+
try {
|
|
2957
|
+
location = await realpath3(directory);
|
|
3014
2958
|
} catch (error) {
|
|
3015
|
-
|
|
3016
|
-
|
|
3017
|
-
this.installed = false;
|
|
3018
|
-
throw error;
|
|
2959
|
+
if (error.code !== "ENOENT") throw error;
|
|
2960
|
+
location = join4(await realpath3(dirname2(directory)), "session_projcache");
|
|
3019
2961
|
}
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
if (
|
|
3026
|
-
|
|
3027
|
-
this.restores.length = 0;
|
|
3028
|
-
}
|
|
3029
|
-
})();
|
|
3030
|
-
}
|
|
3031
|
-
/** The verified owner completes all asynchronous takeover work before this
|
|
3032
|
-
* synchronous restoration and installs its successor guards without yielding. */
|
|
3033
|
-
forceRestore() {
|
|
3034
|
-
if (this.restores.length && this.guardedMethods.some(({ port, name: name2, replacement }) => port[name2] !== replacement)) {
|
|
3035
|
-
throw new Error("Native metadata guards changed before trusted takeover.");
|
|
2962
|
+
const tableDirectory = table.host.unit.tableDir("sessions");
|
|
2963
|
+
let names;
|
|
2964
|
+
try {
|
|
2965
|
+
names = await readdir2(tableDirectory);
|
|
2966
|
+
} catch (error) {
|
|
2967
|
+
if (error.code !== "ENOENT") throw error;
|
|
2968
|
+
names = [];
|
|
3036
2969
|
}
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
for (const restore of [...this.restores].reverse()) restore();
|
|
3040
|
-
this.restores.length = 0;
|
|
3041
|
-
this.guardedMethods.length = 0;
|
|
3042
|
-
this.fallbackRetired.clear();
|
|
3043
|
-
this.apiHandles.clear();
|
|
3044
|
-
}
|
|
3045
|
-
async converge(input, expected, request) {
|
|
3046
|
-
const snapshot = nativeMetadataSnapshotSchema.parse(input);
|
|
3047
|
-
const id = snapshot.sessionId;
|
|
3048
|
-
this.targets.add(id);
|
|
3049
|
-
this.assertInstances();
|
|
3050
|
-
if (snapshot.queryProvider !== this.queryProvider || snapshot.queryPath !== (this.queryConfiguration?.path ?? null)) return false;
|
|
3051
|
-
if (id !== expected.sessionId || id !== request.expected.sessionId || expected.storeId !== request.expected.storeId || expected.lifecycleId !== request.expected.lifecycleId || !this.admission.isRetired(id)) {
|
|
3052
|
-
this.refuse("native/maintenance-binding-changed", "Native convergence requires the exact durable retirement binding.");
|
|
2970
|
+
if (names.some((name2) => name2.startsWith(`${id}.json.bak.`) || /^\.[a-f0-9-]+\.tmp$/.test(name2) || name2.startsWith(windowsCacheStagePrefix) && name2 !== acceptedStage)) {
|
|
2971
|
+
this.refuse("native/cache-unclassified-resource", "A cache backup or unclassified staging document prevents complete target erasure.");
|
|
3053
2972
|
}
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
}
|
|
3069
|
-
const
|
|
3070
|
-
if (
|
|
3071
|
-
|
|
3072
|
-
...state,
|
|
3073
|
-
archivedSessionIds: state.archivedSessionIds.filter((other) => other !== id),
|
|
3074
|
-
pinnedSessionIds: state.pinnedSessionIds.filter((other) => other !== id)
|
|
3075
|
-
});
|
|
3076
|
-
}
|
|
3077
|
-
this.workspace.headers.delete(id);
|
|
3078
|
-
this.workspace.sessionPaths.delete(id);
|
|
3079
|
-
this.workspace.invalidSessionPaths.delete(id);
|
|
3080
|
-
return true;
|
|
3081
|
-
};
|
|
3082
|
-
if (!await this.workspace.enqueueOperation(convergeWorkspace)) return false;
|
|
3083
|
-
if (this.query) {
|
|
3084
|
-
if (this.queryProvider === "sqlite-0.2.0-rc.2") {
|
|
3085
|
-
await this.query.searchSessions({ query: "native-retirement-reconciliation", limit: 1 });
|
|
2973
|
+
let documentDigest = null;
|
|
2974
|
+
let documentIdentity;
|
|
2975
|
+
const path = join4(tableDirectory, `${id}.json`);
|
|
2976
|
+
try {
|
|
2977
|
+
let handle;
|
|
2978
|
+
try {
|
|
2979
|
+
handle = await openRegularFile(path, "read");
|
|
2980
|
+
} catch (error) {
|
|
2981
|
+
if (error instanceof RegularFileRefusal) this.refuse(error.code === "platform/unsafe-file" ? "native/cache-unsafe-document" : "native/cache-document-changed", error.message);
|
|
2982
|
+
throw error;
|
|
2983
|
+
}
|
|
2984
|
+
try {
|
|
2985
|
+
const before = await handle.stat({ bigint: true });
|
|
2986
|
+
const bytes = await handle.readFile();
|
|
2987
|
+
const after = await handle.stat({ bigint: true });
|
|
2988
|
+
const current = await lstat4(path, { bigint: true });
|
|
2989
|
+
if (!current.isFile() || before.dev !== after.dev || before.ino !== after.ino || before.size !== after.size || before.birthtimeNs !== after.birthtimeNs || before.mtimeNs !== after.mtimeNs || before.ctimeNs !== after.ctimeNs || after.nlink !== 1n || after.dev !== current.dev || after.ino !== current.ino || after.birthtimeNs !== current.birthtimeNs || after.size !== current.size || after.mtimeNs !== current.mtimeNs || after.ctimeNs !== current.ctimeNs) {
|
|
2990
|
+
this.refuse("native/cache-document-changed", "The physical cache document changed while observing its bytes.");
|
|
3086
2991
|
}
|
|
3087
|
-
|
|
3088
|
-
|
|
2992
|
+
documentDigest = createHash5("sha256").update(bytes).digest("hex");
|
|
2993
|
+
if (process.platform === "win32") documentIdentity = physicalIdentity(after);
|
|
2994
|
+
} finally {
|
|
2995
|
+
await handle.close();
|
|
3089
2996
|
}
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
}
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
activeAcceptedFrames = /* @__PURE__ */ new Set();
|
|
3105
|
-
held = /* @__PURE__ */ new Set();
|
|
3106
|
-
released = /* @__PURE__ */ new Map();
|
|
3107
|
-
pending = /* @__PURE__ */ new Map();
|
|
3108
|
-
globalReads = /* @__PURE__ */ new Set();
|
|
3109
|
-
retired = /* @__PURE__ */ new Set();
|
|
3110
|
-
candidates = /* @__PURE__ */ new Set();
|
|
3111
|
-
closing = false;
|
|
3112
|
-
failed = false;
|
|
3113
|
-
ready = false;
|
|
3114
|
-
requiresRecovery() {
|
|
3115
|
-
return this.failed;
|
|
3116
|
-
}
|
|
3117
|
-
activate() {
|
|
3118
|
-
this.ready = true;
|
|
3119
|
-
}
|
|
3120
|
-
suspend() {
|
|
3121
|
-
this.failed = true;
|
|
2997
|
+
} catch (error) {
|
|
2998
|
+
if (error.code !== "ENOENT") throw error;
|
|
2999
|
+
}
|
|
3000
|
+
const memory = table.records.get(id);
|
|
3001
|
+
const memoryDigest = memory === void 0 ? null : digest(memory);
|
|
3002
|
+
const present = documentDigest !== null || memoryDigest !== null;
|
|
3003
|
+
return {
|
|
3004
|
+
present,
|
|
3005
|
+
digest: present ? digest({ documentDigest, memoryDigest }) : null,
|
|
3006
|
+
location,
|
|
3007
|
+
documentDigest,
|
|
3008
|
+
memoryDigest,
|
|
3009
|
+
...documentIdentity ? { physicalIdentity: documentIdentity } : {}
|
|
3010
|
+
};
|
|
3122
3011
|
}
|
|
3123
|
-
|
|
3124
|
-
|
|
3012
|
+
async openCacheDocument(path, access) {
|
|
3013
|
+
try {
|
|
3014
|
+
return await openRegularFile(path, access);
|
|
3015
|
+
} catch (error) {
|
|
3016
|
+
if (error instanceof RegularFileRefusal) this.refuse(error.code === "platform/unsafe-file" ? "native/cache-unsafe-document" : "native/cache-document-changed", error.message);
|
|
3017
|
+
throw error;
|
|
3018
|
+
}
|
|
3125
3019
|
}
|
|
3126
|
-
|
|
3127
|
-
|
|
3020
|
+
async pathAbsent(path) {
|
|
3021
|
+
try {
|
|
3022
|
+
await lstat4(path);
|
|
3023
|
+
return false;
|
|
3024
|
+
} catch (error) {
|
|
3025
|
+
if (error.code === "ENOENT") return true;
|
|
3026
|
+
throw error;
|
|
3027
|
+
}
|
|
3128
3028
|
}
|
|
3129
|
-
|
|
3130
|
-
|
|
3029
|
+
async frozenCacheBytes(handle, snapshot, allowCleared) {
|
|
3030
|
+
const before = await handle.stat({ bigint: true });
|
|
3031
|
+
if (!before.isFile() || before.nlink !== 1n || !samePhysicalIdentity(physicalIdentity(before), snapshot.physicalIdentity)) return false;
|
|
3032
|
+
const bytes = await handle.readFile();
|
|
3033
|
+
const after = await handle.stat({ bigint: true });
|
|
3034
|
+
if (before.dev !== after.dev || before.ino !== after.ino || before.birthtimeNs !== after.birthtimeNs || before.size !== after.size || before.mtimeNs !== after.mtimeNs || before.ctimeNs !== after.ctimeNs || after.nlink !== 1n) return false;
|
|
3035
|
+
return allowCleared && bytes.length === 0 || snapshot.documentDigest !== null && createHash5("sha256").update(bytes).digest("hex") === snapshot.documentDigest;
|
|
3131
3036
|
}
|
|
3132
|
-
|
|
3133
|
-
|
|
3037
|
+
async eraseWindowsCache(snapshot, id, expected, request, table) {
|
|
3038
|
+
if (snapshot.documentDigest !== null && snapshot.physicalIdentity === void 0) return false;
|
|
3039
|
+
if (snapshot.documentDigest === null && snapshot.physicalIdentity !== void 0) return false;
|
|
3040
|
+
const tableDirectory = table.host.unit.tableDir("sessions");
|
|
3041
|
+
const originalPath = join4(tableDirectory, `${id}.json`);
|
|
3042
|
+
const stageName = cacheStageBasename(snapshot, expected, request);
|
|
3043
|
+
const stagePath = join4(tableDirectory, stageName);
|
|
3044
|
+
const current = await this.cacheView(table, id, stageName);
|
|
3045
|
+
if (current.location !== snapshot.location || current.documentDigest !== null && current.documentDigest !== snapshot.documentDigest || current.memoryDigest !== null && current.memoryDigest !== snapshot.memoryDigest || !snapshot.present && current.present) return false;
|
|
3046
|
+
let originalAbsent = await this.pathAbsent(originalPath);
|
|
3047
|
+
let stageAbsent = await this.pathAbsent(stagePath);
|
|
3048
|
+
if (!originalAbsent && !stageAbsent) return false;
|
|
3049
|
+
if (!originalAbsent) {
|
|
3050
|
+
if (snapshot.physicalIdentity === void 0 || current.documentDigest !== snapshot.documentDigest || !samePhysicalIdentity(current.physicalIdentity, snapshot.physicalIdentity)) return false;
|
|
3051
|
+
let handle;
|
|
3052
|
+
try {
|
|
3053
|
+
handle = await this.openCacheDocument(originalPath, "write");
|
|
3054
|
+
} catch (error) {
|
|
3055
|
+
if (error.code === "ENOENT") return false;
|
|
3056
|
+
throw error;
|
|
3057
|
+
}
|
|
3058
|
+
try {
|
|
3059
|
+
if (!await this.frozenCacheBytes(handle, snapshot, false)) return false;
|
|
3060
|
+
try {
|
|
3061
|
+
await this.moveCacheDocument(originalPath, stagePath);
|
|
3062
|
+
} catch (error) {
|
|
3063
|
+
if (["EEXIST", "EXDEV", "ENOENT"].includes(error.code ?? "")) return false;
|
|
3064
|
+
throw error;
|
|
3065
|
+
}
|
|
3066
|
+
} finally {
|
|
3067
|
+
await handle.close();
|
|
3068
|
+
}
|
|
3069
|
+
originalAbsent = await this.pathAbsent(originalPath);
|
|
3070
|
+
stageAbsent = await this.pathAbsent(stagePath);
|
|
3071
|
+
if (!originalAbsent || stageAbsent) return false;
|
|
3072
|
+
}
|
|
3073
|
+
if (!stageAbsent) {
|
|
3074
|
+
let handle;
|
|
3075
|
+
try {
|
|
3076
|
+
handle = await this.openCacheDocument(stagePath, "write");
|
|
3077
|
+
} catch (error) {
|
|
3078
|
+
if (error.code === "ENOENT") return false;
|
|
3079
|
+
throw error;
|
|
3080
|
+
}
|
|
3081
|
+
try {
|
|
3082
|
+
if (!await this.frozenCacheBytes(handle, snapshot, true)) return false;
|
|
3083
|
+
await this.clearCacheStage(handle);
|
|
3084
|
+
} finally {
|
|
3085
|
+
await handle.close();
|
|
3086
|
+
}
|
|
3087
|
+
await this.removeCacheStage(stagePath);
|
|
3088
|
+
if (!await this.pathAbsent(stagePath)) return false;
|
|
3089
|
+
}
|
|
3090
|
+
if (!await this.pathAbsent(originalPath) || !await this.pathAbsent(stagePath)) return false;
|
|
3091
|
+
table.records.delete(id);
|
|
3092
|
+
this.admission.withoutBypass(() => table.host.emitChanged({ domain: table.host.domainName, table: "sessions", key: id, operation: "deleted" }));
|
|
3093
|
+
const final = await this.cacheView(table, id, stageName);
|
|
3094
|
+
return final.location === snapshot.location && !final.present && await this.pathAbsent(stagePath);
|
|
3134
3095
|
}
|
|
3135
|
-
|
|
3136
|
-
const
|
|
3137
|
-
|
|
3096
|
+
async eraseCache(snapshot, id, expected, request) {
|
|
3097
|
+
const table = this.cacheTable;
|
|
3098
|
+
if (!table) return !snapshot.present && snapshot.location === null;
|
|
3099
|
+
return table.host.enqueue(async () => {
|
|
3100
|
+
if (process.platform === "win32") return this.eraseWindowsCache(snapshot, id, expected, request, table);
|
|
3101
|
+
const current = await this.cacheView(table, id);
|
|
3102
|
+
if (current.location !== snapshot.location || current.documentDigest !== null && current.documentDigest !== snapshot.documentDigest || current.memoryDigest !== null && current.memoryDigest !== snapshot.memoryDigest || !snapshot.present && current.present) return false;
|
|
3103
|
+
await table.host.unit.deleteRecord("sessions", id);
|
|
3104
|
+
const tableDirectory = table.host.unit.tableDir("sessions");
|
|
3105
|
+
try {
|
|
3106
|
+
await syncDirectory(tableDirectory);
|
|
3107
|
+
} catch (error) {
|
|
3108
|
+
if (error.code !== "ENOENT") throw error;
|
|
3109
|
+
}
|
|
3110
|
+
table.records.delete(id);
|
|
3111
|
+
this.admission.withoutBypass(() => table.host.emitChanged({ domain: table.host.domainName, table: "sessions", key: id, operation: "deleted" }));
|
|
3112
|
+
const final = await this.cacheView(table, id);
|
|
3113
|
+
return final.location === snapshot.location && !final.present;
|
|
3114
|
+
});
|
|
3138
3115
|
}
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
if (maintenance?.active && maintenance.id === id && !(this.failed && this.candidates.has(id))) return;
|
|
3142
|
-
if (!this.ready || this.closing && !this.isAcceptedFrame() || this.failed && this.candidates.has(id) || this.retired.has(id) || this.held.has(id) && !this.isAcceptedFrame()) {
|
|
3143
|
-
throw new NativeAdmissionError("The session is reserved for retirement or awaiting recovery.");
|
|
3144
|
-
}
|
|
3116
|
+
async capture(sessionId) {
|
|
3117
|
+
return this.admission.bypass(sessionId, () => this.captureInner(sessionId));
|
|
3145
3118
|
}
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3119
|
+
async captureInner(sessionId) {
|
|
3120
|
+
this.targets.add(sessionId);
|
|
3121
|
+
this.assertInstances();
|
|
3122
|
+
this.assertCold(sessionId);
|
|
3123
|
+
const activity = await this.admission.withoutBypass(() => this.ctx.waterfall(
|
|
3124
|
+
"workspace/session-activity",
|
|
3125
|
+
{ sessionId },
|
|
3126
|
+
async () => []
|
|
3127
|
+
));
|
|
3128
|
+
if (activity.length !== 0) this.refuse("native/session-active", "Native deletion refuses active work.");
|
|
3129
|
+
this.assertCold(sessionId);
|
|
3130
|
+
const domain = this.ctx.storageDomain.get("workspace");
|
|
3131
|
+
if (!domain) throw new Error("Native workspace domain is unavailable.");
|
|
3132
|
+
const global = workspaceDomainState2.parse(domain.global.get());
|
|
3133
|
+
if (!global.archivedSessionIds.some((id) => id === sessionId)) this.refuse("native/not-archived", "Native metadata preparation requires an archived session.");
|
|
3134
|
+
const workspaces = [];
|
|
3135
|
+
for (const [workspaceId, raw] of this.workspace.requireTable().entries()) {
|
|
3136
|
+
const record = workspaceRecord.parse(raw);
|
|
3137
|
+
if (!record.sessionIds.some((id) => id === sessionId)) continue;
|
|
3138
|
+
if (record.sessionIds.some((id) => id !== sessionId && this.workspace.sessionPaths.get(id) !== record.path && !this.workspace.missingSessionPaths.has(id))) {
|
|
3139
|
+
this.refuse("native/workspace-pruning-required", "Target removal would prune an unrelated Workspace candidate.");
|
|
3154
3140
|
}
|
|
3155
|
-
|
|
3156
|
-
return value;
|
|
3157
|
-
} catch (error) {
|
|
3158
|
-
token.active = false;
|
|
3159
|
-
throw error;
|
|
3141
|
+
workspaces.push({ workspaceId, record });
|
|
3160
3142
|
}
|
|
3143
|
+
const cache = this.cacheTable ? await this.cacheTable.host.enqueue(() => this.cacheView(this.cacheTable, sessionId)) : { present: false, digest: null, location: null, documentDigest: null, memoryDigest: null };
|
|
3144
|
+
return nativeMetadataSnapshotSchema.parse({
|
|
3145
|
+
schemaVersion: 1,
|
|
3146
|
+
sessionId,
|
|
3147
|
+
global: { archived: global.archivedSessionIds.includes(sessionId), pinned: global.pinnedSessionIds.includes(sessionId) },
|
|
3148
|
+
workspaces,
|
|
3149
|
+
cache,
|
|
3150
|
+
queryProvider: this.queryProvider,
|
|
3151
|
+
queryPath: this.queryConfiguration?.path ?? null
|
|
3152
|
+
});
|
|
3161
3153
|
}
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
const
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
}).catch(() => {
|
|
3154
|
+
wrap(port, name2, replace) {
|
|
3155
|
+
const descriptor2 = Object.getOwnPropertyDescriptor(port, name2);
|
|
3156
|
+
const original = port[name2];
|
|
3157
|
+
const adapter = this;
|
|
3158
|
+
const guarded = replace((...args) => Reflect.apply(original, adapter.receivers.getStore() ?? port, args));
|
|
3159
|
+
const replacement = function(...args) {
|
|
3160
|
+
if (adapter.detached) throw new NativeMetadataRefusal("native/stale-reference", "Reacquire native methods after retirement adapter takeover.");
|
|
3161
|
+
return adapter.receivers.run(this, () => guarded(...args));
|
|
3162
|
+
};
|
|
3163
|
+
Object.defineProperty(port, name2, { configurable: true, writable: true, value: replacement });
|
|
3164
|
+
this.guardedMethods.push({ port, name: name2, replacement });
|
|
3165
|
+
this.restores.push(() => {
|
|
3166
|
+
if (port[name2] !== replacement) throw new Error(`Native metadata guard ${name2} was replaced before withdrawal.`);
|
|
3167
|
+
if (descriptor2) Object.defineProperty(port, name2, descriptor2);
|
|
3168
|
+
else delete port[name2];
|
|
3178
3169
|
});
|
|
3179
|
-
return task;
|
|
3180
3170
|
}
|
|
3181
|
-
|
|
3182
|
-
if (this.
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
throw new NativeAdmissionError("Native admission is not accepting another request.");
|
|
3190
|
-
}
|
|
3191
|
-
const frame = Symbol("native-header-read");
|
|
3192
|
-
this.activeAcceptedFrames.add(frame);
|
|
3193
|
-
const task = this.acceptedFrames.run(frame, () => Promise.resolve().then(work));
|
|
3194
|
-
this.globalReads.add(task);
|
|
3195
|
-
try {
|
|
3196
|
-
return await task;
|
|
3197
|
-
} finally {
|
|
3198
|
-
this.activeAcceptedFrames.delete(frame);
|
|
3199
|
-
this.globalReads.delete(task);
|
|
3200
|
-
}
|
|
3171
|
+
guardEntity(entity) {
|
|
3172
|
+
if (this.guardedEntities.has(entity)) return;
|
|
3173
|
+
this.guardedEntities.add(entity);
|
|
3174
|
+
this.wrap(entity, "attachSession", (original) => (id) => this.tracked(id, async () => {
|
|
3175
|
+
const result = await original(id);
|
|
3176
|
+
this.assert(id);
|
|
3177
|
+
return result;
|
|
3178
|
+
}));
|
|
3201
3179
|
}
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
if (
|
|
3207
|
-
|
|
3208
|
-
|
|
3180
|
+
guardedObservation(id, observation) {
|
|
3181
|
+
this.increment(this.retained, id, 1);
|
|
3182
|
+
let disposed = false;
|
|
3183
|
+
return new Proxy(observation, { get: (target, property) => {
|
|
3184
|
+
if (property === Symbol.dispose) return () => {
|
|
3185
|
+
if (disposed) return;
|
|
3186
|
+
disposed = true;
|
|
3187
|
+
try {
|
|
3188
|
+
target[Symbol.dispose]();
|
|
3189
|
+
} finally {
|
|
3190
|
+
this.increment(this.retained, id, -1);
|
|
3191
|
+
}
|
|
3192
|
+
};
|
|
3193
|
+
if (property === "retain") return () => {
|
|
3194
|
+
this.assert(id);
|
|
3195
|
+
return this.guardedObservation(id, target.retain());
|
|
3196
|
+
};
|
|
3197
|
+
if (property === "events" || property === "projections") this.assert(id);
|
|
3198
|
+
return Reflect.get(target, property, target);
|
|
3199
|
+
} });
|
|
3209
3200
|
}
|
|
3210
|
-
async
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
}
|
|
3215
|
-
let resolve4;
|
|
3216
|
-
const signal = { promise: new Promise((done2) => {
|
|
3217
|
-
resolve4 = done2;
|
|
3218
|
-
}), resolve: () => resolve4() };
|
|
3219
|
-
this.held.add(id);
|
|
3220
|
-
this.released.set(id, signal);
|
|
3221
|
-
this.candidates.add(id);
|
|
3222
|
-
const release = () => {
|
|
3223
|
-
this.held.delete(id);
|
|
3224
|
-
this.released.delete(id);
|
|
3225
|
-
signal.resolve();
|
|
3226
|
-
};
|
|
3201
|
+
async install() {
|
|
3202
|
+
if (this.installed) throw new Error("Native metadata adapter is already installed.");
|
|
3203
|
+
this.assertInstances();
|
|
3204
|
+
this.installed = true;
|
|
3227
3205
|
try {
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3206
|
+
for (const name2 of ["workspace", ...this.cache ? ["session_projcache"] : []]) {
|
|
3207
|
+
const domain = this.ctx.storageDomain.get(name2);
|
|
3208
|
+
if (!domain || domain.constructor.name !== "DomainImpl" || !methods(domain, ["emitChanged"])) {
|
|
3209
|
+
throw new Error("Native metadata domain publication shape changed.");
|
|
3210
|
+
}
|
|
3211
|
+
this.wrap(domain, "emitChanged", (original) => (change) => this.admission.withoutBypass(() => original(change)));
|
|
3212
|
+
}
|
|
3213
|
+
for (const name2 of ["archiveSession", "unarchiveSession", "pinSession", "unpinSession"]) {
|
|
3214
|
+
this.wrap(this.workspace, name2, (original) => (id, ...args) => this.tracked(
|
|
3215
|
+
id,
|
|
3216
|
+
() => this.queueTarget.run(id, async () => original(id, ...args))
|
|
3217
|
+
));
|
|
3218
|
+
}
|
|
3219
|
+
this.wrap(this.workspace, "enqueueOperation", (original) => (operation) => {
|
|
3220
|
+
const id = this.queueTarget.getStore();
|
|
3221
|
+
return original(async () => {
|
|
3222
|
+
if (id !== void 0) this.assert(id);
|
|
3223
|
+
return operation();
|
|
3224
|
+
});
|
|
3225
|
+
});
|
|
3226
|
+
this.wrap(this.workspace, "setState", (original) => async (next) => {
|
|
3227
|
+
const id = this.queueTarget.getStore();
|
|
3228
|
+
if (id !== void 0) this.assert(id);
|
|
3229
|
+
return original(next);
|
|
3230
|
+
});
|
|
3231
|
+
this.wrap(this.workspace, "indexHeader", (original) => (header) => this.tracked(header.id, async () => {
|
|
3232
|
+
const result = await original(header);
|
|
3233
|
+
this.assert(header.id);
|
|
3234
|
+
return result;
|
|
3235
|
+
}));
|
|
3236
|
+
this.wrap(this.workspace, "readSessionHeader", (original) => (id) => this.tracked(id, async () => {
|
|
3237
|
+
const result = await original(id);
|
|
3238
|
+
this.assert(id);
|
|
3239
|
+
return result;
|
|
3240
|
+
}));
|
|
3241
|
+
for (const entity of this.workspace.list()) this.guardEntity(entity);
|
|
3242
|
+
for (const name2 of ["create", "initializeDefault"]) this.wrap(this.workspace, name2, (original) => async (...args) => {
|
|
3243
|
+
const entity = await original(...args);
|
|
3244
|
+
if (entity) this.guardEntity(entity);
|
|
3245
|
+
return entity;
|
|
3246
|
+
});
|
|
3247
|
+
this.wrap(this.sessions, "prepare", (original) => (id, ...args) => {
|
|
3248
|
+
if (id !== void 0) this.assert(id);
|
|
3249
|
+
const session = original(id, ...args);
|
|
3250
|
+
this.assert(session.id);
|
|
3251
|
+
return session;
|
|
3252
|
+
});
|
|
3253
|
+
for (const name2 of ["enter", "announce"]) this.wrap(this.sessions, name2, (original) => (session) => {
|
|
3254
|
+
this.assert(session.id);
|
|
3255
|
+
return original(session);
|
|
3256
|
+
});
|
|
3257
|
+
if (this.cache) {
|
|
3258
|
+
this.wrap(this.cache, "put", (original) => (id, ...args) => this.tracked(id, async () => original(id, ...args)));
|
|
3259
|
+
for (const name2 of ["cachedSnapshot", "cachedPredecessorTitle", "coldSnapshot"]) this.wrap(
|
|
3260
|
+
this.cache,
|
|
3261
|
+
name2,
|
|
3262
|
+
(original) => (header, ...args) => {
|
|
3263
|
+
this.assert(header.id);
|
|
3264
|
+
return original(header, ...args);
|
|
3265
|
+
}
|
|
3266
|
+
);
|
|
3267
|
+
this.wrap(this.cache, "hydratePrepared", (original) => (session, ...args) => {
|
|
3268
|
+
this.assert(session.id);
|
|
3269
|
+
return original(session, ...args);
|
|
3270
|
+
});
|
|
3271
|
+
}
|
|
3272
|
+
if (this.query) {
|
|
3273
|
+
this.wrap(this.query, "_serialized", (original) => (signal, operation) => this.admission.trackGlobal(async () => original(signal, operation)));
|
|
3274
|
+
this.wrap(this.query._observations, "read", (original) => (id, ...args) => this.tracked(id, async () => {
|
|
3275
|
+
const observation = await original(id, ...args);
|
|
3276
|
+
try {
|
|
3277
|
+
this.assert(id);
|
|
3278
|
+
return this.guardedObservation(id, observation);
|
|
3279
|
+
} catch (error) {
|
|
3280
|
+
observation[Symbol.dispose]();
|
|
3281
|
+
throw error;
|
|
3282
|
+
}
|
|
3283
|
+
}));
|
|
3284
|
+
this.wrap(this.query._observations, "store", (original) => (id, ...args) => {
|
|
3285
|
+
this.assert(id);
|
|
3286
|
+
return original(id, ...args);
|
|
3287
|
+
});
|
|
3288
|
+
for (const name2 of ["readSession", "readTitle", "readTitleSnapshot", "listEvents", "filterEvents", "readSurface", "traceSession"]) {
|
|
3289
|
+
if (typeof this.query[name2] === "function") this.wrap(this.query, name2, (original) => (id, ...args) => this.tracked(id, async () => {
|
|
3290
|
+
const result = await original(id, ...args);
|
|
3291
|
+
this.assert(id);
|
|
3292
|
+
return result;
|
|
3293
|
+
}));
|
|
3294
|
+
}
|
|
3295
|
+
for (const name2 of ["searchEvents", "readEvent", "traceEvent"]) if (typeof this.query[name2] === "function") {
|
|
3296
|
+
this.wrap(this.query, name2, (original) => (request, ...args) => this.tracked(
|
|
3297
|
+
request.sessionId,
|
|
3298
|
+
async () => {
|
|
3299
|
+
const result = await original(request, ...args);
|
|
3300
|
+
this.assert(request.sessionId);
|
|
3301
|
+
return result;
|
|
3302
|
+
}
|
|
3303
|
+
));
|
|
3304
|
+
}
|
|
3238
3305
|
}
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
// src/host/native-retirement-persistence.ts
|
|
3244
|
-
import { SessionId as SessionId2 } from "@deepseek-ai/dsh-session";
|
|
3245
|
-
function nativeServiceInstance(value) {
|
|
3246
|
-
return Reflect.get(value, Symbol.for("cordis.original")) ?? value;
|
|
3247
|
-
}
|
|
3248
|
-
var NativePersistenceAdapter = class {
|
|
3249
|
-
constructor(provider, admission) {
|
|
3250
|
-
this.provider = provider;
|
|
3251
|
-
this.admission = admission;
|
|
3252
|
-
const raw = nativeServiceInstance(provider);
|
|
3253
|
-
if (!(raw.tracker?.openHandles instanceof Set) || !(raw.tracker.writers instanceof Map) || !(raw.tracker.pending instanceof Map) || !(raw.migrationPreparations instanceof Map) || !(raw.coldLogMemo instanceof Map)) throw new Error("JSONL retirement runtime shape does not match the pinned provider.");
|
|
3254
|
-
this.raw = raw;
|
|
3255
|
-
this.originalStat = provider.stat.bind(provider);
|
|
3256
|
-
this.originalList = provider.list.bind(provider);
|
|
3257
|
-
}
|
|
3258
|
-
raw;
|
|
3259
|
-
originalStat;
|
|
3260
|
-
originalList;
|
|
3261
|
-
restore = [];
|
|
3262
|
-
detached = false;
|
|
3263
|
-
active = true;
|
|
3264
|
-
managedHandles = /* @__PURE__ */ new WeakSet();
|
|
3265
|
-
stat(id) {
|
|
3266
|
-
return this.originalStat(SessionId2(id));
|
|
3267
|
-
}
|
|
3268
|
-
list() {
|
|
3269
|
-
return this.originalList();
|
|
3270
|
-
}
|
|
3271
|
-
busy(id) {
|
|
3272
|
-
return this.raw.tracker.pending.has(id) || this.raw.tracker.writers.has(id) || [...this.raw.tracker.openHandles].some((handle) => handle.id === id) || this.raw.migrationPreparations.size !== 0;
|
|
3273
|
-
}
|
|
3274
|
-
forget(id) {
|
|
3275
|
-
if (this.busy(id)) throw new NativeAdmissionError("A persistence reference still owns this session.");
|
|
3276
|
-
this.raw.coldLogMemo.delete(id);
|
|
3277
|
-
}
|
|
3278
|
-
install() {
|
|
3279
|
-
this.originalStat = this.provider.stat.bind(this.provider);
|
|
3280
|
-
this.originalList = this.provider.list.bind(this.provider);
|
|
3281
|
-
const route = (target, name2, idFor, transform, globalRead = false) => {
|
|
3282
|
-
target = nativeServiceInstance(target);
|
|
3283
|
-
const record = target;
|
|
3284
|
-
const original = record[name2];
|
|
3285
|
-
if (typeof original !== "function") throw new Error(`Missing pinned JSONL route ${name2}.`);
|
|
3286
|
-
const own = Object.getOwnPropertyDescriptor(target, name2);
|
|
3287
|
-
const adapter2 = this;
|
|
3288
|
-
const wrapper = function(...args) {
|
|
3289
|
-
const receiver = this;
|
|
3290
|
-
if (adapter2.detached) return Promise.reject(new NativeAdmissionError("This native guard belongs to a replaced retirement owner."));
|
|
3291
|
-
const id = idFor(args);
|
|
3292
|
-
if (!adapter2.active) {
|
|
3293
|
-
if (adapter2.admission.isRetired(id)) return Promise.reject(new NativeAdmissionError("This session has a retirement tombstone."));
|
|
3294
|
-
return Reflect.apply(original, receiver, args);
|
|
3306
|
+
if (this.controller) {
|
|
3307
|
+
const adapter = this;
|
|
3308
|
+
for (const name2 of ["resumeObserved", "claim", "createAgent"]) {
|
|
3309
|
+
this.wrap(this.controller.agents, name2, (original) => (id, ...args) => this.tracked(id, () => this.activationTarget.run(id, async () => original(id, ...args))));
|
|
3295
3310
|
}
|
|
3296
|
-
const
|
|
3297
|
-
|
|
3298
|
-
|
|
3311
|
+
const agents = this.agents;
|
|
3312
|
+
for (const name2 of ["create", "resume"]) {
|
|
3313
|
+
this.wrap(agents, name2, (original) => (options, ...args) => {
|
|
3314
|
+
const id = String(name2 === "create" ? options.sessionId : options.resumeSessionId);
|
|
3315
|
+
return this.tracked(id, async () => {
|
|
3316
|
+
const handle = await original(options, ...args);
|
|
3317
|
+
if (this.activationTarget.getStore() === id && options.parentAgent === void 0 && handle.agent.id === id && handle.agent.session.header.origin !== "subagent" && typeof handle.dispose === "function") this.apiHandles.set(id, handle);
|
|
3318
|
+
return handle;
|
|
3319
|
+
});
|
|
3320
|
+
});
|
|
3321
|
+
}
|
|
3322
|
+
this.restores.push(this.ctx.on("agent/disposed", ({ agent }) => {
|
|
3323
|
+
if (this.apiHandles.get(agent.id)?.agent === agent) this.apiHandles.delete(agent.id);
|
|
3324
|
+
}, { global: true }));
|
|
3325
|
+
this.wrap(this.controller.history, "follow", (original) => function(request, signal) {
|
|
3326
|
+
const id = String(request.address.sessionId);
|
|
3327
|
+
adapter.assert(id);
|
|
3328
|
+
return (async function* () {
|
|
3329
|
+
adapter.assert(id);
|
|
3330
|
+
adapter.increment(adapter.followers, id, 1);
|
|
3331
|
+
const cancellation = new AbortController();
|
|
3332
|
+
const iterator = original(request, AbortSignal.any([signal, cancellation.signal]));
|
|
3333
|
+
const stops = adapter.followerStops.get(id) ?? /* @__PURE__ */ new Set();
|
|
3334
|
+
adapter.followerStops.set(id, stops);
|
|
3335
|
+
let finished = false;
|
|
3336
|
+
const finish = () => {
|
|
3337
|
+
if (finished) return;
|
|
3338
|
+
finished = true;
|
|
3339
|
+
adapter.increment(adapter.followers, id, -1);
|
|
3340
|
+
stops.delete(stop);
|
|
3341
|
+
if (!stops.size) adapter.followerStops.delete(id);
|
|
3342
|
+
};
|
|
3343
|
+
const stop = async () => {
|
|
3344
|
+
cancellation.abort();
|
|
3345
|
+
await iterator.return();
|
|
3346
|
+
finish();
|
|
3347
|
+
};
|
|
3348
|
+
stops.add(stop);
|
|
3349
|
+
try {
|
|
3350
|
+
for await (const frame of iterator) {
|
|
3351
|
+
adapter.assert(id);
|
|
3352
|
+
yield frame;
|
|
3353
|
+
}
|
|
3354
|
+
} finally {
|
|
3355
|
+
finish();
|
|
3356
|
+
}
|
|
3357
|
+
})();
|
|
3299
3358
|
});
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
this.
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
else delete record[name2];
|
|
3307
|
-
});
|
|
3308
|
-
};
|
|
3309
|
-
const headerId = (args) => String(args[0].id);
|
|
3310
|
-
route(this.provider, "create", headerId, (value) => this.manage(value));
|
|
3311
|
-
route(this.provider, "open", (args) => String(args[0]), (value) => this.manage(value), true);
|
|
3312
|
-
for (const method of ["persistBatch", "persistHeader", "truncateTornTail", "acquireWriteLease"]) {
|
|
3313
|
-
route(this.provider, method, headerId);
|
|
3359
|
+
}
|
|
3360
|
+
} catch (error) {
|
|
3361
|
+
for (const restore of [...this.restores].reverse()) restore();
|
|
3362
|
+
this.restores.length = 0;
|
|
3363
|
+
this.installed = false;
|
|
3364
|
+
throw error;
|
|
3314
3365
|
}
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
return adapter.admission.isRetired(id) ? void 0 : value;
|
|
3326
|
-
};
|
|
3327
|
-
instance.list = async function(options) {
|
|
3328
|
-
if (adapter.detached) throw new NativeAdmissionError("This native observer belongs to a replaced retirement owner.");
|
|
3329
|
-
return (await adapter.admission.trackGlobal(() => rawList.call(this, options))).filter((item) => !adapter.admission.isRetired(item.header.id));
|
|
3330
|
-
};
|
|
3331
|
-
this.restore.push(() => {
|
|
3332
|
-
instance.stat = rawStat;
|
|
3333
|
-
instance.list = rawList;
|
|
3334
|
-
});
|
|
3335
|
-
return async () => {
|
|
3336
|
-
await this.admission.drain();
|
|
3337
|
-
this.active = false;
|
|
3338
|
-
if (this.originalRetired()) return;
|
|
3339
|
-
for (const undo of [...this.restore].reverse()) undo();
|
|
3340
|
-
this.restore.length = 0;
|
|
3341
|
-
};
|
|
3366
|
+
let closing;
|
|
3367
|
+
return () => closing ??= (async () => {
|
|
3368
|
+
this.fallbackRetired = new Set([...this.targets].filter((id) => this.admission.isRetired(id)));
|
|
3369
|
+
this.disposed = true;
|
|
3370
|
+
this.apiHandles.clear();
|
|
3371
|
+
if (this.fallbackRetired.size === 0) {
|
|
3372
|
+
for (const restore of [...this.restores].reverse()) restore();
|
|
3373
|
+
this.restores.length = 0;
|
|
3374
|
+
}
|
|
3375
|
+
})();
|
|
3342
3376
|
}
|
|
3377
|
+
/** The verified owner completes all asynchronous takeover work before this
|
|
3378
|
+
* synchronous restoration and installs its successor guards without yielding. */
|
|
3343
3379
|
forceRestore() {
|
|
3380
|
+
if (this.restores.length && this.guardedMethods.some(({ port, name: name2, replacement }) => port[name2] !== replacement)) {
|
|
3381
|
+
throw new Error("Native metadata guards changed before trusted takeover.");
|
|
3382
|
+
}
|
|
3344
3383
|
this.detached = true;
|
|
3345
|
-
this.
|
|
3346
|
-
for (const
|
|
3347
|
-
this.
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3384
|
+
this.disposed = true;
|
|
3385
|
+
for (const restore of [...this.restores].reverse()) restore();
|
|
3386
|
+
this.restores.length = 0;
|
|
3387
|
+
this.guardedMethods.length = 0;
|
|
3388
|
+
this.fallbackRetired.clear();
|
|
3389
|
+
this.apiHandles.clear();
|
|
3351
3390
|
}
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
writable: true,
|
|
3361
|
-
value: (...args) => !this.active && !this.admission.isRetired(handle.id) ? Reflect.apply(original, handle, args) : this.admission.track(handle.id, async () => Reflect.apply(original, handle, args))
|
|
3362
|
-
});
|
|
3391
|
+
async converge(input, expected, request) {
|
|
3392
|
+
const snapshot = nativeMetadataSnapshotSchema.parse(input);
|
|
3393
|
+
const id = snapshot.sessionId;
|
|
3394
|
+
this.targets.add(id);
|
|
3395
|
+
this.assertInstances();
|
|
3396
|
+
if (snapshot.queryProvider !== this.queryProvider || snapshot.queryPath !== (this.queryConfiguration?.path ?? null)) return false;
|
|
3397
|
+
if (id !== expected.sessionId || id !== request.expected.sessionId || expected.storeId !== request.expected.storeId || expected.lifecycleId !== request.expected.lifecycleId || !this.admission.isRetired(id)) {
|
|
3398
|
+
this.refuse("native/maintenance-binding-changed", "Native convergence requires the exact durable retirement binding.");
|
|
3363
3399
|
}
|
|
3364
|
-
return
|
|
3400
|
+
if ((this.operations.get(id) ?? 0) > 0 || (this.retained.get(id) ?? 0) > 0 || (this.followers.get(id) ?? 0) > 0) return false;
|
|
3401
|
+
this.assertCold(id);
|
|
3402
|
+
return this.admission.bypass(id, async () => {
|
|
3403
|
+
if (!await this.eraseCache(snapshot.cache, id, expected, request)) return false;
|
|
3404
|
+
this.query?._observations.cache.delete(id);
|
|
3405
|
+
const frozenIds = new Set(snapshot.workspaces.map((item) => item.workspaceId));
|
|
3406
|
+
const convergeWorkspace = async () => {
|
|
3407
|
+
for (const [workspaceId, raw] of this.workspace.requireTable().entries()) {
|
|
3408
|
+
const record = workspaceRecord.parse(raw);
|
|
3409
|
+
if (!record.sessionIds.some((sessionId) => sessionId === id)) continue;
|
|
3410
|
+
if (!frozenIds.has(workspaceId) || record.sessionIds.some((other) => other !== id && this.workspace.sessionPaths.get(other) !== record.path && !this.workspace.missingSessionPaths.has(other))) return false;
|
|
3411
|
+
const entity = this.workspace.get(workspaceId);
|
|
3412
|
+
if (!entity) throw new Error("Native Workspace account lost its runtime entity.");
|
|
3413
|
+
await entity.detachSession(id);
|
|
3414
|
+
}
|
|
3415
|
+
const state = workspaceDomainState2.parse(this.workspace.requireState());
|
|
3416
|
+
if (state.archivedSessionIds.includes(id) || state.pinnedSessionIds.includes(id)) {
|
|
3417
|
+
await this.workspace.setState({
|
|
3418
|
+
...state,
|
|
3419
|
+
archivedSessionIds: state.archivedSessionIds.filter((other) => other !== id),
|
|
3420
|
+
pinnedSessionIds: state.pinnedSessionIds.filter((other) => other !== id)
|
|
3421
|
+
});
|
|
3422
|
+
}
|
|
3423
|
+
this.workspace.headers.delete(id);
|
|
3424
|
+
this.workspace.sessionPaths.delete(id);
|
|
3425
|
+
this.workspace.invalidSessionPaths.delete(id);
|
|
3426
|
+
this.workspace.missingSessionPaths.delete(id);
|
|
3427
|
+
return true;
|
|
3428
|
+
};
|
|
3429
|
+
if (!await this.workspace.enqueueOperation(convergeWorkspace)) return false;
|
|
3430
|
+
if (this.query) {
|
|
3431
|
+
if (this.queryProvider === "sqlite-0.2.0-rc.2") {
|
|
3432
|
+
await this.query.searchSessions({ query: "native-retirement-reconciliation", limit: 1 });
|
|
3433
|
+
}
|
|
3434
|
+
if ((await this.query.listSessions()).some((record) => record.header.id === id)) return false;
|
|
3435
|
+
this.assertInstances();
|
|
3436
|
+
}
|
|
3437
|
+
this.admission.withoutBypass(() => this.ctx.emit("api-session/removed", id));
|
|
3438
|
+
return true;
|
|
3439
|
+
});
|
|
3365
3440
|
}
|
|
3366
3441
|
};
|
|
3367
3442
|
|
|
@@ -4059,7 +4134,7 @@ var NativeRetirementOwner = class _NativeRetirementOwner {
|
|
|
4059
4134
|
this.descriptor = retirementCapabilitiesSchema.parse({
|
|
4060
4135
|
protocolVersion: 1,
|
|
4061
4136
|
ownerId: "session-bin-native-jsonl-owner",
|
|
4062
|
-
hostVersion:
|
|
4137
|
+
hostVersion: legacyDurableHostContract,
|
|
4063
4138
|
providerId: "jsonl-session-records-v1",
|
|
4064
4139
|
storeId: domain.global.get().storeId,
|
|
4065
4140
|
participants: [{ id: "native-jsonl-files", version: fingerprint }, { id: "native-metadata", version: fingerprint }]
|