@tangle-network/cli-agent-registry 0.0.0-stage → 0.14.0

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.
@@ -0,0 +1,131 @@
1
+ //#region src/session-contract.d.ts
2
+ /**
3
+ * The harness session contract: what every registered harness promises about
4
+ * its native session, so one capture path and one reader cover all of them.
5
+ *
6
+ * A harness always persists a native session. Its store sits at a declared
7
+ * place under the execution's private runtime HOME, the sidecar knows the
8
+ * session id for each execution before the turn's evidence is needed, and the
9
+ * served model, usage and ending error can be read back from what it wrote.
10
+ * A harness that cannot promise this cannot be registered.
11
+ *
12
+ * Data and pure functions only: this module is imported by the Worker-safe
13
+ * `/specs` entry, so it must not import Node built-ins.
14
+ *
15
+ * The format ids and store shape mirror `@tangle-network/harness-sessions`,
16
+ * which owns the readers. Where that package is installed, the registry test
17
+ * fails when the two lists of format ids drift.
18
+ */
19
+ declare const SESSION_FORMAT_IDS: readonly ["claude-code.projects-jsonl", "codex.rollout-jsonl", "opencode.sqlite", "pi.session-jsonl", "kimi.session-dir", "factory.session-jsonl", "gemini.chat-json", "amp.thread-json", "forge.sqlite", "hermes.state-sqlite", "prime.session-jsonl", "openclaw.session-jsonl", "sidecar.message-json", "sidecar.session-json"];
20
+ type SessionFormatId = (typeof SESSION_FORMAT_IDS)[number];
21
+ interface NativeSessionStore {
22
+ /**
23
+ * Relative to the execution's private runtime HOME. Normalized, with no
24
+ * `..`; never absolute and never in the workspace.
25
+ */
26
+ root: string;
27
+ /** Globs under root that hold one session. `{id}` is the native session id. */
28
+ files: readonly string[];
29
+ /** How the harness writes. This decides how a copy is taken while it runs. */
30
+ write: "jsonl-append" | "json-rewrite" | "sqlite-wal";
31
+ /** True when one store holds every session in the HOME (opencode.db). Readers select by id. */
32
+ shared: boolean;
33
+ format: SessionFormatId;
34
+ }
35
+ type NativeSessionIdentity = /** The sidecar mints the id and records it before it spawns the CLI. */{
36
+ kind: "pre-minted";
37
+ args: readonly string[];
38
+ } /** The id comes from an API call that precedes the first prompt. */ | {
39
+ kind: "api-created";
40
+ call: "opencode POST /session" | "acp session/new" | "codex thread/start" | "amp threads new";
41
+ }
42
+ /**
43
+ * Only for a CLI that cannot accept an id: the id is the first event's.
44
+ * Past the deadline the attempt fails with `native_session_id_unobserved`.
45
+ */
46
+ | {
47
+ kind: "first-event";
48
+ event: string;
49
+ deadlineMs: number;
50
+ };
51
+ interface HarnessSessionEvidence {
52
+ servedModel: "provider-response" | "session-record" | "turn-context";
53
+ usage: "per-response" | "cumulative" | "session-total";
54
+ endingError: "session-record" | "none";
55
+ }
56
+ interface HarnessSessionContract {
57
+ version: 1;
58
+ stores: readonly [NativeSessionStore, ...NativeSessionStore[]];
59
+ identity: NativeSessionIdentity;
60
+ resume: {
61
+ kind: "args";
62
+ args: readonly string[];
63
+ } | {
64
+ kind: "api";
65
+ };
66
+ /**
67
+ * Argv tokens that disable or redirect persistence. Refused in config
68
+ * extraFlags, in profile flags and in the built argv. A token also matches
69
+ * its `--flag=value` form.
70
+ */
71
+ forbiddenArgs: readonly string[];
72
+ /**
73
+ * Env names, and flags such as `--session-dir`, that move the store, each
74
+ * mapped to the only path it may take, relative to the runtime HOME.
75
+ */
76
+ storeEnv: Readonly<Record<string, string>>;
77
+ evidence: HarnessSessionEvidence;
78
+ /** The pinned CLI version (infra/nix) that the conformance receipt covers. */
79
+ cliVersion: string;
80
+ }
81
+ type HarnessSessionContractErrorCode = "harness_session_contract_missing" | "harness_session_persistence_disabled" | "harness_conformance_missing" | "native_session_id_unobserved";
82
+ declare class HarnessSessionContractError extends Error {
83
+ readonly code: HarnessSessionContractErrorCode;
84
+ constructor(code: HarnessSessionContractErrorCode, message: string);
85
+ }
86
+ /** Replace `{id}` in each token. */
87
+ declare function substituteNativeSessionId(tokens: readonly string[], nativeSessionId: string): string[];
88
+ /** The forbidden tokens present in `argv`, in argv order. */
89
+ declare function forbiddenArgViolations(contract: Pick<HarnessSessionContract, "forbiddenArgs">, argv: readonly string[]): string[];
90
+ /**
91
+ * Refuse flags that disable persistence. Called on configured flags before a
92
+ * turn can start, and on the built argv before spawn.
93
+ */
94
+ declare function assertFlagsHonourSessionContract(harness: string, contract: Pick<HarnessSessionContract, "forbiddenArgs">, flags: readonly string[], source: string): void;
95
+ /**
96
+ * Check the argv the adapter built: no forbidden token, and, for a
97
+ * pre-minted identity on a new session, the identity args carrying the
98
+ * minted id.
99
+ */
100
+ declare function assertArgvHonoursSessionContract(harness: string, contract: HarnessSessionContract, argv: readonly string[], session: {
101
+ mintedId?: string;
102
+ }): void;
103
+ /** Normalize a POSIX path; `..` that escapes the start is kept as a leading `..`. */
104
+ declare function normalizePosixPath(path: string): string;
105
+ /**
106
+ * Refuse a store location the contract does not declare. Each env value (or
107
+ * flag value, for keys that start with `--`) must resolve to exactly
108
+ * HOME/<declared path>; relative flag values resolve against `cwd`.
109
+ */
110
+ declare function assertStoreLocationHonoursSessionContract(harness: string, contract: Pick<HarnessSessionContract, "storeEnv">, input: {
111
+ runtimeHome: string;
112
+ cwd: string;
113
+ env?: Readonly<Record<string, string | undefined>>;
114
+ argv?: readonly string[];
115
+ }): void;
116
+ /**
117
+ * A matcher for one store glob. `**` matches any number of path segments
118
+ * (including none), `*` matches within one segment, and `{id}` is the native
119
+ * session id (any id when omitted).
120
+ */
121
+ declare function compileSessionStoreGlob(glob: string, nativeSessionId?: string): RegExp;
122
+ /**
123
+ * The declared store that holds `pathFromHome` for this session, or
124
+ * undefined. A shared store's files belong to every session.
125
+ */
126
+ declare function sessionStoreForPath(contract: Pick<HarnessSessionContract, "stores">, pathFromHome: string, nativeSessionId: string): NativeSessionStore | undefined;
127
+ /** Structural checks every declared contract must pass. */
128
+ declare function sessionContractProblems(contract: HarnessSessionContract): string[];
129
+ //#endregion
130
+ export { substituteNativeSessionId as _, NativeSessionIdentity as a, SessionFormatId as c, assertStoreLocationHonoursSessionContract as d, compileSessionStoreGlob as f, sessionStoreForPath as g, sessionContractProblems as h, HarnessSessionEvidence as i, assertArgvHonoursSessionContract as l, normalizePosixPath as m, HarnessSessionContractError as n, NativeSessionStore as o, forbiddenArgViolations as p, HarnessSessionContractErrorCode as r, SESSION_FORMAT_IDS as s, HarnessSessionContract as t, assertFlagsHonourSessionContract as u };
131
+ //# sourceMappingURL=session-contract-BC75JdnA.d.mts.map
@@ -0,0 +1,2 @@
1
+ import { _ as substituteNativeSessionId, a as NativeSessionIdentity, c as SessionFormatId, d as assertStoreLocationHonoursSessionContract, f as compileSessionStoreGlob, g as sessionStoreForPath, h as sessionContractProblems, i as HarnessSessionEvidence, l as assertArgvHonoursSessionContract, m as normalizePosixPath, n as HarnessSessionContractError, o as NativeSessionStore, p as forbiddenArgViolations, r as HarnessSessionContractErrorCode, s as SESSION_FORMAT_IDS, t as HarnessSessionContract, u as assertFlagsHonourSessionContract } from "./session-contract-BC75JdnA.mjs";
2
+ export { HarnessSessionContract, HarnessSessionContractError, HarnessSessionContractErrorCode, HarnessSessionEvidence, NativeSessionIdentity, NativeSessionStore, SESSION_FORMAT_IDS, SessionFormatId, assertArgvHonoursSessionContract, assertFlagsHonourSessionContract, assertStoreLocationHonoursSessionContract, compileSessionStoreGlob, forbiddenArgViolations, normalizePosixPath, sessionContractProblems, sessionStoreForPath, substituteNativeSessionId };
@@ -0,0 +1,190 @@
1
+ //#region src/session-contract.ts
2
+ /**
3
+ * The harness session contract: what every registered harness promises about
4
+ * its native session, so one capture path and one reader cover all of them.
5
+ *
6
+ * A harness always persists a native session. Its store sits at a declared
7
+ * place under the execution's private runtime HOME, the sidecar knows the
8
+ * session id for each execution before the turn's evidence is needed, and the
9
+ * served model, usage and ending error can be read back from what it wrote.
10
+ * A harness that cannot promise this cannot be registered.
11
+ *
12
+ * Data and pure functions only: this module is imported by the Worker-safe
13
+ * `/specs` entry, so it must not import Node built-ins.
14
+ *
15
+ * The format ids and store shape mirror `@tangle-network/harness-sessions`,
16
+ * which owns the readers. Where that package is installed, the registry test
17
+ * fails when the two lists of format ids drift.
18
+ */
19
+ const SESSION_FORMAT_IDS = [
20
+ "claude-code.projects-jsonl",
21
+ "codex.rollout-jsonl",
22
+ "opencode.sqlite",
23
+ "pi.session-jsonl",
24
+ "kimi.session-dir",
25
+ "factory.session-jsonl",
26
+ "gemini.chat-json",
27
+ "amp.thread-json",
28
+ "forge.sqlite",
29
+ "hermes.state-sqlite",
30
+ "prime.session-jsonl",
31
+ "openclaw.session-jsonl",
32
+ "sidecar.message-json",
33
+ "sidecar.session-json"
34
+ ];
35
+ var HarnessSessionContractError = class extends Error {
36
+ code;
37
+ constructor(code, message) {
38
+ super(`${code}: ${message}`);
39
+ this.name = "HarnessSessionContractError";
40
+ this.code = code;
41
+ }
42
+ };
43
+ /** Replace `{id}` in each token. */
44
+ function substituteNativeSessionId(tokens, nativeSessionId) {
45
+ return tokens.map((token) => token.split("{id}").join(nativeSessionId));
46
+ }
47
+ function matchesForbidden(token, forbidden) {
48
+ return token === forbidden || token.startsWith(`${forbidden}=`);
49
+ }
50
+ /** The forbidden tokens present in `argv`, in argv order. */
51
+ function forbiddenArgViolations(contract, argv) {
52
+ return argv.filter((token) => contract.forbiddenArgs.some((forbidden) => matchesForbidden(token, forbidden)));
53
+ }
54
+ function indexOfSequence(haystack, needle) {
55
+ if (needle.length === 0) return 0;
56
+ for (let index = 0; index + needle.length <= haystack.length; index++) if (needle.every((token, offset) => haystack[index + offset] === token)) return index;
57
+ return -1;
58
+ }
59
+ /**
60
+ * Refuse flags that disable persistence. Called on configured flags before a
61
+ * turn can start, and on the built argv before spawn.
62
+ */
63
+ function assertFlagsHonourSessionContract(harness, contract, flags, source) {
64
+ const violations = forbiddenArgViolations(contract, flags);
65
+ if (violations.length > 0) throw new HarnessSessionContractError("harness_session_persistence_disabled", `${harness} ${source} carries ${violations.join(", ")}, which disables or redirects its native session`);
66
+ }
67
+ /**
68
+ * Check the argv the adapter built: no forbidden token, and, for a
69
+ * pre-minted identity on a new session, the identity args carrying the
70
+ * minted id.
71
+ */
72
+ function assertArgvHonoursSessionContract(harness, contract, argv, session) {
73
+ assertFlagsHonourSessionContract(harness, contract, argv, "argv");
74
+ if (session.mintedId === void 0) return;
75
+ if (contract.identity.kind !== "pre-minted") throw new HarnessSessionContractError("harness_session_persistence_disabled", `${harness} was given a minted session id but its identity is ${contract.identity.kind}`);
76
+ if (indexOfSequence(argv, substituteNativeSessionId(contract.identity.args, session.mintedId)) < 0) throw new HarnessSessionContractError("harness_session_persistence_disabled", `${harness} argv does not carry its pre-minted session id (${contract.identity.args.join(" ")})`);
77
+ }
78
+ /** Normalize a POSIX path; `..` that escapes the start is kept as a leading `..`. */
79
+ function normalizePosixPath(path) {
80
+ const absolute = path.startsWith("/");
81
+ const parts = [];
82
+ for (const part of path.split("/")) {
83
+ if (part === "" || part === ".") continue;
84
+ if (part === "..") {
85
+ if (parts.length > 0 && parts[parts.length - 1] !== "..") parts.pop();
86
+ else if (!absolute) parts.push("..");
87
+ continue;
88
+ }
89
+ parts.push(part);
90
+ }
91
+ const joined = parts.join("/");
92
+ return absolute ? `/${joined}` : joined || ".";
93
+ }
94
+ function resolvePosix(base, value) {
95
+ return normalizePosixPath(value.startsWith("/") ? value : `${base.replace(/\/+$/u, "")}/${value}`);
96
+ }
97
+ /**
98
+ * Refuse a store location the contract does not declare. Each env value (or
99
+ * flag value, for keys that start with `--`) must resolve to exactly
100
+ * HOME/<declared path>; relative flag values resolve against `cwd`.
101
+ */
102
+ function assertStoreLocationHonoursSessionContract(harness, contract, input) {
103
+ const home = normalizePosixPath(input.runtimeHome);
104
+ const violations = [];
105
+ for (const [name, declared] of Object.entries(contract.storeEnv)) {
106
+ const expected = resolvePosix(home, declared);
107
+ const values = [];
108
+ if (name.startsWith("--")) {
109
+ const argv = input.argv ?? [];
110
+ for (let index = 0; index < argv.length; index++) {
111
+ const token = argv[index];
112
+ if (token === name && index + 1 < argv.length) values.push(argv[index + 1]);
113
+ else if (token.startsWith(`${name}=`)) values.push(token.slice(name.length + 1));
114
+ }
115
+ } else {
116
+ const value = input.env?.[name];
117
+ if (value !== void 0 && value !== "") values.push(value);
118
+ }
119
+ for (const value of values) if (resolvePosix(input.cwd, value) !== expected) violations.push(`${name}=${value} (declared ${expected})`);
120
+ }
121
+ if (violations.length > 0) throw new HarnessSessionContractError("harness_session_persistence_disabled", `${harness} moves its native session store outside the declared location: ${violations.join("; ")}`);
122
+ }
123
+ function escapeRegExp(text) {
124
+ return text.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
125
+ }
126
+ /**
127
+ * A matcher for one store glob. `**` matches any number of path segments
128
+ * (including none), `*` matches within one segment, and `{id}` is the native
129
+ * session id (any id when omitted).
130
+ */
131
+ function compileSessionStoreGlob(glob, nativeSessionId) {
132
+ let pattern = "";
133
+ for (let index = 0; index < glob.length;) if (glob.startsWith("**/", index)) {
134
+ pattern += "(?:[^/]+/)*";
135
+ index += 3;
136
+ } else if (glob.startsWith("**", index)) {
137
+ pattern += ".*";
138
+ index += 2;
139
+ } else if (glob.startsWith("{id}", index)) {
140
+ pattern += nativeSessionId === void 0 ? "[^/]+" : escapeRegExp(nativeSessionId);
141
+ index += 4;
142
+ } else if (glob[index] === "*") {
143
+ pattern += "[^/]*";
144
+ index += 1;
145
+ } else {
146
+ pattern += escapeRegExp(glob[index]);
147
+ index += 1;
148
+ }
149
+ return new RegExp(`^${pattern}$`, "u");
150
+ }
151
+ /**
152
+ * The declared store that holds `pathFromHome` for this session, or
153
+ * undefined. A shared store's files belong to every session.
154
+ */
155
+ function sessionStoreForPath(contract, pathFromHome, nativeSessionId) {
156
+ const path = normalizePosixPath(pathFromHome);
157
+ for (const store of contract.stores) {
158
+ const root = normalizePosixPath(store.root);
159
+ if (!path.startsWith(`${root}/`)) continue;
160
+ const rest = path.slice(root.length + 1);
161
+ const id = store.shared ? void 0 : nativeSessionId;
162
+ if (store.files.some((glob) => compileSessionStoreGlob(glob, id).test(rest))) return store;
163
+ }
164
+ }
165
+ /** Structural checks every declared contract must pass. */
166
+ function sessionContractProblems(contract) {
167
+ const problems = [];
168
+ if (contract.version !== 1) problems.push("version must be 1");
169
+ if (!contract.cliVersion.trim()) problems.push("cliVersion is empty");
170
+ for (const store of contract.stores) {
171
+ const root = normalizePosixPath(store.root);
172
+ if (store.root.startsWith("/") || root.startsWith("..") || root === ".") problems.push(`store root ${store.root} is not a relative path under HOME`);
173
+ if (root !== store.root) problems.push(`store root ${store.root} is not normalized`);
174
+ if (!SESSION_FORMAT_IDS.includes(store.format)) problems.push(`store format ${store.format} is unknown`);
175
+ if (store.files.length === 0) problems.push(`store ${store.root} declares no files`);
176
+ if (!store.shared && !store.files.every((glob) => glob.includes("{id}"))) problems.push(`store ${store.root} is per-session but a glob lacks {id}`);
177
+ }
178
+ if (contract.identity.kind === "pre-minted" && !contract.identity.args.some((token) => token.includes("{id}"))) problems.push("pre-minted identity args lack {id}");
179
+ if (contract.resume.kind === "args" && !contract.resume.args.some((token) => token.includes("{id}"))) problems.push("resume args lack {id}");
180
+ if (contract.identity.kind === "first-event" && !(contract.identity.deadlineMs > 0)) problems.push("first-event identity needs a positive deadline");
181
+ for (const [name, declared] of Object.entries(contract.storeEnv)) {
182
+ const normalized = normalizePosixPath(declared);
183
+ if (declared.startsWith("/") || normalized.startsWith("..")) problems.push(`storeEnv ${name} must name a path under HOME`);
184
+ }
185
+ return problems;
186
+ }
187
+ //#endregion
188
+ export { HarnessSessionContractError, SESSION_FORMAT_IDS, assertArgvHonoursSessionContract, assertFlagsHonourSessionContract, assertStoreLocationHonoursSessionContract, compileSessionStoreGlob, forbiddenArgViolations, normalizePosixPath, sessionContractProblems, sessionStoreForPath, substituteNativeSessionId };
189
+
190
+ //# sourceMappingURL=session-contract.mjs.map